@officexapp/catalogs-cli 0.4.1 → 0.4.3
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.js +131 -16
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -322,6 +322,7 @@ function deepValidateCatalog(schema) {
|
|
|
322
322
|
const edges = routing.edges || [];
|
|
323
323
|
const entry = routing.entry;
|
|
324
324
|
const KNOWN_TYPES = /* @__PURE__ */ new Set([
|
|
325
|
+
// Display / layout
|
|
325
326
|
"heading",
|
|
326
327
|
"paragraph",
|
|
327
328
|
"image",
|
|
@@ -333,23 +334,50 @@ function deepValidateCatalog(schema) {
|
|
|
333
334
|
"pricing_card",
|
|
334
335
|
"testimonial",
|
|
335
336
|
"faq",
|
|
337
|
+
"accordion",
|
|
336
338
|
"timeline",
|
|
337
339
|
"file_download",
|
|
338
340
|
"iframe",
|
|
341
|
+
"table",
|
|
342
|
+
"social_links",
|
|
343
|
+
"tabs",
|
|
344
|
+
"countdown",
|
|
345
|
+
"comparison_table",
|
|
346
|
+
"progress_bar",
|
|
347
|
+
"modal",
|
|
348
|
+
"section_collapse",
|
|
349
|
+
"subform",
|
|
350
|
+
// Inputs
|
|
339
351
|
"short_text",
|
|
352
|
+
"long_text",
|
|
353
|
+
"rich_text",
|
|
340
354
|
"email",
|
|
341
355
|
"phone",
|
|
342
356
|
"url",
|
|
357
|
+
"address",
|
|
343
358
|
"number",
|
|
344
|
-
"
|
|
345
|
-
"
|
|
359
|
+
"currency",
|
|
360
|
+
"date",
|
|
361
|
+
"datetime",
|
|
362
|
+
"time",
|
|
363
|
+
"date_range",
|
|
364
|
+
"dropdown",
|
|
365
|
+
"multiselect",
|
|
346
366
|
"multiple_choice",
|
|
347
367
|
"checkboxes",
|
|
348
|
-
"
|
|
349
|
-
"slider",
|
|
350
|
-
"star_rating",
|
|
368
|
+
"picture_choice",
|
|
351
369
|
"switch",
|
|
352
370
|
"checkbox",
|
|
371
|
+
"choice_matrix",
|
|
372
|
+
"ranking",
|
|
373
|
+
"star_rating",
|
|
374
|
+
"slider",
|
|
375
|
+
"opinion_scale",
|
|
376
|
+
"file_upload",
|
|
377
|
+
"signature",
|
|
378
|
+
"password",
|
|
379
|
+
"location",
|
|
380
|
+
// Special
|
|
353
381
|
"payment"
|
|
354
382
|
]);
|
|
355
383
|
if (entry && !pageIds.has(entry)) {
|
|
@@ -1340,7 +1368,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
1340
1368
|
}
|
|
1341
1369
|
|
|
1342
1370
|
case 'html':
|
|
1343
|
-
return h('
|
|
1371
|
+
return h(HtmlBlock, { content: props.content || '', className: compClass, style: compStyle, formState });
|
|
1344
1372
|
|
|
1345
1373
|
case 'banner': {
|
|
1346
1374
|
const variants = {
|
|
@@ -1976,6 +2004,38 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
1976
2004
|
);
|
|
1977
2005
|
}
|
|
1978
2006
|
|
|
2007
|
+
// --- HtmlBlock: renders HTML content and executes inline <script> tags ---
|
|
2008
|
+
function HtmlBlock({ content, className, style, formState }) {
|
|
2009
|
+
const ref = React.useRef(null);
|
|
2010
|
+
const executedRef = React.useRef(new Set());
|
|
2011
|
+
// Template interpolation: replace {{field_id}} with form state values
|
|
2012
|
+
const interpolated = React.useMemo(() =>
|
|
2013
|
+
(content || '').replace(/{{(w+)}}/g, (_, id) => formState?.[id] ?? ''),
|
|
2014
|
+
[content, formState]
|
|
2015
|
+
);
|
|
2016
|
+
React.useEffect(() => {
|
|
2017
|
+
const container = ref.current;
|
|
2018
|
+
if (!container) return;
|
|
2019
|
+
const scripts = container.querySelectorAll('script');
|
|
2020
|
+
scripts.forEach((orig) => {
|
|
2021
|
+
const key = orig.src || orig.textContent || '';
|
|
2022
|
+
if (executedRef.current.has(key)) return;
|
|
2023
|
+
executedRef.current.add(key);
|
|
2024
|
+
if (orig.src) {
|
|
2025
|
+
const s = document.createElement('script');
|
|
2026
|
+
s.src = orig.src;
|
|
2027
|
+
if (orig.type) s.type = orig.type;
|
|
2028
|
+
s.async = true;
|
|
2029
|
+
container.appendChild(s);
|
|
2030
|
+
} else if (orig.textContent) {
|
|
2031
|
+
try { new Function(orig.textContent)(); }
|
|
2032
|
+
catch (e) { console.error('[CatalogKit:dev] Inline script error:', e); }
|
|
2033
|
+
}
|
|
2034
|
+
});
|
|
2035
|
+
}, [interpolated]);
|
|
2036
|
+
return h('div', { ref, className: 'prose prose-sm max-w-none ' + (className || ''), style, dangerouslySetInnerHTML: { __html: interpolated } });
|
|
2037
|
+
}
|
|
2038
|
+
|
|
1979
2039
|
function ActionButton({ action, themeColor, onAction }) {
|
|
1980
2040
|
const st = action.style || 'primary';
|
|
1981
2041
|
const hasSide = !!action.side_statement;
|
|
@@ -2021,7 +2081,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
2021
2081
|
);
|
|
2022
2082
|
}
|
|
2023
2083
|
|
|
2024
|
-
function StickyBottomBar({ config, page, formState, cartItems, themeColor, onNext, onAction, onBack, historyLen }) {
|
|
2084
|
+
function StickyBottomBar({ config, page, formState, cartItems, themeColor, onNext, onAction, onFieldAndNavigate, onBack, historyLen }) {
|
|
2025
2085
|
const [visible, setVisible] = React.useState(!config.delay_ms);
|
|
2026
2086
|
const [scrollDir, setScrollDir] = React.useState('down');
|
|
2027
2087
|
React.useEffect(() => {
|
|
@@ -2044,15 +2104,27 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
2044
2104
|
glass_dark: { backgroundColor: 'rgba(0,0,0,0.85)', backdropFilter: 'blur(16px)', color: 'white' },
|
|
2045
2105
|
gradient: { background: 'linear-gradient(135deg, ' + themeColor + ' 0%, ' + themeColor + 'dd 100%)', color: 'white' },
|
|
2046
2106
|
};
|
|
2047
|
-
const
|
|
2048
|
-
const
|
|
2049
|
-
if (
|
|
2050
|
-
if (
|
|
2051
|
-
const actionId =
|
|
2107
|
+
const dispatchAction = (act) => {
|
|
2108
|
+
const cmd = act?.action || 'next';
|
|
2109
|
+
if (cmd === 'next') { onNext(); return; }
|
|
2110
|
+
if (cmd.startsWith('action:')) {
|
|
2111
|
+
const actionId = cmd.slice(7);
|
|
2052
2112
|
const action = page.actions?.find(a => a.id === actionId);
|
|
2053
2113
|
if (action) onAction(action); else onNext();
|
|
2054
|
-
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
if (cmd.startsWith('field:')) {
|
|
2117
|
+
const parts = cmd.slice(6).split(':');
|
|
2118
|
+
if (parts.length >= 2) { onFieldAndNavigate(parts[0], parts.slice(1).join(':')); }
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
2121
|
+
onNext();
|
|
2055
2122
|
};
|
|
2123
|
+
const primaryLabel = config.primary?.label
|
|
2124
|
+
? interpolate(config.primary.label)
|
|
2125
|
+
: config.button_text || page.submit_label || 'Continue';
|
|
2126
|
+
const secondaryAction = config.secondary;
|
|
2127
|
+
const secondaryLabel = secondaryAction?.label ? interpolate(secondaryAction.label) : null;
|
|
2056
2128
|
return h('div', {
|
|
2057
2129
|
className: 'cf-sticky-bar' + (show ? '' : ' hidden'),
|
|
2058
2130
|
style: bgStyles[config.style || 'solid'] || bgStyles.solid,
|
|
@@ -2066,12 +2138,19 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
2066
2138
|
h('div', { className: 'flex items-center gap-3' },
|
|
2067
2139
|
config.cart_badge && cartItems.length > 0
|
|
2068
2140
|
? h('span', { className: 'bg-red-500 text-white text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center' }, cartItems.length) : null,
|
|
2141
|
+
secondaryLabel
|
|
2142
|
+
? h('button', {
|
|
2143
|
+
className: 'text-sm font-medium hover:opacity-80 transition-opacity',
|
|
2144
|
+
style: { color: config.style === 'glass_dark' ? 'rgba(255,255,255,0.6)' : '#6b7280' },
|
|
2145
|
+
onClick: () => dispatchAction(secondaryAction),
|
|
2146
|
+
}, secondaryLabel)
|
|
2147
|
+
: null,
|
|
2069
2148
|
h('button', {
|
|
2070
2149
|
className: 'cf-btn-primary text-white text-sm',
|
|
2071
2150
|
style: { backgroundColor: config.style === 'gradient' ? 'rgba(255,255,255,0.9)' : themeColor, color: config.style === 'gradient' ? themeColor : 'white' },
|
|
2072
2151
|
disabled: config.disabled,
|
|
2073
|
-
onClick:
|
|
2074
|
-
},
|
|
2152
|
+
onClick: () => dispatchAction(config.primary),
|
|
2153
|
+
}, primaryLabel)
|
|
2075
2154
|
)
|
|
2076
2155
|
)
|
|
2077
2156
|
);
|
|
@@ -2746,6 +2825,24 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
2746
2825
|
navigateTo(nextPageId);
|
|
2747
2826
|
}, [currentPageId, routing, formState, pages, addToCart, navigateTo, runValidation]);
|
|
2748
2827
|
|
|
2828
|
+
// --- Field + Navigate (for sticky bar field: dispatch) ---
|
|
2829
|
+
const handleFieldAndNavigate = React.useCallback((fieldId, value) => {
|
|
2830
|
+
const newFormState = { ...formState, [fieldId]: value };
|
|
2831
|
+
setFormState(newFormState);
|
|
2832
|
+
const page = pages[currentPageId];
|
|
2833
|
+
if (page) {
|
|
2834
|
+
const errors = validatePage(page, newFormState, devContext);
|
|
2835
|
+
setValidationErrors(errors);
|
|
2836
|
+
if (errors.length > 0) {
|
|
2837
|
+
const el = document.querySelector('[data-component-id="' + errors[0].componentId + '"]');
|
|
2838
|
+
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
2839
|
+
return;
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
const nextPageId = getNextPage(routing, currentPageId, newFormState, devContext);
|
|
2843
|
+
navigateTo(nextPageId);
|
|
2844
|
+
}, [currentPageId, routing, formState, pages, navigateTo]);
|
|
2845
|
+
|
|
2749
2846
|
// --- Resume prompt ---
|
|
2750
2847
|
if (showResumeModal) {
|
|
2751
2848
|
return h('div', { className: 'cf-resume-backdrop' },
|
|
@@ -3037,7 +3134,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
3037
3134
|
? h(StickyBottomBar, {
|
|
3038
3135
|
config: { ...(catalog.settings?.sticky_bar || {}), ...(page.sticky_bar || {}) },
|
|
3039
3136
|
page, formState, cartItems, themeColor,
|
|
3040
|
-
onNext: handleNext, onAction: handleAction, onBack: handleBack,
|
|
3137
|
+
onNext: handleNext, onAction: handleAction, onFieldAndNavigate: handleFieldAndNavigate, onBack: handleBack,
|
|
3041
3138
|
historyLen: history.length,
|
|
3042
3139
|
})
|
|
3043
3140
|
: null
|
|
@@ -4307,6 +4404,24 @@ async function whoami() {
|
|
|
4307
4404
|
}
|
|
4308
4405
|
|
|
4309
4406
|
// src/index.ts
|
|
4407
|
+
var NODE_MIN = 18;
|
|
4408
|
+
var NODE_MAX = 22;
|
|
4409
|
+
var nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
|
|
4410
|
+
if (nodeMajor < NODE_MIN || nodeMajor > NODE_MAX) {
|
|
4411
|
+
console.error(`
|
|
4412
|
+
\x1B[31m\u2716 Unsupported Node.js version: v${process.versions.node}\x1B[0m`);
|
|
4413
|
+
console.error(` Catalog Kit CLI requires Node.js ${NODE_MIN}\u2013${NODE_MAX} (LTS recommended).`);
|
|
4414
|
+
if (nodeMajor > NODE_MAX) {
|
|
4415
|
+
console.error(` Node ${nodeMajor}+ breaks the tsx ESM loader hooks used to load TypeScript catalog files.`);
|
|
4416
|
+
}
|
|
4417
|
+
console.error(`
|
|
4418
|
+
\x1B[1mFix:\x1B[0m Install a compatible version with NVM (Node Version Manager):`);
|
|
4419
|
+
console.error(` curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash`);
|
|
4420
|
+
console.error(` nvm install 22`);
|
|
4421
|
+
console.error(` nvm use 22
|
|
4422
|
+
`);
|
|
4423
|
+
process.exit(1);
|
|
4424
|
+
}
|
|
4310
4425
|
var __dirname = dirname3(fileURLToPath(import.meta.url));
|
|
4311
4426
|
var { version } = JSON.parse(readFileSync5(join4(__dirname, "../package.json"), "utf-8"));
|
|
4312
4427
|
var program = new Command();
|