@officexapp/catalogs-cli 0.4.0 → 0.4.1

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.
Files changed (2) hide show
  1. package/dist/index.js +44 -10
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1275,8 +1275,8 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
1275
1275
  }
1276
1276
 
1277
1277
  // --- Component Renderers ---
1278
- function RenderComponent({ comp, isCover, formState, onFieldChange }) {
1279
- const props = comp.props || {};
1278
+ function RenderComponent({ comp, isCover, formState, onFieldChange, onSubmit, propOverrides }) {
1279
+ const props = { ...(comp.props || {}), ...(propOverrides?.[comp.id] || {}) };
1280
1280
  const type = comp.type;
1281
1281
  const compClass = comp.className || '';
1282
1282
  const compStyle = comp.style || {};
@@ -1452,7 +1452,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
1452
1452
  case 'url':
1453
1453
  case 'number':
1454
1454
  case 'password':
1455
- return h(TextInput, { comp, type, formState, onFieldChange, isCover, compClass, compStyle });
1455
+ return h(TextInput, { comp, type, formState, onFieldChange, isCover, compClass, compStyle, onSubmit });
1456
1456
 
1457
1457
  case 'long_text':
1458
1458
  return h('div', { className: 'space-y-1.5 ' + compClass, style: compStyle },
@@ -1723,7 +1723,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
1723
1723
  );
1724
1724
  }
1725
1725
 
1726
- function TextInput({ comp, type, formState, onFieldChange, isCover, compClass, compStyle }) {
1726
+ function TextInput({ comp, type, formState, onFieldChange, isCover, compClass, compStyle, onSubmit }) {
1727
1727
  const props = comp.props || {};
1728
1728
  const inputType = type === 'email' ? 'email' : type === 'phone' ? 'tel' : type === 'url' ? 'url' : type === 'number' ? 'number' : type === 'password' ? 'password' : 'text';
1729
1729
  return h('div', { className: 'space-y-1.5 ' + compClass, style: compStyle },
@@ -1739,6 +1739,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
1739
1739
  placeholder: props.placeholder || '',
1740
1740
  value: formState[comp.id] ?? '',
1741
1741
  onChange: (e) => onFieldChange(comp.id, e.target.value),
1742
+ onKeyDown: (e) => { if (e.key === 'Enter' && onSubmit) { e.preventDefault(); onSubmit(); } },
1742
1743
  })
1743
1744
  );
1744
1745
  }
@@ -2414,6 +2415,8 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2414
2415
  const historyRef = React.useRef(history);
2415
2416
  historyRef.current = history;
2416
2417
  const autoAdvanceTimer = React.useRef(null);
2418
+ const globalsRef = React.useRef({});
2419
+ const [compPropOverrides, setCompPropOverrides] = React.useState({});
2417
2420
 
2418
2421
  // --- Cart logic ---
2419
2422
  const addToCart = React.useCallback((pageId) => {
@@ -2557,7 +2560,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2557
2560
  getField: (id) => formStateRef.current[id],
2558
2561
  getAllFields: () => ({ ...formStateRef.current }),
2559
2562
  getPageId: () => currentPageId,
2560
- setField: (id, value) => setFormState(prev => ({ ...prev, [id]: value })),
2563
+ setField: (id, value) => onFieldChangeRef.current?.(id, value),
2561
2564
  goNext: () => handleNextRef.current?.(),
2562
2565
  goBack: () => handleBackRef.current?.(),
2563
2566
  on: (event, cb) => { if (!listeners[event]) listeners[event] = new Set(); listeners[event].add(cb); },
@@ -2565,6 +2568,11 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2565
2568
  openCart: () => setCartOpen(true),
2566
2569
  closeCart: () => setCartOpen(false),
2567
2570
  getCartItems: () => [...cartItems],
2571
+ getGlobal: (key) => globalsRef.current[key],
2572
+ setGlobal: (key, value) => { globalsRef.current[key] = value; },
2573
+ setComponentProp: (id, prop, value) => {
2574
+ setCompPropOverrides(prev => ({ ...prev, [id]: { ...(prev[id] || {}), [prop]: value } }));
2575
+ },
2568
2576
  setValidationError: (id, msg) => {
2569
2577
  setValidationErrors(prev => {
2570
2578
  const next = prev.filter(e => e.componentId !== id);
@@ -2573,7 +2581,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2573
2581
  });
2574
2582
  },
2575
2583
  };
2576
- window.CatalogKit = { get: () => instance, getField: instance.getField, setField: instance.setField, getPageId: instance.getPageId, goNext: instance.goNext, goBack: instance.goBack, on: instance.on, off: instance.off };
2584
+ window.CatalogKit = { get: () => instance, getField: instance.getField, setField: instance.setField, getPageId: instance.getPageId, goNext: instance.goNext, goBack: instance.goBack, on: instance.on, off: instance.off, setGlobal: instance.setGlobal, getGlobal: instance.getGlobal, setComponentProp: instance.setComponentProp };
2577
2585
  window.__catalogKitListeners = listeners;
2578
2586
  return () => { delete window.CatalogKit; delete window.__catalogKitListeners; };
2579
2587
  }, []);
