@wire-dsl/engine 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1370 -207
- package/dist/index.d.cts +211 -14
- package/dist/index.d.ts +211 -14
- package/dist/index.js +1368 -207
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -93,6 +93,21 @@ var SourceMapBuilder = class {
|
|
|
93
93
|
this.counters.set("cell", idx + 1);
|
|
94
94
|
return `cell-${idx}`;
|
|
95
95
|
}
|
|
96
|
+
case "tab": {
|
|
97
|
+
const idx = this.counters.get("tab") || 0;
|
|
98
|
+
this.counters.set("tab", idx + 1);
|
|
99
|
+
return `tab-${idx}`;
|
|
100
|
+
}
|
|
101
|
+
case "modal-body": {
|
|
102
|
+
const idx = this.counters.get("modal-body") || 0;
|
|
103
|
+
this.counters.set("modal-body", idx + 1);
|
|
104
|
+
return `modal-body-${idx}`;
|
|
105
|
+
}
|
|
106
|
+
case "modal-footer": {
|
|
107
|
+
const idx = this.counters.get("modal-footer") || 0;
|
|
108
|
+
this.counters.set("modal-footer", idx + 1);
|
|
109
|
+
return `modal-footer-${idx}`;
|
|
110
|
+
}
|
|
96
111
|
case "component-definition":
|
|
97
112
|
return `define-${metadata?.name || "unknown"}`;
|
|
98
113
|
case "layout-definition":
|
|
@@ -155,6 +170,49 @@ var SourceMapBuilder = class {
|
|
|
155
170
|
entry.properties[propertyName] = propertySourceMap;
|
|
156
171
|
return propertySourceMap;
|
|
157
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Add an event handler to an existing node in the SourceMap
|
|
175
|
+
* Events have action expressions as values (e.g., "show(modal) & navigate(Home)")
|
|
176
|
+
*
|
|
177
|
+
* @param nodeId - ID of the node that owns this event
|
|
178
|
+
* @param eventName - Name of the event (e.g., "onClick", "onClose")
|
|
179
|
+
* @param tokens - Captured tokens: name token for event key, CST node for actionChain
|
|
180
|
+
* @returns The EventSourceMap entry created
|
|
181
|
+
*/
|
|
182
|
+
addEvent(nodeId, eventName, tokens) {
|
|
183
|
+
const entry = this.entries.find((e) => e.nodeId === nodeId);
|
|
184
|
+
if (!entry) {
|
|
185
|
+
throw new Error(`Cannot add event to non-existent node: ${nodeId}`);
|
|
186
|
+
}
|
|
187
|
+
if (!entry.events) {
|
|
188
|
+
entry.events = {};
|
|
189
|
+
}
|
|
190
|
+
const nameRange = {
|
|
191
|
+
start: this.getTokenStart(tokens.name),
|
|
192
|
+
end: this.getTokenEnd(tokens.name)
|
|
193
|
+
};
|
|
194
|
+
const valueRange = {
|
|
195
|
+
start: this.getTokenStart(tokens.value),
|
|
196
|
+
end: this.getTokenEnd(tokens.value)
|
|
197
|
+
};
|
|
198
|
+
const fullRange = {
|
|
199
|
+
start: nameRange.start,
|
|
200
|
+
end: valueRange.end
|
|
201
|
+
};
|
|
202
|
+
let rawValue = "";
|
|
203
|
+
if (this.sourceCode && valueRange.start.offset !== void 0 && valueRange.end.offset !== void 0) {
|
|
204
|
+
rawValue = this.sourceCode.slice(valueRange.start.offset, valueRange.end.offset + 1);
|
|
205
|
+
}
|
|
206
|
+
const eventSourceMap = {
|
|
207
|
+
name: eventName,
|
|
208
|
+
value: rawValue,
|
|
209
|
+
range: fullRange,
|
|
210
|
+
nameRange,
|
|
211
|
+
valueRange
|
|
212
|
+
};
|
|
213
|
+
entry.events[eventName] = eventSourceMap;
|
|
214
|
+
return eventSourceMap;
|
|
215
|
+
}
|
|
158
216
|
/**
|
|
159
217
|
* Push a parent onto the stack (when entering a container node)
|
|
160
218
|
*/
|
|
@@ -190,6 +248,9 @@ var SourceMapBuilder = class {
|
|
|
190
248
|
"screen",
|
|
191
249
|
"layout",
|
|
192
250
|
"cell",
|
|
251
|
+
"tab",
|
|
252
|
+
"modal-body",
|
|
253
|
+
"modal-footer",
|
|
193
254
|
"component-definition",
|
|
194
255
|
"layout-definition"
|
|
195
256
|
];
|
|
@@ -434,12 +495,26 @@ var Style = createToken({ name: "Style", pattern: /style\b/ });
|
|
|
434
495
|
var Mocks = createToken({ name: "Mocks", pattern: /mocks\b/ });
|
|
435
496
|
var Colors = createToken({ name: "Colors", pattern: /colors(?=\s*\{)/ });
|
|
436
497
|
var Cell = createToken({ name: "Cell", pattern: /cell\b/ });
|
|
498
|
+
var Tab = createToken({ name: "Tab", pattern: /tab\b/ });
|
|
499
|
+
var Body = createToken({ name: "Body", pattern: /body\b/ });
|
|
500
|
+
var Footer = createToken({ name: "Footer", pattern: /footer\b/ });
|
|
501
|
+
var Navigate = createToken({ name: "Navigate", pattern: /navigate\b/ });
|
|
502
|
+
var Show = createToken({ name: "Show", pattern: /show\b/ });
|
|
503
|
+
var Hide = createToken({ name: "Hide", pattern: /hide\b/ });
|
|
504
|
+
var ToggleAction = createToken({ name: "ToggleAction", pattern: /toggle\b/ });
|
|
505
|
+
var EnableAction = createToken({ name: "EnableAction", pattern: /enable\b/ });
|
|
506
|
+
var DisableAction = createToken({ name: "DisableAction", pattern: /disable\b/ });
|
|
507
|
+
var SetTab = createToken({ name: "SetTab", pattern: /setTab\b/ });
|
|
508
|
+
var Self = createToken({ name: "Self", pattern: /self\b/ });
|
|
437
509
|
var LCurly = createToken({ name: "LCurly", pattern: /{/ });
|
|
438
510
|
var RCurly = createToken({ name: "RCurly", pattern: /}/ });
|
|
439
511
|
var LParen = createToken({ name: "LParen", pattern: /\(/ });
|
|
440
512
|
var RParen = createToken({ name: "RParen", pattern: /\)/ });
|
|
441
513
|
var Colon = createToken({ name: "Colon", pattern: /:/ });
|
|
442
514
|
var Comma = createToken({ name: "Comma", pattern: /,/ });
|
|
515
|
+
var Ampersand = createToken({ name: "Ampersand", pattern: /&/ });
|
|
516
|
+
var LBracket = createToken({ name: "LBracket", pattern: /\[/ });
|
|
517
|
+
var RBracket = createToken({ name: "RBracket", pattern: /\]/ });
|
|
443
518
|
var StringLiteral = createToken({
|
|
444
519
|
name: "StringLiteral",
|
|
445
520
|
pattern: /"(?:[^"\\]|\\.)*"/
|
|
@@ -488,6 +563,18 @@ var allTokens = [
|
|
|
488
563
|
Mocks,
|
|
489
564
|
Colors,
|
|
490
565
|
Cell,
|
|
566
|
+
Tab,
|
|
567
|
+
Body,
|
|
568
|
+
Footer,
|
|
569
|
+
// Event action keywords (must come before Identifier)
|
|
570
|
+
Navigate,
|
|
571
|
+
Show,
|
|
572
|
+
Hide,
|
|
573
|
+
ToggleAction,
|
|
574
|
+
EnableAction,
|
|
575
|
+
DisableAction,
|
|
576
|
+
SetTab,
|
|
577
|
+
Self,
|
|
491
578
|
// Punctuation
|
|
492
579
|
LCurly,
|
|
493
580
|
RCurly,
|
|
@@ -495,6 +582,9 @@ var allTokens = [
|
|
|
495
582
|
RParen,
|
|
496
583
|
Colon,
|
|
497
584
|
Comma,
|
|
585
|
+
Ampersand,
|
|
586
|
+
LBracket,
|
|
587
|
+
RBracket,
|
|
498
588
|
// Literals
|
|
499
589
|
StringLiteral,
|
|
500
590
|
NumberLiteral,
|
|
@@ -602,6 +692,90 @@ var WireDSLParser = class extends CstParser {
|
|
|
602
692
|
this.SUBRULE(this.layout);
|
|
603
693
|
this.CONSUME(RCurly);
|
|
604
694
|
});
|
|
695
|
+
// singleAction: navigate(Screen) | show(id|self) | hide(id|self) | toggle(id|self) | setTab(tabsId, index)
|
|
696
|
+
this.singleAction = this.RULE("singleAction", () => {
|
|
697
|
+
this.OR([
|
|
698
|
+
{
|
|
699
|
+
ALT: () => {
|
|
700
|
+
this.CONSUME(Navigate, { LABEL: "navigate" });
|
|
701
|
+
this.CONSUME(LParen);
|
|
702
|
+
this.CONSUME(Identifier, { LABEL: "targetScreen" });
|
|
703
|
+
this.CONSUME(RParen);
|
|
704
|
+
}
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
ALT: () => {
|
|
708
|
+
this.OR2([
|
|
709
|
+
{ ALT: () => this.CONSUME(Show, { LABEL: "sht" }) },
|
|
710
|
+
{ ALT: () => this.CONSUME(Hide, { LABEL: "sht" }) },
|
|
711
|
+
{ ALT: () => this.CONSUME(ToggleAction, { LABEL: "sht" }) },
|
|
712
|
+
{ ALT: () => this.CONSUME(EnableAction, { LABEL: "sht" }) },
|
|
713
|
+
{ ALT: () => this.CONSUME(DisableAction, { LABEL: "sht" }) }
|
|
714
|
+
]);
|
|
715
|
+
this.CONSUME2(LParen);
|
|
716
|
+
this.OR3([
|
|
717
|
+
{ ALT: () => this.CONSUME(Self, { LABEL: "targetId" }) },
|
|
718
|
+
{ ALT: () => this.CONSUME2(Identifier, { LABEL: "targetId" }) }
|
|
719
|
+
]);
|
|
720
|
+
this.CONSUME2(RParen);
|
|
721
|
+
}
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
ALT: () => {
|
|
725
|
+
this.CONSUME(SetTab, { LABEL: "setTab" });
|
|
726
|
+
this.CONSUME3(LParen);
|
|
727
|
+
this.CONSUME3(Identifier, { LABEL: "tabsId" });
|
|
728
|
+
this.CONSUME(Comma);
|
|
729
|
+
this.CONSUME(NumberLiteral, { LABEL: "tabIndex" });
|
|
730
|
+
this.CONSUME3(RParen);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
]);
|
|
734
|
+
});
|
|
735
|
+
// actionChain: singleAction (& singleAction)*
|
|
736
|
+
this.actionChain = this.RULE("actionChain", () => {
|
|
737
|
+
this.SUBRULE(this.singleAction);
|
|
738
|
+
this.MANY(() => {
|
|
739
|
+
this.CONSUME(Ampersand);
|
|
740
|
+
this.SUBRULE2(this.singleAction);
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
// tab { ... } — children block inside layout tabs
|
|
744
|
+
this.tab = this.RULE("tab", () => {
|
|
745
|
+
this.CONSUME(Tab);
|
|
746
|
+
this.CONSUME(LCurly);
|
|
747
|
+
this.MANY(() => {
|
|
748
|
+
this.OR([
|
|
749
|
+
{ ALT: () => this.SUBRULE(this.component) },
|
|
750
|
+
{ ALT: () => this.SUBRULE(this.layout) }
|
|
751
|
+
]);
|
|
752
|
+
});
|
|
753
|
+
this.CONSUME(RCurly);
|
|
754
|
+
});
|
|
755
|
+
// body { ... } — content section inside layout modal
|
|
756
|
+
this.body = this.RULE("body", () => {
|
|
757
|
+
this.CONSUME(Body);
|
|
758
|
+
this.CONSUME(LCurly);
|
|
759
|
+
this.MANY(() => {
|
|
760
|
+
this.OR([
|
|
761
|
+
{ ALT: () => this.SUBRULE(this.component) },
|
|
762
|
+
{ ALT: () => this.SUBRULE(this.layout) }
|
|
763
|
+
]);
|
|
764
|
+
});
|
|
765
|
+
this.CONSUME(RCurly);
|
|
766
|
+
});
|
|
767
|
+
// footer { ... } — footer section inside layout modal
|
|
768
|
+
this.footer = this.RULE("footer", () => {
|
|
769
|
+
this.CONSUME(Footer);
|
|
770
|
+
this.CONSUME(LCurly);
|
|
771
|
+
this.MANY(() => {
|
|
772
|
+
this.OR([
|
|
773
|
+
{ ALT: () => this.SUBRULE(this.component) },
|
|
774
|
+
{ ALT: () => this.SUBRULE(this.layout) }
|
|
775
|
+
]);
|
|
776
|
+
});
|
|
777
|
+
this.CONSUME(RCurly);
|
|
778
|
+
});
|
|
605
779
|
// layout stack(...) { ... }
|
|
606
780
|
this.layout = this.RULE("layout", () => {
|
|
607
781
|
this.CONSUME(Layout);
|
|
@@ -614,7 +788,10 @@ var WireDSLParser = class extends CstParser {
|
|
|
614
788
|
this.OR([
|
|
615
789
|
{ ALT: () => this.SUBRULE(this.component) },
|
|
616
790
|
{ ALT: () => this.SUBRULE2(this.layout) },
|
|
617
|
-
{ ALT: () => this.SUBRULE(this.cell) }
|
|
791
|
+
{ ALT: () => this.SUBRULE(this.cell) },
|
|
792
|
+
{ ALT: () => this.SUBRULE(this.tab) },
|
|
793
|
+
{ ALT: () => this.SUBRULE(this.body) },
|
|
794
|
+
{ ALT: () => this.SUBRULE(this.footer) }
|
|
618
795
|
]);
|
|
619
796
|
});
|
|
620
797
|
this.CONSUME(RCurly);
|
|
@@ -642,16 +819,27 @@ var WireDSLParser = class extends CstParser {
|
|
|
642
819
|
this.SUBRULE(this.property);
|
|
643
820
|
});
|
|
644
821
|
});
|
|
645
|
-
// property: key: value
|
|
822
|
+
// property: key: value (value can be string, number, identifier, or action chain)
|
|
646
823
|
this.property = this.RULE("property", () => {
|
|
647
824
|
this.CONSUME(Identifier, { LABEL: "propKey" });
|
|
648
825
|
this.CONSUME(Colon);
|
|
649
826
|
this.OR([
|
|
827
|
+
{ ALT: () => this.SUBRULE(this.actionChain) },
|
|
828
|
+
{ ALT: () => this.SUBRULE(this.arrayLiteral) },
|
|
650
829
|
{ ALT: () => this.CONSUME(StringLiteral, { LABEL: "propValue" }) },
|
|
651
830
|
{ ALT: () => this.CONSUME(NumberLiteral, { LABEL: "propValue" }) },
|
|
652
831
|
{ ALT: () => this.CONSUME2(Identifier, { LABEL: "propValue" }) }
|
|
653
832
|
]);
|
|
654
833
|
});
|
|
834
|
+
// ["item1", "item2", "item3"]
|
|
835
|
+
this.arrayLiteral = this.RULE("arrayLiteral", () => {
|
|
836
|
+
this.CONSUME(LBracket);
|
|
837
|
+
this.MANY_SEP({
|
|
838
|
+
SEP: Comma,
|
|
839
|
+
DEF: () => this.CONSUME(StringLiteral, { LABEL: "arrayItem" })
|
|
840
|
+
});
|
|
841
|
+
this.CONSUME(RBracket);
|
|
842
|
+
});
|
|
655
843
|
// (param1: value1, param2: value2)
|
|
656
844
|
this.paramList = this.RULE("paramList", () => {
|
|
657
845
|
this.CONSUME(LParen);
|
|
@@ -790,7 +978,7 @@ var WireDSLVisitor = class extends BaseCstVisitor {
|
|
|
790
978
|
};
|
|
791
979
|
}
|
|
792
980
|
screen(ctx) {
|
|
793
|
-
const params = ctx.paramList ? this.visit(ctx.paramList[0]) : {};
|
|
981
|
+
const { params } = ctx.paramList ? this.visit(ctx.paramList[0]) : { params: {} };
|
|
794
982
|
return {
|
|
795
983
|
type: "screen",
|
|
796
984
|
name: ctx.screenName[0].image,
|
|
@@ -801,10 +989,12 @@ var WireDSLVisitor = class extends BaseCstVisitor {
|
|
|
801
989
|
layout(ctx) {
|
|
802
990
|
const layoutType = ctx.layoutType[0].image;
|
|
803
991
|
const params = {};
|
|
992
|
+
const events = [];
|
|
804
993
|
const children = [];
|
|
805
994
|
if (ctx.paramList) {
|
|
806
995
|
const paramResult = this.visit(ctx.paramList);
|
|
807
|
-
Object.assign(params, paramResult);
|
|
996
|
+
Object.assign(params, paramResult.params);
|
|
997
|
+
events.push(...paramResult.events);
|
|
808
998
|
}
|
|
809
999
|
const childNodes = [];
|
|
810
1000
|
if (ctx.component) {
|
|
@@ -825,20 +1015,33 @@ var WireDSLVisitor = class extends BaseCstVisitor {
|
|
|
825
1015
|
childNodes.push({ type: "cell", node: cell, index: startToken.startOffset });
|
|
826
1016
|
});
|
|
827
1017
|
}
|
|
1018
|
+
if (ctx.tab) {
|
|
1019
|
+
ctx.tab.forEach((tab) => {
|
|
1020
|
+
const startToken = tab.children?.Tab?.[0];
|
|
1021
|
+
childNodes.push({ type: "tab", node: tab, index: startToken.startOffset });
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
if (ctx.body) {
|
|
1025
|
+
ctx.body.forEach((body) => {
|
|
1026
|
+
const startToken = body.children?.Body?.[0];
|
|
1027
|
+
childNodes.push({ type: "body", node: body, index: startToken.startOffset });
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
if (ctx.footer) {
|
|
1031
|
+
ctx.footer.forEach((footer) => {
|
|
1032
|
+
const startToken = footer.children?.Footer?.[0];
|
|
1033
|
+
childNodes.push({ type: "footer", node: footer, index: startToken.startOffset });
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
828
1036
|
childNodes.sort((a, b) => a.index - b.index);
|
|
829
1037
|
childNodes.forEach((item) => {
|
|
830
|
-
|
|
831
|
-
children.push(this.visit(item.node));
|
|
832
|
-
} else if (item.type === "layout") {
|
|
833
|
-
children.push(this.visit(item.node));
|
|
834
|
-
} else if (item.type === "cell") {
|
|
835
|
-
children.push(this.visit(item.node));
|
|
836
|
-
}
|
|
1038
|
+
children.push(this.visit(item.node));
|
|
837
1039
|
});
|
|
838
1040
|
return {
|
|
839
1041
|
type: "layout",
|
|
840
1042
|
layoutType,
|
|
841
1043
|
params,
|
|
1044
|
+
events,
|
|
842
1045
|
children
|
|
843
1046
|
};
|
|
844
1047
|
}
|
|
@@ -878,23 +1081,137 @@ var WireDSLVisitor = class extends BaseCstVisitor {
|
|
|
878
1081
|
children
|
|
879
1082
|
};
|
|
880
1083
|
}
|
|
1084
|
+
singleAction(ctx) {
|
|
1085
|
+
if (ctx.navigate) {
|
|
1086
|
+
return { type: "navigate", screen: ctx.targetScreen[0].image };
|
|
1087
|
+
}
|
|
1088
|
+
if (ctx.sht) {
|
|
1089
|
+
const tokenName = ctx.sht[0].tokenType.name;
|
|
1090
|
+
const typeMap = {
|
|
1091
|
+
Show: "show",
|
|
1092
|
+
Hide: "hide",
|
|
1093
|
+
ToggleAction: "toggle",
|
|
1094
|
+
EnableAction: "enable",
|
|
1095
|
+
DisableAction: "disable"
|
|
1096
|
+
};
|
|
1097
|
+
const type = typeMap[tokenName] ?? "show";
|
|
1098
|
+
const targetToken = ctx.targetId[0];
|
|
1099
|
+
const isSelf = targetToken.tokenType.name === "Self";
|
|
1100
|
+
const targetId = isSelf ? "_self" : targetToken.image;
|
|
1101
|
+
return { type, targetId };
|
|
1102
|
+
}
|
|
1103
|
+
if (ctx.setTab) {
|
|
1104
|
+
return {
|
|
1105
|
+
type: "setTab",
|
|
1106
|
+
tabsId: ctx.tabsId[0].image,
|
|
1107
|
+
index: Number(ctx.tabIndex[0].image)
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
throw new Error("Unknown action type in singleAction visitor");
|
|
1111
|
+
}
|
|
1112
|
+
actionChain(ctx) {
|
|
1113
|
+
const actions = [];
|
|
1114
|
+
if (ctx.singleAction) {
|
|
1115
|
+
ctx.singleAction.forEach((action) => {
|
|
1116
|
+
actions.push(this.visit(action));
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
return actions;
|
|
1120
|
+
}
|
|
1121
|
+
tab(ctx) {
|
|
1122
|
+
const children = [];
|
|
1123
|
+
const childNodes = [];
|
|
1124
|
+
if (ctx.component) {
|
|
1125
|
+
ctx.component.forEach((comp) => {
|
|
1126
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1127
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
if (ctx.layout) {
|
|
1131
|
+
ctx.layout.forEach((layout) => {
|
|
1132
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1133
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1137
|
+
childNodes.forEach((item) => {
|
|
1138
|
+
children.push(this.visit(item.node));
|
|
1139
|
+
});
|
|
1140
|
+
return { type: "tab", children };
|
|
1141
|
+
}
|
|
1142
|
+
body(ctx) {
|
|
1143
|
+
const children = [];
|
|
1144
|
+
const childNodes = [];
|
|
1145
|
+
if (ctx.component) {
|
|
1146
|
+
ctx.component.forEach((comp) => {
|
|
1147
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1148
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
if (ctx.layout) {
|
|
1152
|
+
ctx.layout.forEach((layout) => {
|
|
1153
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1154
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1158
|
+
childNodes.forEach((item) => {
|
|
1159
|
+
children.push(this.visit(item.node));
|
|
1160
|
+
});
|
|
1161
|
+
return { type: "modal-body", children };
|
|
1162
|
+
}
|
|
1163
|
+
footer(ctx) {
|
|
1164
|
+
const children = [];
|
|
1165
|
+
const childNodes = [];
|
|
1166
|
+
if (ctx.component) {
|
|
1167
|
+
ctx.component.forEach((comp) => {
|
|
1168
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1169
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
if (ctx.layout) {
|
|
1173
|
+
ctx.layout.forEach((layout) => {
|
|
1174
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1175
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1179
|
+
childNodes.forEach((item) => {
|
|
1180
|
+
children.push(this.visit(item.node));
|
|
1181
|
+
});
|
|
1182
|
+
return { type: "modal-footer", children };
|
|
1183
|
+
}
|
|
881
1184
|
component(ctx) {
|
|
882
1185
|
const componentType = ctx.componentType[0].image;
|
|
883
1186
|
const props = {};
|
|
1187
|
+
const events = [];
|
|
884
1188
|
if (ctx.property) {
|
|
885
1189
|
ctx.property.forEach((prop) => {
|
|
886
1190
|
const result = this.visit(prop);
|
|
887
|
-
|
|
1191
|
+
if (result.isEvent) {
|
|
1192
|
+
events.push({ event: result.key, actions: result.actions });
|
|
1193
|
+
} else {
|
|
1194
|
+
props[result.key] = result.value;
|
|
1195
|
+
}
|
|
888
1196
|
});
|
|
889
1197
|
}
|
|
890
1198
|
return {
|
|
891
1199
|
type: "component",
|
|
892
1200
|
componentType,
|
|
893
|
-
props
|
|
1201
|
+
props,
|
|
1202
|
+
events
|
|
894
1203
|
};
|
|
895
1204
|
}
|
|
896
1205
|
property(ctx) {
|
|
897
1206
|
const key = ctx.propKey[0].image;
|
|
1207
|
+
if (ctx.actionChain && ctx.actionChain.length > 0) {
|
|
1208
|
+
const actions = this.visit(ctx.actionChain[0]);
|
|
1209
|
+
return { key, isEvent: true, actions };
|
|
1210
|
+
}
|
|
1211
|
+
if (ctx.arrayLiteral && ctx.arrayLiteral.length > 0) {
|
|
1212
|
+
const items = this.visit(ctx.arrayLiteral[0]);
|
|
1213
|
+
return { key, value: items };
|
|
1214
|
+
}
|
|
898
1215
|
const rawValue = ctx.propValue[0].image;
|
|
899
1216
|
let value = rawValue;
|
|
900
1217
|
if (typeof rawValue === "string" && rawValue.startsWith('"')) {
|
|
@@ -904,15 +1221,27 @@ var WireDSLVisitor = class extends BaseCstVisitor {
|
|
|
904
1221
|
}
|
|
905
1222
|
return { key, value };
|
|
906
1223
|
}
|
|
1224
|
+
arrayLiteral(ctx) {
|
|
1225
|
+
if (!ctx.arrayItem) return [];
|
|
1226
|
+
return ctx.arrayItem.map((token) => {
|
|
1227
|
+
const raw = token.image;
|
|
1228
|
+
return raw.startsWith('"') ? raw.slice(1, -1) : raw;
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
907
1231
|
paramList(ctx) {
|
|
908
1232
|
const params = {};
|
|
1233
|
+
const events = [];
|
|
909
1234
|
if (ctx.property) {
|
|
910
1235
|
ctx.property.forEach((prop) => {
|
|
911
1236
|
const result = this.visit(prop);
|
|
912
|
-
|
|
1237
|
+
if (result.isEvent) {
|
|
1238
|
+
events.push({ event: result.key, actions: result.actions });
|
|
1239
|
+
} else {
|
|
1240
|
+
params[result.key] = result.value;
|
|
1241
|
+
}
|
|
913
1242
|
});
|
|
914
1243
|
}
|
|
915
|
-
return params;
|
|
1244
|
+
return { params, events };
|
|
916
1245
|
}
|
|
917
1246
|
};
|
|
918
1247
|
var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
@@ -987,7 +1316,7 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
987
1316
|
return ast;
|
|
988
1317
|
}
|
|
989
1318
|
screen(ctx) {
|
|
990
|
-
const params = ctx.paramList ? this.visit(ctx.paramList[0]) : {};
|
|
1319
|
+
const { params } = ctx.paramList ? this.visit(ctx.paramList[0]) : { params: {} };
|
|
991
1320
|
const screenName = ctx.screenName[0].image;
|
|
992
1321
|
const tokens = {
|
|
993
1322
|
keyword: ctx.Screen[0],
|
|
@@ -1020,9 +1349,11 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1020
1349
|
layout(ctx) {
|
|
1021
1350
|
const layoutType = ctx.layoutType[0].image;
|
|
1022
1351
|
const params = {};
|
|
1352
|
+
const events = [];
|
|
1023
1353
|
if (ctx.paramList) {
|
|
1024
1354
|
const paramResult = this.visit(ctx.paramList);
|
|
1025
|
-
Object.assign(params, paramResult);
|
|
1355
|
+
Object.assign(params, paramResult.params);
|
|
1356
|
+
events.push(...paramResult.events);
|
|
1026
1357
|
}
|
|
1027
1358
|
const tokens = {
|
|
1028
1359
|
keyword: ctx.Layout[0],
|
|
@@ -1034,6 +1365,7 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1034
1365
|
type: "layout",
|
|
1035
1366
|
layoutType,
|
|
1036
1367
|
params,
|
|
1368
|
+
events,
|
|
1037
1369
|
children: []
|
|
1038
1370
|
// Will be filled after push
|
|
1039
1371
|
};
|
|
@@ -1047,13 +1379,21 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1047
1379
|
if (ctx.paramList && ctx.paramList[0]?.children?.property) {
|
|
1048
1380
|
ctx.paramList[0].children.property.forEach((propCtx) => {
|
|
1049
1381
|
const propResult = this.visit(propCtx);
|
|
1382
|
+
if (propResult.isEvent) {
|
|
1383
|
+
this.sourceMapBuilder.addEvent(nodeId, propResult.key, {
|
|
1384
|
+
name: propCtx.children.propKey[0],
|
|
1385
|
+
value: propCtx.children.actionChain[0]
|
|
1386
|
+
});
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
const valueToken = propCtx.children.propValue?.[0] ?? propCtx.children.arrayLiteral?.[0];
|
|
1050
1390
|
this.sourceMapBuilder.addProperty(
|
|
1051
1391
|
nodeId,
|
|
1052
1392
|
propResult.key,
|
|
1053
1393
|
propResult.value,
|
|
1054
1394
|
{
|
|
1055
1395
|
name: propCtx.children.propKey[0],
|
|
1056
|
-
value:
|
|
1396
|
+
value: valueToken
|
|
1057
1397
|
}
|
|
1058
1398
|
);
|
|
1059
1399
|
});
|
|
@@ -1079,6 +1419,24 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1079
1419
|
childNodes.push({ type: "cell", node: cell, index: startToken.startOffset });
|
|
1080
1420
|
});
|
|
1081
1421
|
}
|
|
1422
|
+
if (ctx.tab) {
|
|
1423
|
+
ctx.tab.forEach((tab) => {
|
|
1424
|
+
const startToken = tab.children?.Tab?.[0];
|
|
1425
|
+
childNodes.push({ type: "tab", node: tab, index: startToken.startOffset });
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
if (ctx.body) {
|
|
1429
|
+
ctx.body.forEach((body) => {
|
|
1430
|
+
const startToken = body.children?.Body?.[0];
|
|
1431
|
+
childNodes.push({ type: "body", node: body, index: startToken.startOffset });
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
if (ctx.footer) {
|
|
1435
|
+
ctx.footer.forEach((footer) => {
|
|
1436
|
+
const startToken = footer.children?.Footer?.[0];
|
|
1437
|
+
childNodes.push({ type: "footer", node: footer, index: startToken.startOffset });
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1082
1440
|
childNodes.sort((a, b) => a.index - b.index);
|
|
1083
1441
|
childNodes.forEach((item) => {
|
|
1084
1442
|
ast.children.push(this.visit(item.node));
|
|
@@ -1116,13 +1474,21 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1116
1474
|
if (ctx.property) {
|
|
1117
1475
|
ctx.property.forEach((propCtx) => {
|
|
1118
1476
|
const propResult = this.visit(propCtx);
|
|
1477
|
+
if (propResult.isEvent) {
|
|
1478
|
+
this.sourceMapBuilder.addEvent(nodeId, propResult.key, {
|
|
1479
|
+
name: propCtx.children.propKey[0],
|
|
1480
|
+
value: propCtx.children.actionChain[0]
|
|
1481
|
+
});
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
const valueToken = propCtx.children.propValue?.[0] ?? propCtx.children.arrayLiteral?.[0];
|
|
1119
1485
|
this.sourceMapBuilder.addProperty(
|
|
1120
1486
|
nodeId,
|
|
1121
1487
|
propResult.key,
|
|
1122
1488
|
propResult.value,
|
|
1123
1489
|
{
|
|
1124
1490
|
name: propCtx.children.propKey[0],
|
|
1125
|
-
value:
|
|
1491
|
+
value: valueToken
|
|
1126
1492
|
}
|
|
1127
1493
|
);
|
|
1128
1494
|
});
|
|
@@ -1151,6 +1517,114 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1151
1517
|
}
|
|
1152
1518
|
return ast;
|
|
1153
1519
|
}
|
|
1520
|
+
body(ctx) {
|
|
1521
|
+
const tokens = {
|
|
1522
|
+
keyword: ctx.Body[0],
|
|
1523
|
+
body: ctx.RCurly[0]
|
|
1524
|
+
};
|
|
1525
|
+
const ast = {
|
|
1526
|
+
type: "modal-body",
|
|
1527
|
+
children: []
|
|
1528
|
+
};
|
|
1529
|
+
if (this.sourceMapBuilder) {
|
|
1530
|
+
const nodeId = this.sourceMapBuilder.addNode("modal-body", tokens);
|
|
1531
|
+
ast._meta = { nodeId };
|
|
1532
|
+
this.sourceMapBuilder.pushParent(nodeId);
|
|
1533
|
+
}
|
|
1534
|
+
const childNodes = [];
|
|
1535
|
+
if (ctx.component) {
|
|
1536
|
+
ctx.component.forEach((comp) => {
|
|
1537
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1538
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
if (ctx.layout) {
|
|
1542
|
+
ctx.layout.forEach((layout) => {
|
|
1543
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1544
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1548
|
+
childNodes.forEach((item) => {
|
|
1549
|
+
ast.children.push(this.visit(item.node));
|
|
1550
|
+
});
|
|
1551
|
+
if (this.sourceMapBuilder) {
|
|
1552
|
+
this.sourceMapBuilder.popParent();
|
|
1553
|
+
}
|
|
1554
|
+
return ast;
|
|
1555
|
+
}
|
|
1556
|
+
tab(ctx) {
|
|
1557
|
+
const tokens = {
|
|
1558
|
+
keyword: ctx.Tab[0],
|
|
1559
|
+
body: ctx.RCurly[0]
|
|
1560
|
+
};
|
|
1561
|
+
const ast = {
|
|
1562
|
+
type: "tab",
|
|
1563
|
+
children: []
|
|
1564
|
+
};
|
|
1565
|
+
if (this.sourceMapBuilder) {
|
|
1566
|
+
const nodeId = this.sourceMapBuilder.addNode("tab", tokens);
|
|
1567
|
+
ast._meta = { nodeId };
|
|
1568
|
+
this.sourceMapBuilder.pushParent(nodeId);
|
|
1569
|
+
}
|
|
1570
|
+
const childNodes = [];
|
|
1571
|
+
if (ctx.component) {
|
|
1572
|
+
ctx.component.forEach((comp) => {
|
|
1573
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1574
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
if (ctx.layout) {
|
|
1578
|
+
ctx.layout.forEach((layout) => {
|
|
1579
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1580
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1584
|
+
childNodes.forEach((item) => {
|
|
1585
|
+
ast.children.push(this.visit(item.node));
|
|
1586
|
+
});
|
|
1587
|
+
if (this.sourceMapBuilder) {
|
|
1588
|
+
this.sourceMapBuilder.popParent();
|
|
1589
|
+
}
|
|
1590
|
+
return ast;
|
|
1591
|
+
}
|
|
1592
|
+
footer(ctx) {
|
|
1593
|
+
const tokens = {
|
|
1594
|
+
keyword: ctx.Footer[0],
|
|
1595
|
+
body: ctx.RCurly[0]
|
|
1596
|
+
};
|
|
1597
|
+
const ast = {
|
|
1598
|
+
type: "modal-footer",
|
|
1599
|
+
children: []
|
|
1600
|
+
};
|
|
1601
|
+
if (this.sourceMapBuilder) {
|
|
1602
|
+
const nodeId = this.sourceMapBuilder.addNode("modal-footer", tokens);
|
|
1603
|
+
ast._meta = { nodeId };
|
|
1604
|
+
this.sourceMapBuilder.pushParent(nodeId);
|
|
1605
|
+
}
|
|
1606
|
+
const childNodes = [];
|
|
1607
|
+
if (ctx.component) {
|
|
1608
|
+
ctx.component.forEach((comp) => {
|
|
1609
|
+
const startToken = comp.children?.Component?.[0] || comp.children?.componentType?.[0];
|
|
1610
|
+
childNodes.push({ type: "component", node: comp, index: startToken.startOffset });
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
if (ctx.layout) {
|
|
1614
|
+
ctx.layout.forEach((layout) => {
|
|
1615
|
+
const startToken = layout.children?.Layout?.[0] || layout.children?.layoutType?.[0];
|
|
1616
|
+
childNodes.push({ type: "layout", node: layout, index: startToken.startOffset });
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
childNodes.sort((a, b) => a.index - b.index);
|
|
1620
|
+
childNodes.forEach((item) => {
|
|
1621
|
+
ast.children.push(this.visit(item.node));
|
|
1622
|
+
});
|
|
1623
|
+
if (this.sourceMapBuilder) {
|
|
1624
|
+
this.sourceMapBuilder.popParent();
|
|
1625
|
+
}
|
|
1626
|
+
return ast;
|
|
1627
|
+
}
|
|
1154
1628
|
component(ctx) {
|
|
1155
1629
|
const tokens = {
|
|
1156
1630
|
keyword: ctx.Component[0],
|
|
@@ -1172,13 +1646,21 @@ var WireDSLVisitorWithSourceMap = class extends WireDSLVisitor {
|
|
|
1172
1646
|
if (ctx.property) {
|
|
1173
1647
|
ctx.property.forEach((propCtx) => {
|
|
1174
1648
|
const propResult = this.visit(propCtx);
|
|
1649
|
+
if (propResult.isEvent) {
|
|
1650
|
+
this.sourceMapBuilder.addEvent(nodeId, propResult.key, {
|
|
1651
|
+
name: propCtx.children.propKey[0],
|
|
1652
|
+
value: propCtx.children.actionChain[0]
|
|
1653
|
+
});
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
const valueToken = propCtx.children.propValue?.[0] ?? propCtx.children.arrayLiteral?.[0];
|
|
1175
1657
|
this.sourceMapBuilder.addProperty(
|
|
1176
1658
|
nodeId,
|
|
1177
1659
|
propResult.key,
|
|
1178
1660
|
propResult.value,
|
|
1179
1661
|
{
|
|
1180
1662
|
name: propCtx.children.propKey[0],
|
|
1181
|
-
value:
|
|
1663
|
+
value: valueToken
|
|
1182
1664
|
}
|
|
1183
1665
|
);
|
|
1184
1666
|
});
|
|
@@ -1525,11 +2007,13 @@ function createParserDiagnostic(error) {
|
|
|
1525
2007
|
};
|
|
1526
2008
|
}
|
|
1527
2009
|
function isBooleanLike(value) {
|
|
2010
|
+
if (Array.isArray(value)) return false;
|
|
1528
2011
|
if (typeof value === "number") return value === 0 || value === 1;
|
|
1529
2012
|
const normalized = String(value).trim().toLowerCase();
|
|
1530
2013
|
return normalized === "true" || normalized === "false";
|
|
1531
2014
|
}
|
|
1532
2015
|
function parseBooleanLike(value, fallback = false) {
|
|
2016
|
+
if (Array.isArray(value)) return fallback;
|
|
1533
2017
|
if (typeof value === "number") {
|
|
1534
2018
|
if (value === 1) return true;
|
|
1535
2019
|
if (value === 0) return false;
|
|
@@ -1586,6 +2070,14 @@ function validateSemanticDiagnostics(ast, sourceMap) {
|
|
|
1586
2070
|
count += countChildrenSlots(cellChild);
|
|
1587
2071
|
}
|
|
1588
2072
|
}
|
|
2073
|
+
} else if (child.type === "tab") {
|
|
2074
|
+
for (const tabChild of child.children) {
|
|
2075
|
+
if (tabChild.type === "component") {
|
|
2076
|
+
if (tabChild.componentType === "Children") count += 1;
|
|
2077
|
+
} else if (tabChild.type === "layout") {
|
|
2078
|
+
count += countChildrenSlots(tabChild);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
1589
2081
|
}
|
|
1590
2082
|
}
|
|
1591
2083
|
return count;
|
|
@@ -1832,6 +2324,11 @@ function validateSemanticDiagnostics(ast, sourceMap) {
|
|
|
1832
2324
|
checkLayout(child, insideDefinedLayout);
|
|
1833
2325
|
} else if (child.type === "cell") {
|
|
1834
2326
|
checkCell(child, insideDefinedLayout);
|
|
2327
|
+
} else if (child.type === "tab") {
|
|
2328
|
+
for (const tabChild of child.children) {
|
|
2329
|
+
if (tabChild.type === "component") checkComponent(tabChild, insideDefinedLayout);
|
|
2330
|
+
if (tabChild.type === "layout") checkLayout(tabChild, insideDefinedLayout);
|
|
2331
|
+
}
|
|
1835
2332
|
}
|
|
1836
2333
|
}
|
|
1837
2334
|
};
|
|
@@ -2009,6 +2506,16 @@ function validateDefinitionCycles(ast) {
|
|
|
2009
2506
|
collectLayoutDependencies(cellChild, deps);
|
|
2010
2507
|
}
|
|
2011
2508
|
}
|
|
2509
|
+
} else if (child.type === "tab") {
|
|
2510
|
+
for (const tabChild of child.children) {
|
|
2511
|
+
if (tabChild.type === "component") {
|
|
2512
|
+
if (shouldTrackComponentDependency(tabChild.componentType)) {
|
|
2513
|
+
deps.add(makeComponentKey(tabChild.componentType));
|
|
2514
|
+
}
|
|
2515
|
+
} else if (tabChild.type === "layout") {
|
|
2516
|
+
collectLayoutDependencies(tabChild, deps);
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2012
2519
|
}
|
|
2013
2520
|
}
|
|
2014
2521
|
};
|
|
@@ -2076,6 +2583,13 @@ import {
|
|
|
2076
2583
|
LAYOUTS as LAYOUTS2
|
|
2077
2584
|
} from "@wire-dsl/language-support/components";
|
|
2078
2585
|
|
|
2586
|
+
// src/shared/list-utils.ts
|
|
2587
|
+
function toStringArray(value) {
|
|
2588
|
+
if (Array.isArray(value)) return value;
|
|
2589
|
+
if (value === void 0 || value === "") return [];
|
|
2590
|
+
return String(value).split(",").map((s) => s.trim()).filter(Boolean);
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2079
2593
|
// src/ir/device-presets.ts
|
|
2080
2594
|
var DEVICE_PRESETS = {
|
|
2081
2595
|
mobile: {
|
|
@@ -2161,6 +2675,7 @@ function isValidDevice(device) {
|
|
|
2161
2675
|
}
|
|
2162
2676
|
|
|
2163
2677
|
// src/ir/index.ts
|
|
2678
|
+
var SELF_TARGET = "_self";
|
|
2164
2679
|
var IRStyleSchema = z.object({
|
|
2165
2680
|
density: z.enum(["compact", "normal", "comfortable"]),
|
|
2166
2681
|
spacing: z.enum(["xs", "sm", "md", "lg", "xl"]),
|
|
@@ -2182,12 +2697,27 @@ var IRMetaSchema = z.object({
|
|
|
2182
2697
|
source: z.string().optional(),
|
|
2183
2698
|
nodeId: z.string().optional()
|
|
2184
2699
|
});
|
|
2700
|
+
var IREventActionSchema = z.union([
|
|
2701
|
+
z.object({ type: z.literal("navigate"), screen: z.string() }),
|
|
2702
|
+
z.object({ type: z.literal("show"), targetId: z.string() }),
|
|
2703
|
+
z.object({ type: z.literal("hide"), targetId: z.string() }),
|
|
2704
|
+
z.object({ type: z.literal("toggle"), targetId: z.string() }),
|
|
2705
|
+
z.object({ type: z.literal("enable"), targetId: z.string() }),
|
|
2706
|
+
z.object({ type: z.literal("disable"), targetId: z.string() }),
|
|
2707
|
+
z.object({ type: z.literal("setTab"), tabsId: z.string(), index: z.number().int().min(0) }),
|
|
2708
|
+
z.object({ type: z.literal("navigateItems"), screens: z.array(z.string()) })
|
|
2709
|
+
]);
|
|
2710
|
+
var IREventHandlerSchema = z.object({
|
|
2711
|
+
event: z.enum(["onClick", "onChange", "onActive", "onInactive", "onItemsClick", "onItemClick", "onRowClick", "onClose"]),
|
|
2712
|
+
actions: z.array(IREventActionSchema)
|
|
2713
|
+
});
|
|
2185
2714
|
var IRContainerNodeSchema = z.object({
|
|
2186
2715
|
id: z.string(),
|
|
2187
2716
|
kind: z.literal("container"),
|
|
2188
|
-
containerType: z.enum(["stack", "grid", "split", "panel", "card"]),
|
|
2189
|
-
params: z.record(z.string(), z.union([z.string(), z.number()])),
|
|
2717
|
+
containerType: z.enum(["stack", "grid", "split", "panel", "card", "tabs", "tab", "modal", "modal-body", "modal-footer"]),
|
|
2718
|
+
params: z.record(z.string(), z.union([z.string(), z.number(), z.array(z.string())])),
|
|
2190
2719
|
children: z.array(z.object({ ref: z.string() })),
|
|
2720
|
+
events: z.array(IREventHandlerSchema).optional(),
|
|
2191
2721
|
style: IRNodeStyleSchema,
|
|
2192
2722
|
meta: IRMetaSchema
|
|
2193
2723
|
});
|
|
@@ -2195,7 +2725,9 @@ var IRComponentNodeSchema = z.object({
|
|
|
2195
2725
|
id: z.string(),
|
|
2196
2726
|
kind: z.literal("component"),
|
|
2197
2727
|
componentType: z.string(),
|
|
2198
|
-
props: z.record(z.string(), z.union([z.string(), z.number()])),
|
|
2728
|
+
props: z.record(z.string(), z.union([z.string(), z.number(), z.array(z.string())])),
|
|
2729
|
+
userDefinedId: z.string().regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, "ID must match [a-zA-Z_][a-zA-Z0-9_]*").optional(),
|
|
2730
|
+
events: z.array(IREventHandlerSchema).optional(),
|
|
2199
2731
|
style: IRNodeStyleSchema,
|
|
2200
2732
|
meta: IRMetaSchema
|
|
2201
2733
|
});
|
|
@@ -2204,7 +2736,7 @@ var IRInstanceNodeSchema = z.object({
|
|
|
2204
2736
|
kind: z.literal("instance"),
|
|
2205
2737
|
definitionName: z.string(),
|
|
2206
2738
|
definitionKind: z.enum(["component", "layout"]),
|
|
2207
|
-
invocationProps: z.record(z.string(), z.union([z.string(), z.number()])),
|
|
2739
|
+
invocationProps: z.record(z.string(), z.union([z.string(), z.number(), z.array(z.string())])),
|
|
2208
2740
|
expandedRoot: z.object({ ref: z.string() }),
|
|
2209
2741
|
style: IRNodeStyleSchema,
|
|
2210
2742
|
meta: IRMetaSchema
|
|
@@ -2398,25 +2930,29 @@ ${messages}`);
|
|
|
2398
2930
|
if (layout.children && layout.children.length > 0) {
|
|
2399
2931
|
layout.children.forEach((child) => {
|
|
2400
2932
|
if (child.type === "component") {
|
|
2401
|
-
found.push({
|
|
2402
|
-
componentType: child.componentType,
|
|
2403
|
-
location: "layout"
|
|
2404
|
-
});
|
|
2933
|
+
found.push({ componentType: child.componentType, location: "layout" });
|
|
2405
2934
|
} else if (child.type === "layout") {
|
|
2406
2935
|
this.findComponentsInLayout(child, found);
|
|
2407
2936
|
} else if (child.type === "cell") {
|
|
2408
2937
|
if (child.children) {
|
|
2409
2938
|
child.children.forEach((cellChild) => {
|
|
2410
2939
|
if (cellChild.type === "component") {
|
|
2411
|
-
found.push({
|
|
2412
|
-
componentType: cellChild.componentType,
|
|
2413
|
-
location: "cell"
|
|
2414
|
-
});
|
|
2940
|
+
found.push({ componentType: cellChild.componentType, location: "cell" });
|
|
2415
2941
|
} else if (cellChild.type === "layout") {
|
|
2416
2942
|
this.findComponentsInLayout(cellChild, found);
|
|
2417
2943
|
}
|
|
2418
2944
|
});
|
|
2419
2945
|
}
|
|
2946
|
+
} else if (child.type === "tab") {
|
|
2947
|
+
if (child.children) {
|
|
2948
|
+
child.children.forEach((tabChild) => {
|
|
2949
|
+
if (tabChild.type === "component") {
|
|
2950
|
+
found.push({ componentType: tabChild.componentType, location: "tab" });
|
|
2951
|
+
} else if (tabChild.type === "layout") {
|
|
2952
|
+
this.findComponentsInLayout(tabChild, found);
|
|
2953
|
+
}
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2420
2956
|
}
|
|
2421
2957
|
});
|
|
2422
2958
|
}
|
|
@@ -2433,53 +2969,179 @@ ${messages}`);
|
|
|
2433
2969
|
if (layout.layoutType === "split") {
|
|
2434
2970
|
layoutParams = this.normalizeSplitParams(layoutParams);
|
|
2435
2971
|
}
|
|
2436
|
-
const
|
|
2972
|
+
const nonTabChildren = layout.children.filter((c) => c.type !== "tab");
|
|
2437
2973
|
const layoutDefinition = this.definedLayouts.get(layout.layoutType);
|
|
2438
2974
|
if (layoutDefinition) {
|
|
2439
|
-
return this.expandDefinedLayout(layoutDefinition, layoutParams,
|
|
2975
|
+
return this.expandDefinedLayout(layoutDefinition, layoutParams, nonTabChildren, context, layout._meta?.nodeId);
|
|
2976
|
+
}
|
|
2977
|
+
const nodeId = this.idGen.generate("node");
|
|
2978
|
+
const childRefs = [];
|
|
2979
|
+
if (layout.layoutType === "modal") {
|
|
2980
|
+
const bodyChildren = layout.children.filter((c) => c.type === "modal-body");
|
|
2981
|
+
const footerChildren = layout.children.filter((c) => c.type === "modal-footer");
|
|
2982
|
+
const normalChildren = layout.children.filter((c) => c.type !== "modal-body" && c.type !== "modal-footer");
|
|
2983
|
+
if (bodyChildren.length > 1 || footerChildren.length > 1) {
|
|
2984
|
+
if (bodyChildren.length > 1) {
|
|
2985
|
+
this.warnings.push({
|
|
2986
|
+
type: "modal-003-duplicate-body",
|
|
2987
|
+
message: "MODAL-003: A modal can only have one body section."
|
|
2988
|
+
});
|
|
2989
|
+
}
|
|
2990
|
+
if (footerChildren.length > 1) {
|
|
2991
|
+
this.warnings.push({
|
|
2992
|
+
type: "modal-004-duplicate-footer",
|
|
2993
|
+
message: "MODAL-004: A modal can only have one footer section."
|
|
2994
|
+
});
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
if ((bodyChildren.length > 0 || footerChildren.length > 0) && normalChildren.length > 0) {
|
|
2998
|
+
this.warnings.push({
|
|
2999
|
+
type: "modal-002-mixed-children",
|
|
3000
|
+
message: "MODAL-002: Cannot mix body/footer sections with direct children in a modal. Use either body/footer sections or direct children, not both."
|
|
3001
|
+
});
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
for (const child of layout.children) {
|
|
3005
|
+
if (child.type === "modal-body" || child.type === "modal-footer") {
|
|
3006
|
+
if (layout.layoutType !== "modal") {
|
|
3007
|
+
this.warnings.push({
|
|
3008
|
+
type: "modal-001-invalid-context",
|
|
3009
|
+
message: `MODAL-001: "${child.type}" sections are only valid inside layout modal.`
|
|
3010
|
+
});
|
|
3011
|
+
continue;
|
|
3012
|
+
}
|
|
3013
|
+
const childId = child.type === "modal-body" ? this.convertModalBody(child, context) : this.convertModalFooter(child, context);
|
|
3014
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3015
|
+
} else if (child.type === "layout") {
|
|
3016
|
+
const childId = this.convertLayout(child, context);
|
|
3017
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3018
|
+
} else if (child.type === "component") {
|
|
3019
|
+
const childId = this.convertComponent(child, context);
|
|
3020
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3021
|
+
} else if (child.type === "cell") {
|
|
3022
|
+
const childId = this.convertCell(child, context);
|
|
3023
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3024
|
+
} else if (child.type === "tab") {
|
|
3025
|
+
const childId = this.convertTab(child, context);
|
|
3026
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
const style = {};
|
|
3030
|
+
if (layoutParams.padding !== void 0) {
|
|
3031
|
+
style.padding = String(layoutParams.padding);
|
|
3032
|
+
} else {
|
|
3033
|
+
style.padding = "none";
|
|
3034
|
+
}
|
|
3035
|
+
if (layoutParams.gap !== void 0) {
|
|
3036
|
+
style.gap = String(layoutParams.gap);
|
|
3037
|
+
}
|
|
3038
|
+
if (layoutParams.justify !== void 0) {
|
|
3039
|
+
style.justify = layoutParams.justify;
|
|
3040
|
+
}
|
|
3041
|
+
if (layoutParams.align !== void 0) {
|
|
3042
|
+
style.align = layoutParams.align;
|
|
3043
|
+
}
|
|
3044
|
+
if (layoutParams.background !== void 0) {
|
|
3045
|
+
style.background = String(layoutParams.background);
|
|
3046
|
+
}
|
|
3047
|
+
if (layoutParams.id !== void 0) {
|
|
3048
|
+
const layoutId = String(layoutParams.id);
|
|
3049
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(layoutId)) {
|
|
3050
|
+
this.errors.push({
|
|
3051
|
+
type: "evt-009-invalid-id",
|
|
3052
|
+
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).`
|
|
3053
|
+
});
|
|
3054
|
+
}
|
|
2440
3055
|
}
|
|
3056
|
+
const irEvents = this.convertASTEvents(layout.events || []);
|
|
3057
|
+
const containerNode = {
|
|
3058
|
+
id: nodeId,
|
|
3059
|
+
kind: "container",
|
|
3060
|
+
containerType: layout.layoutType,
|
|
3061
|
+
params: this.cleanParams(layoutParams),
|
|
3062
|
+
children: childRefs,
|
|
3063
|
+
...irEvents.length > 0 ? { events: irEvents } : {},
|
|
3064
|
+
style,
|
|
3065
|
+
meta: {
|
|
3066
|
+
nodeId: context?.instanceScope ? `${layout._meta?.nodeId}@${context.instanceScope}` : layout._meta?.nodeId
|
|
3067
|
+
}
|
|
3068
|
+
};
|
|
3069
|
+
this.nodes[nodeId] = containerNode;
|
|
3070
|
+
return nodeId;
|
|
3071
|
+
}
|
|
3072
|
+
convertTab(tab, context) {
|
|
3073
|
+
const nodeId = this.idGen.generate("node");
|
|
3074
|
+
const childRefs = [];
|
|
3075
|
+
for (const child of tab.children) {
|
|
3076
|
+
if (child.type === "layout") {
|
|
3077
|
+
const childId = this.convertLayout(child, context);
|
|
3078
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3079
|
+
} else if (child.type === "component") {
|
|
3080
|
+
const childId = this.convertComponent(child, context);
|
|
3081
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
const containerNode = {
|
|
3085
|
+
id: nodeId,
|
|
3086
|
+
kind: "container",
|
|
3087
|
+
containerType: "tab",
|
|
3088
|
+
params: {},
|
|
3089
|
+
children: childRefs,
|
|
3090
|
+
style: { padding: "none" },
|
|
3091
|
+
meta: {
|
|
3092
|
+
nodeId: context?.instanceScope ? `${tab._meta?.nodeId}@${context.instanceScope}` : tab._meta?.nodeId
|
|
3093
|
+
}
|
|
3094
|
+
};
|
|
3095
|
+
this.nodes[nodeId] = containerNode;
|
|
3096
|
+
return nodeId;
|
|
3097
|
+
}
|
|
3098
|
+
convertModalBody(body, context) {
|
|
2441
3099
|
const nodeId = this.idGen.generate("node");
|
|
2442
3100
|
const childRefs = [];
|
|
2443
|
-
for (const child of
|
|
3101
|
+
for (const child of body.children) {
|
|
2444
3102
|
if (child.type === "layout") {
|
|
2445
3103
|
const childId = this.convertLayout(child, context);
|
|
2446
3104
|
if (childId) childRefs.push({ ref: childId });
|
|
2447
3105
|
} else if (child.type === "component") {
|
|
2448
3106
|
const childId = this.convertComponent(child, context);
|
|
2449
3107
|
if (childId) childRefs.push({ ref: childId });
|
|
2450
|
-
} else if (child.type === "cell") {
|
|
2451
|
-
const childId = this.convertCell(child, context);
|
|
2452
|
-
if (childId) childRefs.push({ ref: childId });
|
|
2453
3108
|
}
|
|
2454
3109
|
}
|
|
2455
|
-
const
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
3110
|
+
const containerNode = {
|
|
3111
|
+
id: nodeId,
|
|
3112
|
+
kind: "container",
|
|
3113
|
+
containerType: "modal-body",
|
|
3114
|
+
params: { direction: "vertical" },
|
|
3115
|
+
children: childRefs,
|
|
3116
|
+
style: { padding: "md", gap: "md" },
|
|
3117
|
+
meta: {
|
|
3118
|
+
nodeId: context?.instanceScope ? `${body._meta?.nodeId}@${context.instanceScope}` : body._meta?.nodeId
|
|
3119
|
+
}
|
|
3120
|
+
};
|
|
3121
|
+
this.nodes[nodeId] = containerNode;
|
|
3122
|
+
return nodeId;
|
|
3123
|
+
}
|
|
3124
|
+
convertModalFooter(footer, context) {
|
|
3125
|
+
const nodeId = this.idGen.generate("node");
|
|
3126
|
+
const childRefs = [];
|
|
3127
|
+
for (const child of footer.children) {
|
|
3128
|
+
if (child.type === "layout") {
|
|
3129
|
+
const childId = this.convertLayout(child, context);
|
|
3130
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3131
|
+
} else if (child.type === "component") {
|
|
3132
|
+
const childId = this.convertComponent(child, context);
|
|
3133
|
+
if (childId) childRefs.push({ ref: childId });
|
|
3134
|
+
}
|
|
2472
3135
|
}
|
|
2473
3136
|
const containerNode = {
|
|
2474
3137
|
id: nodeId,
|
|
2475
3138
|
kind: "container",
|
|
2476
|
-
containerType:
|
|
2477
|
-
params:
|
|
3139
|
+
containerType: "modal-footer",
|
|
3140
|
+
params: { direction: "horizontal" },
|
|
2478
3141
|
children: childRefs,
|
|
2479
|
-
style,
|
|
3142
|
+
style: { padding: "md", justify: "spaceBetween" },
|
|
2480
3143
|
meta: {
|
|
2481
|
-
|
|
2482
|
-
nodeId: context?.instanceScope ? `${layout._meta?.nodeId}@${context.instanceScope}` : layout._meta?.nodeId
|
|
3144
|
+
nodeId: context?.instanceScope ? `${footer._meta?.nodeId}@${context.instanceScope}` : footer._meta?.nodeId
|
|
2483
3145
|
}
|
|
2484
3146
|
};
|
|
2485
3147
|
this.nodes[nodeId] = containerNode;
|
|
@@ -2562,7 +3224,6 @@ ${messages}`);
|
|
|
2562
3224
|
"IconButton",
|
|
2563
3225
|
"Alert",
|
|
2564
3226
|
"Badge",
|
|
2565
|
-
"Modal",
|
|
2566
3227
|
"List",
|
|
2567
3228
|
"Sidebar",
|
|
2568
3229
|
"Tabs",
|
|
@@ -2574,20 +3235,70 @@ ${messages}`);
|
|
|
2574
3235
|
this.undefinedComponentsUsed.add(component.componentType);
|
|
2575
3236
|
}
|
|
2576
3237
|
const nodeId = this.idGen.generate("node");
|
|
3238
|
+
const rawId = resolvedProps.id !== void 0 ? String(resolvedProps.id) : void 0;
|
|
3239
|
+
if (rawId !== void 0 && !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(rawId)) {
|
|
3240
|
+
this.errors.push({
|
|
3241
|
+
type: "evt-009-invalid-id",
|
|
3242
|
+
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).`
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
3245
|
+
const userDefinedId = rawId;
|
|
3246
|
+
const propsWithoutId = { ...resolvedProps };
|
|
3247
|
+
delete propsWithoutId.id;
|
|
3248
|
+
const irEvents = this.convertASTEvents(component.events || []);
|
|
3249
|
+
const onItemsClickEvent = this.extractOnItemsClickEvent(propsWithoutId);
|
|
3250
|
+
if (onItemsClickEvent) {
|
|
3251
|
+
irEvents.push(onItemsClickEvent);
|
|
3252
|
+
delete propsWithoutId.onItemsClick;
|
|
3253
|
+
}
|
|
2577
3254
|
const componentNode = {
|
|
2578
3255
|
id: nodeId,
|
|
2579
3256
|
kind: "component",
|
|
2580
3257
|
componentType: component.componentType,
|
|
2581
|
-
props:
|
|
3258
|
+
props: propsWithoutId,
|
|
3259
|
+
...userDefinedId ? { userDefinedId } : {},
|
|
3260
|
+
...irEvents.length > 0 ? { events: irEvents } : {},
|
|
2582
3261
|
style: {},
|
|
2583
3262
|
meta: {
|
|
2584
|
-
// Scope nodeId per instance so each expansion gets a unique identifier
|
|
2585
3263
|
nodeId: context?.instanceScope ? `${component._meta?.nodeId}@${context.instanceScope}` : component._meta?.nodeId
|
|
2586
3264
|
}
|
|
2587
3265
|
};
|
|
2588
3266
|
this.nodes[nodeId] = componentNode;
|
|
2589
3267
|
return nodeId;
|
|
2590
3268
|
}
|
|
3269
|
+
convertASTEventAction(action) {
|
|
3270
|
+
switch (action.type) {
|
|
3271
|
+
case "navigate":
|
|
3272
|
+
return { type: "navigate", screen: action.screen };
|
|
3273
|
+
case "show":
|
|
3274
|
+
return { type: "show", targetId: action.targetId };
|
|
3275
|
+
case "hide":
|
|
3276
|
+
return { type: "hide", targetId: action.targetId };
|
|
3277
|
+
case "toggle":
|
|
3278
|
+
return { type: "toggle", targetId: action.targetId };
|
|
3279
|
+
case "enable":
|
|
3280
|
+
return { type: "enable", targetId: action.targetId };
|
|
3281
|
+
case "disable":
|
|
3282
|
+
return { type: "disable", targetId: action.targetId };
|
|
3283
|
+
case "setTab":
|
|
3284
|
+
return { type: "setTab", tabsId: action.tabsId, index: action.index };
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
convertASTEvents(events) {
|
|
3288
|
+
return events.map((handler) => ({
|
|
3289
|
+
event: handler.event,
|
|
3290
|
+
actions: handler.actions.map((a) => this.convertASTEventAction(a))
|
|
3291
|
+
}));
|
|
3292
|
+
}
|
|
3293
|
+
extractOnItemsClickEvent(props) {
|
|
3294
|
+
const value = props.onItemsClick;
|
|
3295
|
+
if (!value) return null;
|
|
3296
|
+
const screens = toStringArray(value);
|
|
3297
|
+
return {
|
|
3298
|
+
event: "onItemsClick",
|
|
3299
|
+
actions: [{ type: "navigateItems", screens }]
|
|
3300
|
+
};
|
|
3301
|
+
}
|
|
2591
3302
|
expandDefinedComponent(definition, invocationArgs, callSiteNodeId, parentContext) {
|
|
2592
3303
|
const context = {
|
|
2593
3304
|
args: invocationArgs,
|
|
@@ -2767,21 +3478,23 @@ ${messages}`);
|
|
|
2767
3478
|
key
|
|
2768
3479
|
);
|
|
2769
3480
|
if (resolvedValue !== void 0) {
|
|
3481
|
+
const metadata = COMPONENTS2[componentType];
|
|
3482
|
+
const propMeta = metadata?.properties?.[key];
|
|
2770
3483
|
const wasPropReference = typeof value === "string" && value.startsWith("prop_");
|
|
2771
|
-
if (wasPropReference) {
|
|
2772
|
-
const
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
type: "invalid-bound-enum-value",
|
|
2779
|
-
message: `Invalid value "${normalizedValue}" for property "${key}" in component "${componentType}". Expected one of: ${property.options.join(", ")}.`
|
|
2780
|
-
});
|
|
2781
|
-
}
|
|
3484
|
+
if (wasPropReference && propMeta?.type === "enum" && Array.isArray(propMeta.options)) {
|
|
3485
|
+
const normalizedValue = String(resolvedValue);
|
|
3486
|
+
if (!propMeta.options.includes(normalizedValue)) {
|
|
3487
|
+
this.warnings.push({
|
|
3488
|
+
type: "invalid-bound-enum-value",
|
|
3489
|
+
message: `Invalid value "${normalizedValue}" for property "${key}" in component "${componentType}". Expected one of: ${propMeta.options.join(", ")}.`
|
|
3490
|
+
});
|
|
2782
3491
|
}
|
|
2783
3492
|
}
|
|
2784
|
-
|
|
3493
|
+
if (propMeta?.type === "list" && !Array.isArray(resolvedValue)) {
|
|
3494
|
+
resolved[key] = toStringArray(resolvedValue);
|
|
3495
|
+
} else {
|
|
3496
|
+
resolved[key] = resolvedValue;
|
|
3497
|
+
}
|
|
2785
3498
|
}
|
|
2786
3499
|
}
|
|
2787
3500
|
return resolved;
|
|
@@ -2853,6 +3566,195 @@ function generateIR(ast) {
|
|
|
2853
3566
|
return generator.generate(ast);
|
|
2854
3567
|
}
|
|
2855
3568
|
|
|
3569
|
+
// src/state.ts
|
|
3570
|
+
function applyStateChange(ir, change, originNodeId) {
|
|
3571
|
+
switch (change.type) {
|
|
3572
|
+
case "setVisible":
|
|
3573
|
+
return applySetVisible(ir, change.targetId, change.visible, originNodeId);
|
|
3574
|
+
case "toggleVisible":
|
|
3575
|
+
return applyToggleVisible(ir, change.targetId, originNodeId);
|
|
3576
|
+
case "setActiveTab":
|
|
3577
|
+
return applySetActiveTab(ir, change.tabsId, change.index);
|
|
3578
|
+
case "setChecked":
|
|
3579
|
+
return applySetBooleanProp(ir, change.targetId, "checked", change.checked, originNodeId);
|
|
3580
|
+
case "toggleChecked":
|
|
3581
|
+
return applyToggleBooleanProp(ir, change.targetId, "checked", originNodeId);
|
|
3582
|
+
case "setEnabled":
|
|
3583
|
+
return applySetBooleanProp(ir, change.targetId, "enabled", change.enabled, originNodeId);
|
|
3584
|
+
case "toggleEnabled":
|
|
3585
|
+
return applyToggleBooleanProp(ir, change.targetId, "enabled", originNodeId);
|
|
3586
|
+
case "setDisabled":
|
|
3587
|
+
return applySetBooleanProp(ir, change.targetId, "disabled", change.disabled, originNodeId);
|
|
3588
|
+
case "navigateTo":
|
|
3589
|
+
return applyNavigateTo(ir, change.screen);
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
function applySetVisible(ir, targetId, visible, originNodeId) {
|
|
3593
|
+
const resolvedId = resolveTargetId(ir, targetId, originNodeId);
|
|
3594
|
+
if (!resolvedId) return ir;
|
|
3595
|
+
const nodes = mutateNodeVisible(ir.project.nodes, resolvedId, visible);
|
|
3596
|
+
return { ...ir, project: { ...ir.project, nodes } };
|
|
3597
|
+
}
|
|
3598
|
+
function applyToggleVisible(ir, targetId, originNodeId) {
|
|
3599
|
+
const resolvedId = resolveTargetId(ir, targetId, originNodeId);
|
|
3600
|
+
if (!resolvedId) return ir;
|
|
3601
|
+
const targetNodeEntry = findNodeByUserDefinedId(ir.project.nodes, resolvedId) || (resolvedId === originNodeId ? findNodeByMetaNodeId(ir.project.nodes, resolvedId) : null);
|
|
3602
|
+
const targetContainerEntry = !targetNodeEntry ? Object.values(ir.project.nodes).find(
|
|
3603
|
+
(n) => n.kind === "container" && (String(n.params.id) === resolvedId || n.meta.nodeId === resolvedId)
|
|
3604
|
+
) : void 0;
|
|
3605
|
+
let currentVisible = true;
|
|
3606
|
+
if (targetNodeEntry?.kind === "component") {
|
|
3607
|
+
currentVisible = targetNodeEntry.props?.visible !== "false";
|
|
3608
|
+
} else if (targetContainerEntry?.kind === "container") {
|
|
3609
|
+
currentVisible = String(targetContainerEntry.params.visible) !== "false";
|
|
3610
|
+
}
|
|
3611
|
+
const nodes = mutateNodeVisible(ir.project.nodes, resolvedId, !currentVisible);
|
|
3612
|
+
return { ...ir, project: { ...ir.project, nodes } };
|
|
3613
|
+
}
|
|
3614
|
+
function applySetActiveTab(ir, tabsId, index) {
|
|
3615
|
+
let found = false;
|
|
3616
|
+
const nodes = {};
|
|
3617
|
+
for (const [nodeKey, node] of Object.entries(ir.project.nodes)) {
|
|
3618
|
+
if (node.kind === "container" && node.containerType === "tabs" && node.params.id === tabsId) {
|
|
3619
|
+
nodes[nodeKey] = {
|
|
3620
|
+
...node,
|
|
3621
|
+
params: { ...node.params, active: index }
|
|
3622
|
+
};
|
|
3623
|
+
found = true;
|
|
3624
|
+
} else if (node.kind === "component" && node.componentType === "Tabs" && node.props.tabsId === tabsId) {
|
|
3625
|
+
nodes[nodeKey] = {
|
|
3626
|
+
...node,
|
|
3627
|
+
props: { ...node.props, active: index }
|
|
3628
|
+
};
|
|
3629
|
+
found = true;
|
|
3630
|
+
} else {
|
|
3631
|
+
nodes[nodeKey] = node;
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
if (!found) {
|
|
3635
|
+
console.warn(`[applyStateChange] setActiveTab: no layout tabs found with id "${tabsId}"`);
|
|
3636
|
+
return ir;
|
|
3637
|
+
}
|
|
3638
|
+
return { ...ir, project: { ...ir.project, nodes } };
|
|
3639
|
+
}
|
|
3640
|
+
function applySetBooleanProp(ir, targetId, propName, value, originNodeId) {
|
|
3641
|
+
const resolvedId = resolveTargetId(ir, targetId, originNodeId);
|
|
3642
|
+
if (!resolvedId) return ir;
|
|
3643
|
+
const target = findTargetComponentNode(ir.project.nodes, resolvedId);
|
|
3644
|
+
if (!target) {
|
|
3645
|
+
console.warn(`[applyStateChange] ${propName}: no node found with id "${resolvedId}"`);
|
|
3646
|
+
return ir;
|
|
3647
|
+
}
|
|
3648
|
+
const nodes = mutateNodeBooleanProp(ir.project.nodes, resolvedId, propName, value);
|
|
3649
|
+
return { ...ir, project: { ...ir.project, nodes } };
|
|
3650
|
+
}
|
|
3651
|
+
function applyToggleBooleanProp(ir, targetId, propName, originNodeId) {
|
|
3652
|
+
const resolvedId = resolveTargetId(ir, targetId, originNodeId);
|
|
3653
|
+
if (!resolvedId) return ir;
|
|
3654
|
+
const targetNodeEntry = findTargetComponentNode(ir.project.nodes, resolvedId);
|
|
3655
|
+
const currentValue = targetNodeEntry ? String(targetNodeEntry.props[propName] || "false").toLowerCase() === "true" : false;
|
|
3656
|
+
const nodes = mutateNodeBooleanProp(ir.project.nodes, resolvedId, propName, !currentValue);
|
|
3657
|
+
return { ...ir, project: { ...ir.project, nodes } };
|
|
3658
|
+
}
|
|
3659
|
+
function applyNavigateTo(ir, screenName) {
|
|
3660
|
+
const updatedProject = {
|
|
3661
|
+
...ir.project,
|
|
3662
|
+
activeScreen: screenName
|
|
3663
|
+
};
|
|
3664
|
+
return { ...ir, project: updatedProject };
|
|
3665
|
+
}
|
|
3666
|
+
function resolveTargetId(ir, targetId, originNodeId) {
|
|
3667
|
+
if (targetId === SELF_TARGET) {
|
|
3668
|
+
if (!originNodeId) {
|
|
3669
|
+
console.warn("[applyStateChange] targetId is _self but no originNodeId was provided");
|
|
3670
|
+
return null;
|
|
3671
|
+
}
|
|
3672
|
+
return originNodeId;
|
|
3673
|
+
}
|
|
3674
|
+
return targetId;
|
|
3675
|
+
}
|
|
3676
|
+
function findNodeByUserDefinedId(nodes, userDefinedId) {
|
|
3677
|
+
for (const node of Object.values(nodes)) {
|
|
3678
|
+
if (node.kind === "component" && node.userDefinedId === userDefinedId) {
|
|
3679
|
+
return node;
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
return null;
|
|
3683
|
+
}
|
|
3684
|
+
function findNodeByMetaNodeId(nodes, metaNodeId) {
|
|
3685
|
+
for (const node of Object.values(nodes)) {
|
|
3686
|
+
if (node.kind === "component" && node.meta.nodeId === metaNodeId) {
|
|
3687
|
+
return node;
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
return null;
|
|
3691
|
+
}
|
|
3692
|
+
function findTargetComponentNode(nodes, targetId) {
|
|
3693
|
+
return findNodeByUserDefinedId(nodes, targetId) || findNodeByMetaNodeId(nodes, targetId);
|
|
3694
|
+
}
|
|
3695
|
+
function mutateNodeVisible(nodes, targetId, visible) {
|
|
3696
|
+
let found = false;
|
|
3697
|
+
const result = {};
|
|
3698
|
+
for (const [key, node] of Object.entries(nodes)) {
|
|
3699
|
+
if (node.kind === "component") {
|
|
3700
|
+
const matchByUserDefined = node.userDefinedId === targetId;
|
|
3701
|
+
const matchByMetaNodeId = node.meta.nodeId === targetId;
|
|
3702
|
+
if (matchByUserDefined || matchByMetaNodeId) {
|
|
3703
|
+
result[key] = {
|
|
3704
|
+
...node,
|
|
3705
|
+
props: { ...node.props, visible: visible ? "true" : "false" }
|
|
3706
|
+
};
|
|
3707
|
+
found = true;
|
|
3708
|
+
} else {
|
|
3709
|
+
result[key] = node;
|
|
3710
|
+
}
|
|
3711
|
+
} else if (node.kind === "container") {
|
|
3712
|
+
const matchByParamsId = node.params.id !== void 0 && String(node.params.id) === targetId;
|
|
3713
|
+
const matchByMetaNodeId = node.meta.nodeId === targetId;
|
|
3714
|
+
if (matchByParamsId || matchByMetaNodeId) {
|
|
3715
|
+
result[key] = {
|
|
3716
|
+
...node,
|
|
3717
|
+
params: { ...node.params, visible: visible ? "true" : "false" }
|
|
3718
|
+
};
|
|
3719
|
+
found = true;
|
|
3720
|
+
} else {
|
|
3721
|
+
result[key] = node;
|
|
3722
|
+
}
|
|
3723
|
+
} else {
|
|
3724
|
+
result[key] = node;
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
if (!found) {
|
|
3728
|
+
console.warn(`[applyStateChange] setVisible: no node found with id "${targetId}"`);
|
|
3729
|
+
}
|
|
3730
|
+
return result;
|
|
3731
|
+
}
|
|
3732
|
+
function mutateNodeBooleanProp(nodes, targetId, propName, value) {
|
|
3733
|
+
let found = false;
|
|
3734
|
+
const result = {};
|
|
3735
|
+
for (const [key, node] of Object.entries(nodes)) {
|
|
3736
|
+
if (node.kind === "component") {
|
|
3737
|
+
const matchByUserDefined = node.userDefinedId === targetId;
|
|
3738
|
+
const matchByMetaNodeId = node.meta.nodeId === targetId;
|
|
3739
|
+
if (matchByUserDefined || matchByMetaNodeId) {
|
|
3740
|
+
result[key] = {
|
|
3741
|
+
...node,
|
|
3742
|
+
props: { ...node.props, [propName]: value ? "true" : "false" }
|
|
3743
|
+
};
|
|
3744
|
+
found = true;
|
|
3745
|
+
} else {
|
|
3746
|
+
result[key] = node;
|
|
3747
|
+
}
|
|
3748
|
+
} else {
|
|
3749
|
+
result[key] = node;
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
if (!found) {
|
|
3753
|
+
console.warn(`[applyStateChange] ${propName}: no node found with id "${targetId}"`);
|
|
3754
|
+
}
|
|
3755
|
+
return result;
|
|
3756
|
+
}
|
|
3757
|
+
|
|
2856
3758
|
// src/shared/spacing.ts
|
|
2857
3759
|
var SPACING_VALUES = {
|
|
2858
3760
|
none: 0,
|
|
@@ -3032,6 +3934,10 @@ var LayoutEngine = class {
|
|
|
3032
3934
|
}
|
|
3033
3935
|
calculateContainer(node, nodeId, x, y, width, height) {
|
|
3034
3936
|
if (node.kind !== "container") return;
|
|
3937
|
+
if (node.containerType === "modal") {
|
|
3938
|
+
this.calculateModal(node, nodeId, x, y, width, height);
|
|
3939
|
+
return;
|
|
3940
|
+
}
|
|
3035
3941
|
const usesOuterPadding = node.containerType !== "card";
|
|
3036
3942
|
const padding = usesOuterPadding ? this.resolveSpacing(node.style.padding) : 0;
|
|
3037
3943
|
const innerX = x + padding;
|
|
@@ -3043,6 +3949,11 @@ var LayoutEngine = class {
|
|
|
3043
3949
|
this.result[nodeId] = { x, y, width, height };
|
|
3044
3950
|
switch (node.containerType) {
|
|
3045
3951
|
case "stack":
|
|
3952
|
+
case "tab":
|
|
3953
|
+
case "modal-body":
|
|
3954
|
+
this.calculateStack(node, innerX, innerY, innerWidth, innerHeight);
|
|
3955
|
+
break;
|
|
3956
|
+
case "modal-footer":
|
|
3046
3957
|
this.calculateStack(node, innerX, innerY, innerWidth, innerHeight);
|
|
3047
3958
|
break;
|
|
3048
3959
|
case "grid":
|
|
@@ -3057,8 +3968,12 @@ var LayoutEngine = class {
|
|
|
3057
3968
|
case "card":
|
|
3058
3969
|
this.calculateCard(node, innerX, innerY, innerWidth, innerHeight);
|
|
3059
3970
|
break;
|
|
3971
|
+
case "tabs":
|
|
3972
|
+
this.calculateTabs(node, innerX, innerY, innerWidth);
|
|
3973
|
+
break;
|
|
3060
3974
|
}
|
|
3061
|
-
|
|
3975
|
+
const isHorizontalStack = node.containerType === "stack" && !isVerticalStack;
|
|
3976
|
+
if ((isVerticalStack || isHorizontalStack || node.containerType === "card" || node.containerType === "tabs" || node.containerType === "tab" || node.containerType === "modal-body" || node.containerType === "modal-footer") && node.children.length > 0) {
|
|
3062
3977
|
let containerMaxY = y;
|
|
3063
3978
|
node.children.forEach((childRef) => {
|
|
3064
3979
|
const childPos = this.result[childRef.ref];
|
|
@@ -3078,8 +3993,22 @@ var LayoutEngine = class {
|
|
|
3078
3993
|
const children = node.children;
|
|
3079
3994
|
if (direction === "vertical") {
|
|
3080
3995
|
let currentY = y;
|
|
3081
|
-
children.
|
|
3996
|
+
const flowChildren = children.filter((cr) => {
|
|
3997
|
+
const n = this.nodes[cr.ref];
|
|
3998
|
+
if (n?.kind === "container" && n.containerType === "modal") return false;
|
|
3999
|
+
return this.isNodeVisible(cr.ref);
|
|
4000
|
+
});
|
|
4001
|
+
let flowCount = 0;
|
|
4002
|
+
children.forEach((childRef) => {
|
|
3082
4003
|
const childNode = this.nodes[childRef.ref];
|
|
4004
|
+
if (childNode?.kind === "container" && childNode.containerType === "modal") {
|
|
4005
|
+
this.calculateNode(childRef.ref, x, currentY, width, 0, "stack");
|
|
4006
|
+
return;
|
|
4007
|
+
}
|
|
4008
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4009
|
+
this.calculateNode(childRef.ref, x, currentY, width, 0, "stack");
|
|
4010
|
+
return;
|
|
4011
|
+
}
|
|
3083
4012
|
let childHeight = this.getComponentHeight();
|
|
3084
4013
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
3085
4014
|
childHeight = Number(childNode.props.height);
|
|
@@ -3090,12 +4019,17 @@ var LayoutEngine = class {
|
|
|
3090
4019
|
}
|
|
3091
4020
|
this.calculateNode(childRef.ref, x, currentY, width, childHeight, "stack");
|
|
3092
4021
|
currentY += childHeight;
|
|
3093
|
-
|
|
4022
|
+
flowCount++;
|
|
4023
|
+
if (flowCount < flowChildren.length) {
|
|
3094
4024
|
currentY += gap;
|
|
3095
4025
|
}
|
|
3096
4026
|
});
|
|
3097
4027
|
let adjustedY = y;
|
|
3098
|
-
|
|
4028
|
+
let adjustedCount = 0;
|
|
4029
|
+
children.forEach((childRef) => {
|
|
4030
|
+
const childNode = this.nodes[childRef.ref];
|
|
4031
|
+
if (childNode?.kind === "container" && childNode.containerType === "modal") return;
|
|
4032
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3099
4033
|
const childPos = this.result[childRef.ref];
|
|
3100
4034
|
if (childPos) {
|
|
3101
4035
|
const deltaY = adjustedY - childPos.y;
|
|
@@ -3104,7 +4038,8 @@ var LayoutEngine = class {
|
|
|
3104
4038
|
this.adjustNodeYPositions(childRef.ref, deltaY);
|
|
3105
4039
|
}
|
|
3106
4040
|
adjustedY += childPos.height;
|
|
3107
|
-
|
|
4041
|
+
adjustedCount++;
|
|
4042
|
+
if (adjustedCount < flowChildren.length) {
|
|
3108
4043
|
adjustedY += gap;
|
|
3109
4044
|
}
|
|
3110
4045
|
}
|
|
@@ -3114,9 +4049,10 @@ var LayoutEngine = class {
|
|
|
3114
4049
|
const crossAlign = node.style.align || "start";
|
|
3115
4050
|
if (justify === "stretch") {
|
|
3116
4051
|
let currentX = x;
|
|
3117
|
-
const
|
|
4052
|
+
const visibleChildren = children.filter((cr) => this.isNodeVisible(cr.ref));
|
|
4053
|
+
const childWidth = this.calculateChildWidth(visibleChildren.length, width, gap);
|
|
3118
4054
|
let stackHeight = 0;
|
|
3119
|
-
|
|
4055
|
+
visibleChildren.forEach((childRef) => {
|
|
3120
4056
|
const childNode = this.nodes[childRef.ref];
|
|
3121
4057
|
let childHeight = this.getComponentHeight();
|
|
3122
4058
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
@@ -3129,6 +4065,10 @@ var LayoutEngine = class {
|
|
|
3129
4065
|
stackHeight = Math.max(stackHeight, childHeight);
|
|
3130
4066
|
});
|
|
3131
4067
|
children.forEach((childRef) => {
|
|
4068
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4069
|
+
this.calculateNode(childRef.ref, currentX, y, 0, 0, "stack");
|
|
4070
|
+
return;
|
|
4071
|
+
}
|
|
3132
4072
|
this.calculateNode(childRef.ref, currentX, y, childWidth, stackHeight, "stack");
|
|
3133
4073
|
currentX += childWidth + gap;
|
|
3134
4074
|
});
|
|
@@ -3137,9 +4077,18 @@ var LayoutEngine = class {
|
|
|
3137
4077
|
const childHeights = [];
|
|
3138
4078
|
const explicitHeightFlags = [];
|
|
3139
4079
|
const flexIndices = /* @__PURE__ */ new Set();
|
|
4080
|
+
const visibleFlags = [];
|
|
3140
4081
|
let stackHeight = 0;
|
|
3141
4082
|
children.forEach((childRef, index) => {
|
|
3142
4083
|
const childNode = this.nodes[childRef.ref];
|
|
4084
|
+
const visible = this.isNodeVisible(childRef.ref);
|
|
4085
|
+
visibleFlags.push(visible);
|
|
4086
|
+
if (!visible) {
|
|
4087
|
+
childWidths.push(0);
|
|
4088
|
+
childHeights.push(0);
|
|
4089
|
+
explicitHeightFlags.push(false);
|
|
4090
|
+
return;
|
|
4091
|
+
}
|
|
3143
4092
|
const hasExplicitHeight = childNode?.kind === "component" && !!childNode.props.height;
|
|
3144
4093
|
const hasExplicitWidth = childNode?.kind === "component" && !!childNode.props.width;
|
|
3145
4094
|
const isBlockButton = childNode?.kind === "component" && childNode.componentType === "Button" && !hasExplicitWidth && this.parseBooleanProp(childNode.props.block, false);
|
|
@@ -3157,7 +4106,8 @@ var LayoutEngine = class {
|
|
|
3157
4106
|
childHeights.push(this.getComponentHeight());
|
|
3158
4107
|
explicitHeightFlags.push(hasExplicitHeight);
|
|
3159
4108
|
});
|
|
3160
|
-
const
|
|
4109
|
+
const visibleCount = visibleFlags.filter(Boolean).length;
|
|
4110
|
+
const totalGapWidth = gap * Math.max(0, visibleCount - 1);
|
|
3161
4111
|
if (flexIndices.size > 0) {
|
|
3162
4112
|
const fixedWidth = childWidths.reduce((sum, w, idx) => {
|
|
3163
4113
|
return flexIndices.has(idx) ? sum : sum + w;
|
|
@@ -3169,6 +4119,7 @@ var LayoutEngine = class {
|
|
|
3169
4119
|
});
|
|
3170
4120
|
}
|
|
3171
4121
|
children.forEach((childRef, index) => {
|
|
4122
|
+
if (!visibleFlags[index]) return;
|
|
3172
4123
|
const childNode = this.nodes[childRef.ref];
|
|
3173
4124
|
const childWidth = childWidths[index];
|
|
3174
4125
|
let childHeight = this.getComponentHeight();
|
|
@@ -3192,14 +4143,18 @@ var LayoutEngine = class {
|
|
|
3192
4143
|
startX = x + width - totalContentWidth;
|
|
3193
4144
|
} else if (justify === "spaceBetween") {
|
|
3194
4145
|
startX = x;
|
|
3195
|
-
dynamicGap =
|
|
4146
|
+
dynamicGap = visibleCount > 1 ? (width - totalChildWidth) / (visibleCount - 1) : 0;
|
|
3196
4147
|
} else if (justify === "spaceAround") {
|
|
3197
|
-
const spacing =
|
|
4148
|
+
const spacing = visibleCount > 0 ? (width - totalChildWidth) / visibleCount : 0;
|
|
3198
4149
|
startX = x + spacing / 2;
|
|
3199
4150
|
dynamicGap = spacing;
|
|
3200
4151
|
}
|
|
3201
4152
|
let currentX = startX;
|
|
3202
4153
|
children.forEach((childRef, index) => {
|
|
4154
|
+
if (!visibleFlags[index]) {
|
|
4155
|
+
this.calculateNode(childRef.ref, currentX, y, 0, 0, "stack");
|
|
4156
|
+
return;
|
|
4157
|
+
}
|
|
3203
4158
|
const childWidth = childWidths[index];
|
|
3204
4159
|
const childHeight = childHeights[index];
|
|
3205
4160
|
let childY = y;
|
|
@@ -3231,6 +4186,7 @@ var LayoutEngine = class {
|
|
|
3231
4186
|
let currentRowMaxHeight = 0;
|
|
3232
4187
|
const rowHeights = [0];
|
|
3233
4188
|
node.children.forEach((childRef) => {
|
|
4189
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3234
4190
|
const child = this.nodes[childRef.ref];
|
|
3235
4191
|
let span = 1;
|
|
3236
4192
|
let childHeight = this.getComponentHeight();
|
|
@@ -3265,6 +4221,18 @@ var LayoutEngine = class {
|
|
|
3265
4221
|
}
|
|
3266
4222
|
return totalHeight;
|
|
3267
4223
|
}
|
|
4224
|
+
if (node.containerType === "tabs") {
|
|
4225
|
+
const activeIndex = Number(node.params.active) || 0;
|
|
4226
|
+
const activeChildRef = node.children[activeIndex] ?? node.children[0];
|
|
4227
|
+
if (!activeChildRef) return totalHeight;
|
|
4228
|
+
const activeChild = this.nodes[activeChildRef.ref];
|
|
4229
|
+
if (activeChild?.kind === "container") {
|
|
4230
|
+
totalHeight += this.calculateContainerHeight(activeChild, availableWidth);
|
|
4231
|
+
} else if (activeChild?.kind === "component") {
|
|
4232
|
+
totalHeight += this.getIntrinsicComponentHeight(activeChild, availableWidth);
|
|
4233
|
+
}
|
|
4234
|
+
return totalHeight;
|
|
4235
|
+
}
|
|
3268
4236
|
if (node.containerType === "split") {
|
|
3269
4237
|
const splitGap = this.resolveSpacing(node.style.gap);
|
|
3270
4238
|
const leftParam = node.params.left;
|
|
@@ -3277,6 +4245,7 @@ var LayoutEngine = class {
|
|
|
3277
4245
|
const rightWidth = Number.isFinite(rightWidthRaw) && rightWidthRaw > 0 ? rightWidthRaw : availableWidth / 2;
|
|
3278
4246
|
let maxHeight = 0;
|
|
3279
4247
|
node.children.forEach((childRef, index) => {
|
|
4248
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3280
4249
|
const child = this.nodes[childRef.ref];
|
|
3281
4250
|
let childHeight = this.getComponentHeight();
|
|
3282
4251
|
const isFirst = index === 0;
|
|
@@ -3304,6 +4273,7 @@ var LayoutEngine = class {
|
|
|
3304
4273
|
if (node.containerType === "stack" && direction === "horizontal") {
|
|
3305
4274
|
let maxHeight = 0;
|
|
3306
4275
|
node.children.forEach((childRef) => {
|
|
4276
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3307
4277
|
const child = this.nodes[childRef.ref];
|
|
3308
4278
|
let childHeight = this.getComponentHeight();
|
|
3309
4279
|
if (child?.kind === "component") {
|
|
@@ -3320,7 +4290,10 @@ var LayoutEngine = class {
|
|
|
3320
4290
|
totalHeight += maxHeight;
|
|
3321
4291
|
return totalHeight;
|
|
3322
4292
|
}
|
|
3323
|
-
node.children.
|
|
4293
|
+
const visibleLinear = node.children.filter((cr) => this.isNodeVisible(cr.ref));
|
|
4294
|
+
let linearIndex = 0;
|
|
4295
|
+
node.children.forEach((childRef) => {
|
|
4296
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3324
4297
|
const child = this.nodes[childRef.ref];
|
|
3325
4298
|
let childHeight = this.getComponentHeight();
|
|
3326
4299
|
if (child?.kind === "component") {
|
|
@@ -3333,7 +4306,8 @@ var LayoutEngine = class {
|
|
|
3333
4306
|
childHeight = this.calculateContainerHeight(child, availableWidth);
|
|
3334
4307
|
}
|
|
3335
4308
|
totalHeight += childHeight;
|
|
3336
|
-
|
|
4309
|
+
linearIndex++;
|
|
4310
|
+
if (linearIndex < visibleLinear.length) {
|
|
3337
4311
|
totalHeight += gap;
|
|
3338
4312
|
}
|
|
3339
4313
|
});
|
|
@@ -3346,6 +4320,10 @@ var LayoutEngine = class {
|
|
|
3346
4320
|
const colWidth = (width - gap * (columns - 1)) / columns;
|
|
3347
4321
|
const cellHeights = {};
|
|
3348
4322
|
node.children.forEach((childRef, cellIndex) => {
|
|
4323
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4324
|
+
cellHeights[cellIndex] = 0;
|
|
4325
|
+
return;
|
|
4326
|
+
}
|
|
3349
4327
|
const child = this.nodes[childRef.ref];
|
|
3350
4328
|
let cellHeight = this.getComponentHeight();
|
|
3351
4329
|
let span = 1;
|
|
@@ -3370,6 +4348,11 @@ var LayoutEngine = class {
|
|
|
3370
4348
|
const rowHeights = [0];
|
|
3371
4349
|
const cellPositions = [];
|
|
3372
4350
|
node.children.forEach((childRef, cellIndex) => {
|
|
4351
|
+
const visible = this.isNodeVisible(childRef.ref);
|
|
4352
|
+
if (!visible) {
|
|
4353
|
+
cellPositions.push({ row: currentRow, col: currentCol, span: 0, visible: false });
|
|
4354
|
+
return;
|
|
4355
|
+
}
|
|
3373
4356
|
const child = this.nodes[childRef.ref];
|
|
3374
4357
|
let span = 1;
|
|
3375
4358
|
if (child?.kind === "container" && child.meta?.source === "cell") {
|
|
@@ -3381,13 +4364,17 @@ var LayoutEngine = class {
|
|
|
3381
4364
|
currentCol = 0;
|
|
3382
4365
|
currentRowMaxHeight = 0;
|
|
3383
4366
|
}
|
|
3384
|
-
cellPositions.push({ row: currentRow, col: currentCol, span });
|
|
4367
|
+
cellPositions.push({ row: currentRow, col: currentCol, span, visible: true });
|
|
3385
4368
|
currentRowMaxHeight = Math.max(currentRowMaxHeight, cellHeights[cellIndex]);
|
|
3386
4369
|
currentCol += span;
|
|
3387
4370
|
});
|
|
3388
4371
|
rowHeights[currentRow] = currentRowMaxHeight;
|
|
3389
4372
|
node.children.forEach((childRef, cellIndex) => {
|
|
3390
|
-
const { row, col, span } = cellPositions[cellIndex];
|
|
4373
|
+
const { row, col, span, visible } = cellPositions[cellIndex];
|
|
4374
|
+
if (!visible) {
|
|
4375
|
+
this.calculateNode(childRef.ref, x, y, 0, 0, "grid");
|
|
4376
|
+
return;
|
|
4377
|
+
}
|
|
3391
4378
|
const cellHeight = rowHeights[row];
|
|
3392
4379
|
let cellY = y;
|
|
3393
4380
|
for (let r = 0; r < row; r++) {
|
|
@@ -3458,8 +4445,14 @@ var LayoutEngine = class {
|
|
|
3458
4445
|
const innerCardWidth = width - cardPadding * 2;
|
|
3459
4446
|
const children = node.children;
|
|
3460
4447
|
let currentY = y + cardPadding;
|
|
3461
|
-
children.
|
|
4448
|
+
const flowChildren = children.filter((cr) => this.isNodeVisible(cr.ref));
|
|
4449
|
+
let flowCount = 0;
|
|
4450
|
+
children.forEach((childRef) => {
|
|
3462
4451
|
const childNode = this.nodes[childRef.ref];
|
|
4452
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4453
|
+
this.calculateNode(childRef.ref, x + cardPadding, currentY, innerCardWidth, 0, "card");
|
|
4454
|
+
return;
|
|
4455
|
+
}
|
|
3463
4456
|
let childHeight = this.getComponentHeight();
|
|
3464
4457
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
3465
4458
|
childHeight = Number(childNode.props.height);
|
|
@@ -3470,11 +4463,63 @@ var LayoutEngine = class {
|
|
|
3470
4463
|
}
|
|
3471
4464
|
this.calculateNode(childRef.ref, x + cardPadding, currentY, innerCardWidth, childHeight, "card");
|
|
3472
4465
|
currentY += childHeight;
|
|
3473
|
-
|
|
4466
|
+
flowCount++;
|
|
4467
|
+
if (flowCount < flowChildren.length) {
|
|
3474
4468
|
currentY += gap;
|
|
3475
4469
|
}
|
|
3476
4470
|
});
|
|
3477
4471
|
}
|
|
4472
|
+
calculateTabs(node, x, y, width) {
|
|
4473
|
+
if (node.kind !== "container") return;
|
|
4474
|
+
const activeIndex = Number(node.params.active) || 0;
|
|
4475
|
+
node.children.forEach((childRef, index) => {
|
|
4476
|
+
if (index === activeIndex) {
|
|
4477
|
+
const tabNode = this.nodes[childRef.ref];
|
|
4478
|
+
let tabHeight = 40;
|
|
4479
|
+
if (tabNode?.kind === "container") {
|
|
4480
|
+
tabHeight = this.calculateContainerHeight(tabNode, width);
|
|
4481
|
+
} else if (tabNode?.kind === "component") {
|
|
4482
|
+
tabHeight = this.getIntrinsicComponentHeight(tabNode, width);
|
|
4483
|
+
}
|
|
4484
|
+
this.calculateNode(childRef.ref, x, y, width, tabHeight, "tabs");
|
|
4485
|
+
}
|
|
4486
|
+
});
|
|
4487
|
+
}
|
|
4488
|
+
calculateModal(node, nodeId, _canvasX, _canvasY, _canvasWidth, _canvasHeight) {
|
|
4489
|
+
if (node.kind !== "container") return;
|
|
4490
|
+
const viewportWidth = this.viewport.width;
|
|
4491
|
+
const size = String(node.params.size || "md");
|
|
4492
|
+
const modalWidths = { sm: 380, md: 520, lg: 720 };
|
|
4493
|
+
const modalWidth = Math.min(modalWidths[size] ?? 520, viewportWidth - 32);
|
|
4494
|
+
const modalX = Math.round((viewportWidth - modalWidth) / 2);
|
|
4495
|
+
const modalY = 64;
|
|
4496
|
+
const hasHeader = node.params.title !== void 0 && node.params.title !== "";
|
|
4497
|
+
const headerHeight = hasHeader ? 48 : 0;
|
|
4498
|
+
let childrenHeight = 0;
|
|
4499
|
+
const innerWidth = modalWidth;
|
|
4500
|
+
let currentY = modalY + headerHeight;
|
|
4501
|
+
node.children.forEach((childRef, index) => {
|
|
4502
|
+
const childNode = this.nodes[childRef.ref];
|
|
4503
|
+
let childHeight = 0;
|
|
4504
|
+
if (childNode?.kind === "container") {
|
|
4505
|
+
childHeight = this.calculateContainerHeight(childNode, innerWidth);
|
|
4506
|
+
} else if (childNode?.kind === "component") {
|
|
4507
|
+
childHeight = this.getIntrinsicComponentHeight(childNode, innerWidth);
|
|
4508
|
+
} else {
|
|
4509
|
+
childHeight = this.getComponentHeight();
|
|
4510
|
+
}
|
|
4511
|
+
this.calculateNode(childRef.ref, modalX, currentY, innerWidth, childHeight, "modal");
|
|
4512
|
+
const resolvedChildHeight = this.result[childRef.ref]?.height ?? childHeight;
|
|
4513
|
+
currentY += resolvedChildHeight;
|
|
4514
|
+
childrenHeight += resolvedChildHeight;
|
|
4515
|
+
if (index < node.children.length - 1) {
|
|
4516
|
+
currentY += 8;
|
|
4517
|
+
childrenHeight += 8;
|
|
4518
|
+
}
|
|
4519
|
+
});
|
|
4520
|
+
const modalHeight = headerHeight + childrenHeight;
|
|
4521
|
+
this.result[nodeId] = { x: modalX, y: modalY, width: modalWidth, height: modalHeight };
|
|
4522
|
+
}
|
|
3478
4523
|
/**
|
|
3479
4524
|
* Calculate layout for an instance node.
|
|
3480
4525
|
* The instance is a transparent wrapper — its bounding box equals the
|
|
@@ -3705,8 +4750,7 @@ var LayoutEngine = class {
|
|
|
3705
4750
|
return Math.max(this.getComponentHeight(), wrappedHeight);
|
|
3706
4751
|
}
|
|
3707
4752
|
if (node.componentType === "SidebarMenu") {
|
|
3708
|
-
const
|
|
3709
|
-
const items = itemsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4753
|
+
const items = toStringArray(node.props.items);
|
|
3710
4754
|
const itemCount = items.length > 0 ? items.length : 3;
|
|
3711
4755
|
const itemHeight = 40;
|
|
3712
4756
|
return Math.max(this.getComponentHeight(), itemCount * itemHeight);
|
|
@@ -3717,7 +4761,7 @@ var LayoutEngine = class {
|
|
|
3717
4761
|
if (node.componentType === "Stat") return 120;
|
|
3718
4762
|
if (node.componentType === "Chart") return 250;
|
|
3719
4763
|
if (node.componentType === "List") {
|
|
3720
|
-
const itemsFromProps =
|
|
4764
|
+
const itemsFromProps = toStringArray(node.props.items);
|
|
3721
4765
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
3722
4766
|
const fallbackCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
3723
4767
|
const itemCount = itemsFromProps.length > 0 ? itemsFromProps.length : fallbackCount;
|
|
@@ -3895,6 +4939,17 @@ var LayoutEngine = class {
|
|
|
3895
4939
|
}
|
|
3896
4940
|
return width;
|
|
3897
4941
|
}
|
|
4942
|
+
/**
|
|
4943
|
+
* Returns false only when the node has an explicit `visible: 'false'` param/prop.
|
|
4944
|
+
* Missing or any other value is treated as visible.
|
|
4945
|
+
*/
|
|
4946
|
+
isNodeVisible(nodeId) {
|
|
4947
|
+
const node = this.nodes[nodeId];
|
|
4948
|
+
if (!node) return true;
|
|
4949
|
+
if (node.kind === "component") return String(node.props.visible) !== "false";
|
|
4950
|
+
if (node.kind === "container") return String(node.params.visible) !== "false";
|
|
4951
|
+
return true;
|
|
4952
|
+
}
|
|
3898
4953
|
parseBooleanProp(value, fallback = false) {
|
|
3899
4954
|
if (typeof value === "boolean") return value;
|
|
3900
4955
|
if (typeof value === "string") {
|
|
@@ -3985,7 +5040,7 @@ var MockDataGenerator = class {
|
|
|
3985
5040
|
for (const [key, rawValues] of Object.entries(mocks)) {
|
|
3986
5041
|
let values = [];
|
|
3987
5042
|
if (typeof rawValues === "string") {
|
|
3988
|
-
values = rawValues
|
|
5043
|
+
values = toStringArray(rawValues);
|
|
3989
5044
|
} else if (Array.isArray(rawValues)) {
|
|
3990
5045
|
values = rawValues.map((v) => typeof v === "string" ? v.trim() : "").filter((v) => v.length > 0);
|
|
3991
5046
|
}
|
|
@@ -4077,7 +5132,7 @@ var MockDataGenerator = class {
|
|
|
4077
5132
|
* columns: "id,name,status,amount"
|
|
4078
5133
|
*/
|
|
4079
5134
|
static generateMockRow(columns, rowIndex, random = false) {
|
|
4080
|
-
const columnNames = columns
|
|
5135
|
+
const columnNames = toStringArray(columns);
|
|
4081
5136
|
const row = {};
|
|
4082
5137
|
columnNames.forEach((col) => {
|
|
4083
5138
|
const mockType = this.inferMockTypeFromColumn(col);
|
|
@@ -5129,6 +6184,9 @@ var SVGRenderer = class {
|
|
|
5129
6184
|
if (!node || !pos) return;
|
|
5130
6185
|
this.renderedNodeIds.add(nodeId);
|
|
5131
6186
|
if (node.kind === "container") {
|
|
6187
|
+
if (String(node.params.visible) === "false") {
|
|
6188
|
+
return;
|
|
6189
|
+
}
|
|
5132
6190
|
const containerGroup = [];
|
|
5133
6191
|
const hasNodeId = node.meta?.nodeId;
|
|
5134
6192
|
if (hasNodeId) {
|
|
@@ -5149,6 +6207,12 @@ var SVGRenderer = class {
|
|
|
5149
6207
|
if (node.containerType === "split") {
|
|
5150
6208
|
this.renderSplitDecoration(node, pos, containerGroup);
|
|
5151
6209
|
}
|
|
6210
|
+
if (node.containerType === "modal") {
|
|
6211
|
+
this.renderModalDecoration(node, pos, containerGroup);
|
|
6212
|
+
}
|
|
6213
|
+
if (node.containerType === "modal-footer") {
|
|
6214
|
+
this.renderModalFooterDecoration(pos, containerGroup);
|
|
6215
|
+
}
|
|
5152
6216
|
const isCellContainer = node.meta?.source === "cell";
|
|
5153
6217
|
if (node.children.length === 0 && this.options.showDiagnostics && !isCellContainer) {
|
|
5154
6218
|
containerGroup.push(this.renderEmptyContainerDiagnostic(pos, node.containerType));
|
|
@@ -5175,6 +6239,9 @@ var SVGRenderer = class {
|
|
|
5175
6239
|
}
|
|
5176
6240
|
output.push(...instanceGroup);
|
|
5177
6241
|
} else if (node.kind === "component") {
|
|
6242
|
+
if (String(node.props.visible) === "false") {
|
|
6243
|
+
return;
|
|
6244
|
+
}
|
|
5178
6245
|
const componentSvg = this.renderComponent(node, pos);
|
|
5179
6246
|
if (componentSvg) {
|
|
5180
6247
|
output.push(componentSvg);
|
|
@@ -5236,8 +6303,6 @@ var SVGRenderer = class {
|
|
|
5236
6303
|
return this.renderAlert(node, pos);
|
|
5237
6304
|
case "Badge":
|
|
5238
6305
|
return this.renderBadge(node, pos);
|
|
5239
|
-
case "Modal":
|
|
5240
|
-
return this.renderModal(node, pos);
|
|
5241
6306
|
case "List":
|
|
5242
6307
|
return this.renderList(node, pos);
|
|
5243
6308
|
case "Stat":
|
|
@@ -5680,8 +6745,8 @@ var SVGRenderer = class {
|
|
|
5680
6745
|
}
|
|
5681
6746
|
renderTable(node, pos) {
|
|
5682
6747
|
const title = String(node.props.title || "");
|
|
5683
|
-
const
|
|
5684
|
-
const columns =
|
|
6748
|
+
const parsedColumns = toStringArray(node.props.columns);
|
|
6749
|
+
const columns = parsedColumns.length > 0 ? parsedColumns : ["Col1", "Col2", "Col3"];
|
|
5685
6750
|
const rowCount = Number(node.props.rows || node.props.rowsMock || 5);
|
|
5686
6751
|
const mockStr = String(node.props.mock || "");
|
|
5687
6752
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
@@ -5689,7 +6754,7 @@ var SVGRenderer = class {
|
|
|
5689
6754
|
const parsedPageCount = Number(node.props.pages || 5);
|
|
5690
6755
|
const pageCount = Number.isFinite(parsedPageCount) && parsedPageCount > 0 ? Math.floor(parsedPageCount) : 5;
|
|
5691
6756
|
const paginationAlign = String(node.props.paginationAlign || "right");
|
|
5692
|
-
const actions =
|
|
6757
|
+
const actions = toStringArray(node.props.actions);
|
|
5693
6758
|
const hasActions = actions.length > 0;
|
|
5694
6759
|
const caption = String(node.props.caption || "").trim();
|
|
5695
6760
|
const hasCaption = caption.length > 0;
|
|
@@ -5702,7 +6767,7 @@ var SVGRenderer = class {
|
|
|
5702
6767
|
const rawCaptionAlign = String(node.props.captionAlign || "");
|
|
5703
6768
|
const captionAlign = rawCaptionAlign === "left" || rawCaptionAlign === "center" || rawCaptionAlign === "right" ? rawCaptionAlign : paginationAlign === "left" ? "right" : "left";
|
|
5704
6769
|
const sameFooterAlign = hasCaption && pagination && captionAlign === paginationAlign;
|
|
5705
|
-
const mockTypes = mockStr
|
|
6770
|
+
const mockTypes = toStringArray(mockStr);
|
|
5706
6771
|
const safeColumns = columns.length > 0 ? columns : ["Column"];
|
|
5707
6772
|
while (mockTypes.length < safeColumns.length) {
|
|
5708
6773
|
const inferred = MockDataGenerator.inferMockTypeFromColumn(safeColumns[mockTypes.length] || "item");
|
|
@@ -5802,6 +6867,13 @@ var SVGRenderer = class {
|
|
|
5802
6867
|
currentX += buttonSize + buttonGap;
|
|
5803
6868
|
});
|
|
5804
6869
|
}
|
|
6870
|
+
const rowEventAttrs = this.getScopedEventAttrs(node, "onRowClick", { index: rowIdx });
|
|
6871
|
+
if (rowEventAttrs) {
|
|
6872
|
+
svg += `
|
|
6873
|
+
<rect x="${pos.x}" y="${rowY}"
|
|
6874
|
+
width="${pos.width}" height="${rowHeight}"
|
|
6875
|
+
fill="transparent" stroke="none" pointer-events="all"${rowEventAttrs}/>`;
|
|
6876
|
+
}
|
|
5805
6877
|
});
|
|
5806
6878
|
const footerTop = headerY + headerHeight + mockRows.length * rowHeight + 16;
|
|
5807
6879
|
if (pagination) {
|
|
@@ -6191,14 +7263,15 @@ var SVGRenderer = class {
|
|
|
6191
7263
|
const label = String(node.props.label || "Checkbox");
|
|
6192
7264
|
const checked = String(node.props.checked || "false").toLowerCase() === "true";
|
|
6193
7265
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7266
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6194
7267
|
const controlColor = this.resolveControlColor();
|
|
6195
7268
|
const checkboxSize = 18;
|
|
6196
7269
|
const checkboxY = pos.y + pos.height / 2 - checkboxSize / 2;
|
|
6197
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6198
|
-
<rect x="${pos.x}" y="${checkboxY}"
|
|
6199
|
-
width="${checkboxSize}" height="${checkboxSize}"
|
|
6200
|
-
rx="4"
|
|
6201
|
-
fill="${checked ? controlColor : this.renderTheme.cardBg}"
|
|
7270
|
+
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}${!clickable ? ' data-clickable="false"' : ""}>
|
|
7271
|
+
<rect x="${pos.x}" y="${checkboxY}"
|
|
7272
|
+
width="${checkboxSize}" height="${checkboxSize}"
|
|
7273
|
+
rx="4"
|
|
7274
|
+
fill="${checked ? controlColor : this.renderTheme.cardBg}"
|
|
6202
7275
|
stroke="${this.renderTheme.border}"
|
|
6203
7276
|
stroke-width="1"/>
|
|
6204
7277
|
${checked ? `<text x="${pos.x + checkboxSize / 2}" y="${checkboxY + 14}"
|
|
@@ -6216,13 +7289,14 @@ var SVGRenderer = class {
|
|
|
6216
7289
|
const label = String(node.props.label || "Radio");
|
|
6217
7290
|
const checked = String(node.props.checked || "false").toLowerCase() === "true";
|
|
6218
7291
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7292
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6219
7293
|
const controlColor = this.resolveControlColor();
|
|
6220
7294
|
const radioSize = 16;
|
|
6221
7295
|
const radioY = pos.y + pos.height / 2 - radioSize / 2;
|
|
6222
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6223
|
-
<circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
|
|
6224
|
-
r="${radioSize / 2}"
|
|
6225
|
-
fill="${this.renderTheme.cardBg}"
|
|
7296
|
+
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}${!clickable ? ' data-clickable="false"' : ""}>
|
|
7297
|
+
<circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
|
|
7298
|
+
r="${radioSize / 2}"
|
|
7299
|
+
fill="${this.renderTheme.cardBg}"
|
|
6226
7300
|
stroke="${this.renderTheme.border}"
|
|
6227
7301
|
stroke-width="1"/>
|
|
6228
7302
|
${checked ? `<circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
|
|
@@ -6238,12 +7312,13 @@ var SVGRenderer = class {
|
|
|
6238
7312
|
const label = String(node.props.label || "Toggle");
|
|
6239
7313
|
const enabled = String(node.props.enabled || "false").toLowerCase() === "true";
|
|
6240
7314
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7315
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6241
7316
|
const controlColor = this.resolveControlColor();
|
|
6242
7317
|
const toggleWidth = 40;
|
|
6243
7318
|
const toggleHeight = 20;
|
|
6244
7319
|
const toggleY = pos.y + pos.height / 2 - toggleHeight / 2;
|
|
6245
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6246
|
-
<rect x="${pos.x}" y="${toggleY}"
|
|
7320
|
+
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}${!clickable ? ' data-clickable="false"' : ""}>
|
|
7321
|
+
<rect x="${pos.x}" y="${toggleY}"
|
|
6247
7322
|
width="${toggleWidth}" height="${toggleHeight}"
|
|
6248
7323
|
rx="10"
|
|
6249
7324
|
fill="${enabled ? controlColor : this.renderTheme.border}"
|
|
@@ -6262,12 +7337,9 @@ var SVGRenderer = class {
|
|
|
6262
7337
|
// ============================================================================
|
|
6263
7338
|
renderSidebar(node, pos) {
|
|
6264
7339
|
const title = String(node.props.title || "Sidebar");
|
|
6265
|
-
const itemsStr = String(node.props.items || "");
|
|
6266
7340
|
const activeItem = String(node.props.active || "");
|
|
6267
|
-
let items =
|
|
6268
|
-
if (
|
|
6269
|
-
items = itemsStr.split(",").map((i) => i.trim());
|
|
6270
|
-
} else {
|
|
7341
|
+
let items = toStringArray(node.props.items);
|
|
7342
|
+
if (items.length === 0) {
|
|
6271
7343
|
const itemCount = Number(node.props.itemsMock || 6);
|
|
6272
7344
|
items = MockDataGenerator.generateMockList("name", itemCount);
|
|
6273
7345
|
}
|
|
@@ -6306,9 +7378,9 @@ var SVGRenderer = class {
|
|
|
6306
7378
|
return svg;
|
|
6307
7379
|
}
|
|
6308
7380
|
renderTabs(node, pos) {
|
|
6309
|
-
const
|
|
6310
|
-
const tabs =
|
|
6311
|
-
const activeProp = node.props.active ?? 0;
|
|
7381
|
+
const parsedTabs = toStringArray(node.props.items);
|
|
7382
|
+
const tabs = parsedTabs.length > 0 ? parsedTabs : ["Tab 1", "Tab 2", "Tab 3"];
|
|
7383
|
+
const activeProp = node.props.active ?? node.props.initialActive ?? 0;
|
|
6312
7384
|
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
6313
7385
|
const variant = String(node.props.variant || "default");
|
|
6314
7386
|
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
@@ -6318,8 +7390,7 @@ var SVGRenderer = class {
|
|
|
6318
7390
|
const tabHeight = pos.height > 0 ? pos.height : sizeMap[String(node.props.size || "md")] ?? 44;
|
|
6319
7391
|
const fontSize = 13;
|
|
6320
7392
|
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
6321
|
-
const
|
|
6322
|
-
const iconList = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
7393
|
+
const iconList = toStringArray(node.props.icons);
|
|
6323
7394
|
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
6324
7395
|
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
6325
7396
|
const tabWidth = pos.width / tabs.length;
|
|
@@ -6423,6 +7494,13 @@ var SVGRenderer = class {
|
|
|
6423
7494
|
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
6424
7495
|
}
|
|
6425
7496
|
}
|
|
7497
|
+
const tabsTriggerAttrs = this.getTabsTriggerAttrs(node, i);
|
|
7498
|
+
if (tabsTriggerAttrs) {
|
|
7499
|
+
svg += `
|
|
7500
|
+
<rect x="${tabX}" y="${pos.y}"
|
|
7501
|
+
width="${tabWidth}" height="${tabHeight}"
|
|
7502
|
+
fill="transparent" stroke="none" pointer-events="all"${tabsTriggerAttrs}/>`;
|
|
7503
|
+
}
|
|
6426
7504
|
});
|
|
6427
7505
|
const contentY = pos.y + tabHeight;
|
|
6428
7506
|
const contentH = pos.height - tabHeight;
|
|
@@ -6536,66 +7614,51 @@ var SVGRenderer = class {
|
|
|
6536
7614
|
text-anchor="middle">${this.escapeXml(text)}</text>
|
|
6537
7615
|
</g>`;
|
|
6538
7616
|
}
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
}
|
|
6544
|
-
const title = String(node.props.title || "Modal");
|
|
7617
|
+
renderModalDecoration(node, pos, output) {
|
|
7618
|
+
if (node.kind !== "container") return;
|
|
7619
|
+
const canvasWidth = this.options.width;
|
|
7620
|
+
const canvasHeight = Math.max(this.options.height, this.calculateContentHeight());
|
|
6545
7621
|
const padding = 16;
|
|
6546
7622
|
const headerHeight = 48;
|
|
6547
|
-
const
|
|
6548
|
-
const
|
|
6549
|
-
const
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
font-size="18"
|
|
6580
|
-
fill="${this.renderTheme.textMuted}">\u2715</text>
|
|
6581
|
-
|
|
6582
|
-
<!-- Content placeholder -->
|
|
6583
|
-
<text x="${modalX + pos.width / 2}" y="${modalY + headerHeight + (pos.height - headerHeight) / 2}"
|
|
6584
|
-
font-family="Arial, Helvetica, sans-serif"
|
|
6585
|
-
font-size="13"
|
|
6586
|
-
fill="${this.renderTheme.textMuted}"
|
|
6587
|
-
text-anchor="middle">Modal content</text>
|
|
6588
|
-
</g>`;
|
|
7623
|
+
const hasTitle = node.params.title !== void 0 && node.params.title !== "";
|
|
7624
|
+
const closable = node.params.closable !== "false" && node.params.closable !== 0;
|
|
7625
|
+
const title = hasTitle ? String(node.params.title) : "";
|
|
7626
|
+
output.push(
|
|
7627
|
+
`<rect x="0" y="0" width="${canvasWidth}" height="${canvasHeight}" fill="black" opacity="0.28" pointer-events="none"/>`
|
|
7628
|
+
);
|
|
7629
|
+
output.push(
|
|
7630
|
+
`<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"/>`
|
|
7631
|
+
);
|
|
7632
|
+
if (hasTitle) {
|
|
7633
|
+
output.push(
|
|
7634
|
+
`<line x1="${pos.x}" y1="${pos.y + headerHeight}" x2="${pos.x + pos.width}" y2="${pos.y + headerHeight}" stroke="${this.renderTheme.border}" stroke-width="1"/>`
|
|
7635
|
+
);
|
|
7636
|
+
output.push(
|
|
7637
|
+
`<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>`
|
|
7638
|
+
);
|
|
7639
|
+
if (closable) {
|
|
7640
|
+
const events = node.events?.find((e) => e.event === "onClose");
|
|
7641
|
+
const closeEventAttr = events ? this.serializeEventHandler(events) : "";
|
|
7642
|
+
const closeX = pos.x + pos.width - padding - 14;
|
|
7643
|
+
const closeY = pos.y + padding + 14;
|
|
7644
|
+
output.push(
|
|
7645
|
+
`<rect x="${closeX - 12}" y="${closeY - 12}" width="24" height="24" rx="4" fill="transparent" stroke="none" pointer-events="all"${closeEventAttr}/>`,
|
|
7646
|
+
`<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>`
|
|
7647
|
+
);
|
|
7648
|
+
}
|
|
7649
|
+
}
|
|
7650
|
+
}
|
|
7651
|
+
renderModalFooterDecoration(pos, output) {
|
|
7652
|
+
output.push(
|
|
7653
|
+
`<line x1="${pos.x}" y1="${pos.y}" x2="${pos.x + pos.width}" y2="${pos.y}" stroke="${this.renderTheme.border}" stroke-width="1"/>`
|
|
7654
|
+
);
|
|
6589
7655
|
}
|
|
6590
7656
|
renderList(node, pos) {
|
|
6591
7657
|
const title = String(node.props.title || "");
|
|
6592
|
-
const itemsStr = String(node.props.items || "");
|
|
6593
7658
|
const mockType = String(node.props.mock || "").trim();
|
|
6594
7659
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
6595
|
-
let items =
|
|
6596
|
-
if (
|
|
6597
|
-
items = itemsStr.split(",").map((i) => i.trim()).filter(Boolean);
|
|
6598
|
-
} else {
|
|
7660
|
+
let items = toStringArray(node.props.items);
|
|
7661
|
+
if (items.length === 0) {
|
|
6599
7662
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
6600
7663
|
const itemCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
6601
7664
|
const resolvedMockType = mockType || "name";
|
|
@@ -6624,6 +7687,7 @@ var SVGRenderer = class {
|
|
|
6624
7687
|
items.forEach((item, i) => {
|
|
6625
7688
|
const itemY = pos.y + titleHeight + i * itemHeight;
|
|
6626
7689
|
if (itemY + itemHeight <= pos.y + pos.height) {
|
|
7690
|
+
const itemEventAttrs = this.getScopedEventAttrs(node, "onItemClick", { index: i });
|
|
6627
7691
|
svg += `
|
|
6628
7692
|
<line x1="${pos.x}" y1="${itemY + itemHeight}"
|
|
6629
7693
|
x2="${pos.x + pos.width}" y2="${itemY + itemHeight}"
|
|
@@ -6632,7 +7696,10 @@ var SVGRenderer = class {
|
|
|
6632
7696
|
<text x="${pos.x + padding}" y="${itemY + 24}"
|
|
6633
7697
|
font-family="Arial, Helvetica, sans-serif"
|
|
6634
7698
|
font-size="13"
|
|
6635
|
-
fill="${this.renderTheme.text}">${this.escapeXml(item)}</text
|
|
7699
|
+
fill="${this.renderTheme.text}">${this.escapeXml(item)}</text>
|
|
7700
|
+
${itemEventAttrs ? `<rect x="${pos.x}" y="${itemY}"
|
|
7701
|
+
width="${pos.width}" height="${itemHeight}"
|
|
7702
|
+
fill="transparent" stroke="none" pointer-events="all"${itemEventAttrs}/>` : ""}`;
|
|
6636
7703
|
}
|
|
6637
7704
|
});
|
|
6638
7705
|
svg += "\n </g>";
|
|
@@ -6866,8 +7933,8 @@ var SVGRenderer = class {
|
|
|
6866
7933
|
return svg;
|
|
6867
7934
|
}
|
|
6868
7935
|
renderBreadcrumbs(node, pos) {
|
|
6869
|
-
const
|
|
6870
|
-
const items =
|
|
7936
|
+
const parsedBreadcrumbs = toStringArray(node.props.items);
|
|
7937
|
+
const items = parsedBreadcrumbs.length > 0 ? parsedBreadcrumbs : ["Home"];
|
|
6871
7938
|
const separator = String(node.props.separator || "/");
|
|
6872
7939
|
const fontSize = 12;
|
|
6873
7940
|
const separatorWidth = 20;
|
|
@@ -6899,10 +7966,9 @@ var SVGRenderer = class {
|
|
|
6899
7966
|
return svg;
|
|
6900
7967
|
}
|
|
6901
7968
|
renderSidebarMenu(node, pos) {
|
|
6902
|
-
const
|
|
6903
|
-
const
|
|
6904
|
-
const
|
|
6905
|
-
const icons = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
7969
|
+
const parsedMenuItems = toStringArray(node.props.items);
|
|
7970
|
+
const items = parsedMenuItems.length > 0 ? parsedMenuItems : ["Item 1", "Item 2", "Item 3"];
|
|
7971
|
+
const icons = toStringArray(node.props.icons);
|
|
6906
7972
|
const itemHeight = 40;
|
|
6907
7973
|
const fontSize = 14;
|
|
6908
7974
|
const activeIndex = Number(node.props.active || 0);
|
|
@@ -6947,6 +8013,13 @@ var SVGRenderer = class {
|
|
|
6947
8013
|
font-size="${fontSize}"
|
|
6948
8014
|
font-weight="${fontWeight}"
|
|
6949
8015
|
fill="${textColor}">${this.escapeXml(item)}</text>`;
|
|
8016
|
+
const itemEventAttrs = this.getScopedEventAttrs(node, "onItemsClick", { index });
|
|
8017
|
+
if (itemEventAttrs) {
|
|
8018
|
+
svg += `
|
|
8019
|
+
<rect x="${pos.x}" y="${itemY}"
|
|
8020
|
+
width="${pos.width}" height="${itemHeight}"
|
|
8021
|
+
fill="transparent" stroke="none" pointer-events="all"${itemEventAttrs}/>`;
|
|
8022
|
+
}
|
|
6950
8023
|
});
|
|
6951
8024
|
svg += "\n </g>";
|
|
6952
8025
|
return svg;
|
|
@@ -7297,7 +8370,7 @@ var SVGRenderer = class {
|
|
|
7297
8370
|
userBadge = { x, y, width, height, label: userLabel };
|
|
7298
8371
|
rightCursor = x - 8;
|
|
7299
8372
|
}
|
|
7300
|
-
const actionLabels = actions
|
|
8373
|
+
const actionLabels = toStringArray(actions);
|
|
7301
8374
|
const actionHeight = 32;
|
|
7302
8375
|
const actionY = pos.y + (pos.height - actionHeight) / 2;
|
|
7303
8376
|
const actionGap = 8;
|
|
@@ -7383,11 +8456,99 @@ var SVGRenderer = class {
|
|
|
7383
8456
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
7384
8457
|
}
|
|
7385
8458
|
/**
|
|
7386
|
-
* Get data-node-id
|
|
7387
|
-
* Enables bidirectional selection between code and canvas
|
|
8459
|
+
* Get data-node-id and event data attributes for SVG elements.
|
|
8460
|
+
* Enables bidirectional selection between code and canvas (data-node-id)
|
|
8461
|
+
* and play test interactivity (data-event-*, data-user-id, data-tabs-id).
|
|
7388
8462
|
*/
|
|
7389
8463
|
getDataNodeId(node) {
|
|
7390
|
-
|
|
8464
|
+
let attrs = "";
|
|
8465
|
+
if (node.meta.nodeId) {
|
|
8466
|
+
attrs += ` data-node-id="${node.meta.nodeId}"`;
|
|
8467
|
+
}
|
|
8468
|
+
if (node.kind === "component") {
|
|
8469
|
+
if (node.userDefinedId) {
|
|
8470
|
+
attrs += ` data-user-id="${node.userDefinedId}"`;
|
|
8471
|
+
}
|
|
8472
|
+
if (node.events && node.events.length > 0) {
|
|
8473
|
+
for (const handler of node.events) {
|
|
8474
|
+
if (!this.isScopedEvent(handler.event)) {
|
|
8475
|
+
attrs += this.serializeEventHandler(handler);
|
|
8476
|
+
}
|
|
8477
|
+
}
|
|
8478
|
+
}
|
|
8479
|
+
}
|
|
8480
|
+
if (node.kind === "container") {
|
|
8481
|
+
if (node.containerType === "tabs" && node.params.id) {
|
|
8482
|
+
attrs += ` data-tabs-id="${node.params.id}"`;
|
|
8483
|
+
if (node.params.active !== void 0) {
|
|
8484
|
+
attrs += ` data-tabs-active="${node.params.active}"`;
|
|
8485
|
+
}
|
|
8486
|
+
}
|
|
8487
|
+
if (node.events && node.events.length > 0) {
|
|
8488
|
+
for (const handler of node.events) {
|
|
8489
|
+
if (node.containerType === "modal" && handler.event === "onClose") continue;
|
|
8490
|
+
attrs += this.serializeEventHandler(handler);
|
|
8491
|
+
}
|
|
8492
|
+
}
|
|
8493
|
+
}
|
|
8494
|
+
return attrs;
|
|
8495
|
+
}
|
|
8496
|
+
isScopedEvent(event) {
|
|
8497
|
+
return event === "onClose" || event === "onItemClick" || event === "onRowClick" || event === "onItemsClick";
|
|
8498
|
+
}
|
|
8499
|
+
getScopedEventAttrs(node, eventName, options = {}) {
|
|
8500
|
+
const handler = node.events?.find((event) => event.event === eventName);
|
|
8501
|
+
if (!handler) return "";
|
|
8502
|
+
let attrs = "";
|
|
8503
|
+
if (node.meta.nodeId) {
|
|
8504
|
+
attrs += ` data-node-id="${node.meta.nodeId}"`;
|
|
8505
|
+
}
|
|
8506
|
+
if (node.userDefinedId) {
|
|
8507
|
+
attrs += ` data-user-id="${node.userDefinedId}"`;
|
|
8508
|
+
}
|
|
8509
|
+
attrs += this.serializeEventHandler(handler);
|
|
8510
|
+
if (options.index !== void 0) {
|
|
8511
|
+
attrs += ` data-event-index="${options.index}"`;
|
|
8512
|
+
}
|
|
8513
|
+
return attrs;
|
|
8514
|
+
}
|
|
8515
|
+
getTabsTriggerAttrs(node, index) {
|
|
8516
|
+
const tabsId = String(node.props.tabsId || "").trim();
|
|
8517
|
+
if (!tabsId) return "";
|
|
8518
|
+
let attrs = "";
|
|
8519
|
+
if (node.meta.nodeId) {
|
|
8520
|
+
attrs += ` data-node-id="${node.meta.nodeId}"`;
|
|
8521
|
+
}
|
|
8522
|
+
attrs += ` data-tabs-id="${tabsId}" data-tabs-trigger-index="${index}"`;
|
|
8523
|
+
return attrs;
|
|
8524
|
+
}
|
|
8525
|
+
serializeEventHandler(handler) {
|
|
8526
|
+
const attrName = this.eventNameToDataAttr(handler.event);
|
|
8527
|
+
const value = handler.actions.map((a) => this.serializeEventAction(a)).join("|");
|
|
8528
|
+
return ` data-event-${attrName}="${value}"`;
|
|
8529
|
+
}
|
|
8530
|
+
eventNameToDataAttr(event) {
|
|
8531
|
+
return event.replace(/^on/, "").toLowerCase();
|
|
8532
|
+
}
|
|
8533
|
+
serializeEventAction(action) {
|
|
8534
|
+
switch (action.type) {
|
|
8535
|
+
case "navigate":
|
|
8536
|
+
return `navigate:${action.screen}`;
|
|
8537
|
+
case "show":
|
|
8538
|
+
return `show:${action.targetId}`;
|
|
8539
|
+
case "hide":
|
|
8540
|
+
return `hide:${action.targetId}`;
|
|
8541
|
+
case "toggle":
|
|
8542
|
+
return `toggle:${action.targetId}`;
|
|
8543
|
+
case "enable":
|
|
8544
|
+
return `enable:${action.targetId}`;
|
|
8545
|
+
case "disable":
|
|
8546
|
+
return `disable:${action.targetId}`;
|
|
8547
|
+
case "setTab":
|
|
8548
|
+
return `setTab:${action.tabsId}:${action.index}`;
|
|
8549
|
+
case "navigateItems":
|
|
8550
|
+
return action.screens.map((s) => `navigate:${s}`).join(",");
|
|
8551
|
+
}
|
|
7391
8552
|
}
|
|
7392
8553
|
};
|
|
7393
8554
|
function renderToSVG(ir, layout, options) {
|
|
@@ -7477,8 +8638,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
7477
8638
|
* Render breadcrumbs as skeleton blocks: <rect> / <rect> / <rect accent>
|
|
7478
8639
|
*/
|
|
7479
8640
|
renderBreadcrumbs(node, pos) {
|
|
7480
|
-
const
|
|
7481
|
-
const items = itemsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
8641
|
+
const items = toStringArray(node.props.items || "Home");
|
|
7482
8642
|
const separator = String(node.props.separator || "/");
|
|
7483
8643
|
const blockColor = this.renderTheme.border;
|
|
7484
8644
|
const charWidth = 6.2;
|
|
@@ -7799,10 +8959,9 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
7799
8959
|
*/
|
|
7800
8960
|
renderTable(node, pos) {
|
|
7801
8961
|
const title = String(node.props.title || "");
|
|
7802
|
-
const
|
|
7803
|
-
const columns = columnsStr.split(",").map((c) => c.trim()).filter(Boolean);
|
|
8962
|
+
const columns = toStringArray(node.props.columns || "Col1,Col2,Col3");
|
|
7804
8963
|
const rowCount = Number(node.props.rows || node.props.rowsMock || 5);
|
|
7805
|
-
const actions =
|
|
8964
|
+
const actions = toStringArray(node.props.actions);
|
|
7806
8965
|
const hasActions = actions.length > 0;
|
|
7807
8966
|
const pagination = this.parseBooleanProp(node.props.pagination, false);
|
|
7808
8967
|
const parsedPageCount = Number(node.props.pages || 5);
|
|
@@ -8088,7 +9247,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
8088
9247
|
const itemsStr = String(node.props.items || "");
|
|
8089
9248
|
let items = [];
|
|
8090
9249
|
if (itemsStr) {
|
|
8091
|
-
items = itemsStr
|
|
9250
|
+
items = toStringArray(itemsStr);
|
|
8092
9251
|
} else {
|
|
8093
9252
|
const itemCount = Number(node.props.itemsMock || 6);
|
|
8094
9253
|
items = Array(itemCount).fill("Item");
|
|
@@ -8125,8 +9284,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
8125
9284
|
* Render SidebarMenu with gray blocks instead of text and no icons
|
|
8126
9285
|
*/
|
|
8127
9286
|
renderSidebarMenu(node, pos) {
|
|
8128
|
-
const
|
|
8129
|
-
const items = itemsStr.split(",").map((s) => s.trim());
|
|
9287
|
+
const items = toStringArray(node.props.items || "Item 1,Item 2,Item 3");
|
|
8130
9288
|
const itemHeight = 40;
|
|
8131
9289
|
const activeIndex = Number(node.props.active || 0);
|
|
8132
9290
|
const accentColor = this.resolveAccentColor();
|
|
@@ -8974,7 +10132,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8974
10132
|
const title = String(node.props.title || "Sidebar");
|
|
8975
10133
|
const itemsStr = String(node.props.items || "");
|
|
8976
10134
|
const activeItem = String(node.props.active || "");
|
|
8977
|
-
const
|
|
10135
|
+
const parsed = toStringArray(itemsStr);
|
|
10136
|
+
const items = parsed.length ? parsed : ["Item 1", "Item 2", "Item 3"];
|
|
8978
10137
|
const itemHeight = 40;
|
|
8979
10138
|
const padding = 16;
|
|
8980
10139
|
const titleHeight = 40;
|
|
@@ -9017,7 +10176,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9017
10176
|
*/
|
|
9018
10177
|
renderTabs(node, pos) {
|
|
9019
10178
|
const itemsStr = String(node.props.items || "");
|
|
9020
|
-
const
|
|
10179
|
+
const parsedTabs = toStringArray(itemsStr);
|
|
10180
|
+
const tabs = parsedTabs.length ? parsedTabs : ["Tab 1", "Tab 2", "Tab 3"];
|
|
9021
10181
|
const activeProp = node.props.active ?? 0;
|
|
9022
10182
|
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
9023
10183
|
const variant = String(node.props.variant || "default");
|
|
@@ -9029,7 +10189,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9029
10189
|
const fontSize = 13;
|
|
9030
10190
|
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
9031
10191
|
const iconsStr = String(node.props.icons || "");
|
|
9032
|
-
const iconList = iconsStr
|
|
10192
|
+
const iconList = toStringArray(iconsStr);
|
|
9033
10193
|
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
9034
10194
|
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
9035
10195
|
const tabWidth = pos.width / tabs.length;
|
|
@@ -9194,7 +10354,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9194
10354
|
<!-- Modal backdrop -->
|
|
9195
10355
|
<rect x="0" y="0"
|
|
9196
10356
|
width="${this.options.width}" height="${overlayHeight}"
|
|
9197
|
-
fill="black" opacity="0.28"/>
|
|
10357
|
+
fill="black" opacity="0.28" pointer-events="none"/>
|
|
9198
10358
|
|
|
9199
10359
|
<!-- Modal box -->
|
|
9200
10360
|
<rect x="${modalX}" y="${modalY}"
|
|
@@ -9222,7 +10382,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9222
10382
|
<text x="${modalX + pos.width - 16}" y="${modalY + padding + 12}"
|
|
9223
10383
|
font-family="${this.fontFamily}"
|
|
9224
10384
|
font-size="18"
|
|
9225
|
-
fill="${this.renderTheme.textMuted}"
|
|
10385
|
+
fill="${this.renderTheme.textMuted}"
|
|
10386
|
+
pointer-events="none">\u2715</text>
|
|
9226
10387
|
|
|
9227
10388
|
<!-- Content placeholder -->
|
|
9228
10389
|
<text x="${modalX + pos.width / 2}" y="${modalY + headerHeight + (pos.height - headerHeight) / 2}"
|
|
@@ -9240,10 +10401,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9240
10401
|
const itemsStr = String(node.props.items || "");
|
|
9241
10402
|
const mockType = String(node.props.mock || "").trim();
|
|
9242
10403
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
9243
|
-
let items =
|
|
9244
|
-
if (
|
|
9245
|
-
items = itemsStr.split(",").map((i) => i.trim()).filter(Boolean);
|
|
9246
|
-
} else {
|
|
10404
|
+
let items = toStringArray(itemsStr);
|
|
10405
|
+
if (!items.length) {
|
|
9247
10406
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
9248
10407
|
const itemCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
9249
10408
|
const resolvedMockType = mockType || "name";
|
|
@@ -9457,7 +10616,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9457
10616
|
*/
|
|
9458
10617
|
renderBreadcrumbs(node, pos) {
|
|
9459
10618
|
const itemsStr = String(node.props.items || "Home");
|
|
9460
|
-
const items = itemsStr
|
|
10619
|
+
const items = toStringArray(itemsStr);
|
|
9461
10620
|
const separator = String(node.props.separator || "/");
|
|
9462
10621
|
const fontSize = 12;
|
|
9463
10622
|
const separatorWidth = 20;
|
|
@@ -9494,8 +10653,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9494
10653
|
renderSidebarMenu(node, pos) {
|
|
9495
10654
|
const itemsStr = String(node.props.items || "Item 1,Item 2,Item 3");
|
|
9496
10655
|
const iconsStr = String(node.props.icons || "");
|
|
9497
|
-
const items = itemsStr
|
|
9498
|
-
const icons = iconsStr
|
|
10656
|
+
const items = toStringArray(itemsStr);
|
|
10657
|
+
const icons = toStringArray(iconsStr);
|
|
9499
10658
|
const itemHeight = 40;
|
|
9500
10659
|
const fontSize = 14;
|
|
9501
10660
|
const activeIndex = Number(node.props.active || 0);
|
|
@@ -9815,11 +10974,13 @@ export {
|
|
|
9815
10974
|
DEVICE_PRESETS,
|
|
9816
10975
|
IRGenerator,
|
|
9817
10976
|
LayoutEngine,
|
|
10977
|
+
SELF_TARGET,
|
|
9818
10978
|
SVGRenderer,
|
|
9819
10979
|
SkeletonSVGRenderer,
|
|
9820
10980
|
SketchSVGRenderer,
|
|
9821
10981
|
SourceMapBuilder,
|
|
9822
10982
|
SourceMapResolver,
|
|
10983
|
+
applyStateChange,
|
|
9823
10984
|
buildSVG,
|
|
9824
10985
|
calculateLayout,
|
|
9825
10986
|
createSVGElement,
|