@widgetic/editor 4.0.6 → 4.0.8
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 +12 -1
- package/dist/components/WidgetPreview.svelte +42 -1
- package/dist/components/WidgetPreview.svelte.d.ts +8 -0
- package/dist/components/editors/EmbedEditor.svelte +101 -530
- package/dist/components/editors/EmbedEditor.svelte.d.ts +7 -17
- package/dist/components/editors/PropsEditor.svelte +328 -142
- package/dist/components/editors/PropsEditor.svelte.d.ts +13 -2
- package/dist/components/ic/AngleInputController.svelte +21 -7
- package/dist/components/ic/AngleInputController.svelte.d.ts +16 -6
- package/dist/components/ic/BrowserInputController.svelte +28 -15
- package/dist/components/ic/BrowserInputController.svelte.d.ts +25 -19
- package/dist/components/ic/CheckboxInputController.svelte +21 -7
- package/dist/components/ic/CheckboxInputController.svelte.d.ts +25 -15
- package/dist/components/ic/ColorPickerInputController.svelte +47 -21
- package/dist/components/ic/ColorPickerInputController.svelte.d.ts +42 -19
- package/dist/components/ic/DateFieldInputController.svelte +21 -7
- package/dist/components/ic/DateFieldInputController.svelte.d.ts +24 -14
- package/dist/components/ic/DatePickerInputController.svelte +25 -8
- package/dist/components/ic/DatePickerInputController.svelte.d.ts +21 -8
- package/dist/components/ic/DropdownInputController.svelte +69 -19
- package/dist/components/ic/DropdownInputController.svelte.d.ts +33 -17
- package/dist/components/ic/FixedPositionInputController.svelte +24 -10
- package/dist/components/ic/FixedPositionInputController.svelte.d.ts +16 -4
- package/dist/components/ic/FontInputController.svelte +53 -18
- package/dist/components/ic/FontInputController.svelte.d.ts +33 -18
- package/dist/components/ic/InputControllerRenderer.svelte +98 -0
- package/dist/components/ic/InputControllerRenderer.svelte.d.ts +40 -0
- package/dist/components/ic/NumberInputController.svelte +23 -9
- package/dist/components/ic/NumberInputController.svelte.d.ts +18 -7
- package/dist/components/ic/PositionInputController.svelte +17 -5
- package/dist/components/ic/PositionInputController.svelte.d.ts +20 -11
- package/dist/components/ic/RadioGroupInputController.svelte +21 -7
- package/dist/components/ic/RadioGroupInputController.svelte.d.ts +22 -12
- package/dist/components/ic/RangeCalendarInputController.svelte +34 -16
- package/dist/components/ic/RangeCalendarInputController.svelte.d.ts +25 -11
- package/dist/components/ic/RoundedCornersInputController.svelte +26 -22
- package/dist/components/ic/RoundedCornersInputController.svelte.d.ts +26 -18
- package/dist/components/ic/SliderInputController.svelte +41 -18
- package/dist/components/ic/SliderInputController.svelte.d.ts +35 -19
- package/dist/components/ic/SliderRangeInputController.svelte +27 -11
- package/dist/components/ic/SliderRangeInputController.svelte.d.ts +22 -9
- package/dist/components/ic/SwitchInputController.svelte +17 -5
- package/dist/components/ic/SwitchInputController.svelte.d.ts +14 -5
- package/dist/components/ic/TagsInputController.svelte +21 -7
- package/dist/components/ic/TagsInputController.svelte.d.ts +16 -6
- package/dist/components/ic/TextAreaInputController.svelte +44 -25
- package/dist/components/ic/TextAreaInputController.svelte.d.ts +6 -4
- package/dist/components/ic/TextAreaPreviewInputController.svelte +21 -7
- package/dist/components/ic/TextAreaPreviewInputController.svelte.d.ts +16 -6
- package/dist/components/ic/TextInputController.svelte +23 -8
- package/dist/components/ic/TextInputController.svelte.d.ts +20 -8
- package/dist/components/ic/TimeInputController.svelte +17 -5
- package/dist/components/ic/TimeInputController.svelte.d.ts +14 -5
- package/dist/components/ic/VectorInputController.svelte +29 -10
- package/dist/components/ic/VectorInputController.svelte.d.ts +33 -18
- package/dist/components/icons/locked.svelte +5 -1
- package/dist/components/icons/locked.svelte.d.ts +11 -24
- package/dist/components/icons/unlocked.svelte +5 -1
- package/dist/components/icons/unlocked.svelte.d.ts +11 -24
- package/dist/index.d.ts +1 -0
- package/dist/schemaViews.d.ts +105 -0
- package/dist/schemaViews.js +11 -0
- package/dist/utils/FontManager.js +11 -4
- package/package.json +16 -12
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<svelte:options runes={false} />
|
|
2
|
+
|
|
1
3
|
<script lang="ts">
|
|
2
4
|
/**
|
|
3
5
|
* PropsEditor Component
|
|
@@ -13,7 +15,16 @@
|
|
|
13
15
|
// Svelte imports
|
|
14
16
|
import { onMount, onDestroy, createEventDispatcher, afterUpdate } from 'svelte';
|
|
15
17
|
const dispatch = createEventDispatcher();
|
|
16
|
-
import { writable, get } from 'svelte/store';
|
|
18
|
+
import { writable, get, type Writable } from 'svelte/store';
|
|
19
|
+
import type {
|
|
20
|
+
ContentSchemaItemView,
|
|
21
|
+
ContentSchemaPropertyView,
|
|
22
|
+
ContentSchemaView,
|
|
23
|
+
DesignSchemaComponentView,
|
|
24
|
+
DesignSchemaPropertyGroupView,
|
|
25
|
+
DesignSchemaPropertyView,
|
|
26
|
+
DesignSchemaView,
|
|
27
|
+
} from '../../schemaViews.js';
|
|
17
28
|
|
|
18
29
|
/**
|
|
19
30
|
* COMPONENTS IMPORTS
|
|
@@ -59,10 +70,12 @@
|
|
|
59
70
|
TagsInputController,
|
|
60
71
|
DateFieldInputController
|
|
61
72
|
} from '../ic';
|
|
73
|
+
import type { InputControllerConfig } from '../ic';
|
|
62
74
|
|
|
63
75
|
/**
|
|
64
76
|
* UTILS IMPORTS
|
|
65
77
|
*/
|
|
78
|
+
import InputControllerRenderer, { type ControllerDescriptor } from '../ic/InputControllerRenderer.svelte';
|
|
66
79
|
import MediaManager from '../../utils/MediaManager';
|
|
67
80
|
import FontManager from '../../utils/FontManager';
|
|
68
81
|
import { config as appConfig } from '../../config';
|
|
@@ -81,15 +94,6 @@
|
|
|
81
94
|
*/
|
|
82
95
|
type InputValue = string | number | boolean | any | null;
|
|
83
96
|
|
|
84
|
-
interface PropertyGroup {
|
|
85
|
-
name: string;
|
|
86
|
-
properties: Array<Property>;
|
|
87
|
-
visibilityCondition?: {
|
|
88
|
-
dependsOn: string | string[];
|
|
89
|
-
condition: string;
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
97
|
// Add this interface near the other interfaces at the top of the file
|
|
94
98
|
interface ContentProperty {
|
|
95
99
|
id: string;
|
|
@@ -114,9 +118,10 @@
|
|
|
114
118
|
// Dimensions
|
|
115
119
|
export let width: string = "450px";
|
|
116
120
|
export let height: string = "720px";
|
|
117
|
-
// Schemas
|
|
118
|
-
|
|
119
|
-
export let
|
|
121
|
+
// Schemas — typed against the package's canonical schema views
|
|
122
|
+
// (`$lib/schemaViews`), the same shapes the consumers normalize to.
|
|
123
|
+
export let designSchema: DesignSchemaView | null = null;
|
|
124
|
+
export let contentSchema: ContentSchemaView | undefined = undefined;
|
|
120
125
|
export let itemTemplate: any = null; // Store template for new items
|
|
121
126
|
// Tab titles
|
|
122
127
|
export let designTabTitle: string = 'Design';
|
|
@@ -135,6 +140,23 @@
|
|
|
135
140
|
// Enable built-in drag (component manages its own absolute positioning)
|
|
136
141
|
export let enableDrag = true;
|
|
137
142
|
|
|
143
|
+
/** Show Save status control in the top drag/header bar (composition page). */
|
|
144
|
+
export let showSaveButton = false;
|
|
145
|
+
/** True when local edits are not yet persisted. */
|
|
146
|
+
export let saveDirty = false;
|
|
147
|
+
/** True while a save request is in flight. */
|
|
148
|
+
export let saveSaving = false;
|
|
149
|
+
|
|
150
|
+
/** Optional content below the shared toolbar, before property fields. */
|
|
151
|
+
export let headerContent = undefined;
|
|
152
|
+
export let headerClass = '';
|
|
153
|
+
export let dragHandleClass = 'text-grey2';
|
|
154
|
+
export let title = 'Edit Properties';
|
|
155
|
+
/** Hide the default missing-schema message for header-only editors. */
|
|
156
|
+
export let showSchemaError = true;
|
|
157
|
+
/** Optional layout wrapper moved by this editor's built-in drag and position API. */
|
|
158
|
+
export let positionTarget = null;
|
|
159
|
+
|
|
138
160
|
// Initial values from saved composition (merged over schema defaults)
|
|
139
161
|
export let initialDesignValues: Record<string, any> = {};
|
|
140
162
|
export let initialContentValues: Record<string, any> = {};
|
|
@@ -144,8 +166,11 @@
|
|
|
144
166
|
*/
|
|
145
167
|
const CONTENT_PADDING = 16; // Outer padding for both tabs
|
|
146
168
|
const CONTENT_INNER_PADDING = 16; // Inner padding for cards in Content tab
|
|
147
|
-
const
|
|
148
|
-
const
|
|
169
|
+
const DRAG_BAR_HEIGHT = 28; // Drag handle / Save row (absolute, overlays the header top)
|
|
170
|
+
const TABS_VERTICAL_PADDING = 14; // Breathing room above and below the tab list
|
|
171
|
+
const TABS_LIST_HEIGHT = 56; // Tabs.List height
|
|
172
|
+
// Drag bar band + gap + tabs + gap + 1px separator
|
|
173
|
+
const HEADER_HEIGHT = DRAG_BAR_HEIGHT + TABS_VERTICAL_PADDING * 2 + TABS_LIST_HEIGHT + 1;
|
|
149
174
|
const EDITOR_STROKE_COLOR = '#e5e7eb'; // Color for editor stroke
|
|
150
175
|
const EDITOR_STROKE_WIDTH = '1px'; // Thickness of editor stroke
|
|
151
176
|
|
|
@@ -191,7 +216,7 @@
|
|
|
191
216
|
const DRAG_SHIFT_X = 24; // shift mirror left so cursor is over drag handle
|
|
192
217
|
|
|
193
218
|
// Helper function to get visible groups
|
|
194
|
-
function getVisibleGroups(components:
|
|
219
|
+
function getVisibleGroups(components: DesignSchemaComponentView[]): PropertyGroup[] {
|
|
195
220
|
return components?.reduce((acc: PropertyGroup[], component) => {
|
|
196
221
|
const visibleGroups = component.propertyGroups.filter((group: PropertyGroup) =>
|
|
197
222
|
shouldShowPropertyGroup(group, component.propertyGroups[0].properties, 'design')
|
|
@@ -205,7 +230,7 @@
|
|
|
205
230
|
|
|
206
231
|
// Update the function with proper type annotations
|
|
207
232
|
function getAccordionItemPosition(
|
|
208
|
-
component:
|
|
233
|
+
component: DesignSchemaComponentView,
|
|
209
234
|
propertyGroup: PropertyGroup,
|
|
210
235
|
groupIndex: number
|
|
211
236
|
): 'first' | 'middle' | 'last' | 'only' {
|
|
@@ -928,8 +953,11 @@
|
|
|
928
953
|
*/
|
|
929
954
|
onDestroy(() => {
|
|
930
955
|
// Clean up drag listeners
|
|
931
|
-
|
|
932
|
-
|
|
956
|
+
if (browser) {
|
|
957
|
+
document.removeEventListener('mousemove', handleDragMove);
|
|
958
|
+
document.removeEventListener('mouseup', handleDragEnd);
|
|
959
|
+
if (dragState.active) document.body.style.cursor = '';
|
|
960
|
+
}
|
|
933
961
|
|
|
934
962
|
// Clean up scroll listeners
|
|
935
963
|
if (designScrollViewport) {
|
|
@@ -1331,7 +1359,44 @@
|
|
|
1331
1359
|
return '';
|
|
1332
1360
|
}
|
|
1333
1361
|
|
|
1334
|
-
|
|
1362
|
+
/**
|
|
1363
|
+
* The bag `renderInputController()` builds for one controller instance.
|
|
1364
|
+
* Declared keys are what PropsEditor itself always supplies; the index
|
|
1365
|
+
* signature carries the schema-driven constraint keys (`min`, `max`,
|
|
1366
|
+
* `options`, …) whose set depends on the property definition.
|
|
1367
|
+
* `describeInputController` picks from this bag per controller kind, so
|
|
1368
|
+
* anything a controller never reads never reaches it.
|
|
1369
|
+
*/
|
|
1370
|
+
interface InputControllerProps {
|
|
1371
|
+
id: string;
|
|
1372
|
+
value: InputValue;
|
|
1373
|
+
opacity?: number;
|
|
1374
|
+
onChange: (value: InputValue) => void;
|
|
1375
|
+
onOpacityChange: (opacity: number) => void;
|
|
1376
|
+
portalTarget: string | HTMLElement;
|
|
1377
|
+
header?: { label: string; helpText?: string };
|
|
1378
|
+
config: InputControllerConfig;
|
|
1379
|
+
controllerId: string;
|
|
1380
|
+
openDropdowns: Writable<Set<string>>;
|
|
1381
|
+
toggleDropdown: (id: string) => void;
|
|
1382
|
+
onFocus: () => void;
|
|
1383
|
+
[schemaKey: string]: any;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
/**
|
|
1387
|
+
* What `renderInputController()` hands the template.
|
|
1388
|
+
* `component` is a 20-member union of controller component types — typing
|
|
1389
|
+
* it as the union makes `<svelte:component this={...}>` collapse attribute
|
|
1390
|
+
* checking to `never` (every prop errors); `any` restores the spread's
|
|
1391
|
+
* intended looseness.
|
|
1392
|
+
*/
|
|
1393
|
+
interface RenderedInputController {
|
|
1394
|
+
component: any; // union of controller components — see comment above
|
|
1395
|
+
props: InputControllerProps;
|
|
1396
|
+
header: { label: string; helpText: string };
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
function renderInputController(schema: string, property: any, propertyIndex: number, isFirstGroup: boolean, contentItemId?: string): RenderedInputController {
|
|
1335
1400
|
const basePropertyId = property.id || property.name;
|
|
1336
1401
|
const propertyId = contentItemId ? `${contentItemId}__${basePropertyId}` : basePropertyId;
|
|
1337
1402
|
const controllerId = `${schema}-${propertyId}`;
|
|
@@ -1342,7 +1407,7 @@
|
|
|
1342
1407
|
component: null,
|
|
1343
1408
|
props: { id: controllerId },
|
|
1344
1409
|
header: { label: basePropertyId, helpText: '' }
|
|
1345
|
-
};
|
|
1410
|
+
} as RenderedInputController;
|
|
1346
1411
|
}
|
|
1347
1412
|
const InputControllerComponent = getInputControllerComponent(inputController.type.toLowerCase());
|
|
1348
1413
|
|
|
@@ -1366,17 +1431,11 @@
|
|
|
1366
1431
|
|
|
1367
1432
|
const { description, label, ...restInputController } = inputController;
|
|
1368
1433
|
|
|
1369
|
-
// Only set shouldFocus on initial mount or explicit focus request
|
|
1370
|
-
const shouldFocus = activeControllerId === controllerId;
|
|
1371
|
-
|
|
1372
1434
|
// Add special handling for position controller
|
|
1373
1435
|
const additionalProps = inputController.type.toLowerCase() === 'position'
|
|
1374
1436
|
? { onDragStateChange: (dragging: boolean) => isDragging = dragging }
|
|
1375
1437
|
: {};
|
|
1376
1438
|
|
|
1377
|
-
// Add visibility dependency tracking
|
|
1378
|
-
const hasVisibilityCondition = property.visibilityCondition !== undefined;
|
|
1379
|
-
|
|
1380
1439
|
return {
|
|
1381
1440
|
component: InputControllerComponent,
|
|
1382
1441
|
props: {
|
|
@@ -1405,7 +1464,6 @@
|
|
|
1405
1464
|
openDropdowns.set(new Set([id]));
|
|
1406
1465
|
}
|
|
1407
1466
|
},
|
|
1408
|
-
shouldFocus,
|
|
1409
1467
|
onFocus: () => {
|
|
1410
1468
|
const newControllerId = `${schema}-${propertyId}`;
|
|
1411
1469
|
if (schema === 'design' && lastFocusedDesignController !== newControllerId) {
|
|
@@ -1427,8 +1485,7 @@
|
|
|
1427
1485
|
groupName: schema
|
|
1428
1486
|
});
|
|
1429
1487
|
}
|
|
1430
|
-
}
|
|
1431
|
-
'data-visibility-dependent': hasVisibilityCondition
|
|
1488
|
+
}
|
|
1432
1489
|
},
|
|
1433
1490
|
header: {
|
|
1434
1491
|
label: String(inputController.type || '').toLowerCase() === 'font'
|
|
@@ -1475,6 +1532,106 @@
|
|
|
1475
1532
|
}
|
|
1476
1533
|
}
|
|
1477
1534
|
|
|
1535
|
+
/**
|
|
1536
|
+
* Narrows the render bag down to exactly the props the chosen controller
|
|
1537
|
+
* declares. Each pick literal is contextually typed against that
|
|
1538
|
+
* controller's declared props interface, so passing a key the controller
|
|
1539
|
+
* never reads — or forgetting a required one — is a compile error here
|
|
1540
|
+
* instead of a silent no-op at runtime.
|
|
1541
|
+
*/
|
|
1542
|
+
function describeInputController(component: ReturnType<typeof getInputControllerComponent> | null, props: InputControllerProps): ControllerDescriptor | null {
|
|
1543
|
+
switch (component) {
|
|
1544
|
+
case TextInputController: return { kind: 'textinput', props: {
|
|
1545
|
+
label: props.label, value: props.value, onChange: props.onChange,
|
|
1546
|
+
config: props.config, type: props.type, placeholder: props.placeholder,
|
|
1547
|
+
description: props.description, onFocus: props.onFocus } };
|
|
1548
|
+
case TextAreaPreviewInputController: return { kind: 'textarea', props: {
|
|
1549
|
+
label: props.label, value: props.value, onChange: props.onChange,
|
|
1550
|
+
config: props.config, description: props.description, portalTarget: props.portalTarget } };
|
|
1551
|
+
case NumberInputController: return { kind: 'number', props: {
|
|
1552
|
+
label: props.label, value: props.value, onChange: props.onChange, config: props.config,
|
|
1553
|
+
helpText: props.helpText, description: props.description, onFocus: props.onFocus } };
|
|
1554
|
+
case SliderInputController: return { kind: 'slider', props: {
|
|
1555
|
+
height: props.height, elementsGap: props.elementsGap, thumbSize: props.thumbSize,
|
|
1556
|
+
sliderType: props.sliderType, barHeight: props.barHeight, label: props.label,
|
|
1557
|
+
value: props.value, onChange: props.onChange, config: props.config,
|
|
1558
|
+
helpText: props.helpText, min: props.min, max: props.max, step: props.step,
|
|
1559
|
+
onFocus: props.onFocus, autofocus: props.autofocus } };
|
|
1560
|
+
case SliderRangeInputController: return { kind: 'sliderrange', props: {
|
|
1561
|
+
label: props.label, value: props.value, onChange: props.onChange, config: props.config,
|
|
1562
|
+
helpText: props.helpText, min: props.min, max: props.max, step: props.step,
|
|
1563
|
+
onFocus: props.onFocus } };
|
|
1564
|
+
case DropdownInputController: return { kind: 'dropdown', props: {
|
|
1565
|
+
label: props.label, defaultSelectedLabel: props.defaultSelectedLabel, value: props.value,
|
|
1566
|
+
onChange: props.onChange, config: props.config, options: props.options,
|
|
1567
|
+
description: props.description, controllerId: props.controllerId,
|
|
1568
|
+
openDropdowns: props.openDropdowns, toggleDropdown: props.toggleDropdown,
|
|
1569
|
+
onFocus: props.onFocus } };
|
|
1570
|
+
case DatePickerInputController: return { kind: 'datepicker', props: {
|
|
1571
|
+
label: props.label, value: props.value, onChange: props.onChange,
|
|
1572
|
+
description: props.description, controllerId: props.controllerId,
|
|
1573
|
+
openDropdowns: props.openDropdowns, toggleDropdown: props.toggleDropdown,
|
|
1574
|
+
onFocus: props.onFocus } };
|
|
1575
|
+
case DateFieldInputController: return { kind: 'datefield', props: {
|
|
1576
|
+
label: props.label, value: props.value, onChange: props.onChange, config: props.config,
|
|
1577
|
+
helpText: props.helpText, onFocus: props.onFocus } };
|
|
1578
|
+
case RangeCalendarInputController: return { kind: 'rangecalendar', props: {
|
|
1579
|
+
label: props.label, value: props.value, onChange: props.onChange,
|
|
1580
|
+
helpText: props.helpText, controllerId: props.controllerId,
|
|
1581
|
+
openDropdowns: props.openDropdowns, toggleDropdown: props.toggleDropdown,
|
|
1582
|
+
onFocus: props.onFocus } };
|
|
1583
|
+
case SwitchInputController: return { kind: 'switch', props: {
|
|
1584
|
+
label: props.label, value: props.value, onChange: props.onChange,
|
|
1585
|
+
description: props.description, onFocus: props.onFocus } };
|
|
1586
|
+
case ColorPickerInputController: return { kind: 'colorpicker', props: {
|
|
1587
|
+
label: props.label, value: props.value, onChange: props.onChange,
|
|
1588
|
+
controllerId: props.controllerId, id: props.id, config: props.config,
|
|
1589
|
+
helpText: props.helpText, saveRecentColors: props.saveRecentColors,
|
|
1590
|
+
showOpacityControls: props.showOpacityControls, autofocus: props.autofocus,
|
|
1591
|
+
portalTarget: props.portalTarget, pickerSize: props.pickerSize,
|
|
1592
|
+
sliderWidth: props.sliderWidth, opacity: props.opacity, showRing: props.showRing,
|
|
1593
|
+
onFocus: props.onFocus, onOpacityChange: props.onOpacityChange } };
|
|
1594
|
+
case CheckboxInputController: return { kind: 'checkbox', props: {
|
|
1595
|
+
value: props.value, onChange: props.onChange, config: props.config,
|
|
1596
|
+
description: props.description, onFocus: props.onFocus, options: props.options } };
|
|
1597
|
+
case RadioGroupInputController: return { kind: 'radiogroup', props: {
|
|
1598
|
+
value: props.value, onChange: props.onChange, config: props.config,
|
|
1599
|
+
description: props.description, onFocus: props.onFocus, options: props.options } };
|
|
1600
|
+
case FontInputController: return { kind: 'font', props: {
|
|
1601
|
+
label: props.label, style: props.style, value: props.value, onChange: props.onChange,
|
|
1602
|
+
description: props.description, controllerId: props.controllerId,
|
|
1603
|
+
openDropdowns: props.openDropdowns, toggleDropdown: props.toggleDropdown,
|
|
1604
|
+
hideRecentFromList: props.hideRecentFromList, hideTrashIcon: props.hideTrashIcon } };
|
|
1605
|
+
case PositionInputController: return { kind: 'position', props: {
|
|
1606
|
+
value: props.value, onChange: props.onChange, onFocus: props.onFocus,
|
|
1607
|
+
maskThumb: props.maskThumb, onDragStateChange: props.onDragStateChange } };
|
|
1608
|
+
case BrowserInputController: return { kind: 'browser', props: {
|
|
1609
|
+
header: props.header, label: props.label, value: props.value,
|
|
1610
|
+
onChange: props.onChange, portalTarget: props.portalTarget, config: props.config } };
|
|
1611
|
+
case FixedPositionInputController: return { kind: 'fixedposition', props: {
|
|
1612
|
+
value: props.value, onChange: props.onChange, onFocus: props.onFocus, id: props.id } };
|
|
1613
|
+
case VectorInputController: return { kind: 'vector', props: {
|
|
1614
|
+
label: props.label, value: props.value, onChange: props.onChange,
|
|
1615
|
+
description: props.description, controllerId: props.controllerId,
|
|
1616
|
+
openDropdowns: props.openDropdowns, toggleDropdown: props.toggleDropdown,
|
|
1617
|
+
onFocus: props.onFocus, hideRecentFromList: props.hideRecentFromList,
|
|
1618
|
+
hideTrashIcon: props.hideTrashIcon } };
|
|
1619
|
+
case AngleInputController: return { kind: 'angle', props: {
|
|
1620
|
+
label: props.label, value: props.value, onChange: props.onChange, config: props.config,
|
|
1621
|
+
helpText: props.helpText, onFocus: props.onFocus } };
|
|
1622
|
+
case TimeInputController: return { kind: 'time', props: {
|
|
1623
|
+
label: props.label, value: props.value, onChange: props.onChange,
|
|
1624
|
+
helpText: props.helpText, onFocus: props.onFocus } };
|
|
1625
|
+
case RoundedCornersInputController: return { kind: 'roundedcorners', props: {
|
|
1626
|
+
value: props.value, onChange: props.onChange, helpText: props.helpText,
|
|
1627
|
+
onFocus: props.onFocus } };
|
|
1628
|
+
case TagsInputController: return { kind: 'tags', props: {
|
|
1629
|
+
label: props.label, value: props.value, onChange: props.onChange, config: props.config,
|
|
1630
|
+
helpText: props.helpText, onFocus: props.onFocus } };
|
|
1631
|
+
default: return null;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1478
1635
|
// Create a store for managing open dropdowns (only one should be open at a time)
|
|
1479
1636
|
const openDropdowns = writable<Set<string>>(new Set());
|
|
1480
1637
|
|
|
@@ -1581,37 +1738,16 @@
|
|
|
1581
1738
|
}
|
|
1582
1739
|
|
|
1583
1740
|
/**
|
|
1584
|
-
*
|
|
1741
|
+
* SCHEMA SHAPES — aliases of the package's canonical schema views
|
|
1742
|
+
* (`$lib/schemaViews`, exported at the package root). PropsEditor used to
|
|
1743
|
+
* declare stricter local duplicates, which made every array flowing from
|
|
1744
|
+
* the open view props unassignable to them; the canonical names are the
|
|
1745
|
+
* same types now, so the contract holds across the package boundary.
|
|
1585
1746
|
*/
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
defaultValue?: any;
|
|
1591
|
-
value?: any; // Add value property to interface
|
|
1592
|
-
constraints?: Record<string, any>;
|
|
1593
|
-
inputController: {
|
|
1594
|
-
type: string;
|
|
1595
|
-
description?: string;
|
|
1596
|
-
helpText?: string;
|
|
1597
|
-
config?: Record<string, any>;
|
|
1598
|
-
};
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
interface ContentItem {
|
|
1602
|
-
id: string;
|
|
1603
|
-
properties: Array<Property>;
|
|
1604
|
-
orderNumber?: number;
|
|
1605
|
-
title?: string;
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
interface ContentSchema {
|
|
1609
|
-
genericContent: {
|
|
1610
|
-
title: string;
|
|
1611
|
-
properties: Array<Property>;
|
|
1612
|
-
};
|
|
1613
|
-
contentItems: ContentItem[];
|
|
1614
|
-
}
|
|
1747
|
+
type Property = DesignSchemaPropertyView;
|
|
1748
|
+
type PropertyGroup = DesignSchemaPropertyGroupView;
|
|
1749
|
+
type ContentItem = ContentSchemaItemView;
|
|
1750
|
+
type ContentSchema = ContentSchemaView;
|
|
1615
1751
|
|
|
1616
1752
|
onMount(() => {
|
|
1617
1753
|
const resizeObserver = new ResizeObserver(() => {
|
|
@@ -2034,7 +2170,13 @@
|
|
|
2034
2170
|
const key = p.id || p.name;
|
|
2035
2171
|
// Canonical state first; schema mirror/defaultValue are legacy
|
|
2036
2172
|
// seeds for keys the editor never touched.
|
|
2037
|
-
const liveOnItem =
|
|
2173
|
+
const liveOnItem =
|
|
2174
|
+
key != null
|
|
2175
|
+
? // Items legitimately carry canonical per-key state alongside
|
|
2176
|
+
// `properties`; route through unknown to the index signature
|
|
2177
|
+
// honestly.
|
|
2178
|
+
(item as unknown as Record<string, unknown>)[key]
|
|
2179
|
+
: undefined;
|
|
2038
2180
|
props[key] = normalizeContentPropertyValue(
|
|
2039
2181
|
pickLiveContentValue(
|
|
2040
2182
|
contentState[`${item.id}__${key}`],
|
|
@@ -2252,28 +2394,43 @@
|
|
|
2252
2394
|
let dragState = { active: false, startX: 0, startY: 0, initialLeft: 0, initialTop: 0 };
|
|
2253
2395
|
|
|
2254
2396
|
function handleDragStart(e: MouseEvent) {
|
|
2255
|
-
|
|
2397
|
+
const target = positionTarget || rootElement;
|
|
2398
|
+
if (!enableDrag || !target || e.button !== 0) return;
|
|
2256
2399
|
e.preventDefault();
|
|
2257
|
-
|
|
2400
|
+
e.stopPropagation();
|
|
2401
|
+
|
|
2402
|
+
// Wrapped editors keep the measured size, including the parent's height clamp.
|
|
2403
|
+
// Resolve percentage/centered placement before leaving flow, never parse it as pixels.
|
|
2404
|
+
if (positionTarget) {
|
|
2405
|
+
const rectangle = target.getBoundingClientRect();
|
|
2406
|
+
const parent = target.offsetParent;
|
|
2407
|
+
const parentRectangle = parent?.getBoundingClientRect() || { left: 0, top: 0 };
|
|
2408
|
+
const left = rectangle.left - parentRectangle.left - (parent?.clientLeft || 0) + (parent?.scrollLeft || 0);
|
|
2409
|
+
const top = rectangle.top - parentRectangle.top - (parent?.clientTop || 0) + (parent?.scrollTop || 0);
|
|
2410
|
+
Object.assign(target.style, {
|
|
2411
|
+
position: 'absolute', margin: '0', transform: 'none',
|
|
2412
|
+
boxSizing: 'border-box', width: `${rectangle.width}px`, height: `${rectangle.height}px`,
|
|
2413
|
+
left: `${left}px`, top: `${top}px`
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2258
2416
|
dragState = {
|
|
2259
2417
|
active: true,
|
|
2260
2418
|
startX: e.clientX,
|
|
2261
2419
|
startY: e.clientY,
|
|
2262
|
-
initialLeft: parseFloat(
|
|
2263
|
-
initialTop: parseFloat(
|
|
2420
|
+
initialLeft: parseFloat(target.style.left) || 0,
|
|
2421
|
+
initialTop: parseFloat(target.style.top) || 0
|
|
2264
2422
|
};
|
|
2265
|
-
|
|
2423
|
+
target.style.zIndex = '50';
|
|
2266
2424
|
document.addEventListener('mousemove', handleDragMove);
|
|
2267
2425
|
document.addEventListener('mouseup', handleDragEnd);
|
|
2268
2426
|
document.body.style.cursor = 'move';
|
|
2269
2427
|
}
|
|
2270
2428
|
|
|
2271
2429
|
function handleDragMove(e: MouseEvent) {
|
|
2272
|
-
if (!dragState.active
|
|
2430
|
+
if (!dragState.active) return;
|
|
2273
2431
|
const left = dragState.initialLeft + (e.clientX - dragState.startX);
|
|
2274
2432
|
const top = dragState.initialTop + (e.clientY - dragState.startY);
|
|
2275
|
-
|
|
2276
|
-
rootElement.style.top = `${top}px`;
|
|
2433
|
+
setPosition(left, top);
|
|
2277
2434
|
dispatch('move', { left, top });
|
|
2278
2435
|
}
|
|
2279
2436
|
|
|
@@ -2283,10 +2440,7 @@
|
|
|
2283
2440
|
document.removeEventListener('mousemove', handleDragMove);
|
|
2284
2441
|
document.removeEventListener('mouseup', handleDragEnd);
|
|
2285
2442
|
document.body.style.cursor = '';
|
|
2286
|
-
dispatch('moveEnd',
|
|
2287
|
-
left: parseFloat(rootElement.style.left) || 0,
|
|
2288
|
-
top: parseFloat(rootElement.style.top) || 0
|
|
2289
|
-
});
|
|
2443
|
+
dispatch('moveEnd', getPosition());
|
|
2290
2444
|
}
|
|
2291
2445
|
|
|
2292
2446
|
/** Public: update a design state value externally (used by EmbedEditor for dimension sync) */
|
|
@@ -2296,17 +2450,19 @@
|
|
|
2296
2450
|
|
|
2297
2451
|
/** Public: set absolute position programmatically (used by parent for layout) */
|
|
2298
2452
|
export function setPosition(left: number, top: number) {
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2453
|
+
const target = positionTarget || rootElement;
|
|
2454
|
+
if (!target) return;
|
|
2455
|
+
target.style.left = `${left}px`;
|
|
2456
|
+
target.style.top = `${top}px`;
|
|
2302
2457
|
}
|
|
2303
2458
|
|
|
2304
2459
|
/** Public: get current position */
|
|
2305
2460
|
export function getPosition() {
|
|
2306
|
-
|
|
2461
|
+
const target = positionTarget || rootElement;
|
|
2462
|
+
if (!target) return { left: 0, top: 0 };
|
|
2307
2463
|
return {
|
|
2308
|
-
left: parseFloat(
|
|
2309
|
-
top: parseFloat(
|
|
2464
|
+
left: parseFloat(target.style.left) || 0,
|
|
2465
|
+
top: parseFloat(target.style.top) || 0
|
|
2310
2466
|
};
|
|
2311
2467
|
}
|
|
2312
2468
|
|
|
@@ -2353,47 +2509,64 @@
|
|
|
2353
2509
|
onValueChange={handleTabChange}
|
|
2354
2510
|
>
|
|
2355
2511
|
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
<div class="text-error text-lg font-medium">No schema configuration available</div>
|
|
2359
|
-
<div class="text-grey5 subtext mt-1">Error code: CONFIG_NOT_FOUND</div>
|
|
2360
|
-
</div>
|
|
2361
|
-
{:else}
|
|
2362
|
-
<!-- Drag Handle - Show outside header when no header exists -->
|
|
2363
|
-
{#if showDragHandle && (!designSchema || !contentSchema)}
|
|
2512
|
+
<!-- A single toolbar owns drag and Save for all editor variants. -->
|
|
2513
|
+
{#if showDragHandle && (designSchema || contentSchema || headerContent)}
|
|
2364
2514
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
2365
|
-
<div class="
|
|
2515
|
+
<div class="props-editor-drag-bar top-0 left-0 w-full flex h-7 shrink-0 items-center justify-center z-999 px-3 {headerContent ? 'relative ' + headerClass : 'absolute'}">
|
|
2366
2516
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
2367
2517
|
<div
|
|
2368
|
-
class="flex items-center justify-center h-
|
|
2518
|
+
class="flex items-center justify-center h-7 cursor-move transition-all duration-200 widgetic-colors opacity-0 hover:opacity-100 drag-handle-container"
|
|
2369
2519
|
onmousedown={handleDragStart}
|
|
2370
2520
|
>
|
|
2371
|
-
<DragHandle class="w-
|
|
2521
|
+
<DragHandle class="w-16.5 h-6 {dragHandleClass} hover:text-grey3 transition-colors" />
|
|
2372
2522
|
</div>
|
|
2523
|
+
{#if showSaveButton}
|
|
2524
|
+
{#if title}<span class="props-editor-title-label absolute left-6 top-1.75 pointer-events-none select-none text-[13px] font-bold leading-none text-[hsl(var(--grey5))]">{title}</span>{/if}
|
|
2525
|
+
<button
|
|
2526
|
+
type="button"
|
|
2527
|
+
class="props-editor-save-btn absolute right-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
|
+
{saveSaving
|
|
2529
|
+
? 'text-[hsl(var(--deep-forest-green))] bg-[hsl(var(--light-green))]'
|
|
2530
|
+
: saveDirty
|
|
2531
|
+
? 'text-white bg-[hsl(var(--deep-forest-green))] hover:bg-[hsl(var(--dark))] hover:text-white'
|
|
2532
|
+
: 'text-[hsl(var(--grey5))] bg-grey2 inset-ring inset-ring-grey3/20 hover:inset-ring-deep-forest-green hover:bg-[hsl(var(--deep-forest-green))] hover:text-white'}"
|
|
2533
|
+
disabled={saveSaving}
|
|
2534
|
+
onclick={(e) => {
|
|
2535
|
+
e.stopPropagation();
|
|
2536
|
+
if (!saveSaving && saveDirty) dispatch('save');
|
|
2537
|
+
}}
|
|
2538
|
+
onmousedown={(e) => e.stopPropagation()}
|
|
2539
|
+
>
|
|
2540
|
+
<svg class="props-editor-save-icon absolute left-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
|
+
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
|
|
2542
|
+
<polyline points="17 21 17 13 7 13 7 21" />
|
|
2543
|
+
<polyline points="7 3 7 8 15 8" />
|
|
2544
|
+
</svg>
|
|
2545
|
+
<span class="props-editor-save-label">{saveSaving ? 'Saving' : 'Save'}</span>
|
|
2546
|
+
</button>
|
|
2547
|
+
{/if}
|
|
2373
2548
|
</div>
|
|
2374
2549
|
{/if}
|
|
2375
2550
|
|
|
2551
|
+
{#if headerContent}
|
|
2552
|
+
<div class="props-editor-header-content shrink-0 {headerClass}">{@render headerContent()}</div>
|
|
2553
|
+
{/if}
|
|
2554
|
+
|
|
2555
|
+
{#if !designSchema && !contentSchema}
|
|
2556
|
+
{#if showSchemaError}
|
|
2557
|
+
<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">No schema configuration available</div>
|
|
2559
|
+
<div class="props-editor-error-code text-grey5 subtext mt-1">Error code: CONFIG_NOT_FOUND</div>
|
|
2560
|
+
</div>
|
|
2561
|
+
{/if}
|
|
2562
|
+
{:else}
|
|
2376
2563
|
<!-- Tabs header with buttons and separator - only show if both schemas exist -->
|
|
2377
2564
|
{#if designSchema && contentSchema}
|
|
2378
2565
|
<div
|
|
2379
|
-
class="tabs-header h-
|
|
2566
|
+
class="tabs-header h-28 shrink-0 flex items-start pt-10.5 px-6 pb-3.5 rounded-t-large header-hover-area relative"
|
|
2380
2567
|
>
|
|
2381
|
-
<!-- Drag Handle - Show inside header when header exists -->
|
|
2382
|
-
{#if showDragHandle}
|
|
2383
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
2384
|
-
<div class="absolute top-0 left-0 w-full flex justify-center z-[999]">
|
|
2385
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
2386
|
-
<div
|
|
2387
|
-
class="flex items-center justify-center h-6 cursor-move transition-all duration-200 widgetic-colors opacity-0 hover:opacity-100 drag-handle-container"
|
|
2388
|
-
onmousedown={handleDragStart}
|
|
2389
|
-
>
|
|
2390
|
-
<DragHandle class="w-[66px] h-[24px] text-grey2 hover:text-grey3 transition-colors" />
|
|
2391
|
-
</div>
|
|
2392
|
-
</div>
|
|
2393
|
-
{/if}
|
|
2394
|
-
|
|
2395
2568
|
<Tabs.List
|
|
2396
|
-
class="tabs-list w-full h-
|
|
2569
|
+
class="tabs-list w-full h-14 rounded-large bg-grey2 p-1"
|
|
2397
2570
|
>
|
|
2398
2571
|
<Tabs.Trigger
|
|
2399
2572
|
value="design"
|
|
@@ -2421,7 +2594,7 @@
|
|
|
2421
2594
|
{/if}
|
|
2422
2595
|
|
|
2423
2596
|
<!-- Design Tab Content -->
|
|
2424
|
-
<div class="tab-design-container
|
|
2597
|
+
<div class="tab-design-container grow scroll-area widgetic-colors hidden {headerContent ? 'min-h-0 flex-1 overflow-hidden' : 'overflow-y-auto'}"
|
|
2425
2598
|
class:hidden={currentTab !== 'design'}
|
|
2426
2599
|
style="max-height: {!designSchema || !contentSchema ? '100%' : `calc(100% - ${HEADER_HEIGHT}px)`}"
|
|
2427
2600
|
data-scrollable={$designScrollState.shouldShowScroll}
|
|
@@ -2430,13 +2603,13 @@
|
|
|
2430
2603
|
class="h-full"
|
|
2431
2604
|
type="hover"
|
|
2432
2605
|
scrollHideDelay={0}
|
|
2433
|
-
scrollbarYClasses="flex touch-none select-none transition-colors w-
|
|
2606
|
+
scrollbarYClasses="flex touch-none select-none transition-colors w-6 px-2 {headerContent ? 'bg-transparent' : 'bg-white'} {!designSchema || !contentSchema ? 'pt-7' : 'pt-4'} pb-6 [&>div]:w-2 [&>div]:bg-grey1 [&>div]:rounded-pill [&>div]:cursor-pointer [&>div]:transition-colors [&>div]:duration-250 [&>div]:hover:bg-grey2"
|
|
2434
2607
|
>
|
|
2435
2608
|
<div class="scroll-viewport" data-radix-scroll-area-viewport bind:this={designScrollViewport}>
|
|
2436
|
-
<div class="px-6 {!designSchema || !contentSchema ? '
|
|
2609
|
+
<div class="px-6 {!designSchema || !contentSchema ? 'pt-7 pb-6' : 'pt-4 pb-6'}" bind:this={designContentWrapper}>
|
|
2437
2610
|
<Tabs.Content
|
|
2438
2611
|
value="design"
|
|
2439
|
-
class="design-tab-content
|
|
2612
|
+
class="design-tab-content mt-0! tabs-content-container w-full outline-none {isFirstLoad ? 'initial-load' : !isInitialTabLoad ? 'animate-slide-left' : ''}"
|
|
2440
2613
|
data-tab="design"
|
|
2441
2614
|
style="animation-duration: 300ms; transform: none;"
|
|
2442
2615
|
>
|
|
@@ -2495,12 +2668,12 @@
|
|
|
2495
2668
|
</div>
|
|
2496
2669
|
{/if}
|
|
2497
2670
|
|
|
2498
|
-
<div class="ic-input-ct {isFontIc ? 'w-full' : '
|
|
2499
|
-
<
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2671
|
+
<div class="ic-input-ct {isFontIc ? 'w-full' : 'shrink-0 ml-auto'}" style={isFontIc ? '' : 'width: 185px;'}>
|
|
2672
|
+
<InputControllerRenderer
|
|
2673
|
+
controller={describeInputController(InputComponent, {
|
|
2674
|
+
...props,
|
|
2675
|
+
value: reactiveValue !== undefined ? reactiveValue : props.value
|
|
2676
|
+
})}
|
|
2504
2677
|
/>
|
|
2505
2678
|
</div>
|
|
2506
2679
|
</div>
|
|
@@ -2533,7 +2706,7 @@
|
|
|
2533
2706
|
|
|
2534
2707
|
<!-- Content Tab Content -->
|
|
2535
2708
|
{#if contentSchema}
|
|
2536
|
-
<div class="tab-content-container
|
|
2709
|
+
<div class="tab-content-container grow overflow-y-auto scroll-area widgetic-colors hidden"
|
|
2537
2710
|
class:hidden={currentTab !== 'content'}
|
|
2538
2711
|
style="max-height: {!contentSchema || !designSchema ? '100%' : `calc(100% - ${HEADER_HEIGHT}px)`}"
|
|
2539
2712
|
data-scrollable={$contentScrollState.shouldShowScroll}
|
|
@@ -2542,13 +2715,13 @@
|
|
|
2542
2715
|
class="h-full"
|
|
2543
2716
|
type="hover"
|
|
2544
2717
|
scrollHideDelay={0}
|
|
2545
|
-
scrollbarYClasses="flex touch-none select-none transition-colors w-
|
|
2718
|
+
scrollbarYClasses="flex touch-none select-none transition-colors w-6 px-2 {headerContent ? 'bg-transparent' : 'bg-white'} {!designSchema || !contentSchema ? 'pt-7' : 'pt-4'} pb-6 [&>div]:w-2 [&>div]:bg-grey1 [&>div]:rounded-pill [&>div]:cursor-pointer [&>div]:transition-colors [&>div]:duration-250 [&>div]:hover:bg-grey2"
|
|
2546
2719
|
>
|
|
2547
2720
|
<div class="scroll-viewport" data-radix-scroll-area-viewport bind:this={contentScrollViewport}>
|
|
2548
|
-
<div class="px-6 {!designSchema || !contentSchema ? 'pt-
|
|
2721
|
+
<div class="px-6 {!designSchema || !contentSchema ? 'pt-7 pb-6' : 'pt-4 pb-6'}" bind:this={contentWrapper}>
|
|
2549
2722
|
<Tabs.Content
|
|
2550
2723
|
value="content"
|
|
2551
|
-
class="content-tab-content
|
|
2724
|
+
class="content-tab-content mt-0! tabs-content-container w-full {isFirstLoad ? 'initial-load' : !isInitialTabLoad ? 'animate-slide-right' : ''}"
|
|
2552
2725
|
data-tab="content"
|
|
2553
2726
|
style="transform: none; position: relative; z-index: 1;"
|
|
2554
2727
|
>
|
|
@@ -2561,7 +2734,7 @@
|
|
|
2561
2734
|
onOpenChange={() => isGenericContentOpen = !isGenericContentOpen}
|
|
2562
2735
|
>
|
|
2563
2736
|
<Collapsible.Trigger class="generic-content-trigger w-full">
|
|
2564
|
-
<div class="flex items-center h-
|
|
2737
|
+
<div class="flex items-center h-13" style="padding-left: {CONTENT_INNER_PADDING}px; padding-right: {CONTENT_INNER_PADDING}px;">
|
|
2565
2738
|
<h3 class="body-medium text-grey7 flex-1 content-title">{contentSchema?.genericContent?.title || 'Content'}</h3>
|
|
2566
2739
|
<ChevronDown class="h-6 w-6 text-grey5 transition-transform chevron" />
|
|
2567
2740
|
</div>
|
|
@@ -2574,7 +2747,7 @@
|
|
|
2574
2747
|
<Collapsible.Content>
|
|
2575
2748
|
{#if isGenericContentOpen}
|
|
2576
2749
|
<Card.Content class="p-0 px-4">
|
|
2577
|
-
<div class="pt-
|
|
2750
|
+
<div class="pt-3.5 pb-4">
|
|
2578
2751
|
<div class="properties-list flex flex-col" style="gap: 8px;">
|
|
2579
2752
|
{#each contentSchema?.genericContent?.properties || [] as property, propertyIndex}
|
|
2580
2753
|
{@const result = renderInputController(
|
|
@@ -2607,12 +2780,9 @@
|
|
|
2607
2780
|
</div>
|
|
2608
2781
|
{/if}
|
|
2609
2782
|
|
|
2610
|
-
<div class="ic-input-ct {isFontIc ? 'w-full' : '
|
|
2611
|
-
<
|
|
2612
|
-
|
|
2613
|
-
{...props}
|
|
2614
|
-
{header}
|
|
2615
|
-
shouldFocus={activeControllerId === props.id}
|
|
2783
|
+
<div class="ic-input-ct {isFontIc ? 'w-full' : 'shrink-0'}" style={isFontIc ? '' : 'width: 185px;'}>
|
|
2784
|
+
<InputControllerRenderer
|
|
2785
|
+
controller={describeInputController(InputComponent, { ...props, header })}
|
|
2616
2786
|
/>
|
|
2617
2787
|
</div>
|
|
2618
2788
|
</div>
|
|
@@ -2650,8 +2820,8 @@
|
|
|
2650
2820
|
onOpenChange={() => toggleItem(item.id)}
|
|
2651
2821
|
>
|
|
2652
2822
|
<Collapsible.Trigger class="content-item-trigger w-full">
|
|
2653
|
-
<div class="flex items-center h-
|
|
2654
|
-
<div class="drag-handle flex items-center justify-center w-
|
|
2823
|
+
<div class="flex items-center h-13 cursor-pointer widgetic-colors">
|
|
2824
|
+
<div class="drag-handle flex items-center justify-center w-14 cursor-move hover:text-grey5">
|
|
2655
2825
|
<IconGripVertical class="w-6 h-6 text-grey2 transition-colors duration-200" />
|
|
2656
2826
|
</div>
|
|
2657
2827
|
|
|
@@ -2674,7 +2844,7 @@
|
|
|
2674
2844
|
<Collapsible.Content>
|
|
2675
2845
|
{#if isItemOpen(item.id)}
|
|
2676
2846
|
<Card.Content class="p-0 px-4">
|
|
2677
|
-
<div class="pt-
|
|
2847
|
+
<div class="pt-3.5 pb-4">
|
|
2678
2848
|
<div class="properties-list flex flex-col" style="gap: 8px;">
|
|
2679
2849
|
{#each item.properties as property, propertyIndex}
|
|
2680
2850
|
{@const result = renderInputController(
|
|
@@ -2708,12 +2878,9 @@
|
|
|
2708
2878
|
</div>
|
|
2709
2879
|
{/if}
|
|
2710
2880
|
|
|
2711
|
-
<div class="ic-input-ct {isFontIc ? 'w-full' : '
|
|
2712
|
-
<
|
|
2713
|
-
|
|
2714
|
-
{...props}
|
|
2715
|
-
{header}
|
|
2716
|
-
shouldFocus={activeControllerId === props.id}
|
|
2881
|
+
<div class="ic-input-ct {isFontIc ? 'w-full' : 'shrink-0'}" style={isFontIc ? '' : 'width: 185px;'}>
|
|
2882
|
+
<InputControllerRenderer
|
|
2883
|
+
controller={describeInputController(InputComponent, { ...props, header })}
|
|
2717
2884
|
/>
|
|
2718
2885
|
</div>
|
|
2719
2886
|
</div>
|
|
@@ -2722,11 +2889,11 @@
|
|
|
2722
2889
|
</div>
|
|
2723
2890
|
|
|
2724
2891
|
<!-- Delete Item Button -->
|
|
2725
|
-
<div class="mt-
|
|
2892
|
+
<div class="mt-4 flex flex-col gap-2 relative z-10">
|
|
2726
2893
|
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
|
|
2727
2894
|
<button
|
|
2728
2895
|
type="button"
|
|
2729
|
-
class="widgetic-colors h-
|
|
2896
|
+
class="widgetic-colors h-8.5 w-full flex items-center justify-center relative z-10 group"
|
|
2730
2897
|
style="
|
|
2731
2898
|
background-color: hsl(var(--error)) !important;
|
|
2732
2899
|
border-radius: var(--radius-small) !important;
|
|
@@ -2764,7 +2931,7 @@
|
|
|
2764
2931
|
<div class="content-add-item-ct mt-4">
|
|
2765
2932
|
<Button
|
|
2766
2933
|
variant="ghost"
|
|
2767
|
-
class="group w-full h-
|
|
2934
|
+
class="group w-full h-13 bg-grey1 hover:bg-grey2 active:scale-[0.98] border-2 border-grey2 rounded-medium"
|
|
2768
2935
|
style="transition: background-color 0.2s ease, transform 0.15s ease !important;"
|
|
2769
2936
|
onclick={handleAddNewItem}
|
|
2770
2937
|
>
|
|
@@ -2788,7 +2955,7 @@
|
|
|
2788
2955
|
<div
|
|
2789
2956
|
bind:this={fileBrowserPortalHost}
|
|
2790
2957
|
id="props-editor-file-browser-portal"
|
|
2791
|
-
class="props-editor-file-browser-portal pointer-events-none absolute inset-0 z-
|
|
2958
|
+
class="props-editor-file-browser-portal pointer-events-none absolute inset-0 z-999998"
|
|
2792
2959
|
></div>
|
|
2793
2960
|
</Tabs.Root>
|
|
2794
2961
|
</div>
|
|
@@ -2806,7 +2973,9 @@
|
|
|
2806
2973
|
.props-editor-container:not(:has(.header-hover-area)) {
|
|
2807
2974
|
position: relative;
|
|
2808
2975
|
}
|
|
2809
|
-
.props-editor-container:not(:has(.header-hover-area)):hover :global(.drag-handle-container)
|
|
2976
|
+
.props-editor-container:not(:has(.header-hover-area)):hover :global(.drag-handle-container),
|
|
2977
|
+
.props-editor-container:has(.header-hover-area:hover) :global(.props-editor-drag-bar .drag-handle-container),
|
|
2978
|
+
.props-editor-container :global(.props-editor-drag-bar:hover .drag-handle-container) {
|
|
2810
2979
|
opacity: 1 !important;
|
|
2811
2980
|
}
|
|
2812
2981
|
:global(.drag-handle-container) {
|
|
@@ -2844,6 +3013,23 @@
|
|
|
2844
3013
|
.props-editor-container:has(.tabs-header) {
|
|
2845
3014
|
--has-both-schemas: 1rem;
|
|
2846
3015
|
}
|
|
3016
|
+
|
|
3017
|
+
/* ── Save button (composition drag bar) ── */
|
|
3018
|
+
/* Icon stays put next to the text; label is centered in the remaining space
|
|
3019
|
+
with a small left bias so "Save" and "Saving" never shift the icon. */
|
|
3020
|
+
.props-editor-save-label {
|
|
3021
|
+
padding-left: 14px;
|
|
3022
|
+
line-height: 1;
|
|
3023
|
+
}
|
|
3024
|
+
.props-editor-save-btn .props-editor-save-icon {
|
|
3025
|
+
color: currentColor !important;
|
|
3026
|
+
stroke: currentColor !important;
|
|
3027
|
+
}
|
|
3028
|
+
.props-editor-save-label,
|
|
3029
|
+
:global(.props-editor-save-label) {
|
|
3030
|
+
color: currentColor !important;
|
|
3031
|
+
line-height: 1;
|
|
3032
|
+
}
|
|
2847
3033
|
:global(.content-items-wrapper) {
|
|
2848
3034
|
padding-top: 1rem;
|
|
2849
3035
|
}
|