@robr0/design-system 0.1.0 → 0.3.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.
- package/README.md +13 -9
- package/components/Accordion/Accordion.js +0 -1
- package/components/Avatar/Avatar.js +1 -0
- package/components/Breadcrumb/Breadcrumb.css +9 -1
- package/components/Breadcrumb/Breadcrumb.d.ts +7 -1
- package/components/Breadcrumb/Breadcrumb.js +3 -2
- package/components/Button/Button.css +18 -0
- package/components/Button/Button.d.ts +38 -16
- package/components/Button/Button.js +76 -58
- package/components/Card/Card.css +6 -0
- package/components/Card/Card.d.ts +17 -5
- package/components/Card/Card.js +101 -80
- package/components/Checkbox/Checkbox.d.ts +24 -12
- package/components/Checkbox/Checkbox.js +99 -85
- package/components/CircularButton/CircularButton.css +11 -0
- package/components/CircularButton/CircularButton.d.ts +7 -1
- package/components/CircularButton/CircularButton.js +15 -4
- package/components/CodeBlock/CodeBlock.js +10 -1
- package/components/ColorPicker/ColorPicker.css +278 -0
- package/components/ColorPicker/ColorPicker.d.ts +50 -0
- package/components/ColorPicker/ColorPicker.js +338 -0
- package/components/Combobox/Combobox.css +0 -36
- package/components/Combobox/Combobox.d.ts +19 -7
- package/components/Combobox/Combobox.js +106 -89
- package/components/CommandPalette/CommandPalette.css +1 -15
- package/components/CommandPalette/CommandPalette.js +6 -5
- package/components/ContextMenu/ContextMenu.css +262 -0
- package/components/ContextMenu/ContextMenu.d.ts +26 -0
- package/components/ContextMenu/ContextMenu.js +350 -0
- package/components/DateInput/DateInput.css +0 -36
- package/components/DateInput/DateInput.d.ts +18 -21
- package/components/DateInput/DateInput.js +76 -64
- package/components/DatePicker/DatePicker.d.ts +9 -2
- package/components/DatePicker/DatePicker.js +137 -128
- package/components/Dialog/Dialog.d.ts +15 -4
- package/components/Dialog/Dialog.js +136 -118
- package/components/Drawer/Drawer.d.ts +15 -4
- package/components/Drawer/Drawer.js +138 -119
- package/components/Dropdown/Dropdown.css +0 -36
- package/components/Dropdown/Dropdown.d.ts +24 -8
- package/components/Dropdown/Dropdown.js +219 -169
- package/components/DropdownMenu/DropdownMenu.js +10 -7
- package/components/Field/Field.css +80 -0
- package/components/Field/Field.d.ts +50 -0
- package/components/Field/Field.js +58 -0
- package/components/Field/FieldContext.d.ts +42 -0
- package/components/Field/FieldContext.js +7 -0
- package/components/FileInput/FileInput.css +0 -36
- package/components/FileInput/FileInput.d.ts +13 -17
- package/components/FileInput/FileInput.js +168 -141
- package/components/Input/Input.css +0 -43
- package/components/Input/Input.d.ts +23 -22
- package/components/Input/Input.js +93 -66
- package/components/Kbd/Kbd.css +28 -0
- package/components/Kbd/Kbd.d.ts +19 -0
- package/components/Kbd/Kbd.js +14 -0
- package/components/Popover/Popover.d.ts +0 -6
- package/components/Popover/Popover.js +22 -16
- package/components/ProgressBar/ProgressBar.js +1 -1
- package/components/RadioButton/RadioButton.d.ts +26 -13
- package/components/RadioButton/RadioButton.js +95 -70
- package/components/SelectionCard/SelectionCard.css +6 -2
- package/components/SelectionCard/SelectionCard.d.ts +10 -4
- package/components/SelectionCard/SelectionCard.js +72 -56
- package/components/Skeleton/Skeleton.css +2 -0
- package/components/Slider/Slider.d.ts +19 -10
- package/components/Slider/Slider.js +50 -40
- package/components/Spinner/Spinner.css +11 -0
- package/components/Spinner/Spinner.d.ts +2 -2
- package/components/Swatch/Swatch.css +52 -0
- package/components/Swatch/Swatch.d.ts +34 -0
- package/components/Swatch/Swatch.js +44 -0
- package/components/Table/Table.css +19 -0
- package/components/Table/Table.d.ts +10 -2
- package/components/Table/Table.js +58 -58
- package/components/Textarea/Textarea.css +0 -43
- package/components/Textarea/Textarea.d.ts +21 -22
- package/components/Textarea/Textarea.js +78 -67
- package/components/ToggleGroup/ToggleGroup.d.ts +18 -8
- package/components/ToggleGroup/ToggleGroup.js +61 -44
- package/components/ToggleSwitch/ToggleSwitch.css +7 -2
- package/components/ToggleSwitch/ToggleSwitch.d.ts +17 -9
- package/components/ToggleSwitch/ToggleSwitch.js +57 -44
- package/components/registry.d.ts +47 -10
- package/components/registry.js +5 -1
- package/components/registry.json +498 -56
- package/components/registry.json.d.ts +498 -56
- package/components/registry.json.js +4 -1
- package/index.d.ts +8 -1
- package/index.js +16 -1
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Everything a labelled form control needs to be announced correctly.
|
|
4
|
+
* Derived once by `Field`; read by a control via `useField()`.
|
|
5
|
+
*
|
|
6
|
+
* Lives in its own module so `Field.tsx` exports only a component — the
|
|
7
|
+
* react-refresh lint rule that keeps Fast Refresh working.
|
|
8
|
+
*/
|
|
9
|
+
export interface FieldContextValue {
|
|
10
|
+
/** id for the control itself — the label's `htmlFor` points at it */
|
|
11
|
+
controlId: string;
|
|
12
|
+
/**
|
|
13
|
+
* id of the rendered `<label>`, or undefined when there is no label.
|
|
14
|
+
*
|
|
15
|
+
* `htmlFor` only associates with *labelable* elements (input, select,
|
|
16
|
+
* textarea, button…). A composite widget built from a `div` — a
|
|
17
|
+
* `role="combobox"` trigger, for instance — is not labelable, so it must
|
|
18
|
+
* point `aria-labelledby` at this id instead.
|
|
19
|
+
*/
|
|
20
|
+
labelId: string | undefined;
|
|
21
|
+
/** id of the helper/error text, or undefined when there is none */
|
|
22
|
+
describedBy: string | undefined;
|
|
23
|
+
/** whether the control should report itself invalid */
|
|
24
|
+
invalid: boolean;
|
|
25
|
+
/** whether the control is required */
|
|
26
|
+
required: boolean;
|
|
27
|
+
/** whether the control is disabled */
|
|
28
|
+
disabled: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare const FieldContext: React.Context<FieldContextValue | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Read the wiring `Field` derived.
|
|
33
|
+
*
|
|
34
|
+
* Returns `null` outside a `Field`, so a control that uses it still renders
|
|
35
|
+
* standalone — always optional-chain the result.
|
|
36
|
+
*
|
|
37
|
+
* ```tsx
|
|
38
|
+
* const field = useField();
|
|
39
|
+
* <input id={field?.controlId} aria-describedby={field?.describedBy} />
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare const useField: () => FieldContextValue | null;
|
|
@@ -14,19 +14,6 @@
|
|
|
14
14
|
LABEL
|
|
15
15
|
============================================ */
|
|
16
16
|
|
|
17
|
-
.ds-file-input__label {
|
|
18
|
-
font-family: var(--font-paragraph-em-family);
|
|
19
|
-
font-size: var(--font-paragraph-em-size);
|
|
20
|
-
font-weight: var(--font-paragraph-em-weight);
|
|
21
|
-
line-height: var(--font-paragraph-em-line-height);
|
|
22
|
-
letter-spacing: var(--font-paragraph-em-letter-spacing);
|
|
23
|
-
color: var(--color-text-primary);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.ds-file-input__required {
|
|
27
|
-
color: var(--color-core-accent-coral);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
17
|
/* ============================================
|
|
31
18
|
DROPZONE
|
|
32
19
|
============================================ */
|
|
@@ -237,16 +224,6 @@
|
|
|
237
224
|
HELPER TEXT
|
|
238
225
|
============================================ */
|
|
239
226
|
|
|
240
|
-
.ds-file-input__helper {
|
|
241
|
-
font-family: var(--font-paragraph-sm-family);
|
|
242
|
-
font-size: var(--font-paragraph-sm-size);
|
|
243
|
-
font-weight: var(--font-paragraph-sm-weight);
|
|
244
|
-
line-height: var(--font-paragraph-sm-line-height);
|
|
245
|
-
letter-spacing: var(--font-paragraph-sm-letter-spacing);
|
|
246
|
-
color: var(--color-text-tertiary);
|
|
247
|
-
margin: 0;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
227
|
/* ============================================
|
|
251
228
|
ERROR STATE
|
|
252
229
|
============================================ */
|
|
@@ -255,18 +232,10 @@
|
|
|
255
232
|
border-color: var(--color-status-error-border);
|
|
256
233
|
}
|
|
257
234
|
|
|
258
|
-
.ds-file-input--error .ds-file-input__helper {
|
|
259
|
-
color: var(--color-status-error-border);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
235
|
/* ============================================
|
|
263
236
|
DISABLED STATE
|
|
264
237
|
============================================ */
|
|
265
238
|
|
|
266
|
-
.ds-file-input--disabled .ds-file-input__label {
|
|
267
|
-
color: var(--color-input-text-disabled);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
239
|
.ds-file-input--disabled .ds-file-input__dropzone {
|
|
271
240
|
background-color: var(--color-input-bg-disabled);
|
|
272
241
|
border-color: var(--color-input-border-disabled);
|
|
@@ -287,11 +256,6 @@
|
|
|
287
256
|
padding: var(--padding-lg); /* 20px */
|
|
288
257
|
}
|
|
289
258
|
|
|
290
|
-
.ds-file-input--compact .ds-file-input__label {
|
|
291
|
-
font-size: var(--font-paragraph-sm-size);
|
|
292
|
-
line-height: var(--font-paragraph-sm-line-height);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
259
|
.ds-file-input--compact .ds-file-input__dropzone-icon {
|
|
296
260
|
--icon-size: var(--icon-size-sm);
|
|
297
261
|
}
|
|
@@ -11,23 +11,16 @@ export interface FileInputFile {
|
|
|
11
11
|
/** Per-file error message — replaces the size line */
|
|
12
12
|
error?: string;
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
/** Props owned by FileInput itself — everything else falls through to the native <input type="file">. */
|
|
15
|
+
type FileInputOwnProps = {
|
|
15
16
|
/** Field label above the dropzone */
|
|
16
17
|
label?: string;
|
|
17
18
|
/** Instructional copy inside the dropzone */
|
|
18
19
|
placeholder?: string;
|
|
19
|
-
/** Comma-separated accept list, e.g. 'image/*,.pdf' */
|
|
20
|
-
accept?: string;
|
|
21
|
-
/** Allow selecting more than one file */
|
|
22
|
-
multiple?: boolean;
|
|
23
20
|
/** Files to list under the dropzone */
|
|
24
21
|
files?: FileInputFile[];
|
|
25
|
-
/** Component size */
|
|
22
|
+
/** Component size (not the native character-width `size` attribute) */
|
|
26
23
|
size?: 'default' | 'compact';
|
|
27
|
-
/** Whether the input is disabled */
|
|
28
|
-
disabled?: boolean;
|
|
29
|
-
/** Whether the input is required */
|
|
30
|
-
required?: boolean;
|
|
31
24
|
/** Error state — shows error styling and message */
|
|
32
25
|
error?: boolean;
|
|
33
26
|
/** Helper or error message displayed below the field */
|
|
@@ -36,19 +29,22 @@ export interface FileInputProps {
|
|
|
36
29
|
onFilesSelected?: (files: File[]) => void;
|
|
37
30
|
/** Called with the id of a file whose remove button was pressed */
|
|
38
31
|
onFileRemove?: (id: string) => void;
|
|
39
|
-
/** Additional CSS classes */
|
|
32
|
+
/** Additional CSS classes — applied to the wrapper, not the <input> */
|
|
40
33
|
className?: string;
|
|
41
|
-
/**
|
|
34
|
+
/** @deprecated Pass the native `aria-label` attribute instead. */
|
|
42
35
|
ariaLabel?: string;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/** HTML id attribute */
|
|
46
|
-
id?: string;
|
|
36
|
+
};
|
|
37
|
+
export interface FileInputProps extends FileInputOwnProps, Omit<React.ComponentPropsWithoutRef<'input'>, keyof FileInputOwnProps | 'type'> {
|
|
47
38
|
}
|
|
48
39
|
/**
|
|
49
40
|
* FileInput component — a click-or-drop zone paired with a list of the
|
|
50
41
|
* chosen files. The list is fully controlled: pass `files` and handle
|
|
51
42
|
* `onFilesSelected` / `onFileRemove` so upload progress and per-file
|
|
52
43
|
* errors stay owned by the host app.
|
|
44
|
+
*
|
|
45
|
+
* Forwards a ref to the underlying `<input type="file">` — useful for
|
|
46
|
+
* programmatically opening the picker or clearing the selection — and spreads
|
|
47
|
+
* unrecognised props onto it.
|
|
53
48
|
*/
|
|
54
|
-
export declare const FileInput:
|
|
49
|
+
export declare const FileInput: React.ForwardRefExoticComponent<FileInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
50
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useRef, useId } from "react";
|
|
2
|
+
import React, { useState, useRef, useId } from "react";
|
|
3
|
+
import { Field } from "../Field/Field.js";
|
|
3
4
|
import "./FileInput.css";
|
|
4
5
|
import "../../fonts/material-symbols.css";
|
|
5
6
|
const formatBytes = (bytes) => {
|
|
@@ -9,101 +10,126 @@ const formatBytes = (bytes) => {
|
|
|
9
10
|
const value = bytes / Math.pow(1024, exponent);
|
|
10
11
|
return `${exponent === 0 ? value : value.toFixed(1)} ${units[exponent]}`;
|
|
11
12
|
};
|
|
12
|
-
const FileInput = (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const handleDragLeave = (e) => {
|
|
54
|
-
if (e.currentTarget.contains(e.relatedTarget)) return;
|
|
55
|
-
setIsDragging(false);
|
|
56
|
-
};
|
|
57
|
-
const openPicker = () => {
|
|
58
|
-
if (!disabled) inputRef.current?.click();
|
|
59
|
-
};
|
|
60
|
-
const handleKeyDown = (e) => {
|
|
61
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
13
|
+
const FileInput = React.forwardRef(
|
|
14
|
+
({
|
|
15
|
+
label,
|
|
16
|
+
placeholder = "Drag and drop, or click to browse",
|
|
17
|
+
accept,
|
|
18
|
+
multiple = false,
|
|
19
|
+
files = [],
|
|
20
|
+
size = "default",
|
|
21
|
+
disabled = false,
|
|
22
|
+
required = false,
|
|
23
|
+
error = false,
|
|
24
|
+
helperText,
|
|
25
|
+
onFilesSelected,
|
|
26
|
+
onFileRemove,
|
|
27
|
+
onChange,
|
|
28
|
+
className = "",
|
|
29
|
+
ariaLabel,
|
|
30
|
+
name,
|
|
31
|
+
id,
|
|
32
|
+
...rest
|
|
33
|
+
}, ref) => {
|
|
34
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
35
|
+
const inputRef = useRef(null);
|
|
36
|
+
const generatedId = useId();
|
|
37
|
+
const inputId = id || name || generatedId;
|
|
38
|
+
const baseClass = "ds-file-input";
|
|
39
|
+
const setInputRef = (node) => {
|
|
40
|
+
inputRef.current = node;
|
|
41
|
+
if (typeof ref === "function") ref(node);
|
|
42
|
+
else if (ref) ref.current = node;
|
|
43
|
+
};
|
|
44
|
+
const emitFiles = (fileList) => {
|
|
45
|
+
if (!fileList || fileList.length === 0) return;
|
|
46
|
+
onFilesSelected?.(Array.from(fileList));
|
|
47
|
+
};
|
|
48
|
+
const handleChange = (e) => {
|
|
49
|
+
onChange?.(e);
|
|
50
|
+
emitFiles(e.target.files);
|
|
51
|
+
e.target.value = "";
|
|
52
|
+
};
|
|
53
|
+
const handleDrop = (e) => {
|
|
62
54
|
e.preventDefault();
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
55
|
+
setIsDragging(false);
|
|
56
|
+
if (disabled) return;
|
|
57
|
+
emitFiles(e.dataTransfer.files);
|
|
58
|
+
};
|
|
59
|
+
const handleDragOver = (e) => {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
if (!disabled) setIsDragging(true);
|
|
62
|
+
};
|
|
63
|
+
const handleDragLeave = (e) => {
|
|
64
|
+
if (e.currentTarget.contains(e.relatedTarget)) return;
|
|
65
|
+
setIsDragging(false);
|
|
66
|
+
};
|
|
67
|
+
const openPicker = () => {
|
|
68
|
+
if (!disabled) inputRef.current?.click();
|
|
69
|
+
};
|
|
70
|
+
const handleKeyDown = (e) => {
|
|
71
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
openPicker();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const classes = [
|
|
77
|
+
baseClass,
|
|
78
|
+
size === "compact" ? `${baseClass}--compact` : "",
|
|
79
|
+
error ? `${baseClass}--error` : "",
|
|
80
|
+
disabled ? `${baseClass}--disabled` : "",
|
|
81
|
+
isDragging ? `${baseClass}--dragging` : "",
|
|
82
|
+
className
|
|
83
|
+
].filter(Boolean).join(" ");
|
|
84
|
+
return /* @__PURE__ */ jsxs(
|
|
85
|
+
Field,
|
|
84
86
|
{
|
|
85
|
-
className:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
onDrop: handleDrop,
|
|
94
|
-
onDragOver: handleDragOver,
|
|
95
|
-
onDragLeave: handleDragLeave,
|
|
87
|
+
className: classes,
|
|
88
|
+
label,
|
|
89
|
+
helperText,
|
|
90
|
+
error,
|
|
91
|
+
required,
|
|
92
|
+
disabled,
|
|
93
|
+
size,
|
|
94
|
+
id: inputId,
|
|
96
95
|
children: [
|
|
97
|
-
/* @__PURE__ */
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
/* @__PURE__ */ jsxs(
|
|
97
|
+
"div",
|
|
98
|
+
{
|
|
99
|
+
className: `${baseClass}__dropzone`,
|
|
100
|
+
role: "button",
|
|
101
|
+
tabIndex: disabled ? -1 : 0,
|
|
102
|
+
"aria-label": ariaLabel || rest["aria-label"] || placeholder,
|
|
103
|
+
"aria-describedby": helperText ? `${inputId}-helper` : void 0,
|
|
104
|
+
"aria-invalid": error || void 0,
|
|
105
|
+
"aria-disabled": disabled || void 0,
|
|
106
|
+
onClick: openPicker,
|
|
107
|
+
onKeyDown: handleKeyDown,
|
|
108
|
+
onDrop: handleDrop,
|
|
109
|
+
onDragOver: handleDragOver,
|
|
110
|
+
onDragLeave: handleDragLeave,
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ jsx(
|
|
113
|
+
"span",
|
|
114
|
+
{
|
|
115
|
+
className: `${baseClass}__dropzone-icon material-symbols-rounded`,
|
|
116
|
+
"aria-hidden": "true",
|
|
117
|
+
children: "upload_file"
|
|
118
|
+
}
|
|
119
|
+
),
|
|
120
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__dropzone-text`, children: placeholder }),
|
|
121
|
+
accept && /* @__PURE__ */ jsxs("span", { className: `${baseClass}__dropzone-hint`, children: [
|
|
122
|
+
"Accepts ",
|
|
123
|
+
accept
|
|
124
|
+
] })
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
),
|
|
103
128
|
/* @__PURE__ */ jsx(
|
|
104
129
|
"input",
|
|
105
130
|
{
|
|
106
|
-
|
|
131
|
+
...rest,
|
|
132
|
+
ref: setInputRef,
|
|
107
133
|
id: inputId,
|
|
108
134
|
name,
|
|
109
135
|
type: "file",
|
|
@@ -113,65 +139,66 @@ const FileInput = ({
|
|
|
113
139
|
disabled,
|
|
114
140
|
required,
|
|
115
141
|
tabIndex: -1,
|
|
142
|
+
"aria-label": label ? void 0 : ariaLabel || rest["aria-label"] || placeholder,
|
|
116
143
|
onChange: handleChange
|
|
117
144
|
}
|
|
118
|
-
)
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
),
|
|
122
|
-
files.length > 0 && /* @__PURE__ */ jsx("ul", { className: `${baseClass}__list`, children: files.map((file) => /* @__PURE__ */ jsxs(
|
|
123
|
-
"li",
|
|
124
|
-
{
|
|
125
|
-
className: [`${baseClass}__file`, file.error ? `${baseClass}__file--error` : ""].filter(Boolean).join(" "),
|
|
126
|
-
children: [
|
|
127
|
-
/* @__PURE__ */ jsx(
|
|
128
|
-
"span",
|
|
129
|
-
{
|
|
130
|
-
className: `${baseClass}__file-icon material-symbols-rounded`,
|
|
131
|
-
"aria-hidden": "true",
|
|
132
|
-
children: file.error ? "error" : "description"
|
|
133
|
-
}
|
|
134
145
|
),
|
|
135
|
-
/* @__PURE__ */
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
className: `${baseClass}__progress`,
|
|
142
|
-
role: "progressbar",
|
|
143
|
-
"aria-valuenow": file.progress,
|
|
144
|
-
"aria-valuemin": 0,
|
|
145
|
-
"aria-valuemax": 100,
|
|
146
|
-
"aria-label": `Uploading ${file.name}`,
|
|
147
|
-
children: /* @__PURE__ */ jsx(
|
|
146
|
+
files.length > 0 && /* @__PURE__ */ jsx("ul", { className: `${baseClass}__list`, children: files.map((file) => /* @__PURE__ */ jsxs(
|
|
147
|
+
"li",
|
|
148
|
+
{
|
|
149
|
+
className: [`${baseClass}__file`, file.error ? `${baseClass}__file--error` : ""].filter(Boolean).join(" "),
|
|
150
|
+
children: [
|
|
151
|
+
/* @__PURE__ */ jsx(
|
|
148
152
|
"span",
|
|
149
153
|
{
|
|
150
|
-
className: `${baseClass}
|
|
151
|
-
|
|
154
|
+
className: `${baseClass}__file-icon material-symbols-rounded`,
|
|
155
|
+
"aria-hidden": "true",
|
|
156
|
+
children: file.error ? "error" : "description"
|
|
157
|
+
}
|
|
158
|
+
),
|
|
159
|
+
/* @__PURE__ */ jsxs("span", { className: `${baseClass}__file-details`, children: [
|
|
160
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__file-name`, children: file.name }),
|
|
161
|
+
file.error ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__file-error`, children: file.error }) : file.size !== void 0 && /* @__PURE__ */ jsx("span", { className: `${baseClass}__file-size`, children: formatBytes(file.size) }),
|
|
162
|
+
file.error === void 0 && file.progress !== void 0 && /* @__PURE__ */ jsx(
|
|
163
|
+
"span",
|
|
164
|
+
{
|
|
165
|
+
className: `${baseClass}__progress`,
|
|
166
|
+
role: "progressbar",
|
|
167
|
+
"aria-valuenow": file.progress,
|
|
168
|
+
"aria-valuemin": 0,
|
|
169
|
+
"aria-valuemax": 100,
|
|
170
|
+
"aria-label": `Uploading ${file.name}`,
|
|
171
|
+
children: /* @__PURE__ */ jsx(
|
|
172
|
+
"span",
|
|
173
|
+
{
|
|
174
|
+
className: `${baseClass}__progress-fill`,
|
|
175
|
+
style: { width: `${Math.min(100, Math.max(0, file.progress))}%` }
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
] }),
|
|
181
|
+
onFileRemove && /* @__PURE__ */ jsx(
|
|
182
|
+
"button",
|
|
183
|
+
{
|
|
184
|
+
type: "button",
|
|
185
|
+
className: `${baseClass}__file-remove`,
|
|
186
|
+
onClick: () => onFileRemove(file.id),
|
|
187
|
+
disabled,
|
|
188
|
+
"aria-label": `Remove ${file.name}`,
|
|
189
|
+
children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: "close" })
|
|
152
190
|
}
|
|
153
191
|
)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
"button",
|
|
159
|
-
{
|
|
160
|
-
type: "button",
|
|
161
|
-
className: `${baseClass}__file-remove`,
|
|
162
|
-
onClick: () => onFileRemove(file.id),
|
|
163
|
-
disabled,
|
|
164
|
-
"aria-label": `Remove ${file.name}`,
|
|
165
|
-
children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: "close" })
|
|
166
|
-
}
|
|
167
|
-
)
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
file.id
|
|
195
|
+
)) })
|
|
168
196
|
]
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
};
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
FileInput.displayName = "FileInput";
|
|
175
202
|
export {
|
|
176
203
|
FileInput
|
|
177
204
|
};
|
|
@@ -10,23 +10,6 @@
|
|
|
10
10
|
width: 100%;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
/* ============================================
|
|
14
|
-
LABEL
|
|
15
|
-
============================================ */
|
|
16
|
-
|
|
17
|
-
.ds-input__label {
|
|
18
|
-
font-family: var(--font-paragraph-em-family);
|
|
19
|
-
font-size: var(--font-paragraph-em-size);
|
|
20
|
-
font-weight: var(--font-paragraph-em-weight);
|
|
21
|
-
line-height: var(--font-paragraph-em-line-height);
|
|
22
|
-
letter-spacing: var(--font-paragraph-em-letter-spacing);
|
|
23
|
-
color: var(--color-text-primary);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.ds-input__required {
|
|
27
|
-
color: var(--color-core-accent-coral);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
13
|
/* ============================================
|
|
31
14
|
FIELD WRAPPER
|
|
32
15
|
============================================ */
|
|
@@ -103,20 +86,6 @@
|
|
|
103
86
|
padding-right: calc(var(--padding-md) + 24px + var(--gap-sm)); /* 16px + 24px icon + 8px gap */
|
|
104
87
|
}
|
|
105
88
|
|
|
106
|
-
/* ============================================
|
|
107
|
-
HELPER TEXT
|
|
108
|
-
============================================ */
|
|
109
|
-
|
|
110
|
-
.ds-input__helper {
|
|
111
|
-
font-family: var(--font-paragraph-sm-family);
|
|
112
|
-
font-size: var(--font-paragraph-sm-size);
|
|
113
|
-
font-weight: var(--font-paragraph-sm-weight);
|
|
114
|
-
line-height: var(--font-paragraph-sm-line-height);
|
|
115
|
-
letter-spacing: var(--font-paragraph-sm-letter-spacing);
|
|
116
|
-
color: var(--color-text-tertiary);
|
|
117
|
-
margin: 0;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
89
|
/* ============================================
|
|
121
90
|
ERROR STATE
|
|
122
91
|
============================================ */
|
|
@@ -129,18 +98,10 @@
|
|
|
129
98
|
border-color: var(--color-status-error-border);
|
|
130
99
|
}
|
|
131
100
|
|
|
132
|
-
.ds-input--error .ds-input__helper {
|
|
133
|
-
color: var(--color-status-error-border);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
101
|
/* ============================================
|
|
137
102
|
DISABLED STATE
|
|
138
103
|
============================================ */
|
|
139
104
|
|
|
140
|
-
.ds-input--disabled .ds-input__label {
|
|
141
|
-
color: var(--color-input-text-disabled);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
105
|
.ds-input--disabled .ds-input__field {
|
|
145
106
|
background-color: var(--color-input-bg-disabled);
|
|
146
107
|
border-color: var(--color-input-border-disabled);
|
|
@@ -187,7 +148,3 @@
|
|
|
187
148
|
padding-right: calc(var(--padding-sm-md) + 20px + var(--gap-sm)); /* 12px + 20px icon + 8px gap */
|
|
188
149
|
}
|
|
189
150
|
|
|
190
|
-
.ds-input--compact .ds-input__label {
|
|
191
|
-
font-size: var(--font-paragraph-sm-size);
|
|
192
|
-
line-height: var(--font-paragraph-sm-line-height);
|
|
193
|
-
}
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
|
|
2
|
+
/** Props owned by Input itself — everything else falls through to the <input>. */
|
|
3
|
+
type InputOwnProps = {
|
|
3
4
|
/** Input label text */
|
|
4
5
|
label?: string;
|
|
5
|
-
/** Placeholder text */
|
|
6
|
-
placeholder?: string;
|
|
7
6
|
/** Current value */
|
|
8
7
|
value?: string;
|
|
9
|
-
/** Input type */
|
|
8
|
+
/** Input type — curated subset; use a dedicated component for checkbox/radio/file */
|
|
10
9
|
type?: 'text' | 'email' | 'password' | 'number' | 'search' | 'tel' | 'url';
|
|
11
|
-
/** Component size */
|
|
10
|
+
/** Component size (not the native character-width `size` attribute) */
|
|
12
11
|
size?: 'default' | 'compact';
|
|
13
|
-
/** Whether the input is disabled */
|
|
14
|
-
disabled?: boolean;
|
|
15
|
-
/** Whether the input is required */
|
|
16
|
-
required?: boolean;
|
|
17
12
|
/** Error state — shows error styling and message */
|
|
18
13
|
error?: boolean;
|
|
19
14
|
/** Helper or error message displayed below the input */
|
|
@@ -22,19 +17,25 @@ export interface InputProps {
|
|
|
22
17
|
iconLeft?: string;
|
|
23
18
|
/** Material Symbol icon name on the right */
|
|
24
19
|
iconRight?: string;
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
/** Additional CSS classes */
|
|
20
|
+
/**
|
|
21
|
+
* Convenience callback receiving the value directly.
|
|
22
|
+
* Fires alongside `onChange`, which keeps the standard React event signature
|
|
23
|
+
* so form libraries (react-hook-form, Formik, TanStack Form) work unmodified.
|
|
24
|
+
*/
|
|
25
|
+
onValueChange?: (value: string) => void;
|
|
26
|
+
/** Additional CSS classes — applied to the wrapper, not the <input> */
|
|
32
27
|
className?: string;
|
|
33
|
-
/**
|
|
28
|
+
/** @deprecated Pass the native `aria-label` attribute instead. */
|
|
34
29
|
ariaLabel?: string;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/** HTML id attribute */
|
|
38
|
-
id?: string;
|
|
30
|
+
};
|
|
31
|
+
export interface InputProps extends InputOwnProps, Omit<React.ComponentPropsWithoutRef<'input'>, keyof InputOwnProps> {
|
|
39
32
|
}
|
|
40
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Text input with optional label, helper text, icons and error state.
|
|
35
|
+
*
|
|
36
|
+
* Forwards a ref to the underlying `<input>` and spreads unrecognised props onto
|
|
37
|
+
* it, so `autoComplete`, `maxLength`, `inputMode`, `data-*` and form-library
|
|
38
|
+
* registration all work.
|
|
39
|
+
*/
|
|
40
|
+
export declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
|
|
41
|
+
export {};
|