@wire-dsl/engine 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1370 -207
- package/dist/index.d.cts +211 -14
- package/dist/index.d.ts +211 -14
- package/dist/index.js +1368 -207
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -24,11 +24,13 @@ __export(index_exports, {
|
|
|
24
24
|
DEVICE_PRESETS: () => DEVICE_PRESETS,
|
|
25
25
|
IRGenerator: () => IRGenerator,
|
|
26
26
|
LayoutEngine: () => LayoutEngine,
|
|
27
|
+
SELF_TARGET: () => SELF_TARGET,
|
|
27
28
|
SVGRenderer: () => SVGRenderer,
|
|
28
29
|
SkeletonSVGRenderer: () => SkeletonSVGRenderer,
|
|
29
30
|
SketchSVGRenderer: () => SketchSVGRenderer,
|
|
30
31
|
SourceMapBuilder: () => SourceMapBuilder,
|
|
31
32
|
SourceMapResolver: () => SourceMapResolver,
|
|
33
|
+
applyStateChange: () => applyStateChange,
|
|
32
34
|
buildSVG: () => buildSVG,
|
|
33
35
|
calculateLayout: () => calculateLayout,
|
|
34
36
|
createSVGElement: () => createSVGElement,
|
|
@@ -142,6 +144,21 @@ var SourceMapBuilder = class {
|
|
|
142
144
|
this.counters.set("cell", idx + 1);
|
|
143
145
|
return `cell-${idx}`;
|
|
144
146
|
}
|
|
147
|
+
case "tab": {
|
|
148
|
+
const idx = this.counters.get("tab") || 0;
|
|
149
|
+
this.counters.set("tab", idx + 1);
|
|
150
|
+
return `tab-${idx}`;
|
|
151
|
+
}
|
|
152
|
+
case "modal-body": {
|
|
153
|
+
const idx = this.counters.get("modal-body") || 0;
|
|
154
|
+
this.counters.set("modal-body", idx + 1);
|
|
155
|
+
return `modal-body-${idx}`;
|
|
156
|
+
}
|
|
157
|
+
case "modal-footer": {
|
|
158
|
+
const idx = this.counters.get("modal-footer") || 0;
|
|
159
|
+
this.counters.set("modal-footer", idx + 1);
|
|
160
|
+
return `modal-footer-${idx}`;
|
|
161
|
+
}
|
|
145
162
|
case "component-definition":
|
|
146
163
|
return `define-${metadata?.name || "unknown"}`;
|
|
147
164
|
case "layout-definition":
|
|
@@ -204,6 +221,49 @@ var SourceMapBuilder = class {
|
|
|
204
221
|
entry.properties[propertyName] = propertySourceMap;
|
|
205
222
|
return propertySourceMap;
|
|
206
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Add an event handler to an existing node in the SourceMap
|
|
226
|
+
* Events have action expressions as values (e.g., "show(modal) & navigate(Home)")
|
|
227
|
+
*
|
|
228
|
+
* @param nodeId - ID of the node that owns this event
|
|
229
|
+
* @param eventName - Name of the event (e.g., "onClick", "onClose")
|
|
230
|
+
* @param tokens - Captured tokens: name token for event key, CST node for actionChain
|
|
231
|
+
* @returns The EventSourceMap entry created
|
|
232
|
+
*/
|
|
233
|
+
addEvent(nodeId, eventName, tokens) {
|
|
234
|
+
const entry = this.entries.find((e) => e.nodeId === nodeId);
|
|
235
|
+
if (!entry) {
|
|
236
|
+
throw new Error(`Cannot add event to non-existent node: ${nodeId}`);
|
|
237
|
+
}
|
|
238
|
+
if (!entry.events) {
|
|
239
|
+
entry.events = {};
|
|
240
|
+
}
|
|
241
|
+
const nameRange = {
|
|
242
|
+
start: this.getTokenStart(tokens.name),
|
|
243
|
+
end: this.getTokenEnd(tokens.name)
|
|
244
|
+
};
|
|
245
|
+
const valueRange = {
|
|
246
|
+
start: this.getTokenStart(tokens.value),
|
|
247
|
+
end: this.getTokenEnd(tokens.value)
|
|
248
|
+
};
|
|
249
|
+
const fullRange = {
|
|
250
|
+
start: nameRange.start,
|
|
251
|
+
end: valueRange.end
|
|
252
|
+
};
|
|
253
|
+
let rawValue = "";
|
|
254
|
+
if (this.sourceCode && valueRange.start.offset !== void 0 && valueRange.end.offset !== void 0) {
|
|
255
|
+
rawValue = this.sourceCode.slice(valueRange.start.offset, valueRange.end.offset + 1);
|
|
256
|
+
}
|
|
257
|
+
const eventSourceMap = {
|
|
258
|
+
name: eventName,
|
|
259
|
+
value: rawValue,
|
|
260
|
+
range: fullRange,
|
|
261
|
+
nameRange,
|
|
262
|
+
valueRange
|
|
263
|
+
};
|
|
264
|
+
entry.events[eventName] = eventSourceMap;
|
|
265
|
+
return eventSourceMap;
|
|
266
|
+
}
|
|
207
267
|
/**
|
|
208
268
|
* Push a parent onto the stack (when entering a container node)
|
|
209
269
|
*/
|
|
@@ -239,6 +299,9 @@ var SourceMapBuilder = class {
|
|
|
239
299
|
"screen",
|
|
240
300
|
"layout",
|
|
241
301
|
"cell",
|
|
302
|
+
"tab",
|
|
303
|
+
"modal-body",
|
|
304
|
+
"modal-footer",
|
|
242
305
|
"component-definition",
|
|
243
306
|
"layout-definition"
|
|
244
307
|
];
|
|
@@ -483,12 +546,26 @@ var Style = (0, import_chevrotain.createToken)({ name: "Style", pattern: /style\
|
|
|
483
546
|
var Mocks = (0, import_chevrotain.createToken)({ name: "Mocks", pattern: /mocks\b/ });
|
|
484
547
|
var Colors = (0, import_chevrotain.createToken)({ name: "Colors", pattern: /colors(?=\s*\{)/ });
|
|
485
548
|
var Cell = (0, import_chevrotain.createToken)({ name: "Cell", pattern: /cell\b/ });
|
|
549
|
+
var Tab = (0, import_chevrotain.createToken)({ name: "Tab", pattern: /tab\b/ });
|
|
550
|
+
var Body = (0, import_chevrotain.createToken)({ name: "Body", pattern: /body\b/ });
|
|
551
|
+
var Footer = (0, import_chevrotain.createToken)({ name: "Footer", pattern: /footer\b/ });
|
|
552
|
+
var Navigate = (0, import_chevrotain.createToken)({ name: "Navigate", pattern: /navigate\b/ });
|
|
553
|
+
var Show = (0, import_chevrotain.createToken)({ name: "Show", pattern: /show\b/ });
|
|
554
|
+
var Hide = (0, import_chevrotain.createToken)({ name: "Hide", pattern: /hide\b/ });
|
|
555
|
+
var ToggleAction = (0, import_chevrotain.createToken)({ name: "ToggleAction", pattern: /toggle\b/ });
|
|
556
|
+
var EnableAction = (0, import_chevrotain.createToken)({ name: "EnableAction", pattern: /enable\b/ });
|
|
557
|
+
var DisableAction = (0, import_chevrotain.createToken)({ name: "DisableAction", pattern: /disable\b/ });
|
|
558
|
+
var SetTab = (0, import_chevrotain.createToken)({ name: "SetTab", pattern: /setTab\b/ });
|
|
559
|
+
var Self = (0, import_chevrotain.createToken)({ name: "Self", pattern: /self\b/ });
|
|
486
560
|
var LCurly = (0, import_chevrotain.createToken)({ name: "LCurly", pattern: /{/ });
|
|
487
561
|
var RCurly = (0, import_chevrotain.createToken)({ name: "RCurly", pattern: /}/ });
|
|
488
562
|
var LParen = (0, import_chevrotain.createToken)({ name: "LParen", pattern: /\(/ });
|
|
489
563
|
var RParen = (0, import_chevrotain.createToken)({ name: "RParen", pattern: /\)/ });
|
|
490
564
|
var Colon = (0, import_chevrotain.createToken)({ name: "Colon", pattern: /:/ });
|
|
491
565
|
var Comma = (0, import_chevrotain.createToken)({ name: "Comma", pattern: /,/ });
|
|
566
|
+
var Ampersand = (0, import_chevrotain.createToken)({ name: "Ampersand", pattern: /&/ });
|
|
567
|
+
var LBracket = (0, import_chevrotain.createToken)({ name: "LBracket", pattern: /\[/ });
|
|
568
|
+
var RBracket = (0, import_chevrotain.createToken)({ name: "RBracket", pattern: /\]/ });
|
|
492
569
|
var StringLiteral = (0, import_chevrotain.createToken)({
|
|
493
570
|
name: "StringLiteral",
|
|
494
571
|
pattern: /"(?:[^"\\]|\\.)*"/
|
|
@@ -537,6 +614,18 @@ var allTokens = [
|
|
|
537
614
|
Mocks,
|
|
538
615
|
Colors,
|
|
539
616
|
Cell,
|
|
617
|
+
Tab,
|
|
618
|
+
Body,
|
|
619
|
+
Footer,
|
|
620
|
+
// Event action keywords (must come before Identifier)
|
|
621
|
+
Navigate,
|
|
622
|
+
Show,
|
|
623
|
+
Hide,
|
|
624
|
+
ToggleAction,
|
|
625
|
+
EnableAction,
|
|
626
|
+
DisableAction,
|
|
627
|
+
SetTab,
|
|
628
|
+
Self,
|
|
540
629
|
// Punctuation
|
|
541
630
|
LCurly,
|
|
542
631
|
RCurly,
|
|
@@ -544,6 +633,9 @@ var allTokens = [
|
|
|
544
633
|
RParen,
|
|
545
634
|
Colon,
|
|
546
635
|
Comma,
|
|
636
|
+
Ampersand,
|
|
637
|
+
LBracket,
|
|
638
|
+
RBracket,
|
|
547
639
|
// Literals
|
|
548
640
|
StringLiteral,
|
|
549
641
|
NumberLiteral,
|
|
@@ -651,6 +743,90 @@ var WireDSLParser = class extends import_chevrotain.CstParser {
|
|
|
651
743
|
this.SUBRULE(this.layout);
|
|
652
744
|
this.CONSUME(RCurly);
|
|
653
745
|
});
|
|
746
|
+
// singleAction: navigate(Screen) | show(id|self) | hide(id|self) | toggle(id|self) | setTab(tabsId, index)
|
|
747
|
+
this.singleAction = this.RULE("singleAction", () => {
|
|
748
|
+
this.OR([
|
|
749
|
+
{
|
|
750
|
+
ALT: () => {
|
|
751
|
+
this.CONSUME(Navigate, { LABEL: "navigate" });
|
|
752
|
+
this.CONSUME(LParen);
|
|
753
|
+
this.CONSUME(Identifier, { LABEL: "targetScreen" });
|
|
754
|
+
this.CONSUME(RParen);
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
ALT: () => {
|
|
759
|
+
this.OR2([
|
|
760
|
+
{ ALT: () => this.CONSUME(Show, { LABEL: "sht" }) },
|
|
761
|
+
{ ALT: () => this.CONSUME(Hide, { LABEL: "sht" }) },
|
|
762
|
+
{ ALT: () => this.CONSUME(ToggleAction, { LABEL: "sht" }) },
|
|
763
|
+
{ ALT: () => this.CONSUME(EnableAction, { LABEL: "sht" }) },
|
|
764
|
+
{ ALT: () => this.CONSUME(DisableAction, { LABEL: "sht" }) }
|
|
765
|
+
]);
|
|
766
|
+
this.CONSUME2(LParen);
|
|
767
|
+
this.OR3([
|
|
768
|
+
{ ALT: () => this.CONSUME(Self, { LABEL: "targetId" }) },
|
|
769
|
+
{ ALT: () => this.CONSUME2(Identifier, { LABEL: "targetId" }) }
|
|
770
|
+
]);
|
|
771
|
+
this.CONSUME2(RParen);
|
|
772
|
+
}
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
ALT: () => {
|
|
776
|
+
this.CONSUME(SetTab, { LABEL: "setTab" });
|
|
777
|
+
this.CONSUME3(LParen);
|
|
778
|
+
this.CONSUME3(Identifier, { LABEL: "tabsId" });
|
|
779
|
+
this.CONSUME(Comma);
|
|
780
|
+
this.CONSUME(NumberLiteral, { LABEL: "tabIndex" });
|
|
781
|
+
this.CONSUME3(RParen);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
]);
|
|
785
|
+
});
|
|
786
|
+
// actionChain: singleAction (& singleAction)*
|
|
787
|
+
this.actionChain = this.RULE("actionChain", () => {
|
|
788
|
+
this.SUBRULE(this.singleAction);
|
|
789
|
+
this.MANY(() => {
|
|
790
|
+
this.CONSUME(Ampersand);
|
|
791
|
+
this.SUBRULE2(this.singleAction);
|
|
792
|
+
});
|
|
793
|
+
});
|
|
794
|
+
// tab { ... } — children block inside layout tabs
|
|
795
|
+
this.tab = this.RULE("tab", () => {
|
|
796
|
+
this.CONSUME(Tab);
|
|
797
|
+
this.CONSUME(LCurly);
|
|
798
|
+
this.MANY(() => {
|
|
799
|
+
this.OR([
|
|
800
|
+
{ ALT: () => this.SUBRULE(this.component) },
|
|
801
|
+
{ ALT: () => this.SUBRULE(this.layout) }
|
|
802
|
+
]);
|
|
803
|
+
});
|
|
804
|
+
this.CONSUME(RCurly);
|
|
805
|
+
});
|
|
806
|
+
// body { ... } — content section inside layout modal
|
|
807
|
+
this.body = this.RULE("body", () => {
|
|
808
|
+
this.CONSUME(Body);
|
|
809
|
+
this.CONSUME(LCurly);
|
|
810
|
+
this.MANY(() => {
|
|
811
|
+
this.OR([
|
|
812
|
+
{ ALT: () => this.SUBRULE(this.component) },
|
|
813
|
+
{ ALT: () => this.SUBRULE(this.layout) }
|
|
814
|
+
]);
|
|
815
|
+
});
|
|
816
|
+
this.CONSUME(RCurly);
|
|
817
|
+
});
|
|
818
|
+
// footer { ... } — footer section inside layout modal
|
|
819
|
+
this.footer = this.RULE("footer", () => {
|
|
820
|
+
this.CONSUME(Footer);
|
|
821
|
+
this.CONSUME(LCurly);
|
|
822
|
+
this.MANY(() => {
|
|
823
|
+
this.OR([
|
|
824
|
+
{ ALT: () => this.SUBRULE(this.component) },
|
|
825
|
+
{ ALT: () => this.SUBRULE(this.layout) }
|
|
826
|
+
]);
|
|
827
|
+
});
|
|
828
|
+
this.CONSUME(RCurly);
|
|
829
|
+
});
|
|
654
830
|
// layout stack(...) { ... }
|
|
655
831
|
this.layout = this.RULE("layout", () => {
|
|
656
832
|
this.CONSUME(Layout);
|
|
@@ -663,7 +839,10 @@ var WireDSLParser = class extends import_chevrotain.CstParser {
|
|
|
663
839
|
this.OR([
|
|
664
840
|
{ ALT: () => this.SUBRULE(this.component) },
|
|
665
841
|
{ ALT: () => this.SUBRULE2(this.layout) },
|
|
666
|
-
{ ALT: () => this.SUBRULE(this.cell) }
|
|
842
|
+
{ ALT: () => this.SUBRULE(this.cell) },
|
|
843
|
+
{ ALT: () => this.SUBRULE(this.tab) },
|
|
844
|
+
{ ALT: () => this.SUBRULE(this.body) },
|
|
845
|
+
{ ALT: () => this.SUBRULE(this.footer) }
|
|
667
846
|
]);
|
|
668
847
|
});
|
|
669
848
|
this.CONSUME(RCurly);
|
|
@@ -691,16 +870,27 @@ var WireDSLParser = class extends import_chevrotain.CstParser {
|
|
|
691
870
|
this.SUBRULE(this.property);
|
|
692
871
|
});
|
|
693
872
|
});
|
|
694
|
-
// property: key: value
|
|
873
|
+
// property: key: value (value can be string, number, identifier, or action chain)
|
|
695
874
|
this.property = this.RULE("property", () => {
|
|
696
875
|
this.CONSUME(Identifier, { LABEL: "propKey" });
|
|
697
876
|
this.CONSUME(Colon);
|
|
698
877
|
this.OR([
|
|
878
|
+
{ ALT: () => this.SUBRULE(this.actionChain) },
|
|
879
|
+
{ ALT: () => this.SUBRULE(this.arrayLiteral) },
|
|
699
880
|
{ ALT: () => this.CONSUME(StringLiteral, { LABEL: "propValue" }) },
|
|
700
881
|
{ ALT: () => this.CONSUME(NumberLiteral, { LABEL: "propValue" }) },
|
|
701
882
|
{ ALT: () => this.CONSUME2(Identifier, { LABEL: "propValue" }) }
|
|
702
883
|
]);
|
|
703
884
|
});
|
|
885
|
+
// ["item1", "item2", "item3"]
|
|
886
|
+
this.arrayLiteral = this.RULE("arrayLiteral", () => {
|
|
887
|
+
this.CONSUME(LBracket);
|
|
888
|
+
this.MANY_SEP({
|
|
889
|
+
SEP: Comma,
|
|
890
|
+
DEF: () => this.CONSUME(StringLiteral, { LABEL: "arrayItem" })
|
|
891
|
+
});
|
|
892
|
+
this.CONSUME(RBracket);
|
|
893
|
+
});
|
|
704
894
|
// (param1: value1, param2: value2)
|
|
705
895
|
this.paramList = this.RULE("paramList", () => {
|
|
706
896
|
this.CONSUME(LParen);
|
|
@@ -839,7 +1029,7 @@ var WireDSLVisitor = class extends BaseCstVisitor {
|
|
|
839
1029
|
};
|
|
840
1030
|
}
|
|
841
1031
|
screen(ctx) {
|
|
842
|
-
const params = ctx.paramList ? this.visit(ctx.paramList[0]) : {};
|
|
1032
|
+
const { params } = ctx.paramList ? this.visit(ctx.paramList[0]) : { params: {} };
|
|
843
1033
|
return {
|
|
844
1034
|
type: "screen",
|
|
845
1035
|
name: ctx.screenName[0].image,
|
|
@@ -850,10 +1040,12 @@ var WireDSLVisitor = class extends BaseCstVisitor {
|
|
|
850
1040
|
layout(ctx) {
|
|
851
1041
|
const layoutType = ctx.layoutType[0].image;
|
|
852
1042
|
const params = {};
|
|
1043
|
+
const events = [];
|
|
853
1044
|
const children = [];
|
|
854
1045
|
if (ctx.paramList) {
|
|
855
1046
|
const paramResult = this.visit(ctx.paramList);
|
|
856
|
-
Object.assign(params, paramResult);
|
|
1047
|
+
Object.assign(params, paramResult.params);
|
|
1048
|
+
events.push(...paramResult.events);
|
|
857
1049
|
}
|
|
858
1050
|
const childNodes = [];
|
|
859
1051
|
if (ctx.component) {
|
|
@@ -874,20 +1066,33 @@ var WireDSLVisitor = class extends BaseCstVisitor {
|
|
|
874
1066
|
childNodes.push({ type: "cell", node: cell, index: startToken.startOffset });
|
|
875
1067
|
});
|
|
876
1068
|
}
|
|
1069
|
+
if (ctx.tab) {
|
|
1070
|
+
ctx.tab.forEach((tab) => {
|
|
1071
|
+
const startToken = tab.children?.Tab?.[0];
|
|
1072
|
+
childNodes.push({ type: "tab", node: tab, index: startToken.startOffset });
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
if (ctx.body) {
|
|
1076
|
+
ctx.body.forEach((body) => {
|
|
1077
|
+
const startToken = body.children?.Body?.[0];
|
|
1078
|
+
childNodes.push({ type: "body", node: body, index: startToken.startOffset });
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
if (ctx.footer) {
|
|
1082
|
+
ctx.footer.forEach((footer) => {
|
|
1083
|
+
const startToken = footer.children?.Footer?.[0];
|
|
1084
|
+
childNodes.push({ type: "footer", node: footer, index: startToken.startOffset });
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
877
1087
|
childNodes.sort((a, b) => a.index - b.index);
|
|
878
1088
|
childNodes.forEach((item) => {
|
|
879
|
-
|
|
880
|
-
children.push(this.visit(item.node));
|
|
881
|
-
} else if (item.type === "layout") {
|
|
882
|
-
children.push(this.visit(item.node));
|
|
883
|
-
} else if (item.type === "cell") {
|
|
884
|
-
children.push(this.visit(item.node));
|
|
885
|
-
}
|
|
1089
|
+
children.push(this.visit(item.node));
|
|
886
1090
|
});
|
|
887
1091
|
return {
|
|
888
1092
|
type: "layout",
|
|
889
1093
|
layoutType,
|
|
890
1094
|
params,
|
|
1095
|
+
events,
|
|
891
1096
|
children
|
|
892
1097
|
};
|
|
893
1098
|
}
|
|
@@ -927,23 +1132,137 @@ var WireDSLVisitor = class extends BaseCstVisitor {
|
|
|
927
1132
|
children
|
|
928
1133
|
};
|
|
929
1134
|
}
|
|
1135
|
+
singleAction(ctx) {
|
|
1136
|
+
if (ctx.navigate) {
|
|
1137
|
+
return { type: "navigate", screen: ctx.targetScreen[0].image };
|
|
1138
|
+
}
|
|
1139
|
+
if (ctx.sht) {
|
|
1140
|
+
const tokenName = ctx.sht[0].tokenType.name;
|
|
1141
|
+
const typeMap = {
|
|
1142
|
+
Show: "show",
|
|
1143
|
+
Hide: "hide",
|
|
1144
|
+
ToggleAction: "toggle",
|
|
1145
|
+
EnableAction: "enable",
|
|
1146
|
+
DisableAction: "disable"
|
|
1147
|
+
};
|
|
1148
|
+
const type = typeMap[tokenName] ?? "show";
|
|
1149
|
+
const targetToken = ctx.targetId[0];
|
|
1150
|
+
const isSelf = targetToken.tokenType.name === "Self";
|
|
1151
|
+
const targetId = isSelf ? "_self" : targetToken.image;
|
|
1152
|
+
return { type, targetId };
|
|
1153
|
+
}
|
|
1154
|
+
if (ctx.setTab) {
|
|
1155
|
+
return {
|
|
1156
|
+
type: "setTab",
|
|
1157
|
+
tabsId: ctx.tabsId[0].image,
|
|
1158
|
+
index: Number(ctx.tabIndex[0].image)
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
throw new Error("Unknown action type in singleAction visitor");
|
|
1162
|
+
}
|
|
1163
|
+
actionChain(ctx) {
|
|
1164
|
+
const actions = [];
|
|
1165
|
+
if (ctx.singleAction) {
|
|
1166
|
+
ctx.singleAction.forEach((action) => {
|
|
1167
|
+
actions.push(this.visit(action));
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
return actions;
|
|
1171
|
+
}
|
|
1172
|
+
tab(ctx) {
|
|
1173
|
+
const children = [];
|
|
1174
|
+
const childNodes = [];
|
|
1175
|
+
if (ctx.component) {
|
|
1176
|
+
ctx.component.forEach((comp) => {
|
|
1177
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1178
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
if (ctx.layout) {
|
|
1182
|
+
ctx.layout.forEach((layout) => {
|
|
1183
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1184
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1188
|
+
childNodes.forEach((item) => {
|
|
1189
|
+
children.push(this.visit(item.node));
|
|
1190
|
+
});
|
|
1191
|
+
return { type: "tab", children };
|
|
1192
|
+
}
|
|
1193
|
+
body(ctx) {
|
|
1194
|
+
const children = [];
|
|
1195
|
+
const childNodes = [];
|
|
1196
|
+
if (ctx.component) {
|
|
1197
|
+
ctx.component.forEach((comp) => {
|
|
1198
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1199
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
if (ctx.layout) {
|
|
1203
|
+
ctx.layout.forEach((layout) => {
|
|
1204
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1205
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1209
|
+
childNodes.forEach((item) => {
|
|
1210
|
+
children.push(this.visit(item.node));
|
|
1211
|
+
});
|
|
1212
|
+
return { type: "modal-body", children };
|
|
1213
|
+
}
|
|
1214
|
+
footer(ctx) {
|
|
1215
|
+
const children = [];
|
|
1216
|
+
const childNodes = [];
|
|
1217
|
+
if (ctx.component) {
|
|
1218
|
+
ctx.component.forEach((comp) => {
|
|
1219
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1220
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
if (ctx.layout) {
|
|
1224
|
+
ctx.layout.forEach((layout) => {
|
|
1225
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1226
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1230
|
+
childNodes.forEach((item) => {
|
|
1231
|
+
children.push(this.visit(item.node));
|
|
1232
|
+
});
|
|
1233
|
+
return { type: "modal-footer", children };
|
|
1234
|
+
}
|
|
930
1235
|
component(ctx) {
|
|
931
1236
|
const componentType = ctx.componentType[0].image;
|
|
932
1237
|
const props = {};
|
|
1238
|
+
const events = [];
|
|
933
1239
|
if (ctx.property) {
|
|
934
1240
|
ctx.property.forEach((prop) => {
|
|
935
1241
|
const result = this.visit(prop);
|
|
936
|
-
|
|
1242
|
+
if (result.isEvent) {
|
|
1243
|
+
events.push({ event: result.key, actions: result.actions });
|
|
1244
|
+
} else {
|
|
1245
|
+
props[result.key] = result.value;
|
|
1246
|
+
}
|
|
937
1247
|
});
|
|
938
1248
|
}
|
|
939
1249
|
return {
|
|
940
1250
|
type: "component",
|
|
941
1251
|
componentType,
|
|
942
|
-
props
|
|
1252
|
+
props,
|
|
1253
|
+
events
|
|
943
1254
|
};
|
|
944
1255
|
}
|
|
945
1256
|
property(ctx) {
|
|
946
1257
|
const key = ctx.propKey[0].image;
|
|
1258
|
+
if (ctx.actionChain && ctx.actionChain.length > 0) {
|
|
1259
|
+
const actions = this.visit(ctx.actionChain[0]);
|
|
1260
|
+
return { key, isEvent: true, actions };
|
|
1261
|
+
}
|
|
1262
|
+
if (ctx.arrayLiteral && ctx.arrayLiteral.length > 0) {
|
|
1263
|
+
const items = this.visit(ctx.arrayLiteral[0]);
|
|
1264
|
+
return { key, value: items };
|
|
1265
|
+
}
|
|
947
1266
|
const rawValue = ctx.propValue[0].image;
|
|
948
1267
|
let value = rawValue;
|
|
949
1268
|
if (typeof rawValue === "string" && rawValue.startsWith('"')) {
|
|
@@ -953,15 +1272,27 @@ var WireDSLVisitor = class extends BaseCstVisitor {
|
|
|
953
1272
|
}
|
|
954
1273
|
return { key, value };
|
|
955
1274
|
}
|
|
1275
|
+
arrayLiteral(ctx) {
|
|
1276
|
+
if (!ctx.arrayItem) return [];
|
|
1277
|
+
return ctx.arrayItem.map((token) => {
|
|
1278
|
+
const raw = token.image;
|
|
1279
|
+
return raw.startsWith('"') ? raw.slice(1, -1) : raw;
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
956
1282
|
paramList(ctx) {
|
|
957
1283
|
const params = {};
|
|
1284
|
+
const events = [];
|
|
958
1285
|
if (ctx.property) {
|
|
959
1286
|
ctx.property.forEach((prop) => {
|
|
960
1287
|
const result = this.visit(prop);
|
|
961
|
-
|
|
1288
|
+
if (result.isEvent) {
|
|
1289
|
+
events.push({ event: result.key, actions: result.actions });
|
|
1290
|
+
} else {
|
|
1291
|
+
params[result.key] = result.value;
|
|
1292
|
+
}
|
|
962
1293
|
});
|
|
963
1294
|
}
|
|
964
|
-
return params;
|
|
1295
|
+
return { params, events };
|
|
965
1296
|
}
|
|
966
1297
|
};
|
|
967
1298
|
var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
@@ -1036,7 +1367,7 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1036
1367
|
return ast;
|
|
1037
1368
|
}
|
|
1038
1369
|
screen(ctx) {
|
|
1039
|
-
const params = ctx.paramList ? this.visit(ctx.paramList[0]) : {};
|
|
1370
|
+
const { params } = ctx.paramList ? this.visit(ctx.paramList[0]) : { params: {} };
|
|
1040
1371
|
const screenName = ctx.screenName[0].image;
|
|
1041
1372
|
const tokens = {
|
|
1042
1373
|
keyword: ctx.Screen[0],
|
|
@@ -1069,9 +1400,11 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1069
1400
|
layout(ctx) {
|
|
1070
1401
|
const layoutType = ctx.layoutType[0].image;
|
|
1071
1402
|
const params = {};
|
|
1403
|
+
const events = [];
|
|
1072
1404
|
if (ctx.paramList) {
|
|
1073
1405
|
const paramResult = this.visit(ctx.paramList);
|
|
1074
|
-
Object.assign(params, paramResult);
|
|
1406
|
+
Object.assign(params, paramResult.params);
|
|
1407
|
+
events.push(...paramResult.events);
|
|
1075
1408
|
}
|
|
1076
1409
|
const tokens = {
|
|
1077
1410
|
keyword: ctx.Layout[0],
|
|
@@ -1083,6 +1416,7 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1083
1416
|
type: "layout",
|
|
1084
1417
|
layoutType,
|
|
1085
1418
|
params,
|
|
1419
|
+
events,
|
|
1086
1420
|
children: []
|
|
1087
1421
|
// Will be filled after push
|
|
1088
1422
|
};
|
|
@@ -1096,13 +1430,21 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1096
1430
|
if (ctx.paramList && ctx.paramList[0]?.children?.property) {
|
|
1097
1431
|
ctx.paramList[0].children.property.forEach((propCtx) => {
|
|
1098
1432
|
const propResult = this.visit(propCtx);
|
|
1433
|
+
if (propResult.isEvent) {
|
|
1434
|
+
this.sourceMapBuilder.addEvent(nodeId, propResult.key, {
|
|
1435
|
+
name: propCtx.children.propKey[0],
|
|
1436
|
+
value: propCtx.children.actionChain[0]
|
|
1437
|
+
});
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
const valueToken = propCtx.children.propValue?.[0] ?? propCtx.children.arrayLiteral?.[0];
|
|
1099
1441
|
this.sourceMapBuilder.addProperty(
|
|
1100
1442
|
nodeId,
|
|
1101
1443
|
propResult.key,
|
|
1102
1444
|
propResult.value,
|
|
1103
1445
|
{
|
|
1104
1446
|
name: propCtx.children.propKey[0],
|
|
1105
|
-
value:
|
|
1447
|
+
value: valueToken
|
|
1106
1448
|
}
|
|
1107
1449
|
);
|
|
1108
1450
|
});
|
|
@@ -1128,6 +1470,24 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1128
1470
|
childNodes.push({ type: "cell", node: cell, index: startToken.startOffset });
|
|
1129
1471
|
});
|
|
1130
1472
|
}
|
|
1473
|
+
if (ctx.tab) {
|
|
1474
|
+
ctx.tab.forEach((tab) => {
|
|
1475
|
+
const startToken = tab.children?.Tab?.[0];
|
|
1476
|
+
childNodes.push({ type: "tab", node: tab, index: startToken.startOffset });
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
if (ctx.body) {
|
|
1480
|
+
ctx.body.forEach((body) => {
|
|
1481
|
+
const startToken = body.children?.Body?.[0];
|
|
1482
|
+
childNodes.push({ type: "body", node: body, index: startToken.startOffset });
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
if (ctx.footer) {
|
|
1486
|
+
ctx.footer.forEach((footer) => {
|
|
1487
|
+
const startToken = footer.children?.Footer?.[0];
|
|
1488
|
+
childNodes.push({ type: "footer", node: footer, index: startToken.startOffset });
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1131
1491
|
childNodes.sort((a, b) => a.index - b.index);
|
|
1132
1492
|
childNodes.forEach((item) => {
|
|
1133
1493
|
ast.children.push(this.visit(item.node));
|
|
@@ -1165,13 +1525,21 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1165
1525
|
if (ctx.property) {
|
|
1166
1526
|
ctx.property.forEach((propCtx) => {
|
|
1167
1527
|
const propResult = this.visit(propCtx);
|
|
1528
|
+
if (propResult.isEvent) {
|
|
1529
|
+
this.sourceMapBuilder.addEvent(nodeId, propResult.key, {
|
|
1530
|
+
name: propCtx.children.propKey[0],
|
|
1531
|
+
value: propCtx.children.actionChain[0]
|
|
1532
|
+
});
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
const valueToken = propCtx.children.propValue?.[0] ?? propCtx.children.arrayLiteral?.[0];
|
|
1168
1536
|
this.sourceMapBuilder.addProperty(
|
|
1169
1537
|
nodeId,
|
|
1170
1538
|
propResult.key,
|
|
1171
1539
|
propResult.value,
|
|
1172
1540
|
{
|
|
1173
1541
|
name: propCtx.children.propKey[0],
|
|
1174
|
-
value:
|
|
1542
|
+
value: valueToken
|
|
1175
1543
|
}
|
|
1176
1544
|
);
|
|
1177
1545
|
});
|
|
@@ -1200,6 +1568,114 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1200
1568
|
}
|
|
1201
1569
|
return ast;
|
|
1202
1570
|
}
|
|
1571
|
+
body(ctx) {
|
|
1572
|
+
const tokens = {
|
|
1573
|
+
keyword: ctx.Body[0],
|
|
1574
|
+
body: ctx.RCurly[0]
|
|
1575
|
+
};
|
|
1576
|
+
const ast = {
|
|
1577
|
+
type: "modal-body",
|
|
1578
|
+
children: []
|
|
1579
|
+
};
|
|
1580
|
+
if (this.sourceMapBuilder) {
|
|
1581
|
+
const nodeId = this.sourceMapBuilder.addNode("modal-body", tokens);
|
|
1582
|
+
ast._meta = { nodeId };
|
|
1583
|
+
this.sourceMapBuilder.pushParent(nodeId);
|
|
1584
|
+
}
|
|
1585
|
+
const childNodes = [];
|
|
1586
|
+
if (ctx.component) {
|
|
1587
|
+
ctx.component.forEach((comp) => {
|
|
1588
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1589
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
if (ctx.layout) {
|
|
1593
|
+
ctx.layout.forEach((layout) => {
|
|
1594
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1595
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1599
|
+
childNodes.forEach((item) => {
|
|
1600
|
+
ast.children.push(this.visit(item.node));
|
|
1601
|
+
});
|
|
1602
|
+
if (this.sourceMapBuilder) {
|
|
1603
|
+
this.sourceMapBuilder.popParent();
|
|
1604
|
+
}
|
|
1605
|
+
return ast;
|
|
1606
|
+
}
|
|
1607
|
+
tab(ctx) {
|
|
1608
|
+
const tokens = {
|
|
1609
|
+
keyword: ctx.Tab[0],
|
|
1610
|
+
body: ctx.RCurly[0]
|
|
1611
|
+
};
|
|
1612
|
+
const ast = {
|
|
1613
|
+
type: "tab",
|
|
1614
|
+
children: []
|
|
1615
|
+
};
|
|
1616
|
+
if (this.sourceMapBuilder) {
|
|
1617
|
+
const nodeId = this.sourceMapBuilder.addNode("tab", tokens);
|
|
1618
|
+
ast._meta = { nodeId };
|
|
1619
|
+
this.sourceMapBuilder.pushParent(nodeId);
|
|
1620
|
+
}
|
|
1621
|
+
const childNodes = [];
|
|
1622
|
+
if (ctx.component) {
|
|
1623
|
+
ctx.component.forEach((comp) => {
|
|
1624
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1625
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
if (ctx.layout) {
|
|
1629
|
+
ctx.layout.forEach((layout) => {
|
|
1630
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1631
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1635
|
+
childNodes.forEach((item) => {
|
|
1636
|
+
ast.children.push(this.visit(item.node));
|
|
1637
|
+
});
|
|
1638
|
+
if (this.sourceMapBuilder) {
|
|
1639
|
+
this.sourceMapBuilder.popParent();
|
|
1640
|
+
}
|
|
1641
|
+
return ast;
|
|
1642
|
+
}
|
|
1643
|
+
footer(ctx) {
|
|
1644
|
+
const tokens = {
|
|
1645
|
+
keyword: ctx.Footer[0],
|
|
1646
|
+
body: ctx.RCurly[0]
|
|
1647
|
+
};
|
|
1648
|
+
const ast = {
|
|
1649
|
+
type: "modal-footer",
|
|
1650
|
+
children: []
|
|
1651
|
+
};
|
|
1652
|
+
if (this.sourceMapBuilder) {
|
|
1653
|
+
const nodeId = this.sourceMapBuilder.addNode("modal-footer", tokens);
|
|
1654
|
+
ast._meta = { nodeId };
|
|
1655
|
+
this.sourceMapBuilder.pushParent(nodeId);
|
|
1656
|
+
}
|
|
1657
|
+
const childNodes = [];
|
|
1658
|
+
if (ctx.component) {
|
|
1659
|
+
ctx.component.forEach((comp) => {
|
|
1660
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1661
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
if (ctx.layout) {
|
|
1665
|
+
ctx.layout.forEach((layout) => {
|
|
1666
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1667
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1670
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1671
|
+
childNodes.forEach((item) => {
|
|
1672
|
+
ast.children.push(this.visit(item.node));
|
|
1673
|
+
});
|
|
1674
|
+
if (this.sourceMapBuilder) {
|
|
1675
|
+
this.sourceMapBuilder.popParent();
|
|
1676
|
+
}
|
|
1677
|
+
return ast;
|
|
1678
|
+
}
|
|
1203
1679
|
component(ctx) {
|
|
1204
1680
|
const tokens = {
|
|
1205
1681
|
keyword: ctx.Component[0],
|
|
@@ -1221,13 +1697,21 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1221
1697
|
if (ctx.property) {
|
|
1222
1698
|
ctx.property.forEach((propCtx) => {
|
|
1223
1699
|
const propResult = this.visit(propCtx);
|
|
1700
|
+
if (propResult.isEvent) {
|
|
1701
|
+
this.sourceMapBuilder.addEvent(nodeId, propResult.key, {
|
|
1702
|
+
name: propCtx.children.propKey[0],
|
|
1703
|
+
value: propCtx.children.actionChain[0]
|
|
1704
|
+
});
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
const valueToken = propCtx.children.propValue?.[0] ?? propCtx.children.arrayLiteral?.[0];
|
|
1224
1708
|
this.sourceMapBuilder.addProperty(
|
|
1225
1709
|
nodeId,
|
|
1226
1710
|
propResult.key,
|
|
1227
1711
|
propResult.value,
|
|
1228
1712
|
{
|
|
1229
1713
|
name: propCtx.children.propKey[0],
|
|
1230
|
-
value:
|
|
1714
|
+
value: valueToken
|
|
1231
1715
|
}
|
|
1232
1716
|
);
|
|
1233
1717
|
});
|
|
@@ -1574,11 +2058,13 @@ function createParserDiagnostic(error) {
|
|
|
1574
2058
|
};
|
|
1575
2059
|
}
|
|
1576
2060
|
function isBooleanLike(value) {
|
|
2061
|
+
if (Array.isArray(value)) return false;
|
|
1577
2062
|
if (typeof value === "number") return value === 0 || value === 1;
|
|
1578
2063
|
const normalized = String(value).trim().toLowerCase();
|
|
1579
2064
|
return normalized === "true" || normalized === "false";
|
|
1580
2065
|
}
|
|
1581
2066
|
function parseBooleanLike(value, fallback = false) {
|
|
2067
|
+
if (Array.isArray(value)) return fallback;
|
|
1582
2068
|
if (typeof value === "number") {
|
|
1583
2069
|
if (value === 1) return true;
|
|
1584
2070
|
if (value === 0) return false;
|
|
@@ -1635,6 +2121,14 @@ function validateSemanticDiagnostics(ast, sourceMap) {
|
|
|
1635
2121
|
count += countChildrenSlots(cellChild);
|
|
1636
2122
|
}
|
|
1637
2123
|
}
|
|
2124
|
+
} else if (child.type === "tab") {
|
|
2125
|
+
for (const tabChild of child.children) {
|
|
2126
|
+
if (tabChild.type === "component") {
|
|
2127
|
+
if (tabChild.componentType === "Children") count += 1;
|
|
2128
|
+
} else if (tabChild.type === "layout") {
|
|
2129
|
+
count += countChildrenSlots(tabChild);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
1638
2132
|
}
|
|
1639
2133
|
}
|
|
1640
2134
|
return count;
|
|
@@ -1881,6 +2375,11 @@ function validateSemanticDiagnostics(ast, sourceMap) {
|
|
|
1881
2375
|
checkLayout(child, insideDefinedLayout);
|
|
1882
2376
|
} else if (child.type === "cell") {
|
|
1883
2377
|
checkCell(child, insideDefinedLayout);
|
|
2378
|
+
} else if (child.type === "tab") {
|
|
2379
|
+
for (const tabChild of child.children) {
|
|
2380
|
+
if (tabChild.type === "component") checkComponent(tabChild, insideDefinedLayout);
|
|
2381
|
+
if (tabChild.type === "layout") checkLayout(tabChild, insideDefinedLayout);
|
|
2382
|
+
}
|
|
1884
2383
|
}
|
|
1885
2384
|
}
|
|
1886
2385
|
};
|
|
@@ -2058,6 +2557,16 @@ function validateDefinitionCycles(ast) {
|
|
|
2058
2557
|
collectLayoutDependencies(cellChild, deps);
|
|
2059
2558
|
}
|
|
2060
2559
|
}
|
|
2560
|
+
} else if (child.type === "tab") {
|
|
2561
|
+
for (const tabChild of child.children) {
|
|
2562
|
+
if (tabChild.type === "component") {
|
|
2563
|
+
if (shouldTrackComponentDependency(tabChild.componentType)) {
|
|
2564
|
+
deps.add(makeComponentKey(tabChild.componentType));
|
|
2565
|
+
}
|
|
2566
|
+
} else if (tabChild.type === "layout") {
|
|
2567
|
+
collectLayoutDependencies(tabChild, deps);
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2061
2570
|
}
|
|
2062
2571
|
}
|
|
2063
2572
|
};
|
|
@@ -2122,6 +2631,13 @@ Components and layouts cannot reference each other in a cycle.`
|
|
|
2122
2631
|
var import_zod = require("zod");
|
|
2123
2632
|
var import_components2 = require("@wire-dsl/language-support/components");
|
|
2124
2633
|
|
|
2634
|
+
// src/shared/list-utils.ts
|
|
2635
|
+
function toStringArray(value) {
|
|
2636
|
+
if (Array.isArray(value)) return value;
|
|
2637
|
+
if (value === void 0 || value === "") return [];
|
|
2638
|
+
return String(value).split(",").map((s) => s.trim()).filter(Boolean);
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2125
2641
|
// src/ir/device-presets.ts
|
|
2126
2642
|
var DEVICE_PRESETS = {
|
|
2127
2643
|
mobile: {
|
|
@@ -2207,6 +2723,7 @@ function isValidDevice(device) {
|
|
|
2207
2723
|
}
|
|
2208
2724
|
|
|
2209
2725
|
// src/ir/index.ts
|
|
2726
|
+
var SELF_TARGET = "_self";
|
|
2210
2727
|
var IRStyleSchema = import_zod.z.object({
|
|
2211
2728
|
density: import_zod.z.enum(["compact", "normal", "comfortable"]),
|
|
2212
2729
|
spacing: import_zod.z.enum(["xs", "sm", "md", "lg", "xl"]),
|
|
@@ -2228,12 +2745,27 @@ var IRMetaSchema = import_zod.z.object({
|
|
|
2228
2745
|
source: import_zod.z.string().optional(),
|
|
2229
2746
|
nodeId: import_zod.z.string().optional()
|
|
2230
2747
|
});
|
|
2748
|
+
var IREventActionSchema = import_zod.z.union([
|
|
2749
|
+
import_zod.z.object({ type: import_zod.z.literal("navigate"), screen: import_zod.z.string() }),
|
|
2750
|
+
import_zod.z.object({ type: import_zod.z.literal("show"), targetId: import_zod.z.string() }),
|
|
2751
|
+
import_zod.z.object({ type: import_zod.z.literal("hide"), targetId: import_zod.z.string() }),
|
|
2752
|
+
import_zod.z.object({ type: import_zod.z.literal("toggle"), targetId: import_zod.z.string() }),
|
|
2753
|
+
import_zod.z.object({ type: import_zod.z.literal("enable"), targetId: import_zod.z.string() }),
|
|
2754
|
+
import_zod.z.object({ type: import_zod.z.literal("disable"), targetId: import_zod.z.string() }),
|
|
2755
|
+
import_zod.z.object({ type: import_zod.z.literal("setTab"), tabsId: import_zod.z.string(), index: import_zod.z.number().int().min(0) }),
|
|
2756
|
+
import_zod.z.object({ type: import_zod.z.literal("navigateItems"), screens: import_zod.z.array(import_zod.z.string()) })
|
|
2757
|
+
]);
|
|
2758
|
+
var IREventHandlerSchema = import_zod.z.object({
|
|
2759
|
+
event: import_zod.z.enum(["onClick", "onChange", "onActive", "onInactive", "onItemsClick", "onItemClick", "onRowClick", "onClose"]),
|
|
2760
|
+
actions: import_zod.z.array(IREventActionSchema)
|
|
2761
|
+
});
|
|
2231
2762
|
var IRContainerNodeSchema = import_zod.z.object({
|
|
2232
2763
|
id: import_zod.z.string(),
|
|
2233
2764
|
kind: import_zod.z.literal("container"),
|
|
2234
|
-
containerType: import_zod.z.enum(["stack", "grid", "split", "panel", "card"]),
|
|
2235
|
-
params: import_zod.z.record(import_zod.z.string(), import_zod.z.union([import_zod.z.string(), import_zod.z.number()])),
|
|
2765
|
+
containerType: import_zod.z.enum(["stack", "grid", "split", "panel", "card", "tabs", "tab", "modal", "modal-body", "modal-footer"]),
|
|
2766
|
+
params: import_zod.z.record(import_zod.z.string(), import_zod.z.union([import_zod.z.string(), import_zod.z.number(), import_zod.z.array(import_zod.z.string())])),
|
|
2236
2767
|
children: import_zod.z.array(import_zod.z.object({ ref: import_zod.z.string() })),
|
|
2768
|
+
events: import_zod.z.array(IREventHandlerSchema).optional(),
|
|
2237
2769
|
style: IRNodeStyleSchema,
|
|
2238
2770
|
meta: IRMetaSchema
|
|
2239
2771
|
});
|
|
@@ -2241,7 +2773,9 @@ var IRComponentNodeSchema = import_zod.z.object({
|
|
|
2241
2773
|
id: import_zod.z.string(),
|
|
2242
2774
|
kind: import_zod.z.literal("component"),
|
|
2243
2775
|
componentType: import_zod.z.string(),
|
|
2244
|
-
props: import_zod.z.record(import_zod.z.string(), import_zod.z.union([import_zod.z.string(), import_zod.z.number()])),
|
|
2776
|
+
props: import_zod.z.record(import_zod.z.string(), import_zod.z.union([import_zod.z.string(), import_zod.z.number(), import_zod.z.array(import_zod.z.string())])),
|
|
2777
|
+
userDefinedId: import_zod.z.string().regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, "ID must match [a-zA-Z_][a-zA-Z0-9_]*").optional(),
|
|
2778
|
+
events: import_zod.z.array(IREventHandlerSchema).optional(),
|
|
2245
2779
|
style: IRNodeStyleSchema,
|
|
2246
2780
|
meta: IRMetaSchema
|
|
2247
2781
|
});
|
|
@@ -2250,7 +2784,7 @@ var IRInstanceNodeSchema = import_zod.z.object({
|
|
|
2250
2784
|
kind: import_zod.z.literal("instance"),
|
|
2251
2785
|
definitionName: import_zod.z.string(),
|
|
2252
2786
|
definitionKind: import_zod.z.enum(["component", "layout"]),
|
|
2253
|
-
invocationProps: import_zod.z.record(import_zod.z.string(), import_zod.z.union([import_zod.z.string(), import_zod.z.number()])),
|
|
2787
|
+
invocationProps: import_zod.z.record(import_zod.z.string(), import_zod.z.union([import_zod.z.string(), import_zod.z.number(), import_zod.z.array(import_zod.z.string())])),
|
|
2254
2788
|
expandedRoot: import_zod.z.object({ ref: import_zod.z.string() }),
|
|
2255
2789
|
style: IRNodeStyleSchema,
|
|
2256
2790
|
meta: IRMetaSchema
|
|
@@ -2444,25 +2978,29 @@ ${messages}`);
|
|
|
2444
2978
|
if (layout.children && layout.children.length > 0) {
|
|
2445
2979
|
layout.children.forEach((child) => {
|
|
2446
2980
|
if (child.type === "component") {
|
|
2447
|
-
found.push({
|
|
2448
|
-
componentType: child.componentType,
|
|
2449
|
-
location: "layout"
|
|
2450
|
-
});
|
|
2981
|
+
found.push({ componentType: child.componentType, location: "layout" });
|
|
2451
2982
|
} else if (child.type === "layout") {
|
|
2452
2983
|
this.findComponentsInLayout(child, found);
|
|
2453
2984
|
} else if (child.type === "cell") {
|
|
2454
2985
|
if (child.children) {
|
|
2455
2986
|
child.children.forEach((cellChild) => {
|
|
2456
2987
|
if (cellChild.type === "component") {
|
|
2457
|
-
found.push({
|
|
2458
|
-
componentType: cellChild.componentType,
|
|
2459
|
-
location: "cell"
|
|
2460
|
-
});
|
|
2988
|
+
found.push({ componentType: cellChild.componentType, location: "cell" });
|
|
2461
2989
|
} else if (cellChild.type === "layout") {
|
|
2462
2990
|
this.findComponentsInLayout(cellChild, found);
|
|
2463
2991
|
}
|
|
2464
2992
|
});
|
|
2465
2993
|
}
|
|
2994
|
+
} else if (child.type === "tab") {
|
|
2995
|
+
if (child.children) {
|
|
2996
|
+
child.children.forEach((tabChild) => {
|
|
2997
|
+
if (tabChild.type === "component") {
|
|
2998
|
+
found.push({ componentType: tabChild.componentType, location: "tab" });
|
|
2999
|
+
} else if (tabChild.type === "layout") {
|
|
3000
|
+
this.findComponentsInLayout(tabChild, found);
|
|
3001
|
+
}
|
|
3002
|
+
});
|
|
3003
|
+
}
|
|
2466
3004
|
}
|
|
2467
3005
|
});
|
|
2468
3006
|
}
|
|
@@ -2479,53 +3017,179 @@ ${messages}`);
|
|
|
2479
3017
|
if (layout.layoutType === "split") {
|
|
2480
3018
|
layoutParams = this.normalizeSplitParams(layoutParams);
|
|
2481
3019
|
}
|
|
2482
|
-
const
|
|
3020
|
+
const nonTabChildren = layout.children.filter((c) => c.type !== "tab");
|
|
2483
3021
|
const layoutDefinition = this.definedLayouts.get(layout.layoutType);
|
|
2484
3022
|
if (layoutDefinition) {
|
|
2485
|
-
return this.expandDefinedLayout(layoutDefinition, layoutParams,
|
|
3023
|
+
return this.expandDefinedLayout(layoutDefinition, layoutParams, nonTabChildren, context, layout._meta?.nodeId);
|
|
3024
|
+
}
|
|
3025
|
+
const nodeId = this.idGen.generate("node");
|
|
3026
|
+
const childRefs = [];
|
|
3027
|
+
if (layout.layoutType === "modal") {
|
|
3028
|
+
const bodyChildren = layout.children.filter((c) => c.type === "modal-body");
|
|
3029
|
+
const footerChildren = layout.children.filter((c) => c.type === "modal-footer");
|
|
3030
|
+
const normalChildren = layout.children.filter((c) => c.type !== "modal-body" && c.type !== "modal-footer");
|
|
3031
|
+
if (bodyChildren.length > 1 || footerChildren.length > 1) {
|
|
3032
|
+
if (bodyChildren.length > 1) {
|
|
3033
|
+
this.warnings.push({
|
|
3034
|
+
type: "modal-003-duplicate-body",
|
|
3035
|
+
message: "MODAL-003: A modal can only have one body section."
|
|
3036
|
+
});
|
|
3037
|
+
}
|
|
3038
|
+
if (footerChildren.length > 1) {
|
|
3039
|
+
this.warnings.push({
|
|
3040
|
+
type: "modal-004-duplicate-footer",
|
|
3041
|
+
message: "MODAL-004: A modal can only have one footer section."
|
|
3042
|
+
});
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
if ((bodyChildren.length > 0 || footerChildren.length > 0) && normalChildren.length > 0) {
|
|
3046
|
+
this.warnings.push({
|
|
3047
|
+
type: "modal-002-mixed-children",
|
|
3048
|
+
message: "MODAL-002: Cannot mix body/footer sections with direct children in a modal. Use either body/footer sections or direct children, not both."
|
|
3049
|
+
});
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
for (const child of layout.children) {
|
|
3053
|
+
if (child.type === "modal-body" || child.type === "modal-footer") {
|
|
3054
|
+
if (layout.layoutType !== "modal") {
|
|
3055
|
+
this.warnings.push({
|
|
3056
|
+
type: "modal-001-invalid-context",
|
|
3057
|
+
message: `MODAL-001: "${child.type}" sections are only valid inside layout modal.`
|
|
3058
|
+
});
|
|
3059
|
+
continue;
|
|
3060
|
+
}
|
|
3061
|
+
const childId = child.type === "modal-body" ? this.convertModalBody(child, context) : this.convertModalFooter(child, context);
|
|
3062
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3063
|
+
} else if (child.type === "layout") {
|
|
3064
|
+
const childId = this.convertLayout(child, context);
|
|
3065
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3066
|
+
} else if (child.type === "component") {
|
|
3067
|
+
const childId = this.convertComponent(child, context);
|
|
3068
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3069
|
+
} else if (child.type === "cell") {
|
|
3070
|
+
const childId = this.convertCell(child, context);
|
|
3071
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3072
|
+
} else if (child.type === "tab") {
|
|
3073
|
+
const childId = this.convertTab(child, context);
|
|
3074
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
const style = {};
|
|
3078
|
+
if (layoutParams.padding !== void 0) {
|
|
3079
|
+
style.padding = String(layoutParams.padding);
|
|
3080
|
+
} else {
|
|
3081
|
+
style.padding = "none";
|
|
3082
|
+
}
|
|
3083
|
+
if (layoutParams.gap !== void 0) {
|
|
3084
|
+
style.gap = String(layoutParams.gap);
|
|
3085
|
+
}
|
|
3086
|
+
if (layoutParams.justify !== void 0) {
|
|
3087
|
+
style.justify = layoutParams.justify;
|
|
3088
|
+
}
|
|
3089
|
+
if (layoutParams.align !== void 0) {
|
|
3090
|
+
style.align = layoutParams.align;
|
|
3091
|
+
}
|
|
3092
|
+
if (layoutParams.background !== void 0) {
|
|
3093
|
+
style.background = String(layoutParams.background);
|
|
3094
|
+
}
|
|
3095
|
+
if (layoutParams.id !== void 0) {
|
|
3096
|
+
const layoutId = String(layoutParams.id);
|
|
3097
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(layoutId)) {
|
|
3098
|
+
this.errors.push({
|
|
3099
|
+
type: "evt-009-invalid-id",
|
|
3100
|
+
message: `EVT-009: id "${layoutId}" is not a valid identifier. Must match [a-zA-Z_][a-zA-Z0-9_]* (cannot start with a digit or contain hyphens).`
|
|
3101
|
+
});
|
|
3102
|
+
}
|
|
2486
3103
|
}
|
|
3104
|
+
const irEvents = this.convertASTEvents(layout.events || []);
|
|
3105
|
+
const containerNode = {
|
|
3106
|
+
id: nodeId,
|
|
3107
|
+
kind: "container",
|
|
3108
|
+
containerType: layout.layoutType,
|
|
3109
|
+
params: this.cleanParams(layoutParams),
|
|
3110
|
+
children: childRefs,
|
|
3111
|
+
...irEvents.length > 0 ? { events: irEvents } : {},
|
|
3112
|
+
style,
|
|
3113
|
+
meta: {
|
|
3114
|
+
nodeId: context?.instanceScope ? `${layout._meta?.nodeId}@${context.instanceScope}` : layout._meta?.nodeId
|
|
3115
|
+
}
|
|
3116
|
+
};
|
|
3117
|
+
this.nodes[nodeId] = containerNode;
|
|
3118
|
+
return nodeId;
|
|
3119
|
+
}
|
|
3120
|
+
convertTab(tab, context) {
|
|
3121
|
+
const nodeId = this.idGen.generate("node");
|
|
3122
|
+
const childRefs = [];
|
|
3123
|
+
for (const child of tab.children) {
|
|
3124
|
+
if (child.type === "layout") {
|
|
3125
|
+
const childId = this.convertLayout(child, context);
|
|
3126
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3127
|
+
} else if (child.type === "component") {
|
|
3128
|
+
const childId = this.convertComponent(child, context);
|
|
3129
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
const containerNode = {
|
|
3133
|
+
id: nodeId,
|
|
3134
|
+
kind: "container",
|
|
3135
|
+
containerType: "tab",
|
|
3136
|
+
params: {},
|
|
3137
|
+
children: childRefs,
|
|
3138
|
+
style: { padding: "none" },
|
|
3139
|
+
meta: {
|
|
3140
|
+
nodeId: context?.instanceScope ? `${tab._meta?.nodeId}@${context.instanceScope}` : tab._meta?.nodeId
|
|
3141
|
+
}
|
|
3142
|
+
};
|
|
3143
|
+
this.nodes[nodeId] = containerNode;
|
|
3144
|
+
return nodeId;
|
|
3145
|
+
}
|
|
3146
|
+
convertModalBody(body, context) {
|
|
2487
3147
|
const nodeId = this.idGen.generate("node");
|
|
2488
3148
|
const childRefs = [];
|
|
2489
|
-
for (const child of
|
|
3149
|
+
for (const child of body.children) {
|
|
2490
3150
|
if (child.type === "layout") {
|
|
2491
3151
|
const childId = this.convertLayout(child, context);
|
|
2492
3152
|
if (childId) childRefs.push({ ref: childId });
|
|
2493
3153
|
} else if (child.type === "component") {
|
|
2494
3154
|
const childId = this.convertComponent(child, context);
|
|
2495
3155
|
if (childId) childRefs.push({ ref: childId });
|
|
2496
|
-
} else if (child.type === "cell") {
|
|
2497
|
-
const childId = this.convertCell(child, context);
|
|
2498
|
-
if (childId) childRefs.push({ ref: childId });
|
|
2499
3156
|
}
|
|
2500
3157
|
}
|
|
2501
|
-
const
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
3158
|
+
const containerNode = {
|
|
3159
|
+
id: nodeId,
|
|
3160
|
+
kind: "container",
|
|
3161
|
+
containerType: "modal-body",
|
|
3162
|
+
params: { direction: "vertical" },
|
|
3163
|
+
children: childRefs,
|
|
3164
|
+
style: { padding: "md", gap: "md" },
|
|
3165
|
+
meta: {
|
|
3166
|
+
nodeId: context?.instanceScope ? `${body._meta?.nodeId}@${context.instanceScope}` : body._meta?.nodeId
|
|
3167
|
+
}
|
|
3168
|
+
};
|
|
3169
|
+
this.nodes[nodeId] = containerNode;
|
|
3170
|
+
return nodeId;
|
|
3171
|
+
}
|
|
3172
|
+
convertModalFooter(footer, context) {
|
|
3173
|
+
const nodeId = this.idGen.generate("node");
|
|
3174
|
+
const childRefs = [];
|
|
3175
|
+
for (const child of footer.children) {
|
|
3176
|
+
if (child.type === "layout") {
|
|
3177
|
+
const childId = this.convertLayout(child, context);
|
|
3178
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3179
|
+
} else if (child.type === "component") {
|
|
3180
|
+
const childId = this.convertComponent(child, context);
|
|
3181
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3182
|
+
}
|
|
2518
3183
|
}
|
|
2519
3184
|
const containerNode = {
|
|
2520
3185
|
id: nodeId,
|
|
2521
3186
|
kind: "container",
|
|
2522
|
-
containerType:
|
|
2523
|
-
params:
|
|
3187
|
+
containerType: "modal-footer",
|
|
3188
|
+
params: { direction: "horizontal" },
|
|
2524
3189
|
children: childRefs,
|
|
2525
|
-
style,
|
|
3190
|
+
style: { padding: "md", justify: "spaceBetween" },
|
|
2526
3191
|
meta: {
|
|
2527
|
-
|
|
2528
|
-
nodeId: context?.instanceScope ? `${layout._meta?.nodeId}@${context.instanceScope}` : layout._meta?.nodeId
|
|
3192
|
+
nodeId: context?.instanceScope ? `${footer._meta?.nodeId}@${context.instanceScope}` : footer._meta?.nodeId
|
|
2529
3193
|
}
|
|
2530
3194
|
};
|
|
2531
3195
|
this.nodes[nodeId] = containerNode;
|
|
@@ -2608,7 +3272,6 @@ ${messages}`);
|
|
|
2608
3272
|
"IconButton",
|
|
2609
3273
|
"Alert",
|
|
2610
3274
|
"Badge",
|
|
2611
|
-
"Modal",
|
|
2612
3275
|
"List",
|
|
2613
3276
|
"Sidebar",
|
|
2614
3277
|
"Tabs",
|
|
@@ -2620,20 +3283,70 @@ ${messages}`);
|
|
|
2620
3283
|
this.undefinedComponentsUsed.add(component.componentType);
|
|
2621
3284
|
}
|
|
2622
3285
|
const nodeId = this.idGen.generate("node");
|
|
3286
|
+
const rawId = resolvedProps.id !== void 0 ? String(resolvedProps.id) : void 0;
|
|
3287
|
+
if (rawId !== void 0 && !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(rawId)) {
|
|
3288
|
+
this.errors.push({
|
|
3289
|
+
type: "evt-009-invalid-id",
|
|
3290
|
+
message: `EVT-009: id "${rawId}" is not a valid identifier. Must match [a-zA-Z_][a-zA-Z0-9_]* (cannot start with a digit or contain hyphens).`
|
|
3291
|
+
});
|
|
3292
|
+
}
|
|
3293
|
+
const userDefinedId = rawId;
|
|
3294
|
+
const propsWithoutId = { ...resolvedProps };
|
|
3295
|
+
delete propsWithoutId.id;
|
|
3296
|
+
const irEvents = this.convertASTEvents(component.events || []);
|
|
3297
|
+
const onItemsClickEvent = this.extractOnItemsClickEvent(propsWithoutId);
|
|
3298
|
+
if (onItemsClickEvent) {
|
|
3299
|
+
irEvents.push(onItemsClickEvent);
|
|
3300
|
+
delete propsWithoutId.onItemsClick;
|
|
3301
|
+
}
|
|
2623
3302
|
const componentNode = {
|
|
2624
3303
|
id: nodeId,
|
|
2625
3304
|
kind: "component",
|
|
2626
3305
|
componentType: component.componentType,
|
|
2627
|
-
props:
|
|
3306
|
+
props: propsWithoutId,
|
|
3307
|
+
...userDefinedId ? { userDefinedId } : {},
|
|
3308
|
+
...irEvents.length > 0 ? { events: irEvents } : {},
|
|
2628
3309
|
style: {},
|
|
2629
3310
|
meta: {
|
|
2630
|
-
// Scope nodeId per instance so each expansion gets a unique identifier
|
|
2631
3311
|
nodeId: context?.instanceScope ? `${component._meta?.nodeId}@${context.instanceScope}` : component._meta?.nodeId
|
|
2632
3312
|
}
|
|
2633
3313
|
};
|
|
2634
3314
|
this.nodes[nodeId] = componentNode;
|
|
2635
3315
|
return nodeId;
|
|
2636
3316
|
}
|
|
3317
|
+
convertASTEventAction(action) {
|
|
3318
|
+
switch (action.type) {
|
|
3319
|
+
case "navigate":
|
|
3320
|
+
return { type: "navigate", screen: action.screen };
|
|
3321
|
+
case "show":
|
|
3322
|
+
return { type: "show", targetId: action.targetId };
|
|
3323
|
+
case "hide":
|
|
3324
|
+
return { type: "hide", targetId: action.targetId };
|
|
3325
|
+
case "toggle":
|
|
3326
|
+
return { type: "toggle", targetId: action.targetId };
|
|
3327
|
+
case "enable":
|
|
3328
|
+
return { type: "enable", targetId: action.targetId };
|
|
3329
|
+
case "disable":
|
|
3330
|
+
return { type: "disable", targetId: action.targetId };
|
|
3331
|
+
case "setTab":
|
|
3332
|
+
return { type: "setTab", tabsId: action.tabsId, index: action.index };
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
convertASTEvents(events) {
|
|
3336
|
+
return events.map((handler) => ({
|
|
3337
|
+
event: handler.event,
|
|
3338
|
+
actions: handler.actions.map((a) => this.convertASTEventAction(a))
|
|
3339
|
+
}));
|
|
3340
|
+
}
|
|
3341
|
+
extractOnItemsClickEvent(props) {
|
|
3342
|
+
const value = props.onItemsClick;
|
|
3343
|
+
if (!value) return null;
|
|
3344
|
+
const screens = toStringArray(value);
|
|
3345
|
+
return {
|
|
3346
|
+
event: "onItemsClick",
|
|
3347
|
+
actions: [{ type: "navigateItems", screens }]
|
|
3348
|
+
};
|
|
3349
|
+
}
|
|
2637
3350
|
expandDefinedComponent(definition, invocationArgs, callSiteNodeId, parentContext) {
|
|
2638
3351
|
const context = {
|
|
2639
3352
|
args: invocationArgs,
|
|
@@ -2813,21 +3526,23 @@ ${messages}`);
|
|
|
2813
3526
|
key
|
|
2814
3527
|
);
|
|
2815
3528
|
if (resolvedValue !== void 0) {
|
|
3529
|
+
const metadata = import_components2.COMPONENTS[componentType];
|
|
3530
|
+
const propMeta = metadata?.properties?.[key];
|
|
2816
3531
|
const wasPropReference = typeof value === "string" && value.startsWith("prop_");
|
|
2817
|
-
if (wasPropReference) {
|
|
2818
|
-
const
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
type: "invalid-bound-enum-value",
|
|
2825
|
-
message: `Invalid value "${normalizedValue}" for property "${key}" in component "${componentType}". Expected one of: ${property.options.join(", ")}.`
|
|
2826
|
-
});
|
|
2827
|
-
}
|
|
3532
|
+
if (wasPropReference && propMeta?.type === "enum" && Array.isArray(propMeta.options)) {
|
|
3533
|
+
const normalizedValue = String(resolvedValue);
|
|
3534
|
+
if (!propMeta.options.includes(normalizedValue)) {
|
|
3535
|
+
this.warnings.push({
|
|
3536
|
+
type: "invalid-bound-enum-value",
|
|
3537
|
+
message: `Invalid value "${normalizedValue}" for property "${key}" in component "${componentType}". Expected one of: ${propMeta.options.join(", ")}.`
|
|
3538
|
+
});
|
|
2828
3539
|
}
|
|
2829
3540
|
}
|
|
2830
|
-
|
|
3541
|
+
if (propMeta?.type === "list" && !Array.isArray(resolvedValue)) {
|
|
3542
|
+
resolved[key] = toStringArray(resolvedValue);
|
|
3543
|
+
} else {
|
|
3544
|
+
resolved[key] = resolvedValue;
|
|
3545
|
+
}
|
|
2831
3546
|
}
|
|
2832
3547
|
}
|
|
2833
3548
|
return resolved;
|
|
@@ -2899,6 +3614,195 @@ function generateIR(ast) {
|
|
|
2899
3614
|
return generator.generate(ast);
|
|
2900
3615
|
}
|
|
2901
3616
|
|
|
3617
|
+
// src/state.ts
|
|
3618
|
+
function applyStateChange(ir, change, originNodeId) {
|
|
3619
|
+
switch (change.type) {
|
|
3620
|
+
case "setVisible":
|
|
3621
|
+
return applySetVisible(ir, change.targetId, change.visible, originNodeId);
|
|
3622
|
+
case "toggleVisible":
|
|
3623
|
+
return applyToggleVisible(ir, change.targetId, originNodeId);
|
|
3624
|
+
case "setActiveTab":
|
|
3625
|
+
return applySetActiveTab(ir, change.tabsId, change.index);
|
|
3626
|
+
case "setChecked":
|
|
3627
|
+
return applySetBooleanProp(ir, change.targetId, "checked", change.checked, originNodeId);
|
|
3628
|
+
case "toggleChecked":
|
|
3629
|
+
return applyToggleBooleanProp(ir, change.targetId, "checked", originNodeId);
|
|
3630
|
+
case "setEnabled":
|
|
3631
|
+
return applySetBooleanProp(ir, change.targetId, "enabled", change.enabled, originNodeId);
|
|
3632
|
+
case "toggleEnabled":
|
|
3633
|
+
return applyToggleBooleanProp(ir, change.targetId, "enabled", originNodeId);
|
|
3634
|
+
case "setDisabled":
|
|
3635
|
+
return applySetBooleanProp(ir, change.targetId, "disabled", change.disabled, originNodeId);
|
|
3636
|
+
case "navigateTo":
|
|
3637
|
+
return applyNavigateTo(ir, change.screen);
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
function applySetVisible(ir, targetId, visible, originNodeId) {
|
|
3641
|
+
const resolvedId = resolveTargetId(ir, targetId, originNodeId);
|
|
3642
|
+
if (!resolvedId) return ir;
|
|
3643
|
+
const nodes = mutateNodeVisible(ir.project.nodes, resolvedId, visible);
|
|
3644
|
+
return { ...ir, project: { ...ir.project, nodes } };
|
|
3645
|
+
}
|
|
3646
|
+
function applyToggleVisible(ir, targetId, originNodeId) {
|
|
3647
|
+
const resolvedId = resolveTargetId(ir, targetId, originNodeId);
|
|
3648
|
+
if (!resolvedId) return ir;
|
|
3649
|
+
const targetNodeEntry = findNodeByUserDefinedId(ir.project.nodes, resolvedId) || (resolvedId === originNodeId ? findNodeByMetaNodeId(ir.project.nodes, resolvedId) : null);
|
|
3650
|
+
const targetContainerEntry = !targetNodeEntry ? Object.values(ir.project.nodes).find(
|
|
3651
|
+
(n) => n.kind === "container" && (String(n.params.id) === resolvedId || n.meta.nodeId === resolvedId)
|
|
3652
|
+
) : void 0;
|
|
3653
|
+
let currentVisible = true;
|
|
3654
|
+
if (targetNodeEntry?.kind === "component") {
|
|
3655
|
+
currentVisible = targetNodeEntry.props?.visible !== "false";
|
|
3656
|
+
} else if (targetContainerEntry?.kind === "container") {
|
|
3657
|
+
currentVisible = String(targetContainerEntry.params.visible) !== "false";
|
|
3658
|
+
}
|
|
3659
|
+
const nodes = mutateNodeVisible(ir.project.nodes, resolvedId, !currentVisible);
|
|
3660
|
+
return { ...ir, project: { ...ir.project, nodes } };
|
|
3661
|
+
}
|
|
3662
|
+
function applySetActiveTab(ir, tabsId, index) {
|
|
3663
|
+
let found = false;
|
|
3664
|
+
const nodes = {};
|
|
3665
|
+
for (const [nodeKey, node] of Object.entries(ir.project.nodes)) {
|
|
3666
|
+
if (node.kind === "container" && node.containerType === "tabs" && node.params.id === tabsId) {
|
|
3667
|
+
nodes[nodeKey] = {
|
|
3668
|
+
...node,
|
|
3669
|
+
params: { ...node.params, active: index }
|
|
3670
|
+
};
|
|
3671
|
+
found = true;
|
|
3672
|
+
} else if (node.kind === "component" && node.componentType === "Tabs" && node.props.tabsId === tabsId) {
|
|
3673
|
+
nodes[nodeKey] = {
|
|
3674
|
+
...node,
|
|
3675
|
+
props: { ...node.props, active: index }
|
|
3676
|
+
};
|
|
3677
|
+
found = true;
|
|
3678
|
+
} else {
|
|
3679
|
+
nodes[nodeKey] = node;
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
if (!found) {
|
|
3683
|
+
console.warn(`[applyStateChange] setActiveTab: no layout tabs found with id "${tabsId}"`);
|
|
3684
|
+
return ir;
|
|
3685
|
+
}
|
|
3686
|
+
return { ...ir, project: { ...ir.project, nodes } };
|
|
3687
|
+
}
|
|
3688
|
+
function applySetBooleanProp(ir, targetId, propName, value, originNodeId) {
|
|
3689
|
+
const resolvedId = resolveTargetId(ir, targetId, originNodeId);
|
|
3690
|
+
if (!resolvedId) return ir;
|
|
3691
|
+
const target = findTargetComponentNode(ir.project.nodes, resolvedId);
|
|
3692
|
+
if (!target) {
|
|
3693
|
+
console.warn(`[applyStateChange] ${propName}: no node found with id "${resolvedId}"`);
|
|
3694
|
+
return ir;
|
|
3695
|
+
}
|
|
3696
|
+
const nodes = mutateNodeBooleanProp(ir.project.nodes, resolvedId, propName, value);
|
|
3697
|
+
return { ...ir, project: { ...ir.project, nodes } };
|
|
3698
|
+
}
|
|
3699
|
+
function applyToggleBooleanProp(ir, targetId, propName, originNodeId) {
|
|
3700
|
+
const resolvedId = resolveTargetId(ir, targetId, originNodeId);
|
|
3701
|
+
if (!resolvedId) return ir;
|
|
3702
|
+
const targetNodeEntry = findTargetComponentNode(ir.project.nodes, resolvedId);
|
|
3703
|
+
const currentValue = targetNodeEntry ? String(targetNodeEntry.props[propName] || "false").toLowerCase() === "true" : false;
|
|
3704
|
+
const nodes = mutateNodeBooleanProp(ir.project.nodes, resolvedId, propName, !currentValue);
|
|
3705
|
+
return { ...ir, project: { ...ir.project, nodes } };
|
|
3706
|
+
}
|
|
3707
|
+
function applyNavigateTo(ir, screenName) {
|
|
3708
|
+
const updatedProject = {
|
|
3709
|
+
...ir.project,
|
|
3710
|
+
activeScreen: screenName
|
|
3711
|
+
};
|
|
3712
|
+
return { ...ir, project: updatedProject };
|
|
3713
|
+
}
|
|
3714
|
+
function resolveTargetId(ir, targetId, originNodeId) {
|
|
3715
|
+
if (targetId === SELF_TARGET) {
|
|
3716
|
+
if (!originNodeId) {
|
|
3717
|
+
console.warn("[applyStateChange] targetId is _self but no originNodeId was provided");
|
|
3718
|
+
return null;
|
|
3719
|
+
}
|
|
3720
|
+
return originNodeId;
|
|
3721
|
+
}
|
|
3722
|
+
return targetId;
|
|
3723
|
+
}
|
|
3724
|
+
function findNodeByUserDefinedId(nodes, userDefinedId) {
|
|
3725
|
+
for (const node of Object.values(nodes)) {
|
|
3726
|
+
if (node.kind === "component" && node.userDefinedId === userDefinedId) {
|
|
3727
|
+
return node;
|
|
3728
|
+
}
|
|
3729
|
+
}
|
|
3730
|
+
return null;
|
|
3731
|
+
}
|
|
3732
|
+
function findNodeByMetaNodeId(nodes, metaNodeId) {
|
|
3733
|
+
for (const node of Object.values(nodes)) {
|
|
3734
|
+
if (node.kind === "component" && node.meta.nodeId === metaNodeId) {
|
|
3735
|
+
return node;
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
return null;
|
|
3739
|
+
}
|
|
3740
|
+
function findTargetComponentNode(nodes, targetId) {
|
|
3741
|
+
return findNodeByUserDefinedId(nodes, targetId) || findNodeByMetaNodeId(nodes, targetId);
|
|
3742
|
+
}
|
|
3743
|
+
function mutateNodeVisible(nodes, targetId, visible) {
|
|
3744
|
+
let found = false;
|
|
3745
|
+
const result = {};
|
|
3746
|
+
for (const [key, node] of Object.entries(nodes)) {
|
|
3747
|
+
if (node.kind === "component") {
|
|
3748
|
+
const matchByUserDefined = node.userDefinedId === targetId;
|
|
3749
|
+
const matchByMetaNodeId = node.meta.nodeId === targetId;
|
|
3750
|
+
if (matchByUserDefined || matchByMetaNodeId) {
|
|
3751
|
+
result[key] = {
|
|
3752
|
+
...node,
|
|
3753
|
+
props: { ...node.props, visible: visible ? "true" : "false" }
|
|
3754
|
+
};
|
|
3755
|
+
found = true;
|
|
3756
|
+
} else {
|
|
3757
|
+
result[key] = node;
|
|
3758
|
+
}
|
|
3759
|
+
} else if (node.kind === "container") {
|
|
3760
|
+
const matchByParamsId = node.params.id !== void 0 && String(node.params.id) === targetId;
|
|
3761
|
+
const matchByMetaNodeId = node.meta.nodeId === targetId;
|
|
3762
|
+
if (matchByParamsId || matchByMetaNodeId) {
|
|
3763
|
+
result[key] = {
|
|
3764
|
+
...node,
|
|
3765
|
+
params: { ...node.params, visible: visible ? "true" : "false" }
|
|
3766
|
+
};
|
|
3767
|
+
found = true;
|
|
3768
|
+
} else {
|
|
3769
|
+
result[key] = node;
|
|
3770
|
+
}
|
|
3771
|
+
} else {
|
|
3772
|
+
result[key] = node;
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
if (!found) {
|
|
3776
|
+
console.warn(`[applyStateChange] setVisible: no node found with id "${targetId}"`);
|
|
3777
|
+
}
|
|
3778
|
+
return result;
|
|
3779
|
+
}
|
|
3780
|
+
function mutateNodeBooleanProp(nodes, targetId, propName, value) {
|
|
3781
|
+
let found = false;
|
|
3782
|
+
const result = {};
|
|
3783
|
+
for (const [key, node] of Object.entries(nodes)) {
|
|
3784
|
+
if (node.kind === "component") {
|
|
3785
|
+
const matchByUserDefined = node.userDefinedId === targetId;
|
|
3786
|
+
const matchByMetaNodeId = node.meta.nodeId === targetId;
|
|
3787
|
+
if (matchByUserDefined || matchByMetaNodeId) {
|
|
3788
|
+
result[key] = {
|
|
3789
|
+
...node,
|
|
3790
|
+
props: { ...node.props, [propName]: value ? "true" : "false" }
|
|
3791
|
+
};
|
|
3792
|
+
found = true;
|
|
3793
|
+
} else {
|
|
3794
|
+
result[key] = node;
|
|
3795
|
+
}
|
|
3796
|
+
} else {
|
|
3797
|
+
result[key] = node;
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
if (!found) {
|
|
3801
|
+
console.warn(`[applyStateChange] ${propName}: no node found with id "${targetId}"`);
|
|
3802
|
+
}
|
|
3803
|
+
return result;
|
|
3804
|
+
}
|
|
3805
|
+
|
|
2902
3806
|
// src/shared/spacing.ts
|
|
2903
3807
|
var SPACING_VALUES = {
|
|
2904
3808
|
none: 0,
|
|
@@ -3078,6 +3982,10 @@ var LayoutEngine = class {
|
|
|
3078
3982
|
}
|
|
3079
3983
|
calculateContainer(node, nodeId, x, y, width, height) {
|
|
3080
3984
|
if (node.kind !== "container") return;
|
|
3985
|
+
if (node.containerType === "modal") {
|
|
3986
|
+
this.calculateModal(node, nodeId, x, y, width, height);
|
|
3987
|
+
return;
|
|
3988
|
+
}
|
|
3081
3989
|
const usesOuterPadding = node.containerType !== "card";
|
|
3082
3990
|
const padding = usesOuterPadding ? this.resolveSpacing(node.style.padding) : 0;
|
|
3083
3991
|
const innerX = x + padding;
|
|
@@ -3089,6 +3997,11 @@ var LayoutEngine = class {
|
|
|
3089
3997
|
this.result[nodeId] = { x, y, width, height };
|
|
3090
3998
|
switch (node.containerType) {
|
|
3091
3999
|
case "stack":
|
|
4000
|
+
case "tab":
|
|
4001
|
+
case "modal-body":
|
|
4002
|
+
this.calculateStack(node, innerX, innerY, innerWidth, innerHeight);
|
|
4003
|
+
break;
|
|
4004
|
+
case "modal-footer":
|
|
3092
4005
|
this.calculateStack(node, innerX, innerY, innerWidth, innerHeight);
|
|
3093
4006
|
break;
|
|
3094
4007
|
case "grid":
|
|
@@ -3103,8 +4016,12 @@ var LayoutEngine = class {
|
|
|
3103
4016
|
case "card":
|
|
3104
4017
|
this.calculateCard(node, innerX, innerY, innerWidth, innerHeight);
|
|
3105
4018
|
break;
|
|
4019
|
+
case "tabs":
|
|
4020
|
+
this.calculateTabs(node, innerX, innerY, innerWidth);
|
|
4021
|
+
break;
|
|
3106
4022
|
}
|
|
3107
|
-
|
|
4023
|
+
const isHorizontalStack = node.containerType === "stack" && !isVerticalStack;
|
|
4024
|
+
if ((isVerticalStack || isHorizontalStack || node.containerType === "card" || node.containerType === "tabs" || node.containerType === "tab" || node.containerType === "modal-body" || node.containerType === "modal-footer") && node.children.length > 0) {
|
|
3108
4025
|
let containerMaxY = y;
|
|
3109
4026
|
node.children.forEach((childRef) => {
|
|
3110
4027
|
const childPos = this.result[childRef.ref];
|
|
@@ -3124,8 +4041,22 @@ var LayoutEngine = class {
|
|
|
3124
4041
|
const children = node.children;
|
|
3125
4042
|
if (direction === "vertical") {
|
|
3126
4043
|
let currentY = y;
|
|
3127
|
-
children.
|
|
4044
|
+
const flowChildren = children.filter((cr) => {
|
|
4045
|
+
const n = this.nodes[cr.ref];
|
|
4046
|
+
if (n?.kind === "container" && n.containerType === "modal") return false;
|
|
4047
|
+
return this.isNodeVisible(cr.ref);
|
|
4048
|
+
});
|
|
4049
|
+
let flowCount = 0;
|
|
4050
|
+
children.forEach((childRef) => {
|
|
3128
4051
|
const childNode = this.nodes[childRef.ref];
|
|
4052
|
+
if (childNode?.kind === "container" && childNode.containerType === "modal") {
|
|
4053
|
+
this.calculateNode(childRef.ref, x, currentY, width, 0, "stack");
|
|
4054
|
+
return;
|
|
4055
|
+
}
|
|
4056
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4057
|
+
this.calculateNode(childRef.ref, x, currentY, width, 0, "stack");
|
|
4058
|
+
return;
|
|
4059
|
+
}
|
|
3129
4060
|
let childHeight = this.getComponentHeight();
|
|
3130
4061
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
3131
4062
|
childHeight = Number(childNode.props.height);
|
|
@@ -3136,12 +4067,17 @@ var LayoutEngine = class {
|
|
|
3136
4067
|
}
|
|
3137
4068
|
this.calculateNode(childRef.ref, x, currentY, width, childHeight, "stack");
|
|
3138
4069
|
currentY += childHeight;
|
|
3139
|
-
|
|
4070
|
+
flowCount++;
|
|
4071
|
+
if (flowCount < flowChildren.length) {
|
|
3140
4072
|
currentY += gap;
|
|
3141
4073
|
}
|
|
3142
4074
|
});
|
|
3143
4075
|
let adjustedY = y;
|
|
3144
|
-
|
|
4076
|
+
let adjustedCount = 0;
|
|
4077
|
+
children.forEach((childRef) => {
|
|
4078
|
+
const childNode = this.nodes[childRef.ref];
|
|
4079
|
+
if (childNode?.kind === "container" && childNode.containerType === "modal") return;
|
|
4080
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3145
4081
|
const childPos = this.result[childRef.ref];
|
|
3146
4082
|
if (childPos) {
|
|
3147
4083
|
const deltaY = adjustedY - childPos.y;
|
|
@@ -3150,7 +4086,8 @@ var LayoutEngine = class {
|
|
|
3150
4086
|
this.adjustNodeYPositions(childRef.ref, deltaY);
|
|
3151
4087
|
}
|
|
3152
4088
|
adjustedY += childPos.height;
|
|
3153
|
-
|
|
4089
|
+
adjustedCount++;
|
|
4090
|
+
if (adjustedCount < flowChildren.length) {
|
|
3154
4091
|
adjustedY += gap;
|
|
3155
4092
|
}
|
|
3156
4093
|
}
|
|
@@ -3160,9 +4097,10 @@ var LayoutEngine = class {
|
|
|
3160
4097
|
const crossAlign = node.style.align || "start";
|
|
3161
4098
|
if (justify === "stretch") {
|
|
3162
4099
|
let currentX = x;
|
|
3163
|
-
const
|
|
4100
|
+
const visibleChildren = children.filter((cr) => this.isNodeVisible(cr.ref));
|
|
4101
|
+
const childWidth = this.calculateChildWidth(visibleChildren.length, width, gap);
|
|
3164
4102
|
let stackHeight = 0;
|
|
3165
|
-
|
|
4103
|
+
visibleChildren.forEach((childRef) => {
|
|
3166
4104
|
const childNode = this.nodes[childRef.ref];
|
|
3167
4105
|
let childHeight = this.getComponentHeight();
|
|
3168
4106
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
@@ -3175,6 +4113,10 @@ var LayoutEngine = class {
|
|
|
3175
4113
|
stackHeight = Math.max(stackHeight, childHeight);
|
|
3176
4114
|
});
|
|
3177
4115
|
children.forEach((childRef) => {
|
|
4116
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4117
|
+
this.calculateNode(childRef.ref, currentX, y, 0, 0, "stack");
|
|
4118
|
+
return;
|
|
4119
|
+
}
|
|
3178
4120
|
this.calculateNode(childRef.ref, currentX, y, childWidth, stackHeight, "stack");
|
|
3179
4121
|
currentX += childWidth + gap;
|
|
3180
4122
|
});
|
|
@@ -3183,9 +4125,18 @@ var LayoutEngine = class {
|
|
|
3183
4125
|
const childHeights = [];
|
|
3184
4126
|
const explicitHeightFlags = [];
|
|
3185
4127
|
const flexIndices = /* @__PURE__ */ new Set();
|
|
4128
|
+
const visibleFlags = [];
|
|
3186
4129
|
let stackHeight = 0;
|
|
3187
4130
|
children.forEach((childRef, index) => {
|
|
3188
4131
|
const childNode = this.nodes[childRef.ref];
|
|
4132
|
+
const visible = this.isNodeVisible(childRef.ref);
|
|
4133
|
+
visibleFlags.push(visible);
|
|
4134
|
+
if (!visible) {
|
|
4135
|
+
childWidths.push(0);
|
|
4136
|
+
childHeights.push(0);
|
|
4137
|
+
explicitHeightFlags.push(false);
|
|
4138
|
+
return;
|
|
4139
|
+
}
|
|
3189
4140
|
const hasExplicitHeight = childNode?.kind === "component" && !!childNode.props.height;
|
|
3190
4141
|
const hasExplicitWidth = childNode?.kind === "component" && !!childNode.props.width;
|
|
3191
4142
|
const isBlockButton = childNode?.kind === "component" && childNode.componentType === "Button" && !hasExplicitWidth && this.parseBooleanProp(childNode.props.block, false);
|
|
@@ -3203,7 +4154,8 @@ var LayoutEngine = class {
|
|
|
3203
4154
|
childHeights.push(this.getComponentHeight());
|
|
3204
4155
|
explicitHeightFlags.push(hasExplicitHeight);
|
|
3205
4156
|
});
|
|
3206
|
-
const
|
|
4157
|
+
const visibleCount = visibleFlags.filter(Boolean).length;
|
|
4158
|
+
const totalGapWidth = gap * Math.max(0, visibleCount - 1);
|
|
3207
4159
|
if (flexIndices.size > 0) {
|
|
3208
4160
|
const fixedWidth = childWidths.reduce((sum, w, idx) => {
|
|
3209
4161
|
return flexIndices.has(idx) ? sum : sum + w;
|
|
@@ -3215,6 +4167,7 @@ var LayoutEngine = class {
|
|
|
3215
4167
|
});
|
|
3216
4168
|
}
|
|
3217
4169
|
children.forEach((childRef, index) => {
|
|
4170
|
+
if (!visibleFlags[index]) return;
|
|
3218
4171
|
const childNode = this.nodes[childRef.ref];
|
|
3219
4172
|
const childWidth = childWidths[index];
|
|
3220
4173
|
let childHeight = this.getComponentHeight();
|
|
@@ -3238,14 +4191,18 @@ var LayoutEngine = class {
|
|
|
3238
4191
|
startX = x + width - totalContentWidth;
|
|
3239
4192
|
} else if (justify === "spaceBetween") {
|
|
3240
4193
|
startX = x;
|
|
3241
|
-
dynamicGap =
|
|
4194
|
+
dynamicGap = visibleCount > 1 ? (width - totalChildWidth) / (visibleCount - 1) : 0;
|
|
3242
4195
|
} else if (justify === "spaceAround") {
|
|
3243
|
-
const spacing =
|
|
4196
|
+
const spacing = visibleCount > 0 ? (width - totalChildWidth) / visibleCount : 0;
|
|
3244
4197
|
startX = x + spacing / 2;
|
|
3245
4198
|
dynamicGap = spacing;
|
|
3246
4199
|
}
|
|
3247
4200
|
let currentX = startX;
|
|
3248
4201
|
children.forEach((childRef, index) => {
|
|
4202
|
+
if (!visibleFlags[index]) {
|
|
4203
|
+
this.calculateNode(childRef.ref, currentX, y, 0, 0, "stack");
|
|
4204
|
+
return;
|
|
4205
|
+
}
|
|
3249
4206
|
const childWidth = childWidths[index];
|
|
3250
4207
|
const childHeight = childHeights[index];
|
|
3251
4208
|
let childY = y;
|
|
@@ -3277,6 +4234,7 @@ var LayoutEngine = class {
|
|
|
3277
4234
|
let currentRowMaxHeight = 0;
|
|
3278
4235
|
const rowHeights = [0];
|
|
3279
4236
|
node.children.forEach((childRef) => {
|
|
4237
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3280
4238
|
const child = this.nodes[childRef.ref];
|
|
3281
4239
|
let span = 1;
|
|
3282
4240
|
let childHeight = this.getComponentHeight();
|
|
@@ -3311,6 +4269,18 @@ var LayoutEngine = class {
|
|
|
3311
4269
|
}
|
|
3312
4270
|
return totalHeight;
|
|
3313
4271
|
}
|
|
4272
|
+
if (node.containerType === "tabs") {
|
|
4273
|
+
const activeIndex = Number(node.params.active) || 0;
|
|
4274
|
+
const activeChildRef = node.children[activeIndex] ?? node.children[0];
|
|
4275
|
+
if (!activeChildRef) return totalHeight;
|
|
4276
|
+
const activeChild = this.nodes[activeChildRef.ref];
|
|
4277
|
+
if (activeChild?.kind === "container") {
|
|
4278
|
+
totalHeight += this.calculateContainerHeight(activeChild, availableWidth);
|
|
4279
|
+
} else if (activeChild?.kind === "component") {
|
|
4280
|
+
totalHeight += this.getIntrinsicComponentHeight(activeChild, availableWidth);
|
|
4281
|
+
}
|
|
4282
|
+
return totalHeight;
|
|
4283
|
+
}
|
|
3314
4284
|
if (node.containerType === "split") {
|
|
3315
4285
|
const splitGap = this.resolveSpacing(node.style.gap);
|
|
3316
4286
|
const leftParam = node.params.left;
|
|
@@ -3323,6 +4293,7 @@ var LayoutEngine = class {
|
|
|
3323
4293
|
const rightWidth = Number.isFinite(rightWidthRaw) && rightWidthRaw > 0 ? rightWidthRaw : availableWidth / 2;
|
|
3324
4294
|
let maxHeight = 0;
|
|
3325
4295
|
node.children.forEach((childRef, index) => {
|
|
4296
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3326
4297
|
const child = this.nodes[childRef.ref];
|
|
3327
4298
|
let childHeight = this.getComponentHeight();
|
|
3328
4299
|
const isFirst = index === 0;
|
|
@@ -3350,6 +4321,7 @@ var LayoutEngine = class {
|
|
|
3350
4321
|
if (node.containerType === "stack" && direction === "horizontal") {
|
|
3351
4322
|
let maxHeight = 0;
|
|
3352
4323
|
node.children.forEach((childRef) => {
|
|
4324
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3353
4325
|
const child = this.nodes[childRef.ref];
|
|
3354
4326
|
let childHeight = this.getComponentHeight();
|
|
3355
4327
|
if (child?.kind === "component") {
|
|
@@ -3366,7 +4338,10 @@ var LayoutEngine = class {
|
|
|
3366
4338
|
totalHeight += maxHeight;
|
|
3367
4339
|
return totalHeight;
|
|
3368
4340
|
}
|
|
3369
|
-
node.children.
|
|
4341
|
+
const visibleLinear = node.children.filter((cr) => this.isNodeVisible(cr.ref));
|
|
4342
|
+
let linearIndex = 0;
|
|
4343
|
+
node.children.forEach((childRef) => {
|
|
4344
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3370
4345
|
const child = this.nodes[childRef.ref];
|
|
3371
4346
|
let childHeight = this.getComponentHeight();
|
|
3372
4347
|
if (child?.kind === "component") {
|
|
@@ -3379,7 +4354,8 @@ var LayoutEngine = class {
|
|
|
3379
4354
|
childHeight = this.calculateContainerHeight(child, availableWidth);
|
|
3380
4355
|
}
|
|
3381
4356
|
totalHeight += childHeight;
|
|
3382
|
-
|
|
4357
|
+
linearIndex++;
|
|
4358
|
+
if (linearIndex < visibleLinear.length) {
|
|
3383
4359
|
totalHeight += gap;
|
|
3384
4360
|
}
|
|
3385
4361
|
});
|
|
@@ -3392,6 +4368,10 @@ var LayoutEngine = class {
|
|
|
3392
4368
|
const colWidth = (width - gap * (columns - 1)) / columns;
|
|
3393
4369
|
const cellHeights = {};
|
|
3394
4370
|
node.children.forEach((childRef, cellIndex) => {
|
|
4371
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4372
|
+
cellHeights[cellIndex] = 0;
|
|
4373
|
+
return;
|
|
4374
|
+
}
|
|
3395
4375
|
const child = this.nodes[childRef.ref];
|
|
3396
4376
|
let cellHeight = this.getComponentHeight();
|
|
3397
4377
|
let span = 1;
|
|
@@ -3416,6 +4396,11 @@ var LayoutEngine = class {
|
|
|
3416
4396
|
const rowHeights = [0];
|
|
3417
4397
|
const cellPositions = [];
|
|
3418
4398
|
node.children.forEach((childRef, cellIndex) => {
|
|
4399
|
+
const visible = this.isNodeVisible(childRef.ref);
|
|
4400
|
+
if (!visible) {
|
|
4401
|
+
cellPositions.push({ row: currentRow, col: currentCol, span: 0, visible: false });
|
|
4402
|
+
return;
|
|
4403
|
+
}
|
|
3419
4404
|
const child = this.nodes[childRef.ref];
|
|
3420
4405
|
let span = 1;
|
|
3421
4406
|
if (child?.kind === "container" && child.meta?.source === "cell") {
|
|
@@ -3427,13 +4412,17 @@ var LayoutEngine = class {
|
|
|
3427
4412
|
currentCol = 0;
|
|
3428
4413
|
currentRowMaxHeight = 0;
|
|
3429
4414
|
}
|
|
3430
|
-
cellPositions.push({ row: currentRow, col: currentCol, span });
|
|
4415
|
+
cellPositions.push({ row: currentRow, col: currentCol, span, visible: true });
|
|
3431
4416
|
currentRowMaxHeight = Math.max(currentRowMaxHeight, cellHeights[cellIndex]);
|
|
3432
4417
|
currentCol += span;
|
|
3433
4418
|
});
|
|
3434
4419
|
rowHeights[currentRow] = currentRowMaxHeight;
|
|
3435
4420
|
node.children.forEach((childRef, cellIndex) => {
|
|
3436
|
-
const { row, col, span } = cellPositions[cellIndex];
|
|
4421
|
+
const { row, col, span, visible } = cellPositions[cellIndex];
|
|
4422
|
+
if (!visible) {
|
|
4423
|
+
this.calculateNode(childRef.ref, x, y, 0, 0, "grid");
|
|
4424
|
+
return;
|
|
4425
|
+
}
|
|
3437
4426
|
const cellHeight = rowHeights[row];
|
|
3438
4427
|
let cellY = y;
|
|
3439
4428
|
for (let r = 0; r < row; r++) {
|
|
@@ -3504,8 +4493,14 @@ var LayoutEngine = class {
|
|
|
3504
4493
|
const innerCardWidth = width - cardPadding * 2;
|
|
3505
4494
|
const children = node.children;
|
|
3506
4495
|
let currentY = y + cardPadding;
|
|
3507
|
-
children.
|
|
4496
|
+
const flowChildren = children.filter((cr) => this.isNodeVisible(cr.ref));
|
|
4497
|
+
let flowCount = 0;
|
|
4498
|
+
children.forEach((childRef) => {
|
|
3508
4499
|
const childNode = this.nodes[childRef.ref];
|
|
4500
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4501
|
+
this.calculateNode(childRef.ref, x + cardPadding, currentY, innerCardWidth, 0, "card");
|
|
4502
|
+
return;
|
|
4503
|
+
}
|
|
3509
4504
|
let childHeight = this.getComponentHeight();
|
|
3510
4505
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
3511
4506
|
childHeight = Number(childNode.props.height);
|
|
@@ -3516,11 +4511,63 @@ var LayoutEngine = class {
|
|
|
3516
4511
|
}
|
|
3517
4512
|
this.calculateNode(childRef.ref, x + cardPadding, currentY, innerCardWidth, childHeight, "card");
|
|
3518
4513
|
currentY += childHeight;
|
|
3519
|
-
|
|
4514
|
+
flowCount++;
|
|
4515
|
+
if (flowCount < flowChildren.length) {
|
|
3520
4516
|
currentY += gap;
|
|
3521
4517
|
}
|
|
3522
4518
|
});
|
|
3523
4519
|
}
|
|
4520
|
+
calculateTabs(node, x, y, width) {
|
|
4521
|
+
if (node.kind !== "container") return;
|
|
4522
|
+
const activeIndex = Number(node.params.active) || 0;
|
|
4523
|
+
node.children.forEach((childRef, index) => {
|
|
4524
|
+
if (index === activeIndex) {
|
|
4525
|
+
const tabNode = this.nodes[childRef.ref];
|
|
4526
|
+
let tabHeight = 40;
|
|
4527
|
+
if (tabNode?.kind === "container") {
|
|
4528
|
+
tabHeight = this.calculateContainerHeight(tabNode, width);
|
|
4529
|
+
} else if (tabNode?.kind === "component") {
|
|
4530
|
+
tabHeight = this.getIntrinsicComponentHeight(tabNode, width);
|
|
4531
|
+
}
|
|
4532
|
+
this.calculateNode(childRef.ref, x, y, width, tabHeight, "tabs");
|
|
4533
|
+
}
|
|
4534
|
+
});
|
|
4535
|
+
}
|
|
4536
|
+
calculateModal(node, nodeId, _canvasX, _canvasY, _canvasWidth, _canvasHeight) {
|
|
4537
|
+
if (node.kind !== "container") return;
|
|
4538
|
+
const viewportWidth = this.viewport.width;
|
|
4539
|
+
const size = String(node.params.size || "md");
|
|
4540
|
+
const modalWidths = { sm: 380, md: 520, lg: 720 };
|
|
4541
|
+
const modalWidth = Math.min(modalWidths[size] ?? 520, viewportWidth - 32);
|
|
4542
|
+
const modalX = Math.round((viewportWidth - modalWidth) / 2);
|
|
4543
|
+
const modalY = 64;
|
|
4544
|
+
const hasHeader = node.params.title !== void 0 && node.params.title !== "";
|
|
4545
|
+
const headerHeight = hasHeader ? 48 : 0;
|
|
4546
|
+
let childrenHeight = 0;
|
|
4547
|
+
const innerWidth = modalWidth;
|
|
4548
|
+
let currentY = modalY + headerHeight;
|
|
4549
|
+
node.children.forEach((childRef, index) => {
|
|
4550
|
+
const childNode = this.nodes[childRef.ref];
|
|
4551
|
+
let childHeight = 0;
|
|
4552
|
+
if (childNode?.kind === "container") {
|
|
4553
|
+
childHeight = this.calculateContainerHeight(childNode, innerWidth);
|
|
4554
|
+
} else if (childNode?.kind === "component") {
|
|
4555
|
+
childHeight = this.getIntrinsicComponentHeight(childNode, innerWidth);
|
|
4556
|
+
} else {
|
|
4557
|
+
childHeight = this.getComponentHeight();
|
|
4558
|
+
}
|
|
4559
|
+
this.calculateNode(childRef.ref, modalX, currentY, innerWidth, childHeight, "modal");
|
|
4560
|
+
const resolvedChildHeight = this.result[childRef.ref]?.height ?? childHeight;
|
|
4561
|
+
currentY += resolvedChildHeight;
|
|
4562
|
+
childrenHeight += resolvedChildHeight;
|
|
4563
|
+
if (index < node.children.length - 1) {
|
|
4564
|
+
currentY += 8;
|
|
4565
|
+
childrenHeight += 8;
|
|
4566
|
+
}
|
|
4567
|
+
});
|
|
4568
|
+
const modalHeight = headerHeight + childrenHeight;
|
|
4569
|
+
this.result[nodeId] = { x: modalX, y: modalY, width: modalWidth, height: modalHeight };
|
|
4570
|
+
}
|
|
3524
4571
|
/**
|
|
3525
4572
|
* Calculate layout for an instance node.
|
|
3526
4573
|
* The instance is a transparent wrapper — its bounding box equals the
|
|
@@ -3751,8 +4798,7 @@ var LayoutEngine = class {
|
|
|
3751
4798
|
return Math.max(this.getComponentHeight(), wrappedHeight);
|
|
3752
4799
|
}
|
|
3753
4800
|
if (node.componentType === "SidebarMenu") {
|
|
3754
|
-
const
|
|
3755
|
-
const items = itemsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4801
|
+
const items = toStringArray(node.props.items);
|
|
3756
4802
|
const itemCount = items.length > 0 ? items.length : 3;
|
|
3757
4803
|
const itemHeight = 40;
|
|
3758
4804
|
return Math.max(this.getComponentHeight(), itemCount * itemHeight);
|
|
@@ -3763,7 +4809,7 @@ var LayoutEngine = class {
|
|
|
3763
4809
|
if (node.componentType === "Stat") return 120;
|
|
3764
4810
|
if (node.componentType === "Chart") return 250;
|
|
3765
4811
|
if (node.componentType === "List") {
|
|
3766
|
-
const itemsFromProps =
|
|
4812
|
+
const itemsFromProps = toStringArray(node.props.items);
|
|
3767
4813
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
3768
4814
|
const fallbackCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
3769
4815
|
const itemCount = itemsFromProps.length > 0 ? itemsFromProps.length : fallbackCount;
|
|
@@ -3941,6 +4987,17 @@ var LayoutEngine = class {
|
|
|
3941
4987
|
}
|
|
3942
4988
|
return width;
|
|
3943
4989
|
}
|
|
4990
|
+
/**
|
|
4991
|
+
* Returns false only when the node has an explicit `visible: 'false'` param/prop.
|
|
4992
|
+
* Missing or any other value is treated as visible.
|
|
4993
|
+
*/
|
|
4994
|
+
isNodeVisible(nodeId) {
|
|
4995
|
+
const node = this.nodes[nodeId];
|
|
4996
|
+
if (!node) return true;
|
|
4997
|
+
if (node.kind === "component") return String(node.props.visible) !== "false";
|
|
4998
|
+
if (node.kind === "container") return String(node.params.visible) !== "false";
|
|
4999
|
+
return true;
|
|
5000
|
+
}
|
|
3944
5001
|
parseBooleanProp(value, fallback = false) {
|
|
3945
5002
|
if (typeof value === "boolean") return value;
|
|
3946
5003
|
if (typeof value === "string") {
|
|
@@ -4031,7 +5088,7 @@ var MockDataGenerator = class {
|
|
|
4031
5088
|
for (const [key, rawValues] of Object.entries(mocks)) {
|
|
4032
5089
|
let values = [];
|
|
4033
5090
|
if (typeof rawValues === "string") {
|
|
4034
|
-
values = rawValues
|
|
5091
|
+
values = toStringArray(rawValues);
|
|
4035
5092
|
} else if (Array.isArray(rawValues)) {
|
|
4036
5093
|
values = rawValues.map((v) => typeof v === "string" ? v.trim() : "").filter((v) => v.length > 0);
|
|
4037
5094
|
}
|
|
@@ -4123,7 +5180,7 @@ var MockDataGenerator = class {
|
|
|
4123
5180
|
* columns: "id,name,status,amount"
|
|
4124
5181
|
*/
|
|
4125
5182
|
static generateMockRow(columns, rowIndex, random = false) {
|
|
4126
|
-
const columnNames = columns
|
|
5183
|
+
const columnNames = toStringArray(columns);
|
|
4127
5184
|
const row = {};
|
|
4128
5185
|
columnNames.forEach((col) => {
|
|
4129
5186
|
const mockType = this.inferMockTypeFromColumn(col);
|
|
@@ -5175,6 +6232,9 @@ var SVGRenderer = class {
|
|
|
5175
6232
|
if (!node || !pos) return;
|
|
5176
6233
|
this.renderedNodeIds.add(nodeId);
|
|
5177
6234
|
if (node.kind === "container") {
|
|
6235
|
+
if (String(node.params.visible) === "false") {
|
|
6236
|
+
return;
|
|
6237
|
+
}
|
|
5178
6238
|
const containerGroup = [];
|
|
5179
6239
|
const hasNodeId = node.meta?.nodeId;
|
|
5180
6240
|
if (hasNodeId) {
|
|
@@ -5195,6 +6255,12 @@ var SVGRenderer = class {
|
|
|
5195
6255
|
if (node.containerType === "split") {
|
|
5196
6256
|
this.renderSplitDecoration(node, pos, containerGroup);
|
|
5197
6257
|
}
|
|
6258
|
+
if (node.containerType === "modal") {
|
|
6259
|
+
this.renderModalDecoration(node, pos, containerGroup);
|
|
6260
|
+
}
|
|
6261
|
+
if (node.containerType === "modal-footer") {
|
|
6262
|
+
this.renderModalFooterDecoration(pos, containerGroup);
|
|
6263
|
+
}
|
|
5198
6264
|
const isCellContainer = node.meta?.source === "cell";
|
|
5199
6265
|
if (node.children.length === 0 && this.options.showDiagnostics && !isCellContainer) {
|
|
5200
6266
|
containerGroup.push(this.renderEmptyContainerDiagnostic(pos, node.containerType));
|
|
@@ -5221,6 +6287,9 @@ var SVGRenderer = class {
|
|
|
5221
6287
|
}
|
|
5222
6288
|
output.push(...instanceGroup);
|
|
5223
6289
|
} else if (node.kind === "component") {
|
|
6290
|
+
if (String(node.props.visible) === "false") {
|
|
6291
|
+
return;
|
|
6292
|
+
}
|
|
5224
6293
|
const componentSvg = this.renderComponent(node, pos);
|
|
5225
6294
|
if (componentSvg) {
|
|
5226
6295
|
output.push(componentSvg);
|
|
@@ -5282,8 +6351,6 @@ var SVGRenderer = class {
|
|
|
5282
6351
|
return this.renderAlert(node, pos);
|
|
5283
6352
|
case "Badge":
|
|
5284
6353
|
return this.renderBadge(node, pos);
|
|
5285
|
-
case "Modal":
|
|
5286
|
-
return this.renderModal(node, pos);
|
|
5287
6354
|
case "List":
|
|
5288
6355
|
return this.renderList(node, pos);
|
|
5289
6356
|
case "Stat":
|
|
@@ -5726,8 +6793,8 @@ var SVGRenderer = class {
|
|
|
5726
6793
|
}
|
|
5727
6794
|
renderTable(node, pos) {
|
|
5728
6795
|
const title = String(node.props.title || "");
|
|
5729
|
-
const
|
|
5730
|
-
const columns =
|
|
6796
|
+
const parsedColumns = toStringArray(node.props.columns);
|
|
6797
|
+
const columns = parsedColumns.length > 0 ? parsedColumns : ["Col1", "Col2", "Col3"];
|
|
5731
6798
|
const rowCount = Number(node.props.rows || node.props.rowsMock || 5);
|
|
5732
6799
|
const mockStr = String(node.props.mock || "");
|
|
5733
6800
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
@@ -5735,7 +6802,7 @@ var SVGRenderer = class {
|
|
|
5735
6802
|
const parsedPageCount = Number(node.props.pages || 5);
|
|
5736
6803
|
const pageCount = Number.isFinite(parsedPageCount) && parsedPageCount > 0 ? Math.floor(parsedPageCount) : 5;
|
|
5737
6804
|
const paginationAlign = String(node.props.paginationAlign || "right");
|
|
5738
|
-
const actions =
|
|
6805
|
+
const actions = toStringArray(node.props.actions);
|
|
5739
6806
|
const hasActions = actions.length > 0;
|
|
5740
6807
|
const caption = String(node.props.caption || "").trim();
|
|
5741
6808
|
const hasCaption = caption.length > 0;
|
|
@@ -5748,7 +6815,7 @@ var SVGRenderer = class {
|
|
|
5748
6815
|
const rawCaptionAlign = String(node.props.captionAlign || "");
|
|
5749
6816
|
const captionAlign = rawCaptionAlign === "left" || rawCaptionAlign === "center" || rawCaptionAlign === "right" ? rawCaptionAlign : paginationAlign === "left" ? "right" : "left";
|
|
5750
6817
|
const sameFooterAlign = hasCaption && pagination && captionAlign === paginationAlign;
|
|
5751
|
-
const mockTypes = mockStr
|
|
6818
|
+
const mockTypes = toStringArray(mockStr);
|
|
5752
6819
|
const safeColumns = columns.length > 0 ? columns : ["Column"];
|
|
5753
6820
|
while (mockTypes.length < safeColumns.length) {
|
|
5754
6821
|
const inferred = MockDataGenerator.inferMockTypeFromColumn(safeColumns[mockTypes.length] || "item");
|
|
@@ -5848,6 +6915,13 @@ var SVGRenderer = class {
|
|
|
5848
6915
|
currentX += buttonSize + buttonGap;
|
|
5849
6916
|
});
|
|
5850
6917
|
}
|
|
6918
|
+
const rowEventAttrs = this.getScopedEventAttrs(node, "onRowClick", { index: rowIdx });
|
|
6919
|
+
if (rowEventAttrs) {
|
|
6920
|
+
svg += `
|
|
6921
|
+
<rect x="${pos.x}" y="${rowY}"
|
|
6922
|
+
width="${pos.width}" height="${rowHeight}"
|
|
6923
|
+
fill="transparent" stroke="none" pointer-events="all"${rowEventAttrs}/>`;
|
|
6924
|
+
}
|
|
5851
6925
|
});
|
|
5852
6926
|
const footerTop = headerY + headerHeight + mockRows.length * rowHeight + 16;
|
|
5853
6927
|
if (pagination) {
|
|
@@ -6237,14 +7311,15 @@ var SVGRenderer = class {
|
|
|
6237
7311
|
const label = String(node.props.label || "Checkbox");
|
|
6238
7312
|
const checked = String(node.props.checked || "false").toLowerCase() === "true";
|
|
6239
7313
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7314
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6240
7315
|
const controlColor = this.resolveControlColor();
|
|
6241
7316
|
const checkboxSize = 18;
|
|
6242
7317
|
const checkboxY = pos.y + pos.height / 2 - checkboxSize / 2;
|
|
6243
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6244
|
-
<rect x="${pos.x}" y="${checkboxY}"
|
|
6245
|
-
width="${checkboxSize}" height="${checkboxSize}"
|
|
6246
|
-
rx="4"
|
|
6247
|
-
fill="${checked ? controlColor : this.renderTheme.cardBg}"
|
|
7318
|
+
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}${!clickable ? ' data-clickable="false"' : ""}>
|
|
7319
|
+
<rect x="${pos.x}" y="${checkboxY}"
|
|
7320
|
+
width="${checkboxSize}" height="${checkboxSize}"
|
|
7321
|
+
rx="4"
|
|
7322
|
+
fill="${checked ? controlColor : this.renderTheme.cardBg}"
|
|
6248
7323
|
stroke="${this.renderTheme.border}"
|
|
6249
7324
|
stroke-width="1"/>
|
|
6250
7325
|
${checked ? `<text x="${pos.x + checkboxSize / 2}" y="${checkboxY + 14}"
|
|
@@ -6262,13 +7337,14 @@ var SVGRenderer = class {
|
|
|
6262
7337
|
const label = String(node.props.label || "Radio");
|
|
6263
7338
|
const checked = String(node.props.checked || "false").toLowerCase() === "true";
|
|
6264
7339
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7340
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6265
7341
|
const controlColor = this.resolveControlColor();
|
|
6266
7342
|
const radioSize = 16;
|
|
6267
7343
|
const radioY = pos.y + pos.height / 2 - radioSize / 2;
|
|
6268
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6269
|
-
<circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
|
|
6270
|
-
r="${radioSize / 2}"
|
|
6271
|
-
fill="${this.renderTheme.cardBg}"
|
|
7344
|
+
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}${!clickable ? ' data-clickable="false"' : ""}>
|
|
7345
|
+
<circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
|
|
7346
|
+
r="${radioSize / 2}"
|
|
7347
|
+
fill="${this.renderTheme.cardBg}"
|
|
6272
7348
|
stroke="${this.renderTheme.border}"
|
|
6273
7349
|
stroke-width="1"/>
|
|
6274
7350
|
${checked ? `<circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
|
|
@@ -6284,12 +7360,13 @@ var SVGRenderer = class {
|
|
|
6284
7360
|
const label = String(node.props.label || "Toggle");
|
|
6285
7361
|
const enabled = String(node.props.enabled || "false").toLowerCase() === "true";
|
|
6286
7362
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7363
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6287
7364
|
const controlColor = this.resolveControlColor();
|
|
6288
7365
|
const toggleWidth = 40;
|
|
6289
7366
|
const toggleHeight = 20;
|
|
6290
7367
|
const toggleY = pos.y + pos.height / 2 - toggleHeight / 2;
|
|
6291
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6292
|
-
<rect x="${pos.x}" y="${toggleY}"
|
|
7368
|
+
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}${!clickable ? ' data-clickable="false"' : ""}>
|
|
7369
|
+
<rect x="${pos.x}" y="${toggleY}"
|
|
6293
7370
|
width="${toggleWidth}" height="${toggleHeight}"
|
|
6294
7371
|
rx="10"
|
|
6295
7372
|
fill="${enabled ? controlColor : this.renderTheme.border}"
|
|
@@ -6308,12 +7385,9 @@ var SVGRenderer = class {
|
|
|
6308
7385
|
// ============================================================================
|
|
6309
7386
|
renderSidebar(node, pos) {
|
|
6310
7387
|
const title = String(node.props.title || "Sidebar");
|
|
6311
|
-
const itemsStr = String(node.props.items || "");
|
|
6312
7388
|
const activeItem = String(node.props.active || "");
|
|
6313
|
-
let items =
|
|
6314
|
-
if (
|
|
6315
|
-
items = itemsStr.split(",").map((i) => i.trim());
|
|
6316
|
-
} else {
|
|
7389
|
+
let items = toStringArray(node.props.items);
|
|
7390
|
+
if (items.length === 0) {
|
|
6317
7391
|
const itemCount = Number(node.props.itemsMock || 6);
|
|
6318
7392
|
items = MockDataGenerator.generateMockList("name", itemCount);
|
|
6319
7393
|
}
|
|
@@ -6352,9 +7426,9 @@ var SVGRenderer = class {
|
|
|
6352
7426
|
return svg;
|
|
6353
7427
|
}
|
|
6354
7428
|
renderTabs(node, pos) {
|
|
6355
|
-
const
|
|
6356
|
-
const tabs =
|
|
6357
|
-
const activeProp = node.props.active ?? 0;
|
|
7429
|
+
const parsedTabs = toStringArray(node.props.items);
|
|
7430
|
+
const tabs = parsedTabs.length > 0 ? parsedTabs : ["Tab 1", "Tab 2", "Tab 3"];
|
|
7431
|
+
const activeProp = node.props.active ?? node.props.initialActive ?? 0;
|
|
6358
7432
|
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
6359
7433
|
const variant = String(node.props.variant || "default");
|
|
6360
7434
|
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
@@ -6364,8 +7438,7 @@ var SVGRenderer = class {
|
|
|
6364
7438
|
const tabHeight = pos.height > 0 ? pos.height : sizeMap[String(node.props.size || "md")] ?? 44;
|
|
6365
7439
|
const fontSize = 13;
|
|
6366
7440
|
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
6367
|
-
const
|
|
6368
|
-
const iconList = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
7441
|
+
const iconList = toStringArray(node.props.icons);
|
|
6369
7442
|
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
6370
7443
|
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
6371
7444
|
const tabWidth = pos.width / tabs.length;
|
|
@@ -6469,6 +7542,13 @@ var SVGRenderer = class {
|
|
|
6469
7542
|
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
6470
7543
|
}
|
|
6471
7544
|
}
|
|
7545
|
+
const tabsTriggerAttrs = this.getTabsTriggerAttrs(node, i);
|
|
7546
|
+
if (tabsTriggerAttrs) {
|
|
7547
|
+
svg += `
|
|
7548
|
+
<rect x="${tabX}" y="${pos.y}"
|
|
7549
|
+
width="${tabWidth}" height="${tabHeight}"
|
|
7550
|
+
fill="transparent" stroke="none" pointer-events="all"${tabsTriggerAttrs}/>`;
|
|
7551
|
+
}
|
|
6472
7552
|
});
|
|
6473
7553
|
const contentY = pos.y + tabHeight;
|
|
6474
7554
|
const contentH = pos.height - tabHeight;
|
|
@@ -6582,66 +7662,51 @@ var SVGRenderer = class {
|
|
|
6582
7662
|
text-anchor="middle">${this.escapeXml(text)}</text>
|
|
6583
7663
|
</g>`;
|
|
6584
7664
|
}
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
}
|
|
6590
|
-
const title = String(node.props.title || "Modal");
|
|
7665
|
+
renderModalDecoration(node, pos, output) {
|
|
7666
|
+
if (node.kind !== "container") return;
|
|
7667
|
+
const canvasWidth = this.options.width;
|
|
7668
|
+
const canvasHeight = Math.max(this.options.height, this.calculateContentHeight());
|
|
6591
7669
|
const padding = 16;
|
|
6592
7670
|
const headerHeight = 48;
|
|
6593
|
-
const
|
|
6594
|
-
const
|
|
6595
|
-
const
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
font-size="18"
|
|
6626
|
-
fill="${this.renderTheme.textMuted}">\u2715</text>
|
|
6627
|
-
|
|
6628
|
-
<!-- Content placeholder -->
|
|
6629
|
-
<text x="${modalX + pos.width / 2}" y="${modalY + headerHeight + (pos.height - headerHeight) / 2}"
|
|
6630
|
-
font-family="Arial, Helvetica, sans-serif"
|
|
6631
|
-
font-size="13"
|
|
6632
|
-
fill="${this.renderTheme.textMuted}"
|
|
6633
|
-
text-anchor="middle">Modal content</text>
|
|
6634
|
-
</g>`;
|
|
7671
|
+
const hasTitle = node.params.title !== void 0 && node.params.title !== "";
|
|
7672
|
+
const closable = node.params.closable !== "false" && node.params.closable !== 0;
|
|
7673
|
+
const title = hasTitle ? String(node.params.title) : "";
|
|
7674
|
+
output.push(
|
|
7675
|
+
`<rect x="0" y="0" width="${canvasWidth}" height="${canvasHeight}" fill="black" opacity="0.28" pointer-events="none"/>`
|
|
7676
|
+
);
|
|
7677
|
+
output.push(
|
|
7678
|
+
`<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" rx="8" fill="${this.renderTheme.cardBg}" stroke="${this.renderTheme.border}" stroke-width="1"/>`
|
|
7679
|
+
);
|
|
7680
|
+
if (hasTitle) {
|
|
7681
|
+
output.push(
|
|
7682
|
+
`<line x1="${pos.x}" y1="${pos.y + headerHeight}" x2="${pos.x + pos.width}" y2="${pos.y + headerHeight}" stroke="${this.renderTheme.border}" stroke-width="1"/>`
|
|
7683
|
+
);
|
|
7684
|
+
output.push(
|
|
7685
|
+
`<text x="${pos.x + padding}" y="${pos.y + padding + 15}" font-family="Arial, Helvetica, sans-serif" font-size="15" font-weight="600" fill="${this.renderTheme.text}">${this.escapeXml(title)}</text>`
|
|
7686
|
+
);
|
|
7687
|
+
if (closable) {
|
|
7688
|
+
const events = node.events?.find((e) => e.event === "onClose");
|
|
7689
|
+
const closeEventAttr = events ? this.serializeEventHandler(events) : "";
|
|
7690
|
+
const closeX = pos.x + pos.width - padding - 14;
|
|
7691
|
+
const closeY = pos.y + padding + 14;
|
|
7692
|
+
output.push(
|
|
7693
|
+
`<rect x="${closeX - 12}" y="${closeY - 12}" width="24" height="24" rx="4" fill="transparent" stroke="none" pointer-events="all"${closeEventAttr}/>`,
|
|
7694
|
+
`<text x="${closeX}" y="${closeY}" font-family="Arial, Helvetica, sans-serif" font-size="16" fill="${this.renderTheme.textMuted}" text-anchor="middle" dominant-baseline="central" pointer-events="none">\u2715</text>`
|
|
7695
|
+
);
|
|
7696
|
+
}
|
|
7697
|
+
}
|
|
7698
|
+
}
|
|
7699
|
+
renderModalFooterDecoration(pos, output) {
|
|
7700
|
+
output.push(
|
|
7701
|
+
`<line x1="${pos.x}" y1="${pos.y}" x2="${pos.x + pos.width}" y2="${pos.y}" stroke="${this.renderTheme.border}" stroke-width="1"/>`
|
|
7702
|
+
);
|
|
6635
7703
|
}
|
|
6636
7704
|
renderList(node, pos) {
|
|
6637
7705
|
const title = String(node.props.title || "");
|
|
6638
|
-
const itemsStr = String(node.props.items || "");
|
|
6639
7706
|
const mockType = String(node.props.mock || "").trim();
|
|
6640
7707
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
6641
|
-
let items =
|
|
6642
|
-
if (
|
|
6643
|
-
items = itemsStr.split(",").map((i) => i.trim()).filter(Boolean);
|
|
6644
|
-
} else {
|
|
7708
|
+
let items = toStringArray(node.props.items);
|
|
7709
|
+
if (items.length === 0) {
|
|
6645
7710
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
6646
7711
|
const itemCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
6647
7712
|
const resolvedMockType = mockType || "name";
|
|
@@ -6670,6 +7735,7 @@ var SVGRenderer = class {
|
|
|
6670
7735
|
items.forEach((item, i) => {
|
|
6671
7736
|
const itemY = pos.y + titleHeight + i * itemHeight;
|
|
6672
7737
|
if (itemY + itemHeight <= pos.y + pos.height) {
|
|
7738
|
+
const itemEventAttrs = this.getScopedEventAttrs(node, "onItemClick", { index: i });
|
|
6673
7739
|
svg += `
|
|
6674
7740
|
<line x1="${pos.x}" y1="${itemY + itemHeight}"
|
|
6675
7741
|
x2="${pos.x + pos.width}" y2="${itemY + itemHeight}"
|
|
@@ -6678,7 +7744,10 @@ var SVGRenderer = class {
|
|
|
6678
7744
|
<text x="${pos.x + padding}" y="${itemY + 24}"
|
|
6679
7745
|
font-family="Arial, Helvetica, sans-serif"
|
|
6680
7746
|
font-size="13"
|
|
6681
|
-
fill="${this.renderTheme.text}">${this.escapeXml(item)}</text
|
|
7747
|
+
fill="${this.renderTheme.text}">${this.escapeXml(item)}</text>
|
|
7748
|
+
${itemEventAttrs ? `<rect x="${pos.x}" y="${itemY}"
|
|
7749
|
+
width="${pos.width}" height="${itemHeight}"
|
|
7750
|
+
fill="transparent" stroke="none" pointer-events="all"${itemEventAttrs}/>` : ""}`;
|
|
6682
7751
|
}
|
|
6683
7752
|
});
|
|
6684
7753
|
svg += "\n </g>";
|
|
@@ -6912,8 +7981,8 @@ var SVGRenderer = class {
|
|
|
6912
7981
|
return svg;
|
|
6913
7982
|
}
|
|
6914
7983
|
renderBreadcrumbs(node, pos) {
|
|
6915
|
-
const
|
|
6916
|
-
const items =
|
|
7984
|
+
const parsedBreadcrumbs = toStringArray(node.props.items);
|
|
7985
|
+
const items = parsedBreadcrumbs.length > 0 ? parsedBreadcrumbs : ["Home"];
|
|
6917
7986
|
const separator = String(node.props.separator || "/");
|
|
6918
7987
|
const fontSize = 12;
|
|
6919
7988
|
const separatorWidth = 20;
|
|
@@ -6945,10 +8014,9 @@ var SVGRenderer = class {
|
|
|
6945
8014
|
return svg;
|
|
6946
8015
|
}
|
|
6947
8016
|
renderSidebarMenu(node, pos) {
|
|
6948
|
-
const
|
|
6949
|
-
const
|
|
6950
|
-
const
|
|
6951
|
-
const icons = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
8017
|
+
const parsedMenuItems = toStringArray(node.props.items);
|
|
8018
|
+
const items = parsedMenuItems.length > 0 ? parsedMenuItems : ["Item 1", "Item 2", "Item 3"];
|
|
8019
|
+
const icons = toStringArray(node.props.icons);
|
|
6952
8020
|
const itemHeight = 40;
|
|
6953
8021
|
const fontSize = 14;
|
|
6954
8022
|
const activeIndex = Number(node.props.active || 0);
|
|
@@ -6993,6 +8061,13 @@ var SVGRenderer = class {
|
|
|
6993
8061
|
font-size="${fontSize}"
|
|
6994
8062
|
font-weight="${fontWeight}"
|
|
6995
8063
|
fill="${textColor}">${this.escapeXml(item)}</text>`;
|
|
8064
|
+
const itemEventAttrs = this.getScopedEventAttrs(node, "onItemsClick", { index });
|
|
8065
|
+
if (itemEventAttrs) {
|
|
8066
|
+
svg += `
|
|
8067
|
+
<rect x="${pos.x}" y="${itemY}"
|
|
8068
|
+
width="${pos.width}" height="${itemHeight}"
|
|
8069
|
+
fill="transparent" stroke="none" pointer-events="all"${itemEventAttrs}/>`;
|
|
8070
|
+
}
|
|
6996
8071
|
});
|
|
6997
8072
|
svg += "\n </g>";
|
|
6998
8073
|
return svg;
|
|
@@ -7343,7 +8418,7 @@ var SVGRenderer = class {
|
|
|
7343
8418
|
userBadge = { x, y, width, height, label: userLabel };
|
|
7344
8419
|
rightCursor = x - 8;
|
|
7345
8420
|
}
|
|
7346
|
-
const actionLabels = actions
|
|
8421
|
+
const actionLabels = toStringArray(actions);
|
|
7347
8422
|
const actionHeight = 32;
|
|
7348
8423
|
const actionY = pos.y + (pos.height - actionHeight) / 2;
|
|
7349
8424
|
const actionGap = 8;
|
|
@@ -7429,11 +8504,99 @@ var SVGRenderer = class {
|
|
|
7429
8504
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
7430
8505
|
}
|
|
7431
8506
|
/**
|
|
7432
|
-
* Get data-node-id
|
|
7433
|
-
* Enables bidirectional selection between code and canvas
|
|
8507
|
+
* Get data-node-id and event data attributes for SVG elements.
|
|
8508
|
+
* Enables bidirectional selection between code and canvas (data-node-id)
|
|
8509
|
+
* and play test interactivity (data-event-*, data-user-id, data-tabs-id).
|
|
7434
8510
|
*/
|
|
7435
8511
|
getDataNodeId(node) {
|
|
7436
|
-
|
|
8512
|
+
let attrs = "";
|
|
8513
|
+
if (node.meta.nodeId) {
|
|
8514
|
+
attrs += ` data-node-id="${node.meta.nodeId}"`;
|
|
8515
|
+
}
|
|
8516
|
+
if (node.kind === "component") {
|
|
8517
|
+
if (node.userDefinedId) {
|
|
8518
|
+
attrs += ` data-user-id="${node.userDefinedId}"`;
|
|
8519
|
+
}
|
|
8520
|
+
if (node.events && node.events.length > 0) {
|
|
8521
|
+
for (const handler of node.events) {
|
|
8522
|
+
if (!this.isScopedEvent(handler.event)) {
|
|
8523
|
+
attrs += this.serializeEventHandler(handler);
|
|
8524
|
+
}
|
|
8525
|
+
}
|
|
8526
|
+
}
|
|
8527
|
+
}
|
|
8528
|
+
if (node.kind === "container") {
|
|
8529
|
+
if (node.containerType === "tabs" && node.params.id) {
|
|
8530
|
+
attrs += ` data-tabs-id="${node.params.id}"`;
|
|
8531
|
+
if (node.params.active !== void 0) {
|
|
8532
|
+
attrs += ` data-tabs-active="${node.params.active}"`;
|
|
8533
|
+
}
|
|
8534
|
+
}
|
|
8535
|
+
if (node.events && node.events.length > 0) {
|
|
8536
|
+
for (const handler of node.events) {
|
|
8537
|
+
if (node.containerType === "modal" && handler.event === "onClose") continue;
|
|
8538
|
+
attrs += this.serializeEventHandler(handler);
|
|
8539
|
+
}
|
|
8540
|
+
}
|
|
8541
|
+
}
|
|
8542
|
+
return attrs;
|
|
8543
|
+
}
|
|
8544
|
+
isScopedEvent(event) {
|
|
8545
|
+
return event === "onClose" || event === "onItemClick" || event === "onRowClick" || event === "onItemsClick";
|
|
8546
|
+
}
|
|
8547
|
+
getScopedEventAttrs(node, eventName, options = {}) {
|
|
8548
|
+
const handler = node.events?.find((event) => event.event === eventName);
|
|
8549
|
+
if (!handler) return "";
|
|
8550
|
+
let attrs = "";
|
|
8551
|
+
if (node.meta.nodeId) {
|
|
8552
|
+
attrs += ` data-node-id="${node.meta.nodeId}"`;
|
|
8553
|
+
}
|
|
8554
|
+
if (node.userDefinedId) {
|
|
8555
|
+
attrs += ` data-user-id="${node.userDefinedId}"`;
|
|
8556
|
+
}
|
|
8557
|
+
attrs += this.serializeEventHandler(handler);
|
|
8558
|
+
if (options.index !== void 0) {
|
|
8559
|
+
attrs += ` data-event-index="${options.index}"`;
|
|
8560
|
+
}
|
|
8561
|
+
return attrs;
|
|
8562
|
+
}
|
|
8563
|
+
getTabsTriggerAttrs(node, index) {
|
|
8564
|
+
const tabsId = String(node.props.tabsId || "").trim();
|
|
8565
|
+
if (!tabsId) return "";
|
|
8566
|
+
let attrs = "";
|
|
8567
|
+
if (node.meta.nodeId) {
|
|
8568
|
+
attrs += ` data-node-id="${node.meta.nodeId}"`;
|
|
8569
|
+
}
|
|
8570
|
+
attrs += ` data-tabs-id="${tabsId}" data-tabs-trigger-index="${index}"`;
|
|
8571
|
+
return attrs;
|
|
8572
|
+
}
|
|
8573
|
+
serializeEventHandler(handler) {
|
|
8574
|
+
const attrName = this.eventNameToDataAttr(handler.event);
|
|
8575
|
+
const value = handler.actions.map((a) => this.serializeEventAction(a)).join("|");
|
|
8576
|
+
return ` data-event-${attrName}="${value}"`;
|
|
8577
|
+
}
|
|
8578
|
+
eventNameToDataAttr(event) {
|
|
8579
|
+
return event.replace(/^on/, "").toLowerCase();
|
|
8580
|
+
}
|
|
8581
|
+
serializeEventAction(action) {
|
|
8582
|
+
switch (action.type) {
|
|
8583
|
+
case "navigate":
|
|
8584
|
+
return `navigate:${action.screen}`;
|
|
8585
|
+
case "show":
|
|
8586
|
+
return `show:${action.targetId}`;
|
|
8587
|
+
case "hide":
|
|
8588
|
+
return `hide:${action.targetId}`;
|
|
8589
|
+
case "toggle":
|
|
8590
|
+
return `toggle:${action.targetId}`;
|
|
8591
|
+
case "enable":
|
|
8592
|
+
return `enable:${action.targetId}`;
|
|
8593
|
+
case "disable":
|
|
8594
|
+
return `disable:${action.targetId}`;
|
|
8595
|
+
case "setTab":
|
|
8596
|
+
return `setTab:${action.tabsId}:${action.index}`;
|
|
8597
|
+
case "navigateItems":
|
|
8598
|
+
return action.screens.map((s) => `navigate:${s}`).join(",");
|
|
8599
|
+
}
|
|
7437
8600
|
}
|
|
7438
8601
|
};
|
|
7439
8602
|
function renderToSVG(ir, layout, options) {
|
|
@@ -7523,8 +8686,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
7523
8686
|
* Render breadcrumbs as skeleton blocks: <rect> / <rect> / <rect accent>
|
|
7524
8687
|
*/
|
|
7525
8688
|
renderBreadcrumbs(node, pos) {
|
|
7526
|
-
const
|
|
7527
|
-
const items = itemsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
8689
|
+
const items = toStringArray(node.props.items || "Home");
|
|
7528
8690
|
const separator = String(node.props.separator || "/");
|
|
7529
8691
|
const blockColor = this.renderTheme.border;
|
|
7530
8692
|
const charWidth = 6.2;
|
|
@@ -7845,10 +9007,9 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
7845
9007
|
*/
|
|
7846
9008
|
renderTable(node, pos) {
|
|
7847
9009
|
const title = String(node.props.title || "");
|
|
7848
|
-
const
|
|
7849
|
-
const columns = columnsStr.split(",").map((c) => c.trim()).filter(Boolean);
|
|
9010
|
+
const columns = toStringArray(node.props.columns || "Col1,Col2,Col3");
|
|
7850
9011
|
const rowCount = Number(node.props.rows || node.props.rowsMock || 5);
|
|
7851
|
-
const actions =
|
|
9012
|
+
const actions = toStringArray(node.props.actions);
|
|
7852
9013
|
const hasActions = actions.length > 0;
|
|
7853
9014
|
const pagination = this.parseBooleanProp(node.props.pagination, false);
|
|
7854
9015
|
const parsedPageCount = Number(node.props.pages || 5);
|
|
@@ -8134,7 +9295,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
8134
9295
|
const itemsStr = String(node.props.items || "");
|
|
8135
9296
|
let items = [];
|
|
8136
9297
|
if (itemsStr) {
|
|
8137
|
-
items = itemsStr
|
|
9298
|
+
items = toStringArray(itemsStr);
|
|
8138
9299
|
} else {
|
|
8139
9300
|
const itemCount = Number(node.props.itemsMock || 6);
|
|
8140
9301
|
items = Array(itemCount).fill("Item");
|
|
@@ -8171,8 +9332,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
8171
9332
|
* Render SidebarMenu with gray blocks instead of text and no icons
|
|
8172
9333
|
*/
|
|
8173
9334
|
renderSidebarMenu(node, pos) {
|
|
8174
|
-
const
|
|
8175
|
-
const items = itemsStr.split(",").map((s) => s.trim());
|
|
9335
|
+
const items = toStringArray(node.props.items || "Item 1,Item 2,Item 3");
|
|
8176
9336
|
const itemHeight = 40;
|
|
8177
9337
|
const activeIndex = Number(node.props.active || 0);
|
|
8178
9338
|
const accentColor = this.resolveAccentColor();
|
|
@@ -9020,7 +10180,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9020
10180
|
const title = String(node.props.title || "Sidebar");
|
|
9021
10181
|
const itemsStr = String(node.props.items || "");
|
|
9022
10182
|
const activeItem = String(node.props.active || "");
|
|
9023
|
-
const
|
|
10183
|
+
const parsed = toStringArray(itemsStr);
|
|
10184
|
+
const items = parsed.length ? parsed : ["Item 1", "Item 2", "Item 3"];
|
|
9024
10185
|
const itemHeight = 40;
|
|
9025
10186
|
const padding = 16;
|
|
9026
10187
|
const titleHeight = 40;
|
|
@@ -9063,7 +10224,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9063
10224
|
*/
|
|
9064
10225
|
renderTabs(node, pos) {
|
|
9065
10226
|
const itemsStr = String(node.props.items || "");
|
|
9066
|
-
const
|
|
10227
|
+
const parsedTabs = toStringArray(itemsStr);
|
|
10228
|
+
const tabs = parsedTabs.length ? parsedTabs : ["Tab 1", "Tab 2", "Tab 3"];
|
|
9067
10229
|
const activeProp = node.props.active ?? 0;
|
|
9068
10230
|
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
9069
10231
|
const variant = String(node.props.variant || "default");
|
|
@@ -9075,7 +10237,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9075
10237
|
const fontSize = 13;
|
|
9076
10238
|
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
9077
10239
|
const iconsStr = String(node.props.icons || "");
|
|
9078
|
-
const iconList = iconsStr
|
|
10240
|
+
const iconList = toStringArray(iconsStr);
|
|
9079
10241
|
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
9080
10242
|
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
9081
10243
|
const tabWidth = pos.width / tabs.length;
|
|
@@ -9240,7 +10402,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9240
10402
|
<!-- Modal backdrop -->
|
|
9241
10403
|
<rect x="0" y="0"
|
|
9242
10404
|
width="${this.options.width}" height="${overlayHeight}"
|
|
9243
|
-
fill="black" opacity="0.28"/>
|
|
10405
|
+
fill="black" opacity="0.28" pointer-events="none"/>
|
|
9244
10406
|
|
|
9245
10407
|
<!-- Modal box -->
|
|
9246
10408
|
<rect x="${modalX}" y="${modalY}"
|
|
@@ -9268,7 +10430,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9268
10430
|
<text x="${modalX + pos.width - 16}" y="${modalY + padding + 12}"
|
|
9269
10431
|
font-family="${this.fontFamily}"
|
|
9270
10432
|
font-size="18"
|
|
9271
|
-
fill="${this.renderTheme.textMuted}"
|
|
10433
|
+
fill="${this.renderTheme.textMuted}"
|
|
10434
|
+
pointer-events="none">\u2715</text>
|
|
9272
10435
|
|
|
9273
10436
|
<!-- Content placeholder -->
|
|
9274
10437
|
<text x="${modalX + pos.width / 2}" y="${modalY + headerHeight + (pos.height - headerHeight) / 2}"
|
|
@@ -9286,10 +10449,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9286
10449
|
const itemsStr = String(node.props.items || "");
|
|
9287
10450
|
const mockType = String(node.props.mock || "").trim();
|
|
9288
10451
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
9289
|
-
let items =
|
|
9290
|
-
if (
|
|
9291
|
-
items = itemsStr.split(",").map((i) => i.trim()).filter(Boolean);
|
|
9292
|
-
} else {
|
|
10452
|
+
let items = toStringArray(itemsStr);
|
|
10453
|
+
if (!items.length) {
|
|
9293
10454
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
9294
10455
|
const itemCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
9295
10456
|
const resolvedMockType = mockType || "name";
|
|
@@ -9503,7 +10664,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9503
10664
|
*/
|
|
9504
10665
|
renderBreadcrumbs(node, pos) {
|
|
9505
10666
|
const itemsStr = String(node.props.items || "Home");
|
|
9506
|
-
const items = itemsStr
|
|
10667
|
+
const items = toStringArray(itemsStr);
|
|
9507
10668
|
const separator = String(node.props.separator || "/");
|
|
9508
10669
|
const fontSize = 12;
|
|
9509
10670
|
const separatorWidth = 20;
|
|
@@ -9540,8 +10701,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9540
10701
|
renderSidebarMenu(node, pos) {
|
|
9541
10702
|
const itemsStr = String(node.props.items || "Item 1,Item 2,Item 3");
|
|
9542
10703
|
const iconsStr = String(node.props.icons || "");
|
|
9543
|
-
const items = itemsStr
|
|
9544
|
-
const icons = iconsStr
|
|
10704
|
+
const items = toStringArray(itemsStr);
|
|
10705
|
+
const icons = toStringArray(iconsStr);
|
|
9545
10706
|
const itemHeight = 40;
|
|
9546
10707
|
const fontSize = 14;
|
|
9547
10708
|
const activeIndex = Number(node.props.active || 0);
|
|
@@ -9862,11 +11023,13 @@ var version = "0.0.1";
|
|
|
9862
11023
|
DEVICE_PRESETS,
|
|
9863
11024
|
IRGenerator,
|
|
9864
11025
|
LayoutEngine,
|
|
11026
|
+
SELF_TARGET,
|
|
9865
11027
|
SVGRenderer,
|
|
9866
11028
|
SkeletonSVGRenderer,
|
|
9867
11029
|
SketchSVGRenderer,
|
|
9868
11030
|
SourceMapBuilder,
|
|
9869
11031
|
SourceMapResolver,
|
|
11032
|
+
applyStateChange,
|
|
9870
11033
|
buildSVG,
|
|
9871
11034
|
calculateLayout,
|
|
9872
11035
|
createSVGElement,
|