@nocoo/basalt 2.1.7 → 2.2.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 (153) hide show
  1. package/ai/INTEGRATION.md +66 -28
  2. package/ai/RECIPES.md +46 -1
  3. package/ai/USAGE.md +2 -2
  4. package/ai/registry.json +65 -23
  5. package/dist/assets/brand-mark.js.map +1 -1
  6. package/dist/charts/area.js.map +1 -1
  7. package/dist/charts/bar.js.map +1 -1
  8. package/dist/charts/bullet.js.map +1 -1
  9. package/dist/charts/charts.js.map +1 -1
  10. package/dist/charts/config.js.map +1 -1
  11. package/dist/charts/custom-chart.js.map +1 -1
  12. package/dist/charts/date-navigation.js.map +1 -1
  13. package/dist/charts/donut.js.map +1 -1
  14. package/dist/charts/frame.js.map +1 -1
  15. package/dist/charts/funnel.js.map +1 -1
  16. package/dist/charts/gauge.js.map +1 -1
  17. package/dist/charts/grouped-bar.js.map +1 -1
  18. package/dist/charts/heatmap-calendar.js.map +1 -1
  19. package/dist/charts/heatmap-matrix.js.map +1 -1
  20. package/dist/charts/item-list.js.map +1 -1
  21. package/dist/charts/legend.js.map +1 -1
  22. package/dist/charts/line.js.map +1 -1
  23. package/dist/charts/palette.js.map +1 -1
  24. package/dist/charts/radar.js.map +1 -1
  25. package/dist/charts/sample.js.map +1 -1
  26. package/dist/charts/sankey.js.map +1 -1
  27. package/dist/charts/series.js.map +1 -1
  28. package/dist/charts/slot-bar.js.map +1 -1
  29. package/dist/charts/sparkline.js.map +1 -1
  30. package/dist/charts/stacked-bar.js.map +1 -1
  31. package/dist/charts/stat-card.js.map +1 -1
  32. package/dist/charts/timeline.js.map +1 -1
  33. package/dist/charts/timeseries.js.map +1 -1
  34. package/dist/charts/tooltip.js.map +1 -1
  35. package/dist/components/accordion.js.map +1 -1
  36. package/dist/components/alert-dialog.js.map +1 -1
  37. package/dist/components/app-header.d.ts +5 -1
  38. package/dist/components/app-header.js +12 -4
  39. package/dist/components/app-header.js.map +1 -1
  40. package/dist/components/app-shell.d.ts +4 -1
  41. package/dist/components/app-shell.js +7 -4
  42. package/dist/components/app-shell.js.map +1 -1
  43. package/dist/components/autocomplete.js.map +1 -1
  44. package/dist/components/avatar.js.map +1 -1
  45. package/dist/components/badge.js.map +1 -1
  46. package/dist/components/banner.js.map +1 -1
  47. package/dist/components/basalt-mark.js.map +1 -1
  48. package/dist/components/battery-meter.js.map +1 -1
  49. package/dist/components/breadcrumbs.js.map +1 -1
  50. package/dist/components/button.js +2 -2
  51. package/dist/components/button.js.map +1 -1
  52. package/dist/components/chat-bubble.js.map +1 -1
  53. package/dist/components/chat-composer.js.map +1 -1
  54. package/dist/components/chat-header.js.map +1 -1
  55. package/dist/components/chat-inbox.js.map +1 -1
  56. package/dist/components/checkbox.js.map +1 -1
  57. package/dist/components/clipboard-text.js.map +1 -1
  58. package/dist/components/code.js.map +1 -1
  59. package/dist/components/collapsible.js.map +1 -1
  60. package/dist/components/combobox.js.map +1 -1
  61. package/dist/components/command-palette.js.map +1 -1
  62. package/dist/components/confirm-dialog.d.ts +4 -1
  63. package/dist/components/confirm-dialog.js +20 -18
  64. package/dist/components/confirm-dialog.js.map +1 -1
  65. package/dist/components/context-menu.js.map +1 -1
  66. package/dist/components/data-table.js +1 -1
  67. package/dist/components/data-table.js.map +1 -1
  68. package/dist/components/date-picker.js.map +1 -1
  69. package/dist/components/delete-resource.js +1 -1
  70. package/dist/components/delete-resource.js.map +1 -1
  71. package/dist/components/description-list.js.map +1 -1
  72. package/dist/components/dialog.js.map +1 -1
  73. package/dist/components/dock.js.map +1 -1
  74. package/dist/components/dropdown-menu.js.map +1 -1
  75. package/dist/components/editable-nav-item.js.map +1 -1
  76. package/dist/components/empty.js.map +1 -1
  77. package/dist/components/fab.js.map +1 -1
  78. package/dist/components/field.js.map +1 -1
  79. package/dist/components/file-dropzone.js.map +1 -1
  80. package/dist/components/filter-bar.js.map +1 -1
  81. package/dist/components/flow.js.map +1 -1
  82. package/dist/components/grid.js.map +1 -1
  83. package/dist/components/hover-card.js.map +1 -1
  84. package/dist/components/icon-picker.js.map +1 -1
  85. package/dist/components/inline-editable.js.map +1 -1
  86. package/dist/components/input-area.js.map +1 -1
  87. package/dist/components/input-group.js.map +1 -1
  88. package/dist/components/input.js.map +1 -1
  89. package/dist/components/label.js.map +1 -1
  90. package/dist/components/layer-card.js.map +1 -1
  91. package/dist/components/link.js.map +1 -1
  92. package/dist/components/loader.js.map +1 -1
  93. package/dist/components/loading-screen.js.map +1 -1
  94. package/dist/components/menu-bar.js.map +1 -1
  95. package/dist/components/meter.js.map +1 -1
  96. package/dist/components/multi-select.js.map +1 -1
  97. package/dist/components/navigation-menu.js.map +1 -1
  98. package/dist/components/overlay.js.map +1 -1
  99. package/dist/components/page-header.js.map +1 -1
  100. package/dist/components/pagination.js.map +1 -1
  101. package/dist/components/popover.js.map +1 -1
  102. package/dist/components/radio.js.map +1 -1
  103. package/dist/components/resource-list.js.map +1 -1
  104. package/dist/components/responsive-master-detail.js +5 -5
  105. package/dist/components/responsive-master-detail.js.map +1 -1
  106. package/dist/components/scroll-area.js.map +1 -1
  107. package/dist/components/section-rule.js.map +1 -1
  108. package/dist/components/segment-control.js.map +1 -1
  109. package/dist/components/select.js.map +1 -1
  110. package/dist/components/sensitive-input.js.map +1 -1
  111. package/dist/components/separator.js.map +1 -1
  112. package/dist/components/sheet.js +1 -0
  113. package/dist/components/sheet.js.map +1 -1
  114. package/dist/components/sidebar.d.ts +3 -1
  115. package/dist/components/sidebar.js +8 -5
  116. package/dist/components/sidebar.js.map +1 -1
  117. package/dist/components/skeleton-line.js.map +1 -1
  118. package/dist/components/slider.js.map +1 -1
  119. package/dist/components/stat-strip.js.map +1 -1
  120. package/dist/components/switch.js.map +1 -1
  121. package/dist/components/table-of-contents.js.map +1 -1
  122. package/dist/components/table-pager.js.map +1 -1
  123. package/dist/components/table.js.map +1 -1
  124. package/dist/components/tabs.js.map +1 -1
  125. package/dist/components/tag-badge.js.map +1 -1
  126. package/dist/components/tag-color-picker.js.map +1 -1
  127. package/dist/components/text.js.map +1 -1
  128. package/dist/components/theme-toggle.js.map +1 -1
  129. package/dist/components/toast.js.map +1 -1
  130. package/dist/components/toggle-group.js +2 -2
  131. package/dist/components/toggle-group.js.map +1 -1
  132. package/dist/components/toggle.js +1 -1
  133. package/dist/components/toggle.js.map +1 -1
  134. package/dist/components/toolbar.js.map +1 -1
  135. package/dist/components/tooltip.js +14 -13
  136. package/dist/components/tooltip.js.map +1 -1
  137. package/dist/components/typeahead-field.js.map +1 -1
  138. package/dist/components/upload-queue.js.map +1 -1
  139. package/dist/providers/accent.js +1 -1
  140. package/dist/providers/accent.js.map +1 -1
  141. package/dist/providers/link.js.map +1 -1
  142. package/dist/providers/theme.js +1 -1
  143. package/dist/providers/theme.js.map +1 -1
  144. package/dist/styles/base.css +116 -0
  145. package/dist/styles/standalone.css +161 -16
  146. package/dist/utils/cn.js.map +1 -1
  147. package/dist/utils/control-surface.js.map +1 -1
  148. package/dist/utils/preference-store.js +1 -1
  149. package/dist/utils/preference-store.js.map +1 -1
  150. package/dist/utils/selection-indicator.js +1 -1
  151. package/dist/utils/selection-indicator.js.map +1 -1
  152. package/dist/utils/use-checkable-group.js.map +1 -1
  153. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"typeahead-field.js","names":[],"sources":["../../src/components/typeahead-field.tsx"],"sourcesContent":["import * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport { type ComponentPropsWithoutRef, useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Input, type InputSize } from \"./input\";\nimport { OVERLAY_GAP, overlayItemClass, overlayPanelClass } from \"./overlay\";\n\nexport type TypeaheadItem = {\n\tvalue: string;\n\tlabel: string;\n\tdisabled?: boolean;\n};\n\nexport function TypeaheadField({\n\titems,\n\tvalue,\n\tdefaultValue = \"\",\n\tonValueChange,\n\tname,\n\tplaceholder = \"Select…\",\n\tdisabled = false,\n\tloading = false,\n\tsize = \"default\",\n\tclassName,\n\tid,\n\tallowFreeform,\n\t\"aria-label\": ariaLabel,\n\t\"aria-describedby\": ariaDescribedBy,\n\t\"aria-invalid\": ariaInvalid,\n\tonBlur,\n\t...rest\n}: {\n\titems: TypeaheadItem[];\n\tvalue?: string;\n\tdefaultValue?: string;\n\tonValueChange?: (value: string) => void;\n\tname?: string;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tloading?: boolean;\n\tsize?: InputSize;\n\tclassName?: string;\n\tid?: string;\n\tallowFreeform: boolean;\n\t\"aria-label\"?: string;\n\t\"aria-describedby\"?: string;\n\t\"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\";\n} & Omit<ComponentPropsWithoutRef<\"div\">, \"children\" | \"defaultValue\" | \"onChange\">) {\n\tconst [uncontrolled, setUncontrolled] = useState(defaultValue);\n\tconst selected = value ?? uncontrolled;\n\tconst [query, setQuery] = useState(displayOf(items, selected));\n\tconst [prevValue, setPrevValue] = useState(value);\n\tconst [open, setOpen] = useState(false);\n\tconst [active, setActive] = useState<number | null>(null);\n\tconst listId = useId();\n\tconst inputRef = useRef<HTMLInputElement>(null);\n\tconst skipFocusOpen = useRef(false);\n\tconst lastPointerPos = useRef<{ x: number; y: number } | null>(null);\n\tif (value !== prevValue) {\n\t\tsetPrevValue(value);\n\t\tif (value !== undefined) {\n\t\t\tsetQuery(displayOf(items, value));\n\t\t} else if (prevValue !== undefined) {\n\t\t\tsetUncontrolled(prevValue);\n\t\t\tsetQuery(displayOf(items, prevValue));\n\t\t}\n\t}\n\tconst selectedDisplay = displayOf(items, selected);\n\tconst [prevDisplay, setPrevDisplay] = useState(selectedDisplay);\n\tif (selectedDisplay !== prevDisplay) {\n\t\tsetPrevDisplay(selectedDisplay);\n\t\tif (!open || query === prevDisplay) {\n\t\t\tsetQuery(selectedDisplay);\n\t\t}\n\t}\n\tconst latestConfigRef = useRef({\n\t\tdefaultValue,\n\t\titems,\n\t\tvalue,\n\t});\n\tuseEffect(() => {\n\t\tlatestConfigRef.current = {\n\t\t\tdefaultValue,\n\t\t\titems,\n\t\t\tvalue,\n\t\t};\n\t});\n\n\tuseEffect(() => {\n\t\tconst node = inputRef.current;\n\t\tconst form = node?.form;\n\t\tif (!form) {\n\t\t\treturn;\n\t\t}\n\t\tlet disposed = false;\n\t\tconst timers = new Set<ReturnType<typeof setTimeout>>();\n\t\tconst onReset = (event: Event) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\ttimers.delete(timer);\n\t\t\t\tif (disposed || event.defaultPrevented) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst cfg = latestConfigRef.current;\n\t\t\t\tif (cfg.value === undefined) {\n\t\t\t\t\tsetUncontrolled(cfg.defaultValue);\n\t\t\t\t\tsetQuery(displayOf(cfg.items, cfg.defaultValue));\n\t\t\t\t} else {\n\t\t\t\t\tsetQuery(displayOf(cfg.items, cfg.value));\n\t\t\t\t}\n\t\t\t\tsetOpen(false);\n\t\t\t\tsetActive(null);\n\t\t\t}, 0);\n\t\t\ttimers.add(timer);\n\t\t};\n\t\tform.addEventListener(\"reset\", onReset);\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\tfor (const timer of timers) {\n\t\t\t\tclearTimeout(timer);\n\t\t\t}\n\t\t\ttimers.clear();\n\t\t\tform.removeEventListener(\"reset\", onReset);\n\t\t};\n\t}, []);\n\tconst filtered = filterItems(items, query, !allowFreeform);\n\tconst activeIndex =\n\t\tactive === null || filtered.length === 0 ? null : Math.min(active, filtered.length - 1);\n\tconst activeItem = activeIndex === null ? undefined : filtered[activeIndex];\n\tconst listOpen = open && filtered.length > 0;\n\tconst busy = disabled || loading;\n\n\tuseEffect(() => {\n\t\tif (!listOpen || activeIndex === null) {\n\t\t\treturn;\n\t\t}\n\t\tconst optionElement = document.getElementById(`${listId}-opt-${activeIndex}`);\n\t\tif (typeof optionElement?.scrollIntoView === \"function\") {\n\t\t\toptionElement.scrollIntoView({ block: \"nearest\" });\n\t\t}\n\t}, [activeIndex, listId, listOpen]);\n\n\tuseEffect(() => {\n\t\tif (!open) {\n\t\t\treturn;\n\t\t}\n\t\tconst nextLen = filterItems(items, query, !allowFreeform).length;\n\t\tsetActive((current) => {\n\t\t\tif (current === null || nextLen === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn Math.min(current, nextLen - 1);\n\t\t});\n\t}, [allowFreeform, items, open, query]);\n\n\tfunction commitValue(next: string, display: string, restoreFocus = true) {\n\t\tif (value === undefined) {\n\t\t\tsetUncontrolled(next);\n\t\t\tsetQuery(display);\n\t\t} else {\n\t\t\tsetQuery(displayOf(items, value));\n\t\t}\n\t\tsetOpen(false);\n\t\tsetActive(null);\n\t\tonValueChange?.(next);\n\t\tif (restoreFocus && inputRef.current && document.activeElement !== inputRef.current) {\n\t\t\tskipFocusOpen.current = true;\n\t\t\tinputRef.current.focus();\n\t\t}\n\t}\n\n\tfunction commitItem(item: TypeaheadItem, restoreFocus = true) {\n\t\tif (item.disabled) {\n\t\t\treturn;\n\t\t}\n\t\tcommitValue(item.value, item.label, restoreFocus);\n\t}\n\n\tfunction commitFreeform(text: string, restoreFocus = true) {\n\t\tconst trimmed = text.trim();\n\t\tconst match = items.find(\n\t\t\t(item) =>\n\t\t\t\t!item.disabled &&\n\t\t\t\t(item.label.toLowerCase() === trimmed.toLowerCase() || item.value === trimmed),\n\t\t);\n\t\tif (match) {\n\t\t\tcommitItem(match, restoreFocus);\n\t\t\treturn;\n\t\t}\n\t\tcommitValue(trimmed, trimmed, restoreFocus);\n\t}\n\n\tuseEffect(() => {\n\t\tif (!open) {\n\t\t\treturn;\n\t\t}\n\t\tconst onKey = (event: KeyboardEvent) => {\n\t\t\tif (event.key !== \"Escape\" || event.isComposing) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t\tevent.stopImmediatePropagation();\n\t\t\tsetOpen(false);\n\t\t\tsetQuery(displayOf(items, selected));\n\t\t\tsetActive(null);\n\t\t};\n\t\twindow.addEventListener(\"keydown\", onKey, true);\n\t\tdocument.addEventListener(\"keydown\", onKey, true);\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"keydown\", onKey, true);\n\t\t\tdocument.removeEventListener(\"keydown\", onKey, true);\n\t\t};\n\t}, [items, open, selected]);\n\n\treturn (\n\t\t<PopoverPrimitive.Root\n\t\t\topen={listOpen}\n\t\t\tonOpenChange={(nextOpen) => {\n\t\t\t\tif (!nextOpen) {\n\t\t\t\t\tsetOpen(false);\n\t\t\t\t\tsetActive(null);\n\t\t\t\t\tif (!allowFreeform) {\n\t\t\t\t\t\tsetQuery(displayOf(items, selected));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\t{...rest}\n\t\t\t\tclassName={cn(\"relative w-full\", className)}\n\t\t\t\tonBlur={(event) => {\n\t\t\t\t\tonBlur?.(event);\n\t\t\t\t\tconst related = event.relatedTarget as Node | null;\n\t\t\t\t\tconst listbox = document.getElementById(listId);\n\t\t\t\t\tif (!event.currentTarget.contains(related) && !listbox?.contains(related)) {\n\t\t\t\t\t\tif (allowFreeform) {\n\t\t\t\t\t\t\tif (query.trim() !== displayOf(items, selected)) {\n\t\t\t\t\t\t\t\tcommitFreeform(query, false);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsetQuery(displayOf(items, selected));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsetOpen(false);\n\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{name ? <input type=\"hidden\" name={name} value={selected} disabled={busy} /> : null}\n\t\t\t\t<PopoverPrimitive.Anchor asChild>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\tid={id}\n\t\t\t\t\t\tvalue={query}\n\t\t\t\t\t\tdisabled={busy}\n\t\t\t\t\t\tsize={size}\n\t\t\t\t\t\taria-busy={loading || undefined}\n\t\t\t\t\t\taria-invalid={ariaInvalid}\n\t\t\t\t\t\taria-describedby={ariaDescribedBy}\n\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\tsetQuery(event.target.value);\n\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonFocus={() => {\n\t\t\t\t\t\t\tif (busy) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (skipFocusOpen.current) {\n\t\t\t\t\t\t\t\tskipFocusOpen.current = false;\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\tif (busy) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!open) {\n\t\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\tif (event.nativeEvent.isComposing || event.key === \"Process\") {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"ArrowDown\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t\t\tif (filtered.length === 0) {\n\t\t\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsetActive((current) => (current === null ? 0 : (current + 1) % filtered.length));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t\t\tif (filtered.length === 0) {\n\t\t\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsetActive((current) =>\n\t\t\t\t\t\t\t\t\tcurrent === null\n\t\t\t\t\t\t\t\t\t\t? filtered.length - 1\n\t\t\t\t\t\t\t\t\t\t: (current - 1 + filtered.length) % filtered.length,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"Enter\") {\n\t\t\t\t\t\t\t\tif (open && activeItem) {\n\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\tcommitItem(activeItem);\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (allowFreeform && query.trim() !== displayOf(items, selected)) {\n\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\tcommitFreeform(query);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\t\trole=\"combobox\"\n\t\t\t\t\t\taria-label={ariaLabel ?? (id ? undefined : placeholder)}\n\t\t\t\t\t\taria-expanded={listOpen}\n\t\t\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\t\t\taria-controls={listOpen ? listId : undefined}\n\t\t\t\t\t\taria-activedescendant={\n\t\t\t\t\t\t\tlistOpen && activeItem && activeIndex !== null\n\t\t\t\t\t\t\t\t? `${listId}-opt-${activeIndex}`\n\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t</PopoverPrimitive.Anchor>\n\t\t\t\t{listOpen ? (\n\t\t\t\t\t<PopoverPrimitive.Portal>\n\t\t\t\t\t\t<PopoverPrimitive.Content\n\t\t\t\t\t\t\tside=\"bottom\"\n\t\t\t\t\t\t\tsideOffset={OVERLAY_GAP}\n\t\t\t\t\t\t\talign=\"start\"\n\t\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\t\tonPointerMove={(event) => {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tlastPointerPos.current &&\n\t\t\t\t\t\t\t\t\tlastPointerPos.current.x === event.clientX &&\n\t\t\t\t\t\t\t\t\tlastPointerPos.current.y === event.clientY\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tlastPointerPos.current = { x: event.clientX, y: event.clientY };\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonEscapeKeyDown={(e) => {\n\t\t\t\t\t\t\t\tconst evt = e as unknown as {\n\t\t\t\t\t\t\t\t\tisComposing?: boolean;\n\t\t\t\t\t\t\t\t\tnativeEvent?: { isComposing?: boolean };\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tif (evt.isComposing || evt.nativeEvent?.isComposing) {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonOpenAutoFocus={(e) => e.preventDefault()}\n\t\t\t\t\t\t\tonCloseAutoFocus={(e) => e.preventDefault()}\n\t\t\t\t\t\t\tonInteractOutside={(e) => {\n\t\t\t\t\t\t\t\tconst target = e.target as Node | null;\n\t\t\t\t\t\t\t\tif (inputRef.current?.contains(target)) {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tid={listId}\n\t\t\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\t\t\tclassName={overlayPanelClass(\n\t\t\t\t\t\t\t\t\"w-[var(--radix-popover-trigger-width)] max-h-[var(--radix-popover-content-available-height)] overflow-y-auto\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{filtered.map((item, index) => (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tkey={`${listId}-opt-${index}`}\n\t\t\t\t\t\t\t\t\tid={`${listId}-opt-${index}`}\n\t\t\t\t\t\t\t\t\trole=\"option\"\n\t\t\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\t\t\tdisabled={item.disabled}\n\t\t\t\t\t\t\t\t\taria-disabled={item.disabled || undefined}\n\t\t\t\t\t\t\t\t\taria-selected={index === activeIndex}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\toverlayItemClass(\"hover:bg-basalt-accent\"),\n\t\t\t\t\t\t\t\t\t\tindex === activeIndex && \"bg-basalt-accent\",\n\t\t\t\t\t\t\t\t\t\titem.disabled && \"opacity-50\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tonPointerMove={(event) => {\n\t\t\t\t\t\t\t\t\t\tif (item.disabled) {\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tconst { clientX, clientY } = event;\n\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\tlastPointerPos.current &&\n\t\t\t\t\t\t\t\t\t\t\tlastPointerPos.current.x === clientX &&\n\t\t\t\t\t\t\t\t\t\t\tlastPointerPos.current.y === clientY\n\t\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tlastPointerPos.current = { x: clientX, y: clientY };\n\t\t\t\t\t\t\t\t\t\tsetActive(index);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonMouseDown={(event) => event.preventDefault()}\n\t\t\t\t\t\t\t\t\tonClick={() => commitItem(item)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{item.label}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</PopoverPrimitive.Content>\n\t\t\t\t\t</PopoverPrimitive.Portal>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t</PopoverPrimitive.Root>\n\t);\n}\n\nfunction displayOf(items: TypeaheadItem[], value: string) {\n\treturn items.find((item) => item.value === value)?.label ?? value;\n}\n\nfunction filterItems(items: TypeaheadItem[], query: string, showAllWhenEmpty: boolean) {\n\tconst needle = query.trim().toLowerCase();\n\tif (needle === \"\") {\n\t\treturn showAllWhenEmpty ? items : [];\n\t}\n\treturn items.filter((item) => item.label.toLowerCase().includes(needle));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"typeahead-field.js","names":["PopoverPrimitive","TypeaheadField","ariaLabel","ariaDescribedBy","ariaInvalid","rest","uncontrolled","setUncontrolled","useState","defaultValue","selected","value","query","setQuery","displayOf","items","prevValue","setPrevValue","open","setOpen","active","setActive","listId","useId","inputRef","useRef","skipFocusOpen","lastPointerPos","selectedDisplay","prevDisplay","setPrevDisplay","latestConfigRef","useEffect","form","disposed","timers","onReset","timer","event","cfg","filtered","filterItems","allowFreeform","activeIndex","activeItem","listOpen","busy","disabled","loading","optionElement","nextLen","current","commitValue","next","display","onValueChange","restoreFocus","commitItem","item","commitFreeform","trimmed","text","match","onKey","nextOpen","cn","className","related","listbox","name","Input","id","placeholder","evt","target","overlayPanelClass","index","overlayItemClass","clientX","clientY","needle","showAllWhenEmpty"],"sources":["../../src/components/typeahead-field.tsx"],"sourcesContent":["import * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport { type ComponentPropsWithoutRef, useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Input, type InputSize } from \"./input\";\nimport { OVERLAY_GAP, overlayItemClass, overlayPanelClass } from \"./overlay\";\n\nexport type TypeaheadItem = {\n\tvalue: string;\n\tlabel: string;\n\tdisabled?: boolean;\n};\n\nexport function TypeaheadField({\n\titems,\n\tvalue,\n\tdefaultValue = \"\",\n\tonValueChange,\n\tname,\n\tplaceholder = \"Select…\",\n\tdisabled = false,\n\tloading = false,\n\tsize = \"default\",\n\tclassName,\n\tid,\n\tallowFreeform,\n\t\"aria-label\": ariaLabel,\n\t\"aria-describedby\": ariaDescribedBy,\n\t\"aria-invalid\": ariaInvalid,\n\tonBlur,\n\t...rest\n}: {\n\titems: TypeaheadItem[];\n\tvalue?: string;\n\tdefaultValue?: string;\n\tonValueChange?: (value: string) => void;\n\tname?: string;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tloading?: boolean;\n\tsize?: InputSize;\n\tclassName?: string;\n\tid?: string;\n\tallowFreeform: boolean;\n\t\"aria-label\"?: string;\n\t\"aria-describedby\"?: string;\n\t\"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\";\n} & Omit<ComponentPropsWithoutRef<\"div\">, \"children\" | \"defaultValue\" | \"onChange\">) {\n\tconst [uncontrolled, setUncontrolled] = useState(defaultValue);\n\tconst selected = value ?? uncontrolled;\n\tconst [query, setQuery] = useState(displayOf(items, selected));\n\tconst [prevValue, setPrevValue] = useState(value);\n\tconst [open, setOpen] = useState(false);\n\tconst [active, setActive] = useState<number | null>(null);\n\tconst listId = useId();\n\tconst inputRef = useRef<HTMLInputElement>(null);\n\tconst skipFocusOpen = useRef(false);\n\tconst lastPointerPos = useRef<{ x: number; y: number } | null>(null);\n\tif (value !== prevValue) {\n\t\tsetPrevValue(value);\n\t\tif (value !== undefined) {\n\t\t\tsetQuery(displayOf(items, value));\n\t\t} else if (prevValue !== undefined) {\n\t\t\tsetUncontrolled(prevValue);\n\t\t\tsetQuery(displayOf(items, prevValue));\n\t\t}\n\t}\n\tconst selectedDisplay = displayOf(items, selected);\n\tconst [prevDisplay, setPrevDisplay] = useState(selectedDisplay);\n\tif (selectedDisplay !== prevDisplay) {\n\t\tsetPrevDisplay(selectedDisplay);\n\t\tif (!open || query === prevDisplay) {\n\t\t\tsetQuery(selectedDisplay);\n\t\t}\n\t}\n\tconst latestConfigRef = useRef({\n\t\tdefaultValue,\n\t\titems,\n\t\tvalue,\n\t});\n\tuseEffect(() => {\n\t\tlatestConfigRef.current = {\n\t\t\tdefaultValue,\n\t\t\titems,\n\t\t\tvalue,\n\t\t};\n\t});\n\n\tuseEffect(() => {\n\t\tconst node = inputRef.current;\n\t\tconst form = node?.form;\n\t\tif (!form) {\n\t\t\treturn;\n\t\t}\n\t\tlet disposed = false;\n\t\tconst timers = new Set<ReturnType<typeof setTimeout>>();\n\t\tconst onReset = (event: Event) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\ttimers.delete(timer);\n\t\t\t\tif (disposed || event.defaultPrevented) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst cfg = latestConfigRef.current;\n\t\t\t\tif (cfg.value === undefined) {\n\t\t\t\t\tsetUncontrolled(cfg.defaultValue);\n\t\t\t\t\tsetQuery(displayOf(cfg.items, cfg.defaultValue));\n\t\t\t\t} else {\n\t\t\t\t\tsetQuery(displayOf(cfg.items, cfg.value));\n\t\t\t\t}\n\t\t\t\tsetOpen(false);\n\t\t\t\tsetActive(null);\n\t\t\t}, 0);\n\t\t\ttimers.add(timer);\n\t\t};\n\t\tform.addEventListener(\"reset\", onReset);\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\tfor (const timer of timers) {\n\t\t\t\tclearTimeout(timer);\n\t\t\t}\n\t\t\ttimers.clear();\n\t\t\tform.removeEventListener(\"reset\", onReset);\n\t\t};\n\t}, []);\n\tconst filtered = filterItems(items, query, !allowFreeform);\n\tconst activeIndex =\n\t\tactive === null || filtered.length === 0 ? null : Math.min(active, filtered.length - 1);\n\tconst activeItem = activeIndex === null ? undefined : filtered[activeIndex];\n\tconst listOpen = open && filtered.length > 0;\n\tconst busy = disabled || loading;\n\n\tuseEffect(() => {\n\t\tif (!listOpen || activeIndex === null) {\n\t\t\treturn;\n\t\t}\n\t\tconst optionElement = document.getElementById(`${listId}-opt-${activeIndex}`);\n\t\tif (typeof optionElement?.scrollIntoView === \"function\") {\n\t\t\toptionElement.scrollIntoView({ block: \"nearest\" });\n\t\t}\n\t}, [activeIndex, listId, listOpen]);\n\n\tuseEffect(() => {\n\t\tif (!open) {\n\t\t\treturn;\n\t\t}\n\t\tconst nextLen = filterItems(items, query, !allowFreeform).length;\n\t\tsetActive((current) => {\n\t\t\tif (current === null || nextLen === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn Math.min(current, nextLen - 1);\n\t\t});\n\t}, [allowFreeform, items, open, query]);\n\n\tfunction commitValue(next: string, display: string, restoreFocus = true) {\n\t\tif (value === undefined) {\n\t\t\tsetUncontrolled(next);\n\t\t\tsetQuery(display);\n\t\t} else {\n\t\t\tsetQuery(displayOf(items, value));\n\t\t}\n\t\tsetOpen(false);\n\t\tsetActive(null);\n\t\tonValueChange?.(next);\n\t\tif (restoreFocus && inputRef.current && document.activeElement !== inputRef.current) {\n\t\t\tskipFocusOpen.current = true;\n\t\t\tinputRef.current.focus();\n\t\t}\n\t}\n\n\tfunction commitItem(item: TypeaheadItem, restoreFocus = true) {\n\t\tif (item.disabled) {\n\t\t\treturn;\n\t\t}\n\t\tcommitValue(item.value, item.label, restoreFocus);\n\t}\n\n\tfunction commitFreeform(text: string, restoreFocus = true) {\n\t\tconst trimmed = text.trim();\n\t\tconst match = items.find(\n\t\t\t(item) =>\n\t\t\t\t!item.disabled &&\n\t\t\t\t(item.label.toLowerCase() === trimmed.toLowerCase() || item.value === trimmed),\n\t\t);\n\t\tif (match) {\n\t\t\tcommitItem(match, restoreFocus);\n\t\t\treturn;\n\t\t}\n\t\tcommitValue(trimmed, trimmed, restoreFocus);\n\t}\n\n\tuseEffect(() => {\n\t\tif (!open) {\n\t\t\treturn;\n\t\t}\n\t\tconst onKey = (event: KeyboardEvent) => {\n\t\t\tif (event.key !== \"Escape\" || event.isComposing) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t\tevent.stopImmediatePropagation();\n\t\t\tsetOpen(false);\n\t\t\tsetQuery(displayOf(items, selected));\n\t\t\tsetActive(null);\n\t\t};\n\t\twindow.addEventListener(\"keydown\", onKey, true);\n\t\tdocument.addEventListener(\"keydown\", onKey, true);\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"keydown\", onKey, true);\n\t\t\tdocument.removeEventListener(\"keydown\", onKey, true);\n\t\t};\n\t}, [items, open, selected]);\n\n\treturn (\n\t\t<PopoverPrimitive.Root\n\t\t\topen={listOpen}\n\t\t\tonOpenChange={(nextOpen) => {\n\t\t\t\tif (!nextOpen) {\n\t\t\t\t\tsetOpen(false);\n\t\t\t\t\tsetActive(null);\n\t\t\t\t\tif (!allowFreeform) {\n\t\t\t\t\t\tsetQuery(displayOf(items, selected));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\t{...rest}\n\t\t\t\tclassName={cn(\"relative w-full\", className)}\n\t\t\t\tonBlur={(event) => {\n\t\t\t\t\tonBlur?.(event);\n\t\t\t\t\tconst related = event.relatedTarget as Node | null;\n\t\t\t\t\tconst listbox = document.getElementById(listId);\n\t\t\t\t\tif (!event.currentTarget.contains(related) && !listbox?.contains(related)) {\n\t\t\t\t\t\tif (allowFreeform) {\n\t\t\t\t\t\t\tif (query.trim() !== displayOf(items, selected)) {\n\t\t\t\t\t\t\t\tcommitFreeform(query, false);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsetQuery(displayOf(items, selected));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsetOpen(false);\n\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{name ? <input type=\"hidden\" name={name} value={selected} disabled={busy} /> : null}\n\t\t\t\t<PopoverPrimitive.Anchor asChild>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\tid={id}\n\t\t\t\t\t\tvalue={query}\n\t\t\t\t\t\tdisabled={busy}\n\t\t\t\t\t\tsize={size}\n\t\t\t\t\t\taria-busy={loading || undefined}\n\t\t\t\t\t\taria-invalid={ariaInvalid}\n\t\t\t\t\t\taria-describedby={ariaDescribedBy}\n\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\tsetQuery(event.target.value);\n\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonFocus={() => {\n\t\t\t\t\t\t\tif (busy) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (skipFocusOpen.current) {\n\t\t\t\t\t\t\t\tskipFocusOpen.current = false;\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\tif (busy) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!open) {\n\t\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\tif (event.nativeEvent.isComposing || event.key === \"Process\") {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"ArrowDown\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t\t\tif (filtered.length === 0) {\n\t\t\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsetActive((current) => (current === null ? 0 : (current + 1) % filtered.length));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t\t\tif (filtered.length === 0) {\n\t\t\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsetActive((current) =>\n\t\t\t\t\t\t\t\t\tcurrent === null\n\t\t\t\t\t\t\t\t\t\t? filtered.length - 1\n\t\t\t\t\t\t\t\t\t\t: (current - 1 + filtered.length) % filtered.length,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"Enter\") {\n\t\t\t\t\t\t\t\tif (open && activeItem) {\n\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\tcommitItem(activeItem);\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (allowFreeform && query.trim() !== displayOf(items, selected)) {\n\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\tcommitFreeform(query);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\t\trole=\"combobox\"\n\t\t\t\t\t\taria-label={ariaLabel ?? (id ? undefined : placeholder)}\n\t\t\t\t\t\taria-expanded={listOpen}\n\t\t\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\t\t\taria-controls={listOpen ? listId : undefined}\n\t\t\t\t\t\taria-activedescendant={\n\t\t\t\t\t\t\tlistOpen && activeItem && activeIndex !== null\n\t\t\t\t\t\t\t\t? `${listId}-opt-${activeIndex}`\n\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t</PopoverPrimitive.Anchor>\n\t\t\t\t{listOpen ? (\n\t\t\t\t\t<PopoverPrimitive.Portal>\n\t\t\t\t\t\t<PopoverPrimitive.Content\n\t\t\t\t\t\t\tside=\"bottom\"\n\t\t\t\t\t\t\tsideOffset={OVERLAY_GAP}\n\t\t\t\t\t\t\talign=\"start\"\n\t\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\t\tonPointerMove={(event) => {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tlastPointerPos.current &&\n\t\t\t\t\t\t\t\t\tlastPointerPos.current.x === event.clientX &&\n\t\t\t\t\t\t\t\t\tlastPointerPos.current.y === event.clientY\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tlastPointerPos.current = { x: event.clientX, y: event.clientY };\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonEscapeKeyDown={(e) => {\n\t\t\t\t\t\t\t\tconst evt = e as unknown as {\n\t\t\t\t\t\t\t\t\tisComposing?: boolean;\n\t\t\t\t\t\t\t\t\tnativeEvent?: { isComposing?: boolean };\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tif (evt.isComposing || evt.nativeEvent?.isComposing) {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonOpenAutoFocus={(e) => e.preventDefault()}\n\t\t\t\t\t\t\tonCloseAutoFocus={(e) => e.preventDefault()}\n\t\t\t\t\t\t\tonInteractOutside={(e) => {\n\t\t\t\t\t\t\t\tconst target = e.target as Node | null;\n\t\t\t\t\t\t\t\tif (inputRef.current?.contains(target)) {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tid={listId}\n\t\t\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\t\t\tclassName={overlayPanelClass(\n\t\t\t\t\t\t\t\t\"w-[var(--radix-popover-trigger-width)] max-h-[var(--radix-popover-content-available-height)] overflow-y-auto\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{filtered.map((item, index) => (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tkey={`${listId}-opt-${index}`}\n\t\t\t\t\t\t\t\t\tid={`${listId}-opt-${index}`}\n\t\t\t\t\t\t\t\t\trole=\"option\"\n\t\t\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\t\t\tdisabled={item.disabled}\n\t\t\t\t\t\t\t\t\taria-disabled={item.disabled || undefined}\n\t\t\t\t\t\t\t\t\taria-selected={index === activeIndex}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\toverlayItemClass(\"hover:bg-basalt-accent\"),\n\t\t\t\t\t\t\t\t\t\tindex === activeIndex && \"bg-basalt-accent\",\n\t\t\t\t\t\t\t\t\t\titem.disabled && \"opacity-50\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tonPointerMove={(event) => {\n\t\t\t\t\t\t\t\t\t\tif (item.disabled) {\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tconst { clientX, clientY } = event;\n\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\tlastPointerPos.current &&\n\t\t\t\t\t\t\t\t\t\t\tlastPointerPos.current.x === clientX &&\n\t\t\t\t\t\t\t\t\t\t\tlastPointerPos.current.y === clientY\n\t\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tlastPointerPos.current = { x: clientX, y: clientY };\n\t\t\t\t\t\t\t\t\t\tsetActive(index);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonMouseDown={(event) => event.preventDefault()}\n\t\t\t\t\t\t\t\t\tonClick={() => commitItem(item)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{item.label}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</PopoverPrimitive.Content>\n\t\t\t\t\t</PopoverPrimitive.Portal>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t</PopoverPrimitive.Root>\n\t);\n}\n\nfunction displayOf(items: TypeaheadItem[], value: string) {\n\treturn items.find((item) => item.value === value)?.label ?? value;\n}\n\nfunction filterItems(items: TypeaheadItem[], query: string, showAllWhenEmpty: boolean) {\n\tconst needle = query.trim().toLowerCase();\n\tif (needle === \"\") {\n\t\treturn showAllWhenEmpty ? items : [];\n\t}\n\treturn items.filter((item) => item.label.toLowerCase().includes(needle));\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"upload-queue.js","names":[],"sources":["../../src/components/upload-queue.tsx"],"sourcesContent":["import { Check, FileText, RotateCcw, Trash2, X } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { Button } from \"./button\";\n\nexport interface UploadFile {\n\t/** Stable queue identifier. */\n\tid: string;\n\t/** Display filename. */\n\tname: string;\n\t/** Optional size in bytes. */\n\tsize?: number;\n\t/** Application-owned transport state. */\n\tstatus: \"queued\" | \"uploading\" | \"success\" | \"error\" | \"cancelled\";\n\t/** Progress percentage; omitted means indeterminate. */\n\tprogress?: number;\n\t/** Application's error message. */\n\terror?: string;\n\t/** Optional image/icon. The caller creates and revokes any object URLs. */\n\tpreview?: ReactNode;\n}\nexport interface UploadLabels {\n\tqueued?: string;\n\tuploading?: string;\n\tsuccess?: string;\n\terror?: string;\n\tcancelled?: string;\n\tcancel?: string;\n\tretry?: string;\n\tremove?: string;\n}\nconst DEFAULT_LABELS = {\n\tqueued: \"Queued\",\n\tuploading: \"Uploading\",\n\tsuccess: \"Uploaded\",\n\terror: \"Upload failed\",\n\tcancelled: \"Cancelled\",\n\tcancel: \"Cancel\",\n\tretry: \"Retry\",\n\tremove: \"Remove\",\n};\n\nexport interface UploadItemProps {\n\t/** Current queue entry. The component does not start a network request. */\n\tfile: UploadFile;\n\t/** Cancel a queued or uploading entry. */\n\tonCancel?: (id: string) => void;\n\t/** Retry a failed or cancelled entry. */\n\tonRetry?: (id: string) => void;\n\t/** Remove an entry that is not currently uploading. */\n\tonRemove?: (id: string) => void;\n\t/** Localized statuses and action labels. */\n\tlabels?: UploadLabels;\n\t/** Additional root classes. */\n\tclassName?: string;\n}\n\nexport function UploadItem({\n\tfile,\n\tonCancel,\n\tonRetry,\n\tonRemove,\n\tlabels,\n\tclassName,\n}: UploadItemProps) {\n\tconst text = { ...DEFAULT_LABELS, ...labels };\n\tconst progress =\n\t\ttypeof file.progress === \"number\" && Number.isFinite(file.progress)\n\t\t\t? Math.max(0, Math.min(100, file.progress))\n\t\t\t: undefined;\n\tconst active = file.status === \"queued\" || file.status === \"uploading\";\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\tBASALT_UI_CLASS,\n\t\t\t\t\"flex min-w-0 items-start gap-3 rounded-basalt-lg border border-basalt-border bg-basalt-card p-3\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<span className=\"flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-basalt-md bg-basalt-accent\">\n\t\t\t\t{file.preview ?? (\n\t\t\t\t\t<FileText className=\"size-5 text-basalt-muted-foreground\" aria-hidden=\"true\" />\n\t\t\t\t)}\n\t\t\t</span>\n\t\t\t<div className=\"min-w-0 flex-1 space-y-1\">\n\t\t\t\t<p className=\"break-words text-sm font-medium\">{file.name}</p>\n\t\t\t\t<p className=\"text-xs text-basalt-muted-foreground\">\n\t\t\t\t\t{file.size !== undefined && `${Math.ceil(file.size / 1024).toLocaleString()} KB · `}\n\t\t\t\t\t{text[file.status]}\n\t\t\t\t</p>\n\t\t\t\t{file.status === \"uploading\" && (\n\t\t\t\t\t<div\n\t\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\t\taria-label={file.name}\n\t\t\t\t\t\taria-valuemin={0}\n\t\t\t\t\t\taria-valuemax={100}\n\t\t\t\t\t\taria-valuenow={progress}\n\t\t\t\t\t\tclassName=\"h-1.5 overflow-hidden rounded-full bg-basalt-accent\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tstyle={{ width: progress === undefined ? \"40%\" : `${progress}%` }}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"h-full rounded-full bg-basalt-primary\",\n\t\t\t\t\t\t\t\tprogress === undefined && \"animate-pulse motion-reduce:animate-none\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t{file.status === \"error\" && file.error && (\n\t\t\t\t\t<p role=\"alert\" className=\"break-words text-xs text-basalt-destructive\">\n\t\t\t\t\t\t{file.error}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"flex shrink-0 items-center gap-1\">\n\t\t\t\t{file.status === \"success\" && (\n\t\t\t\t\t<Check className=\"size-4 text-basalt-success\" aria-hidden=\"true\" />\n\t\t\t\t)}\n\t\t\t\t{active && onCancel && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\taria-label={`${text.cancel} ${file.name}`}\n\t\t\t\t\t\tonClick={() => onCancel(file.id)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<X />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t\t{(file.status === \"error\" || file.status === \"cancelled\") && onRetry && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\taria-label={`${text.retry} ${file.name}`}\n\t\t\t\t\t\tonClick={() => onRetry(file.id)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<RotateCcw />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t\t{file.status !== \"uploading\" && onRemove && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\taria-label={`${text.remove} ${file.name}`}\n\t\t\t\t\t\tonClick={() => onRemove(file.id)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Trash2 />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport interface UploadQueueProps extends Omit<UploadItemProps, \"file\"> {\n\t/** Accessible queue name. */\n\tlabel: string;\n\t/** Application-owned queue entries. */\n\tfiles: readonly UploadFile[];\n\t/** Empty queue text. @default \"No files selected.\" */\n\temptyLabel?: string;\n}\n\nexport function UploadQueue({\n\tlabel,\n\tfiles,\n\temptyLabel = \"No files selected.\",\n\tclassName,\n\t...actions\n}: UploadQueueProps) {\n\treturn (\n\t\t<div className={cn(BASALT_UI_CLASS, \"min-w-0 space-y-2\", className)}>\n\t\t\t{files.length === 0 ? (\n\t\t\t\t<p role=\"status\" className=\"text-sm text-basalt-muted-foreground\">\n\t\t\t\t\t{emptyLabel}\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<ul aria-label={label} className=\"m-0 list-none space-y-2 p-0\">\n\t\t\t\t\t{files.map((file) => (\n\t\t\t\t\t\t<li key={file.id}>\n\t\t\t\t\t\t\t<UploadItem file={file} {...actions} />\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"upload-queue.js","names":["DEFAULT_LABELS","UploadItem","text","labels","progress","file","active","cn","BASALT_UI_CLASS","className","FileText","Check","onCancel","Button","X","onRetry","RotateCcw","onRemove","Trash2","UploadQueue","actions","files","emptyLabel","label"],"sources":["../../src/components/upload-queue.tsx"],"sourcesContent":["import { Check, FileText, RotateCcw, Trash2, X } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { Button } from \"./button\";\n\nexport interface UploadFile {\n\t/** Stable queue identifier. */\n\tid: string;\n\t/** Display filename. */\n\tname: string;\n\t/** Optional size in bytes. */\n\tsize?: number;\n\t/** Application-owned transport state. */\n\tstatus: \"queued\" | \"uploading\" | \"success\" | \"error\" | \"cancelled\";\n\t/** Progress percentage; omitted means indeterminate. */\n\tprogress?: number;\n\t/** Application's error message. */\n\terror?: string;\n\t/** Optional image/icon. The caller creates and revokes any object URLs. */\n\tpreview?: ReactNode;\n}\nexport interface UploadLabels {\n\tqueued?: string;\n\tuploading?: string;\n\tsuccess?: string;\n\terror?: string;\n\tcancelled?: string;\n\tcancel?: string;\n\tretry?: string;\n\tremove?: string;\n}\nconst DEFAULT_LABELS = {\n\tqueued: \"Queued\",\n\tuploading: \"Uploading\",\n\tsuccess: \"Uploaded\",\n\terror: \"Upload failed\",\n\tcancelled: \"Cancelled\",\n\tcancel: \"Cancel\",\n\tretry: \"Retry\",\n\tremove: \"Remove\",\n};\n\nexport interface UploadItemProps {\n\t/** Current queue entry. The component does not start a network request. */\n\tfile: UploadFile;\n\t/** Cancel a queued or uploading entry. */\n\tonCancel?: (id: string) => void;\n\t/** Retry a failed or cancelled entry. */\n\tonRetry?: (id: string) => void;\n\t/** Remove an entry that is not currently uploading. */\n\tonRemove?: (id: string) => void;\n\t/** Localized statuses and action labels. */\n\tlabels?: UploadLabels;\n\t/** Additional root classes. */\n\tclassName?: string;\n}\n\nexport function UploadItem({\n\tfile,\n\tonCancel,\n\tonRetry,\n\tonRemove,\n\tlabels,\n\tclassName,\n}: UploadItemProps) {\n\tconst text = { ...DEFAULT_LABELS, ...labels };\n\tconst progress =\n\t\ttypeof file.progress === \"number\" && Number.isFinite(file.progress)\n\t\t\t? Math.max(0, Math.min(100, file.progress))\n\t\t\t: undefined;\n\tconst active = file.status === \"queued\" || file.status === \"uploading\";\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\tBASALT_UI_CLASS,\n\t\t\t\t\"flex min-w-0 items-start gap-3 rounded-basalt-lg border border-basalt-border bg-basalt-card p-3\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<span className=\"flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-basalt-md bg-basalt-accent\">\n\t\t\t\t{file.preview ?? (\n\t\t\t\t\t<FileText className=\"size-5 text-basalt-muted-foreground\" aria-hidden=\"true\" />\n\t\t\t\t)}\n\t\t\t</span>\n\t\t\t<div className=\"min-w-0 flex-1 space-y-1\">\n\t\t\t\t<p className=\"break-words text-sm font-medium\">{file.name}</p>\n\t\t\t\t<p className=\"text-xs text-basalt-muted-foreground\">\n\t\t\t\t\t{file.size !== undefined && `${Math.ceil(file.size / 1024).toLocaleString()} KB · `}\n\t\t\t\t\t{text[file.status]}\n\t\t\t\t</p>\n\t\t\t\t{file.status === \"uploading\" && (\n\t\t\t\t\t<div\n\t\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\t\taria-label={file.name}\n\t\t\t\t\t\taria-valuemin={0}\n\t\t\t\t\t\taria-valuemax={100}\n\t\t\t\t\t\taria-valuenow={progress}\n\t\t\t\t\t\tclassName=\"h-1.5 overflow-hidden rounded-full bg-basalt-accent\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tstyle={{ width: progress === undefined ? \"40%\" : `${progress}%` }}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"h-full rounded-full bg-basalt-primary\",\n\t\t\t\t\t\t\t\tprogress === undefined && \"animate-pulse motion-reduce:animate-none\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t{file.status === \"error\" && file.error && (\n\t\t\t\t\t<p role=\"alert\" className=\"break-words text-xs text-basalt-destructive\">\n\t\t\t\t\t\t{file.error}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"flex shrink-0 items-center gap-1\">\n\t\t\t\t{file.status === \"success\" && (\n\t\t\t\t\t<Check className=\"size-4 text-basalt-success\" aria-hidden=\"true\" />\n\t\t\t\t)}\n\t\t\t\t{active && onCancel && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\taria-label={`${text.cancel} ${file.name}`}\n\t\t\t\t\t\tonClick={() => onCancel(file.id)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<X />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t\t{(file.status === \"error\" || file.status === \"cancelled\") && onRetry && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\taria-label={`${text.retry} ${file.name}`}\n\t\t\t\t\t\tonClick={() => onRetry(file.id)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<RotateCcw />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t\t{file.status !== \"uploading\" && onRemove && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\taria-label={`${text.remove} ${file.name}`}\n\t\t\t\t\t\tonClick={() => onRemove(file.id)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Trash2 />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport interface UploadQueueProps extends Omit<UploadItemProps, \"file\"> {\n\t/** Accessible queue name. */\n\tlabel: string;\n\t/** Application-owned queue entries. */\n\tfiles: readonly UploadFile[];\n\t/** Empty queue text. @default \"No files selected.\" */\n\temptyLabel?: string;\n}\n\nexport function UploadQueue({\n\tlabel,\n\tfiles,\n\temptyLabel = \"No files selected.\",\n\tclassName,\n\t...actions\n}: UploadQueueProps) {\n\treturn (\n\t\t<div className={cn(BASALT_UI_CLASS, \"min-w-0 space-y-2\", className)}>\n\t\t\t{files.length === 0 ? (\n\t\t\t\t<p role=\"status\" className=\"text-sm text-basalt-muted-foreground\">\n\t\t\t\t\t{emptyLabel}\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<ul aria-label={label} className=\"m-0 list-none space-y-2 p-0\">\n\t\t\t\t\t{files.map((file) => (\n\t\t\t\t\t\t<li key={file.id}>\n\t\t\t\t\t\t\t<UploadItem file={file} {...actions} />\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
@@ -209,7 +209,7 @@ function E(e) {
209
209
  return y(i, b) >= a ? "0 0% 10%" : "0 0% 100%";
210
210
  }
211
211
  function D(e, t = !1) {
212
- typeof document > "u" || O(m(e), t);
212
+ typeof document < "u" && O(m(e), t);
213
213
  }
214
214
  function O(e, t) {
215
215
  let n = T(e, t), r = document.documentElement;
@@ -1 +1 @@
1
- {"version":3,"file":"accent.js","names":[],"sources":["../../src/providers/accent.tsx"],"sourcesContent":["import {\n\tcreateContext,\n\ttype ReactNode,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseState,\n\tuseSyncExternalStore,\n} from \"react\";\nimport { createPreferenceStore } from \"../utils/preference-store\";\n\nexport type AccentSwatch = {\n\tid: string;\n\tlabel: string;\n\ttoken: string;\n\tlight: string;\n\tdark: string;\n};\n\n/** Twelve candy accents inspired by the translucent iMac and iPhone 5C finishes. */\nexport const ACCENT_SWATCHES: readonly AccentSwatch[] = [\n\t{\n\t\tid: \"primary\",\n\t\tlabel: \"Blue\",\n\t\ttoken: \"--basalt-accent-1\",\n\t\tlight: \"204 88% 62%\",\n\t\tdark: \"204 90% 70%\",\n\t},\n\t{\n\t\tid: \"sky\",\n\t\tlabel: \"Ice\",\n\t\ttoken: \"--basalt-accent-2\",\n\t\tlight: \"191 79% 70%\",\n\t\tdark: \"191 79% 74%\",\n\t},\n\t{\n\t\tid: \"teal\",\n\t\tlabel: \"Bondi\",\n\t\ttoken: \"--basalt-accent-3\",\n\t\tlight: \"182 62% 47%\",\n\t\tdark: \"182 62% 63%\",\n\t},\n\t{\n\t\tid: \"green\",\n\t\tlabel: \"Green\",\n\t\ttoken: \"--basalt-accent-4\",\n\t\tlight: \"113 58% 62%\",\n\t\tdark: \"113 58% 70%\",\n\t},\n\t{\n\t\tid: \"lime\",\n\t\tlabel: \"Lime\",\n\t\ttoken: \"--basalt-accent-5\",\n\t\tlight: \"79 70% 64%\",\n\t\tdark: \"79 70% 70%\",\n\t},\n\t{\n\t\tid: \"amber\",\n\t\tlabel: \"Yellow\",\n\t\ttoken: \"--basalt-accent-6\",\n\t\tlight: \"49 100% 69%\",\n\t\tdark: \"49 100% 73%\",\n\t},\n\t{\n\t\tid: \"orange\",\n\t\tlabel: \"Tangerine\",\n\t\ttoken: \"--basalt-accent-7\",\n\t\tlight: \"27 95% 65%\",\n\t\tdark: \"27 95% 72%\",\n\t},\n\t{\n\t\tid: \"red\",\n\t\tlabel: \"Strawberry\",\n\t\ttoken: \"--basalt-accent-8\",\n\t\tlight: \"5 85% 66%\",\n\t\tdark: \"5 85% 73%\",\n\t},\n\t{\n\t\tid: \"rose\",\n\t\tlabel: \"Pink\",\n\t\ttoken: \"--basalt-accent-9\",\n\t\tlight: \"345 88% 72%\",\n\t\tdark: \"345 88% 77%\",\n\t},\n\t{\n\t\tid: \"purple\",\n\t\tlabel: \"Grape\",\n\t\ttoken: \"--basalt-accent-10\",\n\t\tlight: \"280 53% 71%\",\n\t\tdark: \"280 53% 77%\",\n\t},\n\t{\n\t\tid: \"indigo\",\n\t\tlabel: \"Blueberry\",\n\t\ttoken: \"--basalt-accent-11\",\n\t\tlight: \"237 66% 69%\",\n\t\tdark: \"237 66% 77%\",\n\t},\n\t{\n\t\tid: \"gray\",\n\t\tlabel: \"Pearl\",\n\t\ttoken: \"--basalt-accent-12\",\n\t\tlight: \"210 20% 87%\",\n\t\tdark: \"210 20% 88%\",\n\t},\n];\n\nconst LEGACY_ACCENT_IDS = new Map<string, string>([\n\t[\"jade\", \"teal\"],\n\t[\"vermilion\", \"red\"],\n\t[\"magenta\", \"rose\"],\n\t[\"orchid\", \"purple\"],\n\t[\"cobalt\", \"indigo\"],\n\t[\"steel\", \"primary\"],\n\t[\"cadet\", \"sky\"],\n\t[\"seafoam\", \"teal\"],\n\t[\"olive\", \"lime\"],\n\t[\"gold\", \"amber\"],\n\t[\"tangerine\", \"orange\"],\n\t[\"crimson\", \"red\"],\n]);\n\nexport const DEFAULT_ACCENT_ID = \"primary\";\nconst STORAGE_KEY = \"basalt-accent\";\n\ntype AccentContextValue = {\n\taccent: string;\n\tsetAccent: (id: string) => void;\n\tswatches: readonly AccentSwatch[];\n};\n\nconst AccentContext = createContext<AccentContextValue | null>(null);\n\nexport function accentSwatchById(id: string | null | undefined): AccentSwatch {\n\tconst normalized = LEGACY_ACCENT_IDS.get(id ?? \"\") ?? id;\n\treturn ACCENT_SWATCHES.find((swatch) => swatch.id === normalized) ?? ACCENT_SWATCHES[0];\n}\n\nfunction channel(c: number) {\n\treturn c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n}\n\nfunction parseHslChannels(hsl: string): [number, number, number] {\n\tconst [hue, sat, light] = hsl.trim().split(/\\s+/);\n\tconst h = Number(hue);\n\tconst s = Number(sat.replace(\"%\", \"\")) / 100;\n\tconst l = Number(light.replace(\"%\", \"\")) / 100;\n\treturn [h, s, l];\n}\n\nfunction hslToRgbChannels(h: number, s: number, l: number): [number, number, number] {\n\tconst k = (n: number) => (n + h / 30) % 12;\n\tconst a = s * Math.min(l, 1 - l);\n\tconst f = (n: number) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));\n\treturn [255 * f(0), 255 * f(8), 255 * f(4)];\n}\n\nfunction relativeLuminance(rgb: [number, number, number]): number {\n\treturn (\n\t\t0.2126 * channel(rgb[0] / 255) + 0.7152 * channel(rgb[1] / 255) + 0.0722 * channel(rgb[2] / 255)\n\t);\n}\n\nfunction wcagContrast(rgbA: [number, number, number], rgbB: [number, number, number]): number {\n\tconst lA = relativeLuminance(rgbA);\n\tconst lB = relativeLuminance(rgbB);\n\treturn (Math.max(lA, lB) + 0.05) / (Math.min(lA, lB) + 0.05);\n}\n\nconst DARK_TEXT_RGB: [number, number, number] = [26, 26, 26]; // 0 0% 10%\nconst WHITE_TEXT_RGB: [number, number, number] = [255, 255, 255]; // 0 0% 100%\n\nconst LIGHT_SURFACES_RGB: [number, number, number][] = [\n\t[238, 239, 242], // background\n\t[246, 247, 248], // card\n\t[252, 252, 253], // secondary\n\t[255, 255, 255], // bright\n];\n\nconst DARK_SURFACES_RGB: [number, number, number][] = [\n\t[23, 23, 23], // background\n\t[27, 27, 27], // card\n\t[31, 31, 31], // secondary\n\t[36, 36, 36], // bright\n];\n\nfunction compositeRgb(\n\ttop: [number, number, number, number],\n\tbottom: [number, number, number],\n): [number, number, number] {\n\treturn [\n\t\ttop[0] * top[3] + bottom[0] * (1 - top[3]),\n\t\ttop[1] * top[3] + bottom[1] * (1 - top[3]),\n\t\ttop[2] * top[3] + bottom[2] * (1 - top[3]),\n\t];\n}\n\nfunction deriveSemanticPrimary(swatch: AccentSwatch, dark: boolean): string {\n\tconst [h, s, origL] = parseHslChannels(dark ? swatch.dark : swatch.light);\n\tconst targetSurfaces = dark ? DARK_SURFACES_RGB : LIGHT_SURFACES_RGB;\n\tconst foreground = dark ? DARK_TEXT_RGB : WHITE_TEXT_RGB;\n\tconst start = dark\n\t\t? Math.max(Math.round(origL * 100), 45)\n\t\t: Math.min(Math.round(origL * 100), 60);\n\n\tfor (let l = start; dark ? l <= 85 : l >= 15; l += dark ? 1 : -1) {\n\t\tconst rgb = hslToRgbChannels(h, s, l / 100);\n\t\tconst minTextR = Math.min(...targetSurfaces.map((bg) => wcagContrast(rgb, bg)));\n\t\tif (minTextR < 4.55) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (wcagContrast(rgb, foreground) < 4.55) {\n\t\t\tcontinue;\n\t\t}\n\t\tlet hoverOk = true;\n\t\tfor (const bg of targetSurfaces) {\n\t\t\tconst hoverBg = compositeRgb([...rgb, 0.9], bg);\n\t\t\tif (wcagContrast(hoverBg, foreground) < 4.55) {\n\t\t\t\thoverOk = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (hoverOk) {\n\t\t\treturn `${h} ${Math.round(s * 100)}% ${l}%`;\n\t\t}\n\t}\n\treturn dark ? swatch.dark : swatch.light;\n}\n\nexport function accentForeground(hsl: string) {\n\tconst [h, s, l] = parseHslChannels(hsl);\n\tconst rgb = hslToRgbChannels(h, s, l);\n\tconst contrastWhite = wcagContrast(rgb, WHITE_TEXT_RGB);\n\tconst contrastDark = wcagContrast(rgb, DARK_TEXT_RGB);\n\treturn contrastDark >= contrastWhite ? (\"0 0% 10%\" as const) : (\"0 0% 100%\" as const);\n}\n\nexport function applyAccent(id: string, dark = false) {\n\tif (typeof document === \"undefined\") {\n\t\treturn;\n\t}\n\tapplySwatch(accentSwatchById(id), dark);\n}\n\nfunction applySwatch(swatch: AccentSwatch, dark: boolean) {\n\tconst semanticPrimary = deriveSemanticPrimary(swatch, dark);\n\tconst root = document.documentElement;\n\troot.style.setProperty(\"--basalt-primary\", semanticPrimary);\n\troot.style.setProperty(\"--basalt-primary-foreground\", accentForeground(semanticPrimary));\n\troot.style.setProperty(\"--basalt-ring\", semanticPrimary);\n\troot.dataset.accent = swatch.id;\n}\n\nexport interface AccentProviderProps {\n\t/** Color overrides for the twelve preset IDs, using HSL channels (for example `204 88% 62%`).\n\t * Invalid pairs and unknown IDs are ignored. Persistence belongs to the application.\n\t * Chart colors remain fixed. */\n\tpaletteOverrides?: Readonly<Record<string, { light: string; dark: string }>>;\n\t/**\n\t * Application components wrapped by the accent context.\n\t */\n\tchildren: ReactNode;\n\t/**\n\t * Storage key used for accent persistence.\n\t * @default \"basalt-accent\"\n\t */\n\tstorageKey?: string;\n\t/**\n\t * Initial accent identifier used when no stored preference exists or during SSR.\n\t * @default \"primary\"\n\t */\n\tdefaultAccent?: string;\n\t/**\n\t * Whether to persist accent changes to localStorage.\n\t * If false, localStorage is never read or written.\n\t * @default true\n\t */\n\tpersist?: boolean;\n\t/**\n\t * Controlled accent identifier. When provided, the provider acts as a controlled component\n\t * and internal state is driven by this prop.\n\t */\n\taccent?: string;\n\t/**\n\t * Callback fired when accent change is requested.\n\t * In controlled mode, callers are responsible for updating `accent`.\n\t */\n\tonAccentChange?: (accent: string) => void;\n\t/**\n\t * Whether to apply accent CSS variables and data-accent attribute to document.documentElement.\n\t * Set to false when a host theme system manages CSS variables directly.\n\t * @default true\n\t */\n\tapplyToDocument?: boolean;\n}\n\nfunction isValidAccentId(value: unknown): value is string {\n\treturn (\n\t\ttypeof value === \"string\" &&\n\t\t(ACCENT_SWATCHES.some((s) => s.id === value) || LEGACY_ACCENT_IDS.has(value))\n\t);\n}\n\nfunction validHsl(value: unknown): value is string {\n\tif (typeof value !== \"string\" || !/^\\d+(?:\\.\\d+)?\\s+\\d+(?:\\.\\d+)?%\\s+\\d+(?:\\.\\d+)?%$/.test(value))\n\t\treturn false;\n\tconst [h, s, l] = parseHslChannels(value);\n\treturn h <= 360 && s <= 1 && l <= 1;\n}\n\nfunction isDarkMode(): boolean {\n\tif (typeof document === \"undefined\") {\n\t\treturn false;\n\t}\n\treturn document.documentElement.classList.contains(\"dark\");\n}\n\nexport function AccentProvider({\n\tchildren,\n\tpaletteOverrides,\n\tstorageKey = STORAGE_KEY,\n\tdefaultAccent = DEFAULT_ACCENT_ID,\n\tpersist = true,\n\taccent: controlledAccent,\n\tonAccentChange,\n\tapplyToDocument = true,\n}: AccentProviderProps) {\n\tconst isControlled = controlledAccent !== undefined;\n\tconst swatches = useMemo(\n\t\t() =>\n\t\t\tACCENT_SWATCHES.map((swatch) => {\n\t\t\t\tconst override = paletteOverrides?.[swatch.id];\n\t\t\t\treturn override && validHsl(override.light) && validHsl(override.dark)\n\t\t\t\t\t? { ...swatch, light: override.light, dark: override.dark }\n\t\t\t\t\t: swatch;\n\t\t\t}),\n\t\t[paletteOverrides],\n\t);\n\n\tconst [store] = useState(() =>\n\t\tcreatePreferenceStore({\n\t\t\tstorageKey,\n\t\t\tdefaultValue: defaultAccent,\n\t\t\tpersist,\n\t\t\tisValid: isValidAccentId,\n\t\t\tnormalize: (value) => accentSwatchById(value).id,\n\t\t\teventName: \"basalt:accent-change\",\n\t\t}),\n\t);\n\n\tuseEffect(() => {\n\t\tstore.updateConfig({ storageKey, defaultValue: defaultAccent, persist });\n\t}, [store, storageKey, defaultAccent, persist]);\n\n\tconst getServerSnapshot = useCallback(() => accentSwatchById(defaultAccent).id, [defaultAccent]);\n\n\tconst storeAccent = useSyncExternalStore(store.subscribe, store.getSnapshot, getServerSnapshot);\n\n\tconst currentAccent = isControlled ? accentSwatchById(controlledAccent).id : storeAccent;\n\n\tuseEffect(() => {\n\t\tif (!applyToDocument || typeof document === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\t\tconst apply = () => {\n\t\t\tconst dark = isDarkMode();\n\t\t\tfor (const swatch of swatches)\n\t\t\t\tdocument.documentElement.style.setProperty(swatch.token, dark ? swatch.dark : swatch.light);\n\t\t\tapplySwatch(swatches.find((swatch) => swatch.id === currentAccent) ?? swatches[0], dark);\n\t\t};\n\t\tapply();\n\t\tconst observer = new MutationObserver(apply);\n\t\tobserver.observe(document.documentElement, { attributes: true, attributeFilter: [\"class\"] });\n\t\treturn () => observer.disconnect();\n\t}, [currentAccent, applyToDocument, swatches]);\n\n\tconst setAccent = useCallback(\n\t\t(next: string) => {\n\t\t\tconst validId = accentSwatchById(next).id;\n\t\t\tif (isControlled) {\n\t\t\t\tonAccentChange?.(validId);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstore.setValue(validId);\n\t\t\tonAccentChange?.(validId);\n\t\t},\n\t\t[isControlled, onAccentChange, store],\n\t);\n\n\tconst value = useMemo(\n\t\t() => ({ accent: currentAccent, setAccent, swatches }),\n\t\t[currentAccent, setAccent, swatches],\n\t);\n\treturn <AccentContext.Provider value={value}>{children}</AccentContext.Provider>;\n}\n\nexport function useAccent() {\n\tconst ctx = useContext(AccentContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useAccent must be used within AccentProvider\");\n\t}\n\treturn ctx;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"accent.js","names":["ACCENT_SWATCHES","LEGACY_ACCENT_IDS","DEFAULT_ACCENT_ID","STORAGE_KEY","AccentContext","createContext","accentSwatchById","normalized","id","swatch","channel","c","parseHslChannels","hue","sat","light","hsl","hslToRgbChannels","k","n","h","a","s","l","f","relativeLuminance","rgb","wcagContrast","lA","rgbA","lB","rgbB","DARK_TEXT_RGB","WHITE_TEXT_RGB","LIGHT_SURFACES_RGB","DARK_SURFACES_RGB","compositeRgb","top","bottom","deriveSemanticPrimary","origL","dark","targetSurfaces","foreground","start","bg","hoverOk","accentForeground","contrastWhite","applyAccent","applySwatch","semanticPrimary","root","isValidAccentId","value","validHsl","isDarkMode","AccentProvider","controlledAccent","isControlled","swatches","useMemo","override","paletteOverrides","store","useState","createPreferenceStore","defaultAccent","storageKey","persist","getServerSnapshot","useCallback","storeAccent","useSyncExternalStore","currentAccent","applyToDocument","apply","observer","setAccent","validId","next","onAccentChange","useAccent","ctx","useContext"],"sources":["../../src/providers/accent.tsx"],"sourcesContent":["import {\n\tcreateContext,\n\ttype ReactNode,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseState,\n\tuseSyncExternalStore,\n} from \"react\";\nimport { createPreferenceStore } from \"../utils/preference-store\";\n\nexport type AccentSwatch = {\n\tid: string;\n\tlabel: string;\n\ttoken: string;\n\tlight: string;\n\tdark: string;\n};\n\n/** Twelve candy accents inspired by the translucent iMac and iPhone 5C finishes. */\nexport const ACCENT_SWATCHES: readonly AccentSwatch[] = [\n\t{\n\t\tid: \"primary\",\n\t\tlabel: \"Blue\",\n\t\ttoken: \"--basalt-accent-1\",\n\t\tlight: \"204 88% 62%\",\n\t\tdark: \"204 90% 70%\",\n\t},\n\t{\n\t\tid: \"sky\",\n\t\tlabel: \"Ice\",\n\t\ttoken: \"--basalt-accent-2\",\n\t\tlight: \"191 79% 70%\",\n\t\tdark: \"191 79% 74%\",\n\t},\n\t{\n\t\tid: \"teal\",\n\t\tlabel: \"Bondi\",\n\t\ttoken: \"--basalt-accent-3\",\n\t\tlight: \"182 62% 47%\",\n\t\tdark: \"182 62% 63%\",\n\t},\n\t{\n\t\tid: \"green\",\n\t\tlabel: \"Green\",\n\t\ttoken: \"--basalt-accent-4\",\n\t\tlight: \"113 58% 62%\",\n\t\tdark: \"113 58% 70%\",\n\t},\n\t{\n\t\tid: \"lime\",\n\t\tlabel: \"Lime\",\n\t\ttoken: \"--basalt-accent-5\",\n\t\tlight: \"79 70% 64%\",\n\t\tdark: \"79 70% 70%\",\n\t},\n\t{\n\t\tid: \"amber\",\n\t\tlabel: \"Yellow\",\n\t\ttoken: \"--basalt-accent-6\",\n\t\tlight: \"49 100% 69%\",\n\t\tdark: \"49 100% 73%\",\n\t},\n\t{\n\t\tid: \"orange\",\n\t\tlabel: \"Tangerine\",\n\t\ttoken: \"--basalt-accent-7\",\n\t\tlight: \"27 95% 65%\",\n\t\tdark: \"27 95% 72%\",\n\t},\n\t{\n\t\tid: \"red\",\n\t\tlabel: \"Strawberry\",\n\t\ttoken: \"--basalt-accent-8\",\n\t\tlight: \"5 85% 66%\",\n\t\tdark: \"5 85% 73%\",\n\t},\n\t{\n\t\tid: \"rose\",\n\t\tlabel: \"Pink\",\n\t\ttoken: \"--basalt-accent-9\",\n\t\tlight: \"345 88% 72%\",\n\t\tdark: \"345 88% 77%\",\n\t},\n\t{\n\t\tid: \"purple\",\n\t\tlabel: \"Grape\",\n\t\ttoken: \"--basalt-accent-10\",\n\t\tlight: \"280 53% 71%\",\n\t\tdark: \"280 53% 77%\",\n\t},\n\t{\n\t\tid: \"indigo\",\n\t\tlabel: \"Blueberry\",\n\t\ttoken: \"--basalt-accent-11\",\n\t\tlight: \"237 66% 69%\",\n\t\tdark: \"237 66% 77%\",\n\t},\n\t{\n\t\tid: \"gray\",\n\t\tlabel: \"Pearl\",\n\t\ttoken: \"--basalt-accent-12\",\n\t\tlight: \"210 20% 87%\",\n\t\tdark: \"210 20% 88%\",\n\t},\n];\n\nconst LEGACY_ACCENT_IDS = new Map<string, string>([\n\t[\"jade\", \"teal\"],\n\t[\"vermilion\", \"red\"],\n\t[\"magenta\", \"rose\"],\n\t[\"orchid\", \"purple\"],\n\t[\"cobalt\", \"indigo\"],\n\t[\"steel\", \"primary\"],\n\t[\"cadet\", \"sky\"],\n\t[\"seafoam\", \"teal\"],\n\t[\"olive\", \"lime\"],\n\t[\"gold\", \"amber\"],\n\t[\"tangerine\", \"orange\"],\n\t[\"crimson\", \"red\"],\n]);\n\nexport const DEFAULT_ACCENT_ID = \"primary\";\nconst STORAGE_KEY = \"basalt-accent\";\n\ntype AccentContextValue = {\n\taccent: string;\n\tsetAccent: (id: string) => void;\n\tswatches: readonly AccentSwatch[];\n};\n\nconst AccentContext = createContext<AccentContextValue | null>(null);\n\nexport function accentSwatchById(id: string | null | undefined): AccentSwatch {\n\tconst normalized = LEGACY_ACCENT_IDS.get(id ?? \"\") ?? id;\n\treturn ACCENT_SWATCHES.find((swatch) => swatch.id === normalized) ?? ACCENT_SWATCHES[0];\n}\n\nfunction channel(c: number) {\n\treturn c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n}\n\nfunction parseHslChannels(hsl: string): [number, number, number] {\n\tconst [hue, sat, light] = hsl.trim().split(/\\s+/);\n\tconst h = Number(hue);\n\tconst s = Number(sat.replace(\"%\", \"\")) / 100;\n\tconst l = Number(light.replace(\"%\", \"\")) / 100;\n\treturn [h, s, l];\n}\n\nfunction hslToRgbChannels(h: number, s: number, l: number): [number, number, number] {\n\tconst k = (n: number) => (n + h / 30) % 12;\n\tconst a = s * Math.min(l, 1 - l);\n\tconst f = (n: number) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));\n\treturn [255 * f(0), 255 * f(8), 255 * f(4)];\n}\n\nfunction relativeLuminance(rgb: [number, number, number]): number {\n\treturn (\n\t\t0.2126 * channel(rgb[0] / 255) + 0.7152 * channel(rgb[1] / 255) + 0.0722 * channel(rgb[2] / 255)\n\t);\n}\n\nfunction wcagContrast(rgbA: [number, number, number], rgbB: [number, number, number]): number {\n\tconst lA = relativeLuminance(rgbA);\n\tconst lB = relativeLuminance(rgbB);\n\treturn (Math.max(lA, lB) + 0.05) / (Math.min(lA, lB) + 0.05);\n}\n\nconst DARK_TEXT_RGB: [number, number, number] = [26, 26, 26]; // 0 0% 10%\nconst WHITE_TEXT_RGB: [number, number, number] = [255, 255, 255]; // 0 0% 100%\n\nconst LIGHT_SURFACES_RGB: [number, number, number][] = [\n\t[238, 239, 242], // background\n\t[246, 247, 248], // card\n\t[252, 252, 253], // secondary\n\t[255, 255, 255], // bright\n];\n\nconst DARK_SURFACES_RGB: [number, number, number][] = [\n\t[23, 23, 23], // background\n\t[27, 27, 27], // card\n\t[31, 31, 31], // secondary\n\t[36, 36, 36], // bright\n];\n\nfunction compositeRgb(\n\ttop: [number, number, number, number],\n\tbottom: [number, number, number],\n): [number, number, number] {\n\treturn [\n\t\ttop[0] * top[3] + bottom[0] * (1 - top[3]),\n\t\ttop[1] * top[3] + bottom[1] * (1 - top[3]),\n\t\ttop[2] * top[3] + bottom[2] * (1 - top[3]),\n\t];\n}\n\nfunction deriveSemanticPrimary(swatch: AccentSwatch, dark: boolean): string {\n\tconst [h, s, origL] = parseHslChannels(dark ? swatch.dark : swatch.light);\n\tconst targetSurfaces = dark ? DARK_SURFACES_RGB : LIGHT_SURFACES_RGB;\n\tconst foreground = dark ? DARK_TEXT_RGB : WHITE_TEXT_RGB;\n\tconst start = dark\n\t\t? Math.max(Math.round(origL * 100), 45)\n\t\t: Math.min(Math.round(origL * 100), 60);\n\n\tfor (let l = start; dark ? l <= 85 : l >= 15; l += dark ? 1 : -1) {\n\t\tconst rgb = hslToRgbChannels(h, s, l / 100);\n\t\tconst minTextR = Math.min(...targetSurfaces.map((bg) => wcagContrast(rgb, bg)));\n\t\tif (minTextR < 4.55) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (wcagContrast(rgb, foreground) < 4.55) {\n\t\t\tcontinue;\n\t\t}\n\t\tlet hoverOk = true;\n\t\tfor (const bg of targetSurfaces) {\n\t\t\tconst hoverBg = compositeRgb([...rgb, 0.9], bg);\n\t\t\tif (wcagContrast(hoverBg, foreground) < 4.55) {\n\t\t\t\thoverOk = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (hoverOk) {\n\t\t\treturn `${h} ${Math.round(s * 100)}% ${l}%`;\n\t\t}\n\t}\n\treturn dark ? swatch.dark : swatch.light;\n}\n\nexport function accentForeground(hsl: string) {\n\tconst [h, s, l] = parseHslChannels(hsl);\n\tconst rgb = hslToRgbChannels(h, s, l);\n\tconst contrastWhite = wcagContrast(rgb, WHITE_TEXT_RGB);\n\tconst contrastDark = wcagContrast(rgb, DARK_TEXT_RGB);\n\treturn contrastDark >= contrastWhite ? (\"0 0% 10%\" as const) : (\"0 0% 100%\" as const);\n}\n\nexport function applyAccent(id: string, dark = false) {\n\tif (typeof document === \"undefined\") {\n\t\treturn;\n\t}\n\tapplySwatch(accentSwatchById(id), dark);\n}\n\nfunction applySwatch(swatch: AccentSwatch, dark: boolean) {\n\tconst semanticPrimary = deriveSemanticPrimary(swatch, dark);\n\tconst root = document.documentElement;\n\troot.style.setProperty(\"--basalt-primary\", semanticPrimary);\n\troot.style.setProperty(\"--basalt-primary-foreground\", accentForeground(semanticPrimary));\n\troot.style.setProperty(\"--basalt-ring\", semanticPrimary);\n\troot.dataset.accent = swatch.id;\n}\n\nexport interface AccentProviderProps {\n\t/** Color overrides for the twelve preset IDs, using HSL channels (for example `204 88% 62%`).\n\t * Invalid pairs and unknown IDs are ignored. Persistence belongs to the application.\n\t * Chart colors remain fixed. */\n\tpaletteOverrides?: Readonly<Record<string, { light: string; dark: string }>>;\n\t/**\n\t * Application components wrapped by the accent context.\n\t */\n\tchildren: ReactNode;\n\t/**\n\t * Storage key used for accent persistence.\n\t * @default \"basalt-accent\"\n\t */\n\tstorageKey?: string;\n\t/**\n\t * Initial accent identifier used when no stored preference exists or during SSR.\n\t * @default \"primary\"\n\t */\n\tdefaultAccent?: string;\n\t/**\n\t * Whether to persist accent changes to localStorage.\n\t * If false, localStorage is never read or written.\n\t * @default true\n\t */\n\tpersist?: boolean;\n\t/**\n\t * Controlled accent identifier. When provided, the provider acts as a controlled component\n\t * and internal state is driven by this prop.\n\t */\n\taccent?: string;\n\t/**\n\t * Callback fired when accent change is requested.\n\t * In controlled mode, callers are responsible for updating `accent`.\n\t */\n\tonAccentChange?: (accent: string) => void;\n\t/**\n\t * Whether to apply accent CSS variables and data-accent attribute to document.documentElement.\n\t * Set to false when a host theme system manages CSS variables directly.\n\t * @default true\n\t */\n\tapplyToDocument?: boolean;\n}\n\nfunction isValidAccentId(value: unknown): value is string {\n\treturn (\n\t\ttypeof value === \"string\" &&\n\t\t(ACCENT_SWATCHES.some((s) => s.id === value) || LEGACY_ACCENT_IDS.has(value))\n\t);\n}\n\nfunction validHsl(value: unknown): value is string {\n\tif (typeof value !== \"string\" || !/^\\d+(?:\\.\\d+)?\\s+\\d+(?:\\.\\d+)?%\\s+\\d+(?:\\.\\d+)?%$/.test(value))\n\t\treturn false;\n\tconst [h, s, l] = parseHslChannels(value);\n\treturn h <= 360 && s <= 1 && l <= 1;\n}\n\nfunction isDarkMode(): boolean {\n\tif (typeof document === \"undefined\") {\n\t\treturn false;\n\t}\n\treturn document.documentElement.classList.contains(\"dark\");\n}\n\nexport function AccentProvider({\n\tchildren,\n\tpaletteOverrides,\n\tstorageKey = STORAGE_KEY,\n\tdefaultAccent = DEFAULT_ACCENT_ID,\n\tpersist = true,\n\taccent: controlledAccent,\n\tonAccentChange,\n\tapplyToDocument = true,\n}: AccentProviderProps) {\n\tconst isControlled = controlledAccent !== undefined;\n\tconst swatches = useMemo(\n\t\t() =>\n\t\t\tACCENT_SWATCHES.map((swatch) => {\n\t\t\t\tconst override = paletteOverrides?.[swatch.id];\n\t\t\t\treturn override && validHsl(override.light) && validHsl(override.dark)\n\t\t\t\t\t? { ...swatch, light: override.light, dark: override.dark }\n\t\t\t\t\t: swatch;\n\t\t\t}),\n\t\t[paletteOverrides],\n\t);\n\n\tconst [store] = useState(() =>\n\t\tcreatePreferenceStore({\n\t\t\tstorageKey,\n\t\t\tdefaultValue: defaultAccent,\n\t\t\tpersist,\n\t\t\tisValid: isValidAccentId,\n\t\t\tnormalize: (value) => accentSwatchById(value).id,\n\t\t\teventName: \"basalt:accent-change\",\n\t\t}),\n\t);\n\n\tuseEffect(() => {\n\t\tstore.updateConfig({ storageKey, defaultValue: defaultAccent, persist });\n\t}, [store, storageKey, defaultAccent, persist]);\n\n\tconst getServerSnapshot = useCallback(() => accentSwatchById(defaultAccent).id, [defaultAccent]);\n\n\tconst storeAccent = useSyncExternalStore(store.subscribe, store.getSnapshot, getServerSnapshot);\n\n\tconst currentAccent = isControlled ? accentSwatchById(controlledAccent).id : storeAccent;\n\n\tuseEffect(() => {\n\t\tif (!applyToDocument || typeof document === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\t\tconst apply = () => {\n\t\t\tconst dark = isDarkMode();\n\t\t\tfor (const swatch of swatches)\n\t\t\t\tdocument.documentElement.style.setProperty(swatch.token, dark ? swatch.dark : swatch.light);\n\t\t\tapplySwatch(swatches.find((swatch) => swatch.id === currentAccent) ?? swatches[0], dark);\n\t\t};\n\t\tapply();\n\t\tconst observer = new MutationObserver(apply);\n\t\tobserver.observe(document.documentElement, { attributes: true, attributeFilter: [\"class\"] });\n\t\treturn () => observer.disconnect();\n\t}, [currentAccent, applyToDocument, swatches]);\n\n\tconst setAccent = useCallback(\n\t\t(next: string) => {\n\t\t\tconst validId = accentSwatchById(next).id;\n\t\t\tif (isControlled) {\n\t\t\t\tonAccentChange?.(validId);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstore.setValue(validId);\n\t\t\tonAccentChange?.(validId);\n\t\t},\n\t\t[isControlled, onAccentChange, store],\n\t);\n\n\tconst value = useMemo(\n\t\t() => ({ accent: currentAccent, setAccent, swatches }),\n\t\t[currentAccent, setAccent, swatches],\n\t);\n\treturn <AccentContext.Provider value={value}>{children}</AccentContext.Provider>;\n}\n\nexport function useAccent() {\n\tconst ctx = useContext(AccentContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useAccent must be used within AccentProvider\");\n\t}\n\treturn ctx;\n}\n"],"mappings":";;;;;AAqBA,IAAaA,IAA2C;CACvD;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;CACA;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;CACA;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;CACA;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;CACA;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;CACA;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;CACA;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;CACA;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;CACA;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;CACA;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;CACA;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;CACA;EACC,IAAI;EACJ,OAAO;EACP,OAAO;EACP,OAAO;EACP,MAAM;CACP;AACD,GAEMC,oBAAoB,IAAI,IAAoB;CACjD,CAAC,QAAQ,MAAM;CACf,CAAC,aAAa,KAAK;CACnB,CAAC,WAAW,MAAM;CAClB,CAAC,UAAU,QAAQ;CACnB,CAAC,UAAU,QAAQ;CACnB,CAAC,SAAS,SAAS;CACnB,CAAC,SAAS,KAAK;CACf,CAAC,WAAW,MAAM;CAClB,CAAC,SAAS,MAAM;CAChB,CAAC,QAAQ,OAAO;CAChB,CAAC,aAAa,QAAQ;CACtB,CAAC,WAAW,KAAK;AAClB,CAAC,GAEYC,IAAoB,WAC3BC,IAAc,iBAQdC,IAAgBC,EAAyC,IAAI;AAEnE,SAAgBC,EAAiB,GAA6C;CAC7E,IAAMC,IAAaN,EAAkB,IAAIO,KAAM,EAAE,KAAKA;CACtD,OAAOR,EAAgB,MAAM,MAAWS,EAAO,OAAOF,CAAU,KAAKP,EAAgB;AACtF;AAEA,SAASU,EAAQ,GAAW;CAC3B,OAAOC,KAAK,SAAUA,IAAI,UAAUA,IAAI,QAAS,UAAU;AAC5D;AAEA,SAASC,EAAiB,GAAuC;CAChE,IAAM,CAACC,GAAKC,GAAKC,KAASC,EAAI,KAAK,CAAC,CAAC,MAAM,KAAK;CAIhD,OAAO;EAHG,OAAOH,CAGT;EAFE,OAAOC,EAAI,QAAQ,KAAK,EAAE,CAAC,IAAI;EAC/B,OAAOC,EAAM,QAAQ,KAAK,EAAE,CAAC,IAAI;CAC5B;AAChB;AAEA,SAASE,EAAiB,GAAW,GAAW,GAAqC;CACpF,IAAMC,KAAK,OAAeC,IAAIC,IAAI,MAAM,IAClCC,IAAIC,IAAI,KAAK,IAAIC,GAAG,IAAIA,CAAC,GACzBC,KAAK,MAAcD,IAAIF,IAAI,KAAK,IAAI,IAAI,KAAK,IAAIH,EAAEC,CAAC,IAAI,GAAG,KAAK,IAAI,IAAID,EAAEC,CAAC,GAAG,CAAC,CAAC,CAAC;CACvF,OAAO;EAAC,MAAMK,EAAE,CAAC;EAAG,MAAMA,EAAE,CAAC;EAAG,MAAMA,EAAE,CAAC;CAAC;AAC3C;AAEA,SAASC,EAAkB,GAAuC;CACjE,OACC,QAASf,EAAQgB,EAAI,KAAK,GAAG,IAAI,QAAShB,EAAQgB,EAAI,KAAK,GAAG,IAAI,QAAShB,EAAQgB,EAAI,KAAK,GAAG;AAEjG;AAEA,SAASC,EAAa,GAAgC,GAAwC;CAC7F,IAAMC,IAAKH,EAAkBI,CAAI,GAC3BC,IAAKL,EAAkBM,CAAI;CACjC,QAAQ,KAAK,IAAIH,GAAIE,CAAE,IAAI,QAAS,KAAK,IAAIF,GAAIE,CAAE,IAAI;AACxD;AAEA,IAAME,IAA0C;CAAC;CAAI;CAAI;AAAE,GACrDC,IAA2C;CAAC;CAAK;CAAK;AAAG,GAEzDC,IAAiD;CACtD;EAAC;EAAK;EAAK;CAAG;CACd;EAAC;EAAK;EAAK;CAAG;CACd;EAAC;EAAK;EAAK;CAAG;CACd;EAAC;EAAK;EAAK;CAAG;AACf,GAEMC,IAAgD;CACrD;EAAC;EAAI;EAAI;CAAE;CACX;EAAC;EAAI;EAAI;CAAE;CACX;EAAC;EAAI;EAAI;CAAE;CACX;EAAC;EAAI;EAAI;CAAE;AACZ;AAEA,SAASC,EACR,GACA,GAC2B;CAC3B,OAAO;EACNC,EAAI,KAAKA,EAAI,KAAKC,EAAO,MAAM,IAAID,EAAI;EACvCA,EAAI,KAAKA,EAAI,KAAKC,EAAO,MAAM,IAAID,EAAI;EACvCA,EAAI,KAAKA,EAAI,KAAKC,EAAO,MAAM,IAAID,EAAI;CACxC;AACD;AAEA,SAASE,EAAsB,GAAsB,GAAuB;CAC3E,IAAM,CAACnB,GAAGE,GAAGkB,KAAS5B,EAAiB6B,IAAOhC,EAAO,OAAOA,EAAO,KAAK,GAClEiC,IAAiBD,IAAON,IAAoBD,GAC5CS,IAAaF,IAAOT,IAAgBC,GACpCW,IAAQH,IACX,KAAK,IAAI,KAAK,MAAMD,IAAQ,GAAG,GAAG,EAAE,IACpC,KAAK,IAAI,KAAK,MAAMA,IAAQ,GAAG,GAAG,EAAE;CAEvC,KAAK,IAAIjB,IAAIqB,GAAOH,IAAOlB,KAAK,KAAKA,KAAK,IAAI,KAAKkB,IAAO,IAAI,IAAI;EACjE,IAAMf,IAAMT,EAAiBG,GAAGE,GAAGC,IAAI,GAAG;EAK1C,IAJiB,KAAK,IAAI,GAAGmB,EAAe,KAAK,MAAOf,EAAaD,GAAKmB,CAAE,CAAC,CACzE,IAAW,QAGXlB,EAAaD,GAAKiB,CAAU,IAAI,MACnC;EAED,IAAIG,IAAU;EACd,KAAK,IAAMD,KAAMH,GAEhB,IAAIf,EADYS,EAAa,CAAC,GAAGV,GAAK,EAAG,GAAGmB,CAC3B,GAASF,CAAU,IAAI,MAAM;GAC7C,IAAU;GACV;EACD;EAED,IAAIG,GACH,OAAO,GAAG1B,EAAE,GAAG,KAAK,MAAME,IAAI,GAAG,EAAE,IAAIC,EAAE;CAE3C;CACA,OAAOkB,IAAOhC,EAAO,OAAOA,EAAO;AACpC;AAEA,SAAgBsC,EAAiB,GAAa;CAC7C,IAAM,CAAC3B,GAAGE,GAAGC,KAAKX,EAAiBI,CAAG,GAChCU,IAAMT,EAAiBG,GAAGE,GAAGC,CAAC,GAC9ByB,IAAgBrB,EAAaD,GAAKO,CAAc;CAEtD,OADqBN,EAAaD,GAAKM,CAChC,KAAgBgB,IAAiB,aAAwB;AACjE;AAEA,SAAgBC,EAAY,GAAY,IAAO,IAAO;CACrD,AAAI,OAAO,WAAa,OAGxBC,EAAY5C,EAAiBE,CAAE,GAAGiC,CAAI;AACvC;AAEA,SAASS,EAAY,GAAsB,GAAe;CACzD,IAAMC,IAAkBZ,EAAsB9B,GAAQgC,CAAI,GACpDW,IAAO,SAAS;CAItB,AAHAA,EAAK,MAAM,YAAY,oBAAoBD,CAAe,GAC1DC,EAAK,MAAM,YAAY,+BAA+BL,EAAiBI,CAAe,CAAC,GACvFC,EAAK,MAAM,YAAY,iBAAiBD,CAAe,GACvD,EAAK,QAAQ,SAAS1C,EAAO;AAC9B;AA6CA,SAAS4C,EAAgB,GAAiC;CACzD,OACC,OAAOC,KAAU,aAChBtD,EAAgB,MAAM,MAAMsB,EAAE,OAAOgC,CAAK,KAAKrD,EAAkB,IAAIqD,CAAK;AAE7E;AAEA,SAASC,EAAS,GAAiC;CAClD,IAAI,OAAOD,KAAU,YAAY,CAAC,oDAAoD,KAAKA,CAAK,GAC/F,OAAO;CACR,IAAM,CAAClC,GAAGE,GAAGC,KAAKX,EAAiB0C,CAAK;CACxC,OAAOlC,KAAK,OAAOE,KAAK,KAAKC,KAAK;AACnC;AAEA,SAASiC,IAAsB;CAI9B,OAHI,OAAO,WAAa,MAChB,KAED,SAAS,gBAAgB,UAAU,SAAS,MAAM;AAC1D;AAEA,SAAgBC,EAAe,EAC9B,aACA,qBACA,gBAAatD,GACb,mBAAgBD,GAChB,aAAU,IACV,QAAQwD,GACR,mBACA,qBAAkB,MACK;CACvB,IAAMC,IAAeD,MAAqB,KAAA,GACpCE,IAAWC,QAEf7D,EAAgB,KAAK,MAAW;EAC/B,IAAM8D,IAAWC,IAAmBtD,EAAO;EAC3C,OAAOqD,KAAYP,EAASO,EAAS,KAAK,KAAKP,EAASO,EAAS,IAAI,IAClE;GAAE,GAAGrD;GAAQ,OAAOqD,EAAS;GAAO,MAAMA,EAAS;EAAK,IACxDrD;CACJ,CAAC,GACF,CAACsD,CAAgB,CAClB,GAEM,CAACC,KAASC,QACfC,EAAsB;EACrB;EACA,cAAcC;EACd;EACA,SAASd;EACT,YAAY,MAAU/C,EAAiBgD,CAAK,CAAC,CAAC;EAC9C,WAAW;CACZ,CAAC,CACF;CAEA,QAAgB;EACf,EAAM,aAAa;GAAE;GAAY,cAAca;GAAe;EAAQ,CAAC;CACxE,GAAG;EAACH;EAAOI;EAAYD;EAAeE;CAAO,CAAC;CAE9C,IAAMC,IAAoBC,QAAkBjE,EAAiB6D,CAAa,CAAC,CAAC,IAAI,CAACA,CAAa,CAAC,GAEzFK,IAAcC,EAAqBT,EAAM,WAAWA,EAAM,aAAaM,CAAiB,GAExFI,IAAgBf,IAAerD,EAAiBoD,CAAgB,CAAC,CAAC,KAAKc;CAE7E,QAAgB;EACf,IAAI,CAACG,KAAmB,OAAO,WAAa,KAC3C;EAED,IAAMC,UAAc;GACnB,IAAMnC,IAAOe,EAAW;GACxB,KAAK,IAAM/C,KAAUmD,GACpB,SAAS,gBAAgB,MAAM,YAAYnD,EAAO,OAAOgC,IAAOhC,EAAO,OAAOA,EAAO,KAAK;GAC3F,EAAYmD,EAAS,MAAM,MAAWnD,EAAO,OAAOiE,CAAa,KAAKd,EAAS,IAAInB,CAAI;EACxF;EACA,EAAM;EACN,IAAMoC,IAAW,IAAI,iBAAiBD,CAAK;EAE3C,OADAC,EAAS,QAAQ,SAAS,iBAAiB;GAAE,YAAY;GAAM,iBAAiB,CAAC,OAAO;EAAE,CAAC,SAC9EA,EAAS,WAAW;CAClC,GAAG;EAACH;EAAeC;EAAiBf;CAAQ,CAAC;CAE7C,IAAMkB,IAAYP,GAChB,MAAiB;EACjB,IAAMQ,IAAUzE,EAAiB0E,CAAI,CAAC,CAAC;EACvC,IAAIrB,GAAc;GACjB,IAAiBoB,CAAO;GACxB;EACD;EAEA,AADAf,EAAM,SAASe,CAAO,GACtBE,IAAiBF,CAAO;CACzB,GACA;EAACpB;EAAcsB;EAAgBjB;CAAK,CACrC,GAEMV,IAAQO,SACN;EAAE,QAAQa;EAAe;EAAW;CAAS,IACpD;EAACA;EAAeI;EAAWlB;CAAQ,CACpC;CACA,OAAO,kBAACxD,EAAc,UAAf;EAA+B;EAAQ;CAAiC,CAAA;AAChF;AAEA,SAAgB8E,IAAY;CAC3B,IAAMC,IAAMC,EAAWhF,CAAa;CACpC,IAAI,CAAC+E,GACJ,MAAU,MAAM,8CAA8C;CAE/D,OAAOA;AACR"}
@@ -1 +1 @@
1
- {"version":3,"file":"link.js","names":[],"sources":["../../src/providers/link.tsx"],"sourcesContent":["import { type ComponentType, createContext, type ReactNode, useContext } from \"react\";\n\nexport type LinkComponent = ComponentType<{\n\thref: string;\n\tclassName?: string;\n\tchildren?: ReactNode;\n}>;\n\nexport interface LinkProviderProps {\n\t/**\n\t * Custom link renderer component injected into Basalt Link.\n\t * When omitted (undefined), Basalt Link internally falls back to a standard HTML \"a\" element.\n\t * Note: this prop only accepts a custom React component (ComponentType<{ href: string; className?: string; children?: ReactNode }>); string literals like \"a\" are not accepted.\n\t * useLinkComponent() returns the custom LinkComponent or \"a\" when no custom renderer is configured, and safely returns \"a\" even without LinkProvider without throwing.\n\t * Component renders a React context provider without native HTML rest attribute forwarding or forwarded ref.\n\t */\n\trender?: LinkComponent;\n\t/**\n\t * Application components wrapped by the link routing context.\n\t */\n\tchildren: ReactNode;\n}\n\nconst LinkContext = createContext<LinkComponent | \"a\">(\"a\");\n\nexport function LinkProvider({ render, children }: LinkProviderProps) {\n\treturn <LinkContext.Provider value={render ?? \"a\"}>{children}</LinkContext.Provider>;\n}\n\nexport function useLinkComponent() {\n\treturn useContext(LinkContext);\n}\n"],"mappings":";;;;AAuBA,IAAM,IAAc,EAAmC,GAAG;AAE1D,SAAgB,EAAa,EAAE,WAAQ,eAA+B;CACrE,OAAO,kBAAC,EAAY,UAAb;EAAsB,OAAO,KAAU;EAAM;CAA+B,CAAA;AACpF;AAEA,SAAgB,IAAmB;CAClC,OAAO,EAAW,CAAW;AAC9B"}
1
+ {"version":3,"file":"link.js","names":["LinkContext","createContext","LinkProvider","render","useLinkComponent","useContext"],"sources":["../../src/providers/link.tsx"],"sourcesContent":["import { type ComponentType, createContext, type ReactNode, useContext } from \"react\";\n\nexport type LinkComponent = ComponentType<{\n\thref: string;\n\tclassName?: string;\n\tchildren?: ReactNode;\n}>;\n\nexport interface LinkProviderProps {\n\t/**\n\t * Custom link renderer component injected into Basalt Link.\n\t * When omitted (undefined), Basalt Link internally falls back to a standard HTML \"a\" element.\n\t * Note: this prop only accepts a custom React component (ComponentType<{ href: string; className?: string; children?: ReactNode }>); string literals like \"a\" are not accepted.\n\t * useLinkComponent() returns the custom LinkComponent or \"a\" when no custom renderer is configured, and safely returns \"a\" even without LinkProvider without throwing.\n\t * Component renders a React context provider without native HTML rest attribute forwarding or forwarded ref.\n\t */\n\trender?: LinkComponent;\n\t/**\n\t * Application components wrapped by the link routing context.\n\t */\n\tchildren: ReactNode;\n}\n\nconst LinkContext = createContext<LinkComponent | \"a\">(\"a\");\n\nexport function LinkProvider({ render, children }: LinkProviderProps) {\n\treturn <LinkContext.Provider value={render ?? \"a\"}>{children}</LinkContext.Provider>;\n}\n\nexport function useLinkComponent() {\n\treturn useContext(LinkContext);\n}\n"],"mappings":";;;;AAuBA,IAAMA,IAAcC,EAAmC,GAAG;AAE1D,SAAgBC,EAAa,EAAE,WAAQ,eAA+B;CACrE,OAAO,kBAACF,EAAY,UAAb;EAAsB,OAAOG,KAAU;EAAM;CAA+B,CAAA;AACpF;AAEA,SAAgBC,IAAmB;CAClC,OAAOC,EAAWL,CAAW;AAC9B"}
@@ -41,7 +41,7 @@ function p({ children: t, storageKey: r = l, defaultTheme: p = "system", persist
41
41
  ]);
42
42
  let b = n(() => p, [p]), x = s(y.subscribe, y.getSnapshot, b), S = v ? h : x;
43
43
  i(() => {
44
- if (!(!_ || typeof window > "u") && (d(S), S === "system")) try {
44
+ if (_ && typeof window < "u" && (d(S), S === "system")) try {
45
45
  let e = window.matchMedia("(prefers-color-scheme: dark)"), t = () => d("system");
46
46
  return e.addEventListener("change", t), () => e.removeEventListener("change", t);
47
47
  } catch {
@@ -1 +1 @@
1
- {"version":3,"file":"theme.js","names":[],"sources":["../../src/providers/theme.tsx"],"sourcesContent":["import {\n\tcreateContext,\n\ttype ReactNode,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseState,\n\tuseSyncExternalStore,\n} from \"react\";\nimport { createPreferenceStore } from \"../utils/preference-store\";\n\nexport type BasaltTheme = \"light\" | \"dark\" | \"system\";\n\nconst STORAGE_KEY = \"theme\";\n\ntype ThemeContextValue = {\n\ttheme: BasaltTheme;\n\tsetTheme: (theme: BasaltTheme) => void;\n};\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nfunction applyTheme(theme: BasaltTheme) {\n\tif (typeof document === \"undefined\") {\n\t\treturn;\n\t}\n\tconst root = document.documentElement;\n\tlet systemDark = false;\n\ttry {\n\t\tif (typeof window !== \"undefined\" && typeof window.matchMedia === \"function\") {\n\t\t\tsystemDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\t\t}\n\t} catch {\n\t\tsystemDark = false;\n\t}\n\tconst dark = theme === \"dark\" || (theme === \"system\" && systemDark);\n\troot.classList.toggle(\"dark\", dark);\n\troot.classList.toggle(\"light\", !dark);\n\troot.dataset.mode = dark ? \"dark\" : \"light\";\n}\n\nexport interface ThemeProviderProps {\n\t/**\n\t * Application components wrapped by the theme context.\n\t */\n\tchildren: ReactNode;\n\t/**\n\t * Storage key used for theme persistence.\n\t * @default \"theme\"\n\t */\n\tstorageKey?: string;\n\t/**\n\t * Initial theme used when no stored preference exists or during SSR.\n\t * @default \"system\"\n\t */\n\tdefaultTheme?: BasaltTheme;\n\t/**\n\t * Whether to persist theme changes to localStorage.\n\t * If false, localStorage is never read or written.\n\t * @default true\n\t */\n\tpersist?: boolean;\n\t/**\n\t * Controlled theme value. When provided, the provider acts as a controlled component\n\t * and internal state is driven by this prop.\n\t */\n\ttheme?: BasaltTheme;\n\t/**\n\t * Callback fired when theme change is requested.\n\t * In controlled mode, callers are responsible for updating `theme`.\n\t */\n\tonThemeChange?: (theme: BasaltTheme) => void;\n\t/**\n\t * Whether to apply mode classes and data-mode attribute to document.documentElement.\n\t * Set to false when a host theme system manages the document root.\n\t * @default true\n\t */\n\tapplyToDocument?: boolean;\n}\n\nfunction isValidTheme(value: unknown): value is BasaltTheme {\n\treturn value === \"light\" || value === \"dark\" || value === \"system\";\n}\n\nexport function ThemeProvider({\n\tchildren,\n\tstorageKey = STORAGE_KEY,\n\tdefaultTheme = \"system\",\n\tpersist = true,\n\ttheme: controlledTheme,\n\tonThemeChange,\n\tapplyToDocument = true,\n}: ThemeProviderProps) {\n\tconst isControlled = controlledTheme !== undefined;\n\n\tconst [store] = useState(() =>\n\t\tcreatePreferenceStore({\n\t\t\tstorageKey,\n\t\t\tdefaultValue: defaultTheme,\n\t\t\tpersist,\n\t\t\tisValid: isValidTheme,\n\t\t\tnormalize: (value) => value,\n\t\t\teventName: \"basalt:theme-change\",\n\t\t}),\n\t);\n\n\tuseEffect(() => {\n\t\tstore.updateConfig({ storageKey, defaultValue: defaultTheme, persist });\n\t}, [store, storageKey, defaultTheme, persist]);\n\n\tconst getServerSnapshot = useCallback(() => defaultTheme, [defaultTheme]);\n\n\tconst storeTheme = useSyncExternalStore(store.subscribe, store.getSnapshot, getServerSnapshot);\n\n\tconst currentTheme = isControlled ? controlledTheme : storeTheme;\n\n\tuseEffect(() => {\n\t\tif (!applyToDocument || typeof window === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\t\tapplyTheme(currentTheme);\n\t\tif (currentTheme !== \"system\") {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n\t\t\tconst onChange = () => applyTheme(\"system\");\n\t\t\tmq.addEventListener(\"change\", onChange);\n\t\t\treturn () => mq.removeEventListener(\"change\", onChange);\n\t\t} catch {\n\t\t\t// matchMedia not supported or restricted\n\t\t\treturn;\n\t\t}\n\t}, [currentTheme, applyToDocument]);\n\n\tconst setTheme = useCallback(\n\t\t(next: BasaltTheme) => {\n\t\t\tif (isControlled) {\n\t\t\t\tonThemeChange?.(next);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstore.setValue(next);\n\t\t\tonThemeChange?.(next);\n\t\t},\n\t\t[isControlled, onThemeChange, store],\n\t);\n\n\tconst value = useMemo(() => ({ theme: currentTheme, setTheme }), [currentTheme, setTheme]);\n\n\treturn <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\nexport function useTheme() {\n\tconst ctx = useContext(ThemeContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useTheme must be used within ThemeProvider\");\n\t}\n\treturn ctx;\n}\n"],"mappings":";;;;;AAcA,IAAM,IAAc,SAOd,IAAe,EAAwC,IAAI;AAEjE,SAAS,EAAW,GAAoB;CACvC,IAAI,OAAO,WAAa,KACvB;CAED,IAAM,IAAO,SAAS,iBAClB,IAAa;CACjB,IAAI;EACH,AAAI,OAAO,SAAW,OAAe,OAAO,OAAO,cAAe,eACjE,IAAa,OAAO,WAAW,8BAA8B,CAAC,CAAC;CAEjE,QAAQ;EACP,IAAa;CACd;CACA,IAAM,IAAO,MAAU,UAAW,MAAU,YAAY;CAGxD,AAFA,EAAK,UAAU,OAAO,QAAQ,CAAI,GAClC,EAAK,UAAU,OAAO,SAAS,CAAC,CAAI,GACpC,EAAK,QAAQ,OAAO,IAAO,SAAS;AACrC;AAyCA,SAAS,EAAa,GAAsC;CAC3D,OAAO,MAAU,WAAW,MAAU,UAAU,MAAU;AAC3D;AAEA,SAAgB,EAAc,EAC7B,aACA,gBAAa,GACb,kBAAe,UACf,aAAU,IACV,OAAO,GACP,kBACA,qBAAkB,MACI;CACtB,IAAM,IAAe,MAAoB,KAAA,GAEnC,CAAC,KAAS,QACf,EAAsB;EACrB;EACA,cAAc;EACd;EACA,SAAS;EACT,YAAY,MAAU;EACtB,WAAW;CACZ,CAAC,CACF;CAEA,QAAgB;EACf,EAAM,aAAa;GAAE;GAAY,cAAc;GAAc;EAAQ,CAAC;CACvE,GAAG;EAAC;EAAO;EAAY;EAAc;CAAO,CAAC;CAE7C,IAAM,IAAoB,QAAkB,GAAc,CAAC,CAAY,CAAC,GAElE,IAAa,EAAqB,EAAM,WAAW,EAAM,aAAa,CAAiB,GAEvF,IAAe,IAAe,IAAkB;CAEtD,QAAgB;EACX,OAAC,KAAmB,OAAO,SAAW,SAG1C,EAAW,CAAY,GACnB,MAAiB,WAGrB,IAAI;GACH,IAAM,IAAK,OAAO,WAAW,8BAA8B,GACrD,UAAiB,EAAW,QAAQ;GAE1C,OADA,EAAG,iBAAiB,UAAU,CAAQ,SACzB,EAAG,oBAAoB,UAAU,CAAQ;EACvD,QAAQ;GAEP;EACD;CACD,GAAG,CAAC,GAAc,CAAe,CAAC;CAElC,IAAM,IAAW,GACf,MAAsB;EACtB,IAAI,GAAc;GACjB,IAAgB,CAAI;GACpB;EACD;EAEA,AADA,EAAM,SAAS,CAAI,GACnB,IAAgB,CAAI;CACrB,GACA;EAAC;EAAc;EAAe;CAAK,CACpC,GAEM,IAAQ,SAAe;EAAE,OAAO;EAAc;CAAS,IAAI,CAAC,GAAc,CAAQ,CAAC;CAEzF,OAAO,kBAAC,EAAa,UAAd;EAA8B;EAAQ;CAAgC,CAAA;AAC9E;AAEA,SAAgB,IAAW;CAC1B,IAAM,IAAM,EAAW,CAAY;CACnC,IAAI,CAAC,GACJ,MAAU,MAAM,4CAA4C;CAE7D,OAAO;AACR"}
1
+ {"version":3,"file":"theme.js","names":["STORAGE_KEY","ThemeContext","createContext","applyTheme","root","systemDark","dark","theme","isValidTheme","value","ThemeProvider","controlledTheme","isControlled","store","useState","createPreferenceStore","defaultTheme","storageKey","persist","getServerSnapshot","useCallback","storeTheme","useSyncExternalStore","currentTheme","applyToDocument","mq","onChange","setTheme","next","onThemeChange","useMemo","useTheme","ctx","useContext"],"sources":["../../src/providers/theme.tsx"],"sourcesContent":["import {\n\tcreateContext,\n\ttype ReactNode,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseState,\n\tuseSyncExternalStore,\n} from \"react\";\nimport { createPreferenceStore } from \"../utils/preference-store\";\n\nexport type BasaltTheme = \"light\" | \"dark\" | \"system\";\n\nconst STORAGE_KEY = \"theme\";\n\ntype ThemeContextValue = {\n\ttheme: BasaltTheme;\n\tsetTheme: (theme: BasaltTheme) => void;\n};\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nfunction applyTheme(theme: BasaltTheme) {\n\tif (typeof document === \"undefined\") {\n\t\treturn;\n\t}\n\tconst root = document.documentElement;\n\tlet systemDark = false;\n\ttry {\n\t\tif (typeof window !== \"undefined\" && typeof window.matchMedia === \"function\") {\n\t\t\tsystemDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\t\t}\n\t} catch {\n\t\tsystemDark = false;\n\t}\n\tconst dark = theme === \"dark\" || (theme === \"system\" && systemDark);\n\troot.classList.toggle(\"dark\", dark);\n\troot.classList.toggle(\"light\", !dark);\n\troot.dataset.mode = dark ? \"dark\" : \"light\";\n}\n\nexport interface ThemeProviderProps {\n\t/**\n\t * Application components wrapped by the theme context.\n\t */\n\tchildren: ReactNode;\n\t/**\n\t * Storage key used for theme persistence.\n\t * @default \"theme\"\n\t */\n\tstorageKey?: string;\n\t/**\n\t * Initial theme used when no stored preference exists or during SSR.\n\t * @default \"system\"\n\t */\n\tdefaultTheme?: BasaltTheme;\n\t/**\n\t * Whether to persist theme changes to localStorage.\n\t * If false, localStorage is never read or written.\n\t * @default true\n\t */\n\tpersist?: boolean;\n\t/**\n\t * Controlled theme value. When provided, the provider acts as a controlled component\n\t * and internal state is driven by this prop.\n\t */\n\ttheme?: BasaltTheme;\n\t/**\n\t * Callback fired when theme change is requested.\n\t * In controlled mode, callers are responsible for updating `theme`.\n\t */\n\tonThemeChange?: (theme: BasaltTheme) => void;\n\t/**\n\t * Whether to apply mode classes and data-mode attribute to document.documentElement.\n\t * Set to false when a host theme system manages the document root.\n\t * @default true\n\t */\n\tapplyToDocument?: boolean;\n}\n\nfunction isValidTheme(value: unknown): value is BasaltTheme {\n\treturn value === \"light\" || value === \"dark\" || value === \"system\";\n}\n\nexport function ThemeProvider({\n\tchildren,\n\tstorageKey = STORAGE_KEY,\n\tdefaultTheme = \"system\",\n\tpersist = true,\n\ttheme: controlledTheme,\n\tonThemeChange,\n\tapplyToDocument = true,\n}: ThemeProviderProps) {\n\tconst isControlled = controlledTheme !== undefined;\n\n\tconst [store] = useState(() =>\n\t\tcreatePreferenceStore({\n\t\t\tstorageKey,\n\t\t\tdefaultValue: defaultTheme,\n\t\t\tpersist,\n\t\t\tisValid: isValidTheme,\n\t\t\tnormalize: (value) => value,\n\t\t\teventName: \"basalt:theme-change\",\n\t\t}),\n\t);\n\n\tuseEffect(() => {\n\t\tstore.updateConfig({ storageKey, defaultValue: defaultTheme, persist });\n\t}, [store, storageKey, defaultTheme, persist]);\n\n\tconst getServerSnapshot = useCallback(() => defaultTheme, [defaultTheme]);\n\n\tconst storeTheme = useSyncExternalStore(store.subscribe, store.getSnapshot, getServerSnapshot);\n\n\tconst currentTheme = isControlled ? controlledTheme : storeTheme;\n\n\tuseEffect(() => {\n\t\tif (!applyToDocument || typeof window === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\t\tapplyTheme(currentTheme);\n\t\tif (currentTheme !== \"system\") {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n\t\t\tconst onChange = () => applyTheme(\"system\");\n\t\t\tmq.addEventListener(\"change\", onChange);\n\t\t\treturn () => mq.removeEventListener(\"change\", onChange);\n\t\t} catch {\n\t\t\t// matchMedia not supported or restricted\n\t\t\treturn;\n\t\t}\n\t}, [currentTheme, applyToDocument]);\n\n\tconst setTheme = useCallback(\n\t\t(next: BasaltTheme) => {\n\t\t\tif (isControlled) {\n\t\t\t\tonThemeChange?.(next);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstore.setValue(next);\n\t\t\tonThemeChange?.(next);\n\t\t},\n\t\t[isControlled, onThemeChange, store],\n\t);\n\n\tconst value = useMemo(() => ({ theme: currentTheme, setTheme }), [currentTheme, setTheme]);\n\n\treturn <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\nexport function useTheme() {\n\tconst ctx = useContext(ThemeContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useTheme must be used within ThemeProvider\");\n\t}\n\treturn ctx;\n}\n"],"mappings":";;;;;AAcA,IAAMA,IAAc,SAOdC,IAAeC,EAAwC,IAAI;AAEjE,SAASC,EAAW,GAAoB;CACvC,IAAI,OAAO,WAAa,KACvB;CAED,IAAMC,IAAO,SAAS,iBAClBC,IAAa;CACjB,IAAI;EACH,AAAI,OAAO,SAAW,OAAe,OAAO,OAAO,cAAe,eACjE,IAAa,OAAO,WAAW,8BAA8B,CAAC,CAAC;CAEjE,QAAQ;EACP,IAAa;CACd;CACA,IAAMC,IAAOC,MAAU,UAAWA,MAAU,YAAYF;CAGxD,AAFAD,EAAK,UAAU,OAAO,QAAQE,CAAI,GAClCF,EAAK,UAAU,OAAO,SAAS,CAACE,CAAI,GACpC,EAAK,QAAQ,OAAOA,IAAO,SAAS;AACrC;AAyCA,SAASE,EAAa,GAAsC;CAC3D,OAAOC,MAAU,WAAWA,MAAU,UAAUA,MAAU;AAC3D;AAEA,SAAgBC,EAAc,EAC7B,aACA,gBAAaV,GACb,kBAAe,UACf,aAAU,IACV,OAAOW,GACP,kBACA,qBAAkB,MACI;CACtB,IAAMC,IAAeD,MAAoB,KAAA,GAEnC,CAACE,KAASC,QACfC,EAAsB;EACrB;EACA,cAAcC;EACd;EACA,SAASR;EACT,YAAY,MAAUC;EACtB,WAAW;CACZ,CAAC,CACF;CAEA,QAAgB;EACf,EAAM,aAAa;GAAE;GAAY,cAAcO;GAAc;EAAQ,CAAC;CACvE,GAAG;EAACH;EAAOI;EAAYD;EAAcE;CAAO,CAAC;CAE7C,IAAMC,IAAoBC,QAAkBJ,GAAc,CAACA,CAAY,CAAC,GAElEK,IAAaC,EAAqBT,EAAM,WAAWA,EAAM,aAAaM,CAAiB,GAEvFI,IAAeX,IAAeD,IAAkBU;CAEtD,QAAgB;EACf,IAAKG,KAAmB,OAAO,SAAW,QAG1CrB,EAAWoB,CAAY,GACnBA,MAAiB,WAGrB,IAAI;GACH,IAAME,IAAK,OAAO,WAAW,8BAA8B,GACrDC,UAAiBvB,EAAW,QAAQ;GAE1C,OADAsB,EAAG,iBAAiB,UAAUC,CAAQ,SACzBD,EAAG,oBAAoB,UAAUC,CAAQ;EACvD,QAAQ;GAEP;EACD;CACD,GAAG,CAACH,GAAcC,CAAe,CAAC;CAElC,IAAMG,IAAWP,GACf,MAAsB;EACtB,IAAIR,GAAc;GACjB,IAAgBgB,CAAI;GACpB;EACD;EAEA,AADAf,EAAM,SAASe,CAAI,GACnBC,IAAgBD,CAAI;CACrB,GACA;EAAChB;EAAciB;EAAehB;CAAK,CACpC,GAEMJ,IAAQqB,SAAe;EAAE,OAAOP;EAAc;CAAS,IAAI,CAACA,GAAcI,CAAQ,CAAC;CAEzF,OAAO,kBAAC1B,EAAa,UAAd;EAA8B;EAAQ;CAAgC,CAAA;AAC9E;AAEA,SAAgB8B,IAAW;CAC1B,IAAMC,IAAMC,EAAWhC,CAAY;CACnC,IAAI,CAAC+B,GACJ,MAAU,MAAM,4CAA4C;CAE7D,OAAOA;AACR"}
@@ -81,3 +81,119 @@
81
81
  border-collapse: collapse;
82
82
  }
83
83
  }
84
+
85
+ @layer base {
86
+ [data-basalt-shell] {
87
+ --basalt-safe-top: env(safe-area-inset-top, 0px);
88
+ --basalt-sticky-top: 0px;
89
+ --basalt-main-height: 100%;
90
+ --basalt-main-overflow: hidden;
91
+ --basalt-island-overflow: auto;
92
+ --basalt-island-overscroll: contain;
93
+ --basalt-sidebar-height: 100%;
94
+ box-sizing: border-box;
95
+ height: 100dvh;
96
+ max-height: 100dvh;
97
+ overflow: hidden;
98
+ overscroll-behavior: none;
99
+ padding: var(--basalt-safe-top) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px)
100
+ env(safe-area-inset-left, 0px);
101
+ }
102
+
103
+ [data-basalt-shell="document"] {
104
+ --basalt-sticky-top: var(--basalt-safe-top);
105
+ --basalt-main-height: auto;
106
+ --basalt-main-overflow: visible;
107
+ --basalt-island-overflow: visible;
108
+ --basalt-island-overscroll: auto;
109
+ --basalt-sidebar-height: calc(
110
+ 100dvh -
111
+ var(--basalt-safe-top) -
112
+ env(safe-area-inset-bottom, 0px)
113
+ );
114
+ height: auto;
115
+ min-height: 100dvh;
116
+ max-height: none;
117
+ overflow: visible;
118
+ overscroll-behavior: auto;
119
+ }
120
+
121
+ @media (width < 768px) {
122
+ [data-basalt-shell="responsive"] {
123
+ --basalt-sticky-top: var(--basalt-safe-top);
124
+ --basalt-main-height: auto;
125
+ --basalt-main-overflow: visible;
126
+ --basalt-island-overflow: visible;
127
+ --basalt-island-overscroll: auto;
128
+ --basalt-sidebar-height: calc(
129
+ 100dvh -
130
+ var(--basalt-safe-top) -
131
+ env(safe-area-inset-bottom, 0px)
132
+ );
133
+ height: auto;
134
+ min-height: 100dvh;
135
+ max-height: none;
136
+ overflow: visible;
137
+ overscroll-behavior: auto;
138
+ }
139
+ }
140
+
141
+ [data-basalt-main] {
142
+ height: var(--basalt-main-height, 100%);
143
+ overflow: var(--basalt-main-overflow, hidden);
144
+ }
145
+
146
+ [data-basalt-island] {
147
+ overflow-y: var(--basalt-island-overflow, auto);
148
+ overscroll-behavior-y: var(--basalt-island-overscroll, contain);
149
+ }
150
+
151
+ [data-basalt-sidebar] {
152
+ height: var(--basalt-sidebar-height, 100dvh);
153
+ max-height: 100dvh;
154
+ }
155
+
156
+ [data-basalt-sidebar]:not([data-overlay]) {
157
+ top: var(--basalt-sticky-top, 0px);
158
+ }
159
+ }
160
+
161
+ @layer base {
162
+ [data-basalt-sheet] {
163
+ --basalt-sidebar-height: 100%;
164
+ max-height: 100dvh;
165
+ }
166
+
167
+ [data-basalt-sheet] [data-basalt-sidebar],
168
+ [data-basalt-sidebar][data-overlay] {
169
+ box-sizing: border-box;
170
+ padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px)
171
+ env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
172
+ }
173
+ }
174
+
175
+ @layer base {
176
+ [data-basalt-header][data-sticky] {
177
+ position: sticky;
178
+ top: var(--basalt-sticky-top, 0px);
179
+ z-index: 20;
180
+ background-color: hsl(var(--basalt-card));
181
+ }
182
+
183
+ [data-basalt-header][data-sticky]::before {
184
+ content: "";
185
+ position: absolute;
186
+ inset-inline: 0;
187
+ bottom: 100%;
188
+ height: var(--basalt-sticky-top, 0px);
189
+ background: inherit;
190
+ pointer-events: none;
191
+ }
192
+
193
+ [data-basalt-header][data-density="compact"]
194
+ :where([data-basalt-header-leading], [data-basalt-header-actions])
195
+ :where(button, a, [role="button"]) {
196
+ min-height: 44px;
197
+ min-width: 44px;
198
+ }
199
+ }