@crisp-ui-kit/crisp 0.23.4 → 0.23.5

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.
@@ -114,5 +114,5 @@ function FileUpload({ className, labels, "aria-label": ariaLabel, accept, disabl
114
114
  // instead means attaching them here; without inputRef, `openFilePicker`
115
115
  // finds no input and returns silently, so the dropzone opens nothing.
116
116
  inputRef, dropzoneRef, } = api;
117
- return ((0, jsx_runtime_1.jsxs)("div", { ...rest, className: (0, fileupload_recipe_1.fileuploadRecipe)({ className }), children: [(0, jsx_runtime_1.jsxs)("button", { ...dropzoneProps, ref: dropzoneRef, type: "button", className: "fileupload-zone", tabIndex: disabled ? -1 : 0, "aria-label": ariaLabel ?? t.hint, disabled: disabled, "data-dragging": dragging ? "" : undefined, "data-disabled": disabled ? "" : undefined, "data-invalid": invalid ? "" : undefined, onClick: () => openFilePicker(), children: [(0, jsx_runtime_1.jsx)("span", { className: "fileupload-title", children: t.title }), (0, jsx_runtime_1.jsx)("span", { className: "fileupload-hint", children: t.hint })] }), (0, jsx_runtime_1.jsx)("input", { ...hiddenInputProps, ref: inputRef, className: "fileupload-input" }), acceptedFileEntries.length > 0 && ((0, jsx_runtime_1.jsx)("ul", { className: "fileupload-list", children: acceptedFileEntries.map((entry) => ((0, jsx_runtime_1.jsxs)("li", { className: "fileupload-item", "data-status": entry.status, children: [(0, jsx_runtime_1.jsx)("span", { className: "fileupload-item-name", children: entry.file.name }), entry.status === "uploading" && entry.progress != null && ((0, jsx_runtime_1.jsx)("span", { className: "fileupload-progress", role: "progressbar", "aria-valuenow": Math.round(entry.progress), "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": entry.file.name, children: (0, jsx_runtime_1.jsx)("span", { className: "fileupload-progress-fill", style: { width: `${entry.progress}%` } }) })), (0, jsx_runtime_1.jsx)("span", { className: "fileupload-item-size", children: formatSize(entry.file.size) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "fileupload-remove", "aria-label": `${t.remove}: ${entry.file.name}`, onClick: () => removeFileEntry(entry.id), children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 13, strokeWidth: 2 }) })] }, entry.id))) })), rejections.length > 0 && ((0, jsx_runtime_1.jsx)("ul", { className: "fileupload-errors", "aria-live": "polite", children: rejections.map((r) => ((0, jsx_runtime_1.jsx)("li", { className: "fileupload-error", children: `${r.file.name}: ${r.errors.map((e) => t.errors[e] ?? e).join(" ")}` }, r.file.name))) }))] }));
117
+ return ((0, jsx_runtime_1.jsxs)("div", { ...rest, className: (0, fileupload_recipe_1.fileuploadRecipe)({ className }), children: [(0, jsx_runtime_1.jsxs)("button", { ...dropzoneProps, ref: dropzoneRef, type: "button", className: "fileupload-zone", tabIndex: disabled ? -1 : 0, "aria-label": ariaLabel ?? t.hint, disabled: disabled, "data-dragging": dragging ? "" : undefined, "data-disabled": disabled ? "" : undefined, "data-invalid": invalid ? "" : undefined, onClick: () => openFilePicker(), children: [(0, jsx_runtime_1.jsx)("span", { className: "fileupload-title", children: t.title }), (0, jsx_runtime_1.jsx)("span", { className: "fileupload-hint", children: t.hint })] }), (0, jsx_runtime_1.jsx)("input", { ...hiddenInputProps, ref: inputRef, className: "fileupload-input" }), acceptedFileEntries.length > 0 && ((0, jsx_runtime_1.jsx)("ul", { className: "fileupload-list", children: acceptedFileEntries.map((entry) => ((0, jsx_runtime_1.jsxs)("li", { className: "fileupload-item", "data-status": entry.status, children: [(0, jsx_runtime_1.jsx)("span", { className: "fileupload-item-name", children: entry.file.name }), entry.status === "uploading" && entry.progress != null && ((0, jsx_runtime_1.jsx)("span", { className: "fileupload-progress", role: "progressbar", "aria-valuenow": Math.round(entry.progress), "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": entry.file.name, children: (0, jsx_runtime_1.jsx)("span", { className: "fileupload-progress-fill", style: { width: `${entry.progress}%` } }) })), (0, jsx_runtime_1.jsx)("span", { className: "fileupload-item-size", children: formatSize(entry.file.size) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "fileupload-remove", "aria-label": `${t.remove}: ${entry.file.name}`, onClick: () => removeFileEntry(entry.id), children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 13, strokeWidth: 2 }) })] }, entry.id))) })), (0, jsx_runtime_1.jsx)("ul", { className: "fileupload-errors", "aria-live": "polite", children: rejections.map((r) => ((0, jsx_runtime_1.jsx)("li", { className: "fileupload-error", children: `${r.file.name}: ${r.errors.map((e) => t.errors[e] ?? e).join(" ")}` }, r.file.name))) })] }));
118
118
  }
