@adea-ai/ui 0.69.1 → 0.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/README.md +14 -9
  2. package/dist/components/composites/appearance-editor/appearance-editor.d.ts +1 -1
  3. package/dist/components/composites/appearance-editor/appearance-editor.js +1 -1
  4. package/dist/components/composites/appearance-editor/appearance-editor.js.map +1 -1
  5. package/dist/components/composites/appearance-editor/appearance-rows.d.ts +7 -0
  6. package/dist/components/composites/appearance-editor/appearance-rows.d.ts.map +1 -1
  7. package/dist/components/composites/appearance-editor/appearance-rows.js +187 -41
  8. package/dist/components/composites/appearance-editor/appearance-rows.js.map +1 -1
  9. package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts +6 -4
  10. package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts.map +1 -1
  11. package/dist/components/ui/dropdown-menu/dropdown-menu.js +21 -15
  12. package/dist/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
  13. package/dist/r/appearance-editor.json +1 -1
  14. package/dist/r/registry.json +1 -1
  15. package/dist/r/src/components/composites/appearance-editor/appearance-editor.tsx +1 -1
  16. package/dist/r/src/components/composites/appearance-editor/appearance-rows.tsx +231 -34
  17. package/dist/r/src/components/ui/dropdown-menu/dropdown-menu.tsx +8 -6
  18. package/package.json +1 -1
  19. package/registry.json +1 -1
  20. package/src/components/composites/appearance-editor/appearance-editor.tsx +1 -1
  21. package/src/components/composites/appearance-editor/appearance-rows.tsx +231 -34
  22. package/src/components/ui/dropdown-menu/dropdown-menu.tsx +8 -6
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown-menu.js","names":["DropdownMenu","KobalteDropdownMenu","Check","ChevronRight","Circle","ComponentProps","splitProps","menuContentPadding","menuItem","menuLabel","menuSeparator","overlayMotion","overlaySurface","popoverArrow","cn","props","_$createComponent","DropdownMenuTrigger","Trigger","DropdownMenuPortal","Portal","DropdownMenuContentProps","Content","hideArrow","DropdownMenuContent","local","rest","children","_$mergeProps","class","_$memo","Arrow","DropdownMenuItem","Item","variant","shortcut","_el$","_tmpl$","_$insert","DropdownMenuShortcut","_el$2","_tmpl$2","_$spread","DropdownMenuCheckboxItem","CheckboxItem","ItemIndicator","DropdownMenuRadioGroup","RadioGroup","DropdownMenuRadioItem","RadioItem","DropdownMenuLabel","GroupLabel","DropdownMenuGroup","Group","DropdownMenuSeparator","Separator","DropdownMenuSub","Sub","DropdownMenuSubTrigger","SubTrigger","DropdownMenuSubContent","SubContent","DropdownMenu","_$createComponent","KobalteDropdownMenu","_$mergeProps","_$memo"],"sources":["../../../../src/components/ui/dropdown-menu/dropdown-menu.tsx"],"sourcesContent":["import { DropdownMenu as KobalteDropdownMenu } from '@kobalte/core/dropdown-menu'\nimport { Check, ChevronRight, Circle } from 'lucide-solid'\nimport type { ComponentProps } from 'solid-js'\nimport { splitProps } from 'solid-js'\nimport {\n menuContentPadding,\n menuItem,\n menuLabel,\n menuSeparator,\n overlayMotion,\n overlaySurface,\n popoverArrow,\n} from '#lib/overlay'\nimport { cn } from '#lib/utils'\n\n/**\n * DropdownMenu.\n *\n * A list of actions on a trigger. This is the right home for \"what can I do\n * with this thing\": rename, duplicate, export, delete. It is not a Select\n * (which holds a *value* the user is choosing) and not a Popover (which holds\n * arbitrary content).\n *\n * Kobalte supplies the full menu contract — arrow keys, typeahead, the roving\n * highlight, Escape to close and return focus — which is exactly what a\n * hand-rolled `div` with click handlers gets wrong.\n *\n * The `destructive` variant on an item is a *colour*, not a confirmation.\n * Pairing it with an AlertDialog is the caller's job.\n */\nexport function DropdownMenu(props: ComponentProps<typeof KobalteDropdownMenu>) {\n return <KobalteDropdownMenu {...props} />\n}\n\nexport function DropdownMenuTrigger(props: ComponentProps<typeof KobalteDropdownMenu.Trigger>) {\n return <KobalteDropdownMenu.Trigger {...props} />\n}\n\nexport function DropdownMenuPortal(props: ComponentProps<typeof KobalteDropdownMenu.Portal>) {\n return <KobalteDropdownMenu.Portal {...props} />\n}\n\nexport type DropdownMenuContentProps = ComponentProps<typeof KobalteDropdownMenu.Content> & {\n hideArrow?: boolean\n}\n\nexport function DropdownMenuContent(props: DropdownMenuContentProps) {\n const [local, rest] = splitProps(props, ['class', 'hideArrow', 'children'])\n\n return (\n <KobalteDropdownMenu.Portal>\n <KobalteDropdownMenu.Content\n class={cn(\n overlaySurface,\n overlayMotion,\n 'z-(--z-menu) min-w-[10rem] origin-(--kb-menu-content-transform-origin) overflow-hidden',\n menuContentPadding,\n local.class\n )}\n {...rest}\n >\n {!local.hideArrow && <KobalteDropdownMenu.Arrow aria-hidden=\"true\" class={popoverArrow} />}\n {local.children}\n </KobalteDropdownMenu.Content>\n </KobalteDropdownMenu.Portal>\n )\n}\n\nexport function DropdownMenuItem(\n props: ComponentProps<typeof KobalteDropdownMenu.Item> & {\n /** Colour the row as a destructive action. Confirm it separately. */\n variant?: 'default' | 'destructive'\n /** Draw a shortcut hint at the trailing edge of the row. */\n shortcut?: string\n }\n) {\n const [local, rest] = splitProps(props, ['class', 'variant', 'shortcut', 'children'])\n\n return (\n <KobalteDropdownMenu.Item\n class={cn(\n menuItem,\n {\n 'text-destructive data-[highlighted]:bg-destructive-subtle data-[highlighted]:text-destructive':\n local.variant === 'destructive',\n },\n local.class\n )}\n {...rest}\n >\n {local.children}\n {local.shortcut ? (\n <span class=\"text-muted-foreground ms-auto font-mono text-2xs tracking-widest\">\n {local.shortcut}\n </span>\n ) : null}\n </KobalteDropdownMenu.Item>\n )\n}\n\n/**\n * The chord drawn at a row's trailing edge.\n *\n * A component as well as the `shortcut` prop on an item, because a caller composing\n * a row by hand needs the same treatment — and because the alternative is every\n * caller inventing the same mono-and-dimmed span.\n */\nexport function DropdownMenuShortcut(props: ComponentProps<'span'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n class={cn('text-muted-foreground ms-auto font-mono text-2xs tracking-widest', local.class)}\n {...rest}\n />\n )\n}\n\nexport function DropdownMenuCheckboxItem(\n props: ComponentProps<typeof KobalteDropdownMenu.CheckboxItem>\n) {\n const [local, rest] = splitProps(props, ['class', 'children'])\n\n return (\n <KobalteDropdownMenu.CheckboxItem class={cn(menuItem, 'pe-8', local.class)} {...rest}>\n {local.children}\n <KobalteDropdownMenu.ItemIndicator class=\"absolute end-2 flex size-4 items-center justify-center\">\n <Check class=\"size-4\" />\n </KobalteDropdownMenu.ItemIndicator>\n </KobalteDropdownMenu.CheckboxItem>\n )\n}\n\nexport function DropdownMenuRadioGroup(\n props: ComponentProps<typeof KobalteDropdownMenu.RadioGroup>\n) {\n return <KobalteDropdownMenu.RadioGroup {...props} />\n}\n\nexport function DropdownMenuRadioItem(props: ComponentProps<typeof KobalteDropdownMenu.RadioItem>) {\n const [local, rest] = splitProps(props, ['class', 'children'])\n\n return (\n <KobalteDropdownMenu.RadioItem class={cn(menuItem, 'pe-8', local.class)} {...rest}>\n {local.children}\n <KobalteDropdownMenu.ItemIndicator class=\"absolute end-2 flex size-4 items-center justify-center\">\n <Circle class=\"size-2 fill-current\" />\n </KobalteDropdownMenu.ItemIndicator>\n </KobalteDropdownMenu.RadioItem>\n )\n}\n\nexport function DropdownMenuLabel(props: ComponentProps<typeof KobalteDropdownMenu.GroupLabel>) {\n const [local, rest] = splitProps(props, ['class'])\n return <KobalteDropdownMenu.GroupLabel class={cn(menuLabel, local.class)} {...rest} />\n}\n\nexport function DropdownMenuGroup(props: ComponentProps<typeof KobalteDropdownMenu.Group>) {\n return <KobalteDropdownMenu.Group {...props} />\n}\n\nexport function DropdownMenuSeparator(props: ComponentProps<typeof KobalteDropdownMenu.Separator>) {\n const [local, rest] = splitProps(props, ['class'])\n return <KobalteDropdownMenu.Separator class={cn(menuSeparator, local.class)} {...rest} />\n}\n\nexport function DropdownMenuSub(props: ComponentProps<typeof KobalteDropdownMenu.Sub>) {\n return <KobalteDropdownMenu.Sub {...props} />\n}\n\nexport function DropdownMenuSubTrigger(\n props: ComponentProps<typeof KobalteDropdownMenu.SubTrigger>\n) {\n const [local, rest] = splitProps(props, ['class', 'children'])\n\n return (\n <KobalteDropdownMenu.SubTrigger class={cn(menuItem, local.class)} {...rest}>\n {local.children}\n <ChevronRight class=\"ms-auto size-4\" />\n </KobalteDropdownMenu.SubTrigger>\n )\n}\n\nexport function DropdownMenuSubContent(\n props: ComponentProps<typeof KobalteDropdownMenu.SubContent>\n) {\n const [local, rest] = splitProps(props, ['class'])\n\n return (\n <KobalteDropdownMenu.Portal>\n <KobalteDropdownMenu.SubContent\n class={cn(\n overlaySurface,\n overlayMotion,\n 'z-(--z-menu) min-w-[8rem] origin-(--kb-menu-content-transform-origin) overflow-hidden',\n menuContentPadding,\n local.class\n )}\n {...rest}\n />\n </KobalteDropdownMenu.Portal>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dropdown-menu.js","names":["DropdownMenu","KobalteDropdownMenu","Check","ChevronRight","Circle","ComponentProps","splitProps","menuContentPadding","menuItem","menuLabel","menuSeparator","overlayMotion","overlaySurface","popoverArrow","cn","props","_$createComponent","DropdownMenuTrigger","Trigger","DropdownMenuPortal","Portal","DropdownMenuContentProps","Content","hideArrow","portalMount","HTMLElement","DropdownMenuContent","local","rest","mount","children","_$mergeProps","class","_$memo","Arrow","DropdownMenuItem","Item","variant","shortcut","_el$","_tmpl$","_$insert","DropdownMenuShortcut","_el$2","_tmpl$2","_$spread","DropdownMenuCheckboxItem","CheckboxItem","ItemIndicator","DropdownMenuRadioGroup","RadioGroup","DropdownMenuRadioItem","RadioItem","DropdownMenuLabel","GroupLabel","DropdownMenuGroup","Group","DropdownMenuSeparator","Separator","DropdownMenuSub","Sub","DropdownMenuSubTrigger","SubTrigger","DropdownMenuSubContent","SubContent","DropdownMenu","_$createComponent","KobalteDropdownMenu","_$mergeProps","_$memo"],"sources":["../../../../src/components/ui/dropdown-menu/dropdown-menu.tsx"],"sourcesContent":["import { DropdownMenu as KobalteDropdownMenu } from '@kobalte/core/dropdown-menu'\nimport { Check, ChevronRight, Circle } from 'lucide-solid'\nimport type { ComponentProps } from 'solid-js'\nimport { splitProps } from 'solid-js'\nimport {\n menuContentPadding,\n menuItem,\n menuLabel,\n menuSeparator,\n overlayMotion,\n overlaySurface,\n popoverArrow,\n} from '#lib/overlay'\nimport { cn } from '#lib/utils'\n\n/**\n * DropdownMenu.\n *\n * A list of actions on a trigger, with radio groups for persistent choices\n * that fit naturally in a compact menu. Use Select for form inputs whose\n * selected value needs the field-style control, and Popover for arbitrary\n * content.\n *\n * Kobalte supplies the full menu contract — arrow keys, typeahead, the roving\n * highlight, Escape to close and return focus — which is exactly what a\n * hand-rolled `div` with click handlers gets wrong.\n *\n * The `destructive` variant on an item is a *colour*, not a confirmation.\n * Pairing it with an AlertDialog is the caller's job.\n */\nexport function DropdownMenu(props: ComponentProps<typeof KobalteDropdownMenu>) {\n return <KobalteDropdownMenu {...props} />\n}\n\nexport function DropdownMenuTrigger(props: ComponentProps<typeof KobalteDropdownMenu.Trigger>) {\n return <KobalteDropdownMenu.Trigger {...props} />\n}\n\nexport function DropdownMenuPortal(props: ComponentProps<typeof KobalteDropdownMenu.Portal>) {\n return <KobalteDropdownMenu.Portal {...props} />\n}\n\nexport type DropdownMenuContentProps = ComponentProps<typeof KobalteDropdownMenu.Content> & {\n hideArrow?: boolean\n /** Mount under an owner element such as a modal, keeping the menu in its accessible subtree. */\n portalMount?: HTMLElement\n}\n\nexport function DropdownMenuContent(props: DropdownMenuContentProps) {\n const [local, rest] = splitProps(props, ['class', 'hideArrow', 'children', 'portalMount'])\n\n return (\n <KobalteDropdownMenu.Portal mount={local.portalMount}>\n <KobalteDropdownMenu.Content\n class={cn(\n overlaySurface,\n overlayMotion,\n 'z-(--z-menu) min-w-[10rem] origin-(--kb-menu-content-transform-origin) overflow-hidden',\n menuContentPadding,\n local.class\n )}\n {...rest}\n >\n {!local.hideArrow && <KobalteDropdownMenu.Arrow aria-hidden=\"true\" class={popoverArrow} />}\n {local.children}\n </KobalteDropdownMenu.Content>\n </KobalteDropdownMenu.Portal>\n )\n}\n\nexport function DropdownMenuItem(\n props: ComponentProps<typeof KobalteDropdownMenu.Item> & {\n /** Colour the row as a destructive action. Confirm it separately. */\n variant?: 'default' | 'destructive'\n /** Draw a shortcut hint at the trailing edge of the row. */\n shortcut?: string\n }\n) {\n const [local, rest] = splitProps(props, ['class', 'variant', 'shortcut', 'children'])\n\n return (\n <KobalteDropdownMenu.Item\n class={cn(\n menuItem,\n {\n 'text-destructive data-[highlighted]:bg-destructive-subtle data-[highlighted]:text-destructive':\n local.variant === 'destructive',\n },\n local.class\n )}\n {...rest}\n >\n {local.children}\n {local.shortcut ? (\n <span class=\"text-muted-foreground ms-auto font-mono text-2xs tracking-widest\">\n {local.shortcut}\n </span>\n ) : null}\n </KobalteDropdownMenu.Item>\n )\n}\n\n/**\n * The chord drawn at a row's trailing edge.\n *\n * A component as well as the `shortcut` prop on an item, because a caller composing\n * a row by hand needs the same treatment — and because the alternative is every\n * caller inventing the same mono-and-dimmed span.\n */\nexport function DropdownMenuShortcut(props: ComponentProps<'span'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n class={cn('text-muted-foreground ms-auto font-mono text-2xs tracking-widest', local.class)}\n {...rest}\n />\n )\n}\n\nexport function DropdownMenuCheckboxItem(\n props: ComponentProps<typeof KobalteDropdownMenu.CheckboxItem>\n) {\n const [local, rest] = splitProps(props, ['class', 'children'])\n\n return (\n <KobalteDropdownMenu.CheckboxItem class={cn(menuItem, 'pe-8', local.class)} {...rest}>\n {local.children}\n <KobalteDropdownMenu.ItemIndicator class=\"absolute end-2 flex size-4 items-center justify-center\">\n <Check class=\"size-4\" />\n </KobalteDropdownMenu.ItemIndicator>\n </KobalteDropdownMenu.CheckboxItem>\n )\n}\n\nexport function DropdownMenuRadioGroup(\n props: ComponentProps<typeof KobalteDropdownMenu.RadioGroup>\n) {\n return <KobalteDropdownMenu.RadioGroup {...props} />\n}\n\nexport function DropdownMenuRadioItem(props: ComponentProps<typeof KobalteDropdownMenu.RadioItem>) {\n const [local, rest] = splitProps(props, ['class', 'children'])\n\n return (\n <KobalteDropdownMenu.RadioItem class={cn(menuItem, 'pe-8', local.class)} {...rest}>\n {local.children}\n <KobalteDropdownMenu.ItemIndicator class=\"absolute end-2 flex size-4 items-center justify-center\">\n <Circle class=\"size-2 fill-current\" />\n </KobalteDropdownMenu.ItemIndicator>\n </KobalteDropdownMenu.RadioItem>\n )\n}\n\nexport function DropdownMenuLabel(props: ComponentProps<typeof KobalteDropdownMenu.GroupLabel>) {\n const [local, rest] = splitProps(props, ['class'])\n return <KobalteDropdownMenu.GroupLabel class={cn(menuLabel, local.class)} {...rest} />\n}\n\nexport function DropdownMenuGroup(props: ComponentProps<typeof KobalteDropdownMenu.Group>) {\n return <KobalteDropdownMenu.Group {...props} />\n}\n\nexport function DropdownMenuSeparator(props: ComponentProps<typeof KobalteDropdownMenu.Separator>) {\n const [local, rest] = splitProps(props, ['class'])\n return <KobalteDropdownMenu.Separator class={cn(menuSeparator, local.class)} {...rest} />\n}\n\nexport function DropdownMenuSub(props: ComponentProps<typeof KobalteDropdownMenu.Sub>) {\n return <KobalteDropdownMenu.Sub {...props} />\n}\n\nexport function DropdownMenuSubTrigger(\n props: ComponentProps<typeof KobalteDropdownMenu.SubTrigger>\n) {\n const [local, rest] = splitProps(props, ['class', 'children'])\n\n return (\n <KobalteDropdownMenu.SubTrigger class={cn(menuItem, local.class)} {...rest}>\n {local.children}\n <ChevronRight class=\"ms-auto size-4\" />\n </KobalteDropdownMenu.SubTrigger>\n )\n}\n\nexport function DropdownMenuSubContent(\n props: ComponentProps<typeof KobalteDropdownMenu.SubContent>\n) {\n const [local, rest] = splitProps(props, ['class'])\n\n return (\n <KobalteDropdownMenu.Portal>\n <KobalteDropdownMenu.SubContent\n class={cn(\n overlaySurface,\n overlayMotion,\n 'z-(--z-menu) min-w-[8rem] origin-(--kb-menu-content-transform-origin) overflow-hidden',\n menuContentPadding,\n local.class\n )}\n {...rest}\n />\n </KobalteDropdownMenu.Portal>\n )\n}\n"],"mappings":"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"}
@@ -10,9 +10,9 @@
10
10
  ],
11
11
  "registryDependencies": [
12
12
  "@adea-ai/ui/button",
13
+ "@adea-ai/ui/dropdown-menu",
13
14
  "@adea-ai/ui/input",
14
15
  "@adea-ai/ui/popover",
15
- "@adea-ai/ui/select",
16
16
  "@adea-ai/ui/switch",
17
17
  "lib"
18
18
  ],
@@ -1652,9 +1652,9 @@
1652
1652
  ],
