@gnome-ui/react 1.45.2 → 1.46.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 (31) hide show
  1. package/dist/components/AboutDialog/AboutDialog.cjs +1 -1
  2. package/dist/components/AboutDialog/AboutDialog.cjs.map +1 -1
  3. package/dist/components/AboutDialog/AboutDialog.js +105 -106
  4. package/dist/components/AboutDialog/AboutDialog.js.map +1 -1
  5. package/dist/components/BottomSheet/BottomSheet.cjs +1 -1
  6. package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
  7. package/dist/components/BottomSheet/BottomSheet.js +36 -36
  8. package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
  9. package/dist/components/Dialog/Dialog.cjs +1 -1
  10. package/dist/components/Dialog/Dialog.cjs.map +1 -1
  11. package/dist/components/Dialog/Dialog.js +50 -51
  12. package/dist/components/Dialog/Dialog.js.map +1 -1
  13. package/dist/components/Dialog/dialogUtils.cjs +1 -1
  14. package/dist/components/Dialog/dialogUtils.cjs.map +1 -1
  15. package/dist/components/Dialog/dialogUtils.d.ts +6 -0
  16. package/dist/components/Dialog/dialogUtils.js +11 -2
  17. package/dist/components/Dialog/dialogUtils.js.map +1 -1
  18. package/dist/components/Dropdown/Dropdown.cjs +1 -1
  19. package/dist/components/Dropdown/Dropdown.cjs.map +1 -1
  20. package/dist/components/Dropdown/Dropdown.js +88 -84
  21. package/dist/components/Dropdown/Dropdown.js.map +1 -1
  22. package/dist/components/PasswordEntryRow/PasswordEntryRow.cjs +1 -1
  23. package/dist/components/PasswordEntryRow/PasswordEntryRow.cjs.map +1 -1
  24. package/dist/components/PasswordEntryRow/PasswordEntryRow.d.ts +1 -1
  25. package/dist/components/PasswordEntryRow/PasswordEntryRow.js +10 -9
  26. package/dist/components/PasswordEntryRow/PasswordEntryRow.js.map +1 -1
  27. package/dist/components/ShortcutsDialog/ShortcutsDialog.cjs +1 -1
  28. package/dist/components/ShortcutsDialog/ShortcutsDialog.cjs.map +1 -1
  29. package/dist/components/ShortcutsDialog/ShortcutsDialog.js +74 -74
  30. package/dist/components/ShortcutsDialog/ShortcutsDialog.js.map +1 -1
  31. package/package.json +1 -1
@@ -1,149 +1,153 @@
1
1
  import { Icon as e } from "../Icon/Icon.js";
2
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";
3
+ import { useCallback as n, useEffect as r, useId as i, useLayoutEffect as a, useRef as o, useState as s } from "react";
4
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
5
+ import { Check as u, PanDown as d } from "@gnome-ui/icons";
6
6
  //#region src/components/Dropdown/Dropdown.tsx
