@widgetic/canvas 0.5.4 → 0.5.6

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.
@@ -36,17 +36,30 @@ declare const __propDef: {
36
36
  onWidgetShapeDeleting?: ((widgetId: string) => Promise<boolean>) | null;
37
37
  onWidgetRename?: ((widgetId: string, newName: string) => void) | null;
38
38
  onRefreshScreenshot?: ((widgetId: string) => void) | null;
39
+ /** Host persists canvases.primary_widget_id after Canvas paints the red stroke. */ onSetPrimaryWidget?: ((widgetId: string) => void) | null;
39
40
  getWidgetName?: ((widgetId: string) => string | null) | null;
40
41
  /** Returns true when widgetId still exists in the host widget list. */ isWidgetKnown?: ((widgetId: string) => boolean) | null;
41
42
  showLogs?: boolean;
42
43
  shapeFillColor?: () => string;
43
44
  deselectAll?: () => void;
45
+ duplicateSelectedObjects?: () => Promise<void>;
46
+ deleteSelectedObjects?: () => Promise<void>;
44
47
  markSelectedAsDuplicating?: () => void;
45
48
  linkObjectToWidget?: (fabricObject: any, widgetId: string, widgetName?: string | null) => void;
46
49
  linkFrameToWidget?: (objectId: string, widgetId: string) => void;
47
50
  replaceObjectWithWidgetImage?: (objectId: string, widgetId: string, screenshotDataUrl: string) => Promise<string | null>;
48
51
  updateWidgetImage?: (widgetId: string, newScreenshotDataUrl: string) => void;
49
52
  selectWidgetShape?: (widgetId: string) => boolean;
53
+ getWidgetShapeScreenRect?: (widgetId: string) => {
54
+ left: number;
55
+ top: number;
56
+ width: number;
57
+ height: number;
58
+ viewportLeft: number;
59
+ viewportTop: number;
60
+ viewportWidth: number;
61
+ viewportHeight: number;
62
+ } | null;
50
63
  getWidgetShapeDataUrl?: (widgetId: string) => string | null;
51
64
  addWidgetShape?: (widgetId: string, widgetName?: string, gridIndex?: number, totalCount?: number, previewImageUrl?: string | null) => string | null;
52
65
  getLinkedWidgetIds?: () => Set<string>;
@@ -64,6 +77,8 @@ declare const __propDef: {
64
77
  onSelectArrowTool?: () => void;
65
78
  onSelectShapeTool?: (shapeType: "rect" | "circle" | "circle-true" | "triangle" | "arrow") => void;
66
79
  onSelectFrameTool?: () => void;
80
+ setPrimaryWidgetShape?: (widgetId: string) => boolean;
81
+ getPrimaryWidgetId?: () => string | null;
67
82
  onSelectImageTool?: () => void;
68
83
  onSelectPanTool?: () => void;
69
84
  zoomIn?: () => void;
@@ -71,10 +86,11 @@ declare const __propDef: {
71
86
  zoomTo?: (percent: number) => void;
72
87
  zoomReset?: () => void;
73
88
  zoomFit?: () => void;
74
- onGroupSelection?: () => void;
89
+ onGroupSelection?: () => string | null;
75
90
  onUngroupSelection?: () => void;
76
91
  onSelectAllObjects?: () => void;
77
92
  onDeselectAllObjects?: () => void;
93
+ saveNow?: () => void;
78
94
  undo?: () => Promise<void>;
79
95
  redo?: () => Promise<void>;
80
96
  saveCanvas?: () => string | null;
@@ -98,12 +114,24 @@ export type CanvasSlots = typeof __propDef.slots;
98
114
  export default class Canvas extends SvelteComponentTyped<CanvasProps, CanvasEvents, CanvasSlots> {
99
115
  get shapeFillColor(): () => string;
100
116
  get deselectAll(): () => void;
117
+ get duplicateSelectedObjects(): () => Promise<void>;
118
+ get deleteSelectedObjects(): () => Promise<void>;
101
119
  get markSelectedAsDuplicating(): () => void;
102
120
  get linkObjectToWidget(): (fabricObject: any, widgetId: string, widgetName?: string | null) => void;
103
121
  get linkFrameToWidget(): (objectId: string, widgetId: string) => void;
104
122
  get replaceObjectWithWidgetImage(): (objectId: string, widgetId: string, screenshotDataUrl: string) => Promise<string | null>;
105
123
  get updateWidgetImage(): (widgetId: string, newScreenshotDataUrl: string) => void;
106
124
  get selectWidgetShape(): (widgetId: string) => boolean;
125
+ get getWidgetShapeScreenRect(): (widgetId: string) => {
126
+ left: number;
127
+ top: number;
128
+ width: number;
129
+ height: number;
130
+ viewportLeft: number;
131
+ viewportTop: number;
132
+ viewportWidth: number;
133
+ viewportHeight: number;
134
+ } | null;
107
135
  get getWidgetShapeDataUrl(): (widgetId: string) => string | null;
108
136
  get addWidgetShape(): (widgetId: string, widgetName?: string, gridIndex?: number, totalCount?: number, previewImageUrl?: string | null) => string | null;
109
137
  get getLinkedWidgetIds(): () => Set<string>;
@@ -121,6 +149,8 @@ export default class Canvas extends SvelteComponentTyped<CanvasProps, CanvasEven
121
149
  get onSelectArrowTool(): () => void;
122
150
  get onSelectShapeTool(): (shapeType: "rect" | "circle" | "circle-true" | "triangle" | "arrow") => void;
123
151
  get onSelectFrameTool(): () => void;
152
+ get setPrimaryWidgetShape(): (widgetId: string) => boolean;
153
+ get getPrimaryWidgetId(): () => string | null;
124
154
  get onSelectImageTool(): () => void;
125
155
  get onSelectPanTool(): () => void;
126
156
  get zoomIn(): () => void;
@@ -128,10 +158,11 @@ export default class Canvas extends SvelteComponentTyped<CanvasProps, CanvasEven
128
158
  get zoomTo(): (percent: number) => void;
129
159
  get zoomReset(): () => void;
130
160
  get zoomFit(): () => void;
131
- get onGroupSelection(): () => void;
161
+ get onGroupSelection(): () => string | null;
132
162
  get onUngroupSelection(): () => void;
133
163
  get onSelectAllObjects(): () => void;
134
164
  get onDeselectAllObjects(): () => void;
165
+ get saveNow(): () => void;
135
166
  get undo(): () => Promise<void>;
136
167
  get redo(): () => Promise<void>;
137
168
  get saveCanvas(): () => string | null;
@@ -974,6 +974,11 @@ button:disabled {
974
974
  color: white !important;
975
975
  box-shadow: 0 2px 8px rgba(26, 58, 42, 0.25);
976
976
  }
977
+ .wdg-tool-active :global(svg),
978
+ .wdg-tool-active :global(svg *) {
979
+ color: white !important;
980
+ stroke: currentColor !important;
981
+ }
977
982
  .wdg-tool-active:hover {
978
983
  background: #244b36 !important;
979
984
  color: white !important;
@@ -241,13 +241,26 @@
241
241
 
242
242
  .panel-btn.active {
243
243
  background: var(--main-color, #314F2F);
244
- color: white;
244
+ color: white !important;
245
245
  border-color: var(--main-color, #314F2F);
246
246
  }
247
247
 
248
+ /* Host apps (widget-creator / site) may set a global svg color; force icons to follow the button. */
249
+ .panel-btn.active :global(svg),
250
+ .panel-btn.active :global(svg *) {
251
+ color: white !important;
252
+ stroke: currentColor !important;
253
+ }
254
+
248
255
  .panel-btn.active:hover {
249
256
  background: var(--main-color-hover, #263d24);
250
- color: white;
257
+ color: white !important;
258
+ }
259
+
260
+ .panel-btn.active:hover :global(svg),
261
+ .panel-btn.active:hover :global(svg *) {
262
+ color: white !important;
263
+ stroke: currentColor !important;
251
264
  }
252
265
 
253
266
  .panel-btn:disabled {
@@ -27,6 +27,11 @@
27
27
  export let onStrokeTypeChange: (type: 'solid' | 'dashed' | 'dotted') => void = () => {};
28
28
  export let onOpacityChange: (opacity: number) => void = () => {};
29
29
  export let onFillOpacityChange: (opacity: number) => void = () => {};
30
+ export let onGroupFillChange: (color: string) => void = () => {};
31
+ export let onGroupFillOpacityChange: (opacity: number) => void = () => {};
32
+ export let onGroupStrokeChange: (color: string) => void = () => {};
33
+ export let onGroupStrokeWidthChange: (width: number) => void = () => {};
34
+ export let onGroupStrokeTypeChange: (type: 'solid' | 'dashed' | 'dotted') => void = () => {};
30
35
  export let onBackgroundColorChange: (color: string) => void = () => {};
31
36
  export let onBackgroundOpacityChange: (opacity: number) => void = () => {};
32
37
 
@@ -81,6 +86,7 @@
81
86
 
82
87
  // Callback: fires when the user requests a screenshot refresh for the linked widget
83
88
  export let onRefreshScreenshot: ((widgetId: string) => void) | null = null;
89
+ export let onSetAsPrimary: ((widgetId: string) => void) | null = null;
84
90
 
85
91
  // Widget name resolver: given a widgetId, returns the widget name (or null)
86
92
  export let getWidgetName: ((widgetId: string) => string | null) | null = null;
@@ -133,6 +139,7 @@
133
139
  // FrameShape objects have type === 'FrameShape', not 'group', so isGroup is always false for them.
134
140
  // isMultiSelect is only true for plain Groups and ActiveSelections (not Frames).
135
141
  $: isMultiSelect = (isGroup && !isFrame) || isActiveSelection;
142
+ $: isPlainGroup = isGroup && !isFrame;
136
143
  $: isLine = objectType === 'line';
137
144
  $: isImage = objectType === 'image' || customType === 'image';
138
145
  // Widget link: true when the selected object has already been converted to a widget
@@ -220,7 +227,6 @@
220
227
  // For Frames (Group-based), read fill/stroke from custom properties (_frameFill, _frameStroke)
221
228
  // because the actual fill/stroke is on the background Rect child, not the Group itself
222
229
  $: currentFill = isFrame ? (selectedObject?._frameFill ?? '#FFFFFF') : (selectedObject?.fill ?? '#FFFFFF');
223
- // Fill opacity is stored as custom property _fillOpacity (0-1), display as percentage
224
230
  $: currentFillOpacity = Math.round((selectedObject?._fillOpacity ?? 1) * 100);
225
231
  $: currentStroke = isFrame ? (selectedObject?._frameStroke ?? '#000000') : (selectedObject?.stroke ?? '#000000');
226
232
  $: currentStrokeWidth = isFrame ? (selectedObject?._frameStrokeWidth ?? 1) : (selectedObject?.strokeWidth ?? 1);
@@ -238,6 +244,11 @@
238
244
  ? (selectedObject as any)?._frameStrokeDashArray
239
245
  : selectedObject?.strokeDashArray
240
246
  );
247
+ $: groupFill = selectedObject?._groupFill ?? '#FFFFFF';
248
+ $: groupFillOpacity = Math.round((selectedObject?._groupFillOpacity ?? 1) * 100);
249
+ $: groupStroke = selectedObject?._groupStroke ?? '#000000';
250
+ $: groupStrokeWidth = selectedObject?._groupStrokeWidth ?? 0;
251
+ $: groupStrokeType = getStrokeType((selectedObject as any)?._groupStrokeDashArray);
241
252
 
242
253
  // Text properties - for groups, get from first textbox
243
254
  $: firstTextboxInGroup = (selectedObject?.type === 'group' || selectedObject?.type === 'activeSelection' || selectedObject?.type === 'activeselection')
@@ -538,6 +549,20 @@
538
549
  Refresh Widget Preview
539
550
  </button>
540
551
  {/if}
552
+ {#if onSetAsPrimary && widgetId}
553
+ {#if selectedObject?._isPrimaryWidget}
554
+ <span class="primary-widget-badge">Primary widget</span>
555
+ {:else}
556
+ <button
557
+ class="set-primary-btn"
558
+ type="button"
559
+ title="Use this widget as the canvas primary (red stroke)"
560
+ onclick={() => onSetAsPrimary && widgetId && onSetAsPrimary(widgetId)}
561
+ >
562
+ Set as primary
563
+ </button>
564
+ {/if}
565
+ {/if}
541
566
  {/if}
542
567
  {:else}
543
568
  <span class="prop-label">Convert to Widget</span>
@@ -1115,6 +1140,81 @@
1115
1140
  {/if}
1116
1141
  {/if}
1117
1142
 
1143
+ <!-- ==================== GROUP BOX (chrome around children, like Frame) ==================== -->
1144
+ {#if isPlainGroup && !isWidgetImage}
1145
+ <div class="section-divider">
1146
+ <span class="section-title">Group Box</span>
1147
+ </div>
1148
+ {#if wireframeMode || !isMinimized}
1149
+ <div class="prop-section">
1150
+ <ColorIC
1151
+ label="Group Fill Color"
1152
+ value={groupFill || '#FFFFFF'}
1153
+ onChange={onGroupFillChange}
1154
+ showPicker={!wireframeMode}
1155
+ presets={wireframeMode ? wireframeColorValues : null}
1156
+ />
1157
+ </div>
1158
+ {/if}
1159
+ <SliderUnitIC
1160
+ label="Group Fill Opacity"
1161
+ value={groupFillOpacity}
1162
+ min={0}
1163
+ max={100}
1164
+ step={1}
1165
+ unit="%"
1166
+ onChange={onGroupFillOpacityChange}
1167
+ />
1168
+ {#if wireframeMode || !isMinimized}
1169
+ <div class="prop-section">
1170
+ <ColorIC
1171
+ label="Group Stroke Color"
1172
+ value={groupStroke || '#000000'}
1173
+ onChange={onGroupStrokeChange}
1174
+ showPicker={!wireframeMode}
1175
+ presets={wireframeMode ? wireframeColorValues : null}
1176
+ />
1177
+ </div>
1178
+ {/if}
1179
+ <SliderUnitIC
1180
+ label="Group Stroke Width"
1181
+ value={groupStrokeWidth}
1182
+ min={0}
1183
+ max={20}
1184
+ step={1}
1185
+ unit="px"
1186
+ onChange={onGroupStrokeWidthChange}
1187
+ />
1188
+ {#if !isMinimized}
1189
+ <div class="prop-section">
1190
+ <span class="prop-label">Group Stroke Type</span>
1191
+ <div class="stroke-type-buttons">
1192
+ <button
1193
+ class="stroke-type-btn {groupStrokeType === 'solid' ? 'active' : ''}"
1194
+ onclick={() => onGroupStrokeTypeChange('solid')}
1195
+ >
1196
+ <span class="stroke-preview solid"></span>
1197
+ Solid
1198
+ </button>
1199
+ <button
1200
+ class="stroke-type-btn {groupStrokeType === 'dashed' ? 'active' : ''}"
1201
+ onclick={() => onGroupStrokeTypeChange('dashed')}
1202
+ >
1203
+ <span class="stroke-preview dashed"></span>
1204
+ Dashed
1205
+ </button>
1206
+ <button
1207
+ class="stroke-type-btn {groupStrokeType === 'dotted' ? 'active' : ''}"
1208
+ onclick={() => onGroupStrokeTypeChange('dotted')}
1209
+ >
1210
+ <span class="stroke-preview dotted"></span>
1211
+ Dotted
1212
+ </button>
1213
+ </div>
1214
+ </div>
1215
+ {/if}
1216
+ {/if}
1217
+
1118
1218
  <!-- ==================== TEXT PROPERTIES (for selection/group containing textboxes or frames) ==================== -->
1119
1219
  {#if !isMinimized && hasTextboxInSelection}
1120
1220
  <div class="section-divider">
@@ -1583,6 +1683,37 @@
1583
1683
  flex-shrink: 0;
1584
1684
  }
1585
1685
 
1686
+ .set-primary-btn {
1687
+ display: flex;
1688
+ align-items: center;
1689
+ margin-top: 4px;
1690
+ padding: 3px 8px;
1691
+ font-size: 11px;
1692
+ font-weight: 500;
1693
+ color: #991b1b;
1694
+ background: #fef2f2;
1695
+ border: 1px solid #fecaca;
1696
+ border-radius: 4px;
1697
+ cursor: pointer;
1698
+ width: fit-content;
1699
+ }
1700
+ .set-primary-btn:hover {
1701
+ background: #fee2e2;
1702
+ border-color: #f87171;
1703
+ }
1704
+ .primary-widget-badge {
1705
+ display: inline-block;
1706
+ margin-top: 4px;
1707
+ padding: 3px 8px;
1708
+ font-size: 11px;
1709
+ font-weight: 600;
1710
+ color: #991b1b;
1711
+ background: #fef2f2;
1712
+ border: 1px solid #fecaca;
1713
+ border-radius: 4px;
1714
+ width: fit-content;
1715
+ }
1716
+
1586
1717
  .widget-orphan-notice {
1587
1718
  font-size: 11px;
1588
1719
  line-height: 1.45;
@@ -12,6 +12,11 @@ declare const __propDef: {
12
12
  onStrokeTypeChange?: (type: "solid" | "dashed" | "dotted") => void;
13
13
  onOpacityChange?: (opacity: number) => void;
14
14
  onFillOpacityChange?: (opacity: number) => void;
15
+ onGroupFillChange?: (color: string) => void;
16
+ onGroupFillOpacityChange?: (opacity: number) => void;
17
+ onGroupStrokeChange?: (color: string) => void;
18
+ onGroupStrokeWidthChange?: (width: number) => void;
19
+ onGroupStrokeTypeChange?: (type: "solid" | "dashed" | "dotted") => void;
15
20
  onBackgroundColorChange?: (color: string) => void;
16
21
  onBackgroundOpacityChange?: (opacity: number) => void;
17
22
  onFontSizeChange?: (size: number) => void;
@@ -44,6 +49,7 @@ declare const __propDef: {
44
49
  onWidgetClick?: ((widgetId: string) => void) | null;
45
50
  onWidgetRename?: ((widgetId: string, newName: string) => void) | null;
46
51
  onRefreshScreenshot?: ((widgetId: string) => void) | null;
52
+ onSetAsPrimary?: ((widgetId: string) => void) | null;
47
53
  getWidgetName?: ((widgetId: string) => string | null) | null;
48
54
  /** Returns false when the linked widget was deleted or moved off this canvas. */ isWidgetKnown?: ((widgetId: string) => boolean) | null;
49
55
  /** Remove the selected orphaned widget shape from the canvas. */ onRemoveOrphanedShape?: (() => void) | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@widgetic/canvas",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "type": "module",
5
5
  "svelte": "./dist/index.js",
6
6
  "module": "./dist/index.js",