@juxtui/ui 0.7.0 → 0.8.1
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/LICENSE +21 -21
- package/README.md +216 -64
- package/dist/JTooltip-CAL9lsPF.js.map +1 -1
- package/dist/editor.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/nuxt.js.map +1 -1
- package/dist/style.css +3187 -3187
- package/dist/tailwind.css +74 -74
- package/dist/tokens.css +237 -237
- package/package.json +58 -28
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["_useModel","_openBlock","_createElementBlock","_renderSlot","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_openBlock","_createElementBlock","_normalizeClass","_createBlock","_resolveDynamicComponent","_withCtx","_createElementVNode","_unref","_renderSlot","_createTextVNode","_toDisplayString","_createVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createBlock","_resolveDynamicComponent","_createCommentVNode","_createElementVNode","_toDisplayString","_createTextVNode","_createVNode","_unref","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_normalizeStyle","_createElementVNode","_renderSlot","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_createCommentVNode","_renderSlot","_Fragment","_createTextVNode","_toDisplayString","_sfc_main","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList","_createVNode","JAvatar","_toDisplayString","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createBlock","_resolveDynamicComponent","_createCommentVNode","_createElementVNode","_toDisplayString","_createTextVNode","_createVNode","_unref","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_createElementVNode","_Fragment","_renderList","_toDisplayString","_renderSlot","_sfc_main","_hoisted_1","_useModel","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createBlock","_resolveDynamicComponent","_mergeProps","_withCtx","_createElementVNode","_createElementBlock","_renderSlot","_createCommentVNode","_createVNode","_Transition","_unref","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_openBlock","_createBlock","_resolveDynamicComponent","_normalizeClass","_withCtx","_createElementBlock","_renderSlot","_createElementVNode","_toDisplayString","_createCommentVNode","_sfc_main","$attrs","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_useModel","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createElementVNode","_mergeProps","_createStaticVNode","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_useModel","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_unref","_renderSlot","_createTextVNode","_toDisplayString","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_useModel","_openBlock","_createBlock","JPortal","_withCtx","_createVNode","_Transition","_createElementBlock","_createCommentVNode","_createElementVNode","_unref","_withDirectives","_vModelText","_withModifiers","_Fragment","_renderList","_toDisplayString","_normalizeClass","_renderSlot","_resolveDynamicComponent","_createTextVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_useModel","_openBlock","_createBlock","JPortal","_withCtx","_createVNode","_Transition","_createElementBlock","_createCommentVNode","_unref","_createElementVNode","_normalizeClass","_renderSlot","_toDisplayString","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_renderSlot","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_renderSlot","_createCommentVNode","_toDisplayString","_createVNode","_unref","JMenuContent","_withCtx","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createCommentVNode","_toDisplayString","_createTextVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createElementVNode","_renderSlot","_createCommentVNode","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_renderSlot","_sfc_main","$attrs","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_useModel","_openBlock","_createBlock","JFieldShell","_unref","_normalizeClass","_normalizeStyle","_createSlots","_withCtx","_createElementVNode","_createElementBlock","_renderSlot","_createCommentVNode","_withDirectives","_mergeProps","_vModelDynamic","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_useModel","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createElementVNode","_renderSlot","_createCommentVNode","_createBlock","JBurger","_createVNode","_Transition","_withCtx","_withDirectives","_vShow","_sfc_main","$slots","_hoisted_1","_openBlock","_createBlock","_resolveDynamicComponent","_normalizeClass","_withCtx","_createElementBlock","_renderSlot","_createCommentVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_useModel","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_createVNode","_unref","_Fragment","_renderList","_toDisplayString","_sfc_main","_hoisted_1","_useModel","_openBlock","_createElementBlock","_Fragment","_createVNode","_unref","_mergeProps","_withCtx","_renderSlot","JPortal","_Transition","_normalizeStyle","_createCommentVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createElementBlock","_normalizeClass","_toDisplayString","_createCommentVNode","_createElementVNode","_normalizeStyle","_sfc_main","$attrs","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createElementVNode","_mergeProps","_unref","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_useModel","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_createElementVNode","_sfc_main","_hoisted_1","_hoisted_2","_useModel","_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList","_createElementVNode","_normalizeStyle","_sfc_main","$attrs","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_useModel","_openBlock","_createBlock","JFieldShell","_unref","_normalizeClass","_normalizeStyle","_createSlots","_withCtx","_createElementVNode","_mergeProps","_renderSlot","_createTextVNode","_toDisplayString","_createElementBlock","_Fragment","_createVNode","_createCommentVNode","JPortal","_Transition","_withModifiers","_renderList","_sfc_main","$slots","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_useModel","_openBlock","_createBlock","JPortal","_withCtx","_createVNode","_Transition","_createElementBlock","_createCommentVNode","_normalizeClass","_unref","_createElementVNode","_renderSlot","_toDisplayString","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createElementBlock","_renderSlot","_createCommentVNode","_createElementVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_useModel","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createVNode","_unref","_createCommentVNode","_renderSlot","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createBlock","_resolveDynamicComponent","_normalizeClass","_withCtx","_createElementBlock","_renderSlot","_createCommentVNode","_createElementVNode","_sfc_main","_openBlock","_createElementBlock","_normalizeStyle","_Fragment","_renderList","_normalizeClass","_sfc_main","_hoisted_1","_hoisted_2","_useModel","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_normalizeStyle","_Fragment","_renderList","_toDisplayString","_createCommentVNode","_sfc_main","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_createVNode","_unref","_toDisplayString","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_openBlock","_createElementBlock","_createElementVNode","_renderSlot","_createCommentVNode","_toDisplayString","_normalizeClass","_createBlock","_resolveDynamicComponent","_createTextVNode","_sfc_main","$attrs","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_useModel","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createElementVNode","_mergeProps","_createCommentVNode","_renderSlot","_createTextVNode","_toDisplayString","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_useModel","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_normalizeStyle","_Fragment","_renderList","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createCommentVNode","_createElementVNode","_createVNode","_unref","_sfc_main","$attrs","$slots","_hoisted_1","_useModel","_openBlock","_createBlock","JFieldShell","_unref","_normalizeClass","_normalizeStyle","_createSlots","_withCtx","_createElementVNode","_withDirectives","_mergeProps","_vModelText","_renderSlot","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_renderSlot","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createVNode","_Transition","_withCtx","_createBlock","_unref","_resolveDynamicComponent","_createCommentVNode","_createElementVNode","_toDisplayString","_sfc_main","_hoisted_1","_openBlock","_createBlock","JPortal","_withCtx","_createElementVNode","_normalizeClass","_createVNode","_TransitionGroup","_createElementBlock","_Fragment","_renderList","JToastItem","_sfc_main","_hoisted_1","_useModel","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_sfc_main","_openBlock","_createElementBlock","_normalizeClass","_unref","_renderSlot","_sfc_main","_openBlock","_createBlock","_resolveDynamicComponent","_withCtx","_renderSlot","_sfc_main","components"],"sources":["../src/components/accordion/context.ts","../src/components/accordion/JAccordion.vue","../src/components/accordion/JAccordion.vue","../src/components/accordion/JAccordionItem.vue","../src/components/accordion/JAccordionItem.vue","../src/components/alert/JAlert.vue","../src/components/alert/JAlert.vue","../src/components/aspect-ratio/JAspectRatio.vue","../src/components/aspect-ratio/JAspectRatio.vue","../src/components/avatar/JAvatar.vue","../src/components/avatar/JAvatar.vue","../src/components/avatar-group/JAvatarGroup.vue","../src/components/avatar-group/JAvatarGroup.vue","../src/components/badge/JBadge.vue","../src/components/badge/JBadge.vue","../src/components/banner/JBanner.vue","../src/components/banner/JBanner.vue","../src/components/breadcrumb/JBreadcrumb.vue","../src/components/breadcrumb/JBreadcrumb.vue","../src/components/burger/JBurger.vue","../src/components/burger/JBurger.vue","../src/components/button/JButton.vue","../src/components/button/JButton.vue","../src/components/card/JCard.vue","../src/components/card/JCard.vue","../src/components/checkbox/JCheckbox.vue","../src/components/checkbox/JCheckbox.vue","../src/components/collapsible/JCollapsible.vue","../src/components/collapsible/JCollapsible.vue","../src/composables/useFocusTrap.ts","../src/composables/useScrollLock.ts","../src/components/command-palette/JCommandPalette.vue","../src/components/command-palette/JCommandPalette.vue","../src/composables/useModal.ts","../src/components/dialog/JDialog.vue","../src/components/dialog/JDialog.vue","../src/components/dropdown-menu/JDropdownMenuLabel.vue","../src/components/dropdown-menu/JDropdownMenuLabel.vue","../src/components/dropdown-menu/JDropdownMenuSeparator.vue","../src/components/dropdown-menu/JDropdownMenuSub.vue","../src/components/dropdown-menu/JDropdownMenuSub.vue","../src/components/empty-state/JEmptyState.vue","../src/components/empty-state/JEmptyState.vue","../src/components/footer/JFooter.vue","../src/components/footer/JFooter.vue","../src/components/footer/JFooterColumn.vue","../src/components/footer/JFooterColumn.vue","../src/components/input/JInput.vue","../src/components/input/JInput.vue","../src/components/navbar/JNavbar.vue","../src/components/navbar/JNavbar.vue","../src/components/navbar/JNavbarLink.vue","../src/components/navbar/JNavbarLink.vue","../src/components/pagination/JPagination.vue","../src/components/pagination/JPagination.vue","../src/components/popover/JPopover.vue","../src/components/popover/JPopover.vue","../src/components/progress/JProgress.vue","../src/components/progress/JProgress.vue","../src/components/radio/context.ts","../src/components/radio/JRadio.vue","../src/components/radio/JRadio.vue","../src/components/radio/JRadioGroup.vue","../src/components/radio/JRadioGroup.vue","../src/components/rating/JRating.vue","../src/components/rating/JRating.vue","../src/components/select/JSelect.vue","../src/components/select/JSelect.vue","../src/components/separator/JSeparator.vue","../src/components/separator/JSeparator.vue","../src/components/sheet/JSheet.vue","../src/components/sheet/JSheet.vue","../src/components/sidebar/JSidebar.vue","../src/components/sidebar/JSidebar.vue","../src/components/sidebar/JSidebarGroup.vue","../src/components/sidebar/JSidebarGroup.vue","../src/components/sidebar/JSidebarItem.vue","../src/components/sidebar/JSidebarItem.vue","../src/components/skeleton/JSkeleton.vue","../src/components/skeleton/JSkeleton.vue","../src/components/slider/JSlider.vue","../src/components/slider/JSlider.vue","../src/components/spinner/JSpinner.vue","../src/components/spinner/JSpinner.vue","../src/components/stat/JStat.vue","../src/components/stat/JStat.vue","../src/components/switch/JSwitch.vue","../src/components/switch/JSwitch.vue","../src/components/tabs/JTabs.vue","../src/components/tabs/JTabs.vue","../src/components/tag/JTag.vue","../src/components/tag/JTag.vue","../src/components/textarea/JTextarea.vue","../src/components/textarea/JTextarea.vue","../src/components/timeline/JTimeline.vue","../src/components/timeline/JTimeline.vue","../src/components/timeline/JTimelineItem.vue","../src/components/timeline/JTimelineItem.vue","../src/composables/useToast.ts","../src/components/toast/JToastItem.vue","../src/components/toast/JToastItem.vue","../src/components/toast/JToaster.vue","../src/components/toast/JToaster.vue","../src/components/toggle-group/context.ts","../src/components/toggle-group/JToggleGroup.vue","../src/components/toggle-group/JToggleGroup.vue","../src/components/toggle-group/JToggleGroupItem.vue","../src/components/toggle-group/JToggleGroupItem.vue","../src/components/visually-hidden/JVisuallyHidden.vue","../src/components/visually-hidden/JVisuallyHidden.vue","../src/components.ts","../src/composables/useRecentCommands.ts","../src/plugin.ts"],"sourcesContent":["import type { InjectionKey } from 'vue'\n\nexport interface AccordionContext {\n isOpen: (value: string) => boolean\n toggle: (value: string) => void\n headingLevel: () => number\n}\n\nexport const ACCORDION: InjectionKey<AccordionContext> = Symbol('JAccordion')\n","<script setup lang=\"ts\">\r\nimport { provide } from 'vue'\r\nimport { focusElement } from '../../utils/dom'\r\nimport { ACCORDION } from './context'\r\n\r\nexport interface AccordionProps {\r\n /** `single` keeps at most one item open; `multiple` lets any number stay open. */\r\n type?: 'single' | 'multiple'\r\n /** In `single` mode, allow closing the open item so none are open. */\r\n collapsible?: boolean\r\n /** Heading level wrapping each trigger, for a correct document outline. */\r\n headingLevel?: 2 | 3 | 4 | 5 | 6\r\n}\r\n\r\nconst props = withDefaults(defineProps<AccordionProps>(), {\r\n type: 'single',\r\n collapsible: true,\r\n headingLevel: 3,\r\n})\r\n\r\n/** Open item(s): a value in `single` mode, an array in `multiple` mode. */\r\nconst model = defineModel<string | string[] | null>({ default: null })\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n\r\nfunction openValues(): string[] {\r\n const value = model.value\r\n if (Array.isArray(value)) return value\r\n return value ? [value] : []\r\n}\r\n\r\nprovide(ACCORDION, {\r\n isOpen: value => openValues().includes(value),\r\n toggle: (value) => {\r\n const current = openValues()\r\n const isOpen = current.includes(value)\r\n if (props.type === 'multiple') {\r\n model.value = isOpen ? current.filter(v => v !== value) : [...current, value]\r\n return\r\n }\r\n if (isOpen) {\r\n if (props.collapsible) model.value = null\r\n return\r\n }\r\n model.value = value\r\n },\r\n headingLevel: () => props.headingLevel,\r\n})\r\n\r\n// Arrow keys move between headers, per the WAI-ARIA accordion pattern.\r\nfunction onKeydown(event: KeyboardEvent) {\r\n const target = event.target as HTMLElement\r\n if (!target.classList.contains('j-accordion__trigger')) return\r\n const root = event.currentTarget as HTMLElement\r\n const triggers = Array.from(root.querySelectorAll<HTMLElement>('.j-accordion__trigger:not(:disabled)'))\r\n .filter(el => el.closest('.j-accordion') === root)\r\n const index = triggers.indexOf(target)\r\n let next: HTMLElement | undefined\r\n if (event.key === 'ArrowDown') next = triggers[(index + 1) % triggers.length]\r\n else if (event.key === 'ArrowUp') next = triggers[(index - 1 + triggers.length) % triggers.length]\r\n else if (event.key === 'Home') next = triggers[0]\r\n else if (event.key === 'End') next = triggers[triggers.length - 1]\r\n if (!next) return\r\n event.preventDefault()\r\n focusElement(next)\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-accordion\" @keydown=\"onKeydown\">\r\n <slot />\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-accordion {\r\n display: flex;\r\n flex-direction: column;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { provide } from 'vue'\r\nimport { focusElement } from '../../utils/dom'\r\nimport { ACCORDION } from './context'\r\n\r\nexport interface AccordionProps {\r\n /** `single` keeps at most one item open; `multiple` lets any number stay open. */\r\n type?: 'single' | 'multiple'\r\n /** In `single` mode, allow closing the open item so none are open. */\r\n collapsible?: boolean\r\n /** Heading level wrapping each trigger, for a correct document outline. */\r\n headingLevel?: 2 | 3 | 4 | 5 | 6\r\n}\r\n\r\nconst props = withDefaults(defineProps<AccordionProps>(), {\r\n type: 'single',\r\n collapsible: true,\r\n headingLevel: 3,\r\n})\r\n\r\n/** Open item(s): a value in `single` mode, an array in `multiple` mode. */\r\nconst model = defineModel<string | string[] | null>({ default: null })\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n\r\nfunction openValues(): string[] {\r\n const value = model.value\r\n if (Array.isArray(value)) return value\r\n return value ? [value] : []\r\n}\r\n\r\nprovide(ACCORDION, {\r\n isOpen: value => openValues().includes(value),\r\n toggle: (value) => {\r\n const current = openValues()\r\n const isOpen = current.includes(value)\r\n if (props.type === 'multiple') {\r\n model.value = isOpen ? current.filter(v => v !== value) : [...current, value]\r\n return\r\n }\r\n if (isOpen) {\r\n if (props.collapsible) model.value = null\r\n return\r\n }\r\n model.value = value\r\n },\r\n headingLevel: () => props.headingLevel,\r\n})\r\n\r\n// Arrow keys move between headers, per the WAI-ARIA accordion pattern.\r\nfunction onKeydown(event: KeyboardEvent) {\r\n const target = event.target as HTMLElement\r\n if (!target.classList.contains('j-accordion__trigger')) return\r\n const root = event.currentTarget as HTMLElement\r\n const triggers = Array.from(root.querySelectorAll<HTMLElement>('.j-accordion__trigger:not(:disabled)'))\r\n .filter(el => el.closest('.j-accordion') === root)\r\n const index = triggers.indexOf(target)\r\n let next: HTMLElement | undefined\r\n if (event.key === 'ArrowDown') next = triggers[(index + 1) % triggers.length]\r\n else if (event.key === 'ArrowUp') next = triggers[(index - 1 + triggers.length) % triggers.length]\r\n else if (event.key === 'Home') next = triggers[0]\r\n else if (event.key === 'End') next = triggers[triggers.length - 1]\r\n if (!next) return\r\n event.preventDefault()\r\n focusElement(next)\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-accordion\" @keydown=\"onKeydown\">\r\n <slot />\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-accordion {\r\n display: flex;\r\n flex-direction: column;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, inject, useId } from 'vue'\r\nimport { IconChevronDown } from '../../icons'\r\nimport { ACCORDION } from './context'\r\n\r\nexport interface AccordionItemProps {\r\n /** Identifies the item in the accordion's `v-model`. */\r\n value: string\r\n title?: string\r\n disabled?: boolean\r\n}\r\n\r\nconst props = defineProps<AccordionItemProps>()\r\n\r\ndefineSlots<{ default?: () => unknown, title?: () => unknown }>()\r\n\r\nconst accordion = inject(ACCORDION)!\r\nconst id = `j-${useId()}`\r\nconst triggerId = `${id}-trigger`\r\nconst panelId = `${id}-panel`\r\nconst open = computed(() => accordion.isOpen(props.value))\r\nconst heading = computed(() => `h${accordion.headingLevel()}`)\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-accordion__item\" :data-state=\"open ? 'open' : 'closed'\" :class=\"{ 'is-disabled': disabled }\">\r\n <component :is=\"heading\" class=\"j-accordion__heading\">\r\n <button\r\n :id=\"triggerId\"\r\n type=\"button\"\r\n class=\"j-accordion__trigger\"\r\n :aria-expanded=\"open\"\r\n :aria-controls=\"panelId\"\r\n :disabled=\"disabled\"\r\n @click=\"accordion.toggle(value)\"\r\n >\r\n <span class=\"j-accordion__title\"><slot name=\"title\">{{ title }}</slot></span>\r\n <IconChevronDown class=\"j-accordion__chevron\" />\r\n </button>\r\n </component>\r\n <div\r\n :id=\"panelId\"\r\n role=\"region\"\r\n class=\"j-accordion__panel\"\r\n :aria-labelledby=\"triggerId\"\r\n :inert=\"!open\"\r\n >\r\n <div class=\"j-accordion__panel-inner\">\r\n <div class=\"j-accordion__content\">\r\n <slot />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-accordion__item {\r\n border-bottom: 1px solid var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-accordion__item,\r\n[data-theme='dark'] .j-accordion__item {\r\n border-bottom-color: var(--juxt-border);\r\n}\r\n\r\n.j-accordion__heading {\r\n margin: 0;\r\n font: inherit;\r\n}\r\n\r\n.j-accordion__trigger {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--juxt-space-4);\r\n width: 100%;\r\n min-height: 3rem;\r\n padding: var(--juxt-space-3) 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-xs);\r\n background: transparent;\r\n color: var(--juxt-fg);\r\n font: inherit;\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n text-align: left;\r\n cursor: pointer;\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n -webkit-tap-highlight-color: transparent;\r\n transition:\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-accordion__trigger:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.j-accordion__trigger:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-accordion__chevron {\r\n flex: none;\r\n width: 1rem;\r\n height: 1rem;\r\n color: var(--juxt-fg-muted);\r\n transition:\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-accordion__trigger:hover:not(:disabled) .j-accordion__chevron {\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-accordion__item[data-state='open'] .j-accordion__chevron {\r\n transform: rotate(180deg);\r\n}\r\n\r\n/* Height follows content via grid rows | no measuring, no magic numbers. */\r\n.j-accordion__panel {\r\n display: grid;\r\n grid-template-rows: 0fr;\r\n transition: grid-template-rows var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n.j-accordion__item[data-state='open'] .j-accordion__panel {\r\n grid-template-rows: 1fr;\r\n}\r\n\r\n.j-accordion__panel-inner {\r\n min-height: 0;\r\n overflow: hidden;\r\n}\r\n\r\n.j-accordion__content {\r\n padding-bottom: var(--juxt-space-4);\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n opacity: 0;\r\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-accordion__item[data-state='open'] .j-accordion__content {\r\n opacity: 1;\r\n transition-delay: 60ms;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-accordion__panel,\r\n .j-accordion__chevron {\r\n transition: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, inject, useId } from 'vue'\r\nimport { IconChevronDown } from '../../icons'\r\nimport { ACCORDION } from './context'\r\n\r\nexport interface AccordionItemProps {\r\n /** Identifies the item in the accordion's `v-model`. */\r\n value: string\r\n title?: string\r\n disabled?: boolean\r\n}\r\n\r\nconst props = defineProps<AccordionItemProps>()\r\n\r\ndefineSlots<{ default?: () => unknown, title?: () => unknown }>()\r\n\r\nconst accordion = inject(ACCORDION)!\r\nconst id = `j-${useId()}`\r\nconst triggerId = `${id}-trigger`\r\nconst panelId = `${id}-panel`\r\nconst open = computed(() => accordion.isOpen(props.value))\r\nconst heading = computed(() => `h${accordion.headingLevel()}`)\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-accordion__item\" :data-state=\"open ? 'open' : 'closed'\" :class=\"{ 'is-disabled': disabled }\">\r\n <component :is=\"heading\" class=\"j-accordion__heading\">\r\n <button\r\n :id=\"triggerId\"\r\n type=\"button\"\r\n class=\"j-accordion__trigger\"\r\n :aria-expanded=\"open\"\r\n :aria-controls=\"panelId\"\r\n :disabled=\"disabled\"\r\n @click=\"accordion.toggle(value)\"\r\n >\r\n <span class=\"j-accordion__title\"><slot name=\"title\">{{ title }}</slot></span>\r\n <IconChevronDown class=\"j-accordion__chevron\" />\r\n </button>\r\n </component>\r\n <div\r\n :id=\"panelId\"\r\n role=\"region\"\r\n class=\"j-accordion__panel\"\r\n :aria-labelledby=\"triggerId\"\r\n :inert=\"!open\"\r\n >\r\n <div class=\"j-accordion__panel-inner\">\r\n <div class=\"j-accordion__content\">\r\n <slot />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-accordion__item {\r\n border-bottom: 1px solid var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-accordion__item,\r\n[data-theme='dark'] .j-accordion__item {\r\n border-bottom-color: var(--juxt-border);\r\n}\r\n\r\n.j-accordion__heading {\r\n margin: 0;\r\n font: inherit;\r\n}\r\n\r\n.j-accordion__trigger {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--juxt-space-4);\r\n width: 100%;\r\n min-height: 3rem;\r\n padding: var(--juxt-space-3) 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-xs);\r\n background: transparent;\r\n color: var(--juxt-fg);\r\n font: inherit;\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n text-align: left;\r\n cursor: pointer;\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n -webkit-tap-highlight-color: transparent;\r\n transition:\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-accordion__trigger:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.j-accordion__trigger:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-accordion__chevron {\r\n flex: none;\r\n width: 1rem;\r\n height: 1rem;\r\n color: var(--juxt-fg-muted);\r\n transition:\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-accordion__trigger:hover:not(:disabled) .j-accordion__chevron {\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-accordion__item[data-state='open'] .j-accordion__chevron {\r\n transform: rotate(180deg);\r\n}\r\n\r\n/* Height follows content via grid rows | no measuring, no magic numbers. */\r\n.j-accordion__panel {\r\n display: grid;\r\n grid-template-rows: 0fr;\r\n transition: grid-template-rows var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n.j-accordion__item[data-state='open'] .j-accordion__panel {\r\n grid-template-rows: 1fr;\r\n}\r\n\r\n.j-accordion__panel-inner {\r\n min-height: 0;\r\n overflow: hidden;\r\n}\r\n\r\n.j-accordion__content {\r\n padding-bottom: var(--juxt-space-4);\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n opacity: 0;\r\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-accordion__item[data-state='open'] .j-accordion__content {\r\n opacity: 1;\r\n transition-delay: 60ms;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-accordion__panel,\r\n .j-accordion__chevron {\r\n transition: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX } from '../../icons'\r\n\r\nexport type AlertVariant = 'neutral' | 'info' | 'success' | 'warning' | 'destructive'\r\n\r\nexport interface AlertProps {\r\n variant?: AlertVariant\r\n title?: string\r\n description?: string\r\n /** Show a close button; emits `dismiss`. */\r\n dismissible?: boolean\r\n /** Hide the leading icon. */\r\n hideIcon?: boolean\r\n}\r\n\r\nwithDefaults(defineProps<AlertProps>(), { variant: 'neutral' })\r\n\r\nconst emit = defineEmits<{ dismiss: [] }>()\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n icon?: () => unknown\r\n actions?: () => unknown\r\n}>()\r\n\r\nconst icons = {\r\n neutral: IconInfo,\r\n info: IconInfo,\r\n success: IconCircleCheck,\r\n warning: IconAlert,\r\n destructive: IconCircleX,\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-alert\" :class=\"`j-alert--${variant}`\">\r\n <span v-if=\"!hideIcon\" class=\"j-alert__icon\" aria-hidden=\"true\">\r\n <slot name=\"icon\"><component :is=\"icons[variant]\" /></slot>\r\n </span>\r\n <div class=\"j-alert__body\">\r\n <p v-if=\"title\" class=\"j-alert__title\">\r\n {{ title }}\r\n </p>\r\n <div v-if=\"description || $slots.default\" class=\"j-alert__description\">\r\n <slot>{{ description }}</slot>\r\n </div>\r\n <div v-if=\"$slots.actions\" class=\"j-alert__actions\">\r\n <slot name=\"actions\" />\r\n </div>\r\n </div>\r\n <button v-if=\"dismissible\" type=\"button\" class=\"j-alert__close j-focusable\" aria-label=\"Dismiss\" @click=\"emit('dismiss')\">\r\n <IconX />\r\n </button>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n/*\r\n * Alerts stay on the page's surface. Colour lives in the icon and a hairline\r\n * of the border | enough to be noticed, never enough to shout.\r\n */\r\n.j-alert {\r\n --j-alert-accent: var(--juxt-fg-muted);\r\n --j-alert-border: var(--juxt-border);\r\n\r\n position: relative;\r\n display: flex;\r\n align-items: flex-start;\r\n gap: var(--juxt-space-3);\r\n padding: var(--juxt-space-3) var(--juxt-space-4);\r\n border: 1px solid var(--j-alert-border);\r\n border-radius: var(--juxt-radius-md);\r\n background: var(--juxt-surface);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-alert--info {\r\n --j-alert-accent: var(--juxt-info-text);\r\n}\r\n\r\n.j-alert--success {\r\n --j-alert-accent: var(--juxt-success-text);\r\n}\r\n\r\n.j-alert--warning {\r\n --j-alert-accent: var(--juxt-warning-text);\r\n --j-alert-border: var(--juxt-warning-border);\r\n}\r\n\r\n.j-alert--destructive {\r\n --j-alert-accent: var(--juxt-danger-text);\r\n --j-alert-border: var(--juxt-danger-border);\r\n}\r\n\r\n.j-alert__icon {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n height: 1.25rem;\r\n color: var(--j-alert-accent);\r\n}\r\n\r\n.j-alert__icon > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-alert__body {\r\n display: flex;\r\n flex: 1;\r\n flex-direction: column;\r\n gap: var(--juxt-space-0-5);\r\n min-width: 0;\r\n}\r\n\r\n.j-alert__title {\r\n margin: 0;\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: 1.25rem;\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-alert__description {\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-alert__description > p {\r\n margin: 0;\r\n}\r\n\r\n.j-alert__actions {\r\n display: flex;\r\n flex-wrap: wrap;\r\n gap: var(--juxt-space-2);\r\n margin-top: var(--juxt-space-2);\r\n}\r\n\r\n.j-alert:has(.j-alert__close) {\r\n padding-right: var(--juxt-space-10);\r\n}\r\n\r\n.j-alert__close {\r\n position: absolute;\r\n top: var(--juxt-space-2-5);\r\n right: var(--juxt-space-2-5);\r\n display: grid;\r\n place-items: center;\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-xs);\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n cursor: pointer;\r\n transition-property: background-color, color, outline-color, outline-offset;\r\n}\r\n\r\n.j-alert__close:hover {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-alert__close > svg {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX } from '../../icons'\r\n\r\nexport type AlertVariant = 'neutral' | 'info' | 'success' | 'warning' | 'destructive'\r\n\r\nexport interface AlertProps {\r\n variant?: AlertVariant\r\n title?: string\r\n description?: string\r\n /** Show a close button; emits `dismiss`. */\r\n dismissible?: boolean\r\n /** Hide the leading icon. */\r\n hideIcon?: boolean\r\n}\r\n\r\nwithDefaults(defineProps<AlertProps>(), { variant: 'neutral' })\r\n\r\nconst emit = defineEmits<{ dismiss: [] }>()\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n icon?: () => unknown\r\n actions?: () => unknown\r\n}>()\r\n\r\nconst icons = {\r\n neutral: IconInfo,\r\n info: IconInfo,\r\n success: IconCircleCheck,\r\n warning: IconAlert,\r\n destructive: IconCircleX,\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-alert\" :class=\"`j-alert--${variant}`\">\r\n <span v-if=\"!hideIcon\" class=\"j-alert__icon\" aria-hidden=\"true\">\r\n <slot name=\"icon\"><component :is=\"icons[variant]\" /></slot>\r\n </span>\r\n <div class=\"j-alert__body\">\r\n <p v-if=\"title\" class=\"j-alert__title\">\r\n {{ title }}\r\n </p>\r\n <div v-if=\"description || $slots.default\" class=\"j-alert__description\">\r\n <slot>{{ description }}</slot>\r\n </div>\r\n <div v-if=\"$slots.actions\" class=\"j-alert__actions\">\r\n <slot name=\"actions\" />\r\n </div>\r\n </div>\r\n <button v-if=\"dismissible\" type=\"button\" class=\"j-alert__close j-focusable\" aria-label=\"Dismiss\" @click=\"emit('dismiss')\">\r\n <IconX />\r\n </button>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n/*\r\n * Alerts stay on the page's surface. Colour lives in the icon and a hairline\r\n * of the border | enough to be noticed, never enough to shout.\r\n */\r\n.j-alert {\r\n --j-alert-accent: var(--juxt-fg-muted);\r\n --j-alert-border: var(--juxt-border);\r\n\r\n position: relative;\r\n display: flex;\r\n align-items: flex-start;\r\n gap: var(--juxt-space-3);\r\n padding: var(--juxt-space-3) var(--juxt-space-4);\r\n border: 1px solid var(--j-alert-border);\r\n border-radius: var(--juxt-radius-md);\r\n background: var(--juxt-surface);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-alert--info {\r\n --j-alert-accent: var(--juxt-info-text);\r\n}\r\n\r\n.j-alert--success {\r\n --j-alert-accent: var(--juxt-success-text);\r\n}\r\n\r\n.j-alert--warning {\r\n --j-alert-accent: var(--juxt-warning-text);\r\n --j-alert-border: var(--juxt-warning-border);\r\n}\r\n\r\n.j-alert--destructive {\r\n --j-alert-accent: var(--juxt-danger-text);\r\n --j-alert-border: var(--juxt-danger-border);\r\n}\r\n\r\n.j-alert__icon {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n height: 1.25rem;\r\n color: var(--j-alert-accent);\r\n}\r\n\r\n.j-alert__icon > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-alert__body {\r\n display: flex;\r\n flex: 1;\r\n flex-direction: column;\r\n gap: var(--juxt-space-0-5);\r\n min-width: 0;\r\n}\r\n\r\n.j-alert__title {\r\n margin: 0;\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: 1.25rem;\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-alert__description {\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-alert__description > p {\r\n margin: 0;\r\n}\r\n\r\n.j-alert__actions {\r\n display: flex;\r\n flex-wrap: wrap;\r\n gap: var(--juxt-space-2);\r\n margin-top: var(--juxt-space-2);\r\n}\r\n\r\n.j-alert:has(.j-alert__close) {\r\n padding-right: var(--juxt-space-10);\r\n}\r\n\r\n.j-alert__close {\r\n position: absolute;\r\n top: var(--juxt-space-2-5);\r\n right: var(--juxt-space-2-5);\r\n display: grid;\r\n place-items: center;\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-xs);\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n cursor: pointer;\r\n transition-property: background-color, color, outline-color, outline-offset;\r\n}\r\n\r\n.j-alert__close:hover {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-alert__close > svg {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\n\r\nexport interface AspectRatioProps {\r\n /** Width/height ratio, e.g. `16 / 9` or `1`. */\r\n ratio?: number\r\n}\r\n\r\nconst props = withDefaults(defineProps<AspectRatioProps>(), { ratio: 16 / 9 })\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n\r\nconst style = computed(() => ({ '--j-aspect-ratio': String(props.ratio) }))\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-aspect-ratio\" :style=\"style\">\r\n <div class=\"j-aspect-ratio__content\">\r\n <slot />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-aspect-ratio {\r\n position: relative;\r\n width: 100%;\r\n aspect-ratio: var(--j-aspect-ratio, 16 / 9);\r\n}\r\n\r\n.j-aspect-ratio__content {\r\n position: absolute;\r\n inset: 0;\r\n overflow: hidden;\r\n}\r\n\r\n.j-aspect-ratio__content > :where(img, video, iframe, canvas) {\r\n display: block;\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\n\r\nexport interface AspectRatioProps {\r\n /** Width/height ratio, e.g. `16 / 9` or `1`. */\r\n ratio?: number\r\n}\r\n\r\nconst props = withDefaults(defineProps<AspectRatioProps>(), { ratio: 16 / 9 })\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n\r\nconst style = computed(() => ({ '--j-aspect-ratio': String(props.ratio) }))\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-aspect-ratio\" :style=\"style\">\r\n <div class=\"j-aspect-ratio__content\">\r\n <slot />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-aspect-ratio {\r\n position: relative;\r\n width: 100%;\r\n aspect-ratio: var(--j-aspect-ratio, 16 / 9);\r\n}\r\n\r\n.j-aspect-ratio__content {\r\n position: absolute;\r\n inset: 0;\r\n overflow: hidden;\r\n}\r\n\r\n.j-aspect-ratio__content > :where(img, video, iframe, canvas) {\r\n display: block;\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, onMounted, ref, watch } from 'vue'\r\n\r\nexport type AvatarStatus = 'online' | 'away' | 'busy' | 'offline'\r\n\r\nexport interface AvatarProps {\r\n src?: string\r\n /** Image alt text. Defaults to `name`. */\r\n alt?: string\r\n /** Full name | used for initials and as the accessible name. */\r\n name?: string\r\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\r\n shape?: 'circle' | 'square'\r\n status?: AvatarStatus\r\n}\r\n\r\nconst props = withDefaults(defineProps<AvatarProps>(), { size: 'md', shape: 'circle' })\r\n\r\ndefineSlots<{ fallback?: () => unknown }>()\r\n\r\nconst state = ref<'loading' | 'loaded' | 'error'>(props.src ? 'loading' : 'error')\r\nconst img = ref<HTMLImageElement | null>(null)\r\n\r\nwatch(() => props.src, (src) => {\r\n state.value = src ? 'loading' : 'error'\r\n})\r\n\r\n// An image cached before hydration may have finished loading already.\r\nonMounted(() => {\r\n if (img.value?.complete && img.value.naturalWidth > 0) state.value = 'loaded'\r\n})\r\n\r\nconst initials = computed(() => {\r\n const parts = (props.name ?? '').trim().split(/\\s+/).filter(Boolean)\r\n if (parts.length === 0) return ''\r\n const first = parts[0]!\r\n const last = parts.length > 1 ? parts[parts.length - 1]! : ''\r\n return (first[0]! + (last[0] ?? '')).toUpperCase()\r\n})\r\n\r\nconst statusLabel: Record<AvatarStatus, string> = {\r\n online: 'Online',\r\n away: 'Away',\r\n busy: 'Busy',\r\n offline: 'Offline',\r\n}\r\n\r\nconst label = computed(() => {\r\n const who = props.alt ?? props.name\r\n if (!who) return undefined\r\n return props.status ? `${who} (${statusLabel[props.status]})` : who\r\n})\r\n</script>\r\n\r\n<template>\r\n <span\r\n class=\"j-avatar\"\r\n :class=\"[`j-avatar--${size}`, `j-avatar--${shape}`]\"\r\n :role=\"label ? 'img' : undefined\"\r\n :aria-label=\"label\"\r\n :data-state=\"state\"\r\n >\r\n <span class=\"j-avatar__frame\">\r\n <img\r\n v-if=\"src && state !== 'error'\"\r\n ref=\"img\"\r\n class=\"j-avatar__image\"\r\n :src=\"src\"\r\n alt=\"\"\r\n @load=\"state = 'loaded'\"\r\n @error=\"state = 'error'\"\r\n />\r\n <span v-if=\"state !== 'loaded'\" class=\"j-avatar__fallback\" aria-hidden=\"true\">\r\n <slot name=\"fallback\">\r\n <template v-if=\"initials\">{{ initials }}</template>\r\n <svg v-else viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"j-avatar__glyph\">\r\n <circle cx=\"8\" cy=\"6\" r=\"2.75\" />\r\n <path d=\"M2.75 14c.6-2.6 2.7-4.25 5.25-4.25S12.65 11.4 13.25 14z\" />\r\n </svg>\r\n </slot>\r\n </span>\r\n </span>\r\n <span v-if=\"status\" class=\"j-avatar__status\" :class=\"`j-avatar__status--${status}`\" aria-hidden=\"true\" />\r\n </span>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-avatar {\r\n --j-avatar-size: 2rem;\r\n --j-avatar-font: var(--juxt-text-xs);\r\n --j-avatar-status: 0.5rem;\r\n --j-avatar-radius: var(--juxt-radius-full);\r\n\r\n position: relative;\r\n display: inline-flex;\r\n flex: none;\r\n width: var(--j-avatar-size);\r\n height: var(--j-avatar-size);\r\n font-family: var(--juxt-font-sans);\r\n vertical-align: middle;\r\n}\r\n\r\n.j-avatar--xs {\r\n --j-avatar-size: 1.25rem;\r\n --j-avatar-font: 0.5625rem;\r\n --j-avatar-status: 0.375rem;\r\n}\r\n\r\n.j-avatar--sm {\r\n --j-avatar-size: 1.5rem;\r\n --j-avatar-font: 0.625rem;\r\n --j-avatar-status: 0.4375rem;\r\n}\r\n\r\n.j-avatar--lg {\r\n --j-avatar-size: 2.5rem;\r\n --j-avatar-font: var(--juxt-text-sm);\r\n --j-avatar-status: 0.625rem;\r\n}\r\n\r\n.j-avatar--xl {\r\n --j-avatar-size: 3.5rem;\r\n --j-avatar-font: var(--juxt-text-lg);\r\n --j-avatar-status: 0.75rem;\r\n}\r\n\r\n.j-avatar--square {\r\n --j-avatar-radius: calc(var(--j-avatar-size) * 0.22);\r\n}\r\n\r\n.j-avatar__frame {\r\n position: relative;\r\n display: grid;\r\n width: 100%;\r\n height: 100%;\r\n overflow: hidden;\r\n border-radius: var(--j-avatar-radius);\r\n background: var(--juxt-surface-active);\r\n /* A hairline keeps light photos from bleeding into light backgrounds. */\r\n box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.06);\r\n}\r\n\r\n.dark .j-avatar__frame,\r\n[data-theme='dark'] .j-avatar__frame {\r\n box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.06);\r\n}\r\n\r\n.j-avatar__image,\r\n.j-avatar__fallback {\r\n grid-area: 1 / 1;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.j-avatar__image {\r\n object-fit: cover;\r\n opacity: 0;\r\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-avatar[data-state='loaded'] .j-avatar__image {\r\n opacity: 1;\r\n}\r\n\r\n.j-avatar__fallback {\r\n display: grid;\r\n place-items: center;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--j-avatar-font);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: 0.02em;\r\n line-height: 1;\r\n user-select: none;\r\n}\r\n\r\n.j-avatar__glyph {\r\n width: 62%;\r\n height: 62%;\r\n margin-top: 18%;\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-avatar__status {\r\n position: absolute;\r\n right: 0;\r\n bottom: 0;\r\n width: var(--j-avatar-status);\r\n height: var(--j-avatar-status);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-fg-muted);\r\n /* The ring is the page background, so the dot cuts cleanly out of the photo. */\r\n box-shadow: 0 0 0 2px var(--j-avatar-ring, var(--juxt-bg));\r\n}\r\n\r\n.j-avatar--square .j-avatar__status {\r\n right: -2px;\r\n bottom: -2px;\r\n}\r\n\r\n.j-avatar__status--online {\r\n background: var(--juxt-accent);\r\n}\r\n\r\n.j-avatar__status--away {\r\n background: var(--juxt-warning);\r\n}\r\n\r\n.j-avatar__status--busy {\r\n background: var(--juxt-danger);\r\n}\r\n\r\n.j-avatar__status--offline {\r\n background: var(--juxt-fg-disabled);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, onMounted, ref, watch } from 'vue'\r\n\r\nexport type AvatarStatus = 'online' | 'away' | 'busy' | 'offline'\r\n\r\nexport interface AvatarProps {\r\n src?: string\r\n /** Image alt text. Defaults to `name`. */\r\n alt?: string\r\n /** Full name | used for initials and as the accessible name. */\r\n name?: string\r\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\r\n shape?: 'circle' | 'square'\r\n status?: AvatarStatus\r\n}\r\n\r\nconst props = withDefaults(defineProps<AvatarProps>(), { size: 'md', shape: 'circle' })\r\n\r\ndefineSlots<{ fallback?: () => unknown }>()\r\n\r\nconst state = ref<'loading' | 'loaded' | 'error'>(props.src ? 'loading' : 'error')\r\nconst img = ref<HTMLImageElement | null>(null)\r\n\r\nwatch(() => props.src, (src) => {\r\n state.value = src ? 'loading' : 'error'\r\n})\r\n\r\n// An image cached before hydration may have finished loading already.\r\nonMounted(() => {\r\n if (img.value?.complete && img.value.naturalWidth > 0) state.value = 'loaded'\r\n})\r\n\r\nconst initials = computed(() => {\r\n const parts = (props.name ?? '').trim().split(/\\s+/).filter(Boolean)\r\n if (parts.length === 0) return ''\r\n const first = parts[0]!\r\n const last = parts.length > 1 ? parts[parts.length - 1]! : ''\r\n return (first[0]! + (last[0] ?? '')).toUpperCase()\r\n})\r\n\r\nconst statusLabel: Record<AvatarStatus, string> = {\r\n online: 'Online',\r\n away: 'Away',\r\n busy: 'Busy',\r\n offline: 'Offline',\r\n}\r\n\r\nconst label = computed(() => {\r\n const who = props.alt ?? props.name\r\n if (!who) return undefined\r\n return props.status ? `${who} (${statusLabel[props.status]})` : who\r\n})\r\n</script>\r\n\r\n<template>\r\n <span\r\n class=\"j-avatar\"\r\n :class=\"[`j-avatar--${size}`, `j-avatar--${shape}`]\"\r\n :role=\"label ? 'img' : undefined\"\r\n :aria-label=\"label\"\r\n :data-state=\"state\"\r\n >\r\n <span class=\"j-avatar__frame\">\r\n <img\r\n v-if=\"src && state !== 'error'\"\r\n ref=\"img\"\r\n class=\"j-avatar__image\"\r\n :src=\"src\"\r\n alt=\"\"\r\n @load=\"state = 'loaded'\"\r\n @error=\"state = 'error'\"\r\n />\r\n <span v-if=\"state !== 'loaded'\" class=\"j-avatar__fallback\" aria-hidden=\"true\">\r\n <slot name=\"fallback\">\r\n <template v-if=\"initials\">{{ initials }}</template>\r\n <svg v-else viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"j-avatar__glyph\">\r\n <circle cx=\"8\" cy=\"6\" r=\"2.75\" />\r\n <path d=\"M2.75 14c.6-2.6 2.7-4.25 5.25-4.25S12.65 11.4 13.25 14z\" />\r\n </svg>\r\n </slot>\r\n </span>\r\n </span>\r\n <span v-if=\"status\" class=\"j-avatar__status\" :class=\"`j-avatar__status--${status}`\" aria-hidden=\"true\" />\r\n </span>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-avatar {\r\n --j-avatar-size: 2rem;\r\n --j-avatar-font: var(--juxt-text-xs);\r\n --j-avatar-status: 0.5rem;\r\n --j-avatar-radius: var(--juxt-radius-full);\r\n\r\n position: relative;\r\n display: inline-flex;\r\n flex: none;\r\n width: var(--j-avatar-size);\r\n height: var(--j-avatar-size);\r\n font-family: var(--juxt-font-sans);\r\n vertical-align: middle;\r\n}\r\n\r\n.j-avatar--xs {\r\n --j-avatar-size: 1.25rem;\r\n --j-avatar-font: 0.5625rem;\r\n --j-avatar-status: 0.375rem;\r\n}\r\n\r\n.j-avatar--sm {\r\n --j-avatar-size: 1.5rem;\r\n --j-avatar-font: 0.625rem;\r\n --j-avatar-status: 0.4375rem;\r\n}\r\n\r\n.j-avatar--lg {\r\n --j-avatar-size: 2.5rem;\r\n --j-avatar-font: var(--juxt-text-sm);\r\n --j-avatar-status: 0.625rem;\r\n}\r\n\r\n.j-avatar--xl {\r\n --j-avatar-size: 3.5rem;\r\n --j-avatar-font: var(--juxt-text-lg);\r\n --j-avatar-status: 0.75rem;\r\n}\r\n\r\n.j-avatar--square {\r\n --j-avatar-radius: calc(var(--j-avatar-size) * 0.22);\r\n}\r\n\r\n.j-avatar__frame {\r\n position: relative;\r\n display: grid;\r\n width: 100%;\r\n height: 100%;\r\n overflow: hidden;\r\n border-radius: var(--j-avatar-radius);\r\n background: var(--juxt-surface-active);\r\n /* A hairline keeps light photos from bleeding into light backgrounds. */\r\n box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.06);\r\n}\r\n\r\n.dark .j-avatar__frame,\r\n[data-theme='dark'] .j-avatar__frame {\r\n box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.06);\r\n}\r\n\r\n.j-avatar__image,\r\n.j-avatar__fallback {\r\n grid-area: 1 / 1;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.j-avatar__image {\r\n object-fit: cover;\r\n opacity: 0;\r\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-avatar[data-state='loaded'] .j-avatar__image {\r\n opacity: 1;\r\n}\r\n\r\n.j-avatar__fallback {\r\n display: grid;\r\n place-items: center;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--j-avatar-font);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: 0.02em;\r\n line-height: 1;\r\n user-select: none;\r\n}\r\n\r\n.j-avatar__glyph {\r\n width: 62%;\r\n height: 62%;\r\n margin-top: 18%;\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-avatar__status {\r\n position: absolute;\r\n right: 0;\r\n bottom: 0;\r\n width: var(--j-avatar-status);\r\n height: var(--j-avatar-status);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-fg-muted);\r\n /* The ring is the page background, so the dot cuts cleanly out of the photo. */\r\n box-shadow: 0 0 0 2px var(--j-avatar-ring, var(--juxt-bg));\r\n}\r\n\r\n.j-avatar--square .j-avatar__status {\r\n right: -2px;\r\n bottom: -2px;\r\n}\r\n\r\n.j-avatar__status--online {\r\n background: var(--juxt-accent);\r\n}\r\n\r\n.j-avatar__status--away {\r\n background: var(--juxt-warning);\r\n}\r\n\r\n.j-avatar__status--busy {\r\n background: var(--juxt-danger);\r\n}\r\n\r\n.j-avatar__status--offline {\r\n background: var(--juxt-fg-disabled);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport JAvatar from '../avatar/JAvatar.vue'\r\n\r\nexport interface AvatarGroupItem {\r\n src?: string\r\n name?: string\r\n}\r\n\r\nexport interface AvatarGroupProps {\r\n items: AvatarGroupItem[]\r\n /** Avatars shown before the rest collapse into a \"+N\" indicator. */\r\n max?: number\r\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\r\n shape?: 'circle' | 'square'\r\n /** Accessible name for the group. Defaults to a plain count, not an assumption about who's in it. */\r\n label?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<AvatarGroupProps>(), { size: 'md', shape: 'circle' })\r\n\r\nconst visible = computed(() => (props.max ? props.items.slice(0, props.max) : props.items))\r\nconst overflow = computed(() => Math.max(0, props.items.length - visible.value.length))\r\nconst groupLabel = computed(() => props.label ?? `${props.items.length} ${props.items.length === 1 ? 'avatar' : 'avatars'}`)\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-avatar-group\" :class=\"`j-avatar-group--${size}`\" role=\"group\" :aria-label=\"groupLabel\">\r\n <span v-for=\"(item, index) in visible\" :key=\"index\" class=\"j-avatar-group__item\">\r\n <JAvatar :src=\"item.src\" :name=\"item.name\" :size=\"size\" :shape=\"shape\" />\r\n </span>\r\n <span\r\n v-if=\"overflow > 0\"\r\n class=\"j-avatar-group__item j-avatar-group__overflow j-focusable\"\r\n :class=\"{ 'j-avatar-group__overflow--square': shape === 'square' }\"\r\n tabindex=\"0\"\r\n :aria-label=\"`${overflow} more`\"\r\n >\r\n +{{ overflow }}\r\n </span>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-avatar-group {\r\n --j-avatar-group-overlap: 0.5em;\r\n\r\n display: inline-flex;\r\n align-items: center;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-avatar-group__item {\r\n position: relative;\r\n display: inline-flex;\r\n flex: none;\r\n border-radius: var(--juxt-radius-full);\r\n box-shadow: 0 0 0 2px var(--juxt-bg);\r\n}\r\n\r\n.j-avatar-group__item:not(:first-child) {\r\n margin-left: calc(var(--j-avatar-group-overlap) * -1);\r\n}\r\n\r\n.j-avatar-group__overflow {\r\n display: grid;\r\n place-items: center;\r\n width: 2rem;\r\n height: 2rem;\r\n background: var(--juxt-surface-active);\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-xs);\r\n font-weight: var(--juxt-weight-medium);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-avatar-group--xs .j-avatar-group__overflow {\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n font-size: var(--juxt-text-2xs);\r\n}\r\n\r\n.j-avatar-group--sm .j-avatar-group__overflow {\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n font-size: 0.625rem;\r\n}\r\n\r\n.j-avatar-group--lg .j-avatar-group__overflow {\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n font-size: var(--juxt-text-sm);\r\n}\r\n\r\n.j-avatar-group--xl .j-avatar-group__overflow {\r\n width: 3.5rem;\r\n height: 3.5rem;\r\n font-size: var(--juxt-text-lg);\r\n}\r\n\r\n.j-avatar-group__overflow--square {\r\n border-radius: 22%;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport JAvatar from '../avatar/JAvatar.vue'\r\n\r\nexport interface AvatarGroupItem {\r\n src?: string\r\n name?: string\r\n}\r\n\r\nexport interface AvatarGroupProps {\r\n items: AvatarGroupItem[]\r\n /** Avatars shown before the rest collapse into a \"+N\" indicator. */\r\n max?: number\r\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\r\n shape?: 'circle' | 'square'\r\n /** Accessible name for the group. Defaults to a plain count, not an assumption about who's in it. */\r\n label?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<AvatarGroupProps>(), { size: 'md', shape: 'circle' })\r\n\r\nconst visible = computed(() => (props.max ? props.items.slice(0, props.max) : props.items))\r\nconst overflow = computed(() => Math.max(0, props.items.length - visible.value.length))\r\nconst groupLabel = computed(() => props.label ?? `${props.items.length} ${props.items.length === 1 ? 'avatar' : 'avatars'}`)\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-avatar-group\" :class=\"`j-avatar-group--${size}`\" role=\"group\" :aria-label=\"groupLabel\">\r\n <span v-for=\"(item, index) in visible\" :key=\"index\" class=\"j-avatar-group__item\">\r\n <JAvatar :src=\"item.src\" :name=\"item.name\" :size=\"size\" :shape=\"shape\" />\r\n </span>\r\n <span\r\n v-if=\"overflow > 0\"\r\n class=\"j-avatar-group__item j-avatar-group__overflow j-focusable\"\r\n :class=\"{ 'j-avatar-group__overflow--square': shape === 'square' }\"\r\n tabindex=\"0\"\r\n :aria-label=\"`${overflow} more`\"\r\n >\r\n +{{ overflow }}\r\n </span>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-avatar-group {\r\n --j-avatar-group-overlap: 0.5em;\r\n\r\n display: inline-flex;\r\n align-items: center;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-avatar-group__item {\r\n position: relative;\r\n display: inline-flex;\r\n flex: none;\r\n border-radius: var(--juxt-radius-full);\r\n box-shadow: 0 0 0 2px var(--juxt-bg);\r\n}\r\n\r\n.j-avatar-group__item:not(:first-child) {\r\n margin-left: calc(var(--j-avatar-group-overlap) * -1);\r\n}\r\n\r\n.j-avatar-group__overflow {\r\n display: grid;\r\n place-items: center;\r\n width: 2rem;\r\n height: 2rem;\r\n background: var(--juxt-surface-active);\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-xs);\r\n font-weight: var(--juxt-weight-medium);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-avatar-group--xs .j-avatar-group__overflow {\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n font-size: var(--juxt-text-2xs);\r\n}\r\n\r\n.j-avatar-group--sm .j-avatar-group__overflow {\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n font-size: 0.625rem;\r\n}\r\n\r\n.j-avatar-group--lg .j-avatar-group__overflow {\r\n width: 2.5rem;\r\n height: 2.5rem;\r\n font-size: var(--juxt-text-sm);\r\n}\r\n\r\n.j-avatar-group--xl .j-avatar-group__overflow {\r\n width: 3.5rem;\r\n height: 3.5rem;\r\n font-size: var(--juxt-text-lg);\r\n}\r\n\r\n.j-avatar-group__overflow--square {\r\n border-radius: 22%;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nexport type BadgeVariant = 'neutral' | 'success' | 'warning' | 'destructive' | 'accent'\r\n\r\nexport interface BadgeProps {\r\n variant?: BadgeVariant\r\n size?: 'sm' | 'md'\r\n /**\r\n * Status style: plain text led by a small square marker, no fill.\r\n * Reads as a state rather than a label.\r\n */\r\n dot?: boolean\r\n}\r\n\r\nwithDefaults(defineProps<BadgeProps>(), { variant: 'neutral', size: 'md' })\r\n\r\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\r\n</script>\r\n\r\n<template>\r\n <span class=\"j-badge\" :class=\"[`j-badge--${variant}`, `j-badge--${size}`, { 'j-badge--status': dot }]\">\r\n <span v-if=\"dot\" class=\"j-badge__marker\" aria-hidden=\"true\" />\r\n <span v-else-if=\"$slots.leading\" class=\"j-badge__icon\"><slot name=\"leading\" /></span>\r\n <slot />\r\n </span>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n/*\r\n * Badges are small, square-shouldered labels | closer to a stamp than a pill.\r\n * No borders: a quiet tint and the text colour carry the meaning.\r\n */\r\n.j-badge {\r\n --j-badge-fg: var(--juxt-fg-secondary);\r\n --j-badge-bg: var(--juxt-surface-active);\r\n --j-badge-marker: var(--juxt-fg-muted);\r\n\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n gap: 0.375rem;\r\n height: 1.25rem;\r\n padding: 0 0.375rem;\r\n border-radius: var(--juxt-radius-xs);\r\n background: var(--j-badge-bg);\r\n color: var(--j-badge-fg);\r\n font-family: var(--juxt-font-sans);\r\n font-size: var(--juxt-text-xs);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: 1;\r\n letter-spacing: 0;\r\n white-space: nowrap;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-badge--sm {\r\n gap: 0.3125rem;\r\n height: 1.0625rem;\r\n padding: 0 0.3125rem;\r\n font-size: var(--juxt-text-2xs);\r\n}\r\n\r\n.j-badge--success {\r\n --j-badge-fg: var(--juxt-success-text);\r\n --j-badge-bg: var(--juxt-success-soft);\r\n --j-badge-marker: var(--juxt-success);\r\n}\r\n\r\n.j-badge--warning {\r\n --j-badge-fg: var(--juxt-warning-text);\r\n --j-badge-bg: var(--juxt-warning-soft);\r\n --j-badge-marker: var(--juxt-warning);\r\n}\r\n\r\n.j-badge--destructive {\r\n --j-badge-fg: var(--juxt-danger-text);\r\n --j-badge-bg: var(--juxt-danger-soft);\r\n --j-badge-marker: var(--juxt-danger);\r\n}\r\n\r\n/* Accent is the only solid badge | reserve it for the thing that matters. */\r\n.j-badge--accent {\r\n --j-badge-fg: var(--juxt-accent-fg);\r\n --j-badge-bg: var(--juxt-accent-strong);\r\n --j-badge-marker: var(--juxt-accent);\r\n}\r\n\r\n/* Status: text first, colour only in the marker. */\r\n.j-badge--status {\r\n padding: 0;\r\n background: none;\r\n color: var(--juxt-fg-secondary);\r\n}\r\n\r\n.j-badge__marker {\r\n flex: none;\r\n width: 0.4375rem;\r\n height: 0.4375rem;\r\n border-radius: 1.5px;\r\n background: var(--j-badge-marker);\r\n}\r\n\r\n.j-badge--sm .j-badge__marker {\r\n width: 0.375rem;\r\n height: 0.375rem;\r\n}\r\n\r\n.j-badge__icon {\r\n display: inline-flex;\r\n margin-left: -0.0625rem;\r\n}\r\n\r\n.j-badge__icon > svg {\r\n width: 0.75rem;\r\n height: 0.75rem;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nexport type BadgeVariant = 'neutral' | 'success' | 'warning' | 'destructive' | 'accent'\r\n\r\nexport interface BadgeProps {\r\n variant?: BadgeVariant\r\n size?: 'sm' | 'md'\r\n /**\r\n * Status style: plain text led by a small square marker, no fill.\r\n * Reads as a state rather than a label.\r\n */\r\n dot?: boolean\r\n}\r\n\r\nwithDefaults(defineProps<BadgeProps>(), { variant: 'neutral', size: 'md' })\r\n\r\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\r\n</script>\r\n\r\n<template>\r\n <span class=\"j-badge\" :class=\"[`j-badge--${variant}`, `j-badge--${size}`, { 'j-badge--status': dot }]\">\r\n <span v-if=\"dot\" class=\"j-badge__marker\" aria-hidden=\"true\" />\r\n <span v-else-if=\"$slots.leading\" class=\"j-badge__icon\"><slot name=\"leading\" /></span>\r\n <slot />\r\n </span>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n/*\r\n * Badges are small, square-shouldered labels | closer to a stamp than a pill.\r\n * No borders: a quiet tint and the text colour carry the meaning.\r\n */\r\n.j-badge {\r\n --j-badge-fg: var(--juxt-fg-secondary);\r\n --j-badge-bg: var(--juxt-surface-active);\r\n --j-badge-marker: var(--juxt-fg-muted);\r\n\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n gap: 0.375rem;\r\n height: 1.25rem;\r\n padding: 0 0.375rem;\r\n border-radius: var(--juxt-radius-xs);\r\n background: var(--j-badge-bg);\r\n color: var(--j-badge-fg);\r\n font-family: var(--juxt-font-sans);\r\n font-size: var(--juxt-text-xs);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: 1;\r\n letter-spacing: 0;\r\n white-space: nowrap;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-badge--sm {\r\n gap: 0.3125rem;\r\n height: 1.0625rem;\r\n padding: 0 0.3125rem;\r\n font-size: var(--juxt-text-2xs);\r\n}\r\n\r\n.j-badge--success {\r\n --j-badge-fg: var(--juxt-success-text);\r\n --j-badge-bg: var(--juxt-success-soft);\r\n --j-badge-marker: var(--juxt-success);\r\n}\r\n\r\n.j-badge--warning {\r\n --j-badge-fg: var(--juxt-warning-text);\r\n --j-badge-bg: var(--juxt-warning-soft);\r\n --j-badge-marker: var(--juxt-warning);\r\n}\r\n\r\n.j-badge--destructive {\r\n --j-badge-fg: var(--juxt-danger-text);\r\n --j-badge-bg: var(--juxt-danger-soft);\r\n --j-badge-marker: var(--juxt-danger);\r\n}\r\n\r\n/* Accent is the only solid badge | reserve it for the thing that matters. */\r\n.j-badge--accent {\r\n --j-badge-fg: var(--juxt-accent-fg);\r\n --j-badge-bg: var(--juxt-accent-strong);\r\n --j-badge-marker: var(--juxt-accent);\r\n}\r\n\r\n/* Status: text first, colour only in the marker. */\r\n.j-badge--status {\r\n padding: 0;\r\n background: none;\r\n color: var(--juxt-fg-secondary);\r\n}\r\n\r\n.j-badge__marker {\r\n flex: none;\r\n width: 0.4375rem;\r\n height: 0.4375rem;\r\n border-radius: 1.5px;\r\n background: var(--j-badge-marker);\r\n}\r\n\r\n.j-badge--sm .j-badge__marker {\r\n width: 0.375rem;\r\n height: 0.375rem;\r\n}\r\n\r\n.j-badge__icon {\r\n display: inline-flex;\r\n margin-left: -0.0625rem;\r\n}\r\n\r\n.j-badge__icon > svg {\r\n width: 0.75rem;\r\n height: 0.75rem;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX } from '../../icons'\r\n\r\nexport type BannerVariant = 'neutral' | 'info' | 'success' | 'warning' | 'destructive'\r\n\r\nexport interface BannerProps {\r\n variant?: BannerVariant\r\n title?: string\r\n description?: string\r\n /** Show a close button; emits `dismiss`. */\r\n dismissible?: boolean\r\n /** Hide the leading icon. */\r\n hideIcon?: boolean\r\n}\r\n\r\nwithDefaults(defineProps<BannerProps>(), { variant: 'neutral' })\r\n\r\nconst emit = defineEmits<{ dismiss: [] }>()\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n icon?: () => unknown\r\n actions?: () => unknown\r\n}>()\r\n\r\nconst icons = {\r\n neutral: IconInfo,\r\n info: IconInfo,\r\n success: IconCircleCheck,\r\n warning: IconAlert,\r\n destructive: IconCircleX,\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-banner\" :class=\"`j-banner--${variant}`\" role=\"region\" :aria-label=\"title\">\r\n <span v-if=\"!hideIcon\" class=\"j-banner__icon\" aria-hidden=\"true\">\r\n <slot name=\"icon\"><component :is=\"icons[variant]\" /></slot>\r\n </span>\r\n <div class=\"j-banner__body\">\r\n <p v-if=\"title\" class=\"j-banner__title\">\r\n {{ title }}\r\n </p>\r\n <div v-if=\"description || $slots.default\" class=\"j-banner__description\">\r\n <slot>{{ description }}</slot>\r\n </div>\r\n </div>\r\n <div v-if=\"$slots.actions\" class=\"j-banner__actions\">\r\n <slot name=\"actions\" />\r\n </div>\r\n <button v-if=\"dismissible\" type=\"button\" class=\"j-banner__close j-focusable\" aria-label=\"Dismiss\" @click=\"emit('dismiss')\">\r\n <IconX />\r\n </button>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n/*\r\n * Banners run edge-to-edge across a page or section, unlike JAlert which sits\r\n * inline in a card or form. A flat tinted surface and a left accent bar\r\n * distinguish them from the bordered, neutral surface of an alert.\r\n */\r\n.j-banner {\r\n --j-banner-accent: var(--juxt-fg-muted);\r\n --j-banner-bg: var(--juxt-surface-sunken);\r\n\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-3);\r\n padding: var(--juxt-space-3) var(--juxt-space-5);\r\n border-left: 3px solid var(--j-banner-accent);\r\n background: var(--j-banner-bg);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-banner--info {\r\n --j-banner-accent: var(--juxt-info);\r\n --j-banner-bg: var(--juxt-info-soft);\r\n}\r\n\r\n.j-banner--success {\r\n --j-banner-accent: var(--juxt-success);\r\n --j-banner-bg: var(--juxt-success-soft);\r\n}\r\n\r\n.j-banner--warning {\r\n --j-banner-accent: var(--juxt-warning);\r\n --j-banner-bg: var(--juxt-warning-soft);\r\n}\r\n\r\n.j-banner--destructive {\r\n --j-banner-accent: var(--juxt-danger);\r\n --j-banner-bg: var(--juxt-danger-soft);\r\n}\r\n\r\n.j-banner__icon {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n height: 1.25rem;\r\n color: var(--j-banner-accent);\r\n}\r\n\r\n.j-banner__icon > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-banner__body {\r\n display: flex;\r\n flex: 1;\r\n flex-wrap: wrap;\r\n align-items: baseline;\r\n gap: var(--juxt-space-1) var(--juxt-space-2);\r\n min-width: 0;\r\n}\r\n\r\n.j-banner__title {\r\n margin: 0;\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-banner__description {\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-banner__description > p {\r\n margin: 0;\r\n}\r\n\r\n.j-banner__actions {\r\n display: flex;\r\n flex: none;\r\n flex-wrap: wrap;\r\n gap: var(--juxt-space-2);\r\n}\r\n\r\n.j-banner__close {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-xs);\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n cursor: pointer;\r\n transition-property: background-color, color, outline-color, outline-offset;\r\n}\r\n\r\n.j-banner__close:hover {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-banner__close > svg {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX } from '../../icons'\r\n\r\nexport type BannerVariant = 'neutral' | 'info' | 'success' | 'warning' | 'destructive'\r\n\r\nexport interface BannerProps {\r\n variant?: BannerVariant\r\n title?: string\r\n description?: string\r\n /** Show a close button; emits `dismiss`. */\r\n dismissible?: boolean\r\n /** Hide the leading icon. */\r\n hideIcon?: boolean\r\n}\r\n\r\nwithDefaults(defineProps<BannerProps>(), { variant: 'neutral' })\r\n\r\nconst emit = defineEmits<{ dismiss: [] }>()\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n icon?: () => unknown\r\n actions?: () => unknown\r\n}>()\r\n\r\nconst icons = {\r\n neutral: IconInfo,\r\n info: IconInfo,\r\n success: IconCircleCheck,\r\n warning: IconAlert,\r\n destructive: IconCircleX,\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-banner\" :class=\"`j-banner--${variant}`\" role=\"region\" :aria-label=\"title\">\r\n <span v-if=\"!hideIcon\" class=\"j-banner__icon\" aria-hidden=\"true\">\r\n <slot name=\"icon\"><component :is=\"icons[variant]\" /></slot>\r\n </span>\r\n <div class=\"j-banner__body\">\r\n <p v-if=\"title\" class=\"j-banner__title\">\r\n {{ title }}\r\n </p>\r\n <div v-if=\"description || $slots.default\" class=\"j-banner__description\">\r\n <slot>{{ description }}</slot>\r\n </div>\r\n </div>\r\n <div v-if=\"$slots.actions\" class=\"j-banner__actions\">\r\n <slot name=\"actions\" />\r\n </div>\r\n <button v-if=\"dismissible\" type=\"button\" class=\"j-banner__close j-focusable\" aria-label=\"Dismiss\" @click=\"emit('dismiss')\">\r\n <IconX />\r\n </button>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n/*\r\n * Banners run edge-to-edge across a page or section, unlike JAlert which sits\r\n * inline in a card or form. A flat tinted surface and a left accent bar\r\n * distinguish them from the bordered, neutral surface of an alert.\r\n */\r\n.j-banner {\r\n --j-banner-accent: var(--juxt-fg-muted);\r\n --j-banner-bg: var(--juxt-surface-sunken);\r\n\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-3);\r\n padding: var(--juxt-space-3) var(--juxt-space-5);\r\n border-left: 3px solid var(--j-banner-accent);\r\n background: var(--j-banner-bg);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-banner--info {\r\n --j-banner-accent: var(--juxt-info);\r\n --j-banner-bg: var(--juxt-info-soft);\r\n}\r\n\r\n.j-banner--success {\r\n --j-banner-accent: var(--juxt-success);\r\n --j-banner-bg: var(--juxt-success-soft);\r\n}\r\n\r\n.j-banner--warning {\r\n --j-banner-accent: var(--juxt-warning);\r\n --j-banner-bg: var(--juxt-warning-soft);\r\n}\r\n\r\n.j-banner--destructive {\r\n --j-banner-accent: var(--juxt-danger);\r\n --j-banner-bg: var(--juxt-danger-soft);\r\n}\r\n\r\n.j-banner__icon {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n height: 1.25rem;\r\n color: var(--j-banner-accent);\r\n}\r\n\r\n.j-banner__icon > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-banner__body {\r\n display: flex;\r\n flex: 1;\r\n flex-wrap: wrap;\r\n align-items: baseline;\r\n gap: var(--juxt-space-1) var(--juxt-space-2);\r\n min-width: 0;\r\n}\r\n\r\n.j-banner__title {\r\n margin: 0;\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-banner__description {\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-banner__description > p {\r\n margin: 0;\r\n}\r\n\r\n.j-banner__actions {\r\n display: flex;\r\n flex: none;\r\n flex-wrap: wrap;\r\n gap: var(--juxt-space-2);\r\n}\r\n\r\n.j-banner__close {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-xs);\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n cursor: pointer;\r\n transition-property: background-color, color, outline-color, outline-offset;\r\n}\r\n\r\n.j-banner__close:hover {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-banner__close > svg {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\n\r\nexport interface BreadcrumbItem {\r\n label: string\r\n href?: string\r\n}\r\n\r\nexport interface BreadcrumbProps {\r\n /** Trail of items, first to last. The last item renders as the current page. */\r\n items?: BreadcrumbItem[]\r\n /** Accessible name for the nav landmark. Defaults to \"Breadcrumb\". */\r\n label?: string\r\n /** Collapse middle items behind an ellipsis once the trail is longer than this. */\r\n maxItems?: number\r\n}\r\n\r\nconst props = defineProps<BreadcrumbProps>()\r\n\r\n/**\r\n * Provide `items` for the common case, or omit it and pass your own `<li>`\r\n * elements (with class `j-breadcrumb__item`) into the default slot for full\r\n * control over each crumb.\r\n */\r\ndefineSlots<{ default?: () => unknown }>()\r\n\r\ninterface ResolvedItem extends BreadcrumbItem {\r\n ellipsis?: boolean\r\n}\r\n\r\nconst visibleItems = computed<ResolvedItem[]>(() => {\r\n const list = props.items ?? []\r\n if (!props.maxItems || list.length <= props.maxItems) return list\r\n const tailCount = Math.max(1, props.maxItems - 2)\r\n return [list[0]!, { label: '…', ellipsis: true }, ...list.slice(-tailCount)]\r\n})\r\n</script>\r\n\r\n<template>\r\n <nav class=\"j-breadcrumb\" :aria-label=\"label || 'Breadcrumb'\">\r\n <ol class=\"j-breadcrumb__list\">\r\n <template v-if=\"items && items.length\">\r\n <li v-for=\"(item, index) in visibleItems\" :key=\"`${item.label}-${index}`\" class=\"j-breadcrumb__item\">\r\n <span v-if=\"item.ellipsis\" class=\"j-breadcrumb__ellipsis\" aria-hidden=\"true\">{{ item.label }}</span>\r\n <a v-else-if=\"item.href && index !== visibleItems.length - 1\" class=\"j-breadcrumb__link j-focusable\" :href=\"item.href\">{{ item.label }}</a>\r\n <span v-else class=\"j-breadcrumb__current\" :aria-current=\"index === visibleItems.length - 1 ? 'page' : undefined\">{{ item.label }}</span>\r\n </li>\r\n </template>\r\n <slot v-else />\r\n </ol>\r\n </nav>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-breadcrumb {\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-breadcrumb__list {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: var(--juxt-space-1-5);\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n.j-breadcrumb__item {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1-5);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-snug);\r\n}\r\n\r\n.j-breadcrumb__item:not(:first-child)::before {\r\n content: '';\r\n display: inline-block;\r\n width: 0.3125rem;\r\n height: 0.3125rem;\r\n margin-right: var(--juxt-space-1-5);\r\n border-top: 1.5px solid var(--juxt-fg-disabled);\r\n border-right: 1.5px solid var(--juxt-fg-disabled);\r\n transform: rotate(45deg);\r\n}\r\n\r\n.j-breadcrumb__link {\r\n border-radius: var(--juxt-radius-xs);\r\n color: var(--juxt-fg-muted);\r\n text-decoration: none;\r\n transition: color var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-breadcrumb__link:hover {\r\n color: var(--juxt-fg);\r\n text-decoration: underline;\r\n text-underline-offset: 2px;\r\n}\r\n\r\n.j-breadcrumb__current {\r\n color: var(--juxt-fg);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-breadcrumb__ellipsis {\r\n color: var(--juxt-fg-disabled);\r\n user-select: none;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\n\r\nexport interface BreadcrumbItem {\r\n label: string\r\n href?: string\r\n}\r\n\r\nexport interface BreadcrumbProps {\r\n /** Trail of items, first to last. The last item renders as the current page. */\r\n items?: BreadcrumbItem[]\r\n /** Accessible name for the nav landmark. Defaults to \"Breadcrumb\". */\r\n label?: string\r\n /** Collapse middle items behind an ellipsis once the trail is longer than this. */\r\n maxItems?: number\r\n}\r\n\r\nconst props = defineProps<BreadcrumbProps>()\r\n\r\n/**\r\n * Provide `items` for the common case, or omit it and pass your own `<li>`\r\n * elements (with class `j-breadcrumb__item`) into the default slot for full\r\n * control over each crumb.\r\n */\r\ndefineSlots<{ default?: () => unknown }>()\r\n\r\ninterface ResolvedItem extends BreadcrumbItem {\r\n ellipsis?: boolean\r\n}\r\n\r\nconst visibleItems = computed<ResolvedItem[]>(() => {\r\n const list = props.items ?? []\r\n if (!props.maxItems || list.length <= props.maxItems) return list\r\n const tailCount = Math.max(1, props.maxItems - 2)\r\n return [list[0]!, { label: '…', ellipsis: true }, ...list.slice(-tailCount)]\r\n})\r\n</script>\r\n\r\n<template>\r\n <nav class=\"j-breadcrumb\" :aria-label=\"label || 'Breadcrumb'\">\r\n <ol class=\"j-breadcrumb__list\">\r\n <template v-if=\"items && items.length\">\r\n <li v-for=\"(item, index) in visibleItems\" :key=\"`${item.label}-${index}`\" class=\"j-breadcrumb__item\">\r\n <span v-if=\"item.ellipsis\" class=\"j-breadcrumb__ellipsis\" aria-hidden=\"true\">{{ item.label }}</span>\r\n <a v-else-if=\"item.href && index !== visibleItems.length - 1\" class=\"j-breadcrumb__link j-focusable\" :href=\"item.href\">{{ item.label }}</a>\r\n <span v-else class=\"j-breadcrumb__current\" :aria-current=\"index === visibleItems.length - 1 ? 'page' : undefined\">{{ item.label }}</span>\r\n </li>\r\n </template>\r\n <slot v-else />\r\n </ol>\r\n </nav>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-breadcrumb {\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-breadcrumb__list {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: var(--juxt-space-1-5);\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n.j-breadcrumb__item {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1-5);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-snug);\r\n}\r\n\r\n.j-breadcrumb__item:not(:first-child)::before {\r\n content: '';\r\n display: inline-block;\r\n width: 0.3125rem;\r\n height: 0.3125rem;\r\n margin-right: var(--juxt-space-1-5);\r\n border-top: 1.5px solid var(--juxt-fg-disabled);\r\n border-right: 1.5px solid var(--juxt-fg-disabled);\r\n transform: rotate(45deg);\r\n}\r\n\r\n.j-breadcrumb__link {\r\n border-radius: var(--juxt-radius-xs);\r\n color: var(--juxt-fg-muted);\r\n text-decoration: none;\r\n transition: color var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-breadcrumb__link:hover {\r\n color: var(--juxt-fg);\r\n text-decoration: underline;\r\n text-underline-offset: 2px;\r\n}\r\n\r\n.j-breadcrumb__current {\r\n color: var(--juxt-fg);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-breadcrumb__ellipsis {\r\n color: var(--juxt-fg-disabled);\r\n user-select: none;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\nexport interface BurgerProps {\n /** Accessible name while closed. */\n openLabel?: string\n /** Accessible name while open. */\n closeLabel?: string\n size?: 'sm' | 'md'\n /** Id of the element this button shows and hides. */\n controls?: string\n}\n\nwithDefaults(defineProps<BurgerProps>(), {\n openLabel: 'Open menu',\n closeLabel: 'Close menu',\n size: 'md',\n})\n\nconst open = defineModel<boolean>({ default: false })\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"j-burger j-focusable\"\n :class=\"`j-burger--${size}`\"\n :data-state=\"open ? 'open' : 'closed'\"\n :aria-expanded=\"open\"\n :aria-controls=\"controls\"\n :aria-label=\"open ? closeLabel : openLabel\"\n @click=\"open = !open\"\n >\n <span class=\"j-burger__lines\" aria-hidden=\"true\">\n <span class=\"j-burger__line\" />\n <span class=\"j-burger__line\" />\n <span class=\"j-burger__line\" />\n </span>\n </button>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-burger {\n --j-burger-size: var(--juxt-control-md);\n --j-burger-width: 1rem;\n --j-burger-gap: 0.3125rem;\n\n display: inline-grid;\n place-items: center;\n width: var(--j-burger-size);\n height: var(--j-burger-size);\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: background-color, outline-color, outline-offset;\n}\n\n.j-burger--sm {\n --j-burger-size: var(--juxt-control-sm);\n --j-burger-width: 0.875rem;\n --j-burger-gap: 0.25rem;\n}\n\n.j-burger:hover {\n background: var(--juxt-surface-hover);\n}\n\n.j-burger__lines {\n position: relative;\n display: block;\n width: var(--j-burger-width);\n height: calc(var(--j-burger-gap) * 2 + 1.5px);\n}\n\n.j-burger__line {\n position: absolute;\n left: 0;\n width: 100%;\n height: 1.5px;\n border-radius: 1px;\n background: currentColor;\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-burger__line:nth-child(1) {\n top: 0;\n}\n\n.j-burger__line:nth-child(2) {\n top: var(--j-burger-gap);\n}\n\n.j-burger__line:nth-child(3) {\n top: calc(var(--j-burger-gap) * 2);\n}\n\n/* Outer lines meet in the middle and cross; the middle one fades. */\n.j-burger[data-state='open'] .j-burger__line:nth-child(1) {\n transform: translateY(var(--j-burger-gap)) rotate(45deg);\n}\n\n.j-burger[data-state='open'] .j-burger__line:nth-child(2) {\n opacity: 0;\n transform: scaleX(0.4);\n}\n\n.j-burger[data-state='open'] .j-burger__line:nth-child(3) {\n transform: translateY(calc(var(--j-burger-gap) * -1)) rotate(-45deg);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-burger__line {\n transition-duration: 0ms;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport interface BurgerProps {\n /** Accessible name while closed. */\n openLabel?: string\n /** Accessible name while open. */\n closeLabel?: string\n size?: 'sm' | 'md'\n /** Id of the element this button shows and hides. */\n controls?: string\n}\n\nwithDefaults(defineProps<BurgerProps>(), {\n openLabel: 'Open menu',\n closeLabel: 'Close menu',\n size: 'md',\n})\n\nconst open = defineModel<boolean>({ default: false })\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"j-burger j-focusable\"\n :class=\"`j-burger--${size}`\"\n :data-state=\"open ? 'open' : 'closed'\"\n :aria-expanded=\"open\"\n :aria-controls=\"controls\"\n :aria-label=\"open ? closeLabel : openLabel\"\n @click=\"open = !open\"\n >\n <span class=\"j-burger__lines\" aria-hidden=\"true\">\n <span class=\"j-burger__line\" />\n <span class=\"j-burger__line\" />\n <span class=\"j-burger__line\" />\n </span>\n </button>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-burger {\n --j-burger-size: var(--juxt-control-md);\n --j-burger-width: 1rem;\n --j-burger-gap: 0.3125rem;\n\n display: inline-grid;\n place-items: center;\n width: var(--j-burger-size);\n height: var(--j-burger-size);\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: background-color, outline-color, outline-offset;\n}\n\n.j-burger--sm {\n --j-burger-size: var(--juxt-control-sm);\n --j-burger-width: 0.875rem;\n --j-burger-gap: 0.25rem;\n}\n\n.j-burger:hover {\n background: var(--juxt-surface-hover);\n}\n\n.j-burger__lines {\n position: relative;\n display: block;\n width: var(--j-burger-width);\n height: calc(var(--j-burger-gap) * 2 + 1.5px);\n}\n\n.j-burger__line {\n position: absolute;\n left: 0;\n width: 100%;\n height: 1.5px;\n border-radius: 1px;\n background: currentColor;\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-burger__line:nth-child(1) {\n top: 0;\n}\n\n.j-burger__line:nth-child(2) {\n top: var(--j-burger-gap);\n}\n\n.j-burger__line:nth-child(3) {\n top: calc(var(--j-burger-gap) * 2);\n}\n\n/* Outer lines meet in the middle and cross; the middle one fades. */\n.j-burger[data-state='open'] .j-burger__line:nth-child(1) {\n transform: translateY(var(--j-burger-gap)) rotate(45deg);\n}\n\n.j-burger[data-state='open'] .j-burger__line:nth-child(2) {\n opacity: 0;\n transform: scaleX(0.4);\n}\n\n.j-burger[data-state='open'] .j-burger__line:nth-child(3) {\n transform: translateY(calc(var(--j-burger-gap) * -1)) rotate(-45deg);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-burger__line {\n transition-duration: 0ms;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\r\nimport type { Component } from 'vue'\r\nimport { computed } from 'vue'\r\nimport { Spinner } from '../../icons'\r\n\r\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive'\r\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg'\r\n\r\nexport interface ButtonProps {\r\n /** Visual weight. `primary` is for the single most important action in a view. */\r\n variant?: ButtonVariant\r\n size?: ButtonSize\r\n /** Shows a spinner, keeps the width, and blocks activation while staying focusable. */\r\n loading?: boolean\r\n disabled?: boolean\r\n /** Square button for a single icon. Provide an `aria-label`. */\r\n icon?: boolean\r\n /** Stretch to the full width of the container. */\r\n block?: boolean\r\n /** Render as another element or component, e.g. `a` or `NuxtLink`. */\r\n as?: string | Component\r\n type?: 'button' | 'submit' | 'reset'\r\n}\r\n\r\nconst props = withDefaults(defineProps<ButtonProps>(), {\r\n variant: 'primary',\r\n size: 'md',\r\n as: 'button',\r\n type: 'button',\r\n})\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n leading?: () => unknown\r\n trailing?: () => unknown\r\n}>()\r\n\r\nconst isNativeButton = computed(() => props.as === 'button')\r\nconst inert = computed(() => props.disabled || props.loading)\r\n\r\nconst attrs = computed(() => {\r\n if (isNativeButton.value) {\r\n return {\r\n type: props.type,\r\n // A loading button stays focusable so keyboard users don't lose their place.\r\n 'disabled': props.disabled || undefined,\r\n 'aria-disabled': props.loading || undefined,\r\n }\r\n }\r\n return {\r\n 'aria-disabled': inert.value || undefined,\r\n 'tabindex': props.disabled ? -1 : undefined,\r\n }\r\n})\r\n\r\nfunction onClick(event: MouseEvent) {\r\n if (inert.value) {\r\n event.preventDefault()\r\n event.stopImmediatePropagation()\r\n }\r\n}\r\n</script>\r\n\r\n<template>\r\n <component\r\n :is=\"as\"\r\n v-bind=\"attrs\"\r\n class=\"j-button j-focusable\"\r\n :class=\"[\r\n `j-button--${variant}`,\r\n `j-button--${size}`,\r\n { 'j-button--icon': icon, 'j-button--block': block, 'is-loading': loading },\r\n ]\"\r\n :aria-busy=\"loading || undefined\"\r\n :data-disabled=\"disabled || undefined\"\r\n @click.capture=\"onClick\"\r\n >\r\n <span class=\"j-button__content\">\r\n <span v-if=\"$slots.leading\" class=\"j-button__icon\"><slot name=\"leading\" /></span>\r\n <slot />\r\n <span v-if=\"$slots.trailing\" class=\"j-button__icon\"><slot name=\"trailing\" /></span>\r\n </span>\r\n <Transition name=\"j-button-spinner\">\r\n <span v-if=\"loading\" class=\"j-button__spinner\">\r\n <Spinner :size=\"size === 'xs' ? 12 : size === 'lg' ? 16 : 14\" />\r\n </span>\r\n </Transition>\r\n </component>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-button {\r\n --j-button-height: var(--juxt-control-md);\r\n --j-button-px: var(--juxt-space-3);\r\n --j-button-gap: var(--juxt-space-1-5);\r\n --j-button-font: var(--juxt-text-md);\r\n --j-button-radius: var(--juxt-radius-sm);\r\n\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n height: var(--j-button-height);\r\n padding: 0 var(--j-button-px);\r\n border: 1px solid transparent;\r\n border-radius: var(--j-button-radius);\r\n font-family: var(--juxt-font-sans);\r\n font-size: var(--j-button-font);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: 1;\r\n letter-spacing: var(--juxt-tracking-tight);\r\n white-space: nowrap;\r\n text-decoration: none;\r\n cursor: pointer;\r\n user-select: none;\r\n -webkit-tap-highlight-color: transparent;\r\n touch-action: manipulation;\r\n transition-property: background-color, border-color, color, box-shadow, transform, outline-color, outline-offset;\r\n transition-duration: var(--juxt-duration-fast);\r\n transition-timing-function: var(--juxt-ease-standard);\r\n}\r\n\r\n/* Pressed: a short, physical dip. Released slower than pressed. */\r\n.j-button:active:not([aria-disabled='true'], :disabled) {\r\n transform: scale(0.97);\r\n transition-duration: var(--juxt-duration-instant);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-button:active:not([aria-disabled='true'], :disabled) {\r\n transform: none;\r\n }\r\n}\r\n\r\n.j-button__content {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--j-button-gap);\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-button__icon {\r\n display: inline-flex;\r\n margin-inline: -0.125rem;\r\n opacity: 0.85;\r\n}\r\n\r\n.j-button__icon > svg {\r\n width: 1em;\r\n height: 1em;\r\n}\r\n\r\n.j-button.is-loading {\r\n cursor: progress;\r\n}\r\n\r\n.j-button.is-loading .j-button__content {\r\n opacity: 0;\r\n transform: scale(0.96);\r\n}\r\n\r\n.j-button__spinner {\r\n position: absolute;\r\n inset: 0;\r\n display: grid;\r\n place-items: center;\r\n}\r\n\r\n.j-button-spinner-enter-active {\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-out) 40ms,\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out) 40ms;\r\n}\r\n\r\n.j-button-spinner-leave-active {\r\n transition:\r\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\r\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\r\n}\r\n\r\n.j-button-spinner-enter-from,\r\n.j-button-spinner-leave-to {\r\n opacity: 0;\r\n transform: scale(0.8);\r\n}\r\n\r\n/* Sizes */\r\n.j-button--xs {\r\n --j-button-height: var(--juxt-control-xs);\r\n --j-button-px: var(--juxt-space-2);\r\n --j-button-gap: var(--juxt-space-1);\r\n --j-button-font: var(--juxt-text-xs);\r\n --j-button-radius: var(--juxt-radius-xs);\r\n}\r\n\r\n.j-button--sm {\r\n --j-button-height: var(--juxt-control-sm);\r\n --j-button-px: var(--juxt-space-2-5);\r\n --j-button-gap: var(--juxt-space-1-5);\r\n --j-button-font: var(--juxt-text-sm);\r\n}\r\n\r\n.j-button--lg {\r\n --j-button-height: var(--juxt-control-lg);\r\n --j-button-px: var(--juxt-space-4);\r\n --j-button-gap: var(--juxt-space-2);\r\n --j-button-font: var(--juxt-text-md);\r\n --j-button-radius: var(--juxt-radius-md);\r\n}\r\n\r\n.j-button--icon {\r\n width: var(--j-button-height);\r\n padding: 0;\r\n}\r\n\r\n.j-button--icon .j-button__content > svg,\r\n.j-button--icon .j-button__content > :where(span, i) > svg {\r\n width: calc(var(--j-button-height) * 0.46);\r\n height: calc(var(--j-button-height) * 0.46);\r\n}\r\n\r\n.j-button--block {\r\n display: flex;\r\n width: 100%;\r\n}\r\n\r\n/* Variants */\r\n.j-button--primary {\r\n background: var(--juxt-ink);\r\n color: var(--juxt-ink-fg);\r\n box-shadow: var(--juxt-shadow-xs), var(--juxt-control-highlight);\r\n}\r\n\r\n.j-button--primary:hover:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-ink-hover);\r\n}\r\n\r\n.j-button--secondary {\r\n background: var(--juxt-surface-sunken);\r\n border-color: var(--juxt-border-subtle);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.dark .j-button--secondary,\r\n[data-theme='dark'] .j-button--secondary {\r\n background: var(--juxt-surface-raised);\r\n border-color: var(--juxt-border-subtle);\r\n}\r\n\r\n.j-button--secondary:hover:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-surface-active);\r\n}\r\n\r\n.j-button--outline {\r\n background: var(--juxt-surface);\r\n border-color: var(--juxt-border);\r\n color: var(--juxt-fg);\r\n box-shadow: var(--juxt-shadow-xs);\r\n}\r\n\r\n.j-button--outline:hover:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-surface-hover);\r\n border-color: var(--juxt-border-strong);\r\n}\r\n\r\n.j-button--ghost {\r\n background: transparent;\r\n color: var(--juxt-fg-secondary);\r\n}\r\n\r\n.j-button--ghost:hover:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-button--ghost:active:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-surface-active);\r\n}\r\n\r\n.j-button--destructive {\r\n background: var(--juxt-danger);\r\n color: var(--juxt-danger-fg);\r\n box-shadow: var(--juxt-shadow-xs), var(--juxt-control-highlight);\r\n}\r\n\r\n.j-button--destructive:hover:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-danger-hover);\r\n}\r\n\r\n.j-button--destructive:focus-visible {\r\n outline-color: var(--juxt-danger);\r\n}\r\n\r\n/* Disabled: quiet, not ghostly. */\r\n.j-button:disabled,\r\n.j-button[data-disabled] {\r\n cursor: not-allowed;\r\n opacity: 0.45;\r\n box-shadow: none;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { Component } from 'vue'\r\nimport { computed } from 'vue'\r\nimport { Spinner } from '../../icons'\r\n\r\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive'\r\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg'\r\n\r\nexport interface ButtonProps {\r\n /** Visual weight. `primary` is for the single most important action in a view. */\r\n variant?: ButtonVariant\r\n size?: ButtonSize\r\n /** Shows a spinner, keeps the width, and blocks activation while staying focusable. */\r\n loading?: boolean\r\n disabled?: boolean\r\n /** Square button for a single icon. Provide an `aria-label`. */\r\n icon?: boolean\r\n /** Stretch to the full width of the container. */\r\n block?: boolean\r\n /** Render as another element or component, e.g. `a` or `NuxtLink`. */\r\n as?: string | Component\r\n type?: 'button' | 'submit' | 'reset'\r\n}\r\n\r\nconst props = withDefaults(defineProps<ButtonProps>(), {\r\n variant: 'primary',\r\n size: 'md',\r\n as: 'button',\r\n type: 'button',\r\n})\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n leading?: () => unknown\r\n trailing?: () => unknown\r\n}>()\r\n\r\nconst isNativeButton = computed(() => props.as === 'button')\r\nconst inert = computed(() => props.disabled || props.loading)\r\n\r\nconst attrs = computed(() => {\r\n if (isNativeButton.value) {\r\n return {\r\n type: props.type,\r\n // A loading button stays focusable so keyboard users don't lose their place.\r\n 'disabled': props.disabled || undefined,\r\n 'aria-disabled': props.loading || undefined,\r\n }\r\n }\r\n return {\r\n 'aria-disabled': inert.value || undefined,\r\n 'tabindex': props.disabled ? -1 : undefined,\r\n }\r\n})\r\n\r\nfunction onClick(event: MouseEvent) {\r\n if (inert.value) {\r\n event.preventDefault()\r\n event.stopImmediatePropagation()\r\n }\r\n}\r\n</script>\r\n\r\n<template>\r\n <component\r\n :is=\"as\"\r\n v-bind=\"attrs\"\r\n class=\"j-button j-focusable\"\r\n :class=\"[\r\n `j-button--${variant}`,\r\n `j-button--${size}`,\r\n { 'j-button--icon': icon, 'j-button--block': block, 'is-loading': loading },\r\n ]\"\r\n :aria-busy=\"loading || undefined\"\r\n :data-disabled=\"disabled || undefined\"\r\n @click.capture=\"onClick\"\r\n >\r\n <span class=\"j-button__content\">\r\n <span v-if=\"$slots.leading\" class=\"j-button__icon\"><slot name=\"leading\" /></span>\r\n <slot />\r\n <span v-if=\"$slots.trailing\" class=\"j-button__icon\"><slot name=\"trailing\" /></span>\r\n </span>\r\n <Transition name=\"j-button-spinner\">\r\n <span v-if=\"loading\" class=\"j-button__spinner\">\r\n <Spinner :size=\"size === 'xs' ? 12 : size === 'lg' ? 16 : 14\" />\r\n </span>\r\n </Transition>\r\n </component>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-button {\r\n --j-button-height: var(--juxt-control-md);\r\n --j-button-px: var(--juxt-space-3);\r\n --j-button-gap: var(--juxt-space-1-5);\r\n --j-button-font: var(--juxt-text-md);\r\n --j-button-radius: var(--juxt-radius-sm);\r\n\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n height: var(--j-button-height);\r\n padding: 0 var(--j-button-px);\r\n border: 1px solid transparent;\r\n border-radius: var(--j-button-radius);\r\n font-family: var(--juxt-font-sans);\r\n font-size: var(--j-button-font);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: 1;\r\n letter-spacing: var(--juxt-tracking-tight);\r\n white-space: nowrap;\r\n text-decoration: none;\r\n cursor: pointer;\r\n user-select: none;\r\n -webkit-tap-highlight-color: transparent;\r\n touch-action: manipulation;\r\n transition-property: background-color, border-color, color, box-shadow, transform, outline-color, outline-offset;\r\n transition-duration: var(--juxt-duration-fast);\r\n transition-timing-function: var(--juxt-ease-standard);\r\n}\r\n\r\n/* Pressed: a short, physical dip. Released slower than pressed. */\r\n.j-button:active:not([aria-disabled='true'], :disabled) {\r\n transform: scale(0.97);\r\n transition-duration: var(--juxt-duration-instant);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-button:active:not([aria-disabled='true'], :disabled) {\r\n transform: none;\r\n }\r\n}\r\n\r\n.j-button__content {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--j-button-gap);\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-button__icon {\r\n display: inline-flex;\r\n margin-inline: -0.125rem;\r\n opacity: 0.85;\r\n}\r\n\r\n.j-button__icon > svg {\r\n width: 1em;\r\n height: 1em;\r\n}\r\n\r\n.j-button.is-loading {\r\n cursor: progress;\r\n}\r\n\r\n.j-button.is-loading .j-button__content {\r\n opacity: 0;\r\n transform: scale(0.96);\r\n}\r\n\r\n.j-button__spinner {\r\n position: absolute;\r\n inset: 0;\r\n display: grid;\r\n place-items: center;\r\n}\r\n\r\n.j-button-spinner-enter-active {\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-out) 40ms,\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out) 40ms;\r\n}\r\n\r\n.j-button-spinner-leave-active {\r\n transition:\r\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\r\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\r\n}\r\n\r\n.j-button-spinner-enter-from,\r\n.j-button-spinner-leave-to {\r\n opacity: 0;\r\n transform: scale(0.8);\r\n}\r\n\r\n/* Sizes */\r\n.j-button--xs {\r\n --j-button-height: var(--juxt-control-xs);\r\n --j-button-px: var(--juxt-space-2);\r\n --j-button-gap: var(--juxt-space-1);\r\n --j-button-font: var(--juxt-text-xs);\r\n --j-button-radius: var(--juxt-radius-xs);\r\n}\r\n\r\n.j-button--sm {\r\n --j-button-height: var(--juxt-control-sm);\r\n --j-button-px: var(--juxt-space-2-5);\r\n --j-button-gap: var(--juxt-space-1-5);\r\n --j-button-font: var(--juxt-text-sm);\r\n}\r\n\r\n.j-button--lg {\r\n --j-button-height: var(--juxt-control-lg);\r\n --j-button-px: var(--juxt-space-4);\r\n --j-button-gap: var(--juxt-space-2);\r\n --j-button-font: var(--juxt-text-md);\r\n --j-button-radius: var(--juxt-radius-md);\r\n}\r\n\r\n.j-button--icon {\r\n width: var(--j-button-height);\r\n padding: 0;\r\n}\r\n\r\n.j-button--icon .j-button__content > svg,\r\n.j-button--icon .j-button__content > :where(span, i) > svg {\r\n width: calc(var(--j-button-height) * 0.46);\r\n height: calc(var(--j-button-height) * 0.46);\r\n}\r\n\r\n.j-button--block {\r\n display: flex;\r\n width: 100%;\r\n}\r\n\r\n/* Variants */\r\n.j-button--primary {\r\n background: var(--juxt-ink);\r\n color: var(--juxt-ink-fg);\r\n box-shadow: var(--juxt-shadow-xs), var(--juxt-control-highlight);\r\n}\r\n\r\n.j-button--primary:hover:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-ink-hover);\r\n}\r\n\r\n.j-button--secondary {\r\n background: var(--juxt-surface-sunken);\r\n border-color: var(--juxt-border-subtle);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.dark .j-button--secondary,\r\n[data-theme='dark'] .j-button--secondary {\r\n background: var(--juxt-surface-raised);\r\n border-color: var(--juxt-border-subtle);\r\n}\r\n\r\n.j-button--secondary:hover:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-surface-active);\r\n}\r\n\r\n.j-button--outline {\r\n background: var(--juxt-surface);\r\n border-color: var(--juxt-border);\r\n color: var(--juxt-fg);\r\n box-shadow: var(--juxt-shadow-xs);\r\n}\r\n\r\n.j-button--outline:hover:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-surface-hover);\r\n border-color: var(--juxt-border-strong);\r\n}\r\n\r\n.j-button--ghost {\r\n background: transparent;\r\n color: var(--juxt-fg-secondary);\r\n}\r\n\r\n.j-button--ghost:hover:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-button--ghost:active:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-surface-active);\r\n}\r\n\r\n.j-button--destructive {\r\n background: var(--juxt-danger);\r\n color: var(--juxt-danger-fg);\r\n box-shadow: var(--juxt-shadow-xs), var(--juxt-control-highlight);\r\n}\r\n\r\n.j-button--destructive:hover:not([aria-disabled='true'], :disabled) {\r\n background: var(--juxt-danger-hover);\r\n}\r\n\r\n.j-button--destructive:focus-visible {\r\n outline-color: var(--juxt-danger);\r\n}\r\n\r\n/* Disabled: quiet, not ghostly. */\r\n.j-button:disabled,\r\n.j-button[data-disabled] {\r\n cursor: not-allowed;\r\n opacity: 0.45;\r\n box-shadow: none;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { Component } from 'vue'\r\n\r\nexport interface CardProps {\r\n title?: string\r\n description?: string\r\n /** `outline` sits on the page; `muted` recedes into it. */\r\n variant?: 'outline' | 'muted'\r\n padding?: 'none' | 'sm' | 'md' | 'lg'\r\n as?: string | Component\r\n}\r\n\r\nwithDefaults(defineProps<CardProps>(), { variant: 'outline', padding: 'md', as: 'div' })\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n header?: () => unknown\r\n /** Sits to the right of the title | a button, badge or menu. */\r\n actions?: () => unknown\r\n footer?: () => unknown\r\n}>()\r\n</script>\r\n\r\n<template>\r\n <component :is=\"as\" class=\"j-card\" :class=\"[`j-card--${variant}`, `j-card--p-${padding}`]\">\r\n <header v-if=\"title || description || $slots.header || $slots.actions\" class=\"j-card__header\">\r\n <slot name=\"header\">\r\n <div class=\"j-card__heading\">\r\n <h3 v-if=\"title\" class=\"j-card__title\">\r\n {{ title }}\r\n </h3>\r\n <p v-if=\"description\" class=\"j-card__description\">\r\n {{ description }}\r\n </p>\r\n </div>\r\n </slot>\r\n <div v-if=\"$slots.actions\" class=\"j-card__actions\">\r\n <slot name=\"actions\" />\r\n </div>\r\n </header>\r\n <div v-if=\"$slots.default\" class=\"j-card__body\">\r\n <slot />\r\n </div>\r\n <footer v-if=\"$slots.footer\" class=\"j-card__footer\">\r\n <slot name=\"footer\" />\r\n </footer>\r\n </component>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-card {\r\n --j-card-p: var(--juxt-space-5);\r\n\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0;\r\n border-radius: var(--juxt-radius-lg);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-card--outline {\r\n --j-avatar-ring: var(--juxt-surface);\r\n\r\n border: 1px solid var(--juxt-border);\r\n background: var(--juxt-surface);\r\n box-shadow: var(--juxt-shadow-xs);\r\n}\r\n\r\n.j-card--muted {\r\n --j-avatar-ring: var(--juxt-surface-sunken);\r\n\r\n border: 1px solid transparent;\r\n background: var(--juxt-surface-sunken);\r\n}\r\n\r\n.j-card--p-none {\r\n --j-card-p: 0;\r\n}\r\n\r\n.j-card--p-sm {\r\n --j-card-p: var(--juxt-space-3);\r\n}\r\n\r\n.j-card--p-lg {\r\n --j-card-p: var(--juxt-space-6);\r\n}\r\n\r\n.j-card__header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: var(--juxt-space-4);\r\n padding: var(--j-card-p) var(--j-card-p) 0;\r\n}\r\n\r\n.j-card__heading {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-1);\r\n min-width: 0;\r\n}\r\n\r\n.j-card__title {\r\n margin: 0;\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-semibold);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-card__description {\r\n margin: 0;\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-card__actions {\r\n display: flex;\r\n flex: none;\r\n align-items: center;\r\n gap: var(--juxt-space-2);\r\n margin: calc(var(--juxt-space-1) * -1) 0;\r\n}\r\n\r\n.j-card__body {\r\n flex: 1;\r\n padding: var(--j-card-p);\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-card__header + .j-card__body {\r\n padding-top: var(--juxt-space-4);\r\n}\r\n\r\n.j-card__footer {\r\n display: flex;\r\n align-items: center;\r\n justify-content: flex-end;\r\n gap: var(--juxt-space-2);\r\n padding: var(--juxt-space-3) var(--j-card-p);\r\n border-top: 1px solid var(--juxt-border-subtle);\r\n}\r\n\r\n.j-card__header:last-child {\r\n padding-bottom: var(--j-card-p);\r\n}\r\n\r\n.j-card--muted .j-card__footer {\r\n border-top-color: var(--juxt-border);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { Component } from 'vue'\r\n\r\nexport interface CardProps {\r\n title?: string\r\n description?: string\r\n /** `outline` sits on the page; `muted` recedes into it. */\r\n variant?: 'outline' | 'muted'\r\n padding?: 'none' | 'sm' | 'md' | 'lg'\r\n as?: string | Component\r\n}\r\n\r\nwithDefaults(defineProps<CardProps>(), { variant: 'outline', padding: 'md', as: 'div' })\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n header?: () => unknown\r\n /** Sits to the right of the title | a button, badge or menu. */\r\n actions?: () => unknown\r\n footer?: () => unknown\r\n}>()\r\n</script>\r\n\r\n<template>\r\n <component :is=\"as\" class=\"j-card\" :class=\"[`j-card--${variant}`, `j-card--p-${padding}`]\">\r\n <header v-if=\"title || description || $slots.header || $slots.actions\" class=\"j-card__header\">\r\n <slot name=\"header\">\r\n <div class=\"j-card__heading\">\r\n <h3 v-if=\"title\" class=\"j-card__title\">\r\n {{ title }}\r\n </h3>\r\n <p v-if=\"description\" class=\"j-card__description\">\r\n {{ description }}\r\n </p>\r\n </div>\r\n </slot>\r\n <div v-if=\"$slots.actions\" class=\"j-card__actions\">\r\n <slot name=\"actions\" />\r\n </div>\r\n </header>\r\n <div v-if=\"$slots.default\" class=\"j-card__body\">\r\n <slot />\r\n </div>\r\n <footer v-if=\"$slots.footer\" class=\"j-card__footer\">\r\n <slot name=\"footer\" />\r\n </footer>\r\n </component>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-card {\r\n --j-card-p: var(--juxt-space-5);\r\n\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0;\r\n border-radius: var(--juxt-radius-lg);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-card--outline {\r\n --j-avatar-ring: var(--juxt-surface);\r\n\r\n border: 1px solid var(--juxt-border);\r\n background: var(--juxt-surface);\r\n box-shadow: var(--juxt-shadow-xs);\r\n}\r\n\r\n.j-card--muted {\r\n --j-avatar-ring: var(--juxt-surface-sunken);\r\n\r\n border: 1px solid transparent;\r\n background: var(--juxt-surface-sunken);\r\n}\r\n\r\n.j-card--p-none {\r\n --j-card-p: 0;\r\n}\r\n\r\n.j-card--p-sm {\r\n --j-card-p: var(--juxt-space-3);\r\n}\r\n\r\n.j-card--p-lg {\r\n --j-card-p: var(--juxt-space-6);\r\n}\r\n\r\n.j-card__header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: var(--juxt-space-4);\r\n padding: var(--j-card-p) var(--j-card-p) 0;\r\n}\r\n\r\n.j-card__heading {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-1);\r\n min-width: 0;\r\n}\r\n\r\n.j-card__title {\r\n margin: 0;\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-semibold);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-card__description {\r\n margin: 0;\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-card__actions {\r\n display: flex;\r\n flex: none;\r\n align-items: center;\r\n gap: var(--juxt-space-2);\r\n margin: calc(var(--juxt-space-1) * -1) 0;\r\n}\r\n\r\n.j-card__body {\r\n flex: 1;\r\n padding: var(--j-card-p);\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-card__header + .j-card__body {\r\n padding-top: var(--juxt-space-4);\r\n}\r\n\r\n.j-card__footer {\r\n display: flex;\r\n align-items: center;\r\n justify-content: flex-end;\r\n gap: var(--juxt-space-2);\r\n padding: var(--juxt-space-3) var(--j-card-p);\r\n border-top: 1px solid var(--juxt-border-subtle);\r\n}\r\n\r\n.j-card__header:last-child {\r\n padding-bottom: var(--j-card-p);\r\n}\r\n\r\n.j-card--muted .j-card__footer {\r\n border-top-color: var(--juxt-border);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, ref, useId, watchEffect } from 'vue'\r\n\r\nexport interface CheckboxProps {\r\n label?: string\r\n description?: string\r\n disabled?: boolean\r\n required?: boolean\r\n /** Shows a dash. Typically used for \"select all\" when only some items are selected. */\r\n indeterminate?: boolean\r\n name?: string\r\n value?: string\r\n id?: string\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = defineProps<CheckboxProps>()\r\nconst model = defineModel<boolean>({ default: false })\r\n\r\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\r\n\r\nconst generated = useId()\r\nconst id = computed(() => props.id ?? `j-${generated}`)\r\nconst descriptionId = computed(() => `${id.value}-description`)\r\nconst input = ref<HTMLInputElement | null>(null)\r\n\r\n// `indeterminate` is a DOM property, not an attribute.\r\nwatchEffect(() => {\r\n if (input.value) input.value.indeterminate = !!props.indeterminate\r\n})\r\n\r\nconst state = computed(() => (props.indeterminate ? 'indeterminate' : model.value ? 'checked' : 'unchecked'))\r\n\r\nfunction onChange(event: Event) {\r\n model.value = (event.target as HTMLInputElement).checked\r\n}\r\n\r\ndefineExpose({ input, focus: () => input.value?.focus() })\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-checkbox\"\r\n :class=\"[$attrs.class, { 'is-disabled': disabled }]\"\r\n :style=\"$attrs.style as any\"\r\n :data-state=\"state\"\r\n >\r\n <span class=\"j-checkbox__control\">\r\n <input\r\n :id=\"id\"\r\n ref=\"input\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n type=\"checkbox\"\r\n class=\"j-checkbox__input\"\r\n :checked=\"model\"\r\n :disabled=\"disabled\"\r\n :required=\"required\"\r\n :name=\"name\"\r\n :value=\"value\"\r\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\r\n @change=\"onChange\"\r\n />\r\n <span class=\"j-checkbox__box\" aria-hidden=\"true\">\r\n <svg class=\"j-checkbox__check\" viewBox=\"0 0 16 16\" fill=\"none\">\r\n <path d=\"M4 8.25l2.75 2.75L12 5.5\" pathLength=\"1\" />\r\n </svg>\r\n <svg class=\"j-checkbox__dash\" viewBox=\"0 0 16 16\" fill=\"none\">\r\n <path d=\"M4.5 8h7\" />\r\n </svg>\r\n </span>\r\n </span>\r\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-checkbox__text\">\r\n <label v-if=\"label || $slots.default\" :for=\"id\" class=\"j-checkbox__label\">\r\n <slot>{{ label }}</slot>\r\n </label>\r\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-checkbox__description\">\r\n <slot name=\"description\">{{ description }}</slot>\r\n </span>\r\n </span>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-checkbox {\r\n --j-checkbox-size: 1rem;\r\n\r\n display: inline-flex;\r\n align-items: flex-start;\r\n gap: var(--juxt-space-2-5);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-checkbox__control {\r\n position: relative;\r\n display: inline-flex;\r\n flex: none;\r\n /* Align the box with the first line of the label. */\r\n margin-top: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-checkbox-size)) / 2);\r\n}\r\n\r\n.j-checkbox__input {\r\n position: absolute;\r\n inset: -4px;\r\n z-index: 1;\r\n width: calc(100% + 8px);\r\n height: calc(100% + 8px);\r\n margin: 0;\r\n opacity: 0;\r\n cursor: pointer;\r\n}\r\n\r\n.j-checkbox__input:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-checkbox__box {\r\n display: grid;\r\n place-items: center;\r\n width: var(--j-checkbox-size);\r\n height: var(--j-checkbox-size);\r\n border: 1px solid var(--juxt-border-strong);\r\n border-radius: var(--juxt-radius-xs);\r\n background: var(--juxt-surface);\r\n box-shadow: var(--juxt-shadow-xs);\r\n color: var(--juxt-accent-fg);\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n transition:\r\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-checkbox__box > svg {\r\n grid-area: 1 / 1;\r\n width: 100%;\r\n height: 100%;\r\n stroke: currentColor;\r\n stroke-width: 2;\r\n stroke-linecap: round;\r\n stroke-linejoin: round;\r\n}\r\n\r\n.j-checkbox__input:hover:not(:disabled) + .j-checkbox__box {\r\n border-color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-checkbox__input:active:not(:disabled) + .j-checkbox__box {\r\n transform: scale(0.9);\r\n}\r\n\r\n.j-checkbox__input:focus-visible + .j-checkbox__box {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.j-checkbox[data-state='checked'] .j-checkbox__box,\r\n.j-checkbox[data-state='indeterminate'] .j-checkbox__box {\r\n border-color: var(--juxt-accent);\r\n background: var(--juxt-accent);\r\n}\r\n\r\n.j-checkbox[data-state='checked'] .j-checkbox__input:hover:not(:disabled) + .j-checkbox__box,\r\n.j-checkbox[data-state='indeterminate'] .j-checkbox__input:hover:not(:disabled) + .j-checkbox__box {\r\n border-color: var(--juxt-accent-hover);\r\n background: var(--juxt-accent-hover);\r\n}\r\n\r\n/* The check draws itself in, just after the fill lands. */\r\n.j-checkbox__check path {\r\n stroke-dasharray: 1;\r\n stroke-dashoffset: 1;\r\n transition: stroke-dashoffset var(--juxt-duration-fast) var(--juxt-ease-in);\r\n}\r\n\r\n.j-checkbox[data-state='checked'] .j-checkbox__check path {\r\n stroke-dashoffset: 0;\r\n transition: stroke-dashoffset var(--juxt-duration-normal) var(--juxt-ease-out) 40ms;\r\n}\r\n\r\n.j-checkbox__dash {\r\n opacity: 0;\r\n transform: scaleX(0.4);\r\n transition:\r\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-checkbox[data-state='indeterminate'] .j-checkbox__dash {\r\n opacity: 1;\r\n transform: none;\r\n}\r\n\r\n.j-checkbox[data-state='indeterminate'] .j-checkbox__check {\r\n opacity: 0;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-checkbox__check path,\r\n .j-checkbox[data-state='checked'] .j-checkbox__check path {\r\n transition-duration: 0ms;\r\n }\r\n\r\n .j-checkbox__input:active:not(:disabled) + .j-checkbox__box {\r\n transform: none;\r\n }\r\n}\r\n\r\n.j-checkbox__text {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-0-5);\r\n min-width: 0;\r\n}\r\n\r\n.j-checkbox__label {\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.j-checkbox__description {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-checkbox.is-disabled .j-checkbox__box {\r\n opacity: 0.5;\r\n box-shadow: none;\r\n}\r\n\r\n.j-checkbox.is-disabled .j-checkbox__label {\r\n color: var(--juxt-fg-muted);\r\n cursor: not-allowed;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, ref, useId, watchEffect } from 'vue'\r\n\r\nexport interface CheckboxProps {\r\n label?: string\r\n description?: string\r\n disabled?: boolean\r\n required?: boolean\r\n /** Shows a dash. Typically used for \"select all\" when only some items are selected. */\r\n indeterminate?: boolean\r\n name?: string\r\n value?: string\r\n id?: string\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = defineProps<CheckboxProps>()\r\nconst model = defineModel<boolean>({ default: false })\r\n\r\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\r\n\r\nconst generated = useId()\r\nconst id = computed(() => props.id ?? `j-${generated}`)\r\nconst descriptionId = computed(() => `${id.value}-description`)\r\nconst input = ref<HTMLInputElement | null>(null)\r\n\r\n// `indeterminate` is a DOM property, not an attribute.\r\nwatchEffect(() => {\r\n if (input.value) input.value.indeterminate = !!props.indeterminate\r\n})\r\n\r\nconst state = computed(() => (props.indeterminate ? 'indeterminate' : model.value ? 'checked' : 'unchecked'))\r\n\r\nfunction onChange(event: Event) {\r\n model.value = (event.target as HTMLInputElement).checked\r\n}\r\n\r\ndefineExpose({ input, focus: () => input.value?.focus() })\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-checkbox\"\r\n :class=\"[$attrs.class, { 'is-disabled': disabled }]\"\r\n :style=\"$attrs.style as any\"\r\n :data-state=\"state\"\r\n >\r\n <span class=\"j-checkbox__control\">\r\n <input\r\n :id=\"id\"\r\n ref=\"input\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n type=\"checkbox\"\r\n class=\"j-checkbox__input\"\r\n :checked=\"model\"\r\n :disabled=\"disabled\"\r\n :required=\"required\"\r\n :name=\"name\"\r\n :value=\"value\"\r\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\r\n @change=\"onChange\"\r\n />\r\n <span class=\"j-checkbox__box\" aria-hidden=\"true\">\r\n <svg class=\"j-checkbox__check\" viewBox=\"0 0 16 16\" fill=\"none\">\r\n <path d=\"M4 8.25l2.75 2.75L12 5.5\" pathLength=\"1\" />\r\n </svg>\r\n <svg class=\"j-checkbox__dash\" viewBox=\"0 0 16 16\" fill=\"none\">\r\n <path d=\"M4.5 8h7\" />\r\n </svg>\r\n </span>\r\n </span>\r\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-checkbox__text\">\r\n <label v-if=\"label || $slots.default\" :for=\"id\" class=\"j-checkbox__label\">\r\n <slot>{{ label }}</slot>\r\n </label>\r\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-checkbox__description\">\r\n <slot name=\"description\">{{ description }}</slot>\r\n </span>\r\n </span>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-checkbox {\r\n --j-checkbox-size: 1rem;\r\n\r\n display: inline-flex;\r\n align-items: flex-start;\r\n gap: var(--juxt-space-2-5);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-checkbox__control {\r\n position: relative;\r\n display: inline-flex;\r\n flex: none;\r\n /* Align the box with the first line of the label. */\r\n margin-top: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-checkbox-size)) / 2);\r\n}\r\n\r\n.j-checkbox__input {\r\n position: absolute;\r\n inset: -4px;\r\n z-index: 1;\r\n width: calc(100% + 8px);\r\n height: calc(100% + 8px);\r\n margin: 0;\r\n opacity: 0;\r\n cursor: pointer;\r\n}\r\n\r\n.j-checkbox__input:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-checkbox__box {\r\n display: grid;\r\n place-items: center;\r\n width: var(--j-checkbox-size);\r\n height: var(--j-checkbox-size);\r\n border: 1px solid var(--juxt-border-strong);\r\n border-radius: var(--juxt-radius-xs);\r\n background: var(--juxt-surface);\r\n box-shadow: var(--juxt-shadow-xs);\r\n color: var(--juxt-accent-fg);\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n transition:\r\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-checkbox__box > svg {\r\n grid-area: 1 / 1;\r\n width: 100%;\r\n height: 100%;\r\n stroke: currentColor;\r\n stroke-width: 2;\r\n stroke-linecap: round;\r\n stroke-linejoin: round;\r\n}\r\n\r\n.j-checkbox__input:hover:not(:disabled) + .j-checkbox__box {\r\n border-color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-checkbox__input:active:not(:disabled) + .j-checkbox__box {\r\n transform: scale(0.9);\r\n}\r\n\r\n.j-checkbox__input:focus-visible + .j-checkbox__box {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.j-checkbox[data-state='checked'] .j-checkbox__box,\r\n.j-checkbox[data-state='indeterminate'] .j-checkbox__box {\r\n border-color: var(--juxt-accent);\r\n background: var(--juxt-accent);\r\n}\r\n\r\n.j-checkbox[data-state='checked'] .j-checkbox__input:hover:not(:disabled) + .j-checkbox__box,\r\n.j-checkbox[data-state='indeterminate'] .j-checkbox__input:hover:not(:disabled) + .j-checkbox__box {\r\n border-color: var(--juxt-accent-hover);\r\n background: var(--juxt-accent-hover);\r\n}\r\n\r\n/* The check draws itself in, just after the fill lands. */\r\n.j-checkbox__check path {\r\n stroke-dasharray: 1;\r\n stroke-dashoffset: 1;\r\n transition: stroke-dashoffset var(--juxt-duration-fast) var(--juxt-ease-in);\r\n}\r\n\r\n.j-checkbox[data-state='checked'] .j-checkbox__check path {\r\n stroke-dashoffset: 0;\r\n transition: stroke-dashoffset var(--juxt-duration-normal) var(--juxt-ease-out) 40ms;\r\n}\r\n\r\n.j-checkbox__dash {\r\n opacity: 0;\r\n transform: scaleX(0.4);\r\n transition:\r\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-checkbox[data-state='indeterminate'] .j-checkbox__dash {\r\n opacity: 1;\r\n transform: none;\r\n}\r\n\r\n.j-checkbox[data-state='indeterminate'] .j-checkbox__check {\r\n opacity: 0;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-checkbox__check path,\r\n .j-checkbox[data-state='checked'] .j-checkbox__check path {\r\n transition-duration: 0ms;\r\n }\r\n\r\n .j-checkbox__input:active:not(:disabled) + .j-checkbox__box {\r\n transform: none;\r\n }\r\n}\r\n\r\n.j-checkbox__text {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-0-5);\r\n min-width: 0;\r\n}\r\n\r\n.j-checkbox__label {\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.j-checkbox__description {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-checkbox.is-disabled .j-checkbox__box {\r\n opacity: 0.5;\r\n box-shadow: none;\r\n}\r\n\r\n.j-checkbox.is-disabled .j-checkbox__label {\r\n color: var(--juxt-fg-muted);\r\n cursor: not-allowed;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { useId } from 'vue'\r\nimport { IconChevronDown } from '../../icons'\r\n\r\nexport interface CollapsibleProps {\r\n /** Visible summary text; use the `trigger` slot for custom content instead. */\r\n label?: string\r\n disabled?: boolean\r\n}\r\n\r\ndefineProps<CollapsibleProps>()\r\n\r\n/** A single disclosure primitive | for a group of them, use `JAccordion`. */\r\nconst model = defineModel<boolean>({ default: false })\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n trigger?: () => unknown\r\n}>()\r\n\r\nconst id = `j-${useId()}`\r\nconst triggerId = `${id}-trigger`\r\nconst panelId = `${id}-panel`\r\n\r\nfunction toggle() {\r\n model.value = !model.value\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-collapsible\" :data-state=\"model ? 'open' : 'closed'\">\r\n <button\r\n :id=\"triggerId\"\r\n type=\"button\"\r\n class=\"j-collapsible__trigger j-focusable\"\r\n :aria-expanded=\"model\"\r\n :aria-controls=\"panelId\"\r\n :disabled=\"disabled\"\r\n @click=\"toggle\"\r\n >\r\n <IconChevronDown class=\"j-collapsible__chevron\" />\r\n <span class=\"j-collapsible__label\"><slot name=\"trigger\">{{ label }}</slot></span>\r\n </button>\r\n <div\r\n :id=\"panelId\"\r\n role=\"region\"\r\n class=\"j-collapsible__panel\"\r\n :aria-labelledby=\"triggerId\"\r\n :inert=\"!model\"\r\n >\r\n <div class=\"j-collapsible__panel-inner\">\r\n <div class=\"j-collapsible__content\">\r\n <slot />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-collapsible {\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-collapsible__trigger {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1-5);\r\n padding: var(--juxt-space-1) 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-xs);\r\n background: transparent;\r\n color: var(--juxt-fg);\r\n font: inherit;\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n outline: 2px solid transparent;\r\n outline-offset: 2px;\r\n -webkit-tap-highlight-color: transparent;\r\n transition:\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-collapsible__trigger:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-collapsible__chevron {\r\n flex: none;\r\n width: 1rem;\r\n height: 1rem;\r\n color: var(--juxt-fg-muted);\r\n transition: transform var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-collapsible[data-state='open'] .j-collapsible__chevron {\r\n transform: rotate(180deg);\r\n}\r\n\r\n.j-collapsible__panel {\r\n display: grid;\r\n grid-template-rows: 0fr;\r\n transition: grid-template-rows var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n.j-collapsible[data-state='open'] .j-collapsible__panel {\r\n grid-template-rows: 1fr;\r\n}\r\n\r\n.j-collapsible__panel-inner {\r\n min-height: 0;\r\n overflow: hidden;\r\n}\r\n\r\n.j-collapsible__content {\r\n padding-top: var(--juxt-space-2);\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n opacity: 0;\r\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-collapsible[data-state='open'] .j-collapsible__content {\r\n opacity: 1;\r\n transition-delay: 60ms;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-collapsible__panel,\r\n .j-collapsible__chevron {\r\n transition: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { useId } from 'vue'\r\nimport { IconChevronDown } from '../../icons'\r\n\r\nexport interface CollapsibleProps {\r\n /** Visible summary text; use the `trigger` slot for custom content instead. */\r\n label?: string\r\n disabled?: boolean\r\n}\r\n\r\ndefineProps<CollapsibleProps>()\r\n\r\n/** A single disclosure primitive | for a group of them, use `JAccordion`. */\r\nconst model = defineModel<boolean>({ default: false })\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n trigger?: () => unknown\r\n}>()\r\n\r\nconst id = `j-${useId()}`\r\nconst triggerId = `${id}-trigger`\r\nconst panelId = `${id}-panel`\r\n\r\nfunction toggle() {\r\n model.value = !model.value\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-collapsible\" :data-state=\"model ? 'open' : 'closed'\">\r\n <button\r\n :id=\"triggerId\"\r\n type=\"button\"\r\n class=\"j-collapsible__trigger j-focusable\"\r\n :aria-expanded=\"model\"\r\n :aria-controls=\"panelId\"\r\n :disabled=\"disabled\"\r\n @click=\"toggle\"\r\n >\r\n <IconChevronDown class=\"j-collapsible__chevron\" />\r\n <span class=\"j-collapsible__label\"><slot name=\"trigger\">{{ label }}</slot></span>\r\n </button>\r\n <div\r\n :id=\"panelId\"\r\n role=\"region\"\r\n class=\"j-collapsible__panel\"\r\n :aria-labelledby=\"triggerId\"\r\n :inert=\"!model\"\r\n >\r\n <div class=\"j-collapsible__panel-inner\">\r\n <div class=\"j-collapsible__content\">\r\n <slot />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-collapsible {\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-collapsible__trigger {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1-5);\r\n padding: var(--juxt-space-1) 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-xs);\r\n background: transparent;\r\n color: var(--juxt-fg);\r\n font: inherit;\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n outline: 2px solid transparent;\r\n outline-offset: 2px;\r\n -webkit-tap-highlight-color: transparent;\r\n transition:\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-collapsible__trigger:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-collapsible__chevron {\r\n flex: none;\r\n width: 1rem;\r\n height: 1rem;\r\n color: var(--juxt-fg-muted);\r\n transition: transform var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-collapsible[data-state='open'] .j-collapsible__chevron {\r\n transform: rotate(180deg);\r\n}\r\n\r\n.j-collapsible__panel {\r\n display: grid;\r\n grid-template-rows: 0fr;\r\n transition: grid-template-rows var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n.j-collapsible[data-state='open'] .j-collapsible__panel {\r\n grid-template-rows: 1fr;\r\n}\r\n\r\n.j-collapsible__panel-inner {\r\n min-height: 0;\r\n overflow: hidden;\r\n}\r\n\r\n.j-collapsible__content {\r\n padding-top: var(--juxt-space-2);\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n opacity: 0;\r\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-collapsible[data-state='open'] .j-collapsible__content {\r\n opacity: 1;\r\n transition-delay: 60ms;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-collapsible__panel,\r\n .j-collapsible__chevron {\r\n transition: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","import type { Ref } from 'vue'\r\nimport { nextTick, onBeforeUnmount, watch } from 'vue'\r\nimport { focusElement, getFocusableElements, isClient } from '../utils/dom'\r\n\r\nexport interface FocusTrapOptions {\r\n /** Element to focus when the trap activates. Falls back to the first focusable element, then the container. */\r\n initialFocus?: () => HTMLElement | null | undefined\r\n /** Return focus to whatever was focused before activation. Defaults to true. */\r\n restoreFocus?: boolean\r\n}\r\n\r\ninterface Trap {\r\n container: Ref<HTMLElement | null>\r\n}\r\n\r\n// Traps nest: only the most recently activated one is in charge.\r\nconst stack: Trap[] = []\r\n\r\nfunction isTop(trap: Trap) {\r\n return stack[stack.length - 1] === trap\r\n}\r\n\r\nlet listening = false\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (event.key !== 'Tab' || event.defaultPrevented) return\r\n const trap = stack[stack.length - 1]\r\n const container = trap?.container.value\r\n if (!container) return\r\n\r\n const focusable = getFocusableElements(container)\r\n if (focusable.length === 0) {\r\n event.preventDefault()\r\n focusElement(container)\r\n return\r\n }\r\n\r\n const first = focusable[0]!\r\n const last = focusable[focusable.length - 1]!\r\n const active = document.activeElement as HTMLElement | null\r\n const inside = active ? container.contains(active) : false\r\n\r\n if (event.shiftKey && (active === first || active === container || !inside)) {\r\n event.preventDefault()\r\n focusElement(last)\r\n } else if (!event.shiftKey && (active === last || !inside)) {\r\n event.preventDefault()\r\n focusElement(first)\r\n }\r\n}\r\n\r\nfunction onFocusIn(event: FocusEvent) {\r\n const trap = stack[stack.length - 1]\r\n const container = trap?.container.value\r\n const target = event.target as Node | null\r\n if (!container || !target || container.contains(target)) return\r\n // Focus escaped (e.g. a programmatic focus elsewhere) | bring it back.\r\n if ((target as HTMLElement).closest?.('[data-juxt-layer-ignore]')) return\r\n const [first] = getFocusableElements(container)\r\n focusElement(first ?? container)\r\n}\r\n\r\nfunction listen() {\r\n if (listening || !isClient) return\r\n document.addEventListener('keydown', onKeydown)\r\n document.addEventListener('focusin', onFocusIn)\r\n listening = true\r\n}\r\n\r\nfunction unlisten() {\r\n if (!listening || stack.length > 0) return\r\n document.removeEventListener('keydown', onKeydown)\r\n document.removeEventListener('focusin', onFocusIn)\r\n listening = false\r\n}\r\n\r\n/**\r\n * Keep keyboard focus inside `container` while `active` is true, and put it\r\n * back where it came from afterwards.\r\n */\r\nexport function useFocusTrap(container: Ref<HTMLElement | null>, active: Ref<boolean>, options: FocusTrapOptions = {}) {\r\n const trap: Trap = { container }\r\n let returnTo: HTMLElement | null = null\r\n // The container as it was while active. By the time we deactivate, Vue may\r\n // already have cleared the template ref while the element animates out.\r\n let activeContainer: HTMLElement | null = null\r\n\r\n async function activate() {\r\n returnTo = document.activeElement as HTMLElement | null\r\n stack.push(trap)\r\n listen()\r\n await nextTick()\r\n const el = container.value\r\n activeContainer = el\r\n if (!el || !isTop(trap)) return\r\n if (el.contains(document.activeElement)) return\r\n const target = options.initialFocus?.() ?? el.querySelector<HTMLElement>('[autofocus], [data-autofocus]') ?? getFocusableElements(el)[0] ?? el\r\n focusElement(target)\r\n }\r\n\r\n function deactivate() {\r\n const index = stack.indexOf(trap)\r\n if (index === -1) return\r\n stack.splice(index, 1)\r\n unlisten()\r\n\r\n if (options.restoreFocus === false) return\r\n const target = returnTo\r\n returnTo = null\r\n if (!target || !target.isConnected) return\r\n const current = document.activeElement as HTMLElement | null\r\n // Only restore when focus would otherwise be lost. If the user deliberately\r\n // moved focus somewhere meaningful, respect that.\r\n const lost = !current || current === document.body || !current.isConnected\r\n || !!activeContainer?.contains(current) || !!container.value?.contains(current)\r\n activeContainer = null\r\n if (lost) focusElement(target)\r\n }\r\n\r\n if (isClient) {\r\n watch(\r\n active,\r\n (value) => {\r\n if (value) activate()\r\n else deactivate()\r\n },\r\n { immediate: true, flush: 'post' },\r\n )\r\n onBeforeUnmount(deactivate)\r\n }\r\n}\r\n","import type { Ref } from 'vue'\r\nimport { onBeforeUnmount, watch } from 'vue'\r\nimport { isClient } from '../utils/dom'\r\n\r\nlet locks = 0\r\nlet saved: { overflow: string, paddingRight: string } | null = null\r\n\r\nfunction lock() {\r\n locks++\r\n if (locks > 1) return\r\n const body = document.body\r\n const scrollbar = window.innerWidth - document.documentElement.clientWidth\r\n saved = { overflow: body.style.overflow, paddingRight: body.style.paddingRight }\r\n body.style.overflow = 'hidden'\r\n if (scrollbar > 0) {\r\n // Compensate for the vanished scrollbar so the page doesn't jump sideways.\r\n const current = Number.parseFloat(getComputedStyle(body).paddingRight) || 0\r\n body.style.paddingRight = `${current + scrollbar}px`\r\n }\r\n document.documentElement.style.setProperty('--juxt-scrollbar-width', `${scrollbar}px`)\r\n document.documentElement.dataset.juxtScrollLocked = ''\r\n}\r\n\r\nfunction unlock() {\r\n locks = Math.max(0, locks - 1)\r\n if (locks > 0 || !saved) return\r\n document.body.style.overflow = saved.overflow\r\n document.body.style.paddingRight = saved.paddingRight\r\n document.documentElement.style.removeProperty('--juxt-scrollbar-width')\r\n delete document.documentElement.dataset.juxtScrollLocked\r\n saved = null\r\n}\r\n\r\n/** Prevent the page behind an overlay from scrolling while `active` is true. */\r\nexport function useScrollLock(active: Ref<boolean>) {\r\n if (!isClient) return\r\n let held = false\r\n\r\n const set = (value: boolean) => {\r\n if (value === held) return\r\n held = value\r\n if (value) lock()\r\n else unlock()\r\n }\r\n\r\n watch(active, set, { immediate: true })\r\n onBeforeUnmount(() => set(false))\r\n}\r\n","<script setup lang=\"ts\">\r\nimport type { Component } from 'vue'\r\nimport type { MatchRange } from './search'\r\nimport { computed, nextTick, onMounted, ref, useId, watch } from 'vue'\r\nimport { useFocusTrap } from '../../composables/useFocusTrap'\r\nimport { formatHotkey, useHotkey } from '../../composables/useHotkey'\r\nimport { useLayer } from '../../composables/useLayer'\r\nimport { useScrollLock } from '../../composables/useScrollLock'\r\nimport { IconSearch, Spinner } from '../../icons'\r\nimport { matchText, splitByRanges } from './search'\r\nimport JPortal from '../primitives/JPortal.vue'\r\n\r\nexport interface CommandItem {\r\n id: string\r\n label: string\r\n description?: string\r\n /** An icon component, rendered at 16px. */\r\n icon?: Component\r\n /** Shortcut to display, in hotkey syntax: `mod+shift+p`. Display only. */\r\n shortcut?: string\r\n /** Extra terms that should find this item. */\r\n keywords?: string[]\r\n disabled?: boolean\r\n /** Called when the item is chosen, before `select` is emitted. */\r\n onSelect?: (item: CommandItem) => void\r\n}\r\n\r\nexport interface CommandGroup {\r\n id: string\r\n label?: string\r\n items: CommandItem[]\r\n}\r\n\r\nexport interface CommandPaletteProps {\r\n groups: CommandGroup[]\r\n /** Shown as a \"Recent\" group while the search is empty. */\r\n recent?: CommandItem[]\r\n placeholder?: string\r\n /** Show a loading indicator | for async results. */\r\n loading?: boolean\r\n /**\r\n * Built-in ranking and filtering. Set to `false` when you filter yourself\r\n * (e.g. server-side search driven by `v-model:search`).\r\n */\r\n filter?: boolean\r\n /** Global shortcut that toggles the palette. `false` to disable. */\r\n hotkey?: string | false\r\n /** Accessible name of the dialog. */\r\n label?: string\r\n emptyText?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<CommandPaletteProps>(), {\r\n placeholder: 'Type a command or search…',\r\n filter: true,\r\n hotkey: 'mod+k',\r\n label: 'Command palette',\r\n emptyText: 'No results',\r\n})\r\n\r\nconst open = defineModel<boolean>('open', { default: false })\r\nconst search = defineModel<string>('search', { default: '' })\r\n\r\nconst emit = defineEmits<{\r\n select: [item: CommandItem]\r\n}>()\r\n\r\ndefineSlots<{\r\n item?: (props: { item: CommandItem, active: boolean }) => unknown\r\n empty?: (props: { search: string }) => unknown\r\n footer?: () => unknown\r\n}>()\r\n\r\nconst id = `j-${useId()}`\r\nconst listboxId = `${id}-listbox`\r\n// Keys contain group and item ids; keep the DOM id selector-safe.\r\nconst optionId = (key: string) => `${id}-option-${key.replace(/[^\\w-]/g, '_')}`\r\n\r\n// --- Results ---------------------------------------------------------------\r\ninterface Row {\r\n key: string\r\n item: CommandItem\r\n ranges: MatchRange[]\r\n score: number\r\n}\r\n\r\ninterface Section {\r\n id: string\r\n label?: string\r\n rows: Row[]\r\n}\r\n\r\nconst sections = computed<Section[]>(() => {\r\n const query = search.value.trim()\r\n const toKey = (groupId: string, item: CommandItem) => `${groupId}:${item.id}`\r\n\r\n if (!query || !props.filter) {\r\n const base: Section[] = []\r\n if (!query && props.recent?.length)\r\n base.push({ id: 'recent', label: 'Recent', rows: props.recent.map(item => ({ key: toKey('recent', item), item, ranges: [], score: 1 })) })\r\n for (const group of props.groups) {\r\n if (group.items.length)\r\n base.push({ id: group.id, label: group.label, rows: group.items.map(item => ({ key: toKey(group.id, item), item, ranges: [], score: 1 })) })\r\n }\r\n return base\r\n }\r\n\r\n const ranked: Section[] = []\r\n for (const group of props.groups) {\r\n const rows: Row[] = []\r\n for (const item of group.items) {\r\n const label = matchText(query, item.label)\r\n let score = label.score\r\n if (item.keywords) {\r\n for (const keyword of item.keywords) score = Math.max(score, matchText(query, keyword).score * 0.9)\r\n }\r\n if (item.description) score = Math.max(score, matchText(query, item.description).score * 0.5)\r\n if (score > 0) rows.push({ key: toKey(group.id, item), item, ranges: label.ranges, score })\r\n }\r\n if (rows.length) {\r\n rows.sort((a, b) => b.score - a.score)\r\n ranked.push({ id: group.id, label: group.label, rows })\r\n }\r\n }\r\n // The group holding the best match comes first.\r\n return ranked.sort((a, b) => b.rows[0]!.score - a.rows[0]!.score)\r\n})\r\n\r\nconst rows = computed(() => sections.value.flatMap(section => section.rows))\r\nconst rowIndex = computed(() => new Map(rows.value.map((row, index) => [row.key, index])))\r\n\r\nconst activeIndex = ref(0)\r\nconst activeRow = computed(() => rows.value[activeIndex.value])\r\n\r\nfunction firstEnabled(from = 0, delta: 1 | -1 = 1): number {\r\n const count = rows.value.length\r\n for (let i = 0; i < count; i++) {\r\n const index = (from + i * delta + count * 2) % count\r\n if (!rows.value[index]!.item.disabled) return index\r\n }\r\n return -1\r\n}\r\n\r\n// Every keystroke puts the best match under the cursor.\r\nwatch(rows, () => (activeIndex.value = Math.max(0, firstEnabled())))\r\n\r\n// --- Opening & closing -----------------------------------------------------\r\nconst panel = ref<HTMLElement | null>(null)\r\nconst input = ref<HTMLInputElement | null>(null)\r\nconst list = ref<HTMLElement | null>(null)\r\n\r\nuseHotkey(() => props.hotkey, () => (open.value = !open.value))\r\nuseLayer({ active: open, elements: () => [panel.value], onEscape: () => close() })\r\nuseFocusTrap(panel, open, { initialFocus: () => input.value })\r\nuseScrollLock(open)\r\n\r\nwatch(open, (value) => {\r\n if (!value) return\r\n search.value = ''\r\n activeIndex.value = Math.max(0, firstEnabled())\r\n nextTick(() => list.value?.scrollTo({ top: 0 }))\r\n})\r\n\r\nfunction close() {\r\n open.value = false\r\n}\r\n\r\nlet pressedOutside = false\r\nfunction onPointerDown(event: PointerEvent) {\r\n pressedOutside = event.target === event.currentTarget\r\n}\r\nfunction onClick(event: MouseEvent) {\r\n if (pressedOutside && event.target === event.currentTarget) close()\r\n pressedOutside = false\r\n}\r\n\r\n// --- Selection & navigation ------------------------------------------------\r\nfunction choose(row: Row | undefined) {\r\n if (!row || row.item.disabled) return\r\n row.item.onSelect?.(row.item)\r\n emit('select', row.item)\r\n close()\r\n}\r\n\r\nfunction setActive(index: number) {\r\n if (index < 0) return\r\n activeIndex.value = index\r\n const row = rows.value[index]\r\n if (!row) return\r\n nextTick(() => {\r\n const el = document.getElementById(optionId(row.key))\r\n // Keep a little context around the active row at the list edges.\r\n if (index === 0) list.value?.scrollTo({ top: 0 })\r\n else el?.scrollIntoView({ block: 'nearest' })\r\n })\r\n}\r\n\r\nfunction move(delta: 1 | -1, distance = 1) {\r\n const count = rows.value.length\r\n if (!count) return\r\n let index = activeIndex.value\r\n for (let step = 0; step < distance; step++) {\r\n const next = firstEnabled((index + delta + count) % count, delta)\r\n if (next === -1) return\r\n // Page jumps stop at the ends instead of wrapping.\r\n if (distance > 1 && ((delta === 1 && next < index) || (delta === -1 && next > index))) break\r\n index = next\r\n }\r\n setActive(index)\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (event.isComposing) return\r\n // Emacs-style Ctrl+N / Ctrl+P for people who never leave the home row.\r\n const ctrl = event.ctrlKey && !event.metaKey && !event.altKey\r\n const key = event.key\r\n if (key === 'ArrowDown' || (ctrl && key === 'n')) move(1)\r\n else if (key === 'ArrowUp' || (ctrl && key === 'p')) move(-1)\r\n else if (key === 'PageDown') move(1, 5)\r\n else if (key === 'PageUp') move(-1, 5)\r\n else if (key === 'Enter') choose(activeRow.value)\r\n else return\r\n event.preventDefault()\r\n}\r\n\r\n// Hover follows the pointer only when it moves | scrolling with the keyboard\r\n// under a resting cursor must not steal the selection.\r\nfunction onRowPointerMove(index: number) {\r\n if (activeIndex.value !== index && !rows.value[index]?.item.disabled) activeIndex.value = index\r\n}\r\n\r\n// --- Announcements ---------------------------------------------------------\r\nconst announcement = ref('')\r\nlet announceTimer: ReturnType<typeof setTimeout> | undefined\r\nwatch([rows, () => props.loading], () => {\r\n clearTimeout(announceTimer)\r\n announceTimer = setTimeout(() => {\r\n if (!open.value) return\r\n if (props.loading) announcement.value = 'Loading results'\r\n else announcement.value = rows.value.length === 1 ? '1 result' : `${rows.value.length} results`\r\n }, 350)\r\n})\r\n\r\n// Shortcut glyphs depend on the platform | resolve them on the client only.\r\nconst mounted = ref(false)\r\nonMounted(() => (mounted.value = true))\r\nconst keys = (hotkey: string) => (mounted.value ? formatHotkey(hotkey) : [])\r\n\r\ndefineExpose({ close, focus: () => input.value?.focus() })\r\n</script>\r\n\r\n<template>\r\n <JPortal>\r\n <Transition name=\"j-command-overlay\">\r\n <div v-if=\"open\" class=\"j-command__overlay\" aria-hidden=\"true\" />\r\n </Transition>\r\n <Transition name=\"j-command\" :duration=\"{ enter: 180, leave: 110 }\">\r\n <div v-if=\"open\" class=\"j-command__positioner\" @pointerdown=\"onPointerDown\" @click=\"onClick\">\r\n <div\r\n ref=\"panel\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n :aria-label=\"label\"\r\n class=\"j-command\"\r\n >\r\n <div class=\"j-command__search\">\r\n <IconSearch class=\"j-command__search-icon\" />\r\n <input\r\n ref=\"input\"\r\n v-model=\"search\"\r\n class=\"j-command__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n aria-expanded=\"true\"\r\n aria-autocomplete=\"list\"\r\n :aria-controls=\"listboxId\"\r\n :aria-activedescendant=\"activeRow ? optionId(activeRow.key) : undefined\"\r\n :aria-label=\"label\"\r\n :placeholder=\"placeholder\"\r\n autocomplete=\"off\"\r\n autocorrect=\"off\"\r\n autocapitalize=\"off\"\r\n spellcheck=\"false\"\r\n enterkeyhint=\"go\"\r\n @keydown=\"onKeydown\"\r\n />\r\n <button type=\"button\" class=\"j-command__esc j-kbd\" tabindex=\"-1\" aria-label=\"Close\" @click=\"close\">\r\n Esc\r\n </button>\r\n <span v-if=\"loading\" class=\"j-command__progress\" aria-hidden=\"true\" />\r\n </div>\r\n\r\n <div\r\n :id=\"listboxId\"\r\n ref=\"list\"\r\n role=\"listbox\"\r\n :aria-label=\"label\"\r\n class=\"j-command__list\"\r\n @pointerdown.prevent\r\n >\r\n <div\r\n v-for=\"section in sections\"\r\n :key=\"section.id\"\r\n role=\"group\"\r\n class=\"j-command__group\"\r\n :aria-labelledby=\"section.label ? `${id}-group-${section.id}` : undefined\"\r\n >\r\n <div v-if=\"section.label\" :id=\"`${id}-group-${section.id}`\" class=\"j-command__group-label\" role=\"presentation\">\r\n {{ section.label }}\r\n </div>\r\n <div\r\n v-for=\"row in section.rows\"\r\n :id=\"optionId(row.key)\"\r\n :key=\"row.key\"\r\n role=\"option\"\r\n class=\"j-command__item\"\r\n :class=\"{ 'is-active': row.key === activeRow?.key }\"\r\n :aria-selected=\"row.key === activeRow?.key\"\r\n :aria-disabled=\"row.item.disabled || undefined\"\r\n @pointermove=\"onRowPointerMove(rowIndex.get(row.key)!)\"\r\n @click=\"choose(row)\"\r\n >\r\n <slot name=\"item\" :item=\"row.item\" :active=\"row.key === activeRow?.key\">\r\n <span v-if=\"row.item.icon\" class=\"j-command__item-icon\">\r\n <component :is=\"row.item.icon\" />\r\n </span>\r\n <span class=\"j-command__item-text\">\r\n <span class=\"j-command__item-label\" :class=\"{ 'is-searching': row.ranges.length }\">\r\n <template v-for=\"(part, i) in splitByRanges(row.item.label, row.ranges)\" :key=\"i\">\r\n <mark v-if=\"part.match\">{{ part.text }}</mark>\r\n <template v-else>{{ part.text }}</template>\r\n </template>\r\n </span>\r\n <span v-if=\"row.item.description\" class=\"j-command__item-description\">{{ row.item.description }}</span>\r\n </span>\r\n <span v-if=\"row.item.shortcut\" class=\"j-command__item-shortcut\" aria-hidden=\"true\">\r\n <kbd v-for=\"key in keys(row.item.shortcut)\" :key=\"key\" class=\"j-kbd\">{{ key }}</kbd>\r\n </span>\r\n </slot>\r\n </div>\r\n </div>\r\n\r\n <div v-if=\"!rows.length\" class=\"j-command__empty\" role=\"presentation\">\r\n <template v-if=\"loading\">\r\n <Spinner :size=\"16\" />\r\n <span>Searching…</span>\r\n </template>\r\n <slot v-else name=\"empty\" :search=\"search\">\r\n <span class=\"j-command__empty-title\">{{ emptyText }}</span>\r\n <span v-if=\"search\" class=\"j-command__empty-hint\">Nothing matches “{{ search }}”. Try fewer words.</span>\r\n </slot>\r\n </div>\r\n </div>\r\n\r\n <div class=\"j-command__footer\">\r\n <slot name=\"footer\">\r\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">↑</kbd><kbd class=\"j-kbd\">↓</kbd> Navigate</span>\r\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">↵</kbd> Select</span>\r\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">Esc</kbd> Close</span>\r\n </slot>\r\n </div>\r\n\r\n <span class=\"j-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ announcement }}</span>\r\n </div>\r\n </div>\r\n </Transition>\r\n </JPortal>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-command__overlay {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-overlay);\r\n background: var(--juxt-overlay);\r\n}\r\n\r\n.j-command__positioner {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-modal);\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: center;\r\n padding: max(12vh, var(--juxt-space-4)) var(--juxt-space-4) var(--juxt-space-4);\r\n}\r\n\r\n.j-command {\r\n position: relative;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n max-width: 40rem;\r\n max-height: min(34rem, calc(100dvh - 12vh - var(--juxt-space-8)));\r\n overflow: hidden;\r\n border: 1px solid var(--juxt-border);\r\n border-radius: var(--juxt-radius-lg);\r\n background: var(--juxt-surface-raised);\r\n box-shadow: var(--juxt-shadow-lg);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n/* Search */\r\n.j-command__search {\r\n position: relative;\r\n display: flex;\r\n flex: none;\r\n align-items: center;\r\n gap: var(--juxt-space-3);\r\n height: 3.25rem;\r\n padding: 0 var(--juxt-space-3) 0 var(--juxt-space-4);\r\n border-bottom: 1px solid var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-command__search,\r\n[data-theme='dark'] .j-command__search {\r\n border-bottom-color: var(--juxt-border);\r\n}\r\n\r\n.j-command__search-icon {\r\n flex: none;\r\n width: 1.125rem;\r\n height: 1.125rem;\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-command__input {\r\n flex: 1;\r\n min-width: 0;\r\n height: 100%;\r\n padding: 0;\r\n border: 0;\r\n outline: none;\r\n background: transparent;\r\n color: var(--juxt-fg);\r\n font: inherit;\r\n font-size: var(--juxt-text-lg);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n caret-color: var(--juxt-accent);\r\n}\r\n\r\n.j-command__input::placeholder {\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-command__esc {\r\n height: 1.375rem;\r\n padding: 0 var(--juxt-space-1-5);\r\n cursor: pointer;\r\n}\r\n\r\n.j-command__esc:hover {\r\n color: var(--juxt-fg-secondary);\r\n}\r\n\r\n/* An indeterminate hairline: present, never loud. */\r\n.j-command__progress {\r\n position: absolute;\r\n right: 0;\r\n bottom: -1px;\r\n left: 0;\r\n height: 1px;\r\n overflow: hidden;\r\n}\r\n\r\n.j-command__progress::after {\r\n content: '';\r\n position: absolute;\r\n inset: 0;\r\n width: 40%;\r\n background: linear-gradient(90deg, transparent, var(--juxt-accent), transparent);\r\n animation: j-command-progress 1.1s var(--juxt-ease-standard) infinite;\r\n}\r\n\r\n@keyframes j-command-progress {\r\n from {\r\n transform: translateX(-100%);\r\n }\r\n\r\n to {\r\n transform: translateX(250%);\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-command__progress::after {\r\n width: 100%;\r\n animation: j-command-pulse 1.4s ease-in-out infinite;\r\n }\r\n\r\n @keyframes j-command-pulse {\r\n 50% {\r\n opacity: 0.3;\r\n }\r\n }\r\n}\r\n\r\n/* List */\r\n.j-command__list {\r\n flex: 1;\r\n min-height: 0;\r\n padding: var(--juxt-space-1-5);\r\n overflow-y: auto;\r\n overscroll-behavior: contain;\r\n scroll-padding-block: var(--juxt-space-8) var(--juxt-space-1-5);\r\n}\r\n\r\n.j-command__group + .j-command__group {\r\n margin-top: var(--juxt-space-1);\r\n}\r\n\r\n.j-command__group-label {\r\n padding: var(--juxt-space-2) var(--juxt-space-2-5) var(--juxt-space-1-5);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n font-weight: var(--juxt-weight-medium);\r\n user-select: none;\r\n}\r\n\r\n.j-command__item {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-2-5);\r\n height: 2.5rem;\r\n padding: 0 var(--juxt-space-2-5);\r\n border-radius: var(--juxt-radius-sm);\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n/* The one place the palette uses green: a hairline marking where you are. */\r\n.j-command__item::before {\r\n content: '';\r\n position: absolute;\r\n top: 0.625rem;\r\n bottom: 0.625rem;\r\n left: 0;\r\n width: 2px;\r\n border-radius: 0 2px 2px 0;\r\n background: var(--juxt-accent);\r\n opacity: 0;\r\n transform: scaleY(0.4);\r\n transition:\r\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-command__item.is-active {\r\n background: var(--juxt-surface-hover);\r\n}\r\n\r\n.j-command__item.is-active::before {\r\n opacity: 1;\r\n transform: none;\r\n}\r\n\r\n.j-command__item[aria-disabled='true'] {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-command__item-icon {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-command__item-icon > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-command__item.is-active .j-command__item-icon {\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-command__item-text {\r\n display: flex;\r\n flex: 1;\r\n align-items: baseline;\r\n gap: var(--juxt-space-2);\r\n min-width: 0;\r\n overflow: hidden;\r\n white-space: nowrap;\r\n}\r\n\r\n.j-command__item-label {\r\n flex: none;\r\n max-width: 100%;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n}\r\n\r\n.j-command__item-label.is-searching {\r\n color: var(--juxt-fg-secondary);\r\n}\r\n\r\n.j-command__item-label mark {\r\n background: none;\r\n color: var(--juxt-fg);\r\n font-weight: var(--juxt-weight-medium);\r\n}\r\n\r\n.j-command__item-description {\r\n overflow: hidden;\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n text-overflow: ellipsis;\r\n}\r\n\r\n.j-command__item-shortcut {\r\n display: inline-flex;\r\n flex: none;\r\n gap: 3px;\r\n}\r\n\r\n.j-command__empty {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n gap: var(--juxt-space-1-5);\r\n min-height: 8rem;\r\n padding: var(--juxt-space-6) var(--juxt-space-4);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n text-align: center;\r\n}\r\n\r\n.j-command__empty-title {\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n}\r\n\r\n/* Footer */\r\n.j-command__footer {\r\n display: flex;\r\n flex: none;\r\n align-items: center;\r\n gap: var(--juxt-space-4);\r\n height: 2.25rem;\r\n padding: 0 var(--juxt-space-4);\r\n border-top: 1px solid var(--juxt-border-subtle);\r\n background: var(--juxt-surface-sunken);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n}\r\n\r\n.dark .j-command__footer,\r\n[data-theme='dark'] .j-command__footer {\r\n border-top-color: var(--juxt-border);\r\n background: var(--juxt-surface);\r\n}\r\n\r\n.j-command__hint {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1);\r\n}\r\n\r\n.j-command__hint .j-kbd + .j-kbd {\r\n margin-left: -1px;\r\n}\r\n\r\n/* Motion: quick in, quicker out. */\r\n.j-command-overlay-enter-active {\r\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-command-overlay-leave-active {\r\n transition: opacity var(--juxt-duration-instant) var(--juxt-ease-in);\r\n}\r\n\r\n.j-command-overlay-enter-from,\r\n.j-command-overlay-leave-to {\r\n opacity: 0;\r\n}\r\n\r\n.j-command-enter-active .j-command {\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-command-leave-active .j-command {\r\n transition:\r\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\r\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\r\n}\r\n\r\n.j-command-enter-from .j-command,\r\n.j-command-leave-to .j-command {\r\n opacity: 0;\r\n transform: translateY(calc(var(--juxt-motion-shift) * -1)) scale(0.985);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-command-enter-from .j-command,\r\n .j-command-leave-to .j-command {\r\n transform: none;\r\n }\r\n}\r\n\r\n@media (max-width: 639px) {\r\n .j-command__positioner {\r\n padding: var(--juxt-space-2);\r\n }\r\n\r\n .j-command {\r\n max-height: calc(100dvh - var(--juxt-space-4));\r\n }\r\n\r\n .j-command__footer {\r\n display: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { Component } from 'vue'\r\nimport type { MatchRange } from './search'\r\nimport { computed, nextTick, onMounted, ref, useId, watch } from 'vue'\r\nimport { useFocusTrap } from '../../composables/useFocusTrap'\r\nimport { formatHotkey, useHotkey } from '../../composables/useHotkey'\r\nimport { useLayer } from '../../composables/useLayer'\r\nimport { useScrollLock } from '../../composables/useScrollLock'\r\nimport { IconSearch, Spinner } from '../../icons'\r\nimport { matchText, splitByRanges } from './search'\r\nimport JPortal from '../primitives/JPortal.vue'\r\n\r\nexport interface CommandItem {\r\n id: string\r\n label: string\r\n description?: string\r\n /** An icon component, rendered at 16px. */\r\n icon?: Component\r\n /** Shortcut to display, in hotkey syntax: `mod+shift+p`. Display only. */\r\n shortcut?: string\r\n /** Extra terms that should find this item. */\r\n keywords?: string[]\r\n disabled?: boolean\r\n /** Called when the item is chosen, before `select` is emitted. */\r\n onSelect?: (item: CommandItem) => void\r\n}\r\n\r\nexport interface CommandGroup {\r\n id: string\r\n label?: string\r\n items: CommandItem[]\r\n}\r\n\r\nexport interface CommandPaletteProps {\r\n groups: CommandGroup[]\r\n /** Shown as a \"Recent\" group while the search is empty. */\r\n recent?: CommandItem[]\r\n placeholder?: string\r\n /** Show a loading indicator | for async results. */\r\n loading?: boolean\r\n /**\r\n * Built-in ranking and filtering. Set to `false` when you filter yourself\r\n * (e.g. server-side search driven by `v-model:search`).\r\n */\r\n filter?: boolean\r\n /** Global shortcut that toggles the palette. `false` to disable. */\r\n hotkey?: string | false\r\n /** Accessible name of the dialog. */\r\n label?: string\r\n emptyText?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<CommandPaletteProps>(), {\r\n placeholder: 'Type a command or search…',\r\n filter: true,\r\n hotkey: 'mod+k',\r\n label: 'Command palette',\r\n emptyText: 'No results',\r\n})\r\n\r\nconst open = defineModel<boolean>('open', { default: false })\r\nconst search = defineModel<string>('search', { default: '' })\r\n\r\nconst emit = defineEmits<{\r\n select: [item: CommandItem]\r\n}>()\r\n\r\ndefineSlots<{\r\n item?: (props: { item: CommandItem, active: boolean }) => unknown\r\n empty?: (props: { search: string }) => unknown\r\n footer?: () => unknown\r\n}>()\r\n\r\nconst id = `j-${useId()}`\r\nconst listboxId = `${id}-listbox`\r\n// Keys contain group and item ids; keep the DOM id selector-safe.\r\nconst optionId = (key: string) => `${id}-option-${key.replace(/[^\\w-]/g, '_')}`\r\n\r\n// --- Results ---------------------------------------------------------------\r\ninterface Row {\r\n key: string\r\n item: CommandItem\r\n ranges: MatchRange[]\r\n score: number\r\n}\r\n\r\ninterface Section {\r\n id: string\r\n label?: string\r\n rows: Row[]\r\n}\r\n\r\nconst sections = computed<Section[]>(() => {\r\n const query = search.value.trim()\r\n const toKey = (groupId: string, item: CommandItem) => `${groupId}:${item.id}`\r\n\r\n if (!query || !props.filter) {\r\n const base: Section[] = []\r\n if (!query && props.recent?.length)\r\n base.push({ id: 'recent', label: 'Recent', rows: props.recent.map(item => ({ key: toKey('recent', item), item, ranges: [], score: 1 })) })\r\n for (const group of props.groups) {\r\n if (group.items.length)\r\n base.push({ id: group.id, label: group.label, rows: group.items.map(item => ({ key: toKey(group.id, item), item, ranges: [], score: 1 })) })\r\n }\r\n return base\r\n }\r\n\r\n const ranked: Section[] = []\r\n for (const group of props.groups) {\r\n const rows: Row[] = []\r\n for (const item of group.items) {\r\n const label = matchText(query, item.label)\r\n let score = label.score\r\n if (item.keywords) {\r\n for (const keyword of item.keywords) score = Math.max(score, matchText(query, keyword).score * 0.9)\r\n }\r\n if (item.description) score = Math.max(score, matchText(query, item.description).score * 0.5)\r\n if (score > 0) rows.push({ key: toKey(group.id, item), item, ranges: label.ranges, score })\r\n }\r\n if (rows.length) {\r\n rows.sort((a, b) => b.score - a.score)\r\n ranked.push({ id: group.id, label: group.label, rows })\r\n }\r\n }\r\n // The group holding the best match comes first.\r\n return ranked.sort((a, b) => b.rows[0]!.score - a.rows[0]!.score)\r\n})\r\n\r\nconst rows = computed(() => sections.value.flatMap(section => section.rows))\r\nconst rowIndex = computed(() => new Map(rows.value.map((row, index) => [row.key, index])))\r\n\r\nconst activeIndex = ref(0)\r\nconst activeRow = computed(() => rows.value[activeIndex.value])\r\n\r\nfunction firstEnabled(from = 0, delta: 1 | -1 = 1): number {\r\n const count = rows.value.length\r\n for (let i = 0; i < count; i++) {\r\n const index = (from + i * delta + count * 2) % count\r\n if (!rows.value[index]!.item.disabled) return index\r\n }\r\n return -1\r\n}\r\n\r\n// Every keystroke puts the best match under the cursor.\r\nwatch(rows, () => (activeIndex.value = Math.max(0, firstEnabled())))\r\n\r\n// --- Opening & closing -----------------------------------------------------\r\nconst panel = ref<HTMLElement | null>(null)\r\nconst input = ref<HTMLInputElement | null>(null)\r\nconst list = ref<HTMLElement | null>(null)\r\n\r\nuseHotkey(() => props.hotkey, () => (open.value = !open.value))\r\nuseLayer({ active: open, elements: () => [panel.value], onEscape: () => close() })\r\nuseFocusTrap(panel, open, { initialFocus: () => input.value })\r\nuseScrollLock(open)\r\n\r\nwatch(open, (value) => {\r\n if (!value) return\r\n search.value = ''\r\n activeIndex.value = Math.max(0, firstEnabled())\r\n nextTick(() => list.value?.scrollTo({ top: 0 }))\r\n})\r\n\r\nfunction close() {\r\n open.value = false\r\n}\r\n\r\nlet pressedOutside = false\r\nfunction onPointerDown(event: PointerEvent) {\r\n pressedOutside = event.target === event.currentTarget\r\n}\r\nfunction onClick(event: MouseEvent) {\r\n if (pressedOutside && event.target === event.currentTarget) close()\r\n pressedOutside = false\r\n}\r\n\r\n// --- Selection & navigation ------------------------------------------------\r\nfunction choose(row: Row | undefined) {\r\n if (!row || row.item.disabled) return\r\n row.item.onSelect?.(row.item)\r\n emit('select', row.item)\r\n close()\r\n}\r\n\r\nfunction setActive(index: number) {\r\n if (index < 0) return\r\n activeIndex.value = index\r\n const row = rows.value[index]\r\n if (!row) return\r\n nextTick(() => {\r\n const el = document.getElementById(optionId(row.key))\r\n // Keep a little context around the active row at the list edges.\r\n if (index === 0) list.value?.scrollTo({ top: 0 })\r\n else el?.scrollIntoView({ block: 'nearest' })\r\n })\r\n}\r\n\r\nfunction move(delta: 1 | -1, distance = 1) {\r\n const count = rows.value.length\r\n if (!count) return\r\n let index = activeIndex.value\r\n for (let step = 0; step < distance; step++) {\r\n const next = firstEnabled((index + delta + count) % count, delta)\r\n if (next === -1) return\r\n // Page jumps stop at the ends instead of wrapping.\r\n if (distance > 1 && ((delta === 1 && next < index) || (delta === -1 && next > index))) break\r\n index = next\r\n }\r\n setActive(index)\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (event.isComposing) return\r\n // Emacs-style Ctrl+N / Ctrl+P for people who never leave the home row.\r\n const ctrl = event.ctrlKey && !event.metaKey && !event.altKey\r\n const key = event.key\r\n if (key === 'ArrowDown' || (ctrl && key === 'n')) move(1)\r\n else if (key === 'ArrowUp' || (ctrl && key === 'p')) move(-1)\r\n else if (key === 'PageDown') move(1, 5)\r\n else if (key === 'PageUp') move(-1, 5)\r\n else if (key === 'Enter') choose(activeRow.value)\r\n else return\r\n event.preventDefault()\r\n}\r\n\r\n// Hover follows the pointer only when it moves | scrolling with the keyboard\r\n// under a resting cursor must not steal the selection.\r\nfunction onRowPointerMove(index: number) {\r\n if (activeIndex.value !== index && !rows.value[index]?.item.disabled) activeIndex.value = index\r\n}\r\n\r\n// --- Announcements ---------------------------------------------------------\r\nconst announcement = ref('')\r\nlet announceTimer: ReturnType<typeof setTimeout> | undefined\r\nwatch([rows, () => props.loading], () => {\r\n clearTimeout(announceTimer)\r\n announceTimer = setTimeout(() => {\r\n if (!open.value) return\r\n if (props.loading) announcement.value = 'Loading results'\r\n else announcement.value = rows.value.length === 1 ? '1 result' : `${rows.value.length} results`\r\n }, 350)\r\n})\r\n\r\n// Shortcut glyphs depend on the platform | resolve them on the client only.\r\nconst mounted = ref(false)\r\nonMounted(() => (mounted.value = true))\r\nconst keys = (hotkey: string) => (mounted.value ? formatHotkey(hotkey) : [])\r\n\r\ndefineExpose({ close, focus: () => input.value?.focus() })\r\n</script>\r\n\r\n<template>\r\n <JPortal>\r\n <Transition name=\"j-command-overlay\">\r\n <div v-if=\"open\" class=\"j-command__overlay\" aria-hidden=\"true\" />\r\n </Transition>\r\n <Transition name=\"j-command\" :duration=\"{ enter: 180, leave: 110 }\">\r\n <div v-if=\"open\" class=\"j-command__positioner\" @pointerdown=\"onPointerDown\" @click=\"onClick\">\r\n <div\r\n ref=\"panel\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n :aria-label=\"label\"\r\n class=\"j-command\"\r\n >\r\n <div class=\"j-command__search\">\r\n <IconSearch class=\"j-command__search-icon\" />\r\n <input\r\n ref=\"input\"\r\n v-model=\"search\"\r\n class=\"j-command__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n aria-expanded=\"true\"\r\n aria-autocomplete=\"list\"\r\n :aria-controls=\"listboxId\"\r\n :aria-activedescendant=\"activeRow ? optionId(activeRow.key) : undefined\"\r\n :aria-label=\"label\"\r\n :placeholder=\"placeholder\"\r\n autocomplete=\"off\"\r\n autocorrect=\"off\"\r\n autocapitalize=\"off\"\r\n spellcheck=\"false\"\r\n enterkeyhint=\"go\"\r\n @keydown=\"onKeydown\"\r\n />\r\n <button type=\"button\" class=\"j-command__esc j-kbd\" tabindex=\"-1\" aria-label=\"Close\" @click=\"close\">\r\n Esc\r\n </button>\r\n <span v-if=\"loading\" class=\"j-command__progress\" aria-hidden=\"true\" />\r\n </div>\r\n\r\n <div\r\n :id=\"listboxId\"\r\n ref=\"list\"\r\n role=\"listbox\"\r\n :aria-label=\"label\"\r\n class=\"j-command__list\"\r\n @pointerdown.prevent\r\n >\r\n <div\r\n v-for=\"section in sections\"\r\n :key=\"section.id\"\r\n role=\"group\"\r\n class=\"j-command__group\"\r\n :aria-labelledby=\"section.label ? `${id}-group-${section.id}` : undefined\"\r\n >\r\n <div v-if=\"section.label\" :id=\"`${id}-group-${section.id}`\" class=\"j-command__group-label\" role=\"presentation\">\r\n {{ section.label }}\r\n </div>\r\n <div\r\n v-for=\"row in section.rows\"\r\n :id=\"optionId(row.key)\"\r\n :key=\"row.key\"\r\n role=\"option\"\r\n class=\"j-command__item\"\r\n :class=\"{ 'is-active': row.key === activeRow?.key }\"\r\n :aria-selected=\"row.key === activeRow?.key\"\r\n :aria-disabled=\"row.item.disabled || undefined\"\r\n @pointermove=\"onRowPointerMove(rowIndex.get(row.key)!)\"\r\n @click=\"choose(row)\"\r\n >\r\n <slot name=\"item\" :item=\"row.item\" :active=\"row.key === activeRow?.key\">\r\n <span v-if=\"row.item.icon\" class=\"j-command__item-icon\">\r\n <component :is=\"row.item.icon\" />\r\n </span>\r\n <span class=\"j-command__item-text\">\r\n <span class=\"j-command__item-label\" :class=\"{ 'is-searching': row.ranges.length }\">\r\n <template v-for=\"(part, i) in splitByRanges(row.item.label, row.ranges)\" :key=\"i\">\r\n <mark v-if=\"part.match\">{{ part.text }}</mark>\r\n <template v-else>{{ part.text }}</template>\r\n </template>\r\n </span>\r\n <span v-if=\"row.item.description\" class=\"j-command__item-description\">{{ row.item.description }}</span>\r\n </span>\r\n <span v-if=\"row.item.shortcut\" class=\"j-command__item-shortcut\" aria-hidden=\"true\">\r\n <kbd v-for=\"key in keys(row.item.shortcut)\" :key=\"key\" class=\"j-kbd\">{{ key }}</kbd>\r\n </span>\r\n </slot>\r\n </div>\r\n </div>\r\n\r\n <div v-if=\"!rows.length\" class=\"j-command__empty\" role=\"presentation\">\r\n <template v-if=\"loading\">\r\n <Spinner :size=\"16\" />\r\n <span>Searching…</span>\r\n </template>\r\n <slot v-else name=\"empty\" :search=\"search\">\r\n <span class=\"j-command__empty-title\">{{ emptyText }}</span>\r\n <span v-if=\"search\" class=\"j-command__empty-hint\">Nothing matches “{{ search }}”. Try fewer words.</span>\r\n </slot>\r\n </div>\r\n </div>\r\n\r\n <div class=\"j-command__footer\">\r\n <slot name=\"footer\">\r\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">↑</kbd><kbd class=\"j-kbd\">↓</kbd> Navigate</span>\r\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">↵</kbd> Select</span>\r\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">Esc</kbd> Close</span>\r\n </slot>\r\n </div>\r\n\r\n <span class=\"j-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ announcement }}</span>\r\n </div>\r\n </div>\r\n </Transition>\r\n </JPortal>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-command__overlay {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-overlay);\r\n background: var(--juxt-overlay);\r\n}\r\n\r\n.j-command__positioner {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-modal);\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: center;\r\n padding: max(12vh, var(--juxt-space-4)) var(--juxt-space-4) var(--juxt-space-4);\r\n}\r\n\r\n.j-command {\r\n position: relative;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n max-width: 40rem;\r\n max-height: min(34rem, calc(100dvh - 12vh - var(--juxt-space-8)));\r\n overflow: hidden;\r\n border: 1px solid var(--juxt-border);\r\n border-radius: var(--juxt-radius-lg);\r\n background: var(--juxt-surface-raised);\r\n box-shadow: var(--juxt-shadow-lg);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n/* Search */\r\n.j-command__search {\r\n position: relative;\r\n display: flex;\r\n flex: none;\r\n align-items: center;\r\n gap: var(--juxt-space-3);\r\n height: 3.25rem;\r\n padding: 0 var(--juxt-space-3) 0 var(--juxt-space-4);\r\n border-bottom: 1px solid var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-command__search,\r\n[data-theme='dark'] .j-command__search {\r\n border-bottom-color: var(--juxt-border);\r\n}\r\n\r\n.j-command__search-icon {\r\n flex: none;\r\n width: 1.125rem;\r\n height: 1.125rem;\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-command__input {\r\n flex: 1;\r\n min-width: 0;\r\n height: 100%;\r\n padding: 0;\r\n border: 0;\r\n outline: none;\r\n background: transparent;\r\n color: var(--juxt-fg);\r\n font: inherit;\r\n font-size: var(--juxt-text-lg);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n caret-color: var(--juxt-accent);\r\n}\r\n\r\n.j-command__input::placeholder {\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-command__esc {\r\n height: 1.375rem;\r\n padding: 0 var(--juxt-space-1-5);\r\n cursor: pointer;\r\n}\r\n\r\n.j-command__esc:hover {\r\n color: var(--juxt-fg-secondary);\r\n}\r\n\r\n/* An indeterminate hairline: present, never loud. */\r\n.j-command__progress {\r\n position: absolute;\r\n right: 0;\r\n bottom: -1px;\r\n left: 0;\r\n height: 1px;\r\n overflow: hidden;\r\n}\r\n\r\n.j-command__progress::after {\r\n content: '';\r\n position: absolute;\r\n inset: 0;\r\n width: 40%;\r\n background: linear-gradient(90deg, transparent, var(--juxt-accent), transparent);\r\n animation: j-command-progress 1.1s var(--juxt-ease-standard) infinite;\r\n}\r\n\r\n@keyframes j-command-progress {\r\n from {\r\n transform: translateX(-100%);\r\n }\r\n\r\n to {\r\n transform: translateX(250%);\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-command__progress::after {\r\n width: 100%;\r\n animation: j-command-pulse 1.4s ease-in-out infinite;\r\n }\r\n\r\n @keyframes j-command-pulse {\r\n 50% {\r\n opacity: 0.3;\r\n }\r\n }\r\n}\r\n\r\n/* List */\r\n.j-command__list {\r\n flex: 1;\r\n min-height: 0;\r\n padding: var(--juxt-space-1-5);\r\n overflow-y: auto;\r\n overscroll-behavior: contain;\r\n scroll-padding-block: var(--juxt-space-8) var(--juxt-space-1-5);\r\n}\r\n\r\n.j-command__group + .j-command__group {\r\n margin-top: var(--juxt-space-1);\r\n}\r\n\r\n.j-command__group-label {\r\n padding: var(--juxt-space-2) var(--juxt-space-2-5) var(--juxt-space-1-5);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n font-weight: var(--juxt-weight-medium);\r\n user-select: none;\r\n}\r\n\r\n.j-command__item {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-2-5);\r\n height: 2.5rem;\r\n padding: 0 var(--juxt-space-2-5);\r\n border-radius: var(--juxt-radius-sm);\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n/* The one place the palette uses green: a hairline marking where you are. */\r\n.j-command__item::before {\r\n content: '';\r\n position: absolute;\r\n top: 0.625rem;\r\n bottom: 0.625rem;\r\n left: 0;\r\n width: 2px;\r\n border-radius: 0 2px 2px 0;\r\n background: var(--juxt-accent);\r\n opacity: 0;\r\n transform: scaleY(0.4);\r\n transition:\r\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-command__item.is-active {\r\n background: var(--juxt-surface-hover);\r\n}\r\n\r\n.j-command__item.is-active::before {\r\n opacity: 1;\r\n transform: none;\r\n}\r\n\r\n.j-command__item[aria-disabled='true'] {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-command__item-icon {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-command__item-icon > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-command__item.is-active .j-command__item-icon {\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-command__item-text {\r\n display: flex;\r\n flex: 1;\r\n align-items: baseline;\r\n gap: var(--juxt-space-2);\r\n min-width: 0;\r\n overflow: hidden;\r\n white-space: nowrap;\r\n}\r\n\r\n.j-command__item-label {\r\n flex: none;\r\n max-width: 100%;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n}\r\n\r\n.j-command__item-label.is-searching {\r\n color: var(--juxt-fg-secondary);\r\n}\r\n\r\n.j-command__item-label mark {\r\n background: none;\r\n color: var(--juxt-fg);\r\n font-weight: var(--juxt-weight-medium);\r\n}\r\n\r\n.j-command__item-description {\r\n overflow: hidden;\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n text-overflow: ellipsis;\r\n}\r\n\r\n.j-command__item-shortcut {\r\n display: inline-flex;\r\n flex: none;\r\n gap: 3px;\r\n}\r\n\r\n.j-command__empty {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n gap: var(--juxt-space-1-5);\r\n min-height: 8rem;\r\n padding: var(--juxt-space-6) var(--juxt-space-4);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n text-align: center;\r\n}\r\n\r\n.j-command__empty-title {\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n}\r\n\r\n/* Footer */\r\n.j-command__footer {\r\n display: flex;\r\n flex: none;\r\n align-items: center;\r\n gap: var(--juxt-space-4);\r\n height: 2.25rem;\r\n padding: 0 var(--juxt-space-4);\r\n border-top: 1px solid var(--juxt-border-subtle);\r\n background: var(--juxt-surface-sunken);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n}\r\n\r\n.dark .j-command__footer,\r\n[data-theme='dark'] .j-command__footer {\r\n border-top-color: var(--juxt-border);\r\n background: var(--juxt-surface);\r\n}\r\n\r\n.j-command__hint {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1);\r\n}\r\n\r\n.j-command__hint .j-kbd + .j-kbd {\r\n margin-left: -1px;\r\n}\r\n\r\n/* Motion: quick in, quicker out. */\r\n.j-command-overlay-enter-active {\r\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-command-overlay-leave-active {\r\n transition: opacity var(--juxt-duration-instant) var(--juxt-ease-in);\r\n}\r\n\r\n.j-command-overlay-enter-from,\r\n.j-command-overlay-leave-to {\r\n opacity: 0;\r\n}\r\n\r\n.j-command-enter-active .j-command {\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-command-leave-active .j-command {\r\n transition:\r\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\r\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\r\n}\r\n\r\n.j-command-enter-from .j-command,\r\n.j-command-leave-to .j-command {\r\n opacity: 0;\r\n transform: translateY(calc(var(--juxt-motion-shift) * -1)) scale(0.985);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-command-enter-from .j-command,\r\n .j-command-leave-to .j-command {\r\n transform: none;\r\n }\r\n}\r\n\r\n@media (max-width: 639px) {\r\n .j-command__positioner {\r\n padding: var(--juxt-space-2);\r\n }\r\n\r\n .j-command {\r\n max-height: calc(100dvh - var(--juxt-space-4));\r\n }\r\n\r\n .j-command__footer {\r\n display: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","import type { Ref } from 'vue'\r\nimport { ref } from 'vue'\r\nimport { prefersReducedMotion } from '../utils/dom'\r\nimport { useFocusTrap } from './useFocusTrap'\r\nimport { useLayer } from './useLayer'\r\nimport { useScrollLock } from './useScrollLock'\r\n\r\nexport interface ModalOptions {\r\n closeOnEscape: () => boolean\r\n closeOnOverlay: () => boolean\r\n close: () => void\r\n /** Selector for the element to focus first, inside the panel. */\r\n focusSelector?: string\r\n}\r\n\r\n/**\r\n * Internal: everything a modal surface (Dialog, Sheet) shares | layering,\r\n * focus trap and restoration, scroll lock, backdrop clicks and the \"nudge\"\r\n * when dismissal is refused.\r\n */\r\nexport function useModal(panel: Ref<HTMLElement | null>, open: Ref<boolean>, options: ModalOptions) {\r\n const nudging = ref(false)\r\n\r\n // A modal that refuses to close gives a small, polite nudge instead.\r\n function nudge() {\r\n if (prefersReducedMotion()) return\r\n nudging.value = false\r\n requestAnimationFrame(() => (nudging.value = true))\r\n }\r\n\r\n const { isTopLayer } = useLayer({\r\n active: open,\r\n elements: () => [panel.value],\r\n onEscape: () => (options.closeOnEscape() ? options.close() : nudge()),\r\n })\r\n\r\n useFocusTrap(panel, open, {\r\n // Prefer an explicit autofocus, then the first form field. Otherwise focus the\r\n // panel itself so screen readers announce it without jumping to a button.\r\n initialFocus: () =>\r\n panel.value?.querySelector<HTMLElement>('[autofocus], [data-autofocus]')\r\n ?? (options.focusSelector ? panel.value?.querySelector<HTMLElement>(options.focusSelector) : null)\r\n ?? panel.value,\r\n })\r\n\r\n useScrollLock(open)\r\n\r\n // Close on a click that both starts and ends on the backdrop, so dragging a\r\n // text selection out of the panel never dismisses it.\r\n let pressedOutside = false\r\n function onBackdropPointerDown(event: PointerEvent) {\r\n pressedOutside = event.target === event.currentTarget && isTopLayer()\r\n }\r\n function onBackdropClick(event: MouseEvent) {\r\n const outside = pressedOutside && event.target === event.currentTarget\r\n pressedOutside = false\r\n if (!outside) return\r\n if (options.closeOnOverlay()) options.close()\r\n else nudge()\r\n }\r\n\r\n return { nudging, onBackdropPointerDown, onBackdropClick }\r\n}\r\n\r\n/** Form fields worth focusing first in a modal body. */\r\nexport const FIELD_SELECTOR = ':is(input:not([type=hidden]), textarea, select, [role=combobox]):not(:disabled)'\r\n","<script setup lang=\"ts\">\r\nimport { ref, useId } from 'vue'\r\nimport { FIELD_SELECTOR, useModal } from '../../composables/useModal'\r\nimport { IconX } from '../../icons'\r\nimport JPortal from '../primitives/JPortal.vue'\r\n\r\nexport interface DialogProps {\r\n title?: string\r\n description?: string\r\n size?: 'sm' | 'md' | 'lg'\r\n /** Close when the backdrop is clicked. Defaults to true. */\r\n closeOnOverlay?: boolean\r\n /** Close when Escape is pressed. Defaults to true. */\r\n closeOnEscape?: boolean\r\n /** Hide the close button in the corner. */\r\n hideClose?: boolean\r\n /** Accessible label when there is no visible title. */\r\n ariaLabel?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<DialogProps>(), {\r\n size: 'md',\r\n closeOnOverlay: true,\r\n closeOnEscape: true,\r\n})\r\n\r\nconst open = defineModel<boolean>('open', { default: false })\r\n\r\nconst emit = defineEmits<{\r\n /** Fired after the leave transition finishes | a good moment to reset form state. */\r\n afterClose: []\r\n}>()\r\n\r\ndefineSlots<{\r\n default?: (props: { close: () => void }) => unknown\r\n /** Custom header. Put `titleId` on your heading so the dialog stays labelled. */\r\n header?: (props: { titleId: string, descriptionId: string }) => unknown\r\n footer?: (props: { close: () => void }) => unknown\r\n}>()\r\n\r\nconst id = useId()\r\nconst titleId = `j-${id}-title`\r\nconst descriptionId = `j-${id}-description`\r\n\r\nconst panel = ref<HTMLElement | null>(null)\r\n\r\nfunction close() {\r\n open.value = false\r\n}\r\n\r\nconst { nudging, onBackdropPointerDown, onBackdropClick } = useModal(panel, open, {\r\n closeOnEscape: () => props.closeOnEscape,\r\n closeOnOverlay: () => props.closeOnOverlay,\r\n close,\r\n focusSelector: `.j-dialog__body ${FIELD_SELECTOR}`,\r\n})\r\n\r\ndefineExpose({ close })\r\n</script>\r\n\r\n<template>\r\n <JPortal>\r\n <Transition name=\"j-dialog-overlay\">\r\n <div v-if=\"open\" class=\"j-dialog__overlay\" aria-hidden=\"true\" />\r\n </Transition>\r\n <Transition name=\"j-dialog\" :duration=\"{ enter: 240, leave: 150 }\" @after-leave=\"emit('afterClose')\">\r\n <div v-if=\"open\" class=\"j-dialog__positioner\" @pointerdown=\"onBackdropPointerDown\" @click=\"onBackdropClick\">\r\n <div\r\n ref=\"panel\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n tabindex=\"-1\"\r\n class=\"j-dialog__panel\"\r\n :class=\"[`j-dialog__panel--${size}`, { 'is-nudging': nudging }]\"\r\n :aria-labelledby=\"title || $slots.header ? titleId : undefined\"\r\n :aria-label=\"ariaLabel\"\r\n :aria-describedby=\"description ? descriptionId : undefined\"\r\n @animationend=\"nudging = false\"\r\n >\r\n <header v-if=\"title || description || $slots.header\" class=\"j-dialog__header\">\r\n <slot name=\"header\" :title-id=\"titleId\" :description-id=\"descriptionId\">\r\n <h2 :id=\"titleId\" class=\"j-dialog__title\">\r\n {{ title }}\r\n </h2>\r\n <p v-if=\"description\" :id=\"descriptionId\" class=\"j-dialog__description\">\r\n {{ description }}\r\n </p>\r\n </slot>\r\n </header>\r\n <div v-if=\"$slots.default\" class=\"j-dialog__body\">\r\n <slot :close=\"close\" />\r\n </div>\r\n <footer v-if=\"$slots.footer\" class=\"j-dialog__footer\">\r\n <slot name=\"footer\" :close=\"close\" />\r\n </footer>\r\n <button\r\n v-if=\"!hideClose\"\r\n type=\"button\"\r\n class=\"j-dialog__close j-focusable\"\r\n aria-label=\"Close\"\r\n @click=\"close\"\r\n >\r\n <IconX />\r\n </button>\r\n </div>\r\n </div>\r\n </Transition>\r\n </JPortal>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-dialog__overlay {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-overlay);\r\n background: var(--juxt-overlay);\r\n}\r\n\r\n.j-dialog__positioner {\r\n --j-dialog-shift: calc(var(--juxt-motion-shift) * 2);\r\n\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-modal);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: var(--juxt-space-4);\r\n overflow-y: auto;\r\n overscroll-behavior: contain;\r\n}\r\n\r\n.j-dialog__panel {\r\n position: relative;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n max-height: calc(100dvh - var(--juxt-space-8));\r\n border: 1px solid var(--juxt-border);\r\n border-radius: var(--juxt-radius-lg);\r\n background: var(--juxt-surface-raised);\r\n box-shadow: var(--juxt-shadow-lg);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n outline: none;\r\n}\r\n\r\n.j-dialog__panel--sm {\r\n max-width: 25rem;\r\n}\r\n\r\n.j-dialog__panel--md {\r\n max-width: 30rem;\r\n}\r\n\r\n.j-dialog__panel--lg {\r\n max-width: 40rem;\r\n}\r\n\r\n.j-dialog__header {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-1-5);\r\n padding: var(--juxt-space-5) var(--juxt-space-12) 0 var(--juxt-space-5);\r\n}\r\n\r\n.j-dialog__title {\r\n margin: 0;\r\n font-size: var(--juxt-text-lg);\r\n font-weight: var(--juxt-weight-semibold);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-dialog__description {\r\n margin: 0;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-dialog__body {\r\n flex: 1;\r\n min-height: 0;\r\n padding: var(--juxt-space-4) var(--juxt-space-5) 0;\r\n overflow-y: auto;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-dialog__header + .j-dialog__footer {\r\n margin-top: var(--juxt-space-1);\r\n}\r\n\r\n.j-dialog__footer {\r\n display: flex;\r\n justify-content: flex-end;\r\n gap: var(--juxt-space-2);\r\n padding: var(--juxt-space-5);\r\n}\r\n\r\n.j-dialog__body:last-child {\r\n padding-bottom: var(--juxt-space-5);\r\n}\r\n\r\n.j-dialog__close {\r\n position: absolute;\r\n top: var(--juxt-space-3);\r\n right: var(--juxt-space-3);\r\n display: grid;\r\n place-items: center;\r\n width: var(--juxt-control-sm);\r\n height: var(--juxt-control-sm);\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-sm);\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n cursor: pointer;\r\n transition-property: background-color, color, outline-color, outline-offset;\r\n}\r\n\r\n.j-dialog__close:hover {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-dialog__close > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n/* Motion: the backdrop fades; the panel fades, lifts and settles. */\r\n.j-dialog-overlay-enter-active {\r\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-dialog-overlay-leave-active {\r\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-in);\r\n}\r\n\r\n.j-dialog-overlay-enter-from,\r\n.j-dialog-overlay-leave-to {\r\n opacity: 0;\r\n}\r\n\r\n.j-dialog-enter-active .j-dialog__panel {\r\n transition:\r\n opacity var(--juxt-duration-normal) var(--juxt-ease-out),\r\n transform var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n.j-dialog-leave-active .j-dialog__panel {\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-in),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-in);\r\n}\r\n\r\n.j-dialog-enter-from .j-dialog__panel,\r\n.j-dialog-leave-to .j-dialog__panel {\r\n opacity: 0;\r\n transform: translateY(var(--j-dialog-shift)) scale(var(--juxt-motion-scale));\r\n}\r\n\r\n.j-dialog__panel.is-nudging {\r\n animation: j-dialog-nudge 220ms var(--juxt-ease-out);\r\n}\r\n\r\n@keyframes j-dialog-nudge {\r\n 40% {\r\n transform: scale(1.012);\r\n }\r\n}\r\n\r\n/* Small screens: a sheet anchored to the bottom, within thumb reach. */\r\n@media (max-width: 639px) {\r\n .j-dialog__positioner {\r\n --j-dialog-shift: calc(var(--juxt-motion-shift) * 6);\r\n\r\n align-items: flex-end;\r\n padding: var(--juxt-space-2);\r\n padding-bottom: max(var(--juxt-space-2), env(safe-area-inset-bottom));\r\n }\r\n\r\n .j-dialog__panel {\r\n max-width: none;\r\n max-height: calc(100dvh - var(--juxt-space-12));\r\n }\r\n\r\n .j-dialog-enter-from .j-dialog__panel,\r\n .j-dialog-leave-to .j-dialog__panel {\r\n transform: translateY(var(--j-dialog-shift));\r\n }\r\n\r\n .j-dialog__footer {\r\n flex-direction: column-reverse;\r\n }\r\n\r\n .j-dialog__footer > * {\r\n width: 100%;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { ref, useId } from 'vue'\r\nimport { FIELD_SELECTOR, useModal } from '../../composables/useModal'\r\nimport { IconX } from '../../icons'\r\nimport JPortal from '../primitives/JPortal.vue'\r\n\r\nexport interface DialogProps {\r\n title?: string\r\n description?: string\r\n size?: 'sm' | 'md' | 'lg'\r\n /** Close when the backdrop is clicked. Defaults to true. */\r\n closeOnOverlay?: boolean\r\n /** Close when Escape is pressed. Defaults to true. */\r\n closeOnEscape?: boolean\r\n /** Hide the close button in the corner. */\r\n hideClose?: boolean\r\n /** Accessible label when there is no visible title. */\r\n ariaLabel?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<DialogProps>(), {\r\n size: 'md',\r\n closeOnOverlay: true,\r\n closeOnEscape: true,\r\n})\r\n\r\nconst open = defineModel<boolean>('open', { default: false })\r\n\r\nconst emit = defineEmits<{\r\n /** Fired after the leave transition finishes | a good moment to reset form state. */\r\n afterClose: []\r\n}>()\r\n\r\ndefineSlots<{\r\n default?: (props: { close: () => void }) => unknown\r\n /** Custom header. Put `titleId` on your heading so the dialog stays labelled. */\r\n header?: (props: { titleId: string, descriptionId: string }) => unknown\r\n footer?: (props: { close: () => void }) => unknown\r\n}>()\r\n\r\nconst id = useId()\r\nconst titleId = `j-${id}-title`\r\nconst descriptionId = `j-${id}-description`\r\n\r\nconst panel = ref<HTMLElement | null>(null)\r\n\r\nfunction close() {\r\n open.value = false\r\n}\r\n\r\nconst { nudging, onBackdropPointerDown, onBackdropClick } = useModal(panel, open, {\r\n closeOnEscape: () => props.closeOnEscape,\r\n closeOnOverlay: () => props.closeOnOverlay,\r\n close,\r\n focusSelector: `.j-dialog__body ${FIELD_SELECTOR}`,\r\n})\r\n\r\ndefineExpose({ close })\r\n</script>\r\n\r\n<template>\r\n <JPortal>\r\n <Transition name=\"j-dialog-overlay\">\r\n <div v-if=\"open\" class=\"j-dialog__overlay\" aria-hidden=\"true\" />\r\n </Transition>\r\n <Transition name=\"j-dialog\" :duration=\"{ enter: 240, leave: 150 }\" @after-leave=\"emit('afterClose')\">\r\n <div v-if=\"open\" class=\"j-dialog__positioner\" @pointerdown=\"onBackdropPointerDown\" @click=\"onBackdropClick\">\r\n <div\r\n ref=\"panel\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n tabindex=\"-1\"\r\n class=\"j-dialog__panel\"\r\n :class=\"[`j-dialog__panel--${size}`, { 'is-nudging': nudging }]\"\r\n :aria-labelledby=\"title || $slots.header ? titleId : undefined\"\r\n :aria-label=\"ariaLabel\"\r\n :aria-describedby=\"description ? descriptionId : undefined\"\r\n @animationend=\"nudging = false\"\r\n >\r\n <header v-if=\"title || description || $slots.header\" class=\"j-dialog__header\">\r\n <slot name=\"header\" :title-id=\"titleId\" :description-id=\"descriptionId\">\r\n <h2 :id=\"titleId\" class=\"j-dialog__title\">\r\n {{ title }}\r\n </h2>\r\n <p v-if=\"description\" :id=\"descriptionId\" class=\"j-dialog__description\">\r\n {{ description }}\r\n </p>\r\n </slot>\r\n </header>\r\n <div v-if=\"$slots.default\" class=\"j-dialog__body\">\r\n <slot :close=\"close\" />\r\n </div>\r\n <footer v-if=\"$slots.footer\" class=\"j-dialog__footer\">\r\n <slot name=\"footer\" :close=\"close\" />\r\n </footer>\r\n <button\r\n v-if=\"!hideClose\"\r\n type=\"button\"\r\n class=\"j-dialog__close j-focusable\"\r\n aria-label=\"Close\"\r\n @click=\"close\"\r\n >\r\n <IconX />\r\n </button>\r\n </div>\r\n </div>\r\n </Transition>\r\n </JPortal>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-dialog__overlay {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-overlay);\r\n background: var(--juxt-overlay);\r\n}\r\n\r\n.j-dialog__positioner {\r\n --j-dialog-shift: calc(var(--juxt-motion-shift) * 2);\r\n\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-modal);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: var(--juxt-space-4);\r\n overflow-y: auto;\r\n overscroll-behavior: contain;\r\n}\r\n\r\n.j-dialog__panel {\r\n position: relative;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n max-height: calc(100dvh - var(--juxt-space-8));\r\n border: 1px solid var(--juxt-border);\r\n border-radius: var(--juxt-radius-lg);\r\n background: var(--juxt-surface-raised);\r\n box-shadow: var(--juxt-shadow-lg);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n outline: none;\r\n}\r\n\r\n.j-dialog__panel--sm {\r\n max-width: 25rem;\r\n}\r\n\r\n.j-dialog__panel--md {\r\n max-width: 30rem;\r\n}\r\n\r\n.j-dialog__panel--lg {\r\n max-width: 40rem;\r\n}\r\n\r\n.j-dialog__header {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-1-5);\r\n padding: var(--juxt-space-5) var(--juxt-space-12) 0 var(--juxt-space-5);\r\n}\r\n\r\n.j-dialog__title {\r\n margin: 0;\r\n font-size: var(--juxt-text-lg);\r\n font-weight: var(--juxt-weight-semibold);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-dialog__description {\r\n margin: 0;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-dialog__body {\r\n flex: 1;\r\n min-height: 0;\r\n padding: var(--juxt-space-4) var(--juxt-space-5) 0;\r\n overflow-y: auto;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-dialog__header + .j-dialog__footer {\r\n margin-top: var(--juxt-space-1);\r\n}\r\n\r\n.j-dialog__footer {\r\n display: flex;\r\n justify-content: flex-end;\r\n gap: var(--juxt-space-2);\r\n padding: var(--juxt-space-5);\r\n}\r\n\r\n.j-dialog__body:last-child {\r\n padding-bottom: var(--juxt-space-5);\r\n}\r\n\r\n.j-dialog__close {\r\n position: absolute;\r\n top: var(--juxt-space-3);\r\n right: var(--juxt-space-3);\r\n display: grid;\r\n place-items: center;\r\n width: var(--juxt-control-sm);\r\n height: var(--juxt-control-sm);\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-sm);\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n cursor: pointer;\r\n transition-property: background-color, color, outline-color, outline-offset;\r\n}\r\n\r\n.j-dialog__close:hover {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-dialog__close > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n/* Motion: the backdrop fades; the panel fades, lifts and settles. */\r\n.j-dialog-overlay-enter-active {\r\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-dialog-overlay-leave-active {\r\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-in);\r\n}\r\n\r\n.j-dialog-overlay-enter-from,\r\n.j-dialog-overlay-leave-to {\r\n opacity: 0;\r\n}\r\n\r\n.j-dialog-enter-active .j-dialog__panel {\r\n transition:\r\n opacity var(--juxt-duration-normal) var(--juxt-ease-out),\r\n transform var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n.j-dialog-leave-active .j-dialog__panel {\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-in),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-in);\r\n}\r\n\r\n.j-dialog-enter-from .j-dialog__panel,\r\n.j-dialog-leave-to .j-dialog__panel {\r\n opacity: 0;\r\n transform: translateY(var(--j-dialog-shift)) scale(var(--juxt-motion-scale));\r\n}\r\n\r\n.j-dialog__panel.is-nudging {\r\n animation: j-dialog-nudge 220ms var(--juxt-ease-out);\r\n}\r\n\r\n@keyframes j-dialog-nudge {\r\n 40% {\r\n transform: scale(1.012);\r\n }\r\n}\r\n\r\n/* Small screens: a sheet anchored to the bottom, within thumb reach. */\r\n@media (max-width: 639px) {\r\n .j-dialog__positioner {\r\n --j-dialog-shift: calc(var(--juxt-motion-shift) * 6);\r\n\r\n align-items: flex-end;\r\n padding: var(--juxt-space-2);\r\n padding-bottom: max(var(--juxt-space-2), env(safe-area-inset-bottom));\r\n }\r\n\r\n .j-dialog__panel {\r\n max-width: none;\r\n max-height: calc(100dvh - var(--juxt-space-12));\r\n }\r\n\r\n .j-dialog-enter-from .j-dialog__panel,\r\n .j-dialog-leave-to .j-dialog__panel {\r\n transform: translateY(var(--j-dialog-shift));\r\n }\r\n\r\n .j-dialog__footer {\r\n flex-direction: column-reverse;\r\n }\r\n\r\n .j-dialog__footer > * {\r\n width: 100%;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\ndefineSlots<{ default?: () => unknown }>()\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-menu__label\" role=\"presentation\">\r\n <slot />\r\n </div>\r\n</template>\r\n","<script setup lang=\"ts\">\r\ndefineSlots<{ default?: () => unknown }>()\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-menu__label\" role=\"presentation\">\r\n <slot />\r\n </div>\r\n</template>\r\n","<template>\r\n <div role=\"separator\" aria-orientation=\"horizontal\" class=\"j-menu__separator\" />\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport { computed, inject, nextTick, ref, useId } from 'vue'\r\nimport { IconChevronRight } from '../../icons'\r\nimport { focusElement } from '../../utils/dom'\r\nimport { getMenuItems, MENU_LEVEL, MENU_ROOT } from './context'\r\nimport JMenuContent from './JMenuContent.vue'\r\n\r\nexport interface DropdownMenuSubProps {\r\n label: string\r\n disabled?: boolean\r\n}\r\n\r\nconst props = defineProps<DropdownMenuSubProps>()\r\n\r\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\r\n\r\nconst root = inject(MENU_ROOT)!\r\nconst level = inject(MENU_LEVEL)!\r\n\r\nconst id = useId()\r\nconst itemId = `j-${id}-subtrigger`\r\nconst menuId = `j-${id}-submenu`\r\nconst item = ref<HTMLElement | null>(null)\r\nconst sub = ref<InstanceType<typeof JMenuContent> | null>(null)\r\nconst initialFocus = ref<'first' | 'none'>('none')\r\nconst isOpen = computed(() => level.openSub.value === menuId)\r\n\r\nlet openTimer: ReturnType<typeof setTimeout> | undefined\r\n\r\nfunction openSub(focus: 'first' | 'none') {\r\n if (props.disabled) return\r\n initialFocus.value = focus\r\n level.setOpenSub(menuId)\r\n}\r\n\r\nfunction closeSub(returnFocus: boolean) {\r\n if (level.openSub.value === menuId) level.setOpenSub(null)\r\n if (returnFocus) nextTick(() => focusElement(item.value))\r\n}\r\n\r\nfunction onPointerMove() {\r\n level.cancelSubClose()\r\n if (props.disabled) return\r\n if (document.activeElement !== item.value) focusElement(item.value)\r\n if (isOpen.value) return\r\n clearTimeout(openTimer)\r\n openTimer = setTimeout(() => openSub('none'), 90)\r\n}\r\n\r\nfunction onPointerLeave() {\r\n clearTimeout(openTimer)\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (event.key === 'ArrowRight' || event.key === 'Enter' || event.key === ' ') {\r\n event.preventDefault()\r\n event.stopPropagation()\r\n if (isOpen.value) focusElement(getMenuItems(sub.value?.content ?? null)[0])\r\n else openSub('first')\r\n }\r\n}\r\n\r\nfunction onClick() {\r\n openSub('first')\r\n}\r\n\r\n// A press outside the submenu either lands back in the parent (close just the\r\n// submenu) or somewhere else entirely (close the whole menu).\r\nfunction onOutside(event: PointerEvent) {\r\n const target = event.target as Node\r\n if (level.content.value?.contains(target)) closeSub(false)\r\n else root.close(false)\r\n}\r\n</script>\r\n\r\n<template>\r\n <div\r\n :id=\"itemId\"\r\n ref=\"item\"\r\n role=\"menuitem\"\r\n tabindex=\"-1\"\r\n class=\"j-menu__item\"\r\n aria-haspopup=\"menu\"\r\n :aria-expanded=\"isOpen\"\r\n :aria-controls=\"isOpen ? menuId : undefined\"\r\n :aria-disabled=\"disabled || undefined\"\r\n @pointermove=\"onPointerMove\"\r\n @pointerleave=\"onPointerLeave\"\r\n @keydown=\"onKeydown\"\r\n @click=\"onClick\"\r\n >\r\n <span v-if=\"$slots.leading\" class=\"j-menu__icon\"><slot name=\"leading\" /></span>\r\n <span class=\"j-menu__text\">{{ label }}</span>\r\n <IconChevronRight class=\"j-menu__chevron\" />\r\n </div>\r\n <JMenuContent\r\n :id=\"menuId\"\r\n ref=\"sub\"\r\n :open=\"isOpen\"\r\n :reference=\"item\"\r\n :labelledby=\"itemId\"\r\n placement=\"right-start\"\r\n :offset=\"{ mainAxis: 2, crossAxis: -5 }\"\r\n :initial-focus=\"initialFocus\"\r\n is-sub\r\n @escape=\"closeSub(true)\"\r\n @back=\"closeSub(true)\"\r\n @outside=\"onOutside\"\r\n @pointerenter=\"level.cancelSubClose()\"\r\n >\r\n <slot />\r\n </JMenuContent>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport { computed, inject, nextTick, ref, useId } from 'vue'\r\nimport { IconChevronRight } from '../../icons'\r\nimport { focusElement } from '../../utils/dom'\r\nimport { getMenuItems, MENU_LEVEL, MENU_ROOT } from './context'\r\nimport JMenuContent from './JMenuContent.vue'\r\n\r\nexport interface DropdownMenuSubProps {\r\n label: string\r\n disabled?: boolean\r\n}\r\n\r\nconst props = defineProps<DropdownMenuSubProps>()\r\n\r\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\r\n\r\nconst root = inject(MENU_ROOT)!\r\nconst level = inject(MENU_LEVEL)!\r\n\r\nconst id = useId()\r\nconst itemId = `j-${id}-subtrigger`\r\nconst menuId = `j-${id}-submenu`\r\nconst item = ref<HTMLElement | null>(null)\r\nconst sub = ref<InstanceType<typeof JMenuContent> | null>(null)\r\nconst initialFocus = ref<'first' | 'none'>('none')\r\nconst isOpen = computed(() => level.openSub.value === menuId)\r\n\r\nlet openTimer: ReturnType<typeof setTimeout> | undefined\r\n\r\nfunction openSub(focus: 'first' | 'none') {\r\n if (props.disabled) return\r\n initialFocus.value = focus\r\n level.setOpenSub(menuId)\r\n}\r\n\r\nfunction closeSub(returnFocus: boolean) {\r\n if (level.openSub.value === menuId) level.setOpenSub(null)\r\n if (returnFocus) nextTick(() => focusElement(item.value))\r\n}\r\n\r\nfunction onPointerMove() {\r\n level.cancelSubClose()\r\n if (props.disabled) return\r\n if (document.activeElement !== item.value) focusElement(item.value)\r\n if (isOpen.value) return\r\n clearTimeout(openTimer)\r\n openTimer = setTimeout(() => openSub('none'), 90)\r\n}\r\n\r\nfunction onPointerLeave() {\r\n clearTimeout(openTimer)\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (event.key === 'ArrowRight' || event.key === 'Enter' || event.key === ' ') {\r\n event.preventDefault()\r\n event.stopPropagation()\r\n if (isOpen.value) focusElement(getMenuItems(sub.value?.content ?? null)[0])\r\n else openSub('first')\r\n }\r\n}\r\n\r\nfunction onClick() {\r\n openSub('first')\r\n}\r\n\r\n// A press outside the submenu either lands back in the parent (close just the\r\n// submenu) or somewhere else entirely (close the whole menu).\r\nfunction onOutside(event: PointerEvent) {\r\n const target = event.target as Node\r\n if (level.content.value?.contains(target)) closeSub(false)\r\n else root.close(false)\r\n}\r\n</script>\r\n\r\n<template>\r\n <div\r\n :id=\"itemId\"\r\n ref=\"item\"\r\n role=\"menuitem\"\r\n tabindex=\"-1\"\r\n class=\"j-menu__item\"\r\n aria-haspopup=\"menu\"\r\n :aria-expanded=\"isOpen\"\r\n :aria-controls=\"isOpen ? menuId : undefined\"\r\n :aria-disabled=\"disabled || undefined\"\r\n @pointermove=\"onPointerMove\"\r\n @pointerleave=\"onPointerLeave\"\r\n @keydown=\"onKeydown\"\r\n @click=\"onClick\"\r\n >\r\n <span v-if=\"$slots.leading\" class=\"j-menu__icon\"><slot name=\"leading\" /></span>\r\n <span class=\"j-menu__text\">{{ label }}</span>\r\n <IconChevronRight class=\"j-menu__chevron\" />\r\n </div>\r\n <JMenuContent\r\n :id=\"menuId\"\r\n ref=\"sub\"\r\n :open=\"isOpen\"\r\n :reference=\"item\"\r\n :labelledby=\"itemId\"\r\n placement=\"right-start\"\r\n :offset=\"{ mainAxis: 2, crossAxis: -5 }\"\r\n :initial-focus=\"initialFocus\"\r\n is-sub\r\n @escape=\"closeSub(true)\"\r\n @back=\"closeSub(true)\"\r\n @outside=\"onOutside\"\r\n @pointerenter=\"level.cancelSubClose()\"\r\n >\r\n <slot />\r\n </JMenuContent>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nexport interface EmptyStateProps {\r\n title?: string\r\n description?: string\r\n size?: 'sm' | 'md' | 'lg'\r\n}\r\n\r\nwithDefaults(defineProps<EmptyStateProps>(), { size: 'md' })\r\n\r\ndefineSlots<{\r\n /** Icon or illustration shown above the title. */\r\n icon?: () => unknown\r\n default?: () => unknown\r\n /** Call to action, typically a `JButton`. */\r\n actions?: () => unknown\r\n}>()\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-empty-state\" :class=\"`j-empty-state--${size}`\">\r\n <div v-if=\"$slots.icon\" class=\"j-empty-state__icon\">\r\n <slot name=\"icon\" />\r\n </div>\r\n <p v-if=\"title\" class=\"j-empty-state__title\">\r\n {{ title }}\r\n </p>\r\n <div v-if=\"description || $slots.default\" class=\"j-empty-state__description\">\r\n <slot>{{ description }}</slot>\r\n </div>\r\n <div v-if=\"$slots.actions\" class=\"j-empty-state__actions\">\r\n <slot name=\"actions\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-empty-state {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n max-width: 24rem;\r\n margin: 0 auto;\r\n padding: var(--juxt-space-8) var(--juxt-space-4);\r\n text-align: center;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-empty-state--sm {\r\n max-width: 18rem;\r\n padding: var(--juxt-space-6) var(--juxt-space-3);\r\n}\r\n\r\n.j-empty-state--lg {\r\n max-width: 28rem;\r\n padding: var(--juxt-space-12) var(--juxt-space-4);\r\n}\r\n\r\n.j-empty-state__icon {\r\n display: grid;\r\n place-items: center;\r\n width: 2.75rem;\r\n height: 2.75rem;\r\n margin-bottom: var(--juxt-space-4);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-surface-sunken);\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-empty-state--lg .j-empty-state__icon {\r\n width: 3.5rem;\r\n height: 3.5rem;\r\n}\r\n\r\n.j-empty-state__icon > svg {\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n}\r\n\r\n.j-empty-state--lg .j-empty-state__icon > svg {\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n}\r\n\r\n.j-empty-state__title {\r\n margin: 0;\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-lg);\r\n font-weight: var(--juxt-weight-semibold);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-empty-state__description {\r\n margin-top: var(--juxt-space-1-5);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-empty-state__description > p {\r\n margin: 0;\r\n}\r\n\r\n.j-empty-state__actions {\r\n display: flex;\r\n flex-wrap: wrap;\r\n justify-content: center;\r\n gap: var(--juxt-space-2);\r\n margin-top: var(--juxt-space-5);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nexport interface EmptyStateProps {\r\n title?: string\r\n description?: string\r\n size?: 'sm' | 'md' | 'lg'\r\n}\r\n\r\nwithDefaults(defineProps<EmptyStateProps>(), { size: 'md' })\r\n\r\ndefineSlots<{\r\n /** Icon or illustration shown above the title. */\r\n icon?: () => unknown\r\n default?: () => unknown\r\n /** Call to action, typically a `JButton`. */\r\n actions?: () => unknown\r\n}>()\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-empty-state\" :class=\"`j-empty-state--${size}`\">\r\n <div v-if=\"$slots.icon\" class=\"j-empty-state__icon\">\r\n <slot name=\"icon\" />\r\n </div>\r\n <p v-if=\"title\" class=\"j-empty-state__title\">\r\n {{ title }}\r\n </p>\r\n <div v-if=\"description || $slots.default\" class=\"j-empty-state__description\">\r\n <slot>{{ description }}</slot>\r\n </div>\r\n <div v-if=\"$slots.actions\" class=\"j-empty-state__actions\">\r\n <slot name=\"actions\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-empty-state {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n max-width: 24rem;\r\n margin: 0 auto;\r\n padding: var(--juxt-space-8) var(--juxt-space-4);\r\n text-align: center;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-empty-state--sm {\r\n max-width: 18rem;\r\n padding: var(--juxt-space-6) var(--juxt-space-3);\r\n}\r\n\r\n.j-empty-state--lg {\r\n max-width: 28rem;\r\n padding: var(--juxt-space-12) var(--juxt-space-4);\r\n}\r\n\r\n.j-empty-state__icon {\r\n display: grid;\r\n place-items: center;\r\n width: 2.75rem;\r\n height: 2.75rem;\r\n margin-bottom: var(--juxt-space-4);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-surface-sunken);\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-empty-state--lg .j-empty-state__icon {\r\n width: 3.5rem;\r\n height: 3.5rem;\r\n}\r\n\r\n.j-empty-state__icon > svg {\r\n width: 1.25rem;\r\n height: 1.25rem;\r\n}\r\n\r\n.j-empty-state--lg .j-empty-state__icon > svg {\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n}\r\n\r\n.j-empty-state__title {\r\n margin: 0;\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-lg);\r\n font-weight: var(--juxt-weight-semibold);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-empty-state__description {\r\n margin-top: var(--juxt-space-1-5);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-empty-state__description > p {\r\n margin: 0;\r\n}\r\n\r\n.j-empty-state__actions {\r\n display: flex;\r\n flex-wrap: wrap;\r\n justify-content: center;\r\n gap: var(--juxt-space-2);\r\n margin-top: var(--juxt-space-5);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nexport interface FooterProps {\n /** Maximum content width; any CSS length. `none` spans the full width. */\n maxWidth?: string\n /** Hairline above the footer. */\n bordered?: boolean\n}\n\nconst props = withDefaults(defineProps<FooterProps>(), { maxWidth: '72rem', bordered: true })\n\ndefineSlots<{\n /** Logo and a short line about the product. */\n brand?: () => unknown\n /** Link columns, typically `JFooterColumn`s. */\n default?: () => unknown\n /** The last line: copyright, legal links, social icons. */\n bottom?: () => unknown\n}>()\n\nconst style = computed(() => ({ '--j-footer-max': props.maxWidth === 'none' ? 'none' : props.maxWidth }))\n</script>\n\n<template>\n <footer class=\"j-footer\" :class=\"{ 'is-bordered': bordered }\" :style=\"style\">\n <div class=\"j-footer__inner\">\n <div v-if=\"$slots.brand || $slots.default\" class=\"j-footer__top\">\n <div v-if=\"$slots.brand\" class=\"j-footer__brand\">\n <slot name=\"brand\" />\n </div>\n <div v-if=\"$slots.default\" class=\"j-footer__columns\">\n <slot />\n </div>\n </div>\n <div v-if=\"$slots.bottom\" class=\"j-footer__bottom\">\n <slot name=\"bottom\" />\n </div>\n </div>\n </footer>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-footer {\n background: var(--juxt-bg);\n color: var(--juxt-fg-secondary);\n font-family: var(--juxt-font-sans);\n font-size: var(--juxt-text-sm);\n}\n\n.j-footer.is-bordered {\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-footer.is-bordered,\n[data-theme='dark'] .j-footer.is-bordered {\n border-top-color: var(--juxt-border);\n}\n\n.j-footer__inner {\n max-width: var(--j-footer-max);\n margin: 0 auto;\n padding: var(--juxt-space-12) var(--juxt-space-4) var(--juxt-space-8);\n}\n\n.j-footer__top {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--juxt-space-10);\n}\n\n.j-footer__brand {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-3);\n max-width: 18rem;\n color: var(--juxt-fg-muted);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-footer__columns {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--juxt-space-8) var(--juxt-space-6);\n}\n\n@media (min-width: 768px) {\n .j-footer__top {\n grid-template-columns: minmax(12rem, 1fr) minmax(0, 2fr);\n }\n\n .j-footer__columns {\n grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));\n }\n}\n\n.j-footer__bottom {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--juxt-space-3) var(--juxt-space-6);\n margin-top: var(--juxt-space-12);\n padding-top: var(--juxt-space-6);\n border-top: 1px solid var(--juxt-border-subtle);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n}\n\n.dark .j-footer__bottom,\n[data-theme='dark'] .j-footer__bottom {\n border-top-color: var(--juxt-border);\n}\n\n.j-footer__bottom a {\n color: inherit;\n text-decoration: none;\n transition: color var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-footer__bottom a:hover {\n color: var(--juxt-fg);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nexport interface FooterProps {\n /** Maximum content width; any CSS length. `none` spans the full width. */\n maxWidth?: string\n /** Hairline above the footer. */\n bordered?: boolean\n}\n\nconst props = withDefaults(defineProps<FooterProps>(), { maxWidth: '72rem', bordered: true })\n\ndefineSlots<{\n /** Logo and a short line about the product. */\n brand?: () => unknown\n /** Link columns, typically `JFooterColumn`s. */\n default?: () => unknown\n /** The last line: copyright, legal links, social icons. */\n bottom?: () => unknown\n}>()\n\nconst style = computed(() => ({ '--j-footer-max': props.maxWidth === 'none' ? 'none' : props.maxWidth }))\n</script>\n\n<template>\n <footer class=\"j-footer\" :class=\"{ 'is-bordered': bordered }\" :style=\"style\">\n <div class=\"j-footer__inner\">\n <div v-if=\"$slots.brand || $slots.default\" class=\"j-footer__top\">\n <div v-if=\"$slots.brand\" class=\"j-footer__brand\">\n <slot name=\"brand\" />\n </div>\n <div v-if=\"$slots.default\" class=\"j-footer__columns\">\n <slot />\n </div>\n </div>\n <div v-if=\"$slots.bottom\" class=\"j-footer__bottom\">\n <slot name=\"bottom\" />\n </div>\n </div>\n </footer>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-footer {\n background: var(--juxt-bg);\n color: var(--juxt-fg-secondary);\n font-family: var(--juxt-font-sans);\n font-size: var(--juxt-text-sm);\n}\n\n.j-footer.is-bordered {\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-footer.is-bordered,\n[data-theme='dark'] .j-footer.is-bordered {\n border-top-color: var(--juxt-border);\n}\n\n.j-footer__inner {\n max-width: var(--j-footer-max);\n margin: 0 auto;\n padding: var(--juxt-space-12) var(--juxt-space-4) var(--juxt-space-8);\n}\n\n.j-footer__top {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--juxt-space-10);\n}\n\n.j-footer__brand {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-3);\n max-width: 18rem;\n color: var(--juxt-fg-muted);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-footer__columns {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--juxt-space-8) var(--juxt-space-6);\n}\n\n@media (min-width: 768px) {\n .j-footer__top {\n grid-template-columns: minmax(12rem, 1fr) minmax(0, 2fr);\n }\n\n .j-footer__columns {\n grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));\n }\n}\n\n.j-footer__bottom {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--juxt-space-3) var(--juxt-space-6);\n margin-top: var(--juxt-space-12);\n padding-top: var(--juxt-space-6);\n border-top: 1px solid var(--juxt-border-subtle);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n}\n\n.dark .j-footer__bottom,\n[data-theme='dark'] .j-footer__bottom {\n border-top-color: var(--juxt-border);\n}\n\n.j-footer__bottom a {\n color: inherit;\n text-decoration: none;\n transition: color var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-footer__bottom a:hover {\n color: var(--juxt-fg);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useId } from 'vue'\n\nexport interface FooterColumnProps {\n title: string\n}\n\ndefineProps<FooterColumnProps>()\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst titleId = `j-${useId()}-title`\n</script>\n\n<template>\n <nav class=\"j-footer-column\" :aria-labelledby=\"titleId\">\n <p :id=\"titleId\" class=\"j-footer-column__title\">\n {{ title }}\n </p>\n <div class=\"j-footer-column__links\">\n <slot />\n </div>\n </nav>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-footer-column {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-3);\n min-width: 0;\n}\n\n.j-footer-column__title {\n margin: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-footer-column__links {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--juxt-space-2);\n}\n\n.j-footer-column__links a {\n border-radius: var(--juxt-radius-xs);\n color: var(--juxt-fg-muted);\n text-decoration: none;\n outline: 2px solid transparent;\n outline-offset: 0;\n transition:\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-footer-column__links a:hover {\n color: var(--juxt-fg);\n}\n\n.j-footer-column__links a:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useId } from 'vue'\n\nexport interface FooterColumnProps {\n title: string\n}\n\ndefineProps<FooterColumnProps>()\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst titleId = `j-${useId()}-title`\n</script>\n\n<template>\n <nav class=\"j-footer-column\" :aria-labelledby=\"titleId\">\n <p :id=\"titleId\" class=\"j-footer-column__title\">\n {{ title }}\n </p>\n <div class=\"j-footer-column__links\">\n <slot />\n </div>\n </nav>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-footer-column {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-3);\n min-width: 0;\n}\n\n.j-footer-column__title {\n margin: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-footer-column__links {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--juxt-space-2);\n}\n\n.j-footer-column__links a {\n border-radius: var(--juxt-radius-xs);\n color: var(--juxt-fg-muted);\n text-decoration: none;\n outline: 2px solid transparent;\n outline-offset: 0;\n transition:\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-footer-column__links a:hover {\n color: var(--juxt-fg);\n}\n\n.j-footer-column__links a:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n}\n</style>\n","<script setup lang=\"ts\">\r\nimport type { FieldProps } from '../field/useField'\r\nimport { ref } from 'vue'\r\nimport JFieldShell from '../field/JFieldShell.vue'\r\nimport { useField } from '../field/useField'\r\n\r\nexport type InputSize = 'sm' | 'md' | 'lg'\r\n\r\nexport interface InputProps extends FieldProps {\r\n type?: string\r\n placeholder?: string\r\n size?: InputSize\r\n readonly?: boolean\r\n name?: string\r\n autocomplete?: string\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = withDefaults(defineProps<InputProps>(), {\r\n type: 'text',\r\n size: 'md',\r\n error: undefined,\r\n})\r\n\r\nconst model = defineModel<string | number | null>({ default: '' })\r\n\r\ndefineSlots<{\r\n leading?: () => unknown\r\n trailing?: () => unknown\r\n label?: () => unknown\r\n}>()\r\n\r\nconst field = useField(props)\r\nconst input = ref<HTMLInputElement | null>(null)\r\n\r\n// Clicking the frame (padding, icons) focuses the input without a blur flicker.\r\nfunction onFramePointerDown(event: PointerEvent) {\r\n if (event.target === input.value || props.disabled) return\r\n if ((event.target as HTMLElement).closest('button, a, input, select, textarea, [tabindex]')) return\r\n event.preventDefault()\r\n input.value?.focus()\r\n}\r\n\r\ndefineExpose({\r\n /** The underlying `<input>` element. */\r\n input,\r\n focus: () => input.value?.focus(),\r\n select: () => input.value?.select(),\r\n})\r\n</script>\r\n\r\n<template>\r\n <JFieldShell\r\n :id=\"field.id.value\"\r\n :class=\"$attrs.class\"\r\n :style=\"$attrs.style\"\r\n :label-id=\"field.labelId.value\"\r\n :description-id=\"field.descriptionId.value\"\r\n :error-id=\"field.errorId.value\"\r\n :label=\"label\"\r\n :description=\"description\"\r\n :error-message=\"field.errorMessage.value\"\r\n :show-description=\"field.showDescription.value\"\r\n :required=\"required\"\r\n :disabled=\"disabled\"\r\n >\r\n <template v-if=\"$slots.label\" #label>\r\n <slot name=\"label\" />\r\n </template>\r\n <div\r\n class=\"j-control j-input\"\r\n :class=\"[`j-control--${size}`, { 'is-invalid': field.invalid.value, 'is-disabled': disabled, 'is-readonly': readonly }]\"\r\n @pointerdown=\"onFramePointerDown\"\r\n >\r\n <span v-if=\"$slots.leading\" class=\"j-control__adornment j-control__adornment--leading\">\r\n <slot name=\"leading\" />\r\n </span>\r\n <input\r\n :id=\"field.id.value\"\r\n ref=\"input\"\r\n v-model=\"model\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n class=\"j-input__native\"\r\n :type=\"type\"\r\n :name=\"name\"\r\n :placeholder=\"placeholder\"\r\n :disabled=\"disabled\"\r\n :readonly=\"readonly\"\r\n :required=\"required\"\r\n :autocomplete=\"autocomplete\"\r\n :aria-invalid=\"field.invalid.value || undefined\"\r\n :aria-describedby=\"field.describedBy.value\"\r\n />\r\n <span v-if=\"$slots.trailing\" class=\"j-control__adornment j-control__adornment--trailing\">\r\n <slot name=\"trailing\" />\r\n </span>\r\n </div>\r\n </JFieldShell>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-input__native {\r\n flex: 1;\r\n width: 100%;\r\n min-width: 0;\r\n height: calc(var(--j-control-height) - 2px);\r\n padding: 0;\r\n border: 0;\r\n outline: 0;\r\n background: transparent;\r\n color: inherit;\r\n font: inherit;\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-input__native::placeholder {\r\n color: var(--juxt-fg-muted);\r\n opacity: 1;\r\n}\r\n\r\n.j-input__native:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-input__native::-webkit-search-cancel-button {\r\n display: none;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { FieldProps } from '../field/useField'\r\nimport { ref } from 'vue'\r\nimport JFieldShell from '../field/JFieldShell.vue'\r\nimport { useField } from '../field/useField'\r\n\r\nexport type InputSize = 'sm' | 'md' | 'lg'\r\n\r\nexport interface InputProps extends FieldProps {\r\n type?: string\r\n placeholder?: string\r\n size?: InputSize\r\n readonly?: boolean\r\n name?: string\r\n autocomplete?: string\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = withDefaults(defineProps<InputProps>(), {\r\n type: 'text',\r\n size: 'md',\r\n error: undefined,\r\n})\r\n\r\nconst model = defineModel<string | number | null>({ default: '' })\r\n\r\ndefineSlots<{\r\n leading?: () => unknown\r\n trailing?: () => unknown\r\n label?: () => unknown\r\n}>()\r\n\r\nconst field = useField(props)\r\nconst input = ref<HTMLInputElement | null>(null)\r\n\r\n// Clicking the frame (padding, icons) focuses the input without a blur flicker.\r\nfunction onFramePointerDown(event: PointerEvent) {\r\n if (event.target === input.value || props.disabled) return\r\n if ((event.target as HTMLElement).closest('button, a, input, select, textarea, [tabindex]')) return\r\n event.preventDefault()\r\n input.value?.focus()\r\n}\r\n\r\ndefineExpose({\r\n /** The underlying `<input>` element. */\r\n input,\r\n focus: () => input.value?.focus(),\r\n select: () => input.value?.select(),\r\n})\r\n</script>\r\n\r\n<template>\r\n <JFieldShell\r\n :id=\"field.id.value\"\r\n :class=\"$attrs.class\"\r\n :style=\"$attrs.style\"\r\n :label-id=\"field.labelId.value\"\r\n :description-id=\"field.descriptionId.value\"\r\n :error-id=\"field.errorId.value\"\r\n :label=\"label\"\r\n :description=\"description\"\r\n :error-message=\"field.errorMessage.value\"\r\n :show-description=\"field.showDescription.value\"\r\n :required=\"required\"\r\n :disabled=\"disabled\"\r\n >\r\n <template v-if=\"$slots.label\" #label>\r\n <slot name=\"label\" />\r\n </template>\r\n <div\r\n class=\"j-control j-input\"\r\n :class=\"[`j-control--${size}`, { 'is-invalid': field.invalid.value, 'is-disabled': disabled, 'is-readonly': readonly }]\"\r\n @pointerdown=\"onFramePointerDown\"\r\n >\r\n <span v-if=\"$slots.leading\" class=\"j-control__adornment j-control__adornment--leading\">\r\n <slot name=\"leading\" />\r\n </span>\r\n <input\r\n :id=\"field.id.value\"\r\n ref=\"input\"\r\n v-model=\"model\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n class=\"j-input__native\"\r\n :type=\"type\"\r\n :name=\"name\"\r\n :placeholder=\"placeholder\"\r\n :disabled=\"disabled\"\r\n :readonly=\"readonly\"\r\n :required=\"required\"\r\n :autocomplete=\"autocomplete\"\r\n :aria-invalid=\"field.invalid.value || undefined\"\r\n :aria-describedby=\"field.describedBy.value\"\r\n />\r\n <span v-if=\"$slots.trailing\" class=\"j-control__adornment j-control__adornment--trailing\">\r\n <slot name=\"trailing\" />\r\n </span>\r\n </div>\r\n </JFieldShell>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-input__native {\r\n flex: 1;\r\n width: 100%;\r\n min-width: 0;\r\n height: calc(var(--j-control-height) - 2px);\r\n padding: 0;\r\n border: 0;\r\n outline: 0;\r\n background: transparent;\r\n color: inherit;\r\n font: inherit;\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-input__native::placeholder {\r\n color: var(--juxt-fg-muted);\r\n opacity: 1;\r\n}\r\n\r\n.j-input__native:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-input__native::-webkit-search-cancel-button {\r\n display: none;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'\nimport { useLayer } from '../../composables/useLayer'\nimport { focusElement } from '../../utils/dom'\nimport JBurger from '../burger/JBurger.vue'\n\nexport interface NavbarProps {\n /** Stick to the top of the viewport while scrolling. */\n sticky?: boolean\n /** Hairline under the bar. */\n bordered?: boolean\n /** Accessible name of the navigation landmark. */\n label?: string\n /** Width below which links collapse behind the burger. `never` keeps them inline. */\n collapse?: 'sm' | 'md' | 'lg' | 'never'\n /** Maximum content width; any CSS length. `none` spans the full width. */\n maxWidth?: string\n}\n\nconst props = withDefaults(defineProps<NavbarProps>(), {\n sticky: true,\n bordered: true,\n label: 'Main',\n collapse: 'md',\n maxWidth: '72rem',\n})\n\n/** Mobile menu state. */\nconst open = defineModel<boolean>('open', { default: false })\n\ndefineSlots<{\n /** Logo or product name, usually a link home. */\n brand?: () => unknown\n /** Navigation links, typically `JNavbarLink`s. Shown inline, or in the mobile menu. */\n default?: () => unknown\n /** Always visible on the right: search, theme switch, avatar. */\n actions?: () => unknown\n /** Extra content at the bottom of the mobile menu, e.g. sign-in buttons. */\n 'menu-footer'?: () => unknown\n}>()\n\nconst menuId = `j-${useId()}-menu`\nconst root = ref<HTMLElement | null>(null)\nconst menu = ref<HTMLElement | null>(null)\n\nconst breakpoints = { sm: 640, md: 768, lg: 1024 }\n\nfunction close(returnFocus = false) {\n if (!open.value) return\n open.value = false\n if (returnFocus) nextTick(() => focusElement(root.value?.querySelector<HTMLElement>('.j-burger')))\n}\n\nuseLayer({\n active: open,\n elements: () => [root.value],\n onEscape: () => close(true),\n onPointerDownOutside: () => close(),\n})\n\n// Choosing a destination closes the menu; opening a nested disclosure doesn't.\nfunction onMenuClick(event: MouseEvent) {\n const target = (event.target as HTMLElement).closest('a, button')\n if (target && !target.hasAttribute('aria-expanded')) close()\n}\n\n// Growing past the breakpoint makes the menu irrelevant, so drop it.\nlet media: MediaQueryList | undefined\nconst onMediaChange = (event: MediaQueryListEvent) => event.matches && close()\nonMounted(() => {\n if (props.collapse === 'never') return\n media = window.matchMedia(`(min-width: ${breakpoints[props.collapse]}px)`)\n media.addEventListener('change', onMediaChange)\n})\nonBeforeUnmount(() => media?.removeEventListener('change', onMediaChange))\n\nwatch(open, async (value) => {\n if (!value) return\n await nextTick()\n const first = menu.value?.querySelector<HTMLElement>('a, button')\n first?.focus({ preventScroll: true })\n})\n\nconst style = computed(() => ({ '--j-navbar-max': props.maxWidth === 'none' ? 'none' : props.maxWidth }))\n</script>\n\n<template>\n <header\n ref=\"root\"\n class=\"j-navbar\"\n :class=\"[`j-navbar--collapse-${collapse}`, { 'is-sticky': sticky, 'is-bordered': bordered, 'is-open': open }]\"\n :style=\"style\"\n >\n <div class=\"j-navbar__bar\">\n <div v-if=\"$slots.brand\" class=\"j-navbar__brand\">\n <slot name=\"brand\" />\n </div>\n <nav v-if=\"$slots.default\" class=\"j-navbar__links\" :aria-label=\"label\">\n <slot />\n </nav>\n <div class=\"j-navbar__end\">\n <div v-if=\"$slots.actions\" class=\"j-navbar__actions\">\n <slot name=\"actions\" />\n </div>\n <JBurger\n v-if=\"collapse !== 'never' && ($slots.default || $slots['menu-footer'])\"\n v-model=\"open\"\n class=\"j-navbar__burger\"\n :controls=\"menuId\"\n />\n </div>\n </div>\n <Transition name=\"j-navbar-menu\">\n <div v-show=\"open\" :id=\"menuId\" ref=\"menu\" class=\"j-navbar__menu\" @click=\"onMenuClick\">\n <nav v-if=\"$slots.default\" class=\"j-navbar__menu-links\" :aria-label=\"label\">\n <slot />\n </nav>\n <div v-if=\"$slots['menu-footer']\" class=\"j-navbar__menu-footer\">\n <slot name=\"menu-footer\" />\n </div>\n </div>\n </Transition>\n </header>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-navbar {\n --j-navbar-height: 3.5rem;\n\n position: relative;\n z-index: var(--juxt-z-sticky);\n background: var(--juxt-bg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-navbar.is-sticky {\n position: sticky;\n top: 0;\n}\n\n.j-navbar.is-bordered {\n box-shadow: inset 0 -1px 0 var(--juxt-border-subtle);\n}\n\n.dark .j-navbar.is-bordered,\n[data-theme='dark'] .j-navbar.is-bordered {\n box-shadow: inset 0 -1px 0 var(--juxt-border);\n}\n\n.j-navbar__bar {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-6);\n max-width: var(--j-navbar-max);\n height: var(--j-navbar-height);\n margin: 0 auto;\n padding: 0 var(--juxt-space-4);\n}\n\n.j-navbar__brand {\n display: flex;\n flex: none;\n align-items: center;\n}\n\n.j-navbar__links {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1);\n min-width: 0;\n}\n\n.j-navbar__end {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n margin-left: auto;\n}\n\n.j-navbar__actions {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n}\n\n.j-navbar__burger {\n display: none;\n margin-right: calc(var(--juxt-space-2) * -1);\n}\n\n/* Mobile menu: a panel that drops from the bar, links stacked with big targets. */\n.j-navbar__menu {\n position: absolute;\n top: 100%;\n right: 0;\n left: 0;\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-4);\n max-height: calc(100dvh - var(--j-navbar-height));\n padding: var(--juxt-space-3) var(--juxt-space-4) var(--juxt-space-5);\n overflow-y: auto;\n border-bottom: 1px solid var(--juxt-border);\n background: var(--juxt-bg);\n box-shadow: var(--juxt-shadow-md);\n}\n\n.j-navbar__menu-links {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n.j-navbar__menu-links .j-navbar-link {\n justify-content: flex-start;\n height: 2.75rem;\n padding: 0 var(--juxt-space-3);\n font-size: var(--juxt-text-lg);\n}\n\n.j-navbar__menu-footer {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-2);\n padding-top: var(--juxt-space-4);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.j-navbar__menu-footer > * {\n width: 100%;\n}\n\n.j-navbar-menu-enter-active {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-navbar-menu-leave-active {\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\n}\n\n.j-navbar-menu-enter-from,\n.j-navbar-menu-leave-to {\n opacity: 0;\n transform: translateY(calc(var(--juxt-motion-shift) * -1));\n}\n\n/* Collapse points. Above them the menu never shows, below them the links hide. */\n.j-navbar--collapse-never .j-navbar__menu {\n display: none !important;\n}\n\n@media (max-width: 639.98px) {\n .j-navbar--collapse-sm .j-navbar__links { display: none; }\n .j-navbar--collapse-sm .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 640px) {\n .j-navbar--collapse-sm .j-navbar__menu { display: none !important; }\n}\n\n@media (max-width: 767.98px) {\n .j-navbar--collapse-md .j-navbar__links { display: none; }\n .j-navbar--collapse-md .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 768px) {\n .j-navbar--collapse-md .j-navbar__menu { display: none !important; }\n}\n\n@media (max-width: 1023.98px) {\n .j-navbar--collapse-lg .j-navbar__links { display: none; }\n .j-navbar--collapse-lg .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 1024px) {\n .j-navbar--collapse-lg .j-navbar__menu { display: none !important; }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'\nimport { useLayer } from '../../composables/useLayer'\nimport { focusElement } from '../../utils/dom'\nimport JBurger from '../burger/JBurger.vue'\n\nexport interface NavbarProps {\n /** Stick to the top of the viewport while scrolling. */\n sticky?: boolean\n /** Hairline under the bar. */\n bordered?: boolean\n /** Accessible name of the navigation landmark. */\n label?: string\n /** Width below which links collapse behind the burger. `never` keeps them inline. */\n collapse?: 'sm' | 'md' | 'lg' | 'never'\n /** Maximum content width; any CSS length. `none` spans the full width. */\n maxWidth?: string\n}\n\nconst props = withDefaults(defineProps<NavbarProps>(), {\n sticky: true,\n bordered: true,\n label: 'Main',\n collapse: 'md',\n maxWidth: '72rem',\n})\n\n/** Mobile menu state. */\nconst open = defineModel<boolean>('open', { default: false })\n\ndefineSlots<{\n /** Logo or product name, usually a link home. */\n brand?: () => unknown\n /** Navigation links, typically `JNavbarLink`s. Shown inline, or in the mobile menu. */\n default?: () => unknown\n /** Always visible on the right: search, theme switch, avatar. */\n actions?: () => unknown\n /** Extra content at the bottom of the mobile menu, e.g. sign-in buttons. */\n 'menu-footer'?: () => unknown\n}>()\n\nconst menuId = `j-${useId()}-menu`\nconst root = ref<HTMLElement | null>(null)\nconst menu = ref<HTMLElement | null>(null)\n\nconst breakpoints = { sm: 640, md: 768, lg: 1024 }\n\nfunction close(returnFocus = false) {\n if (!open.value) return\n open.value = false\n if (returnFocus) nextTick(() => focusElement(root.value?.querySelector<HTMLElement>('.j-burger')))\n}\n\nuseLayer({\n active: open,\n elements: () => [root.value],\n onEscape: () => close(true),\n onPointerDownOutside: () => close(),\n})\n\n// Choosing a destination closes the menu; opening a nested disclosure doesn't.\nfunction onMenuClick(event: MouseEvent) {\n const target = (event.target as HTMLElement).closest('a, button')\n if (target && !target.hasAttribute('aria-expanded')) close()\n}\n\n// Growing past the breakpoint makes the menu irrelevant, so drop it.\nlet media: MediaQueryList | undefined\nconst onMediaChange = (event: MediaQueryListEvent) => event.matches && close()\nonMounted(() => {\n if (props.collapse === 'never') return\n media = window.matchMedia(`(min-width: ${breakpoints[props.collapse]}px)`)\n media.addEventListener('change', onMediaChange)\n})\nonBeforeUnmount(() => media?.removeEventListener('change', onMediaChange))\n\nwatch(open, async (value) => {\n if (!value) return\n await nextTick()\n const first = menu.value?.querySelector<HTMLElement>('a, button')\n first?.focus({ preventScroll: true })\n})\n\nconst style = computed(() => ({ '--j-navbar-max': props.maxWidth === 'none' ? 'none' : props.maxWidth }))\n</script>\n\n<template>\n <header\n ref=\"root\"\n class=\"j-navbar\"\n :class=\"[`j-navbar--collapse-${collapse}`, { 'is-sticky': sticky, 'is-bordered': bordered, 'is-open': open }]\"\n :style=\"style\"\n >\n <div class=\"j-navbar__bar\">\n <div v-if=\"$slots.brand\" class=\"j-navbar__brand\">\n <slot name=\"brand\" />\n </div>\n <nav v-if=\"$slots.default\" class=\"j-navbar__links\" :aria-label=\"label\">\n <slot />\n </nav>\n <div class=\"j-navbar__end\">\n <div v-if=\"$slots.actions\" class=\"j-navbar__actions\">\n <slot name=\"actions\" />\n </div>\n <JBurger\n v-if=\"collapse !== 'never' && ($slots.default || $slots['menu-footer'])\"\n v-model=\"open\"\n class=\"j-navbar__burger\"\n :controls=\"menuId\"\n />\n </div>\n </div>\n <Transition name=\"j-navbar-menu\">\n <div v-show=\"open\" :id=\"menuId\" ref=\"menu\" class=\"j-navbar__menu\" @click=\"onMenuClick\">\n <nav v-if=\"$slots.default\" class=\"j-navbar__menu-links\" :aria-label=\"label\">\n <slot />\n </nav>\n <div v-if=\"$slots['menu-footer']\" class=\"j-navbar__menu-footer\">\n <slot name=\"menu-footer\" />\n </div>\n </div>\n </Transition>\n </header>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-navbar {\n --j-navbar-height: 3.5rem;\n\n position: relative;\n z-index: var(--juxt-z-sticky);\n background: var(--juxt-bg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-navbar.is-sticky {\n position: sticky;\n top: 0;\n}\n\n.j-navbar.is-bordered {\n box-shadow: inset 0 -1px 0 var(--juxt-border-subtle);\n}\n\n.dark .j-navbar.is-bordered,\n[data-theme='dark'] .j-navbar.is-bordered {\n box-shadow: inset 0 -1px 0 var(--juxt-border);\n}\n\n.j-navbar__bar {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-6);\n max-width: var(--j-navbar-max);\n height: var(--j-navbar-height);\n margin: 0 auto;\n padding: 0 var(--juxt-space-4);\n}\n\n.j-navbar__brand {\n display: flex;\n flex: none;\n align-items: center;\n}\n\n.j-navbar__links {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1);\n min-width: 0;\n}\n\n.j-navbar__end {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n margin-left: auto;\n}\n\n.j-navbar__actions {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n}\n\n.j-navbar__burger {\n display: none;\n margin-right: calc(var(--juxt-space-2) * -1);\n}\n\n/* Mobile menu: a panel that drops from the bar, links stacked with big targets. */\n.j-navbar__menu {\n position: absolute;\n top: 100%;\n right: 0;\n left: 0;\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-4);\n max-height: calc(100dvh - var(--j-navbar-height));\n padding: var(--juxt-space-3) var(--juxt-space-4) var(--juxt-space-5);\n overflow-y: auto;\n border-bottom: 1px solid var(--juxt-border);\n background: var(--juxt-bg);\n box-shadow: var(--juxt-shadow-md);\n}\n\n.j-navbar__menu-links {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n.j-navbar__menu-links .j-navbar-link {\n justify-content: flex-start;\n height: 2.75rem;\n padding: 0 var(--juxt-space-3);\n font-size: var(--juxt-text-lg);\n}\n\n.j-navbar__menu-footer {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-2);\n padding-top: var(--juxt-space-4);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.j-navbar__menu-footer > * {\n width: 100%;\n}\n\n.j-navbar-menu-enter-active {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-navbar-menu-leave-active {\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\n}\n\n.j-navbar-menu-enter-from,\n.j-navbar-menu-leave-to {\n opacity: 0;\n transform: translateY(calc(var(--juxt-motion-shift) * -1));\n}\n\n/* Collapse points. Above them the menu never shows, below them the links hide. */\n.j-navbar--collapse-never .j-navbar__menu {\n display: none !important;\n}\n\n@media (max-width: 639.98px) {\n .j-navbar--collapse-sm .j-navbar__links { display: none; }\n .j-navbar--collapse-sm .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 640px) {\n .j-navbar--collapse-sm .j-navbar__menu { display: none !important; }\n}\n\n@media (max-width: 767.98px) {\n .j-navbar--collapse-md .j-navbar__links { display: none; }\n .j-navbar--collapse-md .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 768px) {\n .j-navbar--collapse-md .j-navbar__menu { display: none !important; }\n}\n\n@media (max-width: 1023.98px) {\n .j-navbar--collapse-lg .j-navbar__links { display: none; }\n .j-navbar--collapse-lg .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 1024px) {\n .j-navbar--collapse-lg .j-navbar__menu { display: none !important; }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface NavbarLinkProps {\n /** Marks the current page: styled and announced with `aria-current=\"page\"`. */\n active?: boolean\n /** Render as another element or component, e.g. `NuxtLink` or `RouterLink`. */\n as?: string | Component\n}\n\nwithDefaults(defineProps<NavbarLinkProps>(), { as: 'a' })\n\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\n</script>\n\n<template>\n <component\n :is=\"as\"\n class=\"j-navbar-link j-focusable\"\n :class=\"{ 'is-active': active }\"\n :aria-current=\"active ? 'page' : undefined\"\n >\n <span v-if=\"$slots.leading\" class=\"j-navbar-link__icon\"><slot name=\"leading\" /></span>\n <slot />\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-navbar-link {\n display: inline-flex;\n align-items: center;\n gap: var(--juxt-space-1-5);\n height: var(--juxt-control-sm);\n padding: 0 var(--juxt-space-2-5);\n border-radius: var(--juxt-radius-sm);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n text-decoration: none;\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: color, background-color, outline-color, outline-offset;\n}\n\n.j-navbar-link:hover {\n color: var(--juxt-fg);\n}\n\n.j-navbar-link.is-active {\n color: var(--juxt-fg);\n}\n\n/* In the mobile menu the current page gets a quiet fill, like a selected row. */\n.j-navbar__menu .j-navbar-link.is-active {\n background: var(--juxt-surface-hover);\n}\n\n.j-navbar-link__icon {\n display: inline-flex;\n color: var(--juxt-fg-muted);\n}\n\n.j-navbar-link__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface NavbarLinkProps {\n /** Marks the current page: styled and announced with `aria-current=\"page\"`. */\n active?: boolean\n /** Render as another element or component, e.g. `NuxtLink` or `RouterLink`. */\n as?: string | Component\n}\n\nwithDefaults(defineProps<NavbarLinkProps>(), { as: 'a' })\n\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\n</script>\n\n<template>\n <component\n :is=\"as\"\n class=\"j-navbar-link j-focusable\"\n :class=\"{ 'is-active': active }\"\n :aria-current=\"active ? 'page' : undefined\"\n >\n <span v-if=\"$slots.leading\" class=\"j-navbar-link__icon\"><slot name=\"leading\" /></span>\n <slot />\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-navbar-link {\n display: inline-flex;\n align-items: center;\n gap: var(--juxt-space-1-5);\n height: var(--juxt-control-sm);\n padding: 0 var(--juxt-space-2-5);\n border-radius: var(--juxt-radius-sm);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n text-decoration: none;\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: color, background-color, outline-color, outline-offset;\n}\n\n.j-navbar-link:hover {\n color: var(--juxt-fg);\n}\n\n.j-navbar-link.is-active {\n color: var(--juxt-fg);\n}\n\n/* In the mobile menu the current page gets a quiet fill, like a selected row. */\n.j-navbar__menu .j-navbar-link.is-active {\n background: var(--juxt-surface-hover);\n}\n\n.j-navbar-link__icon {\n display: inline-flex;\n color: var(--juxt-fg-muted);\n}\n\n.j-navbar-link__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\r\nimport { computed, nextTick, ref } from 'vue'\r\nimport { IconChevronLeft, IconChevronRight, IconMoreHorizontal } from '../../icons'\r\nimport { focusElement } from '../../utils/dom'\r\n\r\nexport interface PaginationProps {\r\n /** Total number of pages. */\r\n total: number\r\n /** Pages shown on each side of the current page before collapsing to an ellipsis. */\r\n siblingCount?: number\r\n /** Accessible name for the nav landmark. */\r\n label?: string\r\n size?: 'sm' | 'md'\r\n disabled?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<PaginationProps>(), {\r\n siblingCount: 1,\r\n size: 'md',\r\n})\r\n\r\nconst model = defineModel<number>({ default: 1 })\r\n\r\nconst list = ref<HTMLElement | null>(null)\r\n\r\nconst totalPages = computed(() => Math.max(1, Math.floor(props.total)))\r\nconst current = computed(() => Math.min(Math.max(1, model.value), totalPages.value))\r\n\r\ntype PageItem = number | 'ellipsis'\r\n\r\nconst pageItems = computed<PageItem[]>(() => {\r\n const total = totalPages.value\r\n const siblings = Math.max(0, props.siblingCount)\r\n const totalSlots = siblings * 2 + 5\r\n if (total <= totalSlots) return Array.from({ length: total }, (_, i) => i + 1)\r\n\r\n const left = Math.max(current.value - siblings, 1)\r\n const right = Math.min(current.value + siblings, total)\r\n const showLeftEllipsis = left > 2\r\n const showRightEllipsis = right < total - 1\r\n\r\n const items: PageItem[] = [1]\r\n if (showLeftEllipsis) items.push('ellipsis')\r\n else for (let p = 2; p < left; p++) items.push(p)\r\n for (let p = Math.max(left, 2); p <= Math.min(right, total - 1); p++) items.push(p)\r\n if (showRightEllipsis) items.push('ellipsis')\r\n else for (let p = right + 1; p < total; p++) items.push(p)\r\n if (total > 1) items.push(total)\r\n return items\r\n})\r\n\r\nfunction goTo(page: number, focus = false) {\r\n if (props.disabled) return\r\n model.value = Math.min(Math.max(1, page), totalPages.value)\r\n if (focus) nextTick(() => focusElement(list.value?.querySelector<HTMLElement>('[aria-current=\"page\"]')))\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n const target = event.target as HTMLElement\r\n if (!target.classList.contains('j-pagination__page')) return\r\n if (event.key === 'Home') {\r\n event.preventDefault()\r\n goTo(1, true)\r\n }\r\n else if (event.key === 'End') {\r\n event.preventDefault()\r\n goTo(totalPages.value, true)\r\n }\r\n}\r\n</script>\r\n\r\n<template>\r\n <nav class=\"j-pagination\" :class=\"`j-pagination--${size}`\" :aria-label=\"label || 'Pagination'\">\r\n <button\r\n type=\"button\"\r\n class=\"j-pagination__nav j-focusable\"\r\n aria-label=\"Previous page\"\r\n :disabled=\"disabled || current === 1\"\r\n @click=\"goTo(current - 1, true)\"\r\n >\r\n <IconChevronLeft />\r\n </button>\r\n <ul ref=\"list\" class=\"j-pagination__list\" @keydown=\"onKeydown\">\r\n <li v-for=\"(item, index) in pageItems\" :key=\"index\">\r\n <span v-if=\"item === 'ellipsis'\" class=\"j-pagination__ellipsis\" aria-hidden=\"true\">\r\n <IconMoreHorizontal />\r\n </span>\r\n <button\r\n v-else\r\n type=\"button\"\r\n class=\"j-pagination__page j-focusable\"\r\n :class=\"{ 'is-current': item === current }\"\r\n :aria-current=\"item === current ? 'page' : undefined\"\r\n :aria-label=\"`Page ${item}`\"\r\n :disabled=\"disabled\"\r\n @click=\"goTo(item)\"\r\n >\r\n {{ item }}\r\n </button>\r\n </li>\r\n </ul>\r\n <button\r\n type=\"button\"\r\n class=\"j-pagination__nav j-focusable\"\r\n aria-label=\"Next page\"\r\n :disabled=\"disabled || current === totalPages\"\r\n @click=\"goTo(current + 1, true)\"\r\n >\r\n <IconChevronRight />\r\n </button>\r\n </nav>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-pagination {\r\n --j-pagination-size: var(--juxt-control-md);\r\n\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-pagination--sm {\r\n --j-pagination-size: var(--juxt-control-sm);\r\n}\r\n\r\n.j-pagination__list {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1);\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n.j-pagination__nav,\r\n.j-pagination__page {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n width: var(--j-pagination-size);\r\n height: var(--j-pagination-size);\r\n border: 1px solid transparent;\r\n border-radius: var(--juxt-radius-sm);\r\n background: transparent;\r\n color: var(--juxt-fg-secondary);\r\n font-family: inherit;\r\n font-size: var(--juxt-text-sm);\r\n font-weight: var(--juxt-weight-medium);\r\n font-variant-numeric: tabular-nums;\r\n cursor: pointer;\r\n transition:\r\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-pagination__nav > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-pagination__nav:hover:not(:disabled),\r\n.j-pagination__page:hover:not(:disabled) {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-pagination__nav:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-pagination__page.is-current {\r\n border-color: var(--juxt-border);\r\n background: var(--juxt-surface-active);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-pagination__page:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-pagination__ellipsis {\r\n display: grid;\r\n place-items: center;\r\n width: var(--j-pagination-size);\r\n height: var(--j-pagination-size);\r\n color: var(--juxt-fg-disabled);\r\n}\r\n\r\n.j-pagination__ellipsis > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, nextTick, ref } from 'vue'\r\nimport { IconChevronLeft, IconChevronRight, IconMoreHorizontal } from '../../icons'\r\nimport { focusElement } from '../../utils/dom'\r\n\r\nexport interface PaginationProps {\r\n /** Total number of pages. */\r\n total: number\r\n /** Pages shown on each side of the current page before collapsing to an ellipsis. */\r\n siblingCount?: number\r\n /** Accessible name for the nav landmark. */\r\n label?: string\r\n size?: 'sm' | 'md'\r\n disabled?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<PaginationProps>(), {\r\n siblingCount: 1,\r\n size: 'md',\r\n})\r\n\r\nconst model = defineModel<number>({ default: 1 })\r\n\r\nconst list = ref<HTMLElement | null>(null)\r\n\r\nconst totalPages = computed(() => Math.max(1, Math.floor(props.total)))\r\nconst current = computed(() => Math.min(Math.max(1, model.value), totalPages.value))\r\n\r\ntype PageItem = number | 'ellipsis'\r\n\r\nconst pageItems = computed<PageItem[]>(() => {\r\n const total = totalPages.value\r\n const siblings = Math.max(0, props.siblingCount)\r\n const totalSlots = siblings * 2 + 5\r\n if (total <= totalSlots) return Array.from({ length: total }, (_, i) => i + 1)\r\n\r\n const left = Math.max(current.value - siblings, 1)\r\n const right = Math.min(current.value + siblings, total)\r\n const showLeftEllipsis = left > 2\r\n const showRightEllipsis = right < total - 1\r\n\r\n const items: PageItem[] = [1]\r\n if (showLeftEllipsis) items.push('ellipsis')\r\n else for (let p = 2; p < left; p++) items.push(p)\r\n for (let p = Math.max(left, 2); p <= Math.min(right, total - 1); p++) items.push(p)\r\n if (showRightEllipsis) items.push('ellipsis')\r\n else for (let p = right + 1; p < total; p++) items.push(p)\r\n if (total > 1) items.push(total)\r\n return items\r\n})\r\n\r\nfunction goTo(page: number, focus = false) {\r\n if (props.disabled) return\r\n model.value = Math.min(Math.max(1, page), totalPages.value)\r\n if (focus) nextTick(() => focusElement(list.value?.querySelector<HTMLElement>('[aria-current=\"page\"]')))\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n const target = event.target as HTMLElement\r\n if (!target.classList.contains('j-pagination__page')) return\r\n if (event.key === 'Home') {\r\n event.preventDefault()\r\n goTo(1, true)\r\n }\r\n else if (event.key === 'End') {\r\n event.preventDefault()\r\n goTo(totalPages.value, true)\r\n }\r\n}\r\n</script>\r\n\r\n<template>\r\n <nav class=\"j-pagination\" :class=\"`j-pagination--${size}`\" :aria-label=\"label || 'Pagination'\">\r\n <button\r\n type=\"button\"\r\n class=\"j-pagination__nav j-focusable\"\r\n aria-label=\"Previous page\"\r\n :disabled=\"disabled || current === 1\"\r\n @click=\"goTo(current - 1, true)\"\r\n >\r\n <IconChevronLeft />\r\n </button>\r\n <ul ref=\"list\" class=\"j-pagination__list\" @keydown=\"onKeydown\">\r\n <li v-for=\"(item, index) in pageItems\" :key=\"index\">\r\n <span v-if=\"item === 'ellipsis'\" class=\"j-pagination__ellipsis\" aria-hidden=\"true\">\r\n <IconMoreHorizontal />\r\n </span>\r\n <button\r\n v-else\r\n type=\"button\"\r\n class=\"j-pagination__page j-focusable\"\r\n :class=\"{ 'is-current': item === current }\"\r\n :aria-current=\"item === current ? 'page' : undefined\"\r\n :aria-label=\"`Page ${item}`\"\r\n :disabled=\"disabled\"\r\n @click=\"goTo(item)\"\r\n >\r\n {{ item }}\r\n </button>\r\n </li>\r\n </ul>\r\n <button\r\n type=\"button\"\r\n class=\"j-pagination__nav j-focusable\"\r\n aria-label=\"Next page\"\r\n :disabled=\"disabled || current === totalPages\"\r\n @click=\"goTo(current + 1, true)\"\r\n >\r\n <IconChevronRight />\r\n </button>\r\n </nav>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-pagination {\r\n --j-pagination-size: var(--juxt-control-md);\r\n\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-pagination--sm {\r\n --j-pagination-size: var(--juxt-control-sm);\r\n}\r\n\r\n.j-pagination__list {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1);\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n.j-pagination__nav,\r\n.j-pagination__page {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n width: var(--j-pagination-size);\r\n height: var(--j-pagination-size);\r\n border: 1px solid transparent;\r\n border-radius: var(--juxt-radius-sm);\r\n background: transparent;\r\n color: var(--juxt-fg-secondary);\r\n font-family: inherit;\r\n font-size: var(--juxt-text-sm);\r\n font-weight: var(--juxt-weight-medium);\r\n font-variant-numeric: tabular-nums;\r\n cursor: pointer;\r\n transition:\r\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-pagination__nav > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-pagination__nav:hover:not(:disabled),\r\n.j-pagination__page:hover:not(:disabled) {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-pagination__nav:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-pagination__page.is-current {\r\n border-color: var(--juxt-border);\r\n background: var(--juxt-surface-active);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-pagination__page:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-pagination__ellipsis {\r\n display: grid;\r\n place-items: center;\r\n width: var(--j-pagination-size);\r\n height: var(--j-pagination-size);\r\n color: var(--juxt-fg-disabled);\r\n}\r\n\r\n.j-pagination__ellipsis > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { Placement } from '../../composables/useFloating'\r\nimport { computed, nextTick, ref, useId, watch } from 'vue'\r\nimport { useFloating } from '../../composables/useFloating'\r\nimport { useLayer } from '../../composables/useLayer'\r\nimport { focusElement, getFocusableElements, unrefElement } from '../../utils/dom'\r\nimport JPortal from '../primitives/JPortal.vue'\r\nimport { JSlot } from '../primitives/JSlot'\r\n\r\nexport interface PopoverProps {\r\n placement?: Placement\r\n /** Distance from the trigger in px. */\r\n offset?: number\r\n /** Accessible name of the popover content. */\r\n ariaLabel?: string\r\n /** Close when focus or a click leaves the popover. Defaults to true. */\r\n closeOnOutside?: boolean\r\n disabled?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<PopoverProps>(), {\r\n placement: 'bottom',\r\n offset: 8,\r\n closeOnOutside: true,\r\n})\r\n\r\nconst open = defineModel<boolean>('open', { default: false })\r\n\r\ndefineSlots<{\r\n trigger?: (props: { open: boolean }) => unknown\r\n default?: (props: { close: () => void }) => unknown\r\n}>()\r\n\r\nconst id = `j-${useId()}-popover`\r\nconst triggerRef = ref()\r\nconst triggerEl = computed(() => unrefElement(triggerRef.value))\r\nconst content = ref<HTMLElement | null>(null)\r\n\r\nconst { styles } = useFloating(triggerEl, content, () => ({ placement: props.placement, offset: props.offset }))\r\n\r\nfunction close(restoreFocus = false) {\r\n if (!open.value) return\r\n open.value = false\r\n if (restoreFocus) nextTick(() => focusElement(triggerEl.value))\r\n}\r\n\r\nuseLayer({\r\n active: open,\r\n elements: () => [triggerEl.value, content.value],\r\n onEscape: () => close(true),\r\n onPointerDownOutside: () => props.closeOnOutside && close(false),\r\n})\r\n\r\n// Moving focus in makes the content reachable without a trap: this is a\r\n// non-modal surface, so Tab is allowed to leave (and that closes it).\r\nwatch(open, async (value) => {\r\n if (!value) return\r\n await nextTick()\r\n const el = content.value\r\n if (!el) return\r\n focusElement(el.querySelector<HTMLElement>('[autofocus], [data-autofocus]') ?? getFocusableElements(el)[0] ?? el)\r\n}, { flush: 'post' })\r\n\r\nfunction onFocusOut(event: FocusEvent) {\r\n if (!props.closeOnOutside) return\r\n const next = event.relatedTarget as Node | null\r\n if (!next) return\r\n if (content.value?.contains(next) || triggerEl.value?.contains(next)) return\r\n close(false)\r\n}\r\n\r\nfunction toggle() {\r\n if (props.disabled) return\r\n open.value = !open.value\r\n}\r\n\r\nconst triggerProps = computed(() => ({\r\n 'aria-haspopup': 'dialog',\r\n 'aria-expanded': open.value,\r\n 'aria-controls': open.value ? id : undefined,\r\n 'data-state': open.value ? 'open' : 'closed',\r\n 'onClick': toggle,\r\n}))\r\n\r\ndefineExpose({ close: () => close(true) })\r\n</script>\r\n\r\n<template>\r\n <JSlot ref=\"triggerRef\" v-bind=\"triggerProps\">\r\n <slot name=\"trigger\" :open=\"open\" />\r\n </JSlot>\r\n <JPortal>\r\n <Transition name=\"j-pop\">\r\n <div\r\n v-if=\"open\"\r\n :id=\"id\"\r\n ref=\"content\"\r\n role=\"dialog\"\r\n tabindex=\"-1\"\r\n class=\"j-popover j-popover-surface\"\r\n :style=\"styles\"\r\n :aria-label=\"ariaLabel\"\r\n @focusout=\"onFocusOut\"\r\n >\r\n <slot :close=\"() => close(true)\" />\r\n </div>\r\n </Transition>\r\n </JPortal>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-popover {\r\n z-index: var(--juxt-z-popover);\r\n box-sizing: border-box;\r\n max-width: calc(100vw - 16px);\r\n max-height: var(--j-available-height, 32rem);\r\n padding: var(--juxt-space-4);\r\n overflow: auto;\r\n outline: none;\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { Placement } from '../../composables/useFloating'\r\nimport { computed, nextTick, ref, useId, watch } from 'vue'\r\nimport { useFloating } from '../../composables/useFloating'\r\nimport { useLayer } from '../../composables/useLayer'\r\nimport { focusElement, getFocusableElements, unrefElement } from '../../utils/dom'\r\nimport JPortal from '../primitives/JPortal.vue'\r\nimport { JSlot } from '../primitives/JSlot'\r\n\r\nexport interface PopoverProps {\r\n placement?: Placement\r\n /** Distance from the trigger in px. */\r\n offset?: number\r\n /** Accessible name of the popover content. */\r\n ariaLabel?: string\r\n /** Close when focus or a click leaves the popover. Defaults to true. */\r\n closeOnOutside?: boolean\r\n disabled?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<PopoverProps>(), {\r\n placement: 'bottom',\r\n offset: 8,\r\n closeOnOutside: true,\r\n})\r\n\r\nconst open = defineModel<boolean>('open', { default: false })\r\n\r\ndefineSlots<{\r\n trigger?: (props: { open: boolean }) => unknown\r\n default?: (props: { close: () => void }) => unknown\r\n}>()\r\n\r\nconst id = `j-${useId()}-popover`\r\nconst triggerRef = ref()\r\nconst triggerEl = computed(() => unrefElement(triggerRef.value))\r\nconst content = ref<HTMLElement | null>(null)\r\n\r\nconst { styles } = useFloating(triggerEl, content, () => ({ placement: props.placement, offset: props.offset }))\r\n\r\nfunction close(restoreFocus = false) {\r\n if (!open.value) return\r\n open.value = false\r\n if (restoreFocus) nextTick(() => focusElement(triggerEl.value))\r\n}\r\n\r\nuseLayer({\r\n active: open,\r\n elements: () => [triggerEl.value, content.value],\r\n onEscape: () => close(true),\r\n onPointerDownOutside: () => props.closeOnOutside && close(false),\r\n})\r\n\r\n// Moving focus in makes the content reachable without a trap: this is a\r\n// non-modal surface, so Tab is allowed to leave (and that closes it).\r\nwatch(open, async (value) => {\r\n if (!value) return\r\n await nextTick()\r\n const el = content.value\r\n if (!el) return\r\n focusElement(el.querySelector<HTMLElement>('[autofocus], [data-autofocus]') ?? getFocusableElements(el)[0] ?? el)\r\n}, { flush: 'post' })\r\n\r\nfunction onFocusOut(event: FocusEvent) {\r\n if (!props.closeOnOutside) return\r\n const next = event.relatedTarget as Node | null\r\n if (!next) return\r\n if (content.value?.contains(next) || triggerEl.value?.contains(next)) return\r\n close(false)\r\n}\r\n\r\nfunction toggle() {\r\n if (props.disabled) return\r\n open.value = !open.value\r\n}\r\n\r\nconst triggerProps = computed(() => ({\r\n 'aria-haspopup': 'dialog',\r\n 'aria-expanded': open.value,\r\n 'aria-controls': open.value ? id : undefined,\r\n 'data-state': open.value ? 'open' : 'closed',\r\n 'onClick': toggle,\r\n}))\r\n\r\ndefineExpose({ close: () => close(true) })\r\n</script>\r\n\r\n<template>\r\n <JSlot ref=\"triggerRef\" v-bind=\"triggerProps\">\r\n <slot name=\"trigger\" :open=\"open\" />\r\n </JSlot>\r\n <JPortal>\r\n <Transition name=\"j-pop\">\r\n <div\r\n v-if=\"open\"\r\n :id=\"id\"\r\n ref=\"content\"\r\n role=\"dialog\"\r\n tabindex=\"-1\"\r\n class=\"j-popover j-popover-surface\"\r\n :style=\"styles\"\r\n :aria-label=\"ariaLabel\"\r\n @focusout=\"onFocusOut\"\r\n >\r\n <slot :close=\"() => close(true)\" />\r\n </div>\r\n </Transition>\r\n </JPortal>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-popover {\r\n z-index: var(--juxt-z-popover);\r\n box-sizing: border-box;\r\n max-width: calc(100vw - 16px);\r\n max-height: var(--j-available-height, 32rem);\r\n padding: var(--juxt-space-4);\r\n overflow: auto;\r\n outline: none;\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, useId } from 'vue'\r\n\r\nexport interface ProgressProps {\r\n /** Current value. `null` shows an indeterminate bar. */\r\n value?: number | null\r\n max?: number\r\n size?: 'sm' | 'md'\r\n /** Visible label above the bar; also its accessible name. */\r\n label?: string\r\n /** Show the percentage next to the label. */\r\n showValue?: boolean\r\n /** `accent` for progress toward a goal, `neutral` for plain measurement. */\r\n variant?: 'accent' | 'neutral'\r\n}\r\n\r\nconst props = withDefaults(defineProps<ProgressProps>(), {\r\n value: null,\r\n max: 100,\r\n size: 'md',\r\n variant: 'accent',\r\n})\r\n\r\nconst id = `j-${useId()}`\r\nconst indeterminate = computed(() => props.value === null || props.value === undefined)\r\nconst ratio = computed(() => (indeterminate.value ? 0 : Math.min(1, Math.max(0, Number(props.value) / props.max))))\r\nconst percent = computed(() => Math.round(ratio.value * 100))\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-progress\" :class=\"[`j-progress--${size}`, `j-progress--${variant}`, { 'is-indeterminate': indeterminate }]\">\r\n <div v-if=\"label || showValue\" class=\"j-progress__header\">\r\n <span v-if=\"label\" :id=\"`${id}-label`\" class=\"j-progress__label\">{{ label }}</span>\r\n <span v-if=\"showValue && !indeterminate\" class=\"j-progress__value\">{{ percent }}%</span>\r\n </div>\r\n <div\r\n class=\"j-progress__track\"\r\n role=\"progressbar\"\r\n :aria-labelledby=\"label ? `${id}-label` : undefined\"\r\n aria-valuemin=\"0\"\r\n :aria-valuemax=\"max\"\r\n :aria-valuenow=\"indeterminate ? undefined : value!\"\r\n :aria-valuetext=\"indeterminate ? 'Loading' : `${percent}%`\"\r\n >\r\n <span class=\"j-progress__fill\" :style=\"indeterminate ? undefined : { transform: `scaleX(${ratio})` }\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-progress {\r\n --j-progress-height: 0.375rem;\r\n --j-progress-fill: var(--juxt-accent);\r\n\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-2);\r\n width: 100%;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-progress--sm {\r\n --j-progress-height: 0.25rem;\r\n}\r\n\r\n.j-progress--neutral {\r\n --j-progress-fill: var(--juxt-fg);\r\n}\r\n\r\n.j-progress__header {\r\n display: flex;\r\n align-items: baseline;\r\n justify-content: space-between;\r\n gap: var(--juxt-space-3);\r\n font-size: var(--juxt-text-sm);\r\n}\r\n\r\n.j-progress__label {\r\n color: var(--juxt-fg);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-progress__value {\r\n color: var(--juxt-fg-muted);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-progress__track {\r\n position: relative;\r\n height: var(--j-progress-height);\r\n overflow: hidden;\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-surface-active);\r\n}\r\n\r\n/* Width changes are transforms, so updates glide without reflow. */\r\n.j-progress__fill {\r\n position: absolute;\r\n inset: 0;\r\n border-radius: inherit;\r\n background: var(--j-progress-fill);\r\n transform: scaleX(0);\r\n transform-origin: left;\r\n transition: transform var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n.j-progress.is-indeterminate .j-progress__fill {\r\n width: 40%;\r\n transform: none;\r\n animation: j-progress-indeterminate 1.4s var(--juxt-ease-standard) infinite;\r\n}\r\n\r\n@keyframes j-progress-indeterminate {\r\n from {\r\n transform: translateX(-100%);\r\n }\r\n\r\n to {\r\n transform: translateX(250%);\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-progress.is-indeterminate .j-progress__fill {\r\n width: 100%;\r\n animation: j-progress-pulse 1.6s ease-in-out infinite;\r\n }\r\n\r\n @keyframes j-progress-pulse {\r\n 50% {\r\n opacity: 0.35;\r\n }\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, useId } from 'vue'\r\n\r\nexport interface ProgressProps {\r\n /** Current value. `null` shows an indeterminate bar. */\r\n value?: number | null\r\n max?: number\r\n size?: 'sm' | 'md'\r\n /** Visible label above the bar; also its accessible name. */\r\n label?: string\r\n /** Show the percentage next to the label. */\r\n showValue?: boolean\r\n /** `accent` for progress toward a goal, `neutral` for plain measurement. */\r\n variant?: 'accent' | 'neutral'\r\n}\r\n\r\nconst props = withDefaults(defineProps<ProgressProps>(), {\r\n value: null,\r\n max: 100,\r\n size: 'md',\r\n variant: 'accent',\r\n})\r\n\r\nconst id = `j-${useId()}`\r\nconst indeterminate = computed(() => props.value === null || props.value === undefined)\r\nconst ratio = computed(() => (indeterminate.value ? 0 : Math.min(1, Math.max(0, Number(props.value) / props.max))))\r\nconst percent = computed(() => Math.round(ratio.value * 100))\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-progress\" :class=\"[`j-progress--${size}`, `j-progress--${variant}`, { 'is-indeterminate': indeterminate }]\">\r\n <div v-if=\"label || showValue\" class=\"j-progress__header\">\r\n <span v-if=\"label\" :id=\"`${id}-label`\" class=\"j-progress__label\">{{ label }}</span>\r\n <span v-if=\"showValue && !indeterminate\" class=\"j-progress__value\">{{ percent }}%</span>\r\n </div>\r\n <div\r\n class=\"j-progress__track\"\r\n role=\"progressbar\"\r\n :aria-labelledby=\"label ? `${id}-label` : undefined\"\r\n aria-valuemin=\"0\"\r\n :aria-valuemax=\"max\"\r\n :aria-valuenow=\"indeterminate ? undefined : value!\"\r\n :aria-valuetext=\"indeterminate ? 'Loading' : `${percent}%`\"\r\n >\r\n <span class=\"j-progress__fill\" :style=\"indeterminate ? undefined : { transform: `scaleX(${ratio})` }\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-progress {\r\n --j-progress-height: 0.375rem;\r\n --j-progress-fill: var(--juxt-accent);\r\n\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-2);\r\n width: 100%;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-progress--sm {\r\n --j-progress-height: 0.25rem;\r\n}\r\n\r\n.j-progress--neutral {\r\n --j-progress-fill: var(--juxt-fg);\r\n}\r\n\r\n.j-progress__header {\r\n display: flex;\r\n align-items: baseline;\r\n justify-content: space-between;\r\n gap: var(--juxt-space-3);\r\n font-size: var(--juxt-text-sm);\r\n}\r\n\r\n.j-progress__label {\r\n color: var(--juxt-fg);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-progress__value {\r\n color: var(--juxt-fg-muted);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-progress__track {\r\n position: relative;\r\n height: var(--j-progress-height);\r\n overflow: hidden;\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-surface-active);\r\n}\r\n\r\n/* Width changes are transforms, so updates glide without reflow. */\r\n.j-progress__fill {\r\n position: absolute;\r\n inset: 0;\r\n border-radius: inherit;\r\n background: var(--j-progress-fill);\r\n transform: scaleX(0);\r\n transform-origin: left;\r\n transition: transform var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n.j-progress.is-indeterminate .j-progress__fill {\r\n width: 40%;\r\n transform: none;\r\n animation: j-progress-indeterminate 1.4s var(--juxt-ease-standard) infinite;\r\n}\r\n\r\n@keyframes j-progress-indeterminate {\r\n from {\r\n transform: translateX(-100%);\r\n }\r\n\r\n to {\r\n transform: translateX(250%);\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-progress.is-indeterminate .j-progress__fill {\r\n width: 100%;\r\n animation: j-progress-pulse 1.6s ease-in-out infinite;\r\n }\r\n\r\n @keyframes j-progress-pulse {\r\n 50% {\r\n opacity: 0.35;\r\n }\r\n }\r\n}\r\n}\r\n</style>\r\n","import type { ComputedRef, InjectionKey } from 'vue'\n\nexport type RadioValue = string | number\n\nexport interface RadioGroupContext {\n name: ComputedRef<string>\n value: ComputedRef<RadioValue | null | undefined>\n disabled: ComputedRef<boolean>\n required: ComputedRef<boolean>\n invalid: ComputedRef<boolean>\n select: (value: RadioValue) => void\n}\n\nexport const RADIO_GROUP: InjectionKey<RadioGroupContext> = Symbol('JRadioGroup')\n","<script setup lang=\"ts\">\r\nimport type { RadioValue } from './context'\r\nimport { computed, inject, useId } from 'vue'\r\nimport { RADIO_GROUP } from './context'\r\n\r\nexport interface RadioProps {\r\n value: RadioValue\r\n label?: string\r\n description?: string\r\n disabled?: boolean\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = defineProps<RadioProps>()\r\n\r\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\r\n\r\nconst group = inject(RADIO_GROUP, null)\r\n\r\nconst id = `j-${useId()}`\r\nconst descriptionId = `${id}-description`\r\nconst checked = computed(() => group?.value.value === props.value)\r\nconst isDisabled = computed(() => props.disabled || !!group?.disabled.value)\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-radio\"\r\n :class=\"[$attrs.class, { 'is-disabled': isDisabled }]\"\r\n :style=\"$attrs.style as any\"\r\n :data-state=\"checked ? 'checked' : 'unchecked'\"\r\n >\r\n <span class=\"j-radio__control\">\r\n <input\r\n :id=\"id\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n type=\"radio\"\r\n class=\"j-radio__input\"\r\n :name=\"group?.name.value\"\r\n :value=\"value\"\r\n :checked=\"checked\"\r\n :disabled=\"isDisabled\"\r\n :required=\"group?.required.value\"\r\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\r\n @change=\"group?.select(value)\"\r\n />\r\n <span class=\"j-radio__circle\" aria-hidden=\"true\" />\r\n </span>\r\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-radio__text\">\r\n <label v-if=\"label || $slots.default\" :for=\"id\" class=\"j-radio__label\">\r\n <slot>{{ label }}</slot>\r\n </label>\r\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-radio__description\">\r\n <slot name=\"description\">{{ description }}</slot>\r\n </span>\r\n </span>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-radio {\r\n --j-radio-size: 1rem;\r\n\r\n display: inline-flex;\r\n align-items: flex-start;\r\n gap: var(--juxt-space-2-5);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-radio__control {\r\n position: relative;\r\n display: inline-flex;\r\n flex: none;\r\n margin-top: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-radio-size)) / 2);\r\n}\r\n\r\n.j-radio__input {\r\n position: absolute;\r\n inset: -4px;\r\n z-index: 1;\r\n width: calc(100% + 8px);\r\n height: calc(100% + 8px);\r\n margin: 0;\r\n opacity: 0;\r\n cursor: pointer;\r\n}\r\n\r\n.j-radio__input:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-radio__circle {\r\n position: relative;\r\n display: block;\r\n width: var(--j-radio-size);\r\n height: var(--j-radio-size);\r\n border: 1px solid var(--juxt-border-strong);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-surface);\r\n box-shadow: var(--juxt-shadow-xs);\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n transition:\r\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n/* The inner dot grows out of the centre once the fill lands. */\r\n.j-radio__circle::after {\r\n content: '';\r\n position: absolute;\r\n inset: 0;\r\n margin: auto;\r\n width: 0.375rem;\r\n height: 0.375rem;\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-accent-fg);\r\n opacity: 0;\r\n transform: scale(0.3);\r\n transition:\r\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-radio__input:hover:not(:disabled) + .j-radio__circle {\r\n border-color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-radio__input:active:not(:disabled) + .j-radio__circle {\r\n transform: scale(0.9);\r\n}\r\n\r\n.j-radio__input:focus-visible + .j-radio__circle {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.j-radio[data-state='checked'] .j-radio__circle {\r\n border-color: var(--juxt-accent);\r\n background: var(--juxt-accent);\r\n}\r\n\r\n.j-radio[data-state='checked'] .j-radio__input:hover:not(:disabled) + .j-radio__circle {\r\n border-color: var(--juxt-accent-hover);\r\n background: var(--juxt-accent-hover);\r\n}\r\n\r\n.j-radio[data-state='checked'] .j-radio__circle::after {\r\n opacity: 1;\r\n transform: none;\r\n transition-delay: 30ms;\r\n}\r\n\r\n.j-radio-group.is-invalid .j-radio:not([data-state='checked']) .j-radio__circle {\r\n border-color: var(--juxt-danger-border);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-radio__input:active:not(:disabled) + .j-radio__circle {\r\n transform: none;\r\n }\r\n}\r\n\r\n.j-radio__text {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-0-5);\r\n min-width: 0;\r\n}\r\n\r\n.j-radio__label {\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.j-radio__description {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-radio.is-disabled .j-radio__circle {\r\n opacity: 0.5;\r\n box-shadow: none;\r\n}\r\n\r\n.j-radio.is-disabled .j-radio__label {\r\n color: var(--juxt-fg-muted);\r\n cursor: not-allowed;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { RadioValue } from './context'\r\nimport { computed, inject, useId } from 'vue'\r\nimport { RADIO_GROUP } from './context'\r\n\r\nexport interface RadioProps {\r\n value: RadioValue\r\n label?: string\r\n description?: string\r\n disabled?: boolean\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = defineProps<RadioProps>()\r\n\r\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\r\n\r\nconst group = inject(RADIO_GROUP, null)\r\n\r\nconst id = `j-${useId()}`\r\nconst descriptionId = `${id}-description`\r\nconst checked = computed(() => group?.value.value === props.value)\r\nconst isDisabled = computed(() => props.disabled || !!group?.disabled.value)\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-radio\"\r\n :class=\"[$attrs.class, { 'is-disabled': isDisabled }]\"\r\n :style=\"$attrs.style as any\"\r\n :data-state=\"checked ? 'checked' : 'unchecked'\"\r\n >\r\n <span class=\"j-radio__control\">\r\n <input\r\n :id=\"id\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n type=\"radio\"\r\n class=\"j-radio__input\"\r\n :name=\"group?.name.value\"\r\n :value=\"value\"\r\n :checked=\"checked\"\r\n :disabled=\"isDisabled\"\r\n :required=\"group?.required.value\"\r\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\r\n @change=\"group?.select(value)\"\r\n />\r\n <span class=\"j-radio__circle\" aria-hidden=\"true\" />\r\n </span>\r\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-radio__text\">\r\n <label v-if=\"label || $slots.default\" :for=\"id\" class=\"j-radio__label\">\r\n <slot>{{ label }}</slot>\r\n </label>\r\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-radio__description\">\r\n <slot name=\"description\">{{ description }}</slot>\r\n </span>\r\n </span>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-radio {\r\n --j-radio-size: 1rem;\r\n\r\n display: inline-flex;\r\n align-items: flex-start;\r\n gap: var(--juxt-space-2-5);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-radio__control {\r\n position: relative;\r\n display: inline-flex;\r\n flex: none;\r\n margin-top: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-radio-size)) / 2);\r\n}\r\n\r\n.j-radio__input {\r\n position: absolute;\r\n inset: -4px;\r\n z-index: 1;\r\n width: calc(100% + 8px);\r\n height: calc(100% + 8px);\r\n margin: 0;\r\n opacity: 0;\r\n cursor: pointer;\r\n}\r\n\r\n.j-radio__input:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-radio__circle {\r\n position: relative;\r\n display: block;\r\n width: var(--j-radio-size);\r\n height: var(--j-radio-size);\r\n border: 1px solid var(--juxt-border-strong);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-surface);\r\n box-shadow: var(--juxt-shadow-xs);\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n transition:\r\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n/* The inner dot grows out of the centre once the fill lands. */\r\n.j-radio__circle::after {\r\n content: '';\r\n position: absolute;\r\n inset: 0;\r\n margin: auto;\r\n width: 0.375rem;\r\n height: 0.375rem;\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-accent-fg);\r\n opacity: 0;\r\n transform: scale(0.3);\r\n transition:\r\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-radio__input:hover:not(:disabled) + .j-radio__circle {\r\n border-color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-radio__input:active:not(:disabled) + .j-radio__circle {\r\n transform: scale(0.9);\r\n}\r\n\r\n.j-radio__input:focus-visible + .j-radio__circle {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.j-radio[data-state='checked'] .j-radio__circle {\r\n border-color: var(--juxt-accent);\r\n background: var(--juxt-accent);\r\n}\r\n\r\n.j-radio[data-state='checked'] .j-radio__input:hover:not(:disabled) + .j-radio__circle {\r\n border-color: var(--juxt-accent-hover);\r\n background: var(--juxt-accent-hover);\r\n}\r\n\r\n.j-radio[data-state='checked'] .j-radio__circle::after {\r\n opacity: 1;\r\n transform: none;\r\n transition-delay: 30ms;\r\n}\r\n\r\n.j-radio-group.is-invalid .j-radio:not([data-state='checked']) .j-radio__circle {\r\n border-color: var(--juxt-danger-border);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-radio__input:active:not(:disabled) + .j-radio__circle {\r\n transform: none;\r\n }\r\n}\r\n\r\n.j-radio__text {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-0-5);\r\n min-width: 0;\r\n}\r\n\r\n.j-radio__label {\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.j-radio__description {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-radio.is-disabled .j-radio__circle {\r\n opacity: 0.5;\r\n box-shadow: none;\r\n}\r\n\r\n.j-radio.is-disabled .j-radio__label {\r\n color: var(--juxt-fg-muted);\r\n cursor: not-allowed;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { RadioValue } from './context'\r\nimport { computed, provide, useId } from 'vue'\r\nimport { RADIO_GROUP } from './context'\r\n\r\nexport interface RadioGroupProps {\r\n /** Group label, rendered as the fieldset legend. */\r\n label?: string\r\n description?: string\r\n /** An error message, or `true` to mark the group invalid without one. */\r\n error?: string | boolean\r\n orientation?: 'vertical' | 'horizontal'\r\n disabled?: boolean\r\n required?: boolean\r\n /** Form field name. Generated when omitted. */\r\n name?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<RadioGroupProps>(), { orientation: 'vertical', error: undefined })\r\n\r\nconst model = defineModel<RadioValue | null>({ default: null })\r\n\r\ndefineSlots<{ default?: () => unknown, label?: () => unknown }>()\r\n\r\nconst id = `j-${useId()}`\r\nconst descriptionId = `${id}-description`\r\nconst errorId = `${id}-error`\r\nconst errorMessage = computed(() => (typeof props.error === 'string' ? props.error : ''))\r\n\r\nprovide(RADIO_GROUP, {\r\n name: computed(() => props.name ?? id),\r\n value: computed(() => model.value),\r\n disabled: computed(() => props.disabled),\r\n required: computed(() => props.required),\r\n invalid: computed(() => !!props.error),\r\n select: (value) => {\r\n if (!props.disabled) model.value = value\r\n },\r\n})\r\n\r\nconst describedBy = computed(() => (errorMessage.value ? errorId : props.description ? descriptionId : undefined))\r\n</script>\r\n\r\n<template>\r\n <fieldset\r\n class=\"j-radio-group\"\r\n :class=\"{ 'is-disabled': disabled, 'is-invalid': !!error }\"\r\n :disabled=\"disabled\"\r\n :aria-describedby=\"describedBy\"\r\n :aria-invalid=\"!!error || undefined\"\r\n :aria-required=\"required || undefined\"\r\n >\r\n <legend v-if=\"label || $slots.label\" class=\"j-radio-group__legend\">\r\n <slot name=\"label\">{{ label }}</slot>\r\n <span v-if=\"required\" class=\"j-field__required\" aria-hidden=\"true\">*</span>\r\n </legend>\r\n <p v-if=\"description && !errorMessage\" :id=\"descriptionId\" class=\"j-radio-group__description\">\r\n {{ description }}\r\n </p>\r\n <div class=\"j-radio-group__items\" :class=\"`j-radio-group__items--${orientation}`\">\r\n <slot />\r\n </div>\r\n <p v-if=\"errorMessage\" :id=\"errorId\" class=\"j-radio-group__error\">\r\n {{ errorMessage }}\r\n </p>\r\n </fieldset>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-radio-group {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-1-5);\r\n min-width: 0;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-radio-group__legend {\r\n display: inline-flex;\r\n gap: var(--juxt-space-0-5);\r\n margin-bottom: var(--juxt-space-1-5);\r\n padding: 0;\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-sm);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-radio-group__description,\r\n.j-radio-group__error {\r\n margin: calc(var(--juxt-space-1) * -1) 0 var(--juxt-space-1-5);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-radio-group__error {\r\n margin: var(--juxt-space-1) 0 0;\r\n color: var(--juxt-danger-text);\r\n}\r\n\r\n.j-radio-group__items {\r\n display: flex;\r\n}\r\n\r\n.j-radio-group__items--vertical {\r\n flex-direction: column;\r\n gap: var(--juxt-space-3);\r\n}\r\n\r\n.j-radio-group__items--horizontal {\r\n flex-wrap: wrap;\r\n gap: var(--juxt-space-3) var(--juxt-space-6);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { RadioValue } from './context'\r\nimport { computed, provide, useId } from 'vue'\r\nimport { RADIO_GROUP } from './context'\r\n\r\nexport interface RadioGroupProps {\r\n /** Group label, rendered as the fieldset legend. */\r\n label?: string\r\n description?: string\r\n /** An error message, or `true` to mark the group invalid without one. */\r\n error?: string | boolean\r\n orientation?: 'vertical' | 'horizontal'\r\n disabled?: boolean\r\n required?: boolean\r\n /** Form field name. Generated when omitted. */\r\n name?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<RadioGroupProps>(), { orientation: 'vertical', error: undefined })\r\n\r\nconst model = defineModel<RadioValue | null>({ default: null })\r\n\r\ndefineSlots<{ default?: () => unknown, label?: () => unknown }>()\r\n\r\nconst id = `j-${useId()}`\r\nconst descriptionId = `${id}-description`\r\nconst errorId = `${id}-error`\r\nconst errorMessage = computed(() => (typeof props.error === 'string' ? props.error : ''))\r\n\r\nprovide(RADIO_GROUP, {\r\n name: computed(() => props.name ?? id),\r\n value: computed(() => model.value),\r\n disabled: computed(() => props.disabled),\r\n required: computed(() => props.required),\r\n invalid: computed(() => !!props.error),\r\n select: (value) => {\r\n if (!props.disabled) model.value = value\r\n },\r\n})\r\n\r\nconst describedBy = computed(() => (errorMessage.value ? errorId : props.description ? descriptionId : undefined))\r\n</script>\r\n\r\n<template>\r\n <fieldset\r\n class=\"j-radio-group\"\r\n :class=\"{ 'is-disabled': disabled, 'is-invalid': !!error }\"\r\n :disabled=\"disabled\"\r\n :aria-describedby=\"describedBy\"\r\n :aria-invalid=\"!!error || undefined\"\r\n :aria-required=\"required || undefined\"\r\n >\r\n <legend v-if=\"label || $slots.label\" class=\"j-radio-group__legend\">\r\n <slot name=\"label\">{{ label }}</slot>\r\n <span v-if=\"required\" class=\"j-field__required\" aria-hidden=\"true\">*</span>\r\n </legend>\r\n <p v-if=\"description && !errorMessage\" :id=\"descriptionId\" class=\"j-radio-group__description\">\r\n {{ description }}\r\n </p>\r\n <div class=\"j-radio-group__items\" :class=\"`j-radio-group__items--${orientation}`\">\r\n <slot />\r\n </div>\r\n <p v-if=\"errorMessage\" :id=\"errorId\" class=\"j-radio-group__error\">\r\n {{ errorMessage }}\r\n </p>\r\n </fieldset>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-radio-group {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-1-5);\r\n min-width: 0;\r\n margin: 0;\r\n padding: 0;\r\n border: 0;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-radio-group__legend {\r\n display: inline-flex;\r\n gap: var(--juxt-space-0-5);\r\n margin-bottom: var(--juxt-space-1-5);\r\n padding: 0;\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-sm);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-radio-group__description,\r\n.j-radio-group__error {\r\n margin: calc(var(--juxt-space-1) * -1) 0 var(--juxt-space-1-5);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-radio-group__error {\r\n margin: var(--juxt-space-1) 0 0;\r\n color: var(--juxt-danger-text);\r\n}\r\n\r\n.j-radio-group__items {\r\n display: flex;\r\n}\r\n\r\n.j-radio-group__items--vertical {\r\n flex-direction: column;\r\n gap: var(--juxt-space-3);\r\n}\r\n\r\n.j-radio-group__items--horizontal {\r\n flex-wrap: wrap;\r\n gap: var(--juxt-space-3) var(--juxt-space-6);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, ref } from 'vue'\r\n\r\nexport interface RatingProps {\r\n /** Number of stars. */\r\n max?: number\r\n /** Static display mode | no interaction, ideal for showing an average. */\r\n readonly?: boolean\r\n disabled?: boolean\r\n size?: 'sm' | 'md' | 'lg'\r\n /** Accessible name for the group, e.g. \"Rate this product\". */\r\n label?: string\r\n /** Allow half-star precision. Only applies in `readonly` mode. */\r\n allowHalf?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<RatingProps>(), { max: 5, size: 'md' })\r\n\r\nconst model = defineModel<number>({ default: 0 })\r\n\r\nconst hovered = ref<number | null>(null)\r\nconst stars = computed(() => Array.from({ length: props.max }, (_, i) => i + 1))\r\nconst display = computed(() => hovered.value ?? model.value)\r\n\r\nfunction fillFor(star: number) {\r\n const value = display.value\r\n if (value >= star) return 1\r\n if (props.readonly && props.allowHalf && value >= star - 0.5) return 0.5\r\n return 0\r\n}\r\n\r\nfunction select(star: number) {\r\n if (props.readonly || props.disabled) return\r\n model.value = star\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (props.readonly || props.disabled) return\r\n let next: number | undefined\r\n if (event.key === 'ArrowRight' || event.key === 'ArrowUp') next = Math.min(props.max, model.value + 1)\r\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') next = Math.max(0, model.value - 1)\r\n else if (event.key === 'Home') next = 0\r\n else if (event.key === 'End') next = props.max\r\n if (next === undefined) return\r\n event.preventDefault()\r\n model.value = next\r\n}\r\n\r\nconst groupLabel = computed(() => props.label ?? (props.readonly ? `${model.value} out of ${props.max} stars` : 'Rating'))\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-rating\"\r\n :class=\"[`j-rating--${size}`, { 'is-readonly': readonly, 'is-disabled': disabled }]\"\r\n :role=\"readonly ? 'img' : 'radiogroup'\"\r\n :aria-label=\"groupLabel\"\r\n :aria-disabled=\"disabled || undefined\"\r\n @mouseleave=\"hovered = null\"\r\n >\r\n <template v-if=\"readonly\">\r\n <span v-for=\"star in stars\" :key=\"star\" class=\"j-rating__star\" aria-hidden=\"true\">\r\n <svg viewBox=\"0 0 16 16\" class=\"j-rating__star-bg\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\r\n <span class=\"j-rating__star-fill\" :style=\"{ width: `${fillFor(star) * 100}%` }\">\r\n <svg viewBox=\"0 0 16 16\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\r\n </span>\r\n </span>\r\n </template>\r\n <template v-else>\r\n <button\r\n v-for=\"star in stars\"\r\n :key=\"star\"\r\n type=\"button\"\r\n role=\"radio\"\r\n class=\"j-rating__button j-focusable\"\r\n :aria-checked=\"model === star\"\r\n :aria-label=\"`${star} ${star === 1 ? 'star' : 'stars'}`\"\r\n :tabindex=\"disabled ? -1 : (model === star || (model === 0 && star === 1)) ? 0 : -1\"\r\n :disabled=\"disabled\"\r\n @click=\"select(star)\"\r\n @mouseenter=\"hovered = star\"\r\n @focus=\"hovered = star\"\r\n @blur=\"hovered = null\"\r\n @keydown=\"onKeydown\"\r\n >\r\n <svg viewBox=\"0 0 16 16\" class=\"j-rating__star-bg\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\r\n <span class=\"j-rating__star-fill\" :style=\"{ width: `${fillFor(star) * 100}%` }\">\r\n <svg viewBox=\"0 0 16 16\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\r\n </span>\r\n </button>\r\n </template>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-rating {\r\n --j-rating-size: 1.25rem;\r\n\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--juxt-space-0-5);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-rating--sm {\r\n --j-rating-size: 1rem;\r\n}\r\n\r\n.j-rating--lg {\r\n --j-rating-size: 1.5rem;\r\n}\r\n\r\n.j-rating__star,\r\n.j-rating__button {\r\n position: relative;\r\n display: inline-flex;\r\n flex: none;\r\n width: var(--j-rating-size);\r\n height: var(--j-rating-size);\r\n padding: 0;\r\n border: 0;\r\n background: transparent;\r\n color: var(--juxt-fg-disabled);\r\n cursor: default;\r\n}\r\n\r\n.j-rating__button {\r\n border-radius: var(--juxt-radius-xs);\r\n cursor: pointer;\r\n transition: transform var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-rating__button:hover:not(:disabled) {\r\n transform: scale(1.1);\r\n}\r\n\r\n.j-rating__button:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-rating.is-disabled .j-rating__button {\r\n opacity: 0.5;\r\n}\r\n\r\n.j-rating__star-bg,\r\n.j-rating__star-fill > svg {\r\n position: absolute;\r\n inset: 0;\r\n width: 100%;\r\n height: 100%;\r\n fill: currentColor;\r\n stroke: none;\r\n}\r\n\r\n.j-rating__star-fill {\r\n position: absolute;\r\n inset: 0;\r\n overflow: hidden;\r\n color: var(--juxt-warning);\r\n transition: width var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-rating__button:hover:not(:disabled) {\r\n transform: none;\r\n }\r\n\r\n .j-rating__star-fill {\r\n transition: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, ref } from 'vue'\r\n\r\nexport interface RatingProps {\r\n /** Number of stars. */\r\n max?: number\r\n /** Static display mode | no interaction, ideal for showing an average. */\r\n readonly?: boolean\r\n disabled?: boolean\r\n size?: 'sm' | 'md' | 'lg'\r\n /** Accessible name for the group, e.g. \"Rate this product\". */\r\n label?: string\r\n /** Allow half-star precision. Only applies in `readonly` mode. */\r\n allowHalf?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<RatingProps>(), { max: 5, size: 'md' })\r\n\r\nconst model = defineModel<number>({ default: 0 })\r\n\r\nconst hovered = ref<number | null>(null)\r\nconst stars = computed(() => Array.from({ length: props.max }, (_, i) => i + 1))\r\nconst display = computed(() => hovered.value ?? model.value)\r\n\r\nfunction fillFor(star: number) {\r\n const value = display.value\r\n if (value >= star) return 1\r\n if (props.readonly && props.allowHalf && value >= star - 0.5) return 0.5\r\n return 0\r\n}\r\n\r\nfunction select(star: number) {\r\n if (props.readonly || props.disabled) return\r\n model.value = star\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (props.readonly || props.disabled) return\r\n let next: number | undefined\r\n if (event.key === 'ArrowRight' || event.key === 'ArrowUp') next = Math.min(props.max, model.value + 1)\r\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') next = Math.max(0, model.value - 1)\r\n else if (event.key === 'Home') next = 0\r\n else if (event.key === 'End') next = props.max\r\n if (next === undefined) return\r\n event.preventDefault()\r\n model.value = next\r\n}\r\n\r\nconst groupLabel = computed(() => props.label ?? (props.readonly ? `${model.value} out of ${props.max} stars` : 'Rating'))\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-rating\"\r\n :class=\"[`j-rating--${size}`, { 'is-readonly': readonly, 'is-disabled': disabled }]\"\r\n :role=\"readonly ? 'img' : 'radiogroup'\"\r\n :aria-label=\"groupLabel\"\r\n :aria-disabled=\"disabled || undefined\"\r\n @mouseleave=\"hovered = null\"\r\n >\r\n <template v-if=\"readonly\">\r\n <span v-for=\"star in stars\" :key=\"star\" class=\"j-rating__star\" aria-hidden=\"true\">\r\n <svg viewBox=\"0 0 16 16\" class=\"j-rating__star-bg\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\r\n <span class=\"j-rating__star-fill\" :style=\"{ width: `${fillFor(star) * 100}%` }\">\r\n <svg viewBox=\"0 0 16 16\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\r\n </span>\r\n </span>\r\n </template>\r\n <template v-else>\r\n <button\r\n v-for=\"star in stars\"\r\n :key=\"star\"\r\n type=\"button\"\r\n role=\"radio\"\r\n class=\"j-rating__button j-focusable\"\r\n :aria-checked=\"model === star\"\r\n :aria-label=\"`${star} ${star === 1 ? 'star' : 'stars'}`\"\r\n :tabindex=\"disabled ? -1 : (model === star || (model === 0 && star === 1)) ? 0 : -1\"\r\n :disabled=\"disabled\"\r\n @click=\"select(star)\"\r\n @mouseenter=\"hovered = star\"\r\n @focus=\"hovered = star\"\r\n @blur=\"hovered = null\"\r\n @keydown=\"onKeydown\"\r\n >\r\n <svg viewBox=\"0 0 16 16\" class=\"j-rating__star-bg\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\r\n <span class=\"j-rating__star-fill\" :style=\"{ width: `${fillFor(star) * 100}%` }\">\r\n <svg viewBox=\"0 0 16 16\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\r\n </span>\r\n </button>\r\n </template>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-rating {\r\n --j-rating-size: 1.25rem;\r\n\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--juxt-space-0-5);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-rating--sm {\r\n --j-rating-size: 1rem;\r\n}\r\n\r\n.j-rating--lg {\r\n --j-rating-size: 1.5rem;\r\n}\r\n\r\n.j-rating__star,\r\n.j-rating__button {\r\n position: relative;\r\n display: inline-flex;\r\n flex: none;\r\n width: var(--j-rating-size);\r\n height: var(--j-rating-size);\r\n padding: 0;\r\n border: 0;\r\n background: transparent;\r\n color: var(--juxt-fg-disabled);\r\n cursor: default;\r\n}\r\n\r\n.j-rating__button {\r\n border-radius: var(--juxt-radius-xs);\r\n cursor: pointer;\r\n transition: transform var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-rating__button:hover:not(:disabled) {\r\n transform: scale(1.1);\r\n}\r\n\r\n.j-rating__button:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-rating.is-disabled .j-rating__button {\r\n opacity: 0.5;\r\n}\r\n\r\n.j-rating__star-bg,\r\n.j-rating__star-fill > svg {\r\n position: absolute;\r\n inset: 0;\r\n width: 100%;\r\n height: 100%;\r\n fill: currentColor;\r\n stroke: none;\r\n}\r\n\r\n.j-rating__star-fill {\r\n position: absolute;\r\n inset: 0;\r\n overflow: hidden;\r\n color: var(--juxt-warning);\r\n transition: width var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-rating__button:hover:not(:disabled) {\r\n transform: none;\r\n }\r\n\r\n .j-rating__star-fill {\r\n transition: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\" generic=\"T extends string | number = string\">\r\nimport type { FieldProps } from '../field/useField'\r\nimport { computed, nextTick, ref, watch } from 'vue'\r\nimport { useFloating } from '../../composables/useFloating'\r\nimport { useLayer } from '../../composables/useLayer'\r\nimport { useTypeahead } from '../../composables/useTypeahead'\r\nimport { IconCheck, IconChevronUpDown } from '../../icons'\r\nimport JFieldShell from '../field/JFieldShell.vue'\r\nimport { useField } from '../field/useField'\r\nimport JPortal from '../primitives/JPortal.vue'\r\n\r\nexport interface SelectOption<V = string | number> {\r\n value: V\r\n label: string\r\n description?: string\r\n disabled?: boolean\r\n}\r\n\r\nexport interface SelectProps<V> extends FieldProps {\r\n /** Options as objects, or plain strings when value and label are the same. */\r\n options: Array<SelectOption<V> | V>\r\n placeholder?: string\r\n size?: 'sm' | 'md' | 'lg'\r\n /** Submits the value under this name in a form. */\r\n name?: string\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = withDefaults(defineProps<SelectProps<T>>(), {\r\n placeholder: 'Select…',\r\n size: 'md',\r\n error: undefined,\r\n})\r\n\r\nconst model = defineModel<T | null>({ default: null })\r\n\r\nconst emit = defineEmits<{\r\n open: []\r\n close: []\r\n}>()\r\n\r\ndefineSlots<{\r\n option?: (props: { option: SelectOption<T>, selected: boolean, active: boolean }) => unknown\r\n value?: (props: { option: SelectOption<T> }) => unknown\r\n label?: () => unknown\r\n}>()\r\n\r\nconst field = useField(props)\r\nconst listboxId = computed(() => `${field.id.value}-listbox`)\r\nconst valueId = computed(() => `${field.id.value}-value`)\r\nconst optionId = (index: number) => `${field.id.value}-option-${index}`\r\n\r\nconst normalized = computed<SelectOption<T>[]>(() =>\r\n props.options.map(option =>\r\n typeof option === 'object' && option !== null ? option : { value: option as T, label: String(option) },\r\n ),\r\n)\r\n\r\n/*\r\n * The list the user sees and navigates. Today it's every option; a search\r\n * input only needs to set `query` for this to become a filtered view.\r\n */\r\nconst query = ref('')\r\nconst visibleOptions = computed(() => {\r\n const q = query.value.trim().toLowerCase()\r\n if (!q) return normalized.value\r\n return normalized.value.filter(option => option.label.toLowerCase().includes(q))\r\n})\r\n\r\nconst selected = computed(() => normalized.value.find(option => option.value === model.value))\r\n\r\nconst open = ref(false)\r\nconst activeIndex = ref(-1)\r\nconst trigger = ref<HTMLButtonElement | null>(null)\r\nconst listbox = ref<HTMLElement | null>(null)\r\nconst pointerActive = ref(false)\r\n\r\nconst { styles } = useFloating(trigger, listbox, { placement: 'bottom-start', offset: 4, matchWidth: true })\r\nconst typeahead = useTypeahead()\r\n\r\nuseLayer({\r\n active: open,\r\n elements: () => [trigger.value, listbox.value],\r\n onEscape: () => close(),\r\n onPointerDownOutside: () => close(),\r\n})\r\n\r\nconst isEnabled = (index: number) => {\r\n const option = visibleOptions.value[index]\r\n return !!option && !option.disabled\r\n}\r\n\r\nfunction step(from: number, delta: 1 | -1): number {\r\n const count = visibleOptions.value.length\r\n for (let i = from + delta; i >= 0 && i < count; i += delta) {\r\n if (isEnabled(i)) return i\r\n }\r\n return from\r\n}\r\n\r\nconst first = () => step(-1, 1)\r\nconst last = () => step(visibleOptions.value.length, -1)\r\n\r\nfunction setActive(index: number, scroll = true) {\r\n activeIndex.value = index\r\n if (!scroll) return\r\n nextTick(() => {\r\n document.getElementById(optionId(index))?.scrollIntoView({ block: 'nearest' })\r\n })\r\n}\r\n\r\nfunction openList(focus: 'selected' | 'first' | 'last' = 'selected') {\r\n if (props.disabled || open.value) return\r\n open.value = true\r\n emit('open')\r\n const selectedIndex = visibleOptions.value.findIndex(option => option.value === model.value)\r\n const target = focus === 'last' ? last() : focus === 'first' ? first() : selectedIndex >= 0 ? selectedIndex : first()\r\n setActive(target)\r\n}\r\n\r\nfunction close() {\r\n if (!open.value) return\r\n open.value = false\r\n pointerActive.value = false\r\n emit('close')\r\n}\r\n\r\nfunction commit(index: number) {\r\n const option = visibleOptions.value[index]\r\n if (!option || option.disabled) return\r\n model.value = option.value\r\n close()\r\n}\r\n\r\nfunction onTriggerClick() {\r\n if (open.value) close()\r\n else openList()\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (props.disabled) return\r\n const { key } = event\r\n\r\n if (!open.value) {\r\n if (key === 'ArrowDown' || key === 'Enter' || key === ' ') {\r\n event.preventDefault()\r\n openList()\r\n } else if (key === 'ArrowUp') {\r\n event.preventDefault()\r\n openList()\r\n } else if (key === 'Home') {\r\n event.preventDefault()\r\n openList('first')\r\n } else if (key === 'End') {\r\n event.preventDefault()\r\n openList('last')\r\n } else if (key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey) {\r\n const index = typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\r\n if (index >= 0) {\r\n openList()\r\n setActive(index)\r\n }\r\n }\r\n return\r\n }\r\n\r\n switch (key) {\r\n case 'ArrowDown':\r\n event.preventDefault()\r\n if (event.altKey) return\r\n setActive(step(activeIndex.value, 1))\r\n break\r\n case 'ArrowUp':\r\n event.preventDefault()\r\n if (event.altKey) return commit(activeIndex.value)\r\n setActive(step(activeIndex.value, -1))\r\n break\r\n case 'Home':\r\n event.preventDefault()\r\n setActive(first())\r\n break\r\n case 'End':\r\n event.preventDefault()\r\n setActive(last())\r\n break\r\n case 'PageDown':\r\n event.preventDefault()\r\n setActive(Math.min(last(), step(Math.min(activeIndex.value + 9, visibleOptions.value.length - 1), 1)))\r\n break\r\n case 'PageUp':\r\n event.preventDefault()\r\n setActive(Math.max(first(), step(Math.max(activeIndex.value - 9, 0), -1)))\r\n break\r\n case 'Enter':\r\n event.preventDefault()\r\n commit(activeIndex.value)\r\n break\r\n case 'Tab':\r\n commit(activeIndex.value)\r\n close()\r\n break\r\n case ' ':\r\n event.preventDefault()\r\n if (typeahead.isSearching()) typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\r\n else commit(activeIndex.value)\r\n break\r\n default:\r\n if (key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey) {\r\n const index = typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\r\n if (index >= 0) setActive(index)\r\n }\r\n }\r\n}\r\n\r\n// Only let the pointer steer the active option once it actually moves, so a\r\n// list opening under a resting cursor doesn't hijack the keyboard position.\r\nfunction onOptionPointerMove(index: number) {\r\n pointerActive.value = true\r\n if (activeIndex.value !== index && isEnabled(index)) setActive(index, false)\r\n}\r\n\r\nwatch(() => props.disabled, value => value && close())\r\n\r\ndefineExpose({ focus: () => trigger.value?.focus(), open: () => openList(), close })\r\n</script>\r\n\r\n<template>\r\n <JFieldShell\r\n :id=\"field.id.value\"\r\n :class=\"$attrs.class\"\r\n :style=\"$attrs.style\"\r\n :label-id=\"field.labelId.value\"\r\n :description-id=\"field.descriptionId.value\"\r\n :error-id=\"field.errorId.value\"\r\n :label=\"label\"\r\n :description=\"description\"\r\n :error-message=\"field.errorMessage.value\"\r\n :show-description=\"field.showDescription.value\"\r\n :required=\"required\"\r\n :disabled=\"disabled\"\r\n >\r\n <template v-if=\"$slots.label\" #label>\r\n <slot name=\"label\" />\r\n </template>\r\n <button\r\n :id=\"field.id.value\"\r\n ref=\"trigger\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n type=\"button\"\r\n role=\"combobox\"\r\n class=\"j-control j-select__trigger\"\r\n :class=\"[`j-control--${size}`, { 'is-open': open, 'is-invalid': field.invalid.value, 'is-disabled': disabled }]\"\r\n aria-haspopup=\"listbox\"\r\n :aria-expanded=\"open\"\r\n :aria-controls=\"open ? listboxId : undefined\"\r\n :aria-activedescendant=\"open && activeIndex >= 0 ? optionId(activeIndex) : undefined\"\r\n :aria-labelledby=\"label || $slots.label ? `${field.labelId.value} ${valueId}` : undefined\"\r\n :aria-describedby=\"field.describedBy.value\"\r\n :aria-invalid=\"field.invalid.value || undefined\"\r\n :aria-required=\"required || undefined\"\r\n :disabled=\"disabled\"\r\n @click=\"onTriggerClick\"\r\n @keydown=\"onKeydown\"\r\n >\r\n <span :id=\"valueId\" class=\"j-select__value\" :class=\"{ 'is-placeholder': !selected }\">\r\n <slot v-if=\"selected\" name=\"value\" :option=\"selected\">{{ selected.label }}</slot>\r\n <template v-else>{{ placeholder }}</template>\r\n </span>\r\n <IconChevronUpDown class=\"j-select__chevron\" />\r\n </button>\r\n <input v-if=\"name\" type=\"hidden\" :name=\"name\" :value=\"model ?? ''\" />\r\n\r\n <JPortal>\r\n <Transition name=\"j-pop\">\r\n <ul\r\n v-if=\"open\"\r\n :id=\"listboxId\"\r\n ref=\"listbox\"\r\n role=\"listbox\"\r\n class=\"j-select__listbox j-popover-surface\"\r\n :class=\"{ 'is-pointer': pointerActive }\"\r\n :style=\"styles\"\r\n :aria-labelledby=\"label ? field.labelId.value : undefined\"\r\n tabindex=\"-1\"\r\n @pointerdown.prevent\r\n >\r\n <li\r\n v-for=\"(option, index) in visibleOptions\"\r\n :id=\"optionId(index)\"\r\n :key=\"String(option.value)\"\r\n role=\"option\"\r\n class=\"j-select__option\"\r\n :class=\"{ 'is-active': index === activeIndex, 'is-selected': option.value === model }\"\r\n :aria-selected=\"option.value === model\"\r\n :aria-disabled=\"option.disabled || undefined\"\r\n @pointermove=\"onOptionPointerMove(index)\"\r\n @click=\"commit(index)\"\r\n >\r\n <span class=\"j-select__option-text\">\r\n <slot name=\"option\" :option=\"option\" :selected=\"option.value === model\" :active=\"index === activeIndex\">\r\n <span class=\"j-select__option-label\">{{ option.label }}</span>\r\n <span v-if=\"option.description\" class=\"j-select__option-description\">{{ option.description }}</span>\r\n </slot>\r\n </span>\r\n <IconCheck class=\"j-select__check\" />\r\n </li>\r\n <li v-if=\"visibleOptions.length === 0\" class=\"j-select__empty\" role=\"presentation\">\r\n No options\r\n </li>\r\n </ul>\r\n </Transition>\r\n </JPortal>\r\n </JFieldShell>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-select__trigger {\r\n width: 100%;\r\n justify-content: space-between;\r\n text-align: left;\r\n cursor: pointer;\r\n -webkit-tap-highlight-color: transparent;\r\n}\r\n\r\n.j-select__trigger:focus-visible {\r\n outline: none;\r\n}\r\n\r\n.j-select__trigger:focus-visible:not(.is-invalid) {\r\n border-color: var(--juxt-accent);\r\n box-shadow: 0 0 0 3px var(--juxt-ring-soft);\r\n}\r\n\r\n.j-select__trigger:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-select__value {\r\n overflow: hidden;\r\n min-width: 0;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-select__value.is-placeholder {\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-select__trigger.is-disabled .j-select__value {\r\n color: var(--juxt-fg-disabled);\r\n}\r\n\r\n.j-select__chevron {\r\n flex: none;\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n margin-right: -0.125rem;\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-select__listbox {\r\n z-index: var(--juxt-z-popover);\r\n box-sizing: border-box;\r\n max-width: calc(100vw - 16px);\r\n max-height: min(18rem, var(--j-available-height, 18rem));\r\n margin: 0;\r\n padding: var(--juxt-space-1);\r\n overflow-y: auto;\r\n overscroll-behavior: contain;\r\n list-style: none;\r\n outline: none;\r\n scroll-padding-block: var(--juxt-space-1);\r\n}\r\n\r\n.j-select__option {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-2);\r\n min-height: var(--juxt-control-sm);\r\n padding: var(--juxt-space-1-5) var(--juxt-space-2);\r\n border-radius: var(--juxt-radius-xs);\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.j-select__option.is-active {\r\n background: var(--juxt-surface-hover);\r\n}\r\n\r\n.j-select__option[aria-disabled='true'] {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-select__option-text {\r\n display: flex;\r\n flex: 1;\r\n flex-direction: column;\r\n gap: 1px;\r\n min-width: 0;\r\n}\r\n\r\n.j-select__option-label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.j-select__option-description {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n}\r\n\r\n.j-select__check {\r\n flex: none;\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n color: var(--juxt-accent-text);\r\n opacity: 0;\r\n transform: scale(0.8);\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-select__option.is-selected .j-select__check {\r\n opacity: 1;\r\n transform: none;\r\n}\r\n\r\n.j-select__empty {\r\n padding: var(--juxt-space-3) var(--juxt-space-2);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n text-align: center;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\" generic=\"T extends string | number = string\">\r\nimport type { FieldProps } from '../field/useField'\r\nimport { computed, nextTick, ref, watch } from 'vue'\r\nimport { useFloating } from '../../composables/useFloating'\r\nimport { useLayer } from '../../composables/useLayer'\r\nimport { useTypeahead } from '../../composables/useTypeahead'\r\nimport { IconCheck, IconChevronUpDown } from '../../icons'\r\nimport JFieldShell from '../field/JFieldShell.vue'\r\nimport { useField } from '../field/useField'\r\nimport JPortal from '../primitives/JPortal.vue'\r\n\r\nexport interface SelectOption<V = string | number> {\r\n value: V\r\n label: string\r\n description?: string\r\n disabled?: boolean\r\n}\r\n\r\nexport interface SelectProps<V> extends FieldProps {\r\n /** Options as objects, or plain strings when value and label are the same. */\r\n options: Array<SelectOption<V> | V>\r\n placeholder?: string\r\n size?: 'sm' | 'md' | 'lg'\r\n /** Submits the value under this name in a form. */\r\n name?: string\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = withDefaults(defineProps<SelectProps<T>>(), {\r\n placeholder: 'Select…',\r\n size: 'md',\r\n error: undefined,\r\n})\r\n\r\nconst model = defineModel<T | null>({ default: null })\r\n\r\nconst emit = defineEmits<{\r\n open: []\r\n close: []\r\n}>()\r\n\r\ndefineSlots<{\r\n option?: (props: { option: SelectOption<T>, selected: boolean, active: boolean }) => unknown\r\n value?: (props: { option: SelectOption<T> }) => unknown\r\n label?: () => unknown\r\n}>()\r\n\r\nconst field = useField(props)\r\nconst listboxId = computed(() => `${field.id.value}-listbox`)\r\nconst valueId = computed(() => `${field.id.value}-value`)\r\nconst optionId = (index: number) => `${field.id.value}-option-${index}`\r\n\r\nconst normalized = computed<SelectOption<T>[]>(() =>\r\n props.options.map(option =>\r\n typeof option === 'object' && option !== null ? option : { value: option as T, label: String(option) },\r\n ),\r\n)\r\n\r\n/*\r\n * The list the user sees and navigates. Today it's every option; a search\r\n * input only needs to set `query` for this to become a filtered view.\r\n */\r\nconst query = ref('')\r\nconst visibleOptions = computed(() => {\r\n const q = query.value.trim().toLowerCase()\r\n if (!q) return normalized.value\r\n return normalized.value.filter(option => option.label.toLowerCase().includes(q))\r\n})\r\n\r\nconst selected = computed(() => normalized.value.find(option => option.value === model.value))\r\n\r\nconst open = ref(false)\r\nconst activeIndex = ref(-1)\r\nconst trigger = ref<HTMLButtonElement | null>(null)\r\nconst listbox = ref<HTMLElement | null>(null)\r\nconst pointerActive = ref(false)\r\n\r\nconst { styles } = useFloating(trigger, listbox, { placement: 'bottom-start', offset: 4, matchWidth: true })\r\nconst typeahead = useTypeahead()\r\n\r\nuseLayer({\r\n active: open,\r\n elements: () => [trigger.value, listbox.value],\r\n onEscape: () => close(),\r\n onPointerDownOutside: () => close(),\r\n})\r\n\r\nconst isEnabled = (index: number) => {\r\n const option = visibleOptions.value[index]\r\n return !!option && !option.disabled\r\n}\r\n\r\nfunction step(from: number, delta: 1 | -1): number {\r\n const count = visibleOptions.value.length\r\n for (let i = from + delta; i >= 0 && i < count; i += delta) {\r\n if (isEnabled(i)) return i\r\n }\r\n return from\r\n}\r\n\r\nconst first = () => step(-1, 1)\r\nconst last = () => step(visibleOptions.value.length, -1)\r\n\r\nfunction setActive(index: number, scroll = true) {\r\n activeIndex.value = index\r\n if (!scroll) return\r\n nextTick(() => {\r\n document.getElementById(optionId(index))?.scrollIntoView({ block: 'nearest' })\r\n })\r\n}\r\n\r\nfunction openList(focus: 'selected' | 'first' | 'last' = 'selected') {\r\n if (props.disabled || open.value) return\r\n open.value = true\r\n emit('open')\r\n const selectedIndex = visibleOptions.value.findIndex(option => option.value === model.value)\r\n const target = focus === 'last' ? last() : focus === 'first' ? first() : selectedIndex >= 0 ? selectedIndex : first()\r\n setActive(target)\r\n}\r\n\r\nfunction close() {\r\n if (!open.value) return\r\n open.value = false\r\n pointerActive.value = false\r\n emit('close')\r\n}\r\n\r\nfunction commit(index: number) {\r\n const option = visibleOptions.value[index]\r\n if (!option || option.disabled) return\r\n model.value = option.value\r\n close()\r\n}\r\n\r\nfunction onTriggerClick() {\r\n if (open.value) close()\r\n else openList()\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (props.disabled) return\r\n const { key } = event\r\n\r\n if (!open.value) {\r\n if (key === 'ArrowDown' || key === 'Enter' || key === ' ') {\r\n event.preventDefault()\r\n openList()\r\n } else if (key === 'ArrowUp') {\r\n event.preventDefault()\r\n openList()\r\n } else if (key === 'Home') {\r\n event.preventDefault()\r\n openList('first')\r\n } else if (key === 'End') {\r\n event.preventDefault()\r\n openList('last')\r\n } else if (key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey) {\r\n const index = typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\r\n if (index >= 0) {\r\n openList()\r\n setActive(index)\r\n }\r\n }\r\n return\r\n }\r\n\r\n switch (key) {\r\n case 'ArrowDown':\r\n event.preventDefault()\r\n if (event.altKey) return\r\n setActive(step(activeIndex.value, 1))\r\n break\r\n case 'ArrowUp':\r\n event.preventDefault()\r\n if (event.altKey) return commit(activeIndex.value)\r\n setActive(step(activeIndex.value, -1))\r\n break\r\n case 'Home':\r\n event.preventDefault()\r\n setActive(first())\r\n break\r\n case 'End':\r\n event.preventDefault()\r\n setActive(last())\r\n break\r\n case 'PageDown':\r\n event.preventDefault()\r\n setActive(Math.min(last(), step(Math.min(activeIndex.value + 9, visibleOptions.value.length - 1), 1)))\r\n break\r\n case 'PageUp':\r\n event.preventDefault()\r\n setActive(Math.max(first(), step(Math.max(activeIndex.value - 9, 0), -1)))\r\n break\r\n case 'Enter':\r\n event.preventDefault()\r\n commit(activeIndex.value)\r\n break\r\n case 'Tab':\r\n commit(activeIndex.value)\r\n close()\r\n break\r\n case ' ':\r\n event.preventDefault()\r\n if (typeahead.isSearching()) typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\r\n else commit(activeIndex.value)\r\n break\r\n default:\r\n if (key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey) {\r\n const index = typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\r\n if (index >= 0) setActive(index)\r\n }\r\n }\r\n}\r\n\r\n// Only let the pointer steer the active option once it actually moves, so a\r\n// list opening under a resting cursor doesn't hijack the keyboard position.\r\nfunction onOptionPointerMove(index: number) {\r\n pointerActive.value = true\r\n if (activeIndex.value !== index && isEnabled(index)) setActive(index, false)\r\n}\r\n\r\nwatch(() => props.disabled, value => value && close())\r\n\r\ndefineExpose({ focus: () => trigger.value?.focus(), open: () => openList(), close })\r\n</script>\r\n\r\n<template>\r\n <JFieldShell\r\n :id=\"field.id.value\"\r\n :class=\"$attrs.class\"\r\n :style=\"$attrs.style\"\r\n :label-id=\"field.labelId.value\"\r\n :description-id=\"field.descriptionId.value\"\r\n :error-id=\"field.errorId.value\"\r\n :label=\"label\"\r\n :description=\"description\"\r\n :error-message=\"field.errorMessage.value\"\r\n :show-description=\"field.showDescription.value\"\r\n :required=\"required\"\r\n :disabled=\"disabled\"\r\n >\r\n <template v-if=\"$slots.label\" #label>\r\n <slot name=\"label\" />\r\n </template>\r\n <button\r\n :id=\"field.id.value\"\r\n ref=\"trigger\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n type=\"button\"\r\n role=\"combobox\"\r\n class=\"j-control j-select__trigger\"\r\n :class=\"[`j-control--${size}`, { 'is-open': open, 'is-invalid': field.invalid.value, 'is-disabled': disabled }]\"\r\n aria-haspopup=\"listbox\"\r\n :aria-expanded=\"open\"\r\n :aria-controls=\"open ? listboxId : undefined\"\r\n :aria-activedescendant=\"open && activeIndex >= 0 ? optionId(activeIndex) : undefined\"\r\n :aria-labelledby=\"label || $slots.label ? `${field.labelId.value} ${valueId}` : undefined\"\r\n :aria-describedby=\"field.describedBy.value\"\r\n :aria-invalid=\"field.invalid.value || undefined\"\r\n :aria-required=\"required || undefined\"\r\n :disabled=\"disabled\"\r\n @click=\"onTriggerClick\"\r\n @keydown=\"onKeydown\"\r\n >\r\n <span :id=\"valueId\" class=\"j-select__value\" :class=\"{ 'is-placeholder': !selected }\">\r\n <slot v-if=\"selected\" name=\"value\" :option=\"selected\">{{ selected.label }}</slot>\r\n <template v-else>{{ placeholder }}</template>\r\n </span>\r\n <IconChevronUpDown class=\"j-select__chevron\" />\r\n </button>\r\n <input v-if=\"name\" type=\"hidden\" :name=\"name\" :value=\"model ?? ''\" />\r\n\r\n <JPortal>\r\n <Transition name=\"j-pop\">\r\n <ul\r\n v-if=\"open\"\r\n :id=\"listboxId\"\r\n ref=\"listbox\"\r\n role=\"listbox\"\r\n class=\"j-select__listbox j-popover-surface\"\r\n :class=\"{ 'is-pointer': pointerActive }\"\r\n :style=\"styles\"\r\n :aria-labelledby=\"label ? field.labelId.value : undefined\"\r\n tabindex=\"-1\"\r\n @pointerdown.prevent\r\n >\r\n <li\r\n v-for=\"(option, index) in visibleOptions\"\r\n :id=\"optionId(index)\"\r\n :key=\"String(option.value)\"\r\n role=\"option\"\r\n class=\"j-select__option\"\r\n :class=\"{ 'is-active': index === activeIndex, 'is-selected': option.value === model }\"\r\n :aria-selected=\"option.value === model\"\r\n :aria-disabled=\"option.disabled || undefined\"\r\n @pointermove=\"onOptionPointerMove(index)\"\r\n @click=\"commit(index)\"\r\n >\r\n <span class=\"j-select__option-text\">\r\n <slot name=\"option\" :option=\"option\" :selected=\"option.value === model\" :active=\"index === activeIndex\">\r\n <span class=\"j-select__option-label\">{{ option.label }}</span>\r\n <span v-if=\"option.description\" class=\"j-select__option-description\">{{ option.description }}</span>\r\n </slot>\r\n </span>\r\n <IconCheck class=\"j-select__check\" />\r\n </li>\r\n <li v-if=\"visibleOptions.length === 0\" class=\"j-select__empty\" role=\"presentation\">\r\n No options\r\n </li>\r\n </ul>\r\n </Transition>\r\n </JPortal>\r\n </JFieldShell>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-select__trigger {\r\n width: 100%;\r\n justify-content: space-between;\r\n text-align: left;\r\n cursor: pointer;\r\n -webkit-tap-highlight-color: transparent;\r\n}\r\n\r\n.j-select__trigger:focus-visible {\r\n outline: none;\r\n}\r\n\r\n.j-select__trigger:focus-visible:not(.is-invalid) {\r\n border-color: var(--juxt-accent);\r\n box-shadow: 0 0 0 3px var(--juxt-ring-soft);\r\n}\r\n\r\n.j-select__trigger:disabled {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-select__value {\r\n overflow: hidden;\r\n min-width: 0;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-select__value.is-placeholder {\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-select__trigger.is-disabled .j-select__value {\r\n color: var(--juxt-fg-disabled);\r\n}\r\n\r\n.j-select__chevron {\r\n flex: none;\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n margin-right: -0.125rem;\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-select__listbox {\r\n z-index: var(--juxt-z-popover);\r\n box-sizing: border-box;\r\n max-width: calc(100vw - 16px);\r\n max-height: min(18rem, var(--j-available-height, 18rem));\r\n margin: 0;\r\n padding: var(--juxt-space-1);\r\n overflow-y: auto;\r\n overscroll-behavior: contain;\r\n list-style: none;\r\n outline: none;\r\n scroll-padding-block: var(--juxt-space-1);\r\n}\r\n\r\n.j-select__option {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-2);\r\n min-height: var(--juxt-control-sm);\r\n padding: var(--juxt-space-1-5) var(--juxt-space-2);\r\n border-radius: var(--juxt-radius-xs);\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.j-select__option.is-active {\r\n background: var(--juxt-surface-hover);\r\n}\r\n\r\n.j-select__option[aria-disabled='true'] {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-select__option-text {\r\n display: flex;\r\n flex: 1;\r\n flex-direction: column;\r\n gap: 1px;\r\n min-width: 0;\r\n}\r\n\r\n.j-select__option-label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.j-select__option-description {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n}\r\n\r\n.j-select__check {\r\n flex: none;\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n color: var(--juxt-accent-text);\r\n opacity: 0;\r\n transform: scale(0.8);\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-select__option.is-selected .j-select__check {\r\n opacity: 1;\r\n transform: none;\r\n}\r\n\r\n.j-select__empty {\r\n padding: var(--juxt-space-3) var(--juxt-space-2);\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n text-align: center;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nexport interface SeparatorProps {\r\n orientation?: 'horizontal' | 'vertical'\r\n /** Text in the middle of a horizontal separator, e.g. \"or\". */\r\n label?: string\r\n /**\r\n * Purely visual (the default). Set to false when the separator divides\r\n * meaningful groups and should be announced.\r\n */\r\n decorative?: boolean\r\n}\r\n\r\nwithDefaults(defineProps<SeparatorProps>(), { orientation: 'horizontal', decorative: true })\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-separator\"\r\n :class=\"[`j-separator--${orientation}`, { 'j-separator--labelled': label || $slots.default }]\"\r\n :role=\"decorative ? 'none' : 'separator'\"\r\n :aria-orientation=\"decorative ? undefined : orientation\"\r\n >\r\n <span v-if=\"label || $slots.default\" class=\"j-separator__label\"><slot>{{ label }}</slot></span>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-separator {\r\n flex: none;\r\n background: var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-separator,\r\n[data-theme='dark'] .j-separator {\r\n background: var(--juxt-border);\r\n}\r\n\r\n.j-separator--horizontal {\r\n width: 100%;\r\n height: 1px;\r\n}\r\n\r\n.j-separator--vertical {\r\n align-self: stretch;\r\n width: 1px;\r\n min-height: 1rem;\r\n}\r\n\r\n.j-separator--labelled {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-3);\r\n height: auto;\r\n background: none !important;\r\n color: var(--juxt-fg-muted);\r\n font-family: var(--juxt-font-sans);\r\n font-size: var(--juxt-text-xs);\r\n}\r\n\r\n.j-separator--labelled::before,\r\n.j-separator--labelled::after {\r\n content: '';\r\n flex: 1;\r\n height: 1px;\r\n background: var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-separator--labelled::before,\r\n.dark .j-separator--labelled::after,\r\n[data-theme='dark'] .j-separator--labelled::before,\r\n[data-theme='dark'] .j-separator--labelled::after {\r\n background: var(--juxt-border);\r\n}\r\n\r\n.j-separator__label {\r\n flex: none;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nexport interface SeparatorProps {\r\n orientation?: 'horizontal' | 'vertical'\r\n /** Text in the middle of a horizontal separator, e.g. \"or\". */\r\n label?: string\r\n /**\r\n * Purely visual (the default). Set to false when the separator divides\r\n * meaningful groups and should be announced.\r\n */\r\n decorative?: boolean\r\n}\r\n\r\nwithDefaults(defineProps<SeparatorProps>(), { orientation: 'horizontal', decorative: true })\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-separator\"\r\n :class=\"[`j-separator--${orientation}`, { 'j-separator--labelled': label || $slots.default }]\"\r\n :role=\"decorative ? 'none' : 'separator'\"\r\n :aria-orientation=\"decorative ? undefined : orientation\"\r\n >\r\n <span v-if=\"label || $slots.default\" class=\"j-separator__label\"><slot>{{ label }}</slot></span>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-separator {\r\n flex: none;\r\n background: var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-separator,\r\n[data-theme='dark'] .j-separator {\r\n background: var(--juxt-border);\r\n}\r\n\r\n.j-separator--horizontal {\r\n width: 100%;\r\n height: 1px;\r\n}\r\n\r\n.j-separator--vertical {\r\n align-self: stretch;\r\n width: 1px;\r\n min-height: 1rem;\r\n}\r\n\r\n.j-separator--labelled {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-3);\r\n height: auto;\r\n background: none !important;\r\n color: var(--juxt-fg-muted);\r\n font-family: var(--juxt-font-sans);\r\n font-size: var(--juxt-text-xs);\r\n}\r\n\r\n.j-separator--labelled::before,\r\n.j-separator--labelled::after {\r\n content: '';\r\n flex: 1;\r\n height: 1px;\r\n background: var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-separator--labelled::before,\r\n.dark .j-separator--labelled::after,\r\n[data-theme='dark'] .j-separator--labelled::before,\r\n[data-theme='dark'] .j-separator--labelled::after {\r\n background: var(--juxt-border);\r\n}\r\n\r\n.j-separator__label {\r\n flex: none;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { ref, useId } from 'vue'\r\nimport { FIELD_SELECTOR, useModal } from '../../composables/useModal'\r\nimport { IconX } from '../../icons'\r\nimport JPortal from '../primitives/JPortal.vue'\r\n\r\nexport interface SheetProps {\r\n title?: string\r\n description?: string\r\n /** Edge the sheet slides in from. */\r\n side?: 'right' | 'left' | 'bottom'\r\n /** Width (or height for `bottom`): 320, 400 or 560px. */\r\n size?: 'sm' | 'md' | 'lg'\r\n closeOnOverlay?: boolean\r\n closeOnEscape?: boolean\r\n hideClose?: boolean\r\n /** Accessible label when there is no visible title. */\r\n ariaLabel?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<SheetProps>(), {\r\n side: 'right',\r\n size: 'md',\r\n closeOnOverlay: true,\r\n closeOnEscape: true,\r\n})\r\n\r\nconst open = defineModel<boolean>('open', { default: false })\r\n\r\nconst emit = defineEmits<{\r\n /** After the leave transition finishes. */\r\n afterClose: []\r\n}>()\r\n\r\ndefineSlots<{\r\n default?: (props: { close: () => void }) => unknown\r\n header?: (props: { titleId: string, descriptionId: string }) => unknown\r\n footer?: (props: { close: () => void }) => unknown\r\n}>()\r\n\r\nconst id = useId()\r\nconst titleId = `j-${id}-title`\r\nconst descriptionId = `j-${id}-description`\r\nconst panel = ref<HTMLElement | null>(null)\r\n\r\nfunction close() {\r\n open.value = false\r\n}\r\n\r\nconst { nudging, onBackdropPointerDown, onBackdropClick } = useModal(panel, open, {\r\n closeOnEscape: () => props.closeOnEscape,\r\n closeOnOverlay: () => props.closeOnOverlay,\r\n close,\r\n focusSelector: `.j-sheet__body ${FIELD_SELECTOR}`,\r\n})\r\n\r\ndefineExpose({ close })\r\n</script>\r\n\r\n<template>\r\n <JPortal>\r\n <Transition name=\"j-sheet-overlay\">\r\n <div v-if=\"open\" class=\"j-sheet__overlay\" aria-hidden=\"true\" />\r\n </Transition>\r\n <Transition name=\"j-sheet\" :duration=\"{ enter: 280, leave: 180 }\" @after-leave=\"emit('afterClose')\">\r\n <div\r\n v-if=\"open\"\r\n class=\"j-sheet__positioner\"\r\n :class=\"`j-sheet__positioner--${side}`\"\r\n @pointerdown=\"onBackdropPointerDown\"\r\n @click=\"onBackdropClick\"\r\n >\r\n <div\r\n ref=\"panel\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n tabindex=\"-1\"\r\n class=\"j-sheet\"\r\n :class=\"[`j-sheet--${side}`, `j-sheet--${size}`, { 'is-nudging': nudging }]\"\r\n :aria-labelledby=\"title || $slots.header ? titleId : undefined\"\r\n :aria-label=\"ariaLabel\"\r\n :aria-describedby=\"description ? descriptionId : undefined\"\r\n @animationend=\"nudging = false\"\r\n >\r\n <header v-if=\"title || description || $slots.header\" class=\"j-sheet__header\">\r\n <slot name=\"header\" :title-id=\"titleId\" :description-id=\"descriptionId\">\r\n <h2 :id=\"titleId\" class=\"j-sheet__title\">\r\n {{ title }}\r\n </h2>\r\n <p v-if=\"description\" :id=\"descriptionId\" class=\"j-sheet__description\">\r\n {{ description }}\r\n </p>\r\n </slot>\r\n </header>\r\n <div class=\"j-sheet__body\">\r\n <slot :close=\"close\" />\r\n </div>\r\n <footer v-if=\"$slots.footer\" class=\"j-sheet__footer\">\r\n <slot name=\"footer\" :close=\"close\" />\r\n </footer>\r\n <button v-if=\"!hideClose\" type=\"button\" class=\"j-sheet__close j-focusable\" aria-label=\"Close\" @click=\"close\">\r\n <IconX />\r\n </button>\r\n </div>\r\n </div>\r\n </Transition>\r\n </JPortal>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-sheet__overlay {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-overlay);\r\n background: var(--juxt-overlay);\r\n}\r\n\r\n.j-sheet__positioner {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-modal);\r\n display: flex;\r\n padding: var(--juxt-space-2);\r\n}\r\n\r\n.j-sheet__positioner--right {\r\n justify-content: flex-end;\r\n}\r\n\r\n.j-sheet__positioner--left {\r\n justify-content: flex-start;\r\n}\r\n\r\n.j-sheet__positioner--bottom {\r\n align-items: flex-end;\r\n padding-bottom: max(var(--juxt-space-2), env(safe-area-inset-bottom));\r\n}\r\n\r\n/* A floating panel with a small inset from the edges | calmer than edge-to-edge. */\r\n.j-sheet {\r\n position: relative;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n border: 1px solid var(--juxt-border);\r\n border-radius: var(--juxt-radius-lg);\r\n background: var(--juxt-surface-raised);\r\n box-shadow: var(--juxt-shadow-lg);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n outline: none;\r\n}\r\n\r\n.j-sheet--right,\r\n.j-sheet--left {\r\n height: 100%;\r\n}\r\n\r\n.j-sheet--right.j-sheet--sm,\r\n.j-sheet--left.j-sheet--sm {\r\n max-width: 20rem;\r\n}\r\n\r\n.j-sheet--right.j-sheet--md,\r\n.j-sheet--left.j-sheet--md {\r\n max-width: 25rem;\r\n}\r\n\r\n.j-sheet--right.j-sheet--lg,\r\n.j-sheet--left.j-sheet--lg {\r\n max-width: 35rem;\r\n}\r\n\r\n.j-sheet--bottom.j-sheet--sm {\r\n max-height: 20rem;\r\n}\r\n\r\n.j-sheet--bottom.j-sheet--md {\r\n max-height: 25rem;\r\n}\r\n\r\n.j-sheet--bottom.j-sheet--lg {\r\n max-height: min(35rem, calc(100dvh - var(--juxt-space-12)));\r\n}\r\n\r\n.j-sheet__header {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-1-5);\r\n padding: var(--juxt-space-5) var(--juxt-space-12) var(--juxt-space-4) var(--juxt-space-5);\r\n border-bottom: 1px solid var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-sheet__header,\r\n[data-theme='dark'] .j-sheet__header {\r\n border-bottom-color: var(--juxt-border);\r\n}\r\n\r\n.j-sheet__title {\r\n margin: 0;\r\n font-size: var(--juxt-text-lg);\r\n font-weight: var(--juxt-weight-semibold);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-sheet__description {\r\n margin: 0;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-sheet__body {\r\n flex: 1;\r\n min-height: 0;\r\n padding: var(--juxt-space-5);\r\n overflow-y: auto;\r\n overscroll-behavior: contain;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-sheet__footer {\r\n display: flex;\r\n justify-content: flex-end;\r\n gap: var(--juxt-space-2);\r\n padding: var(--juxt-space-4) var(--juxt-space-5);\r\n border-top: 1px solid var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-sheet__footer,\r\n[data-theme='dark'] .j-sheet__footer {\r\n border-top-color: var(--juxt-border);\r\n}\r\n\r\n.j-sheet__close {\r\n position: absolute;\r\n top: var(--juxt-space-3);\r\n right: var(--juxt-space-3);\r\n display: grid;\r\n place-items: center;\r\n width: var(--juxt-control-sm);\r\n height: var(--juxt-control-sm);\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-sm);\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n cursor: pointer;\r\n transition-property: background-color, color, outline-color, outline-offset;\r\n}\r\n\r\n.j-sheet__close:hover {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-sheet__close > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-sheet.is-nudging {\r\n animation: j-sheet-nudge 220ms var(--juxt-ease-out);\r\n}\r\n\r\n@keyframes j-sheet-nudge {\r\n 40% {\r\n transform: scale(1.006);\r\n }\r\n}\r\n\r\n/* Motion: the backdrop fades; the sheet slides in from its edge. */\r\n.j-sheet-overlay-enter-active {\r\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-sheet-overlay-leave-active {\r\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-in);\r\n}\r\n\r\n.j-sheet-overlay-enter-from,\r\n.j-sheet-overlay-leave-to {\r\n opacity: 0;\r\n}\r\n\r\n.j-sheet-enter-active .j-sheet {\r\n transition: transform 280ms var(--juxt-ease-out);\r\n}\r\n\r\n.j-sheet-leave-active .j-sheet {\r\n transition: transform var(--juxt-duration-normal) var(--juxt-ease-in);\r\n}\r\n\r\n.j-sheet-enter-from .j-sheet--right,\r\n.j-sheet-leave-to .j-sheet--right {\r\n transform: translateX(calc(100% + var(--juxt-space-4)));\r\n}\r\n\r\n.j-sheet-enter-from .j-sheet--left,\r\n.j-sheet-leave-to .j-sheet--left {\r\n transform: translateX(calc(-100% - var(--juxt-space-4)));\r\n}\r\n\r\n.j-sheet-enter-from .j-sheet--bottom,\r\n.j-sheet-leave-to .j-sheet--bottom {\r\n transform: translateY(calc(100% + var(--juxt-space-4)));\r\n}\r\n\r\n/* Reduced motion: the sheet fades in place instead of travelling. */\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-sheet-enter-active .j-sheet,\r\n .j-sheet-leave-active .j-sheet {\r\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-standard);\r\n }\r\n\r\n .j-sheet-enter-from .j-sheet,\r\n .j-sheet-leave-to .j-sheet {\r\n opacity: 0;\r\n transform: none !important;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { ref, useId } from 'vue'\r\nimport { FIELD_SELECTOR, useModal } from '../../composables/useModal'\r\nimport { IconX } from '../../icons'\r\nimport JPortal from '../primitives/JPortal.vue'\r\n\r\nexport interface SheetProps {\r\n title?: string\r\n description?: string\r\n /** Edge the sheet slides in from. */\r\n side?: 'right' | 'left' | 'bottom'\r\n /** Width (or height for `bottom`): 320, 400 or 560px. */\r\n size?: 'sm' | 'md' | 'lg'\r\n closeOnOverlay?: boolean\r\n closeOnEscape?: boolean\r\n hideClose?: boolean\r\n /** Accessible label when there is no visible title. */\r\n ariaLabel?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<SheetProps>(), {\r\n side: 'right',\r\n size: 'md',\r\n closeOnOverlay: true,\r\n closeOnEscape: true,\r\n})\r\n\r\nconst open = defineModel<boolean>('open', { default: false })\r\n\r\nconst emit = defineEmits<{\r\n /** After the leave transition finishes. */\r\n afterClose: []\r\n}>()\r\n\r\ndefineSlots<{\r\n default?: (props: { close: () => void }) => unknown\r\n header?: (props: { titleId: string, descriptionId: string }) => unknown\r\n footer?: (props: { close: () => void }) => unknown\r\n}>()\r\n\r\nconst id = useId()\r\nconst titleId = `j-${id}-title`\r\nconst descriptionId = `j-${id}-description`\r\nconst panel = ref<HTMLElement | null>(null)\r\n\r\nfunction close() {\r\n open.value = false\r\n}\r\n\r\nconst { nudging, onBackdropPointerDown, onBackdropClick } = useModal(panel, open, {\r\n closeOnEscape: () => props.closeOnEscape,\r\n closeOnOverlay: () => props.closeOnOverlay,\r\n close,\r\n focusSelector: `.j-sheet__body ${FIELD_SELECTOR}`,\r\n})\r\n\r\ndefineExpose({ close })\r\n</script>\r\n\r\n<template>\r\n <JPortal>\r\n <Transition name=\"j-sheet-overlay\">\r\n <div v-if=\"open\" class=\"j-sheet__overlay\" aria-hidden=\"true\" />\r\n </Transition>\r\n <Transition name=\"j-sheet\" :duration=\"{ enter: 280, leave: 180 }\" @after-leave=\"emit('afterClose')\">\r\n <div\r\n v-if=\"open\"\r\n class=\"j-sheet__positioner\"\r\n :class=\"`j-sheet__positioner--${side}`\"\r\n @pointerdown=\"onBackdropPointerDown\"\r\n @click=\"onBackdropClick\"\r\n >\r\n <div\r\n ref=\"panel\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n tabindex=\"-1\"\r\n class=\"j-sheet\"\r\n :class=\"[`j-sheet--${side}`, `j-sheet--${size}`, { 'is-nudging': nudging }]\"\r\n :aria-labelledby=\"title || $slots.header ? titleId : undefined\"\r\n :aria-label=\"ariaLabel\"\r\n :aria-describedby=\"description ? descriptionId : undefined\"\r\n @animationend=\"nudging = false\"\r\n >\r\n <header v-if=\"title || description || $slots.header\" class=\"j-sheet__header\">\r\n <slot name=\"header\" :title-id=\"titleId\" :description-id=\"descriptionId\">\r\n <h2 :id=\"titleId\" class=\"j-sheet__title\">\r\n {{ title }}\r\n </h2>\r\n <p v-if=\"description\" :id=\"descriptionId\" class=\"j-sheet__description\">\r\n {{ description }}\r\n </p>\r\n </slot>\r\n </header>\r\n <div class=\"j-sheet__body\">\r\n <slot :close=\"close\" />\r\n </div>\r\n <footer v-if=\"$slots.footer\" class=\"j-sheet__footer\">\r\n <slot name=\"footer\" :close=\"close\" />\r\n </footer>\r\n <button v-if=\"!hideClose\" type=\"button\" class=\"j-sheet__close j-focusable\" aria-label=\"Close\" @click=\"close\">\r\n <IconX />\r\n </button>\r\n </div>\r\n </div>\r\n </Transition>\r\n </JPortal>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-sheet__overlay {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-overlay);\r\n background: var(--juxt-overlay);\r\n}\r\n\r\n.j-sheet__positioner {\r\n position: fixed;\r\n inset: 0;\r\n z-index: var(--juxt-z-modal);\r\n display: flex;\r\n padding: var(--juxt-space-2);\r\n}\r\n\r\n.j-sheet__positioner--right {\r\n justify-content: flex-end;\r\n}\r\n\r\n.j-sheet__positioner--left {\r\n justify-content: flex-start;\r\n}\r\n\r\n.j-sheet__positioner--bottom {\r\n align-items: flex-end;\r\n padding-bottom: max(var(--juxt-space-2), env(safe-area-inset-bottom));\r\n}\r\n\r\n/* A floating panel with a small inset from the edges | calmer than edge-to-edge. */\r\n.j-sheet {\r\n position: relative;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n border: 1px solid var(--juxt-border);\r\n border-radius: var(--juxt-radius-lg);\r\n background: var(--juxt-surface-raised);\r\n box-shadow: var(--juxt-shadow-lg);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n outline: none;\r\n}\r\n\r\n.j-sheet--right,\r\n.j-sheet--left {\r\n height: 100%;\r\n}\r\n\r\n.j-sheet--right.j-sheet--sm,\r\n.j-sheet--left.j-sheet--sm {\r\n max-width: 20rem;\r\n}\r\n\r\n.j-sheet--right.j-sheet--md,\r\n.j-sheet--left.j-sheet--md {\r\n max-width: 25rem;\r\n}\r\n\r\n.j-sheet--right.j-sheet--lg,\r\n.j-sheet--left.j-sheet--lg {\r\n max-width: 35rem;\r\n}\r\n\r\n.j-sheet--bottom.j-sheet--sm {\r\n max-height: 20rem;\r\n}\r\n\r\n.j-sheet--bottom.j-sheet--md {\r\n max-height: 25rem;\r\n}\r\n\r\n.j-sheet--bottom.j-sheet--lg {\r\n max-height: min(35rem, calc(100dvh - var(--juxt-space-12)));\r\n}\r\n\r\n.j-sheet__header {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-1-5);\r\n padding: var(--juxt-space-5) var(--juxt-space-12) var(--juxt-space-4) var(--juxt-space-5);\r\n border-bottom: 1px solid var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-sheet__header,\r\n[data-theme='dark'] .j-sheet__header {\r\n border-bottom-color: var(--juxt-border);\r\n}\r\n\r\n.j-sheet__title {\r\n margin: 0;\r\n font-size: var(--juxt-text-lg);\r\n font-weight: var(--juxt-weight-semibold);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-sheet__description {\r\n margin: 0;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-sheet__body {\r\n flex: 1;\r\n min-height: 0;\r\n padding: var(--juxt-space-5);\r\n overflow-y: auto;\r\n overscroll-behavior: contain;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-sheet__footer {\r\n display: flex;\r\n justify-content: flex-end;\r\n gap: var(--juxt-space-2);\r\n padding: var(--juxt-space-4) var(--juxt-space-5);\r\n border-top: 1px solid var(--juxt-border-subtle);\r\n}\r\n\r\n.dark .j-sheet__footer,\r\n[data-theme='dark'] .j-sheet__footer {\r\n border-top-color: var(--juxt-border);\r\n}\r\n\r\n.j-sheet__close {\r\n position: absolute;\r\n top: var(--juxt-space-3);\r\n right: var(--juxt-space-3);\r\n display: grid;\r\n place-items: center;\r\n width: var(--juxt-control-sm);\r\n height: var(--juxt-control-sm);\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-sm);\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n cursor: pointer;\r\n transition-property: background-color, color, outline-color, outline-offset;\r\n}\r\n\r\n.j-sheet__close:hover {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-sheet__close > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-sheet.is-nudging {\r\n animation: j-sheet-nudge 220ms var(--juxt-ease-out);\r\n}\r\n\r\n@keyframes j-sheet-nudge {\r\n 40% {\r\n transform: scale(1.006);\r\n }\r\n}\r\n\r\n/* Motion: the backdrop fades; the sheet slides in from its edge. */\r\n.j-sheet-overlay-enter-active {\r\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-sheet-overlay-leave-active {\r\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-in);\r\n}\r\n\r\n.j-sheet-overlay-enter-from,\r\n.j-sheet-overlay-leave-to {\r\n opacity: 0;\r\n}\r\n\r\n.j-sheet-enter-active .j-sheet {\r\n transition: transform 280ms var(--juxt-ease-out);\r\n}\r\n\r\n.j-sheet-leave-active .j-sheet {\r\n transition: transform var(--juxt-duration-normal) var(--juxt-ease-in);\r\n}\r\n\r\n.j-sheet-enter-from .j-sheet--right,\r\n.j-sheet-leave-to .j-sheet--right {\r\n transform: translateX(calc(100% + var(--juxt-space-4)));\r\n}\r\n\r\n.j-sheet-enter-from .j-sheet--left,\r\n.j-sheet-leave-to .j-sheet--left {\r\n transform: translateX(calc(-100% - var(--juxt-space-4)));\r\n}\r\n\r\n.j-sheet-enter-from .j-sheet--bottom,\r\n.j-sheet-leave-to .j-sheet--bottom {\r\n transform: translateY(calc(100% + var(--juxt-space-4)));\r\n}\r\n\r\n/* Reduced motion: the sheet fades in place instead of travelling. */\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-sheet-enter-active .j-sheet,\r\n .j-sheet-leave-active .j-sheet {\r\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-standard);\r\n }\r\n\r\n .j-sheet-enter-from .j-sheet,\r\n .j-sheet-leave-to .j-sheet {\r\n opacity: 0;\r\n transform: none !important;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\nexport interface SidebarProps {\n /** Accessible name of the navigation landmark. */\n label?: string\n}\n\nwithDefaults(defineProps<SidebarProps>(), { label: 'Sidebar' })\n\ndefineSlots<{\n /** Top of the sidebar: logo, workspace switcher. */\n header?: () => unknown\n /** Items and groups. */\n default?: () => unknown\n /** Pinned to the bottom: account, settings, help. */\n footer?: () => unknown\n}>()\n</script>\n\n<template>\n <aside class=\"j-sidebar\">\n <div v-if=\"$slots.header\" class=\"j-sidebar__header\">\n <slot name=\"header\" />\n </div>\n <nav class=\"j-sidebar__nav\" :aria-label=\"label\">\n <slot />\n </nav>\n <div v-if=\"$slots.footer\" class=\"j-sidebar__footer\">\n <slot name=\"footer\" />\n </div>\n </aside>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar {\n display: flex;\n flex-direction: column;\n width: 100%;\n /*\n * Not `height: 100%`: inside a parent that only has a min-height (e.g. a\n * `min-h-screen` flex row) a percentage height can't resolve, and a set\n * height also stops the flex/grid parent from stretching the sidebar, so it\n * would collapse to its content. Min and max of 100% pin it to a parent with\n * a real height (nav scrolls inside), and leave it `auto` everywhere else, so\n * the parent stretches it to full height and the footer stays at the bottom.\n */\n height: auto;\n min-height: 100%;\n max-height: 100%;\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-sidebar__header {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n padding: var(--juxt-space-3) var(--juxt-space-3) var(--juxt-space-2);\n}\n\n.j-sidebar__nav {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--juxt-space-4);\n min-height: 0;\n padding: var(--juxt-space-2);\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n.j-sidebar__footer {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n padding: var(--juxt-space-2);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-sidebar__footer,\n[data-theme='dark'] .j-sidebar__footer {\n border-top-color: var(--juxt-border);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport interface SidebarProps {\n /** Accessible name of the navigation landmark. */\n label?: string\n}\n\nwithDefaults(defineProps<SidebarProps>(), { label: 'Sidebar' })\n\ndefineSlots<{\n /** Top of the sidebar: logo, workspace switcher. */\n header?: () => unknown\n /** Items and groups. */\n default?: () => unknown\n /** Pinned to the bottom: account, settings, help. */\n footer?: () => unknown\n}>()\n</script>\n\n<template>\n <aside class=\"j-sidebar\">\n <div v-if=\"$slots.header\" class=\"j-sidebar__header\">\n <slot name=\"header\" />\n </div>\n <nav class=\"j-sidebar__nav\" :aria-label=\"label\">\n <slot />\n </nav>\n <div v-if=\"$slots.footer\" class=\"j-sidebar__footer\">\n <slot name=\"footer\" />\n </div>\n </aside>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar {\n display: flex;\n flex-direction: column;\n width: 100%;\n /*\n * Not `height: 100%`: inside a parent that only has a min-height (e.g. a\n * `min-h-screen` flex row) a percentage height can't resolve, and a set\n * height also stops the flex/grid parent from stretching the sidebar, so it\n * would collapse to its content. Min and max of 100% pin it to a parent with\n * a real height (nav scrolls inside), and leave it `auto` everywhere else, so\n * the parent stretches it to full height and the footer stays at the bottom.\n */\n height: auto;\n min-height: 100%;\n max-height: 100%;\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-sidebar__header {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n padding: var(--juxt-space-3) var(--juxt-space-3) var(--juxt-space-2);\n}\n\n.j-sidebar__nav {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--juxt-space-4);\n min-height: 0;\n padding: var(--juxt-space-2);\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n.j-sidebar__footer {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n padding: var(--juxt-space-2);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-sidebar__footer,\n[data-theme='dark'] .j-sidebar__footer {\n border-top-color: var(--juxt-border);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useId } from 'vue'\nimport { IconChevronDown } from '../../icons'\n\nexport interface SidebarGroupProps {\n title?: string\n /** Let the title fold the group away. */\n collapsible?: boolean\n}\n\nwithDefaults(defineProps<SidebarGroupProps>(), { collapsible: true })\n\n/** Whether the group is expanded. Open by default. */\nconst open = defineModel<boolean>('open', { default: true })\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst id = `j-${useId()}`\nconst titleId = `${id}-title`\nconst listId = `${id}-list`\n</script>\n\n<template>\n <div class=\"j-sidebar-group\" :data-state=\"open ? 'open' : 'closed'\">\n <button\n v-if=\"title && collapsible\"\n :id=\"titleId\"\n type=\"button\"\n class=\"j-sidebar-group__title j-sidebar-group__toggle j-focusable\"\n :aria-expanded=\"open\"\n :aria-controls=\"listId\"\n @click=\"open = !open\"\n >\n <span>{{ title }}</span>\n <IconChevronDown class=\"j-sidebar-group__chevron\" />\n </button>\n <p v-else-if=\"title\" :id=\"titleId\" class=\"j-sidebar-group__title\">\n {{ title }}\n </p>\n <div\n :id=\"listId\"\n class=\"j-sidebar-group__panel\"\n role=\"group\"\n :aria-labelledby=\"title ? titleId : undefined\"\n :inert=\"!open\"\n >\n <div class=\"j-sidebar-group__panel-inner\">\n <div class=\"j-sidebar-group__items\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar-group {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n.j-sidebar-group__title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--juxt-space-2);\n margin: 0;\n padding: var(--juxt-space-1) var(--juxt-space-2);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n line-height: var(--juxt-leading-snug);\n}\n\n.j-sidebar-group__toggle {\n width: 100%;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n font-family: inherit;\n text-align: left;\n cursor: pointer;\n transition-property: color, outline-color, outline-offset;\n}\n\n.j-sidebar-group__toggle:hover {\n color: var(--juxt-fg-secondary);\n}\n\n.j-sidebar-group__chevron {\n flex: none;\n width: 0.75rem;\n height: 0.75rem;\n opacity: 0;\n transform: rotate(-90deg);\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n/* The chevron only appears on hover or focus, or while the group is folded. */\n.j-sidebar-group__toggle:hover .j-sidebar-group__chevron,\n.j-sidebar-group__toggle:focus-visible .j-sidebar-group__chevron,\n.j-sidebar-group[data-state='closed'] .j-sidebar-group__chevron {\n opacity: 1;\n}\n\n.j-sidebar-group[data-state='open'] .j-sidebar-group__chevron {\n transform: none;\n}\n\n.j-sidebar-group__panel {\n display: grid;\n grid-template-rows: 1fr;\n transition: grid-template-rows var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-sidebar-group[data-state='closed'] .j-sidebar-group__panel {\n grid-template-rows: 0fr;\n}\n\n.j-sidebar-group__panel-inner {\n min-height: 0;\n overflow: hidden;\n}\n\n.j-sidebar-group__items {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-sidebar-group__panel,\n .j-sidebar-group__chevron {\n transition: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useId } from 'vue'\nimport { IconChevronDown } from '../../icons'\n\nexport interface SidebarGroupProps {\n title?: string\n /** Let the title fold the group away. */\n collapsible?: boolean\n}\n\nwithDefaults(defineProps<SidebarGroupProps>(), { collapsible: true })\n\n/** Whether the group is expanded. Open by default. */\nconst open = defineModel<boolean>('open', { default: true })\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst id = `j-${useId()}`\nconst titleId = `${id}-title`\nconst listId = `${id}-list`\n</script>\n\n<template>\n <div class=\"j-sidebar-group\" :data-state=\"open ? 'open' : 'closed'\">\n <button\n v-if=\"title && collapsible\"\n :id=\"titleId\"\n type=\"button\"\n class=\"j-sidebar-group__title j-sidebar-group__toggle j-focusable\"\n :aria-expanded=\"open\"\n :aria-controls=\"listId\"\n @click=\"open = !open\"\n >\n <span>{{ title }}</span>\n <IconChevronDown class=\"j-sidebar-group__chevron\" />\n </button>\n <p v-else-if=\"title\" :id=\"titleId\" class=\"j-sidebar-group__title\">\n {{ title }}\n </p>\n <div\n :id=\"listId\"\n class=\"j-sidebar-group__panel\"\n role=\"group\"\n :aria-labelledby=\"title ? titleId : undefined\"\n :inert=\"!open\"\n >\n <div class=\"j-sidebar-group__panel-inner\">\n <div class=\"j-sidebar-group__items\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar-group {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n.j-sidebar-group__title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--juxt-space-2);\n margin: 0;\n padding: var(--juxt-space-1) var(--juxt-space-2);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n line-height: var(--juxt-leading-snug);\n}\n\n.j-sidebar-group__toggle {\n width: 100%;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n font-family: inherit;\n text-align: left;\n cursor: pointer;\n transition-property: color, outline-color, outline-offset;\n}\n\n.j-sidebar-group__toggle:hover {\n color: var(--juxt-fg-secondary);\n}\n\n.j-sidebar-group__chevron {\n flex: none;\n width: 0.75rem;\n height: 0.75rem;\n opacity: 0;\n transform: rotate(-90deg);\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n/* The chevron only appears on hover or focus, or while the group is folded. */\n.j-sidebar-group__toggle:hover .j-sidebar-group__chevron,\n.j-sidebar-group__toggle:focus-visible .j-sidebar-group__chevron,\n.j-sidebar-group[data-state='closed'] .j-sidebar-group__chevron {\n opacity: 1;\n}\n\n.j-sidebar-group[data-state='open'] .j-sidebar-group__chevron {\n transform: none;\n}\n\n.j-sidebar-group__panel {\n display: grid;\n grid-template-rows: 1fr;\n transition: grid-template-rows var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-sidebar-group[data-state='closed'] .j-sidebar-group__panel {\n grid-template-rows: 0fr;\n}\n\n.j-sidebar-group__panel-inner {\n min-height: 0;\n overflow: hidden;\n}\n\n.j-sidebar-group__items {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-sidebar-group__panel,\n .j-sidebar-group__chevron {\n transition: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface SidebarItemProps {\n /** Marks the current page: styled and announced with `aria-current=\"page\"`. */\n active?: boolean\n disabled?: boolean\n /** Render as another element or component, e.g. `NuxtLink`. */\n as?: string | Component\n}\n\nwithDefaults(defineProps<SidebarItemProps>(), { as: 'a' })\n\ndefineSlots<{\n default?: () => unknown\n /** Icon before the label. */\n leading?: () => unknown\n /** After the label: a count, badge or shortcut. */\n trailing?: () => unknown\n}>()\n</script>\n\n<template>\n <component\n :is=\"as\"\n class=\"j-sidebar-item j-focusable\"\n :class=\"{ 'is-active': active, 'is-disabled': disabled }\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n >\n <span v-if=\"$slots.leading\" class=\"j-sidebar-item__icon\"><slot name=\"leading\" /></span>\n <span class=\"j-sidebar-item__label\"><slot /></span>\n <span v-if=\"$slots.trailing\" class=\"j-sidebar-item__trailing\"><slot name=\"trailing\" /></span>\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar-item {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n min-height: var(--juxt-control-sm);\n padding: 0 var(--juxt-space-2);\n border-radius: var(--juxt-radius-sm);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n letter-spacing: var(--juxt-tracking-tight);\n text-decoration: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: color, background-color, outline-color, outline-offset;\n}\n\n.j-sidebar-item:hover:not(.is-disabled) {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-sidebar-item.is-active {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n font-weight: var(--juxt-weight-medium);\n}\n\n/* Same marker as the command palette: a short accent line where you are. */\n.j-sidebar-item.is-active::before {\n content: '';\n position: absolute;\n top: 0.4375rem;\n bottom: 0.4375rem;\n left: 0;\n width: 2px;\n border-radius: 0 2px 2px 0;\n background: var(--juxt-accent);\n}\n\n.j-sidebar-item.is-disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-sidebar-item__icon {\n display: inline-flex;\n flex: none;\n color: var(--juxt-fg-muted);\n}\n\n.j-sidebar-item.is-active .j-sidebar-item__icon,\n.j-sidebar-item:hover:not(.is-disabled) .j-sidebar-item__icon {\n color: var(--juxt-fg);\n}\n\n.j-sidebar-item__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-sidebar-item__label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.j-sidebar-item__trailing {\n display: inline-flex;\n flex: none;\n align-items: center;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-variant-numeric: tabular-nums;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface SidebarItemProps {\n /** Marks the current page: styled and announced with `aria-current=\"page\"`. */\n active?: boolean\n disabled?: boolean\n /** Render as another element or component, e.g. `NuxtLink`. */\n as?: string | Component\n}\n\nwithDefaults(defineProps<SidebarItemProps>(), { as: 'a' })\n\ndefineSlots<{\n default?: () => unknown\n /** Icon before the label. */\n leading?: () => unknown\n /** After the label: a count, badge or shortcut. */\n trailing?: () => unknown\n}>()\n</script>\n\n<template>\n <component\n :is=\"as\"\n class=\"j-sidebar-item j-focusable\"\n :class=\"{ 'is-active': active, 'is-disabled': disabled }\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n >\n <span v-if=\"$slots.leading\" class=\"j-sidebar-item__icon\"><slot name=\"leading\" /></span>\n <span class=\"j-sidebar-item__label\"><slot /></span>\n <span v-if=\"$slots.trailing\" class=\"j-sidebar-item__trailing\"><slot name=\"trailing\" /></span>\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar-item {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n min-height: var(--juxt-control-sm);\n padding: 0 var(--juxt-space-2);\n border-radius: var(--juxt-radius-sm);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n letter-spacing: var(--juxt-tracking-tight);\n text-decoration: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: color, background-color, outline-color, outline-offset;\n}\n\n.j-sidebar-item:hover:not(.is-disabled) {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-sidebar-item.is-active {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n font-weight: var(--juxt-weight-medium);\n}\n\n/* Same marker as the command palette: a short accent line where you are. */\n.j-sidebar-item.is-active::before {\n content: '';\n position: absolute;\n top: 0.4375rem;\n bottom: 0.4375rem;\n left: 0;\n width: 2px;\n border-radius: 0 2px 2px 0;\n background: var(--juxt-accent);\n}\n\n.j-sidebar-item.is-disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-sidebar-item__icon {\n display: inline-flex;\n flex: none;\n color: var(--juxt-fg-muted);\n}\n\n.j-sidebar-item.is-active .j-sidebar-item__icon,\n.j-sidebar-item:hover:not(.is-disabled) .j-sidebar-item__icon {\n color: var(--juxt-fg);\n}\n\n.j-sidebar-item__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-sidebar-item__label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.j-sidebar-item__trailing {\n display: inline-flex;\n flex: none;\n align-items: center;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-variant-numeric: tabular-nums;\n}\n}\n</style>\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\n\r\nexport interface SkeletonProps {\r\n /** `text` renders one or more lines; `circle` suits avatars. */\r\n shape?: 'rect' | 'text' | 'circle'\r\n /** Number of lines for `text`. The last one is shorter, like a real paragraph. */\r\n lines?: number\r\n /** Any CSS length, e.g. `12rem` or `100%`. */\r\n width?: string\r\n height?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<SkeletonProps>(), { shape: 'rect', lines: 1 })\r\n\r\nconst style = computed(() => ({ width: props.width, height: props.height }))\r\n</script>\r\n\r\n<template>\r\n <span v-if=\"shape === 'text'\" class=\"j-skeleton-lines\" :style=\"{ width }\" aria-hidden=\"true\">\r\n <span\r\n v-for=\"i in lines\"\r\n :key=\"i\"\r\n class=\"j-skeleton j-skeleton--text\"\r\n :style=\"{ width: i === lines && lines > 1 ? '62%' : undefined, height }\"\r\n />\r\n </span>\r\n <span v-else class=\"j-skeleton\" :class=\"`j-skeleton--${shape}`\" :style=\"style\" aria-hidden=\"true\" />\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-skeleton {\r\n display: block;\r\n width: 100%;\r\n height: 1rem;\r\n border-radius: var(--juxt-radius-sm);\r\n background: var(--juxt-surface-active);\r\n animation: j-skeleton-pulse 1.8s var(--juxt-ease-standard) infinite;\r\n}\r\n\r\n.j-skeleton--circle {\r\n width: 2rem;\r\n height: 2rem;\r\n border-radius: var(--juxt-radius-full);\r\n}\r\n\r\n.j-skeleton--text {\r\n height: 0.75rem;\r\n border-radius: var(--juxt-radius-xs);\r\n}\r\n\r\n.j-skeleton-lines {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-2);\r\n width: 100%;\r\n}\r\n\r\n/* A slow breath, not a shimmer | loading shouldn't compete for attention. */\r\n@keyframes j-skeleton-pulse {\r\n 50% {\r\n opacity: 0.5;\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-skeleton {\r\n animation: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\n\r\nexport interface SkeletonProps {\r\n /** `text` renders one or more lines; `circle` suits avatars. */\r\n shape?: 'rect' | 'text' | 'circle'\r\n /** Number of lines for `text`. The last one is shorter, like a real paragraph. */\r\n lines?: number\r\n /** Any CSS length, e.g. `12rem` or `100%`. */\r\n width?: string\r\n height?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<SkeletonProps>(), { shape: 'rect', lines: 1 })\r\n\r\nconst style = computed(() => ({ width: props.width, height: props.height }))\r\n</script>\r\n\r\n<template>\r\n <span v-if=\"shape === 'text'\" class=\"j-skeleton-lines\" :style=\"{ width }\" aria-hidden=\"true\">\r\n <span\r\n v-for=\"i in lines\"\r\n :key=\"i\"\r\n class=\"j-skeleton j-skeleton--text\"\r\n :style=\"{ width: i === lines && lines > 1 ? '62%' : undefined, height }\"\r\n />\r\n </span>\r\n <span v-else class=\"j-skeleton\" :class=\"`j-skeleton--${shape}`\" :style=\"style\" aria-hidden=\"true\" />\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-skeleton {\r\n display: block;\r\n width: 100%;\r\n height: 1rem;\r\n border-radius: var(--juxt-radius-sm);\r\n background: var(--juxt-surface-active);\r\n animation: j-skeleton-pulse 1.8s var(--juxt-ease-standard) infinite;\r\n}\r\n\r\n.j-skeleton--circle {\r\n width: 2rem;\r\n height: 2rem;\r\n border-radius: var(--juxt-radius-full);\r\n}\r\n\r\n.j-skeleton--text {\r\n height: 0.75rem;\r\n border-radius: var(--juxt-radius-xs);\r\n}\r\n\r\n.j-skeleton-lines {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-2);\r\n width: 100%;\r\n}\r\n\r\n/* A slow breath, not a shimmer | loading shouldn't compete for attention. */\r\n@keyframes j-skeleton-pulse {\r\n 50% {\r\n opacity: 0.5;\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-skeleton {\r\n animation: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, ref } from 'vue'\r\n\r\nexport interface SliderProps {\r\n min?: number\r\n max?: number\r\n step?: number\r\n disabled?: boolean\r\n /** Accessible name for the thumb(s) when no visible label is associated. */\r\n label?: string\r\n size?: 'sm' | 'md'\r\n /** Show the current value in a floating bubble above the active thumb. */\r\n showValue?: boolean\r\n /** Format the displayed and announced value. */\r\n format?: (value: number) => string\r\n}\r\n\r\nconst props = withDefaults(defineProps<SliderProps>(), {\r\n min: 0,\r\n max: 100,\r\n step: 1,\r\n size: 'md',\r\n})\r\n\r\n/** A single value, or a `[low, high]` tuple for a range slider. */\r\nconst model = defineModel<number | [number, number]>({ default: 0 })\r\n\r\nconst track = ref<HTMLElement | null>(null)\r\nconst activeThumb = ref<number | null>(null)\r\n\r\nconst isRange = computed(() => Array.isArray(model.value))\r\nconst values = computed<number[]>(() => (Array.isArray(model.value) ? model.value : [model.value as number]))\r\n\r\nfunction clamp(value: number) {\r\n const stepped = Math.round((value - props.min) / props.step) * props.step + props.min\r\n const rounded = Number(stepped.toFixed(6))\r\n return Math.min(props.max, Math.max(props.min, rounded))\r\n}\r\n\r\nfunction setValue(index: number, raw: number) {\r\n if (props.disabled) return\r\n const next = [...values.value]\r\n let value = clamp(raw)\r\n if (isRange.value) {\r\n const lower = index === 0 ? props.min : next[index - 1]!\r\n const upper = index === next.length - 1 ? props.max : next[index + 1]!\r\n value = Math.min(upper, Math.max(lower, value))\r\n }\r\n next[index] = value\r\n model.value = isRange.value ? ([next[0]!, next[1]!] as [number, number]) : next[0]!\r\n}\r\n\r\nfunction percent(value: number) {\r\n if (props.max === props.min) return 0\r\n return ((value - props.min) / (props.max - props.min)) * 100\r\n}\r\n\r\nfunction valueFromClientX(clientX: number): number {\r\n const rect = track.value!.getBoundingClientRect()\r\n const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width))\r\n return props.min + ratio * (props.max - props.min)\r\n}\r\n\r\nfunction nearestThumb(value: number) {\r\n let nearest = 0\r\n let best = Infinity\r\n values.value.forEach((v, i) => {\r\n const dist = Math.abs(v - value)\r\n if (dist < best) {\r\n best = dist\r\n nearest = i\r\n }\r\n })\r\n return nearest\r\n}\r\n\r\nfunction onPointerMove(event: PointerEvent) {\r\n if (activeThumb.value === null) return\r\n setValue(activeThumb.value, valueFromClientX(event.clientX))\r\n}\r\n\r\nfunction onPointerUp() {\r\n activeThumb.value = null\r\n window.removeEventListener('pointermove', onPointerMove)\r\n window.removeEventListener('pointerup', onPointerUp)\r\n}\r\n\r\nfunction startDrag(index: number) {\r\n activeThumb.value = index\r\n window.addEventListener('pointermove', onPointerMove)\r\n window.addEventListener('pointerup', onPointerUp)\r\n}\r\n\r\nfunction onTrackPointerDown(event: PointerEvent) {\r\n if (props.disabled) return\r\n const value = valueFromClientX(event.clientX)\r\n const index = nearestThumb(value)\r\n setValue(index, value)\r\n startDrag(index)\r\n}\r\n\r\nfunction onThumbPointerDown(index: number, event: PointerEvent) {\r\n if (props.disabled) return\r\n event.stopPropagation()\r\n ;(event.currentTarget as HTMLElement).focus()\r\n startDrag(index)\r\n}\r\n\r\nfunction onKeydown(index: number, event: KeyboardEvent) {\r\n if (props.disabled) return\r\n const big = props.step * 10 || (props.max - props.min) / 10\r\n const current = values.value[index]!\r\n let next: number | undefined\r\n if (event.key === 'ArrowRight' || event.key === 'ArrowUp') next = current + props.step\r\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') next = current - props.step\r\n else if (event.key === 'PageUp') next = current + big\r\n else if (event.key === 'PageDown') next = current - big\r\n else if (event.key === 'Home') next = props.min\r\n else if (event.key === 'End') next = props.max\r\n if (next === undefined) return\r\n event.preventDefault()\r\n setValue(index, next)\r\n}\r\n\r\nfunction display(value: number) {\r\n return props.format ? props.format(value) : String(Math.round(value * 100) / 100)\r\n}\r\n\r\nconst fillStyle = computed(() => {\r\n if (isRange.value) {\r\n const [a, b] = values.value\r\n return { left: `${percent(a!)}%`, width: `${Math.max(0, percent(b!) - percent(a!))}%` }\r\n }\r\n return { left: '0%', width: `${percent(values.value[0]!)}%` }\r\n})\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-slider\" :class=\"[`j-slider--${size}`, { 'is-disabled': disabled }]\">\r\n <div ref=\"track\" class=\"j-slider__track\" @pointerdown=\"onTrackPointerDown\">\r\n <span class=\"j-slider__range\" :style=\"fillStyle\" />\r\n <span\r\n v-for=\"(value, index) in values\"\r\n :key=\"index\"\r\n class=\"j-slider__thumb j-focusable\"\r\n :class=\"{ 'is-dragging': activeThumb === index }\"\r\n role=\"slider\"\r\n :tabindex=\"disabled ? -1 : 0\"\r\n :aria-label=\"label\"\r\n :aria-orientation=\"'horizontal'\"\r\n :aria-valuemin=\"min\"\r\n :aria-valuemax=\"max\"\r\n :aria-valuenow=\"value\"\r\n :aria-valuetext=\"display(value)\"\r\n :aria-disabled=\"disabled || undefined\"\r\n :style=\"{ left: `${percent(value)}%` }\"\r\n @pointerdown=\"onThumbPointerDown(index, $event)\"\r\n @keydown=\"onKeydown(index, $event)\"\r\n >\r\n <span v-if=\"showValue\" class=\"j-slider__bubble\">{{ display(value) }}</span>\r\n </span>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-slider {\r\n --j-slider-track: 0.25rem;\r\n --j-slider-thumb: 1rem;\r\n\r\n display: flex;\r\n align-items: center;\r\n width: 100%;\r\n padding: calc(var(--j-slider-thumb) / 2) 0;\r\n font-family: var(--juxt-font-sans);\r\n touch-action: none;\r\n}\r\n\r\n.j-slider--sm {\r\n --j-slider-track: 0.1875rem;\r\n --j-slider-thumb: 0.875rem;\r\n}\r\n\r\n.j-slider__track {\r\n position: relative;\r\n width: 100%;\r\n height: var(--j-slider-track);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-surface-active);\r\n cursor: pointer;\r\n}\r\n\r\n.j-slider.is-disabled .j-slider__track {\r\n cursor: not-allowed;\r\n opacity: 0.5;\r\n}\r\n\r\n.j-slider__range {\r\n position: absolute;\r\n top: 0;\r\n bottom: 0;\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-accent);\r\n}\r\n\r\n.j-slider__thumb {\r\n position: absolute;\r\n top: 50%;\r\n width: var(--j-slider-thumb);\r\n height: var(--j-slider-thumb);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-surface);\r\n box-shadow: 0 0 0 1px var(--juxt-border-strong), var(--juxt-shadow-sm);\r\n cursor: grab;\r\n transform: translate(-50%, -50%);\r\n transition:\r\n box-shadow var(--juxt-duration-fast) var(--juxt-ease-out),\r\n left var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-slider__thumb.is-dragging {\r\n cursor: grabbing;\r\n transition: none;\r\n}\r\n\r\n.j-slider__thumb:hover {\r\n box-shadow: 0 0 0 1px var(--juxt-accent), var(--juxt-shadow-sm);\r\n}\r\n\r\n.j-slider__thumb:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 3px;\r\n}\r\n\r\n.j-slider.is-disabled .j-slider__thumb {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-slider__bubble {\r\n position: absolute;\r\n bottom: calc(100% + var(--juxt-space-2));\r\n left: 50%;\r\n padding: var(--juxt-space-0-5) var(--juxt-space-1-5);\r\n border-radius: var(--juxt-radius-xs);\r\n background: var(--juxt-tooltip-bg);\r\n color: var(--juxt-tooltip-fg);\r\n font-size: var(--juxt-text-2xs);\r\n font-weight: var(--juxt-weight-medium);\r\n font-variant-numeric: tabular-nums;\r\n white-space: nowrap;\r\n pointer-events: none;\r\n transform: translateX(-50%);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-slider__thumb {\r\n transition: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, ref } from 'vue'\r\n\r\nexport interface SliderProps {\r\n min?: number\r\n max?: number\r\n step?: number\r\n disabled?: boolean\r\n /** Accessible name for the thumb(s) when no visible label is associated. */\r\n label?: string\r\n size?: 'sm' | 'md'\r\n /** Show the current value in a floating bubble above the active thumb. */\r\n showValue?: boolean\r\n /** Format the displayed and announced value. */\r\n format?: (value: number) => string\r\n}\r\n\r\nconst props = withDefaults(defineProps<SliderProps>(), {\r\n min: 0,\r\n max: 100,\r\n step: 1,\r\n size: 'md',\r\n})\r\n\r\n/** A single value, or a `[low, high]` tuple for a range slider. */\r\nconst model = defineModel<number | [number, number]>({ default: 0 })\r\n\r\nconst track = ref<HTMLElement | null>(null)\r\nconst activeThumb = ref<number | null>(null)\r\n\r\nconst isRange = computed(() => Array.isArray(model.value))\r\nconst values = computed<number[]>(() => (Array.isArray(model.value) ? model.value : [model.value as number]))\r\n\r\nfunction clamp(value: number) {\r\n const stepped = Math.round((value - props.min) / props.step) * props.step + props.min\r\n const rounded = Number(stepped.toFixed(6))\r\n return Math.min(props.max, Math.max(props.min, rounded))\r\n}\r\n\r\nfunction setValue(index: number, raw: number) {\r\n if (props.disabled) return\r\n const next = [...values.value]\r\n let value = clamp(raw)\r\n if (isRange.value) {\r\n const lower = index === 0 ? props.min : next[index - 1]!\r\n const upper = index === next.length - 1 ? props.max : next[index + 1]!\r\n value = Math.min(upper, Math.max(lower, value))\r\n }\r\n next[index] = value\r\n model.value = isRange.value ? ([next[0]!, next[1]!] as [number, number]) : next[0]!\r\n}\r\n\r\nfunction percent(value: number) {\r\n if (props.max === props.min) return 0\r\n return ((value - props.min) / (props.max - props.min)) * 100\r\n}\r\n\r\nfunction valueFromClientX(clientX: number): number {\r\n const rect = track.value!.getBoundingClientRect()\r\n const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width))\r\n return props.min + ratio * (props.max - props.min)\r\n}\r\n\r\nfunction nearestThumb(value: number) {\r\n let nearest = 0\r\n let best = Infinity\r\n values.value.forEach((v, i) => {\r\n const dist = Math.abs(v - value)\r\n if (dist < best) {\r\n best = dist\r\n nearest = i\r\n }\r\n })\r\n return nearest\r\n}\r\n\r\nfunction onPointerMove(event: PointerEvent) {\r\n if (activeThumb.value === null) return\r\n setValue(activeThumb.value, valueFromClientX(event.clientX))\r\n}\r\n\r\nfunction onPointerUp() {\r\n activeThumb.value = null\r\n window.removeEventListener('pointermove', onPointerMove)\r\n window.removeEventListener('pointerup', onPointerUp)\r\n}\r\n\r\nfunction startDrag(index: number) {\r\n activeThumb.value = index\r\n window.addEventListener('pointermove', onPointerMove)\r\n window.addEventListener('pointerup', onPointerUp)\r\n}\r\n\r\nfunction onTrackPointerDown(event: PointerEvent) {\r\n if (props.disabled) return\r\n const value = valueFromClientX(event.clientX)\r\n const index = nearestThumb(value)\r\n setValue(index, value)\r\n startDrag(index)\r\n}\r\n\r\nfunction onThumbPointerDown(index: number, event: PointerEvent) {\r\n if (props.disabled) return\r\n event.stopPropagation()\r\n ;(event.currentTarget as HTMLElement).focus()\r\n startDrag(index)\r\n}\r\n\r\nfunction onKeydown(index: number, event: KeyboardEvent) {\r\n if (props.disabled) return\r\n const big = props.step * 10 || (props.max - props.min) / 10\r\n const current = values.value[index]!\r\n let next: number | undefined\r\n if (event.key === 'ArrowRight' || event.key === 'ArrowUp') next = current + props.step\r\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') next = current - props.step\r\n else if (event.key === 'PageUp') next = current + big\r\n else if (event.key === 'PageDown') next = current - big\r\n else if (event.key === 'Home') next = props.min\r\n else if (event.key === 'End') next = props.max\r\n if (next === undefined) return\r\n event.preventDefault()\r\n setValue(index, next)\r\n}\r\n\r\nfunction display(value: number) {\r\n return props.format ? props.format(value) : String(Math.round(value * 100) / 100)\r\n}\r\n\r\nconst fillStyle = computed(() => {\r\n if (isRange.value) {\r\n const [a, b] = values.value\r\n return { left: `${percent(a!)}%`, width: `${Math.max(0, percent(b!) - percent(a!))}%` }\r\n }\r\n return { left: '0%', width: `${percent(values.value[0]!)}%` }\r\n})\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-slider\" :class=\"[`j-slider--${size}`, { 'is-disabled': disabled }]\">\r\n <div ref=\"track\" class=\"j-slider__track\" @pointerdown=\"onTrackPointerDown\">\r\n <span class=\"j-slider__range\" :style=\"fillStyle\" />\r\n <span\r\n v-for=\"(value, index) in values\"\r\n :key=\"index\"\r\n class=\"j-slider__thumb j-focusable\"\r\n :class=\"{ 'is-dragging': activeThumb === index }\"\r\n role=\"slider\"\r\n :tabindex=\"disabled ? -1 : 0\"\r\n :aria-label=\"label\"\r\n :aria-orientation=\"'horizontal'\"\r\n :aria-valuemin=\"min\"\r\n :aria-valuemax=\"max\"\r\n :aria-valuenow=\"value\"\r\n :aria-valuetext=\"display(value)\"\r\n :aria-disabled=\"disabled || undefined\"\r\n :style=\"{ left: `${percent(value)}%` }\"\r\n @pointerdown=\"onThumbPointerDown(index, $event)\"\r\n @keydown=\"onKeydown(index, $event)\"\r\n >\r\n <span v-if=\"showValue\" class=\"j-slider__bubble\">{{ display(value) }}</span>\r\n </span>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-slider {\r\n --j-slider-track: 0.25rem;\r\n --j-slider-thumb: 1rem;\r\n\r\n display: flex;\r\n align-items: center;\r\n width: 100%;\r\n padding: calc(var(--j-slider-thumb) / 2) 0;\r\n font-family: var(--juxt-font-sans);\r\n touch-action: none;\r\n}\r\n\r\n.j-slider--sm {\r\n --j-slider-track: 0.1875rem;\r\n --j-slider-thumb: 0.875rem;\r\n}\r\n\r\n.j-slider__track {\r\n position: relative;\r\n width: 100%;\r\n height: var(--j-slider-track);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-surface-active);\r\n cursor: pointer;\r\n}\r\n\r\n.j-slider.is-disabled .j-slider__track {\r\n cursor: not-allowed;\r\n opacity: 0.5;\r\n}\r\n\r\n.j-slider__range {\r\n position: absolute;\r\n top: 0;\r\n bottom: 0;\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-accent);\r\n}\r\n\r\n.j-slider__thumb {\r\n position: absolute;\r\n top: 50%;\r\n width: var(--j-slider-thumb);\r\n height: var(--j-slider-thumb);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-surface);\r\n box-shadow: 0 0 0 1px var(--juxt-border-strong), var(--juxt-shadow-sm);\r\n cursor: grab;\r\n transform: translate(-50%, -50%);\r\n transition:\r\n box-shadow var(--juxt-duration-fast) var(--juxt-ease-out),\r\n left var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-slider__thumb.is-dragging {\r\n cursor: grabbing;\r\n transition: none;\r\n}\r\n\r\n.j-slider__thumb:hover {\r\n box-shadow: 0 0 0 1px var(--juxt-accent), var(--juxt-shadow-sm);\r\n}\r\n\r\n.j-slider__thumb:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 3px;\r\n}\r\n\r\n.j-slider.is-disabled .j-slider__thumb {\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-slider__bubble {\r\n position: absolute;\r\n bottom: calc(100% + var(--juxt-space-2));\r\n left: 50%;\r\n padding: var(--juxt-space-0-5) var(--juxt-space-1-5);\r\n border-radius: var(--juxt-radius-xs);\r\n background: var(--juxt-tooltip-bg);\r\n color: var(--juxt-tooltip-fg);\r\n font-size: var(--juxt-text-2xs);\r\n font-weight: var(--juxt-weight-medium);\r\n font-variant-numeric: tabular-nums;\r\n white-space: nowrap;\r\n pointer-events: none;\r\n transform: translateX(-50%);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-slider__thumb {\r\n transition: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { Spinner } from '../../icons'\r\n\r\nexport interface SpinnerProps {\r\n size?: 'sm' | 'md' | 'lg'\r\n /** Announced to screen readers. Set to an empty string when the context already says it. */\r\n label?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<SpinnerProps>(), { size: 'md', label: 'Loading' })\r\n\r\nconst pixels = computed(() => ({ sm: 14, md: 16, lg: 20 })[props.size])\r\n</script>\r\n\r\n<template>\r\n <span class=\"j-spinner-wrap\" :role=\"label ? 'status' : undefined\">\r\n <Spinner :size=\"pixels\" />\r\n <span v-if=\"label\" class=\"j-sr-only\">{{ label }}</span>\r\n </span>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-spinner-wrap {\r\n display: inline-flex;\r\n color: var(--juxt-fg-muted);\r\n vertical-align: middle;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { Spinner } from '../../icons'\r\n\r\nexport interface SpinnerProps {\r\n size?: 'sm' | 'md' | 'lg'\r\n /** Announced to screen readers. Set to an empty string when the context already says it. */\r\n label?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<SpinnerProps>(), { size: 'md', label: 'Loading' })\r\n\r\nconst pixels = computed(() => ({ sm: 14, md: 16, lg: 20 })[props.size])\r\n</script>\r\n\r\n<template>\r\n <span class=\"j-spinner-wrap\" :role=\"label ? 'status' : undefined\">\r\n <Spinner :size=\"pixels\" />\r\n <span v-if=\"label\" class=\"j-sr-only\">{{ label }}</span>\r\n </span>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-spinner-wrap {\r\n display: inline-flex;\r\n color: var(--juxt-fg-muted);\r\n vertical-align: middle;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { IconArrowDownRight, IconArrowUpRight } from '../../icons'\r\n\r\nexport type StatTrend = 'up' | 'down' | 'neutral'\r\n\r\nexport interface StatProps {\r\n label: string\r\n value: string | number\r\n /** Delta shown next to the value, e.g. `\"+12%\"`. */\r\n delta?: string\r\n trend?: StatTrend\r\n description?: string\r\n}\r\n\r\nconst props = defineProps<StatProps>()\r\n\r\ndefineSlots<{\r\n /** Icon shown next to the label. */\r\n icon?: () => unknown\r\n}>()\r\n\r\nconst trendIcon = computed(() => (props.trend === 'up' ? IconArrowUpRight : props.trend === 'down' ? IconArrowDownRight : null))\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-stat\">\r\n <div class=\"j-stat__header\">\r\n <span v-if=\"$slots.icon\" class=\"j-stat__icon\" aria-hidden=\"true\"><slot name=\"icon\" /></span>\r\n <span class=\"j-stat__label\">{{ label }}</span>\r\n </div>\r\n <div class=\"j-stat__row\">\r\n <span class=\"j-stat__value\">{{ value }}</span>\r\n <span v-if=\"delta\" class=\"j-stat__delta\" :class=\"trend ? `j-stat__delta--${trend}` : undefined\">\r\n <component :is=\"trendIcon\" v-if=\"trendIcon\" aria-hidden=\"true\" />\r\n {{ delta }}\r\n </span>\r\n </div>\r\n <p v-if=\"description\" class=\"j-stat__description\">\r\n {{ description }}\r\n </p>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-stat {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-1-5);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-stat__header {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1-5);\r\n}\r\n\r\n.j-stat__icon {\r\n display: grid;\r\n place-items: center;\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-stat__icon > svg {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n\r\n.j-stat__label {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-stat__row {\r\n display: flex;\r\n align-items: baseline;\r\n gap: var(--juxt-space-2);\r\n}\r\n\r\n.j-stat__value {\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-3xl);\r\n font-weight: var(--juxt-weight-semibold);\r\n line-height: var(--juxt-leading-tight);\r\n letter-spacing: var(--juxt-tracking-tighter);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-stat__delta {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.125rem;\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n font-weight: var(--juxt-weight-medium);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-stat__delta > svg {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n\r\n.j-stat__delta--up {\r\n color: var(--juxt-success-text);\r\n}\r\n\r\n.j-stat__delta--down {\r\n color: var(--juxt-danger-text);\r\n}\r\n\r\n.j-stat__description {\r\n margin: 0;\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { IconArrowDownRight, IconArrowUpRight } from '../../icons'\r\n\r\nexport type StatTrend = 'up' | 'down' | 'neutral'\r\n\r\nexport interface StatProps {\r\n label: string\r\n value: string | number\r\n /** Delta shown next to the value, e.g. `\"+12%\"`. */\r\n delta?: string\r\n trend?: StatTrend\r\n description?: string\r\n}\r\n\r\nconst props = defineProps<StatProps>()\r\n\r\ndefineSlots<{\r\n /** Icon shown next to the label. */\r\n icon?: () => unknown\r\n}>()\r\n\r\nconst trendIcon = computed(() => (props.trend === 'up' ? IconArrowUpRight : props.trend === 'down' ? IconArrowDownRight : null))\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-stat\">\r\n <div class=\"j-stat__header\">\r\n <span v-if=\"$slots.icon\" class=\"j-stat__icon\" aria-hidden=\"true\"><slot name=\"icon\" /></span>\r\n <span class=\"j-stat__label\">{{ label }}</span>\r\n </div>\r\n <div class=\"j-stat__row\">\r\n <span class=\"j-stat__value\">{{ value }}</span>\r\n <span v-if=\"delta\" class=\"j-stat__delta\" :class=\"trend ? `j-stat__delta--${trend}` : undefined\">\r\n <component :is=\"trendIcon\" v-if=\"trendIcon\" aria-hidden=\"true\" />\r\n {{ delta }}\r\n </span>\r\n </div>\r\n <p v-if=\"description\" class=\"j-stat__description\">\r\n {{ description }}\r\n </p>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-stat {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-1-5);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-stat__header {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--juxt-space-1-5);\r\n}\r\n\r\n.j-stat__icon {\r\n display: grid;\r\n place-items: center;\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-stat__icon > svg {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n\r\n.j-stat__label {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-stat__row {\r\n display: flex;\r\n align-items: baseline;\r\n gap: var(--juxt-space-2);\r\n}\r\n\r\n.j-stat__value {\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-3xl);\r\n font-weight: var(--juxt-weight-semibold);\r\n line-height: var(--juxt-leading-tight);\r\n letter-spacing: var(--juxt-tracking-tighter);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-stat__delta {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.125rem;\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n font-weight: var(--juxt-weight-medium);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-stat__delta > svg {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n\r\n.j-stat__delta--up {\r\n color: var(--juxt-success-text);\r\n}\r\n\r\n.j-stat__delta--down {\r\n color: var(--juxt-danger-text);\r\n}\r\n\r\n.j-stat__description {\r\n margin: 0;\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, ref, useId } from 'vue'\r\n\r\nexport interface SwitchProps {\r\n label?: string\r\n description?: string\r\n disabled?: boolean\r\n size?: 'sm' | 'md'\r\n /** Submits `value` under `name` in a form when on. */\r\n name?: string\r\n value?: string\r\n id?: string\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = withDefaults(defineProps<SwitchProps>(), { size: 'md', value: 'on' })\r\nconst model = defineModel<boolean>({ default: false })\r\n\r\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\r\n\r\nconst generated = useId()\r\nconst id = computed(() => props.id ?? `j-${generated}`)\r\nconst labelId = computed(() => `${id.value}-label`)\r\nconst descriptionId = computed(() => `${id.value}-description`)\r\nconst button = ref<HTMLButtonElement | null>(null)\r\n\r\nfunction toggle() {\r\n if (props.disabled) return\r\n model.value = !model.value\r\n}\r\n\r\ndefineExpose({ focus: () => button.value?.focus() })\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-switch\"\r\n :class=\"[$attrs.class, `j-switch--${size}`, { 'is-disabled': disabled }]\"\r\n :style=\"$attrs.style as any\"\r\n :data-state=\"model ? 'on' : 'off'\"\r\n >\r\n <button\r\n :id=\"id\"\r\n ref=\"button\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n type=\"button\"\r\n role=\"switch\"\r\n class=\"j-switch__track\"\r\n :aria-checked=\"model\"\r\n :aria-labelledby=\"label || $slots.default ? labelId : undefined\"\r\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\r\n :disabled=\"disabled\"\r\n @click=\"toggle\"\r\n >\r\n <span class=\"j-switch__thumb\" />\r\n </button>\r\n <input v-if=\"name && model\" type=\"hidden\" :name=\"name\" :value=\"value\" />\r\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-switch__text\">\r\n <label v-if=\"label || $slots.default\" :id=\"labelId\" :for=\"id\" class=\"j-switch__label\">\r\n <slot>{{ label }}</slot>\r\n </label>\r\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-switch__description\">\r\n <slot name=\"description\">{{ description }}</slot>\r\n </span>\r\n </span>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-switch {\r\n --j-switch-w: 2rem;\r\n --j-switch-h: 1.125rem;\r\n --j-switch-pad: 2px;\r\n --j-switch-thumb: calc(var(--j-switch-h) - var(--j-switch-pad) * 2);\r\n\r\n display: inline-flex;\r\n align-items: flex-start;\r\n gap: var(--juxt-space-2-5);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-switch--sm {\r\n --j-switch-w: 1.625rem;\r\n --j-switch-h: 0.9375rem;\r\n}\r\n\r\n.j-switch__track {\r\n position: relative;\r\n flex: none;\r\n width: var(--j-switch-w);\r\n height: var(--j-switch-h);\r\n margin: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-switch-h)) / 2) 0 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-border-strong);\r\n box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.08);\r\n cursor: pointer;\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n -webkit-tap-highlight-color: transparent;\r\n transition:\r\n background-color var(--juxt-duration-normal) var(--juxt-ease-standard),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-switch__track:hover:not(:disabled) {\r\n background: var(--juxt-fg-disabled);\r\n}\r\n\r\n.j-switch__track:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.j-switch[data-state='on'] .j-switch__track {\r\n background: var(--juxt-accent);\r\n}\r\n\r\n.j-switch[data-state='on'] .j-switch__track:hover:not(:disabled) {\r\n background: var(--juxt-accent-hover);\r\n}\r\n\r\n.j-switch__thumb {\r\n position: absolute;\r\n top: var(--j-switch-pad);\r\n left: var(--j-switch-pad);\r\n width: var(--j-switch-thumb);\r\n height: var(--j-switch-thumb);\r\n border-radius: var(--juxt-radius-full);\r\n background: #fff;\r\n box-shadow: 0 1px 2px rgb(0 0 0 / 0.2), 0 0 0 0.5px rgb(0 0 0 / 0.04);\r\n transition:\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\r\n width var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-switch[data-state='on'] .j-switch__thumb {\r\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2));\r\n}\r\n\r\n/* A whisper of stretch while pressed, so the thumb feels held. */\r\n.j-switch__track:active:not(:disabled) .j-switch__thumb {\r\n width: calc(var(--j-switch-thumb) + 3px);\r\n}\r\n\r\n.j-switch[data-state='on'] .j-switch__track:active:not(:disabled) .j-switch__thumb {\r\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2 - 3px));\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-switch__track:active:not(:disabled) .j-switch__thumb {\r\n width: var(--j-switch-thumb);\r\n }\r\n\r\n .j-switch[data-state='on'] .j-switch__track:active:not(:disabled) .j-switch__thumb {\r\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2));\r\n }\r\n}\r\n\r\n.j-switch__text {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-0-5);\r\n min-width: 0;\r\n}\r\n\r\n.j-switch__label {\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.j-switch__description {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-switch.is-disabled .j-switch__track {\r\n cursor: not-allowed;\r\n opacity: 0.5;\r\n}\r\n\r\n.j-switch.is-disabled .j-switch__label {\r\n color: var(--juxt-fg-muted);\r\n cursor: not-allowed;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, ref, useId } from 'vue'\r\n\r\nexport interface SwitchProps {\r\n label?: string\r\n description?: string\r\n disabled?: boolean\r\n size?: 'sm' | 'md'\r\n /** Submits `value` under `name` in a form when on. */\r\n name?: string\r\n value?: string\r\n id?: string\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = withDefaults(defineProps<SwitchProps>(), { size: 'md', value: 'on' })\r\nconst model = defineModel<boolean>({ default: false })\r\n\r\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\r\n\r\nconst generated = useId()\r\nconst id = computed(() => props.id ?? `j-${generated}`)\r\nconst labelId = computed(() => `${id.value}-label`)\r\nconst descriptionId = computed(() => `${id.value}-description`)\r\nconst button = ref<HTMLButtonElement | null>(null)\r\n\r\nfunction toggle() {\r\n if (props.disabled) return\r\n model.value = !model.value\r\n}\r\n\r\ndefineExpose({ focus: () => button.value?.focus() })\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-switch\"\r\n :class=\"[$attrs.class, `j-switch--${size}`, { 'is-disabled': disabled }]\"\r\n :style=\"$attrs.style as any\"\r\n :data-state=\"model ? 'on' : 'off'\"\r\n >\r\n <button\r\n :id=\"id\"\r\n ref=\"button\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n type=\"button\"\r\n role=\"switch\"\r\n class=\"j-switch__track\"\r\n :aria-checked=\"model\"\r\n :aria-labelledby=\"label || $slots.default ? labelId : undefined\"\r\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\r\n :disabled=\"disabled\"\r\n @click=\"toggle\"\r\n >\r\n <span class=\"j-switch__thumb\" />\r\n </button>\r\n <input v-if=\"name && model\" type=\"hidden\" :name=\"name\" :value=\"value\" />\r\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-switch__text\">\r\n <label v-if=\"label || $slots.default\" :id=\"labelId\" :for=\"id\" class=\"j-switch__label\">\r\n <slot>{{ label }}</slot>\r\n </label>\r\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-switch__description\">\r\n <slot name=\"description\">{{ description }}</slot>\r\n </span>\r\n </span>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-switch {\r\n --j-switch-w: 2rem;\r\n --j-switch-h: 1.125rem;\r\n --j-switch-pad: 2px;\r\n --j-switch-thumb: calc(var(--j-switch-h) - var(--j-switch-pad) * 2);\r\n\r\n display: inline-flex;\r\n align-items: flex-start;\r\n gap: var(--juxt-space-2-5);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-switch--sm {\r\n --j-switch-w: 1.625rem;\r\n --j-switch-h: 0.9375rem;\r\n}\r\n\r\n.j-switch__track {\r\n position: relative;\r\n flex: none;\r\n width: var(--j-switch-w);\r\n height: var(--j-switch-h);\r\n margin: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-switch-h)) / 2) 0 0;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-border-strong);\r\n box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.08);\r\n cursor: pointer;\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n -webkit-tap-highlight-color: transparent;\r\n transition:\r\n background-color var(--juxt-duration-normal) var(--juxt-ease-standard),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-switch__track:hover:not(:disabled) {\r\n background: var(--juxt-fg-disabled);\r\n}\r\n\r\n.j-switch__track:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.j-switch[data-state='on'] .j-switch__track {\r\n background: var(--juxt-accent);\r\n}\r\n\r\n.j-switch[data-state='on'] .j-switch__track:hover:not(:disabled) {\r\n background: var(--juxt-accent-hover);\r\n}\r\n\r\n.j-switch__thumb {\r\n position: absolute;\r\n top: var(--j-switch-pad);\r\n left: var(--j-switch-pad);\r\n width: var(--j-switch-thumb);\r\n height: var(--j-switch-thumb);\r\n border-radius: var(--juxt-radius-full);\r\n background: #fff;\r\n box-shadow: 0 1px 2px rgb(0 0 0 / 0.2), 0 0 0 0.5px rgb(0 0 0 / 0.04);\r\n transition:\r\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\r\n width var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-switch[data-state='on'] .j-switch__thumb {\r\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2));\r\n}\r\n\r\n/* A whisper of stretch while pressed, so the thumb feels held. */\r\n.j-switch__track:active:not(:disabled) .j-switch__thumb {\r\n width: calc(var(--j-switch-thumb) + 3px);\r\n}\r\n\r\n.j-switch[data-state='on'] .j-switch__track:active:not(:disabled) .j-switch__thumb {\r\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2 - 3px));\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-switch__track:active:not(:disabled) .j-switch__thumb {\r\n width: var(--j-switch-thumb);\r\n }\r\n\r\n .j-switch[data-state='on'] .j-switch__track:active:not(:disabled) .j-switch__thumb {\r\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2));\r\n }\r\n}\r\n\r\n.j-switch__text {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-0-5);\r\n min-width: 0;\r\n}\r\n\r\n.j-switch__label {\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n line-height: var(--juxt-leading-snug);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.j-switch__description {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-switch.is-disabled .j-switch__track {\r\n cursor: not-allowed;\r\n opacity: 0.5;\r\n}\r\n\r\n.j-switch.is-disabled .j-switch__label {\r\n color: var(--juxt-fg-muted);\r\n cursor: not-allowed;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'\r\nimport { focusElement } from '../../utils/dom'\r\n\r\nexport interface TabItem {\r\n value: string\r\n label: string\r\n disabled?: boolean\r\n}\r\n\r\nexport interface TabsProps {\r\n items: TabItem[]\r\n /** `line` for page sections, `pill` for compact view switches. */\r\n variant?: 'line' | 'pill'\r\n size?: 'sm' | 'md'\r\n /** Accessible name for the tab list. */\r\n label?: string\r\n /** Stretch tabs to fill the width. */\r\n fill?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<TabsProps>(), { variant: 'line', size: 'md' })\r\n\r\nconst model = defineModel<string>()\r\n\r\ndefineSlots<{\r\n /** Custom tab label. */\r\n tab?: (props: { item: TabItem, selected: boolean }) => unknown\r\n /** Panels are named after each item's `value`. */\r\n [value: string]: ((props: { item: TabItem, selected: boolean }) => unknown) | undefined\r\n}>()\r\n\r\nconst id = `j-${useId()}`\r\nconst tabId = (value: string) => `${id}-tab-${value}`\r\nconst panelId = (value: string) => `${id}-panel-${value}`\r\n\r\nconst selected = computed(() => model.value ?? props.items.find(item => !item.disabled)?.value)\r\n\r\nconst list = ref<HTMLElement | null>(null)\r\nconst indicator = ref({ x: 0, width: 0, ready: false })\r\n// The indicator glides only for deliberate changes, never for layout shifts.\r\nconst animated = ref(false)\r\n\r\nfunction measure() {\r\n const el = list.value?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]')\r\n indicator.value = el ? { x: el.offsetLeft, width: el.offsetWidth, ready: true } : { ...indicator.value, ready: false }\r\n}\r\n\r\nfunction measureWithoutMotion() {\r\n animated.value = false\r\n measure()\r\n requestAnimationFrame(() => requestAnimationFrame(() => (animated.value = true)))\r\n}\r\n\r\nlet observer: ResizeObserver | undefined\r\nonMounted(() => {\r\n measureWithoutMotion()\r\n observer = new ResizeObserver(measureWithoutMotion)\r\n if (list.value) observer.observe(list.value)\r\n // Web fonts change tab widths once they arrive.\r\n document.fonts?.ready.then(measureWithoutMotion)\r\n})\r\nonBeforeUnmount(() => observer?.disconnect())\r\n\r\nwatch(selected, () => nextTick(measure), { flush: 'post' })\r\nwatch(() => props.items, () => nextTick(measure), { deep: true, flush: 'post' })\r\n\r\nfunction select(item: TabItem) {\r\n if (item.disabled) return\r\n model.value = item.value\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n const enabled = props.items.filter(item => !item.disabled)\r\n if (!enabled.length) return\r\n const current = enabled.findIndex(item => item.value === selected.value)\r\n let next: number | undefined\r\n if (event.key === 'ArrowRight') next = (current + 1) % enabled.length\r\n else if (event.key === 'ArrowLeft') next = (current - 1 + enabled.length) % enabled.length\r\n else if (event.key === 'Home') next = 0\r\n else if (event.key === 'End') next = enabled.length - 1\r\n if (next === undefined) return\r\n event.preventDefault()\r\n const item = enabled[next]!\r\n select(item)\r\n nextTick(() => focusElement(document.getElementById(tabId(item.value))))\r\n}\r\n\r\nconst indicatorStyle = computed(() => ({\r\n transform: `translateX(${indicator.value.x}px)`,\r\n width: `${indicator.value.width}px`,\r\n}))\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-tabs\" :class=\"[`j-tabs--${variant}`, `j-tabs--${size}`, { 'j-tabs--fill': fill }]\">\r\n <div\r\n ref=\"list\"\r\n role=\"tablist\"\r\n aria-orientation=\"horizontal\"\r\n :aria-label=\"label\"\r\n class=\"j-tabs__list\"\r\n @keydown=\"onKeydown\"\r\n >\r\n <span\r\n class=\"j-tabs__indicator\"\r\n :class=\"{ 'is-ready': indicator.ready, 'is-animated': animated }\"\r\n :style=\"indicatorStyle\"\r\n aria-hidden=\"true\"\r\n />\r\n <button\r\n v-for=\"item in items\"\r\n :id=\"tabId(item.value)\"\r\n :key=\"item.value\"\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"j-tabs__tab\"\r\n :aria-selected=\"item.value === selected\"\r\n :aria-controls=\"panelId(item.value)\"\r\n :tabindex=\"item.value === selected ? 0 : -1\"\r\n :disabled=\"item.disabled\"\r\n @click=\"select(item)\"\r\n >\r\n <slot name=\"tab\" :item=\"item\" :selected=\"item.value === selected\">\r\n {{ item.label }}\r\n </slot>\r\n </button>\r\n </div>\r\n <template v-for=\"item in items\" :key=\"item.value\">\r\n <div\r\n v-if=\"$slots[item.value]\"\r\n :id=\"panelId(item.value)\"\r\n role=\"tabpanel\"\r\n class=\"j-tabs__panel\"\r\n :aria-labelledby=\"tabId(item.value)\"\r\n :hidden=\"item.value !== selected\"\r\n tabindex=\"0\"\r\n >\r\n <slot :name=\"item.value\" :item=\"item\" :selected=\"item.value === selected\" />\r\n </div>\r\n </template>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-tabs {\r\n --j-tab-height: var(--juxt-control-md);\r\n --j-tab-font: var(--juxt-text-md);\r\n\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-tabs--sm {\r\n --j-tab-height: var(--juxt-control-sm);\r\n --j-tab-font: var(--juxt-text-sm);\r\n}\r\n\r\n.j-tabs__list {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n max-width: 100%;\r\n overflow-x: auto;\r\n scrollbar-width: none;\r\n}\r\n\r\n.j-tabs__list::-webkit-scrollbar {\r\n display: none;\r\n}\r\n\r\n.j-tabs__tab {\r\n position: relative;\r\n z-index: 1;\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n justify-content: center;\r\n gap: var(--juxt-space-1-5);\r\n height: var(--j-tab-height);\r\n padding: 0 var(--juxt-space-3);\r\n border: 0;\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n font: inherit;\r\n font-size: var(--j-tab-font);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n white-space: nowrap;\r\n cursor: pointer;\r\n outline: 2px solid transparent;\r\n outline-offset: -2px;\r\n -webkit-tap-highlight-color: transparent;\r\n transition:\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-tabs__tab:hover:not(:disabled) {\r\n color: var(--juxt-fg-secondary);\r\n}\r\n\r\n.j-tabs__tab[aria-selected='true'] {\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-tabs__tab:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n}\r\n\r\n.j-tabs__tab:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-tabs--fill .j-tabs__tab {\r\n flex: 1;\r\n}\r\n\r\n.j-tabs__indicator {\r\n position: absolute;\r\n left: 0;\r\n opacity: 0;\r\n pointer-events: none;\r\n}\r\n\r\n.j-tabs__indicator.is-ready {\r\n opacity: 1;\r\n}\r\n\r\n.j-tabs__indicator.is-animated {\r\n transition:\r\n transform var(--juxt-duration-slow) var(--juxt-ease-out),\r\n width var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-tabs__indicator.is-animated {\r\n transition: none;\r\n }\r\n}\r\n\r\n/* Line */\r\n.j-tabs--line .j-tabs__list {\r\n gap: var(--juxt-space-1);\r\n box-shadow: inset 0 -1px 0 var(--juxt-border);\r\n}\r\n\r\n.j-tabs--line .j-tabs__tab {\r\n padding: 0 var(--juxt-space-2);\r\n border-radius: var(--juxt-radius-sm) var(--juxt-radius-sm) 0 0;\r\n}\r\n\r\n.j-tabs--line .j-tabs__indicator {\r\n bottom: 0;\r\n height: 2px;\r\n border-radius: 2px 2px 0 0;\r\n background: var(--juxt-fg);\r\n}\r\n\r\n/* Pill */\r\n.j-tabs--pill .j-tabs__list {\r\n align-self: flex-start;\r\n padding: 3px;\r\n border-radius: var(--juxt-radius-md);\r\n background: var(--juxt-surface-sunken);\r\n box-shadow: inset 0 0 0 1px var(--juxt-border-subtle);\r\n}\r\n\r\n.j-tabs--pill.j-tabs--fill .j-tabs__list {\r\n align-self: stretch;\r\n}\r\n\r\n.j-tabs--pill .j-tabs__tab {\r\n --j-tab-height: calc(var(--juxt-control-md) - 6px);\r\n\r\n border-radius: var(--juxt-radius-sm);\r\n}\r\n\r\n.j-tabs--pill.j-tabs--sm .j-tabs__tab {\r\n --j-tab-height: calc(var(--juxt-control-sm) - 6px);\r\n\r\n padding: 0 var(--juxt-space-2-5);\r\n}\r\n\r\n.j-tabs--pill .j-tabs__indicator {\r\n top: 3px;\r\n bottom: 3px;\r\n border-radius: var(--juxt-radius-sm);\r\n background: var(--juxt-surface);\r\n box-shadow: var(--juxt-shadow-sm);\r\n}\r\n\r\n.dark .j-tabs--pill .j-tabs__indicator,\r\n[data-theme='dark'] .j-tabs--pill .j-tabs__indicator {\r\n background: var(--juxt-surface-active);\r\n}\r\n\r\n/* Panels */\r\n.j-tabs__panel {\r\n padding-top: var(--juxt-space-4);\r\n border-radius: var(--juxt-radius-sm);\r\n outline: 2px solid transparent;\r\n outline-offset: 2px;\r\n animation: j-tabs-panel-in var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-tabs__panel:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n}\r\n\r\n@keyframes j-tabs-panel-in {\r\n from {\r\n opacity: 0;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'\r\nimport { focusElement } from '../../utils/dom'\r\n\r\nexport interface TabItem {\r\n value: string\r\n label: string\r\n disabled?: boolean\r\n}\r\n\r\nexport interface TabsProps {\r\n items: TabItem[]\r\n /** `line` for page sections, `pill` for compact view switches. */\r\n variant?: 'line' | 'pill'\r\n size?: 'sm' | 'md'\r\n /** Accessible name for the tab list. */\r\n label?: string\r\n /** Stretch tabs to fill the width. */\r\n fill?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<TabsProps>(), { variant: 'line', size: 'md' })\r\n\r\nconst model = defineModel<string>()\r\n\r\ndefineSlots<{\r\n /** Custom tab label. */\r\n tab?: (props: { item: TabItem, selected: boolean }) => unknown\r\n /** Panels are named after each item's `value`. */\r\n [value: string]: ((props: { item: TabItem, selected: boolean }) => unknown) | undefined\r\n}>()\r\n\r\nconst id = `j-${useId()}`\r\nconst tabId = (value: string) => `${id}-tab-${value}`\r\nconst panelId = (value: string) => `${id}-panel-${value}`\r\n\r\nconst selected = computed(() => model.value ?? props.items.find(item => !item.disabled)?.value)\r\n\r\nconst list = ref<HTMLElement | null>(null)\r\nconst indicator = ref({ x: 0, width: 0, ready: false })\r\n// The indicator glides only for deliberate changes, never for layout shifts.\r\nconst animated = ref(false)\r\n\r\nfunction measure() {\r\n const el = list.value?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]')\r\n indicator.value = el ? { x: el.offsetLeft, width: el.offsetWidth, ready: true } : { ...indicator.value, ready: false }\r\n}\r\n\r\nfunction measureWithoutMotion() {\r\n animated.value = false\r\n measure()\r\n requestAnimationFrame(() => requestAnimationFrame(() => (animated.value = true)))\r\n}\r\n\r\nlet observer: ResizeObserver | undefined\r\nonMounted(() => {\r\n measureWithoutMotion()\r\n observer = new ResizeObserver(measureWithoutMotion)\r\n if (list.value) observer.observe(list.value)\r\n // Web fonts change tab widths once they arrive.\r\n document.fonts?.ready.then(measureWithoutMotion)\r\n})\r\nonBeforeUnmount(() => observer?.disconnect())\r\n\r\nwatch(selected, () => nextTick(measure), { flush: 'post' })\r\nwatch(() => props.items, () => nextTick(measure), { deep: true, flush: 'post' })\r\n\r\nfunction select(item: TabItem) {\r\n if (item.disabled) return\r\n model.value = item.value\r\n}\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n const enabled = props.items.filter(item => !item.disabled)\r\n if (!enabled.length) return\r\n const current = enabled.findIndex(item => item.value === selected.value)\r\n let next: number | undefined\r\n if (event.key === 'ArrowRight') next = (current + 1) % enabled.length\r\n else if (event.key === 'ArrowLeft') next = (current - 1 + enabled.length) % enabled.length\r\n else if (event.key === 'Home') next = 0\r\n else if (event.key === 'End') next = enabled.length - 1\r\n if (next === undefined) return\r\n event.preventDefault()\r\n const item = enabled[next]!\r\n select(item)\r\n nextTick(() => focusElement(document.getElementById(tabId(item.value))))\r\n}\r\n\r\nconst indicatorStyle = computed(() => ({\r\n transform: `translateX(${indicator.value.x}px)`,\r\n width: `${indicator.value.width}px`,\r\n}))\r\n</script>\r\n\r\n<template>\r\n <div class=\"j-tabs\" :class=\"[`j-tabs--${variant}`, `j-tabs--${size}`, { 'j-tabs--fill': fill }]\">\r\n <div\r\n ref=\"list\"\r\n role=\"tablist\"\r\n aria-orientation=\"horizontal\"\r\n :aria-label=\"label\"\r\n class=\"j-tabs__list\"\r\n @keydown=\"onKeydown\"\r\n >\r\n <span\r\n class=\"j-tabs__indicator\"\r\n :class=\"{ 'is-ready': indicator.ready, 'is-animated': animated }\"\r\n :style=\"indicatorStyle\"\r\n aria-hidden=\"true\"\r\n />\r\n <button\r\n v-for=\"item in items\"\r\n :id=\"tabId(item.value)\"\r\n :key=\"item.value\"\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"j-tabs__tab\"\r\n :aria-selected=\"item.value === selected\"\r\n :aria-controls=\"panelId(item.value)\"\r\n :tabindex=\"item.value === selected ? 0 : -1\"\r\n :disabled=\"item.disabled\"\r\n @click=\"select(item)\"\r\n >\r\n <slot name=\"tab\" :item=\"item\" :selected=\"item.value === selected\">\r\n {{ item.label }}\r\n </slot>\r\n </button>\r\n </div>\r\n <template v-for=\"item in items\" :key=\"item.value\">\r\n <div\r\n v-if=\"$slots[item.value]\"\r\n :id=\"panelId(item.value)\"\r\n role=\"tabpanel\"\r\n class=\"j-tabs__panel\"\r\n :aria-labelledby=\"tabId(item.value)\"\r\n :hidden=\"item.value !== selected\"\r\n tabindex=\"0\"\r\n >\r\n <slot :name=\"item.value\" :item=\"item\" :selected=\"item.value === selected\" />\r\n </div>\r\n </template>\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-tabs {\r\n --j-tab-height: var(--juxt-control-md);\r\n --j-tab-font: var(--juxt-text-md);\r\n\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-tabs--sm {\r\n --j-tab-height: var(--juxt-control-sm);\r\n --j-tab-font: var(--juxt-text-sm);\r\n}\r\n\r\n.j-tabs__list {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n max-width: 100%;\r\n overflow-x: auto;\r\n scrollbar-width: none;\r\n}\r\n\r\n.j-tabs__list::-webkit-scrollbar {\r\n display: none;\r\n}\r\n\r\n.j-tabs__tab {\r\n position: relative;\r\n z-index: 1;\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n justify-content: center;\r\n gap: var(--juxt-space-1-5);\r\n height: var(--j-tab-height);\r\n padding: 0 var(--juxt-space-3);\r\n border: 0;\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n font: inherit;\r\n font-size: var(--j-tab-font);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n white-space: nowrap;\r\n cursor: pointer;\r\n outline: 2px solid transparent;\r\n outline-offset: -2px;\r\n -webkit-tap-highlight-color: transparent;\r\n transition:\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-tabs__tab:hover:not(:disabled) {\r\n color: var(--juxt-fg-secondary);\r\n}\r\n\r\n.j-tabs__tab[aria-selected='true'] {\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-tabs__tab:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n}\r\n\r\n.j-tabs__tab:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n\r\n.j-tabs--fill .j-tabs__tab {\r\n flex: 1;\r\n}\r\n\r\n.j-tabs__indicator {\r\n position: absolute;\r\n left: 0;\r\n opacity: 0;\r\n pointer-events: none;\r\n}\r\n\r\n.j-tabs__indicator.is-ready {\r\n opacity: 1;\r\n}\r\n\r\n.j-tabs__indicator.is-animated {\r\n transition:\r\n transform var(--juxt-duration-slow) var(--juxt-ease-out),\r\n width var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .j-tabs__indicator.is-animated {\r\n transition: none;\r\n }\r\n}\r\n\r\n/* Line */\r\n.j-tabs--line .j-tabs__list {\r\n gap: var(--juxt-space-1);\r\n box-shadow: inset 0 -1px 0 var(--juxt-border);\r\n}\r\n\r\n.j-tabs--line .j-tabs__tab {\r\n padding: 0 var(--juxt-space-2);\r\n border-radius: var(--juxt-radius-sm) var(--juxt-radius-sm) 0 0;\r\n}\r\n\r\n.j-tabs--line .j-tabs__indicator {\r\n bottom: 0;\r\n height: 2px;\r\n border-radius: 2px 2px 0 0;\r\n background: var(--juxt-fg);\r\n}\r\n\r\n/* Pill */\r\n.j-tabs--pill .j-tabs__list {\r\n align-self: flex-start;\r\n padding: 3px;\r\n border-radius: var(--juxt-radius-md);\r\n background: var(--juxt-surface-sunken);\r\n box-shadow: inset 0 0 0 1px var(--juxt-border-subtle);\r\n}\r\n\r\n.j-tabs--pill.j-tabs--fill .j-tabs__list {\r\n align-self: stretch;\r\n}\r\n\r\n.j-tabs--pill .j-tabs__tab {\r\n --j-tab-height: calc(var(--juxt-control-md) - 6px);\r\n\r\n border-radius: var(--juxt-radius-sm);\r\n}\r\n\r\n.j-tabs--pill.j-tabs--sm .j-tabs__tab {\r\n --j-tab-height: calc(var(--juxt-control-sm) - 6px);\r\n\r\n padding: 0 var(--juxt-space-2-5);\r\n}\r\n\r\n.j-tabs--pill .j-tabs__indicator {\r\n top: 3px;\r\n bottom: 3px;\r\n border-radius: var(--juxt-radius-sm);\r\n background: var(--juxt-surface);\r\n box-shadow: var(--juxt-shadow-sm);\r\n}\r\n\r\n.dark .j-tabs--pill .j-tabs__indicator,\r\n[data-theme='dark'] .j-tabs--pill .j-tabs__indicator {\r\n background: var(--juxt-surface-active);\r\n}\r\n\r\n/* Panels */\r\n.j-tabs__panel {\r\n padding-top: var(--juxt-space-4);\r\n border-radius: var(--juxt-radius-sm);\r\n outline: 2px solid transparent;\r\n outline-offset: 2px;\r\n animation: j-tabs-panel-in var(--juxt-duration-normal) var(--juxt-ease-out);\r\n}\r\n\r\n.j-tabs__panel:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n}\r\n\r\n@keyframes j-tabs-panel-in {\r\n from {\r\n opacity: 0;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, useSlots } from 'vue'\r\nimport { IconX } from '../../icons'\r\n\r\nexport type TagVariant = 'neutral' | 'success' | 'warning' | 'destructive' | 'accent'\r\n\r\nexport interface TagProps {\r\n variant?: TagVariant\r\n size?: 'sm' | 'md'\r\n disabled?: boolean\r\n /** Accessible label for the remove button, e.g. `\"Remove Engineering\"`. Defaults to \"Remove\". */\r\n removeLabel?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<TagProps>(), { variant: 'neutral', size: 'md' })\r\n\r\nconst emit = defineEmits<{ remove: [] }>()\r\n\r\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\r\n\r\nconst slots = useSlots()\r\n// Falls back to the tag's own text so a list of tags without an explicit\r\n// `removeLabel` each still announce a distinct name, not several \"Remove\"s.\r\nconst text = computed(() => slots.default?.().map(vnode => (typeof vnode.children === 'string' ? vnode.children : '')).join('').trim())\r\nconst removeButtonLabel = computed(() => props.removeLabel || (text.value ? `Remove ${text.value}` : 'Remove'))\r\n\r\nfunction onRemove() {\r\n if (props.disabled) return\r\n emit('remove')\r\n}\r\n</script>\r\n\r\n<template>\r\n <span class=\"j-tag\" :class=\"[`j-tag--${variant}`, `j-tag--${size}`, { 'is-disabled': disabled }]\">\r\n <span v-if=\"$slots.leading\" class=\"j-tag__icon\"><slot name=\"leading\" /></span>\r\n <span class=\"j-tag__label\"><slot /></span>\r\n <button type=\"button\" class=\"j-tag__remove j-focusable\" :aria-label=\"removeButtonLabel\" :disabled=\"disabled\" @click=\"onRemove\">\r\n <IconX />\r\n </button>\r\n </span>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n/*\r\n * Tags are pills, not stamps | fully rounded and carrying their own remove\r\n * affordance, which is what sets them apart from the static JBadge.\r\n */\r\n.j-tag {\r\n --j-tag-fg: var(--juxt-fg-secondary);\r\n --j-tag-bg: var(--juxt-surface-active);\r\n\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n gap: 0.3125rem;\r\n height: 1.375rem;\r\n padding: 0 var(--juxt-space-1) 0 var(--juxt-space-2-5);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--j-tag-bg);\r\n color: var(--j-tag-fg);\r\n font-family: var(--juxt-font-sans);\r\n font-size: var(--juxt-text-xs);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: 1;\r\n white-space: nowrap;\r\n}\r\n\r\n.j-tag--sm {\r\n height: 1.125rem;\r\n padding: 0 0.1875rem 0 var(--juxt-space-2);\r\n font-size: var(--juxt-text-2xs);\r\n}\r\n\r\n.j-tag--success {\r\n --j-tag-fg: var(--juxt-success-text);\r\n --j-tag-bg: var(--juxt-success-soft);\r\n}\r\n\r\n.j-tag--warning {\r\n --j-tag-fg: var(--juxt-warning-text);\r\n --j-tag-bg: var(--juxt-warning-soft);\r\n}\r\n\r\n.j-tag--destructive {\r\n --j-tag-fg: var(--juxt-danger-text);\r\n --j-tag-bg: var(--juxt-danger-soft);\r\n}\r\n\r\n.j-tag--accent {\r\n --j-tag-fg: var(--juxt-accent-fg);\r\n --j-tag-bg: var(--juxt-accent-strong);\r\n}\r\n\r\n.j-tag__icon {\r\n display: inline-flex;\r\n margin-left: -0.0625rem;\r\n}\r\n\r\n.j-tag__icon > svg {\r\n width: 0.75rem;\r\n height: 0.75rem;\r\n}\r\n\r\n.j-tag__label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n}\r\n\r\n.j-tag__remove {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n width: 1.125rem;\r\n height: 1.125rem;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-full);\r\n background: transparent;\r\n color: inherit;\r\n cursor: pointer;\r\n opacity: 0.7;\r\n transition:\r\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-tag--sm .j-tag__remove {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n\r\n.j-tag__remove:hover:not(:disabled) {\r\n background: rgb(0 0 0 / 0.08);\r\n opacity: 1;\r\n}\r\n\r\n.dark .j-tag__remove:hover:not(:disabled),\r\n[data-theme='dark'] .j-tag__remove:hover:not(:disabled) {\r\n background: rgb(255 255 255 / 0.12);\r\n}\r\n\r\n.j-tag__remove > svg {\r\n width: 0.625rem;\r\n height: 0.625rem;\r\n}\r\n\r\n.j-tag--sm .j-tag__remove > svg {\r\n width: 0.5rem;\r\n height: 0.5rem;\r\n}\r\n\r\n.j-tag.is-disabled {\r\n opacity: 0.5;\r\n}\r\n\r\n.j-tag__remove:disabled {\r\n cursor: not-allowed;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, useSlots } from 'vue'\r\nimport { IconX } from '../../icons'\r\n\r\nexport type TagVariant = 'neutral' | 'success' | 'warning' | 'destructive' | 'accent'\r\n\r\nexport interface TagProps {\r\n variant?: TagVariant\r\n size?: 'sm' | 'md'\r\n disabled?: boolean\r\n /** Accessible label for the remove button, e.g. `\"Remove Engineering\"`. Defaults to \"Remove\". */\r\n removeLabel?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<TagProps>(), { variant: 'neutral', size: 'md' })\r\n\r\nconst emit = defineEmits<{ remove: [] }>()\r\n\r\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\r\n\r\nconst slots = useSlots()\r\n// Falls back to the tag's own text so a list of tags without an explicit\r\n// `removeLabel` each still announce a distinct name, not several \"Remove\"s.\r\nconst text = computed(() => slots.default?.().map(vnode => (typeof vnode.children === 'string' ? vnode.children : '')).join('').trim())\r\nconst removeButtonLabel = computed(() => props.removeLabel || (text.value ? `Remove ${text.value}` : 'Remove'))\r\n\r\nfunction onRemove() {\r\n if (props.disabled) return\r\n emit('remove')\r\n}\r\n</script>\r\n\r\n<template>\r\n <span class=\"j-tag\" :class=\"[`j-tag--${variant}`, `j-tag--${size}`, { 'is-disabled': disabled }]\">\r\n <span v-if=\"$slots.leading\" class=\"j-tag__icon\"><slot name=\"leading\" /></span>\r\n <span class=\"j-tag__label\"><slot /></span>\r\n <button type=\"button\" class=\"j-tag__remove j-focusable\" :aria-label=\"removeButtonLabel\" :disabled=\"disabled\" @click=\"onRemove\">\r\n <IconX />\r\n </button>\r\n </span>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n/*\r\n * Tags are pills, not stamps | fully rounded and carrying their own remove\r\n * affordance, which is what sets them apart from the static JBadge.\r\n */\r\n.j-tag {\r\n --j-tag-fg: var(--juxt-fg-secondary);\r\n --j-tag-bg: var(--juxt-surface-active);\r\n\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n gap: 0.3125rem;\r\n height: 1.375rem;\r\n padding: 0 var(--juxt-space-1) 0 var(--juxt-space-2-5);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--j-tag-bg);\r\n color: var(--j-tag-fg);\r\n font-family: var(--juxt-font-sans);\r\n font-size: var(--juxt-text-xs);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: 1;\r\n white-space: nowrap;\r\n}\r\n\r\n.j-tag--sm {\r\n height: 1.125rem;\r\n padding: 0 0.1875rem 0 var(--juxt-space-2);\r\n font-size: var(--juxt-text-2xs);\r\n}\r\n\r\n.j-tag--success {\r\n --j-tag-fg: var(--juxt-success-text);\r\n --j-tag-bg: var(--juxt-success-soft);\r\n}\r\n\r\n.j-tag--warning {\r\n --j-tag-fg: var(--juxt-warning-text);\r\n --j-tag-bg: var(--juxt-warning-soft);\r\n}\r\n\r\n.j-tag--destructive {\r\n --j-tag-fg: var(--juxt-danger-text);\r\n --j-tag-bg: var(--juxt-danger-soft);\r\n}\r\n\r\n.j-tag--accent {\r\n --j-tag-fg: var(--juxt-accent-fg);\r\n --j-tag-bg: var(--juxt-accent-strong);\r\n}\r\n\r\n.j-tag__icon {\r\n display: inline-flex;\r\n margin-left: -0.0625rem;\r\n}\r\n\r\n.j-tag__icon > svg {\r\n width: 0.75rem;\r\n height: 0.75rem;\r\n}\r\n\r\n.j-tag__label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n}\r\n\r\n.j-tag__remove {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n width: 1.125rem;\r\n height: 1.125rem;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-full);\r\n background: transparent;\r\n color: inherit;\r\n cursor: pointer;\r\n opacity: 0.7;\r\n transition:\r\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-tag--sm .j-tag__remove {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n\r\n.j-tag__remove:hover:not(:disabled) {\r\n background: rgb(0 0 0 / 0.08);\r\n opacity: 1;\r\n}\r\n\r\n.dark .j-tag__remove:hover:not(:disabled),\r\n[data-theme='dark'] .j-tag__remove:hover:not(:disabled) {\r\n background: rgb(255 255 255 / 0.12);\r\n}\r\n\r\n.j-tag__remove > svg {\r\n width: 0.625rem;\r\n height: 0.625rem;\r\n}\r\n\r\n.j-tag--sm .j-tag__remove > svg {\r\n width: 0.5rem;\r\n height: 0.5rem;\r\n}\r\n\r\n.j-tag.is-disabled {\r\n opacity: 0.5;\r\n}\r\n\r\n.j-tag__remove:disabled {\r\n cursor: not-allowed;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { FieldProps } from '../field/useField'\r\nimport { nextTick, onMounted, ref, watch } from 'vue'\r\nimport JFieldShell from '../field/JFieldShell.vue'\r\nimport { useField } from '../field/useField'\r\n\r\nexport interface TextareaProps extends FieldProps {\r\n placeholder?: string\r\n rows?: number\r\n /** Grow with the content, up to `maxRows`. */\r\n autoresize?: boolean\r\n maxRows?: number\r\n readonly?: boolean\r\n name?: string\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = withDefaults(defineProps<TextareaProps>(), {\r\n rows: 3,\r\n maxRows: 12,\r\n error: undefined,\r\n})\r\n\r\nconst model = defineModel<string | null>({ default: '' })\r\n\r\ndefineSlots<{ label?: () => unknown }>()\r\n\r\nconst field = useField(props)\r\nconst textarea = ref<HTMLTextAreaElement | null>(null)\r\n\r\nfunction resize() {\r\n const el = textarea.value\r\n if (!props.autoresize || !el) return\r\n const style = getComputedStyle(el)\r\n const line = Number.parseFloat(style.lineHeight) || 20\r\n const chrome = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom)\r\n el.style.height = 'auto'\r\n const max = line * props.maxRows + chrome\r\n const next = Math.min(el.scrollHeight, max)\r\n el.style.height = `${next}px`\r\n el.style.overflowY = el.scrollHeight > max ? 'auto' : 'hidden'\r\n}\r\n\r\nonMounted(resize)\r\nwatch(model, () => nextTick(resize))\r\nwatch(() => props.autoresize, (value) => {\r\n if (value) return nextTick(resize)\r\n if (textarea.value) textarea.value.style.height = ''\r\n})\r\n\r\ndefineExpose({\r\n textarea,\r\n focus: () => textarea.value?.focus(),\r\n})\r\n</script>\r\n\r\n<template>\r\n <JFieldShell\r\n :id=\"field.id.value\"\r\n :class=\"$attrs.class\"\r\n :style=\"$attrs.style\"\r\n :label-id=\"field.labelId.value\"\r\n :description-id=\"field.descriptionId.value\"\r\n :error-id=\"field.errorId.value\"\r\n :label=\"label\"\r\n :description=\"description\"\r\n :error-message=\"field.errorMessage.value\"\r\n :show-description=\"field.showDescription.value\"\r\n :required=\"required\"\r\n :disabled=\"disabled\"\r\n >\r\n <template v-if=\"$slots.label\" #label>\r\n <slot name=\"label\" />\r\n </template>\r\n <div\r\n class=\"j-control j-textarea\"\r\n :class=\"{ 'is-invalid': field.invalid.value, 'is-disabled': disabled, 'is-readonly': readonly }\"\r\n >\r\n <textarea\r\n :id=\"field.id.value\"\r\n ref=\"textarea\"\r\n v-model=\"model\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n class=\"j-textarea__native\"\r\n :class=\"{ 'is-autoresize': autoresize }\"\r\n :rows=\"rows\"\r\n :name=\"name\"\r\n :placeholder=\"placeholder\"\r\n :disabled=\"disabled\"\r\n :readonly=\"readonly\"\r\n :required=\"required\"\r\n :aria-invalid=\"field.invalid.value || undefined\"\r\n :aria-describedby=\"field.describedBy.value\"\r\n />\r\n </div>\r\n </JFieldShell>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-textarea {\r\n align-items: stretch;\r\n padding: 0;\r\n cursor: auto;\r\n}\r\n\r\n.j-textarea__native {\r\n display: block;\r\n width: 100%;\r\n min-height: calc(var(--juxt-control-md) * 2);\r\n padding: var(--juxt-space-2) var(--juxt-space-2-5);\r\n border: 0;\r\n border-radius: inherit;\r\n outline: 0;\r\n background: transparent;\r\n color: inherit;\r\n font: inherit;\r\n line-height: var(--juxt-leading-normal);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n resize: vertical;\r\n}\r\n\r\n.j-textarea__native.is-autoresize {\r\n resize: none;\r\n overflow-y: hidden;\r\n}\r\n\r\n.j-textarea__native::placeholder {\r\n color: var(--juxt-fg-muted);\r\n opacity: 1;\r\n}\r\n\r\n.j-textarea__native:disabled {\r\n cursor: not-allowed;\r\n resize: none;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { FieldProps } from '../field/useField'\r\nimport { nextTick, onMounted, ref, watch } from 'vue'\r\nimport JFieldShell from '../field/JFieldShell.vue'\r\nimport { useField } from '../field/useField'\r\n\r\nexport interface TextareaProps extends FieldProps {\r\n placeholder?: string\r\n rows?: number\r\n /** Grow with the content, up to `maxRows`. */\r\n autoresize?: boolean\r\n maxRows?: number\r\n readonly?: boolean\r\n name?: string\r\n}\r\n\r\ndefineOptions({ inheritAttrs: false })\r\n\r\nconst props = withDefaults(defineProps<TextareaProps>(), {\r\n rows: 3,\r\n maxRows: 12,\r\n error: undefined,\r\n})\r\n\r\nconst model = defineModel<string | null>({ default: '' })\r\n\r\ndefineSlots<{ label?: () => unknown }>()\r\n\r\nconst field = useField(props)\r\nconst textarea = ref<HTMLTextAreaElement | null>(null)\r\n\r\nfunction resize() {\r\n const el = textarea.value\r\n if (!props.autoresize || !el) return\r\n const style = getComputedStyle(el)\r\n const line = Number.parseFloat(style.lineHeight) || 20\r\n const chrome = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom)\r\n el.style.height = 'auto'\r\n const max = line * props.maxRows + chrome\r\n const next = Math.min(el.scrollHeight, max)\r\n el.style.height = `${next}px`\r\n el.style.overflowY = el.scrollHeight > max ? 'auto' : 'hidden'\r\n}\r\n\r\nonMounted(resize)\r\nwatch(model, () => nextTick(resize))\r\nwatch(() => props.autoresize, (value) => {\r\n if (value) return nextTick(resize)\r\n if (textarea.value) textarea.value.style.height = ''\r\n})\r\n\r\ndefineExpose({\r\n textarea,\r\n focus: () => textarea.value?.focus(),\r\n})\r\n</script>\r\n\r\n<template>\r\n <JFieldShell\r\n :id=\"field.id.value\"\r\n :class=\"$attrs.class\"\r\n :style=\"$attrs.style\"\r\n :label-id=\"field.labelId.value\"\r\n :description-id=\"field.descriptionId.value\"\r\n :error-id=\"field.errorId.value\"\r\n :label=\"label\"\r\n :description=\"description\"\r\n :error-message=\"field.errorMessage.value\"\r\n :show-description=\"field.showDescription.value\"\r\n :required=\"required\"\r\n :disabled=\"disabled\"\r\n >\r\n <template v-if=\"$slots.label\" #label>\r\n <slot name=\"label\" />\r\n </template>\r\n <div\r\n class=\"j-control j-textarea\"\r\n :class=\"{ 'is-invalid': field.invalid.value, 'is-disabled': disabled, 'is-readonly': readonly }\"\r\n >\r\n <textarea\r\n :id=\"field.id.value\"\r\n ref=\"textarea\"\r\n v-model=\"model\"\r\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\r\n class=\"j-textarea__native\"\r\n :class=\"{ 'is-autoresize': autoresize }\"\r\n :rows=\"rows\"\r\n :name=\"name\"\r\n :placeholder=\"placeholder\"\r\n :disabled=\"disabled\"\r\n :readonly=\"readonly\"\r\n :required=\"required\"\r\n :aria-invalid=\"field.invalid.value || undefined\"\r\n :aria-describedby=\"field.describedBy.value\"\r\n />\r\n </div>\r\n </JFieldShell>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-textarea {\r\n align-items: stretch;\r\n padding: 0;\r\n cursor: auto;\r\n}\r\n\r\n.j-textarea__native {\r\n display: block;\r\n width: 100%;\r\n min-height: calc(var(--juxt-control-md) * 2);\r\n padding: var(--juxt-space-2) var(--juxt-space-2-5);\r\n border: 0;\r\n border-radius: inherit;\r\n outline: 0;\r\n background: transparent;\r\n color: inherit;\r\n font: inherit;\r\n line-height: var(--juxt-leading-normal);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n resize: vertical;\r\n}\r\n\r\n.j-textarea__native.is-autoresize {\r\n resize: none;\r\n overflow-y: hidden;\r\n}\r\n\r\n.j-textarea__native::placeholder {\r\n color: var(--juxt-fg-muted);\r\n opacity: 1;\r\n}\r\n\r\n.j-textarea__native:disabled {\r\n cursor: not-allowed;\r\n resize: none;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\ndefineSlots<{ default?: () => unknown }>()\r\n</script>\r\n\r\n<template>\r\n <ol class=\"j-timeline\">\r\n <slot />\r\n </ol>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-timeline {\r\n display: flex;\r\n flex-direction: column;\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\ndefineSlots<{ default?: () => unknown }>()\r\n</script>\r\n\r\n<template>\r\n <ol class=\"j-timeline\">\r\n <slot />\r\n </ol>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-timeline {\r\n display: flex;\r\n flex-direction: column;\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n font-family: var(--juxt-font-sans);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nexport type TimelineItemStatus = 'complete' | 'current' | 'upcoming'\r\n\r\nexport interface TimelineItemProps {\r\n title?: string\r\n /** A timestamp or relative time, e.g. \"2 hours ago\". */\r\n time?: string\r\n status?: TimelineItemStatus\r\n}\r\n\r\nwithDefaults(defineProps<TimelineItemProps>(), { status: 'complete' })\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n title?: () => unknown\r\n /** Replace the default dot marker. */\r\n marker?: () => unknown\r\n}>()\r\n</script>\r\n\r\n<template>\r\n <li class=\"j-timeline-item\" :class=\"`j-timeline-item--${status}`\">\r\n <span class=\"j-timeline-item__rail\" aria-hidden=\"true\">\r\n <span class=\"j-timeline-item__marker\">\r\n <slot name=\"marker\" />\r\n </span>\r\n </span>\r\n <div class=\"j-timeline-item__content\">\r\n <div class=\"j-timeline-item__header\">\r\n <p v-if=\"title || $slots.title\" class=\"j-timeline-item__title\">\r\n <slot name=\"title\">{{ title }}</slot>\r\n </p>\r\n <time v-if=\"time\" class=\"j-timeline-item__time\">{{ time }}</time>\r\n </div>\r\n <div v-if=\"$slots.default\" class=\"j-timeline-item__body\">\r\n <slot />\r\n </div>\r\n </div>\r\n </li>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-timeline-item {\r\n position: relative;\r\n display: flex;\r\n gap: var(--juxt-space-3);\r\n padding-bottom: var(--juxt-space-5);\r\n}\r\n\r\n.j-timeline-item:last-child {\r\n padding-bottom: 0;\r\n}\r\n\r\n.j-timeline-item__rail {\r\n position: relative;\r\n display: flex;\r\n flex: none;\r\n justify-content: center;\r\n width: 1rem;\r\n}\r\n\r\n.j-timeline-item__rail::before {\r\n content: '';\r\n position: absolute;\r\n top: 1rem;\r\n bottom: calc(var(--juxt-space-5) * -1);\r\n width: 1px;\r\n background: var(--juxt-border);\r\n}\r\n\r\n.j-timeline-item:last-child .j-timeline-item__rail::before {\r\n display: none;\r\n}\r\n\r\n.j-timeline-item__marker {\r\n z-index: 1;\r\n display: grid;\r\n place-items: center;\r\n width: 0.625rem;\r\n height: 0.625rem;\r\n margin-top: 0.25rem;\r\n border: 2px solid var(--juxt-surface);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-fg-disabled);\r\n box-shadow: 0 0 0 1px var(--juxt-border);\r\n}\r\n\r\n.j-timeline-item--complete .j-timeline-item__marker {\r\n background: var(--juxt-accent);\r\n box-shadow: none;\r\n}\r\n\r\n.j-timeline-item--current .j-timeline-item__marker {\r\n background: var(--juxt-surface);\r\n box-shadow: 0 0 0 2px var(--juxt-accent);\r\n}\r\n\r\n.j-timeline-item__content {\r\n flex: 1;\r\n min-width: 0;\r\n padding-bottom: var(--juxt-space-0-5);\r\n}\r\n\r\n.j-timeline-item__header {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: baseline;\r\n gap: var(--juxt-space-2);\r\n}\r\n\r\n.j-timeline-item__title {\r\n margin: 0;\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-timeline-item--upcoming .j-timeline-item__title {\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-timeline-item__time {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-timeline-item__body {\r\n margin-top: var(--juxt-space-0-5);\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nexport type TimelineItemStatus = 'complete' | 'current' | 'upcoming'\r\n\r\nexport interface TimelineItemProps {\r\n title?: string\r\n /** A timestamp or relative time, e.g. \"2 hours ago\". */\r\n time?: string\r\n status?: TimelineItemStatus\r\n}\r\n\r\nwithDefaults(defineProps<TimelineItemProps>(), { status: 'complete' })\r\n\r\ndefineSlots<{\r\n default?: () => unknown\r\n title?: () => unknown\r\n /** Replace the default dot marker. */\r\n marker?: () => unknown\r\n}>()\r\n</script>\r\n\r\n<template>\r\n <li class=\"j-timeline-item\" :class=\"`j-timeline-item--${status}`\">\r\n <span class=\"j-timeline-item__rail\" aria-hidden=\"true\">\r\n <span class=\"j-timeline-item__marker\">\r\n <slot name=\"marker\" />\r\n </span>\r\n </span>\r\n <div class=\"j-timeline-item__content\">\r\n <div class=\"j-timeline-item__header\">\r\n <p v-if=\"title || $slots.title\" class=\"j-timeline-item__title\">\r\n <slot name=\"title\">{{ title }}</slot>\r\n </p>\r\n <time v-if=\"time\" class=\"j-timeline-item__time\">{{ time }}</time>\r\n </div>\r\n <div v-if=\"$slots.default\" class=\"j-timeline-item__body\">\r\n <slot />\r\n </div>\r\n </div>\r\n </li>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-timeline-item {\r\n position: relative;\r\n display: flex;\r\n gap: var(--juxt-space-3);\r\n padding-bottom: var(--juxt-space-5);\r\n}\r\n\r\n.j-timeline-item:last-child {\r\n padding-bottom: 0;\r\n}\r\n\r\n.j-timeline-item__rail {\r\n position: relative;\r\n display: flex;\r\n flex: none;\r\n justify-content: center;\r\n width: 1rem;\r\n}\r\n\r\n.j-timeline-item__rail::before {\r\n content: '';\r\n position: absolute;\r\n top: 1rem;\r\n bottom: calc(var(--juxt-space-5) * -1);\r\n width: 1px;\r\n background: var(--juxt-border);\r\n}\r\n\r\n.j-timeline-item:last-child .j-timeline-item__rail::before {\r\n display: none;\r\n}\r\n\r\n.j-timeline-item__marker {\r\n z-index: 1;\r\n display: grid;\r\n place-items: center;\r\n width: 0.625rem;\r\n height: 0.625rem;\r\n margin-top: 0.25rem;\r\n border: 2px solid var(--juxt-surface);\r\n border-radius: var(--juxt-radius-full);\r\n background: var(--juxt-fg-disabled);\r\n box-shadow: 0 0 0 1px var(--juxt-border);\r\n}\r\n\r\n.j-timeline-item--complete .j-timeline-item__marker {\r\n background: var(--juxt-accent);\r\n box-shadow: none;\r\n}\r\n\r\n.j-timeline-item--current .j-timeline-item__marker {\r\n background: var(--juxt-surface);\r\n box-shadow: 0 0 0 2px var(--juxt-accent);\r\n}\r\n\r\n.j-timeline-item__content {\r\n flex: 1;\r\n min-width: 0;\r\n padding-bottom: var(--juxt-space-0-5);\r\n}\r\n\r\n.j-timeline-item__header {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: baseline;\r\n gap: var(--juxt-space-2);\r\n}\r\n\r\n.j-timeline-item__title {\r\n margin: 0;\r\n color: var(--juxt-fg);\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-timeline-item--upcoming .j-timeline-item__title {\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-timeline-item__time {\r\n color: var(--juxt-fg-muted);\r\n font-size: var(--juxt-text-xs);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.j-timeline-item__body {\r\n margin-top: var(--juxt-space-0-5);\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n}\r\n</style>\r\n","import { reactive, readonly } from 'vue'\r\n\r\nexport type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info' | 'loading'\r\n\r\nexport interface ToastAction {\r\n label: string\r\n onClick: () => void\r\n}\r\n\r\nexport interface ToastOptions {\r\n /** Reuse an id to replace an existing toast instead of stacking a new one. */\r\n id?: string\r\n description?: string\r\n type?: ToastType\r\n /** Time on screen in ms. `Infinity` keeps it until dismissed. Loading toasts never expire. */\r\n duration?: number\r\n action?: ToastAction\r\n /** Show the close button. Defaults to true. */\r\n dismissible?: boolean\r\n onDismiss?: () => void\r\n}\r\n\r\nexport interface Toast {\r\n id: string\r\n title: string\r\n description?: string\r\n type: ToastType\r\n duration: number\r\n action?: ToastAction\r\n dismissible: boolean\r\n onDismiss?: () => void\r\n /** Bumps whenever the toast changes, restarting its timer. */\r\n version: number\r\n}\r\n\r\nexport const DEFAULT_TOAST_DURATION = 5000\r\n\r\nconst state = reactive<{ toasts: Toast[] }>({ toasts: [] })\r\nlet counter = 0\r\n\r\nfunction resolveDuration(type: ToastType, duration?: number) {\r\n if (duration !== undefined) return duration\r\n if (type === 'loading') return Number.POSITIVE_INFINITY\r\n // Errors deserve a little longer to be read.\r\n if (type === 'error') return DEFAULT_TOAST_DURATION + 2000\r\n return DEFAULT_TOAST_DURATION\r\n}\r\n\r\nfunction create(title: string, options: ToastOptions = {}): string {\r\n const type = options.type ?? 'default'\r\n const existing = options.id ? state.toasts.find(t => t.id === options.id) : undefined\r\n if (existing) {\r\n update(existing.id, { title, ...options })\r\n return existing.id\r\n }\r\n const id = options.id ?? `toast-${++counter}`\r\n state.toasts.push({\r\n id,\r\n title,\r\n description: options.description,\r\n type,\r\n duration: resolveDuration(type, options.duration),\r\n action: options.action,\r\n dismissible: options.dismissible ?? true,\r\n onDismiss: options.onDismiss,\r\n version: 0,\r\n })\r\n return id\r\n}\r\n\r\nfunction update(id: string, patch: Partial<ToastOptions> & { title?: string }) {\r\n const item = state.toasts.find(t => t.id === id)\r\n if (!item) return\r\n const type = patch.type ?? item.type\r\n Object.assign(item, {\r\n ...patch,\r\n type,\r\n duration: patch.duration !== undefined || patch.type ? resolveDuration(type, patch.duration) : item.duration,\r\n version: item.version + 1,\r\n })\r\n}\r\n\r\nfunction dismiss(id?: string) {\r\n if (id === undefined) {\r\n const all = state.toasts.splice(0)\r\n all.forEach(t => t.onDismiss?.())\r\n return\r\n }\r\n const index = state.toasts.findIndex(t => t.id === id)\r\n if (index === -1) return\r\n const [removed] = state.toasts.splice(index, 1)\r\n removed?.onDismiss?.()\r\n}\r\n\r\ntype Message<T> = string | ((value: T) => string)\r\n\r\ninterface PromiseMessages<T> {\r\n loading: string\r\n success: Message<T>\r\n error: Message<unknown>\r\n description?: string\r\n}\r\n\r\nfunction promise<T>(input: Promise<T> | (() => Promise<T>), messages: PromiseMessages<T>): Promise<T> {\r\n const id = create(messages.loading, { type: 'loading', description: messages.description })\r\n const run = typeof input === 'function' ? input() : input\r\n run.then(\r\n (value) => {\r\n const title = typeof messages.success === 'function' ? messages.success(value) : messages.success\r\n update(id, { title, type: 'success', description: undefined })\r\n },\r\n (error: unknown) => {\r\n const title = typeof messages.error === 'function' ? messages.error(error) : messages.error\r\n update(id, { title, type: 'error', description: undefined })\r\n },\r\n )\r\n return run\r\n}\r\n\r\ntype Shortcut = (title: string, options?: Omit<ToastOptions, 'type'>) => string\r\nconst typed = (type: ToastType): Shortcut => (title, options) => create(title, { ...options, type })\r\n\r\nexport interface ToastFn {\r\n (title: string, options?: ToastOptions): string\r\n success: Shortcut\r\n error: Shortcut\r\n warning: Shortcut\r\n info: Shortcut\r\n loading: Shortcut\r\n promise: typeof promise\r\n update: typeof update\r\n dismiss: typeof dismiss\r\n}\r\n\r\n/** Show a toast. Works anywhere | inside or outside components. */\r\nexport const toast: ToastFn = Object.assign(create, {\r\n success: typed('success'),\r\n error: typed('error'),\r\n warning: typed('warning'),\r\n info: typed('info'),\r\n loading: typed('loading'),\r\n promise,\r\n update,\r\n dismiss,\r\n})\r\n\r\nexport function useToast() {\r\n return {\r\n toast,\r\n dismiss,\r\n update,\r\n toasts: readonly(state).toasts,\r\n }\r\n}\r\n\r\n/** @internal Mutable access for JToaster and tests. */\r\nexport const toastState = state\r\n","<script setup lang=\"ts\">\r\n/** Internal: one toast. Owns its timer, swipe gesture and keyboard dismissal. */\r\nimport type { Toast } from '../../composables/useToast'\r\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\r\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX, Spinner } from '../../icons'\r\n\r\nconst props = defineProps<{\r\n toast: Toast\r\n paused: boolean\r\n}>()\r\n\r\nconst emit = defineEmits<{ dismiss: [id: string] }>()\r\n\r\nconst el = ref<HTMLElement | null>(null)\r\nconst timed = computed(() => Number.isFinite(props.toast.duration) && props.toast.duration > 0)\r\n\r\n// --- Timer -----------------------------------------------------------------\r\nlet timer: ReturnType<typeof setTimeout> | undefined\r\nlet remaining = 0\r\nlet startedAt = 0\r\n\r\nfunction start() {\r\n clearTimeout(timer)\r\n if (!timed.value || props.paused) return\r\n startedAt = Date.now()\r\n timer = setTimeout(() => emit('dismiss', props.toast.id), remaining)\r\n}\r\n\r\nfunction pause() {\r\n if (!timer) return\r\n clearTimeout(timer)\r\n timer = undefined\r\n remaining -= Date.now() - startedAt\r\n}\r\n\r\nfunction reset() {\r\n remaining = props.toast.duration\r\n start()\r\n}\r\n\r\nonMounted(reset)\r\nonBeforeUnmount(() => clearTimeout(timer))\r\nwatch(() => props.toast.version, reset)\r\nwatch(() => props.paused, value => (value ? pause() : start()))\r\n\r\n// --- Swipe to dismiss ------------------------------------------------------\r\nconst offset = ref(0)\r\nconst swiping = ref(false)\r\nconst leaving = ref<0 | 1 | -1>(0)\r\nlet origin: { x: number, y: number, id: number } | null = null\r\n\r\nfunction onPointerDown(event: PointerEvent) {\r\n if (event.button !== 0 || (event.target as HTMLElement).closest('button, a')) return\r\n origin = { x: event.clientX, y: event.clientY, id: event.pointerId }\r\n}\r\n\r\nfunction onPointerMove(event: PointerEvent) {\r\n if (!origin || event.pointerId !== origin.id) return\r\n const dx = event.clientX - origin.x\r\n if (!swiping.value) {\r\n // Commit to a swipe only once the gesture is clearly horizontal.\r\n if (Math.abs(dx) < 6 || Math.abs(dx) < Math.abs(event.clientY - origin.y)) return\r\n swiping.value = true\r\n el.value?.setPointerCapture(event.pointerId)\r\n }\r\n offset.value = dx\r\n}\r\n\r\nfunction onPointerUp() {\r\n if (!origin) return\r\n origin = null\r\n if (!swiping.value) return\r\n swiping.value = false\r\n if (Math.abs(offset.value) > 64 && props.toast.dismissible) {\r\n leaving.value = offset.value > 0 ? 1 : -1\r\n emit('dismiss', props.toast.id)\r\n } else {\r\n offset.value = 0\r\n }\r\n}\r\n\r\nconst style = computed(() => {\r\n if (!offset.value) return undefined\r\n return {\r\n '--j-toast-swipe': `${offset.value}px`,\r\n '--j-toast-swipe-opacity': String(Math.max(0, 1 - Math.abs(offset.value) / 200)),\r\n }\r\n})\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (event.key === 'Escape' && props.toast.dismissible) {\r\n event.preventDefault()\r\n event.stopPropagation()\r\n const next = (el.value?.nextElementSibling ?? el.value?.previousElementSibling) as HTMLElement | null\r\n emit('dismiss', props.toast.id)\r\n next?.focus()\r\n }\r\n}\r\n\r\nfunction runAction() {\r\n props.toast.action?.onClick()\r\n emit('dismiss', props.toast.id)\r\n}\r\n\r\nconst icons = {\r\n success: IconCircleCheck,\r\n error: IconCircleX,\r\n warning: IconAlert,\r\n info: IconInfo,\r\n}\r\n</script>\r\n\r\n<template>\r\n <li\r\n ref=\"el\"\r\n class=\"j-toast\"\r\n :class=\"[`j-toast--${toast.type}`, { 'is-swiping': swiping, 'is-swiped': leaving !== 0 }]\"\r\n :style=\"style\"\r\n :data-swipe-direction=\"leaving\"\r\n :role=\"toast.type === 'error' ? 'alert' : undefined\"\r\n tabindex=\"0\"\r\n @pointerdown=\"onPointerDown\"\r\n @pointermove=\"onPointerMove\"\r\n @pointerup=\"onPointerUp\"\r\n @pointercancel=\"onPointerUp\"\r\n @keydown=\"onKeydown\"\r\n >\r\n <span v-if=\"toast.type !== 'default'\" class=\"j-toast__icon\" aria-hidden=\"true\">\r\n <Transition name=\"j-toast-icon\" mode=\"out-in\">\r\n <Spinner v-if=\"toast.type === 'loading'\" :size=\"16\" />\r\n <component :is=\"icons[toast.type]\" v-else :key=\"toast.type\" />\r\n </Transition>\r\n </span>\r\n <div class=\"j-toast__body\">\r\n <p class=\"j-toast__title\">\r\n {{ toast.title }}\r\n </p>\r\n <p v-if=\"toast.description\" class=\"j-toast__description\">\r\n {{ toast.description }}\r\n </p>\r\n <div v-if=\"toast.action\" class=\"j-toast__actions\">\r\n <button type=\"button\" class=\"j-toast__action j-focusable\" @click=\"runAction\">\r\n {{ toast.action.label }}\r\n </button>\r\n </div>\r\n </div>\r\n <button\r\n v-if=\"toast.dismissible\"\r\n type=\"button\"\r\n class=\"j-toast__close j-focusable\"\r\n aria-label=\"Dismiss notification\"\r\n @click=\"emit('dismiss', toast.id)\"\r\n >\r\n <IconX />\r\n </button>\r\n <span\r\n v-if=\"timed\"\r\n :key=\"toast.version\"\r\n class=\"j-toast__progress\"\r\n :class=\"{ 'is-paused': paused }\"\r\n :style=\"{ animationDuration: `${toast.duration}ms` }\"\r\n aria-hidden=\"true\"\r\n />\r\n </li>\r\n</template>\r\n","<script setup lang=\"ts\">\r\n/** Internal: one toast. Owns its timer, swipe gesture and keyboard dismissal. */\r\nimport type { Toast } from '../../composables/useToast'\r\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\r\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX, Spinner } from '../../icons'\r\n\r\nconst props = defineProps<{\r\n toast: Toast\r\n paused: boolean\r\n}>()\r\n\r\nconst emit = defineEmits<{ dismiss: [id: string] }>()\r\n\r\nconst el = ref<HTMLElement | null>(null)\r\nconst timed = computed(() => Number.isFinite(props.toast.duration) && props.toast.duration > 0)\r\n\r\n// --- Timer -----------------------------------------------------------------\r\nlet timer: ReturnType<typeof setTimeout> | undefined\r\nlet remaining = 0\r\nlet startedAt = 0\r\n\r\nfunction start() {\r\n clearTimeout(timer)\r\n if (!timed.value || props.paused) return\r\n startedAt = Date.now()\r\n timer = setTimeout(() => emit('dismiss', props.toast.id), remaining)\r\n}\r\n\r\nfunction pause() {\r\n if (!timer) return\r\n clearTimeout(timer)\r\n timer = undefined\r\n remaining -= Date.now() - startedAt\r\n}\r\n\r\nfunction reset() {\r\n remaining = props.toast.duration\r\n start()\r\n}\r\n\r\nonMounted(reset)\r\nonBeforeUnmount(() => clearTimeout(timer))\r\nwatch(() => props.toast.version, reset)\r\nwatch(() => props.paused, value => (value ? pause() : start()))\r\n\r\n// --- Swipe to dismiss ------------------------------------------------------\r\nconst offset = ref(0)\r\nconst swiping = ref(false)\r\nconst leaving = ref<0 | 1 | -1>(0)\r\nlet origin: { x: number, y: number, id: number } | null = null\r\n\r\nfunction onPointerDown(event: PointerEvent) {\r\n if (event.button !== 0 || (event.target as HTMLElement).closest('button, a')) return\r\n origin = { x: event.clientX, y: event.clientY, id: event.pointerId }\r\n}\r\n\r\nfunction onPointerMove(event: PointerEvent) {\r\n if (!origin || event.pointerId !== origin.id) return\r\n const dx = event.clientX - origin.x\r\n if (!swiping.value) {\r\n // Commit to a swipe only once the gesture is clearly horizontal.\r\n if (Math.abs(dx) < 6 || Math.abs(dx) < Math.abs(event.clientY - origin.y)) return\r\n swiping.value = true\r\n el.value?.setPointerCapture(event.pointerId)\r\n }\r\n offset.value = dx\r\n}\r\n\r\nfunction onPointerUp() {\r\n if (!origin) return\r\n origin = null\r\n if (!swiping.value) return\r\n swiping.value = false\r\n if (Math.abs(offset.value) > 64 && props.toast.dismissible) {\r\n leaving.value = offset.value > 0 ? 1 : -1\r\n emit('dismiss', props.toast.id)\r\n } else {\r\n offset.value = 0\r\n }\r\n}\r\n\r\nconst style = computed(() => {\r\n if (!offset.value) return undefined\r\n return {\r\n '--j-toast-swipe': `${offset.value}px`,\r\n '--j-toast-swipe-opacity': String(Math.max(0, 1 - Math.abs(offset.value) / 200)),\r\n }\r\n})\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (event.key === 'Escape' && props.toast.dismissible) {\r\n event.preventDefault()\r\n event.stopPropagation()\r\n const next = (el.value?.nextElementSibling ?? el.value?.previousElementSibling) as HTMLElement | null\r\n emit('dismiss', props.toast.id)\r\n next?.focus()\r\n }\r\n}\r\n\r\nfunction runAction() {\r\n props.toast.action?.onClick()\r\n emit('dismiss', props.toast.id)\r\n}\r\n\r\nconst icons = {\r\n success: IconCircleCheck,\r\n error: IconCircleX,\r\n warning: IconAlert,\r\n info: IconInfo,\r\n}\r\n</script>\r\n\r\n<template>\r\n <li\r\n ref=\"el\"\r\n class=\"j-toast\"\r\n :class=\"[`j-toast--${toast.type}`, { 'is-swiping': swiping, 'is-swiped': leaving !== 0 }]\"\r\n :style=\"style\"\r\n :data-swipe-direction=\"leaving\"\r\n :role=\"toast.type === 'error' ? 'alert' : undefined\"\r\n tabindex=\"0\"\r\n @pointerdown=\"onPointerDown\"\r\n @pointermove=\"onPointerMove\"\r\n @pointerup=\"onPointerUp\"\r\n @pointercancel=\"onPointerUp\"\r\n @keydown=\"onKeydown\"\r\n >\r\n <span v-if=\"toast.type !== 'default'\" class=\"j-toast__icon\" aria-hidden=\"true\">\r\n <Transition name=\"j-toast-icon\" mode=\"out-in\">\r\n <Spinner v-if=\"toast.type === 'loading'\" :size=\"16\" />\r\n <component :is=\"icons[toast.type]\" v-else :key=\"toast.type\" />\r\n </Transition>\r\n </span>\r\n <div class=\"j-toast__body\">\r\n <p class=\"j-toast__title\">\r\n {{ toast.title }}\r\n </p>\r\n <p v-if=\"toast.description\" class=\"j-toast__description\">\r\n {{ toast.description }}\r\n </p>\r\n <div v-if=\"toast.action\" class=\"j-toast__actions\">\r\n <button type=\"button\" class=\"j-toast__action j-focusable\" @click=\"runAction\">\r\n {{ toast.action.label }}\r\n </button>\r\n </div>\r\n </div>\r\n <button\r\n v-if=\"toast.dismissible\"\r\n type=\"button\"\r\n class=\"j-toast__close j-focusable\"\r\n aria-label=\"Dismiss notification\"\r\n @click=\"emit('dismiss', toast.id)\"\r\n >\r\n <IconX />\r\n </button>\r\n <span\r\n v-if=\"timed\"\r\n :key=\"toast.version\"\r\n class=\"j-toast__progress\"\r\n :class=\"{ 'is-paused': paused }\"\r\n :style=\"{ animationDuration: `${toast.duration}ms` }\"\r\n aria-hidden=\"true\"\r\n />\r\n </li>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\r\nimport { formatHotkey, useHotkey } from '../../composables/useHotkey'\r\nimport { toastState } from '../../composables/useToast'\r\nimport { focusElement, unrefElement } from '../../utils/dom'\r\nimport JToastItem from './JToastItem.vue'\r\nimport JPortal from '../primitives/JPortal.vue'\r\n\r\nexport type ToasterPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'\r\n\r\nexport interface ToasterProps {\r\n position?: ToasterPosition\r\n /** How many toasts are visible at once. Extra toasts wait their turn. */\r\n max?: number\r\n /** Shortcut that moves focus to the newest toast. */\r\n hotkey?: string\r\n /** Accessible name for the notification region. */\r\n label?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<ToasterProps>(), {\r\n position: 'bottom-right',\r\n max: 4,\r\n hotkey: 'alt+t',\r\n label: 'Notifications',\r\n})\r\n\r\nconst list = ref()\r\nconst hovered = ref(false)\r\nconst focused = ref(false)\r\nconst hidden = ref(false)\r\nconst paused = computed(() => hovered.value || focused.value || hidden.value)\r\n\r\nconst isTop = computed(() => props.position.startsWith('top'))\r\n\r\n// Oldest first in the store; show the newest `max`, nearest the screen edge.\r\nconst visible = computed(() => {\r\n const items = toastState.toasts.slice(-props.max)\r\n return isTop.value ? items.reverse() : items\r\n})\r\n\r\nfunction dismiss(id: string) {\r\n const index = toastState.toasts.findIndex(t => t.id === id)\r\n if (index === -1) return\r\n const [removed] = toastState.toasts.splice(index, 1)\r\n removed?.onDismiss?.()\r\n}\r\n\r\nconst hotkeyLabel = ref('')\r\nonMounted(() => (hotkeyLabel.value = formatHotkey(props.hotkey).join('+')))\r\n\r\nuseHotkey(() => props.hotkey, () => {\r\n const items = unrefElement(list.value)?.querySelectorAll<HTMLElement>('.j-toast')\r\n if (!items?.length) return\r\n focusElement(isTop.value ? items[0] : items[items.length - 1])\r\n})\r\n\r\nfunction onVisibility() {\r\n hidden.value = document.visibilityState === 'hidden'\r\n}\r\nonMounted(() => document.addEventListener('visibilitychange', onVisibility))\r\nonBeforeUnmount(() => document.removeEventListener('visibilitychange', onVisibility))\r\n\r\nfunction onFocusOut(event: FocusEvent) {\r\n const current = event.currentTarget as HTMLElement\r\n if (!current.contains(event.relatedTarget as Node | null)) focused.value = false\r\n}\r\n</script>\r\n\r\n<template>\r\n <JPortal>\r\n <section\r\n class=\"j-toaster\"\r\n :class=\"`j-toaster--${position}`\"\r\n :aria-label=\"hotkeyLabel ? `${label} (${hotkeyLabel})` : label\"\r\n data-juxt-layer-ignore\r\n >\r\n <TransitionGroup\r\n ref=\"list\"\r\n tag=\"ol\"\r\n name=\"j-toast\"\r\n class=\"j-toaster__list\"\r\n aria-live=\"polite\"\r\n aria-relevant=\"additions text\"\r\n @pointerenter=\"hovered = true\"\r\n @pointerleave=\"hovered = false\"\r\n @focusin=\"focused = true\"\r\n @focusout=\"onFocusOut\"\r\n >\r\n <JToastItem\r\n v-for=\"item in visible\"\r\n :key=\"item.id\"\r\n :toast=\"item\"\r\n :paused=\"paused\"\r\n @dismiss=\"dismiss\"\r\n />\r\n </TransitionGroup>\r\n </section>\r\n </JPortal>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-toaster {\r\n --j-toast-width: 22.5rem;\r\n --j-toast-edge: var(--juxt-space-4);\r\n\r\n position: fixed;\r\n z-index: var(--juxt-z-toast);\r\n width: var(--j-toast-width);\r\n max-width: calc(100vw - var(--j-toast-edge) * 2);\r\n pointer-events: none;\r\n}\r\n\r\n.j-toaster--top-left,\r\n.j-toaster--top-center,\r\n.j-toaster--top-right {\r\n top: max(var(--j-toast-edge), env(safe-area-inset-top));\r\n}\r\n\r\n.j-toaster--bottom-left,\r\n.j-toaster--bottom-center,\r\n.j-toaster--bottom-right {\r\n bottom: max(var(--j-toast-edge), env(safe-area-inset-bottom));\r\n}\r\n\r\n.j-toaster--top-left,\r\n.j-toaster--bottom-left {\r\n left: var(--j-toast-edge);\r\n}\r\n\r\n.j-toaster--top-right,\r\n.j-toaster--bottom-right {\r\n right: calc(var(--j-toast-edge) + var(--juxt-scrollbar-width, 0px));\r\n}\r\n\r\n.j-toaster--top-center,\r\n.j-toaster--bottom-center {\r\n left: 50%;\r\n transform: translateX(-50%);\r\n}\r\n\r\n.j-toaster__list {\r\n position: relative;\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-2);\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n.j-toast {\r\n --j-toast-enter: calc(var(--juxt-motion-shift) * 3);\r\n\r\n position: relative;\r\n display: flex;\r\n align-items: flex-start;\r\n gap: var(--juxt-space-2-5);\r\n box-sizing: border-box;\r\n width: 100%;\r\n padding: var(--juxt-space-3) var(--juxt-space-10) var(--juxt-space-3) var(--juxt-space-3);\r\n overflow: hidden;\r\n border: 1px solid var(--juxt-border);\r\n border-radius: var(--juxt-radius-md);\r\n background: var(--juxt-surface-raised);\r\n box-shadow: var(--juxt-shadow-md);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n pointer-events: auto;\r\n opacity: var(--j-toast-swipe-opacity, 1);\r\n transform: translateX(var(--j-toast-swipe, 0));\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n touch-action: pan-y;\r\n transition:\r\n transform var(--juxt-duration-slow) var(--juxt-ease-out),\r\n opacity var(--juxt-duration-normal) var(--juxt-ease-out),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-toaster--top-left .j-toast,\r\n.j-toaster--top-center .j-toast,\r\n.j-toaster--top-right .j-toast {\r\n --j-toast-enter: calc(var(--juxt-motion-shift) * -3);\r\n}\r\n\r\n.j-toast.is-swiping {\r\n transition: none;\r\n user-select: none;\r\n}\r\n\r\n.j-toast:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.j-toast__icon {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n width: 1rem;\r\n height: 1.25rem;\r\n}\r\n\r\n.j-toast__icon > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-toast--success .j-toast__icon {\r\n color: var(--juxt-success-text);\r\n}\r\n\r\n.j-toast--error .j-toast__icon {\r\n color: var(--juxt-danger-text);\r\n}\r\n\r\n.j-toast--warning .j-toast__icon {\r\n color: var(--juxt-warning-text);\r\n}\r\n\r\n.j-toast--info .j-toast__icon {\r\n color: var(--juxt-info-text);\r\n}\r\n\r\n.j-toast--loading .j-toast__icon {\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-toast__body {\r\n display: flex;\r\n flex: 1;\r\n flex-direction: column;\r\n gap: var(--juxt-space-0-5);\r\n min-width: 0;\r\n}\r\n\r\n.j-toast__title {\r\n margin: 0;\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: 1.25rem;\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-toast__description {\r\n margin: 0;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-toast__actions {\r\n display: flex;\r\n gap: var(--juxt-space-2);\r\n margin-top: var(--juxt-space-2);\r\n}\r\n\r\n.j-toast__action {\r\n height: var(--juxt-control-xs);\r\n padding: 0 var(--juxt-space-2);\r\n border: 1px solid var(--juxt-border);\r\n border-radius: var(--juxt-radius-xs);\r\n background: var(--juxt-surface);\r\n color: var(--juxt-fg);\r\n font: inherit;\r\n font-size: var(--juxt-text-xs);\r\n font-weight: var(--juxt-weight-medium);\r\n cursor: pointer;\r\n transition-property: background-color, border-color, outline-color, outline-offset;\r\n}\r\n\r\n.j-toast__action:hover {\r\n border-color: var(--juxt-border-strong);\r\n background: var(--juxt-surface-hover);\r\n}\r\n\r\n.j-toast__close {\r\n position: absolute;\r\n top: var(--juxt-space-2-5);\r\n right: var(--juxt-space-2-5);\r\n display: grid;\r\n place-items: center;\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-xs);\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n cursor: pointer;\r\n transition-property: background-color, color, outline-color, outline-offset;\r\n}\r\n\r\n.j-toast__close:hover {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-toast__close > svg {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n\r\n/* Remaining time, drawn as a hairline that drains toward the start. */\r\n.j-toast__progress {\r\n position: absolute;\r\n right: 0;\r\n bottom: 0;\r\n left: 0;\r\n height: 2px;\r\n background: var(--juxt-border-strong);\r\n opacity: 0.6;\r\n transform-origin: left;\r\n animation: j-toast-progress linear forwards;\r\n}\r\n\r\n.j-toast--success .j-toast__progress {\r\n background: var(--juxt-accent-border);\r\n}\r\n\r\n.j-toast__progress.is-paused {\r\n animation-play-state: paused;\r\n}\r\n\r\n@keyframes j-toast-progress {\r\n from {\r\n transform: scaleX(1);\r\n }\r\n\r\n to {\r\n transform: scaleX(0);\r\n }\r\n}\r\n\r\n/* List motion */\r\n.j-toast-enter-from {\r\n opacity: 0;\r\n transform: translateY(var(--j-toast-enter)) scale(0.98);\r\n}\r\n\r\n.j-toast-leave-active {\r\n position: absolute;\r\n width: 100%;\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-in),\r\n transform var(--juxt-duration-normal) var(--juxt-ease-in);\r\n}\r\n\r\n.j-toast-leave-to {\r\n opacity: 0;\r\n transform: scale(0.96);\r\n}\r\n\r\n.j-toast-leave-to.is-swiped {\r\n transform: translateX(calc(var(--j-toast-width) * var(--j-swipe-sign, 1)));\r\n}\r\n\r\n.j-toast-leave-to.is-swiped[data-swipe-direction='-1'] {\r\n --j-swipe-sign: -1;\r\n}\r\n\r\n.j-toast-move {\r\n transition: transform var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n.j-toast-icon-enter-active,\r\n.j-toast-icon-leave-active {\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-toast-icon-enter-from,\r\n.j-toast-icon-leave-to {\r\n opacity: 0;\r\n transform: scale(0.6);\r\n}\r\n\r\n@media (max-width: 639px) {\r\n .j-toaster {\r\n --j-toast-edge: var(--juxt-space-2);\r\n\r\n right: var(--j-toast-edge);\r\n left: var(--j-toast-edge);\r\n width: auto;\r\n max-width: none;\r\n transform: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\r\nimport { formatHotkey, useHotkey } from '../../composables/useHotkey'\r\nimport { toastState } from '../../composables/useToast'\r\nimport { focusElement, unrefElement } from '../../utils/dom'\r\nimport JToastItem from './JToastItem.vue'\r\nimport JPortal from '../primitives/JPortal.vue'\r\n\r\nexport type ToasterPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'\r\n\r\nexport interface ToasterProps {\r\n position?: ToasterPosition\r\n /** How many toasts are visible at once. Extra toasts wait their turn. */\r\n max?: number\r\n /** Shortcut that moves focus to the newest toast. */\r\n hotkey?: string\r\n /** Accessible name for the notification region. */\r\n label?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<ToasterProps>(), {\r\n position: 'bottom-right',\r\n max: 4,\r\n hotkey: 'alt+t',\r\n label: 'Notifications',\r\n})\r\n\r\nconst list = ref()\r\nconst hovered = ref(false)\r\nconst focused = ref(false)\r\nconst hidden = ref(false)\r\nconst paused = computed(() => hovered.value || focused.value || hidden.value)\r\n\r\nconst isTop = computed(() => props.position.startsWith('top'))\r\n\r\n// Oldest first in the store; show the newest `max`, nearest the screen edge.\r\nconst visible = computed(() => {\r\n const items = toastState.toasts.slice(-props.max)\r\n return isTop.value ? items.reverse() : items\r\n})\r\n\r\nfunction dismiss(id: string) {\r\n const index = toastState.toasts.findIndex(t => t.id === id)\r\n if (index === -1) return\r\n const [removed] = toastState.toasts.splice(index, 1)\r\n removed?.onDismiss?.()\r\n}\r\n\r\nconst hotkeyLabel = ref('')\r\nonMounted(() => (hotkeyLabel.value = formatHotkey(props.hotkey).join('+')))\r\n\r\nuseHotkey(() => props.hotkey, () => {\r\n const items = unrefElement(list.value)?.querySelectorAll<HTMLElement>('.j-toast')\r\n if (!items?.length) return\r\n focusElement(isTop.value ? items[0] : items[items.length - 1])\r\n})\r\n\r\nfunction onVisibility() {\r\n hidden.value = document.visibilityState === 'hidden'\r\n}\r\nonMounted(() => document.addEventListener('visibilitychange', onVisibility))\r\nonBeforeUnmount(() => document.removeEventListener('visibilitychange', onVisibility))\r\n\r\nfunction onFocusOut(event: FocusEvent) {\r\n const current = event.currentTarget as HTMLElement\r\n if (!current.contains(event.relatedTarget as Node | null)) focused.value = false\r\n}\r\n</script>\r\n\r\n<template>\r\n <JPortal>\r\n <section\r\n class=\"j-toaster\"\r\n :class=\"`j-toaster--${position}`\"\r\n :aria-label=\"hotkeyLabel ? `${label} (${hotkeyLabel})` : label\"\r\n data-juxt-layer-ignore\r\n >\r\n <TransitionGroup\r\n ref=\"list\"\r\n tag=\"ol\"\r\n name=\"j-toast\"\r\n class=\"j-toaster__list\"\r\n aria-live=\"polite\"\r\n aria-relevant=\"additions text\"\r\n @pointerenter=\"hovered = true\"\r\n @pointerleave=\"hovered = false\"\r\n @focusin=\"focused = true\"\r\n @focusout=\"onFocusOut\"\r\n >\r\n <JToastItem\r\n v-for=\"item in visible\"\r\n :key=\"item.id\"\r\n :toast=\"item\"\r\n :paused=\"paused\"\r\n @dismiss=\"dismiss\"\r\n />\r\n </TransitionGroup>\r\n </section>\r\n </JPortal>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-toaster {\r\n --j-toast-width: 22.5rem;\r\n --j-toast-edge: var(--juxt-space-4);\r\n\r\n position: fixed;\r\n z-index: var(--juxt-z-toast);\r\n width: var(--j-toast-width);\r\n max-width: calc(100vw - var(--j-toast-edge) * 2);\r\n pointer-events: none;\r\n}\r\n\r\n.j-toaster--top-left,\r\n.j-toaster--top-center,\r\n.j-toaster--top-right {\r\n top: max(var(--j-toast-edge), env(safe-area-inset-top));\r\n}\r\n\r\n.j-toaster--bottom-left,\r\n.j-toaster--bottom-center,\r\n.j-toaster--bottom-right {\r\n bottom: max(var(--j-toast-edge), env(safe-area-inset-bottom));\r\n}\r\n\r\n.j-toaster--top-left,\r\n.j-toaster--bottom-left {\r\n left: var(--j-toast-edge);\r\n}\r\n\r\n.j-toaster--top-right,\r\n.j-toaster--bottom-right {\r\n right: calc(var(--j-toast-edge) + var(--juxt-scrollbar-width, 0px));\r\n}\r\n\r\n.j-toaster--top-center,\r\n.j-toaster--bottom-center {\r\n left: 50%;\r\n transform: translateX(-50%);\r\n}\r\n\r\n.j-toaster__list {\r\n position: relative;\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--juxt-space-2);\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n.j-toast {\r\n --j-toast-enter: calc(var(--juxt-motion-shift) * 3);\r\n\r\n position: relative;\r\n display: flex;\r\n align-items: flex-start;\r\n gap: var(--juxt-space-2-5);\r\n box-sizing: border-box;\r\n width: 100%;\r\n padding: var(--juxt-space-3) var(--juxt-space-10) var(--juxt-space-3) var(--juxt-space-3);\r\n overflow: hidden;\r\n border: 1px solid var(--juxt-border);\r\n border-radius: var(--juxt-radius-md);\r\n background: var(--juxt-surface-raised);\r\n box-shadow: var(--juxt-shadow-md);\r\n color: var(--juxt-fg);\r\n font-family: var(--juxt-font-sans);\r\n pointer-events: auto;\r\n opacity: var(--j-toast-swipe-opacity, 1);\r\n transform: translateX(var(--j-toast-swipe, 0));\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n touch-action: pan-y;\r\n transition:\r\n transform var(--juxt-duration-slow) var(--juxt-ease-out),\r\n opacity var(--juxt-duration-normal) var(--juxt-ease-out),\r\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\r\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-toaster--top-left .j-toast,\r\n.j-toaster--top-center .j-toast,\r\n.j-toaster--top-right .j-toast {\r\n --j-toast-enter: calc(var(--juxt-motion-shift) * -3);\r\n}\r\n\r\n.j-toast.is-swiping {\r\n transition: none;\r\n user-select: none;\r\n}\r\n\r\n.j-toast:focus-visible {\r\n outline-color: var(--juxt-ring);\r\n outline-offset: 2px;\r\n}\r\n\r\n.j-toast__icon {\r\n display: grid;\r\n flex: none;\r\n place-items: center;\r\n width: 1rem;\r\n height: 1.25rem;\r\n}\r\n\r\n.j-toast__icon > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-toast--success .j-toast__icon {\r\n color: var(--juxt-success-text);\r\n}\r\n\r\n.j-toast--error .j-toast__icon {\r\n color: var(--juxt-danger-text);\r\n}\r\n\r\n.j-toast--warning .j-toast__icon {\r\n color: var(--juxt-warning-text);\r\n}\r\n\r\n.j-toast--info .j-toast__icon {\r\n color: var(--juxt-info-text);\r\n}\r\n\r\n.j-toast--loading .j-toast__icon {\r\n color: var(--juxt-fg-muted);\r\n}\r\n\r\n.j-toast__body {\r\n display: flex;\r\n flex: 1;\r\n flex-direction: column;\r\n gap: var(--juxt-space-0-5);\r\n min-width: 0;\r\n}\r\n\r\n.j-toast__title {\r\n margin: 0;\r\n font-size: var(--juxt-text-md);\r\n font-weight: var(--juxt-weight-medium);\r\n line-height: 1.25rem;\r\n letter-spacing: var(--juxt-tracking-tight);\r\n}\r\n\r\n.j-toast__description {\r\n margin: 0;\r\n color: var(--juxt-fg-secondary);\r\n font-size: var(--juxt-text-sm);\r\n line-height: var(--juxt-leading-normal);\r\n}\r\n\r\n.j-toast__actions {\r\n display: flex;\r\n gap: var(--juxt-space-2);\r\n margin-top: var(--juxt-space-2);\r\n}\r\n\r\n.j-toast__action {\r\n height: var(--juxt-control-xs);\r\n padding: 0 var(--juxt-space-2);\r\n border: 1px solid var(--juxt-border);\r\n border-radius: var(--juxt-radius-xs);\r\n background: var(--juxt-surface);\r\n color: var(--juxt-fg);\r\n font: inherit;\r\n font-size: var(--juxt-text-xs);\r\n font-weight: var(--juxt-weight-medium);\r\n cursor: pointer;\r\n transition-property: background-color, border-color, outline-color, outline-offset;\r\n}\r\n\r\n.j-toast__action:hover {\r\n border-color: var(--juxt-border-strong);\r\n background: var(--juxt-surface-hover);\r\n}\r\n\r\n.j-toast__close {\r\n position: absolute;\r\n top: var(--juxt-space-2-5);\r\n right: var(--juxt-space-2-5);\r\n display: grid;\r\n place-items: center;\r\n width: 1.5rem;\r\n height: 1.5rem;\r\n padding: 0;\r\n border: 0;\r\n border-radius: var(--juxt-radius-xs);\r\n background: transparent;\r\n color: var(--juxt-fg-muted);\r\n cursor: pointer;\r\n transition-property: background-color, color, outline-color, outline-offset;\r\n}\r\n\r\n.j-toast__close:hover {\r\n background: var(--juxt-surface-hover);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-toast__close > svg {\r\n width: 0.875rem;\r\n height: 0.875rem;\r\n}\r\n\r\n/* Remaining time, drawn as a hairline that drains toward the start. */\r\n.j-toast__progress {\r\n position: absolute;\r\n right: 0;\r\n bottom: 0;\r\n left: 0;\r\n height: 2px;\r\n background: var(--juxt-border-strong);\r\n opacity: 0.6;\r\n transform-origin: left;\r\n animation: j-toast-progress linear forwards;\r\n}\r\n\r\n.j-toast--success .j-toast__progress {\r\n background: var(--juxt-accent-border);\r\n}\r\n\r\n.j-toast__progress.is-paused {\r\n animation-play-state: paused;\r\n}\r\n\r\n@keyframes j-toast-progress {\r\n from {\r\n transform: scaleX(1);\r\n }\r\n\r\n to {\r\n transform: scaleX(0);\r\n }\r\n}\r\n\r\n/* List motion */\r\n.j-toast-enter-from {\r\n opacity: 0;\r\n transform: translateY(var(--j-toast-enter)) scale(0.98);\r\n}\r\n\r\n.j-toast-leave-active {\r\n position: absolute;\r\n width: 100%;\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-in),\r\n transform var(--juxt-duration-normal) var(--juxt-ease-in);\r\n}\r\n\r\n.j-toast-leave-to {\r\n opacity: 0;\r\n transform: scale(0.96);\r\n}\r\n\r\n.j-toast-leave-to.is-swiped {\r\n transform: translateX(calc(var(--j-toast-width) * var(--j-swipe-sign, 1)));\r\n}\r\n\r\n.j-toast-leave-to.is-swiped[data-swipe-direction='-1'] {\r\n --j-swipe-sign: -1;\r\n}\r\n\r\n.j-toast-move {\r\n transition: transform var(--juxt-duration-slow) var(--juxt-ease-out);\r\n}\r\n\r\n.j-toast-icon-enter-active,\r\n.j-toast-icon-leave-active {\r\n transition:\r\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\r\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\r\n}\r\n\r\n.j-toast-icon-enter-from,\r\n.j-toast-icon-leave-to {\r\n opacity: 0;\r\n transform: scale(0.6);\r\n}\r\n\r\n@media (max-width: 639px) {\r\n .j-toaster {\r\n --j-toast-edge: var(--juxt-space-2);\r\n\r\n right: var(--j-toast-edge);\r\n left: var(--j-toast-edge);\r\n width: auto;\r\n max-width: none;\r\n transform: none;\r\n }\r\n}\r\n}\r\n</style>\r\n","import type { ComputedRef, InjectionKey } from 'vue'\r\n\r\nexport type ToggleGroupType = 'single' | 'multiple'\r\n\r\nexport interface ToggleGroupContext {\r\n type: ComputedRef<ToggleGroupType>\r\n disabled: ComputedRef<boolean>\r\n isSelected: (value: string) => boolean\r\n toggle: (value: string) => void\r\n}\r\n\r\nexport const TOGGLE_GROUP: InjectionKey<ToggleGroupContext> = Symbol('JToggleGroup')\r\n","<script setup lang=\"ts\">\r\nimport { computed, provide } from 'vue'\r\nimport { focusElement } from '../../utils/dom'\r\nimport { TOGGLE_GROUP } from './context'\r\n\r\nexport interface ToggleGroupProps {\r\n /** `single` keeps at most one item pressed; `multiple` lets any number stay pressed. */\r\n type?: 'single' | 'multiple'\r\n size?: 'sm' | 'md'\r\n disabled?: boolean\r\n /** Accessible name for the group. */\r\n label?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<ToggleGroupProps>(), {\r\n type: 'single',\r\n size: 'md',\r\n})\r\n\r\n/** The pressed value in `single` mode, or an array of values in `multiple` mode. */\r\nconst model = defineModel<string | string[] | null>({ default: null })\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n\r\nprovide(TOGGLE_GROUP, {\r\n type: computed(() => props.type),\r\n disabled: computed(() => !!props.disabled),\r\n isSelected: (value) => {\r\n if (props.type === 'multiple') return Array.isArray(model.value) && model.value.includes(value)\r\n return model.value === value\r\n },\r\n toggle: (value) => {\r\n if (props.disabled) return\r\n if (props.type === 'multiple') {\r\n const current = Array.isArray(model.value) ? model.value : []\r\n model.value = current.includes(value) ? current.filter(v => v !== value) : [...current, value]\r\n return\r\n }\r\n model.value = value\r\n },\r\n})\r\n\r\n// Arrow keys move between items, per the WAI-ARIA toolbar pattern.\r\nfunction onKeydown(event: KeyboardEvent) {\r\n const target = event.target as HTMLElement\r\n if (!target.classList.contains('j-toggle-group__item')) return\r\n const root = event.currentTarget as HTMLElement\r\n const items = Array.from(root.querySelectorAll<HTMLElement>('.j-toggle-group__item:not(:disabled)'))\r\n const index = items.indexOf(target)\r\n let next: HTMLElement | undefined\r\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') next = items[(index + 1) % items.length]\r\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') next = items[(index - 1 + items.length) % items.length]\r\n else if (event.key === 'Home') next = items[0]\r\n else if (event.key === 'End') next = items[items.length - 1]\r\n if (!next) return\r\n event.preventDefault()\r\n focusElement(next)\r\n}\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-toggle-group\"\r\n :class=\"[`j-toggle-group--${size}`, { 'is-disabled': disabled }]\"\r\n role=\"group\"\r\n :aria-label=\"label\"\r\n @keydown=\"onKeydown\"\r\n >\r\n <slot />\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-toggle-group {\r\n --j-toggle-height: var(--juxt-control-md);\r\n\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 2px;\r\n padding: 2px;\r\n border-radius: var(--juxt-radius-md);\r\n background: var(--juxt-surface-sunken);\r\n box-shadow: inset 0 0 0 1px var(--juxt-border-subtle);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-toggle-group--sm {\r\n --j-toggle-height: var(--juxt-control-sm);\r\n}\r\n\r\n.j-toggle-group.is-disabled {\r\n opacity: 0.6;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, provide } from 'vue'\r\nimport { focusElement } from '../../utils/dom'\r\nimport { TOGGLE_GROUP } from './context'\r\n\r\nexport interface ToggleGroupProps {\r\n /** `single` keeps at most one item pressed; `multiple` lets any number stay pressed. */\r\n type?: 'single' | 'multiple'\r\n size?: 'sm' | 'md'\r\n disabled?: boolean\r\n /** Accessible name for the group. */\r\n label?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<ToggleGroupProps>(), {\r\n type: 'single',\r\n size: 'md',\r\n})\r\n\r\n/** The pressed value in `single` mode, or an array of values in `multiple` mode. */\r\nconst model = defineModel<string | string[] | null>({ default: null })\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n\r\nprovide(TOGGLE_GROUP, {\r\n type: computed(() => props.type),\r\n disabled: computed(() => !!props.disabled),\r\n isSelected: (value) => {\r\n if (props.type === 'multiple') return Array.isArray(model.value) && model.value.includes(value)\r\n return model.value === value\r\n },\r\n toggle: (value) => {\r\n if (props.disabled) return\r\n if (props.type === 'multiple') {\r\n const current = Array.isArray(model.value) ? model.value : []\r\n model.value = current.includes(value) ? current.filter(v => v !== value) : [...current, value]\r\n return\r\n }\r\n model.value = value\r\n },\r\n})\r\n\r\n// Arrow keys move between items, per the WAI-ARIA toolbar pattern.\r\nfunction onKeydown(event: KeyboardEvent) {\r\n const target = event.target as HTMLElement\r\n if (!target.classList.contains('j-toggle-group__item')) return\r\n const root = event.currentTarget as HTMLElement\r\n const items = Array.from(root.querySelectorAll<HTMLElement>('.j-toggle-group__item:not(:disabled)'))\r\n const index = items.indexOf(target)\r\n let next: HTMLElement | undefined\r\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') next = items[(index + 1) % items.length]\r\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') next = items[(index - 1 + items.length) % items.length]\r\n else if (event.key === 'Home') next = items[0]\r\n else if (event.key === 'End') next = items[items.length - 1]\r\n if (!next) return\r\n event.preventDefault()\r\n focusElement(next)\r\n}\r\n</script>\r\n\r\n<template>\r\n <div\r\n class=\"j-toggle-group\"\r\n :class=\"[`j-toggle-group--${size}`, { 'is-disabled': disabled }]\"\r\n role=\"group\"\r\n :aria-label=\"label\"\r\n @keydown=\"onKeydown\"\r\n >\r\n <slot />\r\n </div>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-toggle-group {\r\n --j-toggle-height: var(--juxt-control-md);\r\n\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 2px;\r\n padding: 2px;\r\n border-radius: var(--juxt-radius-md);\r\n background: var(--juxt-surface-sunken);\r\n box-shadow: inset 0 0 0 1px var(--juxt-border-subtle);\r\n font-family: var(--juxt-font-sans);\r\n}\r\n\r\n.j-toggle-group--sm {\r\n --j-toggle-height: var(--juxt-control-sm);\r\n}\r\n\r\n.j-toggle-group.is-disabled {\r\n opacity: 0.6;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, inject } from 'vue'\r\nimport { TOGGLE_GROUP } from './context'\r\n\r\nexport interface ToggleGroupItemProps {\r\n /** Identifies the item in the group's `v-model`. */\r\n value: string\r\n disabled?: boolean\r\n /** Accessible name, when the slot content isn't descriptive enough (e.g. an icon-only item). */\r\n label?: string\r\n}\r\n\r\nconst props = defineProps<ToggleGroupItemProps>()\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n\r\nconst group = inject(TOGGLE_GROUP)!\r\nconst selected = computed(() => group.isSelected(props.value))\r\nconst isDisabled = computed(() => props.disabled || group.disabled.value)\r\n</script>\r\n\r\n<template>\r\n <button\r\n type=\"button\"\r\n class=\"j-toggle-group__item j-focusable\"\r\n :class=\"{ 'is-selected': selected }\"\r\n :aria-pressed=\"selected\"\r\n :aria-label=\"label\"\r\n :disabled=\"isDisabled\"\r\n @click=\"group.toggle(value)\"\r\n >\r\n <slot />\r\n </button>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-toggle-group__item {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: var(--juxt-space-1-5);\r\n height: var(--j-toggle-height, var(--juxt-control-md));\r\n padding: 0 var(--juxt-space-3);\r\n border: 0;\r\n border-radius: var(--juxt-radius-sm);\r\n background: transparent;\r\n color: var(--juxt-fg-secondary);\r\n font: inherit;\r\n font-size: var(--juxt-text-sm);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n white-space: nowrap;\r\n cursor: pointer;\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n -webkit-tap-highlight-color: transparent;\r\n transition:\r\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n box-shadow var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-toggle-group__item > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-toggle-group__item:hover:not(:disabled) {\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-toggle-group__item.is-selected {\r\n background: var(--juxt-surface);\r\n box-shadow: var(--juxt-shadow-sm);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.dark .j-toggle-group__item.is-selected,\r\n[data-theme='dark'] .j-toggle-group__item.is-selected {\r\n background: var(--juxt-surface-active);\r\n}\r\n\r\n.j-toggle-group__item:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport { computed, inject } from 'vue'\r\nimport { TOGGLE_GROUP } from './context'\r\n\r\nexport interface ToggleGroupItemProps {\r\n /** Identifies the item in the group's `v-model`. */\r\n value: string\r\n disabled?: boolean\r\n /** Accessible name, when the slot content isn't descriptive enough (e.g. an icon-only item). */\r\n label?: string\r\n}\r\n\r\nconst props = defineProps<ToggleGroupItemProps>()\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n\r\nconst group = inject(TOGGLE_GROUP)!\r\nconst selected = computed(() => group.isSelected(props.value))\r\nconst isDisabled = computed(() => props.disabled || group.disabled.value)\r\n</script>\r\n\r\n<template>\r\n <button\r\n type=\"button\"\r\n class=\"j-toggle-group__item j-focusable\"\r\n :class=\"{ 'is-selected': selected }\"\r\n :aria-pressed=\"selected\"\r\n :aria-label=\"label\"\r\n :disabled=\"isDisabled\"\r\n @click=\"group.toggle(value)\"\r\n >\r\n <slot />\r\n </button>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n.j-toggle-group__item {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: var(--juxt-space-1-5);\r\n height: var(--j-toggle-height, var(--juxt-control-md));\r\n padding: 0 var(--juxt-space-3);\r\n border: 0;\r\n border-radius: var(--juxt-radius-sm);\r\n background: transparent;\r\n color: var(--juxt-fg-secondary);\r\n font: inherit;\r\n font-size: var(--juxt-text-sm);\r\n font-weight: var(--juxt-weight-medium);\r\n letter-spacing: var(--juxt-tracking-tight);\r\n white-space: nowrap;\r\n cursor: pointer;\r\n outline: 2px solid transparent;\r\n outline-offset: 0;\r\n -webkit-tap-highlight-color: transparent;\r\n transition:\r\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\r\n box-shadow var(--juxt-duration-fast) var(--juxt-ease-standard);\r\n}\r\n\r\n.j-toggle-group__item > svg {\r\n width: 1rem;\r\n height: 1rem;\r\n}\r\n\r\n.j-toggle-group__item:hover:not(:disabled) {\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.j-toggle-group__item.is-selected {\r\n background: var(--juxt-surface);\r\n box-shadow: var(--juxt-shadow-sm);\r\n color: var(--juxt-fg);\r\n}\r\n\r\n.dark .j-toggle-group__item.is-selected,\r\n[data-theme='dark'] .j-toggle-group__item.is-selected {\r\n background: var(--juxt-surface-active);\r\n}\r\n\r\n.j-toggle-group__item:disabled {\r\n color: var(--juxt-fg-disabled);\r\n cursor: not-allowed;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { Component } from 'vue'\r\n\r\nexport interface VisuallyHiddenProps {\r\n /** Render as another element or component, e.g. `label` or `h2`. */\r\n as?: string | Component\r\n}\r\n\r\nwithDefaults(defineProps<VisuallyHiddenProps>(), { as: 'span' })\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n</script>\r\n\r\n<template>\r\n <component :is=\"as\" class=\"j-visually-hidden j-sr-only\">\r\n <slot />\r\n </component>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n/*\r\n * Visual hiding is handled entirely by the shared `.j-sr-only` utility in\r\n * base.css | this component just gives it a typed, composable wrapper so it\r\n * can be used as `<JVisuallyHidden as=\"label\">…</JVisuallyHidden>`.\r\n */\r\n.j-visually-hidden {\r\n display: block;\r\n}\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\nimport type { Component } from 'vue'\r\n\r\nexport interface VisuallyHiddenProps {\r\n /** Render as another element or component, e.g. `label` or `h2`. */\r\n as?: string | Component\r\n}\r\n\r\nwithDefaults(defineProps<VisuallyHiddenProps>(), { as: 'span' })\r\n\r\ndefineSlots<{ default?: () => unknown }>()\r\n</script>\r\n\r\n<template>\r\n <component :is=\"as\" class=\"j-visually-hidden j-sr-only\">\r\n <slot />\r\n </component>\r\n</template>\r\n\r\n<style>\r\n@layer theme, base, juxt, components, utilities;\r\n@layer juxt {\r\n/*\r\n * Visual hiding is handled entirely by the shared `.j-sr-only` utility in\r\n * base.css | this component just gives it a typed, composable wrapper so it\r\n * can be used as `<JVisuallyHidden as=\"label\">…</JVisuallyHidden>`.\r\n */\r\n.j-visually-hidden {\r\n display: block;\r\n}\r\n}\r\n</style>\r\n","export { default as JAccordion } from './components/accordion/JAccordion.vue'\nexport { default as JAccordionItem } from './components/accordion/JAccordionItem.vue'\nexport { default as JAlert } from './components/alert/JAlert.vue'\nexport { default as JAspectRatio } from './components/aspect-ratio/JAspectRatio.vue'\nexport { default as JAvatar } from './components/avatar/JAvatar.vue'\nexport { default as JAvatarGroup } from './components/avatar-group/JAvatarGroup.vue'\nexport { default as JBadge } from './components/badge/JBadge.vue'\nexport { default as JBanner } from './components/banner/JBanner.vue'\nexport { default as JBreadcrumb } from './components/breadcrumb/JBreadcrumb.vue'\nexport { default as JBurger } from './components/burger/JBurger.vue'\nexport { default as JButton } from './components/button/JButton.vue'\nexport { default as JCard } from './components/card/JCard.vue'\nexport { default as JCheckbox } from './components/checkbox/JCheckbox.vue'\nexport { default as JCollapsible } from './components/collapsible/JCollapsible.vue'\nexport { default as JCommandPalette } from './components/command-palette/JCommandPalette.vue'\nexport { default as JDialog } from './components/dialog/JDialog.vue'\nexport { default as JDropdownMenu } from './components/dropdown-menu/JDropdownMenu.vue'\nexport { default as JDropdownMenuItem } from './components/dropdown-menu/JDropdownMenuItem.vue'\nexport { default as JDropdownMenuLabel } from './components/dropdown-menu/JDropdownMenuLabel.vue'\nexport { default as JDropdownMenuSeparator } from './components/dropdown-menu/JDropdownMenuSeparator.vue'\nexport { default as JDropdownMenuSub } from './components/dropdown-menu/JDropdownMenuSub.vue'\nexport { default as JEmptyState } from './components/empty-state/JEmptyState.vue'\nexport { default as JFooter } from './components/footer/JFooter.vue'\nexport { default as JFooterColumn } from './components/footer/JFooterColumn.vue'\nexport { default as JInput } from './components/input/JInput.vue'\nexport { default as JKbd } from './components/kbd/JKbd.vue'\nexport { default as JNavbar } from './components/navbar/JNavbar.vue'\nexport { default as JNavbarLink } from './components/navbar/JNavbarLink.vue'\nexport { default as JPagination } from './components/pagination/JPagination.vue'\nexport { default as JPopover } from './components/popover/JPopover.vue'\nexport { default as JProgress } from './components/progress/JProgress.vue'\nexport { default as JRadio } from './components/radio/JRadio.vue'\nexport { default as JRadioGroup } from './components/radio/JRadioGroup.vue'\nexport { default as JRating } from './components/rating/JRating.vue'\nexport { default as JSelect } from './components/select/JSelect.vue'\nexport { default as JSeparator } from './components/separator/JSeparator.vue'\nexport { default as JSheet } from './components/sheet/JSheet.vue'\nexport { default as JSidebar } from './components/sidebar/JSidebar.vue'\nexport { default as JSidebarGroup } from './components/sidebar/JSidebarGroup.vue'\nexport { default as JSidebarItem } from './components/sidebar/JSidebarItem.vue'\nexport { default as JSkeleton } from './components/skeleton/JSkeleton.vue'\nexport { default as JSlider } from './components/slider/JSlider.vue'\nexport { default as JSpinner } from './components/spinner/JSpinner.vue'\nexport { default as JStat } from './components/stat/JStat.vue'\nexport { default as JSwitch } from './components/switch/JSwitch.vue'\nexport { default as JTabs } from './components/tabs/JTabs.vue'\nexport { default as JTag } from './components/tag/JTag.vue'\nexport { default as JTextarea } from './components/textarea/JTextarea.vue'\nexport { default as JTimeline } from './components/timeline/JTimeline.vue'\nexport { default as JTimelineItem } from './components/timeline/JTimelineItem.vue'\nexport { default as JToaster } from './components/toast/JToaster.vue'\nexport { default as JToggleGroup } from './components/toggle-group/JToggleGroup.vue'\nexport { default as JToggleGroupItem } from './components/toggle-group/JToggleGroupItem.vue'\nexport { default as JTooltip } from './components/tooltip/JTooltip.vue'\nexport { default as JVisuallyHidden } from './components/visually-hidden/JVisuallyHidden.vue'\n","import { onMounted, ref } from 'vue'\r\n\r\nexport interface RecentCommandsOptions {\r\n /** localStorage key. */\r\n key?: string\r\n /** How many ids to remember. */\r\n limit?: number\r\n}\r\n\r\n/**\r\n * Remembers which commands were used most recently, per browser.\r\n * Pair `record(item.id)` in your select handler with `resolve(allItems)`\r\n * to feed the palette's `recent` prop.\r\n */\r\nexport function useRecentCommands(options: RecentCommandsOptions = {}) {\r\n const key = options.key ?? 'juxt:recent-commands'\r\n const limit = options.limit ?? 5\r\n const ids = ref<string[]>([])\r\n\r\n function read(): string[] {\r\n try {\r\n const value = JSON.parse(localStorage.getItem(key) ?? '[]')\r\n return Array.isArray(value) ? value.filter((id): id is string => typeof id === 'string') : []\r\n } catch {\r\n return []\r\n }\r\n }\r\n\r\n function write() {\r\n try {\r\n localStorage.setItem(key, JSON.stringify(ids.value))\r\n } catch {\r\n // Private mode or storage disabled: recents simply won't persist.\r\n }\r\n }\r\n\r\n onMounted(() => (ids.value = read()))\r\n\r\n function record(id: string) {\r\n ids.value = [id, ...ids.value.filter(existing => existing !== id)].slice(0, limit)\r\n write()\r\n }\r\n\r\n function clear() {\r\n ids.value = []\r\n write()\r\n }\r\n\r\n /** Map remembered ids back to items, dropping any that no longer exist. */\r\n function resolve<T extends { id: string }>(items: T[]): T[] {\r\n const byId = new Map(items.map(item => [item.id, item]))\r\n return ids.value.map(id => byId.get(id)).filter((item): item is T => !!item)\r\n }\r\n\r\n return { ids, record, clear, resolve }\r\n}\r\n","import type { Component, Plugin } from 'vue'\r\nimport * as components from './components'\r\n\r\n/**\r\n * Registers every Juxt component globally.\r\n *\r\n * app.use(JuxtUI)\r\n *\r\n * Prefer direct imports (or the Nuxt module) if you want tree-shaking.\r\n */\r\nexport const JuxtUI: Plugin = {\r\n install(app) {\r\n for (const [name, component] of Object.entries(components)) app.component(name, component as Component)\r\n },\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["_useModel","_openBlock","_createElementBlock","_renderSlot","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_openBlock","_createElementBlock","_normalizeClass","_createBlock","_resolveDynamicComponent","_withCtx","_createElementVNode","_unref","_renderSlot","_createTextVNode","_toDisplayString","_createVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createBlock","_resolveDynamicComponent","_createCommentVNode","_createElementVNode","_toDisplayString","_createTextVNode","_createVNode","_unref","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_normalizeStyle","_createElementVNode","_renderSlot","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_createCommentVNode","_renderSlot","_Fragment","_createTextVNode","_toDisplayString","_sfc_main","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList","_createVNode","JAvatar","_toDisplayString","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createBlock","_resolveDynamicComponent","_createCommentVNode","_createElementVNode","_toDisplayString","_createTextVNode","_createVNode","_unref","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_createElementVNode","_Fragment","_renderList","_toDisplayString","_renderSlot","_sfc_main","_hoisted_1","_useModel","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createBlock","_resolveDynamicComponent","_mergeProps","_withCtx","_createElementVNode","_createElementBlock","_renderSlot","_createCommentVNode","_createVNode","_Transition","_unref","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_openBlock","_createBlock","_resolveDynamicComponent","_normalizeClass","_withCtx","_createElementBlock","_renderSlot","_createElementVNode","_toDisplayString","_createCommentVNode","_sfc_main","$attrs","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_useModel","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createElementVNode","_mergeProps","_createStaticVNode","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_useModel","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_unref","_renderSlot","_createTextVNode","_toDisplayString","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_useModel","_openBlock","_createBlock","JPortal","_withCtx","_createVNode","_Transition","_createElementBlock","_createCommentVNode","_createElementVNode","_unref","_withDirectives","_vModelText","_withModifiers","_Fragment","_renderList","_toDisplayString","_normalizeClass","_renderSlot","_resolveDynamicComponent","_createTextVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_useModel","_openBlock","_createBlock","JPortal","_withCtx","_createVNode","_Transition","_createElementBlock","_createCommentVNode","_unref","_createElementVNode","_normalizeClass","_renderSlot","_toDisplayString","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_renderSlot","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_renderSlot","_createCommentVNode","_toDisplayString","_createVNode","_unref","JMenuContent","_withCtx","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createCommentVNode","_toDisplayString","_createTextVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createElementVNode","_renderSlot","_createCommentVNode","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_renderSlot","_sfc_main","$attrs","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_useModel","_openBlock","_createBlock","JFieldShell","_unref","_normalizeClass","_normalizeStyle","_createSlots","_withCtx","_createElementVNode","_createElementBlock","_renderSlot","_createCommentVNode","_withDirectives","_mergeProps","_vModelDynamic","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_useModel","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createElementVNode","_renderSlot","_createCommentVNode","_createBlock","JBurger","_createVNode","_Transition","_withCtx","_withDirectives","_vShow","_sfc_main","$slots","_hoisted_1","_openBlock","_createBlock","_resolveDynamicComponent","_normalizeClass","_withCtx","_createElementBlock","_renderSlot","_createCommentVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_useModel","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_createVNode","_unref","_Fragment","_renderList","_toDisplayString","_sfc_main","_hoisted_1","_useModel","_openBlock","_createElementBlock","_Fragment","_createVNode","_unref","_mergeProps","_withCtx","_renderSlot","JPortal","_Transition","_normalizeStyle","_createCommentVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createElementBlock","_normalizeClass","_toDisplayString","_createCommentVNode","_createElementVNode","_normalizeStyle","_sfc_main","$attrs","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createElementVNode","_mergeProps","_unref","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_useModel","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_createElementVNode","_sfc_main","_hoisted_1","_hoisted_2","_useModel","_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList","_createElementVNode","_normalizeStyle","_sfc_main","$attrs","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_useModel","_openBlock","_createBlock","JFieldShell","_unref","_normalizeClass","_normalizeStyle","_createSlots","_withCtx","_createElementVNode","_mergeProps","_renderSlot","_createTextVNode","_toDisplayString","_createElementBlock","_Fragment","_createVNode","_createCommentVNode","JPortal","_Transition","_withModifiers","_renderList","_sfc_main","$slots","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_useModel","_openBlock","_createBlock","JPortal","_withCtx","_createVNode","_Transition","_createElementBlock","_createCommentVNode","_normalizeClass","_unref","_createElementVNode","_renderSlot","_toDisplayString","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createElementBlock","_renderSlot","_createCommentVNode","_createElementVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_useModel","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createVNode","_unref","_createCommentVNode","_renderSlot","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createBlock","_resolveDynamicComponent","_normalizeClass","_withCtx","_createElementBlock","_renderSlot","_createCommentVNode","_createElementVNode","_sfc_main","_openBlock","_createElementBlock","_normalizeStyle","_Fragment","_renderList","_normalizeClass","_sfc_main","_hoisted_1","_hoisted_2","_useModel","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_normalizeStyle","_Fragment","_renderList","_toDisplayString","_createCommentVNode","_sfc_main","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_createVNode","_unref","_toDisplayString","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_openBlock","_createElementBlock","_createElementVNode","_renderSlot","_createCommentVNode","_toDisplayString","_normalizeClass","_createBlock","_resolveDynamicComponent","_createTextVNode","_sfc_main","$attrs","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_useModel","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createElementVNode","_mergeProps","_createCommentVNode","_renderSlot","_createTextVNode","_toDisplayString","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_useModel","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_normalizeStyle","_Fragment","_renderList","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_createCommentVNode","_createElementVNode","_createVNode","_unref","_sfc_main","$attrs","$slots","_hoisted_1","_useModel","_openBlock","_createBlock","JFieldShell","_unref","_normalizeClass","_normalizeStyle","_createSlots","_withCtx","_createElementVNode","_withDirectives","_mergeProps","_vModelText","_renderSlot","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_renderSlot","_sfc_main","$slots","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_createVNode","_Transition","_withCtx","_createBlock","_unref","_resolveDynamicComponent","_createCommentVNode","_createElementVNode","_toDisplayString","_sfc_main","_hoisted_1","_openBlock","_createBlock","JPortal","_withCtx","_createElementVNode","_normalizeClass","_createVNode","_TransitionGroup","_createElementBlock","_Fragment","_renderList","JToastItem","_sfc_main","_hoisted_1","_useModel","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_sfc_main","_openBlock","_createElementBlock","_normalizeClass","_unref","_renderSlot","_sfc_main","_openBlock","_createBlock","_resolveDynamicComponent","_withCtx","_renderSlot","_sfc_main","components"],"sources":["../src/components/accordion/context.ts","../src/components/accordion/JAccordion.vue","../src/components/accordion/JAccordion.vue","../src/components/accordion/JAccordionItem.vue","../src/components/accordion/JAccordionItem.vue","../src/components/alert/JAlert.vue","../src/components/alert/JAlert.vue","../src/components/aspect-ratio/JAspectRatio.vue","../src/components/aspect-ratio/JAspectRatio.vue","../src/components/avatar/JAvatar.vue","../src/components/avatar/JAvatar.vue","../src/components/avatar-group/JAvatarGroup.vue","../src/components/avatar-group/JAvatarGroup.vue","../src/components/badge/JBadge.vue","../src/components/badge/JBadge.vue","../src/components/banner/JBanner.vue","../src/components/banner/JBanner.vue","../src/components/breadcrumb/JBreadcrumb.vue","../src/components/breadcrumb/JBreadcrumb.vue","../src/components/burger/JBurger.vue","../src/components/burger/JBurger.vue","../src/components/button/JButton.vue","../src/components/button/JButton.vue","../src/components/card/JCard.vue","../src/components/card/JCard.vue","../src/components/checkbox/JCheckbox.vue","../src/components/checkbox/JCheckbox.vue","../src/components/collapsible/JCollapsible.vue","../src/components/collapsible/JCollapsible.vue","../src/composables/useFocusTrap.ts","../src/composables/useScrollLock.ts","../src/components/command-palette/JCommandPalette.vue","../src/components/command-palette/JCommandPalette.vue","../src/composables/useModal.ts","../src/components/dialog/JDialog.vue","../src/components/dialog/JDialog.vue","../src/components/dropdown-menu/JDropdownMenuLabel.vue","../src/components/dropdown-menu/JDropdownMenuLabel.vue","../src/components/dropdown-menu/JDropdownMenuSeparator.vue","../src/components/dropdown-menu/JDropdownMenuSub.vue","../src/components/dropdown-menu/JDropdownMenuSub.vue","../src/components/empty-state/JEmptyState.vue","../src/components/empty-state/JEmptyState.vue","../src/components/footer/JFooter.vue","../src/components/footer/JFooter.vue","../src/components/footer/JFooterColumn.vue","../src/components/footer/JFooterColumn.vue","../src/components/input/JInput.vue","../src/components/input/JInput.vue","../src/components/navbar/JNavbar.vue","../src/components/navbar/JNavbar.vue","../src/components/navbar/JNavbarLink.vue","../src/components/navbar/JNavbarLink.vue","../src/components/pagination/JPagination.vue","../src/components/pagination/JPagination.vue","../src/components/popover/JPopover.vue","../src/components/popover/JPopover.vue","../src/components/progress/JProgress.vue","../src/components/progress/JProgress.vue","../src/components/radio/context.ts","../src/components/radio/JRadio.vue","../src/components/radio/JRadio.vue","../src/components/radio/JRadioGroup.vue","../src/components/radio/JRadioGroup.vue","../src/components/rating/JRating.vue","../src/components/rating/JRating.vue","../src/components/select/JSelect.vue","../src/components/select/JSelect.vue","../src/components/separator/JSeparator.vue","../src/components/separator/JSeparator.vue","../src/components/sheet/JSheet.vue","../src/components/sheet/JSheet.vue","../src/components/sidebar/JSidebar.vue","../src/components/sidebar/JSidebar.vue","../src/components/sidebar/JSidebarGroup.vue","../src/components/sidebar/JSidebarGroup.vue","../src/components/sidebar/JSidebarItem.vue","../src/components/sidebar/JSidebarItem.vue","../src/components/skeleton/JSkeleton.vue","../src/components/skeleton/JSkeleton.vue","../src/components/slider/JSlider.vue","../src/components/slider/JSlider.vue","../src/components/spinner/JSpinner.vue","../src/components/spinner/JSpinner.vue","../src/components/stat/JStat.vue","../src/components/stat/JStat.vue","../src/components/switch/JSwitch.vue","../src/components/switch/JSwitch.vue","../src/components/tabs/JTabs.vue","../src/components/tabs/JTabs.vue","../src/components/tag/JTag.vue","../src/components/tag/JTag.vue","../src/components/textarea/JTextarea.vue","../src/components/textarea/JTextarea.vue","../src/components/timeline/JTimeline.vue","../src/components/timeline/JTimeline.vue","../src/components/timeline/JTimelineItem.vue","../src/components/timeline/JTimelineItem.vue","../src/composables/useToast.ts","../src/components/toast/JToastItem.vue","../src/components/toast/JToastItem.vue","../src/components/toast/JToaster.vue","../src/components/toast/JToaster.vue","../src/components/toggle-group/context.ts","../src/components/toggle-group/JToggleGroup.vue","../src/components/toggle-group/JToggleGroup.vue","../src/components/toggle-group/JToggleGroupItem.vue","../src/components/toggle-group/JToggleGroupItem.vue","../src/components/visually-hidden/JVisuallyHidden.vue","../src/components/visually-hidden/JVisuallyHidden.vue","../src/components.ts","../src/composables/useRecentCommands.ts","../src/plugin.ts"],"sourcesContent":["import type { InjectionKey } from 'vue'\n\nexport interface AccordionContext {\n isOpen: (value: string) => boolean\n toggle: (value: string) => void\n headingLevel: () => number\n}\n\nexport const ACCORDION: InjectionKey<AccordionContext> = Symbol('JAccordion')\n","<script setup lang=\"ts\">\nimport { provide } from 'vue'\nimport { focusElement } from '../../utils/dom'\nimport { ACCORDION } from './context'\n\nexport interface AccordionProps {\n /** `single` keeps at most one item open; `multiple` lets any number stay open. */\n type?: 'single' | 'multiple'\n /** In `single` mode, allow closing the open item so none are open. */\n collapsible?: boolean\n /** Heading level wrapping each trigger, for a correct document outline. */\n headingLevel?: 2 | 3 | 4 | 5 | 6\n}\n\nconst props = withDefaults(defineProps<AccordionProps>(), {\n type: 'single',\n collapsible: true,\n headingLevel: 3,\n})\n\n/** Open item(s): a value in `single` mode, an array in `multiple` mode. */\nconst model = defineModel<string | string[] | null>({ default: null })\n\ndefineSlots<{ default?: () => unknown }>()\n\nfunction openValues(): string[] {\n const value = model.value\n if (Array.isArray(value)) return value\n return value ? [value] : []\n}\n\nprovide(ACCORDION, {\n isOpen: value => openValues().includes(value),\n toggle: (value) => {\n const current = openValues()\n const isOpen = current.includes(value)\n if (props.type === 'multiple') {\n model.value = isOpen ? current.filter(v => v !== value) : [...current, value]\n return\n }\n if (isOpen) {\n if (props.collapsible) model.value = null\n return\n }\n model.value = value\n },\n headingLevel: () => props.headingLevel,\n})\n\n// Arrow keys move between headers, per the WAI-ARIA accordion pattern.\nfunction onKeydown(event: KeyboardEvent) {\n const target = event.target as HTMLElement\n if (!target.classList.contains('j-accordion__trigger')) return\n const root = event.currentTarget as HTMLElement\n const triggers = Array.from(root.querySelectorAll<HTMLElement>('.j-accordion__trigger:not(:disabled)'))\n .filter(el => el.closest('.j-accordion') === root)\n const index = triggers.indexOf(target)\n let next: HTMLElement | undefined\n if (event.key === 'ArrowDown') next = triggers[(index + 1) % triggers.length]\n else if (event.key === 'ArrowUp') next = triggers[(index - 1 + triggers.length) % triggers.length]\n else if (event.key === 'Home') next = triggers[0]\n else if (event.key === 'End') next = triggers[triggers.length - 1]\n if (!next) return\n event.preventDefault()\n focusElement(next)\n}\n</script>\n\n<template>\n <div class=\"j-accordion\" @keydown=\"onKeydown\">\n <slot />\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-accordion {\n display: flex;\n flex-direction: column;\n font-family: var(--juxt-font-sans);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { provide } from 'vue'\nimport { focusElement } from '../../utils/dom'\nimport { ACCORDION } from './context'\n\nexport interface AccordionProps {\n /** `single` keeps at most one item open; `multiple` lets any number stay open. */\n type?: 'single' | 'multiple'\n /** In `single` mode, allow closing the open item so none are open. */\n collapsible?: boolean\n /** Heading level wrapping each trigger, for a correct document outline. */\n headingLevel?: 2 | 3 | 4 | 5 | 6\n}\n\nconst props = withDefaults(defineProps<AccordionProps>(), {\n type: 'single',\n collapsible: true,\n headingLevel: 3,\n})\n\n/** Open item(s): a value in `single` mode, an array in `multiple` mode. */\nconst model = defineModel<string | string[] | null>({ default: null })\n\ndefineSlots<{ default?: () => unknown }>()\n\nfunction openValues(): string[] {\n const value = model.value\n if (Array.isArray(value)) return value\n return value ? [value] : []\n}\n\nprovide(ACCORDION, {\n isOpen: value => openValues().includes(value),\n toggle: (value) => {\n const current = openValues()\n const isOpen = current.includes(value)\n if (props.type === 'multiple') {\n model.value = isOpen ? current.filter(v => v !== value) : [...current, value]\n return\n }\n if (isOpen) {\n if (props.collapsible) model.value = null\n return\n }\n model.value = value\n },\n headingLevel: () => props.headingLevel,\n})\n\n// Arrow keys move between headers, per the WAI-ARIA accordion pattern.\nfunction onKeydown(event: KeyboardEvent) {\n const target = event.target as HTMLElement\n if (!target.classList.contains('j-accordion__trigger')) return\n const root = event.currentTarget as HTMLElement\n const triggers = Array.from(root.querySelectorAll<HTMLElement>('.j-accordion__trigger:not(:disabled)'))\n .filter(el => el.closest('.j-accordion') === root)\n const index = triggers.indexOf(target)\n let next: HTMLElement | undefined\n if (event.key === 'ArrowDown') next = triggers[(index + 1) % triggers.length]\n else if (event.key === 'ArrowUp') next = triggers[(index - 1 + triggers.length) % triggers.length]\n else if (event.key === 'Home') next = triggers[0]\n else if (event.key === 'End') next = triggers[triggers.length - 1]\n if (!next) return\n event.preventDefault()\n focusElement(next)\n}\n</script>\n\n<template>\n <div class=\"j-accordion\" @keydown=\"onKeydown\">\n <slot />\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-accordion {\n display: flex;\n flex-direction: column;\n font-family: var(--juxt-font-sans);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, inject, useId } from 'vue'\nimport { IconChevronDown } from '../../icons'\nimport { ACCORDION } from './context'\n\nexport interface AccordionItemProps {\n /** Identifies the item in the accordion's `v-model`. */\n value: string\n title?: string\n disabled?: boolean\n}\n\nconst props = defineProps<AccordionItemProps>()\n\ndefineSlots<{ default?: () => unknown, title?: () => unknown }>()\n\nconst accordion = inject(ACCORDION)!\nconst id = `j-${useId()}`\nconst triggerId = `${id}-trigger`\nconst panelId = `${id}-panel`\nconst open = computed(() => accordion.isOpen(props.value))\nconst heading = computed(() => `h${accordion.headingLevel()}`)\n</script>\n\n<template>\n <div class=\"j-accordion__item\" :data-state=\"open ? 'open' : 'closed'\" :class=\"{ 'is-disabled': disabled }\">\n <component :is=\"heading\" class=\"j-accordion__heading\">\n <button\n :id=\"triggerId\"\n type=\"button\"\n class=\"j-accordion__trigger\"\n :aria-expanded=\"open\"\n :aria-controls=\"panelId\"\n :disabled=\"disabled\"\n @click=\"accordion.toggle(value)\"\n >\n <span class=\"j-accordion__title\"><slot name=\"title\">{{ title }}</slot></span>\n <IconChevronDown class=\"j-accordion__chevron\" />\n </button>\n </component>\n <div\n :id=\"panelId\"\n role=\"region\"\n class=\"j-accordion__panel\"\n :aria-labelledby=\"triggerId\"\n :inert=\"!open\"\n >\n <div class=\"j-accordion__panel-inner\">\n <div class=\"j-accordion__content\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-accordion__item {\n border-bottom: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-accordion__item,\n[data-theme='dark'] .j-accordion__item {\n border-bottom-color: var(--juxt-border);\n}\n\n.j-accordion__heading {\n margin: 0;\n font: inherit;\n}\n\n.j-accordion__trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--juxt-space-4);\n width: 100%;\n min-height: 3rem;\n padding: var(--juxt-space-3) 0;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n color: var(--juxt-fg);\n font: inherit;\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n text-align: left;\n cursor: pointer;\n outline: 2px solid transparent;\n outline-offset: 0;\n -webkit-tap-highlight-color: transparent;\n transition:\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-accordion__trigger:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n\n.j-accordion__trigger:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-accordion__chevron {\n flex: none;\n width: 1rem;\n height: 1rem;\n color: var(--juxt-fg-muted);\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n color var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-accordion__trigger:hover:not(:disabled) .j-accordion__chevron {\n color: var(--juxt-fg);\n}\n\n.j-accordion__item[data-state='open'] .j-accordion__chevron {\n transform: rotate(180deg);\n}\n\n/* Height follows content via grid rows | no measuring, no magic numbers. */\n.j-accordion__panel {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n.j-accordion__item[data-state='open'] .j-accordion__panel {\n grid-template-rows: 1fr;\n}\n\n.j-accordion__panel-inner {\n min-height: 0;\n overflow: hidden;\n}\n\n.j-accordion__content {\n padding-bottom: var(--juxt-space-4);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n opacity: 0;\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-accordion__item[data-state='open'] .j-accordion__content {\n opacity: 1;\n transition-delay: 60ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-accordion__panel,\n .j-accordion__chevron {\n transition: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, inject, useId } from 'vue'\nimport { IconChevronDown } from '../../icons'\nimport { ACCORDION } from './context'\n\nexport interface AccordionItemProps {\n /** Identifies the item in the accordion's `v-model`. */\n value: string\n title?: string\n disabled?: boolean\n}\n\nconst props = defineProps<AccordionItemProps>()\n\ndefineSlots<{ default?: () => unknown, title?: () => unknown }>()\n\nconst accordion = inject(ACCORDION)!\nconst id = `j-${useId()}`\nconst triggerId = `${id}-trigger`\nconst panelId = `${id}-panel`\nconst open = computed(() => accordion.isOpen(props.value))\nconst heading = computed(() => `h${accordion.headingLevel()}`)\n</script>\n\n<template>\n <div class=\"j-accordion__item\" :data-state=\"open ? 'open' : 'closed'\" :class=\"{ 'is-disabled': disabled }\">\n <component :is=\"heading\" class=\"j-accordion__heading\">\n <button\n :id=\"triggerId\"\n type=\"button\"\n class=\"j-accordion__trigger\"\n :aria-expanded=\"open\"\n :aria-controls=\"panelId\"\n :disabled=\"disabled\"\n @click=\"accordion.toggle(value)\"\n >\n <span class=\"j-accordion__title\"><slot name=\"title\">{{ title }}</slot></span>\n <IconChevronDown class=\"j-accordion__chevron\" />\n </button>\n </component>\n <div\n :id=\"panelId\"\n role=\"region\"\n class=\"j-accordion__panel\"\n :aria-labelledby=\"triggerId\"\n :inert=\"!open\"\n >\n <div class=\"j-accordion__panel-inner\">\n <div class=\"j-accordion__content\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-accordion__item {\n border-bottom: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-accordion__item,\n[data-theme='dark'] .j-accordion__item {\n border-bottom-color: var(--juxt-border);\n}\n\n.j-accordion__heading {\n margin: 0;\n font: inherit;\n}\n\n.j-accordion__trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--juxt-space-4);\n width: 100%;\n min-height: 3rem;\n padding: var(--juxt-space-3) 0;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n color: var(--juxt-fg);\n font: inherit;\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n text-align: left;\n cursor: pointer;\n outline: 2px solid transparent;\n outline-offset: 0;\n -webkit-tap-highlight-color: transparent;\n transition:\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-accordion__trigger:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n\n.j-accordion__trigger:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-accordion__chevron {\n flex: none;\n width: 1rem;\n height: 1rem;\n color: var(--juxt-fg-muted);\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n color var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-accordion__trigger:hover:not(:disabled) .j-accordion__chevron {\n color: var(--juxt-fg);\n}\n\n.j-accordion__item[data-state='open'] .j-accordion__chevron {\n transform: rotate(180deg);\n}\n\n/* Height follows content via grid rows | no measuring, no magic numbers. */\n.j-accordion__panel {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n.j-accordion__item[data-state='open'] .j-accordion__panel {\n grid-template-rows: 1fr;\n}\n\n.j-accordion__panel-inner {\n min-height: 0;\n overflow: hidden;\n}\n\n.j-accordion__content {\n padding-bottom: var(--juxt-space-4);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n opacity: 0;\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-accordion__item[data-state='open'] .j-accordion__content {\n opacity: 1;\n transition-delay: 60ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-accordion__panel,\n .j-accordion__chevron {\n transition: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX } from '../../icons'\n\nexport type AlertVariant = 'neutral' | 'info' | 'success' | 'warning' | 'destructive'\n\nexport interface AlertProps {\n variant?: AlertVariant\n title?: string\n description?: string\n /** Show a close button; emits `dismiss`. */\n dismissible?: boolean\n /** Hide the leading icon. */\n hideIcon?: boolean\n}\n\nwithDefaults(defineProps<AlertProps>(), { variant: 'neutral' })\n\nconst emit = defineEmits<{ dismiss: [] }>()\n\ndefineSlots<{\n default?: () => unknown\n icon?: () => unknown\n actions?: () => unknown\n}>()\n\nconst icons = {\n neutral: IconInfo,\n info: IconInfo,\n success: IconCircleCheck,\n warning: IconAlert,\n destructive: IconCircleX,\n}\n</script>\n\n<template>\n <div class=\"j-alert\" :class=\"`j-alert--${variant}`\">\n <span v-if=\"!hideIcon\" class=\"j-alert__icon\" aria-hidden=\"true\">\n <slot name=\"icon\"><component :is=\"icons[variant]\" /></slot>\n </span>\n <div class=\"j-alert__body\">\n <p v-if=\"title\" class=\"j-alert__title\">\n {{ title }}\n </p>\n <div v-if=\"description || $slots.default\" class=\"j-alert__description\">\n <slot>{{ description }}</slot>\n </div>\n <div v-if=\"$slots.actions\" class=\"j-alert__actions\">\n <slot name=\"actions\" />\n </div>\n </div>\n <button v-if=\"dismissible\" type=\"button\" class=\"j-alert__close j-focusable\" aria-label=\"Dismiss\" @click=\"emit('dismiss')\">\n <IconX />\n </button>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n/*\n * Alerts stay on the page's surface. Colour lives in the icon and a hairline\n * of the border | enough to be noticed, never enough to shout.\n */\n.j-alert {\n --j-alert-accent: var(--juxt-fg-muted);\n --j-alert-border: var(--juxt-border);\n\n position: relative;\n display: flex;\n align-items: flex-start;\n gap: var(--juxt-space-3);\n padding: var(--juxt-space-3) var(--juxt-space-4);\n border: 1px solid var(--j-alert-border);\n border-radius: var(--juxt-radius-md);\n background: var(--juxt-surface);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-alert--info {\n --j-alert-accent: var(--juxt-info-text);\n}\n\n.j-alert--success {\n --j-alert-accent: var(--juxt-success-text);\n}\n\n.j-alert--warning {\n --j-alert-accent: var(--juxt-warning-text);\n --j-alert-border: var(--juxt-warning-border);\n}\n\n.j-alert--destructive {\n --j-alert-accent: var(--juxt-danger-text);\n --j-alert-border: var(--juxt-danger-border);\n}\n\n.j-alert__icon {\n display: grid;\n flex: none;\n place-items: center;\n height: 1.25rem;\n color: var(--j-alert-accent);\n}\n\n.j-alert__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-alert__body {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n min-width: 0;\n}\n\n.j-alert__title {\n margin: 0;\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n line-height: 1.25rem;\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-alert__description {\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-alert__description > p {\n margin: 0;\n}\n\n.j-alert__actions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--juxt-space-2);\n margin-top: var(--juxt-space-2);\n}\n\n.j-alert:has(.j-alert__close) {\n padding-right: var(--juxt-space-10);\n}\n\n.j-alert__close {\n position: absolute;\n top: var(--juxt-space-2-5);\n right: var(--juxt-space-2-5);\n display: grid;\n place-items: center;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n color: var(--juxt-fg-muted);\n cursor: pointer;\n transition-property: background-color, color, outline-color, outline-offset;\n}\n\n.j-alert__close:hover {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-alert__close > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX } from '../../icons'\n\nexport type AlertVariant = 'neutral' | 'info' | 'success' | 'warning' | 'destructive'\n\nexport interface AlertProps {\n variant?: AlertVariant\n title?: string\n description?: string\n /** Show a close button; emits `dismiss`. */\n dismissible?: boolean\n /** Hide the leading icon. */\n hideIcon?: boolean\n}\n\nwithDefaults(defineProps<AlertProps>(), { variant: 'neutral' })\n\nconst emit = defineEmits<{ dismiss: [] }>()\n\ndefineSlots<{\n default?: () => unknown\n icon?: () => unknown\n actions?: () => unknown\n}>()\n\nconst icons = {\n neutral: IconInfo,\n info: IconInfo,\n success: IconCircleCheck,\n warning: IconAlert,\n destructive: IconCircleX,\n}\n</script>\n\n<template>\n <div class=\"j-alert\" :class=\"`j-alert--${variant}`\">\n <span v-if=\"!hideIcon\" class=\"j-alert__icon\" aria-hidden=\"true\">\n <slot name=\"icon\"><component :is=\"icons[variant]\" /></slot>\n </span>\n <div class=\"j-alert__body\">\n <p v-if=\"title\" class=\"j-alert__title\">\n {{ title }}\n </p>\n <div v-if=\"description || $slots.default\" class=\"j-alert__description\">\n <slot>{{ description }}</slot>\n </div>\n <div v-if=\"$slots.actions\" class=\"j-alert__actions\">\n <slot name=\"actions\" />\n </div>\n </div>\n <button v-if=\"dismissible\" type=\"button\" class=\"j-alert__close j-focusable\" aria-label=\"Dismiss\" @click=\"emit('dismiss')\">\n <IconX />\n </button>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n/*\n * Alerts stay on the page's surface. Colour lives in the icon and a hairline\n * of the border | enough to be noticed, never enough to shout.\n */\n.j-alert {\n --j-alert-accent: var(--juxt-fg-muted);\n --j-alert-border: var(--juxt-border);\n\n position: relative;\n display: flex;\n align-items: flex-start;\n gap: var(--juxt-space-3);\n padding: var(--juxt-space-3) var(--juxt-space-4);\n border: 1px solid var(--j-alert-border);\n border-radius: var(--juxt-radius-md);\n background: var(--juxt-surface);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-alert--info {\n --j-alert-accent: var(--juxt-info-text);\n}\n\n.j-alert--success {\n --j-alert-accent: var(--juxt-success-text);\n}\n\n.j-alert--warning {\n --j-alert-accent: var(--juxt-warning-text);\n --j-alert-border: var(--juxt-warning-border);\n}\n\n.j-alert--destructive {\n --j-alert-accent: var(--juxt-danger-text);\n --j-alert-border: var(--juxt-danger-border);\n}\n\n.j-alert__icon {\n display: grid;\n flex: none;\n place-items: center;\n height: 1.25rem;\n color: var(--j-alert-accent);\n}\n\n.j-alert__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-alert__body {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n min-width: 0;\n}\n\n.j-alert__title {\n margin: 0;\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n line-height: 1.25rem;\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-alert__description {\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-alert__description > p {\n margin: 0;\n}\n\n.j-alert__actions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--juxt-space-2);\n margin-top: var(--juxt-space-2);\n}\n\n.j-alert:has(.j-alert__close) {\n padding-right: var(--juxt-space-10);\n}\n\n.j-alert__close {\n position: absolute;\n top: var(--juxt-space-2-5);\n right: var(--juxt-space-2-5);\n display: grid;\n place-items: center;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n color: var(--juxt-fg-muted);\n cursor: pointer;\n transition-property: background-color, color, outline-color, outline-offset;\n}\n\n.j-alert__close:hover {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-alert__close > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nexport interface AspectRatioProps {\n /** Width/height ratio, e.g. `16 / 9` or `1`. */\n ratio?: number\n}\n\nconst props = withDefaults(defineProps<AspectRatioProps>(), { ratio: 16 / 9 })\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst style = computed(() => ({ '--j-aspect-ratio': String(props.ratio) }))\n</script>\n\n<template>\n <div class=\"j-aspect-ratio\" :style=\"style\">\n <div class=\"j-aspect-ratio__content\">\n <slot />\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-aspect-ratio {\n position: relative;\n width: 100%;\n aspect-ratio: var(--j-aspect-ratio, 16 / 9);\n}\n\n.j-aspect-ratio__content {\n position: absolute;\n inset: 0;\n overflow: hidden;\n}\n\n.j-aspect-ratio__content > :where(img, video, iframe, canvas) {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nexport interface AspectRatioProps {\n /** Width/height ratio, e.g. `16 / 9` or `1`. */\n ratio?: number\n}\n\nconst props = withDefaults(defineProps<AspectRatioProps>(), { ratio: 16 / 9 })\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst style = computed(() => ({ '--j-aspect-ratio': String(props.ratio) }))\n</script>\n\n<template>\n <div class=\"j-aspect-ratio\" :style=\"style\">\n <div class=\"j-aspect-ratio__content\">\n <slot />\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-aspect-ratio {\n position: relative;\n width: 100%;\n aspect-ratio: var(--j-aspect-ratio, 16 / 9);\n}\n\n.j-aspect-ratio__content {\n position: absolute;\n inset: 0;\n overflow: hidden;\n}\n\n.j-aspect-ratio__content > :where(img, video, iframe, canvas) {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\n\nexport type AvatarStatus = 'online' | 'away' | 'busy' | 'offline'\n\nexport interface AvatarProps {\n src?: string\n /** Image alt text. Defaults to `name`. */\n alt?: string\n /** Full name | used for initials and as the accessible name. */\n name?: string\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n shape?: 'circle' | 'square'\n status?: AvatarStatus\n}\n\nconst props = withDefaults(defineProps<AvatarProps>(), { size: 'md', shape: 'circle' })\n\ndefineSlots<{ fallback?: () => unknown }>()\n\nconst state = ref<'loading' | 'loaded' | 'error'>(props.src ? 'loading' : 'error')\nconst img = ref<HTMLImageElement | null>(null)\n\nwatch(() => props.src, (src) => {\n state.value = src ? 'loading' : 'error'\n})\n\n// An image cached before hydration may have finished loading already.\nonMounted(() => {\n if (img.value?.complete && img.value.naturalWidth > 0) state.value = 'loaded'\n})\n\nconst initials = computed(() => {\n const parts = (props.name ?? '').trim().split(/\\s+/).filter(Boolean)\n if (parts.length === 0) return ''\n const first = parts[0]!\n const last = parts.length > 1 ? parts[parts.length - 1]! : ''\n return (first[0]! + (last[0] ?? '')).toUpperCase()\n})\n\nconst statusLabel: Record<AvatarStatus, string> = {\n online: 'Online',\n away: 'Away',\n busy: 'Busy',\n offline: 'Offline',\n}\n\nconst label = computed(() => {\n const who = props.alt ?? props.name\n if (!who) return undefined\n return props.status ? `${who} (${statusLabel[props.status]})` : who\n})\n</script>\n\n<template>\n <span\n class=\"j-avatar\"\n :class=\"[`j-avatar--${size}`, `j-avatar--${shape}`]\"\n :role=\"label ? 'img' : undefined\"\n :aria-label=\"label\"\n :data-state=\"state\"\n >\n <span class=\"j-avatar__frame\">\n <img\n v-if=\"src && state !== 'error'\"\n ref=\"img\"\n class=\"j-avatar__image\"\n :src=\"src\"\n alt=\"\"\n @load=\"state = 'loaded'\"\n @error=\"state = 'error'\"\n />\n <span v-if=\"state !== 'loaded'\" class=\"j-avatar__fallback\" aria-hidden=\"true\">\n <slot name=\"fallback\">\n <template v-if=\"initials\">{{ initials }}</template>\n <svg v-else viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"j-avatar__glyph\">\n <circle cx=\"8\" cy=\"6\" r=\"2.75\" />\n <path d=\"M2.75 14c.6-2.6 2.7-4.25 5.25-4.25S12.65 11.4 13.25 14z\" />\n </svg>\n </slot>\n </span>\n </span>\n <span v-if=\"status\" class=\"j-avatar__status\" :class=\"`j-avatar__status--${status}`\" aria-hidden=\"true\" />\n </span>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-avatar {\n --j-avatar-size: 2rem;\n --j-avatar-font: var(--juxt-text-xs);\n --j-avatar-status: 0.5rem;\n --j-avatar-radius: var(--juxt-radius-full);\n\n position: relative;\n display: inline-flex;\n flex: none;\n width: var(--j-avatar-size);\n height: var(--j-avatar-size);\n font-family: var(--juxt-font-sans);\n vertical-align: middle;\n}\n\n.j-avatar--xs {\n --j-avatar-size: 1.25rem;\n --j-avatar-font: 0.5625rem;\n --j-avatar-status: 0.375rem;\n}\n\n.j-avatar--sm {\n --j-avatar-size: 1.5rem;\n --j-avatar-font: 0.625rem;\n --j-avatar-status: 0.4375rem;\n}\n\n.j-avatar--lg {\n --j-avatar-size: 2.5rem;\n --j-avatar-font: var(--juxt-text-sm);\n --j-avatar-status: 0.625rem;\n}\n\n.j-avatar--xl {\n --j-avatar-size: 3.5rem;\n --j-avatar-font: var(--juxt-text-lg);\n --j-avatar-status: 0.75rem;\n}\n\n.j-avatar--square {\n --j-avatar-radius: calc(var(--j-avatar-size) * 0.22);\n}\n\n.j-avatar__frame {\n position: relative;\n display: grid;\n width: 100%;\n height: 100%;\n overflow: hidden;\n border-radius: var(--j-avatar-radius);\n background: var(--juxt-surface-active);\n /* A hairline keeps light photos from bleeding into light backgrounds. */\n box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.06);\n}\n\n.dark .j-avatar__frame,\n[data-theme='dark'] .j-avatar__frame {\n box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.06);\n}\n\n.j-avatar__image,\n.j-avatar__fallback {\n grid-area: 1 / 1;\n width: 100%;\n height: 100%;\n}\n\n.j-avatar__image {\n object-fit: cover;\n opacity: 0;\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-standard);\n}\n\n.j-avatar[data-state='loaded'] .j-avatar__image {\n opacity: 1;\n}\n\n.j-avatar__fallback {\n display: grid;\n place-items: center;\n color: var(--juxt-fg-secondary);\n font-size: var(--j-avatar-font);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: 0.02em;\n line-height: 1;\n user-select: none;\n}\n\n.j-avatar__glyph {\n width: 62%;\n height: 62%;\n margin-top: 18%;\n color: var(--juxt-fg-muted);\n}\n\n.j-avatar__status {\n position: absolute;\n right: 0;\n bottom: 0;\n width: var(--j-avatar-status);\n height: var(--j-avatar-status);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-fg-muted);\n /* The ring is the page background, so the dot cuts cleanly out of the photo. */\n box-shadow: 0 0 0 2px var(--j-avatar-ring, var(--juxt-bg));\n}\n\n.j-avatar--square .j-avatar__status {\n right: -2px;\n bottom: -2px;\n}\n\n.j-avatar__status--online {\n background: var(--juxt-accent);\n}\n\n.j-avatar__status--away {\n background: var(--juxt-warning);\n}\n\n.j-avatar__status--busy {\n background: var(--juxt-danger);\n}\n\n.j-avatar__status--offline {\n background: var(--juxt-fg-disabled);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\n\nexport type AvatarStatus = 'online' | 'away' | 'busy' | 'offline'\n\nexport interface AvatarProps {\n src?: string\n /** Image alt text. Defaults to `name`. */\n alt?: string\n /** Full name | used for initials and as the accessible name. */\n name?: string\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n shape?: 'circle' | 'square'\n status?: AvatarStatus\n}\n\nconst props = withDefaults(defineProps<AvatarProps>(), { size: 'md', shape: 'circle' })\n\ndefineSlots<{ fallback?: () => unknown }>()\n\nconst state = ref<'loading' | 'loaded' | 'error'>(props.src ? 'loading' : 'error')\nconst img = ref<HTMLImageElement | null>(null)\n\nwatch(() => props.src, (src) => {\n state.value = src ? 'loading' : 'error'\n})\n\n// An image cached before hydration may have finished loading already.\nonMounted(() => {\n if (img.value?.complete && img.value.naturalWidth > 0) state.value = 'loaded'\n})\n\nconst initials = computed(() => {\n const parts = (props.name ?? '').trim().split(/\\s+/).filter(Boolean)\n if (parts.length === 0) return ''\n const first = parts[0]!\n const last = parts.length > 1 ? parts[parts.length - 1]! : ''\n return (first[0]! + (last[0] ?? '')).toUpperCase()\n})\n\nconst statusLabel: Record<AvatarStatus, string> = {\n online: 'Online',\n away: 'Away',\n busy: 'Busy',\n offline: 'Offline',\n}\n\nconst label = computed(() => {\n const who = props.alt ?? props.name\n if (!who) return undefined\n return props.status ? `${who} (${statusLabel[props.status]})` : who\n})\n</script>\n\n<template>\n <span\n class=\"j-avatar\"\n :class=\"[`j-avatar--${size}`, `j-avatar--${shape}`]\"\n :role=\"label ? 'img' : undefined\"\n :aria-label=\"label\"\n :data-state=\"state\"\n >\n <span class=\"j-avatar__frame\">\n <img\n v-if=\"src && state !== 'error'\"\n ref=\"img\"\n class=\"j-avatar__image\"\n :src=\"src\"\n alt=\"\"\n @load=\"state = 'loaded'\"\n @error=\"state = 'error'\"\n />\n <span v-if=\"state !== 'loaded'\" class=\"j-avatar__fallback\" aria-hidden=\"true\">\n <slot name=\"fallback\">\n <template v-if=\"initials\">{{ initials }}</template>\n <svg v-else viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"j-avatar__glyph\">\n <circle cx=\"8\" cy=\"6\" r=\"2.75\" />\n <path d=\"M2.75 14c.6-2.6 2.7-4.25 5.25-4.25S12.65 11.4 13.25 14z\" />\n </svg>\n </slot>\n </span>\n </span>\n <span v-if=\"status\" class=\"j-avatar__status\" :class=\"`j-avatar__status--${status}`\" aria-hidden=\"true\" />\n </span>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-avatar {\n --j-avatar-size: 2rem;\n --j-avatar-font: var(--juxt-text-xs);\n --j-avatar-status: 0.5rem;\n --j-avatar-radius: var(--juxt-radius-full);\n\n position: relative;\n display: inline-flex;\n flex: none;\n width: var(--j-avatar-size);\n height: var(--j-avatar-size);\n font-family: var(--juxt-font-sans);\n vertical-align: middle;\n}\n\n.j-avatar--xs {\n --j-avatar-size: 1.25rem;\n --j-avatar-font: 0.5625rem;\n --j-avatar-status: 0.375rem;\n}\n\n.j-avatar--sm {\n --j-avatar-size: 1.5rem;\n --j-avatar-font: 0.625rem;\n --j-avatar-status: 0.4375rem;\n}\n\n.j-avatar--lg {\n --j-avatar-size: 2.5rem;\n --j-avatar-font: var(--juxt-text-sm);\n --j-avatar-status: 0.625rem;\n}\n\n.j-avatar--xl {\n --j-avatar-size: 3.5rem;\n --j-avatar-font: var(--juxt-text-lg);\n --j-avatar-status: 0.75rem;\n}\n\n.j-avatar--square {\n --j-avatar-radius: calc(var(--j-avatar-size) * 0.22);\n}\n\n.j-avatar__frame {\n position: relative;\n display: grid;\n width: 100%;\n height: 100%;\n overflow: hidden;\n border-radius: var(--j-avatar-radius);\n background: var(--juxt-surface-active);\n /* A hairline keeps light photos from bleeding into light backgrounds. */\n box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.06);\n}\n\n.dark .j-avatar__frame,\n[data-theme='dark'] .j-avatar__frame {\n box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.06);\n}\n\n.j-avatar__image,\n.j-avatar__fallback {\n grid-area: 1 / 1;\n width: 100%;\n height: 100%;\n}\n\n.j-avatar__image {\n object-fit: cover;\n opacity: 0;\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-standard);\n}\n\n.j-avatar[data-state='loaded'] .j-avatar__image {\n opacity: 1;\n}\n\n.j-avatar__fallback {\n display: grid;\n place-items: center;\n color: var(--juxt-fg-secondary);\n font-size: var(--j-avatar-font);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: 0.02em;\n line-height: 1;\n user-select: none;\n}\n\n.j-avatar__glyph {\n width: 62%;\n height: 62%;\n margin-top: 18%;\n color: var(--juxt-fg-muted);\n}\n\n.j-avatar__status {\n position: absolute;\n right: 0;\n bottom: 0;\n width: var(--j-avatar-status);\n height: var(--j-avatar-status);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-fg-muted);\n /* The ring is the page background, so the dot cuts cleanly out of the photo. */\n box-shadow: 0 0 0 2px var(--j-avatar-ring, var(--juxt-bg));\n}\n\n.j-avatar--square .j-avatar__status {\n right: -2px;\n bottom: -2px;\n}\n\n.j-avatar__status--online {\n background: var(--juxt-accent);\n}\n\n.j-avatar__status--away {\n background: var(--juxt-warning);\n}\n\n.j-avatar__status--busy {\n background: var(--juxt-danger);\n}\n\n.j-avatar__status--offline {\n background: var(--juxt-fg-disabled);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport JAvatar from '../avatar/JAvatar.vue'\n\nexport interface AvatarGroupItem {\n src?: string\n name?: string\n}\n\nexport interface AvatarGroupProps {\n items: AvatarGroupItem[]\n /** Avatars shown before the rest collapse into a \"+N\" indicator. */\n max?: number\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n shape?: 'circle' | 'square'\n /** Accessible name for the group. Defaults to a plain count, not an assumption about who's in it. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<AvatarGroupProps>(), { size: 'md', shape: 'circle' })\n\nconst visible = computed(() => (props.max ? props.items.slice(0, props.max) : props.items))\nconst overflow = computed(() => Math.max(0, props.items.length - visible.value.length))\nconst groupLabel = computed(() => props.label ?? `${props.items.length} ${props.items.length === 1 ? 'avatar' : 'avatars'}`)\n</script>\n\n<template>\n <div class=\"j-avatar-group\" :class=\"`j-avatar-group--${size}`\" role=\"group\" :aria-label=\"groupLabel\">\n <span v-for=\"(item, index) in visible\" :key=\"index\" class=\"j-avatar-group__item\">\n <JAvatar :src=\"item.src\" :name=\"item.name\" :size=\"size\" :shape=\"shape\" />\n </span>\n <span\n v-if=\"overflow > 0\"\n class=\"j-avatar-group__item j-avatar-group__overflow j-focusable\"\n :class=\"{ 'j-avatar-group__overflow--square': shape === 'square' }\"\n tabindex=\"0\"\n :aria-label=\"`${overflow} more`\"\n >\n +{{ overflow }}\n </span>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-avatar-group {\n --j-avatar-group-overlap: 0.5em;\n\n display: inline-flex;\n align-items: center;\n font-family: var(--juxt-font-sans);\n}\n\n.j-avatar-group__item {\n position: relative;\n display: inline-flex;\n flex: none;\n border-radius: var(--juxt-radius-full);\n box-shadow: 0 0 0 2px var(--juxt-bg);\n}\n\n.j-avatar-group__item:not(:first-child) {\n margin-left: calc(var(--j-avatar-group-overlap) * -1);\n}\n\n.j-avatar-group__overflow {\n display: grid;\n place-items: center;\n width: 2rem;\n height: 2rem;\n background: var(--juxt-surface-active);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n font-variant-numeric: tabular-nums;\n}\n\n.j-avatar-group--xs .j-avatar-group__overflow {\n width: 1.25rem;\n height: 1.25rem;\n font-size: var(--juxt-text-2xs);\n}\n\n.j-avatar-group--sm .j-avatar-group__overflow {\n width: 1.5rem;\n height: 1.5rem;\n font-size: 0.625rem;\n}\n\n.j-avatar-group--lg .j-avatar-group__overflow {\n width: 2.5rem;\n height: 2.5rem;\n font-size: var(--juxt-text-sm);\n}\n\n.j-avatar-group--xl .j-avatar-group__overflow {\n width: 3.5rem;\n height: 3.5rem;\n font-size: var(--juxt-text-lg);\n}\n\n.j-avatar-group__overflow--square {\n border-radius: 22%;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport JAvatar from '../avatar/JAvatar.vue'\n\nexport interface AvatarGroupItem {\n src?: string\n name?: string\n}\n\nexport interface AvatarGroupProps {\n items: AvatarGroupItem[]\n /** Avatars shown before the rest collapse into a \"+N\" indicator. */\n max?: number\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n shape?: 'circle' | 'square'\n /** Accessible name for the group. Defaults to a plain count, not an assumption about who's in it. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<AvatarGroupProps>(), { size: 'md', shape: 'circle' })\n\nconst visible = computed(() => (props.max ? props.items.slice(0, props.max) : props.items))\nconst overflow = computed(() => Math.max(0, props.items.length - visible.value.length))\nconst groupLabel = computed(() => props.label ?? `${props.items.length} ${props.items.length === 1 ? 'avatar' : 'avatars'}`)\n</script>\n\n<template>\n <div class=\"j-avatar-group\" :class=\"`j-avatar-group--${size}`\" role=\"group\" :aria-label=\"groupLabel\">\n <span v-for=\"(item, index) in visible\" :key=\"index\" class=\"j-avatar-group__item\">\n <JAvatar :src=\"item.src\" :name=\"item.name\" :size=\"size\" :shape=\"shape\" />\n </span>\n <span\n v-if=\"overflow > 0\"\n class=\"j-avatar-group__item j-avatar-group__overflow j-focusable\"\n :class=\"{ 'j-avatar-group__overflow--square': shape === 'square' }\"\n tabindex=\"0\"\n :aria-label=\"`${overflow} more`\"\n >\n +{{ overflow }}\n </span>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-avatar-group {\n --j-avatar-group-overlap: 0.5em;\n\n display: inline-flex;\n align-items: center;\n font-family: var(--juxt-font-sans);\n}\n\n.j-avatar-group__item {\n position: relative;\n display: inline-flex;\n flex: none;\n border-radius: var(--juxt-radius-full);\n box-shadow: 0 0 0 2px var(--juxt-bg);\n}\n\n.j-avatar-group__item:not(:first-child) {\n margin-left: calc(var(--j-avatar-group-overlap) * -1);\n}\n\n.j-avatar-group__overflow {\n display: grid;\n place-items: center;\n width: 2rem;\n height: 2rem;\n background: var(--juxt-surface-active);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n font-variant-numeric: tabular-nums;\n}\n\n.j-avatar-group--xs .j-avatar-group__overflow {\n width: 1.25rem;\n height: 1.25rem;\n font-size: var(--juxt-text-2xs);\n}\n\n.j-avatar-group--sm .j-avatar-group__overflow {\n width: 1.5rem;\n height: 1.5rem;\n font-size: 0.625rem;\n}\n\n.j-avatar-group--lg .j-avatar-group__overflow {\n width: 2.5rem;\n height: 2.5rem;\n font-size: var(--juxt-text-sm);\n}\n\n.j-avatar-group--xl .j-avatar-group__overflow {\n width: 3.5rem;\n height: 3.5rem;\n font-size: var(--juxt-text-lg);\n}\n\n.j-avatar-group__overflow--square {\n border-radius: 22%;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport type BadgeVariant = 'neutral' | 'success' | 'warning' | 'destructive' | 'accent'\n\nexport interface BadgeProps {\n variant?: BadgeVariant\n size?: 'sm' | 'md'\n /**\n * Status style: plain text led by a small square marker, no fill.\n * Reads as a state rather than a label.\n */\n dot?: boolean\n}\n\nwithDefaults(defineProps<BadgeProps>(), { variant: 'neutral', size: 'md' })\n\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\n</script>\n\n<template>\n <span class=\"j-badge\" :class=\"[`j-badge--${variant}`, `j-badge--${size}`, { 'j-badge--status': dot }]\">\n <span v-if=\"dot\" class=\"j-badge__marker\" aria-hidden=\"true\" />\n <span v-else-if=\"$slots.leading\" class=\"j-badge__icon\"><slot name=\"leading\" /></span>\n <slot />\n </span>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n/*\n * Badges are small, square-shouldered labels | closer to a stamp than a pill.\n * No borders: a quiet tint and the text colour carry the meaning.\n */\n.j-badge {\n --j-badge-fg: var(--juxt-fg-secondary);\n --j-badge-bg: var(--juxt-surface-active);\n --j-badge-marker: var(--juxt-fg-muted);\n\n display: inline-flex;\n flex: none;\n align-items: center;\n gap: 0.375rem;\n height: 1.25rem;\n padding: 0 0.375rem;\n border-radius: var(--juxt-radius-xs);\n background: var(--j-badge-bg);\n color: var(--j-badge-fg);\n font-family: var(--juxt-font-sans);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n line-height: 1;\n letter-spacing: 0;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n}\n\n.j-badge--sm {\n gap: 0.3125rem;\n height: 1.0625rem;\n padding: 0 0.3125rem;\n font-size: var(--juxt-text-2xs);\n}\n\n.j-badge--success {\n --j-badge-fg: var(--juxt-success-text);\n --j-badge-bg: var(--juxt-success-soft);\n --j-badge-marker: var(--juxt-success);\n}\n\n.j-badge--warning {\n --j-badge-fg: var(--juxt-warning-text);\n --j-badge-bg: var(--juxt-warning-soft);\n --j-badge-marker: var(--juxt-warning);\n}\n\n.j-badge--destructive {\n --j-badge-fg: var(--juxt-danger-text);\n --j-badge-bg: var(--juxt-danger-soft);\n --j-badge-marker: var(--juxt-danger);\n}\n\n/* Accent is the only solid badge | reserve it for the thing that matters. */\n.j-badge--accent {\n --j-badge-fg: var(--juxt-accent-fg);\n --j-badge-bg: var(--juxt-accent-strong);\n --j-badge-marker: var(--juxt-accent);\n}\n\n/* Status: text first, colour only in the marker. */\n.j-badge--status {\n padding: 0;\n background: none;\n color: var(--juxt-fg-secondary);\n}\n\n.j-badge__marker {\n flex: none;\n width: 0.4375rem;\n height: 0.4375rem;\n border-radius: 1.5px;\n background: var(--j-badge-marker);\n}\n\n.j-badge--sm .j-badge__marker {\n width: 0.375rem;\n height: 0.375rem;\n}\n\n.j-badge__icon {\n display: inline-flex;\n margin-left: -0.0625rem;\n}\n\n.j-badge__icon > svg {\n width: 0.75rem;\n height: 0.75rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport type BadgeVariant = 'neutral' | 'success' | 'warning' | 'destructive' | 'accent'\n\nexport interface BadgeProps {\n variant?: BadgeVariant\n size?: 'sm' | 'md'\n /**\n * Status style: plain text led by a small square marker, no fill.\n * Reads as a state rather than a label.\n */\n dot?: boolean\n}\n\nwithDefaults(defineProps<BadgeProps>(), { variant: 'neutral', size: 'md' })\n\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\n</script>\n\n<template>\n <span class=\"j-badge\" :class=\"[`j-badge--${variant}`, `j-badge--${size}`, { 'j-badge--status': dot }]\">\n <span v-if=\"dot\" class=\"j-badge__marker\" aria-hidden=\"true\" />\n <span v-else-if=\"$slots.leading\" class=\"j-badge__icon\"><slot name=\"leading\" /></span>\n <slot />\n </span>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n/*\n * Badges are small, square-shouldered labels | closer to a stamp than a pill.\n * No borders: a quiet tint and the text colour carry the meaning.\n */\n.j-badge {\n --j-badge-fg: var(--juxt-fg-secondary);\n --j-badge-bg: var(--juxt-surface-active);\n --j-badge-marker: var(--juxt-fg-muted);\n\n display: inline-flex;\n flex: none;\n align-items: center;\n gap: 0.375rem;\n height: 1.25rem;\n padding: 0 0.375rem;\n border-radius: var(--juxt-radius-xs);\n background: var(--j-badge-bg);\n color: var(--j-badge-fg);\n font-family: var(--juxt-font-sans);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n line-height: 1;\n letter-spacing: 0;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n}\n\n.j-badge--sm {\n gap: 0.3125rem;\n height: 1.0625rem;\n padding: 0 0.3125rem;\n font-size: var(--juxt-text-2xs);\n}\n\n.j-badge--success {\n --j-badge-fg: var(--juxt-success-text);\n --j-badge-bg: var(--juxt-success-soft);\n --j-badge-marker: var(--juxt-success);\n}\n\n.j-badge--warning {\n --j-badge-fg: var(--juxt-warning-text);\n --j-badge-bg: var(--juxt-warning-soft);\n --j-badge-marker: var(--juxt-warning);\n}\n\n.j-badge--destructive {\n --j-badge-fg: var(--juxt-danger-text);\n --j-badge-bg: var(--juxt-danger-soft);\n --j-badge-marker: var(--juxt-danger);\n}\n\n/* Accent is the only solid badge | reserve it for the thing that matters. */\n.j-badge--accent {\n --j-badge-fg: var(--juxt-accent-fg);\n --j-badge-bg: var(--juxt-accent-strong);\n --j-badge-marker: var(--juxt-accent);\n}\n\n/* Status: text first, colour only in the marker. */\n.j-badge--status {\n padding: 0;\n background: none;\n color: var(--juxt-fg-secondary);\n}\n\n.j-badge__marker {\n flex: none;\n width: 0.4375rem;\n height: 0.4375rem;\n border-radius: 1.5px;\n background: var(--j-badge-marker);\n}\n\n.j-badge--sm .j-badge__marker {\n width: 0.375rem;\n height: 0.375rem;\n}\n\n.j-badge__icon {\n display: inline-flex;\n margin-left: -0.0625rem;\n}\n\n.j-badge__icon > svg {\n width: 0.75rem;\n height: 0.75rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX } from '../../icons'\n\nexport type BannerVariant = 'neutral' | 'info' | 'success' | 'warning' | 'destructive'\n\nexport interface BannerProps {\n variant?: BannerVariant\n title?: string\n description?: string\n /** Show a close button; emits `dismiss`. */\n dismissible?: boolean\n /** Hide the leading icon. */\n hideIcon?: boolean\n}\n\nwithDefaults(defineProps<BannerProps>(), { variant: 'neutral' })\n\nconst emit = defineEmits<{ dismiss: [] }>()\n\ndefineSlots<{\n default?: () => unknown\n icon?: () => unknown\n actions?: () => unknown\n}>()\n\nconst icons = {\n neutral: IconInfo,\n info: IconInfo,\n success: IconCircleCheck,\n warning: IconAlert,\n destructive: IconCircleX,\n}\n</script>\n\n<template>\n <div class=\"j-banner\" :class=\"`j-banner--${variant}`\" role=\"region\" :aria-label=\"title\">\n <span v-if=\"!hideIcon\" class=\"j-banner__icon\" aria-hidden=\"true\">\n <slot name=\"icon\"><component :is=\"icons[variant]\" /></slot>\n </span>\n <div class=\"j-banner__body\">\n <p v-if=\"title\" class=\"j-banner__title\">\n {{ title }}\n </p>\n <div v-if=\"description || $slots.default\" class=\"j-banner__description\">\n <slot>{{ description }}</slot>\n </div>\n </div>\n <div v-if=\"$slots.actions\" class=\"j-banner__actions\">\n <slot name=\"actions\" />\n </div>\n <button v-if=\"dismissible\" type=\"button\" class=\"j-banner__close j-focusable\" aria-label=\"Dismiss\" @click=\"emit('dismiss')\">\n <IconX />\n </button>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n/*\n * Banners run edge-to-edge across a page or section, unlike JAlert which sits\n * inline in a card or form. A flat tinted surface and a left accent bar\n * distinguish them from the bordered, neutral surface of an alert.\n */\n.j-banner {\n --j-banner-accent: var(--juxt-fg-muted);\n --j-banner-bg: var(--juxt-surface-sunken);\n\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--juxt-space-3);\n padding: var(--juxt-space-3) var(--juxt-space-5);\n border-left: 3px solid var(--j-banner-accent);\n background: var(--j-banner-bg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-banner--info {\n --j-banner-accent: var(--juxt-info);\n --j-banner-bg: var(--juxt-info-soft);\n}\n\n.j-banner--success {\n --j-banner-accent: var(--juxt-success);\n --j-banner-bg: var(--juxt-success-soft);\n}\n\n.j-banner--warning {\n --j-banner-accent: var(--juxt-warning);\n --j-banner-bg: var(--juxt-warning-soft);\n}\n\n.j-banner--destructive {\n --j-banner-accent: var(--juxt-danger);\n --j-banner-bg: var(--juxt-danger-soft);\n}\n\n.j-banner__icon {\n display: grid;\n flex: none;\n place-items: center;\n height: 1.25rem;\n color: var(--j-banner-accent);\n}\n\n.j-banner__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-banner__body {\n display: flex;\n flex: 1;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--juxt-space-1) var(--juxt-space-2);\n min-width: 0;\n}\n\n.j-banner__title {\n margin: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-banner__description {\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-banner__description > p {\n margin: 0;\n}\n\n.j-banner__actions {\n display: flex;\n flex: none;\n flex-wrap: wrap;\n gap: var(--juxt-space-2);\n}\n\n.j-banner__close {\n display: grid;\n flex: none;\n place-items: center;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n color: var(--juxt-fg-muted);\n cursor: pointer;\n transition-property: background-color, color, outline-color, outline-offset;\n}\n\n.j-banner__close:hover {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-banner__close > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX } from '../../icons'\n\nexport type BannerVariant = 'neutral' | 'info' | 'success' | 'warning' | 'destructive'\n\nexport interface BannerProps {\n variant?: BannerVariant\n title?: string\n description?: string\n /** Show a close button; emits `dismiss`. */\n dismissible?: boolean\n /** Hide the leading icon. */\n hideIcon?: boolean\n}\n\nwithDefaults(defineProps<BannerProps>(), { variant: 'neutral' })\n\nconst emit = defineEmits<{ dismiss: [] }>()\n\ndefineSlots<{\n default?: () => unknown\n icon?: () => unknown\n actions?: () => unknown\n}>()\n\nconst icons = {\n neutral: IconInfo,\n info: IconInfo,\n success: IconCircleCheck,\n warning: IconAlert,\n destructive: IconCircleX,\n}\n</script>\n\n<template>\n <div class=\"j-banner\" :class=\"`j-banner--${variant}`\" role=\"region\" :aria-label=\"title\">\n <span v-if=\"!hideIcon\" class=\"j-banner__icon\" aria-hidden=\"true\">\n <slot name=\"icon\"><component :is=\"icons[variant]\" /></slot>\n </span>\n <div class=\"j-banner__body\">\n <p v-if=\"title\" class=\"j-banner__title\">\n {{ title }}\n </p>\n <div v-if=\"description || $slots.default\" class=\"j-banner__description\">\n <slot>{{ description }}</slot>\n </div>\n </div>\n <div v-if=\"$slots.actions\" class=\"j-banner__actions\">\n <slot name=\"actions\" />\n </div>\n <button v-if=\"dismissible\" type=\"button\" class=\"j-banner__close j-focusable\" aria-label=\"Dismiss\" @click=\"emit('dismiss')\">\n <IconX />\n </button>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n/*\n * Banners run edge-to-edge across a page or section, unlike JAlert which sits\n * inline in a card or form. A flat tinted surface and a left accent bar\n * distinguish them from the bordered, neutral surface of an alert.\n */\n.j-banner {\n --j-banner-accent: var(--juxt-fg-muted);\n --j-banner-bg: var(--juxt-surface-sunken);\n\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--juxt-space-3);\n padding: var(--juxt-space-3) var(--juxt-space-5);\n border-left: 3px solid var(--j-banner-accent);\n background: var(--j-banner-bg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-banner--info {\n --j-banner-accent: var(--juxt-info);\n --j-banner-bg: var(--juxt-info-soft);\n}\n\n.j-banner--success {\n --j-banner-accent: var(--juxt-success);\n --j-banner-bg: var(--juxt-success-soft);\n}\n\n.j-banner--warning {\n --j-banner-accent: var(--juxt-warning);\n --j-banner-bg: var(--juxt-warning-soft);\n}\n\n.j-banner--destructive {\n --j-banner-accent: var(--juxt-danger);\n --j-banner-bg: var(--juxt-danger-soft);\n}\n\n.j-banner__icon {\n display: grid;\n flex: none;\n place-items: center;\n height: 1.25rem;\n color: var(--j-banner-accent);\n}\n\n.j-banner__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-banner__body {\n display: flex;\n flex: 1;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--juxt-space-1) var(--juxt-space-2);\n min-width: 0;\n}\n\n.j-banner__title {\n margin: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-banner__description {\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-banner__description > p {\n margin: 0;\n}\n\n.j-banner__actions {\n display: flex;\n flex: none;\n flex-wrap: wrap;\n gap: var(--juxt-space-2);\n}\n\n.j-banner__close {\n display: grid;\n flex: none;\n place-items: center;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n color: var(--juxt-fg-muted);\n cursor: pointer;\n transition-property: background-color, color, outline-color, outline-offset;\n}\n\n.j-banner__close:hover {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-banner__close > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nexport interface BreadcrumbItem {\n label: string\n href?: string\n}\n\nexport interface BreadcrumbProps {\n /** Trail of items, first to last. The last item renders as the current page. */\n items?: BreadcrumbItem[]\n /** Accessible name for the nav landmark. Defaults to \"Breadcrumb\". */\n label?: string\n /** Collapse middle items behind an ellipsis once the trail is longer than this. */\n maxItems?: number\n}\n\nconst props = defineProps<BreadcrumbProps>()\n\n/**\n * Provide `items` for the common case, or omit it and pass your own `<li>`\n * elements (with class `j-breadcrumb__item`) into the default slot for full\n * control over each crumb.\n */\ndefineSlots<{ default?: () => unknown }>()\n\ninterface ResolvedItem extends BreadcrumbItem {\n ellipsis?: boolean\n}\n\nconst visibleItems = computed<ResolvedItem[]>(() => {\n const list = props.items ?? []\n if (!props.maxItems || list.length <= props.maxItems) return list\n const tailCount = Math.max(1, props.maxItems - 2)\n return [list[0]!, { label: '…', ellipsis: true }, ...list.slice(-tailCount)]\n})\n</script>\n\n<template>\n <nav class=\"j-breadcrumb\" :aria-label=\"label || 'Breadcrumb'\">\n <ol class=\"j-breadcrumb__list\">\n <template v-if=\"items && items.length\">\n <li v-for=\"(item, index) in visibleItems\" :key=\"`${item.label}-${index}`\" class=\"j-breadcrumb__item\">\n <span v-if=\"item.ellipsis\" class=\"j-breadcrumb__ellipsis\" aria-hidden=\"true\">{{ item.label }}</span>\n <a v-else-if=\"item.href && index !== visibleItems.length - 1\" class=\"j-breadcrumb__link j-focusable\" :href=\"item.href\">{{ item.label }}</a>\n <span v-else class=\"j-breadcrumb__current\" :aria-current=\"index === visibleItems.length - 1 ? 'page' : undefined\">{{ item.label }}</span>\n </li>\n </template>\n <slot v-else />\n </ol>\n </nav>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-breadcrumb {\n font-family: var(--juxt-font-sans);\n}\n\n.j-breadcrumb__list {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--juxt-space-1-5);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.j-breadcrumb__item {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1-5);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-snug);\n}\n\n.j-breadcrumb__item:not(:first-child)::before {\n content: '';\n display: inline-block;\n width: 0.3125rem;\n height: 0.3125rem;\n margin-right: var(--juxt-space-1-5);\n border-top: 1.5px solid var(--juxt-fg-disabled);\n border-right: 1.5px solid var(--juxt-fg-disabled);\n transform: rotate(45deg);\n}\n\n.j-breadcrumb__link {\n border-radius: var(--juxt-radius-xs);\n color: var(--juxt-fg-muted);\n text-decoration: none;\n transition: color var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-breadcrumb__link:hover {\n color: var(--juxt-fg);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n\n.j-breadcrumb__current {\n color: var(--juxt-fg);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-breadcrumb__ellipsis {\n color: var(--juxt-fg-disabled);\n user-select: none;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nexport interface BreadcrumbItem {\n label: string\n href?: string\n}\n\nexport interface BreadcrumbProps {\n /** Trail of items, first to last. The last item renders as the current page. */\n items?: BreadcrumbItem[]\n /** Accessible name for the nav landmark. Defaults to \"Breadcrumb\". */\n label?: string\n /** Collapse middle items behind an ellipsis once the trail is longer than this. */\n maxItems?: number\n}\n\nconst props = defineProps<BreadcrumbProps>()\n\n/**\n * Provide `items` for the common case, or omit it and pass your own `<li>`\n * elements (with class `j-breadcrumb__item`) into the default slot for full\n * control over each crumb.\n */\ndefineSlots<{ default?: () => unknown }>()\n\ninterface ResolvedItem extends BreadcrumbItem {\n ellipsis?: boolean\n}\n\nconst visibleItems = computed<ResolvedItem[]>(() => {\n const list = props.items ?? []\n if (!props.maxItems || list.length <= props.maxItems) return list\n const tailCount = Math.max(1, props.maxItems - 2)\n return [list[0]!, { label: '…', ellipsis: true }, ...list.slice(-tailCount)]\n})\n</script>\n\n<template>\n <nav class=\"j-breadcrumb\" :aria-label=\"label || 'Breadcrumb'\">\n <ol class=\"j-breadcrumb__list\">\n <template v-if=\"items && items.length\">\n <li v-for=\"(item, index) in visibleItems\" :key=\"`${item.label}-${index}`\" class=\"j-breadcrumb__item\">\n <span v-if=\"item.ellipsis\" class=\"j-breadcrumb__ellipsis\" aria-hidden=\"true\">{{ item.label }}</span>\n <a v-else-if=\"item.href && index !== visibleItems.length - 1\" class=\"j-breadcrumb__link j-focusable\" :href=\"item.href\">{{ item.label }}</a>\n <span v-else class=\"j-breadcrumb__current\" :aria-current=\"index === visibleItems.length - 1 ? 'page' : undefined\">{{ item.label }}</span>\n </li>\n </template>\n <slot v-else />\n </ol>\n </nav>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-breadcrumb {\n font-family: var(--juxt-font-sans);\n}\n\n.j-breadcrumb__list {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--juxt-space-1-5);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.j-breadcrumb__item {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1-5);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-snug);\n}\n\n.j-breadcrumb__item:not(:first-child)::before {\n content: '';\n display: inline-block;\n width: 0.3125rem;\n height: 0.3125rem;\n margin-right: var(--juxt-space-1-5);\n border-top: 1.5px solid var(--juxt-fg-disabled);\n border-right: 1.5px solid var(--juxt-fg-disabled);\n transform: rotate(45deg);\n}\n\n.j-breadcrumb__link {\n border-radius: var(--juxt-radius-xs);\n color: var(--juxt-fg-muted);\n text-decoration: none;\n transition: color var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-breadcrumb__link:hover {\n color: var(--juxt-fg);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n\n.j-breadcrumb__current {\n color: var(--juxt-fg);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-breadcrumb__ellipsis {\n color: var(--juxt-fg-disabled);\n user-select: none;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport interface BurgerProps {\n /** Accessible name while closed. */\n openLabel?: string\n /** Accessible name while open. */\n closeLabel?: string\n size?: 'sm' | 'md'\n /** Id of the element this button shows and hides. */\n controls?: string\n}\n\nwithDefaults(defineProps<BurgerProps>(), {\n openLabel: 'Open menu',\n closeLabel: 'Close menu',\n size: 'md',\n})\n\nconst open = defineModel<boolean>({ default: false })\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"j-burger j-focusable\"\n :class=\"`j-burger--${size}`\"\n :data-state=\"open ? 'open' : 'closed'\"\n :aria-expanded=\"open\"\n :aria-controls=\"controls\"\n :aria-label=\"open ? closeLabel : openLabel\"\n @click=\"open = !open\"\n >\n <span class=\"j-burger__lines\" aria-hidden=\"true\">\n <span class=\"j-burger__line\" />\n <span class=\"j-burger__line\" />\n <span class=\"j-burger__line\" />\n </span>\n </button>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-burger {\n --j-burger-size: var(--juxt-control-md);\n --j-burger-width: 1rem;\n --j-burger-gap: 0.3125rem;\n\n display: inline-grid;\n place-items: center;\n width: var(--j-burger-size);\n height: var(--j-burger-size);\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: background-color, outline-color, outline-offset;\n}\n\n.j-burger--sm {\n --j-burger-size: var(--juxt-control-sm);\n --j-burger-width: 0.875rem;\n --j-burger-gap: 0.25rem;\n}\n\n.j-burger:hover {\n background: var(--juxt-surface-hover);\n}\n\n.j-burger__lines {\n position: relative;\n display: block;\n width: var(--j-burger-width);\n height: calc(var(--j-burger-gap) * 2 + 1.5px);\n}\n\n.j-burger__line {\n position: absolute;\n left: 0;\n width: 100%;\n height: 1.5px;\n border-radius: 1px;\n background: currentColor;\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-burger__line:nth-child(1) {\n top: 0;\n}\n\n.j-burger__line:nth-child(2) {\n top: var(--j-burger-gap);\n}\n\n.j-burger__line:nth-child(3) {\n top: calc(var(--j-burger-gap) * 2);\n}\n\n/* Outer lines meet in the middle and cross; the middle one fades. */\n.j-burger[data-state='open'] .j-burger__line:nth-child(1) {\n transform: translateY(var(--j-burger-gap)) rotate(45deg);\n}\n\n.j-burger[data-state='open'] .j-burger__line:nth-child(2) {\n opacity: 0;\n transform: scaleX(0.4);\n}\n\n.j-burger[data-state='open'] .j-burger__line:nth-child(3) {\n transform: translateY(calc(var(--j-burger-gap) * -1)) rotate(-45deg);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-burger__line {\n transition-duration: 0ms;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport interface BurgerProps {\n /** Accessible name while closed. */\n openLabel?: string\n /** Accessible name while open. */\n closeLabel?: string\n size?: 'sm' | 'md'\n /** Id of the element this button shows and hides. */\n controls?: string\n}\n\nwithDefaults(defineProps<BurgerProps>(), {\n openLabel: 'Open menu',\n closeLabel: 'Close menu',\n size: 'md',\n})\n\nconst open = defineModel<boolean>({ default: false })\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"j-burger j-focusable\"\n :class=\"`j-burger--${size}`\"\n :data-state=\"open ? 'open' : 'closed'\"\n :aria-expanded=\"open\"\n :aria-controls=\"controls\"\n :aria-label=\"open ? closeLabel : openLabel\"\n @click=\"open = !open\"\n >\n <span class=\"j-burger__lines\" aria-hidden=\"true\">\n <span class=\"j-burger__line\" />\n <span class=\"j-burger__line\" />\n <span class=\"j-burger__line\" />\n </span>\n </button>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-burger {\n --j-burger-size: var(--juxt-control-md);\n --j-burger-width: 1rem;\n --j-burger-gap: 0.3125rem;\n\n display: inline-grid;\n place-items: center;\n width: var(--j-burger-size);\n height: var(--j-burger-size);\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: background-color, outline-color, outline-offset;\n}\n\n.j-burger--sm {\n --j-burger-size: var(--juxt-control-sm);\n --j-burger-width: 0.875rem;\n --j-burger-gap: 0.25rem;\n}\n\n.j-burger:hover {\n background: var(--juxt-surface-hover);\n}\n\n.j-burger__lines {\n position: relative;\n display: block;\n width: var(--j-burger-width);\n height: calc(var(--j-burger-gap) * 2 + 1.5px);\n}\n\n.j-burger__line {\n position: absolute;\n left: 0;\n width: 100%;\n height: 1.5px;\n border-radius: 1px;\n background: currentColor;\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-burger__line:nth-child(1) {\n top: 0;\n}\n\n.j-burger__line:nth-child(2) {\n top: var(--j-burger-gap);\n}\n\n.j-burger__line:nth-child(3) {\n top: calc(var(--j-burger-gap) * 2);\n}\n\n/* Outer lines meet in the middle and cross; the middle one fades. */\n.j-burger[data-state='open'] .j-burger__line:nth-child(1) {\n transform: translateY(var(--j-burger-gap)) rotate(45deg);\n}\n\n.j-burger[data-state='open'] .j-burger__line:nth-child(2) {\n opacity: 0;\n transform: scaleX(0.4);\n}\n\n.j-burger[data-state='open'] .j-burger__line:nth-child(3) {\n transform: translateY(calc(var(--j-burger-gap) * -1)) rotate(-45deg);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-burger__line {\n transition-duration: 0ms;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\nimport { computed } from 'vue'\nimport { Spinner } from '../../icons'\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive'\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg'\n\nexport interface ButtonProps {\n /** Visual weight. `primary` is for the single most important action in a view. */\n variant?: ButtonVariant\n size?: ButtonSize\n /** Shows a spinner, keeps the width, and blocks activation while staying focusable. */\n loading?: boolean\n disabled?: boolean\n /** Square button for a single icon. Provide an `aria-label`. */\n icon?: boolean\n /** Stretch to the full width of the container. */\n block?: boolean\n /** Render as another element or component, e.g. `a` or `NuxtLink`. */\n as?: string | Component\n type?: 'button' | 'submit' | 'reset'\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n variant: 'primary',\n size: 'md',\n as: 'button',\n type: 'button',\n})\n\ndefineSlots<{\n default?: () => unknown\n leading?: () => unknown\n trailing?: () => unknown\n}>()\n\nconst isNativeButton = computed(() => props.as === 'button')\nconst inert = computed(() => props.disabled || props.loading)\n\nconst attrs = computed(() => {\n if (isNativeButton.value) {\n return {\n type: props.type,\n // A loading button stays focusable so keyboard users don't lose their place.\n 'disabled': props.disabled || undefined,\n 'aria-disabled': props.loading || undefined,\n }\n }\n return {\n 'aria-disabled': inert.value || undefined,\n 'tabindex': props.disabled ? -1 : undefined,\n }\n})\n\nfunction onClick(event: MouseEvent) {\n if (inert.value) {\n event.preventDefault()\n event.stopImmediatePropagation()\n }\n}\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"attrs\"\n class=\"j-button j-focusable\"\n :class=\"[\n `j-button--${variant}`,\n `j-button--${size}`,\n { 'j-button--icon': icon, 'j-button--block': block, 'is-loading': loading },\n ]\"\n :aria-busy=\"loading || undefined\"\n :data-disabled=\"disabled || undefined\"\n @click.capture=\"onClick\"\n >\n <span class=\"j-button__content\">\n <span v-if=\"$slots.leading\" class=\"j-button__icon\"><slot name=\"leading\" /></span>\n <slot />\n <span v-if=\"$slots.trailing\" class=\"j-button__icon\"><slot name=\"trailing\" /></span>\n </span>\n <Transition name=\"j-button-spinner\">\n <span v-if=\"loading\" class=\"j-button__spinner\">\n <Spinner :size=\"size === 'xs' ? 12 : size === 'lg' ? 16 : 14\" />\n </span>\n </Transition>\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-button {\n --j-button-height: var(--juxt-control-md);\n --j-button-px: var(--juxt-space-3);\n --j-button-gap: var(--juxt-space-1-5);\n --j-button-font: var(--juxt-text-md);\n --j-button-radius: var(--juxt-radius-sm);\n\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: var(--j-button-height);\n padding: 0 var(--j-button-px);\n border: 1px solid transparent;\n border-radius: var(--j-button-radius);\n font-family: var(--juxt-font-sans);\n font-size: var(--j-button-font);\n font-weight: var(--juxt-weight-medium);\n line-height: 1;\n letter-spacing: var(--juxt-tracking-tight);\n white-space: nowrap;\n text-decoration: none;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n touch-action: manipulation;\n transition-property: background-color, border-color, color, box-shadow, transform, outline-color, outline-offset;\n transition-duration: var(--juxt-duration-fast);\n transition-timing-function: var(--juxt-ease-standard);\n}\n\n/* Pressed: a short, physical dip. Released slower than pressed. */\n.j-button:active:not([aria-disabled='true'], :disabled) {\n transform: scale(0.97);\n transition-duration: var(--juxt-duration-instant);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-button:active:not([aria-disabled='true'], :disabled) {\n transform: none;\n }\n}\n\n.j-button__content {\n display: inline-flex;\n align-items: center;\n gap: var(--j-button-gap);\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard),\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-button__icon {\n display: inline-flex;\n margin-inline: -0.125rem;\n opacity: 0.85;\n}\n\n.j-button__icon > svg {\n width: 1em;\n height: 1em;\n}\n\n.j-button.is-loading {\n cursor: progress;\n}\n\n.j-button.is-loading .j-button__content {\n opacity: 0;\n transform: scale(0.96);\n}\n\n.j-button__spinner {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n}\n\n.j-button-spinner-enter-active {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out) 40ms,\n transform var(--juxt-duration-normal) var(--juxt-ease-out) 40ms;\n}\n\n.j-button-spinner-leave-active {\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\n}\n\n.j-button-spinner-enter-from,\n.j-button-spinner-leave-to {\n opacity: 0;\n transform: scale(0.8);\n}\n\n/* Sizes */\n.j-button--xs {\n --j-button-height: var(--juxt-control-xs);\n --j-button-px: var(--juxt-space-2);\n --j-button-gap: var(--juxt-space-1);\n --j-button-font: var(--juxt-text-xs);\n --j-button-radius: var(--juxt-radius-xs);\n}\n\n.j-button--sm {\n --j-button-height: var(--juxt-control-sm);\n --j-button-px: var(--juxt-space-2-5);\n --j-button-gap: var(--juxt-space-1-5);\n --j-button-font: var(--juxt-text-sm);\n}\n\n.j-button--lg {\n --j-button-height: var(--juxt-control-lg);\n --j-button-px: var(--juxt-space-4);\n --j-button-gap: var(--juxt-space-2);\n --j-button-font: var(--juxt-text-md);\n --j-button-radius: var(--juxt-radius-md);\n}\n\n.j-button--icon {\n width: var(--j-button-height);\n padding: 0;\n}\n\n.j-button--icon .j-button__content > svg,\n.j-button--icon .j-button__content > :where(span, i) > svg {\n width: calc(var(--j-button-height) * 0.46);\n height: calc(var(--j-button-height) * 0.46);\n}\n\n.j-button--block {\n display: flex;\n width: 100%;\n}\n\n/* Variants */\n.j-button--primary {\n background: var(--juxt-ink);\n color: var(--juxt-ink-fg);\n box-shadow: var(--juxt-shadow-xs), var(--juxt-control-highlight);\n}\n\n.j-button--primary:hover:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-ink-hover);\n}\n\n.j-button--secondary {\n background: var(--juxt-surface-sunken);\n border-color: var(--juxt-border-subtle);\n color: var(--juxt-fg);\n}\n\n.dark .j-button--secondary,\n[data-theme='dark'] .j-button--secondary {\n background: var(--juxt-surface-raised);\n border-color: var(--juxt-border-subtle);\n}\n\n.j-button--secondary:hover:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-surface-active);\n}\n\n.j-button--outline {\n background: var(--juxt-surface);\n border-color: var(--juxt-border);\n color: var(--juxt-fg);\n box-shadow: var(--juxt-shadow-xs);\n}\n\n.j-button--outline:hover:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-surface-hover);\n border-color: var(--juxt-border-strong);\n}\n\n.j-button--ghost {\n background: transparent;\n color: var(--juxt-fg-secondary);\n}\n\n.j-button--ghost:hover:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-button--ghost:active:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-surface-active);\n}\n\n.j-button--destructive {\n background: var(--juxt-danger);\n color: var(--juxt-danger-fg);\n box-shadow: var(--juxt-shadow-xs), var(--juxt-control-highlight);\n}\n\n.j-button--destructive:hover:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-danger-hover);\n}\n\n.j-button--destructive:focus-visible {\n outline-color: var(--juxt-danger);\n}\n\n/* Disabled: quiet, not ghostly. */\n.j-button:disabled,\n.j-button[data-disabled] {\n cursor: not-allowed;\n opacity: 0.45;\n box-shadow: none;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\nimport { computed } from 'vue'\nimport { Spinner } from '../../icons'\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive'\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg'\n\nexport interface ButtonProps {\n /** Visual weight. `primary` is for the single most important action in a view. */\n variant?: ButtonVariant\n size?: ButtonSize\n /** Shows a spinner, keeps the width, and blocks activation while staying focusable. */\n loading?: boolean\n disabled?: boolean\n /** Square button for a single icon. Provide an `aria-label`. */\n icon?: boolean\n /** Stretch to the full width of the container. */\n block?: boolean\n /** Render as another element or component, e.g. `a` or `NuxtLink`. */\n as?: string | Component\n type?: 'button' | 'submit' | 'reset'\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n variant: 'primary',\n size: 'md',\n as: 'button',\n type: 'button',\n})\n\ndefineSlots<{\n default?: () => unknown\n leading?: () => unknown\n trailing?: () => unknown\n}>()\n\nconst isNativeButton = computed(() => props.as === 'button')\nconst inert = computed(() => props.disabled || props.loading)\n\nconst attrs = computed(() => {\n if (isNativeButton.value) {\n return {\n type: props.type,\n // A loading button stays focusable so keyboard users don't lose their place.\n 'disabled': props.disabled || undefined,\n 'aria-disabled': props.loading || undefined,\n }\n }\n return {\n 'aria-disabled': inert.value || undefined,\n 'tabindex': props.disabled ? -1 : undefined,\n }\n})\n\nfunction onClick(event: MouseEvent) {\n if (inert.value) {\n event.preventDefault()\n event.stopImmediatePropagation()\n }\n}\n</script>\n\n<template>\n <component\n :is=\"as\"\n v-bind=\"attrs\"\n class=\"j-button j-focusable\"\n :class=\"[\n `j-button--${variant}`,\n `j-button--${size}`,\n { 'j-button--icon': icon, 'j-button--block': block, 'is-loading': loading },\n ]\"\n :aria-busy=\"loading || undefined\"\n :data-disabled=\"disabled || undefined\"\n @click.capture=\"onClick\"\n >\n <span class=\"j-button__content\">\n <span v-if=\"$slots.leading\" class=\"j-button__icon\"><slot name=\"leading\" /></span>\n <slot />\n <span v-if=\"$slots.trailing\" class=\"j-button__icon\"><slot name=\"trailing\" /></span>\n </span>\n <Transition name=\"j-button-spinner\">\n <span v-if=\"loading\" class=\"j-button__spinner\">\n <Spinner :size=\"size === 'xs' ? 12 : size === 'lg' ? 16 : 14\" />\n </span>\n </Transition>\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-button {\n --j-button-height: var(--juxt-control-md);\n --j-button-px: var(--juxt-space-3);\n --j-button-gap: var(--juxt-space-1-5);\n --j-button-font: var(--juxt-text-md);\n --j-button-radius: var(--juxt-radius-sm);\n\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: var(--j-button-height);\n padding: 0 var(--j-button-px);\n border: 1px solid transparent;\n border-radius: var(--j-button-radius);\n font-family: var(--juxt-font-sans);\n font-size: var(--j-button-font);\n font-weight: var(--juxt-weight-medium);\n line-height: 1;\n letter-spacing: var(--juxt-tracking-tight);\n white-space: nowrap;\n text-decoration: none;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n touch-action: manipulation;\n transition-property: background-color, border-color, color, box-shadow, transform, outline-color, outline-offset;\n transition-duration: var(--juxt-duration-fast);\n transition-timing-function: var(--juxt-ease-standard);\n}\n\n/* Pressed: a short, physical dip. Released slower than pressed. */\n.j-button:active:not([aria-disabled='true'], :disabled) {\n transform: scale(0.97);\n transition-duration: var(--juxt-duration-instant);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-button:active:not([aria-disabled='true'], :disabled) {\n transform: none;\n }\n}\n\n.j-button__content {\n display: inline-flex;\n align-items: center;\n gap: var(--j-button-gap);\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard),\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-button__icon {\n display: inline-flex;\n margin-inline: -0.125rem;\n opacity: 0.85;\n}\n\n.j-button__icon > svg {\n width: 1em;\n height: 1em;\n}\n\n.j-button.is-loading {\n cursor: progress;\n}\n\n.j-button.is-loading .j-button__content {\n opacity: 0;\n transform: scale(0.96);\n}\n\n.j-button__spinner {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n}\n\n.j-button-spinner-enter-active {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out) 40ms,\n transform var(--juxt-duration-normal) var(--juxt-ease-out) 40ms;\n}\n\n.j-button-spinner-leave-active {\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\n}\n\n.j-button-spinner-enter-from,\n.j-button-spinner-leave-to {\n opacity: 0;\n transform: scale(0.8);\n}\n\n/* Sizes */\n.j-button--xs {\n --j-button-height: var(--juxt-control-xs);\n --j-button-px: var(--juxt-space-2);\n --j-button-gap: var(--juxt-space-1);\n --j-button-font: var(--juxt-text-xs);\n --j-button-radius: var(--juxt-radius-xs);\n}\n\n.j-button--sm {\n --j-button-height: var(--juxt-control-sm);\n --j-button-px: var(--juxt-space-2-5);\n --j-button-gap: var(--juxt-space-1-5);\n --j-button-font: var(--juxt-text-sm);\n}\n\n.j-button--lg {\n --j-button-height: var(--juxt-control-lg);\n --j-button-px: var(--juxt-space-4);\n --j-button-gap: var(--juxt-space-2);\n --j-button-font: var(--juxt-text-md);\n --j-button-radius: var(--juxt-radius-md);\n}\n\n.j-button--icon {\n width: var(--j-button-height);\n padding: 0;\n}\n\n.j-button--icon .j-button__content > svg,\n.j-button--icon .j-button__content > :where(span, i) > svg {\n width: calc(var(--j-button-height) * 0.46);\n height: calc(var(--j-button-height) * 0.46);\n}\n\n.j-button--block {\n display: flex;\n width: 100%;\n}\n\n/* Variants */\n.j-button--primary {\n background: var(--juxt-ink);\n color: var(--juxt-ink-fg);\n box-shadow: var(--juxt-shadow-xs), var(--juxt-control-highlight);\n}\n\n.j-button--primary:hover:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-ink-hover);\n}\n\n.j-button--secondary {\n background: var(--juxt-surface-sunken);\n border-color: var(--juxt-border-subtle);\n color: var(--juxt-fg);\n}\n\n.dark .j-button--secondary,\n[data-theme='dark'] .j-button--secondary {\n background: var(--juxt-surface-raised);\n border-color: var(--juxt-border-subtle);\n}\n\n.j-button--secondary:hover:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-surface-active);\n}\n\n.j-button--outline {\n background: var(--juxt-surface);\n border-color: var(--juxt-border);\n color: var(--juxt-fg);\n box-shadow: var(--juxt-shadow-xs);\n}\n\n.j-button--outline:hover:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-surface-hover);\n border-color: var(--juxt-border-strong);\n}\n\n.j-button--ghost {\n background: transparent;\n color: var(--juxt-fg-secondary);\n}\n\n.j-button--ghost:hover:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-button--ghost:active:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-surface-active);\n}\n\n.j-button--destructive {\n background: var(--juxt-danger);\n color: var(--juxt-danger-fg);\n box-shadow: var(--juxt-shadow-xs), var(--juxt-control-highlight);\n}\n\n.j-button--destructive:hover:not([aria-disabled='true'], :disabled) {\n background: var(--juxt-danger-hover);\n}\n\n.j-button--destructive:focus-visible {\n outline-color: var(--juxt-danger);\n}\n\n/* Disabled: quiet, not ghostly. */\n.j-button:disabled,\n.j-button[data-disabled] {\n cursor: not-allowed;\n opacity: 0.45;\n box-shadow: none;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface CardProps {\n title?: string\n description?: string\n /** `outline` sits on the page; `muted` recedes into it. */\n variant?: 'outline' | 'muted'\n padding?: 'none' | 'sm' | 'md' | 'lg'\n as?: string | Component\n}\n\nwithDefaults(defineProps<CardProps>(), { variant: 'outline', padding: 'md', as: 'div' })\n\ndefineSlots<{\n default?: () => unknown\n header?: () => unknown\n /** Sits to the right of the title | a button, badge or menu. */\n actions?: () => unknown\n footer?: () => unknown\n}>()\n</script>\n\n<template>\n <component :is=\"as\" class=\"j-card\" :class=\"[`j-card--${variant}`, `j-card--p-${padding}`]\">\n <header v-if=\"title || description || $slots.header || $slots.actions\" class=\"j-card__header\">\n <slot name=\"header\">\n <div class=\"j-card__heading\">\n <h3 v-if=\"title\" class=\"j-card__title\">\n {{ title }}\n </h3>\n <p v-if=\"description\" class=\"j-card__description\">\n {{ description }}\n </p>\n </div>\n </slot>\n <div v-if=\"$slots.actions\" class=\"j-card__actions\">\n <slot name=\"actions\" />\n </div>\n </header>\n <div v-if=\"$slots.default\" class=\"j-card__body\">\n <slot />\n </div>\n <footer v-if=\"$slots.footer\" class=\"j-card__footer\">\n <slot name=\"footer\" />\n </footer>\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-card {\n --j-card-p: var(--juxt-space-5);\n\n display: flex;\n flex-direction: column;\n min-width: 0;\n border-radius: var(--juxt-radius-lg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-card--outline {\n --j-avatar-ring: var(--juxt-surface);\n\n border: 1px solid var(--juxt-border);\n background: var(--juxt-surface);\n box-shadow: var(--juxt-shadow-xs);\n}\n\n.j-card--muted {\n --j-avatar-ring: var(--juxt-surface-sunken);\n\n border: 1px solid transparent;\n background: var(--juxt-surface-sunken);\n}\n\n.j-card--p-none {\n --j-card-p: 0;\n}\n\n.j-card--p-sm {\n --j-card-p: var(--juxt-space-3);\n}\n\n.j-card--p-lg {\n --j-card-p: var(--juxt-space-6);\n}\n\n.j-card__header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--juxt-space-4);\n padding: var(--j-card-p) var(--j-card-p) 0;\n}\n\n.j-card__heading {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-1);\n min-width: 0;\n}\n\n.j-card__title {\n margin: 0;\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-semibold);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-card__description {\n margin: 0;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-card__actions {\n display: flex;\n flex: none;\n align-items: center;\n gap: var(--juxt-space-2);\n margin: calc(var(--juxt-space-1) * -1) 0;\n}\n\n.j-card__body {\n flex: 1;\n padding: var(--j-card-p);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-card__header + .j-card__body {\n padding-top: var(--juxt-space-4);\n}\n\n.j-card__footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--juxt-space-2);\n padding: var(--juxt-space-3) var(--j-card-p);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.j-card__header:last-child {\n padding-bottom: var(--j-card-p);\n}\n\n.j-card--muted .j-card__footer {\n border-top-color: var(--juxt-border);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface CardProps {\n title?: string\n description?: string\n /** `outline` sits on the page; `muted` recedes into it. */\n variant?: 'outline' | 'muted'\n padding?: 'none' | 'sm' | 'md' | 'lg'\n as?: string | Component\n}\n\nwithDefaults(defineProps<CardProps>(), { variant: 'outline', padding: 'md', as: 'div' })\n\ndefineSlots<{\n default?: () => unknown\n header?: () => unknown\n /** Sits to the right of the title | a button, badge or menu. */\n actions?: () => unknown\n footer?: () => unknown\n}>()\n</script>\n\n<template>\n <component :is=\"as\" class=\"j-card\" :class=\"[`j-card--${variant}`, `j-card--p-${padding}`]\">\n <header v-if=\"title || description || $slots.header || $slots.actions\" class=\"j-card__header\">\n <slot name=\"header\">\n <div class=\"j-card__heading\">\n <h3 v-if=\"title\" class=\"j-card__title\">\n {{ title }}\n </h3>\n <p v-if=\"description\" class=\"j-card__description\">\n {{ description }}\n </p>\n </div>\n </slot>\n <div v-if=\"$slots.actions\" class=\"j-card__actions\">\n <slot name=\"actions\" />\n </div>\n </header>\n <div v-if=\"$slots.default\" class=\"j-card__body\">\n <slot />\n </div>\n <footer v-if=\"$slots.footer\" class=\"j-card__footer\">\n <slot name=\"footer\" />\n </footer>\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-card {\n --j-card-p: var(--juxt-space-5);\n\n display: flex;\n flex-direction: column;\n min-width: 0;\n border-radius: var(--juxt-radius-lg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-card--outline {\n --j-avatar-ring: var(--juxt-surface);\n\n border: 1px solid var(--juxt-border);\n background: var(--juxt-surface);\n box-shadow: var(--juxt-shadow-xs);\n}\n\n.j-card--muted {\n --j-avatar-ring: var(--juxt-surface-sunken);\n\n border: 1px solid transparent;\n background: var(--juxt-surface-sunken);\n}\n\n.j-card--p-none {\n --j-card-p: 0;\n}\n\n.j-card--p-sm {\n --j-card-p: var(--juxt-space-3);\n}\n\n.j-card--p-lg {\n --j-card-p: var(--juxt-space-6);\n}\n\n.j-card__header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--juxt-space-4);\n padding: var(--j-card-p) var(--j-card-p) 0;\n}\n\n.j-card__heading {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-1);\n min-width: 0;\n}\n\n.j-card__title {\n margin: 0;\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-semibold);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-card__description {\n margin: 0;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-card__actions {\n display: flex;\n flex: none;\n align-items: center;\n gap: var(--juxt-space-2);\n margin: calc(var(--juxt-space-1) * -1) 0;\n}\n\n.j-card__body {\n flex: 1;\n padding: var(--j-card-p);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-card__header + .j-card__body {\n padding-top: var(--juxt-space-4);\n}\n\n.j-card__footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--juxt-space-2);\n padding: var(--juxt-space-3) var(--j-card-p);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.j-card__header:last-child {\n padding-bottom: var(--j-card-p);\n}\n\n.j-card--muted .j-card__footer {\n border-top-color: var(--juxt-border);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, watchEffect } from 'vue'\n\nexport interface CheckboxProps {\n label?: string\n description?: string\n disabled?: boolean\n required?: boolean\n /** Shows a dash. Typically used for \"select all\" when only some items are selected. */\n indeterminate?: boolean\n name?: string\n value?: string\n id?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<CheckboxProps>()\nconst model = defineModel<boolean>({ default: false })\n\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\n\nconst generated = useId()\nconst id = computed(() => props.id ?? `j-${generated}`)\nconst descriptionId = computed(() => `${id.value}-description`)\nconst input = ref<HTMLInputElement | null>(null)\n\n// `indeterminate` is a DOM property, not an attribute.\nwatchEffect(() => {\n if (input.value) input.value.indeterminate = !!props.indeterminate\n})\n\nconst state = computed(() => (props.indeterminate ? 'indeterminate' : model.value ? 'checked' : 'unchecked'))\n\nfunction onChange(event: Event) {\n model.value = (event.target as HTMLInputElement).checked\n}\n\ndefineExpose({ input, focus: () => input.value?.focus() })\n</script>\n\n<template>\n <div\n class=\"j-checkbox\"\n :class=\"[$attrs.class, { 'is-disabled': disabled }]\"\n :style=\"$attrs.style as any\"\n :data-state=\"state\"\n >\n <span class=\"j-checkbox__control\">\n <input\n :id=\"id\"\n ref=\"input\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n type=\"checkbox\"\n class=\"j-checkbox__input\"\n :checked=\"model\"\n :disabled=\"disabled\"\n :required=\"required\"\n :name=\"name\"\n :value=\"value\"\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\n @change=\"onChange\"\n />\n <span class=\"j-checkbox__box\" aria-hidden=\"true\">\n <svg class=\"j-checkbox__check\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 8.25l2.75 2.75L12 5.5\" pathLength=\"1\" />\n </svg>\n <svg class=\"j-checkbox__dash\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4.5 8h7\" />\n </svg>\n </span>\n </span>\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-checkbox__text\">\n <label v-if=\"label || $slots.default\" :for=\"id\" class=\"j-checkbox__label\">\n <slot>{{ label }}</slot>\n </label>\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-checkbox__description\">\n <slot name=\"description\">{{ description }}</slot>\n </span>\n </span>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-checkbox {\n --j-checkbox-size: 1rem;\n\n display: inline-flex;\n align-items: flex-start;\n gap: var(--juxt-space-2-5);\n font-family: var(--juxt-font-sans);\n}\n\n.j-checkbox__control {\n position: relative;\n display: inline-flex;\n flex: none;\n /* Align the box with the first line of the label. */\n margin-top: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-checkbox-size)) / 2);\n}\n\n.j-checkbox__input {\n position: absolute;\n inset: -4px;\n z-index: 1;\n width: calc(100% + 8px);\n height: calc(100% + 8px);\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n.j-checkbox__input:disabled {\n cursor: not-allowed;\n}\n\n.j-checkbox__box {\n display: grid;\n place-items: center;\n width: var(--j-checkbox-size);\n height: var(--j-checkbox-size);\n border: 1px solid var(--juxt-border-strong);\n border-radius: var(--juxt-radius-xs);\n background: var(--juxt-surface);\n box-shadow: var(--juxt-shadow-xs);\n color: var(--juxt-accent-fg);\n outline: 2px solid transparent;\n outline-offset: 0;\n transition:\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n transform var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-checkbox__box > svg {\n grid-area: 1 / 1;\n width: 100%;\n height: 100%;\n stroke: currentColor;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n\n.j-checkbox__input:hover:not(:disabled) + .j-checkbox__box {\n border-color: var(--juxt-fg-muted);\n}\n\n.j-checkbox__input:active:not(:disabled) + .j-checkbox__box {\n transform: scale(0.9);\n}\n\n.j-checkbox__input:focus-visible + .j-checkbox__box {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n\n.j-checkbox[data-state='checked'] .j-checkbox__box,\n.j-checkbox[data-state='indeterminate'] .j-checkbox__box {\n border-color: var(--juxt-accent);\n background: var(--juxt-accent);\n}\n\n.j-checkbox[data-state='checked'] .j-checkbox__input:hover:not(:disabled) + .j-checkbox__box,\n.j-checkbox[data-state='indeterminate'] .j-checkbox__input:hover:not(:disabled) + .j-checkbox__box {\n border-color: var(--juxt-accent-hover);\n background: var(--juxt-accent-hover);\n}\n\n/* The check draws itself in, just after the fill lands. */\n.j-checkbox__check path {\n stroke-dasharray: 1;\n stroke-dashoffset: 1;\n transition: stroke-dashoffset var(--juxt-duration-fast) var(--juxt-ease-in);\n}\n\n.j-checkbox[data-state='checked'] .j-checkbox__check path {\n stroke-dashoffset: 0;\n transition: stroke-dashoffset var(--juxt-duration-normal) var(--juxt-ease-out) 40ms;\n}\n\n.j-checkbox__dash {\n opacity: 0;\n transform: scaleX(0.4);\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-checkbox[data-state='indeterminate'] .j-checkbox__dash {\n opacity: 1;\n transform: none;\n}\n\n.j-checkbox[data-state='indeterminate'] .j-checkbox__check {\n opacity: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-checkbox__check path,\n .j-checkbox[data-state='checked'] .j-checkbox__check path {\n transition-duration: 0ms;\n }\n\n .j-checkbox__input:active:not(:disabled) + .j-checkbox__box {\n transform: none;\n }\n}\n\n.j-checkbox__text {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n min-width: 0;\n}\n\n.j-checkbox__label {\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n user-select: none;\n}\n\n.j-checkbox__description {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-checkbox.is-disabled .j-checkbox__box {\n opacity: 0.5;\n box-shadow: none;\n}\n\n.j-checkbox.is-disabled .j-checkbox__label {\n color: var(--juxt-fg-muted);\n cursor: not-allowed;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, watchEffect } from 'vue'\n\nexport interface CheckboxProps {\n label?: string\n description?: string\n disabled?: boolean\n required?: boolean\n /** Shows a dash. Typically used for \"select all\" when only some items are selected. */\n indeterminate?: boolean\n name?: string\n value?: string\n id?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<CheckboxProps>()\nconst model = defineModel<boolean>({ default: false })\n\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\n\nconst generated = useId()\nconst id = computed(() => props.id ?? `j-${generated}`)\nconst descriptionId = computed(() => `${id.value}-description`)\nconst input = ref<HTMLInputElement | null>(null)\n\n// `indeterminate` is a DOM property, not an attribute.\nwatchEffect(() => {\n if (input.value) input.value.indeterminate = !!props.indeterminate\n})\n\nconst state = computed(() => (props.indeterminate ? 'indeterminate' : model.value ? 'checked' : 'unchecked'))\n\nfunction onChange(event: Event) {\n model.value = (event.target as HTMLInputElement).checked\n}\n\ndefineExpose({ input, focus: () => input.value?.focus() })\n</script>\n\n<template>\n <div\n class=\"j-checkbox\"\n :class=\"[$attrs.class, { 'is-disabled': disabled }]\"\n :style=\"$attrs.style as any\"\n :data-state=\"state\"\n >\n <span class=\"j-checkbox__control\">\n <input\n :id=\"id\"\n ref=\"input\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n type=\"checkbox\"\n class=\"j-checkbox__input\"\n :checked=\"model\"\n :disabled=\"disabled\"\n :required=\"required\"\n :name=\"name\"\n :value=\"value\"\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\n @change=\"onChange\"\n />\n <span class=\"j-checkbox__box\" aria-hidden=\"true\">\n <svg class=\"j-checkbox__check\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 8.25l2.75 2.75L12 5.5\" pathLength=\"1\" />\n </svg>\n <svg class=\"j-checkbox__dash\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4.5 8h7\" />\n </svg>\n </span>\n </span>\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-checkbox__text\">\n <label v-if=\"label || $slots.default\" :for=\"id\" class=\"j-checkbox__label\">\n <slot>{{ label }}</slot>\n </label>\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-checkbox__description\">\n <slot name=\"description\">{{ description }}</slot>\n </span>\n </span>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-checkbox {\n --j-checkbox-size: 1rem;\n\n display: inline-flex;\n align-items: flex-start;\n gap: var(--juxt-space-2-5);\n font-family: var(--juxt-font-sans);\n}\n\n.j-checkbox__control {\n position: relative;\n display: inline-flex;\n flex: none;\n /* Align the box with the first line of the label. */\n margin-top: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-checkbox-size)) / 2);\n}\n\n.j-checkbox__input {\n position: absolute;\n inset: -4px;\n z-index: 1;\n width: calc(100% + 8px);\n height: calc(100% + 8px);\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n.j-checkbox__input:disabled {\n cursor: not-allowed;\n}\n\n.j-checkbox__box {\n display: grid;\n place-items: center;\n width: var(--j-checkbox-size);\n height: var(--j-checkbox-size);\n border: 1px solid var(--juxt-border-strong);\n border-radius: var(--juxt-radius-xs);\n background: var(--juxt-surface);\n box-shadow: var(--juxt-shadow-xs);\n color: var(--juxt-accent-fg);\n outline: 2px solid transparent;\n outline-offset: 0;\n transition:\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n transform var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-checkbox__box > svg {\n grid-area: 1 / 1;\n width: 100%;\n height: 100%;\n stroke: currentColor;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n\n.j-checkbox__input:hover:not(:disabled) + .j-checkbox__box {\n border-color: var(--juxt-fg-muted);\n}\n\n.j-checkbox__input:active:not(:disabled) + .j-checkbox__box {\n transform: scale(0.9);\n}\n\n.j-checkbox__input:focus-visible + .j-checkbox__box {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n\n.j-checkbox[data-state='checked'] .j-checkbox__box,\n.j-checkbox[data-state='indeterminate'] .j-checkbox__box {\n border-color: var(--juxt-accent);\n background: var(--juxt-accent);\n}\n\n.j-checkbox[data-state='checked'] .j-checkbox__input:hover:not(:disabled) + .j-checkbox__box,\n.j-checkbox[data-state='indeterminate'] .j-checkbox__input:hover:not(:disabled) + .j-checkbox__box {\n border-color: var(--juxt-accent-hover);\n background: var(--juxt-accent-hover);\n}\n\n/* The check draws itself in, just after the fill lands. */\n.j-checkbox__check path {\n stroke-dasharray: 1;\n stroke-dashoffset: 1;\n transition: stroke-dashoffset var(--juxt-duration-fast) var(--juxt-ease-in);\n}\n\n.j-checkbox[data-state='checked'] .j-checkbox__check path {\n stroke-dashoffset: 0;\n transition: stroke-dashoffset var(--juxt-duration-normal) var(--juxt-ease-out) 40ms;\n}\n\n.j-checkbox__dash {\n opacity: 0;\n transform: scaleX(0.4);\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-checkbox[data-state='indeterminate'] .j-checkbox__dash {\n opacity: 1;\n transform: none;\n}\n\n.j-checkbox[data-state='indeterminate'] .j-checkbox__check {\n opacity: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-checkbox__check path,\n .j-checkbox[data-state='checked'] .j-checkbox__check path {\n transition-duration: 0ms;\n }\n\n .j-checkbox__input:active:not(:disabled) + .j-checkbox__box {\n transform: none;\n }\n}\n\n.j-checkbox__text {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n min-width: 0;\n}\n\n.j-checkbox__label {\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n user-select: none;\n}\n\n.j-checkbox__description {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-checkbox.is-disabled .j-checkbox__box {\n opacity: 0.5;\n box-shadow: none;\n}\n\n.j-checkbox.is-disabled .j-checkbox__label {\n color: var(--juxt-fg-muted);\n cursor: not-allowed;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useId } from 'vue'\nimport { IconChevronDown } from '../../icons'\n\nexport interface CollapsibleProps {\n /** Visible summary text; use the `trigger` slot for custom content instead. */\n label?: string\n disabled?: boolean\n}\n\ndefineProps<CollapsibleProps>()\n\n/** A single disclosure primitive | for a group of them, use `JAccordion`. */\nconst model = defineModel<boolean>({ default: false })\n\ndefineSlots<{\n default?: () => unknown\n trigger?: () => unknown\n}>()\n\nconst id = `j-${useId()}`\nconst triggerId = `${id}-trigger`\nconst panelId = `${id}-panel`\n\nfunction toggle() {\n model.value = !model.value\n}\n</script>\n\n<template>\n <div class=\"j-collapsible\" :data-state=\"model ? 'open' : 'closed'\">\n <button\n :id=\"triggerId\"\n type=\"button\"\n class=\"j-collapsible__trigger j-focusable\"\n :aria-expanded=\"model\"\n :aria-controls=\"panelId\"\n :disabled=\"disabled\"\n @click=\"toggle\"\n >\n <IconChevronDown class=\"j-collapsible__chevron\" />\n <span class=\"j-collapsible__label\"><slot name=\"trigger\">{{ label }}</slot></span>\n </button>\n <div\n :id=\"panelId\"\n role=\"region\"\n class=\"j-collapsible__panel\"\n :aria-labelledby=\"triggerId\"\n :inert=\"!model\"\n >\n <div class=\"j-collapsible__panel-inner\">\n <div class=\"j-collapsible__content\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-collapsible {\n font-family: var(--juxt-font-sans);\n}\n\n.j-collapsible__trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--juxt-space-1-5);\n padding: var(--juxt-space-1) 0;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n color: var(--juxt-fg);\n font: inherit;\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n outline: 2px solid transparent;\n outline-offset: 2px;\n -webkit-tap-highlight-color: transparent;\n transition:\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-collapsible__trigger:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-collapsible__chevron {\n flex: none;\n width: 1rem;\n height: 1rem;\n color: var(--juxt-fg-muted);\n transition: transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-collapsible[data-state='open'] .j-collapsible__chevron {\n transform: rotate(180deg);\n}\n\n.j-collapsible__panel {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n.j-collapsible[data-state='open'] .j-collapsible__panel {\n grid-template-rows: 1fr;\n}\n\n.j-collapsible__panel-inner {\n min-height: 0;\n overflow: hidden;\n}\n\n.j-collapsible__content {\n padding-top: var(--juxt-space-2);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n opacity: 0;\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-collapsible[data-state='open'] .j-collapsible__content {\n opacity: 1;\n transition-delay: 60ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-collapsible__panel,\n .j-collapsible__chevron {\n transition: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useId } from 'vue'\nimport { IconChevronDown } from '../../icons'\n\nexport interface CollapsibleProps {\n /** Visible summary text; use the `trigger` slot for custom content instead. */\n label?: string\n disabled?: boolean\n}\n\ndefineProps<CollapsibleProps>()\n\n/** A single disclosure primitive | for a group of them, use `JAccordion`. */\nconst model = defineModel<boolean>({ default: false })\n\ndefineSlots<{\n default?: () => unknown\n trigger?: () => unknown\n}>()\n\nconst id = `j-${useId()}`\nconst triggerId = `${id}-trigger`\nconst panelId = `${id}-panel`\n\nfunction toggle() {\n model.value = !model.value\n}\n</script>\n\n<template>\n <div class=\"j-collapsible\" :data-state=\"model ? 'open' : 'closed'\">\n <button\n :id=\"triggerId\"\n type=\"button\"\n class=\"j-collapsible__trigger j-focusable\"\n :aria-expanded=\"model\"\n :aria-controls=\"panelId\"\n :disabled=\"disabled\"\n @click=\"toggle\"\n >\n <IconChevronDown class=\"j-collapsible__chevron\" />\n <span class=\"j-collapsible__label\"><slot name=\"trigger\">{{ label }}</slot></span>\n </button>\n <div\n :id=\"panelId\"\n role=\"region\"\n class=\"j-collapsible__panel\"\n :aria-labelledby=\"triggerId\"\n :inert=\"!model\"\n >\n <div class=\"j-collapsible__panel-inner\">\n <div class=\"j-collapsible__content\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-collapsible {\n font-family: var(--juxt-font-sans);\n}\n\n.j-collapsible__trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--juxt-space-1-5);\n padding: var(--juxt-space-1) 0;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n color: var(--juxt-fg);\n font: inherit;\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n outline: 2px solid transparent;\n outline-offset: 2px;\n -webkit-tap-highlight-color: transparent;\n transition:\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-collapsible__trigger:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-collapsible__chevron {\n flex: none;\n width: 1rem;\n height: 1rem;\n color: var(--juxt-fg-muted);\n transition: transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-collapsible[data-state='open'] .j-collapsible__chevron {\n transform: rotate(180deg);\n}\n\n.j-collapsible__panel {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n.j-collapsible[data-state='open'] .j-collapsible__panel {\n grid-template-rows: 1fr;\n}\n\n.j-collapsible__panel-inner {\n min-height: 0;\n overflow: hidden;\n}\n\n.j-collapsible__content {\n padding-top: var(--juxt-space-2);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n opacity: 0;\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-collapsible[data-state='open'] .j-collapsible__content {\n opacity: 1;\n transition-delay: 60ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-collapsible__panel,\n .j-collapsible__chevron {\n transition: none;\n }\n}\n}\n</style>\n","import type { Ref } from 'vue'\nimport { nextTick, onBeforeUnmount, watch } from 'vue'\nimport { focusElement, getFocusableElements, isClient } from '../utils/dom'\n\nexport interface FocusTrapOptions {\n /** Element to focus when the trap activates. Falls back to the first focusable element, then the container. */\n initialFocus?: () => HTMLElement | null | undefined\n /** Return focus to whatever was focused before activation. Defaults to true. */\n restoreFocus?: boolean\n}\n\ninterface Trap {\n container: Ref<HTMLElement | null>\n}\n\n// Traps nest: only the most recently activated one is in charge.\nconst stack: Trap[] = []\n\nfunction isTop(trap: Trap) {\n return stack[stack.length - 1] === trap\n}\n\nlet listening = false\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key !== 'Tab' || event.defaultPrevented) return\n const trap = stack[stack.length - 1]\n const container = trap?.container.value\n if (!container) return\n\n const focusable = getFocusableElements(container)\n if (focusable.length === 0) {\n event.preventDefault()\n focusElement(container)\n return\n }\n\n const first = focusable[0]!\n const last = focusable[focusable.length - 1]!\n const active = document.activeElement as HTMLElement | null\n const inside = active ? container.contains(active) : false\n\n if (event.shiftKey && (active === first || active === container || !inside)) {\n event.preventDefault()\n focusElement(last)\n } else if (!event.shiftKey && (active === last || !inside)) {\n event.preventDefault()\n focusElement(first)\n }\n}\n\nfunction onFocusIn(event: FocusEvent) {\n const trap = stack[stack.length - 1]\n const container = trap?.container.value\n const target = event.target as Node | null\n if (!container || !target || container.contains(target)) return\n // Focus escaped (e.g. a programmatic focus elsewhere) | bring it back.\n if ((target as HTMLElement).closest?.('[data-juxt-layer-ignore]')) return\n const [first] = getFocusableElements(container)\n focusElement(first ?? container)\n}\n\nfunction listen() {\n if (listening || !isClient) return\n document.addEventListener('keydown', onKeydown)\n document.addEventListener('focusin', onFocusIn)\n listening = true\n}\n\nfunction unlisten() {\n if (!listening || stack.length > 0) return\n document.removeEventListener('keydown', onKeydown)\n document.removeEventListener('focusin', onFocusIn)\n listening = false\n}\n\n/**\n * Keep keyboard focus inside `container` while `active` is true, and put it\n * back where it came from afterwards.\n */\nexport function useFocusTrap(container: Ref<HTMLElement | null>, active: Ref<boolean>, options: FocusTrapOptions = {}) {\n const trap: Trap = { container }\n let returnTo: HTMLElement | null = null\n // The container as it was while active. By the time we deactivate, Vue may\n // already have cleared the template ref while the element animates out.\n let activeContainer: HTMLElement | null = null\n\n async function activate() {\n returnTo = document.activeElement as HTMLElement | null\n stack.push(trap)\n listen()\n await nextTick()\n const el = container.value\n activeContainer = el\n if (!el || !isTop(trap)) return\n if (el.contains(document.activeElement)) return\n const target = options.initialFocus?.() ?? el.querySelector<HTMLElement>('[autofocus], [data-autofocus]') ?? getFocusableElements(el)[0] ?? el\n focusElement(target)\n }\n\n function deactivate() {\n const index = stack.indexOf(trap)\n if (index === -1) return\n stack.splice(index, 1)\n unlisten()\n\n if (options.restoreFocus === false) return\n const target = returnTo\n returnTo = null\n if (!target || !target.isConnected) return\n const current = document.activeElement as HTMLElement | null\n // Only restore when focus would otherwise be lost. If the user deliberately\n // moved focus somewhere meaningful, respect that.\n const lost = !current || current === document.body || !current.isConnected\n || !!activeContainer?.contains(current) || !!container.value?.contains(current)\n activeContainer = null\n if (lost) focusElement(target)\n }\n\n if (isClient) {\n watch(\n active,\n (value) => {\n if (value) activate()\n else deactivate()\n },\n { immediate: true, flush: 'post' },\n )\n onBeforeUnmount(deactivate)\n }\n}\n","import type { Ref } from 'vue'\nimport { onBeforeUnmount, watch } from 'vue'\nimport { isClient } from '../utils/dom'\n\nlet locks = 0\nlet saved: { overflow: string, paddingRight: string } | null = null\n\nfunction lock() {\n locks++\n if (locks > 1) return\n const body = document.body\n const scrollbar = window.innerWidth - document.documentElement.clientWidth\n saved = { overflow: body.style.overflow, paddingRight: body.style.paddingRight }\n body.style.overflow = 'hidden'\n if (scrollbar > 0) {\n // Compensate for the vanished scrollbar so the page doesn't jump sideways.\n const current = Number.parseFloat(getComputedStyle(body).paddingRight) || 0\n body.style.paddingRight = `${current + scrollbar}px`\n }\n document.documentElement.style.setProperty('--juxt-scrollbar-width', `${scrollbar}px`)\n document.documentElement.dataset.juxtScrollLocked = ''\n}\n\nfunction unlock() {\n locks = Math.max(0, locks - 1)\n if (locks > 0 || !saved) return\n document.body.style.overflow = saved.overflow\n document.body.style.paddingRight = saved.paddingRight\n document.documentElement.style.removeProperty('--juxt-scrollbar-width')\n delete document.documentElement.dataset.juxtScrollLocked\n saved = null\n}\n\n/** Prevent the page behind an overlay from scrolling while `active` is true. */\nexport function useScrollLock(active: Ref<boolean>) {\n if (!isClient) return\n let held = false\n\n const set = (value: boolean) => {\n if (value === held) return\n held = value\n if (value) lock()\n else unlock()\n }\n\n watch(active, set, { immediate: true })\n onBeforeUnmount(() => set(false))\n}\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\nimport type { MatchRange } from './search'\nimport { computed, nextTick, onMounted, ref, useId, watch } from 'vue'\nimport { useFocusTrap } from '../../composables/useFocusTrap'\nimport { formatHotkey, useHotkey } from '../../composables/useHotkey'\nimport { useLayer } from '../../composables/useLayer'\nimport { useScrollLock } from '../../composables/useScrollLock'\nimport { IconSearch, Spinner } from '../../icons'\nimport { matchText, splitByRanges } from './search'\nimport JPortal from '../primitives/JPortal.vue'\n\nexport interface CommandItem {\n id: string\n label: string\n description?: string\n /** An icon component, rendered at 16px. */\n icon?: Component\n /** Shortcut to display, in hotkey syntax: `mod+shift+p`. Display only. */\n shortcut?: string\n /** Extra terms that should find this item. */\n keywords?: string[]\n disabled?: boolean\n /** Called when the item is chosen, before `select` is emitted. */\n onSelect?: (item: CommandItem) => void\n}\n\nexport interface CommandGroup {\n id: string\n label?: string\n items: CommandItem[]\n}\n\nexport interface CommandPaletteProps {\n groups: CommandGroup[]\n /** Shown as a \"Recent\" group while the search is empty. */\n recent?: CommandItem[]\n placeholder?: string\n /** Show a loading indicator | for async results. */\n loading?: boolean\n /**\n * Built-in ranking and filtering. Set to `false` when you filter yourself\n * (e.g. server-side search driven by `v-model:search`).\n */\n filter?: boolean\n /** Global shortcut that toggles the palette. `false` to disable. */\n hotkey?: string | false\n /** Accessible name of the dialog. */\n label?: string\n emptyText?: string\n}\n\nconst props = withDefaults(defineProps<CommandPaletteProps>(), {\n placeholder: 'Type a command or search…',\n filter: true,\n hotkey: 'mod+k',\n label: 'Command palette',\n emptyText: 'No results',\n})\n\nconst open = defineModel<boolean>('open', { default: false })\nconst search = defineModel<string>('search', { default: '' })\n\nconst emit = defineEmits<{\n select: [item: CommandItem]\n}>()\n\ndefineSlots<{\n item?: (props: { item: CommandItem, active: boolean }) => unknown\n empty?: (props: { search: string }) => unknown\n footer?: () => unknown\n}>()\n\nconst id = `j-${useId()}`\nconst listboxId = `${id}-listbox`\n// Keys contain group and item ids; keep the DOM id selector-safe.\nconst optionId = (key: string) => `${id}-option-${key.replace(/[^\\w-]/g, '_')}`\n\n// --- Results ---------------------------------------------------------------\ninterface Row {\n key: string\n item: CommandItem\n ranges: MatchRange[]\n score: number\n}\n\ninterface Section {\n id: string\n label?: string\n rows: Row[]\n}\n\nconst sections = computed<Section[]>(() => {\n const query = search.value.trim()\n const toKey = (groupId: string, item: CommandItem) => `${groupId}:${item.id}`\n\n if (!query || !props.filter) {\n const base: Section[] = []\n if (!query && props.recent?.length)\n base.push({ id: 'recent', label: 'Recent', rows: props.recent.map(item => ({ key: toKey('recent', item), item, ranges: [], score: 1 })) })\n for (const group of props.groups) {\n if (group.items.length)\n base.push({ id: group.id, label: group.label, rows: group.items.map(item => ({ key: toKey(group.id, item), item, ranges: [], score: 1 })) })\n }\n return base\n }\n\n const ranked: Section[] = []\n for (const group of props.groups) {\n const rows: Row[] = []\n for (const item of group.items) {\n const label = matchText(query, item.label)\n let score = label.score\n if (item.keywords) {\n for (const keyword of item.keywords) score = Math.max(score, matchText(query, keyword).score * 0.9)\n }\n if (item.description) score = Math.max(score, matchText(query, item.description).score * 0.5)\n if (score > 0) rows.push({ key: toKey(group.id, item), item, ranges: label.ranges, score })\n }\n if (rows.length) {\n rows.sort((a, b) => b.score - a.score)\n ranked.push({ id: group.id, label: group.label, rows })\n }\n }\n // The group holding the best match comes first.\n return ranked.sort((a, b) => b.rows[0]!.score - a.rows[0]!.score)\n})\n\nconst rows = computed(() => sections.value.flatMap(section => section.rows))\nconst rowIndex = computed(() => new Map(rows.value.map((row, index) => [row.key, index])))\n\nconst activeIndex = ref(0)\nconst activeRow = computed(() => rows.value[activeIndex.value])\n\nfunction firstEnabled(from = 0, delta: 1 | -1 = 1): number {\n const count = rows.value.length\n for (let i = 0; i < count; i++) {\n const index = (from + i * delta + count * 2) % count\n if (!rows.value[index]!.item.disabled) return index\n }\n return -1\n}\n\n// Every keystroke puts the best match under the cursor.\nwatch(rows, () => (activeIndex.value = Math.max(0, firstEnabled())))\n\n// --- Opening & closing -----------------------------------------------------\nconst panel = ref<HTMLElement | null>(null)\nconst input = ref<HTMLInputElement | null>(null)\nconst list = ref<HTMLElement | null>(null)\n\nuseHotkey(() => props.hotkey, () => (open.value = !open.value))\nuseLayer({ active: open, elements: () => [panel.value], onEscape: () => close() })\nuseFocusTrap(panel, open, { initialFocus: () => input.value })\nuseScrollLock(open)\n\nwatch(open, (value) => {\n if (!value) return\n search.value = ''\n activeIndex.value = Math.max(0, firstEnabled())\n nextTick(() => list.value?.scrollTo({ top: 0 }))\n})\n\nfunction close() {\n open.value = false\n}\n\nlet pressedOutside = false\nfunction onPointerDown(event: PointerEvent) {\n pressedOutside = event.target === event.currentTarget\n}\nfunction onClick(event: MouseEvent) {\n if (pressedOutside && event.target === event.currentTarget) close()\n pressedOutside = false\n}\n\n// --- Selection & navigation ------------------------------------------------\nfunction choose(row: Row | undefined) {\n if (!row || row.item.disabled) return\n row.item.onSelect?.(row.item)\n emit('select', row.item)\n close()\n}\n\nfunction setActive(index: number) {\n if (index < 0) return\n activeIndex.value = index\n const row = rows.value[index]\n if (!row) return\n nextTick(() => {\n const el = document.getElementById(optionId(row.key))\n // Keep a little context around the active row at the list edges.\n if (index === 0) list.value?.scrollTo({ top: 0 })\n else el?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction move(delta: 1 | -1, distance = 1) {\n const count = rows.value.length\n if (!count) return\n let index = activeIndex.value\n for (let step = 0; step < distance; step++) {\n const next = firstEnabled((index + delta + count) % count, delta)\n if (next === -1) return\n // Page jumps stop at the ends instead of wrapping.\n if (distance > 1 && ((delta === 1 && next < index) || (delta === -1 && next > index))) break\n index = next\n }\n setActive(index)\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.isComposing) return\n // Emacs-style Ctrl+N / Ctrl+P for people who never leave the home row.\n const ctrl = event.ctrlKey && !event.metaKey && !event.altKey\n const key = event.key\n if (key === 'ArrowDown' || (ctrl && key === 'n')) move(1)\n else if (key === 'ArrowUp' || (ctrl && key === 'p')) move(-1)\n else if (key === 'PageDown') move(1, 5)\n else if (key === 'PageUp') move(-1, 5)\n else if (key === 'Enter') choose(activeRow.value)\n else return\n event.preventDefault()\n}\n\n// Hover follows the pointer only when it moves | scrolling with the keyboard\n// under a resting cursor must not steal the selection.\nfunction onRowPointerMove(index: number) {\n if (activeIndex.value !== index && !rows.value[index]?.item.disabled) activeIndex.value = index\n}\n\n// --- Announcements ---------------------------------------------------------\nconst announcement = ref('')\nlet announceTimer: ReturnType<typeof setTimeout> | undefined\nwatch([rows, () => props.loading], () => {\n clearTimeout(announceTimer)\n announceTimer = setTimeout(() => {\n if (!open.value) return\n if (props.loading) announcement.value = 'Loading results'\n else announcement.value = rows.value.length === 1 ? '1 result' : `${rows.value.length} results`\n }, 350)\n})\n\n// Shortcut glyphs depend on the platform | resolve them on the client only.\nconst mounted = ref(false)\nonMounted(() => (mounted.value = true))\nconst keys = (hotkey: string) => (mounted.value ? formatHotkey(hotkey) : [])\n\ndefineExpose({ close, focus: () => input.value?.focus() })\n</script>\n\n<template>\n <JPortal>\n <Transition name=\"j-command-overlay\">\n <div v-if=\"open\" class=\"j-command__overlay\" aria-hidden=\"true\" />\n </Transition>\n <Transition name=\"j-command\" :duration=\"{ enter: 180, leave: 110 }\">\n <div v-if=\"open\" class=\"j-command__positioner\" @pointerdown=\"onPointerDown\" @click=\"onClick\">\n <div\n ref=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"label\"\n class=\"j-command\"\n >\n <div class=\"j-command__search\">\n <IconSearch class=\"j-command__search-icon\" />\n <input\n ref=\"input\"\n v-model=\"search\"\n class=\"j-command__input\"\n type=\"text\"\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-autocomplete=\"list\"\n :aria-controls=\"listboxId\"\n :aria-activedescendant=\"activeRow ? optionId(activeRow.key) : undefined\"\n :aria-label=\"label\"\n :placeholder=\"placeholder\"\n autocomplete=\"off\"\n autocorrect=\"off\"\n autocapitalize=\"off\"\n spellcheck=\"false\"\n enterkeyhint=\"go\"\n @keydown=\"onKeydown\"\n />\n <button type=\"button\" class=\"j-command__esc j-kbd\" tabindex=\"-1\" aria-label=\"Close\" @click=\"close\">\n Esc\n </button>\n <span v-if=\"loading\" class=\"j-command__progress\" aria-hidden=\"true\" />\n </div>\n\n <div\n :id=\"listboxId\"\n ref=\"list\"\n role=\"listbox\"\n :aria-label=\"label\"\n class=\"j-command__list\"\n @pointerdown.prevent\n >\n <div\n v-for=\"section in sections\"\n :key=\"section.id\"\n role=\"group\"\n class=\"j-command__group\"\n :aria-labelledby=\"section.label ? `${id}-group-${section.id}` : undefined\"\n >\n <div v-if=\"section.label\" :id=\"`${id}-group-${section.id}`\" class=\"j-command__group-label\" role=\"presentation\">\n {{ section.label }}\n </div>\n <div\n v-for=\"row in section.rows\"\n :id=\"optionId(row.key)\"\n :key=\"row.key\"\n role=\"option\"\n class=\"j-command__item\"\n :class=\"{ 'is-active': row.key === activeRow?.key }\"\n :aria-selected=\"row.key === activeRow?.key\"\n :aria-disabled=\"row.item.disabled || undefined\"\n @pointermove=\"onRowPointerMove(rowIndex.get(row.key)!)\"\n @click=\"choose(row)\"\n >\n <slot name=\"item\" :item=\"row.item\" :active=\"row.key === activeRow?.key\">\n <span v-if=\"row.item.icon\" class=\"j-command__item-icon\">\n <component :is=\"row.item.icon\" />\n </span>\n <span class=\"j-command__item-text\">\n <span class=\"j-command__item-label\" :class=\"{ 'is-searching': row.ranges.length }\">\n <template v-for=\"(part, i) in splitByRanges(row.item.label, row.ranges)\" :key=\"i\">\n <mark v-if=\"part.match\">{{ part.text }}</mark>\n <template v-else>{{ part.text }}</template>\n </template>\n </span>\n <span v-if=\"row.item.description\" class=\"j-command__item-description\">{{ row.item.description }}</span>\n </span>\n <span v-if=\"row.item.shortcut\" class=\"j-command__item-shortcut\" aria-hidden=\"true\">\n <kbd v-for=\"key in keys(row.item.shortcut)\" :key=\"key\" class=\"j-kbd\">{{ key }}</kbd>\n </span>\n </slot>\n </div>\n </div>\n\n <div v-if=\"!rows.length\" class=\"j-command__empty\" role=\"presentation\">\n <template v-if=\"loading\">\n <Spinner :size=\"16\" />\n <span>Searching…</span>\n </template>\n <slot v-else name=\"empty\" :search=\"search\">\n <span class=\"j-command__empty-title\">{{ emptyText }}</span>\n <span v-if=\"search\" class=\"j-command__empty-hint\">Nothing matches “{{ search }}”. Try fewer words.</span>\n </slot>\n </div>\n </div>\n\n <div class=\"j-command__footer\">\n <slot name=\"footer\">\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">↑</kbd><kbd class=\"j-kbd\">↓</kbd> Navigate</span>\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">↵</kbd> Select</span>\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">Esc</kbd> Close</span>\n </slot>\n </div>\n\n <span class=\"j-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ announcement }}</span>\n </div>\n </div>\n </Transition>\n </JPortal>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-command__overlay {\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-overlay);\n background: var(--juxt-overlay);\n}\n\n.j-command__positioner {\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-modal);\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding: max(12vh, var(--juxt-space-4)) var(--juxt-space-4) var(--juxt-space-4);\n}\n\n.j-command {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n max-width: 40rem;\n max-height: min(34rem, calc(100dvh - 12vh - var(--juxt-space-8)));\n overflow: hidden;\n border: 1px solid var(--juxt-border);\n border-radius: var(--juxt-radius-lg);\n background: var(--juxt-surface-raised);\n box-shadow: var(--juxt-shadow-lg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n/* Search */\n.j-command__search {\n position: relative;\n display: flex;\n flex: none;\n align-items: center;\n gap: var(--juxt-space-3);\n height: 3.25rem;\n padding: 0 var(--juxt-space-3) 0 var(--juxt-space-4);\n border-bottom: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-command__search,\n[data-theme='dark'] .j-command__search {\n border-bottom-color: var(--juxt-border);\n}\n\n.j-command__search-icon {\n flex: none;\n width: 1.125rem;\n height: 1.125rem;\n color: var(--juxt-fg-muted);\n}\n\n.j-command__input {\n flex: 1;\n min-width: 0;\n height: 100%;\n padding: 0;\n border: 0;\n outline: none;\n background: transparent;\n color: var(--juxt-fg);\n font: inherit;\n font-size: var(--juxt-text-lg);\n letter-spacing: var(--juxt-tracking-tight);\n caret-color: var(--juxt-accent);\n}\n\n.j-command__input::placeholder {\n color: var(--juxt-fg-muted);\n}\n\n.j-command__esc {\n height: 1.375rem;\n padding: 0 var(--juxt-space-1-5);\n cursor: pointer;\n}\n\n.j-command__esc:hover {\n color: var(--juxt-fg-secondary);\n}\n\n/* An indeterminate hairline: present, never loud. */\n.j-command__progress {\n position: absolute;\n right: 0;\n bottom: -1px;\n left: 0;\n height: 1px;\n overflow: hidden;\n}\n\n.j-command__progress::after {\n content: '';\n position: absolute;\n inset: 0;\n width: 40%;\n background: linear-gradient(90deg, transparent, var(--juxt-accent), transparent);\n animation: j-command-progress 1.1s var(--juxt-ease-standard) infinite;\n}\n\n@keyframes j-command-progress {\n from {\n transform: translateX(-100%);\n }\n\n to {\n transform: translateX(250%);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-command__progress::after {\n width: 100%;\n animation: j-command-pulse 1.4s ease-in-out infinite;\n }\n\n @keyframes j-command-pulse {\n 50% {\n opacity: 0.3;\n }\n }\n}\n\n/* List */\n.j-command__list {\n flex: 1;\n min-height: 0;\n padding: var(--juxt-space-1-5);\n overflow-y: auto;\n overscroll-behavior: contain;\n scroll-padding-block: var(--juxt-space-8) var(--juxt-space-1-5);\n}\n\n.j-command__group + .j-command__group {\n margin-top: var(--juxt-space-1);\n}\n\n.j-command__group-label {\n padding: var(--juxt-space-2) var(--juxt-space-2-5) var(--juxt-space-1-5);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n user-select: none;\n}\n\n.j-command__item {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2-5);\n height: 2.5rem;\n padding: 0 var(--juxt-space-2-5);\n border-radius: var(--juxt-radius-sm);\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n user-select: none;\n}\n\n/* The one place the palette uses green: a hairline marking where you are. */\n.j-command__item::before {\n content: '';\n position: absolute;\n top: 0.625rem;\n bottom: 0.625rem;\n left: 0;\n width: 2px;\n border-radius: 0 2px 2px 0;\n background: var(--juxt-accent);\n opacity: 0;\n transform: scaleY(0.4);\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-command__item.is-active {\n background: var(--juxt-surface-hover);\n}\n\n.j-command__item.is-active::before {\n opacity: 1;\n transform: none;\n}\n\n.j-command__item[aria-disabled='true'] {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-command__item-icon {\n display: grid;\n flex: none;\n place-items: center;\n width: 1.25rem;\n height: 1.25rem;\n color: var(--juxt-fg-muted);\n}\n\n.j-command__item-icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-command__item.is-active .j-command__item-icon {\n color: var(--juxt-fg);\n}\n\n.j-command__item-text {\n display: flex;\n flex: 1;\n align-items: baseline;\n gap: var(--juxt-space-2);\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n}\n\n.j-command__item-label {\n flex: none;\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.j-command__item-label.is-searching {\n color: var(--juxt-fg-secondary);\n}\n\n.j-command__item-label mark {\n background: none;\n color: var(--juxt-fg);\n font-weight: var(--juxt-weight-medium);\n}\n\n.j-command__item-description {\n overflow: hidden;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n text-overflow: ellipsis;\n}\n\n.j-command__item-shortcut {\n display: inline-flex;\n flex: none;\n gap: 3px;\n}\n\n.j-command__empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--juxt-space-1-5);\n min-height: 8rem;\n padding: var(--juxt-space-6) var(--juxt-space-4);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n text-align: center;\n}\n\n.j-command__empty-title {\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n}\n\n/* Footer */\n.j-command__footer {\n display: flex;\n flex: none;\n align-items: center;\n gap: var(--juxt-space-4);\n height: 2.25rem;\n padding: 0 var(--juxt-space-4);\n border-top: 1px solid var(--juxt-border-subtle);\n background: var(--juxt-surface-sunken);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n}\n\n.dark .j-command__footer,\n[data-theme='dark'] .j-command__footer {\n border-top-color: var(--juxt-border);\n background: var(--juxt-surface);\n}\n\n.j-command__hint {\n display: inline-flex;\n align-items: center;\n gap: var(--juxt-space-1);\n}\n\n.j-command__hint .j-kbd + .j-kbd {\n margin-left: -1px;\n}\n\n/* Motion: quick in, quicker out. */\n.j-command-overlay-enter-active {\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-command-overlay-leave-active {\n transition: opacity var(--juxt-duration-instant) var(--juxt-ease-in);\n}\n\n.j-command-overlay-enter-from,\n.j-command-overlay-leave-to {\n opacity: 0;\n}\n\n.j-command-enter-active .j-command {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-command-leave-active .j-command {\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\n}\n\n.j-command-enter-from .j-command,\n.j-command-leave-to .j-command {\n opacity: 0;\n transform: translateY(calc(var(--juxt-motion-shift) * -1)) scale(0.985);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-command-enter-from .j-command,\n .j-command-leave-to .j-command {\n transform: none;\n }\n}\n\n@media (max-width: 639px) {\n .j-command__positioner {\n padding: var(--juxt-space-2);\n }\n\n .j-command {\n max-height: calc(100dvh - var(--juxt-space-4));\n }\n\n .j-command__footer {\n display: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\nimport type { MatchRange } from './search'\nimport { computed, nextTick, onMounted, ref, useId, watch } from 'vue'\nimport { useFocusTrap } from '../../composables/useFocusTrap'\nimport { formatHotkey, useHotkey } from '../../composables/useHotkey'\nimport { useLayer } from '../../composables/useLayer'\nimport { useScrollLock } from '../../composables/useScrollLock'\nimport { IconSearch, Spinner } from '../../icons'\nimport { matchText, splitByRanges } from './search'\nimport JPortal from '../primitives/JPortal.vue'\n\nexport interface CommandItem {\n id: string\n label: string\n description?: string\n /** An icon component, rendered at 16px. */\n icon?: Component\n /** Shortcut to display, in hotkey syntax: `mod+shift+p`. Display only. */\n shortcut?: string\n /** Extra terms that should find this item. */\n keywords?: string[]\n disabled?: boolean\n /** Called when the item is chosen, before `select` is emitted. */\n onSelect?: (item: CommandItem) => void\n}\n\nexport interface CommandGroup {\n id: string\n label?: string\n items: CommandItem[]\n}\n\nexport interface CommandPaletteProps {\n groups: CommandGroup[]\n /** Shown as a \"Recent\" group while the search is empty. */\n recent?: CommandItem[]\n placeholder?: string\n /** Show a loading indicator | for async results. */\n loading?: boolean\n /**\n * Built-in ranking and filtering. Set to `false` when you filter yourself\n * (e.g. server-side search driven by `v-model:search`).\n */\n filter?: boolean\n /** Global shortcut that toggles the palette. `false` to disable. */\n hotkey?: string | false\n /** Accessible name of the dialog. */\n label?: string\n emptyText?: string\n}\n\nconst props = withDefaults(defineProps<CommandPaletteProps>(), {\n placeholder: 'Type a command or search…',\n filter: true,\n hotkey: 'mod+k',\n label: 'Command palette',\n emptyText: 'No results',\n})\n\nconst open = defineModel<boolean>('open', { default: false })\nconst search = defineModel<string>('search', { default: '' })\n\nconst emit = defineEmits<{\n select: [item: CommandItem]\n}>()\n\ndefineSlots<{\n item?: (props: { item: CommandItem, active: boolean }) => unknown\n empty?: (props: { search: string }) => unknown\n footer?: () => unknown\n}>()\n\nconst id = `j-${useId()}`\nconst listboxId = `${id}-listbox`\n// Keys contain group and item ids; keep the DOM id selector-safe.\nconst optionId = (key: string) => `${id}-option-${key.replace(/[^\\w-]/g, '_')}`\n\n// --- Results ---------------------------------------------------------------\ninterface Row {\n key: string\n item: CommandItem\n ranges: MatchRange[]\n score: number\n}\n\ninterface Section {\n id: string\n label?: string\n rows: Row[]\n}\n\nconst sections = computed<Section[]>(() => {\n const query = search.value.trim()\n const toKey = (groupId: string, item: CommandItem) => `${groupId}:${item.id}`\n\n if (!query || !props.filter) {\n const base: Section[] = []\n if (!query && props.recent?.length)\n base.push({ id: 'recent', label: 'Recent', rows: props.recent.map(item => ({ key: toKey('recent', item), item, ranges: [], score: 1 })) })\n for (const group of props.groups) {\n if (group.items.length)\n base.push({ id: group.id, label: group.label, rows: group.items.map(item => ({ key: toKey(group.id, item), item, ranges: [], score: 1 })) })\n }\n return base\n }\n\n const ranked: Section[] = []\n for (const group of props.groups) {\n const rows: Row[] = []\n for (const item of group.items) {\n const label = matchText(query, item.label)\n let score = label.score\n if (item.keywords) {\n for (const keyword of item.keywords) score = Math.max(score, matchText(query, keyword).score * 0.9)\n }\n if (item.description) score = Math.max(score, matchText(query, item.description).score * 0.5)\n if (score > 0) rows.push({ key: toKey(group.id, item), item, ranges: label.ranges, score })\n }\n if (rows.length) {\n rows.sort((a, b) => b.score - a.score)\n ranked.push({ id: group.id, label: group.label, rows })\n }\n }\n // The group holding the best match comes first.\n return ranked.sort((a, b) => b.rows[0]!.score - a.rows[0]!.score)\n})\n\nconst rows = computed(() => sections.value.flatMap(section => section.rows))\nconst rowIndex = computed(() => new Map(rows.value.map((row, index) => [row.key, index])))\n\nconst activeIndex = ref(0)\nconst activeRow = computed(() => rows.value[activeIndex.value])\n\nfunction firstEnabled(from = 0, delta: 1 | -1 = 1): number {\n const count = rows.value.length\n for (let i = 0; i < count; i++) {\n const index = (from + i * delta + count * 2) % count\n if (!rows.value[index]!.item.disabled) return index\n }\n return -1\n}\n\n// Every keystroke puts the best match under the cursor.\nwatch(rows, () => (activeIndex.value = Math.max(0, firstEnabled())))\n\n// --- Opening & closing -----------------------------------------------------\nconst panel = ref<HTMLElement | null>(null)\nconst input = ref<HTMLInputElement | null>(null)\nconst list = ref<HTMLElement | null>(null)\n\nuseHotkey(() => props.hotkey, () => (open.value = !open.value))\nuseLayer({ active: open, elements: () => [panel.value], onEscape: () => close() })\nuseFocusTrap(panel, open, { initialFocus: () => input.value })\nuseScrollLock(open)\n\nwatch(open, (value) => {\n if (!value) return\n search.value = ''\n activeIndex.value = Math.max(0, firstEnabled())\n nextTick(() => list.value?.scrollTo({ top: 0 }))\n})\n\nfunction close() {\n open.value = false\n}\n\nlet pressedOutside = false\nfunction onPointerDown(event: PointerEvent) {\n pressedOutside = event.target === event.currentTarget\n}\nfunction onClick(event: MouseEvent) {\n if (pressedOutside && event.target === event.currentTarget) close()\n pressedOutside = false\n}\n\n// --- Selection & navigation ------------------------------------------------\nfunction choose(row: Row | undefined) {\n if (!row || row.item.disabled) return\n row.item.onSelect?.(row.item)\n emit('select', row.item)\n close()\n}\n\nfunction setActive(index: number) {\n if (index < 0) return\n activeIndex.value = index\n const row = rows.value[index]\n if (!row) return\n nextTick(() => {\n const el = document.getElementById(optionId(row.key))\n // Keep a little context around the active row at the list edges.\n if (index === 0) list.value?.scrollTo({ top: 0 })\n else el?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction move(delta: 1 | -1, distance = 1) {\n const count = rows.value.length\n if (!count) return\n let index = activeIndex.value\n for (let step = 0; step < distance; step++) {\n const next = firstEnabled((index + delta + count) % count, delta)\n if (next === -1) return\n // Page jumps stop at the ends instead of wrapping.\n if (distance > 1 && ((delta === 1 && next < index) || (delta === -1 && next > index))) break\n index = next\n }\n setActive(index)\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.isComposing) return\n // Emacs-style Ctrl+N / Ctrl+P for people who never leave the home row.\n const ctrl = event.ctrlKey && !event.metaKey && !event.altKey\n const key = event.key\n if (key === 'ArrowDown' || (ctrl && key === 'n')) move(1)\n else if (key === 'ArrowUp' || (ctrl && key === 'p')) move(-1)\n else if (key === 'PageDown') move(1, 5)\n else if (key === 'PageUp') move(-1, 5)\n else if (key === 'Enter') choose(activeRow.value)\n else return\n event.preventDefault()\n}\n\n// Hover follows the pointer only when it moves | scrolling with the keyboard\n// under a resting cursor must not steal the selection.\nfunction onRowPointerMove(index: number) {\n if (activeIndex.value !== index && !rows.value[index]?.item.disabled) activeIndex.value = index\n}\n\n// --- Announcements ---------------------------------------------------------\nconst announcement = ref('')\nlet announceTimer: ReturnType<typeof setTimeout> | undefined\nwatch([rows, () => props.loading], () => {\n clearTimeout(announceTimer)\n announceTimer = setTimeout(() => {\n if (!open.value) return\n if (props.loading) announcement.value = 'Loading results'\n else announcement.value = rows.value.length === 1 ? '1 result' : `${rows.value.length} results`\n }, 350)\n})\n\n// Shortcut glyphs depend on the platform | resolve them on the client only.\nconst mounted = ref(false)\nonMounted(() => (mounted.value = true))\nconst keys = (hotkey: string) => (mounted.value ? formatHotkey(hotkey) : [])\n\ndefineExpose({ close, focus: () => input.value?.focus() })\n</script>\n\n<template>\n <JPortal>\n <Transition name=\"j-command-overlay\">\n <div v-if=\"open\" class=\"j-command__overlay\" aria-hidden=\"true\" />\n </Transition>\n <Transition name=\"j-command\" :duration=\"{ enter: 180, leave: 110 }\">\n <div v-if=\"open\" class=\"j-command__positioner\" @pointerdown=\"onPointerDown\" @click=\"onClick\">\n <div\n ref=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"label\"\n class=\"j-command\"\n >\n <div class=\"j-command__search\">\n <IconSearch class=\"j-command__search-icon\" />\n <input\n ref=\"input\"\n v-model=\"search\"\n class=\"j-command__input\"\n type=\"text\"\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-autocomplete=\"list\"\n :aria-controls=\"listboxId\"\n :aria-activedescendant=\"activeRow ? optionId(activeRow.key) : undefined\"\n :aria-label=\"label\"\n :placeholder=\"placeholder\"\n autocomplete=\"off\"\n autocorrect=\"off\"\n autocapitalize=\"off\"\n spellcheck=\"false\"\n enterkeyhint=\"go\"\n @keydown=\"onKeydown\"\n />\n <button type=\"button\" class=\"j-command__esc j-kbd\" tabindex=\"-1\" aria-label=\"Close\" @click=\"close\">\n Esc\n </button>\n <span v-if=\"loading\" class=\"j-command__progress\" aria-hidden=\"true\" />\n </div>\n\n <div\n :id=\"listboxId\"\n ref=\"list\"\n role=\"listbox\"\n :aria-label=\"label\"\n class=\"j-command__list\"\n @pointerdown.prevent\n >\n <div\n v-for=\"section in sections\"\n :key=\"section.id\"\n role=\"group\"\n class=\"j-command__group\"\n :aria-labelledby=\"section.label ? `${id}-group-${section.id}` : undefined\"\n >\n <div v-if=\"section.label\" :id=\"`${id}-group-${section.id}`\" class=\"j-command__group-label\" role=\"presentation\">\n {{ section.label }}\n </div>\n <div\n v-for=\"row in section.rows\"\n :id=\"optionId(row.key)\"\n :key=\"row.key\"\n role=\"option\"\n class=\"j-command__item\"\n :class=\"{ 'is-active': row.key === activeRow?.key }\"\n :aria-selected=\"row.key === activeRow?.key\"\n :aria-disabled=\"row.item.disabled || undefined\"\n @pointermove=\"onRowPointerMove(rowIndex.get(row.key)!)\"\n @click=\"choose(row)\"\n >\n <slot name=\"item\" :item=\"row.item\" :active=\"row.key === activeRow?.key\">\n <span v-if=\"row.item.icon\" class=\"j-command__item-icon\">\n <component :is=\"row.item.icon\" />\n </span>\n <span class=\"j-command__item-text\">\n <span class=\"j-command__item-label\" :class=\"{ 'is-searching': row.ranges.length }\">\n <template v-for=\"(part, i) in splitByRanges(row.item.label, row.ranges)\" :key=\"i\">\n <mark v-if=\"part.match\">{{ part.text }}</mark>\n <template v-else>{{ part.text }}</template>\n </template>\n </span>\n <span v-if=\"row.item.description\" class=\"j-command__item-description\">{{ row.item.description }}</span>\n </span>\n <span v-if=\"row.item.shortcut\" class=\"j-command__item-shortcut\" aria-hidden=\"true\">\n <kbd v-for=\"key in keys(row.item.shortcut)\" :key=\"key\" class=\"j-kbd\">{{ key }}</kbd>\n </span>\n </slot>\n </div>\n </div>\n\n <div v-if=\"!rows.length\" class=\"j-command__empty\" role=\"presentation\">\n <template v-if=\"loading\">\n <Spinner :size=\"16\" />\n <span>Searching…</span>\n </template>\n <slot v-else name=\"empty\" :search=\"search\">\n <span class=\"j-command__empty-title\">{{ emptyText }}</span>\n <span v-if=\"search\" class=\"j-command__empty-hint\">Nothing matches “{{ search }}”. Try fewer words.</span>\n </slot>\n </div>\n </div>\n\n <div class=\"j-command__footer\">\n <slot name=\"footer\">\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">↑</kbd><kbd class=\"j-kbd\">↓</kbd> Navigate</span>\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">↵</kbd> Select</span>\n <span class=\"j-command__hint\"><kbd class=\"j-kbd\">Esc</kbd> Close</span>\n </slot>\n </div>\n\n <span class=\"j-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ announcement }}</span>\n </div>\n </div>\n </Transition>\n </JPortal>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-command__overlay {\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-overlay);\n background: var(--juxt-overlay);\n}\n\n.j-command__positioner {\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-modal);\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding: max(12vh, var(--juxt-space-4)) var(--juxt-space-4) var(--juxt-space-4);\n}\n\n.j-command {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n max-width: 40rem;\n max-height: min(34rem, calc(100dvh - 12vh - var(--juxt-space-8)));\n overflow: hidden;\n border: 1px solid var(--juxt-border);\n border-radius: var(--juxt-radius-lg);\n background: var(--juxt-surface-raised);\n box-shadow: var(--juxt-shadow-lg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n/* Search */\n.j-command__search {\n position: relative;\n display: flex;\n flex: none;\n align-items: center;\n gap: var(--juxt-space-3);\n height: 3.25rem;\n padding: 0 var(--juxt-space-3) 0 var(--juxt-space-4);\n border-bottom: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-command__search,\n[data-theme='dark'] .j-command__search {\n border-bottom-color: var(--juxt-border);\n}\n\n.j-command__search-icon {\n flex: none;\n width: 1.125rem;\n height: 1.125rem;\n color: var(--juxt-fg-muted);\n}\n\n.j-command__input {\n flex: 1;\n min-width: 0;\n height: 100%;\n padding: 0;\n border: 0;\n outline: none;\n background: transparent;\n color: var(--juxt-fg);\n font: inherit;\n font-size: var(--juxt-text-lg);\n letter-spacing: var(--juxt-tracking-tight);\n caret-color: var(--juxt-accent);\n}\n\n.j-command__input::placeholder {\n color: var(--juxt-fg-muted);\n}\n\n.j-command__esc {\n height: 1.375rem;\n padding: 0 var(--juxt-space-1-5);\n cursor: pointer;\n}\n\n.j-command__esc:hover {\n color: var(--juxt-fg-secondary);\n}\n\n/* An indeterminate hairline: present, never loud. */\n.j-command__progress {\n position: absolute;\n right: 0;\n bottom: -1px;\n left: 0;\n height: 1px;\n overflow: hidden;\n}\n\n.j-command__progress::after {\n content: '';\n position: absolute;\n inset: 0;\n width: 40%;\n background: linear-gradient(90deg, transparent, var(--juxt-accent), transparent);\n animation: j-command-progress 1.1s var(--juxt-ease-standard) infinite;\n}\n\n@keyframes j-command-progress {\n from {\n transform: translateX(-100%);\n }\n\n to {\n transform: translateX(250%);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-command__progress::after {\n width: 100%;\n animation: j-command-pulse 1.4s ease-in-out infinite;\n }\n\n @keyframes j-command-pulse {\n 50% {\n opacity: 0.3;\n }\n }\n}\n\n/* List */\n.j-command__list {\n flex: 1;\n min-height: 0;\n padding: var(--juxt-space-1-5);\n overflow-y: auto;\n overscroll-behavior: contain;\n scroll-padding-block: var(--juxt-space-8) var(--juxt-space-1-5);\n}\n\n.j-command__group + .j-command__group {\n margin-top: var(--juxt-space-1);\n}\n\n.j-command__group-label {\n padding: var(--juxt-space-2) var(--juxt-space-2-5) var(--juxt-space-1-5);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n user-select: none;\n}\n\n.j-command__item {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2-5);\n height: 2.5rem;\n padding: 0 var(--juxt-space-2-5);\n border-radius: var(--juxt-radius-sm);\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n user-select: none;\n}\n\n/* The one place the palette uses green: a hairline marking where you are. */\n.j-command__item::before {\n content: '';\n position: absolute;\n top: 0.625rem;\n bottom: 0.625rem;\n left: 0;\n width: 2px;\n border-radius: 0 2px 2px 0;\n background: var(--juxt-accent);\n opacity: 0;\n transform: scaleY(0.4);\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-command__item.is-active {\n background: var(--juxt-surface-hover);\n}\n\n.j-command__item.is-active::before {\n opacity: 1;\n transform: none;\n}\n\n.j-command__item[aria-disabled='true'] {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-command__item-icon {\n display: grid;\n flex: none;\n place-items: center;\n width: 1.25rem;\n height: 1.25rem;\n color: var(--juxt-fg-muted);\n}\n\n.j-command__item-icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-command__item.is-active .j-command__item-icon {\n color: var(--juxt-fg);\n}\n\n.j-command__item-text {\n display: flex;\n flex: 1;\n align-items: baseline;\n gap: var(--juxt-space-2);\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n}\n\n.j-command__item-label {\n flex: none;\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.j-command__item-label.is-searching {\n color: var(--juxt-fg-secondary);\n}\n\n.j-command__item-label mark {\n background: none;\n color: var(--juxt-fg);\n font-weight: var(--juxt-weight-medium);\n}\n\n.j-command__item-description {\n overflow: hidden;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n text-overflow: ellipsis;\n}\n\n.j-command__item-shortcut {\n display: inline-flex;\n flex: none;\n gap: 3px;\n}\n\n.j-command__empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--juxt-space-1-5);\n min-height: 8rem;\n padding: var(--juxt-space-6) var(--juxt-space-4);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n text-align: center;\n}\n\n.j-command__empty-title {\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n}\n\n/* Footer */\n.j-command__footer {\n display: flex;\n flex: none;\n align-items: center;\n gap: var(--juxt-space-4);\n height: 2.25rem;\n padding: 0 var(--juxt-space-4);\n border-top: 1px solid var(--juxt-border-subtle);\n background: var(--juxt-surface-sunken);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n}\n\n.dark .j-command__footer,\n[data-theme='dark'] .j-command__footer {\n border-top-color: var(--juxt-border);\n background: var(--juxt-surface);\n}\n\n.j-command__hint {\n display: inline-flex;\n align-items: center;\n gap: var(--juxt-space-1);\n}\n\n.j-command__hint .j-kbd + .j-kbd {\n margin-left: -1px;\n}\n\n/* Motion: quick in, quicker out. */\n.j-command-overlay-enter-active {\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-command-overlay-leave-active {\n transition: opacity var(--juxt-duration-instant) var(--juxt-ease-in);\n}\n\n.j-command-overlay-enter-from,\n.j-command-overlay-leave-to {\n opacity: 0;\n}\n\n.j-command-enter-active .j-command {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-command-leave-active .j-command {\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\n}\n\n.j-command-enter-from .j-command,\n.j-command-leave-to .j-command {\n opacity: 0;\n transform: translateY(calc(var(--juxt-motion-shift) * -1)) scale(0.985);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-command-enter-from .j-command,\n .j-command-leave-to .j-command {\n transform: none;\n }\n}\n\n@media (max-width: 639px) {\n .j-command__positioner {\n padding: var(--juxt-space-2);\n }\n\n .j-command {\n max-height: calc(100dvh - var(--juxt-space-4));\n }\n\n .j-command__footer {\n display: none;\n }\n}\n}\n</style>\n","import type { Ref } from 'vue'\nimport { ref } from 'vue'\nimport { prefersReducedMotion } from '../utils/dom'\nimport { useFocusTrap } from './useFocusTrap'\nimport { useLayer } from './useLayer'\nimport { useScrollLock } from './useScrollLock'\n\nexport interface ModalOptions {\n closeOnEscape: () => boolean\n closeOnOverlay: () => boolean\n close: () => void\n /** Selector for the element to focus first, inside the panel. */\n focusSelector?: string\n}\n\n/**\n * Internal: everything a modal surface (Dialog, Sheet) shares | layering,\n * focus trap and restoration, scroll lock, backdrop clicks and the \"nudge\"\n * when dismissal is refused.\n */\nexport function useModal(panel: Ref<HTMLElement | null>, open: Ref<boolean>, options: ModalOptions) {\n const nudging = ref(false)\n\n // A modal that refuses to close gives a small, polite nudge instead.\n function nudge() {\n if (prefersReducedMotion()) return\n nudging.value = false\n requestAnimationFrame(() => (nudging.value = true))\n }\n\n const { isTopLayer } = useLayer({\n active: open,\n elements: () => [panel.value],\n onEscape: () => (options.closeOnEscape() ? options.close() : nudge()),\n })\n\n useFocusTrap(panel, open, {\n // Prefer an explicit autofocus, then the first form field. Otherwise focus the\n // panel itself so screen readers announce it without jumping to a button.\n initialFocus: () =>\n panel.value?.querySelector<HTMLElement>('[autofocus], [data-autofocus]')\n ?? (options.focusSelector ? panel.value?.querySelector<HTMLElement>(options.focusSelector) : null)\n ?? panel.value,\n })\n\n useScrollLock(open)\n\n // Close on a click that both starts and ends on the backdrop, so dragging a\n // text selection out of the panel never dismisses it.\n let pressedOutside = false\n function onBackdropPointerDown(event: PointerEvent) {\n pressedOutside = event.target === event.currentTarget && isTopLayer()\n }\n function onBackdropClick(event: MouseEvent) {\n const outside = pressedOutside && event.target === event.currentTarget\n pressedOutside = false\n if (!outside) return\n if (options.closeOnOverlay()) options.close()\n else nudge()\n }\n\n return { nudging, onBackdropPointerDown, onBackdropClick }\n}\n\n/** Form fields worth focusing first in a modal body. */\nexport const FIELD_SELECTOR = ':is(input:not([type=hidden]), textarea, select, [role=combobox]):not(:disabled)'\n","<script setup lang=\"ts\">\nimport { ref, useId } from 'vue'\nimport { FIELD_SELECTOR, useModal } from '../../composables/useModal'\nimport { IconX } from '../../icons'\nimport JPortal from '../primitives/JPortal.vue'\n\nexport interface DialogProps {\n title?: string\n description?: string\n size?: 'sm' | 'md' | 'lg'\n /** Close when the backdrop is clicked. Defaults to true. */\n closeOnOverlay?: boolean\n /** Close when Escape is pressed. Defaults to true. */\n closeOnEscape?: boolean\n /** Hide the close button in the corner. */\n hideClose?: boolean\n /** Accessible label when there is no visible title. */\n ariaLabel?: string\n}\n\nconst props = withDefaults(defineProps<DialogProps>(), {\n size: 'md',\n closeOnOverlay: true,\n closeOnEscape: true,\n})\n\nconst open = defineModel<boolean>('open', { default: false })\n\nconst emit = defineEmits<{\n /** Fired after the leave transition finishes | a good moment to reset form state. */\n afterClose: []\n}>()\n\ndefineSlots<{\n default?: (props: { close: () => void }) => unknown\n /** Custom header. Put `titleId` on your heading so the dialog stays labelled. */\n header?: (props: { titleId: string, descriptionId: string }) => unknown\n footer?: (props: { close: () => void }) => unknown\n}>()\n\nconst id = useId()\nconst titleId = `j-${id}-title`\nconst descriptionId = `j-${id}-description`\n\nconst panel = ref<HTMLElement | null>(null)\n\nfunction close() {\n open.value = false\n}\n\nconst { nudging, onBackdropPointerDown, onBackdropClick } = useModal(panel, open, {\n closeOnEscape: () => props.closeOnEscape,\n closeOnOverlay: () => props.closeOnOverlay,\n close,\n focusSelector: `.j-dialog__body ${FIELD_SELECTOR}`,\n})\n\ndefineExpose({ close })\n</script>\n\n<template>\n <JPortal>\n <Transition name=\"j-dialog-overlay\">\n <div v-if=\"open\" class=\"j-dialog__overlay\" aria-hidden=\"true\" />\n </Transition>\n <Transition name=\"j-dialog\" :duration=\"{ enter: 240, leave: 150 }\" @after-leave=\"emit('afterClose')\">\n <div v-if=\"open\" class=\"j-dialog__positioner\" @pointerdown=\"onBackdropPointerDown\" @click=\"onBackdropClick\">\n <div\n ref=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n class=\"j-dialog__panel\"\n :class=\"[`j-dialog__panel--${size}`, { 'is-nudging': nudging }]\"\n :aria-labelledby=\"title || $slots.header ? titleId : undefined\"\n :aria-label=\"ariaLabel\"\n :aria-describedby=\"description ? descriptionId : undefined\"\n @animationend=\"nudging = false\"\n >\n <header v-if=\"title || description || $slots.header\" class=\"j-dialog__header\">\n <slot name=\"header\" :title-id=\"titleId\" :description-id=\"descriptionId\">\n <h2 :id=\"titleId\" class=\"j-dialog__title\">\n {{ title }}\n </h2>\n <p v-if=\"description\" :id=\"descriptionId\" class=\"j-dialog__description\">\n {{ description }}\n </p>\n </slot>\n </header>\n <div v-if=\"$slots.default\" class=\"j-dialog__body\">\n <slot :close=\"close\" />\n </div>\n <footer v-if=\"$slots.footer\" class=\"j-dialog__footer\">\n <slot name=\"footer\" :close=\"close\" />\n </footer>\n <button\n v-if=\"!hideClose\"\n type=\"button\"\n class=\"j-dialog__close j-focusable\"\n aria-label=\"Close\"\n @click=\"close\"\n >\n <IconX />\n </button>\n </div>\n </div>\n </Transition>\n </JPortal>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-dialog__overlay {\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-overlay);\n background: var(--juxt-overlay);\n}\n\n.j-dialog__positioner {\n --j-dialog-shift: calc(var(--juxt-motion-shift) * 2);\n\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-modal);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--juxt-space-4);\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n.j-dialog__panel {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n max-height: calc(100dvh - var(--juxt-space-8));\n border: 1px solid var(--juxt-border);\n border-radius: var(--juxt-radius-lg);\n background: var(--juxt-surface-raised);\n box-shadow: var(--juxt-shadow-lg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n outline: none;\n}\n\n.j-dialog__panel--sm {\n max-width: 25rem;\n}\n\n.j-dialog__panel--md {\n max-width: 30rem;\n}\n\n.j-dialog__panel--lg {\n max-width: 40rem;\n}\n\n.j-dialog__header {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-1-5);\n padding: var(--juxt-space-5) var(--juxt-space-12) 0 var(--juxt-space-5);\n}\n\n.j-dialog__title {\n margin: 0;\n font-size: var(--juxt-text-lg);\n font-weight: var(--juxt-weight-semibold);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-dialog__description {\n margin: 0;\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-dialog__body {\n flex: 1;\n min-height: 0;\n padding: var(--juxt-space-4) var(--juxt-space-5) 0;\n overflow-y: auto;\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-dialog__header + .j-dialog__footer {\n margin-top: var(--juxt-space-1);\n}\n\n.j-dialog__footer {\n display: flex;\n justify-content: flex-end;\n gap: var(--juxt-space-2);\n padding: var(--juxt-space-5);\n}\n\n.j-dialog__body:last-child {\n padding-bottom: var(--juxt-space-5);\n}\n\n.j-dialog__close {\n position: absolute;\n top: var(--juxt-space-3);\n right: var(--juxt-space-3);\n display: grid;\n place-items: center;\n width: var(--juxt-control-sm);\n height: var(--juxt-control-sm);\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg-muted);\n cursor: pointer;\n transition-property: background-color, color, outline-color, outline-offset;\n}\n\n.j-dialog__close:hover {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-dialog__close > svg {\n width: 1rem;\n height: 1rem;\n}\n\n/* Motion: the backdrop fades; the panel fades, lifts and settles. */\n.j-dialog-overlay-enter-active {\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-dialog-overlay-leave-active {\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-in);\n}\n\n.j-dialog-overlay-enter-from,\n.j-dialog-overlay-leave-to {\n opacity: 0;\n}\n\n.j-dialog-enter-active .j-dialog__panel {\n transition:\n opacity var(--juxt-duration-normal) var(--juxt-ease-out),\n transform var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n.j-dialog-leave-active .j-dialog__panel {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-in),\n transform var(--juxt-duration-fast) var(--juxt-ease-in);\n}\n\n.j-dialog-enter-from .j-dialog__panel,\n.j-dialog-leave-to .j-dialog__panel {\n opacity: 0;\n transform: translateY(var(--j-dialog-shift)) scale(var(--juxt-motion-scale));\n}\n\n.j-dialog__panel.is-nudging {\n animation: j-dialog-nudge 220ms var(--juxt-ease-out);\n}\n\n@keyframes j-dialog-nudge {\n 40% {\n transform: scale(1.012);\n }\n}\n\n/* Small screens: a sheet anchored to the bottom, within thumb reach. */\n@media (max-width: 639px) {\n .j-dialog__positioner {\n --j-dialog-shift: calc(var(--juxt-motion-shift) * 6);\n\n align-items: flex-end;\n padding: var(--juxt-space-2);\n padding-bottom: max(var(--juxt-space-2), env(safe-area-inset-bottom));\n }\n\n .j-dialog__panel {\n max-width: none;\n max-height: calc(100dvh - var(--juxt-space-12));\n }\n\n .j-dialog-enter-from .j-dialog__panel,\n .j-dialog-leave-to .j-dialog__panel {\n transform: translateY(var(--j-dialog-shift));\n }\n\n .j-dialog__footer {\n flex-direction: column-reverse;\n }\n\n .j-dialog__footer > * {\n width: 100%;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, useId } from 'vue'\nimport { FIELD_SELECTOR, useModal } from '../../composables/useModal'\nimport { IconX } from '../../icons'\nimport JPortal from '../primitives/JPortal.vue'\n\nexport interface DialogProps {\n title?: string\n description?: string\n size?: 'sm' | 'md' | 'lg'\n /** Close when the backdrop is clicked. Defaults to true. */\n closeOnOverlay?: boolean\n /** Close when Escape is pressed. Defaults to true. */\n closeOnEscape?: boolean\n /** Hide the close button in the corner. */\n hideClose?: boolean\n /** Accessible label when there is no visible title. */\n ariaLabel?: string\n}\n\nconst props = withDefaults(defineProps<DialogProps>(), {\n size: 'md',\n closeOnOverlay: true,\n closeOnEscape: true,\n})\n\nconst open = defineModel<boolean>('open', { default: false })\n\nconst emit = defineEmits<{\n /** Fired after the leave transition finishes | a good moment to reset form state. */\n afterClose: []\n}>()\n\ndefineSlots<{\n default?: (props: { close: () => void }) => unknown\n /** Custom header. Put `titleId` on your heading so the dialog stays labelled. */\n header?: (props: { titleId: string, descriptionId: string }) => unknown\n footer?: (props: { close: () => void }) => unknown\n}>()\n\nconst id = useId()\nconst titleId = `j-${id}-title`\nconst descriptionId = `j-${id}-description`\n\nconst panel = ref<HTMLElement | null>(null)\n\nfunction close() {\n open.value = false\n}\n\nconst { nudging, onBackdropPointerDown, onBackdropClick } = useModal(panel, open, {\n closeOnEscape: () => props.closeOnEscape,\n closeOnOverlay: () => props.closeOnOverlay,\n close,\n focusSelector: `.j-dialog__body ${FIELD_SELECTOR}`,\n})\n\ndefineExpose({ close })\n</script>\n\n<template>\n <JPortal>\n <Transition name=\"j-dialog-overlay\">\n <div v-if=\"open\" class=\"j-dialog__overlay\" aria-hidden=\"true\" />\n </Transition>\n <Transition name=\"j-dialog\" :duration=\"{ enter: 240, leave: 150 }\" @after-leave=\"emit('afterClose')\">\n <div v-if=\"open\" class=\"j-dialog__positioner\" @pointerdown=\"onBackdropPointerDown\" @click=\"onBackdropClick\">\n <div\n ref=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n class=\"j-dialog__panel\"\n :class=\"[`j-dialog__panel--${size}`, { 'is-nudging': nudging }]\"\n :aria-labelledby=\"title || $slots.header ? titleId : undefined\"\n :aria-label=\"ariaLabel\"\n :aria-describedby=\"description ? descriptionId : undefined\"\n @animationend=\"nudging = false\"\n >\n <header v-if=\"title || description || $slots.header\" class=\"j-dialog__header\">\n <slot name=\"header\" :title-id=\"titleId\" :description-id=\"descriptionId\">\n <h2 :id=\"titleId\" class=\"j-dialog__title\">\n {{ title }}\n </h2>\n <p v-if=\"description\" :id=\"descriptionId\" class=\"j-dialog__description\">\n {{ description }}\n </p>\n </slot>\n </header>\n <div v-if=\"$slots.default\" class=\"j-dialog__body\">\n <slot :close=\"close\" />\n </div>\n <footer v-if=\"$slots.footer\" class=\"j-dialog__footer\">\n <slot name=\"footer\" :close=\"close\" />\n </footer>\n <button\n v-if=\"!hideClose\"\n type=\"button\"\n class=\"j-dialog__close j-focusable\"\n aria-label=\"Close\"\n @click=\"close\"\n >\n <IconX />\n </button>\n </div>\n </div>\n </Transition>\n </JPortal>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-dialog__overlay {\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-overlay);\n background: var(--juxt-overlay);\n}\n\n.j-dialog__positioner {\n --j-dialog-shift: calc(var(--juxt-motion-shift) * 2);\n\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-modal);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--juxt-space-4);\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n.j-dialog__panel {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n max-height: calc(100dvh - var(--juxt-space-8));\n border: 1px solid var(--juxt-border);\n border-radius: var(--juxt-radius-lg);\n background: var(--juxt-surface-raised);\n box-shadow: var(--juxt-shadow-lg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n outline: none;\n}\n\n.j-dialog__panel--sm {\n max-width: 25rem;\n}\n\n.j-dialog__panel--md {\n max-width: 30rem;\n}\n\n.j-dialog__panel--lg {\n max-width: 40rem;\n}\n\n.j-dialog__header {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-1-5);\n padding: var(--juxt-space-5) var(--juxt-space-12) 0 var(--juxt-space-5);\n}\n\n.j-dialog__title {\n margin: 0;\n font-size: var(--juxt-text-lg);\n font-weight: var(--juxt-weight-semibold);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-dialog__description {\n margin: 0;\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-dialog__body {\n flex: 1;\n min-height: 0;\n padding: var(--juxt-space-4) var(--juxt-space-5) 0;\n overflow-y: auto;\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-dialog__header + .j-dialog__footer {\n margin-top: var(--juxt-space-1);\n}\n\n.j-dialog__footer {\n display: flex;\n justify-content: flex-end;\n gap: var(--juxt-space-2);\n padding: var(--juxt-space-5);\n}\n\n.j-dialog__body:last-child {\n padding-bottom: var(--juxt-space-5);\n}\n\n.j-dialog__close {\n position: absolute;\n top: var(--juxt-space-3);\n right: var(--juxt-space-3);\n display: grid;\n place-items: center;\n width: var(--juxt-control-sm);\n height: var(--juxt-control-sm);\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg-muted);\n cursor: pointer;\n transition-property: background-color, color, outline-color, outline-offset;\n}\n\n.j-dialog__close:hover {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-dialog__close > svg {\n width: 1rem;\n height: 1rem;\n}\n\n/* Motion: the backdrop fades; the panel fades, lifts and settles. */\n.j-dialog-overlay-enter-active {\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-dialog-overlay-leave-active {\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-in);\n}\n\n.j-dialog-overlay-enter-from,\n.j-dialog-overlay-leave-to {\n opacity: 0;\n}\n\n.j-dialog-enter-active .j-dialog__panel {\n transition:\n opacity var(--juxt-duration-normal) var(--juxt-ease-out),\n transform var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n.j-dialog-leave-active .j-dialog__panel {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-in),\n transform var(--juxt-duration-fast) var(--juxt-ease-in);\n}\n\n.j-dialog-enter-from .j-dialog__panel,\n.j-dialog-leave-to .j-dialog__panel {\n opacity: 0;\n transform: translateY(var(--j-dialog-shift)) scale(var(--juxt-motion-scale));\n}\n\n.j-dialog__panel.is-nudging {\n animation: j-dialog-nudge 220ms var(--juxt-ease-out);\n}\n\n@keyframes j-dialog-nudge {\n 40% {\n transform: scale(1.012);\n }\n}\n\n/* Small screens: a sheet anchored to the bottom, within thumb reach. */\n@media (max-width: 639px) {\n .j-dialog__positioner {\n --j-dialog-shift: calc(var(--juxt-motion-shift) * 6);\n\n align-items: flex-end;\n padding: var(--juxt-space-2);\n padding-bottom: max(var(--juxt-space-2), env(safe-area-inset-bottom));\n }\n\n .j-dialog__panel {\n max-width: none;\n max-height: calc(100dvh - var(--juxt-space-12));\n }\n\n .j-dialog-enter-from .j-dialog__panel,\n .j-dialog-leave-to .j-dialog__panel {\n transform: translateY(var(--j-dialog-shift));\n }\n\n .j-dialog__footer {\n flex-direction: column-reverse;\n }\n\n .j-dialog__footer > * {\n width: 100%;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\ndefineSlots<{ default?: () => unknown }>()\n</script>\n\n<template>\n <div class=\"j-menu__label\" role=\"presentation\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\ndefineSlots<{ default?: () => unknown }>()\n</script>\n\n<template>\n <div class=\"j-menu__label\" role=\"presentation\">\n <slot />\n </div>\n</template>\n","<template>\n <div role=\"separator\" aria-orientation=\"horizontal\" class=\"j-menu__separator\" />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, nextTick, ref, useId } from 'vue'\nimport { IconChevronRight } from '../../icons'\nimport { focusElement } from '../../utils/dom'\nimport { getMenuItems, MENU_LEVEL, MENU_ROOT } from './context'\nimport JMenuContent from './JMenuContent.vue'\n\nexport interface DropdownMenuSubProps {\n label: string\n disabled?: boolean\n}\n\nconst props = defineProps<DropdownMenuSubProps>()\n\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\n\nconst root = inject(MENU_ROOT)!\nconst level = inject(MENU_LEVEL)!\n\nconst id = useId()\nconst itemId = `j-${id}-subtrigger`\nconst menuId = `j-${id}-submenu`\nconst item = ref<HTMLElement | null>(null)\nconst sub = ref<InstanceType<typeof JMenuContent> | null>(null)\nconst initialFocus = ref<'first' | 'none'>('none')\nconst isOpen = computed(() => level.openSub.value === menuId)\n\nlet openTimer: ReturnType<typeof setTimeout> | undefined\n\nfunction openSub(focus: 'first' | 'none') {\n if (props.disabled) return\n initialFocus.value = focus\n level.setOpenSub(menuId)\n}\n\nfunction closeSub(returnFocus: boolean) {\n if (level.openSub.value === menuId) level.setOpenSub(null)\n if (returnFocus) nextTick(() => focusElement(item.value))\n}\n\nfunction onPointerMove() {\n level.cancelSubClose()\n if (props.disabled) return\n if (document.activeElement !== item.value) focusElement(item.value)\n if (isOpen.value) return\n clearTimeout(openTimer)\n openTimer = setTimeout(() => openSub('none'), 90)\n}\n\nfunction onPointerLeave() {\n clearTimeout(openTimer)\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'ArrowRight' || event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n event.stopPropagation()\n if (isOpen.value) focusElement(getMenuItems(sub.value?.content ?? null)[0])\n else openSub('first')\n }\n}\n\nfunction onClick() {\n openSub('first')\n}\n\n// A press outside the submenu either lands back in the parent (close just the\n// submenu) or somewhere else entirely (close the whole menu).\nfunction onOutside(event: PointerEvent) {\n const target = event.target as Node\n if (level.content.value?.contains(target)) closeSub(false)\n else root.close(false)\n}\n</script>\n\n<template>\n <div\n :id=\"itemId\"\n ref=\"item\"\n role=\"menuitem\"\n tabindex=\"-1\"\n class=\"j-menu__item\"\n aria-haspopup=\"menu\"\n :aria-expanded=\"isOpen\"\n :aria-controls=\"isOpen ? menuId : undefined\"\n :aria-disabled=\"disabled || undefined\"\n @pointermove=\"onPointerMove\"\n @pointerleave=\"onPointerLeave\"\n @keydown=\"onKeydown\"\n @click=\"onClick\"\n >\n <span v-if=\"$slots.leading\" class=\"j-menu__icon\"><slot name=\"leading\" /></span>\n <span class=\"j-menu__text\">{{ label }}</span>\n <IconChevronRight class=\"j-menu__chevron\" />\n </div>\n <JMenuContent\n :id=\"menuId\"\n ref=\"sub\"\n :open=\"isOpen\"\n :reference=\"item\"\n :labelledby=\"itemId\"\n placement=\"right-start\"\n :offset=\"{ mainAxis: 2, crossAxis: -5 }\"\n :initial-focus=\"initialFocus\"\n is-sub\n @escape=\"closeSub(true)\"\n @back=\"closeSub(true)\"\n @outside=\"onOutside\"\n @pointerenter=\"level.cancelSubClose()\"\n >\n <slot />\n </JMenuContent>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, nextTick, ref, useId } from 'vue'\nimport { IconChevronRight } from '../../icons'\nimport { focusElement } from '../../utils/dom'\nimport { getMenuItems, MENU_LEVEL, MENU_ROOT } from './context'\nimport JMenuContent from './JMenuContent.vue'\n\nexport interface DropdownMenuSubProps {\n label: string\n disabled?: boolean\n}\n\nconst props = defineProps<DropdownMenuSubProps>()\n\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\n\nconst root = inject(MENU_ROOT)!\nconst level = inject(MENU_LEVEL)!\n\nconst id = useId()\nconst itemId = `j-${id}-subtrigger`\nconst menuId = `j-${id}-submenu`\nconst item = ref<HTMLElement | null>(null)\nconst sub = ref<InstanceType<typeof JMenuContent> | null>(null)\nconst initialFocus = ref<'first' | 'none'>('none')\nconst isOpen = computed(() => level.openSub.value === menuId)\n\nlet openTimer: ReturnType<typeof setTimeout> | undefined\n\nfunction openSub(focus: 'first' | 'none') {\n if (props.disabled) return\n initialFocus.value = focus\n level.setOpenSub(menuId)\n}\n\nfunction closeSub(returnFocus: boolean) {\n if (level.openSub.value === menuId) level.setOpenSub(null)\n if (returnFocus) nextTick(() => focusElement(item.value))\n}\n\nfunction onPointerMove() {\n level.cancelSubClose()\n if (props.disabled) return\n if (document.activeElement !== item.value) focusElement(item.value)\n if (isOpen.value) return\n clearTimeout(openTimer)\n openTimer = setTimeout(() => openSub('none'), 90)\n}\n\nfunction onPointerLeave() {\n clearTimeout(openTimer)\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'ArrowRight' || event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n event.stopPropagation()\n if (isOpen.value) focusElement(getMenuItems(sub.value?.content ?? null)[0])\n else openSub('first')\n }\n}\n\nfunction onClick() {\n openSub('first')\n}\n\n// A press outside the submenu either lands back in the parent (close just the\n// submenu) or somewhere else entirely (close the whole menu).\nfunction onOutside(event: PointerEvent) {\n const target = event.target as Node\n if (level.content.value?.contains(target)) closeSub(false)\n else root.close(false)\n}\n</script>\n\n<template>\n <div\n :id=\"itemId\"\n ref=\"item\"\n role=\"menuitem\"\n tabindex=\"-1\"\n class=\"j-menu__item\"\n aria-haspopup=\"menu\"\n :aria-expanded=\"isOpen\"\n :aria-controls=\"isOpen ? menuId : undefined\"\n :aria-disabled=\"disabled || undefined\"\n @pointermove=\"onPointerMove\"\n @pointerleave=\"onPointerLeave\"\n @keydown=\"onKeydown\"\n @click=\"onClick\"\n >\n <span v-if=\"$slots.leading\" class=\"j-menu__icon\"><slot name=\"leading\" /></span>\n <span class=\"j-menu__text\">{{ label }}</span>\n <IconChevronRight class=\"j-menu__chevron\" />\n </div>\n <JMenuContent\n :id=\"menuId\"\n ref=\"sub\"\n :open=\"isOpen\"\n :reference=\"item\"\n :labelledby=\"itemId\"\n placement=\"right-start\"\n :offset=\"{ mainAxis: 2, crossAxis: -5 }\"\n :initial-focus=\"initialFocus\"\n is-sub\n @escape=\"closeSub(true)\"\n @back=\"closeSub(true)\"\n @outside=\"onOutside\"\n @pointerenter=\"level.cancelSubClose()\"\n >\n <slot />\n </JMenuContent>\n</template>\n","<script setup lang=\"ts\">\nexport interface EmptyStateProps {\n title?: string\n description?: string\n size?: 'sm' | 'md' | 'lg'\n}\n\nwithDefaults(defineProps<EmptyStateProps>(), { size: 'md' })\n\ndefineSlots<{\n /** Icon or illustration shown above the title. */\n icon?: () => unknown\n default?: () => unknown\n /** Call to action, typically a `JButton`. */\n actions?: () => unknown\n}>()\n</script>\n\n<template>\n <div class=\"j-empty-state\" :class=\"`j-empty-state--${size}`\">\n <div v-if=\"$slots.icon\" class=\"j-empty-state__icon\">\n <slot name=\"icon\" />\n </div>\n <p v-if=\"title\" class=\"j-empty-state__title\">\n {{ title }}\n </p>\n <div v-if=\"description || $slots.default\" class=\"j-empty-state__description\">\n <slot>{{ description }}</slot>\n </div>\n <div v-if=\"$slots.actions\" class=\"j-empty-state__actions\">\n <slot name=\"actions\" />\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n max-width: 24rem;\n margin: 0 auto;\n padding: var(--juxt-space-8) var(--juxt-space-4);\n text-align: center;\n font-family: var(--juxt-font-sans);\n}\n\n.j-empty-state--sm {\n max-width: 18rem;\n padding: var(--juxt-space-6) var(--juxt-space-3);\n}\n\n.j-empty-state--lg {\n max-width: 28rem;\n padding: var(--juxt-space-12) var(--juxt-space-4);\n}\n\n.j-empty-state__icon {\n display: grid;\n place-items: center;\n width: 2.75rem;\n height: 2.75rem;\n margin-bottom: var(--juxt-space-4);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-surface-sunken);\n color: var(--juxt-fg-muted);\n}\n\n.j-empty-state--lg .j-empty-state__icon {\n width: 3.5rem;\n height: 3.5rem;\n}\n\n.j-empty-state__icon > svg {\n width: 1.25rem;\n height: 1.25rem;\n}\n\n.j-empty-state--lg .j-empty-state__icon > svg {\n width: 1.5rem;\n height: 1.5rem;\n}\n\n.j-empty-state__title {\n margin: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-lg);\n font-weight: var(--juxt-weight-semibold);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-empty-state__description {\n margin-top: var(--juxt-space-1-5);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-empty-state__description > p {\n margin: 0;\n}\n\n.j-empty-state__actions {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: var(--juxt-space-2);\n margin-top: var(--juxt-space-5);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport interface EmptyStateProps {\n title?: string\n description?: string\n size?: 'sm' | 'md' | 'lg'\n}\n\nwithDefaults(defineProps<EmptyStateProps>(), { size: 'md' })\n\ndefineSlots<{\n /** Icon or illustration shown above the title. */\n icon?: () => unknown\n default?: () => unknown\n /** Call to action, typically a `JButton`. */\n actions?: () => unknown\n}>()\n</script>\n\n<template>\n <div class=\"j-empty-state\" :class=\"`j-empty-state--${size}`\">\n <div v-if=\"$slots.icon\" class=\"j-empty-state__icon\">\n <slot name=\"icon\" />\n </div>\n <p v-if=\"title\" class=\"j-empty-state__title\">\n {{ title }}\n </p>\n <div v-if=\"description || $slots.default\" class=\"j-empty-state__description\">\n <slot>{{ description }}</slot>\n </div>\n <div v-if=\"$slots.actions\" class=\"j-empty-state__actions\">\n <slot name=\"actions\" />\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n max-width: 24rem;\n margin: 0 auto;\n padding: var(--juxt-space-8) var(--juxt-space-4);\n text-align: center;\n font-family: var(--juxt-font-sans);\n}\n\n.j-empty-state--sm {\n max-width: 18rem;\n padding: var(--juxt-space-6) var(--juxt-space-3);\n}\n\n.j-empty-state--lg {\n max-width: 28rem;\n padding: var(--juxt-space-12) var(--juxt-space-4);\n}\n\n.j-empty-state__icon {\n display: grid;\n place-items: center;\n width: 2.75rem;\n height: 2.75rem;\n margin-bottom: var(--juxt-space-4);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-surface-sunken);\n color: var(--juxt-fg-muted);\n}\n\n.j-empty-state--lg .j-empty-state__icon {\n width: 3.5rem;\n height: 3.5rem;\n}\n\n.j-empty-state__icon > svg {\n width: 1.25rem;\n height: 1.25rem;\n}\n\n.j-empty-state--lg .j-empty-state__icon > svg {\n width: 1.5rem;\n height: 1.5rem;\n}\n\n.j-empty-state__title {\n margin: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-lg);\n font-weight: var(--juxt-weight-semibold);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-empty-state__description {\n margin-top: var(--juxt-space-1-5);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-empty-state__description > p {\n margin: 0;\n}\n\n.j-empty-state__actions {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: var(--juxt-space-2);\n margin-top: var(--juxt-space-5);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nexport interface FooterProps {\n /** Maximum content width; any CSS length. `none` spans the full width. */\n maxWidth?: string\n /** Hairline above the footer. */\n bordered?: boolean\n}\n\nconst props = withDefaults(defineProps<FooterProps>(), { maxWidth: '72rem', bordered: true })\n\ndefineSlots<{\n /** Logo and a short line about the product. */\n brand?: () => unknown\n /** Link columns, typically `JFooterColumn`s. */\n default?: () => unknown\n /** The last line: copyright, legal links, social icons. */\n bottom?: () => unknown\n}>()\n\nconst style = computed(() => ({ '--j-footer-max': props.maxWidth === 'none' ? 'none' : props.maxWidth }))\n</script>\n\n<template>\n <footer class=\"j-footer\" :class=\"{ 'is-bordered': bordered }\" :style=\"style\">\n <div class=\"j-footer__inner\">\n <div v-if=\"$slots.brand || $slots.default\" class=\"j-footer__top\">\n <div v-if=\"$slots.brand\" class=\"j-footer__brand\">\n <slot name=\"brand\" />\n </div>\n <div v-if=\"$slots.default\" class=\"j-footer__columns\">\n <slot />\n </div>\n </div>\n <div v-if=\"$slots.bottom\" class=\"j-footer__bottom\">\n <slot name=\"bottom\" />\n </div>\n </div>\n </footer>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-footer {\n background: var(--juxt-bg);\n color: var(--juxt-fg-secondary);\n font-family: var(--juxt-font-sans);\n font-size: var(--juxt-text-sm);\n}\n\n.j-footer.is-bordered {\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-footer.is-bordered,\n[data-theme='dark'] .j-footer.is-bordered {\n border-top-color: var(--juxt-border);\n}\n\n.j-footer__inner {\n max-width: var(--j-footer-max);\n margin: 0 auto;\n padding: var(--juxt-space-12) var(--juxt-space-4) var(--juxt-space-8);\n}\n\n.j-footer__top {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--juxt-space-10);\n}\n\n.j-footer__brand {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-3);\n max-width: 18rem;\n color: var(--juxt-fg-muted);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-footer__columns {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--juxt-space-8) var(--juxt-space-6);\n}\n\n@media (min-width: 768px) {\n .j-footer__top {\n grid-template-columns: minmax(12rem, 1fr) minmax(0, 2fr);\n }\n\n .j-footer__columns {\n grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));\n }\n}\n\n.j-footer__bottom {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--juxt-space-3) var(--juxt-space-6);\n margin-top: var(--juxt-space-12);\n padding-top: var(--juxt-space-6);\n border-top: 1px solid var(--juxt-border-subtle);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n}\n\n.dark .j-footer__bottom,\n[data-theme='dark'] .j-footer__bottom {\n border-top-color: var(--juxt-border);\n}\n\n.j-footer__bottom a {\n color: inherit;\n text-decoration: none;\n transition: color var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-footer__bottom a:hover {\n color: var(--juxt-fg);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nexport interface FooterProps {\n /** Maximum content width; any CSS length. `none` spans the full width. */\n maxWidth?: string\n /** Hairline above the footer. */\n bordered?: boolean\n}\n\nconst props = withDefaults(defineProps<FooterProps>(), { maxWidth: '72rem', bordered: true })\n\ndefineSlots<{\n /** Logo and a short line about the product. */\n brand?: () => unknown\n /** Link columns, typically `JFooterColumn`s. */\n default?: () => unknown\n /** The last line: copyright, legal links, social icons. */\n bottom?: () => unknown\n}>()\n\nconst style = computed(() => ({ '--j-footer-max': props.maxWidth === 'none' ? 'none' : props.maxWidth }))\n</script>\n\n<template>\n <footer class=\"j-footer\" :class=\"{ 'is-bordered': bordered }\" :style=\"style\">\n <div class=\"j-footer__inner\">\n <div v-if=\"$slots.brand || $slots.default\" class=\"j-footer__top\">\n <div v-if=\"$slots.brand\" class=\"j-footer__brand\">\n <slot name=\"brand\" />\n </div>\n <div v-if=\"$slots.default\" class=\"j-footer__columns\">\n <slot />\n </div>\n </div>\n <div v-if=\"$slots.bottom\" class=\"j-footer__bottom\">\n <slot name=\"bottom\" />\n </div>\n </div>\n </footer>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-footer {\n background: var(--juxt-bg);\n color: var(--juxt-fg-secondary);\n font-family: var(--juxt-font-sans);\n font-size: var(--juxt-text-sm);\n}\n\n.j-footer.is-bordered {\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-footer.is-bordered,\n[data-theme='dark'] .j-footer.is-bordered {\n border-top-color: var(--juxt-border);\n}\n\n.j-footer__inner {\n max-width: var(--j-footer-max);\n margin: 0 auto;\n padding: var(--juxt-space-12) var(--juxt-space-4) var(--juxt-space-8);\n}\n\n.j-footer__top {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--juxt-space-10);\n}\n\n.j-footer__brand {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-3);\n max-width: 18rem;\n color: var(--juxt-fg-muted);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-footer__columns {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--juxt-space-8) var(--juxt-space-6);\n}\n\n@media (min-width: 768px) {\n .j-footer__top {\n grid-template-columns: minmax(12rem, 1fr) minmax(0, 2fr);\n }\n\n .j-footer__columns {\n grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));\n }\n}\n\n.j-footer__bottom {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--juxt-space-3) var(--juxt-space-6);\n margin-top: var(--juxt-space-12);\n padding-top: var(--juxt-space-6);\n border-top: 1px solid var(--juxt-border-subtle);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n}\n\n.dark .j-footer__bottom,\n[data-theme='dark'] .j-footer__bottom {\n border-top-color: var(--juxt-border);\n}\n\n.j-footer__bottom a {\n color: inherit;\n text-decoration: none;\n transition: color var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-footer__bottom a:hover {\n color: var(--juxt-fg);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useId } from 'vue'\n\nexport interface FooterColumnProps {\n title: string\n}\n\ndefineProps<FooterColumnProps>()\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst titleId = `j-${useId()}-title`\n</script>\n\n<template>\n <nav class=\"j-footer-column\" :aria-labelledby=\"titleId\">\n <p :id=\"titleId\" class=\"j-footer-column__title\">\n {{ title }}\n </p>\n <div class=\"j-footer-column__links\">\n <slot />\n </div>\n </nav>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-footer-column {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-3);\n min-width: 0;\n}\n\n.j-footer-column__title {\n margin: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-footer-column__links {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--juxt-space-2);\n}\n\n.j-footer-column__links a {\n border-radius: var(--juxt-radius-xs);\n color: var(--juxt-fg-muted);\n text-decoration: none;\n outline: 2px solid transparent;\n outline-offset: 0;\n transition:\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-footer-column__links a:hover {\n color: var(--juxt-fg);\n}\n\n.j-footer-column__links a:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useId } from 'vue'\n\nexport interface FooterColumnProps {\n title: string\n}\n\ndefineProps<FooterColumnProps>()\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst titleId = `j-${useId()}-title`\n</script>\n\n<template>\n <nav class=\"j-footer-column\" :aria-labelledby=\"titleId\">\n <p :id=\"titleId\" class=\"j-footer-column__title\">\n {{ title }}\n </p>\n <div class=\"j-footer-column__links\">\n <slot />\n </div>\n </nav>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-footer-column {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-3);\n min-width: 0;\n}\n\n.j-footer-column__title {\n margin: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-footer-column__links {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--juxt-space-2);\n}\n\n.j-footer-column__links a {\n border-radius: var(--juxt-radius-xs);\n color: var(--juxt-fg-muted);\n text-decoration: none;\n outline: 2px solid transparent;\n outline-offset: 0;\n transition:\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-footer-column__links a:hover {\n color: var(--juxt-fg);\n}\n\n.j-footer-column__links a:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { FieldProps } from '../field/useField'\nimport { ref } from 'vue'\nimport JFieldShell from '../field/JFieldShell.vue'\nimport { useField } from '../field/useField'\n\nexport type InputSize = 'sm' | 'md' | 'lg'\n\nexport interface InputProps extends FieldProps {\n type?: string\n placeholder?: string\n size?: InputSize\n readonly?: boolean\n name?: string\n autocomplete?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<InputProps>(), {\n type: 'text',\n size: 'md',\n error: undefined,\n})\n\nconst model = defineModel<string | number | null>({ default: '' })\n\ndefineSlots<{\n leading?: () => unknown\n trailing?: () => unknown\n label?: () => unknown\n}>()\n\nconst field = useField(props)\nconst input = ref<HTMLInputElement | null>(null)\n\n// Clicking the frame (padding, icons) focuses the input without a blur flicker.\nfunction onFramePointerDown(event: PointerEvent) {\n if (event.target === input.value || props.disabled) return\n if ((event.target as HTMLElement).closest('button, a, input, select, textarea, [tabindex]')) return\n event.preventDefault()\n input.value?.focus()\n}\n\ndefineExpose({\n /** The underlying `<input>` element. */\n input,\n focus: () => input.value?.focus(),\n select: () => input.value?.select(),\n})\n</script>\n\n<template>\n <JFieldShell\n :id=\"field.id.value\"\n :class=\"$attrs.class\"\n :style=\"$attrs.style\"\n :label-id=\"field.labelId.value\"\n :description-id=\"field.descriptionId.value\"\n :error-id=\"field.errorId.value\"\n :label=\"label\"\n :description=\"description\"\n :error-message=\"field.errorMessage.value\"\n :show-description=\"field.showDescription.value\"\n :required=\"required\"\n :disabled=\"disabled\"\n >\n <template v-if=\"$slots.label\" #label>\n <slot name=\"label\" />\n </template>\n <div\n class=\"j-control j-input\"\n :class=\"[`j-control--${size}`, { 'is-invalid': field.invalid.value, 'is-disabled': disabled, 'is-readonly': readonly }]\"\n @pointerdown=\"onFramePointerDown\"\n >\n <span v-if=\"$slots.leading\" class=\"j-control__adornment j-control__adornment--leading\">\n <slot name=\"leading\" />\n </span>\n <input\n :id=\"field.id.value\"\n ref=\"input\"\n v-model=\"model\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n class=\"j-input__native\"\n :type=\"type\"\n :name=\"name\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :required=\"required\"\n :autocomplete=\"autocomplete\"\n :aria-invalid=\"field.invalid.value || undefined\"\n :aria-describedby=\"field.describedBy.value\"\n />\n <span v-if=\"$slots.trailing\" class=\"j-control__adornment j-control__adornment--trailing\">\n <slot name=\"trailing\" />\n </span>\n </div>\n </JFieldShell>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-input__native {\n flex: 1;\n width: 100%;\n min-width: 0;\n height: calc(var(--j-control-height) - 2px);\n padding: 0;\n border: 0;\n outline: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-input__native::placeholder {\n color: var(--juxt-fg-muted);\n opacity: 1;\n}\n\n.j-input__native:disabled {\n cursor: not-allowed;\n}\n\n.j-input__native::-webkit-search-cancel-button {\n display: none;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { FieldProps } from '../field/useField'\nimport { ref } from 'vue'\nimport JFieldShell from '../field/JFieldShell.vue'\nimport { useField } from '../field/useField'\n\nexport type InputSize = 'sm' | 'md' | 'lg'\n\nexport interface InputProps extends FieldProps {\n type?: string\n placeholder?: string\n size?: InputSize\n readonly?: boolean\n name?: string\n autocomplete?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<InputProps>(), {\n type: 'text',\n size: 'md',\n error: undefined,\n})\n\nconst model = defineModel<string | number | null>({ default: '' })\n\ndefineSlots<{\n leading?: () => unknown\n trailing?: () => unknown\n label?: () => unknown\n}>()\n\nconst field = useField(props)\nconst input = ref<HTMLInputElement | null>(null)\n\n// Clicking the frame (padding, icons) focuses the input without a blur flicker.\nfunction onFramePointerDown(event: PointerEvent) {\n if (event.target === input.value || props.disabled) return\n if ((event.target as HTMLElement).closest('button, a, input, select, textarea, [tabindex]')) return\n event.preventDefault()\n input.value?.focus()\n}\n\ndefineExpose({\n /** The underlying `<input>` element. */\n input,\n focus: () => input.value?.focus(),\n select: () => input.value?.select(),\n})\n</script>\n\n<template>\n <JFieldShell\n :id=\"field.id.value\"\n :class=\"$attrs.class\"\n :style=\"$attrs.style\"\n :label-id=\"field.labelId.value\"\n :description-id=\"field.descriptionId.value\"\n :error-id=\"field.errorId.value\"\n :label=\"label\"\n :description=\"description\"\n :error-message=\"field.errorMessage.value\"\n :show-description=\"field.showDescription.value\"\n :required=\"required\"\n :disabled=\"disabled\"\n >\n <template v-if=\"$slots.label\" #label>\n <slot name=\"label\" />\n </template>\n <div\n class=\"j-control j-input\"\n :class=\"[`j-control--${size}`, { 'is-invalid': field.invalid.value, 'is-disabled': disabled, 'is-readonly': readonly }]\"\n @pointerdown=\"onFramePointerDown\"\n >\n <span v-if=\"$slots.leading\" class=\"j-control__adornment j-control__adornment--leading\">\n <slot name=\"leading\" />\n </span>\n <input\n :id=\"field.id.value\"\n ref=\"input\"\n v-model=\"model\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n class=\"j-input__native\"\n :type=\"type\"\n :name=\"name\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :required=\"required\"\n :autocomplete=\"autocomplete\"\n :aria-invalid=\"field.invalid.value || undefined\"\n :aria-describedby=\"field.describedBy.value\"\n />\n <span v-if=\"$slots.trailing\" class=\"j-control__adornment j-control__adornment--trailing\">\n <slot name=\"trailing\" />\n </span>\n </div>\n </JFieldShell>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-input__native {\n flex: 1;\n width: 100%;\n min-width: 0;\n height: calc(var(--j-control-height) - 2px);\n padding: 0;\n border: 0;\n outline: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-input__native::placeholder {\n color: var(--juxt-fg-muted);\n opacity: 1;\n}\n\n.j-input__native:disabled {\n cursor: not-allowed;\n}\n\n.j-input__native::-webkit-search-cancel-button {\n display: none;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'\nimport { useLayer } from '../../composables/useLayer'\nimport { focusElement } from '../../utils/dom'\nimport JBurger from '../burger/JBurger.vue'\n\nexport interface NavbarProps {\n /** Stick to the top of the viewport while scrolling. */\n sticky?: boolean\n /** Hairline under the bar. */\n bordered?: boolean\n /** Accessible name of the navigation landmark. */\n label?: string\n /** Width below which links collapse behind the burger. `never` keeps them inline. */\n collapse?: 'sm' | 'md' | 'lg' | 'never'\n /** Maximum content width; any CSS length. `none` spans the full width. */\n maxWidth?: string\n}\n\nconst props = withDefaults(defineProps<NavbarProps>(), {\n sticky: true,\n bordered: true,\n label: 'Main',\n collapse: 'md',\n maxWidth: '72rem',\n})\n\n/** Mobile menu state. */\nconst open = defineModel<boolean>('open', { default: false })\n\ndefineSlots<{\n /** Logo or product name, usually a link home. */\n brand?: () => unknown\n /** Navigation links, typically `JNavbarLink`s. Shown inline, or in the mobile menu. */\n default?: () => unknown\n /** Always visible on the right: search, theme switch, avatar. */\n actions?: () => unknown\n /** Extra content at the bottom of the mobile menu, e.g. sign-in buttons. */\n 'menu-footer'?: () => unknown\n}>()\n\nconst menuId = `j-${useId()}-menu`\nconst root = ref<HTMLElement | null>(null)\nconst menu = ref<HTMLElement | null>(null)\n\nconst breakpoints = { sm: 640, md: 768, lg: 1024 }\n\nfunction close(returnFocus = false) {\n if (!open.value) return\n open.value = false\n if (returnFocus) nextTick(() => focusElement(root.value?.querySelector<HTMLElement>('.j-burger')))\n}\n\nuseLayer({\n active: open,\n elements: () => [root.value],\n onEscape: () => close(true),\n onPointerDownOutside: () => close(),\n})\n\n// Choosing a destination closes the menu; opening a nested disclosure doesn't.\nfunction onMenuClick(event: MouseEvent) {\n const target = (event.target as HTMLElement).closest('a, button')\n if (target && !target.hasAttribute('aria-expanded')) close()\n}\n\n// Growing past the breakpoint makes the menu irrelevant, so drop it.\nlet media: MediaQueryList | undefined\nconst onMediaChange = (event: MediaQueryListEvent) => event.matches && close()\nonMounted(() => {\n if (props.collapse === 'never') return\n media = window.matchMedia(`(min-width: ${breakpoints[props.collapse]}px)`)\n media.addEventListener('change', onMediaChange)\n})\nonBeforeUnmount(() => media?.removeEventListener('change', onMediaChange))\n\nwatch(open, async (value) => {\n if (!value) return\n await nextTick()\n const first = menu.value?.querySelector<HTMLElement>('a, button')\n first?.focus({ preventScroll: true })\n})\n\nconst style = computed(() => ({ '--j-navbar-max': props.maxWidth === 'none' ? 'none' : props.maxWidth }))\n</script>\n\n<template>\n <header\n ref=\"root\"\n class=\"j-navbar\"\n :class=\"[`j-navbar--collapse-${collapse}`, { 'is-sticky': sticky, 'is-bordered': bordered, 'is-open': open }]\"\n :style=\"style\"\n >\n <div class=\"j-navbar__bar\">\n <div v-if=\"$slots.brand\" class=\"j-navbar__brand\">\n <slot name=\"brand\" />\n </div>\n <nav v-if=\"$slots.default\" class=\"j-navbar__links\" :aria-label=\"label\">\n <slot />\n </nav>\n <div class=\"j-navbar__end\">\n <div v-if=\"$slots.actions\" class=\"j-navbar__actions\">\n <slot name=\"actions\" />\n </div>\n <JBurger\n v-if=\"collapse !== 'never' && ($slots.default || $slots['menu-footer'])\"\n v-model=\"open\"\n class=\"j-navbar__burger\"\n :controls=\"menuId\"\n />\n </div>\n </div>\n <Transition name=\"j-navbar-menu\">\n <div v-show=\"open\" :id=\"menuId\" ref=\"menu\" class=\"j-navbar__menu\" @click=\"onMenuClick\">\n <nav v-if=\"$slots.default\" class=\"j-navbar__menu-links\" :aria-label=\"label\">\n <slot />\n </nav>\n <div v-if=\"$slots['menu-footer']\" class=\"j-navbar__menu-footer\">\n <slot name=\"menu-footer\" />\n </div>\n </div>\n </Transition>\n </header>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-navbar {\n --j-navbar-height: 3.5rem;\n\n position: relative;\n z-index: var(--juxt-z-sticky);\n background: var(--juxt-bg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-navbar.is-sticky {\n position: sticky;\n top: 0;\n}\n\n.j-navbar.is-bordered {\n box-shadow: inset 0 -1px 0 var(--juxt-border-subtle);\n}\n\n.dark .j-navbar.is-bordered,\n[data-theme='dark'] .j-navbar.is-bordered {\n box-shadow: inset 0 -1px 0 var(--juxt-border);\n}\n\n.j-navbar__bar {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-6);\n max-width: var(--j-navbar-max);\n height: var(--j-navbar-height);\n margin: 0 auto;\n padding: 0 var(--juxt-space-4);\n}\n\n.j-navbar__brand {\n display: flex;\n flex: none;\n align-items: center;\n}\n\n.j-navbar__links {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1);\n min-width: 0;\n}\n\n.j-navbar__end {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n margin-left: auto;\n}\n\n.j-navbar__actions {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n}\n\n.j-navbar__burger {\n display: none;\n margin-right: calc(var(--juxt-space-2) * -1);\n}\n\n/* Mobile menu: a panel that drops from the bar, links stacked with big targets. */\n.j-navbar__menu {\n position: absolute;\n top: 100%;\n right: 0;\n left: 0;\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-4);\n max-height: calc(100dvh - var(--j-navbar-height));\n padding: var(--juxt-space-3) var(--juxt-space-4) var(--juxt-space-5);\n overflow-y: auto;\n border-bottom: 1px solid var(--juxt-border);\n background: var(--juxt-bg);\n box-shadow: var(--juxt-shadow-md);\n}\n\n.j-navbar__menu-links {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n.j-navbar__menu-links .j-navbar-link {\n justify-content: flex-start;\n height: 2.75rem;\n padding: 0 var(--juxt-space-3);\n font-size: var(--juxt-text-lg);\n}\n\n.j-navbar__menu-footer {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-2);\n padding-top: var(--juxt-space-4);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.j-navbar__menu-footer > * {\n width: 100%;\n}\n\n.j-navbar-menu-enter-active {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-navbar-menu-leave-active {\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\n}\n\n.j-navbar-menu-enter-from,\n.j-navbar-menu-leave-to {\n opacity: 0;\n transform: translateY(calc(var(--juxt-motion-shift) * -1));\n}\n\n/* Collapse points. Above them the menu never shows, below them the links hide. */\n.j-navbar--collapse-never .j-navbar__menu {\n display: none !important;\n}\n\n@media (max-width: 639.98px) {\n .j-navbar--collapse-sm .j-navbar__links { display: none; }\n .j-navbar--collapse-sm .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 640px) {\n .j-navbar--collapse-sm .j-navbar__menu { display: none !important; }\n}\n\n@media (max-width: 767.98px) {\n .j-navbar--collapse-md .j-navbar__links { display: none; }\n .j-navbar--collapse-md .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 768px) {\n .j-navbar--collapse-md .j-navbar__menu { display: none !important; }\n}\n\n@media (max-width: 1023.98px) {\n .j-navbar--collapse-lg .j-navbar__links { display: none; }\n .j-navbar--collapse-lg .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 1024px) {\n .j-navbar--collapse-lg .j-navbar__menu { display: none !important; }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'\nimport { useLayer } from '../../composables/useLayer'\nimport { focusElement } from '../../utils/dom'\nimport JBurger from '../burger/JBurger.vue'\n\nexport interface NavbarProps {\n /** Stick to the top of the viewport while scrolling. */\n sticky?: boolean\n /** Hairline under the bar. */\n bordered?: boolean\n /** Accessible name of the navigation landmark. */\n label?: string\n /** Width below which links collapse behind the burger. `never` keeps them inline. */\n collapse?: 'sm' | 'md' | 'lg' | 'never'\n /** Maximum content width; any CSS length. `none` spans the full width. */\n maxWidth?: string\n}\n\nconst props = withDefaults(defineProps<NavbarProps>(), {\n sticky: true,\n bordered: true,\n label: 'Main',\n collapse: 'md',\n maxWidth: '72rem',\n})\n\n/** Mobile menu state. */\nconst open = defineModel<boolean>('open', { default: false })\n\ndefineSlots<{\n /** Logo or product name, usually a link home. */\n brand?: () => unknown\n /** Navigation links, typically `JNavbarLink`s. Shown inline, or in the mobile menu. */\n default?: () => unknown\n /** Always visible on the right: search, theme switch, avatar. */\n actions?: () => unknown\n /** Extra content at the bottom of the mobile menu, e.g. sign-in buttons. */\n 'menu-footer'?: () => unknown\n}>()\n\nconst menuId = `j-${useId()}-menu`\nconst root = ref<HTMLElement | null>(null)\nconst menu = ref<HTMLElement | null>(null)\n\nconst breakpoints = { sm: 640, md: 768, lg: 1024 }\n\nfunction close(returnFocus = false) {\n if (!open.value) return\n open.value = false\n if (returnFocus) nextTick(() => focusElement(root.value?.querySelector<HTMLElement>('.j-burger')))\n}\n\nuseLayer({\n active: open,\n elements: () => [root.value],\n onEscape: () => close(true),\n onPointerDownOutside: () => close(),\n})\n\n// Choosing a destination closes the menu; opening a nested disclosure doesn't.\nfunction onMenuClick(event: MouseEvent) {\n const target = (event.target as HTMLElement).closest('a, button')\n if (target && !target.hasAttribute('aria-expanded')) close()\n}\n\n// Growing past the breakpoint makes the menu irrelevant, so drop it.\nlet media: MediaQueryList | undefined\nconst onMediaChange = (event: MediaQueryListEvent) => event.matches && close()\nonMounted(() => {\n if (props.collapse === 'never') return\n media = window.matchMedia(`(min-width: ${breakpoints[props.collapse]}px)`)\n media.addEventListener('change', onMediaChange)\n})\nonBeforeUnmount(() => media?.removeEventListener('change', onMediaChange))\n\nwatch(open, async (value) => {\n if (!value) return\n await nextTick()\n const first = menu.value?.querySelector<HTMLElement>('a, button')\n first?.focus({ preventScroll: true })\n})\n\nconst style = computed(() => ({ '--j-navbar-max': props.maxWidth === 'none' ? 'none' : props.maxWidth }))\n</script>\n\n<template>\n <header\n ref=\"root\"\n class=\"j-navbar\"\n :class=\"[`j-navbar--collapse-${collapse}`, { 'is-sticky': sticky, 'is-bordered': bordered, 'is-open': open }]\"\n :style=\"style\"\n >\n <div class=\"j-navbar__bar\">\n <div v-if=\"$slots.brand\" class=\"j-navbar__brand\">\n <slot name=\"brand\" />\n </div>\n <nav v-if=\"$slots.default\" class=\"j-navbar__links\" :aria-label=\"label\">\n <slot />\n </nav>\n <div class=\"j-navbar__end\">\n <div v-if=\"$slots.actions\" class=\"j-navbar__actions\">\n <slot name=\"actions\" />\n </div>\n <JBurger\n v-if=\"collapse !== 'never' && ($slots.default || $slots['menu-footer'])\"\n v-model=\"open\"\n class=\"j-navbar__burger\"\n :controls=\"menuId\"\n />\n </div>\n </div>\n <Transition name=\"j-navbar-menu\">\n <div v-show=\"open\" :id=\"menuId\" ref=\"menu\" class=\"j-navbar__menu\" @click=\"onMenuClick\">\n <nav v-if=\"$slots.default\" class=\"j-navbar__menu-links\" :aria-label=\"label\">\n <slot />\n </nav>\n <div v-if=\"$slots['menu-footer']\" class=\"j-navbar__menu-footer\">\n <slot name=\"menu-footer\" />\n </div>\n </div>\n </Transition>\n </header>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-navbar {\n --j-navbar-height: 3.5rem;\n\n position: relative;\n z-index: var(--juxt-z-sticky);\n background: var(--juxt-bg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-navbar.is-sticky {\n position: sticky;\n top: 0;\n}\n\n.j-navbar.is-bordered {\n box-shadow: inset 0 -1px 0 var(--juxt-border-subtle);\n}\n\n.dark .j-navbar.is-bordered,\n[data-theme='dark'] .j-navbar.is-bordered {\n box-shadow: inset 0 -1px 0 var(--juxt-border);\n}\n\n.j-navbar__bar {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-6);\n max-width: var(--j-navbar-max);\n height: var(--j-navbar-height);\n margin: 0 auto;\n padding: 0 var(--juxt-space-4);\n}\n\n.j-navbar__brand {\n display: flex;\n flex: none;\n align-items: center;\n}\n\n.j-navbar__links {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1);\n min-width: 0;\n}\n\n.j-navbar__end {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n margin-left: auto;\n}\n\n.j-navbar__actions {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n}\n\n.j-navbar__burger {\n display: none;\n margin-right: calc(var(--juxt-space-2) * -1);\n}\n\n/* Mobile menu: a panel that drops from the bar, links stacked with big targets. */\n.j-navbar__menu {\n position: absolute;\n top: 100%;\n right: 0;\n left: 0;\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-4);\n max-height: calc(100dvh - var(--j-navbar-height));\n padding: var(--juxt-space-3) var(--juxt-space-4) var(--juxt-space-5);\n overflow-y: auto;\n border-bottom: 1px solid var(--juxt-border);\n background: var(--juxt-bg);\n box-shadow: var(--juxt-shadow-md);\n}\n\n.j-navbar__menu-links {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n.j-navbar__menu-links .j-navbar-link {\n justify-content: flex-start;\n height: 2.75rem;\n padding: 0 var(--juxt-space-3);\n font-size: var(--juxt-text-lg);\n}\n\n.j-navbar__menu-footer {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-2);\n padding-top: var(--juxt-space-4);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.j-navbar__menu-footer > * {\n width: 100%;\n}\n\n.j-navbar-menu-enter-active {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-navbar-menu-leave-active {\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-in),\n transform var(--juxt-duration-instant) var(--juxt-ease-in);\n}\n\n.j-navbar-menu-enter-from,\n.j-navbar-menu-leave-to {\n opacity: 0;\n transform: translateY(calc(var(--juxt-motion-shift) * -1));\n}\n\n/* Collapse points. Above them the menu never shows, below them the links hide. */\n.j-navbar--collapse-never .j-navbar__menu {\n display: none !important;\n}\n\n@media (max-width: 639.98px) {\n .j-navbar--collapse-sm .j-navbar__links { display: none; }\n .j-navbar--collapse-sm .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 640px) {\n .j-navbar--collapse-sm .j-navbar__menu { display: none !important; }\n}\n\n@media (max-width: 767.98px) {\n .j-navbar--collapse-md .j-navbar__links { display: none; }\n .j-navbar--collapse-md .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 768px) {\n .j-navbar--collapse-md .j-navbar__menu { display: none !important; }\n}\n\n@media (max-width: 1023.98px) {\n .j-navbar--collapse-lg .j-navbar__links { display: none; }\n .j-navbar--collapse-lg .j-navbar__burger { display: inline-grid; }\n}\n\n@media (min-width: 1024px) {\n .j-navbar--collapse-lg .j-navbar__menu { display: none !important; }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface NavbarLinkProps {\n /** Marks the current page: styled and announced with `aria-current=\"page\"`. */\n active?: boolean\n /** Render as another element or component, e.g. `NuxtLink` or `RouterLink`. */\n as?: string | Component\n}\n\nwithDefaults(defineProps<NavbarLinkProps>(), { as: 'a' })\n\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\n</script>\n\n<template>\n <component\n :is=\"as\"\n class=\"j-navbar-link j-focusable\"\n :class=\"{ 'is-active': active }\"\n :aria-current=\"active ? 'page' : undefined\"\n >\n <span v-if=\"$slots.leading\" class=\"j-navbar-link__icon\"><slot name=\"leading\" /></span>\n <slot />\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-navbar-link {\n display: inline-flex;\n align-items: center;\n gap: var(--juxt-space-1-5);\n height: var(--juxt-control-sm);\n padding: 0 var(--juxt-space-2-5);\n border-radius: var(--juxt-radius-sm);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n text-decoration: none;\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: color, background-color, outline-color, outline-offset;\n}\n\n.j-navbar-link:hover {\n color: var(--juxt-fg);\n}\n\n.j-navbar-link.is-active {\n color: var(--juxt-fg);\n}\n\n/* In the mobile menu the current page gets a quiet fill, like a selected row. */\n.j-navbar__menu .j-navbar-link.is-active {\n background: var(--juxt-surface-hover);\n}\n\n.j-navbar-link__icon {\n display: inline-flex;\n color: var(--juxt-fg-muted);\n}\n\n.j-navbar-link__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface NavbarLinkProps {\n /** Marks the current page: styled and announced with `aria-current=\"page\"`. */\n active?: boolean\n /** Render as another element or component, e.g. `NuxtLink` or `RouterLink`. */\n as?: string | Component\n}\n\nwithDefaults(defineProps<NavbarLinkProps>(), { as: 'a' })\n\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\n</script>\n\n<template>\n <component\n :is=\"as\"\n class=\"j-navbar-link j-focusable\"\n :class=\"{ 'is-active': active }\"\n :aria-current=\"active ? 'page' : undefined\"\n >\n <span v-if=\"$slots.leading\" class=\"j-navbar-link__icon\"><slot name=\"leading\" /></span>\n <slot />\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-navbar-link {\n display: inline-flex;\n align-items: center;\n gap: var(--juxt-space-1-5);\n height: var(--juxt-control-sm);\n padding: 0 var(--juxt-space-2-5);\n border-radius: var(--juxt-radius-sm);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n text-decoration: none;\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: color, background-color, outline-color, outline-offset;\n}\n\n.j-navbar-link:hover {\n color: var(--juxt-fg);\n}\n\n.j-navbar-link.is-active {\n color: var(--juxt-fg);\n}\n\n/* In the mobile menu the current page gets a quiet fill, like a selected row. */\n.j-navbar__menu .j-navbar-link.is-active {\n background: var(--juxt-surface-hover);\n}\n\n.j-navbar-link__icon {\n display: inline-flex;\n color: var(--juxt-fg-muted);\n}\n\n.j-navbar-link__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref } from 'vue'\nimport { IconChevronLeft, IconChevronRight, IconMoreHorizontal } from '../../icons'\nimport { focusElement } from '../../utils/dom'\n\nexport interface PaginationProps {\n /** Total number of pages. */\n total: number\n /** Pages shown on each side of the current page before collapsing to an ellipsis. */\n siblingCount?: number\n /** Accessible name for the nav landmark. */\n label?: string\n size?: 'sm' | 'md'\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<PaginationProps>(), {\n siblingCount: 1,\n size: 'md',\n})\n\nconst model = defineModel<number>({ default: 1 })\n\nconst list = ref<HTMLElement | null>(null)\n\nconst totalPages = computed(() => Math.max(1, Math.floor(props.total)))\nconst current = computed(() => Math.min(Math.max(1, model.value), totalPages.value))\n\ntype PageItem = number | 'ellipsis'\n\nconst pageItems = computed<PageItem[]>(() => {\n const total = totalPages.value\n const siblings = Math.max(0, props.siblingCount)\n const totalSlots = siblings * 2 + 5\n if (total <= totalSlots) return Array.from({ length: total }, (_, i) => i + 1)\n\n const left = Math.max(current.value - siblings, 1)\n const right = Math.min(current.value + siblings, total)\n const showLeftEllipsis = left > 2\n const showRightEllipsis = right < total - 1\n\n const items: PageItem[] = [1]\n if (showLeftEllipsis) items.push('ellipsis')\n else for (let p = 2; p < left; p++) items.push(p)\n for (let p = Math.max(left, 2); p <= Math.min(right, total - 1); p++) items.push(p)\n if (showRightEllipsis) items.push('ellipsis')\n else for (let p = right + 1; p < total; p++) items.push(p)\n if (total > 1) items.push(total)\n return items\n})\n\nfunction goTo(page: number, focus = false) {\n if (props.disabled) return\n model.value = Math.min(Math.max(1, page), totalPages.value)\n if (focus) nextTick(() => focusElement(list.value?.querySelector<HTMLElement>('[aria-current=\"page\"]')))\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n const target = event.target as HTMLElement\n if (!target.classList.contains('j-pagination__page')) return\n if (event.key === 'Home') {\n event.preventDefault()\n goTo(1, true)\n }\n else if (event.key === 'End') {\n event.preventDefault()\n goTo(totalPages.value, true)\n }\n}\n</script>\n\n<template>\n <nav class=\"j-pagination\" :class=\"`j-pagination--${size}`\" :aria-label=\"label || 'Pagination'\">\n <button\n type=\"button\"\n class=\"j-pagination__nav j-focusable\"\n aria-label=\"Previous page\"\n :disabled=\"disabled || current === 1\"\n @click=\"goTo(current - 1, true)\"\n >\n <IconChevronLeft />\n </button>\n <ul ref=\"list\" class=\"j-pagination__list\" @keydown=\"onKeydown\">\n <li v-for=\"(item, index) in pageItems\" :key=\"index\">\n <span v-if=\"item === 'ellipsis'\" class=\"j-pagination__ellipsis\" aria-hidden=\"true\">\n <IconMoreHorizontal />\n </span>\n <button\n v-else\n type=\"button\"\n class=\"j-pagination__page j-focusable\"\n :class=\"{ 'is-current': item === current }\"\n :aria-current=\"item === current ? 'page' : undefined\"\n :aria-label=\"`Page ${item}`\"\n :disabled=\"disabled\"\n @click=\"goTo(item)\"\n >\n {{ item }}\n </button>\n </li>\n </ul>\n <button\n type=\"button\"\n class=\"j-pagination__nav j-focusable\"\n aria-label=\"Next page\"\n :disabled=\"disabled || current === totalPages\"\n @click=\"goTo(current + 1, true)\"\n >\n <IconChevronRight />\n </button>\n </nav>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-pagination {\n --j-pagination-size: var(--juxt-control-md);\n\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1);\n font-family: var(--juxt-font-sans);\n}\n\n.j-pagination--sm {\n --j-pagination-size: var(--juxt-control-sm);\n}\n\n.j-pagination__list {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.j-pagination__nav,\n.j-pagination__page {\n display: grid;\n flex: none;\n place-items: center;\n width: var(--j-pagination-size);\n height: var(--j-pagination-size);\n border: 1px solid transparent;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg-secondary);\n font-family: inherit;\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n font-variant-numeric: tabular-nums;\n cursor: pointer;\n transition:\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n color var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-pagination__nav > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-pagination__nav:hover:not(:disabled),\n.j-pagination__page:hover:not(:disabled) {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-pagination__nav:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-pagination__page.is-current {\n border-color: var(--juxt-border);\n background: var(--juxt-surface-active);\n color: var(--juxt-fg);\n}\n\n.j-pagination__page:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-pagination__ellipsis {\n display: grid;\n place-items: center;\n width: var(--j-pagination-size);\n height: var(--j-pagination-size);\n color: var(--juxt-fg-disabled);\n}\n\n.j-pagination__ellipsis > svg {\n width: 1rem;\n height: 1rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref } from 'vue'\nimport { IconChevronLeft, IconChevronRight, IconMoreHorizontal } from '../../icons'\nimport { focusElement } from '../../utils/dom'\n\nexport interface PaginationProps {\n /** Total number of pages. */\n total: number\n /** Pages shown on each side of the current page before collapsing to an ellipsis. */\n siblingCount?: number\n /** Accessible name for the nav landmark. */\n label?: string\n size?: 'sm' | 'md'\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<PaginationProps>(), {\n siblingCount: 1,\n size: 'md',\n})\n\nconst model = defineModel<number>({ default: 1 })\n\nconst list = ref<HTMLElement | null>(null)\n\nconst totalPages = computed(() => Math.max(1, Math.floor(props.total)))\nconst current = computed(() => Math.min(Math.max(1, model.value), totalPages.value))\n\ntype PageItem = number | 'ellipsis'\n\nconst pageItems = computed<PageItem[]>(() => {\n const total = totalPages.value\n const siblings = Math.max(0, props.siblingCount)\n const totalSlots = siblings * 2 + 5\n if (total <= totalSlots) return Array.from({ length: total }, (_, i) => i + 1)\n\n const left = Math.max(current.value - siblings, 1)\n const right = Math.min(current.value + siblings, total)\n const showLeftEllipsis = left > 2\n const showRightEllipsis = right < total - 1\n\n const items: PageItem[] = [1]\n if (showLeftEllipsis) items.push('ellipsis')\n else for (let p = 2; p < left; p++) items.push(p)\n for (let p = Math.max(left, 2); p <= Math.min(right, total - 1); p++) items.push(p)\n if (showRightEllipsis) items.push('ellipsis')\n else for (let p = right + 1; p < total; p++) items.push(p)\n if (total > 1) items.push(total)\n return items\n})\n\nfunction goTo(page: number, focus = false) {\n if (props.disabled) return\n model.value = Math.min(Math.max(1, page), totalPages.value)\n if (focus) nextTick(() => focusElement(list.value?.querySelector<HTMLElement>('[aria-current=\"page\"]')))\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n const target = event.target as HTMLElement\n if (!target.classList.contains('j-pagination__page')) return\n if (event.key === 'Home') {\n event.preventDefault()\n goTo(1, true)\n }\n else if (event.key === 'End') {\n event.preventDefault()\n goTo(totalPages.value, true)\n }\n}\n</script>\n\n<template>\n <nav class=\"j-pagination\" :class=\"`j-pagination--${size}`\" :aria-label=\"label || 'Pagination'\">\n <button\n type=\"button\"\n class=\"j-pagination__nav j-focusable\"\n aria-label=\"Previous page\"\n :disabled=\"disabled || current === 1\"\n @click=\"goTo(current - 1, true)\"\n >\n <IconChevronLeft />\n </button>\n <ul ref=\"list\" class=\"j-pagination__list\" @keydown=\"onKeydown\">\n <li v-for=\"(item, index) in pageItems\" :key=\"index\">\n <span v-if=\"item === 'ellipsis'\" class=\"j-pagination__ellipsis\" aria-hidden=\"true\">\n <IconMoreHorizontal />\n </span>\n <button\n v-else\n type=\"button\"\n class=\"j-pagination__page j-focusable\"\n :class=\"{ 'is-current': item === current }\"\n :aria-current=\"item === current ? 'page' : undefined\"\n :aria-label=\"`Page ${item}`\"\n :disabled=\"disabled\"\n @click=\"goTo(item)\"\n >\n {{ item }}\n </button>\n </li>\n </ul>\n <button\n type=\"button\"\n class=\"j-pagination__nav j-focusable\"\n aria-label=\"Next page\"\n :disabled=\"disabled || current === totalPages\"\n @click=\"goTo(current + 1, true)\"\n >\n <IconChevronRight />\n </button>\n </nav>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-pagination {\n --j-pagination-size: var(--juxt-control-md);\n\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1);\n font-family: var(--juxt-font-sans);\n}\n\n.j-pagination--sm {\n --j-pagination-size: var(--juxt-control-sm);\n}\n\n.j-pagination__list {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.j-pagination__nav,\n.j-pagination__page {\n display: grid;\n flex: none;\n place-items: center;\n width: var(--j-pagination-size);\n height: var(--j-pagination-size);\n border: 1px solid transparent;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg-secondary);\n font-family: inherit;\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n font-variant-numeric: tabular-nums;\n cursor: pointer;\n transition:\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n color var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-pagination__nav > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-pagination__nav:hover:not(:disabled),\n.j-pagination__page:hover:not(:disabled) {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-pagination__nav:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-pagination__page.is-current {\n border-color: var(--juxt-border);\n background: var(--juxt-surface-active);\n color: var(--juxt-fg);\n}\n\n.j-pagination__page:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-pagination__ellipsis {\n display: grid;\n place-items: center;\n width: var(--j-pagination-size);\n height: var(--j-pagination-size);\n color: var(--juxt-fg-disabled);\n}\n\n.j-pagination__ellipsis > svg {\n width: 1rem;\n height: 1rem;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Placement } from '../../composables/useFloating'\nimport { computed, nextTick, ref, useId, watch } from 'vue'\nimport { useFloating } from '../../composables/useFloating'\nimport { useLayer } from '../../composables/useLayer'\nimport { focusElement, getFocusableElements, unrefElement } from '../../utils/dom'\nimport JPortal from '../primitives/JPortal.vue'\nimport { JSlot } from '../primitives/JSlot'\n\nexport interface PopoverProps {\n placement?: Placement\n /** Distance from the trigger in px. */\n offset?: number\n /** Accessible name of the popover content. */\n ariaLabel?: string\n /** Close when focus or a click leaves the popover. Defaults to true. */\n closeOnOutside?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<PopoverProps>(), {\n placement: 'bottom',\n offset: 8,\n closeOnOutside: true,\n})\n\nconst open = defineModel<boolean>('open', { default: false })\n\ndefineSlots<{\n trigger?: (props: { open: boolean }) => unknown\n default?: (props: { close: () => void }) => unknown\n}>()\n\nconst id = `j-${useId()}-popover`\nconst triggerRef = ref()\nconst triggerEl = computed(() => unrefElement(triggerRef.value))\nconst content = ref<HTMLElement | null>(null)\n\nconst { styles } = useFloating(triggerEl, content, () => ({ placement: props.placement, offset: props.offset }))\n\nfunction close(restoreFocus = false) {\n if (!open.value) return\n open.value = false\n if (restoreFocus) nextTick(() => focusElement(triggerEl.value))\n}\n\nuseLayer({\n active: open,\n elements: () => [triggerEl.value, content.value],\n onEscape: () => close(true),\n onPointerDownOutside: () => props.closeOnOutside && close(false),\n})\n\n// Moving focus in makes the content reachable without a trap: this is a\n// non-modal surface, so Tab is allowed to leave (and that closes it).\nwatch(open, async (value) => {\n if (!value) return\n await nextTick()\n const el = content.value\n if (!el) return\n focusElement(el.querySelector<HTMLElement>('[autofocus], [data-autofocus]') ?? getFocusableElements(el)[0] ?? el)\n}, { flush: 'post' })\n\nfunction onFocusOut(event: FocusEvent) {\n if (!props.closeOnOutside) return\n const next = event.relatedTarget as Node | null\n if (!next) return\n if (content.value?.contains(next) || triggerEl.value?.contains(next)) return\n close(false)\n}\n\nfunction toggle() {\n if (props.disabled) return\n open.value = !open.value\n}\n\nconst triggerProps = computed(() => ({\n 'aria-haspopup': 'dialog',\n 'aria-expanded': open.value,\n 'aria-controls': open.value ? id : undefined,\n 'data-state': open.value ? 'open' : 'closed',\n 'onClick': toggle,\n}))\n\ndefineExpose({ close: () => close(true) })\n</script>\n\n<template>\n <JSlot ref=\"triggerRef\" v-bind=\"triggerProps\">\n <slot name=\"trigger\" :open=\"open\" />\n </JSlot>\n <JPortal>\n <Transition name=\"j-pop\">\n <div\n v-if=\"open\"\n :id=\"id\"\n ref=\"content\"\n role=\"dialog\"\n tabindex=\"-1\"\n class=\"j-popover j-popover-surface\"\n :style=\"styles\"\n :aria-label=\"ariaLabel\"\n @focusout=\"onFocusOut\"\n >\n <slot :close=\"() => close(true)\" />\n </div>\n </Transition>\n </JPortal>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-popover {\n z-index: var(--juxt-z-popover);\n box-sizing: border-box;\n max-width: calc(100vw - 16px);\n max-height: var(--j-available-height, 32rem);\n padding: var(--juxt-space-4);\n overflow: auto;\n outline: none;\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Placement } from '../../composables/useFloating'\nimport { computed, nextTick, ref, useId, watch } from 'vue'\nimport { useFloating } from '../../composables/useFloating'\nimport { useLayer } from '../../composables/useLayer'\nimport { focusElement, getFocusableElements, unrefElement } from '../../utils/dom'\nimport JPortal from '../primitives/JPortal.vue'\nimport { JSlot } from '../primitives/JSlot'\n\nexport interface PopoverProps {\n placement?: Placement\n /** Distance from the trigger in px. */\n offset?: number\n /** Accessible name of the popover content. */\n ariaLabel?: string\n /** Close when focus or a click leaves the popover. Defaults to true. */\n closeOnOutside?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<PopoverProps>(), {\n placement: 'bottom',\n offset: 8,\n closeOnOutside: true,\n})\n\nconst open = defineModel<boolean>('open', { default: false })\n\ndefineSlots<{\n trigger?: (props: { open: boolean }) => unknown\n default?: (props: { close: () => void }) => unknown\n}>()\n\nconst id = `j-${useId()}-popover`\nconst triggerRef = ref()\nconst triggerEl = computed(() => unrefElement(triggerRef.value))\nconst content = ref<HTMLElement | null>(null)\n\nconst { styles } = useFloating(triggerEl, content, () => ({ placement: props.placement, offset: props.offset }))\n\nfunction close(restoreFocus = false) {\n if (!open.value) return\n open.value = false\n if (restoreFocus) nextTick(() => focusElement(triggerEl.value))\n}\n\nuseLayer({\n active: open,\n elements: () => [triggerEl.value, content.value],\n onEscape: () => close(true),\n onPointerDownOutside: () => props.closeOnOutside && close(false),\n})\n\n// Moving focus in makes the content reachable without a trap: this is a\n// non-modal surface, so Tab is allowed to leave (and that closes it).\nwatch(open, async (value) => {\n if (!value) return\n await nextTick()\n const el = content.value\n if (!el) return\n focusElement(el.querySelector<HTMLElement>('[autofocus], [data-autofocus]') ?? getFocusableElements(el)[0] ?? el)\n}, { flush: 'post' })\n\nfunction onFocusOut(event: FocusEvent) {\n if (!props.closeOnOutside) return\n const next = event.relatedTarget as Node | null\n if (!next) return\n if (content.value?.contains(next) || triggerEl.value?.contains(next)) return\n close(false)\n}\n\nfunction toggle() {\n if (props.disabled) return\n open.value = !open.value\n}\n\nconst triggerProps = computed(() => ({\n 'aria-haspopup': 'dialog',\n 'aria-expanded': open.value,\n 'aria-controls': open.value ? id : undefined,\n 'data-state': open.value ? 'open' : 'closed',\n 'onClick': toggle,\n}))\n\ndefineExpose({ close: () => close(true) })\n</script>\n\n<template>\n <JSlot ref=\"triggerRef\" v-bind=\"triggerProps\">\n <slot name=\"trigger\" :open=\"open\" />\n </JSlot>\n <JPortal>\n <Transition name=\"j-pop\">\n <div\n v-if=\"open\"\n :id=\"id\"\n ref=\"content\"\n role=\"dialog\"\n tabindex=\"-1\"\n class=\"j-popover j-popover-surface\"\n :style=\"styles\"\n :aria-label=\"ariaLabel\"\n @focusout=\"onFocusOut\"\n >\n <slot :close=\"() => close(true)\" />\n </div>\n </Transition>\n </JPortal>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-popover {\n z-index: var(--juxt-z-popover);\n box-sizing: border-box;\n max-width: calc(100vw - 16px);\n max-height: var(--j-available-height, 32rem);\n padding: var(--juxt-space-4);\n overflow: auto;\n outline: none;\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useId } from 'vue'\n\nexport interface ProgressProps {\n /** Current value. `null` shows an indeterminate bar. */\n value?: number | null\n max?: number\n size?: 'sm' | 'md'\n /** Visible label above the bar; also its accessible name. */\n label?: string\n /** Show the percentage next to the label. */\n showValue?: boolean\n /** `accent` for progress toward a goal, `neutral` for plain measurement. */\n variant?: 'accent' | 'neutral'\n}\n\nconst props = withDefaults(defineProps<ProgressProps>(), {\n value: null,\n max: 100,\n size: 'md',\n variant: 'accent',\n})\n\nconst id = `j-${useId()}`\nconst indeterminate = computed(() => props.value === null || props.value === undefined)\nconst ratio = computed(() => (indeterminate.value ? 0 : Math.min(1, Math.max(0, Number(props.value) / props.max))))\nconst percent = computed(() => Math.round(ratio.value * 100))\n</script>\n\n<template>\n <div class=\"j-progress\" :class=\"[`j-progress--${size}`, `j-progress--${variant}`, { 'is-indeterminate': indeterminate }]\">\n <div v-if=\"label || showValue\" class=\"j-progress__header\">\n <span v-if=\"label\" :id=\"`${id}-label`\" class=\"j-progress__label\">{{ label }}</span>\n <span v-if=\"showValue && !indeterminate\" class=\"j-progress__value\">{{ percent }}%</span>\n </div>\n <div\n class=\"j-progress__track\"\n role=\"progressbar\"\n :aria-labelledby=\"label ? `${id}-label` : undefined\"\n aria-valuemin=\"0\"\n :aria-valuemax=\"max\"\n :aria-valuenow=\"indeterminate ? undefined : value!\"\n :aria-valuetext=\"indeterminate ? 'Loading' : `${percent}%`\"\n >\n <span class=\"j-progress__fill\" :style=\"indeterminate ? undefined : { transform: `scaleX(${ratio})` }\" />\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-progress {\n --j-progress-height: 0.375rem;\n --j-progress-fill: var(--juxt-accent);\n\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-2);\n width: 100%;\n font-family: var(--juxt-font-sans);\n}\n\n.j-progress--sm {\n --j-progress-height: 0.25rem;\n}\n\n.j-progress--neutral {\n --j-progress-fill: var(--juxt-fg);\n}\n\n.j-progress__header {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: var(--juxt-space-3);\n font-size: var(--juxt-text-sm);\n}\n\n.j-progress__label {\n color: var(--juxt-fg);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-progress__value {\n color: var(--juxt-fg-muted);\n font-variant-numeric: tabular-nums;\n}\n\n.j-progress__track {\n position: relative;\n height: var(--j-progress-height);\n overflow: hidden;\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-surface-active);\n}\n\n/* Width changes are transforms, so updates glide without reflow. */\n.j-progress__fill {\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: var(--j-progress-fill);\n transform: scaleX(0);\n transform-origin: left;\n transition: transform var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n.j-progress.is-indeterminate .j-progress__fill {\n width: 40%;\n transform: none;\n animation: j-progress-indeterminate 1.4s var(--juxt-ease-standard) infinite;\n}\n\n@keyframes j-progress-indeterminate {\n from {\n transform: translateX(-100%);\n }\n\n to {\n transform: translateX(250%);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-progress.is-indeterminate .j-progress__fill {\n width: 100%;\n animation: j-progress-pulse 1.6s ease-in-out infinite;\n }\n\n @keyframes j-progress-pulse {\n 50% {\n opacity: 0.35;\n }\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useId } from 'vue'\n\nexport interface ProgressProps {\n /** Current value. `null` shows an indeterminate bar. */\n value?: number | null\n max?: number\n size?: 'sm' | 'md'\n /** Visible label above the bar; also its accessible name. */\n label?: string\n /** Show the percentage next to the label. */\n showValue?: boolean\n /** `accent` for progress toward a goal, `neutral` for plain measurement. */\n variant?: 'accent' | 'neutral'\n}\n\nconst props = withDefaults(defineProps<ProgressProps>(), {\n value: null,\n max: 100,\n size: 'md',\n variant: 'accent',\n})\n\nconst id = `j-${useId()}`\nconst indeterminate = computed(() => props.value === null || props.value === undefined)\nconst ratio = computed(() => (indeterminate.value ? 0 : Math.min(1, Math.max(0, Number(props.value) / props.max))))\nconst percent = computed(() => Math.round(ratio.value * 100))\n</script>\n\n<template>\n <div class=\"j-progress\" :class=\"[`j-progress--${size}`, `j-progress--${variant}`, { 'is-indeterminate': indeterminate }]\">\n <div v-if=\"label || showValue\" class=\"j-progress__header\">\n <span v-if=\"label\" :id=\"`${id}-label`\" class=\"j-progress__label\">{{ label }}</span>\n <span v-if=\"showValue && !indeterminate\" class=\"j-progress__value\">{{ percent }}%</span>\n </div>\n <div\n class=\"j-progress__track\"\n role=\"progressbar\"\n :aria-labelledby=\"label ? `${id}-label` : undefined\"\n aria-valuemin=\"0\"\n :aria-valuemax=\"max\"\n :aria-valuenow=\"indeterminate ? undefined : value!\"\n :aria-valuetext=\"indeterminate ? 'Loading' : `${percent}%`\"\n >\n <span class=\"j-progress__fill\" :style=\"indeterminate ? undefined : { transform: `scaleX(${ratio})` }\" />\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-progress {\n --j-progress-height: 0.375rem;\n --j-progress-fill: var(--juxt-accent);\n\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-2);\n width: 100%;\n font-family: var(--juxt-font-sans);\n}\n\n.j-progress--sm {\n --j-progress-height: 0.25rem;\n}\n\n.j-progress--neutral {\n --j-progress-fill: var(--juxt-fg);\n}\n\n.j-progress__header {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: var(--juxt-space-3);\n font-size: var(--juxt-text-sm);\n}\n\n.j-progress__label {\n color: var(--juxt-fg);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-progress__value {\n color: var(--juxt-fg-muted);\n font-variant-numeric: tabular-nums;\n}\n\n.j-progress__track {\n position: relative;\n height: var(--j-progress-height);\n overflow: hidden;\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-surface-active);\n}\n\n/* Width changes are transforms, so updates glide without reflow. */\n.j-progress__fill {\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: var(--j-progress-fill);\n transform: scaleX(0);\n transform-origin: left;\n transition: transform var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n.j-progress.is-indeterminate .j-progress__fill {\n width: 40%;\n transform: none;\n animation: j-progress-indeterminate 1.4s var(--juxt-ease-standard) infinite;\n}\n\n@keyframes j-progress-indeterminate {\n from {\n transform: translateX(-100%);\n }\n\n to {\n transform: translateX(250%);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-progress.is-indeterminate .j-progress__fill {\n width: 100%;\n animation: j-progress-pulse 1.6s ease-in-out infinite;\n }\n\n @keyframes j-progress-pulse {\n 50% {\n opacity: 0.35;\n }\n }\n}\n}\n</style>\n","import type { ComputedRef, InjectionKey } from 'vue'\n\nexport type RadioValue = string | number\n\nexport interface RadioGroupContext {\n name: ComputedRef<string>\n value: ComputedRef<RadioValue | null | undefined>\n disabled: ComputedRef<boolean>\n required: ComputedRef<boolean>\n invalid: ComputedRef<boolean>\n select: (value: RadioValue) => void\n}\n\nexport const RADIO_GROUP: InjectionKey<RadioGroupContext> = Symbol('JRadioGroup')\n","<script setup lang=\"ts\">\nimport type { RadioValue } from './context'\nimport { computed, inject, useId } from 'vue'\nimport { RADIO_GROUP } from './context'\n\nexport interface RadioProps {\n value: RadioValue\n label?: string\n description?: string\n disabled?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<RadioProps>()\n\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\n\nconst group = inject(RADIO_GROUP, null)\n\nconst id = `j-${useId()}`\nconst descriptionId = `${id}-description`\nconst checked = computed(() => group?.value.value === props.value)\nconst isDisabled = computed(() => props.disabled || !!group?.disabled.value)\n</script>\n\n<template>\n <div\n class=\"j-radio\"\n :class=\"[$attrs.class, { 'is-disabled': isDisabled }]\"\n :style=\"$attrs.style as any\"\n :data-state=\"checked ? 'checked' : 'unchecked'\"\n >\n <span class=\"j-radio__control\">\n <input\n :id=\"id\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n type=\"radio\"\n class=\"j-radio__input\"\n :name=\"group?.name.value\"\n :value=\"value\"\n :checked=\"checked\"\n :disabled=\"isDisabled\"\n :required=\"group?.required.value\"\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\n @change=\"group?.select(value)\"\n />\n <span class=\"j-radio__circle\" aria-hidden=\"true\" />\n </span>\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-radio__text\">\n <label v-if=\"label || $slots.default\" :for=\"id\" class=\"j-radio__label\">\n <slot>{{ label }}</slot>\n </label>\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-radio__description\">\n <slot name=\"description\">{{ description }}</slot>\n </span>\n </span>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-radio {\n --j-radio-size: 1rem;\n\n display: inline-flex;\n align-items: flex-start;\n gap: var(--juxt-space-2-5);\n font-family: var(--juxt-font-sans);\n}\n\n.j-radio__control {\n position: relative;\n display: inline-flex;\n flex: none;\n margin-top: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-radio-size)) / 2);\n}\n\n.j-radio__input {\n position: absolute;\n inset: -4px;\n z-index: 1;\n width: calc(100% + 8px);\n height: calc(100% + 8px);\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n.j-radio__input:disabled {\n cursor: not-allowed;\n}\n\n.j-radio__circle {\n position: relative;\n display: block;\n width: var(--j-radio-size);\n height: var(--j-radio-size);\n border: 1px solid var(--juxt-border-strong);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-surface);\n box-shadow: var(--juxt-shadow-xs);\n outline: 2px solid transparent;\n outline-offset: 0;\n transition:\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n transform var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n/* The inner dot grows out of the centre once the fill lands. */\n.j-radio__circle::after {\n content: '';\n position: absolute;\n inset: 0;\n margin: auto;\n width: 0.375rem;\n height: 0.375rem;\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-accent-fg);\n opacity: 0;\n transform: scale(0.3);\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-radio__input:hover:not(:disabled) + .j-radio__circle {\n border-color: var(--juxt-fg-muted);\n}\n\n.j-radio__input:active:not(:disabled) + .j-radio__circle {\n transform: scale(0.9);\n}\n\n.j-radio__input:focus-visible + .j-radio__circle {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n\n.j-radio[data-state='checked'] .j-radio__circle {\n border-color: var(--juxt-accent);\n background: var(--juxt-accent);\n}\n\n.j-radio[data-state='checked'] .j-radio__input:hover:not(:disabled) + .j-radio__circle {\n border-color: var(--juxt-accent-hover);\n background: var(--juxt-accent-hover);\n}\n\n.j-radio[data-state='checked'] .j-radio__circle::after {\n opacity: 1;\n transform: none;\n transition-delay: 30ms;\n}\n\n.j-radio-group.is-invalid .j-radio:not([data-state='checked']) .j-radio__circle {\n border-color: var(--juxt-danger-border);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-radio__input:active:not(:disabled) + .j-radio__circle {\n transform: none;\n }\n}\n\n.j-radio__text {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n min-width: 0;\n}\n\n.j-radio__label {\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n user-select: none;\n}\n\n.j-radio__description {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-radio.is-disabled .j-radio__circle {\n opacity: 0.5;\n box-shadow: none;\n}\n\n.j-radio.is-disabled .j-radio__label {\n color: var(--juxt-fg-muted);\n cursor: not-allowed;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { RadioValue } from './context'\nimport { computed, inject, useId } from 'vue'\nimport { RADIO_GROUP } from './context'\n\nexport interface RadioProps {\n value: RadioValue\n label?: string\n description?: string\n disabled?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<RadioProps>()\n\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\n\nconst group = inject(RADIO_GROUP, null)\n\nconst id = `j-${useId()}`\nconst descriptionId = `${id}-description`\nconst checked = computed(() => group?.value.value === props.value)\nconst isDisabled = computed(() => props.disabled || !!group?.disabled.value)\n</script>\n\n<template>\n <div\n class=\"j-radio\"\n :class=\"[$attrs.class, { 'is-disabled': isDisabled }]\"\n :style=\"$attrs.style as any\"\n :data-state=\"checked ? 'checked' : 'unchecked'\"\n >\n <span class=\"j-radio__control\">\n <input\n :id=\"id\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n type=\"radio\"\n class=\"j-radio__input\"\n :name=\"group?.name.value\"\n :value=\"value\"\n :checked=\"checked\"\n :disabled=\"isDisabled\"\n :required=\"group?.required.value\"\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\n @change=\"group?.select(value)\"\n />\n <span class=\"j-radio__circle\" aria-hidden=\"true\" />\n </span>\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-radio__text\">\n <label v-if=\"label || $slots.default\" :for=\"id\" class=\"j-radio__label\">\n <slot>{{ label }}</slot>\n </label>\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-radio__description\">\n <slot name=\"description\">{{ description }}</slot>\n </span>\n </span>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-radio {\n --j-radio-size: 1rem;\n\n display: inline-flex;\n align-items: flex-start;\n gap: var(--juxt-space-2-5);\n font-family: var(--juxt-font-sans);\n}\n\n.j-radio__control {\n position: relative;\n display: inline-flex;\n flex: none;\n margin-top: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-radio-size)) / 2);\n}\n\n.j-radio__input {\n position: absolute;\n inset: -4px;\n z-index: 1;\n width: calc(100% + 8px);\n height: calc(100% + 8px);\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n.j-radio__input:disabled {\n cursor: not-allowed;\n}\n\n.j-radio__circle {\n position: relative;\n display: block;\n width: var(--j-radio-size);\n height: var(--j-radio-size);\n border: 1px solid var(--juxt-border-strong);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-surface);\n box-shadow: var(--juxt-shadow-xs);\n outline: 2px solid transparent;\n outline-offset: 0;\n transition:\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n border-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n transform var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n/* The inner dot grows out of the centre once the fill lands. */\n.j-radio__circle::after {\n content: '';\n position: absolute;\n inset: 0;\n margin: auto;\n width: 0.375rem;\n height: 0.375rem;\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-accent-fg);\n opacity: 0;\n transform: scale(0.3);\n transition:\n opacity var(--juxt-duration-instant) var(--juxt-ease-standard),\n transform var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-radio__input:hover:not(:disabled) + .j-radio__circle {\n border-color: var(--juxt-fg-muted);\n}\n\n.j-radio__input:active:not(:disabled) + .j-radio__circle {\n transform: scale(0.9);\n}\n\n.j-radio__input:focus-visible + .j-radio__circle {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n\n.j-radio[data-state='checked'] .j-radio__circle {\n border-color: var(--juxt-accent);\n background: var(--juxt-accent);\n}\n\n.j-radio[data-state='checked'] .j-radio__input:hover:not(:disabled) + .j-radio__circle {\n border-color: var(--juxt-accent-hover);\n background: var(--juxt-accent-hover);\n}\n\n.j-radio[data-state='checked'] .j-radio__circle::after {\n opacity: 1;\n transform: none;\n transition-delay: 30ms;\n}\n\n.j-radio-group.is-invalid .j-radio:not([data-state='checked']) .j-radio__circle {\n border-color: var(--juxt-danger-border);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-radio__input:active:not(:disabled) + .j-radio__circle {\n transform: none;\n }\n}\n\n.j-radio__text {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n min-width: 0;\n}\n\n.j-radio__label {\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n user-select: none;\n}\n\n.j-radio__description {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-radio.is-disabled .j-radio__circle {\n opacity: 0.5;\n box-shadow: none;\n}\n\n.j-radio.is-disabled .j-radio__label {\n color: var(--juxt-fg-muted);\n cursor: not-allowed;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { RadioValue } from './context'\nimport { computed, provide, useId } from 'vue'\nimport { RADIO_GROUP } from './context'\n\nexport interface RadioGroupProps {\n /** Group label, rendered as the fieldset legend. */\n label?: string\n description?: string\n /** An error message, or `true` to mark the group invalid without one. */\n error?: string | boolean\n orientation?: 'vertical' | 'horizontal'\n disabled?: boolean\n required?: boolean\n /** Form field name. Generated when omitted. */\n name?: string\n}\n\nconst props = withDefaults(defineProps<RadioGroupProps>(), { orientation: 'vertical', error: undefined })\n\nconst model = defineModel<RadioValue | null>({ default: null })\n\ndefineSlots<{ default?: () => unknown, label?: () => unknown }>()\n\nconst id = `j-${useId()}`\nconst descriptionId = `${id}-description`\nconst errorId = `${id}-error`\nconst errorMessage = computed(() => (typeof props.error === 'string' ? props.error : ''))\n\nprovide(RADIO_GROUP, {\n name: computed(() => props.name ?? id),\n value: computed(() => model.value),\n disabled: computed(() => props.disabled),\n required: computed(() => props.required),\n invalid: computed(() => !!props.error),\n select: (value) => {\n if (!props.disabled) model.value = value\n },\n})\n\nconst describedBy = computed(() => (errorMessage.value ? errorId : props.description ? descriptionId : undefined))\n</script>\n\n<template>\n <fieldset\n class=\"j-radio-group\"\n :class=\"{ 'is-disabled': disabled, 'is-invalid': !!error }\"\n :disabled=\"disabled\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"!!error || undefined\"\n :aria-required=\"required || undefined\"\n >\n <legend v-if=\"label || $slots.label\" class=\"j-radio-group__legend\">\n <slot name=\"label\">{{ label }}</slot>\n <span v-if=\"required\" class=\"j-field__required\" aria-hidden=\"true\">*</span>\n </legend>\n <p v-if=\"description && !errorMessage\" :id=\"descriptionId\" class=\"j-radio-group__description\">\n {{ description }}\n </p>\n <div class=\"j-radio-group__items\" :class=\"`j-radio-group__items--${orientation}`\">\n <slot />\n </div>\n <p v-if=\"errorMessage\" :id=\"errorId\" class=\"j-radio-group__error\">\n {{ errorMessage }}\n </p>\n </fieldset>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-radio-group {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-1-5);\n min-width: 0;\n margin: 0;\n padding: 0;\n border: 0;\n font-family: var(--juxt-font-sans);\n}\n\n.j-radio-group__legend {\n display: inline-flex;\n gap: var(--juxt-space-0-5);\n margin-bottom: var(--juxt-space-1-5);\n padding: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-radio-group__description,\n.j-radio-group__error {\n margin: calc(var(--juxt-space-1) * -1) 0 var(--juxt-space-1-5);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-radio-group__error {\n margin: var(--juxt-space-1) 0 0;\n color: var(--juxt-danger-text);\n}\n\n.j-radio-group__items {\n display: flex;\n}\n\n.j-radio-group__items--vertical {\n flex-direction: column;\n gap: var(--juxt-space-3);\n}\n\n.j-radio-group__items--horizontal {\n flex-wrap: wrap;\n gap: var(--juxt-space-3) var(--juxt-space-6);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { RadioValue } from './context'\nimport { computed, provide, useId } from 'vue'\nimport { RADIO_GROUP } from './context'\n\nexport interface RadioGroupProps {\n /** Group label, rendered as the fieldset legend. */\n label?: string\n description?: string\n /** An error message, or `true` to mark the group invalid without one. */\n error?: string | boolean\n orientation?: 'vertical' | 'horizontal'\n disabled?: boolean\n required?: boolean\n /** Form field name. Generated when omitted. */\n name?: string\n}\n\nconst props = withDefaults(defineProps<RadioGroupProps>(), { orientation: 'vertical', error: undefined })\n\nconst model = defineModel<RadioValue | null>({ default: null })\n\ndefineSlots<{ default?: () => unknown, label?: () => unknown }>()\n\nconst id = `j-${useId()}`\nconst descriptionId = `${id}-description`\nconst errorId = `${id}-error`\nconst errorMessage = computed(() => (typeof props.error === 'string' ? props.error : ''))\n\nprovide(RADIO_GROUP, {\n name: computed(() => props.name ?? id),\n value: computed(() => model.value),\n disabled: computed(() => props.disabled),\n required: computed(() => props.required),\n invalid: computed(() => !!props.error),\n select: (value) => {\n if (!props.disabled) model.value = value\n },\n})\n\nconst describedBy = computed(() => (errorMessage.value ? errorId : props.description ? descriptionId : undefined))\n</script>\n\n<template>\n <fieldset\n class=\"j-radio-group\"\n :class=\"{ 'is-disabled': disabled, 'is-invalid': !!error }\"\n :disabled=\"disabled\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"!!error || undefined\"\n :aria-required=\"required || undefined\"\n >\n <legend v-if=\"label || $slots.label\" class=\"j-radio-group__legend\">\n <slot name=\"label\">{{ label }}</slot>\n <span v-if=\"required\" class=\"j-field__required\" aria-hidden=\"true\">*</span>\n </legend>\n <p v-if=\"description && !errorMessage\" :id=\"descriptionId\" class=\"j-radio-group__description\">\n {{ description }}\n </p>\n <div class=\"j-radio-group__items\" :class=\"`j-radio-group__items--${orientation}`\">\n <slot />\n </div>\n <p v-if=\"errorMessage\" :id=\"errorId\" class=\"j-radio-group__error\">\n {{ errorMessage }}\n </p>\n </fieldset>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-radio-group {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-1-5);\n min-width: 0;\n margin: 0;\n padding: 0;\n border: 0;\n font-family: var(--juxt-font-sans);\n}\n\n.j-radio-group__legend {\n display: inline-flex;\n gap: var(--juxt-space-0-5);\n margin-bottom: var(--juxt-space-1-5);\n padding: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-radio-group__description,\n.j-radio-group__error {\n margin: calc(var(--juxt-space-1) * -1) 0 var(--juxt-space-1-5);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-radio-group__error {\n margin: var(--juxt-space-1) 0 0;\n color: var(--juxt-danger-text);\n}\n\n.j-radio-group__items {\n display: flex;\n}\n\n.j-radio-group__items--vertical {\n flex-direction: column;\n gap: var(--juxt-space-3);\n}\n\n.j-radio-group__items--horizontal {\n flex-wrap: wrap;\n gap: var(--juxt-space-3) var(--juxt-space-6);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nexport interface RatingProps {\n /** Number of stars. */\n max?: number\n /** Static display mode | no interaction, ideal for showing an average. */\n readonly?: boolean\n disabled?: boolean\n size?: 'sm' | 'md' | 'lg'\n /** Accessible name for the group, e.g. \"Rate this product\". */\n label?: string\n /** Allow half-star precision. Only applies in `readonly` mode. */\n allowHalf?: boolean\n}\n\nconst props = withDefaults(defineProps<RatingProps>(), { max: 5, size: 'md' })\n\nconst model = defineModel<number>({ default: 0 })\n\nconst hovered = ref<number | null>(null)\nconst stars = computed(() => Array.from({ length: props.max }, (_, i) => i + 1))\nconst display = computed(() => hovered.value ?? model.value)\n\nfunction fillFor(star: number) {\n const value = display.value\n if (value >= star) return 1\n if (props.readonly && props.allowHalf && value >= star - 0.5) return 0.5\n return 0\n}\n\nfunction select(star: number) {\n if (props.readonly || props.disabled) return\n model.value = star\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (props.readonly || props.disabled) return\n let next: number | undefined\n if (event.key === 'ArrowRight' || event.key === 'ArrowUp') next = Math.min(props.max, model.value + 1)\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') next = Math.max(0, model.value - 1)\n else if (event.key === 'Home') next = 0\n else if (event.key === 'End') next = props.max\n if (next === undefined) return\n event.preventDefault()\n model.value = next\n}\n\nconst groupLabel = computed(() => props.label ?? (props.readonly ? `${model.value} out of ${props.max} stars` : 'Rating'))\n</script>\n\n<template>\n <div\n class=\"j-rating\"\n :class=\"[`j-rating--${size}`, { 'is-readonly': readonly, 'is-disabled': disabled }]\"\n :role=\"readonly ? 'img' : 'radiogroup'\"\n :aria-label=\"groupLabel\"\n :aria-disabled=\"disabled || undefined\"\n @mouseleave=\"hovered = null\"\n >\n <template v-if=\"readonly\">\n <span v-for=\"star in stars\" :key=\"star\" class=\"j-rating__star\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 16 16\" class=\"j-rating__star-bg\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\n <span class=\"j-rating__star-fill\" :style=\"{ width: `${fillFor(star) * 100}%` }\">\n <svg viewBox=\"0 0 16 16\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\n </span>\n </span>\n </template>\n <template v-else>\n <button\n v-for=\"star in stars\"\n :key=\"star\"\n type=\"button\"\n role=\"radio\"\n class=\"j-rating__button j-focusable\"\n :aria-checked=\"model === star\"\n :aria-label=\"`${star} ${star === 1 ? 'star' : 'stars'}`\"\n :tabindex=\"disabled ? -1 : (model === star || (model === 0 && star === 1)) ? 0 : -1\"\n :disabled=\"disabled\"\n @click=\"select(star)\"\n @mouseenter=\"hovered = star\"\n @focus=\"hovered = star\"\n @blur=\"hovered = null\"\n @keydown=\"onKeydown\"\n >\n <svg viewBox=\"0 0 16 16\" class=\"j-rating__star-bg\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\n <span class=\"j-rating__star-fill\" :style=\"{ width: `${fillFor(star) * 100}%` }\">\n <svg viewBox=\"0 0 16 16\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\n </span>\n </button>\n </template>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-rating {\n --j-rating-size: 1.25rem;\n\n display: inline-flex;\n align-items: center;\n gap: var(--juxt-space-0-5);\n font-family: var(--juxt-font-sans);\n}\n\n.j-rating--sm {\n --j-rating-size: 1rem;\n}\n\n.j-rating--lg {\n --j-rating-size: 1.5rem;\n}\n\n.j-rating__star,\n.j-rating__button {\n position: relative;\n display: inline-flex;\n flex: none;\n width: var(--j-rating-size);\n height: var(--j-rating-size);\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--juxt-fg-disabled);\n cursor: default;\n}\n\n.j-rating__button {\n border-radius: var(--juxt-radius-xs);\n cursor: pointer;\n transition: transform var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-rating__button:hover:not(:disabled) {\n transform: scale(1.1);\n}\n\n.j-rating__button:disabled {\n cursor: not-allowed;\n}\n\n.j-rating.is-disabled .j-rating__button {\n opacity: 0.5;\n}\n\n.j-rating__star-bg,\n.j-rating__star-fill > svg {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n fill: currentColor;\n stroke: none;\n}\n\n.j-rating__star-fill {\n position: absolute;\n inset: 0;\n overflow: hidden;\n color: var(--juxt-warning);\n transition: width var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-rating__button:hover:not(:disabled) {\n transform: none;\n }\n\n .j-rating__star-fill {\n transition: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nexport interface RatingProps {\n /** Number of stars. */\n max?: number\n /** Static display mode | no interaction, ideal for showing an average. */\n readonly?: boolean\n disabled?: boolean\n size?: 'sm' | 'md' | 'lg'\n /** Accessible name for the group, e.g. \"Rate this product\". */\n label?: string\n /** Allow half-star precision. Only applies in `readonly` mode. */\n allowHalf?: boolean\n}\n\nconst props = withDefaults(defineProps<RatingProps>(), { max: 5, size: 'md' })\n\nconst model = defineModel<number>({ default: 0 })\n\nconst hovered = ref<number | null>(null)\nconst stars = computed(() => Array.from({ length: props.max }, (_, i) => i + 1))\nconst display = computed(() => hovered.value ?? model.value)\n\nfunction fillFor(star: number) {\n const value = display.value\n if (value >= star) return 1\n if (props.readonly && props.allowHalf && value >= star - 0.5) return 0.5\n return 0\n}\n\nfunction select(star: number) {\n if (props.readonly || props.disabled) return\n model.value = star\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (props.readonly || props.disabled) return\n let next: number | undefined\n if (event.key === 'ArrowRight' || event.key === 'ArrowUp') next = Math.min(props.max, model.value + 1)\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') next = Math.max(0, model.value - 1)\n else if (event.key === 'Home') next = 0\n else if (event.key === 'End') next = props.max\n if (next === undefined) return\n event.preventDefault()\n model.value = next\n}\n\nconst groupLabel = computed(() => props.label ?? (props.readonly ? `${model.value} out of ${props.max} stars` : 'Rating'))\n</script>\n\n<template>\n <div\n class=\"j-rating\"\n :class=\"[`j-rating--${size}`, { 'is-readonly': readonly, 'is-disabled': disabled }]\"\n :role=\"readonly ? 'img' : 'radiogroup'\"\n :aria-label=\"groupLabel\"\n :aria-disabled=\"disabled || undefined\"\n @mouseleave=\"hovered = null\"\n >\n <template v-if=\"readonly\">\n <span v-for=\"star in stars\" :key=\"star\" class=\"j-rating__star\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 16 16\" class=\"j-rating__star-bg\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\n <span class=\"j-rating__star-fill\" :style=\"{ width: `${fillFor(star) * 100}%` }\">\n <svg viewBox=\"0 0 16 16\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\n </span>\n </span>\n </template>\n <template v-else>\n <button\n v-for=\"star in stars\"\n :key=\"star\"\n type=\"button\"\n role=\"radio\"\n class=\"j-rating__button j-focusable\"\n :aria-checked=\"model === star\"\n :aria-label=\"`${star} ${star === 1 ? 'star' : 'stars'}`\"\n :tabindex=\"disabled ? -1 : (model === star || (model === 0 && star === 1)) ? 0 : -1\"\n :disabled=\"disabled\"\n @click=\"select(star)\"\n @mouseenter=\"hovered = star\"\n @focus=\"hovered = star\"\n @blur=\"hovered = null\"\n @keydown=\"onKeydown\"\n >\n <svg viewBox=\"0 0 16 16\" class=\"j-rating__star-bg\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\n <span class=\"j-rating__star-fill\" :style=\"{ width: `${fillFor(star) * 100}%` }\">\n <svg viewBox=\"0 0 16 16\"><path d=\"M8 1.5l1.98 4.01 4.42.64-3.2 3.12.76 4.4L8 11.6l-3.96 2.08.76-4.4-3.2-3.12 4.42-.64z\" /></svg>\n </span>\n </button>\n </template>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-rating {\n --j-rating-size: 1.25rem;\n\n display: inline-flex;\n align-items: center;\n gap: var(--juxt-space-0-5);\n font-family: var(--juxt-font-sans);\n}\n\n.j-rating--sm {\n --j-rating-size: 1rem;\n}\n\n.j-rating--lg {\n --j-rating-size: 1.5rem;\n}\n\n.j-rating__star,\n.j-rating__button {\n position: relative;\n display: inline-flex;\n flex: none;\n width: var(--j-rating-size);\n height: var(--j-rating-size);\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--juxt-fg-disabled);\n cursor: default;\n}\n\n.j-rating__button {\n border-radius: var(--juxt-radius-xs);\n cursor: pointer;\n transition: transform var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-rating__button:hover:not(:disabled) {\n transform: scale(1.1);\n}\n\n.j-rating__button:disabled {\n cursor: not-allowed;\n}\n\n.j-rating.is-disabled .j-rating__button {\n opacity: 0.5;\n}\n\n.j-rating__star-bg,\n.j-rating__star-fill > svg {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n fill: currentColor;\n stroke: none;\n}\n\n.j-rating__star-fill {\n position: absolute;\n inset: 0;\n overflow: hidden;\n color: var(--juxt-warning);\n transition: width var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-rating__button:hover:not(:disabled) {\n transform: none;\n }\n\n .j-rating__star-fill {\n transition: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T extends string | number = string\">\nimport type { FieldProps } from '../field/useField'\nimport { computed, nextTick, ref, watch } from 'vue'\nimport { useFloating } from '../../composables/useFloating'\nimport { useLayer } from '../../composables/useLayer'\nimport { useTypeahead } from '../../composables/useTypeahead'\nimport { IconCheck, IconChevronUpDown } from '../../icons'\nimport JFieldShell from '../field/JFieldShell.vue'\nimport { useField } from '../field/useField'\nimport JPortal from '../primitives/JPortal.vue'\n\nexport interface SelectOption<V = string | number> {\n value: V\n label: string\n description?: string\n disabled?: boolean\n}\n\nexport interface SelectProps<V> extends FieldProps {\n /** Options as objects, or plain strings when value and label are the same. */\n options: Array<SelectOption<V> | V>\n placeholder?: string\n size?: 'sm' | 'md' | 'lg'\n /** Submits the value under this name in a form. */\n name?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SelectProps<T>>(), {\n placeholder: 'Select…',\n size: 'md',\n error: undefined,\n})\n\nconst model = defineModel<T | null>({ default: null })\n\nconst emit = defineEmits<{\n open: []\n close: []\n}>()\n\ndefineSlots<{\n option?: (props: { option: SelectOption<T>, selected: boolean, active: boolean }) => unknown\n value?: (props: { option: SelectOption<T> }) => unknown\n label?: () => unknown\n}>()\n\nconst field = useField(props)\nconst listboxId = computed(() => `${field.id.value}-listbox`)\nconst valueId = computed(() => `${field.id.value}-value`)\nconst optionId = (index: number) => `${field.id.value}-option-${index}`\n\nconst normalized = computed<SelectOption<T>[]>(() =>\n props.options.map(option =>\n typeof option === 'object' && option !== null ? option : { value: option as T, label: String(option) },\n ),\n)\n\n/*\n * The list the user sees and navigates. Today it's every option; a search\n * input only needs to set `query` for this to become a filtered view.\n */\nconst query = ref('')\nconst visibleOptions = computed(() => {\n const q = query.value.trim().toLowerCase()\n if (!q) return normalized.value\n return normalized.value.filter(option => option.label.toLowerCase().includes(q))\n})\n\nconst selected = computed(() => normalized.value.find(option => option.value === model.value))\n\nconst open = ref(false)\nconst activeIndex = ref(-1)\nconst trigger = ref<HTMLButtonElement | null>(null)\nconst listbox = ref<HTMLElement | null>(null)\nconst pointerActive = ref(false)\n\nconst { styles } = useFloating(trigger, listbox, { placement: 'bottom-start', offset: 4, matchWidth: true })\nconst typeahead = useTypeahead()\n\nuseLayer({\n active: open,\n elements: () => [trigger.value, listbox.value],\n onEscape: () => close(),\n onPointerDownOutside: () => close(),\n})\n\nconst isEnabled = (index: number) => {\n const option = visibleOptions.value[index]\n return !!option && !option.disabled\n}\n\nfunction step(from: number, delta: 1 | -1): number {\n const count = visibleOptions.value.length\n for (let i = from + delta; i >= 0 && i < count; i += delta) {\n if (isEnabled(i)) return i\n }\n return from\n}\n\nconst first = () => step(-1, 1)\nconst last = () => step(visibleOptions.value.length, -1)\n\nfunction setActive(index: number, scroll = true) {\n activeIndex.value = index\n if (!scroll) return\n nextTick(() => {\n document.getElementById(optionId(index))?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction openList(focus: 'selected' | 'first' | 'last' = 'selected') {\n if (props.disabled || open.value) return\n open.value = true\n emit('open')\n const selectedIndex = visibleOptions.value.findIndex(option => option.value === model.value)\n const target = focus === 'last' ? last() : focus === 'first' ? first() : selectedIndex >= 0 ? selectedIndex : first()\n setActive(target)\n}\n\nfunction close() {\n if (!open.value) return\n open.value = false\n pointerActive.value = false\n emit('close')\n}\n\nfunction commit(index: number) {\n const option = visibleOptions.value[index]\n if (!option || option.disabled) return\n model.value = option.value\n close()\n}\n\nfunction onTriggerClick() {\n if (open.value) close()\n else openList()\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (props.disabled) return\n const { key } = event\n\n if (!open.value) {\n if (key === 'ArrowDown' || key === 'Enter' || key === ' ') {\n event.preventDefault()\n openList()\n } else if (key === 'ArrowUp') {\n event.preventDefault()\n openList()\n } else if (key === 'Home') {\n event.preventDefault()\n openList('first')\n } else if (key === 'End') {\n event.preventDefault()\n openList('last')\n } else if (key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey) {\n const index = typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\n if (index >= 0) {\n openList()\n setActive(index)\n }\n }\n return\n }\n\n switch (key) {\n case 'ArrowDown':\n event.preventDefault()\n if (event.altKey) return\n setActive(step(activeIndex.value, 1))\n break\n case 'ArrowUp':\n event.preventDefault()\n if (event.altKey) return commit(activeIndex.value)\n setActive(step(activeIndex.value, -1))\n break\n case 'Home':\n event.preventDefault()\n setActive(first())\n break\n case 'End':\n event.preventDefault()\n setActive(last())\n break\n case 'PageDown':\n event.preventDefault()\n setActive(Math.min(last(), step(Math.min(activeIndex.value + 9, visibleOptions.value.length - 1), 1)))\n break\n case 'PageUp':\n event.preventDefault()\n setActive(Math.max(first(), step(Math.max(activeIndex.value - 9, 0), -1)))\n break\n case 'Enter':\n event.preventDefault()\n commit(activeIndex.value)\n break\n case 'Tab':\n commit(activeIndex.value)\n close()\n break\n case ' ':\n event.preventDefault()\n if (typeahead.isSearching()) typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\n else commit(activeIndex.value)\n break\n default:\n if (key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey) {\n const index = typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\n if (index >= 0) setActive(index)\n }\n }\n}\n\n// Only let the pointer steer the active option once it actually moves, so a\n// list opening under a resting cursor doesn't hijack the keyboard position.\nfunction onOptionPointerMove(index: number) {\n pointerActive.value = true\n if (activeIndex.value !== index && isEnabled(index)) setActive(index, false)\n}\n\nwatch(() => props.disabled, value => value && close())\n\ndefineExpose({ focus: () => trigger.value?.focus(), open: () => openList(), close })\n</script>\n\n<template>\n <JFieldShell\n :id=\"field.id.value\"\n :class=\"$attrs.class\"\n :style=\"$attrs.style\"\n :label-id=\"field.labelId.value\"\n :description-id=\"field.descriptionId.value\"\n :error-id=\"field.errorId.value\"\n :label=\"label\"\n :description=\"description\"\n :error-message=\"field.errorMessage.value\"\n :show-description=\"field.showDescription.value\"\n :required=\"required\"\n :disabled=\"disabled\"\n >\n <template v-if=\"$slots.label\" #label>\n <slot name=\"label\" />\n </template>\n <button\n :id=\"field.id.value\"\n ref=\"trigger\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n type=\"button\"\n role=\"combobox\"\n class=\"j-control j-select__trigger\"\n :class=\"[`j-control--${size}`, { 'is-open': open, 'is-invalid': field.invalid.value, 'is-disabled': disabled }]\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"open\"\n :aria-controls=\"open ? listboxId : undefined\"\n :aria-activedescendant=\"open && activeIndex >= 0 ? optionId(activeIndex) : undefined\"\n :aria-labelledby=\"label || $slots.label ? `${field.labelId.value} ${valueId}` : undefined\"\n :aria-describedby=\"field.describedBy.value\"\n :aria-invalid=\"field.invalid.value || undefined\"\n :aria-required=\"required || undefined\"\n :disabled=\"disabled\"\n @click=\"onTriggerClick\"\n @keydown=\"onKeydown\"\n >\n <span :id=\"valueId\" class=\"j-select__value\" :class=\"{ 'is-placeholder': !selected }\">\n <slot v-if=\"selected\" name=\"value\" :option=\"selected\">{{ selected.label }}</slot>\n <template v-else>{{ placeholder }}</template>\n </span>\n <IconChevronUpDown class=\"j-select__chevron\" />\n </button>\n <input v-if=\"name\" type=\"hidden\" :name=\"name\" :value=\"model ?? ''\" />\n\n <JPortal>\n <Transition name=\"j-pop\">\n <ul\n v-if=\"open\"\n :id=\"listboxId\"\n ref=\"listbox\"\n role=\"listbox\"\n class=\"j-select__listbox j-popover-surface\"\n :class=\"{ 'is-pointer': pointerActive }\"\n :style=\"styles\"\n :aria-labelledby=\"label ? field.labelId.value : undefined\"\n tabindex=\"-1\"\n @pointerdown.prevent\n >\n <li\n v-for=\"(option, index) in visibleOptions\"\n :id=\"optionId(index)\"\n :key=\"String(option.value)\"\n role=\"option\"\n class=\"j-select__option\"\n :class=\"{ 'is-active': index === activeIndex, 'is-selected': option.value === model }\"\n :aria-selected=\"option.value === model\"\n :aria-disabled=\"option.disabled || undefined\"\n @pointermove=\"onOptionPointerMove(index)\"\n @click=\"commit(index)\"\n >\n <span class=\"j-select__option-text\">\n <slot name=\"option\" :option=\"option\" :selected=\"option.value === model\" :active=\"index === activeIndex\">\n <span class=\"j-select__option-label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"j-select__option-description\">{{ option.description }}</span>\n </slot>\n </span>\n <IconCheck class=\"j-select__check\" />\n </li>\n <li v-if=\"visibleOptions.length === 0\" class=\"j-select__empty\" role=\"presentation\">\n No options\n </li>\n </ul>\n </Transition>\n </JPortal>\n </JFieldShell>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-select__trigger {\n width: 100%;\n justify-content: space-between;\n text-align: left;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n.j-select__trigger:focus-visible {\n outline: none;\n}\n\n.j-select__trigger:focus-visible:not(.is-invalid) {\n border-color: var(--juxt-accent);\n box-shadow: 0 0 0 3px var(--juxt-ring-soft);\n}\n\n.j-select__trigger:disabled {\n cursor: not-allowed;\n}\n\n.j-select__value {\n overflow: hidden;\n min-width: 0;\n text-overflow: ellipsis;\n white-space: nowrap;\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-select__value.is-placeholder {\n color: var(--juxt-fg-muted);\n}\n\n.j-select__trigger.is-disabled .j-select__value {\n color: var(--juxt-fg-disabled);\n}\n\n.j-select__chevron {\n flex: none;\n width: 0.875rem;\n height: 0.875rem;\n margin-right: -0.125rem;\n color: var(--juxt-fg-muted);\n}\n\n.j-select__listbox {\n z-index: var(--juxt-z-popover);\n box-sizing: border-box;\n max-width: calc(100vw - 16px);\n max-height: min(18rem, var(--j-available-height, 18rem));\n margin: 0;\n padding: var(--juxt-space-1);\n overflow-y: auto;\n overscroll-behavior: contain;\n list-style: none;\n outline: none;\n scroll-padding-block: var(--juxt-space-1);\n}\n\n.j-select__option {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n min-height: var(--juxt-control-sm);\n padding: var(--juxt-space-1-5) var(--juxt-space-2);\n border-radius: var(--juxt-radius-xs);\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n user-select: none;\n}\n\n.j-select__option.is-active {\n background: var(--juxt-surface-hover);\n}\n\n.j-select__option[aria-disabled='true'] {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-select__option-text {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n}\n\n.j-select__option-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.j-select__option-description {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n}\n\n.j-select__check {\n flex: none;\n width: 0.875rem;\n height: 0.875rem;\n color: var(--juxt-accent-text);\n opacity: 0;\n transform: scale(0.8);\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-select__option.is-selected .j-select__check {\n opacity: 1;\n transform: none;\n}\n\n.j-select__empty {\n padding: var(--juxt-space-3) var(--juxt-space-2);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n text-align: center;\n}\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T extends string | number = string\">\nimport type { FieldProps } from '../field/useField'\nimport { computed, nextTick, ref, watch } from 'vue'\nimport { useFloating } from '../../composables/useFloating'\nimport { useLayer } from '../../composables/useLayer'\nimport { useTypeahead } from '../../composables/useTypeahead'\nimport { IconCheck, IconChevronUpDown } from '../../icons'\nimport JFieldShell from '../field/JFieldShell.vue'\nimport { useField } from '../field/useField'\nimport JPortal from '../primitives/JPortal.vue'\n\nexport interface SelectOption<V = string | number> {\n value: V\n label: string\n description?: string\n disabled?: boolean\n}\n\nexport interface SelectProps<V> extends FieldProps {\n /** Options as objects, or plain strings when value and label are the same. */\n options: Array<SelectOption<V> | V>\n placeholder?: string\n size?: 'sm' | 'md' | 'lg'\n /** Submits the value under this name in a form. */\n name?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SelectProps<T>>(), {\n placeholder: 'Select…',\n size: 'md',\n error: undefined,\n})\n\nconst model = defineModel<T | null>({ default: null })\n\nconst emit = defineEmits<{\n open: []\n close: []\n}>()\n\ndefineSlots<{\n option?: (props: { option: SelectOption<T>, selected: boolean, active: boolean }) => unknown\n value?: (props: { option: SelectOption<T> }) => unknown\n label?: () => unknown\n}>()\n\nconst field = useField(props)\nconst listboxId = computed(() => `${field.id.value}-listbox`)\nconst valueId = computed(() => `${field.id.value}-value`)\nconst optionId = (index: number) => `${field.id.value}-option-${index}`\n\nconst normalized = computed<SelectOption<T>[]>(() =>\n props.options.map(option =>\n typeof option === 'object' && option !== null ? option : { value: option as T, label: String(option) },\n ),\n)\n\n/*\n * The list the user sees and navigates. Today it's every option; a search\n * input only needs to set `query` for this to become a filtered view.\n */\nconst query = ref('')\nconst visibleOptions = computed(() => {\n const q = query.value.trim().toLowerCase()\n if (!q) return normalized.value\n return normalized.value.filter(option => option.label.toLowerCase().includes(q))\n})\n\nconst selected = computed(() => normalized.value.find(option => option.value === model.value))\n\nconst open = ref(false)\nconst activeIndex = ref(-1)\nconst trigger = ref<HTMLButtonElement | null>(null)\nconst listbox = ref<HTMLElement | null>(null)\nconst pointerActive = ref(false)\n\nconst { styles } = useFloating(trigger, listbox, { placement: 'bottom-start', offset: 4, matchWidth: true })\nconst typeahead = useTypeahead()\n\nuseLayer({\n active: open,\n elements: () => [trigger.value, listbox.value],\n onEscape: () => close(),\n onPointerDownOutside: () => close(),\n})\n\nconst isEnabled = (index: number) => {\n const option = visibleOptions.value[index]\n return !!option && !option.disabled\n}\n\nfunction step(from: number, delta: 1 | -1): number {\n const count = visibleOptions.value.length\n for (let i = from + delta; i >= 0 && i < count; i += delta) {\n if (isEnabled(i)) return i\n }\n return from\n}\n\nconst first = () => step(-1, 1)\nconst last = () => step(visibleOptions.value.length, -1)\n\nfunction setActive(index: number, scroll = true) {\n activeIndex.value = index\n if (!scroll) return\n nextTick(() => {\n document.getElementById(optionId(index))?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction openList(focus: 'selected' | 'first' | 'last' = 'selected') {\n if (props.disabled || open.value) return\n open.value = true\n emit('open')\n const selectedIndex = visibleOptions.value.findIndex(option => option.value === model.value)\n const target = focus === 'last' ? last() : focus === 'first' ? first() : selectedIndex >= 0 ? selectedIndex : first()\n setActive(target)\n}\n\nfunction close() {\n if (!open.value) return\n open.value = false\n pointerActive.value = false\n emit('close')\n}\n\nfunction commit(index: number) {\n const option = visibleOptions.value[index]\n if (!option || option.disabled) return\n model.value = option.value\n close()\n}\n\nfunction onTriggerClick() {\n if (open.value) close()\n else openList()\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (props.disabled) return\n const { key } = event\n\n if (!open.value) {\n if (key === 'ArrowDown' || key === 'Enter' || key === ' ') {\n event.preventDefault()\n openList()\n } else if (key === 'ArrowUp') {\n event.preventDefault()\n openList()\n } else if (key === 'Home') {\n event.preventDefault()\n openList('first')\n } else if (key === 'End') {\n event.preventDefault()\n openList('last')\n } else if (key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey) {\n const index = typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\n if (index >= 0) {\n openList()\n setActive(index)\n }\n }\n return\n }\n\n switch (key) {\n case 'ArrowDown':\n event.preventDefault()\n if (event.altKey) return\n setActive(step(activeIndex.value, 1))\n break\n case 'ArrowUp':\n event.preventDefault()\n if (event.altKey) return commit(activeIndex.value)\n setActive(step(activeIndex.value, -1))\n break\n case 'Home':\n event.preventDefault()\n setActive(first())\n break\n case 'End':\n event.preventDefault()\n setActive(last())\n break\n case 'PageDown':\n event.preventDefault()\n setActive(Math.min(last(), step(Math.min(activeIndex.value + 9, visibleOptions.value.length - 1), 1)))\n break\n case 'PageUp':\n event.preventDefault()\n setActive(Math.max(first(), step(Math.max(activeIndex.value - 9, 0), -1)))\n break\n case 'Enter':\n event.preventDefault()\n commit(activeIndex.value)\n break\n case 'Tab':\n commit(activeIndex.value)\n close()\n break\n case ' ':\n event.preventDefault()\n if (typeahead.isSearching()) typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\n else commit(activeIndex.value)\n break\n default:\n if (key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey) {\n const index = typeahead.search(key, visibleOptions.value, option => option.label, activeIndex.value)\n if (index >= 0) setActive(index)\n }\n }\n}\n\n// Only let the pointer steer the active option once it actually moves, so a\n// list opening under a resting cursor doesn't hijack the keyboard position.\nfunction onOptionPointerMove(index: number) {\n pointerActive.value = true\n if (activeIndex.value !== index && isEnabled(index)) setActive(index, false)\n}\n\nwatch(() => props.disabled, value => value && close())\n\ndefineExpose({ focus: () => trigger.value?.focus(), open: () => openList(), close })\n</script>\n\n<template>\n <JFieldShell\n :id=\"field.id.value\"\n :class=\"$attrs.class\"\n :style=\"$attrs.style\"\n :label-id=\"field.labelId.value\"\n :description-id=\"field.descriptionId.value\"\n :error-id=\"field.errorId.value\"\n :label=\"label\"\n :description=\"description\"\n :error-message=\"field.errorMessage.value\"\n :show-description=\"field.showDescription.value\"\n :required=\"required\"\n :disabled=\"disabled\"\n >\n <template v-if=\"$slots.label\" #label>\n <slot name=\"label\" />\n </template>\n <button\n :id=\"field.id.value\"\n ref=\"trigger\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n type=\"button\"\n role=\"combobox\"\n class=\"j-control j-select__trigger\"\n :class=\"[`j-control--${size}`, { 'is-open': open, 'is-invalid': field.invalid.value, 'is-disabled': disabled }]\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"open\"\n :aria-controls=\"open ? listboxId : undefined\"\n :aria-activedescendant=\"open && activeIndex >= 0 ? optionId(activeIndex) : undefined\"\n :aria-labelledby=\"label || $slots.label ? `${field.labelId.value} ${valueId}` : undefined\"\n :aria-describedby=\"field.describedBy.value\"\n :aria-invalid=\"field.invalid.value || undefined\"\n :aria-required=\"required || undefined\"\n :disabled=\"disabled\"\n @click=\"onTriggerClick\"\n @keydown=\"onKeydown\"\n >\n <span :id=\"valueId\" class=\"j-select__value\" :class=\"{ 'is-placeholder': !selected }\">\n <slot v-if=\"selected\" name=\"value\" :option=\"selected\">{{ selected.label }}</slot>\n <template v-else>{{ placeholder }}</template>\n </span>\n <IconChevronUpDown class=\"j-select__chevron\" />\n </button>\n <input v-if=\"name\" type=\"hidden\" :name=\"name\" :value=\"model ?? ''\" />\n\n <JPortal>\n <Transition name=\"j-pop\">\n <ul\n v-if=\"open\"\n :id=\"listboxId\"\n ref=\"listbox\"\n role=\"listbox\"\n class=\"j-select__listbox j-popover-surface\"\n :class=\"{ 'is-pointer': pointerActive }\"\n :style=\"styles\"\n :aria-labelledby=\"label ? field.labelId.value : undefined\"\n tabindex=\"-1\"\n @pointerdown.prevent\n >\n <li\n v-for=\"(option, index) in visibleOptions\"\n :id=\"optionId(index)\"\n :key=\"String(option.value)\"\n role=\"option\"\n class=\"j-select__option\"\n :class=\"{ 'is-active': index === activeIndex, 'is-selected': option.value === model }\"\n :aria-selected=\"option.value === model\"\n :aria-disabled=\"option.disabled || undefined\"\n @pointermove=\"onOptionPointerMove(index)\"\n @click=\"commit(index)\"\n >\n <span class=\"j-select__option-text\">\n <slot name=\"option\" :option=\"option\" :selected=\"option.value === model\" :active=\"index === activeIndex\">\n <span class=\"j-select__option-label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"j-select__option-description\">{{ option.description }}</span>\n </slot>\n </span>\n <IconCheck class=\"j-select__check\" />\n </li>\n <li v-if=\"visibleOptions.length === 0\" class=\"j-select__empty\" role=\"presentation\">\n No options\n </li>\n </ul>\n </Transition>\n </JPortal>\n </JFieldShell>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-select__trigger {\n width: 100%;\n justify-content: space-between;\n text-align: left;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n.j-select__trigger:focus-visible {\n outline: none;\n}\n\n.j-select__trigger:focus-visible:not(.is-invalid) {\n border-color: var(--juxt-accent);\n box-shadow: 0 0 0 3px var(--juxt-ring-soft);\n}\n\n.j-select__trigger:disabled {\n cursor: not-allowed;\n}\n\n.j-select__value {\n overflow: hidden;\n min-width: 0;\n text-overflow: ellipsis;\n white-space: nowrap;\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-select__value.is-placeholder {\n color: var(--juxt-fg-muted);\n}\n\n.j-select__trigger.is-disabled .j-select__value {\n color: var(--juxt-fg-disabled);\n}\n\n.j-select__chevron {\n flex: none;\n width: 0.875rem;\n height: 0.875rem;\n margin-right: -0.125rem;\n color: var(--juxt-fg-muted);\n}\n\n.j-select__listbox {\n z-index: var(--juxt-z-popover);\n box-sizing: border-box;\n max-width: calc(100vw - 16px);\n max-height: min(18rem, var(--j-available-height, 18rem));\n margin: 0;\n padding: var(--juxt-space-1);\n overflow-y: auto;\n overscroll-behavior: contain;\n list-style: none;\n outline: none;\n scroll-padding-block: var(--juxt-space-1);\n}\n\n.j-select__option {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n min-height: var(--juxt-control-sm);\n padding: var(--juxt-space-1-5) var(--juxt-space-2);\n border-radius: var(--juxt-radius-xs);\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n user-select: none;\n}\n\n.j-select__option.is-active {\n background: var(--juxt-surface-hover);\n}\n\n.j-select__option[aria-disabled='true'] {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-select__option-text {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n}\n\n.j-select__option-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.j-select__option-description {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n}\n\n.j-select__check {\n flex: none;\n width: 0.875rem;\n height: 0.875rem;\n color: var(--juxt-accent-text);\n opacity: 0;\n transform: scale(0.8);\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-select__option.is-selected .j-select__check {\n opacity: 1;\n transform: none;\n}\n\n.j-select__empty {\n padding: var(--juxt-space-3) var(--juxt-space-2);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n text-align: center;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport interface SeparatorProps {\n orientation?: 'horizontal' | 'vertical'\n /** Text in the middle of a horizontal separator, e.g. \"or\". */\n label?: string\n /**\n * Purely visual (the default). Set to false when the separator divides\n * meaningful groups and should be announced.\n */\n decorative?: boolean\n}\n\nwithDefaults(defineProps<SeparatorProps>(), { orientation: 'horizontal', decorative: true })\n\ndefineSlots<{ default?: () => unknown }>()\n</script>\n\n<template>\n <div\n class=\"j-separator\"\n :class=\"[`j-separator--${orientation}`, { 'j-separator--labelled': label || $slots.default }]\"\n :role=\"decorative ? 'none' : 'separator'\"\n :aria-orientation=\"decorative ? undefined : orientation\"\n >\n <span v-if=\"label || $slots.default\" class=\"j-separator__label\"><slot>{{ label }}</slot></span>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-separator {\n flex: none;\n background: var(--juxt-border-subtle);\n}\n\n.dark .j-separator,\n[data-theme='dark'] .j-separator {\n background: var(--juxt-border);\n}\n\n.j-separator--horizontal {\n width: 100%;\n height: 1px;\n}\n\n.j-separator--vertical {\n align-self: stretch;\n width: 1px;\n min-height: 1rem;\n}\n\n.j-separator--labelled {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-3);\n height: auto;\n background: none !important;\n color: var(--juxt-fg-muted);\n font-family: var(--juxt-font-sans);\n font-size: var(--juxt-text-xs);\n}\n\n.j-separator--labelled::before,\n.j-separator--labelled::after {\n content: '';\n flex: 1;\n height: 1px;\n background: var(--juxt-border-subtle);\n}\n\n.dark .j-separator--labelled::before,\n.dark .j-separator--labelled::after,\n[data-theme='dark'] .j-separator--labelled::before,\n[data-theme='dark'] .j-separator--labelled::after {\n background: var(--juxt-border);\n}\n\n.j-separator__label {\n flex: none;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport interface SeparatorProps {\n orientation?: 'horizontal' | 'vertical'\n /** Text in the middle of a horizontal separator, e.g. \"or\". */\n label?: string\n /**\n * Purely visual (the default). Set to false when the separator divides\n * meaningful groups and should be announced.\n */\n decorative?: boolean\n}\n\nwithDefaults(defineProps<SeparatorProps>(), { orientation: 'horizontal', decorative: true })\n\ndefineSlots<{ default?: () => unknown }>()\n</script>\n\n<template>\n <div\n class=\"j-separator\"\n :class=\"[`j-separator--${orientation}`, { 'j-separator--labelled': label || $slots.default }]\"\n :role=\"decorative ? 'none' : 'separator'\"\n :aria-orientation=\"decorative ? undefined : orientation\"\n >\n <span v-if=\"label || $slots.default\" class=\"j-separator__label\"><slot>{{ label }}</slot></span>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-separator {\n flex: none;\n background: var(--juxt-border-subtle);\n}\n\n.dark .j-separator,\n[data-theme='dark'] .j-separator {\n background: var(--juxt-border);\n}\n\n.j-separator--horizontal {\n width: 100%;\n height: 1px;\n}\n\n.j-separator--vertical {\n align-self: stretch;\n width: 1px;\n min-height: 1rem;\n}\n\n.j-separator--labelled {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-3);\n height: auto;\n background: none !important;\n color: var(--juxt-fg-muted);\n font-family: var(--juxt-font-sans);\n font-size: var(--juxt-text-xs);\n}\n\n.j-separator--labelled::before,\n.j-separator--labelled::after {\n content: '';\n flex: 1;\n height: 1px;\n background: var(--juxt-border-subtle);\n}\n\n.dark .j-separator--labelled::before,\n.dark .j-separator--labelled::after,\n[data-theme='dark'] .j-separator--labelled::before,\n[data-theme='dark'] .j-separator--labelled::after {\n background: var(--juxt-border);\n}\n\n.j-separator__label {\n flex: none;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, useId } from 'vue'\nimport { FIELD_SELECTOR, useModal } from '../../composables/useModal'\nimport { IconX } from '../../icons'\nimport JPortal from '../primitives/JPortal.vue'\n\nexport interface SheetProps {\n title?: string\n description?: string\n /** Edge the sheet slides in from. */\n side?: 'right' | 'left' | 'bottom'\n /** Width (or height for `bottom`): 320, 400 or 560px. */\n size?: 'sm' | 'md' | 'lg'\n closeOnOverlay?: boolean\n closeOnEscape?: boolean\n hideClose?: boolean\n /** Accessible label when there is no visible title. */\n ariaLabel?: string\n}\n\nconst props = withDefaults(defineProps<SheetProps>(), {\n side: 'right',\n size: 'md',\n closeOnOverlay: true,\n closeOnEscape: true,\n})\n\nconst open = defineModel<boolean>('open', { default: false })\n\nconst emit = defineEmits<{\n /** After the leave transition finishes. */\n afterClose: []\n}>()\n\ndefineSlots<{\n default?: (props: { close: () => void }) => unknown\n header?: (props: { titleId: string, descriptionId: string }) => unknown\n footer?: (props: { close: () => void }) => unknown\n}>()\n\nconst id = useId()\nconst titleId = `j-${id}-title`\nconst descriptionId = `j-${id}-description`\nconst panel = ref<HTMLElement | null>(null)\n\nfunction close() {\n open.value = false\n}\n\nconst { nudging, onBackdropPointerDown, onBackdropClick } = useModal(panel, open, {\n closeOnEscape: () => props.closeOnEscape,\n closeOnOverlay: () => props.closeOnOverlay,\n close,\n focusSelector: `.j-sheet__body ${FIELD_SELECTOR}`,\n})\n\ndefineExpose({ close })\n</script>\n\n<template>\n <JPortal>\n <Transition name=\"j-sheet-overlay\">\n <div v-if=\"open\" class=\"j-sheet__overlay\" aria-hidden=\"true\" />\n </Transition>\n <Transition name=\"j-sheet\" :duration=\"{ enter: 280, leave: 180 }\" @after-leave=\"emit('afterClose')\">\n <div\n v-if=\"open\"\n class=\"j-sheet__positioner\"\n :class=\"`j-sheet__positioner--${side}`\"\n @pointerdown=\"onBackdropPointerDown\"\n @click=\"onBackdropClick\"\n >\n <div\n ref=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n class=\"j-sheet\"\n :class=\"[`j-sheet--${side}`, `j-sheet--${size}`, { 'is-nudging': nudging }]\"\n :aria-labelledby=\"title || $slots.header ? titleId : undefined\"\n :aria-label=\"ariaLabel\"\n :aria-describedby=\"description ? descriptionId : undefined\"\n @animationend=\"nudging = false\"\n >\n <header v-if=\"title || description || $slots.header\" class=\"j-sheet__header\">\n <slot name=\"header\" :title-id=\"titleId\" :description-id=\"descriptionId\">\n <h2 :id=\"titleId\" class=\"j-sheet__title\">\n {{ title }}\n </h2>\n <p v-if=\"description\" :id=\"descriptionId\" class=\"j-sheet__description\">\n {{ description }}\n </p>\n </slot>\n </header>\n <div class=\"j-sheet__body\">\n <slot :close=\"close\" />\n </div>\n <footer v-if=\"$slots.footer\" class=\"j-sheet__footer\">\n <slot name=\"footer\" :close=\"close\" />\n </footer>\n <button v-if=\"!hideClose\" type=\"button\" class=\"j-sheet__close j-focusable\" aria-label=\"Close\" @click=\"close\">\n <IconX />\n </button>\n </div>\n </div>\n </Transition>\n </JPortal>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sheet__overlay {\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-overlay);\n background: var(--juxt-overlay);\n}\n\n.j-sheet__positioner {\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-modal);\n display: flex;\n padding: var(--juxt-space-2);\n}\n\n.j-sheet__positioner--right {\n justify-content: flex-end;\n}\n\n.j-sheet__positioner--left {\n justify-content: flex-start;\n}\n\n.j-sheet__positioner--bottom {\n align-items: flex-end;\n padding-bottom: max(var(--juxt-space-2), env(safe-area-inset-bottom));\n}\n\n/* A floating panel with a small inset from the edges | calmer than edge-to-edge. */\n.j-sheet {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n border: 1px solid var(--juxt-border);\n border-radius: var(--juxt-radius-lg);\n background: var(--juxt-surface-raised);\n box-shadow: var(--juxt-shadow-lg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n outline: none;\n}\n\n.j-sheet--right,\n.j-sheet--left {\n height: 100%;\n}\n\n.j-sheet--right.j-sheet--sm,\n.j-sheet--left.j-sheet--sm {\n max-width: 20rem;\n}\n\n.j-sheet--right.j-sheet--md,\n.j-sheet--left.j-sheet--md {\n max-width: 25rem;\n}\n\n.j-sheet--right.j-sheet--lg,\n.j-sheet--left.j-sheet--lg {\n max-width: 35rem;\n}\n\n.j-sheet--bottom.j-sheet--sm {\n max-height: 20rem;\n}\n\n.j-sheet--bottom.j-sheet--md {\n max-height: 25rem;\n}\n\n.j-sheet--bottom.j-sheet--lg {\n max-height: min(35rem, calc(100dvh - var(--juxt-space-12)));\n}\n\n.j-sheet__header {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-1-5);\n padding: var(--juxt-space-5) var(--juxt-space-12) var(--juxt-space-4) var(--juxt-space-5);\n border-bottom: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-sheet__header,\n[data-theme='dark'] .j-sheet__header {\n border-bottom-color: var(--juxt-border);\n}\n\n.j-sheet__title {\n margin: 0;\n font-size: var(--juxt-text-lg);\n font-weight: var(--juxt-weight-semibold);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-sheet__description {\n margin: 0;\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-sheet__body {\n flex: 1;\n min-height: 0;\n padding: var(--juxt-space-5);\n overflow-y: auto;\n overscroll-behavior: contain;\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-sheet__footer {\n display: flex;\n justify-content: flex-end;\n gap: var(--juxt-space-2);\n padding: var(--juxt-space-4) var(--juxt-space-5);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-sheet__footer,\n[data-theme='dark'] .j-sheet__footer {\n border-top-color: var(--juxt-border);\n}\n\n.j-sheet__close {\n position: absolute;\n top: var(--juxt-space-3);\n right: var(--juxt-space-3);\n display: grid;\n place-items: center;\n width: var(--juxt-control-sm);\n height: var(--juxt-control-sm);\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg-muted);\n cursor: pointer;\n transition-property: background-color, color, outline-color, outline-offset;\n}\n\n.j-sheet__close:hover {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-sheet__close > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-sheet.is-nudging {\n animation: j-sheet-nudge 220ms var(--juxt-ease-out);\n}\n\n@keyframes j-sheet-nudge {\n 40% {\n transform: scale(1.006);\n }\n}\n\n/* Motion: the backdrop fades; the sheet slides in from its edge. */\n.j-sheet-overlay-enter-active {\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-sheet-overlay-leave-active {\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-in);\n}\n\n.j-sheet-overlay-enter-from,\n.j-sheet-overlay-leave-to {\n opacity: 0;\n}\n\n.j-sheet-enter-active .j-sheet {\n transition: transform 280ms var(--juxt-ease-out);\n}\n\n.j-sheet-leave-active .j-sheet {\n transition: transform var(--juxt-duration-normal) var(--juxt-ease-in);\n}\n\n.j-sheet-enter-from .j-sheet--right,\n.j-sheet-leave-to .j-sheet--right {\n transform: translateX(calc(100% + var(--juxt-space-4)));\n}\n\n.j-sheet-enter-from .j-sheet--left,\n.j-sheet-leave-to .j-sheet--left {\n transform: translateX(calc(-100% - var(--juxt-space-4)));\n}\n\n.j-sheet-enter-from .j-sheet--bottom,\n.j-sheet-leave-to .j-sheet--bottom {\n transform: translateY(calc(100% + var(--juxt-space-4)));\n}\n\n/* Reduced motion: the sheet fades in place instead of travelling. */\n@media (prefers-reduced-motion: reduce) {\n .j-sheet-enter-active .j-sheet,\n .j-sheet-leave-active .j-sheet {\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-standard);\n }\n\n .j-sheet-enter-from .j-sheet,\n .j-sheet-leave-to .j-sheet {\n opacity: 0;\n transform: none !important;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, useId } from 'vue'\nimport { FIELD_SELECTOR, useModal } from '../../composables/useModal'\nimport { IconX } from '../../icons'\nimport JPortal from '../primitives/JPortal.vue'\n\nexport interface SheetProps {\n title?: string\n description?: string\n /** Edge the sheet slides in from. */\n side?: 'right' | 'left' | 'bottom'\n /** Width (or height for `bottom`): 320, 400 or 560px. */\n size?: 'sm' | 'md' | 'lg'\n closeOnOverlay?: boolean\n closeOnEscape?: boolean\n hideClose?: boolean\n /** Accessible label when there is no visible title. */\n ariaLabel?: string\n}\n\nconst props = withDefaults(defineProps<SheetProps>(), {\n side: 'right',\n size: 'md',\n closeOnOverlay: true,\n closeOnEscape: true,\n})\n\nconst open = defineModel<boolean>('open', { default: false })\n\nconst emit = defineEmits<{\n /** After the leave transition finishes. */\n afterClose: []\n}>()\n\ndefineSlots<{\n default?: (props: { close: () => void }) => unknown\n header?: (props: { titleId: string, descriptionId: string }) => unknown\n footer?: (props: { close: () => void }) => unknown\n}>()\n\nconst id = useId()\nconst titleId = `j-${id}-title`\nconst descriptionId = `j-${id}-description`\nconst panel = ref<HTMLElement | null>(null)\n\nfunction close() {\n open.value = false\n}\n\nconst { nudging, onBackdropPointerDown, onBackdropClick } = useModal(panel, open, {\n closeOnEscape: () => props.closeOnEscape,\n closeOnOverlay: () => props.closeOnOverlay,\n close,\n focusSelector: `.j-sheet__body ${FIELD_SELECTOR}`,\n})\n\ndefineExpose({ close })\n</script>\n\n<template>\n <JPortal>\n <Transition name=\"j-sheet-overlay\">\n <div v-if=\"open\" class=\"j-sheet__overlay\" aria-hidden=\"true\" />\n </Transition>\n <Transition name=\"j-sheet\" :duration=\"{ enter: 280, leave: 180 }\" @after-leave=\"emit('afterClose')\">\n <div\n v-if=\"open\"\n class=\"j-sheet__positioner\"\n :class=\"`j-sheet__positioner--${side}`\"\n @pointerdown=\"onBackdropPointerDown\"\n @click=\"onBackdropClick\"\n >\n <div\n ref=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n class=\"j-sheet\"\n :class=\"[`j-sheet--${side}`, `j-sheet--${size}`, { 'is-nudging': nudging }]\"\n :aria-labelledby=\"title || $slots.header ? titleId : undefined\"\n :aria-label=\"ariaLabel\"\n :aria-describedby=\"description ? descriptionId : undefined\"\n @animationend=\"nudging = false\"\n >\n <header v-if=\"title || description || $slots.header\" class=\"j-sheet__header\">\n <slot name=\"header\" :title-id=\"titleId\" :description-id=\"descriptionId\">\n <h2 :id=\"titleId\" class=\"j-sheet__title\">\n {{ title }}\n </h2>\n <p v-if=\"description\" :id=\"descriptionId\" class=\"j-sheet__description\">\n {{ description }}\n </p>\n </slot>\n </header>\n <div class=\"j-sheet__body\">\n <slot :close=\"close\" />\n </div>\n <footer v-if=\"$slots.footer\" class=\"j-sheet__footer\">\n <slot name=\"footer\" :close=\"close\" />\n </footer>\n <button v-if=\"!hideClose\" type=\"button\" class=\"j-sheet__close j-focusable\" aria-label=\"Close\" @click=\"close\">\n <IconX />\n </button>\n </div>\n </div>\n </Transition>\n </JPortal>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sheet__overlay {\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-overlay);\n background: var(--juxt-overlay);\n}\n\n.j-sheet__positioner {\n position: fixed;\n inset: 0;\n z-index: var(--juxt-z-modal);\n display: flex;\n padding: var(--juxt-space-2);\n}\n\n.j-sheet__positioner--right {\n justify-content: flex-end;\n}\n\n.j-sheet__positioner--left {\n justify-content: flex-start;\n}\n\n.j-sheet__positioner--bottom {\n align-items: flex-end;\n padding-bottom: max(var(--juxt-space-2), env(safe-area-inset-bottom));\n}\n\n/* A floating panel with a small inset from the edges | calmer than edge-to-edge. */\n.j-sheet {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n border: 1px solid var(--juxt-border);\n border-radius: var(--juxt-radius-lg);\n background: var(--juxt-surface-raised);\n box-shadow: var(--juxt-shadow-lg);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n outline: none;\n}\n\n.j-sheet--right,\n.j-sheet--left {\n height: 100%;\n}\n\n.j-sheet--right.j-sheet--sm,\n.j-sheet--left.j-sheet--sm {\n max-width: 20rem;\n}\n\n.j-sheet--right.j-sheet--md,\n.j-sheet--left.j-sheet--md {\n max-width: 25rem;\n}\n\n.j-sheet--right.j-sheet--lg,\n.j-sheet--left.j-sheet--lg {\n max-width: 35rem;\n}\n\n.j-sheet--bottom.j-sheet--sm {\n max-height: 20rem;\n}\n\n.j-sheet--bottom.j-sheet--md {\n max-height: 25rem;\n}\n\n.j-sheet--bottom.j-sheet--lg {\n max-height: min(35rem, calc(100dvh - var(--juxt-space-12)));\n}\n\n.j-sheet__header {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-1-5);\n padding: var(--juxt-space-5) var(--juxt-space-12) var(--juxt-space-4) var(--juxt-space-5);\n border-bottom: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-sheet__header,\n[data-theme='dark'] .j-sheet__header {\n border-bottom-color: var(--juxt-border);\n}\n\n.j-sheet__title {\n margin: 0;\n font-size: var(--juxt-text-lg);\n font-weight: var(--juxt-weight-semibold);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-sheet__description {\n margin: 0;\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-sheet__body {\n flex: 1;\n min-height: 0;\n padding: var(--juxt-space-5);\n overflow-y: auto;\n overscroll-behavior: contain;\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-sheet__footer {\n display: flex;\n justify-content: flex-end;\n gap: var(--juxt-space-2);\n padding: var(--juxt-space-4) var(--juxt-space-5);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-sheet__footer,\n[data-theme='dark'] .j-sheet__footer {\n border-top-color: var(--juxt-border);\n}\n\n.j-sheet__close {\n position: absolute;\n top: var(--juxt-space-3);\n right: var(--juxt-space-3);\n display: grid;\n place-items: center;\n width: var(--juxt-control-sm);\n height: var(--juxt-control-sm);\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg-muted);\n cursor: pointer;\n transition-property: background-color, color, outline-color, outline-offset;\n}\n\n.j-sheet__close:hover {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-sheet__close > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-sheet.is-nudging {\n animation: j-sheet-nudge 220ms var(--juxt-ease-out);\n}\n\n@keyframes j-sheet-nudge {\n 40% {\n transform: scale(1.006);\n }\n}\n\n/* Motion: the backdrop fades; the sheet slides in from its edge. */\n.j-sheet-overlay-enter-active {\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-sheet-overlay-leave-active {\n transition: opacity var(--juxt-duration-fast) var(--juxt-ease-in);\n}\n\n.j-sheet-overlay-enter-from,\n.j-sheet-overlay-leave-to {\n opacity: 0;\n}\n\n.j-sheet-enter-active .j-sheet {\n transition: transform 280ms var(--juxt-ease-out);\n}\n\n.j-sheet-leave-active .j-sheet {\n transition: transform var(--juxt-duration-normal) var(--juxt-ease-in);\n}\n\n.j-sheet-enter-from .j-sheet--right,\n.j-sheet-leave-to .j-sheet--right {\n transform: translateX(calc(100% + var(--juxt-space-4)));\n}\n\n.j-sheet-enter-from .j-sheet--left,\n.j-sheet-leave-to .j-sheet--left {\n transform: translateX(calc(-100% - var(--juxt-space-4)));\n}\n\n.j-sheet-enter-from .j-sheet--bottom,\n.j-sheet-leave-to .j-sheet--bottom {\n transform: translateY(calc(100% + var(--juxt-space-4)));\n}\n\n/* Reduced motion: the sheet fades in place instead of travelling. */\n@media (prefers-reduced-motion: reduce) {\n .j-sheet-enter-active .j-sheet,\n .j-sheet-leave-active .j-sheet {\n transition: opacity var(--juxt-duration-normal) var(--juxt-ease-standard);\n }\n\n .j-sheet-enter-from .j-sheet,\n .j-sheet-leave-to .j-sheet {\n opacity: 0;\n transform: none !important;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport interface SidebarProps {\n /** Accessible name of the navigation landmark. */\n label?: string\n}\n\nwithDefaults(defineProps<SidebarProps>(), { label: 'Sidebar' })\n\ndefineSlots<{\n /** Top of the sidebar: logo, workspace switcher. */\n header?: () => unknown\n /** Items and groups. */\n default?: () => unknown\n /** Pinned to the bottom: account, settings, help. */\n footer?: () => unknown\n}>()\n</script>\n\n<template>\n <aside class=\"j-sidebar\">\n <div v-if=\"$slots.header\" class=\"j-sidebar__header\">\n <slot name=\"header\" />\n </div>\n <nav class=\"j-sidebar__nav\" :aria-label=\"label\">\n <slot />\n </nav>\n <div v-if=\"$slots.footer\" class=\"j-sidebar__footer\">\n <slot name=\"footer\" />\n </div>\n </aside>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar {\n display: flex;\n flex-direction: column;\n width: 100%;\n /*\n * Not `height: 100%`: inside a parent that only has a min-height (e.g. a\n * `min-h-screen` flex row) a percentage height can't resolve, and a set\n * height also stops the flex/grid parent from stretching the sidebar, so it\n * would collapse to its content. Min and max of 100% pin it to a parent with\n * a real height (nav scrolls inside), and leave it `auto` everywhere else, so\n * the parent stretches it to full height and the footer stays at the bottom.\n */\n height: auto;\n min-height: 100%;\n max-height: 100%;\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-sidebar__header {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n padding: var(--juxt-space-3) var(--juxt-space-3) var(--juxt-space-2);\n}\n\n.j-sidebar__nav {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--juxt-space-4);\n min-height: 0;\n padding: var(--juxt-space-2);\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n.j-sidebar__footer {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n padding: var(--juxt-space-2);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-sidebar__footer,\n[data-theme='dark'] .j-sidebar__footer {\n border-top-color: var(--juxt-border);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport interface SidebarProps {\n /** Accessible name of the navigation landmark. */\n label?: string\n}\n\nwithDefaults(defineProps<SidebarProps>(), { label: 'Sidebar' })\n\ndefineSlots<{\n /** Top of the sidebar: logo, workspace switcher. */\n header?: () => unknown\n /** Items and groups. */\n default?: () => unknown\n /** Pinned to the bottom: account, settings, help. */\n footer?: () => unknown\n}>()\n</script>\n\n<template>\n <aside class=\"j-sidebar\">\n <div v-if=\"$slots.header\" class=\"j-sidebar__header\">\n <slot name=\"header\" />\n </div>\n <nav class=\"j-sidebar__nav\" :aria-label=\"label\">\n <slot />\n </nav>\n <div v-if=\"$slots.footer\" class=\"j-sidebar__footer\">\n <slot name=\"footer\" />\n </div>\n </aside>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar {\n display: flex;\n flex-direction: column;\n width: 100%;\n /*\n * Not `height: 100%`: inside a parent that only has a min-height (e.g. a\n * `min-h-screen` flex row) a percentage height can't resolve, and a set\n * height also stops the flex/grid parent from stretching the sidebar, so it\n * would collapse to its content. Min and max of 100% pin it to a parent with\n * a real height (nav scrolls inside), and leave it `auto` everywhere else, so\n * the parent stretches it to full height and the footer stays at the bottom.\n */\n height: auto;\n min-height: 100%;\n max-height: 100%;\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n}\n\n.j-sidebar__header {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n padding: var(--juxt-space-3) var(--juxt-space-3) var(--juxt-space-2);\n}\n\n.j-sidebar__nav {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--juxt-space-4);\n min-height: 0;\n padding: var(--juxt-space-2);\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n.j-sidebar__footer {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n padding: var(--juxt-space-2);\n border-top: 1px solid var(--juxt-border-subtle);\n}\n\n.dark .j-sidebar__footer,\n[data-theme='dark'] .j-sidebar__footer {\n border-top-color: var(--juxt-border);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useId } from 'vue'\nimport { IconChevronDown } from '../../icons'\n\nexport interface SidebarGroupProps {\n title?: string\n /** Let the title fold the group away. */\n collapsible?: boolean\n}\n\nwithDefaults(defineProps<SidebarGroupProps>(), { collapsible: true })\n\n/** Whether the group is expanded. Open by default. */\nconst open = defineModel<boolean>('open', { default: true })\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst id = `j-${useId()}`\nconst titleId = `${id}-title`\nconst listId = `${id}-list`\n</script>\n\n<template>\n <div class=\"j-sidebar-group\" :data-state=\"open ? 'open' : 'closed'\">\n <button\n v-if=\"title && collapsible\"\n :id=\"titleId\"\n type=\"button\"\n class=\"j-sidebar-group__title j-sidebar-group__toggle j-focusable\"\n :aria-expanded=\"open\"\n :aria-controls=\"listId\"\n @click=\"open = !open\"\n >\n <span>{{ title }}</span>\n <IconChevronDown class=\"j-sidebar-group__chevron\" />\n </button>\n <p v-else-if=\"title\" :id=\"titleId\" class=\"j-sidebar-group__title\">\n {{ title }}\n </p>\n <div\n :id=\"listId\"\n class=\"j-sidebar-group__panel\"\n role=\"group\"\n :aria-labelledby=\"title ? titleId : undefined\"\n :inert=\"!open\"\n >\n <div class=\"j-sidebar-group__panel-inner\">\n <div class=\"j-sidebar-group__items\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar-group {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n.j-sidebar-group__title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--juxt-space-2);\n margin: 0;\n padding: var(--juxt-space-1) var(--juxt-space-2);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n line-height: var(--juxt-leading-snug);\n}\n\n.j-sidebar-group__toggle {\n width: 100%;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n font-family: inherit;\n text-align: left;\n cursor: pointer;\n transition-property: color, outline-color, outline-offset;\n}\n\n.j-sidebar-group__toggle:hover {\n color: var(--juxt-fg-secondary);\n}\n\n.j-sidebar-group__chevron {\n flex: none;\n width: 0.75rem;\n height: 0.75rem;\n opacity: 0;\n transform: rotate(-90deg);\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n/* The chevron only appears on hover or focus, or while the group is folded. */\n.j-sidebar-group__toggle:hover .j-sidebar-group__chevron,\n.j-sidebar-group__toggle:focus-visible .j-sidebar-group__chevron,\n.j-sidebar-group[data-state='closed'] .j-sidebar-group__chevron {\n opacity: 1;\n}\n\n.j-sidebar-group[data-state='open'] .j-sidebar-group__chevron {\n transform: none;\n}\n\n.j-sidebar-group__panel {\n display: grid;\n grid-template-rows: 1fr;\n transition: grid-template-rows var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-sidebar-group[data-state='closed'] .j-sidebar-group__panel {\n grid-template-rows: 0fr;\n}\n\n.j-sidebar-group__panel-inner {\n min-height: 0;\n overflow: hidden;\n}\n\n.j-sidebar-group__items {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-sidebar-group__panel,\n .j-sidebar-group__chevron {\n transition: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useId } from 'vue'\nimport { IconChevronDown } from '../../icons'\n\nexport interface SidebarGroupProps {\n title?: string\n /** Let the title fold the group away. */\n collapsible?: boolean\n}\n\nwithDefaults(defineProps<SidebarGroupProps>(), { collapsible: true })\n\n/** Whether the group is expanded. Open by default. */\nconst open = defineModel<boolean>('open', { default: true })\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst id = `j-${useId()}`\nconst titleId = `${id}-title`\nconst listId = `${id}-list`\n</script>\n\n<template>\n <div class=\"j-sidebar-group\" :data-state=\"open ? 'open' : 'closed'\">\n <button\n v-if=\"title && collapsible\"\n :id=\"titleId\"\n type=\"button\"\n class=\"j-sidebar-group__title j-sidebar-group__toggle j-focusable\"\n :aria-expanded=\"open\"\n :aria-controls=\"listId\"\n @click=\"open = !open\"\n >\n <span>{{ title }}</span>\n <IconChevronDown class=\"j-sidebar-group__chevron\" />\n </button>\n <p v-else-if=\"title\" :id=\"titleId\" class=\"j-sidebar-group__title\">\n {{ title }}\n </p>\n <div\n :id=\"listId\"\n class=\"j-sidebar-group__panel\"\n role=\"group\"\n :aria-labelledby=\"title ? titleId : undefined\"\n :inert=\"!open\"\n >\n <div class=\"j-sidebar-group__panel-inner\">\n <div class=\"j-sidebar-group__items\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar-group {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n.j-sidebar-group__title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--juxt-space-2);\n margin: 0;\n padding: var(--juxt-space-1) var(--juxt-space-2);\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n line-height: var(--juxt-leading-snug);\n}\n\n.j-sidebar-group__toggle {\n width: 100%;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n font-family: inherit;\n text-align: left;\n cursor: pointer;\n transition-property: color, outline-color, outline-offset;\n}\n\n.j-sidebar-group__toggle:hover {\n color: var(--juxt-fg-secondary);\n}\n\n.j-sidebar-group__chevron {\n flex: none;\n width: 0.75rem;\n height: 0.75rem;\n opacity: 0;\n transform: rotate(-90deg);\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n/* The chevron only appears on hover or focus, or while the group is folded. */\n.j-sidebar-group__toggle:hover .j-sidebar-group__chevron,\n.j-sidebar-group__toggle:focus-visible .j-sidebar-group__chevron,\n.j-sidebar-group[data-state='closed'] .j-sidebar-group__chevron {\n opacity: 1;\n}\n\n.j-sidebar-group[data-state='open'] .j-sidebar-group__chevron {\n transform: none;\n}\n\n.j-sidebar-group__panel {\n display: grid;\n grid-template-rows: 1fr;\n transition: grid-template-rows var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-sidebar-group[data-state='closed'] .j-sidebar-group__panel {\n grid-template-rows: 0fr;\n}\n\n.j-sidebar-group__panel-inner {\n min-height: 0;\n overflow: hidden;\n}\n\n.j-sidebar-group__items {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-sidebar-group__panel,\n .j-sidebar-group__chevron {\n transition: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface SidebarItemProps {\n /** Marks the current page: styled and announced with `aria-current=\"page\"`. */\n active?: boolean\n disabled?: boolean\n /** Render as another element or component, e.g. `NuxtLink`. */\n as?: string | Component\n}\n\nwithDefaults(defineProps<SidebarItemProps>(), { as: 'a' })\n\ndefineSlots<{\n default?: () => unknown\n /** Icon before the label. */\n leading?: () => unknown\n /** After the label: a count, badge or shortcut. */\n trailing?: () => unknown\n}>()\n</script>\n\n<template>\n <component\n :is=\"as\"\n class=\"j-sidebar-item j-focusable\"\n :class=\"{ 'is-active': active, 'is-disabled': disabled }\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n >\n <span v-if=\"$slots.leading\" class=\"j-sidebar-item__icon\"><slot name=\"leading\" /></span>\n <span class=\"j-sidebar-item__label\"><slot /></span>\n <span v-if=\"$slots.trailing\" class=\"j-sidebar-item__trailing\"><slot name=\"trailing\" /></span>\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar-item {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n min-height: var(--juxt-control-sm);\n padding: 0 var(--juxt-space-2);\n border-radius: var(--juxt-radius-sm);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n letter-spacing: var(--juxt-tracking-tight);\n text-decoration: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: color, background-color, outline-color, outline-offset;\n}\n\n.j-sidebar-item:hover:not(.is-disabled) {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-sidebar-item.is-active {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n font-weight: var(--juxt-weight-medium);\n}\n\n/* Same marker as the command palette: a short accent line where you are. */\n.j-sidebar-item.is-active::before {\n content: '';\n position: absolute;\n top: 0.4375rem;\n bottom: 0.4375rem;\n left: 0;\n width: 2px;\n border-radius: 0 2px 2px 0;\n background: var(--juxt-accent);\n}\n\n.j-sidebar-item.is-disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-sidebar-item__icon {\n display: inline-flex;\n flex: none;\n color: var(--juxt-fg-muted);\n}\n\n.j-sidebar-item.is-active .j-sidebar-item__icon,\n.j-sidebar-item:hover:not(.is-disabled) .j-sidebar-item__icon {\n color: var(--juxt-fg);\n}\n\n.j-sidebar-item__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-sidebar-item__label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.j-sidebar-item__trailing {\n display: inline-flex;\n flex: none;\n align-items: center;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-variant-numeric: tabular-nums;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface SidebarItemProps {\n /** Marks the current page: styled and announced with `aria-current=\"page\"`. */\n active?: boolean\n disabled?: boolean\n /** Render as another element or component, e.g. `NuxtLink`. */\n as?: string | Component\n}\n\nwithDefaults(defineProps<SidebarItemProps>(), { as: 'a' })\n\ndefineSlots<{\n default?: () => unknown\n /** Icon before the label. */\n leading?: () => unknown\n /** After the label: a count, badge or shortcut. */\n trailing?: () => unknown\n}>()\n</script>\n\n<template>\n <component\n :is=\"as\"\n class=\"j-sidebar-item j-focusable\"\n :class=\"{ 'is-active': active, 'is-disabled': disabled }\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n >\n <span v-if=\"$slots.leading\" class=\"j-sidebar-item__icon\"><slot name=\"leading\" /></span>\n <span class=\"j-sidebar-item__label\"><slot /></span>\n <span v-if=\"$slots.trailing\" class=\"j-sidebar-item__trailing\"><slot name=\"trailing\" /></span>\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-sidebar-item {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--juxt-space-2);\n min-height: var(--juxt-control-sm);\n padding: 0 var(--juxt-space-2);\n border-radius: var(--juxt-radius-sm);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n letter-spacing: var(--juxt-tracking-tight);\n text-decoration: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition-property: color, background-color, outline-color, outline-offset;\n}\n\n.j-sidebar-item:hover:not(.is-disabled) {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-sidebar-item.is-active {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n font-weight: var(--juxt-weight-medium);\n}\n\n/* Same marker as the command palette: a short accent line where you are. */\n.j-sidebar-item.is-active::before {\n content: '';\n position: absolute;\n top: 0.4375rem;\n bottom: 0.4375rem;\n left: 0;\n width: 2px;\n border-radius: 0 2px 2px 0;\n background: var(--juxt-accent);\n}\n\n.j-sidebar-item.is-disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-sidebar-item__icon {\n display: inline-flex;\n flex: none;\n color: var(--juxt-fg-muted);\n}\n\n.j-sidebar-item.is-active .j-sidebar-item__icon,\n.j-sidebar-item:hover:not(.is-disabled) .j-sidebar-item__icon {\n color: var(--juxt-fg);\n}\n\n.j-sidebar-item__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-sidebar-item__label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.j-sidebar-item__trailing {\n display: inline-flex;\n flex: none;\n align-items: center;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-variant-numeric: tabular-nums;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nexport interface SkeletonProps {\n /** `text` renders one or more lines; `circle` suits avatars. */\n shape?: 'rect' | 'text' | 'circle'\n /** Number of lines for `text`. The last one is shorter, like a real paragraph. */\n lines?: number\n /** Any CSS length, e.g. `12rem` or `100%`. */\n width?: string\n height?: string\n}\n\nconst props = withDefaults(defineProps<SkeletonProps>(), { shape: 'rect', lines: 1 })\n\nconst style = computed(() => ({ width: props.width, height: props.height }))\n</script>\n\n<template>\n <span v-if=\"shape === 'text'\" class=\"j-skeleton-lines\" :style=\"{ width }\" aria-hidden=\"true\">\n <span\n v-for=\"i in lines\"\n :key=\"i\"\n class=\"j-skeleton j-skeleton--text\"\n :style=\"{ width: i === lines && lines > 1 ? '62%' : undefined, height }\"\n />\n </span>\n <span v-else class=\"j-skeleton\" :class=\"`j-skeleton--${shape}`\" :style=\"style\" aria-hidden=\"true\" />\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-skeleton {\n display: block;\n width: 100%;\n height: 1rem;\n border-radius: var(--juxt-radius-sm);\n background: var(--juxt-surface-active);\n animation: j-skeleton-pulse 1.8s var(--juxt-ease-standard) infinite;\n}\n\n.j-skeleton--circle {\n width: 2rem;\n height: 2rem;\n border-radius: var(--juxt-radius-full);\n}\n\n.j-skeleton--text {\n height: 0.75rem;\n border-radius: var(--juxt-radius-xs);\n}\n\n.j-skeleton-lines {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-2);\n width: 100%;\n}\n\n/* A slow breath, not a shimmer | loading shouldn't compete for attention. */\n@keyframes j-skeleton-pulse {\n 50% {\n opacity: 0.5;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-skeleton {\n animation: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nexport interface SkeletonProps {\n /** `text` renders one or more lines; `circle` suits avatars. */\n shape?: 'rect' | 'text' | 'circle'\n /** Number of lines for `text`. The last one is shorter, like a real paragraph. */\n lines?: number\n /** Any CSS length, e.g. `12rem` or `100%`. */\n width?: string\n height?: string\n}\n\nconst props = withDefaults(defineProps<SkeletonProps>(), { shape: 'rect', lines: 1 })\n\nconst style = computed(() => ({ width: props.width, height: props.height }))\n</script>\n\n<template>\n <span v-if=\"shape === 'text'\" class=\"j-skeleton-lines\" :style=\"{ width }\" aria-hidden=\"true\">\n <span\n v-for=\"i in lines\"\n :key=\"i\"\n class=\"j-skeleton j-skeleton--text\"\n :style=\"{ width: i === lines && lines > 1 ? '62%' : undefined, height }\"\n />\n </span>\n <span v-else class=\"j-skeleton\" :class=\"`j-skeleton--${shape}`\" :style=\"style\" aria-hidden=\"true\" />\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-skeleton {\n display: block;\n width: 100%;\n height: 1rem;\n border-radius: var(--juxt-radius-sm);\n background: var(--juxt-surface-active);\n animation: j-skeleton-pulse 1.8s var(--juxt-ease-standard) infinite;\n}\n\n.j-skeleton--circle {\n width: 2rem;\n height: 2rem;\n border-radius: var(--juxt-radius-full);\n}\n\n.j-skeleton--text {\n height: 0.75rem;\n border-radius: var(--juxt-radius-xs);\n}\n\n.j-skeleton-lines {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-2);\n width: 100%;\n}\n\n/* A slow breath, not a shimmer | loading shouldn't compete for attention. */\n@keyframes j-skeleton-pulse {\n 50% {\n opacity: 0.5;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-skeleton {\n animation: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nexport interface SliderProps {\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n /** Accessible name for the thumb(s) when no visible label is associated. */\n label?: string\n size?: 'sm' | 'md'\n /** Show the current value in a floating bubble above the active thumb. */\n showValue?: boolean\n /** Format the displayed and announced value. */\n format?: (value: number) => string\n}\n\nconst props = withDefaults(defineProps<SliderProps>(), {\n min: 0,\n max: 100,\n step: 1,\n size: 'md',\n})\n\n/** A single value, or a `[low, high]` tuple for a range slider. */\nconst model = defineModel<number | [number, number]>({ default: 0 })\n\nconst track = ref<HTMLElement | null>(null)\nconst activeThumb = ref<number | null>(null)\n\nconst isRange = computed(() => Array.isArray(model.value))\nconst values = computed<number[]>(() => (Array.isArray(model.value) ? model.value : [model.value as number]))\n\nfunction clamp(value: number) {\n const stepped = Math.round((value - props.min) / props.step) * props.step + props.min\n const rounded = Number(stepped.toFixed(6))\n return Math.min(props.max, Math.max(props.min, rounded))\n}\n\nfunction setValue(index: number, raw: number) {\n if (props.disabled) return\n const next = [...values.value]\n let value = clamp(raw)\n if (isRange.value) {\n const lower = index === 0 ? props.min : next[index - 1]!\n const upper = index === next.length - 1 ? props.max : next[index + 1]!\n value = Math.min(upper, Math.max(lower, value))\n }\n next[index] = value\n model.value = isRange.value ? ([next[0]!, next[1]!] as [number, number]) : next[0]!\n}\n\nfunction percent(value: number) {\n if (props.max === props.min) return 0\n return ((value - props.min) / (props.max - props.min)) * 100\n}\n\nfunction valueFromClientX(clientX: number): number {\n const rect = track.value!.getBoundingClientRect()\n const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width))\n return props.min + ratio * (props.max - props.min)\n}\n\nfunction nearestThumb(value: number) {\n let nearest = 0\n let best = Infinity\n values.value.forEach((v, i) => {\n const dist = Math.abs(v - value)\n if (dist < best) {\n best = dist\n nearest = i\n }\n })\n return nearest\n}\n\nfunction onPointerMove(event: PointerEvent) {\n if (activeThumb.value === null) return\n setValue(activeThumb.value, valueFromClientX(event.clientX))\n}\n\nfunction onPointerUp() {\n activeThumb.value = null\n window.removeEventListener('pointermove', onPointerMove)\n window.removeEventListener('pointerup', onPointerUp)\n}\n\nfunction startDrag(index: number) {\n activeThumb.value = index\n window.addEventListener('pointermove', onPointerMove)\n window.addEventListener('pointerup', onPointerUp)\n}\n\nfunction onTrackPointerDown(event: PointerEvent) {\n if (props.disabled) return\n const value = valueFromClientX(event.clientX)\n const index = nearestThumb(value)\n setValue(index, value)\n startDrag(index)\n}\n\nfunction onThumbPointerDown(index: number, event: PointerEvent) {\n if (props.disabled) return\n event.stopPropagation()\n ;(event.currentTarget as HTMLElement).focus()\n startDrag(index)\n}\n\nfunction onKeydown(index: number, event: KeyboardEvent) {\n if (props.disabled) return\n const big = props.step * 10 || (props.max - props.min) / 10\n const current = values.value[index]!\n let next: number | undefined\n if (event.key === 'ArrowRight' || event.key === 'ArrowUp') next = current + props.step\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') next = current - props.step\n else if (event.key === 'PageUp') next = current + big\n else if (event.key === 'PageDown') next = current - big\n else if (event.key === 'Home') next = props.min\n else if (event.key === 'End') next = props.max\n if (next === undefined) return\n event.preventDefault()\n setValue(index, next)\n}\n\nfunction display(value: number) {\n return props.format ? props.format(value) : String(Math.round(value * 100) / 100)\n}\n\nconst fillStyle = computed(() => {\n if (isRange.value) {\n const [a, b] = values.value\n return { left: `${percent(a!)}%`, width: `${Math.max(0, percent(b!) - percent(a!))}%` }\n }\n return { left: '0%', width: `${percent(values.value[0]!)}%` }\n})\n</script>\n\n<template>\n <div class=\"j-slider\" :class=\"[`j-slider--${size}`, { 'is-disabled': disabled }]\">\n <div ref=\"track\" class=\"j-slider__track\" @pointerdown=\"onTrackPointerDown\">\n <span class=\"j-slider__range\" :style=\"fillStyle\" />\n <span\n v-for=\"(value, index) in values\"\n :key=\"index\"\n class=\"j-slider__thumb j-focusable\"\n :class=\"{ 'is-dragging': activeThumb === index }\"\n role=\"slider\"\n :tabindex=\"disabled ? -1 : 0\"\n :aria-label=\"label\"\n :aria-orientation=\"'horizontal'\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuenow=\"value\"\n :aria-valuetext=\"display(value)\"\n :aria-disabled=\"disabled || undefined\"\n :style=\"{ left: `${percent(value)}%` }\"\n @pointerdown=\"onThumbPointerDown(index, $event)\"\n @keydown=\"onKeydown(index, $event)\"\n >\n <span v-if=\"showValue\" class=\"j-slider__bubble\">{{ display(value) }}</span>\n </span>\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-slider {\n --j-slider-track: 0.25rem;\n --j-slider-thumb: 1rem;\n\n display: flex;\n align-items: center;\n width: 100%;\n padding: calc(var(--j-slider-thumb) / 2) 0;\n font-family: var(--juxt-font-sans);\n touch-action: none;\n}\n\n.j-slider--sm {\n --j-slider-track: 0.1875rem;\n --j-slider-thumb: 0.875rem;\n}\n\n.j-slider__track {\n position: relative;\n width: 100%;\n height: var(--j-slider-track);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-surface-active);\n cursor: pointer;\n}\n\n.j-slider.is-disabled .j-slider__track {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.j-slider__range {\n position: absolute;\n top: 0;\n bottom: 0;\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-accent);\n}\n\n.j-slider__thumb {\n position: absolute;\n top: 50%;\n width: var(--j-slider-thumb);\n height: var(--j-slider-thumb);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-surface);\n box-shadow: 0 0 0 1px var(--juxt-border-strong), var(--juxt-shadow-sm);\n cursor: grab;\n transform: translate(-50%, -50%);\n transition:\n box-shadow var(--juxt-duration-fast) var(--juxt-ease-out),\n left var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-slider__thumb.is-dragging {\n cursor: grabbing;\n transition: none;\n}\n\n.j-slider__thumb:hover {\n box-shadow: 0 0 0 1px var(--juxt-accent), var(--juxt-shadow-sm);\n}\n\n.j-slider__thumb:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 3px;\n}\n\n.j-slider.is-disabled .j-slider__thumb {\n cursor: not-allowed;\n}\n\n.j-slider__bubble {\n position: absolute;\n bottom: calc(100% + var(--juxt-space-2));\n left: 50%;\n padding: var(--juxt-space-0-5) var(--juxt-space-1-5);\n border-radius: var(--juxt-radius-xs);\n background: var(--juxt-tooltip-bg);\n color: var(--juxt-tooltip-fg);\n font-size: var(--juxt-text-2xs);\n font-weight: var(--juxt-weight-medium);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n pointer-events: none;\n transform: translateX(-50%);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-slider__thumb {\n transition: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nexport interface SliderProps {\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n /** Accessible name for the thumb(s) when no visible label is associated. */\n label?: string\n size?: 'sm' | 'md'\n /** Show the current value in a floating bubble above the active thumb. */\n showValue?: boolean\n /** Format the displayed and announced value. */\n format?: (value: number) => string\n}\n\nconst props = withDefaults(defineProps<SliderProps>(), {\n min: 0,\n max: 100,\n step: 1,\n size: 'md',\n})\n\n/** A single value, or a `[low, high]` tuple for a range slider. */\nconst model = defineModel<number | [number, number]>({ default: 0 })\n\nconst track = ref<HTMLElement | null>(null)\nconst activeThumb = ref<number | null>(null)\n\nconst isRange = computed(() => Array.isArray(model.value))\nconst values = computed<number[]>(() => (Array.isArray(model.value) ? model.value : [model.value as number]))\n\nfunction clamp(value: number) {\n const stepped = Math.round((value - props.min) / props.step) * props.step + props.min\n const rounded = Number(stepped.toFixed(6))\n return Math.min(props.max, Math.max(props.min, rounded))\n}\n\nfunction setValue(index: number, raw: number) {\n if (props.disabled) return\n const next = [...values.value]\n let value = clamp(raw)\n if (isRange.value) {\n const lower = index === 0 ? props.min : next[index - 1]!\n const upper = index === next.length - 1 ? props.max : next[index + 1]!\n value = Math.min(upper, Math.max(lower, value))\n }\n next[index] = value\n model.value = isRange.value ? ([next[0]!, next[1]!] as [number, number]) : next[0]!\n}\n\nfunction percent(value: number) {\n if (props.max === props.min) return 0\n return ((value - props.min) / (props.max - props.min)) * 100\n}\n\nfunction valueFromClientX(clientX: number): number {\n const rect = track.value!.getBoundingClientRect()\n const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width))\n return props.min + ratio * (props.max - props.min)\n}\n\nfunction nearestThumb(value: number) {\n let nearest = 0\n let best = Infinity\n values.value.forEach((v, i) => {\n const dist = Math.abs(v - value)\n if (dist < best) {\n best = dist\n nearest = i\n }\n })\n return nearest\n}\n\nfunction onPointerMove(event: PointerEvent) {\n if (activeThumb.value === null) return\n setValue(activeThumb.value, valueFromClientX(event.clientX))\n}\n\nfunction onPointerUp() {\n activeThumb.value = null\n window.removeEventListener('pointermove', onPointerMove)\n window.removeEventListener('pointerup', onPointerUp)\n}\n\nfunction startDrag(index: number) {\n activeThumb.value = index\n window.addEventListener('pointermove', onPointerMove)\n window.addEventListener('pointerup', onPointerUp)\n}\n\nfunction onTrackPointerDown(event: PointerEvent) {\n if (props.disabled) return\n const value = valueFromClientX(event.clientX)\n const index = nearestThumb(value)\n setValue(index, value)\n startDrag(index)\n}\n\nfunction onThumbPointerDown(index: number, event: PointerEvent) {\n if (props.disabled) return\n event.stopPropagation()\n ;(event.currentTarget as HTMLElement).focus()\n startDrag(index)\n}\n\nfunction onKeydown(index: number, event: KeyboardEvent) {\n if (props.disabled) return\n const big = props.step * 10 || (props.max - props.min) / 10\n const current = values.value[index]!\n let next: number | undefined\n if (event.key === 'ArrowRight' || event.key === 'ArrowUp') next = current + props.step\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') next = current - props.step\n else if (event.key === 'PageUp') next = current + big\n else if (event.key === 'PageDown') next = current - big\n else if (event.key === 'Home') next = props.min\n else if (event.key === 'End') next = props.max\n if (next === undefined) return\n event.preventDefault()\n setValue(index, next)\n}\n\nfunction display(value: number) {\n return props.format ? props.format(value) : String(Math.round(value * 100) / 100)\n}\n\nconst fillStyle = computed(() => {\n if (isRange.value) {\n const [a, b] = values.value\n return { left: `${percent(a!)}%`, width: `${Math.max(0, percent(b!) - percent(a!))}%` }\n }\n return { left: '0%', width: `${percent(values.value[0]!)}%` }\n})\n</script>\n\n<template>\n <div class=\"j-slider\" :class=\"[`j-slider--${size}`, { 'is-disabled': disabled }]\">\n <div ref=\"track\" class=\"j-slider__track\" @pointerdown=\"onTrackPointerDown\">\n <span class=\"j-slider__range\" :style=\"fillStyle\" />\n <span\n v-for=\"(value, index) in values\"\n :key=\"index\"\n class=\"j-slider__thumb j-focusable\"\n :class=\"{ 'is-dragging': activeThumb === index }\"\n role=\"slider\"\n :tabindex=\"disabled ? -1 : 0\"\n :aria-label=\"label\"\n :aria-orientation=\"'horizontal'\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuenow=\"value\"\n :aria-valuetext=\"display(value)\"\n :aria-disabled=\"disabled || undefined\"\n :style=\"{ left: `${percent(value)}%` }\"\n @pointerdown=\"onThumbPointerDown(index, $event)\"\n @keydown=\"onKeydown(index, $event)\"\n >\n <span v-if=\"showValue\" class=\"j-slider__bubble\">{{ display(value) }}</span>\n </span>\n </div>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-slider {\n --j-slider-track: 0.25rem;\n --j-slider-thumb: 1rem;\n\n display: flex;\n align-items: center;\n width: 100%;\n padding: calc(var(--j-slider-thumb) / 2) 0;\n font-family: var(--juxt-font-sans);\n touch-action: none;\n}\n\n.j-slider--sm {\n --j-slider-track: 0.1875rem;\n --j-slider-thumb: 0.875rem;\n}\n\n.j-slider__track {\n position: relative;\n width: 100%;\n height: var(--j-slider-track);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-surface-active);\n cursor: pointer;\n}\n\n.j-slider.is-disabled .j-slider__track {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.j-slider__range {\n position: absolute;\n top: 0;\n bottom: 0;\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-accent);\n}\n\n.j-slider__thumb {\n position: absolute;\n top: 50%;\n width: var(--j-slider-thumb);\n height: var(--j-slider-thumb);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-surface);\n box-shadow: 0 0 0 1px var(--juxt-border-strong), var(--juxt-shadow-sm);\n cursor: grab;\n transform: translate(-50%, -50%);\n transition:\n box-shadow var(--juxt-duration-fast) var(--juxt-ease-out),\n left var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-slider__thumb.is-dragging {\n cursor: grabbing;\n transition: none;\n}\n\n.j-slider__thumb:hover {\n box-shadow: 0 0 0 1px var(--juxt-accent), var(--juxt-shadow-sm);\n}\n\n.j-slider__thumb:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 3px;\n}\n\n.j-slider.is-disabled .j-slider__thumb {\n cursor: not-allowed;\n}\n\n.j-slider__bubble {\n position: absolute;\n bottom: calc(100% + var(--juxt-space-2));\n left: 50%;\n padding: var(--juxt-space-0-5) var(--juxt-space-1-5);\n border-radius: var(--juxt-radius-xs);\n background: var(--juxt-tooltip-bg);\n color: var(--juxt-tooltip-fg);\n font-size: var(--juxt-text-2xs);\n font-weight: var(--juxt-weight-medium);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n pointer-events: none;\n transform: translateX(-50%);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-slider__thumb {\n transition: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { Spinner } from '../../icons'\n\nexport interface SpinnerProps {\n size?: 'sm' | 'md' | 'lg'\n /** Announced to screen readers. Set to an empty string when the context already says it. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<SpinnerProps>(), { size: 'md', label: 'Loading' })\n\nconst pixels = computed(() => ({ sm: 14, md: 16, lg: 20 })[props.size])\n</script>\n\n<template>\n <span class=\"j-spinner-wrap\" :role=\"label ? 'status' : undefined\">\n <Spinner :size=\"pixels\" />\n <span v-if=\"label\" class=\"j-sr-only\">{{ label }}</span>\n </span>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-spinner-wrap {\n display: inline-flex;\n color: var(--juxt-fg-muted);\n vertical-align: middle;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { Spinner } from '../../icons'\n\nexport interface SpinnerProps {\n size?: 'sm' | 'md' | 'lg'\n /** Announced to screen readers. Set to an empty string when the context already says it. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<SpinnerProps>(), { size: 'md', label: 'Loading' })\n\nconst pixels = computed(() => ({ sm: 14, md: 16, lg: 20 })[props.size])\n</script>\n\n<template>\n <span class=\"j-spinner-wrap\" :role=\"label ? 'status' : undefined\">\n <Spinner :size=\"pixels\" />\n <span v-if=\"label\" class=\"j-sr-only\">{{ label }}</span>\n </span>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-spinner-wrap {\n display: inline-flex;\n color: var(--juxt-fg-muted);\n vertical-align: middle;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { IconArrowDownRight, IconArrowUpRight } from '../../icons'\n\nexport type StatTrend = 'up' | 'down' | 'neutral'\n\nexport interface StatProps {\n label: string\n value: string | number\n /** Delta shown next to the value, e.g. `\"+12%\"`. */\n delta?: string\n trend?: StatTrend\n description?: string\n}\n\nconst props = defineProps<StatProps>()\n\ndefineSlots<{\n /** Icon shown next to the label. */\n icon?: () => unknown\n}>()\n\nconst trendIcon = computed(() => (props.trend === 'up' ? IconArrowUpRight : props.trend === 'down' ? IconArrowDownRight : null))\n</script>\n\n<template>\n <div class=\"j-stat\">\n <div class=\"j-stat__header\">\n <span v-if=\"$slots.icon\" class=\"j-stat__icon\" aria-hidden=\"true\"><slot name=\"icon\" /></span>\n <span class=\"j-stat__label\">{{ label }}</span>\n </div>\n <div class=\"j-stat__row\">\n <span class=\"j-stat__value\">{{ value }}</span>\n <span v-if=\"delta\" class=\"j-stat__delta\" :class=\"trend ? `j-stat__delta--${trend}` : undefined\">\n <component :is=\"trendIcon\" v-if=\"trendIcon\" aria-hidden=\"true\" />\n {{ delta }}\n </span>\n </div>\n <p v-if=\"description\" class=\"j-stat__description\">\n {{ description }}\n </p>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-stat {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-1-5);\n font-family: var(--juxt-font-sans);\n}\n\n.j-stat__header {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1-5);\n}\n\n.j-stat__icon {\n display: grid;\n place-items: center;\n color: var(--juxt-fg-muted);\n}\n\n.j-stat__icon > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n.j-stat__label {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-stat__row {\n display: flex;\n align-items: baseline;\n gap: var(--juxt-space-2);\n}\n\n.j-stat__value {\n color: var(--juxt-fg);\n font-size: var(--juxt-text-3xl);\n font-weight: var(--juxt-weight-semibold);\n line-height: var(--juxt-leading-tight);\n letter-spacing: var(--juxt-tracking-tighter);\n font-variant-numeric: tabular-nums;\n}\n\n.j-stat__delta {\n display: inline-flex;\n align-items: center;\n gap: 0.125rem;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n font-variant-numeric: tabular-nums;\n}\n\n.j-stat__delta > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n.j-stat__delta--up {\n color: var(--juxt-success-text);\n}\n\n.j-stat__delta--down {\n color: var(--juxt-danger-text);\n}\n\n.j-stat__description {\n margin: 0;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n line-height: var(--juxt-leading-normal);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { IconArrowDownRight, IconArrowUpRight } from '../../icons'\n\nexport type StatTrend = 'up' | 'down' | 'neutral'\n\nexport interface StatProps {\n label: string\n value: string | number\n /** Delta shown next to the value, e.g. `\"+12%\"`. */\n delta?: string\n trend?: StatTrend\n description?: string\n}\n\nconst props = defineProps<StatProps>()\n\ndefineSlots<{\n /** Icon shown next to the label. */\n icon?: () => unknown\n}>()\n\nconst trendIcon = computed(() => (props.trend === 'up' ? IconArrowUpRight : props.trend === 'down' ? IconArrowDownRight : null))\n</script>\n\n<template>\n <div class=\"j-stat\">\n <div class=\"j-stat__header\">\n <span v-if=\"$slots.icon\" class=\"j-stat__icon\" aria-hidden=\"true\"><slot name=\"icon\" /></span>\n <span class=\"j-stat__label\">{{ label }}</span>\n </div>\n <div class=\"j-stat__row\">\n <span class=\"j-stat__value\">{{ value }}</span>\n <span v-if=\"delta\" class=\"j-stat__delta\" :class=\"trend ? `j-stat__delta--${trend}` : undefined\">\n <component :is=\"trendIcon\" v-if=\"trendIcon\" aria-hidden=\"true\" />\n {{ delta }}\n </span>\n </div>\n <p v-if=\"description\" class=\"j-stat__description\">\n {{ description }}\n </p>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-stat {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-1-5);\n font-family: var(--juxt-font-sans);\n}\n\n.j-stat__header {\n display: flex;\n align-items: center;\n gap: var(--juxt-space-1-5);\n}\n\n.j-stat__icon {\n display: grid;\n place-items: center;\n color: var(--juxt-fg-muted);\n}\n\n.j-stat__icon > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n.j-stat__label {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-stat__row {\n display: flex;\n align-items: baseline;\n gap: var(--juxt-space-2);\n}\n\n.j-stat__value {\n color: var(--juxt-fg);\n font-size: var(--juxt-text-3xl);\n font-weight: var(--juxt-weight-semibold);\n line-height: var(--juxt-leading-tight);\n letter-spacing: var(--juxt-tracking-tighter);\n font-variant-numeric: tabular-nums;\n}\n\n.j-stat__delta {\n display: inline-flex;\n align-items: center;\n gap: 0.125rem;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n font-variant-numeric: tabular-nums;\n}\n\n.j-stat__delta > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n.j-stat__delta--up {\n color: var(--juxt-success-text);\n}\n\n.j-stat__delta--down {\n color: var(--juxt-danger-text);\n}\n\n.j-stat__description {\n margin: 0;\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n line-height: var(--juxt-leading-normal);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId } from 'vue'\n\nexport interface SwitchProps {\n label?: string\n description?: string\n disabled?: boolean\n size?: 'sm' | 'md'\n /** Submits `value` under `name` in a form when on. */\n name?: string\n value?: string\n id?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SwitchProps>(), { size: 'md', value: 'on' })\nconst model = defineModel<boolean>({ default: false })\n\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\n\nconst generated = useId()\nconst id = computed(() => props.id ?? `j-${generated}`)\nconst labelId = computed(() => `${id.value}-label`)\nconst descriptionId = computed(() => `${id.value}-description`)\nconst button = ref<HTMLButtonElement | null>(null)\n\nfunction toggle() {\n if (props.disabled) return\n model.value = !model.value\n}\n\ndefineExpose({ focus: () => button.value?.focus() })\n</script>\n\n<template>\n <div\n class=\"j-switch\"\n :class=\"[$attrs.class, `j-switch--${size}`, { 'is-disabled': disabled }]\"\n :style=\"$attrs.style as any\"\n :data-state=\"model ? 'on' : 'off'\"\n >\n <button\n :id=\"id\"\n ref=\"button\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n type=\"button\"\n role=\"switch\"\n class=\"j-switch__track\"\n :aria-checked=\"model\"\n :aria-labelledby=\"label || $slots.default ? labelId : undefined\"\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\n :disabled=\"disabled\"\n @click=\"toggle\"\n >\n <span class=\"j-switch__thumb\" />\n </button>\n <input v-if=\"name && model\" type=\"hidden\" :name=\"name\" :value=\"value\" />\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-switch__text\">\n <label v-if=\"label || $slots.default\" :id=\"labelId\" :for=\"id\" class=\"j-switch__label\">\n <slot>{{ label }}</slot>\n </label>\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-switch__description\">\n <slot name=\"description\">{{ description }}</slot>\n </span>\n </span>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-switch {\n --j-switch-w: 2rem;\n --j-switch-h: 1.125rem;\n --j-switch-pad: 2px;\n --j-switch-thumb: calc(var(--j-switch-h) - var(--j-switch-pad) * 2);\n\n display: inline-flex;\n align-items: flex-start;\n gap: var(--juxt-space-2-5);\n font-family: var(--juxt-font-sans);\n}\n\n.j-switch--sm {\n --j-switch-w: 1.625rem;\n --j-switch-h: 0.9375rem;\n}\n\n.j-switch__track {\n position: relative;\n flex: none;\n width: var(--j-switch-w);\n height: var(--j-switch-h);\n margin: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-switch-h)) / 2) 0 0;\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-border-strong);\n box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.08);\n cursor: pointer;\n outline: 2px solid transparent;\n outline-offset: 0;\n -webkit-tap-highlight-color: transparent;\n transition:\n background-color var(--juxt-duration-normal) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-switch__track:hover:not(:disabled) {\n background: var(--juxt-fg-disabled);\n}\n\n.j-switch__track:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n\n.j-switch[data-state='on'] .j-switch__track {\n background: var(--juxt-accent);\n}\n\n.j-switch[data-state='on'] .j-switch__track:hover:not(:disabled) {\n background: var(--juxt-accent-hover);\n}\n\n.j-switch__thumb {\n position: absolute;\n top: var(--j-switch-pad);\n left: var(--j-switch-pad);\n width: var(--j-switch-thumb);\n height: var(--j-switch-thumb);\n border-radius: var(--juxt-radius-full);\n background: #fff;\n box-shadow: 0 1px 2px rgb(0 0 0 / 0.2), 0 0 0 0.5px rgb(0 0 0 / 0.04);\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n width var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-switch[data-state='on'] .j-switch__thumb {\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2));\n}\n\n/* A whisper of stretch while pressed, so the thumb feels held. */\n.j-switch__track:active:not(:disabled) .j-switch__thumb {\n width: calc(var(--j-switch-thumb) + 3px);\n}\n\n.j-switch[data-state='on'] .j-switch__track:active:not(:disabled) .j-switch__thumb {\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2 - 3px));\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-switch__track:active:not(:disabled) .j-switch__thumb {\n width: var(--j-switch-thumb);\n }\n\n .j-switch[data-state='on'] .j-switch__track:active:not(:disabled) .j-switch__thumb {\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2));\n }\n}\n\n.j-switch__text {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n min-width: 0;\n}\n\n.j-switch__label {\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n user-select: none;\n}\n\n.j-switch__description {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-switch.is-disabled .j-switch__track {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.j-switch.is-disabled .j-switch__label {\n color: var(--juxt-fg-muted);\n cursor: not-allowed;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId } from 'vue'\n\nexport interface SwitchProps {\n label?: string\n description?: string\n disabled?: boolean\n size?: 'sm' | 'md'\n /** Submits `value` under `name` in a form when on. */\n name?: string\n value?: string\n id?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SwitchProps>(), { size: 'md', value: 'on' })\nconst model = defineModel<boolean>({ default: false })\n\ndefineSlots<{ default?: () => unknown, description?: () => unknown }>()\n\nconst generated = useId()\nconst id = computed(() => props.id ?? `j-${generated}`)\nconst labelId = computed(() => `${id.value}-label`)\nconst descriptionId = computed(() => `${id.value}-description`)\nconst button = ref<HTMLButtonElement | null>(null)\n\nfunction toggle() {\n if (props.disabled) return\n model.value = !model.value\n}\n\ndefineExpose({ focus: () => button.value?.focus() })\n</script>\n\n<template>\n <div\n class=\"j-switch\"\n :class=\"[$attrs.class, `j-switch--${size}`, { 'is-disabled': disabled }]\"\n :style=\"$attrs.style as any\"\n :data-state=\"model ? 'on' : 'off'\"\n >\n <button\n :id=\"id\"\n ref=\"button\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n type=\"button\"\n role=\"switch\"\n class=\"j-switch__track\"\n :aria-checked=\"model\"\n :aria-labelledby=\"label || $slots.default ? labelId : undefined\"\n :aria-describedby=\"description || $slots.description ? descriptionId : undefined\"\n :disabled=\"disabled\"\n @click=\"toggle\"\n >\n <span class=\"j-switch__thumb\" />\n </button>\n <input v-if=\"name && model\" type=\"hidden\" :name=\"name\" :value=\"value\" />\n <span v-if=\"label || $slots.default || description || $slots.description\" class=\"j-switch__text\">\n <label v-if=\"label || $slots.default\" :id=\"labelId\" :for=\"id\" class=\"j-switch__label\">\n <slot>{{ label }}</slot>\n </label>\n <span v-if=\"description || $slots.description\" :id=\"descriptionId\" class=\"j-switch__description\">\n <slot name=\"description\">{{ description }}</slot>\n </span>\n </span>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-switch {\n --j-switch-w: 2rem;\n --j-switch-h: 1.125rem;\n --j-switch-pad: 2px;\n --j-switch-thumb: calc(var(--j-switch-h) - var(--j-switch-pad) * 2);\n\n display: inline-flex;\n align-items: flex-start;\n gap: var(--juxt-space-2-5);\n font-family: var(--juxt-font-sans);\n}\n\n.j-switch--sm {\n --j-switch-w: 1.625rem;\n --j-switch-h: 0.9375rem;\n}\n\n.j-switch__track {\n position: relative;\n flex: none;\n width: var(--j-switch-w);\n height: var(--j-switch-h);\n margin: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-switch-h)) / 2) 0 0;\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-border-strong);\n box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.08);\n cursor: pointer;\n outline: 2px solid transparent;\n outline-offset: 0;\n -webkit-tap-highlight-color: transparent;\n transition:\n background-color var(--juxt-duration-normal) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-switch__track:hover:not(:disabled) {\n background: var(--juxt-fg-disabled);\n}\n\n.j-switch__track:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n\n.j-switch[data-state='on'] .j-switch__track {\n background: var(--juxt-accent);\n}\n\n.j-switch[data-state='on'] .j-switch__track:hover:not(:disabled) {\n background: var(--juxt-accent-hover);\n}\n\n.j-switch__thumb {\n position: absolute;\n top: var(--j-switch-pad);\n left: var(--j-switch-pad);\n width: var(--j-switch-thumb);\n height: var(--j-switch-thumb);\n border-radius: var(--juxt-radius-full);\n background: #fff;\n box-shadow: 0 1px 2px rgb(0 0 0 / 0.2), 0 0 0 0.5px rgb(0 0 0 / 0.04);\n transition:\n transform var(--juxt-duration-normal) var(--juxt-ease-out),\n width var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-switch[data-state='on'] .j-switch__thumb {\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2));\n}\n\n/* A whisper of stretch while pressed, so the thumb feels held. */\n.j-switch__track:active:not(:disabled) .j-switch__thumb {\n width: calc(var(--j-switch-thumb) + 3px);\n}\n\n.j-switch[data-state='on'] .j-switch__track:active:not(:disabled) .j-switch__thumb {\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2 - 3px));\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-switch__track:active:not(:disabled) .j-switch__thumb {\n width: var(--j-switch-thumb);\n }\n\n .j-switch[data-state='on'] .j-switch__track:active:not(:disabled) .j-switch__thumb {\n transform: translateX(calc(var(--j-switch-w) - var(--j-switch-thumb) - var(--j-switch-pad) * 2));\n }\n}\n\n.j-switch__text {\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n min-width: 0;\n}\n\n.j-switch__label {\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n line-height: var(--juxt-leading-snug);\n letter-spacing: var(--juxt-tracking-tight);\n cursor: pointer;\n user-select: none;\n}\n\n.j-switch__description {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-switch.is-disabled .j-switch__track {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.j-switch.is-disabled .j-switch__label {\n color: var(--juxt-fg-muted);\n cursor: not-allowed;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'\nimport { focusElement } from '../../utils/dom'\n\nexport interface TabItem {\n value: string\n label: string\n disabled?: boolean\n}\n\nexport interface TabsProps {\n items: TabItem[]\n /** `line` for page sections, `pill` for compact view switches. */\n variant?: 'line' | 'pill'\n size?: 'sm' | 'md'\n /** Accessible name for the tab list. */\n label?: string\n /** Stretch tabs to fill the width. */\n fill?: boolean\n}\n\nconst props = withDefaults(defineProps<TabsProps>(), { variant: 'line', size: 'md' })\n\nconst model = defineModel<string>()\n\ndefineSlots<{\n /** Custom tab label. */\n tab?: (props: { item: TabItem, selected: boolean }) => unknown\n /** Panels are named after each item's `value`. */\n [value: string]: ((props: { item: TabItem, selected: boolean }) => unknown) | undefined\n}>()\n\nconst id = `j-${useId()}`\nconst tabId = (value: string) => `${id}-tab-${value}`\nconst panelId = (value: string) => `${id}-panel-${value}`\n\nconst selected = computed(() => model.value ?? props.items.find(item => !item.disabled)?.value)\n\nconst list = ref<HTMLElement | null>(null)\nconst indicator = ref({ x: 0, width: 0, ready: false })\n// The indicator glides only for deliberate changes, never for layout shifts.\nconst animated = ref(false)\n\nfunction measure() {\n const el = list.value?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]')\n indicator.value = el ? { x: el.offsetLeft, width: el.offsetWidth, ready: true } : { ...indicator.value, ready: false }\n}\n\nfunction measureWithoutMotion() {\n animated.value = false\n measure()\n requestAnimationFrame(() => requestAnimationFrame(() => (animated.value = true)))\n}\n\nlet observer: ResizeObserver | undefined\nonMounted(() => {\n measureWithoutMotion()\n observer = new ResizeObserver(measureWithoutMotion)\n if (list.value) observer.observe(list.value)\n // Web fonts change tab widths once they arrive.\n document.fonts?.ready.then(measureWithoutMotion)\n})\nonBeforeUnmount(() => observer?.disconnect())\n\nwatch(selected, () => nextTick(measure), { flush: 'post' })\nwatch(() => props.items, () => nextTick(measure), { deep: true, flush: 'post' })\n\nfunction select(item: TabItem) {\n if (item.disabled) return\n model.value = item.value\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n const enabled = props.items.filter(item => !item.disabled)\n if (!enabled.length) return\n const current = enabled.findIndex(item => item.value === selected.value)\n let next: number | undefined\n if (event.key === 'ArrowRight') next = (current + 1) % enabled.length\n else if (event.key === 'ArrowLeft') next = (current - 1 + enabled.length) % enabled.length\n else if (event.key === 'Home') next = 0\n else if (event.key === 'End') next = enabled.length - 1\n if (next === undefined) return\n event.preventDefault()\n const item = enabled[next]!\n select(item)\n nextTick(() => focusElement(document.getElementById(tabId(item.value))))\n}\n\nconst indicatorStyle = computed(() => ({\n transform: `translateX(${indicator.value.x}px)`,\n width: `${indicator.value.width}px`,\n}))\n</script>\n\n<template>\n <div class=\"j-tabs\" :class=\"[`j-tabs--${variant}`, `j-tabs--${size}`, { 'j-tabs--fill': fill }]\">\n <div\n ref=\"list\"\n role=\"tablist\"\n aria-orientation=\"horizontal\"\n :aria-label=\"label\"\n class=\"j-tabs__list\"\n @keydown=\"onKeydown\"\n >\n <span\n class=\"j-tabs__indicator\"\n :class=\"{ 'is-ready': indicator.ready, 'is-animated': animated }\"\n :style=\"indicatorStyle\"\n aria-hidden=\"true\"\n />\n <button\n v-for=\"item in items\"\n :id=\"tabId(item.value)\"\n :key=\"item.value\"\n type=\"button\"\n role=\"tab\"\n class=\"j-tabs__tab\"\n :aria-selected=\"item.value === selected\"\n :aria-controls=\"panelId(item.value)\"\n :tabindex=\"item.value === selected ? 0 : -1\"\n :disabled=\"item.disabled\"\n @click=\"select(item)\"\n >\n <slot name=\"tab\" :item=\"item\" :selected=\"item.value === selected\">\n {{ item.label }}\n </slot>\n </button>\n </div>\n <template v-for=\"item in items\" :key=\"item.value\">\n <div\n v-if=\"$slots[item.value]\"\n :id=\"panelId(item.value)\"\n role=\"tabpanel\"\n class=\"j-tabs__panel\"\n :aria-labelledby=\"tabId(item.value)\"\n :hidden=\"item.value !== selected\"\n tabindex=\"0\"\n >\n <slot :name=\"item.value\" :item=\"item\" :selected=\"item.value === selected\" />\n </div>\n </template>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-tabs {\n --j-tab-height: var(--juxt-control-md);\n --j-tab-font: var(--juxt-text-md);\n\n display: flex;\n flex-direction: column;\n min-width: 0;\n font-family: var(--juxt-font-sans);\n}\n\n.j-tabs--sm {\n --j-tab-height: var(--juxt-control-sm);\n --j-tab-font: var(--juxt-text-sm);\n}\n\n.j-tabs__list {\n position: relative;\n display: flex;\n align-items: center;\n max-width: 100%;\n overflow-x: auto;\n scrollbar-width: none;\n}\n\n.j-tabs__list::-webkit-scrollbar {\n display: none;\n}\n\n.j-tabs__tab {\n position: relative;\n z-index: 1;\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: var(--juxt-space-1-5);\n height: var(--j-tab-height);\n padding: 0 var(--juxt-space-3);\n border: 0;\n background: transparent;\n color: var(--juxt-fg-muted);\n font: inherit;\n font-size: var(--j-tab-font);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n white-space: nowrap;\n cursor: pointer;\n outline: 2px solid transparent;\n outline-offset: -2px;\n -webkit-tap-highlight-color: transparent;\n transition:\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-tabs__tab:hover:not(:disabled) {\n color: var(--juxt-fg-secondary);\n}\n\n.j-tabs__tab[aria-selected='true'] {\n color: var(--juxt-fg);\n}\n\n.j-tabs__tab:focus-visible {\n outline-color: var(--juxt-ring);\n}\n\n.j-tabs__tab:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-tabs--fill .j-tabs__tab {\n flex: 1;\n}\n\n.j-tabs__indicator {\n position: absolute;\n left: 0;\n opacity: 0;\n pointer-events: none;\n}\n\n.j-tabs__indicator.is-ready {\n opacity: 1;\n}\n\n.j-tabs__indicator.is-animated {\n transition:\n transform var(--juxt-duration-slow) var(--juxt-ease-out),\n width var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-tabs__indicator.is-animated {\n transition: none;\n }\n}\n\n/* Line */\n.j-tabs--line .j-tabs__list {\n gap: var(--juxt-space-1);\n box-shadow: inset 0 -1px 0 var(--juxt-border);\n}\n\n.j-tabs--line .j-tabs__tab {\n padding: 0 var(--juxt-space-2);\n border-radius: var(--juxt-radius-sm) var(--juxt-radius-sm) 0 0;\n}\n\n.j-tabs--line .j-tabs__indicator {\n bottom: 0;\n height: 2px;\n border-radius: 2px 2px 0 0;\n background: var(--juxt-fg);\n}\n\n/* Pill */\n.j-tabs--pill .j-tabs__list {\n align-self: flex-start;\n padding: 3px;\n border-radius: var(--juxt-radius-md);\n background: var(--juxt-surface-sunken);\n box-shadow: inset 0 0 0 1px var(--juxt-border-subtle);\n}\n\n.j-tabs--pill.j-tabs--fill .j-tabs__list {\n align-self: stretch;\n}\n\n.j-tabs--pill .j-tabs__tab {\n --j-tab-height: calc(var(--juxt-control-md) - 6px);\n\n border-radius: var(--juxt-radius-sm);\n}\n\n.j-tabs--pill.j-tabs--sm .j-tabs__tab {\n --j-tab-height: calc(var(--juxt-control-sm) - 6px);\n\n padding: 0 var(--juxt-space-2-5);\n}\n\n.j-tabs--pill .j-tabs__indicator {\n top: 3px;\n bottom: 3px;\n border-radius: var(--juxt-radius-sm);\n background: var(--juxt-surface);\n box-shadow: var(--juxt-shadow-sm);\n}\n\n.dark .j-tabs--pill .j-tabs__indicator,\n[data-theme='dark'] .j-tabs--pill .j-tabs__indicator {\n background: var(--juxt-surface-active);\n}\n\n/* Panels */\n.j-tabs__panel {\n padding-top: var(--juxt-space-4);\n border-radius: var(--juxt-radius-sm);\n outline: 2px solid transparent;\n outline-offset: 2px;\n animation: j-tabs-panel-in var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-tabs__panel:focus-visible {\n outline-color: var(--juxt-ring);\n}\n\n@keyframes j-tabs-panel-in {\n from {\n opacity: 0;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'\nimport { focusElement } from '../../utils/dom'\n\nexport interface TabItem {\n value: string\n label: string\n disabled?: boolean\n}\n\nexport interface TabsProps {\n items: TabItem[]\n /** `line` for page sections, `pill` for compact view switches. */\n variant?: 'line' | 'pill'\n size?: 'sm' | 'md'\n /** Accessible name for the tab list. */\n label?: string\n /** Stretch tabs to fill the width. */\n fill?: boolean\n}\n\nconst props = withDefaults(defineProps<TabsProps>(), { variant: 'line', size: 'md' })\n\nconst model = defineModel<string>()\n\ndefineSlots<{\n /** Custom tab label. */\n tab?: (props: { item: TabItem, selected: boolean }) => unknown\n /** Panels are named after each item's `value`. */\n [value: string]: ((props: { item: TabItem, selected: boolean }) => unknown) | undefined\n}>()\n\nconst id = `j-${useId()}`\nconst tabId = (value: string) => `${id}-tab-${value}`\nconst panelId = (value: string) => `${id}-panel-${value}`\n\nconst selected = computed(() => model.value ?? props.items.find(item => !item.disabled)?.value)\n\nconst list = ref<HTMLElement | null>(null)\nconst indicator = ref({ x: 0, width: 0, ready: false })\n// The indicator glides only for deliberate changes, never for layout shifts.\nconst animated = ref(false)\n\nfunction measure() {\n const el = list.value?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]')\n indicator.value = el ? { x: el.offsetLeft, width: el.offsetWidth, ready: true } : { ...indicator.value, ready: false }\n}\n\nfunction measureWithoutMotion() {\n animated.value = false\n measure()\n requestAnimationFrame(() => requestAnimationFrame(() => (animated.value = true)))\n}\n\nlet observer: ResizeObserver | undefined\nonMounted(() => {\n measureWithoutMotion()\n observer = new ResizeObserver(measureWithoutMotion)\n if (list.value) observer.observe(list.value)\n // Web fonts change tab widths once they arrive.\n document.fonts?.ready.then(measureWithoutMotion)\n})\nonBeforeUnmount(() => observer?.disconnect())\n\nwatch(selected, () => nextTick(measure), { flush: 'post' })\nwatch(() => props.items, () => nextTick(measure), { deep: true, flush: 'post' })\n\nfunction select(item: TabItem) {\n if (item.disabled) return\n model.value = item.value\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n const enabled = props.items.filter(item => !item.disabled)\n if (!enabled.length) return\n const current = enabled.findIndex(item => item.value === selected.value)\n let next: number | undefined\n if (event.key === 'ArrowRight') next = (current + 1) % enabled.length\n else if (event.key === 'ArrowLeft') next = (current - 1 + enabled.length) % enabled.length\n else if (event.key === 'Home') next = 0\n else if (event.key === 'End') next = enabled.length - 1\n if (next === undefined) return\n event.preventDefault()\n const item = enabled[next]!\n select(item)\n nextTick(() => focusElement(document.getElementById(tabId(item.value))))\n}\n\nconst indicatorStyle = computed(() => ({\n transform: `translateX(${indicator.value.x}px)`,\n width: `${indicator.value.width}px`,\n}))\n</script>\n\n<template>\n <div class=\"j-tabs\" :class=\"[`j-tabs--${variant}`, `j-tabs--${size}`, { 'j-tabs--fill': fill }]\">\n <div\n ref=\"list\"\n role=\"tablist\"\n aria-orientation=\"horizontal\"\n :aria-label=\"label\"\n class=\"j-tabs__list\"\n @keydown=\"onKeydown\"\n >\n <span\n class=\"j-tabs__indicator\"\n :class=\"{ 'is-ready': indicator.ready, 'is-animated': animated }\"\n :style=\"indicatorStyle\"\n aria-hidden=\"true\"\n />\n <button\n v-for=\"item in items\"\n :id=\"tabId(item.value)\"\n :key=\"item.value\"\n type=\"button\"\n role=\"tab\"\n class=\"j-tabs__tab\"\n :aria-selected=\"item.value === selected\"\n :aria-controls=\"panelId(item.value)\"\n :tabindex=\"item.value === selected ? 0 : -1\"\n :disabled=\"item.disabled\"\n @click=\"select(item)\"\n >\n <slot name=\"tab\" :item=\"item\" :selected=\"item.value === selected\">\n {{ item.label }}\n </slot>\n </button>\n </div>\n <template v-for=\"item in items\" :key=\"item.value\">\n <div\n v-if=\"$slots[item.value]\"\n :id=\"panelId(item.value)\"\n role=\"tabpanel\"\n class=\"j-tabs__panel\"\n :aria-labelledby=\"tabId(item.value)\"\n :hidden=\"item.value !== selected\"\n tabindex=\"0\"\n >\n <slot :name=\"item.value\" :item=\"item\" :selected=\"item.value === selected\" />\n </div>\n </template>\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-tabs {\n --j-tab-height: var(--juxt-control-md);\n --j-tab-font: var(--juxt-text-md);\n\n display: flex;\n flex-direction: column;\n min-width: 0;\n font-family: var(--juxt-font-sans);\n}\n\n.j-tabs--sm {\n --j-tab-height: var(--juxt-control-sm);\n --j-tab-font: var(--juxt-text-sm);\n}\n\n.j-tabs__list {\n position: relative;\n display: flex;\n align-items: center;\n max-width: 100%;\n overflow-x: auto;\n scrollbar-width: none;\n}\n\n.j-tabs__list::-webkit-scrollbar {\n display: none;\n}\n\n.j-tabs__tab {\n position: relative;\n z-index: 1;\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: var(--juxt-space-1-5);\n height: var(--j-tab-height);\n padding: 0 var(--juxt-space-3);\n border: 0;\n background: transparent;\n color: var(--juxt-fg-muted);\n font: inherit;\n font-size: var(--j-tab-font);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n white-space: nowrap;\n cursor: pointer;\n outline: 2px solid transparent;\n outline-offset: -2px;\n -webkit-tap-highlight-color: transparent;\n transition:\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-tabs__tab:hover:not(:disabled) {\n color: var(--juxt-fg-secondary);\n}\n\n.j-tabs__tab[aria-selected='true'] {\n color: var(--juxt-fg);\n}\n\n.j-tabs__tab:focus-visible {\n outline-color: var(--juxt-ring);\n}\n\n.j-tabs__tab:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n\n.j-tabs--fill .j-tabs__tab {\n flex: 1;\n}\n\n.j-tabs__indicator {\n position: absolute;\n left: 0;\n opacity: 0;\n pointer-events: none;\n}\n\n.j-tabs__indicator.is-ready {\n opacity: 1;\n}\n\n.j-tabs__indicator.is-animated {\n transition:\n transform var(--juxt-duration-slow) var(--juxt-ease-out),\n width var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .j-tabs__indicator.is-animated {\n transition: none;\n }\n}\n\n/* Line */\n.j-tabs--line .j-tabs__list {\n gap: var(--juxt-space-1);\n box-shadow: inset 0 -1px 0 var(--juxt-border);\n}\n\n.j-tabs--line .j-tabs__tab {\n padding: 0 var(--juxt-space-2);\n border-radius: var(--juxt-radius-sm) var(--juxt-radius-sm) 0 0;\n}\n\n.j-tabs--line .j-tabs__indicator {\n bottom: 0;\n height: 2px;\n border-radius: 2px 2px 0 0;\n background: var(--juxt-fg);\n}\n\n/* Pill */\n.j-tabs--pill .j-tabs__list {\n align-self: flex-start;\n padding: 3px;\n border-radius: var(--juxt-radius-md);\n background: var(--juxt-surface-sunken);\n box-shadow: inset 0 0 0 1px var(--juxt-border-subtle);\n}\n\n.j-tabs--pill.j-tabs--fill .j-tabs__list {\n align-self: stretch;\n}\n\n.j-tabs--pill .j-tabs__tab {\n --j-tab-height: calc(var(--juxt-control-md) - 6px);\n\n border-radius: var(--juxt-radius-sm);\n}\n\n.j-tabs--pill.j-tabs--sm .j-tabs__tab {\n --j-tab-height: calc(var(--juxt-control-sm) - 6px);\n\n padding: 0 var(--juxt-space-2-5);\n}\n\n.j-tabs--pill .j-tabs__indicator {\n top: 3px;\n bottom: 3px;\n border-radius: var(--juxt-radius-sm);\n background: var(--juxt-surface);\n box-shadow: var(--juxt-shadow-sm);\n}\n\n.dark .j-tabs--pill .j-tabs__indicator,\n[data-theme='dark'] .j-tabs--pill .j-tabs__indicator {\n background: var(--juxt-surface-active);\n}\n\n/* Panels */\n.j-tabs__panel {\n padding-top: var(--juxt-space-4);\n border-radius: var(--juxt-radius-sm);\n outline: 2px solid transparent;\n outline-offset: 2px;\n animation: j-tabs-panel-in var(--juxt-duration-normal) var(--juxt-ease-out);\n}\n\n.j-tabs__panel:focus-visible {\n outline-color: var(--juxt-ring);\n}\n\n@keyframes j-tabs-panel-in {\n from {\n opacity: 0;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useSlots } from 'vue'\nimport { IconX } from '../../icons'\n\nexport type TagVariant = 'neutral' | 'success' | 'warning' | 'destructive' | 'accent'\n\nexport interface TagProps {\n variant?: TagVariant\n size?: 'sm' | 'md'\n disabled?: boolean\n /** Accessible label for the remove button, e.g. `\"Remove Engineering\"`. Defaults to \"Remove\". */\n removeLabel?: string\n}\n\nconst props = withDefaults(defineProps<TagProps>(), { variant: 'neutral', size: 'md' })\n\nconst emit = defineEmits<{ remove: [] }>()\n\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\n\nconst slots = useSlots()\n// Falls back to the tag's own text so a list of tags without an explicit\n// `removeLabel` each still announce a distinct name, not several \"Remove\"s.\nconst text = computed(() => slots.default?.().map(vnode => (typeof vnode.children === 'string' ? vnode.children : '')).join('').trim())\nconst removeButtonLabel = computed(() => props.removeLabel || (text.value ? `Remove ${text.value}` : 'Remove'))\n\nfunction onRemove() {\n if (props.disabled) return\n emit('remove')\n}\n</script>\n\n<template>\n <span class=\"j-tag\" :class=\"[`j-tag--${variant}`, `j-tag--${size}`, { 'is-disabled': disabled }]\">\n <span v-if=\"$slots.leading\" class=\"j-tag__icon\"><slot name=\"leading\" /></span>\n <span class=\"j-tag__label\"><slot /></span>\n <button type=\"button\" class=\"j-tag__remove j-focusable\" :aria-label=\"removeButtonLabel\" :disabled=\"disabled\" @click=\"onRemove\">\n <IconX />\n </button>\n </span>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n/*\n * Tags are pills, not stamps | fully rounded and carrying their own remove\n * affordance, which is what sets them apart from the static JBadge.\n */\n.j-tag {\n --j-tag-fg: var(--juxt-fg-secondary);\n --j-tag-bg: var(--juxt-surface-active);\n\n display: inline-flex;\n flex: none;\n align-items: center;\n gap: 0.3125rem;\n height: 1.375rem;\n padding: 0 var(--juxt-space-1) 0 var(--juxt-space-2-5);\n border-radius: var(--juxt-radius-full);\n background: var(--j-tag-bg);\n color: var(--j-tag-fg);\n font-family: var(--juxt-font-sans);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n line-height: 1;\n white-space: nowrap;\n}\n\n.j-tag--sm {\n height: 1.125rem;\n padding: 0 0.1875rem 0 var(--juxt-space-2);\n font-size: var(--juxt-text-2xs);\n}\n\n.j-tag--success {\n --j-tag-fg: var(--juxt-success-text);\n --j-tag-bg: var(--juxt-success-soft);\n}\n\n.j-tag--warning {\n --j-tag-fg: var(--juxt-warning-text);\n --j-tag-bg: var(--juxt-warning-soft);\n}\n\n.j-tag--destructive {\n --j-tag-fg: var(--juxt-danger-text);\n --j-tag-bg: var(--juxt-danger-soft);\n}\n\n.j-tag--accent {\n --j-tag-fg: var(--juxt-accent-fg);\n --j-tag-bg: var(--juxt-accent-strong);\n}\n\n.j-tag__icon {\n display: inline-flex;\n margin-left: -0.0625rem;\n}\n\n.j-tag__icon > svg {\n width: 0.75rem;\n height: 0.75rem;\n}\n\n.j-tag__label {\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.j-tag__remove {\n display: grid;\n flex: none;\n place-items: center;\n width: 1.125rem;\n height: 1.125rem;\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-full);\n background: transparent;\n color: inherit;\n cursor: pointer;\n opacity: 0.7;\n transition:\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-tag--sm .j-tag__remove {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n.j-tag__remove:hover:not(:disabled) {\n background: rgb(0 0 0 / 0.08);\n opacity: 1;\n}\n\n.dark .j-tag__remove:hover:not(:disabled),\n[data-theme='dark'] .j-tag__remove:hover:not(:disabled) {\n background: rgb(255 255 255 / 0.12);\n}\n\n.j-tag__remove > svg {\n width: 0.625rem;\n height: 0.625rem;\n}\n\n.j-tag--sm .j-tag__remove > svg {\n width: 0.5rem;\n height: 0.5rem;\n}\n\n.j-tag.is-disabled {\n opacity: 0.5;\n}\n\n.j-tag__remove:disabled {\n cursor: not-allowed;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useSlots } from 'vue'\nimport { IconX } from '../../icons'\n\nexport type TagVariant = 'neutral' | 'success' | 'warning' | 'destructive' | 'accent'\n\nexport interface TagProps {\n variant?: TagVariant\n size?: 'sm' | 'md'\n disabled?: boolean\n /** Accessible label for the remove button, e.g. `\"Remove Engineering\"`. Defaults to \"Remove\". */\n removeLabel?: string\n}\n\nconst props = withDefaults(defineProps<TagProps>(), { variant: 'neutral', size: 'md' })\n\nconst emit = defineEmits<{ remove: [] }>()\n\ndefineSlots<{ default?: () => unknown, leading?: () => unknown }>()\n\nconst slots = useSlots()\n// Falls back to the tag's own text so a list of tags without an explicit\n// `removeLabel` each still announce a distinct name, not several \"Remove\"s.\nconst text = computed(() => slots.default?.().map(vnode => (typeof vnode.children === 'string' ? vnode.children : '')).join('').trim())\nconst removeButtonLabel = computed(() => props.removeLabel || (text.value ? `Remove ${text.value}` : 'Remove'))\n\nfunction onRemove() {\n if (props.disabled) return\n emit('remove')\n}\n</script>\n\n<template>\n <span class=\"j-tag\" :class=\"[`j-tag--${variant}`, `j-tag--${size}`, { 'is-disabled': disabled }]\">\n <span v-if=\"$slots.leading\" class=\"j-tag__icon\"><slot name=\"leading\" /></span>\n <span class=\"j-tag__label\"><slot /></span>\n <button type=\"button\" class=\"j-tag__remove j-focusable\" :aria-label=\"removeButtonLabel\" :disabled=\"disabled\" @click=\"onRemove\">\n <IconX />\n </button>\n </span>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n/*\n * Tags are pills, not stamps | fully rounded and carrying their own remove\n * affordance, which is what sets them apart from the static JBadge.\n */\n.j-tag {\n --j-tag-fg: var(--juxt-fg-secondary);\n --j-tag-bg: var(--juxt-surface-active);\n\n display: inline-flex;\n flex: none;\n align-items: center;\n gap: 0.3125rem;\n height: 1.375rem;\n padding: 0 var(--juxt-space-1) 0 var(--juxt-space-2-5);\n border-radius: var(--juxt-radius-full);\n background: var(--j-tag-bg);\n color: var(--j-tag-fg);\n font-family: var(--juxt-font-sans);\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n line-height: 1;\n white-space: nowrap;\n}\n\n.j-tag--sm {\n height: 1.125rem;\n padding: 0 0.1875rem 0 var(--juxt-space-2);\n font-size: var(--juxt-text-2xs);\n}\n\n.j-tag--success {\n --j-tag-fg: var(--juxt-success-text);\n --j-tag-bg: var(--juxt-success-soft);\n}\n\n.j-tag--warning {\n --j-tag-fg: var(--juxt-warning-text);\n --j-tag-bg: var(--juxt-warning-soft);\n}\n\n.j-tag--destructive {\n --j-tag-fg: var(--juxt-danger-text);\n --j-tag-bg: var(--juxt-danger-soft);\n}\n\n.j-tag--accent {\n --j-tag-fg: var(--juxt-accent-fg);\n --j-tag-bg: var(--juxt-accent-strong);\n}\n\n.j-tag__icon {\n display: inline-flex;\n margin-left: -0.0625rem;\n}\n\n.j-tag__icon > svg {\n width: 0.75rem;\n height: 0.75rem;\n}\n\n.j-tag__label {\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.j-tag__remove {\n display: grid;\n flex: none;\n place-items: center;\n width: 1.125rem;\n height: 1.125rem;\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-full);\n background: transparent;\n color: inherit;\n cursor: pointer;\n opacity: 0.7;\n transition:\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n opacity var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-tag--sm .j-tag__remove {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n.j-tag__remove:hover:not(:disabled) {\n background: rgb(0 0 0 / 0.08);\n opacity: 1;\n}\n\n.dark .j-tag__remove:hover:not(:disabled),\n[data-theme='dark'] .j-tag__remove:hover:not(:disabled) {\n background: rgb(255 255 255 / 0.12);\n}\n\n.j-tag__remove > svg {\n width: 0.625rem;\n height: 0.625rem;\n}\n\n.j-tag--sm .j-tag__remove > svg {\n width: 0.5rem;\n height: 0.5rem;\n}\n\n.j-tag.is-disabled {\n opacity: 0.5;\n}\n\n.j-tag__remove:disabled {\n cursor: not-allowed;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { FieldProps } from '../field/useField'\nimport { nextTick, onMounted, ref, watch } from 'vue'\nimport JFieldShell from '../field/JFieldShell.vue'\nimport { useField } from '../field/useField'\n\nexport interface TextareaProps extends FieldProps {\n placeholder?: string\n rows?: number\n /** Grow with the content, up to `maxRows`. */\n autoresize?: boolean\n maxRows?: number\n readonly?: boolean\n name?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<TextareaProps>(), {\n rows: 3,\n maxRows: 12,\n error: undefined,\n})\n\nconst model = defineModel<string | null>({ default: '' })\n\ndefineSlots<{ label?: () => unknown }>()\n\nconst field = useField(props)\nconst textarea = ref<HTMLTextAreaElement | null>(null)\n\nfunction resize() {\n const el = textarea.value\n if (!props.autoresize || !el) return\n const style = getComputedStyle(el)\n const line = Number.parseFloat(style.lineHeight) || 20\n const chrome = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom)\n el.style.height = 'auto'\n const max = line * props.maxRows + chrome\n const next = Math.min(el.scrollHeight, max)\n el.style.height = `${next}px`\n el.style.overflowY = el.scrollHeight > max ? 'auto' : 'hidden'\n}\n\nonMounted(resize)\nwatch(model, () => nextTick(resize))\nwatch(() => props.autoresize, (value) => {\n if (value) return nextTick(resize)\n if (textarea.value) textarea.value.style.height = ''\n})\n\ndefineExpose({\n textarea,\n focus: () => textarea.value?.focus(),\n})\n</script>\n\n<template>\n <JFieldShell\n :id=\"field.id.value\"\n :class=\"$attrs.class\"\n :style=\"$attrs.style\"\n :label-id=\"field.labelId.value\"\n :description-id=\"field.descriptionId.value\"\n :error-id=\"field.errorId.value\"\n :label=\"label\"\n :description=\"description\"\n :error-message=\"field.errorMessage.value\"\n :show-description=\"field.showDescription.value\"\n :required=\"required\"\n :disabled=\"disabled\"\n >\n <template v-if=\"$slots.label\" #label>\n <slot name=\"label\" />\n </template>\n <div\n class=\"j-control j-textarea\"\n :class=\"{ 'is-invalid': field.invalid.value, 'is-disabled': disabled, 'is-readonly': readonly }\"\n >\n <textarea\n :id=\"field.id.value\"\n ref=\"textarea\"\n v-model=\"model\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n class=\"j-textarea__native\"\n :class=\"{ 'is-autoresize': autoresize }\"\n :rows=\"rows\"\n :name=\"name\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :required=\"required\"\n :aria-invalid=\"field.invalid.value || undefined\"\n :aria-describedby=\"field.describedBy.value\"\n />\n </div>\n </JFieldShell>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-textarea {\n align-items: stretch;\n padding: 0;\n cursor: auto;\n}\n\n.j-textarea__native {\n display: block;\n width: 100%;\n min-height: calc(var(--juxt-control-md) * 2);\n padding: var(--juxt-space-2) var(--juxt-space-2-5);\n border: 0;\n border-radius: inherit;\n outline: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n line-height: var(--juxt-leading-normal);\n letter-spacing: var(--juxt-tracking-tight);\n resize: vertical;\n}\n\n.j-textarea__native.is-autoresize {\n resize: none;\n overflow-y: hidden;\n}\n\n.j-textarea__native::placeholder {\n color: var(--juxt-fg-muted);\n opacity: 1;\n}\n\n.j-textarea__native:disabled {\n cursor: not-allowed;\n resize: none;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { FieldProps } from '../field/useField'\nimport { nextTick, onMounted, ref, watch } from 'vue'\nimport JFieldShell from '../field/JFieldShell.vue'\nimport { useField } from '../field/useField'\n\nexport interface TextareaProps extends FieldProps {\n placeholder?: string\n rows?: number\n /** Grow with the content, up to `maxRows`. */\n autoresize?: boolean\n maxRows?: number\n readonly?: boolean\n name?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<TextareaProps>(), {\n rows: 3,\n maxRows: 12,\n error: undefined,\n})\n\nconst model = defineModel<string | null>({ default: '' })\n\ndefineSlots<{ label?: () => unknown }>()\n\nconst field = useField(props)\nconst textarea = ref<HTMLTextAreaElement | null>(null)\n\nfunction resize() {\n const el = textarea.value\n if (!props.autoresize || !el) return\n const style = getComputedStyle(el)\n const line = Number.parseFloat(style.lineHeight) || 20\n const chrome = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom)\n el.style.height = 'auto'\n const max = line * props.maxRows + chrome\n const next = Math.min(el.scrollHeight, max)\n el.style.height = `${next}px`\n el.style.overflowY = el.scrollHeight > max ? 'auto' : 'hidden'\n}\n\nonMounted(resize)\nwatch(model, () => nextTick(resize))\nwatch(() => props.autoresize, (value) => {\n if (value) return nextTick(resize)\n if (textarea.value) textarea.value.style.height = ''\n})\n\ndefineExpose({\n textarea,\n focus: () => textarea.value?.focus(),\n})\n</script>\n\n<template>\n <JFieldShell\n :id=\"field.id.value\"\n :class=\"$attrs.class\"\n :style=\"$attrs.style\"\n :label-id=\"field.labelId.value\"\n :description-id=\"field.descriptionId.value\"\n :error-id=\"field.errorId.value\"\n :label=\"label\"\n :description=\"description\"\n :error-message=\"field.errorMessage.value\"\n :show-description=\"field.showDescription.value\"\n :required=\"required\"\n :disabled=\"disabled\"\n >\n <template v-if=\"$slots.label\" #label>\n <slot name=\"label\" />\n </template>\n <div\n class=\"j-control j-textarea\"\n :class=\"{ 'is-invalid': field.invalid.value, 'is-disabled': disabled, 'is-readonly': readonly }\"\n >\n <textarea\n :id=\"field.id.value\"\n ref=\"textarea\"\n v-model=\"model\"\n v-bind=\"{ ...$attrs, class: undefined, style: undefined }\"\n class=\"j-textarea__native\"\n :class=\"{ 'is-autoresize': autoresize }\"\n :rows=\"rows\"\n :name=\"name\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :required=\"required\"\n :aria-invalid=\"field.invalid.value || undefined\"\n :aria-describedby=\"field.describedBy.value\"\n />\n </div>\n </JFieldShell>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-textarea {\n align-items: stretch;\n padding: 0;\n cursor: auto;\n}\n\n.j-textarea__native {\n display: block;\n width: 100%;\n min-height: calc(var(--juxt-control-md) * 2);\n padding: var(--juxt-space-2) var(--juxt-space-2-5);\n border: 0;\n border-radius: inherit;\n outline: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n line-height: var(--juxt-leading-normal);\n letter-spacing: var(--juxt-tracking-tight);\n resize: vertical;\n}\n\n.j-textarea__native.is-autoresize {\n resize: none;\n overflow-y: hidden;\n}\n\n.j-textarea__native::placeholder {\n color: var(--juxt-fg-muted);\n opacity: 1;\n}\n\n.j-textarea__native:disabled {\n cursor: not-allowed;\n resize: none;\n}\n}\n</style>\n","<script setup lang=\"ts\">\ndefineSlots<{ default?: () => unknown }>()\n</script>\n\n<template>\n <ol class=\"j-timeline\">\n <slot />\n </ol>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-timeline {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n font-family: var(--juxt-font-sans);\n}\n}\n</style>\n","<script setup lang=\"ts\">\ndefineSlots<{ default?: () => unknown }>()\n</script>\n\n<template>\n <ol class=\"j-timeline\">\n <slot />\n </ol>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-timeline {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n font-family: var(--juxt-font-sans);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport type TimelineItemStatus = 'complete' | 'current' | 'upcoming'\n\nexport interface TimelineItemProps {\n title?: string\n /** A timestamp or relative time, e.g. \"2 hours ago\". */\n time?: string\n status?: TimelineItemStatus\n}\n\nwithDefaults(defineProps<TimelineItemProps>(), { status: 'complete' })\n\ndefineSlots<{\n default?: () => unknown\n title?: () => unknown\n /** Replace the default dot marker. */\n marker?: () => unknown\n}>()\n</script>\n\n<template>\n <li class=\"j-timeline-item\" :class=\"`j-timeline-item--${status}`\">\n <span class=\"j-timeline-item__rail\" aria-hidden=\"true\">\n <span class=\"j-timeline-item__marker\">\n <slot name=\"marker\" />\n </span>\n </span>\n <div class=\"j-timeline-item__content\">\n <div class=\"j-timeline-item__header\">\n <p v-if=\"title || $slots.title\" class=\"j-timeline-item__title\">\n <slot name=\"title\">{{ title }}</slot>\n </p>\n <time v-if=\"time\" class=\"j-timeline-item__time\">{{ time }}</time>\n </div>\n <div v-if=\"$slots.default\" class=\"j-timeline-item__body\">\n <slot />\n </div>\n </div>\n </li>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-timeline-item {\n position: relative;\n display: flex;\n gap: var(--juxt-space-3);\n padding-bottom: var(--juxt-space-5);\n}\n\n.j-timeline-item:last-child {\n padding-bottom: 0;\n}\n\n.j-timeline-item__rail {\n position: relative;\n display: flex;\n flex: none;\n justify-content: center;\n width: 1rem;\n}\n\n.j-timeline-item__rail::before {\n content: '';\n position: absolute;\n top: 1rem;\n bottom: calc(var(--juxt-space-5) * -1);\n width: 1px;\n background: var(--juxt-border);\n}\n\n.j-timeline-item:last-child .j-timeline-item__rail::before {\n display: none;\n}\n\n.j-timeline-item__marker {\n z-index: 1;\n display: grid;\n place-items: center;\n width: 0.625rem;\n height: 0.625rem;\n margin-top: 0.25rem;\n border: 2px solid var(--juxt-surface);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-fg-disabled);\n box-shadow: 0 0 0 1px var(--juxt-border);\n}\n\n.j-timeline-item--complete .j-timeline-item__marker {\n background: var(--juxt-accent);\n box-shadow: none;\n}\n\n.j-timeline-item--current .j-timeline-item__marker {\n background: var(--juxt-surface);\n box-shadow: 0 0 0 2px var(--juxt-accent);\n}\n\n.j-timeline-item__content {\n flex: 1;\n min-width: 0;\n padding-bottom: var(--juxt-space-0-5);\n}\n\n.j-timeline-item__header {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--juxt-space-2);\n}\n\n.j-timeline-item__title {\n margin: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-timeline-item--upcoming .j-timeline-item__title {\n color: var(--juxt-fg-muted);\n}\n\n.j-timeline-item__time {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-variant-numeric: tabular-nums;\n}\n\n.j-timeline-item__body {\n margin-top: var(--juxt-space-0-5);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n}\n</style>\n","<script setup lang=\"ts\">\nexport type TimelineItemStatus = 'complete' | 'current' | 'upcoming'\n\nexport interface TimelineItemProps {\n title?: string\n /** A timestamp or relative time, e.g. \"2 hours ago\". */\n time?: string\n status?: TimelineItemStatus\n}\n\nwithDefaults(defineProps<TimelineItemProps>(), { status: 'complete' })\n\ndefineSlots<{\n default?: () => unknown\n title?: () => unknown\n /** Replace the default dot marker. */\n marker?: () => unknown\n}>()\n</script>\n\n<template>\n <li class=\"j-timeline-item\" :class=\"`j-timeline-item--${status}`\">\n <span class=\"j-timeline-item__rail\" aria-hidden=\"true\">\n <span class=\"j-timeline-item__marker\">\n <slot name=\"marker\" />\n </span>\n </span>\n <div class=\"j-timeline-item__content\">\n <div class=\"j-timeline-item__header\">\n <p v-if=\"title || $slots.title\" class=\"j-timeline-item__title\">\n <slot name=\"title\">{{ title }}</slot>\n </p>\n <time v-if=\"time\" class=\"j-timeline-item__time\">{{ time }}</time>\n </div>\n <div v-if=\"$slots.default\" class=\"j-timeline-item__body\">\n <slot />\n </div>\n </div>\n </li>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-timeline-item {\n position: relative;\n display: flex;\n gap: var(--juxt-space-3);\n padding-bottom: var(--juxt-space-5);\n}\n\n.j-timeline-item:last-child {\n padding-bottom: 0;\n}\n\n.j-timeline-item__rail {\n position: relative;\n display: flex;\n flex: none;\n justify-content: center;\n width: 1rem;\n}\n\n.j-timeline-item__rail::before {\n content: '';\n position: absolute;\n top: 1rem;\n bottom: calc(var(--juxt-space-5) * -1);\n width: 1px;\n background: var(--juxt-border);\n}\n\n.j-timeline-item:last-child .j-timeline-item__rail::before {\n display: none;\n}\n\n.j-timeline-item__marker {\n z-index: 1;\n display: grid;\n place-items: center;\n width: 0.625rem;\n height: 0.625rem;\n margin-top: 0.25rem;\n border: 2px solid var(--juxt-surface);\n border-radius: var(--juxt-radius-full);\n background: var(--juxt-fg-disabled);\n box-shadow: 0 0 0 1px var(--juxt-border);\n}\n\n.j-timeline-item--complete .j-timeline-item__marker {\n background: var(--juxt-accent);\n box-shadow: none;\n}\n\n.j-timeline-item--current .j-timeline-item__marker {\n background: var(--juxt-surface);\n box-shadow: 0 0 0 2px var(--juxt-accent);\n}\n\n.j-timeline-item__content {\n flex: 1;\n min-width: 0;\n padding-bottom: var(--juxt-space-0-5);\n}\n\n.j-timeline-item__header {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--juxt-space-2);\n}\n\n.j-timeline-item__title {\n margin: 0;\n color: var(--juxt-fg);\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-timeline-item--upcoming .j-timeline-item__title {\n color: var(--juxt-fg-muted);\n}\n\n.j-timeline-item__time {\n color: var(--juxt-fg-muted);\n font-size: var(--juxt-text-xs);\n font-variant-numeric: tabular-nums;\n}\n\n.j-timeline-item__body {\n margin-top: var(--juxt-space-0-5);\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n}\n</style>\n","import { reactive, readonly } from 'vue'\n\nexport type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info' | 'loading'\n\nexport interface ToastAction {\n label: string\n onClick: () => void\n}\n\nexport interface ToastOptions {\n /** Reuse an id to replace an existing toast instead of stacking a new one. */\n id?: string\n description?: string\n type?: ToastType\n /** Time on screen in ms. `Infinity` keeps it until dismissed. Loading toasts never expire. */\n duration?: number\n action?: ToastAction\n /** Show the close button. Defaults to true. */\n dismissible?: boolean\n onDismiss?: () => void\n}\n\nexport interface Toast {\n id: string\n title: string\n description?: string\n type: ToastType\n duration: number\n action?: ToastAction\n dismissible: boolean\n onDismiss?: () => void\n /** Bumps whenever the toast changes, restarting its timer. */\n version: number\n}\n\nexport const DEFAULT_TOAST_DURATION = 5000\n\nconst state = reactive<{ toasts: Toast[] }>({ toasts: [] })\nlet counter = 0\n\nfunction resolveDuration(type: ToastType, duration?: number) {\n if (duration !== undefined) return duration\n if (type === 'loading') return Number.POSITIVE_INFINITY\n // Errors deserve a little longer to be read.\n if (type === 'error') return DEFAULT_TOAST_DURATION + 2000\n return DEFAULT_TOAST_DURATION\n}\n\nfunction create(title: string, options: ToastOptions = {}): string {\n const type = options.type ?? 'default'\n const existing = options.id ? state.toasts.find(t => t.id === options.id) : undefined\n if (existing) {\n update(existing.id, { title, ...options })\n return existing.id\n }\n const id = options.id ?? `toast-${++counter}`\n state.toasts.push({\n id,\n title,\n description: options.description,\n type,\n duration: resolveDuration(type, options.duration),\n action: options.action,\n dismissible: options.dismissible ?? true,\n onDismiss: options.onDismiss,\n version: 0,\n })\n return id\n}\n\nfunction update(id: string, patch: Partial<ToastOptions> & { title?: string }) {\n const item = state.toasts.find(t => t.id === id)\n if (!item) return\n const type = patch.type ?? item.type\n Object.assign(item, {\n ...patch,\n type,\n duration: patch.duration !== undefined || patch.type ? resolveDuration(type, patch.duration) : item.duration,\n version: item.version + 1,\n })\n}\n\nfunction dismiss(id?: string) {\n if (id === undefined) {\n const all = state.toasts.splice(0)\n all.forEach(t => t.onDismiss?.())\n return\n }\n const index = state.toasts.findIndex(t => t.id === id)\n if (index === -1) return\n const [removed] = state.toasts.splice(index, 1)\n removed?.onDismiss?.()\n}\n\ntype Message<T> = string | ((value: T) => string)\n\ninterface PromiseMessages<T> {\n loading: string\n success: Message<T>\n error: Message<unknown>\n description?: string\n}\n\nfunction promise<T>(input: Promise<T> | (() => Promise<T>), messages: PromiseMessages<T>): Promise<T> {\n const id = create(messages.loading, { type: 'loading', description: messages.description })\n const run = typeof input === 'function' ? input() : input\n run.then(\n (value) => {\n const title = typeof messages.success === 'function' ? messages.success(value) : messages.success\n update(id, { title, type: 'success', description: undefined })\n },\n (error: unknown) => {\n const title = typeof messages.error === 'function' ? messages.error(error) : messages.error\n update(id, { title, type: 'error', description: undefined })\n },\n )\n return run\n}\n\ntype Shortcut = (title: string, options?: Omit<ToastOptions, 'type'>) => string\nconst typed = (type: ToastType): Shortcut => (title, options) => create(title, { ...options, type })\n\nexport interface ToastFn {\n (title: string, options?: ToastOptions): string\n success: Shortcut\n error: Shortcut\n warning: Shortcut\n info: Shortcut\n loading: Shortcut\n promise: typeof promise\n update: typeof update\n dismiss: typeof dismiss\n}\n\n/** Show a toast. Works anywhere | inside or outside components. */\nexport const toast: ToastFn = Object.assign(create, {\n success: typed('success'),\n error: typed('error'),\n warning: typed('warning'),\n info: typed('info'),\n loading: typed('loading'),\n promise,\n update,\n dismiss,\n})\n\nexport function useToast() {\n return {\n toast,\n dismiss,\n update,\n toasts: readonly(state).toasts,\n }\n}\n\n/** @internal Mutable access for JToaster and tests. */\nexport const toastState = state\n","<script setup lang=\"ts\">\n/** Internal: one toast. Owns its timer, swipe gesture and keyboard dismissal. */\nimport type { Toast } from '../../composables/useToast'\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX, Spinner } from '../../icons'\n\nconst props = defineProps<{\n toast: Toast\n paused: boolean\n}>()\n\nconst emit = defineEmits<{ dismiss: [id: string] }>()\n\nconst el = ref<HTMLElement | null>(null)\nconst timed = computed(() => Number.isFinite(props.toast.duration) && props.toast.duration > 0)\n\n// --- Timer -----------------------------------------------------------------\nlet timer: ReturnType<typeof setTimeout> | undefined\nlet remaining = 0\nlet startedAt = 0\n\nfunction start() {\n clearTimeout(timer)\n if (!timed.value || props.paused) return\n startedAt = Date.now()\n timer = setTimeout(() => emit('dismiss', props.toast.id), remaining)\n}\n\nfunction pause() {\n if (!timer) return\n clearTimeout(timer)\n timer = undefined\n remaining -= Date.now() - startedAt\n}\n\nfunction reset() {\n remaining = props.toast.duration\n start()\n}\n\nonMounted(reset)\nonBeforeUnmount(() => clearTimeout(timer))\nwatch(() => props.toast.version, reset)\nwatch(() => props.paused, value => (value ? pause() : start()))\n\n// --- Swipe to dismiss ------------------------------------------------------\nconst offset = ref(0)\nconst swiping = ref(false)\nconst leaving = ref<0 | 1 | -1>(0)\nlet origin: { x: number, y: number, id: number } | null = null\n\nfunction onPointerDown(event: PointerEvent) {\n if (event.button !== 0 || (event.target as HTMLElement).closest('button, a')) return\n origin = { x: event.clientX, y: event.clientY, id: event.pointerId }\n}\n\nfunction onPointerMove(event: PointerEvent) {\n if (!origin || event.pointerId !== origin.id) return\n const dx = event.clientX - origin.x\n if (!swiping.value) {\n // Commit to a swipe only once the gesture is clearly horizontal.\n if (Math.abs(dx) < 6 || Math.abs(dx) < Math.abs(event.clientY - origin.y)) return\n swiping.value = true\n el.value?.setPointerCapture(event.pointerId)\n }\n offset.value = dx\n}\n\nfunction onPointerUp() {\n if (!origin) return\n origin = null\n if (!swiping.value) return\n swiping.value = false\n if (Math.abs(offset.value) > 64 && props.toast.dismissible) {\n leaving.value = offset.value > 0 ? 1 : -1\n emit('dismiss', props.toast.id)\n } else {\n offset.value = 0\n }\n}\n\nconst style = computed(() => {\n if (!offset.value) return undefined\n return {\n '--j-toast-swipe': `${offset.value}px`,\n '--j-toast-swipe-opacity': String(Math.max(0, 1 - Math.abs(offset.value) / 200)),\n }\n})\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape' && props.toast.dismissible) {\n event.preventDefault()\n event.stopPropagation()\n const next = (el.value?.nextElementSibling ?? el.value?.previousElementSibling) as HTMLElement | null\n emit('dismiss', props.toast.id)\n next?.focus()\n }\n}\n\nfunction runAction() {\n props.toast.action?.onClick()\n emit('dismiss', props.toast.id)\n}\n\nconst icons = {\n success: IconCircleCheck,\n error: IconCircleX,\n warning: IconAlert,\n info: IconInfo,\n}\n</script>\n\n<template>\n <li\n ref=\"el\"\n class=\"j-toast\"\n :class=\"[`j-toast--${toast.type}`, { 'is-swiping': swiping, 'is-swiped': leaving !== 0 }]\"\n :style=\"style\"\n :data-swipe-direction=\"leaving\"\n :role=\"toast.type === 'error' ? 'alert' : undefined\"\n tabindex=\"0\"\n @pointerdown=\"onPointerDown\"\n @pointermove=\"onPointerMove\"\n @pointerup=\"onPointerUp\"\n @pointercancel=\"onPointerUp\"\n @keydown=\"onKeydown\"\n >\n <span v-if=\"toast.type !== 'default'\" class=\"j-toast__icon\" aria-hidden=\"true\">\n <Transition name=\"j-toast-icon\" mode=\"out-in\">\n <Spinner v-if=\"toast.type === 'loading'\" :size=\"16\" />\n <component :is=\"icons[toast.type]\" v-else :key=\"toast.type\" />\n </Transition>\n </span>\n <div class=\"j-toast__body\">\n <p class=\"j-toast__title\">\n {{ toast.title }}\n </p>\n <p v-if=\"toast.description\" class=\"j-toast__description\">\n {{ toast.description }}\n </p>\n <div v-if=\"toast.action\" class=\"j-toast__actions\">\n <button type=\"button\" class=\"j-toast__action j-focusable\" @click=\"runAction\">\n {{ toast.action.label }}\n </button>\n </div>\n </div>\n <button\n v-if=\"toast.dismissible\"\n type=\"button\"\n class=\"j-toast__close j-focusable\"\n aria-label=\"Dismiss notification\"\n @click=\"emit('dismiss', toast.id)\"\n >\n <IconX />\n </button>\n <span\n v-if=\"timed\"\n :key=\"toast.version\"\n class=\"j-toast__progress\"\n :class=\"{ 'is-paused': paused }\"\n :style=\"{ animationDuration: `${toast.duration}ms` }\"\n aria-hidden=\"true\"\n />\n </li>\n</template>\n","<script setup lang=\"ts\">\n/** Internal: one toast. Owns its timer, swipe gesture and keyboard dismissal. */\nimport type { Toast } from '../../composables/useToast'\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { IconAlert, IconCircleCheck, IconCircleX, IconInfo, IconX, Spinner } from '../../icons'\n\nconst props = defineProps<{\n toast: Toast\n paused: boolean\n}>()\n\nconst emit = defineEmits<{ dismiss: [id: string] }>()\n\nconst el = ref<HTMLElement | null>(null)\nconst timed = computed(() => Number.isFinite(props.toast.duration) && props.toast.duration > 0)\n\n// --- Timer -----------------------------------------------------------------\nlet timer: ReturnType<typeof setTimeout> | undefined\nlet remaining = 0\nlet startedAt = 0\n\nfunction start() {\n clearTimeout(timer)\n if (!timed.value || props.paused) return\n startedAt = Date.now()\n timer = setTimeout(() => emit('dismiss', props.toast.id), remaining)\n}\n\nfunction pause() {\n if (!timer) return\n clearTimeout(timer)\n timer = undefined\n remaining -= Date.now() - startedAt\n}\n\nfunction reset() {\n remaining = props.toast.duration\n start()\n}\n\nonMounted(reset)\nonBeforeUnmount(() => clearTimeout(timer))\nwatch(() => props.toast.version, reset)\nwatch(() => props.paused, value => (value ? pause() : start()))\n\n// --- Swipe to dismiss ------------------------------------------------------\nconst offset = ref(0)\nconst swiping = ref(false)\nconst leaving = ref<0 | 1 | -1>(0)\nlet origin: { x: number, y: number, id: number } | null = null\n\nfunction onPointerDown(event: PointerEvent) {\n if (event.button !== 0 || (event.target as HTMLElement).closest('button, a')) return\n origin = { x: event.clientX, y: event.clientY, id: event.pointerId }\n}\n\nfunction onPointerMove(event: PointerEvent) {\n if (!origin || event.pointerId !== origin.id) return\n const dx = event.clientX - origin.x\n if (!swiping.value) {\n // Commit to a swipe only once the gesture is clearly horizontal.\n if (Math.abs(dx) < 6 || Math.abs(dx) < Math.abs(event.clientY - origin.y)) return\n swiping.value = true\n el.value?.setPointerCapture(event.pointerId)\n }\n offset.value = dx\n}\n\nfunction onPointerUp() {\n if (!origin) return\n origin = null\n if (!swiping.value) return\n swiping.value = false\n if (Math.abs(offset.value) > 64 && props.toast.dismissible) {\n leaving.value = offset.value > 0 ? 1 : -1\n emit('dismiss', props.toast.id)\n } else {\n offset.value = 0\n }\n}\n\nconst style = computed(() => {\n if (!offset.value) return undefined\n return {\n '--j-toast-swipe': `${offset.value}px`,\n '--j-toast-swipe-opacity': String(Math.max(0, 1 - Math.abs(offset.value) / 200)),\n }\n})\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape' && props.toast.dismissible) {\n event.preventDefault()\n event.stopPropagation()\n const next = (el.value?.nextElementSibling ?? el.value?.previousElementSibling) as HTMLElement | null\n emit('dismiss', props.toast.id)\n next?.focus()\n }\n}\n\nfunction runAction() {\n props.toast.action?.onClick()\n emit('dismiss', props.toast.id)\n}\n\nconst icons = {\n success: IconCircleCheck,\n error: IconCircleX,\n warning: IconAlert,\n info: IconInfo,\n}\n</script>\n\n<template>\n <li\n ref=\"el\"\n class=\"j-toast\"\n :class=\"[`j-toast--${toast.type}`, { 'is-swiping': swiping, 'is-swiped': leaving !== 0 }]\"\n :style=\"style\"\n :data-swipe-direction=\"leaving\"\n :role=\"toast.type === 'error' ? 'alert' : undefined\"\n tabindex=\"0\"\n @pointerdown=\"onPointerDown\"\n @pointermove=\"onPointerMove\"\n @pointerup=\"onPointerUp\"\n @pointercancel=\"onPointerUp\"\n @keydown=\"onKeydown\"\n >\n <span v-if=\"toast.type !== 'default'\" class=\"j-toast__icon\" aria-hidden=\"true\">\n <Transition name=\"j-toast-icon\" mode=\"out-in\">\n <Spinner v-if=\"toast.type === 'loading'\" :size=\"16\" />\n <component :is=\"icons[toast.type]\" v-else :key=\"toast.type\" />\n </Transition>\n </span>\n <div class=\"j-toast__body\">\n <p class=\"j-toast__title\">\n {{ toast.title }}\n </p>\n <p v-if=\"toast.description\" class=\"j-toast__description\">\n {{ toast.description }}\n </p>\n <div v-if=\"toast.action\" class=\"j-toast__actions\">\n <button type=\"button\" class=\"j-toast__action j-focusable\" @click=\"runAction\">\n {{ toast.action.label }}\n </button>\n </div>\n </div>\n <button\n v-if=\"toast.dismissible\"\n type=\"button\"\n class=\"j-toast__close j-focusable\"\n aria-label=\"Dismiss notification\"\n @click=\"emit('dismiss', toast.id)\"\n >\n <IconX />\n </button>\n <span\n v-if=\"timed\"\n :key=\"toast.version\"\n class=\"j-toast__progress\"\n :class=\"{ 'is-paused': paused }\"\n :style=\"{ animationDuration: `${toast.duration}ms` }\"\n aria-hidden=\"true\"\n />\n </li>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport { formatHotkey, useHotkey } from '../../composables/useHotkey'\nimport { toastState } from '../../composables/useToast'\nimport { focusElement, unrefElement } from '../../utils/dom'\nimport JToastItem from './JToastItem.vue'\nimport JPortal from '../primitives/JPortal.vue'\n\nexport type ToasterPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'\n\nexport interface ToasterProps {\n position?: ToasterPosition\n /** How many toasts are visible at once. Extra toasts wait their turn. */\n max?: number\n /** Shortcut that moves focus to the newest toast. */\n hotkey?: string\n /** Accessible name for the notification region. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToasterProps>(), {\n position: 'bottom-right',\n max: 4,\n hotkey: 'alt+t',\n label: 'Notifications',\n})\n\nconst list = ref()\nconst hovered = ref(false)\nconst focused = ref(false)\nconst hidden = ref(false)\nconst paused = computed(() => hovered.value || focused.value || hidden.value)\n\nconst isTop = computed(() => props.position.startsWith('top'))\n\n// Oldest first in the store; show the newest `max`, nearest the screen edge.\nconst visible = computed(() => {\n const items = toastState.toasts.slice(-props.max)\n return isTop.value ? items.reverse() : items\n})\n\nfunction dismiss(id: string) {\n const index = toastState.toasts.findIndex(t => t.id === id)\n if (index === -1) return\n const [removed] = toastState.toasts.splice(index, 1)\n removed?.onDismiss?.()\n}\n\nconst hotkeyLabel = ref('')\nonMounted(() => (hotkeyLabel.value = formatHotkey(props.hotkey).join('+')))\n\nuseHotkey(() => props.hotkey, () => {\n const items = unrefElement(list.value)?.querySelectorAll<HTMLElement>('.j-toast')\n if (!items?.length) return\n focusElement(isTop.value ? items[0] : items[items.length - 1])\n})\n\nfunction onVisibility() {\n hidden.value = document.visibilityState === 'hidden'\n}\nonMounted(() => document.addEventListener('visibilitychange', onVisibility))\nonBeforeUnmount(() => document.removeEventListener('visibilitychange', onVisibility))\n\nfunction onFocusOut(event: FocusEvent) {\n const current = event.currentTarget as HTMLElement\n if (!current.contains(event.relatedTarget as Node | null)) focused.value = false\n}\n</script>\n\n<template>\n <JPortal>\n <section\n class=\"j-toaster\"\n :class=\"`j-toaster--${position}`\"\n :aria-label=\"hotkeyLabel ? `${label} (${hotkeyLabel})` : label\"\n data-juxt-layer-ignore\n >\n <TransitionGroup\n ref=\"list\"\n tag=\"ol\"\n name=\"j-toast\"\n class=\"j-toaster__list\"\n aria-live=\"polite\"\n aria-relevant=\"additions text\"\n @pointerenter=\"hovered = true\"\n @pointerleave=\"hovered = false\"\n @focusin=\"focused = true\"\n @focusout=\"onFocusOut\"\n >\n <JToastItem\n v-for=\"item in visible\"\n :key=\"item.id\"\n :toast=\"item\"\n :paused=\"paused\"\n @dismiss=\"dismiss\"\n />\n </TransitionGroup>\n </section>\n </JPortal>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-toaster {\n --j-toast-width: 22.5rem;\n --j-toast-edge: var(--juxt-space-4);\n\n position: fixed;\n z-index: var(--juxt-z-toast);\n width: var(--j-toast-width);\n max-width: calc(100vw - var(--j-toast-edge) * 2);\n pointer-events: none;\n}\n\n.j-toaster--top-left,\n.j-toaster--top-center,\n.j-toaster--top-right {\n top: max(var(--j-toast-edge), env(safe-area-inset-top));\n}\n\n.j-toaster--bottom-left,\n.j-toaster--bottom-center,\n.j-toaster--bottom-right {\n bottom: max(var(--j-toast-edge), env(safe-area-inset-bottom));\n}\n\n.j-toaster--top-left,\n.j-toaster--bottom-left {\n left: var(--j-toast-edge);\n}\n\n.j-toaster--top-right,\n.j-toaster--bottom-right {\n right: calc(var(--j-toast-edge) + var(--juxt-scrollbar-width, 0px));\n}\n\n.j-toaster--top-center,\n.j-toaster--bottom-center {\n left: 50%;\n transform: translateX(-50%);\n}\n\n.j-toaster__list {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.j-toast {\n --j-toast-enter: calc(var(--juxt-motion-shift) * 3);\n\n position: relative;\n display: flex;\n align-items: flex-start;\n gap: var(--juxt-space-2-5);\n box-sizing: border-box;\n width: 100%;\n padding: var(--juxt-space-3) var(--juxt-space-10) var(--juxt-space-3) var(--juxt-space-3);\n overflow: hidden;\n border: 1px solid var(--juxt-border);\n border-radius: var(--juxt-radius-md);\n background: var(--juxt-surface-raised);\n box-shadow: var(--juxt-shadow-md);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n pointer-events: auto;\n opacity: var(--j-toast-swipe-opacity, 1);\n transform: translateX(var(--j-toast-swipe, 0));\n outline: 2px solid transparent;\n outline-offset: 0;\n touch-action: pan-y;\n transition:\n transform var(--juxt-duration-slow) var(--juxt-ease-out),\n opacity var(--juxt-duration-normal) var(--juxt-ease-out),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-toaster--top-left .j-toast,\n.j-toaster--top-center .j-toast,\n.j-toaster--top-right .j-toast {\n --j-toast-enter: calc(var(--juxt-motion-shift) * -3);\n}\n\n.j-toast.is-swiping {\n transition: none;\n user-select: none;\n}\n\n.j-toast:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n\n.j-toast__icon {\n display: grid;\n flex: none;\n place-items: center;\n width: 1rem;\n height: 1.25rem;\n}\n\n.j-toast__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-toast--success .j-toast__icon {\n color: var(--juxt-success-text);\n}\n\n.j-toast--error .j-toast__icon {\n color: var(--juxt-danger-text);\n}\n\n.j-toast--warning .j-toast__icon {\n color: var(--juxt-warning-text);\n}\n\n.j-toast--info .j-toast__icon {\n color: var(--juxt-info-text);\n}\n\n.j-toast--loading .j-toast__icon {\n color: var(--juxt-fg-muted);\n}\n\n.j-toast__body {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n min-width: 0;\n}\n\n.j-toast__title {\n margin: 0;\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n line-height: 1.25rem;\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-toast__description {\n margin: 0;\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-toast__actions {\n display: flex;\n gap: var(--juxt-space-2);\n margin-top: var(--juxt-space-2);\n}\n\n.j-toast__action {\n height: var(--juxt-control-xs);\n padding: 0 var(--juxt-space-2);\n border: 1px solid var(--juxt-border);\n border-radius: var(--juxt-radius-xs);\n background: var(--juxt-surface);\n color: var(--juxt-fg);\n font: inherit;\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n cursor: pointer;\n transition-property: background-color, border-color, outline-color, outline-offset;\n}\n\n.j-toast__action:hover {\n border-color: var(--juxt-border-strong);\n background: var(--juxt-surface-hover);\n}\n\n.j-toast__close {\n position: absolute;\n top: var(--juxt-space-2-5);\n right: var(--juxt-space-2-5);\n display: grid;\n place-items: center;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n color: var(--juxt-fg-muted);\n cursor: pointer;\n transition-property: background-color, color, outline-color, outline-offset;\n}\n\n.j-toast__close:hover {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-toast__close > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n/* Remaining time, drawn as a hairline that drains toward the start. */\n.j-toast__progress {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 2px;\n background: var(--juxt-border-strong);\n opacity: 0.6;\n transform-origin: left;\n animation: j-toast-progress linear forwards;\n}\n\n.j-toast--success .j-toast__progress {\n background: var(--juxt-accent-border);\n}\n\n.j-toast__progress.is-paused {\n animation-play-state: paused;\n}\n\n@keyframes j-toast-progress {\n from {\n transform: scaleX(1);\n }\n\n to {\n transform: scaleX(0);\n }\n}\n\n/* List motion */\n.j-toast-enter-from {\n opacity: 0;\n transform: translateY(var(--j-toast-enter)) scale(0.98);\n}\n\n.j-toast-leave-active {\n position: absolute;\n width: 100%;\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-in),\n transform var(--juxt-duration-normal) var(--juxt-ease-in);\n}\n\n.j-toast-leave-to {\n opacity: 0;\n transform: scale(0.96);\n}\n\n.j-toast-leave-to.is-swiped {\n transform: translateX(calc(var(--j-toast-width) * var(--j-swipe-sign, 1)));\n}\n\n.j-toast-leave-to.is-swiped[data-swipe-direction='-1'] {\n --j-swipe-sign: -1;\n}\n\n.j-toast-move {\n transition: transform var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n.j-toast-icon-enter-active,\n.j-toast-icon-leave-active {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-toast-icon-enter-from,\n.j-toast-icon-leave-to {\n opacity: 0;\n transform: scale(0.6);\n}\n\n@media (max-width: 639px) {\n .j-toaster {\n --j-toast-edge: var(--juxt-space-2);\n\n right: var(--j-toast-edge);\n left: var(--j-toast-edge);\n width: auto;\n max-width: none;\n transform: none;\n }\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport { formatHotkey, useHotkey } from '../../composables/useHotkey'\nimport { toastState } from '../../composables/useToast'\nimport { focusElement, unrefElement } from '../../utils/dom'\nimport JToastItem from './JToastItem.vue'\nimport JPortal from '../primitives/JPortal.vue'\n\nexport type ToasterPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'\n\nexport interface ToasterProps {\n position?: ToasterPosition\n /** How many toasts are visible at once. Extra toasts wait their turn. */\n max?: number\n /** Shortcut that moves focus to the newest toast. */\n hotkey?: string\n /** Accessible name for the notification region. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToasterProps>(), {\n position: 'bottom-right',\n max: 4,\n hotkey: 'alt+t',\n label: 'Notifications',\n})\n\nconst list = ref()\nconst hovered = ref(false)\nconst focused = ref(false)\nconst hidden = ref(false)\nconst paused = computed(() => hovered.value || focused.value || hidden.value)\n\nconst isTop = computed(() => props.position.startsWith('top'))\n\n// Oldest first in the store; show the newest `max`, nearest the screen edge.\nconst visible = computed(() => {\n const items = toastState.toasts.slice(-props.max)\n return isTop.value ? items.reverse() : items\n})\n\nfunction dismiss(id: string) {\n const index = toastState.toasts.findIndex(t => t.id === id)\n if (index === -1) return\n const [removed] = toastState.toasts.splice(index, 1)\n removed?.onDismiss?.()\n}\n\nconst hotkeyLabel = ref('')\nonMounted(() => (hotkeyLabel.value = formatHotkey(props.hotkey).join('+')))\n\nuseHotkey(() => props.hotkey, () => {\n const items = unrefElement(list.value)?.querySelectorAll<HTMLElement>('.j-toast')\n if (!items?.length) return\n focusElement(isTop.value ? items[0] : items[items.length - 1])\n})\n\nfunction onVisibility() {\n hidden.value = document.visibilityState === 'hidden'\n}\nonMounted(() => document.addEventListener('visibilitychange', onVisibility))\nonBeforeUnmount(() => document.removeEventListener('visibilitychange', onVisibility))\n\nfunction onFocusOut(event: FocusEvent) {\n const current = event.currentTarget as HTMLElement\n if (!current.contains(event.relatedTarget as Node | null)) focused.value = false\n}\n</script>\n\n<template>\n <JPortal>\n <section\n class=\"j-toaster\"\n :class=\"`j-toaster--${position}`\"\n :aria-label=\"hotkeyLabel ? `${label} (${hotkeyLabel})` : label\"\n data-juxt-layer-ignore\n >\n <TransitionGroup\n ref=\"list\"\n tag=\"ol\"\n name=\"j-toast\"\n class=\"j-toaster__list\"\n aria-live=\"polite\"\n aria-relevant=\"additions text\"\n @pointerenter=\"hovered = true\"\n @pointerleave=\"hovered = false\"\n @focusin=\"focused = true\"\n @focusout=\"onFocusOut\"\n >\n <JToastItem\n v-for=\"item in visible\"\n :key=\"item.id\"\n :toast=\"item\"\n :paused=\"paused\"\n @dismiss=\"dismiss\"\n />\n </TransitionGroup>\n </section>\n </JPortal>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-toaster {\n --j-toast-width: 22.5rem;\n --j-toast-edge: var(--juxt-space-4);\n\n position: fixed;\n z-index: var(--juxt-z-toast);\n width: var(--j-toast-width);\n max-width: calc(100vw - var(--j-toast-edge) * 2);\n pointer-events: none;\n}\n\n.j-toaster--top-left,\n.j-toaster--top-center,\n.j-toaster--top-right {\n top: max(var(--j-toast-edge), env(safe-area-inset-top));\n}\n\n.j-toaster--bottom-left,\n.j-toaster--bottom-center,\n.j-toaster--bottom-right {\n bottom: max(var(--j-toast-edge), env(safe-area-inset-bottom));\n}\n\n.j-toaster--top-left,\n.j-toaster--bottom-left {\n left: var(--j-toast-edge);\n}\n\n.j-toaster--top-right,\n.j-toaster--bottom-right {\n right: calc(var(--j-toast-edge) + var(--juxt-scrollbar-width, 0px));\n}\n\n.j-toaster--top-center,\n.j-toaster--bottom-center {\n left: 50%;\n transform: translateX(-50%);\n}\n\n.j-toaster__list {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: var(--juxt-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.j-toast {\n --j-toast-enter: calc(var(--juxt-motion-shift) * 3);\n\n position: relative;\n display: flex;\n align-items: flex-start;\n gap: var(--juxt-space-2-5);\n box-sizing: border-box;\n width: 100%;\n padding: var(--juxt-space-3) var(--juxt-space-10) var(--juxt-space-3) var(--juxt-space-3);\n overflow: hidden;\n border: 1px solid var(--juxt-border);\n border-radius: var(--juxt-radius-md);\n background: var(--juxt-surface-raised);\n box-shadow: var(--juxt-shadow-md);\n color: var(--juxt-fg);\n font-family: var(--juxt-font-sans);\n pointer-events: auto;\n opacity: var(--j-toast-swipe-opacity, 1);\n transform: translateX(var(--j-toast-swipe, 0));\n outline: 2px solid transparent;\n outline-offset: 0;\n touch-action: pan-y;\n transition:\n transform var(--juxt-duration-slow) var(--juxt-ease-out),\n opacity var(--juxt-duration-normal) var(--juxt-ease-out),\n outline-color var(--juxt-duration-fast) var(--juxt-ease-out),\n outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-toaster--top-left .j-toast,\n.j-toaster--top-center .j-toast,\n.j-toaster--top-right .j-toast {\n --j-toast-enter: calc(var(--juxt-motion-shift) * -3);\n}\n\n.j-toast.is-swiping {\n transition: none;\n user-select: none;\n}\n\n.j-toast:focus-visible {\n outline-color: var(--juxt-ring);\n outline-offset: 2px;\n}\n\n.j-toast__icon {\n display: grid;\n flex: none;\n place-items: center;\n width: 1rem;\n height: 1.25rem;\n}\n\n.j-toast__icon > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-toast--success .j-toast__icon {\n color: var(--juxt-success-text);\n}\n\n.j-toast--error .j-toast__icon {\n color: var(--juxt-danger-text);\n}\n\n.j-toast--warning .j-toast__icon {\n color: var(--juxt-warning-text);\n}\n\n.j-toast--info .j-toast__icon {\n color: var(--juxt-info-text);\n}\n\n.j-toast--loading .j-toast__icon {\n color: var(--juxt-fg-muted);\n}\n\n.j-toast__body {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--juxt-space-0-5);\n min-width: 0;\n}\n\n.j-toast__title {\n margin: 0;\n font-size: var(--juxt-text-md);\n font-weight: var(--juxt-weight-medium);\n line-height: 1.25rem;\n letter-spacing: var(--juxt-tracking-tight);\n}\n\n.j-toast__description {\n margin: 0;\n color: var(--juxt-fg-secondary);\n font-size: var(--juxt-text-sm);\n line-height: var(--juxt-leading-normal);\n}\n\n.j-toast__actions {\n display: flex;\n gap: var(--juxt-space-2);\n margin-top: var(--juxt-space-2);\n}\n\n.j-toast__action {\n height: var(--juxt-control-xs);\n padding: 0 var(--juxt-space-2);\n border: 1px solid var(--juxt-border);\n border-radius: var(--juxt-radius-xs);\n background: var(--juxt-surface);\n color: var(--juxt-fg);\n font: inherit;\n font-size: var(--juxt-text-xs);\n font-weight: var(--juxt-weight-medium);\n cursor: pointer;\n transition-property: background-color, border-color, outline-color, outline-offset;\n}\n\n.j-toast__action:hover {\n border-color: var(--juxt-border-strong);\n background: var(--juxt-surface-hover);\n}\n\n.j-toast__close {\n position: absolute;\n top: var(--juxt-space-2-5);\n right: var(--juxt-space-2-5);\n display: grid;\n place-items: center;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: 0;\n border-radius: var(--juxt-radius-xs);\n background: transparent;\n color: var(--juxt-fg-muted);\n cursor: pointer;\n transition-property: background-color, color, outline-color, outline-offset;\n}\n\n.j-toast__close:hover {\n background: var(--juxt-surface-hover);\n color: var(--juxt-fg);\n}\n\n.j-toast__close > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n/* Remaining time, drawn as a hairline that drains toward the start. */\n.j-toast__progress {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 2px;\n background: var(--juxt-border-strong);\n opacity: 0.6;\n transform-origin: left;\n animation: j-toast-progress linear forwards;\n}\n\n.j-toast--success .j-toast__progress {\n background: var(--juxt-accent-border);\n}\n\n.j-toast__progress.is-paused {\n animation-play-state: paused;\n}\n\n@keyframes j-toast-progress {\n from {\n transform: scaleX(1);\n }\n\n to {\n transform: scaleX(0);\n }\n}\n\n/* List motion */\n.j-toast-enter-from {\n opacity: 0;\n transform: translateY(var(--j-toast-enter)) scale(0.98);\n}\n\n.j-toast-leave-active {\n position: absolute;\n width: 100%;\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-in),\n transform var(--juxt-duration-normal) var(--juxt-ease-in);\n}\n\n.j-toast-leave-to {\n opacity: 0;\n transform: scale(0.96);\n}\n\n.j-toast-leave-to.is-swiped {\n transform: translateX(calc(var(--j-toast-width) * var(--j-swipe-sign, 1)));\n}\n\n.j-toast-leave-to.is-swiped[data-swipe-direction='-1'] {\n --j-swipe-sign: -1;\n}\n\n.j-toast-move {\n transition: transform var(--juxt-duration-slow) var(--juxt-ease-out);\n}\n\n.j-toast-icon-enter-active,\n.j-toast-icon-leave-active {\n transition:\n opacity var(--juxt-duration-fast) var(--juxt-ease-out),\n transform var(--juxt-duration-fast) var(--juxt-ease-out);\n}\n\n.j-toast-icon-enter-from,\n.j-toast-icon-leave-to {\n opacity: 0;\n transform: scale(0.6);\n}\n\n@media (max-width: 639px) {\n .j-toaster {\n --j-toast-edge: var(--juxt-space-2);\n\n right: var(--j-toast-edge);\n left: var(--j-toast-edge);\n width: auto;\n max-width: none;\n transform: none;\n }\n}\n}\n</style>\n","import type { ComputedRef, InjectionKey } from 'vue'\n\nexport type ToggleGroupType = 'single' | 'multiple'\n\nexport interface ToggleGroupContext {\n type: ComputedRef<ToggleGroupType>\n disabled: ComputedRef<boolean>\n isSelected: (value: string) => boolean\n toggle: (value: string) => void\n}\n\nexport const TOGGLE_GROUP: InjectionKey<ToggleGroupContext> = Symbol('JToggleGroup')\n","<script setup lang=\"ts\">\nimport { computed, provide } from 'vue'\nimport { focusElement } from '../../utils/dom'\nimport { TOGGLE_GROUP } from './context'\n\nexport interface ToggleGroupProps {\n /** `single` keeps at most one item pressed; `multiple` lets any number stay pressed. */\n type?: 'single' | 'multiple'\n size?: 'sm' | 'md'\n disabled?: boolean\n /** Accessible name for the group. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToggleGroupProps>(), {\n type: 'single',\n size: 'md',\n})\n\n/** The pressed value in `single` mode, or an array of values in `multiple` mode. */\nconst model = defineModel<string | string[] | null>({ default: null })\n\ndefineSlots<{ default?: () => unknown }>()\n\nprovide(TOGGLE_GROUP, {\n type: computed(() => props.type),\n disabled: computed(() => !!props.disabled),\n isSelected: (value) => {\n if (props.type === 'multiple') return Array.isArray(model.value) && model.value.includes(value)\n return model.value === value\n },\n toggle: (value) => {\n if (props.disabled) return\n if (props.type === 'multiple') {\n const current = Array.isArray(model.value) ? model.value : []\n model.value = current.includes(value) ? current.filter(v => v !== value) : [...current, value]\n return\n }\n model.value = value\n },\n})\n\n// Arrow keys move between items, per the WAI-ARIA toolbar pattern.\nfunction onKeydown(event: KeyboardEvent) {\n const target = event.target as HTMLElement\n if (!target.classList.contains('j-toggle-group__item')) return\n const root = event.currentTarget as HTMLElement\n const items = Array.from(root.querySelectorAll<HTMLElement>('.j-toggle-group__item:not(:disabled)'))\n const index = items.indexOf(target)\n let next: HTMLElement | undefined\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') next = items[(index + 1) % items.length]\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') next = items[(index - 1 + items.length) % items.length]\n else if (event.key === 'Home') next = items[0]\n else if (event.key === 'End') next = items[items.length - 1]\n if (!next) return\n event.preventDefault()\n focusElement(next)\n}\n</script>\n\n<template>\n <div\n class=\"j-toggle-group\"\n :class=\"[`j-toggle-group--${size}`, { 'is-disabled': disabled }]\"\n role=\"group\"\n :aria-label=\"label\"\n @keydown=\"onKeydown\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-toggle-group {\n --j-toggle-height: var(--juxt-control-md);\n\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 2px;\n border-radius: var(--juxt-radius-md);\n background: var(--juxt-surface-sunken);\n box-shadow: inset 0 0 0 1px var(--juxt-border-subtle);\n font-family: var(--juxt-font-sans);\n}\n\n.j-toggle-group--sm {\n --j-toggle-height: var(--juxt-control-sm);\n}\n\n.j-toggle-group.is-disabled {\n opacity: 0.6;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, provide } from 'vue'\nimport { focusElement } from '../../utils/dom'\nimport { TOGGLE_GROUP } from './context'\n\nexport interface ToggleGroupProps {\n /** `single` keeps at most one item pressed; `multiple` lets any number stay pressed. */\n type?: 'single' | 'multiple'\n size?: 'sm' | 'md'\n disabled?: boolean\n /** Accessible name for the group. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToggleGroupProps>(), {\n type: 'single',\n size: 'md',\n})\n\n/** The pressed value in `single` mode, or an array of values in `multiple` mode. */\nconst model = defineModel<string | string[] | null>({ default: null })\n\ndefineSlots<{ default?: () => unknown }>()\n\nprovide(TOGGLE_GROUP, {\n type: computed(() => props.type),\n disabled: computed(() => !!props.disabled),\n isSelected: (value) => {\n if (props.type === 'multiple') return Array.isArray(model.value) && model.value.includes(value)\n return model.value === value\n },\n toggle: (value) => {\n if (props.disabled) return\n if (props.type === 'multiple') {\n const current = Array.isArray(model.value) ? model.value : []\n model.value = current.includes(value) ? current.filter(v => v !== value) : [...current, value]\n return\n }\n model.value = value\n },\n})\n\n// Arrow keys move between items, per the WAI-ARIA toolbar pattern.\nfunction onKeydown(event: KeyboardEvent) {\n const target = event.target as HTMLElement\n if (!target.classList.contains('j-toggle-group__item')) return\n const root = event.currentTarget as HTMLElement\n const items = Array.from(root.querySelectorAll<HTMLElement>('.j-toggle-group__item:not(:disabled)'))\n const index = items.indexOf(target)\n let next: HTMLElement | undefined\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') next = items[(index + 1) % items.length]\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') next = items[(index - 1 + items.length) % items.length]\n else if (event.key === 'Home') next = items[0]\n else if (event.key === 'End') next = items[items.length - 1]\n if (!next) return\n event.preventDefault()\n focusElement(next)\n}\n</script>\n\n<template>\n <div\n class=\"j-toggle-group\"\n :class=\"[`j-toggle-group--${size}`, { 'is-disabled': disabled }]\"\n role=\"group\"\n :aria-label=\"label\"\n @keydown=\"onKeydown\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-toggle-group {\n --j-toggle-height: var(--juxt-control-md);\n\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 2px;\n border-radius: var(--juxt-radius-md);\n background: var(--juxt-surface-sunken);\n box-shadow: inset 0 0 0 1px var(--juxt-border-subtle);\n font-family: var(--juxt-font-sans);\n}\n\n.j-toggle-group--sm {\n --j-toggle-height: var(--juxt-control-sm);\n}\n\n.j-toggle-group.is-disabled {\n opacity: 0.6;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, inject } from 'vue'\nimport { TOGGLE_GROUP } from './context'\n\nexport interface ToggleGroupItemProps {\n /** Identifies the item in the group's `v-model`. */\n value: string\n disabled?: boolean\n /** Accessible name, when the slot content isn't descriptive enough (e.g. an icon-only item). */\n label?: string\n}\n\nconst props = defineProps<ToggleGroupItemProps>()\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst group = inject(TOGGLE_GROUP)!\nconst selected = computed(() => group.isSelected(props.value))\nconst isDisabled = computed(() => props.disabled || group.disabled.value)\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"j-toggle-group__item j-focusable\"\n :class=\"{ 'is-selected': selected }\"\n :aria-pressed=\"selected\"\n :aria-label=\"label\"\n :disabled=\"isDisabled\"\n @click=\"group.toggle(value)\"\n >\n <slot />\n </button>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-toggle-group__item {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--juxt-space-1-5);\n height: var(--j-toggle-height, var(--juxt-control-md));\n padding: 0 var(--juxt-space-3);\n border: 0;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg-secondary);\n font: inherit;\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n white-space: nowrap;\n cursor: pointer;\n outline: 2px solid transparent;\n outline-offset: 0;\n -webkit-tap-highlight-color: transparent;\n transition:\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n box-shadow var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-toggle-group__item > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-toggle-group__item:hover:not(:disabled) {\n color: var(--juxt-fg);\n}\n\n.j-toggle-group__item.is-selected {\n background: var(--juxt-surface);\n box-shadow: var(--juxt-shadow-sm);\n color: var(--juxt-fg);\n}\n\n.dark .j-toggle-group__item.is-selected,\n[data-theme='dark'] .j-toggle-group__item.is-selected {\n background: var(--juxt-surface-active);\n}\n\n.j-toggle-group__item:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, inject } from 'vue'\nimport { TOGGLE_GROUP } from './context'\n\nexport interface ToggleGroupItemProps {\n /** Identifies the item in the group's `v-model`. */\n value: string\n disabled?: boolean\n /** Accessible name, when the slot content isn't descriptive enough (e.g. an icon-only item). */\n label?: string\n}\n\nconst props = defineProps<ToggleGroupItemProps>()\n\ndefineSlots<{ default?: () => unknown }>()\n\nconst group = inject(TOGGLE_GROUP)!\nconst selected = computed(() => group.isSelected(props.value))\nconst isDisabled = computed(() => props.disabled || group.disabled.value)\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"j-toggle-group__item j-focusable\"\n :class=\"{ 'is-selected': selected }\"\n :aria-pressed=\"selected\"\n :aria-label=\"label\"\n :disabled=\"isDisabled\"\n @click=\"group.toggle(value)\"\n >\n <slot />\n </button>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n.j-toggle-group__item {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--juxt-space-1-5);\n height: var(--j-toggle-height, var(--juxt-control-md));\n padding: 0 var(--juxt-space-3);\n border: 0;\n border-radius: var(--juxt-radius-sm);\n background: transparent;\n color: var(--juxt-fg-secondary);\n font: inherit;\n font-size: var(--juxt-text-sm);\n font-weight: var(--juxt-weight-medium);\n letter-spacing: var(--juxt-tracking-tight);\n white-space: nowrap;\n cursor: pointer;\n outline: 2px solid transparent;\n outline-offset: 0;\n -webkit-tap-highlight-color: transparent;\n transition:\n background-color var(--juxt-duration-fast) var(--juxt-ease-standard),\n color var(--juxt-duration-fast) var(--juxt-ease-standard),\n box-shadow var(--juxt-duration-fast) var(--juxt-ease-standard);\n}\n\n.j-toggle-group__item > svg {\n width: 1rem;\n height: 1rem;\n}\n\n.j-toggle-group__item:hover:not(:disabled) {\n color: var(--juxt-fg);\n}\n\n.j-toggle-group__item.is-selected {\n background: var(--juxt-surface);\n box-shadow: var(--juxt-shadow-sm);\n color: var(--juxt-fg);\n}\n\n.dark .j-toggle-group__item.is-selected,\n[data-theme='dark'] .j-toggle-group__item.is-selected {\n background: var(--juxt-surface-active);\n}\n\n.j-toggle-group__item:disabled {\n color: var(--juxt-fg-disabled);\n cursor: not-allowed;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface VisuallyHiddenProps {\n /** Render as another element or component, e.g. `label` or `h2`. */\n as?: string | Component\n}\n\nwithDefaults(defineProps<VisuallyHiddenProps>(), { as: 'span' })\n\ndefineSlots<{ default?: () => unknown }>()\n</script>\n\n<template>\n <component :is=\"as\" class=\"j-visually-hidden j-sr-only\">\n <slot />\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n/*\n * Visual hiding is handled entirely by the shared `.j-sr-only` utility in\n * base.css | this component just gives it a typed, composable wrapper so it\n * can be used as `<JVisuallyHidden as=\"label\">…</JVisuallyHidden>`.\n */\n.j-visually-hidden {\n display: block;\n}\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Component } from 'vue'\n\nexport interface VisuallyHiddenProps {\n /** Render as another element or component, e.g. `label` or `h2`. */\n as?: string | Component\n}\n\nwithDefaults(defineProps<VisuallyHiddenProps>(), { as: 'span' })\n\ndefineSlots<{ default?: () => unknown }>()\n</script>\n\n<template>\n <component :is=\"as\" class=\"j-visually-hidden j-sr-only\">\n <slot />\n </component>\n</template>\n\n<style>\n@layer theme, base, juxt, components, utilities;\n@layer juxt {\n/*\n * Visual hiding is handled entirely by the shared `.j-sr-only` utility in\n * base.css | this component just gives it a typed, composable wrapper so it\n * can be used as `<JVisuallyHidden as=\"label\">…</JVisuallyHidden>`.\n */\n.j-visually-hidden {\n display: block;\n}\n}\n</style>\n","export { default as JAccordion } from './components/accordion/JAccordion.vue'\nexport { default as JAccordionItem } from './components/accordion/JAccordionItem.vue'\nexport { default as JAlert } from './components/alert/JAlert.vue'\nexport { default as JAspectRatio } from './components/aspect-ratio/JAspectRatio.vue'\nexport { default as JAvatar } from './components/avatar/JAvatar.vue'\nexport { default as JAvatarGroup } from './components/avatar-group/JAvatarGroup.vue'\nexport { default as JBadge } from './components/badge/JBadge.vue'\nexport { default as JBanner } from './components/banner/JBanner.vue'\nexport { default as JBreadcrumb } from './components/breadcrumb/JBreadcrumb.vue'\nexport { default as JBurger } from './components/burger/JBurger.vue'\nexport { default as JButton } from './components/button/JButton.vue'\nexport { default as JCard } from './components/card/JCard.vue'\nexport { default as JCheckbox } from './components/checkbox/JCheckbox.vue'\nexport { default as JCollapsible } from './components/collapsible/JCollapsible.vue'\nexport { default as JCommandPalette } from './components/command-palette/JCommandPalette.vue'\nexport { default as JDialog } from './components/dialog/JDialog.vue'\nexport { default as JDropdownMenu } from './components/dropdown-menu/JDropdownMenu.vue'\nexport { default as JDropdownMenuItem } from './components/dropdown-menu/JDropdownMenuItem.vue'\nexport { default as JDropdownMenuLabel } from './components/dropdown-menu/JDropdownMenuLabel.vue'\nexport { default as JDropdownMenuSeparator } from './components/dropdown-menu/JDropdownMenuSeparator.vue'\nexport { default as JDropdownMenuSub } from './components/dropdown-menu/JDropdownMenuSub.vue'\nexport { default as JEmptyState } from './components/empty-state/JEmptyState.vue'\nexport { default as JFooter } from './components/footer/JFooter.vue'\nexport { default as JFooterColumn } from './components/footer/JFooterColumn.vue'\nexport { default as JInput } from './components/input/JInput.vue'\nexport { default as JKbd } from './components/kbd/JKbd.vue'\nexport { default as JNavbar } from './components/navbar/JNavbar.vue'\nexport { default as JNavbarLink } from './components/navbar/JNavbarLink.vue'\nexport { default as JPagination } from './components/pagination/JPagination.vue'\nexport { default as JPopover } from './components/popover/JPopover.vue'\nexport { default as JProgress } from './components/progress/JProgress.vue'\nexport { default as JRadio } from './components/radio/JRadio.vue'\nexport { default as JRadioGroup } from './components/radio/JRadioGroup.vue'\nexport { default as JRating } from './components/rating/JRating.vue'\nexport { default as JSelect } from './components/select/JSelect.vue'\nexport { default as JSeparator } from './components/separator/JSeparator.vue'\nexport { default as JSheet } from './components/sheet/JSheet.vue'\nexport { default as JSidebar } from './components/sidebar/JSidebar.vue'\nexport { default as JSidebarGroup } from './components/sidebar/JSidebarGroup.vue'\nexport { default as JSidebarItem } from './components/sidebar/JSidebarItem.vue'\nexport { default as JSkeleton } from './components/skeleton/JSkeleton.vue'\nexport { default as JSlider } from './components/slider/JSlider.vue'\nexport { default as JSpinner } from './components/spinner/JSpinner.vue'\nexport { default as JStat } from './components/stat/JStat.vue'\nexport { default as JSwitch } from './components/switch/JSwitch.vue'\nexport { default as JTabs } from './components/tabs/JTabs.vue'\nexport { default as JTag } from './components/tag/JTag.vue'\nexport { default as JTextarea } from './components/textarea/JTextarea.vue'\nexport { default as JTimeline } from './components/timeline/JTimeline.vue'\nexport { default as JTimelineItem } from './components/timeline/JTimelineItem.vue'\nexport { default as JToaster } from './components/toast/JToaster.vue'\nexport { default as JToggleGroup } from './components/toggle-group/JToggleGroup.vue'\nexport { default as JToggleGroupItem } from './components/toggle-group/JToggleGroupItem.vue'\nexport { default as JTooltip } from './components/tooltip/JTooltip.vue'\nexport { default as JVisuallyHidden } from './components/visually-hidden/JVisuallyHidden.vue'\n","import { onMounted, ref } from 'vue'\n\nexport interface RecentCommandsOptions {\n /** localStorage key. */\n key?: string\n /** How many ids to remember. */\n limit?: number\n}\n\n/**\n * Remembers which commands were used most recently, per browser.\n * Pair `record(item.id)` in your select handler with `resolve(allItems)`\n * to feed the palette's `recent` prop.\n */\nexport function useRecentCommands(options: RecentCommandsOptions = {}) {\n const key = options.key ?? 'juxt:recent-commands'\n const limit = options.limit ?? 5\n const ids = ref<string[]>([])\n\n function read(): string[] {\n try {\n const value = JSON.parse(localStorage.getItem(key) ?? '[]')\n return Array.isArray(value) ? value.filter((id): id is string => typeof id === 'string') : []\n } catch {\n return []\n }\n }\n\n function write() {\n try {\n localStorage.setItem(key, JSON.stringify(ids.value))\n } catch {\n // Private mode or storage disabled: recents simply won't persist.\n }\n }\n\n onMounted(() => (ids.value = read()))\n\n function record(id: string) {\n ids.value = [id, ...ids.value.filter(existing => existing !== id)].slice(0, limit)\n write()\n }\n\n function clear() {\n ids.value = []\n write()\n }\n\n /** Map remembered ids back to items, dropping any that no longer exist. */\n function resolve<T extends { id: string }>(items: T[]): T[] {\n const byId = new Map(items.map(item => [item.id, item]))\n return ids.value.map(id => byId.get(id)).filter((item): item is T => !!item)\n }\n\n return { ids, record, clear, resolve }\n}\n","import type { Component, Plugin } from 'vue'\nimport * as components from './components'\n\n/**\n * Registers every Juxt component globally.\n *\n * app.use(JuxtUI)\n *\n * Prefer direct imports (or the Nuxt module) if you want tree-shaking.\n */\nexport const JuxtUI: Plugin = {\n install(app) {\n for (const [name, component] of Object.entries(components)) app.component(name, component as Component)\n },\n}\n"],"mappings":"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"}
|