@zalify/storefront-kit 0.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 (213) hide show
  1. package/LICENSE.md +49 -0
  2. package/README.md +63 -0
  3. package/dist/commerce/countdown.d.ts +18 -0
  4. package/dist/commerce/countdown.js +28 -0
  5. package/dist/commerce/events.d.ts +29 -0
  6. package/dist/commerce/events.js +14 -0
  7. package/dist/commerce/facets.d.ts +27 -0
  8. package/dist/commerce/facets.js +71 -0
  9. package/dist/commerce/index.d.ts +11 -0
  10. package/dist/commerce/index.js +11 -0
  11. package/dist/commerce/money.d.ts +51 -0
  12. package/dist/commerce/money.js +83 -0
  13. package/dist/commerce/product.d.ts +102 -0
  14. package/dist/commerce/product.js +93 -0
  15. package/dist/editor/frame.d.ts +57 -0
  16. package/dist/editor/frame.js +241 -0
  17. package/dist/editor/host.d.ts +31 -0
  18. package/dist/editor/host.js +67 -0
  19. package/dist/editor/index.d.ts +9 -0
  20. package/dist/editor/index.js +9 -0
  21. package/dist/react/adapter.d.ts +49 -0
  22. package/dist/react/adapter.js +23 -0
  23. package/dist/react/blocks/_box.d.ts +10 -0
  24. package/dist/react/blocks/_box.js +17 -0
  25. package/dist/react/blocks/_slide.d.ts +24 -0
  26. package/dist/react/blocks/_slide.js +21 -0
  27. package/dist/react/blocks/_video-card.d.ts +11 -0
  28. package/dist/react/blocks/_video-card.js +39 -0
  29. package/dist/react/blocks/accordion.d.ts +12 -0
  30. package/dist/react/blocks/accordion.js +5 -0
  31. package/dist/react/blocks/button.d.ts +10 -0
  32. package/dist/react/blocks/button.js +15 -0
  33. package/dist/react/blocks/countdown.d.ts +9 -0
  34. package/dist/react/blocks/countdown.js +64 -0
  35. package/dist/react/blocks/custom-liquid.d.ts +11 -0
  36. package/dist/react/blocks/custom-liquid.js +4 -0
  37. package/dist/react/blocks/group.d.ts +9 -0
  38. package/dist/react/blocks/group.js +8 -0
  39. package/dist/react/blocks/image.d.ts +8 -0
  40. package/dist/react/blocks/image.js +7 -0
  41. package/dist/react/blocks/message.d.ts +14 -0
  42. package/dist/react/blocks/message.js +5 -0
  43. package/dist/react/blocks/quote.d.ts +9 -0
  44. package/dist/react/blocks/quote.js +5 -0
  45. package/dist/react/blocks/spacer.d.ts +8 -0
  46. package/dist/react/blocks/spacer.js +5 -0
  47. package/dist/react/blocks/story.d.ts +17 -0
  48. package/dist/react/blocks/story.js +7 -0
  49. package/dist/react/blocks/text.d.ts +9 -0
  50. package/dist/react/blocks/text.js +5 -0
  51. package/dist/react/blocks/video.d.ts +17 -0
  52. package/dist/react/blocks/video.js +56 -0
  53. package/dist/react/components/Facets.d.ts +21 -0
  54. package/dist/react/components/Facets.js +220 -0
  55. package/dist/react/components/Icon.d.ts +12 -0
  56. package/dist/react/components/Icon.js +66 -0
  57. package/dist/react/components/PaymentIcons.d.ts +5 -0
  58. package/dist/react/components/PaymentIcons.generated.d.ts +22 -0
  59. package/dist/react/components/PaymentIcons.generated.js +21 -0
  60. package/dist/react/components/PaymentIcons.js +18 -0
  61. package/dist/react/components/Price.d.ts +24 -0
  62. package/dist/react/components/Price.js +9 -0
  63. package/dist/react/components/ProductBadges.d.ts +24 -0
  64. package/dist/react/components/ProductBadges.js +57 -0
  65. package/dist/react/components/ProductCard.d.ts +17 -0
  66. package/dist/react/components/ProductCard.js +202 -0
  67. package/dist/react/components/Rating.d.ts +11 -0
  68. package/dist/react/components/Rating.js +35 -0
  69. package/dist/react/components/SwatchStyle.d.ts +23 -0
  70. package/dist/react/components/SwatchStyle.js +22 -0
  71. package/dist/react/components/VideoModal.d.ts +55 -0
  72. package/dist/react/components/VideoModal.js +102 -0
  73. package/dist/react/engine/CssVariables.d.ts +1 -0
  74. package/dist/react/engine/CssVariables.js +105 -0
  75. package/dist/react/engine/context.d.ts +43 -0
  76. package/dist/react/engine/context.js +45 -0
  77. package/dist/react/engine/fonts.d.ts +15 -0
  78. package/dist/react/engine/fonts.js +33 -0
  79. package/dist/react/engine/images.d.ts +24 -0
  80. package/dist/react/engine/images.js +40 -0
  81. package/dist/react/engine/render.d.ts +29 -0
  82. package/dist/react/engine/render.js +87 -0
  83. package/dist/react/engine/settings.d.ts +8 -0
  84. package/dist/react/engine/settings.js +68 -0
  85. package/dist/react/engine/store.d.ts +64 -0
  86. package/dist/react/engine/store.js +42 -0
  87. package/dist/react/engine/translate.d.ts +7 -0
  88. package/dist/react/engine/translate.js +33 -0
  89. package/dist/react/engine/types.d.ts +128 -0
  90. package/dist/react/engine/types.js +1 -0
  91. package/dist/react/index.d.ts +38 -0
  92. package/dist/react/index.js +39 -0
  93. package/dist/react/registries.d.ts +10 -0
  94. package/dist/react/registries.js +68 -0
  95. package/dist/react/registries.server.d.ts +2 -0
  96. package/dist/react/registries.server.js +98 -0
  97. package/dist/react/sections/404.d.ts +10 -0
  98. package/dist/react/sections/404.js +10 -0
  99. package/dist/react/sections/announcement-bar.d.ts +14 -0
  100. package/dist/react/sections/announcement-bar.js +84 -0
  101. package/dist/react/sections/article.d.ts +17 -0
  102. package/dist/react/sections/article.js +31 -0
  103. package/dist/react/sections/contact-form.d.ts +16 -0
  104. package/dist/react/sections/contact-form.js +8 -0
  105. package/dist/react/sections/custom-liquid.d.ts +14 -0
  106. package/dist/react/sections/custom-liquid.js +7 -0
  107. package/dist/react/sections/custom-section.d.ts +14 -0
  108. package/dist/react/sections/custom-section.js +9 -0
  109. package/dist/react/sections/featured-collection.d.ts +10 -0
  110. package/dist/react/sections/featured-collection.js +23 -0
  111. package/dist/react/sections/hero.d.ts +6 -0
  112. package/dist/react/sections/hero.js +35 -0
  113. package/dist/react/sections/image-with-text.d.ts +13 -0
  114. package/dist/react/sections/image-with-text.js +9 -0
  115. package/dist/react/sections/multicolumn.d.ts +15 -0
  116. package/dist/react/sections/multicolumn.js +8 -0
  117. package/dist/react/sections/page.d.ts +13 -0
  118. package/dist/react/sections/page.js +11 -0
  119. package/dist/react/sections/password.d.ts +15 -0
  120. package/dist/react/sections/password.js +9 -0
  121. package/dist/react/sections/rich-text.d.ts +12 -0
  122. package/dist/react/sections/rich-text.js +7 -0
  123. package/dist/react/sections/stories.d.ts +18 -0
  124. package/dist/react/sections/stories.js +10 -0
  125. package/dist/react/sections/video-bubble.d.ts +13 -0
  126. package/dist/react/sections/video-bubble.js +54 -0
  127. package/dist/react/sections/video-carousel.d.ts +19 -0
  128. package/dist/react/sections/video-carousel.js +9 -0
  129. package/dist/react/server.d.ts +7 -0
  130. package/dist/react/server.js +42 -0
  131. package/dist/schemas/bridge.d.ts +98 -0
  132. package/dist/schemas/bridge.js +50 -0
  133. package/dist/schemas/data.d.ts +54 -0
  134. package/dist/schemas/data.js +22 -0
  135. package/dist/schemas/index.d.ts +14 -0
  136. package/dist/schemas/index.js +14 -0
  137. package/dist/schemas/manifest.d.ts +63 -0
  138. package/dist/schemas/manifest.js +32 -0
  139. package/dist/schemas/schema.d.ts +159 -0
  140. package/dist/schemas/schema.js +129 -0
  141. package/dist/schemas/validate.d.ts +22 -0
  142. package/dist/schemas/validate.js +199 -0
  143. package/package.json +84 -0
  144. package/src/commerce/countdown.ts +38 -0
  145. package/src/commerce/events.ts +43 -0
  146. package/src/commerce/facets.ts +88 -0
  147. package/src/commerce/index.ts +11 -0
  148. package/src/commerce/money.ts +120 -0
  149. package/src/commerce/product.ts +177 -0
  150. package/src/editor/frame.ts +305 -0
  151. package/src/editor/host.ts +101 -0
  152. package/src/editor/index.ts +9 -0
  153. package/src/react/adapter.tsx +83 -0
  154. package/src/react/blocks/_box.tsx +37 -0
  155. package/src/react/blocks/_slide.tsx +96 -0
  156. package/src/react/blocks/_video-card.tsx +131 -0
  157. package/src/react/blocks/accordion.tsx +33 -0
  158. package/src/react/blocks/button.tsx +36 -0
  159. package/src/react/blocks/countdown.tsx +105 -0
  160. package/src/react/blocks/custom-liquid.tsx +21 -0
  161. package/src/react/blocks/group.tsx +32 -0
  162. package/src/react/blocks/image.tsx +29 -0
  163. package/src/react/blocks/message.tsx +28 -0
  164. package/src/react/blocks/quote.tsx +24 -0
  165. package/src/react/blocks/spacer.tsx +17 -0
  166. package/src/react/blocks/story.tsx +40 -0
  167. package/src/react/blocks/text.tsx +24 -0
  168. package/src/react/blocks/video.tsx +132 -0
  169. package/src/react/components/Facets.tsx +424 -0
  170. package/src/react/components/Icon.tsx +96 -0
  171. package/src/react/components/PaymentIcons.generated.ts +24 -0
  172. package/src/react/components/PaymentIcons.tsx +27 -0
  173. package/src/react/components/Price.tsx +72 -0
  174. package/src/react/components/ProductBadges.tsx +95 -0
  175. package/src/react/components/ProductCard.tsx +417 -0
  176. package/src/react/components/Rating.tsx +59 -0
  177. package/src/react/components/SwatchStyle.tsx +50 -0
  178. package/src/react/components/VideoModal.tsx +236 -0
  179. package/src/react/engine/CssVariables.tsx +142 -0
  180. package/src/react/engine/context.tsx +108 -0
  181. package/src/react/engine/fonts.ts +42 -0
  182. package/src/react/engine/images.tsx +58 -0
  183. package/src/react/engine/render.tsx +190 -0
  184. package/src/react/engine/settings.ts +89 -0
  185. package/src/react/engine/store.ts +103 -0
  186. package/src/react/engine/translate.ts +40 -0
  187. package/src/react/engine/types.ts +142 -0
  188. package/src/react/index.ts +78 -0
  189. package/src/react/registries.server.ts +110 -0
  190. package/src/react/registries.ts +80 -0
  191. package/src/react/sections/404.tsx +37 -0
  192. package/src/react/sections/announcement-bar.tsx +170 -0
  193. package/src/react/sections/article.tsx +97 -0
  194. package/src/react/sections/contact-form.tsx +104 -0
  195. package/src/react/sections/custom-liquid.tsx +39 -0
  196. package/src/react/sections/custom-section.tsx +44 -0
  197. package/src/react/sections/featured-collection.tsx +89 -0
  198. package/src/react/sections/hero.tsx +84 -0
  199. package/src/react/sections/image-with-text.tsx +61 -0
  200. package/src/react/sections/multicolumn.tsx +46 -0
  201. package/src/react/sections/page.tsx +43 -0
  202. package/src/react/sections/password.tsx +60 -0
  203. package/src/react/sections/rich-text.tsx +37 -0
  204. package/src/react/sections/stories.tsx +51 -0
  205. package/src/react/sections/video-bubble.tsx +145 -0
  206. package/src/react/sections/video-carousel.tsx +57 -0
  207. package/src/react/server.ts +60 -0
  208. package/src/schemas/bridge.ts +143 -0
  209. package/src/schemas/data.ts +72 -0
  210. package/src/schemas/index.ts +14 -0
  211. package/src/schemas/manifest.ts +85 -0
  212. package/src/schemas/schema.ts +287 -0
  213. package/src/schemas/validate.ts +302 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Resolves global theme settings the same way Shopify does: schema
