xmlui 0.10.20 → 0.10.21

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xmlui",
3
- "version": "0.10.20",
3
+ "version": "0.10.21",
4
4
  "sideEffects": false,
5
5
  "scripts": {
6
6
  "start-test-bed": "cd src/testing/infrastructure && xmlui start",
@@ -54,7 +54,7 @@
54
54
  "@types/color": "3.0.6",
55
55
  "@vitejs/plugin-react": "4.3.0",
56
56
  "adm-zip": "0.5.10",
57
- "axios": "1.8.2",
57
+ "axios": "1.12.0",
58
58
  "chroma-js": "^3.1.2",
59
59
  "classnames": "2.5.1",
60
60
  "cmdk": "^1.0.4",
@@ -95,8 +95,6 @@
95
95
  "react-sticky-el": "^2.1.1",
96
96
  "react-textarea-autosize": "8.5.3",
97
97
  "react-time-picker": "^8.0.2",
98
- "react-virtualized-auto-sizer": "1.0.24",
99
- "react-window": "1.8.10",
100
98
  "recharts": "^2.15.1",
101
99
  "rehype-raw": "^7.0.0",
102
100
  "remark-gfm": "^4.0.1",
@@ -106,7 +104,7 @@
106
104
  "turndown": "^7.2.0",
107
105
  "unist-util-visit": "^5.0.0",
108
106
  "use-context-selector": "1.4.1",
109
- "virtua": "0.40.0",
107
+ "virtua": "^0.40.0",
110
108
  "vite-plugin-lib-inject-css": "1.3.0",
111
109
  "vite-plugin-svgr": "4.2.0",
112
110
  "vscode-languageserver": "^9.0.1",
@@ -131,7 +129,6 @@
131
129
  "@types/react-datepicker": "4.19.5",
132
130
  "@types/react-dom": "18.2.8",
133
131
  "@types/react-measure": "^2.0.8",
134
- "@types/react-window": "1.8.8",
135
132
  "@types/yargs": "17.0.31",
136
133
  "@typescript-eslint/eslint-plugin": "8.15.0",
137
134
  "@typescript-eslint/parser": "8.15.0",
@@ -121,7 +121,7 @@ exports.AppMd = (0, metadata_helpers_1.createMetadata)({
121
121
  },
122
122
  },
123
123
  });