1653
1653
  "registryDependencies": [
1654
1654
  "@adea-ai/ui/button",
1655
+ "@adea-ai/ui/dropdown-menu",
1655
1656
  "@adea-ai/ui/input",
1656
1657
  "@adea-ai/ui/popover",
1657
- "@adea-ai/ui/select",
1658
1658
  "@adea-ai/ui/switch",
1659
1659
  "lib"
1660
1660
  ],
@@ -208,7 +208,7 @@ export function AppearanceEditor(props: AppearanceEditorProps) {
208
208
  )
209
209
  }
210
210
 
211
- /** Modal anchoring/dismissal and nested-listbox behavior come from Kobalte.
211
+ /** Modal anchoring/dismissal and nested-menu behavior come from Kobalte.
212
212
  * Its measured available height also bounds scrolling, keeping footer actions
213
213
  * reachable when a narrow popup begins below its trigger. */
214
214
  export function AppearancePopover(props: AppearancePopoverProps) {
@@ -24,18 +24,33 @@
24
24
  // Retains the compact divided row and palette-preview selector from the pinned
25
25
  // Zeron/Adea composition; Kobalte owns option navigation and nested dismissal.
26
26
  import type { AdeaTheme, AdeaThemeRecord } from '@adea-ai/themes'
27
- import { Show, createMemo, createSignal, type JSX } from 'solid-js'
28
- import { Palette } from 'lucide-solid'
27
+ import { For, Show, createMemo, createSignal, onCleanup, type JSX } from 'solid-js'
28
+ import { ChevronDown, Palette } from 'lucide-solid'
29
29
  import { cn } from '../../../lib/utils'
30
+ import { Button } from '../../ui/button/button'
30
31
  import {
31
- Select,
32
- SelectContent,
33
- SelectItem,
34
- SelectTrigger,
35
- SelectValue,
36
- } from '../../ui/select/select'
32
+ DropdownMenu,
33
+ DropdownMenuContent,
34
+ DropdownMenuRadioGroup,
35
+ DropdownMenuRadioItem,
36
+ DropdownMenuTrigger,
37
+ } from '../../ui/dropdown-menu/dropdown-menu'
37
38
  import { PalettePreview } from './theme-preview'
