tempest-react-sdk 0.74.0 → 0.76.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 (147) hide show
  1. package/README.md +3 -1
  2. package/dist/br/BrazilMap.module.cjs.map +1 -1
  3. package/dist/br/BrazilMap.module.js.map +1 -1
  4. package/dist/br/MunicipalitySearch.cjs +1 -1
  5. package/dist/br/MunicipalitySearch.cjs.map +1 -1
  6. package/dist/br/MunicipalitySearch.js +66 -48
  7. package/dist/br/MunicipalitySearch.js.map +1 -1
  8. package/dist/br/MunicipalitySearch.module.cjs +1 -1
  9. package/dist/br/MunicipalitySearch.module.cjs.map +1 -1
  10. package/dist/br/MunicipalitySearch.module.js +4 -3
  11. package/dist/br/MunicipalitySearch.module.js.map +1 -1
  12. package/dist/br.d.ts +12 -0
  13. package/dist/components/AIChat/AIChat.module.cjs.map +1 -1
  14. package/dist/components/AIChat/AIChat.module.js.map +1 -1
  15. package/dist/components/AppBar/AppBar.cjs +1 -1
  16. package/dist/components/AppBar/AppBar.cjs.map +1 -1
  17. package/dist/components/AppBar/AppBar.js +1 -0
  18. package/dist/components/AppBar/AppBar.js.map +1 -1
  19. package/dist/components/AppBar/AppBar.module.cjs +1 -1
  20. package/dist/components/AppBar/AppBar.module.cjs.map +1 -1
  21. package/dist/components/AppBar/AppBar.module.js +12 -11
  22. package/dist/components/AppBar/AppBar.module.js.map +1 -1
  23. package/dist/components/Button/Button.module.cjs.map +1 -1
  24. package/dist/components/Button/Button.module.js.map +1 -1
  25. package/dist/components/Button/ButtonContent.cjs.map +1 -1
  26. package/dist/components/Button/ButtonContent.js.map +1 -1
  27. package/dist/components/Carousel/Carousel.module.cjs.map +1 -1
  28. package/dist/components/Carousel/Carousel.module.js.map +1 -1
  29. package/dist/components/Chat/Chat.module.cjs.map +1 -1
  30. package/dist/components/Chat/Chat.module.js.map +1 -1
  31. package/dist/components/Combobox/Combobox.cjs +1 -1
  32. package/dist/components/Combobox/Combobox.cjs.map +1 -1
  33. package/dist/components/Combobox/Combobox.js +18 -23
  34. package/dist/components/Combobox/Combobox.js.map +1 -1
  35. package/dist/components/Combobox/use-listbox-dismiss.cjs +2 -0
  36. package/dist/components/Combobox/use-listbox-dismiss.cjs.map +1 -0
  37. package/dist/components/Combobox/use-listbox-dismiss.js +34 -0
  38. package/dist/components/Combobox/use-listbox-dismiss.js.map +1 -0
  39. package/dist/components/Dropzone/Dropzone.module.cjs.map +1 -1
  40. package/dist/components/Dropzone/Dropzone.module.js.map +1 -1
  41. package/dist/components/FilterBar/FilterBar.module.cjs.map +1 -1
  42. package/dist/components/FilterBar/FilterBar.module.js.map +1 -1
  43. package/dist/components/FloatingActionButton/FloatingActionButton.module.cjs.map +1 -1
  44. package/dist/components/FloatingActionButton/FloatingActionButton.module.js.map +1 -1
  45. package/dist/components/ImageCropper/ImageCropper.module.cjs.map +1 -1
  46. package/dist/components/ImageCropper/ImageCropper.module.js.map +1 -1
  47. package/dist/components/ListTile/ListTile.module.cjs.map +1 -1
  48. package/dist/components/ListTile/ListTile.module.js.map +1 -1
  49. package/dist/components/Markdown/Markdown.module.cjs.map +1 -1
  50. package/dist/components/Markdown/Markdown.module.js.map +1 -1
  51. package/dist/components/Menubar/Menubar.module.cjs.map +1 -1
  52. package/dist/components/Menubar/Menubar.module.js.map +1 -1
  53. package/dist/components/MultiSelect/MultiSelect.cjs +1 -1
  54. package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
  55. package/dist/components/MultiSelect/MultiSelect.js +55 -54
  56. package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
  57. package/dist/components/Navbar/Navbar.cjs +1 -1
  58. package/dist/components/Navbar/Navbar.cjs.map +1 -1
  59. package/dist/components/Navbar/Navbar.js +1 -0
  60. package/dist/components/Navbar/Navbar.js.map +1 -1
  61. package/dist/components/Navbar/Navbar.module.cjs +1 -1
  62. package/dist/components/Navbar/Navbar.module.cjs.map +1 -1
  63. package/dist/components/Navbar/Navbar.module.js +9 -8
  64. package/dist/components/Navbar/Navbar.module.js.map +1 -1
  65. package/dist/components/NavigationMenu/NavigationMenu.module.cjs.map +1 -1
  66. package/dist/components/NavigationMenu/NavigationMenu.module.js.map +1 -1
  67. package/dist/components/NavigationRail/NavigationRail.module.cjs.map +1 -1
  68. package/dist/components/NavigationRail/NavigationRail.module.js.map +1 -1
  69. package/dist/components/NotificationCenter/NotificationCenter.module.cjs.map +1 -1
  70. package/dist/components/NotificationCenter/NotificationCenter.module.js.map +1 -1
  71. package/dist/components/PasswordInput/PasswordInput.module.cjs.map +1 -1
  72. package/dist/components/PasswordInput/PasswordInput.module.js.map +1 -1
  73. package/dist/components/PinInput/PinInput.module.cjs.map +1 -1
  74. package/dist/components/PinInput/PinInput.module.js.map +1 -1
  75. package/dist/components/SectionHeader/SectionHeader.cjs +1 -1
  76. package/dist/components/SectionHeader/SectionHeader.cjs.map +1 -1
  77. package/dist/components/SectionHeader/SectionHeader.js +33 -25
  78. package/dist/components/SectionHeader/SectionHeader.js.map +1 -1
  79. package/dist/components/Tour/Tour.module.cjs.map +1 -1
  80. package/dist/components/Tour/Tour.module.js.map +1 -1
  81. package/dist/components/Transfer/Transfer.module.cjs.map +1 -1
  82. package/dist/components/Transfer/Transfer.module.js.map +1 -1
  83. package/dist/components/VirtualTable/VirtualTable.module.cjs.map +1 -1
  84. package/dist/components/VirtualTable/VirtualTable.module.js.map +1 -1
  85. package/dist/geo/TrajectoryMap.module.cjs.map +1 -1
  86. package/dist/geo/TrajectoryMap.module.js.map +1 -1
  87. package/dist/icons/IconPicker.module.cjs.map +1 -1
  88. package/dist/icons/IconPicker.module.js.map +1 -1
  89. package/dist/neural.cjs +2 -0
  90. package/dist/neural.cjs.map +1 -0
  91. package/dist/neural.d.ts +110 -0
  92. package/dist/neural.js +48 -0
  93. package/dist/neural.js.map +1 -0
  94. package/dist/styles/actions.css +2 -2
  95. package/dist/styles/br.css +3 -3
  96. package/dist/styles/chat.css +5 -5
  97. package/dist/styles/component/AIChat.css +3 -3
  98. package/dist/styles/component/AppBar.css +1 -0
  99. package/dist/styles/component/BrazilMap.css +2 -2
  100. package/dist/styles/component/Button.css +1 -1
  101. package/dist/styles/component/Carousel.css +3 -3
  102. package/dist/styles/component/Chat.css +2 -2
  103. package/dist/styles/component/Dropzone.css +2 -1
  104. package/dist/styles/component/FilterBar.css +2 -2
  105. package/dist/styles/component/FloatingActionButton.css +1 -1
  106. package/dist/styles/component/IconPicker.css +1 -1
  107. package/dist/styles/component/ImageCropper.css +2 -2
  108. package/dist/styles/component/ListTile.css +1 -1
  109. package/dist/styles/component/Markdown.css +1 -1
  110. package/dist/styles/component/Menubar.css +1 -1
  111. package/dist/styles/component/MunicipalitySearch.css +1 -1
  112. package/dist/styles/component/Navbar.css +1 -0
  113. package/dist/styles/component/NavigationMenu.css +1 -1
  114. package/dist/styles/component/NavigationRail.css +1 -1
  115. package/dist/styles/component/NotificationCenter.css +3 -3
  116. package/dist/styles/component/PasswordInput.css +1 -1
  117. package/dist/styles/component/PinInput.css +1 -1
  118. package/dist/styles/component/Tour.css +2 -2
  119. package/dist/styles/component/TrajectoryMap.css +1 -1
  120. package/dist/styles/component/Transfer.css +2 -2
  121. package/dist/styles/component/VirtualTable.css +2 -2
  122. package/dist/styles/data.css +10 -10
  123. package/dist/styles/feedback.css +3 -3
  124. package/dist/styles/forms.css +6 -5
  125. package/dist/styles/geo.css +1 -1
  126. package/dist/styles/icons.css +1 -1
  127. package/dist/styles/inverse.css +225 -0
  128. package/dist/styles/navigation.css +8 -6
  129. package/dist/styles.css +1 -1
  130. package/dist/tempest-react-sdk.d.ts +130 -17
  131. package/dist/theme/create-theme.cjs +1 -1
  132. package/dist/theme/create-theme.cjs.map +1 -1
  133. package/dist/theme/create-theme.js +6 -1
  134. package/dist/theme/create-theme.js.map +1 -1
  135. package/dist/theme/inverse-render.cjs +1 -1
  136. package/dist/theme/inverse-render.cjs.map +1 -1
  137. package/dist/theme/inverse-render.js +1 -1
  138. package/dist/theme/inverse-render.js.map +1 -1
  139. package/dist/theme/inverse-restore.cjs +5 -0
  140. package/dist/theme/inverse-restore.cjs.map +1 -0
  141. package/dist/theme/inverse-restore.js +30 -0
  142. package/dist/theme/inverse-restore.js.map +1 -0
  143. package/dist/theme/inverse-surface.cjs +1 -1
  144. package/dist/theme/inverse-surface.cjs.map +1 -1
  145. package/dist/theme/inverse-surface.js +69 -72
  146. package/dist/theme/inverse-surface.js.map +1 -1
  147. package/package.json +11 -1
