@gnome-ui/react 1.40.1 → 1.41.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 +42 -0
- package/dist/components/AvatarRotator/AvatarRotator.cjs +2 -0
- package/dist/components/AvatarRotator/AvatarRotator.cjs.map +1 -0
- package/dist/components/AvatarRotator/AvatarRotator.d.ts +36 -0
- package/dist/components/AvatarRotator/AvatarRotator.js +84 -0
- package/dist/components/AvatarRotator/AvatarRotator.js.map +1 -0
- package/dist/components/AvatarRotator/AvatarRotator.module.css.cjs +2 -0
- package/dist/components/AvatarRotator/AvatarRotator.module.css.cjs.map +1 -0
- package/dist/components/AvatarRotator/AvatarRotator.module.css.js +13 -0
- package/dist/components/AvatarRotator/AvatarRotator.module.css.js.map +1 -0
- package/dist/components/AvatarRotator/index.d.ts +2 -0
- package/dist/components/AvatarRotator.cjs +1 -0
- package/dist/components/AvatarRotator.d.ts +2 -0
- package/dist/components/AvatarRotator.js +2 -0
- package/dist/components/Chip/Chip.cjs +1 -1
- package/dist/components/Chip/Chip.cjs.map +1 -1
- package/dist/components/Chip/Chip.js +19 -19
- package/dist/components/ContributionGraph/ContributionGraph.cjs +1 -1
- package/dist/components/ContributionGraph/ContributionGraph.cjs.map +1 -1
- package/dist/components/ContributionGraph/ContributionGraph.js +108 -117
- package/dist/components/ContributionGraph/ContributionGraph.js.map +1 -1
- package/dist/components/CountDownTimer/CountDownTimer.cjs +1 -1
- package/dist/components/CountDownTimer/CountDownTimer.cjs.map +1 -1
- package/dist/components/CountDownTimer/CountDownTimer.js +37 -28
- package/dist/components/CountDownTimer/CountDownTimer.js.map +1 -1
- package/dist/components/Dropdown/Dropdown.cjs +1 -1
- package/dist/components/Dropdown/Dropdown.cjs.map +1 -1
- package/dist/components/Dropdown/Dropdown.js +45 -46
- package/dist/components/Dropdown/Dropdown.js.map +1 -1
- package/dist/components/GnomeProvider/GnomeContext.cjs +2 -0
- package/dist/components/GnomeProvider/GnomeContext.cjs.map +1 -0
- package/dist/components/GnomeProvider/GnomeContext.d.ts +16 -0
- package/dist/components/GnomeProvider/GnomeContext.js +40 -0
- package/dist/components/GnomeProvider/GnomeContext.js.map +1 -0
- package/dist/components/GnomeProvider/GnomeProvider.cjs +2 -0
- package/dist/components/GnomeProvider/GnomeProvider.cjs.map +1 -0
- package/dist/components/GnomeProvider/GnomeProvider.d.ts +24 -0
- package/dist/components/GnomeProvider/GnomeProvider.js +25 -0
- package/dist/components/GnomeProvider/GnomeProvider.js.map +1 -0
- package/dist/components/PasswordEntryRow/PasswordEntryRow.cjs +1 -1
- package/dist/components/PasswordEntryRow/PasswordEntryRow.cjs.map +1 -1
- package/dist/components/PasswordEntryRow/PasswordEntryRow.js +11 -12
- package/dist/components/PasswordEntryRow/PasswordEntryRow.js.map +1 -1
- package/dist/components/SearchBar/SearchBar.cjs +1 -1
- package/dist/components/SearchBar/SearchBar.cjs.map +1 -1
- package/dist/components/SearchBar/SearchBar.js +38 -39
- package/dist/components/SearchBar/SearchBar.js.map +1 -1
- package/dist/components/Sidebar/SidebarSection.cjs +1 -1
- package/dist/components/Sidebar/SidebarSection.cjs.map +1 -1
- package/dist/components/Sidebar/SidebarSection.js +41 -42
- package/dist/components/Sidebar/SidebarSection.js.map +1 -1
- package/dist/components/Toast/Toast.cjs +1 -1
- package/dist/components/Toast/Toast.cjs.map +1 -1
- package/dist/components/Toast/Toast.js +18 -18
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks.cjs +1 -1
- package/dist/hooks.js +3 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +90 -87
- package/dist/style.css +1 -1
- package/package.json +16 -1
- package/dist/icons/dist/icons/Check.cjs +0 -2
- package/dist/icons/dist/icons/Check.cjs.map +0 -1
- package/dist/icons/dist/icons/Check.js +0 -12
- package/dist/icons/dist/icons/Check.js.map +0 -1
- package/dist/icons/dist/icons/Close.cjs +0 -2
- package/dist/icons/dist/icons/Close.cjs.map +0 -1
- package/dist/icons/dist/icons/Close.js +0 -12
- package/dist/icons/dist/icons/Close.js.map +0 -1
- package/dist/icons/dist/icons/PanDown.cjs +0 -2
- package/dist/icons/dist/icons/PanDown.cjs.map +0 -1
- package/dist/icons/dist/icons/PanDown.js +0 -12
- package/dist/icons/dist/icons/PanDown.js.map +0 -1
- package/dist/icons/dist/icons/PanUp.cjs +0 -2
- package/dist/icons/dist/icons/PanUp.cjs.map +0 -1
- package/dist/icons/dist/icons/PanUp.js +0 -12
- package/dist/icons/dist/icons/PanUp.js.map +0 -1
- package/dist/icons/dist/icons/Search.cjs +0 -2
- package/dist/icons/dist/icons/Search.cjs.map +0 -1
- package/dist/icons/dist/icons/Search.js +0 -12
- package/dist/icons/dist/icons/Search.js.map +0 -1
- package/dist/icons/dist/icons/ViewConceal.cjs +0 -2
- package/dist/icons/dist/icons/ViewConceal.cjs.map +0 -1
- package/dist/icons/dist/icons/ViewConceal.js +0 -12
- package/dist/icons/dist/icons/ViewConceal.js.map +0 -1
- package/dist/icons/dist/icons/ViewReveal.cjs +0 -2
- package/dist/icons/dist/icons/ViewReveal.cjs.map +0 -1
- package/dist/icons/dist/icons/ViewReveal.js +0 -12
- package/dist/icons/dist/icons/ViewReveal.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../Icon/Icon.cjs`),t=require(
|
|
1
|
+
const e=require(`../Icon/Icon.cjs`),t=require(`./Dropdown.module.css.cjs`);let n=require(`react`),r=require(`react/jsx-runtime`),i=require(`@gnome-ui/icons`);function a({options:a,value:o,onChange:s,placeholder:c=`Select an option`,disabled:l,className:u,...d}){let[f,p]=(0,n.useState)(!1),[m,h]=(0,n.useState)(-1),[g,_]=(0,n.useState)(!1),v=(0,n.useId)(),y=(0,n.useId)(),b=(0,n.useRef)(null),x=(0,n.useRef)(null),S=a.find(e=>e.value===o),C=(0,n.useCallback)(()=>{if(!b.current)return;let e=b.current.getBoundingClientRect(),t=window.innerHeight-e.bottom,n=Math.min(a.length*48+8,280);_(t<n&&e.top>n)},[a.length]),w=(0,n.useCallback)(()=>{if(l)return;C(),p(!0);let e=a.findIndex(e=>e.value===o&&!e.disabled);h(e>=0?e:a.findIndex(e=>!e.disabled))},[l,C,a,o]),T=(0,n.useCallback)(()=>{p(!1),h(-1),b.current?.focus()},[]),E=(0,n.useCallback)(e=>{e.disabled||(s?.(e.value),T())},[s,T]);(0,n.useEffect)(()=>{!f||m<0||!x.current||x.current.children[m]?.scrollIntoView({block:`nearest`})},[m,f]),(0,n.useEffect)(()=>{if(!f)return;let e=e=>{!b.current?.contains(e.target)&&!x.current?.contains(e.target)&&T()};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[f,T]);let D=(0,n.useCallback)(e=>{switch(e.key){case`Enter`:case` `:case`ArrowDown`:e.preventDefault(),w();break;case`ArrowUp`:e.preventDefault(),C(),p(!0),h(a.reduce((e,t,n)=>t.disabled?e:n,-1));break}},[w,C,a]),O=(0,n.useCallback)(e=>{let t=a.map((e,t)=>({o:e,i:t})).filter(({o:e})=>!e.disabled).map(({i:e})=>e),n=t.indexOf(m);switch(e.key){case`ArrowDown`:{e.preventDefault();let r=t[Math.min(n+1,t.length-1)];h(r??m);break}case`ArrowUp`:{e.preventDefault();let r=t[Math.max(n-1,0)];h(r??m);break}case`Home`:e.preventDefault(),h(t[0]??-1);break;case`End`:e.preventDefault(),h(t[t.length-1]??-1);break;case`Enter`:case` `:e.preventDefault(),m>=0&&E(a[m]);break;case`Escape`:case`Tab`:T();break}},[a,m,E,T]);return(0,r.jsxs)(`div`,{className:[t.default.wrapper,u].filter(Boolean).join(` `),...d,children:[(0,r.jsxs)(`button`,{ref:b,id:v,type:`button`,role:`combobox`,"aria-haspopup":`listbox`,"aria-expanded":f,"aria-controls":y,"aria-activedescendant":f&&m>=0?`${y}-opt-${m}`:void 0,disabled:l,className:[t.default.trigger,f?t.default.triggerOpen:null].filter(Boolean).join(` `),onClick:()=>f?T():w(),onKeyDown:D,children:[(0,r.jsx)(`span`,{className:[t.default.triggerLabel,S?null:t.default.placeholder].filter(Boolean).join(` `),children:S?.label??c}),(0,r.jsx)(e.Icon,{icon:i.PanDown,size:`md`,"aria-hidden":!0,className:[t.default.chevron,f?t.default.chevronOpen:null].filter(Boolean).join(` `)})]}),f&&(0,r.jsx)(`ul`,{ref:x,id:y,role:`listbox`,"aria-labelledby":v,tabIndex:-1,className:[t.default.list,g?t.default.listUp:t.default.listDown].filter(Boolean).join(` `),onKeyDown:O,children:a.map((n,a)=>(0,r.jsxs)(`li`,{id:`${y}-opt-${a}`,role:`option`,"aria-selected":n.value===o,"aria-disabled":n.disabled,className:[t.default.option,n.value===o?t.default.optionSelected:null,a===m?t.default.optionActive:null,n.disabled?t.default.optionDisabled:null].filter(Boolean).join(` `),onMouseEnter:()=>!n.disabled&&h(a),onClick:()=>E(n),children:[(0,r.jsxs)(`span`,{className:t.default.optionText,children:[(0,r.jsx)(`span`,{className:t.default.optionLabel,children:n.label}),n.description&&(0,r.jsx)(`span`,{className:t.default.optionDesc,children:n.description})]}),n.value===o&&(0,r.jsx)(e.Icon,{icon:i.Check,size:`md`,"aria-hidden":!0,className:t.default.checkIcon})]},n.value))})]})}exports.Dropdown=a;
|
|
2
2
|
//# sourceMappingURL=Dropdown.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropdown.cjs","names":[],"sources":["../../../src/components/Dropdown/Dropdown.tsx"],"sourcesContent":["import {\n useState,\n useRef,\n useId,\n useEffect,\n useCallback,\n type KeyboardEvent,\n type HTMLAttributes,\n} from \"react\";\nimport { PanDown, Check } from \"@gnome-ui/icons\";\nimport { Icon } from \"../Icon\";\nimport styles from \"./Dropdown.module.css\";\n\nexport interface DropdownOption<V extends string = string> {\n /** The value submitted / returned on selection. */\n value: V;\n /** Display label shown in the list and trigger. */\n label: string;\n /** Optional descriptive text shown below the label. */\n description?: string;\n /** Whether the option is selectable. */\n disabled?: boolean;\n}\n\nexport interface DropdownProps<V extends string = string>\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** The list of selectable options. */\n options: DropdownOption<V>[];\n /** The currently selected value. */\n value?: V;\n /** Called when the user selects an option. */\n onChange?: (value: V) => void;\n /** Placeholder shown when no option is selected. */\n placeholder?: string;\n /** Accessible label for the control (used as `aria-label`). */\n \"aria-label\"?: string;\n /** Disables the entire control. */\n disabled?: boolean;\n}\n\n/**\n * Expandable option list following the Adwaita combo-row / drop-down pattern.\n *\n * - Fully keyboard-navigable: Space/Enter opens; ↑/↓ navigate; Enter selects; Escape closes.\n * - Uses `role=\"combobox\"` + `role=\"listbox\"` + `role=\"option\"` ARIA pattern.\n * - Closes when clicking outside or pressing Escape.\n * - The list flips above the trigger when there is not enough space below.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/drop-down-lists.html\n */\nexport function Dropdown<V extends string = string>({\n options,\n value,\n onChange,\n placeholder = \"Select an option\",\n disabled,\n className,\n ...props\n}: DropdownProps<V>) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const [flipUp, setFlipUp] = useState(false);\n\n const triggerId = useId();\n const listboxId = useId();\n const triggerRef = useRef<HTMLButtonElement>(null);\n const listRef = useRef<HTMLUListElement>(null);\n\n const selected = options.find((o) => o.value === value);\n\n // Compute flip direction when opening\n const computeFlip = useCallback(() => {\n if (!triggerRef.current) return;\n const rect = triggerRef.current.getBoundingClientRect();\n const spaceBelow = window.innerHeight - rect.bottom;\n const listH = Math.min(options.length * 48 + 8, 280);\n setFlipUp(spaceBelow < listH && rect.top > listH);\n }, [options.length]);\n\n const openList = useCallback(() => {\n if (disabled) return;\n computeFlip();\n setOpen(true);\n const idx = options.findIndex((o) => o.value === value && !o.disabled);\n setActiveIndex(idx >= 0 ? idx : options.findIndex((o) => !o.disabled));\n }, [disabled, computeFlip, options, value]);\n\n const closeList = useCallback(() => {\n setOpen(false);\n setActiveIndex(-1);\n triggerRef.current?.focus();\n }, []);\n\n const selectOption = useCallback(\n (opt: DropdownOption<V>) => {\n if (opt.disabled) return;\n onChange?.(opt.value);\n closeList();\n },\n [onChange, closeList]\n );\n\n // Scroll active item into view\n useEffect(() => {\n if (!open || activeIndex < 0 || !listRef.current) return;\n const item = listRef.current.children[activeIndex] as HTMLElement | undefined;\n item?.scrollIntoView({ block: \"nearest\" });\n }, [activeIndex, open]);\n\n // Close on outside click\n useEffect(() => {\n if (!open) return;\n const handler = (e: MouseEvent) => {\n if (\n !triggerRef.current?.contains(e.target as Node) &&\n !listRef.current?.contains(e.target as Node)\n ) {\n closeList();\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open, closeList]);\n\n // Trigger keyboard handler\n const handleTriggerKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n switch (e.key) {\n case \"Enter\":\n case \" \":\n case \"ArrowDown\":\n e.preventDefault();\n openList();\n break;\n case \"ArrowUp\":\n e.preventDefault();\n computeFlip();\n setOpen(true);\n setActiveIndex(\n options.reduce(\n (last, o, i) => (!o.disabled ? i : last),\n -1\n )\n );\n break;\n }\n },\n [openList, computeFlip, options]\n );\n\n // Listbox keyboard handler\n const handleListKeyDown = useCallback(\n (e: KeyboardEvent<HTMLUListElement>) => {\n const enabledIndexes = options\n .map((o, i) => ({ o, i }))\n .filter(({ o }) => !o.disabled)\n .map(({ i }) => i);\n\n const currentPos = enabledIndexes.indexOf(activeIndex);\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n const next = enabledIndexes[Math.min(currentPos + 1, enabledIndexes.length - 1)];\n setActiveIndex(next ?? activeIndex);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n const prev = enabledIndexes[Math.max(currentPos - 1, 0)];\n setActiveIndex(prev ?? activeIndex);\n break;\n }\n case \"Home\":\n e.preventDefault();\n setActiveIndex(enabledIndexes[0] ?? -1);\n break;\n case \"End\":\n e.preventDefault();\n setActiveIndex(enabledIndexes[enabledIndexes.length - 1] ?? -1);\n break;\n case \"Enter\":\n case \" \": {\n e.preventDefault();\n if (activeIndex >= 0) selectOption(options[activeIndex]);\n break;\n }\n case \"Escape\":\n case \"Tab\":\n closeList();\n break;\n }\n },\n [options, activeIndex, selectOption, closeList]\n );\n\n return (\n <div\n className={[styles.wrapper, className].filter(Boolean).join(\" \")}\n {...props}\n >\n {/* Trigger button */}\n <button\n ref={triggerRef}\n id={triggerId}\n type=\"button\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={listboxId}\n aria-activedescendant={\n open && activeIndex >= 0 ? `${listboxId}-opt-${activeIndex}` : undefined\n }\n disabled={disabled}\n className={[styles.trigger, open ? styles.triggerOpen : null]\n .filter(Boolean)\n .join(\" \")}\n onClick={() => (open ? closeList() : openList())}\n onKeyDown={handleTriggerKeyDown}\n >\n <span className={[styles.triggerLabel, !selected ? styles.placeholder : null]\n .filter(Boolean).join(\" \")}>\n {selected?.label ?? placeholder}\n </span>\n <Icon\n icon={PanDown}\n size=\"md\"\n aria-hidden\n className={[styles.chevron, open ? styles.chevronOpen : null]\n .filter(Boolean).join(\" \")}\n />\n </button>\n\n {/* Listbox */}\n {open && (\n <ul\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n aria-labelledby={triggerId}\n tabIndex={-1}\n className={[styles.list, flipUp ? styles.listUp : styles.listDown]\n .filter(Boolean)\n .join(\" \")}\n onKeyDown={handleListKeyDown}\n >\n {options.map((opt, i) => (\n <li\n key={opt.value}\n id={`${listboxId}-opt-${i}`}\n role=\"option\"\n aria-selected={opt.value === value}\n aria-disabled={opt.disabled}\n className={[\n styles.option,\n opt.value === value ? styles.optionSelected : null,\n i === activeIndex ? styles.optionActive : null,\n opt.disabled ? styles.optionDisabled : null,\n ]\n .filter(Boolean)\n .join(\" \")}\n onMouseEnter={() => !opt.disabled && setActiveIndex(i)}\n onClick={() => selectOption(opt)}\n >\n <span className={styles.optionText}>\n <span className={styles.optionLabel}>{opt.label}</span>\n {opt.description && (\n <span className={styles.optionDesc}>{opt.description}</span>\n )}\n </span>\n {opt.value === value && (\n <Icon icon={Check} size=\"md\" aria-hidden className={styles.checkIcon} />\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Dropdown.cjs","names":[],"sources":["../../../src/components/Dropdown/Dropdown.tsx"],"sourcesContent":["import {\n useState,\n useRef,\n useId,\n useEffect,\n useCallback,\n type KeyboardEvent,\n type HTMLAttributes,\n} from \"react\";\nimport { PanDown, Check } from \"@gnome-ui/icons\";\nimport { Icon } from \"../Icon\";\nimport styles from \"./Dropdown.module.css\";\n\nexport interface DropdownOption<V extends string = string> {\n /** The value submitted / returned on selection. */\n value: V;\n /** Display label shown in the list and trigger. */\n label: string;\n /** Optional descriptive text shown below the label. */\n description?: string;\n /** Whether the option is selectable. */\n disabled?: boolean;\n}\n\nexport interface DropdownProps<V extends string = string>\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** The list of selectable options. */\n options: DropdownOption<V>[];\n /** The currently selected value. */\n value?: V;\n /** Called when the user selects an option. */\n onChange?: (value: V) => void;\n /** Placeholder shown when no option is selected. */\n placeholder?: string;\n /** Accessible label for the control (used as `aria-label`). */\n \"aria-label\"?: string;\n /** Disables the entire control. */\n disabled?: boolean;\n}\n\n/**\n * Expandable option list following the Adwaita combo-row / drop-down pattern.\n *\n * - Fully keyboard-navigable: Space/Enter opens; ↑/↓ navigate; Enter selects; Escape closes.\n * - Uses `role=\"combobox\"` + `role=\"listbox\"` + `role=\"option\"` ARIA pattern.\n * - Closes when clicking outside or pressing Escape.\n * - The list flips above the trigger when there is not enough space below.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/drop-down-lists.html\n */\nexport function Dropdown<V extends string = string>({\n options,\n value,\n onChange,\n placeholder = \"Select an option\",\n disabled,\n className,\n ...props\n}: DropdownProps<V>) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const [flipUp, setFlipUp] = useState(false);\n\n const triggerId = useId();\n const listboxId = useId();\n const triggerRef = useRef<HTMLButtonElement>(null);\n const listRef = useRef<HTMLUListElement>(null);\n\n const selected = options.find((o) => o.value === value);\n\n // Compute flip direction when opening\n const computeFlip = useCallback(() => {\n if (!triggerRef.current) return;\n const rect = triggerRef.current.getBoundingClientRect();\n const spaceBelow = window.innerHeight - rect.bottom;\n const listH = Math.min(options.length * 48 + 8, 280);\n setFlipUp(spaceBelow < listH && rect.top > listH);\n }, [options.length]);\n\n const openList = useCallback(() => {\n if (disabled) return;\n computeFlip();\n setOpen(true);\n const idx = options.findIndex((o) => o.value === value && !o.disabled);\n setActiveIndex(idx >= 0 ? idx : options.findIndex((o) => !o.disabled));\n }, [disabled, computeFlip, options, value]);\n\n const closeList = useCallback(() => {\n setOpen(false);\n setActiveIndex(-1);\n triggerRef.current?.focus();\n }, []);\n\n const selectOption = useCallback(\n (opt: DropdownOption<V>) => {\n if (opt.disabled) return;\n onChange?.(opt.value);\n closeList();\n },\n [onChange, closeList]\n );\n\n // Scroll active item into view\n useEffect(() => {\n if (!open || activeIndex < 0 || !listRef.current) return;\n const item = listRef.current.children[activeIndex] as HTMLElement | undefined;\n item?.scrollIntoView({ block: \"nearest\" });\n }, [activeIndex, open]);\n\n // Close on outside click\n useEffect(() => {\n if (!open) return;\n const handler = (e: MouseEvent) => {\n if (\n !triggerRef.current?.contains(e.target as Node) &&\n !listRef.current?.contains(e.target as Node)\n ) {\n closeList();\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open, closeList]);\n\n // Trigger keyboard handler\n const handleTriggerKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n switch (e.key) {\n case \"Enter\":\n case \" \":\n case \"ArrowDown\":\n e.preventDefault();\n openList();\n break;\n case \"ArrowUp\":\n e.preventDefault();\n computeFlip();\n setOpen(true);\n setActiveIndex(\n options.reduce(\n (last, o, i) => (!o.disabled ? i : last),\n -1\n )\n );\n break;\n }\n },\n [openList, computeFlip, options]\n );\n\n // Listbox keyboard handler\n const handleListKeyDown = useCallback(\n (e: KeyboardEvent<HTMLUListElement>) => {\n const enabledIndexes = options\n .map((o, i) => ({ o, i }))\n .filter(({ o }) => !o.disabled)\n .map(({ i }) => i);\n\n const currentPos = enabledIndexes.indexOf(activeIndex);\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n const next = enabledIndexes[Math.min(currentPos + 1, enabledIndexes.length - 1)];\n setActiveIndex(next ?? activeIndex);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n const prev = enabledIndexes[Math.max(currentPos - 1, 0)];\n setActiveIndex(prev ?? activeIndex);\n break;\n }\n case \"Home\":\n e.preventDefault();\n setActiveIndex(enabledIndexes[0] ?? -1);\n break;\n case \"End\":\n e.preventDefault();\n setActiveIndex(enabledIndexes[enabledIndexes.length - 1] ?? -1);\n break;\n case \"Enter\":\n case \" \": {\n e.preventDefault();\n if (activeIndex >= 0) selectOption(options[activeIndex]);\n break;\n }\n case \"Escape\":\n case \"Tab\":\n closeList();\n break;\n }\n },\n [options, activeIndex, selectOption, closeList]\n );\n\n return (\n <div\n className={[styles.wrapper, className].filter(Boolean).join(\" \")}\n {...props}\n >\n {/* Trigger button */}\n <button\n ref={triggerRef}\n id={triggerId}\n type=\"button\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={listboxId}\n aria-activedescendant={\n open && activeIndex >= 0 ? `${listboxId}-opt-${activeIndex}` : undefined\n }\n disabled={disabled}\n className={[styles.trigger, open ? styles.triggerOpen : null]\n .filter(Boolean)\n .join(\" \")}\n onClick={() => (open ? closeList() : openList())}\n onKeyDown={handleTriggerKeyDown}\n >\n <span className={[styles.triggerLabel, !selected ? styles.placeholder : null]\n .filter(Boolean).join(\" \")}>\n {selected?.label ?? placeholder}\n </span>\n <Icon\n icon={PanDown}\n size=\"md\"\n aria-hidden\n className={[styles.chevron, open ? styles.chevronOpen : null]\n .filter(Boolean).join(\" \")}\n />\n </button>\n\n {/* Listbox */}\n {open && (\n <ul\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n aria-labelledby={triggerId}\n tabIndex={-1}\n className={[styles.list, flipUp ? styles.listUp : styles.listDown]\n .filter(Boolean)\n .join(\" \")}\n onKeyDown={handleListKeyDown}\n >\n {options.map((opt, i) => (\n <li\n key={opt.value}\n id={`${listboxId}-opt-${i}`}\n role=\"option\"\n aria-selected={opt.value === value}\n aria-disabled={opt.disabled}\n className={[\n styles.option,\n opt.value === value ? styles.optionSelected : null,\n i === activeIndex ? styles.optionActive : null,\n opt.disabled ? styles.optionDisabled : null,\n ]\n .filter(Boolean)\n .join(\" \")}\n onMouseEnter={() => !opt.disabled && setActiveIndex(i)}\n onClick={() => selectOption(opt)}\n >\n <span className={styles.optionText}>\n <span className={styles.optionLabel}>{opt.label}</span>\n {opt.description && (\n <span className={styles.optionDesc}>{opt.description}</span>\n )}\n </span>\n {opt.value === value && (\n <Icon icon={Check} size=\"md\" aria-hidden className={styles.checkIcon} />\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { Icon as e } from "../Icon/Icon.js";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
2
|
+
import t from "./Dropdown.module.css.js";
|
|
3
|
+
import { useCallback as n, useEffect as r, useId as i, useRef as a, useState as o } from "react";
|
|
4
|
+
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
5
|
+
import { Check as l, PanDown as u } from "@gnome-ui/icons";
|
|
7
6
|
//#region src/components/Dropdown/Dropdown.tsx
|
|
8
7
|
function d({ options: d, value: f, onChange: p, placeholder: m = "Select an option", disabled: h, className: g, ..._ }) {
|
|
9
|
-
let [v, y] =
|
|
8
|
+
let [v, y] = o(!1), [b, x] = o(-1), [S, C] = o(!1), w = i(), T = i(), E = a(null), D = a(null), O = d.find((e) => e.value === f), k = n(() => {
|
|
10
9
|
if (!E.current) return;
|
|
11
10
|
let e = E.current.getBoundingClientRect(), t = window.innerHeight - e.bottom, n = Math.min(d.length * 48 + 8, 280);
|
|
12
11
|
C(t < n && e.top > n);
|
|
13
|
-
}, [d.length]), A =
|
|
12
|
+
}, [d.length]), A = n(() => {
|
|
14
13
|
if (h) return;
|
|
15
14
|
k(), y(!0);
|
|
16
15
|
let e = d.findIndex((e) => e.value === f && !e.disabled);
|
|
@@ -20,21 +19,21 @@ function d({ options: d, value: f, onChange: p, placeholder: m = "Select an opti
|
|
|
20
19
|
k,
|
|
21
20
|
d,
|
|
22
21
|
f
|
|
23
|
-
]), j =
|
|
22
|
+
]), j = n(() => {
|
|
24
23
|
y(!1), x(-1), E.current?.focus();
|
|
25
|
-
}, []), M =
|
|
24
|
+
}, []), M = n((e) => {
|
|
26
25
|
e.disabled || (p?.(e.value), j());
|
|
27
26
|
}, [p, j]);
|
|
28
|
-
|
|
27
|
+
r(() => {
|
|
29
28
|
!v || b < 0 || !D.current || D.current.children[b]?.scrollIntoView({ block: "nearest" });
|
|
30
|
-
}, [b, v]),
|
|
29
|
+
}, [b, v]), r(() => {
|
|
31
30
|
if (!v) return;
|
|
32
31
|
let e = (e) => {
|
|
33
32
|
!E.current?.contains(e.target) && !D.current?.contains(e.target) && j();
|
|
34
33
|
};
|
|
35
34
|
return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
36
35
|
}, [v, j]);
|
|
37
|
-
let N =
|
|
36
|
+
let N = n((e) => {
|
|
38
37
|
switch (e.key) {
|
|
39
38
|
case "Enter":
|
|
40
39
|
case " ":
|
|
@@ -49,7 +48,7 @@ function d({ options: d, value: f, onChange: p, placeholder: m = "Select an opti
|
|
|
49
48
|
A,
|
|
50
49
|
k,
|
|
51
50
|
d
|
|
52
|
-
]), P =
|
|
51
|
+
]), P = n((e) => {
|
|
53
52
|
let t = d.map((e, t) => ({
|
|
54
53
|
o: e,
|
|
55
54
|
i: t
|
|
@@ -88,10 +87,10 @@ function d({ options: d, value: f, onChange: p, placeholder: m = "Select an opti
|
|
|
88
87
|
M,
|
|
89
88
|
j
|
|
90
89
|
]);
|
|
91
|
-
return /* @__PURE__ */
|
|
92
|
-
className: [
|
|
90
|
+
return /* @__PURE__ */ c("div", {
|
|
91
|
+
className: [t.wrapper, g].filter(Boolean).join(" "),
|
|
93
92
|
..._,
|
|
94
|
-
children: [/* @__PURE__ */
|
|
93
|
+
children: [/* @__PURE__ */ c("button", {
|
|
95
94
|
ref: E,
|
|
96
95
|
id: w,
|
|
97
96
|
type: "button",
|
|
@@ -101,55 +100,55 @@ function d({ options: d, value: f, onChange: p, placeholder: m = "Select an opti
|
|
|
101
100
|
"aria-controls": T,
|
|
102
101
|
"aria-activedescendant": v && b >= 0 ? `${T}-opt-${b}` : void 0,
|
|
103
102
|
disabled: h,
|
|
104
|
-
className: [
|
|
103
|
+
className: [t.trigger, v ? t.triggerOpen : null].filter(Boolean).join(" "),
|
|
105
104
|
onClick: () => v ? j() : A(),
|
|
106
105
|
onKeyDown: N,
|
|
107
|
-
children: [/* @__PURE__ */
|
|
108
|
-
className: [
|
|
106
|
+
children: [/* @__PURE__ */ s("span", {
|
|
107
|
+
className: [t.triggerLabel, O ? null : t.placeholder].filter(Boolean).join(" "),
|
|
109
108
|
children: O?.label ?? m
|
|
110
|
-
}), /* @__PURE__ */
|
|
111
|
-
icon:
|
|
109
|
+
}), /* @__PURE__ */ s(e, {
|
|
110
|
+
icon: u,
|
|
112
111
|
size: "md",
|
|
113
112
|
"aria-hidden": !0,
|
|
114
|
-
className: [
|
|
113
|
+
className: [t.chevron, v ? t.chevronOpen : null].filter(Boolean).join(" ")
|
|
115
114
|
})]
|
|
116
|
-
}), v && /* @__PURE__ */
|
|
115
|
+
}), v && /* @__PURE__ */ s("ul", {
|
|
117
116
|
ref: D,
|
|
118
117
|
id: T,
|
|
119
118
|
role: "listbox",
|
|
120
119
|
"aria-labelledby": w,
|
|
121
120
|
tabIndex: -1,
|
|
122
|
-
className: [
|
|
121
|
+
className: [t.list, S ? t.listUp : t.listDown].filter(Boolean).join(" "),
|
|
123
122
|
onKeyDown: P,
|
|
124
|
-
children: d.map((
|
|
125
|
-
id: `${T}-opt-${
|
|
123
|
+
children: d.map((n, r) => /* @__PURE__ */ c("li", {
|
|
124
|
+
id: `${T}-opt-${r}`,
|
|
126
125
|
role: "option",
|
|
127
|
-
"aria-selected":
|
|
128
|
-
"aria-disabled":
|
|
126
|
+
"aria-selected": n.value === f,
|
|
127
|
+
"aria-disabled": n.disabled,
|
|
129
128
|
className: [
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
t.option,
|
|
130
|
+
n.value === f ? t.optionSelected : null,
|
|
131
|
+
r === b ? t.optionActive : null,
|
|
132
|
+
n.disabled ? t.optionDisabled : null
|
|
134
133
|
].filter(Boolean).join(" "),
|
|
135
|
-
onMouseEnter: () => !
|
|
136
|
-
onClick: () => M(
|
|
137
|
-
children: [/* @__PURE__ */
|
|
138
|
-
className:
|
|
139
|
-
children: [/* @__PURE__ */
|
|
140
|
-
className:
|
|
141
|
-
children:
|
|
142
|
-
}),
|
|
143
|
-
className:
|
|
144
|
-
children:
|
|
134
|
+
onMouseEnter: () => !n.disabled && x(r),
|
|
135
|
+
onClick: () => M(n),
|
|
136
|
+
children: [/* @__PURE__ */ c("span", {
|
|
137
|
+
className: t.optionText,
|
|
138
|
+
children: [/* @__PURE__ */ s("span", {
|
|
139
|
+
className: t.optionLabel,
|
|
140
|
+
children: n.label
|
|
141
|
+
}), n.description && /* @__PURE__ */ s("span", {
|
|
142
|
+
className: t.optionDesc,
|
|
143
|
+
children: n.description
|
|
145
144
|
})]
|
|
146
|
-
}),
|
|
147
|
-
icon:
|
|
145
|
+
}), n.value === f && /* @__PURE__ */ s(e, {
|
|
146
|
+
icon: l,
|
|
148
147
|
size: "md",
|
|
149
148
|
"aria-hidden": !0,
|
|
150
|
-
className:
|
|
149
|
+
className: t.checkIcon
|
|
151
150
|
})]
|
|
152
|
-
},
|
|
151
|
+
}, n.value))
|
|
153
152
|
})]
|
|
154
153
|
});
|
|
155
154
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropdown.js","names":[],"sources":["../../../src/components/Dropdown/Dropdown.tsx"],"sourcesContent":["import {\n useState,\n useRef,\n useId,\n useEffect,\n useCallback,\n type KeyboardEvent,\n type HTMLAttributes,\n} from \"react\";\nimport { PanDown, Check } from \"@gnome-ui/icons\";\nimport { Icon } from \"../Icon\";\nimport styles from \"./Dropdown.module.css\";\n\nexport interface DropdownOption<V extends string = string> {\n /** The value submitted / returned on selection. */\n value: V;\n /** Display label shown in the list and trigger. */\n label: string;\n /** Optional descriptive text shown below the label. */\n description?: string;\n /** Whether the option is selectable. */\n disabled?: boolean;\n}\n\nexport interface DropdownProps<V extends string = string>\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** The list of selectable options. */\n options: DropdownOption<V>[];\n /** The currently selected value. */\n value?: V;\n /** Called when the user selects an option. */\n onChange?: (value: V) => void;\n /** Placeholder shown when no option is selected. */\n placeholder?: string;\n /** Accessible label for the control (used as `aria-label`). */\n \"aria-label\"?: string;\n /** Disables the entire control. */\n disabled?: boolean;\n}\n\n/**\n * Expandable option list following the Adwaita combo-row / drop-down pattern.\n *\n * - Fully keyboard-navigable: Space/Enter opens; ↑/↓ navigate; Enter selects; Escape closes.\n * - Uses `role=\"combobox\"` + `role=\"listbox\"` + `role=\"option\"` ARIA pattern.\n * - Closes when clicking outside or pressing Escape.\n * - The list flips above the trigger when there is not enough space below.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/drop-down-lists.html\n */\nexport function Dropdown<V extends string = string>({\n options,\n value,\n onChange,\n placeholder = \"Select an option\",\n disabled,\n className,\n ...props\n}: DropdownProps<V>) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const [flipUp, setFlipUp] = useState(false);\n\n const triggerId = useId();\n const listboxId = useId();\n const triggerRef = useRef<HTMLButtonElement>(null);\n const listRef = useRef<HTMLUListElement>(null);\n\n const selected = options.find((o) => o.value === value);\n\n // Compute flip direction when opening\n const computeFlip = useCallback(() => {\n if (!triggerRef.current) return;\n const rect = triggerRef.current.getBoundingClientRect();\n const spaceBelow = window.innerHeight - rect.bottom;\n const listH = Math.min(options.length * 48 + 8, 280);\n setFlipUp(spaceBelow < listH && rect.top > listH);\n }, [options.length]);\n\n const openList = useCallback(() => {\n if (disabled) return;\n computeFlip();\n setOpen(true);\n const idx = options.findIndex((o) => o.value === value && !o.disabled);\n setActiveIndex(idx >= 0 ? idx : options.findIndex((o) => !o.disabled));\n }, [disabled, computeFlip, options, value]);\n\n const closeList = useCallback(() => {\n setOpen(false);\n setActiveIndex(-1);\n triggerRef.current?.focus();\n }, []);\n\n const selectOption = useCallback(\n (opt: DropdownOption<V>) => {\n if (opt.disabled) return;\n onChange?.(opt.value);\n closeList();\n },\n [onChange, closeList]\n );\n\n // Scroll active item into view\n useEffect(() => {\n if (!open || activeIndex < 0 || !listRef.current) return;\n const item = listRef.current.children[activeIndex] as HTMLElement | undefined;\n item?.scrollIntoView({ block: \"nearest\" });\n }, [activeIndex, open]);\n\n // Close on outside click\n useEffect(() => {\n if (!open) return;\n const handler = (e: MouseEvent) => {\n if (\n !triggerRef.current?.contains(e.target as Node) &&\n !listRef.current?.contains(e.target as Node)\n ) {\n closeList();\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open, closeList]);\n\n // Trigger keyboard handler\n const handleTriggerKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n switch (e.key) {\n case \"Enter\":\n case \" \":\n case \"ArrowDown\":\n e.preventDefault();\n openList();\n break;\n case \"ArrowUp\":\n e.preventDefault();\n computeFlip();\n setOpen(true);\n setActiveIndex(\n options.reduce(\n (last, o, i) => (!o.disabled ? i : last),\n -1\n )\n );\n break;\n }\n },\n [openList, computeFlip, options]\n );\n\n // Listbox keyboard handler\n const handleListKeyDown = useCallback(\n (e: KeyboardEvent<HTMLUListElement>) => {\n const enabledIndexes = options\n .map((o, i) => ({ o, i }))\n .filter(({ o }) => !o.disabled)\n .map(({ i }) => i);\n\n const currentPos = enabledIndexes.indexOf(activeIndex);\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n const next = enabledIndexes[Math.min(currentPos + 1, enabledIndexes.length - 1)];\n setActiveIndex(next ?? activeIndex);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n const prev = enabledIndexes[Math.max(currentPos - 1, 0)];\n setActiveIndex(prev ?? activeIndex);\n break;\n }\n case \"Home\":\n e.preventDefault();\n setActiveIndex(enabledIndexes[0] ?? -1);\n break;\n case \"End\":\n e.preventDefault();\n setActiveIndex(enabledIndexes[enabledIndexes.length - 1] ?? -1);\n break;\n case \"Enter\":\n case \" \": {\n e.preventDefault();\n if (activeIndex >= 0) selectOption(options[activeIndex]);\n break;\n }\n case \"Escape\":\n case \"Tab\":\n closeList();\n break;\n }\n },\n [options, activeIndex, selectOption, closeList]\n );\n\n return (\n <div\n className={[styles.wrapper, className].filter(Boolean).join(\" \")}\n {...props}\n >\n {/* Trigger button */}\n <button\n ref={triggerRef}\n id={triggerId}\n type=\"button\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={listboxId}\n aria-activedescendant={\n open && activeIndex >= 0 ? `${listboxId}-opt-${activeIndex}` : undefined\n }\n disabled={disabled}\n className={[styles.trigger, open ? styles.triggerOpen : null]\n .filter(Boolean)\n .join(\" \")}\n onClick={() => (open ? closeList() : openList())}\n onKeyDown={handleTriggerKeyDown}\n >\n <span className={[styles.triggerLabel, !selected ? styles.placeholder : null]\n .filter(Boolean).join(\" \")}>\n {selected?.label ?? placeholder}\n </span>\n <Icon\n icon={PanDown}\n size=\"md\"\n aria-hidden\n className={[styles.chevron, open ? styles.chevronOpen : null]\n .filter(Boolean).join(\" \")}\n />\n </button>\n\n {/* Listbox */}\n {open && (\n <ul\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n aria-labelledby={triggerId}\n tabIndex={-1}\n className={[styles.list, flipUp ? styles.listUp : styles.listDown]\n .filter(Boolean)\n .join(\" \")}\n onKeyDown={handleListKeyDown}\n >\n {options.map((opt, i) => (\n <li\n key={opt.value}\n id={`${listboxId}-opt-${i}`}\n role=\"option\"\n aria-selected={opt.value === value}\n aria-disabled={opt.disabled}\n className={[\n styles.option,\n opt.value === value ? styles.optionSelected : null,\n i === activeIndex ? styles.optionActive : null,\n opt.disabled ? styles.optionDisabled : null,\n ]\n .filter(Boolean)\n .join(\" \")}\n onMouseEnter={() => !opt.disabled && setActiveIndex(i)}\n onClick={() => selectOption(opt)}\n >\n <span className={styles.optionText}>\n <span className={styles.optionLabel}>{opt.label}</span>\n {opt.description && (\n <span className={styles.optionDesc}>{opt.description}</span>\n )}\n </span>\n {opt.value === value && (\n <Icon icon={Check} size=\"md\" aria-hidden className={styles.checkIcon} />\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Dropdown.js","names":[],"sources":["../../../src/components/Dropdown/Dropdown.tsx"],"sourcesContent":["import {\n useState,\n useRef,\n useId,\n useEffect,\n useCallback,\n type KeyboardEvent,\n type HTMLAttributes,\n} from \"react\";\nimport { PanDown, Check } from \"@gnome-ui/icons\";\nimport { Icon } from \"../Icon\";\nimport styles from \"./Dropdown.module.css\";\n\nexport interface DropdownOption<V extends string = string> {\n /** The value submitted / returned on selection. */\n value: V;\n /** Display label shown in the list and trigger. */\n label: string;\n /** Optional descriptive text shown below the label. */\n description?: string;\n /** Whether the option is selectable. */\n disabled?: boolean;\n}\n\nexport interface DropdownProps<V extends string = string>\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** The list of selectable options. */\n options: DropdownOption<V>[];\n /** The currently selected value. */\n value?: V;\n /** Called when the user selects an option. */\n onChange?: (value: V) => void;\n /** Placeholder shown when no option is selected. */\n placeholder?: string;\n /** Accessible label for the control (used as `aria-label`). */\n \"aria-label\"?: string;\n /** Disables the entire control. */\n disabled?: boolean;\n}\n\n/**\n * Expandable option list following the Adwaita combo-row / drop-down pattern.\n *\n * - Fully keyboard-navigable: Space/Enter opens; ↑/↓ navigate; Enter selects; Escape closes.\n * - Uses `role=\"combobox\"` + `role=\"listbox\"` + `role=\"option\"` ARIA pattern.\n * - Closes when clicking outside or pressing Escape.\n * - The list flips above the trigger when there is not enough space below.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/drop-down-lists.html\n */\nexport function Dropdown<V extends string = string>({\n options,\n value,\n onChange,\n placeholder = \"Select an option\",\n disabled,\n className,\n ...props\n}: DropdownProps<V>) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const [flipUp, setFlipUp] = useState(false);\n\n const triggerId = useId();\n const listboxId = useId();\n const triggerRef = useRef<HTMLButtonElement>(null);\n const listRef = useRef<HTMLUListElement>(null);\n\n const selected = options.find((o) => o.value === value);\n\n // Compute flip direction when opening\n const computeFlip = useCallback(() => {\n if (!triggerRef.current) return;\n const rect = triggerRef.current.getBoundingClientRect();\n const spaceBelow = window.innerHeight - rect.bottom;\n const listH = Math.min(options.length * 48 + 8, 280);\n setFlipUp(spaceBelow < listH && rect.top > listH);\n }, [options.length]);\n\n const openList = useCallback(() => {\n if (disabled) return;\n computeFlip();\n setOpen(true);\n const idx = options.findIndex((o) => o.value === value && !o.disabled);\n setActiveIndex(idx >= 0 ? idx : options.findIndex((o) => !o.disabled));\n }, [disabled, computeFlip, options, value]);\n\n const closeList = useCallback(() => {\n setOpen(false);\n setActiveIndex(-1);\n triggerRef.current?.focus();\n }, []);\n\n const selectOption = useCallback(\n (opt: DropdownOption<V>) => {\n if (opt.disabled) return;\n onChange?.(opt.value);\n closeList();\n },\n [onChange, closeList]\n );\n\n // Scroll active item into view\n useEffect(() => {\n if (!open || activeIndex < 0 || !listRef.current) return;\n const item = listRef.current.children[activeIndex] as HTMLElement | undefined;\n item?.scrollIntoView({ block: \"nearest\" });\n }, [activeIndex, open]);\n\n // Close on outside click\n useEffect(() => {\n if (!open) return;\n const handler = (e: MouseEvent) => {\n if (\n !triggerRef.current?.contains(e.target as Node) &&\n !listRef.current?.contains(e.target as Node)\n ) {\n closeList();\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open, closeList]);\n\n // Trigger keyboard handler\n const handleTriggerKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n switch (e.key) {\n case \"Enter\":\n case \" \":\n case \"ArrowDown\":\n e.preventDefault();\n openList();\n break;\n case \"ArrowUp\":\n e.preventDefault();\n computeFlip();\n setOpen(true);\n setActiveIndex(\n options.reduce(\n (last, o, i) => (!o.disabled ? i : last),\n -1\n )\n );\n break;\n }\n },\n [openList, computeFlip, options]\n );\n\n // Listbox keyboard handler\n const handleListKeyDown = useCallback(\n (e: KeyboardEvent<HTMLUListElement>) => {\n const enabledIndexes = options\n .map((o, i) => ({ o, i }))\n .filter(({ o }) => !o.disabled)\n .map(({ i }) => i);\n\n const currentPos = enabledIndexes.indexOf(activeIndex);\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n const next = enabledIndexes[Math.min(currentPos + 1, enabledIndexes.length - 1)];\n setActiveIndex(next ?? activeIndex);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n const prev = enabledIndexes[Math.max(currentPos - 1, 0)];\n setActiveIndex(prev ?? activeIndex);\n break;\n }\n case \"Home\":\n e.preventDefault();\n setActiveIndex(enabledIndexes[0] ?? -1);\n break;\n case \"End\":\n e.preventDefault();\n setActiveIndex(enabledIndexes[enabledIndexes.length - 1] ?? -1);\n break;\n case \"Enter\":\n case \" \": {\n e.preventDefault();\n if (activeIndex >= 0) selectOption(options[activeIndex]);\n break;\n }\n case \"Escape\":\n case \"Tab\":\n closeList();\n break;\n }\n },\n [options, activeIndex, selectOption, closeList]\n );\n\n return (\n <div\n className={[styles.wrapper, className].filter(Boolean).join(\" \")}\n {...props}\n >\n {/* Trigger button */}\n <button\n ref={triggerRef}\n id={triggerId}\n type=\"button\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={listboxId}\n aria-activedescendant={\n open && activeIndex >= 0 ? `${listboxId}-opt-${activeIndex}` : undefined\n }\n disabled={disabled}\n className={[styles.trigger, open ? styles.triggerOpen : null]\n .filter(Boolean)\n .join(\" \")}\n onClick={() => (open ? closeList() : openList())}\n onKeyDown={handleTriggerKeyDown}\n >\n <span className={[styles.triggerLabel, !selected ? styles.placeholder : null]\n .filter(Boolean).join(\" \")}>\n {selected?.label ?? placeholder}\n </span>\n <Icon\n icon={PanDown}\n size=\"md\"\n aria-hidden\n className={[styles.chevron, open ? styles.chevronOpen : null]\n .filter(Boolean).join(\" \")}\n />\n </button>\n\n {/* Listbox */}\n {open && (\n <ul\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n aria-labelledby={triggerId}\n tabIndex={-1}\n className={[styles.list, flipUp ? styles.listUp : styles.listDown]\n .filter(Boolean)\n .join(\" \")}\n onKeyDown={handleListKeyDown}\n >\n {options.map((opt, i) => (\n <li\n key={opt.value}\n id={`${listboxId}-opt-${i}`}\n role=\"option\"\n aria-selected={opt.value === value}\n aria-disabled={opt.disabled}\n className={[\n styles.option,\n opt.value === value ? styles.optionSelected : null,\n i === activeIndex ? styles.optionActive : null,\n opt.disabled ? styles.optionDisabled : null,\n ]\n .filter(Boolean)\n .join(\" \")}\n onMouseEnter={() => !opt.disabled && setActiveIndex(i)}\n onClick={() => selectOption(opt)}\n >\n <span className={styles.optionText}>\n <span className={styles.optionLabel}>{opt.label}</span>\n {opt.description && (\n <span className={styles.optionDesc}>{opt.description}</span>\n )}\n </span>\n {opt.value === value && (\n <Icon icon={Check} size=\"md\" aria-hidden className={styles.checkIcon} />\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`react`);var t=(0,e.createContext)({locale:void 0,dir:`ltr`,numberFormat:void 0,dateTimeFormat:void 0});function n(){return(0,e.useContext)(t).locale}function r(){return(0,e.useContext)(t).dir}function i(n){let{locale:r,numberFormat:i}=(0,e.useContext)(t);return(0,e.useMemo)(()=>new Intl.NumberFormat(r,{...i,...n}),[r,i,n])}function a(n){let{locale:r,dateTimeFormat:i}=(0,e.useContext)(t);return(0,e.useMemo)(()=>new Intl.DateTimeFormat(r,{...i,...n}),[r,i,n])}exports.GnomeContext=t,exports.useDateTimeFormatter=a,exports.useDir=r,exports.useLocale=n,exports.useNumberFormatter=i;
|
|
2
|
+
//# sourceMappingURL=GnomeContext.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GnomeContext.cjs","names":[],"sources":["../../../src/components/GnomeProvider/GnomeContext.ts"],"sourcesContent":["import { createContext, useContext, useMemo } from \"react\";\n\nexport type GnomeDir = \"ltr\" | \"rtl\";\n\nexport interface GnomeContextValue {\n locale: string | undefined;\n dir: GnomeDir;\n numberFormat: Intl.NumberFormatOptions | undefined;\n dateTimeFormat: Intl.DateTimeFormatOptions | undefined;\n}\n\nexport const GnomeContext = createContext<GnomeContextValue>({\n locale: undefined,\n dir: \"ltr\",\n numberFormat: undefined,\n dateTimeFormat: undefined,\n});\n\n/** Returns the locale set by the nearest `GnomeProvider`, or `undefined` to use the browser locale. */\nexport function useLocale(): string | undefined {\n return useContext(GnomeContext).locale;\n}\n\n/** Returns the text direction set by the nearest `GnomeProvider`. Defaults to `\"ltr\"`. */\nexport function useDir(): GnomeDir {\n return useContext(GnomeContext).dir;\n}\n\n/** Returns an `Intl.NumberFormat` configured from `GnomeProvider` defaults plus local overrides. */\nexport function useNumberFormatter(\n options?: Intl.NumberFormatOptions,\n): Intl.NumberFormat {\n const { locale, numberFormat } = useContext(GnomeContext);\n\n return useMemo(\n () => new Intl.NumberFormat(locale, { ...numberFormat, ...options }),\n [locale, numberFormat, options],\n );\n}\n\n/** Returns an `Intl.DateTimeFormat` configured from `GnomeProvider` defaults plus local overrides. */\nexport function useDateTimeFormatter(\n options?: Intl.DateTimeFormatOptions,\n): Intl.DateTimeFormat {\n const { locale, dateTimeFormat } = useContext(GnomeContext);\n\n return useMemo(\n () => new Intl.DateTimeFormat(locale, { ...dateTimeFormat, ...options }),\n [locale, dateTimeFormat, options],\n );\n}\n"],"mappings":"0FAWA,IAAa,GAAA,EAAA,EAAA,eAAgD,CAC3D,OAAQ,IAAA,GACR,IAAK,MACL,aAAc,IAAA,GACd,eAAgB,IAAA,GACjB,CAAC,CAGF,SAAgB,GAAgC,CAC9C,OAAA,EAAA,EAAA,YAAkB,EAAa,CAAC,OAIlC,SAAgB,GAAmB,CACjC,OAAA,EAAA,EAAA,YAAkB,EAAa,CAAC,IAIlC,SAAgB,EACd,EACmB,CACnB,GAAM,CAAE,SAAQ,iBAAA,EAAA,EAAA,YAA4B,EAAa,CAEzD,OAAA,EAAA,EAAA,aACQ,IAAI,KAAK,aAAa,EAAQ,CAAE,GAAG,EAAc,GAAG,EAAS,CAAC,CACpE,CAAC,EAAQ,EAAc,EAAQ,CAChC,CAIH,SAAgB,EACd,EACqB,CACrB,GAAM,CAAE,SAAQ,mBAAA,EAAA,EAAA,YAA8B,EAAa,CAE3D,OAAA,EAAA,EAAA,aACQ,IAAI,KAAK,eAAe,EAAQ,CAAE,GAAG,EAAgB,GAAG,EAAS,CAAC,CACxE,CAAC,EAAQ,EAAgB,EAAQ,CAClC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type GnomeDir = "ltr" | "rtl";
|
|
2
|
+
export interface GnomeContextValue {
|
|
3
|
+
locale: string | undefined;
|
|
4
|
+
dir: GnomeDir;
|
|
5
|
+
numberFormat: Intl.NumberFormatOptions | undefined;
|
|
6
|
+
dateTimeFormat: Intl.DateTimeFormatOptions | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare const GnomeContext: import('react').Context<GnomeContextValue>;
|
|
9
|
+
/** Returns the locale set by the nearest `GnomeProvider`, or `undefined` to use the browser locale. */
|
|
10
|
+
export declare function useLocale(): string | undefined;
|
|
11
|
+
/** Returns the text direction set by the nearest `GnomeProvider`. Defaults to `"ltr"`. */
|
|
12
|
+
export declare function useDir(): GnomeDir;
|
|
13
|
+
/** Returns an `Intl.NumberFormat` configured from `GnomeProvider` defaults plus local overrides. */
|
|
14
|
+
export declare function useNumberFormatter(options?: Intl.NumberFormatOptions): Intl.NumberFormat;
|
|
15
|
+
/** Returns an `Intl.DateTimeFormat` configured from `GnomeProvider` defaults plus local overrides. */
|
|
16
|
+
export declare function useDateTimeFormatter(options?: Intl.DateTimeFormatOptions): Intl.DateTimeFormat;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createContext as e, useContext as t, useMemo as n } from "react";
|
|
2
|
+
//#region src/components/GnomeProvider/GnomeContext.ts
|
|
3
|
+
var r = e({
|
|
4
|
+
locale: void 0,
|
|
5
|
+
dir: "ltr",
|
|
6
|
+
numberFormat: void 0,
|
|
7
|
+
dateTimeFormat: void 0
|
|
8
|
+
});
|
|
9
|
+
function i() {
|
|
10
|
+
return t(r).locale;
|
|
11
|
+
}
|
|
12
|
+
function a() {
|
|
13
|
+
return t(r).dir;
|
|
14
|
+
}
|
|
15
|
+
function o(e) {
|
|
16
|
+
let { locale: i, numberFormat: a } = t(r);
|
|
17
|
+
return n(() => new Intl.NumberFormat(i, {
|
|
18
|
+
...a,
|
|
19
|
+
...e
|
|
20
|
+
}), [
|
|
21
|
+
i,
|
|
22
|
+
a,
|
|
23
|
+
e
|
|
24
|
+
]);
|
|
25
|
+
}
|
|
26
|
+
function s(e) {
|
|
27
|
+
let { locale: i, dateTimeFormat: a } = t(r);
|
|
28
|
+
return n(() => new Intl.DateTimeFormat(i, {
|
|
29
|
+
...a,
|
|
30
|
+
...e
|
|
31
|
+
}), [
|
|
32
|
+
i,
|
|
33
|
+
a,
|
|
34
|
+
e
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { r as GnomeContext, s as useDateTimeFormatter, a as useDir, i as useLocale, o as useNumberFormatter };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=GnomeContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GnomeContext.js","names":[],"sources":["../../../src/components/GnomeProvider/GnomeContext.ts"],"sourcesContent":["import { createContext, useContext, useMemo } from \"react\";\n\nexport type GnomeDir = \"ltr\" | \"rtl\";\n\nexport interface GnomeContextValue {\n locale: string | undefined;\n dir: GnomeDir;\n numberFormat: Intl.NumberFormatOptions | undefined;\n dateTimeFormat: Intl.DateTimeFormatOptions | undefined;\n}\n\nexport const GnomeContext = createContext<GnomeContextValue>({\n locale: undefined,\n dir: \"ltr\",\n numberFormat: undefined,\n dateTimeFormat: undefined,\n});\n\n/** Returns the locale set by the nearest `GnomeProvider`, or `undefined` to use the browser locale. */\nexport function useLocale(): string | undefined {\n return useContext(GnomeContext).locale;\n}\n\n/** Returns the text direction set by the nearest `GnomeProvider`. Defaults to `\"ltr\"`. */\nexport function useDir(): GnomeDir {\n return useContext(GnomeContext).dir;\n}\n\n/** Returns an `Intl.NumberFormat` configured from `GnomeProvider` defaults plus local overrides. */\nexport function useNumberFormatter(\n options?: Intl.NumberFormatOptions,\n): Intl.NumberFormat {\n const { locale, numberFormat } = useContext(GnomeContext);\n\n return useMemo(\n () => new Intl.NumberFormat(locale, { ...numberFormat, ...options }),\n [locale, numberFormat, options],\n );\n}\n\n/** Returns an `Intl.DateTimeFormat` configured from `GnomeProvider` defaults plus local overrides. */\nexport function useDateTimeFormatter(\n options?: Intl.DateTimeFormatOptions,\n): Intl.DateTimeFormat {\n const { locale, dateTimeFormat } = useContext(GnomeContext);\n\n return useMemo(\n () => new Intl.DateTimeFormat(locale, { ...dateTimeFormat, ...options }),\n [locale, dateTimeFormat, options],\n );\n}\n"],"mappings":";;AAWA,IAAa,IAAe,EAAiC;CAC3D,QAAQ,KAAA;CACR,KAAK;CACL,cAAc,KAAA;CACd,gBAAgB,KAAA;CACjB,CAAC;AAGF,SAAgB,IAAgC;AAC9C,QAAO,EAAW,EAAa,CAAC;;AAIlC,SAAgB,IAAmB;AACjC,QAAO,EAAW,EAAa,CAAC;;AAIlC,SAAgB,EACd,GACmB;CACnB,IAAM,EAAE,WAAQ,oBAAiB,EAAW,EAAa;AAEzD,QAAO,QACC,IAAI,KAAK,aAAa,GAAQ;EAAE,GAAG;EAAc,GAAG;EAAS,CAAC,EACpE;EAAC;EAAQ;EAAc;EAAQ,CAChC;;AAIH,SAAgB,EACd,GACqB;CACrB,IAAM,EAAE,WAAQ,sBAAmB,EAAW,EAAa;AAE3D,QAAO,QACC,IAAI,KAAK,eAAe,GAAQ;EAAE,GAAG;EAAgB,GAAG;EAAS,CAAC,EACxE;EAAC;EAAQ;EAAgB;EAAQ,CAClC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./GnomeContext.cjs`);let t=require(`react`),n=require(`react/jsx-runtime`);function r({locale:r,dir:i=`ltr`,numberFormat:a,dateTimeFormat:o,children:s}){let c=(0,t.useMemo)(()=>({locale:r,dir:i,numberFormat:a,dateTimeFormat:o}),[r,i,a,o]);return(0,n.jsx)(e.GnomeContext.Provider,{value:c,children:s})}exports.GnomeProvider=r;
|
|
2
|
+
//# sourceMappingURL=GnomeProvider.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GnomeProvider.cjs","names":[],"sources":["../../../src/components/GnomeProvider/GnomeProvider.tsx"],"sourcesContent":["import { useMemo, type ReactNode } from \"react\";\nimport { GnomeContext, type GnomeDir } from \"./GnomeContext\";\n\nexport interface GnomeProviderProps {\n /**\n * BCP 47 locale tag (e.g. `\"es-ES\"`, `\"de-DE\"`).\n * When omitted, components fall back to the browser locale.\n */\n locale?: string;\n /** Text direction. Defaults to `\"ltr\"`. */\n dir?: GnomeDir;\n /**\n * Global defaults for number formatting.\n *\n * Use `{ notation: \"compact\", compactDisplay: \"short\" }` for compact\n * values such as `1K`; omit it or set `{ notation: \"standard\" }` for\n * full values such as `1,000`.\n */\n numberFormat?: Intl.NumberFormatOptions;\n /** Global defaults for date/time formatting. */\n dateTimeFormat?: Intl.DateTimeFormatOptions;\n children: ReactNode;\n}\n\n/** Provides locale and text direction to all descendant gnome-ui components. */\nexport function GnomeProvider({\n locale,\n dir = \"ltr\",\n numberFormat,\n dateTimeFormat,\n children,\n}: GnomeProviderProps) {\n const value = useMemo(\n () => ({ locale, dir, numberFormat, dateTimeFormat }),\n [locale, dir, numberFormat, dateTimeFormat],\n );\n\n return (\n <GnomeContext.Provider value={value}>\n {children}\n </GnomeContext.Provider>\n );\n}\n"],"mappings":"+JAyBA,SAAgB,EAAc,CAC5B,SACA,MAAM,MACN,eACA,iBACA,YACqB,CACrB,IAAM,GAAA,EAAA,EAAA,cACG,CAAE,SAAQ,MAAK,eAAc,iBAAgB,EACpD,CAAC,EAAQ,EAAK,EAAc,EAAe,CAC5C,CAED,OACE,EAAA,EAAA,KAAC,EAAA,aAAa,SAAd,CAA8B,QAC3B,WACqB,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { GnomeDir } from './GnomeContext';
|
|
3
|
+
export interface GnomeProviderProps {
|
|
4
|
+
/**
|
|
5
|
+
* BCP 47 locale tag (e.g. `"es-ES"`, `"de-DE"`).
|
|
6
|
+
* When omitted, components fall back to the browser locale.
|
|
7
|
+
*/
|
|
8
|
+
locale?: string;
|
|
9
|
+
/** Text direction. Defaults to `"ltr"`. */
|
|
10
|
+
dir?: GnomeDir;
|
|
11
|
+
/**
|
|
12
|
+
* Global defaults for number formatting.
|
|
13
|
+
*
|
|
14
|
+
* Use `{ notation: "compact", compactDisplay: "short" }` for compact
|
|
15
|
+
* values such as `1K`; omit it or set `{ notation: "standard" }` for
|
|
16
|
+
* full values such as `1,000`.
|
|
17
|
+
*/
|
|
18
|
+
numberFormat?: Intl.NumberFormatOptions;
|
|
19
|
+
/** Global defaults for date/time formatting. */
|
|
20
|
+
dateTimeFormat?: Intl.DateTimeFormatOptions;
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
/** Provides locale and text direction to all descendant gnome-ui components. */
|
|
24
|
+
export declare function GnomeProvider({ locale, dir, numberFormat, dateTimeFormat, children, }: GnomeProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { GnomeContext as e } from "./GnomeContext.js";
|
|
2
|
+
import { useMemo as t } from "react";
|
|
3
|
+
import { jsx as n } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/GnomeProvider/GnomeProvider.tsx
|
|
5
|
+
function r({ locale: r, dir: i = "ltr", numberFormat: a, dateTimeFormat: o, children: s }) {
|
|
6
|
+
let c = t(() => ({
|
|
7
|
+
locale: r,
|
|
8
|
+
dir: i,
|
|
9
|
+
numberFormat: a,
|
|
10
|
+
dateTimeFormat: o
|
|
11
|
+
}), [
|
|
12
|
+
r,
|
|
13
|
+
i,
|
|
14
|
+
a,
|
|
15
|
+
o
|
|
16
|
+
]);
|
|
17
|
+
return /* @__PURE__ */ n(e.Provider, {
|
|
18
|
+
value: c,
|
|
19
|
+
children: s
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { r as GnomeProvider };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=GnomeProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GnomeProvider.js","names":[],"sources":["../../../src/components/GnomeProvider/GnomeProvider.tsx"],"sourcesContent":["import { useMemo, type ReactNode } from \"react\";\nimport { GnomeContext, type GnomeDir } from \"./GnomeContext\";\n\nexport interface GnomeProviderProps {\n /**\n * BCP 47 locale tag (e.g. `\"es-ES\"`, `\"de-DE\"`).\n * When omitted, components fall back to the browser locale.\n */\n locale?: string;\n /** Text direction. Defaults to `\"ltr\"`. */\n dir?: GnomeDir;\n /**\n * Global defaults for number formatting.\n *\n * Use `{ notation: \"compact\", compactDisplay: \"short\" }` for compact\n * values such as `1K`; omit it or set `{ notation: \"standard\" }` for\n * full values such as `1,000`.\n */\n numberFormat?: Intl.NumberFormatOptions;\n /** Global defaults for date/time formatting. */\n dateTimeFormat?: Intl.DateTimeFormatOptions;\n children: ReactNode;\n}\n\n/** Provides locale and text direction to all descendant gnome-ui components. */\nexport function GnomeProvider({\n locale,\n dir = \"ltr\",\n numberFormat,\n dateTimeFormat,\n children,\n}: GnomeProviderProps) {\n const value = useMemo(\n () => ({ locale, dir, numberFormat, dateTimeFormat }),\n [locale, dir, numberFormat, dateTimeFormat],\n );\n\n return (\n <GnomeContext.Provider value={value}>\n {children}\n </GnomeContext.Provider>\n );\n}\n"],"mappings":";;;;AAyBA,SAAgB,EAAc,EAC5B,WACA,SAAM,OACN,iBACA,mBACA,eACqB;CACrB,IAAM,IAAQ,SACL;EAAE;EAAQ;EAAK;EAAc;EAAgB,GACpD;EAAC;EAAQ;EAAK;EAAc;EAAe,CAC5C;AAED,QACE,kBAAC,EAAa,UAAd;EAA8B;EAC3B;EACqB,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../Icon/Icon.cjs`),t=require(
|
|
1
|
+
const e=require(`../Icon/Icon.cjs`),t=require(`../EntryRow/EntryRow.cjs`),n=require(`./PasswordEntryRow.module.css.cjs`);let r=require(`react`),i=require(`react/jsx-runtime`),a=require(`@gnome-ui/icons`);function o({trailing:o,disabled:s,...c}){let[l,u]=(0,r.useState)(!1),d=(0,i.jsx)(`button`,{type:`button`,className:n.default.revealButton,onClick:e=>{e.stopPropagation(),u(e=>!e)},"aria-label":l?`Conceal password`:`Reveal password`,"aria-pressed":l,disabled:s,children:(0,i.jsx)(e.Icon,{icon:l?a.ViewConceal:a.ViewReveal,size:`md`,"aria-hidden":!0})});return(0,i.jsx)(t.EntryRow,{...c,type:l?`text`:`password`,disabled:s,trailing:(0,i.jsxs)(i.Fragment,{children:[o,d]})})}exports.PasswordEntryRow=o;
|
|
2
2
|
//# sourceMappingURL=PasswordEntryRow.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PasswordEntryRow.cjs","names":[],"sources":["../../../src/components/PasswordEntryRow/PasswordEntryRow.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { ViewReveal, ViewConceal } from \"@gnome-ui/icons\";\nimport { EntryRow, type EntryRowProps } from \"../EntryRow\";\nimport styles from \"./PasswordEntryRow.module.css\";\n\nexport interface PasswordEntryRowProps extends Omit<EntryRowProps, \"type\" | \"trailing\"> {\n /** Additional trailing widgets placed before the reveal button. */\n trailing?: ReactNode;\n}\n\n/**\n * Password entry row with a built-in reveal/conceal toggle.\n *\n * Mirrors `AdwPasswordEntryRow` — an `EntryRow` variant that defaults to\n * `type=\"password\"` and provides a trailing icon button to reveal or conceal\n * the entered text. Use inside a `BoxedList` for password settings fields.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.PasswordEntryRow.html\n */\nexport function PasswordEntryRow({ trailing, disabled, ...props }: PasswordEntryRowProps) {\n const [revealed, setRevealed] = useState(false);\n\n const revealButton = (\n <button\n type=\"button\"\n className={styles.revealButton}\n onClick={(e) => {\n e.stopPropagation();\n setRevealed((v) => !v);\n }}\n aria-label={revealed ? \"Conceal password\" : \"Reveal password\"}\n aria-pressed={revealed}\n disabled={disabled}\n >\n <Icon\n icon={revealed ? ViewConceal : ViewReveal}\n size=\"md\"\n aria-hidden\n />\n </button>\n );\n\n return (\n <EntryRow\n {...props}\n type={revealed ? \"text\" : \"password\"}\n disabled={disabled}\n trailing={\n <>\n {trailing}\n {revealButton}\n </>\n }\n />\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"PasswordEntryRow.cjs","names":[],"sources":["../../../src/components/PasswordEntryRow/PasswordEntryRow.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { ViewReveal, ViewConceal } from \"@gnome-ui/icons\";\nimport { EntryRow, type EntryRowProps } from \"../EntryRow\";\nimport styles from \"./PasswordEntryRow.module.css\";\n\nexport interface PasswordEntryRowProps extends Omit<EntryRowProps, \"type\" | \"trailing\"> {\n /** Additional trailing widgets placed before the reveal button. */\n trailing?: ReactNode;\n}\n\n/**\n * Password entry row with a built-in reveal/conceal toggle.\n *\n * Mirrors `AdwPasswordEntryRow` — an `EntryRow` variant that defaults to\n * `type=\"password\"` and provides a trailing icon button to reveal or conceal\n * the entered text. Use inside a `BoxedList` for password settings fields.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.PasswordEntryRow.html\n */\nexport function PasswordEntryRow({ trailing, disabled, ...props }: PasswordEntryRowProps) {\n const [revealed, setRevealed] = useState(false);\n\n const revealButton = (\n <button\n type=\"button\"\n className={styles.revealButton}\n onClick={(e) => {\n e.stopPropagation();\n setRevealed((v) => !v);\n }}\n aria-label={revealed ? \"Conceal password\" : \"Reveal password\"}\n aria-pressed={revealed}\n disabled={disabled}\n >\n <Icon\n icon={revealed ? ViewConceal : ViewReveal}\n size=\"md\"\n aria-hidden\n />\n </button>\n );\n\n return (\n <EntryRow\n {...props}\n type={revealed ? \"text\" : \"password\"}\n disabled={disabled}\n trailing={\n <>\n {trailing}\n {revealButton}\n </>\n }\n />\n );\n}\n"],"mappings":"4MAoBA,SAAgB,EAAiB,CAAE,WAAU,WAAU,GAAG,GAAgC,CACxF,GAAM,CAAC,EAAU,IAAA,EAAA,EAAA,UAAwB,GAAM,CAEzC,GACJ,EAAA,EAAA,KAAC,SAAD,CACE,KAAK,SACL,UAAW,EAAA,QAAO,aAClB,QAAU,GAAM,CACd,EAAE,iBAAiB,CACnB,EAAa,GAAM,CAAC,EAAE,EAExB,aAAY,EAAW,mBAAqB,kBAC5C,eAAc,EACJ,qBAEV,EAAA,EAAA,KAAC,EAAA,KAAD,CACE,KAAM,EAAW,EAAA,YAAc,EAAA,WAC/B,KAAK,KACL,cAAA,GACA,CAAA,CACK,CAAA,CAGX,OACE,EAAA,EAAA,KAAC,EAAA,SAAD,CACE,GAAI,EACJ,KAAM,EAAW,OAAS,WAChB,WACV,UACE,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,CACG,EACA,EACA,CAAA,CAAA,CAEL,CAAA"}
|
|
@@ -1,32 +1,31 @@
|
|
|
1
1
|
import { Icon as e } from "../Icon/Icon.js";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import i from "
|
|
6
|
-
import {
|
|
7
|
-
import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
2
|
+
import { EntryRow as t } from "../EntryRow/EntryRow.js";
|
|
3
|
+
import n from "./PasswordEntryRow.module.css.js";
|
|
4
|
+
import { useState as r } from "react";
|
|
5
|
+
import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
+
import { ViewConceal as s, ViewReveal as c } from "@gnome-ui/icons";
|
|
8
7
|
//#region src/components/PasswordEntryRow/PasswordEntryRow.tsx
|
|
9
8
|
function l({ trailing: l, disabled: u, ...d }) {
|
|
10
|
-
let [f, p] =
|
|
9
|
+
let [f, p] = r(!1), m = /* @__PURE__ */ a("button", {
|
|
11
10
|
type: "button",
|
|
12
|
-
className:
|
|
11
|
+
className: n.revealButton,
|
|
13
12
|
onClick: (e) => {
|
|
14
13
|
e.stopPropagation(), p((e) => !e);
|
|
15
14
|
},
|
|
16
15
|
"aria-label": f ? "Conceal password" : "Reveal password",
|
|
17
16
|
"aria-pressed": f,
|
|
18
17
|
disabled: u,
|
|
19
|
-
children: /* @__PURE__ */
|
|
20
|
-
icon: f ?
|
|
18
|
+
children: /* @__PURE__ */ a(e, {
|
|
19
|
+
icon: f ? s : c,
|
|
21
20
|
size: "md",
|
|
22
21
|
"aria-hidden": !0
|
|
23
22
|
})
|
|
24
23
|
});
|
|
25
|
-
return /* @__PURE__ */
|
|
24
|
+
return /* @__PURE__ */ a(t, {
|
|
26
25
|
...d,
|
|
27
26
|
type: f ? "text" : "password",
|
|
28
27
|
disabled: u,
|
|
29
|
-
trailing: /* @__PURE__ */
|
|
28
|
+
trailing: /* @__PURE__ */ o(i, { children: [l, m] })
|
|
30
29
|
});
|
|
31
30
|
}
|
|
32
31
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PasswordEntryRow.js","names":[],"sources":["../../../src/components/PasswordEntryRow/PasswordEntryRow.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { ViewReveal, ViewConceal } from \"@gnome-ui/icons\";\nimport { EntryRow, type EntryRowProps } from \"../EntryRow\";\nimport styles from \"./PasswordEntryRow.module.css\";\n\nexport interface PasswordEntryRowProps extends Omit<EntryRowProps, \"type\" | \"trailing\"> {\n /** Additional trailing widgets placed before the reveal button. */\n trailing?: ReactNode;\n}\n\n/**\n * Password entry row with a built-in reveal/conceal toggle.\n *\n * Mirrors `AdwPasswordEntryRow` — an `EntryRow` variant that defaults to\n * `type=\"password\"` and provides a trailing icon button to reveal or conceal\n * the entered text. Use inside a `BoxedList` for password settings fields.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.PasswordEntryRow.html\n */\nexport function PasswordEntryRow({ trailing, disabled, ...props }: PasswordEntryRowProps) {\n const [revealed, setRevealed] = useState(false);\n\n const revealButton = (\n <button\n type=\"button\"\n className={styles.revealButton}\n onClick={(e) => {\n e.stopPropagation();\n setRevealed((v) => !v);\n }}\n aria-label={revealed ? \"Conceal password\" : \"Reveal password\"}\n aria-pressed={revealed}\n disabled={disabled}\n >\n <Icon\n icon={revealed ? ViewConceal : ViewReveal}\n size=\"md\"\n aria-hidden\n />\n </button>\n );\n\n return (\n <EntryRow\n {...props}\n type={revealed ? \"text\" : \"password\"}\n disabled={disabled}\n trailing={\n <>\n {trailing}\n {revealButton}\n </>\n }\n />\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"PasswordEntryRow.js","names":[],"sources":["../../../src/components/PasswordEntryRow/PasswordEntryRow.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { ViewReveal, ViewConceal } from \"@gnome-ui/icons\";\nimport { EntryRow, type EntryRowProps } from \"../EntryRow\";\nimport styles from \"./PasswordEntryRow.module.css\";\n\nexport interface PasswordEntryRowProps extends Omit<EntryRowProps, \"type\" | \"trailing\"> {\n /** Additional trailing widgets placed before the reveal button. */\n trailing?: ReactNode;\n}\n\n/**\n * Password entry row with a built-in reveal/conceal toggle.\n *\n * Mirrors `AdwPasswordEntryRow` — an `EntryRow` variant that defaults to\n * `type=\"password\"` and provides a trailing icon button to reveal or conceal\n * the entered text. Use inside a `BoxedList` for password settings fields.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.PasswordEntryRow.html\n */\nexport function PasswordEntryRow({ trailing, disabled, ...props }: PasswordEntryRowProps) {\n const [revealed, setRevealed] = useState(false);\n\n const revealButton = (\n <button\n type=\"button\"\n className={styles.revealButton}\n onClick={(e) => {\n e.stopPropagation();\n setRevealed((v) => !v);\n }}\n aria-label={revealed ? \"Conceal password\" : \"Reveal password\"}\n aria-pressed={revealed}\n disabled={disabled}\n >\n <Icon\n icon={revealed ? ViewConceal : ViewReveal}\n size=\"md\"\n aria-hidden\n />\n </button>\n );\n\n return (\n <EntryRow\n {...props}\n type={revealed ? \"text\" : \"password\"}\n disabled={disabled}\n trailing={\n <>\n {trailing}\n {revealButton}\n </>\n }\n />\n );\n}\n"],"mappings":";;;;;;;AAoBA,SAAgB,EAAiB,EAAE,aAAU,aAAU,GAAG,KAAgC;CACxF,IAAM,CAAC,GAAU,KAAe,EAAS,GAAM,EAEzC,IACJ,kBAAC,UAAD;EACE,MAAK;EACL,WAAW,EAAO;EAClB,UAAU,MAAM;AAEd,GADA,EAAE,iBAAiB,EACnB,GAAa,MAAM,CAAC,EAAE;;EAExB,cAAY,IAAW,qBAAqB;EAC5C,gBAAc;EACJ;YAEV,kBAAC,GAAD;GACE,MAAM,IAAW,IAAc;GAC/B,MAAK;GACL,eAAA;GACA,CAAA;EACK,CAAA;AAGX,QACE,kBAAC,GAAD;EACE,GAAI;EACJ,MAAM,IAAW,SAAS;EAChB;EACV,UACE,kBAAA,GAAA,EAAA,UAAA,CACG,GACA,EACA,EAAA,CAAA;EAEL,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../Icon/Icon.cjs`),t=require(
|
|
1
|
+
const e=require(`../Icon/Icon.cjs`),t=require(`../Spinner/Spinner.cjs`),n=require(`./SearchBar.module.css.cjs`);let r=require(`react`),i=require(`react-dom`),a=require(`react/jsx-runtime`),o=require(`@gnome-ui/icons`);function s({open:s,onClose:c,onClear:l,children:u,value:d,onChange:f,placeholder:p=`Search…`,disabled:m,inline:h=!1,className:g,suggestions:_,onSuggestionSelect:v,loadingSuggestions:y=!1,renderSuggestion:b,suggestionsLabel:x=`Suggestions`,...S}){let C=(0,r.useRef)(null),w=(0,r.useRef)(null),T=(0,r.useRef)(null),E=(0,r.useId)(),D=(0,r.useId)(),[O,k]=(0,r.useState)(-1),[A,j]=(0,r.useState)(null),M=(_?.length??0)>0,N=s&&(y||M);(0,r.useEffect)(()=>{k(-1)},[_]),(0,r.useEffect)(()=>{if(s){let e=requestAnimationFrame(()=>C.current?.focus());return()=>cancelAnimationFrame(e)}},[s]),(0,r.useEffect)(()=>{if(!N||!w.current){j(null);return}function e(){if(!w.current)return;let e=w.current.getBoundingClientRect();j({top:e.bottom,left:e.left,width:e.width})}return e(),window.addEventListener(`scroll`,e,{passive:!0,capture:!0}),window.addEventListener(`resize`,e,{passive:!0}),()=>{window.removeEventListener(`scroll`,e,{capture:!0}),window.removeEventListener(`resize`,e)}},[N]),(0,r.useEffect)(()=>{O<0||!T.current||T.current.children[O]?.scrollIntoView({block:`nearest`})},[O]);let P=(0,r.useCallback)(e=>{if(N&&M&&_){if(e.key===`ArrowDown`){e.preventDefault(),k(e=>Math.min(e+1,_.length-1));return}if(e.key===`ArrowUp`){e.preventDefault(),k(e=>Math.max(e-1,-1));return}if(e.key===`Enter`&&O>=0){e.preventDefault(),v?.(_[O]);return}}e.key===`Escape`&&(e.preventDefault(),c?.()),S.onKeyDown?.(e)},[c,S,N,M,_,O,v]),F=d!==void 0&&d!==``,I=O>=0&&_?.[O]?`${D}-${_[O].id}`:void 0,L=N&&A?(0,i.createPortal)((0,a.jsxs)(`ul`,{ref:T,role:`listbox`,"aria-label":x,id:E,className:n.default.suggestionsPanel,style:{top:A.top,left:A.left,width:A.width},onMouseDown:e=>e.preventDefault(),children:[y&&(0,a.jsx)(`li`,{role:`presentation`,className:n.default.suggestionsLoading,children:(0,a.jsx)(t.Spinner,{size:`sm`,label:`Loading suggestions…`})}),!y&&_?.map((e,t)=>(0,a.jsx)(`li`,{id:`${D}-${e.id}`,role:`option`,"aria-selected":t===O,className:[n.default.suggestionItem,t===O?n.default.suggestionItemActive:null].filter(Boolean).join(` `),onMouseEnter:()=>k(t),onMouseLeave:()=>k(-1),onClick:()=>v?.(e),children:b?b(e):e.label},e.id))]}),document.body):null;return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(`div`,{ref:w,className:[n.default.wrapper,s?n.default.open:null,g].filter(Boolean).join(` `),"aria-hidden":!s,children:[(0,a.jsxs)(`div`,{className:[n.default.bar,h?n.default.inline:null,m?n.default.disabled:null].filter(Boolean).join(` `),children:[(0,a.jsx)(`span`,{className:n.default.searchIcon,"aria-hidden":!0,children:(0,a.jsx)(e.Icon,{icon:o.Search,size:`md`})}),(0,a.jsx)(`input`,{ref:C,type:`search`,role:`combobox`,"aria-autocomplete":`list`,"aria-expanded":N,"aria-controls":N?E:void 0,"aria-activedescendant":I,value:d,onChange:f,placeholder:p,disabled:m,tabIndex:s?void 0:-1,className:n.default.input,onKeyDown:P,...S}),F&&(0,a.jsx)(`button`,{type:`button`,className:n.default.clearButton,"aria-label":`Clear search`,tabIndex:s?void 0:-1,disabled:m,onClick:()=>{l?.(),C.current?.focus()},children:(0,a.jsx)(e.Icon,{icon:o.Close,size:`md`})})]}),u&&(0,a.jsx)(`div`,{className:n.default.filterRow,children:u})]}),typeof document<`u`&&L]})}exports.SearchBar=s;
|
|
2
2
|
//# sourceMappingURL=SearchBar.cjs.map
|