@@ -2615,9 +2623,11 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2615
2623
  const onFieldChange = React.useCallback((id, value) => {
2616
2624
  setFormState(prev => ({ ...prev, [id]: value }));
2617
2625
  devEvents.emit('field_change', { field_id: id, value, page_id: currentPageId });
2618
- // Fire CatalogKit fieldchange
2626
+ // Fire CatalogKit fieldchange (unscoped + scoped)
2619
2627
  const ckListeners = window.__catalogKitListeners || {};
2620
- const fcSet = ckListeners['fieldchange']; if (fcSet?.size) for (const cb of fcSet) { try { cb({ fieldId: id, value, pageId: currentPageId }); } catch {} }
2628
+ const fcPayload = { fieldId: id, value, pageId: currentPageId };
2629
+ const fcSet = ckListeners['fieldchange']; if (fcSet?.size) for (const cb of fcSet) { try { cb(fcPayload); } catch {} }
2630
+ const scopedSet = ckListeners['fieldchange:' + id]; if (scopedSet?.size) for (const cb of scopedSet) { try { cb(fcPayload); } catch {} }
2621
2631
 
2622
2632
  // Auto-advance: if page has auto_advance and this is a selection-type input
2623
2633
  const pg = pages[currentPageId];
@@ -2647,6 +2657,8 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2647
2657
  }
2648
2658
  }
2649
2659
  }, [currentPageId, pages, formState, routing, navigateTo]);
2660
+ const onFieldChangeRef = React.useRef(onFieldChange);
2661
+ onFieldChangeRef.current = onFieldChange;
2650
2662
 
2651
2663
  // --- Validation ---
2652
2664
  const runValidation = React.useCallback(() => {
@@ -2684,6 +2696,21 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2684
2696
  if (currentPage?.offer) {
2685
2697
  if (!currentPage.offer.accept_field) addToCart(currentPageId);
2686
2698
  }
2699
+ // Fire CatalogKit beforenext event (scoped: "beforenext" + "beforenext:<pageId>")
2700
+ let prevented = false;
2701
+ let nextPageOverride;
2702
+ const beforeNextPayload = {
2703
+ pageId: currentPageId,
2704
+ preventDefault: () => { prevented = true; },
2705
+ setNextPage: (id) => { nextPageOverride = id; },
2706
+ };
2707
+ const ckListeners = window.__catalogKitListeners || {};
2708
+ for (const key of ['beforenext', 'beforenext:' + currentPageId]) {
2709
+ const set = ckListeners[key]; if (!set?.size) continue;
2710
+ for (const cb of set) { try { cb(beforeNextPayload); } catch (e) { console.error('[CatalogKit]', key, e); } }
2711
+ }
2712
+ if (prevented) return;
2713
+ if (nextPageOverride !== undefined) { navigateTo(nextPageOverride); return; }
2687
2714
  const nextId = getNextPage(routing, currentPageId, formState, devContext);
2688
2715
  navigateTo(nextId);
2689
2716
  }, [currentPageId, routing, formState, pages, addToCart, navigateTo, runValidation]);
@@ -2879,6 +2906,11 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2879
2906
  if (c.prefill_mode === 'hidden' && formState[c.id] != null && formState[c.id] !== '') return false;
2880
2907
  return true;
2881
2908
  });
2909
+ // Find the last input component for Enter-to-submit
2910
+ const inputTypes = ['short_text', 'long_text', 'email', 'phone', 'url', 'number', 'password', 'currency', 'date', 'datetime', 'time', 'address'];
2911
+ const lastInputComp = [...components].reverse().find(c => inputTypes.includes(c.type));
2912
+ const lastInputId = lastInputComp?.id;
2913
+
2882
2914
  const bgImage = page.background_image || catalog.settings?.theme?.background_image;
2883
2915
 
2884
2916
  // Cart UI (shared between cover and standard)
@@ -2910,8 +2942,9 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2910
2942
  );
2911
2943
  }
2912
2944
  const fieldError = validationErrors.find(e => e.componentId === comp.id);
2945
+ const submitHandler = comp.id === lastInputId ? handleNext : undefined;
2913
2946
  return h('div', { key: comp.id || i, 'data-component-id': comp.id, 'data-component-type': comp.type, className: fieldError ? 'cf-field-error' : '' },
2914
- h(RenderComponent, { comp, isCover: true, formState, onFieldChange }),
2947
+ h(RenderComponent, { comp, isCover: true, formState, onFieldChange, onSubmit: submitHandler, propOverrides: compPropOverrides }),
2915
2948
  fieldError ? h('p', { className: 'text-xs text-red-300 font-medium mt-1', role: 'alert' }, fieldError.message) : null
2916
2949
  );
2917
2950
  }),
@@ -2972,8 +3005,9 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2972
3005
  );
2973
3006
  }
2974
3007
  const fieldError = validationErrors.find(e => e.componentId === comp.id);
3008
+ const submitHandler = comp.id === lastInputId ? handleNext : undefined;
2975
3009
  return h('div', { key: comp.id || i, 'data-component-id': comp.id, 'data-component-type': comp.type, className: fieldError ? 'cf-field-error' : '' },
2976
- h(RenderComponent, { comp, isCover: false, formState, onFieldChange }),
3010
+ h(RenderComponent, { comp, isCover: false, formState, onFieldChange, onSubmit: submitHandler, propOverrides: compPropOverrides }),
2977
3011
  fieldError ? h('p', { className: 'text-xs text-red-500 font-medium mt-1', role: 'alert' }, fieldError.message) : null
2978
3012
  );
2979
3013
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@officexapp/catalogs-cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "CLI for Catalog Kit — upload videos, push catalogs, manage assets",
5
5
  "type": "module",
6
6
  "bin": {