@gnome-ui/react 1.40.0 → 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.
Files changed (90) hide show
  1. package/README.md +42 -0
  2. package/dist/components/AvatarRotator/AvatarRotator.cjs +2 -0
  3. package/dist/components/AvatarRotator/AvatarRotator.cjs.map +1 -0
  4. package/dist/components/AvatarRotator/AvatarRotator.d.ts +36 -0
  5. package/dist/components/AvatarRotator/AvatarRotator.js +84 -0
  6. package/dist/components/AvatarRotator/AvatarRotator.js.map +1 -0
  7. package/dist/components/AvatarRotator/AvatarRotator.module.css.cjs +2 -0
  8. package/dist/components/AvatarRotator/AvatarRotator.module.css.cjs.map +1 -0
  9. package/dist/components/AvatarRotator/AvatarRotator.module.css.js +13 -0
  10. package/dist/components/AvatarRotator/AvatarRotator.module.css.js.map +1 -0
  11. package/dist/components/AvatarRotator/index.d.ts +2 -0
  12. package/dist/components/AvatarRotator.cjs +1 -0
  13. package/dist/components/AvatarRotator.d.ts +2 -0
  14. package/dist/components/AvatarRotator.js +2 -0
  15. package/dist/components/Chip/Chip.cjs +1 -1
  16. package/dist/components/Chip/Chip.cjs.map +1 -1
  17. package/dist/components/Chip/Chip.js +19 -19
  18. package/dist/components/ContributionGraph/ContributionGraph.cjs +1 -1
  19. package/dist/components/ContributionGraph/ContributionGraph.cjs.map +1 -1
  20. package/dist/components/ContributionGraph/ContributionGraph.js +108 -117
  21. package/dist/components/ContributionGraph/ContributionGraph.js.map +1 -1
  22. package/dist/components/CountDownTimer/CountDownTimer.cjs +1 -1
  23. package/dist/components/CountDownTimer/CountDownTimer.cjs.map +1 -1
  24. package/dist/components/CountDownTimer/CountDownTimer.js +37 -28
  25. package/dist/components/CountDownTimer/CountDownTimer.js.map +1 -1
  26. package/dist/components/Dropdown/Dropdown.cjs +1 -1
  27. package/dist/components/Dropdown/Dropdown.cjs.map +1 -1
  28. package/dist/components/Dropdown/Dropdown.js +45 -46
  29. package/dist/components/Dropdown/Dropdown.js.map +1 -1
  30. package/dist/components/GnomeProvider/GnomeContext.cjs +2 -0
  31. package/dist/components/GnomeProvider/GnomeContext.cjs.map +1 -0
  32. package/dist/components/GnomeProvider/GnomeContext.d.ts +16 -0
  33. package/dist/components/GnomeProvider/GnomeContext.js +40 -0
  34. package/dist/components/GnomeProvider/GnomeContext.js.map +1 -0
  35. package/dist/components/GnomeProvider/GnomeProvider.cjs +2 -0
  36. package/dist/components/GnomeProvider/GnomeProvider.cjs.map +1 -0
  37. package/dist/components/GnomeProvider/GnomeProvider.d.ts +24 -0
  38. package/dist/components/GnomeProvider/GnomeProvider.js +25 -0
  39. package/dist/components/GnomeProvider/GnomeProvider.js.map +1 -0
  40. package/dist/components/PasswordEntryRow/PasswordEntryRow.cjs +1 -1
  41. package/dist/components/PasswordEntryRow/PasswordEntryRow.cjs.map +1 -1
  42. package/dist/components/PasswordEntryRow/PasswordEntryRow.js +11 -12
  43. package/dist/components/PasswordEntryRow/PasswordEntryRow.js.map +1 -1
  44. package/dist/components/SearchBar/SearchBar.cjs +1 -1
  45. package/dist/components/SearchBar/SearchBar.cjs.map +1 -1
  46. package/dist/components/SearchBar/SearchBar.js +38 -39
  47. package/dist/components/SearchBar/SearchBar.js.map +1 -1
  48. package/dist/components/Sidebar/SidebarSection.cjs +1 -1
  49. package/dist/components/Sidebar/SidebarSection.cjs.map +1 -1
  50. package/dist/components/Sidebar/SidebarSection.js +41 -42
  51. package/dist/components/Sidebar/SidebarSection.js.map +1 -1
  52. package/dist/components/Toast/Toast.cjs +1 -1
  53. package/dist/components/Toast/Toast.cjs.map +1 -1
  54. package/dist/components/Toast/Toast.js +18 -18
  55. package/dist/hooks/index.d.ts +2 -0
  56. package/dist/hooks.cjs +1 -1
  57. package/dist/hooks.js +3 -2
  58. package/dist/index.cjs +1 -1
  59. package/dist/index.d.ts +8 -0
  60. package/dist/index.js +90 -86
  61. package/dist/style.css +1 -1
  62. package/package.json +16 -1
  63. package/dist/icons/dist/icons/Check.cjs +0 -2
  64. package/dist/icons/dist/icons/Check.cjs.map +0 -1
  65. package/dist/icons/dist/icons/Check.js +0 -12
  66. package/dist/icons/dist/icons/Check.js.map +0 -1
  67. package/dist/icons/dist/icons/Close.cjs +0 -2
  68. package/dist/icons/dist/icons/Close.cjs.map +0 -1
  69. package/dist/icons/dist/icons/Close.js +0 -12
  70. package/dist/icons/dist/icons/Close.js.map +0 -1
  71. package/dist/icons/dist/icons/PanDown.cjs +0 -2
  72. package/dist/icons/dist/icons/PanDown.cjs.map +0 -1
  73. package/dist/icons/dist/icons/PanDown.js +0 -12
  74. package/dist/icons/dist/icons/PanDown.js.map +0 -1
  75. package/dist/icons/dist/icons/PanUp.cjs +0 -2
  76. package/dist/icons/dist/icons/PanUp.cjs.map +0 -1
  77. package/dist/icons/dist/icons/PanUp.js +0 -12
  78. package/dist/icons/dist/icons/PanUp.js.map +0 -1
  79. package/dist/icons/dist/icons/Search.cjs +0 -2
  80. package/dist/icons/dist/icons/Search.cjs.map +0 -1
  81. package/dist/icons/dist/icons/Search.js +0 -12
  82. package/dist/icons/dist/icons/Search.js.map +0 -1
  83. package/dist/icons/dist/icons/ViewConceal.cjs +0 -2
  84. package/dist/icons/dist/icons/ViewConceal.cjs.map +0 -1
  85. package/dist/icons/dist/icons/ViewConceal.js +0 -12
  86. package/dist/icons/dist/icons/ViewConceal.js.map +0 -1
  87. package/dist/icons/dist/icons/ViewReveal.cjs +0 -2
  88. package/dist/icons/dist/icons/ViewReveal.cjs.map +0 -1
  89. package/dist/icons/dist/icons/ViewReveal.js +0 -12
  90. package/dist/icons/dist/icons/ViewReveal.js.map +0 -1
