@widgetic/editor 4.0.7 → 4.0.9
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 +32 -1
- package/dist/components/WidgetPreview.svelte.d.ts +5 -0
- package/dist/components/editors/PropsEditor.svelte +171 -87
- package/dist/components/editors/PropsEditor.svelte.d.ts +3 -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 -19
- package/dist/components/ic/ColorPickerInputController.svelte.d.ts +42 -17
- 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 +39 -14
- 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 +70 -41
- package/dist/components/ic/FontInputController.svelte.d.ts +33 -18
- 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 +24 -10
- package/dist/components/ic/RoundedCornersInputController.svelte.d.ts +26 -16
- 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 +18 -12
- 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/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/schemaViews.d.ts +105 -0
- package/dist/schemaViews.js +11 -0
- package/dist/utils/FontManager.js +1 -1
- package/package.json +9 -6
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Based on the Widget's design schema and content schema it generates 2 panels of
|
|
|
9
9
|
|
|
10
10
|
1. add the component as a dependency in the npm package of the project
|
|
11
11
|
2. import the component in the project:
|
|
12
|
-
`import PropsEditor from '@widgetic/
|
|
12
|
+
`import PropsEditor from '@widgetic/editor';`
|
|
13
13
|
3. load the design and content schemas and pass them as props to the component:
|
|
14
14
|
```js
|
|
15
15
|
let designSchemaUrl = '/design-debug-schema.json';
|
|
@@ -71,6 +71,17 @@ npm i --legacy-peer-deps
|
|
|
71
71
|
Note: use --legacy-peer-deps if you get peer dependency errors
|
|
72
72
|
as our project uses svelte 5 and some of the dependencies are still using svelte 4.
|
|
73
73
|
|
|
74
|
+
### Sibling packages
|
|
75
|
+
|
|
76
|
+
This app compiles sibling `@widgetic/*` source in dev through the dev-alias
|
|
77
|
+
setup `@widgetic/dev-tools` owns. The alias tables, the `$lib` remap, and
|
|
78
|
+
everything else the mapping needs come from the installed
|
|
79
|
+
`@widgetic/dev-tools` dependency. An ordinary `npm install` is the whole
|
|
80
|
+
setup — there is no linking step.
|
|
81
|
+
|
|
82
|
+
The why and the silent-degrade failure mode live in
|
|
83
|
+
[the dev-tools README](../dev-tools/README.md).
|
|
84
|
+
|
|
74
85
|
## Start the development server
|
|
75
86
|
|
|
76
87
|
```bash
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
resize: { width: number; height: number };
|
|
11
11
|
mobileViewport: { width: number; height: number };
|
|
12
12
|
desktopViewport: void;
|
|
13
|
+
deviceView: { mobile: boolean };
|
|
13
14
|
fitToArea: void;
|
|
14
15
|
iframeLoad: Event;
|
|
15
16
|
iframeError: Event;
|
|
@@ -64,6 +65,9 @@
|
|
|
64
65
|
export let enableDrag: boolean = false;
|
|
65
66
|
/** W×H number inputs in the chrome header, live-synced with resize. */
|
|
66
67
|
export let showDimensionInputs: boolean = false;
|
|
68
|
+
/** Desktop / mobile pair in the chrome header, before the dimension inputs. */
|
|
69
|
+
export let showChromeDeviceToggle: boolean = false;
|
|
70
|
+
export let deviceMobile: boolean = false;
|
|
67
71
|
/**
|
|
68
72
|
* concentric — always resize from center (widget-creator / Canvas).
|
|
69
73
|
* corner — opposite corner fixed; hold Alt for concentric (composition page).
|
|
@@ -886,8 +890,35 @@
|
|
|
886
890
|
<span class="preview-chrome-drag-grip shrink-0 text-gray-400 text-xs leading-none" title="Drag preview" aria-hidden="true">⠿</span>
|
|
887
891
|
{/if}
|
|
888
892
|
<span class="preview-chrome-title text-[11px] font-semibold uppercase tracking-wide text-gray-500 shrink-0">Preview</span>
|
|
893
|
+
{#if showChromeDeviceToggle}
|
|
894
|
+
<div
|
|
895
|
+
class="preview-chrome-device-toggle ml-auto flex shrink-0 items-center overflow-hidden rounded border border-gray-300"
|
|
896
|
+
onpointerdown={(event) => event.stopPropagation()}
|
|
897
|
+
>
|
|
898
|
+
<button
|
|
899
|
+
type="button"
|
|
900
|
+
class="preview-chrome-device-desktop flex h-6 w-6 items-center justify-center {!deviceMobile ? 'bg-gray-100 text-gray-800' : 'bg-white text-gray-400'} hover:text-gray-800 focus:outline-none focus-visible:outline-none"
|
|
901
|
+
aria-label="Desktop preview"
|
|
902
|
+
aria-pressed={!deviceMobile}
|
|
903
|
+
onclick={() => dispatch('deviceView', { mobile: false })}
|
|
904
|
+
onpointerdown={(event) => event.stopPropagation()}
|
|
905
|
+
>
|
|
906
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
|
|
907
|
+
</button>
|
|
908
|
+
<button
|
|
909
|
+
type="button"
|
|
910
|
+
class="preview-chrome-device-mobile flex h-6 w-6 items-center justify-center border-l border-gray-300 {deviceMobile ? 'bg-gray-100 text-gray-800' : 'bg-white text-gray-400'} hover:text-gray-800 focus:outline-none focus-visible:outline-none"
|
|
911
|
+
aria-label="Mobile preview"
|
|
912
|
+
aria-pressed={deviceMobile}
|
|
913
|
+
onclick={() => dispatch('deviceView', { mobile: true })}
|
|
914
|
+
onpointerdown={(event) => event.stopPropagation()}
|
|
915
|
+
>
|
|
916
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5" y="2" width="14" height="20" rx="2" ry="2"/><line x1="12" y1="18" x2="12.01" y2="18"/></svg>
|
|
917
|
+
</button>
|
|
918
|
+
</div>
|
|
919
|
+
{/if}
|
|
889
920
|
{#if showDimensionInputs}
|
|
890
|
-
<div class="preview-chrome-dimensions
|
|
921
|
+
<div class="preview-chrome-dimensions flex items-center gap-1.5 {showChromeDeviceToggle ? '' : 'ml-auto'}">
|
|
891
922
|
<label class="preview-dim-label flex items-center gap-1 text-[10px] font-semibold text-gray-500">
|
|
892
923
|
<span>W</span>
|
|
893
924
|
<input
|
|
@@ -45,6 +45,8 @@ declare const WidgetPreview: $$__sveltets_2_IsomorphicComponent<{
|
|
|
45
45
|
enableResize?: boolean;
|
|
46
46
|
/** Drag only via the preview chrome header (keeps iframe interactive). */ enableDrag?: boolean;
|
|
47
47
|
/** W×H number inputs in the chrome header, live-synced with resize. */ showDimensionInputs?: boolean;
|
|
48
|
+
/** Desktop / mobile pair in the chrome header, before the dimension inputs. */ showChromeDeviceToggle?: boolean;
|
|
49
|
+
deviceMobile?: boolean;
|
|
48
50
|
/**
|
|
49
51
|
* concentric — always resize from center (widget-creator / Canvas).
|
|
50
52
|
* corner — opposite corner fixed; hold Alt for concentric (composition page).
|
|
@@ -86,6 +88,9 @@ declare const WidgetPreview: $$__sveltets_2_IsomorphicComponent<{
|
|
|
86
88
|
height: number;
|
|
87
89
|
}>;
|
|
88
90
|
desktopViewport: CustomEvent<void>;
|
|
91
|
+
deviceView: CustomEvent<{
|
|
92
|
+
mobile: boolean;
|
|
93
|
+
}>;
|
|
89
94
|
fitToArea: CustomEvent<void>;
|
|
90
95
|
iframeLoad: CustomEvent<Event>;
|
|
91
96
|
iframeError: CustomEvent<Event>;
|
|
@@ -15,7 +15,16 @@
|
|
|
15
15
|
// Svelte imports
|
|
16
16
|
import { onMount, onDestroy, createEventDispatcher, afterUpdate } from 'svelte';
|
|
17
17
|
const dispatch = createEventDispatcher();
|
|
18
|
-
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';
|
|
19
28
|
|
|
20
29
|
/**
|
|
21
30
|
* COMPONENTS IMPORTS
|
|
@@ -61,6 +70,7 @@
|
|
|
61
70
|
TagsInputController,
|
|
62
71
|
DateFieldInputController
|
|
63
72
|
} from '../ic';
|
|
73
|
+
import type { InputControllerConfig } from '../ic';
|
|
64
74
|
|
|
65
75
|
/**
|
|
66
76
|
* UTILS IMPORTS
|
|
@@ -84,15 +94,6 @@
|
|
|
84
94
|
*/
|
|
85
95
|
type InputValue = string | number | boolean | any | null;
|
|
86
96
|
|
|
87
|
-
interface PropertyGroup {
|
|
88
|
-
name: string;
|
|
89
|
-
properties: Array<Property>;
|
|
90
|
-
visibilityCondition?: {
|
|
91
|
-
dependsOn: string | string[];
|
|
92
|
-
condition: string;
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
97
|
// Add this interface near the other interfaces at the top of the file
|
|
97
98
|
interface ContentProperty {
|
|
98
99
|
id: string;
|
|
@@ -117,9 +118,10 @@
|
|
|
117
118
|
// Dimensions
|
|
118
119
|
export let width: string = "450px";
|
|
119
120
|
export let height: string = "720px";
|
|
120
|
-
// Schemas
|
|
121
|
-
|
|
122
|
-
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;
|
|
123
125
|
export let itemTemplate: any = null; // Store template for new items
|
|
124
126
|
// Tab titles
|
|
125
127
|
export let designTabTitle: string = 'Design';
|
|
@@ -214,7 +216,7 @@
|
|
|
214
216
|
const DRAG_SHIFT_X = 24; // shift mirror left so cursor is over drag handle
|
|
215
217
|
|
|
216
218
|
// Helper function to get visible groups
|
|
217
|
-
function getVisibleGroups(components:
|
|
219
|
+
function getVisibleGroups(components: DesignSchemaComponentView[]): PropertyGroup[] {
|
|
218
220
|
return components?.reduce((acc: PropertyGroup[], component) => {
|
|
219
221
|
const visibleGroups = component.propertyGroups.filter((group: PropertyGroup) =>
|
|
220
222
|
shouldShowPropertyGroup(group, component.propertyGroups[0].properties, 'design')
|
|
@@ -228,7 +230,7 @@
|
|
|
228
230
|
|
|
229
231
|
// Update the function with proper type annotations
|
|
230
232
|
function getAccordionItemPosition(
|
|
231
|
-
component:
|
|
233
|
+
component: DesignSchemaComponentView,
|
|
232
234
|
propertyGroup: PropertyGroup,
|
|
233
235
|
groupIndex: number
|
|
234
236
|
): 'first' | 'middle' | 'last' | 'only' {
|
|
@@ -1357,7 +1359,44 @@
|
|
|
1357
1359
|
return '';
|
|
1358
1360
|
}
|
|
1359
1361
|
|
|
1360
|
-
|
|
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 {
|
|
1361
1400
|
const basePropertyId = property.id || property.name;
|
|
1362
1401
|
const propertyId = contentItemId ? `${contentItemId}__${basePropertyId}` : basePropertyId;
|
|
1363
1402
|
const controllerId = `${schema}-${propertyId}`;
|
|
@@ -1368,7 +1407,7 @@
|
|
|
1368
1407
|
component: null,
|
|
1369
1408
|
props: { id: controllerId },
|
|
1370
1409
|
header: { label: basePropertyId, helpText: '' }
|
|
1371
|
-
};
|
|
1410
|
+
} as RenderedInputController;
|
|
1372
1411
|
}
|
|
1373
1412
|
const InputControllerComponent = getInputControllerComponent(inputController.type.toLowerCase());
|
|
1374
1413
|
|
|
@@ -1392,17 +1431,11 @@
|
|
|
1392
1431
|
|
|
1393
1432
|
const { description, label, ...restInputController } = inputController;
|
|
1394
1433
|
|
|
1395
|
-
// Only set shouldFocus on initial mount or explicit focus request
|
|
1396
|
-
const shouldFocus = activeControllerId === controllerId;
|
|
1397
|
-
|
|
1398
1434
|
// Add special handling for position controller
|
|
1399
1435
|
const additionalProps = inputController.type.toLowerCase() === 'position'
|
|
1400
1436
|
? { onDragStateChange: (dragging: boolean) => isDragging = dragging }
|
|
1401
1437
|
: {};
|
|
1402
1438
|
|
|
1403
|
-
// Add visibility dependency tracking
|
|
1404
|
-
const hasVisibilityCondition = property.visibilityCondition !== undefined;
|
|
1405
|
-
|
|
1406
1439
|
return {
|
|
1407
1440
|
component: InputControllerComponent,
|
|
1408
1441
|
props: {
|
|
@@ -1431,7 +1464,6 @@
|
|
|
1431
1464
|
openDropdowns.set(new Set([id]));
|
|
1432
1465
|
}
|
|
1433
1466
|
},
|
|
1434
|
-
shouldFocus,
|
|
1435
1467
|
onFocus: () => {
|
|
1436
1468
|
const newControllerId = `${schema}-${propertyId}`;
|
|
1437
1469
|
if (schema === 'design' && lastFocusedDesignController !== newControllerId) {
|
|
@@ -1453,8 +1485,7 @@
|
|
|
1453
1485
|
groupName: schema
|
|
1454
1486
|
});
|
|
1455
1487
|
}
|
|
1456
|
-
}
|
|
1457
|
-
'data-visibility-dependent': hasVisibilityCondition
|
|
1488
|
+
}
|
|
1458
1489
|
},
|
|
1459
1490
|
header: {
|
|
1460
1491
|
label: String(inputController.type || '').toLowerCase() === 'font'
|
|
@@ -1501,31 +1532,102 @@
|
|
|
1501
1532
|
}
|
|
1502
1533
|
}
|
|
1503
1534
|
|
|
1504
|
-
/**
|
|
1505
|
-
|
|
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 {
|
|
1506
1543
|
switch (component) {
|
|
1507
|
-
case TextInputController: return { kind: 'textinput', props
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
case
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
case
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
case
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
case
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
case
|
|
1528
|
-
|
|
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 } };
|
|
1529
1631
|
default: return null;
|
|
1530
1632
|
}
|
|
1531
1633
|
}
|
|
@@ -1636,39 +1738,16 @@
|
|
|
1636
1738
|
}
|
|
1637
1739
|
|
|
1638
1740
|
/**
|
|
1639
|
-
*
|
|
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.
|
|
1640
1746
|
*/
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
defaultValue?: any;
|
|
1646
|
-
value?: any; // Add value property to interface
|
|
1647
|
-
constraints?: Record<string, any>;
|
|
1648
|
-
inputController: {
|
|
1649
|
-
type: string;
|
|
1650
|
-
description?: string;
|
|
1651
|
-
helpText?: string;
|
|
1652
|
-
config?: Record<string, any>;
|
|
1653
|
-
};
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
|
-
interface ContentItem {
|
|
1657
|
-
// Saved content may carry schema-defined values alongside its metadata.
|
|
1658
|
-
[propertyName: string]: unknown;
|
|
1659
|
-
id: string;
|
|
1660
|
-
properties: Array<Property>;
|
|
1661
|
-
orderNumber?: number;
|
|
1662
|
-
title?: string;
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
interface ContentSchema {
|
|
1666
|
-
genericContent: {
|
|
1667
|
-
title: string;
|
|
1668
|
-
properties: Array<Property>;
|
|
1669
|
-
};
|
|
1670
|
-
contentItems: ContentItem[];
|
|
1671
|
-
}
|
|
1747
|
+
type Property = DesignSchemaPropertyView;
|
|
1748
|
+
type PropertyGroup = DesignSchemaPropertyGroupView;
|
|
1749
|
+
type ContentItem = ContentSchemaItemView;
|
|
1750
|
+
type ContentSchema = ContentSchemaView;
|
|
1672
1751
|
|
|
1673
1752
|
onMount(() => {
|
|
1674
1753
|
const resizeObserver = new ResizeObserver(() => {
|
|
@@ -2091,7 +2170,13 @@
|
|
|
2091
2170
|
const key = p.id || p.name;
|
|
2092
2171
|
// Canonical state first; schema mirror/defaultValue are legacy
|
|
2093
2172
|
// seeds for keys the editor never touched.
|
|
2094
|
-
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;
|
|
2095
2180
|
props[key] = normalizeContentPropertyValue(
|
|
2096
2181
|
pickLiveContentValue(
|
|
2097
2182
|
contentState[`${item.id}__${key}`],
|
|
@@ -2587,8 +2672,7 @@
|
|
|
2587
2672
|
<InputControllerRenderer
|
|
2588
2673
|
controller={describeInputController(InputComponent, {
|
|
2589
2674
|
...props,
|
|
2590
|
-
value: reactiveValue !== undefined ? reactiveValue : props.value
|
|
2591
|
-
shouldFocus: activeControllerId === props.id
|
|
2675
|
+
value: reactiveValue !== undefined ? reactiveValue : props.value
|
|
2592
2676
|
})}
|
|
2593
2677
|
/>
|
|
2594
2678
|
</div>
|
|
@@ -2698,7 +2782,7 @@
|
|
|
2698
2782
|
|
|
2699
2783
|
<div class="ic-input-ct {isFontIc ? 'w-full' : 'shrink-0'}" style={isFontIc ? '' : 'width: 185px;'}>
|
|
2700
2784
|
<InputControllerRenderer
|
|
2701
|
-
controller={describeInputController(InputComponent, { ...props, header
|
|
2785
|
+
controller={describeInputController(InputComponent, { ...props, header })}
|
|
2702
2786
|
/>
|
|
2703
2787
|
</div>
|
|
2704
2788
|
</div>
|
|
@@ -2796,7 +2880,7 @@
|
|
|
2796
2880
|
|
|
2797
2881
|
<div class="ic-input-ct {isFontIc ? 'w-full' : 'shrink-0'}" style={isFontIc ? '' : 'width: 185px;'}>
|
|
2798
2882
|
<InputControllerRenderer
|
|
2799
|
-
controller={describeInputController(InputComponent, { ...props, header
|
|
2883
|
+
controller={describeInputController(InputComponent, { ...props, header })}
|
|
2800
2884
|
/>
|
|
2801
2885
|
</div>
|
|
2802
2886
|
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ContentSchemaView, DesignSchemaView } from '../../schemaViews.js';
|
|
1
2
|
/**
|
|
2
3
|
* STYLES from Widgetic Design System
|
|
3
4
|
* Note: props-editor.css global rules (body, *) moved to app.css (standalone only).
|
|
@@ -24,8 +25,8 @@ declare const PropsEditor: $$__sveltets_2_IsomorphicComponent<{
|
|
|
24
25
|
style?: string;
|
|
25
26
|
width?: string;
|
|
26
27
|
height?: string;
|
|
27
|
-
designSchema?:
|
|
28
|
-
contentSchema?:
|
|
28
|
+
designSchema?: DesignSchemaView | null;
|
|
29
|
+
contentSchema?: ContentSchemaView | undefined;
|
|
29
30
|
itemTemplate?: any;
|
|
30
31
|
designTabTitle?: string;
|
|
31
32
|
contentTabTitle?: string;
|
|
@@ -11,19 +11,33 @@
|
|
|
11
11
|
* - Smooth transitions and visual feedback
|
|
12
12
|
-->
|
|
13
13
|
|
|
14
|
+
<script module lang="ts">
|
|
15
|
+
import type { InputControllerConfig } from './index';
|
|
16
|
+
|
|
17
|
+
/** Declared props contract — export lets below are annotated from it so the
|
|
18
|
+
* interface cannot drift from what the component actually reads. */
|
|
19
|
+
export interface AngleInputControllerProps {
|
|
20
|
+
label: string;
|
|
21
|
+
value?: number;
|
|
22
|
+
onChange: (newValue: number) => void;
|
|
23
|
+
config?: InputControllerConfig;
|
|
24
|
+
helpText?: string;
|
|
25
|
+
onFocus?: () => void;
|
|
26
|
+
}
|
|
27
|
+
</script>
|
|
28
|
+
|
|
14
29
|
<script lang="ts">
|
|
15
30
|
import { onMount, onDestroy } from 'svelte';
|
|
16
31
|
import { Button, Input } from '@widgetic/design-system/components';
|
|
17
32
|
import InputControllerHeader from './InputControllerHeader.svelte';
|
|
18
33
|
import NumberControls from './NumberControls.svelte';
|
|
19
|
-
import type { InputControllerConfig } from './index';
|
|
20
34
|
|
|
21
|
-
export let label:
|
|
22
|
-
export let value:
|
|
23
|
-
export let onChange:
|
|
24
|
-
export let config:
|
|
25
|
-
export let helpText:
|
|
26
|
-
export let onFocus:
|
|
35
|
+
export let label: AngleInputControllerProps['label'];
|
|
36
|
+
export let value: AngleInputControllerProps['value'] = 0;
|
|
37
|
+
export let onChange: AngleInputControllerProps['onChange'];
|
|
38
|
+
export let config: AngleInputControllerProps['config'] = {};
|
|
39
|
+
export let helpText: AngleInputControllerProps['helpText'] = undefined;
|
|
40
|
+
export let onFocus: AngleInputControllerProps['onFocus'] = () => {};
|
|
27
41
|
|
|
28
42
|
// Circle indicator state
|
|
29
43
|
let isDragging = false;
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { InputControllerConfig } from './index';
|
|
2
|
+
/** Declared props contract — export lets below are annotated from it so the
|
|
3
|
+
* interface cannot drift from what the component actually reads. */
|
|
4
|
+
export interface AngleInputControllerProps {
|
|
5
|
+
label: string;
|
|
6
|
+
value?: number;
|
|
7
|
+
onChange: (newValue: number) => void;
|
|
8
|
+
config?: InputControllerConfig;
|
|
9
|
+
helpText?: string;
|
|
10
|
+
onFocus?: () => void;
|
|
11
|
+
}
|
|
2
12
|
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
13
|
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
14
|
$$bindings?: Bindings;
|
|
@@ -13,12 +23,12 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
|
|
|
13
23
|
z_$$bindings?: Bindings;
|
|
14
24
|
}
|
|
15
25
|
declare const AngleInputController: $$__sveltets_2_IsomorphicComponent<{
|
|
16
|
-
label:
|
|
17
|
-
value?:
|
|
18
|
-
onChange:
|
|
19
|
-
config?:
|
|
20
|
-
helpText?:
|
|
21
|
-
onFocus?:
|
|
26
|
+
label: AngleInputControllerProps["label"];
|
|
27
|
+
value?: AngleInputControllerProps["value"];
|
|
28
|
+
onChange: AngleInputControllerProps["onChange"];
|
|
29
|
+
config?: AngleInputControllerProps["config"];
|
|
30
|
+
helpText?: AngleInputControllerProps["helpText"];
|
|
31
|
+
onFocus?: AngleInputControllerProps["onFocus"];
|
|
22
32
|
}, {
|
|
23
33
|
[evt: string]: CustomEvent<any>;
|
|
24
34
|
}, {}, {}, string>;
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { InputControllerConfig } from './index';
|
|
3
|
+
|
|
4
|
+
interface FileValue {
|
|
5
|
+
id: string;
|
|
6
|
+
file: string;
|
|
7
|
+
url: string;
|
|
8
|
+
hash?: string; // Add hash property for ThumbHash
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Declared props contract — export lets below are annotated from it so the
|
|
12
|
+
* interface cannot drift from what the component actually reads. */
|
|
13
|
+
export interface BrowserInputControllerProps {
|
|
14
|
+
header?: { label: string; helpText?: string };
|
|
15
|
+
label?: string;
|
|
16
|
+
value?: string | FileValue;
|
|
17
|
+
onChange?: (newValue: string | FileValue) => void;
|
|
18
|
+
portalTarget?: string | HTMLElement;
|
|
19
|
+
config?: InputControllerConfig;
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
1
23
|
<script lang="ts">
|
|
2
24
|
/**
|
|
3
25
|
* BrowserInputController Component
|
|
@@ -16,8 +38,6 @@
|
|
|
16
38
|
import X from '../icons/x.svelte';
|
|
17
39
|
// console.log("BrowserInputController - Tooltip:", Tooltip);
|
|
18
40
|
|
|
19
|
-
// config imports
|
|
20
|
-
import type { InputControllerConfig } from './index';
|
|
21
41
|
|
|
22
42
|
// utils imports
|
|
23
43
|
import MediaManager from '../../utils/MediaManager';
|
|
@@ -32,26 +52,19 @@
|
|
|
32
52
|
|
|
33
53
|
import { InputControllerHeader } from './index';
|
|
34
54
|
|
|
35
|
-
interface FileValue {
|
|
36
|
-
id: string;
|
|
37
|
-
file: string;
|
|
38
|
-
url: string;
|
|
39
|
-
hash?: string; // Add hash property for ThumbHash
|
|
40
|
-
}
|
|
41
|
-
|
|
42
55
|
// Component props with default values
|
|
43
|
-
export let header:
|
|
44
|
-
export let label:
|
|
45
|
-
export let value:
|
|
46
|
-
export let onChange:
|
|
47
|
-
export let portalTarget:
|
|
56
|
+
export let header: BrowserInputControllerProps['header'] = undefined;
|
|
57
|
+
export let label: BrowserInputControllerProps['label'] = '';
|
|
58
|
+
export let value: BrowserInputControllerProps['value'] = '';
|
|
59
|
+
export let onChange: BrowserInputControllerProps['onChange'] = () => {};
|
|
60
|
+
export let portalTarget: BrowserInputControllerProps['portalTarget'] = 'portal-element';
|
|
48
61
|
|
|
49
62
|
/** Full-panel overlay when portaled to props-editor panel (not document body). */
|
|
50
63
|
$: fillEditorPanel =
|
|
51
64
|
portalTarget === 'props-editor-file-browser-portal' ||
|
|
52
65
|
(portalTarget instanceof HTMLElement &&
|
|
53
66
|
portalTarget.id === 'props-editor-file-browser-portal');
|
|
54
|
-
export let config:
|
|
67
|
+
export let config: BrowserInputControllerProps['config'] = {};
|
|
55
68
|
|
|
56
69
|
// Extract allowed file types from config or default to all files
|
|
57
70
|
const fileTypes = config.fileTypes || ['*'];
|