upgade package

This commit is contained in:
CHIEFSOFT\ameye
2024-04-23 14:04:21 -04:00
parent ede879d821
commit 44f6fb0816
966 changed files with 7972 additions and 88698 deletions
+43
View File
@@ -0,0 +1,43 @@
import {useEffect, useState} from 'react'
import {Tab} from 'bootstrap'
import {
MenuComponent,
DrawerComponent,
ScrollComponent,
ScrollTopComponent,
StickyComponent,
ToggleComponent,
SwapperComponent,
} from '../assets/ts/components'
import {ThemeModeComponent} from '../assets/ts/layout'
import {useLayout} from './core'
export function MasterInit() {
const {config} = useLayout()
const [initialized, setInitialized] = useState(false)
const pluginsInitialization = () => {
ThemeModeComponent.init()
setTimeout(() => {
ToggleComponent.bootstrap()
ScrollTopComponent.bootstrap()
DrawerComponent.bootstrap()
StickyComponent.bootstrap()
MenuComponent.bootstrap()
ScrollComponent.bootstrap()
SwapperComponent.bootstrap()
document.querySelectorAll('[data-bs-toggle="tab"]').forEach((tab) => {
Tab.getOrCreateInstance(tab)
})
}, 500)
}
useEffect(() => {
if (!initialized) {
setInitialized(true)
pluginsInitialization()
}
}, [config, initialized])
return <></>
}
+50
View File
@@ -0,0 +1,50 @@
import {useEffect} from 'react'
import {Outlet, useLocation} from 'react-router-dom'
import {HeaderWrapper} from './components/header'
import {RightToolbar} from '../partials/layout/RightToolbar'
import {ScrollTop} from './components/scroll-top'
import {FooterWrapper} from './components/footer'
import {Sidebar} from './components/sidebar'
import {ActivityDrawer, DrawerMessenger, InviteUsers, UpgradePlan} from '../partials'
import {PageDataProvider} from './core'
import {reInitMenu} from '../helpers'
const MasterLayout = () => {
const location = useLocation()
useEffect(() => {
reInitMenu()
}, [location.key])
return (
<PageDataProvider>
<div className='d-flex flex-column flex-root app-root' id='kt_app_root'>
<div className='app-page flex-column flex-column-fluid' id='kt_app_page'>
<HeaderWrapper />
<div className='app-wrapper flex-column flex-row-fluid' id='kt_app_wrapper'>
<Sidebar />
<div className='app-main flex-column flex-row-fluid' id='kt_app_main'>
<div className='d-flex flex-column flex-column-fluid'>
<Outlet />
</div>
<FooterWrapper />
</div>
</div>
</div>
</div>
{/* begin:: Drawers */}
<ActivityDrawer />
<RightToolbar />
<DrawerMessenger />
{/* end:: Drawers */}
{/* begin:: Modals */}
<InviteUsers />
<UpgradePlan />
{/* end:: Modals */}
<ScrollTop />
</PageDataProvider>
)
}
export {MasterLayout}
@@ -0,0 +1,42 @@
import {useEffect} from 'react'
import {useLocation} from 'react-router'
import clsx from 'clsx'
import {useLayout} from '../../core'
import {DrawerComponent} from '../../../assets/ts/components'
import {WithChildren} from '../../../helpers'
const Content = ({children}: WithChildren) => {
const {config, classes} = useLayout()
const location = useLocation()
useEffect(() => {
DrawerComponent.hideAll()
}, [location])
const appContentContainer = config.app?.content?.container
return (
<div
id='kt_app_content'
className={clsx(
'app-content flex-column-fluid',
classes.content.join(' '),
config?.app?.content?.class
)}
>
{appContentContainer ? (
<div
id='kt_app_content_container'
className={clsx('app-container', classes.contentContainer.join(' '), {
'container-xxl': appContentContainer === 'fixed',
'container-fluid': appContentContainer === 'fluid',
})}
>
{children}
</div>
) : (
<>{children}</>
)}
</div>
)
}
export {Content}
@@ -0,0 +1 @@
export * from './Content'
@@ -0,0 +1,61 @@
import {useEffect} from 'react'
import {ILayout, useLayout} from '../../core'
const Footer = () => {
const {config} = useLayout()
useEffect(() => {
updateDOM(config)
}, [config])
return (
<>
<div className='text-gray-900 order-2 order-md-1'>
<span className='text-muted fw-semibold me-1'>
{new Date().getFullYear().toString()}&copy;
</span>
<a
href='https://keenthemes.com/'
target='_blank'
className='text-gray-800 text-hover-primary'
>
Keenthemes
</a>
</div>
<ul className='menu menu-gray-600 menu-hover-primary fw-semibold order-1'>
<li className='menu-item'>
<a href='https://keenthemes.com/' target='_blank' className='menu-link px-2'>
About
</a>
</li>
<li className='menu-item'>
<a href='https://devs.keenthemes.com/' target='_blank' className='menu-link px-2'>
Support
</a>
</li>
<li className='menu-item'>
<a
href='https://themeforest.net/item/metronic-responsive-admin-dashboard-template/4021469'
target='_blank'
className='menu-link px-2'
>
Purchase
</a>
</li>
</ul>
</>
)
}
const updateDOM = (config: ILayout) => {
if (config.app?.footer?.fixed?.desktop) {
document.body.classList.add('data-kt-app-footer-fixed', 'true')
}
if (config.app?.footer?.fixed?.mobile) {
document.body.classList.add('data-kt-app-footer-fixed-mobile', 'true')
}
}
export {Footer}
@@ -0,0 +1,30 @@
import clsx from 'clsx'
import {useLayout} from '../../core'
import {Footer} from './Footer'
const FooterWrapper = () => {
const {config} = useLayout()
if (!config.app?.footer?.display) {
return null
}
return (
<div className='app-footer' id='kt_app_footer'>
{config.app.footer.containerClass ? (
<div
className={clsx(
'app-container',
config.app.footer.container === 'fixed' ? 'container-xxl' : 'container-fluid',
config.app.footer.containerClass
)}
>
<Footer />
</div>
) : (
<Footer />
)}
</div>
)
}
export {FooterWrapper}
@@ -0,0 +1 @@
export * from './FooterWrapper'
@@ -0,0 +1,92 @@
/* eslint-disable no-prototype-builtins */
import {FC, useEffect} from 'react'
import {ILayout, useLayout} from '../../core'
import {MenuInner} from './header-menus'
const Header: FC = () => {
const {config} = useLayout()
useEffect(() => {
updateDOM(config)
}, [config])
return (
<div
className='
menu
menu-rounded
menu-column
menu-lg-row
my-5
my-lg-0
align-items-stretch
fw-semibold
px-2 px-lg-0
'
id='kt_app_header_menu'
data-kt-menu='true'
>
<MenuInner />
</div>
)
}
const updateDOM = (config: ILayout) => {
if (config.app?.header?.default?.fixed?.desktop) {
document.body.setAttribute('data-kt-app-header-fixed', 'true')
document.body.setAttribute('data-kt-app-header-minimize', 'on')
}
if (config.app?.header?.default?.fixed?.mobile) {
document.body.setAttribute('data-kt-app-header-fixed-mobile', 'true')
}
if (config.app?.header?.default?.stacked) {
document.body.setAttribute('data-kt-app-header-stacked', 'true')
}
const appHeaderDefaultStickyEnabled = config.app?.header?.default?.sticky?.enabled
let appHeaderDefaultStickyAttributes: {[attrName: string]: string} = {}
if (appHeaderDefaultStickyEnabled) {
appHeaderDefaultStickyAttributes = config.app?.header?.default?.sticky?.attributes as {
[attrName: string]: string
}
}
const appHeaderDefaultMinimizeEnabled = config.app?.header?.default?.minimize?.enabled
let appHeaderDefaultMinimizeAttributes: {[attrName: string]: string} = {}
if (appHeaderDefaultMinimizeEnabled) {
appHeaderDefaultMinimizeAttributes = config.app?.header?.default?.minimize?.attributes as {
[attrName: string]: string
}
}
setTimeout(() => {
const headerElement = document.getElementById('kt_app_header')
// header
if (headerElement) {
const headerAttributes = headerElement
.getAttributeNames()
.filter((t) => t.indexOf('data-') > -1)
headerAttributes.forEach((attr) => headerElement.removeAttribute(attr))
if (appHeaderDefaultStickyEnabled) {
for (const key in appHeaderDefaultStickyAttributes) {
if (appHeaderDefaultStickyAttributes.hasOwnProperty(key)) {
headerElement.setAttribute(key, appHeaderDefaultStickyAttributes[key])
}
}
}
if (appHeaderDefaultMinimizeEnabled) {
for (const key in appHeaderDefaultMinimizeAttributes) {
if (appHeaderDefaultMinimizeAttributes.hasOwnProperty(key)) {
headerElement.setAttribute(key, appHeaderDefaultMinimizeAttributes[key])
}
}
}
}
}, 0)
}
export {Header}
@@ -0,0 +1,111 @@
import clsx from 'clsx'
import {Link} from 'react-router-dom'
import {KTIcon, toAbsoluteUrl} from '../../../helpers'
import {LayoutSetup, useLayout} from '../../core'
import {Header} from './Header'
import {Navbar} from './Navbar'
export function HeaderWrapper() {
const {config, classes} = useLayout()
if (config.app?.header?.default?.container === 'fluid') {
LayoutSetup.classes.headerContainer.push("container-fluid");
} else {
LayoutSetup.classes.headerContainer.push("container-xxl");
}
if (!config.app?.header?.display) {
return null
}
return (
<div id='kt_app_header' className='app-header'>
<div
id='kt_app_header_container'
className={clsx(
'app-container',
classes.headerContainer.join(' '),
config.app?.header?.default?.containerClass
)}
>
{config.app.sidebar?.display && (
<>
{config.layoutType !== 'dark-header' && config.layoutType !== 'light-header' ? (
<div
className='d-flex align-items-center d-lg-none ms-n2 me-2'
title='Show sidebar menu'
>
<div
className='btn btn-icon btn-active-color-primary w-35px h-35px'
id='kt_app_sidebar_mobile_toggle'
>
<KTIcon iconName='abstract-14' className=' fs-1' />
</div>
<div className='d-flex align-items-center flex-grow-1 flex-lg-grow-0'>
<Link to='/dashboard' className='d-lg-none'>
<img
alt='Logo'
src={toAbsoluteUrl('media/logos/default-small.svg')}
className='h-30px'
/>
</Link>
</div>
</div>
) : null}
</>
)}
{!(config.layoutType === 'dark-sidebar' || config.layoutType === 'light-sidebar') && (
<div className='d-flex align-items-center flex-grow-1 flex-lg-grow-0 me-lg-15'>
<Link to='/dashboard'>
{config.layoutType === 'dark-header' ? (
<img
alt='Logo'
src={toAbsoluteUrl('media/logos/sidelogo.png')}
className='h-20px h-lg-30px app-sidebar-logo-default'
/>
) : (
<>
<img
alt='Logo'
src={toAbsoluteUrl('media/logos/sidelogo.png')}
className='h-20px h-lg-30px app-sidebar-logo-default theme-light-show'
/>
<img
alt='Logo'
src={toAbsoluteUrl('media/logos/sidelogo.png')}
className='h-20px h-lg-30px app-sidebar-logo-default theme-dark-show'
/>
</>
)}
</Link>
</div>
)}
<div
id='kt_app_header_wrapper'
className='d-flex align-items-stretch justify-content-between flex-lg-grow-1'
>
{config.app.header.default?.content === 'menu' &&
config.app.header.default.menu?.display && (
<div
className='app-header-menu app-header-mobile-drawer align-items-stretch'
data-kt-drawer='true'
data-kt-drawer-name='app-header-menu'
data-kt-drawer-activate='{default: true, lg: false}'
data-kt-drawer-overlay='true'
data-kt-drawer-width='225px'
data-kt-drawer-direction='end'
data-kt-drawer-toggle='#kt_app_header_menu_toggle'
data-kt-swapper='true'
data-kt-swapper-mode="{default: 'append', lg: 'prepend'}"
data-kt-swapper-parent="{default: '#kt_app_body', lg: '#kt_app_header_wrapper'}"
>
<Header />
</div>
)}
<Navbar />
</div>
</div>
</div>
)
}
@@ -0,0 +1,75 @@
import clsx from 'clsx'
import {KTIcon, toAbsoluteUrl} from '../../../helpers'
import {HeaderNotificationsMenu, HeaderUserMenu, Search, ThemeModeSwitcher} from '../../../partials'
import {useLayout} from '../../core'
const itemClass = 'ms-1 ms-md-4'
const btnClass =
'btn btn-icon btn-custom btn-icon-muted btn-active-light btn-active-color-primary w-35px h-35px'
const userAvatarClass = 'symbol-35px'
const btnIconClass = 'fs-2'
const Navbar = () => {
const {config} = useLayout()
return (
<div className='app-navbar flex-shrink-0'>
<div className={clsx('app-navbar-item align-items-stretch', itemClass)}>
<Search />
</div>
<div className={clsx('app-navbar-item', itemClass)}>
<div id='kt_activities_toggle' className={btnClass}>
<KTIcon iconName='chart-simple' className={btnIconClass} />
</div>
</div>
<div className={clsx('app-navbar-item', itemClass)}>
<div
data-kt-menu-trigger="{default: 'click'}"
data-kt-menu-attach='parent'
data-kt-menu-placement='bottom-end'
className={btnClass}
>
<KTIcon iconName='element-plus' className={btnIconClass} />
</div>
<HeaderNotificationsMenu />
</div>
<div className={clsx('app-navbar-item', itemClass)}>
<div className={clsx('position-relative', btnClass)} id='kt_drawer_chat_toggle'>
<KTIcon iconName='message-text-2' className={btnIconClass} />
<span className='bullet bullet-dot bg-success h-6px w-6px position-absolute translate-middle top-0 start-50 animation-blink' />
</div>
</div>
<div className={clsx('app-navbar-item', itemClass)}>
<ThemeModeSwitcher toggleBtnClass={clsx('btn-active-light-primary btn-custom')} />
</div>
<div className={clsx('app-navbar-item', itemClass)}>
<div
className={clsx('cursor-pointer symbol', userAvatarClass)}
data-kt-menu-trigger="{default: 'click'}"
data-kt-menu-attach='parent'
data-kt-menu-placement='bottom-end'
>
<img src={toAbsoluteUrl('media/avatars/300-3.jpg')} alt='' />
</div>
<HeaderUserMenu />
</div>
{config.app?.header?.default?.menu?.display && (
<div className='app-navbar-item d-lg-none ms-2 me-n3' title='Show header menu'>
<div
className='btn btn-icon btn-active-color-primary w-35px h-35px'
id='kt_app_header_menu_toggle'
>
<KTIcon iconName='text-align-left' className={btnIconClass} />
</div>
</div>
)}
</div>
)
}
export {Navbar}
@@ -0,0 +1,162 @@
import {FC} from 'react'
import {Link} from 'react-router-dom'
import {toAbsoluteUrl} from '../../../../helpers'
import {useLayout} from '../../../core'
const MegaMenu: FC = () => {
const {setLayoutType, setToolbarType} = useLayout()
return (
<div className='row'>
{/* begin:Col */}
<div className='col-lg-6'>
{/* begin:Row */}
<div className='row'>
{/* begin:Col */}
<div className='col-lg-6 mb-3'>
{/* begin:Heading */}
<h4 className='fs-6 fs-lg-4 text-gray-800 fw-bold mt-3 mb-3 ms-4'>Layouts</h4>
{/* end:Heading */}
{/* begin:Menu item */}
<div className='menu-item p-0 m-0'>
{/* begin:Menu link */}
<a onClick={() => setLayoutType('light-sidebar')} className='menu-link'>
<span className='menu-bullet'>
<span className='bullet bullet-dot bg-gray-300i h-6px w-6px'></span>
</span>
<span className='menu-title'>Light Sidebar</span>
</a>
{/* end:Menu link */}
</div>
{/* end:Menu item */}
{/* begin:Menu item */}
<div className='menu-item p-0 m-0'>
{/* begin:Menu link */}
<a onClick={() => setLayoutType('dark-sidebar')} className='menu-link'>
<span className='menu-bullet'>
<span className='bullet bullet-dot bg-gray-300i h-6px w-6px'></span>
</span>
<span className='menu-title'>Dark Sidebar</span>
</a>
{/* end:Menu link */}
</div>
{/* end:Menu item */}
{/* begin:Menu item */}
<div className='menu-item p-0 m-0'>
{/* begin:Menu link */}
<a onClick={() => setLayoutType('light-header')} className='menu-link'>
<span className='menu-bullet'>
<span className='bullet bullet-dot bg-gray-300i h-6px w-6px'></span>
</span>
<span className='menu-title'>Light Header</span>
</a>
{/* end:Menu link */}
</div>
{/* end:Menu item */}
{/* begin:Menu item */}
<div className='menu-item p-0 m-0'>
{/* begin:Menu link */}
<a onClick={() => setLayoutType('dark-header')} className='menu-link'>
<span className='menu-bullet'>
<span className='bullet bullet-dot bg-gray-300i h-6px w-6px'></span>
</span>
<span className='menu-title'>Dark Header</span>
</a>
{/* end:Menu link */}
</div>
{/* end:Menu item */}
</div>
{/* end:Col */}
{/* begin:Col */}
<div className='col-lg-6 mb-3'>
{/* begin:Heading */}
<h4 className='fs-6 fs-lg-4 text-gray-800 fw-bold mt-3 mb-3 ms-4'>Toolbars</h4>
{/* end:Heading */}
{/* begin:Menu item */}
<div className='menu-item p-0 m-0'>
{/* begin:Menu link */}
<a onClick={() => setToolbarType('classic')} className='menu-link'>
<span className='menu-bullet'>
<span className='bullet bullet-dot bg-gray-300i h-6px w-6px'></span>
</span>
<span className='menu-title'>Classic</span>
</a>
{/* end:Menu link */}
</div>
{/* end:Menu item */}
{/* begin:Menu item */}
<div className='menu-item p-0 m-0'>
{/* begin:Menu link */}
<a onClick={() => setToolbarType('saas')} className='menu-link'>
<span className='menu-bullet'>
<span className='bullet bullet-dot bg-gray-300i h-6px w-6px'></span>
</span>
<span className='menu-title'>SaaS</span>
</a>
{/* end:Menu link */}
</div>
{/* end:Menu item */}
{/* begin:Menu item */}
<div className='menu-item p-0 m-0'>
{/* begin:Menu link */}
<a onClick={() => setToolbarType('accounting')} className='menu-link'>
<span className='menu-bullet'>
<span className='bullet bullet-dot bg-gray-300i h-6px w-6px'></span>
</span>
<span className='menu-title'>Accounting</span>
</a>
{/* end:Menu link */}
</div>
{/* end:Menu item */}
{/* begin:Menu item */}
<div className='menu-item p-0 m-0'>
{/* begin:Menu link */}
<a onClick={() => setToolbarType('extended')} className='menu-link'>
<span className='menu-bullet'>
<span className='bullet bullet-dot bg-gray-300i h-6px w-6px'></span>
</span>
<span className='menu-title'>Extended</span>
</a>
{/* end:Menu link */}
</div>
{/* end:Menu item */}
{/* begin:Menu item */}
<div className='menu-item p-0 m-0'>
{/* begin:Menu link */}
<a onClick={() => setToolbarType('reports')} className='menu-link'>
<span className='menu-bullet'>
<span className='bullet bullet-dot bg-gray-300i h-6px w-6px'></span>
</span>
<span className='menu-title'>Reports</span>
</a>
{/* end:Menu link */}
</div>
{/* end:Menu item */}
</div>
{/* end:Col */}
</div>
{/* end:Row */}
<div className='separator separator-dashed mx-lg-5 mt-2 mb-6'></div>
{/* begin:Layout Builder */}
<div className='d-flex flex-stack flex-wrap flex-lg-nowrap gap-2 mb-5 mb-lg-0 mx-lg-5'>
<div className='d-flex flex-column me-5'>
<div className='fs-6 fw-bold text-gray-800'>Layout Builder</div>
<div className='fs-7 fw-semibold text-muted'>Customize view</div>
</div>
<Link to='/builder' className='btn btn-sm btn-primary fw-bold'>
Try Builder
</Link>
</div>
{/* end:Layout Builder */}
</div>
{/* end:Col */}
{/* begin:Col */}
<div className='col-lg-6 mb-3 py-lg-3 pe-lg-8 d-flex align-items-center'>
<img src={toAbsoluteUrl('media/stock/900x600/45.jpg')} className='rounded mw-100' alt='' />
</div>
{/* end:Col */}
</div>
)
}
export {MegaMenu}
@@ -0,0 +1,130 @@
import {useIntl} from 'react-intl'
import {MenuItem} from './MenuItem'
import {MenuInnerWithSub} from './MenuInnerWithSub'
import {MegaMenu} from './MegaMenu'
export function MenuInner() {
const intl = useIntl()
return (
<>
<MenuItem title={intl.formatMessage({id: 'MENU.DASHBOARD'})} to='/dashboard' />
<MenuItem title='Layout Builder' to='/builder' />
<MenuInnerWithSub
title='Crafted'
to='/crafted'
menuPlacement='bottom-start'
menuTrigger='click'
>
{/* PAGES */}
<MenuInnerWithSub
title='Pages'
to='/crafted/pages'
fontIcon='bi-archive'
hasArrow={true}
menuPlacement='right-start'
menuTrigger={`{default:'click', lg: 'hover'}`}
>
<MenuInnerWithSub
title='Profile'
to='/crafted/pages/profile'
hasArrow={true}
hasBullet={true}
menuPlacement='right-start'
menuTrigger={`{default:'click', lg: 'hover'}`}
>
<MenuItem to='/crafted/pages/profile/overview' title='Overview' hasBullet={true} />
<MenuItem to='/crafted/pages/profile/projects' title='Projects' hasBullet={true} />
<MenuItem to='/crafted/pages/profile/campaigns' title='Campaigns' hasBullet={true} />
<MenuItem to='/crafted/pages/profile/documents' title='Documents' hasBullet={true} />
<MenuItem
to='/crafted/pages/profile/connections'
title='Connections'
hasBullet={true}
/>
</MenuInnerWithSub>
<MenuInnerWithSub
title='Wizards'
to='/crafted/pages/wizards'
hasArrow={true}
hasBullet={true}
menuPlacement='right-start'
menuTrigger={`{default:'click', lg: 'hover'}`}
>
<MenuItem to='/crafted/pages/wizards/horizontal' title='Horizontal' hasBullet={true} />
<MenuItem to='/crafted/pages/wizards/vertical' title='Vertical' hasBullet={true} />
</MenuInnerWithSub>
</MenuInnerWithSub>
{/* ACCOUNT */}
<MenuInnerWithSub
title='Accounts'
to='/crafted/accounts'
fontIcon='bi-person'
hasArrow={true}
menuPlacement='right-start'
menuTrigger={`{default:'click', lg: 'hover'}`}
>
<MenuItem to='/crafted/account/overview' title='Overview' hasBullet={true} />
<MenuItem to='/crafted/account/settings' title='Settings' hasBullet={true} />
</MenuInnerWithSub>
{/* ERRORS */}
<MenuInnerWithSub
title='Errors'
to='/error'
fontIcon='bi-sticky'
hasArrow={true}
menuPlacement='right-start'
menuTrigger={`{default:'click', lg: 'hover'}`}
>
<MenuItem to='/error/404' title='Error 404' hasBullet={true} />
<MenuItem to='/error/500' title='Error 500' hasBullet={true} />
</MenuInnerWithSub>
{/* Widgets */}
<MenuInnerWithSub
title='Widgets'
to='/crafted/widgets'
fontIcon='bi-layers'
hasArrow={true}
menuPlacement='right-start'
menuTrigger={`{default:'click', lg: 'hover'}`}
>
<MenuItem to='/crafted/widgets/lists' title='Lists' hasBullet={true} />
<MenuItem to='/crafted/widgets/statistics' title='Statistics' hasBullet={true} />
<MenuItem to='/crafted/widgets/charts' title='Charts' hasBullet={true} />
<MenuItem to='/crafted/widgets/mixed' title='Mixed' hasBullet={true} />
<MenuItem to='/crafted/widgets/tables' title='Tables' hasBullet={true} />
<MenuItem to='/crafted/widgets/feeds' title='Feeds' hasBullet={true} />
</MenuInnerWithSub>
</MenuInnerWithSub>
<MenuInnerWithSub title='Apps' to='/apps' menuPlacement='bottom-start' menuTrigger='click'>
{/* PAGES */}
<MenuInnerWithSub
title='Chat'
to='/apps/chat'
icon='message-text-2'
hasArrow={true}
menuPlacement='right-start'
menuTrigger={`{default:'click', lg: 'hover'}`}
>
<MenuItem to='/apps/chat/private-chat' title='Private Chat' hasBullet={true} />
<MenuItem to='/apps/chat/group-chat' title='Group Chart' hasBullet={true} />
<MenuItem to='/apps/chat/drawer-chat' title='Drawer Chart' hasBullet={true} />
</MenuInnerWithSub>
<MenuItem icon='abstract-28' to='/apps/user-management/users' title='User management' />
</MenuInnerWithSub>
<MenuInnerWithSub
isMega={true}
title='Layouts'
to='/mega-menu'
menuPlacement='bottom-start'
menuTrigger='click'
>
<MegaMenu />
</MenuInnerWithSub>
</>
)
}
@@ -0,0 +1,82 @@
import {FC, useEffect, useRef} from 'react'
import {useLocation} from 'react-router'
import clsx from 'clsx'
import {checkIsActive, KTIcon, WithChildren} from '../../../../helpers'
type Props = {
to: string
title: string
icon?: string
fontIcon?: string
menuTrigger?: 'click' | `{default:'click', lg: 'hover'}`
menuPlacement?: 'right-start' | 'bottom-start' | 'left-start'
hasArrow?: boolean
hasBullet?: boolean
isMega?: boolean
}
const MenuInnerWithSub: FC<Props & WithChildren> = ({
children,
to,
title,
icon,
fontIcon,
menuTrigger,
menuPlacement,
hasArrow = false,
hasBullet = false,
isMega = false,
}) => {
const menuItemRef = useRef<HTMLDivElement>(null)
const {pathname} = useLocation()
useEffect(() => {
if (menuItemRef.current && menuTrigger && menuPlacement) {
menuItemRef.current.setAttribute('data-kt-menu-trigger', menuTrigger)
menuItemRef.current.setAttribute('data-kt-menu-placement', menuPlacement)
}
}, [menuTrigger, menuPlacement])
return (
<div ref={menuItemRef} className='menu-item menu-lg-down-accordion me-lg-1'>
<span
className={clsx('menu-link py-3', {
active: checkIsActive(pathname, to),
})}
>
{hasBullet && (
<span className='menu-bullet'>
<span className='bullet bullet-dot'></span>
</span>
)}
{icon && (
<span className='menu-icon'>
<KTIcon iconName={icon} className='fs-2' />
</span>
)}
{fontIcon && (
<span className='menu-icon'>
<i className={clsx('bi fs-3', fontIcon)}></i>
</span>
)}
<span className='menu-title'>{title}</span>
{hasArrow && <span className='menu-arrow'></span>}
</span>
<div
className={clsx(
'menu-sub menu-sub-lg-down-accordion menu-sub-lg-dropdown',
isMega ? 'w-100 w-lg-850px p-5 p-lg-5' : 'menu-rounded-0 py-lg-4 w-lg-225px'
)}
data-kt-menu-dismiss='true'
>
{children}
</div>
</div>
)
}
export {MenuInnerWithSub}
@@ -0,0 +1,53 @@
import {FC} from 'react'
import {useLocation} from 'react-router'
import {Link} from 'react-router-dom'
import clsx from 'clsx'
import {checkIsActive, KTIcon} from '../../../../helpers'
type Props = {
to: string
title: string
icon?: string
fontIcon?: string
hasArrow?: boolean
hasBullet?: boolean
}
const MenuItem: FC<Props> = ({to, title, icon, fontIcon, hasArrow = false, hasBullet = false}) => {
const {pathname} = useLocation()
return (
<div className='menu-item me-lg-1'>
<Link
className={clsx('menu-link py-3', {
'active menu-here': checkIsActive(pathname, to),
})}
to={to}
>
{hasBullet && (
<span className='menu-bullet'>
<span className='bullet bullet-dot'></span>
</span>
)}
{icon && (
<span className='menu-icon'>
<KTIcon iconName={icon} className='fs-2' />
</span>
)}
{fontIcon && (
<span className='menu-icon'>
<i className={clsx('bi fs-3', fontIcon)}></i>
</span>
)}
<span className='menu-title'>{title}</span>
{hasArrow && <span className='menu-arrow'></span>}
</Link>
</div>
)
}
export {MenuItem}
@@ -0,0 +1 @@
export * from './MenuInner'
@@ -0,0 +1 @@
export * from './HeaderWrapper'
@@ -0,0 +1,57 @@
import {useEffect, useState} from 'react'
import {useLocation} from 'react-router-dom'
import {
DrawerComponent,
ScrollTopComponent,
StickyComponent,
ToggleComponent,
} from '../../../assets/ts/components'
import {KTIcon} from '../../../helpers'
export function ScrollTop() {
const {pathname} = useLocation()
const [initialized, setInintialized] = useState(false)
const pluginsReinitialization = () => {
setTimeout(() => {
StickyComponent.reInitialization()
setTimeout(() => {
ToggleComponent.reinitialization()
DrawerComponent.reinitialization()
}, 70)
}, 140)
}
const scrollTop = () => {
ScrollTopComponent.goTop()
}
const updateHeaderSticky = () => {
const stickyHeader = document.body.querySelectorAll(`[data-kt-sticky-name="header"]`)
if (stickyHeader && stickyHeader.length > 0) {
const sticky = StickyComponent.getInstance(stickyHeader[0] as HTMLElement)
if (sticky) {
sticky.update()
}
}
}
useEffect(() => {
if (!initialized) {
setInintialized(true)
} else {
pluginsReinitialization()
}
updateHeaderSticky()
setTimeout(() => {
scrollTop()
}, 0)
}, [initialized, pathname])
return (
<div id='kt_scrolltop' className='scrolltop' data-kt-scrolltop='true'>
<KTIcon iconName='arrow-up' />
</div>
)
}
@@ -0,0 +1 @@
export * from './ScrollTop'
@@ -0,0 +1,141 @@
/* eslint-disable no-prototype-builtins */
import clsx from 'clsx'
import {useEffect, useRef} from 'react'
import {ILayout, useLayout} from '../../core'
import {SidebarMenu} from './sidebar-menu/SidebarMenu'
import {SidebarFooter} from './SidebarFooter'
import {SidebarLogo} from './SidebarLogo'
const Sidebar = () => {
const {config} = useLayout()
const sidebarRef = useRef<HTMLDivElement>(null)
useEffect(() => {
updateDOM(config)
}, [config])
if (!config.app?.sidebar?.display) {
return null
}
return (
<>
{(config.layoutType === 'dark-sidebar' || config.layoutType === 'light-sidebar') && (
<div
ref={sidebarRef}
id='kt_app_sidebar'
className={clsx('app-sidebar', config.app?.sidebar?.default?.class)}
>
<SidebarLogo sidebarRef={sidebarRef} />
<SidebarMenu />
<SidebarFooter />
</div>
)}
</>
)
}
const updateDOM = (config: ILayout) => {
if (config.layoutType === 'dark-sidebar' || config.layoutType === 'light-sidebar') {
if (config.app?.sidebar?.default?.minimize?.desktop?.enabled) {
if (config.app?.sidebar?.default?.minimize?.desktop?.default) {
document.body.setAttribute('data-kt-app-sidebar-minimize', 'on')
}
if (config.app?.sidebar?.default?.minimize?.desktop?.hoverable) {
document.body.setAttribute('data-kt-app-sidebar-hoverable', 'true')
}
}
if (config.app?.sidebar?.default?.minimize?.mobile?.enabled) {
if (config.app?.sidebar?.default?.minimize?.mobile?.default) {
document.body.setAttribute('data-kt-app-sidebar-minimize-mobile', 'on')
}
if (config.app?.sidebar?.default?.minimize?.mobile?.hoverable) {
document.body.setAttribute('data-kt-app-sidebar-hoverable-mobile', 'true')
}
}
if (config.app?.sidebar?.default?.collapse?.desktop?.enabled) {
if (config.app?.sidebar?.default?.collapse?.desktop?.default) {
document.body.setAttribute('data-kt-app-sidebar-collapse', 'on')
}
}
if (config.app?.sidebar?.default?.collapse?.mobile?.enabled) {
if (config.app?.sidebar?.default?.collapse?.mobile?.default) {
document.body.setAttribute('data-kt-app-sidebar-collapse-mobile', 'on')
}
}
if (config.app?.sidebar?.default?.push) {
if (config.app?.sidebar?.default?.push?.header) {
document.body.setAttribute('data-kt-app-sidebar-push-header', 'true')
}
if (config.app?.sidebar?.default?.push?.toolbar) {
document.body.setAttribute('data-kt-app-sidebar-push-toolbar', 'true')
}
if (config.app?.sidebar?.default?.push?.footer) {
document.body.setAttribute('data-kt-app-sidebar-push-footer', 'true')
}
}
if (config.app?.sidebar?.default?.stacked) {
document.body.setAttribute('app-sidebar-stacked', 'true')
}
document.body.setAttribute('data-kt-app-sidebar-enabled', 'true')
document.body.setAttribute(
'data-kt-app-sidebar-fixed',
config.app?.sidebar?.default?.fixed?.desktop?.toString() || ''
)
const appSidebarDefaultDrawerEnabled = config.app?.sidebar?.default?.drawer?.enabled
let appSidebarDefaultDrawerAttributes: {[attrName: string]: string} = {}
if (appSidebarDefaultDrawerEnabled) {
appSidebarDefaultDrawerAttributes = config.app?.sidebar?.default?.drawer?.attributes as {
[attrName: string]: string
}
}
const appSidebarDefaultStickyEnabled = config.app?.sidebar?.default?.sticky?.enabled
let appSidebarDefaultStickyAttributes: {[attrName: string]: string} = {}
if (appSidebarDefaultStickyEnabled) {
appSidebarDefaultStickyAttributes = config.app?.sidebar?.default?.sticky?.attributes as {
[attrName: string]: string
}
}
setTimeout(() => {
const sidebarElement = document.getElementById('kt_app_sidebar')
// sidebar
if (sidebarElement) {
const sidebarAttributes = sidebarElement
.getAttributeNames()
.filter((t) => t.indexOf('data-') > -1)
sidebarAttributes.forEach((attr) => sidebarElement.removeAttribute(attr))
if (appSidebarDefaultDrawerEnabled) {
for (const key in appSidebarDefaultDrawerAttributes) {
if (appSidebarDefaultDrawerAttributes.hasOwnProperty(key)) {
sidebarElement.setAttribute(key, appSidebarDefaultDrawerAttributes[key])
}
}
}
if (appSidebarDefaultStickyEnabled) {
for (const key in appSidebarDefaultStickyAttributes) {
if (appSidebarDefaultStickyAttributes.hasOwnProperty(key)) {
sidebarElement.setAttribute(key, appSidebarDefaultStickyAttributes[key])
}
}
}
}
}, 0)
}
}
export {Sidebar}
@@ -0,0 +1,26 @@
import {KTIcon} from '../../../helpers'
const SidebarFooter = () => {
return (
<div>
</div>
// <div className='app-sidebar-footer flex-column-auto pt-2 pb-6 px-6' id='kt_app_sidebar_footer'>
// <a
// href={import.meta.env.VITE_APP_PREVIEW_DOCS_URL}
// target='_blank'
// className='btn btn-flex flex-center btn-custom btn-primary overflow-hidden text-nowrap px-0 h-40px w-100'
// data-bs-toggle='tooltip'
// data-bs-trigger='hover'
// data-bs-dismiss-='click'
// title='Metronic Docs & Components'
// >
// {/*<span className='btn-label'>Docs & Components</span>*/}
// <KTIcon iconName='document' className='btn-icon fs-2 m-0' />
// </a>
// </div>
)
}
export {SidebarFooter}
@@ -0,0 +1,101 @@
import {Link} from 'react-router-dom'
import clsx from 'clsx'
import {KTIcon, toAbsoluteUrl} from '../../../helpers'
import {useLayout} from '../../core'
import {MutableRefObject, useEffect, useRef} from 'react'
import {ToggleComponent} from '../../../assets/ts/components'
type PropsType = {
sidebarRef: MutableRefObject<HTMLDivElement | null>
}
const SidebarLogo = (props: PropsType) => {
const {config} = useLayout()
const toggleRef = useRef<HTMLDivElement>(null)
const appSidebarDefaultMinimizeDesktopEnabled =
config?.app?.sidebar?.default?.minimize?.desktop?.enabled
const appSidebarDefaultCollapseDesktopEnabled =
config?.app?.sidebar?.default?.collapse?.desktop?.enabled
const toggleType = appSidebarDefaultCollapseDesktopEnabled
? 'collapse'
: appSidebarDefaultMinimizeDesktopEnabled
? 'minimize'
: ''
const toggleState = appSidebarDefaultMinimizeDesktopEnabled ? 'active' : ''
const appSidebarDefaultMinimizeDefault = config.app?.sidebar?.default?.minimize?.desktop?.default
useEffect(() => {
setTimeout(() => {
const toggleObj = ToggleComponent.getInstance(toggleRef.current!) as ToggleComponent | null
if (toggleObj === null) {
return
}
// Add a class to prevent sidebar hover effect after toggle click
toggleObj.on('kt.toggle.change', function () {
// Set animation state
props.sidebarRef.current!.classList.add('animating')
// Wait till animation finishes
setTimeout(function () {
// Remove animation state
props.sidebarRef.current!.classList.remove('animating')
}, 300)
})
}, 600)
}, [toggleRef, props.sidebarRef])
return (
<div className='app-sidebar-logo px-6' id='kt_app_sidebar_logo'>
<Link to='/dashboard'>
{config.layoutType === 'dark-sidebar' ? (
<img
alt='Logo'
src={toAbsoluteUrl('media/logos/default-dark.svg')}
className='h-25px app-sidebar-logo-default'
/>
) : (
<>
<img
alt='Logo'
src={toAbsoluteUrl('media/logos/sidelogo.png')}
className='h-25px app-sidebar-logo-default theme-light-show'
/>
<img
alt='Logo'
src={toAbsoluteUrl('media/logos/default-dark.svg')}
className='h-25px app-sidebar-logo-default theme-dark-show'
/>
</>
)}
<img
alt='Logo'
src={toAbsoluteUrl('media/logos/default-small.svg')}
className='h-20px app-sidebar-logo-minimize'
/>
</Link>
{(appSidebarDefaultMinimizeDesktopEnabled || appSidebarDefaultCollapseDesktopEnabled) && (
<div
ref={toggleRef}
id='kt_app_sidebar_toggle'
className={clsx(
'app-sidebar-toggle btn btn-icon btn-shadow btn-sm btn-color-muted btn-active-color-primary h-30px w-30px position-absolute top-50 start-100 translate-middle rotate',
{active: appSidebarDefaultMinimizeDefault}
)}
data-kt-toggle='true'
data-kt-toggle-state={toggleState}
data-kt-toggle-target='body'
data-kt-toggle-name={`app-sidebar-${toggleType}`}
>
<KTIcon iconName='black-left-line' className='fs-3 rotate-180 ms-1' />
</div>
)}
</div>
)
}
export {SidebarLogo}
@@ -0,0 +1 @@
export * from './Sidebar'
@@ -0,0 +1,30 @@
import {SidebarMenuMain} from './SidebarMenuMain'
const SidebarMenu = () => {
return (
<div className='app-sidebar-menu overflow-hidden flex-column-fluid'>
<div
id='kt_app_sidebar_menu_wrapper'
className='app-sidebar-wrapper hover-scroll-overlay-y my-5'
data-kt-scroll='true'
data-kt-scroll-activate='true'
data-kt-scroll-height='auto'
data-kt-scroll-dependencies='#kt_app_sidebar_logo, #kt_app_sidebar_footer'
data-kt-scroll-wrappers='#kt_app_sidebar_menu'
data-kt-scroll-offset='5px'
data-kt-scroll-save-state='true'
>
<div
className='menu menu-column menu-rounded menu-sub-indention px-3'
id='#kt_app_sidebar_menu'
data-kt-menu='true'
data-kt-menu-expand='false'
>
<SidebarMenuMain />
</div>
</div>
</div>
)
}
export {SidebarMenu}
@@ -0,0 +1,53 @@
import {FC} from 'react'
import clsx from 'clsx'
import {Link} from 'react-router-dom'
import {useLocation} from 'react-router'
import {checkIsActive, KTIcon, WithChildren} from '../../../../helpers'
import {useLayout} from '../../../core'
type Props = {
to: string
title: string
icon?: string
fontIcon?: string
hasBullet?: boolean
}
const SidebarMenuItem: FC<Props & WithChildren> = ({
children,
to,
title,
icon,
fontIcon,
hasBullet = false,
}) => {
const {pathname} = useLocation()
const isActive = checkIsActive(pathname, to)
const {config} = useLayout()
const {app} = config
return (
<div className='menu-item'>
<Link className={clsx('menu-link without-sub', {active: isActive})} to={to}>
{hasBullet && (
<span className='menu-bullet'>
<span className='bullet bullet-dot'></span>
</span>
)}
{icon && app?.sidebar?.default?.menu?.iconType === 'svg' && (
<span className='menu-icon'>
{' '}
<KTIcon iconName={icon} className='fs-2' />
</span>
)}
{fontIcon && app?.sidebar?.default?.menu?.iconType === 'font' && (
<i className={clsx('bi fs-3', fontIcon)}></i>
)}
<span className='menu-title'>{title}</span>
</Link>
{children}
</div>
)
}
export {SidebarMenuItem}
@@ -0,0 +1,57 @@
import React from 'react'
import clsx from 'clsx'
import {useLocation} from 'react-router'
import {checkIsActive, KTIcon, WithChildren} from '../../../../helpers'
import {useLayout} from '../../../core'
type Props = {
to: string
title: string
icon?: string
fontIcon?: string
hasBullet?: boolean
}
const SidebarMenuItemWithSub: React.FC<Props & WithChildren> = ({
children,
to,
title,
icon,
fontIcon,
hasBullet,
}) => {
const {pathname} = useLocation()
const isActive = checkIsActive(pathname, to)
const {config} = useLayout()
const {app} = config
return (
<div
className={clsx('menu-item', {'here show': isActive}, 'menu-accordion')}
data-kt-menu-trigger='click'
>
<span className='menu-link'>
{hasBullet && (
<span className='menu-bullet'>
<span className='bullet bullet-dot'></span>
</span>
)}
{icon && app?.sidebar?.default?.menu?.iconType === 'svg' && (
<span className='menu-icon'>
<KTIcon iconName={icon} className='fs-2' />
</span>
)}
{fontIcon && app?.sidebar?.default?.menu?.iconType === 'font' && (
<i className={clsx('bi fs-3', fontIcon)}></i>
)}
<span className='menu-title'>{title}</span>
<span className='menu-arrow'></span>
</span>
<div className={clsx('menu-sub menu-sub-accordion', {'menu-active-bg': isActive})}>
{children}
</div>
</div>
)
}
export {SidebarMenuItemWithSub}
@@ -0,0 +1,121 @@
import {useIntl} from 'react-intl'
import {KTIcon} from '../../../../helpers'
import {SidebarMenuItemWithSub} from './SidebarMenuItemWithSub'
import {SidebarMenuItem} from './SidebarMenuItem'
const SidebarMenuMain = () => {
const intl = useIntl()
return (
<>
<SidebarMenuItem
to='/dashboard'
icon='element-11'
title={intl.formatMessage({id: 'MENU.DASHBOARD'})}
fontIcon='bi-app-indicator'
/>
{/*<SidebarMenuItem to='/builder' icon='switch' title='Layout Builder' fontIcon='bi-layers' />*/}
<div className='menu-item'>
<div className='menu-content pt-8 pb-2'>
<span className='menu-section text-muted text-uppercase fs-8 ls-1'>Crafted</span>
</div>
</div>
<SidebarMenuItemWithSub
to='/crafted/pages'
title='Pages'
fontIcon='bi-archive'
icon='element-plus'
>
<SidebarMenuItemWithSub to='/crafted/pages/profile' title='Profile' hasBullet={true}>
<SidebarMenuItem to='/crafted/pages/profile/overview' title='Overview' hasBullet={true} />
<SidebarMenuItem to='/crafted/pages/profile/projects' title='Projects' hasBullet={true} />
<SidebarMenuItem
to='/crafted/pages/profile/campaigns'
title='Campaigns'
hasBullet={true}
/>
<SidebarMenuItem
to='/crafted/pages/profile/documents'
title='Documents'
hasBullet={true}
/>
<SidebarMenuItem
to='/crafted/pages/profile/connections'
title='Connections'
hasBullet={true}
/>
</SidebarMenuItemWithSub>
<SidebarMenuItemWithSub to='/crafted/pages/wizards' title='Wizards' hasBullet={true}>
<SidebarMenuItem
to='/crafted/pages/wizards/horizontal'
title='Horizontal'
hasBullet={true}
/>
<SidebarMenuItem to='/crafted/pages/wizards/vertical' title='Vertical' hasBullet={true} />
</SidebarMenuItemWithSub>
</SidebarMenuItemWithSub>
<SidebarMenuItemWithSub
to='/crafted/accounts'
title='Accounts'
icon='profile-circle'
fontIcon='bi-person'
>
<SidebarMenuItem to='/crafted/account/overview' title='Overview' hasBullet={true} />
<SidebarMenuItem to='/crafted/account/settings' title='Settings' hasBullet={true} />
</SidebarMenuItemWithSub>
<SidebarMenuItemWithSub to='/error' title='Errors' fontIcon='bi-sticky' icon='cross-circle'>
<SidebarMenuItem to='/error/404' title='Error 404' hasBullet={true} />
<SidebarMenuItem to='/error/500' title='Error 500' hasBullet={true} />
</SidebarMenuItemWithSub>
<SidebarMenuItemWithSub
to='/crafted/widgets'
title='Widgets'
icon='element-7'
fontIcon='bi-layers'
>
<SidebarMenuItem to='/crafted/widgets/lists' title='Lists' hasBullet={true} />
<SidebarMenuItem to='/crafted/widgets/statistics' title='Statistics' hasBullet={true} />
<SidebarMenuItem to='/crafted/widgets/charts' title='Charts' hasBullet={true} />
<SidebarMenuItem to='/crafted/widgets/mixed' title='Mixed' hasBullet={true} />
<SidebarMenuItem to='/crafted/widgets/tables' title='Tables' hasBullet={true} />
<SidebarMenuItem to='/crafted/widgets/feeds' title='Feeds' hasBullet={true} />
</SidebarMenuItemWithSub>
<div className='menu-item'>
<div className='menu-content pt-8 pb-2'>
<span className='menu-section text-muted text-uppercase fs-8 ls-1'>Apps</span>
</div>
</div>
<SidebarMenuItemWithSub
to='/apps/chat'
title='Chat'
fontIcon='bi-chat-left'
icon='message-text-2'
>
<SidebarMenuItem to='/apps/chat/private-chat' title='Private Chat' hasBullet={true} />
<SidebarMenuItem to='/apps/chat/group-chat' title='Group Chart' hasBullet={true} />
<SidebarMenuItem to='/apps/chat/drawer-chat' title='Drawer Chart' hasBullet={true} />
</SidebarMenuItemWithSub>
<SidebarMenuItem
to='/apps/user-management/users'
icon='abstract-28'
title='User management'
fontIcon='bi-layers'
/>
{/*<div className='menu-item'>*/}
{/* <a*/}
{/* target='_blank'*/}
{/* className='menu-link'*/}
{/* href={import.meta.env.VITE_APP_PREVIEW_DOCS_URL + '/changelog'}*/}
{/* >*/}
{/* <span className='menu-icon'>*/}
{/* <KTIcon iconName='code' className='fs-2' />*/}
{/* </span>*/}
{/* <span className='menu-title'>Changelog {import.meta.env.VITE_APP_VERSION}</span>*/}
{/* </a>*/}
{/*</div>*/}
</>
)
}
export {SidebarMenuMain}
@@ -0,0 +1,102 @@
/* eslint-disable no-prototype-builtins */
import {useEffect} from 'react'
import {ILayout, useLayout} from '../../core'
import {
ToolbarAccounting,
ToolbarClassic,
ToolbarExtended,
ToolbarReports,
ToolbarSaas,
} from './toolbars'
const Toolbar = () => {
const {config} = useLayout()
useEffect(() => {
updateDOM(config)
document.body.setAttribute('data-kt-app-toolbar-enabled', 'true')
}, [config])
switch (config.app?.toolbar?.layout) {
case 'classic':
return <ToolbarClassic />
case 'accounting':
return <ToolbarAccounting />
case 'extended':
return <ToolbarExtended />
case 'reports':
return <ToolbarReports />
case 'saas':
return <ToolbarSaas />
default:
return <ToolbarClassic />
}
}
const updateDOM = (config: ILayout) => {
let appToolbarSwapAttributes: {[attrName: string]: string} = {}
const appToolbarSwapEnabled = config.app?.toolbar?.swap?.enabled
if (appToolbarSwapEnabled) {
appToolbarSwapAttributes = config.app?.toolbar?.swap?.attributes as {[attrName: string]: string}
}
let appToolbarStickyAttributes: {[attrName: string]: string} = {}
const appToolbarStickyEnabled = config.app?.toolbar?.sticky?.enabled
if (appToolbarStickyEnabled) {
appToolbarStickyAttributes = config.app?.toolbar?.sticky?.attributes as {
[attrName: string]: string
}
let appToolbarMinimizeAttributes: {[attrName: string]: string} = {}
const appToolbarMinimizeEnabled = config.app?.toolbar?.minimize?.enabled
if (appToolbarMinimizeEnabled) {
appToolbarMinimizeAttributes = config.app?.toolbar?.minimize?.attributes as {
[attrName: string]: string
}
}
if (config.app?.toolbar?.fixed?.desktop) {
document.body.setAttribute('data-kt-app-toolbar-fixed', 'true')
}
if (config.app?.toolbar?.fixed?.mobile) {
document.body.setAttribute('data-kt-app-toolbar-fixed-mobile', 'true')
}
setTimeout(() => {
const toolbarElement = document.getElementById('kt_app_toolbar')
// toolbar
if (toolbarElement) {
const toolbarAttributes = toolbarElement
.getAttributeNames()
.filter((t) => t.indexOf('data-') > -1)
toolbarAttributes.forEach((attr) => toolbarElement.removeAttribute(attr))
if (appToolbarSwapEnabled) {
for (const key in appToolbarSwapAttributes) {
if (appToolbarSwapAttributes.hasOwnProperty(key)) {
toolbarElement.setAttribute(key, appToolbarSwapAttributes[key])
}
}
}
if (appToolbarStickyEnabled) {
for (const key in appToolbarStickyAttributes) {
if (appToolbarStickyAttributes.hasOwnProperty(key)) {
toolbarElement.setAttribute(key, appToolbarStickyAttributes[key])
}
}
}
if (appToolbarMinimizeEnabled) {
for (const key in appToolbarMinimizeAttributes) {
if (appToolbarMinimizeAttributes.hasOwnProperty(key)) {
toolbarElement.setAttribute(key, appToolbarMinimizeAttributes[key])
}
}
}
}
}, 0)
}
}
export {Toolbar}
@@ -0,0 +1,51 @@
import clsx from 'clsx'
import {ToolbarType, useLayout} from '../../core'
import {Toolbar} from './Toolbar'
import {PageTitleWrapper} from './page-title'
const ToolbarWrapper = () => {
const {config, classes} = useLayout()
if (!config.app?.toolbar?.display) {
return null
}
const isPageTitleVisible = showPageTitle(
config.app?.toolbar?.layout,
config.app?.pageTitle?.display
)
return (
<div
id='kt_app_toolbar'
className={clsx('app-toolbar', classes.toolbar.join(' '), config?.app?.toolbar?.class)}
>
<div
id='kt_app_toolbar_container'
className={clsx(
'app-container',
classes.toolbarContainer.join(' '),
config.app?.toolbar?.containerClass,
config.app?.toolbar?.minimize?.enabled ? 'app-toolbar-minimize' : '',
{
'container-fluid': config.app?.toolbar?.container === 'fluid',
'container-xxl': config.app?.toolbar?.container === 'fixed',
}
)}
>
{isPageTitleVisible && <PageTitleWrapper />}
<Toolbar />
</div>
</div>
)
}
const showPageTitle = (appToolbarLayout?: ToolbarType, appPageTitleDisplay?: boolean): boolean => {
const viewsWithPageTitles = ['classic', 'reports', 'saas']
if (!appToolbarLayout || !appPageTitleDisplay) {
return false
}
return appPageTitleDisplay && viewsWithPageTitles.some((t) => t === appToolbarLayout)
}
export {ToolbarWrapper}
@@ -0,0 +1 @@
export * from './ToolbarWrapper'
@@ -0,0 +1,86 @@
import clsx from 'clsx'
import {Link} from 'react-router-dom'
import {useLayout} from '../../../core'
import {usePageData} from '../../../core/PageData'
const PageTitle = () => {
const {pageTitle, pageDescription, pageBreadcrumbs} = usePageData()
const {config, classes} = useLayout()
const appPageTitleDirection = config.app?.pageTitle?.direction
return (
<div
id='kt_page_title'
data-kt-swapper='true'
data-kt-swapper-mode='prepend'
data-kt-swapper-parent="{default: '#kt_content_container', 'lg': '#kt_toolbar_container'}"
className={clsx(
'page-title d-flex flex-wrap me-3',
classes.pageTitle.join(' '),
config.app?.pageTitle?.class,
{
'flex-column justify-content-center': appPageTitleDirection === 'column',
'align-items-center': appPageTitleDirection !== 'column',
}
)}
>
{/* begin::Title */}
{config.app?.pageTitle?.display && pageTitle && (
<h1
className={clsx('page-heading d-flex text-gray-900 fw-bold fs-3 my-0', {
'flex-column justify-content-center': appPageTitleDirection,
'align-items-center': !appPageTitleDirection,
})}
>
{pageTitle}
{pageDescription && config.app?.pageTitle && config.app?.pageTitle?.description && (
<span
className={clsx('page-desc text-muted fs-7 fw-semibold', {
'pt-2': appPageTitleDirection === 'column',
})}
>
{config.app?.pageTitle?.direction === 'row' && (
<span className='h-20px border-1 border-gray-300 border-start ms-3 mx-2'></span>
)}
{pageDescription}{' '}
</span>
)}
</h1>
)}
{/* end::Title */}
{pageBreadcrumbs &&
pageBreadcrumbs.length > 0 &&
config.app?.pageTitle &&
config.app?.pageTitle?.breadCrumb && (
<>
{config.app?.pageTitle?.direction === 'row' && (
<span className='h-20px border-gray-300 border-start mx-4'></span>
)}
<ul className='breadcrumb breadcrumb-separatorless fw-semibold fs-7 my-0'>
{Array.from(pageBreadcrumbs).map((item, index) => (
<li
className={clsx('breadcrumb-item', {
'text-gray-900': !item.isSeparator && item.isActive,
'text-muted': !item.isSeparator && !item.isActive,
})}
key={`${item.path}${index}`}
>
{!item.isSeparator ? (
<Link className='text-muted text-hover-primary' to={item.path}>
{item.title}
</Link>
) : (
<span className='bullet bg-gray-500 w-5px h-2px'></span>
)}
</li>
))}
<li className='breadcrumb-item text-gray-900'>{pageTitle}</li>
</ul>
</>
)}
</div>
)
}
export {PageTitle}
@@ -0,0 +1,13 @@
import {useLayout} from '../../../core'
import {PageTitle} from './PageTitle'
const PageTitleWrapper = () => {
const {config} = useLayout()
if (!config.app?.pageTitle?.display) {
return null
}
return <PageTitle />
}
export {PageTitleWrapper}
@@ -0,0 +1 @@
export * from './PageTitleWrapper'
@@ -0,0 +1,139 @@
import {FC, useEffect, useState} from 'react'
import {KTIcon} from '../../../../helpers'
const ToolbarAccounting: FC = () => {
const [progress, setProgress] = useState<string>('1')
const [filter, setFilter] = useState<string>('1')
useEffect(() => {
document.body.setAttribute('data-kt-app-toolbar-fixed', 'true')
}, [])
return (
<>
<div className='d-flex align-items-center me-5'>
{/* begin::Input group */}
<div className='d-flex align-items-center flex-shrink-0'>
{/* begin::Label */}
<span className='fs-7 text-gray-700 fw-bold pe-3 d-none d-md-block'>Actions:</span>
{/* end::Label */}
{/* begin::Actions */}
<div className='d-flex flex-shrink-0'>
{/* begin::Button */}
<div
data-bs-toggle='tooltip'
data-bs-placement='top'
data-bs-trigger='hover'
title='Add a team member'
>
<a href='#' className='btn btn-sm btn-icon btn-active-color-success'>
<KTIcon iconName='plus-square' className='fs-2x' />
</a>
</div>
{/* end::Button */}
{/* begin::Button */}
<div
data-bs-toggle='tooltip'
data-bs-placement='top'
data-bs-trigger='hover'
title='Create new account'
>
<a href='#' className='btn btn-sm btn-icon btn-active-color-success'>
<KTIcon iconName='minus-square' className='fs-2x' />
</a>
</div>
{/* end::Button */}
{/* begin::Button */}
<div
data-bs-toggle='tooltip'
data-bs-placement='top'
data-bs-trigger='hover'
title='Invite friends'
>
<a href='#' className='btn btn-sm btn-icon btn-active-color-success'>
<KTIcon iconName='dots-square' className='fs-2x' />
</a>
</div>
{/* end::Button */}
</div>
{/* end::Actions */}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className='d-flex align-items-center flex-shrink-0'>
{/* begin::Desktop separartor */}
<div className='bullet bg-secondary h-35px w-1px mx-5'></div>
{/* end::Desktop separartor */}
{/* begin::Label */}
<span className='fs-7 text-gray-700 fw-bold pe-4 ps-1 d-none d-md-block'>Progress:</span>
{/* end::Label */}
<div className='progress w-100px w-xl-150px w-xxl-300px h-25px bg-light-success'>
<div
className='progress-bar rounded bg-success fs-7 fw-bold'
role='progressbar'
style={{width: '72%'}}
aria-valuenow={72}
aria-valuemin={0}
aria-valuemax={100}
>
72%
</div>
</div>
</div>
{/* end::Input group */}
{/* end::Toolbar start */}
</div>
{/* begin::Toolbar end */}
<div className='d-flex align-items-center'>
{/* begin::Input group */}
<div className='me-3'>
{/* begin::Select */}
<select
className='form-select form-select-sm form-select-solid'
data-control='select2'
data-placeholder='Latest'
data-hide-search='true'
value={progress}
onChange={(e) => setProgress(e.target.value)}
>
<option value=''></option>
<option value='1'>Today 16 Feb</option>
<option value='2'>In Progress</option>
<option value='3'>Done</option>
</select>
{/* end::Select */}
</div>
{/* end::Input group- */}
{/* begin::Input group- */}
<div className='m-0'>
{/* begin::Select */}
<select
className='form-select form-select-sm form-select-solid w-md-125px'
data-control='select2'
data-placeholder='Filters'
data-hide-search='true'
value={filter}
onChange={(e) => setFilter(e.target.value)}
>
<option value=''></option>
<option value='1'>Filters</option>
<option value='2'>In Progress</option>
<option value='3'>Done</option>
</select>
{/* end::Content */}
</div>
{/* end::Input group- */}
</div>
</>
)
}
export {ToolbarAccounting}
@@ -0,0 +1,66 @@
import clsx from 'clsx'
import {useState} from 'react'
import {KTIcon} from '../../../../helpers'
import {CreateAppModal, Dropdown1} from '../../../../partials'
import {useLayout} from '../../../core'
const ToolbarClassic = () => {
const {config} = useLayout()
const [showCreateAppModal, setShowCreateAppModal] = useState<boolean>(false)
const daterangepickerButtonClass = config.app?.toolbar?.fixed?.desktop
? 'btn-light'
: 'bg-body btn-color-gray-700 btn-active-color-primary'
return (
<div className='d-flex align-items-center gap-2 gap-lg-3'>
{config.app?.toolbar?.filterButton && (
<div className='m-0'>
<a
href='#'
className={clsx('btn btn-sm btn-flex fw-bold', daterangepickerButtonClass)}
data-kt-menu-trigger='click'
data-kt-menu-placement='bottom-end'
>
<KTIcon iconName='filter' className='fs-6 text-muted me-1' />
Filter
</a>
<Dropdown1 />
</div>
)}
{config.app?.toolbar?.daterangepickerButton && (
<div
data-kt-daterangepicker='true'
data-kt-daterangepicker-opens='left'
className={clsx(
'btn btn-sm fw-bold d-flex align-items-center px-4',
daterangepickerButtonClass
)}
>
<div className='text-gray-600 fw-bold'>Loading date range...</div>
<KTIcon iconName='calendar-8' className='fs-1 ms-2 me-0' />
</div>
)}
{config.app?.toolbar?.secondaryButton && (
<a href='#' className='btn btn-sm btn-flex btn-light fw-bold'>
Filter
</a>
)}
{config.app?.toolbar?.primaryButton && (
<a
href='#'
onClick={() => setShowCreateAppModal(true)}
className='btn btn-sm fw-bold btn-primary'
>
Create
</a>
)}
<CreateAppModal show={showCreateAppModal} handleClose={() => setShowCreateAppModal(false)} />
</div>
)
}
export {ToolbarClassic}
@@ -0,0 +1,154 @@
import {FC, useEffect, useState} from 'react'
import {KTIcon, toAbsoluteUrl} from '../../../../helpers'
const ToolbarExtended: FC = () => {
const [progress, setProgress] = useState<string>('1')
const [search, setSearch] = useState<string>('')
useEffect(() => {
document.body.setAttribute('data-kt-app-toolbar-fixed', 'true')
}, [])
return (
<>
<div className='d-flex align-items-center flex-shrink-0 me-5'>
{/* begin::Label */}
<span className='fs-7 fw-bold text-gray-700 pe-4 d-none d-md-block'>Team:</span>
{/* end::Label */}
{/* begin::Users */}
<div className='symbol-group symbol-hover flex-shrink-0 me-2'>
{/* begin::User */}
<div className='symbol symbol-circle symbol-35px'>
<div className='symbol-label fw-bold bg-warning text-inverse-warning'>A</div>
</div>
{/* end::User */}
{/* begin::User */}
<div className='symbol symbol-circle symbol-35px'>
<img src={toAbsoluteUrl('media/avatars/300-1.jpg')} alt='' />
</div>
{/* end::User */}
{/* begin::User */}
<div className='symbol symbol-circle symbol-35px'>
<img src={toAbsoluteUrl('media/avatars/300-2.jpg')} alt='' />
</div>
{/* end::User */}
{/* begin::User */}
<div className='symbol symbol-circle symbol-35px'>
<div className='symbol-label fw-bold bg-primary text-inverse-primary'>S</div>
</div>
{/* end::User */}
{/* begin::User */}
<div className='symbol symbol-circle symbol-35px'>
<img src={toAbsoluteUrl('media/avatars/300-5.jpg')} alt='' />
</div>
{/* end::User */}
{/* begin::User */}
<div className='symbol symbol-circle symbol-35px'>
<div className='symbol-label fw-bold bg-danger text-inverse-danger'>P</div>
</div>
{/* end::User */}
{/* begin::User */}
<div className='symbol symbol-circle symbol-35px'>
<img src={toAbsoluteUrl('media/avatars/300-20.jpg')} alt='' />
</div>
{/* end::User */}
</div>
{/* end::Users */}
{/* begin::Button */}
<div
data-bs-toggle='tooltip'
data-bs-placement='top'
data-bs-trigger='hover'
title='Invite a team member'
>
<a href='#' className='btn btn-sm btn-icon'>
<KTIcon iconName='plus-square' className='fs-2hx text-success' />
</a>
</div>
</div>
{/* end::Button */}
{/* end::Toolbar start */}
{/* begin::Toolbar end */}
<div className='d-flex align-items-center overflow-auto'>
{/* begin::Search */}
<div className='position-relative my-1'>
<KTIcon
iconName='magnifier'
className='fs-3 text-gray-500 position-absolute top-50 translate-middle ps-10'
/>
<input
type='text'
className='form-control form-control-sm form-control-solid w-150px ps-10'
name='Search Team'
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder='Search Team'
/>
</div>
{/* end::Search */}
{/* begin::Separartor */}
<div className='bullet bg-secondary h-35px w-1px mx-6'></div>
{/* end::Separartor */}
{/* begin::Label */}
<span className='fs-7 fw-bold text-gray-700 flex-shrink-0 pe-4 d-none d-md-block'>
Sort By:
</span>
{/* end::Label */}
{/* begin::Select */}
<select
className='form-select form-select-sm w-125px form-select-solid me-6'
data-control='select2'
data-placeholder='Latest'
data-hide-search='true'
value={progress}
onChange={(e) => setProgress(e.target.value)}
>
<option value=''></option>
<option value='1'>Latest</option>
<option value='2'>In Progress</option>
<option value='3'>Done</option>
</select>
{/* end::Select */}
{/* begin::Actions */}
<div className='d-flex align-items-center'>
<button
type='button'
className='btn btn-sm btn-icon btn-light-primary me-3'
data-bs-toggle='tooltip'
data-bs-placement='top'
title='Enable grid view'
>
<KTIcon iconName='element-11' className='fs-3 text-primary' />
</button>
<button
type='button'
className='btn btn-sm btn-icon btn-light'
data-bs-toggle='tooltip'
data-bs-placement='top'
title='Enable row view'
>
<KTIcon iconName='row-horizontal' className='fs-3 text-gray-500' />
</button>
</div>
{/* end::Actions */}
</div>
</>
)
}
export {ToolbarExtended}
@@ -0,0 +1,115 @@
import {useEffect, useState} from 'react'
import {KTIcon} from '../../../../helpers'
const ToolbarReports = () => {
const [progress, setProgress] = useState<string>('1')
useEffect(() => {
document.body.setAttribute('data-kt-app-toolbar-fixed', 'true')
}, [])
return (
<div className='d-flex align-items-center overflow-auto'>
{/* begin::Wrapper */}
<div className='d-flex align-items-center flex-shrink-0'>
{/* begin::Label */}
<span className='fs-7 fw-bold text-gray-700 flex-shrink-0 pe-4 d-none d-md-block'>
Filter By:
</span>
{/* end::Label */}
<div className='flex-shrink-0 '>
<ul className='nav'>
<li className='nav-item'>
<a
className='nav-link btn btn-sm btn-color-muted btn-active-color-primary btn-active-light active fw-semibold fs-7 px-4 me-1'
data-bs-toggle='tab'
href='#'
>
Today
</a>
</li>
<li className='nav-item'>
<a
className='nav-link btn btn-sm btn-color-muted btn-active-color-primary btn-active-light fw-semibold fs-7 px-4 me-1'
data-bs-toggle='tab'
href=''
>
Week
</a>
</li>
<li className='nav-item'>
<a
className='nav-link btn btn-sm btn-color-muted btn-active-color-primary btn-active-light fw-semibold fs-7 px-4'
data-bs-toggle='tab'
href='#'
>
Day
</a>
</li>
</ul>
</div>
</div>
{/* end::Wrapper */}
{/* begin::Separartor */}
<div className='bullet bg-secondary h-35px w-1px mx-5'></div>
{/* end::Separartor */}
{/* begin::Wrapper */}
<div className='d-flex align-items-center'>
{/* begin::Label */}
<span className='fs-7 fw-bold text-gray-700 flex-shrink-0 pe-4 d-none d-md-block'>
Sort By:
</span>
{/* end::Label */}
{/* begin::Select */}
<select
className='form-select form-select-sm w-md-125px form-select-solid'
data-control='select2'
data-placeholder='Latest'
data-hide-search='true'
value={progress}
onChange={(e) => setProgress(e.target.value)}
>
<option value=''></option>
<option value='1'>Latest</option>
<option value='2'>In Progress</option>
<option value='3'>Done</option>
</select>
{/* end::Select */}
{/* begin::Actions */}
<div className='d-flex align-items-center ms-3'>
<button
type='button'
className='btn btn-sm btn-icon btn-light-primary me-3'
data-bs-toggle='tooltip'
data-bs-placement='top'
title='Enable grid view'
>
<KTIcon iconName='element-11' className='fs-2 text-primary' />
</button>
<button
type='button'
className='btn btn-sm btn-icon btn-light'
data-bs-toggle='tooltip'
data-bs-placement='top'
title='Enable row view'
>
<KTIcon iconName='abstract-14' className=' fs-2 text-gray-500' />
</button>
</div>
{/* end::Actions */}
</div>
{/* end::Wrapper */}
</div>
)
}
export {ToolbarReports}
@@ -0,0 +1,126 @@
import {FC, useEffect, useState} from 'react'
import {KTIcon} from '../../../../helpers'
const ToolbarSaas: FC = () => {
const [progress, setProgress] = useState<string>('1')
useEffect(() => {
document.body.setAttribute('data-kt-app-toolbar-fixed', 'true')
}, [])
return (
<div className='d-flex align-items-center gap-2'>
{/* begin::Action wrapper */}
<div className='d-flex align-items-center'>
{/* begin::Label */}
<span className='fs-7 fw-bold text-gray-700 pe-4 text-nowrap d-none d-md-block'>
Sort By:
</span>
{/* end::Label */}
{/* begin::Select */}
<select
className='form-select form-select-sm form-select-solid w-100px w-xxl-125px'
data-control='select2'
data-placeholder='Latest'
data-hide-search='true'
onChange={(e) => setProgress(e.target.value)}
value={progress}
>
<option value=''></option>
<option value='1'>Latest</option>
<option value='2'>In Progress</option>
<option value='3'>Done</option>
</select>
{/* end::Select */}
</div>
{/* end::Action wrapper */}
{/* begin::Action wrapper */}
<div className='d-flex align-items-center'>
{/* begin::Separartor */}
<div className='bullet bg-secondary h-35px w-1px mx-5'></div>
{/* end::Separartor */}
{/* begin::Label */}
<span className='fs-7 text-gray-700 fw-bold'>Impact Level:</span>
{/* end::Label */}
{/* begin::NoUiSlider */}
<div className='d-flex align-items-center ps-4'>
<div
id='kt_app_toolbar_slider'
className='noUi-target noUi-target-success w-75px w-xxl-150px noUi-sm'
></div>
<span
id='kt_app_toolbar_slider_value'
className='d-flex flex-center bg-light-success rounded-circle w-35px h-35px ms-4 fs-7 fw-bold text-success'
data-bs-toggle='tooltip'
data-bs-placement='top'
title='Set impact level'
></span>
</div>
{/* end::NoUiSlider */}
{/* begin::Separartor */}
<div className='bullet bg-secondary h-35px w-1px mx-5'></div>
{/* end::Separartor */}
</div>
{/* end::Action wrapper */}
{/* begin::Action wrapper */}
<div className='d-flex align-items-center'>
{/* begin::Label */}
<span className='fs-7 text-gray-700 fw-bold pe-3 d-none d-md-block'>Quick Tools:</span>
{/* end::Label */}
{/* begin::Actions */}
<div className='d-flex'>
{/* begin::Action */}
<a
href='#'
className='btn btn-sm btn-icon btn-icon-muted btn-active-icon-success'
data-bs-toggle='tooltip'
data-bs-trigger='hover'
data-bs-placement='top'
title='Add new page'
>
<KTIcon iconName='files' className='fs-2x' />
</a>
{/* end::Action */}
{/* begin::Action */}
<a
href='#'
className='btn btn-sm btn-icon btn-icon-muted btn-active-icon-success'
data-bs-toggle='tooltip'
data-bs-trigger='hover'
data-bs-placement='top'
title='Add new category'
>
<KTIcon iconName='add-files' className='fs-2x' />
</a>
{/* end::Action */}
{/* begin::Action */}
<a
href='#'
className='btn btn-sm btn-icon btn-icon-muted btn-active-icon-success'
data-bs-toggle='tooltip'
data-bs-trigger='hover'
data-bs-placement='top'
title='Add new section'
>
<KTIcon iconName='search-list' className='fs-2x' />
</a>
{/* end::Action */}
</div>
{/* end::Actions */}
</div>
{/* end::Action wrapper */}
</div>
)
}
export {ToolbarSaas}
@@ -0,0 +1,5 @@
export * from './ToolbarAccounting'
export * from './ToolbarClassic'
export * from './ToolbarExtended'
export * from './ToolbarReports'
export * from './ToolbarSaas'
@@ -0,0 +1,77 @@
import {
createContext,
Dispatch,
FC,
SetStateAction,
useContext,
useEffect,
useState,
} from 'react'
import {WithChildren} from '../../helpers'
const MetronicSplashScreenContext = createContext<Dispatch<SetStateAction<number>> | undefined>(
undefined
)
const MetronicSplashScreenProvider: FC<WithChildren> = ({children}) => {
const [count, setCount] = useState(0)
const visible = count > 0
useEffect(() => {
// Show SplashScreen
if (visible) {
document.body.classList.remove('page-loading')
return () => {
document.body.classList.add('page-loading')
}
}
// Hide SplashScreen
let timeout: number
if (!visible) {
timeout = window.setTimeout(() => {
document.body.classList.add('page-loading')
}, 3000)
}
return () => {
clearTimeout(timeout)
}
}, [visible])
return (
<MetronicSplashScreenContext.Provider value={setCount}>
{children}
</MetronicSplashScreenContext.Provider>
)
}
const LayoutSplashScreen: FC<{visible?: boolean}> = ({visible = true}) => {
// Everything are ready - remove splashscreen
const setCount = useContext(MetronicSplashScreenContext)
useEffect(() => {
if (!visible) {
return
}
if (setCount) {
setCount((prev) => {
return prev + 1
})
}
return () => {
if (setCount) {
setCount((prev) => {
return prev - 1
})
}
}
}, [setCount, visible])
return null
}
export {MetronicSplashScreenProvider, LayoutSplashScreen}
+99
View File
@@ -0,0 +1,99 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable react-refresh/only-export-components */
/* eslint-disable @typescript-eslint/no-unused-vars */
import {FC, createContext, useContext, useEffect, useState} from 'react'
import {WithChildren} from '../../helpers'
export interface PageLink {
title: string
path: string
isActive: boolean
isSeparator?: boolean
}
export interface PageDataContextModel {
pageTitle?: string
setPageTitle: (_title: string) => void
pageDescription?: string
setPageDescription: (_description: string) => void
pageBreadcrumbs?: Array<PageLink>
setPageBreadcrumbs: (_breadcrumbs: Array<PageLink>) => void
}
const PageDataContext = createContext<PageDataContextModel>({
setPageTitle: (_title: string) => {},
setPageBreadcrumbs: (_breadcrumbs: Array<PageLink>) => {},
setPageDescription: (_description: string) => {},
})
const PageDataProvider: FC<WithChildren> = ({children}) => {
const [pageTitle, setPageTitle] = useState<string>('')
const [pageDescription, setPageDescription] = useState<string>('')
const [pageBreadcrumbs, setPageBreadcrumbs] = useState<Array<PageLink>>([])
const value: PageDataContextModel = {
pageTitle,
setPageTitle,
pageDescription,
setPageDescription,
pageBreadcrumbs,
setPageBreadcrumbs,
}
return <PageDataContext.Provider value={value}>{children}</PageDataContext.Provider>
}
function usePageData() {
return useContext(PageDataContext)
}
type Props = {
description?: string
breadcrumbs?: Array<PageLink>
}
const PageTitle: FC<Props & WithChildren> = ({children, description, breadcrumbs}) => {
const {setPageTitle, setPageDescription, setPageBreadcrumbs} = usePageData()
useEffect(() => {
if (children) {
setPageTitle(children.toString())
}
return () => {
setPageTitle('')
}
}, [children])
useEffect(() => {
if (description) {
setPageDescription(description)
}
return () => {
setPageDescription('')
}
}, [description])
useEffect(() => {
if (breadcrumbs) {
setPageBreadcrumbs(breadcrumbs)
}
return () => {
setPageBreadcrumbs([])
}
}, [breadcrumbs])
return <></>
}
const PageDescription: FC<WithChildren> = ({children}) => {
const {setPageDescription} = usePageData()
useEffect(() => {
if (children) {
setPageDescription(children.toString())
}
return () => {
setPageDescription('')
}
}, [children])
return <></>
}
export {PageDescription, PageTitle, PageDataProvider, usePageData}
+139
View File
@@ -0,0 +1,139 @@
import {ILayout} from './_Models'
export const DefaultConfig: ILayout = {
layoutType: 'dark-sidebar',
main: {
componentName: 'main',
type: 'default',
pageBgWhite: false,
iconType: 'duotone',
},
app: {
general: {
componentName: 'general',
evolution: true,
layoutType: 'default',
mode: 'light',
rtl: false,
primaryColor: '#50CD89',
pageBgWhite: false,
pageWidth: 'default',
},
header: {
componentName: 'header',
display: true,
default: {
container: 'fluid',
containerClass: 'd-flex align-items-stretch justify-content-between',
fixed: {
desktop: true,
mobile: false,
},
content: 'menu',
menu: {
display: true,
iconType: 'svg',
},
},
},
sidebar: {
componentName: 'sidebar',
display: true,
default: {
class: 'flex-column',
push: {
header: true,
toolbar: true,
footer: true,
},
drawer: {
enabled: true,
attributes: {
'data-kt-drawer': 'true',
'data-kt-drawer-name': 'app-sidebar',
'data-kt-drawer-activate': '{default: true, lg: false}',
'data-kt-drawer-overlay': 'true',
'data-kt-drawer-width': '225px',
'data-kt-drawer-direction': 'start',
'data-kt-drawer-toggle': '#kt_app_sidebar_mobile_toggle',
},
},
fixed: {
desktop: true,
},
minimize: {
desktop: {
enabled: true,
default: false,
hoverable: true,
},
},
menu: {
iconType: 'svg',
},
},
},
toolbar: {
componentName: 'toolbar',
display: true,
layout: 'classic',
class: 'py-3 py-lg-6',
container: 'fluid',
containerClass: 'd-flex flex-stack',
fixed: {
desktop: false,
mobile: false,
},
// custom settings,
filterButton: true,
daterangepickerButton: false,
primaryButton: true,
primaryButtonLabel: 'Create',
primaryButtonModal: 'create-app',
},
pageTitle: {
componentName: 'page-title',
display: true,
breadCrumb: true,
description: false,
direction: 'column',
},
content: {
componentName: 'content',
container: 'fluid',
},
footer: {
componentName: 'footer',
display: true,
container: 'fluid',
containerClass: 'd-flex flex-column flex-md-row flex-center flex-md-stack py-3',
fixed: {
desktop: false,
mobile: false,
},
},
pageLoader: {
componentName: 'page-loader',
type: 'none',
logoImage: 'sidelogo.png',
logoClass: 'mh-75px',
},
},
illustrations: {
componentName: 'illustrations',
set: 'sketchy-1',
},
scrolltop: {
componentName: 'scrolltop',
display: true,
},
engage: {
componentName: 'engage',
demos: {
enabled: true,
},
purchase: {
enabled: false,
},
},
}
+113
View File
@@ -0,0 +1,113 @@
/* eslint-disable react-refresh/only-export-components */
/* eslint-disable @typescript-eslint/no-unused-vars */
import {FC, createContext, useContext, useState, useEffect} from 'react'
import {DefaultConfig} from './_LayoutConfig'
import {
setLayoutIntoLocalStorage,
getEmptyCssClasses,
getEmptyCSSVariables,
getEmptyHTMLAttributes,
LayoutSetup,
} from './_LayoutSetup'
import {
ILayout,
ILayoutCSSVariables,
ILayoutCSSClasses,
ILayoutHTMLAttributes,
LayoutType,
ToolbarType,
} from './_Models'
import {WithChildren} from '../../helpers'
export interface LayoutContextModel {
config: ILayout
classes: ILayoutCSSClasses
attributes: ILayoutHTMLAttributes
cssVariables: ILayoutCSSVariables
setLayout: (config: LayoutSetup) => void
setLayoutType: (layoutType: LayoutType) => void
setToolbarType: (toolbarType: ToolbarType) => void
}
const LayoutContext = createContext<LayoutContextModel>({
config: DefaultConfig,
classes: getEmptyCssClasses(),
attributes: getEmptyHTMLAttributes(),
cssVariables: getEmptyCSSVariables(),
setLayout: (_config: LayoutSetup) => {},
setLayoutType: (_layoutType: LayoutType) => {},
setToolbarType: (_toolbarType: ToolbarType) => {},
})
const enableSplashScreen = () => {
const splashScreen = document.getElementById('splash-screen')
if (splashScreen) {
splashScreen.style.setProperty('display', 'flex')
}
}
const disableSplashScreen = () => {
const splashScreen = document.getElementById('splash-screen')
if (splashScreen) {
splashScreen.style.setProperty('display', 'none')
}
}
const LayoutProvider: FC<WithChildren> = ({children}) => {
const [config, setConfig] = useState(LayoutSetup.config)
const [classes, setClasses] = useState(LayoutSetup.classes)
const [attributes, setAttributes] = useState(LayoutSetup.attributes)
const [cssVariables, setCSSVariables] = useState(LayoutSetup.cssVariables)
const setLayout = (_themeConfig: Partial<ILayout>) => {
enableSplashScreen()
const bodyClasses = Array.from(document.body.classList)
bodyClasses.forEach((cl) => document.body.classList.remove(cl))
const updatedConfig = LayoutSetup.updatePartialConfig(_themeConfig)
setConfig(Object.assign({}, updatedConfig))
setClasses(LayoutSetup.classes)
setAttributes(LayoutSetup.attributes)
setCSSVariables(LayoutSetup.cssVariables)
setTimeout(() => {
disableSplashScreen()
}, 500)
}
const setToolbarType = (toolbarType: ToolbarType) => {
const updatedConfig = {...config}
if (updatedConfig.app?.toolbar) {
updatedConfig.app.toolbar.layout = toolbarType
}
setLayoutIntoLocalStorage(updatedConfig)
window.location.reload()
}
const setLayoutType = (layoutType: LayoutType) => {
const updatedLayout = {...config, layoutType}
setLayoutIntoLocalStorage(updatedLayout)
window.location.reload()
}
const value: LayoutContextModel = {
config,
classes,
attributes,
cssVariables,
setLayout,
setLayoutType,
setToolbarType,
}
useEffect(() => {
disableSplashScreen()
}, [])
return <LayoutContext.Provider value={value}>{children}</LayoutContext.Provider>
}
export {LayoutContext, LayoutProvider}
export function useLayout() {
return useContext(LayoutContext)
}
+245
View File
@@ -0,0 +1,245 @@
import {
ILayout,
ILayoutCSSClasses,
ILayoutCSSVariables,
ILayoutHTMLAttributes,
} from "./_Models";
import { DefaultConfig } from "./_LayoutConfig";
const LAYOUT_CONFIG_KEY =
import.meta.env.VITE_APP_BASE_LAYOUT_CONFIG_KEY || "LayoutConfig";
const getLayoutFromLocalStorage = (): ILayout => {
const ls = localStorage.getItem(LAYOUT_CONFIG_KEY);
if (ls) {
try {
return JSON.parse(ls) as ILayout;
} catch (er) {
console.error(er);
}
}
return DefaultConfig;
};
const setLayoutIntoLocalStorage = (config: ILayout) => {
try {
localStorage.setItem(LAYOUT_CONFIG_KEY, JSON.stringify(config));
} catch (er) {
console.error(er);
}
};
const getEmptyCssClasses = (): ILayoutCSSClasses => {
return {
header: [],
headerContainer: [],
headerMobile: [],
headerMenu: [],
aside: [],
asideMenu: [],
asideToggle: [],
toolbar: [],
toolbarContainer: [],
content: [],
contentContainer: [],
footerContainer: [],
sidebar: [],
pageTitle: [],
pageContainer: [],
};
};
const getEmptyHTMLAttributes = () => {
return {
asideMenu: new Map(),
headerMobile: new Map(),
headerMenu: new Map(),
headerContainer: new Map(),
pageTitle: new Map(),
};
};
const getEmptyCSSVariables = () => {
return {
body: new Map(),
};
};
class LayoutSetup {
public static isLoaded: boolean = false;
public static config: ILayout = getLayoutFromLocalStorage();
public static classes: ILayoutCSSClasses = getEmptyCssClasses();
public static attributes: ILayoutHTMLAttributes = getEmptyHTMLAttributes();
public static cssVariables: ILayoutCSSVariables = getEmptyCSSVariables();
private static initCSSClasses(): void {
LayoutSetup.classes = getEmptyCssClasses();
}
private static initHTMLAttributes(): void {
LayoutSetup.attributes = Object.assign({}, getEmptyHTMLAttributes());
}
private static initCSSVariables(): void {
LayoutSetup.cssVariables = getEmptyCSSVariables();
}
private static initConfig(config: ILayout): ILayout {
let updatedConfig = LayoutSetup.initLayoutSettings(config);
updatedConfig = LayoutSetup.initToolbarSetting(updatedConfig);
return LayoutSetup.initWidthSettings(updatedConfig);
}
private static initLayoutSettings(config: ILayout): ILayout {
const updatedConfig = { ...config };
// clear body classes
document.body.className = "";
// clear body attributes
const bodyAttributes = document.body
.getAttributeNames()
.filter((t) => t.indexOf("data-") > -1);
bodyAttributes.forEach((attr) => document.body.removeAttribute(attr));
document.body.setAttribute("style", "");
document.body.setAttribute("id", "kt_app_body");
document.body.setAttribute("data-kt-app-layout", updatedConfig.layoutType);
document.body.classList.add("app-default");
const pageWidth = updatedConfig.app?.general?.pageWidth;
if (
updatedConfig.layoutType === "light-header" ||
updatedConfig.layoutType === "dark-header"
) {
if (pageWidth === "default") {
const header = updatedConfig.app?.header;
if (header && header.default && header.default.container) {
header.default.container = "fixed";
}
const toolbar = updatedConfig.app?.toolbar;
if (toolbar) {
toolbar.container = "fixed";
}
const content = updatedConfig.app?.content;
if (content) {
content.container = "fixed";
}
const footer = updatedConfig.app?.footer;
if (footer) {
footer.container = "fixed";
}
const updatedApp = {
...updatedConfig.app,
...header,
...toolbar,
...content,
...footer,
};
return { ...updatedConfig, app: updatedApp };
}
}
LayoutSetup.initHeaderSettigs(updatedConfig);
return updatedConfig;
}
private static initToolbarSetting(config: ILayout): ILayout {
const updatedConfig = { ...config };
const appHeaderDefaultContent = updatedConfig.app?.header?.default?.content;
if (appHeaderDefaultContent === "page-title") {
const toolbar = updatedConfig.app?.toolbar;
if (toolbar) {
toolbar.display = false;
const updatedApp = { ...updatedConfig.app, ...toolbar };
return { ...updatedConfig, app: updatedApp };
}
return updatedConfig;
}
const pageTitle = updatedConfig.app?.pageTitle;
if (pageTitle) {
pageTitle.description = false;
pageTitle.breadCrumb = true;
const updatedApp = { ...updatedConfig.app, ...pageTitle };
return { ...updatedConfig, app: updatedApp };
}
return updatedConfig;
}
private static initHeaderSettigs(config: ILayout) {
const container = config.app?.header?.default?.container;
if (container === "fluid") {
this.classes.headerContainer.push("container-fluid");
} else {
this.classes.headerContainer.push("container-xxl");
}
}
private static initWidthSettings(config: ILayout): ILayout {
const updatedConfig = { ...config };
const pageWidth = updatedConfig.app?.general?.pageWidth;
if (!pageWidth || pageWidth === "default") {
return config;
}
const header = updatedConfig.app?.header;
if (header && header.default) {
header.default.container = pageWidth;
}
const toolbar = updatedConfig.app?.toolbar;
if (toolbar) {
toolbar.container = pageWidth;
}
const content = updatedConfig.app?.content;
if (content) {
content.container = pageWidth;
}
const footer = updatedConfig.app?.footer;
if (footer) {
footer.container = pageWidth;
}
const updatedApp = {
...updatedConfig.app,
...header,
...toolbar,
...content,
...footer,
};
return { ...updatedConfig, app: updatedApp };
}
public static updatePartialConfig(fieldsToUpdate: Partial<ILayout>): ILayout {
const config = LayoutSetup.config;
const updatedConfig = { ...config, ...fieldsToUpdate };
LayoutSetup.initCSSClasses();
LayoutSetup.initCSSVariables();
LayoutSetup.initHTMLAttributes();
LayoutSetup.isLoaded = false;
LayoutSetup.config = LayoutSetup.initConfig(
Object.assign({}, updatedConfig)
);
LayoutSetup.isLoaded = true; // remove loading there
return updatedConfig;
}
public static setConfig(config: ILayout): void {
setLayoutIntoLocalStorage(config);
}
public static bootstrap = (() => {
LayoutSetup.updatePartialConfig(LayoutSetup.config);
})();
}
export {
LayoutSetup,
getLayoutFromLocalStorage,
setLayoutIntoLocalStorage,
getEmptyCssClasses,
getEmptyCSSVariables,
getEmptyHTMLAttributes,
};
+258
View File
@@ -0,0 +1,258 @@
export type LayoutType = 'dark-sidebar' | 'light-sidebar' | 'dark-header' | 'light-header'
export type CSSClassesType = {
[key: string]: string[]
}
export type HTMLAttributesType = {
[key: string]: {
[attrName: string]: string | boolean
}
}
export interface ILayoutComponent {
componentName?: string
}
export interface IPageLoader extends ILayoutComponent {
componentName?: 'page-loader'
type?: 'none' | 'default' | 'spinner-message' | 'spinner-logo'
logoImage?: string
logoClass?: string
}
export interface IScrollTop extends ILayoutComponent {
display?: boolean
}
export interface IHeader extends ILayoutComponent {
componentName?: 'header'
display?: boolean
default?: {
container?: 'fluid' | 'fixed'
containerClass?: string
fixed?: {
desktop?: boolean
mobile?: boolean
}
content?: string
menu?: {
display?: boolean
iconType?: 'svg' | 'font'
}
stacked?: boolean
sticky?: {
enabled?: boolean
attributes?: {[attrName: string]: string}
}
minimize?: {
enabled?: boolean
attributes?: {[attrName: string]: string}
}
}
}
export interface ISidebar extends ILayoutComponent {
componentName?: 'sidebar'
display?: boolean
default?: {
class?: string
push?: {
header?: boolean
toolbar?: boolean
footer?: boolean
}
drawer?: {
enabled?: boolean
attributes?: {[attrName: string]: string}
}
sticky?: {
enabled?: boolean
attributes?: {[attrName: string]: string}
}
fixed?: {
desktop?: boolean
}
minimize?: {
desktop?: {
enabled?: boolean
default?: boolean
hoverable?: boolean
}
mobile?: {
enabled?: boolean
default?: boolean
hoverable?: boolean
}
}
menu?: {
iconType?: 'svg' | 'font'
}
collapse?: {
desktop?: {
enabled?: boolean
default?: boolean
}
mobile?: {
enabled?: boolean
default?: boolean
}
}
stacked?: boolean
}
toggle?: boolean
}
export type ToolbarType = 'classic' | 'accounting' | 'extended' | 'reports' | 'saas'
export interface IToolbar extends ILayoutComponent {
componentName?: 'toolbar'
display?: boolean
layout?: ToolbarType
class?: string
container?: 'fixed' | 'fluid'
containerClass?: string
fixed?: {
desktop?: boolean
mobile?: boolean
}
swap?: {
enabled?: boolean
attributes?: {[attrName: string]: string}
}
sticky?: {
enabled?: boolean
attributes?: {[attrName: string]: string}
}
minimize?: {
enabled?: boolean
attributes?: {[attrName: string]: string}
}
// Custom settings
filterButton?: boolean
daterangepickerButton?: boolean
primaryButton?: boolean
primaryButtonLabel?: string
primaryButtonModal?: string
secondaryButton?: boolean
}
export interface IMain extends ILayoutComponent {
type?: 'blank' | 'default' | 'none' // Set layout type: default|blank|none
pageBgWhite?: boolean // Set true if page background color is white
iconType: 'duotone' | 'solid' | 'outline'
}
export interface IIllustrations extends ILayoutComponent {
componentName?: 'illustrations'
set?: 'dozzy-1' | 'sigma-1' | 'sketchy-1' | 'unitedpalms-1'
}
export interface IGeneral extends ILayoutComponent {
componentName?: 'general'
evolution?: boolean
layoutType?: 'default' | 'blank'
mode?: 'light' | 'dark' | 'system'
rtl?: boolean
primaryColor?: string // Used in email templates
pageBgWhite?: boolean // Set true if page background color is white
pageWidth?: 'default' | 'fluid' | 'fixed'
}
export interface IMegaMenu extends ILayoutComponent {
display: boolean
}
export interface ISidebarPanel extends ILayoutComponent {
componentName?: 'sidebar-panel'
display: boolean
}
export interface IContent extends ILayoutComponent {
componentName?: 'content'
container?: 'fixed' | 'fluid'
class?: string
}
export interface IFooter extends ILayoutComponent {
componentName?: 'footer'
display?: boolean
container?: 'fluid' | 'fixed'
containerClass?: string
placement?: string
fixed?: {
desktop?: boolean
mobile?: boolean
}
}
export interface IPageTitle extends ILayoutComponent {
componentName?: 'page-title'
display?: boolean
breadCrumb?: boolean
description?: boolean
direction?: 'row' | 'column'
class?: string
}
export interface IEngage extends ILayoutComponent {
componentName?: 'engage'
demos?: {
enabled?: boolean
}
purchase?: {
enabled?: boolean
}
}
export interface IApp {
general?: IGeneral
header?: IHeader
sidebar?: ISidebar
sidebarPanel?: ISidebarPanel
toolbar?: IToolbar
pageTitle?: IPageTitle
content?: IContent
footer?: IFooter
pageLoader?: IPageLoader
}
export interface ILayout {
layoutType: LayoutType
main?: IMain
app?: IApp
illustrations?: IIllustrations
scrolltop?: IScrollTop
engage?: IEngage
}
export interface ILayoutCSSClasses {
header: Array<string>
headerContainer: Array<string>
headerMobile: Array<string>
headerMenu: Array<string>
aside: Array<string>
asideMenu: Array<string>
asideToggle: Array<string>
sidebar: Array<string>
toolbar: Array<string>
toolbarContainer: Array<string>
content: Array<string>
contentContainer: Array<string>
footerContainer: Array<string>
pageTitle: Array<string>
pageContainer: Array<string>
}
export interface ILayoutHTMLAttributes {
asideMenu: Map<string, string | number | boolean>
headerMobile: Map<string, string | number | boolean>
headerMenu: Map<string, string | number | boolean>
headerContainer: Map<string, string | number | boolean>
pageTitle: Map<string, string | number | boolean>
}
export interface ILayoutCSSVariables {
body: Map<string, string | number | boolean>
}
+6
View File
@@ -0,0 +1,6 @@
export * from './_LayoutConfig'
export * from './_LayoutProvider'
export * from './_Models'
export * from './_LayoutSetup'
export * from './PageData'
export * from './MetronicSplashScreen'