38
39
 
40
+ const POINTER_FOCUS_TARGET_SELECTOR =
41
+ 'a[href], area[href], button, input, select, textarea, iframe, [tabindex], [contenteditable], details > summary:first-of-type'
42
+
43
+ function isTabStop(element: HTMLElement) {
44
+ return (
45
+ (element.tabIndex >= 0 ||
46
+ (element.matches('details > summary:first-of-type') && !element.hasAttribute('tabindex'))) &&
47
+ !element.matches(':disabled') &&
48
+ element.getAttribute('aria-disabled') !== 'true' &&
49
+ element.closest('[hidden], [inert], [aria-hidden="true"]') === null &&
50
+ element.getClientRects().length > 0
51
+ )
52
+ }
53
+
39
54
  export function SettingsRow(props: {
40
55
  title: string
41
56
  icon: JSX.Element
@@ -66,6 +81,13 @@ export function SettingsRow(props: {
66
81
  )
67
82
  }
68
83
 
84
+ /**
85
+ * A compact persistent theme choice. Kobalte handles menu navigation and
86
+ * dismissal; this row moves Tab to the adjacent browser-reported stop because
87
+ * menus consume Tab by default. Enumerating elements and checking their native
88
+ * tabIndex keeps details summaries in the order while preserving explicit
89
+ * tabindex overrides.
90
+ */
69
91
  export function ThemeRow(props: {
70
92
  appearance: 'light' | 'dark'
71
93
  selectedId: string
@@ -75,6 +97,15 @@ export function ThemeRow(props: {
75
97
  onSelect: (id: string) => void
76
98
  }) {
77
99
  const [portalMount, setPortalMount] = createSignal<HTMLDivElement>()
100
+ const [triggerElement, setTriggerElement] = createSignal<HTMLButtonElement>()
101
+ const [menuOpen, setMenuOpen] = createSignal(false)
102
+ const [focusOutsideTarget, setFocusOutsideTarget] = createSignal<HTMLElement>()
103
+ const [restoreTriggerFocus, setRestoreTriggerFocus] = createSignal(false)
104
+ let focusTargetAfterTab: HTMLElement | undefined
105
+ let focusLifecycleVersion = 0
106
+ onCleanup(() => {
107
+ focusLifecycleVersion += 1
108
+ })
78
109
  const options = createMemo(() =>
79
110
  props.themes.filter((theme) => theme.appearance === props.appearance)
80
111
  )
@@ -84,38 +115,204 @@ export function ThemeRow(props: {
84
115
  options().find((theme) => theme.id === props.preview.id)
85
116
  )
86
117
  const label = () => (props.appearance === 'light' ? 'Light theme' : 'Dark theme')
118
+ const closeMenu = () => {
119
+ if (menuOpen()) focusLifecycleVersion += 1
120
+ setMenuOpen(false)
121
+ }
122
+ const moveFocusOnTab = (event: KeyboardEvent) => {
123
+ if (event.key !== 'Tab' || event.isComposing) return
124
+
125
+ const trigger = triggerElement()
126
+ if (!trigger) return
127
+ const parentDialog = portalMount()?.closest<HTMLElement>('[role="dialog"]')
128
+ const focusScope = parentDialog ?? portalMount()?.ownerDocument.body
129
+ if (!focusScope) return
130
+
131
+ const focusStops = Array.from(focusScope.querySelectorAll<HTMLElement>('*')).filter(
132
+ (element) =>
133
+ isTabStop(element) &&
134
+ element.closest('[role="menu"], [hidden], [inert], [aria-hidden="true"]') === null
135
+ )
136
+ const orderedFocusStops = [
137
+ ...focusStops
138
+ .filter((element) => element.tabIndex > 0)
139
+ .toSorted((a, b) => a.tabIndex - b.tabIndex),
140
+ ...focusStops.filter((element) => element.tabIndex <= 0),
141
+ ]
142
+ const triggerIndex = orderedFocusStops.indexOf(trigger)
143
+ const direction = event.shiftKey ? -1 : 1
144
+ const adjacent =
145
+ orderedFocusStops[triggerIndex + direction] ??
146
+ (parentDialog ? (direction > 0 ? orderedFocusStops[0] : orderedFocusStops.at(-1)) : undefined)
147
+
148
+ event.stopPropagation()
149
+ setRestoreTriggerFocus(false)
150
+ if (adjacent) {
151
+ event.preventDefault()
152
+ focusTargetAfterTab = adjacent
153
+ setFocusOutsideTarget(adjacent)
154
+ closeMenu()
155
+ adjacent.focus({ preventScroll: true })
156
+ } else {
157
+ focusTargetAfterTab = undefined
158
+ setFocusOutsideTarget(undefined)
159
+ closeMenu()
160
+ }
161
+ }
87
162
  return (
88
163
  <SettingsRow title={label()} icon={<Palette />}>
89
164
  <div ref={setPortalMount}>
90
- <Select
165
+ <DropdownMenu
91
166
  modal={false}
92
- options={options()}
93
- value={selected()}
94
- optionValue="id"
95
- optionTextValue="name"
96
- disabled={props.disabled || options().length === 0}
97
- onChange={(theme) => {
98
- if (theme) props.onSelect(theme.id)
167
+ open={menuOpen()}
168
+ onOpenChange={(open) => {
169
+ if (open !== menuOpen()) focusLifecycleVersion += 1
170
+ setMenuOpen(open)
171
+ if (open) {
172
+ focusTargetAfterTab = undefined
173
+ setFocusOutsideTarget(undefined)
174
+ setRestoreTriggerFocus(false)
175
+ }
99
176
  }}
100
- itemComponent={(item) => (
101
- <SelectItem item={item.item}>
102
- <PalettePreview theme={item.item.rawValue} />
103
- {item.item.rawValue.name}
104
- </SelectItem>
105
- )}
106
177
  >
107
- <SelectTrigger size="sm" aria-label={label()} class="w-full sm:w-52">
108
- <SelectValue>
109
- {() => (
110
- <>
111
- <PalettePreview theme={props.preview} />
112
- <span>{selected()?.name ?? props.preview.name}</span>
113
- </>
114
- )}
115
- </SelectValue>
116
- </SelectTrigger>
117
- <SelectContent portalMount={portalMount()} />
118
- </Select>
178
+ <DropdownMenuTrigger
179
+ ref={setTriggerElement}
180
+ as={Button}
181
+ variant="outline"
182
+ size="sm"
183
+ aria-label={label()}
184
+ disabled={props.disabled || options().length === 0}
185
+ class="w-full justify-between sm:w-52"
186
+ >
187
+ <span class="flex min-w-0 flex-1 items-center gap-2">
188
+ <PalettePreview theme={props.preview} />
189
+ <span class="min-w-0 flex-1 truncate">{selected()?.name ?? props.preview.name}</span>
190
+ </span>
191
+ <ChevronDown aria-hidden="true" class="shrink-0 text-muted-foreground" />
192
+ </DropdownMenuTrigger>
193
+ <DropdownMenuContent
194
+ portalMount={portalMount()}
195
+ class="max-h-(--kb-popper-content-available-height) w-(--kb-popper-anchor-width) min-w-32 overflow-x-hidden overflow-y-auto"
196
+ onInteractOutside={(event) => {
197
+ if (event.detail.originalEvent.type !== 'pointerdown') return
198
+ const parentDialog = portalMount()?.closest<HTMLElement>('[role="dialog"]')
199
+ const focusScope = parentDialog ?? portalMount()?.ownerDocument.body
200
+ const target = event.detail.originalEvent.target
201
+ if (!(target instanceof Element) || !focusScope?.contains(target)) return
202
+
203
+ const focusTarget = target.closest<HTMLElement>(POINTER_FOCUS_TARGET_SELECTOR)
204
+ if (focusTarget && isTabStop(focusTarget)) {
205
+ setRestoreTriggerFocus(false)
206
+ setFocusOutsideTarget(focusTarget)
207
+ } else {
208
+ setRestoreTriggerFocus(true)
209
+ }
210
+ }}
211
+ onFocusOutside={(event) => {
212
+ // The containing dialog or document body can briefly reclaim
213
+ // focus when the menu opens. Keep that handoff from dismissing
214
+ // it; focus moving to another descendant still closes the menu.
215
+ const parentDialog = portalMount()?.closest<HTMLElement>('[role="dialog"]')
216
+ const focusScope = parentDialog ?? portalMount()?.ownerDocument.body
217
+ const target = event.detail.originalEvent.target
218
+ if (target === focusScope) {
219
+ event.preventDefault()
220
+ } else if (target instanceof HTMLElement && focusScope?.contains(target)) {
221
+ setRestoreTriggerFocus(false)
222
+ setFocusOutsideTarget(target)
223
+ }
224
+ }}
225
+ onEscapeKeyDown={() => setRestoreTriggerFocus(true)}
226
+ onCloseAutoFocus={(event) => {
227
+ // A prior menu instance can finish its focus cleanup after this
228
+ // row has already reopened. Do not let that cleanup steal focus.
229
+ if (menuOpen()) {
230
+ event.preventDefault()
231
+ return
232
+ }
233
+
234
+ const lifecycleVersion = focusLifecycleVersion
235
+ const shouldRestore = restoreTriggerFocus()
236
+ const focusTarget = focusTargetAfterTab ?? focusOutsideTarget()
237
+ focusTargetAfterTab = undefined
238
+ setFocusOutsideTarget(undefined)
239
+ setRestoreTriggerFocus(false)
240
+ if (shouldRestore) {
241
+ // The saved focus may be the containing dialog rather than
242
+ // the trigger when WebKit opens this menu from a pointer click.
243
+ event.preventDefault()
244
+ triggerElement()?.focus({ preventScroll: true })
245
+ return
246
+ }
247
+
248
+ const parentDialog = portalMount()?.closest<HTMLElement>('[role="dialog"]')
249
+ const focusScope = parentDialog ?? portalMount()?.ownerDocument.body
250
+ if (focusTarget?.isConnected && focusScope?.contains(focusTarget)) {
251
+ // Preserve the intended in-scope destination across
252
+ // Kobalte's deferred stale-focus restore.
253
+ event.preventDefault()
254
+ queueMicrotask(() => {
255
+ if (lifecycleVersion !== focusLifecycleVersion || menuOpen()) return
256
+
257
+ const activeElement = document.activeElement
258
+ const isMenuFocus =
259
+ activeElement instanceof HTMLElement &&
260
+ activeElement.closest('[role="menu"]') !== null
261
+ const isTriggerFocus = activeElement === triggerElement()
262
+ if (
263
+ activeElement !== focusTarget &&
264
+ (activeElement === document.body ||
265
+ activeElement === focusScope ||
266
+ isMenuFocus ||
267
+ isTriggerFocus)
268
+ ) {
269
+ focusTarget.focus({ preventScroll: true })
270
+ }
271
+ })
272
+ return
273
+ }
274
+
275
+ const activeElement = document.activeElement
276
+ if (
277
+ focusScope &&
278
+ activeElement instanceof HTMLElement &&
279
+ activeElement !== focusScope &&
280
+ focusScope.contains(activeElement) &&
281
+ activeElement.closest('[role="menu"]') === null
282
+ ) {
283
+ // Preserve a real focus target in this scope instead of
284
+ // restoring the stale element captured when the menu mounted. A
285
+ // focused menu item is about to unmount, so Kobalte must restore
286
+ // focus to the trigger instead.
287
+ event.preventDefault()
288
+ }
289
+ }}
290
+ >
291
+ <DropdownMenuRadioGroup
292
+ value={selected()?.id ?? ''}
293
+ aria-label={`${label()} options`}
294
+ on:keydown={moveFocusOnTab}
295
+ onChange={(id) => {
296
+ if (!props.disabled && typeof id === 'string') props.onSelect(id)
297
+ }}
298
+ >
299
+ <For each={options()}>
300
+ {(theme) => (
301
+ <DropdownMenuRadioItem
302
+ value={theme.id}
303
+ textValue={theme.name}
304
+ closeOnSelect={true}
305
+ disabled={props.disabled}
306
+ onSelect={() => setRestoreTriggerFocus(true)}
307
+ >
308
+ <PalettePreview theme={theme} />
309
+ <span class="min-w-0 flex-1 truncate">{theme.name}</span>
310
+ </DropdownMenuRadioItem>
311
+ )}
312
+ </For>
313
+ </DropdownMenuRadioGroup>
314
+ </DropdownMenuContent>
315
+ </DropdownMenu>
119
316
  </div>
120
317
  </SettingsRow>
121
318
  )
@@ -16,10 +16,10 @@ import { cn } from '../../../lib/utils'
16
16
  /**
17
17
  * DropdownMenu.
18
18
  *
19
- * A list of actions on a trigger. This is the right home for "what can I do
20
- * with this thing": rename, duplicate, export, delete. It is not a Select
21
- * (which holds a *value* the user is choosing) and not a Popover (which holds
22
- * arbitrary content).
19
+ * A list of actions on a trigger, with radio groups for persistent choices
20
+ * that fit naturally in a compact menu. Use Select for form inputs whose
21
+ * selected value needs the field-style control, and Popover for arbitrary
22
+ * content.
23
23
  *
24
24
  * Kobalte supplies the full menu contract — arrow keys, typeahead, the roving
25
25
  * highlight, Escape to close and return focus — which is exactly what a
@@ -42,13 +42,15 @@ export function DropdownMenuPortal(props: ComponentProps<typeof KobalteDropdownM
42
42
 
43
43
  export type DropdownMenuContentProps = ComponentProps<typeof KobalteDropdownMenu.Content> & {
44
44
  hideArrow?: boolean
45
+ /** Mount under an owner element such as a modal, keeping the menu in its accessible subtree. */
46
+ portalMount?: HTMLElement
45
47
  }
46
48
 
47
49
  export function DropdownMenuContent(props: DropdownMenuContentProps) {
48
- const [local, rest] = splitProps(props, ['class', 'hideArrow', 'children'])
50
+ const [local, rest] = splitProps(props, ['class', 'hideArrow', 'children', 'portalMount'])
49
51
 
50
52
  return (
51
- <KobalteDropdownMenu.Portal>
53
+ <KobalteDropdownMenu.Portal mount={local.portalMount}>
52
54
  <KobalteDropdownMenu.Content
53
55
  class={cn(
54
56
  overlaySurface,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adea-ai/ui",
3
- "version": "0.69.1",
3
+ "version": "0.70.0",
4
4
  "description": "The Adea design system: Kobalte-backed Solid components, semantic OKLCH tokens, and the app-shell layout primitives shared by Adea and Cortana.",
5
5
  "keywords": [
6
6
  "adea",
package/registry.json CHANGED
@@ -1652,9 +1652,9 @@
1652
1652
  ],
1653
1653
  "registryDependencies": [
1654
1654
  "@adea-ai/ui/button",
1655
+ "@adea-ai/ui/dropdown-menu",
1655
1656
  "@adea-ai/ui/input",
1656
1657
  "@adea-ai/ui/popover",
1657
- "@adea-ai/ui/select",
1658
1658
  "@adea-ai/ui/switch",
1659
1659
  "lib"
1660
1660
  ],
@@ -208,7 +208,7 @@ export function AppearanceEditor(props: AppearanceEditorProps) {
208
208
  )
209
209
  }
210
210
 
211
- /** Modal anchoring/dismissal and nested-listbox behavior come from Kobalte.
211
+ /** Modal anchoring/dismissal and nested-menu behavior come from Kobalte.
212
212
  * Its measured available height also bounds scrolling, keeping footer actions
213
213
  * reachable when a narrow popup begins below its trigger. */
214
214
  export function AppearancePopover(props: AppearancePopoverProps) {