@parallelworks/ui 0.45.0 → 0.47.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 +16 -5
- package/dist/ai/agent/permissions.d.ts +1 -1
- package/dist/ai/agent/permissions.js +1 -1
- package/dist/ai/ai.css +4 -4
- package/dist/ai/components/ChatInput.js +1 -1
- package/dist/ai/components/ChatStage.d.ts +1 -1
- package/dist/ai/components/ChatStage.js +1 -1
- package/dist/ai/core/pasteLanguage.d.ts +1 -1
- package/dist/ai/core/pasteLanguage.js +1 -1
- package/dist/brandIcons.js +1 -1
- package/dist/components/ColorInput.d.ts +36 -0
- package/dist/components/ColorInput.js +82 -0
- package/dist/components/Combobox.d.ts +5 -2
- package/dist/components/Combobox.js +12 -11
- package/dist/components/CreateModal.js +2 -2
- package/dist/components/DatePicker.d.ts +5 -1
- package/dist/components/DatePicker.js +7 -7
- package/dist/components/Field.d.ts +19 -5
- package/dist/components/Field.js +21 -4
- package/dist/components/Input.d.ts +11 -1
- package/dist/components/Input.js +9 -6
- package/dist/components/Loader.d.ts +1 -1
- package/dist/components/MenuEntries.js +1 -1
- package/dist/components/NumberInput.d.ts +1 -0
- package/dist/components/OneTimeCodeInput.d.ts +23 -0
- package/dist/components/OneTimeCodeInput.js +39 -0
- package/dist/components/Provider.d.ts +15 -6
- package/dist/components/Provider.js +17 -5
- package/dist/components/SearchInput.d.ts +1 -0
- package/dist/components/Section.d.ts +7 -2
- package/dist/components/Section.js +58 -39
- package/dist/components/SegmentedControl.d.ts +4 -2
- package/dist/components/SegmentedControl.js +4 -2
- package/dist/components/Select.d.ts +2 -1
- package/dist/components/Select.js +1 -1
- package/dist/components/StatusBadge.d.ts +2 -2
- package/dist/components/StatusBadge.js +1 -1
- package/dist/components/Tabs.js +1 -0
- package/dist/components/ThemeEditor.js +15 -52
- package/dist/components/Toast.js +1 -1
- package/dist/components/ToggleGroup.d.ts +42 -0
- package/dist/components/ToggleGroup.js +69 -0
- package/dist/components/comboboxItems.d.ts +1 -1
- package/dist/components/fieldBox.js +1 -1
- package/dist/components/tone.d.ts +1 -1
- package/dist/editing.d.ts +1 -1
- package/dist/file-explorer/FileExplorerContext.js +11 -11
- package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +4 -4
- package/dist/file-explorer/FilePreview/useObjectText.js +4 -4
- package/dist/file-explorer/FilePreview/usePresignedUrl.js +4 -4
- package/dist/file-explorer/lib/types.d.ts +2 -2
- package/dist/file-explorer/toasts.js +9 -9
- package/dist/form/Fields/BooleanField.js +1 -1
- package/dist/form/Fields/ColorField.js +6 -10
- package/dist/form/Fields/DropdownField.js +1 -1
- package/dist/form/Fields/DurationField.js +1 -1
- package/dist/form/Fields/MultiDropdownField.js +1 -1
- package/dist/form/Fields/StringField.js +1 -1
- package/dist/form/Fields/TextAreaField.js +1 -1
- package/dist/form/Form.js +1 -1
- package/dist/form/utils/fieldItems.d.ts +1 -1
- package/dist/graph/GraphEditorDialogs.js +1 -1
- package/dist/graph/InputDialog.js +2 -2
- package/dist/graph/SuggestionInput.d.ts +1 -1
- package/dist/graph/SuggestionInput.js +1 -1
- package/dist/graph/WorkflowSettingsDialog.js +1 -1
- package/dist/graph/editorFields.js +2 -2
- package/dist/graph/editorPrimitives.d.ts +2 -2
- package/dist/graph/editorPrimitives.js +1 -1
- package/dist/graph/editorStrings.d.ts +2 -2
- package/dist/graph/editorStrings.js +4 -4
- package/dist/graph/inputRefs.js +2 -2
- package/dist/graph/stepWith.js +1 -1
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +14 -10
- package/dist/list/ListViewControls.js +1 -1
- package/dist/list/listSearch.js +1 -1
- package/dist/logviewer/segments.d.ts +1 -1
- package/dist/logviewer/segments.js +1 -1
- package/dist/styles/theme.css +1 -1
- package/dist/styles.css +1 -1
- package/dist/theme/derive.d.ts +3 -0
- package/dist/theme/derive.js +11 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,9 +32,14 @@ Each need has one component. Reach for it before styling markup by hand.
|
|
|
32
32
|
| Work under way | `Loader`; `Skeleton` in a `LoadingRegion` for the shape of content still loading |
|
|
33
33
|
| Nothing to show yet, or nothing matches | `EmptyState` |
|
|
34
34
|
| A text field | `Input`, `Textarea`, or a control framed by `Field`, whose `actions` hold buttons that act on it |
|
|
35
|
-
| A number, a date | `NumberInput`, `DatePicker` |
|
|
35
|
+
| A number, a date, a color | `NumberInput`, `DatePicker`, `ColorInput` |
|
|
36
|
+
| A one-time code, such as a sign-in check | `OneTimeCodeInput` |
|
|
37
|
+
| A labeled value nobody edits, among fields | `FieldValue` in a `Field`; `DescriptionList` for a set of them alone |
|
|
38
|
+
| An error beside a control outside a `Field` | `ErrorText` |
|
|
39
|
+
| A dense field in a toolbar or a table cell | any field control with `size="sm"`; a width in `className` wins, and `cx` merges classes the same way |
|
|
36
40
|
| On or off | `Switch`; `Checkbox` in a list of things to tick, several of which may be on |
|
|
37
41
|
| One of a few choices, all shown | `RadioGroup` in a form; `SegmentedControl` in a toolbar, or as a field's control in a `Field group` |
|
|
42
|
+
| Narrowing a list by values, such as statuses: several at once, or one or none | `ToggleGroup` |
|
|
38
43
|
| Narrowing what is shown by typing | `SearchInput`; `ListSearchControl` on a list page's toolbar |
|
|
39
44
|
| A value from a list | `Select`, or `Combobox` when the list needs filtering |
|
|
40
45
|
| Free text with suggestions, or tags | `Autocomplete`; `Combobox` with `multiple creatable` |
|
|
@@ -78,6 +83,12 @@ Some pairs look alike and are split on purpose:
|
|
|
78
83
|
thing, each with its own panel or route. A `SegmentedControl` sets a value,
|
|
79
84
|
such as a time range, that changes what one view shows. `Tabs` with
|
|
80
85
|
`variant="segmented"` look the same, so the choice rests on what pressing does.
|
|
86
|
+
- **SegmentedControl or ToggleGroup.** A `SegmentedControl` holds a setting
|
|
87
|
+
that always has exactly one value, such as a time range or which view of the
|
|
88
|
+
data to show. A `ToggleGroup` filters a list by values and may be off: its
|
|
89
|
+
options are pressed independently, or one or none with `multiple={false}`,
|
|
90
|
+
and `all` leads with an All option for "no filter". Each option can carry a
|
|
91
|
+
status dot or icon and a count.
|
|
81
92
|
- **SearchInput or ListSearchControl.** A `SearchInput` is a box that is
|
|
82
93
|
always shown and narrows what sits beside it, such as a panel's rows or a
|
|
83
94
|
log's lines. `ListSearchControl` belongs to a list page's toolbar: it folds to
|
|
@@ -300,8 +311,8 @@ root:
|
|
|
300
311
|
|
|
301
312
|
| Variable | Meaning |
|
|
302
313
|
| ------------------- | ----------------------------------------------------------------------- |
|
|
303
|
-
| `--app-inset-top` | The height of a fixed top bar.
|
|
304
|
-
| `--app-inset-start` | The width of a fixed sidebar on the start side.
|
|
314
|
+
| `--app-inset-top` | The height of a fixed top bar. Drawers and toasts start below it. |
|
|
315
|
+
| `--app-inset-start` | The width of a fixed sidebar on the start side. Toasts center over the page beside it, and a drawer at its widest leaves that strip, and never less than 2.5rem. Update it when the sidebar collapses. |
|
|
305
316
|
|
|
306
317
|
```ts
|
|
307
318
|
document.documentElement.style.setProperty('--app-inset-top', '56px')
|
|
@@ -340,7 +351,7 @@ name is a meaning, such as `RetryIcon` or `WarningIcon`, drawn from
|
|
|
340
351
|
stroke. Don't import `lucide-react` directly: a name here keeps one glyph per
|
|
341
352
|
meaning across the apps.
|
|
342
353
|
|
|
343
|
-
- **Size and
|
|
354
|
+
- **Size and color:** an icon is `1em` square in `currentColor`, so it follows
|
|
344
355
|
the text around it. A class such as `size-4` or `h-3 w-3` sets another size,
|
|
345
356
|
and `size` takes pixels.
|
|
346
357
|
- **Accessibility:** an icon is decoration, hidden from assistive technology,
|
|
@@ -362,7 +373,7 @@ meaning across the apps.
|
|
|
362
373
|
|
|
363
374
|
Logos of other companies' products, such as `GitHubIcon` or `UbuntuIcon`, are
|
|
364
375
|
in `@parallelworks/ui/brand-icons`. They take the same props, are filled in the
|
|
365
|
-
text's
|
|
376
|
+
text's color, and are only for naming the product. They come from Simple Icons
|
|
366
377
|
(CC0) and Bootstrap Icons (MIT), and the file carries their notices.
|
|
367
378
|
|
|
368
379
|
The `UI/Icons` story shows every icon and logo by name.
|
|
@@ -6,7 +6,7 @@ export declare const DEFAULT_PERMISSION_MODE: PermissionMode;
|
|
|
6
6
|
export declare function isNamedPermissionMode(mode: string | undefined): mode is NamedPermissionMode;
|
|
7
7
|
/** The daemon clamps a remote request to its owner's remoteMaxPermissionMode
|
|
8
8
|
* and refuses a mode it does not know, so a menu offers only what that
|
|
9
|
-
* machine will
|
|
9
|
+
* machine will honor. */
|
|
10
10
|
export declare function permissionModesUpTo(ceiling: string | undefined, accepted?: readonly string[] | undefined): PermissionMode[];
|
|
11
11
|
export declare function clampPermissionMode(mode: PermissionMode, ceiling: string | undefined, accepted?: readonly string[] | undefined): PermissionMode;
|
|
12
12
|
export {};
|
|
@@ -26,7 +26,7 @@ var LEGACY_PERMISSION_MODES = [
|
|
|
26
26
|
];
|
|
27
27
|
/** The daemon clamps a remote request to its owner's remoteMaxPermissionMode
|
|
28
28
|
* and refuses a mode it does not know, so a menu offers only what that
|
|
29
|
-
* machine will
|
|
29
|
+
* machine will honor. */
|
|
30
30
|
function permissionModesUpTo(ceiling, accepted = void 0) {
|
|
31
31
|
const max = isNamedPermissionMode(ceiling) ? PERMISSION_RANK[ceiling] : PERMISSION_RANK[DEFAULT_PERMISSION_MODE];
|
|
32
32
|
const known = accepted?.length ? accepted : LEGACY_PERMISSION_MODES;
|
package/dist/ai/ai.css
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
padding: 0.125em 0.375em;
|
|
56
56
|
background-color: color-mix(in oklab, var(--theme-app, #000) 8%, transparent);
|
|
57
57
|
border-radius: 0.25rem;
|
|
58
|
-
/* Identifiers carry a
|
|
58
|
+
/* Identifiers carry a color of their own so they read as code at a glance
|
|
59
59
|
* without the weight the plugin gives them: the theme's link blue, which
|
|
60
60
|
* every palette already keeps legible on its surfaces. */
|
|
61
61
|
color: var(--theme-link, #2f6fa8);
|
|
@@ -248,7 +248,7 @@
|
|
|
248
248
|
explicitly to ensure correct colors in all modes.
|
|
249
249
|
See: https://shiki.style/guide/dual-themes#class-based-dark-mode */
|
|
250
250
|
|
|
251
|
-
/* Light mode: the primary Shiki theme's
|
|
251
|
+
/* Light mode: the primary Shiki theme's colors (--sdm-c) on the block's own
|
|
252
252
|
surface, which stands in for the theme's background. */
|
|
253
253
|
.markdown [data-streamdown="code-block-body"] pre {
|
|
254
254
|
/* biome-ignore lint/complexity/noImportantStyles: the highlighter sets the background as an inline style */
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
color: var(--sdm-c, inherit);
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
/* Dark mode: the --shiki-dark
|
|
262
|
+
/* Dark mode: the --shiki-dark colors, again on the block's surface. */
|
|
263
263
|
.dark .markdown [data-streamdown="code-block-body"] pre {
|
|
264
264
|
/* biome-ignore lint/complexity/noImportantStyles: the highlighter sets the background as an inline style */
|
|
265
265
|
background-color: transparent !important;
|
|
@@ -369,7 +369,7 @@
|
|
|
369
369
|
|
|
370
370
|
/* The composer is one surface lifted off the page: a hairline of the ink for
|
|
371
371
|
* its edge, and a short, soft shadow that deepens while it has focus. The
|
|
372
|
-
* shadow is black on every scheme; an ink-
|
|
372
|
+
* shadow is black on every scheme; an ink-colored one would glow on dark. */
|
|
373
373
|
.chat-composer {
|
|
374
374
|
box-shadow:
|
|
375
375
|
0 0 0 1px color-mix(in oklab, var(--theme-app, #000) 12%, transparent),
|
|
@@ -261,7 +261,7 @@ var ChatInput = forwardRef(function ChatInput({ onSend, disabled = false, placeh
|
|
|
261
261
|
"aria-label": inputLabel ?? effectivePlaceholder,
|
|
262
262
|
disabled,
|
|
263
263
|
rows: 1,
|
|
264
|
-
className: cx("theme-text block w-full resize-none overflow-hidden", "bg-transparent border-0 px-4", toolbar ? "pt-3.5 pb-1" : "pt-3 pb-1", "placeholder:theme-
|
|
264
|
+
className: cx("theme-text block w-full resize-none overflow-hidden", "bg-transparent border-0 px-4", toolbar ? "pt-3.5 pb-1" : "pt-3 pb-1", "placeholder:text-(--theme-placeholder)", "focus:outline-none focus:ring-0", "disabled:opacity-50 disabled:cursor-not-allowed", "text-[1rem] leading-relaxed")
|
|
265
265
|
})
|
|
266
266
|
}), !toolbar && action]
|
|
267
267
|
}),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
/** Where a new conversation starts: the heading and the composer together, a
|
|
3
|
-
* little above
|
|
3
|
+
* little above center, with what the surface offers next under the box. The
|
|
4
4
|
* composer keeps its place however much is under it, so surfaces that share
|
|
5
5
|
* the stage put it in the same spot; a long list scrolls instead. */
|
|
6
6
|
export default function ChatStage({ heading, subheading, children, }: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
//#region src/ai/components/ChatStage.tsx
|
|
3
3
|
/** Where a new conversation starts: the heading and the composer together, a
|
|
4
|
-
* little above
|
|
4
|
+
* little above center, with what the surface offers next under the box. The
|
|
5
5
|
* composer keeps its place however much is under it, so surfaces that share
|
|
6
6
|
* the stage put it in the same spot; a long list scrolls instead. */
|
|
7
7
|
function ChatStage({ heading, subheading, children }) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The highlighter's name for the language a paste is written in, or ''
|
|
2
|
-
* when it is not
|
|
2
|
+
* when it is not recognizably one. */
|
|
3
3
|
export declare function guessPasteLanguage(text: string): string;
|
|
4
4
|
/** The paste as a fenced code block in its language. The fence outruns any
|
|
5
5
|
* run of backticks inside the paste, so nothing in it can close the fence. */
|
|
@@ -17,7 +17,7 @@ function isJson(trimmed) {
|
|
|
17
17
|
var YAML_LINE = /^\s*(-\s+)?["']?[\w./-]+["']?\s*:(\s|$)|^\s*-(\s|$)|^---\s*$/;
|
|
18
18
|
var TOML_LINE = /^\s*\[{1,2}[\w."-]+\]{1,2}\s*$|^\s*[\w."-]+\s*=\s*\S/;
|
|
19
19
|
/** The highlighter's name for the language a paste is written in, or ''
|
|
20
|
-
* when it is not
|
|
20
|
+
* when it is not recognizably one. */
|
|
21
21
|
function guessPasteLanguage(text) {
|
|
22
22
|
const trimmed = text.trim();
|
|
23
23
|
if (!trimmed) return "";
|
package/dist/brandIcons.js
CHANGED
|
@@ -25,7 +25,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
25
25
|
*
|
|
26
26
|
* Each logo is its owner's trademark; use one only to refer to its product.
|
|
27
27
|
*/
|
|
28
|
-
/** A one-
|
|
28
|
+
/** A one-color logo in the text's color, at the text's size. */
|
|
29
29
|
function brand(viewBox, ...paths) {
|
|
30
30
|
function Icon({ title, size = "1em", color = "currentColor", strokeWidth: _strokeWidth, absoluteStrokeWidth: _absoluteStrokeWidth, nonScalingStroke: _nonScalingStroke, children, ...props }) {
|
|
31
31
|
const named = Boolean(title || props["aria-label"] || props["aria-labelledby"]);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type FieldBoxSize } from './fieldBox.js';
|
|
3
|
+
export interface ColorInputProps {
|
|
4
|
+
/** A CSS color the theme reads: a hex, `oklch()` or `color(display-p3 …)`. */
|
|
5
|
+
value: string;
|
|
6
|
+
/** Called with each color that parses, as typed or as picked: a picked one is a hex. */
|
|
7
|
+
onValueChange: (color: string) => void;
|
|
8
|
+
/** Without a label the row stands alone; name it with `aria-label`. */
|
|
9
|
+
label?: ReactNode;
|
|
10
|
+
/** Beside the label and outside it, so it stays out of the field's name. */
|
|
11
|
+
hint?: ReactNode;
|
|
12
|
+
/** Help under the field. */
|
|
13
|
+
description?: ReactNode;
|
|
14
|
+
error?: ReactNode;
|
|
15
|
+
/** `sm` for dense places such as toolbars and table cells. */
|
|
16
|
+
size?: FieldBoxSize | undefined;
|
|
17
|
+
/** The hex field's id, which a label names. */
|
|
18
|
+
id?: string | undefined;
|
|
19
|
+
/** Submits the color under this name with a form. */
|
|
20
|
+
name?: string | undefined;
|
|
21
|
+
disabled?: boolean | undefined;
|
|
22
|
+
required?: boolean | undefined;
|
|
23
|
+
'aria-label'?: string | undefined;
|
|
24
|
+
'aria-labelledby'?: string | undefined;
|
|
25
|
+
'aria-describedby'?: string | undefined;
|
|
26
|
+
'aria-invalid'?: boolean | undefined;
|
|
27
|
+
'aria-required'?: boolean | undefined;
|
|
28
|
+
/** On the row that holds the swatch and the hex field: its width or margin. */
|
|
29
|
+
className?: string | undefined;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A color as a swatch that opens the system picker, beside a field its hex can be typed or pasted
|
|
33
|
+
* in. A half-typed color stays in the field, marked invalid, until it parses; leaving the field
|
|
34
|
+
* puts back the last color that did.
|
|
35
|
+
*/
|
|
36
|
+
export declare function ColorInput({ value, onValueChange, label, hint, description, error, size, id, name, disabled, required, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, 'aria-required': ariaRequired, className, }: ColorInputProps): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { cx } from "../cx.js";
|
|
2
|
+
import { useStrings } from "./Provider.js";
|
|
3
|
+
import { parseColor, toHex } from "../theme/color.js";
|
|
4
|
+
import { ErrorText } from "./Field.js";
|
|
5
|
+
import { fieldBox } from "./fieldBox.js";
|
|
6
|
+
import { framed } from "./Input.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { useId, useState } from "react";
|
|
9
|
+
//#region src/components/ColorInput.tsx
|
|
10
|
+
var swatchClassName = cx("shrink-0 cursor-pointer p-0.5", "[&::-webkit-color-swatch-wrapper]:p-0 [&::-webkit-color-swatch]:rounded [&::-webkit-color-swatch]:border-0", "[&::-moz-color-swatch]:rounded [&::-moz-color-swatch]:border-0");
|
|
11
|
+
/**
|
|
12
|
+
* A color as a swatch that opens the system picker, beside a field its hex can be typed or pasted
|
|
13
|
+
* in. A half-typed color stays in the field, marked invalid, until it parses; leaving the field
|
|
14
|
+
* puts back the last color that did.
|
|
15
|
+
*/
|
|
16
|
+
function ColorInput({ value, onValueChange, label, hint, description, error, size = "md", id, name, disabled, required, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, className }) {
|
|
17
|
+
const t = useStrings().colorInput;
|
|
18
|
+
const [draft, setDraft] = useState(null);
|
|
19
|
+
const errorId = useId();
|
|
20
|
+
const parsed = parseColor(value);
|
|
21
|
+
const unparsed = draft !== null && parseColor(draft) === null;
|
|
22
|
+
const named = typeof label === "string" ? label : ariaLabel;
|
|
23
|
+
const ownError = unparsed && label === void 0;
|
|
24
|
+
return framed({
|
|
25
|
+
label,
|
|
26
|
+
hint,
|
|
27
|
+
description,
|
|
28
|
+
error: error ?? (unparsed ? t.invalid : void 0)
|
|
29
|
+
}, {
|
|
30
|
+
id,
|
|
31
|
+
required,
|
|
32
|
+
disabled
|
|
33
|
+
}, (control) => /* @__PURE__ */ jsxs("div", {
|
|
34
|
+
className: cx("flex w-full flex-col gap-1", className),
|
|
35
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
36
|
+
className: cx("flex items-center", size === "sm" ? "gap-1.5" : "gap-2"),
|
|
37
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
38
|
+
type: "color",
|
|
39
|
+
"aria-label": t.pick(named),
|
|
40
|
+
value: parsed ? toHex(parsed) : "#000000",
|
|
41
|
+
disabled,
|
|
42
|
+
onChange: (e) => {
|
|
43
|
+
setDraft(null);
|
|
44
|
+
onValueChange(e.target.value);
|
|
45
|
+
},
|
|
46
|
+
className: cx(fieldBox({ size }), swatchClassName, size === "sm" ? "w-8" : "w-10")
|
|
47
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
48
|
+
id,
|
|
49
|
+
name,
|
|
50
|
+
"aria-label": ariaLabel,
|
|
51
|
+
"aria-labelledby": ariaLabelledBy,
|
|
52
|
+
...control,
|
|
53
|
+
"aria-describedby": [
|
|
54
|
+
ariaDescribedBy,
|
|
55
|
+
control["aria-describedby"],
|
|
56
|
+
ownError && errorId
|
|
57
|
+
].filter(Boolean).join(" ") || void 0,
|
|
58
|
+
"aria-invalid": control["aria-invalid"] ?? (ariaInvalid || unparsed || void 0),
|
|
59
|
+
"aria-required": control["aria-required"] ?? (ariaRequired || void 0),
|
|
60
|
+
required,
|
|
61
|
+
disabled,
|
|
62
|
+
spellCheck: false,
|
|
63
|
+
autoComplete: "off",
|
|
64
|
+
value: draft ?? value,
|
|
65
|
+
onChange: (e) => {
|
|
66
|
+
const text = e.target.value;
|
|
67
|
+
if (parseColor(text)) {
|
|
68
|
+
setDraft(null);
|
|
69
|
+
onValueChange(text.trim().toLowerCase());
|
|
70
|
+
} else setDraft(text);
|
|
71
|
+
},
|
|
72
|
+
onBlur: () => setDraft(null),
|
|
73
|
+
className: cx(fieldBox({ size }), "font-mono")
|
|
74
|
+
})]
|
|
75
|
+
}), ownError && /* @__PURE__ */ jsx(ErrorText, {
|
|
76
|
+
id: errorId,
|
|
77
|
+
children: t.invalid
|
|
78
|
+
})]
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
export { ColorInput };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReactElement, type ReactNode } from 'react';
|
|
2
2
|
import { type ComboboxItems } from './comboboxItems.js';
|
|
3
|
+
import { type FieldBoxSize } from './fieldBox.js';
|
|
3
4
|
export type { ComboboxGroup, ComboboxItem, ComboboxItems, ComboboxLeaf, } from './comboboxItems.js';
|
|
4
5
|
interface FieldProps {
|
|
5
6
|
id?: string | undefined;
|
|
@@ -11,7 +12,9 @@ interface FieldProps {
|
|
|
11
12
|
'aria-describedby'?: string | undefined;
|
|
12
13
|
'aria-invalid'?: boolean | undefined;
|
|
13
14
|
'aria-required'?: boolean | undefined;
|
|
14
|
-
/**
|
|
15
|
+
/** `sm` for dense places such as toolbars and table cells. */
|
|
16
|
+
size?: FieldBoxSize | undefined;
|
|
17
|
+
/** Classes for the field box: border, background, width. */
|
|
15
18
|
className?: string | undefined;
|
|
16
19
|
}
|
|
17
20
|
type SelectionProps<Value> = {
|
|
@@ -54,4 +57,4 @@ export interface AutocompleteProps extends FieldProps {
|
|
|
54
57
|
/** A pick-from-the-list field: one value, or several with `multiple`. Typing filters the list. */
|
|
55
58
|
export declare function Combobox<Value = string>(props: ComboboxProps<Value>): import("react").JSX.Element;
|
|
56
59
|
/** A free-text field that suggests values as you type. */
|
|
57
|
-
export declare function Autocomplete({ items, value, defaultValue, onValueChange, className, placeholder, disabled, required, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, 'aria-required': ariaRequired, }: AutocompleteProps): import("react").JSX.Element;
|
|
60
|
+
export declare function Autocomplete({ items, value, defaultValue, onValueChange, className, placeholder, disabled, required, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, 'aria-required': ariaRequired, size, }: AutocompleteProps): import("react").JSX.Element;
|
|
@@ -4,8 +4,8 @@ import { panelClassName, panelItemClassName, positionerClassName } from "./float
|
|
|
4
4
|
import { Tooltip } from "./Tooltip.js";
|
|
5
5
|
import { useStrings } from "./Provider.js";
|
|
6
6
|
import { useQueryMatch } from "./search.js";
|
|
7
|
-
import { isComboboxGroup } from "./comboboxItems.js";
|
|
8
7
|
import { fieldBox } from "./fieldBox.js";
|
|
8
|
+
import { isComboboxGroup } from "./comboboxItems.js";
|
|
9
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
import { useCallback, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
11
11
|
import { Combobox } from "@base-ui/react/combobox";
|
|
@@ -68,17 +68,18 @@ function useEntryFilter() {
|
|
|
68
68
|
return useCallback((entry, query) => entry.created === true || match(query, entry.label, typeof entry.value === "string" ? entry.value : void 0, entry.group), [match]);
|
|
69
69
|
}
|
|
70
70
|
var entryLabel = (entry) => entry?.label ?? "";
|
|
71
|
-
var fieldClassName = cx(fieldBox({
|
|
71
|
+
var fieldClassName = (size) => cx(fieldBox({
|
|
72
|
+
size,
|
|
72
73
|
within: true,
|
|
73
74
|
multiline: true,
|
|
74
75
|
padded: false
|
|
75
76
|
}), "relative flex items-center gap-1");
|
|
76
|
-
var inputClassName = "
|
|
77
|
+
var inputClassName = (size) => cx("min-w-12 flex-1 self-stretch border-0 bg-transparent p-0 text-inherit outline-none placeholder:text-(--theme-placeholder) disabled:cursor-not-allowed", size === "sm" ? "pl-2" : "pl-3");
|
|
77
78
|
var triggerClassName = "flex shrink-0 items-center self-stretch px-2 text-(--theme-muted-text-color) disabled:cursor-not-allowed";
|
|
78
79
|
var popupInputClassName = fieldBox({ size: "sm" });
|
|
79
80
|
/** A pick-from-the-list field: one value, or several with `multiple`. Typing filters the list. */
|
|
80
81
|
function Combobox$1(props) {
|
|
81
|
-
const { items, isItemEqualToValue = dequal, itemToStringLabel = String, className, placeholder, disabled, required, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, trigger, triggerTooltip } = props;
|
|
82
|
+
const { items, isItemEqualToValue = dequal, itemToStringLabel = String, className, placeholder, disabled, required, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, trigger, triggerTooltip, size = "md" } = props;
|
|
82
83
|
const entries = useEntries(items);
|
|
83
84
|
const filter = useEntryFilter();
|
|
84
85
|
const strings = useStrings();
|
|
@@ -122,7 +123,7 @@ function Combobox$1(props) {
|
|
|
122
123
|
"aria-invalid": ariaInvalid || void 0,
|
|
123
124
|
"aria-required": ariaRequired || required || void 0,
|
|
124
125
|
autoComplete: "off",
|
|
125
|
-
className: trigger ? popupInputClassName : inputClassName
|
|
126
|
+
className: trigger ? popupInputClassName : inputClassName(size)
|
|
126
127
|
});
|
|
127
128
|
const itemRow = (entry, rowProps) => /* @__PURE__ */ jsxs(Combobox.Item, {
|
|
128
129
|
value: entry,
|
|
@@ -158,9 +159,9 @@ function Combobox$1(props) {
|
|
|
158
159
|
renderItem: itemRow
|
|
159
160
|
})] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Combobox.InputGroup, {
|
|
160
161
|
"data-testid": "combobox",
|
|
161
|
-
className: cx(fieldClassName, className),
|
|
162
|
+
className: cx(fieldClassName(size), className),
|
|
162
163
|
children: [props.multiple ? /* @__PURE__ */ jsxs(Combobox.Chips, {
|
|
163
|
-
className: "flex min-w-0 flex-1 flex-wrap items-center gap-1
|
|
164
|
+
className: cx("flex min-w-0 flex-1 flex-wrap items-center gap-1 pl-1", size === "sm" ? "py-0.5" : "py-1"),
|
|
164
165
|
children: [/* @__PURE__ */ jsx(Combobox.Value, { children: (values) => values.map((entry) => /* @__PURE__ */ jsxs(Combobox.Chip, {
|
|
165
166
|
"aria-label": entry.label,
|
|
166
167
|
className: "flex items-center gap-1 rounded bg-(--theme-muted-panel-bg) py-0.5 pr-0.5 pl-1.5 text-xs outline-none data-highlighted:bg-(--theme-hover)",
|
|
@@ -171,7 +172,7 @@ function Combobox$1(props) {
|
|
|
171
172
|
})]
|
|
172
173
|
}, entry.label)) }), input]
|
|
173
174
|
}) : /* @__PURE__ */ jsxs(Fragment, { children: [selectedIcon && /* @__PURE__ */ jsx("span", {
|
|
174
|
-
className: "flex shrink-0 items-center pl-3",
|
|
175
|
+
className: cx("flex shrink-0 items-center", size === "sm" ? "pl-2" : "pl-3"),
|
|
175
176
|
children: selectedIcon
|
|
176
177
|
}), input] }), /* @__PURE__ */ jsx(Combobox.Trigger, {
|
|
177
178
|
"aria-label": t.showOptions,
|
|
@@ -216,7 +217,7 @@ function Combobox$1(props) {
|
|
|
216
217
|
});
|
|
217
218
|
}
|
|
218
219
|
/** A free-text field that suggests values as you type. */
|
|
219
|
-
function Autocomplete$1({ items, value, defaultValue, onValueChange, className, placeholder, disabled, required, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired }) {
|
|
220
|
+
function Autocomplete$1({ items, value, defaultValue, onValueChange, className, placeholder, disabled, required, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, size = "md" }) {
|
|
220
221
|
const entries = useEntries(items);
|
|
221
222
|
const filter = useEntryFilter();
|
|
222
223
|
const t = useStrings().dropdown;
|
|
@@ -234,7 +235,7 @@ function Autocomplete$1({ items, value, defaultValue, onValueChange, className,
|
|
|
234
235
|
virtualized: true,
|
|
235
236
|
children: [/* @__PURE__ */ jsxs(Autocomplete.InputGroup, {
|
|
236
237
|
"data-testid": "combobox",
|
|
237
|
-
className: cx(fieldClassName, className),
|
|
238
|
+
className: cx(fieldClassName(size), className),
|
|
238
239
|
children: [/* @__PURE__ */ jsx(Autocomplete.Input, {
|
|
239
240
|
id,
|
|
240
241
|
placeholder,
|
|
@@ -244,7 +245,7 @@ function Autocomplete$1({ items, value, defaultValue, onValueChange, className,
|
|
|
244
245
|
"aria-invalid": ariaInvalid || void 0,
|
|
245
246
|
"aria-required": ariaRequired || required || void 0,
|
|
246
247
|
autoComplete: "off",
|
|
247
|
-
className: inputClassName
|
|
248
|
+
className: inputClassName(size)
|
|
248
249
|
}), entries.flat.length > 0 && /* @__PURE__ */ jsx(Autocomplete.Trigger, {
|
|
249
250
|
"aria-label": t.showOptions,
|
|
250
251
|
"data-testid": "combobox-button",
|
|
@@ -124,7 +124,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
124
124
|
placeholder: namePlaceholder,
|
|
125
125
|
"aria-label": namePlaceholder,
|
|
126
126
|
onAnimationEnd: () => setNameFlash(false),
|
|
127
|
-
className: cx("mt-2 -mb-1 -mx-1.5 w-[calc(100%+0.75rem)] rounded-md border-0 px-1.5 py-1 bg-transparent shadow-none text-lg font-medium text-(--theme-app) placeholder:text-(--theme-
|
|
127
|
+
className: cx("mt-2 -mb-1 -mx-1.5 w-[calc(100%+0.75rem)] rounded-md border-0 px-1.5 py-1 bg-transparent shadow-none text-lg font-medium text-(--theme-app) placeholder:text-(--theme-placeholder) focus:outline-none", nameFlash && "animate-field-flash", nameMissing ? "ring-1 ring-(--theme-error)" : "focus:ring-0")
|
|
128
128
|
}),
|
|
129
129
|
nameError && /* @__PURE__ */ jsx("p", {
|
|
130
130
|
className: "mt-1 text-xs text-(--theme-error)",
|
|
@@ -142,7 +142,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
142
142
|
placeholder: descriptionText,
|
|
143
143
|
"aria-label": descriptionText,
|
|
144
144
|
rows: 3,
|
|
145
|
-
className: "mt-1.5 w-full border-0 p-0 bg-transparent shadow-none resize-none text-sm text-(--theme-app) placeholder:text-(--theme-
|
|
145
|
+
className: "mt-1.5 w-full border-0 p-0 bg-transparent shadow-none resize-none text-sm text-(--theme-app) placeholder:text-(--theme-placeholder) focus:outline-none focus:ring-0"
|
|
146
146
|
}) : /* @__PURE__ */ jsx("div", { className: "mt-3" })
|
|
147
147
|
] }),
|
|
148
148
|
children && /* @__PURE__ */ jsx("div", {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FieldBoxSize } from './fieldBox.js';
|
|
1
2
|
export interface DatePickerProps {
|
|
2
3
|
/** As a native date input holds it: `yyyy-MM-dd`, or with `withTime` the local
|
|
3
4
|
* `yyyy-MM-ddTHH:mm` of `datetime-local`. Empty while nothing is chosen. */
|
|
@@ -21,6 +22,9 @@ export interface DatePickerProps {
|
|
|
21
22
|
'aria-describedby'?: string | undefined;
|
|
22
23
|
'aria-invalid'?: boolean | undefined;
|
|
23
24
|
'aria-required'?: boolean | undefined;
|
|
25
|
+
/** `sm` for dense places such as toolbars and table cells. */
|
|
26
|
+
size?: FieldBoxSize | undefined;
|
|
27
|
+
/** On the row that holds the date and the time: its width or margin. */
|
|
24
28
|
className?: string | undefined;
|
|
25
29
|
}
|
|
26
30
|
/**
|
|
@@ -28,4 +32,4 @@ export interface DatePickerProps {
|
|
|
28
32
|
* date or datetime-local input holds it, so it reads the same in forms and URLs; months, weekdays
|
|
29
33
|
* and the shown date follow the reader's locale.
|
|
30
34
|
*/
|
|
31
|
-
export declare function DatePicker({ value, onValueChange, withTime, min, max, timeStep, required, disabled, placeholder, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, 'aria-required': ariaRequired, className, }: DatePickerProps): import("react").JSX.Element;
|
|
35
|
+
export declare function DatePicker({ value, onValueChange, withTime, min, max, timeStep, required, disabled, placeholder, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, 'aria-required': ariaRequired, size, className, }: DatePickerProps): import("react").JSX.Element;
|
|
@@ -65,13 +65,13 @@ var calendarClassNames = {
|
|
|
65
65
|
outside: "text-(--theme-muted-text-color) opacity-60",
|
|
66
66
|
disabled: "text-(--theme-muted-text-color) opacity-40"
|
|
67
67
|
};
|
|
68
|
-
var triggerClassName = cx(fieldBox(), "flex flex-1 min-w-
|
|
68
|
+
var triggerClassName = (size) => cx(fieldBox({ size }), "flex flex-1 cursor-pointer items-center text-left", size === "sm" ? "min-w-32 gap-1.5" : "min-w-40 gap-2");
|
|
69
69
|
/**
|
|
70
70
|
* A day from a calendar, and with `withTime` a time beside it. The value is a string as a native
|
|
71
71
|
* date or datetime-local input holds it, so it reads the same in forms and URLs; months, weekdays
|
|
72
72
|
* and the shown date follow the reader's locale.
|
|
73
73
|
*/
|
|
74
|
-
function DatePicker({ value, onValueChange, withTime = false, min, max, timeStep = 60, required = false, disabled = false, placeholder, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, className }) {
|
|
74
|
+
function DatePicker({ value, onValueChange, withTime = false, min, max, timeStep = 60, required = false, disabled = false, placeholder, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, size = "md", className }) {
|
|
75
75
|
const t = useStrings().datePicker;
|
|
76
76
|
const { common } = useStrings();
|
|
77
77
|
const [open, setOpen] = useState(false);
|
|
@@ -95,7 +95,7 @@ function DatePicker({ value, onValueChange, withTime = false, min, max, timeStep
|
|
|
95
95
|
};
|
|
96
96
|
const sameDay = (bound) => day !== void 0 && parseDay(bound)?.getTime() === day.getTime();
|
|
97
97
|
return /* @__PURE__ */ jsxs("div", {
|
|
98
|
-
className: cx("flex w-full items-center gap-2", className),
|
|
98
|
+
className: cx("flex w-full items-center", size === "sm" ? "gap-1.5" : "gap-2", className),
|
|
99
99
|
children: [/* @__PURE__ */ jsxs(Popover.Root, {
|
|
100
100
|
open,
|
|
101
101
|
onOpenChange: setOpen,
|
|
@@ -107,12 +107,12 @@ function DatePicker({ value, onValueChange, withTime = false, min, max, timeStep
|
|
|
107
107
|
"aria-describedby": ariaDescribedBy,
|
|
108
108
|
"aria-invalid": ariaInvalid || void 0,
|
|
109
109
|
"aria-required": ariaRequired || void 0,
|
|
110
|
-
className: triggerClassName,
|
|
110
|
+
className: triggerClassName(size),
|
|
111
111
|
children: [/* @__PURE__ */ jsx(CalendarIcon, {
|
|
112
112
|
"aria-hidden": "true",
|
|
113
|
-
className: "
|
|
113
|
+
className: cx("shrink-0 text-(--theme-muted-text-color)", size === "sm" ? "size-3" : "size-3.5")
|
|
114
114
|
}), /* @__PURE__ */ jsx("span", {
|
|
115
|
-
className: cx("truncate", !day && "text-(--theme-
|
|
115
|
+
className: cx("truncate", !day && "text-(--theme-placeholder)"),
|
|
116
116
|
children: day ? shownDay.format(day) : placeholder ?? t.pickDate
|
|
117
117
|
})]
|
|
118
118
|
}), /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Positioner, {
|
|
@@ -183,7 +183,7 @@ function DatePicker({ value, onValueChange, withTime = false, min, max, timeStep
|
|
|
183
183
|
const next = event.target.value;
|
|
184
184
|
if (next) emit(day ?? earliest ?? /* @__PURE__ */ new Date(), next);
|
|
185
185
|
},
|
|
186
|
-
className: cx(fieldBox(), "w-auto tabular-nums")
|
|
186
|
+
className: cx(fieldBox({ size }), "w-auto tabular-nums")
|
|
187
187
|
})]
|
|
188
188
|
});
|
|
189
189
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type ReactNode } from 'react';
|
|
1
|
+
import { type OutputHTMLAttributes, type ReactNode } from 'react';
|
|
2
|
+
import type { FieldBoxSize } from './fieldBox.js';
|
|
2
3
|
/** What a field's control takes to be named, described and marked by its field. Spread it
|
|
3
4
|
* on the control: `{(control) => <input {...control} />}`. */
|
|
4
5
|
export interface FieldControlProps {
|
|
@@ -43,12 +44,15 @@ export interface FieldProps {
|
|
|
43
44
|
id?: string | undefined;
|
|
44
45
|
className?: string | undefined;
|
|
45
46
|
/** The control, or a function that places the control props on it. A Base UI control, such
|
|
46
|
-
* as Select, Combobox, NumberInput, Checkbox or Switch, needs only the `id`.
|
|
47
|
+
* as Select, Combobox, NumberInput, Checkbox or Switch, needs only the `id`. A value shown
|
|
48
|
+
* but not edited goes in a `FieldValue`. */
|
|
47
49
|
children: ReactNode | ((control: FieldControlProps) => ReactNode);
|
|
48
50
|
}
|
|
49
|
-
/** An error in the look a Field gives its own, for
|
|
50
|
-
* the whole of it, such as a row of a list editor.
|
|
51
|
-
|
|
51
|
+
/** An error in the look a Field gives its own, for a control outside a Field, or for a part of
|
|
52
|
+
* one rather than the whole of it, such as a row of a list editor. Give it an `id` and name it
|
|
53
|
+
* in the control's `aria-describedby` so the error is read with the control. */
|
|
54
|
+
export declare function ErrorText({ id, children }: {
|
|
55
|
+
id?: string | undefined;
|
|
52
56
|
children: ReactNode;
|
|
53
57
|
}): import("react").JSX.Element | null;
|
|
54
58
|
/** A control with its label, help and error, tied to it for assistive technology. One frame
|
|
@@ -68,3 +72,13 @@ export declare function RevealToggle({ revealed, onRevealedChange, disabled, }:
|
|
|
68
72
|
onRevealedChange: (revealed: boolean) => void;
|
|
69
73
|
disabled?: boolean | undefined;
|
|
70
74
|
}): import("react").JSX.Element;
|
|
75
|
+
export interface FieldValueProps extends OutputHTMLAttributes<HTMLOutputElement> {
|
|
76
|
+
/** Matches the height and text of the controls around it, as `size` does on a control. */
|
|
77
|
+
size?: FieldBoxSize | undefined;
|
|
78
|
+
}
|
|
79
|
+
/** A value a Field shows that nobody edits, such as one worked out from other fields. It is an
|
|
80
|
+
* `<output>`, which the Field's label names and which reads out when the value changes; list
|
|
81
|
+
* the controls it is worked out from in `htmlFor`. It keeps a control's height, so it lines
|
|
82
|
+
* up with the fields around it. For a set of names and values on their own, use
|
|
83
|
+
* DescriptionList. */
|
|
84
|
+
export declare function FieldValue({ size, className, children, ...props }: FieldValueProps): import("react").JSX.Element;
|
package/dist/components/Field.js
CHANGED
|
@@ -20,10 +20,12 @@ function useFieldControl() {
|
|
|
20
20
|
var labelClassName = "flex items-center gap-1 text-sm font-medium text-(--theme-app)";
|
|
21
21
|
var actionsClassName = "ml-auto flex shrink-0 items-center gap-1";
|
|
22
22
|
var errorTextClassName = "flex items-center gap-1.5 text-xs text-(--theme-error)";
|
|
23
|
-
/** An error in the look a Field gives its own, for
|
|
24
|
-
* the whole of it, such as a row of a list editor.
|
|
25
|
-
|
|
23
|
+
/** An error in the look a Field gives its own, for a control outside a Field, or for a part of
|
|
24
|
+
* one rather than the whole of it, such as a row of a list editor. Give it an `id` and name it
|
|
25
|
+
* in the control's `aria-describedby` so the error is read with the control. */
|
|
26
|
+
function ErrorText({ id, children }) {
|
|
26
27
|
return children ? /* @__PURE__ */ jsxs("div", {
|
|
28
|
+
id,
|
|
27
29
|
role: "alert",
|
|
28
30
|
className: errorTextClassName,
|
|
29
31
|
children: [/* @__PURE__ */ jsx(WarningIcon, {
|
|
@@ -155,5 +157,20 @@ function RevealToggle({ revealed, onRevealedChange, disabled }) {
|
|
|
155
157
|
})
|
|
156
158
|
});
|
|
157
159
|
}
|
|
160
|
+
/** A value a Field shows that nobody edits, such as one worked out from other fields. It is an
|
|
161
|
+
* `<output>`, which the Field's label names and which reads out when the value changes; list
|
|
162
|
+
* the controls it is worked out from in `htmlFor`. It keeps a control's height, so it lines
|
|
163
|
+
* up with the fields around it. For a set of names and values on their own, use
|
|
164
|
+
* DescriptionList. */
|
|
165
|
+
function FieldValue({ size = "md", className, children, ...props }) {
|
|
166
|
+
const control = useFieldControl();
|
|
167
|
+
return /* @__PURE__ */ jsx("output", {
|
|
168
|
+
id: control?.id,
|
|
169
|
+
"aria-describedby": control?.["aria-describedby"],
|
|
170
|
+
...props,
|
|
171
|
+
className: cx("flex min-w-0 items-center break-words text-(--theme-app)", size === "sm" ? "min-h-7 text-xs" : "min-h-9 text-sm", className),
|
|
172
|
+
children
|
|
173
|
+
});
|
|
174
|
+
}
|
|
158
175
|
//#endregion
|
|
159
|
-
export { ErrorText, Field$1 as Field, FieldOption, FieldRowContext, RevealToggle, useFieldControl };
|
|
176
|
+
export { ErrorText, Field$1 as Field, FieldOption, FieldRowContext, FieldValue, RevealToggle, useFieldControl };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { InputHTMLAttributes, ReactNode, TextareaHTMLAttributes } from 'react';
|
|
2
|
+
import { type FieldControlProps } from './Field.js';
|
|
3
|
+
import { type FieldBoxSize } from './fieldBox.js';
|
|
2
4
|
interface FramedProps {
|
|
3
5
|
/** Without a label the box stands alone; name it with `aria-label`. */
|
|
4
6
|
label?: ReactNode;
|
|
@@ -8,11 +10,19 @@ interface FramedProps {
|
|
|
8
10
|
description?: ReactNode;
|
|
9
11
|
error?: ReactNode;
|
|
10
12
|
mono?: boolean;
|
|
13
|
+
/** `sm` for dense places such as toolbars and table cells. */
|
|
14
|
+
size?: FieldBoxSize | undefined;
|
|
11
15
|
}
|
|
12
|
-
export interface InputProps extends InputHTMLAttributes<HTMLInputElement>, FramedProps {
|
|
16
|
+
export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>, FramedProps {
|
|
13
17
|
}
|
|
14
18
|
export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement>, FramedProps {
|
|
15
19
|
}
|
|
20
|
+
/** The Field around a control when it has a label; the control alone when it doesn't. */
|
|
21
|
+
export declare function framed({ label, hint, description, error }: Omit<FramedProps, 'mono' | 'size'>, { id, required, disabled, }: {
|
|
22
|
+
id?: string | undefined;
|
|
23
|
+
required?: boolean | undefined;
|
|
24
|
+
disabled?: boolean | undefined;
|
|
25
|
+
}, control: (props: Partial<FieldControlProps>) => ReactNode): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
|
|
16
26
|
/** A single-line text field. `type="password"` adds a button that shows what was typed. */
|
|
17
27
|
export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
18
28
|
/** A multi-line text field that grows by hand. */
|