@widgetic/canvas 0.5.4

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 (51) hide show
  1. package/README.md +45 -0
  2. package/dist/canvas/Canvas.svelte +10678 -0
  3. package/dist/canvas/Canvas.svelte.d.ts +147 -0
  4. package/dist/canvas/CanvasToolbar.svelte +1422 -0
  5. package/dist/canvas/CanvasToolbar.svelte.d.ts +54 -0
  6. package/dist/canvas/ContextMenu.svelte +279 -0
  7. package/dist/canvas/ContextMenu.svelte.d.ts +36 -0
  8. package/dist/canvas/PanZoomPanel.svelte +315 -0
  9. package/dist/canvas/PanZoomPanel.svelte.d.ts +32 -0
  10. package/dist/canvas/canvasLogger.d.ts +5 -0
  11. package/dist/canvas/canvasLogger.js +19 -0
  12. package/dist/canvas/index.d.ts +13 -0
  13. package/dist/canvas/index.js +12 -0
  14. package/dist/canvas/props-panel/PropsPanel.svelte +1902 -0
  15. package/dist/canvas/props-panel/PropsPanel.svelte.d.ts +73 -0
  16. package/dist/canvas/props-panel/TextPropsICSection.svelte +238 -0
  17. package/dist/canvas/props-panel/TextPropsICSection.svelte.d.ts +36 -0
  18. package/dist/canvas/shapes/FrameShape.d.ts +97 -0
  19. package/dist/canvas/shapes/FrameShape.js +951 -0
  20. package/dist/canvas/shapes/ImageShape.d.ts +38 -0
  21. package/dist/canvas/shapes/ImageShape.js +245 -0
  22. package/dist/canvas/shapes/ShapeLibrary.d.ts +64 -0
  23. package/dist/canvas/shapes/ShapeLibrary.js +526 -0
  24. package/dist/canvas/shapes/WidgetShape.d.ts +21 -0
  25. package/dist/canvas/shapes/WidgetShape.js +132 -0
  26. package/dist/canvas/types.d.ts +26 -0
  27. package/dist/canvas/types.js +5 -0
  28. package/dist/components/Tooltip.svelte +179 -0
  29. package/dist/components/Tooltip.svelte.d.ts +21 -0
  30. package/dist/components/index.d.ts +11 -0
  31. package/dist/components/index.js +13 -0
  32. package/dist/components/input-controls/ColorIC.svelte +388 -0
  33. package/dist/components/input-controls/ColorIC.svelte.d.ts +22 -0
  34. package/dist/components/input-controls/FontSelectorIC.svelte +69 -0
  35. package/dist/components/input-controls/FontSelectorIC.svelte.d.ts +17 -0
  36. package/dist/components/input-controls/SliderUnitIC.svelte +217 -0
  37. package/dist/components/input-controls/SliderUnitIC.svelte.d.ts +24 -0
  38. package/dist/components/input-controls/TextAlignIC.svelte +84 -0
  39. package/dist/components/input-controls/TextAlignIC.svelte.d.ts +17 -0
  40. package/dist/components/input-controls/TextStyleIC.svelte +85 -0
  41. package/dist/components/input-controls/TextStyleIC.svelte.d.ts +21 -0
  42. package/dist/icons/arrow.svg +3 -0
  43. package/dist/icons/checkmark.svg +3 -0
  44. package/dist/icons/draw.svg +22 -0
  45. package/dist/icons/eraser.svg +23 -0
  46. package/dist/icons/hand.svg +6 -0
  47. package/dist/icons/select.svg +3 -0
  48. package/dist/icons/text.svg +5 -0
  49. package/dist/index.d.ts +11 -0
  50. package/dist/index.js +12 -0
  51. package/package.json +101 -0
