@wtfalch/design 0.9.1 → 0.10.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/README.md +6 -3
- package/dist/components/Callout.js +1 -1
- package/dist/components/Checkbox.js +1 -1
- package/dist/components/Command.js +3 -3
- package/dist/components/DangerZone.js +1 -1
- package/dist/components/Empty.js +10 -3
- package/dist/components/Field.js +1 -1
- package/dist/components/Identity.js +1 -1
- package/dist/components/Input.d.ts +10 -0
- package/dist/components/Input.js +1 -1
- package/dist/components/Menu.js +1 -1
- package/dist/components/Pill.d.ts +1 -2
- package/dist/components/Pill.js +1 -2
- package/dist/components/Popover.js +1 -1
- package/dist/components/Rows.js +2 -2
- package/dist/components/Slider.js +1 -1
- package/dist/components/SplitPane.js +2 -2
- package/dist/components/Table.js +1 -1
- package/dist/components/Tabs.js +2 -2
- package/dist/components/Textarea.d.ts +10 -0
- package/dist/components/Textarea.js +1 -1
- package/dist/components/Toggle.js +1 -1
- package/dist/components/Tour.js +6 -2
- package/dist/styles/index.css +88 -182
- package/dist/tf.css +88 -182
- package/dist/valet.css +88 -182
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,9 +13,12 @@ The components, and the tokens. Not the classes.
|
|
|
13
13
|
|
|
14
14
|
A class in this package is an implementation detail: `.card`, `.set-row`,
|
|
15
15
|
`.pill` and the rest may be renamed or deleted whenever the component that
|
|
16
|
-
draws them changes
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
draws them changes. 0.9.0 did exactly that to several of them, and 0.10.0
|
|
17
|
+
deleted 41 more: the components that drew them draw themselves now, so the
|
|
18
|
+
rules had no reader left.
|
|
19
|
+
|
|
20
|
+
If you need a card, render `<Card>`; if you need a row of your own, write it
|
|
21
|
+
in the token vocabulary:
|
|
19
22
|
|
|
20
23
|
```css
|
|
21
24
|
.app-row { display: flex; gap: var(--space-3); align-items: center; }
|
|
@@ -70,7 +70,7 @@ export default function Callout({ tone, children, icon, timed, onDismiss, classN
|
|
|
70
70
|
return (_jsxs("div", { className: `callout${tone ? ` callout-${tone}` : ''}${running ? ' callout-timed' : ''}${className ? ` ${className}` : ''}`,
|
|
71
71
|
/* An alert interrupts whatever is being read, which is right for bad news
|
|
72
72
|
and rude for "saved". */
|
|
73
|
-
role: tone === 'bad' ? 'alert' : 'status', onMouseEnter: () => running && setPaused(true), onMouseLeave: () => running && setPaused(false), onFocusCapture: () => running && setPaused(true), onBlurCapture: () => running && setPaused(false), children: [mark && (_jsx("span", { className: "
|
|
73
|
+
role: tone === 'bad' ? 'alert' : 'status', onMouseEnter: () => running && setPaused(true), onMouseLeave: () => running && setPaused(false), onFocusCapture: () => running && setPaused(true), onBlurCapture: () => running && setPaused(false), children: [mark && (_jsx("span", { className: "flex-none grid place-items-center mt-px opacity-90", "aria-hidden": "true", children: _jsx(Icon, { name: mark, size: 16 }) })), _jsx("div", { className: "ctl-grow", children: children }), running && (
|
|
74
74
|
/* Decoration: the time remaining is not information anybody can act on,
|
|
75
75
|
and a screen reader counting down a bar is noise on top of a message
|
|
76
76
|
it has already read out. */
|
|
@@ -50,5 +50,5 @@ export default function Checkbox({ label, hint, meta, checked, defaultChecked, o
|
|
|
50
50
|
the controlled and uncontrolled cases are the same call: pass both and
|
|
51
51
|
let whichever was given decide. Passing `isSelected={false}` here
|
|
52
52
|
instead would silently pin every uncontrolled box to off. */
|
|
53
|
-
isSelected: checked, defaultSelected: defaultChecked, onChange: onChange, name: name, value: value, isDisabled: disabled, children: _jsxs("span", { className: "choice-body", children: [_jsx("span", { className: "choice-name", children: label }), hint && _jsx("span", { className: "choice-why", children: hint }), meta && _jsx("span", { className: "
|
|
53
|
+
isSelected: checked, defaultSelected: defaultChecked, onChange: onChange, name: name, value: value, isDisabled: disabled, children: _jsxs("span", { className: "choice-body", children: [_jsx("span", { className: "choice-name", children: label }), hint && _jsx("span", { className: "choice-why", children: hint }), meta && _jsx("span", { className: "text-muted text-xs font-mono", children: meta })] }) }));
|
|
54
54
|
}
|
|
@@ -31,14 +31,14 @@ export default function Command({ open, onOpenChange, groups, placeholder = 'Sea
|
|
|
31
31
|
delete before they can search for anything else. */
|
|
32
32
|
if (!next)
|
|
33
33
|
setQuery('');
|
|
34
|
-
}, isDismissable: true, children: _jsx(AriaModal, { className: `cmd${className ? ` ${className}` : ''}`, children: _jsx(Dialog, { className: "
|
|
34
|
+
}, isDismissable: true, children: _jsx(AriaModal, { className: `cmd${className ? ` ${className}` : ''}`, children: _jsx(Dialog, { className: "outline-none flex flex-col min-h-0", "aria-label": label, children: ({ close }) => (_jsxs(Autocomplete, { inputValue: query, onInputChange: setQuery, filter: () => true, children: [_jsxs(SearchField, { className: "flex items-center gap-2 py-3 px-4 border-b border-border", "aria-label": label, autoFocus: true, children: [_jsx(Icon, { name: "chat", className: "flex-none w-4 h-4 text-muted" }), _jsx(Input, { className: "cmd-input", placeholder: placeholder })] }), empty ? (
|
|
35
35
|
/* The query, quoted. A palette that says "No results" to a
|
|
36
36
|
typo looks like a palette that is broken. */
|
|
37
|
-
_jsxs("p", { className: "cmd-empty", children: ["Nothing matches ", _jsx("strong", { children: query }), "."] })) : (_jsx(ListBox, { className: "
|
|
37
|
+
_jsxs("p", { className: "cmd-empty", children: ["Nothing matches ", _jsx("strong", { children: query }), "."] })) : (_jsx(ListBox, { className: "p-2 overflow-auto overscroll-contain outline-none min-h-0", "aria-label": label, selectionMode: "none", children: shown.map((group) => (_jsxs(ListBoxSection, { children: [_jsx(Header, { className: "pt-2 px-2 pb-1 text-2xs uppercase tracking-normal text-muted", children: group.title }), group.commands.map((command) => (_jsxs(ListBoxItem, { id: command.id, className: "cmd-item", textValue: command.label, isDisabled: command.disabled, onAction: () => {
|
|
38
38
|
command.onRun?.();
|
|
39
39
|
/* Closing is this component's job, not every
|
|
40
40
|
caller's. Forty `onRun`s that each remember to
|
|
41
41
|
close is thirty-nine chances to forget. */
|
|
42
42
|
close();
|
|
43
|
-
}, children: [command.icon && _jsx(Icon, { name: command.icon, className: "cmd-icon" }), _jsxs("span", { className: "
|
|
43
|
+
}, children: [command.icon && _jsx(Icon, { name: command.icon, className: "cmd-icon" }), _jsxs("span", { className: "flex-[1_1_auto] min-w-0 grid", children: [_jsx(Text, { slot: "label", className: "overflow-hidden text-ellipsis whitespace-nowrap", children: command.label }), command.description && (_jsx(Text, { slot: "description", className: "cmd-desc", children: command.description }))] }), command.shortcut && _jsx(Kbd, { children: command.shortcut })] }, command.id)))] }, group.title))) }))] })) }) }) }));
|
|
44
44
|
}
|
|
@@ -53,5 +53,5 @@ match, label, busyLabel, busy = false, disabled = false, onConfirm,
|
|
|
53
53
|
unavailable, }) {
|
|
54
54
|
const [asking, setAsking] = useState(false);
|
|
55
55
|
const [typed, setTyped] = useState('');
|
|
56
|
-
return (_jsxs("div", { className: `danger-act${kind === 'plain' ? ' plain' : ''}`, children: [_jsx("h4", { children: title }), _jsx("div", { className: "set-hint", children: description }), unavailable ? (_jsx("div", { className: "set-hint
|
|
56
|
+
return (_jsxs("div", { className: `danger-act${kind === 'plain' ? ' plain' : ''}`, children: [_jsx("h4", { children: title }), _jsx("div", { className: "set-hint", children: description }), unavailable ? (_jsx("div", { className: "set-hint mt-2", children: unavailable })) : confirm === 'type' ? (_jsx(_Fragment, { children: _jsxs("div", { className: "row danger-row field-row", children: [_jsx(Field, { label: "Confirm", hint: _jsxs(_Fragment, { children: ["Type ", _jsx("code", { className: "mono danger-name", children: match }), " to confirm."] }), children: (f) => (_jsx(Input, { ...f, mono: true, value: typed, disabled: disabled || busy, autoComplete: "off", spellCheck: false, onChange: (e) => setTyped(e.target.value) })) }), _jsx(Button, { kind: "danger", isDisabled: disabled || busy || typed.trim() !== match, onPress: onConfirm, children: busy ? (busyLabel ?? '…') : label })] }) })) : confirm === 'none' ? (_jsx("div", { className: "row danger-row", children: _jsx(Button, { kind: kind === 'plain' ? 'primary' : 'danger', isDisabled: disabled || busy, onPress: onConfirm, children: busy ? (busyLabel ?? '…') : label }) })) : asking ? (_jsxs("div", { className: "row danger-row", children: [_jsx(Button, { kind: "danger", isDisabled: disabled || busy, onPress: onConfirm, children: busy ? (busyLabel ?? '…') : label }), _jsx(Button, { isDisabled: disabled || busy, onPress: () => setAsking(false), children: "Cancel" })] })) : (_jsx("div", { className: "row danger-row", children: _jsx(Button, { kind: "danger", isDisabled: disabled || busy, onPress: () => setAsking(true), children: label }) }))] }));
|
|
57
57
|
}
|
package/dist/components/Empty.js
CHANGED
|
@@ -50,7 +50,14 @@ export default function Empty({ icon, illustration, children, action, className,
|
|
|
50
50
|
/* `currentColor` reaches the figure through the two rules the
|
|
51
51
|
illustrations ship with, so the colour is set here and the drawing
|
|
52
52
|
follows it -- the same grey as the sentence under it, in every
|
|
53
|
-
theme.
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
theme.
|
|
54
|
+
|
|
55
|
+
This wanted a rule of its own for a while. `.illo` sets
|
|
56
|
+
`color: inherit`, and while the package's sheets were unlayered an
|
|
57
|
+
unlayered declaration beat a layered utility whatever the
|
|
58
|
+
specificity, so `text-muted` lost silently and the figure came out
|
|
59
|
+
full text black beside a muted sentence. Wrapping the sheets in
|
|
60
|
+
`@layer components` put them *below* `@layer utilities` in the
|
|
61
|
+
declared order, and the utility has won ever since. */
|
|
62
|
+
_jsx(Illustration, { name: illustration, size: 160, className: "text-muted" })) : (icon && (_jsx("span", { className: "text-muted leading-[0]", "aria-hidden": "true", children: _jsx(Icon, { name: icon, size: 20 }) }))), _jsx("p", { className: "m-0 text-muted text-sm leading-[1.5] max-w-[46ch]", children: children }), action && _jsx("div", { className: "mt-1", children: action })] }));
|
|
56
63
|
}
|
package/dist/components/Field.js
CHANGED
|
@@ -53,5 +53,5 @@ export default function Field({ label, hint, error, required, children, labelHid
|
|
|
53
53
|
'aria-describedby': [hint && hintId, error && errorId].filter(Boolean).join(' ') || undefined,
|
|
54
54
|
'aria-invalid': error ? true : undefined,
|
|
55
55
|
};
|
|
56
|
-
return (_jsxs("div", { className: `field field-${layout}-layout${error ? ' field-bad' : ''}${className ? ` ${className}` : ''}`, children: [_jsxs("label", { id: labelId, className: labelHidden ? 'sr-only' : 'field-label', htmlFor: id, children: [label, required && (_jsxs("span", { className: "
|
|
56
|
+
return (_jsxs("div", { className: `field field-${layout}-layout${error ? ' field-bad' : ''}${className ? ` ${className}` : ''}`, children: [_jsxs("label", { id: labelId, className: labelHidden ? 'sr-only' : 'field-label', htmlFor: id, children: [label, required && (_jsxs("span", { className: "text-muted", "aria-label": "required", children: [' ', "*"] }))] }), hint && (_jsx("p", { className: "field-hint", id: hintId, children: hint })), typeof children === 'function' ? (children(wiring)) : (_jsx(FieldWiringContext.Provider, { value: wiring, children: children })), error && (_jsx("p", { className: "field-error", id: errorId, role: "alert", children: error }))] }));
|
|
57
57
|
}
|
|
@@ -39,5 +39,5 @@ export default function Identity({ name, address, kind = 'line', size = 'md', as
|
|
|
39
39
|
/* The hue is a variable and the stylesheet decides what to do with it:
|
|
40
40
|
a `background` written here would be a colour the theme cannot reach,
|
|
41
41
|
and the discs would stay saturated on Paper. */
|
|
42
|
-
style: { '--ident-hue': hueOf(address) }, children: [_jsx("span", { className: "ident-disc", "aria-hidden": "true", children: initials }), _jsxs("span", { className: "
|
|
42
|
+
style: { '--ident-hue': hueOf(address) }, children: [_jsx("span", { className: "ident-disc", "aria-hidden": "true", children: initials }), _jsxs("span", { className: "grid min-w-0", children: [_jsx("span", { className: "overflow-hidden text-ellipsis whitespace-nowrap font-normal", children: shown }), kind === 'full' && shown !== address && (_jsx("span", { className: "overflow-hidden text-ellipsis whitespace-nowrap text-xs text-muted", children: address }))] }), aside && _jsx("span", { className: "flex-none text-muted text-xs", children: aside }), onRemove && (_jsx("button", { type: "button", className: "ident-remove", onClick: onRemove, "aria-label": `Remove ${shown}`, children: _jsx("svg", { viewBox: "0 0 24 24", width: "12", height: "12", "aria-hidden": "true", children: _jsx("path", { fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", d: "M6 6l12 12M18 6L6 18" }) }) }))] }));
|
|
43
43
|
}
|
|
@@ -19,6 +19,16 @@ export interface Props extends Omit<React.InputHTMLAttributes<HTMLInputElement>,
|
|
|
19
19
|
* opens this, which is what the mail client's `.mail-search-key` was. */
|
|
20
20
|
trailing?: React.ReactNode;
|
|
21
21
|
className?: string;
|
|
22
|
+
/** Accepted and not rendered. `Field`'s render prop hands a caller every
|
|
23
|
+
* piece of its wiring at once, and the documented way to use it is
|
|
24
|
+
* `<Field>{(f) => <Input {...f} />}</Field>` -- so `labelId` arrives here
|
|
25
|
+
* whether or not this control wants it. `Select` does want it, because a
|
|
26
|
+
* button takes its name from its contents; an `<input>` does not, because
|
|
27
|
+
* `Field`'s `<label for>` already names it. Taking it out of the props is
|
|
28
|
+
* what keeps it off the DOM node: React passes through anything it does
|
|
29
|
+
* not recognise, and the browser got `labelid="…"` with a console warning
|
|
30
|
+
* on every field in valet and tf. */
|
|
31
|
+
labelId?: string;
|
|
22
32
|
}
|
|
23
33
|
declare const Input: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<HTMLInputElement>>;
|
|
24
34
|
export default Input;
|
package/dist/components/Input.js
CHANGED
|
@@ -49,7 +49,7 @@ import { forwardRef, useState } from 'react';
|
|
|
49
49
|
import { ToggleButton } from 'react-aria-components';
|
|
50
50
|
import Icon from './Icon.js';
|
|
51
51
|
import { useFieldWiring } from './fieldWiring.js';
|
|
52
|
-
const Input = forwardRef(function Input({ size = 'md', mono, block, className, type = 'text', icon, onClear, trailing, ...rest }, ref) {
|
|
52
|
+
const Input = forwardRef(function Input({ size = 'md', mono, block, className, type = 'text', icon, onClear, trailing, labelId: _labelId, ...rest }, ref) {
|
|
53
53
|
/* What the `Field` above wired, when the caller did not thread it by hand.
|
|
54
54
|
Explicit props win: a caller that named an `id` meant that id, and a
|
|
55
55
|
render-prop `Field` spreading its wiring is passing the same values in
|
package/dist/components/Menu.js
CHANGED
|
@@ -17,7 +17,7 @@ function renderItem(item) {
|
|
|
17
17
|
/* Typeahead needs a string, and `label` may be a node. Without this,
|
|
18
18
|
typing the first letter of an item whose label is markup matches
|
|
19
19
|
nothing and the menu looks broken. */
|
|
20
|
-
textValue: typeof item.label === 'string' ? item.label : item.id, children: [item.icon && _jsx(Icon, { name: item.icon, className: "menu-icon" }), _jsxs("span", { className: "
|
|
20
|
+
textValue: typeof item.label === 'string' ? item.label : item.id, children: [item.icon && _jsx(Icon, { name: item.icon, className: "menu-icon" }), _jsxs("span", { className: "flex-[1_1_auto] min-w-0 grid", children: [_jsx(Text, { slot: "label", className: "overflow-hidden text-ellipsis whitespace-nowrap", children: item.label }), item.description && (_jsx(Text, { slot: "description", className: "menu-desc", children: item.description }))] }), item.shortcut && _jsx(Kbd, { children: item.shortcut }), item.items && (_jsx("svg", { className: "menu-more", viewBox: "0 0 24 24", width: "12", height: "12", "aria-hidden": "true", children: _jsx("path", { fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", d: "m9 6 6 6-6 6" }) }))] }, item.id));
|
|
21
21
|
const body = item.items ? (_jsxs(SubmenuTrigger, { children: [row, _jsx(AriaPopover, { className: "menu-sheet surface-panel border border-border rounded-md min-w-[12rem] max-w-[min(20rem,calc(100vw-var(--space-6)))] max-h-[24rem] overflow-auto overscroll-contain z-[60]", children: _jsx(AriaMenu, { className: "p-1 outline-none grid gap-[var(--border-width)]", children: item.items.map(renderItem) }) })] }, item.id)) : (row);
|
|
22
22
|
if (!item.separated)
|
|
23
23
|
return body;
|
|
@@ -22,8 +22,7 @@
|
|
|
22
22
|
* label from 4.7:1 to 3.9:1. The tones mix at 10% into the panel for that
|
|
23
23
|
* reason and not for taste.
|
|
24
24
|
*
|
|
25
|
-
* This was the second-most-copied markup in the app — `<span className="pill
|
|
26
|
-
* pill-info">` at every call site, and a gallery page teaching it. A page that
|
|
25
|
+
* This was the second-most-copied markup in the app — `<span className="pill * pill-info">` at every call site, and a gallery page teaching it. A page that
|
|
27
26
|
* documents hand-written markup is a page that will be copied.
|
|
28
27
|
*/
|
|
29
28
|
export default function Pill({ tone, quiet, inRow, className, children, ...rest }: {
|
package/dist/components/Pill.js
CHANGED
|
@@ -23,8 +23,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
23
23
|
* label from 4.7:1 to 3.9:1. The tones mix at 10% into the panel for that
|
|
24
24
|
* reason and not for taste.
|
|
25
25
|
*
|
|
26
|
-
* This was the second-most-copied markup in the app — `<span className="pill
|
|
27
|
-
* pill-info">` at every call site, and a gallery page teaching it. A page that
|
|
26
|
+
* This was the second-most-copied markup in the app — `<span className="pill * pill-info">` at every call site, and a gallery page teaching it. A page that
|
|
28
27
|
* documents hand-written markup is a page that will be copied.
|
|
29
28
|
*/
|
|
30
29
|
export default function Pill({ tone, quiet, inRow, className, children, ...rest }) {
|
|
@@ -8,5 +8,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
line at the package's front door. */
|
|
9
9
|
import { Popover as AriaPopover, Dialog, DialogTrigger, } from 'react-aria-components';
|
|
10
10
|
export default function Popover({ trigger, children, placement = 'bottom start', label, offset = 6, open, onOpenChange, className, }) {
|
|
11
|
-
return (_jsxs(DialogTrigger, { isOpen: open, onOpenChange: onOpenChange, children: [trigger, _jsx(AriaPopover, { className: `pop${className ? ` ${className}` : ''}`, placement: placement, offset: offset, children: _jsx(Dialog, { className: "
|
|
11
|
+
return (_jsxs(DialogTrigger, { isOpen: open, onOpenChange: onOpenChange, children: [trigger, _jsx(AriaPopover, { className: `pop${className ? ` ${className}` : ''}`, placement: placement, offset: offset, children: _jsx(Dialog, { className: "p-3 outline-none text-text text-sm", "aria-label": label, children: ({ close }) => (typeof children === 'function' ? children(close) : children) }) })] }));
|
|
12
12
|
}
|
package/dist/components/Rows.js
CHANGED
|
@@ -45,14 +45,14 @@ export function Rows({ children, empty, label, look = 'list', className, }) {
|
|
|
45
45
|
const items = Array.isArray(children) ? children.filter(Boolean) : children;
|
|
46
46
|
const none = Array.isArray(items) ? items.length === 0 : !items;
|
|
47
47
|
if (none) {
|
|
48
|
-
return empty ? _jsx("div", { className: "set-hint
|
|
48
|
+
return empty ? _jsx("div", { className: "set-hint py-3", children: empty }) : null;
|
|
49
49
|
}
|
|
50
50
|
return (_jsx("div", { className: `rows${look === 'pick' ? ' rows-pick' : ''}${className ? ` ${className}` : ''}`,
|
|
51
51
|
// biome-ignore lint/a11y/useSemanticElements: valid ARIA on a div; a <ul> brings the browser's list reset and the stylesheet and 204 baselines key on `div.rows`.
|
|
52
52
|
role: "list", "aria-label": label, children: items }));
|
|
53
53
|
}
|
|
54
54
|
export function Row({ name, pills, hint, trail, actions, below, onClick, tone, picked, loading, waiting, align = 'center', className, }) {
|
|
55
|
-
const body = (_jsxs(_Fragment, { children: [_jsxs("span", { className: "row-subject", children: [_jsxs("span", { className: "named", children: [_jsx("span", { className: "truncate", children: name }), pills] }), hint && _jsx("div", { className: "set-hint", children: hint })] }), trail && _jsx("span", { className: "
|
|
55
|
+
const body = (_jsxs(_Fragment, { children: [_jsxs("span", { className: "row-subject", children: [_jsxs("span", { className: "named", children: [_jsx("span", { className: "truncate", children: name }), pills] }), hint && _jsx("div", { className: "set-hint", children: hint })] }), trail && _jsx("span", { className: "flex-none ml-auto whitespace-nowrap set-hint mono", children: trail })] }));
|
|
56
56
|
const rowClass = `set-row rows-row${tone ? ` rows-${tone}` : ''}` +
|
|
57
57
|
`${align === 'start' ? ' rows-top' : ''}${picked ? ' is-picked' : ''}` +
|
|
58
58
|
`${loading ? ' is-loading' : ''}${waiting ? ' is-waiting' : ''}` +
|
|
@@ -88,7 +88,7 @@ export default function Slider({ label, hint, value, min, max, step, onChange, f
|
|
|
88
88
|
const risk = danger
|
|
89
89
|
? Math.max(0, Math.min(1, (live - danger.from) / ((danger.to ?? max) - danger.from || 1)))
|
|
90
90
|
: 0;
|
|
91
|
-
return (_jsxs("div", { className: `slider-row slider-${size}${disabled ? ' is-disabled' : ''}${className ? ` ${className}` : ''}`, children: [_jsxs("label", { htmlFor: id, className: labelHidden ? 'sr-only' : '
|
|
91
|
+
return (_jsxs("div", { className: `slider-row slider-${size}${disabled ? ' is-disabled' : ''}${className ? ` ${className}` : ''}`, children: [_jsxs("label", { htmlFor: id, className: labelHidden ? 'sr-only' : 'flex-[1_1_260px] min-w-0 grid gap-px', children: [_jsx("span", { className: "text-text", children: label }), hint && _jsx("span", { className: "slider-hint", children: hint })] }), _jsxs("div", { className: "slider-control", children: [_jsxs("span", { className: "slider-box", children: [_jsx("span", { className: "slider-track", style: {
|
|
92
92
|
'--slider-f': String(fill / 100),
|
|
93
93
|
'--slider-risk': `${Math.round(risk * 100)}%`,
|
|
94
94
|
}, children: _jsx("input", { id: id, type: "range", className: "slider", min: min, max: max, step: step ?? 'any', value: live, disabled: disabled, "aria-valuetext": format(live), ref: input, onKeyDown: () => {
|
|
@@ -102,9 +102,9 @@ export default function SplitPane({ children, direction = 'row', defaultSize = 3
|
|
|
102
102
|
return;
|
|
103
103
|
event.preventDefault();
|
|
104
104
|
};
|
|
105
|
-
return (_jsxs("div", { ref: frame, className: `split dir-${direction}${className ? ` ${className}` : ''}`, style: { '--split': `${size}%` }, children: [_jsx("div", { className: "
|
|
105
|
+
return (_jsxs("div", { ref: frame, className: `split dir-${direction}${className ? ` ${className}` : ''}`, style: { '--split': `${size}%` }, children: [_jsx("div", { className: "min-w-0 min-h-0 overflow-hidden flex flex-col flex-[0_0_var(--split)]", id: firstPaneId, children: first }), _jsx("div", { role: "separator", tabIndex: 0, "aria-label": label, "aria-orientation": direction === 'row' ? 'vertical' : 'horizontal', "aria-controls": firstPaneId, "aria-valuenow": Math.round(size), "aria-valuemin": min, "aria-valuemax": max, className: "split-handle", onPointerDown: onPointerDown, onKeyDown: onKeyDown,
|
|
106
106
|
/* Back to where it started. A drag has no undo, and the size is
|
|
107
107
|
remembered, so without this a mis-drag is permanent until you get it
|
|
108
108
|
right by hand. */
|
|
109
|
-
onDoubleClick: () => move(defaultSize), children: _jsx("span", { className: "split-grip", "aria-hidden": "true" }) }), _jsx("div", { className: "
|
|
109
|
+
onDoubleClick: () => move(defaultSize), children: _jsx("span", { className: "split-grip", "aria-hidden": "true" }) }), _jsx("div", { className: "min-w-0 min-h-0 overflow-hidden flex flex-col flex-[1_1_0]", children: second })] }));
|
|
110
110
|
}
|
package/dist/components/Table.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
export default function Table({ caption, columns, rows, keyOf, empty, className, }) {
|
|
3
3
|
if (!rows.length) {
|
|
4
|
-
return empty ? _jsx("div", { className: "set-hint
|
|
4
|
+
return empty ? _jsx("div", { className: "set-hint py-3", children: empty }) : null;
|
|
5
5
|
}
|
|
6
6
|
return (
|
|
7
7
|
/* The one thing about a table that is a property of one element. Its
|
package/dist/components/Tabs.js
CHANGED
|
@@ -64,7 +64,7 @@ export default function Tabs({ tabs, value, onChange, label, orientation = 'hori
|
|
|
64
64
|
tabs[i - 1]?.group !== t.group &&
|
|
65
65
|
(() => {
|
|
66
66
|
const g = groups.find((x) => x.id === t.group);
|
|
67
|
-
return g ? (_jsxs("div", { className: "tab-group", role: "presentation", children: [_jsx("span", { className: "
|
|
67
|
+
return g ? (_jsxs("div", { className: "tab-group", role: "presentation", children: [_jsx("span", { className: "block text-sm font-strong text-text", children: g.label }), g.hint && (_jsx("span", { className: "block mt-px text-xs text-muted leading-[1.35]", children: g.hint }))] })) : null;
|
|
68
68
|
})(), _jsxs("button", { role: "tab", type: "button", className: `tab${t.id === value ? ' on' : ''}${grouped && t.group ? ' in-group' : ''}`, "aria-selected": t.id === value, disabled: t.disabled,
|
|
69
69
|
/* One tab stop for the whole strip: everything but the current tab is
|
|
70
70
|
taken out of the tab order, and the arrows move between them. */
|
|
@@ -85,5 +85,5 @@ export default function Tabs({ tabs, value, onChange, label, orientation = 'hori
|
|
|
85
85
|
e.preventDefault();
|
|
86
86
|
move(tabs.length, -1);
|
|
87
87
|
}
|
|
88
|
-
}, children: [_jsx("span", { className: "tab-label", children: t.label }), t.badge && (_jsx("span", { className: "tab-badge", title: t.badgeTitle, "aria-label": t.badgeTitle, children: t.badge })), t.hint && orientation === 'vertical' && !(grouped && t.group) && (_jsx("span", { className: "
|
|
88
|
+
}, children: [_jsx("span", { className: "tab-label", children: t.label }), t.badge && (_jsx("span", { className: "tab-badge", title: t.badgeTitle, "aria-label": t.badgeTitle, children: t.badge })), t.hint && orientation === 'vertical' && !(grouped && t.group) && (_jsx("span", { className: "block mt-px text-xs text-muted leading-[1.35]", children: t.hint }))] })] }, t.id))), _jsx("span", { className: "tabs-rule", "aria-hidden": "true" })] }));
|
|
89
89
|
}
|
|
@@ -4,6 +4,16 @@ export interface Props extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaEle
|
|
|
4
4
|
* characters are the content. */
|
|
5
5
|
mono?: boolean;
|
|
6
6
|
className?: string;
|
|
7
|
+
/** Accepted and not rendered. `Field`'s render prop hands a caller every
|
|
8
|
+
* piece of its wiring at once, and the documented way to use it is
|
|
9
|
+
* `<Field>{(f) => <Input {...f} />}</Field>` -- so `labelId` arrives here
|
|
10
|
+
* whether or not this control wants it. `Select` does want it, because a
|
|
11
|
+
* button takes its name from its contents; an `<input>` does not, because
|
|
12
|
+
* `Field`'s `<label for>` already names it. Taking it out of the props is
|
|
13
|
+
* what keeps it off the DOM node: React passes through anything it does
|
|
14
|
+
* not recognise, and the browser got `labelid="…"` with a console warning
|
|
15
|
+
* on every field in valet and tf. */
|
|
16
|
+
labelId?: string;
|
|
7
17
|
}
|
|
8
18
|
declare const Textarea: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<HTMLTextAreaElement>>;
|
|
9
19
|
export default Textarea;
|
|
@@ -15,7 +15,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
15
15
|
*/
|
|
16
16
|
import { forwardRef } from 'react';
|
|
17
17
|
import { useFieldWiring } from './fieldWiring.js';
|
|
18
|
-
const Textarea = forwardRef(function Textarea({ size = 'md', mono, className, ...rest }, ref) {
|
|
18
|
+
const Textarea = forwardRef(function Textarea({ size = 'md', mono, className, labelId: _labelId, ...rest }, ref) {
|
|
19
19
|
const classes = [size === 'md' ? '' : `size-${size}`, mono ? 'mono' : '', className ?? '']
|
|
20
20
|
.filter(Boolean)
|
|
21
21
|
.join(' ');
|
|
@@ -158,7 +158,7 @@ export default function Toggle({ label, hint, checked, onChange, disabled, said,
|
|
|
158
158
|
/* Read-only, not disabled, while a request is out: focus stays where it
|
|
159
159
|
is and the row does not dim, it just refuses a second answer until
|
|
160
160
|
the first has been taken. */
|
|
161
|
-
isReadOnly: pending, children: [_jsxs("span", { className: labelHidden ? 'sr-only' : 'switch-body', children: [_jsx("span", { className: "
|
|
161
|
+
isReadOnly: pending, children: [_jsxs("span", { className: labelHidden ? 'sr-only' : 'switch-body', children: [_jsx("span", { className: "text-text", children: label }), hint && _jsx("span", { className: "switch-hint", children: hint })] }), said && _jsx("span", { className: "flex-none text-xs text-muted mono", children: said }), _jsx("span", { className: "toggle", "aria-hidden": "true", "data-held": held || undefined, "data-dragging": knob === null ? undefined : true, style: knob === null ? undefined : { '--knob-x': knob }, onPointerDown: (e) => {
|
|
162
162
|
if (disabled || pending || e.button !== 0)
|
|
163
163
|
return;
|
|
164
164
|
e.stopPropagation();
|
package/dist/components/Tour.js
CHANGED
|
@@ -99,7 +99,11 @@ export default function Tour({ stops, onDone, storageKey, }) {
|
|
|
99
99
|
bottom: below ? undefined : window.innerHeight - box.top + 14,
|
|
100
100
|
left: Math.max(16, Math.min(box.left + box.width / 2 - 170, window.innerWidth - 356)),
|
|
101
101
|
};
|
|
102
|
-
return createPortal(_jsxs("div", {
|
|
102
|
+
return createPortal(_jsxs("div", {
|
|
103
|
+
/* Over everything and catching nothing: the scrim is painted by the
|
|
104
|
+
hole's own shadow, so there is no full-screen element here to swallow
|
|
105
|
+
a click meant for the page underneath. */
|
|
106
|
+
className: "fixed inset-0 z-[60] pointer-events-none", onKeyDown: (e) => {
|
|
103
107
|
if (e.key === 'Escape')
|
|
104
108
|
finish();
|
|
105
109
|
if (e.key === 'Enter' || e.key === 'ArrowRight')
|
|
@@ -111,5 +115,5 @@ export default function Tour({ stops, onDone, storageKey, }) {
|
|
|
111
115
|
height: box.height + 12,
|
|
112
116
|
} }), _jsxs("div", { ref: card, className: "tour-card", style: style,
|
|
113
117
|
// biome-ignore lint/a11y/useSemanticElements: a <dialog> brings the top layer, ::backdrop and showModal() focus semantics; this is an anchored coach-mark card that positions itself and manages its own focus, so the role is the faithful choice.
|
|
114
|
-
role: "dialog", "aria-modal": "true", "aria-labelledby": "tour-title", tabIndex: -1, children: [_jsx("strong", { id: "tour-title", className: "
|
|
118
|
+
role: "dialog", "aria-modal": "true", "aria-labelledby": "tour-title", tabIndex: -1, children: [_jsx("strong", { id: "tour-title", className: "text-base", children: stop.title }), _jsx("p", { className: "tour-body", children: stop.body }), _jsxs("div", { className: "tour-actions", children: [_jsxs("span", { className: "tour-count mono", children: [at + 1, " of ", live.length] }), _jsx("span", { className: "ctl-grow" }), _jsx(Button, { kind: "ghost", size: "sm", onPress: finish, children: "Skip" }), _jsx(Button, { kind: "primary", size: "sm", onPress: next, children: at + 1 < live.length ? 'Next' : 'Done' })] })] })] }), document.body);
|
|
115
119
|
}
|