@gem-sdk/pages 1.36.0 → 1.36.20

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.
@@ -29,6 +29,7 @@ const Toolbox = ()=>{
29
29
  const addSection = core.useSectionStore((s)=>s.addSection);
30
30
  const changeSwatches = core.useShopStore((s)=>s.changeSwatches);
31
31
  const updateItemName = core.useBuilderPreviewStore((s)=>s.updateItemName);
32
+ const updateItemAttribute = core.useBuilderPreviewStore((s)=>s.updateItemAttribute);
32
33
  const changeLayoutSettings = core.useShopStore((s)=>s.changeLayoutSettings);
33
34
  const changeCreateThemeSectionCount = core.useShopStore((s)=>s.changeCreateThemeSectionCount);
34
35
  const changeShopPlan = core.useShopStore((s)=>s.changeShopPlan);
@@ -304,6 +305,14 @@ const Toolbox = ()=>{
304
305
  }, [
305
306
  updateItemName
306
307
  ]);
308
+ const onUpdateItemAttribute = react.useCallback((e)=>{
309
+ const detail = e.detail;
310
+ if (detail.uid) {
311
+ updateItemAttribute(detail.uid, detail.value || '', detail.attr || '');
312
+ }
313
+ }, [
314
+ updateItemAttribute
315
+ ]);
307
316
  react.useEffect(()=>{
308
317
  if (fonts) {
309
318
  setFontsToHead('google-font-element', fonts);
@@ -328,6 +337,7 @@ const Toolbox = ()=>{
328
337
  window.addEventListener('set-dynamic-product', onUpdateDynamicProduct);
329
338
  window.addEventListener('set-dynamic-collection', onUpdateDynamicCollection);
330
339
  window.addEventListener('update-item-name', onUpdateItemName);
340
+ window.addEventListener('update-item-attribute', onUpdateItemAttribute);
331
341
  return ()=>{
332
342
  window.removeEventListener('update-shop-info', onChangeShopInfo);
333
343
  window.removeEventListener('revalidate-query', onRevalidateQuery);
@@ -344,6 +354,7 @@ const Toolbox = ()=>{
344
354
  window.removeEventListener('set-dynamic-product', onUpdateDynamicProduct);
345
355
  window.removeEventListener('set-dynamic-collection', onUpdateDynamicCollection);
346
356
  window.removeEventListener('update-item-name', onUpdateItemName);
357
+ window.removeEventListener('update-item-attribute', onUpdateItemAttribute);
347
358
  };
348
359
  }, [
349
360
  onAddEntity,
@@ -361,7 +372,8 @@ const Toolbox = ()=>{
361
372
  onUpdateCreateThemeSectionCount,
362
373
  onUpdateDynamicProduct,
363
374
  onUpdateDynamicCollection,
364
- onUpdateItemName
375
+ onUpdateItemName,
376
+ onUpdateItemAttribute
365
377
  ]);
366
378
  return /*#__PURE__*/ jsxRuntime.jsx("div", {
367
379
  className: "toolbox"
@@ -9,6 +9,7 @@ var getFallback = require('../helpers/get-fallback.js');
9
9
  var normalize = require('../helpers/normalize.js');
10
10
  var parseJson = require('../helpers/parse-json.js');
11
11
  var nextjs = require('@sentry/nextjs');
12
+ var customFonts = require('../custom-fonts.js');
12
13
 
13
14
  const getStaticPagePropsV2 = (fetcher, shopifyFetcher)=>async (slug)=>{
14
15
  try {
@@ -33,14 +34,16 @@ const getStaticPagePropsV2 = (fetcher, shopifyFetcher)=>async (slug)=>{
33
34
  throw new Error(theme.reason?.[0]);
34
35
  }
35
36
  const dataBuilder = theme.value.publishedThemePages?.[0];
37
+ const themePageCustomFonts = theme.value?.publishedThemePages?.[0]?.themePageCustomFonts;
36
38
  if (!dataBuilder) {
37
39
  throw new Error(`No data builder found for slug: /${slug}`);
38
40
  }
39
41
  const pageTemplate = normalize.parseBuilderTemplateV2(dataBuilder);
40
- const [elementFontStyle, fontStyle, fallback] = await Promise.all([
42
+ const [elementFontStyle, fontStyle, fallback, customFonts$1] = await Promise.all([
41
43
  googleFonts.getFontStyleFromPageTemplate(pageTemplate),
42
44
  googleFonts.getFontFromGlobalStyle(dataBuilder?.pageStyle?.data),
43
- getFallback.getFallbackV2(fetcher, pageTemplate)
45
+ getFallback.getFallbackV2(fetcher, pageTemplate),
46
+ customFonts.getCustomFonts(themePageCustomFonts)
44
47
  ]);
45
48
  const mobileOnly = dataBuilder.isMobile ?? false;
46
49
  const description = dataBuilder?.themePageDataSEO?.find((item)=>item?.key === 'global-meta-description')?.value;
@@ -137,7 +140,8 @@ const getStaticPagePropsV2 = (fetcher, shopifyFetcher)=>async (slug)=>{
137
140
  tiktokPixelId: dataBuilder.themePageAnalytic?.tiktokPixelID ?? null,
138
141
  customCodeHeader: dataBuilder.themePageCustomCode?.header ?? null,
139
142
  customCodeBody: dataBuilder.themePageCustomCode?.body ?? null,
140
- pageHandle: dataBuilder.handle ?? null
143
+ pageHandle: dataBuilder.handle ?? null,
144
+ customFonts: customFonts$1
141
145
  });
142
146
  } catch (err) {
143
147
  nextjs.captureException(err);
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ const composeFontMimeType = (fonts)=>{
4
+ const result = {};
5
+ fonts?.forEach((font)=>{
6
+ if (!font) return;
7
+ const fontKey = `${font.fontFamily}_${font.fontStyle}_${font.fontWeight}`;
8
+ const fontData = {
9
+ font: {
10
+ fontFamily: font.fontFamily,
11
+ fontStyle: font.fontStyle,
12
+ fontWeight: font.fontWeight
13
+ },
14
+ urls: [
15
+ {
16
+ url: font.backupFilePath || font.filePath,
17
+ mimeType: font.mimeType
18
+ }
19
+ ]
20
+ };
21
+ if (!result[fontKey]) {
22
+ result[fontKey] = fontData;
23
+ } else {
24
+ result[fontKey]?.urls.push(fontData?.urls?.[0]);
25
+ }
26
+ });
27
+ return result;
28
+ };
29
+ // Currently support only ttf, otf, woff, woff2
30
+ const formatName = (mimeType)=>{
31
+ const fontMimeTypes = {
32
+ 'font/ttf': 'truetype',
33
+ 'application/x-font-opentype': 'opentype',
34
+ 'application/font-woff': 'woff',
35
+ 'application/font-woff2': 'woff2'
36
+ };
37
+ return fontMimeTypes[mimeType];
38
+ };
39
+ const composeFontFaces = (fontsMimeType)=>{
40
+ const fontFaces = [];
41
+ for (const [, value] of Object.entries(fontsMimeType)){
42
+ fontFaces.push(`
43
+ @font-face {
44
+ font-family: '${value.font.fontFamily}';
45
+ src: ${value.urls.map((item)=>`url('${item.url}') format('${formatName(item.mimeType)}')`).join('\n')};
46
+ font-style: ${value?.font?.fontStyle ?? 'normal'};
47
+ font-weight: ${value?.font?.fontWeight ?? 'normal'};
48
+ }
49
+ `);
50
+ }
51
+ return fontFaces.map((fontFace)=>fontFace).join('\n');
52
+ };
53
+ const getCustomFonts = (fonts)=>{
54
+ if (!fonts) return null;
55
+ const fontsMimeType = composeFontMimeType(fonts);
56
+ return composeFontFaces(fontsMimeType);
57
+ };
58
+
59
+ exports.composeFontFaces = composeFontFaces;
60
+ exports.composeFontMimeType = composeFontMimeType;
61
+ exports.formatName = formatName;
62
+ exports.getCustomFonts = getCustomFonts;
@@ -70,6 +70,12 @@ const getValueByDevice = (data, device)=>{
70
70
  const genCSSVariable = (deviceData)=>{
71
71
  return Object.entries(mapObject(flattenObject(deviceData))).map(([key, value])=>{
72
72
  if (value === undefined) return undefined;
73
+ if ([
74
+ 'font-heading',
75
+ 'font-body'
76
+ ].includes(key)) {
77
+ return `--g-${key}: '${value}'`;
78
+ }
73
79
  return `--g-${key}:${value}`;
74
80
  }).filter((v)=>v !== undefined).join(';');
75
81
  };
@@ -8,7 +8,7 @@ var router = require('next/router');
8
8
  var useTrackingView = require('../libs/hooks/use-tracking-view.js');
9
9
  var parseHtml = require('../libs/parse-html.js');
10
10
 
11
- const StaticPageV2 = ({ components, builderData, sectionData, seo, themeStyle, fontStyle, elementFontStyle, customCodeHeader, shopToken, pageHandle })=>{
11
+ const StaticPageV2 = ({ components, builderData, sectionData, seo, themeStyle, fontStyle, elementFontStyle, customCodeHeader, shopToken, pageHandle, customFonts })=>{
12
12
  const router$1 = router.useRouter();
13
13
  useTrackingView.useTrackingView(shopToken, pageHandle, router$1.isFallback);
14
14
  if (router$1.isFallback) {
@@ -58,7 +58,14 @@ const StaticPageV2 = ({ components, builderData, sectionData, seo, themeStyle, f
58
58
  dangerouslySetInnerHTML: {
59
59
  __html: fontStyle
60
60
  }
61
- }, fontStyle))
61
+ }, fontStyle)),
62
+ customFonts && /*#__PURE__*/ jsxRuntime.jsx("style", {
63
+ "data-id": "custom-element-fonts",
64
+ type: "text/css",
65
+ dangerouslySetInnerHTML: {
66
+ __html: customFonts
67
+ }
68
+ }, fontStyle)
62
69
  ]
63
70
  }),
64
71
  /*#__PURE__*/ jsxRuntime.jsx(core.PageProvider, {
@@ -25,6 +25,7 @@ const Toolbox = ()=>{
25
25
  const addSection = useSectionStore((s)=>s.addSection);
26
26
  const changeSwatches = useShopStore((s)=>s.changeSwatches);
27
27
  const updateItemName = useBuilderPreviewStore((s)=>s.updateItemName);
28
+ const updateItemAttribute = useBuilderPreviewStore((s)=>s.updateItemAttribute);
28
29
  const changeLayoutSettings = useShopStore((s)=>s.changeLayoutSettings);
29
30
  const changeCreateThemeSectionCount = useShopStore((s)=>s.changeCreateThemeSectionCount);
30
31
  const changeShopPlan = useShopStore((s)=>s.changeShopPlan);
@@ -300,6 +301,14 @@ const Toolbox = ()=>{
300
301
  }, [
301
302
  updateItemName
302
303
  ]);
304
+ const onUpdateItemAttribute = useCallback((e)=>{
305
+ const detail = e.detail;
306
+ if (detail.uid) {
307
+ updateItemAttribute(detail.uid, detail.value || '', detail.attr || '');
308
+ }
309
+ }, [
310
+ updateItemAttribute
311
+ ]);
303
312
  useEffect(()=>{
304
313
  if (fonts) {
305
314
  setFontsToHead('google-font-element', fonts);
@@ -324,6 +333,7 @@ const Toolbox = ()=>{
324
333
  window.addEventListener('set-dynamic-product', onUpdateDynamicProduct);
325
334
  window.addEventListener('set-dynamic-collection', onUpdateDynamicCollection);
326
335
  window.addEventListener('update-item-name', onUpdateItemName);
336
+ window.addEventListener('update-item-attribute', onUpdateItemAttribute);
327
337
  return ()=>{
328
338
  window.removeEventListener('update-shop-info', onChangeShopInfo);
329
339
  window.removeEventListener('revalidate-query', onRevalidateQuery);
@@ -340,6 +350,7 @@ const Toolbox = ()=>{
340
350
  window.removeEventListener('set-dynamic-product', onUpdateDynamicProduct);
341
351
  window.removeEventListener('set-dynamic-collection', onUpdateDynamicCollection);
342
352
  window.removeEventListener('update-item-name', onUpdateItemName);
353
+ window.removeEventListener('update-item-attribute', onUpdateItemAttribute);
343
354
  };
344
355
  }, [
345
356
  onAddEntity,
@@ -357,7 +368,8 @@ const Toolbox = ()=>{
357
368
  onUpdateCreateThemeSectionCount,
358
369
  onUpdateDynamicProduct,
359
370
  onUpdateDynamicCollection,
360
- onUpdateItemName
371
+ onUpdateItemName,
372
+ onUpdateItemAttribute
361
373
  ]);
362
374
  return /*#__PURE__*/ jsx("div", {
363
375
  className: "toolbox"
@@ -7,6 +7,7 @@ import { getFallbackV2 } from '../helpers/get-fallback.js';
7
7
  import { parseBuilderTemplateV2 } from '../helpers/normalize.js';
8
8
  import { parseJson, serializableJson } from '../helpers/parse-json.js';
9
9
  import { captureException } from '@sentry/nextjs';
10
+ import { getCustomFonts } from '../custom-fonts.js';
10
11
 
11
12
  const getStaticPagePropsV2 = (fetcher, shopifyFetcher)=>async (slug)=>{
12
13
  try {
@@ -31,14 +32,16 @@ const getStaticPagePropsV2 = (fetcher, shopifyFetcher)=>async (slug)=>{
31
32
  throw new Error(theme.reason?.[0]);
32
33
  }
33
34
  const dataBuilder = theme.value.publishedThemePages?.[0];
35
+ const themePageCustomFonts = theme.value?.publishedThemePages?.[0]?.themePageCustomFonts;
34
36
  if (!dataBuilder) {
35
37
  throw new Error(`No data builder found for slug: /${slug}`);
36
38
  }
37
39
  const pageTemplate = parseBuilderTemplateV2(dataBuilder);
38
- const [elementFontStyle, fontStyle, fallback] = await Promise.all([
40
+ const [elementFontStyle, fontStyle, fallback, customFonts] = await Promise.all([
39
41
  getFontStyleFromPageTemplate(pageTemplate),
40
42
  getFontFromGlobalStyle(dataBuilder?.pageStyle?.data),
41
- getFallbackV2(fetcher, pageTemplate)
43
+ getFallbackV2(fetcher, pageTemplate),
44
+ getCustomFonts(themePageCustomFonts)
42
45
  ]);
43
46
  const mobileOnly = dataBuilder.isMobile ?? false;
44
47
  const description = dataBuilder?.themePageDataSEO?.find((item)=>item?.key === 'global-meta-description')?.value;
@@ -135,7 +138,8 @@ const getStaticPagePropsV2 = (fetcher, shopifyFetcher)=>async (slug)=>{
135
138
  tiktokPixelId: dataBuilder.themePageAnalytic?.tiktokPixelID ?? null,
136
139
  customCodeHeader: dataBuilder.themePageCustomCode?.header ?? null,
137
140
  customCodeBody: dataBuilder.themePageCustomCode?.body ?? null,
138
- pageHandle: dataBuilder.handle ?? null
141
+ pageHandle: dataBuilder.handle ?? null,
142
+ customFonts
139
143
  });
140
144
  } catch (err) {
141
145
  captureException(err);
@@ -0,0 +1,57 @@
1
+ const composeFontMimeType = (fonts)=>{
2
+ const result = {};
3
+ fonts?.forEach((font)=>{
4
+ if (!font) return;
5
+ const fontKey = `${font.fontFamily}_${font.fontStyle}_${font.fontWeight}`;
6
+ const fontData = {
7
+ font: {
8
+ fontFamily: font.fontFamily,
9
+ fontStyle: font.fontStyle,
10
+ fontWeight: font.fontWeight
11
+ },
12
+ urls: [
13
+ {
14
+ url: font.backupFilePath || font.filePath,
15
+ mimeType: font.mimeType
16
+ }
17
+ ]
18
+ };
19
+ if (!result[fontKey]) {
20
+ result[fontKey] = fontData;
21
+ } else {
22
+ result[fontKey]?.urls.push(fontData?.urls?.[0]);
23
+ }
24
+ });
25
+ return result;
26
+ };
27
+ // Currently support only ttf, otf, woff, woff2
28
+ const formatName = (mimeType)=>{
29
+ const fontMimeTypes = {
30
+ 'font/ttf': 'truetype',
31
+ 'application/x-font-opentype': 'opentype',
32
+ 'application/font-woff': 'woff',
33
+ 'application/font-woff2': 'woff2'
34
+ };
35
+ return fontMimeTypes[mimeType];
36
+ };
37
+ const composeFontFaces = (fontsMimeType)=>{
38
+ const fontFaces = [];
39
+ for (const [, value] of Object.entries(fontsMimeType)){
40
+ fontFaces.push(`
41
+ @font-face {
42
+ font-family: '${value.font.fontFamily}';
43
+ src: ${value.urls.map((item)=>`url('${item.url}') format('${formatName(item.mimeType)}')`).join('\n')};
44
+ font-style: ${value?.font?.fontStyle ?? 'normal'};
45
+ font-weight: ${value?.font?.fontWeight ?? 'normal'};
46
+ }
47
+ `);
48
+ }
49
+ return fontFaces.map((fontFace)=>fontFace).join('\n');
50
+ };
51
+ const getCustomFonts = (fonts)=>{
52
+ if (!fonts) return null;
53
+ const fontsMimeType = composeFontMimeType(fonts);
54
+ return composeFontFaces(fontsMimeType);
55
+ };
56
+
57
+ export { composeFontFaces, composeFontMimeType, formatName, getCustomFonts };
@@ -68,6 +68,12 @@ const getValueByDevice = (data, device)=>{
68
68
  const genCSSVariable = (deviceData)=>{
69
69
  return Object.entries(mapObject(flattenObject(deviceData))).map(([key, value])=>{
70
70
  if (value === undefined) return undefined;
71
+ if ([
72
+ 'font-heading',
73
+ 'font-body'
74
+ ].includes(key)) {
75
+ return `--g-${key}: '${value}'`;
76
+ }
71
77
  return `--g-${key}:${value}`;
72
78
  }).filter((v)=>v !== undefined).join(';');
73
79
  };
@@ -6,7 +6,7 @@ import { useRouter } from 'next/router';
6
6
  import { useTrackingView } from '../libs/hooks/use-tracking-view.js';
7
7
  import { parseHtml } from '../libs/parse-html.js';
8
8
 
9
- const StaticPageV2 = ({ components, builderData, sectionData, seo, themeStyle, fontStyle, elementFontStyle, customCodeHeader, shopToken, pageHandle })=>{
9
+ const StaticPageV2 = ({ components, builderData, sectionData, seo, themeStyle, fontStyle, elementFontStyle, customCodeHeader, shopToken, pageHandle, customFonts })=>{
10
10
  const router = useRouter();
11
11
  useTrackingView(shopToken, pageHandle, router.isFallback);
12
12
  if (router.isFallback) {
@@ -56,7 +56,14 @@ const StaticPageV2 = ({ components, builderData, sectionData, seo, themeStyle, f
56
56
  dangerouslySetInnerHTML: {
57
57
  __html: fontStyle
58
58
  }
59
- }, fontStyle))
59
+ }, fontStyle)),
60
+ customFonts && /*#__PURE__*/ jsx("style", {
61
+ "data-id": "custom-element-fonts",
62
+ type: "text/css",
63
+ dangerouslySetInnerHTML: {
64
+ __html: customFonts
65
+ }
66
+ }, fontStyle)
60
67
  ]
61
68
  }),
62
69
  /*#__PURE__*/ jsx(PageProvider, {
@@ -37,6 +37,7 @@ type PageBuilderProps = {
37
37
  customCodeHeader?: string | null;
38
38
  customCodeBody?: string | null;
39
39
  isStorefront?: boolean;
40
+ customFonts?: string | null;
40
41
  };
41
42
  type PageBuilderPropsV2 = {
42
43
  builderData?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gem-sdk/pages",
3
- "version": "1.36.0",
3
+ "version": "1.36.20",
4
4
  "license": "MIT",
5
5
  "sideEffects": false,
6
6
  "main": "dist/cjs/index.js",
@@ -25,7 +25,7 @@
25
25
  "next-seo": "^6.0.0"
26
26
  },
27
27
  "devDependencies": {
28
- "@gem-sdk/core": "1.36.0",
28
+ "@gem-sdk/core": "1.36.20",
29
29
  "@gem-sdk/plugin-cookie-bar": "1.35.0",
30
30
  "@gem-sdk/plugin-quick-view": "1.35.0",
31
31
  "@gem-sdk/plugin-sticky-add-to-cart": "1.35.0"