@useinsider/guido 3.0.0 → 3.1.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.
Files changed (101) hide show
  1. package/dist/components/organisms/header/EditorActions.vue.js +10 -8
  2. package/dist/components/organisms/header/EditorActions.vue2.js +41 -31
  3. package/dist/components/organisms/header/MigrationConfirmModal.vue.js +17 -0
  4. package/dist/components/organisms/header/MigrationConfirmModal.vue2.js +39 -0
  5. package/dist/components/organisms/onboarding/GenericOnboarding.vue.js +1 -1
  6. package/dist/components/organisms/onboarding/GenericOnboarding.vue2.js +1 -1
  7. package/dist/components/organisms/onboarding/TextBlockOnboarding.vue.js +1 -1
  8. package/dist/components/organisms/onboarding/TextBlockOnboarding.vue2.js +2 -2
  9. package/dist/config/compiler/unsubscribeCompilerRules.js +14 -14
  10. package/dist/config/compiler/utils/recommendationCompilerUtils.js +29 -18
  11. package/dist/config/i18n/en/labels.json.js +8 -3
  12. package/dist/config/migrator/itemsBlockMigrator.js +135 -131
  13. package/dist/config/migrator/recommendationMigrator.js +58 -54
  14. package/dist/enums/block.js +4 -0
  15. package/dist/extensions/Blocks/Items/block.js +30 -21
  16. package/dist/extensions/Blocks/Items/iconsRegistry.js +7 -6
  17. package/dist/extensions/Blocks/Items/items.css.js +48 -0
  18. package/dist/extensions/Blocks/Recommendation/block.js +64 -34
  19. package/dist/extensions/Blocks/Recommendation/constants/blockIds.js +1 -1
  20. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  21. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +36 -34
  22. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +15 -12
  23. package/dist/extensions/Blocks/Recommendation/controls/cardBackground/index.js +4 -4
  24. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +693 -144
  25. package/dist/extensions/Blocks/Recommendation/controls/customAttribute/index.js +78 -0
  26. package/dist/extensions/Blocks/Recommendation/controls/main/algorithm.js +15 -15
  27. package/dist/extensions/Blocks/Recommendation/controls/main/currency.js +24 -24
  28. package/dist/extensions/Blocks/Recommendation/controls/main/filters.js +2 -2
  29. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +107 -78
  30. package/dist/extensions/Blocks/Recommendation/controls/{layout/index.js → main/layoutOrientation.js} +34 -23
  31. package/dist/extensions/Blocks/Recommendation/controls/main/locale.js +2 -2
  32. package/dist/extensions/Blocks/Recommendation/controls/main/productCount.js +58 -0
  33. package/dist/extensions/Blocks/Recommendation/controls/main/productLayout.js +150 -64
  34. package/dist/extensions/Blocks/Recommendation/controls/main/shuffle.js +2 -2
  35. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +202 -200
  36. package/dist/extensions/Blocks/Recommendation/controls/mobileLayout/cssRules.js +25 -8
  37. package/dist/extensions/Blocks/Recommendation/controls/name/textTrim.js +6 -5
  38. package/dist/extensions/Blocks/Recommendation/controls/omnibusDiscount/textAfter.js +8 -8
  39. package/dist/extensions/Blocks/Recommendation/controls/omnibusDiscount/textBefore.js +21 -21
  40. package/dist/extensions/Blocks/Recommendation/controls/omnibusPrice/textAfter.js +13 -13
  41. package/dist/extensions/Blocks/Recommendation/controls/omnibusPrice/textBefore.js +17 -17
  42. package/dist/extensions/Blocks/Recommendation/controls/spacing/index.js +94 -100
  43. package/dist/extensions/Blocks/Recommendation/controls/syncInfoMessage.js +65 -0
  44. package/dist/extensions/Blocks/Recommendation/extension.js +20 -18
  45. package/dist/extensions/Blocks/Recommendation/iconsRegistry.js +5 -4
  46. package/dist/extensions/Blocks/Recommendation/recommendation.css.js +209 -2
  47. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +135 -111
  48. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +9 -7
  49. package/dist/extensions/Blocks/Recommendation/templates/grid/elementRenderer.js +63 -34
  50. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +25 -28
  51. package/dist/extensions/Blocks/Recommendation/templates/index.js +8 -8
  52. package/dist/extensions/Blocks/Recommendation/templates/list/elementRenderer.js +28 -13
  53. package/dist/extensions/Blocks/Recommendation/templates/list/template.js +25 -44
  54. package/dist/extensions/Blocks/Recommendation/templates/utils.js +62 -38
  55. package/dist/extensions/Blocks/common-control.js +96 -39
  56. package/dist/guido.css +1 -1
  57. package/dist/src/@types/extensions/block.d.ts +2 -0
  58. package/dist/src/App.vue.d.ts +3 -1
  59. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -1
  60. package/dist/src/components/organisms/header/MigrationConfirmModal.vue.d.ts +6 -0
  61. package/dist/src/components/wrappers/WpModal.vue.d.ts +2 -2
  62. package/dist/src/enums/block.d.ts +4 -0
  63. package/dist/src/extensions/Blocks/Items/block.d.ts +3 -1
  64. package/dist/src/extensions/Blocks/Recommendation/block.d.ts +4 -1
  65. package/dist/src/extensions/Blocks/Recommendation/constants/blockIds.d.ts +2 -1
  66. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +9 -1
  67. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  68. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +10 -0
  69. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +134 -44
  70. package/dist/src/extensions/Blocks/Recommendation/controls/customAttribute/index.d.ts +105 -0
  71. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +3 -2
  72. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +5 -1
  73. package/dist/src/extensions/Blocks/Recommendation/controls/{layout/index.d.ts → main/layoutOrientation.d.ts} +3 -3
  74. package/dist/src/extensions/Blocks/Recommendation/controls/main/productCount.d.ts +28 -0
  75. package/dist/src/extensions/Blocks/Recommendation/controls/main/productLayout.d.ts +38 -20
  76. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +6 -2
  77. package/dist/src/extensions/Blocks/Recommendation/controls/mobileLayout/cssRules.d.ts +23 -1
  78. package/dist/src/extensions/Blocks/Recommendation/controls/spacing/index.d.ts +8 -18
  79. package/dist/src/extensions/Blocks/Recommendation/controls/syncInfoMessage.d.ts +34 -0
  80. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +2 -0
  81. package/dist/src/extensions/Blocks/Recommendation/templates/grid/elementRenderer.d.ts +1 -1
  82. package/dist/src/extensions/Blocks/Recommendation/templates/list/elementRenderer.d.ts +1 -1
  83. package/dist/src/extensions/Blocks/Recommendation/templates/list/template.d.ts +10 -4
  84. package/dist/src/extensions/Blocks/Recommendation/templates/utils.d.ts +37 -2
  85. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +13 -0
  86. package/dist/src/extensions/Blocks/common-control.d.ts +29 -2
  87. package/dist/src/stores/template.d.ts +29 -0
  88. package/dist/src/utils/migrationBannerHtml.d.ts +2 -0
  89. package/dist/static/assets/info.svg.js +5 -0
  90. package/dist/static/styles/components/wide-panel.css.js +1 -0
  91. package/dist/static/styles/customEditorStyle.css.js +9 -0
  92. package/dist/static/styles/variables.css.js +3 -0
  93. package/dist/stores/template.js +15 -0
  94. package/dist/utils/migrationBannerHtml.js +21 -0
  95. package/package.json +3 -2
  96. package/dist/src/extensions/Blocks/Recommendation/controls/cardBackgroundColorControl.d.ts +0 -25
  97. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusDiscountTextAfterControl.d.ts +0 -15
  98. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusDiscountTextBeforeControl.d.ts +0 -15
  99. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusPriceTextAfterControl.d.ts +0 -15
  100. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusPriceTextBeforeControl.d.ts +0 -15
  101. package/dist/src/extensions/Blocks/Recommendation/controls/spacingControl.d.ts +0 -60
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Custom Attribute Element Controls
3
+ *
4
+ * Controls for styling custom product attribute elements in recommendation cards.
5
+ *
6
+ * Unlike built-in elements (Name, Price, etc.) where all instances share a single
7
+ * block ID and should be styled uniformly, custom attributes are conceptually
8
+ * different elements (e.g., "brand" vs "rating_star") that need independent styling.
9
+ *
10
+ * Each custom attribute element carries a unique `product-attr` HTML attribute
11
+ * (e.g., `product-attr="product_attribute.brand"`). The controls below use this
12
+ * attribute to scope style changes to only the same attribute type across all
13
+ * product cards — changing "brand" color won't affect "rating_star" color.
14
+ */
15
+ import { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
16
+ /**
17
+ * Grouped Custom Attribute Controls
18
+ * Use this object for cleaner imports in extension.ts
19
+ */
20
+ export declare const CustomAttributeControls: {
21
+ readonly align: {
22
+ new (): {
23
+ getId(): string;
24
+ getTargetNodes(root: ImmutableHtmlNode): ImmutableHtmlNode[];
25
+ getParentControlId(): string;
26
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
27
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
28
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
29
+ getAdditionalModifications(_root: ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
30
+ isVisible(_node: ImmutableHtmlNode): boolean;
31
+ };
32
+ };
33
+ readonly color: {
34
+ new (): {
35
+ getId(): string;
36
+ getTargetNodes(root: ImmutableHtmlNode): ImmutableHtmlNode[];
37
+ getParentControlId(): string;
38
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
39
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
40
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
41
+ getAdditionalModifications(_root: ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
42
+ isVisible(_node: ImmutableHtmlNode): boolean;
43
+ };
44
+ };
45
+ readonly size: {
46
+ new (): {
47
+ getId(): string;
48
+ getTargetNodes(root: ImmutableHtmlNode): ImmutableHtmlNode[];
49
+ getParentControlId(): string;
50
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
51
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
52
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
53
+ getAdditionalModifications(_root: ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
54
+ isVisible(_node: ImmutableHtmlNode): boolean;
55
+ };
56
+ };
57
+ readonly style: {
58
+ new (): {
59
+ getId(): string;
60
+ getTargetNodes(root: ImmutableHtmlNode): ImmutableHtmlNode[];
61
+ getParentControlId(): string;
62
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
63
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
64
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
65
+ getAdditionalModifications(_root: ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
66
+ isVisible(_node: ImmutableHtmlNode): boolean;
67
+ };
68
+ };
69
+ readonly fontFamily: {
70
+ new (): {
71
+ getId(): string;
72
+ getTargetNodes(root: ImmutableHtmlNode): ImmutableHtmlNode[];
73
+ getParentControlId(): string;
74
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
75
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
76
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
77
+ getAdditionalModifications(_root: ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
78
+ isVisible(_node: ImmutableHtmlNode): boolean;
79
+ };
80
+ };
81
+ readonly background: {
82
+ new (): {
83
+ getId(): string;
84
+ getTargetNodes(root: ImmutableHtmlNode): ImmutableHtmlNode[];
85
+ getParentControlId(): string;
86
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
87
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
88
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
89
+ getAdditionalModifications(_root: ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
90
+ isVisible(_node: ImmutableHtmlNode): boolean;
91
+ };
92
+ };
93
+ readonly paddings: {
94
+ new (): {
95
+ getId(): string;
96
+ getTargetNodes(root: ImmutableHtmlNode): ImmutableHtmlNode[];
97
+ getParentControlId(): string;
98
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
99
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
100
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
101
+ getAdditionalModifications(_root: ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
102
+ isVisible(_node: ImmutableHtmlNode): boolean;
103
+ };
104
+ };
105
+ };
@@ -9,7 +9,7 @@
9
9
  * // Or individual imports
10
10
  * import { NameAlignControl, ButtonColorControl } from './controls';
11
11
  */
12
- export { RecommendationBlockControl, CONTROL_BLOCK_ID, AlgorithmControl, ALGORITHM_CONTROL_ID, LocaleControl, LOCALE_CONTROL_ID, CurrencyControl, CURRENCY_CONTROL_ID, ProductLayoutControl, PRODUCT_LAYOUT_CONTROL_ID, FiltersControl, FILTERS_CONTROL_ID, ShuffleControl, SHUFFLE_CONTROL_ID, } from './main';
12
+ export { RecommendationBlockControl, CONTROL_BLOCK_ID, AlgorithmControl, ALGORITHM_CONTROL_ID, LocaleControl, LOCALE_CONTROL_ID, CurrencyControl, CURRENCY_CONTROL_ID, ProductCountControl, PRODUCT_COUNT_CONTROL_ID, ProductLayoutControl, PRODUCT_LAYOUT_CONTROL_ID, FiltersControl, FILTERS_CONTROL_ID, ShuffleControl, SHUFFLE_CONTROL_ID, LayoutOrientationControl, LAYOUT_ORIENTATION_CONTROL_ID, } from './main';
13
13
  export * from './main/utils';
14
14
  export { NameControls } from './name';
15
15
  export { PriceControls } from './price';
@@ -18,7 +18,8 @@ export { OmnibusPriceControls } from './omnibusPrice';
18
18
  export { OmnibusDiscountControls } from './omnibusDiscount';
19
19
  export { ButtonControls } from './button';
20
20
  export { ImageControls } from './image';
21
+ export { CustomAttributeControls } from './customAttribute';
21
22
  export { SpacingControl, SPACING_CONTROL_ID } from './spacing';
22
23
  export { CardBackgroundColorControl, CARD_BACKGROUND_COLOR_CONTROL_ID } from './cardBackground';
23
- export { LayoutControl, LAYOUT_CONTROL_ID } from './layout';
24
24
  export { RecommendationCardCompositionControl, COMPOSITION_CONTROL_BLOCK_ID } from './cardComposition';
25
+ export { SyncInfoMessageControl, SYNC_INFO_MESSAGE_CONTROL_ID } from './syncInfoMessage';
@@ -3,11 +3,13 @@ import { CommonControl } from '../../../common-control';
3
3
  import { AlgorithmControl, ALGORITHM_CONTROL_ID } from './algorithm';
4
4
  import { CurrencyControl, CURRENCY_CONTROL_ID } from './currency';
5
5
  import { FiltersControl, FILTERS_CONTROL_ID } from './filters';
6
+ import { LayoutOrientationControl, LAYOUT_ORIENTATION_CONTROL_ID } from './layoutOrientation';
6
7
  import { LocaleControl, LOCALE_CONTROL_ID } from './locale';
8
+ import { ProductCountControl, PRODUCT_COUNT_CONTROL_ID } from './productCount';
7
9
  import { ProductLayoutControl, PRODUCT_LAYOUT_CONTROL_ID } from './productLayout';
8
10
  import { ShuffleControl, SHUFFLE_CONTROL_ID } from './shuffle';
9
11
  export declare const CONTROL_BLOCK_ID = "ui-elements-recommendation-block";
10
- export { AlgorithmControl, ALGORITHM_CONTROL_ID, LocaleControl, LOCALE_CONTROL_ID, CurrencyControl, CURRENCY_CONTROL_ID, ProductLayoutControl, PRODUCT_LAYOUT_CONTROL_ID, FiltersControl, FILTERS_CONTROL_ID, ShuffleControl, SHUFFLE_CONTROL_ID, };
12
+ export { AlgorithmControl, ALGORITHM_CONTROL_ID, LocaleControl, LOCALE_CONTROL_ID, CurrencyControl, CURRENCY_CONTROL_ID, ProductCountControl, PRODUCT_COUNT_CONTROL_ID, ProductLayoutControl, PRODUCT_LAYOUT_CONTROL_ID, FiltersControl, FILTERS_CONTROL_ID, ShuffleControl, SHUFFLE_CONTROL_ID, LayoutOrientationControl, LAYOUT_ORIENTATION_CONTROL_ID, };
11
13
  export * from './utils';
12
14
  /**
13
15
  * Main recommendation block control that composes all sub-controls
@@ -19,9 +21,11 @@ export declare class RecommendationBlockControl extends CommonControl {
19
21
  private algorithmControl;
20
22
  private localeControl;
21
23
  private currencyControl;
24
+ private productCountControl;
22
25
  private productLayoutControl;
23
26
  private filtersControl;
24
27
  private shuffleControl;
28
+ private layoutOrientationControl;
25
29
  getId(): string;
26
30
  getTemplate(): string;
27
31
  onRender(): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Layout Control
2
+ * Layout Orientation Control
3
3
  *
4
4
  * Manages recommendation block layout orientation
5
5
  * Allows switching between grid and list layouts
@@ -9,12 +9,12 @@
9
9
  import type { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
10
10
  import { CommonControl } from '../../../common-control';
11
11
  import { type Orientation } from '../../templates';
12
- export declare const LAYOUT_CONTROL_ID = "recommendation-layout-control";
12
+ export declare const LAYOUT_ORIENTATION_CONTROL_ID = "recommendation-layout-orientation-control";
13
13
  /**
14
14
  * Control for managing recommendation block layout orientation
15
15
  * Allows switching between grid and list layouts
16
16
  */
17
- export declare class LayoutControl extends CommonControl {
17
+ export declare class LayoutOrientationControl extends CommonControl {
18
18
  private store;
19
19
  private isChangingLayout;
20
20
  getId(): string;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Product Count Control
3
+ *
4
+ * Handles the "Number of Products" setting for the recommendation block.
5
+ * Extracted from ProductLayoutControl for single-responsibility.
6
+ *
7
+ * Changes to product count trigger a product refetch via Pinia store
8
+ * subscription (handled by the main RecommendationBlockControl coordinator).
9
+ *
10
+ * Configuration is stored via node config (persists with template).
11
+ */
12
+ import type { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
13
+ import { CommonControl } from '../../../common-control';
14
+ export declare const PRODUCT_COUNT_CONTROL_ID = "recommendation-product-count-control";
15
+ /**
16
+ * Control for configuring the number of products to display.
17
+ * Updates node config and triggers product refetch via store subscription.
18
+ */
19
+ export declare class ProductCountControl extends CommonControl {
20
+ private store;
21
+ getId(): string;
22
+ getTemplate(): string;
23
+ onRender(): void;
24
+ onTemplateNodeUpdated(node: ImmutableHtmlNode): void;
25
+ _setFormValues(): void;
26
+ _onProductCountChange(value: string): void;
27
+ _listenToFormUpdates(): void;
28
+ }
@@ -1,14 +1,17 @@
1
1
  /**
2
2
  * Product Layout Control
3
3
  *
4
- * Handles product display configuration:
5
- * - Number of products to show
4
+ * Handles product grid/row configuration:
6
5
  * - Products per row on desktop (hidden for list layout)
7
- * - Products per row on mobile (hidden for list layout)
6
+ * - Mobile Layout Optimization toggle (enables/disables mobile-specific layout)
7
+ * - Products per row on mobile (visible only when mobile layout is enabled)
8
8
  * - Triggers product row regeneration on changes
9
9
  *
10
- * Desktop/mobile counters swap visibility based on editor mode
11
- * (detected via Stripo's EditorStatePropertyType.previewDeviceMode).
10
+ * When the mobile layout toggle is ON, only one "Products in One Row" counter
11
+ * is visible at a time — desktop or mobile — based on the editor's current
12
+ * preview device mode. Both counters are placed above the toggle.
13
+ *
14
+ * Note: "Number of Products" is handled by the separate ProductCountControl.
12
15
  *
13
16
  * Configuration is stored via node config (persists with template).
14
17
  */
@@ -16,8 +19,9 @@ import type { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
16
19
  import { CommonControl } from '../../../common-control';
17
20
  export declare const PRODUCT_LAYOUT_CONTROL_ID = "recommendation-product-layout-control";
18
21
  /**
19
- * Control for configuring product count and layout density
20
- * Includes both desktop and mobile "products per row" counters
22
+ * Control for configuring product layout density.
23
+ * Includes desktop "products per row" counter, a mobile layout optimization
24
+ * toggle, and a mobile "products per row" counter (visible when toggle is on).
21
25
  */
22
26
  export declare class ProductLayoutControl extends CommonControl {
23
27
  private store;
@@ -29,24 +33,32 @@ export declare class ProductLayoutControl extends CommonControl {
29
33
  onDestroy(): void;
30
34
  _setFormValues(): void;
31
35
  /**
32
- * Checks if the editor is currently in mobile preview mode
33
- * using Stripo's EditorStatePropertyType API.
36
+ * Returns true when the editor preview is in mobile mode.
34
37
  */
35
- _isMobileMode(): boolean;
38
+ _isMobilePreview(): boolean;
36
39
  /**
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
40
+ * Updates counter visibility based on layout, mobileLayoutEnabled, and editor preview mode.
41
+ *
42
+ * Three mutually exclusive rows exist in the template:
43
+ * 1. "Products in One Row" — generic, shown when toggle is OFF
44
+ * 2. "Products in One Row on Desktop" — shown when toggle is ON + desktop preview
45
+ * 3. "Products in One Row on Mobile" — shown when toggle is ON + mobile preview
46
+ *
47
+ * Only one row is visible at a time. This avoids setUIEAttribute on hidden
48
+ * elements, which Stripo does not apply reliably.
41
49
  */
42
50
  _updateProductsInRowVisibility(): void;
43
51
  /**
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.
52
+ * Initializes the mobile layout toggle using the same pattern as shuffle.ts.
53
+ * Sets the initial UI attribute value from the node config.
54
+ */
55
+ _initializeMobileLayoutToggle(): void;
56
+ /**
57
+ * Handles mobile layout toggle change.
58
+ * When enabled: opts in to mobile layout, restores previous mobileCardsInRow.
59
+ * When disabled: snapshots current mobileCardsInRow, resets to 1, opts out.
47
60
  */
48
- _subscribeToEditorModeChanges(): void;
49
- _onProductCountChange(value: string): void;
61
+ _onMobileLayoutToggleChange(enabled: boolean): void;
50
62
  _onProductsInRowChange(value: number): void;
51
63
  _onMobileCardsInRowChange(value: number): void;
52
64
  _regenerateProductRows(): void;
@@ -54,7 +66,13 @@ export declare class ProductLayoutControl extends CommonControl {
54
66
  _listenToFormUpdates(): void;
55
67
  /**
56
68
  * Subscribe to store changes to update visibility when layout changes
57
- * This is still needed because layout changes come from LayoutControl
69
+ * This is still needed because layout changes come from LayoutOrientationControl
58
70
  */
59
71
  _listenStateUpdates(): void;
72
+ /**
73
+ * Subscribe to editor preview mode changes (desktop ↔ mobile).
74
+ * When the user switches preview mode and mobile layout is enabled,
75
+ * we swap which "Products in One Row" counter is visible.
76
+ */
77
+ _listenEditorModeChanges(): void;
60
78
  }
@@ -56,14 +56,18 @@ export interface RegenerateProductRowsOptions {
56
56
  products?: RecommendationProduct[];
57
57
  /** Optional: pass layout directly to avoid stale DOM reads after setAttribute */
58
58
  layout?: Orientation;
59
+ /** Optional: pass composition directly to avoid stale DOM reads after setAttribute */
60
+ composition?: string[];
59
61
  }
60
62
  /**
61
63
  * Regenerates only the mobile product container rows.
62
64
  * Used when mobile-specific settings change (mobileCardsInRow)
63
65
  * or when the desktop container is regenerated (to keep both in sync).
64
66
  *
65
- * For list layout: hides the mobile row entirely (list is inherently responsive).
66
- * For grid layout: shows the mobile row and populates it.
67
+ * For list layout: clears the mobile row content (list rows are inherently responsive).
68
+ * For grid layout with mobileLayoutEnabled OFF: clears the mobile row content.
69
+ * For grid layout with mobileLayoutEnabled ON: rebuilds the full mobile row
70
+ * structure (<td><table>...products...</table></td>) in a single operation.
67
71
  */
68
72
  export declare function regenerateMobileProductRows(options: Omit<RegenerateProductRowsOptions, 'afterRegenerate'>): void;
69
73
  /**
@@ -5,12 +5,23 @@
5
5
  * Rules are injected into the document's stylesheet via modifyCss
6
6
  * so they appear in the compiled HTML export.
7
7
  */
8
- import type { ImmutableCssNode } from '@stripoinc/ui-editor-extensions';
8
+ import type { ImmutableCssNode, ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
9
9
  import { ModificationDescription } from '@stripoinc/ui-editor-extensions';
10
10
  /** CSS class for desktop product container */
11
11
  export declare const CSS_CLASS_DESKTOP_CONTAINER = "ins-recommendation-desktop-container";
12
12
  /** CSS class for mobile product container */
13
13
  export declare const CSS_CLASS_MOBILE_CONTAINER = "ins-recommendation-mobile-container";
14
+ /** CSS class that opts a block out of the mobile layout container swap */
15
+ export declare const CSS_CLASS_NO_MOBILE_LAYOUT = "ins-recommendation-no-mobile-layout";
16
+ interface HtmlModifierApi {
17
+ getDocumentModifier: () => {
18
+ modifyHtml: (node: ImmutableHtmlNode) => {
19
+ setClass: (cls: string) => void;
20
+ removeClass: (cls: string) => void;
21
+ };
22
+ apply: (desc: ModificationDescription) => void;
23
+ };
24
+ }
14
25
  interface CssModifierApi {
15
26
  getDocumentRootCssNode: () => ImmutableCssNode | null;
16
27
  getDocumentModifier: () => {
@@ -26,4 +37,15 @@ interface CssModifierApi {
26
37
  * Follows the same pattern as textTrim's _ensureCssRulesExist().
27
38
  */
28
39
  export declare function ensureMobileCssRulesExist(api: CssModifierApi): void;
40
+ /**
41
+ * Checks if a block element has the mobile layout opt-out class.
42
+ * Read-side companion to setMobileLayoutOptOut().
43
+ */
44
+ export declare function hasMobileLayoutOptOut(blockElement: ImmutableHtmlNode): boolean;
45
+ /**
46
+ * Adds or removes the mobile layout opt-out class on a block element.
47
+ * When optOut is true, the block keeps the desktop container visible on mobile.
48
+ * When optOut is false, the block follows the default mobile container swap.
49
+ */
50
+ export declare function setMobileLayoutOptOut(api: HtmlModifierApi, blockElement: ImmutableHtmlNode, optOut: boolean): void;
29
51
  export {};
@@ -5,8 +5,9 @@
5
5
  * - Column spacing: horizontal gap between cards in the same row
6
6
  * - Row spacing: vertical gap between different rows
7
7
  *
8
- * Desktop and mobile have independent spacing controls that toggle
9
- * visibility based on Stripo's editor preview mode (desktop/mobile).
8
+ * Desktop spacing controls are always visible when applicable (grid layout).
9
+ * Mobile spacing controls are visible only when mobileLayoutEnabled is true
10
+ * in the node config (controlled by the Mobile Layout Optimization toggle).
10
11
  *
11
12
  * Configuration is stored via node config (persists with template).
12
13
  * Actual spacing is applied to DOM elements via inline styles.
@@ -18,7 +19,8 @@ export declare const SPACING_CONTROL_ID = "recommendation-spacing-control";
18
19
  * Control for managing spacing between product cards
19
20
  * - Column spacing: horizontal gap between cards in the same row
20
21
  * - Row spacing: vertical gap between different rows
21
- * - Separate controls for desktop and mobile with device-mode toggling
22
+ * - Separate controls for desktop and mobile
23
+ * - Mobile controls visibility driven by mobileLayoutEnabled config
22
24
  */
23
25
  export declare class SpacingControl extends CommonControl {
24
26
  private store;
@@ -29,18 +31,11 @@ export declare class SpacingControl extends CommonControl {
29
31
  onTemplateNodeUpdated(node: ImmutableHtmlNode): void;
30
32
  onDestroy(): void;
31
33
  /**
32
- * Checks if the editor is currently in mobile preview mode
33
- * using Stripo's EditorStatePropertyType API.
34
- */
35
- _isMobileMode(): boolean;
36
- /**
37
- * Updates spacing control visibility based on layout orientation, editor mode,
38
- * and products-per-row count.
34
+ * Updates spacing control visibility based on layout orientation and mobileLayoutEnabled.
39
35
  * - List layout: hide column spacing (products are full-width), hide ALL mobile
40
36
  * spacing controls (list is responsive — single row spacing applies to both views)
41
- * - Desktop mode: show desktop spacing, hide mobile spacing
42
- * - Mobile mode: show mobile spacing, hide desktop spacing
43
- * - 1 product per row: hide column spacing (no gap between single column)
37
+ * - Desktop controls: always visible for grid layout (column spacing needs 2+ per row)
38
+ * - Mobile controls: visible when mobileLayoutEnabled is true (grid only)
44
39
  */
45
40
  _updateSpacingVisibility(): void;
46
41
  /**
@@ -108,9 +103,4 @@ export declare class SpacingControl extends CommonControl {
108
103
  * Updates spacing visibility when layout changes via the layout control
109
104
  */
110
105
  _subscribeToOrientationChanges(): void;
111
- /**
112
- * Subscribes to editor preview mode changes via Stripo API.
113
- * Toggles which spacing controls (desktop/mobile) are visible.
114
- */
115
- _subscribeToEditorModeChanges(): void;
116
106
  }
@@ -0,0 +1,34 @@
1
+ import { CommonControl } from '../../common-control';
2
+ import { RecommendationControlId } from '../constants';
3
+ export declare const SYNC_INFO_MESSAGE_CONTROL_ID = RecommendationControlId.SYNC_INFO_MESSAGE;
4
+ /**
5
+ * Static info message control for the product name sub-block settings panel.
6
+ *
7
+ * Follows the same pattern as getMigrationBannerHtml:
8
+ * - Direct HTML template with UIElementType.ICON + text
9
+ * - CSS-class-based styling (defined in recommendation.css)
10
+ * - Repositioned above tabs via DOM manipulation in onRender()
11
+ * - Cleaned up in onDestroy() since the element lives outside Stripo's managed area
12
+ */
13
+ export declare class SyncInfoMessageControl extends CommonControl {
14
+ private _repositionedElement;
15
+ getId(): string;
16
+ getTemplate(): string;
17
+ onRender(): void;
18
+ onDestroy(): void;
19
+ /**
20
+ * Moves the info message from inside the Settings tab content
21
+ * to above the tab bar in the DOM tree.
22
+ *
23
+ * DOM structure (Stripo internals):
24
+ * ue-control-panel
25
+ * div.control-panel-header-wrapper
26
+ * div.control-panel-header ← title bar (X, title, undo)
27
+ * ue-tabs-header.control-panel-tabs ← tab bar (Settings | Styles)
28
+ * div.control-panel-content-wrapper ← tab content (where our control renders)
29
+ *
30
+ * We insert the message between the title bar and the tab bar.
31
+ * The element reference is stored so onDestroy() can remove it.
32
+ */
33
+ private _repositionAboveTabs;
34
+ }
@@ -13,6 +13,7 @@ interface PerBlockConfigs {
13
13
  filters: Filter[];
14
14
  id: number;
15
15
  language: string;
16
+ mobileLayoutEnabled: boolean;
16
17
  orientation: Orientation;
17
18
  productIds: string[];
18
19
  recommendedProducts: [];
@@ -22,6 +23,7 @@ interface PerBlockConfigs {
22
23
  textTrimming: boolean;
23
24
  unresponsive: boolean;
24
25
  size: string;
26
+ customAttributes: string[];
25
27
  }
26
28
  interface PerBlockState {
27
29
  recommendationConfigs: PerBlockConfigs;
@@ -1,4 +1,4 @@
1
- import type { ElementRenderer } from '../utils';
1
+ import { type ElementRenderer } from '../utils';
2
2
  /**
3
3
  * Grid element renderer
4
4
  * Each element returns a `<td>` containing a nested table structure:
@@ -1,4 +1,4 @@
1
- import type { ElementRenderer } from '../utils';
1
+ import { type ElementRenderer } from '../utils';
2
2
  /**
3
3
  * List element renderer
4
4
  * - Image and Button return `<td>` cells for the 3-column layout
@@ -1,18 +1,24 @@
1
1
  import type { RecommendationProduct } from '@@/Types/recommendation';
2
2
  /**
3
3
  * Generates a list product card with 3-column layout
4
- * Uses listElementRenderer to render Image, Info content, and Button
4
+ * Uses buildElementRenderer to render Image, Info content, and Button
5
5
  * Layout: [Image (120px)] [Info table (flexible)] [Button (100px)]
6
+ *
7
+ * The info cell rows are built dynamically from the composition array,
8
+ * supporting both built-in attributes and custom product attributes.
9
+ * Image and Button are always separate columns (not in the info cell).
6
10
  * @param product - The product data
11
+ * @param composition - Array defining order of card elements
7
12
  * @returns HTML string for a single product card row
8
13
  */
9
- export declare function getListProductCard(product: RecommendationProduct): string;
14
+ export declare function getListProductCard(product: RecommendationProduct, composition?: string[]): string;
10
15
  /**
11
16
  * Prepares list product rows
12
17
  * Each product is a full-width row with 3-column layout
13
18
  * @param products - Array of products to display
19
+ * @param composition - Array defining order of card elements
14
20
  * @returns HTML string for product rows
15
21
  */
16
- export declare function prepareProductRows(products: RecommendationProduct[]): string;
22
+ export declare function prepareProductRows(products: RecommendationProduct[], composition?: string[]): string;
17
23
  export declare function getDefaultTemplate(): string;
18
- export declare function generateBlockTemplate(products: RecommendationProduct[], title?: string): string;
24
+ export declare function generateBlockTemplate(products: RecommendationProduct[], title?: string, composition?: string[]): string;
@@ -1,5 +1,35 @@
1
1
  import type { RecommendationProduct } from '@@/Types/recommendation';
2
2
  import { 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 '../constants';
3
+ /**
4
+ * Converts a snake_case attribute name to Title Case display name.
5
+ * e.g., "rating_star" → "Rating Star"
6
+ */
7
+ export declare function toDisplayName(attrName: string): string;
8
+ /**
9
+ * Callback that generates the cell HTML for a custom product attribute.
10
+ * Layout-specific: grid returns `<td>…`, list returns `<tr><td>…</td></tr>`.
11
+ */
12
+ export type CustomCellHtmlGetter = (attrName: string, content: string) => string;
13
+ /**
14
+ * Symbol key for embedding custom attribute HTML in an ElementRenderer.
15
+ * Grid and list renderers store their custom-attribute cell template under this key
16
+ * so that `buildElementRenderer` can read it without a separate callback parameter.
17
+ *
18
+ * Using a Symbol avoids conflicts with the string index signature — call sites that
19
+ * access `renderer[stringKey]` always get `(product) => string` without type narrowing.
20
+ */
21
+ export declare const CUSTOM_CELL_HTML: unique symbol;
22
+ /**
23
+ * Extends a base element renderer with custom attribute entries from the composition.
24
+ * Each `customAttr:<name>` key in the composition gets a renderer closure that calls
25
+ * the layout-specific custom cell HTML getter stored in `baseRenderer[CUSTOM_CELL_HTML]`.
26
+ *
27
+ * This ensures that custom attribute renderers live inside the ElementRenderer object
28
+ * alongside built-in ones, so template code can use a uniform lookup for all attributes.
29
+ * @param baseRenderer - The built-in element renderer (grid or list), must include a [CUSTOM_CELL_HTML] entry
30
+ * @param composition - Array of attribute keys including `customAttr:*` entries
31
+ */
32
+ export declare function buildElementRenderer(baseRenderer: ElementRenderer, composition: string[]): ElementRenderer;
3
33
  export type Orientation = 'list' | 'grid';
4
34
  /**
5
35
  * Options for prepareProductRows unified function
@@ -13,8 +43,11 @@ export interface PrepareProductRowsOptions {
13
43
  composition?: string[];
14
44
  }
15
45
  /**
16
- * Element renderer interface for product card elements
17
- * Keys are ATTR_PRODUCT_* constants for consistent naming
46
+ * Element renderer interface for product card elements.
47
+ * Keys are ATTR_PRODUCT_* constants for built-in elements.
48
+ * The index signature allows dynamic custom attribute keys (e.g., "customAttr:rating_star").
49
+ * The Symbol-keyed [CUSTOM_CELL_HTML] entry stores the layout-specific HTML template
50
+ * for custom attributes — used by `buildElementRenderer` to create custom entries.
18
51
  */
19
52
  export interface ElementRenderer {
20
53
  [ATTR_PRODUCT_IMAGE]: (product: RecommendationProduct) => string;
@@ -24,6 +57,8 @@ export interface ElementRenderer {
24
57
  [ATTR_PRODUCT_OMNIBUS_PRICE]: (product: RecommendationProduct) => string;
25
58
  [ATTR_PRODUCT_OMNIBUS_DISCOUNT]: (product: RecommendationProduct) => string;
26
59
  [ATTR_PRODUCT_BUTTON]: (product: RecommendationProduct) => string;
60
+ [CUSTOM_CELL_HTML]?: CustomCellHtmlGetter;
61
+ [key: string]: (product: RecommendationProduct) => string;
27
62
  }
28
63
  /**
29
64
  * Product card getter function type
@@ -106,6 +106,19 @@ export interface RecommendationNodeConfig {
106
106
  * Vertical spacing between product rows in pixels
107
107
  */
108
108
  rowSpacing: number;
109
+ /**
110
+ * Whether mobile layout optimization is enabled
111
+ * When enabled, shows mobile-specific product row configuration
112
+ * When disabled, adds opt-out CSS class to prevent mobile container swap
113
+ * @default false
114
+ */
115
+ mobileLayoutEnabled: boolean;
116
+ /**
117
+ * Snapshot of mobileCardsInRow before disabling mobile layout
118
+ * Used to restore the previous value when re-enabling
119
+ * @default 1
120
+ */
121
+ previousMobileCardsInRow: number;
109
122
  /**
110
123
  * Horizontal spacing between product cards on mobile in pixels (grid layout only)
111
124
  */
@@ -9,7 +9,20 @@ interface TextInputProps {
9
9
  interface LabelProps {
10
10
  text: string;
11
11
  name?: string;
12
- position?: 'top' | 'left';
12
+ hint?: string;
13
+ }
14
+ interface MessageProps {
15
+ name?: string;
16
+ type?: string;
17
+ }
18
+ interface InfoMessageProps {
19
+ name: string;
20
+ icon?: string;
21
+ type?: string;
22
+ }
23
+ interface IconProps {
24
+ src: string;
25
+ className?: string;
13
26
  }
14
27
  interface IconButtonProps {
15
28
  name: string;
@@ -73,7 +86,20 @@ export declare abstract class CommonControl extends Control {
73
86
  * @returns true if block instance changed, false otherwise
74
87
  */
75
88
  protected handleBlockInstanceChange(updateUI: () => void): boolean;
76
- _GuLabel({ text, name }: LabelProps): string;
89
+ _GuLabel({ text, name, hint }: LabelProps): string;
90
+ _GuMessage({ name, type }: MessageProps): string;
91
+ /**
92
+ * Returns template HTML for an info message with an optional icon.
93
+ * Use in getTemplate() — the icon is registered in a hidden store so Stripo initializes it.
94
+ * Pair with _setInfoMessageValue() in onRender() to set the text content.
95
+ */
96
+ _GuOnPageMessage({ name, icon, type }: InfoMessageProps): string;
97
+ /**
98
+ * Sets the info message value with the pre-registered icon.
99
+ * Use in onRender() — reads the icon's outerHTML from the hidden store
100
+ * and combines it with the text in Stripo's alert-message layout.
101
+ */
102
+ _setInfoMessageValue(name: string, text: string): void;
77
103
  _GuToggle(name: string): string;
78
104
  _GuSelectItem({ text, value }: {
79
105
  text: string;
@@ -89,6 +115,7 @@ export declare abstract class CommonControl extends Control {
89
115
  }): string;
90
116
  _GuRadioButton({ name, buttons, id }: RadioButtonProps): string;
91
117
  _GuButton({ name, label, id }: ButtonProps): string;
118
+ _GuIcon({ src, className }: IconProps): string;
92
119
  /**
93
120
  *
94
121
  * @param param0