@yourself.create/ngx-form-designer 0.0.4 → 0.0.6
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/fesm2022/uch-web-ngx-form-designer.mjs +2000 -1214
- package/fesm2022/uch-web-ngx-form-designer.mjs.map +1 -1
- package/lib/data/data-provider.d.ts +9 -0
- package/lib/form-core/models.d.ts +3 -13
- package/lib/form-designer/data-panel/data-panel.component.d.ts +4 -2
- package/lib/form-designer/designer-state.service.d.ts +34 -0
- package/lib/form-designer/dynamic-properties/dynamic-properties.component.d.ts +3 -5
- package/lib/form-designer/inspector-sections/inspector-transform-section.component.d.ts +12 -0
- package/lib/form-designer/layout-canvas.component.d.ts +11 -1
- package/lib/form-designer/properties-panel.component.d.ts +4 -0
- package/lib/form-renderer/json-form-renderer.component.d.ts +27 -2
- package/lib/form-renderer/layout-node.component.d.ts +10 -0
- package/lib/widgets/field-widgets/select/select-widget.component.d.ts +12 -2
- package/lib/widgets/widget-definition.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { v4 } from 'uuid';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { Injectable, InjectionToken, NgModule, inject, signal, computed, EventEmitter, DestroyRef, Injector, afterNextRender, ViewContainerRef, Input, ViewChild, Output, Inject, ChangeDetectionStrategy, Component, effect, ElementRef, NgZone, input, output, HostListener,
|
|
3
|
+
import { Injectable, InjectionToken, NgModule, inject, signal, computed, EventEmitter, DestroyRef, Injector, afterNextRender, ViewContainerRef, Input, ViewChild, Output, Inject, ChangeDetectionStrategy, Component, effect, ElementRef, NgZone, input, output, HostListener, untracked, ChangeDetectorRef, Pipe, ContentChildren } from '@angular/core';
|
|
4
4
|
import { BehaviorSubject, Subject, merge, of, filter, map, debounceTime as debounceTime$1, skip, firstValueFrom } from 'rxjs';
|
|
5
5
|
import * as i1 from '@angular/common';
|
|
6
6
|
import { CommonModule, DOCUMENT } from '@angular/common';
|
|
@@ -491,18 +491,9 @@ class FormEngine {
|
|
|
491
491
|
const field = this.getFieldById(fieldId);
|
|
492
492
|
if (!field)
|
|
493
493
|
return false;
|
|
494
|
-
// 1. Legacy
|
|
495
|
-
if (field.conditionalVisibility) {
|
|
496
|
-
const cv = field.conditionalVisibility;
|
|
497
|
-
const val = this.values[cv.fieldName];
|
|
498
|
-
if (cv.operator === 'equals' && val !== cv.value)
|
|
499
|
-
return false;
|
|
500
|
-
if (cv.operator === 'notEquals' && val === cv.value)
|
|
501
|
-
return false;
|
|
502
|
-
}
|
|
503
|
-
// 2. Dependencies (Legacy)
|
|
494
|
+
// 1. Dependencies (Legacy)
|
|
504
495
|
let visible = this.evaluateDependencyRules(field, 'show', 'hide', true);
|
|
505
|
-
//
|
|
496
|
+
// 2. Enterprise Rules
|
|
506
497
|
visible = this.evaluateEnterpriseRules(field, 'visible', 'hidden', visible);
|
|
507
498
|
return visible;
|
|
508
499
|
}
|
|
@@ -823,6 +814,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
823
814
|
}]
|
|
824
815
|
}] });
|
|
825
816
|
|
|
817
|
+
const TRANSFORM_CONTROL_KEYS = new Set([
|
|
818
|
+
'transformX',
|
|
819
|
+
'transformY',
|
|
820
|
+
'transformZ',
|
|
821
|
+
'rotate',
|
|
822
|
+
'scale'
|
|
823
|
+
]);
|
|
826
824
|
const WRAPPER_SURFACE_STYLE_KEYS = new Set([
|
|
827
825
|
'backgroundColor',
|
|
828
826
|
'borderColor',
|
|
@@ -860,10 +858,13 @@ function normalizeStyle$1(style) {
|
|
|
860
858
|
if (!style)
|
|
861
859
|
return {};
|
|
862
860
|
const result = {};
|
|
861
|
+
const transform = buildTransform(style);
|
|
863
862
|
Object.keys(style).forEach(key => {
|
|
864
863
|
const value = style[key];
|
|
865
864
|
if (value === undefined || value === null || value === '')
|
|
866
865
|
return;
|
|
866
|
+
if (TRANSFORM_CONTROL_KEYS.has(key))
|
|
867
|
+
return;
|
|
867
868
|
// 1. Check for Spacing Tokens (padding*, margin*, gap)
|
|
868
869
|
// Only map known tokens to avoid accidental string matching
|
|
869
870
|
const isSpacing = /^(padding|margin|gap)/i.test(key);
|
|
@@ -892,6 +893,10 @@ function normalizeStyle$1(style) {
|
|
|
892
893
|
}
|
|
893
894
|
result[key] = value;
|
|
894
895
|
});
|
|
896
|
+
if (transform) {
|
|
897
|
+
const existingTransform = typeof result['transform'] === 'string' ? result['transform'].trim() : '';
|
|
898
|
+
result['transform'] = existingTransform ? `${existingTransform} ${transform}` : transform;
|
|
899
|
+
}
|
|
895
900
|
return result;
|
|
896
901
|
}
|
|
897
902
|
function mergeAndNormalize(base, override) {
|
|
@@ -930,6 +935,75 @@ function hasWrapperSurfaceStyles(style) {
|
|
|
930
935
|
}
|
|
931
936
|
return Object.keys(style).some(key => WRAPPER_SURFACE_STYLE_KEYS.has(key));
|
|
932
937
|
}
|
|
938
|
+
function buildTransform(style) {
|
|
939
|
+
const transforms = [];
|
|
940
|
+
const translateX = normalizeLength(style['transformX']);
|
|
941
|
+
const translateY = normalizeLength(style['transformY']);
|
|
942
|
+
const translateZ = normalizeLength(style['transformZ']);
|
|
943
|
+
const rotate = normalizeAngle(style['rotate']);
|
|
944
|
+
const scale = normalizeScale(style['scale']);
|
|
945
|
+
if (translateX || translateY || translateZ) {
|
|
946
|
+
transforms.push(`translate3d(${translateX ?? '0px'}, ${translateY ?? '0px'}, ${translateZ ?? '0px'})`);
|
|
947
|
+
}
|
|
948
|
+
if (rotate) {
|
|
949
|
+
transforms.push(`rotate(${rotate})`);
|
|
950
|
+
}
|
|
951
|
+
if (scale) {
|
|
952
|
+
transforms.push(`scale(${scale})`);
|
|
953
|
+
}
|
|
954
|
+
return transforms.join(' ');
|
|
955
|
+
}
|
|
956
|
+
function normalizeLength(value) {
|
|
957
|
+
if (value === undefined || value === null || value === '') {
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
961
|
+
return `${value}px`;
|
|
962
|
+
}
|
|
963
|
+
if (typeof value === 'string') {
|
|
964
|
+
const trimmed = value.trim();
|
|
965
|
+
if (!trimmed) {
|
|
966
|
+
return null;
|
|
967
|
+
}
|
|
968
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
969
|
+
return `${trimmed}px`;
|
|
970
|
+
}
|
|
971
|
+
return trimmed;
|
|
972
|
+
}
|
|
973
|
+
return null;
|
|
974
|
+
}
|
|
975
|
+
function normalizeAngle(value) {
|
|
976
|
+
if (value === undefined || value === null || value === '') {
|
|
977
|
+
return null;
|
|
978
|
+
}
|
|
979
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
980
|
+
return `${value}deg`;
|
|
981
|
+
}
|
|
982
|
+
if (typeof value === 'string') {
|
|
983
|
+
const trimmed = value.trim();
|
|
984
|
+
if (!trimmed) {
|
|
985
|
+
return null;
|
|
986
|
+
}
|
|
987
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
988
|
+
return `${trimmed}deg`;
|
|
989
|
+
}
|
|
990
|
+
return trimmed;
|
|
991
|
+
}
|
|
992
|
+
return null;
|
|
993
|
+
}
|
|
994
|
+
function normalizeScale(value) {
|
|
995
|
+
if (value === undefined || value === null || value === '') {
|
|
996
|
+
return null;
|
|
997
|
+
}
|
|
998
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
999
|
+
return String(value);
|
|
1000
|
+
}
|
|
1001
|
+
if (typeof value === 'string') {
|
|
1002
|
+
const trimmed = value.trim();
|
|
1003
|
+
return trimmed || null;
|
|
1004
|
+
}
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
933
1007
|
|
|
934
1008
|
const WIDGET_EDITOR_CONTEXT = new InjectionToken('WIDGET_EDITOR_CONTEXT');
|
|
935
1009
|
|
|
@@ -1023,6 +1097,7 @@ class DesignerStateService {
|
|
|
1023
1097
|
isReadOnly = signal(false);
|
|
1024
1098
|
// Structure of clipboard data
|
|
1025
1099
|
clipboard = signal(null);
|
|
1100
|
+
pendingFieldInsert = signal(null);
|
|
1026
1101
|
history = [];
|
|
1027
1102
|
historyIndex = signal(-1);
|
|
1028
1103
|
historyLength = signal(0);
|
|
@@ -1043,6 +1118,21 @@ class DesignerStateService {
|
|
|
1043
1118
|
const entry = this.layoutIndex()[id];
|
|
1044
1119
|
return entry?.node ?? null;
|
|
1045
1120
|
});
|
|
1121
|
+
selectedEntry = computed(() => {
|
|
1122
|
+
const id = this.selectedNodeId();
|
|
1123
|
+
if (!id)
|
|
1124
|
+
return null;
|
|
1125
|
+
return this.layoutIndex()[id] ?? null;
|
|
1126
|
+
});
|
|
1127
|
+
selectedColumnId = computed(() => this.findSelectionColumnEntry(this.selectedEntry())?.path.at(-1) ?? null);
|
|
1128
|
+
selectedRowId = computed(() => this.findSelectionRowEntry(this.selectedEntry())?.path.at(-1) ?? null);
|
|
1129
|
+
canInsertColumnBeforeSelection = computed(() => !!this.resolveColumnInsertTarget(this.selectedEntry()));
|
|
1130
|
+
canInsertColumnAfterSelection = computed(() => !!this.resolveColumnInsertTarget(this.selectedEntry()));
|
|
1131
|
+
canInsertRowInSelectedColumn = computed(() => !!this.findSelectionColumnEntry(this.selectedEntry()));
|
|
1132
|
+
canInsertRowBeforeSelection = computed(() => !!this.resolveRelativeRowInsertTarget(this.selectedEntry()));
|
|
1133
|
+
canInsertRowAfterSelection = computed(() => !!this.resolveRelativeRowInsertTarget(this.selectedEntry()));
|
|
1134
|
+
canArmFieldInsertBeforeSelection = computed(() => !!this.getSelectedFieldReference());
|
|
1135
|
+
canArmFieldInsertAfterSelection = computed(() => !!this.getSelectedFieldReference());
|
|
1046
1136
|
selectedField = computed(() => {
|
|
1047
1137
|
const selectedId = this.selectedNodeId();
|
|
1048
1138
|
if (!selectedId)
|
|
@@ -1091,6 +1181,7 @@ class DesignerStateService {
|
|
|
1091
1181
|
this.restoreSnapshot(nextIndex);
|
|
1092
1182
|
}
|
|
1093
1183
|
selectNode(id) {
|
|
1184
|
+
this.pendingFieldInsert.set(null);
|
|
1094
1185
|
if (!id) {
|
|
1095
1186
|
this.selectedNodeId.set(null);
|
|
1096
1187
|
this.selectedNodeIds.set([]);
|
|
@@ -1102,6 +1193,7 @@ class DesignerStateService {
|
|
|
1102
1193
|
this.closeContextMenu();
|
|
1103
1194
|
}
|
|
1104
1195
|
toggleNodeSelection(id) {
|
|
1196
|
+
this.pendingFieldInsert.set(null);
|
|
1105
1197
|
const current = this.selectedNodeIds();
|
|
1106
1198
|
const exists = current.includes(id);
|
|
1107
1199
|
const next = exists ? current.filter(item => item !== id) : [...current, id];
|
|
@@ -1116,6 +1208,14 @@ class DesignerStateService {
|
|
|
1116
1208
|
isNodeSelected(nodeId) {
|
|
1117
1209
|
return this.selectedNodeIds().includes(nodeId);
|
|
1118
1210
|
}
|
|
1211
|
+
isSelectionRowAncestor(nodeId) {
|
|
1212
|
+
const selectedId = this.selectedNodeId();
|
|
1213
|
+
return !!selectedId && selectedId !== nodeId && this.selectedRowId() === nodeId;
|
|
1214
|
+
}
|
|
1215
|
+
isSelectionColumnAncestor(nodeId) {
|
|
1216
|
+
const selectedId = this.selectedNodeId();
|
|
1217
|
+
return !!selectedId && selectedId !== nodeId && this.selectedColumnId() === nodeId;
|
|
1218
|
+
}
|
|
1119
1219
|
composeScopedNodeId(scopePath, nodeId) {
|
|
1120
1220
|
return composeScopedNodeId(scopePath, nodeId);
|
|
1121
1221
|
}
|
|
@@ -1364,6 +1464,7 @@ class DesignerStateService {
|
|
|
1364
1464
|
}
|
|
1365
1465
|
};
|
|
1366
1466
|
removeRecursive(scopeSchema.layout);
|
|
1467
|
+
this.pruneEmptyRows(scopeSchema.layout);
|
|
1367
1468
|
scopeSchema.fields = scopeSchema.fields.filter(field => !fieldsToRemove.has(field.id));
|
|
1368
1469
|
}
|
|
1369
1470
|
this.setSchema(nextSchema);
|
|
@@ -1559,20 +1660,67 @@ class DesignerStateService {
|
|
|
1559
1660
|
const selectedId = this.selectedNodeId();
|
|
1560
1661
|
const selectedEntry = selectedId ? this.layoutIndex()[selectedId] : undefined;
|
|
1561
1662
|
const insertionScopePath = this.resolveInsertionScopePath(selectedEntry);
|
|
1663
|
+
const pendingFieldInsert = this.pendingFieldInsert();
|
|
1664
|
+
const canInsertRelativeToSelectedWidget = !!selectedEntry
|
|
1665
|
+
&& selectedEntry.node.type === 'widget'
|
|
1666
|
+
&& this.sameScope(selectedEntry.scopePath, insertionScopePath)
|
|
1667
|
+
&& !!selectedEntry.node.refId;
|
|
1562
1668
|
const nextSchema = this.cloneValue(current);
|
|
1563
1669
|
const targetSchema = this.resolveSchemaAtScope(nextSchema, insertionScopePath);
|
|
1564
1670
|
if (!targetSchema)
|
|
1565
1671
|
return;
|
|
1566
|
-
const targetCol = this.resolveTargetColumnForFieldInsert(targetSchema, selectedEntry, insertionScopePath);
|
|
1567
|
-
if (!targetCol)
|
|
1568
|
-
return;
|
|
1569
1672
|
const inserted = this.createInsertedWidgets(widgetDef, widgetDef.type);
|
|
1570
|
-
|
|
1571
|
-
|
|
1673
|
+
const targetedInsert = pendingFieldInsert
|
|
1674
|
+
? this.resolveProgrammaticFieldInsertTarget(nextSchema, {
|
|
1675
|
+
referenceFieldId: pendingFieldInsert.referenceFieldId,
|
|
1676
|
+
position: pendingFieldInsert.position
|
|
1677
|
+
})
|
|
1678
|
+
: canInsertRelativeToSelectedWidget
|
|
1679
|
+
? this.resolveProgrammaticFieldInsertTarget(nextSchema, {
|
|
1680
|
+
referenceFieldId: selectedEntry.node.refId,
|
|
1681
|
+
position: 'after'
|
|
1682
|
+
})
|
|
1683
|
+
: null;
|
|
1684
|
+
if (targetedInsert) {
|
|
1685
|
+
targetedInsert.schema.fields.push(...inserted.fields);
|
|
1686
|
+
targetedInsert.column.children.splice(targetedInsert.index, 0, ...inserted.nodes);
|
|
1687
|
+
}
|
|
1688
|
+
else {
|
|
1689
|
+
const targetCol = this.resolveTargetColumnForFieldInsert(targetSchema, selectedEntry, insertionScopePath);
|
|
1690
|
+
if (!targetCol)
|
|
1691
|
+
return;
|
|
1692
|
+
targetSchema.fields.push(...inserted.fields);
|
|
1693
|
+
targetCol.children.push(...inserted.nodes);
|
|
1694
|
+
}
|
|
1695
|
+
this.pendingFieldInsert.set(null);
|
|
1572
1696
|
this.setSchema(nextSchema);
|
|
1573
1697
|
const primaryNode = inserted.primaryNode;
|
|
1574
1698
|
this.selectNode(this.composeScopedNodeId(insertionScopePath, primaryNode.id));
|
|
1575
1699
|
}
|
|
1700
|
+
insertColumnBeforeSelection() {
|
|
1701
|
+
this.insertColumnRelativeToSelection('before');
|
|
1702
|
+
}
|
|
1703
|
+
insertColumnAfterSelection() {
|
|
1704
|
+
this.insertColumnRelativeToSelection('after');
|
|
1705
|
+
}
|
|
1706
|
+
insertRowBeforeSelection() {
|
|
1707
|
+
this.insertRowRelativeToSelection('before');
|
|
1708
|
+
}
|
|
1709
|
+
insertRowAfterSelection() {
|
|
1710
|
+
this.insertRowRelativeToSelection('after');
|
|
1711
|
+
}
|
|
1712
|
+
insertRowInSelectedColumn() {
|
|
1713
|
+
const columnEntry = this.findSelectionColumnEntry(this.selectedEntry());
|
|
1714
|
+
if (!columnEntry)
|
|
1715
|
+
return;
|
|
1716
|
+
this.insertRowInColumn(columnEntry.path.at(-1) ?? columnEntry.rawNodeId, 1);
|
|
1717
|
+
}
|
|
1718
|
+
armFieldInsertBeforeSelection() {
|
|
1719
|
+
this.armFieldInsertForSelection('before');
|
|
1720
|
+
}
|
|
1721
|
+
armFieldInsertAfterSelection() {
|
|
1722
|
+
this.armFieldInsertForSelection('after');
|
|
1723
|
+
}
|
|
1576
1724
|
insertField(options) {
|
|
1577
1725
|
if (this.isReadOnly())
|
|
1578
1726
|
return null;
|
|
@@ -1969,11 +2117,21 @@ class DesignerStateService {
|
|
|
1969
2117
|
const scopeSchema = this.resolveSchemaAtScope(newSchema, entry.scopePath);
|
|
1970
2118
|
if (!scopeSchema)
|
|
1971
2119
|
return;
|
|
2120
|
+
const fieldsToRemove = new Set();
|
|
2121
|
+
const collectFieldIds = (node) => {
|
|
2122
|
+
if (node.type === 'widget' && node.refId) {
|
|
2123
|
+
fieldsToRemove.add(node.refId);
|
|
2124
|
+
}
|
|
2125
|
+
if (node.type === 'row' || node.type === 'col') {
|
|
2126
|
+
node.children.forEach(child => collectFieldIds(child));
|
|
2127
|
+
}
|
|
2128
|
+
};
|
|
1972
2129
|
// Find parent row and remove column
|
|
1973
2130
|
const removeFromRow = (node) => {
|
|
1974
2131
|
if (node.type === 'row') {
|
|
1975
2132
|
const idx = node.children.findIndex(c => c.id === entry.rawNodeId && c.type === 'col');
|
|
1976
2133
|
if (idx !== -1) {
|
|
2134
|
+
collectFieldIds(node.children[idx]);
|
|
1977
2135
|
node.children.splice(idx, 1);
|
|
1978
2136
|
return true;
|
|
1979
2137
|
}
|
|
@@ -1987,6 +2145,8 @@ class DesignerStateService {
|
|
|
1987
2145
|
return false;
|
|
1988
2146
|
};
|
|
1989
2147
|
removeFromRow(scopeSchema.layout);
|
|
2148
|
+
this.pruneEmptyRows(scopeSchema.layout);
|
|
2149
|
+
scopeSchema.fields = scopeSchema.fields.filter(field => !fieldsToRemove.has(field.id));
|
|
1990
2150
|
this.setSchema(newSchema);
|
|
1991
2151
|
}
|
|
1992
2152
|
/** Set preset column layout (e.g., 3,4,6) on a row */
|
|
@@ -2199,6 +2359,7 @@ class DesignerStateService {
|
|
|
2199
2359
|
return false;
|
|
2200
2360
|
};
|
|
2201
2361
|
removeFromParent(scopeSchema.layout, entry.rawNodeId);
|
|
2362
|
+
this.pruneEmptyRows(scopeSchema.layout);
|
|
2202
2363
|
scopeSchema.fields = scopeSchema.fields.filter(field => !fieldsToRemove.has(field.id));
|
|
2203
2364
|
// Clear selection
|
|
2204
2365
|
if (this.selectedNodeId() === nodeId || this.selectedNodeId() === this.composeScopedNodeId(entry.scopePath, entry.rawNodeId)) {
|
|
@@ -2418,6 +2579,22 @@ class DesignerStateService {
|
|
|
2418
2579
|
return null;
|
|
2419
2580
|
return selectedEntry;
|
|
2420
2581
|
}
|
|
2582
|
+
findSelectionColumnEntry(entry) {
|
|
2583
|
+
return this.findAncestorEntryByType(entry, 'col');
|
|
2584
|
+
}
|
|
2585
|
+
findSelectionRowEntry(entry) {
|
|
2586
|
+
return this.findAncestorEntryByType(entry, 'row');
|
|
2587
|
+
}
|
|
2588
|
+
findAncestorEntryByType(entry, type) {
|
|
2589
|
+
let cursor = entry;
|
|
2590
|
+
while (cursor) {
|
|
2591
|
+
if (cursor.node.type === type) {
|
|
2592
|
+
return cursor;
|
|
2593
|
+
}
|
|
2594
|
+
cursor = cursor.parentId ? this.layoutIndex()[cursor.parentId] : null;
|
|
2595
|
+
}
|
|
2596
|
+
return null;
|
|
2597
|
+
}
|
|
2421
2598
|
resolveSchemaAtScope(root, scopePath) {
|
|
2422
2599
|
let cursor = root;
|
|
2423
2600
|
for (const repeatableFieldId of scopePath) {
|
|
@@ -2464,6 +2641,102 @@ class DesignerStateService {
|
|
|
2464
2641
|
}
|
|
2465
2642
|
return this.findFirstColumn(targetSchema.layout);
|
|
2466
2643
|
}
|
|
2644
|
+
resolveColumnInsertTarget(selectionEntry) {
|
|
2645
|
+
const columnEntry = this.findSelectionColumnEntry(selectionEntry);
|
|
2646
|
+
const rowEntry = this.findSelectionRowEntry(selectionEntry);
|
|
2647
|
+
if (!columnEntry || !rowEntry)
|
|
2648
|
+
return null;
|
|
2649
|
+
return { rowEntry, columnEntry };
|
|
2650
|
+
}
|
|
2651
|
+
insertColumnRelativeToSelection(position) {
|
|
2652
|
+
if (this.isReadOnly())
|
|
2653
|
+
return;
|
|
2654
|
+
const target = this.resolveColumnInsertTarget(this.selectedEntry());
|
|
2655
|
+
if (!target)
|
|
2656
|
+
return;
|
|
2657
|
+
const current = this.schema();
|
|
2658
|
+
const nextSchema = this.cloneValue(current);
|
|
2659
|
+
const scopeSchema = this.resolveSchemaAtScope(nextSchema, target.rowEntry.scopePath);
|
|
2660
|
+
if (!scopeSchema)
|
|
2661
|
+
return;
|
|
2662
|
+
const rowNode = this.findNode(scopeSchema.layout, target.rowEntry.rawNodeId);
|
|
2663
|
+
const selectedColumn = this.findNode(scopeSchema.layout, target.columnEntry.rawNodeId);
|
|
2664
|
+
if (rowNode?.type !== 'row' || selectedColumn?.type !== 'col')
|
|
2665
|
+
return;
|
|
2666
|
+
const insertIndex = position === 'before' ? target.columnEntry.index : target.columnEntry.index + 1;
|
|
2667
|
+
const nextColumn = {
|
|
2668
|
+
id: v4(),
|
|
2669
|
+
type: 'col',
|
|
2670
|
+
responsive: this.cloneValue(selectedColumn.responsive ?? { xs: 12 }),
|
|
2671
|
+
children: []
|
|
2672
|
+
};
|
|
2673
|
+
rowNode.children.splice(insertIndex, 0, nextColumn);
|
|
2674
|
+
this.setSchema(nextSchema);
|
|
2675
|
+
this.selectNode(this.composeScopedNodeId(target.rowEntry.scopePath, nextColumn.id));
|
|
2676
|
+
}
|
|
2677
|
+
resolveRelativeRowInsertTarget(selectionEntry) {
|
|
2678
|
+
if (!selectionEntry)
|
|
2679
|
+
return null;
|
|
2680
|
+
if (selectionEntry.node.type === 'widget') {
|
|
2681
|
+
const containerEntry = this.findSelectionColumnEntry(selectionEntry);
|
|
2682
|
+
if (!containerEntry)
|
|
2683
|
+
return null;
|
|
2684
|
+
return { containerEntry, referenceIndex: selectionEntry.index };
|
|
2685
|
+
}
|
|
2686
|
+
if (selectionEntry.node.type === 'row') {
|
|
2687
|
+
const containerEntry = selectionEntry.parentId
|
|
2688
|
+
? this.findAncestorEntryByType(this.layoutIndex()[selectionEntry.parentId], 'col')
|
|
2689
|
+
: null;
|
|
2690
|
+
if (!containerEntry)
|
|
2691
|
+
return null;
|
|
2692
|
+
return { containerEntry, referenceIndex: selectionEntry.index };
|
|
2693
|
+
}
|
|
2694
|
+
return null;
|
|
2695
|
+
}
|
|
2696
|
+
insertRowRelativeToSelection(position) {
|
|
2697
|
+
if (this.isReadOnly())
|
|
2698
|
+
return;
|
|
2699
|
+
const target = this.resolveRelativeRowInsertTarget(this.selectedEntry());
|
|
2700
|
+
if (!target)
|
|
2701
|
+
return;
|
|
2702
|
+
const current = this.schema();
|
|
2703
|
+
const nextSchema = this.cloneValue(current);
|
|
2704
|
+
const scopeSchema = this.resolveSchemaAtScope(nextSchema, target.containerEntry.scopePath);
|
|
2705
|
+
if (!scopeSchema)
|
|
2706
|
+
return;
|
|
2707
|
+
const container = this.findNode(scopeSchema.layout, target.containerEntry.rawNodeId);
|
|
2708
|
+
if (container?.type !== 'col')
|
|
2709
|
+
return;
|
|
2710
|
+
const nextRow = {
|
|
2711
|
+
id: v4(),
|
|
2712
|
+
type: 'row',
|
|
2713
|
+
children: [
|
|
2714
|
+
{
|
|
2715
|
+
id: v4(),
|
|
2716
|
+
type: 'col',
|
|
2717
|
+
responsive: { xs: 12 },
|
|
2718
|
+
children: []
|
|
2719
|
+
}
|
|
2720
|
+
]
|
|
2721
|
+
};
|
|
2722
|
+
const insertIndex = position === 'before' ? target.referenceIndex : target.referenceIndex + 1;
|
|
2723
|
+
container.children.splice(insertIndex, 0, nextRow);
|
|
2724
|
+
this.setSchema(nextSchema);
|
|
2725
|
+
this.selectNode(this.composeScopedNodeId(target.containerEntry.scopePath, nextRow.children[0].id));
|
|
2726
|
+
}
|
|
2727
|
+
armFieldInsertForSelection(position) {
|
|
2728
|
+
const referenceFieldId = this.getSelectedFieldReference();
|
|
2729
|
+
if (!referenceFieldId)
|
|
2730
|
+
return;
|
|
2731
|
+
this.pendingFieldInsert.set({ referenceFieldId, position });
|
|
2732
|
+
}
|
|
2733
|
+
getSelectedFieldReference() {
|
|
2734
|
+
const entry = this.selectedEntry();
|
|
2735
|
+
if (!entry || entry.node.type !== 'widget')
|
|
2736
|
+
return null;
|
|
2737
|
+
const refId = entry.node.refId;
|
|
2738
|
+
return typeof refId === 'string' && refId.trim().length > 0 ? refId : null;
|
|
2739
|
+
}
|
|
2467
2740
|
resolveFieldWidgetDefinition(widgetId, type) {
|
|
2468
2741
|
if (widgetId) {
|
|
2469
2742
|
const byId = this.widgetDefs.find(widget => widget.id === widgetId);
|
|
@@ -2657,6 +2930,20 @@ class DesignerStateService {
|
|
|
2657
2930
|
const [removed] = children.splice(result.index, 1);
|
|
2658
2931
|
return removed ?? null;
|
|
2659
2932
|
}
|
|
2933
|
+
pruneEmptyRows(node) {
|
|
2934
|
+
if (node.type === 'widget') {
|
|
2935
|
+
return true;
|
|
2936
|
+
}
|
|
2937
|
+
if (node.type === 'col') {
|
|
2938
|
+
node.children = node.children.filter(child => this.pruneEmptyRows(child));
|
|
2939
|
+
return true;
|
|
2940
|
+
}
|
|
2941
|
+
if (node.type === 'row') {
|
|
2942
|
+
node.children = node.children.filter(child => this.pruneEmptyRows(child));
|
|
2943
|
+
return node.children.length > 0;
|
|
2944
|
+
}
|
|
2945
|
+
return true;
|
|
2946
|
+
}
|
|
2660
2947
|
findWidgetByRefId(node, refId) {
|
|
2661
2948
|
if (node.type === 'widget' && node.refId === refId) {
|
|
2662
2949
|
return node;
|
|
@@ -2863,6 +3150,12 @@ class LayoutNodeComponent {
|
|
|
2863
3150
|
get isSelected() {
|
|
2864
3151
|
return this.designerState.isNodeSelected(this.getScopedNodeId(this.node.id));
|
|
2865
3152
|
}
|
|
3153
|
+
get isRowSelectionAncestor() {
|
|
3154
|
+
return this.node.type === 'row' && this.designerState.isSelectionRowAncestor(this.getScopedNodeId(this.node.id));
|
|
3155
|
+
}
|
|
3156
|
+
get isColumnSelectionAncestor() {
|
|
3157
|
+
return this.node.type === 'col' && this.designerState.isSelectionColumnAncestor(this.getScopedNodeId(this.node.id));
|
|
3158
|
+
}
|
|
2866
3159
|
get isResizing() {
|
|
2867
3160
|
return this.activeResizeNodeId === this.node.id;
|
|
2868
3161
|
}
|
|
@@ -2961,6 +3254,24 @@ class LayoutNodeComponent {
|
|
|
2961
3254
|
wrapWidgetInRow() {
|
|
2962
3255
|
this.designerState.wrapWidgetInRow(this.getScopedNodeId(this.node.id));
|
|
2963
3256
|
}
|
|
3257
|
+
decreaseSelectedColumnSpan() {
|
|
3258
|
+
this.adjustSelectedColumnSpan(-1);
|
|
3259
|
+
}
|
|
3260
|
+
increaseSelectedColumnSpan() {
|
|
3261
|
+
this.adjustSelectedColumnSpan(1);
|
|
3262
|
+
}
|
|
3263
|
+
canDecreaseSelectedColumnSpan() {
|
|
3264
|
+
const span = this.getSelectedColumnSpan();
|
|
3265
|
+
return span !== null && span > LayoutNodeComponent.MIN_COLUMN_SPAN;
|
|
3266
|
+
}
|
|
3267
|
+
canIncreaseSelectedColumnSpan() {
|
|
3268
|
+
const span = this.getSelectedColumnSpan();
|
|
3269
|
+
return span !== null && span < LayoutNodeComponent.MAX_COLUMN_SPAN;
|
|
3270
|
+
}
|
|
3271
|
+
getSelectedColumnSpanLabel() {
|
|
3272
|
+
const span = this.getSelectedColumnSpan();
|
|
3273
|
+
return span === null ? '--/12' : `${span}/12`;
|
|
3274
|
+
}
|
|
2964
3275
|
getNodeTypeLabel() {
|
|
2965
3276
|
if (this.node.type === 'widget') {
|
|
2966
3277
|
const widgetNode = this.node;
|
|
@@ -3270,6 +3581,32 @@ class LayoutNodeComponent {
|
|
|
3270
3581
|
node.responsive[this.breakpoint] = nextSpan;
|
|
3271
3582
|
this.cdr.detectChanges();
|
|
3272
3583
|
}
|
|
3584
|
+
adjustSelectedColumnSpan(delta) {
|
|
3585
|
+
const columnEntry = this.getSelectedColumnEntry();
|
|
3586
|
+
const column = columnEntry?.node;
|
|
3587
|
+
if (!column || column.type !== 'col')
|
|
3588
|
+
return;
|
|
3589
|
+
const responsive = { ...(column.responsive ?? { xs: LayoutNodeComponent.MAX_COLUMN_SPAN }) };
|
|
3590
|
+
const currentSpan = this.getEffectiveSpan(responsive);
|
|
3591
|
+
const nextSpan = Math.max(LayoutNodeComponent.MIN_COLUMN_SPAN, Math.min(LayoutNodeComponent.MAX_COLUMN_SPAN, currentSpan + delta));
|
|
3592
|
+
if (nextSpan === currentSpan)
|
|
3593
|
+
return;
|
|
3594
|
+
responsive[this.breakpoint] = nextSpan;
|
|
3595
|
+
this.designerState.updateNodeResponsive(columnEntry.path.at(-1) ?? columnEntry.rawNodeId, responsive);
|
|
3596
|
+
}
|
|
3597
|
+
getSelectedColumnSpan() {
|
|
3598
|
+
const columnEntry = this.getSelectedColumnEntry();
|
|
3599
|
+
const column = columnEntry?.node;
|
|
3600
|
+
if (!column || column.type !== 'col')
|
|
3601
|
+
return null;
|
|
3602
|
+
return this.getEffectiveSpan(column.responsive ?? { xs: LayoutNodeComponent.MAX_COLUMN_SPAN });
|
|
3603
|
+
}
|
|
3604
|
+
getSelectedColumnEntry() {
|
|
3605
|
+
const columnId = this.designerState.selectedColumnId();
|
|
3606
|
+
if (!columnId)
|
|
3607
|
+
return null;
|
|
3608
|
+
return this.designerState.layoutIndex()[columnId] ?? null;
|
|
3609
|
+
}
|
|
3273
3610
|
getColClasses(node) {
|
|
3274
3611
|
if (node.type !== 'col')
|
|
3275
3612
|
return '';
|
|
@@ -3445,6 +3782,9 @@ class LayoutNodeComponent {
|
|
|
3445
3782
|
[class.outline-dashed]="designMode && showLayoutGuides"
|
|
3446
3783
|
[class.outline-1]="designMode && showLayoutGuides"
|
|
3447
3784
|
[class.outline-blue-200]="designMode && showLayoutGuides && !isSelected"
|
|
3785
|
+
[class.ring-1]="designMode && isRowSelectionAncestor"
|
|
3786
|
+
[class.ring-emerald-200]="designMode && isRowSelectionAncestor"
|
|
3787
|
+
[class.bg-emerald-50]="designMode && isRowSelectionAncestor"
|
|
3448
3788
|
[class.ring-2]="designMode && isSelected"
|
|
3449
3789
|
[class.ring-blue-500]="designMode && isSelected"
|
|
3450
3790
|
[class.bg-blue-50]="designMode && isSelected">
|
|
@@ -3520,6 +3860,9 @@ class LayoutNodeComponent {
|
|
|
3520
3860
|
[class.outline-dashed]="designMode && showLayoutGuides"
|
|
3521
3861
|
[class.outline-1]="designMode && showLayoutGuides"
|
|
3522
3862
|
[class.outline-gray-300]="designMode && showLayoutGuides && !isSelected"
|
|
3863
|
+
[class.ring-1]="designMode && isColumnSelectionAncestor"
|
|
3864
|
+
[class.ring-amber-200]="designMode && isColumnSelectionAncestor"
|
|
3865
|
+
[class.bg-amber-50]="designMode && isColumnSelectionAncestor"
|
|
3523
3866
|
[class.ring-2]="designMode && isSelected"
|
|
3524
3867
|
[class.ring-blue-500]="designMode && isSelected"
|
|
3525
3868
|
[class.bg-blue-50]="designMode && isSelected && asCol(node).children.length === 0">
|
|
@@ -3547,6 +3890,22 @@ class LayoutNodeComponent {
|
|
|
3547
3890
|
<lucide-icon name="layout-list" class="w-3.5 h-3.5"></lucide-icon>
|
|
3548
3891
|
</button>
|
|
3549
3892
|
<div class="w-px h-4 bg-gray-700 mx-0.5"></div>
|
|
3893
|
+
<button type="button"
|
|
3894
|
+
(click)="decreaseSelectedColumnSpan()"
|
|
3895
|
+
class="px-2 py-1 hover:bg-gray-700 rounded transition-colors disabled:opacity-40 disabled:hover:bg-transparent"
|
|
3896
|
+
title="Decrease width"
|
|
3897
|
+
[disabled]="!canDecreaseSelectedColumnSpan()">
|
|
3898
|
+
-
|
|
3899
|
+
</button>
|
|
3900
|
+
<span class="min-w-10 text-center text-[10px] font-semibold text-gray-300">{{ getSelectedColumnSpanLabel() }}</span>
|
|
3901
|
+
<button type="button"
|
|
3902
|
+
(click)="increaseSelectedColumnSpan()"
|
|
3903
|
+
class="px-2 py-1 hover:bg-gray-700 rounded transition-colors disabled:opacity-40 disabled:hover:bg-transparent"
|
|
3904
|
+
title="Increase width"
|
|
3905
|
+
[disabled]="!canIncreaseSelectedColumnSpan()">
|
|
3906
|
+
+
|
|
3907
|
+
</button>
|
|
3908
|
+
<div class="w-px h-4 bg-gray-700 mx-0.5"></div>
|
|
3550
3909
|
<span class="text-gray-400 uppercase font-semibold text-[10px] px-1.5">Column</span>
|
|
3551
3910
|
</div>
|
|
3552
3911
|
|
|
@@ -3582,13 +3941,6 @@ class LayoutNodeComponent {
|
|
|
3582
3941
|
<div *cdkDragPlaceholder class="min-h-[50px] bg-blue-50 border-2 border-blue-200 border-dashed mb-2 rounded"></div>
|
|
3583
3942
|
</div>
|
|
3584
3943
|
|
|
3585
|
-
<!-- Placeholder for empty col in designer -->
|
|
3586
|
-
<div *ngIf="asCol(node).children.length === 0 && designMode"
|
|
3587
|
-
class="h-full w-full min-h-[4rem] flex flex-col items-center justify-center p-4 border-2 border-dashed border-gray-200 rounded-lg bg-gray-50/50 hover:bg-gray-100 hover:border-blue-300 transition-all text-gray-400 gap-2">
|
|
3588
|
-
<lucide-icon name="plus" class="w-5 h-5 opacity-50"></lucide-icon>
|
|
3589
|
-
<span class="text-xs font-medium">Drop Widget Here</span>
|
|
3590
|
-
</div>
|
|
3591
|
-
|
|
3592
3944
|
<!-- Resize Handle - z-50 to stay above widget overlays -->
|
|
3593
3945
|
<!-- Width Resize (Right) - Only if not last column in row -->
|
|
3594
3946
|
<div *ngIf="designMode && isSelected"
|
|
@@ -3639,6 +3991,22 @@ class LayoutNodeComponent {
|
|
|
3639
3991
|
<lucide-icon name="group" class="w-3.5 h-3.5"></lucide-icon>
|
|
3640
3992
|
</button>
|
|
3641
3993
|
<div class="w-px h-4 bg-gray-700 mx-0.5"></div>
|
|
3994
|
+
<button type="button"
|
|
3995
|
+
(click)="decreaseSelectedColumnSpan()"
|
|
3996
|
+
class="px-2 py-1 hover:bg-gray-700 rounded transition-colors disabled:opacity-40 disabled:hover:bg-transparent"
|
|
3997
|
+
title="Decrease width"
|
|
3998
|
+
[disabled]="!canDecreaseSelectedColumnSpan()">
|
|
3999
|
+
-
|
|
4000
|
+
</button>
|
|
4001
|
+
<span class="min-w-10 text-center text-[10px] font-semibold text-gray-300">{{ getSelectedColumnSpanLabel() }}</span>
|
|
4002
|
+
<button type="button"
|
|
4003
|
+
(click)="increaseSelectedColumnSpan()"
|
|
4004
|
+
class="px-2 py-1 hover:bg-gray-700 rounded transition-colors disabled:opacity-40 disabled:hover:bg-transparent"
|
|
4005
|
+
title="Increase width"
|
|
4006
|
+
[disabled]="!canIncreaseSelectedColumnSpan()">
|
|
4007
|
+
+
|
|
4008
|
+
</button>
|
|
4009
|
+
<div class="w-px h-4 bg-gray-700 mx-0.5"></div>
|
|
3642
4010
|
<span class="text-gray-400 uppercase font-semibold text-[10px] px-1.5">{{ getNodeTypeLabel() }}</span>
|
|
3643
4011
|
</div>
|
|
3644
4012
|
|
|
@@ -3699,6 +4067,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
3699
4067
|
[class.outline-dashed]="designMode && showLayoutGuides"
|
|
3700
4068
|
[class.outline-1]="designMode && showLayoutGuides"
|
|
3701
4069
|
[class.outline-blue-200]="designMode && showLayoutGuides && !isSelected"
|
|
4070
|
+
[class.ring-1]="designMode && isRowSelectionAncestor"
|
|
4071
|
+
[class.ring-emerald-200]="designMode && isRowSelectionAncestor"
|
|
4072
|
+
[class.bg-emerald-50]="designMode && isRowSelectionAncestor"
|
|
3702
4073
|
[class.ring-2]="designMode && isSelected"
|
|
3703
4074
|
[class.ring-blue-500]="designMode && isSelected"
|
|
3704
4075
|
[class.bg-blue-50]="designMode && isSelected">
|
|
@@ -3774,6 +4145,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
3774
4145
|
[class.outline-dashed]="designMode && showLayoutGuides"
|
|
3775
4146
|
[class.outline-1]="designMode && showLayoutGuides"
|
|
3776
4147
|
[class.outline-gray-300]="designMode && showLayoutGuides && !isSelected"
|
|
4148
|
+
[class.ring-1]="designMode && isColumnSelectionAncestor"
|
|
4149
|
+
[class.ring-amber-200]="designMode && isColumnSelectionAncestor"
|
|
4150
|
+
[class.bg-amber-50]="designMode && isColumnSelectionAncestor"
|
|
3777
4151
|
[class.ring-2]="designMode && isSelected"
|
|
3778
4152
|
[class.ring-blue-500]="designMode && isSelected"
|
|
3779
4153
|
[class.bg-blue-50]="designMode && isSelected && asCol(node).children.length === 0">
|
|
@@ -3801,6 +4175,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
3801
4175
|
<lucide-icon name="layout-list" class="w-3.5 h-3.5"></lucide-icon>
|
|
3802
4176
|
</button>
|
|
3803
4177
|
<div class="w-px h-4 bg-gray-700 mx-0.5"></div>
|
|
4178
|
+
<button type="button"
|
|
4179
|
+
(click)="decreaseSelectedColumnSpan()"
|
|
4180
|
+
class="px-2 py-1 hover:bg-gray-700 rounded transition-colors disabled:opacity-40 disabled:hover:bg-transparent"
|
|
4181
|
+
title="Decrease width"
|
|
4182
|
+
[disabled]="!canDecreaseSelectedColumnSpan()">
|
|
4183
|
+
-
|
|
4184
|
+
</button>
|
|
4185
|
+
<span class="min-w-10 text-center text-[10px] font-semibold text-gray-300">{{ getSelectedColumnSpanLabel() }}</span>
|
|
4186
|
+
<button type="button"
|
|
4187
|
+
(click)="increaseSelectedColumnSpan()"
|
|
4188
|
+
class="px-2 py-1 hover:bg-gray-700 rounded transition-colors disabled:opacity-40 disabled:hover:bg-transparent"
|
|
4189
|
+
title="Increase width"
|
|
4190
|
+
[disabled]="!canIncreaseSelectedColumnSpan()">
|
|
4191
|
+
+
|
|
4192
|
+
</button>
|
|
4193
|
+
<div class="w-px h-4 bg-gray-700 mx-0.5"></div>
|
|
3804
4194
|
<span class="text-gray-400 uppercase font-semibold text-[10px] px-1.5">Column</span>
|
|
3805
4195
|
</div>
|
|
3806
4196
|
|
|
@@ -3836,13 +4226,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
3836
4226
|
<div *cdkDragPlaceholder class="min-h-[50px] bg-blue-50 border-2 border-blue-200 border-dashed mb-2 rounded"></div>
|
|
3837
4227
|
</div>
|
|
3838
4228
|
|
|
3839
|
-
<!-- Placeholder for empty col in designer -->
|
|
3840
|
-
<div *ngIf="asCol(node).children.length === 0 && designMode"
|
|
3841
|
-
class="h-full w-full min-h-[4rem] flex flex-col items-center justify-center p-4 border-2 border-dashed border-gray-200 rounded-lg bg-gray-50/50 hover:bg-gray-100 hover:border-blue-300 transition-all text-gray-400 gap-2">
|
|
3842
|
-
<lucide-icon name="plus" class="w-5 h-5 opacity-50"></lucide-icon>
|
|
3843
|
-
<span class="text-xs font-medium">Drop Widget Here</span>
|
|
3844
|
-
</div>
|
|
3845
|
-
|
|
3846
4229
|
<!-- Resize Handle - z-50 to stay above widget overlays -->
|
|
3847
4230
|
<!-- Width Resize (Right) - Only if not last column in row -->
|
|
3848
4231
|
<div *ngIf="designMode && isSelected"
|
|
@@ -3893,6 +4276,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
3893
4276
|
<lucide-icon name="group" class="w-3.5 h-3.5"></lucide-icon>
|
|
3894
4277
|
</button>
|
|
3895
4278
|
<div class="w-px h-4 bg-gray-700 mx-0.5"></div>
|
|
4279
|
+
<button type="button"
|
|
4280
|
+
(click)="decreaseSelectedColumnSpan()"
|
|
4281
|
+
class="px-2 py-1 hover:bg-gray-700 rounded transition-colors disabled:opacity-40 disabled:hover:bg-transparent"
|
|
4282
|
+
title="Decrease width"
|
|
4283
|
+
[disabled]="!canDecreaseSelectedColumnSpan()">
|
|
4284
|
+
-
|
|
4285
|
+
</button>
|
|
4286
|
+
<span class="min-w-10 text-center text-[10px] font-semibold text-gray-300">{{ getSelectedColumnSpanLabel() }}</span>
|
|
4287
|
+
<button type="button"
|
|
4288
|
+
(click)="increaseSelectedColumnSpan()"
|
|
4289
|
+
class="px-2 py-1 hover:bg-gray-700 rounded transition-colors disabled:opacity-40 disabled:hover:bg-transparent"
|
|
4290
|
+
title="Increase width"
|
|
4291
|
+
[disabled]="!canIncreaseSelectedColumnSpan()">
|
|
4292
|
+
+
|
|
4293
|
+
</button>
|
|
4294
|
+
<div class="w-px h-4 bg-gray-700 mx-0.5"></div>
|
|
3896
4295
|
<span class="text-gray-400 uppercase font-semibold text-[10px] px-1.5">{{ getNodeTypeLabel() }}</span>
|
|
3897
4296
|
</div>
|
|
3898
4297
|
|
|
@@ -4864,7 +5263,7 @@ class JsonFormRendererComponent {
|
|
|
4864
5263
|
this.valueChange.emit(fieldValueMap);
|
|
4865
5264
|
this.groupedValueChange.emit(groupedValues);
|
|
4866
5265
|
this.combinedValueChange.emit(combinedValues);
|
|
4867
|
-
this.validationChange.emit(this.getValidationResult());
|
|
5266
|
+
this.validationChange.emit(this.getValidationResult(true));
|
|
4868
5267
|
}
|
|
4869
5268
|
disposeRunner() {
|
|
4870
5269
|
if (this.runner) {
|
|
@@ -5016,21 +5415,17 @@ class JsonFormRendererComponent {
|
|
|
5016
5415
|
event.preventDefault();
|
|
5017
5416
|
if (this.mode === 'design')
|
|
5018
5417
|
return;
|
|
5019
|
-
const
|
|
5020
|
-
const validation = {
|
|
5021
|
-
errors: { ...errors },
|
|
5022
|
-
isValid: Object.keys(errors).length === 0
|
|
5023
|
-
};
|
|
5418
|
+
const validation = this.getValidationResult(true);
|
|
5024
5419
|
this.validationChange.emit(validation);
|
|
5025
5420
|
// Notify all widgets to show errors if any
|
|
5026
5421
|
this.engine?.submit();
|
|
5027
5422
|
const preUploadFieldValueMap = this.uploadOnSubmit
|
|
5028
|
-
? await this.buildFieldValueMap(this.engine?.getValues() ?? {})
|
|
5423
|
+
? await this.buildFieldValueMap(this.engine?.getValues() ?? {}, { normalizeFileFieldsForSubmit: true })
|
|
5029
5424
|
: undefined;
|
|
5030
5425
|
const uploadedFiles = this.uploadOnSubmit ? await this.uploadPendingFiles() : {};
|
|
5031
5426
|
this.uploadedFilesChange.emit(uploadedFiles);
|
|
5032
5427
|
const values = this.engine?.getValues() ?? {};
|
|
5033
|
-
const fieldValueMap = await this.buildFieldValueMap(values);
|
|
5428
|
+
const fieldValueMap = await this.buildFieldValueMap(values, { normalizeFileFieldsForSubmit: true });
|
|
5034
5429
|
const submitValues = preUploadFieldValueMap
|
|
5035
5430
|
? this.mergeFileMetadata(fieldValueMap, preUploadFieldValueMap)
|
|
5036
5431
|
: fieldValueMap;
|
|
@@ -5042,13 +5437,134 @@ class JsonFormRendererComponent {
|
|
|
5042
5437
|
validation
|
|
5043
5438
|
});
|
|
5044
5439
|
}
|
|
5045
|
-
getValidationResult() {
|
|
5046
|
-
const errors =
|
|
5440
|
+
getValidationResult(revalidate = false) {
|
|
5441
|
+
const errors = revalidate
|
|
5442
|
+
? (this.engine?.validate() ?? {})
|
|
5443
|
+
: (this.engine?.getErrors() ?? {});
|
|
5047
5444
|
return {
|
|
5048
|
-
errors,
|
|
5049
|
-
isValid: Object.keys(errors).length === 0
|
|
5445
|
+
errors: { ...errors },
|
|
5446
|
+
isValid: Object.keys(errors).length === 0,
|
|
5447
|
+
fields: this.buildFieldValidationState(errors)
|
|
5050
5448
|
};
|
|
5051
5449
|
}
|
|
5450
|
+
buildFieldValidationState(errors) {
|
|
5451
|
+
const schema = this.engine?.getSchema() ?? this.schema;
|
|
5452
|
+
if (!schema)
|
|
5453
|
+
return {};
|
|
5454
|
+
const states = {};
|
|
5455
|
+
for (const field of schema.fields) {
|
|
5456
|
+
const visible = this.engine ? this.engine.isFieldVisible(field.id) : true;
|
|
5457
|
+
const required = visible && (this.engine
|
|
5458
|
+
? this.engine.isFieldRequired(field.id)
|
|
5459
|
+
: !!field.html5?.required);
|
|
5460
|
+
const fieldErrors = errors[field.name] ? [...errors[field.name]] : [];
|
|
5461
|
+
states[field.name] = {
|
|
5462
|
+
fieldId: field.id,
|
|
5463
|
+
fieldName: field.name,
|
|
5464
|
+
...(field.label ? { label: field.label } : {}),
|
|
5465
|
+
visible,
|
|
5466
|
+
required,
|
|
5467
|
+
valid: fieldErrors.length === 0,
|
|
5468
|
+
errors: fieldErrors,
|
|
5469
|
+
validators: this.describeFieldValidators(field, visible, required)
|
|
5470
|
+
};
|
|
5471
|
+
}
|
|
5472
|
+
return states;
|
|
5473
|
+
}
|
|
5474
|
+
describeFieldValidators(field, visible, required) {
|
|
5475
|
+
const validators = [];
|
|
5476
|
+
const value = this.engine?.getValue(field.name);
|
|
5477
|
+
const hasValue = !this.isValidationEmpty(value);
|
|
5478
|
+
if (this.hasRequiredValidation(field) || required) {
|
|
5479
|
+
validators.push({
|
|
5480
|
+
name: 'required',
|
|
5481
|
+
source: 'required',
|
|
5482
|
+
active: visible && required,
|
|
5483
|
+
message: 'This field is required.'
|
|
5484
|
+
});
|
|
5485
|
+
}
|
|
5486
|
+
if (field.html5?.minLength !== undefined) {
|
|
5487
|
+
validators.push({
|
|
5488
|
+
name: 'minLength',
|
|
5489
|
+
source: 'html5',
|
|
5490
|
+
active: visible && hasValue,
|
|
5491
|
+
value: field.html5.minLength
|
|
5492
|
+
});
|
|
5493
|
+
}
|
|
5494
|
+
if (field.html5?.maxLength !== undefined) {
|
|
5495
|
+
validators.push({
|
|
5496
|
+
name: 'maxLength',
|
|
5497
|
+
source: 'html5',
|
|
5498
|
+
active: visible && hasValue,
|
|
5499
|
+
value: field.html5.maxLength
|
|
5500
|
+
});
|
|
5501
|
+
}
|
|
5502
|
+
if (field.html5?.min !== undefined) {
|
|
5503
|
+
validators.push({
|
|
5504
|
+
name: 'min',
|
|
5505
|
+
source: 'html5',
|
|
5506
|
+
active: visible && hasValue,
|
|
5507
|
+
value: field.html5.min
|
|
5508
|
+
});
|
|
5509
|
+
}
|
|
5510
|
+
if (field.html5?.max !== undefined) {
|
|
5511
|
+
validators.push({
|
|
5512
|
+
name: 'max',
|
|
5513
|
+
source: 'html5',
|
|
5514
|
+
active: visible && hasValue,
|
|
5515
|
+
value: field.html5.max
|
|
5516
|
+
});
|
|
5517
|
+
}
|
|
5518
|
+
if (field.html5?.pattern) {
|
|
5519
|
+
validators.push({
|
|
5520
|
+
name: 'pattern',
|
|
5521
|
+
source: 'html5',
|
|
5522
|
+
active: visible && hasValue,
|
|
5523
|
+
value: field.html5.pattern
|
|
5524
|
+
});
|
|
5525
|
+
}
|
|
5526
|
+
for (const rule of field.validation ?? []) {
|
|
5527
|
+
validators.push({
|
|
5528
|
+
name: rule.type === 'builtin' ? (rule.name ?? 'builtin') : 'expression',
|
|
5529
|
+
source: 'custom',
|
|
5530
|
+
active: visible && hasValue && this.isValidationRuleActive(rule),
|
|
5531
|
+
value: rule.type === 'expression' ? rule.expression : rule.name,
|
|
5532
|
+
message: rule.message
|
|
5533
|
+
});
|
|
5534
|
+
}
|
|
5535
|
+
return validators;
|
|
5536
|
+
}
|
|
5537
|
+
hasRequiredValidation(field) {
|
|
5538
|
+
if (field.html5?.required)
|
|
5539
|
+
return true;
|
|
5540
|
+
if (field.dependencies?.some(rule => rule.effect === 'require' || rule.effect === 'optional')) {
|
|
5541
|
+
return true;
|
|
5542
|
+
}
|
|
5543
|
+
if (field.rules?.some(rule => rule.action === 'required'
|
|
5544
|
+
|| rule.action === 'optional'
|
|
5545
|
+
|| rule.elseAction === 'required'
|
|
5546
|
+
|| rule.elseAction === 'optional')) {
|
|
5547
|
+
return true;
|
|
5548
|
+
}
|
|
5549
|
+
return false;
|
|
5550
|
+
}
|
|
5551
|
+
isValidationRuleActive(rule) {
|
|
5552
|
+
if (!rule.when)
|
|
5553
|
+
return true;
|
|
5554
|
+
try {
|
|
5555
|
+
const checkFn = new Function('form', `return ${rule.when}`);
|
|
5556
|
+
return checkFn(this.engine?.getValues() ?? {});
|
|
5557
|
+
}
|
|
5558
|
+
catch {
|
|
5559
|
+
return false;
|
|
5560
|
+
}
|
|
5561
|
+
}
|
|
5562
|
+
isValidationEmpty(value) {
|
|
5563
|
+
return value === null
|
|
5564
|
+
|| value === undefined
|
|
5565
|
+
|| value === ''
|
|
5566
|
+
|| (Array.isArray(value) && value.length === 0);
|
|
5567
|
+
}
|
|
5052
5568
|
async uploadPendingFiles() {
|
|
5053
5569
|
if (!this.engine)
|
|
5054
5570
|
return {};
|
|
@@ -5142,13 +5658,13 @@ class JsonFormRendererComponent {
|
|
|
5142
5658
|
isObjectRecord(value) {
|
|
5143
5659
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
5144
5660
|
}
|
|
5145
|
-
async buildFieldValueMap(values) {
|
|
5661
|
+
async buildFieldValueMap(values, options = {}) {
|
|
5146
5662
|
const schema = this.engine?.getSchema();
|
|
5147
5663
|
if (!schema)
|
|
5148
5664
|
return {};
|
|
5149
|
-
return this.buildFieldValueMapForSchema(schema, values);
|
|
5665
|
+
return this.buildFieldValueMapForSchema(schema, values, options);
|
|
5150
5666
|
}
|
|
5151
|
-
async buildFieldValueMapForSchema(schema, valuesScope) {
|
|
5667
|
+
async buildFieldValueMapForSchema(schema, valuesScope, options = {}) {
|
|
5152
5668
|
const mapped = {};
|
|
5153
5669
|
for (const field of schema.fields) {
|
|
5154
5670
|
if (!field?.id || !field?.name)
|
|
@@ -5163,7 +5679,7 @@ class JsonFormRendererComponent {
|
|
|
5163
5679
|
rows.push({});
|
|
5164
5680
|
continue;
|
|
5165
5681
|
}
|
|
5166
|
-
rows.push(await this.buildFieldValueMapForSchema(itemSchema, row));
|
|
5682
|
+
rows.push(await this.buildFieldValueMapForSchema(itemSchema, row, options));
|
|
5167
5683
|
}
|
|
5168
5684
|
}
|
|
5169
5685
|
mapped[field.id] = {
|
|
@@ -5175,7 +5691,10 @@ class JsonFormRendererComponent {
|
|
|
5175
5691
|
if (field.type === 'file') {
|
|
5176
5692
|
const fileValue = {
|
|
5177
5693
|
fieldName: field.name,
|
|
5178
|
-
fieldValue:
|
|
5694
|
+
fieldValue: options.normalizeFileFieldsForSubmit
|
|
5695
|
+
? this.normalizeFileFieldSubmitValue(rawValue)
|
|
5696
|
+
: rawValue,
|
|
5697
|
+
...(options.normalizeFileFieldsForSubmit ? { fieldType: field.type } : {}),
|
|
5179
5698
|
...(await this.buildFileFieldMetadata(rawValue))
|
|
5180
5699
|
};
|
|
5181
5700
|
mapped[field.id] = fileValue;
|
|
@@ -5263,6 +5782,18 @@ class JsonFormRendererComponent {
|
|
|
5263
5782
|
return undefined;
|
|
5264
5783
|
return values.length === 1 ? values[0] : values;
|
|
5265
5784
|
}
|
|
5785
|
+
normalizeFileFieldSubmitValue(value) {
|
|
5786
|
+
if (this.isFileList(value)) {
|
|
5787
|
+
return Array.from(value);
|
|
5788
|
+
}
|
|
5789
|
+
if (Array.isArray(value)) {
|
|
5790
|
+
return [...value];
|
|
5791
|
+
}
|
|
5792
|
+
if (value === null || value === undefined) {
|
|
5793
|
+
return [];
|
|
5794
|
+
}
|
|
5795
|
+
return [value];
|
|
5796
|
+
}
|
|
5266
5797
|
getUploadedFileRefs(value) {
|
|
5267
5798
|
if (this.isUploadedFileRef(value))
|
|
5268
5799
|
return [value];
|
|
@@ -5678,11 +6209,7 @@ class FormJourneyViewerComponent {
|
|
|
5678
6209
|
return { ok: false, reason: 'unknown-page' };
|
|
5679
6210
|
}
|
|
5680
6211
|
if (!this.viewOnly && this.renderer?.engine) {
|
|
5681
|
-
const
|
|
5682
|
-
const validation = {
|
|
5683
|
-
errors: { ...errors },
|
|
5684
|
-
isValid: Object.keys(errors).length === 0
|
|
5685
|
-
};
|
|
6212
|
+
const validation = this.renderer.getValidationResult(true);
|
|
5686
6213
|
this.formValidationChange.emit(validation);
|
|
5687
6214
|
if (!validation.isValid) {
|
|
5688
6215
|
return { ok: false, reason: 'validation' };
|
|
@@ -7684,6 +8211,7 @@ class LayoutCanvasComponent {
|
|
|
7684
8211
|
showLiveSchemaEditor = signal(false);
|
|
7685
8212
|
liveSchemaEditorText = signal('');
|
|
7686
8213
|
liveSchemaEditorError = signal('');
|
|
8214
|
+
openContextSubmenu = signal(null);
|
|
7687
8215
|
liveSchemaEditorOptions = {
|
|
7688
8216
|
fontSize: 12,
|
|
7689
8217
|
lineNumbersMinChars: 3,
|
|
@@ -7801,6 +8329,7 @@ class LayoutCanvasComponent {
|
|
|
7801
8329
|
this.closeContextMenu();
|
|
7802
8330
|
}
|
|
7803
8331
|
closeContextMenu() {
|
|
8332
|
+
this.openContextSubmenu.set(null);
|
|
7804
8333
|
this.state.closeContextMenu();
|
|
7805
8334
|
}
|
|
7806
8335
|
groupSelected() {
|
|
@@ -7811,6 +8340,46 @@ class LayoutCanvasComponent {
|
|
|
7811
8340
|
this.state.ungroupSelectedFields();
|
|
7812
8341
|
this.closeContextMenu();
|
|
7813
8342
|
}
|
|
8343
|
+
hasStructuralInsertActions() {
|
|
8344
|
+
return this.state.canInsertColumnBeforeSelection()
|
|
8345
|
+
|| this.state.canInsertColumnAfterSelection()
|
|
8346
|
+
|| this.state.canInsertRowInSelectedColumn()
|
|
8347
|
+
|| this.state.canInsertRowBeforeSelection()
|
|
8348
|
+
|| this.state.canInsertRowAfterSelection()
|
|
8349
|
+
|| this.state.canArmFieldInsertBeforeSelection()
|
|
8350
|
+
|| this.state.canArmFieldInsertAfterSelection();
|
|
8351
|
+
}
|
|
8352
|
+
toggleInsertSubmenu() {
|
|
8353
|
+
this.openContextSubmenu.update(current => current === 'insert' ? null : 'insert');
|
|
8354
|
+
}
|
|
8355
|
+
insertColumnBeforeSelection() {
|
|
8356
|
+
this.state.insertColumnBeforeSelection();
|
|
8357
|
+
this.closeContextMenu();
|
|
8358
|
+
}
|
|
8359
|
+
insertColumnAfterSelection() {
|
|
8360
|
+
this.state.insertColumnAfterSelection();
|
|
8361
|
+
this.closeContextMenu();
|
|
8362
|
+
}
|
|
8363
|
+
insertRowInSelectedColumn() {
|
|
8364
|
+
this.state.insertRowInSelectedColumn();
|
|
8365
|
+
this.closeContextMenu();
|
|
8366
|
+
}
|
|
8367
|
+
insertRowBeforeSelection() {
|
|
8368
|
+
this.state.insertRowBeforeSelection();
|
|
8369
|
+
this.closeContextMenu();
|
|
8370
|
+
}
|
|
8371
|
+
insertRowAfterSelection() {
|
|
8372
|
+
this.state.insertRowAfterSelection();
|
|
8373
|
+
this.closeContextMenu();
|
|
8374
|
+
}
|
|
8375
|
+
armFieldInsertBeforeSelection() {
|
|
8376
|
+
this.state.armFieldInsertBeforeSelection();
|
|
8377
|
+
this.closeContextMenu();
|
|
8378
|
+
}
|
|
8379
|
+
armFieldInsertAfterSelection() {
|
|
8380
|
+
this.state.armFieldInsertAfterSelection();
|
|
8381
|
+
this.closeContextMenu();
|
|
8382
|
+
}
|
|
7814
8383
|
onCanvasContextMenu(event) {
|
|
7815
8384
|
event.preventDefault();
|
|
7816
8385
|
event.stopPropagation();
|
|
@@ -8249,6 +8818,56 @@ class LayoutCanvasComponent {
|
|
|
8249
8818
|
<span>Delete</span>
|
|
8250
8819
|
<span class="opacity-60 text-[10px]">Del</span>
|
|
8251
8820
|
</button>
|
|
8821
|
+
<div *ngIf="hasStructuralInsertActions()" class="relative">
|
|
8822
|
+
<div class="h-px bg-border-default my-1"></div>
|
|
8823
|
+
<button type="button"
|
|
8824
|
+
aria-label="Open insert submenu"
|
|
8825
|
+
class="flex w-full items-center justify-between px-3 py-2 text-left text-text-primary hover:bg-slate-50"
|
|
8826
|
+
[attr.aria-expanded]="openContextSubmenu() === 'insert'"
|
|
8827
|
+
(click)="toggleInsertSubmenu(); $event.stopPropagation()">
|
|
8828
|
+
<span>Insert…</span>
|
|
8829
|
+
<span class="text-[10px] opacity-60">{{ openContextSubmenu() === 'insert' ? '‹' : '›' }}</span>
|
|
8830
|
+
</button>
|
|
8831
|
+
|
|
8832
|
+
<div *ngIf="openContextSubmenu() === 'insert'"
|
|
8833
|
+
class="absolute left-full top-0 ml-1 min-w-[210px] rounded-md border border-border-default bg-surface-default shadow-popover text-[12px]">
|
|
8834
|
+
<button *ngIf="!state.isReadOnly() && state.canInsertColumnBeforeSelection()" type="button"
|
|
8835
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
8836
|
+
(click)="insertColumnBeforeSelection()">
|
|
8837
|
+
Add column left
|
|
8838
|
+
</button>
|
|
8839
|
+
<button *ngIf="!state.isReadOnly() && state.canInsertColumnAfterSelection()" type="button"
|
|
8840
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
8841
|
+
(click)="insertColumnAfterSelection()">
|
|
8842
|
+
Add column right
|
|
8843
|
+
</button>
|
|
8844
|
+
<button *ngIf="!state.isReadOnly() && state.canInsertRowInSelectedColumn()" type="button"
|
|
8845
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
8846
|
+
(click)="insertRowInSelectedColumn()">
|
|
8847
|
+
Add row in column
|
|
8848
|
+
</button>
|
|
8849
|
+
<button *ngIf="!state.isReadOnly() && state.canInsertRowBeforeSelection()" type="button"
|
|
8850
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
8851
|
+
(click)="insertRowBeforeSelection()">
|
|
8852
|
+
Add row above
|
|
8853
|
+
</button>
|
|
8854
|
+
<button *ngIf="!state.isReadOnly() && state.canInsertRowAfterSelection()" type="button"
|
|
8855
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
8856
|
+
(click)="insertRowAfterSelection()">
|
|
8857
|
+
Add row below
|
|
8858
|
+
</button>
|
|
8859
|
+
<button *ngIf="!state.isReadOnly() && state.canArmFieldInsertBeforeSelection()" type="button"
|
|
8860
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
8861
|
+
(click)="armFieldInsertBeforeSelection()">
|
|
8862
|
+
Next widget above
|
|
8863
|
+
</button>
|
|
8864
|
+
<button *ngIf="!state.isReadOnly() && state.canArmFieldInsertAfterSelection()" type="button"
|
|
8865
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
8866
|
+
(click)="armFieldInsertAfterSelection()">
|
|
8867
|
+
Next widget below
|
|
8868
|
+
</button>
|
|
8869
|
+
</div>
|
|
8870
|
+
</div>
|
|
8252
8871
|
<div class="h-px bg-border-default my-1"></div>
|
|
8253
8872
|
<button *ngIf="!state.isReadOnly()" type="button"
|
|
8254
8873
|
class="w-full px-3 py-2 text-left hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed text-text-primary"
|
|
@@ -8495,6 +9114,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
8495
9114
|
<span>Delete</span>
|
|
8496
9115
|
<span class="opacity-60 text-[10px]">Del</span>
|
|
8497
9116
|
</button>
|
|
9117
|
+
<div *ngIf="hasStructuralInsertActions()" class="relative">
|
|
9118
|
+
<div class="h-px bg-border-default my-1"></div>
|
|
9119
|
+
<button type="button"
|
|
9120
|
+
aria-label="Open insert submenu"
|
|
9121
|
+
class="flex w-full items-center justify-between px-3 py-2 text-left text-text-primary hover:bg-slate-50"
|
|
9122
|
+
[attr.aria-expanded]="openContextSubmenu() === 'insert'"
|
|
9123
|
+
(click)="toggleInsertSubmenu(); $event.stopPropagation()">
|
|
9124
|
+
<span>Insert…</span>
|
|
9125
|
+
<span class="text-[10px] opacity-60">{{ openContextSubmenu() === 'insert' ? '‹' : '›' }}</span>
|
|
9126
|
+
</button>
|
|
9127
|
+
|
|
9128
|
+
<div *ngIf="openContextSubmenu() === 'insert'"
|
|
9129
|
+
class="absolute left-full top-0 ml-1 min-w-[210px] rounded-md border border-border-default bg-surface-default shadow-popover text-[12px]">
|
|
9130
|
+
<button *ngIf="!state.isReadOnly() && state.canInsertColumnBeforeSelection()" type="button"
|
|
9131
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
9132
|
+
(click)="insertColumnBeforeSelection()">
|
|
9133
|
+
Add column left
|
|
9134
|
+
</button>
|
|
9135
|
+
<button *ngIf="!state.isReadOnly() && state.canInsertColumnAfterSelection()" type="button"
|
|
9136
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
9137
|
+
(click)="insertColumnAfterSelection()">
|
|
9138
|
+
Add column right
|
|
9139
|
+
</button>
|
|
9140
|
+
<button *ngIf="!state.isReadOnly() && state.canInsertRowInSelectedColumn()" type="button"
|
|
9141
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
9142
|
+
(click)="insertRowInSelectedColumn()">
|
|
9143
|
+
Add row in column
|
|
9144
|
+
</button>
|
|
9145
|
+
<button *ngIf="!state.isReadOnly() && state.canInsertRowBeforeSelection()" type="button"
|
|
9146
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
9147
|
+
(click)="insertRowBeforeSelection()">
|
|
9148
|
+
Add row above
|
|
9149
|
+
</button>
|
|
9150
|
+
<button *ngIf="!state.isReadOnly() && state.canInsertRowAfterSelection()" type="button"
|
|
9151
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
9152
|
+
(click)="insertRowAfterSelection()">
|
|
9153
|
+
Add row below
|
|
9154
|
+
</button>
|
|
9155
|
+
<button *ngIf="!state.isReadOnly() && state.canArmFieldInsertBeforeSelection()" type="button"
|
|
9156
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
9157
|
+
(click)="armFieldInsertBeforeSelection()">
|
|
9158
|
+
Next widget above
|
|
9159
|
+
</button>
|
|
9160
|
+
<button *ngIf="!state.isReadOnly() && state.canArmFieldInsertAfterSelection()" type="button"
|
|
9161
|
+
class="w-full px-3 py-2 text-left hover:bg-slate-50 text-text-primary"
|
|
9162
|
+
(click)="armFieldInsertAfterSelection()">
|
|
9163
|
+
Next widget below
|
|
9164
|
+
</button>
|
|
9165
|
+
</div>
|
|
9166
|
+
</div>
|
|
8498
9167
|
<div class="h-px bg-border-default my-1"></div>
|
|
8499
9168
|
<button *ngIf="!state.isReadOnly()" type="button"
|
|
8500
9169
|
class="w-full px-3 py-2 text-left hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed text-text-primary"
|
|
@@ -9606,18 +10275,11 @@ class DynamicPropertiesComponent {
|
|
|
9606
10275
|
onPropertyChange;
|
|
9607
10276
|
designerCtx = inject(DesignerContext);
|
|
9608
10277
|
validatorTypeOptions = [
|
|
9609
|
-
{ label: '
|
|
9610
|
-
{ label: '
|
|
9611
|
-
{ label: 'Min Value', value: 'min' },
|
|
9612
|
-
{ label: 'Max Value', value: 'max' },
|
|
9613
|
-
{ label: 'Min Length', value: 'minLength' },
|
|
9614
|
-
{ label: 'Max Length', value: 'maxLength' },
|
|
9615
|
-
{ label: 'Pattern', value: 'pattern' }
|
|
10278
|
+
{ label: 'Built-in', value: 'builtin' },
|
|
10279
|
+
{ label: 'Expression', value: 'expression' }
|
|
9616
10280
|
];
|
|
9617
|
-
|
|
9618
|
-
{ label: '
|
|
9619
|
-
{ label: 'Field Equals', value: 'equals' },
|
|
9620
|
-
{ label: 'Field Not Equals', value: 'notEquals' }
|
|
10281
|
+
builtinValidatorOptions = [
|
|
10282
|
+
{ label: 'Email', value: 'email' }
|
|
9621
10283
|
];
|
|
9622
10284
|
get properties() {
|
|
9623
10285
|
if (!this.config)
|
|
@@ -9954,18 +10616,15 @@ class DynamicPropertiesComponent {
|
|
|
9954
10616
|
addValidator(path) {
|
|
9955
10617
|
if (this.readOnly)
|
|
9956
10618
|
return;
|
|
9957
|
-
const validators = this.getValue(path) || [];
|
|
9958
|
-
validators.push(
|
|
9959
|
-
name: 'required',
|
|
9960
|
-
message: 'This field is required'
|
|
9961
|
-
});
|
|
10619
|
+
const validators = [...(this.getValue(path) || [])];
|
|
10620
|
+
validators.push(this.createDefaultValidationRule());
|
|
9962
10621
|
this.setValue(path, validators);
|
|
9963
10622
|
this.handleFieldChange();
|
|
9964
10623
|
}
|
|
9965
10624
|
removeValidator(path, index) {
|
|
9966
10625
|
if (this.readOnly)
|
|
9967
10626
|
return;
|
|
9968
|
-
const validators = this.getValue(path) || [];
|
|
10627
|
+
const validators = [...(this.getValue(path) || [])];
|
|
9969
10628
|
validators.splice(index, 1);
|
|
9970
10629
|
this.setValue(path, validators);
|
|
9971
10630
|
this.handleFieldChange();
|
|
@@ -9973,57 +10632,47 @@ class DynamicPropertiesComponent {
|
|
|
9973
10632
|
updateValidator(path, index, field, value) {
|
|
9974
10633
|
if (this.readOnly)
|
|
9975
10634
|
return;
|
|
9976
|
-
const validators = this.getValue(path) || [];
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9980
|
-
|
|
9981
|
-
|
|
9982
|
-
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
10635
|
+
const validators = [...(this.getValue(path) || [])];
|
|
10636
|
+
const currentRule = validators[index];
|
|
10637
|
+
if (!currentRule) {
|
|
10638
|
+
return;
|
|
10639
|
+
}
|
|
10640
|
+
let nextRule = { ...currentRule };
|
|
10641
|
+
if (field === 'type') {
|
|
10642
|
+
nextRule = value === 'expression'
|
|
10643
|
+
? {
|
|
10644
|
+
type: 'expression',
|
|
10645
|
+
expression: 'return true;',
|
|
10646
|
+
message: 'Validation failed.'
|
|
10647
|
+
}
|
|
10648
|
+
: this.createDefaultValidationRule();
|
|
10649
|
+
}
|
|
10650
|
+
else if (field === 'name') {
|
|
10651
|
+
nextRule.name = String(value);
|
|
10652
|
+
nextRule.message = this.defaultValidationMessage(nextRule);
|
|
10653
|
+
}
|
|
10654
|
+
else if (field === 'expression') {
|
|
10655
|
+
nextRule.expression = String(value);
|
|
9993
10656
|
}
|
|
10657
|
+
else if (field === 'message') {
|
|
10658
|
+
nextRule.message = String(value);
|
|
10659
|
+
}
|
|
10660
|
+
validators[index] = nextRule;
|
|
10661
|
+
this.setValue(path, validators);
|
|
10662
|
+
this.handleFieldChange();
|
|
9994
10663
|
}
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
minLength: 'Min Length',
|
|
10001
|
-
maxLength: 'Max Length',
|
|
10002
|
-
pattern: 'Regex Pattern',
|
|
10003
|
-
email: 'Email Address'
|
|
10664
|
+
createDefaultValidationRule() {
|
|
10665
|
+
return {
|
|
10666
|
+
type: 'builtin',
|
|
10667
|
+
name: 'email',
|
|
10668
|
+
message: 'Enter a valid email address.'
|
|
10004
10669
|
};
|
|
10005
|
-
return labels[name] || name;
|
|
10006
|
-
}
|
|
10007
|
-
// Conditional Logic Methods
|
|
10008
|
-
enableConditional(path) {
|
|
10009
|
-
if (this.readOnly)
|
|
10010
|
-
return;
|
|
10011
|
-
this.setValue(path, {
|
|
10012
|
-
action: 'visible',
|
|
10013
|
-
operator: 'eq'
|
|
10014
|
-
});
|
|
10015
10670
|
}
|
|
10016
|
-
|
|
10017
|
-
if (
|
|
10018
|
-
return;
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
updateConditional(path, field, value) {
|
|
10022
|
-
if (this.readOnly)
|
|
10023
|
-
return;
|
|
10024
|
-
const condition = this.getValue(path) || {};
|
|
10025
|
-
condition[field] = value;
|
|
10026
|
-
this.setValue(path, condition);
|
|
10671
|
+
defaultValidationMessage(rule) {
|
|
10672
|
+
if (rule.type === 'builtin' && rule.name === 'email') {
|
|
10673
|
+
return 'Enter a valid email address.';
|
|
10674
|
+
}
|
|
10675
|
+
return 'Validation failed.';
|
|
10027
10676
|
}
|
|
10028
10677
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: DynamicPropertiesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10029
10678
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: DynamicPropertiesComponent, isStandalone: true, selector: "app-dynamic-properties", inputs: { onPropertyChange: "onPropertyChange", config: "config", readOnly: "readOnly", includeSections: "includeSections", excludeSections: "excludeSections", allFields: "allFields" }, outputs: { configChange: "configChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
@@ -10180,16 +10829,27 @@ class DynamicPropertiesComponent {
|
|
|
10180
10829
|
</div>
|
|
10181
10830
|
<div class="space-y-2">
|
|
10182
10831
|
<div *ngFor="let val of getValue(field.key) || []; let i = index" class="flex items-center gap-2 p-2 bg-white rounded border border-gray-200">
|
|
10183
|
-
<select [
|
|
10184
|
-
(ngModelChange)="
|
|
10832
|
+
<select [ngModel]="val.type || 'builtin'"
|
|
10833
|
+
(ngModelChange)="updateValidator(field.key, i, 'type', $event)"
|
|
10185
10834
|
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10186
10835
|
<option *ngFor="let option of validatorTypeOptions" [value]="option.value">{{ option.label }}</option>
|
|
10187
10836
|
</select>
|
|
10188
|
-
<
|
|
10837
|
+
<select *ngIf="(val.type || 'builtin') === 'builtin'"
|
|
10838
|
+
[ngModel]="val.name || 'email'"
|
|
10839
|
+
(ngModelChange)="updateValidator(field.key, i, 'name', $event)"
|
|
10840
|
+
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10841
|
+
<option *ngFor="let option of builtinValidatorOptions" [value]="option.value">{{ option.label }}</option>
|
|
10842
|
+
</select>
|
|
10843
|
+
<input *ngIf="val.type === 'expression'"
|
|
10189
10844
|
type="text"
|
|
10190
|
-
[
|
|
10191
|
-
(
|
|
10192
|
-
placeholder="
|
|
10845
|
+
[ngModel]="val.expression || ''"
|
|
10846
|
+
(ngModelChange)="updateValidator(field.key, i, 'expression', $event)"
|
|
10847
|
+
placeholder="return true;"
|
|
10848
|
+
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10849
|
+
<input type="text"
|
|
10850
|
+
[ngModel]="val.message || ''"
|
|
10851
|
+
(ngModelChange)="updateValidator(field.key, i, 'message', $event)"
|
|
10852
|
+
placeholder="Validation message"
|
|
10193
10853
|
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10194
10854
|
<button type="button"
|
|
10195
10855
|
(click)="removeValidator(field.key, i)"
|
|
@@ -10201,36 +10861,6 @@ class DynamicPropertiesComponent {
|
|
|
10201
10861
|
</div>
|
|
10202
10862
|
</ui-field-wrapper>
|
|
10203
10863
|
|
|
10204
|
-
<!-- Conditional Editor -->
|
|
10205
|
-
<ui-field-wrapper *ngIf="field.type === 'conditional-editor'" [label]="field.label || ''" [helpText]="field.helpText || ''">
|
|
10206
|
-
<div class="w-full border border-gray-200 rounded-lg p-3 bg-gray-50">
|
|
10207
|
-
<div class="flex flex-col gap-2">
|
|
10208
|
-
<select [ngModel]="getValue(field.key + '.type')"
|
|
10209
|
-
(ngModelChange)="setValue(field.key + '.type', $event); handleFieldChange()"
|
|
10210
|
-
class="h-8 w-full rounded border border-gray-300 bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10211
|
-
<option *ngFor="let option of conditionalTypeOptions" [value]="option.value">{{ option.label }}</option>
|
|
10212
|
-
</select>
|
|
10213
|
-
<div *ngIf="getValue(field.key + '.type') !== 'always'" class="flex gap-2">
|
|
10214
|
-
<input type="text"
|
|
10215
|
-
[ngModel]="getValue(field.key + '.field')"
|
|
10216
|
-
(ngModelChange)="setValue(field.key + '.field', $event)"
|
|
10217
|
-
(blur)="handleFieldChange()"
|
|
10218
|
-
placeholder="Field Name"
|
|
10219
|
-
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10220
|
-
<input type="text"
|
|
10221
|
-
[ngModel]="getValue(field.key + '.value')"
|
|
10222
|
-
(ngModelChange)="setValue(field.key + '.value', $event)"
|
|
10223
|
-
(blur)="handleFieldChange()"
|
|
10224
|
-
placeholder="Value"
|
|
10225
|
-
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10226
|
-
</div>
|
|
10227
|
-
</div>
|
|
10228
|
-
<div *ngIf="getValue(field.key + '.type') === 'always'" class="text-xs text-gray-400 mt-2">
|
|
10229
|
-
Field always visible.
|
|
10230
|
-
</div>
|
|
10231
|
-
</div>
|
|
10232
|
-
</ui-field-wrapper>
|
|
10233
|
-
|
|
10234
10864
|
<!-- Field Reference -->
|
|
10235
10865
|
<ui-field-wrapper *ngIf="field.type === 'field-reference'" [label]="field.label || ''" [helpText]="field.helpText || ''">
|
|
10236
10866
|
<select [ngModel]="getValue(field.key) || ''"
|
|
@@ -10492,16 +11122,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
10492
11122
|
</div>
|
|
10493
11123
|
<div class="space-y-2">
|
|
10494
11124
|
<div *ngFor="let val of getValue(field.key) || []; let i = index" class="flex items-center gap-2 p-2 bg-white rounded border border-gray-200">
|
|
10495
|
-
<select [
|
|
10496
|
-
(ngModelChange)="
|
|
11125
|
+
<select [ngModel]="val.type || 'builtin'"
|
|
11126
|
+
(ngModelChange)="updateValidator(field.key, i, 'type', $event)"
|
|
10497
11127
|
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10498
11128
|
<option *ngFor="let option of validatorTypeOptions" [value]="option.value">{{ option.label }}</option>
|
|
10499
11129
|
</select>
|
|
10500
|
-
<
|
|
11130
|
+
<select *ngIf="(val.type || 'builtin') === 'builtin'"
|
|
11131
|
+
[ngModel]="val.name || 'email'"
|
|
11132
|
+
(ngModelChange)="updateValidator(field.key, i, 'name', $event)"
|
|
11133
|
+
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
11134
|
+
<option *ngFor="let option of builtinValidatorOptions" [value]="option.value">{{ option.label }}</option>
|
|
11135
|
+
</select>
|
|
11136
|
+
<input *ngIf="val.type === 'expression'"
|
|
10501
11137
|
type="text"
|
|
10502
|
-
[
|
|
10503
|
-
(
|
|
10504
|
-
placeholder="
|
|
11138
|
+
[ngModel]="val.expression || ''"
|
|
11139
|
+
(ngModelChange)="updateValidator(field.key, i, 'expression', $event)"
|
|
11140
|
+
placeholder="return true;"
|
|
11141
|
+
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
11142
|
+
<input type="text"
|
|
11143
|
+
[ngModel]="val.message || ''"
|
|
11144
|
+
(ngModelChange)="updateValidator(field.key, i, 'message', $event)"
|
|
11145
|
+
placeholder="Validation message"
|
|
10505
11146
|
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10506
11147
|
<button type="button"
|
|
10507
11148
|
(click)="removeValidator(field.key, i)"
|
|
@@ -10513,36 +11154,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
10513
11154
|
</div>
|
|
10514
11155
|
</ui-field-wrapper>
|
|
10515
11156
|
|
|
10516
|
-
<!-- Conditional Editor -->
|
|
10517
|
-
<ui-field-wrapper *ngIf="field.type === 'conditional-editor'" [label]="field.label || ''" [helpText]="field.helpText || ''">
|
|
10518
|
-
<div class="w-full border border-gray-200 rounded-lg p-3 bg-gray-50">
|
|
10519
|
-
<div class="flex flex-col gap-2">
|
|
10520
|
-
<select [ngModel]="getValue(field.key + '.type')"
|
|
10521
|
-
(ngModelChange)="setValue(field.key + '.type', $event); handleFieldChange()"
|
|
10522
|
-
class="h-8 w-full rounded border border-gray-300 bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10523
|
-
<option *ngFor="let option of conditionalTypeOptions" [value]="option.value">{{ option.label }}</option>
|
|
10524
|
-
</select>
|
|
10525
|
-
<div *ngIf="getValue(field.key + '.type') !== 'always'" class="flex gap-2">
|
|
10526
|
-
<input type="text"
|
|
10527
|
-
[ngModel]="getValue(field.key + '.field')"
|
|
10528
|
-
(ngModelChange)="setValue(field.key + '.field', $event)"
|
|
10529
|
-
(blur)="handleFieldChange()"
|
|
10530
|
-
placeholder="Field Name"
|
|
10531
|
-
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10532
|
-
<input type="text"
|
|
10533
|
-
[ngModel]="getValue(field.key + '.value')"
|
|
10534
|
-
(ngModelChange)="setValue(field.key + '.value', $event)"
|
|
10535
|
-
(blur)="handleFieldChange()"
|
|
10536
|
-
placeholder="Value"
|
|
10537
|
-
class="flex-1 h-8 px-2 text-sm rounded border border-gray-300 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
10538
|
-
</div>
|
|
10539
|
-
</div>
|
|
10540
|
-
<div *ngIf="getValue(field.key + '.type') === 'always'" class="text-xs text-gray-400 mt-2">
|
|
10541
|
-
Field always visible.
|
|
10542
|
-
</div>
|
|
10543
|
-
</div>
|
|
10544
|
-
</ui-field-wrapper>
|
|
10545
|
-
|
|
10546
11157
|
<!-- Field Reference -->
|
|
10547
11158
|
<ui-field-wrapper *ngIf="field.type === 'field-reference'" [label]="field.label || ''" [helpText]="field.helpText || ''">
|
|
10548
11159
|
<select [ngModel]="getValue(field.key) || ''"
|
|
@@ -13616,6 +14227,161 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
13616
14227
|
`, styles: [".inspector-input{height:1.75rem;padding-left:.5rem;padding-right:.5rem;font-size:.75rem;line-height:1rem;background-color:var(--color-input-dark);border:1px solid var(--color-border-dark);border-radius:var(--radius-md);color:var(--color-ink-700)}.inspector-input:focus{outline:none;border-color:var(--color-primary-blue)}.inspector-input-with-unit{display:flex;align-items:center;background-color:var(--color-input-dark);border:1px solid var(--color-border-dark);border-radius:var(--radius-md);overflow:hidden}.inspector-number-input{height:1.5rem;padding-left:.5rem;padding-right:.5rem;background-color:transparent;border:none;font-size:.75rem;line-height:1rem;color:var(--color-ink-700)}.inspector-number-input:focus{outline:none}.inspector-unit{font-size:10px;color:var(--color-ink-400);padding-left:.25rem;padding-right:.25rem;background-color:var(--color-input-dark);height:1.5rem;display:flex;align-items:center;border-left:1px solid var(--color-border-dark)}\n"] }]
|
|
13617
14228
|
}] });
|
|
13618
14229
|
|
|
14230
|
+
class InspectorTransformSectionComponent {
|
|
14231
|
+
style = input({});
|
|
14232
|
+
styleChange = output();
|
|
14233
|
+
numberValue(key, fallback) {
|
|
14234
|
+
const value = this.style()?.[key];
|
|
14235
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
14236
|
+
return value;
|
|
14237
|
+
}
|
|
14238
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
14239
|
+
const parsed = Number(value);
|
|
14240
|
+
if (Number.isFinite(parsed)) {
|
|
14241
|
+
return parsed;
|
|
14242
|
+
}
|
|
14243
|
+
}
|
|
14244
|
+
return fallback;
|
|
14245
|
+
}
|
|
14246
|
+
updateTransform(key, value) {
|
|
14247
|
+
const parsed = typeof value === 'number' ? value : Number(value);
|
|
14248
|
+
if (!Number.isFinite(parsed)) {
|
|
14249
|
+
return;
|
|
14250
|
+
}
|
|
14251
|
+
this.styleChange.emit({
|
|
14252
|
+
...(this.style() ?? {}),
|
|
14253
|
+
[key]: parsed
|
|
14254
|
+
});
|
|
14255
|
+
}
|
|
14256
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: InspectorTransformSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14257
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.17", type: InspectorTransformSectionComponent, isStandalone: true, selector: "inspector-transform-section", inputs: { style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { styleChange: "styleChange" }, ngImport: i0, template: `
|
|
14258
|
+
<div class="flex flex-col gap-3">
|
|
14259
|
+
<ui-range-number
|
|
14260
|
+
label="Translate X"
|
|
14261
|
+
prefix="X"
|
|
14262
|
+
hint="px"
|
|
14263
|
+
helpText="Move the widget horizontally."
|
|
14264
|
+
[min]="-200"
|
|
14265
|
+
[max]="200"
|
|
14266
|
+
[step]="1"
|
|
14267
|
+
[value]="numberValue('transformX', 0)"
|
|
14268
|
+
(valueChange)="updateTransform('transformX', $event)">
|
|
14269
|
+
</ui-range-number>
|
|
14270
|
+
|
|
14271
|
+
<ui-range-number
|
|
14272
|
+
label="Translate Y"
|
|
14273
|
+
prefix="Y"
|
|
14274
|
+
hint="px"
|
|
14275
|
+
helpText="Move the widget vertically."
|
|
14276
|
+
[min]="-200"
|
|
14277
|
+
[max]="200"
|
|
14278
|
+
[step]="1"
|
|
14279
|
+
[value]="numberValue('transformY', 0)"
|
|
14280
|
+
(valueChange)="updateTransform('transformY', $event)">
|
|
14281
|
+
</ui-range-number>
|
|
14282
|
+
|
|
14283
|
+
<ui-range-number
|
|
14284
|
+
label="Translate Z"
|
|
14285
|
+
prefix="Z"
|
|
14286
|
+
hint="px"
|
|
14287
|
+
helpText="Move the widget on the z-axis for 3D transforms."
|
|
14288
|
+
[min]="-200"
|
|
14289
|
+
[max]="200"
|
|
14290
|
+
[step]="1"
|
|
14291
|
+
[value]="numberValue('transformZ', 0)"
|
|
14292
|
+
(valueChange)="updateTransform('transformZ', $event)">
|
|
14293
|
+
</ui-range-number>
|
|
14294
|
+
|
|
14295
|
+
<ui-input
|
|
14296
|
+
label="Rotate"
|
|
14297
|
+
hint="deg"
|
|
14298
|
+
helpText="Rotate the widget in degrees."
|
|
14299
|
+
type="number"
|
|
14300
|
+
[step]="1"
|
|
14301
|
+
[model]="numberValue('rotate', 0)"
|
|
14302
|
+
(modelChange)="updateTransform('rotate', $event)">
|
|
14303
|
+
</ui-input>
|
|
14304
|
+
|
|
14305
|
+
<ui-input
|
|
14306
|
+
label="Scale"
|
|
14307
|
+
helpText="Scale the widget uniformly."
|
|
14308
|
+
type="number"
|
|
14309
|
+
[min]="0"
|
|
14310
|
+
[step]="0.1"
|
|
14311
|
+
[model]="numberValue('scale', 1)"
|
|
14312
|
+
(modelChange)="updateTransform('scale', $event)">
|
|
14313
|
+
</ui-input>
|
|
14314
|
+
</div>
|
|
14315
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: UiInputComponent, selector: "ui-input", inputs: ["label", "hint", "helpText", "placeholder", "type", "min", "max", "step", "model"], outputs: ["modelChange", "onBlur"] }, { kind: "component", type: UiRangeNumberComponent, selector: "ui-range-number", inputs: ["label", "hint", "helpText", "prefix", "min", "max", "step", "value"], outputs: ["valueChange"] }] });
|
|
14316
|
+
}
|
|
14317
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: InspectorTransformSectionComponent, decorators: [{
|
|
14318
|
+
type: Component,
|
|
14319
|
+
args: [{
|
|
14320
|
+
selector: 'inspector-transform-section',
|
|
14321
|
+
standalone: true,
|
|
14322
|
+
imports: [CommonModule, UiInputComponent, UiRangeNumberComponent],
|
|
14323
|
+
template: `
|
|
14324
|
+
<div class="flex flex-col gap-3">
|
|
14325
|
+
<ui-range-number
|
|
14326
|
+
label="Translate X"
|
|
14327
|
+
prefix="X"
|
|
14328
|
+
hint="px"
|
|
14329
|
+
helpText="Move the widget horizontally."
|
|
14330
|
+
[min]="-200"
|
|
14331
|
+
[max]="200"
|
|
14332
|
+
[step]="1"
|
|
14333
|
+
[value]="numberValue('transformX', 0)"
|
|
14334
|
+
(valueChange)="updateTransform('transformX', $event)">
|
|
14335
|
+
</ui-range-number>
|
|
14336
|
+
|
|
14337
|
+
<ui-range-number
|
|
14338
|
+
label="Translate Y"
|
|
14339
|
+
prefix="Y"
|
|
14340
|
+
hint="px"
|
|
14341
|
+
helpText="Move the widget vertically."
|
|
14342
|
+
[min]="-200"
|
|
14343
|
+
[max]="200"
|
|
14344
|
+
[step]="1"
|
|
14345
|
+
[value]="numberValue('transformY', 0)"
|
|
14346
|
+
(valueChange)="updateTransform('transformY', $event)">
|
|
14347
|
+
</ui-range-number>
|
|
14348
|
+
|
|
14349
|
+
<ui-range-number
|
|
14350
|
+
label="Translate Z"
|
|
14351
|
+
prefix="Z"
|
|
14352
|
+
hint="px"
|
|
14353
|
+
helpText="Move the widget on the z-axis for 3D transforms."
|
|
14354
|
+
[min]="-200"
|
|
14355
|
+
[max]="200"
|
|
14356
|
+
[step]="1"
|
|
14357
|
+
[value]="numberValue('transformZ', 0)"
|
|
14358
|
+
(valueChange)="updateTransform('transformZ', $event)">
|
|
14359
|
+
</ui-range-number>
|
|
14360
|
+
|
|
14361
|
+
<ui-input
|
|
14362
|
+
label="Rotate"
|
|
14363
|
+
hint="deg"
|
|
14364
|
+
helpText="Rotate the widget in degrees."
|
|
14365
|
+
type="number"
|
|
14366
|
+
[step]="1"
|
|
14367
|
+
[model]="numberValue('rotate', 0)"
|
|
14368
|
+
(modelChange)="updateTransform('rotate', $event)">
|
|
14369
|
+
</ui-input>
|
|
14370
|
+
|
|
14371
|
+
<ui-input
|
|
14372
|
+
label="Scale"
|
|
14373
|
+
helpText="Scale the widget uniformly."
|
|
14374
|
+
type="number"
|
|
14375
|
+
[min]="0"
|
|
14376
|
+
[step]="0.1"
|
|
14377
|
+
[model]="numberValue('scale', 1)"
|
|
14378
|
+
(modelChange)="updateTransform('scale', $event)">
|
|
14379
|
+
</ui-input>
|
|
14380
|
+
</div>
|
|
14381
|
+
`
|
|
14382
|
+
}]
|
|
14383
|
+
}] });
|
|
14384
|
+
|
|
13619
14385
|
const DEDICATED_STYLE_KEYS = new Set([
|
|
13620
14386
|
'alignItems',
|
|
13621
14387
|
'alignSelf',
|
|
@@ -13684,7 +14450,13 @@ const DEDICATED_STYLE_KEYS = new Set([
|
|
|
13684
14450
|
'right',
|
|
13685
14451
|
'textAlign',
|
|
13686
14452
|
'textDecoration',
|
|
14453
|
+
'transform',
|
|
14454
|
+
'transformX',
|
|
14455
|
+
'transformY',
|
|
14456
|
+
'transformZ',
|
|
13687
14457
|
'top',
|
|
14458
|
+
'rotate',
|
|
14459
|
+
'scale',
|
|
13688
14460
|
'width',
|
|
13689
14461
|
'zIndex'
|
|
13690
14462
|
]);
|
|
@@ -14668,6 +15440,8 @@ class DataPanelComponent {
|
|
|
14668
15440
|
selectionFieldId;
|
|
14669
15441
|
selectionMatchPath;
|
|
14670
15442
|
childRowsPath;
|
|
15443
|
+
formatNumericOptionLabels = false;
|
|
15444
|
+
optionLabelPrefixPath;
|
|
14671
15445
|
rootPathOptions = [];
|
|
14672
15446
|
rowPathOptions = [];
|
|
14673
15447
|
// Value/Image Config
|
|
@@ -14908,6 +15682,8 @@ class DataPanelComponent {
|
|
|
14908
15682
|
this.selectionFieldId = undefined;
|
|
14909
15683
|
this.selectionMatchPath = undefined;
|
|
14910
15684
|
this.childRowsPath = undefined;
|
|
15685
|
+
this.formatNumericOptionLabels = false;
|
|
15686
|
+
this.optionLabelPrefixPath = undefined;
|
|
14911
15687
|
this.rootPathOptions = [];
|
|
14912
15688
|
this.rowPathOptions = [];
|
|
14913
15689
|
}
|
|
@@ -14941,13 +15717,15 @@ class DataPanelComponent {
|
|
|
14941
15717
|
this.staticOptions = (d.staticOptions || []).map(option => ({ ...option }));
|
|
14942
15718
|
this.staticValue = d.staticValue !== undefined ? d.staticValue : this.readScalarTargetValue();
|
|
14943
15719
|
this.selectedSourceId = d.datasourceId;
|
|
14944
|
-
this.labelKey = d.
|
|
14945
|
-
this.valueKey = d.
|
|
15720
|
+
this.labelKey = d.labelKey;
|
|
15721
|
+
this.valueKey = d.valueKey;
|
|
14946
15722
|
this.rowsPath = d.rowsPath;
|
|
14947
15723
|
this.rowSelectionMode = d.rowSelectionMode ?? 'first';
|
|
14948
15724
|
this.selectionFieldId = d.selectionFieldId;
|
|
14949
15725
|
this.selectionMatchPath = d.selectionMatchPath;
|
|
14950
15726
|
this.childRowsPath = d.childRowsPath;
|
|
15727
|
+
this.formatNumericOptionLabels = d.formatNumericOptionLabels === true;
|
|
15728
|
+
this.optionLabelPrefixPath = d.optionLabelPrefixPath;
|
|
14951
15729
|
// Search
|
|
14952
15730
|
this.searchEnabled = !!d.searchEnabled;
|
|
14953
15731
|
this.optionsLimit = d.optionsLimit;
|
|
@@ -14999,8 +15777,12 @@ class DataPanelComponent {
|
|
|
14999
15777
|
labelKey: this.sourceType === 'source' ? this.labelKey : undefined,
|
|
15000
15778
|
valueKey: this.sourceType === 'source' ? this.valueKey : undefined,
|
|
15001
15779
|
rowsPath: this.sourceType === 'source' ? this.normalizedRowsPath() : undefined,
|
|
15002
|
-
|
|
15003
|
-
|
|
15780
|
+
formatNumericOptionLabels: this.shouldPersistOptionLabelFormatting()
|
|
15781
|
+
? this.formatNumericOptionLabels
|
|
15782
|
+
: undefined,
|
|
15783
|
+
optionLabelPrefixPath: this.shouldPersistOptionLabelFormatting()
|
|
15784
|
+
? this.optionLabelPrefixPath
|
|
15785
|
+
: undefined,
|
|
15004
15786
|
rowSelectionMode: this.sourceType === 'source' && this.bindingShape === 'scalar' && this.rowSelectionMode === 'selected'
|
|
15005
15787
|
? 'selected'
|
|
15006
15788
|
: this.sourceType === 'source' && this.bindingShape === 'list' && this.rowSelectionMode === 'selected'
|
|
@@ -15109,6 +15891,9 @@ class DataPanelComponent {
|
|
|
15109
15891
|
showOptionMappingControls() {
|
|
15110
15892
|
return this.sourceType === 'source' && this.widgetType !== 'table' && this.usesOptionMapping();
|
|
15111
15893
|
}
|
|
15894
|
+
showOptionLabelFormattingControls() {
|
|
15895
|
+
return this.widgetType === 'select' && this.usesOptionMapping();
|
|
15896
|
+
}
|
|
15112
15897
|
showStaticOptionsEditor() {
|
|
15113
15898
|
return this.sourceType === 'static' && this.widgetType !== 'table' && this.usesOptionMapping();
|
|
15114
15899
|
}
|
|
@@ -15159,17 +15944,8 @@ class DataPanelComponent {
|
|
|
15159
15944
|
const sample = this.extractPreviewRows(this.previewRows, this.effectiveRowsPath())[0];
|
|
15160
15945
|
return sample ? collectArrayPaths(sample) : [];
|
|
15161
15946
|
}
|
|
15162
|
-
|
|
15163
|
-
|
|
15164
|
-
return false;
|
|
15165
|
-
return !!this.normalizedRowsPath() || !!this.childRowsPath || hasPathSyntax$1(this.labelKey);
|
|
15166
|
-
}
|
|
15167
|
-
shouldPersistStructuredValuePath() {
|
|
15168
|
-
if (!this.valueKey)
|
|
15169
|
-
return false;
|
|
15170
|
-
if (!this.usesOptionMapping())
|
|
15171
|
-
return true;
|
|
15172
|
-
return !!this.normalizedRowsPath() || !!this.childRowsPath || hasPathSyntax$1(this.valueKey);
|
|
15947
|
+
shouldPersistOptionLabelFormatting() {
|
|
15948
|
+
return this.widgetType === 'select' && this.usesOptionMapping();
|
|
15173
15949
|
}
|
|
15174
15950
|
usesOptionMapping() {
|
|
15175
15951
|
return this.bindingShape === 'list' || this.widgetType === 'search';
|
|
@@ -15490,6 +16266,38 @@ class DataPanelComponent {
|
|
|
15490
16266
|
</div>
|
|
15491
16267
|
</div>
|
|
15492
16268
|
|
|
16269
|
+
<div class="rounded-lg border border-gray-200 bg-white p-3" *ngIf="showOptionLabelFormattingControls()">
|
|
16270
|
+
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500">Amount Display</div>
|
|
16271
|
+
<div class="mt-1 text-[11px] text-gray-500">Format the visible dropdown label without changing the stored option value.</div>
|
|
16272
|
+
|
|
16273
|
+
<label class="mt-3 flex items-center gap-2 text-sm text-gray-700">
|
|
16274
|
+
<input
|
|
16275
|
+
type="checkbox"
|
|
16276
|
+
[checked]="formatNumericOptionLabels"
|
|
16277
|
+
(change)="formatNumericOptionLabels = $any($event.target).checked; emitChange()"
|
|
16278
|
+
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
|
|
16279
|
+
<span>Show thousand separators for numeric labels</span>
|
|
16280
|
+
</label>
|
|
16281
|
+
|
|
16282
|
+
<div class="mt-3 flex flex-col gap-1">
|
|
16283
|
+
<label class="text-xs font-medium text-gray-500">Prefix Key</label>
|
|
16284
|
+
<input
|
|
16285
|
+
[attr.list]="'prefix-cols-' + config.id"
|
|
16286
|
+
[(ngModel)]="optionLabelPrefixPath"
|
|
16287
|
+
(ngModelChange)="emitChange()"
|
|
16288
|
+
[placeholder]="effectiveRowsPath() ? 'e.g. currency or meta.currency' : 'e.g. currency'"
|
|
16289
|
+
class="h-8 w-full rounded-md border border-gray-300 px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
16290
|
+
<datalist [id]="'prefix-cols-' + config.id">
|
|
16291
|
+
<option *ngFor="let col of sourceColumns" [value]="col.name"></option>
|
|
16292
|
+
<option *ngFor="let path of availableRowPaths()" [value]="path"></option>
|
|
16293
|
+
<option *ngFor="let path of availableRootPaths()" [value]="path"></option>
|
|
16294
|
+
</datalist>
|
|
16295
|
+
<p class="text-[10px] text-gray-400">
|
|
16296
|
+
Reads a display-only prefix from the datasource or event payload. The select still stores only the mapped value.
|
|
16297
|
+
</p>
|
|
16298
|
+
</div>
|
|
16299
|
+
</div>
|
|
16300
|
+
|
|
15493
16301
|
<div class="rounded-lg border border-gray-200 bg-white p-3" *ngIf="showScalarMappingControls()">
|
|
15494
16302
|
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500">Value Mapping</div>
|
|
15495
16303
|
<div class="mt-1 text-[11px] text-gray-500">Choose the value path and how this field picks a row from the datasource.</div>
|
|
@@ -16077,6 +16885,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
16077
16885
|
</div>
|
|
16078
16886
|
</div>
|
|
16079
16887
|
|
|
16888
|
+
<div class="rounded-lg border border-gray-200 bg-white p-3" *ngIf="showOptionLabelFormattingControls()">
|
|
16889
|
+
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500">Amount Display</div>
|
|
16890
|
+
<div class="mt-1 text-[11px] text-gray-500">Format the visible dropdown label without changing the stored option value.</div>
|
|
16891
|
+
|
|
16892
|
+
<label class="mt-3 flex items-center gap-2 text-sm text-gray-700">
|
|
16893
|
+
<input
|
|
16894
|
+
type="checkbox"
|
|
16895
|
+
[checked]="formatNumericOptionLabels"
|
|
16896
|
+
(change)="formatNumericOptionLabels = $any($event.target).checked; emitChange()"
|
|
16897
|
+
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
|
|
16898
|
+
<span>Show thousand separators for numeric labels</span>
|
|
16899
|
+
</label>
|
|
16900
|
+
|
|
16901
|
+
<div class="mt-3 flex flex-col gap-1">
|
|
16902
|
+
<label class="text-xs font-medium text-gray-500">Prefix Key</label>
|
|
16903
|
+
<input
|
|
16904
|
+
[attr.list]="'prefix-cols-' + config.id"
|
|
16905
|
+
[(ngModel)]="optionLabelPrefixPath"
|
|
16906
|
+
(ngModelChange)="emitChange()"
|
|
16907
|
+
[placeholder]="effectiveRowsPath() ? 'e.g. currency or meta.currency' : 'e.g. currency'"
|
|
16908
|
+
class="h-8 w-full rounded-md border border-gray-300 px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none">
|
|
16909
|
+
<datalist [id]="'prefix-cols-' + config.id">
|
|
16910
|
+
<option *ngFor="let col of sourceColumns" [value]="col.name"></option>
|
|
16911
|
+
<option *ngFor="let path of availableRowPaths()" [value]="path"></option>
|
|
16912
|
+
<option *ngFor="let path of availableRootPaths()" [value]="path"></option>
|
|
16913
|
+
</datalist>
|
|
16914
|
+
<p class="text-[10px] text-gray-400">
|
|
16915
|
+
Reads a display-only prefix from the datasource or event payload. The select still stores only the mapped value.
|
|
16916
|
+
</p>
|
|
16917
|
+
</div>
|
|
16918
|
+
</div>
|
|
16919
|
+
|
|
16080
16920
|
<div class="rounded-lg border border-gray-200 bg-white p-3" *ngIf="showScalarMappingControls()">
|
|
16081
16921
|
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500">Value Mapping</div>
|
|
16082
16922
|
<div class="mt-1 text-[11px] text-gray-500">Choose the value path and how this field picks a row from the datasource.</div>
|
|
@@ -17334,6 +18174,15 @@ class WidgetInspectorComponent {
|
|
|
17334
18174
|
</div>
|
|
17335
18175
|
</ui-accordion>
|
|
17336
18176
|
|
|
18177
|
+
<ui-accordion title="Transform" [expanded]="false">
|
|
18178
|
+
<div [class.pointer-events-none]="readOnly()" [class.opacity-60]="readOnly()">
|
|
18179
|
+
<inspector-transform-section
|
|
18180
|
+
[style]="currentStyle()"
|
|
18181
|
+
(styleChange)="onStyleChange($event)">
|
|
18182
|
+
</inspector-transform-section>
|
|
18183
|
+
</div>
|
|
18184
|
+
</ui-accordion>
|
|
18185
|
+
|
|
17337
18186
|
<ui-accordion title="Position" [expanded]="false">
|
|
17338
18187
|
<div [class.pointer-events-none]="readOnly()" [class.opacity-60]="readOnly()">
|
|
17339
18188
|
<inspector-position-section
|
|
@@ -17366,7 +18215,7 @@ class WidgetInspectorComponent {
|
|
|
17366
18215
|
[config]="inspectorField()"
|
|
17367
18216
|
[allFields]="stateService.getSelectedScopeFields()"
|
|
17368
18217
|
[readOnly]="readOnly()"
|
|
17369
|
-
[excludeSections]="['Layout', 'Spacing', 'Size', 'Typography', 'Appearance', 'Box Model', 'Position', 'Effects', 'Advanced']"
|
|
18218
|
+
[excludeSections]="['Layout', 'Spacing', 'Size', 'Typography', 'Appearance', 'Box Model', 'Position', 'Effects', 'Transform', 'Advanced']"
|
|
17370
18219
|
(configChange)="onFieldConfigChange($event)">
|
|
17371
18220
|
</app-dynamic-properties>
|
|
17372
18221
|
</div>
|
|
@@ -17406,7 +18255,7 @@ class WidgetInspectorComponent {
|
|
|
17406
18255
|
</div>
|
|
17407
18256
|
`, isInline: true, styles: [":host{display:block;height:100%}.custom-scrollbar::-webkit-scrollbar{width:8px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px}.custom-scrollbar::-webkit-scrollbar-thumb:hover{background:#94a3b8}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: UiIconModule }, { kind: "component", type: i3$1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: UiAccordionComponent, selector: "ui-accordion", inputs: ["title", "subtitle", "expanded", "showAdd"] }, { kind: "component", type:
|
|
17408
18257
|
// Style Sections
|
|
17409
|
-
InspectorSpacingSectionComponent, selector: "inspector-spacing-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorSizeSectionComponent, selector: "inspector-size-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorTypographySectionComponent, selector: "inspector-typography-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorBordersSectionComponent, selector: "inspector-borders-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorLayoutSectionComponent, selector: "inspector-layout-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorBackgroundsSectionComponent, selector: "inspector-backgrounds-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorEffectsSectionComponent, selector: "inspector-effects-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorPositionSectionComponent, selector: "inspector-position-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorAdvancedSectionComponent, selector: "inspector-advanced-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type:
|
|
18258
|
+
InspectorSpacingSectionComponent, selector: "inspector-spacing-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorSizeSectionComponent, selector: "inspector-size-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorTypographySectionComponent, selector: "inspector-typography-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorBordersSectionComponent, selector: "inspector-borders-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorLayoutSectionComponent, selector: "inspector-layout-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorBackgroundsSectionComponent, selector: "inspector-backgrounds-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorEffectsSectionComponent, selector: "inspector-effects-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorPositionSectionComponent, selector: "inspector-position-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorTransformSectionComponent, selector: "inspector-transform-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorAdvancedSectionComponent, selector: "inspector-advanced-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type:
|
|
17410
18259
|
// Functional Panels (All restored)
|
|
17411
18260
|
DynamicPropertiesComponent, selector: "app-dynamic-properties", inputs: ["onPropertyChange", "config", "readOnly", "includeSections", "excludeSections", "allFields"], outputs: ["configChange"] }, { kind: "component", type: DataPanelComponent, selector: "app-data-panel", inputs: ["config", "readOnly", "dataConsumer", "bindingShape", "dataTargetPath", "widgetType", "allFields"], outputs: ["configChange"] }, { kind: "component", type: RulesPanelComponent, selector: "app-rules-panel", inputs: ["readOnly", "rules", "allFields"], outputs: ["rulesChange"] }] });
|
|
17412
18261
|
}
|
|
@@ -17425,6 +18274,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
17425
18274
|
InspectorBackgroundsSectionComponent,
|
|
17426
18275
|
InspectorEffectsSectionComponent,
|
|
17427
18276
|
InspectorPositionSectionComponent,
|
|
18277
|
+
InspectorTransformSectionComponent,
|
|
17428
18278
|
InspectorAdvancedSectionComponent,
|
|
17429
18279
|
// Functional Panels (All restored)
|
|
17430
18280
|
DynamicPropertiesComponent,
|
|
@@ -17541,6 +18391,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
17541
18391
|
</div>
|
|
17542
18392
|
</ui-accordion>
|
|
17543
18393
|
|
|
18394
|
+
<ui-accordion title="Transform" [expanded]="false">
|
|
18395
|
+
<div [class.pointer-events-none]="readOnly()" [class.opacity-60]="readOnly()">
|
|
18396
|
+
<inspector-transform-section
|
|
18397
|
+
[style]="currentStyle()"
|
|
18398
|
+
(styleChange)="onStyleChange($event)">
|
|
18399
|
+
</inspector-transform-section>
|
|
18400
|
+
</div>
|
|
18401
|
+
</ui-accordion>
|
|
18402
|
+
|
|
17544
18403
|
<ui-accordion title="Position" [expanded]="false">
|
|
17545
18404
|
<div [class.pointer-events-none]="readOnly()" [class.opacity-60]="readOnly()">
|
|
17546
18405
|
<inspector-position-section
|
|
@@ -17573,7 +18432,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
17573
18432
|
[config]="inspectorField()"
|
|
17574
18433
|
[allFields]="stateService.getSelectedScopeFields()"
|
|
17575
18434
|
[readOnly]="readOnly()"
|
|
17576
|
-
[excludeSections]="['Layout', 'Spacing', 'Size', 'Typography', 'Appearance', 'Box Model', 'Position', 'Effects', 'Advanced']"
|
|
18435
|
+
[excludeSections]="['Layout', 'Spacing', 'Size', 'Typography', 'Appearance', 'Box Model', 'Position', 'Effects', 'Transform', 'Advanced']"
|
|
17577
18436
|
(configChange)="onFieldConfigChange($event)">
|
|
17578
18437
|
</app-dynamic-properties>
|
|
17579
18438
|
</div>
|
|
@@ -17683,191 +18542,125 @@ class FormSettingsInspectorComponent {
|
|
|
17683
18542
|
</div>
|
|
17684
18543
|
|
|
17685
18544
|
<div class="h-px bg-border-default my-1"></div>
|
|
18545
|
+
</div>
|
|
18546
|
+
}
|
|
18547
|
+
|
|
18548
|
+
<!-- ===================== STYLE TAB ===================== -->
|
|
18549
|
+
@if (activeTab() === 'Style') {
|
|
18550
|
+
<div class="flex flex-col" [class.pointer-events-none]="readOnly()" [class.opacity-60]="readOnly()">
|
|
18551
|
+
|
|
18552
|
+
<div class="px-4 py-3 text-[12px] text-text-primary opacity-80 bg-slate-50 border-b border-border-default">
|
|
18553
|
+
Global styles applied to the form container.
|
|
18554
|
+
</div>
|
|
18555
|
+
|
|
18556
|
+
<ui-accordion title="Layout" [expanded]="true">
|
|
18557
|
+
<inspector-layout-section
|
|
18558
|
+
[style]="currentStyle()"
|
|
18559
|
+
(styleChange)="onStyleChange($event)">
|
|
18560
|
+
</inspector-layout-section>
|
|
18561
|
+
</ui-accordion>
|
|
17686
18562
|
|
|
18563
|
+
<ui-accordion title="Backgrounds" [expanded]="false">
|
|
18564
|
+
<inspector-backgrounds-section
|
|
18565
|
+
[style]="currentStyle()"
|
|
18566
|
+
(styleChange)="onStyleChange($event)">
|
|
18567
|
+
</inspector-backgrounds-section>
|
|
18568
|
+
</ui-accordion>
|
|
18569
|
+
|
|
18570
|
+
<ui-accordion title="Borders" [expanded]="false">
|
|
18571
|
+
<inspector-borders-section
|
|
18572
|
+
[style]="currentStyle()"
|
|
18573
|
+
(styleChange)="onStyleChange($event)">
|
|
18574
|
+
</inspector-borders-section>
|
|
18575
|
+
</ui-accordion>
|
|
18576
|
+
|
|
18577
|
+
<ui-accordion title="Effects" [expanded]="false">
|
|
18578
|
+
<inspector-effects-section
|
|
18579
|
+
[style]="currentStyle()"
|
|
18580
|
+
(styleChange)="onStyleChange($event)">
|
|
18581
|
+
</inspector-effects-section>
|
|
18582
|
+
</ui-accordion>
|
|
18583
|
+
|
|
18584
|
+
<ui-accordion title="Advanced" [expanded]="false">
|
|
18585
|
+
<inspector-advanced-section
|
|
18586
|
+
[style]="currentStyle()"
|
|
18587
|
+
(styleChange)="onStyleChange($event)">
|
|
18588
|
+
</inspector-advanced-section>
|
|
18589
|
+
</ui-accordion>
|
|
18590
|
+
|
|
18591
|
+
<!-- Bottom spacer -->
|
|
18592
|
+
<div class="h-10"></div>
|
|
18593
|
+
</div>
|
|
18594
|
+
}
|
|
18595
|
+
|
|
18596
|
+
</div>
|
|
18597
|
+
</div>
|
|
18598
|
+
`, isInline: true, styles: [":host{display:block;height:100%}.custom-scrollbar::-webkit-scrollbar{width:8px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px}.custom-scrollbar::-webkit-scrollbar-thumb:hover{background:#94a3b8}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: UiIconModule }, { kind: "component", type: i3$1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: UiAccordionComponent, selector: "ui-accordion", inputs: ["title", "subtitle", "expanded", "showAdd"] }, { kind: "component", type: InspectorLayoutSectionComponent, selector: "inspector-layout-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorBackgroundsSectionComponent, selector: "inspector-backgrounds-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorBordersSectionComponent, selector: "inspector-borders-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorEffectsSectionComponent, selector: "inspector-effects-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorAdvancedSectionComponent, selector: "inspector-advanced-section", inputs: ["style"], outputs: ["styleChange"] }] });
|
|
18599
|
+
}
|
|
18600
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FormSettingsInspectorComponent, decorators: [{
|
|
18601
|
+
type: Component,
|
|
18602
|
+
args: [{ selector: 'form-settings-inspector', standalone: true, imports: [
|
|
18603
|
+
CommonModule,
|
|
18604
|
+
FormsModule,
|
|
18605
|
+
UiIconModule,
|
|
18606
|
+
UiAccordionComponent,
|
|
18607
|
+
InspectorLayoutSectionComponent,
|
|
18608
|
+
InspectorBackgroundsSectionComponent,
|
|
18609
|
+
InspectorBordersSectionComponent,
|
|
18610
|
+
InspectorEffectsSectionComponent,
|
|
18611
|
+
InspectorAdvancedSectionComponent,
|
|
18612
|
+
], template: `
|
|
18613
|
+
<div class="h-full flex flex-col bg-surface-default font-sans text-[12px] text-text-primary">
|
|
18614
|
+
|
|
18615
|
+
<!-- Header -->
|
|
18616
|
+
<div class="flex items-center justify-between px-3 py-2 border-b border-border-default bg-slate-50">
|
|
18617
|
+
<div class="flex items-center gap-2 font-medium text-[12px] text-text-strong">
|
|
18618
|
+
<lucide-icon name="settings" class="w-4 h-4 text-text-primary opacity-60"></lucide-icon>
|
|
18619
|
+
<span>Form Settings</span>
|
|
18620
|
+
</div>
|
|
18621
|
+
</div>
|
|
18622
|
+
|
|
18623
|
+
<!-- Tabs -->
|
|
18624
|
+
<div class="flex items-center px-2 pt-2 border-b border-border-default bg-surface-default">
|
|
18625
|
+
@for (tab of tabs; track tab) {
|
|
18626
|
+
<button
|
|
18627
|
+
type="button"
|
|
18628
|
+
(click)="activeTab.set(tab)"
|
|
18629
|
+
[class]="activeTab() === tab
|
|
18630
|
+
? 'px-2 pb-2 font-semibold text-primary-500 border-b-2 border-b-primary-500'
|
|
18631
|
+
: 'px-2 pb-2 text-text-primary opacity-60 hover:opacity-100 hover:text-text-primary border-b-2 border-transparent'">
|
|
18632
|
+
{{ tab }}
|
|
18633
|
+
</button>
|
|
18634
|
+
}
|
|
18635
|
+
</div>
|
|
18636
|
+
|
|
18637
|
+
<!-- Scrollable Content -->
|
|
18638
|
+
<div class="flex-1 overflow-y-auto custom-scrollbar bg-surface-default">
|
|
18639
|
+
|
|
18640
|
+
<!-- ===================== SETTINGS TAB ===================== -->
|
|
18641
|
+
@if (activeTab() === 'Settings') {
|
|
18642
|
+
<div class="p-4 flex flex-col gap-4">
|
|
18643
|
+
|
|
17687
18644
|
<div class="flex flex-col gap-1">
|
|
17688
|
-
<label class="text-[10px] text-text-primary opacity-70 uppercase font-semibold">
|
|
18645
|
+
<label class="text-[10px] text-text-primary opacity-70 uppercase font-semibold">Form Title</label>
|
|
17689
18646
|
<input
|
|
17690
|
-
[ngModel]="schema().
|
|
18647
|
+
[ngModel]="schema().title"
|
|
17691
18648
|
[disabled]="readOnly()"
|
|
17692
|
-
(ngModelChange)="updateSettings('
|
|
17693
|
-
placeholder="Submit"
|
|
18649
|
+
(ngModelChange)="updateSettings('title', $event)"
|
|
17694
18650
|
class="h-8 w-full rounded border border-border-default px-2 text-[12px] focus:border-focus-border focus:ring-1 focus:ring-focus-border outline-none bg-surface-default">
|
|
17695
18651
|
</div>
|
|
17696
18652
|
|
|
17697
18653
|
<div class="flex flex-col gap-1">
|
|
17698
|
-
<label class="text-[10px] text-text-primary opacity-70 uppercase font-semibold">
|
|
17699
|
-
<
|
|
17700
|
-
|
|
17701
|
-
|
|
17702
|
-
|
|
17703
|
-
|
|
17704
|
-
|
|
17705
|
-
id="showReset"
|
|
17706
|
-
class="h-3.5 w-3.5 rounded border-border-default text-primary-500 focus:ring-primary-500">
|
|
17707
|
-
<label for="showReset" class="text-[12px] text-text-primary">Show Reset Button</label>
|
|
17708
|
-
</div>
|
|
17709
|
-
|
|
17710
|
-
@if (schema().showResetButton !== false) {
|
|
17711
|
-
<input
|
|
17712
|
-
[ngModel]="schema().resetButtonText"
|
|
17713
|
-
[disabled]="readOnly()"
|
|
17714
|
-
(ngModelChange)="updateSettings('resetButtonText', $event)"
|
|
17715
|
-
placeholder="Reset"
|
|
17716
|
-
class="h-8 w-full rounded border border-border-default px-2 text-[12px] focus:border-focus-border focus:ring-1 focus:ring-focus-border outline-none bg-surface-default">
|
|
17717
|
-
}
|
|
17718
|
-
</div>
|
|
17719
|
-
</div>
|
|
17720
|
-
}
|
|
17721
|
-
|
|
17722
|
-
<!-- ===================== STYLE TAB ===================== -->
|
|
17723
|
-
@if (activeTab() === 'Style') {
|
|
17724
|
-
<div class="flex flex-col" [class.pointer-events-none]="readOnly()" [class.opacity-60]="readOnly()">
|
|
17725
|
-
|
|
17726
|
-
<div class="px-4 py-3 text-[12px] text-text-primary opacity-80 bg-slate-50 border-b border-border-default">
|
|
17727
|
-
Global styles applied to the form container.
|
|
17728
|
-
</div>
|
|
17729
|
-
|
|
17730
|
-
<ui-accordion title="Layout" [expanded]="true">
|
|
17731
|
-
<inspector-layout-section
|
|
17732
|
-
[style]="currentStyle()"
|
|
17733
|
-
(styleChange)="onStyleChange($event)">
|
|
17734
|
-
</inspector-layout-section>
|
|
17735
|
-
</ui-accordion>
|
|
17736
|
-
|
|
17737
|
-
<ui-accordion title="Backgrounds" [expanded]="false">
|
|
17738
|
-
<inspector-backgrounds-section
|
|
17739
|
-
[style]="currentStyle()"
|
|
17740
|
-
(styleChange)="onStyleChange($event)">
|
|
17741
|
-
</inspector-backgrounds-section>
|
|
17742
|
-
</ui-accordion>
|
|
17743
|
-
|
|
17744
|
-
<ui-accordion title="Borders" [expanded]="false">
|
|
17745
|
-
<inspector-borders-section
|
|
17746
|
-
[style]="currentStyle()"
|
|
17747
|
-
(styleChange)="onStyleChange($event)">
|
|
17748
|
-
</inspector-borders-section>
|
|
17749
|
-
</ui-accordion>
|
|
17750
|
-
|
|
17751
|
-
<ui-accordion title="Effects" [expanded]="false">
|
|
17752
|
-
<inspector-effects-section
|
|
17753
|
-
[style]="currentStyle()"
|
|
17754
|
-
(styleChange)="onStyleChange($event)">
|
|
17755
|
-
</inspector-effects-section>
|
|
17756
|
-
</ui-accordion>
|
|
17757
|
-
|
|
17758
|
-
<ui-accordion title="Advanced" [expanded]="false">
|
|
17759
|
-
<inspector-advanced-section
|
|
17760
|
-
[style]="currentStyle()"
|
|
17761
|
-
(styleChange)="onStyleChange($event)">
|
|
17762
|
-
</inspector-advanced-section>
|
|
17763
|
-
</ui-accordion>
|
|
17764
|
-
|
|
17765
|
-
<!-- Bottom spacer -->
|
|
17766
|
-
<div class="h-10"></div>
|
|
17767
|
-
</div>
|
|
17768
|
-
}
|
|
17769
|
-
|
|
17770
|
-
</div>
|
|
17771
|
-
</div>
|
|
17772
|
-
`, isInline: true, styles: [":host{display:block;height:100%}.custom-scrollbar::-webkit-scrollbar{width:8px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px}.custom-scrollbar::-webkit-scrollbar-thumb:hover{background:#94a3b8}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: UiIconModule }, { kind: "component", type: i3$1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: UiAccordionComponent, selector: "ui-accordion", inputs: ["title", "subtitle", "expanded", "showAdd"] }, { kind: "component", type: InspectorLayoutSectionComponent, selector: "inspector-layout-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorBackgroundsSectionComponent, selector: "inspector-backgrounds-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorBordersSectionComponent, selector: "inspector-borders-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorEffectsSectionComponent, selector: "inspector-effects-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorAdvancedSectionComponent, selector: "inspector-advanced-section", inputs: ["style"], outputs: ["styleChange"] }] });
|
|
17773
|
-
}
|
|
17774
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FormSettingsInspectorComponent, decorators: [{
|
|
17775
|
-
type: Component,
|
|
17776
|
-
args: [{ selector: 'form-settings-inspector', standalone: true, imports: [
|
|
17777
|
-
CommonModule,
|
|
17778
|
-
FormsModule,
|
|
17779
|
-
UiIconModule,
|
|
17780
|
-
UiAccordionComponent,
|
|
17781
|
-
InspectorLayoutSectionComponent,
|
|
17782
|
-
InspectorBackgroundsSectionComponent,
|
|
17783
|
-
InspectorBordersSectionComponent,
|
|
17784
|
-
InspectorEffectsSectionComponent,
|
|
17785
|
-
InspectorAdvancedSectionComponent,
|
|
17786
|
-
], template: `
|
|
17787
|
-
<div class="h-full flex flex-col bg-surface-default font-sans text-[12px] text-text-primary">
|
|
17788
|
-
|
|
17789
|
-
<!-- Header -->
|
|
17790
|
-
<div class="flex items-center justify-between px-3 py-2 border-b border-border-default bg-slate-50">
|
|
17791
|
-
<div class="flex items-center gap-2 font-medium text-[12px] text-text-strong">
|
|
17792
|
-
<lucide-icon name="settings" class="w-4 h-4 text-text-primary opacity-60"></lucide-icon>
|
|
17793
|
-
<span>Form Settings</span>
|
|
17794
|
-
</div>
|
|
17795
|
-
</div>
|
|
17796
|
-
|
|
17797
|
-
<!-- Tabs -->
|
|
17798
|
-
<div class="flex items-center px-2 pt-2 border-b border-border-default bg-surface-default">
|
|
17799
|
-
@for (tab of tabs; track tab) {
|
|
17800
|
-
<button
|
|
17801
|
-
type="button"
|
|
17802
|
-
(click)="activeTab.set(tab)"
|
|
17803
|
-
[class]="activeTab() === tab
|
|
17804
|
-
? 'px-2 pb-2 font-semibold text-primary-500 border-b-2 border-b-primary-500'
|
|
17805
|
-
: 'px-2 pb-2 text-text-primary opacity-60 hover:opacity-100 hover:text-text-primary border-b-2 border-transparent'">
|
|
17806
|
-
{{ tab }}
|
|
17807
|
-
</button>
|
|
17808
|
-
}
|
|
17809
|
-
</div>
|
|
17810
|
-
|
|
17811
|
-
<!-- Scrollable Content -->
|
|
17812
|
-
<div class="flex-1 overflow-y-auto custom-scrollbar bg-surface-default">
|
|
17813
|
-
|
|
17814
|
-
<!-- ===================== SETTINGS TAB ===================== -->
|
|
17815
|
-
@if (activeTab() === 'Settings') {
|
|
17816
|
-
<div class="p-4 flex flex-col gap-4">
|
|
17817
|
-
|
|
17818
|
-
<div class="flex flex-col gap-1">
|
|
17819
|
-
<label class="text-[10px] text-text-primary opacity-70 uppercase font-semibold">Form Title</label>
|
|
17820
|
-
<input
|
|
17821
|
-
[ngModel]="schema().title"
|
|
17822
|
-
[disabled]="readOnly()"
|
|
17823
|
-
(ngModelChange)="updateSettings('title', $event)"
|
|
17824
|
-
class="h-8 w-full rounded border border-border-default px-2 text-[12px] focus:border-focus-border focus:ring-1 focus:ring-focus-border outline-none bg-surface-default">
|
|
17825
|
-
</div>
|
|
17826
|
-
|
|
17827
|
-
<div class="flex flex-col gap-1">
|
|
17828
|
-
<label class="text-[10px] text-text-primary opacity-70 uppercase font-semibold">Description</label>
|
|
17829
|
-
<textarea
|
|
17830
|
-
[ngModel]="schema().description"
|
|
17831
|
-
[disabled]="readOnly()"
|
|
17832
|
-
(ngModelChange)="updateSettings('description', $event)"
|
|
17833
|
-
rows="3"
|
|
17834
|
-
class="w-full rounded border border-border-default p-2 text-[12px] focus:border-focus-border focus:ring-1 focus:ring-focus-border outline-none bg-surface-default"></textarea>
|
|
17835
|
-
</div>
|
|
17836
|
-
|
|
17837
|
-
<div class="h-px bg-border-default my-1"></div>
|
|
17838
|
-
|
|
17839
|
-
<div class="flex flex-col gap-1">
|
|
17840
|
-
<label class="text-[10px] text-text-primary opacity-70 uppercase font-semibold">Submit Button</label>
|
|
17841
|
-
<input
|
|
17842
|
-
[ngModel]="schema().submitButtonText"
|
|
17843
|
-
[disabled]="readOnly()"
|
|
17844
|
-
(ngModelChange)="updateSettings('submitButtonText', $event)"
|
|
17845
|
-
placeholder="Submit"
|
|
17846
|
-
class="h-8 w-full rounded border border-border-default px-2 text-[12px] focus:border-focus-border focus:ring-1 focus:ring-focus-border outline-none bg-surface-default">
|
|
17847
|
-
</div>
|
|
17848
|
-
|
|
17849
|
-
<div class="flex flex-col gap-1">
|
|
17850
|
-
<label class="text-[10px] text-text-primary opacity-70 uppercase font-semibold">Reset Button</label>
|
|
17851
|
-
<div class="flex items-center gap-2 mb-1">
|
|
17852
|
-
<input
|
|
17853
|
-
type="checkbox"
|
|
17854
|
-
[ngModel]="schema().showResetButton !== false"
|
|
17855
|
-
[disabled]="readOnly()"
|
|
17856
|
-
(ngModelChange)="updateSettings('showResetButton', $event)"
|
|
17857
|
-
id="showReset"
|
|
17858
|
-
class="h-3.5 w-3.5 rounded border-border-default text-primary-500 focus:ring-primary-500">
|
|
17859
|
-
<label for="showReset" class="text-[12px] text-text-primary">Show Reset Button</label>
|
|
17860
|
-
</div>
|
|
17861
|
-
|
|
17862
|
-
@if (schema().showResetButton !== false) {
|
|
17863
|
-
<input
|
|
17864
|
-
[ngModel]="schema().resetButtonText"
|
|
17865
|
-
[disabled]="readOnly()"
|
|
17866
|
-
(ngModelChange)="updateSettings('resetButtonText', $event)"
|
|
17867
|
-
placeholder="Reset"
|
|
17868
|
-
class="h-8 w-full rounded border border-border-default px-2 text-[12px] focus:border-focus-border focus:ring-1 focus:ring-focus-border outline-none bg-surface-default">
|
|
17869
|
-
}
|
|
18654
|
+
<label class="text-[10px] text-text-primary opacity-70 uppercase font-semibold">Description</label>
|
|
18655
|
+
<textarea
|
|
18656
|
+
[ngModel]="schema().description"
|
|
18657
|
+
[disabled]="readOnly()"
|
|
18658
|
+
(ngModelChange)="updateSettings('description', $event)"
|
|
18659
|
+
rows="3"
|
|
18660
|
+
class="w-full rounded border border-border-default p-2 text-[12px] focus:border-focus-border focus:ring-1 focus:ring-focus-border outline-none bg-surface-default"></textarea>
|
|
17870
18661
|
</div>
|
|
18662
|
+
|
|
18663
|
+
<div class="h-px bg-border-default my-1"></div>
|
|
17871
18664
|
</div>
|
|
17872
18665
|
}
|
|
17873
18666
|
|
|
@@ -17924,165 +18717,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
17924
18717
|
`, styles: [":host{display:block;height:100%}.custom-scrollbar::-webkit-scrollbar{width:8px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px}.custom-scrollbar::-webkit-scrollbar-thumb:hover{background:#94a3b8}\n"] }]
|
|
17925
18718
|
}] });
|
|
17926
18719
|
|
|
17927
|
-
class UiTabComponent {
|
|
17928
|
-
label = '';
|
|
17929
|
-
name = ''; // Unique key for controlled mode
|
|
17930
|
-
disabled = false;
|
|
17931
|
-
badge;
|
|
17932
|
-
badgeTone = 'neutral';
|
|
17933
|
-
template;
|
|
17934
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: UiTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17935
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: UiTabComponent, isStandalone: true, selector: "ui-tab", inputs: { label: "label", name: "name", disabled: "disabled", badge: "badge", badgeTone: "badgeTone" }, viewQueries: [{ propertyName: "template", first: true, predicate: ["tpl"], descendants: true, static: true }], ngImport: i0, template: `<ng-template #tpl><ng-content></ng-content></ng-template>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
17936
|
-
}
|
|
17937
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: UiTabComponent, decorators: [{
|
|
17938
|
-
type: Component,
|
|
17939
|
-
args: [{
|
|
17940
|
-
selector: 'ui-tab',
|
|
17941
|
-
standalone: true,
|
|
17942
|
-
imports: [CommonModule],
|
|
17943
|
-
template: `<ng-template #tpl><ng-content></ng-content></ng-template>`
|
|
17944
|
-
}]
|
|
17945
|
-
}], propDecorators: { label: [{
|
|
17946
|
-
type: Input
|
|
17947
|
-
}], name: [{
|
|
17948
|
-
type: Input
|
|
17949
|
-
}], disabled: [{
|
|
17950
|
-
type: Input
|
|
17951
|
-
}], badge: [{
|
|
17952
|
-
type: Input
|
|
17953
|
-
}], badgeTone: [{
|
|
17954
|
-
type: Input
|
|
17955
|
-
}], template: [{
|
|
17956
|
-
type: ViewChild,
|
|
17957
|
-
args: ['tpl', { static: true }]
|
|
17958
|
-
}] } });
|
|
17959
|
-
class UiTabsComponent {
|
|
17960
|
-
tabQuery;
|
|
17961
|
-
activeTab;
|
|
17962
|
-
activeTabChange = new EventEmitter();
|
|
17963
|
-
// Internal state if uncontrolled
|
|
17964
|
-
_internalIndex = 0;
|
|
17965
|
-
tabs = [];
|
|
17966
|
-
ngAfterContentInit() {
|
|
17967
|
-
this.tabs = this.tabQuery.toArray();
|
|
17968
|
-
}
|
|
17969
|
-
isActive(tab) {
|
|
17970
|
-
if (this.activeTab !== undefined) {
|
|
17971
|
-
return this.activeTab === (tab.name || tab.label);
|
|
17972
|
-
}
|
|
17973
|
-
return this.tabs.indexOf(tab) === this._internalIndex;
|
|
17974
|
-
}
|
|
17975
|
-
activate(tab) {
|
|
17976
|
-
if (tab.disabled)
|
|
17977
|
-
return;
|
|
17978
|
-
const key = tab.name || tab.label;
|
|
17979
|
-
if (this.activeTab !== undefined) {
|
|
17980
|
-
this.activeTabChange.emit(key);
|
|
17981
|
-
}
|
|
17982
|
-
else {
|
|
17983
|
-
this._internalIndex = this.tabs.indexOf(tab);
|
|
17984
|
-
}
|
|
17985
|
-
}
|
|
17986
|
-
get activeTemplate() {
|
|
17987
|
-
if (this.tabs.length === 0)
|
|
17988
|
-
return null;
|
|
17989
|
-
let activeTab;
|
|
17990
|
-
if (this.activeTab !== undefined) {
|
|
17991
|
-
activeTab = this.tabs.find(t => (t.name || t.label) === this.activeTab);
|
|
17992
|
-
}
|
|
17993
|
-
else {
|
|
17994
|
-
activeTab = this.tabs[this._internalIndex];
|
|
17995
|
-
}
|
|
17996
|
-
return activeTab?.template || null;
|
|
17997
|
-
}
|
|
17998
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: UiTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17999
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: UiTabsComponent, isStandalone: true, selector: "ui-tabs", inputs: { activeTab: "activeTab" }, outputs: { activeTabChange: "activeTabChange" }, queries: [{ propertyName: "tabQuery", predicate: UiTabComponent }], ngImport: i0, template: `
|
|
18000
|
-
<div class="flex flex-col min-h-0 h-full">
|
|
18001
|
-
<div class="flex items-center gap-1 px-3 border-b border-slate-200 bg-white shrink-0">
|
|
18002
|
-
<button
|
|
18003
|
-
*ngFor="let tab of tabs; let i = index"
|
|
18004
|
-
(click)="activate(tab)"
|
|
18005
|
-
class="relative h-10 px-3 text-xs font-semibold transition-colors rounded-t-md border-b-2"
|
|
18006
|
-
[class.text-accent-600]="isActive(tab)"
|
|
18007
|
-
[class.border-accent-600]="isActive(tab)"
|
|
18008
|
-
[class.text-ink-500]="!isActive(tab)"
|
|
18009
|
-
[class.border-transparent]="!isActive(tab)"
|
|
18010
|
-
[class.hover:text-ink-700]="!isActive(tab)"
|
|
18011
|
-
[disabled]="tab.disabled"
|
|
18012
|
-
>
|
|
18013
|
-
<span class="flex items-center gap-2">
|
|
18014
|
-
{{ tab.label }}
|
|
18015
|
-
<span *ngIf="tab.badge" class="text-[10px] px-1.5 py-0.5 rounded-full border"
|
|
18016
|
-
[class.bg-accent-50]="tab.badgeTone === 'accent'"
|
|
18017
|
-
[class.border-accent-200]="tab.badgeTone === 'accent'"
|
|
18018
|
-
[class.text-accent-700]="tab.badgeTone === 'accent'"
|
|
18019
|
-
[class.bg-slate-100]="tab.badgeTone === 'neutral'"
|
|
18020
|
-
[class.border-slate-200]="tab.badgeTone === 'neutral'"
|
|
18021
|
-
[class.text-slate-600]="tab.badgeTone === 'neutral'">
|
|
18022
|
-
{{ tab.badge }}
|
|
18023
|
-
</span>
|
|
18024
|
-
</span>
|
|
18025
|
-
</button>
|
|
18026
|
-
</div>
|
|
18027
|
-
|
|
18028
|
-
<div class="flex-1 min-h-0 overflow-hidden bg-slate-50/30">
|
|
18029
|
-
<ng-container *ngIf="activeTemplate">
|
|
18030
|
-
<ng-container *ngTemplateOutlet="activeTemplate"></ng-container>
|
|
18031
|
-
</ng-container>
|
|
18032
|
-
</div>
|
|
18033
|
-
</div>
|
|
18034
|
-
`, isInline: true, styles: [":host{display:block;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
18035
|
-
}
|
|
18036
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: UiTabsComponent, decorators: [{
|
|
18037
|
-
type: Component,
|
|
18038
|
-
args: [{ selector: 'ui-tabs', standalone: true, imports: [CommonModule], template: `
|
|
18039
|
-
<div class="flex flex-col min-h-0 h-full">
|
|
18040
|
-
<div class="flex items-center gap-1 px-3 border-b border-slate-200 bg-white shrink-0">
|
|
18041
|
-
<button
|
|
18042
|
-
*ngFor="let tab of tabs; let i = index"
|
|
18043
|
-
(click)="activate(tab)"
|
|
18044
|
-
class="relative h-10 px-3 text-xs font-semibold transition-colors rounded-t-md border-b-2"
|
|
18045
|
-
[class.text-accent-600]="isActive(tab)"
|
|
18046
|
-
[class.border-accent-600]="isActive(tab)"
|
|
18047
|
-
[class.text-ink-500]="!isActive(tab)"
|
|
18048
|
-
[class.border-transparent]="!isActive(tab)"
|
|
18049
|
-
[class.hover:text-ink-700]="!isActive(tab)"
|
|
18050
|
-
[disabled]="tab.disabled"
|
|
18051
|
-
>
|
|
18052
|
-
<span class="flex items-center gap-2">
|
|
18053
|
-
{{ tab.label }}
|
|
18054
|
-
<span *ngIf="tab.badge" class="text-[10px] px-1.5 py-0.5 rounded-full border"
|
|
18055
|
-
[class.bg-accent-50]="tab.badgeTone === 'accent'"
|
|
18056
|
-
[class.border-accent-200]="tab.badgeTone === 'accent'"
|
|
18057
|
-
[class.text-accent-700]="tab.badgeTone === 'accent'"
|
|
18058
|
-
[class.bg-slate-100]="tab.badgeTone === 'neutral'"
|
|
18059
|
-
[class.border-slate-200]="tab.badgeTone === 'neutral'"
|
|
18060
|
-
[class.text-slate-600]="tab.badgeTone === 'neutral'">
|
|
18061
|
-
{{ tab.badge }}
|
|
18062
|
-
</span>
|
|
18063
|
-
</span>
|
|
18064
|
-
</button>
|
|
18065
|
-
</div>
|
|
18066
|
-
|
|
18067
|
-
<div class="flex-1 min-h-0 overflow-hidden bg-slate-50/30">
|
|
18068
|
-
<ng-container *ngIf="activeTemplate">
|
|
18069
|
-
<ng-container *ngTemplateOutlet="activeTemplate"></ng-container>
|
|
18070
|
-
</ng-container>
|
|
18071
|
-
</div>
|
|
18072
|
-
</div>
|
|
18073
|
-
`, styles: [":host{display:block;height:100%}\n"] }]
|
|
18074
|
-
}], propDecorators: { tabQuery: [{
|
|
18075
|
-
type: ContentChildren,
|
|
18076
|
-
args: [UiTabComponent]
|
|
18077
|
-
}], activeTab: [{
|
|
18078
|
-
type: Input
|
|
18079
|
-
}], activeTabChange: [{
|
|
18080
|
-
type: Output
|
|
18081
|
-
}] } });
|
|
18082
|
-
|
|
18083
18720
|
class PropertiesPanelComponent {
|
|
18084
18721
|
state;
|
|
18085
18722
|
injector;
|
|
18723
|
+
layoutInspectorTabs = ['Style', 'Settings'];
|
|
18724
|
+
activeLayoutInspectorTab = signal('Style');
|
|
18086
18725
|
spacingOptions = [
|
|
18087
18726
|
{ label: 'None', value: 'none' },
|
|
18088
18727
|
{ label: 'XS', value: 'xs' },
|
|
@@ -18105,9 +18744,11 @@ class PropertiesPanelComponent {
|
|
|
18105
18744
|
// No need to initialize form settings manually anymore, component inputs handle it
|
|
18106
18745
|
// Track the previous widget to avoid unnecessary re-renders
|
|
18107
18746
|
let previousWidgetId = null;
|
|
18747
|
+
let previousLayoutNodeKey = null;
|
|
18108
18748
|
effect(() => {
|
|
18109
18749
|
const node = this.state.selectedNode();
|
|
18110
18750
|
const currentWidgetId = (node && node.type === 'widget') ? node.id : null;
|
|
18751
|
+
const currentLayoutNodeKey = (node && node.type !== 'widget') ? `${node.type}:${node.id}` : null;
|
|
18111
18752
|
// Only re-render if the widget actually changed
|
|
18112
18753
|
if (currentWidgetId !== previousWidgetId) {
|
|
18113
18754
|
previousWidgetId = currentWidgetId;
|
|
@@ -18120,6 +18761,12 @@ class PropertiesPanelComponent {
|
|
|
18120
18761
|
this.inspectorContainer.clear();
|
|
18121
18762
|
}
|
|
18122
18763
|
}
|
|
18764
|
+
if (currentLayoutNodeKey !== previousLayoutNodeKey) {
|
|
18765
|
+
previousLayoutNodeKey = currentLayoutNodeKey;
|
|
18766
|
+
if (currentLayoutNodeKey) {
|
|
18767
|
+
this.activeLayoutInspectorTab.set('Style');
|
|
18768
|
+
}
|
|
18769
|
+
}
|
|
18123
18770
|
});
|
|
18124
18771
|
}
|
|
18125
18772
|
responsiveWidthOptions(includeInherit) {
|
|
@@ -18359,7 +19006,7 @@ class PropertiesPanelComponent {
|
|
|
18359
19006
|
this.state.selectNode(null);
|
|
18360
19007
|
}
|
|
18361
19008
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: PropertiesPanelComponent, deps: [{ token: DesignerStateService }, { token: WIDGET_DEFINITIONS }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component });
|
|
18362
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
19009
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: PropertiesPanelComponent, isStandalone: true, selector: "app-properties-panel", viewQueries: [{ propertyName: "inspectorContainer", first: true, predicate: ["inspectorContainer"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: `
|
|
18363
19010
|
<div class="properties-shell h-full border-l border-border-default bg-surface-default flex flex-col font-sans text-[12px]">
|
|
18364
19011
|
|
|
18365
19012
|
<div *ngIf="state.selectedNode() as node; else noSelection" class="flex flex-col h-full">
|
|
@@ -18386,239 +19033,182 @@ class PropertiesPanelComponent {
|
|
|
18386
19033
|
</div>
|
|
18387
19034
|
</div>
|
|
18388
19035
|
|
|
18389
|
-
|
|
18390
|
-
|
|
18391
|
-
|
|
18392
|
-
|
|
18393
|
-
|
|
18394
|
-
|
|
18395
|
-
|
|
18396
|
-
|
|
18397
|
-
|
|
18398
|
-
|
|
18399
|
-
|
|
18400
|
-
|
|
18401
|
-
<!-- Presets -->
|
|
18402
|
-
<div class="grid grid-cols-3 gap-2 mb-3">
|
|
18403
|
-
<button (click)="applyPreset(node.id, 1)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="1 Column">
|
|
18404
|
-
<div class="flex gap-1 w-full h-4 justify-center px-1">
|
|
18405
|
-
<div class="w-full bg-border-default rounded-sm"></div>
|
|
18406
|
-
</div>
|
|
18407
|
-
</button>
|
|
18408
|
-
<button (click)="applyPreset(node.id, 2)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="2 Columns">
|
|
18409
|
-
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
18410
|
-
<div class="w-1/2 bg-border-default rounded-sm"></div>
|
|
18411
|
-
<div class="w-1/2 bg-border-default rounded-sm"></div>
|
|
18412
|
-
</div>
|
|
18413
|
-
</button>
|
|
18414
|
-
<button (click)="applyPreset(node.id, 3)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="3 Columns">
|
|
18415
|
-
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
18416
|
-
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
18417
|
-
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
18418
|
-
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
18419
|
-
</div>
|
|
18420
|
-
</button>
|
|
18421
|
-
<button (click)="applyPreset(node.id, 4)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="4 Columns">
|
|
18422
|
-
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
18423
|
-
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
18424
|
-
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
18425
|
-
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
18426
|
-
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
18427
|
-
</div>
|
|
18428
|
-
</button>
|
|
18429
|
-
<button (click)="applyPreset(node.id, 6)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="6 Columns">
|
|
18430
|
-
<span class="text-[12px] text-text-primary font-medium">6 Col</span>
|
|
18431
|
-
</button>
|
|
18432
|
-
<button (click)="applyPreset(node.id, 12)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="12 Columns">
|
|
18433
|
-
<span class="text-[12px] text-text-primary font-medium">12 Col</span>
|
|
18434
|
-
</button>
|
|
18435
|
-
</div>
|
|
18436
|
-
|
|
18437
|
-
<!-- Manual Actions -->
|
|
18438
|
-
<div class="flex flex-col gap-2">
|
|
18439
|
-
<button (click)="addColumn(node.id)" class="w-full h-9 bg-primary-500 text-white rounded-md hover:opacity-90 flex items-center justify-center gap-2 text-[12px] font-medium transition-colors">
|
|
18440
|
-
<lucide-icon name="plus" class="w-4 h-4"></lucide-icon> Add Column
|
|
18441
|
-
</button>
|
|
18442
|
-
</div>
|
|
18443
|
-
</div>
|
|
18444
|
-
|
|
18445
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18446
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Layout</div>
|
|
18447
|
-
<inspector-layout-section
|
|
18448
|
-
[style]="nodeStyle(node)"
|
|
18449
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18450
|
-
</inspector-layout-section>
|
|
18451
|
-
</div>
|
|
18452
|
-
|
|
18453
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18454
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Spacing</div>
|
|
18455
|
-
<inspector-spacing-section
|
|
18456
|
-
[style]="nodeStyle(node)"
|
|
18457
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18458
|
-
</inspector-spacing-section>
|
|
18459
|
-
</div>
|
|
18460
|
-
|
|
18461
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18462
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Size</div>
|
|
18463
|
-
<inspector-size-section
|
|
18464
|
-
[style]="nodeStyle(node)"
|
|
18465
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18466
|
-
</inspector-size-section>
|
|
18467
|
-
</div>
|
|
18468
|
-
|
|
18469
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18470
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Backgrounds</div>
|
|
18471
|
-
<inspector-backgrounds-section
|
|
18472
|
-
[style]="nodeStyle(node)"
|
|
18473
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18474
|
-
</inspector-backgrounds-section>
|
|
18475
|
-
</div>
|
|
18476
|
-
|
|
18477
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18478
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Borders</div>
|
|
18479
|
-
<inspector-borders-section
|
|
18480
|
-
[style]="nodeStyle(node)"
|
|
18481
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18482
|
-
</inspector-borders-section>
|
|
18483
|
-
</div>
|
|
18484
|
-
|
|
18485
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18486
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Effects</div>
|
|
18487
|
-
<inspector-effects-section
|
|
18488
|
-
[style]="nodeStyle(node)"
|
|
18489
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18490
|
-
</inspector-effects-section>
|
|
18491
|
-
</div>
|
|
18492
|
-
|
|
18493
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18494
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Advanced</div>
|
|
18495
|
-
<inspector-advanced-section
|
|
18496
|
-
[style]="nodeStyle(node)"
|
|
18497
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18498
|
-
</inspector-advanced-section>
|
|
18499
|
-
</div>
|
|
18500
|
-
</div>
|
|
18501
|
-
</ng-container>
|
|
18502
|
-
<ng-container *ngIf="node.type === 'col'">
|
|
18503
|
-
<div class="inspector-stack space-y-4">
|
|
18504
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18505
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Responsive Width (1-12)</div>
|
|
18506
|
-
|
|
18507
|
-
<!-- XS (Mobile) -->
|
|
18508
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18509
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">XS</span>
|
|
18510
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.xs" (ngModelChange)="onResponsiveChange(node.id, 'xs', $event)">
|
|
18511
|
-
<option *ngFor="let option of responsiveWidthOptions(false)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18512
|
-
</select>
|
|
18513
|
-
</div>
|
|
18514
|
-
|
|
18515
|
-
<!-- SM (Large Phones) -->
|
|
18516
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18517
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">SM</span>
|
|
18518
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.sm" (ngModelChange)="onResponsiveChange(node.id, 'sm', $event)">
|
|
18519
|
-
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18520
|
-
</select>
|
|
18521
|
-
</div>
|
|
18522
|
-
|
|
18523
|
-
<!-- MD (Tablet) -->
|
|
18524
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18525
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">MD</span>
|
|
18526
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.md" (ngModelChange)="onResponsiveChange(node.id, 'md', $event)">
|
|
18527
|
-
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18528
|
-
</select>
|
|
18529
|
-
</div>
|
|
18530
|
-
|
|
18531
|
-
<!-- LG (Desktop) -->
|
|
18532
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18533
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">LG</span>
|
|
18534
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.lg" (ngModelChange)="onResponsiveChange(node.id, 'lg', $event)">
|
|
18535
|
-
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18536
|
-
</select>
|
|
18537
|
-
</div>
|
|
18538
|
-
|
|
18539
|
-
<!-- XL (Large Desktop) -->
|
|
18540
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18541
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">XL</span>
|
|
18542
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.xl" (ngModelChange)="onResponsiveChange(node.id, 'xl', $event)">
|
|
18543
|
-
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18544
|
-
</select>
|
|
18545
|
-
</div>
|
|
18546
|
-
|
|
18547
|
-
<!-- 2XL (Extra Large) -->
|
|
18548
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18549
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">2XL</span>
|
|
18550
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive ? node.responsive['2xl'] : undefined" (ngModelChange)="onResponsiveChange(node.id, '2xl', $event)">
|
|
18551
|
-
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18552
|
-
</select>
|
|
18553
|
-
</div>
|
|
18554
|
-
</div>
|
|
18555
|
-
|
|
18556
|
-
<div *ngIf="!state.isReadOnly()" class="inspector-card bg-red-50 border border-red-100 rounded-lg p-3">
|
|
18557
|
-
<button (click)="removeColumn(node.id)" class="w-full h-8 text-red-600 hover:text-red-700 flex items-center justify-center gap-2 text-[12px] font-medium transition-colors">
|
|
18558
|
-
<lucide-icon name="trash-2" class="w-4 h-4"></lucide-icon> Remove Column
|
|
18559
|
-
</button>
|
|
18560
|
-
</div>
|
|
19036
|
+
<div class="flex items-center px-2 pt-2 border-b border-border-default bg-surface-default">
|
|
19037
|
+
@for (tab of layoutInspectorTabs; track tab) {
|
|
19038
|
+
<button
|
|
19039
|
+
type="button"
|
|
19040
|
+
(click)="activeLayoutInspectorTab.set(tab)"
|
|
19041
|
+
[class]="activeLayoutInspectorTab() === tab
|
|
19042
|
+
? 'px-2 pb-2 font-semibold text-primary-500 border-b-2 border-b-primary-500'
|
|
19043
|
+
: 'px-2 pb-2 text-text-primary opacity-60 hover:opacity-100 hover:text-text-primary border-b-2 border-transparent'">
|
|
19044
|
+
{{ tab }}
|
|
19045
|
+
</button>
|
|
19046
|
+
}
|
|
19047
|
+
</div>
|
|
18561
19048
|
|
|
18562
|
-
|
|
18563
|
-
|
|
18564
|
-
|
|
18565
|
-
|
|
18566
|
-
|
|
18567
|
-
|
|
18568
|
-
|
|
19049
|
+
<div class="flex-1 overflow-y-auto custom-scrollbar bg-surface-default">
|
|
19050
|
+
@if (activeLayoutInspectorTab() === 'Style') {
|
|
19051
|
+
<div class="flex flex-col" [class.pointer-events-none]="state.isReadOnly()" [class.opacity-60]="state.isReadOnly()">
|
|
19052
|
+
<ui-accordion title="Layout" [expanded]="true">
|
|
19053
|
+
<inspector-layout-section
|
|
19054
|
+
[style]="nodeStyle(node)"
|
|
19055
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19056
|
+
</inspector-layout-section>
|
|
19057
|
+
</ui-accordion>
|
|
19058
|
+
|
|
19059
|
+
<ui-accordion title="Spacing" [expanded]="false">
|
|
19060
|
+
<inspector-spacing-section
|
|
19061
|
+
[style]="nodeStyle(node)"
|
|
19062
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19063
|
+
</inspector-spacing-section>
|
|
19064
|
+
</ui-accordion>
|
|
19065
|
+
|
|
19066
|
+
<ui-accordion title="Size" [expanded]="false">
|
|
19067
|
+
<inspector-size-section
|
|
19068
|
+
[style]="nodeStyle(node)"
|
|
19069
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19070
|
+
</inspector-size-section>
|
|
19071
|
+
</ui-accordion>
|
|
19072
|
+
|
|
19073
|
+
<ui-accordion title="Backgrounds" [expanded]="false">
|
|
19074
|
+
<inspector-backgrounds-section
|
|
19075
|
+
[style]="nodeStyle(node)"
|
|
19076
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19077
|
+
</inspector-backgrounds-section>
|
|
19078
|
+
</ui-accordion>
|
|
19079
|
+
|
|
19080
|
+
<ui-accordion title="Borders" [expanded]="false">
|
|
19081
|
+
<inspector-borders-section
|
|
19082
|
+
[style]="nodeStyle(node)"
|
|
19083
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19084
|
+
</inspector-borders-section>
|
|
19085
|
+
</ui-accordion>
|
|
19086
|
+
|
|
19087
|
+
<ui-accordion title="Effects" [expanded]="false">
|
|
19088
|
+
<inspector-effects-section
|
|
19089
|
+
[style]="nodeStyle(node)"
|
|
19090
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19091
|
+
</inspector-effects-section>
|
|
19092
|
+
</ui-accordion>
|
|
19093
|
+
|
|
19094
|
+
<ui-accordion title="Advanced" [expanded]="false">
|
|
19095
|
+
<inspector-advanced-section
|
|
19096
|
+
[style]="nodeStyle(node)"
|
|
19097
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19098
|
+
</inspector-advanced-section>
|
|
19099
|
+
</ui-accordion>
|
|
19100
|
+
|
|
19101
|
+
<div class="h-10"></div>
|
|
19102
|
+
</div>
|
|
19103
|
+
}
|
|
19104
|
+
|
|
19105
|
+
@if (activeLayoutInspectorTab() === 'Settings') {
|
|
19106
|
+
<div class="p-4 flex flex-col gap-4">
|
|
19107
|
+
@if (node.type === 'row') {
|
|
19108
|
+
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm"
|
|
19109
|
+
[class.opacity-60]="state.isReadOnly()"
|
|
19110
|
+
[class.pointer-events-none]="state.isReadOnly()">
|
|
19111
|
+
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Column Structure</div>
|
|
19112
|
+
<div class="grid grid-cols-3 gap-2 mb-3">
|
|
19113
|
+
<button (click)="applyPreset(node.id, 1)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="1 Column">
|
|
19114
|
+
<div class="flex gap-1 w-full h-4 justify-center px-1">
|
|
19115
|
+
<div class="w-full bg-border-default rounded-sm"></div>
|
|
19116
|
+
</div>
|
|
19117
|
+
</button>
|
|
19118
|
+
<button (click)="applyPreset(node.id, 2)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="2 Columns">
|
|
19119
|
+
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
19120
|
+
<div class="w-1/2 bg-border-default rounded-sm"></div>
|
|
19121
|
+
<div class="w-1/2 bg-border-default rounded-sm"></div>
|
|
19122
|
+
</div>
|
|
19123
|
+
</button>
|
|
19124
|
+
<button (click)="applyPreset(node.id, 3)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="3 Columns">
|
|
19125
|
+
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
19126
|
+
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
19127
|
+
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
19128
|
+
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
19129
|
+
</div>
|
|
19130
|
+
</button>
|
|
19131
|
+
<button (click)="applyPreset(node.id, 4)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="4 Columns">
|
|
19132
|
+
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
19133
|
+
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
19134
|
+
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
19135
|
+
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
19136
|
+
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
19137
|
+
</div>
|
|
19138
|
+
</button>
|
|
19139
|
+
<button (click)="applyPreset(node.id, 6)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="6 Columns">
|
|
19140
|
+
<span class="text-[12px] text-text-primary font-medium">6 Col</span>
|
|
19141
|
+
</button>
|
|
19142
|
+
<button (click)="applyPreset(node.id, 12)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="12 Columns">
|
|
19143
|
+
<span class="text-[12px] text-text-primary font-medium">12 Col</span>
|
|
19144
|
+
</button>
|
|
19145
|
+
</div>
|
|
19146
|
+
<button (click)="addColumn(node.id)" class="w-full h-9 bg-primary-500 text-white rounded-md hover:opacity-90 flex items-center justify-center gap-2 text-[12px] font-medium transition-colors">
|
|
19147
|
+
<lucide-icon name="plus" class="w-4 h-4"></lucide-icon> Add Column
|
|
19148
|
+
</button>
|
|
19149
|
+
</div>
|
|
19150
|
+
}
|
|
19151
|
+
|
|
19152
|
+
@if (node.type === 'col') {
|
|
19153
|
+
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm"
|
|
19154
|
+
[class.opacity-60]="state.isReadOnly()"
|
|
19155
|
+
[class.pointer-events-none]="state.isReadOnly()">
|
|
19156
|
+
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Responsive Width (1-12)</div>
|
|
19157
|
+
|
|
19158
|
+
<div class="flex items-center gap-2 mb-2">
|
|
19159
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">XS</span>
|
|
19160
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.xs" (ngModelChange)="onResponsiveChange(node.id, 'xs', $event)">
|
|
19161
|
+
<option *ngFor="let option of responsiveWidthOptions(false)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19162
|
+
</select>
|
|
19163
|
+
</div>
|
|
18569
19164
|
|
|
18570
|
-
|
|
18571
|
-
|
|
18572
|
-
|
|
18573
|
-
|
|
18574
|
-
|
|
18575
|
-
|
|
18576
|
-
</div>
|
|
19165
|
+
<div class="flex items-center gap-2 mb-2">
|
|
19166
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">SM</span>
|
|
19167
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.sm" (ngModelChange)="onResponsiveChange(node.id, 'sm', $event)">
|
|
19168
|
+
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19169
|
+
</select>
|
|
19170
|
+
</div>
|
|
18577
19171
|
|
|
18578
|
-
|
|
18579
|
-
|
|
18580
|
-
|
|
18581
|
-
|
|
18582
|
-
|
|
18583
|
-
|
|
18584
|
-
</div>
|
|
19172
|
+
<div class="flex items-center gap-2 mb-2">
|
|
19173
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">MD</span>
|
|
19174
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.md" (ngModelChange)="onResponsiveChange(node.id, 'md', $event)">
|
|
19175
|
+
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19176
|
+
</select>
|
|
19177
|
+
</div>
|
|
18585
19178
|
|
|
18586
|
-
|
|
18587
|
-
|
|
18588
|
-
|
|
18589
|
-
|
|
18590
|
-
|
|
18591
|
-
|
|
18592
|
-
</div>
|
|
19179
|
+
<div class="flex items-center gap-2 mb-2">
|
|
19180
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">LG</span>
|
|
19181
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.lg" (ngModelChange)="onResponsiveChange(node.id, 'lg', $event)">
|
|
19182
|
+
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19183
|
+
</select>
|
|
19184
|
+
</div>
|
|
18593
19185
|
|
|
18594
|
-
|
|
18595
|
-
|
|
18596
|
-
|
|
18597
|
-
|
|
18598
|
-
|
|
18599
|
-
|
|
18600
|
-
</div>
|
|
19186
|
+
<div class="flex items-center gap-2 mb-2">
|
|
19187
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">XL</span>
|
|
19188
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.xl" (ngModelChange)="onResponsiveChange(node.id, 'xl', $event)">
|
|
19189
|
+
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19190
|
+
</select>
|
|
19191
|
+
</div>
|
|
18601
19192
|
|
|
18602
|
-
|
|
18603
|
-
|
|
18604
|
-
|
|
18605
|
-
|
|
18606
|
-
|
|
18607
|
-
|
|
18608
|
-
|
|
19193
|
+
<div class="flex items-center gap-2">
|
|
19194
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">2XL</span>
|
|
19195
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive ? node.responsive['2xl'] : undefined" (ngModelChange)="onResponsiveChange(node.id, '2xl', $event)">
|
|
19196
|
+
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19197
|
+
</select>
|
|
19198
|
+
</div>
|
|
19199
|
+
</div>
|
|
18609
19200
|
|
|
18610
|
-
|
|
18611
|
-
|
|
18612
|
-
|
|
18613
|
-
|
|
18614
|
-
|
|
18615
|
-
|
|
18616
|
-
|
|
18617
|
-
|
|
18618
|
-
|
|
18619
|
-
|
|
18620
|
-
|
|
18621
|
-
</ui-tabs>
|
|
19201
|
+
@if (!state.isReadOnly()) {
|
|
19202
|
+
<div class="inspector-card bg-red-50 border border-red-100 rounded-lg p-3">
|
|
19203
|
+
<button (click)="removeColumn(node.id)" class="w-full h-8 text-red-600 hover:text-red-700 flex items-center justify-center gap-2 text-[12px] font-medium transition-colors">
|
|
19204
|
+
<lucide-icon name="trash-2" class="w-4 h-4"></lucide-icon> Remove Column
|
|
19205
|
+
</button>
|
|
19206
|
+
</div>
|
|
19207
|
+
}
|
|
19208
|
+
}
|
|
19209
|
+
</div>
|
|
19210
|
+
}
|
|
19211
|
+
</div>
|
|
18622
19212
|
</ng-container>
|
|
18623
19213
|
</div>
|
|
18624
19214
|
|
|
@@ -18631,7 +19221,7 @@ class PropertiesPanelComponent {
|
|
|
18631
19221
|
</form-settings-inspector>
|
|
18632
19222
|
</ng-template>
|
|
18633
19223
|
</div>
|
|
18634
|
-
`, isInline: true, styles: [".custom-scrollbar::-webkit-scrollbar{width:6px}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#d1d5db;border-radius:99px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: UiIconModule }, { kind: "component", type: i3$1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type:
|
|
19224
|
+
`, isInline: true, styles: [".custom-scrollbar::-webkit-scrollbar{width:6px}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#d1d5db;border-radius:99px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: UiIconModule }, { kind: "component", type: i3$1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: UiAccordionComponent, selector: "ui-accordion", inputs: ["title", "subtitle", "expanded", "showAdd"] }, { kind: "component", type: WidgetInspectorComponent, selector: "widget-inspector", inputs: ["node", "field", "readOnly"], outputs: ["fieldChange", "styleChange", "rulesChange", "duplicate", "delete"] }, { kind: "component", type: FormSettingsInspectorComponent, selector: "form-settings-inspector", inputs: ["schema", "readOnly"], outputs: ["settingsChange", "styleChange"] }, { kind: "component", type: InspectorLayoutSectionComponent, selector: "inspector-layout-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorSpacingSectionComponent, selector: "inspector-spacing-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorSizeSectionComponent, selector: "inspector-size-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorBackgroundsSectionComponent, selector: "inspector-backgrounds-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorBordersSectionComponent, selector: "inspector-borders-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorEffectsSectionComponent, selector: "inspector-effects-section", inputs: ["style"], outputs: ["styleChange"] }, { kind: "component", type: InspectorAdvancedSectionComponent, selector: "inspector-advanced-section", inputs: ["style"], outputs: ["styleChange"] }] });
|
|
18635
19225
|
}
|
|
18636
19226
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: PropertiesPanelComponent, decorators: [{
|
|
18637
19227
|
type: Component,
|
|
@@ -18639,8 +19229,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
18639
19229
|
CommonModule,
|
|
18640
19230
|
FormsModule,
|
|
18641
19231
|
UiIconModule,
|
|
18642
|
-
|
|
18643
|
-
UiTabComponent,
|
|
19232
|
+
UiAccordionComponent,
|
|
18644
19233
|
WidgetInspectorComponent,
|
|
18645
19234
|
FormSettingsInspectorComponent,
|
|
18646
19235
|
InspectorLayoutSectionComponent,
|
|
@@ -18677,239 +19266,182 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
18677
19266
|
</div>
|
|
18678
19267
|
</div>
|
|
18679
19268
|
|
|
18680
|
-
|
|
18681
|
-
|
|
18682
|
-
|
|
18683
|
-
|
|
18684
|
-
|
|
18685
|
-
|
|
18686
|
-
|
|
18687
|
-
|
|
18688
|
-
|
|
18689
|
-
|
|
18690
|
-
|
|
18691
|
-
|
|
18692
|
-
<!-- Presets -->
|
|
18693
|
-
<div class="grid grid-cols-3 gap-2 mb-3">
|
|
18694
|
-
<button (click)="applyPreset(node.id, 1)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="1 Column">
|
|
18695
|
-
<div class="flex gap-1 w-full h-4 justify-center px-1">
|
|
18696
|
-
<div class="w-full bg-border-default rounded-sm"></div>
|
|
18697
|
-
</div>
|
|
18698
|
-
</button>
|
|
18699
|
-
<button (click)="applyPreset(node.id, 2)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="2 Columns">
|
|
18700
|
-
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
18701
|
-
<div class="w-1/2 bg-border-default rounded-sm"></div>
|
|
18702
|
-
<div class="w-1/2 bg-border-default rounded-sm"></div>
|
|
18703
|
-
</div>
|
|
18704
|
-
</button>
|
|
18705
|
-
<button (click)="applyPreset(node.id, 3)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="3 Columns">
|
|
18706
|
-
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
18707
|
-
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
18708
|
-
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
18709
|
-
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
18710
|
-
</div>
|
|
18711
|
-
</button>
|
|
18712
|
-
<button (click)="applyPreset(node.id, 4)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="4 Columns">
|
|
18713
|
-
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
18714
|
-
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
18715
|
-
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
18716
|
-
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
18717
|
-
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
18718
|
-
</div>
|
|
18719
|
-
</button>
|
|
18720
|
-
<button (click)="applyPreset(node.id, 6)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="6 Columns">
|
|
18721
|
-
<span class="text-[12px] text-text-primary font-medium">6 Col</span>
|
|
18722
|
-
</button>
|
|
18723
|
-
<button (click)="applyPreset(node.id, 12)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="12 Columns">
|
|
18724
|
-
<span class="text-[12px] text-text-primary font-medium">12 Col</span>
|
|
18725
|
-
</button>
|
|
18726
|
-
</div>
|
|
18727
|
-
|
|
18728
|
-
<!-- Manual Actions -->
|
|
18729
|
-
<div class="flex flex-col gap-2">
|
|
18730
|
-
<button (click)="addColumn(node.id)" class="w-full h-9 bg-primary-500 text-white rounded-md hover:opacity-90 flex items-center justify-center gap-2 text-[12px] font-medium transition-colors">
|
|
18731
|
-
<lucide-icon name="plus" class="w-4 h-4"></lucide-icon> Add Column
|
|
18732
|
-
</button>
|
|
18733
|
-
</div>
|
|
18734
|
-
</div>
|
|
18735
|
-
|
|
18736
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18737
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Layout</div>
|
|
18738
|
-
<inspector-layout-section
|
|
18739
|
-
[style]="nodeStyle(node)"
|
|
18740
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18741
|
-
</inspector-layout-section>
|
|
18742
|
-
</div>
|
|
18743
|
-
|
|
18744
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18745
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Spacing</div>
|
|
18746
|
-
<inspector-spacing-section
|
|
18747
|
-
[style]="nodeStyle(node)"
|
|
18748
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18749
|
-
</inspector-spacing-section>
|
|
18750
|
-
</div>
|
|
18751
|
-
|
|
18752
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18753
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Size</div>
|
|
18754
|
-
<inspector-size-section
|
|
18755
|
-
[style]="nodeStyle(node)"
|
|
18756
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18757
|
-
</inspector-size-section>
|
|
18758
|
-
</div>
|
|
18759
|
-
|
|
18760
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18761
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Backgrounds</div>
|
|
18762
|
-
<inspector-backgrounds-section
|
|
18763
|
-
[style]="nodeStyle(node)"
|
|
18764
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18765
|
-
</inspector-backgrounds-section>
|
|
18766
|
-
</div>
|
|
18767
|
-
|
|
18768
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18769
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Borders</div>
|
|
18770
|
-
<inspector-borders-section
|
|
18771
|
-
[style]="nodeStyle(node)"
|
|
18772
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18773
|
-
</inspector-borders-section>
|
|
18774
|
-
</div>
|
|
18775
|
-
|
|
18776
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18777
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Effects</div>
|
|
18778
|
-
<inspector-effects-section
|
|
18779
|
-
[style]="nodeStyle(node)"
|
|
18780
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18781
|
-
</inspector-effects-section>
|
|
18782
|
-
</div>
|
|
18783
|
-
|
|
18784
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18785
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Advanced</div>
|
|
18786
|
-
<inspector-advanced-section
|
|
18787
|
-
[style]="nodeStyle(node)"
|
|
18788
|
-
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
18789
|
-
</inspector-advanced-section>
|
|
18790
|
-
</div>
|
|
18791
|
-
</div>
|
|
18792
|
-
</ng-container>
|
|
18793
|
-
<ng-container *ngIf="node.type === 'col'">
|
|
18794
|
-
<div class="inspector-stack space-y-4">
|
|
18795
|
-
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm" [class.opacity-50]="state.isReadOnly()" [class.pointer-events-none]="state.isReadOnly()">
|
|
18796
|
-
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Responsive Width (1-12)</div>
|
|
18797
|
-
|
|
18798
|
-
<!-- XS (Mobile) -->
|
|
18799
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18800
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">XS</span>
|
|
18801
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.xs" (ngModelChange)="onResponsiveChange(node.id, 'xs', $event)">
|
|
18802
|
-
<option *ngFor="let option of responsiveWidthOptions(false)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18803
|
-
</select>
|
|
18804
|
-
</div>
|
|
18805
|
-
|
|
18806
|
-
<!-- SM (Large Phones) -->
|
|
18807
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18808
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">SM</span>
|
|
18809
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.sm" (ngModelChange)="onResponsiveChange(node.id, 'sm', $event)">
|
|
18810
|
-
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18811
|
-
</select>
|
|
18812
|
-
</div>
|
|
18813
|
-
|
|
18814
|
-
<!-- MD (Tablet) -->
|
|
18815
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18816
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">MD</span>
|
|
18817
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.md" (ngModelChange)="onResponsiveChange(node.id, 'md', $event)">
|
|
18818
|
-
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18819
|
-
</select>
|
|
18820
|
-
</div>
|
|
18821
|
-
|
|
18822
|
-
<!-- LG (Desktop) -->
|
|
18823
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18824
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">LG</span>
|
|
18825
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.lg" (ngModelChange)="onResponsiveChange(node.id, 'lg', $event)">
|
|
18826
|
-
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18827
|
-
</select>
|
|
18828
|
-
</div>
|
|
18829
|
-
|
|
18830
|
-
<!-- XL (Large Desktop) -->
|
|
18831
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18832
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">XL</span>
|
|
18833
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.xl" (ngModelChange)="onResponsiveChange(node.id, 'xl', $event)">
|
|
18834
|
-
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18835
|
-
</select>
|
|
18836
|
-
</div>
|
|
18837
|
-
|
|
18838
|
-
<!-- 2XL (Extra Large) -->
|
|
18839
|
-
<div class="flex items-center gap-2 mb-2">
|
|
18840
|
-
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">2XL</span>
|
|
18841
|
-
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive ? node.responsive['2xl'] : undefined" (ngModelChange)="onResponsiveChange(node.id, '2xl', $event)">
|
|
18842
|
-
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
18843
|
-
</select>
|
|
18844
|
-
</div>
|
|
18845
|
-
</div>
|
|
18846
|
-
|
|
18847
|
-
<div *ngIf="!state.isReadOnly()" class="inspector-card bg-red-50 border border-red-100 rounded-lg p-3">
|
|
18848
|
-
<button (click)="removeColumn(node.id)" class="w-full h-8 text-red-600 hover:text-red-700 flex items-center justify-center gap-2 text-[12px] font-medium transition-colors">
|
|
18849
|
-
<lucide-icon name="trash-2" class="w-4 h-4"></lucide-icon> Remove Column
|
|
18850
|
-
</button>
|
|
18851
|
-
</div>
|
|
19269
|
+
<div class="flex items-center px-2 pt-2 border-b border-border-default bg-surface-default">
|
|
19270
|
+
@for (tab of layoutInspectorTabs; track tab) {
|
|
19271
|
+
<button
|
|
19272
|
+
type="button"
|
|
19273
|
+
(click)="activeLayoutInspectorTab.set(tab)"
|
|
19274
|
+
[class]="activeLayoutInspectorTab() === tab
|
|
19275
|
+
? 'px-2 pb-2 font-semibold text-primary-500 border-b-2 border-b-primary-500'
|
|
19276
|
+
: 'px-2 pb-2 text-text-primary opacity-60 hover:opacity-100 hover:text-text-primary border-b-2 border-transparent'">
|
|
19277
|
+
{{ tab }}
|
|
19278
|
+
</button>
|
|
19279
|
+
}
|
|
19280
|
+
</div>
|
|
18852
19281
|
|
|
18853
|
-
|
|
18854
|
-
|
|
18855
|
-
|
|
18856
|
-
|
|
18857
|
-
|
|
18858
|
-
|
|
18859
|
-
|
|
19282
|
+
<div class="flex-1 overflow-y-auto custom-scrollbar bg-surface-default">
|
|
19283
|
+
@if (activeLayoutInspectorTab() === 'Style') {
|
|
19284
|
+
<div class="flex flex-col" [class.pointer-events-none]="state.isReadOnly()" [class.opacity-60]="state.isReadOnly()">
|
|
19285
|
+
<ui-accordion title="Layout" [expanded]="true">
|
|
19286
|
+
<inspector-layout-section
|
|
19287
|
+
[style]="nodeStyle(node)"
|
|
19288
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19289
|
+
</inspector-layout-section>
|
|
19290
|
+
</ui-accordion>
|
|
19291
|
+
|
|
19292
|
+
<ui-accordion title="Spacing" [expanded]="false">
|
|
19293
|
+
<inspector-spacing-section
|
|
19294
|
+
[style]="nodeStyle(node)"
|
|
19295
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19296
|
+
</inspector-spacing-section>
|
|
19297
|
+
</ui-accordion>
|
|
19298
|
+
|
|
19299
|
+
<ui-accordion title="Size" [expanded]="false">
|
|
19300
|
+
<inspector-size-section
|
|
19301
|
+
[style]="nodeStyle(node)"
|
|
19302
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19303
|
+
</inspector-size-section>
|
|
19304
|
+
</ui-accordion>
|
|
19305
|
+
|
|
19306
|
+
<ui-accordion title="Backgrounds" [expanded]="false">
|
|
19307
|
+
<inspector-backgrounds-section
|
|
19308
|
+
[style]="nodeStyle(node)"
|
|
19309
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19310
|
+
</inspector-backgrounds-section>
|
|
19311
|
+
</ui-accordion>
|
|
19312
|
+
|
|
19313
|
+
<ui-accordion title="Borders" [expanded]="false">
|
|
19314
|
+
<inspector-borders-section
|
|
19315
|
+
[style]="nodeStyle(node)"
|
|
19316
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19317
|
+
</inspector-borders-section>
|
|
19318
|
+
</ui-accordion>
|
|
19319
|
+
|
|
19320
|
+
<ui-accordion title="Effects" [expanded]="false">
|
|
19321
|
+
<inspector-effects-section
|
|
19322
|
+
[style]="nodeStyle(node)"
|
|
19323
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19324
|
+
</inspector-effects-section>
|
|
19325
|
+
</ui-accordion>
|
|
19326
|
+
|
|
19327
|
+
<ui-accordion title="Advanced" [expanded]="false">
|
|
19328
|
+
<inspector-advanced-section
|
|
19329
|
+
[style]="nodeStyle(node)"
|
|
19330
|
+
(styleChange)="onNodeInspectorStyleChange(node.id, $event)">
|
|
19331
|
+
</inspector-advanced-section>
|
|
19332
|
+
</ui-accordion>
|
|
19333
|
+
|
|
19334
|
+
<div class="h-10"></div>
|
|
19335
|
+
</div>
|
|
19336
|
+
}
|
|
19337
|
+
|
|
19338
|
+
@if (activeLayoutInspectorTab() === 'Settings') {
|
|
19339
|
+
<div class="p-4 flex flex-col gap-4">
|
|
19340
|
+
@if (node.type === 'row') {
|
|
19341
|
+
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm"
|
|
19342
|
+
[class.opacity-60]="state.isReadOnly()"
|
|
19343
|
+
[class.pointer-events-none]="state.isReadOnly()">
|
|
19344
|
+
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Column Structure</div>
|
|
19345
|
+
<div class="grid grid-cols-3 gap-2 mb-3">
|
|
19346
|
+
<button (click)="applyPreset(node.id, 1)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="1 Column">
|
|
19347
|
+
<div class="flex gap-1 w-full h-4 justify-center px-1">
|
|
19348
|
+
<div class="w-full bg-border-default rounded-sm"></div>
|
|
19349
|
+
</div>
|
|
19350
|
+
</button>
|
|
19351
|
+
<button (click)="applyPreset(node.id, 2)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="2 Columns">
|
|
19352
|
+
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
19353
|
+
<div class="w-1/2 bg-border-default rounded-sm"></div>
|
|
19354
|
+
<div class="w-1/2 bg-border-default rounded-sm"></div>
|
|
19355
|
+
</div>
|
|
19356
|
+
</button>
|
|
19357
|
+
<button (click)="applyPreset(node.id, 3)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="3 Columns">
|
|
19358
|
+
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
19359
|
+
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
19360
|
+
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
19361
|
+
<div class="w-1/3 bg-border-default rounded-sm"></div>
|
|
19362
|
+
</div>
|
|
19363
|
+
</button>
|
|
19364
|
+
<button (click)="applyPreset(node.id, 4)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="4 Columns">
|
|
19365
|
+
<div class="flex gap-0.5 w-full h-4 px-1">
|
|
19366
|
+
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
19367
|
+
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
19368
|
+
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
19369
|
+
<div class="w-1/4 bg-border-default rounded-sm"></div>
|
|
19370
|
+
</div>
|
|
19371
|
+
</button>
|
|
19372
|
+
<button (click)="applyPreset(node.id, 6)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="6 Columns">
|
|
19373
|
+
<span class="text-[12px] text-text-primary font-medium">6 Col</span>
|
|
19374
|
+
</button>
|
|
19375
|
+
<button (click)="applyPreset(node.id, 12)" class="h-9 border border-border-default rounded hover:bg-slate-50 flex items-center justify-center p-1" title="12 Columns">
|
|
19376
|
+
<span class="text-[12px] text-text-primary font-medium">12 Col</span>
|
|
19377
|
+
</button>
|
|
19378
|
+
</div>
|
|
19379
|
+
<button (click)="addColumn(node.id)" class="w-full h-9 bg-primary-500 text-white rounded-md hover:opacity-90 flex items-center justify-center gap-2 text-[12px] font-medium transition-colors">
|
|
19380
|
+
<lucide-icon name="plus" class="w-4 h-4"></lucide-icon> Add Column
|
|
19381
|
+
</button>
|
|
19382
|
+
</div>
|
|
19383
|
+
}
|
|
19384
|
+
|
|
19385
|
+
@if (node.type === 'col') {
|
|
19386
|
+
<div class="inspector-card bg-surface-default border border-border-default rounded-lg p-4 shadow-sm"
|
|
19387
|
+
[class.opacity-60]="state.isReadOnly()"
|
|
19388
|
+
[class.pointer-events-none]="state.isReadOnly()">
|
|
19389
|
+
<div class="section-title text-[11px] font-semibold text-text-primary opacity-70 uppercase tracking-wide mb-3">Responsive Width (1-12)</div>
|
|
19390
|
+
|
|
19391
|
+
<div class="flex items-center gap-2 mb-2">
|
|
19392
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">XS</span>
|
|
19393
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.xs" (ngModelChange)="onResponsiveChange(node.id, 'xs', $event)">
|
|
19394
|
+
<option *ngFor="let option of responsiveWidthOptions(false)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19395
|
+
</select>
|
|
19396
|
+
</div>
|
|
18860
19397
|
|
|
18861
|
-
|
|
18862
|
-
|
|
18863
|
-
|
|
18864
|
-
|
|
18865
|
-
|
|
18866
|
-
|
|
18867
|
-
</div>
|
|
19398
|
+
<div class="flex items-center gap-2 mb-2">
|
|
19399
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">SM</span>
|
|
19400
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.sm" (ngModelChange)="onResponsiveChange(node.id, 'sm', $event)">
|
|
19401
|
+
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19402
|
+
</select>
|
|
19403
|
+
</div>
|
|
18868
19404
|
|
|
18869
|
-
|
|
18870
|
-
|
|
18871
|
-
|
|
18872
|
-
|
|
18873
|
-
|
|
18874
|
-
|
|
18875
|
-
</div>
|
|
19405
|
+
<div class="flex items-center gap-2 mb-2">
|
|
19406
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">MD</span>
|
|
19407
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.md" (ngModelChange)="onResponsiveChange(node.id, 'md', $event)">
|
|
19408
|
+
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19409
|
+
</select>
|
|
19410
|
+
</div>
|
|
18876
19411
|
|
|
18877
|
-
|
|
18878
|
-
|
|
18879
|
-
|
|
18880
|
-
|
|
18881
|
-
|
|
18882
|
-
|
|
18883
|
-
</div>
|
|
19412
|
+
<div class="flex items-center gap-2 mb-2">
|
|
19413
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">LG</span>
|
|
19414
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.lg" (ngModelChange)="onResponsiveChange(node.id, 'lg', $event)">
|
|
19415
|
+
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19416
|
+
</select>
|
|
19417
|
+
</div>
|
|
18884
19418
|
|
|
18885
|
-
|
|
18886
|
-
|
|
18887
|
-
|
|
18888
|
-
|
|
18889
|
-
|
|
18890
|
-
|
|
18891
|
-
</div>
|
|
19419
|
+
<div class="flex items-center gap-2 mb-2">
|
|
19420
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">XL</span>
|
|
19421
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive.xl" (ngModelChange)="onResponsiveChange(node.id, 'xl', $event)">
|
|
19422
|
+
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19423
|
+
</select>
|
|
19424
|
+
</div>
|
|
18892
19425
|
|
|
18893
|
-
|
|
18894
|
-
|
|
18895
|
-
|
|
18896
|
-
|
|
18897
|
-
|
|
18898
|
-
|
|
18899
|
-
|
|
19426
|
+
<div class="flex items-center gap-2">
|
|
19427
|
+
<span class="text-[12px] w-8 text-text-primary opacity-70 font-medium">2XL</span>
|
|
19428
|
+
<select class="h-8 flex-1 rounded-md border border-border-default bg-white px-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" [ngModel]="node.responsive ? node.responsive['2xl'] : undefined" (ngModelChange)="onResponsiveChange(node.id, '2xl', $event)">
|
|
19429
|
+
<option *ngFor="let option of responsiveWidthOptions(true)" [ngValue]="option.value">{{ option.label }}</option>
|
|
19430
|
+
</select>
|
|
19431
|
+
</div>
|
|
19432
|
+
</div>
|
|
18900
19433
|
|
|
18901
|
-
|
|
18902
|
-
|
|
18903
|
-
|
|
18904
|
-
|
|
18905
|
-
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
|
|
18909
|
-
|
|
18910
|
-
|
|
18911
|
-
|
|
18912
|
-
</ui-tabs>
|
|
19434
|
+
@if (!state.isReadOnly()) {
|
|
19435
|
+
<div class="inspector-card bg-red-50 border border-red-100 rounded-lg p-3">
|
|
19436
|
+
<button (click)="removeColumn(node.id)" class="w-full h-8 text-red-600 hover:text-red-700 flex items-center justify-center gap-2 text-[12px] font-medium transition-colors">
|
|
19437
|
+
<lucide-icon name="trash-2" class="w-4 h-4"></lucide-icon> Remove Column
|
|
19438
|
+
</button>
|
|
19439
|
+
</div>
|
|
19440
|
+
}
|
|
19441
|
+
}
|
|
19442
|
+
</div>
|
|
19443
|
+
}
|
|
19444
|
+
</div>
|
|
18913
19445
|
</ng-container>
|
|
18914
19446
|
</div>
|
|
18915
19447
|
|
|
@@ -20856,9 +21388,9 @@ const BASIC_CONTACT_TEMPLATE = {
|
|
|
20856
21388
|
},
|
|
20857
21389
|
{
|
|
20858
21390
|
"id": "btn_submit",
|
|
20859
|
-
"widgetId": "core.form:
|
|
21391
|
+
"widgetId": "core.form:form-button",
|
|
20860
21392
|
"name": "submit",
|
|
20861
|
-
"type": "
|
|
21393
|
+
"type": "form-button",
|
|
20862
21394
|
"label": "Send message",
|
|
20863
21395
|
"variant": "primary",
|
|
20864
21396
|
"buttonType": "submit",
|
|
@@ -20875,9 +21407,9 @@ const BASIC_CONTACT_TEMPLATE = {
|
|
|
20875
21407
|
},
|
|
20876
21408
|
{
|
|
20877
21409
|
"id": "btn_reset",
|
|
20878
|
-
"widgetId": "core.form:
|
|
21410
|
+
"widgetId": "core.form:form-button",
|
|
20879
21411
|
"name": "reset",
|
|
20880
|
-
"type": "
|
|
21412
|
+
"type": "form-button",
|
|
20881
21413
|
"label": "Reset",
|
|
20882
21414
|
"variant": "secondary",
|
|
20883
21415
|
"buttonType": "reset"
|
|
@@ -21269,9 +21801,9 @@ const RULES_SHIPPING_TEMPLATE = {
|
|
|
21269
21801
|
},
|
|
21270
21802
|
{
|
|
21271
21803
|
"id": "btn_submit",
|
|
21272
|
-
"widgetId": "core.form:
|
|
21804
|
+
"widgetId": "core.form:form-button",
|
|
21273
21805
|
"name": "submit",
|
|
21274
|
-
"type": "
|
|
21806
|
+
"type": "form-button",
|
|
21275
21807
|
"label": "Place order",
|
|
21276
21808
|
"variant": "primary",
|
|
21277
21809
|
"buttonType": "submit",
|
|
@@ -21286,9 +21818,9 @@ const RULES_SHIPPING_TEMPLATE = {
|
|
|
21286
21818
|
},
|
|
21287
21819
|
{
|
|
21288
21820
|
"id": "btn_reset",
|
|
21289
|
-
"widgetId": "core.form:
|
|
21821
|
+
"widgetId": "core.form:form-button",
|
|
21290
21822
|
"name": "reset",
|
|
21291
|
-
"type": "
|
|
21823
|
+
"type": "form-button",
|
|
21292
21824
|
"label": "Reset",
|
|
21293
21825
|
"variant": "secondary",
|
|
21294
21826
|
"buttonType": "reset"
|
|
@@ -21578,9 +22110,9 @@ const DATA_SOURCES_TEMPLATE = {
|
|
|
21578
22110
|
},
|
|
21579
22111
|
{
|
|
21580
22112
|
"id": "btn_submit",
|
|
21581
|
-
"widgetId": "core.form:
|
|
22113
|
+
"widgetId": "core.form:form-button",
|
|
21582
22114
|
"name": "submit",
|
|
21583
|
-
"type": "
|
|
22115
|
+
"type": "form-button",
|
|
21584
22116
|
"label": "Create profile",
|
|
21585
22117
|
"variant": "primary",
|
|
21586
22118
|
"buttonType": "submit"
|
|
@@ -31299,18 +31831,15 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31299
31831
|
return runtimeOptions;
|
|
31300
31832
|
}
|
|
31301
31833
|
const cfg = getEffectiveDataConfig(field);
|
|
31302
|
-
|
|
31303
|
-
|
|
31304
|
-
rows = this.resolveCollectionRows(rows, cfg, engine);
|
|
31305
|
-
// 2. Apply Filters
|
|
31306
|
-
rows = this.applyRowFilters(rows, cfg.filters, engine);
|
|
31834
|
+
const rowContexts = await this.getOptionRowContexts(cfg, field, engine);
|
|
31835
|
+
const filteredContexts = this.applyOptionRowFilters(rowContexts, cfg.filters, engine);
|
|
31307
31836
|
// 3. Map to Options
|
|
31308
31837
|
if (cfg.type === 'static') {
|
|
31309
|
-
return
|
|
31838
|
+
return filteredContexts.map(context => this.mapContextToOption(context, 'label', 'value', cfg));
|
|
31310
31839
|
}
|
|
31311
|
-
const labelKey = cfg.
|
|
31312
|
-
const valueKey = cfg.
|
|
31313
|
-
return
|
|
31840
|
+
const labelKey = cfg.labelKey || 'label';
|
|
31841
|
+
const valueKey = cfg.valueKey || 'value';
|
|
31842
|
+
return filteredContexts.map(context => this.mapContextToOption(context, labelKey, valueKey, cfg));
|
|
31314
31843
|
}
|
|
31315
31844
|
async queryOptions(field, query, engine) {
|
|
31316
31845
|
const runtimeOptions = await this.getRuntimeOptions(field, engine);
|
|
@@ -31428,8 +31957,13 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31428
31957
|
if (!rows || rows.length === 0) {
|
|
31429
31958
|
return field.defaultValue;
|
|
31430
31959
|
}
|
|
31431
|
-
const
|
|
31432
|
-
|
|
31960
|
+
const selectedRow = this.selectScalarRow(rows, cfg, engine);
|
|
31961
|
+
if (cfg.rowSelectionMode === 'selected' && !selectedRow) {
|
|
31962
|
+
const currentValue = field.name && engine ? engine.getValue(field.name) : undefined;
|
|
31963
|
+
return currentValue !== undefined ? currentValue : field.defaultValue;
|
|
31964
|
+
}
|
|
31965
|
+
const row = selectedRow ?? rows[0];
|
|
31966
|
+
const resolvedPath = cfg.valueKey;
|
|
31433
31967
|
const resolvedValue = resolvePathValue(row, resolvedPath);
|
|
31434
31968
|
if (resolvedPath && resolvedValue !== undefined) {
|
|
31435
31969
|
return resolvedValue;
|
|
@@ -31479,6 +32013,21 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31479
32013
|
const result = await this.client.query(cfg.datasourceId);
|
|
31480
32014
|
return result.rows;
|
|
31481
32015
|
}
|
|
32016
|
+
async getOptionRowContexts(cfg, field, engine) {
|
|
32017
|
+
const rows = await this.getRawRows(cfg, field, engine);
|
|
32018
|
+
return this.resolveCollectionRowContexts(rows, cfg, engine);
|
|
32019
|
+
}
|
|
32020
|
+
resolveCollectionRowContexts(rows, cfg, engine) {
|
|
32021
|
+
const parentContexts = this.extractRowContexts(rows, cfg.rowsPath);
|
|
32022
|
+
if (cfg.rowSelectionMode === 'selected' && cfg.childRowsPath) {
|
|
32023
|
+
const selectedParentContext = this.selectOptionContext(parentContexts, cfg, engine);
|
|
32024
|
+
if (!selectedParentContext) {
|
|
32025
|
+
return [];
|
|
32026
|
+
}
|
|
32027
|
+
return this.extractRowContexts([selectedParentContext.row], cfg.childRowsPath, selectedParentContext);
|
|
32028
|
+
}
|
|
32029
|
+
return parentContexts;
|
|
32030
|
+
}
|
|
31482
32031
|
resolveCollectionRows(rows, cfg, engine) {
|
|
31483
32032
|
const parentRows = this.extractRows(rows, cfg.rowsPath);
|
|
31484
32033
|
if (cfg.rowSelectionMode === 'selected' && cfg.childRowsPath) {
|
|
@@ -31493,6 +32042,38 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31493
32042
|
}
|
|
31494
32043
|
return parentRows;
|
|
31495
32044
|
}
|
|
32045
|
+
extractRowContexts(rows, path, sourceContext) {
|
|
32046
|
+
const normalizedPath = path?.trim();
|
|
32047
|
+
if (!normalizedPath) {
|
|
32048
|
+
return rows.map(row => ({
|
|
32049
|
+
row,
|
|
32050
|
+
parentRow: sourceContext?.row,
|
|
32051
|
+
sourceRow: sourceContext?.sourceRow ?? row
|
|
32052
|
+
}));
|
|
32053
|
+
}
|
|
32054
|
+
const flattened = [];
|
|
32055
|
+
for (const row of rows) {
|
|
32056
|
+
const resolved = resolvePathValue(row, normalizedPath);
|
|
32057
|
+
if (Array.isArray(resolved)) {
|
|
32058
|
+
for (const entry of resolved) {
|
|
32059
|
+
flattened.push({
|
|
32060
|
+
row: this.toRowRecord(entry),
|
|
32061
|
+
parentRow: sourceContext?.row,
|
|
32062
|
+
sourceRow: sourceContext?.sourceRow ?? row
|
|
32063
|
+
});
|
|
32064
|
+
}
|
|
32065
|
+
continue;
|
|
32066
|
+
}
|
|
32067
|
+
if (resolved !== undefined && resolved !== null) {
|
|
32068
|
+
flattened.push({
|
|
32069
|
+
row: this.toRowRecord(resolved),
|
|
32070
|
+
parentRow: sourceContext?.row,
|
|
32071
|
+
sourceRow: sourceContext?.sourceRow ?? row
|
|
32072
|
+
});
|
|
32073
|
+
}
|
|
32074
|
+
}
|
|
32075
|
+
return flattened;
|
|
32076
|
+
}
|
|
31496
32077
|
extractRows(rows, path) {
|
|
31497
32078
|
const normalizedPath = path?.trim();
|
|
31498
32079
|
if (!normalizedPath) {
|
|
@@ -31530,36 +32111,57 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31530
32111
|
}
|
|
31531
32112
|
return rows.find(row => valuesMatch(resolvePathValue(row, cfg.selectionMatchPath), selectorValue));
|
|
31532
32113
|
}
|
|
32114
|
+
selectOptionContext(contexts, cfg, engine) {
|
|
32115
|
+
if (contexts.length === 0)
|
|
32116
|
+
return undefined;
|
|
32117
|
+
if (cfg.rowSelectionMode !== 'selected' || !cfg.selectionFieldId || !cfg.selectionMatchPath || !engine) {
|
|
32118
|
+
return contexts[0];
|
|
32119
|
+
}
|
|
32120
|
+
const schema = engine.getSchema();
|
|
32121
|
+
const selectorField = schema.fields.find(candidate => candidate.id === cfg.selectionFieldId);
|
|
32122
|
+
if (!selectorField) {
|
|
32123
|
+
return undefined;
|
|
32124
|
+
}
|
|
32125
|
+
const selectorValue = engine.getValue(selectorField.name);
|
|
32126
|
+
if (selectorValue === undefined || selectorValue === null || selectorValue === '') {
|
|
32127
|
+
return undefined;
|
|
32128
|
+
}
|
|
32129
|
+
return contexts.find(context => valuesMatch(resolvePathValue(context.row, cfg.selectionMatchPath), selectorValue));
|
|
32130
|
+
}
|
|
31533
32131
|
applyRowFilters(rows, filters, engine) {
|
|
31534
32132
|
if (!filters || filters.length === 0)
|
|
31535
32133
|
return rows;
|
|
31536
|
-
return rows.filter(row =>
|
|
31537
|
-
|
|
31538
|
-
|
|
31539
|
-
|
|
31540
|
-
|
|
31541
|
-
|
|
31542
|
-
|
|
31543
|
-
|
|
31544
|
-
|
|
31545
|
-
|
|
31546
|
-
|
|
31547
|
-
|
|
31548
|
-
|
|
31549
|
-
|
|
31550
|
-
|
|
31551
|
-
|
|
31552
|
-
|
|
31553
|
-
|
|
31554
|
-
|
|
31555
|
-
|
|
31556
|
-
|
|
31557
|
-
|
|
31558
|
-
|
|
31559
|
-
|
|
31560
|
-
|
|
31561
|
-
|
|
31562
|
-
|
|
32134
|
+
return rows.filter(row => this.matchesRowFilters(row, filters, engine));
|
|
32135
|
+
}
|
|
32136
|
+
applyOptionRowFilters(contexts, filters, engine) {
|
|
32137
|
+
if (!filters || filters.length === 0)
|
|
32138
|
+
return contexts;
|
|
32139
|
+
return contexts.filter(context => this.matchesRowFilters(context.row, filters, engine));
|
|
32140
|
+
}
|
|
32141
|
+
matchesRowFilters(row, filters, engine) {
|
|
32142
|
+
return filters.every(filter => {
|
|
32143
|
+
const expected = this.resolveFilterValue(filter, engine);
|
|
32144
|
+
if (expected === undefined)
|
|
32145
|
+
return true;
|
|
32146
|
+
const actual = resolvePathValue(row, filter.column);
|
|
32147
|
+
const val = expected;
|
|
32148
|
+
switch (filter.op) {
|
|
32149
|
+
case 'eq': return actual === val;
|
|
32150
|
+
case 'neq': return actual !== val;
|
|
32151
|
+
case 'in': return Array.isArray(val) ? val.includes(actual) : actual === val;
|
|
32152
|
+
case 'contains':
|
|
32153
|
+
case 'like':
|
|
32154
|
+
return String(actual ?? '').toLowerCase().includes(String(val ?? '').toLowerCase());
|
|
32155
|
+
case 'startsWith':
|
|
32156
|
+
return String(actual ?? '').toLowerCase().startsWith(String(val ?? '').toLowerCase());
|
|
32157
|
+
case 'endsWith':
|
|
32158
|
+
return String(actual ?? '').toLowerCase().endsWith(String(val ?? '').toLowerCase());
|
|
32159
|
+
case 'gt': return (actual ?? 0) > (val ?? 0);
|
|
32160
|
+
case 'gte': return (actual ?? 0) >= (val ?? 0);
|
|
32161
|
+
case 'lt': return (actual ?? 0) < (val ?? 0);
|
|
32162
|
+
case 'lte': return (actual ?? 0) <= (val ?? 0);
|
|
32163
|
+
default: return true;
|
|
32164
|
+
}
|
|
31563
32165
|
});
|
|
31564
32166
|
}
|
|
31565
32167
|
resolveFilters(cfg, engine) {
|
|
@@ -31617,6 +32219,56 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31617
32219
|
value: this.toOptionValue(resolvePathValue(row, valueKey))
|
|
31618
32220
|
};
|
|
31619
32221
|
}
|
|
32222
|
+
mapContextToOption(context, labelKey, valueKey, cfg) {
|
|
32223
|
+
const rawLabel = String(resolvePathValue(context.row, labelKey) ?? '');
|
|
32224
|
+
const prefix = this.resolveOptionLabelPrefix(context, cfg);
|
|
32225
|
+
const label = this.formatOptionLabel(rawLabel, cfg);
|
|
32226
|
+
return {
|
|
32227
|
+
label: prefix ? `${prefix} ${label}` : label,
|
|
32228
|
+
value: this.toOptionValue(resolvePathValue(context.row, valueKey))
|
|
32229
|
+
};
|
|
32230
|
+
}
|
|
32231
|
+
resolveOptionLabelPrefix(context, cfg) {
|
|
32232
|
+
const prefixPath = cfg.optionLabelPrefixPath?.trim();
|
|
32233
|
+
if (!prefixPath) {
|
|
32234
|
+
return '';
|
|
32235
|
+
}
|
|
32236
|
+
for (const candidate of [context.row, context.parentRow, context.sourceRow]) {
|
|
32237
|
+
if (!candidate)
|
|
32238
|
+
continue;
|
|
32239
|
+
const resolved = resolvePathValue(candidate, prefixPath);
|
|
32240
|
+
if (resolved === undefined || resolved === null)
|
|
32241
|
+
continue;
|
|
32242
|
+
const value = String(resolved).trim();
|
|
32243
|
+
if (value.length > 0) {
|
|
32244
|
+
return value;
|
|
32245
|
+
}
|
|
32246
|
+
}
|
|
32247
|
+
return '';
|
|
32248
|
+
}
|
|
32249
|
+
formatOptionLabel(label, cfg) {
|
|
32250
|
+
if (!cfg.formatNumericOptionLabels) {
|
|
32251
|
+
return label;
|
|
32252
|
+
}
|
|
32253
|
+
const trimmed = label.trim();
|
|
32254
|
+
if (!trimmed) {
|
|
32255
|
+
return label;
|
|
32256
|
+
}
|
|
32257
|
+
const normalized = trimmed.replace(/,/g, '');
|
|
32258
|
+
if (!/^-?\d+(\.\d+)?$/.test(normalized)) {
|
|
32259
|
+
return label;
|
|
32260
|
+
}
|
|
32261
|
+
const numericValue = Number(normalized);
|
|
32262
|
+
if (!Number.isFinite(numericValue)) {
|
|
32263
|
+
return label;
|
|
32264
|
+
}
|
|
32265
|
+
const fractionPart = normalized.split('.')[1];
|
|
32266
|
+
return new Intl.NumberFormat(undefined, {
|
|
32267
|
+
useGrouping: true,
|
|
32268
|
+
minimumFractionDigits: fractionPart?.length ?? 0,
|
|
32269
|
+
maximumFractionDigits: fractionPart?.length ?? 0
|
|
32270
|
+
}).format(numericValue);
|
|
32271
|
+
}
|
|
31620
32272
|
async getRuntimeOptions(field, engine) {
|
|
31621
32273
|
if (!engine)
|
|
31622
32274
|
return undefined;
|
|
@@ -31631,7 +32283,7 @@ class DefaultDataProvider extends DataProvider {
|
|
|
31631
32283
|
return cfg.type === 'source' || cfg.type === 'global' || cfg.type === 'api';
|
|
31632
32284
|
}
|
|
31633
32285
|
shouldUseLocalResolution(cfg) {
|
|
31634
|
-
if (cfg.rowsPath || cfg.
|
|
32286
|
+
if (cfg.rowsPath || cfg.optionLabelPrefixPath || cfg.rowSelectionMode || cfg.selectionFieldId || cfg.selectionMatchPath || cfg.childRowsPath) {
|
|
31635
32287
|
return true;
|
|
31636
32288
|
}
|
|
31637
32289
|
if (hasPathSyntax(cfg.labelKey) || hasPathSyntax(cfg.valueKey)) {
|
|
@@ -32022,7 +32674,6 @@ class TextFieldWidgetComponent {
|
|
|
32022
32674
|
dataConfig.type ?? '',
|
|
32023
32675
|
sourceKey,
|
|
32024
32676
|
String(dataConfig.valueKey ?? ''),
|
|
32025
|
-
String(dataConfig.valuePath ?? ''),
|
|
32026
32677
|
String(dataConfig.rowsPath ?? ''),
|
|
32027
32678
|
String(dataConfig.rowSelectionMode ?? ''),
|
|
32028
32679
|
String(dataConfig.selectionFieldId ?? ''),
|
|
@@ -32817,12 +33468,14 @@ class SelectWidgetComponent {
|
|
|
32817
33468
|
loading = false;
|
|
32818
33469
|
loadError = null;
|
|
32819
33470
|
options = [];
|
|
33471
|
+
rawOptions = [];
|
|
32820
33472
|
searchTerms$ = new Subject();
|
|
32821
33473
|
requestId = 0;
|
|
32822
33474
|
currentSearchTerm = '';
|
|
32823
33475
|
runtimeManagedField = false;
|
|
32824
33476
|
runtimeOptionsLoaded = false;
|
|
32825
33477
|
dependencyValueSnapshot = new Map();
|
|
33478
|
+
displayDependencyValueSnapshot = new Map();
|
|
32826
33479
|
cachedStyleSource;
|
|
32827
33480
|
cachedWrapperStyles = { width: '100%' };
|
|
32828
33481
|
cachedControlCssVars = this.toCssVarMap({});
|
|
@@ -32864,7 +33517,7 @@ class SelectWidgetComponent {
|
|
|
32864
33517
|
return;
|
|
32865
33518
|
this.runtimeOptionsLoaded = false;
|
|
32866
33519
|
this.currentSearchTerm = '';
|
|
32867
|
-
this.
|
|
33520
|
+
this.setOptionsFromRaw([]);
|
|
32868
33521
|
void this.loadOptions(this.currentSearchTerm);
|
|
32869
33522
|
});
|
|
32870
33523
|
}
|
|
@@ -32886,7 +33539,7 @@ class SelectWidgetComponent {
|
|
|
32886
33539
|
this.runtimeManagedField = this.runtimeFieldDataAccessRegistry.hasFieldAccess(this.config, this.engine);
|
|
32887
33540
|
if (!this.runtimeManagedField) {
|
|
32888
33541
|
if (this.areApiCallsSuppressed() && this.hasSelectedValue()) {
|
|
32889
|
-
this.
|
|
33542
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions([]));
|
|
32890
33543
|
}
|
|
32891
33544
|
else {
|
|
32892
33545
|
void this.loadOptions(this.currentSearchTerm);
|
|
@@ -32895,7 +33548,7 @@ class SelectWidgetComponent {
|
|
|
32895
33548
|
}
|
|
32896
33549
|
else if (this.hasSelectedValue()) {
|
|
32897
33550
|
if (this.areApiCallsSuppressed()) {
|
|
32898
|
-
this.
|
|
33551
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions([]));
|
|
32899
33552
|
this.runtimeOptionsLoaded = true;
|
|
32900
33553
|
}
|
|
32901
33554
|
else {
|
|
@@ -32904,32 +33557,38 @@ class SelectWidgetComponent {
|
|
|
32904
33557
|
});
|
|
32905
33558
|
}
|
|
32906
33559
|
}
|
|
32907
|
-
const dependencyIds = this.getDependencyFieldIds();
|
|
32908
33560
|
if (this.engine) {
|
|
32909
33561
|
const initialValues = this.engine.getValues();
|
|
32910
|
-
this.
|
|
33562
|
+
this.seedValueSnapshotFromValues(this.dependencyValueSnapshot, this.getDependencyFieldIds(), initialValues);
|
|
33563
|
+
this.seedValueSnapshotFromValues(this.displayDependencyValueSnapshot, this.getDisplayDependencyFieldIds(), initialValues);
|
|
32911
33564
|
this.engine.valueChanges$
|
|
32912
33565
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
32913
33566
|
.subscribe(values => {
|
|
32914
33567
|
this.syncEnabledState();
|
|
32915
33568
|
if (this.areApiCallsSuppressed()) {
|
|
32916
|
-
this.
|
|
33569
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(this.rawOptions));
|
|
32917
33570
|
this.cdr.markForCheck();
|
|
32918
33571
|
}
|
|
32919
|
-
if (dependencyIds.length === 0)
|
|
32920
|
-
return;
|
|
32921
33572
|
const schema = this.engine.getSchema();
|
|
32922
|
-
const
|
|
32923
|
-
|
|
32924
|
-
|
|
32925
|
-
|
|
33573
|
+
const dependencyIds = this.getDependencyFieldIds();
|
|
33574
|
+
const displayDependencyIds = this.getDisplayDependencyFieldIds();
|
|
33575
|
+
const dependencyNames = this.resolveFieldNames(schema.fields, dependencyIds);
|
|
33576
|
+
const displayDependencyNames = this.resolveFieldNames(schema.fields, displayDependencyIds);
|
|
33577
|
+
if (displayDependencyNames.length > 0 && this.haveFieldValuesChanged(this.displayDependencyValueSnapshot, displayDependencyNames, values)) {
|
|
33578
|
+
this.applyDisplayFormatting();
|
|
33579
|
+
this.syncStoredFieldLabel(this.control.value);
|
|
33580
|
+
this.cdr.markForCheck();
|
|
33581
|
+
}
|
|
33582
|
+
if (dependencyNames.length === 0)
|
|
33583
|
+
return;
|
|
33584
|
+
if (!this.haveFieldValuesChanged(this.dependencyValueSnapshot, dependencyNames, values))
|
|
32926
33585
|
return;
|
|
32927
33586
|
this.runtimeFieldDataAccessRegistry.invalidateFieldAndDescendants(this.engine, this.config.id);
|
|
32928
33587
|
if (this.hasSelectedValue()) {
|
|
32929
33588
|
this.control.setValue(null);
|
|
32930
33589
|
}
|
|
32931
33590
|
this.currentSearchTerm = '';
|
|
32932
|
-
this.
|
|
33591
|
+
this.setOptionsFromRaw([]);
|
|
32933
33592
|
this.runtimeOptionsLoaded = false;
|
|
32934
33593
|
if (!this.runtimeManagedField) {
|
|
32935
33594
|
void this.loadOptions(this.currentSearchTerm);
|
|
@@ -33072,6 +33731,10 @@ class SelectWidgetComponent {
|
|
|
33072
33731
|
return [];
|
|
33073
33732
|
return cfg.dependsOn.map(d => d.fieldId).filter((id) => !!id);
|
|
33074
33733
|
}
|
|
33734
|
+
getDisplayDependencyFieldIds() {
|
|
33735
|
+
const prefixFieldId = this.config.dataConfig?.optionLabelPrefixFieldId;
|
|
33736
|
+
return prefixFieldId ? [prefixFieldId] : [];
|
|
33737
|
+
}
|
|
33075
33738
|
toCssVarMap(controlStyles) {
|
|
33076
33739
|
const vars = {
|
|
33077
33740
|
'--fd-select-border-color': OUTLINED_FIELD_IDLE_BORDER_COLOR,
|
|
@@ -33140,7 +33803,7 @@ class SelectWidgetComponent {
|
|
|
33140
33803
|
opts = await this.dataProvider.getOptions(this.config, this.engine);
|
|
33141
33804
|
}
|
|
33142
33805
|
if (this.requestId === reqId) {
|
|
33143
|
-
this.
|
|
33806
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(opts));
|
|
33144
33807
|
this.syncStoredFieldLabel(this.control.value);
|
|
33145
33808
|
this.loading = false;
|
|
33146
33809
|
this.loadError = null;
|
|
@@ -33152,7 +33815,7 @@ class SelectWidgetComponent {
|
|
|
33152
33815
|
this.loading = false;
|
|
33153
33816
|
this.loadError = 'Failed to load options.';
|
|
33154
33817
|
if (!isSearch) {
|
|
33155
|
-
this.
|
|
33818
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(this.config.staticOptions || []));
|
|
33156
33819
|
}
|
|
33157
33820
|
this.cdr.markForCheck();
|
|
33158
33821
|
}
|
|
@@ -33173,7 +33836,7 @@ class SelectWidgetComponent {
|
|
|
33173
33836
|
if (this.runtimeOptionsLoaded && this.options.length > 0)
|
|
33174
33837
|
return;
|
|
33175
33838
|
if (this.areApiCallsSuppressed()) {
|
|
33176
|
-
this.
|
|
33839
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions(this.rawOptions));
|
|
33177
33840
|
this.runtimeOptionsLoaded = true;
|
|
33178
33841
|
return;
|
|
33179
33842
|
}
|
|
@@ -33182,25 +33845,29 @@ class SelectWidgetComponent {
|
|
|
33182
33845
|
this.runtimeOptionsLoaded = true;
|
|
33183
33846
|
}
|
|
33184
33847
|
}
|
|
33185
|
-
|
|
33186
|
-
if (!this.engine ||
|
|
33848
|
+
seedValueSnapshotFromValues(snapshot, fieldIds, values) {
|
|
33849
|
+
if (!this.engine || fieldIds.length === 0)
|
|
33187
33850
|
return;
|
|
33188
|
-
const
|
|
33189
|
-
|
|
33190
|
-
.filter(field => dependencyIds.includes(field.id))
|
|
33191
|
-
.map(field => field.name);
|
|
33192
|
-
for (const depName of depNames) {
|
|
33193
|
-
this.dependencyValueSnapshot.set(depName, values[depName]);
|
|
33851
|
+
for (const fieldName of this.resolveFieldNames(this.engine.getSchema().fields, fieldIds)) {
|
|
33852
|
+
snapshot.set(fieldName, values[fieldName]);
|
|
33194
33853
|
}
|
|
33195
33854
|
}
|
|
33196
|
-
|
|
33855
|
+
resolveFieldNames(fields, fieldIds) {
|
|
33856
|
+
if (fieldIds.length === 0) {
|
|
33857
|
+
return [];
|
|
33858
|
+
}
|
|
33859
|
+
return fields
|
|
33860
|
+
.filter(field => fieldIds.includes(field.id))
|
|
33861
|
+
.map(field => field.name);
|
|
33862
|
+
}
|
|
33863
|
+
haveFieldValuesChanged(snapshot, fieldNames, values) {
|
|
33197
33864
|
let changed = false;
|
|
33198
|
-
for (const
|
|
33199
|
-
const previousValue =
|
|
33200
|
-
const nextValue = values[
|
|
33865
|
+
for (const fieldName of fieldNames) {
|
|
33866
|
+
const previousValue = snapshot.get(fieldName);
|
|
33867
|
+
const nextValue = values[fieldName];
|
|
33201
33868
|
if (!Object.is(previousValue, nextValue)) {
|
|
33202
33869
|
changed = true;
|
|
33203
|
-
|
|
33870
|
+
snapshot.set(fieldName, nextValue);
|
|
33204
33871
|
}
|
|
33205
33872
|
}
|
|
33206
33873
|
return changed;
|
|
@@ -33212,12 +33879,19 @@ class SelectWidgetComponent {
|
|
|
33212
33879
|
this.syncEnabledState();
|
|
33213
33880
|
this.runtimeManagedField = this.runtimeFieldDataAccessRegistry.hasFieldAccess(this.config, this.engine);
|
|
33214
33881
|
this.runtimeOptionsLoaded = false;
|
|
33215
|
-
this.
|
|
33882
|
+
this.dependencyValueSnapshot.clear();
|
|
33883
|
+
this.displayDependencyValueSnapshot.clear();
|
|
33884
|
+
this.setOptionsFromRaw([]);
|
|
33216
33885
|
this.loadError = null;
|
|
33217
33886
|
this.currentSearchTerm = '';
|
|
33887
|
+
if (this.engine) {
|
|
33888
|
+
const currentValues = this.engine.getValues();
|
|
33889
|
+
this.seedValueSnapshotFromValues(this.dependencyValueSnapshot, this.getDependencyFieldIds(), currentValues);
|
|
33890
|
+
this.seedValueSnapshotFromValues(this.displayDependencyValueSnapshot, this.getDisplayDependencyFieldIds(), currentValues);
|
|
33891
|
+
}
|
|
33218
33892
|
if (!this.runtimeManagedField) {
|
|
33219
33893
|
if (this.areApiCallsSuppressed() && this.hasSelectedValue()) {
|
|
33220
|
-
this.
|
|
33894
|
+
this.setOptionsFromRaw(this.withSelectedValueFallbackOptions([]));
|
|
33221
33895
|
return;
|
|
33222
33896
|
}
|
|
33223
33897
|
void this.loadOptions(this.currentSearchTerm);
|
|
@@ -33253,6 +33927,8 @@ class SelectWidgetComponent {
|
|
|
33253
33927
|
String(dataConfig.datasourceId ?? ''),
|
|
33254
33928
|
String(dataConfig.labelKey ?? ''),
|
|
33255
33929
|
String(dataConfig.valueKey ?? ''),
|
|
33930
|
+
String(dataConfig.formatNumericOptionLabels ?? ''),
|
|
33931
|
+
String(dataConfig.optionLabelPrefixFieldId ?? ''),
|
|
33256
33932
|
String(dataConfig.searchEnabled ?? ''),
|
|
33257
33933
|
String(dataConfig.optionsLimit ?? ''),
|
|
33258
33934
|
dependencySignature,
|
|
@@ -33297,6 +33973,62 @@ class SelectWidgetComponent {
|
|
|
33297
33973
|
this.cachedInputAttributes = { 'aria-label': accessibleLabel };
|
|
33298
33974
|
}
|
|
33299
33975
|
}
|
|
33976
|
+
setOptionsFromRaw(options) {
|
|
33977
|
+
this.rawOptions = [...options];
|
|
33978
|
+
this.applyDisplayFormatting();
|
|
33979
|
+
}
|
|
33980
|
+
applyDisplayFormatting() {
|
|
33981
|
+
this.options = this.rawOptions.map(option => ({
|
|
33982
|
+
...option,
|
|
33983
|
+
label: this.formatOptionLabel(option.label)
|
|
33984
|
+
}));
|
|
33985
|
+
}
|
|
33986
|
+
formatOptionLabel(label) {
|
|
33987
|
+
const formattedLabel = this.config.dataConfig?.formatNumericOptionLabels
|
|
33988
|
+
? this.formatNumericLabel(label)
|
|
33989
|
+
: label;
|
|
33990
|
+
const prefix = this.resolveOptionLabelPrefix();
|
|
33991
|
+
return prefix ? `${prefix} ${formattedLabel}` : formattedLabel;
|
|
33992
|
+
}
|
|
33993
|
+
resolveOptionLabelPrefix() {
|
|
33994
|
+
const prefixFieldId = this.config.dataConfig?.optionLabelPrefixFieldId;
|
|
33995
|
+
if (!prefixFieldId
|
|
33996
|
+
|| !this.engine
|
|
33997
|
+
|| typeof this.engine.getValue !== 'function') {
|
|
33998
|
+
return '';
|
|
33999
|
+
}
|
|
34000
|
+
const prefixField = this.engine.getSchema().fields.find(field => field.id === prefixFieldId);
|
|
34001
|
+
if (!prefixField) {
|
|
34002
|
+
return '';
|
|
34003
|
+
}
|
|
34004
|
+
const value = this.engine.getValue(prefixField.name);
|
|
34005
|
+
if (value === undefined || value === null) {
|
|
34006
|
+
return '';
|
|
34007
|
+
}
|
|
34008
|
+
const trimmed = String(value).trim();
|
|
34009
|
+
return trimmed;
|
|
34010
|
+
}
|
|
34011
|
+
formatNumericLabel(label) {
|
|
34012
|
+
const trimmed = label.trim();
|
|
34013
|
+
if (!trimmed) {
|
|
34014
|
+
return label;
|
|
34015
|
+
}
|
|
34016
|
+
const normalized = trimmed.replace(/,/g, '');
|
|
34017
|
+
if (!/^-?\d+(\.\d+)?$/.test(normalized)) {
|
|
34018
|
+
return label;
|
|
34019
|
+
}
|
|
34020
|
+
const numericValue = Number(normalized);
|
|
34021
|
+
if (!Number.isFinite(numericValue)) {
|
|
34022
|
+
return label;
|
|
34023
|
+
}
|
|
34024
|
+
const fractionPart = normalized.split('.')[1];
|
|
34025
|
+
const formatter = new Intl.NumberFormat(undefined, {
|
|
34026
|
+
useGrouping: true,
|
|
34027
|
+
minimumFractionDigits: fractionPart?.length ?? 0,
|
|
34028
|
+
maximumFractionDigits: fractionPart?.length ?? 0
|
|
34029
|
+
});
|
|
34030
|
+
return formatter.format(numericValue);
|
|
34031
|
+
}
|
|
33300
34032
|
toSafeNonNegativeInt(value, fallback) {
|
|
33301
34033
|
if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {
|
|
33302
34034
|
return Math.floor(value);
|
|
@@ -33330,7 +34062,11 @@ class SelectWidgetComponent {
|
|
|
33330
34062
|
if (!this.hasMeaningfulValue(rawValue)) {
|
|
33331
34063
|
return [];
|
|
33332
34064
|
}
|
|
33333
|
-
|
|
34065
|
+
const storedLabel = this.getStoredFieldLabel();
|
|
34066
|
+
if (this.shouldUseStoredFallbackLabel(storedLabel)) {
|
|
34067
|
+
return buildFallbackOptions(rawValue, storedLabel);
|
|
34068
|
+
}
|
|
34069
|
+
return buildFallbackOptions(rawValue);
|
|
33334
34070
|
}
|
|
33335
34071
|
hasOptionValue(options, value) {
|
|
33336
34072
|
return options.some(option => Object.is(option.value, value) || String(option.value) === String(value));
|
|
@@ -33369,6 +34105,18 @@ class SelectWidgetComponent {
|
|
|
33369
34105
|
}
|
|
33370
34106
|
return this.engine.getFieldLabel(this.config.name);
|
|
33371
34107
|
}
|
|
34108
|
+
shouldUseStoredFallbackLabel(label) {
|
|
34109
|
+
if (label === undefined) {
|
|
34110
|
+
return false;
|
|
34111
|
+
}
|
|
34112
|
+
if (this.config.dataConfig?.optionLabelPrefixPath) {
|
|
34113
|
+
return true;
|
|
34114
|
+
}
|
|
34115
|
+
if (this.config.dataConfig?.optionLabelPrefixFieldId) {
|
|
34116
|
+
return this.resolveOptionLabelPrefix().length === 0;
|
|
34117
|
+
}
|
|
34118
|
+
return this.config.dataConfig?.formatNumericOptionLabels !== true;
|
|
34119
|
+
}
|
|
33372
34120
|
setStoredFieldLabel(label) {
|
|
33373
34121
|
if (!this.engine || typeof this.engine.setFieldLabel !== 'function') {
|
|
33374
34122
|
return;
|
|
@@ -36926,10 +37674,6 @@ const FILE_BASIC_FIELDS = [
|
|
|
36926
37674
|
{ key: 'tooltip', type: 'text', label: 'Tooltip' },
|
|
36927
37675
|
...COMMON_GROUP_FIELDS
|
|
36928
37676
|
];
|
|
36929
|
-
const COMMON_ICON_FIELDS = [
|
|
36930
|
-
{ key: 'prefixIcon', type: 'text', label: 'Prefix Icon (Material Name)' },
|
|
36931
|
-
{ key: 'suffixIcon', type: 'text', label: 'Suffix Icon (Material Name)' }
|
|
36932
|
-
];
|
|
36933
37677
|
const COMMON_APPEARANCE_FIELDS = [
|
|
36934
37678
|
{
|
|
36935
37679
|
key: 'appearance',
|
|
@@ -36964,18 +37708,6 @@ const COMMON_STYLE_SECTIONS = [
|
|
|
36964
37708
|
STYLE_TRANSFORM_SECTION,
|
|
36965
37709
|
STYLE_EFFECTS_SECTION
|
|
36966
37710
|
];
|
|
36967
|
-
const COMMON_VALIDATION_SECTION = {
|
|
36968
|
-
label: 'Validation',
|
|
36969
|
-
fields: [
|
|
36970
|
-
{ key: 'validators', type: 'validators-editor', label: 'Validation Rules' }
|
|
36971
|
-
]
|
|
36972
|
-
};
|
|
36973
|
-
const COMMON_CONDITIONAL_SECTION = {
|
|
36974
|
-
label: 'Conditional',
|
|
36975
|
-
fields: [
|
|
36976
|
-
{ key: 'conditional', type: 'conditional-editor', label: 'Conditional Logic' }
|
|
36977
|
-
]
|
|
36978
|
-
};
|
|
36979
37711
|
// Start with standard sections for Text Field
|
|
36980
37712
|
const BASE_REQUIRED_FIELD = {
|
|
36981
37713
|
key: 'html5.required',
|
|
@@ -36983,67 +37715,25 @@ const BASE_REQUIRED_FIELD = {
|
|
|
36983
37715
|
label: 'Required',
|
|
36984
37716
|
helpText: 'Base required state; Rules can override this dynamically.'
|
|
36985
37717
|
};
|
|
36986
|
-
const
|
|
36987
|
-
|
|
36988
|
-
|
|
36989
|
-
|
|
36990
|
-
|
|
36991
|
-
{ key: 'html5.maxLength', type: 'number', label: 'Max Length' },
|
|
36992
|
-
{ key: 'html5.pattern', type: 'text', label: 'Pattern (regex)' },
|
|
36993
|
-
{ key: 'validators', type: 'validators-editor', label: 'Advanced Rules' }
|
|
36994
|
-
]
|
|
36995
|
-
};
|
|
36996
|
-
const NUMBER_VALIDATION_SECTION = {
|
|
36997
|
-
label: 'Validation',
|
|
36998
|
-
fields: [
|
|
36999
|
-
BASE_REQUIRED_FIELD,
|
|
37000
|
-
{ key: 'html5.min', type: 'number', label: 'Minimum Value' },
|
|
37001
|
-
{ key: 'html5.max', type: 'number', label: 'Maximum Value' },
|
|
37002
|
-
{ key: 'html5.step', type: 'number', label: 'Step (Increment)' },
|
|
37003
|
-
{ key: 'validators', type: 'validators-editor', label: 'Advanced Rules' }
|
|
37004
|
-
]
|
|
37005
|
-
};
|
|
37006
|
-
const DATE_VALIDATION_SECTION = {
|
|
37007
|
-
label: 'Validation',
|
|
37008
|
-
fields: [
|
|
37009
|
-
BASE_REQUIRED_FIELD,
|
|
37010
|
-
{ key: 'html5.min', type: 'text', label: 'Earliest Date (YYYY-MM-DD)' },
|
|
37011
|
-
{ key: 'html5.max', type: 'text', label: 'Latest Date (YYYY-MM-DD)' },
|
|
37012
|
-
{ key: 'validators', type: 'validators-editor', label: 'Advanced Rules' }
|
|
37013
|
-
]
|
|
37014
|
-
};
|
|
37015
|
-
const TIME_VALIDATION_SECTION = {
|
|
37016
|
-
label: 'Validation',
|
|
37017
|
-
fields: [
|
|
37018
|
-
BASE_REQUIRED_FIELD,
|
|
37019
|
-
{ key: 'html5.min', type: 'text', label: 'Earliest Time (HH:MM)' },
|
|
37020
|
-
{ key: 'html5.max', type: 'text', label: 'Latest Time (HH:MM)' },
|
|
37021
|
-
{ key: 'validators', type: 'validators-editor', label: 'Advanced Rules' }
|
|
37022
|
-
]
|
|
37023
|
-
};
|
|
37024
|
-
const DATETIME_VALIDATION_SECTION = {
|
|
37025
|
-
label: 'Validation',
|
|
37026
|
-
fields: [
|
|
37027
|
-
BASE_REQUIRED_FIELD,
|
|
37028
|
-
{ key: 'html5.min', type: 'text', label: 'Earliest (YYYY-MM-DDTHH:MM)' },
|
|
37029
|
-
{ key: 'html5.max', type: 'text', label: 'Latest (YYYY-MM-DDTHH:MM)' },
|
|
37030
|
-
{ key: 'validators', type: 'validators-editor', label: 'Advanced Rules' }
|
|
37031
|
-
]
|
|
37032
|
-
};
|
|
37033
|
-
const SELECT_VALIDATION_SECTION = {
|
|
37034
|
-
label: 'Validation',
|
|
37035
|
-
fields: [
|
|
37036
|
-
BASE_REQUIRED_FIELD,
|
|
37037
|
-
{ key: 'validators', type: 'validators-editor', label: 'Advanced Rules' }
|
|
37038
|
-
]
|
|
37039
|
-
};
|
|
37040
|
-
const FILE_VALIDATION_SECTION = {
|
|
37041
|
-
label: 'Validation',
|
|
37042
|
-
fields: [
|
|
37043
|
-
BASE_REQUIRED_FIELD,
|
|
37044
|
-
{ key: 'validators', type: 'validators-editor', label: 'Advanced Rules' }
|
|
37045
|
-
]
|
|
37718
|
+
const CUSTOM_VALIDATION_RULES_FIELD = {
|
|
37719
|
+
key: 'validation',
|
|
37720
|
+
type: 'validators-editor',
|
|
37721
|
+
label: 'Custom Validation Rules',
|
|
37722
|
+
helpText: 'Use these for field-level validation checks. Use the Rules tab for conditional behaviour.'
|
|
37046
37723
|
};
|
|
37724
|
+
function createValidationSection(...fields) {
|
|
37725
|
+
return {
|
|
37726
|
+
label: 'Validation',
|
|
37727
|
+
fields: [...fields, CUSTOM_VALIDATION_RULES_FIELD]
|
|
37728
|
+
};
|
|
37729
|
+
}
|
|
37730
|
+
const TEXT_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD, { key: 'html5.minLength', type: 'number', label: 'Min Length' }, { key: 'html5.maxLength', type: 'number', label: 'Max Length' }, { key: 'html5.pattern', type: 'text', label: 'Pattern (regex)' });
|
|
37731
|
+
const NUMBER_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD, { key: 'html5.min', type: 'number', label: 'Minimum Value' }, { key: 'html5.max', type: 'number', label: 'Maximum Value' }, { key: 'html5.step', type: 'number', label: 'Step (Increment)' });
|
|
37732
|
+
const DATE_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD, { key: 'html5.min', type: 'text', label: 'Earliest Date (YYYY-MM-DD)' }, { key: 'html5.max', type: 'text', label: 'Latest Date (YYYY-MM-DD)' });
|
|
37733
|
+
const TIME_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD, { key: 'html5.min', type: 'text', label: 'Earliest Time (HH:MM)' }, { key: 'html5.max', type: 'text', label: 'Latest Time (HH:MM)' });
|
|
37734
|
+
const DATETIME_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD, { key: 'html5.min', type: 'text', label: 'Earliest (YYYY-MM-DDTHH:MM)' }, { key: 'html5.max', type: 'text', label: 'Latest (YYYY-MM-DDTHH:MM)' });
|
|
37735
|
+
const SELECT_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD);
|
|
37736
|
+
const FILE_VALIDATION_SECTION = createValidationSection(BASE_REQUIRED_FIELD);
|
|
37047
37737
|
const BUTTON_VARIANT_OPTIONS = [
|
|
37048
37738
|
{ label: 'Primary (Blue)', value: 'primary' },
|
|
37049
37739
|
{ label: 'Secondary (Outline)', value: 'secondary' }
|
|
@@ -37084,7 +37774,6 @@ const IMAGE_BUTTON_PROPERTIES = [
|
|
|
37084
37774
|
];
|
|
37085
37775
|
const TEXT_WIDGET_PROPERTIES = [
|
|
37086
37776
|
{ label: 'Basic', fields: COMMON_BASIC_FIELDS },
|
|
37087
|
-
{ label: 'Icons', fields: COMMON_ICON_FIELDS },
|
|
37088
37777
|
TEXT_VALIDATION_SECTION,
|
|
37089
37778
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37090
37779
|
...COMMON_STYLE_SECTIONS
|
|
@@ -37115,7 +37804,6 @@ const FILE_WIDGET_PROPERTIES = [
|
|
|
37115
37804
|
]
|
|
37116
37805
|
},
|
|
37117
37806
|
FILE_VALIDATION_SECTION,
|
|
37118
|
-
COMMON_CONDITIONAL_SECTION,
|
|
37119
37807
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37120
37808
|
...COMMON_STYLE_SECTIONS
|
|
37121
37809
|
];
|
|
@@ -37250,9 +37938,7 @@ const FIELD_WIDGETS = [
|
|
|
37250
37938
|
...COMMON_BASIC_FIELDS
|
|
37251
37939
|
]
|
|
37252
37940
|
},
|
|
37253
|
-
{ label: 'Icons', fields: COMMON_ICON_FIELDS },
|
|
37254
37941
|
NUMBER_VALIDATION_SECTION,
|
|
37255
|
-
COMMON_CONDITIONAL_SECTION,
|
|
37256
37942
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37257
37943
|
...COMMON_STYLE_SECTIONS
|
|
37258
37944
|
]
|
|
@@ -37280,7 +37966,6 @@ const FIELD_WIDGETS = [
|
|
|
37280
37966
|
dataBinding: { shape: 'scalar', targetPath: 'defaultValue' },
|
|
37281
37967
|
properties: [
|
|
37282
37968
|
{ label: 'Basic', fields: COMMON_BASIC_FIELDS },
|
|
37283
|
-
{ label: 'Icons', fields: COMMON_ICON_FIELDS },
|
|
37284
37969
|
DATE_VALIDATION_SECTION,
|
|
37285
37970
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37286
37971
|
...COMMON_STYLE_SECTIONS
|
|
@@ -37309,7 +37994,6 @@ const FIELD_WIDGETS = [
|
|
|
37309
37994
|
dataBinding: { shape: 'scalar', targetPath: 'defaultValue' },
|
|
37310
37995
|
properties: [
|
|
37311
37996
|
{ label: 'Basic', fields: COMMON_BASIC_FIELDS },
|
|
37312
|
-
{ label: 'Icons', fields: COMMON_ICON_FIELDS },
|
|
37313
37997
|
TIME_VALIDATION_SECTION,
|
|
37314
37998
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37315
37999
|
...COMMON_STYLE_SECTIONS
|
|
@@ -37336,7 +38020,6 @@ const FIELD_WIDGETS = [
|
|
|
37336
38020
|
dataBinding: { shape: 'scalar', targetPath: 'defaultValue' },
|
|
37337
38021
|
properties: [
|
|
37338
38022
|
{ label: 'Basic', fields: COMMON_BASIC_FIELDS },
|
|
37339
|
-
{ label: 'Icons', fields: COMMON_ICON_FIELDS },
|
|
37340
38023
|
DATETIME_VALIDATION_SECTION,
|
|
37341
38024
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37342
38025
|
...COMMON_STYLE_SECTIONS
|
|
@@ -37366,7 +38049,6 @@ const FIELD_WIDGETS = [
|
|
|
37366
38049
|
dataBinding: { shape: 'scalar', targetPath: 'defaultValue' },
|
|
37367
38050
|
properties: [
|
|
37368
38051
|
{ label: 'Basic', fields: COMMON_BASIC_FIELDS },
|
|
37369
|
-
{ label: 'Icons', fields: COMMON_ICON_FIELDS },
|
|
37370
38052
|
DATE_VALIDATION_SECTION,
|
|
37371
38053
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37372
38054
|
...COMMON_STYLE_SECTIONS
|
|
@@ -37396,7 +38078,6 @@ const FIELD_WIDGETS = [
|
|
|
37396
38078
|
dataBinding: { shape: 'scalar', targetPath: 'defaultValue' },
|
|
37397
38079
|
properties: [
|
|
37398
38080
|
{ label: 'Basic', fields: COMMON_BASIC_FIELDS },
|
|
37399
|
-
{ label: 'Icons', fields: COMMON_ICON_FIELDS },
|
|
37400
38081
|
DATE_VALIDATION_SECTION,
|
|
37401
38082
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37402
38083
|
...COMMON_STYLE_SECTIONS
|
|
@@ -37583,7 +38264,6 @@ const FIELD_WIDGETS = [
|
|
|
37583
38264
|
dataBinding: { shape: 'scalar', targetPath: 'defaultValue' },
|
|
37584
38265
|
properties: [
|
|
37585
38266
|
{ label: 'Basic', fields: COMMON_BASIC_FIELDS },
|
|
37586
|
-
{ label: 'Icons', fields: COMMON_ICON_FIELDS },
|
|
37587
38267
|
NUMBER_VALIDATION_SECTION,
|
|
37588
38268
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37589
38269
|
...COMMON_STYLE_SECTIONS
|
|
@@ -37663,44 +38343,6 @@ const FIELD_WIDGETS = [
|
|
|
37663
38343
|
dataConsumer: 'none',
|
|
37664
38344
|
properties: BUTTON_PROPERTIES
|
|
37665
38345
|
}),
|
|
37666
|
-
defineWidget(pluginId$2, {
|
|
37667
|
-
kind: 'button',
|
|
37668
|
-
flavor: 'form',
|
|
37669
|
-
type: 'submit-button',
|
|
37670
|
-
icon: 'send',
|
|
37671
|
-
label: 'Submit Button',
|
|
37672
|
-
createConfig: () => ({
|
|
37673
|
-
id: generateId$2(),
|
|
37674
|
-
name: 'submit_' + Date.now(),
|
|
37675
|
-
type: 'submit-button',
|
|
37676
|
-
label: 'Submit',
|
|
37677
|
-
variant: 'primary',
|
|
37678
|
-
buttonType: 'submit',
|
|
37679
|
-
style: { width: 'auto' }
|
|
37680
|
-
}),
|
|
37681
|
-
renderer: ButtonWidgetComponent,
|
|
37682
|
-
dataConsumer: 'none',
|
|
37683
|
-
properties: BUTTON_PROPERTIES
|
|
37684
|
-
}),
|
|
37685
|
-
defineWidget(pluginId$2, {
|
|
37686
|
-
kind: 'button',
|
|
37687
|
-
flavor: 'form',
|
|
37688
|
-
type: 'reset-button',
|
|
37689
|
-
icon: 'rotate-ccw',
|
|
37690
|
-
label: 'Reset Button',
|
|
37691
|
-
createConfig: () => ({
|
|
37692
|
-
id: generateId$2(),
|
|
37693
|
-
name: 'reset_' + Date.now(),
|
|
37694
|
-
type: 'reset-button',
|
|
37695
|
-
label: 'Reset',
|
|
37696
|
-
variant: 'secondary',
|
|
37697
|
-
buttonType: 'reset',
|
|
37698
|
-
style: { width: 'auto' }
|
|
37699
|
-
}),
|
|
37700
|
-
renderer: ButtonWidgetComponent,
|
|
37701
|
-
dataConsumer: 'none',
|
|
37702
|
-
properties: BUTTON_PROPERTIES
|
|
37703
|
-
}),
|
|
37704
38346
|
defineWidget(pluginId$2, {
|
|
37705
38347
|
kind: 'button',
|
|
37706
38348
|
flavor: 'form',
|
|
@@ -37758,7 +38400,6 @@ const FIELD_WIDGETS = [
|
|
|
37758
38400
|
},
|
|
37759
38401
|
// Options are now handled in Data Tab
|
|
37760
38402
|
SELECT_VALIDATION_SECTION,
|
|
37761
|
-
COMMON_CONDITIONAL_SECTION,
|
|
37762
38403
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37763
38404
|
...COMMON_STYLE_SECTIONS
|
|
37764
38405
|
]
|
|
@@ -37788,7 +38429,6 @@ const FIELD_WIDGETS = [
|
|
|
37788
38429
|
{ label: 'Basic', fields: COMMON_BASIC_FIELDS },
|
|
37789
38430
|
// Options handled in Data Tab
|
|
37790
38431
|
SELECT_VALIDATION_SECTION,
|
|
37791
|
-
COMMON_CONDITIONAL_SECTION,
|
|
37792
38432
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37793
38433
|
...COMMON_STYLE_SECTIONS
|
|
37794
38434
|
]
|
|
@@ -37818,7 +38458,6 @@ const FIELD_WIDGETS = [
|
|
|
37818
38458
|
{ label: 'Basic', fields: COMMON_BASIC_FIELDS },
|
|
37819
38459
|
// Options handled in Data Tab
|
|
37820
38460
|
SELECT_VALIDATION_SECTION,
|
|
37821
|
-
COMMON_CONDITIONAL_SECTION,
|
|
37822
38461
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37823
38462
|
...COMMON_STYLE_SECTIONS
|
|
37824
38463
|
]
|
|
@@ -37843,14 +38482,7 @@ const FIELD_WIDGETS = [
|
|
|
37843
38482
|
properties: [
|
|
37844
38483
|
{ label: 'Basic', fields: COMMON_BASIC_FIELDS },
|
|
37845
38484
|
// Boolean specific validation or standard
|
|
37846
|
-
|
|
37847
|
-
label: 'Validation',
|
|
37848
|
-
fields: [
|
|
37849
|
-
BASE_REQUIRED_FIELD,
|
|
37850
|
-
{ key: 'validators', type: 'validators-editor', label: 'Advanced Rules' }
|
|
37851
|
-
]
|
|
37852
|
-
},
|
|
37853
|
-
COMMON_CONDITIONAL_SECTION,
|
|
38485
|
+
createValidationSection(BASE_REQUIRED_FIELD),
|
|
37854
38486
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37855
38487
|
...COMMON_STYLE_SECTIONS
|
|
37856
38488
|
]
|
|
@@ -37881,7 +38513,6 @@ const FIELD_WIDGETS = [
|
|
|
37881
38513
|
properties: [
|
|
37882
38514
|
{ label: 'Basic', fields: COMMON_BASIC_FIELDS },
|
|
37883
38515
|
SELECT_VALIDATION_SECTION,
|
|
37884
|
-
COMMON_CONDITIONAL_SECTION,
|
|
37885
38516
|
{ label: 'Appearance', fields: COMMON_APPEARANCE_FIELDS },
|
|
37886
38517
|
...COMMON_STYLE_SECTIONS
|
|
37887
38518
|
]
|
|
@@ -37945,7 +38576,6 @@ const FIELD_WIDGETS = [
|
|
|
37945
38576
|
]
|
|
37946
38577
|
},
|
|
37947
38578
|
...COMMON_STYLE_SECTIONS,
|
|
37948
|
-
COMMON_CONDITIONAL_SECTION
|
|
37949
38579
|
]
|
|
37950
38580
|
})
|
|
37951
38581
|
];
|
|
@@ -40147,6 +40777,162 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
40147
40777
|
args: [DOCUMENT]
|
|
40148
40778
|
}] }] });
|
|
40149
40779
|
|
|
40780
|
+
class UiTabComponent {
|
|
40781
|
+
label = '';
|
|
40782
|
+
name = ''; // Unique key for controlled mode
|
|
40783
|
+
disabled = false;
|
|
40784
|
+
badge;
|
|
40785
|
+
badgeTone = 'neutral';
|
|
40786
|
+
template;
|
|
40787
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: UiTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
40788
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: UiTabComponent, isStandalone: true, selector: "ui-tab", inputs: { label: "label", name: "name", disabled: "disabled", badge: "badge", badgeTone: "badgeTone" }, viewQueries: [{ propertyName: "template", first: true, predicate: ["tpl"], descendants: true, static: true }], ngImport: i0, template: `<ng-template #tpl><ng-content></ng-content></ng-template>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
40789
|
+
}
|
|
40790
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: UiTabComponent, decorators: [{
|
|
40791
|
+
type: Component,
|
|
40792
|
+
args: [{
|
|
40793
|
+
selector: 'ui-tab',
|
|
40794
|
+
standalone: true,
|
|
40795
|
+
imports: [CommonModule],
|
|
40796
|
+
template: `<ng-template #tpl><ng-content></ng-content></ng-template>`
|
|
40797
|
+
}]
|
|
40798
|
+
}], propDecorators: { label: [{
|
|
40799
|
+
type: Input
|
|
40800
|
+
}], name: [{
|
|
40801
|
+
type: Input
|
|
40802
|
+
}], disabled: [{
|
|
40803
|
+
type: Input
|
|
40804
|
+
}], badge: [{
|
|
40805
|
+
type: Input
|
|
40806
|
+
}], badgeTone: [{
|
|
40807
|
+
type: Input
|
|
40808
|
+
}], template: [{
|
|
40809
|
+
type: ViewChild,
|
|
40810
|
+
args: ['tpl', { static: true }]
|
|
40811
|
+
}] } });
|
|
40812
|
+
class UiTabsComponent {
|
|
40813
|
+
tabQuery;
|
|
40814
|
+
activeTab;
|
|
40815
|
+
activeTabChange = new EventEmitter();
|
|
40816
|
+
// Internal state if uncontrolled
|
|
40817
|
+
_internalIndex = 0;
|
|
40818
|
+
tabs = [];
|
|
40819
|
+
ngAfterContentInit() {
|
|
40820
|
+
this.tabs = this.tabQuery.toArray();
|
|
40821
|
+
}
|
|
40822
|
+
isActive(tab) {
|
|
40823
|
+
if (this.activeTab !== undefined) {
|
|
40824
|
+
return this.activeTab === (tab.name || tab.label);
|
|
40825
|
+
}
|
|
40826
|
+
return this.tabs.indexOf(tab) === this._internalIndex;
|
|
40827
|
+
}
|
|
40828
|
+
activate(tab) {
|
|
40829
|
+
if (tab.disabled)
|
|
40830
|
+
return;
|
|
40831
|
+
const key = tab.name || tab.label;
|
|
40832
|
+
if (this.activeTab !== undefined) {
|
|
40833
|
+
this.activeTabChange.emit(key);
|
|
40834
|
+
}
|
|
40835
|
+
else {
|
|
40836
|
+
this._internalIndex = this.tabs.indexOf(tab);
|
|
40837
|
+
}
|
|
40838
|
+
}
|
|
40839
|
+
get activeTemplate() {
|
|
40840
|
+
if (this.tabs.length === 0)
|
|
40841
|
+
return null;
|
|
40842
|
+
let activeTab;
|
|
40843
|
+
if (this.activeTab !== undefined) {
|
|
40844
|
+
activeTab = this.tabs.find(t => (t.name || t.label) === this.activeTab);
|
|
40845
|
+
}
|
|
40846
|
+
else {
|
|
40847
|
+
activeTab = this.tabs[this._internalIndex];
|
|
40848
|
+
}
|
|
40849
|
+
return activeTab?.template || null;
|
|
40850
|
+
}
|
|
40851
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: UiTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
40852
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: UiTabsComponent, isStandalone: true, selector: "ui-tabs", inputs: { activeTab: "activeTab" }, outputs: { activeTabChange: "activeTabChange" }, queries: [{ propertyName: "tabQuery", predicate: UiTabComponent }], ngImport: i0, template: `
|
|
40853
|
+
<div class="flex flex-col min-h-0 h-full">
|
|
40854
|
+
<div class="flex items-center gap-1 px-3 border-b border-slate-200 bg-white shrink-0">
|
|
40855
|
+
<button
|
|
40856
|
+
*ngFor="let tab of tabs; let i = index"
|
|
40857
|
+
(click)="activate(tab)"
|
|
40858
|
+
class="relative h-10 px-3 text-xs font-semibold transition-colors rounded-t-md border-b-2"
|
|
40859
|
+
[class.text-accent-600]="isActive(tab)"
|
|
40860
|
+
[class.border-accent-600]="isActive(tab)"
|
|
40861
|
+
[class.text-ink-500]="!isActive(tab)"
|
|
40862
|
+
[class.border-transparent]="!isActive(tab)"
|
|
40863
|
+
[class.hover:text-ink-700]="!isActive(tab)"
|
|
40864
|
+
[disabled]="tab.disabled"
|
|
40865
|
+
>
|
|
40866
|
+
<span class="flex items-center gap-2">
|
|
40867
|
+
{{ tab.label }}
|
|
40868
|
+
<span *ngIf="tab.badge" class="text-[10px] px-1.5 py-0.5 rounded-full border"
|
|
40869
|
+
[class.bg-accent-50]="tab.badgeTone === 'accent'"
|
|
40870
|
+
[class.border-accent-200]="tab.badgeTone === 'accent'"
|
|
40871
|
+
[class.text-accent-700]="tab.badgeTone === 'accent'"
|
|
40872
|
+
[class.bg-slate-100]="tab.badgeTone === 'neutral'"
|
|
40873
|
+
[class.border-slate-200]="tab.badgeTone === 'neutral'"
|
|
40874
|
+
[class.text-slate-600]="tab.badgeTone === 'neutral'">
|
|
40875
|
+
{{ tab.badge }}
|
|
40876
|
+
</span>
|
|
40877
|
+
</span>
|
|
40878
|
+
</button>
|
|
40879
|
+
</div>
|
|
40880
|
+
|
|
40881
|
+
<div class="flex-1 min-h-0 overflow-hidden bg-slate-50/30">
|
|
40882
|
+
<ng-container *ngIf="activeTemplate">
|
|
40883
|
+
<ng-container *ngTemplateOutlet="activeTemplate"></ng-container>
|
|
40884
|
+
</ng-container>
|
|
40885
|
+
</div>
|
|
40886
|
+
</div>
|
|
40887
|
+
`, isInline: true, styles: [":host{display:block;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
40888
|
+
}
|
|
40889
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: UiTabsComponent, decorators: [{
|
|
40890
|
+
type: Component,
|
|
40891
|
+
args: [{ selector: 'ui-tabs', standalone: true, imports: [CommonModule], template: `
|
|
40892
|
+
<div class="flex flex-col min-h-0 h-full">
|
|
40893
|
+
<div class="flex items-center gap-1 px-3 border-b border-slate-200 bg-white shrink-0">
|
|
40894
|
+
<button
|
|
40895
|
+
*ngFor="let tab of tabs; let i = index"
|
|
40896
|
+
(click)="activate(tab)"
|
|
40897
|
+
class="relative h-10 px-3 text-xs font-semibold transition-colors rounded-t-md border-b-2"
|
|
40898
|
+
[class.text-accent-600]="isActive(tab)"
|
|
40899
|
+
[class.border-accent-600]="isActive(tab)"
|
|
40900
|
+
[class.text-ink-500]="!isActive(tab)"
|
|
40901
|
+
[class.border-transparent]="!isActive(tab)"
|
|
40902
|
+
[class.hover:text-ink-700]="!isActive(tab)"
|
|
40903
|
+
[disabled]="tab.disabled"
|
|
40904
|
+
>
|
|
40905
|
+
<span class="flex items-center gap-2">
|
|
40906
|
+
{{ tab.label }}
|
|
40907
|
+
<span *ngIf="tab.badge" class="text-[10px] px-1.5 py-0.5 rounded-full border"
|
|
40908
|
+
[class.bg-accent-50]="tab.badgeTone === 'accent'"
|
|
40909
|
+
[class.border-accent-200]="tab.badgeTone === 'accent'"
|
|
40910
|
+
[class.text-accent-700]="tab.badgeTone === 'accent'"
|
|
40911
|
+
[class.bg-slate-100]="tab.badgeTone === 'neutral'"
|
|
40912
|
+
[class.border-slate-200]="tab.badgeTone === 'neutral'"
|
|
40913
|
+
[class.text-slate-600]="tab.badgeTone === 'neutral'">
|
|
40914
|
+
{{ tab.badge }}
|
|
40915
|
+
</span>
|
|
40916
|
+
</span>
|
|
40917
|
+
</button>
|
|
40918
|
+
</div>
|
|
40919
|
+
|
|
40920
|
+
<div class="flex-1 min-h-0 overflow-hidden bg-slate-50/30">
|
|
40921
|
+
<ng-container *ngIf="activeTemplate">
|
|
40922
|
+
<ng-container *ngTemplateOutlet="activeTemplate"></ng-container>
|
|
40923
|
+
</ng-container>
|
|
40924
|
+
</div>
|
|
40925
|
+
</div>
|
|
40926
|
+
`, styles: [":host{display:block;height:100%}\n"] }]
|
|
40927
|
+
}], propDecorators: { tabQuery: [{
|
|
40928
|
+
type: ContentChildren,
|
|
40929
|
+
args: [UiTabComponent]
|
|
40930
|
+
}], activeTab: [{
|
|
40931
|
+
type: Input
|
|
40932
|
+
}], activeTabChange: [{
|
|
40933
|
+
type: Output
|
|
40934
|
+
}] } });
|
|
40935
|
+
|
|
40150
40936
|
class AiToolRegistryService {
|
|
40151
40937
|
state;
|
|
40152
40938
|
widgetDefs;
|