@@ -1 +1 @@
1
- {"version":3,"file":"MultiSelect.cjs","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract, the knobs that make it multi (maxItems, filter,\n * emptyMessage) and portal. The body is the listbox with chip removal, type-ahead and\n * the ARIA active-descendant wiring reading one active index.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./MultiSelect.module.css\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n options: MultiSelectOption[];\n /** Currently selected values. */\n value: string[];\n onChange: (value: string[]) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Cap the number of selectable items. */\n maxItems?: number;\n /** Custom filter — return true to keep the option. Default: case-insensitive substring on label. */\n filter?: (option: MultiSelectOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n /**\n * Render the list in a portal, positioned under the field. Default `true`.\n *\n * In flow, an ancestor with `overflow` other than `visible` clips the list —\n * in the last row of a `DataTable` none of it was visible (measured in Chrome\n * at 1440 px). In a portal it escapes the clip, takes the field's width,\n * flips above the field when there is no room below, and follows it on\n * scroll, resize and when the field grows a line of chips.\n */\n portal?: boolean;\n className?: string;\n}\n\nfunction defaultFilter(option: MultiSelectOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\n/**\n * MultiSelect — a filterable dropdown that selects many options, shown as\n * removable chips inside the field. Selecting toggles a value; Backspace on an\n * empty query removes the last chip.\n *\n * Keyboard: ArrowUp/ArrowDown navigate, Enter toggles the active option, Esc\n * closes, Backspace (empty input) pops the last chip.\n */\nexport function MultiSelect({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n maxItems,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: MultiSelectProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n const [fieldNode, setFieldNode] = useState<HTMLDivElement | null>(null);\n const [listNode, setListNode] = useState<HTMLUListElement | null>(null);\n const floatingStyle = useAnchorPosition({\n anchor: fieldNode,\n floating: listNode,\n side: \"bottom\",\n align: \"start\",\n offset: LIST_OFFSET,\n enabled: portal && open,\n matchWidth: true,\n });\n\n const selectedSet = useMemo(() => new Set(value), [value]);\n const selectedOptions = useMemo(\n () =>\n value\n .map((v) => options.find((o) => o.value === v))\n .filter((o): o is MultiSelectOption => Boolean(o)),\n [value, options],\n );\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const atMax = maxItems !== undefined && value.length >= maxItems;\n\n const toggle = useCallback(\n (option: MultiSelectOption): void => {\n if (option.disabled) return;\n if (selectedSet.has(option.value)) {\n onChange(value.filter((v) => v !== option.value));\n } else {\n if (atMax) return;\n onChange([...value, option.value]);\n }\n setQuery(\"\");\n inputRef.current?.focus();\n },\n [selectedSet, onChange, value, atMax],\n );\n\n const removeAt = useCallback(\n (optionValue: string): void => {\n onChange(value.filter((v) => v !== optionValue));\n },\n [onChange, value],\n );\n\n /**\n * Close on a press outside both the field and the list.\n *\n * The list is checked on its own because in a portal it is not inside the\n * field's wrapper: a press on its scrollbar, or on the empty message, would\n * otherwise count as outside and close it.\n */\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n const target = event.target as Node;\n if (rootRef.current?.contains(target) || listNode?.contains(target)) return;\n setOpen(false);\n setQuery(\"\");\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, listNode]);\n\n /**\n * Open the list and keep focus on the input when the empty part of the field\n * is pressed.\n *\n * Only a press on the field itself counts: a press on a chip or on its remove\n * button has its own handler, and the input focuses itself.\n */\n const handleFieldMouseDown = (event: React.MouseEvent<HTMLDivElement>): void => {\n if (disabled || event.target !== event.currentTarget) return;\n event.preventDefault();\n inputRef.current?.focus();\n setOpen(true);\n };\n\n /**\n * Keyboard model of the input.\n *\n * `Escape` calls `preventDefault()` only while the list is open: that marks the\n * key as consumed, so an enclosing `Modal` or `Drawer` stays open and the next\n * `Escape` reaches it. With the list closed the key is left alone and dismisses\n * the enclosing layer, as the user expects.\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) toggle(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n setOpen(false);\n setQuery(\"\");\n } else if (event.key === \"Backspace\" && query === \"\" && value.length > 0) {\n removeAt(value[value.length - 1]);\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\n aria-multiselectable\n className={cn(styles.menu, portal && styles.portalled)}\n style={floatingStyle}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => {\n const isSelected = selectedSet.has(option.value);\n return (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || (!isSelected && atMax)}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n isSelected && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n toggle(option);\n }}\n >\n <span className={styles.check} aria-hidden>\n {isSelected ? \"✓\" : \"\"}\n </span>\n {option.label}\n </li>\n );\n })\n )}\n </ul>\n );\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div\n ref={setFieldNode}\n className={cn(styles.field, disabled && styles.disabled)}\n onMouseDown={handleFieldMouseDown}\n >\n {selectedOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.label}\n <button\n type=\"button\"\n className={styles.chipRemove}\n aria-label={`Remover ${option.label}`}\n disabled={disabled}\n onMouseDown={(event) => {\n event.preventDefault();\n removeAt(option.value);\n }}\n >\n ×\n </button>\n </span>\n ))}\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n className={styles.input}\n placeholder={value.length === 0 ? placeholder : \"\"}\n disabled={disabled}\n value={query}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n </div>\n {open && (portal ? <Portal>{list}</Portal> : list)}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MultiSelect.cjs","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract, the knobs that make it multi (maxItems, filter,\n * emptyMessage) and portal. The body is the listbox with chip removal, type-ahead and\n * the ARIA active-descendant wiring reading one active index.\n */\nimport { useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { useListboxDismiss } from \"@/components/Combobox/use-listbox-dismiss\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./MultiSelect.module.css\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n options: MultiSelectOption[];\n /** Currently selected values. */\n value: string[];\n onChange: (value: string[]) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Cap the number of selectable items. */\n maxItems?: number;\n /** Custom filter — return true to keep the option. Default: case-insensitive substring on label. */\n filter?: (option: MultiSelectOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n /**\n * Render the list in a portal, positioned under the field. Default `true`.\n *\n * In flow, an ancestor with `overflow` other than `visible` clips the list —\n * in the last row of a `DataTable` none of it was visible (measured in Chrome\n * at 1440 px). In a portal it escapes the clip, takes the field's width,\n * flips above the field when there is no room below, and follows it on\n * scroll, resize and when the field grows a line of chips.\n */\n portal?: boolean;\n className?: string;\n}\n\nfunction defaultFilter(option: MultiSelectOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\n/**\n * MultiSelect — a filterable dropdown that selects many options, shown as\n * removable chips inside the field. Selecting toggles a value; Backspace on an\n * empty query removes the last chip.\n *\n * Keyboard: ArrowUp/ArrowDown navigate — the active option is announced through\n * `aria-activedescendant`, focus stays on the input — Enter toggles the active\n * option, Esc closes, Backspace (empty input) pops the last chip, and\n * Tab/Shift+Tab leave the field closing the list.\n */\nexport function MultiSelect({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n maxItems,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: MultiSelectProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n const [fieldNode, setFieldNode] = useState<HTMLDivElement | null>(null);\n const [listNode, setListNode] = useState<HTMLUListElement | null>(null);\n const floatingStyle = useAnchorPosition({\n anchor: fieldNode,\n floating: listNode,\n side: \"bottom\",\n align: \"start\",\n offset: LIST_OFFSET,\n enabled: portal && open,\n matchWidth: true,\n });\n\n const selectedSet = useMemo(() => new Set(value), [value]);\n const selectedOptions = useMemo(\n () =>\n value\n .map((v) => options.find((o) => o.value === v))\n .filter((o): o is MultiSelectOption => Boolean(o)),\n [value, options],\n );\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const atMax = maxItems !== undefined && value.length >= maxItems;\n\n const toggle = useCallback(\n (option: MultiSelectOption): void => {\n if (option.disabled) return;\n if (selectedSet.has(option.value)) {\n onChange(value.filter((v) => v !== option.value));\n } else {\n if (atMax) return;\n onChange([...value, option.value]);\n }\n setQuery(\"\");\n inputRef.current?.focus();\n },\n [selectedSet, onChange, value, atMax],\n );\n\n const removeAt = useCallback(\n (optionValue: string): void => {\n onChange(value.filter((v) => v !== optionValue));\n },\n [onChange, value],\n );\n\n const close = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n }, []);\n\n const { onInputBlur, onListMouseDown } = useListboxDismiss({\n open,\n rootRef,\n listNode,\n onDismiss: close,\n });\n const optionId = (index: number): string => `${id}-option-${index}`;\n const activeOption = open ? filtered[activeIndex] : undefined;\n\n /**\n * Open the list and keep focus on the input when the empty part of the field\n * is pressed.\n *\n * Only a press on the field itself counts: a press on a chip or on its remove\n * button has its own handler, and the input focuses itself.\n */\n const handleFieldMouseDown = (event: React.MouseEvent<HTMLDivElement>): void => {\n if (disabled || event.target !== event.currentTarget) return;\n event.preventDefault();\n inputRef.current?.focus();\n setOpen(true);\n };\n\n /**\n * Keyboard model of the input.\n *\n * `Escape` calls `preventDefault()` only while the list is open: that marks the\n * key as consumed, so an enclosing `Modal` or `Drawer` stays open and the next\n * `Escape` reaches it. With the list closed the key is left alone and dismisses\n * the enclosing layer, as the user expects.\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) toggle(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n close();\n } else if (event.key === \"Backspace\" && query === \"\" && value.length > 0) {\n removeAt(value[value.length - 1]);\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\n aria-multiselectable\n className={cn(styles.menu, portal && styles.portalled)}\n style={floatingStyle}\n onMouseDown={onListMouseDown}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => {\n const isSelected = selectedSet.has(option.value);\n return (\n <li\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || (!isSelected && atMax)}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n isSelected && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={() => toggle(option)}\n >\n <span className={styles.check} aria-hidden>\n {isSelected ? \"✓\" : \"\"}\n </span>\n {option.label}\n </li>\n );\n })\n )}\n </ul>\n );\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div\n ref={setFieldNode}\n className={cn(styles.field, disabled && styles.disabled)}\n onMouseDown={handleFieldMouseDown}\n >\n {selectedOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.label}\n <button\n type=\"button\"\n className={styles.chipRemove}\n aria-label={`Remover ${option.label}`}\n disabled={disabled}\n onMouseDown={(event) => {\n event.preventDefault();\n removeAt(option.value);\n }}\n >\n ×\n </button>\n </span>\n ))}\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n aria-activedescendant={activeOption ? optionId(activeIndex) : undefined}\n className={styles.input}\n placeholder={value.length === 0 ? placeholder : \"\"}\n disabled={disabled}\n value={query}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n onBlur={onInputBlur}\n />\n </div>\n {open && (portal ? <Portal>{list}</Portal> : list)}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
2
  import { Portal as t } from "../Portal/Portal.js";
3
3
  import { useAnchorPosition as n } from "../Portal/anchor-position.js";
4
- import r from "./MultiSelect.module.js";
5
- import { useCallback as i, useEffect as a, useId as o, useMemo as s, useRef as c, useState as l } from "react";
4
+ import { useListboxDismiss as r } from "../Combobox/use-listbox-dismiss.js";
5
+ import i from "./MultiSelect.module.js";
6
+ import { useCallback as a, useId as o, useMemo as s, useRef as c, useState as l } from "react";
6
7
  import { jsx as u, jsxs as d } from "react/jsx-runtime";
7
8
  //#region src/components/MultiSelect/MultiSelect.tsx
8
9
  function f(e, t) {
@@ -10,7 +11,7 @@ function f(e, t) {
10
11
  }
11
12
  var p = 4;
12
13
  function m({ options: m, value: h, onChange: g, label: _, placeholder: v = "Selecione", helperText: y, error: b, disabled: x, maxItems: S, filter: C = f, emptyMessage: w = "Nenhuma opção encontrada", portal: T = !0, className: E }) {
13
- let D = o(), O = c(null), k = c(null), [A, j] = l(!1), [M, N] = l(""), [P, F] = l(0), [I, L] = l(null), [R, z] = l(null), B = n({
14
+ let D = o(), O = c(null), k = c(null), [A, j] = l(!1), [M, N] = l(""), [P, F] = l(0), [I, L] = l(null), [R, ee] = l(null), z = n({
14
15
  anchor: I,
15
16
  floating: R,
16
17
  side: "bottom",
@@ -18,96 +19,94 @@ function m({ options: m, value: h, onChange: g, label: _, placeholder: v = "Sele
18
19
  offset: p,
19
20
  enabled: T && A,
20
21
  matchWidth: !0
21
- }), V = s(() => new Set(h), [h]), H = s(() => h.map((e) => m.find((t) => t.value === e)).filter((e) => !!e), [h, m]), U = s(() => m.filter((e) => C(e, M)), [
22
+ }), B = s(() => new Set(h), [h]), V = s(() => h.map((e) => m.find((t) => t.value === e)).filter((e) => !!e), [h, m]), H = s(() => m.filter((e) => C(e, M)), [
22
23
  m,
23
24
  C,
24
25
  M
25
- ]), W = S !== void 0 && h.length >= S, G = i((e) => {
26
+ ]), U = S !== void 0 && h.length >= S, W = a((e) => {
26
27
  if (!e.disabled) {
27
- if (V.has(e.value)) g(h.filter((t) => t !== e.value));
28
+ if (B.has(e.value)) g(h.filter((t) => t !== e.value));
28
29
  else {
29
- if (W) return;
30
+ if (U) return;
30
31
  g([...h, e.value]);
31
32
  }
32
33
  N(""), k.current?.focus();
33
34
  }
34
35
  }, [
35
- V,
36
+ B,
36
37
  g,
37
38
  h,
38
- W
39
- ]), K = i((e) => {
39
+ U
40
+ ]), G = a((e) => {
40
41
  g(h.filter((t) => t !== e));
41
- }, [g, h]);
42
- a(() => {
43
- if (!A) return;
44
- let e = (e) => {
45
- let t = e.target;
46
- O.current?.contains(t) || R?.contains(t) || (j(!1), N(""));
47
- };
48
- return window.addEventListener("mousedown", e), () => window.removeEventListener("mousedown", e);
49
- }, [A, R]);
50
- let q = (e) => {
42
+ }, [g, h]), K = a(() => {
43
+ j(!1), N("");
44
+ }, []), { onInputBlur: q, onListMouseDown: J } = r({
45
+ open: A,
46
+ rootRef: O,
47
+ listNode: R,
48
+ onDismiss: K
49
+ }), Y = (e) => `${D}-option-${e}`, X = A ? H[P] : void 0, Z = (e) => {
51
50
  x || e.target !== e.currentTarget || (e.preventDefault(), k.current?.focus(), j(!0));
52
- }, J = (e) => {
53
- if (e.key === "ArrowDown") e.preventDefault(), j(!0), F((e) => Math.min(U.length - 1, e + 1));
51
+ }, Q = (e) => {
52
+ if (e.key === "ArrowDown") e.preventDefault(), j(!0), F((e) => Math.min(H.length - 1, e + 1));
54
53
  else if (e.key === "ArrowUp") e.preventDefault(), F((e) => Math.max(0, e - 1));
55
54
  else if (e.key === "Enter") {
56
55
  e.preventDefault();
57
- let t = U[P];
58
- t && G(t);
59
- } else e.key === "Escape" ? (A && e.preventDefault(), j(!1), N("")) : e.key === "Backspace" && M === "" && h.length > 0 && K(h[h.length - 1]);
60
- }, Y = /* @__PURE__ */ u("ul", {
61
- ref: z,
56
+ let t = H[P];
57
+ t && W(t);
58
+ } else e.key === "Escape" ? (A && e.preventDefault(), K()) : e.key === "Backspace" && M === "" && h.length > 0 && G(h[h.length - 1]);
59
+ }, $ = /* @__PURE__ */ u("ul", {
60
+ ref: ee,
62
61
  id: `${D}-listbox`,
63
62
  role: "listbox",
64
63
  "aria-multiselectable": !0,
65
- className: e(r.menu, T && r.portalled),
66
- style: B,
67
- children: U.length === 0 ? /* @__PURE__ */ u("li", {
68
- className: r.empty,
64
+ className: e(i.menu, T && i.portalled),
65
+ style: z,
66
+ onMouseDown: J,
67
+ children: H.length === 0 ? /* @__PURE__ */ u("li", {
68
+ className: i.empty,
69
69
  children: w
70
- }) : U.map((t, n) => {
71
- let i = V.has(t.value);
70
+ }) : H.map((t, n) => {
71
+ let r = B.has(t.value);
72
72
  return /* @__PURE__ */ d("li", {
73
+ id: Y(n),
73
74
  role: "option",
74
- "aria-selected": i,
75
- "aria-disabled": t.disabled || !i && W,
76
- className: e(r.option, n === P && r.active, i && r.selected),
75
+ "aria-selected": r,
76
+ "aria-disabled": t.disabled || !r && U,
77
+ className: e(i.option, n === P && i.active, r && i.selected),
77
78
  onMouseEnter: () => F(n),
78
- onMouseDown: (e) => {
79
- e.preventDefault(), G(t);
80
- },
79
+ onMouseDown: () => W(t),
81
80
  children: [/* @__PURE__ */ u("span", {
82
- className: r.check,
81
+ className: i.check,
83
82
  "aria-hidden": !0,
84
- children: i ? "✓" : ""
83
+ children: r ? "✓" : ""
85
84
  }), t.label]
86
85
  }, t.value);
87
86
  })
88
87
  });
89
88
  return /* @__PURE__ */ d("div", {
90
89
  ref: O,
91
- className: e(r.wrapper, b && r.error, E),
90
+ className: e(i.wrapper, b && i.error, E),
92
91
  children: [
93
92
  _ && /* @__PURE__ */ u("label", {
94
93
  htmlFor: D,
95
- className: r.label,
94
+ className: i.label,
96
95
  children: _
97
96
  }),
98
97
  /* @__PURE__ */ d("div", {
99
98
  ref: L,
100
- className: e(r.field, x && r.disabled),
101
- onMouseDown: q,
102
- children: [H.map((e) => /* @__PURE__ */ d("span", {
103
- className: r.chip,
99
+ className: e(i.field, x && i.disabled),
100
+ onMouseDown: Z,
101
+ children: [V.map((e) => /* @__PURE__ */ d("span", {
102
+ className: i.chip,
104
103
  children: [e.label, /* @__PURE__ */ u("button", {
105
104
  type: "button",
106
- className: r.chipRemove,
105
+ className: i.chipRemove,
107
106
  "aria-label": `Remover ${e.label}`,
108
107
  disabled: x,
109
108
  onMouseDown: (t) => {
110
- t.preventDefault(), K(e.value);
109
+ t.preventDefault(), G(e.value);
111
110
  },
112
111
  children: "×"
113
112
  })]
@@ -119,7 +118,8 @@ function m({ options: m, value: h, onChange: g, label: _, placeholder: v = "Sele
119
118
  "aria-expanded": A,
120
119
  "aria-controls": `${D}-listbox`,
121
120
  "aria-autocomplete": "list",
122
- className: r.input,
121
+ "aria-activedescendant": X ? Y(P) : void 0,
122
+ className: i.input,
123
123
  placeholder: h.length === 0 ? v : "",
124
124
  disabled: x,
125
125
  value: M,
@@ -127,15 +127,16 @@ function m({ options: m, value: h, onChange: g, label: _, placeholder: v = "Sele
127
127
  onChange: (e) => {
128
128
  N(e.target.value), j(!0), F(0);
129
129
  },
130
- onKeyDown: J
130
+ onKeyDown: Q,
131
+ onBlur: q
131
132
  })]
132
133
  }),
133
- A && (T ? /* @__PURE__ */ u(t, { children: Y }) : Y),
134
+ A && (T ? /* @__PURE__ */ u(t, { children: $ }) : $),
134
135
  b ? /* @__PURE__ */ u("span", {
135
- className: r.errorText,
136
+ className: i.errorText,
136
137
  children: b
137
138
  }) : y ? /* @__PURE__ */ u("span", {
138
- className: r.helper,
139
+ className: i.helper,
139
140
  children: y
140
141
  }) : null
141
142
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"MultiSelect.js","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract, the knobs that make it multi (maxItems, filter,\n * emptyMessage) and portal. The body is the listbox with chip removal, type-ahead and\n * the ARIA active-descendant wiring reading one active index.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./MultiSelect.module.css\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n options: MultiSelectOption[];\n /** Currently selected values. */\n value: string[];\n onChange: (value: string[]) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Cap the number of selectable items. */\n maxItems?: number;\n /** Custom filter — return true to keep the option. Default: case-insensitive substring on label. */\n filter?: (option: MultiSelectOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n /**\n * Render the list in a portal, positioned under the field. Default `true`.\n *\n * In flow, an ancestor with `overflow` other than `visible` clips the list —\n * in the last row of a `DataTable` none of it was visible (measured in Chrome\n * at 1440 px). In a portal it escapes the clip, takes the field's width,\n * flips above the field when there is no room below, and follows it on\n * scroll, resize and when the field grows a line of chips.\n */\n portal?: boolean;\n className?: string;\n}\n\nfunction defaultFilter(option: MultiSelectOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\n/**\n * MultiSelect — a filterable dropdown that selects many options, shown as\n * removable chips inside the field. Selecting toggles a value; Backspace on an\n * empty query removes the last chip.\n *\n * Keyboard: ArrowUp/ArrowDown navigate, Enter toggles the active option, Esc\n * closes, Backspace (empty input) pops the last chip.\n */\nexport function MultiSelect({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n maxItems,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: MultiSelectProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n const [fieldNode, setFieldNode] = useState<HTMLDivElement | null>(null);\n const [listNode, setListNode] = useState<HTMLUListElement | null>(null);\n const floatingStyle = useAnchorPosition({\n anchor: fieldNode,\n floating: listNode,\n side: \"bottom\",\n align: \"start\",\n offset: LIST_OFFSET,\n enabled: portal && open,\n matchWidth: true,\n });\n\n const selectedSet = useMemo(() => new Set(value), [value]);\n const selectedOptions = useMemo(\n () =>\n value\n .map((v) => options.find((o) => o.value === v))\n .filter((o): o is MultiSelectOption => Boolean(o)),\n [value, options],\n );\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const atMax = maxItems !== undefined && value.length >= maxItems;\n\n const toggle = useCallback(\n (option: MultiSelectOption): void => {\n if (option.disabled) return;\n if (selectedSet.has(option.value)) {\n onChange(value.filter((v) => v !== option.value));\n } else {\n if (atMax) return;\n onChange([...value, option.value]);\n }\n setQuery(\"\");\n inputRef.current?.focus();\n },\n [selectedSet, onChange, value, atMax],\n );\n\n const removeAt = useCallback(\n (optionValue: string): void => {\n onChange(value.filter((v) => v !== optionValue));\n },\n [onChange, value],\n );\n\n /**\n * Close on a press outside both the field and the list.\n *\n * The list is checked on its own because in a portal it is not inside the\n * field's wrapper: a press on its scrollbar, or on the empty message, would\n * otherwise count as outside and close it.\n */\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n const target = event.target as Node;\n if (rootRef.current?.contains(target) || listNode?.contains(target)) return;\n setOpen(false);\n setQuery(\"\");\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, listNode]);\n\n /**\n * Open the list and keep focus on the input when the empty part of the field\n * is pressed.\n *\n * Only a press on the field itself counts: a press on a chip or on its remove\n * button has its own handler, and the input focuses itself.\n */\n const handleFieldMouseDown = (event: React.MouseEvent<HTMLDivElement>): void => {\n if (disabled || event.target !== event.currentTarget) return;\n event.preventDefault();\n inputRef.current?.focus();\n setOpen(true);\n };\n\n /**\n * Keyboard model of the input.\n *\n * `Escape` calls `preventDefault()` only while the list is open: that marks the\n * key as consumed, so an enclosing `Modal` or `Drawer` stays open and the next\n * `Escape` reaches it. With the list closed the key is left alone and dismisses\n * the enclosing layer, as the user expects.\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) toggle(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n setOpen(false);\n setQuery(\"\");\n } else if (event.key === \"Backspace\" && query === \"\" && value.length > 0) {\n removeAt(value[value.length - 1]);\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\n aria-multiselectable\n className={cn(styles.menu, portal && styles.portalled)}\n style={floatingStyle}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => {\n const isSelected = selectedSet.has(option.value);\n return (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || (!isSelected && atMax)}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n isSelected && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n toggle(option);\n }}\n >\n <span className={styles.check} aria-hidden>\n {isSelected ? \"✓\" : \"\"}\n </span>\n {option.label}\n </li>\n );\n })\n )}\n </ul>\n );\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div\n ref={setFieldNode}\n className={cn(styles.field, disabled && styles.disabled)}\n onMouseDown={handleFieldMouseDown}\n >\n {selectedOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.label}\n <button\n type=\"button\"\n className={styles.chipRemove}\n aria-label={`Remover ${option.label}`}\n disabled={disabled}\n onMouseDown={(event) => {\n event.preventDefault();\n removeAt(option.value);\n }}\n >\n ×\n </button>\n </span>\n ))}\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n className={styles.input}\n placeholder={value.length === 0 ? placeholder : \"\"}\n disabled={disabled}\n value={query}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n </div>\n {open && (portal ? <Portal>{list}</Portal> : list)}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MultiSelect.js","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract, the knobs that make it multi (maxItems, filter,\n * emptyMessage) and portal. The body is the listbox with chip removal, type-ahead and\n * the ARIA active-descendant wiring reading one active index.\n */\nimport { useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { useListboxDismiss } from \"@/components/Combobox/use-listbox-dismiss\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./MultiSelect.module.css\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n options: MultiSelectOption[];\n /** Currently selected values. */\n value: string[];\n onChange: (value: string[]) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Cap the number of selectable items. */\n maxItems?: number;\n /** Custom filter — return true to keep the option. Default: case-insensitive substring on label. */\n filter?: (option: MultiSelectOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n /**\n * Render the list in a portal, positioned under the field. Default `true`.\n *\n * In flow, an ancestor with `overflow` other than `visible` clips the list —\n * in the last row of a `DataTable` none of it was visible (measured in Chrome\n * at 1440 px). In a portal it escapes the clip, takes the field's width,\n * flips above the field when there is no room below, and follows it on\n * scroll, resize and when the field grows a line of chips.\n */\n portal?: boolean;\n className?: string;\n}\n\nfunction defaultFilter(option: MultiSelectOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\n/**\n * MultiSelect — a filterable dropdown that selects many options, shown as\n * removable chips inside the field. Selecting toggles a value; Backspace on an\n * empty query removes the last chip.\n *\n * Keyboard: ArrowUp/ArrowDown navigate — the active option is announced through\n * `aria-activedescendant`, focus stays on the input — Enter toggles the active\n * option, Esc closes, Backspace (empty input) pops the last chip, and\n * Tab/Shift+Tab leave the field closing the list.\n */\nexport function MultiSelect({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n maxItems,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: MultiSelectProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n const [fieldNode, setFieldNode] = useState<HTMLDivElement | null>(null);\n const [listNode, setListNode] = useState<HTMLUListElement | null>(null);\n const floatingStyle = useAnchorPosition({\n anchor: fieldNode,\n floating: listNode,\n side: \"bottom\",\n align: \"start\",\n offset: LIST_OFFSET,\n enabled: portal && open,\n matchWidth: true,\n });\n\n const selectedSet = useMemo(() => new Set(value), [value]);\n const selectedOptions = useMemo(\n () =>\n value\n .map((v) => options.find((o) => o.value === v))\n .filter((o): o is MultiSelectOption => Boolean(o)),\n [value, options],\n );\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const atMax = maxItems !== undefined && value.length >= maxItems;\n\n const toggle = useCallback(\n (option: MultiSelectOption): void => {\n if (option.disabled) return;\n if (selectedSet.has(option.value)) {\n onChange(value.filter((v) => v !== option.value));\n } else {\n if (atMax) return;\n onChange([...value, option.value]);\n }\n setQuery(\"\");\n inputRef.current?.focus();\n },\n [selectedSet, onChange, value, atMax],\n );\n\n const removeAt = useCallback(\n (optionValue: string): void => {\n onChange(value.filter((v) => v !== optionValue));\n },\n [onChange, value],\n );\n\n const close = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n }, []);\n\n const { onInputBlur, onListMouseDown } = useListboxDismiss({\n open,\n rootRef,\n listNode,\n onDismiss: close,\n });\n const optionId = (index: number): string => `${id}-option-${index}`;\n const activeOption = open ? filtered[activeIndex] : undefined;\n\n /**\n * Open the list and keep focus on the input when the empty part of the field\n * is pressed.\n *\n * Only a press on the field itself counts: a press on a chip or on its remove\n * button has its own handler, and the input focuses itself.\n */\n const handleFieldMouseDown = (event: React.MouseEvent<HTMLDivElement>): void => {\n if (disabled || event.target !== event.currentTarget) return;\n event.preventDefault();\n inputRef.current?.focus();\n setOpen(true);\n };\n\n /**\n * Keyboard model of the input.\n *\n * `Escape` calls `preventDefault()` only while the list is open: that marks the\n * key as consumed, so an enclosing `Modal` or `Drawer` stays open and the next\n * `Escape` reaches it. With the list closed the key is left alone and dismisses\n * the enclosing layer, as the user expects.\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) toggle(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n close();\n } else if (event.key === \"Backspace\" && query === \"\" && value.length > 0) {\n removeAt(value[value.length - 1]);\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\n aria-multiselectable\n className={cn(styles.menu, portal && styles.portalled)}\n style={floatingStyle}\n onMouseDown={onListMouseDown}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => {\n const isSelected = selectedSet.has(option.value);\n return (\n <li\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || (!isSelected && atMax)}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n isSelected && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={() => toggle(option)}\n >\n <span className={styles.check} aria-hidden>\n {isSelected ? \"✓\" : \"\"}\n </span>\n {option.label}\n </li>\n );\n })\n )}\n </ul>\n );\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div\n ref={setFieldNode}\n className={cn(styles.field, disabled && styles.disabled)}\n onMouseDown={handleFieldMouseDown}\n >\n {selectedOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.label}\n <button\n type=\"button\"\n className={styles.chipRemove}\n aria-label={`Remover ${option.label}`}\n disabled={disabled}\n onMouseDown={(event) => {\n event.preventDefault();\n removeAt(option.value);\n }}\n >\n ×\n </button>\n </span>\n ))}\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n aria-activedescendant={activeOption ? optionId(activeIndex) : undefined}\n className={styles.input}\n placeholder={value.length === 0 ? placeholder : \"\"}\n disabled={disabled}\n value={query}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n onBlur={onInputBlur}\n />\n </div>\n {open && (portal ? <Portal>{list}</Portal> : list)}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("./Navbar.module.cjs");let n=require("react/jsx-runtime");function r({logo:r,nav:i,actions:a,sticky:o=!0,tone:s=`surface`,bordered:c=!0,className:l,...u}){return(0,n.jsxs)(`header`,{className:e.cn(t.default.navbar,t.default[s],o&&t.default.sticky,c&&t.default.bordered,l),...u,children:[r&&(0,n.jsx)(`div`,{className:t.default.logo,children:r}),i&&(0,n.jsx)(`nav`,{className:t.default.nav,children:i}),a&&(0,n.jsx)(`div`,{className:t.default.actions,children:a})]})}exports.Navbar=r;
1
+ const e=require("../../utils/cn.cjs"),t=require("./Navbar.module.cjs");let n=require("react/jsx-runtime");function r({logo:r,nav:i,actions:a,sticky:o=!0,tone:s=`surface`,bordered:c=!0,className:l,...u}){return(0,n.jsxs)(`header`,{className:e.cn(t.default.navbar,t.default[s],o&&t.default.sticky,c&&t.default.bordered,l),"data-tempest-tone":s===`inverse`?`inverse`:void 0,...u,children:[r&&(0,n.jsx)(`div`,{className:t.default.logo,children:r}),i&&(0,n.jsx)(`nav`,{className:t.default.nav,children:i}),a&&(0,n.jsx)(`div`,{className:t.default.actions,children:a})]})}exports.Navbar=r;
2
2
  //# sourceMappingURL=Navbar.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Navbar.cjs","names":[],"sources":["../../../src/components/Navbar/Navbar.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Navbar.module.css\";\n\nexport type NavbarTone = \"surface\" | \"primary\" | \"transparent\";\n\nexport interface NavbarProps extends HTMLAttributes<HTMLElement> {\n /** Left slot — typically logo + brand name. */\n logo?: ReactNode;\n /** Center slot — typically nav links. */\n nav?: ReactNode;\n /** Right slot — typically user menu / actions. */\n actions?: ReactNode;\n /** Make the bar sticky at the top of the scroll container. Default `true`. */\n sticky?: boolean;\n /** Visual tone. Default `\"surface\"`. */\n tone?: NavbarTone;\n /** When set, renders a thin bottom border. Default `true`. */\n bordered?: boolean;\n}\n\n/**\n * Top app bar. Three-slot layout (logo / nav / actions) that collapses\n * gracefully on mobile (nav slot wraps below).\n *\n * @example\n * <Navbar\n * logo={<img src=\"/logo.svg\" alt=\"App\" />}\n * nav={<NavLinks />}\n * actions={<UserMenu />}\n * />\n */\nexport function Navbar({\n logo,\n nav,\n actions,\n sticky = true,\n tone = \"surface\",\n bordered = true,\n className,\n ...props\n}: NavbarProps) {\n return (\n <header\n className={cn(\n styles.navbar,\n styles[tone],\n sticky && styles.sticky,\n bordered && styles.bordered,\n className,\n )}\n {...props}\n >\n {logo && <div className={styles.logo}>{logo}</div>}\n {nav && <nav className={styles.nav}>{nav}</nav>}\n {actions && <div className={styles.actions}>{actions}</div>}\n </header>\n );\n}\n"],"mappings":"0GAgCA,SAAgB,EAAO,CACnB,OACA,MACA,UACA,SAAS,GACT,OAAO,UACP,WAAW,GACX,YACA,GAAG,GACS,CACZ,OACI,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,UAAW,EAAA,GACP,EAAA,QAAO,OACP,EAAA,QAAO,GACP,GAAU,EAAA,QAAO,OACjB,GAAY,EAAA,QAAO,SACnB,CACJ,EACA,GAAI,EARR,SAAA,CAUK,IAAQ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAO,SAAA,CAAU,CAAA,EAChD,IAAO,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,IAAM,SAAA,CAAS,CAAA,EAC7C,IAAW,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,QAAU,SAAA,CAAa,CAAA,CACtD,GAEhB"}
1
+ {"version":3,"file":"Navbar.cjs","names":[],"sources":["../../../src/components/Navbar/Navbar.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Navbar.module.css\";\n\n/**\n * Visual tone of a {@link Navbar}.\n *\n * - `\"surface\"` — the page's surface and text.\n * - `\"primary\"` — the brand fill (`--tempest-primary`) with\n * `--tempest-primary-foreground` ink. Only the bar's own text follows the fill:\n * components inside it keep the page's tokens. Measured in Chromium over the\n * SDK's `#0066ff`, a link button inside it reads 1:1, muted text 1.59:1 and the\n * focus ring 1.41:1. Fine for a bar that holds a logo and plain text.\n * - `\"inverse\"` — the inverse surface: every color token inverted inside the bar,\n * so a primary `Button`, a link, the focus ring and muted text are measured\n * against the fill. See {@link NavbarProps.tone}.\n * - `\"transparent\"` — no background; the text inherits.\n */\nexport type NavbarTone = \"surface\" | \"primary\" | \"inverse\" | \"transparent\";\n\nexport interface NavbarProps extends HTMLAttributes<HTMLElement> {\n /** Left slot — typically logo + brand name. */\n logo?: ReactNode;\n /** Center slot — typically nav links. */\n nav?: ReactNode;\n /** Right slot — typically user menu / actions. */\n actions?: ReactNode;\n /** Make the bar sticky at the top of the scroll container. Default `true`. */\n sticky?: boolean;\n /**\n * Visual tone. Default `\"surface\"`.\n *\n * `\"inverse\"` writes `data-tempest-tone=\"inverse\"` and paints the bar with the\n * inverse surface's `--tempest-bg`, so the components in the slots invert with\n * it. The tokens are opt-in: `tempest-react-sdk/styles/inverse.css` for the\n * SDK's own blue, or `createTheme({ primary, inverse: createInverseSurface })`\n * for yours. With neither loaded the bar falls back to the page's\n * `--tempest-bg` and `--tempest-text` — legible, not inverted.\n *\n * A separate tone rather than a change to `\"primary\"`, because the inverse\n * fill is not always the brand's `500`: for `#0066ff` it is the `800`\n * (`#042e75`), and turning `\"primary\"` into it would repaint every bar that\n * uses it today.\n */\n tone?: NavbarTone;\n /** When set, renders a thin bottom border. Default `true`. */\n bordered?: boolean;\n}\n\n/**\n * Top app bar. Three-slot layout (logo / nav / actions) that collapses\n * gracefully on mobile (nav slot wraps below).\n *\n * @example\n * <Navbar\n * logo={<img src=\"/logo.svg\" alt=\"App\" />}\n * nav={<NavLinks />}\n * actions={<UserMenu />}\n * />\n */\nexport function Navbar({\n logo,\n nav,\n actions,\n sticky = true,\n tone = \"surface\",\n bordered = true,\n className,\n ...props\n}: NavbarProps) {\n return (\n <header\n className={cn(\n styles.navbar,\n styles[tone],\n sticky && styles.sticky,\n bordered && styles.bordered,\n className,\n )}\n data-tempest-tone={tone === \"inverse\" ? \"inverse\" : undefined}\n {...props}\n >\n {logo && <div className={styles.logo}>{logo}</div>}\n {nav && <nav className={styles.nav}>{nav}</nav>}\n {actions && <div className={styles.actions}>{actions}</div>}\n </header>\n );\n}\n"],"mappings":"0GA4DA,SAAgB,EAAO,CACnB,OACA,MACA,UACA,SAAS,GACT,OAAO,UACP,WAAW,GACX,YACA,GAAG,GACS,CACZ,OACI,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,UAAW,EAAA,GACP,EAAA,QAAO,OACP,EAAA,QAAO,GACP,GAAU,EAAA,QAAO,OACjB,GAAY,EAAA,QAAO,SACnB,CACJ,EACA,oBAAmB,IAAS,UAAY,UAAY,IAAA,GACpD,GAAI,EATR,SAAA,CAWK,IAAQ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAO,SAAA,CAAU,CAAA,EAChD,IAAO,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,IAAM,SAAA,CAAS,CAAA,EAC7C,IAAW,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,QAAU,SAAA,CAAa,CAAA,CACtD,GAEhB"}
@@ -5,6 +5,7 @@ import { jsx as n, jsxs as r } from "react/jsx-runtime";
5
5
  function i({ logo: i, nav: a, actions: o, sticky: s = !0, tone: c = "surface", bordered: l = !0, className: u, ...d }) {
6
6
  return /* @__PURE__ */ r("header", {
7
7
  className: e(t.navbar, t[c], s && t.sticky, l && t.bordered, u),
8
+ "data-tempest-tone": c === "inverse" ? "inverse" : void 0,
8
9
  ...d,
9
10
  children: [
10
11
  i && /* @__PURE__ */ n("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"Navbar.js","names":[],"sources":["../../../src/components/Navbar/Navbar.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Navbar.module.css\";\n\nexport type NavbarTone = \"surface\" | \"primary\" | \"transparent\";\n\nexport interface NavbarProps extends HTMLAttributes<HTMLElement> {\n /** Left slot — typically logo + brand name. */\n logo?: ReactNode;\n /** Center slot — typically nav links. */\n nav?: ReactNode;\n /** Right slot — typically user menu / actions. */\n actions?: ReactNode;\n /** Make the bar sticky at the top of the scroll container. Default `true`. */\n sticky?: boolean;\n /** Visual tone. Default `\"surface\"`. */\n tone?: NavbarTone;\n /** When set, renders a thin bottom border. Default `true`. */\n bordered?: boolean;\n}\n\n/**\n * Top app bar. Three-slot layout (logo / nav / actions) that collapses\n * gracefully on mobile (nav slot wraps below).\n *\n * @example\n * <Navbar\n * logo={<img src=\"/logo.svg\" alt=\"App\" />}\n * nav={<NavLinks />}\n * actions={<UserMenu />}\n * />\n */\nexport function Navbar({\n logo,\n nav,\n actions,\n sticky = true,\n tone = \"surface\",\n bordered = true,\n className,\n ...props\n}: NavbarProps) {\n return (\n <header\n className={cn(\n styles.navbar,\n styles[tone],\n sticky && styles.sticky,\n bordered && styles.bordered,\n className,\n )}\n {...props}\n >\n {logo && <div className={styles.logo}>{logo}</div>}\n {nav && <nav className={styles.nav}>{nav}</nav>}\n {actions && <div className={styles.actions}>{actions}</div>}\n </header>\n );\n}\n"],"mappings":";;;;AAgCA,SAAgB,EAAO,EACnB,SACA,QACA,YACA,YAAS,IACT,UAAO,WACP,cAAW,IACX,cACA,GAAG,KACS;CACZ,OACI,kBAAC,UAAD;EACI,WAAW,EACP,EAAO,QACP,EAAO,IACP,KAAU,EAAO,QACjB,KAAY,EAAO,UACnB,CACJ;EACA,GAAI;EARR,UAAA;GAUK,KAAQ,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAO,UAAA;GAAU,CAAA;GAChD,KAAO,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAM,UAAA;GAAS,CAAA;GAC7C,KAAW,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAU,UAAA;GAAa,CAAA;EACtD;;AAEhB"}
1
+ {"version":3,"file":"Navbar.js","names":[],"sources":["../../../src/components/Navbar/Navbar.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Navbar.module.css\";\n\n/**\n * Visual tone of a {@link Navbar}.\n *\n * - `\"surface\"` — the page's surface and text.\n * - `\"primary\"` — the brand fill (`--tempest-primary`) with\n * `--tempest-primary-foreground` ink. Only the bar's own text follows the fill:\n * components inside it keep the page's tokens. Measured in Chromium over the\n * SDK's `#0066ff`, a link button inside it reads 1:1, muted text 1.59:1 and the\n * focus ring 1.41:1. Fine for a bar that holds a logo and plain text.\n * - `\"inverse\"` — the inverse surface: every color token inverted inside the bar,\n * so a primary `Button`, a link, the focus ring and muted text are measured\n * against the fill. See {@link NavbarProps.tone}.\n * - `\"transparent\"` — no background; the text inherits.\n */\nexport type NavbarTone = \"surface\" | \"primary\" | \"inverse\" | \"transparent\";\n\nexport interface NavbarProps extends HTMLAttributes<HTMLElement> {\n /** Left slot — typically logo + brand name. */\n logo?: ReactNode;\n /** Center slot — typically nav links. */\n nav?: ReactNode;\n /** Right slot — typically user menu / actions. */\n actions?: ReactNode;\n /** Make the bar sticky at the top of the scroll container. Default `true`. */\n sticky?: boolean;\n /**\n * Visual tone. Default `\"surface\"`.\n *\n * `\"inverse\"` writes `data-tempest-tone=\"inverse\"` and paints the bar with the\n * inverse surface's `--tempest-bg`, so the components in the slots invert with\n * it. The tokens are opt-in: `tempest-react-sdk/styles/inverse.css` for the\n * SDK's own blue, or `createTheme({ primary, inverse: createInverseSurface })`\n * for yours. With neither loaded the bar falls back to the page's\n * `--tempest-bg` and `--tempest-text` — legible, not inverted.\n *\n * A separate tone rather than a change to `\"primary\"`, because the inverse\n * fill is not always the brand's `500`: for `#0066ff` it is the `800`\n * (`#042e75`), and turning `\"primary\"` into it would repaint every bar that\n * uses it today.\n */\n tone?: NavbarTone;\n /** When set, renders a thin bottom border. Default `true`. */\n bordered?: boolean;\n}\n\n/**\n * Top app bar. Three-slot layout (logo / nav / actions) that collapses\n * gracefully on mobile (nav slot wraps below).\n *\n * @example\n * <Navbar\n * logo={<img src=\"/logo.svg\" alt=\"App\" />}\n * nav={<NavLinks />}\n * actions={<UserMenu />}\n * />\n */\nexport function Navbar({\n logo,\n nav,\n actions,\n sticky = true,\n tone = \"surface\",\n bordered = true,\n className,\n ...props\n}: NavbarProps) {\n return (\n <header\n className={cn(\n styles.navbar,\n styles[tone],\n sticky && styles.sticky,\n bordered && styles.bordered,\n className,\n )}\n data-tempest-tone={tone === \"inverse\" ? \"inverse\" : undefined}\n {...props}\n >\n {logo && <div className={styles.logo}>{logo}</div>}\n {nav && <nav className={styles.nav}>{nav}</nav>}\n {actions && <div className={styles.actions}>{actions}</div>}\n </header>\n );\n}\n"],"mappings":";;;;AA4DA,SAAgB,EAAO,EACnB,SACA,QACA,YACA,YAAS,IACT,UAAO,WACP,cAAW,IACX,cACA,GAAG,KACS;CACZ,OACI,kBAAC,UAAD;EACI,WAAW,EACP,EAAO,QACP,EAAO,IACP,KAAU,EAAO,QACjB,KAAY,EAAO,UACnB,CACJ;EACA,qBAAmB,MAAS,YAAY,YAAY,KAAA;EACpD,GAAI;EATR,UAAA;GAWK,KAAQ,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAO,UAAA;GAAU,CAAA;GAChD,KAAO,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAM,UAAA;GAAS,CAAA;GAC7C,KAAW,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAU,UAAA;GAAa,CAAA;EACtD;;AAEhB"}
@@ -1,3 +1,3 @@
1
1
  require("../../styles/component/Navbar.css");
2
- var e=`tempest_navbar_Ey25T`,t=`tempest_surface_xyKhM`,n=`tempest_primary_w3ft-`,r=`tempest_transparent_N1vMR`,i=`tempest_bordered_HD49R`,a=`tempest_sticky_0XsOs`,o=`tempest_logo_O9j3q`,s=`tempest_nav_0oisE`,c=`tempest_actions_8BNWJ`,l={navbar:e,surface:t,primary:n,transparent:r,bordered:i,sticky:a,logo:o,nav:s,actions:c};exports.actions=c,exports.bordered=i,exports.default=l,exports.logo=o,exports.nav=s,exports.navbar=e,exports.primary=n,exports.sticky=a,exports.surface=t,exports.transparent=r;
2
+ var e=`tempest_navbar_Ey25T`,t=`tempest_surface_xyKhM`,n=`tempest_primary_w3ft-`,r=`tempest_inverse_f26-i`,i=`tempest_transparent_N1vMR`,a=`tempest_bordered_HD49R`,o=`tempest_sticky_0XsOs`,s=`tempest_logo_O9j3q`,c=`tempest_nav_0oisE`,l=`tempest_actions_8BNWJ`,u={navbar:e,surface:t,primary:n,inverse:r,transparent:i,bordered:a,sticky:o,logo:s,nav:c,actions:l};exports.actions=l,exports.bordered=a,exports.default=u,exports.inverse=r,exports.logo=s,exports.nav=c,exports.navbar=e,exports.primary=n,exports.sticky=o,exports.surface=t,exports.transparent=i;
3
3
  //# sourceMappingURL=Navbar.module.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Navbar.module.cjs","names":[],"sources":["../../../src/components/Navbar/Navbar.module.css"],"sourcesContent":[".navbar {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-4);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n min-height: 56px;\n width: 100%;\n font-family: var(--tempest-font-sans);\n padding-top: max(var(--tempest-space-3), env(safe-area-inset-top, 0));\n}\n\n.navbar.surface {\n background: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.navbar.primary {\n background: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.navbar.transparent {\n background: transparent;\n color: inherit;\n}\n\n.navbar.bordered {\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.navbar.sticky {\n position: sticky;\n top: 0;\n z-index: 30;\n backdrop-filter: saturate(180%) blur(8px);\n}\n\n.logo {\n display: inline-flex;\n align-items: center;\n flex: 0 0 auto;\n}\n\n.nav {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n flex: 1 1 auto;\n min-width: 0;\n overflow-x: auto;\n}\n\n.actions {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n flex: 0 0 auto;\n margin-left: auto;\n}\n"],"mappings":";"}
1
+ {"version":3,"file":"Navbar.module.cjs","names":[],"sources":["../../../src/components/Navbar/Navbar.module.css"],"sourcesContent":[".navbar {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-4);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n min-height: 56px;\n width: 100%;\n font-family: var(--tempest-font-sans);\n padding-top: max(var(--tempest-space-3), env(safe-area-inset-top, 0));\n}\n\n.navbar.surface {\n background: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.navbar.primary {\n background: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.navbar.inverse {\n background: var(--tempest-bg);\n color: var(--tempest-text);\n}\n\n.navbar.transparent {\n background: transparent;\n color: inherit;\n}\n\n.navbar.bordered {\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.navbar.sticky {\n position: sticky;\n top: 0;\n z-index: 30;\n backdrop-filter: saturate(180%) blur(8px);\n}\n\n.logo {\n display: inline-flex;\n align-items: center;\n flex: 0 0 auto;\n}\n\n.nav {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n flex: 1 1 auto;\n min-width: 0;\n overflow-x: auto;\n}\n\n.actions {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n flex: 0 0 auto;\n margin-left: auto;\n}\n"],"mappings":";"}
@@ -1,17 +1,18 @@
1
1
  import "../../styles/component/Navbar.css";
2
2
  //#region src/components/Navbar/Navbar.module.css
3
- var e = "tempest_navbar_Ey25T", t = "tempest_surface_xyKhM", n = "tempest_primary_w3ft-", r = "tempest_transparent_N1vMR", i = "tempest_bordered_HD49R", a = "tempest_sticky_0XsOs", o = "tempest_logo_O9j3q", s = "tempest_nav_0oisE", c = "tempest_actions_8BNWJ", l = {
3
+ var e = "tempest_navbar_Ey25T", t = "tempest_surface_xyKhM", n = "tempest_primary_w3ft-", r = "tempest_inverse_f26-i", i = "tempest_transparent_N1vMR", a = "tempest_bordered_HD49R", o = "tempest_sticky_0XsOs", s = "tempest_logo_O9j3q", c = "tempest_nav_0oisE", l = "tempest_actions_8BNWJ", u = {
4
4
  navbar: e,
5
5
  surface: t,
6
6
  primary: n,
7
- transparent: r,
8
- bordered: i,
9
- sticky: a,
10
- logo: o,
11
- nav: s,
12
- actions: c
7
+ inverse: r,
8
+ transparent: i,
9
+ bordered: a,
10
+ sticky: o,
11
+ logo: s,
12
+ nav: c,
13
+ actions: l
13
14
  };
14
15
  //#endregion
15
- export { c as actions, i as bordered, l as default, o as logo, s as nav, e as navbar, n as primary, a as sticky, t as surface, r as transparent };
16
+ export { l as actions, a as bordered, u as default, r as inverse, s as logo, c as nav, e as navbar, n as primary, o as sticky, t as surface, i as transparent };
16
17
 
17
18
  //# sourceMappingURL=Navbar.module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Navbar.module.js","names":[],"sources":["../../../src/components/Navbar/Navbar.module.css"],"sourcesContent":[".navbar {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-4);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n min-height: 56px;\n width: 100%;\n font-family: var(--tempest-font-sans);\n padding-top: max(var(--tempest-space-3), env(safe-area-inset-top, 0));\n}\n\n.navbar.surface {\n background: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.navbar.primary {\n background: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.navbar.transparent {\n background: transparent;\n color: inherit;\n}\n\n.navbar.bordered {\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.navbar.sticky {\n position: sticky;\n top: 0;\n z-index: 30;\n backdrop-filter: saturate(180%) blur(8px);\n}\n\n.logo {\n display: inline-flex;\n align-items: center;\n flex: 0 0 auto;\n}\n\n.nav {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n flex: 1 1 auto;\n min-width: 0;\n overflow-x: auto;\n}\n\n.actions {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n flex: 0 0 auto;\n margin-left: auto;\n}\n"],"mappings":";"}
1
+ {"version":3,"file":"Navbar.module.js","names":[],"sources":["../../../src/components/Navbar/Navbar.module.css"],"sourcesContent":[".navbar {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-4);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n min-height: 56px;\n width: 100%;\n font-family: var(--tempest-font-sans);\n padding-top: max(var(--tempest-space-3), env(safe-area-inset-top, 0));\n}\n\n.navbar.surface {\n background: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.navbar.primary {\n background: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.navbar.inverse {\n background: var(--tempest-bg);\n color: var(--tempest-text);\n}\n\n.navbar.transparent {\n background: transparent;\n color: inherit;\n}\n\n.navbar.bordered {\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.navbar.sticky {\n position: sticky;\n top: 0;\n z-index: 30;\n backdrop-filter: saturate(180%) blur(8px);\n}\n\n.logo {\n display: inline-flex;\n align-items: center;\n flex: 0 0 auto;\n}\n\n.nav {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n flex: 1 1 auto;\n min-width: 0;\n overflow-x: auto;\n}\n\n.actions {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n flex: 0 0 auto;\n margin-left: auto;\n}\n"],"mappings":";"}
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationMenu.module.cjs","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.module.css"],"sourcesContent":[".root {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n}\n\n.list {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.item {\n position: relative;\n}\n\n.link,\n.trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n text-decoration: none;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .link:hover,\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.link:focus-visible,\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-nav-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.subItem {\n display: block;\n}\n\n.subItem .link {\n width: 100%;\n}\n\n@keyframes tempest-nav-menu-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n\n.portalled {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
1
+ {"version":3,"file":"NavigationMenu.module.cjs","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.module.css"],"sourcesContent":[".root {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n}\n\n.list {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.item {\n position: relative;\n}\n\n.link,\n.trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n text-decoration: none;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .link:hover,\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.link:focus-visible,\n.trigger:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-nav-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.subItem {\n display: block;\n}\n\n.subItem .link {\n width: 100%;\n}\n\n@keyframes tempest-nav-menu-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n\n.portalled {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationMenu.module.js","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.module.css"],"sourcesContent":[".root {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n}\n\n.list {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.item {\n position: relative;\n}\n\n.link,\n.trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n text-decoration: none;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .link:hover,\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.link:focus-visible,\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-nav-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.subItem {\n display: block;\n}\n\n.subItem .link {\n width: 100%;\n}\n\n@keyframes tempest-nav-menu-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n\n.portalled {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
1
+ {"version":3,"file":"NavigationMenu.module.js","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.module.css"],"sourcesContent":[".root {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n}\n\n.list {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.item {\n position: relative;\n}\n\n.link,\n.trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n text-decoration: none;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .link:hover,\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.link:focus-visible,\n.trigger:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-nav-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.subItem {\n display: block;\n}\n\n.subItem .link {\n width: 100%;\n}\n\n@keyframes tempest-nav-menu-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n\n.portalled {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationRail.module.cjs","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.module.css"],"sourcesContent":[".rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 80px;\n height: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-2);\n background: var(--tempest-surface);\n border-right: 1px solid var(--tempest-border);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-bottom: var(--tempest-space-2);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--tempest-space-1);\n width: 100%;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.footer {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-top: auto;\n padding-top: var(--tempest-space-2);\n}\n\n.item {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-1);\n background: none;\n border: 0;\n border-radius: var(--tempest-radius-md);\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition:\n color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.item:hover {\n color: var(--tempest-text);\n background: var(--tempest-surface-2);\n}\n\n.item:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color, var(--tempest-primary));\n outline-offset: var(--tempest-focus-ring-offset, 2px);\n}\n\n.item.active {\n color: var(--tempest-primary-on-soft);\n background: var(--tempest-primary-soft);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 24px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n font-size: 10px;\n line-height: 14px;\n text-align: center;\n font-weight: var(--tempest-weight-semibold);\n}\n\n.label {\n line-height: 1;\n text-align: center;\n}\n"],"mappings":";"}
1
+ {"version":3,"file":"NavigationRail.module.cjs","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.module.css"],"sourcesContent":[".rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 80px;\n height: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-2);\n background: var(--tempest-surface);\n border-right: 1px solid var(--tempest-border);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-bottom: var(--tempest-space-2);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--tempest-space-1);\n width: 100%;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.footer {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-top: auto;\n padding-top: var(--tempest-space-2);\n}\n\n.item {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-1);\n background: none;\n border: 0;\n border-radius: var(--tempest-radius-md);\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition:\n color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.item:hover {\n color: var(--tempest-text);\n background: var(--tempest-surface-2);\n}\n\n.item:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color, currentColor);\n outline-offset: var(--tempest-focus-ring-offset, 2px);\n}\n\n.item.active {\n color: var(--tempest-primary-on-soft);\n background: var(--tempest-primary-soft);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 24px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n font-size: 10px;\n line-height: 14px;\n text-align: center;\n font-weight: var(--tempest-weight-semibold);\n}\n\n.label {\n line-height: 1;\n text-align: center;\n}\n"],"mappings":";"}
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationRail.module.js","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.module.css"],"sourcesContent":[".rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 80px;\n height: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-2);\n background: var(--tempest-surface);\n border-right: 1px solid var(--tempest-border);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-bottom: var(--tempest-space-2);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--tempest-space-1);\n width: 100%;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.footer {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-top: auto;\n padding-top: var(--tempest-space-2);\n}\n\n.item {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-1);\n background: none;\n border: 0;\n border-radius: var(--tempest-radius-md);\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition:\n color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.item:hover {\n color: var(--tempest-text);\n background: var(--tempest-surface-2);\n}\n\n.item:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color, var(--tempest-primary));\n outline-offset: var(--tempest-focus-ring-offset, 2px);\n}\n\n.item.active {\n color: var(--tempest-primary-on-soft);\n background: var(--tempest-primary-soft);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 24px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n font-size: 10px;\n line-height: 14px;\n text-align: center;\n font-weight: var(--tempest-weight-semibold);\n}\n\n.label {\n line-height: 1;\n text-align: center;\n}\n"],"mappings":";"}
1
+ {"version":3,"file":"NavigationRail.module.js","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.module.css"],"sourcesContent":[".rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 80px;\n height: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-2);\n background: var(--tempest-surface);\n border-right: 1px solid var(--tempest-border);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-bottom: var(--tempest-space-2);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--tempest-space-1);\n width: 100%;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.footer {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-top: auto;\n padding-top: var(--tempest-space-2);\n}\n\n.item {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-1);\n background: none;\n border: 0;\n border-radius: var(--tempest-radius-md);\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition:\n color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.item:hover {\n color: var(--tempest-text);\n background: var(--tempest-surface-2);\n}\n\n.item:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color, currentColor);\n outline-offset: var(--tempest-focus-ring-offset, 2px);\n}\n\n.item.active {\n color: var(--tempest-primary-on-soft);\n background: var(--tempest-primary-soft);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 24px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n font-size: 10px;\n line-height: 14px;\n text-align: center;\n font-weight: var(--tempest-weight-semibold);\n}\n\n.label {\n line-height: 1;\n text-align: center;\n}\n"],"mappings":";"}
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationCenter.module.cjs","names":[],"sources":["../../../src/components/NotificationCenter/NotificationCenter.module.css"],"sourcesContent":[".panel {\n display: flex;\n flex-direction: column;\n min-width: 0;\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.title {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n margin: 0;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n}\n\n.headerAction {\n padding: 0;\n border: none;\n background: none;\n color: var(--tempest-primary);\n font: inherit;\n font-size: var(--tempest-text-xs);\n cursor: pointer;\n white-space: nowrap;\n}\n\n.headerAction:hover {\n text-decoration: underline;\n}\n\n.headerAction:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n border-radius: var(--tempest-radius-sm);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n overflow-y: auto;\n}\n\n.item {\n display: flex;\n align-items: flex-start;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.item:last-child {\n border-bottom: none;\n}\n\n/*\n * Unread is marked by a left bar plus a tinted background, never by colour alone\n * — the distinction has to survive a monochrome display and colour blindness.\n * `aria-current` on the row carries the same information non-visually.\n */\n.item.unread {\n background-color: var(--tempest-surface);\n box-shadow: inset 3px 0 0 0 var(--tempest-primary);\n}\n\n.icon {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface-2, var(--tempest-surface));\n color: var(--tempest-text-muted);\n}\n\n.body {\n display: flex;\n flex: 1;\n min-width: 0;\n flex-direction: column;\n gap: var(--tempest-space-1);\n}\n\n/*\n * The whole title/body block is one button so the hit target matches what the\n * user perceives as \"the notification\", instead of just its title text.\n */\n.trigger {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n border-radius: var(--tempest-radius-sm);\n}\n\n.itemTitle {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n line-height: var(--tempest-leading-snug);\n}\n\n.itemBody {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n}\n\n.time {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n.dismiss {\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-full);\n background: none;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-base);\n line-height: 1;\n cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .dismiss:hover {\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n }\n}\n\n.dismiss:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n"],"mappings":";"}
1
+ {"version":3,"file":"NotificationCenter.module.cjs","names":[],"sources":["../../../src/components/NotificationCenter/NotificationCenter.module.css"],"sourcesContent":[".panel {\n display: flex;\n flex-direction: column;\n min-width: 0;\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.title {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n margin: 0;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n}\n\n.headerAction {\n padding: 0;\n border: none;\n background: none;\n color: var(--tempest-primary);\n font: inherit;\n font-size: var(--tempest-text-xs);\n cursor: pointer;\n white-space: nowrap;\n}\n\n.headerAction:hover {\n text-decoration: underline;\n}\n\n.headerAction:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n border-radius: var(--tempest-radius-sm);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n overflow-y: auto;\n}\n\n.item {\n display: flex;\n align-items: flex-start;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.item:last-child {\n border-bottom: none;\n}\n\n/*\n * Unread is marked by a left bar plus a tinted background, never by colour alone\n * — the distinction has to survive a monochrome display and colour blindness.\n * `aria-current` on the row carries the same information non-visually.\n */\n.item.unread {\n background-color: var(--tempest-surface);\n box-shadow: inset 3px 0 0 0 var(--tempest-primary);\n}\n\n.icon {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface-2, var(--tempest-surface));\n color: var(--tempest-text-muted);\n}\n\n.body {\n display: flex;\n flex: 1;\n min-width: 0;\n flex-direction: column;\n gap: var(--tempest-space-1);\n}\n\n/*\n * The whole title/body block is one button so the hit target matches what the\n * user perceives as \"the notification\", instead of just its title text.\n */\n.trigger {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.trigger:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n border-radius: var(--tempest-radius-sm);\n}\n\n.itemTitle {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n line-height: var(--tempest-leading-snug);\n}\n\n.itemBody {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n}\n\n.time {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n.dismiss {\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-full);\n background: none;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-base);\n line-height: 1;\n cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .dismiss:hover {\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n }\n}\n\n.dismiss:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n"],"mappings":";"}
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationCenter.module.js","names":[],"sources":["../../../src/components/NotificationCenter/NotificationCenter.module.css"],"sourcesContent":[".panel {\n display: flex;\n flex-direction: column;\n min-width: 0;\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.title {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n margin: 0;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n}\n\n.headerAction {\n padding: 0;\n border: none;\n background: none;\n color: var(--tempest-primary);\n font: inherit;\n font-size: var(--tempest-text-xs);\n cursor: pointer;\n white-space: nowrap;\n}\n\n.headerAction:hover {\n text-decoration: underline;\n}\n\n.headerAction:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n border-radius: var(--tempest-radius-sm);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n overflow-y: auto;\n}\n\n.item {\n display: flex;\n align-items: flex-start;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.item:last-child {\n border-bottom: none;\n}\n\n/*\n * Unread is marked by a left bar plus a tinted background, never by colour alone\n * — the distinction has to survive a monochrome display and colour blindness.\n * `aria-current` on the row carries the same information non-visually.\n */\n.item.unread {\n background-color: var(--tempest-surface);\n box-shadow: inset 3px 0 0 0 var(--tempest-primary);\n}\n\n.icon {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface-2, var(--tempest-surface));\n color: var(--tempest-text-muted);\n}\n\n.body {\n display: flex;\n flex: 1;\n min-width: 0;\n flex-direction: column;\n gap: var(--tempest-space-1);\n}\n\n/*\n * The whole title/body block is one button so the hit target matches what the\n * user perceives as \"the notification\", instead of just its title text.\n */\n.trigger {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n border-radius: var(--tempest-radius-sm);\n}\n\n.itemTitle {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n line-height: var(--tempest-leading-snug);\n}\n\n.itemBody {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n}\n\n.time {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n.dismiss {\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-full);\n background: none;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-base);\n line-height: 1;\n cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .dismiss:hover {\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n }\n}\n\n.dismiss:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n"],"mappings":";"}
1
+ {"version":3,"file":"NotificationCenter.module.js","names":[],"sources":["../../../src/components/NotificationCenter/NotificationCenter.module.css"],"sourcesContent":[".panel {\n display: flex;\n flex-direction: column;\n min-width: 0;\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.title {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n margin: 0;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n}\n\n.headerAction {\n padding: 0;\n border: none;\n background: none;\n color: var(--tempest-primary);\n font: inherit;\n font-size: var(--tempest-text-xs);\n cursor: pointer;\n white-space: nowrap;\n}\n\n.headerAction:hover {\n text-decoration: underline;\n}\n\n.headerAction:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n border-radius: var(--tempest-radius-sm);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n overflow-y: auto;\n}\n\n.item {\n display: flex;\n align-items: flex-start;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.item:last-child {\n border-bottom: none;\n}\n\n/*\n * Unread is marked by a left bar plus a tinted background, never by colour alone\n * — the distinction has to survive a monochrome display and colour blindness.\n * `aria-current` on the row carries the same information non-visually.\n */\n.item.unread {\n background-color: var(--tempest-surface);\n box-shadow: inset 3px 0 0 0 var(--tempest-primary);\n}\n\n.icon {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface-2, var(--tempest-surface));\n color: var(--tempest-text-muted);\n}\n\n.body {\n display: flex;\n flex: 1;\n min-width: 0;\n flex-direction: column;\n gap: var(--tempest-space-1);\n}\n\n/*\n * The whole title/body block is one button so the hit target matches what the\n * user perceives as \"the notification\", instead of just its title text.\n */\n.trigger {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.trigger:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n border-radius: var(--tempest-radius-sm);\n}\n\n.itemTitle {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n line-height: var(--tempest-leading-snug);\n}\n\n.itemBody {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n}\n\n.time {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n.dismiss {\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-full);\n background: none;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-base);\n line-height: 1;\n cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .dismiss:hover {\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n }\n}\n\n.dismiss:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n"],"mappings":";"}
@@ -1 +1 @@
1
- {"version":3,"file":"PasswordInput.module.cjs","names":[],"sources":["../../../src/components/PasswordInput/PasswordInput.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n width: 100%;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n color: var(--tempest-text);\n}\n\n.field {\n position: relative;\n display: flex;\n align-items: center;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background: var(--tempest-surface);\n transition: border-color var(--tempest-duration-fast, 120ms);\n}\n\n.field:focus-within {\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width, 2px)\n var(--tempest-focus-ring-color, var(--tempest-primary));\n}\n\n.input {\n flex: 1 1 auto;\n border: 0;\n background: transparent;\n outline: 0;\n color: var(--tempest-text);\n font: inherit;\n font-family: var(--tempest-font-sans);\n}\n\n.sm .input {\n padding: 6px 8px;\n font-size: var(--tempest-text-sm);\n}\n\n.md .input {\n padding: 8px 12px;\n font-size: var(--tempest-text-md);\n}\n\n.lg .input {\n padding: 10px 14px;\n font-size: var(--tempest-text-lg);\n}\n\n.toggle {\n flex: 0 0 auto;\n background: none;\n border: 0;\n cursor: pointer;\n padding: 0 12px;\n font-size: 16px;\n color: var(--tempest-text-muted);\n}\n\n.toggle:hover {\n color: var(--tempest-text);\n}\n\n.error .field {\n border-color: var(--tempest-danger-border, var(--tempest-danger));\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger-fg, var(--tempest-danger));\n}\n\n.strength {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n font-size: var(--tempest-text-xs);\n}\n\n.strengthBar {\n flex: 1 1 auto;\n height: 4px;\n background: var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n overflow: hidden;\n}\n\n.strengthBar > span {\n display: block;\n height: 100%;\n transition: width var(--tempest-duration-base, 200ms);\n}\n\n.strengthLabel {\n color: var(--tempest-text-muted);\n flex: 0 0 auto;\n}\n\n.level0 .strengthBar > span,\n.level1 .strengthBar > span {\n background: var(--tempest-danger-solid, var(--tempest-danger));\n}\n\n.level2 .strengthBar > span {\n background: var(--tempest-warning-solid, var(--tempest-warning));\n}\n\n.level3 .strengthBar > span,\n.level4 .strengthBar > span {\n background: var(--tempest-success-solid, var(--tempest-success));\n}\n"],"mappings":";"}
1
+ {"version":3,"file":"PasswordInput.module.cjs","names":[],"sources":["../../../src/components/PasswordInput/PasswordInput.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n width: 100%;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n color: var(--tempest-text);\n}\n\n.field {\n position: relative;\n display: flex;\n align-items: center;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background: var(--tempest-surface);\n transition: border-color var(--tempest-duration-fast, 120ms);\n}\n\n.field:focus-within {\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width, 2px)\n var(--tempest-focus-ring-color, currentColor);\n}\n\n.input {\n flex: 1 1 auto;\n border: 0;\n background: transparent;\n outline: 0;\n color: var(--tempest-text);\n font: inherit;\n font-family: var(--tempest-font-sans);\n}\n\n.sm .input {\n padding: 6px 8px;\n font-size: var(--tempest-text-sm);\n}\n\n.md .input {\n padding: 8px 12px;\n font-size: var(--tempest-text-md);\n}\n\n.lg .input {\n padding: 10px 14px;\n font-size: var(--tempest-text-lg);\n}\n\n.toggle {\n flex: 0 0 auto;\n background: none;\n border: 0;\n cursor: pointer;\n padding: 0 12px;\n font-size: 16px;\n color: var(--tempest-text-muted);\n}\n\n.toggle:hover {\n color: var(--tempest-text);\n}\n\n.error .field {\n border-color: var(--tempest-danger-border, var(--tempest-danger));\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger-fg, var(--tempest-danger));\n}\n\n.strength {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n font-size: var(--tempest-text-xs);\n}\n\n.strengthBar {\n flex: 1 1 auto;\n height: 4px;\n background: var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n overflow: hidden;\n}\n\n.strengthBar > span {\n display: block;\n height: 100%;\n transition: width var(--tempest-duration-base, 200ms);\n}\n\n.strengthLabel {\n color: var(--tempest-text-muted);\n flex: 0 0 auto;\n}\n\n.level0 .strengthBar > span,\n.level1 .strengthBar > span {\n background: var(--tempest-danger-solid, var(--tempest-danger));\n}\n\n.level2 .strengthBar > span {\n background: var(--tempest-warning-solid, var(--tempest-warning));\n}\n\n.level3 .strengthBar > span,\n.level4 .strengthBar > span {\n background: var(--tempest-success-solid, var(--tempest-success));\n}\n"],"mappings":";"}