@wire-dsl/engine 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1369 -207
- package/dist/index.d.cts +211 -14
- package/dist/index.d.ts +211 -14
- package/dist/index.js +1367 -207
- package/package.json +2 -2
package/dist/index.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,9 +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;
|
|
3062
|
-
if ((isVerticalStack || isHorizontalStack || node.containerType === "card") && node.children.length > 0) {
|
|
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) {
|
|
3063
3977
|
let containerMaxY = y;
|
|
3064
3978
|
node.children.forEach((childRef) => {
|
|
3065
3979
|
const childPos = this.result[childRef.ref];
|
|
@@ -3079,8 +3993,22 @@ var LayoutEngine = class {
|
|
|
3079
3993
|
const children = node.children;
|
|
3080
3994
|
if (direction === "vertical") {
|
|
3081
3995
|
let currentY = y;
|
|
3082
|
-
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) => {
|
|
3083
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
|
+
}
|
|
3084
4012
|
let childHeight = this.getComponentHeight();
|
|
3085
4013
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
3086
4014
|
childHeight = Number(childNode.props.height);
|
|
@@ -3091,12 +4019,17 @@ var LayoutEngine = class {
|
|
|
3091
4019
|
}
|
|
3092
4020
|
this.calculateNode(childRef.ref, x, currentY, width, childHeight, "stack");
|
|
3093
4021
|
currentY += childHeight;
|
|
3094
|
-
|
|
4022
|
+
flowCount++;
|
|
4023
|
+
if (flowCount < flowChildren.length) {
|
|
3095
4024
|
currentY += gap;
|
|
3096
4025
|
}
|
|
3097
4026
|
});
|
|
3098
4027
|
let adjustedY = y;
|
|
3099
|
-
|
|
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;
|
|
3100
4033
|
const childPos = this.result[childRef.ref];
|
|
3101
4034
|
if (childPos) {
|
|
3102
4035
|
const deltaY = adjustedY - childPos.y;
|
|
@@ -3105,7 +4038,8 @@ var LayoutEngine = class {
|
|
|
3105
4038
|
this.adjustNodeYPositions(childRef.ref, deltaY);
|
|
3106
4039
|
}
|
|
3107
4040
|
adjustedY += childPos.height;
|
|
3108
|
-
|
|
4041
|
+
adjustedCount++;
|
|
4042
|
+
if (adjustedCount < flowChildren.length) {
|
|
3109
4043
|
adjustedY += gap;
|
|
3110
4044
|
}
|
|
3111
4045
|
}
|
|
@@ -3115,9 +4049,10 @@ var LayoutEngine = class {
|
|
|
3115
4049
|
const crossAlign = node.style.align || "start";
|
|
3116
4050
|
if (justify === "stretch") {
|
|
3117
4051
|
let currentX = x;
|
|
3118
|
-
const
|
|
4052
|
+
const visibleChildren = children.filter((cr) => this.isNodeVisible(cr.ref));
|
|
4053
|
+
const childWidth = this.calculateChildWidth(visibleChildren.length, width, gap);
|
|
3119
4054
|
let stackHeight = 0;
|
|
3120
|
-
|
|
4055
|
+
visibleChildren.forEach((childRef) => {
|
|
3121
4056
|
const childNode = this.nodes[childRef.ref];
|
|
3122
4057
|
let childHeight = this.getComponentHeight();
|
|
3123
4058
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
@@ -3130,6 +4065,10 @@ var LayoutEngine = class {
|
|
|
3130
4065
|
stackHeight = Math.max(stackHeight, childHeight);
|
|
3131
4066
|
});
|
|
3132
4067
|
children.forEach((childRef) => {
|
|
4068
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4069
|
+
this.calculateNode(childRef.ref, currentX, y, 0, 0, "stack");
|
|
4070
|
+
return;
|
|
4071
|
+
}
|
|
3133
4072
|
this.calculateNode(childRef.ref, currentX, y, childWidth, stackHeight, "stack");
|
|
3134
4073
|
currentX += childWidth + gap;
|
|
3135
4074
|
});
|
|
@@ -3138,9 +4077,18 @@ var LayoutEngine = class {
|
|
|
3138
4077
|
const childHeights = [];
|
|
3139
4078
|
const explicitHeightFlags = [];
|
|
3140
4079
|
const flexIndices = /* @__PURE__ */ new Set();
|
|
4080
|
+
const visibleFlags = [];
|
|
3141
4081
|
let stackHeight = 0;
|
|
3142
4082
|
children.forEach((childRef, index) => {
|
|
3143
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
|
+
}
|
|
3144
4092
|
const hasExplicitHeight = childNode?.kind === "component" && !!childNode.props.height;
|
|
3145
4093
|
const hasExplicitWidth = childNode?.kind === "component" && !!childNode.props.width;
|
|
3146
4094
|
const isBlockButton = childNode?.kind === "component" && childNode.componentType === "Button" && !hasExplicitWidth && this.parseBooleanProp(childNode.props.block, false);
|
|
@@ -3158,7 +4106,8 @@ var LayoutEngine = class {
|
|
|
3158
4106
|
childHeights.push(this.getComponentHeight());
|
|
3159
4107
|
explicitHeightFlags.push(hasExplicitHeight);
|
|
3160
4108
|
});
|
|
3161
|
-
const
|
|
4109
|
+
const visibleCount = visibleFlags.filter(Boolean).length;
|
|
4110
|
+
const totalGapWidth = gap * Math.max(0, visibleCount - 1);
|
|
3162
4111
|
if (flexIndices.size > 0) {
|
|
3163
4112
|
const fixedWidth = childWidths.reduce((sum, w, idx) => {
|
|
3164
4113
|
return flexIndices.has(idx) ? sum : sum + w;
|
|
@@ -3170,6 +4119,7 @@ var LayoutEngine = class {
|
|
|
3170
4119
|
});
|
|
3171
4120
|
}
|
|
3172
4121
|
children.forEach((childRef, index) => {
|
|
4122
|
+
if (!visibleFlags[index]) return;
|
|
3173
4123
|
const childNode = this.nodes[childRef.ref];
|
|
3174
4124
|
const childWidth = childWidths[index];
|
|
3175
4125
|
let childHeight = this.getComponentHeight();
|
|
@@ -3193,14 +4143,18 @@ var LayoutEngine = class {
|
|
|
3193
4143
|
startX = x + width - totalContentWidth;
|
|
3194
4144
|
} else if (justify === "spaceBetween") {
|
|
3195
4145
|
startX = x;
|
|
3196
|
-
dynamicGap =
|
|
4146
|
+
dynamicGap = visibleCount > 1 ? (width - totalChildWidth) / (visibleCount - 1) : 0;
|
|
3197
4147
|
} else if (justify === "spaceAround") {
|
|
3198
|
-
const spacing =
|
|
4148
|
+
const spacing = visibleCount > 0 ? (width - totalChildWidth) / visibleCount : 0;
|
|
3199
4149
|
startX = x + spacing / 2;
|
|
3200
4150
|
dynamicGap = spacing;
|
|
3201
4151
|
}
|
|
3202
4152
|
let currentX = startX;
|
|
3203
4153
|
children.forEach((childRef, index) => {
|
|
4154
|
+
if (!visibleFlags[index]) {
|
|
4155
|
+
this.calculateNode(childRef.ref, currentX, y, 0, 0, "stack");
|
|
4156
|
+
return;
|
|
4157
|
+
}
|
|
3204
4158
|
const childWidth = childWidths[index];
|
|
3205
4159
|
const childHeight = childHeights[index];
|
|
3206
4160
|
let childY = y;
|
|
@@ -3232,6 +4186,7 @@ var LayoutEngine = class {
|
|
|
3232
4186
|
let currentRowMaxHeight = 0;
|
|
3233
4187
|
const rowHeights = [0];
|
|
3234
4188
|
node.children.forEach((childRef) => {
|
|
4189
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3235
4190
|
const child = this.nodes[childRef.ref];
|
|
3236
4191
|
let span = 1;
|
|
3237
4192
|
let childHeight = this.getComponentHeight();
|
|
@@ -3266,6 +4221,18 @@ var LayoutEngine = class {
|
|
|
3266
4221
|
}
|
|
3267
4222
|
return totalHeight;
|
|
3268
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
|
+
}
|
|
3269
4236
|
if (node.containerType === "split") {
|
|
3270
4237
|
const splitGap = this.resolveSpacing(node.style.gap);
|
|
3271
4238
|
const leftParam = node.params.left;
|
|
@@ -3278,6 +4245,7 @@ var LayoutEngine = class {
|
|
|
3278
4245
|
const rightWidth = Number.isFinite(rightWidthRaw) && rightWidthRaw > 0 ? rightWidthRaw : availableWidth / 2;
|
|
3279
4246
|
let maxHeight = 0;
|
|
3280
4247
|
node.children.forEach((childRef, index) => {
|
|
4248
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3281
4249
|
const child = this.nodes[childRef.ref];
|
|
3282
4250
|
let childHeight = this.getComponentHeight();
|
|
3283
4251
|
const isFirst = index === 0;
|
|
@@ -3305,6 +4273,7 @@ var LayoutEngine = class {
|
|
|
3305
4273
|
if (node.containerType === "stack" && direction === "horizontal") {
|
|
3306
4274
|
let maxHeight = 0;
|
|
3307
4275
|
node.children.forEach((childRef) => {
|
|
4276
|
+
if (!this.isNodeVisible(childRef.ref)) return;
|
|
3308
4277
|
const child = this.nodes[childRef.ref];
|
|
3309
4278
|
let childHeight = this.getComponentHeight();
|
|
3310
4279
|
if (child?.kind === "component") {
|
|
@@ -3321,7 +4290,10 @@ var LayoutEngine = class {
|
|
|
3321
4290
|
totalHeight += maxHeight;
|
|
3322
4291
|
return totalHeight;
|
|
3323
4292
|
}
|
|
3324
|
-
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;
|
|
3325
4297
|
const child = this.nodes[childRef.ref];
|
|
3326
4298
|
let childHeight = this.getComponentHeight();
|
|
3327
4299
|
if (child?.kind === "component") {
|
|
@@ -3334,7 +4306,8 @@ var LayoutEngine = class {
|
|
|
3334
4306
|
childHeight = this.calculateContainerHeight(child, availableWidth);
|
|
3335
4307
|
}
|
|
3336
4308
|
totalHeight += childHeight;
|
|
3337
|
-
|
|
4309
|
+
linearIndex++;
|
|
4310
|
+
if (linearIndex < visibleLinear.length) {
|
|
3338
4311
|
totalHeight += gap;
|
|
3339
4312
|
}
|
|
3340
4313
|
});
|
|
@@ -3347,6 +4320,10 @@ var LayoutEngine = class {
|
|
|
3347
4320
|
const colWidth = (width - gap * (columns - 1)) / columns;
|
|
3348
4321
|
const cellHeights = {};
|
|
3349
4322
|
node.children.forEach((childRef, cellIndex) => {
|
|
4323
|
+
if (!this.isNodeVisible(childRef.ref)) {
|
|
4324
|
+
cellHeights[cellIndex] = 0;
|
|
4325
|
+
return;
|
|
4326
|
+
}
|
|
3350
4327
|
const child = this.nodes[childRef.ref];
|
|
3351
4328
|
let cellHeight = this.getComponentHeight();
|
|
3352
4329
|
let span = 1;
|
|
@@ -3371,6 +4348,11 @@ var LayoutEngine = class {
|
|
|
3371
4348
|
const rowHeights = [0];
|
|
3372
4349
|
const cellPositions = [];
|
|
3373
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
|
+
}
|
|
3374
4356
|
const child = this.nodes[childRef.ref];
|
|
3375
4357
|
let span = 1;
|
|
3376
4358
|
if (child?.kind === "container" && child.meta?.source === "cell") {
|
|
@@ -3382,13 +4364,17 @@ var LayoutEngine = class {
|
|
|
3382
4364
|
currentCol = 0;
|
|
3383
4365
|
currentRowMaxHeight = 0;
|
|
3384
4366
|
}
|
|
3385
|
-
cellPositions.push({ row: currentRow, col: currentCol, span });
|
|
4367
|
+
cellPositions.push({ row: currentRow, col: currentCol, span, visible: true });
|
|
3386
4368
|
currentRowMaxHeight = Math.max(currentRowMaxHeight, cellHeights[cellIndex]);
|
|
3387
4369
|
currentCol += span;
|
|
3388
4370
|
});
|
|
3389
4371
|
rowHeights[currentRow] = currentRowMaxHeight;
|
|
3390
4372
|
node.children.forEach((childRef, cellIndex) => {
|
|
3391
|
-
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
|
+
}
|
|
3392
4378
|
const cellHeight = rowHeights[row];
|
|
3393
4379
|
let cellY = y;
|
|
3394
4380
|
for (let r = 0; r < row; r++) {
|
|
@@ -3459,8 +4445,14 @@ var LayoutEngine = class {
|
|
|
3459
4445
|
const innerCardWidth = width - cardPadding * 2;
|
|
3460
4446
|
const children = node.children;
|
|
3461
4447
|
let currentY = y + cardPadding;
|
|
3462
|
-
children.
|
|
4448
|
+
const flowChildren = children.filter((cr) => this.isNodeVisible(cr.ref));
|
|
4449
|
+
let flowCount = 0;
|
|
4450
|
+
children.forEach((childRef) => {
|
|
3463
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
|
+
}
|
|
3464
4456
|
let childHeight = this.getComponentHeight();
|
|
3465
4457
|
if (childNode?.kind === "component" && childNode.props.height) {
|
|
3466
4458
|
childHeight = Number(childNode.props.height);
|
|
@@ -3471,11 +4463,63 @@ var LayoutEngine = class {
|
|
|
3471
4463
|
}
|
|
3472
4464
|
this.calculateNode(childRef.ref, x + cardPadding, currentY, innerCardWidth, childHeight, "card");
|
|
3473
4465
|
currentY += childHeight;
|
|
3474
|
-
|
|
4466
|
+
flowCount++;
|
|
4467
|
+
if (flowCount < flowChildren.length) {
|
|
3475
4468
|
currentY += gap;
|
|
3476
4469
|
}
|
|
3477
4470
|
});
|
|
3478
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
|
+
}
|
|
3479
4523
|
/**
|
|
3480
4524
|
* Calculate layout for an instance node.
|
|
3481
4525
|
* The instance is a transparent wrapper — its bounding box equals the
|
|
@@ -3706,8 +4750,7 @@ var LayoutEngine = class {
|
|
|
3706
4750
|
return Math.max(this.getComponentHeight(), wrappedHeight);
|
|
3707
4751
|
}
|
|
3708
4752
|
if (node.componentType === "SidebarMenu") {
|
|
3709
|
-
const
|
|
3710
|
-
const items = itemsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4753
|
+
const items = toStringArray(node.props.items);
|
|
3711
4754
|
const itemCount = items.length > 0 ? items.length : 3;
|
|
3712
4755
|
const itemHeight = 40;
|
|
3713
4756
|
return Math.max(this.getComponentHeight(), itemCount * itemHeight);
|
|
@@ -3718,7 +4761,7 @@ var LayoutEngine = class {
|
|
|
3718
4761
|
if (node.componentType === "Stat") return 120;
|
|
3719
4762
|
if (node.componentType === "Chart") return 250;
|
|
3720
4763
|
if (node.componentType === "List") {
|
|
3721
|
-
const itemsFromProps =
|
|
4764
|
+
const itemsFromProps = toStringArray(node.props.items);
|
|
3722
4765
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
3723
4766
|
const fallbackCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
3724
4767
|
const itemCount = itemsFromProps.length > 0 ? itemsFromProps.length : fallbackCount;
|
|
@@ -3896,6 +4939,17 @@ var LayoutEngine = class {
|
|
|
3896
4939
|
}
|
|
3897
4940
|
return width;
|
|
3898
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
|
+
}
|
|
3899
4953
|
parseBooleanProp(value, fallback = false) {
|
|
3900
4954
|
if (typeof value === "boolean") return value;
|
|
3901
4955
|
if (typeof value === "string") {
|
|
@@ -3986,7 +5040,7 @@ var MockDataGenerator = class {
|
|
|
3986
5040
|
for (const [key, rawValues] of Object.entries(mocks)) {
|
|
3987
5041
|
let values = [];
|
|
3988
5042
|
if (typeof rawValues === "string") {
|
|
3989
|
-
values = rawValues
|
|
5043
|
+
values = toStringArray(rawValues);
|
|
3990
5044
|
} else if (Array.isArray(rawValues)) {
|
|
3991
5045
|
values = rawValues.map((v) => typeof v === "string" ? v.trim() : "").filter((v) => v.length > 0);
|
|
3992
5046
|
}
|
|
@@ -4078,7 +5132,7 @@ var MockDataGenerator = class {
|
|
|
4078
5132
|
* columns: "id,name,status,amount"
|
|
4079
5133
|
*/
|
|
4080
5134
|
static generateMockRow(columns, rowIndex, random = false) {
|
|
4081
|
-
const columnNames = columns
|
|
5135
|
+
const columnNames = toStringArray(columns);
|
|
4082
5136
|
const row = {};
|
|
4083
5137
|
columnNames.forEach((col) => {
|
|
4084
5138
|
const mockType = this.inferMockTypeFromColumn(col);
|
|
@@ -5130,6 +6184,9 @@ var SVGRenderer = class {
|
|
|
5130
6184
|
if (!node || !pos) return;
|
|
5131
6185
|
this.renderedNodeIds.add(nodeId);
|
|
5132
6186
|
if (node.kind === "container") {
|
|
6187
|
+
if (String(node.params.visible) === "false") {
|
|
6188
|
+
return;
|
|
6189
|
+
}
|
|
5133
6190
|
const containerGroup = [];
|
|
5134
6191
|
const hasNodeId = node.meta?.nodeId;
|
|
5135
6192
|
if (hasNodeId) {
|
|
@@ -5150,6 +6207,12 @@ var SVGRenderer = class {
|
|
|
5150
6207
|
if (node.containerType === "split") {
|
|
5151
6208
|
this.renderSplitDecoration(node, pos, containerGroup);
|
|
5152
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
|
+
}
|
|
5153
6216
|
const isCellContainer = node.meta?.source === "cell";
|
|
5154
6217
|
if (node.children.length === 0 && this.options.showDiagnostics && !isCellContainer) {
|
|
5155
6218
|
containerGroup.push(this.renderEmptyContainerDiagnostic(pos, node.containerType));
|
|
@@ -5176,6 +6239,9 @@ var SVGRenderer = class {
|
|
|
5176
6239
|
}
|
|
5177
6240
|
output.push(...instanceGroup);
|
|
5178
6241
|
} else if (node.kind === "component") {
|
|
6242
|
+
if (String(node.props.visible) === "false") {
|
|
6243
|
+
return;
|
|
6244
|
+
}
|
|
5179
6245
|
const componentSvg = this.renderComponent(node, pos);
|
|
5180
6246
|
if (componentSvg) {
|
|
5181
6247
|
output.push(componentSvg);
|
|
@@ -5237,8 +6303,6 @@ var SVGRenderer = class {
|
|
|
5237
6303
|
return this.renderAlert(node, pos);
|
|
5238
6304
|
case "Badge":
|
|
5239
6305
|
return this.renderBadge(node, pos);
|
|
5240
|
-
case "Modal":
|
|
5241
|
-
return this.renderModal(node, pos);
|
|
5242
6306
|
case "List":
|
|
5243
6307
|
return this.renderList(node, pos);
|
|
5244
6308
|
case "Stat":
|
|
@@ -5681,8 +6745,8 @@ var SVGRenderer = class {
|
|
|
5681
6745
|
}
|
|
5682
6746
|
renderTable(node, pos) {
|
|
5683
6747
|
const title = String(node.props.title || "");
|
|
5684
|
-
const
|
|
5685
|
-
const columns =
|
|
6748
|
+
const parsedColumns = toStringArray(node.props.columns);
|
|
6749
|
+
const columns = parsedColumns.length > 0 ? parsedColumns : ["Col1", "Col2", "Col3"];
|
|
5686
6750
|
const rowCount = Number(node.props.rows || node.props.rowsMock || 5);
|
|
5687
6751
|
const mockStr = String(node.props.mock || "");
|
|
5688
6752
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
@@ -5690,7 +6754,7 @@ var SVGRenderer = class {
|
|
|
5690
6754
|
const parsedPageCount = Number(node.props.pages || 5);
|
|
5691
6755
|
const pageCount = Number.isFinite(parsedPageCount) && parsedPageCount > 0 ? Math.floor(parsedPageCount) : 5;
|
|
5692
6756
|
const paginationAlign = String(node.props.paginationAlign || "right");
|
|
5693
|
-
const actions =
|
|
6757
|
+
const actions = toStringArray(node.props.actions);
|
|
5694
6758
|
const hasActions = actions.length > 0;
|
|
5695
6759
|
const caption = String(node.props.caption || "").trim();
|
|
5696
6760
|
const hasCaption = caption.length > 0;
|
|
@@ -5703,7 +6767,7 @@ var SVGRenderer = class {
|
|
|
5703
6767
|
const rawCaptionAlign = String(node.props.captionAlign || "");
|
|
5704
6768
|
const captionAlign = rawCaptionAlign === "left" || rawCaptionAlign === "center" || rawCaptionAlign === "right" ? rawCaptionAlign : paginationAlign === "left" ? "right" : "left";
|
|
5705
6769
|
const sameFooterAlign = hasCaption && pagination && captionAlign === paginationAlign;
|
|
5706
|
-
const mockTypes = mockStr
|
|
6770
|
+
const mockTypes = toStringArray(mockStr);
|
|
5707
6771
|
const safeColumns = columns.length > 0 ? columns : ["Column"];
|
|
5708
6772
|
while (mockTypes.length < safeColumns.length) {
|
|
5709
6773
|
const inferred = MockDataGenerator.inferMockTypeFromColumn(safeColumns[mockTypes.length] || "item");
|
|
@@ -5803,6 +6867,13 @@ var SVGRenderer = class {
|
|
|
5803
6867
|
currentX += buttonSize + buttonGap;
|
|
5804
6868
|
});
|
|
5805
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
|
+
}
|
|
5806
6877
|
});
|
|
5807
6878
|
const footerTop = headerY + headerHeight + mockRows.length * rowHeight + 16;
|
|
5808
6879
|
if (pagination) {
|
|
@@ -6192,14 +7263,15 @@ var SVGRenderer = class {
|
|
|
6192
7263
|
const label = String(node.props.label || "Checkbox");
|
|
6193
7264
|
const checked = String(node.props.checked || "false").toLowerCase() === "true";
|
|
6194
7265
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7266
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6195
7267
|
const controlColor = this.resolveControlColor();
|
|
6196
7268
|
const checkboxSize = 18;
|
|
6197
7269
|
const checkboxY = pos.y + pos.height / 2 - checkboxSize / 2;
|
|
6198
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6199
|
-
<rect x="${pos.x}" y="${checkboxY}"
|
|
6200
|
-
width="${checkboxSize}" height="${checkboxSize}"
|
|
6201
|
-
rx="4"
|
|
6202
|
-
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}"
|
|
6203
7275
|
stroke="${this.renderTheme.border}"
|
|
6204
7276
|
stroke-width="1"/>
|
|
6205
7277
|
${checked ? `<text x="${pos.x + checkboxSize / 2}" y="${checkboxY + 14}"
|
|
@@ -6217,13 +7289,14 @@ var SVGRenderer = class {
|
|
|
6217
7289
|
const label = String(node.props.label || "Radio");
|
|
6218
7290
|
const checked = String(node.props.checked || "false").toLowerCase() === "true";
|
|
6219
7291
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7292
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6220
7293
|
const controlColor = this.resolveControlColor();
|
|
6221
7294
|
const radioSize = 16;
|
|
6222
7295
|
const radioY = pos.y + pos.height / 2 - radioSize / 2;
|
|
6223
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6224
|
-
<circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
|
|
6225
|
-
r="${radioSize / 2}"
|
|
6226
|
-
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}"
|
|
6227
7300
|
stroke="${this.renderTheme.border}"
|
|
6228
7301
|
stroke-width="1"/>
|
|
6229
7302
|
${checked ? `<circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
|
|
@@ -6239,12 +7312,13 @@ var SVGRenderer = class {
|
|
|
6239
7312
|
const label = String(node.props.label || "Toggle");
|
|
6240
7313
|
const enabled = String(node.props.enabled || "false").toLowerCase() === "true";
|
|
6241
7314
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
7315
|
+
const clickable = String(node.props.clickable ?? "true") !== "false";
|
|
6242
7316
|
const controlColor = this.resolveControlColor();
|
|
6243
7317
|
const toggleWidth = 40;
|
|
6244
7318
|
const toggleHeight = 20;
|
|
6245
7319
|
const toggleY = pos.y + pos.height / 2 - toggleHeight / 2;
|
|
6246
|
-
return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
|
|
6247
|
-
<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}"
|
|
6248
7322
|
width="${toggleWidth}" height="${toggleHeight}"
|
|
6249
7323
|
rx="10"
|
|
6250
7324
|
fill="${enabled ? controlColor : this.renderTheme.border}"
|
|
@@ -6263,12 +7337,9 @@ var SVGRenderer = class {
|
|
|
6263
7337
|
// ============================================================================
|
|
6264
7338
|
renderSidebar(node, pos) {
|
|
6265
7339
|
const title = String(node.props.title || "Sidebar");
|
|
6266
|
-
const itemsStr = String(node.props.items || "");
|
|
6267
7340
|
const activeItem = String(node.props.active || "");
|
|
6268
|
-
let items =
|
|
6269
|
-
if (
|
|
6270
|
-
items = itemsStr.split(",").map((i) => i.trim());
|
|
6271
|
-
} else {
|
|
7341
|
+
let items = toStringArray(node.props.items);
|
|
7342
|
+
if (items.length === 0) {
|
|
6272
7343
|
const itemCount = Number(node.props.itemsMock || 6);
|
|
6273
7344
|
items = MockDataGenerator.generateMockList("name", itemCount);
|
|
6274
7345
|
}
|
|
@@ -6307,9 +7378,9 @@ var SVGRenderer = class {
|
|
|
6307
7378
|
return svg;
|
|
6308
7379
|
}
|
|
6309
7380
|
renderTabs(node, pos) {
|
|
6310
|
-
const
|
|
6311
|
-
const tabs =
|
|
6312
|
-
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;
|
|
6313
7384
|
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
6314
7385
|
const variant = String(node.props.variant || "default");
|
|
6315
7386
|
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
@@ -6319,8 +7390,7 @@ var SVGRenderer = class {
|
|
|
6319
7390
|
const tabHeight = pos.height > 0 ? pos.height : sizeMap[String(node.props.size || "md")] ?? 44;
|
|
6320
7391
|
const fontSize = 13;
|
|
6321
7392
|
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
6322
|
-
const
|
|
6323
|
-
const iconList = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
7393
|
+
const iconList = toStringArray(node.props.icons);
|
|
6324
7394
|
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
6325
7395
|
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
6326
7396
|
const tabWidth = pos.width / tabs.length;
|
|
@@ -6424,6 +7494,13 @@ var SVGRenderer = class {
|
|
|
6424
7494
|
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
6425
7495
|
}
|
|
6426
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
|
+
}
|
|
6427
7504
|
});
|
|
6428
7505
|
const contentY = pos.y + tabHeight;
|
|
6429
7506
|
const contentH = pos.height - tabHeight;
|
|
@@ -6537,66 +7614,51 @@ var SVGRenderer = class {
|
|
|
6537
7614
|
text-anchor="middle">${this.escapeXml(text)}</text>
|
|
6538
7615
|
</g>`;
|
|
6539
7616
|
}
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
}
|
|
6545
|
-
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());
|
|
6546
7621
|
const padding = 16;
|
|
6547
7622
|
const headerHeight = 48;
|
|
6548
|
-
const
|
|
6549
|
-
const
|
|
6550
|
-
const
|
|
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
|
-
|
|
6580
|
-
font-size="18"
|
|
6581
|
-
fill="${this.renderTheme.textMuted}">\u2715</text>
|
|
6582
|
-
|
|
6583
|
-
<!-- Content placeholder -->
|
|
6584
|
-
<text x="${modalX + pos.width / 2}" y="${modalY + headerHeight + (pos.height - headerHeight) / 2}"
|
|
6585
|
-
font-family="Arial, Helvetica, sans-serif"
|
|
6586
|
-
font-size="13"
|
|
6587
|
-
fill="${this.renderTheme.textMuted}"
|
|
6588
|
-
text-anchor="middle">Modal content</text>
|
|
6589
|
-
</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
|
+
);
|
|
6590
7655
|
}
|
|
6591
7656
|
renderList(node, pos) {
|
|
6592
7657
|
const title = String(node.props.title || "");
|
|
6593
|
-
const itemsStr = String(node.props.items || "");
|
|
6594
7658
|
const mockType = String(node.props.mock || "").trim();
|
|
6595
7659
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
6596
|
-
let items =
|
|
6597
|
-
if (
|
|
6598
|
-
items = itemsStr.split(",").map((i) => i.trim()).filter(Boolean);
|
|
6599
|
-
} else {
|
|
7660
|
+
let items = toStringArray(node.props.items);
|
|
7661
|
+
if (items.length === 0) {
|
|
6600
7662
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
6601
7663
|
const itemCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
6602
7664
|
const resolvedMockType = mockType || "name";
|
|
@@ -6625,6 +7687,7 @@ var SVGRenderer = class {
|
|
|
6625
7687
|
items.forEach((item, i) => {
|
|
6626
7688
|
const itemY = pos.y + titleHeight + i * itemHeight;
|
|
6627
7689
|
if (itemY + itemHeight <= pos.y + pos.height) {
|
|
7690
|
+
const itemEventAttrs = this.getScopedEventAttrs(node, "onItemClick", { index: i });
|
|
6628
7691
|
svg += `
|
|
6629
7692
|
<line x1="${pos.x}" y1="${itemY + itemHeight}"
|
|
6630
7693
|
x2="${pos.x + pos.width}" y2="${itemY + itemHeight}"
|
|
@@ -6633,7 +7696,10 @@ var SVGRenderer = class {
|
|
|
6633
7696
|
<text x="${pos.x + padding}" y="${itemY + 24}"
|
|
6634
7697
|
font-family="Arial, Helvetica, sans-serif"
|
|
6635
7698
|
font-size="13"
|
|
6636
|
-
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}/>` : ""}`;
|
|
6637
7703
|
}
|
|
6638
7704
|
});
|
|
6639
7705
|
svg += "\n </g>";
|
|
@@ -6867,8 +7933,8 @@ var SVGRenderer = class {
|
|
|
6867
7933
|
return svg;
|
|
6868
7934
|
}
|
|
6869
7935
|
renderBreadcrumbs(node, pos) {
|
|
6870
|
-
const
|
|
6871
|
-
const items =
|
|
7936
|
+
const parsedBreadcrumbs = toStringArray(node.props.items);
|
|
7937
|
+
const items = parsedBreadcrumbs.length > 0 ? parsedBreadcrumbs : ["Home"];
|
|
6872
7938
|
const separator = String(node.props.separator || "/");
|
|
6873
7939
|
const fontSize = 12;
|
|
6874
7940
|
const separatorWidth = 20;
|
|
@@ -6900,10 +7966,9 @@ var SVGRenderer = class {
|
|
|
6900
7966
|
return svg;
|
|
6901
7967
|
}
|
|
6902
7968
|
renderSidebarMenu(node, pos) {
|
|
6903
|
-
const
|
|
6904
|
-
const
|
|
6905
|
-
const
|
|
6906
|
-
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);
|
|
6907
7972
|
const itemHeight = 40;
|
|
6908
7973
|
const fontSize = 14;
|
|
6909
7974
|
const activeIndex = Number(node.props.active || 0);
|
|
@@ -6948,6 +8013,13 @@ var SVGRenderer = class {
|
|
|
6948
8013
|
font-size="${fontSize}"
|
|
6949
8014
|
font-weight="${fontWeight}"
|
|
6950
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
|
+
}
|
|
6951
8023
|
});
|
|
6952
8024
|
svg += "\n </g>";
|
|
6953
8025
|
return svg;
|
|
@@ -7298,7 +8370,7 @@ var SVGRenderer = class {
|
|
|
7298
8370
|
userBadge = { x, y, width, height, label: userLabel };
|
|
7299
8371
|
rightCursor = x - 8;
|
|
7300
8372
|
}
|
|
7301
|
-
const actionLabels = actions
|
|
8373
|
+
const actionLabels = toStringArray(actions);
|
|
7302
8374
|
const actionHeight = 32;
|
|
7303
8375
|
const actionY = pos.y + (pos.height - actionHeight) / 2;
|
|
7304
8376
|
const actionGap = 8;
|
|
@@ -7384,11 +8456,99 @@ var SVGRenderer = class {
|
|
|
7384
8456
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
7385
8457
|
}
|
|
7386
8458
|
/**
|
|
7387
|
-
* Get data-node-id
|
|
7388
|
-
* 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).
|
|
7389
8462
|
*/
|
|
7390
8463
|
getDataNodeId(node) {
|
|
7391
|
-
|
|
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
|
+
}
|
|
7392
8552
|
}
|
|
7393
8553
|
};
|
|
7394
8554
|
function renderToSVG(ir, layout, options) {
|
|
@@ -7478,8 +8638,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
7478
8638
|
* Render breadcrumbs as skeleton blocks: <rect> / <rect> / <rect accent>
|
|
7479
8639
|
*/
|
|
7480
8640
|
renderBreadcrumbs(node, pos) {
|
|
7481
|
-
const
|
|
7482
|
-
const items = itemsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
8641
|
+
const items = toStringArray(node.props.items || "Home");
|
|
7483
8642
|
const separator = String(node.props.separator || "/");
|
|
7484
8643
|
const blockColor = this.renderTheme.border;
|
|
7485
8644
|
const charWidth = 6.2;
|
|
@@ -7800,10 +8959,9 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
7800
8959
|
*/
|
|
7801
8960
|
renderTable(node, pos) {
|
|
7802
8961
|
const title = String(node.props.title || "");
|
|
7803
|
-
const
|
|
7804
|
-
const columns = columnsStr.split(",").map((c) => c.trim()).filter(Boolean);
|
|
8962
|
+
const columns = toStringArray(node.props.columns || "Col1,Col2,Col3");
|
|
7805
8963
|
const rowCount = Number(node.props.rows || node.props.rowsMock || 5);
|
|
7806
|
-
const actions =
|
|
8964
|
+
const actions = toStringArray(node.props.actions);
|
|
7807
8965
|
const hasActions = actions.length > 0;
|
|
7808
8966
|
const pagination = this.parseBooleanProp(node.props.pagination, false);
|
|
7809
8967
|
const parsedPageCount = Number(node.props.pages || 5);
|
|
@@ -8089,7 +9247,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
8089
9247
|
const itemsStr = String(node.props.items || "");
|
|
8090
9248
|
let items = [];
|
|
8091
9249
|
if (itemsStr) {
|
|
8092
|
-
items = itemsStr
|
|
9250
|
+
items = toStringArray(itemsStr);
|
|
8093
9251
|
} else {
|
|
8094
9252
|
const itemCount = Number(node.props.itemsMock || 6);
|
|
8095
9253
|
items = Array(itemCount).fill("Item");
|
|
@@ -8126,8 +9284,7 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
8126
9284
|
* Render SidebarMenu with gray blocks instead of text and no icons
|
|
8127
9285
|
*/
|
|
8128
9286
|
renderSidebarMenu(node, pos) {
|
|
8129
|
-
const
|
|
8130
|
-
const items = itemsStr.split(",").map((s) => s.trim());
|
|
9287
|
+
const items = toStringArray(node.props.items || "Item 1,Item 2,Item 3");
|
|
8131
9288
|
const itemHeight = 40;
|
|
8132
9289
|
const activeIndex = Number(node.props.active || 0);
|
|
8133
9290
|
const accentColor = this.resolveAccentColor();
|
|
@@ -8975,7 +10132,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8975
10132
|
const title = String(node.props.title || "Sidebar");
|
|
8976
10133
|
const itemsStr = String(node.props.items || "");
|
|
8977
10134
|
const activeItem = String(node.props.active || "");
|
|
8978
|
-
const
|
|
10135
|
+
const parsed = toStringArray(itemsStr);
|
|
10136
|
+
const items = parsed.length ? parsed : ["Item 1", "Item 2", "Item 3"];
|
|
8979
10137
|
const itemHeight = 40;
|
|
8980
10138
|
const padding = 16;
|
|
8981
10139
|
const titleHeight = 40;
|
|
@@ -9018,7 +10176,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9018
10176
|
*/
|
|
9019
10177
|
renderTabs(node, pos) {
|
|
9020
10178
|
const itemsStr = String(node.props.items || "");
|
|
9021
|
-
const
|
|
10179
|
+
const parsedTabs = toStringArray(itemsStr);
|
|
10180
|
+
const tabs = parsedTabs.length ? parsedTabs : ["Tab 1", "Tab 2", "Tab 3"];
|
|
9022
10181
|
const activeProp = node.props.active ?? 0;
|
|
9023
10182
|
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
9024
10183
|
const variant = String(node.props.variant || "default");
|
|
@@ -9030,7 +10189,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9030
10189
|
const fontSize = 13;
|
|
9031
10190
|
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
9032
10191
|
const iconsStr = String(node.props.icons || "");
|
|
9033
|
-
const iconList = iconsStr
|
|
10192
|
+
const iconList = toStringArray(iconsStr);
|
|
9034
10193
|
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
9035
10194
|
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
9036
10195
|
const tabWidth = pos.width / tabs.length;
|
|
@@ -9195,7 +10354,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9195
10354
|
<!-- Modal backdrop -->
|
|
9196
10355
|
<rect x="0" y="0"
|
|
9197
10356
|
width="${this.options.width}" height="${overlayHeight}"
|
|
9198
|
-
fill="black" opacity="0.28"/>
|
|
10357
|
+
fill="black" opacity="0.28" pointer-events="none"/>
|
|
9199
10358
|
|
|
9200
10359
|
<!-- Modal box -->
|
|
9201
10360
|
<rect x="${modalX}" y="${modalY}"
|
|
@@ -9223,7 +10382,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9223
10382
|
<text x="${modalX + pos.width - 16}" y="${modalY + padding + 12}"
|
|
9224
10383
|
font-family="${this.fontFamily}"
|
|
9225
10384
|
font-size="18"
|
|
9226
|
-
fill="${this.renderTheme.textMuted}"
|
|
10385
|
+
fill="${this.renderTheme.textMuted}"
|
|
10386
|
+
pointer-events="none">\u2715</text>
|
|
9227
10387
|
|
|
9228
10388
|
<!-- Content placeholder -->
|
|
9229
10389
|
<text x="${modalX + pos.width / 2}" y="${modalY + headerHeight + (pos.height - headerHeight) / 2}"
|
|
@@ -9241,10 +10401,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9241
10401
|
const itemsStr = String(node.props.items || "");
|
|
9242
10402
|
const mockType = String(node.props.mock || "").trim();
|
|
9243
10403
|
const random = this.parseBooleanProp(node.props.random, false);
|
|
9244
|
-
let items =
|
|
9245
|
-
if (
|
|
9246
|
-
items = itemsStr.split(",").map((i) => i.trim()).filter(Boolean);
|
|
9247
|
-
} else {
|
|
10404
|
+
let items = toStringArray(itemsStr);
|
|
10405
|
+
if (!items.length) {
|
|
9248
10406
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
9249
10407
|
const itemCount = Number.isFinite(parsedItemsMock) ? Math.max(0, Math.floor(parsedItemsMock)) : 4;
|
|
9250
10408
|
const resolvedMockType = mockType || "name";
|
|
@@ -9458,7 +10616,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9458
10616
|
*/
|
|
9459
10617
|
renderBreadcrumbs(node, pos) {
|
|
9460
10618
|
const itemsStr = String(node.props.items || "Home");
|
|
9461
|
-
const items = itemsStr
|
|
10619
|
+
const items = toStringArray(itemsStr);
|
|
9462
10620
|
const separator = String(node.props.separator || "/");
|
|
9463
10621
|
const fontSize = 12;
|
|
9464
10622
|
const separatorWidth = 20;
|
|
@@ -9495,8 +10653,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
9495
10653
|
renderSidebarMenu(node, pos) {
|
|
9496
10654
|
const itemsStr = String(node.props.items || "Item 1,Item 2,Item 3");
|
|
9497
10655
|
const iconsStr = String(node.props.icons || "");
|
|
9498
|
-
const items = itemsStr
|
|
9499
|
-
const icons = iconsStr
|
|
10656
|
+
const items = toStringArray(itemsStr);
|
|
10657
|
+
const icons = toStringArray(iconsStr);
|
|
9500
10658
|
const itemHeight = 40;
|
|
9501
10659
|
const fontSize = 14;
|
|
9502
10660
|
const activeIndex = Number(node.props.active || 0);
|
|
@@ -9816,11 +10974,13 @@ export {
|
|
|
9816
10974
|
DEVICE_PRESETS,
|
|
9817
10975
|
IRGenerator,
|
|
9818
10976
|
LayoutEngine,
|
|
10977
|
+
SELF_TARGET,
|
|
9819
10978
|
SVGRenderer,
|
|
9820
10979
|
SkeletonSVGRenderer,
|
|
9821
10980
|
SketchSVGRenderer,
|
|
9822
10981
|
SourceMapBuilder,
|
|
9823
10982
|
SourceMapResolver,
|
|
10983
|
+
applyStateChange,
|
|
9824
10984
|
buildSVG,
|
|
9825
10985
|
calculateLayout,
|
|
9826
10986
|
createSVGElement,
|