@invana/canvas-ui 0.0.1

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.
@@ -0,0 +1,184 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { FieldConfig } from '@invana/forms';
3
+ import { NodeStyle } from '@invana/graph';
4
+
5
+ /** Shape kinds the v1 geometry tab exposes. */
6
+ type ShapeKind = NonNullable<NodeStyle['shape']>['kind'];
7
+ type StrokeAlignment = NonNullable<NodeStyle['bgStrokeAlignment']>;
8
+ type StrokeCap = NonNullable<NodeStyle['bgStrokeCap']>;
9
+ type StrokeJoin = NonNullable<NodeStyle['bgStrokeJoin']>;
10
+ type LabelPlacement = NonNullable<NodeStyle['labelPlacement']>;
11
+ /** Strip `readonly` (NodeStyle's fields are readonly) so the form holds a
12
+ * plain mutable value object. Homomorphic — preserves optionality. */
13
+ type Mutable<T> = {
14
+ -readonly [K in keyof T]: T[K];
15
+ };
16
+ /**
17
+ * NodeStyle fields the form takes **verbatim** — same name, same type. Derived
18
+ * from {@link NodeStyle} via `Pick`, so their types track the engine and a
19
+ * renamed/removed field surfaces here as a compile error (no silent drift).
20
+ * Add a scalar passthrough control = add its key here.
21
+ */
22
+ type NodeStylePassthroughFields = Mutable<Pick<NodeStyle, 'size' | 'bgAlpha' | 'bgStrokeAlpha' | 'bgStrokeWidth' | 'bgStrokeAlignment' | 'bgStrokeCap' | 'bgStrokeJoin' | 'labelText' | 'labelFontSize' | 'labelPlacement' | 'labelOffsetX' | 'labelOffsetY'>>;
23
+ /**
24
+ * Fields whose form encoding deliberately **differs** from {@link NodeStyle},
25
+ * so they can't be `Pick`ed — `mapping.ts` converts them:
26
+ * - `shape` discriminated union → `shapeKind` select + per-kind geometry numbers,
27
+ * - colours → hex strings (the design-kit swatch's encoding) not `0xRRGGBB`,
28
+ * - the `[dash, gap]` tuple → two number fields,
29
+ * - `labelFontWeight` narrowed to `number` (NodeStyle allows `number | string`).
30
+ *
31
+ * Drift on the *source* fields is still caught: `styleToForm` reads
32
+ * `style.shape` / `style.bgFill` / `style.bgStrokeDashArray` / … directly, so a
33
+ * rename in `NodeStyle` breaks `mapping.ts` at compile time.
34
+ */
35
+ interface NodeStyleEncodedFields {
36
+ shapeKind?: ShapeKind;
37
+ radius?: number;
38
+ width?: number;
39
+ height?: number;
40
+ cornerRadius?: number;
41
+ sides?: number;
42
+ points?: number;
43
+ innerRadius?: number;
44
+ outerRadius?: number;
45
+ bgFill?: string;
46
+ bgStrokeColor?: string;
47
+ labelColor?: string;
48
+ bgStrokeDashLength?: number;
49
+ bgStrokeDashGap?: number;
50
+ labelFontWeight?: number;
51
+ }
52
+ /**
53
+ * Flat form-field shape the `@invana/forms` generator renders. The passthrough
54
+ * half is **derived from {@link NodeStyle}**; the rest is re-encoded for scalar
55
+ * inputs (see {@link NodeStyleEncodedFields}). `styleToForm` / `formToStyle`
56
+ * (`mapping.ts`) round-trip between this and `Partial<NodeStyle>`.
57
+ */
58
+ type NodeStyleFields = NodeStylePassthroughFields & NodeStyleEncodedFields;
59
+ /**
60
+ * react-hook-form state shape. `<ObjectField name="style" …>` registers each
61
+ * leaf under `style.<field>`, so the form's values nest under a `style` key.
62
+ * This is the type the consumer parameterises its `useForm` with.
63
+ */
64
+ interface NodeStyleFormState {
65
+ style: NodeStyleFields;
66
+ }
67
+
68
+ interface NodeStyleEditorProps {
69
+ /**
70
+ * Initial field values, loaded into the form once on mount. Same shape the
71
+ * form produces (see {@link NodeStyleFields}); seed it from an engine style
72
+ * with the exported `styleToForm`. Remount (via `key`) to reload.
73
+ */
74
+ defaults?: NodeStyleFields;
75
+ /**
76
+ * The form schema. Either a static `FieldConfig[]` or a function of the
77
+ * current values — the function form lets fields react to other fields (the
78
+ * built-in default varies the geometry inputs with `shapeKind`). Defaults to
79
+ * the built-in grouped NodeStyle field set ({@link nodeStyleFields}).
80
+ */
81
+ fields?: FieldConfig[] | ((values: NodeStyleFields) => FieldConfig[]);
82
+ /**
83
+ * Called with the current values when the user submits. This is where the
84
+ * consumer puts its logic — map back to a style with `formToStyle` and apply
85
+ * it wherever (a node, many nodes, an undo stack, …). The component itself
86
+ * does none of that.
87
+ */
88
+ onSubmit: (values: NodeStyleFields) => void;
89
+ /** Submit button label. Default `'Apply'`. */
90
+ submitLabel?: string;
91
+ }
92
+ /**
93
+ * Self-contained, engine-agnostic style form.
94
+ *
95
+ * Takes `defaults` + `fields`, owns a react-hook-form instance, renders the
96
+ * schema with `@invana/forms` (each field `group` becomes an accordion
97
+ * section), and on submit hands the current values to `onSubmit`. It knows
98
+ * nothing about `Canvas`, layers, or how a style is stored — it just loads the
99
+ * defaults and tracks the user's edits in the shape the `fields` define.
100
+ *
101
+ * The NodeStyle ⇄ form-fields mapping (`styleToForm` / `formToStyle`) is the
102
+ * consumer's plug-in, used in `defaults` and inside `onSubmit`.
103
+ */
104
+ declare function NodeStyleEditor({ defaults, fields, onSubmit, submitLabel, }: NodeStyleEditorProps): react_jsx_runtime.JSX.Element;
105
+
106
+ /**
107
+ * Geometry-tab fields for the current shape kind. Dynamic: the per-kind
108
+ * numerics (radius / width-height / sides / points…) change with the watched
109
+ * `shapeKind`, which is how the form-generator handles the discriminated
110
+ * union. Falls back to no geometry numerics until a kind is chosen.
111
+ */
112
+ declare function geometryFields(kind: ShapeKind | undefined): FieldConfig[];
113
+ declare const BACKGROUND_FIELDS: FieldConfig[];
114
+ declare const STROKE_FIELDS: FieldConfig[];
115
+ declare const LABEL_FIELDS: FieldConfig[];
116
+ /**
117
+ * The full NodeStyle field set as one grouped `FieldConfig[]` — the default
118
+ * `fields` for `<NodeStyleEditor>`. `@invana/forms` renders each `group` as an
119
+ * accordion section. Geometry numerics vary with the current `shapeKind`
120
+ * (the discriminated union), so this is a function of the live values.
121
+ */
122
+ declare function nodeStyleFields(values?: NodeStyleFields): FieldConfig[];
123
+
124
+ /**
125
+ * Construct a fresh shape spec with sane defaults for a given kind. Used when
126
+ * the user switches `shapeKind` and there's no seeded geometry to preserve.
127
+ */
128
+ declare function defaultShapeFor(kind: ShapeKind): NonNullable<NodeStyle['shape']>;
129
+ /**
130
+ * Map an engine `Partial<NodeStyle>` (e.g. the result of
131
+ * `layer.resolveNodeStyle(node)`) to the flat, string-colour
132
+ * {@link NodeStyleFields} the `@invana/forms` generator renders.
133
+ *
134
+ * Extracts only the literal fields the editor handles, guarding non-scalar
135
+ * values (image / glyph fills, string font weights) so they round-trip as
136
+ * `undefined` rather than corrupting a field. The `shape` discriminated union
137
+ * is flattened to `shapeKind` + the geometry numbers of that kind; the dash
138
+ * tuple is split; colours become hex strings.
139
+ */
140
+ declare function styleToForm(style: Partial<NodeStyle>): NodeStyleFields;
141
+ /**
142
+ * Map the flat {@link NodeStyleFields} the form holds back to an engine
143
+ * `Partial<NodeStyle>` — inverse of {@link styleToForm}. Only fields the form
144
+ * actually set are included (no `undefined` keys), so the result is safe to
145
+ * spread over an existing style on commit:
146
+ * `store.updateNode(id, { style: { ...resolveNodeStyle(node), ...formToStyle(fields) } })`.
147
+ */
148
+ declare function formToStyle(f: NodeStyleFields): Partial<NodeStyle>;
149
+
150
+ /**
151
+ * Shared colour palette offered as swatch presets on every colour field across
152
+ * the editors (node fill / stroke / label, and the upcoming edge / canvas
153
+ * surfaces). Hex strings so a seeded `#rrggbb` value highlights its matching
154
+ * preset. A specific editor can pass its own palette instead where it matters.
155
+ */
156
+ declare const COLOR_PRESETS: readonly [{
157
+ readonly label: "Slate";
158
+ readonly value: "#9ca3af";
159
+ }, {
160
+ readonly label: "Red";
161
+ readonly value: "#ef4444";
162
+ }, {
163
+ readonly label: "Amber";
164
+ readonly value: "#f59e0b";
165
+ }, {
166
+ readonly label: "Emerald";
167
+ readonly value: "#10b981";
168
+ }, {
169
+ readonly label: "Blue";
170
+ readonly value: "#3b82f6";
171
+ }, {
172
+ readonly label: "Violet";
173
+ readonly value: "#8b5cf6";
174
+ }];
175
+
176
+ /**
177
+ * Colour conversion helpers. `NodeStyle` (and the rest of the engine) stores
178
+ * colours as 24-bit RGB numbers (`0xRRGGBB`); HTML `<input type="color">` and
179
+ * the design-kit colour swatch use `#rrggbb` strings. These bridge the two.
180
+ */
181
+ declare function numberToHex(n: number | undefined): string;
182
+ declare function hexToNumber(hex: string): number;
183
+
184
+ export { BACKGROUND_FIELDS, COLOR_PRESETS, LABEL_FIELDS, type LabelPlacement, NodeStyleEditor, type NodeStyleEditorProps, type NodeStyleFields, type NodeStyleFormState, STROKE_FIELDS, type ShapeKind, type StrokeAlignment, type StrokeCap, type StrokeJoin, defaultShapeFor, formToStyle, geometryFields, hexToNumber, nodeStyleFields, numberToHex, styleToForm };
package/dist/index.js ADDED
@@ -0,0 +1,295 @@
1
+ import { FormField } from '@invana/forms';
2
+ import { Button } from '@invana/ui';
3
+ import { useForm, useWatch, FormProvider } from 'react-hook-form';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+
6
+ // src/editors/node-style/NodeStyleEditor.tsx
7
+
8
+ // src/presets/colors.ts
9
+ var COLOR_PRESETS = [
10
+ { label: "Slate", value: "#9ca3af" },
11
+ { label: "Red", value: "#ef4444" },
12
+ { label: "Amber", value: "#f59e0b" },
13
+ { label: "Emerald", value: "#10b981" },
14
+ { label: "Blue", value: "#3b82f6" },
15
+ { label: "Violet", value: "#8b5cf6" }
16
+ ];
17
+
18
+ // src/editors/node-style/fields.ts
19
+ var SHAPE_KIND_FIELD = {
20
+ name: "shapeKind",
21
+ type: "select",
22
+ label: "Shape",
23
+ options: [
24
+ { value: "circle", label: "Circle" },
25
+ { value: "rect", label: "Rectangle" },
26
+ { value: "regular-polygon", label: "Regular polygon" },
27
+ { value: "star", label: "Star" }
28
+ ]
29
+ };
30
+ var SIZE_FIELD = {
31
+ name: "size",
32
+ type: "number",
33
+ label: "Size",
34
+ min: 0,
35
+ max: 200,
36
+ step: 1,
37
+ description: "Unified radius / half-extent. Overrides the shape's native size axis."
38
+ };
39
+ var GEOMETRY_BY_KIND = {
40
+ circle: [{ name: "radius", type: "number", label: "Radius", min: 0, max: 200, step: 1 }],
41
+ rect: [
42
+ { name: "width", type: "number", label: "Width", min: 0, max: 400, step: 1 },
43
+ { name: "height", type: "number", label: "Height", min: 0, max: 400, step: 1 },
44
+ { name: "cornerRadius", type: "number", label: "Corner radius", min: 0, max: 200, step: 1 }
45
+ ],
46
+ "regular-polygon": [
47
+ { name: "sides", type: "number", label: "Sides", min: 3, max: 20, step: 1 },
48
+ { name: "radius", type: "number", label: "Radius", min: 0, max: 200, step: 1 }
49
+ ],
50
+ star: [
51
+ { name: "points", type: "number", label: "Points", min: 3, max: 20, step: 1 },
52
+ { name: "innerRadius", type: "number", label: "Inner radius", min: 0, max: 200, step: 1 },
53
+ { name: "outerRadius", type: "number", label: "Outer radius", min: 0, max: 200, step: 1 }
54
+ ]
55
+ };
56
+ function geometryFields(kind) {
57
+ const geometry = kind ? GEOMETRY_BY_KIND[kind] ?? [] : [];
58
+ return [SHAPE_KIND_FIELD, ...geometry, SIZE_FIELD];
59
+ }
60
+ var BACKGROUND_FIELDS = [
61
+ {
62
+ name: "bgFill",
63
+ type: "color",
64
+ label: "Fill color",
65
+ presetColors: [...COLOR_PRESETS],
66
+ description: "Solid color. Use the engine API directly for stacked / image / glyph fills."
67
+ },
68
+ { name: "bgAlpha", type: "number", label: "Fill alpha", min: 0, max: 1, step: 0.01 }
69
+ ];
70
+ var STROKE_FIELDS = [
71
+ { name: "bgStrokeColor", type: "color", label: "Stroke color", presetColors: [...COLOR_PRESETS] },
72
+ { name: "bgStrokeAlpha", type: "number", label: "Stroke alpha", min: 0, max: 1, step: 0.01 },
73
+ { name: "bgStrokeWidth", type: "number", label: "Stroke width", min: 0, max: 50, step: 0.5 },
74
+ {
75
+ name: "bgStrokeAlignment",
76
+ type: "select",
77
+ label: "Stroke alignment",
78
+ options: [
79
+ { value: "inside", label: "Inside" },
80
+ { value: "center", label: "Center" },
81
+ { value: "outside", label: "Outside" }
82
+ ]
83
+ },
84
+ {
85
+ name: "bgStrokeDashLength",
86
+ type: "number",
87
+ label: "Dash length",
88
+ min: 0,
89
+ max: 50,
90
+ step: 1,
91
+ description: "Leave dash + gap at 0 for a solid stroke."
92
+ },
93
+ { name: "bgStrokeDashGap", type: "number", label: "Dash gap", min: 0, max: 50, step: 1 },
94
+ {
95
+ name: "bgStrokeCap",
96
+ type: "select",
97
+ label: "Cap",
98
+ options: [
99
+ { value: "butt", label: "Butt" },
100
+ { value: "round", label: "Round" },
101
+ { value: "square", label: "Square" }
102
+ ]
103
+ },
104
+ {
105
+ name: "bgStrokeJoin",
106
+ type: "select",
107
+ label: "Join",
108
+ options: [
109
+ { value: "miter", label: "Miter" },
110
+ { value: "round", label: "Round" },
111
+ { value: "bevel", label: "Bevel" }
112
+ ]
113
+ }
114
+ ];
115
+ var LABEL_FIELDS = [
116
+ { name: "labelText", type: "text", label: "Text", placeholder: "(uses node id / data field)" },
117
+ { name: "labelColor", type: "color", label: "Color", presetColors: [...COLOR_PRESETS] },
118
+ { name: "labelFontSize", type: "number", label: "Font size", min: 1, max: 120, step: 1 },
119
+ { name: "labelFontWeight", type: "number", label: "Font weight", min: 100, max: 900, step: 100 },
120
+ {
121
+ name: "labelPlacement",
122
+ type: "select",
123
+ label: "Placement",
124
+ description: "inside-* placements clip / truncate to fit the shape.",
125
+ options: [
126
+ { value: "center", label: "Center (anchor)" },
127
+ { value: "top", label: "Top" },
128
+ { value: "bottom", label: "Bottom" },
129
+ { value: "left", label: "Left" },
130
+ { value: "right", label: "Right" },
131
+ { value: "top-left", label: "Top-left" },
132
+ { value: "top-right", label: "Top-right" },
133
+ { value: "bottom-left", label: "Bottom-left" },
134
+ { value: "bottom-right", label: "Bottom-right" },
135
+ { value: "inside-center", label: "Inside center (contained)" },
136
+ { value: "inside-top", label: "Inside top" },
137
+ { value: "inside-bottom", label: "Inside bottom" },
138
+ { value: "inside-left", label: "Inside left" },
139
+ { value: "inside-right", label: "Inside right" }
140
+ ]
141
+ },
142
+ { name: "labelOffsetX", type: "number", label: "Offset X", min: -200, max: 200, step: 1 },
143
+ { name: "labelOffsetY", type: "number", label: "Offset Y", min: -200, max: 200, step: 1 }
144
+ ];
145
+ var withGroup = (group) => (f) => ({ ...f, group });
146
+ function nodeStyleFields(values = {}) {
147
+ return [
148
+ ...geometryFields(values.shapeKind).map(withGroup("Geometry")),
149
+ ...BACKGROUND_FIELDS.map(withGroup("Background")),
150
+ ...STROKE_FIELDS.map(withGroup("Stroke")),
151
+ ...LABEL_FIELDS.map(withGroup("Label"))
152
+ ];
153
+ }
154
+ function NodeStyleEditor({
155
+ defaults = {},
156
+ fields = nodeStyleFields,
157
+ onSubmit,
158
+ submitLabel = "Apply"
159
+ }) {
160
+ const form = useForm({ defaultValues: { style: defaults } });
161
+ const { control, getValues } = form;
162
+ const values = useWatch({ control, name: "style" });
163
+ const resolvedFields = typeof fields === "function" ? fields(values ?? {}) : fields;
164
+ const c = control;
165
+ return (
166
+ // `@invana/forms` leaf fields read `useFormContext()`, so the whole form
167
+ // must be on context — not just control.
168
+ /* @__PURE__ */ jsx(FormProvider, { ...form, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12, padding: 16 }, children: [
169
+ /* @__PURE__ */ jsx(FormField.ObjectField, { control: c, name: "style", fields: resolvedFields }),
170
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", justifyContent: "flex-end" }, children: /* @__PURE__ */ jsx(Button, { onClick: () => onSubmit(getValues("style")), children: submitLabel }) })
171
+ ] }) })
172
+ );
173
+ }
174
+
175
+ // src/utils/color.ts
176
+ function numberToHex(n) {
177
+ if (n === void 0 || Number.isNaN(n)) return "#000000";
178
+ const clamped = Math.max(0, Math.min(16777215, Math.floor(n)));
179
+ return "#" + clamped.toString(16).padStart(6, "0");
180
+ }
181
+ function hexToNumber(hex) {
182
+ const stripped = hex.startsWith("#") ? hex.slice(1) : hex;
183
+ const parsed = Number.parseInt(stripped, 16);
184
+ return Number.isNaN(parsed) ? 0 : parsed;
185
+ }
186
+
187
+ // src/editors/node-style/mapping.ts
188
+ function defaultShapeFor(kind) {
189
+ switch (kind) {
190
+ case "rect":
191
+ return { kind: "rect", width: 24, height: 24 };
192
+ case "regular-polygon":
193
+ return { kind: "regular-polygon", sides: 6, radius: 12 };
194
+ case "star":
195
+ return { kind: "star", points: 5, innerRadius: 6, outerRadius: 12 };
196
+ case "circle":
197
+ default:
198
+ return { kind: "circle", radius: 12 };
199
+ }
200
+ }
201
+ function colorToHex(v) {
202
+ return typeof v === "number" ? numberToHex(v) : void 0;
203
+ }
204
+ function hexToColor(s) {
205
+ return s && s.length > 0 ? hexToNumber(s) : void 0;
206
+ }
207
+ function styleToForm(style) {
208
+ const s = style.shape;
209
+ const dash = style.bgStrokeDashArray;
210
+ return {
211
+ shapeKind: s?.kind,
212
+ radius: s && "radius" in s ? s.radius : void 0,
213
+ // circle, regular-polygon
214
+ width: s && "width" in s ? s.width : void 0,
215
+ height: s && "height" in s ? s.height : void 0,
216
+ cornerRadius: s && "cornerRadius" in s ? s.cornerRadius : void 0,
217
+ sides: s && "sides" in s ? s.sides : void 0,
218
+ points: s && "points" in s ? s.points : void 0,
219
+ innerRadius: s && "innerRadius" in s ? s.innerRadius : void 0,
220
+ outerRadius: s && "outerRadius" in s ? s.outerRadius : void 0,
221
+ size: style.size,
222
+ bgFill: colorToHex(style.bgFill),
223
+ bgAlpha: style.bgAlpha,
224
+ bgStrokeColor: colorToHex(style.bgStrokeColor),
225
+ bgStrokeAlpha: style.bgStrokeAlpha,
226
+ bgStrokeWidth: style.bgStrokeWidth,
227
+ bgStrokeAlignment: style.bgStrokeAlignment,
228
+ bgStrokeDashLength: dash?.[0],
229
+ bgStrokeDashGap: dash?.[1],
230
+ bgStrokeCap: style.bgStrokeCap,
231
+ bgStrokeJoin: style.bgStrokeJoin,
232
+ labelText: style.labelText,
233
+ labelColor: colorToHex(style.labelColor),
234
+ labelFontSize: style.labelFontSize,
235
+ labelFontWeight: typeof style.labelFontWeight === "number" ? style.labelFontWeight : void 0,
236
+ labelPlacement: style.labelPlacement,
237
+ labelOffsetX: style.labelOffsetX,
238
+ labelOffsetY: style.labelOffsetY
239
+ };
240
+ }
241
+ function buildShape(f) {
242
+ switch (f.shapeKind) {
243
+ case "rect":
244
+ return {
245
+ kind: "rect",
246
+ width: f.width ?? 24,
247
+ height: f.height ?? 24,
248
+ ...f.cornerRadius != null ? { cornerRadius: f.cornerRadius } : {}
249
+ };
250
+ case "regular-polygon":
251
+ return { kind: "regular-polygon", sides: f.sides ?? 6, radius: f.radius ?? 12 };
252
+ case "star":
253
+ return {
254
+ kind: "star",
255
+ points: f.points ?? 5,
256
+ innerRadius: f.innerRadius ?? 6,
257
+ outerRadius: f.outerRadius ?? 12
258
+ };
259
+ case "circle":
260
+ return { kind: "circle", radius: f.radius ?? 12 };
261
+ default:
262
+ return void 0;
263
+ }
264
+ }
265
+ function formToStyle(f) {
266
+ const out = {};
267
+ if (f.size !== void 0) out.size = f.size;
268
+ const bgFill = hexToColor(f.bgFill);
269
+ if (bgFill !== void 0) out.bgFill = bgFill;
270
+ if (f.bgAlpha !== void 0) out.bgAlpha = f.bgAlpha;
271
+ const bgStrokeColor = hexToColor(f.bgStrokeColor);
272
+ if (bgStrokeColor !== void 0) out.bgStrokeColor = bgStrokeColor;
273
+ if (f.bgStrokeAlpha !== void 0) out.bgStrokeAlpha = f.bgStrokeAlpha;
274
+ if (f.bgStrokeWidth !== void 0) out.bgStrokeWidth = f.bgStrokeWidth;
275
+ if (f.bgStrokeAlignment !== void 0) out.bgStrokeAlignment = f.bgStrokeAlignment;
276
+ if (f.bgStrokeCap !== void 0) out.bgStrokeCap = f.bgStrokeCap;
277
+ if (f.bgStrokeJoin !== void 0) out.bgStrokeJoin = f.bgStrokeJoin;
278
+ if (f.labelText !== void 0) out.labelText = f.labelText;
279
+ const labelColor = hexToColor(f.labelColor);
280
+ if (labelColor !== void 0) out.labelColor = labelColor;
281
+ if (f.labelFontSize !== void 0) out.labelFontSize = f.labelFontSize;
282
+ if (f.labelFontWeight !== void 0) out.labelFontWeight = f.labelFontWeight;
283
+ if (f.labelPlacement !== void 0) out.labelPlacement = f.labelPlacement;
284
+ if (f.labelOffsetX !== void 0) out.labelOffsetX = f.labelOffsetX;
285
+ if (f.labelOffsetY !== void 0) out.labelOffsetY = f.labelOffsetY;
286
+ if (f.shapeKind) out.shape = buildShape(f);
287
+ if (f.bgStrokeDashLength != null || f.bgStrokeDashGap != null) {
288
+ out.bgStrokeDashArray = [f.bgStrokeDashLength ?? 0, f.bgStrokeDashGap ?? 0];
289
+ }
290
+ return out;
291
+ }
292
+
293
+ export { BACKGROUND_FIELDS, COLOR_PRESETS, LABEL_FIELDS, NodeStyleEditor, STROKE_FIELDS, defaultShapeFor, formToStyle, geometryFields, hexToNumber, nodeStyleFields, numberToHex, styleToForm };
294
+ //# sourceMappingURL=index.js.map
295
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presets/colors.ts","../src/editors/node-style/fields.ts","../src/editors/node-style/NodeStyleEditor.tsx","../src/utils/color.ts","../src/editors/node-style/mapping.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Shared colour palette offered as swatch presets on every colour field across\n * the editors (node fill / stroke / label, and the upcoming edge / canvas\n * surfaces). Hex strings so a seeded `#rrggbb` value highlights its matching\n * preset. A specific editor can pass its own palette instead where it matters.\n */\nexport const COLOR_PRESETS = [\n { label: 'Slate', value: '#9ca3af' },\n { label: 'Red', value: '#ef4444' },\n { label: 'Amber', value: '#f59e0b' },\n { label: 'Emerald', value: '#10b981' },\n { label: 'Blue', value: '#3b82f6' },\n { label: 'Violet', value: '#8b5cf6' },\n] as const;\n","import type { FieldConfig } from '@invana/forms';\n\nimport { COLOR_PRESETS } from '../../presets/colors';\nimport type { NodeStyleFields, ShapeKind } from './types';\n\n/**\n * `@invana/forms` field schemas for the NodeStyle editor. Field `name`s match\n * the keys of {@link NodeStyleFields} 1:1 so the generator's `style.<name>`\n * paths line up with the mapping in `mapping.ts`. {@link nodeStyleFields}\n * assembles them into the grouped default schema `<NodeStyleEditor>` renders.\n *\n * Adding a control = adding a `FieldConfig` here (plus the matching key in\n * `NodeStyleFields` and a `mapping.ts` line) — no bespoke JSX.\n */\n\nconst SHAPE_KIND_FIELD: FieldConfig = {\n name: 'shapeKind',\n type: 'select',\n label: 'Shape',\n options: [\n { value: 'circle', label: 'Circle' },\n { value: 'rect', label: 'Rectangle' },\n { value: 'regular-polygon', label: 'Regular polygon' },\n { value: 'star', label: 'Star' },\n ],\n};\n\nconst SIZE_FIELD: FieldConfig = {\n name: 'size',\n type: 'number',\n label: 'Size',\n min: 0,\n max: 200,\n step: 1,\n description: \"Unified radius / half-extent. Overrides the shape's native size axis.\",\n};\n\n/** Per-kind geometry numerics, keyed by shape kind. */\nconst GEOMETRY_BY_KIND: Record<ShapeKind, FieldConfig[]> = {\n circle: [{ name: 'radius', type: 'number', label: 'Radius', min: 0, max: 200, step: 1 }],\n rect: [\n { name: 'width', type: 'number', label: 'Width', min: 0, max: 400, step: 1 },\n { name: 'height', type: 'number', label: 'Height', min: 0, max: 400, step: 1 },\n { name: 'cornerRadius', type: 'number', label: 'Corner radius', min: 0, max: 200, step: 1 },\n ],\n 'regular-polygon': [\n { name: 'sides', type: 'number', label: 'Sides', min: 3, max: 20, step: 1 },\n { name: 'radius', type: 'number', label: 'Radius', min: 0, max: 200, step: 1 },\n ],\n star: [\n { name: 'points', type: 'number', label: 'Points', min: 3, max: 20, step: 1 },\n { name: 'innerRadius', type: 'number', label: 'Inner radius', min: 0, max: 200, step: 1 },\n { name: 'outerRadius', type: 'number', label: 'Outer radius', min: 0, max: 200, step: 1 },\n ],\n} as Record<ShapeKind, FieldConfig[]>;\n\n/**\n * Geometry-tab fields for the current shape kind. Dynamic: the per-kind\n * numerics (radius / width-height / sides / points…) change with the watched\n * `shapeKind`, which is how the form-generator handles the discriminated\n * union. Falls back to no geometry numerics until a kind is chosen.\n */\nexport function geometryFields(kind: ShapeKind | undefined): FieldConfig[] {\n const geometry = kind ? (GEOMETRY_BY_KIND[kind] ?? []) : [];\n return [SHAPE_KIND_FIELD, ...geometry, SIZE_FIELD];\n}\n\nexport const BACKGROUND_FIELDS: FieldConfig[] = [\n {\n name: 'bgFill',\n type: 'color',\n label: 'Fill color',\n presetColors: [...COLOR_PRESETS],\n description: 'Solid color. Use the engine API directly for stacked / image / glyph fills.',\n },\n { name: 'bgAlpha', type: 'number', label: 'Fill alpha', min: 0, max: 1, step: 0.01 },\n];\n\nexport const STROKE_FIELDS: FieldConfig[] = [\n { name: 'bgStrokeColor', type: 'color', label: 'Stroke color', presetColors: [...COLOR_PRESETS] },\n { name: 'bgStrokeAlpha', type: 'number', label: 'Stroke alpha', min: 0, max: 1, step: 0.01 },\n { name: 'bgStrokeWidth', type: 'number', label: 'Stroke width', min: 0, max: 50, step: 0.5 },\n {\n name: 'bgStrokeAlignment',\n type: 'select',\n label: 'Stroke alignment',\n options: [\n { value: 'inside', label: 'Inside' },\n { value: 'center', label: 'Center' },\n { value: 'outside', label: 'Outside' },\n ],\n },\n {\n name: 'bgStrokeDashLength',\n type: 'number',\n label: 'Dash length',\n min: 0,\n max: 50,\n step: 1,\n description: 'Leave dash + gap at 0 for a solid stroke.',\n },\n { name: 'bgStrokeDashGap', type: 'number', label: 'Dash gap', min: 0, max: 50, step: 1 },\n {\n name: 'bgStrokeCap',\n type: 'select',\n label: 'Cap',\n options: [\n { value: 'butt', label: 'Butt' },\n { value: 'round', label: 'Round' },\n { value: 'square', label: 'Square' },\n ],\n },\n {\n name: 'bgStrokeJoin',\n type: 'select',\n label: 'Join',\n options: [\n { value: 'miter', label: 'Miter' },\n { value: 'round', label: 'Round' },\n { value: 'bevel', label: 'Bevel' },\n ],\n },\n];\n\nexport const LABEL_FIELDS: FieldConfig[] = [\n { name: 'labelText', type: 'text', label: 'Text', placeholder: '(uses node id / data field)' },\n { name: 'labelColor', type: 'color', label: 'Color', presetColors: [...COLOR_PRESETS] },\n { name: 'labelFontSize', type: 'number', label: 'Font size', min: 1, max: 120, step: 1 },\n { name: 'labelFontWeight', type: 'number', label: 'Font weight', min: 100, max: 900, step: 100 },\n {\n name: 'labelPlacement',\n type: 'select',\n label: 'Placement',\n description: 'inside-* placements clip / truncate to fit the shape.',\n options: [\n { value: 'center', label: 'Center (anchor)' },\n { value: 'top', label: 'Top' },\n { value: 'bottom', label: 'Bottom' },\n { value: 'left', label: 'Left' },\n { value: 'right', label: 'Right' },\n { value: 'top-left', label: 'Top-left' },\n { value: 'top-right', label: 'Top-right' },\n { value: 'bottom-left', label: 'Bottom-left' },\n { value: 'bottom-right', label: 'Bottom-right' },\n { value: 'inside-center', label: 'Inside center (contained)' },\n { value: 'inside-top', label: 'Inside top' },\n { value: 'inside-bottom', label: 'Inside bottom' },\n { value: 'inside-left', label: 'Inside left' },\n { value: 'inside-right', label: 'Inside right' },\n ],\n },\n { name: 'labelOffsetX', type: 'number', label: 'Offset X', min: -200, max: 200, step: 1 },\n { name: 'labelOffsetY', type: 'number', label: 'Offset Y', min: -200, max: 200, step: 1 },\n];\n\nconst withGroup =\n (group: string) =>\n (f: FieldConfig): FieldConfig => ({ ...f, group });\n\n/**\n * The full NodeStyle field set as one grouped `FieldConfig[]` — the default\n * `fields` for `<NodeStyleEditor>`. `@invana/forms` renders each `group` as an\n * accordion section. Geometry numerics vary with the current `shapeKind`\n * (the discriminated union), so this is a function of the live values.\n */\nexport function nodeStyleFields(values: NodeStyleFields = {}): FieldConfig[] {\n return [\n ...geometryFields(values.shapeKind).map(withGroup('Geometry')),\n ...BACKGROUND_FIELDS.map(withGroup('Background')),\n ...STROKE_FIELDS.map(withGroup('Stroke')),\n ...LABEL_FIELDS.map(withGroup('Label')),\n ];\n}\n","import { FormField, type FieldConfig } from '@invana/forms';\nimport { Button } from '@invana/ui';\nimport {\n FormProvider,\n useForm,\n useWatch,\n type Control,\n type FieldValues,\n} from 'react-hook-form';\n\nimport { nodeStyleFields } from './fields';\nimport type { NodeStyleFields, NodeStyleFormState } from './types';\n\nexport interface NodeStyleEditorProps {\n /**\n * Initial field values, loaded into the form once on mount. Same shape the\n * form produces (see {@link NodeStyleFields}); seed it from an engine style\n * with the exported `styleToForm`. Remount (via `key`) to reload.\n */\n defaults?: NodeStyleFields;\n /**\n * The form schema. Either a static `FieldConfig[]` or a function of the\n * current values — the function form lets fields react to other fields (the\n * built-in default varies the geometry inputs with `shapeKind`). Defaults to\n * the built-in grouped NodeStyle field set ({@link nodeStyleFields}).\n */\n fields?: FieldConfig[] | ((values: NodeStyleFields) => FieldConfig[]);\n /**\n * Called with the current values when the user submits. This is where the\n * consumer puts its logic — map back to a style with `formToStyle` and apply\n * it wherever (a node, many nodes, an undo stack, …). The component itself\n * does none of that.\n */\n onSubmit: (values: NodeStyleFields) => void;\n /** Submit button label. Default `'Apply'`. */\n submitLabel?: string;\n}\n\n/**\n * Self-contained, engine-agnostic style form.\n *\n * Takes `defaults` + `fields`, owns a react-hook-form instance, renders the\n * schema with `@invana/forms` (each field `group` becomes an accordion\n * section), and on submit hands the current values to `onSubmit`. It knows\n * nothing about `Canvas`, layers, or how a style is stored — it just loads the\n * defaults and tracks the user's edits in the shape the `fields` define.\n *\n * The NodeStyle ⇄ form-fields mapping (`styleToForm` / `formToStyle`) is the\n * consumer's plug-in, used in `defaults` and inside `onSubmit`.\n */\nexport function NodeStyleEditor({\n defaults = {},\n fields = nodeStyleFields,\n onSubmit,\n submitLabel = 'Apply',\n}: NodeStyleEditorProps) {\n const form = useForm<NodeStyleFormState>({ defaultValues: { style: defaults } });\n const { control, getValues } = form;\n\n // Recompute fields from the live values when `fields` is a function (drives\n // the geometry tab's per-kind numerics off the watched `shapeKind`).\n const values = useWatch({ control, name: 'style' }) as NodeStyleFields | undefined;\n const resolvedFields = typeof fields === 'function' ? fields(values ?? {}) : fields;\n\n // RHF 7.76's `Control<NodeStyleFormState>` isn't assignable to `ObjectField`'s\n // `Control<any>` (the field-name union is contravariant). Widen at the boundary.\n const c = control as unknown as Control<FieldValues>;\n\n return (\n // `@invana/forms` leaf fields read `useFormContext()`, so the whole form\n // must be on context — not just control.\n <FormProvider {...form}>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 12, padding: 16 }}>\n <FormField.ObjectField control={c} name=\"style\" fields={resolvedFields} />\n <div style={{ display: 'flex', justifyContent: 'flex-end' }}>\n <Button onClick={() => onSubmit(getValues('style'))}>{submitLabel}</Button>\n </div>\n </div>\n </FormProvider>\n );\n}\n","/**\n * Colour conversion helpers. `NodeStyle` (and the rest of the engine) stores\n * colours as 24-bit RGB numbers (`0xRRGGBB`); HTML `<input type=\"color\">` and\n * the design-kit colour swatch use `#rrggbb` strings. These bridge the two.\n */\n\nexport function numberToHex(n: number | undefined): string {\n if (n === undefined || Number.isNaN(n)) return '#000000';\n const clamped = Math.max(0, Math.min(0xffffff, Math.floor(n)));\n return '#' + clamped.toString(16).padStart(6, '0');\n}\n\nexport function hexToNumber(hex: string): number {\n const stripped = hex.startsWith('#') ? hex.slice(1) : hex;\n const parsed = Number.parseInt(stripped, 16);\n return Number.isNaN(parsed) ? 0 : parsed;\n}\n","import type { NodeStyle } from '@invana/graph';\n\nimport { hexToNumber, numberToHex } from '../../utils/color';\nimport type { NodeStyleFields, ShapeKind } from './types';\n\n/**\n * Construct a fresh shape spec with sane defaults for a given kind. Used when\n * the user switches `shapeKind` and there's no seeded geometry to preserve.\n */\nexport function defaultShapeFor(kind: ShapeKind): NonNullable<NodeStyle['shape']> {\n switch (kind) {\n case 'rect':\n return { kind: 'rect', width: 24, height: 24 };\n case 'regular-polygon':\n return { kind: 'regular-polygon', sides: 6, radius: 12 };\n case 'star':\n return { kind: 'star', points: 5, innerRadius: 6, outerRadius: 12 };\n case 'circle':\n default:\n return { kind: 'circle', radius: 12 };\n }\n}\n\n/** `0xRRGGBB` → `#rrggbb`, but only for real numbers — non-colour fills\n * (image / glyph / stacked layers) round-trip as `undefined` so the editor\n * leaves them untouched. Accepts the full `ShapeFill` and guards. */\nfunction colorToHex(v: unknown): string | undefined {\n return typeof v === 'number' ? numberToHex(v) : undefined;\n}\n\n/** `#rrggbb` → `0xRRGGBB`; empty / undefined stays undefined (don't bake a\n * black `0x000000` onto fields the user never set). */\nfunction hexToColor(s: string | undefined): number | undefined {\n return s && s.length > 0 ? hexToNumber(s) : undefined;\n}\n\n/**\n * Map an engine `Partial<NodeStyle>` (e.g. the result of\n * `layer.resolveNodeStyle(node)`) to the flat, string-colour\n * {@link NodeStyleFields} the `@invana/forms` generator renders.\n *\n * Extracts only the literal fields the editor handles, guarding non-scalar\n * values (image / glyph fills, string font weights) so they round-trip as\n * `undefined` rather than corrupting a field. The `shape` discriminated union\n * is flattened to `shapeKind` + the geometry numbers of that kind; the dash\n * tuple is split; colours become hex strings.\n */\nexport function styleToForm(style: Partial<NodeStyle>): NodeStyleFields {\n const s = style.shape;\n const dash = style.bgStrokeDashArray;\n // `in`-based narrowing rather than `s.kind === …`: the shape union includes\n // an open-ended custom `kind: string & {}`, which defeats discriminated-union\n // narrowing on `kind` for the whole union. Property presence still narrows.\n return {\n shapeKind: s?.kind,\n radius: s && 'radius' in s ? s.radius : undefined, // circle, regular-polygon\n width: s && 'width' in s ? s.width : undefined,\n height: s && 'height' in s ? s.height : undefined,\n cornerRadius: s && 'cornerRadius' in s ? s.cornerRadius : undefined,\n sides: s && 'sides' in s ? s.sides : undefined,\n points: s && 'points' in s ? s.points : undefined,\n innerRadius: s && 'innerRadius' in s ? s.innerRadius : undefined,\n outerRadius: s && 'outerRadius' in s ? s.outerRadius : undefined,\n size: style.size,\n\n bgFill: colorToHex(style.bgFill),\n bgAlpha: style.bgAlpha,\n\n bgStrokeColor: colorToHex(style.bgStrokeColor),\n bgStrokeAlpha: style.bgStrokeAlpha,\n bgStrokeWidth: style.bgStrokeWidth,\n bgStrokeAlignment: style.bgStrokeAlignment,\n bgStrokeDashLength: dash?.[0],\n bgStrokeDashGap: dash?.[1],\n bgStrokeCap: style.bgStrokeCap,\n bgStrokeJoin: style.bgStrokeJoin,\n\n labelText: style.labelText,\n labelColor: colorToHex(style.labelColor),\n labelFontSize: style.labelFontSize,\n labelFontWeight: typeof style.labelFontWeight === 'number' ? style.labelFontWeight : undefined,\n labelPlacement: style.labelPlacement,\n labelOffsetX: style.labelOffsetX,\n labelOffsetY: style.labelOffsetY,\n };\n}\n\n/** Rebuild the shape discriminated union from the flat geometry fields,\n * defaulting any axis the user left blank for the current kind. */\nfunction buildShape(f: NodeStyleFields): NodeStyle['shape'] {\n switch (f.shapeKind) {\n case 'rect':\n return {\n kind: 'rect',\n width: f.width ?? 24,\n height: f.height ?? 24,\n ...(f.cornerRadius != null ? { cornerRadius: f.cornerRadius } : {}),\n };\n case 'regular-polygon':\n return { kind: 'regular-polygon', sides: f.sides ?? 6, radius: f.radius ?? 12 };\n case 'star':\n return {\n kind: 'star',\n points: f.points ?? 5,\n innerRadius: f.innerRadius ?? 6,\n outerRadius: f.outerRadius ?? 12,\n };\n case 'circle':\n return { kind: 'circle', radius: f.radius ?? 12 };\n default:\n return undefined;\n }\n}\n\n/**\n * Map the flat {@link NodeStyleFields} the form holds back to an engine\n * `Partial<NodeStyle>` — inverse of {@link styleToForm}. Only fields the form\n * actually set are included (no `undefined` keys), so the result is safe to\n * spread over an existing style on commit:\n * `store.updateNode(id, { style: { ...resolveNodeStyle(node), ...formToStyle(fields) } })`.\n */\nexport function formToStyle(f: NodeStyleFields): Partial<NodeStyle> {\n // `NodeStyle` fields are readonly; accumulate into a mutable view, return as Partial.\n const out: { -readonly [K in keyof NodeStyle]?: NodeStyle[K] } = {};\n\n if (f.size !== undefined) out.size = f.size;\n\n const bgFill = hexToColor(f.bgFill);\n if (bgFill !== undefined) out.bgFill = bgFill;\n if (f.bgAlpha !== undefined) out.bgAlpha = f.bgAlpha;\n\n const bgStrokeColor = hexToColor(f.bgStrokeColor);\n if (bgStrokeColor !== undefined) out.bgStrokeColor = bgStrokeColor;\n if (f.bgStrokeAlpha !== undefined) out.bgStrokeAlpha = f.bgStrokeAlpha;\n if (f.bgStrokeWidth !== undefined) out.bgStrokeWidth = f.bgStrokeWidth;\n if (f.bgStrokeAlignment !== undefined) out.bgStrokeAlignment = f.bgStrokeAlignment;\n if (f.bgStrokeCap !== undefined) out.bgStrokeCap = f.bgStrokeCap;\n if (f.bgStrokeJoin !== undefined) out.bgStrokeJoin = f.bgStrokeJoin;\n\n if (f.labelText !== undefined) out.labelText = f.labelText;\n const labelColor = hexToColor(f.labelColor);\n if (labelColor !== undefined) out.labelColor = labelColor;\n if (f.labelFontSize !== undefined) out.labelFontSize = f.labelFontSize;\n if (f.labelFontWeight !== undefined) out.labelFontWeight = f.labelFontWeight;\n if (f.labelPlacement !== undefined) out.labelPlacement = f.labelPlacement;\n if (f.labelOffsetX !== undefined) out.labelOffsetX = f.labelOffsetX;\n if (f.labelOffsetY !== undefined) out.labelOffsetY = f.labelOffsetY;\n\n if (f.shapeKind) out.shape = buildShape(f);\n if (f.bgStrokeDashLength != null || f.bgStrokeDashGap != null) {\n out.bgStrokeDashArray = [f.bgStrokeDashLength ?? 0, f.bgStrokeDashGap ?? 0];\n }\n\n return out;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@invana/canvas-ui",
3
+ "version": "0.0.1",
4
+ "description": "Schema-driven, engine-agnostic React UI components (style editors, panels, controls) for Invana graph tools — built on the @invana/forms design-kit form-generator.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "typedocOptions": {
9
+ "entryPoints": [
10
+ "src/index.ts"
11
+ ]
12
+ },
13
+ "peerDependencies": {
14
+ "@invana/ui": "*",
15
+ "@invana/themes": "*",
16
+ "@invana/styling": "*",
17
+ "@invana/forms": "*",
18
+ "react": ">=18.0.0",
19
+ "react-dom": ">=18.0.0",
20
+ "react-hook-form": "^7",
21
+ "@invana/graph": "0.0.1"
22
+ },
23
+ "devDependencies": {
24
+ "@invana/ui": "^0.0.4",
25
+ "@invana/themes": "^0.0.4",
26
+ "@invana/styling": "^0.0.4",
27
+ "@invana/forms": "^0.0.4",
28
+ "@types/react": "^18.3.12",
29
+ "@types/react-dom": "^18.3.1",
30
+ "react": "^18.3.1",
31
+ "react-dom": "^18.3.1",
32
+ "react-hook-form": "^7.69.0",
33
+ "tsup": "^8.3.5",
34
+ "typescript": "5.9.2",
35
+ "@invana/graph": "0.0.1",
36
+ "@repo/typescript-config": "0.0.0"
37
+ },
38
+ "keywords": [
39
+ "canvas",
40
+ "react",
41
+ "ui",
42
+ "components",
43
+ "graph",
44
+ "editor",
45
+ "design-kit"
46
+ ],
47
+ "license": "Apache-2.0",
48
+ "module": "./dist/index.js",
49
+ "exports": {
50
+ ".": {
51
+ "types": "./dist/index.d.ts",
52
+ "import": "./dist/index.js",
53
+ "default": "./dist/index.js"
54
+ }
55
+ },
56
+ "files": [
57
+ "dist"
58
+ ],
59
+ "publishConfig": {
60
+ "access": "public"
61
+ },
62
+ "scripts": {
63
+ "build": "tsup",
64
+ "dev": "tsup --watch",
65
+ "lint": "eslint src/",
66
+ "check-types": "tsc --noEmit",
67
+ "clean": "rm -rf dist"
68
+ }
69
+ }