@spark-ui/components 11.6.0 → 11.6.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.
package/dist/docgen.json CHANGED
@@ -32253,6 +32253,16 @@
32253
32253
  }
32254
32254
  }
32255
32255
  },
32256
+ "findFocusableElement": {
32257
+ "tags": {
32258
+ "param": "candidates - Array of candidate elements to check\ninputRef - Input element to use as last resort",
32259
+ "returns": "The first focusable element, or null if none found"
32260
+ },
32261
+ "description": "Finds the first focusable element from a list of candidates\nFalls back to inputRef if no other element is focusable",
32262
+ "displayName": "findFocusableElement",
32263
+ "methods": [],
32264
+ "props": {}
32265
+ },
32256
32266
  "FormField": {
32257
32267
  "tags": {},
32258
32268
  "description": "",
@@ -159,6 +159,29 @@ function getFileIcon(file) {
159
159
  }
160
160
  return (0, import_react.createElement)(import_CvOutline.CvOutline);
161
161
  }
162
+ function isFocusable(element) {
163
+ if (!element) {
164
+ return false;
165
+ }
166
+ const tabIndex = element.tabIndex;
167
+ if (tabIndex >= 0) {
168
+ return true;
169
+ }
170
+ const isContentEditable = String(element.contentEditable) === "true";
171
+ const naturallyFocusable = element instanceof HTMLInputElement || element instanceof HTMLButtonElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement || element instanceof HTMLAnchorElement && Boolean(element.href) || isContentEditable;
172
+ return naturallyFocusable;
173
+ }
174
+ function findFocusableElement(candidates, inputRef) {
175
+ for (const candidate of candidates) {
176
+ if (isFocusable(candidate)) {
177
+ return candidate;
178
+ }
179
+ }
180
+ if (inputRef.current) {
181
+ return inputRef.current;
182
+ }
183
+ return null;
184
+ }
162
185
 
163
186
  // src/file-upload/useFileUploadState.tsx