@@ -1,2 +1,2 @@
1
- const e=require(`../Icon/Icon.cjs`),t=require(`../../icons/dist/icons/PanDown.cjs`),n=require(`../../icons/dist/icons/Check.cjs`),r=require(`./Dropdown.module.css.cjs`);let i=require(`react`),a=require(`react/jsx-runtime`);function o({options:o,value:s,onChange:c,placeholder:l=`Select an option`,disabled:u,className:d,...f}){let[p,m]=(0,i.useState)(!1),[h,g]=(0,i.useState)(-1),[_,v]=(0,i.useState)(!1),y=(0,i.useId)(),b=(0,i.useId)(),x=(0,i.useRef)(null),S=(0,i.useRef)(null),C=o.find(e=>e.value===s),w=(0,i.useCallback)(()=>{if(!x.current)return;let e=x.current.getBoundingClientRect(),t=window.innerHeight-e.bottom,n=Math.min(o.length*48+8,280);v(t<n&&e.top>n)},[o.length]),T=(0,i.useCallback)(()=>{if(u)return;w(),m(!0);let e=o.findIndex(e=>e.value===s&&!e.disabled);g(e>=0?e:o.findIndex(e=>!e.disabled))},[u,w,o,s]),E=(0,i.useCallback)(()=>{m(!1),g(-1),x.current?.focus()},[]),D=(0,i.useCallback)(e=>{e.disabled||(c?.(e.value),E())},[c,E]);(0,i.useEffect)(()=>{!p||h<0||!S.current||S.current.children[h]?.scrollIntoView({block:`nearest`})},[h,p]),(0,i.useEffect)(()=>{if(!p)return;let e=e=>{!x.current?.contains(e.target)&&!S.current?.contains(e.target)&&E()};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[p,E]);let O=(0,i.useCallback)(e=>{switch(e.key){case`Enter`:case` `:case`ArrowDown`:e.preventDefault(),T();break;case`ArrowUp`:e.preventDefault(),w(),m(!0),g(o.reduce((e,t,n)=>t.disabled?e:n,-1));break}},[T,w,o]),k=(0,i.useCallback)(e=>{let t=o.map((e,t)=>({o:e,i:t})).filter(({o:e})=>!e.disabled).map(({i:e})=>e),n=t.indexOf(h);switch(e.key){case`ArrowDown`:{e.preventDefault();let r=t[Math.min(n+1,t.length-1)];g(r??h);break}case`ArrowUp`:{e.preventDefault();let r=t[Math.max(n-1,0)];g(r??h);break}case`Home`:e.preventDefault(),g(t[0]??-1);break;case`End`:e.preventDefault(),g(t[t.length-1]??-1);break;case`Enter`:case` `:e.preventDefault(),h>=0&&D(o[h]);break;case`Escape`:case`Tab`:E();break}},[o,h,D,E]);return(0,a.jsxs)(`div`,{className:[r.default.wrapper,d].filter(Boolean).join(` `),...f,children:[(0,a.jsxs)(`button`,{ref:x,id:y,type:`button`,role:`combobox`,"aria-haspopup":`listbox`,"aria-expanded":p,"aria-controls":b,"aria-activedescendant":p&&h>=0?`${b}-opt-${h}`:void 0,disabled:u,className:[r.default.trigger,p?r.default.triggerOpen:null].filter(Boolean).join(` `),onClick:()=>p?E():T(),onKeyDown:O,children:[(0,a.jsx)(`span`,{className:[r.default.triggerLabel,C?null:r.default.placeholder].filter(Boolean).join(` `),children:C?.label??l}),(0,a.jsx)(e.Icon,{icon:t.e,size:`md`,"aria-hidden":!0,className:[r.default.chevron,p?r.default.chevronOpen:null].filter(Boolean).join(` `)})]}),p&&(0,a.jsx)(`ul`,{ref:S,id:b,role:`listbox`,"aria-labelledby":y,tabIndex:-1,className:[r.default.list,_?r.default.listUp:r.default.listDown].filter(Boolean).join(` `),onKeyDown:k,children:o.map((t,i)=>(0,a.jsxs)(`li`,{id:`${b}-opt-${i}`,role:`option`,"aria-selected":t.value===s,"aria-disabled":t.disabled,className:[r.default.option,t.value===s?r.default.optionSelected:null,i===h?r.default.optionActive:null,t.disabled?r.default.optionDisabled:null].filter(Boolean).join(` `),onMouseEnter:()=>!t.disabled&&g(i),onClick:()=>D(t),children:[(0,a.jsxs)(`span`,{className:r.default.optionText,children:[(0,a.jsx)(`span`,{className:r.default.optionLabel,children:t.label}),t.description&&(0,a.jsx)(`span`,{className:r.default.optionDesc,children:t.description})]}),t.value===s&&(0,a.jsx)(e.Icon,{icon:n.e,size:`md`,"aria-hidden":!0,className:r.default.checkIcon})]},t.value))})]})}exports.Dropdown=o;
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 { e as t } from "../../icons/dist/icons/PanDown.js";
3
- import { e as n } from "../../icons/dist/icons/Check.js";
4
- import r from "./Dropdown.module.css.js";
5
- import { useCallback as i, useEffect as a, useId as o, useRef as s, useState as c } from "react";
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] = c(!1), [b, x] = c(-1), [S, C] = c(!1), w = o(), T = o(), E = s(null), D = s(null), O = d.find((e) => e.value === f), k = i(() => {
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 = i(() => {
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 = i(() => {
22
+ ]), j = n(() => {
24
23
  y(!1), x(-1), E.current?.focus();
25
- }, []), M = i((e) => {
24
+ }, []), M = n((e) => {
26
25
  e.disabled || (p?.(e.value), j());
27
26
  }, [p, j]);
28
- a(() => {
27
+ r(() => {
29
28
  !v || b < 0 || !D.current || D.current.children[b]?.scrollIntoView({ block: "nearest" });
30
- }, [b, v]), a(() => {
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 = i((e) => {
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 = i((e) => {
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__ */ u("div", {
92
- className: [r.wrapper, g].filter(Boolean).join(" "),
90
+ return /* @__PURE__ */ c("div", {
91
+ className: [t.wrapper, g].filter(Boolean).join(" "),
93
92
  ..._,
94
- children: [/* @__PURE__ */ u("button", {
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: [r.trigger, v ? r.triggerOpen : null].filter(Boolean).join(" "),
103
+ className: [t.trigger, v ? t.triggerOpen : null].filter(Boolean).join(" "),
105
104
  onClick: () => v ? j() : A(),
106
105
  onKeyDown: N,
107
- children: [/* @__PURE__ */ l("span", {
108
- className: [r.triggerLabel, O ? null : r.placeholder].filter(Boolean).join(" "),
106
+ children: [/* @__PURE__ */ s("span", {
107
+ className: [t.triggerLabel, O ? null : t.placeholder].filter(Boolean).join(" "),
109
108
  children: O?.label ?? m
110
- }), /* @__PURE__ */ l(e, {
111
- icon: t,
109
+ }), /* @__PURE__ */ s(e, {
110
+ icon: u,
112
111
  size: "md",
113
112
  "aria-hidden": !0,
114
- className: [r.chevron, v ? r.chevronOpen : null].filter(Boolean).join(" ")
113
+ className: [t.chevron, v ? t.chevronOpen : null].filter(Boolean).join(" ")
115
114
  })]
116
- }), v && /* @__PURE__ */ l("ul", {
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: [r.list, S ? r.listUp : r.listDown].filter(Boolean).join(" "),
121
+ className: [t.list, S ? t.listUp : t.listDown].filter(Boolean).join(" "),
123
122
  onKeyDown: P,
124
- children: d.map((t, i) => /* @__PURE__ */ u("li", {
125
- id: `${T}-opt-${i}`,
123
+ children: d.map((n, r) => /* @__PURE__ */ c("li", {
124
+ id: `${T}-opt-${r}`,
126
125
  role: "option",
127
- "aria-selected": t.value === f,
128
- "aria-disabled": t.disabled,
126
+ "aria-selected": n.value === f,
127
+ "aria-disabled": n.disabled,
129
128
  className: [
130
- r.option,
131
- t.value === f ? r.optionSelected : null,
132
- i === b ? r.optionActive : null,
133
- t.disabled ? r.optionDisabled : null
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: () => !t.disabled && x(i),
136
- onClick: () => M(t),
137
- children: [/* @__PURE__ */ u("span", {
138
- className: r.optionText,
139
- children: [/* @__PURE__ */ l("span", {
140
- className: r.optionLabel,
141
- children: t.label
142
- }), t.description && /* @__PURE__ */ l("span", {
143
- className: r.optionDesc,
144
- children: t.description
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
- }), t.value === f && /* @__PURE__ */ l(e, {
147
- icon: n,
145
+ }), n.value === f && /* @__PURE__ */ s(e, {
146
+ icon: l,
148
147
  size: "md",
149
148
  "aria-hidden": !0,
150
- className: r.checkIcon
149
+ className: t.checkIcon
151
150
  })]
152
- }, t.value))
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(`../../icons/dist/icons/ViewReveal.cjs`),n=require(`../../icons/dist/icons/ViewConceal.cjs`),r=require(`../EntryRow/EntryRow.cjs`),i=require(`./PasswordEntryRow.module.css.cjs`);let a=require(`react`),o=require(`react/jsx-runtime`);function s({trailing:s,disabled:c,...l}){let[u,d]=(0,a.useState)(!1),f=(0,o.jsx)(`button`,{type:`button`,className:i.default.revealButton,onClick:e=>{e.stopPropagation(),d(e=>!e)},"aria-label":u?`Conceal password`:`Reveal password`,"aria-pressed":u,disabled:c,children:(0,o.jsx)(e.Icon,{icon:u?n.e:t.e,size:`md`,"aria-hidden":!0})});return(0,o.jsx)(r.EntryRow,{...l,type:u?`text`:`password`,disabled:c,trailing:(0,o.jsxs)(o.Fragment,{children:[s,f]})})}exports.PasswordEntryRow=s;
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":"sRAoBA,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,EAAc,EAAA,EAC/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
+ {"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 { e as t } from "../../icons/dist/icons/ViewReveal.js";
3
- import { e as n } from "../../icons/dist/icons/ViewConceal.js";
4
- import { EntryRow as r } from "../EntryRow/EntryRow.js";
5
- import i from "./PasswordEntryRow.module.css.js";
6
- import { useState as a } from "react";
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] = a(!1), m = /* @__PURE__ */ s("button", {
9
+ let [f, p] = r(!1), m = /* @__PURE__ */ a("button", {
11
10
  type: "button",
12
- className: i.revealButton,
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__ */ s(e, {
20
- icon: f ? n : t,
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__ */ s(r, {
24
+ return /* @__PURE__ */ a(t, {
26
25
  ...d,
27
26
  type: f ? "text" : "password",
28
27
  disabled: u,
29
- trailing: /* @__PURE__ */ c(o, { children: [l, m] })
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":";;;;;;;;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
+ {"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(`../../icons/dist/icons/Close.cjs`),n=require(`../../icons/dist/icons/Search.cjs`),r=require(`../Spinner/Spinner.cjs`),i=require(`./SearchBar.module.css.cjs`);let a=require(`react`),o=require(`react-dom`),s=require(`react/jsx-runtime`);function c({open:c,onClose:l,onClear:u,children:d,value:f,onChange:p,placeholder:m=`Search…`,disabled:h,inline:g=!1,className:_,suggestions:v,onSuggestionSelect:y,loadingSuggestions:b=!1,renderSuggestion:x,suggestionsLabel:S=`Suggestions`,...C}){let w=(0,a.useRef)(null),T=(0,a.useRef)(null),E=(0,a.useRef)(null),D=(0,a.useId)(),O=(0,a.useId)(),[k,A]=(0,a.useState)(-1),[j,M]=(0,a.useState)(null),N=(v?.length??0)>0,P=c&&(b||N);(0,a.useEffect)(()=>{A(-1)},[v]),(0,a.useEffect)(()=>{if(c){let e=requestAnimationFrame(()=>w.current?.focus());return()=>cancelAnimationFrame(e)}},[c]),(0,a.useEffect)(()=>{if(!P||!T.current){M(null);return}function e(){if(!T.current)return;let e=T.current.getBoundingClientRect();M({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)}},[P]),(0,a.useEffect)(()=>{k<0||!E.current||E.current.children[k]?.scrollIntoView({block:`nearest`})},[k]);let F=(0,a.useCallback)(e=>{if(P&&N&&v){if(e.key===`ArrowDown`){e.preventDefault(),A(e=>Math.min(e+1,v.length-1));return}if(e.key===`ArrowUp`){e.preventDefault(),A(e=>Math.max(e-1,-1));return}if(e.key===`Enter`&&k>=0){e.preventDefault(),y?.(v[k]);return}}e.key===`Escape`&&(e.preventDefault(),l?.()),C.onKeyDown?.(e)},[l,C,P,N,v,k,y]),I=f!==void 0&&f!==``,L=k>=0&&v?.[k]?`${O}-${v[k].id}`:void 0,R=P&&j?(0,o.createPortal)((0,s.jsxs)(`ul`,{ref:E,role:`listbox`,"aria-label":S,id:D,className:i.default.suggestionsPanel,style:{top:j.top,left:j.left,width:j.width},onMouseDown:e=>e.preventDefault(),children:[b&&(0,s.jsx)(`li`,{role:`presentation`,className:i.default.suggestionsLoading,children:(0,s.jsx)(r.Spinner,{size:`sm`,label:`Loading suggestions…`})}),!b&&v?.map((e,t)=>(0,s.jsx)(`li`,{id:`${O}-${e.id}`,role:`option`,"aria-selected":t===k,className:[i.default.suggestionItem,t===k?i.default.suggestionItemActive:null].filter(Boolean).join(` `),onMouseEnter:()=>A(t),onMouseLeave:()=>A(-1),onClick:()=>y?.(e),children:x?x(e):e.label},e.id))]}),document.body):null;return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(`div`,{ref:T,className:[i.default.wrapper,c?i.default.open:null,_].filter(Boolean).join(` `),"aria-hidden":!c,children:[(0,s.jsxs)(`div`,{className:[i.default.bar,g?i.default.inline:null,h?i.default.disabled:null].filter(Boolean).join(` `),children:[(0,s.jsx)(`span`,{className:i.default.searchIcon,"aria-hidden":!0,children:(0,s.jsx)(e.Icon,{icon:n.e,size:`md`})}),(0,s.jsx)(`input`,{ref:w,type:`search`,role:`combobox`,"aria-autocomplete":`list`,"aria-expanded":P,"aria-controls":P?D:void 0,"aria-activedescendant":L,value:f,onChange:p,placeholder:m,disabled:h,tabIndex:c?void 0:-1,className:i.default.input,onKeyDown:F,...C}),I&&(0,s.jsx)(`button`,{type:`button`,className:i.default.clearButton,"aria-label":`Clear search`,tabIndex:c?void 0:-1,disabled:h,onClick:()=>{u?.(),w.current?.focus()},children:(0,s.jsx)(e.Icon,{icon:t.e,size:`md`})})]}),d&&(0,s.jsx)(`div`,{className:i.default.filterRow,children:d})]}),typeof document<`u`&&R]})}exports.SearchBar=c;
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