svelte-flexiboards 0.3.2 → 0.4.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.
Files changed (49) hide show
  1. package/dist/components/flexi-add.svelte +2 -15
  2. package/dist/components/flexi-add.svelte.d.ts +0 -12
  3. package/dist/components/flexi-delete.svelte +3 -16
  4. package/dist/components/flexi-delete.svelte.d.ts +0 -12
  5. package/dist/components/flexi-grab.svelte +2 -2
  6. package/dist/components/flexi-target.svelte +8 -19
  7. package/dist/components/flexi-widget.svelte +2 -2
  8. package/dist/components/responsive-flexi-board.svelte +83 -0
  9. package/dist/components/responsive-flexi-board.svelte.d.ts +34 -0
  10. package/dist/index.d.ts +4 -1
  11. package/dist/index.js +4 -1
  12. package/dist/system/board/base.svelte.d.ts +15 -0
  13. package/dist/system/board/controller.svelte.d.ts +26 -4
  14. package/dist/system/board/controller.svelte.js +237 -28
  15. package/dist/system/board/types.d.ts +26 -0
  16. package/dist/system/grid/base.svelte.d.ts +9 -0
  17. package/dist/system/grid/base.svelte.js +12 -1
  18. package/dist/system/grid/flow-grid.svelte.js +105 -36
  19. package/dist/system/grid/free-grid.svelte.d.ts +6 -2
  20. package/dist/system/grid/free-grid.svelte.js +139 -20
  21. package/dist/system/misc/deleter.svelte.d.ts +0 -4
  22. package/dist/system/misc/deleter.svelte.js +1 -6
  23. package/dist/system/portal.js +0 -1
  24. package/dist/system/responsive/base.svelte.d.ts +46 -0
  25. package/dist/system/responsive/base.svelte.js +1 -0
  26. package/dist/system/responsive/controller.svelte.d.ts +78 -0
  27. package/dist/system/responsive/controller.svelte.js +264 -0
  28. package/dist/system/responsive/index.d.ts +16 -0
  29. package/dist/system/responsive/index.js +36 -0
  30. package/dist/system/responsive/types.d.ts +56 -0
  31. package/dist/system/responsive/types.js +1 -0
  32. package/dist/system/shared/event-bus.d.ts +3 -1
  33. package/dist/system/shared/utils.svelte.d.ts +2 -0
  34. package/dist/system/shared/utils.svelte.js +39 -22
  35. package/dist/system/target/controller.svelte.d.ts +7 -2
  36. package/dist/system/target/controller.svelte.js +103 -30
  37. package/dist/system/types.d.ts +13 -2
  38. package/dist/system/widget/base.svelte.d.ts +40 -1
  39. package/dist/system/widget/base.svelte.js +84 -2
  40. package/dist/system/widget/controller.svelte.d.ts +4 -1
  41. package/dist/system/widget/controller.svelte.js +106 -17
  42. package/dist/system/widget/events.js +10 -3
  43. package/dist/system/widget/interpolation-utils.d.ts +14 -0
  44. package/dist/system/widget/interpolation-utils.js +32 -0
  45. package/dist/system/widget/interpolator.svelte.d.ts +2 -1
  46. package/dist/system/widget/interpolator.svelte.js +63 -22
  47. package/dist/system/widget/triggers.svelte.js +1 -1
  48. package/dist/system/widget/types.d.ts +51 -6
  49. package/package.json +1 -1
@@ -1,11 +1,11 @@
1
1
  import { tick, untrack } from 'svelte';
2
- import { FreeFormFlexiGrid } from '../grid/free-grid.svelte.js';
3
2
  import { FlexiGrid, FlowFlexiGrid } from '../grid/index.js';
4
3
  import { FlexiWidgetController } from '../widget/base.svelte.js';
5
4
  import { InternalFlexiWidgetController } from '../widget/controller.svelte.js';
6
5
  import { getFlexiEventBus } from '../shared/event-bus.js';
7
6
  import { SvelteSet } from 'svelte/reactivity';