164
187
  function useFileUploadState({
@@ -371,6 +394,7 @@ var FileUpload = ({
371
394
  const triggerRef = (0, import_react3.useRef)(null);
372
395
  const dropzoneRef = (0, import_react3.useRef)(null);
373
396
  const deleteButtonRefs = (0, import_react3.useRef)([]);
397
+ const rejectedFileDeleteButtonRefs = (0, import_react3.useRef)([]);
374
398
  const disabled = field.disabled ?? disabledProp;
375
399
  const readOnly = field.readOnly ?? readOnlyProp;
376
400
  const {
@@ -401,6 +425,10 @@ var FileUpload = ({
401
425
  clearFilesFromHook();
402
426
  deleteButtonRefs.current = [];
403
427
  };
428
+ const clearRejectedFilesWithRefs = () => {
429
+ clearRejectedFiles();
430
+ rejectedFileDeleteButtonRefs.current = [];
431
+ };
404
432
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
405
433
  FileUploadContext.Provider,
406
434
  {
@@ -412,10 +440,11 @@ var FileUpload = ({
412
440
  removeFile,
413
441
  removeRejectedFile,
414
442
  clearFiles,
415
- clearRejectedFiles,
443
+ clearRejectedFiles: clearRejectedFilesWithRefs,
416
444
  triggerRef,
417
445
  dropzoneRef,
418
446
  deleteButtonRefs,
447
+ rejectedFileDeleteButtonRefs,
419
448
  multiple,
420
449
  maxFiles,
421
450
  maxFilesReached,
@@ -1706,7 +1735,16 @@ var ItemDeleteTrigger = ({
1706
1735
  onClick,
1707
1736
  ...props
1708
1737
  }) => {
1709
- const { removeFile, triggerRef, dropzoneRef, deleteButtonRefs, disabled, readOnly, files } = useFileUploadContext();
1738
+ const {
1739
+ removeFile,
1740
+ triggerRef,
1741
+ dropzoneRef,
1742
+ deleteButtonRefs,
1743
+ inputRef,
1744
+ disabled,
1745
+ readOnly,
1746
+ files
1747
+ } = useFileUploadContext();
1710
1748
  const buttonRef = (0, import_react10.useRef)(null);
1711
1749
  const fileIndex = files.findIndex((f) => f.name === file.name && f.size === file.size);
1712
1750
  const handleClick = (e) => {
@@ -1723,7 +1761,10 @@ var ItemDeleteTrigger = ({
1723
1761
  nextButton.focus();
1724
1762
  }
1725
1763
  } else {
1726
- const focusTarget = triggerRef.current || dropzoneRef.current;
1764
+ const focusTarget = findFocusableElement(
1765
+ [triggerRef.current, dropzoneRef.current],
1766
+ inputRef
1767
+ );
1727
1768
  if (focusTarget) {
1728
1769
  focusTarget.focus();
1729
1770
  }
@@ -1996,7 +2037,16 @@ var RejectedFileDeleteTrigger = ({
1996
2037
  onClick,
1997
2038
  ...props
1998
2039
  }) => {
1999
- const { removeRejectedFile, triggerRef, dropzoneRef, disabled, readOnly, rejectedFiles } = useFileUploadContext();
2040
+ const {
2041
+ removeRejectedFile,
2042
+ triggerRef,
2043
+ dropzoneRef,
2044
+ rejectedFileDeleteButtonRefs,
2045
+ inputRef,
2046
+ disabled,
2047
+ readOnly,
2048
+ rejectedFiles
2049
+ } = useFileUploadContext();
2000
2050
  const buttonRef = (0, import_react14.useRef)(null);
2001
2051
  const rejectedFileIndex = rejectedFiles.findIndex(
2002
2052
  (rf) => rf.file.name === rejectedFile.file.name && rf.file.size === rejectedFile.file.size
@@ -2006,18 +2056,43 @@ var RejectedFileDeleteTrigger = ({
2006
2056
  return;
2007
2057
  }
2008
2058
  removeRejectedFile(rejectedFileIndex);
2009
- setTimeout(() => {
2010
- const focusTarget = triggerRef.current || dropzoneRef.current;
2011
- if (focusTarget) {
2012
- focusTarget.focus();
2059
+ requestAnimationFrame(() => {
2060
+ const remainingButtons = rejectedFileDeleteButtonRefs.current.filter(Boolean);
2061
+ if (remainingButtons.length > 0) {
2062
+ const targetIndex = Math.min(rejectedFileIndex, remainingButtons.length - 1);
2063
+ const nextButton = remainingButtons[targetIndex];
2064
+ if (nextButton) {
2065
+ nextButton.focus();
2066
+ }
2067
+ } else {
2068
+ const focusTarget = findFocusableElement(
2069
+ [triggerRef.current, dropzoneRef.current],
2070
+ inputRef
2071
+ );
2072
+ if (focusTarget) {
2073
+ focusTarget.focus();
2074
+ }
2013
2075
  }
2014
- }, 0);
2076
+ });
2015
2077
  onClick?.(e);
2016
2078
  };
2079
+ const setRef = (node) => {
2080
+ buttonRef.current = node;
2081
+ if (node) {
2082
+ while (rejectedFileDeleteButtonRefs.current.length <= rejectedFileIndex) {
2083
+ rejectedFileDeleteButtonRefs.current.push(null);
2084
+ }
2085
+ rejectedFileDeleteButtonRefs.current[rejectedFileIndex] = node;
2086
+ } else {
2087
+ if (rejectedFileDeleteButtonRefs.current[rejectedFileIndex]) {
2088
+ rejectedFileDeleteButtonRefs.current[rejectedFileIndex] = null;
2089
+ }
2090
+ }
2091
+ };
2017
2092
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2018
2093
  IconButton,
2019
2094
  {
2020
- ref: buttonRef,
2095
+ ref: setRef,
2021
2096
  "data-spark-component": "file-upload-rejected-file-delete-trigger",
2022
2097
  className: (0, import_class_variance_authority13.cx)(className),
2023
2098
  onClick: handleClick,