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.
- package/README.md +3 -1
- package/dist/br/BrazilMap.module.cjs.map +1 -1
- package/dist/br/BrazilMap.module.js.map +1 -1
- package/dist/br/MunicipalitySearch.cjs +1 -1
- package/dist/br/MunicipalitySearch.cjs.map +1 -1
- package/dist/br/MunicipalitySearch.js +66 -48
- package/dist/br/MunicipalitySearch.js.map +1 -1
- package/dist/br/MunicipalitySearch.module.cjs +1 -1
- package/dist/br/MunicipalitySearch.module.cjs.map +1 -1
- package/dist/br/MunicipalitySearch.module.js +4 -3
- package/dist/br/MunicipalitySearch.module.js.map +1 -1
- package/dist/br.d.ts +12 -0
- package/dist/components/AIChat/AIChat.module.cjs.map +1 -1
- package/dist/components/AIChat/AIChat.module.js.map +1 -1
- package/dist/components/AppBar/AppBar.cjs +1 -1
- package/dist/components/AppBar/AppBar.cjs.map +1 -1
- package/dist/components/AppBar/AppBar.js +1 -0
- package/dist/components/AppBar/AppBar.js.map +1 -1
- package/dist/components/AppBar/AppBar.module.cjs +1 -1
- package/dist/components/AppBar/AppBar.module.cjs.map +1 -1
- package/dist/components/AppBar/AppBar.module.js +12 -11
- package/dist/components/AppBar/AppBar.module.js.map +1 -1
- package/dist/components/Button/Button.module.cjs.map +1 -1
- package/dist/components/Button/Button.module.js.map +1 -1
- package/dist/components/Button/ButtonContent.cjs.map +1 -1
- package/dist/components/Button/ButtonContent.js.map +1 -1
- package/dist/components/Carousel/Carousel.module.cjs.map +1 -1
- package/dist/components/Carousel/Carousel.module.js.map +1 -1
- package/dist/components/Chat/Chat.module.cjs.map +1 -1
- package/dist/components/Chat/Chat.module.js.map +1 -1
- package/dist/components/Combobox/Combobox.cjs +1 -1
- package/dist/components/Combobox/Combobox.cjs.map +1 -1
- package/dist/components/Combobox/Combobox.js +18 -23
- package/dist/components/Combobox/Combobox.js.map +1 -1
- package/dist/components/Combobox/use-listbox-dismiss.cjs +2 -0
- package/dist/components/Combobox/use-listbox-dismiss.cjs.map +1 -0
- package/dist/components/Combobox/use-listbox-dismiss.js +34 -0
- package/dist/components/Combobox/use-listbox-dismiss.js.map +1 -0
- package/dist/components/Dropzone/Dropzone.module.cjs.map +1 -1
- package/dist/components/Dropzone/Dropzone.module.js.map +1 -1
- package/dist/components/FilterBar/FilterBar.module.cjs.map +1 -1
- package/dist/components/FilterBar/FilterBar.module.js.map +1 -1
- package/dist/components/FloatingActionButton/FloatingActionButton.module.cjs.map +1 -1
- package/dist/components/FloatingActionButton/FloatingActionButton.module.js.map +1 -1
- package/dist/components/ImageCropper/ImageCropper.module.cjs.map +1 -1
- package/dist/components/ImageCropper/ImageCropper.module.js.map +1 -1
- package/dist/components/ListTile/ListTile.module.cjs.map +1 -1
- package/dist/components/ListTile/ListTile.module.js.map +1 -1
- package/dist/components/Markdown/Markdown.module.cjs.map +1 -1
- package/dist/components/Markdown/Markdown.module.js.map +1 -1
- package/dist/components/Menubar/Menubar.module.cjs.map +1 -1
- package/dist/components/Menubar/Menubar.module.js.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.cjs +1 -1
- package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.js +55 -54
- package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/components/Navbar/Navbar.cjs +1 -1
- package/dist/components/Navbar/Navbar.cjs.map +1 -1
- package/dist/components/Navbar/Navbar.js +1 -0
- package/dist/components/Navbar/Navbar.js.map +1 -1
- package/dist/components/Navbar/Navbar.module.cjs +1 -1
- package/dist/components/Navbar/Navbar.module.cjs.map +1 -1
- package/dist/components/Navbar/Navbar.module.js +9 -8
- package/dist/components/Navbar/Navbar.module.js.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.module.cjs.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.module.js.map +1 -1
- package/dist/components/NavigationRail/NavigationRail.module.cjs.map +1 -1
- package/dist/components/NavigationRail/NavigationRail.module.js.map +1 -1
- package/dist/components/NotificationCenter/NotificationCenter.module.cjs.map +1 -1
- package/dist/components/NotificationCenter/NotificationCenter.module.js.map +1 -1
- package/dist/components/PasswordInput/PasswordInput.module.cjs.map +1 -1
- package/dist/components/PasswordInput/PasswordInput.module.js.map +1 -1
- package/dist/components/PinInput/PinInput.module.cjs.map +1 -1
- package/dist/components/PinInput/PinInput.module.js.map +1 -1
- package/dist/components/SectionHeader/SectionHeader.cjs +1 -1
- package/dist/components/SectionHeader/SectionHeader.cjs.map +1 -1
- package/dist/components/SectionHeader/SectionHeader.js +33 -25
- package/dist/components/SectionHeader/SectionHeader.js.map +1 -1
- package/dist/components/Tour/Tour.module.cjs.map +1 -1
- package/dist/components/Tour/Tour.module.js.map +1 -1
- package/dist/components/Transfer/Transfer.module.cjs.map +1 -1
- package/dist/components/Transfer/Transfer.module.js.map +1 -1
- package/dist/components/VirtualTable/VirtualTable.module.cjs.map +1 -1
- package/dist/components/VirtualTable/VirtualTable.module.js.map +1 -1
- package/dist/geo/TrajectoryMap.module.cjs.map +1 -1
- package/dist/geo/TrajectoryMap.module.js.map +1 -1
- package/dist/icons/IconPicker.module.cjs.map +1 -1
- package/dist/icons/IconPicker.module.js.map +1 -1
- package/dist/neural.cjs +2 -0
- package/dist/neural.cjs.map +1 -0
- package/dist/neural.d.ts +110 -0
- package/dist/neural.js +48 -0
- package/dist/neural.js.map +1 -0
- package/dist/styles/actions.css +2 -2
- package/dist/styles/br.css +3 -3
- package/dist/styles/chat.css +5 -5
- package/dist/styles/component/AIChat.css +3 -3
- package/dist/styles/component/AppBar.css +1 -0
- package/dist/styles/component/BrazilMap.css +2 -2
- package/dist/styles/component/Button.css +1 -1
- package/dist/styles/component/Carousel.css +3 -3
- package/dist/styles/component/Chat.css +2 -2
- package/dist/styles/component/Dropzone.css +2 -1
- package/dist/styles/component/FilterBar.css +2 -2
- package/dist/styles/component/FloatingActionButton.css +1 -1
- package/dist/styles/component/IconPicker.css +1 -1
- package/dist/styles/component/ImageCropper.css +2 -2
- package/dist/styles/component/ListTile.css +1 -1
- package/dist/styles/component/Markdown.css +1 -1
- package/dist/styles/component/Menubar.css +1 -1
- package/dist/styles/component/MunicipalitySearch.css +1 -1
- package/dist/styles/component/Navbar.css +1 -0
- package/dist/styles/component/NavigationMenu.css +1 -1
- package/dist/styles/component/NavigationRail.css +1 -1
- package/dist/styles/component/NotificationCenter.css +3 -3
- package/dist/styles/component/PasswordInput.css +1 -1
- package/dist/styles/component/PinInput.css +1 -1
- package/dist/styles/component/Tour.css +2 -2
- package/dist/styles/component/TrajectoryMap.css +1 -1
- package/dist/styles/component/Transfer.css +2 -2
- package/dist/styles/component/VirtualTable.css +2 -2
- package/dist/styles/data.css +10 -10
- package/dist/styles/feedback.css +3 -3
- package/dist/styles/forms.css +6 -5
- package/dist/styles/geo.css +1 -1
- package/dist/styles/icons.css +1 -1
- package/dist/styles/inverse.css +225 -0
- package/dist/styles/navigation.css +8 -6
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.d.ts +130 -17
- package/dist/theme/create-theme.cjs +1 -1
- package/dist/theme/create-theme.cjs.map +1 -1
- package/dist/theme/create-theme.js +6 -1
- package/dist/theme/create-theme.js.map +1 -1
- package/dist/theme/inverse-render.cjs +1 -1
- package/dist/theme/inverse-render.cjs.map +1 -1
- package/dist/theme/inverse-render.js +1 -1
- package/dist/theme/inverse-render.js.map +1 -1
- package/dist/theme/inverse-restore.cjs +5 -0
- package/dist/theme/inverse-restore.cjs.map +1 -0
- package/dist/theme/inverse-restore.js +30 -0
- package/dist/theme/inverse-restore.js.map +1 -0
- package/dist/theme/inverse-surface.cjs +1 -1
- package/dist/theme/inverse-surface.cjs.map +1 -1
- package/dist/theme/inverse-surface.js +69 -72
- package/dist/theme/inverse-surface.js.map +1 -1
- package/package.json +11 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.cjs","names":[],"sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract (label, placeholder, helperText, error, disabled,\n * className) and the combobox-specific knobs: filter, emptyMessage, portal. The body\n * is the listbox — open state, active descendant, type-ahead and the ARIA wiring —\n * and those cannot be separated without passing the same active index back and\n * forth.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { CaretIcon } from \"@/components/Select/CaretIcon\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Combobox.module.css\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Custom filter — return true to keep the option. Default is case-insensitive substring match on label. */\n filter?: (option: ComboboxOption, 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 or the list changes size.\n */\n portal?: boolean;\n className?: string;\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\nfunction defaultFilter(option: ComboboxOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * Combobox — text input with a filterable dropdown of options.\n *\n * Selecting an option fires `onChange(value)`. Typing filters the list.\n * Keyboard: ArrowUp/ArrowDown to navigate, Enter to select, Esc to close.\n */\nexport function Combobox({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: ComboboxProps) {\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 selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? \"\",\n [options, value],\n );\n\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const closeAndReset = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n setActiveIndex(0);\n }, []);\n\n const handleSelect = useCallback(\n (option: ComboboxOption): void => {\n if (option.disabled) return;\n onChange(option.value);\n closeAndReset();\n },\n [onChange, closeAndReset],\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 closeAndReset();\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, closeAndReset, listNode]);\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) handleSelect(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n closeAndReset();\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\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 <li\n key={option.value}\n role=\"option\"\n aria-selected={option.value === value}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n option.value === value && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n handleSelect(option);\n }}\n >\n {option.label}\n </li>\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 ref={setFieldNode} className={styles.field}>\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={placeholder}\n disabled={disabled}\n value={open ? query : selectedLabel}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n <span className={styles.caret} aria-hidden>\n <CaretIcon />\n </span>\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":"Combobox.cjs","names":[],"sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract (label, placeholder, helperText, error, disabled,\n * className) and the combobox-specific knobs: filter, emptyMessage, portal. The body\n * is the listbox — open state, active descendant, type-ahead and the ARIA wiring —\n * and those cannot be separated without passing the same active index back and\n * forth.\n */\nimport { useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { CaretIcon } from \"@/components/Select/CaretIcon\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Combobox.module.css\";\nimport { useListboxDismiss } from \"./use-listbox-dismiss\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Custom filter — return true to keep the option. Default is case-insensitive substring match on label. */\n filter?: (option: ComboboxOption, 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 or the list changes size.\n */\n portal?: boolean;\n className?: string;\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\nfunction defaultFilter(option: ComboboxOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * Combobox — text input with a filterable dropdown of options.\n *\n * Selecting an option fires `onChange(value)`. Typing filters the list.\n * Keyboard: ArrowUp/ArrowDown to navigate — the active option is announced\n * through `aria-activedescendant`, focus stays on the input — Enter to select,\n * Esc to close, and Tab/Shift+Tab leave the field closing the list.\n */\nexport function Combobox({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: ComboboxProps) {\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 selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? \"\",\n [options, value],\n );\n\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const closeAndReset = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n setActiveIndex(0);\n }, []);\n\n const handleSelect = useCallback(\n (option: ComboboxOption): void => {\n if (option.disabled) return;\n onChange(option.value);\n closeAndReset();\n },\n [onChange, closeAndReset],\n );\n\n const { onInputBlur, onListMouseDown } = useListboxDismiss({\n open,\n rootRef,\n listNode,\n onDismiss: closeAndReset,\n });\n const optionId = (index: number): string => `${id}-option-${index}`;\n const activeOption = open ? filtered[activeIndex] : undefined;\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) handleSelect(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n closeAndReset();\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\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 <li\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={option.value === value}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n option.value === value && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={() => handleSelect(option)}\n >\n {option.label}\n </li>\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 ref={setFieldNode} className={styles.field}>\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={placeholder}\n disabled={disabled}\n value={open ? query : selectedLabel}\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 <span className={styles.caret} aria-hidden>\n <CaretIcon />\n </span>\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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"}
|
|
@@ -3,7 +3,8 @@ import { Portal as t } from "../Portal/Portal.js";
|
|
|
3
3
|
import { useAnchorPosition as n } from "../Portal/anchor-position.js";
|
|
4
4
|
import { CaretIcon as r } from "../Select/CaretIcon.js";
|
|
5
5
|
import i from "./Combobox.module.js";
|
|
6
|
-
import {
|
|
6
|
+
import { useListboxDismiss as a } from "./use-listbox-dismiss.js";
|
|
7
|
+
import { useCallback as o, useId as s, useMemo as c, useRef as l, useState as u } from "react";
|
|
7
8
|
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
8
9
|
//#region src/components/Combobox/Combobox.tsx
|
|
9
10
|
var p = 4;
|
|
@@ -23,24 +24,16 @@ function h({ options: h, value: g, onChange: _, label: v, placeholder: y = "Sele
|
|
|
23
24
|
h,
|
|
24
25
|
C,
|
|
25
26
|
M
|
|
26
|
-
]), U =
|
|
27
|
+
]), U = o(() => {
|
|
27
28
|
j(!1), N(""), F(0);
|
|
28
|
-
}, []), W =
|
|
29
|
+
}, []), W = o((e) => {
|
|
29
30
|
e.disabled || (_(e.value), U());
|
|
30
|
-
}, [_, U])
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
};
|
|
37
|
-
return window.addEventListener("mousedown", e), () => window.removeEventListener("mousedown", e);
|
|
38
|
-
}, [
|
|
39
|
-
A,
|
|
40
|
-
U,
|
|
41
|
-
R
|
|
42
|
-
]);
|
|
43
|
-
let G = (e) => {
|
|
31
|
+
}, [_, U]), { onInputBlur: G, onListMouseDown: K } = a({
|
|
32
|
+
open: A,
|
|
33
|
+
rootRef: O,
|
|
34
|
+
listNode: R,
|
|
35
|
+
onDismiss: U
|
|
36
|
+
}), q = (e) => `${D}-option-${e}`, J = A ? H[P] : void 0, Y = (e) => {
|
|
44
37
|
if (e.key === "ArrowDown") e.preventDefault(), j(!0), F((e) => Math.min(H.length - 1, e + 1));
|
|
45
38
|
else if (e.key === "ArrowUp") e.preventDefault(), F((e) => Math.max(0, e - 1));
|
|
46
39
|
else if (e.key === "Enter") {
|
|
@@ -48,23 +41,23 @@ function h({ options: h, value: g, onChange: _, label: v, placeholder: y = "Sele
|
|
|
48
41
|
let t = H[P];
|
|
49
42
|
t && W(t);
|
|
50
43
|
} else e.key === "Escape" && (A && e.preventDefault(), U());
|
|
51
|
-
},
|
|
44
|
+
}, X = /* @__PURE__ */ d("ul", {
|
|
52
45
|
ref: z,
|
|
53
46
|
id: `${D}-listbox`,
|
|
54
47
|
role: "listbox",
|
|
55
48
|
className: e(i.menu, T && i.portalled),
|
|
56
49
|
style: B,
|
|
50
|
+
onMouseDown: K,
|
|
57
51
|
children: H.length === 0 ? /* @__PURE__ */ d("li", {
|
|
58
52
|
className: i.empty,
|
|
59
53
|
children: w
|
|
60
54
|
}) : H.map((t, n) => /* @__PURE__ */ d("li", {
|
|
55
|
+
id: q(n),
|
|
61
56
|
role: "option",
|
|
62
57
|
"aria-selected": t.value === g,
|
|
63
58
|
className: e(i.option, n === P && i.active, t.value === g && i.selected),
|
|
64
59
|
onMouseEnter: () => F(n),
|
|
65
|
-
onMouseDown: (
|
|
66
|
-
e.preventDefault(), W(t);
|
|
67
|
-
},
|
|
60
|
+
onMouseDown: () => W(t),
|
|
68
61
|
children: t.label
|
|
69
62
|
}, t.value))
|
|
70
63
|
});
|
|
@@ -88,6 +81,7 @@ function h({ options: h, value: g, onChange: _, label: v, placeholder: y = "Sele
|
|
|
88
81
|
"aria-expanded": A,
|
|
89
82
|
"aria-controls": `${D}-listbox`,
|
|
90
83
|
"aria-autocomplete": "list",
|
|
84
|
+
"aria-activedescendant": J ? q(P) : void 0,
|
|
91
85
|
className: i.input,
|
|
92
86
|
placeholder: y,
|
|
93
87
|
disabled: S,
|
|
@@ -96,14 +90,15 @@ function h({ options: h, value: g, onChange: _, label: v, placeholder: y = "Sele
|
|
|
96
90
|
onChange: (e) => {
|
|
97
91
|
N(e.target.value), j(!0), F(0);
|
|
98
92
|
},
|
|
99
|
-
onKeyDown:
|
|
93
|
+
onKeyDown: Y,
|
|
94
|
+
onBlur: G
|
|
100
95
|
}), /* @__PURE__ */ d("span", {
|
|
101
96
|
className: i.caret,
|
|
102
97
|
"aria-hidden": !0,
|
|
103
98
|
children: /* @__PURE__ */ d(r, {})
|
|
104
99
|
})]
|
|
105
100
|
}),
|
|
106
|
-
A && (T ? /* @__PURE__ */ d(t, { children:
|
|
101
|
+
A && (T ? /* @__PURE__ */ d(t, { children: X }) : X),
|
|
107
102
|
x ? /* @__PURE__ */ d("span", {
|
|
108
103
|
className: i.errorText,
|
|
109
104
|
children: x
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.js","names":[],"sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract (label, placeholder, helperText, error, disabled,\n * className) and the combobox-specific knobs: filter, emptyMessage, portal. The body\n * is the listbox — open state, active descendant, type-ahead and the ARIA wiring —\n * and those cannot be separated without passing the same active index back and\n * forth.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { CaretIcon } from \"@/components/Select/CaretIcon\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Combobox.module.css\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Custom filter — return true to keep the option. Default is case-insensitive substring match on label. */\n filter?: (option: ComboboxOption, 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 or the list changes size.\n */\n portal?: boolean;\n className?: string;\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\nfunction defaultFilter(option: ComboboxOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * Combobox — text input with a filterable dropdown of options.\n *\n * Selecting an option fires `onChange(value)`. Typing filters the list.\n * Keyboard: ArrowUp/ArrowDown to navigate, Enter to select, Esc to close.\n */\nexport function Combobox({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: ComboboxProps) {\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 selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? \"\",\n [options, value],\n );\n\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const closeAndReset = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n setActiveIndex(0);\n }, []);\n\n const handleSelect = useCallback(\n (option: ComboboxOption): void => {\n if (option.disabled) return;\n onChange(option.value);\n closeAndReset();\n },\n [onChange, closeAndReset],\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 closeAndReset();\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, closeAndReset, listNode]);\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) handleSelect(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n closeAndReset();\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\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 <li\n key={option.value}\n role=\"option\"\n aria-selected={option.value === value}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n option.value === value && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n handleSelect(option);\n }}\n >\n {option.label}\n </li>\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 ref={setFieldNode} className={styles.field}>\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={placeholder}\n disabled={disabled}\n value={open ? query : selectedLabel}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n <span className={styles.caret} aria-hidden>\n <CaretIcon />\n </span>\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":"Combobox.js","names":[],"sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract (label, placeholder, helperText, error, disabled,\n * className) and the combobox-specific knobs: filter, emptyMessage, portal. The body\n * is the listbox — open state, active descendant, type-ahead and the ARIA wiring —\n * and those cannot be separated without passing the same active index back and\n * forth.\n */\nimport { useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { CaretIcon } from \"@/components/Select/CaretIcon\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Combobox.module.css\";\nimport { useListboxDismiss } from \"./use-listbox-dismiss\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Custom filter — return true to keep the option. Default is case-insensitive substring match on label. */\n filter?: (option: ComboboxOption, 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 or the list changes size.\n */\n portal?: boolean;\n className?: string;\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\nfunction defaultFilter(option: ComboboxOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * Combobox — text input with a filterable dropdown of options.\n *\n * Selecting an option fires `onChange(value)`. Typing filters the list.\n * Keyboard: ArrowUp/ArrowDown to navigate — the active option is announced\n * through `aria-activedescendant`, focus stays on the input — Enter to select,\n * Esc to close, and Tab/Shift+Tab leave the field closing the list.\n */\nexport function Combobox({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: ComboboxProps) {\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 selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? \"\",\n [options, value],\n );\n\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const closeAndReset = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n setActiveIndex(0);\n }, []);\n\n const handleSelect = useCallback(\n (option: ComboboxOption): void => {\n if (option.disabled) return;\n onChange(option.value);\n closeAndReset();\n },\n [onChange, closeAndReset],\n );\n\n const { onInputBlur, onListMouseDown } = useListboxDismiss({\n open,\n rootRef,\n listNode,\n onDismiss: closeAndReset,\n });\n const optionId = (index: number): string => `${id}-option-${index}`;\n const activeOption = open ? filtered[activeIndex] : undefined;\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) handleSelect(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n closeAndReset();\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\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 <li\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={option.value === value}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n option.value === value && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={() => handleSelect(option)}\n >\n {option.label}\n </li>\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 ref={setFieldNode} className={styles.field}>\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={placeholder}\n disabled={disabled}\n value={open ? query : selectedLabel}\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 <span className={styles.caret} aria-hidden>\n <CaretIcon />\n </span>\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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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require("react");function t(e){e.preventDefault()}function n({open:n,rootRef:r,listNode:i,onDismiss:a}){return(0,e.useEffect)(()=>{if(!n)return;let e=e=>{let t=e.target;r.current?.contains(t)||i?.contains(t)||a()};return window.addEventListener(`mousedown`,e),()=>window.removeEventListener(`mousedown`,e)},[n,r,i,a]),{onInputBlur:(0,e.useCallback)(e=>{let t=e.relatedTarget;n&&t instanceof Node&&!i?.contains(t)&&a()},[n,i,a]),onListMouseDown:t}}exports.useListboxDismiss=n;
|
|
2
|
+
//# sourceMappingURL=use-listbox-dismiss.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-listbox-dismiss.cjs","names":[],"sources":["../../../src/components/Combobox/use-listbox-dismiss.ts"],"sourcesContent":["import { useCallback, useEffect, type FocusEvent, type MouseEvent, type RefObject } from \"react\";\n\n/** Inputs of {@link useListboxDismiss}. */\nexport interface ListboxDismissOptions {\n /** Whether the list is open; nothing is listened to while it is closed. */\n open: boolean;\n /** The component's wrapper — a press inside it is not a press outside. */\n rootRef: RefObject<HTMLElement | null>;\n /** The list node, checked on its own because in a portal it lives outside `rootRef`. */\n listNode: HTMLElement | null;\n /** Closes the list. Keep it stable (`useCallback`), it is an effect dependency. */\n onDismiss: () => void;\n}\n\n/** Handlers {@link useListboxDismiss} hands back to wire on the input and the list. */\nexport interface ListboxDismissHandlers {\n /** `onBlur` of the combobox input. */\n onInputBlur: (event: FocusEvent<HTMLInputElement>) => void;\n /** `onMouseDown` of the list element. */\n onListMouseDown: (event: MouseEvent<HTMLElement>) => void;\n}\n\n/**\n * Keeps focus on the input when a press lands anywhere on the list — an option,\n * the empty message, the padding or the scrollbar.\n *\n * @param event - The `mousedown` on the list.\n */\nfunction keepInputFocus(event: MouseEvent<HTMLElement>): void {\n event.preventDefault();\n}\n\n/**\n * Closes the list of an input-driven listbox (`Combobox`, `MultiSelect`) when the\n * user leaves it, by pointer or by keyboard.\n *\n * Pointer: a press outside both the wrapper and the list closes it. The list is\n * checked on its own because in a portal it is not inside the wrapper.\n *\n * Keyboard: the list closes when focus moves from the input to an element outside\n * the list. Before this (#430, measured in Chromium headless on `main`), `Tab` and\n * `Shift+Tab` took focus to the neighbouring control and left the list open on top\n * of it — `aria-expanded=\"true\"` on a field that no longer had focus — until a\n * press outside or `Escape` on the input, which no longer received keys. A blur\n * whose `relatedTarget` is `null` (focus went to a non-focusable spot or out of\n * the page) is left to the pointer rule, so a press on the label or the helper\n * text keeps behaving as a press inside.\n *\n * A press on the list calls `preventDefault()`, so it never blurs the input: the\n * option's handler runs with focus still on the field, and a press on the empty\n * message or the scrollbar no longer drops focus to `<body>`.\n *\n * @param options - See {@link ListboxDismissOptions}.\n * @returns The input `onBlur` and the list `onMouseDown` handlers.\n */\nexport function useListboxDismiss({\n open,\n rootRef,\n listNode,\n onDismiss,\n}: ListboxDismissOptions): ListboxDismissHandlers {\n useEffect(() => {\n if (!open) return;\n const onDown = (event: globalThis.MouseEvent): void => {\n const target = event.target as Node;\n if (rootRef.current?.contains(target) || listNode?.contains(target)) return;\n onDismiss();\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, rootRef, listNode, onDismiss]);\n\n const onInputBlur = useCallback(\n (event: FocusEvent<HTMLInputElement>): void => {\n const next = event.relatedTarget;\n if (!open || !(next instanceof Node) || listNode?.contains(next)) return;\n onDismiss();\n },\n [open, listNode, onDismiss],\n );\n\n return { onInputBlur, onListMouseDown: keepInputFocus };\n}\n"],"mappings":"uBA4BA,SAAS,EAAe,EAAsC,CAC1D,EAAM,eAAe,CACzB,CAyBA,SAAgB,EAAkB,CAC9B,OACA,UACA,WACA,aAC8C,CAqB9C,OApBA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAU,GAAuC,CACnD,IAAM,EAAS,EAAM,OACjB,EAAQ,SAAS,SAAS,CAAM,GAAK,GAAU,SAAS,CAAM,GAClE,EAAU,CACd,EAEA,OADA,OAAO,iBAAiB,YAAa,CAAM,MAC9B,OAAO,oBAAoB,YAAa,CAAM,CAC/D,EAAG,CAAC,EAAM,EAAS,EAAU,CAAS,CAAC,EAWhC,CAAE,aAAA,EATW,EAAA,YAAA,CACf,GAA8C,CAC3C,IAAM,EAAO,EAAM,cACd,GAAU,aAAgB,MAAS,IAAU,SAAS,CAAI,GAC/D,EAAU,CACd,EACA,CAAC,EAAM,EAAU,CAAS,CAGrB,EAAa,gBAAiB,CAAe,CAC1D"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useCallback as e, useEffect as t } from "react";
|
|
2
|
+
//#region src/components/Combobox/use-listbox-dismiss.ts
|
|
3
|
+
function n(e) {
|
|
4
|
+
e.preventDefault();
|
|
5
|
+
}
|
|
6
|
+
function r({ open: r, rootRef: i, listNode: a, onDismiss: o }) {
|
|
7
|
+
return t(() => {
|
|
8
|
+
if (!r) return;
|
|
9
|
+
let e = (e) => {
|
|
10
|
+
let t = e.target;
|
|
11
|
+
i.current?.contains(t) || a?.contains(t) || o();
|
|
12
|
+
};
|
|
13
|
+
return window.addEventListener("mousedown", e), () => window.removeEventListener("mousedown", e);
|
|
14
|
+
}, [
|
|
15
|
+
r,
|
|
16
|
+
i,
|
|
17
|
+
a,
|
|
18
|
+
o
|
|
19
|
+
]), {
|
|
20
|
+
onInputBlur: e((e) => {
|
|
21
|
+
let t = e.relatedTarget;
|
|
22
|
+
r && t instanceof Node && !a?.contains(t) && o();
|
|
23
|
+
}, [
|
|
24
|
+
r,
|
|
25
|
+
a,
|
|
26
|
+
o
|
|
27
|
+
]),
|
|
28
|
+
onListMouseDown: n
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { r as useListboxDismiss };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=use-listbox-dismiss.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-listbox-dismiss.js","names":[],"sources":["../../../src/components/Combobox/use-listbox-dismiss.ts"],"sourcesContent":["import { useCallback, useEffect, type FocusEvent, type MouseEvent, type RefObject } from \"react\";\n\n/** Inputs of {@link useListboxDismiss}. */\nexport interface ListboxDismissOptions {\n /** Whether the list is open; nothing is listened to while it is closed. */\n open: boolean;\n /** The component's wrapper — a press inside it is not a press outside. */\n rootRef: RefObject<HTMLElement | null>;\n /** The list node, checked on its own because in a portal it lives outside `rootRef`. */\n listNode: HTMLElement | null;\n /** Closes the list. Keep it stable (`useCallback`), it is an effect dependency. */\n onDismiss: () => void;\n}\n\n/** Handlers {@link useListboxDismiss} hands back to wire on the input and the list. */\nexport interface ListboxDismissHandlers {\n /** `onBlur` of the combobox input. */\n onInputBlur: (event: FocusEvent<HTMLInputElement>) => void;\n /** `onMouseDown` of the list element. */\n onListMouseDown: (event: MouseEvent<HTMLElement>) => void;\n}\n\n/**\n * Keeps focus on the input when a press lands anywhere on the list — an option,\n * the empty message, the padding or the scrollbar.\n *\n * @param event - The `mousedown` on the list.\n */\nfunction keepInputFocus(event: MouseEvent<HTMLElement>): void {\n event.preventDefault();\n}\n\n/**\n * Closes the list of an input-driven listbox (`Combobox`, `MultiSelect`) when the\n * user leaves it, by pointer or by keyboard.\n *\n * Pointer: a press outside both the wrapper and the list closes it. The list is\n * checked on its own because in a portal it is not inside the wrapper.\n *\n * Keyboard: the list closes when focus moves from the input to an element outside\n * the list. Before this (#430, measured in Chromium headless on `main`), `Tab` and\n * `Shift+Tab` took focus to the neighbouring control and left the list open on top\n * of it — `aria-expanded=\"true\"` on a field that no longer had focus — until a\n * press outside or `Escape` on the input, which no longer received keys. A blur\n * whose `relatedTarget` is `null` (focus went to a non-focusable spot or out of\n * the page) is left to the pointer rule, so a press on the label or the helper\n * text keeps behaving as a press inside.\n *\n * A press on the list calls `preventDefault()`, so it never blurs the input: the\n * option's handler runs with focus still on the field, and a press on the empty\n * message or the scrollbar no longer drops focus to `<body>`.\n *\n * @param options - See {@link ListboxDismissOptions}.\n * @returns The input `onBlur` and the list `onMouseDown` handlers.\n */\nexport function useListboxDismiss({\n open,\n rootRef,\n listNode,\n onDismiss,\n}: ListboxDismissOptions): ListboxDismissHandlers {\n useEffect(() => {\n if (!open) return;\n const onDown = (event: globalThis.MouseEvent): void => {\n const target = event.target as Node;\n if (rootRef.current?.contains(target) || listNode?.contains(target)) return;\n onDismiss();\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, rootRef, listNode, onDismiss]);\n\n const onInputBlur = useCallback(\n (event: FocusEvent<HTMLInputElement>): void => {\n const next = event.relatedTarget;\n if (!open || !(next instanceof Node) || listNode?.contains(next)) return;\n onDismiss();\n },\n [open, listNode, onDismiss],\n );\n\n return { onInputBlur, onListMouseDown: keepInputFocus };\n}\n"],"mappings":";;AA4BA,SAAS,EAAe,GAAsC;CAC1D,EAAM,eAAe;AACzB;AAyBA,SAAgB,EAAkB,EAC9B,SACA,YACA,aACA,gBAC8C;CAqB9C,OApBA,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,KAAU,MAAuC;GACnD,IAAM,IAAS,EAAM;GACjB,EAAQ,SAAS,SAAS,CAAM,KAAK,GAAU,SAAS,CAAM,KAClE,EAAU;EACd;EAEA,OADA,OAAO,iBAAiB,aAAa,CAAM,SAC9B,OAAO,oBAAoB,aAAa,CAAM;CAC/D,GAAG;EAAC;EAAM;EAAS;EAAU;CAAS,CAAC,GAWhC;EAAE,aATW,GACf,MAA8C;GAC3C,IAAM,IAAO,EAAM;GACf,AAAC,KAAU,aAAgB,QAAS,IAAU,SAAS,CAAI,KAC/D,EAAU;EACd,GACA;GAAC;GAAM;GAAU;EAAS,CAGrB;EAAa,iBAAiB;CAAe;AAC1D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropzone.module.cjs","names":[],"sources":["../../../src/components/Dropzone/Dropzone.module.css"],"sourcesContent":[".zone {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n min-height: 120px;\n padding: var(--tempest-space-6, 24px);\n border: 2px dashed var(--tempest-border);\n border-radius: var(--tempest-radius-lg, 12px);\n background: var(--tempest-surface);\n color: var(--tempest-text-muted);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n cursor: pointer;\n transition:\n border-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.zone:hover,\n.zone:focus-visible {\n border-color: var(--tempest-primary);\n color: var(--tempest-text);\n outline:
|
|
1
|
+
{"version":3,"file":"Dropzone.module.cjs","names":[],"sources":["../../../src/components/Dropzone/Dropzone.module.css"],"sourcesContent":[".zone {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n min-height: 120px;\n padding: var(--tempest-space-6, 24px);\n border: 2px dashed var(--tempest-border);\n border-radius: var(--tempest-radius-lg, 12px);\n background: var(--tempest-surface);\n color: var(--tempest-text-muted);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n cursor: pointer;\n transition:\n border-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.zone:hover,\n.zone:focus-visible {\n border-color: var(--tempest-primary);\n color: var(--tempest-text);\n}\n\n.zone: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.dragging {\n border-color: var(--tempest-primary);\n background: var(--tempest-primary-soft);\n color: var(--tempest-text);\n}\n\n.disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.input {\n display: none;\n}\n\n.prompt {\n line-height: 1.4;\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropzone.module.js","names":[],"sources":["../../../src/components/Dropzone/Dropzone.module.css"],"sourcesContent":[".zone {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n min-height: 120px;\n padding: var(--tempest-space-6, 24px);\n border: 2px dashed var(--tempest-border);\n border-radius: var(--tempest-radius-lg, 12px);\n background: var(--tempest-surface);\n color: var(--tempest-text-muted);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n cursor: pointer;\n transition:\n border-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.zone:hover,\n.zone:focus-visible {\n border-color: var(--tempest-primary);\n color: var(--tempest-text);\n outline:
|
|
1
|
+
{"version":3,"file":"Dropzone.module.js","names":[],"sources":["../../../src/components/Dropzone/Dropzone.module.css"],"sourcesContent":[".zone {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n min-height: 120px;\n padding: var(--tempest-space-6, 24px);\n border: 2px dashed var(--tempest-border);\n border-radius: var(--tempest-radius-lg, 12px);\n background: var(--tempest-surface);\n color: var(--tempest-text-muted);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n cursor: pointer;\n transition:\n border-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.zone:hover,\n.zone:focus-visible {\n border-color: var(--tempest-primary);\n color: var(--tempest-text);\n}\n\n.zone: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.dragging {\n border-color: var(--tempest-primary);\n background: var(--tempest-primary-soft);\n color: var(--tempest-text);\n}\n\n.disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.input {\n display: none;\n}\n\n.prompt {\n line-height: 1.4;\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterBar.module.cjs","names":[],"sources":["../../../src/components/FilterBar/FilterBar.module.css"],"sourcesContent":[".bar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.chips {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-1) var(--tempest-space-1) var(--tempest-space-1)\n var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.empty {\n color: var(--tempest-text-muted);\n}\n\n.remove {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 1.25rem;\n height: 1.25rem;\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.remove:hover {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text);\n}\n\n/*\n * The editor wraps as a whole on a narrow screen: field, condition and value each\n * keep a usable width instead of shrinking into three unreadable boxes.\n */\n.editor {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-surface);\n}\n\n.control {\n display: inline-flex;\n min-width: 0;\n}\n\n.select,\n.input {\n min-width: 7rem;\n max-width: 14rem;\n padding: var(--tempest-space-1) var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-bg);\n color: inherit;\n font: inherit;\n}\n\n.select:focus-visible,\n.input:focus-visible {\n outline:
|
|
1
|
+
{"version":3,"file":"FilterBar.module.cjs","names":[],"sources":["../../../src/components/FilterBar/FilterBar.module.css"],"sourcesContent":[".bar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.chips {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-1) var(--tempest-space-1) var(--tempest-space-1)\n var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.empty {\n color: var(--tempest-text-muted);\n}\n\n.remove {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 1.25rem;\n height: 1.25rem;\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.remove:hover {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text);\n}\n\n/*\n * The editor wraps as a whole on a narrow screen: field, condition and value each\n * keep a usable width instead of shrinking into three unreadable boxes.\n */\n.editor {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-surface);\n}\n\n.control {\n display: inline-flex;\n min-width: 0;\n}\n\n.select,\n.input {\n min-width: 7rem;\n max-width: 14rem;\n padding: var(--tempest-space-1) var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-bg);\n color: inherit;\n font: inherit;\n}\n\n.select:focus-visible,\n.input:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: calc(var(--tempest-focus-ring-width) * -1);\n}\n\n.actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n}\n\n.primary,\n.secondary,\n.ghost {\n padding: var(--tempest-space-1) var(--tempest-space-3);\n border-radius: var(--tempest-radius-md);\n font: inherit;\n cursor: pointer;\n}\n\n.primary {\n border: none;\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.primary:disabled {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n.secondary {\n border: 1px dashed var(--tempest-border-strong);\n background-color: transparent;\n color: var(--tempest-text);\n}\n\n.secondary:disabled {\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n.ghost {\n border: none;\n background: none;\n color: var(--tempest-text-muted);\n text-decoration: underline;\n}\n\n.primary:focus-visible,\n.secondary:focus-visible,\n.ghost:focus-visible,\n.remove:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: 1px;\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterBar.module.js","names":[],"sources":["../../../src/components/FilterBar/FilterBar.module.css"],"sourcesContent":[".bar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.chips {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-1) var(--tempest-space-1) var(--tempest-space-1)\n var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.empty {\n color: var(--tempest-text-muted);\n}\n\n.remove {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 1.25rem;\n height: 1.25rem;\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.remove:hover {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text);\n}\n\n/*\n * The editor wraps as a whole on a narrow screen: field, condition and value each\n * keep a usable width instead of shrinking into three unreadable boxes.\n */\n.editor {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-surface);\n}\n\n.control {\n display: inline-flex;\n min-width: 0;\n}\n\n.select,\n.input {\n min-width: 7rem;\n max-width: 14rem;\n padding: var(--tempest-space-1) var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-bg);\n color: inherit;\n font: inherit;\n}\n\n.select:focus-visible,\n.input:focus-visible {\n outline:
|
|
1
|
+
{"version":3,"file":"FilterBar.module.js","names":[],"sources":["../../../src/components/FilterBar/FilterBar.module.css"],"sourcesContent":[".bar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.chips {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-1) var(--tempest-space-1) var(--tempest-space-1)\n var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.empty {\n color: var(--tempest-text-muted);\n}\n\n.remove {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 1.25rem;\n height: 1.25rem;\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.remove:hover {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text);\n}\n\n/*\n * The editor wraps as a whole on a narrow screen: field, condition and value each\n * keep a usable width instead of shrinking into three unreadable boxes.\n */\n.editor {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-surface);\n}\n\n.control {\n display: inline-flex;\n min-width: 0;\n}\n\n.select,\n.input {\n min-width: 7rem;\n max-width: 14rem;\n padding: var(--tempest-space-1) var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-bg);\n color: inherit;\n font: inherit;\n}\n\n.select:focus-visible,\n.input:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: calc(var(--tempest-focus-ring-width) * -1);\n}\n\n.actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n}\n\n.primary,\n.secondary,\n.ghost {\n padding: var(--tempest-space-1) var(--tempest-space-3);\n border-radius: var(--tempest-radius-md);\n font: inherit;\n cursor: pointer;\n}\n\n.primary {\n border: none;\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.primary:disabled {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n.secondary {\n border: 1px dashed var(--tempest-border-strong);\n background-color: transparent;\n color: var(--tempest-text);\n}\n\n.secondary:disabled {\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n.ghost {\n border: none;\n background: none;\n color: var(--tempest-text-muted);\n text-decoration: underline;\n}\n\n.primary:focus-visible,\n.secondary:focus-visible,\n.ghost:focus-visible,\n.remove:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: 1px;\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FloatingActionButton.module.cjs","names":[],"sources":["../../../src/components/FloatingActionButton/FloatingActionButton.module.css"],"sourcesContent":[".fab {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n border: 0;\n cursor: pointer;\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n box-shadow: var(--tempest-shadow-lg);\n transition:\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n box-shadow var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n transform var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.fab:active {\n transform: scale(0.96);\n}\n\n.fab:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color,
|
|
1
|
+
{"version":3,"file":"FloatingActionButton.module.cjs","names":[],"sources":["../../../src/components/FloatingActionButton/FloatingActionButton.module.css"],"sourcesContent":[".fab {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n border: 0;\n cursor: pointer;\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n box-shadow: var(--tempest-shadow-lg);\n transition:\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n box-shadow var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n transform var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.fab:active {\n transform: scale(0.96);\n}\n\n.fab: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.fab:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n box-shadow: var(--tempest-shadow-sm);\n}\n\n/* Variants */\n.primary {\n background: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.primary:hover:not(:disabled) {\n background: var(--tempest-primary-active, var(--tempest-primary));\n}\n\n.surface {\n background: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.surface:hover:not(:disabled) {\n background: var(--tempest-surface-2);\n}\n\n/* Round (icon-only) sizing */\n.round.sm {\n width: 40px;\n height: 40px;\n border-radius: var(--tempest-radius-lg);\n}\n\n.round.md {\n width: 56px;\n height: 56px;\n border-radius: var(--tempest-radius-lg);\n}\n\n.round.lg {\n width: 72px;\n height: 72px;\n border-radius: var(--tempest-radius-xl);\n}\n\n/* Extended (pill) sizing */\n.extended {\n border-radius: var(--tempest-radius-full);\n}\n\n.extended.sm {\n height: 40px;\n padding: 0 var(--tempest-space-4);\n font-size: var(--tempest-text-sm);\n}\n\n.extended.md {\n height: 56px;\n padding: 0 var(--tempest-space-5);\n font-size: var(--tempest-text-md);\n}\n\n.extended.lg {\n height: 72px;\n padding: 0 var(--tempest-space-6);\n font-size: var(--tempest-text-lg);\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.round.sm .icon {\n font-size: 20px;\n}\n\n.round.lg .icon {\n font-size: 32px;\n}\n\n.label {\n line-height: 1;\n}\n\n/* Positioning */\n.fixed {\n position: fixed;\n z-index: var(--tempest-z-dropdown, 1000);\n}\n\n.bottomRight {\n right: var(--tempest-space-4);\n bottom: calc(var(--tempest-space-4) + env(safe-area-inset-bottom, 0));\n}\n\n.bottomLeft {\n left: var(--tempest-space-4);\n bottom: calc(var(--tempest-space-4) + env(safe-area-inset-bottom, 0));\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FloatingActionButton.module.js","names":[],"sources":["../../../src/components/FloatingActionButton/FloatingActionButton.module.css"],"sourcesContent":[".fab {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n border: 0;\n cursor: pointer;\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n box-shadow: var(--tempest-shadow-lg);\n transition:\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n box-shadow var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n transform var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.fab:active {\n transform: scale(0.96);\n}\n\n.fab:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color,
|
|
1
|
+
{"version":3,"file":"FloatingActionButton.module.js","names":[],"sources":["../../../src/components/FloatingActionButton/FloatingActionButton.module.css"],"sourcesContent":[".fab {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n border: 0;\n cursor: pointer;\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n box-shadow: var(--tempest-shadow-lg);\n transition:\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n box-shadow var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n transform var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.fab:active {\n transform: scale(0.96);\n}\n\n.fab: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.fab:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n box-shadow: var(--tempest-shadow-sm);\n}\n\n/* Variants */\n.primary {\n background: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.primary:hover:not(:disabled) {\n background: var(--tempest-primary-active, var(--tempest-primary));\n}\n\n.surface {\n background: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.surface:hover:not(:disabled) {\n background: var(--tempest-surface-2);\n}\n\n/* Round (icon-only) sizing */\n.round.sm {\n width: 40px;\n height: 40px;\n border-radius: var(--tempest-radius-lg);\n}\n\n.round.md {\n width: 56px;\n height: 56px;\n border-radius: var(--tempest-radius-lg);\n}\n\n.round.lg {\n width: 72px;\n height: 72px;\n border-radius: var(--tempest-radius-xl);\n}\n\n/* Extended (pill) sizing */\n.extended {\n border-radius: var(--tempest-radius-full);\n}\n\n.extended.sm {\n height: 40px;\n padding: 0 var(--tempest-space-4);\n font-size: var(--tempest-text-sm);\n}\n\n.extended.md {\n height: 56px;\n padding: 0 var(--tempest-space-5);\n font-size: var(--tempest-text-md);\n}\n\n.extended.lg {\n height: 72px;\n padding: 0 var(--tempest-space-6);\n font-size: var(--tempest-text-lg);\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.round.sm .icon {\n font-size: 20px;\n}\n\n.round.lg .icon {\n font-size: 32px;\n}\n\n.label {\n line-height: 1;\n}\n\n/* Positioning */\n.fixed {\n position: fixed;\n z-index: var(--tempest-z-dropdown, 1000);\n}\n\n.bottomRight {\n right: var(--tempest-space-4);\n bottom: calc(var(--tempest-space-4) + env(safe-area-inset-bottom, 0));\n}\n\n.bottomLeft {\n left: var(--tempest-space-4);\n bottom: calc(var(--tempest-space-4) + env(safe-area-inset-bottom, 0));\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageCropper.module.cjs","names":[],"sources":["../../../src/components/ImageCropper/ImageCropper.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-3);\n max-width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.frame {\n position: relative;\n width: 100%;\n overflow: hidden;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface);\n /*\n * `grab` rather than `move`: nothing is being moved to a destination, the\n * image is being dragged under a fixed frame.\n */\n cursor: grab;\n /*\n * The frame owns dragging on touch, so the browser must not also scroll the\n * page from the same gesture.\n */\n touch-action: none;\n user-select: none;\n}\n\n.frame:active {\n cursor: grabbing;\n}\n\n.frame:focus-visible {\n outline:
|
|
1
|
+
{"version":3,"file":"ImageCropper.module.cjs","names":[],"sources":["../../../src/components/ImageCropper/ImageCropper.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-3);\n max-width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.frame {\n position: relative;\n width: 100%;\n overflow: hidden;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface);\n /*\n * `grab` rather than `move`: nothing is being moved to a destination, the\n * image is being dragged under a fixed frame.\n */\n cursor: grab;\n /*\n * The frame owns dragging on touch, so the browser must not also scroll the\n * page from the same gesture.\n */\n touch-action: none;\n user-select: none;\n}\n\n.frame:active {\n cursor: grabbing;\n}\n\n.frame: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.circle {\n border-radius: var(--tempest-radius-full);\n}\n\n.image {\n position: absolute;\n top: 50%;\n left: 50%;\n /*\n * Both `max-*`, not just the width. The size is set inline from the zoom, so\n * either cap silently overrides it — and an app reset carrying\n * `img { max-height: 100% }` is enough, since a 0-0-1 element rule meets no\n * competition here. The failure is worse than it looks: `crop()` builds its\n * rectangle from `computeCropRect({ image: natural, ... })` and never reads the\n * rendered element, so the export stays correct while the preview is squashed\n * on one axis. The user drags against a distorted image and gets a framing they\n * never saw. A component that sizes itself inline owns neutralising both caps.\n */\n max-width: none;\n max-height: none;\n pointer-events: none;\n /*\n * Centring lives in `translate` and the pan in the inline `transform`, which\n * are separate CSS properties applied in that order. Keeping them apart means\n * a pan is just `transform: translate(x, y)` — it never has to re-derive the\n * `-50% -50%` centring, and the two cannot overwrite each other.\n */\n translate: -50% -50%;\n}\n\n.controls {\n display: flex;\n /*\n * Wraps rather than squeezing. The zoom slider is the flexible item, so on a\n * narrow row it is the one that gives — and a range input has no useful floor\n * of its own, so it gave all the way to `width: 0`, leaving the thumb alone and\n * no visible focus ring while the button beside it kept both. Measured at 390px\n * with a crowded row. Wrapping moves the slider to its own line instead.\n */\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-3);\n}\n\n.zoom {\n /*\n * `min-width: 0` is what let it collapse to nothing: it exists to let a flex\n * item shrink past its min-content size, which for a range input is the whole\n * control. A real floor keeps the slider draggable and its focus ring visible;\n * the wrap on `.controls` is what absorbs the width this refuses to give up.\n */\n flex: 1 1 6rem;\n min-width: 6rem;\n accent-color: var(--tempest-primary);\n}\n\n.zoom:disabled {\n opacity: 0.5;\n}\n\n.reset {\n padding: var(--tempest-space-1) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font: inherit;\n font-size: var(--tempest-text-sm);\n cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .reset:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.reset: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.reset:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.hint {\n margin: 0;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n.hint kbd {\n padding: 0 var(--tempest-space-1);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-surface);\n font-family: var(--tempest-font-mono, monospace);\n font-size: inherit;\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageCropper.module.js","names":[],"sources":["../../../src/components/ImageCropper/ImageCropper.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-3);\n max-width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.frame {\n position: relative;\n width: 100%;\n overflow: hidden;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface);\n /*\n * `grab` rather than `move`: nothing is being moved to a destination, the\n * image is being dragged under a fixed frame.\n */\n cursor: grab;\n /*\n * The frame owns dragging on touch, so the browser must not also scroll the\n * page from the same gesture.\n */\n touch-action: none;\n user-select: none;\n}\n\n.frame:active {\n cursor: grabbing;\n}\n\n.frame:focus-visible {\n outline:
|
|
1
|
+
{"version":3,"file":"ImageCropper.module.js","names":[],"sources":["../../../src/components/ImageCropper/ImageCropper.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-3);\n max-width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.frame {\n position: relative;\n width: 100%;\n overflow: hidden;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface);\n /*\n * `grab` rather than `move`: nothing is being moved to a destination, the\n * image is being dragged under a fixed frame.\n */\n cursor: grab;\n /*\n * The frame owns dragging on touch, so the browser must not also scroll the\n * page from the same gesture.\n */\n touch-action: none;\n user-select: none;\n}\n\n.frame:active {\n cursor: grabbing;\n}\n\n.frame: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.circle {\n border-radius: var(--tempest-radius-full);\n}\n\n.image {\n position: absolute;\n top: 50%;\n left: 50%;\n /*\n * Both `max-*`, not just the width. The size is set inline from the zoom, so\n * either cap silently overrides it — and an app reset carrying\n * `img { max-height: 100% }` is enough, since a 0-0-1 element rule meets no\n * competition here. The failure is worse than it looks: `crop()` builds its\n * rectangle from `computeCropRect({ image: natural, ... })` and never reads the\n * rendered element, so the export stays correct while the preview is squashed\n * on one axis. The user drags against a distorted image and gets a framing they\n * never saw. A component that sizes itself inline owns neutralising both caps.\n */\n max-width: none;\n max-height: none;\n pointer-events: none;\n /*\n * Centring lives in `translate` and the pan in the inline `transform`, which\n * are separate CSS properties applied in that order. Keeping them apart means\n * a pan is just `transform: translate(x, y)` — it never has to re-derive the\n * `-50% -50%` centring, and the two cannot overwrite each other.\n */\n translate: -50% -50%;\n}\n\n.controls {\n display: flex;\n /*\n * Wraps rather than squeezing. The zoom slider is the flexible item, so on a\n * narrow row it is the one that gives — and a range input has no useful floor\n * of its own, so it gave all the way to `width: 0`, leaving the thumb alone and\n * no visible focus ring while the button beside it kept both. Measured at 390px\n * with a crowded row. Wrapping moves the slider to its own line instead.\n */\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-3);\n}\n\n.zoom {\n /*\n * `min-width: 0` is what let it collapse to nothing: it exists to let a flex\n * item shrink past its min-content size, which for a range input is the whole\n * control. A real floor keeps the slider draggable and its focus ring visible;\n * the wrap on `.controls` is what absorbs the width this refuses to give up.\n */\n flex: 1 1 6rem;\n min-width: 6rem;\n accent-color: var(--tempest-primary);\n}\n\n.zoom:disabled {\n opacity: 0.5;\n}\n\n.reset {\n padding: var(--tempest-space-1) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font: inherit;\n font-size: var(--tempest-text-sm);\n cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .reset:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.reset: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.reset:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.hint {\n margin: 0;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n.hint kbd {\n padding: 0 var(--tempest-space-1);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-surface);\n font-family: var(--tempest-font-mono, monospace);\n font-size: inherit;\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListTile.module.cjs","names":[],"sources":["../../../src/components/ListTile/ListTile.module.css"],"sourcesContent":[".tile {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n width: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n background: var(--tempest-surface);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n text-align: left;\n border: 0;\n border-radius: var(--tempest-radius-md);\n}\n\n.interactive {\n cursor: pointer;\n transition: background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.interactive:hover {\n background: var(--tempest-surface-2);\n}\n\n.interactive:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color,
|
|
1
|
+
{"version":3,"file":"ListTile.module.cjs","names":[],"sources":["../../../src/components/ListTile/ListTile.module.css"],"sourcesContent":[".tile {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n width: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n background: var(--tempest-surface);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n text-align: left;\n border: 0;\n border-radius: var(--tempest-radius-md);\n}\n\n.interactive {\n cursor: pointer;\n transition: background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.interactive:hover {\n background: var(--tempest-surface-2);\n}\n\n.interactive: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.selected {\n background: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n.interactive.selected:hover {\n background: var(--tempest-primary-soft-hover, var(--tempest-primary-soft));\n}\n\n.disabled {\n opacity: 0.4;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.leading,\n.trailing {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n line-height: 1;\n}\n\n.trailing {\n margin-left: auto;\n color: var(--tempest-text-muted);\n}\n\n.body {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n flex: 1 1 auto;\n}\n\n.title {\n line-height: var(--tempest-leading-snug, 1.3);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.subtitle {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n line-height: var(--tempest-leading-snug, 1.3);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.selected .subtitle {\n color: var(--tempest-primary);\n opacity: 0.8;\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListTile.module.js","names":[],"sources":["../../../src/components/ListTile/ListTile.module.css"],"sourcesContent":[".tile {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n width: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n background: var(--tempest-surface);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n text-align: left;\n border: 0;\n border-radius: var(--tempest-radius-md);\n}\n\n.interactive {\n cursor: pointer;\n transition: background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.interactive:hover {\n background: var(--tempest-surface-2);\n}\n\n.interactive:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color,
|
|
1
|
+
{"version":3,"file":"ListTile.module.js","names":[],"sources":["../../../src/components/ListTile/ListTile.module.css"],"sourcesContent":[".tile {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n width: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n background: var(--tempest-surface);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n text-align: left;\n border: 0;\n border-radius: var(--tempest-radius-md);\n}\n\n.interactive {\n cursor: pointer;\n transition: background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.interactive:hover {\n background: var(--tempest-surface-2);\n}\n\n.interactive: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.selected {\n background: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n.interactive.selected:hover {\n background: var(--tempest-primary-soft-hover, var(--tempest-primary-soft));\n}\n\n.disabled {\n opacity: 0.4;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.leading,\n.trailing {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n line-height: 1;\n}\n\n.trailing {\n margin-left: auto;\n color: var(--tempest-text-muted);\n}\n\n.body {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n flex: 1 1 auto;\n}\n\n.title {\n line-height: var(--tempest-leading-snug, 1.3);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.subtitle {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n line-height: var(--tempest-leading-snug, 1.3);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.selected .subtitle {\n color: var(--tempest-primary);\n opacity: 0.8;\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Markdown.module.cjs","names":[],"sources":["../../../src/components/Markdown/Markdown.module.css"],"sourcesContent":[".root {\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-relaxed);\n overflow-wrap: anywhere;\n}\n\n/*\n * Every block spaces itself with `margin-block-end` and the last child clears it,\n * so a Markdown body dropped into a card does not add a phantom gap at the bottom.\n */\n.root > * {\n margin-block: 0 var(--tempest-space-3);\n}\n\n.root > *:last-child {\n margin-block-end: 0;\n}\n\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n margin-block-start: var(--tempest-space-5);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-tight);\n}\n\n.root > .h1:first-child,\n.root > .h2:first-child,\n.root > .h3:first-child,\n.root > .h4:first-child,\n.root > .h5:first-child,\n.root > .h6:first-child {\n margin-block-start: 0;\n}\n\n.h1 {\n font-size: var(--tempest-text-2xl);\n}\n\n.h2 {\n font-size: var(--tempest-text-xl);\n}\n\n.h3 {\n font-size: var(--tempest-text-lg);\n}\n\n.h4,\n.h5,\n.h6 {\n font-size: var(--tempest-text-base);\n}\n\n.list {\n padding-inline-start: var(--tempest-space-6);\n}\n\n.list .list {\n margin-block: var(--tempest-space-1) 0;\n}\n\n.item {\n margin-block-end: var(--tempest-space-1);\n}\n\n.item > p {\n margin: 0;\n}\n\n.quote {\n padding-inline-start: var(--tempest-space-4);\n border-inline-start: 3px solid var(--tempest-border-strong);\n color: var(--tempest-text-muted);\n}\n\n.rule {\n height: 1px;\n border: 0;\n background-color: var(--tempest-border);\n}\n\n.inlineCode {\n padding: 0.1em 0.35em;\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n font-family: var(--tempest-font-mono);\n font-size: 0.9em;\n}\n\n.link {\n color: var(--tempest-primary);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n\n.image {\n max-width: 100%;\n height: auto;\n border-radius: var(--tempest-radius-md);\n}\n\n/*\n * A wide table scrolls inside its own box, and that box is focusable — a scroll\n * container with nothing focusable inside cannot be reached by keyboard, which is\n * the same defect the Table fix addressed.\n */\n.tableScroll {\n overflow-x: auto;\n}\n\n.tableScroll:focus-visible {\n outline:
|
|
1
|
+
{"version":3,"file":"Markdown.module.cjs","names":[],"sources":["../../../src/components/Markdown/Markdown.module.css"],"sourcesContent":[".root {\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-relaxed);\n overflow-wrap: anywhere;\n}\n\n/*\n * Every block spaces itself with `margin-block-end` and the last child clears it,\n * so a Markdown body dropped into a card does not add a phantom gap at the bottom.\n */\n.root > * {\n margin-block: 0 var(--tempest-space-3);\n}\n\n.root > *:last-child {\n margin-block-end: 0;\n}\n\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n margin-block-start: var(--tempest-space-5);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-tight);\n}\n\n.root > .h1:first-child,\n.root > .h2:first-child,\n.root > .h3:first-child,\n.root > .h4:first-child,\n.root > .h5:first-child,\n.root > .h6:first-child {\n margin-block-start: 0;\n}\n\n.h1 {\n font-size: var(--tempest-text-2xl);\n}\n\n.h2 {\n font-size: var(--tempest-text-xl);\n}\n\n.h3 {\n font-size: var(--tempest-text-lg);\n}\n\n.h4,\n.h5,\n.h6 {\n font-size: var(--tempest-text-base);\n}\n\n.list {\n padding-inline-start: var(--tempest-space-6);\n}\n\n.list .list {\n margin-block: var(--tempest-space-1) 0;\n}\n\n.item {\n margin-block-end: var(--tempest-space-1);\n}\n\n.item > p {\n margin: 0;\n}\n\n.quote {\n padding-inline-start: var(--tempest-space-4);\n border-inline-start: 3px solid var(--tempest-border-strong);\n color: var(--tempest-text-muted);\n}\n\n.rule {\n height: 1px;\n border: 0;\n background-color: var(--tempest-border);\n}\n\n.inlineCode {\n padding: 0.1em 0.35em;\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n font-family: var(--tempest-font-mono);\n font-size: 0.9em;\n}\n\n.link {\n color: var(--tempest-primary);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n\n.image {\n max-width: 100%;\n height: auto;\n border-radius: var(--tempest-radius-md);\n}\n\n/*\n * A wide table scrolls inside its own box, and that box is focusable — a scroll\n * container with nothing focusable inside cannot be reached by keyboard, which is\n * the same defect the Table fix addressed.\n */\n.tableScroll {\n overflow-x: auto;\n}\n\n.tableScroll: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.table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--tempest-text-sm);\n}\n\n.table th,\n.table td {\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-bottom: 1px solid var(--tempest-border);\n text-align: left;\n}\n\n.table th {\n color: var(--tempest-text-muted);\n font-weight: var(--tempest-weight-semibold);\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Markdown.module.js","names":[],"sources":["../../../src/components/Markdown/Markdown.module.css"],"sourcesContent":[".root {\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-relaxed);\n overflow-wrap: anywhere;\n}\n\n/*\n * Every block spaces itself with `margin-block-end` and the last child clears it,\n * so a Markdown body dropped into a card does not add a phantom gap at the bottom.\n */\n.root > * {\n margin-block: 0 var(--tempest-space-3);\n}\n\n.root > *:last-child {\n margin-block-end: 0;\n}\n\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n margin-block-start: var(--tempest-space-5);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-tight);\n}\n\n.root > .h1:first-child,\n.root > .h2:first-child,\n.root > .h3:first-child,\n.root > .h4:first-child,\n.root > .h5:first-child,\n.root > .h6:first-child {\n margin-block-start: 0;\n}\n\n.h1 {\n font-size: var(--tempest-text-2xl);\n}\n\n.h2 {\n font-size: var(--tempest-text-xl);\n}\n\n.h3 {\n font-size: var(--tempest-text-lg);\n}\n\n.h4,\n.h5,\n.h6 {\n font-size: var(--tempest-text-base);\n}\n\n.list {\n padding-inline-start: var(--tempest-space-6);\n}\n\n.list .list {\n margin-block: var(--tempest-space-1) 0;\n}\n\n.item {\n margin-block-end: var(--tempest-space-1);\n}\n\n.item > p {\n margin: 0;\n}\n\n.quote {\n padding-inline-start: var(--tempest-space-4);\n border-inline-start: 3px solid var(--tempest-border-strong);\n color: var(--tempest-text-muted);\n}\n\n.rule {\n height: 1px;\n border: 0;\n background-color: var(--tempest-border);\n}\n\n.inlineCode {\n padding: 0.1em 0.35em;\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n font-family: var(--tempest-font-mono);\n font-size: 0.9em;\n}\n\n.link {\n color: var(--tempest-primary);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n\n.image {\n max-width: 100%;\n height: auto;\n border-radius: var(--tempest-radius-md);\n}\n\n/*\n * A wide table scrolls inside its own box, and that box is focusable — a scroll\n * container with nothing focusable inside cannot be reached by keyboard, which is\n * the same defect the Table fix addressed.\n */\n.tableScroll {\n overflow-x: auto;\n}\n\n.tableScroll:focus-visible {\n outline:
|
|
1
|
+
{"version":3,"file":"Markdown.module.js","names":[],"sources":["../../../src/components/Markdown/Markdown.module.css"],"sourcesContent":[".root {\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-relaxed);\n overflow-wrap: anywhere;\n}\n\n/*\n * Every block spaces itself with `margin-block-end` and the last child clears it,\n * so a Markdown body dropped into a card does not add a phantom gap at the bottom.\n */\n.root > * {\n margin-block: 0 var(--tempest-space-3);\n}\n\n.root > *:last-child {\n margin-block-end: 0;\n}\n\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n margin-block-start: var(--tempest-space-5);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-tight);\n}\n\n.root > .h1:first-child,\n.root > .h2:first-child,\n.root > .h3:first-child,\n.root > .h4:first-child,\n.root > .h5:first-child,\n.root > .h6:first-child {\n margin-block-start: 0;\n}\n\n.h1 {\n font-size: var(--tempest-text-2xl);\n}\n\n.h2 {\n font-size: var(--tempest-text-xl);\n}\n\n.h3 {\n font-size: var(--tempest-text-lg);\n}\n\n.h4,\n.h5,\n.h6 {\n font-size: var(--tempest-text-base);\n}\n\n.list {\n padding-inline-start: var(--tempest-space-6);\n}\n\n.list .list {\n margin-block: var(--tempest-space-1) 0;\n}\n\n.item {\n margin-block-end: var(--tempest-space-1);\n}\n\n.item > p {\n margin: 0;\n}\n\n.quote {\n padding-inline-start: var(--tempest-space-4);\n border-inline-start: 3px solid var(--tempest-border-strong);\n color: var(--tempest-text-muted);\n}\n\n.rule {\n height: 1px;\n border: 0;\n background-color: var(--tempest-border);\n}\n\n.inlineCode {\n padding: 0.1em 0.35em;\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n font-family: var(--tempest-font-mono);\n font-size: 0.9em;\n}\n\n.link {\n color: var(--tempest-primary);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n\n.image {\n max-width: 100%;\n height: auto;\n border-radius: var(--tempest-radius-md);\n}\n\n/*\n * A wide table scrolls inside its own box, and that box is focusable — a scroll\n * container with nothing focusable inside cannot be reached by keyboard, which is\n * the same defect the Table fix addressed.\n */\n.tableScroll {\n overflow-x: auto;\n}\n\n.tableScroll: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.table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--tempest-text-sm);\n}\n\n.table th,\n.table td {\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-bottom: 1px solid var(--tempest-border);\n text-align: left;\n}\n\n.table th {\n color: var(--tempest-text-muted);\n font-weight: var(--tempest-weight-semibold);\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Menubar.module.cjs","names":[],"sources":["../../../src/components/Menubar/Menubar.module.css"],"sourcesContent":[".root {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.menu {\n position: relative;\n}\n\n.trigger {\n display: inline-flex;\n align-items: center;\n padding: var(--tempest-space-1) 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 cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.trigger:focus-visible {\n outline:
|
|
1
|
+
{"version":3,"file":"Menubar.module.cjs","names":[],"sources":["../../../src/components/Menubar/Menubar.module.css"],"sourcesContent":[".root {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.menu {\n position: relative;\n}\n\n.trigger {\n display: inline-flex;\n align-items: center;\n padding: var(--tempest-space-1) 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 cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\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}\n\n.triggerOpen {\n background-color: var(--tempest-surface);\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: 200px;\n max-width: 360px;\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-menubar-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-4);\n width: 100%;\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: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n text-align: left;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .item:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.item:focus {\n outline: none;\n background-color: var(--tempest-surface);\n}\n\n.item:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n.itemLabel {\n flex: 1;\n}\n\n.shortcut {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-variant-numeric: tabular-nums;\n}\n\n.separator {\n height: 1px;\n background-color: var(--tempest-border);\n margin: var(--tempest-space-1) 0;\n}\n\n@keyframes tempest-menubar-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-sm);\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Menubar.module.js","names":[],"sources":["../../../src/components/Menubar/Menubar.module.css"],"sourcesContent":[".root {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.menu {\n position: relative;\n}\n\n.trigger {\n display: inline-flex;\n align-items: center;\n padding: var(--tempest-space-1) 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 cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.trigger:focus-visible {\n outline:
|
|
1
|
+
{"version":3,"file":"Menubar.module.js","names":[],"sources":["../../../src/components/Menubar/Menubar.module.css"],"sourcesContent":[".root {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.menu {\n position: relative;\n}\n\n.trigger {\n display: inline-flex;\n align-items: center;\n padding: var(--tempest-space-1) 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 cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\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}\n\n.triggerOpen {\n background-color: var(--tempest-surface);\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: 200px;\n max-width: 360px;\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-menubar-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-4);\n width: 100%;\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: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n text-align: left;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .item:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.item:focus {\n outline: none;\n background-color: var(--tempest-surface);\n}\n\n.item:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n.itemLabel {\n flex: 1;\n}\n\n.shortcut {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-variant-numeric: tabular-nums;\n}\n\n.separator {\n height: 1px;\n background-color: var(--tempest-border);\n margin: var(--tempest-space-1) 0;\n}\n\n@keyframes tempest-menubar-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-sm);\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("../Portal/Portal.cjs"),n=require("../Portal/anchor-position.cjs"),r=require("./MultiSelect.module.cjs");let
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../Portal/Portal.cjs"),n=require("../Portal/anchor-position.cjs"),r=require("../Combobox/use-listbox-dismiss.cjs"),i=require("./MultiSelect.module.cjs");let a=require("react"),o=require("react/jsx-runtime");function s(e,t){return!t||e.label.toLowerCase().includes(t.toLowerCase())}var c=4;function l({options:l,value:u,onChange:d,label:f,placeholder:p=`Selecione`,helperText:m,error:h,disabled:g,maxItems:_,filter:v=s,emptyMessage:y=`Nenhuma opção encontrada`,portal:b=!0,className:x}){let S=(0,a.useId)(),C=(0,a.useRef)(null),w=(0,a.useRef)(null),[T,E]=(0,a.useState)(!1),[D,O]=(0,a.useState)(``),[k,A]=(0,a.useState)(0),[j,M]=(0,a.useState)(null),[N,P]=(0,a.useState)(null),F=n.useAnchorPosition({anchor:j,floating:N,side:`bottom`,align:`start`,offset:c,enabled:b&&T,matchWidth:!0}),I=(0,a.useMemo)(()=>new Set(u),[u]),L=(0,a.useMemo)(()=>u.map(e=>l.find(t=>t.value===e)).filter(e=>!!e),[u,l]),R=(0,a.useMemo)(()=>l.filter(e=>v(e,D)),[l,v,D]),z=_!==void 0&&u.length>=_,B=(0,a.useCallback)(e=>{if(!e.disabled){if(I.has(e.value))d(u.filter(t=>t!==e.value));else{if(z)return;d([...u,e.value])}O(``),w.current?.focus()}},[I,d,u,z]),V=(0,a.useCallback)(e=>{d(u.filter(t=>t!==e))},[d,u]),H=(0,a.useCallback)(()=>{E(!1),O(``)},[]),{onInputBlur:U,onListMouseDown:W}=r.useListboxDismiss({open:T,rootRef:C,listNode:N,onDismiss:H}),G=e=>`${S}-option-${e}`,K=T?R[k]:void 0,q=e=>{g||e.target!==e.currentTarget||(e.preventDefault(),w.current?.focus(),E(!0))},J=e=>{if(e.key===`ArrowDown`)e.preventDefault(),E(!0),A(e=>Math.min(R.length-1,e+1));else if(e.key===`ArrowUp`)e.preventDefault(),A(e=>Math.max(0,e-1));else if(e.key===`Enter`){e.preventDefault();let t=R[k];t&&B(t)}else e.key===`Escape`?(T&&e.preventDefault(),H()):e.key===`Backspace`&&D===``&&u.length>0&&V(u[u.length-1])},Y=(0,o.jsx)(`ul`,{ref:P,id:`${S}-listbox`,role:`listbox`,"aria-multiselectable":!0,className:e.cn(i.default.menu,b&&i.default.portalled),style:F,onMouseDown:W,children:R.length===0?(0,o.jsx)(`li`,{className:i.default.empty,children:y}):R.map((t,n)=>{let r=I.has(t.value);return(0,o.jsxs)(`li`,{id:G(n),role:`option`,"aria-selected":r,"aria-disabled":t.disabled||!r&&z,className:e.cn(i.default.option,n===k&&i.default.active,r&&i.default.selected),onMouseEnter:()=>A(n),onMouseDown:()=>B(t),children:[(0,o.jsx)(`span`,{className:i.default.check,"aria-hidden":!0,children:r?`✓`:``}),t.label]},t.value)})});return(0,o.jsxs)(`div`,{ref:C,className:e.cn(i.default.wrapper,h&&i.default.error,x),children:[f&&(0,o.jsx)(`label`,{htmlFor:S,className:i.default.label,children:f}),(0,o.jsxs)(`div`,{ref:M,className:e.cn(i.default.field,g&&i.default.disabled),onMouseDown:q,children:[L.map(e=>(0,o.jsxs)(`span`,{className:i.default.chip,children:[e.label,(0,o.jsx)(`button`,{type:`button`,className:i.default.chipRemove,"aria-label":`Remover ${e.label}`,disabled:g,onMouseDown:t=>{t.preventDefault(),V(e.value)},children:`×`})]},e.value)),(0,o.jsx)(`input`,{id:S,ref:w,type:`text`,role:`combobox`,"aria-expanded":T,"aria-controls":`${S}-listbox`,"aria-autocomplete":`list`,"aria-activedescendant":K?G(k):void 0,className:i.default.input,placeholder:u.length===0?p:``,disabled:g,value:D,onFocus:()=>E(!0),onChange:e=>{O(e.target.value),E(!0),A(0)},onKeyDown:J,onBlur:U})]}),T&&(b?(0,o.jsx)(t.Portal,{children:Y}):Y),h?(0,o.jsx)(`span`,{className:i.default.errorText,children:h}):m?(0,o.jsx)(`span`,{className:i.default.helper,children:m}):null]})}exports.MultiSelect=l;
|
|
2
2
|
//# sourceMappingURL=MultiSelect.cjs.map
|