@widgetic/editor 4.0.3 → 4.0.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.
|
@@ -59,6 +59,17 @@
|
|
|
59
59
|
export let mobileViewportWidth: number = 390;
|
|
60
60
|
export let mobileViewportHeight: number = 667;
|
|
61
61
|
export let enableResize: boolean = false;
|
|
62
|
+
/** Drag only via the preview chrome header (keeps iframe interactive). */
|
|
63
|
+
export let enableDrag: boolean = false;
|
|
64
|
+
/** W×H number inputs in the chrome header, live-synced with resize. */
|
|
65
|
+
export let showDimensionInputs: boolean = false;
|
|
66
|
+
/**
|
|
67
|
+
* concentric — always resize from center (widget-creator / Canvas).
|
|
68
|
+
* corner — opposite corner fixed; hold Alt for concentric (composition page).
|
|
69
|
+
*/
|
|
70
|
+
export let resizeMode: 'concentric' | 'corner' = 'concentric';
|
|
71
|
+
/** Initial / reset placement inside the stage. */
|
|
72
|
+
export let defaultStageAlign: 'center' | 'right-half' = 'center';
|
|
62
73
|
/** Observe stage size and fill it (desktop modal) */
|
|
63
74
|
export let autoFitStage: boolean = false;
|
|
64
75
|
/** No padding/border on preview-content-container — matches widget-creator Edit panel */
|
|
@@ -87,32 +98,165 @@
|
|
|
87
98
|
const MIN_WIDTH = 120;
|
|
88
99
|
const MIN_HEIGHT = 80;
|
|
89
100
|
const HANDLE_MARGIN = 6;
|
|
90
|
-
/** Inset padding so resize handles
|
|
91
|
-
const RESIZE_PAD_X =
|
|
92
|
-
const RESIZE_PAD_TOP =
|
|
93
|
-
const RESIZE_PAD_BOTTOM =
|
|
101
|
+
/** Inset padding so resize handles stay inside bounds */
|
|
102
|
+
const RESIZE_PAD_X = 12;
|
|
103
|
+
const RESIZE_PAD_TOP = 12;
|
|
104
|
+
const RESIZE_PAD_BOTTOM = 12;
|
|
105
|
+
const PREVIEW_CHROME_HEADER_H = 36;
|
|
106
|
+
const PREVIEW_STROKE = '#3b82f6';
|
|
107
|
+
const PREVIEW_STROKE_ALT = '#f97316';
|
|
94
108
|
let isResizing = false;
|
|
109
|
+
let isDraggingPreview = false;
|
|
110
|
+
/** Alt held — keeps orange stroke on composition even after pointerup. */
|
|
111
|
+
let isAltKeyDown = false;
|
|
112
|
+
let previewShellLeft = 0;
|
|
113
|
+
let previewShellTop = 0;
|
|
114
|
+
let previewPositionReady = false;
|
|
115
|
+
let widthInputValue = '';
|
|
116
|
+
let heightInputValue = '';
|
|
95
117
|
let contentContainerEl: HTMLElement | null = null;
|
|
96
118
|
let resizeStageEl: HTMLElement | null = null;
|
|
97
119
|
let stageResizeObserver: ResizeObserver | null = null;
|
|
98
120
|
|
|
121
|
+
$: showPreviewChrome = enableDrag || showDimensionInputs;
|
|
122
|
+
$: chromeHeaderHeight = showPreviewChrome ? PREVIEW_CHROME_HEADER_H : 0;
|
|
123
|
+
$: if (resizeWidth != null) widthInputValue = String(Math.round(resizeWidth));
|
|
124
|
+
$: if (resizeHeight != null) heightInputValue = String(Math.round(resizeHeight));
|
|
125
|
+
$: showSymmetricalStroke = resizeMode === 'corner' && isAltKeyDown;
|
|
126
|
+
$: previewStrokeColor = showSymmetricalStroke ? PREVIEW_STROKE_ALT : PREVIEW_STROKE;
|
|
127
|
+
|
|
99
128
|
export function getResizeLimits(): { maxWidth: number; maxHeight: number } {
|
|
100
129
|
const stageEl = resizeStageEl ?? contentContainerEl;
|
|
130
|
+
const chromeH = showPreviewChrome ? PREVIEW_CHROME_HEADER_H : 0;
|
|
101
131
|
if (stageEl) {
|
|
102
132
|
return {
|
|
103
133
|
maxWidth: Math.max(MIN_WIDTH, stageEl.clientWidth),
|
|
104
|
-
maxHeight: Math.max(MIN_HEIGHT, stageEl.clientHeight),
|
|
134
|
+
maxHeight: Math.max(MIN_HEIGHT, stageEl.clientHeight - chromeH),
|
|
105
135
|
};
|
|
106
136
|
}
|
|
107
137
|
if (hasFixedBounds && boundsWidth != null && boundsHeight != null) {
|
|
108
138
|
return {
|
|
109
139
|
maxWidth: Math.max(MIN_WIDTH, boundsWidth - RESIZE_PAD_X * 2),
|
|
110
|
-
maxHeight: Math.max(MIN_HEIGHT, boundsHeight - RESIZE_PAD_TOP - RESIZE_PAD_BOTTOM),
|
|
140
|
+
maxHeight: Math.max(MIN_HEIGHT, boundsHeight - RESIZE_PAD_TOP - RESIZE_PAD_BOTTOM - chromeH),
|
|
111
141
|
};
|
|
112
142
|
}
|
|
113
143
|
return { maxWidth: 720, maxHeight: 420 };
|
|
114
144
|
}
|
|
115
145
|
|
|
146
|
+
function clampDimension(value: number, min: number, max: number): number {
|
|
147
|
+
return Math.max(min, Math.min(max, value));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function computeDefaultShellPosition(
|
|
151
|
+
stageWidth: number,
|
|
152
|
+
stageHeight: number,
|
|
153
|
+
shellWidth: number,
|
|
154
|
+
shellHeight: number
|
|
155
|
+
): { left: number; top: number } {
|
|
156
|
+
const maxLeft = Math.max(RESIZE_PAD_X, stageWidth - shellWidth - RESIZE_PAD_X);
|
|
157
|
+
const maxTop = Math.max(RESIZE_PAD_TOP, stageHeight - shellHeight - RESIZE_PAD_BOTTOM);
|
|
158
|
+
if (defaultStageAlign === 'right-half') {
|
|
159
|
+
const rightHalfCenterX = stageWidth * 0.75;
|
|
160
|
+
const left = clampDimension(rightHalfCenterX - shellWidth / 2, RESIZE_PAD_X, maxLeft);
|
|
161
|
+
const top = clampDimension((stageHeight - shellHeight) / 2, RESIZE_PAD_TOP, maxTop);
|
|
162
|
+
return { left, top };
|
|
163
|
+
}
|
|
164
|
+
const left = clampDimension((stageWidth - shellWidth) / 2, RESIZE_PAD_X, maxLeft);
|
|
165
|
+
const top = clampDimension((stageHeight - shellHeight) / 2, RESIZE_PAD_TOP, maxTop);
|
|
166
|
+
return { left, top };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Dock / reset preview shell to defaultStageAlign. */
|
|
170
|
+
export function resetPreviewPosition(): void {
|
|
171
|
+
const stageEl = resizeStageEl ?? contentContainerEl;
|
|
172
|
+
if (!stageEl || resizeWidth == null || resizeHeight == null) return;
|
|
173
|
+
if (stageEl.clientWidth < 40 || stageEl.clientHeight < 40) {
|
|
174
|
+
requestAnimationFrame(() => resetPreviewPosition());
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const shellWidth = resizeWidth;
|
|
178
|
+
const shellHeight = resizeHeight + chromeHeaderHeight;
|
|
179
|
+
const next = computeDefaultShellPosition(
|
|
180
|
+
stageEl.clientWidth,
|
|
181
|
+
stageEl.clientHeight,
|
|
182
|
+
shellWidth,
|
|
183
|
+
shellHeight
|
|
184
|
+
);
|
|
185
|
+
previewShellLeft = next.left;
|
|
186
|
+
previewShellTop = next.top;
|
|
187
|
+
previewPositionReady = true;
|
|
188
|
+
console.log('[WidgetPreview] Reset preview position', {
|
|
189
|
+
align: defaultStageAlign,
|
|
190
|
+
left: previewShellLeft,
|
|
191
|
+
top: previewShellTop,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function ensurePreviewPosition(): void {
|
|
196
|
+
if (previewPositionReady) return;
|
|
197
|
+
resetPreviewPosition();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function applyDimensionFromInput(dimension: 'width' | 'height', raw: string) {
|
|
201
|
+
const { maxWidth, maxHeight } = getResizeLimits();
|
|
202
|
+
const parsed = Number.parseInt(raw, 10);
|
|
203
|
+
if (!Number.isFinite(parsed)) return;
|
|
204
|
+
if (dimension === 'width') {
|
|
205
|
+
resizeWidth = clampDimension(parsed, MIN_WIDTH, maxWidth);
|
|
206
|
+
widthInputValue = String(Math.round(resizeWidth));
|
|
207
|
+
} else {
|
|
208
|
+
resizeHeight = clampDimension(parsed, MIN_HEIGHT, maxHeight);
|
|
209
|
+
heightInputValue = String(Math.round(resizeHeight));
|
|
210
|
+
}
|
|
211
|
+
if (resizeWidth != null && resizeHeight != null) {
|
|
212
|
+
dispatch('resize', { width: resizeWidth, height: resizeHeight });
|
|
213
|
+
}
|
|
214
|
+
console.log('[WidgetPreview] Dimension input', { dimension, value: parsed });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function startPreviewDrag(event: PointerEvent) {
|
|
218
|
+
if (!enableDrag || isResizing) return;
|
|
219
|
+
const target = event.target as HTMLElement | null;
|
|
220
|
+
if (target?.closest('input, button, a, label')) return;
|
|
221
|
+
event.preventDefault();
|
|
222
|
+
event.stopPropagation();
|
|
223
|
+
isDraggingPreview = true;
|
|
224
|
+
const startX = event.clientX;
|
|
225
|
+
const startY = event.clientY;
|
|
226
|
+
const startLeft = previewShellLeft;
|
|
227
|
+
const startTop = previewShellTop;
|
|
228
|
+
const stageEl = resizeStageEl ?? contentContainerEl;
|
|
229
|
+
const shellWidth = resizeWidth ?? 0;
|
|
230
|
+
const shellHeight = (resizeHeight ?? 0) + chromeHeaderHeight;
|
|
231
|
+
|
|
232
|
+
function onPointerMove(e: PointerEvent) {
|
|
233
|
+
if (!stageEl) return;
|
|
234
|
+
const maxLeft = Math.max(RESIZE_PAD_X, stageEl.clientWidth - shellWidth - RESIZE_PAD_X);
|
|
235
|
+
const maxTop = Math.max(RESIZE_PAD_TOP, stageEl.clientHeight - shellHeight - RESIZE_PAD_BOTTOM);
|
|
236
|
+
previewShellLeft = clampDimension(startLeft + (e.clientX - startX), RESIZE_PAD_X, maxLeft);
|
|
237
|
+
previewShellTop = clampDimension(startTop + (e.clientY - startY), RESIZE_PAD_TOP, maxTop);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function endDrag() {
|
|
241
|
+
isDraggingPreview = false;
|
|
242
|
+
document.removeEventListener('pointermove', onPointerMove);
|
|
243
|
+
document.removeEventListener('pointerup', endDrag);
|
|
244
|
+
document.removeEventListener('pointercancel', endDrag);
|
|
245
|
+
document.body.style.removeProperty('user-select');
|
|
246
|
+
document.body.style.removeProperty('cursor');
|
|
247
|
+
console.log('[WidgetPreview] Preview drag end', {
|
|
248
|
+
left: previewShellLeft,
|
|
249
|
+
top: previewShellTop,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
document.body.style.userSelect = 'none';
|
|
254
|
+
document.body.style.cursor = 'move';
|
|
255
|
+
document.addEventListener('pointermove', onPointerMove);
|
|
256
|
+
document.addEventListener('pointerup', endDrag);
|
|
257
|
+
document.addEventListener('pointercancel', endDrag);
|
|
258
|
+
}
|
|
259
|
+
|
|
116
260
|
/** Firefox/Chrome skip painting a cross-origin iframe that started at opacity 0 or inside a CSS filter until a reflow (opening DevTools). */
|
|
117
261
|
let iframePaintNudgeGeneration = 0;
|
|
118
262
|
export function nudgeIframePaint(): void {
|
|
@@ -475,22 +619,65 @@
|
|
|
475
619
|
event.preventDefault();
|
|
476
620
|
event.stopPropagation();
|
|
477
621
|
isResizing = true;
|
|
622
|
+
if (event.altKey) isAltKeyDown = true;
|
|
478
623
|
const startX = event.clientX;
|
|
479
624
|
const startY = event.clientY;
|
|
480
|
-
|
|
625
|
+
const startLeft = previewShellLeft;
|
|
626
|
+
const startTop = previewShellTop;
|
|
481
627
|
const { maxWidth, maxHeight } = getResizeLimits();
|
|
482
628
|
const startWidth = resizeWidth ?? maxWidth;
|
|
483
629
|
const startHeight = resizeHeight ?? maxHeight;
|
|
630
|
+
const alwaysConcentric = resizeMode === 'concentric';
|
|
484
631
|
|
|
485
632
|
function onPointerMove(e: PointerEvent) {
|
|
486
|
-
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
633
|
+
if (e.altKey) isAltKeyDown = true;
|
|
634
|
+
const useConcentric = alwaysConcentric || isAltKeyDown || e.altKey;
|
|
635
|
+
const dx = e.clientX - startX;
|
|
636
|
+
const dy = e.clientY - startY;
|
|
637
|
+
|
|
638
|
+
if (useConcentric) {
|
|
639
|
+
const deltaW = signX !== 0 ? dx * signX * 2 : 0;
|
|
640
|
+
const deltaH = signY !== 0 ? dy * signY * 2 : 0;
|
|
641
|
+
const nextW = clampDimension(startWidth + deltaW, MIN_WIDTH, maxWidth);
|
|
642
|
+
const nextH = clampDimension(startHeight + deltaH, MIN_HEIGHT, maxHeight);
|
|
643
|
+
resizeWidth = nextW;
|
|
644
|
+
resizeHeight = nextH;
|
|
645
|
+
previewShellLeft = startLeft + (startWidth - nextW) / 2;
|
|
646
|
+
previewShellTop = startTop + (startHeight - nextH) / 2;
|
|
647
|
+
} else {
|
|
648
|
+
let nextW = startWidth;
|
|
649
|
+
let nextH = startHeight;
|
|
650
|
+
let nextLeft = startLeft;
|
|
651
|
+
let nextTop = startTop;
|
|
652
|
+
|
|
653
|
+
if (signX === -1) {
|
|
654
|
+
nextW = clampDimension(startWidth - dx, MIN_WIDTH, maxWidth);
|
|
655
|
+
nextLeft = startLeft + (startWidth - nextW);
|
|
656
|
+
} else if (signX === 1) {
|
|
657
|
+
nextW = clampDimension(startWidth + dx, MIN_WIDTH, maxWidth);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (signY === -1) {
|
|
661
|
+
nextH = clampDimension(startHeight - dy, MIN_HEIGHT, maxHeight);
|
|
662
|
+
nextTop = startTop + (startHeight - nextH);
|
|
663
|
+
} else if (signY === 1) {
|
|
664
|
+
nextH = clampDimension(startHeight + dy, MIN_HEIGHT, maxHeight);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
resizeWidth = nextW;
|
|
668
|
+
resizeHeight = nextH;
|
|
669
|
+
previewShellLeft = nextLeft;
|
|
670
|
+
previewShellTop = nextTop;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (resizeWidth != null && resizeHeight != null) {
|
|
674
|
+
dispatch('resize', { width: resizeWidth, height: resizeHeight });
|
|
675
|
+
}
|
|
490
676
|
}
|
|
491
677
|
|
|
492
678
|
function endResize() {
|
|
493
679
|
isResizing = false;
|
|
680
|
+
// Keep orange stroke while Alt remains held (composition corner mode).
|
|
494
681
|
document.removeEventListener('pointermove', onPointerMove);
|
|
495
682
|
document.removeEventListener('pointerup', endResize);
|
|
496
683
|
document.removeEventListener('pointercancel', endResize);
|
|
@@ -502,7 +689,14 @@
|
|
|
502
689
|
}
|
|
503
690
|
|
|
504
691
|
document.body.style.userSelect = 'none';
|
|
505
|
-
document.body.style.cursor =
|
|
692
|
+
document.body.style.cursor =
|
|
693
|
+
signX !== 0 && signY !== 0
|
|
694
|
+
? signX * signY > 0
|
|
695
|
+
? 'nwse-resize'
|
|
696
|
+
: 'nesw-resize'
|
|
697
|
+
: signX !== 0
|
|
698
|
+
? 'ew-resize'
|
|
699
|
+
: 'ns-resize';
|
|
506
700
|
document.addEventListener('pointermove', onPointerMove);
|
|
507
701
|
document.addEventListener('pointerup', endResize);
|
|
508
702
|
document.addEventListener('pointercancel', endResize);
|
|
@@ -510,6 +704,7 @@
|
|
|
510
704
|
|
|
511
705
|
$: if (enableResize && contentContainerEl && (resizeWidth != null || resizeHeight != null)) {
|
|
512
706
|
clampResizeToLimits();
|
|
707
|
+
ensurePreviewPosition();
|
|
513
708
|
}
|
|
514
709
|
|
|
515
710
|
$: if (autoFitStage && resizeStageEl) {
|
|
@@ -523,6 +718,27 @@
|
|
|
523
718
|
|
|
524
719
|
onMount(() => {
|
|
525
720
|
window.addEventListener('message', handleMessage);
|
|
721
|
+
const onAltDown = (e: KeyboardEvent) => {
|
|
722
|
+
if (e.key === 'Alt' || e.code === 'AltLeft' || e.code === 'AltRight') {
|
|
723
|
+
isAltKeyDown = true;
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
const onAltUp = (e: KeyboardEvent) => {
|
|
727
|
+
if (e.key === 'Alt' || e.code === 'AltLeft' || e.code === 'AltRight') {
|
|
728
|
+
isAltKeyDown = false;
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
const onWindowBlur = () => {
|
|
732
|
+
isAltKeyDown = false;
|
|
733
|
+
};
|
|
734
|
+
window.addEventListener('keydown', onAltDown);
|
|
735
|
+
window.addEventListener('keyup', onAltUp);
|
|
736
|
+
window.addEventListener('blur', onWindowBlur);
|
|
737
|
+
return () => {
|
|
738
|
+
window.removeEventListener('keydown', onAltDown);
|
|
739
|
+
window.removeEventListener('keyup', onAltUp);
|
|
740
|
+
window.removeEventListener('blur', onWindowBlur);
|
|
741
|
+
};
|
|
526
742
|
});
|
|
527
743
|
|
|
528
744
|
onDestroy(() => {
|
|
@@ -636,12 +852,66 @@
|
|
|
636
852
|
{:else if effectiveSrc || (mode === 'standalone' && isResolvingUrl)}
|
|
637
853
|
<div
|
|
638
854
|
bind:this={resizeStageEl}
|
|
639
|
-
class="{enableResize ? 'preview-resize-stage-ct absolute inset-0 overflow-hidden
|
|
855
|
+
class="{enableResize ? 'preview-resize-stage-ct absolute inset-0 overflow-hidden' : 'preview-resize-stage-ct relative flex items-center justify-center w-full h-full'}"
|
|
640
856
|
>
|
|
641
857
|
<div
|
|
642
|
-
class="preview-iframe-
|
|
643
|
-
|
|
858
|
+
class="preview-iframe-shell relative flex flex-col {enableResize ? 'preview-iframe-shell-positioned' : ''}"
|
|
859
|
+
class:preview-iframe-shell-symmetrical={showSymmetricalStroke}
|
|
860
|
+
style="{enableResize
|
|
861
|
+
? `position: absolute; left: ${previewShellLeft}px; top: ${previewShellTop}px; width: ${resizeWidth ? resizeWidth + 'px' : '100%'};`
|
|
862
|
+
: `width: ${resizeWidth ? resizeWidth + 'px' : '100%'}; height: ${resizeHeight ? resizeHeight + 'px' : '100%'};`} transition: {isResizing || isDraggingPreview ? 'none' : 'width 0.2s, height 0.2s, left 0.15s, top 0.15s'};"
|
|
644
863
|
>
|
|
864
|
+
{#if showPreviewChrome}
|
|
865
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
866
|
+
<div
|
|
867
|
+
class="preview-chrome-header flex items-center gap-2 h-9 px-2 border border-b-0 rounded-t-md bg-white select-none {enableDrag ? 'cursor-move' : ''}"
|
|
868
|
+
style="border-color: {previewStrokeColor};"
|
|
869
|
+
onpointerdown={startPreviewDrag}
|
|
870
|
+
>
|
|
871
|
+
{#if enableDrag}
|
|
872
|
+
<span class="preview-chrome-drag-grip shrink-0 text-gray-400 text-xs leading-none" title="Drag preview" aria-hidden="true">⠿</span>
|
|
873
|
+
{/if}
|
|
874
|
+
<span class="preview-chrome-title text-[11px] font-semibold uppercase tracking-wide text-gray-500 shrink-0">Preview</span>
|
|
875
|
+
{#if showDimensionInputs}
|
|
876
|
+
<div class="preview-chrome-dimensions ml-auto flex items-center gap-1.5">
|
|
877
|
+
<label class="preview-dim-label flex items-center gap-1 text-[10px] font-semibold text-gray-500">
|
|
878
|
+
<span>W</span>
|
|
879
|
+
<input
|
|
880
|
+
type="number"
|
|
881
|
+
min={MIN_WIDTH}
|
|
882
|
+
class="preview-dim-input w-14 h-6 rounded border border-gray-300 bg-white px-1 text-[11px] text-gray-800 focus:outline-none focus:border-blue-400 focus-visible:outline-none"
|
|
883
|
+
value={widthInputValue}
|
|
884
|
+
oninput={(e) => {
|
|
885
|
+
widthInputValue = e.currentTarget.value;
|
|
886
|
+
applyDimensionFromInput('width', e.currentTarget.value);
|
|
887
|
+
}}
|
|
888
|
+
onpointerdown={(e) => e.stopPropagation()}
|
|
889
|
+
/>
|
|
890
|
+
</label>
|
|
891
|
+
<span class="text-[10px] text-gray-400">×</span>
|
|
892
|
+
<label class="preview-dim-label flex items-center gap-1 text-[10px] font-semibold text-gray-500">
|
|
893
|
+
<span>H</span>
|
|
894
|
+
<input
|
|
895
|
+
type="number"
|
|
896
|
+
min={MIN_HEIGHT}
|
|
897
|
+
class="preview-dim-input w-14 h-6 rounded border border-gray-300 bg-white px-1 text-[11px] text-gray-800 focus:outline-none focus:border-blue-400 focus-visible:outline-none"
|
|
898
|
+
value={heightInputValue}
|
|
899
|
+
oninput={(e) => {
|
|
900
|
+
heightInputValue = e.currentTarget.value;
|
|
901
|
+
applyDimensionFromInput('height', e.currentTarget.value);
|
|
902
|
+
}}
|
|
903
|
+
onpointerdown={(e) => e.stopPropagation()}
|
|
904
|
+
/>
|
|
905
|
+
</label>
|
|
906
|
+
</div>
|
|
907
|
+
{/if}
|
|
908
|
+
</div>
|
|
909
|
+
{/if}
|
|
910
|
+
|
|
911
|
+
<div
|
|
912
|
+
class="preview-iframe-wrapper relative"
|
|
913
|
+
style="width: 100%; height: {resizeHeight ? resizeHeight + 'px' : '100%'};"
|
|
914
|
+
>
|
|
645
915
|
{#if showResizeHandles}
|
|
646
916
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
647
917
|
<div class="preview-resize-handle preview-resize-handle-tl" onpointerdown={(e) => startResize(e, -1, -1)}></div>
|
|
@@ -653,7 +923,7 @@
|
|
|
653
923
|
<div class="preview-resize-handle preview-resize-handle-br" onpointerdown={(e) => startResize(e, 1, 1)}></div>
|
|
654
924
|
{/if}
|
|
655
925
|
|
|
656
|
-
{#if resizeWidth || resizeHeight}
|
|
926
|
+
{#if (resizeWidth || resizeHeight) && !showDimensionInputs}
|
|
657
927
|
<div
|
|
658
928
|
class="widget-preview-dimension-badge absolute left-1/2 -translate-x-1/2 text-[10px] bg-black/70 px-1.5 py-px rounded text-gray-300 select-none pointer-events-none z-20 whitespace-nowrap"
|
|
659
929
|
style="top: calc(100% + 4px);"
|
|
@@ -662,7 +932,10 @@
|
|
|
662
932
|
</div>
|
|
663
933
|
{/if}
|
|
664
934
|
|
|
665
|
-
<div
|
|
935
|
+
<div
|
|
936
|
+
class="preview-iframe-container relative w-full h-full overflow-hidden {compactChrome && !enableResize ? '' : showUrlBar || mode === 'controlled' || enableResize ? 'border' : ''}"
|
|
937
|
+
style={enableResize ? `border-color: ${previewStrokeColor}; border-radius: ${showPreviewChrome ? '0 0 6px 6px' : '6px'};` : ''}
|
|
938
|
+
>
|
|
666
939
|
{#if mode === 'standalone' && iframeError}
|
|
667
940
|
<div class="widget-preview-error-ct absolute inset-0 flex items-center justify-center bg-white z-10 rounded">
|
|
668
941
|
<div class="widget-preview-error-content-ct flex flex-col items-center gap-2 text-center px-4">
|
|
@@ -756,6 +1029,7 @@
|
|
|
756
1029
|
<GenerateLoader overlay title="Generating code…" subtitle="" size="lg" />
|
|
757
1030
|
{/if}
|
|
758
1031
|
</div>
|
|
1032
|
+
</div>
|
|
759
1033
|
</div>
|
|
760
1034
|
</div>
|
|
761
1035
|
|
|
@@ -784,10 +1058,10 @@
|
|
|
784
1058
|
{/if}
|
|
785
1059
|
</div>
|
|
786
1060
|
|
|
787
|
-
{#if isResizing}
|
|
1061
|
+
{#if isResizing || isDraggingPreview}
|
|
788
1062
|
<div
|
|
789
|
-
class="widget-preview-resize-guard-ct fixed inset-0 z-999999
|
|
790
|
-
style="background: transparent;"
|
|
1063
|
+
class="widget-preview-resize-guard-ct fixed inset-0 z-999999"
|
|
1064
|
+
style="background: transparent; cursor: {isDraggingPreview ? 'move' : 'nwse-resize'};"
|
|
791
1065
|
aria-hidden="true"
|
|
792
1066
|
></div>
|
|
793
1067
|
{/if}
|
|
@@ -799,38 +1073,35 @@
|
|
|
799
1073
|
|
|
800
1074
|
.preview-resize-handle {
|
|
801
1075
|
position: absolute;
|
|
802
|
-
width:
|
|
803
|
-
height:
|
|
1076
|
+
width: 10px;
|
|
1077
|
+
height: 10px;
|
|
804
1078
|
z-index: 30;
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
.preview-resize-handle::before,
|
|
809
|
-
.preview-resize-handle::after {
|
|
810
|
-
content: '';
|
|
811
|
-
position: absolute;
|
|
812
|
-
background: #94a3b8;
|
|
1079
|
+
box-sizing: border-box;
|
|
1080
|
+
background: #ffffff;
|
|
1081
|
+
border: 1.5px solid #3b82f6;
|
|
813
1082
|
border-radius: 1px;
|
|
814
1083
|
}
|
|
815
1084
|
|
|
816
|
-
.preview-
|
|
817
|
-
.preview-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
.preview-resize-handle-tr { top: -4px; right: -4px; cursor: nesw-resize; }
|
|
821
|
-
.preview-resize-handle-tr::before { top: 0; right: 0; width: 10px; height: 2px; }
|
|
822
|
-
.preview-resize-handle-tr::after { top: 0; right: 0; width: 2px; height: 10px; }
|
|
1085
|
+
.preview-iframe-shell-symmetrical .preview-resize-handle,
|
|
1086
|
+
.preview-iframe-shell-symmetrical .preview-iframe-container {
|
|
1087
|
+
border-color: #f97316 !important;
|
|
1088
|
+
}
|
|
823
1089
|
|
|
824
|
-
.preview-
|
|
825
|
-
.preview-
|
|
826
|
-
.preview-
|
|
1090
|
+
.preview-chrome-header button:focus,
|
|
1091
|
+
.preview-chrome-header button:focus-visible,
|
|
1092
|
+
.preview-chrome-header input:focus,
|
|
1093
|
+
.preview-chrome-header input:focus-visible {
|
|
1094
|
+
outline: none;
|
|
1095
|
+
box-shadow: none;
|
|
1096
|
+
}
|
|
827
1097
|
|
|
828
|
-
.preview-resize-handle-
|
|
829
|
-
.preview-resize-handle-
|
|
830
|
-
.preview-resize-handle-
|
|
1098
|
+
.preview-resize-handle-tl { top: -5px; left: -5px; cursor: nwse-resize; }
|
|
1099
|
+
.preview-resize-handle-tr { top: -5px; right: -5px; cursor: nesw-resize; }
|
|
1100
|
+
.preview-resize-handle-bl { bottom: -5px; left: -5px; cursor: nesw-resize; }
|
|
1101
|
+
.preview-resize-handle-br { bottom: -5px; right: -5px; cursor: nwse-resize; }
|
|
831
1102
|
|
|
832
|
-
.preview-resize-handle:hover
|
|
833
|
-
|
|
834
|
-
|
|
1103
|
+
.preview-resize-handle:hover {
|
|
1104
|
+
background: #eff6ff;
|
|
1105
|
+
border-color: #2563eb;
|
|
835
1106
|
}
|
|
836
1107
|
</style>
|
|
@@ -43,6 +43,13 @@ declare const WidgetPreview: $$__sveltets_2_IsomorphicComponent<{
|
|
|
43
43
|
mobileViewportWidth?: number;
|
|
44
44
|
mobileViewportHeight?: number;
|
|
45
45
|
enableResize?: boolean;
|
|
46
|
+
/** Drag only via the preview chrome header (keeps iframe interactive). */ enableDrag?: boolean;
|
|
47
|
+
/** W×H number inputs in the chrome header, live-synced with resize. */ showDimensionInputs?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* concentric — always resize from center (widget-creator / Canvas).
|
|
50
|
+
* corner — opposite corner fixed; hold Alt for concentric (composition page).
|
|
51
|
+
*/ resizeMode?: "concentric" | "corner";
|
|
52
|
+
/** Initial / reset placement inside the stage. */ defaultStageAlign?: "center" | "right-half";
|
|
46
53
|
/** Observe stage size and fill it (desktop modal) */ autoFitStage?: boolean;
|
|
47
54
|
/** No padding/border on preview-content-container — matches widget-creator Edit panel */ compactChrome?: boolean;
|
|
48
55
|
/** Fixed preview stage size; inner iframe resizes within these bounds */ boundsWidth?: number | null;
|
|
@@ -54,6 +61,7 @@ declare const WidgetPreview: $$__sveltets_2_IsomorphicComponent<{
|
|
|
54
61
|
maxWidth: number;
|
|
55
62
|
maxHeight: number;
|
|
56
63
|
};
|
|
64
|
+
resetPreviewPosition?: () => void;
|
|
57
65
|
nudgeIframePaint?: () => void;
|
|
58
66
|
fitToContainer?: () => void;
|
|
59
67
|
sendMessageToPreview?: (message: Record<string, unknown>) => void;
|
|
@@ -90,6 +98,7 @@ declare const WidgetPreview: $$__sveltets_2_IsomorphicComponent<{
|
|
|
90
98
|
maxWidth: number;
|
|
91
99
|
maxHeight: number;
|
|
92
100
|
};
|
|
101
|
+
resetPreviewPosition: () => void;
|
|
93
102
|
nudgeIframePaint: () => void;
|
|
94
103
|
fitToContainer: () => void;
|
|
95
104
|
sendMessageToPreview: (message: Record<string, unknown>) => void;
|