124
- function AppNode({ node, extractValue, renderChild, className, lookupEventHandler }) {
124
+ function AppNode({ node, extractValue, renderChild, className, lookupEventHandler, registerComponentApi }) {
125
125
  // --- Use ref to track if we've already processed the navigation to avoid duplicates in strict mode
126
126
  const processedNavRef = (0, react_1.useRef)(false);
127
127
  // --- Memoize the layout type to avoid unnecessary re-extraction
@@ -330,7 +330,7 @@ function AppNode({ node, extractValue, renderChild, className, lookupEventHandle
330
330
  const renderedFooter = (0, react_1.useMemo)(() => renderChild(Footer), [Footer, renderChild]);
331
331
  const renderedNavPanel = (0, react_1.useMemo)(() => renderChild(NavPanel), [NavPanel, renderChild]);
332
332
  const renderedContent = (0, react_1.useMemo)(() => renderChild(restChildren), [restChildren, renderChild]);
333
- return ((0, jsx_runtime_1.jsxs)(AppNative_1.App, Object.assign({}, appProps, { header: renderedHeader, footer: renderedFooter, navPanel: renderedNavPanel, navPanelDef: NavPanel, logoContentDef: node.props.logoTemplate, renderChild: renderChild, children: [renderedContent, (0, jsx_runtime_1.jsx)(SearchIndexCollector, { Pages: Pages, renderChild: renderChild })] })));
333
+ return ((0, jsx_runtime_1.jsxs)(AppNative_1.App, Object.assign({}, appProps, { header: renderedHeader, footer: renderedFooter, navPanel: renderedNavPanel, navPanelDef: NavPanel, logoContentDef: node.props.logoTemplate, renderChild: renderChild, registerComponentApi: registerComponentApi, children: [renderedContent, (0, jsx_runtime_1.jsx)(SearchIndexCollector, { Pages: Pages, renderChild: renderChild })] })));
334
334
  }
335
335
  const HIDDEN_STYLE = {
336
336
  position: "absolute",
@@ -457,8 +457,8 @@ function PageIndexer({ Page, renderChild, onIndexed, }) {
457
457
  // console.log(`PageIndexer (${pageUrl}): Rendering content for ref population.`);
458
458
  return (0, jsx_runtime_1.jsx)("div", { ref: contentRef, children: renderChild(Page.children) });
459
459
  }
460
- exports.appRenderer = (0, renderers_1.createComponentRenderer)(COMP, exports.AppMd, ({ node, extractValue, renderChild, className, lookupEventHandler }) => {
461
- return ((0, jsx_runtime_1.jsx)(AppNode, { node: node, renderChild: renderChild, extractValue: extractValue, className: className, lookupEventHandler: lookupEventHandler }));
460
+ exports.appRenderer = (0, renderers_1.createComponentRenderer)(COMP, exports.AppMd, ({ node, extractValue, renderChild, className, lookupEventHandler, registerComponentApi }) => {
461
+ return ((0, jsx_runtime_1.jsx)(AppNode, { node: node, renderChild: renderChild, extractValue: extractValue, className: className, lookupEventHandler: lookupEventHandler, registerComponentApi: registerComponentApi }));
462
462
  });
463
463
  // --- Process the entire navigation tree recursively and build hierarchy
464
464
  function processNavItems(items, parentHierarchy, extractValue) {
@@ -44,7 +44,7 @@ exports.defaultProps = {
44
44
  onMessageReceived: lodash_es_1.noop,
45
45
  };
46
46
  function App(_a) {
47
- var { children, style = constants_1.EMPTY_OBJECT, layout, loggedInUser, scrollWholePage = exports.defaultProps.scrollWholePage, noScrollbarGutters = exports.defaultProps.noScrollbarGutters, onReady = exports.defaultProps.onReady, onMessageReceived = exports.defaultProps.onMessageReceived, header, navPanel, footer, navPanelDef, logoContentDef, logo, logoDark, logoLight, defaultTone, defaultTheme, autoDetectTone = exports.defaultProps.autoDetectTone, renderChild, name, className, applyDefaultContentPadding } = _a, rest = __rest(_a, ["children", "style", "layout", "loggedInUser", "scrollWholePage", "noScrollbarGutters", "onReady", "onMessageReceived", "header", "navPanel", "footer", "navPanelDef", "logoContentDef", "logo", "logoDark", "logoLight", "defaultTone", "defaultTheme", "autoDetectTone", "renderChild", "name", "className", "applyDefaultContentPadding"]);
47
+ var { children, style = constants_1.EMPTY_OBJECT, layout, loggedInUser, scrollWholePage = exports.defaultProps.scrollWholePage, noScrollbarGutters = exports.defaultProps.noScrollbarGutters, onReady = exports.defaultProps.onReady, onMessageReceived = exports.defaultProps.onMessageReceived, header, navPanel, footer, navPanelDef, logoContentDef, logo, logoDark, logoLight, defaultTone, defaultTheme, autoDetectTone = exports.defaultProps.autoDetectTone, renderChild, name, className, applyDefaultContentPadding, registerComponentApi } = _a, rest = __rest(_a, ["children", "style", "layout", "loggedInUser", "scrollWholePage", "noScrollbarGutters", "onReady", "onMessageReceived", "header", "navPanel", "footer", "navPanelDef", "logoContentDef", "logo", "logoDark", "logoLight", "defaultTone", "defaultTheme", "autoDetectTone", "renderChild", "name", "className", "applyDefaultContentPadding", "registerComponentApi"]);
48
48
  const { getThemeVar } = (0, ThemeContext_1.useTheme)();
49
49
  const { setActiveThemeTone, setActiveThemeId, themes } = (0, ThemeContext_1.useThemes)();
50
50
  const mounted = (0, react_1.useRef)(false);
@@ -419,7 +419,6 @@ exports.DateInput = (0, react_2.forwardRef)(function DateInputNative(_a, ref) {
419
419
  }
420
420
  }, [day, month, year]);
421
421
  // Component API registration
422
- (0, react_2.useImperativeHandle)(ref, () => dateInputRef.current);
423
422
  (0, react_2.useEffect)(() => {
424
423
  if (registerComponentApi) {
425
424
  registerComponentApi({
@@ -205,7 +205,6 @@ function cleanUpSubject(subject) {
205
205
  const Form = (0, react_2.forwardRef)(function (_a, ref) {
206
206
  var { formState, dispatch, initialValue = constants_1.EMPTY_OBJECT, children, style, className, enabled = true, cancelLabel = exports.defaultProps.cancelLabel, saveLabel = exports.defaultProps.saveLabel, saveInProgressLabel = exports.defaultProps.saveInProgressLabel, swapCancelAndSave, onSubmit, onCancel, onReset, onSuccess, buttonRow, id, registerComponentApi, itemLabelBreak = exports.defaultProps.itemLabelBreak, itemLabelWidth, itemLabelPosition = exports.defaultProps.itemLabelPosition, keepModalOpenOnSubmit = exports.defaultProps.keepModalOpenOnSubmit, hideButtonRowUntilDirty } = _a, rest = __rest(_a, ["formState", "dispatch", "initialValue", "children", "style", "className", "enabled", "cancelLabel", "saveLabel", "saveInProgressLabel", "swapCancelAndSave", "onSubmit", "onCancel", "onReset", "onSuccess", "buttonRow", "id", "registerComponentApi", "itemLabelBreak", "itemLabelWidth", "itemLabelPosition", "keepModalOpenOnSubmit", "hideButtonRowUntilDirty"]);
207
207
  const formRef = (0, react_2.useRef)(null);
208
- (0, react_2.useImperativeHandle)(ref, () => formRef.current);
209
208
  const [confirmSubmitModalVisible, setConfirmSubmitModalVisible] = (0, react_2.useState)(false);
210
209
  const requestModalFormClose = (0, ModalVisibilityContext_1.useModalFormClose)();
211
210
  const isEnabled = enabled && !formState.submitInProgress;
@@ -22,8 +22,6 @@ const IFrame_module_scss_1 = __importDefault(require("./IFrame.module.scss"));
22
22
  exports.IFrame = (0, react_1.memo)((0, react_1.forwardRef)(function IFrame(_a, ref) {
23
23
  var { src, srcdoc, allow, name, referrerPolicy, sandbox, style, className, onLoad, registerComponentApi } = _a, rest = __rest(_a, ["src", "srcdoc", "allow", "name", "referrerPolicy", "sandbox", "style", "className", "onLoad", "registerComponentApi"]);
24
24
  const iframeRef = (0, react_1.useRef)(null);
25
- // Expose the iframe element to parent via ref
26
- (0, react_1.useImperativeHandle)(ref, () => iframeRef.current, []);
27
25
  // Register component APIs
28
26
  (0, react_1.useEffect)(() => {
29
27
  registerComponentApi === null || registerComponentApi === void 0 ? void 0 : registerComponentApi({
@@ -33,7 +33,11 @@ exports.TextMd = (0, metadata_helpers_1.createMetadata)({
33
33
  variant: {
34
34
  description: "An optional string value that provides named presets for text variants with a " +
35
35
  "unique combination of font style, weight, size, color, and other parameters. " +
36
- "If not defined, the text uses the current style of its context.",
36
+ "If not defined, the text uses the current style of its context. " +
37
+ "In addition to predefined variants, you can specify custom variant names and style them " +
38
+ "using theme variables with the pattern `{cssProperty}-Text-{variantName}` " +
39
+ "(e.g., `textColor-Text-brandTitle`, `fontSize-Text-highlight`). " +
40
+ "See the documentation for a complete list of supported CSS properties.",
37
41
  availableValues: abstractions_1.variantOptionsMd,
38
42
  },
39
43
  maxLines: (0, metadata_helpers_1.d)("This property determines the maximum number of lines the component can wrap to. " +
@@ -1063,6 +1063,323 @@ fixtures_1.test.describe("Theme Variables", () => {
1063
1063
  }));
1064
1064
  });
1065
1065
  // =============================================================================
1066
+ // CUSTOM VARIANT TESTS
1067
+ // =============================================================================
1068
+ fixtures_1.test.describe("Custom Variants", () => {
1069
+ (0, fixtures_1.test)("custom variant textColor theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1070
+ const EXPECTED = "rgb(255, 0, 0)";
1071
+ yield initTestBed(`
1072
+ <App>
1073
+ <Theme textColor-Text-customRed="${EXPECTED}">
1074
+ <Text variant="customRed" testId="text">Hello Custom!</Text>
1075
+ </Theme>
1076
+ </App>
1077
+ `);
1078
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("color", EXPECTED);
1079
+ }));
1080
+ (0, fixtures_1.test)("custom variant fontFamily theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1081
+ const EXPECTED = "monospace";
1082
+ yield initTestBed(`
1083
+ <App>
1084
+ <Theme fontFamily-Text-customMono="${EXPECTED}">
1085
+ <Text variant="customMono" testId="text">Hello Custom!</Text>
1086
+ </Theme>
1087
+ </App>
1088
+ `);
1089
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("font-family", EXPECTED);
1090
+ }));
1091
+ (0, fixtures_1.test)("custom variant fontSize theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1092
+ const EXPECTED = "24px";
1093
+ yield initTestBed(`
1094
+ <App>
1095
+ <Theme fontSize-Text-customLarge="${EXPECTED}">
1096
+ <Text variant="customLarge" testId="text">Hello Custom!</Text>
1097
+ </Theme>
1098
+ </App>
1099
+ `);
1100
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("font-size", EXPECTED);
1101
+ }));
1102
+ (0, fixtures_1.test)("custom variant fontStyle theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1103
+ const EXPECTED = "italic";
1104
+ yield initTestBed(`
1105
+ <App>
1106
+ <Theme fontStyle-Text-customItalic="${EXPECTED}">
1107
+ <Text variant="customItalic" testId="text">Hello Custom!</Text>
1108
+ </Theme>
1109
+ </App>
1110
+ `);
1111
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("font-style", EXPECTED);
1112
+ }));
1113
+ (0, fixtures_1.test)("custom variant fontWeight theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1114
+ const EXPECTED = "700";
1115
+ yield initTestBed(`
1116
+ <App>
1117
+ <Theme fontWeight-Text-customBold="bold">
1118
+ <Text variant="customBold" testId="text">Hello Custom!</Text>
1119
+ </Theme>
1120
+ </App>
1121
+ `);
1122
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("font-weight", EXPECTED);
1123
+ }));
1124
+ (0, fixtures_1.test)("custom variant fontStretch theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1125
+ const EXPECTED = "125%";
1126
+ yield initTestBed(`
1127
+ <App>
1128
+ <Theme fontStretch-Text-customExpanded="expanded">
1129
+ <Text variant="customExpanded" testId="text">Hello Custom!</Text>
1130
+ </Theme>
1131
+ </App>
1132
+ `);
1133
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("font-stretch", EXPECTED);
1134
+ }));
1135
+ (0, fixtures_1.test)("custom variant textDecorationLine theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1136
+ const EXPECTED = "underline";
1137
+ yield initTestBed(`
1138
+ <App>
1139
+ <Theme textDecorationLine-Text-customUnderline="${EXPECTED}">
1140
+ <Text variant="customUnderline" testId="text">Hello Custom!</Text>
1141
+ </Theme>
1142
+ </App>
1143
+ `);
1144
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("text-decoration-line", EXPECTED);
1145
+ }));
1146
+ (0, fixtures_1.test)("custom variant textDecorationColor theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1147
+ const EXPECTED = "rgb(255, 0, 0)";
1148
+ yield initTestBed(`
1149
+ <App>
1150
+ <Theme
1151
+ textDecorationLine-Text-customDeco="underline"
1152
+ textDecorationColor-Text-customDeco="${EXPECTED}"
1153
+ >
1154
+ <Text variant="customDeco" testId="text">Hello Custom!</Text>
1155
+ </Theme>
1156
+ </App>
1157
+ `);
1158
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("text-decoration-color", EXPECTED);
1159
+ }));
1160
+ (0, fixtures_1.test)("custom variant textDecorationStyle theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1161
+ const EXPECTED = "wavy";
1162
+ yield initTestBed(`
1163
+ <App>
1164
+ <Theme
1165
+ textDecorationLine-Text-customWavy="underline"
1166
+ textDecorationStyle-Text-customWavy="${EXPECTED}"
1167
+ >
1168
+ <Text variant="customWavy" testId="text">Hello Custom!</Text>
1169
+ </Theme>
1170
+ </App>
1171
+ `);
1172
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("text-decoration-style", EXPECTED);
1173
+ }));
1174
+ (0, fixtures_1.test)("custom variant textDecorationThickness theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1175
+ const EXPECTED = "3px";
1176
+ yield initTestBed(`
1177
+ <App>
1178
+ <Theme
1179
+ textDecorationLine-Text-customThick="underline"
1180
+ textDecorationThickness-Text-customThick="${EXPECTED}"
1181
+ >
1182
+ <Text variant="customThick" testId="text">Hello Custom!</Text>
1183
+ </Theme>
1184
+ </App>
1185
+ `);
1186
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("text-decoration-thickness", EXPECTED);
1187
+ }));
1188
+ (0, fixtures_1.test)("custom variant textUnderlineOffset theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1189
+ const EXPECTED = "5px";
1190
+ yield initTestBed(`
1191
+ <App>
1192
+ <Theme
1193
+ textDecorationLine-Text-customOffset="underline"
1194
+ textUnderlineOffset-Text-customOffset="${EXPECTED}"
1195
+ >
1196
+ <Text variant="customOffset" testId="text">Hello Custom!</Text>
1197
+ </Theme>
1198
+ </App>
1199
+ `);
1200
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("text-underline-offset", EXPECTED);
1201
+ }));
1202
+ (0, fixtures_1.test)("custom variant lineHeight theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1203
+ const EXPECTED = "32px";
1204
+ yield initTestBed(`
1205
+ <App>
1206
+ <Theme lineHeight-Text-customLineHeight="${EXPECTED}">
1207
+ <Text variant="customLineHeight" testId="text">Hello Custom!</Text>
1208
+ </Theme>
1209
+ </App>
1210
+ `);
1211
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("line-height", EXPECTED);
1212
+ }));
1213
+ (0, fixtures_1.test)("custom variant backgroundColor theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1214
+ const EXPECTED = "rgb(255, 255, 0)";
1215
+ yield initTestBed(`
1216
+ <App>
1217
+ <Theme backgroundColor-Text-customBg="${EXPECTED}">
1218
+ <Text variant="customBg" testId="text">Hello Custom!</Text>
1219
+ </Theme>
1220
+ </App>
1221
+ `);
1222
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("background-color", EXPECTED);
1223
+ }));
1224
+ (0, fixtures_1.test)("custom variant textTransform theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1225
+ const EXPECTED = "uppercase";
1226
+ yield initTestBed(`
1227
+ <App>
1228
+ <Theme textTransform-Text-customUpper="${EXPECTED}">
1229
+ <Text variant="customUpper" testId="text">Hello Custom!</Text>
1230
+ </Theme>
1231
+ </App>
1232
+ `);
1233
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("text-transform", EXPECTED);
1234
+ }));
1235
+ (0, fixtures_1.test)("custom variant letterSpacing theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1236
+ const EXPECTED = "5px";
1237
+ yield initTestBed(`
1238
+ <App>
1239
+ <Theme letterSpacing-Text-customSpaced="${EXPECTED}">
1240
+ <Text variant="customSpaced" testId="text">Hello Custom!</Text>
1241
+ </Theme>
1242
+ </App>
1243
+ `);
1244
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("letter-spacing", EXPECTED);
1245
+ }));
1246
+ (0, fixtures_1.test)("custom variant wordSpacing theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1247
+ const EXPECTED = "10px";
1248
+ yield initTestBed(`
1249
+ <App>
1250
+ <Theme wordSpacing-Text-customWordSpace="${EXPECTED}">
1251
+ <Text variant="customWordSpace" testId="text">Hello Custom!</Text>
1252
+ </Theme>
1253
+ </App>
1254
+ `);
1255
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("word-spacing", EXPECTED);
1256
+ }));
1257
+ (0, fixtures_1.test)("custom variant textShadow theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1258
+ const EXPECTED = "rgb(255, 0, 0) 2px 2px 4px";
1259
+ yield initTestBed(`
1260
+ <App>
1261
+ <Theme textShadow-Text-customShadow="${EXPECTED}">
1262
+ <Text variant="customShadow" testId="text">Hello Custom!</Text>
1263
+ </Theme>
1264
+ </App>
1265
+ `);
1266
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("text-shadow", EXPECTED);
1267
+ }));
1268
+ (0, fixtures_1.test)("custom variant textIndent theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1269
+ const EXPECTED = "20px";
1270
+ yield initTestBed(`
1271
+ <App>
1272
+ <Theme textIndent-Text-customIndent="${EXPECTED}">
1273
+ <Text variant="customIndent" testId="text">Hello Custom!</Text>
1274
+ </Theme>
1275
+ </App>
1276
+ `);
1277
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("text-indent", EXPECTED);
1278
+ }));
1279
+ (0, fixtures_1.test)("custom variant textAlign theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1280
+ const EXPECTED = "center";
1281
+ yield initTestBed(`
1282
+ <App>
1283
+ <Theme textAlign-Text-customCenter="${EXPECTED}">
1284
+ <Text variant="customCenter" testId="text">Hello Custom!</Text>
1285
+ </Theme>
1286
+ </App>
1287
+ `);
1288
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("text-align", EXPECTED);
1289
+ }));
1290
+ (0, fixtures_1.test)("custom variant textAlignLast theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1291
+ const EXPECTED = "right";
1292
+ yield initTestBed(`
1293
+ <App>
1294
+ <Theme textAlignLast-Text-customAlignLast="${EXPECTED}">
1295
+ <Text variant="customAlignLast" testId="text">Hello Custom!</Text>
1296
+ </Theme>
1297
+ </App>
1298
+ `);
1299
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("text-align-last", EXPECTED);
1300
+ }));
1301
+ (0, fixtures_1.test)("custom variant wordBreak theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1302
+ const EXPECTED = "break-all";
1303
+ yield initTestBed(`
1304
+ <App>
1305
+ <Theme wordBreak-Text-customBreak="${EXPECTED}">
1306
+ <Text variant="customBreak" breakMode="'{undefined}'" testId="text">Hello Custom!</Text>
1307
+ </Theme>
1308
+ </App>
1309
+ `);
1310
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("word-break", EXPECTED);
1311
+ }));
1312
+ (0, fixtures_1.test)("custom variant wordWrap theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1313
+ const EXPECTED = "break-word";
1314
+ yield initTestBed(`
1315
+ <App>
1316
+ <Theme wordWrap-Text-customWrap="${EXPECTED}">
1317
+ <Text variant="customWrap" breakMode="'{undefined}'" testId="text">Hello Custom!</Text>
1318
+ </Theme>
1319
+ </App>
1320
+ `);
1321
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("word-wrap", EXPECTED);
1322
+ }));
1323
+ (0, fixtures_1.test)("custom variant direction theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1324
+ const EXPECTED = "rtl";
1325
+ yield initTestBed(`
1326
+ <App>
1327
+ <Theme direction-Text-customRtl="${EXPECTED}">
1328
+ <Text variant="customRtl" testId="text">Hello Custom!</Text>
1329
+ </Theme>
1330
+ </App>
1331
+ `);
1332
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("direction", EXPECTED);
1333
+ }));
1334
+ (0, fixtures_1.test)("custom variant writingMode theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1335
+ const EXPECTED = "vertical-rl";
1336
+ yield initTestBed(`
1337
+ <App>
1338
+ <Theme writingMode-Text-customVertical="${EXPECTED}">
1339
+ <Text variant="customVertical" testId="text">Hello Custom!</Text>
1340
+ </Theme>
1341
+ </App>
1342
+ `);
1343
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("writing-mode", EXPECTED);
1344
+ }));
1345
+ (0, fixtures_1.test)("custom variant lineBreak theme variable", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1346
+ const EXPECTED = "strict";
1347
+ yield initTestBed(`
1348
+ <App>
1349
+ <Theme lineBreak-Text-customLineBreak="${EXPECTED}">
1350
+ <Text variant="customLineBreak" testId="text">Hello Custom!</Text>
1351
+ </Theme>
1352
+ </App>
1353
+ `);
1354
+ yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveCSS("line-break", EXPECTED);
1355
+ }));
1356
+ (0, fixtures_1.test)("custom variant with multiple theme variables", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
1357
+ yield initTestBed(`
1358
+ <App>
1359
+ <Theme
1360
+ textColor-Text-pinkElephant="rgb(255, 192, 203)"
1361
+ fontWeight-Text-pinkElephant="bold"
1362
+ textColor-Text-greenDog="rgb(0, 128, 0)"
1363
+ fontStyle-Text-greenDog="italic"
1364
+ >
1365
+ <Text variant="pinkElephant" testId="pink">
1366
+ Hello Pink Elephant!
1367
+ </Text>
1368
+ <Text variant="greenDog" testId="green">
1369
+ Hello Green Dog!
1370
+ </Text>
1371
+ </Theme>
1372
+ </App>
1373
+ `);
1374
+ const pinkText = page.getByTestId("pink");
1375
+ const greenText = page.getByTestId("green");
1376
+ yield (0, fixtures_1.expect)(pinkText).toHaveCSS("color", "rgb(255, 192, 203)");
1377
+ yield (0, fixtures_1.expect)(pinkText).toHaveCSS("font-weight", "700");
1378
+ yield (0, fixtures_1.expect)(greenText).toHaveCSS("color", "rgb(0, 128, 0)");
1379
+ yield (0, fixtures_1.expect)(greenText).toHaveCSS("font-style", "italic");
1380
+ }));
1381
+ });
1382
+ // =============================================================================
1066
1383
  // SMOKE TESTS (kept for compatibility)
1067
1384
  // =============================================================================
1068
1385
  fixtures_1.test.describe("smoke tests", { tag: "@smoke" }, () => {
@@ -15,6 +15,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.Text = exports.defaultProps = void 0;
18
+ exports.getCustomVariantCache = getCustomVariantCache;
19
+ exports.setCustomVariantCache = setCustomVariantCache;
20
+ exports.hasCustomVariantCache = hasCustomVariantCache;
21
+ exports.clearCustomVariantCache = clearCustomVariantCache;
22
+ exports.getCustomVariantCacheStats = getCustomVariantCacheStats;
18
23
  const jsx_runtime_1 = require("react/jsx-runtime");
19
24
  const react_1 = require("react");
20
25
  const react_compose_refs_1 = require("@radix-ui/react-compose-refs");
@@ -22,6 +27,54 @@ const classnames_1 = __importDefault(require("classnames"));
22
27
  const Text_module_scss_1 = __importDefault(require("./Text.module.scss"));
23
28
  const css_utils_1 = require("../../components-core/utils/css-utils");
24
29
  const abstractions_1 = require("../abstractions");
30
+ const StyleContext_1 = require("../../components-core/theming/StyleContext");
31
+ const constants_1 = require("../../components-core/constants");
32
+ const layout_resolver_1 = require("../../components-core/theming/layout-resolver");
33
+ /**
34
+ * Global cache that stores custom variant CSS styles.
35
+ * Key: variant value (string)
36
+ *
37
+ * This cache ensures the same variant value always generates the same CSS.
38
+ */
39
+ const customVariantCache = new Map();
40
+ /**
41
+ * Retrieves a cached custom variant entry if it exists.
42
+ */
43
+ function getCustomVariantCache(variant) {
44
+ return customVariantCache.get(variant);
45
+ }
46
+ /**
47
+ * Stores a custom variant entry in the cache.
48
+ */
49
+ function setCustomVariantCache(variant, entry) {
50
+ customVariantCache.set(variant, Object.assign(Object.assign({}, entry), { createdAt: Date.now() }));
51
+ }
52
+ /**
53
+ * Checks if a custom variant is already cached.
54
+ */
55
+ function hasCustomVariantCache(variant) {
56
+ return customVariantCache.has(variant);
57
+ }
58
+ /**
59
+ * Clears the entire custom variant cache.
60
+ * Useful for testing or full app resets.
61
+ */
62
+ function clearCustomVariantCache() {
63
+ customVariantCache.clear();
64
+ }
65
+ /**
66
+ * Gets cache statistics for debugging.
67
+ */
68
+ function getCustomVariantCacheStats() {
69
+ return {
70
+ totalEntries: customVariantCache.size,
71
+ entries: Array.from(customVariantCache.entries()).map(([key, entry]) => ({
72
+ key,
73
+ className: entry.className,
74
+ createdAt: new Date(entry.createdAt).toISOString(),
75
+ })),
76
+ };
77
+ }
25
78
  exports.defaultProps = {
26
79
  maxLines: 0,
27
80
  preserveLinebreaks: false,
@@ -57,6 +110,62 @@ exports.Text = (0, react_1.forwardRef)(function Text(_a, forwardedRef) {
57
110
  return "div";
58
111
  return abstractions_1.TextVariantElement[variant];
59
112
  }, [variant]);
113
+ // Custom variant CSS generation
114
+ // Following React hook rules: hooks must be called unconditionally
115
+ // We always call useComponentStyle, passing empty object for known variants
116
+ const isCustomVariant = (0, react_1.useMemo)(() => {
117
+ return variant && !abstractions_1.TextVariantElement[variant];
118
+ }, [variant]);
119
+ // Always call useComponentStyle (React hook rule: no conditional hooks)
120
+ // For now, pass empty object; later this will contain assembled CSS properties
121
+ const variantSpec = (0, react_1.useMemo)(() => {
122
+ if (!isCustomVariant)
123
+ return constants_1.EMPTY_OBJECT;
124
+ const subject = `-Text-${variant}`;
125
+ const cssInput = {
126
+ color: (0, layout_resolver_1.toCssVar)(`$textColor${subject}`),
127
+ "font-family": (0, layout_resolver_1.toCssVar)(`$fontFamily${subject}`),
128
+ "font-size": (0, layout_resolver_1.toCssVar)(`$fontSize${subject}`),
129
+ "font-style": (0, layout_resolver_1.toCssVar)(`$fontStyle${subject}`),
130
+ "font-weight": (0, layout_resolver_1.toCssVar)(`$fontWeight${subject}`),
131
+ "font-stretch": (0, layout_resolver_1.toCssVar)(`$fontStretch${subject}`),
132
+ "text-decoration-line": (0, layout_resolver_1.toCssVar)(`$textDecorationLine${subject}`),
133
+ "text-decoration-color": (0, layout_resolver_1.toCssVar)(`$textDecorationColor${subject}`),
134
+ "text-decoration-style": (0, layout_resolver_1.toCssVar)(`$textDecorationStyle${subject}`),
135
+ "text-decoration-thickness": (0, layout_resolver_1.toCssVar)(`$textDecorationThickness${subject}`),
136
+ "text-underline-offset": (0, layout_resolver_1.toCssVar)(`$textUnderlineOffset${subject}`),
137
+ "line-height": (0, layout_resolver_1.toCssVar)(`$lineHeight${subject}`),
138
+ "background-color": (0, layout_resolver_1.toCssVar)(`$backgroundColor${subject}`),
139
+ "text-transform": (0, layout_resolver_1.toCssVar)(`$textTransform${subject}`),
140
+ "letter-spacing": (0, layout_resolver_1.toCssVar)(`$letterSpacing${subject}`),
141
+ "word-spacing": (0, layout_resolver_1.toCssVar)(`$wordSpacing${subject}`),
142
+ "text-shadow": (0, layout_resolver_1.toCssVar)(`$textShadow${subject}`),
143
+ "text-indent": (0, layout_resolver_1.toCssVar)(`$textIndent${subject}`),
144
+ "text-align": (0, layout_resolver_1.toCssVar)(`$textAlign${subject}`),
145
+ "text-align-last": (0, layout_resolver_1.toCssVar)(`$textAlignLast${subject}`),
146
+ "word-break": (0, layout_resolver_1.toCssVar)(`$wordBreak${subject}`),
147
+ "word-wrap": (0, layout_resolver_1.toCssVar)(`$wordWrap${subject}`),
148
+ direction: (0, layout_resolver_1.toCssVar)(`$direction${subject}`),
149
+ "writing-mode": (0, layout_resolver_1.toCssVar)(`$writingMode${subject}`),
150
+ "line-break": (0, layout_resolver_1.toCssVar)(`$lineBreak${subject}`),
151
+ };
152
+ return cssInput;
153
+ }, [isCustomVariant, variant]);
154
+ const customVariantClassName = (0, StyleContext_1.useComponentStyle)(variantSpec);
155
+ // Store custom variant in cache if it's a new custom variant
156
+ (0, react_1.useEffect)(() => {
157
+ if (isCustomVariant && variant && customVariantClassName) {
158
+ // Check if this variant is already cached
159
+ if (!hasCustomVariantCache(variant)) {
160
+ // TODO: When CSS generation is implemented, extract the actual CSS text
161
+ // For now, store placeholder information
162
+ setCustomVariantCache(variant, {
163
+ className: customVariantClassName,
164
+ cssText: "", // Will be populated when CSS generation is implemented
165
+ });
166
+ }
167
+ }
168
+ }, [isCustomVariant, variant, customVariantClassName]);
60
169
  // Determine overflow mode classes based on overflowMode and existing props
61
170
  const overflowClasses = (0, react_1.useMemo)(() => {
62
171
  const classes = {};
@@ -127,7 +236,9 @@ exports.Text = (0, react_1.forwardRef)(function Text(_a, forwardedRef) {
127
236
  }
128
237
  return classes;
129
238
  }, [breakMode]);
130
- return ((0, jsx_runtime_1.jsx)(Element, Object.assign({}, restVariantSpecificProps, { ref: ref, className: (0, classnames_1.default)(syntaxHighlightClasses, Text_module_scss_1.default.text, Text_module_scss_1.default[variant || "default"], Object.assign(Object.assign({ [Text_module_scss_1.default.preserveLinebreaks]: preserveLinebreaks }, overflowClasses), breakClasses), className), style: Object.assign(Object.assign({}, style), (overflowMode === "ellipsis" || (!overflowMode && maxLines)
239
+ return ((0, jsx_runtime_1.jsx)(Element, Object.assign({}, restVariantSpecificProps, { ref: ref, className: (0, classnames_1.default)(syntaxHighlightClasses, Text_module_scss_1.default.text,
240
+ // Use custom variant className if it's a custom variant, otherwise use predefined variant style
241
+ isCustomVariant ? customVariantClassName : Text_module_scss_1.default[variant || "default"], Object.assign(Object.assign({ [Text_module_scss_1.default.preserveLinebreaks]: preserveLinebreaks }, overflowClasses), breakClasses), className), style: Object.assign(Object.assign({}, style), (overflowMode === "ellipsis" || (!overflowMode && maxLines)
131
242
  ? (0, css_utils_1.getMaxLinesStyle)(maxLines)
132
243
  : {})), children: children })));
133
244
  });
@@ -357,7 +357,6 @@ exports.TimeInputNative = (0, react_1.forwardRef)(function TimeInputNative(_a, r
357
357
  return `${h24}:${m24}:${s24}`;
358
358
  }, [hour, minute, second, amPm, is12HourFormat]);
359
359
  // Component API registration
360
- (0, react_1.useImperativeHandle)(ref, () => timeInputRef.current);
361
360
  (0, react_1.useEffect)(() => {
362
361
  if (registerComponentApi) {
363
362
  registerComponentApi({
@@ -86,7 +86,6 @@ exports.Timer = (0, react_1.forwardRef)(function Timer(_a, forwardedRef) {
86
86
  isRunning: () => isRunning && !isPaused,
87
87
  }), [pause, resume, isPaused, isRunning]);
88
88
  // Register both APIs together
89
- (0, react_1.useImperativeHandle)(forwardedRef, () => timerApi, [timerApi]);
90
89
  (0, react_1.useEffect)(() => {
91
90
  if (registerComponentApi) {
92
91
  registerComponentApi(timerApi);