@treeui/vue 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/TAppShell.cjs.map +1 -1
- package/dist/components/TAppShell.js.map +1 -1
- package/dist/components/TAppShell.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TAppShell.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TAppShell.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/TAppShell.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/TDatePicker.cjs.map +1 -1
- package/dist/components/TDatePicker.js.map +1 -1
- package/dist/components/TDatePicker.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TDatePicker.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TDatePicker.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/TDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/TDateTimePicker.cjs.map +1 -1
- package/dist/components/TDateTimePicker.js.map +1 -1
- package/dist/components/TDateTimePicker.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TDateTimePicker.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TDateTimePicker.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/TDateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/TDrawer.cjs.map +1 -1
- package/dist/components/TDrawer.js.map +1 -1
- package/dist/components/TDrawer.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TDrawer.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TDrawer.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/TDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/TIcon.cjs.map +1 -1
- package/dist/components/TIcon.js.map +1 -1
- package/dist/components/TIcon.vue.d.ts.map +1 -1
- package/dist/components/TIcon.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TIcon.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TIcon.vue_vue_type_script_setup_true_lang.js +18 -17
- package/dist/components/TIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/TModal.cjs.map +1 -1
- package/dist/components/TModal.js.map +1 -1
- package/dist/components/TModal.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TModal.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TModal.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/TModal.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/TMultiSelect.cjs.map +1 -1
- package/dist/components/TMultiSelect.js.map +1 -1
- package/dist/components/TMultiSelect.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TMultiSelect.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TMultiSelect.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/TMultiSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/TNavMenu.cjs.map +1 -1
- package/dist/components/TNavMenu.js.map +1 -1
- package/dist/components/TNavMenu.vue.d.ts +7 -2
- package/dist/components/TNavMenu.vue.d.ts.map +1 -1
- package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.js +50 -49
- package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/TPricingCard.vue.d.ts +1 -1
- package/dist/components/TPricingCard.vue.d.ts.map +1 -1
- package/dist/components/TSidebar.cjs.map +1 -1
- package/dist/components/TSidebar.js.map +1 -1
- package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/TSteps.cjs.map +1 -1
- package/dist/components/TSteps.js.map +1 -1
- package/dist/components/TSteps.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TSteps.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TSteps.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/TSteps.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/TToast.cjs.map +1 -1
- package/dist/components/TToast.js.map +1 -1
- package/dist/components/TToast.vue.d.ts.map +1 -1
- package/dist/components/TToast.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TToast.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TToast.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/components/TToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/TTreeViewItem.cjs.map +1 -1
- package/dist/components/TTreeViewItem.js.map +1 -1
- package/dist/components/TTreeViewItem.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/components/TTreeViewItem.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
- package/dist/components/TTreeViewItem.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/TTreeViewItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/index.d.ts +2 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +77 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +10 -9
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TAppShell.cjs","names":[],"sources":["../../src/components/TAppShell.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n createId,\n focusFirst,\n focusLast,\n getFocusableElements,\n isEscapeKey,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSidebarSide } from './TSidebar.vue';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst CloseIcon = getTreeIcon('x');\n\nconst props = withDefaults(\n defineProps<{\n as?: string;\n side?: TSidebarSide;\n sidebarWidth?: string;\n collapsedWidth?: string;\n collapsible?: boolean;\n collapsed?: boolean;\n defaultCollapsed?: boolean;\n mobile?: boolean;\n breakpoint?: string;\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n closeOnEscape?: boolean;\n closeOnOverlay?: boolean;\n showMenuButton?: boolean;\n /** Render the built-in collapse toggle in the sidebar footer (desktop). */\n showCollapseButton?: boolean;\n menuLabel?: string;\n closeLabel?: string;\n sidebarLabel?: string;\n collapseLabel?: string;\n expandLabel?: string;\n }>(),\n {\n as: 'div',\n side: 'left',\n sidebarWidth: '17.5rem',\n collapsedWidth: '4.75rem',\n collapsible: false,\n collapsed: undefined,\n defaultCollapsed: false,\n mobile: undefined,\n breakpoint: '768px',\n sidebarOpen: undefined,\n defaultSidebarOpen: false,\n closeOnEscape: true,\n closeOnOverlay: true,\n showMenuButton: true,\n showCollapseButton: true,\n menuLabel: 'Open menu',\n closeLabel: 'Close menu',\n sidebarLabel: 'Sidebar',\n collapseLabel: 'Collapse sidebar',\n expandLabel: 'Expand sidebar',\n },\n);\n\nconst emit = defineEmits<{\n 'update:collapsed': [value: boolean];\n 'collapse-change': [value: boolean];\n 'update:sidebarOpen': [value: boolean];\n 'sidebar-open-change': [value: boolean];\n}>();\n\nexport interface AppShellSlotProps {\n mobile: boolean;\n collapsed: boolean;\n sidebarOpen: boolean;\n toggleSidebar: () => void;\n closeSidebar: () => void;\n toggleCollapsed: () => void;\n}\n\ndefineSlots<{\n header?: (props: AppShellSlotProps) => unknown;\n /** Header region over the sidebar rail; the `header` slot then aligns with the content panel. */\n 'header-start'?: (props: AppShellSlotProps) => unknown;\n /** Pinned above the sidebar body, inset to line up with nav item icons. */\n 'sidebar-header'?: (props: AppShellSlotProps) => unknown;\n sidebar?: (props: AppShellSlotProps) => unknown;\n /** Pinned to the bottom of the sidebar, inset to line up with nav item icons. */\n 'sidebar-footer'?: (props: AppShellSlotProps) => unknown;\n default?: (props: Pick<AppShellSlotProps, 'mobile' | 'collapsed'>) => unknown;\n 'menu-icon'?: (props: { sidebarOpen: boolean }) => unknown;\n 'collapse-icon'?: (props: { collapsed: boolean }) => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebarId = createId('t-app-shell-sidebar');\n\nconst { value: isCollapsed, setValue: setCollapsed } = useControllableOpen(\n toRef(props, 'collapsed'),\n props.defaultCollapsed,\n (value) => {\n emit('update:collapsed', value);\n emit('collapse-change', value);\n },\n);\n\nconst { value: isSidebarOpen, setValue: setSidebarOpen } = useControllableOpen(\n toRef(props, 'sidebarOpen'),\n props.defaultSidebarOpen,\n (value) => {\n emit('update:sidebarOpen', value);\n emit('sidebar-open-change', value);\n },\n);\n\n// Auto responsive detection when `mobile` is not explicitly controlled.\nconst autoMobile = ref(false);\nlet mediaQuery: MediaQueryList | null = null;\n\nconst onMediaChange = (event: MediaQueryListEvent | MediaQueryList) => {\n autoMobile.value = event.matches;\n};\n\nconst setupMediaQuery = () => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return;\n }\n\n teardownMediaQuery();\n mediaQuery = window.matchMedia(`(max-width: ${props.breakpoint})`);\n autoMobile.value = mediaQuery.matches;\n mediaQuery.addEventListener?.('change', onMediaChange);\n};\n\nconst teardownMediaQuery = () => {\n mediaQuery?.removeEventListener?.('change', onMediaChange);\n mediaQuery = null;\n};\n\nconst isMobile = computed(() => props.mobile ?? autoMobile.value);\n\n// Collapsing is desktop-only, so the built-in toggle follows the same rule.\nconst showCollapseToggle = computed(\n () => !isMobile.value && props.collapsible && props.showCollapseButton,\n);\n\n// Collapse is a desktop-only concept; the mobile drawer always shows the\n// expanded sidebar, so descendants and slots see `collapsed: false` there.\nconst effectiveCollapsed = computed(() =>\n isMobile.value ? false : isCollapsed.value,\n);\n\nconst surfaceRef = ref<HTMLElement | null>(null);\nconst previousFocusedElement = ref<HTMLElement | null>(null);\nlet previousBodyOverflow = '';\nlet bodyLocked = false;\n\nconst lockBodyScroll = () => {\n if (typeof document === 'undefined' || bodyLocked) {\n return;\n }\n\n previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n bodyLocked = true;\n};\n\nconst unlockBodyScroll = () => {\n if (typeof document === 'undefined' || !bodyLocked) {\n return;\n }\n\n document.body.style.overflow = previousBodyOverflow;\n bodyLocked = false;\n};\n\nconst focusSurface = () => {\n nextTick(() => {\n if (!surfaceRef.value) {\n return;\n }\n\n const focused = focusFirst(surfaceRef.value);\n if (!focused) {\n surfaceRef.value.focus();\n }\n });\n};\n\nconst openSidebar = () => {\n previousFocusedElement.value =\n typeof document === 'undefined'\n ? null\n : (document.activeElement as HTMLElement | null);\n setSidebarOpen(true);\n};\n\nconst closeSidebar = () => {\n setSidebarOpen(false);\n};\n\nconst toggleSidebar = () => {\n if (isSidebarOpen.value) {\n closeSidebar();\n } else {\n openSidebar();\n }\n};\n\nconst toggleCollapsed = () => {\n if (!props.collapsible) {\n return;\n }\n\n setCollapsed(!isCollapsed.value);\n};\n\nconst onOverlayClick = () => {\n if (props.closeOnOverlay) {\n closeSidebar();\n }\n};\n\nconst onKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event) && props.closeOnEscape) {\n event.preventDefault();\n closeSidebar();\n return;\n }\n\n if (event.key !== 'Tab' || !surfaceRef.value) {\n return;\n }\n\n const focusable = getFocusableElements(surfaceRef.value);\n\n if (!focusable.length) {\n event.preventDefault();\n surfaceRef.value.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const activeElement = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && activeElement === first) {\n event.preventDefault();\n focusLast(surfaceRef.value);\n }\n\n if (!event.shiftKey && activeElement === last) {\n event.preventDefault();\n focusFirst(surfaceRef.value);\n }\n};\n\n// The off-canvas drawer only exists in mobile mode; keep body-scroll lock and\n// focus handling in sync with both the open flag and the responsive mode.\nconst syncOverlayState = (open: boolean, wasOpen: boolean) => {\n if (open && isMobile.value) {\n lockBodyScroll();\n focusSurface();\n return;\n }\n\n if (wasOpen) {\n unlockBodyScroll();\n nextTick(() => {\n previousFocusedElement.value?.focus();\n });\n }\n};\n\nwatch(isSidebarOpen, (value, previousValue) => {\n syncOverlayState(value, Boolean(previousValue));\n});\n\nwatch(isMobile, (mobile) => {\n if (!isSidebarOpen.value) {\n return;\n }\n\n // Leaving mobile removes the drawer from the DOM, so release the lock and\n // restore focus; re-entering mobile while \"open\" re-arms the overlay.\n if (mobile) {\n lockBodyScroll();\n focusSurface();\n } else {\n unlockBodyScroll();\n }\n});\n\nwatch(\n () => props.breakpoint,\n () => {\n if (typeof window !== 'undefined') {\n setupMediaQuery();\n }\n },\n);\n\nonMounted(() => {\n setupMediaQuery();\n});\n\nonBeforeUnmount(() => {\n teardownMediaQuery();\n unlockBodyScroll();\n});\n\nprovide(treeSidebarInjectionKey, {\n collapsed: effectiveCollapsed,\n});\n\nconst rootClasses = computed(() => [\n 't-app-shell',\n `t-app-shell--${props.side}`,\n {\n 'is-mobile': isMobile.value,\n 'is-collapsed':\n !isMobile.value && props.collapsible && isCollapsed.value,\n 'is-sidebar-open': isMobile.value && isSidebarOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => [\n {\n '--tree-app-shell-sidebar-width': props.sidebarWidth,\n '--tree-app-shell-collapsed-width': props.collapsedWidth,\n },\n attrs.style,\n]);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst slotProps = computed<AppShellSlotProps>(() => ({\n mobile: isMobile.value,\n collapsed: effectiveCollapsed.value,\n sidebarOpen: isSidebarOpen.value,\n toggleSidebar,\n closeSidebar,\n toggleCollapsed,\n}));\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-mobile=\"isMobile ? 'true' : 'false'\"\n >\n <header\n class=\"t-app-shell__header\"\n :class=\"{ 'has-start': Boolean($slots['header-start']) }\"\n >\n <button\n v-if=\"isMobile && showMenuButton\"\n type=\"button\"\n class=\"t-app-shell__menu-button\"\n :aria-label=\"menuLabel\"\n :aria-expanded=\"isSidebarOpen\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleSidebar\"\n >\n <slot\n name=\"menu-icon\"\n :sidebar-open=\"isSidebarOpen\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"6\"\n y2=\"6\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"12\"\n y2=\"12\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"18\"\n y2=\"18\"\n />\n </svg>\n </slot>\n </button>\n\n <div\n v-if=\"$slots['header-start']\"\n class=\"t-app-shell__header-start\"\n >\n <slot\n name=\"header-start\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__header-content\">\n <slot\n name=\"header\"\n v-bind=\"slotProps\"\n />\n </div>\n </header>\n\n <aside\n v-if=\"!isMobile\"\n :id=\"sidebarId\"\n class=\"t-app-shell__sidebar\"\n :aria-label=\"sidebarLabel\"\n >\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer'] || showCollapseToggle\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n\n <button\n v-if=\"showCollapseToggle\"\n type=\"button\"\n class=\"t-app-shell__collapse-button\"\n :aria-label=\"isCollapsed ? expandLabel : collapseLabel\"\n :aria-expanded=\"!isCollapsed\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleCollapsed\"\n >\n <slot\n name=\"collapse-icon\"\n :collapsed=\"effectiveCollapsed\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"3\"\n y=\"3\"\n width=\"18\"\n height=\"18\"\n rx=\"2\"\n />\n <path d=\"M9 3v18\" />\n </svg>\n </slot>\n </button>\n </div>\n </aside>\n\n <main class=\"t-app-shell__main\">\n <slot\n :mobile=\"isMobile\"\n :collapsed=\"effectiveCollapsed\"\n />\n </main>\n\n <transition name=\"t-app-shell-fade\">\n <div\n v-if=\"isMobile && isSidebarOpen\"\n class=\"t-app-shell__overlay\"\n >\n <div\n class=\"t-app-shell__backdrop\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n <aside\n :id=\"sidebarId\"\n ref=\"surfaceRef\"\n class=\"t-app-shell__drawer\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"sidebarLabel\"\n tabindex=\"-1\"\n @keydown=\"onKeydown\"\n >\n <div class=\"t-app-shell__drawer-topbar\">\n <button\n type=\"button\"\n class=\"t-app-shell__drawer-close\"\n :aria-label=\"closeLabel\"\n @click=\"closeSidebar\"\n >\n <CloseIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n <div class=\"t-app-shell__drawer-body\">\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer']\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n </div>\n </div>\n </aside>\n </div>\n </transition>\n </component>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"TAppShell.cjs","names":[],"sources":["../../src/components/TAppShell.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n createId,\n focusFirst,\n focusLast,\n getFocusableElements,\n isEscapeKey,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSidebarSide } from './TSidebar.vue';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst CloseIcon = computed(() => getTreeIcon('x'));\n\nconst props = withDefaults(\n defineProps<{\n as?: string;\n side?: TSidebarSide;\n sidebarWidth?: string;\n collapsedWidth?: string;\n collapsible?: boolean;\n collapsed?: boolean;\n defaultCollapsed?: boolean;\n mobile?: boolean;\n breakpoint?: string;\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n closeOnEscape?: boolean;\n closeOnOverlay?: boolean;\n showMenuButton?: boolean;\n /** Render the built-in collapse toggle in the sidebar footer (desktop). */\n showCollapseButton?: boolean;\n menuLabel?: string;\n closeLabel?: string;\n sidebarLabel?: string;\n collapseLabel?: string;\n expandLabel?: string;\n }>(),\n {\n as: 'div',\n side: 'left',\n sidebarWidth: '17.5rem',\n collapsedWidth: '4.75rem',\n collapsible: false,\n collapsed: undefined,\n defaultCollapsed: false,\n mobile: undefined,\n breakpoint: '768px',\n sidebarOpen: undefined,\n defaultSidebarOpen: false,\n closeOnEscape: true,\n closeOnOverlay: true,\n showMenuButton: true,\n showCollapseButton: true,\n menuLabel: 'Open menu',\n closeLabel: 'Close menu',\n sidebarLabel: 'Sidebar',\n collapseLabel: 'Collapse sidebar',\n expandLabel: 'Expand sidebar',\n },\n);\n\nconst emit = defineEmits<{\n 'update:collapsed': [value: boolean];\n 'collapse-change': [value: boolean];\n 'update:sidebarOpen': [value: boolean];\n 'sidebar-open-change': [value: boolean];\n}>();\n\nexport interface AppShellSlotProps {\n mobile: boolean;\n collapsed: boolean;\n sidebarOpen: boolean;\n toggleSidebar: () => void;\n closeSidebar: () => void;\n toggleCollapsed: () => void;\n}\n\ndefineSlots<{\n header?: (props: AppShellSlotProps) => unknown;\n /** Header region over the sidebar rail; the `header` slot then aligns with the content panel. */\n 'header-start'?: (props: AppShellSlotProps) => unknown;\n /** Pinned above the sidebar body, inset to line up with nav item icons. */\n 'sidebar-header'?: (props: AppShellSlotProps) => unknown;\n sidebar?: (props: AppShellSlotProps) => unknown;\n /** Pinned to the bottom of the sidebar, inset to line up with nav item icons. */\n 'sidebar-footer'?: (props: AppShellSlotProps) => unknown;\n default?: (props: Pick<AppShellSlotProps, 'mobile' | 'collapsed'>) => unknown;\n 'menu-icon'?: (props: { sidebarOpen: boolean }) => unknown;\n 'collapse-icon'?: (props: { collapsed: boolean }) => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebarId = createId('t-app-shell-sidebar');\n\nconst { value: isCollapsed, setValue: setCollapsed } = useControllableOpen(\n toRef(props, 'collapsed'),\n props.defaultCollapsed,\n (value) => {\n emit('update:collapsed', value);\n emit('collapse-change', value);\n },\n);\n\nconst { value: isSidebarOpen, setValue: setSidebarOpen } = useControllableOpen(\n toRef(props, 'sidebarOpen'),\n props.defaultSidebarOpen,\n (value) => {\n emit('update:sidebarOpen', value);\n emit('sidebar-open-change', value);\n },\n);\n\n// Auto responsive detection when `mobile` is not explicitly controlled.\nconst autoMobile = ref(false);\nlet mediaQuery: MediaQueryList | null = null;\n\nconst onMediaChange = (event: MediaQueryListEvent | MediaQueryList) => {\n autoMobile.value = event.matches;\n};\n\nconst setupMediaQuery = () => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return;\n }\n\n teardownMediaQuery();\n mediaQuery = window.matchMedia(`(max-width: ${props.breakpoint})`);\n autoMobile.value = mediaQuery.matches;\n mediaQuery.addEventListener?.('change', onMediaChange);\n};\n\nconst teardownMediaQuery = () => {\n mediaQuery?.removeEventListener?.('change', onMediaChange);\n mediaQuery = null;\n};\n\nconst isMobile = computed(() => props.mobile ?? autoMobile.value);\n\n// Collapsing is desktop-only, so the built-in toggle follows the same rule.\nconst showCollapseToggle = computed(\n () => !isMobile.value && props.collapsible && props.showCollapseButton,\n);\n\n// Collapse is a desktop-only concept; the mobile drawer always shows the\n// expanded sidebar, so descendants and slots see `collapsed: false` there.\nconst effectiveCollapsed = computed(() =>\n isMobile.value ? false : isCollapsed.value,\n);\n\nconst surfaceRef = ref<HTMLElement | null>(null);\nconst previousFocusedElement = ref<HTMLElement | null>(null);\nlet previousBodyOverflow = '';\nlet bodyLocked = false;\n\nconst lockBodyScroll = () => {\n if (typeof document === 'undefined' || bodyLocked) {\n return;\n }\n\n previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n bodyLocked = true;\n};\n\nconst unlockBodyScroll = () => {\n if (typeof document === 'undefined' || !bodyLocked) {\n return;\n }\n\n document.body.style.overflow = previousBodyOverflow;\n bodyLocked = false;\n};\n\nconst focusSurface = () => {\n nextTick(() => {\n if (!surfaceRef.value) {\n return;\n }\n\n const focused = focusFirst(surfaceRef.value);\n if (!focused) {\n surfaceRef.value.focus();\n }\n });\n};\n\nconst openSidebar = () => {\n previousFocusedElement.value =\n typeof document === 'undefined'\n ? null\n : (document.activeElement as HTMLElement | null);\n setSidebarOpen(true);\n};\n\nconst closeSidebar = () => {\n setSidebarOpen(false);\n};\n\nconst toggleSidebar = () => {\n if (isSidebarOpen.value) {\n closeSidebar();\n } else {\n openSidebar();\n }\n};\n\nconst toggleCollapsed = () => {\n if (!props.collapsible) {\n return;\n }\n\n setCollapsed(!isCollapsed.value);\n};\n\nconst onOverlayClick = () => {\n if (props.closeOnOverlay) {\n closeSidebar();\n }\n};\n\nconst onKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event) && props.closeOnEscape) {\n event.preventDefault();\n closeSidebar();\n return;\n }\n\n if (event.key !== 'Tab' || !surfaceRef.value) {\n return;\n }\n\n const focusable = getFocusableElements(surfaceRef.value);\n\n if (!focusable.length) {\n event.preventDefault();\n surfaceRef.value.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const activeElement = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && activeElement === first) {\n event.preventDefault();\n focusLast(surfaceRef.value);\n }\n\n if (!event.shiftKey && activeElement === last) {\n event.preventDefault();\n focusFirst(surfaceRef.value);\n }\n};\n\n// The off-canvas drawer only exists in mobile mode; keep body-scroll lock and\n// focus handling in sync with both the open flag and the responsive mode.\nconst syncOverlayState = (open: boolean, wasOpen: boolean) => {\n if (open && isMobile.value) {\n lockBodyScroll();\n focusSurface();\n return;\n }\n\n if (wasOpen) {\n unlockBodyScroll();\n nextTick(() => {\n previousFocusedElement.value?.focus();\n });\n }\n};\n\nwatch(isSidebarOpen, (value, previousValue) => {\n syncOverlayState(value, Boolean(previousValue));\n});\n\nwatch(isMobile, (mobile) => {\n if (!isSidebarOpen.value) {\n return;\n }\n\n // Leaving mobile removes the drawer from the DOM, so release the lock and\n // restore focus; re-entering mobile while \"open\" re-arms the overlay.\n if (mobile) {\n lockBodyScroll();\n focusSurface();\n } else {\n unlockBodyScroll();\n }\n});\n\nwatch(\n () => props.breakpoint,\n () => {\n if (typeof window !== 'undefined') {\n setupMediaQuery();\n }\n },\n);\n\nonMounted(() => {\n setupMediaQuery();\n});\n\nonBeforeUnmount(() => {\n teardownMediaQuery();\n unlockBodyScroll();\n});\n\nprovide(treeSidebarInjectionKey, {\n collapsed: effectiveCollapsed,\n});\n\nconst rootClasses = computed(() => [\n 't-app-shell',\n `t-app-shell--${props.side}`,\n {\n 'is-mobile': isMobile.value,\n 'is-collapsed':\n !isMobile.value && props.collapsible && isCollapsed.value,\n 'is-sidebar-open': isMobile.value && isSidebarOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => [\n {\n '--tree-app-shell-sidebar-width': props.sidebarWidth,\n '--tree-app-shell-collapsed-width': props.collapsedWidth,\n },\n attrs.style,\n]);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst slotProps = computed<AppShellSlotProps>(() => ({\n mobile: isMobile.value,\n collapsed: effectiveCollapsed.value,\n sidebarOpen: isSidebarOpen.value,\n toggleSidebar,\n closeSidebar,\n toggleCollapsed,\n}));\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-mobile=\"isMobile ? 'true' : 'false'\"\n >\n <header\n class=\"t-app-shell__header\"\n :class=\"{ 'has-start': Boolean($slots['header-start']) }\"\n >\n <button\n v-if=\"isMobile && showMenuButton\"\n type=\"button\"\n class=\"t-app-shell__menu-button\"\n :aria-label=\"menuLabel\"\n :aria-expanded=\"isSidebarOpen\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleSidebar\"\n >\n <slot\n name=\"menu-icon\"\n :sidebar-open=\"isSidebarOpen\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"6\"\n y2=\"6\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"12\"\n y2=\"12\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"18\"\n y2=\"18\"\n />\n </svg>\n </slot>\n </button>\n\n <div\n v-if=\"$slots['header-start']\"\n class=\"t-app-shell__header-start\"\n >\n <slot\n name=\"header-start\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__header-content\">\n <slot\n name=\"header\"\n v-bind=\"slotProps\"\n />\n </div>\n </header>\n\n <aside\n v-if=\"!isMobile\"\n :id=\"sidebarId\"\n class=\"t-app-shell__sidebar\"\n :aria-label=\"sidebarLabel\"\n >\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer'] || showCollapseToggle\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n\n <button\n v-if=\"showCollapseToggle\"\n type=\"button\"\n class=\"t-app-shell__collapse-button\"\n :aria-label=\"isCollapsed ? expandLabel : collapseLabel\"\n :aria-expanded=\"!isCollapsed\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleCollapsed\"\n >\n <slot\n name=\"collapse-icon\"\n :collapsed=\"effectiveCollapsed\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"3\"\n y=\"3\"\n width=\"18\"\n height=\"18\"\n rx=\"2\"\n />\n <path d=\"M9 3v18\" />\n </svg>\n </slot>\n </button>\n </div>\n </aside>\n\n <main class=\"t-app-shell__main\">\n <slot\n :mobile=\"isMobile\"\n :collapsed=\"effectiveCollapsed\"\n />\n </main>\n\n <transition name=\"t-app-shell-fade\">\n <div\n v-if=\"isMobile && isSidebarOpen\"\n class=\"t-app-shell__overlay\"\n >\n <div\n class=\"t-app-shell__backdrop\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n <aside\n :id=\"sidebarId\"\n ref=\"surfaceRef\"\n class=\"t-app-shell__drawer\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"sidebarLabel\"\n tabindex=\"-1\"\n @keydown=\"onKeydown\"\n >\n <div class=\"t-app-shell__drawer-topbar\">\n <button\n type=\"button\"\n class=\"t-app-shell__drawer-close\"\n :aria-label=\"closeLabel\"\n @click=\"closeSidebar\"\n >\n <CloseIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n <div class=\"t-app-shell__drawer-body\">\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer']\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n </div>\n </div>\n </aside>\n </div>\n </transition>\n </component>\n</template>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TAppShell.js","names":[],"sources":["../../src/components/TAppShell.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n createId,\n focusFirst,\n focusLast,\n getFocusableElements,\n isEscapeKey,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSidebarSide } from './TSidebar.vue';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst CloseIcon = getTreeIcon('x');\n\nconst props = withDefaults(\n defineProps<{\n as?: string;\n side?: TSidebarSide;\n sidebarWidth?: string;\n collapsedWidth?: string;\n collapsible?: boolean;\n collapsed?: boolean;\n defaultCollapsed?: boolean;\n mobile?: boolean;\n breakpoint?: string;\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n closeOnEscape?: boolean;\n closeOnOverlay?: boolean;\n showMenuButton?: boolean;\n /** Render the built-in collapse toggle in the sidebar footer (desktop). */\n showCollapseButton?: boolean;\n menuLabel?: string;\n closeLabel?: string;\n sidebarLabel?: string;\n collapseLabel?: string;\n expandLabel?: string;\n }>(),\n {\n as: 'div',\n side: 'left',\n sidebarWidth: '17.5rem',\n collapsedWidth: '4.75rem',\n collapsible: false,\n collapsed: undefined,\n defaultCollapsed: false,\n mobile: undefined,\n breakpoint: '768px',\n sidebarOpen: undefined,\n defaultSidebarOpen: false,\n closeOnEscape: true,\n closeOnOverlay: true,\n showMenuButton: true,\n showCollapseButton: true,\n menuLabel: 'Open menu',\n closeLabel: 'Close menu',\n sidebarLabel: 'Sidebar',\n collapseLabel: 'Collapse sidebar',\n expandLabel: 'Expand sidebar',\n },\n);\n\nconst emit = defineEmits<{\n 'update:collapsed': [value: boolean];\n 'collapse-change': [value: boolean];\n 'update:sidebarOpen': [value: boolean];\n 'sidebar-open-change': [value: boolean];\n}>();\n\nexport interface AppShellSlotProps {\n mobile: boolean;\n collapsed: boolean;\n sidebarOpen: boolean;\n toggleSidebar: () => void;\n closeSidebar: () => void;\n toggleCollapsed: () => void;\n}\n\ndefineSlots<{\n header?: (props: AppShellSlotProps) => unknown;\n /** Header region over the sidebar rail; the `header` slot then aligns with the content panel. */\n 'header-start'?: (props: AppShellSlotProps) => unknown;\n /** Pinned above the sidebar body, inset to line up with nav item icons. */\n 'sidebar-header'?: (props: AppShellSlotProps) => unknown;\n sidebar?: (props: AppShellSlotProps) => unknown;\n /** Pinned to the bottom of the sidebar, inset to line up with nav item icons. */\n 'sidebar-footer'?: (props: AppShellSlotProps) => unknown;\n default?: (props: Pick<AppShellSlotProps, 'mobile' | 'collapsed'>) => unknown;\n 'menu-icon'?: (props: { sidebarOpen: boolean }) => unknown;\n 'collapse-icon'?: (props: { collapsed: boolean }) => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebarId = createId('t-app-shell-sidebar');\n\nconst { value: isCollapsed, setValue: setCollapsed } = useControllableOpen(\n toRef(props, 'collapsed'),\n props.defaultCollapsed,\n (value) => {\n emit('update:collapsed', value);\n emit('collapse-change', value);\n },\n);\n\nconst { value: isSidebarOpen, setValue: setSidebarOpen } = useControllableOpen(\n toRef(props, 'sidebarOpen'),\n props.defaultSidebarOpen,\n (value) => {\n emit('update:sidebarOpen', value);\n emit('sidebar-open-change', value);\n },\n);\n\n// Auto responsive detection when `mobile` is not explicitly controlled.\nconst autoMobile = ref(false);\nlet mediaQuery: MediaQueryList | null = null;\n\nconst onMediaChange = (event: MediaQueryListEvent | MediaQueryList) => {\n autoMobile.value = event.matches;\n};\n\nconst setupMediaQuery = () => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return;\n }\n\n teardownMediaQuery();\n mediaQuery = window.matchMedia(`(max-width: ${props.breakpoint})`);\n autoMobile.value = mediaQuery.matches;\n mediaQuery.addEventListener?.('change', onMediaChange);\n};\n\nconst teardownMediaQuery = () => {\n mediaQuery?.removeEventListener?.('change', onMediaChange);\n mediaQuery = null;\n};\n\nconst isMobile = computed(() => props.mobile ?? autoMobile.value);\n\n// Collapsing is desktop-only, so the built-in toggle follows the same rule.\nconst showCollapseToggle = computed(\n () => !isMobile.value && props.collapsible && props.showCollapseButton,\n);\n\n// Collapse is a desktop-only concept; the mobile drawer always shows the\n// expanded sidebar, so descendants and slots see `collapsed: false` there.\nconst effectiveCollapsed = computed(() =>\n isMobile.value ? false : isCollapsed.value,\n);\n\nconst surfaceRef = ref<HTMLElement | null>(null);\nconst previousFocusedElement = ref<HTMLElement | null>(null);\nlet previousBodyOverflow = '';\nlet bodyLocked = false;\n\nconst lockBodyScroll = () => {\n if (typeof document === 'undefined' || bodyLocked) {\n return;\n }\n\n previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n bodyLocked = true;\n};\n\nconst unlockBodyScroll = () => {\n if (typeof document === 'undefined' || !bodyLocked) {\n return;\n }\n\n document.body.style.overflow = previousBodyOverflow;\n bodyLocked = false;\n};\n\nconst focusSurface = () => {\n nextTick(() => {\n if (!surfaceRef.value) {\n return;\n }\n\n const focused = focusFirst(surfaceRef.value);\n if (!focused) {\n surfaceRef.value.focus();\n }\n });\n};\n\nconst openSidebar = () => {\n previousFocusedElement.value =\n typeof document === 'undefined'\n ? null\n : (document.activeElement as HTMLElement | null);\n setSidebarOpen(true);\n};\n\nconst closeSidebar = () => {\n setSidebarOpen(false);\n};\n\nconst toggleSidebar = () => {\n if (isSidebarOpen.value) {\n closeSidebar();\n } else {\n openSidebar();\n }\n};\n\nconst toggleCollapsed = () => {\n if (!props.collapsible) {\n return;\n }\n\n setCollapsed(!isCollapsed.value);\n};\n\nconst onOverlayClick = () => {\n if (props.closeOnOverlay) {\n closeSidebar();\n }\n};\n\nconst onKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event) && props.closeOnEscape) {\n event.preventDefault();\n closeSidebar();\n return;\n }\n\n if (event.key !== 'Tab' || !surfaceRef.value) {\n return;\n }\n\n const focusable = getFocusableElements(surfaceRef.value);\n\n if (!focusable.length) {\n event.preventDefault();\n surfaceRef.value.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const activeElement = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && activeElement === first) {\n event.preventDefault();\n focusLast(surfaceRef.value);\n }\n\n if (!event.shiftKey && activeElement === last) {\n event.preventDefault();\n focusFirst(surfaceRef.value);\n }\n};\n\n// The off-canvas drawer only exists in mobile mode; keep body-scroll lock and\n// focus handling in sync with both the open flag and the responsive mode.\nconst syncOverlayState = (open: boolean, wasOpen: boolean) => {\n if (open && isMobile.value) {\n lockBodyScroll();\n focusSurface();\n return;\n }\n\n if (wasOpen) {\n unlockBodyScroll();\n nextTick(() => {\n previousFocusedElement.value?.focus();\n });\n }\n};\n\nwatch(isSidebarOpen, (value, previousValue) => {\n syncOverlayState(value, Boolean(previousValue));\n});\n\nwatch(isMobile, (mobile) => {\n if (!isSidebarOpen.value) {\n return;\n }\n\n // Leaving mobile removes the drawer from the DOM, so release the lock and\n // restore focus; re-entering mobile while \"open\" re-arms the overlay.\n if (mobile) {\n lockBodyScroll();\n focusSurface();\n } else {\n unlockBodyScroll();\n }\n});\n\nwatch(\n () => props.breakpoint,\n () => {\n if (typeof window !== 'undefined') {\n setupMediaQuery();\n }\n },\n);\n\nonMounted(() => {\n setupMediaQuery();\n});\n\nonBeforeUnmount(() => {\n teardownMediaQuery();\n unlockBodyScroll();\n});\n\nprovide(treeSidebarInjectionKey, {\n collapsed: effectiveCollapsed,\n});\n\nconst rootClasses = computed(() => [\n 't-app-shell',\n `t-app-shell--${props.side}`,\n {\n 'is-mobile': isMobile.value,\n 'is-collapsed':\n !isMobile.value && props.collapsible && isCollapsed.value,\n 'is-sidebar-open': isMobile.value && isSidebarOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => [\n {\n '--tree-app-shell-sidebar-width': props.sidebarWidth,\n '--tree-app-shell-collapsed-width': props.collapsedWidth,\n },\n attrs.style,\n]);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst slotProps = computed<AppShellSlotProps>(() => ({\n mobile: isMobile.value,\n collapsed: effectiveCollapsed.value,\n sidebarOpen: isSidebarOpen.value,\n toggleSidebar,\n closeSidebar,\n toggleCollapsed,\n}));\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-mobile=\"isMobile ? 'true' : 'false'\"\n >\n <header\n class=\"t-app-shell__header\"\n :class=\"{ 'has-start': Boolean($slots['header-start']) }\"\n >\n <button\n v-if=\"isMobile && showMenuButton\"\n type=\"button\"\n class=\"t-app-shell__menu-button\"\n :aria-label=\"menuLabel\"\n :aria-expanded=\"isSidebarOpen\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleSidebar\"\n >\n <slot\n name=\"menu-icon\"\n :sidebar-open=\"isSidebarOpen\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"6\"\n y2=\"6\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"12\"\n y2=\"12\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"18\"\n y2=\"18\"\n />\n </svg>\n </slot>\n </button>\n\n <div\n v-if=\"$slots['header-start']\"\n class=\"t-app-shell__header-start\"\n >\n <slot\n name=\"header-start\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__header-content\">\n <slot\n name=\"header\"\n v-bind=\"slotProps\"\n />\n </div>\n </header>\n\n <aside\n v-if=\"!isMobile\"\n :id=\"sidebarId\"\n class=\"t-app-shell__sidebar\"\n :aria-label=\"sidebarLabel\"\n >\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer'] || showCollapseToggle\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n\n <button\n v-if=\"showCollapseToggle\"\n type=\"button\"\n class=\"t-app-shell__collapse-button\"\n :aria-label=\"isCollapsed ? expandLabel : collapseLabel\"\n :aria-expanded=\"!isCollapsed\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleCollapsed\"\n >\n <slot\n name=\"collapse-icon\"\n :collapsed=\"effectiveCollapsed\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"3\"\n y=\"3\"\n width=\"18\"\n height=\"18\"\n rx=\"2\"\n />\n <path d=\"M9 3v18\" />\n </svg>\n </slot>\n </button>\n </div>\n </aside>\n\n <main class=\"t-app-shell__main\">\n <slot\n :mobile=\"isMobile\"\n :collapsed=\"effectiveCollapsed\"\n />\n </main>\n\n <transition name=\"t-app-shell-fade\">\n <div\n v-if=\"isMobile && isSidebarOpen\"\n class=\"t-app-shell__overlay\"\n >\n <div\n class=\"t-app-shell__backdrop\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n <aside\n :id=\"sidebarId\"\n ref=\"surfaceRef\"\n class=\"t-app-shell__drawer\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"sidebarLabel\"\n tabindex=\"-1\"\n @keydown=\"onKeydown\"\n >\n <div class=\"t-app-shell__drawer-topbar\">\n <button\n type=\"button\"\n class=\"t-app-shell__drawer-close\"\n :aria-label=\"closeLabel\"\n @click=\"closeSidebar\"\n >\n <CloseIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n <div class=\"t-app-shell__drawer-body\">\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer']\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n </div>\n </div>\n </aside>\n </div>\n </transition>\n </component>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"TAppShell.js","names":[],"sources":["../../src/components/TAppShell.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n createId,\n focusFirst,\n focusLast,\n getFocusableElements,\n isEscapeKey,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSidebarSide } from './TSidebar.vue';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst CloseIcon = computed(() => getTreeIcon('x'));\n\nconst props = withDefaults(\n defineProps<{\n as?: string;\n side?: TSidebarSide;\n sidebarWidth?: string;\n collapsedWidth?: string;\n collapsible?: boolean;\n collapsed?: boolean;\n defaultCollapsed?: boolean;\n mobile?: boolean;\n breakpoint?: string;\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n closeOnEscape?: boolean;\n closeOnOverlay?: boolean;\n showMenuButton?: boolean;\n /** Render the built-in collapse toggle in the sidebar footer (desktop). */\n showCollapseButton?: boolean;\n menuLabel?: string;\n closeLabel?: string;\n sidebarLabel?: string;\n collapseLabel?: string;\n expandLabel?: string;\n }>(),\n {\n as: 'div',\n side: 'left',\n sidebarWidth: '17.5rem',\n collapsedWidth: '4.75rem',\n collapsible: false,\n collapsed: undefined,\n defaultCollapsed: false,\n mobile: undefined,\n breakpoint: '768px',\n sidebarOpen: undefined,\n defaultSidebarOpen: false,\n closeOnEscape: true,\n closeOnOverlay: true,\n showMenuButton: true,\n showCollapseButton: true,\n menuLabel: 'Open menu',\n closeLabel: 'Close menu',\n sidebarLabel: 'Sidebar',\n collapseLabel: 'Collapse sidebar',\n expandLabel: 'Expand sidebar',\n },\n);\n\nconst emit = defineEmits<{\n 'update:collapsed': [value: boolean];\n 'collapse-change': [value: boolean];\n 'update:sidebarOpen': [value: boolean];\n 'sidebar-open-change': [value: boolean];\n}>();\n\nexport interface AppShellSlotProps {\n mobile: boolean;\n collapsed: boolean;\n sidebarOpen: boolean;\n toggleSidebar: () => void;\n closeSidebar: () => void;\n toggleCollapsed: () => void;\n}\n\ndefineSlots<{\n header?: (props: AppShellSlotProps) => unknown;\n /** Header region over the sidebar rail; the `header` slot then aligns with the content panel. */\n 'header-start'?: (props: AppShellSlotProps) => unknown;\n /** Pinned above the sidebar body, inset to line up with nav item icons. */\n 'sidebar-header'?: (props: AppShellSlotProps) => unknown;\n sidebar?: (props: AppShellSlotProps) => unknown;\n /** Pinned to the bottom of the sidebar, inset to line up with nav item icons. */\n 'sidebar-footer'?: (props: AppShellSlotProps) => unknown;\n default?: (props: Pick<AppShellSlotProps, 'mobile' | 'collapsed'>) => unknown;\n 'menu-icon'?: (props: { sidebarOpen: boolean }) => unknown;\n 'collapse-icon'?: (props: { collapsed: boolean }) => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebarId = createId('t-app-shell-sidebar');\n\nconst { value: isCollapsed, setValue: setCollapsed } = useControllableOpen(\n toRef(props, 'collapsed'),\n props.defaultCollapsed,\n (value) => {\n emit('update:collapsed', value);\n emit('collapse-change', value);\n },\n);\n\nconst { value: isSidebarOpen, setValue: setSidebarOpen } = useControllableOpen(\n toRef(props, 'sidebarOpen'),\n props.defaultSidebarOpen,\n (value) => {\n emit('update:sidebarOpen', value);\n emit('sidebar-open-change', value);\n },\n);\n\n// Auto responsive detection when `mobile` is not explicitly controlled.\nconst autoMobile = ref(false);\nlet mediaQuery: MediaQueryList | null = null;\n\nconst onMediaChange = (event: MediaQueryListEvent | MediaQueryList) => {\n autoMobile.value = event.matches;\n};\n\nconst setupMediaQuery = () => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return;\n }\n\n teardownMediaQuery();\n mediaQuery = window.matchMedia(`(max-width: ${props.breakpoint})`);\n autoMobile.value = mediaQuery.matches;\n mediaQuery.addEventListener?.('change', onMediaChange);\n};\n\nconst teardownMediaQuery = () => {\n mediaQuery?.removeEventListener?.('change', onMediaChange);\n mediaQuery = null;\n};\n\nconst isMobile = computed(() => props.mobile ?? autoMobile.value);\n\n// Collapsing is desktop-only, so the built-in toggle follows the same rule.\nconst showCollapseToggle = computed(\n () => !isMobile.value && props.collapsible && props.showCollapseButton,\n);\n\n// Collapse is a desktop-only concept; the mobile drawer always shows the\n// expanded sidebar, so descendants and slots see `collapsed: false` there.\nconst effectiveCollapsed = computed(() =>\n isMobile.value ? false : isCollapsed.value,\n);\n\nconst surfaceRef = ref<HTMLElement | null>(null);\nconst previousFocusedElement = ref<HTMLElement | null>(null);\nlet previousBodyOverflow = '';\nlet bodyLocked = false;\n\nconst lockBodyScroll = () => {\n if (typeof document === 'undefined' || bodyLocked) {\n return;\n }\n\n previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n bodyLocked = true;\n};\n\nconst unlockBodyScroll = () => {\n if (typeof document === 'undefined' || !bodyLocked) {\n return;\n }\n\n document.body.style.overflow = previousBodyOverflow;\n bodyLocked = false;\n};\n\nconst focusSurface = () => {\n nextTick(() => {\n if (!surfaceRef.value) {\n return;\n }\n\n const focused = focusFirst(surfaceRef.value);\n if (!focused) {\n surfaceRef.value.focus();\n }\n });\n};\n\nconst openSidebar = () => {\n previousFocusedElement.value =\n typeof document === 'undefined'\n ? null\n : (document.activeElement as HTMLElement | null);\n setSidebarOpen(true);\n};\n\nconst closeSidebar = () => {\n setSidebarOpen(false);\n};\n\nconst toggleSidebar = () => {\n if (isSidebarOpen.value) {\n closeSidebar();\n } else {\n openSidebar();\n }\n};\n\nconst toggleCollapsed = () => {\n if (!props.collapsible) {\n return;\n }\n\n setCollapsed(!isCollapsed.value);\n};\n\nconst onOverlayClick = () => {\n if (props.closeOnOverlay) {\n closeSidebar();\n }\n};\n\nconst onKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event) && props.closeOnEscape) {\n event.preventDefault();\n closeSidebar();\n return;\n }\n\n if (event.key !== 'Tab' || !surfaceRef.value) {\n return;\n }\n\n const focusable = getFocusableElements(surfaceRef.value);\n\n if (!focusable.length) {\n event.preventDefault();\n surfaceRef.value.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const activeElement = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && activeElement === first) {\n event.preventDefault();\n focusLast(surfaceRef.value);\n }\n\n if (!event.shiftKey && activeElement === last) {\n event.preventDefault();\n focusFirst(surfaceRef.value);\n }\n};\n\n// The off-canvas drawer only exists in mobile mode; keep body-scroll lock and\n// focus handling in sync with both the open flag and the responsive mode.\nconst syncOverlayState = (open: boolean, wasOpen: boolean) => {\n if (open && isMobile.value) {\n lockBodyScroll();\n focusSurface();\n return;\n }\n\n if (wasOpen) {\n unlockBodyScroll();\n nextTick(() => {\n previousFocusedElement.value?.focus();\n });\n }\n};\n\nwatch(isSidebarOpen, (value, previousValue) => {\n syncOverlayState(value, Boolean(previousValue));\n});\n\nwatch(isMobile, (mobile) => {\n if (!isSidebarOpen.value) {\n return;\n }\n\n // Leaving mobile removes the drawer from the DOM, so release the lock and\n // restore focus; re-entering mobile while \"open\" re-arms the overlay.\n if (mobile) {\n lockBodyScroll();\n focusSurface();\n } else {\n unlockBodyScroll();\n }\n});\n\nwatch(\n () => props.breakpoint,\n () => {\n if (typeof window !== 'undefined') {\n setupMediaQuery();\n }\n },\n);\n\nonMounted(() => {\n setupMediaQuery();\n});\n\nonBeforeUnmount(() => {\n teardownMediaQuery();\n unlockBodyScroll();\n});\n\nprovide(treeSidebarInjectionKey, {\n collapsed: effectiveCollapsed,\n});\n\nconst rootClasses = computed(() => [\n 't-app-shell',\n `t-app-shell--${props.side}`,\n {\n 'is-mobile': isMobile.value,\n 'is-collapsed':\n !isMobile.value && props.collapsible && isCollapsed.value,\n 'is-sidebar-open': isMobile.value && isSidebarOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => [\n {\n '--tree-app-shell-sidebar-width': props.sidebarWidth,\n '--tree-app-shell-collapsed-width': props.collapsedWidth,\n },\n attrs.style,\n]);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst slotProps = computed<AppShellSlotProps>(() => ({\n mobile: isMobile.value,\n collapsed: effectiveCollapsed.value,\n sidebarOpen: isSidebarOpen.value,\n toggleSidebar,\n closeSidebar,\n toggleCollapsed,\n}));\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-mobile=\"isMobile ? 'true' : 'false'\"\n >\n <header\n class=\"t-app-shell__header\"\n :class=\"{ 'has-start': Boolean($slots['header-start']) }\"\n >\n <button\n v-if=\"isMobile && showMenuButton\"\n type=\"button\"\n class=\"t-app-shell__menu-button\"\n :aria-label=\"menuLabel\"\n :aria-expanded=\"isSidebarOpen\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleSidebar\"\n >\n <slot\n name=\"menu-icon\"\n :sidebar-open=\"isSidebarOpen\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"6\"\n y2=\"6\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"12\"\n y2=\"12\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"18\"\n y2=\"18\"\n />\n </svg>\n </slot>\n </button>\n\n <div\n v-if=\"$slots['header-start']\"\n class=\"t-app-shell__header-start\"\n >\n <slot\n name=\"header-start\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__header-content\">\n <slot\n name=\"header\"\n v-bind=\"slotProps\"\n />\n </div>\n </header>\n\n <aside\n v-if=\"!isMobile\"\n :id=\"sidebarId\"\n class=\"t-app-shell__sidebar\"\n :aria-label=\"sidebarLabel\"\n >\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer'] || showCollapseToggle\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n\n <button\n v-if=\"showCollapseToggle\"\n type=\"button\"\n class=\"t-app-shell__collapse-button\"\n :aria-label=\"isCollapsed ? expandLabel : collapseLabel\"\n :aria-expanded=\"!isCollapsed\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleCollapsed\"\n >\n <slot\n name=\"collapse-icon\"\n :collapsed=\"effectiveCollapsed\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"3\"\n y=\"3\"\n width=\"18\"\n height=\"18\"\n rx=\"2\"\n />\n <path d=\"M9 3v18\" />\n </svg>\n </slot>\n </button>\n </div>\n </aside>\n\n <main class=\"t-app-shell__main\">\n <slot\n :mobile=\"isMobile\"\n :collapsed=\"effectiveCollapsed\"\n />\n </main>\n\n <transition name=\"t-app-shell-fade\">\n <div\n v-if=\"isMobile && isSidebarOpen\"\n class=\"t-app-shell__overlay\"\n >\n <div\n class=\"t-app-shell__backdrop\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n <aside\n :id=\"sidebarId\"\n ref=\"surfaceRef\"\n class=\"t-app-shell__drawer\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"sidebarLabel\"\n tabindex=\"-1\"\n @keydown=\"onKeydown\"\n >\n <div class=\"t-app-shell__drawer-topbar\">\n <button\n type=\"button\"\n class=\"t-app-shell__drawer-close\"\n :aria-label=\"closeLabel\"\n @click=\"closeSidebar\"\n >\n <CloseIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n <div class=\"t-app-shell__drawer-body\">\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer']\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n </div>\n </div>\n </aside>\n </div>\n </transition>\n </component>\n</template>\n"],"mappings":""}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../composables/useControllableOpen.cjs"),t=require("./sidebar.cjs");let n=require("vue"),r=require("@treeui/utils"),i=require("@treeui/icons");var a=[`aria-label`,`aria-expanded`,`aria-controls`],o={key:1,class:`t-app-shell__header-start`},s={class:`t-app-shell__header-content`},c=[`id`,`aria-label`],l={key:0,class:`t-app-shell__sidebar-header`},ee={class:`t-app-shell__sidebar-body`},u={key:1,class:`t-app-shell__sidebar-footer`},d=[`aria-label`,`aria-expanded`,`aria-controls`],f={class:`t-app-shell__main`},p={key:0,class:`t-app-shell__overlay`},m=[`id`,`aria-label`],h={class:`t-app-shell__drawer-topbar`},g=[`aria-label`],_={class:`t-app-shell__drawer-body`},v={key:0,class:`t-app-shell__sidebar-header`},y={class:`t-app-shell__sidebar-body`},b={key:1,class:`t-app-shell__sidebar-footer`},x=(0,n.defineComponent)({inheritAttrs:!1,__name:`TAppShell`,props:{as:{default:`div`},side:{default:`left`},sidebarWidth:{default:`17.5rem`},collapsedWidth:{default:`4.75rem`},collapsible:{type:Boolean,default:!1},collapsed:{type:Boolean,default:void 0},defaultCollapsed:{type:Boolean,default:!1},mobile:{type:Boolean,default:void 0},breakpoint:{default:`768px`},sidebarOpen:{type:Boolean,default:void 0},defaultSidebarOpen:{type:Boolean,default:!1},closeOnEscape:{type:Boolean,default:!0},closeOnOverlay:{type:Boolean,default:!0},showMenuButton:{type:Boolean,default:!0},showCollapseButton:{type:Boolean,default:!0},menuLabel:{default:`Open menu`},closeLabel:{default:`Close menu`},sidebarLabel:{default:`Sidebar`},collapseLabel:{default:`Collapse sidebar`},expandLabel:{default:`Expand sidebar`}},emits:[`update:collapsed`,`collapse-change`,`update:sidebarOpen`,`sidebar-open-change`],setup(x,{emit:S}){let C=(0,i.getTreeIcon)(`x`),w=x,T=S,E=(0,n.useAttrs)(),D=(0,r.createId)(`t-app-shell-sidebar`),{value:O,setValue:k}=e.useControllableOpen((0,n.toRef)(w,`collapsed`),w.defaultCollapsed,e=>{T(`update:collapsed`,e),T(`collapse-change`,e)}),{value:A,setValue:j}=e.useControllableOpen((0,n.toRef)(w,`sidebarOpen`),w.defaultSidebarOpen,e=>{T(`update:sidebarOpen`,e),T(`sidebar-open-change`,e)}),M=(0,n.ref)(!1),N=null,P=e=>{M.value=e.matches},F=()=>{typeof window>`u`||typeof window.matchMedia!=`function`||(I(),N=window.matchMedia(`(max-width: ${w.breakpoint})`),M.value=N.matches,N.addEventListener?.(`change`,P))},I=()=>{N?.removeEventListener?.(`change`,P),N=null},L=(0,n.computed)(()=>w.mobile??M.value),R=(0,n.computed)(()=>!L.value&&w.collapsible&&w.showCollapseButton),z=(0,n.computed)(()=>!L.value&&O.value),B=(0,n.ref)(null),V=(0,n.ref)(null),H=``,U=!1,W=()=>{typeof document>`u`||U||(H=document.body.style.overflow,document.body.style.overflow=`hidden`,U=!0)},G=()=>{typeof document>`u`||!U||(document.body.style.overflow=H,U=!1)},K=()=>{(0,n.nextTick)(()=>{B.value&&((0,r.focusFirst)(B.value)||B.value.focus())})},q=()=>{V.value=typeof document>`u`?null:document.activeElement,j(!0)},J=()=>{j(!1)},Y=()=>{A.value?J():q()},X=()=>{w.collapsible&&k(!O.value)},Z=()=>{w.closeOnOverlay&&J()},Q=e=>{if((0,r.isEscapeKey)(e)&&w.closeOnEscape){e.preventDefault(),J();return}if(e.key!==`Tab`||!B.value)return;let t=(0,r.getFocusableElements)(B.value);if(!t.length){e.preventDefault(),B.value.focus();return}let n=t[0],i=t[t.length-1],a=document.activeElement;e.shiftKey&&a===n&&(e.preventDefault(),(0,r.focusLast)(B.value)),!e.shiftKey&&a===i&&(e.preventDefault(),(0,r.focusFirst)(B.value))},te=(e,t)=>{if(e&&L.value){W(),K();return}t&&(G(),(0,n.nextTick)(()=>{V.value?.focus()}))};(0,n.watch)(A,(e,t)=>{te(e,!!t)}),(0,n.watch)(L,e=>{A.value&&(e?(W(),K()):G())}),(0,n.watch)(()=>w.breakpoint,()=>{typeof window<`u`&&F()}),(0,n.onMounted)(()=>{F()}),(0,n.onBeforeUnmount)(()=>{I(),G()}),(0,n.provide)(t.treeSidebarInjectionKey,{collapsed:z});let ne=(0,n.computed)(()=>[`t-app-shell`,`t-app-shell--${w.side}`,{"is-mobile":L.value,"is-collapsed":!L.value&&w.collapsible&&O.value,"is-sidebar-open":L.value&&A.value},E.class]),re=(0,n.computed)(()=>[{"--tree-app-shell-sidebar-width":w.sidebarWidth,"--tree-app-shell-collapsed-width":w.collapsedWidth},E.style]),ie=(0,n.computed)(()=>{let{class:e,style:t,...n}=E;return n}),$=(0,n.computed)(()=>({mobile:L.value,collapsed:z.value,sidebarOpen:A.value,toggleSidebar:Y,closeSidebar:J,toggleCollapsed:X}));return(e,t)=>((0,n.openBlock)(),(0,n.createBlock)((0,n.resolveDynamicComponent)(x.as),(0,n.mergeProps)(ie.value,{class:ne.value,style:re.value,"data-mobile":L.value?`true`:`false`}),{default:(0,n.withCtx)(()=>[(0,n.createElementVNode)(`header`,{class:(0,n.normalizeClass)([`t-app-shell__header`,{"has-start":!!e.$slots[`header-start`]}])},[L.value&&x.showMenuButton?((0,n.openBlock)(),(0,n.createElementBlock)(`button`,{key:0,type:`button`,class:`t-app-shell__menu-button`,"aria-label":x.menuLabel,"aria-expanded":(0,n.unref)(A),"aria-controls":(0,n.unref)(D),onClick:Y},[(0,n.renderSlot)(e.$slots,`menu-icon`,{sidebarOpen:(0,n.unref)(A)},()=>[t[0]||=(0,n.createElementVNode)(`svg`,{xmlns:`http://www.w3.org/2000/svg`,width:`24`,height:`24`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":`true`},[(0,n.createElementVNode)(`line`,{x1:`4`,x2:`20`,y1:`6`,y2:`6`}),(0,n.createElementVNode)(`line`,{x1:`4`,x2:`20`,y1:`12`,y2:`12`}),(0,n.createElementVNode)(`line`,{x1:`4`,x2:`20`,y1:`18`,y2:`18`})],-1)])],8,a)):(0,n.createCommentVNode)(``,!0),e.$slots[`header-start`]?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,o,[(0,n.renderSlot)(e.$slots,`header-start`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))])):(0,n.createCommentVNode)(``,!0),(0,n.createElementVNode)(`div`,s,[(0,n.renderSlot)(e.$slots,`header`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))])],2),L.value?(0,n.createCommentVNode)(``,!0):((0,n.openBlock)(),(0,n.createElementBlock)(`aside`,{key:0,id:(0,n.unref)(D),class:`t-app-shell__sidebar`,"aria-label":x.sidebarLabel},[e.$slots[`sidebar-header`]?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,l,[(0,n.renderSlot)(e.$slots,`sidebar-header`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))])):(0,n.createCommentVNode)(``,!0),(0,n.createElementVNode)(`div`,ee,[(0,n.renderSlot)(e.$slots,`sidebar`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))]),e.$slots[`sidebar-footer`]||R.value?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,u,[(0,n.renderSlot)(e.$slots,`sidebar-footer`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value))),R.value?((0,n.openBlock)(),(0,n.createElementBlock)(`button`,{key:0,type:`button`,class:`t-app-shell__collapse-button`,"aria-label":(0,n.unref)(O)?x.expandLabel:x.collapseLabel,"aria-expanded":!(0,n.unref)(O),"aria-controls":(0,n.unref)(D),onClick:X},[(0,n.renderSlot)(e.$slots,`collapse-icon`,{collapsed:z.value},()=>[t[1]||=(0,n.createElementVNode)(`svg`,{xmlns:`http://www.w3.org/2000/svg`,width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":`true`},[(0,n.createElementVNode)(`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`}),(0,n.createElementVNode)(`path`,{d:`M9 3v18`})],-1)])],8,d)):(0,n.createCommentVNode)(``,!0)])):(0,n.createCommentVNode)(``,!0)],8,c)),(0,n.createElementVNode)(`main`,f,[(0,n.renderSlot)(e.$slots,`default`,{mobile:L.value,collapsed:z.value})]),(0,n.createVNode)(n.Transition,{name:`t-app-shell-fade`},{default:(0,n.withCtx)(()=>[L.value&&(0,n.unref)(A)?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,p,[(0,n.createElementVNode)(`div`,{class:`t-app-shell__backdrop`,"aria-hidden":`true`,onClick:Z}),(0,n.createElementVNode)(`aside`,{id:(0,n.unref)(D),ref_key:`surfaceRef`,ref:B,class:`t-app-shell__drawer`,role:`dialog`,"aria-modal":`true`,"aria-label":x.sidebarLabel,tabindex:`-1`,onKeydown:Q},[(0,n.createElementVNode)(`div`,h,[(0,n.createElementVNode)(`button`,{type:`button`,class:`t-app-shell__drawer-close`,"aria-label":x.closeLabel,onClick:J},[(0,n.createVNode)((0,n.unref)(C),(0,n.normalizeProps)((0,n.guardReactiveProps)((0,n.unref)(i.treeIconDefaults))),null,16)],8,g)]),(0,n.createElementVNode)(`div`,_,[e.$slots[`sidebar-header`]?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,v,[(0,n.renderSlot)(e.$slots,`sidebar-header`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))])):(0,n.createCommentVNode)(``,!0),(0,n.createElementVNode)(`div`,y,[(0,n.renderSlot)(e.$slots,`sidebar`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))]),e.$slots[`sidebar-footer`]?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,b,[(0,n.renderSlot)(e.$slots,`sidebar-footer`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))])):(0,n.createCommentVNode)(``,!0)])],40,m)])):(0,n.createCommentVNode)(``,!0)]),_:3})]),_:3},16,[`class`,`style`,`data-mobile`]))}});exports.default=x;
|
|
1
|
+
const e=require("../composables/useControllableOpen.cjs"),t=require("./sidebar.cjs");let n=require("vue"),r=require("@treeui/utils"),i=require("@treeui/icons");var a=[`aria-label`,`aria-expanded`,`aria-controls`],o={key:1,class:`t-app-shell__header-start`},s={class:`t-app-shell__header-content`},c=[`id`,`aria-label`],l={key:0,class:`t-app-shell__sidebar-header`},ee={class:`t-app-shell__sidebar-body`},u={key:1,class:`t-app-shell__sidebar-footer`},d=[`aria-label`,`aria-expanded`,`aria-controls`],f={class:`t-app-shell__main`},p={key:0,class:`t-app-shell__overlay`},m=[`id`,`aria-label`],h={class:`t-app-shell__drawer-topbar`},g=[`aria-label`],_={class:`t-app-shell__drawer-body`},v={key:0,class:`t-app-shell__sidebar-header`},y={class:`t-app-shell__sidebar-body`},b={key:1,class:`t-app-shell__sidebar-footer`},x=(0,n.defineComponent)({inheritAttrs:!1,__name:`TAppShell`,props:{as:{default:`div`},side:{default:`left`},sidebarWidth:{default:`17.5rem`},collapsedWidth:{default:`4.75rem`},collapsible:{type:Boolean,default:!1},collapsed:{type:Boolean,default:void 0},defaultCollapsed:{type:Boolean,default:!1},mobile:{type:Boolean,default:void 0},breakpoint:{default:`768px`},sidebarOpen:{type:Boolean,default:void 0},defaultSidebarOpen:{type:Boolean,default:!1},closeOnEscape:{type:Boolean,default:!0},closeOnOverlay:{type:Boolean,default:!0},showMenuButton:{type:Boolean,default:!0},showCollapseButton:{type:Boolean,default:!0},menuLabel:{default:`Open menu`},closeLabel:{default:`Close menu`},sidebarLabel:{default:`Sidebar`},collapseLabel:{default:`Collapse sidebar`},expandLabel:{default:`Expand sidebar`}},emits:[`update:collapsed`,`collapse-change`,`update:sidebarOpen`,`sidebar-open-change`],setup(x,{emit:S}){let C=(0,n.computed)(()=>(0,i.getTreeIcon)(`x`)),w=x,T=S,E=(0,n.useAttrs)(),D=(0,r.createId)(`t-app-shell-sidebar`),{value:O,setValue:k}=e.useControllableOpen((0,n.toRef)(w,`collapsed`),w.defaultCollapsed,e=>{T(`update:collapsed`,e),T(`collapse-change`,e)}),{value:A,setValue:j}=e.useControllableOpen((0,n.toRef)(w,`sidebarOpen`),w.defaultSidebarOpen,e=>{T(`update:sidebarOpen`,e),T(`sidebar-open-change`,e)}),M=(0,n.ref)(!1),N=null,P=e=>{M.value=e.matches},F=()=>{typeof window>`u`||typeof window.matchMedia!=`function`||(I(),N=window.matchMedia(`(max-width: ${w.breakpoint})`),M.value=N.matches,N.addEventListener?.(`change`,P))},I=()=>{N?.removeEventListener?.(`change`,P),N=null},L=(0,n.computed)(()=>w.mobile??M.value),R=(0,n.computed)(()=>!L.value&&w.collapsible&&w.showCollapseButton),z=(0,n.computed)(()=>!L.value&&O.value),B=(0,n.ref)(null),V=(0,n.ref)(null),H=``,U=!1,W=()=>{typeof document>`u`||U||(H=document.body.style.overflow,document.body.style.overflow=`hidden`,U=!0)},G=()=>{typeof document>`u`||!U||(document.body.style.overflow=H,U=!1)},K=()=>{(0,n.nextTick)(()=>{B.value&&((0,r.focusFirst)(B.value)||B.value.focus())})},q=()=>{V.value=typeof document>`u`?null:document.activeElement,j(!0)},J=()=>{j(!1)},Y=()=>{A.value?J():q()},X=()=>{w.collapsible&&k(!O.value)},Z=()=>{w.closeOnOverlay&&J()},Q=e=>{if((0,r.isEscapeKey)(e)&&w.closeOnEscape){e.preventDefault(),J();return}if(e.key!==`Tab`||!B.value)return;let t=(0,r.getFocusableElements)(B.value);if(!t.length){e.preventDefault(),B.value.focus();return}let n=t[0],i=t[t.length-1],a=document.activeElement;e.shiftKey&&a===n&&(e.preventDefault(),(0,r.focusLast)(B.value)),!e.shiftKey&&a===i&&(e.preventDefault(),(0,r.focusFirst)(B.value))},te=(e,t)=>{if(e&&L.value){W(),K();return}t&&(G(),(0,n.nextTick)(()=>{V.value?.focus()}))};(0,n.watch)(A,(e,t)=>{te(e,!!t)}),(0,n.watch)(L,e=>{A.value&&(e?(W(),K()):G())}),(0,n.watch)(()=>w.breakpoint,()=>{typeof window<`u`&&F()}),(0,n.onMounted)(()=>{F()}),(0,n.onBeforeUnmount)(()=>{I(),G()}),(0,n.provide)(t.treeSidebarInjectionKey,{collapsed:z});let ne=(0,n.computed)(()=>[`t-app-shell`,`t-app-shell--${w.side}`,{"is-mobile":L.value,"is-collapsed":!L.value&&w.collapsible&&O.value,"is-sidebar-open":L.value&&A.value},E.class]),re=(0,n.computed)(()=>[{"--tree-app-shell-sidebar-width":w.sidebarWidth,"--tree-app-shell-collapsed-width":w.collapsedWidth},E.style]),ie=(0,n.computed)(()=>{let{class:e,style:t,...n}=E;return n}),$=(0,n.computed)(()=>({mobile:L.value,collapsed:z.value,sidebarOpen:A.value,toggleSidebar:Y,closeSidebar:J,toggleCollapsed:X}));return(e,t)=>((0,n.openBlock)(),(0,n.createBlock)((0,n.resolveDynamicComponent)(x.as),(0,n.mergeProps)(ie.value,{class:ne.value,style:re.value,"data-mobile":L.value?`true`:`false`}),{default:(0,n.withCtx)(()=>[(0,n.createElementVNode)(`header`,{class:(0,n.normalizeClass)([`t-app-shell__header`,{"has-start":!!e.$slots[`header-start`]}])},[L.value&&x.showMenuButton?((0,n.openBlock)(),(0,n.createElementBlock)(`button`,{key:0,type:`button`,class:`t-app-shell__menu-button`,"aria-label":x.menuLabel,"aria-expanded":(0,n.unref)(A),"aria-controls":(0,n.unref)(D),onClick:Y},[(0,n.renderSlot)(e.$slots,`menu-icon`,{sidebarOpen:(0,n.unref)(A)},()=>[t[0]||=(0,n.createElementVNode)(`svg`,{xmlns:`http://www.w3.org/2000/svg`,width:`24`,height:`24`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":`true`},[(0,n.createElementVNode)(`line`,{x1:`4`,x2:`20`,y1:`6`,y2:`6`}),(0,n.createElementVNode)(`line`,{x1:`4`,x2:`20`,y1:`12`,y2:`12`}),(0,n.createElementVNode)(`line`,{x1:`4`,x2:`20`,y1:`18`,y2:`18`})],-1)])],8,a)):(0,n.createCommentVNode)(``,!0),e.$slots[`header-start`]?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,o,[(0,n.renderSlot)(e.$slots,`header-start`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))])):(0,n.createCommentVNode)(``,!0),(0,n.createElementVNode)(`div`,s,[(0,n.renderSlot)(e.$slots,`header`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))])],2),L.value?(0,n.createCommentVNode)(``,!0):((0,n.openBlock)(),(0,n.createElementBlock)(`aside`,{key:0,id:(0,n.unref)(D),class:`t-app-shell__sidebar`,"aria-label":x.sidebarLabel},[e.$slots[`sidebar-header`]?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,l,[(0,n.renderSlot)(e.$slots,`sidebar-header`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))])):(0,n.createCommentVNode)(``,!0),(0,n.createElementVNode)(`div`,ee,[(0,n.renderSlot)(e.$slots,`sidebar`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))]),e.$slots[`sidebar-footer`]||R.value?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,u,[(0,n.renderSlot)(e.$slots,`sidebar-footer`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value))),R.value?((0,n.openBlock)(),(0,n.createElementBlock)(`button`,{key:0,type:`button`,class:`t-app-shell__collapse-button`,"aria-label":(0,n.unref)(O)?x.expandLabel:x.collapseLabel,"aria-expanded":!(0,n.unref)(O),"aria-controls":(0,n.unref)(D),onClick:X},[(0,n.renderSlot)(e.$slots,`collapse-icon`,{collapsed:z.value},()=>[t[1]||=(0,n.createElementVNode)(`svg`,{xmlns:`http://www.w3.org/2000/svg`,width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":`true`},[(0,n.createElementVNode)(`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`}),(0,n.createElementVNode)(`path`,{d:`M9 3v18`})],-1)])],8,d)):(0,n.createCommentVNode)(``,!0)])):(0,n.createCommentVNode)(``,!0)],8,c)),(0,n.createElementVNode)(`main`,f,[(0,n.renderSlot)(e.$slots,`default`,{mobile:L.value,collapsed:z.value})]),(0,n.createVNode)(n.Transition,{name:`t-app-shell-fade`},{default:(0,n.withCtx)(()=>[L.value&&(0,n.unref)(A)?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,p,[(0,n.createElementVNode)(`div`,{class:`t-app-shell__backdrop`,"aria-hidden":`true`,onClick:Z}),(0,n.createElementVNode)(`aside`,{id:(0,n.unref)(D),ref_key:`surfaceRef`,ref:B,class:`t-app-shell__drawer`,role:`dialog`,"aria-modal":`true`,"aria-label":x.sidebarLabel,tabindex:`-1`,onKeydown:Q},[(0,n.createElementVNode)(`div`,h,[(0,n.createElementVNode)(`button`,{type:`button`,class:`t-app-shell__drawer-close`,"aria-label":x.closeLabel,onClick:J},[(0,n.createVNode)((0,n.unref)(C),(0,n.normalizeProps)((0,n.guardReactiveProps)((0,n.unref)(i.treeIconDefaults))),null,16)],8,g)]),(0,n.createElementVNode)(`div`,_,[e.$slots[`sidebar-header`]?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,v,[(0,n.renderSlot)(e.$slots,`sidebar-header`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))])):(0,n.createCommentVNode)(``,!0),(0,n.createElementVNode)(`div`,y,[(0,n.renderSlot)(e.$slots,`sidebar`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))]),e.$slots[`sidebar-footer`]?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,b,[(0,n.renderSlot)(e.$slots,`sidebar-footer`,(0,n.normalizeProps)((0,n.guardReactiveProps)($.value)))])):(0,n.createCommentVNode)(``,!0)])],40,m)])):(0,n.createCommentVNode)(``,!0)]),_:3})]),_:3},16,[`class`,`style`,`data-mobile`]))}});exports.default=x;
|
|
2
2
|
//# sourceMappingURL=TAppShell.vue_vue_type_script_setup_true_lang.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TAppShell.vue_vue_type_script_setup_true_lang.cjs","names":["$slots"],"sources":["../../src/components/TAppShell.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n createId,\n focusFirst,\n focusLast,\n getFocusableElements,\n isEscapeKey,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSidebarSide } from './TSidebar.vue';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst CloseIcon = getTreeIcon('x');\n\nconst props = withDefaults(\n defineProps<{\n as?: string;\n side?: TSidebarSide;\n sidebarWidth?: string;\n collapsedWidth?: string;\n collapsible?: boolean;\n collapsed?: boolean;\n defaultCollapsed?: boolean;\n mobile?: boolean;\n breakpoint?: string;\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n closeOnEscape?: boolean;\n closeOnOverlay?: boolean;\n showMenuButton?: boolean;\n /** Render the built-in collapse toggle in the sidebar footer (desktop). */\n showCollapseButton?: boolean;\n menuLabel?: string;\n closeLabel?: string;\n sidebarLabel?: string;\n collapseLabel?: string;\n expandLabel?: string;\n }>(),\n {\n as: 'div',\n side: 'left',\n sidebarWidth: '17.5rem',\n collapsedWidth: '4.75rem',\n collapsible: false,\n collapsed: undefined,\n defaultCollapsed: false,\n mobile: undefined,\n breakpoint: '768px',\n sidebarOpen: undefined,\n defaultSidebarOpen: false,\n closeOnEscape: true,\n closeOnOverlay: true,\n showMenuButton: true,\n showCollapseButton: true,\n menuLabel: 'Open menu',\n closeLabel: 'Close menu',\n sidebarLabel: 'Sidebar',\n collapseLabel: 'Collapse sidebar',\n expandLabel: 'Expand sidebar',\n },\n);\n\nconst emit = defineEmits<{\n 'update:collapsed': [value: boolean];\n 'collapse-change': [value: boolean];\n 'update:sidebarOpen': [value: boolean];\n 'sidebar-open-change': [value: boolean];\n}>();\n\nexport interface AppShellSlotProps {\n mobile: boolean;\n collapsed: boolean;\n sidebarOpen: boolean;\n toggleSidebar: () => void;\n closeSidebar: () => void;\n toggleCollapsed: () => void;\n}\n\ndefineSlots<{\n header?: (props: AppShellSlotProps) => unknown;\n /** Header region over the sidebar rail; the `header` slot then aligns with the content panel. */\n 'header-start'?: (props: AppShellSlotProps) => unknown;\n /** Pinned above the sidebar body, inset to line up with nav item icons. */\n 'sidebar-header'?: (props: AppShellSlotProps) => unknown;\n sidebar?: (props: AppShellSlotProps) => unknown;\n /** Pinned to the bottom of the sidebar, inset to line up with nav item icons. */\n 'sidebar-footer'?: (props: AppShellSlotProps) => unknown;\n default?: (props: Pick<AppShellSlotProps, 'mobile' | 'collapsed'>) => unknown;\n 'menu-icon'?: (props: { sidebarOpen: boolean }) => unknown;\n 'collapse-icon'?: (props: { collapsed: boolean }) => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebarId = createId('t-app-shell-sidebar');\n\nconst { value: isCollapsed, setValue: setCollapsed } = useControllableOpen(\n toRef(props, 'collapsed'),\n props.defaultCollapsed,\n (value) => {\n emit('update:collapsed', value);\n emit('collapse-change', value);\n },\n);\n\nconst { value: isSidebarOpen, setValue: setSidebarOpen } = useControllableOpen(\n toRef(props, 'sidebarOpen'),\n props.defaultSidebarOpen,\n (value) => {\n emit('update:sidebarOpen', value);\n emit('sidebar-open-change', value);\n },\n);\n\n// Auto responsive detection when `mobile` is not explicitly controlled.\nconst autoMobile = ref(false);\nlet mediaQuery: MediaQueryList | null = null;\n\nconst onMediaChange = (event: MediaQueryListEvent | MediaQueryList) => {\n autoMobile.value = event.matches;\n};\n\nconst setupMediaQuery = () => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return;\n }\n\n teardownMediaQuery();\n mediaQuery = window.matchMedia(`(max-width: ${props.breakpoint})`);\n autoMobile.value = mediaQuery.matches;\n mediaQuery.addEventListener?.('change', onMediaChange);\n};\n\nconst teardownMediaQuery = () => {\n mediaQuery?.removeEventListener?.('change', onMediaChange);\n mediaQuery = null;\n};\n\nconst isMobile = computed(() => props.mobile ?? autoMobile.value);\n\n// Collapsing is desktop-only, so the built-in toggle follows the same rule.\nconst showCollapseToggle = computed(\n () => !isMobile.value && props.collapsible && props.showCollapseButton,\n);\n\n// Collapse is a desktop-only concept; the mobile drawer always shows the\n// expanded sidebar, so descendants and slots see `collapsed: false` there.\nconst effectiveCollapsed = computed(() =>\n isMobile.value ? false : isCollapsed.value,\n);\n\nconst surfaceRef = ref<HTMLElement | null>(null);\nconst previousFocusedElement = ref<HTMLElement | null>(null);\nlet previousBodyOverflow = '';\nlet bodyLocked = false;\n\nconst lockBodyScroll = () => {\n if (typeof document === 'undefined' || bodyLocked) {\n return;\n }\n\n previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n bodyLocked = true;\n};\n\nconst unlockBodyScroll = () => {\n if (typeof document === 'undefined' || !bodyLocked) {\n return;\n }\n\n document.body.style.overflow = previousBodyOverflow;\n bodyLocked = false;\n};\n\nconst focusSurface = () => {\n nextTick(() => {\n if (!surfaceRef.value) {\n return;\n }\n\n const focused = focusFirst(surfaceRef.value);\n if (!focused) {\n surfaceRef.value.focus();\n }\n });\n};\n\nconst openSidebar = () => {\n previousFocusedElement.value =\n typeof document === 'undefined'\n ? null\n : (document.activeElement as HTMLElement | null);\n setSidebarOpen(true);\n};\n\nconst closeSidebar = () => {\n setSidebarOpen(false);\n};\n\nconst toggleSidebar = () => {\n if (isSidebarOpen.value) {\n closeSidebar();\n } else {\n openSidebar();\n }\n};\n\nconst toggleCollapsed = () => {\n if (!props.collapsible) {\n return;\n }\n\n setCollapsed(!isCollapsed.value);\n};\n\nconst onOverlayClick = () => {\n if (props.closeOnOverlay) {\n closeSidebar();\n }\n};\n\nconst onKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event) && props.closeOnEscape) {\n event.preventDefault();\n closeSidebar();\n return;\n }\n\n if (event.key !== 'Tab' || !surfaceRef.value) {\n return;\n }\n\n const focusable = getFocusableElements(surfaceRef.value);\n\n if (!focusable.length) {\n event.preventDefault();\n surfaceRef.value.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const activeElement = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && activeElement === first) {\n event.preventDefault();\n focusLast(surfaceRef.value);\n }\n\n if (!event.shiftKey && activeElement === last) {\n event.preventDefault();\n focusFirst(surfaceRef.value);\n }\n};\n\n// The off-canvas drawer only exists in mobile mode; keep body-scroll lock and\n// focus handling in sync with both the open flag and the responsive mode.\nconst syncOverlayState = (open: boolean, wasOpen: boolean) => {\n if (open && isMobile.value) {\n lockBodyScroll();\n focusSurface();\n return;\n }\n\n if (wasOpen) {\n unlockBodyScroll();\n nextTick(() => {\n previousFocusedElement.value?.focus();\n });\n }\n};\n\nwatch(isSidebarOpen, (value, previousValue) => {\n syncOverlayState(value, Boolean(previousValue));\n});\n\nwatch(isMobile, (mobile) => {\n if (!isSidebarOpen.value) {\n return;\n }\n\n // Leaving mobile removes the drawer from the DOM, so release the lock and\n // restore focus; re-entering mobile while \"open\" re-arms the overlay.\n if (mobile) {\n lockBodyScroll();\n focusSurface();\n } else {\n unlockBodyScroll();\n }\n});\n\nwatch(\n () => props.breakpoint,\n () => {\n if (typeof window !== 'undefined') {\n setupMediaQuery();\n }\n },\n);\n\nonMounted(() => {\n setupMediaQuery();\n});\n\nonBeforeUnmount(() => {\n teardownMediaQuery();\n unlockBodyScroll();\n});\n\nprovide(treeSidebarInjectionKey, {\n collapsed: effectiveCollapsed,\n});\n\nconst rootClasses = computed(() => [\n 't-app-shell',\n `t-app-shell--${props.side}`,\n {\n 'is-mobile': isMobile.value,\n 'is-collapsed':\n !isMobile.value && props.collapsible && isCollapsed.value,\n 'is-sidebar-open': isMobile.value && isSidebarOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => [\n {\n '--tree-app-shell-sidebar-width': props.sidebarWidth,\n '--tree-app-shell-collapsed-width': props.collapsedWidth,\n },\n attrs.style,\n]);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst slotProps = computed<AppShellSlotProps>(() => ({\n mobile: isMobile.value,\n collapsed: effectiveCollapsed.value,\n sidebarOpen: isSidebarOpen.value,\n toggleSidebar,\n closeSidebar,\n toggleCollapsed,\n}));\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-mobile=\"isMobile ? 'true' : 'false'\"\n >\n <header\n class=\"t-app-shell__header\"\n :class=\"{ 'has-start': Boolean($slots['header-start']) }\"\n >\n <button\n v-if=\"isMobile && showMenuButton\"\n type=\"button\"\n class=\"t-app-shell__menu-button\"\n :aria-label=\"menuLabel\"\n :aria-expanded=\"isSidebarOpen\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleSidebar\"\n >\n <slot\n name=\"menu-icon\"\n :sidebar-open=\"isSidebarOpen\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"6\"\n y2=\"6\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"12\"\n y2=\"12\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"18\"\n y2=\"18\"\n />\n </svg>\n </slot>\n </button>\n\n <div\n v-if=\"$slots['header-start']\"\n class=\"t-app-shell__header-start\"\n >\n <slot\n name=\"header-start\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__header-content\">\n <slot\n name=\"header\"\n v-bind=\"slotProps\"\n />\n </div>\n </header>\n\n <aside\n v-if=\"!isMobile\"\n :id=\"sidebarId\"\n class=\"t-app-shell__sidebar\"\n :aria-label=\"sidebarLabel\"\n >\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer'] || showCollapseToggle\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n\n <button\n v-if=\"showCollapseToggle\"\n type=\"button\"\n class=\"t-app-shell__collapse-button\"\n :aria-label=\"isCollapsed ? expandLabel : collapseLabel\"\n :aria-expanded=\"!isCollapsed\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleCollapsed\"\n >\n <slot\n name=\"collapse-icon\"\n :collapsed=\"effectiveCollapsed\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"3\"\n y=\"3\"\n width=\"18\"\n height=\"18\"\n rx=\"2\"\n />\n <path d=\"M9 3v18\" />\n </svg>\n </slot>\n </button>\n </div>\n </aside>\n\n <main class=\"t-app-shell__main\">\n <slot\n :mobile=\"isMobile\"\n :collapsed=\"effectiveCollapsed\"\n />\n </main>\n\n <transition name=\"t-app-shell-fade\">\n <div\n v-if=\"isMobile && isSidebarOpen\"\n class=\"t-app-shell__overlay\"\n >\n <div\n class=\"t-app-shell__backdrop\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n <aside\n :id=\"sidebarId\"\n ref=\"surfaceRef\"\n class=\"t-app-shell__drawer\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"sidebarLabel\"\n tabindex=\"-1\"\n @keydown=\"onKeydown\"\n >\n <div class=\"t-app-shell__drawer-topbar\">\n <button\n type=\"button\"\n class=\"t-app-shell__drawer-close\"\n :aria-label=\"closeLabel\"\n @click=\"closeSidebar\"\n >\n <CloseIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n <div class=\"t-app-shell__drawer-body\">\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer']\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n </div>\n </div>\n </aside>\n </div>\n </transition>\n </component>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TAppShell.vue_vue_type_script_setup_true_lang.cjs","names":["$slots"],"sources":["../../src/components/TAppShell.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n createId,\n focusFirst,\n focusLast,\n getFocusableElements,\n isEscapeKey,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSidebarSide } from './TSidebar.vue';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst CloseIcon = computed(() => getTreeIcon('x'));\n\nconst props = withDefaults(\n defineProps<{\n as?: string;\n side?: TSidebarSide;\n sidebarWidth?: string;\n collapsedWidth?: string;\n collapsible?: boolean;\n collapsed?: boolean;\n defaultCollapsed?: boolean;\n mobile?: boolean;\n breakpoint?: string;\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n closeOnEscape?: boolean;\n closeOnOverlay?: boolean;\n showMenuButton?: boolean;\n /** Render the built-in collapse toggle in the sidebar footer (desktop). */\n showCollapseButton?: boolean;\n menuLabel?: string;\n closeLabel?: string;\n sidebarLabel?: string;\n collapseLabel?: string;\n expandLabel?: string;\n }>(),\n {\n as: 'div',\n side: 'left',\n sidebarWidth: '17.5rem',\n collapsedWidth: '4.75rem',\n collapsible: false,\n collapsed: undefined,\n defaultCollapsed: false,\n mobile: undefined,\n breakpoint: '768px',\n sidebarOpen: undefined,\n defaultSidebarOpen: false,\n closeOnEscape: true,\n closeOnOverlay: true,\n showMenuButton: true,\n showCollapseButton: true,\n menuLabel: 'Open menu',\n closeLabel: 'Close menu',\n sidebarLabel: 'Sidebar',\n collapseLabel: 'Collapse sidebar',\n expandLabel: 'Expand sidebar',\n },\n);\n\nconst emit = defineEmits<{\n 'update:collapsed': [value: boolean];\n 'collapse-change': [value: boolean];\n 'update:sidebarOpen': [value: boolean];\n 'sidebar-open-change': [value: boolean];\n}>();\n\nexport interface AppShellSlotProps {\n mobile: boolean;\n collapsed: boolean;\n sidebarOpen: boolean;\n toggleSidebar: () => void;\n closeSidebar: () => void;\n toggleCollapsed: () => void;\n}\n\ndefineSlots<{\n header?: (props: AppShellSlotProps) => unknown;\n /** Header region over the sidebar rail; the `header` slot then aligns with the content panel. */\n 'header-start'?: (props: AppShellSlotProps) => unknown;\n /** Pinned above the sidebar body, inset to line up with nav item icons. */\n 'sidebar-header'?: (props: AppShellSlotProps) => unknown;\n sidebar?: (props: AppShellSlotProps) => unknown;\n /** Pinned to the bottom of the sidebar, inset to line up with nav item icons. */\n 'sidebar-footer'?: (props: AppShellSlotProps) => unknown;\n default?: (props: Pick<AppShellSlotProps, 'mobile' | 'collapsed'>) => unknown;\n 'menu-icon'?: (props: { sidebarOpen: boolean }) => unknown;\n 'collapse-icon'?: (props: { collapsed: boolean }) => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebarId = createId('t-app-shell-sidebar');\n\nconst { value: isCollapsed, setValue: setCollapsed } = useControllableOpen(\n toRef(props, 'collapsed'),\n props.defaultCollapsed,\n (value) => {\n emit('update:collapsed', value);\n emit('collapse-change', value);\n },\n);\n\nconst { value: isSidebarOpen, setValue: setSidebarOpen } = useControllableOpen(\n toRef(props, 'sidebarOpen'),\n props.defaultSidebarOpen,\n (value) => {\n emit('update:sidebarOpen', value);\n emit('sidebar-open-change', value);\n },\n);\n\n// Auto responsive detection when `mobile` is not explicitly controlled.\nconst autoMobile = ref(false);\nlet mediaQuery: MediaQueryList | null = null;\n\nconst onMediaChange = (event: MediaQueryListEvent | MediaQueryList) => {\n autoMobile.value = event.matches;\n};\n\nconst setupMediaQuery = () => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return;\n }\n\n teardownMediaQuery();\n mediaQuery = window.matchMedia(`(max-width: ${props.breakpoint})`);\n autoMobile.value = mediaQuery.matches;\n mediaQuery.addEventListener?.('change', onMediaChange);\n};\n\nconst teardownMediaQuery = () => {\n mediaQuery?.removeEventListener?.('change', onMediaChange);\n mediaQuery = null;\n};\n\nconst isMobile = computed(() => props.mobile ?? autoMobile.value);\n\n// Collapsing is desktop-only, so the built-in toggle follows the same rule.\nconst showCollapseToggle = computed(\n () => !isMobile.value && props.collapsible && props.showCollapseButton,\n);\n\n// Collapse is a desktop-only concept; the mobile drawer always shows the\n// expanded sidebar, so descendants and slots see `collapsed: false` there.\nconst effectiveCollapsed = computed(() =>\n isMobile.value ? false : isCollapsed.value,\n);\n\nconst surfaceRef = ref<HTMLElement | null>(null);\nconst previousFocusedElement = ref<HTMLElement | null>(null);\nlet previousBodyOverflow = '';\nlet bodyLocked = false;\n\nconst lockBodyScroll = () => {\n if (typeof document === 'undefined' || bodyLocked) {\n return;\n }\n\n previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n bodyLocked = true;\n};\n\nconst unlockBodyScroll = () => {\n if (typeof document === 'undefined' || !bodyLocked) {\n return;\n }\n\n document.body.style.overflow = previousBodyOverflow;\n bodyLocked = false;\n};\n\nconst focusSurface = () => {\n nextTick(() => {\n if (!surfaceRef.value) {\n return;\n }\n\n const focused = focusFirst(surfaceRef.value);\n if (!focused) {\n surfaceRef.value.focus();\n }\n });\n};\n\nconst openSidebar = () => {\n previousFocusedElement.value =\n typeof document === 'undefined'\n ? null\n : (document.activeElement as HTMLElement | null);\n setSidebarOpen(true);\n};\n\nconst closeSidebar = () => {\n setSidebarOpen(false);\n};\n\nconst toggleSidebar = () => {\n if (isSidebarOpen.value) {\n closeSidebar();\n } else {\n openSidebar();\n }\n};\n\nconst toggleCollapsed = () => {\n if (!props.collapsible) {\n return;\n }\n\n setCollapsed(!isCollapsed.value);\n};\n\nconst onOverlayClick = () => {\n if (props.closeOnOverlay) {\n closeSidebar();\n }\n};\n\nconst onKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event) && props.closeOnEscape) {\n event.preventDefault();\n closeSidebar();\n return;\n }\n\n if (event.key !== 'Tab' || !surfaceRef.value) {\n return;\n }\n\n const focusable = getFocusableElements(surfaceRef.value);\n\n if (!focusable.length) {\n event.preventDefault();\n surfaceRef.value.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const activeElement = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && activeElement === first) {\n event.preventDefault();\n focusLast(surfaceRef.value);\n }\n\n if (!event.shiftKey && activeElement === last) {\n event.preventDefault();\n focusFirst(surfaceRef.value);\n }\n};\n\n// The off-canvas drawer only exists in mobile mode; keep body-scroll lock and\n// focus handling in sync with both the open flag and the responsive mode.\nconst syncOverlayState = (open: boolean, wasOpen: boolean) => {\n if (open && isMobile.value) {\n lockBodyScroll();\n focusSurface();\n return;\n }\n\n if (wasOpen) {\n unlockBodyScroll();\n nextTick(() => {\n previousFocusedElement.value?.focus();\n });\n }\n};\n\nwatch(isSidebarOpen, (value, previousValue) => {\n syncOverlayState(value, Boolean(previousValue));\n});\n\nwatch(isMobile, (mobile) => {\n if (!isSidebarOpen.value) {\n return;\n }\n\n // Leaving mobile removes the drawer from the DOM, so release the lock and\n // restore focus; re-entering mobile while \"open\" re-arms the overlay.\n if (mobile) {\n lockBodyScroll();\n focusSurface();\n } else {\n unlockBodyScroll();\n }\n});\n\nwatch(\n () => props.breakpoint,\n () => {\n if (typeof window !== 'undefined') {\n setupMediaQuery();\n }\n },\n);\n\nonMounted(() => {\n setupMediaQuery();\n});\n\nonBeforeUnmount(() => {\n teardownMediaQuery();\n unlockBodyScroll();\n});\n\nprovide(treeSidebarInjectionKey, {\n collapsed: effectiveCollapsed,\n});\n\nconst rootClasses = computed(() => [\n 't-app-shell',\n `t-app-shell--${props.side}`,\n {\n 'is-mobile': isMobile.value,\n 'is-collapsed':\n !isMobile.value && props.collapsible && isCollapsed.value,\n 'is-sidebar-open': isMobile.value && isSidebarOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => [\n {\n '--tree-app-shell-sidebar-width': props.sidebarWidth,\n '--tree-app-shell-collapsed-width': props.collapsedWidth,\n },\n attrs.style,\n]);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst slotProps = computed<AppShellSlotProps>(() => ({\n mobile: isMobile.value,\n collapsed: effectiveCollapsed.value,\n sidebarOpen: isSidebarOpen.value,\n toggleSidebar,\n closeSidebar,\n toggleCollapsed,\n}));\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-mobile=\"isMobile ? 'true' : 'false'\"\n >\n <header\n class=\"t-app-shell__header\"\n :class=\"{ 'has-start': Boolean($slots['header-start']) }\"\n >\n <button\n v-if=\"isMobile && showMenuButton\"\n type=\"button\"\n class=\"t-app-shell__menu-button\"\n :aria-label=\"menuLabel\"\n :aria-expanded=\"isSidebarOpen\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleSidebar\"\n >\n <slot\n name=\"menu-icon\"\n :sidebar-open=\"isSidebarOpen\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"6\"\n y2=\"6\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"12\"\n y2=\"12\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"18\"\n y2=\"18\"\n />\n </svg>\n </slot>\n </button>\n\n <div\n v-if=\"$slots['header-start']\"\n class=\"t-app-shell__header-start\"\n >\n <slot\n name=\"header-start\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__header-content\">\n <slot\n name=\"header\"\n v-bind=\"slotProps\"\n />\n </div>\n </header>\n\n <aside\n v-if=\"!isMobile\"\n :id=\"sidebarId\"\n class=\"t-app-shell__sidebar\"\n :aria-label=\"sidebarLabel\"\n >\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer'] || showCollapseToggle\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n\n <button\n v-if=\"showCollapseToggle\"\n type=\"button\"\n class=\"t-app-shell__collapse-button\"\n :aria-label=\"isCollapsed ? expandLabel : collapseLabel\"\n :aria-expanded=\"!isCollapsed\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleCollapsed\"\n >\n <slot\n name=\"collapse-icon\"\n :collapsed=\"effectiveCollapsed\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"3\"\n y=\"3\"\n width=\"18\"\n height=\"18\"\n rx=\"2\"\n />\n <path d=\"M9 3v18\" />\n </svg>\n </slot>\n </button>\n </div>\n </aside>\n\n <main class=\"t-app-shell__main\">\n <slot\n :mobile=\"isMobile\"\n :collapsed=\"effectiveCollapsed\"\n />\n </main>\n\n <transition name=\"t-app-shell-fade\">\n <div\n v-if=\"isMobile && isSidebarOpen\"\n class=\"t-app-shell__overlay\"\n >\n <div\n class=\"t-app-shell__backdrop\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n <aside\n :id=\"sidebarId\"\n ref=\"surfaceRef\"\n class=\"t-app-shell__drawer\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"sidebarLabel\"\n tabindex=\"-1\"\n @keydown=\"onKeydown\"\n >\n <div class=\"t-app-shell__drawer-topbar\">\n <button\n type=\"button\"\n class=\"t-app-shell__drawer-close\"\n :aria-label=\"closeLabel\"\n @click=\"closeSidebar\"\n >\n <CloseIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n <div class=\"t-app-shell__drawer-body\">\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer']\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n </div>\n </div>\n </aside>\n </div>\n </transition>\n </component>\n</template>\n"],"mappings":"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"}
|
|
@@ -92,7 +92,7 @@ var S = [
|
|
|
92
92
|
"sidebar-open-change"
|
|
93
93
|
],
|
|
94
94
|
setup(l, { emit: E }) {
|
|
95
|
-
let D = ue("x"), O = l, k = E, A = ae(), j = oe("t-app-shell-sidebar"), { value: M, setValue: Te } = e(_(O, "collapsed"), O.defaultCollapsed, (e) => {
|
|
95
|
+
let D = r(() => ue("x")), O = l, k = E, A = ae(), j = oe("t-app-shell-sidebar"), { value: M, setValue: Te } = e(_(O, "collapsed"), O.defaultCollapsed, (e) => {
|
|
96
96
|
k("update:collapsed", e), k("collapse-change", e);
|
|
97
97
|
}), { value: N, setValue: P } = e(_(O, "sidebarOpen"), O.defaultSidebarOpen, (e) => {
|
|
98
98
|
k("update:sidebarOpen", e), k("sidebar-open-change", e);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TAppShell.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../src/components/TAppShell.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n createId,\n focusFirst,\n focusLast,\n getFocusableElements,\n isEscapeKey,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSidebarSide } from './TSidebar.vue';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst CloseIcon = getTreeIcon('x');\n\nconst props = withDefaults(\n defineProps<{\n as?: string;\n side?: TSidebarSide;\n sidebarWidth?: string;\n collapsedWidth?: string;\n collapsible?: boolean;\n collapsed?: boolean;\n defaultCollapsed?: boolean;\n mobile?: boolean;\n breakpoint?: string;\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n closeOnEscape?: boolean;\n closeOnOverlay?: boolean;\n showMenuButton?: boolean;\n /** Render the built-in collapse toggle in the sidebar footer (desktop). */\n showCollapseButton?: boolean;\n menuLabel?: string;\n closeLabel?: string;\n sidebarLabel?: string;\n collapseLabel?: string;\n expandLabel?: string;\n }>(),\n {\n as: 'div',\n side: 'left',\n sidebarWidth: '17.5rem',\n collapsedWidth: '4.75rem',\n collapsible: false,\n collapsed: undefined,\n defaultCollapsed: false,\n mobile: undefined,\n breakpoint: '768px',\n sidebarOpen: undefined,\n defaultSidebarOpen: false,\n closeOnEscape: true,\n closeOnOverlay: true,\n showMenuButton: true,\n showCollapseButton: true,\n menuLabel: 'Open menu',\n closeLabel: 'Close menu',\n sidebarLabel: 'Sidebar',\n collapseLabel: 'Collapse sidebar',\n expandLabel: 'Expand sidebar',\n },\n);\n\nconst emit = defineEmits<{\n 'update:collapsed': [value: boolean];\n 'collapse-change': [value: boolean];\n 'update:sidebarOpen': [value: boolean];\n 'sidebar-open-change': [value: boolean];\n}>();\n\nexport interface AppShellSlotProps {\n mobile: boolean;\n collapsed: boolean;\n sidebarOpen: boolean;\n toggleSidebar: () => void;\n closeSidebar: () => void;\n toggleCollapsed: () => void;\n}\n\ndefineSlots<{\n header?: (props: AppShellSlotProps) => unknown;\n /** Header region over the sidebar rail; the `header` slot then aligns with the content panel. */\n 'header-start'?: (props: AppShellSlotProps) => unknown;\n /** Pinned above the sidebar body, inset to line up with nav item icons. */\n 'sidebar-header'?: (props: AppShellSlotProps) => unknown;\n sidebar?: (props: AppShellSlotProps) => unknown;\n /** Pinned to the bottom of the sidebar, inset to line up with nav item icons. */\n 'sidebar-footer'?: (props: AppShellSlotProps) => unknown;\n default?: (props: Pick<AppShellSlotProps, 'mobile' | 'collapsed'>) => unknown;\n 'menu-icon'?: (props: { sidebarOpen: boolean }) => unknown;\n 'collapse-icon'?: (props: { collapsed: boolean }) => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebarId = createId('t-app-shell-sidebar');\n\nconst { value: isCollapsed, setValue: setCollapsed } = useControllableOpen(\n toRef(props, 'collapsed'),\n props.defaultCollapsed,\n (value) => {\n emit('update:collapsed', value);\n emit('collapse-change', value);\n },\n);\n\nconst { value: isSidebarOpen, setValue: setSidebarOpen } = useControllableOpen(\n toRef(props, 'sidebarOpen'),\n props.defaultSidebarOpen,\n (value) => {\n emit('update:sidebarOpen', value);\n emit('sidebar-open-change', value);\n },\n);\n\n// Auto responsive detection when `mobile` is not explicitly controlled.\nconst autoMobile = ref(false);\nlet mediaQuery: MediaQueryList | null = null;\n\nconst onMediaChange = (event: MediaQueryListEvent | MediaQueryList) => {\n autoMobile.value = event.matches;\n};\n\nconst setupMediaQuery = () => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return;\n }\n\n teardownMediaQuery();\n mediaQuery = window.matchMedia(`(max-width: ${props.breakpoint})`);\n autoMobile.value = mediaQuery.matches;\n mediaQuery.addEventListener?.('change', onMediaChange);\n};\n\nconst teardownMediaQuery = () => {\n mediaQuery?.removeEventListener?.('change', onMediaChange);\n mediaQuery = null;\n};\n\nconst isMobile = computed(() => props.mobile ?? autoMobile.value);\n\n// Collapsing is desktop-only, so the built-in toggle follows the same rule.\nconst showCollapseToggle = computed(\n () => !isMobile.value && props.collapsible && props.showCollapseButton,\n);\n\n// Collapse is a desktop-only concept; the mobile drawer always shows the\n// expanded sidebar, so descendants and slots see `collapsed: false` there.\nconst effectiveCollapsed = computed(() =>\n isMobile.value ? false : isCollapsed.value,\n);\n\nconst surfaceRef = ref<HTMLElement | null>(null);\nconst previousFocusedElement = ref<HTMLElement | null>(null);\nlet previousBodyOverflow = '';\nlet bodyLocked = false;\n\nconst lockBodyScroll = () => {\n if (typeof document === 'undefined' || bodyLocked) {\n return;\n }\n\n previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n bodyLocked = true;\n};\n\nconst unlockBodyScroll = () => {\n if (typeof document === 'undefined' || !bodyLocked) {\n return;\n }\n\n document.body.style.overflow = previousBodyOverflow;\n bodyLocked = false;\n};\n\nconst focusSurface = () => {\n nextTick(() => {\n if (!surfaceRef.value) {\n return;\n }\n\n const focused = focusFirst(surfaceRef.value);\n if (!focused) {\n surfaceRef.value.focus();\n }\n });\n};\n\nconst openSidebar = () => {\n previousFocusedElement.value =\n typeof document === 'undefined'\n ? null\n : (document.activeElement as HTMLElement | null);\n setSidebarOpen(true);\n};\n\nconst closeSidebar = () => {\n setSidebarOpen(false);\n};\n\nconst toggleSidebar = () => {\n if (isSidebarOpen.value) {\n closeSidebar();\n } else {\n openSidebar();\n }\n};\n\nconst toggleCollapsed = () => {\n if (!props.collapsible) {\n return;\n }\n\n setCollapsed(!isCollapsed.value);\n};\n\nconst onOverlayClick = () => {\n if (props.closeOnOverlay) {\n closeSidebar();\n }\n};\n\nconst onKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event) && props.closeOnEscape) {\n event.preventDefault();\n closeSidebar();\n return;\n }\n\n if (event.key !== 'Tab' || !surfaceRef.value) {\n return;\n }\n\n const focusable = getFocusableElements(surfaceRef.value);\n\n if (!focusable.length) {\n event.preventDefault();\n surfaceRef.value.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const activeElement = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && activeElement === first) {\n event.preventDefault();\n focusLast(surfaceRef.value);\n }\n\n if (!event.shiftKey && activeElement === last) {\n event.preventDefault();\n focusFirst(surfaceRef.value);\n }\n};\n\n// The off-canvas drawer only exists in mobile mode; keep body-scroll lock and\n// focus handling in sync with both the open flag and the responsive mode.\nconst syncOverlayState = (open: boolean, wasOpen: boolean) => {\n if (open && isMobile.value) {\n lockBodyScroll();\n focusSurface();\n return;\n }\n\n if (wasOpen) {\n unlockBodyScroll();\n nextTick(() => {\n previousFocusedElement.value?.focus();\n });\n }\n};\n\nwatch(isSidebarOpen, (value, previousValue) => {\n syncOverlayState(value, Boolean(previousValue));\n});\n\nwatch(isMobile, (mobile) => {\n if (!isSidebarOpen.value) {\n return;\n }\n\n // Leaving mobile removes the drawer from the DOM, so release the lock and\n // restore focus; re-entering mobile while \"open\" re-arms the overlay.\n if (mobile) {\n lockBodyScroll();\n focusSurface();\n } else {\n unlockBodyScroll();\n }\n});\n\nwatch(\n () => props.breakpoint,\n () => {\n if (typeof window !== 'undefined') {\n setupMediaQuery();\n }\n },\n);\n\nonMounted(() => {\n setupMediaQuery();\n});\n\nonBeforeUnmount(() => {\n teardownMediaQuery();\n unlockBodyScroll();\n});\n\nprovide(treeSidebarInjectionKey, {\n collapsed: effectiveCollapsed,\n});\n\nconst rootClasses = computed(() => [\n 't-app-shell',\n `t-app-shell--${props.side}`,\n {\n 'is-mobile': isMobile.value,\n 'is-collapsed':\n !isMobile.value && props.collapsible && isCollapsed.value,\n 'is-sidebar-open': isMobile.value && isSidebarOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => [\n {\n '--tree-app-shell-sidebar-width': props.sidebarWidth,\n '--tree-app-shell-collapsed-width': props.collapsedWidth,\n },\n attrs.style,\n]);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst slotProps = computed<AppShellSlotProps>(() => ({\n mobile: isMobile.value,\n collapsed: effectiveCollapsed.value,\n sidebarOpen: isSidebarOpen.value,\n toggleSidebar,\n closeSidebar,\n toggleCollapsed,\n}));\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-mobile=\"isMobile ? 'true' : 'false'\"\n >\n <header\n class=\"t-app-shell__header\"\n :class=\"{ 'has-start': Boolean($slots['header-start']) }\"\n >\n <button\n v-if=\"isMobile && showMenuButton\"\n type=\"button\"\n class=\"t-app-shell__menu-button\"\n :aria-label=\"menuLabel\"\n :aria-expanded=\"isSidebarOpen\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleSidebar\"\n >\n <slot\n name=\"menu-icon\"\n :sidebar-open=\"isSidebarOpen\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"6\"\n y2=\"6\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"12\"\n y2=\"12\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"18\"\n y2=\"18\"\n />\n </svg>\n </slot>\n </button>\n\n <div\n v-if=\"$slots['header-start']\"\n class=\"t-app-shell__header-start\"\n >\n <slot\n name=\"header-start\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__header-content\">\n <slot\n name=\"header\"\n v-bind=\"slotProps\"\n />\n </div>\n </header>\n\n <aside\n v-if=\"!isMobile\"\n :id=\"sidebarId\"\n class=\"t-app-shell__sidebar\"\n :aria-label=\"sidebarLabel\"\n >\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer'] || showCollapseToggle\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n\n <button\n v-if=\"showCollapseToggle\"\n type=\"button\"\n class=\"t-app-shell__collapse-button\"\n :aria-label=\"isCollapsed ? expandLabel : collapseLabel\"\n :aria-expanded=\"!isCollapsed\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleCollapsed\"\n >\n <slot\n name=\"collapse-icon\"\n :collapsed=\"effectiveCollapsed\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"3\"\n y=\"3\"\n width=\"18\"\n height=\"18\"\n rx=\"2\"\n />\n <path d=\"M9 3v18\" />\n </svg>\n </slot>\n </button>\n </div>\n </aside>\n\n <main class=\"t-app-shell__main\">\n <slot\n :mobile=\"isMobile\"\n :collapsed=\"effectiveCollapsed\"\n />\n </main>\n\n <transition name=\"t-app-shell-fade\">\n <div\n v-if=\"isMobile && isSidebarOpen\"\n class=\"t-app-shell__overlay\"\n >\n <div\n class=\"t-app-shell__backdrop\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n <aside\n :id=\"sidebarId\"\n ref=\"surfaceRef\"\n class=\"t-app-shell__drawer\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"sidebarLabel\"\n tabindex=\"-1\"\n @keydown=\"onKeydown\"\n >\n <div class=\"t-app-shell__drawer-topbar\">\n <button\n type=\"button\"\n class=\"t-app-shell__drawer-close\"\n :aria-label=\"closeLabel\"\n @click=\"closeSidebar\"\n >\n <CloseIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n <div class=\"t-app-shell__drawer-body\">\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer']\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n </div>\n </div>\n </aside>\n </div>\n </transition>\n </component>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TAppShell.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../src/components/TAppShell.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n createId,\n focusFirst,\n focusLast,\n getFocusableElements,\n isEscapeKey,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSidebarSide } from './TSidebar.vue';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst CloseIcon = computed(() => getTreeIcon('x'));\n\nconst props = withDefaults(\n defineProps<{\n as?: string;\n side?: TSidebarSide;\n sidebarWidth?: string;\n collapsedWidth?: string;\n collapsible?: boolean;\n collapsed?: boolean;\n defaultCollapsed?: boolean;\n mobile?: boolean;\n breakpoint?: string;\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n closeOnEscape?: boolean;\n closeOnOverlay?: boolean;\n showMenuButton?: boolean;\n /** Render the built-in collapse toggle in the sidebar footer (desktop). */\n showCollapseButton?: boolean;\n menuLabel?: string;\n closeLabel?: string;\n sidebarLabel?: string;\n collapseLabel?: string;\n expandLabel?: string;\n }>(),\n {\n as: 'div',\n side: 'left',\n sidebarWidth: '17.5rem',\n collapsedWidth: '4.75rem',\n collapsible: false,\n collapsed: undefined,\n defaultCollapsed: false,\n mobile: undefined,\n breakpoint: '768px',\n sidebarOpen: undefined,\n defaultSidebarOpen: false,\n closeOnEscape: true,\n closeOnOverlay: true,\n showMenuButton: true,\n showCollapseButton: true,\n menuLabel: 'Open menu',\n closeLabel: 'Close menu',\n sidebarLabel: 'Sidebar',\n collapseLabel: 'Collapse sidebar',\n expandLabel: 'Expand sidebar',\n },\n);\n\nconst emit = defineEmits<{\n 'update:collapsed': [value: boolean];\n 'collapse-change': [value: boolean];\n 'update:sidebarOpen': [value: boolean];\n 'sidebar-open-change': [value: boolean];\n}>();\n\nexport interface AppShellSlotProps {\n mobile: boolean;\n collapsed: boolean;\n sidebarOpen: boolean;\n toggleSidebar: () => void;\n closeSidebar: () => void;\n toggleCollapsed: () => void;\n}\n\ndefineSlots<{\n header?: (props: AppShellSlotProps) => unknown;\n /** Header region over the sidebar rail; the `header` slot then aligns with the content panel. */\n 'header-start'?: (props: AppShellSlotProps) => unknown;\n /** Pinned above the sidebar body, inset to line up with nav item icons. */\n 'sidebar-header'?: (props: AppShellSlotProps) => unknown;\n sidebar?: (props: AppShellSlotProps) => unknown;\n /** Pinned to the bottom of the sidebar, inset to line up with nav item icons. */\n 'sidebar-footer'?: (props: AppShellSlotProps) => unknown;\n default?: (props: Pick<AppShellSlotProps, 'mobile' | 'collapsed'>) => unknown;\n 'menu-icon'?: (props: { sidebarOpen: boolean }) => unknown;\n 'collapse-icon'?: (props: { collapsed: boolean }) => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebarId = createId('t-app-shell-sidebar');\n\nconst { value: isCollapsed, setValue: setCollapsed } = useControllableOpen(\n toRef(props, 'collapsed'),\n props.defaultCollapsed,\n (value) => {\n emit('update:collapsed', value);\n emit('collapse-change', value);\n },\n);\n\nconst { value: isSidebarOpen, setValue: setSidebarOpen } = useControllableOpen(\n toRef(props, 'sidebarOpen'),\n props.defaultSidebarOpen,\n (value) => {\n emit('update:sidebarOpen', value);\n emit('sidebar-open-change', value);\n },\n);\n\n// Auto responsive detection when `mobile` is not explicitly controlled.\nconst autoMobile = ref(false);\nlet mediaQuery: MediaQueryList | null = null;\n\nconst onMediaChange = (event: MediaQueryListEvent | MediaQueryList) => {\n autoMobile.value = event.matches;\n};\n\nconst setupMediaQuery = () => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return;\n }\n\n teardownMediaQuery();\n mediaQuery = window.matchMedia(`(max-width: ${props.breakpoint})`);\n autoMobile.value = mediaQuery.matches;\n mediaQuery.addEventListener?.('change', onMediaChange);\n};\n\nconst teardownMediaQuery = () => {\n mediaQuery?.removeEventListener?.('change', onMediaChange);\n mediaQuery = null;\n};\n\nconst isMobile = computed(() => props.mobile ?? autoMobile.value);\n\n// Collapsing is desktop-only, so the built-in toggle follows the same rule.\nconst showCollapseToggle = computed(\n () => !isMobile.value && props.collapsible && props.showCollapseButton,\n);\n\n// Collapse is a desktop-only concept; the mobile drawer always shows the\n// expanded sidebar, so descendants and slots see `collapsed: false` there.\nconst effectiveCollapsed = computed(() =>\n isMobile.value ? false : isCollapsed.value,\n);\n\nconst surfaceRef = ref<HTMLElement | null>(null);\nconst previousFocusedElement = ref<HTMLElement | null>(null);\nlet previousBodyOverflow = '';\nlet bodyLocked = false;\n\nconst lockBodyScroll = () => {\n if (typeof document === 'undefined' || bodyLocked) {\n return;\n }\n\n previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n bodyLocked = true;\n};\n\nconst unlockBodyScroll = () => {\n if (typeof document === 'undefined' || !bodyLocked) {\n return;\n }\n\n document.body.style.overflow = previousBodyOverflow;\n bodyLocked = false;\n};\n\nconst focusSurface = () => {\n nextTick(() => {\n if (!surfaceRef.value) {\n return;\n }\n\n const focused = focusFirst(surfaceRef.value);\n if (!focused) {\n surfaceRef.value.focus();\n }\n });\n};\n\nconst openSidebar = () => {\n previousFocusedElement.value =\n typeof document === 'undefined'\n ? null\n : (document.activeElement as HTMLElement | null);\n setSidebarOpen(true);\n};\n\nconst closeSidebar = () => {\n setSidebarOpen(false);\n};\n\nconst toggleSidebar = () => {\n if (isSidebarOpen.value) {\n closeSidebar();\n } else {\n openSidebar();\n }\n};\n\nconst toggleCollapsed = () => {\n if (!props.collapsible) {\n return;\n }\n\n setCollapsed(!isCollapsed.value);\n};\n\nconst onOverlayClick = () => {\n if (props.closeOnOverlay) {\n closeSidebar();\n }\n};\n\nconst onKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event) && props.closeOnEscape) {\n event.preventDefault();\n closeSidebar();\n return;\n }\n\n if (event.key !== 'Tab' || !surfaceRef.value) {\n return;\n }\n\n const focusable = getFocusableElements(surfaceRef.value);\n\n if (!focusable.length) {\n event.preventDefault();\n surfaceRef.value.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const activeElement = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && activeElement === first) {\n event.preventDefault();\n focusLast(surfaceRef.value);\n }\n\n if (!event.shiftKey && activeElement === last) {\n event.preventDefault();\n focusFirst(surfaceRef.value);\n }\n};\n\n// The off-canvas drawer only exists in mobile mode; keep body-scroll lock and\n// focus handling in sync with both the open flag and the responsive mode.\nconst syncOverlayState = (open: boolean, wasOpen: boolean) => {\n if (open && isMobile.value) {\n lockBodyScroll();\n focusSurface();\n return;\n }\n\n if (wasOpen) {\n unlockBodyScroll();\n nextTick(() => {\n previousFocusedElement.value?.focus();\n });\n }\n};\n\nwatch(isSidebarOpen, (value, previousValue) => {\n syncOverlayState(value, Boolean(previousValue));\n});\n\nwatch(isMobile, (mobile) => {\n if (!isSidebarOpen.value) {\n return;\n }\n\n // Leaving mobile removes the drawer from the DOM, so release the lock and\n // restore focus; re-entering mobile while \"open\" re-arms the overlay.\n if (mobile) {\n lockBodyScroll();\n focusSurface();\n } else {\n unlockBodyScroll();\n }\n});\n\nwatch(\n () => props.breakpoint,\n () => {\n if (typeof window !== 'undefined') {\n setupMediaQuery();\n }\n },\n);\n\nonMounted(() => {\n setupMediaQuery();\n});\n\nonBeforeUnmount(() => {\n teardownMediaQuery();\n unlockBodyScroll();\n});\n\nprovide(treeSidebarInjectionKey, {\n collapsed: effectiveCollapsed,\n});\n\nconst rootClasses = computed(() => [\n 't-app-shell',\n `t-app-shell--${props.side}`,\n {\n 'is-mobile': isMobile.value,\n 'is-collapsed':\n !isMobile.value && props.collapsible && isCollapsed.value,\n 'is-sidebar-open': isMobile.value && isSidebarOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => [\n {\n '--tree-app-shell-sidebar-width': props.sidebarWidth,\n '--tree-app-shell-collapsed-width': props.collapsedWidth,\n },\n attrs.style,\n]);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst slotProps = computed<AppShellSlotProps>(() => ({\n mobile: isMobile.value,\n collapsed: effectiveCollapsed.value,\n sidebarOpen: isSidebarOpen.value,\n toggleSidebar,\n closeSidebar,\n toggleCollapsed,\n}));\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-mobile=\"isMobile ? 'true' : 'false'\"\n >\n <header\n class=\"t-app-shell__header\"\n :class=\"{ 'has-start': Boolean($slots['header-start']) }\"\n >\n <button\n v-if=\"isMobile && showMenuButton\"\n type=\"button\"\n class=\"t-app-shell__menu-button\"\n :aria-label=\"menuLabel\"\n :aria-expanded=\"isSidebarOpen\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleSidebar\"\n >\n <slot\n name=\"menu-icon\"\n :sidebar-open=\"isSidebarOpen\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"6\"\n y2=\"6\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"12\"\n y2=\"12\"\n />\n <line\n x1=\"4\"\n x2=\"20\"\n y1=\"18\"\n y2=\"18\"\n />\n </svg>\n </slot>\n </button>\n\n <div\n v-if=\"$slots['header-start']\"\n class=\"t-app-shell__header-start\"\n >\n <slot\n name=\"header-start\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__header-content\">\n <slot\n name=\"header\"\n v-bind=\"slotProps\"\n />\n </div>\n </header>\n\n <aside\n v-if=\"!isMobile\"\n :id=\"sidebarId\"\n class=\"t-app-shell__sidebar\"\n :aria-label=\"sidebarLabel\"\n >\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer'] || showCollapseToggle\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n\n <button\n v-if=\"showCollapseToggle\"\n type=\"button\"\n class=\"t-app-shell__collapse-button\"\n :aria-label=\"isCollapsed ? expandLabel : collapseLabel\"\n :aria-expanded=\"!isCollapsed\"\n :aria-controls=\"sidebarId\"\n @click=\"toggleCollapsed\"\n >\n <slot\n name=\"collapse-icon\"\n :collapsed=\"effectiveCollapsed\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"3\"\n y=\"3\"\n width=\"18\"\n height=\"18\"\n rx=\"2\"\n />\n <path d=\"M9 3v18\" />\n </svg>\n </slot>\n </button>\n </div>\n </aside>\n\n <main class=\"t-app-shell__main\">\n <slot\n :mobile=\"isMobile\"\n :collapsed=\"effectiveCollapsed\"\n />\n </main>\n\n <transition name=\"t-app-shell-fade\">\n <div\n v-if=\"isMobile && isSidebarOpen\"\n class=\"t-app-shell__overlay\"\n >\n <div\n class=\"t-app-shell__backdrop\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n <aside\n :id=\"sidebarId\"\n ref=\"surfaceRef\"\n class=\"t-app-shell__drawer\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"sidebarLabel\"\n tabindex=\"-1\"\n @keydown=\"onKeydown\"\n >\n <div class=\"t-app-shell__drawer-topbar\">\n <button\n type=\"button\"\n class=\"t-app-shell__drawer-close\"\n :aria-label=\"closeLabel\"\n @click=\"closeSidebar\"\n >\n <CloseIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n <div class=\"t-app-shell__drawer-body\">\n <div\n v-if=\"$slots['sidebar-header']\"\n class=\"t-app-shell__sidebar-header\"\n >\n <slot\n name=\"sidebar-header\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div class=\"t-app-shell__sidebar-body\">\n <slot\n name=\"sidebar\"\n v-bind=\"slotProps\"\n />\n </div>\n\n <div\n v-if=\"$slots['sidebar-footer']\"\n class=\"t-app-shell__sidebar-footer\"\n >\n <slot\n name=\"sidebar-footer\"\n v-bind=\"slotProps\"\n />\n </div>\n </div>\n </aside>\n </div>\n </transition>\n </component>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BA,IAAM,IAAY,QAAe,GAAY,GAAG,CAAC,GAE3C,IAAQ,GAgDR,IAAO,GA8BP,IAAQ,GAAS,GACjB,IAAY,GAAS,qBAAqB,GAE1C,EAAE,OAAO,GAAa,UAAU,OAAiB,EACrD,EAAM,GAAO,WAAW,GACxB,EAAM,mBACL,MAAU;GAET,AADA,EAAK,oBAAoB,CAAK,GAC9B,EAAK,mBAAmB,CAAK;EAC/B,CACF,GAEM,EAAE,OAAO,GAAe,UAAU,MAAmB,EACzD,EAAM,GAAO,aAAa,GAC1B,EAAM,qBACL,MAAU;GAET,AADA,EAAK,sBAAsB,CAAK,GAChC,EAAK,uBAAuB,CAAK;EACnC,CACF,GAGM,IAAa,EAAI,EAAK,GACxB,IAAoC,MAElC,KAAiB,MAAgD;GACrE,EAAW,QAAQ,EAAM;EAC3B,GAEM,UAAwB;GACxB,OAAO,SAAW,OAAe,OAAO,OAAO,cAAe,eAIlE,EAAmB,GACnB,IAAa,OAAO,WAAW,eAAe,EAAM,WAAW,EAAE,GACjE,EAAW,QAAQ,EAAW,SAC9B,EAAW,mBAAmB,UAAU,CAAa;EACvD,GAEM,UAA2B;GAE/B,AADA,GAAY,sBAAsB,UAAU,CAAa,GACzD,IAAa;EACf,GAEM,IAAW,QAAe,EAAM,UAAU,EAAW,KAAK,GAG1D,IAAqB,QACnB,CAAC,EAAS,SAAS,EAAM,eAAe,EAAM,kBACtD,GAIM,IAAqB,QACzB,GAAS,SAAgB,EAAY,KACvC,GAEM,IAAa,EAAwB,IAAI,GACzC,IAAyB,EAAwB,IAAI,GACvD,IAAuB,IACvB,IAAa,IAEX,UAAuB;GACvB,OAAO,WAAa,OAAe,MAIvC,IAAuB,SAAS,KAAK,MAAM,UAC3C,SAAS,KAAK,MAAM,WAAW,UAC/B,IAAa;EACf,GAEM,UAAyB;GACzB,OAAO,WAAa,OAAe,CAAC,MAIxC,SAAS,KAAK,MAAM,WAAW,GAC/B,IAAa;EACf,GAEM,UAAqB;GACzB,QAAe;IACR,EAAW,UAIA,EAAW,EAAW,KACjC,KACH,EAAW,MAAM,MAAM;GAE3B,CAAC;EACH,GAEM,WAAoB;GAKxB,AAJA,EAAuB,QACrB,OAAO,WAAa,MAChB,OACC,SAAS,eAChB,EAAe,EAAI;EACrB,GAEM,UAAqB;GACzB,EAAe,EAAK;EACtB,GAEM,UAAsB;GAC1B,AAAI,EAAc,QAChB,EAAa,IAEb,GAAY;EAEhB,GAEM,UAAwB;GACvB,EAAM,eAIX,GAAa,CAAC,EAAY,KAAK;EACjC,GAEM,WAAuB;GAC3B,AAAI,EAAM,kBACR,EAAa;EAEjB,GAEM,MAAa,MAAyB;GAC1C,IAAI,GAAY,CAAK,KAAK,EAAM,eAAe;IAE7C,AADA,EAAM,eAAe,GACrB,EAAa;IACb;GACF;GAEA,IAAI,EAAM,QAAQ,SAAS,CAAC,EAAW,OACrC;GAGF,IAAM,IAAY,GAAqB,EAAW,KAAK;GAEvD,IAAI,CAAC,EAAU,QAAQ;IAErB,AADA,EAAM,eAAe,GACrB,EAAW,MAAM,MAAM;IACvB;GACF;GAEA,IAAM,IAAQ,EAAU,IAClB,IAAO,EAAU,EAAU,SAAS,IACpC,IAAgB,SAAS;GAO/B,AALI,EAAM,YAAY,MAAkB,MACtC,EAAM,eAAe,GACrB,GAAU,EAAW,KAAK,IAGxB,CAAC,EAAM,YAAY,MAAkB,MACvC,EAAM,eAAe,GACrB,EAAW,EAAW,KAAK;EAE/B,GAIM,MAAoB,GAAe,MAAqB;GAC5D,IAAI,KAAQ,EAAS,OAAO;IAE1B,AADA,EAAe,GACf,EAAa;IACb;GACF;GAEA,AAAI,MACF,EAAiB,GACjB,QAAe;IACb,EAAuB,OAAO,MAAM;GACtC,CAAC;EAEL;EAuCA,AArCA,EAAM,IAAgB,GAAO,MAAkB;GAC7C,GAAiB,GAAO,EAAQ,CAAc;EAChD,CAAC,GAED,EAAM,IAAW,MAAW;GACrB,EAAc,UAMf,KACF,EAAe,GACf,EAAa,KAEb,EAAiB;EAErB,CAAC,GAED,QACQ,EAAM,kBACN;GACJ,AAAI,OAAO,SAAW,OACpB,EAAgB;EAEpB,CACF,GAEA,SAAgB;GACd,EAAgB;EAClB,CAAC,GAED,SAAsB;GAEpB,AADA,EAAmB,GACnB,EAAiB;EACnB,CAAC,GAED,GAAQ,GAAyB,EAC/B,WAAW,EACb,CAAC;EAED,IAAM,KAAc,QAAe;GACjC;GACA,gBAAgB,EAAM;GACtB;IACE,aAAa,EAAS;IACtB,gBACE,CAAC,EAAS,SAAS,EAAM,eAAe,EAAY;IACtD,mBAAmB,EAAS,SAAS,EAAc;GACrD;GACA,EAAM;EACR,CAAC,GAEK,KAAY,QAAe,CAC/B;GACE,kCAAkC,EAAM;GACxC,oCAAoC,EAAM;EAC5C,GACA,EAAM,KACR,CAAC,GAEK,KAAY,QAAe;GAC/B,IAAM,EAAE,OAAO,GAAQ,OAAO,GAAQ,GAAG,MAAS;GAClD,OAAO;EACT,CAAC,GAEK,IAAY,SAAmC;GACnD,QAAQ,EAAS;GACjB,WAAW,EAAmB;GAC9B,aAAa,EAAc;GAC3B;GACA;GACA;EACF,EAAE;yBAIA,EAuNY,GAtNL,EAAA,EAAE,GADT,GAEU,GAqNE,OArNO;GAChB,OAAO,GAAA;GACP,OAAO,GAAA;GACP,eAAa,EAAA,QAAQ,SAAA;;oBAqEb;IAnET,EAmES,UAAA,EAlEP,OAAK,EAAA,CAAC,uBAAqB,EAAA,aACJ,EAAQA,EAAAA,OAAM,gBAAA,CAAA,CAAA,EAAA,GAAA;KAG7B,EAAA,SAAY,EAAA,kBAAA,EAAA,GADpB,EA6CS,UAAA;;MA3CP,MAAK;MACL,OAAM;MACL,cAAY,EAAA;MACZ,iBAAe,EAAA,CAAA;MACf,iBAAe,EAAA,CAAA;MACf,SAAO;SAER,EAmCO,EAAA,QAAA,aAAA,EAjCJ,aAAc,EAAA,CAAA,EAAa,SAiCvB,CAAA,AAAA,EAAA,OA/BL,EA8BM,OAAA;MA7BJ,OAAM;MACN,OAAM;MACN,QAAO;MACP,SAAQ;MACR,MAAK;MACL,QAAO;MACP,gBAAa;MACb,kBAAe;MACf,mBAAgB;MAChB,eAAY;;MAEZ,EAKE,QAAA;OAJA,IAAG;OACH,IAAG;OACH,IAAG;OACH,IAAG;;MAEL,EAKE,QAAA;OAJA,IAAG;OACH,IAAG;OACH,IAAG;OACH,IAAG;;MAEL,EAKE,QAAA;OAJA,IAAG;OACH,IAAG;OACH,IAAG;OACH,IAAG;;;KAOHA,EAAAA,OAAM,mBAAA,EAAA,GADd,EAQM,OARN,GAQM,CAJJ,EAGE,EAAA,QAAA,gBAAA,EAAA,EADQ,EAAA,KAAS,CAAA,CAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA;KAIrB,EAKM,OALN,IAKM,CAJJ,EAGE,EAAA,QAAA,UAAA,EAAA,EADQ,EAAA,KAAS,CAAA,CAAA,CAAA,CAAA;;IAMd,EAAA,qBAAA,EAAA,GADT,EAqEQ,SAAA;;KAnEL,IAAI,EAAA,CAAA;KACL,OAAM;KACL,cAAY,EAAA;;KAGLA,EAAAA,OAAM,qBAAA,EAAA,GADd,EAQM,OARN,IAQM,CAJJ,EAGE,EAAA,QAAA,kBAAA,EAAA,EADQ,EAAA,KAAS,CAAA,CAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA;KAIrB,EAKM,OALN,IAKM,CAJJ,EAGE,EAAA,QAAA,WAAA,EAAA,EADQ,EAAA,KAAS,CAAA,CAAA,CAAA,CAAA;KAKbA,EAAAA,OAAM,qBAAsB,EAAA,SAAA,EAAA,GADpC,EA6CM,OA7CN,GA6CM,CAzCJ,EAGE,EAAA,QAAA,kBAAA,EAAA,EADQ,EAAA,KAAS,CAAA,CAAA,GAIX,EAAA,SAAA,EAAA,GADR,EAmCS,UAAA;;MAjCP,MAAK;MACL,OAAM;MACL,cAAY,EAAA,CAAA,IAAc,EAAA,cAAc,EAAA;MACxC,iBAAa,CAAG,EAAA,CAAA;MAChB,iBAAe,EAAA,CAAA;MACf,SAAO;SAER,EAyBO,EAAA,QAAA,iBAAA,EAvBJ,WAAW,EAAA,MAAkB,SAuBzB,CAAA,AAAA,EAAA,OArBL,EAoBM,OAAA;MAnBJ,OAAM;MACN,OAAM;MACN,QAAO;MACP,SAAQ;MACR,MAAK;MACL,QAAO;MACP,gBAAa;MACb,kBAAe;MACf,mBAAgB;MAChB,eAAY;SAEZ,EAME,QAAA;MALA,GAAE;MACF,GAAE;MACF,OAAM;MACN,QAAO;MACP,IAAG;SAEL,EAAoB,QAAA,EAAd,GAAE,UAAS,CAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA,GAAA,GAAA,EAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA;;IAO3B,EAKO,QALP,IAKO,CAJL,EAGE,EAAA,QAAA,WAAA;KAFC,QAAQ,EAAA;KACR,WAAW,EAAA;;IAIhB,EA4Da,GAAA,EA5DD,MAAK,mBAAkB,GAAA;uBAEzB,EAAA,SAAY,EAAA,CAAA,KAAA,EAAA,GADpB,EA0DM,OA1DN,IA0DM,CAtDJ,EAIE,OAAA;MAHA,OAAM;MACN,eAAY;MACX,SAAO;SAEV,EAgDQ,SAAA;MA/CL,IAAI,EAAA,CAAA;eACD;MAAJ,KAAI;MACJ,OAAM;MACN,MAAK;MACL,cAAW;MACV,cAAY,EAAA;MACb,UAAS;MACC;SAEV,EASM,OATN,IASM,CARJ,EAOS,UAAA;MANP,MAAK;MACL,OAAM;MACL,cAAY,EAAA;MACZ,SAAO;SAER,EAAuC,EAAA,CAAA,GAAA,EAAA,EAApB,EAAA,EAAA,CAAgB,CAAA,GAAA,MAAA,EAAA,CAAA,GAAA,GAAA,EAAA,CAAA,CAAA,GAGvC,EA2BM,OA3BN,IA2BM;MAzBIA,EAAAA,OAAM,qBAAA,EAAA,GADd,EAQM,OARN,IAQM,CAJJ,EAGE,EAAA,QAAA,kBAAA,EAAA,EADQ,EAAA,KAAS,CAAA,CAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA;MAIrB,EAKM,OALN,IAKM,CAJJ,EAGE,EAAA,QAAA,WAAA,EAAA,EADQ,EAAA,KAAS,CAAA,CAAA,CAAA,CAAA;MAKbA,EAAAA,OAAM,qBAAA,EAAA,GADd,EAQM,OARN,GAQM,CAJJ,EAGE,EAAA,QAAA,kBAAA,EAAA,EADQ,EAAA,KAAS,CAAA,CAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TDatePicker.cjs","names":[],"sources":["../../src/components/TDatePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n addDays,\n addMonths,\n createId,\n isActivationKey,\n isEscapeKey,\n isSameDay,\n parseDateValue,\n startOfDay,\n startOfMonth,\n startOfWeek,\n toDateValue,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n type ComponentPublicInstance,\n computed,\n nextTick,\n onBeforeUnmount,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSize } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\ninterface CalendarCell {\n date: Date;\n dayLabel: number;\n disabled: boolean;\n inMonth: boolean;\n isFocused: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n}\n\nconst CalendarIcon = getTreeIcon('calendar');\nconst ChevronLeftIcon = getTreeIcon('chevron-left');\nconst ChevronRightIcon = getTreeIcon('chevron-right');\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n open?: boolean;\n defaultOpen?: boolean;\n size?: TSize;\n disabled?: boolean;\n invalid?: boolean;\n placeholder?: string;\n locale?: string;\n min?: string;\n max?: string;\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n id?: string;\n }>(),\n {\n modelValue: '',\n open: undefined,\n defaultOpen: false,\n size: 'md',\n disabled: false,\n invalid: false,\n placeholder: 'Select date',\n locale: 'en-US',\n min: '',\n max: '',\n weekStartsOn: 0,\n id: undefined,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n select: [value: string];\n}>();\n\nconst attrs = useAttrs();\nconst today = startOfDay(new Date());\nconst selectedDate = computed(() => parseDateValue(props.modelValue));\nconst minDate = computed(() => parseDateValue(props.min));\nconst maxDate = computed(() => parseDateValue(props.max));\nconst calendarId = props.id ?? createId('t-date-picker');\nconst captionId = `${calendarId}-caption`;\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst dayRefs = new Map<string, HTMLButtonElement>();\nconst viewMonth = ref(startOfMonth(selectedDate.value ?? today));\nconst focusedDate = ref<Date>(selectedDate.value ?? today);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, name: _name, ...rest } = attrs;\n return rest;\n});\n\nconst hiddenInputName = computed(() =>\n typeof attrs.name === 'string' ? attrs.name : undefined,\n);\n\nconst rootClasses = computed(() => [\n 't-date-picker',\n `t-date-picker--${props.size}`,\n {\n 'is-disabled': props.disabled,\n 'is-invalid': props.invalid,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst triggerText = computed(() => {\n if (!selectedDate.value) {\n return props.placeholder;\n }\n\n return new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(selectedDate.value);\n});\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n month: 'long',\n year: 'numeric',\n }).format(viewMonth.value),\n);\n\nconst weekdayLabels = computed(() => {\n const formatter = new Intl.DateTimeFormat(props.locale, { weekday: 'short' });\n const weekBase = startOfWeek(new Date(2024, 0, 7), props.weekStartsOn);\n\n return Array.from({ length: 7 }, (_, index) =>\n formatter.format(addDays(weekBase, index)),\n );\n});\n\nconst isDateDisabled = (date: Date) => {\n if (props.disabled) {\n return true;\n }\n\n if (minDate.value && date < minDate.value) {\n return true;\n }\n\n if (maxDate.value && date > maxDate.value) {\n return true;\n }\n\n return false;\n};\n\nconst syncCalendarView = (date: Date) => {\n focusedDate.value = startOfDay(date);\n viewMonth.value = startOfMonth(date);\n};\n\nwatch(\n selectedDate,\n (value) => {\n if (value) {\n syncCalendarView(value);\n return;\n }\n\n if (!isOpen.value) {\n syncCalendarView(today);\n }\n },\n { immediate: true },\n);\n\nconst cells = computed(() => {\n const startDate = startOfWeek(startOfMonth(viewMonth.value), props.weekStartsOn);\n\n return Array.from({ length: 6 }, (_, weekIndex) =>\n Array.from({ length: 7 }, (_, dayIndex) => {\n const date = addDays(startDate, weekIndex * 7 + dayIndex);\n const key = toDateValue(date);\n\n return {\n date,\n dayLabel: date.getDate(),\n disabled: isDateDisabled(date),\n inMonth: date.getMonth() === viewMonth.value.getMonth(),\n isFocused: isSameDay(date, focusedDate.value),\n isSelected: isSameDay(date, selectedDate.value),\n isToday: isSameDay(date, today),\n key,\n } satisfies CalendarCell;\n }),\n );\n});\n\nconst canMovePrev = computed(() => {\n if (!minDate.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) > startOfMonth(minDate.value);\n});\n\nconst canMoveNext = computed(() => {\n if (!maxDate.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) < startOfMonth(maxDate.value);\n});\n\nconst focusCurrentDay = () => {\n const key = toDateValue(focusedDate.value);\n nextTick(() => {\n dayRefs.get(key)?.focus();\n });\n};\n\nconst setDayRef = (\n element: Element | ComponentPublicInstance | null,\n key: string,\n) => {\n if (element instanceof HTMLButtonElement) {\n dayRefs.set(key, element);\n return;\n }\n\n dayRefs.delete(key);\n};\n\nconst openCalendar = () => {\n if (props.disabled) {\n return;\n }\n\n syncCalendarView(selectedDate.value ?? focusedDate.value ?? today);\n setValue(true);\n focusCurrentDay();\n};\n\nconst closeCalendar = (restoreFocus = false) => {\n setValue(false);\n\n if (restoreFocus) {\n nextTick(() => {\n triggerRef.value?.focus();\n });\n }\n};\n\nconst toggleCalendar = () => {\n if (isOpen.value) {\n closeCalendar();\n return;\n }\n\n openCalendar();\n};\n\nconst moveCalendar = (direction: 1 | -1) => {\n const nextMonth = addMonths(viewMonth.value, direction);\n syncCalendarView(nextMonth);\n focusCurrentDay();\n};\n\nconst moveFocus = (nextDate: Date) => {\n if (isDateDisabled(nextDate)) {\n return;\n }\n\n syncCalendarView(nextDate);\n focusCurrentDay();\n};\n\nconst selectDate = (date: Date) => {\n if (isDateDisabled(date)) {\n return;\n }\n\n const normalizedDate = startOfDay(date);\n const value = toDateValue(normalizedDate);\n\n syncCalendarView(normalizedDate);\n emit('update:modelValue', value);\n emit('select', value);\n closeCalendar(true);\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) {\n return;\n }\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeCalendar();\n return;\n }\n\n if (\n isActivationKey(event) ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowUp'\n ) {\n event.preventDefault();\n openCalendar();\n }\n};\n\nconst onDayKeydown = (event: KeyboardEvent, date: Date) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeCalendar(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectDate(date);\n return;\n }\n\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault();\n moveFocus(addDays(date, -1));\n break;\n case 'ArrowRight':\n event.preventDefault();\n moveFocus(addDays(date, 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(addDays(date, -7));\n break;\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(addDays(date, 7));\n break;\n case 'Home':\n event.preventDefault();\n moveFocus(startOfWeek(date, props.weekStartsOn));\n break;\n case 'End':\n event.preventDefault();\n moveFocus(addDays(startOfWeek(date, props.weekStartsOn), 6));\n break;\n case 'PageUp':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? -12 : -1));\n break;\n case 'PageDown':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? 12 : 1));\n break;\n default:\n break;\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) {\n return;\n }\n\n const target = event.target;\n if (!(target instanceof Node)) {\n return;\n }\n\n if (rootRef.value?.contains(target)) {\n return;\n }\n\n closeCalendar();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n return;\n }\n\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <input\n v-if=\"hiddenInputName\"\n type=\"hidden\"\n :name=\"hiddenInputName\"\n :value=\"modelValue\"\n >\n <button\n ref=\"triggerRef\"\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-date-picker__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? calendarId : undefined\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :aria-invalid=\"invalid || undefined\"\n @click=\"toggleCalendar\"\n @keydown=\"onTriggerKeydown\"\n >\n <span\n v-if=\"$slots.prefix\"\n class=\"t-date-picker__slot t-date-picker__slot--prefix\"\n >\n <slot name=\"prefix\" />\n </span>\n <span\n class=\"t-date-picker__value\"\n :data-placeholder=\"selectedDate ? undefined : true\"\n >\n {{ triggerText }}\n </span>\n <span class=\"t-date-picker__slot t-date-picker__slot--suffix\">\n <slot name=\"suffix\">\n <CalendarIcon v-bind=\"treeIconDefaults\" />\n </slot>\n </span>\n </button>\n\n <transition name=\"t-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"calendarId\"\n class=\"t-date-picker__content\"\n role=\"dialog\"\n aria-modal=\"false\"\n :aria-labelledby=\"captionId\"\n >\n <div class=\"t-date-picker__header\">\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMovePrev\"\n aria-label=\"Previous month\"\n @click=\"moveCalendar(-1)\"\n >\n <ChevronLeftIcon v-bind=\"treeIconDefaults\" />\n </button>\n <p\n :id=\"captionId\"\n class=\"t-date-picker__month\"\n >\n {{ monthLabel }}\n </p>\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMoveNext\"\n aria-label=\"Next month\"\n @click=\"moveCalendar(1)\"\n >\n <ChevronRightIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n\n <div\n class=\"t-date-picker__weekdays\"\n aria-hidden=\"true\"\n >\n <span\n v-for=\"label in weekdayLabels\"\n :key=\"label\"\n class=\"t-date-picker__weekday\"\n >\n {{ label }}\n </span>\n </div>\n\n <div\n class=\"t-date-picker__grid\"\n role=\"grid\"\n :aria-labelledby=\"captionId\"\n >\n <div\n v-for=\"(week, weekIndex) in cells\"\n :key=\"weekIndex\"\n class=\"t-date-picker__row\"\n role=\"row\"\n >\n <div\n v-for=\"cell in week\"\n :key=\"cell.key\"\n class=\"t-date-picker__cell\"\n role=\"gridcell\"\n :aria-selected=\"cell.isSelected\"\n >\n <button\n :ref=\"(element) => setDayRef(element, cell.key)\"\n type=\"button\"\n class=\"t-date-picker__day\"\n :class=\"{\n 'is-muted': !cell.inMonth,\n 'is-selected': cell.isSelected,\n 'is-today': cell.isToday,\n }\"\n :data-date=\"cell.key\"\n :disabled=\"cell.disabled\"\n :tabindex=\"cell.isFocused ? 0 : -1\"\n :aria-current=\"cell.isToday ? 'date' : undefined\"\n :aria-label=\"cell.key\"\n @click=\"selectDate(cell.date)\"\n @keydown=\"onDayKeydown($event, cell.date)\"\n >\n {{ cell.dayLabel }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"TDatePicker.cjs","names":[],"sources":["../../src/components/TDatePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n addDays,\n addMonths,\n createId,\n isActivationKey,\n isEscapeKey,\n isSameDay,\n parseDateValue,\n startOfDay,\n startOfMonth,\n startOfWeek,\n toDateValue,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n type ComponentPublicInstance,\n computed,\n nextTick,\n onBeforeUnmount,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSize } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\ninterface CalendarCell {\n date: Date;\n dayLabel: number;\n disabled: boolean;\n inMonth: boolean;\n isFocused: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n}\n\nconst CalendarIcon = computed(() => getTreeIcon('calendar'));\nconst ChevronLeftIcon = computed(() => getTreeIcon('chevron-left'));\nconst ChevronRightIcon = computed(() => getTreeIcon('chevron-right'));\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n open?: boolean;\n defaultOpen?: boolean;\n size?: TSize;\n disabled?: boolean;\n invalid?: boolean;\n placeholder?: string;\n locale?: string;\n min?: string;\n max?: string;\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n id?: string;\n }>(),\n {\n modelValue: '',\n open: undefined,\n defaultOpen: false,\n size: 'md',\n disabled: false,\n invalid: false,\n placeholder: 'Select date',\n locale: 'en-US',\n min: '',\n max: '',\n weekStartsOn: 0,\n id: undefined,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n select: [value: string];\n}>();\n\nconst attrs = useAttrs();\nconst today = startOfDay(new Date());\nconst selectedDate = computed(() => parseDateValue(props.modelValue));\nconst minDate = computed(() => parseDateValue(props.min));\nconst maxDate = computed(() => parseDateValue(props.max));\nconst calendarId = props.id ?? createId('t-date-picker');\nconst captionId = `${calendarId}-caption`;\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst dayRefs = new Map<string, HTMLButtonElement>();\nconst viewMonth = ref(startOfMonth(selectedDate.value ?? today));\nconst focusedDate = ref<Date>(selectedDate.value ?? today);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, name: _name, ...rest } = attrs;\n return rest;\n});\n\nconst hiddenInputName = computed(() =>\n typeof attrs.name === 'string' ? attrs.name : undefined,\n);\n\nconst rootClasses = computed(() => [\n 't-date-picker',\n `t-date-picker--${props.size}`,\n {\n 'is-disabled': props.disabled,\n 'is-invalid': props.invalid,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst triggerText = computed(() => {\n if (!selectedDate.value) {\n return props.placeholder;\n }\n\n return new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(selectedDate.value);\n});\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n month: 'long',\n year: 'numeric',\n }).format(viewMonth.value),\n);\n\nconst weekdayLabels = computed(() => {\n const formatter = new Intl.DateTimeFormat(props.locale, { weekday: 'short' });\n const weekBase = startOfWeek(new Date(2024, 0, 7), props.weekStartsOn);\n\n return Array.from({ length: 7 }, (_, index) =>\n formatter.format(addDays(weekBase, index)),\n );\n});\n\nconst isDateDisabled = (date: Date) => {\n if (props.disabled) {\n return true;\n }\n\n if (minDate.value && date < minDate.value) {\n return true;\n }\n\n if (maxDate.value && date > maxDate.value) {\n return true;\n }\n\n return false;\n};\n\nconst syncCalendarView = (date: Date) => {\n focusedDate.value = startOfDay(date);\n viewMonth.value = startOfMonth(date);\n};\n\nwatch(\n selectedDate,\n (value) => {\n if (value) {\n syncCalendarView(value);\n return;\n }\n\n if (!isOpen.value) {\n syncCalendarView(today);\n }\n },\n { immediate: true },\n);\n\nconst cells = computed(() => {\n const startDate = startOfWeek(startOfMonth(viewMonth.value), props.weekStartsOn);\n\n return Array.from({ length: 6 }, (_, weekIndex) =>\n Array.from({ length: 7 }, (_, dayIndex) => {\n const date = addDays(startDate, weekIndex * 7 + dayIndex);\n const key = toDateValue(date);\n\n return {\n date,\n dayLabel: date.getDate(),\n disabled: isDateDisabled(date),\n inMonth: date.getMonth() === viewMonth.value.getMonth(),\n isFocused: isSameDay(date, focusedDate.value),\n isSelected: isSameDay(date, selectedDate.value),\n isToday: isSameDay(date, today),\n key,\n } satisfies CalendarCell;\n }),\n );\n});\n\nconst canMovePrev = computed(() => {\n if (!minDate.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) > startOfMonth(minDate.value);\n});\n\nconst canMoveNext = computed(() => {\n if (!maxDate.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) < startOfMonth(maxDate.value);\n});\n\nconst focusCurrentDay = () => {\n const key = toDateValue(focusedDate.value);\n nextTick(() => {\n dayRefs.get(key)?.focus();\n });\n};\n\nconst setDayRef = (\n element: Element | ComponentPublicInstance | null,\n key: string,\n) => {\n if (element instanceof HTMLButtonElement) {\n dayRefs.set(key, element);\n return;\n }\n\n dayRefs.delete(key);\n};\n\nconst openCalendar = () => {\n if (props.disabled) {\n return;\n }\n\n syncCalendarView(selectedDate.value ?? focusedDate.value ?? today);\n setValue(true);\n focusCurrentDay();\n};\n\nconst closeCalendar = (restoreFocus = false) => {\n setValue(false);\n\n if (restoreFocus) {\n nextTick(() => {\n triggerRef.value?.focus();\n });\n }\n};\n\nconst toggleCalendar = () => {\n if (isOpen.value) {\n closeCalendar();\n return;\n }\n\n openCalendar();\n};\n\nconst moveCalendar = (direction: 1 | -1) => {\n const nextMonth = addMonths(viewMonth.value, direction);\n syncCalendarView(nextMonth);\n focusCurrentDay();\n};\n\nconst moveFocus = (nextDate: Date) => {\n if (isDateDisabled(nextDate)) {\n return;\n }\n\n syncCalendarView(nextDate);\n focusCurrentDay();\n};\n\nconst selectDate = (date: Date) => {\n if (isDateDisabled(date)) {\n return;\n }\n\n const normalizedDate = startOfDay(date);\n const value = toDateValue(normalizedDate);\n\n syncCalendarView(normalizedDate);\n emit('update:modelValue', value);\n emit('select', value);\n closeCalendar(true);\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) {\n return;\n }\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeCalendar();\n return;\n }\n\n if (\n isActivationKey(event) ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowUp'\n ) {\n event.preventDefault();\n openCalendar();\n }\n};\n\nconst onDayKeydown = (event: KeyboardEvent, date: Date) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeCalendar(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectDate(date);\n return;\n }\n\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault();\n moveFocus(addDays(date, -1));\n break;\n case 'ArrowRight':\n event.preventDefault();\n moveFocus(addDays(date, 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(addDays(date, -7));\n break;\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(addDays(date, 7));\n break;\n case 'Home':\n event.preventDefault();\n moveFocus(startOfWeek(date, props.weekStartsOn));\n break;\n case 'End':\n event.preventDefault();\n moveFocus(addDays(startOfWeek(date, props.weekStartsOn), 6));\n break;\n case 'PageUp':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? -12 : -1));\n break;\n case 'PageDown':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? 12 : 1));\n break;\n default:\n break;\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) {\n return;\n }\n\n const target = event.target;\n if (!(target instanceof Node)) {\n return;\n }\n\n if (rootRef.value?.contains(target)) {\n return;\n }\n\n closeCalendar();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n return;\n }\n\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <input\n v-if=\"hiddenInputName\"\n type=\"hidden\"\n :name=\"hiddenInputName\"\n :value=\"modelValue\"\n >\n <button\n ref=\"triggerRef\"\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-date-picker__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? calendarId : undefined\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :aria-invalid=\"invalid || undefined\"\n @click=\"toggleCalendar\"\n @keydown=\"onTriggerKeydown\"\n >\n <span\n v-if=\"$slots.prefix\"\n class=\"t-date-picker__slot t-date-picker__slot--prefix\"\n >\n <slot name=\"prefix\" />\n </span>\n <span\n class=\"t-date-picker__value\"\n :data-placeholder=\"selectedDate ? undefined : true\"\n >\n {{ triggerText }}\n </span>\n <span class=\"t-date-picker__slot t-date-picker__slot--suffix\">\n <slot name=\"suffix\">\n <CalendarIcon v-bind=\"treeIconDefaults\" />\n </slot>\n </span>\n </button>\n\n <transition name=\"t-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"calendarId\"\n class=\"t-date-picker__content\"\n role=\"dialog\"\n aria-modal=\"false\"\n :aria-labelledby=\"captionId\"\n >\n <div class=\"t-date-picker__header\">\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMovePrev\"\n aria-label=\"Previous month\"\n @click=\"moveCalendar(-1)\"\n >\n <ChevronLeftIcon v-bind=\"treeIconDefaults\" />\n </button>\n <p\n :id=\"captionId\"\n class=\"t-date-picker__month\"\n >\n {{ monthLabel }}\n </p>\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMoveNext\"\n aria-label=\"Next month\"\n @click=\"moveCalendar(1)\"\n >\n <ChevronRightIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n\n <div\n class=\"t-date-picker__weekdays\"\n aria-hidden=\"true\"\n >\n <span\n v-for=\"label in weekdayLabels\"\n :key=\"label\"\n class=\"t-date-picker__weekday\"\n >\n {{ label }}\n </span>\n </div>\n\n <div\n class=\"t-date-picker__grid\"\n role=\"grid\"\n :aria-labelledby=\"captionId\"\n >\n <div\n v-for=\"(week, weekIndex) in cells\"\n :key=\"weekIndex\"\n class=\"t-date-picker__row\"\n role=\"row\"\n >\n <div\n v-for=\"cell in week\"\n :key=\"cell.key\"\n class=\"t-date-picker__cell\"\n role=\"gridcell\"\n :aria-selected=\"cell.isSelected\"\n >\n <button\n :ref=\"(element) => setDayRef(element, cell.key)\"\n type=\"button\"\n class=\"t-date-picker__day\"\n :class=\"{\n 'is-muted': !cell.inMonth,\n 'is-selected': cell.isSelected,\n 'is-today': cell.isToday,\n }\"\n :data-date=\"cell.key\"\n :disabled=\"cell.disabled\"\n :tabindex=\"cell.isFocused ? 0 : -1\"\n :aria-current=\"cell.isToday ? 'date' : undefined\"\n :aria-label=\"cell.key\"\n @click=\"selectDate(cell.date)\"\n @keydown=\"onDayKeydown($event, cell.date)\"\n >\n {{ cell.dayLabel }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":""}
|