8
7
  import { getPointerService } from '../shared/utils.svelte.js';
8
+ import { FreeFormFlexiGrid } from '../grid/free-grid.svelte.js';
9
9
  export class InternalFlexiTargetController {
10
10
  #widgets = $state(new SvelteSet());
11
11
  #orderedWidgets = $state([]);
@@ -32,10 +32,16 @@ export class InternalFlexiTargetController {
32
32
  x: 0,
33
33
  y: 0
34
34
  });
35
+ // Raw (fractional) cell position - used for resize snapping (rounds instead of floors)
36
+ #rawMouseCellPosition = $state({
37
+ x: 0,
38
+ y: 0
39
+ });
35
40
  key;
36
41
  #grid = null;
37
42
  #preGrabSnapshot = null;
38
43
  #gridSnapshot = null;
44
+ registry = $derived(this.provider?.registry);
39
45
  #targetConfig = $state(undefined);
40
46
  #pointerService = getPointerService();
41
47
  config = $derived({
@@ -122,10 +128,20 @@ export class InternalFlexiTargetController {
122
128
  }
123
129
  createWidget(config) {
124
130
  const [x, y, width, height] = [config.x, config.y, config.width, config.height];
131
+ let widgetConfig = {};
132
+ if (config.type) {
133
+ if (!this.registry?.[config.type]) {
134
+ console.warn('createWidget(): widget with type ', config.type, ' not found in registry, it will be missing settings.');
135
+ }
136
+ else {
137
+ widgetConfig = { ...this.registry[config.type] };
138
+ }
139
+ }
125
140
  const widget = new InternalFlexiWidgetController({
126
- config,
141
+ config: { ...widgetConfig, ...config },
127
142
  provider: this.provider,
128
- target: this
143
+ target: this,
144
+ type: config.type
129
145
  });
130
146
  // If the widget can't be added, it's probably a collision.
131
147
  if (!this.#tryAddWidget(widget, x, y, width, height)) {
@@ -160,19 +176,46 @@ export class InternalFlexiTargetController {
160
176
  if (deleted && 'destroy' in widget) {
161
177
  widget.destroy();
162
178
  }
179
+ // Clear the pre-grab snapshot only if the deleted widget is the one being grabbed/resized
180
+ // (prevents the board's safety net from restoring a widget that was intentionally removed)
181
+ // Note: We don't clear if a different widget is deleted during a grab operation
182
+ if (this.actionWidget?.widget === widget) {
183
+ this.forgetPreGrabSnapshot();
184
+ }
163
185
  // Apply any deferred operations like row collapsing now that the operation is complete
164
186
  this.applyGridPostCompletionOperations();
165
187
  return deleted;
166
188
  }
167
189
  /**
168
190
  * Imports a layout of widgets into this target, replacing any existing widgets.
191
+ * Widgets with types not found in the registry will be skipped with a warning.
169
192
  * @param layout The layout to import.
170
193
  */
171
194
  importLayout(layout) {
195
+ if (!this.registry) {
196
+ console.warn('importLayout(): no registry provided, cannot import layout. Provide a registry to the FlexiBoard component.');
197
+ return;
198
+ }
172
199
  this.widgets.clear();
173
200
  this.grid.clear();
174
- for (const config of layout) {
175
- this.createWidget(config);
201
+ for (const entry of layout) {
202
+ if (!entry.type) {
203
+ console.warn('importLayout(): skipping widget entry with no type:', entry);
204
+ continue;
205
+ }
206
+ if (!this.registry[entry.type]) {
207
+ console.warn(`importLayout(): widget type "${entry.type}" not found in registry, skipping widget.`);
208
+ continue;
209
+ }
210
+ this.createWidget({
211
+ id: entry.id,
212
+ type: entry.type,
213
+ x: entry.x,
214
+ y: entry.y,
215
+ width: entry.width,
216
+ height: entry.height,
217
+ metadata: entry.metadata
218
+ });
176
219
  }
177
220
  }
178
221
  /**
@@ -180,41 +223,51 @@ export class InternalFlexiTargetController {
180
223
  * @returns The layout of widgets.
181
224
  */
182
225
  exportLayout() {
183
- const result = [];
184
- // Likely much more information than needed, but we've got it.
185
- for (const widget of this.widgets) {
186
- result.push({
187
- component: widget.component,
188
- componentProps: widget.componentProps,
189
- snippet: widget.snippet,
190
- width: widget.width,
191
- height: widget.height,
192
- x: widget.x,
193
- y: widget.y,
194
- draggable: widget.draggable,
195
- resizability: widget.resizability,
196
- className: widget.className,
197
- metadata: widget.metadata
198
- });
199
- }
200
- return result;
226
+ // Prevent reactive subscriptions onto exportLayout directly - they should use onLayoutChange.
227
+ return untrack(() => {
228
+ const result = [];
229
+ // Likely much more information than needed, but we've got it.
230
+ for (const widget of this.internalWidgets) {
231
+ if (!widget.type) {
232
+ console.warn('exportLayout(): widget has no type, it will be skipped.');
233
+ continue;
234
+ }
235
+ const entry = {
236
+ type: widget.type,
237
+ width: widget.width,
238
+ height: widget.height,
239
+ x: widget.x,
240
+ y: widget.y,
241
+ metadata: widget.metadata
242
+ };
243
+ // Only include id if user provided one
244
+ if (widget.userProvidedId) {
245
+ entry.id = widget.userProvidedId;
246
+ }
247
+ result.push(entry);
248
+ }
249
+ return result;
250
+ });
201
251
  }
202
- #createShadow(of) {
252
+ #createShadow(of, action) {
203
253
  const shadow = new InternalFlexiWidgetController({
204
254
  config: {
205
255
  width: of.width,
206
256
  height: of.height,
207
257
  component: of.component,
208
258
  draggable: of.draggable,
259
+ draggability: of.draggability,
209
260
  resizability: of.resizability,
210
261
  snippet: of.snippet,
211
262
  className: of.className,
212
- componentProps: of.componentProps
263
+ componentProps: of.componentProps,
264
+ metadata: of.metadata
213
265
  },
214
266
  provider: this.provider,
215
267
  target: this,
216
268
  isShadow: true
217
269
  });
270
+ shadow.interpolationAnimationHint = action === 'resize' ? 'resize' : 'move';
218
271
  return shadow;
219
272
  }
220
273
  // Events
@@ -240,6 +293,9 @@ export class InternalFlexiTargetController {
240
293
  forgetPreGrabSnapshot() {
241
294
  this.#preGrabSnapshot = null;
242
295
  }
296
+ hasPreGrabSnapshot() {
297
+ return this.#preGrabSnapshot !== null;
298
+ }
243
299
  applyGridPostCompletionOperations() {
244
300
  this.grid.applyPostCompletionOperations();
245
301
  }
@@ -259,6 +315,9 @@ export class InternalFlexiTargetController {
259
315
  // Try to formally place the widget in the grid, which will also serve as a final check that
260
316
  // the drop is possible.
261
317
  const result = this.#tryAddWidget(widget, x, y, width, height);
318
+ if (!result) {
319
+ widget.isBeingDropped = false;
320
+ }
262
321
  // Apply any deferred operations like row collapsing now that the operation is complete
263
322
  if (result) {
264
323
  this.applyGridPostCompletionOperations();
@@ -269,6 +328,8 @@ export class InternalFlexiTargetController {
269
328
  }
270
329
  onmousegridcellmove(event) {
271
330
  this.#updateMouseCellPosition(event.cellX, event.cellY);
331
+ this.#rawMouseCellPosition.x = event.rawCellX;
332
+ this.#rawMouseCellPosition.y = event.rawCellY;
272
333
  this.#updateDropzoneWidget();
273
334
  }
274
335
  onWidgetGrabbed(event) {
@@ -394,9 +455,10 @@ export class InternalFlexiTargetController {
394
455
  const grid = this.grid;
395
456
  // Take a snapshot of the grid so we can restore its state if the hover stops.
396
457
  this.#gridSnapshot = grid.takeSnapshot();
458
+ grid.setDragSnapshot(this.#gridSnapshot);
397
459
  // TODO: Not sure why the $effect.root is needed, but it is.
398
460
  this.#dropzoneWidgetDestroy = $effect.root(() => {
399
- this.dropzoneWidget = this.#createShadow(this.actionWidget.widget);
461
+ this.dropzoneWidget = this.#createShadow(this.actionWidget.widget, this.actionWidget.action);
400
462
  });
401
463
  let [x, y, width, height] = this.#getDropzoneLocation(this.actionWidget);
402
464
  const added = this.grid.tryPlaceWidget(this.dropzoneWidget, x, y, width, height, true);
@@ -455,8 +517,12 @@ export class InternalFlexiTargetController {
455
517
  }
456
518
  #getNewWidgetHeightAndWidth(widget, mouseCellPosition) {
457
519
  const grid = this.grid;
458
- let newWidth = Math.max(1, mouseCellPosition.x - widget.x);
459
- let newHeight = Math.max(1, mouseCellPosition.y - widget.y);
520
+ // Use raw (fractional) position with rounding for smoother resize snapping
521
+ // This makes the widget snap to the next cell when more than halfway through
522
+ const roundedX = Math.round(this.#rawMouseCellPosition.x);
523
+ const roundedY = Math.round(this.#rawMouseCellPosition.y);
524
+ let newWidth = Math.max(1, Math.min(widget.maxWidth, Math.max(roundedX - widget.x, widget.minWidth)));
525
+ let newHeight = Math.max(1, Math.min(widget.maxHeight, Math.max(roundedY - widget.y, widget.minHeight)));
460
526
  // If the widget is in a flow layout, then they can't change their flow axis dimensions.
461
527
  // NEXT: show this visually to the user by faking the "horizontal"/"vertical" resizable modes.
462
528
  if (this.config.layout.type == 'flow' && this.config.layout.flowAxis == 'row') {
@@ -479,17 +545,21 @@ export class InternalFlexiTargetController {
479
545
  if (!this.dropzoneWidget) {
480
546
  return;
481
547
  }
548
+ const dropzoneWidget = this.dropzoneWidget;
482
549
  const grid = this.grid;
483
- grid.removeWidget(this.dropzoneWidget);
550
+ grid.removeWidget(dropzoneWidget);
484
551
  if (this.#isDropzoneWidgetAdded) {
485
- this.widgets.delete(this.dropzoneWidget);
552
+ this.widgets.delete(dropzoneWidget);
486
553
  this.#isDropzoneWidgetAdded = false;
487
554
  }
488
555
  grid.restoreFromSnapshot(this.#gridSnapshot);
556
+ grid.clearDragSnapshot();
489
557
  this.#gridSnapshot = null;
490
558
  this.dropzoneWidget = null;
491
559
  this.#dropzoneWidgetDestroy?.();
492
560
  this.#dropzoneWidgetDestroy = null;
561
+ // Clean up the shadow widget's event subscriptions and reset counters
562
+ dropzoneWidget.destroy();
493
563
  }
494
564
  // State-related getters and setters
495
565
  /**
@@ -543,6 +613,9 @@ export class InternalFlexiTargetController {
543
613
  set dropzoneWidget(value) {
544
614
  this.#dropzoneWidget.value = value;
545
615
  }
616
+ get shouldRenderDropzoneWidget() {
617
+ return this.#isDropzoneWidgetAdded && !!this.dropzoneWidget;
618
+ }
546
619
  get widgets() {
547
620
  return this.#widgets;
548
621
  }
@@ -1,9 +1,10 @@
1
1
  import type { InternalFlexiTargetController } from './target/controller.svelte.js';
2
2
  import type { FlexiAddController, InternalFlexiAddController } from './misc/adder.svelte.js';
3
3
  import type { FlexiWidgetController } from './widget/base.svelte.js';
4
- import type { FlexiWidgetConfiguration } from './widget/types.js';
5
4
  import type { InternalFlexiBoardController } from './board/controller.svelte.js';
6
5
  import type { InternalFlexiWidgetController } from './widget/controller.svelte.js';
6
+ import type { FlexiLayout } from './board/types.js';
7
+ import type { InternalResponsiveFlexiBoardController } from './responsive/controller.svelte.js';
7
8
  export type ProxiedValue<T> = {
8
9
  value: T;
9
10
  };
@@ -16,6 +17,7 @@ export type FlexiCommonProps<T> = {
16
17
  onfirstcreate?: (instance: T) => void;
17
18
  };
18
19
  export type WidgetResizability = 'none' | 'horizontal' | 'vertical' | 'both';
20
+ export type WidgetDraggability = 'none' | 'movable' | 'full';
19
21
  export type WidgetGrabAction = {
20
22
  action: 'grab';
21
23
  widget: InternalFlexiWidgetController;
@@ -112,6 +114,8 @@ export type TargetEvent = {
112
114
  export type MouseGridCellMoveEvent = {
113
115
  cellX: number;
114
116
  cellY: number;
117
+ rawCellX: number;
118
+ rawCellY: number;
115
119
  };
116
120
  export type GrabbedWidgetMouseEvent = {
117
121
  widget: FlexiWidgetController;
@@ -119,7 +123,6 @@ export type GrabbedWidgetMouseEvent = {
119
123
  export type HoveredTargetEvent = {
120
124
  target: InternalFlexiTargetController;
121
125
  };
122
- export type FlexiSavedLayout = Record<string, FlexiWidgetConfiguration[]>;
123
126
  export type WidgetActionEvent = (PointerEvent & {
124
127
  isKeyboard?: undefined;
125
128
  }) | (KeyboardEvent & {
@@ -127,3 +130,11 @@ export type WidgetActionEvent = (PointerEvent & {
127
130
  clientX: number;
128
131
  clientY: number;
129
132
  });
133
+ export type BoardLayoutChangeEvent = {
134
+ board: InternalFlexiBoardController;
135
+ layout: FlexiLayout;
136
+ breakpoint?: string;
137
+ };
138
+ export type ResponsiveLayoutImportEvent = {
139
+ responsiveController: InternalResponsiveFlexiBoardController;
140
+ };
@@ -1,6 +1,6 @@
1
1
  import type { Component } from 'svelte';
2
2
  import type { FlexiTargetController } from '../target/index.js';
3
- import type { WidgetAction, WidgetResizability } from '../types.js';
3
+ import type { WidgetAction, WidgetDraggability, WidgetResizability } from '../types.js';
4
4
  import { type FlexiWidgetChildrenSnippet, type FlexiWidgetClasses, type FlexiWidgetConstructorParams } from './types.js';
5
5
  export declare class FlexiWidgetController {
6
6
  #private;
@@ -43,9 +43,23 @@ export declare class FlexiWidgetController {
43
43
  set currentAction(value: WidgetAction | null);
44
44
  /**
45
45
  * Whether the widget is draggable.
46
+ * @deprecated Prefer the use of `draggability` instead for finer control. When `true`, `draggability = 'full'`, when `false`, `draggability = 'none'`.
46
47
  */
47
48
  get draggable(): boolean;
48
49
  set draggable(value: boolean);
50
+ /**
51
+ * The draggability of the widget.
52
+ */
53
+ get draggability(): WidgetDraggability;
54
+ set draggability(value: WidgetDraggability);
55
+ /**
56
+ * Whether the widget can be grabbed.
57
+ */
58
+ get isGrabbable(): boolean;
59
+ /**
60
+ * Whether the widget can be moved.
61
+ */
62
+ get isMovable(): boolean;
49
63
  /**
50
64
  * The resizability of the widget.
51
65
  */
@@ -122,6 +136,31 @@ export declare class FlexiWidgetController {
122
136
  */
123
137
  get isBeingDropped(): boolean;
124
138
  set isBeingDropped(value: boolean);
139
+ /**
140
+ * The minimum width of the widget in units.
141
+ */
142
+ get minWidth(): number;
143
+ /**
144
+ * The minimum height of the widget in units.
145
+ */
146
+ get minHeight(): number;
147
+ /**
148
+ * The maximum width of the widget in units.
149
+ */
150
+ get maxWidth(): number;
151
+ /**
152
+ * The maximum height of the widget in units.
153
+ */
154
+ get maxHeight(): number;
155
+ /**
156
+ * The user-provided stable identifier for this widget, if any.
157
+ * This is used for persistence and layout import/export.
158
+ */
159
+ get userProvidedId(): string | undefined;
160
+ /**
161
+ * The type of this widget (registry key for looking up configuration).
162
+ */
163
+ get type(): string | undefined;
125
164
  }
126
165
  export type WidgetStateData = {
127
166
  currentAction: WidgetAction | null;
@@ -1,6 +1,5 @@
1
1
  import { FlexiControllerBase } from '../base.svelte.js';
2
2
  import { defaultTriggerConfig } from './types.js';
3
- import { WidgetMoveInterpolator } from './interpolator.svelte.js';
4
3
  export class FlexiWidgetController {
5
4
  /**
6
5
  * The target this widget is under. This is not defined if the widget has not yet been dropped in the board.
@@ -47,6 +46,14 @@ export class FlexiWidgetController {
47
46
  this.#targetWidgetDefaults?.draggable ??
48
47
  this.#providerWidgetDefaults?.draggable ??
49
48
  true,
49
+ draggability: this.#rawConfig.draggability ??
50
+ this.#targetWidgetDefaults?.draggability ??
51
+ this.#providerWidgetDefaults?.draggability ??
52
+ // This is not pretty but will ensure backwards compatibility with the old `draggable` property.
53
+ (this.#rawConfig.draggable !== undefined ? (this.#rawConfig.draggable ? 'full' : 'none') : undefined) ??
54
+ (this.#targetWidgetDefaults?.draggable !== undefined ? (this.#targetWidgetDefaults?.draggable ? 'full' : 'none') : undefined) ??
55
+ (this.#providerWidgetDefaults?.draggable !== undefined ? (this.#providerWidgetDefaults?.draggable ? 'full' : 'none') : undefined) ??
56
+ 'full',
50
57
  className: this.#rawConfig.className ??
51
58
  this.#targetWidgetDefaults?.className ??
52
59
  this.#providerWidgetDefaults?.className,
@@ -62,7 +69,23 @@ export class FlexiWidgetController {
62
69
  resizeTrigger: this.#rawConfig.resizeTrigger ??
63
70
  this.#targetWidgetDefaults?.resizeTrigger ??
64
71
  this.#providerWidgetDefaults?.resizeTrigger ??
65
- defaultTriggerConfig
72
+ defaultTriggerConfig,
73
+ minWidth: this.#rawConfig.minWidth ??
74
+ this.#targetWidgetDefaults?.minWidth ??
75
+ this.#providerWidgetDefaults?.minWidth ??
76
+ 1,
77
+ minHeight: this.#rawConfig.minHeight ??
78
+ this.#targetWidgetDefaults?.minHeight ??
79
+ this.#providerWidgetDefaults?.minHeight ??
80
+ 1,
81
+ maxWidth: this.#rawConfig.maxWidth ??
82
+ this.#targetWidgetDefaults?.maxWidth ??
83
+ this.#providerWidgetDefaults?.maxWidth ??
84
+ Infinity,
85
+ maxHeight: this.#rawConfig.maxHeight ??
86
+ this.#targetWidgetDefaults?.maxHeight ??
87
+ this.#providerWidgetDefaults?.maxHeight ??
88
+ Infinity,
66
89
  });
67
90
  // Reactive state properties - single source of truth
68
91
  backingState = $state();
@@ -97,6 +120,7 @@ export class FlexiWidgetController {
97
120
  }
98
121
  /**
99
122
  * Whether the widget is draggable.
123
+ * @deprecated Prefer the use of `draggability` instead for finer control. When `true`, `draggability = 'full'`, when `false`, `draggability = 'none'`.
100
124
  */
101
125
  get draggable() {
102
126
  return this.#config.draggable;
@@ -104,6 +128,27 @@ export class FlexiWidgetController {
104
128
  set draggable(value) {
105
129
  this.#rawConfig.draggable = value;
106
130
  }
131
+ /**
132
+ * The draggability of the widget.
133
+ */
134
+ get draggability() {
135
+ return this.#config.draggability;
136
+ }
137
+ set draggability(value) {
138
+ this.#rawConfig.draggability = value;
139
+ }
140
+ /**
141
+ * Whether the widget can be grabbed.
142
+ */
143
+ get isGrabbable() {
144
+ return this.#config.draggability == 'full';
145
+ }
146
+ /**
147
+ * Whether the widget can be moved.
148
+ */
149
+ get isMovable() {
150
+ return this.#config.draggability == 'movable' || this.#config.draggability == 'full';
151
+ }
107
152
  /**
108
153
  * The resizability of the widget.
109
154
  */
@@ -228,4 +273,41 @@ export class FlexiWidgetController {
228
273
  set isBeingDropped(value) {
229
274
  this.backingState.isBeingDropped = value;
230
275
  }
276
+ /**
277
+ * The minimum width of the widget in units.
278
+ */
279
+ get minWidth() {
280
+ return this.#config.minWidth;
281
+ }
282
+ /**
283
+ * The minimum height of the widget in units.
284
+ */
285
+ get minHeight() {
286
+ return this.#config.minHeight;
287
+ }
288
+ /**
289
+ * The maximum width of the widget in units.
290
+ */
291
+ get maxWidth() {
292
+ return this.#config.maxWidth;
293
+ }
294
+ /**
295
+ * The maximum height of the widget in units.
296
+ */
297
+ get maxHeight() {
298
+ return this.#config.maxHeight;
299
+ }
300
+ /**
301
+ * The user-provided stable identifier for this widget, if any.
302
+ * This is used for persistence and layout import/export.
303
+ */
304
+ get userProvidedId() {
305
+ return undefined; // Overridden in InternalFlexiWidgetController
306
+ }
307
+ /**
308
+ * The type of this widget (registry key for looking up configuration).
309
+ */
310
+ get type() {
311
+ return undefined; // Overridden in InternalFlexiWidgetController
312
+ }
231
313
  }
@@ -1,7 +1,7 @@
1
1
  import type { WidgetDroppedEvent, WidgetEvent, WidgetGrabbedEvent, WidgetResizingEvent } from '../types.js';
2
2
  import { FlexiWidgetController } from './base.svelte.js';
3
3
  import type { InternalFlexiTargetController } from '../target/controller.svelte.js';
4
- import { WidgetMoveInterpolator } from './interpolator.svelte.js';
4
+ import { WidgetMoveInterpolator, type WidgetMovementAnimation } from './interpolator.svelte.js';
5
5
  import type { FlexiWidgetConstructorParams } from './types.js';
6
6
  import type { InternalFlexiBoardController } from '../board/controller.svelte.js';
7
7
  export declare class InternalFlexiWidgetController extends FlexiWidgetController {
@@ -10,6 +10,8 @@ export declare class InternalFlexiWidgetController extends FlexiWidgetController
10
10
  internalTarget?: InternalFlexiTargetController;
11
11
  provider: InternalFlexiBoardController;
12
12
  mounted: boolean;
13
+ get type(): string | undefined;
14
+ get userProvidedId(): string | undefined;
13
15
  /**
14
16
  * The styling to apply to the widget.
15
17
  */
@@ -57,4 +59,5 @@ export declare class InternalFlexiWidgetController extends FlexiWidgetController
57
59
  * Whether the widget should draw a placeholder widget in the DOM.
58
60
  */
59
61
  get shouldDrawPlaceholder(): boolean;
62
+ set interpolationAnimationHint(value: WidgetMovementAnimation | null);
60
63
  }