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