@parallelworks/ui 0.46.0 → 0.48.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 +17 -2
- package/dist/ai/components/ChatInput.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/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 +12 -3
- package/dist/components/Provider.js +14 -2
- 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/Tabs.js +1 -0
- package/dist/components/ThemeEditor.js +15 -52
- package/dist/components/ToggleGroup.d.ts +42 -0
- package/dist/components/ToggleGroup.js +69 -0
- package/dist/components/fieldBox.js +1 -1
- 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/graph/GraphEditorDialogs.js +1 -1
- package/dist/graph/InputDialog.js +2 -2
- package/dist/graph/WorkflowSettingsDialog.js +1 -1
- package/dist/graph/editorFields.js +2 -2
- package/dist/graph/inputRefs.js +1 -1
- package/dist/graph/stepWith.js +1 -1
- package/dist/icons.d.ts +17 -1
- package/dist/icons.js +19 -3
- 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/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
|
@@ -30,6 +30,8 @@ export interface UIStrings {
|
|
|
30
30
|
share: string;
|
|
31
31
|
send: string;
|
|
32
32
|
filter: string;
|
|
33
|
+
/** A filter's choice that turns it off, showing everything. */
|
|
34
|
+
all: string;
|
|
33
35
|
loading: string;
|
|
34
36
|
loadMore: string;
|
|
35
37
|
showMore: string;
|
|
@@ -110,6 +112,16 @@ export interface UIStrings {
|
|
|
110
112
|
previousMonth: string;
|
|
111
113
|
nextMonth: string;
|
|
112
114
|
};
|
|
115
|
+
colorInput: {
|
|
116
|
+
/** Names the picker beside the hex field: the field's name, or none when it has no plain one. */
|
|
117
|
+
pick: (name?: string) => string;
|
|
118
|
+
/** Under the hex field while what is typed isn't a color. */
|
|
119
|
+
invalid: string;
|
|
120
|
+
};
|
|
121
|
+
oneTimeCode: {
|
|
122
|
+
/** Names each box after the first, which the field's label names. */
|
|
123
|
+
character: (position: number, length: number) => string;
|
|
124
|
+
};
|
|
113
125
|
theme: {
|
|
114
126
|
/** Names the light, dark or system choice. */
|
|
115
127
|
appearance: string;
|
|
@@ -131,9 +143,6 @@ export interface UIStrings {
|
|
|
131
143
|
sidebarAccent: string;
|
|
132
144
|
code: string;
|
|
133
145
|
codeStyle: (style: 'balanced' | 'vivid' | 'muted' | 'mono') => string;
|
|
134
|
-
/** Names a color input's picker, beside its hex field. */
|
|
135
|
-
pick: (name: string) => string;
|
|
136
|
-
invalidColor: string;
|
|
137
146
|
copy: string;
|
|
138
147
|
/** The thing a copy toast names. */
|
|
139
148
|
copied: string;
|
|
@@ -45,6 +45,7 @@ var DEFAULTS = {
|
|
|
45
45
|
share: "Share",
|
|
46
46
|
send: "Send",
|
|
47
47
|
filter: "Filter",
|
|
48
|
+
all: "All",
|
|
48
49
|
loading: "Loading…",
|
|
49
50
|
loadMore: "Load more",
|
|
50
51
|
showMore: "Show more",
|
|
@@ -112,6 +113,11 @@ var DEFAULTS = {
|
|
|
112
113
|
previousMonth: "Previous month",
|
|
113
114
|
nextMonth: "Next month"
|
|
114
115
|
},
|
|
116
|
+
colorInput: {
|
|
117
|
+
pick: (name) => name ? `Pick ${name.toLowerCase()}` : "Pick a color",
|
|
118
|
+
invalid: "Enter a hex color, such as #1f6feb."
|
|
119
|
+
},
|
|
120
|
+
oneTimeCode: { character: (position, length) => `Character ${position} of ${length}` },
|
|
115
121
|
theme: {
|
|
116
122
|
appearance: "Appearance",
|
|
117
123
|
light: "Light",
|
|
@@ -133,8 +139,6 @@ var DEFAULTS = {
|
|
|
133
139
|
muted: "Muted",
|
|
134
140
|
mono: "Monochrome"
|
|
135
141
|
})[style],
|
|
136
|
-
pick: (name) => `Pick ${name.toLowerCase()}`,
|
|
137
|
-
invalidColor: "Enter a hex color, such as #1f6feb.",
|
|
138
142
|
copy: "Copy theme",
|
|
139
143
|
copied: "theme",
|
|
140
144
|
import: "Import theme",
|
|
@@ -632,6 +636,14 @@ function UIProvider({ strings, navigation, data, slots, engine, children }) {
|
|
|
632
636
|
...DEFAULTS.strings.datePicker,
|
|
633
637
|
...strings?.datePicker
|
|
634
638
|
},
|
|
639
|
+
colorInput: {
|
|
640
|
+
...DEFAULTS.strings.colorInput,
|
|
641
|
+
...strings?.colorInput
|
|
642
|
+
},
|
|
643
|
+
oneTimeCode: {
|
|
644
|
+
...DEFAULTS.strings.oneTimeCode,
|
|
645
|
+
...strings?.oneTimeCode
|
|
646
|
+
},
|
|
635
647
|
theme: {
|
|
636
648
|
...DEFAULTS.strings.theme,
|
|
637
649
|
...strings?.theme
|
|
@@ -5,6 +5,7 @@ export interface SearchInputProps extends Omit<InputHTMLAttributes<HTMLInputElem
|
|
|
5
5
|
onValueChange: (value: string) => void;
|
|
6
6
|
/** Names the box, and is its placeholder unless `placeholder` says otherwise. */
|
|
7
7
|
label?: string | undefined;
|
|
8
|
+
/** `sm` for dense places such as toolbars and table cells. */
|
|
8
9
|
size?: FieldBoxSize | undefined;
|
|
9
10
|
/** On the wrapper: its width or margin. */
|
|
10
11
|
className?: string | undefined;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
2
|
export type SectionVariant = 'plain' | 'card';
|
|
3
3
|
export interface SectionProps {
|
|
4
4
|
title: ReactNode;
|
|
5
|
+
/** Under the title in muted text, saying what the section holds; it describes
|
|
6
|
+
* the section, and its toggle, to assistive technology. */
|
|
7
|
+
description?: ReactNode;
|
|
5
8
|
/** Beside the title, outside the toggle, such as an info tip or a count. */
|
|
6
9
|
beside?: ReactNode;
|
|
7
10
|
/** At the header's far end, such as an add button or a range picker. */
|
|
@@ -18,6 +21,8 @@ export interface SectionProps {
|
|
|
18
21
|
defaultOpen?: boolean | undefined;
|
|
19
22
|
/** The heading level the title sits at in the page's outline. */
|
|
20
23
|
headingLevel?: 2 | 3 | 4 | undefined;
|
|
24
|
+
/** On the section, so a link or a scroll can target it. */
|
|
25
|
+
id?: string | undefined;
|
|
21
26
|
className?: string | undefined;
|
|
22
27
|
children: ReactNode;
|
|
23
28
|
}
|
|
@@ -25,4 +30,4 @@ export interface SectionProps {
|
|
|
25
30
|
* that can fold away. Folding keeps the content mounted, so fields hold their
|
|
26
31
|
* values and validation while closed. For a line inside content that reveals
|
|
27
32
|
* more detail, use Disclosure. */
|
|
28
|
-
export declare function Section({ title, beside, actions, variant, collapsible, open, onOpenChange, defaultOpen, headingLevel, className, children, }: SectionProps): import("react").JSX.Element;
|
|
33
|
+
export declare function Section({ title, description, beside, actions, variant, collapsible, open, onOpenChange, defaultOpen, headingLevel, id, className, children, }: SectionProps): import("react").JSX.Element;
|
|
@@ -2,73 +2,92 @@ import { cx } from "../cx.js";
|
|
|
2
2
|
import { ChevronRightIcon } from "../icons.js";
|
|
3
3
|
import { collapsePanelClassName } from "./Disclosure.js";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { useId } from "react";
|
|
5
6
|
import { Collapsible } from "@base-ui/react/collapsible";
|
|
6
7
|
//#region src/components/Section.tsx
|
|
7
8
|
var rootClasses = {
|
|
8
9
|
plain: "flex w-full flex-col",
|
|
9
|
-
card: "
|
|
10
|
+
card: "panel mb-1 flex w-full flex-col"
|
|
10
11
|
};
|
|
11
12
|
var headerClasses = {
|
|
12
13
|
plain: "mb-2 flex w-full items-center gap-2 border-b border-(--theme-border) pb-1",
|
|
13
|
-
card: "flex w-full items-center gap-2 px-
|
|
14
|
+
card: "flex w-full items-center gap-2 px-3 py-3"
|
|
14
15
|
};
|
|
16
|
+
var cardBodyClassName = "px-3 pb-3";
|
|
15
17
|
var titleClasses = {
|
|
16
18
|
plain: "text-lg font-semibold",
|
|
17
|
-
card: "text-sm font-bold
|
|
19
|
+
card: "text-sm font-bold"
|
|
18
20
|
};
|
|
21
|
+
var foldedTitleClasses = {
|
|
22
|
+
plain: "",
|
|
23
|
+
card: "not-data-panel-open:text-(--theme-muted-text-color)"
|
|
24
|
+
};
|
|
25
|
+
var chevronIndent = "pl-6";
|
|
19
26
|
/** A titled part of a page or form, its title a heading in the page's outline,
|
|
20
27
|
* that can fold away. Folding keeps the content mounted, so fields hold their
|
|
21
28
|
* values and validation while closed. For a line inside content that reveals
|
|
22
29
|
* more detail, use Disclosure. */
|
|
23
|
-
function Section({ title, beside, actions, variant = "plain", collapsible = true, open, onOpenChange, defaultOpen = true, headingLevel = 3, className, children }) {
|
|
30
|
+
function Section({ title, description, beside, actions, variant = "plain", collapsible = true, open, onOpenChange, defaultOpen = true, headingLevel = 3, id, className, children }) {
|
|
24
31
|
const Heading = `h${headingLevel}`;
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
children: actions
|
|
28
|
-
});
|
|
32
|
+
const descriptionId = useId();
|
|
33
|
+
const describedBy = description ? descriptionId : void 0;
|
|
29
34
|
const titleProps = { "data-section-title": "" };
|
|
30
35
|
const fill = !beside && !actions;
|
|
36
|
+
const header = (heading) => /* @__PURE__ */ jsxs("div", {
|
|
37
|
+
className: headerClasses[variant],
|
|
38
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
39
|
+
className: "flex min-w-0 flex-1 flex-col gap-0.5",
|
|
40
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
41
|
+
className: "flex min-w-0 items-center gap-2",
|
|
42
|
+
children: [heading, beside]
|
|
43
|
+
}), description && /* @__PURE__ */ jsx("p", {
|
|
44
|
+
id: descriptionId,
|
|
45
|
+
className: cx("text-sm theme-muted-text", collapsible && chevronIndent),
|
|
46
|
+
children: description
|
|
47
|
+
})]
|
|
48
|
+
}), actions && /* @__PURE__ */ jsx("div", {
|
|
49
|
+
className: "flex shrink-0 items-center gap-2",
|
|
50
|
+
children: actions
|
|
51
|
+
})]
|
|
52
|
+
});
|
|
31
53
|
if (!collapsible) return /* @__PURE__ */ jsxs("section", {
|
|
54
|
+
id,
|
|
55
|
+
"aria-describedby": describedBy,
|
|
32
56
|
className: cx(rootClasses[variant], className),
|
|
33
|
-
children: [/* @__PURE__ */
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
beside,
|
|
42
|
-
end
|
|
43
|
-
]
|
|
44
|
-
}), children]
|
|
57
|
+
children: [header(/* @__PURE__ */ jsx(Heading, {
|
|
58
|
+
...titleProps,
|
|
59
|
+
className: cx("flex items-center", titleClasses[variant]),
|
|
60
|
+
children: title
|
|
61
|
+
})), variant === "card" ? /* @__PURE__ */ jsx("div", {
|
|
62
|
+
className: cardBodyClassName,
|
|
63
|
+
children
|
|
64
|
+
}) : children]
|
|
45
65
|
});
|
|
46
66
|
return /* @__PURE__ */ jsxs(Collapsible.Root, {
|
|
67
|
+
id,
|
|
68
|
+
"aria-describedby": describedBy,
|
|
47
69
|
open,
|
|
48
70
|
onOpenChange: (next) => onOpenChange?.(next),
|
|
49
71
|
defaultOpen,
|
|
50
72
|
className: cx(rootClasses[variant], className),
|
|
51
|
-
children: [/* @__PURE__ */
|
|
52
|
-
className:
|
|
53
|
-
children:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
})
|
|
64
|
-
}),
|
|
65
|
-
beside,
|
|
66
|
-
end
|
|
67
|
-
]
|
|
68
|
-
}), /* @__PURE__ */ jsx(Collapsible.Panel, {
|
|
73
|
+
children: [header(/* @__PURE__ */ jsx(Heading, {
|
|
74
|
+
className: cx("flex min-w-0", fill && "flex-1"),
|
|
75
|
+
children: /* @__PURE__ */ jsxs(Collapsible.Trigger, {
|
|
76
|
+
...titleProps,
|
|
77
|
+
"aria-describedby": describedBy,
|
|
78
|
+
className: cx("group flex min-w-0 cursor-pointer items-center gap-1 text-left", fill && "w-full", titleClasses[variant], foldedTitleClasses[variant]),
|
|
79
|
+
children: [/* @__PURE__ */ jsx(ChevronRightIcon, {
|
|
80
|
+
"aria-hidden": "true",
|
|
81
|
+
className: cx("shrink-0 transition-transform group-data-panel-open:rotate-90 motion-reduce:transition-none", "px-1 text-[1.25rem]")
|
|
82
|
+
}), title]
|
|
83
|
+
})
|
|
84
|
+
})), /* @__PURE__ */ jsx(Collapsible.Panel, {
|
|
69
85
|
keepMounted: true,
|
|
70
86
|
className: collapsePanelClassName,
|
|
71
|
-
children
|
|
87
|
+
children: variant === "card" ? /* @__PURE__ */ jsx("div", {
|
|
88
|
+
className: cardBodyClassName,
|
|
89
|
+
children
|
|
90
|
+
}) : children
|
|
72
91
|
})]
|
|
73
92
|
});
|
|
74
93
|
}
|
|
@@ -10,8 +10,10 @@ export interface SegmentedOption<T extends string> {
|
|
|
10
10
|
}
|
|
11
11
|
/** A few mutually exclusive choices side by side, one always current, such as
|
|
12
12
|
* a time range or what to group by. To move between views or pages, use
|
|
13
|
-
* `Tabs` with `variant="segmented"`, which looks the same.
|
|
14
|
-
*
|
|
13
|
+
* `Tabs` with `variant="segmented"`, which looks the same. To filter a list
|
|
14
|
+
* by values that may all be off, or several at once, use `ToggleGroup`. Name
|
|
15
|
+
* it with `label`, or leave that out inside a `Field group`, whose legend
|
|
16
|
+
* names it. */
|
|
15
17
|
export declare function SegmentedControl<T extends string>({ label, options, value, onValueChange, fill, iconOnly, testId, }: SegmentLayout & {
|
|
16
18
|
label?: string | undefined;
|
|
17
19
|
options: SegmentedOption<T>[];
|
|
@@ -6,8 +6,10 @@ import { ToggleGroup } from "@base-ui/react/toggle-group";
|
|
|
6
6
|
//#region src/components/SegmentedControl.tsx
|
|
7
7
|
/** A few mutually exclusive choices side by side, one always current, such as
|
|
8
8
|
* a time range or what to group by. To move between views or pages, use
|
|
9
|
-
* `Tabs` with `variant="segmented"`, which looks the same.
|
|
10
|
-
*
|
|
9
|
+
* `Tabs` with `variant="segmented"`, which looks the same. To filter a list
|
|
10
|
+
* by values that may all be off, or several at once, use `ToggleGroup`. Name
|
|
11
|
+
* it with `label`, or leave that out inside a `Field group`, whose legend
|
|
12
|
+
* names it. */
|
|
11
13
|
function SegmentedControl({ label, options, value, onValueChange, fill = false, iconOnly = false, testId }) {
|
|
12
14
|
const layout = {
|
|
13
15
|
fill,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Select as BaseSelect } from '@base-ui/react/select';
|
|
2
2
|
import type { ReactElement, ReactNode } from 'react';
|
|
3
|
+
import { type FieldBoxSize } from './fieldBox.js';
|
|
3
4
|
export interface SelectItem<Value> {
|
|
4
5
|
value: Value;
|
|
5
6
|
label: string;
|
|
@@ -24,7 +25,7 @@ export type SelectProps<Value, Multiple extends boolean | undefined = false> = O
|
|
|
24
25
|
/** A hint on the trigger button. */
|
|
25
26
|
triggerTooltip?: ReactNode;
|
|
26
27
|
/** `sm` for dense places such as toolbars and table cells. */
|
|
27
|
-
size?:
|
|
28
|
+
size?: FieldBoxSize | undefined;
|
|
28
29
|
/** The item a person is on as they move through the open list, then undefined once it
|
|
29
30
|
* closes, such as to preview a theme before it is chosen. */
|
|
30
31
|
onItemHighlighted?: ((value: Value | undefined) => void) | undefined;
|
|
@@ -36,7 +36,7 @@ function Select$1({ items, placeholder, className, "aria-label": ariaLabel, "ari
|
|
|
36
36
|
className: cx(fieldBox({ size }), triggerClassName, className),
|
|
37
37
|
children: [/* @__PURE__ */ jsx(Select.Value, {
|
|
38
38
|
placeholder,
|
|
39
|
-
className: "truncate data-placeholder:text-(--theme-
|
|
39
|
+
className: "truncate data-placeholder:text-(--theme-placeholder)",
|
|
40
40
|
children: rootProps.multiple ? (values) => values.length > 0 ? values.map(labelOf).join(", ") : placeholder : void 0
|
|
41
41
|
}), /* @__PURE__ */ jsx(Select.Icon, {
|
|
42
42
|
className: "shrink-0 text-(--theme-muted-text-color)",
|
package/dist/components/Tabs.js
CHANGED
|
@@ -52,6 +52,7 @@ function Tabs$1({ label, items, value, defaultValue, onValueChange, variant = "l
|
|
|
52
52
|
},
|
|
53
53
|
orientation: layout.iconOnly ? "vertical" : "horizontal",
|
|
54
54
|
"data-testid": testId,
|
|
55
|
+
className: children ? void 0 : "contents",
|
|
55
56
|
children: [showRow && /* @__PURE__ */ jsxs("div", {
|
|
56
57
|
className: cx("flex items-center justify-between gap-2", !segmented && "border-b border-(--theme-border)", sticky && stickyClasses[sticky], className),
|
|
57
58
|
children: [/* @__PURE__ */ jsxs(Tabs.List, {
|
|
@@ -2,18 +2,19 @@ import { cx } from "../cx.js";
|
|
|
2
2
|
import { useStrings } from "./Provider.js";
|
|
3
3
|
import { useCopy } from "./useCopy.js";
|
|
4
4
|
import { Button } from "./Button.js";
|
|
5
|
+
import { parseColor, toHex } from "../theme/color.js";
|
|
6
|
+
import { CODE_STYLES, DEFAULT_THEME, THEME_PRESETS, modeOf, parseTheme, schemeFor, serializeTheme } from "../theme/model.js";
|
|
7
|
+
import { deriveTheme, generateTheme } from "../theme/derive.js";
|
|
8
|
+
import { prefersWideGamut } from "../theme/index.js";
|
|
9
|
+
import { Field } from "./Field.js";
|
|
5
10
|
import { fieldBox } from "./fieldBox.js";
|
|
11
|
+
import { Textarea } from "./Input.js";
|
|
12
|
+
import { ColorInput } from "./ColorInput.js";
|
|
6
13
|
import { Disclosure } from "./Disclosure.js";
|
|
7
14
|
import { Switch } from "./Switch.js";
|
|
8
|
-
import { Field } from "./Field.js";
|
|
9
|
-
import { Textarea } from "./Input.js";
|
|
10
15
|
import { Select } from "./Select.js";
|
|
11
16
|
import { SegmentedControl } from "./SegmentedControl.js";
|
|
12
17
|
import { Slider } from "./Slider.js";
|
|
13
|
-
import { parseColor, toHex } from "../theme/color.js";
|
|
14
|
-
import { CODE_STYLES, DEFAULT_THEME, THEME_PRESETS, modeOf, parseTheme, schemeFor, serializeTheme } from "../theme/model.js";
|
|
15
|
-
import { deriveTheme, generateTheme } from "../theme/derive.js";
|
|
16
|
-
import { prefersWideGamut } from "../theme/index.js";
|
|
17
18
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18
19
|
import { useId, useMemo, useState } from "react";
|
|
19
20
|
//#region src/components/ThemeEditor.tsx
|
|
@@ -101,15 +102,15 @@ function ThemeEditor({ value, onChange, mode, onModeChange, preference, onPrefer
|
|
|
101
102
|
onValueChange: setEditing
|
|
102
103
|
})
|
|
103
104
|
}),
|
|
104
|
-
/* @__PURE__ */ jsx(
|
|
105
|
+
/* @__PURE__ */ jsx(ColorInput, {
|
|
105
106
|
label: t.base,
|
|
106
107
|
value: seed.base,
|
|
107
|
-
|
|
108
|
+
onValueChange: (base) => setSeed({ base })
|
|
108
109
|
}),
|
|
109
|
-
/* @__PURE__ */ jsx(
|
|
110
|
+
/* @__PURE__ */ jsx(ColorInput, {
|
|
110
111
|
label: t.accent,
|
|
111
112
|
value: seed.accent,
|
|
112
|
-
|
|
113
|
+
onValueChange: (accent) => setSeed({ accent })
|
|
113
114
|
}),
|
|
114
115
|
/* @__PURE__ */ jsx(Field, {
|
|
115
116
|
label: t.contrast,
|
|
@@ -141,20 +142,20 @@ function ThemeEditor({ value, onChange, mode, onModeChange, preference, onPrefer
|
|
|
141
142
|
}),
|
|
142
143
|
scheme.sidebar && /* @__PURE__ */ jsxs("div", {
|
|
143
144
|
className: "flex flex-col gap-5 border-l-2 border-(--theme-border) pl-4",
|
|
144
|
-
children: [/* @__PURE__ */ jsx(
|
|
145
|
+
children: [/* @__PURE__ */ jsx(ColorInput, {
|
|
145
146
|
label: t.sidebarBase,
|
|
146
147
|
value: scheme.sidebar.base ?? derived["--theme-sidebar-bg"],
|
|
147
|
-
|
|
148
|
+
onValueChange: (base) => update({
|
|
148
149
|
...scheme,
|
|
149
150
|
sidebar: {
|
|
150
151
|
...scheme.sidebar,
|
|
151
152
|
base
|
|
152
153
|
}
|
|
153
154
|
})
|
|
154
|
-
}), /* @__PURE__ */ jsx(
|
|
155
|
+
}), /* @__PURE__ */ jsx(ColorInput, {
|
|
155
156
|
label: t.sidebarAccent,
|
|
156
157
|
value: scheme.sidebar.accent ?? seed.accent,
|
|
157
|
-
|
|
158
|
+
onValueChange: (accent) => update({
|
|
158
159
|
...scheme,
|
|
159
160
|
sidebar: {
|
|
160
161
|
...scheme.sidebar,
|
|
@@ -214,44 +215,6 @@ function Swatch({ colors }) {
|
|
|
214
215
|
}, i))
|
|
215
216
|
});
|
|
216
217
|
}
|
|
217
|
-
/** A color as a picker beside a hex field. A half-typed hex stays in the field, marked, until it parses. */
|
|
218
|
-
function ColorField({ label, value, onChange }) {
|
|
219
|
-
const { theme: t } = useStrings();
|
|
220
|
-
const [draft, setDraft] = useState(null);
|
|
221
|
-
const parsed = parseColor(value);
|
|
222
|
-
const hex = parsed ? toHex(parsed) : "#000000";
|
|
223
|
-
const invalid = draft !== null && parseColor(draft) === null;
|
|
224
|
-
return /* @__PURE__ */ jsx(Field, {
|
|
225
|
-
label,
|
|
226
|
-
error: invalid ? t.invalidColor : void 0,
|
|
227
|
-
children: (control) => /* @__PURE__ */ jsxs("div", {
|
|
228
|
-
className: "flex items-center gap-2",
|
|
229
|
-
children: [/* @__PURE__ */ jsx("input", {
|
|
230
|
-
type: "color",
|
|
231
|
-
"aria-label": t.pick(label),
|
|
232
|
-
value: hex,
|
|
233
|
-
onChange: (e) => {
|
|
234
|
-
setDraft(null);
|
|
235
|
-
onChange(e.target.value);
|
|
236
|
-
},
|
|
237
|
-
className: "h-8 w-10 shrink-0 cursor-pointer rounded-md border border-(--theme-border) bg-transparent p-0.5"
|
|
238
|
-
}), /* @__PURE__ */ jsx("input", {
|
|
239
|
-
...control,
|
|
240
|
-
className: fieldBox({ size: "sm" }),
|
|
241
|
-
spellCheck: false,
|
|
242
|
-
value: draft ?? value,
|
|
243
|
-
onChange: (e) => {
|
|
244
|
-
const text = e.target.value;
|
|
245
|
-
if (parseColor(text)) {
|
|
246
|
-
setDraft(null);
|
|
247
|
-
onChange(text.trim().toLowerCase());
|
|
248
|
-
} else setDraft(text);
|
|
249
|
-
},
|
|
250
|
-
onBlur: () => setDraft(null)
|
|
251
|
-
})]
|
|
252
|
-
})
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
218
|
function ImportTheme({ onImport }) {
|
|
256
219
|
const { theme: t } = useStrings();
|
|
257
220
|
const [open, setOpen] = useState(false);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface ToggleGroupOption<T extends string> {
|
|
3
|
+
value: T;
|
|
4
|
+
label: string;
|
|
5
|
+
/** Before the label, such as a `StatusDot` in the status's tone or a provider's logo. */
|
|
6
|
+
icon?: ReactNode;
|
|
7
|
+
/** How many items the option matches, after the label. */
|
|
8
|
+
count?: number | undefined;
|
|
9
|
+
disabled?: boolean | undefined;
|
|
10
|
+
testId?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ToggleGroupProps<T extends string> {
|
|
13
|
+
/** Names the group for assistive technology. Leave it out inside a
|
|
14
|
+
* `Field group`, whose legend names it. */
|
|
15
|
+
label?: string | undefined;
|
|
16
|
+
options: readonly ToggleGroupOption<T>[];
|
|
17
|
+
/** The pressed options. Empty filters nothing out. */
|
|
18
|
+
value: readonly T[];
|
|
19
|
+
/** Called with the pressed options in the order `options` lists them. */
|
|
20
|
+
onValueChange: (value: T[]) => void;
|
|
21
|
+
/** Several options may be pressed at once; `false` allows one or none. */
|
|
22
|
+
multiple?: boolean | undefined;
|
|
23
|
+
/** Leads with an "All" option, pressed while nothing else is, that clears
|
|
24
|
+
* the others. `{ count }` shows the total beside it. */
|
|
25
|
+
all?: boolean | {
|
|
26
|
+
count?: number | undefined;
|
|
27
|
+
} | undefined;
|
|
28
|
+
size?: 'sm' | 'md' | undefined;
|
|
29
|
+
disabled?: boolean | undefined;
|
|
30
|
+
testId?: string;
|
|
31
|
+
}
|
|
32
|
+
/** Options that narrow a list, such as run statuses or job states, as a row of
|
|
33
|
+
* toggle buttons that wraps on a narrow screen. Pressing one adds it to the
|
|
34
|
+
* filter and pressing it again takes it out, so an empty filter shows
|
|
35
|
+
* everything; with `multiple={false}` pressing one replaces the last. Each
|
|
36
|
+
* option may carry an icon, such as a status dot, and a count.
|
|
37
|
+
*
|
|
38
|
+
* Use `SegmentedControl` instead for a setting that always has exactly one
|
|
39
|
+
* value, such as a time range, what to group by, or which view of the same
|
|
40
|
+
* data to show. A filter that may be off, or takes several values, is a
|
|
41
|
+
* `ToggleGroup`. */
|
|
42
|
+
export declare function ToggleGroup<T extends string>({ label, options, value, onValueChange, multiple, all, size, disabled, testId, }: ToggleGroupProps<T>): import("react").JSX.Element;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { cx } from "../cx.js";
|
|
2
|
+
import { buttonClasses } from "./buttonClasses.js";
|
|
3
|
+
import { useStrings } from "./Provider.js";
|
|
4
|
+
import { formatCount } from "../format.js";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Toggle } from "@base-ui/react/toggle";
|
|
7
|
+
import { ToggleGroup } from "@base-ui/react/toggle-group";
|
|
8
|
+
//#region src/components/ToggleGroup.tsx
|
|
9
|
+
var ALL = "\0all";
|
|
10
|
+
/** Options that narrow a list, such as run statuses or job states, as a row of
|
|
11
|
+
* toggle buttons that wraps on a narrow screen. Pressing one adds it to the
|
|
12
|
+
* filter and pressing it again takes it out, so an empty filter shows
|
|
13
|
+
* everything; with `multiple={false}` pressing one replaces the last. Each
|
|
14
|
+
* option may carry an icon, such as a status dot, and a count.
|
|
15
|
+
*
|
|
16
|
+
* Use `SegmentedControl` instead for a setting that always has exactly one
|
|
17
|
+
* value, such as a time range, what to group by, or which view of the same
|
|
18
|
+
* data to show. A filter that may be off, or takes several values, is a
|
|
19
|
+
* `ToggleGroup`. */
|
|
20
|
+
function ToggleGroup$1({ label, options, value, onValueChange, multiple = true, all = false, size = "sm", disabled = false, testId }) {
|
|
21
|
+
const strings = useStrings();
|
|
22
|
+
const allCount = typeof all === "object" ? all.count : void 0;
|
|
23
|
+
const pressed = all && value.length === 0 ? [ALL] : value;
|
|
24
|
+
return /* @__PURE__ */ jsxs(ToggleGroup, {
|
|
25
|
+
"aria-label": label,
|
|
26
|
+
"data-testid": testId,
|
|
27
|
+
className: "flex flex-wrap items-center gap-1",
|
|
28
|
+
multiple,
|
|
29
|
+
disabled,
|
|
30
|
+
value: pressed,
|
|
31
|
+
onValueChange: (next) => {
|
|
32
|
+
if (next.includes(ALL) && !pressed.includes(ALL)) {
|
|
33
|
+
onValueChange([]);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
onValueChange(options.filter((option) => next.includes(option.value)).map((option) => option.value));
|
|
37
|
+
},
|
|
38
|
+
children: [all && /* @__PURE__ */ jsx(Chip, {
|
|
39
|
+
value: ALL,
|
|
40
|
+
label: strings.common.all,
|
|
41
|
+
count: allCount,
|
|
42
|
+
size
|
|
43
|
+
}), options.map((option) => /* @__PURE__ */ jsx(Chip, {
|
|
44
|
+
...option,
|
|
45
|
+
size
|
|
46
|
+
}, option.value))]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function Chip({ value, label, icon, count, disabled, testId, size }) {
|
|
50
|
+
return /* @__PURE__ */ jsxs(Toggle, {
|
|
51
|
+
value,
|
|
52
|
+
disabled,
|
|
53
|
+
"data-testid": testId,
|
|
54
|
+
className: cx("group aria-pressed:bg-(--theme-accent-subtle)", buttonClasses({
|
|
55
|
+
variant: "outline",
|
|
56
|
+
size
|
|
57
|
+
})),
|
|
58
|
+
children: [
|
|
59
|
+
icon,
|
|
60
|
+
label,
|
|
61
|
+
count !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
62
|
+
className: "tabular-nums text-(--theme-muted-text-color) group-aria-pressed:text-(--theme-app)",
|
|
63
|
+
children: formatCount(count)
|
|
64
|
+
})
|
|
65
|
+
]
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
export { ToggleGroup$1 as ToggleGroup };
|
|
@@ -16,7 +16,7 @@ var sizes = {
|
|
|
16
16
|
text: "text-sm"
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
|
-
var base = "w-full min-w-0 rounded-md border border-(--theme-border) bg-(--theme-input-bg) text-(--theme-app) outline-none transition-colors placeholder:text-(--theme-
|
|
19
|
+
var base = "w-full min-w-0 rounded-md border border-(--theme-border) bg-(--theme-input-bg) text-(--theme-app) outline-none transition-colors placeholder:text-(--theme-placeholder)";
|
|
20
20
|
var own = cx("focus-visible:border-(--theme-accent) focus-visible:ring-1 focus-visible:ring-(--theme-accent)", "aria-invalid:border-(--theme-error) aria-invalid:focus-visible:ring-(--theme-error)", "disabled:cursor-not-allowed disabled:bg-(--theme-input-disabled-bg) disabled:text-(--theme-placeholder)", "data-disabled:cursor-not-allowed data-disabled:bg-(--theme-input-disabled-bg) data-disabled:text-(--theme-placeholder)");
|
|
21
21
|
var inside = cx("has-[:focus-visible]:border-(--theme-accent) has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-(--theme-accent)", "has-[[aria-invalid=true]]:border-(--theme-error) has-[[aria-invalid=true]:focus-visible]:ring-(--theme-error)", "has-[input:disabled]:cursor-not-allowed has-[input:disabled]:bg-(--theme-input-disabled-bg) has-[input:disabled]:text-(--theme-placeholder)", "data-disabled:cursor-not-allowed data-disabled:bg-(--theme-input-disabled-bg) data-disabled:text-(--theme-placeholder)");
|
|
22
22
|
/** The box every input-like control draws: text inputs, textareas, and the triggers of
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Tooltip } from "../../components/Tooltip.js";
|
|
2
|
-
import { Switch } from "../../components/Switch.js";
|
|
3
2
|
import { Field } from "../../components/Field.js";
|
|
3
|
+
import { Switch } from "../../components/Switch.js";
|
|
4
4
|
import { fieldFrame } from "../fieldFrame.js";
|
|
5
5
|
import { useFieldValue } from "../useFieldValue.js";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { cx } from "../../cx.js";
|
|
2
|
-
import { fieldBox } from "../../components/fieldBox.js";
|
|
3
1
|
import { Field } from "../../components/Field.js";
|
|
2
|
+
import { ColorInput } from "../../components/ColorInput.js";
|
|
4
3
|
import { fieldFrame } from "../fieldFrame.js";
|
|
5
4
|
import { useFieldValue } from "../useFieldValue.js";
|
|
6
5
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -8,20 +7,17 @@ import { jsx } from "react/jsx-runtime";
|
|
|
8
7
|
function ColorField(props) {
|
|
9
8
|
const { field, disabled, onChange } = props;
|
|
10
9
|
const [value, change] = useFieldValue(field.name ?? "");
|
|
11
|
-
const handleChange = (e) => {
|
|
12
|
-
onChange?.(e.target.value);
|
|
13
|
-
change(e.target.value);
|
|
14
|
-
};
|
|
15
10
|
return /* @__PURE__ */ jsx(Field, {
|
|
16
11
|
...fieldFrame(props),
|
|
17
|
-
children: (control) => /* @__PURE__ */ jsx(
|
|
12
|
+
children: (control) => /* @__PURE__ */ jsx(ColorInput, {
|
|
18
13
|
...control,
|
|
19
|
-
className: cx(fieldBox(), "w-18 cursor-pointer p-1"),
|
|
20
14
|
name: field.name,
|
|
21
15
|
value: value || "#fbbf24",
|
|
22
|
-
type: "color",
|
|
23
16
|
disabled,
|
|
24
|
-
|
|
17
|
+
onValueChange: (color) => {
|
|
18
|
+
onChange?.(color);
|
|
19
|
+
change(color);
|
|
20
|
+
}
|
|
25
21
|
})
|
|
26
22
|
});
|
|
27
23
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Autocomplete, Combobox } from "../../components/Combobox.js";
|
|
2
1
|
import { Field } from "../../components/Field.js";
|
|
2
|
+
import { Autocomplete, Combobox } from "../../components/Combobox.js";
|
|
3
3
|
import { toFieldItems } from "../utils/fieldItems.js";
|
|
4
4
|
import { fieldFrame } from "../fieldFrame.js";
|
|
5
5
|
import { useChoiceField } from "../useChoiceField.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useStrings } from "../../components/Provider.js";
|
|
2
2
|
import { Checkbox } from "../../components/Checkbox.js";
|
|
3
|
-
import { fieldBox } from "../../components/fieldBox.js";
|
|
4
3
|
import { Field, FieldOption } from "../../components/Field.js";
|
|
4
|
+
import { fieldBox } from "../../components/fieldBox.js";
|
|
5
5
|
import { fieldFrame } from "../fieldFrame.js";
|
|
6
6
|
import { useFieldValue } from "../useFieldValue.js";
|
|
7
7
|
import { formatDurationText, parseDurationText } from "../utils/duration.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Combobox } from "../../components/Combobox.js";
|
|
2
1
|
import { Field } from "../../components/Field.js";
|
|
2
|
+
import { Combobox } from "../../components/Combobox.js";
|
|
3
3
|
import { toFieldItems } from "../utils/fieldItems.js";
|
|
4
4
|
import { fieldFrame } from "../fieldFrame.js";
|
|
5
5
|
import { useFieldValue } from "../useFieldValue.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cx } from "../../cx.js";
|
|
2
|
-
import { fieldBox } from "../../components/fieldBox.js";
|
|
3
2
|
import { Field, RevealToggle } from "../../components/Field.js";
|
|
3
|
+
import { fieldBox } from "../../components/fieldBox.js";
|
|
4
4
|
import { fieldFrame } from "../fieldFrame.js";
|
|
5
5
|
import { useFieldValue } from "../useFieldValue.js";
|
|
6
6
|
import { getPlaceholder } from "../utils/getPlaceholder.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cx } from "../../cx.js";
|
|
2
|
-
import { fieldBox } from "../../components/fieldBox.js";
|
|
3
2
|
import { Field } from "../../components/Field.js";
|
|
3
|
+
import { fieldBox } from "../../components/fieldBox.js";
|
|
4
4
|
import { fieldFrame } from "../fieldFrame.js";
|
|
5
5
|
import { useFieldValue } from "../useFieldValue.js";
|
|
6
6
|
import { getPlaceholder } from "../utils/getPlaceholder.js";
|
package/dist/form/Form.js
CHANGED
|
@@ -2,8 +2,8 @@ import { cx } from "../cx.js";
|
|
|
2
2
|
import { Loader } from "../components/Loader.js";
|
|
3
3
|
import { useStrings, useWorkflowEngine } from "../components/Provider.js";
|
|
4
4
|
import { Button } from "../components/Button.js";
|
|
5
|
-
import { InfoTip } from "../components/InfoTip.js";
|
|
6
5
|
import { FieldRowContext } from "../components/Field.js";
|
|
6
|
+
import { InfoTip } from "../components/InfoTip.js";
|
|
7
7
|
import { Section } from "../components/Section.js";
|
|
8
8
|
import { parseWizardConfig } from "./Wizard/utils.js";
|
|
9
9
|
import { inputWidth, resolvedFlag } from "./lib.js";
|