@parallelworks/ui 0.59.0 → 0.60.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.
@@ -9,6 +9,8 @@ export interface ColorInputProps {
9
9
  label?: ReactNode;
10
10
  /** Beside the label and outside it, so it stays out of the field's name. */
11
11
  hint?: ReactNode;
12
+ /** Buttons at the far end of the label's row that act on the color, as Field's `actions`. */
13
+ actions?: ReactNode;
12
14
  /** Help under the field. */
13
15
  description?: ReactNode;
14
16
  error?: ReactNode;
@@ -33,4 +35,4 @@ export interface ColorInputProps {
33
35
  * in. A half-typed color stays in the field, marked invalid, until it parses; leaving the field
34
36
  * puts back the last color that did.
35
37
  */
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;
38
+ export declare function ColorInput({ value, onValueChange, label, hint, actions, 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;
@@ -14,7 +14,7 @@ var swatchClassName = cx("shrink-0 cursor-pointer p-0.5", "[&::-webkit-color-swa
14
14
  * in. A half-typed color stays in the field, marked invalid, until it parses; leaving the field
15
15
  * puts back the last color that did.
16
16
  */
17
- 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
+ function ColorInput({ value, onValueChange, label, hint, actions, description, error, size = "md", id, name, disabled, required, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, className }) {
18
18
  const t = useStrings().colorInput;
19
19
  const [draft, setDraft] = useState(null);
20
20
  const errorId = useId();
@@ -25,6 +25,7 @@ function ColorInput({ value, onValueChange, label, hint, description, error, siz
25
25
  return framed({
26
26
  label,
27
27
  hint,
28
+ actions,
28
29
  description,
29
30
  error: error ?? (unparsed ? t.invalid : void 0),
30
31
  size
@@ -22,7 +22,9 @@ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElemen
22
22
  ref?: Ref<HTMLTextAreaElement> | undefined;
23
23
  }
24
24
  /** The Field around a control when it has a label; the control alone when it doesn't. */
25
- export declare function framed({ label, hint, help, description, error, size }: Omit<FramedProps, 'mono'>, { id, required, disabled, }: {
25
+ export declare function framed({ label, hint, help, actions, description, error, size, }: Omit<FramedProps, 'mono'> & {
26
+ actions?: ReactNode;
27
+ }, { id, required, disabled, }: {
26
28
  id?: string | undefined;
27
29
  required?: boolean | undefined;
28
30
  disabled?: boolean | undefined;
@@ -6,12 +6,13 @@ import { useState } from "react";
6
6
  import { Input } from "@base-ui/react/input";
7
7
  //#region src/components/Input.tsx
8
8
  /** The Field around a control when it has a label; the control alone when it doesn't. */
9
- function framed({ label, hint, help, description, error, size }, { id, required, disabled }, control) {
9
+ function framed({ label, hint, help, actions, description, error, size }, { id, required, disabled }, control) {
10
10
  if (label === void 0) return control(error ? { "aria-invalid": true } : {});
11
11
  return /* @__PURE__ */ jsx(Field, {
12
12
  label,
13
13
  hint,
14
14
  help,
15
+ actions,
15
16
  description,
16
17
  error,
17
18
  required,
@@ -153,6 +153,8 @@ export interface UIStrings {
153
153
  /** The background a theme is generated from. */
154
154
  base: string;
155
155
  accent: string;
156
+ /** Exchanges the background and the accent. */
157
+ swap: string;
156
158
  contrast: string;
157
159
  /** Under the contrast slider. */
158
160
  contrastHelp: string;
@@ -153,6 +153,7 @@ var DEFAULTS = {
153
153
  presets: "Presets",
154
154
  base: "Background",
155
155
  accent: "Accent",
156
+ swap: "Swap background and accent",
156
157
  contrast: "Contrast",
157
158
  contrastHelp: "Separates surfaces and strengthens text. Text always meets WCAG AA.",
158
159
  customSidebar: "Custom sidebar",
@@ -1,7 +1,9 @@
1
1
  import { cx } from "../cx.js";
2
+ import { SwapIcon } from "../icons.js";
2
3
  import { useStrings } from "./Provider.js";
3
4
  import { useCopy } from "./useCopy.js";
4
5
  import { Button } from "./Button.js";
6
+ import { IconButton } from "./IconButton.js";
5
7
  import { fieldBox } from "./fieldBox.js";
6
8
  import { parseColor, toHex } from "../theme/color.js";
7
9
  import { CODE_STYLES, DEFAULT_THEME, THEME_PRESETS, modeOf, parseTheme, schemeFor, serializeTheme } from "../theme/model.js";
@@ -110,6 +112,14 @@ function ThemeEditor({ value, onChange, mode, onModeChange, preference, onPrefer
110
112
  }),
111
113
  /* @__PURE__ */ jsx(ColorInput, {
112
114
  label: t.accent,
115
+ actions: /* @__PURE__ */ jsx(IconButton, {
116
+ icon: /* @__PURE__ */ jsx(SwapIcon, { className: "rotate-90" }),
117
+ label: t.swap,
118
+ onClick: () => setSeed({
119
+ base: seed.accent,
120
+ accent: seed.base
121
+ })
122
+ }),
113
123
  value: seed.accent,
114
124
  onValueChange: (accent) => setSeed({ accent })
115
125
  }),
@@ -402,6 +402,10 @@ function build(scheme, options) {
402
402
  }
403
403
  const sidebarBase = scheme.sidebar?.base ? oklch(scheme.sidebar.base) : neutral(dark ? Math.max(.08, base.l - .035 * s) : base.l - .035 * s);
404
404
  const sidebarAccent = scheme.sidebar?.accent ? oklch(scheme.sidebar.accent) : accent;
405
+ const activeTint = sidebarAccent.c < .02 ? sidebarBase : {
406
+ ...sidebarAccent,
407
+ c: Math.min(sidebarAccent.c, .08)
408
+ };
405
409
  const sidebarDark = isDarkColor(toHex(sidebarBase));
406
410
  const sidebarDir = sidebarDark ? 1 : -1;
407
411
  set("--theme-sidebar-bg", sidebarBase);
@@ -409,17 +413,18 @@ function build(scheme, options) {
409
413
  ...sidebarBase,
410
414
  l: sidebarBase.l + sidebarDir * (sidebarDark ? .08 : .05) * s
411
415
  });
412
- const activeAt = (f) => ({
413
- l: sidebarBase.l + sidebarDir * (sidebarDark ? .12 : .07) * s * f,
414
- c: Math.min(sidebarAccent.c, .08),
415
- h: sidebarAccent.h
416
+ const activeAt = (dir) => (f) => ({
417
+ l: sidebarBase.l + dir * (sidebarDark ? .12 : .07) * s * f,
418
+ c: activeTint.c,
419
+ h: activeTint.h
416
420
  });
417
- set("--theme-sidebar-active-bg", activeAt(roomFor(activeAt, t.primary)));
421
+ const [active, room] = [activeAt(sidebarDir), activeAt(-sidebarDir)].map((fill) => [fill, roomFor(fill, t.primary)]).reduce((a, b) => b[1] > a[1] ? b : a);
422
+ set("--theme-sidebar-active-bg", active(room));
418
423
  solved("--theme-sidebar-text", inkFor(sidebarBase, sidebarBase, sidebarDark), ["--theme-sidebar-bg", "--theme-sidebar-hover"], t.primary);
419
424
  solved("--theme-sidebar-active-text", {
420
425
  l: sidebarBase.l,
421
- c: Math.min(sidebarAccent.c, .04),
422
- h: sidebarAccent.h
426
+ c: Math.min(activeTint.c, .04),
427
+ h: activeTint.h
423
428
  }, ["--theme-sidebar-active-bg"], t.primary);
424
429
  const style = options.code ?? "balanced";
425
430
  set("--theme-code-bg", mutedPanel);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parallelworks/ui",
3
- "version": "0.59.0",
3
+ "version": "0.60.0",
4
4
  "description": "React UI components: a theme contract, icons, brand logos, primitives, lists, forms, a job graph, a code editor, a log viewer, a file explorer and an AI chat, each on its own subpath",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {