@safe-ugc-ui/react 0.5.1 → 1.0.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.d.ts +50 -6
- package/dist/index.js +696 -44
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// src/UGCRenderer.tsx
|
|
2
|
-
import { useMemo as useMemo2 } from "react";
|
|
2
|
+
import { useEffect as useEffect2, useMemo as useMemo2, useState as useState4 } from "react";
|
|
3
3
|
import { validate, validateRaw } from "@safe-ugc-ui/validator";
|
|
4
|
+
import { COMPACT_BREAKPOINT_MAX_WIDTH } from "@safe-ugc-ui/types";
|
|
4
5
|
|
|
5
6
|
// src/UGCContainer.tsx
|
|
7
|
+
import { forwardRef } from "react";
|
|
6
8
|
import { jsx } from "react/jsx-runtime";
|
|
7
9
|
var containerStyle = {
|
|
8
10
|
overflow: "hidden",
|
|
@@ -10,10 +12,12 @@ var containerStyle = {
|
|
|
10
12
|
contain: "content",
|
|
11
13
|
position: "relative"
|
|
12
14
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
15
|
+
var UGCContainer = forwardRef(
|
|
16
|
+
function UGCContainer2({ children, style }, ref) {
|
|
17
|
+
const mergedStyle = style ? { ...containerStyle, ...style } : containerStyle;
|
|
18
|
+
return /* @__PURE__ */ jsx("div", { ref, style: mergedStyle, children });
|
|
19
|
+
}
|
|
20
|
+
);
|
|
17
21
|
|
|
18
22
|
// src/node-renderer.tsx
|
|
19
23
|
import {
|
|
@@ -25,7 +29,9 @@ import {
|
|
|
25
29
|
} from "@safe-ugc-ui/types";
|
|
26
30
|
|
|
27
31
|
// src/state-resolver.ts
|
|
28
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
PROTOTYPE_POLLUTION_SEGMENTS
|
|
34
|
+
} from "@safe-ugc-ui/types";
|
|
29
35
|
function parseRefSegments(path) {
|
|
30
36
|
const segments = [];
|
|
31
37
|
const dotParts = path.split(".");
|
|
@@ -85,6 +91,111 @@ function resolveValue(value, state, locals) {
|
|
|
85
91
|
}
|
|
86
92
|
return value;
|
|
87
93
|
}
|
|
94
|
+
function stringifyTextScalar(value) {
|
|
95
|
+
if (value === void 0) return "";
|
|
96
|
+
if (value === null) return "null";
|
|
97
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
98
|
+
return String(value);
|
|
99
|
+
}
|
|
100
|
+
return "";
|
|
101
|
+
}
|
|
102
|
+
function isTemplateObject(value) {
|
|
103
|
+
return typeof value === "object" && value !== null && "$template" in value && Array.isArray(value.$template);
|
|
104
|
+
}
|
|
105
|
+
function resolveTemplate(value, state, locals) {
|
|
106
|
+
if (!isTemplateObject(value)) {
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
return value.$template.map((part) => stringifyTextScalar(resolveValue(part, state, locals))).join("");
|
|
110
|
+
}
|
|
111
|
+
function resolveTextValue(value, state, locals) {
|
|
112
|
+
const template = resolveTemplate(value, state, locals);
|
|
113
|
+
if (template !== void 0) {
|
|
114
|
+
return template;
|
|
115
|
+
}
|
|
116
|
+
return stringifyTextScalar(resolveValue(value, state, locals));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/condition-resolver.ts
|
|
120
|
+
import { MAX_CONDITION_DEPTH, isRef } from "@safe-ugc-ui/types";
|
|
121
|
+
function resolveOperand(operand, state, locals) {
|
|
122
|
+
if (isRef(operand)) {
|
|
123
|
+
const resolved = resolveRef(operand.$ref, state, locals);
|
|
124
|
+
if (resolved === null || typeof resolved === "string" || typeof resolved === "number" || typeof resolved === "boolean") {
|
|
125
|
+
return resolved;
|
|
126
|
+
}
|
|
127
|
+
return void 0;
|
|
128
|
+
}
|
|
129
|
+
return operand;
|
|
130
|
+
}
|
|
131
|
+
function compareValues(op, left, right) {
|
|
132
|
+
if (left === void 0 || right === void 0) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (op === "eq") return left === right;
|
|
136
|
+
if (op === "ne") return left !== right;
|
|
137
|
+
if (left === null || right === null) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
if (typeof left !== typeof right) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
if (typeof left !== "number" && typeof left !== "string" || typeof right !== "number" && typeof right !== "string") {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
switch (op) {
|
|
147
|
+
case "gt":
|
|
148
|
+
return left > right;
|
|
149
|
+
case "gte":
|
|
150
|
+
return left >= right;
|
|
151
|
+
case "lt":
|
|
152
|
+
return left < right;
|
|
153
|
+
case "lte":
|
|
154
|
+
return left <= right;
|
|
155
|
+
default:
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function evaluateCondition(condition, state, locals, depth = 1) {
|
|
160
|
+
if (depth > MAX_CONDITION_DEPTH) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
if (typeof condition === "boolean") {
|
|
164
|
+
return condition;
|
|
165
|
+
}
|
|
166
|
+
if (isRef(condition)) {
|
|
167
|
+
return resolveRef(condition.$ref, state, locals) === true;
|
|
168
|
+
}
|
|
169
|
+
if (typeof condition !== "object" || condition === null || Array.isArray(condition)) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
const conditionObj = condition;
|
|
173
|
+
switch (conditionObj.op) {
|
|
174
|
+
case "not":
|
|
175
|
+
return !evaluateCondition(conditionObj.value, state, locals, depth + 1);
|
|
176
|
+
case "and":
|
|
177
|
+
return Array.isArray(conditionObj.values) && conditionObj.values.every(
|
|
178
|
+
(value) => evaluateCondition(value, state, locals, depth + 1)
|
|
179
|
+
);
|
|
180
|
+
case "or":
|
|
181
|
+
return Array.isArray(conditionObj.values) && conditionObj.values.some(
|
|
182
|
+
(value) => evaluateCondition(value, state, locals, depth + 1)
|
|
183
|
+
);
|
|
184
|
+
case "eq":
|
|
185
|
+
case "ne":
|
|
186
|
+
case "gt":
|
|
187
|
+
case "gte":
|
|
188
|
+
case "lt":
|
|
189
|
+
case "lte":
|
|
190
|
+
return compareValues(
|
|
191
|
+
conditionObj.op,
|
|
192
|
+
resolveOperand(conditionObj.left, state, locals),
|
|
193
|
+
resolveOperand(conditionObj.right, state, locals)
|
|
194
|
+
);
|
|
195
|
+
default:
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
88
199
|
|
|
89
200
|
// src/style-mapper.ts
|
|
90
201
|
import { ALLOWED_TRANSITION_PROPERTIES } from "@safe-ugc-ui/types";
|
|
@@ -99,6 +210,7 @@ var DIRECT_MAP_PROPS = [
|
|
|
99
210
|
"gap",
|
|
100
211
|
"width",
|
|
101
212
|
"height",
|
|
213
|
+
"aspectRatio",
|
|
102
214
|
"minWidth",
|
|
103
215
|
"maxWidth",
|
|
104
216
|
"minHeight",
|
|
@@ -155,6 +267,19 @@ function containsForbiddenCssFunction(value) {
|
|
|
155
267
|
const lower = value.toLowerCase();
|
|
156
268
|
return FORBIDDEN_CSS_FUNCTIONS_LOWER.some((fn) => lower.includes(fn));
|
|
157
269
|
}
|
|
270
|
+
function isValidAspectRatio(value) {
|
|
271
|
+
if (typeof value === "number") {
|
|
272
|
+
return Number.isFinite(value) && value > 0;
|
|
273
|
+
}
|
|
274
|
+
if (typeof value !== "string" || containsForbiddenCssFunction(value)) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
const match = value.match(/^\s*([0-9]+(?:\.[0-9]+)?)\s*\/\s*([0-9]+(?:\.[0-9]+)?)\s*$/);
|
|
278
|
+
if (!match) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
return Number(match[1]) > 0 && Number(match[2]) > 0;
|
|
282
|
+
}
|
|
158
283
|
function resolveStyleValue(value, state, locals) {
|
|
159
284
|
return resolveValue(value, state, locals);
|
|
160
285
|
}
|
|
@@ -365,6 +490,9 @@ function mapStyle(style, state, locals) {
|
|
|
365
490
|
if (typeof resolved === "string" && containsForbiddenCssFunction(resolved)) {
|
|
366
491
|
continue;
|
|
367
492
|
}
|
|
493
|
+
if (prop === "aspectRatio" && !isValidAspectRatio(resolved)) {
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
368
496
|
css[prop] = resolved;
|
|
369
497
|
}
|
|
370
498
|
}
|
|
@@ -516,9 +644,40 @@ function Column({ style, hoverStyle, children }) {
|
|
|
516
644
|
|
|
517
645
|
// src/components/Text.tsx
|
|
518
646
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
519
|
-
function
|
|
647
|
+
function getClampStyle(maxLines, truncate) {
|
|
648
|
+
if (!maxLines || maxLines < 1) {
|
|
649
|
+
return void 0;
|
|
650
|
+
}
|
|
651
|
+
if (maxLines === 1) {
|
|
652
|
+
return {
|
|
653
|
+
display: "inline-block",
|
|
654
|
+
maxWidth: "100%",
|
|
655
|
+
overflow: "hidden",
|
|
656
|
+
whiteSpace: "nowrap",
|
|
657
|
+
textOverflow: truncate ?? "ellipsis"
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
661
|
+
display: "-webkit-box",
|
|
662
|
+
maxWidth: "100%",
|
|
663
|
+
overflow: "hidden",
|
|
664
|
+
textOverflow: truncate ?? "ellipsis",
|
|
665
|
+
WebkitBoxOrient: "vertical",
|
|
666
|
+
WebkitLineClamp: maxLines
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
function Text({
|
|
670
|
+
content,
|
|
671
|
+
spans,
|
|
672
|
+
maxLines,
|
|
673
|
+
truncate,
|
|
674
|
+
style,
|
|
675
|
+
hoverStyle
|
|
676
|
+
}) {
|
|
520
677
|
const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
|
|
521
|
-
|
|
678
|
+
const clampStyle = getClampStyle(maxLines, truncate);
|
|
679
|
+
const mergedStyle = clampStyle ? { ...resolvedStyle, ...clampStyle } : resolvedStyle;
|
|
680
|
+
return /* @__PURE__ */ jsx5("span", { style: mergedStyle, onMouseEnter, onMouseLeave, children: spans?.map((span, index) => /* @__PURE__ */ jsx5("span", { style: span.style, children: span.text }, index)) ?? content ?? "" });
|
|
522
681
|
}
|
|
523
682
|
|
|
524
683
|
// src/components/Image.tsx
|
|
@@ -751,13 +910,279 @@ function Toggle({ value, onToggle, onAction, disabled, style, hoverStyle }) {
|
|
|
751
910
|
);
|
|
752
911
|
}
|
|
753
912
|
|
|
913
|
+
// src/components/Accordion.tsx
|
|
914
|
+
import { useId, useState as useState2 } from "react";
|
|
915
|
+
import { jsx as jsx18, jsxs } from "react/jsx-runtime";
|
|
916
|
+
function getInitialExpandedIds(items, defaultExpanded, allowMultiple) {
|
|
917
|
+
const enabledIds = new Set(
|
|
918
|
+
items.filter((item) => item.disabled !== true).map((item) => item.id)
|
|
919
|
+
);
|
|
920
|
+
const initial = (defaultExpanded ?? []).filter((id) => enabledIds.has(id));
|
|
921
|
+
return allowMultiple ? initial : initial.slice(0, 1);
|
|
922
|
+
}
|
|
923
|
+
function Accordion({
|
|
924
|
+
items,
|
|
925
|
+
allowMultiple = false,
|
|
926
|
+
defaultExpanded,
|
|
927
|
+
style,
|
|
928
|
+
hoverStyle
|
|
929
|
+
}) {
|
|
930
|
+
const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
|
|
931
|
+
const [expandedIds, setExpandedIds] = useState2(
|
|
932
|
+
() => getInitialExpandedIds(items, defaultExpanded, allowMultiple)
|
|
933
|
+
);
|
|
934
|
+
const baseId = useId();
|
|
935
|
+
const expandedSet = new Set(expandedIds);
|
|
936
|
+
const toggleItem = (itemId, disabled) => {
|
|
937
|
+
if (disabled) {
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
setExpandedIds((current) => {
|
|
941
|
+
const currentSet = new Set(current);
|
|
942
|
+
if (allowMultiple) {
|
|
943
|
+
if (currentSet.has(itemId)) {
|
|
944
|
+
currentSet.delete(itemId);
|
|
945
|
+
} else {
|
|
946
|
+
currentSet.add(itemId);
|
|
947
|
+
}
|
|
948
|
+
return [...currentSet];
|
|
949
|
+
}
|
|
950
|
+
return currentSet.has(itemId) ? [] : [itemId];
|
|
951
|
+
});
|
|
952
|
+
};
|
|
953
|
+
return /* @__PURE__ */ jsx18("div", { style: resolvedStyle, onMouseEnter, onMouseLeave, children: items.map((item) => {
|
|
954
|
+
const expanded = expandedSet.has(item.id);
|
|
955
|
+
const buttonId = `${baseId}-${item.id}-button`;
|
|
956
|
+
const panelId = `${baseId}-${item.id}-panel`;
|
|
957
|
+
return /* @__PURE__ */ jsxs(
|
|
958
|
+
"div",
|
|
959
|
+
{
|
|
960
|
+
style: {
|
|
961
|
+
borderTop: "1px solid rgba(148, 163, 184, 0.25)"
|
|
962
|
+
},
|
|
963
|
+
children: [
|
|
964
|
+
/* @__PURE__ */ jsxs(
|
|
965
|
+
"button",
|
|
966
|
+
{
|
|
967
|
+
id: buttonId,
|
|
968
|
+
type: "button",
|
|
969
|
+
"aria-expanded": expanded,
|
|
970
|
+
"aria-controls": panelId,
|
|
971
|
+
disabled: item.disabled,
|
|
972
|
+
onClick: () => toggleItem(item.id, item.disabled),
|
|
973
|
+
style: {
|
|
974
|
+
width: "100%",
|
|
975
|
+
display: "flex",
|
|
976
|
+
alignItems: "center",
|
|
977
|
+
justifyContent: "space-between",
|
|
978
|
+
padding: "12px 0",
|
|
979
|
+
background: "transparent",
|
|
980
|
+
border: "none",
|
|
981
|
+
color: "inherit",
|
|
982
|
+
textAlign: "left",
|
|
983
|
+
cursor: item.disabled ? "default" : "pointer",
|
|
984
|
+
opacity: item.disabled ? 0.5 : 1
|
|
985
|
+
},
|
|
986
|
+
children: [
|
|
987
|
+
/* @__PURE__ */ jsx18("span", { children: item.label }),
|
|
988
|
+
/* @__PURE__ */ jsx18("span", { "aria-hidden": "true", children: expanded ? "-" : "+" })
|
|
989
|
+
]
|
|
990
|
+
}
|
|
991
|
+
),
|
|
992
|
+
expanded ? /* @__PURE__ */ jsx18(
|
|
993
|
+
"div",
|
|
994
|
+
{
|
|
995
|
+
id: panelId,
|
|
996
|
+
role: "region",
|
|
997
|
+
"aria-labelledby": buttonId,
|
|
998
|
+
style: { paddingBottom: 12 },
|
|
999
|
+
children: item.content
|
|
1000
|
+
}
|
|
1001
|
+
) : null
|
|
1002
|
+
]
|
|
1003
|
+
},
|
|
1004
|
+
item.id
|
|
1005
|
+
);
|
|
1006
|
+
}) });
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/components/Tabs.tsx
|
|
1010
|
+
import {
|
|
1011
|
+
useEffect,
|
|
1012
|
+
useId as useId2,
|
|
1013
|
+
useRef,
|
|
1014
|
+
useState as useState3
|
|
1015
|
+
} from "react";
|
|
1016
|
+
import { jsx as jsx19, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1017
|
+
function getEnabledTabIds(tabs) {
|
|
1018
|
+
return tabs.filter((tab) => tab.disabled !== true).map((tab) => tab.id);
|
|
1019
|
+
}
|
|
1020
|
+
function getInitialSelectedTab(tabs, defaultTab) {
|
|
1021
|
+
const enabledIds = getEnabledTabIds(tabs);
|
|
1022
|
+
if (enabledIds.length === 0) {
|
|
1023
|
+
return void 0;
|
|
1024
|
+
}
|
|
1025
|
+
if (defaultTab && enabledIds.includes(defaultTab)) {
|
|
1026
|
+
return defaultTab;
|
|
1027
|
+
}
|
|
1028
|
+
return enabledIds[0];
|
|
1029
|
+
}
|
|
1030
|
+
function getNextEnabledIndex(tabs, startIndex, direction) {
|
|
1031
|
+
for (let offset = 1; offset <= tabs.length; offset++) {
|
|
1032
|
+
const nextIndex = (startIndex + direction * offset + tabs.length) % tabs.length;
|
|
1033
|
+
if (tabs[nextIndex]?.disabled !== true) {
|
|
1034
|
+
return nextIndex;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
return startIndex;
|
|
1038
|
+
}
|
|
1039
|
+
function focusTab(buttonRefs, index) {
|
|
1040
|
+
buttonRefs.current[index]?.focus();
|
|
1041
|
+
}
|
|
1042
|
+
function Tabs({
|
|
1043
|
+
tabs,
|
|
1044
|
+
defaultTab,
|
|
1045
|
+
style,
|
|
1046
|
+
hoverStyle
|
|
1047
|
+
}) {
|
|
1048
|
+
const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
|
|
1049
|
+
const [selectedTab, setSelectedTab] = useState3(
|
|
1050
|
+
() => getInitialSelectedTab(tabs, defaultTab)
|
|
1051
|
+
);
|
|
1052
|
+
const baseId = useId2();
|
|
1053
|
+
const buttonRefs = useRef([]);
|
|
1054
|
+
useEffect(() => {
|
|
1055
|
+
setSelectedTab((current) => {
|
|
1056
|
+
const enabledIds = getEnabledTabIds(tabs);
|
|
1057
|
+
if (enabledIds.length === 0) {
|
|
1058
|
+
return void 0;
|
|
1059
|
+
}
|
|
1060
|
+
if (current && enabledIds.includes(current)) {
|
|
1061
|
+
return current;
|
|
1062
|
+
}
|
|
1063
|
+
if (defaultTab && enabledIds.includes(defaultTab)) {
|
|
1064
|
+
return defaultTab;
|
|
1065
|
+
}
|
|
1066
|
+
return enabledIds[0];
|
|
1067
|
+
});
|
|
1068
|
+
}, [tabs, defaultTab]);
|
|
1069
|
+
const selectedItem = tabs.find((tab) => tab.id === selectedTab && tab.disabled !== true) ?? tabs.find((tab) => tab.disabled !== true);
|
|
1070
|
+
const activateTab = (index, focus = false) => {
|
|
1071
|
+
const tab = tabs[index];
|
|
1072
|
+
if (!tab || tab.disabled) {
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
setSelectedTab(tab.id);
|
|
1076
|
+
if (focus) {
|
|
1077
|
+
focusTab(buttonRefs, index);
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
const onTabKeyDown = (event, index) => {
|
|
1081
|
+
switch (event.key) {
|
|
1082
|
+
case "ArrowRight":
|
|
1083
|
+
case "ArrowDown": {
|
|
1084
|
+
event.preventDefault();
|
|
1085
|
+
activateTab(getNextEnabledIndex(tabs, index, 1), true);
|
|
1086
|
+
break;
|
|
1087
|
+
}
|
|
1088
|
+
case "ArrowLeft":
|
|
1089
|
+
case "ArrowUp": {
|
|
1090
|
+
event.preventDefault();
|
|
1091
|
+
activateTab(getNextEnabledIndex(tabs, index, -1), true);
|
|
1092
|
+
break;
|
|
1093
|
+
}
|
|
1094
|
+
case "Home": {
|
|
1095
|
+
event.preventDefault();
|
|
1096
|
+
const firstEnabledIndex = tabs.findIndex((tab) => tab.disabled !== true);
|
|
1097
|
+
if (firstEnabledIndex >= 0) {
|
|
1098
|
+
activateTab(firstEnabledIndex, true);
|
|
1099
|
+
}
|
|
1100
|
+
break;
|
|
1101
|
+
}
|
|
1102
|
+
case "End": {
|
|
1103
|
+
event.preventDefault();
|
|
1104
|
+
const lastEnabledIndex = [...tabs].map((tab, currentIndex) => ({ tab, currentIndex })).reverse().find(({ tab }) => tab.disabled !== true);
|
|
1105
|
+
if (lastEnabledIndex) {
|
|
1106
|
+
activateTab(lastEnabledIndex.currentIndex, true);
|
|
1107
|
+
}
|
|
1108
|
+
break;
|
|
1109
|
+
}
|
|
1110
|
+
default:
|
|
1111
|
+
break;
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
return /* @__PURE__ */ jsxs2("div", { style: resolvedStyle, onMouseEnter, onMouseLeave, children: [
|
|
1115
|
+
/* @__PURE__ */ jsx19(
|
|
1116
|
+
"div",
|
|
1117
|
+
{
|
|
1118
|
+
role: "tablist",
|
|
1119
|
+
"aria-orientation": "horizontal",
|
|
1120
|
+
style: {
|
|
1121
|
+
display: "flex",
|
|
1122
|
+
gap: 8,
|
|
1123
|
+
borderBottom: "1px solid rgba(148, 163, 184, 0.25)",
|
|
1124
|
+
paddingBottom: 8
|
|
1125
|
+
},
|
|
1126
|
+
children: tabs.map((tab, index) => {
|
|
1127
|
+
const selected = selectedItem?.id === tab.id;
|
|
1128
|
+
const tabId = `${baseId}-${tab.id}-tab`;
|
|
1129
|
+
const panelId = `${baseId}-${tab.id}-panel`;
|
|
1130
|
+
return /* @__PURE__ */ jsx19(
|
|
1131
|
+
"button",
|
|
1132
|
+
{
|
|
1133
|
+
ref: (element) => {
|
|
1134
|
+
buttonRefs.current[index] = element;
|
|
1135
|
+
},
|
|
1136
|
+
id: tabId,
|
|
1137
|
+
type: "button",
|
|
1138
|
+
role: "tab",
|
|
1139
|
+
"aria-selected": selected,
|
|
1140
|
+
"aria-controls": panelId,
|
|
1141
|
+
disabled: tab.disabled,
|
|
1142
|
+
tabIndex: selected ? 0 : -1,
|
|
1143
|
+
onClick: () => activateTab(index),
|
|
1144
|
+
onKeyDown: (event) => onTabKeyDown(event, index),
|
|
1145
|
+
style: {
|
|
1146
|
+
padding: "8px 12px",
|
|
1147
|
+
borderRadius: 8,
|
|
1148
|
+
border: "none",
|
|
1149
|
+
background: selected ? "rgba(148, 163, 184, 0.16)" : "transparent",
|
|
1150
|
+
color: "inherit",
|
|
1151
|
+
cursor: tab.disabled ? "default" : "pointer",
|
|
1152
|
+
opacity: tab.disabled ? 0.5 : 1,
|
|
1153
|
+
fontWeight: selected ? 600 : 400
|
|
1154
|
+
},
|
|
1155
|
+
children: tab.label
|
|
1156
|
+
},
|
|
1157
|
+
tab.id
|
|
1158
|
+
);
|
|
1159
|
+
})
|
|
1160
|
+
}
|
|
1161
|
+
),
|
|
1162
|
+
selectedItem ? /* @__PURE__ */ jsx19(
|
|
1163
|
+
"div",
|
|
1164
|
+
{
|
|
1165
|
+
id: `${baseId}-${selectedItem.id}-panel`,
|
|
1166
|
+
role: "tabpanel",
|
|
1167
|
+
"aria-labelledby": `${baseId}-${selectedItem.id}-tab`,
|
|
1168
|
+
style: { paddingTop: 12 },
|
|
1169
|
+
children: selectedItem.content
|
|
1170
|
+
}
|
|
1171
|
+
) : null
|
|
1172
|
+
] });
|
|
1173
|
+
}
|
|
1174
|
+
|
|
754
1175
|
// src/node-renderer.tsx
|
|
755
|
-
import { jsx as
|
|
1176
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
756
1177
|
function isForLoop(obj) {
|
|
757
1178
|
if (obj == null || typeof obj !== "object" || Array.isArray(obj)) return false;
|
|
758
1179
|
const o = obj;
|
|
759
1180
|
return typeof o.for === "string" && typeof o.in === "string" && o.template != null;
|
|
760
1181
|
}
|
|
1182
|
+
function isFragmentUse(obj) {
|
|
1183
|
+
if (obj == null || typeof obj !== "object" || Array.isArray(obj)) return false;
|
|
1184
|
+
return typeof obj.$use === "string";
|
|
1185
|
+
}
|
|
761
1186
|
function utf8ByteLength(str) {
|
|
762
1187
|
let bytes = 0;
|
|
763
1188
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -810,20 +1235,155 @@ function mergeStyleWithCardStyles(nodeStyle, cardStyles) {
|
|
|
810
1235
|
hoverStyle: mergedHoverStyle
|
|
811
1236
|
};
|
|
812
1237
|
}
|
|
1238
|
+
function getCompactResponsiveStyle(nodeResponsive) {
|
|
1239
|
+
if (!nodeResponsive) return void 0;
|
|
1240
|
+
const compact = nodeResponsive.compact;
|
|
1241
|
+
if (compact == null || typeof compact !== "object" || Array.isArray(compact)) {
|
|
1242
|
+
return void 0;
|
|
1243
|
+
}
|
|
1244
|
+
return compact;
|
|
1245
|
+
}
|
|
1246
|
+
function mergeEffectiveNodeStyle(node, ctx) {
|
|
1247
|
+
const baseStyle = mergeStyleWithCardStyles(node.style, ctx.cardStyles);
|
|
1248
|
+
if (!ctx.responsive.compact) {
|
|
1249
|
+
return baseStyle;
|
|
1250
|
+
}
|
|
1251
|
+
const compactOverride = mergeNamedStyle(
|
|
1252
|
+
getCompactResponsiveStyle(node.responsive),
|
|
1253
|
+
ctx.cardStyles
|
|
1254
|
+
);
|
|
1255
|
+
if (!compactOverride) {
|
|
1256
|
+
return baseStyle;
|
|
1257
|
+
}
|
|
1258
|
+
const {
|
|
1259
|
+
hoverStyle: _hoverStyle,
|
|
1260
|
+
transition: _transition,
|
|
1261
|
+
...compactStyleWithoutInteractiveFields
|
|
1262
|
+
} = compactOverride;
|
|
1263
|
+
return {
|
|
1264
|
+
...baseStyle ?? {},
|
|
1265
|
+
...compactStyleWithoutInteractiveFields
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
function resolveTextPayload(node, ctx) {
|
|
1269
|
+
const rawSpans = Array.isArray(node.spans) ? node.spans : void 0;
|
|
1270
|
+
if (rawSpans) {
|
|
1271
|
+
const spans = rawSpans.map((span) => {
|
|
1272
|
+
const rawSpan = span;
|
|
1273
|
+
const spanStyle = rawSpan.style != null && typeof rawSpan.style === "object" && !Array.isArray(rawSpan.style) ? rawSpan.style : void 0;
|
|
1274
|
+
return {
|
|
1275
|
+
text: resolveTextValue(rawSpan.text, ctx.state, ctx.locals),
|
|
1276
|
+
style: spanStyle ? mapStyle(spanStyle, ctx.state, ctx.locals) : void 0,
|
|
1277
|
+
rawStyleBytes: spanStyle ? utf8ByteLength(JSON.stringify(spanStyle)) : 0
|
|
1278
|
+
};
|
|
1279
|
+
});
|
|
1280
|
+
const combinedText = spans.map((span) => span.text).join("");
|
|
1281
|
+
return {
|
|
1282
|
+
spans: spans.map(({ rawStyleBytes: _rawStyleBytes, ...span }) => span),
|
|
1283
|
+
textBytes: utf8ByteLength(combinedText),
|
|
1284
|
+
styleBytes: spans.reduce((sum, span) => sum + span.rawStyleBytes, 0)
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
const content = resolveTextValue(node.content, ctx.state, ctx.locals);
|
|
1288
|
+
return {
|
|
1289
|
+
content,
|
|
1290
|
+
textBytes: utf8ByteLength(content),
|
|
1291
|
+
styleBytes: 0
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
function resolveAccordionItems(node, ctx, key) {
|
|
1295
|
+
const rawItems = Array.isArray(node.items) ? node.items : [];
|
|
1296
|
+
return rawItems.flatMap((item, index) => {
|
|
1297
|
+
if (item == null || typeof item !== "object" || Array.isArray(item)) {
|
|
1298
|
+
return [];
|
|
1299
|
+
}
|
|
1300
|
+
const rawItem = item;
|
|
1301
|
+
const id = typeof rawItem.id === "string" ? rawItem.id : `item-${index}`;
|
|
1302
|
+
const label = resolveTextValue(rawItem.label, ctx.state, ctx.locals);
|
|
1303
|
+
const resolvedDisabled = resolveValue(rawItem.disabled, ctx.state, ctx.locals);
|
|
1304
|
+
const disabled = typeof resolvedDisabled === "boolean" ? resolvedDisabled : void 0;
|
|
1305
|
+
const content = renderNode(
|
|
1306
|
+
rawItem.content,
|
|
1307
|
+
ctx,
|
|
1308
|
+
`${String(key)}.items[${index}].content`
|
|
1309
|
+
);
|
|
1310
|
+
return [{ id, label, content, disabled }];
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
function resolveTabsItems(node, ctx, key) {
|
|
1314
|
+
const rawTabs = Array.isArray(node.tabs) ? node.tabs : [];
|
|
1315
|
+
return rawTabs.flatMap((tab, index) => {
|
|
1316
|
+
if (tab == null || typeof tab !== "object" || Array.isArray(tab)) {
|
|
1317
|
+
return [];
|
|
1318
|
+
}
|
|
1319
|
+
const rawTab = tab;
|
|
1320
|
+
const id = typeof rawTab.id === "string" ? rawTab.id : `tab-${index}`;
|
|
1321
|
+
const label = resolveTextValue(rawTab.label, ctx.state, ctx.locals);
|
|
1322
|
+
const resolvedDisabled = resolveValue(rawTab.disabled, ctx.state, ctx.locals);
|
|
1323
|
+
const disabled = typeof resolvedDisabled === "boolean" ? resolvedDisabled : void 0;
|
|
1324
|
+
const content = renderNode(
|
|
1325
|
+
rawTab.content,
|
|
1326
|
+
ctx,
|
|
1327
|
+
`${String(key)}.tabs[${index}].content`
|
|
1328
|
+
);
|
|
1329
|
+
return [{ id, label, content, disabled }];
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
813
1332
|
function renderNode(node, ctx, key) {
|
|
814
1333
|
if (node == null || typeof node !== "object") return null;
|
|
1334
|
+
if (isFragmentUse(node)) {
|
|
1335
|
+
if ("$if" in node && !evaluateCondition(node.$if, ctx.state, ctx.locals)) {
|
|
1336
|
+
return null;
|
|
1337
|
+
}
|
|
1338
|
+
if ((ctx.fragmentStack?.length ?? 0) > 0) {
|
|
1339
|
+
ctx.onError?.([{
|
|
1340
|
+
code: "RUNTIME_FRAGMENT_NESTED_USE",
|
|
1341
|
+
message: 'Fragments may not contain nested "$use" references',
|
|
1342
|
+
path: String(key)
|
|
1343
|
+
}]);
|
|
1344
|
+
return null;
|
|
1345
|
+
}
|
|
1346
|
+
if (ctx.fragmentStack?.includes(node.$use)) {
|
|
1347
|
+
ctx.onError?.([{
|
|
1348
|
+
code: "RUNTIME_FRAGMENT_CYCLE",
|
|
1349
|
+
message: `Fragment "${node.$use}" recursively references itself`,
|
|
1350
|
+
path: String(key)
|
|
1351
|
+
}]);
|
|
1352
|
+
return null;
|
|
1353
|
+
}
|
|
1354
|
+
const fragment = ctx.fragments?.[node.$use];
|
|
1355
|
+
if (fragment == null) {
|
|
1356
|
+
ctx.onError?.([{
|
|
1357
|
+
code: "RUNTIME_FRAGMENT_NOT_FOUND",
|
|
1358
|
+
message: `Fragment "${node.$use}" was not found`,
|
|
1359
|
+
path: String(key)
|
|
1360
|
+
}]);
|
|
1361
|
+
return null;
|
|
1362
|
+
}
|
|
1363
|
+
return renderNode(
|
|
1364
|
+
fragment,
|
|
1365
|
+
{
|
|
1366
|
+
...ctx,
|
|
1367
|
+
fragmentStack: [...ctx.fragmentStack ?? [], node.$use]
|
|
1368
|
+
},
|
|
1369
|
+
key
|
|
1370
|
+
);
|
|
1371
|
+
}
|
|
815
1372
|
const n = node;
|
|
816
1373
|
if (!n.type) return null;
|
|
817
|
-
|
|
1374
|
+
if ("$if" in n && !evaluateCondition(n.$if, ctx.state, ctx.locals)) {
|
|
1375
|
+
return null;
|
|
1376
|
+
}
|
|
1377
|
+
const mergedRawStyle = mergeEffectiveNodeStyle(n, ctx);
|
|
818
1378
|
const rv = (val) => resolveValue(val, ctx.state, ctx.locals);
|
|
819
|
-
|
|
1379
|
+
let styleDelta = mergedRawStyle ? utf8ByteLength(JSON.stringify(mergedRawStyle)) : 0;
|
|
820
1380
|
const overflowDelta = mergedRawStyle?.overflow === "auto" ? 1 : 0;
|
|
821
|
-
let
|
|
1381
|
+
let resolvedTextPayload;
|
|
822
1382
|
let textDelta = 0;
|
|
823
1383
|
if (n.type === "Text") {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1384
|
+
resolvedTextPayload = resolveTextPayload(n, ctx);
|
|
1385
|
+
textDelta = resolvedTextPayload.textBytes;
|
|
1386
|
+
styleDelta += resolvedTextPayload.styleBytes;
|
|
827
1387
|
}
|
|
828
1388
|
if (ctx.limits.nodeCount + 1 > MAX_NODE_COUNT) {
|
|
829
1389
|
ctx.onError?.([{ code: "RUNTIME_NODE_LIMIT", message: `Node count exceeds maximum of ${MAX_NODE_COUNT}`, path: String(key) }]);
|
|
@@ -851,17 +1411,31 @@ function renderNode(node, ctx, key) {
|
|
|
851
1411
|
const childElements = renderChildren(n.children, ctx);
|
|
852
1412
|
switch (n.type) {
|
|
853
1413
|
case "Box":
|
|
854
|
-
return /* @__PURE__ */
|
|
1414
|
+
return /* @__PURE__ */ jsx20(Box, { style: cssStyle, hoverStyle: cssHoverStyle, children: childElements }, key);
|
|
855
1415
|
case "Row":
|
|
856
|
-
return /* @__PURE__ */
|
|
1416
|
+
return /* @__PURE__ */ jsx20(Row, { style: cssStyle, hoverStyle: cssHoverStyle, children: childElements }, key);
|
|
857
1417
|
case "Column":
|
|
858
|
-
return /* @__PURE__ */
|
|
1418
|
+
return /* @__PURE__ */ jsx20(Column, { style: cssStyle, hoverStyle: cssHoverStyle, children: childElements }, key);
|
|
859
1419
|
case "Stack":
|
|
860
|
-
return /* @__PURE__ */
|
|
1420
|
+
return /* @__PURE__ */ jsx20(Stack, { style: cssStyle, hoverStyle: cssHoverStyle, children: childElements }, key);
|
|
861
1421
|
case "Grid":
|
|
862
|
-
return /* @__PURE__ */
|
|
863
|
-
case "Text":
|
|
864
|
-
|
|
1422
|
+
return /* @__PURE__ */ jsx20(Grid, { style: cssStyle, hoverStyle: cssHoverStyle, children: childElements }, key);
|
|
1423
|
+
case "Text": {
|
|
1424
|
+
const maxLines = typeof n.maxLines === "number" ? n.maxLines : void 0;
|
|
1425
|
+
const truncate = n.truncate === "clip" || n.truncate === "ellipsis" ? n.truncate : void 0;
|
|
1426
|
+
return /* @__PURE__ */ jsx20(
|
|
1427
|
+
Text,
|
|
1428
|
+
{
|
|
1429
|
+
content: resolvedTextPayload?.content,
|
|
1430
|
+
spans: resolvedTextPayload?.spans,
|
|
1431
|
+
maxLines,
|
|
1432
|
+
truncate,
|
|
1433
|
+
style: cssStyle,
|
|
1434
|
+
hoverStyle: cssHoverStyle
|
|
1435
|
+
},
|
|
1436
|
+
key
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
865
1439
|
case "Image": {
|
|
866
1440
|
let src = rv(n.src);
|
|
867
1441
|
if (typeof src !== "string" || !src) return null;
|
|
@@ -872,7 +1446,7 @@ function renderNode(node, ctx, key) {
|
|
|
872
1446
|
if (typeof resolved === "string" && resolved.trim().toLowerCase().startsWith("javascript:")) return null;
|
|
873
1447
|
const resolvedAlt = rv(n.alt);
|
|
874
1448
|
const alt = typeof resolvedAlt === "string" ? resolvedAlt : void 0;
|
|
875
|
-
return /* @__PURE__ */
|
|
1449
|
+
return /* @__PURE__ */ jsx20(Image, { src: resolved, alt, style: cssStyle, hoverStyle: cssHoverStyle }, key);
|
|
876
1450
|
}
|
|
877
1451
|
case "Avatar": {
|
|
878
1452
|
let src = rv(n.src);
|
|
@@ -884,7 +1458,7 @@ function renderNode(node, ctx, key) {
|
|
|
884
1458
|
if (typeof resolved === "string" && resolved.trim().toLowerCase().startsWith("javascript:")) return null;
|
|
885
1459
|
const resolvedSize = rv(n.size);
|
|
886
1460
|
const size = typeof resolvedSize === "number" || typeof resolvedSize === "string" ? resolvedSize : void 0;
|
|
887
|
-
return /* @__PURE__ */
|
|
1461
|
+
return /* @__PURE__ */ jsx20(Avatar, { src: resolved, size, style: cssStyle, hoverStyle: cssHoverStyle }, key);
|
|
888
1462
|
}
|
|
889
1463
|
case "Icon": {
|
|
890
1464
|
const resolvedName = rv(n.name);
|
|
@@ -893,7 +1467,7 @@ function renderNode(node, ctx, key) {
|
|
|
893
1467
|
const size = typeof resolvedSize === "number" || typeof resolvedSize === "string" ? resolvedSize : void 0;
|
|
894
1468
|
const resolvedColor = rv(n.color);
|
|
895
1469
|
const color = typeof resolvedColor === "string" ? resolvedColor : void 0;
|
|
896
|
-
return /* @__PURE__ */
|
|
1470
|
+
return /* @__PURE__ */ jsx20(
|
|
897
1471
|
Icon,
|
|
898
1472
|
{
|
|
899
1473
|
name,
|
|
@@ -909,14 +1483,14 @@ function renderNode(node, ctx, key) {
|
|
|
909
1483
|
case "Spacer": {
|
|
910
1484
|
const resolvedSize = rv(n.size);
|
|
911
1485
|
const size = typeof resolvedSize === "number" || typeof resolvedSize === "string" ? resolvedSize : void 0;
|
|
912
|
-
return /* @__PURE__ */
|
|
1486
|
+
return /* @__PURE__ */ jsx20(Spacer, { size, style: cssStyle, hoverStyle: cssHoverStyle }, key);
|
|
913
1487
|
}
|
|
914
1488
|
case "Divider": {
|
|
915
1489
|
const resolvedColor = rv(n.color);
|
|
916
1490
|
const color = typeof resolvedColor === "string" ? resolvedColor : void 0;
|
|
917
1491
|
const resolvedThickness = rv(n.thickness);
|
|
918
1492
|
const thickness = typeof resolvedThickness === "number" || typeof resolvedThickness === "string" ? resolvedThickness : void 0;
|
|
919
|
-
return /* @__PURE__ */
|
|
1493
|
+
return /* @__PURE__ */ jsx20(Divider, { color, thickness, style: cssStyle, hoverStyle: cssHoverStyle }, key);
|
|
920
1494
|
}
|
|
921
1495
|
case "ProgressBar": {
|
|
922
1496
|
const resolvedValue = rv(n.value);
|
|
@@ -925,33 +1499,33 @@ function renderNode(node, ctx, key) {
|
|
|
925
1499
|
const max = typeof resolvedMax === "number" ? resolvedMax : 100;
|
|
926
1500
|
const resolvedColor = rv(n.color);
|
|
927
1501
|
const color = typeof resolvedColor === "string" ? resolvedColor : void 0;
|
|
928
|
-
return /* @__PURE__ */
|
|
1502
|
+
return /* @__PURE__ */ jsx20(ProgressBar, { value, max, color, style: cssStyle, hoverStyle: cssHoverStyle }, key);
|
|
929
1503
|
}
|
|
930
1504
|
case "Badge": {
|
|
931
|
-
const
|
|
932
|
-
const label = typeof resolvedLabel === "string" ? resolvedLabel : "";
|
|
1505
|
+
const label = resolveTextValue(n.label, ctx.state, ctx.locals);
|
|
933
1506
|
const resolvedColor = rv(n.color);
|
|
934
1507
|
const color = typeof resolvedColor === "string" ? resolvedColor : void 0;
|
|
935
|
-
return /* @__PURE__ */
|
|
1508
|
+
return /* @__PURE__ */ jsx20(Badge, { label, color, style: cssStyle, hoverStyle: cssHoverStyle }, key);
|
|
936
1509
|
}
|
|
937
1510
|
case "Chip": {
|
|
938
|
-
const
|
|
939
|
-
const label = typeof resolvedLabel === "string" ? resolvedLabel : "";
|
|
1511
|
+
const label = resolveTextValue(n.label, ctx.state, ctx.locals);
|
|
940
1512
|
const resolvedColor = rv(n.color);
|
|
941
1513
|
const color = typeof resolvedColor === "string" ? resolvedColor : void 0;
|
|
942
|
-
return /* @__PURE__ */
|
|
1514
|
+
return /* @__PURE__ */ jsx20(Chip, { label, color, style: cssStyle, hoverStyle: cssHoverStyle }, key);
|
|
943
1515
|
}
|
|
944
1516
|
case "Button": {
|
|
945
|
-
const
|
|
946
|
-
const label = typeof resolvedLabel === "string" ? resolvedLabel : "";
|
|
1517
|
+
const label = resolveTextValue(n.label, ctx.state, ctx.locals);
|
|
947
1518
|
const actionVal = n.action;
|
|
948
1519
|
const action = typeof actionVal === "string" ? actionVal : "";
|
|
949
|
-
|
|
1520
|
+
const resolvedDisabled = rv(n.disabled);
|
|
1521
|
+
const disabled = typeof resolvedDisabled === "boolean" ? resolvedDisabled : void 0;
|
|
1522
|
+
return /* @__PURE__ */ jsx20(
|
|
950
1523
|
Button,
|
|
951
1524
|
{
|
|
952
1525
|
label,
|
|
953
1526
|
action,
|
|
954
1527
|
onAction: ctx.onAction,
|
|
1528
|
+
disabled,
|
|
955
1529
|
style: cssStyle,
|
|
956
1530
|
hoverStyle: cssHoverStyle
|
|
957
1531
|
},
|
|
@@ -963,12 +1537,47 @@ function renderNode(node, ctx, key) {
|
|
|
963
1537
|
const value = typeof resolvedValue === "boolean" ? resolvedValue : false;
|
|
964
1538
|
const onToggleVal = n.onToggle;
|
|
965
1539
|
const onToggle = typeof onToggleVal === "string" ? onToggleVal : "";
|
|
966
|
-
|
|
1540
|
+
const resolvedDisabled = rv(n.disabled);
|
|
1541
|
+
const disabled = typeof resolvedDisabled === "boolean" ? resolvedDisabled : void 0;
|
|
1542
|
+
return /* @__PURE__ */ jsx20(
|
|
967
1543
|
Toggle,
|
|
968
1544
|
{
|
|
969
1545
|
value,
|
|
970
1546
|
onToggle,
|
|
971
1547
|
onAction: ctx.onAction,
|
|
1548
|
+
disabled,
|
|
1549
|
+
style: cssStyle,
|
|
1550
|
+
hoverStyle: cssHoverStyle
|
|
1551
|
+
},
|
|
1552
|
+
key
|
|
1553
|
+
);
|
|
1554
|
+
}
|
|
1555
|
+
case "Accordion": {
|
|
1556
|
+
const items = resolveAccordionItems(n, ctx, key);
|
|
1557
|
+
const allowMultiple = n.allowMultiple === true;
|
|
1558
|
+
const defaultExpanded = Array.isArray(n.defaultExpanded) ? n.defaultExpanded.filter(
|
|
1559
|
+
(value) => typeof value === "string"
|
|
1560
|
+
) : void 0;
|
|
1561
|
+
return /* @__PURE__ */ jsx20(
|
|
1562
|
+
Accordion,
|
|
1563
|
+
{
|
|
1564
|
+
items,
|
|
1565
|
+
allowMultiple,
|
|
1566
|
+
defaultExpanded,
|
|
1567
|
+
style: cssStyle,
|
|
1568
|
+
hoverStyle: cssHoverStyle
|
|
1569
|
+
},
|
|
1570
|
+
key
|
|
1571
|
+
);
|
|
1572
|
+
}
|
|
1573
|
+
case "Tabs": {
|
|
1574
|
+
const tabs = resolveTabsItems(n, ctx, key);
|
|
1575
|
+
const defaultTab = typeof n.defaultTab === "string" ? n.defaultTab : void 0;
|
|
1576
|
+
return /* @__PURE__ */ jsx20(
|
|
1577
|
+
Tabs,
|
|
1578
|
+
{
|
|
1579
|
+
tabs,
|
|
1580
|
+
defaultTab,
|
|
972
1581
|
style: cssStyle,
|
|
973
1582
|
hoverStyle: cssHoverStyle
|
|
974
1583
|
},
|
|
@@ -1016,7 +1625,7 @@ function renderForLoop(loop, ctx) {
|
|
|
1016
1625
|
}
|
|
1017
1626
|
return elements;
|
|
1018
1627
|
}
|
|
1019
|
-
function renderTree(rootNode, state, assets, cardStyles, iconResolver, onAction, onError) {
|
|
1628
|
+
function renderTree(rootNode, state, assets, cardStyles, iconResolver, onAction, onError, responsive = { compact: false }, fragments) {
|
|
1020
1629
|
const limits = {
|
|
1021
1630
|
nodeCount: 0,
|
|
1022
1631
|
textBytes: 0,
|
|
@@ -1027,16 +1636,19 @@ function renderTree(rootNode, state, assets, cardStyles, iconResolver, onAction,
|
|
|
1027
1636
|
state,
|
|
1028
1637
|
assets,
|
|
1029
1638
|
cardStyles,
|
|
1639
|
+
fragments,
|
|
1640
|
+
fragmentStack: [],
|
|
1030
1641
|
iconResolver,
|
|
1031
1642
|
onAction,
|
|
1032
1643
|
onError,
|
|
1033
|
-
limits
|
|
1644
|
+
limits,
|
|
1645
|
+
responsive
|
|
1034
1646
|
};
|
|
1035
1647
|
return renderNode(rootNode, ctx, "root");
|
|
1036
1648
|
}
|
|
1037
1649
|
|
|
1038
1650
|
// src/UGCRenderer.tsx
|
|
1039
|
-
import { jsx as
|
|
1651
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
1040
1652
|
function UGCRenderer({
|
|
1041
1653
|
card,
|
|
1042
1654
|
viewName,
|
|
@@ -1047,6 +1659,10 @@ function UGCRenderer({
|
|
|
1047
1659
|
iconResolver,
|
|
1048
1660
|
onAction
|
|
1049
1661
|
}) {
|
|
1662
|
+
const [containerElement, setContainerElement] = useState4(null);
|
|
1663
|
+
const [containerWidth, setContainerWidth] = useState4(
|
|
1664
|
+
typeof window === "undefined" ? null : window.innerWidth
|
|
1665
|
+
);
|
|
1050
1666
|
const result = useMemo2(() => {
|
|
1051
1667
|
const validationResult = typeof card === "string" ? validateRaw(card) : validate(card);
|
|
1052
1668
|
if (!validationResult.valid) {
|
|
@@ -1064,30 +1680,65 @@ function UGCRenderer({
|
|
|
1064
1680
|
...stateOverride ?? {}
|
|
1065
1681
|
};
|
|
1066
1682
|
const cardStyles = cardObj.styles;
|
|
1683
|
+
const fragments = cardObj.fragments;
|
|
1067
1684
|
return {
|
|
1068
1685
|
valid: true,
|
|
1069
1686
|
rootNode: views[selectedView],
|
|
1070
1687
|
state: mergedState,
|
|
1071
|
-
cardStyles
|
|
1688
|
+
cardStyles,
|
|
1689
|
+
fragments
|
|
1072
1690
|
};
|
|
1073
1691
|
}, [card, viewName, stateOverride]);
|
|
1692
|
+
useEffect2(() => {
|
|
1693
|
+
if (!containerElement) {
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
const updateWidth = (nextWidth) => {
|
|
1697
|
+
if (typeof nextWidth === "number" && Number.isFinite(nextWidth)) {
|
|
1698
|
+
setContainerWidth(nextWidth);
|
|
1699
|
+
return;
|
|
1700
|
+
}
|
|
1701
|
+
setContainerWidth(containerElement.getBoundingClientRect().width);
|
|
1702
|
+
};
|
|
1703
|
+
updateWidth();
|
|
1704
|
+
if (typeof ResizeObserver === "function") {
|
|
1705
|
+
const observer = new ResizeObserver((entries) => {
|
|
1706
|
+
const entry = entries[0];
|
|
1707
|
+
updateWidth(entry?.contentRect?.width);
|
|
1708
|
+
});
|
|
1709
|
+
observer.observe(containerElement);
|
|
1710
|
+
return () => observer.disconnect();
|
|
1711
|
+
}
|
|
1712
|
+
const handleResize = () => updateWidth();
|
|
1713
|
+
window.addEventListener("resize", handleResize);
|
|
1714
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
1715
|
+
}, [containerElement]);
|
|
1716
|
+
const responsive = useMemo2(
|
|
1717
|
+
() => ({
|
|
1718
|
+
compact: containerWidth != null && containerWidth <= COMPACT_BREAKPOINT_MAX_WIDTH
|
|
1719
|
+
}),
|
|
1720
|
+
[containerWidth]
|
|
1721
|
+
);
|
|
1074
1722
|
if (!result.valid) {
|
|
1075
1723
|
if (onError && result.errors.length > 0) {
|
|
1076
1724
|
onError(result.errors);
|
|
1077
1725
|
}
|
|
1078
1726
|
return null;
|
|
1079
1727
|
}
|
|
1080
|
-
return /* @__PURE__ */
|
|
1728
|
+
return /* @__PURE__ */ jsx21(UGCContainer, { ref: setContainerElement, style: containerStyle2, children: renderTree(
|
|
1081
1729
|
result.rootNode,
|
|
1082
1730
|
result.state,
|
|
1083
1731
|
assets,
|
|
1084
1732
|
result.cardStyles,
|
|
1085
1733
|
iconResolver,
|
|
1086
1734
|
onAction,
|
|
1087
|
-
onError
|
|
1735
|
+
onError,
|
|
1736
|
+
responsive,
|
|
1737
|
+
result.fragments
|
|
1088
1738
|
) });
|
|
1089
1739
|
}
|
|
1090
1740
|
export {
|
|
1741
|
+
Accordion,
|
|
1091
1742
|
Avatar,
|
|
1092
1743
|
Badge,
|
|
1093
1744
|
Box,
|
|
@@ -1102,6 +1753,7 @@ export {
|
|
|
1102
1753
|
Row,
|
|
1103
1754
|
Spacer,
|
|
1104
1755
|
Stack,
|
|
1756
|
+
Tabs,
|
|
1105
1757
|
Text,
|
|
1106
1758
|
Toggle,
|
|
1107
1759
|
UGCContainer,
|