@widgetic/editor 4.0.1 โ 4.0.3
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/dist/components/MockWidget.svelte +1 -1
- package/dist/components/WidgetPreview.svelte +36 -52
- package/dist/components/editors/EmbedEditor.svelte +1 -1
- package/dist/components/editors/PropsEditor.svelte +309 -227
- package/dist/components/ic/BrowserInputController.svelte +26 -19
- package/dist/components/ic/CheckboxInputController.svelte +1 -1
- package/dist/components/ic/ColorPickerInputController.svelte +37 -37
- package/dist/components/ic/DatePickerInputController.svelte +6 -6
- package/dist/components/ic/DropdownInputController.svelte +1 -1
- package/dist/components/ic/FontInputController.svelte +225 -116
- package/dist/components/ic/FontInputController.svelte.d.ts +4 -2
- package/dist/components/ic/NumberInputController.svelte +37 -37
- package/dist/components/ic/RadioGroupInputController.svelte +1 -1
- package/dist/components/ic/SliderRangeInputController.svelte +1 -1
- package/dist/components/ic/SwitchInputController.svelte +1 -1
- package/dist/components/ic/TextAreaInputController.svelte +213 -85
- package/dist/components/ic/TextAreaInputController.svelte.d.ts +1 -0
- package/dist/components/ic/TextAreaPreviewInputController.svelte +81 -79
- package/dist/components/ic/VectorInputController.svelte +34 -34
- package/dist/components/ic/shared/CategoryTabs.svelte +1 -0
- package/dist/components/ic/shared/RecentItemsSection.svelte +21 -6
- package/dist/components/ic/shared/RecentItemsSection.svelte.d.ts +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +19 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/stores/userSession.d.ts +1 -1
- package/dist/stores/userSession.js +17 -2
- package/dist/utils/FontManager.d.ts +3 -1
- package/dist/utils/FontManager.js +95 -103
- package/dist/utils/VectorManager.d.ts +3 -1
- package/dist/utils/VectorManager.js +30 -70
- package/dist/utils/colorTransparency.js +3 -3
- package/dist/utils/fontUtils.js +1 -1
- package/dist/utils/passiveEvents.d.ts +5 -0
- package/dist/utils/passiveEvents.js +9 -18
- package/dist/utils/recentColors.js +15 -15
- package/dist/utils/vectorUtils.js +1 -1
- package/package.json +1 -1
|
@@ -157,6 +157,18 @@
|
|
|
157
157
|
let hasLoggedContent = false; // Prevents duplicate logging of content tab info
|
|
158
158
|
let designState: Record<string, InputValue> = { ...initialDesignValues }; // Stores design property values
|
|
159
159
|
let contentState: Record<string, InputValue> = { ...initialContentValues }; // Stores content property values
|
|
160
|
+
|
|
161
|
+
let prevInitialDesignValues = initialDesignValues;
|
|
162
|
+
$: if (initialDesignValues !== prevInitialDesignValues) {
|
|
163
|
+
prevInitialDesignValues = initialDesignValues;
|
|
164
|
+
designState = { ...initialDesignValues };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let prevInitialContentValues = initialContentValues;
|
|
168
|
+
$: if (initialContentValues !== prevInitialContentValues) {
|
|
169
|
+
prevInitialContentValues = initialContentValues;
|
|
170
|
+
contentState = { ...initialContentValues };
|
|
171
|
+
}
|
|
160
172
|
let isGenericContentOpen = true; // Controls generic content section visibility
|
|
161
173
|
let lastFocusedDesignController: string | null = null; // Tracks last focused design input
|
|
162
174
|
let lastFocusedContentController: string | null = null; // Tracks last focused content input
|
|
@@ -226,7 +238,7 @@
|
|
|
226
238
|
}
|
|
227
239
|
|
|
228
240
|
if (keysToRemove.length > 0) {
|
|
229
|
-
console.log('๐งน [PropsEditor] Clearing font weight states on init:', keysToRemove);
|
|
241
|
+
// console.log('๐งน [PropsEditor] Clearing font weight states on init:', keysToRemove);
|
|
230
242
|
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
231
243
|
}
|
|
232
244
|
}
|
|
@@ -275,26 +287,8 @@
|
|
|
275
287
|
// Initialize FontManager
|
|
276
288
|
const initializeEditor = async () => {
|
|
277
289
|
try {
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
// hasContentSchema: !!contentSchema
|
|
281
|
-
// });
|
|
282
|
-
|
|
283
|
-
// Initialize FontManager when PropsEditor mounts
|
|
284
|
-
// console.log('๐ [PropsEditor] Initializing FontManager...');
|
|
285
|
-
const fontManagerInstance = await FontManager.getInstance(appConfig.GOOGLE_FONTS_API_KEY);
|
|
286
|
-
// console.log('โ
[PropsEditor] FontManager initialized');
|
|
287
|
-
|
|
288
|
-
// Preload the "Mine" category fonts to prevent loading delay in FontInputController instances
|
|
289
|
-
try {
|
|
290
|
-
const customFonts = await fontManagerInstance.loadCustomFonts();
|
|
291
|
-
console.log('โ
[PropsEditor] Preloaded "Mine" category fonts:', {
|
|
292
|
-
count: customFonts.length,
|
|
293
|
-
fonts: customFonts.slice(0, 3).map(f => f.family)
|
|
294
|
-
});
|
|
295
|
-
} catch (error) {
|
|
296
|
-
console.error('โ [PropsEditor] Failed to preload "Mine" category fonts:', error);
|
|
297
|
-
}
|
|
290
|
+
// Initialize FontManager in background without blocking editor startup
|
|
291
|
+
void FontManager.getInstance(appConfig.GOOGLE_FONTS_API_KEY);
|
|
298
292
|
|
|
299
293
|
// Log design tab info if schema exists
|
|
300
294
|
if (designSchema && designSchema[0]?.components && !hasLoggedDesign) {
|
|
@@ -507,7 +501,7 @@
|
|
|
507
501
|
|
|
508
502
|
// Update event handlers
|
|
509
503
|
currentDraggable.on('drag:start', (event: any) => {
|
|
510
|
-
console.log('[Draggable] drag:start event');
|
|
504
|
+
// console.log('[Draggable] drag:start event');
|
|
511
505
|
isDragging = true; // Set flag immediately to prevent afterUpdate interference
|
|
512
506
|
|
|
513
507
|
const dragHandle = event.originalSource?.querySelector('.drag-handle');
|
|
@@ -577,7 +571,7 @@
|
|
|
577
571
|
if (deltaX > dragThreshold || deltaY > dragThreshold) {
|
|
578
572
|
isDragStarted = true;
|
|
579
573
|
// isDragging = true; - This is now set in drag:start
|
|
580
|
-
console.log('[Draggable] isDragStarted set to true');
|
|
574
|
+
// console.log('[Draggable] isDragStarted set to true');
|
|
581
575
|
|
|
582
576
|
requestAnimationFrame(() => {
|
|
583
577
|
const mirror = document.querySelector('.draggable-mirror');
|
|
@@ -705,7 +699,7 @@
|
|
|
705
699
|
});
|
|
706
700
|
|
|
707
701
|
currentDraggable.on('sortable:sorted', (event: { newIndex: number; oldIndex: number }) => {
|
|
708
|
-
console.log('[Draggable] sortable:sorted event fired', { oldIndex: event.oldIndex, newIndex: event.newIndex });
|
|
702
|
+
// console.log('[Draggable] sortable:sorted event fired', { oldIndex: event.oldIndex, newIndex: event.newIndex });
|
|
709
703
|
try {
|
|
710
704
|
const { oldIndex, newIndex } = event;
|
|
711
705
|
|
|
@@ -718,7 +712,7 @@
|
|
|
718
712
|
|
|
719
713
|
// No need to do anything if the item was dropped in the same place
|
|
720
714
|
if (oldIndex === newIndex) {
|
|
721
|
-
console.log('[Drag] Item dropped in the same position. No change needed.');
|
|
715
|
+
// console.log('[Drag] Item dropped in the same position. No change needed.');
|
|
722
716
|
isDragStarted = false;
|
|
723
717
|
return;
|
|
724
718
|
}
|
|
@@ -728,7 +722,7 @@
|
|
|
728
722
|
isDragStarted = false;
|
|
729
723
|
return;
|
|
730
724
|
}
|
|
731
|
-
console.log('[Drag] Before:', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
725
|
+
// console.log('[Drag] Before:', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
732
726
|
|
|
733
727
|
const items = [...contentSchema.contentItems];
|
|
734
728
|
const [movedItem] = items.splice(oldIndex, 1);
|
|
@@ -749,7 +743,7 @@
|
|
|
749
743
|
|
|
750
744
|
const renumbered = renumberAutoItemTitles(items);
|
|
751
745
|
contentSchema = { ...contentSchema, contentItems: renumbered };
|
|
752
|
-
console.log('[Drag] After (reordered with indices):', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
746
|
+
// console.log('[Drag] After (reordered with indices):', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
753
747
|
dispatch('updated', { contentSchema });
|
|
754
748
|
dispatchContentItemsChanged(contentSchema.contentItems);
|
|
755
749
|
|
|
@@ -855,9 +849,11 @@
|
|
|
855
849
|
};
|
|
856
850
|
|
|
857
851
|
// Add this near other reactive declarations at the top level
|
|
858
|
-
$: if (Array.isArray(contentSchema?.contentItems)
|
|
859
|
-
|
|
860
|
-
|
|
852
|
+
$: if (Array.isArray(contentSchema?.contentItems)) {
|
|
853
|
+
const templateSource = contentSchema.contentItems.find((item: ContentItem) => Array.isArray(item.properties) && item.properties.length > 0);
|
|
854
|
+
if (templateSource) {
|
|
855
|
+
itemTemplate = JSON.parse(JSON.stringify(templateSource));
|
|
856
|
+
}
|
|
861
857
|
}
|
|
862
858
|
|
|
863
859
|
// Populate contentState with per-item values keyed as "itemId__propName".
|
|
@@ -872,10 +868,12 @@
|
|
|
872
868
|
if (contentState[key] === undefined) {
|
|
873
869
|
// Prefer the parent's canonical saved-value map over the
|
|
874
870
|
// schema mirror, which older compositions may not fill.
|
|
875
|
-
const
|
|
876
|
-
const
|
|
877
|
-
|
|
878
|
-
|
|
871
|
+
const propKey = prop.id || prop.name;
|
|
872
|
+
const parentValue = parentItemValues?.[item.id]?.[propKey];
|
|
873
|
+
const liveOnItem = propKey != null ? (item as Record<string, unknown>)[propKey] : undefined;
|
|
874
|
+
const seed = pickLiveContentValue(parentValue, liveOnItem, prop.value);
|
|
875
|
+
if (seed !== undefined && seed !== '') {
|
|
876
|
+
patch[key] = seed as InputValue;
|
|
879
877
|
needsUpdate = true;
|
|
880
878
|
}
|
|
881
879
|
}
|
|
@@ -883,7 +881,7 @@
|
|
|
883
881
|
}
|
|
884
882
|
if (needsUpdate) {
|
|
885
883
|
contentState = { ...contentState, ...patch };
|
|
886
|
-
console.log('[PropsEditor] Initialized content item state keys:', Object.keys(patch));
|
|
884
|
+
// console.log('[PropsEditor] Initialized content item state keys:', Object.keys(patch));
|
|
887
885
|
}
|
|
888
886
|
}
|
|
889
887
|
|
|
@@ -1022,63 +1020,61 @@
|
|
|
1022
1020
|
* @param opacity - Optional opacity value for color properties
|
|
1023
1021
|
*/
|
|
1024
1022
|
function updateState(schema: string, propertyId: string, value: InputValue, opacity?: number) {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
if (
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
contentSchema = contentSchema; // trigger Svelte reactivity
|
|
1055
|
-
}
|
|
1023
|
+
if (schema === 'design') {
|
|
1024
|
+
const newState = { ...designState };
|
|
1025
|
+
newState[propertyId] = value;
|
|
1026
|
+
if (opacity !== undefined) {
|
|
1027
|
+
newState[`${propertyId}_opacity`] = opacity;
|
|
1028
|
+
}
|
|
1029
|
+
designState = newState;
|
|
1030
|
+
} else {
|
|
1031
|
+
const newState = { ...contentState };
|
|
1032
|
+
newState[propertyId] = value;
|
|
1033
|
+
if (opacity !== undefined) {
|
|
1034
|
+
newState[`${propertyId}_opacity`] = opacity;
|
|
1035
|
+
}
|
|
1036
|
+
contentState = newState;
|
|
1037
|
+
|
|
1038
|
+
// For content item properties (keyed as "itemId__propName"), also update item.properties[].value
|
|
1039
|
+
if (propertyId.includes('__') && Array.isArray(contentSchema?.contentItems)) {
|
|
1040
|
+
const [itemId, basePropName] = propertyId.split('__');
|
|
1041
|
+
const item = contentSchema.contentItems.find((i: ContentItem) => i.id === itemId);
|
|
1042
|
+
if (item) {
|
|
1043
|
+
const prop = item.properties.find((p: any) => (p.id || p.name) === basePropName);
|
|
1044
|
+
if (prop) prop.value = value;
|
|
1045
|
+
|
|
1046
|
+
// Update the collapsible header title when a "title"-like property changes
|
|
1047
|
+
const propName = (basePropName || '').toLowerCase();
|
|
1048
|
+
if (propName.includes('title') || propName.includes('name') || propName.includes('label')) {
|
|
1049
|
+
if (typeof value === 'string' && value.trim()) {
|
|
1050
|
+
item.title = value;
|
|
1051
|
+
contentSchema = contentSchema; // trigger Svelte reactivity
|
|
1056
1052
|
}
|
|
1057
1053
|
}
|
|
1058
1054
|
}
|
|
1059
1055
|
}
|
|
1056
|
+
}
|
|
1060
1057
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1058
|
+
// Dispatch with both composite key and item context for parent to handle
|
|
1059
|
+
let itemId: string | undefined;
|
|
1060
|
+
let originalPropertyId = propertyId;
|
|
1061
|
+
if (schema === 'content' && propertyId.includes('__')) {
|
|
1062
|
+
[itemId, originalPropertyId] = propertyId.split('__');
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
dispatch('propertyChange', { schema, propertyId: originalPropertyId, value, opacity, itemId });
|
|
1069
1066
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1067
|
+
if (schema === 'content' && itemId && Array.isArray(contentSchema?.contentItems)) {
|
|
1068
|
+
dispatchContentItemsChanged(contentSchema.contentItems);
|
|
1069
|
+
}
|
|
1073
1070
|
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
}
|
|
1071
|
+
// When editing a content item property, navigate widget to that item
|
|
1072
|
+
if (itemId) {
|
|
1073
|
+
const itemIndex = getContentItemIndex(itemId);
|
|
1074
|
+
if (itemIndex >= 0) {
|
|
1075
|
+
dispatch('goToItem', { index: itemIndex });
|
|
1080
1076
|
}
|
|
1081
|
-
}
|
|
1077
|
+
}
|
|
1082
1078
|
}
|
|
1083
1079
|
|
|
1084
1080
|
function getContentItemsMax(): number | null {
|
|
@@ -1133,10 +1129,10 @@
|
|
|
1133
1129
|
lastFocusedContentController = controllerId;
|
|
1134
1130
|
}
|
|
1135
1131
|
|
|
1136
|
-
console.log(`Controller focused in ${currentTab} tab:`, {
|
|
1137
|
-
controllerId,
|
|
1138
|
-
...controllerInfo
|
|
1139
|
-
});
|
|
1132
|
+
// console.log(`Controller focused in ${currentTab} tab:`, {
|
|
1133
|
+
// controllerId,
|
|
1134
|
+
// ...controllerInfo
|
|
1135
|
+
// });
|
|
1140
1136
|
}
|
|
1141
1137
|
}
|
|
1142
1138
|
|
|
@@ -1149,12 +1145,12 @@
|
|
|
1149
1145
|
const currentValue = tab ?? getActiveTab();
|
|
1150
1146
|
const previousTab = currentTab;
|
|
1151
1147
|
|
|
1152
|
-
console.log('๐ [PropsEditor] Tab Change:', {
|
|
1153
|
-
from: previousTab,
|
|
1154
|
-
to: currentValue,
|
|
1155
|
-
hasContentSchema: !!contentSchema,
|
|
1156
|
-
isInitialLoad
|
|
1157
|
-
});
|
|
1148
|
+
// console.log('๐ [PropsEditor] Tab Change:', {
|
|
1149
|
+
// from: previousTab,
|
|
1150
|
+
// to: currentValue,
|
|
1151
|
+
// hasContentSchema: !!contentSchema,
|
|
1152
|
+
// isInitialLoad
|
|
1153
|
+
// });
|
|
1158
1154
|
|
|
1159
1155
|
// Update tab state
|
|
1160
1156
|
currentTab = currentValue;
|
|
@@ -1164,16 +1160,16 @@
|
|
|
1164
1160
|
|
|
1165
1161
|
// Only proceed with content-related operations if contentSchema exists
|
|
1166
1162
|
if (currentValue === 'content' && !contentSchema) {
|
|
1167
|
-
console.log('โน๏ธ [PropsEditor] Content tab selected but no content schema available');
|
|
1163
|
+
// console.log('โน๏ธ [PropsEditor] Content tab selected but no content schema available');
|
|
1168
1164
|
return;
|
|
1169
1165
|
}
|
|
1170
1166
|
|
|
1171
1167
|
// Only log state if content exists
|
|
1172
1168
|
if (contentSchema) {
|
|
1173
|
-
console.log('๐พ Storing', currentValue, 'tab state:', {
|
|
1174
|
-
openItems: Array.from(openItems),
|
|
1175
|
-
openAccordion: currentValue === 'design' ? designOpenAccordion : contentOpenAccordion
|
|
1176
|
-
});
|
|
1169
|
+
// console.log('๐พ Storing', currentValue, 'tab state:', {
|
|
1170
|
+
// openItems: Array.from(openItems),
|
|
1171
|
+
// openAccordion: currentValue === 'design' ? designOpenAccordion : contentOpenAccordion
|
|
1172
|
+
// });
|
|
1177
1173
|
}
|
|
1178
1174
|
|
|
1179
1175
|
// First find and remove all modal-related elements, but preserve draggable
|
|
@@ -1194,15 +1190,15 @@
|
|
|
1194
1190
|
'.command-dialog'
|
|
1195
1191
|
].join(','));
|
|
1196
1192
|
|
|
1197
|
-
console.log('๐งน Cleaning up modals:', {
|
|
1198
|
-
count: modalElements.length,
|
|
1199
|
-
elements: Array.from(modalElements).map(el => ({
|
|
1200
|
-
className: el.className,
|
|
1201
|
-
role: el.getAttribute('role'),
|
|
1202
|
-
state: el.getAttribute('data-state'),
|
|
1203
|
-
side: el.getAttribute('data-side')
|
|
1204
|
-
}))
|
|
1205
|
-
});
|
|
1193
|
+
// console.log('๐งน Cleaning up modals:', {
|
|
1194
|
+
// count: modalElements.length,
|
|
1195
|
+
// elements: Array.from(modalElements).map(el => ({
|
|
1196
|
+
// className: el.className,
|
|
1197
|
+
// role: el.getAttribute('role'),
|
|
1198
|
+
// state: el.getAttribute('data-state'),
|
|
1199
|
+
// side: el.getAttribute('data-side')
|
|
1200
|
+
// }))
|
|
1201
|
+
// });
|
|
1206
1202
|
|
|
1207
1203
|
// Remove each modal immediately, but protect scroll-related and draggable elements
|
|
1208
1204
|
modalElements.forEach(element => {
|
|
@@ -1232,7 +1228,7 @@
|
|
|
1232
1228
|
// Clear the dropdowns store
|
|
1233
1229
|
const currentDropdowns = get(openDropdowns);
|
|
1234
1230
|
if (currentDropdowns.size > 0) {
|
|
1235
|
-
console.log('๐๏ธ Clearing dropdowns:', Array.from(currentDropdowns));
|
|
1231
|
+
// console.log('๐๏ธ Clearing dropdowns:', Array.from(currentDropdowns));
|
|
1236
1232
|
}
|
|
1237
1233
|
openDropdowns.set(new Set());
|
|
1238
1234
|
|
|
@@ -1250,7 +1246,7 @@
|
|
|
1250
1246
|
if (currentValue === 'content' && contentSchema) {
|
|
1251
1247
|
// Wait for tab content to be rendered
|
|
1252
1248
|
setTimeout(async () => {
|
|
1253
|
-
console.log('๐ฏ Checking draggable state in content tab');
|
|
1249
|
+
// console.log('๐ฏ Checking draggable state in content tab');
|
|
1254
1250
|
|
|
1255
1251
|
// Check if draggable needs initialization
|
|
1256
1252
|
const containers = document.querySelectorAll('.content-items');
|
|
@@ -1260,10 +1256,10 @@
|
|
|
1260
1256
|
!document.querySelector('.draggable-container');
|
|
1261
1257
|
|
|
1262
1258
|
if (needsInit) {
|
|
1263
|
-
console.log('๐ Reinitializing draggable');
|
|
1259
|
+
// console.log('๐ Reinitializing draggable');
|
|
1264
1260
|
await initializeDraggable();
|
|
1265
1261
|
} else {
|
|
1266
|
-
console.log('โ
Draggable already initialized and working');
|
|
1262
|
+
// console.log('โ
Draggable already initialized and working');
|
|
1267
1263
|
}
|
|
1268
1264
|
}, 50);
|
|
1269
1265
|
}
|
|
@@ -1274,10 +1270,10 @@
|
|
|
1274
1270
|
|
|
1275
1271
|
// Only log if content exists
|
|
1276
1272
|
if (contentSchema) {
|
|
1277
|
-
console.log('๐ Restoring state for', currentValue, 'tab:', {
|
|
1278
|
-
openItems: Array.from(openItems),
|
|
1279
|
-
accordionToOpen
|
|
1280
|
-
});
|
|
1273
|
+
// console.log('๐ Restoring state for', currentValue, 'tab:', {
|
|
1274
|
+
// openItems: Array.from(openItems),
|
|
1275
|
+
// accordionToOpen
|
|
1276
|
+
// });
|
|
1281
1277
|
}
|
|
1282
1278
|
|
|
1283
1279
|
// Restore accordion states after a short delay to ensure DOM is ready
|
|
@@ -1290,7 +1286,7 @@
|
|
|
1290
1286
|
}
|
|
1291
1287
|
}
|
|
1292
1288
|
if (itemsToOpen.length > 0) {
|
|
1293
|
-
console.log('๐ Opened accordions:', itemsToOpen);
|
|
1289
|
+
// console.log('๐ Opened accordions:', itemsToOpen);
|
|
1294
1290
|
}
|
|
1295
1291
|
}, 50);
|
|
1296
1292
|
|
|
@@ -1301,7 +1297,7 @@
|
|
|
1301
1297
|
: lastFocusedContentController;
|
|
1302
1298
|
|
|
1303
1299
|
if (controllerToFocus) {
|
|
1304
|
-
console.log('๐ฏ Focusing last focused controller:', controllerToFocus);
|
|
1300
|
+
// console.log('๐ฏ Focusing last focused controller:', controllerToFocus);
|
|
1305
1301
|
activeControllerId = controllerToFocus;
|
|
1306
1302
|
focusController(controllerToFocus);
|
|
1307
1303
|
} else {
|
|
@@ -1312,7 +1308,7 @@
|
|
|
1312
1308
|
if (firstController) {
|
|
1313
1309
|
const id = firstController.getAttribute('id');
|
|
1314
1310
|
if (id) {
|
|
1315
|
-
console.log('๐ฏ Focusing first controller:', id);
|
|
1311
|
+
// console.log('๐ฏ Focusing first controller:', id);
|
|
1316
1312
|
activeControllerId = id;
|
|
1317
1313
|
focusController(id);
|
|
1318
1314
|
}
|
|
@@ -1325,12 +1321,30 @@
|
|
|
1325
1321
|
* Input Controller Management
|
|
1326
1322
|
* Handles rendering and configuration of different input types
|
|
1327
1323
|
*/
|
|
1324
|
+
function fontIcVisibleLabel(propertyId: string, schemaLabel?: string, description?: string): string {
|
|
1325
|
+
const fromSchema = String(schemaLabel || '').trim();
|
|
1326
|
+
if (fromSchema) return fromSchema;
|
|
1327
|
+
const trimmed = String(description || '').trim();
|
|
1328
|
+
if (trimmed && !/^(titleFont|subtitleFont|headingFont|descriptionFont|font|titeFont)$/i.test(trimmed)) {
|
|
1329
|
+
return trimmed;
|
|
1330
|
+
}
|
|
1331
|
+
return '';
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1328
1334
|
function renderInputController(schema: string, property: any, propertyIndex: number, isFirstGroup: boolean, contentItemId?: string) {
|
|
1329
1335
|
const basePropertyId = property.id || property.name;
|
|
1330
1336
|
const propertyId = contentItemId ? `${contentItemId}__${basePropertyId}` : basePropertyId;
|
|
1337
|
+
const controllerId = `${schema}-${propertyId}`;
|
|
1331
1338
|
const { type, inputController } = property;
|
|
1339
|
+
if (!inputController?.type) {
|
|
1340
|
+
console.warn('[PropsEditor] Skipping property without inputController.type:', propertyId, property);
|
|
1341
|
+
return {
|
|
1342
|
+
component: null,
|
|
1343
|
+
props: { id: controllerId },
|
|
1344
|
+
header: { label: basePropertyId, helpText: '' }
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1332
1347
|
const InputControllerComponent = getInputControllerComponent(inputController.type.toLowerCase());
|
|
1333
|
-
const controllerId = `${schema}-${propertyId}`;
|
|
1334
1348
|
|
|
1335
1349
|
const stateValue = schema === 'design' ? designState[propertyId] : contentState[propertyId];
|
|
1336
1350
|
let initialValue = stateValue !== undefined ? stateValue : (property.value !== undefined ? property.value : property.defaultValue);
|
|
@@ -1373,6 +1387,9 @@
|
|
|
1373
1387
|
onOpacityChange: (opacity: number) => updateState(schema, propertyId, initialValue, opacity),
|
|
1374
1388
|
portalTarget,
|
|
1375
1389
|
...restInputController,
|
|
1390
|
+
...(String(inputController.type || '').toLowerCase() === 'font'
|
|
1391
|
+
? { label: fontIcVisibleLabel(propertyId, label, description) }
|
|
1392
|
+
: {}),
|
|
1376
1393
|
...property.constraints,
|
|
1377
1394
|
...additionalProps,
|
|
1378
1395
|
config: inputController.config,
|
|
@@ -1381,10 +1398,10 @@
|
|
|
1381
1398
|
toggleDropdown: (id: string) => {
|
|
1382
1399
|
const currentSet = get(openDropdowns);
|
|
1383
1400
|
if (currentSet.has(id)) {
|
|
1384
|
-
console.log(`[toggleDropdown] CLOSING ${id}`);
|
|
1401
|
+
// console.log(`[toggleDropdown] CLOSING ${id}`);
|
|
1385
1402
|
openDropdowns.set(new Set());
|
|
1386
1403
|
} else {
|
|
1387
|
-
console.log(`[toggleDropdown] OPENING ${id} (closing all others)`);
|
|
1404
|
+
// console.log(`[toggleDropdown] OPENING ${id} (closing all others)`);
|
|
1388
1405
|
openDropdowns.set(new Set([id]));
|
|
1389
1406
|
}
|
|
1390
1407
|
},
|
|
@@ -1414,7 +1431,9 @@
|
|
|
1414
1431
|
'data-visibility-dependent': hasVisibilityCondition
|
|
1415
1432
|
},
|
|
1416
1433
|
header: {
|
|
1417
|
-
label:
|
|
1434
|
+
label: String(inputController.type || '').toLowerCase() === 'font'
|
|
1435
|
+
? fontIcVisibleLabel(propertyId, label, description)
|
|
1436
|
+
: (description || propertyId),
|
|
1418
1437
|
helpText: inputController.helpText
|
|
1419
1438
|
}
|
|
1420
1439
|
};
|
|
@@ -1464,7 +1483,7 @@
|
|
|
1464
1483
|
* Used when changing tabs or when scroll events occur
|
|
1465
1484
|
*/
|
|
1466
1485
|
function closeAllDropdowns() {
|
|
1467
|
-
console.log('Closing all dropdowns');
|
|
1486
|
+
// console.log('Closing all dropdowns');
|
|
1468
1487
|
openDropdowns.set(new Set());
|
|
1469
1488
|
}
|
|
1470
1489
|
|
|
@@ -1528,7 +1547,7 @@
|
|
|
1528
1547
|
* @param itemId - Identifier for the content item to toggle
|
|
1529
1548
|
*/
|
|
1530
1549
|
function toggleItem(itemId: string) {
|
|
1531
|
-
console.log('๐ Toggling item:', itemId, 'Current state:', openItems.has(itemId));
|
|
1550
|
+
// console.log('๐ Toggling item:', itemId, 'Current state:', openItems.has(itemId));
|
|
1532
1551
|
|
|
1533
1552
|
if (openItems.has(itemId)) {
|
|
1534
1553
|
openItems.delete(itemId);
|
|
@@ -1548,11 +1567,11 @@
|
|
|
1548
1567
|
openItems = new Set(openItems);
|
|
1549
1568
|
previouslyOpen = new Set(previouslyOpen);
|
|
1550
1569
|
|
|
1551
|
-
console.log('โ
New state:', {
|
|
1552
|
-
isOpen: openItems.has(itemId),
|
|
1553
|
-
openItems: Array.from(openItems),
|
|
1554
|
-
previouslyOpen: Array.from(previouslyOpen)
|
|
1555
|
-
});
|
|
1570
|
+
// console.log('โ
New state:', {
|
|
1571
|
+
// isOpen: openItems.has(itemId),
|
|
1572
|
+
// openItems: Array.from(openItems),
|
|
1573
|
+
// previouslyOpen: Array.from(previouslyOpen)
|
|
1574
|
+
// });
|
|
1556
1575
|
}
|
|
1557
1576
|
|
|
1558
1577
|
/** Returns the index of a content item by its id, or -1 if not found */
|
|
@@ -1617,25 +1636,25 @@
|
|
|
1617
1636
|
* Controls input focus behavior and tab switching
|
|
1618
1637
|
*/
|
|
1619
1638
|
function focusController(controllerId: string) {
|
|
1620
|
-
console.log('Attempting to focus controller:', controllerId);
|
|
1639
|
+
// console.log('Attempting to focus controller:', controllerId);
|
|
1621
1640
|
activeControllerId = controllerId;
|
|
1622
1641
|
const element = document.getElementById(controllerId);
|
|
1623
1642
|
if (element) {
|
|
1624
1643
|
const componentInstance = element.querySelector(':scope > *');
|
|
1625
1644
|
if (componentInstance && 'focus' in componentInstance) {
|
|
1626
|
-
console.log('Calling focus method on component');
|
|
1645
|
+
// console.log('Calling focus method on component');
|
|
1627
1646
|
(componentInstance as any).focus();
|
|
1628
1647
|
} else {
|
|
1629
1648
|
const input = element.querySelector('input, textarea, [role="slider"], button');
|
|
1630
1649
|
if (input instanceof HTMLElement) {
|
|
1631
|
-
console.log('Found focusable element:', input);
|
|
1650
|
+
// console.log('Found focusable element:', input);
|
|
1632
1651
|
input.focus();
|
|
1633
1652
|
} else {
|
|
1634
|
-
console.log('No focusable element found in controller');
|
|
1653
|
+
// console.log('No focusable element found in controller');
|
|
1635
1654
|
}
|
|
1636
1655
|
}
|
|
1637
1656
|
} else {
|
|
1638
|
-
console.log('Controller element not found:', controllerId);
|
|
1657
|
+
// console.log('Controller element not found:', controllerId);
|
|
1639
1658
|
}
|
|
1640
1659
|
}
|
|
1641
1660
|
|
|
@@ -1660,7 +1679,7 @@
|
|
|
1660
1679
|
contentSchema.contentItems.forEach((item: ContentItem) => {
|
|
1661
1680
|
if (item.id) {
|
|
1662
1681
|
openItems.add(item.id);
|
|
1663
|
-
console.log("Added content item:", item.id);
|
|
1682
|
+
// console.log("Added content item:", item.id);
|
|
1664
1683
|
}
|
|
1665
1684
|
});
|
|
1666
1685
|
} else {
|
|
@@ -1712,74 +1731,75 @@
|
|
|
1712
1731
|
* and appends it to the content items list
|
|
1713
1732
|
*/
|
|
1714
1733
|
function formatDefaultItemTitle(itemNumber: number): string {
|
|
1715
|
-
|
|
1716
|
-
return `Item ${itemNumber}`;
|
|
1734
|
+
return formatNumberedTitle('Item', itemNumber);
|
|
1717
1735
|
}
|
|
1718
1736
|
|
|
1719
|
-
|
|
1720
|
-
|
|
1737
|
+
/** "Tile 1", "Photo 12", "Item 03" โ prefix used for the next auto title. */
|
|
1738
|
+
function getNumberedTitlePrefix(title: string | undefined): string | null {
|
|
1739
|
+
if (!title?.trim()) return null;
|
|
1740
|
+
const match = title.trim().match(/^(.+?)\s+(\d+)$/);
|
|
1741
|
+
return match?.[1] ?? null;
|
|
1721
1742
|
}
|
|
1722
1743
|
|
|
1723
|
-
function
|
|
1724
|
-
return `Photo ${photoNumber}`;
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
function isAutoGeneratedItemTitle(title: string | undefined): boolean {
|
|
1728
|
-
if (!title?.trim()) return true;
|
|
1729
|
-
const trimmed = title.trim();
|
|
1744
|
+
function isKnownGenericAutoTitle(title: string): boolean {
|
|
1730
1745
|
return (
|
|
1731
|
-
/^Item 0?\d+$/i.test(
|
|
1732
|
-
/^Slide \d+$/i.test(
|
|
1733
|
-
/^Photo \d+$/i.test(
|
|
1746
|
+
/^Item 0?\d+$/i.test(title) ||
|
|
1747
|
+
/^Slide \d+$/i.test(title) ||
|
|
1748
|
+
/^Photo \d+$/i.test(title) ||
|
|
1749
|
+
/^Image \d+$/i.test(title)
|
|
1734
1750
|
);
|
|
1735
1751
|
}
|
|
1736
1752
|
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1753
|
+
/** Most common "Name N" prefix in the list (Tile / Photo / Slide / Item / โฆ). */
|
|
1754
|
+
function detectDominantTitlePrefix(items: ContentItem[]): string {
|
|
1755
|
+
const counts = new Map<string, number>();
|
|
1756
|
+
for (const item of items) {
|
|
1757
|
+
const prefix = getNumberedTitlePrefix(item.title);
|
|
1758
|
+
if (!prefix) continue;
|
|
1759
|
+
counts.set(prefix, (counts.get(prefix) || 0) + 1);
|
|
1760
|
+
}
|
|
1761
|
+
let bestPrefix = 'Item';
|
|
1762
|
+
let bestCount = 0;
|
|
1763
|
+
for (const [prefix, count] of counts) {
|
|
1764
|
+
if (count > bestCount) {
|
|
1765
|
+
bestPrefix = prefix;
|
|
1766
|
+
bestCount = count;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
return bestPrefix;
|
|
1743
1770
|
}
|
|
1744
1771
|
|
|
1745
|
-
function
|
|
1746
|
-
if (
|
|
1747
|
-
|
|
1748
|
-
return formatDefaultItemTitle(position);
|
|
1772
|
+
function formatNumberedTitle(prefix: string, itemNumber: number): string {
|
|
1773
|
+
if (/^item$/i.test(prefix) && itemNumber < 10) return `Item 0${itemNumber}`;
|
|
1774
|
+
return `${prefix} ${itemNumber}`;
|
|
1749
1775
|
}
|
|
1750
1776
|
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
for (const item of items) {
|
|
1760
|
-
const title = item.title?.trim() || '';
|
|
1761
|
-
if (/^Photo \d+$/i.test(title)) photoCount++;
|
|
1762
|
-
else if (/^Slide \d+$/i.test(title)) slideCount++;
|
|
1763
|
-
else if (/^Item 0?\d+$/i.test(title) || !title) itemCount++;
|
|
1777
|
+
function isAutoGeneratedItemTitle(title: string | undefined, items?: ContentItem[]): boolean {
|
|
1778
|
+
if (!title?.trim()) return true;
|
|
1779
|
+
const trimmed = title.trim();
|
|
1780
|
+
if (isKnownGenericAutoTitle(trimmed)) return true;
|
|
1781
|
+
if (items && items.length) {
|
|
1782
|
+
const prefix = getNumberedTitlePrefix(trimmed);
|
|
1783
|
+
const dominant = detectDominantTitlePrefix(items);
|
|
1784
|
+
return !!prefix && prefix.toLowerCase() === dominant.toLowerCase();
|
|
1764
1785
|
}
|
|
1786
|
+
return false;
|
|
1787
|
+
}
|
|
1765
1788
|
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
return
|
|
1789
|
+
function formatAutoItemTitle(title: string | undefined, position: number): string {
|
|
1790
|
+
const prefix = getNumberedTitlePrefix(title);
|
|
1791
|
+
if (prefix) return formatNumberedTitle(prefix, position);
|
|
1792
|
+
return formatDefaultItemTitle(position);
|
|
1769
1793
|
}
|
|
1770
1794
|
|
|
1771
1795
|
function formatNewItemTitle(items: ContentItem[]): string {
|
|
1772
|
-
|
|
1773
|
-
const pattern = detectDominantAutoTitlePattern(items);
|
|
1774
|
-
if (pattern === 'photo') return formatPhotoTitle(nextNumber);
|
|
1775
|
-
if (pattern === 'slide') return formatSlideTitle(nextNumber);
|
|
1776
|
-
return formatDefaultItemTitle(nextNumber);
|
|
1796
|
+
return formatNumberedTitle(detectDominantTitlePrefix(items), items.length + 1);
|
|
1777
1797
|
}
|
|
1778
1798
|
|
|
1779
1799
|
function renumberAutoItemTitles(items: ContentItem[]): ContentItem[] {
|
|
1780
1800
|
return items.map((item, index) => {
|
|
1781
1801
|
const position = index + 1;
|
|
1782
|
-
if (!isAutoGeneratedItemTitle(item.title)) {
|
|
1802
|
+
if (!isAutoGeneratedItemTitle(item.title, items)) {
|
|
1783
1803
|
return { ...item, orderNumber: position };
|
|
1784
1804
|
}
|
|
1785
1805
|
const nextTitle = formatAutoItemTitle(item.title, position);
|
|
@@ -1801,10 +1821,11 @@
|
|
|
1801
1821
|
}
|
|
1802
1822
|
}
|
|
1803
1823
|
}
|
|
1804
|
-
|
|
1824
|
+
const itemList = Array.isArray(contentSchema?.contentItems) ? contentSchema.contentItems : [];
|
|
1825
|
+
if (item.title && item.title.trim() && !isAutoGeneratedItemTitle(item.title, itemList)) {
|
|
1805
1826
|
return item.title;
|
|
1806
1827
|
}
|
|
1807
|
-
if (isAutoGeneratedItemTitle(item.title)) {
|
|
1828
|
+
if (isAutoGeneratedItemTitle(item.title, itemList)) {
|
|
1808
1829
|
return formatAutoItemTitle(item.title, index + 1);
|
|
1809
1830
|
}
|
|
1810
1831
|
return formatDefaultItemTitle(index + 1);
|
|
@@ -1860,7 +1881,7 @@
|
|
|
1860
1881
|
return;
|
|
1861
1882
|
}
|
|
1862
1883
|
// Log before add
|
|
1863
|
-
console.log('[Add] Before:', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
1884
|
+
// console.log('[Add] Before:', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
1864
1885
|
// Generate a unique ID for the new item
|
|
1865
1886
|
let uniqueId: string;
|
|
1866
1887
|
do {
|
|
@@ -1868,13 +1889,20 @@
|
|
|
1868
1889
|
} while (contentSchema.contentItems.some((item: ContentItem) => item.id === uniqueId));
|
|
1869
1890
|
const newTitle = formatNewItemTitle(contentSchema.contentItems);
|
|
1870
1891
|
// Create the new item (copy template or last item structure)
|
|
1871
|
-
const template =
|
|
1892
|
+
const template =
|
|
1893
|
+
(Array.isArray(contentSchema.contentItems) &&
|
|
1894
|
+
contentSchema.contentItems.find((item: ContentItem) => Array.isArray(item.properties) && item.properties.length > 0)) ||
|
|
1895
|
+
itemTemplate;
|
|
1896
|
+
if (!template || !Array.isArray(template.properties) || template.properties.length === 0) {
|
|
1897
|
+
console.warn('[Add] Cannot add item: template has no properties');
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1872
1900
|
const newItem: ContentItem = {
|
|
1873
1901
|
...template,
|
|
1874
1902
|
id: uniqueId,
|
|
1875
1903
|
title: newTitle,
|
|
1876
1904
|
orderNumber: contentSchema.contentItems.length + 1,
|
|
1877
|
-
properties: template.properties
|
|
1905
|
+
properties: cloneProperties(template.properties),
|
|
1878
1906
|
};
|
|
1879
1907
|
// Remove the new item if it exists elsewhere (shouldn't, but for safety)
|
|
1880
1908
|
const nextItems = [...contentSchema.contentItems.filter((i: ContentItem) => i.id !== uniqueId), newItem];
|
|
@@ -1896,8 +1924,8 @@
|
|
|
1896
1924
|
dispatch('goToItem', { index: newItemIndex });
|
|
1897
1925
|
}
|
|
1898
1926
|
// Log after add
|
|
1899
|
-
console.log('[Add] After (should be at end):', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
1900
|
-
console.log('[Add] New item added:', newItem);
|
|
1927
|
+
// console.log('[Add] After (should be at end):', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
1928
|
+
// console.log('[Add] New item added:', newItem);
|
|
1901
1929
|
|
|
1902
1930
|
// Ensure DOM order matches array order after adding new item
|
|
1903
1931
|
setTimeout(() => {
|
|
@@ -1934,20 +1962,47 @@
|
|
|
1934
1962
|
}
|
|
1935
1963
|
}
|
|
1936
1964
|
|
|
1937
|
-
|
|
1965
|
+
function unwrapContentMediaValue(raw: unknown): unknown {
|
|
1966
|
+
if (raw && typeof raw === 'object' && 'url' in (raw as Record<string, unknown>)) {
|
|
1967
|
+
return (raw as { url?: string }).url ?? '';
|
|
1968
|
+
}
|
|
1969
|
+
return raw;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
function pickLiveContentValue(...candidates: unknown[]): unknown {
|
|
1973
|
+
for (const candidate of candidates) {
|
|
1974
|
+
const unwrapped = unwrapContentMediaValue(candidate);
|
|
1975
|
+
if (unwrapped === undefined || unwrapped === null || unwrapped === '') continue;
|
|
1976
|
+
return unwrapped;
|
|
1977
|
+
}
|
|
1978
|
+
return '';
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
function emptyValueForNewItemProperty(prop: any): unknown {
|
|
1982
|
+
const icType = String(prop?.inputController?.type || '').toLowerCase();
|
|
1983
|
+
if (['browser', 'image', 'file', 'video', 'audio'].includes(icType)) {
|
|
1984
|
+
return '';
|
|
1985
|
+
}
|
|
1986
|
+
if (typeof prop?.defaultValue === 'boolean' || typeof prop?.defaultValue === 'number') {
|
|
1987
|
+
return prop.defaultValue;
|
|
1988
|
+
}
|
|
1989
|
+
return '';
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
// Clone controller schema for a new item. Do not copy Unsplash/sample defaults
|
|
1993
|
+
// into value โ those overwrite live gallery URLs in the preview overlay.
|
|
1938
1994
|
function cloneProperties(properties: Array<any>): Property[] {
|
|
1939
1995
|
if (!properties || !Array.isArray(properties)) {
|
|
1940
1996
|
return [];
|
|
1941
1997
|
}
|
|
1942
1998
|
|
|
1943
1999
|
return properties.map(prop => {
|
|
1944
|
-
// Create a deep clone of each property
|
|
1945
2000
|
const clonedProp: Property = {
|
|
1946
2001
|
id: prop.id || prop.name,
|
|
1947
2002
|
name: prop.name || prop.id,
|
|
1948
2003
|
type: prop.type || 'string', // Ensure type is set
|
|
1949
2004
|
defaultValue: prop.defaultValue,
|
|
1950
|
-
value: prop
|
|
2005
|
+
value: emptyValueForNewItemProperty(prop),
|
|
1951
2006
|
inputController: {
|
|
1952
2007
|
type: prop.inputController?.type || 'text', // Ensure inputController.type is set
|
|
1953
2008
|
description: prop.inputController?.description,
|
|
@@ -1979,8 +2034,13 @@
|
|
|
1979
2034
|
const key = p.id || p.name;
|
|
1980
2035
|
// Canonical state first; schema mirror/defaultValue are legacy
|
|
1981
2036
|
// seeds for keys the editor never touched.
|
|
2037
|
+
const liveOnItem = key != null ? (item as Record<string, unknown>)[key] : undefined;
|
|
1982
2038
|
props[key] = normalizeContentPropertyValue(
|
|
1983
|
-
|
|
2039
|
+
pickLiveContentValue(
|
|
2040
|
+
contentState[`${item.id}__${key}`],
|
|
2041
|
+
p.value,
|
|
2042
|
+
liveOnItem
|
|
2043
|
+
)
|
|
1984
2044
|
);
|
|
1985
2045
|
});
|
|
1986
2046
|
// Carry opacity state so parent saves don't lose per-property
|
|
@@ -1995,7 +2055,7 @@
|
|
|
1995
2055
|
}
|
|
1996
2056
|
return { id: item.id, title: item.title, ...props };
|
|
1997
2057
|
});
|
|
1998
|
-
console.log('[PropsEditor] Dispatching contentItemsChanged:', serialized.length, 'items');
|
|
2058
|
+
// console.log('[PropsEditor] Dispatching contentItemsChanged:', serialized.length, 'items');
|
|
1999
2059
|
dispatch('contentItemsChanged', { items: serialized });
|
|
2000
2060
|
}
|
|
2001
2061
|
|
|
@@ -2012,7 +2072,7 @@
|
|
|
2012
2072
|
}
|
|
2013
2073
|
|
|
2014
2074
|
// Log before deletion
|
|
2015
|
-
console.log('[Delete] Before:', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
2075
|
+
// console.log('[Delete] Before:', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
2016
2076
|
const deletedIndex = contentSchema.contentItems.findIndex((i: ContentItem) => i.id === itemId);
|
|
2017
2077
|
const initialLength = contentSchema.contentItems.length;
|
|
2018
2078
|
contentSchema.contentItems = contentSchema.contentItems.filter((i: ContentItem) => i.id !== itemId);
|
|
@@ -2024,7 +2084,7 @@
|
|
|
2024
2084
|
console.warn('๐ค No item found with ID:', itemId);
|
|
2025
2085
|
} else {
|
|
2026
2086
|
// Log after deletion
|
|
2027
|
-
console.log('[Delete] After:', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
2087
|
+
// console.log('[Delete] After:', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
2028
2088
|
// Notify parent about the updated items list
|
|
2029
2089
|
dispatch('updated', { contentSchema });
|
|
2030
2090
|
dispatchContentItemsChanged(contentSchema.contentItems);
|
|
@@ -2034,7 +2094,7 @@
|
|
|
2034
2094
|
dispatch('goToItem', { index: targetIndex });
|
|
2035
2095
|
}
|
|
2036
2096
|
setTimeout(() => {
|
|
2037
|
-
console.log('๐ Reinitializing draggable after deletion');
|
|
2097
|
+
// console.log('๐ Reinitializing draggable after deletion');
|
|
2038
2098
|
initializeDraggable();
|
|
2039
2099
|
}, 0);
|
|
2040
2100
|
}
|
|
@@ -2099,13 +2159,13 @@
|
|
|
2099
2159
|
// Initialize the correct tab based on schema availability
|
|
2100
2160
|
const initialTab = getActiveTab();
|
|
2101
2161
|
if (initialTab === 'content' && contentSchema) {
|
|
2102
|
-
console.log('๐ฏ Initializing content tab as default');
|
|
2162
|
+
// console.log('๐ฏ Initializing content tab as default');
|
|
2103
2163
|
currentTab = 'content';
|
|
2104
2164
|
|
|
2105
2165
|
// Wait for DOM to be ready
|
|
2106
2166
|
setTimeout(async () => {
|
|
2107
2167
|
try {
|
|
2108
|
-
console.log('๐ Initializing draggable for content tab');
|
|
2168
|
+
// console.log('๐ Initializing draggable for content tab');
|
|
2109
2169
|
await initializeDraggable();
|
|
2110
2170
|
|
|
2111
2171
|
// Set initial states
|
|
@@ -2180,7 +2240,7 @@
|
|
|
2180
2240
|
const newTab = getActiveTab();
|
|
2181
2241
|
if (defaultTab !== newTab) {
|
|
2182
2242
|
defaultTab = newTab;
|
|
2183
|
-
console.log('๐ [PropsEditor] Updated defaultTab:', { from: defaultTab, to: newTab, hasDesignSchema: !!designSchema, hasContentSchema: !!contentSchema });
|
|
2243
|
+
// console.log('๐ [PropsEditor] Updated defaultTab:', { from: defaultTab, to: newTab, hasDesignSchema: !!designSchema, hasContentSchema: !!contentSchema });
|
|
2184
2244
|
}
|
|
2185
2245
|
}
|
|
2186
2246
|
|
|
@@ -2252,7 +2312,7 @@
|
|
|
2252
2312
|
|
|
2253
2313
|
// Add a reactive log after render to show the current array order and titles
|
|
2254
2314
|
$: if (contentSchema && Array.isArray(contentSchema?.contentItems)) {
|
|
2255
|
-
console.log('[Render] Current contentItems order:', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
2315
|
+
// console.log('[Render] Current contentItems order:', contentSchema.contentItems.map((i: ContentItem) => ({ id: i.id, title: i.title })));
|
|
2256
2316
|
}
|
|
2257
2317
|
|
|
2258
2318
|
/** Safe accessor โ always returns an array even when contentSchema.contentItems is missing or not an array */
|
|
@@ -2268,7 +2328,7 @@
|
|
|
2268
2328
|
// Helper: log DOM visual order for debugging
|
|
2269
2329
|
function logOrder(label: string) {
|
|
2270
2330
|
const titles = Array.from(document.querySelectorAll('.content-item .content-title')).map(el => el.textContent?.trim());
|
|
2271
|
-
console.log(`[Order-check] ${label}:`, titles);
|
|
2331
|
+
// console.log(`[Order-check] ${label}:`, titles);
|
|
2272
2332
|
const transforms = Array.from(document.querySelectorAll('.content-item')).map(el => ({ id: (el as HTMLElement).dataset.id, transform: (el as HTMLElement).style.transform }));
|
|
2273
2333
|
console.table(transforms);
|
|
2274
2334
|
}
|
|
@@ -2337,9 +2397,9 @@
|
|
|
2337
2397
|
>
|
|
2338
2398
|
<Tabs.Trigger
|
|
2339
2399
|
value="design"
|
|
2340
|
-
class="
|
|
2400
|
+
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"
|
|
2341
2401
|
>
|
|
2342
|
-
{designTabTitle}
|
|
2402
|
+
<span class="body">{designTabTitle}</span>
|
|
2343
2403
|
</Tabs.Trigger>
|
|
2344
2404
|
{#if contentSchema}
|
|
2345
2405
|
<Tabs.Trigger
|
|
@@ -2347,9 +2407,9 @@
|
|
|
2347
2407
|
initializeDraggable();
|
|
2348
2408
|
}}
|
|
2349
2409
|
value="content"
|
|
2350
|
-
class="
|
|
2410
|
+
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"
|
|
2351
2411
|
>
|
|
2352
|
-
{contentTabTitle}
|
|
2412
|
+
<span class="body">{contentTabTitle}</span>
|
|
2353
2413
|
</Tabs.Trigger>
|
|
2354
2414
|
{/if}
|
|
2355
2415
|
</Tabs.List>
|
|
@@ -2415,24 +2475,27 @@
|
|
|
2415
2475
|
{@const reactiveValue = designState[stateKey]}
|
|
2416
2476
|
|
|
2417
2477
|
{#if isVisible}
|
|
2478
|
+
{@const isFontIc = property.inputController.type.toLowerCase() === 'font'}
|
|
2418
2479
|
<div
|
|
2419
|
-
class="ictrl input-controller flex items-center justify-between w-full p-0"
|
|
2480
|
+
class="ictrl input-controller flex {isFontIc ? 'font-ic-host flex-col items-stretch' : 'items-center justify-between'} w-full p-0"
|
|
2420
2481
|
data-state={$openDropdowns?.has(props.controllerId) ? 'open' : 'closed'}
|
|
2421
|
-
class:double-height={
|
|
2482
|
+
class:double-height={isFontIc}
|
|
2422
2483
|
class:position-height={property.inputController.type.toLowerCase() === 'position'}
|
|
2423
2484
|
class:fixed-position-height={property.inputController.type.toLowerCase() === 'fixedposition'}
|
|
2424
2485
|
class:text-area-preview-height={property.inputController.type.toLowerCase() === 'textarea' || property.inputController.type.toLowerCase() === 'textArea'}
|
|
2425
2486
|
class:position-controller={property.inputController.type.toLowerCase() === 'position'}
|
|
2426
2487
|
class:position-dragging={property.inputController.type.toLowerCase() === 'position' && isDragging}
|
|
2427
2488
|
>
|
|
2489
|
+
{#if !isFontIc}
|
|
2428
2490
|
<div class="ic-header-ct flex-1 min-w-0">
|
|
2429
2491
|
<InputControllerHeader
|
|
2430
2492
|
label={header.label}
|
|
2431
2493
|
helpText={header.helpText}
|
|
2432
2494
|
/>
|
|
2433
2495
|
</div>
|
|
2496
|
+
{/if}
|
|
2434
2497
|
|
|
2435
|
-
<div class="ic-input-ct flex-shrink-0 ml-auto" style=
|
|
2498
|
+
<div class="ic-input-ct {isFontIc ? 'w-full' : 'flex-shrink-0 ml-auto'}" style={isFontIc ? '' : 'width: 185px;'}>
|
|
2436
2499
|
<svelte:component
|
|
2437
2500
|
this={InputComponent}
|
|
2438
2501
|
{...props}
|
|
@@ -2509,6 +2572,7 @@
|
|
|
2509
2572
|
{/if}
|
|
2510
2573
|
|
|
2511
2574
|
<Collapsible.Content>
|
|
2575
|
+
{#if isGenericContentOpen}
|
|
2512
2576
|
<Card.Content class="p-0 px-4">
|
|
2513
2577
|
<div class="pt-[14px] pb-[16px]">
|
|
2514
2578
|
<div class="properties-list flex flex-col" style="gap: 8px;">
|
|
@@ -2525,17 +2589,16 @@
|
|
|
2525
2589
|
{@const isVisible = shouldShowProperty(property, contentSchema?.genericContent?.properties || [], 'content')}
|
|
2526
2590
|
|
|
2527
2591
|
{#if isVisible}
|
|
2528
|
-
|
|
2529
|
-
|
|
2592
|
+
{@const isFontIc = property.inputController.type.toLowerCase() === 'font'}
|
|
2593
|
+
<div class="ictrl input-controller flex {isFontIc ? 'font-ic-host flex-col items-stretch' : 'items-center justify-between'} w-full p-0 browser-input-wrapper"
|
|
2594
|
+
class:double-height={isFontIc}
|
|
2530
2595
|
>
|
|
2531
|
-
|
|
2532
|
-
{#if InputComponent !== BrowserInputController}
|
|
2596
|
+
{#if !isFontIc && InputComponent !== BrowserInputController}
|
|
2533
2597
|
<InputControllerHeader
|
|
2534
2598
|
label={header.label}
|
|
2535
2599
|
helpText={header.helpText}
|
|
2536
2600
|
/>
|
|
2537
|
-
{:else}
|
|
2538
|
-
<!-- Special case for BrowserInputController -->
|
|
2601
|
+
{:else if !isFontIc}
|
|
2539
2602
|
<div class="flex-1 min-w-0 pr-4">
|
|
2540
2603
|
<InputControllerHeader
|
|
2541
2604
|
label={header.label}
|
|
@@ -2544,8 +2607,7 @@
|
|
|
2544
2607
|
</div>
|
|
2545
2608
|
{/if}
|
|
2546
2609
|
|
|
2547
|
-
|
|
2548
|
-
<div class="ic-input-ct flex-shrink-0" style="width: 185px;">
|
|
2610
|
+
<div class="ic-input-ct {isFontIc ? 'w-full' : 'flex-shrink-0'}" style={isFontIc ? '' : 'width: 185px;'}>
|
|
2549
2611
|
<svelte:component
|
|
2550
2612
|
this={InputComponent}
|
|
2551
2613
|
{...props}
|
|
@@ -2559,6 +2621,7 @@
|
|
|
2559
2621
|
</div>
|
|
2560
2622
|
</div>
|
|
2561
2623
|
</Card.Content>
|
|
2624
|
+
{/if}
|
|
2562
2625
|
</Collapsible.Content>
|
|
2563
2626
|
</Collapsible.Root>
|
|
2564
2627
|
</Card.Root>
|
|
@@ -2609,6 +2672,7 @@
|
|
|
2609
2672
|
{/if}
|
|
2610
2673
|
|
|
2611
2674
|
<Collapsible.Content>
|
|
2675
|
+
{#if isItemOpen(item.id)}
|
|
2612
2676
|
<Card.Content class="p-0 px-4">
|
|
2613
2677
|
<div class="pt-[14px] pb-[16px]">
|
|
2614
2678
|
<div class="properties-list flex flex-col" style="gap: 8px;">
|
|
@@ -2625,18 +2689,17 @@
|
|
|
2625
2689
|
{@const header = result.header}
|
|
2626
2690
|
{@const isVisible = shouldShowProperty(property, item.properties, 'content')}
|
|
2627
2691
|
|
|
2628
|
-
{#if isVisible}
|
|
2629
|
-
|
|
2630
|
-
|
|
2692
|
+
{#if isVisible && InputComponent}
|
|
2693
|
+
{@const isFontIc = (property.inputController?.type || '').toLowerCase() === 'font'}
|
|
2694
|
+
<div class="ictrl input-controller flex {isFontIc ? 'font-ic-host flex-col items-stretch' : 'items-center justify-between'} w-full p-0 browser-input-wrapper"
|
|
2695
|
+
class:double-height={isFontIc}
|
|
2631
2696
|
>
|
|
2632
|
-
|
|
2633
|
-
{#if InputComponent !== BrowserInputController}
|
|
2697
|
+
{#if !isFontIc && InputComponent !== BrowserInputController}
|
|
2634
2698
|
<InputControllerHeader
|
|
2635
2699
|
label={header.label}
|
|
2636
2700
|
helpText={header.helpText}
|
|
2637
2701
|
/>
|
|
2638
|
-
{:else}
|
|
2639
|
-
<!-- Special case for BrowserInputController -->
|
|
2702
|
+
{:else if !isFontIc}
|
|
2640
2703
|
<div class="flex-1 min-w-0 pr-4">
|
|
2641
2704
|
<InputControllerHeader
|
|
2642
2705
|
label={header.label}
|
|
@@ -2645,8 +2708,7 @@
|
|
|
2645
2708
|
</div>
|
|
2646
2709
|
{/if}
|
|
2647
2710
|
|
|
2648
|
-
|
|
2649
|
-
<div class="ic-input-ct flex-shrink-0" style="width: 185px;">
|
|
2711
|
+
<div class="ic-input-ct {isFontIc ? 'w-full' : 'flex-shrink-0'}" style={isFontIc ? '' : 'width: 185px;'}>
|
|
2650
2712
|
<svelte:component
|
|
2651
2713
|
this={InputComponent}
|
|
2652
2714
|
{...props}
|
|
@@ -2690,6 +2752,7 @@
|
|
|
2690
2752
|
</div>
|
|
2691
2753
|
</div>
|
|
2692
2754
|
</Card.Content>
|
|
2755
|
+
{/if}
|
|
2693
2756
|
</Collapsible.Content>
|
|
2694
2757
|
</Collapsible.Root>
|
|
2695
2758
|
</Card.Root>
|
|
@@ -3283,6 +3346,23 @@
|
|
|
3283
3346
|
flex-shrink: 0 !important;
|
|
3284
3347
|
}
|
|
3285
3348
|
|
|
3349
|
+
/* FontIC is two stacked 185px rows with its own labels. Do not squeeze
|
|
3350
|
+
the whole controller into the single 185px color column (that hid labels). */
|
|
3351
|
+
:global(.input-controller.font-ic-host) {
|
|
3352
|
+
display: flex !important;
|
|
3353
|
+
flex-direction: column !important;
|
|
3354
|
+
align-items: stretch !important;
|
|
3355
|
+
justify-content: flex-start !important;
|
|
3356
|
+
}
|
|
3357
|
+
|
|
3358
|
+
:global(.input-controller.font-ic-host > div:first-child),
|
|
3359
|
+
:global(.input-controller.font-ic-host > div:last-child) {
|
|
3360
|
+
width: 100% !important;
|
|
3361
|
+
max-width: 100% !important;
|
|
3362
|
+
flex: none !important;
|
|
3363
|
+
margin-right: 0 !important;
|
|
3364
|
+
}
|
|
3365
|
+
|
|
3286
3366
|
/* New animation system */
|
|
3287
3367
|
:global(.accordion-content) {
|
|
3288
3368
|
overflow: hidden;
|
|
@@ -3370,7 +3450,9 @@
|
|
|
3370
3450
|
|
|
3371
3451
|
/* Specific style for font controllers */
|
|
3372
3452
|
:global(.input-controller.double-height) {
|
|
3373
|
-
height:
|
|
3453
|
+
height: auto !important;
|
|
3454
|
+
min-height: 104px;
|
|
3455
|
+
align-items: flex-start !important;
|
|
3374
3456
|
}
|
|
3375
3457
|
|
|
3376
3458
|
/* Ensure content containers maintain width */
|