Skip to content

Value types

A value type describes how one kind of value is displayed and edited. The shell ships: text, multiline, email, number, boolean, color, enum, icon, status, date, weekday, month, month-year and duration.

icon lists every registered icon live — shell icons, graph silhouettes and icons plugins define via ctx.ui.define({ icons }) — with a glyph preview. Config: { allowEmpty?: boolean, allowNone?: boolean, defaultLabel?: string, kinds?: ["icon" | "silhouette"] }. allowNone adds a “None” choice stored as "none", meaning “no icon at all” (the empty value keeps meaning “owner default”). Money and quantity are intentionally left to plugins, since they need real currency / unit data.

color opens a swatch picker (curated “status” and “category” tone groups, a hex field and the native picker). The value is always stored as a plain #rrggbb string. A plugin may rename or replace the groups per field with config: { groups?: ColorSwatchGroup[] } ({ id, title, swatches: [{ value, name }] }; title and name are i18n keys), e.g. a supply-chain plugin can show “Risk” / “Flow domain” instead of the defaults:

{ "key": "color", "type": "color", "label": "scflow.color",
"config": { "groups": [
{ "id": "risk", "title": "scflow.colors.risk",
"swatches": [{ "value": "#dc2626", "name": "scflow.colors.high" }] }
] } }
ctx.registerValueType<Priority, void>({
id: "priority", // becomes "myplugin:priority"
name: ctx.i18n.key("type.priority"),
description: ctx.i18n.key("type.priorityHint"),
format: (value, { size, t }) =>
size === "short" ? t(`priority.${value}.short`) : t(`priority.${value}.long`),
parse: (raw) => raw as Priority,
Editor: PriorityEditor, // optional; a text field is used otherwise
Display: PriorityBadge, // optional; `format` is used otherwise
});
  • format(value, context) gets size ("short" for lists, badges and toolbars, "detailed" for cards and forms), the active locale, t, formatDate and formatNumber.
  • Editor never writes state: onChange reports the draft, onCommit the validated value. The caller emits the event.
  • A surface can render a reactive typed value without a plugin component via panel.value: provide its type, a value selector, and optionally hideWhenFalsy, size, config, or className.

Use them anywhere in your own UI:

import { ValueDisplay, ValueEditor, GeneratedForm } from "@/kernel";
<ValueDisplay type="duration" value={{ value: 2, unit: "quarter" }} />
<ValueEditor type="date" value={day} onChange={setDraft} onCommit={commit} />
<GeneratedForm fields={fields} values={values} onChange={emitChange} />

duration values are { value: number, unit } with unit in minute, hour, day, week, month, quarter, year; toMinutes and compareDurations keep durations in different units comparable.

A setting’s type may be any registered value type id, with an optional config:

ctx.defineSettings({
title: ctx.i18n.key("settings.title"),
settings: [
{ key: "reviewEvery", type: "duration", label: ctx.i18n.key("settings.every"),
default: { value: 1, unit: "month" } },
],
});

Documentation under CC BY 4.0 · © SCFlow contributors ·Back to supplychainflow.org