@crisp-ui-kit/crisp 0.26.0 → 0.26.1

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.
@@ -159,6 +159,31 @@ function Command({ open, onOpenChange, items = exports.defaultCommandItems, plac
159
159
  if (queryProp === undefined)
160
160
  setInternalQuery(next);
161
161
  }, [onQueryChange, queryProp]);
162
+ /**
163
+ * Where the user was when the palette opened.
164
+ *
165
+ * The dialog machine restores focus on close, and for Dialog and Drawer that
166
+ * lands on the trigger: focus-trap remembers whatever was focused when the
167
+ * trap activated. The palette is different — the combobox focuses the search
168
+ * input as part of the same commit, so the element the trap remembers is the
169
+ * input, which is then removed with the palette, and focus falls to
170
+ * `document.body`. The palette is the one surface opened from anywhere with
171
+ * a shortcut, so losing the caller's place is worst exactly here (#241).
172
+ *
173
+ * Recorded by watching focus WHILE CLOSED rather than reading
174
+ * `document.activeElement` when `open` flips: by the time an effect of ours
175
+ * runs, the input may already hold focus.
176
+ */
177
+ const openerRef = React.useRef(null);
178
+ React.useEffect(() => {
179
+ if (open)
180
+ return;
181
+ const onFocusIn = (e) => {
182
+ openerRef.current = e.target;
183
+ };
184
+ document.addEventListener("focusin", onFocusIn);
185
+ return () => document.removeEventListener("focusin", onFocusIn);
186
+ }, [open]);
162
187
  const dialogService = (0, react_1.useMachine)(dialog.machine, {
163
188
  id: dialogId,
164
189
  open,
@@ -166,6 +191,7 @@ function Command({ open, onOpenChange, items = exports.defaultCommandItems, plac
166
191
  onOpenChange(details.open);
167
192
  },
168
193
  initialFocusEl: () => inputRef.current,
194
+ finalFocusEl: () => openerRef.current,
169
195
  "aria-label": ariaLabel,
170
196
  });
171
197
  const dlg = dialog.connect(dialogService, react_1.normalizeProps);
@@ -122,6 +122,31 @@ export function Command({ open, onOpenChange, items = defaultCommandItems, place
122
122
  if (queryProp === undefined)
123
123
  setInternalQuery(next);
124
124
  }, [onQueryChange, queryProp]);
125
+ /**
126
+ * Where the user was when the palette opened.
127
+ *
128
+ * The dialog machine restores focus on close, and for Dialog and Drawer that
129
+ * lands on the trigger: focus-trap remembers whatever was focused when the
130
+ * trap activated. The palette is different — the combobox focuses the search
131
+ * input as part of the same commit, so the element the trap remembers is the
132
+ * input, which is then removed with the palette, and focus falls to
133
+ * `document.body`. The palette is the one surface opened from anywhere with
134
+ * a shortcut, so losing the caller's place is worst exactly here (#241).
135
+ *
136
+ * Recorded by watching focus WHILE CLOSED rather than reading
137
+ * `document.activeElement` when `open` flips: by the time an effect of ours
138
+ * runs, the input may already hold focus.
139
+ */
140
+ const openerRef = React.useRef(null);
141
+ React.useEffect(() => {
142
+ if (open)
143
+ return;
144
+ const onFocusIn = (e) => {
145
+ openerRef.current = e.target;
146
+ };
147
+ document.addEventListener("focusin", onFocusIn);
148
+ return () => document.removeEventListener("focusin", onFocusIn);
149
+ }, [open]);
125
150
  const dialogService = useMachine(dialog.machine, {
126
151
  id: dialogId,
127
152
  open,
@@ -129,6 +154,7 @@ export function Command({ open, onOpenChange, items = defaultCommandItems, place
129
154
  onOpenChange(details.open);
130
155
  },
131
156
  initialFocusEl: () => inputRef.current,
157
+ finalFocusEl: () => openerRef.current,
132
158
  "aria-label": ariaLabel,
133
159
  });
134
160
  const dlg = dialog.connect(dialogService, normalizeProps);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.26.0",
3
+ "version": "0.26.1",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",