@@ -92,6 +92,10 @@ const config = (0, core_1.defineRecipe)({
92
92
  & .fileupload-remove:focus-visible { outline-color: ${tokens_1.contract.strokeFocusRing}; }
93
93
 
94
94
  & .fileupload-errors { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["0.5"]}; margin: 0; padding: 0; list-style: none; }
95
+ /* out of flow while empty, so the always-mounted live region costs no
96
+ layout: display:none would take it out of the accessibility tree, which
97
+ is the one thing it exists to be in */
98
+ & .fileupload-errors:empty { position: absolute; }
95
99
  & .fileupload-error { color: ${tokens_1.contract.bgDanger}; font-size: ${tokens_1.contract.text.md}; line-height: 18px; }
96
100
  `,
97
101
  variants: {},
@@ -48,7 +48,7 @@ const ICONS = {
48
48
  };
49
49
  function ToastCard({ item, onClose, dismissLabel, }) {
50
50
  const Icon = ICONS[item.tone ?? "info"];
51
- return ((0, jsx_runtime_1.jsxs)("div", { className: "toast", role: "status", "data-tone": item.tone ?? "info", children: [(0, jsx_runtime_1.jsx)("span", { className: "toast-icon", children: (0, jsx_runtime_1.jsx)(Icon, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "toast-body", children: [(0, jsx_runtime_1.jsx)("div", { className: "toast-title", children: item.title }), item.description != null && ((0, jsx_runtime_1.jsx)("div", { className: "toast-desc", children: item.description })), item.action && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "toast-action", onClick: () => {
51
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "toast", "data-tone": item.tone ?? "info", children: [(0, jsx_runtime_1.jsx)("span", { className: "toast-icon", children: (0, jsx_runtime_1.jsx)(Icon, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "toast-body", children: [(0, jsx_runtime_1.jsx)("div", { className: "toast-title", children: item.title }), item.description != null && ((0, jsx_runtime_1.jsx)("div", { className: "toast-desc", children: item.description })), item.action && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "toast-action", onClick: () => {
52
52
  item.action?.onClick();
53
53
  onClose();
54
54
  }, children: item.action.label }))] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "toast-close", "aria-label": dismissLabel, onClick: onClose, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 15, strokeWidth: 2, "aria-hidden": "true" }) })] }));
@@ -84,7 +84,7 @@ function ToastProvider({ children, duration = 4000, dismissLabel = "Dismiss", re
84
84
  return () => map.forEach(clearTimeout);
85
85
  }, []);
86
86
  const api = React.useMemo(() => ({ toast, dismiss }), [toast, dismiss]);
87
- return ((0, jsx_runtime_1.jsxs)(ToastContext.Provider, { value: api, children: [children, (0, jsx_runtime_1.jsx)("div", { className: (0, toast_recipe_1.toastRecipe)(), children: (0, jsx_runtime_1.jsx)("div", { className: "toast-viewport", role: "region", "aria-label": regionLabel, children: items.map((t) => ((0, jsx_runtime_1.jsx)(ToastCard, { item: t, onClose: () => dismiss(t.id), dismissLabel: dismissLabel }, t.id))) }) })] }));
87
+ return ((0, jsx_runtime_1.jsxs)(ToastContext.Provider, { value: api, children: [children, (0, jsx_runtime_1.jsx)("div", { className: (0, toast_recipe_1.toastRecipe)(), children: (0, jsx_runtime_1.jsx)("div", { className: "toast-viewport", role: "region", "aria-label": regionLabel, "aria-live": "polite", "aria-atomic": "false", children: items.map((t) => ((0, jsx_runtime_1.jsx)(ToastCard, { item: t, onClose: () => dismiss(t.id), dismissLabel: dismissLabel }, t.id))) }) })] }));
88
88
  }
89
89
  ToastProvider.displayName = "ToastProvider";
90
90
  function useToast() {
@@ -77,5 +77,5 @@ export function FileUpload({ className, labels, "aria-label": ariaLabel, accept,
77
77
  // instead means attaching them here; without inputRef, `openFilePicker`
78
78
  // finds no input and returns silently, so the dropzone opens nothing.
79
79
  inputRef, dropzoneRef, } = api;
80
- return (_jsxs("div", { ...rest, className: fileuploadRecipe({ className }), children: [_jsxs("button", { ...dropzoneProps, ref: dropzoneRef, type: "button", className: "fileupload-zone", tabIndex: disabled ? -1 : 0, "aria-label": ariaLabel ?? t.hint, disabled: disabled, "data-dragging": dragging ? "" : undefined, "data-disabled": disabled ? "" : undefined, "data-invalid": invalid ? "" : undefined, onClick: () => openFilePicker(), children: [_jsx("span", { className: "fileupload-title", children: t.title }), _jsx("span", { className: "fileupload-hint", children: t.hint })] }), _jsx("input", { ...hiddenInputProps, ref: inputRef, className: "fileupload-input" }), acceptedFileEntries.length > 0 && (_jsx("ul", { className: "fileupload-list", children: acceptedFileEntries.map((entry) => (_jsxs("li", { className: "fileupload-item", "data-status": entry.status, children: [_jsx("span", { className: "fileupload-item-name", children: entry.file.name }), entry.status === "uploading" && entry.progress != null && (_jsx("span", { className: "fileupload-progress", role: "progressbar", "aria-valuenow": Math.round(entry.progress), "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": entry.file.name, children: _jsx("span", { className: "fileupload-progress-fill", style: { width: `${entry.progress}%` } }) })), _jsx("span", { className: "fileupload-item-size", children: formatSize(entry.file.size) }), _jsx("button", { type: "button", className: "fileupload-remove", "aria-label": `${t.remove}: ${entry.file.name}`, onClick: () => removeFileEntry(entry.id), children: _jsx(X, { size: 13, strokeWidth: 2 }) })] }, entry.id))) })), rejections.length > 0 && (_jsx("ul", { className: "fileupload-errors", "aria-live": "polite", children: rejections.map((r) => (_jsx("li", { className: "fileupload-error", children: `${r.file.name}: ${r.errors.map((e) => t.errors[e] ?? e).join(" ")}` }, r.file.name))) }))] }));
80
+ return (_jsxs("div", { ...rest, className: fileuploadRecipe({ className }), children: [_jsxs("button", { ...dropzoneProps, ref: dropzoneRef, type: "button", className: "fileupload-zone", tabIndex: disabled ? -1 : 0, "aria-label": ariaLabel ?? t.hint, disabled: disabled, "data-dragging": dragging ? "" : undefined, "data-disabled": disabled ? "" : undefined, "data-invalid": invalid ? "" : undefined, onClick: () => openFilePicker(), children: [_jsx("span", { className: "fileupload-title", children: t.title }), _jsx("span", { className: "fileupload-hint", children: t.hint })] }), _jsx("input", { ...hiddenInputProps, ref: inputRef, className: "fileupload-input" }), acceptedFileEntries.length > 0 && (_jsx("ul", { className: "fileupload-list", children: acceptedFileEntries.map((entry) => (_jsxs("li", { className: "fileupload-item", "data-status": entry.status, children: [_jsx("span", { className: "fileupload-item-name", children: entry.file.name }), entry.status === "uploading" && entry.progress != null && (_jsx("span", { className: "fileupload-progress", role: "progressbar", "aria-valuenow": Math.round(entry.progress), "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": entry.file.name, children: _jsx("span", { className: "fileupload-progress-fill", style: { width: `${entry.progress}%` } }) })), _jsx("span", { className: "fileupload-item-size", children: formatSize(entry.file.size) }), _jsx("button", { type: "button", className: "fileupload-remove", "aria-label": `${t.remove}: ${entry.file.name}`, onClick: () => removeFileEntry(entry.id), children: _jsx(X, { size: 13, strokeWidth: 2 }) })] }, entry.id))) })), _jsx("ul", { className: "fileupload-errors", "aria-live": "polite", children: rejections.map((r) => (_jsx("li", { className: "fileupload-error", children: `${r.file.name}: ${r.errors.map((e) => t.errors[e] ?? e).join(" ")}` }, r.file.name))) })] }));
81
81
  }
@@ -89,6 +89,10 @@ const config = defineRecipe({
89
89
  & .fileupload-remove:focus-visible { outline-color: ${c.strokeFocusRing}; }
90
90
 
91
91
  & .fileupload-errors { display: flex; flex-direction: column; gap: ${c.space["0.5"]}; margin: 0; padding: 0; list-style: none; }
92
+ /* out of flow while empty, so the always-mounted live region costs no
93
+ layout: display:none would take it out of the accessibility tree, which
94
+ is the one thing it exists to be in */
95
+ & .fileupload-errors:empty { position: absolute; }
92
96
  & .fileupload-error { color: ${c.bgDanger}; font-size: ${c.text.md}; line-height: 18px; }
93
97
  `,
94
98
  variants: {},
@@ -11,7 +11,7 @@ const ICONS = {
11
11
  };
12
12
  function ToastCard({ item, onClose, dismissLabel, }) {
13
13
  const Icon = ICONS[item.tone ?? "info"];
14
- return (_jsxs("div", { className: "toast", role: "status", "data-tone": item.tone ?? "info", children: [_jsx("span", { className: "toast-icon", children: _jsx(Icon, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), _jsxs("div", { className: "toast-body", children: [_jsx("div", { className: "toast-title", children: item.title }), item.description != null && (_jsx("div", { className: "toast-desc", children: item.description })), item.action && (_jsx("button", { type: "button", className: "toast-action", onClick: () => {
14
+ return (_jsxs("div", { className: "toast", "data-tone": item.tone ?? "info", children: [_jsx("span", { className: "toast-icon", children: _jsx(Icon, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), _jsxs("div", { className: "toast-body", children: [_jsx("div", { className: "toast-title", children: item.title }), item.description != null && (_jsx("div", { className: "toast-desc", children: item.description })), item.action && (_jsx("button", { type: "button", className: "toast-action", onClick: () => {
15
15
  item.action?.onClick();
16
16
  onClose();
17
17
  }, children: item.action.label }))] }), _jsx("button", { type: "button", className: "toast-close", "aria-label": dismissLabel, onClick: onClose, children: _jsx(X, { size: 15, strokeWidth: 2, "aria-hidden": "true" }) })] }));
@@ -47,7 +47,7 @@ export function ToastProvider({ children, duration = 4000, dismissLabel = "Dismi
47
47
  return () => map.forEach(clearTimeout);
48
48
  }, []);
49
49
  const api = React.useMemo(() => ({ toast, dismiss }), [toast, dismiss]);
50
- return (_jsxs(ToastContext.Provider, { value: api, children: [children, _jsx("div", { className: toastRecipe(), children: _jsx("div", { className: "toast-viewport", role: "region", "aria-label": regionLabel, children: items.map((t) => (_jsx(ToastCard, { item: t, onClose: () => dismiss(t.id), dismissLabel: dismissLabel }, t.id))) }) })] }));
50
+ return (_jsxs(ToastContext.Provider, { value: api, children: [children, _jsx("div", { className: toastRecipe(), children: _jsx("div", { className: "toast-viewport", role: "region", "aria-label": regionLabel, "aria-live": "polite", "aria-atomic": "false", children: items.map((t) => (_jsx(ToastCard, { item: t, onClose: () => dismiss(t.id), dismissLabel: dismissLabel }, t.id))) }) })] }));
51
51
  }
52
52
  ToastProvider.displayName = "ToastProvider";
53
53
  export function useToast() {
@@ -72,6 +72,10 @@
72
72
  & .fileupload-remove:focus-visible { outline-color: var(--stroke-focus-ring); }
73
73
 
74
74
  & .fileupload-errors { display: flex; flex-direction: column; gap: var(--space-0-5); margin: 0; padding: 0; list-style: none; }
75
+ /* out of flow while empty, so the always-mounted live region costs no
76
+ layout: display:none would take it out of the accessibility tree, which
77
+ is the one thing it exists to be in */
78
+ & .fileupload-errors:empty { position: absolute; }
75
79
  & .fileupload-error { color: var(--bg-danger); font-size: var(--text-md); line-height: 18px; }
76
80
 
77
81
  }
package/dist/styles.css CHANGED
@@ -950,6 +950,10 @@
950
950
  & .fileupload-remove:focus-visible { outline-color: var(--stroke-focus-ring); }
951
951
 
952
952
  & .fileupload-errors { display: flex; flex-direction: column; gap: var(--space-0-5); margin: 0; padding: 0; list-style: none; }
953
+ /* out of flow while empty, so the always-mounted live region costs no
954
+ layout: display:none would take it out of the accessibility tree, which
955
+ is the one thing it exists to be in */
956
+ & .fileupload-errors:empty { position: absolute; }
953
957
  & .fileupload-error { color: var(--bg-danger); font-size: var(--text-md); line-height: 18px; }
954
958
 
955
959
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.23.4",
3
+ "version": "0.23.5",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",