@robr0/design-system 0.2.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 +8 -6
- package/components/Accordion/Accordion.js +0 -1
- package/components/Avatar/Avatar.js +1 -0
- 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 +6 -0
- package/components/Button/Button.js +13 -3
- package/components/Card/Card.css +6 -0
- 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.js +90 -82
- 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.js +37 -33
- package/components/DatePicker/DatePicker.js +45 -34
- package/components/Dialog/Dialog.js +5 -4
- package/components/Drawer/Drawer.js +7 -2
- package/components/Dropdown/Dropdown.css +0 -36
- package/components/Dropdown/Dropdown.js +119 -109
- 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.js +111 -103
- package/components/Input/Input.css +0 -43
- package/components/Input/Input.js +51 -46
- 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/SelectionCard/SelectionCard.css +6 -2
- package/components/Skeleton/Skeleton.css +2 -0
- 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.js +1 -1
- package/components/Textarea/Textarea.css +0 -43
- package/components/Textarea/Textarea.js +37 -35
- package/components/ToggleSwitch/ToggleSwitch.css +7 -2
- 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,19 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** Props owned by Kbd itself — everything else falls through to the <kbd> element. */
|
|
3
|
+
type KbdOwnProps = {
|
|
4
|
+
/** Key size */
|
|
5
|
+
size?: 'default' | 'compact';
|
|
6
|
+
/** Additional CSS classes */
|
|
7
|
+
className?: string;
|
|
8
|
+
/** The key legend — e.g. ⌘, K, Esc, Shift */
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
};
|
|
11
|
+
export interface KbdProps extends KbdOwnProps, Omit<React.ComponentPropsWithoutRef<'kbd'>, keyof KbdOwnProps> {
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Kbd renders a single keyboard key as a keycap, for shortcut hints in
|
|
15
|
+
* menus and docs prose. Compose several for a chord: `<Kbd>⌘</Kbd><Kbd>K</Kbd>`.
|
|
16
|
+
* Purely presentational — safe to render from a Server Component.
|
|
17
|
+
*/
|
|
18
|
+
export declare const Kbd: React.ForwardRefExoticComponent<KbdProps & React.RefAttributes<HTMLElement>>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import "./Kbd.css";
|
|
4
|
+
const Kbd = React.forwardRef(
|
|
5
|
+
({ size = "default", className = "", children, ...rest }, ref) => {
|
|
6
|
+
const baseClass = "ds-kbd";
|
|
7
|
+
const classes = [baseClass, `${baseClass}--${size}`, className].filter(Boolean).join(" ");
|
|
8
|
+
return /* @__PURE__ */ jsx("kbd", { ...rest, ref, className: classes, children });
|
|
9
|
+
}
|
|
10
|
+
);
|
|
11
|
+
Kbd.displayName = "Kbd";
|
|
12
|
+
export {
|
|
13
|
+
Kbd
|
|
14
|
+
};
|
|
@@ -19,10 +19,4 @@ export interface PopoverProps {
|
|
|
19
19
|
/** Additional CSS classes for the popover panel */
|
|
20
20
|
className?: string;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Popover component for contextual overlays.
|
|
24
|
-
* Wraps a trigger element and displays content in a
|
|
25
|
-
* positioned panel. Supports click and hover triggers
|
|
26
|
-
* with automatic outside-click dismissal.
|
|
27
|
-
*/
|
|
28
22
|
export declare const Popover: ({ children, content, size, position, trigger, open: controlledOpen, onOpenChange, ariaLabel, className, }: PopoverProps) => React.JSX.Element;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useRef, useCallback, useEffect } from "react";
|
|
2
|
+
import React, { useState, useRef, useCallback, useEffect } from "react";
|
|
3
3
|
import "./Popover.css";
|
|
4
|
+
const INTERACTIVE_TAGS = /* @__PURE__ */ new Set(["button", "a", "input", "select", "textarea", "summary"]);
|
|
5
|
+
const canCarryPopoverSemantics = (el) => typeof el.type === "string" ? INTERACTIVE_TAGS.has(el.type) : true;
|
|
4
6
|
const Popover = ({
|
|
5
7
|
children,
|
|
6
8
|
content,
|
|
@@ -73,26 +75,30 @@ const Popover = ({
|
|
|
73
75
|
onMouseEnter: handleMouseEnter,
|
|
74
76
|
onMouseLeave: handleMouseLeave,
|
|
75
77
|
children: [
|
|
76
|
-
/* @__PURE__ */ jsx(
|
|
77
|
-
"
|
|
78
|
+
/* @__PURE__ */ jsx("div", { className: `${baseClass}__trigger`, onClick: handleClick, children: React.isValidElement(children) && canCarryPopoverSemantics(children) ? React.cloneElement(children, {
|
|
79
|
+
"aria-expanded": isOpen,
|
|
80
|
+
"aria-haspopup": "dialog",
|
|
81
|
+
...ariaLabel ? { "aria-label": ariaLabel } : {}
|
|
82
|
+
}) : trigger === "click" ? /* @__PURE__ */ jsx(
|
|
83
|
+
"button",
|
|
78
84
|
{
|
|
79
|
-
|
|
80
|
-
onClick: handleClick,
|
|
81
|
-
role: trigger === "click" ? "button" : void 0,
|
|
82
|
-
tabIndex: trigger === "click" ? 0 : void 0,
|
|
85
|
+
type: "button",
|
|
83
86
|
"aria-expanded": isOpen,
|
|
84
|
-
"aria-label": ariaLabel,
|
|
85
87
|
"aria-haspopup": "dialog",
|
|
86
|
-
|
|
87
|
-
if (trigger === "click" && (e.key === "Enter" || e.key === " ")) {
|
|
88
|
-
e.preventDefault();
|
|
89
|
-
setOpen(!isOpen);
|
|
90
|
-
}
|
|
91
|
-
},
|
|
88
|
+
"aria-label": ariaLabel,
|
|
92
89
|
children
|
|
93
90
|
}
|
|
94
|
-
),
|
|
95
|
-
/* @__PURE__ */ jsx(
|
|
91
|
+
) : children }),
|
|
92
|
+
/* @__PURE__ */ jsx(
|
|
93
|
+
"div",
|
|
94
|
+
{
|
|
95
|
+
className: panelClasses,
|
|
96
|
+
role: "dialog",
|
|
97
|
+
"aria-label": ariaLabel || "Popover",
|
|
98
|
+
"aria-hidden": !isOpen,
|
|
99
|
+
children: content
|
|
100
|
+
}
|
|
101
|
+
)
|
|
96
102
|
]
|
|
97
103
|
}
|
|
98
104
|
);
|
|
@@ -14,7 +14,7 @@ const ProgressBar = ({
|
|
|
14
14
|
`${baseClass}--${size}`,
|
|
15
15
|
className
|
|
16
16
|
].filter(Boolean).join(" ");
|
|
17
|
-
return /* @__PURE__ */ jsxs("div", { className: classes, role: "progressbar", "aria-valuenow": clamped, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": ariaLabel, children: [
|
|
17
|
+
return /* @__PURE__ */ jsxs("div", { className: classes, role: "progressbar", "aria-valuenow": clamped, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": ariaLabel || "Progress", children: [
|
|
18
18
|
/* @__PURE__ */ jsx("div", { className: `${baseClass}__track`, children: /* @__PURE__ */ jsx("div", { className: `${baseClass}__fill`, style: { width: `${clamped}%` } }) }),
|
|
19
19
|
showLabel && /* @__PURE__ */ jsxs("span", { className: `${baseClass}__label`, children: [
|
|
20
20
|
clamped,
|
|
@@ -232,8 +232,12 @@
|
|
|
232
232
|
.ds-selection-card__toggle-icon {
|
|
233
233
|
/* Intentionally off the --icon-size-* scale — same reason as ToggleSwitch:
|
|
234
234
|
this glyph lives inside the 20x20 toggle thumb, and the smallest scale
|
|
235
|
-
step (20px) would fill it completely.
|
|
236
|
-
|
|
235
|
+
step (20px) would fill it completely. Sized via --icon-size, not
|
|
236
|
+
font-size, so the icon's box shrinks with the glyph (see ToggleSwitch).
|
|
237
|
+
|
|
238
|
+
ds-allow(icon-size): 14px glyph inside the 20px thumb — the smallest
|
|
239
|
+
scale step would fill it edge to edge. */
|
|
240
|
+
--icon-size: 14px;
|
|
237
241
|
color: var(--color-action-primary-bg);
|
|
238
242
|
}
|
|
239
243
|
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
var(--color-bg-container-primary) 50%,
|
|
23
23
|
transparent 100%
|
|
24
24
|
);
|
|
25
|
+
/* ds-allow(motion): the shimmer needs a symmetric in-out curve and no
|
|
26
|
+
--motion-ease-* token provides one — the duration is tokenized. */
|
|
25
27
|
animation: ds-skeleton-shimmer var(--motion-duration-loop-shimmer) ease-in-out infinite;
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -59,6 +59,17 @@
|
|
|
59
59
|
stroke: var(--color-text-secondary);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/* Inherit — both circles draw in currentColor so the spinner matches the
|
|
63
|
+
text/icon colour of whatever control contains it (e.g. a loading Button) */
|
|
64
|
+
.ds-spinner--inherit .ds-spinner__track {
|
|
65
|
+
stroke: currentColor;
|
|
66
|
+
opacity: 0.25;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.ds-spinner--inherit .ds-spinner__arc {
|
|
70
|
+
stroke: currentColor;
|
|
71
|
+
}
|
|
72
|
+
|
|
62
73
|
/* ============================================
|
|
63
74
|
ANIMATION
|
|
64
75
|
============================================ */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export interface SpinnerProps {
|
|
2
2
|
/** Size of the spinner */
|
|
3
3
|
size?: 'sm' | 'md' | 'lg';
|
|
4
|
-
/** Visual variant */
|
|
5
|
-
variant?: 'primary' | 'neutral';
|
|
4
|
+
/** Visual variant — `inherit` draws the spinner in `currentColor`, for use inside coloured controls */
|
|
5
|
+
variant?: 'primary' | 'neutral' | 'inherit';
|
|
6
6
|
/** Accessible label */
|
|
7
7
|
label?: string;
|
|
8
8
|
/** Additional CSS classes */
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
SWATCH COMPONENT
|
|
3
|
+
Clickable colour tile for palettes and pickers
|
|
4
|
+
============================================ */
|
|
5
|
+
|
|
6
|
+
/* Base */
|
|
7
|
+
|
|
8
|
+
.ds-swatch {
|
|
9
|
+
width: 24px;
|
|
10
|
+
height: 24px;
|
|
11
|
+
padding: 0;
|
|
12
|
+
border: none;
|
|
13
|
+
border-radius: var(--radius-full);
|
|
14
|
+
background-color: var(--ds-swatch-color);
|
|
15
|
+
/* A hairline inset keeps very light colours visible on the white floor */
|
|
16
|
+
box-shadow: inset 0 0 0 var(--border-xs) var(--color-bg-container-border);
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
flex-shrink: 0;
|
|
19
|
+
transition: box-shadow var(--motion-duration-base) var(--motion-ease-standard);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Sizes */
|
|
23
|
+
|
|
24
|
+
.ds-swatch--compact {
|
|
25
|
+
width: 20px;
|
|
26
|
+
height: 20px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Shapes */
|
|
30
|
+
|
|
31
|
+
.ds-swatch--square {
|
|
32
|
+
border-radius: var(--radius-sm);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* States */
|
|
36
|
+
|
|
37
|
+
.ds-swatch--selected {
|
|
38
|
+
box-shadow:
|
|
39
|
+
inset 0 0 0 var(--border-xs) var(--color-bg-container-border),
|
|
40
|
+
0 0 0 var(--border-md) var(--color-bg-page-primary),
|
|
41
|
+
0 0 0 calc(var(--border-md) * 2) var(--color-text-primary);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.ds-swatch:focus-visible {
|
|
45
|
+
outline: var(--border-md) solid var(--color-input-border-selected);
|
|
46
|
+
outline-offset: var(--border-md);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.ds-swatch--disabled {
|
|
50
|
+
opacity: 0.4;
|
|
51
|
+
cursor: not-allowed;
|
|
52
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** Props owned by Swatch itself — everything else falls through to the <button>. */
|
|
3
|
+
type SwatchOwnProps = {
|
|
4
|
+
/** The colour this swatch shows — any valid CSS colour, typically a hex value */
|
|
5
|
+
value: string;
|
|
6
|
+
/**
|
|
7
|
+
* Accessible name for the swatch. Defaults to the colour value, which is
|
|
8
|
+
* meaningful but terse — prefer a human name ("Teal 07") when you have one.
|
|
9
|
+
*/
|
|
10
|
+
label?: string;
|
|
11
|
+
/** Selected state — renders the selection ring and sets aria-pressed */
|
|
12
|
+
selected?: boolean;
|
|
13
|
+
/** Swatch size */
|
|
14
|
+
size?: 'default' | 'compact';
|
|
15
|
+
/** Corner treatment — circle matches the site's preset grids; square suits picker triggers */
|
|
16
|
+
shape?: 'circle' | 'square';
|
|
17
|
+
/** Whether the swatch is disabled */
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
/** Additional CSS classes */
|
|
20
|
+
className?: string;
|
|
21
|
+
};
|
|
22
|
+
export interface SwatchProps extends SwatchOwnProps, Omit<React.ComponentPropsWithoutRef<'button'>, keyof SwatchOwnProps | 'type'> {
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A clickable colour tile — the building block for preset palettes and
|
|
26
|
+
* picker triggers. Renders a `<button>` whose background is the `value`
|
|
27
|
+
* colour; `selected` draws the theme-aware selection ring.
|
|
28
|
+
*
|
|
29
|
+
* Purely presentational (no hooks), so it can be rendered from a Server
|
|
30
|
+
* Component; interactivity arrives through the native `onClick` you pass.
|
|
31
|
+
* Forwards a ref to the `<button>` and spreads unrecognised props onto it.
|
|
32
|
+
*/
|
|
33
|
+
export declare const Swatch: React.ForwardRefExoticComponent<SwatchProps & React.RefAttributes<HTMLButtonElement>>;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import "./Swatch.css";
|
|
4
|
+
const Swatch = React.forwardRef(
|
|
5
|
+
({
|
|
6
|
+
value,
|
|
7
|
+
label,
|
|
8
|
+
selected = false,
|
|
9
|
+
size = "default",
|
|
10
|
+
shape = "circle",
|
|
11
|
+
disabled = false,
|
|
12
|
+
className = "",
|
|
13
|
+
style,
|
|
14
|
+
...rest
|
|
15
|
+
}, ref) => {
|
|
16
|
+
const baseClass = "ds-swatch";
|
|
17
|
+
const classes = [
|
|
18
|
+
baseClass,
|
|
19
|
+
size === "compact" ? `${baseClass}--compact` : "",
|
|
20
|
+
shape === "square" ? `${baseClass}--square` : "",
|
|
21
|
+
selected ? `${baseClass}--selected` : "",
|
|
22
|
+
disabled ? `${baseClass}--disabled` : "",
|
|
23
|
+
className
|
|
24
|
+
].filter(Boolean).join(" ");
|
|
25
|
+
const swatchStyle = { ...style, "--ds-swatch-color": value };
|
|
26
|
+
return /* @__PURE__ */ jsx(
|
|
27
|
+
"button",
|
|
28
|
+
{
|
|
29
|
+
...rest,
|
|
30
|
+
ref,
|
|
31
|
+
type: "button",
|
|
32
|
+
className: classes,
|
|
33
|
+
style: swatchStyle,
|
|
34
|
+
disabled,
|
|
35
|
+
"aria-pressed": selected,
|
|
36
|
+
"aria-label": rest["aria-label"] ?? label ?? value
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
Swatch.displayName = "Swatch";
|
|
42
|
+
export {
|
|
43
|
+
Swatch
|
|
44
|
+
};
|
|
@@ -184,3 +184,22 @@
|
|
|
184
184
|
line-height: var(--font-paragraph-sm-line-height);
|
|
185
185
|
letter-spacing: var(--font-paragraph-sm-letter-spacing);
|
|
186
186
|
}
|
|
187
|
+
|
|
188
|
+
/* ============================================
|
|
189
|
+
SCREEN-READER-ONLY
|
|
190
|
+
Fallback text for header cells with no visible
|
|
191
|
+
label (checkbox / actions columns) — an empty
|
|
192
|
+
<th> is unannounced.
|
|
193
|
+
============================================ */
|
|
194
|
+
|
|
195
|
+
.ds-table__sr-only {
|
|
196
|
+
position: absolute;
|
|
197
|
+
width: 1px;
|
|
198
|
+
height: 1px;
|
|
199
|
+
padding: 0;
|
|
200
|
+
margin: -1px;
|
|
201
|
+
overflow: hidden;
|
|
202
|
+
clip: rect(0, 0, 0, 0);
|
|
203
|
+
white-space: nowrap;
|
|
204
|
+
border: 0;
|
|
205
|
+
}
|
|
@@ -38,7 +38,7 @@ const Table = React.forwardRef(
|
|
|
38
38
|
width: col.width,
|
|
39
39
|
textAlign: col.align || "left"
|
|
40
40
|
},
|
|
41
|
-
children: col.header
|
|
41
|
+
children: col.header || /* @__PURE__ */ jsx("span", { className: `${baseClass}__sr-only`, children: col.key })
|
|
42
42
|
},
|
|
43
43
|
col.key
|
|
44
44
|
)) }) }),
|
|
@@ -14,19 +14,6 @@
|
|
|
14
14
|
LABEL
|
|
15
15
|
============================================ */
|
|
16
16
|
|
|
17
|
-
.ds-textarea__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-textarea__required {
|
|
27
|
-
color: var(--color-core-accent-coral);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
17
|
/* ============================================
|
|
31
18
|
FIELD
|
|
32
19
|
============================================ */
|
|
@@ -69,24 +56,6 @@
|
|
|
69
56
|
FOOTER (helper + counter)
|
|
70
57
|
============================================ */
|
|
71
58
|
|
|
72
|
-
.ds-textarea__footer {
|
|
73
|
-
display: flex;
|
|
74
|
-
align-items: flex-start;
|
|
75
|
-
justify-content: space-between;
|
|
76
|
-
gap: var(--gap-sm);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.ds-textarea__helper {
|
|
80
|
-
font-family: var(--font-paragraph-sm-family);
|
|
81
|
-
font-size: var(--font-paragraph-sm-size);
|
|
82
|
-
font-weight: var(--font-paragraph-sm-weight);
|
|
83
|
-
line-height: var(--font-paragraph-sm-line-height);
|
|
84
|
-
letter-spacing: var(--font-paragraph-sm-letter-spacing);
|
|
85
|
-
color: var(--color-text-tertiary);
|
|
86
|
-
margin: 0;
|
|
87
|
-
flex: 1;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
59
|
.ds-textarea__counter {
|
|
91
60
|
font-family: var(--font-paragraph-sm-family);
|
|
92
61
|
font-size: var(--font-paragraph-sm-size);
|
|
@@ -108,10 +77,6 @@
|
|
|
108
77
|
border-color: var(--color-status-error-border);
|
|
109
78
|
}
|
|
110
79
|
|
|
111
|
-
.ds-textarea--error .ds-textarea__helper {
|
|
112
|
-
color: var(--color-status-error-border);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
80
|
.ds-textarea--error .ds-textarea__counter {
|
|
116
81
|
color: var(--color-status-error-border);
|
|
117
82
|
}
|
|
@@ -120,10 +85,6 @@
|
|
|
120
85
|
DISABLED STATE
|
|
121
86
|
============================================ */
|
|
122
87
|
|
|
123
|
-
.ds-textarea--disabled .ds-textarea__label {
|
|
124
|
-
color: var(--color-input-text-disabled);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
88
|
.ds-textarea--disabled .ds-textarea__field {
|
|
128
89
|
background-color: var(--color-input-bg-disabled);
|
|
129
90
|
border-color: var(--color-input-border-disabled);
|
|
@@ -146,7 +107,3 @@
|
|
|
146
107
|
letter-spacing: var(--font-paragraph-sm-letter-spacing);
|
|
147
108
|
}
|
|
148
109
|
|
|
149
|
-
.ds-textarea--compact .ds-textarea__label {
|
|
150
|
-
font-size: var(--font-paragraph-sm-size);
|
|
151
|
-
line-height: var(--font-paragraph-sm-line-height);
|
|
152
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
+
import { Field } from "../Field/Field.js";
|
|
3
4
|
import "./Textarea.css";
|
|
4
5
|
const Textarea = React.forwardRef(
|
|
5
6
|
({
|
|
@@ -37,44 +38,45 @@ const Textarea = React.forwardRef(
|
|
|
37
38
|
};
|
|
38
39
|
const inputId = id || name || label?.toLowerCase().replace(/\s+/g, "-");
|
|
39
40
|
const charCount = value?.length || 0;
|
|
40
|
-
return /* @__PURE__ */
|
|
41
|
-
|
|
41
|
+
return /* @__PURE__ */ jsx(
|
|
42
|
+
Field,
|
|
43
|
+
{
|
|
44
|
+
className: classes,
|
|
42
45
|
label,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
{
|
|
51
|
-
...rest,
|
|
52
|
-
ref,
|
|
53
|
-
className: `${baseClass}__field`,
|
|
54
|
-
id: inputId,
|
|
55
|
-
name,
|
|
56
|
-
value,
|
|
57
|
-
placeholder,
|
|
58
|
-
rows,
|
|
59
|
-
disabled,
|
|
60
|
-
required,
|
|
61
|
-
maxLength,
|
|
62
|
-
"aria-label": ariaLabel || rest["aria-label"] || label,
|
|
63
|
-
"aria-invalid": error,
|
|
64
|
-
"aria-describedby": helperText ? `${inputId}-helper` : rest["aria-describedby"],
|
|
65
|
-
onChange: handleChange,
|
|
66
|
-
style: { resize, ...style }
|
|
67
|
-
}
|
|
68
|
-
),
|
|
69
|
-
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__footer`, children: [
|
|
70
|
-
helperText && /* @__PURE__ */ jsx("p", { className: `${baseClass}__helper`, id: `${inputId}-helper`, children: helperText }),
|
|
71
|
-
maxLength && /* @__PURE__ */ jsxs("span", { className: `${baseClass}__counter`, "aria-live": "polite", "aria-atomic": "true", children: [
|
|
46
|
+
helperText,
|
|
47
|
+
error,
|
|
48
|
+
required,
|
|
49
|
+
disabled,
|
|
50
|
+
size,
|
|
51
|
+
id: inputId,
|
|
52
|
+
aside: maxLength ? /* @__PURE__ */ jsxs("span", { className: `${baseClass}__counter`, "aria-live": "polite", "aria-atomic": "true", children: [
|
|
72
53
|
charCount,
|
|
73
54
|
"/",
|
|
74
55
|
maxLength
|
|
75
|
-
] })
|
|
76
|
-
|
|
77
|
-
|
|
56
|
+
] }) : void 0,
|
|
57
|
+
children: /* @__PURE__ */ jsx(
|
|
58
|
+
"textarea",
|
|
59
|
+
{
|
|
60
|
+
...rest,
|
|
61
|
+
ref,
|
|
62
|
+
className: `${baseClass}__field`,
|
|
63
|
+
id: inputId,
|
|
64
|
+
name,
|
|
65
|
+
value,
|
|
66
|
+
placeholder,
|
|
67
|
+
rows,
|
|
68
|
+
disabled,
|
|
69
|
+
required,
|
|
70
|
+
maxLength,
|
|
71
|
+
"aria-label": ariaLabel || rest["aria-label"] || label,
|
|
72
|
+
"aria-invalid": error,
|
|
73
|
+
"aria-describedby": helperText ? `${inputId}-helper` : rest["aria-describedby"],
|
|
74
|
+
onChange: handleChange,
|
|
75
|
+
style: { resize, ...style }
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
);
|
|
78
80
|
}
|
|
79
81
|
);
|
|
80
82
|
Textarea.displayName = "Textarea";
|
|
@@ -59,7 +59,10 @@
|
|
|
59
59
|
Set via --icon-size (not font-size): .material-symbols-rounded derives
|
|
60
60
|
font-size, width, and height from it together. Overriding font-size
|
|
61
61
|
alone leaves the box at the 24px default, so the glyph hangs off-centre
|
|
62
|
-
inside the thumb.
|
|
62
|
+
inside the thumb.
|
|
63
|
+
|
|
64
|
+
ds-allow(icon-size): 14px glyph inside the 20px thumb — the smallest
|
|
65
|
+
scale step would fill it edge to edge. */
|
|
63
66
|
--icon-size: 14px;
|
|
64
67
|
color: var(--color-action-primary-bg);
|
|
65
68
|
}
|
|
@@ -123,7 +126,9 @@
|
|
|
123
126
|
|
|
124
127
|
.ds-toggle-switch--compact .ds-toggle-switch__thumb-icon {
|
|
125
128
|
/* Off-scale for the same reason as the default size — the compact thumb
|
|
126
|
-
is only 16x16.
|
|
129
|
+
is only 16x16.
|
|
130
|
+
|
|
131
|
+
ds-allow(icon-size): 12px glyph inside the 16px compact thumb. */
|
|
127
132
|
--icon-size: 12px;
|
|
128
133
|
}
|
|
129
134
|
|
package/components/registry.d.ts
CHANGED
|
@@ -1,19 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* COMPONENT REGISTRY — the single source of truth for the public
|
|
3
|
-
* component
|
|
2
|
+
* COMPONENT REGISTRY — the single source of truth for the public component
|
|
3
|
+
* list, count, and per-component metadata.
|
|
4
|
+
*
|
|
5
|
+
* The data lives in registry.json. Each entry in `components` carries:
|
|
6
|
+
*
|
|
7
|
+
* name the folder under src/components (also the exported name)
|
|
8
|
+
* label the display name — "Navigation" for the Nav folder
|
|
9
|
+
* slug the website route segment — stored, not derived, so the
|
|
10
|
+
* Nav → navigation exception is plain data rather than a
|
|
11
|
+
* special case duplicated across validators
|
|
12
|
+
* description one line, used for the page title, the sidebar search index,
|
|
13
|
+
* and anywhere the component is summarised
|
|
14
|
+
* category one of `componentCategories`
|
|
15
|
+
* client whether the component declares 'use client' — i.e. whether it
|
|
16
|
+
* uses hooks or defines handlers. Presentational components are
|
|
17
|
+
* deliberately false so consumers can render them from a React
|
|
18
|
+
* Server Component.
|
|
4
19
|
*
|
|
5
|
-
* The data lives in registry.json: `components` is the official list of
|
|
6
|
-
* public components (one entry per folder in src/components), and
|
|
7
20
|
* `docOnlyHelpers` names the internal documentation helpers that are
|
|
8
21
|
* deliberately excluded from the public count.
|
|
9
22
|
*
|
|
10
|
-
* scripts/validate-component-registry.mjs compares the registry against
|
|
11
|
-
*
|
|
12
|
-
*
|
|
23
|
+
* scripts/validate-component-registry.mjs compares the registry against the
|
|
24
|
+
* filesystem on every build (library, Storybook, and website), so it cannot
|
|
25
|
+
* silently drift from reality.
|
|
26
|
+
*
|
|
27
|
+
* Anywhere a component count is displayed — the Storybook overview, the
|
|
28
|
+
* website mega-nav, case studies — import COMPONENT_COUNT from here. Never
|
|
29
|
+
* hardcode the number.
|
|
30
|
+
*/
|
|
31
|
+
export interface ComponentMeta {
|
|
32
|
+
/** Folder under src/components, and the exported component name */
|
|
33
|
+
name: string;
|
|
34
|
+
/** Display name — may differ from `name` (Nav → "Navigation") */
|
|
35
|
+
label: string;
|
|
36
|
+
/** Website route segment under /components */
|
|
37
|
+
slug: string;
|
|
38
|
+
/** One-line summary, used for page titles and search */
|
|
39
|
+
description: string;
|
|
40
|
+
/** Grouping — one of `componentCategories` */
|
|
41
|
+
category: string;
|
|
42
|
+
/** Whether the component declares 'use client' */
|
|
43
|
+
client: boolean;
|
|
44
|
+
}
|
|
45
|
+
/** Full metadata for every public component, alphabetical by name. */
|
|
46
|
+
export declare const componentMetadata: readonly ComponentMeta[];
|
|
47
|
+
/** The categories a component may belong to. */
|
|
48
|
+
export declare const componentCategories: readonly string[];
|
|
49
|
+
/**
|
|
50
|
+
* The official public component list, names only.
|
|
13
51
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* here. Never hardcode the number.
|
|
52
|
+
* Unchanged in shape from before the metadata migration, so every existing
|
|
53
|
+
* consumer keeps working without an edit.
|
|
17
54
|
*/
|
|
18
55
|
export declare const componentRegistry: readonly string[];
|
|
19
56
|
/** The official public component count. */
|
package/components/registry.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import registry from "./registry.json.js";
|
|
2
|
-
const
|
|
2
|
+
const componentMetadata = registry.components;
|
|
3
|
+
const componentCategories = registry.categories;
|
|
4
|
+
const componentRegistry = registry.components.map((c) => c.name);
|
|
3
5
|
const COMPONENT_COUNT = registry.components.length;
|
|
4
6
|
export {
|
|
5
7
|
COMPONENT_COUNT,
|
|
8
|
+
componentCategories,
|
|
9
|
+
componentMetadata,
|
|
6
10
|
componentRegistry
|
|
7
11
|
};
|