f1ow 0.1.4 → 0.1.5

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.
@@ -1,6 +1,7 @@
1
1
  import { CanvasElement, ElementStyle, ToolType } from '../types';
2
2
  import { ContextMenuItem } from '../components/ContextMenu/ContextMenu';
3
3
  import { CollaborationConfig } from '../collaboration/types';
4
+ import { CustomElementConfig } from '../utils/elementRegistry';
4
5
  export type { ContextMenuItem };
5
6
  /** Context passed to custom context menu renderers */
6
7
  export interface ContextMenuContext {
@@ -84,6 +85,27 @@ export interface FlowCanvasProps {
84
85
  * Pass a `CollaborationConfig` to connect, or `undefined`/`null` to disable.
85
86
  */
86
87
  collaboration?: CollaborationConfig | null;
88
+ /**
89
+ * Register custom element types for this canvas instance.
90
+ *
91
+ * Each config is passed to `elementRegistry.register()` once on mount.
92
+ * Custom types go through the same validation pipeline as built-in types;
93
+ * the optional `validate` callback handles type-specific field checks.
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * <FlowCanvas
98
+ * customElementTypes={[{
99
+ * type: 'sticky-note',
100
+ * displayName: 'Sticky Note',
101
+ * validate: (el) =>
102
+ * typeof el.content === 'string' || 'content must be a string',
103
+ * defaults: { content: '', color: '#ffeb3b' },
104
+ * }]}
105
+ * />
106
+ * ```
107
+ */
108
+ customElementTypes?: CustomElementConfig[];
87
109
  /**
88
110
  * Configure Web Workers for background processing (elbow routing, SVG export).
89
111
  *
@@ -9,6 +9,8 @@ export { distance, normalizeRect, rotatePoint, isPointInRect, getDiamondPoints,
9
9
  export { exportToDataURL, downloadPNG, exportToJSON, downloadJSON, exportToSVG, downloadSVG } from '../utils/export';
10
10
  export { drawArrowhead, arrowheadSize, flatToPoints } from '../utils/arrowheads';
11
11
  export { computeCurveControlPoint, quadBezierAt, quadBezierTangent, curveArrowPrev, CURVE_RATIO } from '../utils/curve';
12
+ export { elementRegistry, registerCustomElement } from '../utils/elementRegistry';
13
+ export type { CustomElementConfig, ValidationResult } from '../utils/elementRegistry';
12
14
  export { computeElbowPoints, computeElbowRoute, simplifyElbowPath, clearElbowRouteCache, directionFromFixedPoint, directionFromPoints, directionFromShapeToPoint, directionFromEdgePoint, getElbowPreferredDirection, } from '../utils/elbow';
13
15
  export type { Direction } from '../utils/elbow';
14
16
  export { getConnectionPoints, getEdgePoint, getEdgePointFromFixedPoint, computeFixedPoint, getAnchorPosition, findNearestSnapTarget, isConnectable, recomputeBoundPoints, findConnectorsForElement, addBoundElement, removeBoundElement, syncBoundElements, } from '../utils/connection';
@@ -0,0 +1,146 @@
1
+ /**
2
+ * ─── Element Registry ────────────────────────────────────────────────────────
3
+ *
4
+ * A plugin/extension point that lets consumers register custom element types
5
+ * alongside the 8 built-in types. Every element passing through
6
+ * `addElement`, `updateElement`, or `setElements` is validated here first.
7
+ *
8
+ * Built-in types (rectangle | ellipse | diamond | line | arrow |
9
+ * freedraw | text | image) are pre-validated with field-level checks.
10
+ * Custom types only need to pass the common base checks; additional
11
+ * field validation is supplied via the `validate` callback.
12
+ *
13
+ * @example — register a custom type globally before rendering:
14
+ * ```ts
15
+ * import { registerCustomElement } from 'f1ow';
16
+ *
17
+ * registerCustomElement({
18
+ * type: 'sticky-note',
19
+ * displayName: 'Sticky Note',
20
+ * validate: (el) => typeof el.content === 'string' || 'content must be a string',
21
+ * defaults: { content: '', color: '#ffeb3b' },
22
+ * });
23
+ * ```
24
+ *
25
+ * @example — or pass directly to the component (registered on mount):
26
+ * ```tsx
27
+ * <FlowCanvas
28
+ * customElementTypes={[{
29
+ * type: 'sticky-note',
30
+ * validate: (el) => typeof el.content === 'string' || 'content must be a string',
31
+ * }]}
32
+ * />
33
+ * ```
34
+ */
35
+ /** Result of a validation call. */
36
+ export type ValidationResult = {
37
+ valid: true;
38
+ } | {
39
+ valid: false;
40
+ error: string;
41
+ };
42
+ /**
43
+ * Configuration object for a custom element type.
44
+ *
45
+ * @template T - Shape of the custom element's extra fields.
46
+ */
47
+ export interface CustomElementConfig<T extends Record<string, unknown> = Record<string, unknown>> {
48
+ /**
49
+ * Unique type identifier.
50
+ * Must not conflict with a built-in type unless `allowOverride` is `true`.
51
+ */
52
+ type: string;
53
+ /** Human-readable name used in error/warning messages. Defaults to `type`. */
54
+ displayName?: string;
55
+ /**
56
+ * Additional validator for type-specific fields.
57
+ * Called **after** base-field (id, x, y, width, height, rotation, style)
58
+ * validation passes.
59
+ *
60
+ * Return `true` if the element is valid, or a string describing the error.
61
+ */
62
+ validate?: (element: Record<string, unknown>) => true | string;
63
+ /**
64
+ * Default field values merged into the element object when it is added via
65
+ * `addElement`. Fields already present on the element are NOT overwritten.
66
+ * Only applied to elements whose type matches this config.
67
+ */
68
+ defaults?: Partial<T>;
69
+ /**
70
+ * Allow replacing an existing registration (built-in or custom).
71
+ * Useful when a consumer wants to tighten / relax built-in validation.
72
+ * Default: `false`.
73
+ */
74
+ allowOverride?: boolean;
75
+ }
76
+ declare class ElementRegistryClass {
77
+ private customs;
78
+ /**
79
+ * Register a custom element type.
80
+ *
81
+ * @throws If the type already exists and `allowOverride` is not `true`.
82
+ */
83
+ register(config: CustomElementConfig): void;
84
+ /** Returns `true` if the type is known (built-in or custom). */
85
+ isRegistered(type: string): boolean;
86
+ /** Retrieve the custom config for a type (undefined for built-in types). */
87
+ getCustomConfig(type: string): CustomElementConfig | undefined;
88
+ /** All registered type names, built-in first then custom. */
89
+ getRegisteredTypes(): string[];
90
+ /**
91
+ * Validate a full element before it enters the canvas store.
92
+ *
93
+ * Checks:
94
+ * 1. Non-null object with required base fields (id, type, x, y, w, h, rotation, style)
95
+ * 2. `type` is a known/registered type
96
+ * 3. Type-specific field checks for all 8 built-in types
97
+ * 4. Custom `validate` callback (custom types only)
98
+ */
99
+ validateElement(element: unknown): ValidationResult;
100
+ /**
101
+ * Validate a partial update before it is applied to an existing element.
102
+ *
103
+ * Prevents overwriting immutable fields (`id` and `type`), and checks
104
+ * numeric fields for finiteness.
105
+ */
106
+ validateUpdate(updates: Record<string, unknown>): ValidationResult;
107
+ /**
108
+ * Merge custom `defaults` into the element.
109
+ * Existing fields on the element take priority — defaults only fill gaps.
110
+ * Only active when the element's type has a custom config with `defaults`.
111
+ */
112
+ applyDefaults<T extends {
113
+ type?: unknown;
114
+ }>(element: T): T;
115
+ private _validateStyle;
116
+ private _validateBuiltinFields;
117
+ }
118
+ /**
119
+ * The global element registry singleton.
120
+ *
121
+ * Pre-populated with field-level validators for all 8 built-in element types.
122
+ * Imported by the canvas store to gate every element mutation.
123
+ *
124
+ * Use `elementRegistry.register()` or the `registerCustomElement()` helper
125
+ * to add new element types before rendering `<FlowCanvas>`.
126
+ */
127
+ export declare const elementRegistry: ElementRegistryClass;
128
+ /**
129
+ * Shorthand to register a custom element type on the global registry.
130
+ *
131
+ * Equivalent to `elementRegistry.register(config)`.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * import { registerCustomElement } from 'f1ow';
136
+ *
137
+ * registerCustomElement({
138
+ * type: 'sticky-note',
139
+ * displayName: 'Sticky Note',
140
+ * validate: (el) => typeof el.content === 'string' || 'content must be a string',
141
+ * defaults: { content: '', color: '#ffeb3b' },
142
+ * });
143
+ * ```
144
+ */
145
+ export declare function registerCustomElement(config: CustomElementConfig): void;
146
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "f1ow",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "description": "Interactive canvas drawing toolkit built on KonvaJS — drop-in React component for diagrams, sketches & whiteboards",
6
6
  "author": "Nuumz <info@nuumz.com>",