@@ -0,0 +1,73 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ class?: string;
5
+ style?: string;
6
+ selectedObject?: any;
7
+ wireframeMode?: boolean;
8
+ onFillChange?: (color: string) => void;
9
+ onStrokeChange?: (color: string) => void;
10
+ onStrokeWidthChange?: (width: number) => void;
11
+ onRoundnessChange?: (roundness: number) => void;
12
+ onStrokeTypeChange?: (type: "solid" | "dashed" | "dotted") => void;
13
+ onOpacityChange?: (opacity: number) => void;
14
+ onFillOpacityChange?: (opacity: number) => void;
15
+ onBackgroundColorChange?: (color: string) => void;
16
+ onBackgroundOpacityChange?: (opacity: number) => void;
17
+ onFontSizeChange?: (size: number) => void;
18
+ onFontFamilyChange?: (family: string) => void;
19
+ onFontWeightChange?: (weight: "normal" | "bold") => void;
20
+ onFontStyleChange?: (style: "normal" | "italic") => void;
21
+ onUnderlineChange?: (underline: boolean) => void;
22
+ onTextAlignChange?: (align: "left" | "center" | "right") => void;
23
+ onTextPaddingChange?: (padding: number) => void;
24
+ onTextColorChange?: (color: string) => void;
25
+ onFrameLabelChange?: (label: string) => void;
26
+ onFrameLabelFontSizeChange?: (size: number) => void;
27
+ onFrameLabelFontFamilyChange?: (family: string) => void;
28
+ onFrameLabelTextAlignChange?: (align: "left" | "center" | "right") => void;
29
+ onFrameLabelTextColorChange?: (color: string) => void;
30
+ onFrameLabelFontWeightChange?: (weight: "normal" | "bold") => void;
31
+ onFrameLabelFontStyleChange?: (style: "normal" | "italic") => void;
32
+ onFrameLabelUnderlineChange?: (underline: boolean) => void;
33
+ onFrameLabelBackgroundColorChange?: (color: string) => void;
34
+ onTextboxBoxFillChange?: (color: string) => void;
35
+ onTextboxBoxStrokeChange?: (color: string) => void;
36
+ onTextboxBoxStrokeWidthChange?: (width: number) => void;
37
+ onTextboxBoxStrokeTypeChange?: (type: "solid" | "dashed" | "dotted") => void;
38
+ onTextStrokeColorChange?: (color: string) => void;
39
+ onTextStrokeWidthChange?: (width: number) => void;
40
+ onImageUrlLoad?: (url: string) => void;
41
+ onImageSourceChange?: () => void;
42
+ onCapture?: (() => void) | null;
43
+ onConvertToWidget?: (() => void) | null;
44
+ onWidgetClick?: ((widgetId: string) => void) | null;
45
+ onWidgetRename?: ((widgetId: string, newName: string) => void) | null;
46
+ onRefreshScreenshot?: ((widgetId: string) => void) | null;
47
+ getWidgetName?: ((widgetId: string) => string | null) | null;
48
+ /** Returns false when the linked widget was deleted or moved off this canvas. */ isWidgetKnown?: ((widgetId: string) => boolean) | null;
49
+ /** Remove the selected orphaned widget shape from the canvas. */ onRemoveOrphanedShape?: (() => void) | null;
50
+ onBringToFront?: () => void;
51
+ onBringForward?: () => void;
52
+ onSendBackward?: () => void;
53
+ onSendToBack?: () => void;
54
+ onAlignLeft?: () => void;
55
+ onAlignCenterH?: () => void;
56
+ onAlignRight?: () => void;
57
+ onAlignTop?: () => void;
58
+ onAlignMiddleV?: () => void;
59
+ onAlignBottom?: () => void;
60
+ onWidthChange?: (width: number) => void;
61
+ onHeightChange?: (height: number) => void;
62
+ };
63
+ events: {
64
+ [evt: string]: CustomEvent<any>;
65
+ };
66
+ slots: {};
67
+ };
68
+ export type PropsPanelProps = typeof __propDef.props;
69
+ export type PropsPanelEvents = typeof __propDef.events;
70
+ export type PropsPanelSlots = typeof __propDef.slots;
71
+ export default class PropsPanel extends SvelteComponentTyped<PropsPanelProps, PropsPanelEvents, PropsPanelSlots> {
72
+ }
73
+ export {};
@@ -0,0 +1,238 @@
1
+ <script lang="ts">
2
+ import ColorIC from '../../components/input-controls/ColorIC.svelte';
3
+ import SliderUnitIC from '../../components/input-controls/SliderUnitIC.svelte';
4
+
5
+ let className = '';
6
+ export { className as class };
7
+ export let style = '';
8
+
9
+ // Current values
10
+ export let fontSize: number = 28;
11
+ export let fontFamily: string = 'Arial';
12
+ export let fontWeight: 'normal' | 'bold' = 'normal';
13
+ export let fontStyle: 'normal' | 'italic' = 'normal';
14
+ export let underline: boolean = false;
15
+ export let textAlign: 'left' | 'center' | 'right' = 'left';
16
+ export let textColor: string = '#000000';
17
+
18
+ // Callbacks
19
+ export let onFontSizeChange: (size: number) => void = () => {};
20
+ export let onFontFamilyChange: (family: string) => void = () => {};
21
+ export let onFontWeightChange: (weight: 'normal' | 'bold') => void = () => {};
22
+ export let onFontStyleChange: (style: 'normal' | 'italic') => void = () => {};
23
+ export let onUnderlineChange: (underline: boolean) => void = () => {};
24
+ export let onTextAlignChange: (align: 'left' | 'center' | 'right') => void = () => {};
25
+ export let onTextColorChange: (color: string) => void = () => {};
26
+
27
+ // Show/hide certain controls
28
+ export let showFontSize: boolean = true;
29
+ export let showFontFamily: boolean = true;
30
+ export let showTextStyle: boolean = true;
31
+ export let showTextAlign: boolean = true;
32
+ export let showTextColor: boolean = true;
33
+
34
+ // Font presets
35
+ const fontFamilies = [
36
+ 'Arial',
37
+ 'Helvetica',
38
+ 'Times New Roman',
39
+ 'Georgia',
40
+ 'Verdana',
41
+ 'Courier New',
42
+ 'Impact',
43
+ 'Comic Sans MS'
44
+ ];
45
+
46
+ function handleFontFamilyChange(e: Event) {
47
+ const select = e.target as HTMLSelectElement;
48
+ onFontFamilyChange(select.value);
49
+ }
50
+ </script>
51
+
52
+ <div class="{className ? className + ' ' : ''}text-props-component" style={style}>
53
+
54
+ {#if showFontSize}
55
+ <SliderUnitIC
56
+ label="Font Size"
57
+ value={fontSize}
58
+ min={8}
59
+ max={120}
60
+ step={1}
61
+ unit="px"
62
+ onChange={onFontSizeChange}
63
+ />
64
+ {/if}
65
+
66
+ {#if showFontFamily}
67
+ <div class="prop-section">
68
+ <span class="prop-label">Font Family</span>
69
+ <select
70
+ value={fontFamily}
71
+ onchange={handleFontFamilyChange}
72
+ class="font-select"
73
+ >
74
+ {#each fontFamilies as font}
75
+ <option value={font} style="font-family: {font}">{font}</option>
76
+ {/each}
77
+ </select>
78
+ </div>
79
+ {/if}
80
+
81
+ {#if showTextStyle}
82
+ <div class="prop-section">
83
+ <span class="prop-label">Text Style</span>
84
+ <div class="text-style-buttons">
85
+ <button
86
+ class="style-btn {fontWeight === 'bold' ? 'active' : ''}"
87
+ onclick={() => onFontWeightChange(fontWeight === 'bold' ? 'normal' : 'bold')}
88
+ title="Bold"
89
+ >
90
+ <strong>B</strong>
91
+ </button>
92
+ <button
93
+ class="style-btn {fontStyle === 'italic' ? 'active' : ''}"
94
+ onclick={() => onFontStyleChange(fontStyle === 'italic' ? 'normal' : 'italic')}
95
+ title="Italic"
96
+ >
97
+ <em>I</em>
98
+ </button>
99
+ <button
100
+ class="style-btn {underline ? 'active' : ''}"
101
+ onclick={() => onUnderlineChange(!underline)}
102
+ title="Underline"
103
+ >
104
+ <span style="text-decoration: underline">U</span>
105
+ </button>
106
+ </div>
107
+ </div>
108
+ {/if}
109
+
110
+ {#if showTextAlign}
111
+ <div class="prop-section">
112
+ <span class="prop-label">Text Align</span>
113
+ <div class="align-buttons">
114
+ <button
115
+ class="align-btn {textAlign === 'left' ? 'active' : ''}"
116
+ onclick={() => onTextAlignChange('left')}
117
+ title="Align Left"
118
+ >⫷</button>
119
+ <button
120
+ class="align-btn {textAlign === 'center' ? 'active' : ''}"
121
+ onclick={() => onTextAlignChange('center')}
122
+ title="Center"
123
+ >☰</button>
124
+ <button
125
+ class="align-btn {textAlign === 'right' ? 'active' : ''}"
126
+ onclick={() => onTextAlignChange('right')}
127
+ title="Align Right"
128
+ >⫸</button>
129
+ </div>
130
+ </div>
131
+ {/if}
132
+
133
+ {#if showTextColor}
134
+ <ColorIC
135
+ label="Text Color"
136
+ value={textColor}
137
+ onChange={onTextColorChange}
138
+ />
139
+ {/if}
140
+ </div>
141
+
142
+ <style>
143
+ .text-props-component {
144
+ display: flex;
145
+ flex-direction: column;
146
+ gap: 10px;
147
+ }
148
+
149
+ .prop-section {
150
+ display: flex;
151
+ flex-direction: column;
152
+ gap: 6px;
153
+ }
154
+
155
+ .prop-label {
156
+ font-size: 11px;
157
+ font-weight: 600;
158
+ color: #6b7280;
159
+ text-transform: uppercase;
160
+ letter-spacing: 0.5px;
161
+ }
162
+
163
+ /* Font select */
164
+ .font-select {
165
+ width: 100%;
166
+ padding: 6px 8px;
167
+ border: 1px solid #d1d5db;
168
+ border-radius: 4px;
169
+ font-size: 12px;
170
+ cursor: pointer;
171
+ background: white;
172
+ }
173
+
174
+ .font-select:focus {
175
+ outline: none;
176
+ border-color: var(--accent, #314F2F);
177
+ }
178
+
179
+ /* Text style buttons */
180
+ .text-style-buttons {
181
+ display: flex;
182
+ gap: 4px;
183
+ }
184
+
185
+ .style-btn {
186
+ width: 32px;
187
+ height: 32px;
188
+ display: flex;
189
+ align-items: center;
190
+ justify-content: center;
191
+ border: 1px solid #d1d5db;
192
+ border-radius: 4px;
193
+ background: white;
194
+ font-size: 14px;
195
+ cursor: pointer;
196
+ transition: all 0.15s ease;
197
+ }
198
+
199
+ .style-btn:hover {
200
+ background: #f3f4f6;
201
+ }
202
+
203
+ .style-btn.active {
204
+ background: var(--accent, #314F2F);
205
+ color: white;
206
+ border-color: var(--accent, #314F2F);
207
+ }
208
+
209
+ /* Align buttons */
210
+ .align-buttons {
211
+ display: flex;
212
+ gap: 4px;
213
+ }
214
+
215
+ .align-btn {
216
+ flex: 1;
217
+ height: 32px;
218
+ display: flex;
219
+ align-items: center;
220
+ justify-content: center;
221
+ border: 1px solid #d1d5db;
222
+ border-radius: 4px;
223
+ background: white;
224
+ font-size: 14px;
225
+ cursor: pointer;
226
+ transition: all 0.15s ease;
227
+ }
228
+
229
+ .align-btn:hover {
230
+ background: #f3f4f6;
231
+ }
232
+
233
+ .align-btn.active {
234
+ background: var(--accent, #314F2F);
235
+ color: white;
236
+ border-color: var(--accent, #314F2F);
237
+ }
238
+ </style>
@@ -0,0 +1,36 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ class?: string;
5
+ style?: string;
6
+ fontSize?: number;
7
+ fontFamily?: string;
8
+ fontWeight?: "normal" | "bold";
9
+ fontStyle?: "normal" | "italic";
10
+ underline?: boolean;
11
+ textAlign?: "left" | "center" | "right";
12
+ textColor?: string;
13
+ onFontSizeChange?: (size: number) => void;
14
+ onFontFamilyChange?: (family: string) => void;
15
+ onFontWeightChange?: (weight: "normal" | "bold") => void;
16
+ onFontStyleChange?: (style: "normal" | "italic") => void;
17
+ onUnderlineChange?: (underline: boolean) => void;
18
+ onTextAlignChange?: (align: "left" | "center" | "right") => void;
19
+ onTextColorChange?: (color: string) => void;
20
+ showFontSize?: boolean;
21
+ showFontFamily?: boolean;
22
+ showTextStyle?: boolean;
23
+ showTextAlign?: boolean;
24
+ showTextColor?: boolean;
25
+ };
26
+ events: {
27
+ [evt: string]: CustomEvent<any>;
28
+ };
29
+ slots: {};
30
+ };
31
+ export type TextPropsICSectionProps = typeof __propDef.props;
32
+ export type TextPropsICSectionEvents = typeof __propDef.events;
33
+ export type TextPropsICSectionSlots = typeof __propDef.slots;
34
+ export default class TextPropsICSection extends SvelteComponentTyped<TextPropsICSectionProps, TextPropsICSectionEvents, TextPropsICSectionSlots> {
35
+ }
36
+ export {};
@@ -0,0 +1,97 @@
1
+ /**
2
+ * FrameShape - A container shape extending Fabric.js Group
3
+ *
4
+ * Features:
5
+ * - Extends fabric.Group to act as a container for child objects
6
+ * - interactive: true — children can be selected/edited directly (tldraw-like)
7
+ * - Click on a child → selects that child for individual editing
8
+ * - Click on frame background → selects the frame itself
9
+ * - ClipPath for bounding/clipping content inside frame boundaries
10
+ * - Customizable label drawn above the frame (with text wrapping, rotation support)
11
+ * - Custom controls: corner resize only, no rotation handle
12
+ * - Minimum size enforcement
13
+ * - Serialization support with custom properties
14
+ * - addObjectToFrame / removeObjectFromFrame with automatic coordinate conversion
15
+ * (Fabric's native _enterGroup/_exitGroup handles absolute ↔ group-relative transforms)
16
+ *
17
+ * Containment detection (drag in/out) is handled by Canvas.svelte's
18
+ * object:modified handler since it requires canvas-level access.
19
+ */
20
+ import { Group, Rect, type FabricObject, type Canvas as FabricCanvas } from 'fabric';
21
+ export declare const MIN_FRAME_WIDTH = 50;
22
+ export declare const MIN_FRAME_HEIGHT = 30;
23
+ export declare const DEFAULT_STROKE = "#2563eb";
24
+ export declare const FRAME_HOVER_STROKE = "#1d4ed8";
25
+ export interface FrameShapeOptions {
26
+ left?: number;
27
+ top?: number;
28
+ width?: number;
29
+ height?: number;
30
+ fill?: string;
31
+ stroke?: string;
32
+ strokeWidth?: number;
33
+ labelText?: string;
34
+ labelFontSize?: number;
35
+ labelFontFamily?: string;
36
+ labelColor?: string;
37
+ labelBackgroundColor?: string;
38
+ labelTextAlign?: 'left' | 'center' | 'right';
39
+ labelFontWeight?: string;
40
+ labelFontStyle?: string;
41
+ labelUnderline?: boolean;
42
+ }
43
+ export declare class FrameShape extends Group {
44
+ static type: string;
45
+ /**
46
+ * Serialise the frame with type = 'FrameShape' plus all custom label/style
47
+ * props so they survive a canvas.toJSON() → loadFromJSON() round-trip.
48
+ * FRAME_CUSTOM_PROPS and _objectId are always included regardless of what
49
+ * the canvas passes as propertiesToInclude.
50
+ */
51
+ toObject(propertiesToInclude?: any[]): any;
52
+ /**
53
+ * Deserialise a saved FrameShape.
54
+ * Delegates to Group.fromObject (handles enlivenObjects, FixedLayout setup,
55
+ * and subscribeTargets) then upgrades the plain Group to a proper FrameShape
56
+ * instance via Object.setPrototypeOf before re-applying the custom render.
57
+ *
58
+ * WHY setPrototypeOf:
59
+ * Group.fromObject always returns `new Group(...)` whose prototype is Group.prototype.
60
+ * After setPrototypeOf → FrameShape.prototype, the instance:
61
+ * • reports type === 'FrameShape' (via FrameShape.prototype.type)
62
+ * • calls FrameShape.prototype.toObject() on save → emits type:'FrameShape' + custom props
63
+ * • classRegistry dispatches FrameShape.fromObject on next reload
64
+ */
65
+ static fromObject(object: any, options?: any): Promise<FrameShape>;
66
+ }
67
+ export declare function createFrameShape(options?: FrameShapeOptions): Group;
68
+ export declare function setupClipPath(frame: Group): void;
69
+ export declare function updateClipPath(frame: Group): void;
70
+ export declare function updateFrameCornerRadius(frame: Group, radiusPixels: number): void;
71
+ export declare function addObjectToFrame(frame: Group, object: FabricObject, canvas: FabricCanvas): void;
72
+ export declare function removeObjectFromFrame(frame: Group, object: FabricObject, canvas: FabricCanvas): void;
73
+ export declare function isPointInFrameLabel(frame: Group, point: {
74
+ x: number;
75
+ y: number;
76
+ }): boolean;
77
+ export declare function isPointInsideFrame(frame: Group, point: {
78
+ x: number;
79
+ y: number;
80
+ }): boolean;
81
+ export declare function isPointOnFrameBorder(frame: Group, point: {
82
+ x: number;
83
+ y: number;
84
+ }): boolean;
85
+ export declare function applyFrameHitTest(frame: Group): void;
86
+ export declare function getFrameChildren(frame: Group): FabricObject[];
87
+ export declare function getFrameBackground(frame: Group): Rect | null;
88
+ export declare function updateFrameBackground(frame: Group, props: {
89
+ fill?: string;
90
+ stroke?: string;
91
+ strokeWidth?: number;
92
+ }): void;
93
+ export declare function normalizeFrameAfterScaling(frame: Group, preserveChildren?: boolean): void;
94
+ export declare function enforceFrameMinSize(frame: Group): void;
95
+ export declare const FRAME_CUSTOM_PROPS: string[];
96
+ export declare function isFrameShape(obj: any): boolean;
97
+ export declare function reapplyFrameRender(frame: Group): void;