@widgetic/editor 4.0.10 → 4.1.0
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.
- package/README.md +242 -0
- package/dist/components/WidgetPreview.svelte +123 -86
- package/dist/components/WidgetPreview.svelte.d.ts +8 -0
- package/dist/components/color-select/ColorSelect.svelte +4 -4
- package/dist/components/editors/CompositionEditorApp.svelte +562 -0
- package/dist/components/editors/CompositionEditorApp.svelte.d.ts +95 -0
- package/dist/components/editors/EmbedEditor.svelte +33 -22
- package/dist/components/editors/EmbedEditor.svelte.d.ts +1 -1
- package/dist/components/editors/PropsEditor.svelte +267 -850
- package/dist/components/editors/PropsEditor.svelte.d.ts +10 -3
- package/dist/components/ic/AngleInputController.svelte +1 -1
- package/dist/components/ic/BrowserInputController.svelte +58 -31
- package/dist/components/ic/ColorPickerInputController.svelte +55 -21
- package/dist/components/ic/DateFieldInputController.svelte +34 -14
- package/dist/components/ic/DatePickerInputController.svelte +37 -21
- package/dist/components/ic/DropdownInputController.svelte +21 -9
- package/dist/components/ic/DropdownInputController.svelte.d.ts +1 -1
- package/dist/components/ic/FixedPositionInputController.svelte +12 -6
- package/dist/components/ic/FontInputController.svelte +100 -76
- package/dist/components/ic/InputControllerHeader.svelte +16 -9
- package/dist/components/ic/NumberControls.svelte +4 -4
- package/dist/components/ic/NumberInputController.svelte +6 -4
- package/dist/components/ic/PositionInputController.svelte +23 -15
- package/dist/components/ic/RangeCalendarInputController.svelte +36 -20
- package/dist/components/ic/RoundedCornersInputController.svelte +39 -26
- package/dist/components/ic/SliderInputController.svelte +7 -2
- package/dist/components/ic/SwitchInputController.svelte +12 -3
- package/dist/components/ic/TagsInputController.svelte +19 -4
- package/dist/components/ic/TextAreaInputController.svelte +79 -51
- package/dist/components/ic/TextAreaPreviewInputController.svelte +27 -72
- package/dist/components/ic/TextInputController.svelte +17 -1
- package/dist/components/ic/TimeInputController.svelte +11 -4
- package/dist/components/ic/VectorInputController.svelte +101 -70
- package/dist/components/ic/shared/ItemGrid.svelte +12 -4
- package/dist/components/ic/shared/RecentItemsSection.svelte +16 -11
- package/dist/components/ic/shared/RecentItemsSection.svelte.d.ts +1 -1
- package/dist/composition/compositionApi.d.ts +95 -0
- package/dist/composition/compositionApi.js +289 -0
- package/dist/composition/compositionEditorController.d.ts +263 -0
- package/dist/composition/compositionEditorController.js +746 -0
- package/dist/composition/compositionHelpers.d.ts +74 -0
- package/dist/composition/compositionHelpers.js +405 -0
- package/dist/i18n/index.d.ts +4 -0
- package/dist/i18n/index.js +4 -0
- package/dist/i18n/labels.d.ts +15 -0
- package/dist/i18n/labels.js +57 -0
- package/dist/i18n/language.d.ts +228 -0
- package/dist/i18n/language.js +34 -0
- package/dist/i18n/languageKit.d.ts +54 -0
- package/dist/i18n/languageKit.js +102 -0
- package/dist/i18n/messages.d.ts +241 -0
- package/dist/i18n/messages.js +253 -0
- package/dist/i18n/translate.d.ts +6 -0
- package/dist/i18n/translate.js +8 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +8 -0
- package/dist/mediaLibraryContext.d.ts +10 -0
- package/dist/mediaLibraryContext.js +30 -0
- package/dist/utils/FontManager.d.ts +21 -5
- package/dist/utils/FontManager.js +62 -67
- package/dist/utils/VectorManager.d.ts +0 -1
- package/dist/utils/VectorManager.js +10 -12
- package/dist/utils/dockPlacement.d.ts +12 -0
- package/dist/utils/dockPlacement.js +13 -0
- package/dist/utils/editorStorage.d.ts +23 -0
- package/dist/utils/editorStorage.js +95 -0
- package/dist/utils/fieldDirection.d.ts +13 -0
- package/dist/utils/fieldDirection.js +25 -0
- package/dist/utils/fontUtils.js +5 -3
- package/dist/utils/htmlSafety.d.ts +21 -0
- package/dist/utils/htmlSafety.js +38 -0
- package/dist/utils/itemReorder.d.ts +90 -0
- package/dist/utils/itemReorder.js +354 -0
- package/dist/utils/recentColors.js +4 -2
- package/dist/utils/richTextPreview.d.ts +38 -0
- package/dist/utils/richTextPreview.js +207 -0
- package/dist/utils/scrollActivity.d.ts +14 -0
- package/dist/utils/scrollActivity.js +63 -0
- package/dist/utils/shadowDom.d.ts +67 -0
- package/dist/utils/shadowDom.js +211 -0
- package/dist/utils/vectorUtils.js +5 -3
- package/dist/utils/visibilityCondition.d.ts +40 -0
- package/dist/utils/visibilityCondition.js +535 -0
- package/package.json +7 -6
|
@@ -78,7 +78,20 @@
|
|
|
78
78
|
import InputControllerRenderer, { type ControllerDescriptor } from '../ic/InputControllerRenderer.svelte';
|
|
79
79
|
import MediaManager from '../../utils/MediaManager';
|
|
80
80
|
import FontManager from '../../utils/FontManager';
|
|
81
|
+
import { conditionHolds } from '../../utils/visibilityCondition';
|
|
82
|
+
import { removeEditorStorageValuesStartingWith } from '../../utils/editorStorage';
|
|
83
|
+
import { reorderableList, moveListItem, type ItemReorderOptions } from '../../utils/itemReorder';
|
|
84
|
+
import {
|
|
85
|
+
lockPointerStyle,
|
|
86
|
+
queryAllInTreeOf,
|
|
87
|
+
queryInTreeOf,
|
|
88
|
+
treeOf,
|
|
89
|
+
type SearchableTree
|
|
90
|
+
} from '../../utils/shadowDom';
|
|
81
91
|
import { config as appConfig } from '../../config';
|
|
92
|
+
import { provideEditorMediaLibrary } from '../../mediaLibraryContext';
|
|
93
|
+
import { useEditorLanguage } from '../../i18n/language';
|
|
94
|
+
import type { MediaLibraryProvider } from '@widgetic/file-browser';
|
|
82
95
|
|
|
83
96
|
/**
|
|
84
97
|
* STYLES from Widgetic Design System
|
|
@@ -123,9 +136,9 @@
|
|
|
123
136
|
export let designSchema: DesignSchemaView | null = null;
|
|
124
137
|
export let contentSchema: ContentSchemaView | undefined = undefined;
|
|
125
138
|
export let itemTemplate: any = null; // Store template for new items
|
|
126
|
-
// Tab titles
|
|
127
|
-
export let designTabTitle: string =
|
|
128
|
-
export let contentTabTitle: string =
|
|
139
|
+
// Tab titles. Without one, the editor's own word, in the language of the page.
|
|
140
|
+
export let designTabTitle: string | undefined = undefined;
|
|
141
|
+
export let contentTabTitle: string | undefined = undefined;
|
|
129
142
|
// Default tab selection
|
|
130
143
|
export let defaultTab: 'design' | 'content' = 'content';
|
|
131
144
|
|
|
@@ -151,7 +164,7 @@
|
|
|
151
164
|
export let headerContent = undefined;
|
|
152
165
|
export let headerClass = '';
|
|
153
166
|
export let dragHandleClass = 'text-grey2';
|
|
154
|
-
export let title =
|
|
167
|
+
export let title: string | undefined = undefined;
|
|
155
168
|
/** Hide the default missing-schema message for header-only editors. */
|
|
156
169
|
export let showSchemaError = true;
|
|
157
170
|
/** Optional layout wrapper moved by this editor's built-in drag and position API. */
|
|
@@ -161,6 +174,21 @@
|
|
|
161
174
|
export let initialDesignValues: Record<string, any> = {};
|
|
162
175
|
export let initialContentValues: Record<string, any> = {};
|
|
163
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Where the file controls (images, video, audio, documents) get their files from. Without it they show the
|
|
179
|
+
* Widgetic account's uploads, as they always have (configured with `setFileBrowserUploadConfig`). With a
|
|
180
|
+
* `MediaLibraryProvider` they list, upload, rename and delete through its functions instead, so a partner's own
|
|
181
|
+
* storage (a CMS, an S3 bucket, a DAM) can be used with no Widgetic storage at all.
|
|
182
|
+
*/
|
|
183
|
+
export let mediaLibrary: MediaLibraryProvider | undefined = undefined;
|
|
184
|
+
// The file controls are created from the schema several components down: give them the library through context.
|
|
185
|
+
provideEditorMediaLibrary(() => mediaLibrary);
|
|
186
|
+
|
|
187
|
+
// The words of the editor in the language of the page: the editor above this one chose it (see `provideEditorLanguage`).
|
|
188
|
+
const { t } = useEditorLanguage();
|
|
189
|
+
// The title of the bar on top: the host's own, or the editor's word; an empty title hides the bar's title.
|
|
190
|
+
$: titleLabel = title ?? $t('editor.props.title');
|
|
191
|
+
|
|
164
192
|
/**
|
|
165
193
|
* CONSTANTS
|
|
166
194
|
*/
|
|
@@ -254,18 +282,9 @@
|
|
|
254
282
|
|
|
255
283
|
// Clear font weight keys immediately when script loads
|
|
256
284
|
if (browser) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (key && key.includes('font-weight-state-')) {
|
|
261
|
-
keysToRemove.push(key);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (keysToRemove.length > 0) {
|
|
266
|
-
// console.log('🧹 [PropsEditor] Clearing font weight states on init:', keysToRemove);
|
|
267
|
-
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
268
|
-
}
|
|
285
|
+
// Only the editor's own keys are looked at: this used to walk the whole localStorage and remove every key with
|
|
286
|
+
// this text in it, and inside a customer's page that reaches keys that are the customer's.
|
|
287
|
+
removeEditorStorageValuesStartingWith('font-weight-state-');
|
|
269
288
|
}
|
|
270
289
|
|
|
271
290
|
/**
|
|
@@ -356,521 +375,71 @@
|
|
|
356
375
|
}
|
|
357
376
|
};
|
|
358
377
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
}
|
|
378
|
+
/**
|
|
379
|
+
* REORDERING CONTENT ITEMS
|
|
380
|
+
* Dragging an item's handle moves the item in the list. The dragging itself is in utils/itemReorder.ts, which is
|
|
381
|
+
* not a library because the editor can be inside a shadow root on a customer's page (see the file for why no
|
|
382
|
+
* library works there). While an item is dragged the stylesheet folds every item to one row so the whole list is
|
|
383
|
+
* within reach (an item that is open keeps its content, it is only hidden), so nothing has to be closed and
|
|
384
|
+
* opened again around a drag.
|
|
385
|
+
*/
|
|
386
|
+
let isReordering = false;
|
|
369
387
|
|
|
370
|
-
|
|
371
|
-
|
|
388
|
+
function handleReorderStart() {
|
|
389
|
+
isReordering = true;
|
|
390
|
+
}
|
|
372
391
|
|
|
392
|
+
/** An item was dropped at another place in the list: `oldIndex` and `newIndex` are its places before and after. */
|
|
393
|
+
function handleItemsReordered({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) {
|
|
373
394
|
try {
|
|
374
|
-
if (contentSchema
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const containerElement = document.querySelector('.content-items');
|
|
379
|
-
// console.log('🔄 [PropsEditor] Found content container:', containerElement);
|
|
380
|
-
|
|
381
|
-
if (containerElement instanceof HTMLElement) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (currentDraggable) {
|
|
385
|
-
// console.log('🧹 Cleaning up existing draggable instance');
|
|
386
|
-
currentDraggable.destroy();
|
|
387
|
-
currentDraggable = null;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
const { Sortable, Plugins } = await import('@shopify/draggable');
|
|
391
|
-
const { SortAnimation } = Plugins;
|
|
392
|
-
|
|
393
|
-
let dragStartTime = 0;
|
|
394
|
-
let isDragStarted = false;
|
|
395
|
-
let dragThreshold = 0; // pixels to move before considering it a drag
|
|
396
|
-
let initialMousePosition = { x: 0, y: 0 };
|
|
397
|
-
let grabOffsetY = 0;
|
|
398
|
-
let grabOffsetX = 0;
|
|
399
|
-
|
|
400
|
-
// Add forceCleanupState function
|
|
401
|
-
function forceCleanupState() {
|
|
402
|
-
// console.log('🧹 Performing emergency state cleanup');
|
|
403
|
-
|
|
404
|
-
// Clean up any existing mirror elements first
|
|
405
|
-
const mirrors = document.querySelectorAll('.draggable-mirror');
|
|
406
|
-
mirrors.forEach(mirror => {
|
|
407
|
-
if (mirror.parentNode) {
|
|
408
|
-
mirror.parentNode.removeChild(mirror);
|
|
409
|
-
}
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
// Reset all global state
|
|
413
|
-
isDragging = false;
|
|
414
|
-
isDragStarted = false;
|
|
415
|
-
document.documentElement.classList.remove('draggable--is-dragging');
|
|
416
|
-
|
|
417
|
-
// Remove dragging classes from body and document
|
|
418
|
-
document.body.classList.remove('dragging-active');
|
|
419
|
-
document.documentElement.classList.remove('dragging-active');
|
|
420
|
-
|
|
421
|
-
// Force cleanup all items
|
|
422
|
-
document.querySelectorAll('.content-item').forEach((item: Element) => {
|
|
423
|
-
if (item instanceof HTMLElement) {
|
|
424
|
-
// Remove all possible dragging classes
|
|
425
|
-
item.classList.remove('draggable-source--is-dragging');
|
|
426
|
-
item.classList.remove('draggable--over');
|
|
427
|
-
item.classList.remove('draggable--original');
|
|
428
|
-
item.classList.remove('draggable-source--placed');
|
|
429
|
-
item.classList.remove('draggable-mirror');
|
|
430
|
-
|
|
431
|
-
// Reset all possible styles
|
|
432
|
-
item.style.transform = '';
|
|
433
|
-
item.style.transition = 'none';
|
|
434
|
-
// Ensure inline order values are cleared so visual order matches DOM order
|
|
435
|
-
item.style.order = '';
|
|
436
|
-
void item.offsetHeight; // Force reflow
|
|
437
|
-
item.style.transition = '';
|
|
438
|
-
item.style.position = 'relative';
|
|
439
|
-
item.style.zIndex = '1';
|
|
440
|
-
item.style.opacity = '1';
|
|
441
|
-
item.style.visibility = 'visible';
|
|
442
|
-
item.style.pointerEvents = '';
|
|
443
|
-
item.style.cursor = 'pointer';
|
|
444
|
-
|
|
445
|
-
// Update collapsible state based on our openItems
|
|
446
|
-
const itemId = item.getAttribute('data-id');
|
|
447
|
-
const isOpen = itemId && openItems.has(itemId);
|
|
448
|
-
|
|
449
|
-
// Find the collapsible elements
|
|
450
|
-
const collapsibleRoot = item.querySelector('[data-radix-collapsible-root]');
|
|
451
|
-
const trigger = item.querySelector('[data-radix-collapsible-trigger]');
|
|
452
|
-
const content = item.querySelector('[data-radix-collapsible-content]');
|
|
453
|
-
const chevron = item.querySelector('.chevron');
|
|
454
|
-
|
|
455
|
-
// Update states
|
|
456
|
-
if (collapsibleRoot instanceof HTMLElement) {
|
|
457
|
-
collapsibleRoot.setAttribute('data-state', isOpen ? 'open' : 'closed');
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
if (trigger instanceof HTMLElement) {
|
|
461
|
-
trigger.setAttribute('data-state', isOpen ? 'open' : 'closed');
|
|
462
|
-
trigger.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
if (content instanceof HTMLElement) {
|
|
466
|
-
if (!isOpen) {
|
|
467
|
-
content.style.height = '0';
|
|
468
|
-
content.style.overflow = 'hidden';
|
|
469
|
-
} else {
|
|
470
|
-
content.style.height = 'auto';
|
|
471
|
-
content.style.overflow = 'visible';
|
|
472
|
-
}
|
|
473
|
-
content.setAttribute('data-state', isOpen ? 'open' : 'closed');
|
|
474
|
-
content.style.display = ''; // Always reset display property
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
if (chevron instanceof HTMLElement) {
|
|
478
|
-
chevron.style.transform = isOpen ? 'rotate(180deg)' : 'rotate(0deg)';
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
// Clean container
|
|
484
|
-
const container = document.querySelector('.content-items');
|
|
485
|
-
if (container instanceof HTMLElement) {
|
|
486
|
-
container.classList.remove('content-items--is-dragging');
|
|
487
|
-
container.classList.remove('dragging-active');
|
|
488
|
-
container.classList.remove('draggable-container--over');
|
|
489
|
-
container.style.pointerEvents = '';
|
|
490
|
-
container.style.cursor = '';
|
|
491
|
-
|
|
492
|
-
// Force reflow
|
|
493
|
-
void container.offsetHeight;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
currentDraggable = new Sortable(containerElement, {
|
|
498
|
-
draggable: '.content-item',
|
|
499
|
-
handle: '.drag-handle',
|
|
500
|
-
delay: 0,
|
|
501
|
-
mirror: {
|
|
502
|
-
constrainDimensions: true,
|
|
503
|
-
appendTo: 'body',
|
|
504
|
-
xAxis: true,
|
|
505
|
-
yAxis: true
|
|
506
|
-
},
|
|
507
|
-
plugins: [SortAnimation],
|
|
508
|
-
sortAnimation: {
|
|
509
|
-
duration: 200,
|
|
510
|
-
easingFunction: 'ease-in-out'
|
|
511
|
-
},
|
|
512
|
-
classes: {
|
|
513
|
-
'body:dragging': 'draggable--is-dragging',
|
|
514
|
-
'container:dragging': 'dragging-active',
|
|
515
|
-
'source:dragging': 'draggable-source--is-dragging',
|
|
516
|
-
'source:placed': 'draggable-source--placed',
|
|
517
|
-
'container:placed': 'draggable-container--placed',
|
|
518
|
-
'draggable:over': 'draggable--over',
|
|
519
|
-
'container:over': 'draggable-container--over',
|
|
520
|
-
'source:original': 'draggable--original',
|
|
521
|
-
'mirror': 'draggable-mirror'
|
|
522
|
-
}
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
// Update event handlers
|
|
528
|
-
currentDraggable.on('drag:start', (event: any) => {
|
|
529
|
-
// console.log('[Draggable] drag:start event');
|
|
530
|
-
isDragging = true; // Set flag immediately to prevent afterUpdate interference
|
|
531
|
-
|
|
532
|
-
const dragHandle = event.originalSource?.querySelector('.drag-handle');
|
|
533
|
-
const target = event.originalEvent?.target;
|
|
534
|
-
|
|
535
|
-
if (!dragHandle?.contains(target)) {
|
|
536
|
-
event.cancel();
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// Store initial mouse position for threshold calculation
|
|
541
|
-
if (event.originalEvent instanceof MouseEvent) {
|
|
542
|
-
initialMousePosition = {
|
|
543
|
-
x: event.originalEvent.clientX,
|
|
544
|
-
y: event.originalEvent.clientY
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// Store previously open items
|
|
549
|
-
previouslyOpen = new Set(openItems);
|
|
550
|
-
|
|
551
|
-
// Reset open items
|
|
552
|
-
openItems = new Set();
|
|
553
|
-
|
|
554
|
-
// Add a class to body to trigger our CSS rules
|
|
555
|
-
document.body.classList.add('dragging-active');
|
|
556
|
-
|
|
557
|
-
// Force reflow to apply height changes immediately
|
|
558
|
-
document.body.offsetHeight;
|
|
559
|
-
|
|
560
|
-
// Add design system classes to all content items for consistent styling
|
|
561
|
-
document.querySelectorAll('.content-item').forEach(item => {
|
|
562
|
-
item.classList.add('widgetic-colors', 'widgetic-radius');
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
// Force visual update of all dropdown chevrons
|
|
566
|
-
document.querySelectorAll('.content-item .chevron').forEach(chevron => {
|
|
567
|
-
if (chevron instanceof HTMLElement) {
|
|
568
|
-
chevron.style.transform = 'rotate(0deg)';
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
// Add drag:move handler - update mirror position to follow mouse
|
|
574
|
-
currentDraggable.on('drag:move', (event: any) => {
|
|
575
|
-
if (isDragStarted) {
|
|
576
|
-
// Update mirror position to follow mouse cursor, accounting for scroll
|
|
577
|
-
if (event.originalEvent instanceof MouseEvent) {
|
|
578
|
-
requestAnimationFrame(() => {
|
|
579
|
-
const mirror = document.querySelector('.draggable-mirror');
|
|
580
|
-
if (mirror instanceof HTMLElement) {
|
|
581
|
-
// Position mirror accounting for grab offset
|
|
582
|
-
mirror.style.top = `${event.originalEvent.clientY - grabOffsetY}px`;
|
|
583
|
-
mirror.style.left = `${event.originalEvent.clientX - grabOffsetX}px`;
|
|
584
|
-
mirror.style.transform = 'translate3d(0,0,0)';
|
|
585
|
-
}
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
if (event.originalEvent instanceof MouseEvent) {
|
|
592
|
-
const deltaX = Math.abs(event.originalEvent.clientX - initialMousePosition.x);
|
|
593
|
-
const deltaY = Math.abs(event.originalEvent.clientY - initialMousePosition.y);
|
|
594
|
-
|
|
595
|
-
// Only start drag if moved more than threshold
|
|
596
|
-
if (deltaX > dragThreshold || deltaY > dragThreshold) {
|
|
597
|
-
isDragStarted = true;
|
|
598
|
-
// isDragging = true; - This is now set in drag:start
|
|
599
|
-
// console.log('[Draggable] isDragStarted set to true');
|
|
600
|
-
|
|
601
|
-
requestAnimationFrame(() => {
|
|
602
|
-
const mirror = document.querySelector('.draggable-mirror');
|
|
603
|
-
if (mirror instanceof HTMLElement) {
|
|
604
|
-
// First, snap to the correct position to prevent flickering
|
|
605
|
-
mirror.style.top = `${event.originalEvent.clientY - grabOffsetY}px`;
|
|
606
|
-
mirror.style.left = `${event.originalEvent.clientX - grabOffsetX}px`;
|
|
607
|
-
mirror.style.transform = 'translate3d(0,0,0)';
|
|
608
|
-
|
|
609
|
-
// Then apply styling
|
|
610
|
-
mirror.style.height = '56px';
|
|
611
|
-
mirror.style.overflow = 'hidden';
|
|
612
|
-
mirror.style.border = '2px solid hsl(var(--grey2))';
|
|
613
|
-
mirror.style.borderRadius = 'var(--radius-small)';
|
|
614
|
-
mirror.style.backgroundColor = 'white';
|
|
615
|
-
mirror.style.boxShadow = 'none';
|
|
616
|
-
|
|
617
|
-
// Add our styling classes without changing any positioning
|
|
618
|
-
mirror.classList.add('widgetic-colors', 'widgetic-radius');
|
|
619
|
-
|
|
620
|
-
// Remove borders from all children
|
|
621
|
-
Array.from(mirror.querySelectorAll('*')).forEach(el => {
|
|
622
|
-
if (el instanceof HTMLElement) {
|
|
623
|
-
el.style.border = 'none';
|
|
624
|
-
el.style.borderTop = 'none';
|
|
625
|
-
el.style.borderBottom = 'none';
|
|
626
|
-
el.style.borderLeft = 'none';
|
|
627
|
-
el.style.borderRight = 'none';
|
|
628
|
-
}
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
// Ensure the mirror content is in collapsed state
|
|
632
|
-
const collapsibleContent = mirror.querySelector('[data-radix-collapsible-content]');
|
|
633
|
-
if (collapsibleContent instanceof HTMLElement) {
|
|
634
|
-
collapsibleContent.style.height = '0';
|
|
635
|
-
collapsibleContent.style.display = 'none';
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
} else {
|
|
640
|
-
// Cancel the drag if we haven't moved enough
|
|
641
|
-
event.cancel();
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
currentDraggable.on('drag:stop', (event: any) => {
|
|
647
|
-
// Clean up any mirror elements immediately
|
|
648
|
-
requestAnimationFrame(() => {
|
|
649
|
-
const mirrors = document.querySelectorAll('.draggable-mirror');
|
|
650
|
-
mirrors.forEach(mirror => {
|
|
651
|
-
if (mirror.parentNode) {
|
|
652
|
-
mirror.parentNode.removeChild(mirror);
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
// If we never started dragging, this was just a click
|
|
658
|
-
if (!isDragStarted) {
|
|
659
|
-
event.cancel();
|
|
660
|
-
forceCleanupState();
|
|
661
|
-
return;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// Reset drag state
|
|
665
|
-
isDragStarted = false;
|
|
666
|
-
isDragging = false;
|
|
667
|
-
dragStartTime = 0;
|
|
668
|
-
initialMousePosition = { x: 0, y: 0 };
|
|
669
|
-
grabOffsetY = 0;
|
|
670
|
-
grabOffsetX = 0;
|
|
671
|
-
|
|
672
|
-
// Remove the class that forces fixed heights
|
|
673
|
-
document.body.classList.remove('dragging-active');
|
|
674
|
-
|
|
675
|
-
// Reset fixed heights from all items
|
|
676
|
-
document.querySelectorAll('.content-item[data-forced-collapsed="true"]').forEach(item => {
|
|
677
|
-
if (item instanceof HTMLElement) {
|
|
678
|
-
item.style.height = '';
|
|
679
|
-
item.style.minHeight = '';
|
|
680
|
-
item.style.maxHeight = '';
|
|
681
|
-
item.style.overflow = '';
|
|
682
|
-
item.removeAttribute('data-forced-collapsed');
|
|
683
|
-
}
|
|
684
|
-
});
|
|
685
|
-
|
|
686
|
-
// Reset all collapsible content elements
|
|
687
|
-
document.querySelectorAll('[data-radix-collapsible-content]').forEach(content => {
|
|
688
|
-
if (content instanceof HTMLElement) {
|
|
689
|
-
content.style.display = '';
|
|
690
|
-
}
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
// Restore previously open items
|
|
694
|
-
setTimeout(() => {
|
|
695
|
-
openItems = new Set(previouslyOpen);
|
|
696
|
-
|
|
697
|
-
// Force update collapsible elements to match our state
|
|
698
|
-
document.querySelectorAll('[data-radix-collapsible-root]').forEach(root => {
|
|
699
|
-
if (root instanceof HTMLElement) {
|
|
700
|
-
const itemId = root.closest('.content-item')?.getAttribute('data-id');
|
|
701
|
-
if (itemId && openItems.has(itemId)) {
|
|
702
|
-
root.setAttribute('data-state', 'open');
|
|
703
|
-
const trigger = root.querySelector('[data-radix-collapsible-trigger]');
|
|
704
|
-
if (trigger instanceof HTMLElement) {
|
|
705
|
-
trigger.setAttribute('data-state', 'open');
|
|
706
|
-
trigger.setAttribute('aria-expanded', 'true');
|
|
707
|
-
}
|
|
708
|
-
const chevron = root.querySelector('.chevron');
|
|
709
|
-
if (chevron instanceof HTMLElement) {
|
|
710
|
-
chevron.style.transform = 'rotate(180deg)';
|
|
711
|
-
}
|
|
712
|
-
const content = root.querySelector('[data-radix-collapsible-content]');
|
|
713
|
-
if (content instanceof HTMLElement) {
|
|
714
|
-
content.style.height = 'auto';
|
|
715
|
-
content.style.overflow = 'visible';
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
});
|
|
720
|
-
}, 50);
|
|
721
|
-
|
|
722
|
-
// Perform normal cleanup
|
|
723
|
-
forceCleanupState();
|
|
724
|
-
});
|
|
725
|
-
|
|
726
|
-
currentDraggable.on('sortable:sorted', (event: { newIndex: number; oldIndex: number }) => {
|
|
727
|
-
// console.log('[Draggable] sortable:sorted event fired', { oldIndex: event.oldIndex, newIndex: event.newIndex });
|
|
728
|
-
try {
|
|
729
|
-
const { oldIndex, newIndex } = event;
|
|
730
|
-
|
|
731
|
-
// Add robust checks for valid indices
|
|
732
|
-
if (typeof oldIndex !== 'number' || typeof newIndex !== 'number' || oldIndex < 0 || newIndex < 0) {
|
|
733
|
-
console.error('[Drag] Invalid or missing indices from event. Aborting reorder.');
|
|
734
|
-
isDragStarted = false;
|
|
735
|
-
return;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
// No need to do anything if the item was dropped in the same place
|
|
739
|
-
if (oldIndex === newIndex) {
|
|
740
|
-
// console.log('[Drag] Item dropped in the same position. No change needed.');
|
|
741
|
-
isDragStarted = false;
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
if (!Array.isArray(contentSchema?.contentItems)) {
|
|
746
|
-
console.warn('[Drag] contentItems is not an array, skipping reorder');
|
|
747
|
-
isDragStarted = false;
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
|
-
// console.log('[Drag] Before:', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
395
|
+
if (!Array.isArray(contentSchema?.contentItems)) {
|
|
396
|
+
console.warn('[Reorder] contentItems is not an array, skipping reorder');
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
751
399
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
400
|
+
// Robust checks for valid indices
|
|
401
|
+
const itemCount = contentSchema.contentItems.length;
|
|
402
|
+
if (
|
|
403
|
+
!Number.isInteger(oldIndex) || !Number.isInteger(newIndex) ||
|
|
404
|
+
oldIndex < 0 || newIndex < 0 || oldIndex >= itemCount || newIndex >= itemCount
|
|
405
|
+
) {
|
|
406
|
+
console.error('[Reorder] Invalid indices for the dropped item. Aborting reorder.', { oldIndex, newIndex, itemCount });
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
755
409
|
|
|
756
|
-
|
|
757
|
-
if (hasDuplicateIds(items)) {
|
|
758
|
-
console.error('[Drag] Duplicate IDs detected after reordering with indices. Aborting update.', items.map(i => i.id));
|
|
759
|
-
isDragStarted = false;
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
if (items.length !== contentSchema.contentItems.length) {
|
|
764
|
-
console.error('[Drag] Array length mismatch after reordering with indices. Aborting update.');
|
|
765
|
-
isDragStarted = false;
|
|
766
|
-
return;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
const renumbered = renumberAutoItemTitles(items);
|
|
770
|
-
contentSchema = { ...contentSchema, contentItems: renumbered };
|
|
771
|
-
// console.log('[Drag] After (reordered with indices):', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
772
|
-
dispatch('updated', { contentSchema });
|
|
773
|
-
dispatchContentItemsChanged(contentSchema.contentItems);
|
|
774
|
-
|
|
775
|
-
} catch (err) {
|
|
776
|
-
console.error('[Drag] Error updating order in sortable:sorted:', err);
|
|
777
|
-
}
|
|
778
|
-
isDragStarted = false;
|
|
779
|
-
isDragging = false; // Allow afterUpdate to run again
|
|
780
|
-
});
|
|
410
|
+
const items = moveListItem<ContentItem>(contentSchema.contentItems, oldIndex, newIndex);
|
|
781
411
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
mirror.style.borderRadius = 'var(--radius-small)';
|
|
792
|
-
mirror.style.boxShadow = 'none';
|
|
793
|
-
|
|
794
|
-
// Style the mirror element, but do NOT set top, left, or transform.
|
|
795
|
-
mirror.style.height = '56px';
|
|
796
|
-
mirror.style.overflow = 'hidden';
|
|
797
|
-
mirror.style.position = 'fixed';
|
|
798
|
-
mirror.style.width = `${event.originalSource.offsetWidth}px`;
|
|
799
|
-
mirror.style.backgroundColor = 'white';
|
|
800
|
-
mirror.style.pointerEvents = 'none'; // Ensure it doesn't interfere with mouse events
|
|
801
|
-
mirror.style.zIndex = '1000'; // Ensure it's above other elements
|
|
802
|
-
|
|
803
|
-
// Position mirror at exact mouse cursor location
|
|
804
|
-
const mouseEvent = event.originalEvent;
|
|
805
|
-
if (mouseEvent instanceof MouseEvent) {
|
|
806
|
-
// Position mirror centered on mouse cursor
|
|
807
|
-
const mirrorHeight = 56; // Fixed height we set
|
|
808
|
-
const mirrorWidth = event.originalSource.offsetWidth;
|
|
809
|
-
|
|
810
|
-
// Center the mirror on the mouse cursor
|
|
811
|
-
grabOffsetY = mirrorHeight / 2;
|
|
812
|
-
grabOffsetX = mirrorWidth / 2 + DRAG_SHIFT_X;
|
|
813
|
-
|
|
814
|
-
// Position mirror so mouse is at center
|
|
815
|
-
mirror.style.top = `${mouseEvent.clientY - grabOffsetY}px`;
|
|
816
|
-
mirror.style.left = `${mouseEvent.clientX - grabOffsetX}px`;
|
|
817
|
-
mirror.style.transform = 'translate3d(0,0,0)';
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
// Style text with sage-green CSS variable
|
|
821
|
-
const textElements = mirror.querySelectorAll('h4, .content-title, span');
|
|
822
|
-
textElements.forEach(el => {
|
|
823
|
-
if (el instanceof HTMLElement) {
|
|
824
|
-
el.style.color = 'hsl(var(--sage-green))';
|
|
825
|
-
}
|
|
826
|
-
});
|
|
827
|
-
|
|
828
|
-
// Style only chevrons with sage-green CSS variable
|
|
829
|
-
const chevrons = mirror.querySelectorAll('.chevron, svg');
|
|
830
|
-
chevrons.forEach(el => {
|
|
831
|
-
if (el instanceof SVGElement) {
|
|
832
|
-
el.style.stroke = 'hsl(var(--sage-green))';
|
|
833
|
-
el.style.fill = 'transparent';
|
|
834
|
-
|
|
835
|
-
// Also handle any paths inside SVGs
|
|
836
|
-
const paths = el.querySelectorAll('path');
|
|
837
|
-
paths.forEach(path => {
|
|
838
|
-
path.setAttribute('stroke', 'hsl(var(--sage-green))');
|
|
839
|
-
path.setAttribute('fill', 'transparent');
|
|
840
|
-
});
|
|
841
|
-
|
|
842
|
-
// Check if this item was previously open and rotate the chevron accordingly
|
|
843
|
-
const sourceId = event.originalSource.getAttribute('data-id');
|
|
844
|
-
if (sourceId && previouslyOpen && previouslyOpen.has(sourceId) && el.classList.contains('chevron')) {
|
|
845
|
-
// Apply the same transition effect as the normal open/close operation
|
|
846
|
-
el.style.transition = 'transform 200ms ease-in-out';
|
|
847
|
-
el.style.transform = 'rotate(360deg)';
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
});
|
|
851
|
-
}
|
|
852
|
-
});
|
|
412
|
+
// Safety checks before updating state
|
|
413
|
+
if (hasDuplicateIds(items)) {
|
|
414
|
+
console.error('[Reorder] Duplicate IDs detected after reordering with indices. Aborting update.', items.map(i => i.id));
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (items.length !== itemCount) {
|
|
418
|
+
console.error('[Reorder] Array length mismatch after reordering with indices. Aborting update.');
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
853
421
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
422
|
+
const renumbered = renumberAutoItemTitles(items);
|
|
423
|
+
contentSchema = { ...contentSchema, contentItems: renumbered };
|
|
424
|
+
dispatch('updated', { contentSchema });
|
|
425
|
+
dispatchContentItemsChanged(contentSchema.contentItems);
|
|
426
|
+
} catch (err) {
|
|
427
|
+
console.error('[Reorder] Error updating the order after a drop:', err);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
861
430
|
|
|
862
|
-
|
|
431
|
+
/** The drag is over (dropped or cancelled): the list is drawn as usual again. */
|
|
432
|
+
function handleReorderEnd() {
|
|
433
|
+
isReordering = false;
|
|
434
|
+
}
|
|
863
435
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
} finally {
|
|
872
|
-
isInitializing = false;
|
|
873
|
-
}
|
|
436
|
+
/** What the list of content items needs to be reorderable; used with `use:reorderableList` on the list. */
|
|
437
|
+
const contentItemsReorderOptions: ItemReorderOptions = {
|
|
438
|
+
itemSelector: '.content-item',
|
|
439
|
+
handleSelector: '.drag-handle',
|
|
440
|
+
onDragStart: handleReorderStart,
|
|
441
|
+
onReorder: handleItemsReordered,
|
|
442
|
+
onDragEnd: handleReorderEnd
|
|
874
443
|
};
|
|
875
444
|
|
|
876
445
|
// Add this near other reactive declarations at the top level
|
|
@@ -910,6 +479,14 @@
|
|
|
910
479
|
}
|
|
911
480
|
}
|
|
912
481
|
|
|
482
|
+
// The tree the editor is drawn in (the shadow root inside a module, the document on a normal page), kept from the
|
|
483
|
+
// moment it is mounted: when the editor is torn down it is no longer in that tree to ask.
|
|
484
|
+
let editorTree: SearchableTree | null = null;
|
|
485
|
+
|
|
486
|
+
onMount(() => {
|
|
487
|
+
editorTree = treeOf(rootElement);
|
|
488
|
+
});
|
|
489
|
+
|
|
913
490
|
onMount(() => {
|
|
914
491
|
// console.log('PropsEditor Mounted!');
|
|
915
492
|
|
|
@@ -956,7 +533,8 @@
|
|
|
956
533
|
if (browser) {
|
|
957
534
|
document.removeEventListener('mousemove', handleDragMove);
|
|
958
535
|
document.removeEventListener('mouseup', handleDragEnd);
|
|
959
|
-
|
|
536
|
+
releasePointerStyle?.();
|
|
537
|
+
releasePointerStyle = null;
|
|
960
538
|
}
|
|
961
539
|
|
|
962
540
|
// Clean up scroll listeners
|
|
@@ -967,9 +545,9 @@
|
|
|
967
545
|
contentScrollViewport.removeEventListener('scroll', (event) => handleScroll(event, 'content'));
|
|
968
546
|
}
|
|
969
547
|
|
|
970
|
-
// Clean up any remaining tooltips
|
|
971
|
-
if (browser) {
|
|
972
|
-
const tooltips =
|
|
548
|
+
// Clean up any remaining tooltips, the editor's own: the page it is on has tooltips of its own that must stay
|
|
549
|
+
if (browser && editorTree) {
|
|
550
|
+
const tooltips = Array.from(editorTree.querySelectorAll('[role="tooltip"]'));
|
|
973
551
|
tooltips.forEach(tooltip => {
|
|
974
552
|
if (tooltip.parentNode) {
|
|
975
553
|
tooltip.parentNode.removeChild(tooltip);
|
|
@@ -1024,12 +602,11 @@
|
|
|
1024
602
|
// Replace ALL hyphens with underscores in the condition string
|
|
1025
603
|
const safeCondition = condition.replace(/-/g, '_');
|
|
1026
604
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
return evalFunction(...Object.values(context));
|
|
605
|
+
// The condition is text from the widget's schema and this editor can run inside a customer's page, so it
|
|
606
|
+
// is read by a small parser and never run as code (it used to go through `new Function`, which ran the
|
|
607
|
+
// widget author's JavaScript with the page's rights and needed 'unsafe-eval'). A condition the parser
|
|
608
|
+
// cannot use keeps the control visible, as a failing one always did, and is reported once in the console.
|
|
609
|
+
return conditionHolds(safeCondition, context);
|
|
1033
610
|
} catch (error) {
|
|
1034
611
|
// console.error('Error evaluating visibility condition:', error);
|
|
1035
612
|
// console.log('Condition:', condition);
|
|
@@ -1200,10 +777,12 @@
|
|
|
1200
777
|
// });
|
|
1201
778
|
}
|
|
1202
779
|
|
|
1203
|
-
// First find and remove all modal-related elements, but preserve
|
|
780
|
+
// First find and remove all modal-related elements, but preserve the item being dragged
|
|
1204
781
|
const cleanup = () => {
|
|
1205
|
-
// Get all possible modal elements with more specific selectors
|
|
1206
|
-
|
|
782
|
+
// Get all possible modal elements with more specific selectors. Only the ones in the editor's own tree (the
|
|
783
|
+
// shadow root inside a module): the customer's page has dialogs and popovers of its own that are none of the
|
|
784
|
+
// editor's business.
|
|
785
|
+
const modalElements = queryAllInTreeOf(rootElement, [
|
|
1207
786
|
'[data-radix-popper-content-wrapper]',
|
|
1208
787
|
'[role="dialog"]',
|
|
1209
788
|
'[cmdk-root]',
|
|
@@ -1228,9 +807,9 @@
|
|
|
1228
807
|
// }))
|
|
1229
808
|
// });
|
|
1230
809
|
|
|
1231
|
-
// Remove each modal immediately, but protect scroll-related and
|
|
810
|
+
// Remove each modal immediately, but protect scroll-related and dragged elements
|
|
1232
811
|
modalElements.forEach(element => {
|
|
1233
|
-
// Don't remove if it's part of the core UI structure or
|
|
812
|
+
// Don't remove if it's part of the core UI structure or being dragged
|
|
1234
813
|
if (
|
|
1235
814
|
element.closest('[role="tabpanel"]') ||
|
|
1236
815
|
element.closest('[data-accordion-content]') ||
|
|
@@ -1242,8 +821,8 @@
|
|
|
1242
821
|
element.classList.contains('scrollbar') ||
|
|
1243
822
|
element.classList.contains('scroll-area') ||
|
|
1244
823
|
element.hasAttribute('data-scrollable') ||
|
|
1245
|
-
element.classList.contains('
|
|
1246
|
-
element.classList.contains('
|
|
824
|
+
element.classList.contains('reorder-mirror') ||
|
|
825
|
+
element.classList.contains('is-reorder-source')
|
|
1247
826
|
) {
|
|
1248
827
|
return;
|
|
1249
828
|
}
|
|
@@ -1270,28 +849,6 @@
|
|
|
1270
849
|
// Reset active controller
|
|
1271
850
|
activeControllerId = null;
|
|
1272
851
|
|
|
1273
|
-
// If switching to content tab and content exists, ensure draggable is initialized
|
|
1274
|
-
if (currentValue === 'content' && contentSchema) {
|
|
1275
|
-
// Wait for tab content to be rendered
|
|
1276
|
-
setTimeout(async () => {
|
|
1277
|
-
// console.log('🎯 Checking draggable state in content tab');
|
|
1278
|
-
|
|
1279
|
-
// Check if draggable needs initialization
|
|
1280
|
-
const containers = document.querySelectorAll('.content-items');
|
|
1281
|
-
const needsInit = !isDraggableInitialized ||
|
|
1282
|
-
!currentDraggable ||
|
|
1283
|
-
containers.length === 0 ||
|
|
1284
|
-
!document.querySelector('.draggable-container');
|
|
1285
|
-
|
|
1286
|
-
if (needsInit) {
|
|
1287
|
-
// console.log('🔄 Reinitializing draggable');
|
|
1288
|
-
await initializeDraggable();
|
|
1289
|
-
} else {
|
|
1290
|
-
// console.log('✅ Draggable already initialized and working');
|
|
1291
|
-
}
|
|
1292
|
-
}, 50);
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
852
|
// Restore open items state for the selected tab
|
|
1296
853
|
openItems = currentValue === 'design' ? designOpenItems : contentOpenItems;
|
|
1297
854
|
const accordionToOpen = currentValue === 'design' ? designOpenAccordion : contentOpenAccordion;
|
|
@@ -1308,7 +865,7 @@
|
|
|
1308
865
|
setTimeout(() => {
|
|
1309
866
|
const itemsToOpen = Array.isArray(accordionToOpen) ? accordionToOpen : (accordionToOpen ? [accordionToOpen] : []);
|
|
1310
867
|
for (const name of itemsToOpen) {
|
|
1311
|
-
const accordion =
|
|
868
|
+
const accordion = queryInTreeOf(rootElement, `[data-accordion-id="${name}"]`);
|
|
1312
869
|
if (accordion) {
|
|
1313
870
|
accordion.setAttribute('data-state', 'open');
|
|
1314
871
|
}
|
|
@@ -1330,7 +887,8 @@
|
|
|
1330
887
|
focusController(controllerToFocus);
|
|
1331
888
|
} else {
|
|
1332
889
|
// Focus first controller
|
|
1333
|
-
const firstController =
|
|
890
|
+
const firstController = queryInTreeOf(
|
|
891
|
+
rootElement,
|
|
1334
892
|
`[data-tab="${currentValue}"] .input-controller:first-child`
|
|
1335
893
|
);
|
|
1336
894
|
if (firstController) {
|
|
@@ -1774,7 +1332,9 @@
|
|
|
1774
1332
|
function focusController(controllerId: string) {
|
|
1775
1333
|
// console.log('Attempting to focus controller:', controllerId);
|
|
1776
1334
|
activeControllerId = controllerId;
|
|
1777
|
-
|
|
1335
|
+
// Looked up in the editor's own tree: in a module that is the shadow root, where `document.getElementById`
|
|
1336
|
+
// finds nothing (and on the customer's page it could find an element of theirs with the same id).
|
|
1337
|
+
const element = treeOf(rootElement)?.getElementById(controllerId);
|
|
1778
1338
|
if (element) {
|
|
1779
1339
|
const componentInstance = element.querySelector(':scope > *');
|
|
1780
1340
|
if (componentInstance && 'focus' in componentInstance) {
|
|
@@ -1840,6 +1400,9 @@
|
|
|
1840
1400
|
// Keydown handler: SHIFT + '+' expands all, SHIFT + '-' collapses all
|
|
1841
1401
|
function handleKeyDown(event: KeyboardEvent) {
|
|
1842
1402
|
// console.log("handleKeyDown triggered: key=", event.key, "shiftKey=", event.shiftKey);
|
|
1403
|
+
// The listener is on the window, so it hears every key typed on the page. Only the ones typed in the editor
|
|
1404
|
+
// count: on a customer's page Shift+Arrow in their own text boxes must not open and close our items.
|
|
1405
|
+
if (rootElement && !event.composedPath().includes(rootElement)) return;
|
|
1843
1406
|
if (event.shiftKey && event.key === 'ArrowDown') {
|
|
1844
1407
|
// console.log("Expanding all content items");
|
|
1845
1408
|
expandAllContentItems();
|
|
@@ -2063,33 +1626,11 @@
|
|
|
2063
1626
|
// console.log('[Add] After (should be at end):', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
2064
1627
|
// console.log('[Add] New item added:', newItem);
|
|
2065
1628
|
|
|
2066
|
-
//
|
|
1629
|
+
// The list is drawn from the array, so the new item is at the end of it. Once it is drawn, open it to reveal
|
|
1630
|
+
// its content. (The DOM order no longer needs to be put right by hand here: nothing moves the items in the
|
|
1631
|
+
// DOM behind the list's back since the drag and drop is done by utils/itemReorder.ts.)
|
|
2067
1632
|
setTimeout(() => {
|
|
2068
|
-
|
|
2069
|
-
if (container && Array.isArray(contentSchema?.contentItems)) {
|
|
2070
|
-
const elementMap = new Map<string, HTMLElement>();
|
|
2071
|
-
Array.from(container.querySelectorAll('.content-item')).forEach((el: Element) => {
|
|
2072
|
-
const id = (el as HTMLElement).dataset.id;
|
|
2073
|
-
if (id) elementMap.set(id, el as HTMLElement);
|
|
2074
|
-
});
|
|
2075
|
-
|
|
2076
|
-
const desiredOrderIds = contentSchema.contentItems.map((item: ContentItem) => item.id);
|
|
2077
|
-
|
|
2078
|
-
// Surgical re-ordering to minimize DOM moves and flicker
|
|
2079
|
-
for (let i = 0; i < desiredOrderIds.length; i++) {
|
|
2080
|
-
const desiredId = desiredOrderIds[i];
|
|
2081
|
-
const currentDomNode = container.children[i] as HTMLElement | undefined;
|
|
2082
|
-
|
|
2083
|
-
// Only move the element if it's not already in the correct position
|
|
2084
|
-
if (currentDomNode?.dataset.id !== desiredId) {
|
|
2085
|
-
const elementToMove = elementMap.get(desiredId);
|
|
2086
|
-
if (elementToMove) {
|
|
2087
|
-
container.insertBefore(elementToMove, currentDomNode || null);
|
|
2088
|
-
}
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
|
-
// Now that the item is positioned, open it to reveal content
|
|
1633
|
+
if (Array.isArray(contentSchema?.contentItems)) {
|
|
2093
1634
|
openItems = new Set([...openItems, uniqueId]);
|
|
2094
1635
|
}
|
|
2095
1636
|
}, 0);
|
|
@@ -2235,10 +1776,6 @@
|
|
|
2235
1776
|
const targetIndex = Math.max(0, deletedIndex - 1);
|
|
2236
1777
|
dispatch('goToItem', { index: targetIndex });
|
|
2237
1778
|
}
|
|
2238
|
-
setTimeout(() => {
|
|
2239
|
-
// console.log('🔄 Reinitializing draggable after deletion');
|
|
2240
|
-
initializeDraggable();
|
|
2241
|
-
}, 0);
|
|
2242
1779
|
}
|
|
2243
1780
|
} catch (error) {
|
|
2244
1781
|
console.error('❌ Error deleting content item:', error);
|
|
@@ -2264,8 +1801,9 @@
|
|
|
2264
1801
|
}
|
|
2265
1802
|
);
|
|
2266
1803
|
|
|
2267
|
-
// Observe content items
|
|
2268
|
-
|
|
1804
|
+
// Observe content items: the ones in the editor's own list. `.content-item` is a name any page may use, and
|
|
1805
|
+
// an element of the page's that is observed here would be hidden whenever it scrolls out of view.
|
|
1806
|
+
contentItemsContainer?.querySelectorAll('.content-item').forEach(item => {
|
|
2269
1807
|
observer.observe(item);
|
|
2270
1808
|
});
|
|
2271
1809
|
|
|
@@ -2274,13 +1812,14 @@
|
|
|
2274
1812
|
|
|
2275
1813
|
// Keep CSS --index in sync with DOM position **after** Svelte has rendered updates
|
|
2276
1814
|
afterUpdate(() => {
|
|
2277
|
-
if (isDragging) return;
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
1815
|
+
if (isDragging || isReordering) return;
|
|
1816
|
+
// The items of the editor's own list, in the order they are drawn (a lookup by data-id on the whole document
|
|
1817
|
+
// would find nothing inside a shadow root, and could find an element of the customer's page).
|
|
1818
|
+
const itemElements = Array.from(contentItemsContainer?.children ?? []);
|
|
1819
|
+
if (itemElements.length === 0) return;
|
|
1820
|
+
|
|
1821
|
+
itemElements.forEach((el: Element, index: number) => {
|
|
1822
|
+
if (el instanceof HTMLElement && el.classList.contains('content-item')) {
|
|
2284
1823
|
el.style.setProperty('--index', index.toString());
|
|
2285
1824
|
}
|
|
2286
1825
|
});
|
|
@@ -2305,11 +1844,8 @@
|
|
|
2305
1844
|
currentTab = 'content';
|
|
2306
1845
|
|
|
2307
1846
|
// Wait for DOM to be ready
|
|
2308
|
-
setTimeout(
|
|
1847
|
+
setTimeout(() => {
|
|
2309
1848
|
try {
|
|
2310
|
-
// console.log('🔄 Initializing draggable for content tab');
|
|
2311
|
-
await initializeDraggable();
|
|
2312
|
-
|
|
2313
1849
|
// Set initial states
|
|
2314
1850
|
isInitialLoad = false;
|
|
2315
1851
|
isFirstLoad = false;
|
|
@@ -2392,6 +1928,8 @@
|
|
|
2392
1928
|
// ─── BUILT-IN DRAG ───────────────────────────────────────────
|
|
2393
1929
|
let rootElement: HTMLElement;
|
|
2394
1930
|
let dragState = { active: false, startX: 0, startY: 0, initialLeft: 0, initialTop: 0 };
|
|
1931
|
+
// Puts the pointer back as it was once the panel is no longer dragged (see lockPointerStyle).
|
|
1932
|
+
let releasePointerStyle: (() => void) | null = null;
|
|
2395
1933
|
|
|
2396
1934
|
function handleDragStart(e: MouseEvent) {
|
|
2397
1935
|
const target = positionTarget || rootElement;
|
|
@@ -2423,7 +1961,10 @@
|
|
|
2423
1961
|
target.style.zIndex = '50';
|
|
2424
1962
|
document.addEventListener('mousemove', handleDragMove);
|
|
2425
1963
|
document.addEventListener('mouseup', handleDragEnd);
|
|
2426
|
-
|
|
1964
|
+
// The cursor is shown on the host of the shadow root the editor is in (on <body> only when it is on a normal page):
|
|
1965
|
+
// the <body> of a customer's page is not the editor's to change.
|
|
1966
|
+
releasePointerStyle?.();
|
|
1967
|
+
releasePointerStyle = lockPointerStyle(rootElement, 'move', { blockTextSelection: false });
|
|
2427
1968
|
}
|
|
2428
1969
|
|
|
2429
1970
|
function handleDragMove(e: MouseEvent) {
|
|
@@ -2439,7 +1980,8 @@
|
|
|
2439
1980
|
dragState.active = false;
|
|
2440
1981
|
document.removeEventListener('mousemove', handleDragMove);
|
|
2441
1982
|
document.removeEventListener('mouseup', handleDragEnd);
|
|
2442
|
-
|
|
1983
|
+
releasePointerStyle?.();
|
|
1984
|
+
releasePointerStyle = null;
|
|
2443
1985
|
dispatch('moveEnd', getPosition());
|
|
2444
1986
|
}
|
|
2445
1987
|
|
|
@@ -2452,6 +1994,7 @@
|
|
|
2452
1994
|
export function setPosition(left: number, top: number) {
|
|
2453
1995
|
const target = positionTarget || rootElement;
|
|
2454
1996
|
if (!target) return;
|
|
1997
|
+
// rtl-ok: the panel is placed in pixels from the left edge of the editor; the dock mirrors the pair on a right to left screen
|
|
2455
1998
|
target.style.left = `${left}px`;
|
|
2456
1999
|
target.style.top = `${top}px`;
|
|
2457
2000
|
}
|
|
@@ -2481,17 +2024,6 @@
|
|
|
2481
2024
|
return ids.length !== new Set(ids).size;
|
|
2482
2025
|
}
|
|
2483
2026
|
|
|
2484
|
-
// Helper: log DOM visual order for debugging
|
|
2485
|
-
function logOrder(label: string) {
|
|
2486
|
-
const titles = Array.from(document.querySelectorAll('.content-item .content-title')).map(el => el.textContent?.trim());
|
|
2487
|
-
// console.log(`[Order-check] ${label}:`, titles);
|
|
2488
|
-
const transforms = Array.from(document.querySelectorAll('.content-item')).map(el => ({ id: (el as HTMLElement).dataset.id, transform: (el as HTMLElement).style.transform }));
|
|
2489
|
-
console.table(transforms);
|
|
2490
|
-
}
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
2027
|
</script>
|
|
2496
2028
|
|
|
2497
2029
|
<div
|
|
@@ -2512,7 +2044,7 @@
|
|
|
2512
2044
|
<!-- A single toolbar owns drag and Save for all editor variants. -->
|
|
2513
2045
|
{#if showDragHandle && (designSchema || contentSchema || headerContent)}
|
|
2514
2046
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
2515
|
-
<div class="props-editor-drag-bar top-0
|
|
2047
|
+
<div class="props-editor-drag-bar top-0 start-0 w-full flex h-7 shrink-0 items-center justify-center z-999 px-3 {headerContent ? 'relative ' + headerClass : 'absolute'}">
|
|
2516
2048
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
2517
2049
|
<div
|
|
2518
2050
|
class="flex items-center justify-center h-7 cursor-move transition-all duration-200 widgetic-colors opacity-0 hover:opacity-100 drag-handle-container"
|
|
@@ -2521,10 +2053,10 @@
|
|
|
2521
2053
|
<DragHandle class="w-16.5 h-6 {dragHandleClass} hover:text-grey3 transition-colors" />
|
|
2522
2054
|
</div>
|
|
2523
2055
|
{#if showSaveButton}
|
|
2524
|
-
{#if
|
|
2056
|
+
{#if titleLabel}<span class="props-editor-title-label absolute start-6 top-1.75 pointer-events-none select-none text-[13px] font-bold leading-none text-[hsl(var(--grey5))]">{titleLabel}</span>{/if}
|
|
2525
2057
|
<button
|
|
2526
2058
|
type="button"
|
|
2527
|
-
class="props-editor-save-btn absolute
|
|
2059
|
+
class="props-editor-save-btn absolute end-6 top-1 inline-flex h-5 w-19 items-center justify-center rounded-md text-[10px] font-semibold leading-none transition-colors focus:outline-none focus-visible:outline-none
|
|
2528
2060
|
{saveSaving
|
|
2529
2061
|
? 'text-[hsl(var(--deep-forest-green))] bg-[hsl(var(--light-green))]'
|
|
2530
2062
|
: saveDirty
|
|
@@ -2537,12 +2069,12 @@
|
|
|
2537
2069
|
}}
|
|
2538
2070
|
onmousedown={(e) => e.stopPropagation()}
|
|
2539
2071
|
>
|
|
2540
|
-
<svg class="props-editor-save-icon absolute
|
|
2072
|
+
<svg class="props-editor-save-icon absolute start-2 h-3 w-3 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
2541
2073
|
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
|
|
2542
2074
|
<polyline points="17 21 17 13 7 13 7 21" />
|
|
2543
2075
|
<polyline points="7 3 7 8 15 8" />
|
|
2544
2076
|
</svg>
|
|
2545
|
-
<span class="props-editor-save-label">{saveSaving ? '
|
|
2077
|
+
<span class="props-editor-save-label">{saveSaving ? $t('editor.props.saving') : $t('editor.props.save')}</span>
|
|
2546
2078
|
</button>
|
|
2547
2079
|
{/if}
|
|
2548
2080
|
</div>
|
|
@@ -2555,8 +2087,8 @@
|
|
|
2555
2087
|
{#if !designSchema && !contentSchema}
|
|
2556
2088
|
{#if showSchemaError}
|
|
2557
2089
|
<div class="props-editor-schema-error flex flex-col items-center justify-center h-full">
|
|
2558
|
-
<div class="props-editor-error-title text-error text-lg font-medium">
|
|
2559
|
-
<div class="props-editor-error-code text-grey5 subtext mt-1">
|
|
2090
|
+
<div class="props-editor-error-title text-error text-lg font-medium">{$t('editor.props.noConfiguration')}</div>
|
|
2091
|
+
<div class="props-editor-error-code text-grey5 subtext mt-1">{$t('editor.failure.errorCode', { code: 'CONFIG_NOT_FOUND' })}</div>
|
|
2560
2092
|
</div>
|
|
2561
2093
|
{/if}
|
|
2562
2094
|
{:else}
|
|
@@ -2572,17 +2104,14 @@
|
|
|
2572
2104
|
value="design"
|
|
2573
2105
|
class="flex-1 h-full rounded-[11px] transition-colors duration-250 text-grey5 hover:text-grey7 hover:underline data-[state=active]:bg-white data-[state=active]:text-grey7 data-[state=active]:hover:text-grey7"
|
|
2574
2106
|
>
|
|
2575
|
-
<span class="body">{designTabTitle}</span>
|
|
2107
|
+
<span class="body">{designTabTitle ?? $t('editor.props.designTab')}</span>
|
|
2576
2108
|
</Tabs.Trigger>
|
|
2577
2109
|
{#if contentSchema}
|
|
2578
2110
|
<Tabs.Trigger
|
|
2579
|
-
onclick={() => {
|
|
2580
|
-
initializeDraggable();
|
|
2581
|
-
}}
|
|
2582
2111
|
value="content"
|
|
2583
2112
|
class="flex-1 h-full rounded-[11px] transition-colors duration-250 text-grey5 hover:text-grey7 hover:underline data-[state=active]:bg-white data-[state=active]:text-grey7 data-[state=active]:hover:text-grey7"
|
|
2584
2113
|
>
|
|
2585
|
-
<span class="body">{contentTabTitle}</span>
|
|
2114
|
+
<span class="body">{contentTabTitle ?? $t('editor.props.contentTab')}</span>
|
|
2586
2115
|
</Tabs.Trigger>
|
|
2587
2116
|
{/if}
|
|
2588
2117
|
</Tabs.List>
|
|
@@ -2668,7 +2197,7 @@
|
|
|
2668
2197
|
</div>
|
|
2669
2198
|
{/if}
|
|
2670
2199
|
|
|
2671
|
-
<div class="ic-input-ct {isFontIc ? 'w-full' : 'shrink-0
|
|
2200
|
+
<div class="ic-input-ct {isFontIc ? 'w-full' : 'shrink-0 ms-auto'}" style={isFontIc ? '' : 'width: 185px;'}>
|
|
2672
2201
|
<InputControllerRenderer
|
|
2673
2202
|
controller={describeInputController(InputComponent, {
|
|
2674
2203
|
...props,
|
|
@@ -2688,13 +2217,13 @@
|
|
|
2688
2217
|
{:else}
|
|
2689
2218
|
<div class="p-4 text-gray-500">
|
|
2690
2219
|
{#if !designSchema}
|
|
2691
|
-
|
|
2220
|
+
{$t('editor.props.noDesignSchema')}
|
|
2692
2221
|
{:else if !designSchema[0]}
|
|
2693
|
-
|
|
2222
|
+
{$t('editor.props.noSchemaData')}
|
|
2694
2223
|
{:else if !designSchema[0].components}
|
|
2695
|
-
|
|
2224
|
+
{$t('editor.props.noComponents')}
|
|
2696
2225
|
{:else}
|
|
2697
|
-
|
|
2226
|
+
{$t('editor.props.noProperties')}
|
|
2698
2227
|
{/if}
|
|
2699
2228
|
</div>
|
|
2700
2229
|
{/if}
|
|
@@ -2734,8 +2263,8 @@
|
|
|
2734
2263
|
onOpenChange={() => isGenericContentOpen = !isGenericContentOpen}
|
|
2735
2264
|
>
|
|
2736
2265
|
<Collapsible.Trigger class="generic-content-trigger w-full">
|
|
2737
|
-
<div class="flex items-center h-13" style="padding-
|
|
2738
|
-
<h3 class="body-medium text-grey7 flex-1 content-title">{contentSchema?.genericContent?.title || '
|
|
2266
|
+
<div class="flex items-center h-13" style="padding-inline-start: {CONTENT_INNER_PADDING}px; padding-inline-end: {CONTENT_INNER_PADDING}px;">
|
|
2267
|
+
<h3 class="body-medium text-grey7 flex-1 content-title">{contentSchema?.genericContent?.title || $t('editor.props.contentCard')}</h3>
|
|
2739
2268
|
<ChevronDown class="h-6 w-6 text-grey5 transition-transform chevron" />
|
|
2740
2269
|
</div>
|
|
2741
2270
|
</Collapsible.Trigger>
|
|
@@ -2772,7 +2301,7 @@
|
|
|
2772
2301
|
helpText={header.helpText}
|
|
2773
2302
|
/>
|
|
2774
2303
|
{:else if !isFontIc}
|
|
2775
|
-
<div class="flex-1 min-w-0
|
|
2304
|
+
<div class="flex-1 min-w-0 pe-4">
|
|
2776
2305
|
<InputControllerHeader
|
|
2777
2306
|
label={header.label}
|
|
2778
2307
|
helpText={header.helpText}
|
|
@@ -2800,7 +2329,7 @@
|
|
|
2800
2329
|
<!-- Content Items Container -->
|
|
2801
2330
|
<div class="content-items-wrapper mt-0" style="position: relative; z-index: 1;">
|
|
2802
2331
|
<!-- Sortable Container -->
|
|
2803
|
-
<div class="content-items flex flex-col gap-4" bind:this={contentItemsContainer} style="position: relative; z-index: 1;">
|
|
2332
|
+
<div class="content-items flex flex-col gap-4" bind:this={contentItemsContainer} use:reorderableList={contentItemsReorderOptions} style="position: relative; z-index: 1;">
|
|
2804
2333
|
{@html (() => {
|
|
2805
2334
|
const items = contentSchema?.contentItems;
|
|
2806
2335
|
if (!Array.isArray(items)) return '';
|
|
@@ -2831,7 +2360,7 @@
|
|
|
2831
2360
|
</h4>
|
|
2832
2361
|
|
|
2833
2362
|
<!-- Arrow Icon -->
|
|
2834
|
-
<div class="
|
|
2363
|
+
<div class="pe-4">
|
|
2835
2364
|
<ChevronDown class="h-6 w-6 text-grey7 transition-transform chevron" />
|
|
2836
2365
|
</div>
|
|
2837
2366
|
</div>
|
|
@@ -2870,7 +2399,7 @@
|
|
|
2870
2399
|
helpText={header.helpText}
|
|
2871
2400
|
/>
|
|
2872
2401
|
{:else if !isFontIc}
|
|
2873
|
-
<div class="flex-1 min-w-0
|
|
2402
|
+
<div class="flex-1 min-w-0 pe-4">
|
|
2874
2403
|
<InputControllerHeader
|
|
2875
2404
|
label={header.label}
|
|
2876
2405
|
helpText={header.helpText}
|
|
@@ -2913,7 +2442,7 @@
|
|
|
2913
2442
|
}}
|
|
2914
2443
|
>
|
|
2915
2444
|
<span class="subtext text-white transition-colors duration-200">
|
|
2916
|
-
|
|
2445
|
+
{$t('editor.props.deleteItem')}
|
|
2917
2446
|
</span>
|
|
2918
2447
|
</button>
|
|
2919
2448
|
</div>
|
|
@@ -2937,7 +2466,7 @@
|
|
|
2937
2466
|
>
|
|
2938
2467
|
<div class="flex items-center justify-center gap-2">
|
|
2939
2468
|
<AddNew class="w-5 h-5 text-grey5" />
|
|
2940
|
-
<span class="body-medium text-grey7 group-hover:underline">
|
|
2469
|
+
<span class="body-medium text-grey7 group-hover:underline">{$t('editor.props.addItem')}</span>
|
|
2941
2470
|
</div>
|
|
2942
2471
|
</Button>
|
|
2943
2472
|
</div>
|
|
@@ -3018,7 +2547,7 @@
|
|
|
3018
2547
|
/* Icon stays put next to the text; label is centered in the remaining space
|
|
3019
2548
|
with a small left bias so "Save" and "Saving" never shift the icon. */
|
|
3020
2549
|
.props-editor-save-label {
|
|
3021
|
-
padding-
|
|
2550
|
+
padding-inline-start: 14px;
|
|
3022
2551
|
line-height: 1;
|
|
3023
2552
|
}
|
|
3024
2553
|
.props-editor-save-btn .props-editor-save-icon {
|
|
@@ -3101,20 +2630,20 @@
|
|
|
3101
2630
|
|
|
3102
2631
|
/* First item - rounded at top only */
|
|
3103
2632
|
:global(.accordion-item[data-position="first"]) {
|
|
3104
|
-
border-
|
|
3105
|
-
border-
|
|
3106
|
-
border-
|
|
3107
|
-
border-
|
|
2633
|
+
border-start-start-radius: var(--radius-small);
|
|
2634
|
+
border-start-end-radius: var(--radius-small);
|
|
2635
|
+
border-end-start-radius: 0;
|
|
2636
|
+
border-end-end-radius: 0;
|
|
3108
2637
|
margin-top: 0; /* Explicitly remove top margin */
|
|
3109
2638
|
}
|
|
3110
2639
|
|
|
3111
2640
|
/* Last item - rounded at bottom only */
|
|
3112
2641
|
:global(.accordion-item[data-position="last"]) {
|
|
3113
2642
|
margin-bottom: 0;
|
|
3114
|
-
border-
|
|
3115
|
-
border-
|
|
3116
|
-
border-
|
|
3117
|
-
border-
|
|
2643
|
+
border-start-start-radius: 0;
|
|
2644
|
+
border-start-end-radius: 0;
|
|
2645
|
+
border-end-start-radius: var(--radius-small);
|
|
2646
|
+
border-end-end-radius: var(--radius-small);
|
|
3118
2647
|
}
|
|
3119
2648
|
|
|
3120
2649
|
/* Middle items - no rounded corners */
|
|
@@ -3134,24 +2663,6 @@
|
|
|
3134
2663
|
justify-content: center !important;
|
|
3135
2664
|
}
|
|
3136
2665
|
|
|
3137
|
-
:global(.draggable-mirror) {
|
|
3138
|
-
opacity: 1 !important;
|
|
3139
|
-
transform-origin: 50% 50%;
|
|
3140
|
-
z-index: 100;
|
|
3141
|
-
pointer-events: none;
|
|
3142
|
-
background-color: white !important;
|
|
3143
|
-
border: 2px solid hsl(var(--grey2)) !important;
|
|
3144
|
-
border-radius: var(--radius-small) !important;
|
|
3145
|
-
}
|
|
3146
|
-
|
|
3147
|
-
:global(.draggable-source--is-dragging) {
|
|
3148
|
-
opacity: 0.3 !important;
|
|
3149
|
-
}
|
|
3150
|
-
|
|
3151
|
-
:global(html.draggable--is-dragging) {
|
|
3152
|
-
cursor: move !important;
|
|
3153
|
-
}
|
|
3154
|
-
|
|
3155
2666
|
:global([data-scrollable="false"] .scrollbar) {
|
|
3156
2667
|
display: none;
|
|
3157
2668
|
}
|
|
@@ -3234,13 +2745,13 @@
|
|
|
3234
2745
|
/* Update scrollbar styling to match */
|
|
3235
2746
|
:global([data-scrollable="true"] .scrollbar) {
|
|
3236
2747
|
width: 8px !important; /* Thinner scrollbar */
|
|
3237
|
-
margin-
|
|
2748
|
+
margin-inline-end: 2px !important; /* Add padding from the right */
|
|
3238
2749
|
}
|
|
3239
2750
|
|
|
3240
2751
|
:global([data-scrollable="true"] .thumb) {
|
|
3241
2752
|
background-color: hsl(var(--grey1)) !important;
|
|
3242
2753
|
border-radius: 3px !important;
|
|
3243
|
-
margin-
|
|
2754
|
+
margin-inline-end: 2px !important;
|
|
3244
2755
|
}
|
|
3245
2756
|
|
|
3246
2757
|
:global([data-scrollable="true"] .thumb:hover) {
|
|
@@ -3249,11 +2760,11 @@
|
|
|
3249
2760
|
|
|
3250
2761
|
:global([data-scrollable="true"] .track) {
|
|
3251
2762
|
background-color: transparent !important;
|
|
3252
|
-
margin-
|
|
2763
|
+
margin-inline-end: 2px !important;
|
|
3253
2764
|
}
|
|
3254
2765
|
|
|
3255
2766
|
:global([data-scrollable="true"]) {
|
|
3256
|
-
padding-
|
|
2767
|
+
padding-inline-end: 2px !important; /* Add padding for scrollbar */
|
|
3257
2768
|
}
|
|
3258
2769
|
|
|
3259
2770
|
/* Add these styles to ensure proper z-index layering */
|
|
@@ -3266,11 +2777,6 @@
|
|
|
3266
2777
|
z-index: 99999 !important; /* Force highest z-index */
|
|
3267
2778
|
}
|
|
3268
2779
|
|
|
3269
|
-
/* Update existing styles to ensure proper stacking */
|
|
3270
|
-
:global(.draggable-mirror) {
|
|
3271
|
-
z-index: 100;
|
|
3272
|
-
}
|
|
3273
|
-
|
|
3274
2780
|
:global(.popover-content) {
|
|
3275
2781
|
z-index: 50;
|
|
3276
2782
|
}
|
|
@@ -3382,7 +2888,7 @@
|
|
|
3382
2888
|
/* Add hover effect for title text */
|
|
3383
2889
|
:global(.content-item-card [data-radix-collapsible-trigger] h4),
|
|
3384
2890
|
:global(.generic-content-card [data-radix-collapsible-trigger] h3) {
|
|
3385
|
-
text-align:
|
|
2891
|
+
text-align: start !important;
|
|
3386
2892
|
}
|
|
3387
2893
|
|
|
3388
2894
|
/* Add hover overlay for the entire trigger area */
|
|
@@ -3454,7 +2960,7 @@
|
|
|
3454
2960
|
/* Keep text left-aligned */
|
|
3455
2961
|
:global(.content-item-card h4),
|
|
3456
2962
|
:global(.generic-content-card h3) {
|
|
3457
|
-
text-align:
|
|
2963
|
+
text-align: start !important;
|
|
3458
2964
|
}
|
|
3459
2965
|
|
|
3460
2966
|
/* Add this new rule to remove focus borders */
|
|
@@ -3524,7 +3030,7 @@
|
|
|
3524
3030
|
:global(.input-controller > div:first-child) {
|
|
3525
3031
|
flex: 1 !important;
|
|
3526
3032
|
min-width: 0 !important;
|
|
3527
|
-
margin-
|
|
3033
|
+
margin-inline-end: 16px !important;
|
|
3528
3034
|
}
|
|
3529
3035
|
|
|
3530
3036
|
:global(.input-controller > div:last-child) {
|
|
@@ -3546,7 +3052,7 @@
|
|
|
3546
3052
|
width: 100% !important;
|
|
3547
3053
|
max-width: 100% !important;
|
|
3548
3054
|
flex: none !important;
|
|
3549
|
-
margin-
|
|
3055
|
+
margin-inline-end: 0 !important;
|
|
3550
3056
|
}
|
|
3551
3057
|
|
|
3552
3058
|
/* New animation system */
|
|
@@ -3726,17 +3232,17 @@
|
|
|
3726
3232
|
|
|
3727
3233
|
/* Modify existing position rules */
|
|
3728
3234
|
:global(.accordion-item[data-position="first"]) {
|
|
3729
|
-
border-
|
|
3730
|
-
border-
|
|
3731
|
-
border-
|
|
3732
|
-
border-
|
|
3235
|
+
border-start-start-radius: var(--radius-small);
|
|
3236
|
+
border-start-end-radius: var(--radius-small);
|
|
3237
|
+
border-end-start-radius: 0;
|
|
3238
|
+
border-end-end-radius: 0;
|
|
3733
3239
|
}
|
|
3734
3240
|
|
|
3735
3241
|
:global(.accordion-item[data-position="last"]) {
|
|
3736
|
-
border-
|
|
3737
|
-
border-
|
|
3738
|
-
border-
|
|
3739
|
-
border-
|
|
3242
|
+
border-start-start-radius: 0;
|
|
3243
|
+
border-start-end-radius: 0;
|
|
3244
|
+
border-end-start-radius: var(--radius-small);
|
|
3245
|
+
border-end-end-radius: var(--radius-small);
|
|
3740
3246
|
}
|
|
3741
3247
|
|
|
3742
3248
|
:global(.accordion-item[data-position="middle"]) {
|
|
@@ -3771,26 +3277,26 @@
|
|
|
3771
3277
|
|
|
3772
3278
|
/* Ensure scroll area maintains border radius */
|
|
3773
3279
|
:global([data-radix-scroll-area-viewport]) {
|
|
3774
|
-
border-
|
|
3280
|
+
border-end-end-radius: var(--radius-large) !important;
|
|
3775
3281
|
}
|
|
3776
3282
|
|
|
3777
3283
|
:global(.scroll-area) {
|
|
3778
|
-
border-
|
|
3284
|
+
border-end-end-radius: var(--radius-large) !important;
|
|
3779
3285
|
}
|
|
3780
3286
|
|
|
3781
3287
|
:global(.scroll-viewport) {
|
|
3782
|
-
border-
|
|
3288
|
+
border-end-end-radius: var(--radius-large) !important;
|
|
3783
3289
|
}
|
|
3784
3290
|
|
|
3785
3291
|
/* Ensure scrollbar doesn't affect border radius */
|
|
3786
3292
|
:global([data-radix-scroll-area-scrollbar]) {
|
|
3787
|
-
border-
|
|
3788
|
-
padding-
|
|
3293
|
+
border-end-end-radius: var(--radius-large) !important;
|
|
3294
|
+
padding-inline-end: 2px !important;
|
|
3789
3295
|
}
|
|
3790
3296
|
|
|
3791
3297
|
:global([data-radix-scroll-area-thumb]) {
|
|
3792
3298
|
border-radius: var(--radius-small) !important;
|
|
3793
|
-
margin-
|
|
3299
|
+
margin-inline-end: 2px !important;
|
|
3794
3300
|
}
|
|
3795
3301
|
|
|
3796
3302
|
/* Add z-index management for tooltips */
|
|
@@ -3830,7 +3336,7 @@
|
|
|
3830
3336
|
/* Content Item Hover Styles */
|
|
3831
3337
|
:global(.content-item-card [data-radix-collapsible-trigger] h4),
|
|
3832
3338
|
:global(.generic-content-card [data-radix-collapsible-trigger] h3) {
|
|
3833
|
-
text-align:
|
|
3339
|
+
text-align: start !important;
|
|
3834
3340
|
}
|
|
3835
3341
|
|
|
3836
3342
|
/* Add new specific hover styles for content titles */
|
|
@@ -3863,7 +3369,6 @@
|
|
|
3863
3369
|
text-decoration: underline;
|
|
3864
3370
|
}
|
|
3865
3371
|
|
|
3866
|
-
/* Add Draggable Sort Animation styles */
|
|
3867
3372
|
:global(.content-item) {
|
|
3868
3373
|
position: relative;
|
|
3869
3374
|
z-index: 1;
|
|
@@ -3871,95 +3376,77 @@
|
|
|
3871
3376
|
transform: translate3d(0, 0, 0);
|
|
3872
3377
|
}
|
|
3873
3378
|
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
:global(.draggable-source--is-dragging .content-title) {
|
|
3883
|
-
text-decoration: none !important;
|
|
3379
|
+
/*
|
|
3380
|
+
* Reordering the items by dragging a handle (utils/itemReorder.ts). While an item is dragged, every item is
|
|
3381
|
+
* folded to one row at once (the items that were open keep their content, it is only hidden), so the whole
|
|
3382
|
+
* list is within reach and the places to drop do not move under the pointer.
|
|
3383
|
+
*/
|
|
3384
|
+
:global(.content-items.is-reordering) {
|
|
3385
|
+
user-select: none;
|
|
3386
|
+
-webkit-user-select: none;
|
|
3884
3387
|
}
|
|
3885
3388
|
|
|
3886
|
-
:
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
:global(.content-items--is-dragging .content-item:not(.draggable-source--is-dragging)) {
|
|
3893
|
-
transition: transform 200ms ease-in-out;
|
|
3894
|
-
}
|
|
3895
|
-
|
|
3896
|
-
:global(.content-item.draggable-mirror) {
|
|
3897
|
-
transform: scale(1.05);
|
|
3389
|
+
/* The card is what is folded, not the item: the line that shows the drop place sits outside the item's box */
|
|
3390
|
+
:global(.content-items.is-reordering .content-item-card) {
|
|
3391
|
+
height: 56px !important;
|
|
3392
|
+
min-height: 56px !important;
|
|
3393
|
+
max-height: 56px !important;
|
|
3394
|
+
overflow: hidden !important;
|
|
3898
3395
|
}
|
|
3899
3396
|
|
|
3900
|
-
:global(.content-
|
|
3901
|
-
|
|
3397
|
+
:global(.content-items.is-reordering [data-collapsible-content]) {
|
|
3398
|
+
display: none !important;
|
|
3902
3399
|
}
|
|
3903
3400
|
|
|
3904
|
-
|
|
3401
|
+
/* The item being dragged stays in place, faded; the floating copy is what follows the pointer */
|
|
3402
|
+
:global(.content-item.is-reorder-source) {
|
|
3905
3403
|
opacity: 0.3;
|
|
3906
3404
|
}
|
|
3907
3405
|
|
|
3908
|
-
:
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
color: hsl(var(--
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
:global(.draggable--original) {
|
|
3921
|
-
display: none;
|
|
3406
|
+
/* A line in the gap where the dragged item would be dropped: in front of an item, or behind the last one */
|
|
3407
|
+
:global(.content-item.is-reorder-drop-before)::before,
|
|
3408
|
+
:global(.content-item.is-reorder-drop-after)::after {
|
|
3409
|
+
content: '';
|
|
3410
|
+
position: absolute;
|
|
3411
|
+
left: 0;
|
|
3412
|
+
right: 0;
|
|
3413
|
+
height: 3px;
|
|
3414
|
+
border-radius: 2px;
|
|
3415
|
+
background-color: hsl(var(--sage-green));
|
|
3416
|
+
pointer-events: none;
|
|
3922
3417
|
}
|
|
3923
3418
|
|
|
3924
|
-
:global(.content-
|
|
3925
|
-
|
|
3419
|
+
:global(.content-item.is-reorder-drop-before)::before {
|
|
3420
|
+
top: -9px;
|
|
3926
3421
|
}
|
|
3927
3422
|
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
background-color: transparent;
|
|
3423
|
+
:global(.content-item.is-reorder-drop-after)::after {
|
|
3424
|
+
bottom: -9px;
|
|
3931
3425
|
}
|
|
3932
3426
|
|
|
3933
|
-
|
|
3934
|
-
|
|
3427
|
+
/* The floating copy of the dragged item: one row, with the editor's green outline */
|
|
3428
|
+
:global(.reorder-mirror) {
|
|
3429
|
+
box-sizing: border-box;
|
|
3430
|
+
border: 2px solid hsl(var(--sage-green));
|
|
3431
|
+
border-radius: var(--radius-small);
|
|
3432
|
+
background-color: white;
|
|
3433
|
+
box-shadow: none;
|
|
3434
|
+
will-change: transform;
|
|
3935
3435
|
}
|
|
3936
3436
|
|
|
3937
|
-
:global(.
|
|
3437
|
+
:global(.reorder-mirror .content-title) {
|
|
3438
|
+
color: hsl(var(--sage-green));
|
|
3938
3439
|
text-decoration: none !important;
|
|
3939
3440
|
}
|
|
3940
3441
|
|
|
3941
|
-
:global(.
|
|
3942
|
-
|
|
3442
|
+
:global(.reorder-mirror .chevron),
|
|
3443
|
+
:global(.reorder-mirror .drag-handle svg) {
|
|
3444
|
+
color: hsl(var(--sage-green));
|
|
3943
3445
|
}
|
|
3944
3446
|
|
|
3945
|
-
:
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
:global(.content-item.draggable--original) {
|
|
3950
|
-
opacity: 0;
|
|
3951
|
-
}
|
|
3952
|
-
|
|
3953
|
-
:global(.content-item.draggable-source--is-dragging) {
|
|
3954
|
-
opacity: 0.3;
|
|
3955
|
-
}
|
|
3956
|
-
|
|
3957
|
-
:global(.content-item.draggable-source--is-dragging .content-title) {
|
|
3958
|
-
text-decoration: none !important;
|
|
3959
|
-
}
|
|
3960
|
-
|
|
3961
|
-
:global(.content-item.draggable-mirror .content-title) {
|
|
3962
|
-
text-decoration: none !important;
|
|
3447
|
+
/* The card inside the copy has its own border: the outline above is the only one */
|
|
3448
|
+
:global(.reorder-mirror .content-item-card) {
|
|
3449
|
+
border: none;
|
|
3963
3450
|
}
|
|
3964
3451
|
|
|
3965
3452
|
/* Update drag handle hover area and content title behavior */
|
|
@@ -3970,6 +3457,8 @@
|
|
|
3970
3457
|
justify-content: center !important;
|
|
3971
3458
|
height: 100% !important;
|
|
3972
3459
|
cursor: move !important;
|
|
3460
|
+
/* Touching the handle drags the item; it must not scroll the page */
|
|
3461
|
+
touch-action: none;
|
|
3973
3462
|
}
|
|
3974
3463
|
|
|
3975
3464
|
/* Keep the icon size consistent */
|
|
@@ -3995,40 +3484,15 @@
|
|
|
3995
3484
|
|
|
3996
3485
|
/* Remove any previous left padding from the flex container */
|
|
3997
3486
|
:global(.content-item-trigger .flex) {
|
|
3998
|
-
padding-
|
|
3999
|
-
}
|
|
4000
|
-
|
|
4001
|
-
:global(.draggable-mirror .drag-handle svg) {
|
|
4002
|
-
color: hsl(var(--grey3)) !important;
|
|
4003
|
-
}
|
|
4004
|
-
|
|
4005
|
-
:global(.draggable--original) {
|
|
4006
|
-
display: none;
|
|
3487
|
+
padding-inline-start: 0 !important;
|
|
4007
3488
|
}
|
|
4008
3489
|
|
|
4009
|
-
:global(.content-items--is-dragging) {
|
|
4010
|
-
cursor: grabbing;
|
|
4011
|
-
}
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
3490
|
:global(.content-item) {
|
|
4015
3491
|
contain: layout style;
|
|
4016
3492
|
will-change: transform;
|
|
4017
3493
|
transform: translate3d(0, 0, 0);
|
|
4018
3494
|
}
|
|
4019
3495
|
|
|
4020
|
-
:global(.draggable-mirror) {
|
|
4021
|
-
contain: layout style;
|
|
4022
|
-
will-change: transform;
|
|
4023
|
-
transform: translate3d(0, 0, 0);
|
|
4024
|
-
}
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
:global(.content-items--is-dragging .content-item:not(.draggable-source--is-dragging)) {
|
|
4028
|
-
transition: transform 200ms cubic-bezier(0.2, 0, 0.2, 1);
|
|
4029
|
-
will-change: transform;
|
|
4030
|
-
}
|
|
4031
|
-
|
|
4032
3496
|
/* Animation classes */
|
|
4033
3497
|
:global(.design-tab-content[data-state="active"]) {
|
|
4034
3498
|
animation: none;
|
|
@@ -4050,6 +3514,15 @@
|
|
|
4050
3514
|
transform: none;
|
|
4051
3515
|
}
|
|
4052
3516
|
|
|
3517
|
+
/* The two tabs trade places on a right to left screen, so each panel slides in from the side that its tab is on. */
|
|
3518
|
+
:global(.design-tab-content.animate-slide-left[data-state="active"]:dir(rtl)) {
|
|
3519
|
+
animation-name: slideFromRight;
|
|
3520
|
+
}
|
|
3521
|
+
|
|
3522
|
+
:global(.content-tab-content.animate-slide-right[data-state="active"]:dir(rtl)) {
|
|
3523
|
+
animation-name: slideFromLeft;
|
|
3524
|
+
}
|
|
3525
|
+
|
|
4053
3526
|
@keyframes slideFromLeft {
|
|
4054
3527
|
from {
|
|
4055
3528
|
opacity: 0;
|
|
@@ -4094,8 +3567,6 @@
|
|
|
4094
3567
|
}
|
|
4095
3568
|
}
|
|
4096
3569
|
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
3570
|
@keyframes slideUp {
|
|
4100
3571
|
from {
|
|
4101
3572
|
opacity: 0;
|
|
@@ -4147,46 +3618,6 @@
|
|
|
4147
3618
|
}
|
|
4148
3619
|
}
|
|
4149
3620
|
|
|
4150
|
-
/* Draggable mirror styles - styling only, no positioning */
|
|
4151
|
-
:global(.draggable-mirror) {
|
|
4152
|
-
opacity: 1 !important;
|
|
4153
|
-
background-color: white !important;
|
|
4154
|
-
border: 2px solid hsl(var(--grey2)) !important;
|
|
4155
|
-
border-radius: var(--radius-small) !important;
|
|
4156
|
-
box-shadow: none !important;
|
|
4157
|
-
}
|
|
4158
|
-
|
|
4159
|
-
/* Style for item being hovered over (potential drop target) */
|
|
4160
|
-
:global(.content-items--is-dragging .content-item.draggable--over) {
|
|
4161
|
-
transform: scale(1.015) !important;
|
|
4162
|
-
border: 2px solid hsl(var(--grey2)) !important;
|
|
4163
|
-
border-radius: var(--radius-small) !important;
|
|
4164
|
-
background-color: hsl(var(--grey1)) !important;
|
|
4165
|
-
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05) !important;
|
|
4166
|
-
z-index: 10 !important;
|
|
4167
|
-
}
|
|
4168
|
-
|
|
4169
|
-
/* Remove all borders and shadows from draggable mirror */
|
|
4170
|
-
:global(.draggable-mirror) {
|
|
4171
|
-
border: 2px solid hsl(var(--grey2)) !important;
|
|
4172
|
-
border-radius: var(--radius-small) !important;
|
|
4173
|
-
box-shadow: none !important;
|
|
4174
|
-
}
|
|
4175
|
-
|
|
4176
|
-
/* Remove borders from all children inside the mirror */
|
|
4177
|
-
:global(.draggable-mirror *) {
|
|
4178
|
-
border: none !important;
|
|
4179
|
-
border-top: none !important;
|
|
4180
|
-
border-bottom: none !important;
|
|
4181
|
-
border-left: none !important;
|
|
4182
|
-
border-right: none !important;
|
|
4183
|
-
}
|
|
4184
|
-
|
|
4185
|
-
/* Make the original item completely transparent during dragging */
|
|
4186
|
-
:global(.draggable-source--is-dragging) {
|
|
4187
|
-
opacity: 0 !important;
|
|
4188
|
-
}
|
|
4189
|
-
|
|
4190
3621
|
:global(body.dragging-preserve-borders .form-input),
|
|
4191
3622
|
:global(body.dragging-preserve-borders [id^="number-input"]),
|
|
4192
3623
|
:global(body.dragging-preserve-borders .number-input-button),
|
|
@@ -4197,20 +3628,6 @@
|
|
|
4197
3628
|
border: 1px solid hsl(var(--grey2)) !important;
|
|
4198
3629
|
}
|
|
4199
3630
|
|
|
4200
|
-
/* Add a new rule to force fixed heights during dragging */
|
|
4201
|
-
:global(.dragging-active .content-item) {
|
|
4202
|
-
height: 56px !important;
|
|
4203
|
-
min-height: 56px !important;
|
|
4204
|
-
max-height: 56px !important;
|
|
4205
|
-
overflow: hidden !important;
|
|
4206
|
-
}
|
|
4207
|
-
|
|
4208
|
-
:global(.dragging-active [data-radix-collapsible-content]) {
|
|
4209
|
-
height: 0 !important;
|
|
4210
|
-
display: none !important;
|
|
4211
|
-
overflow: hidden !important;
|
|
4212
|
-
}
|
|
4213
|
-
|
|
4214
3631
|
/* Add explicit transition for content tab chevrons */
|
|
4215
3632
|
:global(.content-item-card .chevron),
|
|
4216
3633
|
:global(.generic-content-card .chevron) {
|