@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.
Files changed (56) hide show
  1. package/README.md +17 -2
  2. package/dist/ai/components/ChatInput.js +1 -1
  3. package/dist/components/ColorInput.d.ts +36 -0
  4. package/dist/components/ColorInput.js +82 -0
  5. package/dist/components/Combobox.d.ts +5 -2
  6. package/dist/components/Combobox.js +12 -11
  7. package/dist/components/CreateModal.js +2 -2
  8. package/dist/components/DatePicker.d.ts +5 -1
  9. package/dist/components/DatePicker.js +7 -7
  10. package/dist/components/Field.d.ts +19 -5
  11. package/dist/components/Field.js +21 -4
  12. package/dist/components/Input.d.ts +11 -1
  13. package/dist/components/Input.js +9 -6
  14. package/dist/components/MenuEntries.js +1 -1
  15. package/dist/components/NumberInput.d.ts +1 -0
  16. package/dist/components/OneTimeCodeInput.d.ts +23 -0
  17. package/dist/components/OneTimeCodeInput.js +39 -0
  18. package/dist/components/Provider.d.ts +12 -3
  19. package/dist/components/Provider.js +14 -2
  20. package/dist/components/SearchInput.d.ts +1 -0
  21. package/dist/components/Section.d.ts +7 -2
  22. package/dist/components/Section.js +58 -39
  23. package/dist/components/SegmentedControl.d.ts +4 -2
  24. package/dist/components/SegmentedControl.js +4 -2
  25. package/dist/components/Select.d.ts +2 -1
  26. package/dist/components/Select.js +1 -1
  27. package/dist/components/Tabs.js +1 -0
  28. package/dist/components/ThemeEditor.js +15 -52
  29. package/dist/components/ToggleGroup.d.ts +42 -0
  30. package/dist/components/ToggleGroup.js +69 -0
  31. package/dist/components/fieldBox.js +1 -1
  32. package/dist/form/Fields/BooleanField.js +1 -1
  33. package/dist/form/Fields/ColorField.js +6 -10
  34. package/dist/form/Fields/DropdownField.js +1 -1
  35. package/dist/form/Fields/DurationField.js +1 -1
  36. package/dist/form/Fields/MultiDropdownField.js +1 -1
  37. package/dist/form/Fields/StringField.js +1 -1
  38. package/dist/form/Fields/TextAreaField.js +1 -1
  39. package/dist/form/Form.js +1 -1
  40. package/dist/graph/GraphEditorDialogs.js +1 -1
  41. package/dist/graph/InputDialog.js +2 -2
  42. package/dist/graph/WorkflowSettingsDialog.js +1 -1
  43. package/dist/graph/editorFields.js +2 -2
  44. package/dist/graph/inputRefs.js +1 -1
  45. package/dist/graph/stepWith.js +1 -1
  46. package/dist/icons.d.ts +17 -1
  47. package/dist/icons.js +19 -3
  48. package/dist/index.d.ts +5 -1
  49. package/dist/index.js +14 -10
  50. package/dist/list/ListViewControls.js +1 -1
  51. package/dist/list/listSearch.js +1 -1
  52. package/dist/styles/theme.css +1 -1
  53. package/dist/styles.css +1 -1
  54. package/dist/theme/derive.d.ts +3 -0
  55. package/dist/theme/derive.js +11 -7
  56. 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 { ReactNode } from 'react';
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: "group/section panel mb-1 flex w-full flex-col"
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-2 py-3"
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 text-(--theme-muted-text-color) group-data-open/section:text-inherit"
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 end = actions && /* @__PURE__ */ jsx("div", {
26
- className: "ml-auto flex items-center gap-2",
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__ */ jsxs("div", {
34
- className: headerClasses[variant],
35
- children: [
36
- /* @__PURE__ */ jsx(Heading, {
37
- ...titleProps,
38
- className: cx("flex items-center", titleClasses[variant]),
39
- children: title
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__ */ jsxs("div", {
52
- className: headerClasses[variant],
53
- children: [
54
- /* @__PURE__ */ jsx(Heading, {
55
- className: cx("flex min-w-0", fill && "flex-1"),
56
- children: /* @__PURE__ */ jsxs(Collapsible.Trigger, {
57
- ...titleProps,
58
- className: cx("group flex min-w-0 cursor-pointer items-center gap-1 text-left", fill && "w-full", titleClasses[variant]),
59
- children: [/* @__PURE__ */ jsx(ChevronRightIcon, {
60
- "aria-hidden": "true",
61
- className: cx("shrink-0 transition-transform group-data-panel-open:rotate-90 motion-reduce:transition-none", "px-1 text-[1.25rem]")
62
- }), title]
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. Name it with
14
- * `label`, or leave that out inside a `Field group`, whose legend names it. */
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. Name it with
10
- * `label`, or leave that out inside a `Field group`, whose legend names it. */
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?: 'sm' | 'md' | undefined;
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-muted-text-color)",
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)",
@@ -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(ColorField, {
105
+ /* @__PURE__ */ jsx(ColorInput, {
105
106
  label: t.base,
106
107
  value: seed.base,
107
- onChange: (base) => setSeed({ base })
108
+ onValueChange: (base) => setSeed({ base })
108
109
  }),
109
- /* @__PURE__ */ jsx(ColorField, {
110
+ /* @__PURE__ */ jsx(ColorInput, {
110
111
  label: t.accent,
111
112
  value: seed.accent,
112
- onChange: (accent) => setSeed({ accent })
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(ColorField, {
145
+ children: [/* @__PURE__ */ jsx(ColorInput, {
145
146
  label: t.sidebarBase,
146
147
  value: scheme.sidebar.base ?? derived["--theme-sidebar-bg"],
147
- onChange: (base) => update({
148
+ onValueChange: (base) => update({
148
149
  ...scheme,
149
150
  sidebar: {
150
151
  ...scheme.sidebar,
151
152
  base
152
153
  }
153
154
  })
154
- }), /* @__PURE__ */ jsx(ColorField, {
155
+ }), /* @__PURE__ */ jsx(ColorInput, {
155
156
  label: t.sidebarAccent,
156
157
  value: scheme.sidebar.accent ?? seed.accent,
157
- onChange: (accent) => update({
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-muted-text-color)";
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("input", {
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
- onChange: handleChange
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";