@stacksjs/components 0.2.111 → 0.2.113

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.
Files changed (190) hide show
  1. package/dist/{Sidebar-4hn7vw4n.stx → Sidebar-gbd5ctkp.stx} +84 -3
  2. package/dist/{SidebarItem-gtrcw4ky.stx → SidebarItem-9rvbwhxr.stx} +11 -7
  3. package/dist/index.js +14 -11
  4. package/dist/ui/sidebar/themes.d.ts +25 -3
  5. package/package.json +5 -4
  6. package/src/components/CodeBlock.stx +54 -0
  7. package/src/components/Footer.stx +66 -0
  8. package/src/components/Hero.stx +25 -0
  9. package/src/components/Installation.stx +37 -0
  10. package/src/components/index.ts +4 -0
  11. package/src/composables/index.ts +4 -0
  12. package/src/composables/useCopyCode.ts +73 -0
  13. package/src/composables/useDarkMode.ts +100 -0
  14. package/src/composables/useSEO.ts +230 -0
  15. package/src/composables/utilities/index.ts +7 -0
  16. package/src/composables/utilities/useClickOutside.ts +72 -0
  17. package/src/composables/utilities/useFocusTrap.ts +159 -0
  18. package/src/composables/utilities/useKeyboard.ts +124 -0
  19. package/src/composables/utilities/useLocalStorage.ts +132 -0
  20. package/src/composables/utilities/useMediaQuery.ts +118 -0
  21. package/src/index.ts +99 -0
  22. package/src/stx.d.ts +4 -0
  23. package/src/ui/accordion/Accordion.stx +146 -0
  24. package/src/ui/accordion/AccordionItem.stx +37 -0
  25. package/src/ui/accordion/index.ts +47 -0
  26. package/src/ui/audio/Audio.stx +50 -0
  27. package/src/ui/audio/README.md +41 -0
  28. package/src/ui/audio/index.ts +13 -0
  29. package/src/ui/auth/Login.stx +161 -0
  30. package/src/ui/auth/README.md +142 -0
  31. package/src/ui/auth/Signup.stx +159 -0
  32. package/src/ui/auth/TwoFactorChallenge.stx +111 -0
  33. package/src/ui/auth/index.ts +32 -0
  34. package/src/ui/avatar/Avatar.stx +77 -0
  35. package/src/ui/avatar/index.ts +12 -0
  36. package/src/ui/badge/Badge.stx +62 -0
  37. package/src/ui/badge/index.ts +10 -0
  38. package/src/ui/breadcrumb/Breadcrumb.stx +79 -0
  39. package/src/ui/breadcrumb/index.ts +15 -0
  40. package/src/ui/button/Button.stx +74 -0
  41. package/src/ui/button/README.md +80 -0
  42. package/src/ui/button/index.ts +85 -0
  43. package/src/ui/calendar/Calendar.stx +189 -0
  44. package/src/ui/calendar/README.md +254 -0
  45. package/src/ui/calendar/index.ts +14 -0
  46. package/src/ui/card/Card.stx +68 -0
  47. package/src/ui/card/index.ts +13 -0
  48. package/src/ui/checkbox/Checkbox.stx +86 -0
  49. package/src/ui/checkbox/index.ts +16 -0
  50. package/src/ui/combobox/Combobox.stx +96 -0
  51. package/src/ui/combobox/ComboboxButton.stx +25 -0
  52. package/src/ui/combobox/ComboboxInput.stx +32 -0
  53. package/src/ui/combobox/ComboboxOption.stx +49 -0
  54. package/src/ui/combobox/ComboboxOptions.stx +16 -0
  55. package/src/ui/combobox/README.md +150 -0
  56. package/src/ui/combobox/index.ts +40 -0
  57. package/src/ui/command-palette/CommandPalette.stx +81 -0
  58. package/src/ui/command-palette/CommandPaletteItem.stx +27 -0
  59. package/src/ui/command-palette/README.md +42 -0
  60. package/src/ui/command-palette/index.ts +15 -0
  61. package/src/ui/dialog/Dialog.stx +41 -0
  62. package/src/ui/dialog/DialogBackdrop.stx +9 -0
  63. package/src/ui/dialog/DialogDescription.stx +11 -0
  64. package/src/ui/dialog/DialogPanel.stx +16 -0
  65. package/src/ui/dialog/DialogTitle.stx +11 -0
  66. package/src/ui/dialog/README.md +121 -0
  67. package/src/ui/dialog/index.ts +32 -0
  68. package/src/ui/drawer/Drawer.stx +120 -0
  69. package/src/ui/drawer/README.md +90 -0
  70. package/src/ui/drawer/index.ts +9 -0
  71. package/src/ui/dropdown/Dropdown.stx +82 -0
  72. package/src/ui/dropdown/DropdownButton.stx +29 -0
  73. package/src/ui/dropdown/DropdownItem.stx +35 -0
  74. package/src/ui/dropdown/DropdownItems.stx +18 -0
  75. package/src/ui/dropdown/README.md +114 -0
  76. package/src/ui/dropdown/index.ts +29 -0
  77. package/src/ui/form/Form.stx +95 -0
  78. package/src/ui/form/index.ts +67 -0
  79. package/src/ui/heatmap/Heatmap.stx +294 -0
  80. package/src/ui/image/Image.stx +165 -0
  81. package/src/ui/image/README.md +164 -0
  82. package/src/ui/image/index.ts +22 -0
  83. package/src/ui/input/EmailInput.stx +30 -0
  84. package/src/ui/input/NumberInput.stx +126 -0
  85. package/src/ui/input/PasswordInput.stx +143 -0
  86. package/src/ui/input/SearchInput.stx +44 -0
  87. package/src/ui/input/TextInput.stx +132 -0
  88. package/src/ui/input/index.ts +180 -0
  89. package/src/ui/listbox/Listbox.stx +101 -0
  90. package/src/ui/listbox/ListboxButton.stx +28 -0
  91. package/src/ui/listbox/ListboxLabel.stx +11 -0
  92. package/src/ui/listbox/ListboxOption.stx +49 -0
  93. package/src/ui/listbox/ListboxOptions.stx +16 -0
  94. package/src/ui/listbox/README.md +135 -0
  95. package/src/ui/listbox/index.ts +39 -0
  96. package/src/ui/navigator/Navigator.stx +113 -0
  97. package/src/ui/navigator/README.md +352 -0
  98. package/src/ui/navigator/index.ts +20 -0
  99. package/src/ui/notification/Notification.stx +117 -0
  100. package/src/ui/notification/README.md +114 -0
  101. package/src/ui/notification/index.ts +12 -0
  102. package/src/ui/pagination/Pagination.stx +150 -0
  103. package/src/ui/pagination/index.ts +11 -0
  104. package/src/ui/payment/Checkout.stx +31 -0
  105. package/src/ui/payment/DefaultPaymentMethod.stx +62 -0
  106. package/src/ui/payment/PaymentMethods.stx +104 -0
  107. package/src/ui/payment/README.md +345 -0
  108. package/src/ui/payment/SubscriptionCheckout.stx +323 -0
  109. package/src/ui/payment/index.ts +59 -0
  110. package/src/ui/popover/Popover.stx +60 -0
  111. package/src/ui/popover/PopoverButton.stx +29 -0
  112. package/src/ui/popover/PopoverPanel.stx +23 -0
  113. package/src/ui/popover/README.md +91 -0
  114. package/src/ui/popover/index.ts +22 -0
  115. package/src/ui/portal/Portal.stx +57 -0
  116. package/src/ui/portal/index.ts +6 -0
  117. package/src/ui/progress/Progress.stx +131 -0
  118. package/src/ui/progress/index.ts +12 -0
  119. package/src/ui/radio/Radio.stx +77 -0
  120. package/src/ui/radio/index.ts +15 -0
  121. package/src/ui/radio-group/README.md +139 -0
  122. package/src/ui/radio-group/RadioGroup.stx +60 -0
  123. package/src/ui/radio-group/RadioGroupDescription.stx +11 -0
  124. package/src/ui/radio-group/RadioGroupLabel.stx +11 -0
  125. package/src/ui/radio-group/RadioGroupOption.stx +58 -0
  126. package/src/ui/radio-group/index.ts +30 -0
  127. package/src/ui/select/README.md +180 -0
  128. package/src/ui/select/Select.stx +82 -0
  129. package/src/ui/select/index.ts +21 -0
  130. package/src/ui/sidebar/Sidebar.stx +557 -0
  131. package/src/ui/sidebar/SidebarFooter.stx +71 -0
  132. package/src/ui/sidebar/SidebarHeader.stx +103 -0
  133. package/src/ui/sidebar/SidebarItem.stx +90 -0
  134. package/src/ui/sidebar/SidebarSection.stx +83 -0
  135. package/src/ui/sidebar/index.ts +146 -0
  136. package/src/ui/sidebar/themes.ts +277 -0
  137. package/src/ui/skeleton/Skeleton.stx +48 -0
  138. package/src/ui/skeleton/index.ts +11 -0
  139. package/src/ui/spinner/Spinner.stx +109 -0
  140. package/src/ui/spinner/index.ts +9 -0
  141. package/src/ui/stepper/README.md +36 -0
  142. package/src/ui/stepper/Stepper.stx +17 -0
  143. package/src/ui/stepper/StepperStep.stx +57 -0
  144. package/src/ui/stepper/index.ts +19 -0
  145. package/src/ui/storage/README.md +193 -0
  146. package/src/ui/storage/index.ts +347 -0
  147. package/src/ui/switch/README.md +114 -0
  148. package/src/ui/switch/Switch.stx +72 -0
  149. package/src/ui/switch/index.ts +10 -0
  150. package/src/ui/table/README.md +39 -0
  151. package/src/ui/table/Table.stx +14 -0
  152. package/src/ui/table/TableBody.stx +10 -0
  153. package/src/ui/table/TableCell.stx +10 -0
  154. package/src/ui/table/TableHead.stx +10 -0
  155. package/src/ui/table/TableHeader.stx +20 -0
  156. package/src/ui/table/TableRow.stx +11 -0
  157. package/src/ui/table/index.ts +35 -0
  158. package/src/ui/tabs/TabPanel.stx +25 -0
  159. package/src/ui/tabs/Tabs.stx +202 -0
  160. package/src/ui/tabs/index.ts +50 -0
  161. package/src/ui/teleport/Teleport.stx +65 -0
  162. package/src/ui/teleport/index.ts +7 -0
  163. package/src/ui/textarea/Textarea.stx +108 -0
  164. package/src/ui/textarea/index.ts +20 -0
  165. package/src/ui/tooltip/Tooltip.stx +71 -0
  166. package/src/ui/tooltip/index.ts +10 -0
  167. package/src/ui/transition/README.md +30 -0
  168. package/src/ui/transition/Transition.stx +102 -0
  169. package/src/ui/transition/index.ts +25 -0
  170. package/src/ui/video/README.md +47 -0
  171. package/src/ui/video/Video.stx +45 -0
  172. package/src/ui/video/index.ts +16 -0
  173. package/src/ui/virtual-list/VirtualList.stx +67 -0
  174. package/src/ui/virtual-list/index.ts +10 -0
  175. package/src/ui/virtual-table/VirtualTable.stx +103 -0
  176. package/src/ui/virtual-table/index.ts +21 -0
  177. package/src/utils/PROP_VALIDATION.md +458 -0
  178. package/src/utils/THEME_SYSTEM.md +702 -0
  179. package/src/utils/accessibility.ts +841 -0
  180. package/src/utils/animation/easing.ts +368 -0
  181. package/src/utils/animation/helpers.ts +223 -0
  182. package/src/utils/animation/index.ts +5 -0
  183. package/src/utils/animation/keyframes.ts +230 -0
  184. package/src/utils/error-handling.ts +475 -0
  185. package/src/utils/highlighter.ts +93 -0
  186. package/src/utils/i18n.ts +634 -0
  187. package/src/utils/keep-alive.ts +819 -0
  188. package/src/utils/lazy-loading.ts +530 -0
  189. package/src/utils/prop-validation.ts +478 -0
  190. package/src/utils/theme.ts +626 -0