3
+ * defaults from config/settings_schema.json, overlaid with the current
4
+ * preset (or flat values) in config/settings_data.json — both provided
5
+ * at installTheme() time.
6
+ */
7
+ import { getThemeStore } from './store';
8
+ let cache = null;
9
+ function resolve() {
10
+ if (cache)
11
+ return cache;
12
+ const store = getThemeStore();
13
+ const { settingsSchema, settingsData } = store.schema;
14
+ const defaults = {};
15
+ for (const group of settingsSchema) {
16
+ for (const setting of group.settings ?? []) {
17
+ const id = setting.id;
18
+ if (!id || setting.type === 'color_scheme_group')
19
+ continue;
20
+ if ('default' in setting)
21
+ defaults[id] = setting.default;
22
+ }
23
+ }
24
+ const current = typeof settingsData.current === 'string'
25
+ ? (settingsData.presets?.[settingsData.current] ?? {})
26
+ : (settingsData.current ?? {});
27
+ const settings = {
28
+ ...defaults,
29
+ ...current,
30
+ ...store.settingsOverride,
31
+ };
32
+ const rawSchemes = settings.color_schemes;
33
+ let schemes;
34
+ if (rawSchemes) {
35
+ schemes = Object.entries(rawSchemes).map(([id, value]) => ({
36
+ id,
37
+ settings: value.settings,
38
+ }));
39
+ }
40
+ else {
41
+ // Fall back to the schema definition defaults as a single scheme.
42
+ const group = settingsSchema
43
+ .flatMap((g) => g.settings ?? [])
44
+ .find((s) => s.type === 'color_scheme_group');
45
+ const definition = (group?.definition ?? []);
46
+ const schemeSettings = Object.fromEntries(definition.map((d) => [d.id, d.default ?? '#000000']));
47
+ schemes = [{ id: 'scheme-1', settings: schemeSettings }];
48
+ }
49
+ cache = { settings, schemes };
50
+ return cache;
51
+ }
52
+ /**
53
+ * Global theme settings. A lazy proxy so call sites can keep reading
54
+ * `themeSettings.card_density` while resolution waits for installTheme().
55
+ */
56
+ export const themeSettings = new Proxy({}, {
57
+ get: (_target, key) => resolve().settings[key],
58
+ has: (_target, key) => key in resolve().settings,
59
+ ownKeys: () => Reflect.ownKeys(resolve().settings),
60
+ getOwnPropertyDescriptor: (_target, key) => Object.getOwnPropertyDescriptor(resolve().settings, key),
61
+ });
62
+ export function colorSchemes() {
63
+ return resolve().schemes;
64
+ }
65
+ export function getColorScheme(id) {
66
+ const schemes = resolve().schemes;
67
+ return schemes.find((s) => s.id === id) ?? schemes[0];
68
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The engine's data store. Apps install the theme schema (the JSON
3
+ * copied from apps/liquid by `pnpm sync`) plus their component
4
+ * registries once at startup:
5
+ *
6
+ * // app/lib/theme-setup.ts — imported for side effects by root
7
+ * installTheme({schema, sections, blocks, sectionLoaders});
8
+ *
9
+ * No import.meta.glob in here — data loading is the app's business
10
+ * (Vite apps glob, other bundlers can use generated modules), which
11
+ * keeps this package bundler-agnostic.
12
+ */
13
+ import type { BlockComponent, SectionComponent, SectionGroupData, SectionLoader, TemplateData } from './types';
14
+ export interface ThemeSchema {
15
+ /** templates/*.json keyed by name ("index", "product"…). */
16
+ templates: Record<string, TemplateData>;
17
+ /** templates/customers/*.json keyed by name ("login"…). */
18
+ customerTemplates?: Record<string, TemplateData>;
19
+ /** sections/*-group.json keyed by file name ("header-group"…). */
20
+ sectionGroups: Record<string, SectionGroupData>;
21
+ /** config/settings_schema.json. */
22
+ settingsSchema: Array<{
23
+ name: string;
24
+ settings?: Array<Record<string, unknown>>;
25
+ }>;
26
+ /** config/settings_data.json. */
27
+ settingsData: {
28
+ current: string | Record<string, unknown>;
29
+ presets?: Record<string, Record<string, unknown>>;
30
+ };
31
+ /** locales/*.json keyed by file name ("en.default", "en.default.schema"). */
32
+ locales: Record<string, Record<string, unknown>>;
33
+ }
34
+ export interface InstallThemeOptions {
35
+ schema: ThemeSchema;
36
+ /** Full section registry (spread builtinSections, then app extras). */
37
+ sections: Record<string, SectionComponent>;
38
+ /** Full block registry (spread builtinBlocks, then app extras). */
39
+ blocks: Record<string, BlockComponent>;
40
+ /** Server loaders keyed by section type (spread builtinSectionLoaders…). */
41
+ sectionLoaders?: Record<string, SectionLoader>;
42
+ /**
43
+ * App-level overrides applied on top of the resolved theme settings
44
+ * (schema defaults + current preset). The synced schema stays the
45
+ * shared source of truth; this is the seam for one app to diverge
46
+ * deliberately (e.g. a different Google font in one storefront).
47
+ */
48
+ settingsOverride?: Record<string, unknown>;
49
+ }
50
+ interface ThemeStore extends InstallThemeOptions {
51
+ }
52
+ export declare function installTheme(options: InstallThemeOptions): void;
53
+ export declare function getThemeStore(): ThemeStore;
54
+ /** Look up a page template ("index", "product", "customers/login"…). */
55
+ export declare function getTemplate(name: string): TemplateData;
56
+ /** Look up a section group ("header-group", "footer-group", "overlay-group"). */
57
+ export declare function getSectionGroup(name: string): SectionGroupData;
58
+ /**
59
+ * Parse theme JSON that may carry Shopify's leading block comment
60
+ * (templates/*.json are written by the theme editor with a header).
61
+ * Exposed for apps that load the schema from raw strings.
62
+ */
63
+ export declare function parseThemeJson<T>(raw: string, name?: string): T;
64
+ export {};
@@ -0,0 +1,42 @@
1
+ let store = null;
2
+ export function installTheme(options) {
3
+ store = options;
4
+ }
5
+ export function getThemeStore() {
6
+ if (!store) {
7
+ throw new Error('[storefront-kit] installTheme() has not been called. Import your app\'s ' +
8
+ 'theme-setup module (which calls installTheme) before rendering.');
9
+ }
10
+ return store;
11
+ }
12
+ /** Look up a page template ("index", "product", "customers/login"…). */
13
+ export function getTemplate(name) {
14
+ const { schema } = getThemeStore();
15
+ const template = name.startsWith('customers/')
16
+ ? schema.customerTemplates?.[name.slice('customers/'.length)]
17
+ : schema.templates[name];
18
+ if (!template)
19
+ throw new Error(`Unknown theme template: ${name}`);
20
+ return template;
21
+ }
22
+ /** Look up a section group ("header-group", "footer-group", "overlay-group"). */
23
+ export function getSectionGroup(name) {
24
+ const group = getThemeStore().schema.sectionGroups[name];
25
+ if (!group)
26
+ throw new Error(`Unknown section group: ${name}`);
27
+ return group;
28
+ }
29
+ /**
30
+ * Parse theme JSON that may carry Shopify's leading block comment
31
+ * (templates/*.json are written by the theme editor with a header).
32
+ * Exposed for apps that load the schema from raw strings.
33
+ */
34
+ export function parseThemeJson(raw, name = 'theme JSON') {
35
+ const stripped = raw.replace(/^\s*\/\*[\s\S]*?\*\//, '').trim();
36
+ try {
37
+ return JSON.parse(stripped);
38
+ }
39
+ catch (error) {
40
+ throw new Error(`Failed to parse ${name}: ${String(error)}`);
41
+ }
42
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Translate a locale key with `{{ var }}` interpolation.
3
+ *
4
+ * t('general.go_to_slide', {number: 2}) // "Go to slide 2"
5
+ * t('t:general.hero') // schema label
6
+ */
7
+ export declare function t(key: string, vars?: Record<string, string | number>): string;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Mirror of Liquid's `| t` filter over the theme's locale files
3
+ * (locales/en.default.json, provided at installTheme() time). Keys
4
+ * prefixed with "t:" resolve against the schema locale file
5
+ * (en.default.schema.json), matching the theme editor.
6
+ */
7
+ import { getThemeStore } from './store';
8
+ function lookup(tree, key) {
9
+ let node = tree;
10
+ for (const part of key.split('.')) {
11
+ if (node == null || typeof node !== 'object')
12
+ return undefined;
13
+ node = node[part];
14
+ }
15
+ return typeof node === 'string' ? node : undefined;
16
+ }
17
+ /**
18
+ * Translate a locale key with `{{ var }}` interpolation.
19
+ *
20
+ * t('general.go_to_slide', {number: 2}) // "Go to slide 2"
21
+ * t('t:general.hero') // schema label
22
+ */
23
+ export function t(key, vars) {
24
+ const { locales } = getThemeStore().schema;
25
+ const isSchemaKey = key.startsWith('t:');
26
+ const cleanKey = isSchemaKey ? key.slice(2) : key;
27
+ const tree = (locales[isSchemaKey ? 'en.default.schema' : 'en.default'] ??
28
+ {});
29
+ const raw = lookup(tree, cleanKey) ?? cleanKey.split('.').pop() ?? cleanKey;
30
+ if (!vars)
31
+ return raw;
32
+ return raw.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, name) => name in vars ? String(vars[name]) : match);
33
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Data shapes mirrored from the Zalify Liquid theme's JSON schema
3
+ * (templates/*.json, sections/*-group.json, config/settings_data.json).
4
+ * Those files are copied verbatim from the theme and are the source of
5
+ * truth for page structure; these types describe them.
6
+ */
7
+ import type { ComponentType, ReactNode } from 'react';
8
+ export type SettingsBag = Record<string, unknown>;
9
+ export interface BlockData {
10
+ type: string;
11
+ settings?: SettingsBag;
12
+ blocks?: Record<string, BlockData>;
13
+ block_order?: string[];
14
+ disabled?: boolean;
15
+ }
16
+ export interface SectionData {
17
+ type: string;
18
+ settings?: SettingsBag;
19
+ blocks?: Record<string, BlockData>;
20
+ block_order?: string[];
21
+ disabled?: boolean;
22
+ custom_css?: string[];
23
+ }
24
+ export interface TemplateData {
25
+ layout?: string | false;
26
+ wrapper?: string;
27
+ sections: Record<string, SectionData>;
28
+ order: string[];
29
+ }
30
+ export interface SectionGroupData extends TemplateData {
31
+ type: string;
32
+ name: string;
33
+ }
34
+ /** One color scheme from the color_scheme_group setting. */
35
+ export interface ColorScheme {
36
+ id: string;
37
+ settings: {
38
+ background: string;
39
+ text: string;
40
+ accent: string;
41
+ button: string;
42
+ button_label: string;
43
+ };
44
+ }
45
+ /**
46
+ * Global theme settings: schema defaults from config/settings_schema.json
47
+ * overlaid with the current preset in config/settings_data.json.
48
+ * Individual ids are looked up loosely; well-known ones are typed below.
49
+ */
50
+ export interface ThemeSettings extends SettingsBag {
51
+ type_body_font?: string;
52
+ type_body_google?: string;
53
+ type_heading_font?: string;
54
+ type_heading_google?: string;
55
+ type_mono_google?: string;
56
+ max_page_width?: string;
57
+ min_page_margin?: number;
58
+ button_corner_radius?: number;
59
+ input_corner_radius?: number;
60
+ card_corner_radius?: number;
61
+ card_swatch_style?: 'swatch' | 'variant_image' | 'none';
62
+ card_density?: 'comfortable' | 'compact';
63
+ card_show_quick_add?: boolean;
64
+ card_show_secondary_image?: boolean;
65
+ card_show_rating?: boolean;
66
+ card_badge_position?: 'corner' | 'below';
67
+ card_badge_background?: string;
68
+ card_badge_text?: string;
69
+ card_badge_sale_background?: string;
70
+ card_badge_sale_text?: string;
71
+ badge_style_1_labels?: string;
72
+ badge_style_1_background?: string;
73
+ badge_style_1_text?: string;
74
+ badge_style_2_labels?: string;
75
+ badge_style_2_background?: string;
76
+ badge_style_2_text?: string;
77
+ favicon?: string;
78
+ social_instagram?: string;
79
+ social_tiktok?: string;
80
+ social_x?: string;
81
+ social_facebook?: string;
82
+ social_youtube?: string;
83
+ social_pinterest?: string;
84
+ }
85
+ /** Props every ported section component (app/sections/*.tsx) receives. */
86
+ export interface SectionProps<S extends object = SettingsBag> {
87
+ /** Section key inside its template, e.g. "hero" or "main". */
88
+ id: string;
89
+ section: SectionData;
90
+ /** section.settings (defaults are applied by the component itself). */
91
+ settings: S;
92
+ /** Pre-rendered child blocks — the mirror of `{% content_for 'blocks' %}`. */
93
+ children?: ReactNode;
94
+ }
95
+ /** Props every ported block component (app/blocks/*.tsx) receives. */
96
+ export interface BlockProps<S extends object = SettingsBag> {
97
+ id: string;
98
+ block: BlockData;
99
+ settings: S;
100
+ /** Position among siblings — mirror of forloop/find_index tricks. */
101
+ index: number;
102
+ total: number;
103
+ /** Key of the section this block ultimately lives in. */
104
+ sectionId: string;
105
+ /** Pre-rendered nested blocks. */
106
+ children?: ReactNode;
107
+ }
108
+ export type SectionComponent = ComponentType<SectionProps>;
109
+ export type BlockComponent = ComponentType<BlockProps>;
110
+ /**
111
+ * Optional per-section server loader (app/sections/<type>.server.ts).
112
+ * Runs from route loaders for every instance of the section in the
113
+ * page's template; its result is delivered to the component via
114
+ * useSectionData().
115
+ */
116
+ export interface SectionLoaderArgs {
117
+ settings: SettingsBag;
118
+ section: SectionData;
119
+ sectionId: string;
120
+ storefront: {
121
+ query: (query: string, options?: Record<string, unknown>) => Promise<any>;
122
+ CacheLong?: () => unknown;
123
+ CacheShort?: () => unknown;
124
+ };
125
+ params: Record<string, string | undefined>;
126
+ request: Request;
127
+ }
128
+ export type SectionLoader = (args: SectionLoaderArgs) => Promise<unknown>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @zalify/storefront-kit/react — the Zalify theme engine and shared React
3
+ * components, host-framework-agnostic. An app:
4
+ *
5
+ * 1. installTheme({schema, sections, blocks, sectionLoaders}) at
6
+ * startup, merging builtinSections/builtinBlocks with its own
7
+ * app-local components;
8
+ * 2. wraps the tree in <AdapterProvider adapter={…}> injecting its
9
+ * router/cart primitives;
10
+ * 3. renders pages with <ThemeTemplate>/<SectionGroup> and
11
+ * <CssVariables/> in <head>.
12
+ *
13
+ * Server loaders: import {loadSectionData} from '@zalify/storefront-kit/react/server'.
14
+ */
15
+ export * from './engine/types';
16
+ export { installTheme, getTemplate, getSectionGroup, parseThemeJson, } from './engine/store';
17
+ export type { ThemeSchema, InstallThemeOptions } from './engine/store';
18
+ export { themeSettings, colorSchemes, getColorScheme } from './engine/settings';
19
+ export { t } from './engine/translate';
20
+ export { ThemeTemplate, SectionGroup, RenderSection, RenderSections, } from './engine/render';
21
+ export { ThemeDataProvider, TemplateProvider, useThemeGlobals, useResource, useSectionData, useTemplateScope, } from './engine/context';
22
+ export { CssVariables } from './engine/CssVariables';
23
+ export { parseFontHandle, googleFontsHref } from './engine/fonts';
24
+ export { settingImageUrl, imageUrl, imageSrcSet, PlaceholderSvg, } from './engine/images';
25
+ export { AdapterProvider, useAdapter } from './adapter';
26
+ export type { ThemeAdapter, AdapterLinkProps, CartAddFormProps, CartLineInput, } from './adapter';
27
+ export { Icon } from './components/Icon';
28
+ export type { IconName } from './components/Icon';
29
+ export { PaymentIcons } from './components/PaymentIcons';
30
+ export type { PaymentType } from './components/PaymentIcons';
31
+ export { Price } from './components/Price';
32
+ export { Rating } from './components/Rating';
33
+ export { ProductBadges, hasProductBadges } from './components/ProductBadges';
34
+ export { swatchStyle } from './components/SwatchStyle';
35
+ export { ProductCard, PRODUCT_CARD_FRAGMENT } from './components/ProductCard';
36
+ export * from './components/VideoModal';
37
+ export { Facets, COLLECTION_SORT_OPTIONS, SEARCH_SORT_OPTIONS, } from './components/Facets';
38
+ export { builtinSections, builtinBlocks } from './registries';
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @zalify/storefront-kit/react — the Zalify theme engine and shared React
3
+ * components, host-framework-agnostic. An app:
4
+ *
5
+ * 1. installTheme({schema, sections, blocks, sectionLoaders}) at
6
+ * startup, merging builtinSections/builtinBlocks with its own
7
+ * app-local components;
8
+ * 2. wraps the tree in <AdapterProvider adapter={…}> injecting its
9
+ * router/cart primitives;
10
+ * 3. renders pages with <ThemeTemplate>/<SectionGroup> and
11
+ * <CssVariables/> in <head>.
12
+ *
13
+ * Server loaders: import {loadSectionData} from '@zalify/storefront-kit/react/server'.
14
+ */
15
+ // Engine
16
+ export * from './engine/types';
17
+ export { installTheme, getTemplate, getSectionGroup, parseThemeJson, } from './engine/store';
18
+ export { themeSettings, colorSchemes, getColorScheme } from './engine/settings';
19
+ export { t } from './engine/translate';
20
+ export { ThemeTemplate, SectionGroup, RenderSection, RenderSections, } from './engine/render';
21
+ export { ThemeDataProvider, TemplateProvider, useThemeGlobals, useResource, useSectionData, useTemplateScope, } from './engine/context';
22
+ export { CssVariables } from './engine/CssVariables';
23
+ export { parseFontHandle, googleFontsHref } from './engine/fonts';
24
+ export { settingImageUrl, imageUrl, imageSrcSet, PlaceholderSvg, } from './engine/images';
25
+ // Adapter seam
26
+ export { AdapterProvider, useAdapter } from './adapter';
27
+ // Shared components
28
+ export { Icon } from './components/Icon';
29
+ export { PaymentIcons } from './components/PaymentIcons';
30
+ export { Price } from './components/Price';
31
+ export { Rating } from './components/Rating';
32
+ export { ProductBadges, hasProductBadges } from './components/ProductBadges';
33
+ export { swatchStyle } from './components/SwatchStyle';
34
+ export { ProductCard, PRODUCT_CARD_FRAGMENT } from './components/ProductCard';
35
+ export * from './components/VideoModal';
36
+ export { Facets, COLLECTION_SORT_OPTIONS, SEARCH_SORT_OPTIONS, } from './components/Facets';
37
+ // Built-in registries (spread into installTheme with app extras).
38
+ // Server-only loaders live in '@zalify/storefront-kit/react/server'.
39
+ export { builtinSections, builtinBlocks } from './registries';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Built-in component registries. Keys are the theme type names — the
3
+ * section/block `type` values in templates/*.json, which equal the old
4
+ * app/sections/*.tsx and app/blocks/*.tsx file names (including leading
5
+ * underscores and '404'). Apps spread these into installTheme() and add
6
+ * their own app-local sections/blocks on top.
7
+ */
8
+ import type { BlockComponent, SectionComponent } from './engine/types';
9
+ export declare const builtinSections: Record<string, SectionComponent>;
10
+ export declare const builtinBlocks: Record<string, BlockComponent>;
@@ -0,0 +1,68 @@
1
+ // Blocks
2
+ import text from './blocks/text';
3
+ import button from './blocks/button';
4
+ import group from './blocks/group';
5
+ import spacer from './blocks/spacer';
6
+ import image from './blocks/image';
7
+ import box from './blocks/_box';
8
+ import slide from './blocks/_slide';
9
+ import story from './blocks/story';
10
+ import message from './blocks/message';
11
+ import quote from './blocks/quote';
12
+ import accordion from './blocks/accordion';
13
+ import countdown from './blocks/countdown';
14
+ import customLiquidBlock from './blocks/custom-liquid';
15
+ import video from './blocks/video';
16
+ import videoCard from './blocks/_video-card';
17
+ // Sections
18
+ import hero from './sections/hero';
19
+ import stories from './sections/stories';
20
+ import featuredCollection from './sections/featured-collection';
21
+ import multicolumn from './sections/multicolumn';
22
+ import imageWithText from './sections/image-with-text';
23
+ import richText from './sections/rich-text';
24
+ import customSection from './sections/custom-section';
25
+ import customLiquidSection from './sections/custom-liquid';
26
+ import announcementBar from './sections/announcement-bar';
27
+ import notFound from './sections/404';
28
+ import page from './sections/page';
29
+ import article from './sections/article';
30
+ import password from './sections/password';
31
+ import contactForm from './sections/contact-form';
32
+ import videoCarousel from './sections/video-carousel';
33
+ import videoBubble from './sections/video-bubble';
34
+ export const builtinSections = {
35
+ hero,
36
+ stories,
37
+ 'featured-collection': featuredCollection,
38
+ multicolumn,
39
+ 'image-with-text': imageWithText,
40
+ 'rich-text': richText,
41
+ 'custom-section': customSection,
42
+ 'custom-liquid': customLiquidSection,
43
+ 'announcement-bar': announcementBar,
44
+ '404': notFound,
45
+ page,
46
+ article,
47
+ password,
48
+ 'contact-form': contactForm,
49
+ 'video-carousel': videoCarousel,
50
+ 'video-bubble': videoBubble,
51
+ };
52
+ export const builtinBlocks = {
53
+ text,
54
+ button,
55
+ group,
56
+ spacer,
57
+ image,
58
+ _box: box,
59
+ _slide: slide,
60
+ story,
61
+ message,
62
+ quote,
63
+ accordion,
64
+ countdown,
65
+ 'custom-liquid': customLiquidBlock,
66
+ video,
67
+ '_video-card': videoCard,
68
+ };
@@ -0,0 +1,2 @@
1
+ import type { SectionLoader } from './engine/types';
2
+ export declare const builtinSectionLoaders: Record<string, SectionLoader>;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Built-in server loaders, keyed by section type (import from
3
+ * '@zalify/storefront-kit/react/server'). Apps spread these into installTheme()
4
+ * alongside their own app-local loaders.
5
+ *
6
+ * featured-collection: server loader for sections/featured-collection
7
+ * .liquid's data needs — the Liquid `section.settings.collection
8
+ * .products` lookup becomes a Storefront API query. When the collection
9
+ * setting is blank (fresh install), fall back to the first available
10
+ * collection so the section still merchandises real products; if
11
+ * nothing resolves the component renders its placeholder cards (the
12
+ * Liquid onboarding state).
13
+ */
14
+ import { PRODUCT_CARD_FRAGMENT } from './components/ProductCard';
15
+ const FEATURED_COLLECTION_QUERY = `#graphql
16
+ query ThemeFeaturedCollection($handle: String!, $first: Int!) {
17
+ collection(handle: $handle) {
18
+ id
19
+ handle
20
+ title
21
+ products(first: $first) {
22
+ nodes {
23
+ ...ProductCard
24
+ }
25
+ }
26
+ }
27
+ }
28
+ ${PRODUCT_CARD_FRAGMENT}
29
+ `;
30
+ const FIRST_COLLECTIONS_QUERY = `#graphql
31
+ query ThemeFirstCollections($first: Int!) {
32
+ collections(first: 10) {
33
+ nodes {
34
+ id
35
+ handle
36
+ title
37
+ products(first: $first) {
38
+ nodes {
39
+ ...ProductCard
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ${PRODUCT_CARD_FRAGMENT}
46
+ `;
47
+ const LATEST_PRODUCTS_QUERY = `#graphql
48
+ query ThemeLatestProducts($first: Int!) {
49
+ products(first: $first, sortKey: CREATED_AT, reverse: true) {
50
+ nodes {
51
+ ...ProductCard
52
+ }
53
+ }
54
+ }
55
+ ${PRODUCT_CARD_FRAGMENT}
56
+ `;
57
+ const featuredCollectionLoader = async ({ settings, storefront, }) => {
58
+ const first = Number(settings.products_to_show ?? 4) || 4;
59
+ const handle = typeof settings.collection === 'string' && settings.collection
60
+ ? settings.collection
61
+ : null;
62
+ if (handle) {
63
+ const data = await storefront.query(FEATURED_COLLECTION_QUERY, {
64
+ variables: { handle, first },
65
+ cache: storefront.CacheLong?.(),
66
+ });
67
+ if (data?.collection)
68
+ return { collection: data.collection };
69
+ }
70
+ // Fresh-install fallback: first collection that actually has
71
+ // products, then the latest products as a synthetic collection —
72
+ // placeholders only when the store is truly empty.
73
+ const data = await storefront.query(FIRST_COLLECTIONS_QUERY, {
74
+ variables: { first },
75
+ cache: storefront.CacheLong?.(),
76
+ });
77
+ const nonEmpty = (data?.collections?.nodes ?? []).find((node) => node.products?.nodes?.length);
78
+ if (nonEmpty)
79
+ return { collection: nonEmpty };
80
+ const latest = await storefront.query(LATEST_PRODUCTS_QUERY, {
81
+ variables: { first },
82
+ cache: storefront.CacheLong?.(),
83
+ });
84
+ const products = latest?.products?.nodes ?? [];
85
+ if (!products.length)
86
+ return { collection: null };
87
+ return {
88
+ collection: {
89
+ id: 'latest',
90
+ handle: 'all',
91
+ title: 'Featured products',
92
+ products: { nodes: products },
93
+ },
94
+ };
95
+ };
96
+ export const builtinSectionLoaders = {
97
+ 'featured-collection': featuredCollectionLoader,
98
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Port of sections/404.liquid. CSS: app/styles/components/sections-404.css
3
+ */
4
+ import type { SectionProps } from '../engine/types';
5
+ interface NotFoundSettings {
6
+ color_scheme?: string;
7
+ section_spacing?: string;
8
+ }
9
+ export default function NotFoundSection({ settings, }: SectionProps<NotFoundSettings>): import("react").JSX.Element;
10
+ export {};
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { t } from '../engine/translate';
3
+ import { useAdapter } from '../adapter';
4
+ export default function NotFoundSection({ settings, }) {
5
+ const { Link } = useAdapter();
6
+ const { color_scheme: colorScheme = 'scheme-1', section_spacing: spacing = 'md' } = settings;
7
+ return (_jsx("div", { className: `not-found section color-${colorScheme} full-width`, style: {
8
+ '--section-spacing': `var(--space-section-${spacing})`,
9
+ }, children: _jsxs("div", { className: "not-found__inner stack", children: [_jsx("h1", { className: "not-found__title", children: t('404.title') }), _jsx("p", { children: t('404.not_found') }), _jsx(Link, { to: "/collections/all", className: "button", children: t('404.back_to_shopping') })] }) }));
10
+ }
@@ -0,0 +1,14 @@
1
+ import type { SectionProps } from '../engine/types';
2
+ declare global {
3
+ namespace JSX {
4
+ interface IntrinsicElements {
5
+ 'announcement-bar': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
6
+ }
7
+ }
8
+ }
9
+ interface AnnouncementBarSettings {
10
+ rotation_speed?: number;
11
+ color_scheme?: string;
12
+ }
13
+ export default function AnnouncementBarSection({ section, settings, children, }: SectionProps<AnnouncementBarSettings>): import("react").JSX.Element;
14
+ export {};