@useinsider/guido 2.1.0-beta.7d0f92a → 2.1.0-beta.7ffef92

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 (62) hide show
  1. package/README.md +36 -0
  2. package/dist/@types/config/schemas.js +70 -65
  3. package/dist/components/Guido.vue.js +1 -1
  4. package/dist/components/Guido.vue2.js +69 -58
  5. package/dist/components/organisms/extensions/recommendation/FilterItem.vue.js +11 -13
  6. package/dist/components/organisms/extensions/recommendation/FilterItem.vue2.js +55 -24
  7. package/dist/components/organisms/extensions/recommendation/FilterSelectionDrawer.vue.js +3 -3
  8. package/dist/components/organisms/extensions/recommendation/FilterSelectionDrawer.vue2.js +34 -21
  9. package/dist/components/organisms/extensions/recommendation/Filters.vue.js +9 -9
  10. package/dist/components/organisms/extensions/recommendation/Filters.vue2.js +44 -35
  11. package/dist/composables/useBlocksConfig.js +26 -16
  12. package/dist/composables/useHtmlValidator.js +107 -119
  13. package/dist/config/compiler/utils/recommendationCompilerUtils.js +27 -22
  14. package/dist/config/migrator/itemsBlockMigrator.js +101 -97
  15. package/dist/enums/defaults.js +8 -4
  16. package/dist/extensions/Blocks/Recommendation/block.js +26 -23
  17. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +27 -26
  18. package/dist/extensions/Blocks/Recommendation/constants/layout.js +6 -4
  19. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +12 -10
  20. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +124 -72
  21. package/dist/extensions/Blocks/Recommendation/controls/main/productLayout.js +87 -37
  22. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +138 -117
  23. package/dist/extensions/Blocks/Recommendation/controls/mobileLayout/cssRules.js +21 -0
  24. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +179 -133
  25. package/dist/extensions/Blocks/Recommendation/templates/grid/elementRenderer.js +25 -30
  26. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +27 -30
  27. package/dist/extensions/Blocks/Recommendation/templates/list/elementRenderer.js +20 -25
  28. package/dist/extensions/Blocks/Recommendation/templates/utils.js +43 -31
  29. package/dist/extensions/Blocks/Recommendation/utils/filterUtil.js +8 -8
  30. package/dist/extensions/Blocks/Recommendation/validation/filterSchema.js +29 -0
  31. package/dist/extensions/ModulesTabIcons/extension.js +17 -0
  32. package/dist/guido.css +1 -1
  33. package/dist/node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js +257 -187
  34. package/dist/services/recommendationApi.js +11 -9
  35. package/dist/services/stripoApi.js +20 -17
  36. package/dist/src/@types/config/schemas.d.ts +8 -0
  37. package/dist/src/components/organisms/extensions/recommendation/FilterItem.vue.d.ts +1 -0
  38. package/dist/src/components/organisms/extensions/recommendation/Filters.vue.d.ts +15 -1
  39. package/dist/src/composables/useConfig.d.ts +4 -0
  40. package/dist/src/enums/defaults.d.ts +4 -0
  41. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +2 -2
  42. package/dist/src/extensions/Blocks/Recommendation/constants/layout.d.ts +6 -0
  43. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +4 -0
  44. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +17 -1
  45. package/dist/src/extensions/Blocks/Recommendation/controls/main/productLayout.d.ts +22 -4
  46. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +9 -2
  47. package/dist/src/extensions/Blocks/Recommendation/controls/mobileLayout/cssRules.d.ts +29 -0
  48. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +25 -2
  49. package/dist/src/extensions/Blocks/Recommendation/templates/grid/template.d.ts +1 -1
  50. package/dist/src/extensions/Blocks/Recommendation/templates/index.d.ts +2 -0
  51. package/dist/src/extensions/Blocks/Recommendation/templates/utils.d.ts +12 -3
  52. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +6 -0
  53. package/dist/src/extensions/Blocks/Recommendation/validation/filterSchema.d.ts +15 -0
  54. package/dist/src/extensions/ModulesTabIcons/extension.d.ts +2 -0
  55. package/dist/src/stores/config.d.ts +36 -0
  56. package/dist/static/styles/components/notification.css.js +1 -0
  57. package/dist/static/styles/components/version-history.css.js +10 -2
  58. package/dist/static/styles/components/wide-panel.css.js +18 -2
  59. package/dist/static/styles/customEditorStyle.css.js +10 -9
  60. package/dist/static/templates/empty/index.html.js +74 -0
  61. package/dist/static/templates/empty/style.css.js +779 -0
  62. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