@@ -0,0 +1,103 @@
1
+ <script server>
2
+ // The window-chrome strip at the top of a macOS sidebar: traffic lights on
3
+ // the left, floating "Liquid Glass" toolbar buttons on the right, and the
4
+ // whole strip acts as a window drag region inside a Craft window.
5
+ export const theme = $props.theme || $props.variant || 'macos'
6
+ export const showWindowControls = $props.showWindowControls ?? true
7
+ export const actions = $props.actions || [] // [{ id, icon, label }]
8
+ export const title = $props.title || ''
9
+ export const subtitle = $props.subtitle || ''
10
+ export const logo = $props.logo || ''
11
+ export const showSearch = $props.showSearch ?? false
12
+ export const searchPlaceholder = $props.searchPlaceholder || 'Search'
13
+
14
+ export const isMacos = theme === 'macos' || theme === 'tahoe' || theme === 'macos-tahoe' || theme === 'macos-latest'
15
+
16
+ // Traffic lights: 13px circles, 7px apart, inset ~21px — measured on
17
+ // macOS Tahoe. Colors are Apple's exact stoplight values.
18
+ export const trafficLightClasses = 'h-[13px] w-[13px] rounded-full shadow-[inset_0_0_1px_rgba(0,0,0,0.2)]'
19
+
20
+ // Floating toolbar button: a 34px glass circle that brightens on hover.
21
+ export const toolbarButtonClasses = [
22
+ 'grid h-[34px] w-[34px] place-items-center rounded-full',
23
+ 'bg-white/55 dark:bg-white/10',
24
+ 'backdrop-blur-xl',
25
+ 'shadow-[0_1px_4px_rgba(0,0,0,0.12),inset_0_0.5px_0_rgba(255,255,255,0.6)]',
26
+ 'text-[#3c3c43]/75 dark:text-[#ebebf5]/75',
27
+ 'transition-colors duration-150',
28
+ 'hover:bg-white/80 dark:hover:bg-white/20',
29
+ 'active:bg-white/95 dark:active:bg-white/25',
30
+ ].join(' ')
31
+ </script>
32
+
33
+ <script client>
34
+ const emit = defineEmits()
35
+
36
+ function onAction(event) {
37
+ emit('action', event.currentTarget?.dataset?.sidebarAction || event.target.closest('[data-sidebar-action]')?.dataset.sidebarAction)
38
+ }
39
+
40
+ function onSearchInput(event) {
41
+ emit('search', event.target.value)
42
+ }
43
+
44
+ function onWindowControl(action) {
45
+ const api = window.craft?.window
46
+ if (action === 'close') api?.close?.()
47
+ if (action === 'minimize') api?.minimize?.()
48
+ if (action === 'zoom') (api?.maximize || api?.zoom)?.call(api)
49
+ emit('windowControl', action)
50
+ }
51
+ </script>
52
+
53
+ @if(isMacos)
54
+ <div class="flex h-[52px] shrink-0 items-center justify-between pl-[21px] pr-[10px] [-webkit-app-region:drag]">
55
+ @if(showWindowControls)
56
+ <div class="group/lights flex items-center gap-[7px] [-webkit-app-region:no-drag]">
57
+ <button type="button" class="{{ trafficLightClasses }} bg-[#ff5f57]" aria-label="Close window" @click="onWindowControl('close')"></button>
58
+ <button type="button" class="{{ trafficLightClasses }} bg-[#febc2e]" aria-label="Minimize window" @click="onWindowControl('minimize')"></button>
59
+ <button type="button" class="{{ trafficLightClasses }} bg-[#28c840]" aria-label="Zoom window" @click="onWindowControl('zoom')"></button>
60
+ </div>
61
+ @else
62
+ <span></span>
63
+ @endif
64
+
65
+ <div class="flex items-center gap-[8px] [-webkit-app-region:no-drag]">
66
+ @foreach(actions as action)
67
+ <button type="button" class="{{ toolbarButtonClasses }}" aria-label="{{ action.label }}" data-sidebar-action="{{ action.id }}" @click="onAction($event)">
68
+ <span class="{{ action.icon }} h-[16px] w-[16px]" aria-hidden="true"></span>
69
+ </button>
70
+ @endforeach
71
+ <slot name="actions" />
72
+ </div>
73
+ </div>
74
+
75
+ @if(showSearch)
76
+ <div class="shrink-0 px-[10px] pb-[6px]">
77
+ <div class="relative">
78
+ <span class="i-f7-search absolute left-[8px] top-1/2 h-[13px] w-[13px] -translate-y-1/2 text-[#3c3c43]/50 dark:text-[#ebebf5]/50" aria-hidden="true"></span>
79
+ <input
80
+ type="text"
81
+ placeholder="{{ searchPlaceholder }}"
82
+ class="h-[28px] w-full rounded-[8px] border-0 bg-black/5 pl-[26px] pr-[8px] text-[13px] text-black placeholder:text-[#3c3c43]/50 focus:outline-none focus:ring-2 focus:ring-[#0088ff]/60 dark:bg-white/10 dark:text-white dark:placeholder:text-[#ebebf5]/50"
83
+ @input="onSearchInput($event)"
84
+ />
85
+ </div>
86
+ </div>
87
+ @endif
88
+ @else
89
+ <div class="flex h-13 items-center gap-3 border-b border-black/5 px-4 dark:border-white/10">
90
+ @if(logo)
91
+ <img src="{{ logo }}" alt="{{ title }}" class="h-8 w-8 rounded-lg object-cover" />
92
+ @endif
93
+ <div class="min-w-0">
94
+ @if(title)
95
+ <div class="truncate text-sm font-semibold text-black/90 dark:text-white">{{ title }}</div>
96
+ @endif
97
+ @if(subtitle)
98
+ <div class="truncate text-xs text-black/50 dark:text-white/50">{{ subtitle }}</div>
99
+ @endif
100
+ </div>
101
+ <slot name="actions" />
102
+ </div>
103
+ @endif
@@ -0,0 +1,90 @@
1
+ <script server>
2
+ import { resolveIconColor, resolveSidebarTheme } from './themes'
3
+
4
+ export const id = $props.id || ''
5
+ export const label = $props.label || ''
6
+ export const icon = $props.icon || ''
7
+ export const iconColor = $props.iconColor || ''
8
+ export const image = $props.image || ''
9
+ export const href = $props.href || ''
10
+ export const count = $props.count ?? $props.badge ?? ''
11
+ export const active = $props.active ?? false
12
+ export const disabled = $props.disabled ?? false
13
+ export const expandable = $props.expandable ?? false
14
+ export const expanded = $props.expanded ?? true
15
+ export const depth = $props.depth || 0
16
+ export const parents = $props.parents || ''
17
+ export const theme = $props.theme || $props.variant || 'macos'
18
+
19
+ const themeStyle = resolveSidebarTheme(theme).item
20
+
21
+ export const rowClasses = [
22
+ themeStyle.base,
23
+ disabled ? themeStyle.disabled : themeStyle.hover,
24
+ disabled ? '' : themeStyle.pressed,
25
+ active ? themeStyle.active : '',
26
+ ].filter(Boolean).join(' ')
27
+
28
+ export const disclosureClasses = themeStyle.disclosure
29
+ export const chevronClasses = themeStyle.chevron
30
+ export const iconSlotClasses = themeStyle.iconSlot
31
+ export const iconClasses = `${icon} ${themeStyle.icon}`
32
+ export const imageClasses = themeStyle.image
33
+ export const labelClasses = themeStyle.label
34
+ export const countClasses = themeStyle.count
35
+
36
+ // Nested rows are indented; the icon tint accepts macOS system color names
37
+ // ("blue", "yellow", …) or any CSS color.
38
+ export const indentStyle = depth > 0 ? `padding-left: ${depth * themeStyle.indentPerLevel}px` : ''
39
+ // The tint is published as a custom property rather than `color:` directly.
40
+ // An inline `color` would outrank any stylesheet rule, so a selected row
41
+ // could never turn its icon white the way AppKit does, and toggling
42
+ // selection on the client could not restore the original tint afterwards.
43
+ const tint = resolveIconColor(iconColor)
44
+ export const iconStyle = tint ? `--stx-sidebar-icon: ${tint}` : ''
45
+ export const isLink = Boolean(href) && !disabled
46
+ </script>
47
+
48
+ <div
49
+ data-sidebar-row
50
+ data-item-id="{{ id }}"
51
+ @if(parents)data-parents="{{ parents }}"@endif
52
+ @if(expandable)data-expanded="{{ expanded ? 'true' : 'false' }}"@endif
53
+ @if(indentStyle)style="{{ indentStyle }}"@endif
54
+ >
55
+ @if(isLink)
56
+ <a href="{{ href }}" data-sidebar-item data-label="{{ label }}" class="{{ rowClasses }}" @if(active)data-active="true" aria-current="page"@endif>
57
+ <span class="{{ disclosureClasses }}" @if(expandable)data-sidebar-disclosure role="button" aria-label="Toggle {{ label }}"@endif>
58
+ @if(expandable)
59
+ <span data-sidebar-chevron class="{{ chevronClasses }}" aria-hidden="true"></span>
60
+ @endif
61
+ </span>
62
+ @if(image)
63
+ <span class="{{ iconSlotClasses }}"><img src="{{ image }}" alt="" class="{{ imageClasses }}" /></span>
64
+ @elseif(icon)
65
+ <span class="{{ iconSlotClasses }}"><span data-sidebar-icon class="{{ iconClasses }}" @if(iconStyle)style="{{ iconStyle }}"@endif aria-hidden="true"></span></span>
66
+ @endif
67
+ <span class="{{ labelClasses }}">{{ label }}</span>
68
+ @if(count !== '' && count !== null && count !== undefined)
69
+ <span data-sidebar-count class="{{ countClasses }}">{{ count }}</span>
70
+ @endif
71
+ </a>
72
+ @else
73
+ <button type="button" data-sidebar-item data-label="{{ label }}" class="{{ rowClasses }}" @if(active)data-active="true" aria-current="page"@endif @if(disabled)data-disabled disabled aria-disabled="true"@endif>
74
+ <span class="{{ disclosureClasses }}" @if(expandable)data-sidebar-disclosure role="button" aria-label="Toggle {{ label }}"@endif>
75
+ @if(expandable)
76
+ <span data-sidebar-chevron class="{{ chevronClasses }}" aria-hidden="true"></span>
77
+ @endif
78
+ </span>
79
+ @if(image)
80
+ <span class="{{ iconSlotClasses }}"><img src="{{ image }}" alt="" class="{{ imageClasses }}" /></span>
81
+ @elseif(icon)
82
+ <span class="{{ iconSlotClasses }}"><span data-sidebar-icon class="{{ iconClasses }}" @if(iconStyle)style="{{ iconStyle }}"@endif aria-hidden="true"></span></span>
83
+ @endif
84
+ <span class="{{ labelClasses }}">{{ label }}</span>
85
+ @if(count !== '' && count !== null && count !== undefined)
86
+ <span data-sidebar-count class="{{ countClasses }}">{{ count }}</span>
87
+ @endif
88
+ </button>
89
+ @endif
90
+ </div>
@@ -0,0 +1,83 @@
1
+ <script server>
2
+ import { resolveSidebarTheme } from './themes'
3
+
4
+ export const id = $props.id || ''
5
+ export const label = $props.label || ''
6
+ export const items = $props.items || []
7
+ export const collapsible = $props.collapsible ?? true
8
+ export const collapsed = $props.collapsed ?? false
9
+ export const theme = $props.theme || $props.variant || 'macos'
10
+
11
+ const themeStyle = resolveSidebarTheme(theme)
12
+ export const headerClasses = themeStyle.sectionHeader + (collapsible ? ' cursor-default' : '')
13
+ export const chevronClasses = themeStyle.sectionChevron
14
+ export const groupClasses = themeStyle.sectionGroup
15
+
16
+ // Components can't render themselves recursively, so the item tree is
17
+ // flattened into ready-to-render rows here. Each row remembers its depth
18
+ // and ancestor ids — the Sidebar controller uses those to collapse whole
19
+ // subtrees.
20
+ function flatten(list, depth = 0, parents = []) {
21
+ return list.flatMap((item) => {
22
+ const children = item.children || []
23
+ const row = {
24
+ id: item.id,
25
+ label: item.label,
26
+ icon: item.icon || '',
27
+ iconColor: item.iconColor || '',
28
+ image: item.image || '',
29
+ href: item.href || '',
30
+ count: item.count ?? item.badge ?? '',
31
+ active: item.active === true,
32
+ disabled: item.disabled === true,
33
+ expandable: children.length > 0 || item.expandable === true,
34
+ expanded: item.expanded !== false,
35
+ depth,
36
+ parents: parents.join('/'),
37
+ }
38
+ return [row, ...flatten(children, depth + 1, [...parents, item.id])]
39
+ })
40
+ }
41
+
42
+ export const rows = flatten(items)
43
+ </script>
44
+
45
+ <section
46
+ data-sidebar-section
47
+ data-section-id="{{ id }}"
48
+ data-expanded="{{ collapsed ? 'false' : 'true' }}"
49
+ >
50
+ @if(label)
51
+ @if(collapsible)
52
+ <button type="button" data-sidebar-section-header class="{{ headerClasses }}" aria-expanded="{{ collapsed ? 'false' : 'true' }}">
53
+ <span class="truncate">{{ label }}</span>
54
+ <span data-sidebar-section-chevron class="{{ chevronClasses }}" aria-hidden="true"></span>
55
+ </button>
56
+ @else
57
+ <div class="{{ headerClasses }}">
58
+ <span class="truncate">{{ label }}</span>
59
+ </div>
60
+ @endif
61
+ @endif
62
+
63
+ <div class="{{ groupClasses }}" role="group" @if(label)aria-label="{{ label }}"@endif>
64
+ @foreach(rows as row)
65
+ <SidebarItem
66
+ :id="row.id"
67
+ :label="row.label"
68
+ :icon="row.icon"
69
+ :iconColor="row.iconColor"
70
+ :image="row.image"
71
+ :href="row.href"
72
+ :count="row.count"
73
+ :active="row.active"
74
+ :disabled="row.disabled"
75
+ :expandable="row.expandable"
76
+ :expanded="row.expanded"
77
+ :depth="row.depth"
78
+ :parents="row.parents"
79
+ :theme="theme"
80
+ />
81
+ @endforeach
82
+ </div>
83
+ </section>
@@ -0,0 +1,146 @@
1
+ export { default as Sidebar } from './Sidebar.stx'
2
+ export { default as SidebarFooter } from './SidebarFooter.stx'
3
+ export { default as SidebarHeader } from './SidebarHeader.stx'
4
+ export { default as SidebarItem } from './SidebarItem.stx'
5
+ export { default as SidebarSection } from './SidebarSection.stx'
6
+ export * from './themes'
7
+
8
+ /**
9
+ * One navigation row.
10
+ *
11
+ * ```ts
12
+ * { id: 'icloud', label: 'iCloud', icon: 'i-f7-tray', iconColor: 'blue', count: 248, active: true }
13
+ * ```
14
+ */
15
+ export interface SidebarItemData {
16
+ id: string
17
+ label: string
18
+ /** Iconify utility class, e.g. `i-f7-tray`. F7 icons mirror SF Symbols. */
19
+ icon?: string
20
+ /** macOS system color name (`"blue"`, `"red"`, `"yellow"`, …) or any CSS color. */
21
+ iconColor?: string
22
+ /** Image URL rendered instead of an icon (album art, avatars). */
23
+ image?: string
24
+ href?: string
25
+ /** Right-aligned count — rendered as plain gray text like native macOS. */
26
+ count?: string | number
27
+ /** @deprecated Use `count`. */
28
+ badge?: string | number
29
+ active?: boolean
30
+ disabled?: boolean
31
+ /** Nested rows, indented and collapsible under this one. */
32
+ children?: SidebarItemData[]
33
+ /** Show a disclosure chevron even without children. */
34
+ expandable?: boolean
35
+ /** Initial disclosure state when the item has children. Defaults to true. */
36
+ expanded?: boolean
37
+ }
38
+
39
+ /** A titled group of rows (e.g. "Favorites"). Untitled when `label` is empty. */
40
+ export interface SidebarSectionData {
41
+ id: string
42
+ label?: string
43
+ items: SidebarItemData[]
44
+ /** Section headers collapse their group on click. Defaults to true. */
45
+ collapsible?: boolean
46
+ /** Initial collapse state. Defaults to false (expanded). */
47
+ collapsed?: boolean
48
+ }
49
+
50
+ /**
51
+ * Sidebar themes. `macos` recreates the sidebar of the latest macOS
52
+ * (Tahoe, macOS 26/27) — translucent material, Liquid Glass edge shimmer,
53
+ * 30px rows with 9px-radius highlights and plain gray counts. `tahoe`,
54
+ * `macos-tahoe` and `macos-latest` are aliases of `macos`. The remaining
55
+ * names are legacy looks kept for backwards compatibility.
56
+ */
57
+ type MacOSSidebarTheme = 'macos' | 'macos-tahoe' | 'macos-latest' | 'tahoe'
58
+ type LegacySidebarTheme = 'vibrancy' | 'solid' | 'transparent' | 'workspace' | 'desktop'
59
+ export type SidebarThemeChoice = MacOSSidebarTheme | LegacySidebarTheme
60
+
61
+ export interface SidebarProps {
62
+ /** Sections with their items. Omit to compose children via the default slot. */
63
+ sections?: SidebarSectionData[]
64
+ /** Visual theme. Defaults to `macos`. */
65
+ theme?: SidebarThemeChoice
66
+ /** @deprecated Use `theme`. */
67
+ variant?: SidebarThemeChoice
68
+ /** Expanded width in pixels. Defaults to 250 (native macOS default). */
69
+ width?: number
70
+ position?: 'left' | 'right'
71
+ /** Layout placement for app shells. Defaults to `fixed`. */
72
+ placement?: 'fixed' | 'sticky' | 'static'
73
+ bordered?: boolean
74
+ /** Whether the sidebar starts collapsed. */
75
+ collapsed?: boolean
76
+ collapsible?: boolean
77
+ /** `hidden` slides away entirely (macOS); `rail` keeps a compact strip. */
78
+ collapseMode?: 'hidden' | 'rail'
79
+ /** Rail width when `collapseMode` is `rail`. */
80
+ minWidth?: number
81
+ /** localStorage key that persists collapse state. */
82
+ persistKey?: string
83
+ /** App-shell selector whose width CSS variable tracks the sidebar. */
84
+ shellSelector?: string
85
+ widthVar?: string
86
+ collapsedClass?: string
87
+ className?: string
88
+ onCollapse?: (collapsed: boolean) => void
89
+ onSectionToggle?: (sectionId: string) => void
90
+ onItemToggle?: (event: { id: string, expanded: boolean }) => void
91
+ onItemClick?: (item: SidebarItemData, event: Event) => void
92
+ }
93
+
94
+ export interface SidebarSectionProps {
95
+ id: string
96
+ label?: string
97
+ items: SidebarItemData[]
98
+ collapsible?: boolean
99
+ collapsed?: boolean
100
+ theme?: SidebarThemeChoice
101
+ }
102
+
103
+ export interface SidebarItemProps extends SidebarItemData {
104
+ /** Nesting depth — set by SidebarSection when flattening the tree. */
105
+ depth?: number
106
+ /** Slash-joined ancestor ids — set by SidebarSection. */
107
+ parents?: string
108
+ theme?: SidebarThemeChoice
109
+ }
110
+
111
+ /** A floating toolbar or footer action button. */
112
+ export interface SidebarActionData {
113
+ id: string
114
+ /** Iconify utility class. */
115
+ icon: string
116
+ /** Accessible label. */
117
+ label: string
118
+ }
119
+
120
+ export interface SidebarHeaderProps {
121
+ theme?: SidebarThemeChoice
122
+ /** Render traffic lights (wired to Craft's window API when present). */
123
+ showWindowControls?: boolean
124
+ /** Floating Liquid Glass toolbar buttons on the right. */
125
+ actions?: SidebarActionData[]
126
+ showSearch?: boolean
127
+ searchPlaceholder?: string
128
+ /** Legacy (non-macos) header content. */
129
+ title?: string
130
+ subtitle?: string
131
+ logo?: string
132
+ onAction?: (actionId: string) => void
133
+ onSearch?: (value: string) => void
134
+ onWindowControl?: (action: 'close' | 'minimize' | 'zoom') => void
135
+ }
136
+
137
+ export interface SidebarFooterProps {
138
+ theme?: SidebarThemeChoice
139
+ /** Account row, like Music's profile footer. */
140
+ avatar?: string
141
+ name?: string
142
+ detail?: string
143
+ actions?: SidebarActionData[]
144
+ onProfileClick?: () => void
145
+ onAction?: (actionId: string) => void
146
+ }