@wire-dsl/engine 0.9.1 → 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 +1369 -207
- package/dist/index.d.cts +211 -14
- package/dist/index.d.ts +211 -14
- package/dist/index.js +1367 -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,9 +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;
|
|
3108
|
-
if ((isVerticalStack || isHorizontalStack || node.containerType === "card") && node.children.length > 0) {
|
|
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) {
|
|
3109
4025
|
let containerMaxY = y;
|
|
3110
4026
|
node.children.forEach((childRef) => {
|
|
3111
4027
|
const childPos = this.result[childRef.ref];
|
|
@@ -3125,8 +4041,22 @@ var LayoutEngine = class {
|
|
|
3125
4041
|
const children = node.children;
|
|
3126
4042
|
if (direction === "vertical") {
|
|
3127
4043
|
let currentY = y;
|
|
3128
|
-
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) => {
|
|
3129
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
|
+
}
|
|
3130
4060
|
let childHeight = this.getComponentHeight();
|
|
3131
4061
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
3132
4062
|
childHeight = Number(childNode.props.height);
|
|
@@ -3137,12 +4067,17 @@ var LayoutEngine = class {
|
|
|
3137
4067
|
}
|
|
3138
4068
|
this.calculateNode(childRef.ref, x, currentY, width, childHeight, "stack");
|
|
3139
4069
|
currentY += childHeight;
|
|
3140
|
-
|
|
4070
|
+
flowCount++;
|
|
4071
|
+
if (flowCount < flowChildren.length) {
|
|
3141
4072
|
currentY += gap;
|
|
3142
4073
|
}
|
|
3143
4074
|
});
|
|
3144
4075
|
let adjustedY = y;
|
|
3145
|
-
|
|
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;
|
|
3146
4081
|
const childPos = this.result[childRef.ref];
|
|
3147
4082
|
if (childPos) {
|
|
3148
4083
|
const deltaY = adjustedY - childPos.y;
|
|
@@ -3151,7 +4086,8 @@ var LayoutEngine = class {
|
|
|
3151
4086
|
this.adjustNodeYPositions(childRef.ref, deltaY);
|
|
3152
4087
|
}
|
|
3153
4088
|
adjustedY += childPos.height;
|
|
3154
|
-
|
|
4089
|
+
adjustedCount++;
|
|
4090
|
+
if (adjustedCount < flowChildren.length) {
|
|
3155
4091
|
adjustedY += gap;
|
|
3156
4092
|
}
|
|
3157
4093
|
}
|
|
@@ -3161,9 +4097,10 @@ var LayoutEngine = class {
|
|
|
3161
4097
|
const crossAlign = node.style.align || "start";
|
|
3162
4098
|
if (justify === "stretch") {
|
|
3163
4099
|
let currentX = x;
|
|
3164
|
-
const
|
|
4100
|
+
const visibleChildren = children.filter((cr) => this.isNodeVisible(cr.ref));
|
|
4101
|
+
const childWidth = this.calculateChildWidth(visibleChildren.length, width, gap);
|
|
3165
4102
|
let stackHeight = 0;
|
|
3166
|
-
|
|
4103
|
+
visibleChildren.forEach((childRef) => {
|
|
3167
4104
|
const childNode = this.nodes[childRef.ref];
|
|
3168
4105
|
let childHeight = this.getComponentHeight();
|
|
3169
4106
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
@@ -3176,6 +4113,10 @@ var LayoutEngine = class {
|
|
|
3176
4113
|
stackHeight = Math.max(stackHeight, childHeight);
|
|
3177
4114
|
});
|
|
3178
4115
|
children.forEach((childRef) => {
|
|
4116
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4117
|
+
this.calculateNode(childRef.ref, currentX, y, 0, 0, "stack");
|
|
4118
|
+
return;
|
|
4119
|
+
}
|
|
3179
4120
|
this.calculateNode(childRef.ref, currentX, y, childWidth, stackHeight, "stack");
|
|
3180
4121
|
currentX += childWidth + gap;
|
|
3181
4122
|
});
|
|
@@ -3184,9 +4125,18 @@ var LayoutEngine = class {
|
|
|
3184
4125
|
const childHeights = [];
|
|
3185
4126
|
const explicitHeightFlags = [];
|
|
3186
4127
|
const flexIndices = /* @__PURE__ */ new Set();
|
|
4128
|
+
const visibleFlags = [];
|
|
3187
4129
|
let stackHeight = 0;
|
|
3188
4130
|
children.forEach((childRef, index) => {
|
|
3189
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
|
+
}
|
|
3190
4140
|
const hasExplicitHeight = childNode?.kind === "component" && !!childNode.props.height;
|
|
3191
4141
|
const hasExplicitWidth = childNode?.kind === "component" && !!childNode.props.width;
|
|
3192
4142
|
const isBlockButton = childNode?.kind === "component" && childNode.componentType === "Button" && !hasExplicitWidth && this.parseBooleanProp(childNode.props.block, false);
|
|
@@ -3204,7 +4154,8 @@ var LayoutEngine = class {
|
|
|
3204
4154
|
childHeights.push(this.getComponentHeight());
|
|
3205
4155
|
explicitHeightFlags.push(hasExplicitHeight);
|
|
3206
4156
|
});
|
|
3207
|
-
const
|
|
4157
|
+
const visibleCount = visibleFlags.filter(Boolean).length;
|
|
4158
|
+
const totalGapWidth = gap * Math.max(0, visibleCount - 1);
|
|
3208
4159
|
if (flexIndices.size > 0) {
|
|
3209
4160
|
const fixedWidth = childWidths.reduce((sum, w, idx) => {
|
|
3210
4161
|
return flexIndices.has(idx) ? sum : sum + w;
|
|
@@ -3216,6 +4167,7 @@ var LayoutEngine = class {
|
|
|
3216
4167
|
});
|
|
3217
4168
|
}
|
|
3218
4169
|
children.forEach((childRef, index) => {
|
|
4170
|
+
if (!visibleFlags[index]) return;
|
|
3219
4171
|
const childNode = this.nodes[childRef.ref];
|
|
3220
4172
|
const childWidth = childWidths[index];
|
|
3221
4173
|
let childHeight = this.getComponentHeight();
|
|
@@ -3239,14 +4191,18 @@ var LayoutEngine = class {
|
|
|
3239
4191
|
startX = x + width - totalContentWidth;
|
|
3240
4192
|
} else if (justify === "spaceBetween") {
|
|
3241
4193
|
startX = x;
|
|
3242
|
-
dynamicGap =
|
|
4194
|
+
dynamicGap = visibleCount > 1 ? (width - totalChildWidth) / (visibleCount - 1) : 0;
|
|
3243
4195
|
} else if (justify === "spaceAround") {
|
|
3244
|
-
const spacing =
|
|
4196
|
+
const spacing = visibleCount > 0 ? (width - totalChildWidth) / visibleCount : 0;
|
|
3245
4197
|
startX = x + spacing / 2;
|
|
3246
4198
|
dynamicGap = spacing;
|
|
3247
4199
|
}
|
|
3248
4200
|
let currentX = startX;
|
|
3249
4201
|
children.forEach((childRef, index) => {
|
|
4202
|
+
if (!visibleFlags[index]) {
|
|
4203
|
+
this.calculateNode(childRef.ref, currentX, y, 0, 0, "stack");
|
|
4204
|
+
return;
|
|
4205
|
+
}
|
|
3250
4206
|
const childWidth = childWidths[index];
|
|
3251
4207
|
const childHeight = childHeights[index];
|
|
3252
4208
|
let childY = y;
|
|
@@ -3278,6 +4234,7 @@ var LayoutEngine = class {
|
|
|
3278
4234
|
let currentRowMaxHeight = 0;
|
|
3279
4235
|
const rowHeights = [0];
|
|
3280
4236
|
node.children.forEach((childRef) => {
|
|
4237
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3281
4238
|
const child = this.nodes[childRef.ref];
|
|
3282
4239
|
let span = 1;
|
|
3283
4240
|
let childHeight = this.getComponentHeight();
|
|
@@ -3312,6 +4269,18 @@ var LayoutEngine = class {
|
|
|
3312
4269
|
}
|
|
3313
4270
|
return totalHeight;
|
|
3314
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
|
+
}
|
|
3315
4284
|
if (node.containerType === "split") {
|
|
3316
4285
|
const splitGap = this.resolveSpacing(node.style.gap);
|
|
3317
4286
|
const leftParam = node.params.left;
|
|
@@ -3324,6 +4293,7 @@ var LayoutEngine = class {
|
|
|
3324
4293
|
const rightWidth = Number.isFinite(rightWidthRaw) && rightWidthRaw > 0 ? rightWidthRaw : availableWidth / 2;
|
|
3325
4294
|
let maxHeight = 0;
|
|
3326
4295
|
node.children.forEach((childRef, index) => {
|
|
4296
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3327
4297
|
const child = this.nodes[childRef.ref];
|
|
3328
4298
|
let childHeight = this.getComponentHeight();
|
|
3329
4299
|
const isFirst = index === 0;
|
|
@@ -3351,6 +4321,7 @@ var LayoutEngine = class {
|
|
|
3351
4321
|
if (node.containerType === "stack" && direction === "horizontal") {
|
|
3352
4322
|
let maxHeight = 0;
|
|
3353
4323
|
node.children.forEach((childRef) => {
|
|
4324
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3354
4325
|
const child = this.nodes[childRef.ref];
|
|
3355
4326
|
let childHeight = this.getComponentHeight();
|
|
3356
4327
|
if (child?.kind === "component") {
|
|
@@ -3367,7 +4338,10 @@ var LayoutEngine = class {
|
|
|
3367
4338
|
totalHeight += maxHeight;
|
|
3368
4339
|
return totalHeight;
|
|
3369
4340
|
}
|
|
3370
|
-
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;
|
|
3371
4345
|
const child = this.nodes[childRef.ref];
|
|
3372
4346
|
let childHeight = this.getComponentHeight();
|
|
3373
4347
|
if (child?.kind === "component") {
|
|
@@ -3380,7 +4354,8 @@ var LayoutEngine = class {
|
|
|
3380
4354
|
childHeight = this.calculateContainerHeight(child, availableWidth);
|
|
3381
4355
|
}
|
|
3382
4356
|
totalHeight += childHeight;
|
|
3383
|
-
|
|
4357
|
+
linearIndex++;
|
|
4358
|
+
if (linearIndex < visibleLinear.length) {
|
|
3384
4359
|
totalHeight += gap;
|
|
3385
4360
|
}
|
|
3386
4361
|
});
|
|
@@ -3393,6 +4368,10 @@ var LayoutEngine = class {
|
|
|
3393
4368
|
const colWidth = (width - gap * (columns - 1)) / columns;
|
|
3394
4369
|
const cellHeights = {};
|
|
3395
4370
|
node.children.forEach((childRef, cellIndex) => {
|
|
4371
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4372
|
+
cellHeights[cellIndex] = 0;
|
|
4373
|
+
return;
|
|
4374
|
+
}
|
|
3396
4375
|
const child = this.nodes[childRef.ref];
|
|
3397
4376
|
let cellHeight = this.getComponentHeight();
|
|
3398
4377
|
let span = 1;
|
|
@@ -3417,6 +4396,11 @@ var LayoutEngine = class {
|
|
|
3417
4396
|
const rowHeights = [0];
|
|
3418
4397
|
const cellPositions = [];
|
|
3419
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
|
+
}
|
|
3420
4404
|
const child = this.nodes[childRef.ref];
|
|
3421
4405
|
let span = 1;
|
|
3422
4406
|
if (child?.kind === "container" && child.meta?.source === "cell") {
|
|
@@ -3428,13 +4412,17 @@ var LayoutEngine = class {
|
|
|
3428
4412
|
currentCol = 0;
|
|
3429
4413
|
currentRowMaxHeight = 0;
|
|
3430
4414
|
}
|
|
3431
|
-
cellPositions.push({ row: currentRow, col: currentCol, span });
|
|
4415
|
+
cellPositions.push({ row: currentRow, col: currentCol, span, visible: true });
|
|
3432
4416
|
currentRowMaxHeight = Math.max(currentRowMaxHeight, cellHeights[cellIndex]);
|
|
3433
4417
|
currentCol += span;
|
|
3434
4418
|
});
|
|
3435
4419
|
rowHeights[currentRow] = currentRowMaxHeight;
|
|
3436
4420
|
node.children.forEach((childRef, cellIndex) => {
|
|
3437
|
-
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
|
+
}
|
|
3438
4426
|
const cellHeight = rowHeights[row];
|
|
3439
4427
|
let cellY = y;
|
|
3440
4428
|
for (let r = 0; r < row; r++) {
|
|
@@ -3505,8 +4493,14 @@ var LayoutEngine = class {
|
|
|
3505
4493
|
const innerCardWidth = width - cardPadding * 2;
|
|
3506
4494
|
const children = node.children;
|
|
3507
4495
|
let currentY = y + cardPadding;
|
|
3508
|
-
children.
|
|
4496
|
+
const flowChildren = children.filter((cr) => this.isNodeVisible(cr.ref));
|
|
4497
|
+
let flowCount = 0;
|
|
4498
|
+
children.forEach((childRef) => {
|
|
3509
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
|
+
}
|
|
3510
4504
|
let childHeight = this.getComponentHeight();
|
|
3511
4505
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
3512
4506
|
childHeight = Number(childNode.props.height);
|
|
@@ -3517,11 +4511,63 @@ var LayoutEngine = class {
|
|
|
3517
4511
|
}
|
|
3518
4512
|
this.calculateNode(childRef.ref, x + cardPadding, currentY, innerCardWidth, childHeight, "card");
|
|
3519
4513
|
currentY += childHeight;
|
|
3520
|
-
|
|
4514
|
+
flowCount++;
|
|
4515
|
+
if (flowCount < flowChildren.length) {
|
|
3521
4516
|
currentY += gap;
|
|
3522
4517
|
}
|
|
3523
4518
|
});
|
|
3524
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
|
+
}
|
|
3525
4571
|
/**
|
|
3526
4572
|
* Calculate layout for an instance node.
|
|
3527
4573
|
* The instance is a transparent wrapper — its bounding box equals the
|
|
@@ -3752,8 +4798,7 @@ var LayoutEngine = class {
|
|
|
3752
4798
|
return Math.max(this.getComponentHeight(), wrappedHeight);
|
|
3753
4799
|
}
|
|
3754
4800
|
if (node.componentType === "SidebarMenu") {
|
|
3755
|
-
const
|
|
3756
|
-
const items = itemsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4801
|
+
const items = toStringArray(node.props.items);
|
|
3757
4802
|
const itemCount = items.length > 0 ? items.length : 3;
|
|
3758
4803
|
const itemHeight = 40;
|
|
3759
4804
|
return Math.max(this.getComponentHeight(), itemCount * itemHeight);
|
|
@@ -3764,7 +4809,7 @@ var LayoutEngine = class {
|
|
|
3764
4809
|
if (node.componentType === "Stat") return 120;
|
|
3765
4810
|
if (node.componentType === "Chart") return 250;
|
|
3766
4811
|
if (node.componentType === "List") {
|
|
3767
|
-
const itemsFromProps =
|
|
4812
|
+
const itemsFromProps = toStringArray(node.props.items);
|
|
3768
4813
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
3769
4814
|
const fallbackCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
3770
4815
|
const itemCount = itemsFromProps.length > 0 ? itemsFromProps.length : fallbackCount;
|
|
@@ -3942,6 +4987,17 @@ var LayoutEngine = class {
|
|
|
3942
4987
|
}
|
|
3943
4988
|
return width;
|
|
3944
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
|
+
}
|
|
3945
5001
|
parseBooleanProp(value, fallback = false) {
|
|
3946
5002
|
if (typeof value === "boolean") return value;
|
|
3947
5003
|
if (typeof value === "string") {
|
|
@@ -4032,7 +5088,7 @@ var MockDataGenerator = class {
|
|
|
4032
5088
|
for (const [key, rawValues] of Object.entries(mocks)) {
|
|
4033
5089
|
let values = [];
|
|
4034
5090
|
if (typeof rawValues === "string") {
|
|
4035
|
-
values = rawValues
|
|
5091
|
+
values = toStringArray(rawValues);
|
|
4036
5092
|
} else if (Array.isArray(rawValues)) {
|
|
4037
5093
|
values = rawValues.map((v) => typeof v === "string" ? v.trim() : "").filter((v) => v.length > 0);
|
|
4038
5094
|
}
|
|
@@ -4124,7 +5180,7 @@ var MockDataGenerator = class {
|
|
|
4124
5180
|
* columns: "id,name,status,amount"
|
|
4125
5181
|
*/
|
|
4126
5182
|
static generateMockRow(columns, rowIndex, random = false) {
|
|
4127
|
-
const columnNames = columns
|
|
5183
|
+
const columnNames = toStringArray(columns);
|
|
4128
5184
|
const row = {};
|
|
4129
5185
|
columnNames.forEach((col) => {
|
|
4130
5186
|
const mockType = this.inferMockTypeFromColumn(col);
|
|
@@ -5176,6 +6232,9 @@ var SVGRenderer = class {
|
|
|
5176
6232
|
if (!node || !pos) return;
|
|
5177
6233
|
this.renderedNodeIds.add(nodeId);
|
|
5178
6234
|
if (node.kind === "container") {
|
|
6235
|
+
if (String(node.params.visible) === "false") {
|
|
6236
|
+
return;
|
|
6237
|
+
}
|
|
5179
6238
|
const containerGroup = [];
|
|
5180
6239
|
const hasNodeId = node.meta?.nodeId;
|
|
5181
6240
|
if (hasNodeId) {
|
|
@@ -5196,6 +6255,12 @@ var SVGRenderer = class {
|
|
|
5196
6255
|
if (node.containerType === "split") {
|
|
5197
6256
|
this.renderSplitDecoration(node, pos, containerGroup);
|
|
5198
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
|
+
}
|
|
5199
6264
|
const isCellContainer = node.meta?.source === "cell";
|
|
5200
6265
|
if (node.children.length === 0 && this.options.showDiagnostics && !isCellContainer) {
|
|
5201
6266
|
containerGroup.push(this.renderEmptyContainerDiagnostic(pos, node.containerType));
|
|
@@ -5222,6 +6287,9 @@ var SVGRenderer = class {
|
|
|
5222
6287
|
}
|
|
5223
6288
|
output.push(...instanceGroup);
|
|
5224
6289
|
} else if (node.kind === "component") {
|
|
6290
|
+
if (String(node.props.visible) === "false") {
|
|
6291
|
+
return;
|
|
6292
|
+
}
|
|
5225
6293
|
const componentSvg = this.renderComponent(node, pos);
|
|
5226
6294
|
if (componentSvg) {
|
|
5227
6295
|
output.push(componentSvg);
|
|
@@ -5283,8 +6351,6 @@ var SVGRenderer = class {
|
|
|
5283
6351
|
return this.renderAlert(node, pos);
|
|
5284
6352
|
case "Badge":
|
|
5285
6353
|
return this.renderBadge(node, pos);
|
|
5286
|
-
case "Modal":
|
|
5287
|
-
return this.renderModal(node, pos);
|
|
5288
6354
|
case "List":
|
|
5289
6355
|
return this.renderList(node, pos);
|
|
5290
6356
|
case "Stat":
|
|
@@ -5727,8 +6793,8 @@ var SVGRenderer = class {
|
|
|
5727
6793
|
}
|
|
5728
6794
|
renderTable(node, pos) {
|
|
5729
6795
|
const title = String(node.props.title || "");
|
|
5730
|
-
const
|
|
5731
|
-
const columns =
|
|
6796
|
+
const parsedColumns = toStringArray(node.props.columns);
|
|
6797
|
+
const columns = parsedColumns.length > 0 ? parsedColumns : ["Col1", "Col2", "Col3"];
|
|
5732
6798
|
const rowCount = Number(node.props.rows || node.props.rowsMock || 5);
|
|
5733
6799
|
const mockStr = String(node.props.mock || "");
|
|
5734
6800
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
@@ -5736,7 +6802,7 @@ var SVGRenderer = class {
|
|
|
5736
6802
|
const parsedPageCount = Number(node.props.pages || 5);
|
|
5737
6803
|
const pageCount = Number.isFinite(parsedPageCount) && parsedPageCount > 0 ? Math.floor(parsedPageCount) : 5;
|
|
5738
6804
|
const paginationAlign = String(node.props.paginationAlign || "right");
|
|
5739
|
-
const actions =
|
|
6805
|
+
const actions = toStringArray(node.props.actions);
|
|
5740
6806
|
const hasActions = actions.length > 0;
|
|
5741
6807
|
const caption = String(node.props.caption || "").trim();
|
|
5742
6808
|
const hasCaption = caption.length > 0;
|
|
@@ -5749,7 +6815,7 @@ var SVGRenderer = class {
|
|
|
5749
6815
|
const rawCaptionAlign = String(node.props.captionAlign || "");
|
|
5750
6816
|
const captionAlign = rawCaptionAlign === "left" || rawCaptionAlign === "center" || rawCaptionAlign === "right" ? rawCaptionAlign : paginationAlign === "left" ? "right" : "left";
|
|
5751
6817
|
const sameFooterAlign = hasCaption && pagination && captionAlign === paginationAlign;
|
|
5752
|
-
const mockTypes = mockStr
|
|
6818
|
+
const mockTypes = toStringArray(mockStr);
|
|
5753
6819
|
const safeColumns = columns.length > 0 ? columns : ["Column"];
|
|
5754
6820
|
while (mockTypes.length < safeColumns.length) {
|
|
5755
6821
|
const inferred = MockDataGenerator.inferMockTypeFromColumn(safeColumns[mockTypes.length] || "item");
|
|
@@ -5849,6 +6915,13 @@ var SVGRenderer = class {
|
|
|
5849
6915
|
currentX += buttonSize + buttonGap;
|
|
5850
6916
|
});
|
|
5851
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
|
+
}
|
|
5852
6925
|
});
|
|
5853
6926
|
const footerTop = headerY + headerHeight + mockRows.length * rowHeight + 16;
|
|
5854
6927
|
if (pagination) {
|
|
@@ -6238,14 +7311,15 @@ var SVGRenderer = class {
|
|
|
6238
7311
|
const label = String(node.props.label || "Checkbox");
|
|
6239
7312
|
const checked = String(node.props.checked || "false").toLowerCase() === "true";
|
|
6240
7313
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7314
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6241
7315
|
const controlColor = this.resolveControlColor();
|
|
6242
7316
|
const checkboxSize = 18;
|
|
6243
7317
|
const checkboxY = pos.y + pos.height / 2 - checkboxSize / 2;
|
|
6244
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6245
|
-
<rect x="${pos.x}" y="${checkboxY}"
|
|
6246
|
-
width="${checkboxSize}" height="${checkboxSize}"
|
|
6247
|
-
rx="4"
|
|
6248
|
-
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}"
|
|
6249
7323
|
stroke="${this.renderTheme.border}"
|
|
6250
7324
|
stroke-width="1"/>
|
|
6251
7325
|
${checked ? `<text x="${pos.x + checkboxSize / 2}" y="${checkboxY + 14}"
|
|
@@ -6263,13 +7337,14 @@ var SVGRenderer = class {
|
|
|
6263
7337
|
const label = String(node.props.label || "Radio");
|
|
6264
7338
|
const checked = String(node.props.checked || "false").toLowerCase() === "true";
|
|
6265
7339
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7340
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6266
7341
|
const controlColor = this.resolveControlColor();
|
|
6267
7342
|
const radioSize = 16;
|
|
6268
7343
|
const radioY = pos.y + pos.height / 2 - radioSize / 2;
|
|
6269
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6270
|
-
<circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
|
|
6271
|
-
r="${radioSize / 2}"
|
|
6272
|
-
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}"
|
|
6273
7348
|
stroke="${this.renderTheme.border}"
|
|
6274
7349
|
stroke-width="1"/>
|
|
6275
7350
|
${checked ? `<circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
|
|
@@ -6285,12 +7360,13 @@ var SVGRenderer = class {
|
|
|
6285
7360
|
const label = String(node.props.label || "Toggle");
|
|
6286
7361
|
const enabled = String(node.props.enabled || "false").toLowerCase() === "true";
|
|
6287
7362
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7363
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6288
7364
|
const controlColor = this.resolveControlColor();
|
|
6289
7365
|
const toggleWidth = 40;
|
|
6290
7366
|
const toggleHeight = 20;
|
|
6291
7367
|
const toggleY = pos.y + pos.height / 2 - toggleHeight / 2;
|
|
6292
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6293
|
-
<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}"
|
|
6294
7370
|
width="${toggleWidth}" height="${toggleHeight}"
|
|
6295
7371
|
rx="10"
|
|
6296
7372
|
fill="${enabled ? controlColor : this.renderTheme.border}"
|
|
@@ -6309,12 +7385,9 @@ var SVGRenderer = class {
|
|
|
6309
7385
|
// ============================================================================
|
|
6310
7386
|
renderSidebar(node, pos) {
|
|
6311
7387
|
const title = String(node.props.title || "Sidebar");
|
|
6312
|
-
const itemsStr = String(node.props.items || "");
|
|
6313
7388
|
const activeItem = String(node.props.active || "");
|
|
6314
|
-
let items =
|
|
6315
|
-
if (
|
|
6316
|
-
items = itemsStr.split(",").map((i) => i.trim());
|
|
6317
|
-
} else {
|
|
7389
|
+
let items = toStringArray(node.props.items);
|
|
7390
|
+
if (items.length === 0) {
|
|
6318
7391
|
const itemCount = Number(node.props.itemsMock || 6);
|
|
6319
7392
|
items = MockDataGenerator.generateMockList("name", itemCount);
|
|
6320
7393
|
}
|
|
@@ -6353,9 +7426,9 @@ var SVGRenderer = class {
|
|
|
6353
7426
|
return svg;
|
|
6354
7427
|
}
|
|
6355
7428
|
renderTabs(node, pos) {
|
|
6356
|
-
const
|
|
6357
|
-
const tabs =
|
|
6358
|
-
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;
|
|
6359
7432
|
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
6360
7433
|
const variant = String(node.props.variant || "default");
|
|
6361
7434
|
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
@@ -6365,8 +7438,7 @@ var SVGRenderer = class {
|
|
|
6365
7438
|
const tabHeight = pos.height > 0 ? pos.height : sizeMap[String(node.props.size || "md")] ?? 44;
|
|
6366
7439
|
const fontSize = 13;
|
|
6367
7440
|
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
6368
|
-
const
|
|
6369
|
-
const iconList = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
7441
|
+
const iconList = toStringArray(node.props.icons);
|
|
6370
7442
|
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
6371
7443
|
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
6372
7444
|
const tabWidth = pos.width / tabs.length;
|
|
@@ -6470,6 +7542,13 @@ var SVGRenderer = class {
|
|
|
6470
7542
|
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
6471
7543
|
}
|
|
6472
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
|
+
}
|
|
6473
7552
|
});
|
|
6474
7553
|
const contentY = pos.y + tabHeight;
|
|
6475
7554
|
const contentH = pos.height - tabHeight;
|
|
@@ -6583,66 +7662,51 @@ var SVGRenderer = class {
|
|
|
6583
7662
|
text-anchor="middle">${this.escapeXml(text)}</text>
|
|
6584
7663
|
</g>`;
|
|
6585
7664
|
}
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
}
|
|
6591
|
-
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());
|
|
6592
7669
|
const padding = 16;
|
|
6593
7670
|
const headerHeight = 48;
|
|
6594
|
-
const
|
|
6595
|
-
const
|
|
6596
|
-
const
|
|
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
|
-
|
|
6626
|
-
font-size="18"
|
|
6627
|
-
fill="${this.renderTheme.textMuted}">\u2715</text>
|
|
6628
|
-
|
|
6629
|
-
<!-- Content placeholder -->
|
|
6630
|
-
<text x="${modalX + pos.width / 2}" y="${modalY + headerHeight + (pos.height - headerHeight) / 2}"
|
|
6631
|
-
font-family="Arial, Helvetica, sans-serif"
|
|
6632
|
-
font-size="13"
|
|
6633
|
-
fill="${this.renderTheme.textMuted}"
|
|
6634
|
-
text-anchor="middle">Modal content</text>
|
|
6635
|
-
</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
|
+
);
|
|
6636
7703
|
}
|
|
6637
7704
|
renderList(node, pos) {
|
|
6638
7705
|
const title = String(node.props.title || "");
|
|
6639
|
-
const itemsStr = String(node.props.items || "");
|
|
6640
7706
|
const mockType = String(node.props.mock || "").trim();
|
|
6641
7707
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
6642
|
-
let items =
|
|
6643
|
-
if (
|
|
6644
|
-
items = itemsStr.split(",").map((i) => i.trim()).filter(Boolean);
|
|
6645
|
-
} else {
|
|
7708
|
+
let items = toStringArray(node.props.items);
|
|
7709
|
+
if (items.length === 0) {
|
|
6646
7710
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
6647
7711
|
const itemCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
6648
7712
|
const resolvedMockType = mockType || "name";
|
|
@@ -6671,6 +7735,7 @@ var SVGRenderer = class {
|
|
|
6671
7735
|
items.forEach((item, i) => {
|
|
6672
7736
|
const itemY = pos.y + titleHeight + i * itemHeight;
|
|
6673
7737
|
if (itemY + itemHeight <= pos.y + pos.height) {
|
|
7738
|
+
const itemEventAttrs = this.getScopedEventAttrs(node, "onItemClick", { index: i });
|
|
6674
7739
|
svg += `
|
|
6675
7740
|
<line x1="${pos.x}" y1="${itemY + itemHeight}"
|
|
6676
7741
|
x2="${pos.x + pos.width}" y2="${itemY + itemHeight}"
|
|
@@ -6679,7 +7744,10 @@ var SVGRenderer = class {
|
|
|
6679
7744
|
<text x="${pos.x + padding}" y="${itemY + 24}"
|
|
6680
7745
|
font-family="Arial, Helvetica, sans-serif"
|
|
6681
7746
|
font-size="13"
|
|
6682
|
-
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}/>` : ""}`;
|
|
6683
7751
|
}
|
|
6684
7752
|
});
|
|
6685
7753
|
svg += "\n </g>";
|
|
@@ -6913,8 +7981,8 @@ var SVGRenderer = class {
|
|
|
6913
7981
|
return svg;
|
|
6914
7982
|
}
|
|
6915
7983
|
renderBreadcrumbs(node, pos) {
|
|
6916
|
-
const
|
|
6917
|
-
const items =
|
|
7984
|
+
const parsedBreadcrumbs = toStringArray(node.props.items);
|
|
7985
|
+
const items = parsedBreadcrumbs.length > 0 ? parsedBreadcrumbs : ["Home"];
|
|
6918
7986
|
const separator = String(node.props.separator || "/");
|
|
6919
7987
|
const fontSize = 12;
|
|
6920
7988
|
const separatorWidth = 20;
|
|
@@ -6946,10 +8014,9 @@ var SVGRenderer = class {
|
|
|
6946
8014
|
return svg;
|
|
6947
8015
|
}
|
|
6948
8016
|
renderSidebarMenu(node, pos) {
|
|
6949
|
-
const
|
|
6950
|
-
const
|
|
6951
|
-
const
|
|
6952
|
-
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);
|
|
6953
8020
|
const itemHeight = 40;
|
|
6954
8021
|
const fontSize = 14;
|
|
6955
8022
|
const activeIndex = Number(node.props.active || 0);
|
|
@@ -6994,6 +8061,13 @@ var SVGRenderer = class {
|
|
|
6994
8061
|
font-size="${fontSize}"
|
|
6995
8062
|
font-weight="${fontWeight}"
|
|
6996
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
|
+
}
|
|
6997
8071
|
});
|
|
6998
8072
|
svg += "\n </g>";
|
|
6999
8073
|
return svg;
|
|
@@ -7344,7 +8418,7 @@ var SVGRenderer = class {
|
|
|
7344
8418
|
userBadge = { x, y, width, height, label: userLabel };
|
|
7345
8419
|
rightCursor = x - 8;
|
|
7346
8420
|
}
|
|
7347
|
-
const actionLabels = actions
|
|
8421
|
+
const actionLabels = toStringArray(actions);
|
|
7348
8422
|
const actionHeight = 32;
|
|
7349
8423
|
const actionY = pos.y + (pos.height - actionHeight) / 2;
|
|
7350
8424
|
const actionGap = 8;
|
|
@@ -7430,11 +8504,99 @@ var SVGRenderer = class {
|
|
|
7430
8504
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
7431
8505
|
}
|
|
7432
8506
|
/**
|
|
7433
|
-
* Get data-node-id
|
|
7434
|
-
* 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).
|
|
7435
8510
|
*/
|
|
7436
8511
|
getDataNodeId(node) {
|
|
7437
|
-
|
|
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
|
+
}
|
|
7438
8600
|
}
|
|
7439
8601
|
};
|
|
7440
8602
|
function renderToSVG(ir, layout, options) {
|
|
@@ -7524,8 +8686,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
7524
8686
|
* Render breadcrumbs as skeleton blocks: <rect> / <rect> / <rect accent>
|
|
7525
8687
|
*/
|
|
7526
8688
|
renderBreadcrumbs(node, pos) {
|
|
7527
|
-
const
|
|
7528
|
-
const items = itemsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
8689
|
+
const items = toStringArray(node.props.items || "Home");
|
|
7529
8690
|
const separator = String(node.props.separator || "/");
|
|
7530
8691
|
const blockColor = this.renderTheme.border;
|
|
7531
8692
|
const charWidth = 6.2;
|
|
@@ -7846,10 +9007,9 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
7846
9007
|
*/
|
|
7847
9008
|
renderTable(node, pos) {
|
|
7848
9009
|
const title = String(node.props.title || "");
|
|
7849
|
-
const
|
|
7850
|
-
const columns = columnsStr.split(",").map((c) => c.trim()).filter(Boolean);
|
|
9010
|
+
const columns = toStringArray(node.props.columns || "Col1,Col2,Col3");
|
|
7851
9011
|
const rowCount = Number(node.props.rows || node.props.rowsMock || 5);
|
|
7852
|
-
const actions =
|
|
9012
|
+
const actions = toStringArray(node.props.actions);
|
|
7853
9013
|
const hasActions = actions.length > 0;
|
|
7854
9014
|
const pagination = this.parseBooleanProp(node.props.pagination, false);
|
|
7855
9015
|
const parsedPageCount = Number(node.props.pages || 5);
|
|
@@ -8135,7 +9295,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
8135
9295
|
const itemsStr = String(node.props.items || "");
|
|
8136
9296
|
let items = [];
|
|
8137
9297
|
if (itemsStr) {
|
|
8138
|
-
items = itemsStr
|
|
9298
|
+
items = toStringArray(itemsStr);
|
|
8139
9299
|
} else {
|
|
8140
9300
|
const itemCount = Number(node.props.itemsMock || 6);
|
|
8141
9301
|
items = Array(itemCount).fill("Item");
|
|
@@ -8172,8 +9332,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
8172
9332
|
* Render SidebarMenu with gray blocks instead of text and no icons
|
|
8173
9333
|
*/
|
|
8174
9334
|
renderSidebarMenu(node, pos) {
|
|
8175
|
-
const
|
|
8176
|
-
const items = itemsStr.split(",").map((s) => s.trim());
|
|
9335
|
+
const items = toStringArray(node.props.items || "Item 1,Item 2,Item 3");
|
|
8177
9336
|
const itemHeight = 40;
|
|
8178
9337
|
const activeIndex = Number(node.props.active || 0);
|
|
8179
9338
|
const accentColor = this.resolveAccentColor();
|
|
@@ -9021,7 +10180,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9021
10180
|
const title = String(node.props.title || "Sidebar");
|
|
9022
10181
|
const itemsStr = String(node.props.items || "");
|
|
9023
10182
|
const activeItem = String(node.props.active || "");
|
|
9024
|
-
const
|
|
10183
|
+
const parsed = toStringArray(itemsStr);
|
|
10184
|
+
const items = parsed.length ? parsed : ["Item 1", "Item 2", "Item 3"];
|
|
9025
10185
|
const itemHeight = 40;
|
|
9026
10186
|
const padding = 16;
|
|
9027
10187
|
const titleHeight = 40;
|
|
@@ -9064,7 +10224,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9064
10224
|
*/
|
|
9065
10225
|
renderTabs(node, pos) {
|
|
9066
10226
|
const itemsStr = String(node.props.items || "");
|
|
9067
|
-
const
|
|
10227
|
+
const parsedTabs = toStringArray(itemsStr);
|
|
10228
|
+
const tabs = parsedTabs.length ? parsedTabs : ["Tab 1", "Tab 2", "Tab 3"];
|
|
9068
10229
|
const activeProp = node.props.active ?? 0;
|
|
9069
10230
|
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
9070
10231
|
const variant = String(node.props.variant || "default");
|
|
@@ -9076,7 +10237,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9076
10237
|
const fontSize = 13;
|
|
9077
10238
|
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
9078
10239
|
const iconsStr = String(node.props.icons || "");
|
|
9079
|
-
const iconList = iconsStr
|
|
10240
|
+
const iconList = toStringArray(iconsStr);
|
|
9080
10241
|
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
9081
10242
|
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
9082
10243
|
const tabWidth = pos.width / tabs.length;
|
|
@@ -9241,7 +10402,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9241
10402
|
<!-- Modal backdrop -->
|
|
9242
10403
|
<rect x="0" y="0"
|
|
9243
10404
|
width="${this.options.width}" height="${overlayHeight}"
|
|
9244
|
-
fill="black" opacity="0.28"/>
|
|
10405
|
+
fill="black" opacity="0.28" pointer-events="none"/>
|
|
9245
10406
|
|
|
9246
10407
|
<!-- Modal box -->
|
|
9247
10408
|
<rect x="${modalX}" y="${modalY}"
|
|
@@ -9269,7 +10430,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9269
10430
|
<text x="${modalX + pos.width - 16}" y="${modalY + padding + 12}"
|
|
9270
10431
|
font-family="${this.fontFamily}"
|
|
9271
10432
|
font-size="18"
|
|
9272
|
-
fill="${this.renderTheme.textMuted}"
|
|
10433
|
+
fill="${this.renderTheme.textMuted}"
|
|
10434
|
+
pointer-events="none">\u2715</text>
|
|
9273
10435
|
|
|
9274
10436
|
<!-- Content placeholder -->
|
|
9275
10437
|
<text x="${modalX + pos.width / 2}" y="${modalY + headerHeight + (pos.height - headerHeight) / 2}"
|
|
@@ -9287,10 +10449,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9287
10449
|
const itemsStr = String(node.props.items || "");
|
|
9288
10450
|
const mockType = String(node.props.mock || "").trim();
|
|
9289
10451
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
9290
|
-
let items =
|
|
9291
|
-
if (
|
|
9292
|
-
items = itemsStr.split(",").map((i) => i.trim()).filter(Boolean);
|
|
9293
|
-
} else {
|
|
10452
|
+
let items = toStringArray(itemsStr);
|
|
10453
|
+
if (!items.length) {
|
|
9294
10454
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
9295
10455
|
const itemCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
9296
10456
|
const resolvedMockType = mockType || "name";
|
|
@@ -9504,7 +10664,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9504
10664
|
*/
|
|
9505
10665
|
renderBreadcrumbs(node, pos) {
|
|
9506
10666
|
const itemsStr = String(node.props.items || "Home");
|
|
9507
|
-
const items = itemsStr
|
|
10667
|
+
const items = toStringArray(itemsStr);
|
|
9508
10668
|
const separator = String(node.props.separator || "/");
|
|
9509
10669
|
const fontSize = 12;
|
|
9510
10670
|
const separatorWidth = 20;
|
|
@@ -9541,8 +10701,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9541
10701
|
renderSidebarMenu(node, pos) {
|
|
9542
10702
|
const itemsStr = String(node.props.items || "Item 1,Item 2,Item 3");
|
|
9543
10703
|
const iconsStr = String(node.props.icons || "");
|
|
9544
|
-
const items = itemsStr
|
|
9545
|
-
const icons = iconsStr
|
|
10704
|
+
const items = toStringArray(itemsStr);
|
|
10705
|
+
const icons = toStringArray(iconsStr);
|
|
9546
10706
|
const itemHeight = 40;
|
|
9547
10707
|
const fontSize = 14;
|
|
9548
10708
|
const activeIndex = Number(node.props.active || 0);
|
|
@@ -9863,11 +11023,13 @@ var version = "0.0.1";
|
|
|
9863
11023
|
DEVICE_PRESETS,
|
|
9864
11024
|
IRGenerator,
|
|
9865
11025
|
LayoutEngine,
|
|
11026
|
+
SELF_TARGET,
|
|
9866
11027
|
SVGRenderer,
|
|
9867
11028
|
SkeletonSVGRenderer,
|
|
9868
11029
|
SketchSVGRenderer,
|
|
9869
11030
|
SourceMapBuilder,
|
|
9870
11031
|
SourceMapResolver,
|
|
11032
|
+
applyStateChange,
|
|
9871
11033
|
buildSVG,
|
|
9872
11034
|
calculateLayout,
|
|
9873
11035
|
createSVGElement,
|