- import { useHttp as a } from "../composables/useHttp.js";
2
- import { URLS as s } from "../enums/extensions/recommendationBlock.js";
3
- const u = () => {
4
- const { get: o } = a();
1
+ import { useHttp as c } from "../composables/useHttp.js";
2
+ import { URLS as m } from "../enums/extensions/recommendationBlock.js";
3
+ const f = () => {
4
+ const { get: o } = c();
5
5
  return {
6
6
  fetchRecommendationCreateData: async () => {
7
7
  try {
@@ -18,13 +18,15 @@ const u = () => {
18
18
  throw console.error("fetchRecommendationFilters error:", e), e;
19
19
  }
20
20
  },
21
- fetchRecommendationProducts: async (e, c) => {
21
+ fetchRecommendationProducts: async (e, a) => {
22
22
  var n;
23
23
  try {
24
- const t = decodeURIComponent(new URLSearchParams(Object.entries(c)).toString());
24
+ const t = decodeURIComponent(new URLSearchParams(Object.entries(a)).toString());
25
25
  console.debug("🏁 Recommendation API Query:", t);
26
- const r = await o(
27
- `${s.RECOMMENDATION_API_URL}/v2/${e}?${t}`
26
+ const { get: s } = c({
27
+ headers: {}
28
+ }), r = await s(
29
+ `${m.RECOMMENDATION_API_URL}/v2/${e}?${t}`
28
30
  );
29
31
  return ((n = r == null ? void 0 : r.data) == null ? void 0 : n.data) ?? [];
30
32
  } catch (t) {
@@ -34,5 +36,5 @@ const u = () => {
34
36
  };
35
37
  };
36
38
  export {
37
- u as useRecommendationApi
39
+ f as useRecommendationApi
38
40
  };
@@ -1,44 +1,47 @@
1
- import { useHttp as m } from "../composables/useHttp.js";
2
- import { useToaster as i } from "../composables/useToaster.js";
3
- import { MAX_DEFAULT_TEMPLATE_ID as p } from "../enums/defaults.js";
4
- const y = () => {
5
- const { get: r } = m(), { handleError: o } = i();
1
+ import { useHttp as l } from "../composables/useHttp.js";
2
+ import { useToaster as u } from "../composables/useToaster.js";
3
+ import { MAX_DEFAULT_TEMPLATE_ID as d } from "../enums/defaults.js";
4
+ import f from "../static/templates/empty/index.html.js";
5
+ import g from "../static/templates/empty/style.css.js";
6
+ const D = () => {
7
+ const { get: o } = l(), { handleError: a } = u();
6
8
  return {
7
9
  getToken: async () => {
8
10
  try {
9
- const t = Number(localStorage.getItem("ins-guido-test-instance")), { data: e } = await r(`/stripo/get-user-token?test=${t}`);
11
+ const t = Number(localStorage.getItem("ins-guido-test-instance")), { data: e } = await o(`/stripo/get-user-token?test=${t}`);
10
12
  return e.body.token;
11
13
  } catch (t) {
12
- return o(t, "Failed to fetch token"), "";
14
+ return a(t, "Failed to fetch token"), "";
13
15
  }
14
16
  },
15
17
  getCustomFonts: async () => {
16
18
  try {
17
- const { data: t = [] } = await r("/stripo/get-partner-custom-fonts");
19
+ const { data: t = [] } = await o("/stripo/get-partner-custom-fonts");
18
20
  return t.map((e) => ({
19
21
  ...e,
20
22
  active: !0
21
23
  }));
22
24
  } catch (t) {
23
- return o(t, "Failed to fetch custom fonts"), [];
25
+ return a(t, "Failed to fetch custom fonts"), [];
24
26
  }
25
27
  },
26
28
  getDefaultTemplate: async () => {
27
29
  const t = {
28
- html: "",
29
- css: ""
30
+ html: f,
31
+ css: g
30
32
  };
31
33
  try {
32
- const c = new URLSearchParams(window.location.search).get("default-template"), s = c ? parseInt(c) : 0, l = s >= 1 && s <= p ? s : 0, { data: n } = await r(
33
- `/stripo/default-template/${l}`
34
- ), a = typeof n == "string" ? JSON.parse(n) : n;
35
- return !a || typeof a != "object" || !("html" in a) || !("css" in a) ? t : a;
34
+ const e = new URLSearchParams(window.location.search), c = e.get("default-template"), p = e.get("master"), s = c ? parseInt(c) : 0, m = s >= 1 && s <= d ? s : 0;
35
+ if (!m && !p)
36
+ return t;
37
+ const i = `/stripo/default-template/${m}`, { data: n } = await o(i), r = typeof n == "string" ? JSON.parse(n) : n;
38
+ return !r || typeof r != "object" || !("html" in r) || !("css" in r) ? t : r;
36
39
  } catch (e) {
37
- return o(e, "Failed to fetch default template"), t;
40
+ return a(e, "Failed to fetch default template"), t;
38
41
  }
39
42
  }
40
43
  };
41
44
  };
42
45
  export {
43
- y as useStripoApi
46
+ D as useStripoApi
44
47
  };
@@ -129,6 +129,10 @@ export declare const EditorSchema: v.ObjectSchema<{
129
129
  readonly senderName: "";
130
130
  readonly subject: "";
131
131
  }>;
132
+ /** Folder name for user-saved modules (used by Stripo plugin panel for path construction) */
133
+ readonly savedModulesFolderName: v.OptionalSchema<v.StringSchema<undefined>, "savedModules">;
134
+ /** Folder name for default/prebuilt modules (used by Stripo plugin panel for path construction) */
135
+ readonly defaultModulesFolderName: v.OptionalSchema<v.StringSchema<undefined>, "defaultModules">;
132
136
  }, undefined>;
133
137
  /**
134
138
  * UI configuration - visual elements and layout
@@ -441,6 +445,10 @@ export declare const GuidoConfigSchema: v.ObjectSchema<{
441
445
  readonly senderName: "";
442
446
  readonly subject: "";
443
447
  }>;
448
+ /** Folder name for user-saved modules (used by Stripo plugin panel for path construction) */
449
+ readonly savedModulesFolderName: v.OptionalSchema<v.StringSchema<undefined>, "savedModules">;
450
+ /** Folder name for default/prebuilt modules (used by Stripo plugin panel for path construction) */
451
+ readonly defaultModulesFolderName: v.OptionalSchema<v.StringSchema<undefined>, "defaultModules">;
444
452
  }, undefined>, {}>;
445
453
  /** UI configuration */
446
454
  readonly ui: v.OptionalSchema<v.ObjectSchema<{
@@ -3,6 +3,7 @@ type __VLS_Props = {
3
3
  filter: Filter;
4
4
  index: number;
5
5
  hasLogicAdapter?: boolean;
6
+ submitted: boolean;
6
7
  };
7
8
  declare const _default: import("vue").DefineComponent<__VLS_TypePropsToOption<__VLS_Props>, {}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {
8
9
  "delete-filter": (filter: Filter) => void;
@@ -1,2 +1,16 @@
1
- declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
1
+ type __VLS_Props = {
2
+ submitted: boolean;
3
+ };
4
+ declare const _default: import("vue").DefineComponent<__VLS_TypePropsToOption<__VLS_Props>, {
5
+ switchToGroup: (groupId: number) => void;
6
+ }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<__VLS_TypePropsToOption<__VLS_Props>>>, {}>;
2
7
  export default _default;
8
+ type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
9
+ type __VLS_TypePropsToOption<T> = {
10
+ [K in keyof T]-?: {} extends Pick<T, K> ? {
11
+ type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
12
+ } : {
13
+ type: import('vue').PropType<T[K]>;
14
+ required: true;
15
+ };
16
+ };
@@ -41,6 +41,8 @@ export declare const useConfig: () => {
41
41
  senderName: string;
42
42
  subject: string;
43
43
  };
44
+ savedModulesFolderName: string;
45
+ defaultModulesFolderName: string;
44
46
  };
45
47
  ui: {
46
48
  showHeader: boolean;
@@ -130,6 +132,8 @@ export declare const useConfig: () => {
130
132
  senderName: string;
131
133
  subject: string;
132
134
  };
135
+ savedModulesFolderName: string;
136
+ defaultModulesFolderName: string;
133
137
  } | null>;
134
138
  ui: import("vue").ComputedRef<{
135
139
  showHeader: boolean;
@@ -9,3 +9,7 @@ export declare const TemplateTypes: {
9
9
  export declare const EditorType: number;
10
10
  export declare const MAX_DEFAULT_TEMPLATE_ID = 13;
11
11
  export declare const ProductIds: Record<string, number>;
12
+ export declare const ModuleFolderDefaults: {
13
+ readonly SAVED_MODULES: "savedModules";
14
+ readonly DEFAULT_MODULES: "defaultModules";
15
+ };
@@ -8,6 +8,6 @@
8
8
  */
9
9
  export { RecommendationBlockId } from './blockIds';
10
10
  export { RecommendationControlId } from './controlIds';
11
- export { CONTAINER_SELECTOR, CURRENCY_ATTR, ATTR_PRODUCT_IMAGE, ATTR_PRODUCT_NAME, ATTR_PRODUCT_PRICE, ATTR_PRODUCT_OLD_PRICE, ATTR_PRODUCT_OMNIBUS_PRICE, ATTR_PRODUCT_OMNIBUS_DISCOUNT, ATTR_PRODUCT_BUTTON, } from './selectors';
12
- export { LAYOUT_VALUES, LAYOUT_OPTIONS, DEFAULT_PRODUCTS_PER_ROW, DEFAULT_CARDS_IN_ROW, MAX_PRODUCT_COUNT, MIN_PRODUCT_COUNT, MAX_PRODUCTS_PER_ROW, MIN_PRODUCTS_PER_ROW, DEFAULT_COLUMN_SPACING, DEFAULT_ROW_SPACING, MIN_SPACING, MAX_SPACING, SPACING_STEP, } from './layout';
11
+ export { CONTAINER_SELECTOR, DESKTOP_CONTAINER_SELECTOR, MOBILE_CONTAINER_SELECTOR, CURRENCY_ATTR, ATTR_PRODUCT_IMAGE, ATTR_PRODUCT_NAME, ATTR_PRODUCT_PRICE, ATTR_PRODUCT_OLD_PRICE, ATTR_PRODUCT_OMNIBUS_PRICE, ATTR_PRODUCT_OMNIBUS_DISCOUNT, ATTR_PRODUCT_BUTTON, } from './selectors';
12
+ export { LAYOUT_VALUES, LAYOUT_OPTIONS, DEFAULT_PRODUCTS_PER_ROW, DEFAULT_CARDS_IN_ROW, DEFAULT_MOBILE_CARDS_IN_ROW, MAX_PRODUCT_COUNT, MIN_PRODUCT_COUNT, MAX_PRODUCTS_PER_ROW, MIN_PRODUCTS_PER_ROW, MAX_MOBILE_PRODUCTS_PER_ROW, MIN_MOBILE_PRODUCTS_PER_ROW, DEFAULT_COLUMN_SPACING, DEFAULT_ROW_SPACING, MIN_SPACING, MAX_SPACING, SPACING_STEP, } from './layout';
13
13
  export { DEFAULT_NODE_CONFIG, DEFAULT_CURRENCY, DEFAULT_COMPOSITION, DEFAULT_VISIBILITY, CURRENT_CONFIG_VERSION, } from './defaultConfig';
@@ -23,6 +23,12 @@ export declare const MIN_PRODUCT_COUNT = 1;
23
23
  export declare const MAX_PRODUCTS_PER_ROW = 4;
24
24
  /** Minimum number of products per row in grid layout */
25
25
  export declare const MIN_PRODUCTS_PER_ROW = 1;
26
+ /** Default number of products per row on mobile */
27
+ export declare const DEFAULT_MOBILE_CARDS_IN_ROW = 1;
28
+ /** Maximum products per row on mobile */
29
+ export declare const MAX_MOBILE_PRODUCTS_PER_ROW = 2;
30
+ /** Minimum products per row on mobile */
31
+ export declare const MIN_MOBILE_PRODUCTS_PER_ROW = 1;
26
32
  /** Default column spacing between product cards (5px on each side = 10px total) */
27
33
  export declare const DEFAULT_COLUMN_SPACING = 10;
28
34
  /** Default row spacing between product rows */
@@ -6,6 +6,10 @@
6
6
  * Used across controls to target elements within the recommendation block
7
7
  */
8
8
  export declare const CONTAINER_SELECTOR = ".ins-recommendation-product-container";
9
+ /** Desktop product container selector */
10
+ export declare const DESKTOP_CONTAINER_SELECTOR = ".ins-recommendation-desktop-container";
11
+ /** Mobile product container selector */
12
+ export declare const MOBILE_CONTAINER_SELECTOR = ".ins-recommendation-mobile-container";
9
13
  /**
10
14
  * Currency HTML attributes set on the block element
11
15
  * These are read by the HTML compiler at runtime to format prices
@@ -16,7 +16,6 @@ export * from './utils';
16
16
  export declare class RecommendationBlockControl extends CommonControl {
17
17
  private store;
18
18
  private storeUnsubscription;
19
- private initializedBlocks;
20
19
  private algorithmControl;
21
20
  private localeControl;
22
21
  private currencyControl;
@@ -38,6 +37,18 @@ export declare class RecommendationBlockControl extends CommonControl {
38
37
  * Each sub-control manages its own form values and event listeners
39
38
  */
40
39
  _initializeSubControls(): void;
40
+ /**
41
+ * Syncs persisted node config into the Pinia store's block state.
42
+ *
43
+ * setCurrentBlock() creates a default entry (USD, en_US, mostPopular).
44
+ * For saved templates, the real config lives in the node (e.g., TRY, tr_TR).
45
+ * This method reads it and patches the store so fetchRecommendationProducts()
46
+ * uses the correct values.
47
+ *
48
+ * Uses triggerRefetch: false because the fetch hasn't happened yet —
49
+ * values are being prepared for the upcoming initial fetch.
50
+ */
51
+ private _syncNodeConfigToStore;
41
52
  /**
42
53
  * Fetches initial data for a block (filters, algorithms, products).
43
54
  * Shared by onRender() and onTemplateNodeUpdated() to avoid duplication.
@@ -65,4 +76,9 @@ export declare class RecommendationBlockControl extends CommonControl {
65
76
  * (e.g., fetchRecommendationCreateData setting preferred currency).
66
77
  */
67
78
  _listenStateUpdates(): void;
79
+ /**
80
+ * Persists the current filter state from Pinia store to the Stripo node config.
81
+ * This ensures filters survive template save/reload cycles.
82
+ */
83
+ private _persistFiltersToNodeConfig;
68
84
  }
@@ -3,9 +3,13 @@
3
3
  *
4
4
  * Handles product display configuration:
5
5
  * - Number of products to show
6
- * - Products per row (hidden for list layout)
6
+ * - Products per row on desktop (hidden for list layout)
7
+ * - Products per row on mobile (hidden for list layout)
7
8
  * - Triggers product row regeneration on changes
8
9
  *
10
+ * Desktop/mobile counters swap visibility based on editor mode
11
+ * (detected via Stripo's EditorStatePropertyType.previewDeviceMode).
12
+ *
9
13
  * Configuration is stored via node config (persists with template).
10
14
  */
11
15
  import type { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
@@ -13,6 +17,7 @@ import { CommonControl } from '../../../common-control';
13
17
  export declare const PRODUCT_LAYOUT_CONTROL_ID = "recommendation-product-layout-control";
14
18
  /**
15
19
  * Control for configuring product count and layout density
20
+ * Includes both desktop and mobile "products per row" counters
16
21
  */
17
22
  export declare class ProductLayoutControl extends CommonControl {
18
23
  private store;
@@ -24,13 +29,26 @@ export declare class ProductLayoutControl extends CommonControl {
24
29
  onDestroy(): void;
25
30
  _setFormValues(): void;
26
31
  /**
27
- * Updates "Products in One Row" visibility based on layout orientation
28
- * This control is hidden for list layout (products always take full width)
29
- * Reads from node config first, falls back to DOM
32
+ * Checks if the editor is currently in mobile preview mode
33
+ * using Stripo's EditorStatePropertyType API.
34
+ */
35
+ _isMobileMode(): boolean;
36
+ /**
37
+ * Updates counter visibility based on layout orientation and editor mode.
38
+ * - List layout: hide both counters (products always full-width)
39
+ * - Grid + desktop mode: show desktop counter, hide mobile counter
40
+ * - Grid + mobile mode: show mobile counter, hide desktop counter
30
41
  */
31
42
  _updateProductsInRowVisibility(): void;
43
+ /**
44
+ * Subscribes to editor preview mode changes via Stripo API.
45
+ * When the user switches between desktop/mobile preview, toggles
46
+ * which "Products in One Row" counter is visible.
47
+ */
48
+ _subscribeToEditorModeChanges(): void;
32
49
  _onProductCountChange(value: string): void;
33
50
  _onProductsInRowChange(value: number): void;
51
+ _onMobileCardsInRowChange(value: number): void;
34
52
  _regenerateProductRows(): void;
35
53
  _debouncedRegenerateProductRows: import("@vueuse/shared").PromisifyFn<() => void>;
36
54
  _listenToFormUpdates(): void;
@@ -58,8 +58,15 @@ export interface RegenerateProductRowsOptions {
58
58
  layout?: Orientation;
59
59
  }
60
60
  /**
61
- * Regenerates product rows based on current store configuration
62
- * Reads products, layout, and composition from store/DOM and rebuilds the HTML
61
+ * Regenerates only the mobile product container rows.
62
+ * Used when mobile-specific settings change (mobileCardsInRow)
63
+ * or when the desktop container is regenerated (to keep both in sync).
64
+ */
65
+ export declare function regenerateMobileProductRows(options: Omit<RegenerateProductRowsOptions, 'afterRegenerate'>): void;
66
+ /**
67
+ * Regenerates product rows in the desktop container based on current store configuration.
68
+ * Also regenerates the mobile container to keep both in sync.
69
+ * Reads products, layout, and composition from store/DOM and rebuilds the HTML.
63
70
  * @param options - Configuration options for regeneration
64
71
  */
65
72
  export declare function regenerateProductRows(options: RegenerateProductRowsOptions): void;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * CSS rules for mobile grid layout visibility switching
3
+ *
4
+ * Follows the modifyCss pattern from textTrim control.
5
+ * Rules are injected into the document's stylesheet via modifyCss
6
+ * so they appear in the compiled HTML export.
7
+ */
8
+ import type { ImmutableCssNode } from '@stripoinc/ui-editor-extensions';
9
+ import { ModificationDescription } from '@stripoinc/ui-editor-extensions';
10
+ /** CSS class for desktop product container */
11
+ export declare const CSS_CLASS_DESKTOP_CONTAINER = "ins-recommendation-desktop-container";
12
+ /** CSS class for mobile product container */
13
+ export declare const CSS_CLASS_MOBILE_CONTAINER = "ins-recommendation-mobile-container";
14
+ interface CssModifierApi {
15
+ getDocumentRootCssNode: () => ImmutableCssNode | null;
16
+ getDocumentModifier: () => {
17
+ modifyCss: (node: ImmutableCssNode) => {
18
+ appendRule: (rule: string) => void;
19
+ };
20
+ apply: (desc: ModificationDescription) => void;
21
+ };
22
+ }
23
+ /**
24
+ * Ensures mobile layout CSS rules exist in the document stylesheet.
25
+ * Only adds rules if they don't already exist (prevents duplicates across multiple blocks).
26
+ * Follows the same pattern as textTrim's _ensureCssRulesExist().
27
+ */
28
+ export declare function ensureMobileCssRulesExist(api: CssModifierApi): void;
29
+ export {};
@@ -29,6 +29,10 @@ interface PerBlockState {
29
29
  filterStatus: boolean;
30
30
  filterSelectionDrawerStatus: boolean;
31
31
  filterGroup: number;
32
+ /** Whether initial API data (filters, algorithms, products) has been fetched for this block */
33
+ isInitialized: boolean;
34
+ /** Snapshot of filters taken when the drawer opens, used to revert on cancel */
35
+ filterSnapshot: Filter[] | null;
32
36
  }
33
37
  interface StoreState {
34
38
  recommendationCampaignUrls: Record<string, string>;
@@ -123,7 +127,9 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
123
127
  configVersion: number;
124
128
  } & import("pinia").PiniaCustomStateProperties<StoreState>): number;
125
129
  hasFilters(): boolean;
130
+ hasValidFilters(): boolean;
126
131
  getFilterGroupCount(): number;
132
+ getUniqueFilterGroups(): number[];
127
133
  getActivePredictiveAlgorithms: (state: {
128
134
  recommendationCampaignUrls: Record<string, string>;
129
135
  activePredictiveAlgorithms: number[];
@@ -189,6 +195,11 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
189
195
  * Resets currentRecommendationId if it was the deleted block.
190
196
  */
191
197
  removeBlockState(id: number): void;
198
+ /**
199
+ * Marks a block as initialized (initial API data has been fetched).
200
+ * Automatically cleaned up when removeBlockState deletes the block entry.
201
+ */
202
+ markBlockInitialized(id: number): void;
192
203
  /**
193
204
  * Patches the current block's recommendationConfigs.
194
205
  * Replaces `store.$patch({ recommendationConfigs: { ... } })` pattern.
@@ -196,19 +207,31 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
196
207
  patchCurrentBlockConfig(updates: Partial<PerBlockConfigs>, options?: {
197
208
  triggerRefetch?: boolean;
198
209
  }): void;
210
+ /**
211
+ * Creates a filter with the first available attribute and operator pre-selected.
212
+ */
213
+ createDefaultFilter(filterGroup: number, filterNumber: number): Filter;
199
214
  /**
200
215
  * Opens the filter selection drawer for the current block.
201
- * If no filters exist, initializes with a default empty filter
216
+ * Saves a snapshot of current filters for cancel/revert.
217
+ * If no filters exist, initializes with a default filter
202
218
  * so the user has a starting point for input.
203
219
  */
204
220
  openFilterDrawer(): void;
205
221
  /**
206
- * Closes the filter selection drawer for the current block
222
+ * Closes the filter selection drawer for the current block.
223
+ * Called after successful apply — discards the snapshot.
207
224
  */
208
225
  closeFilterDrawer(): void;
226
+ /**
227
+ * Cancels the filter selection drawer and reverts filters
228
+ * to the snapshot taken when the drawer was opened.
229
+ */
230
+ cancelFilterDrawer(): void;
209
231
  fetchRecommendationCreateData(): Promise<void>;
210
232
  fetchRecommendationFilters(): Promise<void>;
211
233
  addFilterGroup(filterGroup: number): void;
234
+ deleteFilterGroup(groupId: number): void;
212
235
  updateFilter(updatedFilter: Filter): void;
213
236
  deleteFilter(filter: Filter): void;
214
237
  addFilter(filter: Filter): void;
@@ -30,4 +30,4 @@ export declare function prepareGridProductRows(products: RecommendationProduct[]
30
30
  */
31
31
  export declare function prepareProductRows(products: RecommendationProduct[], productsPerRow: number, composition?: string[]): string;
32
32
  export declare function getDefaultTemplate(recommendationId?: number): string;
33
- export declare function generateBlockTemplate(products: RecommendationProduct[], productsPerRow: number, title?: string, composition?: string[]): string;
33
+ export declare function generateBlockTemplate(products: RecommendationProduct[], productsPerRow: number, title?: string, composition?: string[], mobileProductsPerRow?: number): string;
@@ -24,12 +24,14 @@ export { DEFAULTS, DEFAULT_CARD_COMPOSITION, DEFAULT_CARD_VISIBILITY, getDefault
24
24
  export declare function prepareProductRows(products: RecommendationProduct[], layout: Orientation, options?: PrepareProductRowsOptions): string;
25
25
  /**
26
26
  * Generates the default template for a given layout.
27
+ * Includes both desktop and mobile product containers for grid layouts.
27
28
  * @param layout - The layout orientation ('list' or 'grid')
28
29
  * @returns Complete HTML template with default products
29
30
  */
30
31
  export declare function getDefaultTemplate(layout?: Orientation): string;
31
32
  /**
32
33
  * Generates a complete block template with custom products.
34
+ * Includes both desktop and mobile product containers for grid layouts.
33
35
  * @param products - Array of products to display
34
36
  * @param layout - The layout orientation ('list' or 'grid')
35
37
  * @param title - Title for the block
@@ -7,6 +7,8 @@ export type Orientation = 'list' | 'grid';
7
7
  export interface PrepareProductRowsOptions {
8
8
  /** Number of products per row (only for grid layout, defaults to 3) */
9
9
  productsPerRow?: number;
10
+ /** Number of products per row on mobile (only for grid layout, defaults to 1) */
11
+ mobileProductsPerRow?: number;
10
12
  /** Array defining order of card elements */
11
13
  composition?: string[];
12
14
  }
@@ -44,14 +46,21 @@ export declare const PLACEHOLDER_IMAGE = "https://email-static.useinsider.com/st
44
46
  * @returns Sanitized HTTPS URL or placeholder
45
47
  */
46
48
  export declare function sanitizeImageUrl(url: string | undefined | null): string;
47
- export declare function getDefaultProducts(): RecommendationProduct[];
49
+ export declare function getDefaultProducts(count?: number): RecommendationProduct[];
48
50
  /**
49
51
  * Creates the block template wrapper HTML for recommendation blocks.
50
- * The template includes title placeholder and product container.
52
+ * The template includes title placeholder and both desktop/mobile product containers.
53
+ *
54
+ * The dual-container approach generates two `ins-recommendation-product-container` tables:
55
+ * - Desktop container: visible by default, hidden on mobile via CSS media query
56
+ * - Mobile container: hidden by default (`display: none`), shown on mobile
57
+ *
58
+ * Both share the `.ins-recommendation-product-container` class so existing selectors
59
+ * (spacing, background color, compiler rules) apply automatically to both.
51
60
  * @param layout - The layout orientation ('list' or 'grid')
52
61
  * @param instanceClass - Optional instance-specific class (e.g., 'ins-recommendation-v3-block-1')
53
62
  * for CSS scoping per block instance. Must be included in the initial template HTML because
54
63
  * Stripo's Angular rendering manages the class attribute and overwrites dynamic setAttribute changes.
55
- * @returns HTML template string with {-{-TITLE-}-} and {-{-PRODUCT_ROWS-}-} placeholders
64
+ * @returns HTML template string with {-{-TITLE-}-}, {-{-PRODUCT_ROWS-}-}, and {-{-MOBILE_PRODUCT_ROWS-}-} placeholders
56
65
  */
57
66
  export declare function createBlockTemplate(layout?: Orientation, instanceClass?: string): string;
@@ -92,6 +92,12 @@ export interface RecommendationNodeConfig {
92
92
  * Number of products per row (grid layout only)
93
93
  */
94
94
  cardsInRow: number;
95
+ /**
96
+ * Number of products per row on mobile (grid layout only)
97
+ * Only applicable when layout is 'grid'
98
+ * @default 1
99
+ */
100
+ mobileCardsInRow: number;
95
101
  /**
96
102
  * Horizontal spacing between product cards in pixels (grid layout only)
97
103
  */
@@ -0,0 +1,15 @@
1
+ import type { Filter } from '@@/Types/recommendation';
2
+ import * as v from 'valibot';
3
+ export declare const FilterSchema: v.ObjectSchema<{
4
+ readonly type: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
5
+ readonly attribute: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
6
+ readonly operator: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
7
+ readonly innerGroupOperator: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
8
+ readonly outerGroupOperator: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
9
+ readonly value: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
10
+ readonly filterGroup: v.NumberSchema<undefined>;
11
+ readonly filterNumber: v.NumberSchema<undefined>;
12
+ readonly isValid: v.BooleanSchema<undefined>;
13
+ }, undefined>;
14
+ export declare function isFilterValid(filter: Filter): boolean;
15
+ export declare function getInvalidFilterFields(filter: Filter): Set<string>;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("@stripoinc/ui-editor-extensions").Extension;
2
+ export default _default;