7
- var d = ({ options: d, value: f, onChange: p, placeholder: m = "Select an option", disabled: h, className: g, ..._ }) => {
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(() => {
9
- if (!E.current) return;
10
- let e = E.current.getBoundingClientRect(), t = window.innerHeight - e.bottom, n = Math.min(d.length * 48 + 8, 280);
11
- C(t < n && e.top > n);
12
- }, [d.length]), A = n(() => {
13
- if (h) return;
14
- k(), y(!0);
15
- let e = d.findIndex((e) => e.value === f && !e.disabled);
16
- x(e >= 0 ? e : d.findIndex((e) => !e.disabled));
7
+ var f = ({ options: f, value: p, onChange: m, placeholder: h = "Select an option", disabled: g, className: _, ...v }) => {
8
+ let [y, b] = s(!1), [x, S] = s(-1), [C, w] = s(!1), T = i(), E = i(), D = o(null), O = o(null), k = f.find((e) => e.value === p), A = n(() => {
9
+ if (!D.current) return;
10
+ let e = D.current.getBoundingClientRect(), t = window.innerHeight - e.bottom, n = Math.min(f.length * 48 + 8, 280);
11
+ w(t < n && e.top > n);
12
+ }, [f.length]), j = n(() => {
13
+ if (g) return;
14
+ A(), b(!0);
15
+ let e = f.findIndex((e) => e.value === p && !e.disabled);
16
+ S(e >= 0 ? e : f.findIndex((e) => !e.disabled));
17
17
  }, [
18
- h,
19
- k,
20
- d,
21
- f
22
- ]), j = n(() => {
23
- y(!1), x(-1), E.current?.focus();
24
- }, []), M = n((e) => {
25
- e.disabled || (p?.(e.value), j());
26
- }, [p, j]);
27
- r(() => {
28
- !v || b < 0 || !D.current || D.current.children[b]?.scrollIntoView({ block: "nearest" });
29
- }, [b, v]), r(() => {
30
- if (!v) return;
18
+ g,
19
+ A,
20
+ f,
21
+ p
22
+ ]), M = n(() => {
23
+ b(!1), S(-1), D.current?.focus();
24
+ }, []), N = n((e) => {
25
+ e.disabled || (m?.(e.value), M());
26
+ }, [m, M]);
27
+ a(() => {
28
+ if (!y || !D.current || !O.current) return;
29
+ let e = D.current.getBoundingClientRect(), t = O.current.offsetHeight;
30
+ w(window.innerHeight - e.bottom < t && e.top > t);
31
+ }, [y]), r(() => {
32
+ !y || x < 0 || !O.current || O.current.children[x]?.scrollIntoView({ block: "nearest" });
33
+ }, [x, y]), r(() => {
34
+ if (!y) return;
31
35
  let e = (e) => {
32
- !E.current?.contains(e.target) && !D.current?.contains(e.target) && j();
36
+ !D.current?.contains(e.target) && !O.current?.contains(e.target) && M();
33
37
  };
34
38
  return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
35
- }, [v, j]);
36
- let N = n((e) => {
39
+ }, [y, M]);
40
+ let P = n((e) => {
37
41
  switch (e.key) {
38
42
  case "Enter":
39
43
  case " ":
40
44
  case "ArrowDown":
41
- e.preventDefault(), A();
45
+ e.preventDefault(), j();
42
46
  break;
43
47
  case "ArrowUp":
44
- e.preventDefault(), k(), y(!0), x(d.reduce((e, t, n) => t.disabled ? e : n, -1));
48
+ e.preventDefault(), A(), b(!0), S(f.reduce((e, t, n) => t.disabled ? e : n, -1));
45
49
  break;
46
50
  }
47
51
  }, [
52
+ j,
48
53
  A,
49
- k,
50
- d
51
- ]), P = n((e) => {
52
- let t = d.map((e, t) => ({
54
+ f
55
+ ]), F = n((e) => {
56
+ let t = f.map((e, t) => ({
53
57
  o: e,
54
58
  i: t
55
- })).filter(({ o: e }) => !e.disabled).map(({ i: e }) => e), n = t.indexOf(b);
59
+ })).filter(({ o: e }) => !e.disabled).map(({ i: e }) => e), n = t.indexOf(x);
56
60
  switch (e.key) {
57
61
  case "ArrowDown": {
58
62
  e.preventDefault();
59
63
  let r = t[Math.min(n + 1, t.length - 1)];
60
- x(r ?? b);
64
+ S(r ?? x);
61
65
  break;
62
66
  }
63
67
  case "ArrowUp": {
64
68
  e.preventDefault();
65
69
  let r = t[Math.max(n - 1, 0)];
66
- x(r ?? b);
70
+ S(r ?? x);
67
71
  break;
68
72
  }
69
73
  case "Home":
70
- e.preventDefault(), x(t[0] ?? -1);
74
+ e.preventDefault(), S(t[0] ?? -1);
71
75
  break;
72
76
  case "End":
73
- e.preventDefault(), x(t[t.length - 1] ?? -1);
77
+ e.preventDefault(), S(t[t.length - 1] ?? -1);
74
78
  break;
75
79
  case "Enter":
76
80
  case " ":
77
- e.preventDefault(), b >= 0 && M(d[b]);
81
+ e.preventDefault(), x >= 0 && N(f[x]);
78
82
  break;
79
83
  case "Escape":
80
84
  case "Tab":
81
- j();
85
+ M();
82
86
  break;
83
87
  }
84
88
  }, [
85
- d,
86
- b,
87
- M,
88
- j
89
+ f,
90
+ x,
91
+ N,
92
+ M
89
93
  ]);
90
- return /* @__PURE__ */ c("div", {
91
- className: [t.wrapper, g].filter(Boolean).join(" "),
92
- ..._,
93
- children: [/* @__PURE__ */ c("button", {
94
- ref: E,
95
- id: w,
94
+ return /* @__PURE__ */ l("div", {
95
+ className: [t.wrapper, _].filter(Boolean).join(" "),
96
+ ...v,
97
+ children: [/* @__PURE__ */ l("button", {
98
+ ref: D,
99
+ id: T,
96
100
  type: "button",
97
101
  role: "combobox",
98
102
  "aria-haspopup": "listbox",
99
- "aria-expanded": v,
100
- "aria-controls": T,
101
- "aria-activedescendant": v && b >= 0 ? `${T}-opt-${b}` : void 0,
102
- disabled: h,
103
- className: [t.trigger, v ? t.triggerOpen : null].filter(Boolean).join(" "),
104
- onClick: () => v ? j() : A(),
105
- onKeyDown: N,
106
- children: [/* @__PURE__ */ s("span", {
107
- className: [t.triggerLabel, O ? null : t.placeholder].filter(Boolean).join(" "),
108
- children: O?.label ?? m
109
- }), /* @__PURE__ */ s(e, {
110
- icon: u,
103
+ "aria-expanded": y,
104
+ "aria-controls": E,
105
+ "aria-activedescendant": y && x >= 0 ? `${E}-opt-${x}` : void 0,
106
+ disabled: g,
107
+ className: [t.trigger, y ? t.triggerOpen : null].filter(Boolean).join(" "),
108
+ onClick: () => y ? M() : j(),
109
+ onKeyDown: P,
110
+ children: [/* @__PURE__ */ c("span", {
111
+ className: [t.triggerLabel, k ? null : t.placeholder].filter(Boolean).join(" "),
112
+ children: k?.label ?? h
113
+ }), /* @__PURE__ */ c(e, {
114
+ icon: d,
111
115
  size: "md",
112
116
  "aria-hidden": !0,
113
- className: [t.chevron, v ? t.chevronOpen : null].filter(Boolean).join(" ")
117
+ className: [t.chevron, y ? t.chevronOpen : null].filter(Boolean).join(" ")
114
118
  })]
115
- }), v && /* @__PURE__ */ s("ul", {
116
- ref: D,
117
- id: T,
119
+ }), y && /* @__PURE__ */ c("ul", {
120
+ ref: O,
121
+ id: E,
118
122
  role: "listbox",
119
- "aria-labelledby": w,
123
+ "aria-labelledby": T,
120
124
  tabIndex: -1,
121
- className: [t.list, S ? t.listUp : t.listDown].filter(Boolean).join(" "),
122
- onKeyDown: P,
123
- children: d.map((n, r) => /* @__PURE__ */ c("li", {
124
- id: `${T}-opt-${r}`,
125
+ className: [t.list, C ? t.listUp : t.listDown].filter(Boolean).join(" "),
126
+ onKeyDown: F,
127
+ children: f.map((n, r) => /* @__PURE__ */ l("li", {
128
+ id: `${E}-opt-${r}`,
125
129
  role: "option",
126
- "aria-selected": n.value === f,
130
+ "aria-selected": n.value === p,
127
131
  "aria-disabled": n.disabled,
128
132
  className: [
129
133
  t.option,
130
- n.value === f ? t.optionSelected : null,
131
- r === b ? t.optionActive : null,
134
+ n.value === p ? t.optionSelected : null,
135
+ r === x ? t.optionActive : null,
132
136
  n.disabled ? t.optionDisabled : null
133
137
  ].filter(Boolean).join(" "),
134
- onMouseEnter: () => !n.disabled && x(r),
135
- onClick: () => M(n),
136
- children: [/* @__PURE__ */ c("span", {
138
+ onMouseEnter: () => !n.disabled && S(r),
139
+ onClick: () => N(n),
140
+ children: [/* @__PURE__ */ l("span", {
137
141
  className: t.optionText,
138
- children: [/* @__PURE__ */ s("span", {
142
+ children: [/* @__PURE__ */ c("span", {
139
143
  className: t.optionLabel,
140
144
  children: n.label
141
- }), n.description && /* @__PURE__ */ s("span", {
145
+ }), n.description && /* @__PURE__ */ c("span", {
142
146
  className: t.optionDesc,
143
147
  children: n.description
144
148
  })]
145
- }), n.value === f && /* @__PURE__ */ s(e, {
146
- icon: l,
149
+ }), n.value === p && /* @__PURE__ */ c(e, {
150
+ icon: u,
147
151
  size: "md",
148
152
  "aria-hidden": !0,
149
153
  className: t.checkIcon
@@ -153,6 +157,6 @@ var d = ({ options: d, value: f, onChange: p, placeholder: m = "Select an option
153
157
  });
154
158
  };
155
159
  //#endregion
156
- export { d as Dropdown };
160
+ export { f as Dropdown };
157
161
 
158
162
  //# sourceMappingURL=Dropdown.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Dropdown.js","names":[],"sources":["../../../src/components/Dropdown/Dropdown.tsx"],"sourcesContent":["import { Check, PanDown } from '@gnome-ui/icons';\nimport {\n type HTMLAttributes,\n type KeyboardEvent,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\n\nimport { Icon } from '../Icon';\n\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 const 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) {\n return;\n }\n\n const rect = triggerRef.current.getBoundingClientRect();\n const spaceBelow = window.innerHeight - rect.bottom;\n const listH = Math.min(options.length * 48 + 8, 280);\n\n setFlipUp(spaceBelow < listH && rect.top > listH);\n }, [options.length]);\n\n const openList = useCallback(() => {\n if (disabled) {\n return;\n }\n\n computeFlip();\n setOpen(true);\n const idx = options.findIndex((o) => o.value === value && !o.disabled);\n\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) {\n return;\n }\n\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) {\n return;\n }\n\n const item = listRef.current.children[activeIndex] as HTMLElement | undefined;\n\n item?.scrollIntoView({ block: 'nearest' });\n }, [activeIndex, open]);\n\n // Close on outside click\n useEffect(() => {\n if (!open) {\n return;\n }\n\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\n document.addEventListener('mousedown', handler);\n\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(options.reduce((last, o, i) => (!o.disabled ? i : last), -1));\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\n setActiveIndex(next ?? activeIndex);\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n const prev = enabledIndexes[Math.max(currentPos - 1, 0)];\n\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) {\n selectOption(options[activeIndex]);\n }\n\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 className={[styles.wrapper, className].filter(Boolean).join(' ')} {...props}>\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].filter(Boolean).join(' ')}\n onClick={() => (open ? closeList() : openList())}\n onKeyDown={handleTriggerKeyDown}\n >\n <span\n className={[styles.triggerLabel, !selected ? styles.placeholder : null]\n .filter(Boolean)\n .join(' ')}\n >\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].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 && <span className={styles.optionDesc}>{opt.description}</span>}\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 { Check, PanDown } from '@gnome-ui/icons';\nimport {\n type HTMLAttributes,\n type KeyboardEvent,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\n\nimport { Icon } from '../Icon';\n\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 const 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) {\n return;\n }\n\n const rect = triggerRef.current.getBoundingClientRect();\n const spaceBelow = window.innerHeight - rect.bottom;\n const listH = Math.min(options.length * 48 + 8, 280);\n\n setFlipUp(spaceBelow < listH && rect.top > listH);\n }, [options.length]);\n\n const openList = useCallback(() => {\n if (disabled) {\n return;\n }\n\n computeFlip();\n setOpen(true);\n const idx = options.findIndex((o) => o.value === value && !o.disabled);\n\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) {\n return;\n }\n\n onChange?.(opt.value);\n closeList();\n },\n [onChange, closeList],\n );\n\n // Refine the flip with the real list height once it has rendered — the\n // pre-open estimate can't account for option descriptions or wrapped labels.\n useLayoutEffect(() => {\n if (!open || !triggerRef.current || !listRef.current) {\n return;\n }\n\n const rect = triggerRef.current.getBoundingClientRect();\n const listH = listRef.current.offsetHeight;\n\n setFlipUp(window.innerHeight - rect.bottom < listH && rect.top > listH);\n }, [open]);\n\n // Scroll active item into view\n useEffect(() => {\n if (!open || activeIndex < 0 || !listRef.current) {\n return;\n }\n\n const item = listRef.current.children[activeIndex] as HTMLElement | undefined;\n\n item?.scrollIntoView({ block: 'nearest' });\n }, [activeIndex, open]);\n\n // Close on outside click\n useEffect(() => {\n if (!open) {\n return;\n }\n\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\n document.addEventListener('mousedown', handler);\n\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(options.reduce((last, o, i) => (!o.disabled ? i : last), -1));\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\n setActiveIndex(next ?? activeIndex);\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n const prev = enabledIndexes[Math.max(currentPos - 1, 0)];\n\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) {\n selectOption(options[activeIndex]);\n }\n\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 className={[styles.wrapper, className].filter(Boolean).join(' ')} {...props}>\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].filter(Boolean).join(' ')}\n onClick={() => (open ? closeList() : openList())}\n onKeyDown={handleTriggerKeyDown}\n >\n <span\n className={[styles.triggerLabel, !selected ? styles.placeholder : null]\n .filter(Boolean)\n .join(' ')}\n >\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].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 && <span className={styles.optionDesc}>{opt.description}</span>}\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,2 +1,2 @@
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");var 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;
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");var o=({trailing:o,disabled:s,autoComplete:c=`current-password`,...l})=>{let[u,d]=(0,r.useState)(!1),f=(0,i.jsx)(`button`,{type:`button`,className:n.default.revealButton,onClick:e=>{e.stopPropagation(),d(e=>!e)},"aria-label":u?`Conceal password`:`Reveal password`,"aria-pressed":u,disabled:s,children:(0,i.jsx)(e.Icon,{icon:u?a.ViewConceal:a.ViewReveal,size:`md`,"aria-hidden":!0})});return(0,i.jsx)(t.EntryRow,{...l,autoComplete:c,type:u?`text`:`password`,disabled:s,trailing:(0,i.jsxs)(i.Fragment,{children:[o,f]})})};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 { ViewConceal, ViewReveal } from '@gnome-ui/icons';\nimport { type ReactNode, useState } from 'react';\n\nimport { EntryRow, type EntryRowProps } from '../EntryRow';\nimport { Icon } from '../Icon';\n\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 const 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 icon={revealed ? ViewConceal : ViewReveal} size=\"md\" aria-hidden />\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":"4MAsBA,IAAa,GAAoB,CAAE,WAAU,WAAU,GAAG,KAAmC,CAC3F,GAAM,CAAC,EAAU,IAAA,EAAA,EAAA,UAAwB,EAAK,EAExC,GACJ,EAAA,EAAA,KAAC,SAAD,CACE,KAAK,SACL,UAAW,EAAA,QAAO,aAClB,QAAU,GAAM,CACd,EAAE,gBAAgB,EAClB,EAAa,GAAM,CAAC,CAAC,CACvB,EACA,aAAY,EAAW,mBAAqB,kBAC5C,eAAc,EACJ,qBAEV,EAAA,EAAA,KAAC,EAAA,KAAD,CAAM,KAAM,EAAW,EAAA,YAAc,EAAA,WAAY,KAAK,KAAK,cAAA,EAAa,CAAA,CAClE,CAAA,EAGV,OACE,EAAA,EAAA,KAAC,EAAA,SAAD,CACE,GAAI,EACJ,KAAM,EAAW,OAAS,WAChB,WACV,UACE,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,CACG,EACA,CACD,CAAA,CAAA,CAEL,CAAA,CAEL"}
1
+ {"version":3,"file":"PasswordEntryRow.cjs","names":[],"sources":["../../../src/components/PasswordEntryRow/PasswordEntryRow.tsx"],"sourcesContent":["import { ViewConceal, ViewReveal } from '@gnome-ui/icons';\nimport { type ReactNode, useState } from 'react';\n\nimport { EntryRow, type EntryRowProps } from '../EntryRow';\nimport { Icon } from '../Icon';\n\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 const PasswordEntryRow = ({\n trailing,\n disabled,\n // \"current-password\" lets password managers and mobile keyboards offer the\n // saved credential; pass \"new-password\" for registration / change forms.\n autoComplete = 'current-password',\n ...props\n}: 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 icon={revealed ? ViewConceal : ViewReveal} size=\"md\" aria-hidden />\n </button>\n );\n\n return (\n <EntryRow\n {...props}\n autoComplete={autoComplete}\n type={revealed ? 'text' : 'password'}\n disabled={disabled}\n trailing={\n <>\n {trailing}\n {revealButton}\n </>\n }\n />\n );\n};\n"],"mappings":"4MAsBA,IAAa,GAAoB,CAC/B,WACA,WAGA,eAAe,mBACf,GAAG,KACwB,CAC3B,GAAM,CAAC,EAAU,IAAA,EAAA,EAAA,UAAwB,EAAK,EAExC,GACJ,EAAA,EAAA,KAAC,SAAD,CACE,KAAK,SACL,UAAW,EAAA,QAAO,aAClB,QAAU,GAAM,CACd,EAAE,gBAAgB,EAClB,EAAa,GAAM,CAAC,CAAC,CACvB,EACA,aAAY,EAAW,mBAAqB,kBAC5C,eAAc,EACJ,qBAEV,EAAA,EAAA,KAAC,EAAA,KAAD,CAAM,KAAM,EAAW,EAAA,YAAc,EAAA,WAAY,KAAK,KAAK,cAAA,EAAa,CAAA,CAClE,CAAA,EAGV,OACE,EAAA,EAAA,KAAC,EAAA,SAAD,CACE,GAAI,EACU,eACd,KAAM,EAAW,OAAS,WAChB,WACV,UACE,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,CACG,EACA,CACD,CAAA,CAAA,CAEL,CAAA,CAEL"}
@@ -13,4 +13,4 @@ export interface PasswordEntryRowProps extends Omit<EntryRowProps, 'type' | 'tra
13
13
  *
14
14
  * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.PasswordEntryRow.html
15
15
  */
16
- export declare const PasswordEntryRow: ({ trailing, disabled, ...props }: PasswordEntryRowProps) => import("react/jsx-runtime").JSX.Element;
16
+ export declare const PasswordEntryRow: ({ trailing, disabled, autoComplete, ...props }: PasswordEntryRowProps) => import("react/jsx-runtime").JSX.Element;
@@ -5,27 +5,28 @@ import { useState as r } from "react";
5
5
  import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
6
6
  import { ViewConceal as s, ViewReveal as c } from "@gnome-ui/icons";
7
7
  //#region src/components/PasswordEntryRow/PasswordEntryRow.tsx
8
- var l = ({ trailing: l, disabled: u, ...d }) => {
9
- let [f, p] = r(!1), m = /* @__PURE__ */ a("button", {
8
+ var l = ({ trailing: l, disabled: u, autoComplete: d = "current-password", ...f }) => {
9
+ let [p, m] = r(!1), h = /* @__PURE__ */ a("button", {
10
10
  type: "button",
11
11
  className: n.revealButton,
12
12
  onClick: (e) => {
13
- e.stopPropagation(), p((e) => !e);
13
+ e.stopPropagation(), m((e) => !e);
14
14
  },
15
- "aria-label": f ? "Conceal password" : "Reveal password",
16
- "aria-pressed": f,
15
+ "aria-label": p ? "Conceal password" : "Reveal password",
16
+ "aria-pressed": p,
17
17
  disabled: u,
18
18
  children: /* @__PURE__ */ a(e, {
19
- icon: f ? s : c,
19
+ icon: p ? s : c,
20
20
  size: "md",
21
21
  "aria-hidden": !0
22
22
  })
23
23
  });
24
24
  return /* @__PURE__ */ a(t, {
25
- ...d,
26
- type: f ? "text" : "password",
25
+ ...f,
26
+ autoComplete: d,
27
+ type: p ? "text" : "password",
27
28
  disabled: u,
28
- trailing: /* @__PURE__ */ o(i, { children: [l, m] })
29
+ trailing: /* @__PURE__ */ o(i, { children: [l, h] })
29
30
  });
30
31
  };
31
32
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"PasswordEntryRow.js","names":[],"sources":["../../../src/components/PasswordEntryRow/PasswordEntryRow.tsx"],"sourcesContent":["import { ViewConceal, ViewReveal } from '@gnome-ui/icons';\nimport { type ReactNode, useState } from 'react';\n\nimport { EntryRow, type EntryRowProps } from '../EntryRow';\nimport { Icon } from '../Icon';\n\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 const 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 icon={revealed ? ViewConceal : ViewReveal} size=\"md\" aria-hidden />\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":";;;;;;;AAsBA,IAAa,KAAoB,EAAE,aAAU,aAAU,GAAG,QAAmC;CAC3F,IAAM,CAAC,GAAU,KAAe,EAAS,EAAK,GAExC,IACJ,kBAAC,UAAD;EACE,MAAK;EACL,WAAW,EAAO;EAClB,UAAU,MAAM;GAEd,AADA,EAAE,gBAAgB,GAClB,GAAa,MAAM,CAAC,CAAC;EACvB;EACA,cAAY,IAAW,qBAAqB;EAC5C,gBAAc;EACJ;YAEV,kBAAC,GAAD;GAAM,MAAM,IAAW,IAAc;GAAY,MAAK;GAAK,eAAA;EAAa,CAAA;CAClE,CAAA;CAGV,OACE,kBAAC,GAAD;EACE,GAAI;EACJ,MAAM,IAAW,SAAS;EAChB;EACV,UACE,kBAAA,GAAA,EAAA,UAAA,CACG,GACA,CACD,EAAA,CAAA;CAEL,CAAA;AAEL"}
1
+ {"version":3,"file":"PasswordEntryRow.js","names":[],"sources":["../../../src/components/PasswordEntryRow/PasswordEntryRow.tsx"],"sourcesContent":["import { ViewConceal, ViewReveal } from '@gnome-ui/icons';\nimport { type ReactNode, useState } from 'react';\n\nimport { EntryRow, type EntryRowProps } from '../EntryRow';\nimport { Icon } from '../Icon';\n\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 const PasswordEntryRow = ({\n trailing,\n disabled,\n // \"current-password\" lets password managers and mobile keyboards offer the\n // saved credential; pass \"new-password\" for registration / change forms.\n autoComplete = 'current-password',\n ...props\n}: 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 icon={revealed ? ViewConceal : ViewReveal} size=\"md\" aria-hidden />\n </button>\n );\n\n return (\n <EntryRow\n {...props}\n autoComplete={autoComplete}\n type={revealed ? 'text' : 'password'}\n disabled={disabled}\n trailing={\n <>\n {trailing}\n {revealButton}\n </>\n }\n />\n );\n};\n"],"mappings":";;;;;;;AAsBA,IAAa,KAAoB,EAC/B,aACA,aAGA,kBAAe,oBACf,GAAG,QACwB;CAC3B,IAAM,CAAC,GAAU,KAAe,EAAS,EAAK,GAExC,IACJ,kBAAC,UAAD;EACE,MAAK;EACL,WAAW,EAAO;EAClB,UAAU,MAAM;GAEd,AADA,EAAE,gBAAgB,GAClB,GAAa,MAAM,CAAC,CAAC;EACvB;EACA,cAAY,IAAW,qBAAqB;EAC5C,gBAAc;EACJ;YAEV,kBAAC,GAAD;GAAM,MAAM,IAAW,IAAc;GAAY,MAAK;GAAK,eAAA;EAAa,CAAA;CAClE,CAAA;CAGV,OACE,kBAAC,GAAD;EACE,GAAI;EACU;EACd,MAAM,IAAW,SAAS;EAChB;EACV,UACE,kBAAA,GAAA,EAAA,UAAA,CACG,GACA,CACD,EAAA,CAAA;CAEL,CAAA;AAEL"}
@@ -1,2 +1,2 @@
1
- const e=require("./ShortcutsDialog.module.css.cjs");let t=require("react"),n=require("react-dom"),r=require("react/jsx-runtime");var i=`button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex="-1"])`,a=({open:a,onClose:o,title:s=`Keyboard Shortcuts`,sections:c})=>{let[l,u]=(0,t.useState)(``),d=(0,t.useRef)(null),f=(0,t.useRef)(null),p=(0,t.useRef)(null),m=(0,t.useRef)(`shortcuts-title-${Math.random().toString(36).slice(2,9)}`);(0,t.useEffect)(()=>{a?(p.current=document.activeElement,requestAnimationFrame(()=>f.current?.focus())):(u(``),p.current?.focus())},[a]);let h=(0,t.useCallback)(e=>{if(e.key===`Escape`){e.preventDefault(),o();return}if(e.key!==`Tab`)return;let t=Array.from(d.current?.querySelectorAll(i)??[]);if(t.length===0)return;let n=t[0],r=t[t.length-1];e.shiftKey?document.activeElement===n&&(e.preventDefault(),r.focus()):document.activeElement===r&&(e.preventDefault(),n.focus())},[o]);if(!a)return null;let g=l.trim().toLowerCase(),_=c.map(e=>({...e,shortcuts:e.shortcuts.filter(({description:e,keys:t})=>!g||e.toLowerCase().includes(g)||t.some(e=>e.toLowerCase().includes(g)))})).filter(e=>e.shortcuts.length>0),v=(0,r.jsx)(`div`,{className:e.default.backdrop,onClick:o,"aria-hidden":`true`,children:(0,r.jsxs)(`div`,{ref:d,role:`dialog`,"aria-modal":`true`,"aria-labelledby":m.current,className:e.default.dialog,onKeyDown:h,onClick:e=>e.stopPropagation(),children:[(0,r.jsxs)(`div`,{className:e.default.header,children:[(0,r.jsx)(`span`,{id:m.current,className:e.default.title,children:s}),(0,r.jsx)(`button`,{type:`button`,className:e.default.closeBtn,"aria-label":`Close`,onClick:o,children:`×`})]}),(0,r.jsxs)(`div`,{className:e.default.searchRow,children:[(0,r.jsx)(`span`,{className:e.default.searchIcon,"aria-hidden":`true`,children:`⌕`}),(0,r.jsx)(`input`,{ref:f,type:`search`,className:e.default.searchInput,placeholder:`Search shortcuts…`,value:l,onChange:e=>u(e.target.value),"aria-label":`Search shortcuts`}),l&&(0,r.jsx)(`button`,{type:`button`,className:e.default.searchClear,"aria-label":`Clear search`,onClick:()=>{u(``),f.current?.focus()},children:`×`})]}),(0,r.jsx)(`div`,{className:e.default.body,role:`list`,children:_.length===0?(0,r.jsxs)(`p`,{className:e.default.empty,children:[`No shortcuts match "`,l,`"`]}):_.map(t=>(0,r.jsxs)(`section`,{className:e.default.section,children:[(0,r.jsx)(`h3`,{className:e.default.sectionTitle,children:t.title}),(0,r.jsx)(`ul`,{className:e.default.list,children:t.shortcuts.map(t=>(0,r.jsxs)(`li`,{className:e.default.row,children:[(0,r.jsx)(`span`,{className:e.default.keys,"aria-label":t.keys.join(` + `),children:t.keys.map((n,i)=>(0,r.jsxs)(`span`,{className:e.default.keyCap,children:[(0,r.jsx)(`kbd`,{children:n}),i<t.keys.length-1&&(0,r.jsx)(`span`,{className:e.default.plus,"aria-hidden":`true`,children:`+`})]},i))}),(0,r.jsx)(`span`,{className:e.default.description,children:t.description})]},t.description))})]},t.title))})]})});return typeof document>`u`?v:(0,n.createPortal)(v,document.body)};exports.ShortcutsDialog=a;
1
+ const e=require("../Dialog/dialogUtils.cjs"),t=require("./ShortcutsDialog.module.css.cjs");let n=require("react"),r=require("react-dom"),i=require("react/jsx-runtime");var a=`button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex="-1"])`,o=({open:o,onClose:s,title:c=`Keyboard Shortcuts`,sections:l})=>{let[u,d]=(0,n.useState)(``),f=(0,n.useRef)(null),p=(0,n.useRef)(null),m=(0,n.useRef)(null),h=(0,n.useRef)(`shortcuts-title-${Math.random().toString(36).slice(2,9)}`);e.useBodyScrollLock(o),(0,n.useEffect)(()=>{o?(m.current=document.activeElement,requestAnimationFrame(()=>p.current?.focus())):(d(``),m.current?.focus())},[o]);let g=(0,n.useCallback)(e=>{if(e.key===`Escape`){e.preventDefault(),s();return}if(e.key!==`Tab`)return;let t=Array.from(f.current?.querySelectorAll(a)??[]);if(t.length===0)return;let n=t[0],r=t[t.length-1];e.shiftKey?document.activeElement===n&&(e.preventDefault(),r.focus()):document.activeElement===r&&(e.preventDefault(),n.focus())},[s]);if(!o)return null;let _=u.trim().toLowerCase(),v=l.map(e=>({...e,shortcuts:e.shortcuts.filter(({description:e,keys:t})=>!_||e.toLowerCase().includes(_)||t.some(e=>e.toLowerCase().includes(_)))})).filter(e=>e.shortcuts.length>0),y=(0,i.jsx)(`div`,{className:t.default.backdrop,onClick:s,children:(0,i.jsxs)(`div`,{ref:f,role:`dialog`,"aria-modal":`true`,"aria-labelledby":h.current,className:t.default.dialog,onKeyDown:g,onClick:e=>e.stopPropagation(),children:[(0,i.jsxs)(`div`,{className:t.default.header,children:[(0,i.jsx)(`span`,{id:h.current,className:t.default.title,children:c}),(0,i.jsx)(`button`,{type:`button`,className:t.default.closeBtn,"aria-label":`Close`,onClick:s,children:`×`})]}),(0,i.jsxs)(`div`,{className:t.default.searchRow,children:[(0,i.jsx)(`span`,{className:t.default.searchIcon,"aria-hidden":`true`,children:`⌕`}),(0,i.jsx)(`input`,{ref:p,type:`search`,className:t.default.searchInput,placeholder:`Search shortcuts…`,value:u,onChange:e=>d(e.target.value),"aria-label":`Search shortcuts`}),u&&(0,i.jsx)(`button`,{type:`button`,className:t.default.searchClear,"aria-label":`Clear search`,onClick:()=>{d(``),p.current?.focus()},children:`×`})]}),(0,i.jsx)(`div`,{className:t.default.body,role:`list`,children:v.length===0?(0,i.jsxs)(`p`,{className:t.default.empty,children:[`No shortcuts match "`,u,`"`]}):v.map(e=>(0,i.jsxs)(`section`,{className:t.default.section,children:[(0,i.jsx)(`h3`,{className:t.default.sectionTitle,children:e.title}),(0,i.jsx)(`ul`,{className:t.default.list,children:e.shortcuts.map(e=>(0,i.jsxs)(`li`,{className:t.default.row,children:[(0,i.jsx)(`span`,{className:t.default.keys,"aria-label":e.keys.join(` + `),children:e.keys.map((n,r)=>(0,i.jsxs)(`span`,{className:t.default.keyCap,children:[(0,i.jsx)(`kbd`,{children:n}),r<e.keys.length-1&&(0,i.jsx)(`span`,{className:t.default.plus,"aria-hidden":`true`,children:`+`})]},r))}),(0,i.jsx)(`span`,{className:t.default.description,children:e.description})]},e.description))})]},e.title))})]})});return typeof document>`u`?y:(0,r.createPortal)(y,document.body)};exports.ShortcutsDialog=o;
2
2
  //# sourceMappingURL=ShortcutsDialog.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ShortcutsDialog.cjs","names":[],"sources":["../../../src/components/ShortcutsDialog/ShortcutsDialog.tsx"],"sourcesContent":["import { type KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react';\n\nimport { createPortal } from 'react-dom';\n\nimport styles from './ShortcutsDialog.module.css';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface ShortcutEntry {\n /**\n * Key combination tokens, e.g. `[\"Ctrl\", \"S\"]` or `[\"F5\"]`.\n * Each token is rendered as a styled `<kbd>` key cap.\n */\n keys: string[];\n /** Human-readable description of what the shortcut does. */\n description: string;\n}\n\nexport interface ShortcutsSection {\n /** Section heading, e.g. \"File\", \"Edit\". */\n title: string;\n shortcuts: ShortcutEntry[];\n}\n\nexport interface ShortcutsDialogProps {\n /** Whether the dialog is visible. */\n open: boolean;\n /** Called when the dialog is dismissed (Escape, close button). */\n onClose: () => void;\n /** Dialog heading. Defaults to `\"Keyboard Shortcuts\"`. */\n title?: string;\n /** Shortcut sections to display. */\n sections: ShortcutsSection[];\n}\n\n// ─── Focusable selector (reused from Dialog) ─────────────────────────────────\n\nconst FOCUSABLE =\n 'button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * Modal dialog listing keyboard shortcuts, grouped in sections, with\n * integrated search to filter by description or key name.\n *\n * Mirrors `AdwShortcutsDialog` (libadwaita 1.8 / GNOME 49).\n *\n * - Renders into a portal (`document.body`).\n * - Traps focus; Escape closes.\n * - Search filters across all sections in real time.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ShortcutsDialog.html\n */\nexport const ShortcutsDialog = ({\n open,\n onClose,\n title = 'Keyboard Shortcuts',\n sections,\n}: ShortcutsDialogProps) => {\n const [query, setQuery] = useState('');\n const dialogRef = useRef<HTMLDivElement>(null);\n const searchRef = useRef<HTMLInputElement>(null);\n const previouslyFocused = useRef<Element | null>(null);\n const titleId = useRef(`shortcuts-title-${Math.random().toString(36).slice(2, 9)}`);\n\n // Save / restore focus\n useEffect(() => {\n if (open) {\n previouslyFocused.current = document.activeElement;\n // Focus the search field after mount\n requestAnimationFrame(() => searchRef.current?.focus());\n } else {\n setQuery('');\n (previouslyFocused.current as HTMLElement | null)?.focus();\n }\n }, [open]);\n\n // Focus trap + Escape\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n onClose();\n\n return;\n }\n\n if (e.key !== 'Tab') {\n return;\n }\n\n const focusable = Array.from(\n dialogRef.current?.querySelectorAll<HTMLElement>(FOCUSABLE) ?? [],\n );\n\n if (focusable.length === 0) {\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n }\n },\n [onClose],\n );\n\n if (!open) {\n return null;\n }\n\n // Filter sections by query\n const q = query.trim().toLowerCase();\n const filtered = sections\n .map((section) => ({\n ...section,\n shortcuts: section.shortcuts.filter(\n ({ description, keys }) =>\n !q ||\n description.toLowerCase().includes(q) ||\n keys.some((k) => k.toLowerCase().includes(q)),\n ),\n }))\n .filter((s) => s.shortcuts.length > 0);\n\n const node = (\n <div className={styles.backdrop} onClick={onClose} aria-hidden=\"true\">\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId.current}\n className={styles.dialog}\n onKeyDown={handleKeyDown}\n onClick={(e) => e.stopPropagation()}\n >\n {/* ── Header ── */}\n <div className={styles.header}>\n <span id={titleId.current} className={styles.title}>\n {title}\n </span>\n <button type=\"button\" className={styles.closeBtn} aria-label=\"Close\" onClick={onClose}>\n ×\n </button>\n </div>\n\n {/* ── Search ── */}\n <div className={styles.searchRow}>\n <span className={styles.searchIcon} aria-hidden=\"true\">\n ⌕\n </span>\n <input\n ref={searchRef}\n type=\"search\"\n className={styles.searchInput}\n placeholder=\"Search shortcuts…\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n aria-label=\"Search shortcuts\"\n />\n {query && (\n <button\n type=\"button\"\n className={styles.searchClear}\n aria-label=\"Clear search\"\n onClick={() => {\n setQuery('');\n searchRef.current?.focus();\n }}\n >\n ×\n </button>\n )}\n </div>\n\n {/* ── Shortcut list ── */}\n <div className={styles.body} role=\"list\">\n {filtered.length === 0 ? (\n <p className={styles.empty}>No shortcuts match \"{query}\"</p>\n ) : (\n filtered.map((section) => (\n <section key={section.title} className={styles.section}>\n <h3 className={styles.sectionTitle}>{section.title}</h3>\n <ul className={styles.list}>\n {section.shortcuts.map((shortcut) => (\n <li key={shortcut.description} className={styles.row}>\n <span className={styles.keys} aria-label={shortcut.keys.join(' + ')}>\n {shortcut.keys.map((key, i) => (\n <span key={i} className={styles.keyCap}>\n <kbd>{key}</kbd>\n {i < shortcut.keys.length - 1 && (\n <span className={styles.plus} aria-hidden=\"true\">\n +\n </span>\n )}\n </span>\n ))}\n </span>\n <span className={styles.description}>{shortcut.description}</span>\n </li>\n ))}\n </ul>\n </section>\n ))\n )}\n </div>\n </div>\n </div>\n );\n\n if (typeof document === 'undefined') {\n return node;\n }\n\n return createPortal(node, document.body);\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ShortcutsDialog.cjs","names":[],"sources":["../../../src/components/ShortcutsDialog/ShortcutsDialog.tsx"],"sourcesContent":["import { type KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react';\n\nimport { createPortal } from 'react-dom';\n\nimport { useBodyScrollLock } from '../Dialog/dialogUtils';\n\nimport styles from './ShortcutsDialog.module.css';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface ShortcutEntry {\n /**\n * Key combination tokens, e.g. `[\"Ctrl\", \"S\"]` or `[\"F5\"]`.\n * Each token is rendered as a styled `<kbd>` key cap.\n */\n keys: string[];\n /** Human-readable description of what the shortcut does. */\n description: string;\n}\n\nexport interface ShortcutsSection {\n /** Section heading, e.g. \"File\", \"Edit\". */\n title: string;\n shortcuts: ShortcutEntry[];\n}\n\nexport interface ShortcutsDialogProps {\n /** Whether the dialog is visible. */\n open: boolean;\n /** Called when the dialog is dismissed (Escape, close button). */\n onClose: () => void;\n /** Dialog heading. Defaults to `\"Keyboard Shortcuts\"`. */\n title?: string;\n /** Shortcut sections to display. */\n sections: ShortcutsSection[];\n}\n\n// ─── Focusable selector (reused from Dialog) ─────────────────────────────────\n\nconst FOCUSABLE =\n 'button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * Modal dialog listing keyboard shortcuts, grouped in sections, with\n * integrated search to filter by description or key name.\n *\n * Mirrors `AdwShortcutsDialog` (libadwaita 1.8 / GNOME 49).\n *\n * - Renders into a portal (`document.body`).\n * - Traps focus; Escape closes.\n * - Search filters across all sections in real time.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ShortcutsDialog.html\n */\nexport const ShortcutsDialog = ({\n open,\n onClose,\n title = 'Keyboard Shortcuts',\n sections,\n}: ShortcutsDialogProps) => {\n const [query, setQuery] = useState('');\n const dialogRef = useRef<HTMLDivElement>(null);\n const searchRef = useRef<HTMLInputElement>(null);\n const previouslyFocused = useRef<Element | null>(null);\n const titleId = useRef(`shortcuts-title-${Math.random().toString(36).slice(2, 9)}`);\n\n useBodyScrollLock(open);\n\n // Save / restore focus\n useEffect(() => {\n if (open) {\n previouslyFocused.current = document.activeElement;\n // Focus the search field after mount\n requestAnimationFrame(() => searchRef.current?.focus());\n } else {\n setQuery('');\n (previouslyFocused.current as HTMLElement | null)?.focus();\n }\n }, [open]);\n\n // Focus trap + Escape\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n onClose();\n\n return;\n }\n\n if (e.key !== 'Tab') {\n return;\n }\n\n const focusable = Array.from(\n dialogRef.current?.querySelectorAll<HTMLElement>(FOCUSABLE) ?? [],\n );\n\n if (focusable.length === 0) {\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n }\n },\n [onClose],\n );\n\n if (!open) {\n return null;\n }\n\n // Filter sections by query\n const q = query.trim().toLowerCase();\n const filtered = sections\n .map((section) => ({\n ...section,\n shortcuts: section.shortcuts.filter(\n ({ description, keys }) =>\n !q ||\n description.toLowerCase().includes(q) ||\n keys.some((k) => k.toLowerCase().includes(q)),\n ),\n }))\n .filter((s) => s.shortcuts.length > 0);\n\n const node = (\n <div className={styles.backdrop} onClick={onClose}>\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId.current}\n className={styles.dialog}\n onKeyDown={handleKeyDown}\n onClick={(e) => e.stopPropagation()}\n >\n {/* ── Header ── */}\n <div className={styles.header}>\n <span id={titleId.current} className={styles.title}>\n {title}\n </span>\n <button type=\"button\" className={styles.closeBtn} aria-label=\"Close\" onClick={onClose}>\n ×\n </button>\n </div>\n\n {/* ── Search ── */}\n <div className={styles.searchRow}>\n <span className={styles.searchIcon} aria-hidden=\"true\">\n ⌕\n </span>\n <input\n ref={searchRef}\n type=\"search\"\n className={styles.searchInput}\n placeholder=\"Search shortcuts…\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n aria-label=\"Search shortcuts\"\n />\n {query && (\n <button\n type=\"button\"\n className={styles.searchClear}\n aria-label=\"Clear search\"\n onClick={() => {\n setQuery('');\n searchRef.current?.focus();\n }}\n >\n ×\n </button>\n )}\n </div>\n\n {/* ── Shortcut list ── */}\n <div className={styles.body} role=\"list\">\n {filtered.length === 0 ? (\n <p className={styles.empty}>No shortcuts match \"{query}\"</p>\n ) : (\n filtered.map((section) => (\n <section key={section.title} className={styles.section}>\n <h3 className={styles.sectionTitle}>{section.title}</h3>\n <ul className={styles.list}>\n {section.shortcuts.map((shortcut) => (\n <li key={shortcut.description} className={styles.row}>\n <span className={styles.keys} aria-label={shortcut.keys.join(' + ')}>\n {shortcut.keys.map((key, i) => (\n <span key={i} className={styles.keyCap}>\n <kbd>{key}</kbd>\n {i < shortcut.keys.length - 1 && (\n <span className={styles.plus} aria-hidden=\"true\">\n +\n </span>\n )}\n </span>\n ))}\n </span>\n <span className={styles.description}>{shortcut.description}</span>\n </li>\n ))}\n </ul>\n </section>\n ))\n )}\n </div>\n </div>\n </div>\n );\n\n if (typeof document === 'undefined') {\n return node;\n }\n\n return createPortal(node, document.body);\n};\n"],"mappings":"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"}