@gem-sdk/pages 1.12.0-next.51 → 1.12.0-next.62

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.
@@ -27,9 +27,49 @@ const ACTIONS_DATA = [
27
27
  }
28
28
  ];
29
29
  const FOOTER_HEIGHT = 48;
30
+ const defaultMargin = {
31
+ desktop: '68px',
32
+ tablet: '16px',
33
+ mobile: '16px'
34
+ };
30
35
  const AddSectionImageToLayout = ()=>{
31
36
  const [link, setLink] = react.useState('');
32
37
  const [isInput, setIsInput] = react.useState(false);
38
+ const [margin, setMargin] = react.useState(defaultMargin);
39
+ react.useEffect(()=>{
40
+ const $iframe = parent.document.querySelector('.iframe');
41
+ if (!$iframe) return;
42
+ const $storefront = document.querySelector('#storefront');
43
+ if (!$storefront) return;
44
+ const $iframeDoc = $iframe.contentDocument || $iframe.contentWindow.document;
45
+ const $section = $iframeDoc.getElementsByTagName('section');
46
+ let prevWidth = 0;
47
+ const observer = new ResizeObserver((entries)=>{
48
+ const currentWidth = entries[0]?.borderBoxSize?.[0]?.inlineSize;
49
+ const shouldUpdateMargin = !!$section.length && typeof currentWidth === 'number' && currentWidth !== prevWidth;
50
+ if (shouldUpdateMargin) {
51
+ prevWidth = currentWidth;
52
+ const $baseSection = $section[0];
53
+ const padding = getComputedStyle($baseSection).getPropertyValue('padding-left');
54
+ const margin = getComputedStyle($baseSection).getPropertyValue('margin-left');
55
+ const newMargin = parseInt(margin, 10) + parseInt(padding, 10);
56
+ const baseMargin = 68;
57
+ if (newMargin > baseMargin) {
58
+ setMargin({
59
+ desktop: `${newMargin}px`,
60
+ tablet: `${newMargin}px`,
61
+ mobile: `${newMargin}px`
62
+ });
63
+ } else {
64
+ setMargin(defaultMargin);
65
+ }
66
+ }
67
+ });
68
+ observer.observe($storefront);
69
+ return ()=>{
70
+ observer.unobserve($storefront);
71
+ };
72
+ }, []);
33
73
  const onClick = (index)=>{
34
74
  if (index === 0) {
35
75
  setIsInput(true);
@@ -52,9 +92,11 @@ const AddSectionImageToLayout = ()=>{
52
92
  }
53
93
  return /*#__PURE__*/ jsxRuntime.jsxs("div", {
54
94
  id: "gps-add-section-image-to-layout-of-bottom",
55
- className: `gps-base-font-family mobile:mx-[16px] mobile:w-[calc(100%_-_32px)] relative mx-[44px] mb-[24px] mt-[40px] w-[calc(100%_-_88px)] justify-center rounded-[3px] border border-[#EEEEEE] py-[24px] ${totalSection ? 'flex' : 'hidden'}`,
95
+ className: `gps-base-font-family relative mx-[44px] mb-[24px] mt-[40px] justify-center rounded-[3px] border border-[#EEEEEE] py-[24px] ${totalSection ? 'flex' : 'hidden'}`,
56
96
  style: {
57
- marginBottom: `${offset + 24}px`
97
+ marginBottom: `${offset + 24}px`,
98
+ ...core.makeStyleResponsive('ml', margin),
99
+ ...core.makeStyleResponsive('mr', margin)
58
100
  },
59
101
  children: [
60
102
  /*#__PURE__*/ jsxRuntime.jsx("span", {
@@ -76,7 +118,7 @@ const AddSectionImageToLayout = ()=>{
76
118
  className: "flex items-center ",
77
119
  children: [
78
120
  /*#__PURE__*/ jsxRuntime.jsx("span", {
79
- className: "mr-[3px] text-[12px] font-normal text-[#676767]",
121
+ className: "mr-[3px] text-[12px] font-normal text-[#676767] text-center",
80
122
  children: action.content
81
123
  }),
82
124
  action.hasAILogo && /*#__PURE__*/ jsxRuntime.jsxs("svg", {
@@ -6,9 +6,7 @@ var react = require('react');
6
6
  const PagesSuggestion = ({ isOpen , link , setLink , setOpenSuggestion })=>{
7
7
  const inputRef = react.useRef(null);
8
8
  const pages = [
9
- 'https://gempages-demo/1',
10
- 'https://www.apple.com/',
11
- 'https://www.zara.com/ca/'
9
+ 'https://seal-commerce-asia.myshopify.com/pages/virtual-reality-heads'
12
10
  ];
13
11
  react.useEffect(()=>{
14
12
  if (isOpen) {
@@ -103,7 +101,7 @@ const PagesSuggestion = ({ isOpen , link , setLink , setOpenSuggestion })=>{
103
101
  /*#__PURE__*/ jsxRuntime.jsx("div", {
104
102
  className: "mt-[-1px] max-h-[500px] bg-white p-2",
105
103
  children: pages.map((page)=>/*#__PURE__*/ jsxRuntime.jsxs("div", {
106
- className: "gps-suggestion-item mb-2 flex h-[56px] cursor-pointer items-center justify-between rounded-[3px] px-[16px] py-[12px] last:mb-0 hover:bg-[#F4F4F4]",
104
+ className: " gps-suggestion-item mb-2 flex h-[56px] cursor-pointer items-center justify-between rounded-[3px] px-[16px] py-[12px] last:mb-0 hover:bg-[#F4F4F4]",
107
105
  "data-url": page,
108
106
  "aria-hidden": true,
109
107
  children: [
@@ -132,7 +130,7 @@ const PagesSuggestion = ({ isOpen , link , setLink , setOpenSuggestion })=>{
132
130
  ]
133
131
  }),
134
132
  /*#__PURE__*/ jsxRuntime.jsx("span", {
135
- className: "item-link ml-[24px] text-xs text-[#212121]",
133
+ className: "item-link ml-[24px] whitespace-nowrap text-xs text-[#212121]",
136
134
  children: page
137
135
  })
138
136
  ]
@@ -9,9 +9,12 @@ var googleFonts = require('../../libs/google-fonts.js');
9
9
  var genCss = require('../../libs/helpers/gen-css.js');
10
10
  var getStorefrontApi = require('../../libs/get-storefront-api.js');
11
11
  var shopifyCdnWithGoogleFonts = require('../../libs/shopify-cdn-with-google-fonts.js');
12
+ var normalize = require('../../libs/helpers/normalize.js');
13
+ var genFonts = require('../../libs/helpers/gen-fonts.js');
12
14
 
13
15
  const globalStyleId = 'global-style';
14
16
  const globalFontId = 'google-font-builder';
17
+ const fontElementSettingClassName = 'google-font-element';
15
18
  const Toolbox = ()=>{
16
19
  const matchMutate = core.useMatchMutate();
17
20
  const provider = core.useShopStore((s)=>s.provider);
@@ -26,6 +29,7 @@ const Toolbox = ()=>{
26
29
  const changeSwatches = core.useShopStore((s)=>s.changeSwatches);
27
30
  const changeLayoutSettings = core.useShopStore((s)=>s.changeLayoutSettings);
28
31
  const clearModal = core.useModalStore((s)=>s.clearModal);
32
+ const [fonts, setFonts] = react.useState([]);
29
33
  // Revalidate all query with key match query/
30
34
  const onRevalidateQuery = react.useCallback(()=>{
31
35
  matchMutate(/query\//, {
@@ -96,10 +100,24 @@ const Toolbox = ()=>{
96
100
  try {
97
101
  const detail = e.detail;
98
102
  if (detail.data) {
103
+ let dataBuilder = {};
99
104
  if (detail.type === 'flat') {
100
105
  initNormalizeState(detail.data);
106
+ dataBuilder = detail.data;
101
107
  } else {
102
108
  initState(detail.data);
109
+ dataBuilder = detail.data.reduceRight((prev, current)=>{
110
+ const item = normalize.normalizeBuilderData(current);
111
+ return {
112
+ ...prev,
113
+ ...item
114
+ };
115
+ }, {});
116
+ }
117
+ // Append link font to head
118
+ if (dataBuilder) {
119
+ const fonts = genFonts.getFontsFromDataBuilder(dataBuilder);
120
+ setFonts(fonts);
103
121
  }
104
122
  }
105
123
  } catch {
@@ -143,11 +161,38 @@ const Toolbox = ()=>{
143
161
  data: detail.propValue,
144
162
  group: detail.group
145
163
  });
164
+ // Check link google font to <head>
165
+ if (detail.propValue?.custom) {
166
+ const customFontFamily = detail.propValue?.custom?.fontFamily;
167
+ const customFontWeight = detail.propValue?.custom?.fontWeight;
168
+ if (customFontFamily && customFontWeight) {
169
+ const isExist = fonts.find((item)=>item.family == customFontFamily && item.variants.includes(customFontWeight));
170
+ if (!isExist) {
171
+ const isFontFamily = fonts.find((item)=>item.family == customFontFamily);
172
+ if (isFontFamily) {
173
+ isFontFamily.variants.push(customFontWeight);
174
+ } else {
175
+ setFonts([
176
+ ...fonts,
177
+ {
178
+ family: customFontFamily,
179
+ variants: [
180
+ customFontWeight
181
+ ],
182
+ subsets: [],
183
+ type: 'google'
184
+ }
185
+ ]);
186
+ }
187
+ }
188
+ }
189
+ }
146
190
  }
147
191
  } catch {
148
192
  //
149
193
  }
150
- }, [
194
+ }, // eslint-disable-next-line react-hooks/exhaustive-deps
195
+ [
151
196
  changeItemPropByKey
152
197
  ]);
153
198
  // Move entity
@@ -196,6 +241,32 @@ const Toolbox = ()=>{
196
241
  }, [
197
242
  changeLayoutSettings
198
243
  ]);
244
+ react.useEffect(()=>{
245
+ if (fonts?.length) {
246
+ fonts.forEach((font)=>{
247
+ const fontUrl = googleFonts.createFontUrl([
248
+ font
249
+ ]);
250
+ if (fontUrl) {
251
+ const googleFont = document.querySelector(`.${fontElementSettingClassName}[data-font="${encodeURI(font.family)}"]`);
252
+ if (googleFont) {
253
+ if (googleFont.getAttribute('href') !== fontUrl) {
254
+ googleFont.setAttribute('href', fontUrl);
255
+ }
256
+ } else {
257
+ const link = document.createElement('link');
258
+ link.className = fontElementSettingClassName;
259
+ link.dataset.font = encodeURI(font.family);
260
+ link.href = fontUrl;
261
+ link.rel = 'stylesheet';
262
+ document.head.appendChild(link);
263
+ }
264
+ }
265
+ });
266
+ }
267
+ }, [
268
+ fonts
269
+ ]);
199
270
  react.useEffect(()=>{
200
271
  window.addEventListener('update-shop-info', onChangeShopInfo);
201
272
  window.addEventListener('revalidate-query', onRevalidateQuery);
package/dist/cjs/index.js CHANGED
@@ -6,7 +6,6 @@ var getHomePagePropsV2 = require('./libs/api/get-home-page-props-v2.js');
6
6
  var getPreviewProps = require('./libs/api/get-preview-props.js');
7
7
  var getBuilderProps = require('./libs/api/get-builder-props.js');
8
8
  var getProductProps = require('./libs/api/get-product-props.js');
9
- var getStaticPageProps = require('./libs/api/get-static-page-props.js');
10
9
  var getStaticPagePropsV2 = require('./libs/api/get-static-page-props-v2.js');
11
10
  var getStaticPagePropsPreview = require('./libs/api/get-static-page-props-preview.js');
12
11
  var fetcher = require('./libs/fetcher.js');
@@ -24,6 +23,7 @@ var _static = require('./pages/static.js');
24
23
  var builder = require('./pages/builder.js');
25
24
  var staticV2 = require('./pages/static-v2.js');
26
25
  var getStaticPaths = require('./libs/getStaticPaths.js');
26
+ var genFonts = require('./libs/helpers/gen-fonts.js');
27
27
  var googleFonts = require('./libs/google-fonts.js');
28
28
  var getStorefrontApi = require('./libs/get-storefront-api.js');
29
29
  var ErrorBoundary = require('./components/ErrorBoundary.js');
@@ -42,7 +42,6 @@ exports.getHomePagePropsV2 = getHomePagePropsV2.getHomePagePropsV2;
42
42
  exports.getPreviewProps = getPreviewProps.getPreviewProps;
43
43
  exports.getBuilderProps = getBuilderProps.getBuilderProps;
44
44
  exports.getProductProps = getProductProps.getProductProps;
45
- exports.getStaticPageProps = getStaticPageProps.getStaticPageProps;
46
45
  exports.getStaticPagePropsV2 = getStaticPagePropsV2.getStaticPagePropsV2;
47
46
  exports.getStaticPagePropsPreview = getStaticPagePropsPreview.getStaticPagePropsPreview;
48
47
  exports.createFetcher = fetcher.createFetcher;
@@ -62,6 +61,7 @@ exports.StaticPage = _static.default;
62
61
  exports.BuilderPage = builder.BuilderPage;
63
62
  exports.StaticPageV2 = staticV2.StaticPageV2;
64
63
  exports.getStaticPaths = getStaticPaths.getStaticPaths;
64
+ exports.getFontsFromDataBuilder = genFonts.getFontsFromDataBuilder;
65
65
  exports.getFontFromGlobalStyle = googleFonts.getFontFromGlobalStyle;
66
66
  exports.getFonts = googleFonts.getFonts;
67
67
  exports.getStorefrontApi = getStorefrontApi.getStorefrontApi;
@@ -37,7 +37,8 @@ const getStaticPagePropsV2 = (fetcher, shopifyFetcher)=>async (slug)=>{
37
37
  throw new Error(`No data builder found for slug: /${slug}`);
38
38
  }
39
39
  const pageTemplate = normalize.parseBuilderTemplateV2(dataBuilder);
40
- const [fontStyle, fallback] = await Promise.all([
40
+ const [elementFontStyle, fontStyle, fallback] = await Promise.all([
41
+ googleFonts.getFontStyleFromPageTemplate(pageTemplate),
41
42
  googleFonts.getFontFromGlobalStyle(dataBuilder?.pageStyle?.data),
42
43
  getFallback.getFallbackV2(fetcher, pageTemplate)
43
44
  ]);
@@ -117,6 +118,7 @@ const getStaticPagePropsV2 = (fetcher, shopifyFetcher)=>async (slug)=>{
117
118
  return parseJson.serializableJson({
118
119
  themeStyle: genCss.genCSS(dataBuilder?.pageStyle?.data, mobileOnly),
119
120
  fontStyle,
121
+ elementFontStyle,
120
122
  builderData: pageTemplate,
121
123
  pageType,
122
124
  moneyFormat: shopMeta?.shop.moneyFormat ?? null,
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var genFonts = require('./helpers/gen-fonts.js');
4
+
3
5
  const CHROME_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36';
4
6
  const IE_UA = 'Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko';
5
7
  async function getFontForUA(url, UA) {
@@ -66,7 +68,14 @@ const getFontFromGlobalStyle = (data, maxSize)=>{
66
68
  return '';
67
69
  }
68
70
  };
71
+ async function getFontStyleFromPageTemplate(pageTemplate) {
72
+ const fontStyle = pageTemplate.map((sectionData)=>{
73
+ return getFonts(genFonts.getFontsFromDataBuilder(sectionData.data));
74
+ });
75
+ return await Promise.all(fontStyle);
76
+ }
69
77
 
70
78
  exports.createFontUrl = createFontUrl;
71
79
  exports.getFontFromGlobalStyle = getFontFromGlobalStyle;
80
+ exports.getFontStyleFromPageTemplate = getFontStyleFromPageTemplate;
72
81
  exports.getFonts = getFonts;
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ const getFontsFromDataBuilder = (dataBuilder)=>{
4
+ const fonts = [];
5
+ for(const uid in dataBuilder){
6
+ if (Object.prototype.hasOwnProperty.call(dataBuilder, uid)) {
7
+ const data = dataBuilder[uid];
8
+ const styles = data.styles;
9
+ const settings = data.settings;
10
+ getFontFromGroupSetting(fonts, styles);
11
+ getFontFromGroupSetting(fonts, settings);
12
+ }
13
+ }
14
+ return fonts;
15
+ };
16
+ const getFontFromGroupSetting = (fonts, groupSetting)=>{
17
+ for(const attr in groupSetting){
18
+ if (Object.prototype.hasOwnProperty.call(groupSetting, attr)) {
19
+ const value = groupSetting[attr];
20
+ if (value) {
21
+ const customFontFamily = value.custom?.fontFamily;
22
+ const customFontWeight = value.custom?.fontWeight;
23
+ if (customFontFamily && customFontWeight) {
24
+ const isExist = fonts.find((item)=>item.family == customFontFamily && item.variants.includes(customFontWeight));
25
+ if (!isExist) {
26
+ const isFontFamily = fonts.find((item)=>item.family == customFontFamily);
27
+ if (isFontFamily) {
28
+ isFontFamily.variants.push(customFontWeight);
29
+ } else {
30
+ fonts.push({
31
+ family: customFontFamily,
32
+ variants: [
33
+ customFontWeight
34
+ ],
35
+ subsets: [],
36
+ type: 'google'
37
+ });
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ };
45
+
46
+ exports.getFontsFromDataBuilder = getFontsFromDataBuilder;
@@ -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 , customCodeHeader , shopToken , pageHandle })=>{
11
+ const StaticPageV2 = ({ components , builderData , sectionData , seo , themeStyle , fontStyle , elementFontStyle , customCodeHeader , shopToken , pageHandle })=>{
12
12
  const router$1 = router.useRouter();
13
13
  useTrackingView.useTrackingView(shopToken, pageHandle, router$1.isFallback);
14
14
  if (router$1.isFallback) {
@@ -51,7 +51,14 @@ const StaticPageV2 = ({ components , builderData , sectionData , seo , themeStyl
51
51
  dangerouslySetInnerHTML: {
52
52
  __html: fontStyle
53
53
  }
54
- })
54
+ }),
55
+ elementFontStyle && elementFontStyle.map((fontStyle)=>/*#__PURE__*/ jsxRuntime.jsx("style", {
56
+ "data-id": "google-element-fonts",
57
+ type: "text/css",
58
+ dangerouslySetInnerHTML: {
59
+ __html: fontStyle
60
+ }
61
+ }, fontStyle))
55
62
  ]
56
63
  }),
57
64
  /*#__PURE__*/ jsxRuntime.jsx(core.BuilderComponentProvider, {
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { useState, useMemo } from 'react';
3
- import { useShopStore, useBuilderPreviewStore } from '@gem-sdk/core';
2
+ import { useState, useEffect, useMemo } from 'react';
3
+ import { useShopStore, useBuilderPreviewStore, makeStyleResponsive } from '@gem-sdk/core';
4
4
 
5
5
  const ACTIONS_DATA = [
6
6
  {
@@ -23,9 +23,49 @@ const ACTIONS_DATA = [
23
23
  }
24
24
  ];
25
25
  const FOOTER_HEIGHT = 48;
26
+ const defaultMargin = {
27
+ desktop: '68px',
28
+ tablet: '16px',
29
+ mobile: '16px'
30
+ };
26
31
  const AddSectionImageToLayout = ()=>{
27
32
  const [link, setLink] = useState('');
28
33
  const [isInput, setIsInput] = useState(false);
34
+ const [margin, setMargin] = useState(defaultMargin);
35
+ useEffect(()=>{
36
+ const $iframe = parent.document.querySelector('.iframe');
37
+ if (!$iframe) return;
38
+ const $storefront = document.querySelector('#storefront');
39
+ if (!$storefront) return;
40
+ const $iframeDoc = $iframe.contentDocument || $iframe.contentWindow.document;
41
+ const $section = $iframeDoc.getElementsByTagName('section');
42
+ let prevWidth = 0;
43
+ const observer = new ResizeObserver((entries)=>{
44
+ const currentWidth = entries[0]?.borderBoxSize?.[0]?.inlineSize;
45
+ const shouldUpdateMargin = !!$section.length && typeof currentWidth === 'number' && currentWidth !== prevWidth;
46
+ if (shouldUpdateMargin) {
47
+ prevWidth = currentWidth;
48
+ const $baseSection = $section[0];
49
+ const padding = getComputedStyle($baseSection).getPropertyValue('padding-left');
50
+ const margin = getComputedStyle($baseSection).getPropertyValue('margin-left');
51
+ const newMargin = parseInt(margin, 10) + parseInt(padding, 10);
52
+ const baseMargin = 68;
53
+ if (newMargin > baseMargin) {
54
+ setMargin({
55
+ desktop: `${newMargin}px`,
56
+ tablet: `${newMargin}px`,
57
+ mobile: `${newMargin}px`
58
+ });
59
+ } else {
60
+ setMargin(defaultMargin);
61
+ }
62
+ }
63
+ });
64
+ observer.observe($storefront);
65
+ return ()=>{
66
+ observer.unobserve($storefront);
67
+ };
68
+ }, []);
29
69
  const onClick = (index)=>{
30
70
  if (index === 0) {
31
71
  setIsInput(true);
@@ -48,9 +88,11 @@ const AddSectionImageToLayout = ()=>{
48
88
  }
49
89
  return /*#__PURE__*/ jsxs("div", {
50
90
  id: "gps-add-section-image-to-layout-of-bottom",
51
- className: `gps-base-font-family mobile:mx-[16px] mobile:w-[calc(100%_-_32px)] relative mx-[44px] mb-[24px] mt-[40px] w-[calc(100%_-_88px)] justify-center rounded-[3px] border border-[#EEEEEE] py-[24px] ${totalSection ? 'flex' : 'hidden'}`,
91
+ className: `gps-base-font-family relative mx-[44px] mb-[24px] mt-[40px] justify-center rounded-[3px] border border-[#EEEEEE] py-[24px] ${totalSection ? 'flex' : 'hidden'}`,
52
92
  style: {
53
- marginBottom: `${offset + 24}px`
93
+ marginBottom: `${offset + 24}px`,
94
+ ...makeStyleResponsive('ml', margin),
95
+ ...makeStyleResponsive('mr', margin)
54
96
  },
55
97
  children: [
56
98
  /*#__PURE__*/ jsx("span", {
@@ -72,7 +114,7 @@ const AddSectionImageToLayout = ()=>{
72
114
  className: "flex items-center ",
73
115
  children: [
74
116
  /*#__PURE__*/ jsx("span", {
75
- className: "mr-[3px] text-[12px] font-normal text-[#676767]",
117
+ className: "mr-[3px] text-[12px] font-normal text-[#676767] text-center",
76
118
  children: action.content
77
119
  }),
78
120
  action.hasAILogo && /*#__PURE__*/ jsxs("svg", {
@@ -4,9 +4,7 @@ import { useRef, useEffect } from 'react';
4
4
  const PagesSuggestion = ({ isOpen , link , setLink , setOpenSuggestion })=>{
5
5
  const inputRef = useRef(null);
6
6
  const pages = [
7
- 'https://gempages-demo/1',
8
- 'https://www.apple.com/',
9
- 'https://www.zara.com/ca/'
7
+ 'https://seal-commerce-asia.myshopify.com/pages/virtual-reality-heads'
10
8
  ];
11
9
  useEffect(()=>{
12
10
  if (isOpen) {
@@ -101,7 +99,7 @@ const PagesSuggestion = ({ isOpen , link , setLink , setOpenSuggestion })=>{
101
99
  /*#__PURE__*/ jsx("div", {
102
100
  className: "mt-[-1px] max-h-[500px] bg-white p-2",
103
101
  children: pages.map((page)=>/*#__PURE__*/ jsxs("div", {
104
- className: "gps-suggestion-item mb-2 flex h-[56px] cursor-pointer items-center justify-between rounded-[3px] px-[16px] py-[12px] last:mb-0 hover:bg-[#F4F4F4]",
102
+ className: " gps-suggestion-item mb-2 flex h-[56px] cursor-pointer items-center justify-between rounded-[3px] px-[16px] py-[12px] last:mb-0 hover:bg-[#F4F4F4]",
105
103
  "data-url": page,
106
104
  "aria-hidden": true,
107
105
  children: [
@@ -130,7 +128,7 @@ const PagesSuggestion = ({ isOpen , link , setLink , setOpenSuggestion })=>{
130
128
  ]
131
129
  }),
132
130
  /*#__PURE__*/ jsx("span", {
133
- className: "item-link ml-[24px] text-xs text-[#212121]",
131
+ className: "item-link ml-[24px] whitespace-nowrap text-xs text-[#212121]",
134
132
  children: page
135
133
  })
136
134
  ]
@@ -1,13 +1,16 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { useMatchMutate, useShopStore, useBuilderPreviewStore, useSectionStore, useModalStore } from '@gem-sdk/core';
3
- import { memo, useCallback, useEffect } from 'react';
3
+ import { memo, useState, useCallback, useEffect } from 'react';
4
4
  import { createFontUrl } from '../../libs/google-fonts.js';
5
5
  import { genCSS } from '../../libs/helpers/gen-css.js';
6
6
  import { getStorefrontApi } from '../../libs/get-storefront-api.js';
7
7
  import { shopifyCdnWithGoogleFonts } from '../../libs/shopify-cdn-with-google-fonts.js';
8
+ import { normalizeBuilderData } from '../../libs/helpers/normalize.js';
9
+ import { getFontsFromDataBuilder } from '../../libs/helpers/gen-fonts.js';
8
10
 
9
11
  const globalStyleId = 'global-style';
10
12
  const globalFontId = 'google-font-builder';
13
+ const fontElementSettingClassName = 'google-font-element';
11
14
  const Toolbox = ()=>{
12
15
  const matchMutate = useMatchMutate();
13
16
  const provider = useShopStore((s)=>s.provider);
@@ -22,6 +25,7 @@ const Toolbox = ()=>{
22
25
  const changeSwatches = useShopStore((s)=>s.changeSwatches);
23
26
  const changeLayoutSettings = useShopStore((s)=>s.changeLayoutSettings);
24
27
  const clearModal = useModalStore((s)=>s.clearModal);
28
+ const [fonts, setFonts] = useState([]);
25
29
  // Revalidate all query with key match query/
26
30
  const onRevalidateQuery = useCallback(()=>{
27
31
  matchMutate(/query\//, {
@@ -92,10 +96,24 @@ const Toolbox = ()=>{
92
96
  try {
93
97
  const detail = e.detail;
94
98
  if (detail.data) {
99
+ let dataBuilder = {};
95
100
  if (detail.type === 'flat') {
96
101
  initNormalizeState(detail.data);
102
+ dataBuilder = detail.data;
97
103
  } else {
98
104
  initState(detail.data);
105
+ dataBuilder = detail.data.reduceRight((prev, current)=>{
106
+ const item = normalizeBuilderData(current);
107
+ return {
108
+ ...prev,
109
+ ...item
110
+ };
111
+ }, {});
112
+ }
113
+ // Append link font to head
114
+ if (dataBuilder) {
115
+ const fonts = getFontsFromDataBuilder(dataBuilder);
116
+ setFonts(fonts);
99
117
  }
100
118
  }
101
119
  } catch {
@@ -139,11 +157,38 @@ const Toolbox = ()=>{
139
157
  data: detail.propValue,
140
158
  group: detail.group
141
159
  });
160
+ // Check link google font to <head>
161
+ if (detail.propValue?.custom) {
162
+ const customFontFamily = detail.propValue?.custom?.fontFamily;
163
+ const customFontWeight = detail.propValue?.custom?.fontWeight;
164
+ if (customFontFamily && customFontWeight) {
165
+ const isExist = fonts.find((item)=>item.family == customFontFamily && item.variants.includes(customFontWeight));
166
+ if (!isExist) {
167
+ const isFontFamily = fonts.find((item)=>item.family == customFontFamily);
168
+ if (isFontFamily) {
169
+ isFontFamily.variants.push(customFontWeight);
170
+ } else {
171
+ setFonts([
172
+ ...fonts,
173
+ {
174
+ family: customFontFamily,
175
+ variants: [
176
+ customFontWeight
177
+ ],
178
+ subsets: [],
179
+ type: 'google'
180
+ }
181
+ ]);
182
+ }
183
+ }
184
+ }
185
+ }
142
186
  }
143
187
  } catch {
144
188
  //
145
189
  }
146
- }, [
190
+ }, // eslint-disable-next-line react-hooks/exhaustive-deps
191
+ [
147
192
  changeItemPropByKey
148
193
  ]);
149
194
  // Move entity
@@ -192,6 +237,32 @@ const Toolbox = ()=>{
192
237
  }, [
193
238
  changeLayoutSettings
194
239
  ]);
240
+ useEffect(()=>{
241
+ if (fonts?.length) {
242
+ fonts.forEach((font)=>{
243
+ const fontUrl = createFontUrl([
244
+ font
245
+ ]);
246
+ if (fontUrl) {
247
+ const googleFont = document.querySelector(`.${fontElementSettingClassName}[data-font="${encodeURI(font.family)}"]`);
248
+ if (googleFont) {
249
+ if (googleFont.getAttribute('href') !== fontUrl) {
250
+ googleFont.setAttribute('href', fontUrl);
251
+ }
252
+ } else {
253
+ const link = document.createElement('link');
254
+ link.className = fontElementSettingClassName;
255
+ link.dataset.font = encodeURI(font.family);
256
+ link.href = fontUrl;
257
+ link.rel = 'stylesheet';
258
+ document.head.appendChild(link);
259
+ }
260
+ }
261
+ });
262
+ }
263
+ }, [
264
+ fonts
265
+ ]);
195
266
  useEffect(()=>{
196
267
  window.addEventListener('update-shop-info', onChangeShopInfo);
197
268
  window.addEventListener('revalidate-query', onRevalidateQuery);
package/dist/esm/index.js CHANGED
@@ -4,7 +4,6 @@ export { getHomePagePropsV2 } from './libs/api/get-home-page-props-v2.js';
4
4
  export { getPreviewProps } from './libs/api/get-preview-props.js';
5
5
  export { getBuilderProps } from './libs/api/get-builder-props.js';
6
6
  export { getProductProps } from './libs/api/get-product-props.js';
7
- export { getStaticPageProps } from './libs/api/get-static-page-props.js';
8
7
  export { getStaticPagePropsV2 } from './libs/api/get-static-page-props-v2.js';
9
8
  export { getStaticPagePropsPreview } from './libs/api/get-static-page-props-preview.js';
10
9
  export { createFetcher, createShopifyFetcher } from './libs/fetcher.js';
@@ -22,6 +21,7 @@ export { default as StaticPage } from './pages/static.js';
22
21
  export { BuilderPage } from './pages/builder.js';
23
22
  export { StaticPageV2 } from './pages/static-v2.js';
24
23
  export { getStaticPaths } from './libs/getStaticPaths.js';
24
+ export { getFontsFromDataBuilder } from './libs/helpers/gen-fonts.js';
25
25
  export { getFontFromGlobalStyle, getFonts } from './libs/google-fonts.js';
26
26
  export { getStorefrontApi } from './libs/get-storefront-api.js';
27
27
  export { ErrorBoundary } from './components/ErrorBoundary.js';
@@ -1,6 +1,6 @@
1
1
  import { PublishedThemePagesDocument, StorePropertyDocument } from '@gem-sdk/core';
2
2
  import { ShopMetaDocument } from '@gem-sdk/adapter-shopify';
3
- import { getFontFromGlobalStyle } from '../google-fonts.js';
3
+ import { getFontStyleFromPageTemplate, getFontFromGlobalStyle } from '../google-fonts.js';
4
4
  import { genCSS } from '../helpers/gen-css.js';
5
5
  import { generateManifest } from '../helpers/generate-manifres.js';
6
6
  import { getFallbackV2 } from '../helpers/get-fallback.js';
@@ -35,7 +35,8 @@ const getStaticPagePropsV2 = (fetcher, shopifyFetcher)=>async (slug)=>{
35
35
  throw new Error(`No data builder found for slug: /${slug}`);
36
36
  }
37
37
  const pageTemplate = parseBuilderTemplateV2(dataBuilder);
38
- const [fontStyle, fallback] = await Promise.all([
38
+ const [elementFontStyle, fontStyle, fallback] = await Promise.all([
39
+ getFontStyleFromPageTemplate(pageTemplate),
39
40
  getFontFromGlobalStyle(dataBuilder?.pageStyle?.data),
40
41
  getFallbackV2(fetcher, pageTemplate)
41
42
  ]);
@@ -115,6 +116,7 @@ const getStaticPagePropsV2 = (fetcher, shopifyFetcher)=>async (slug)=>{
115
116
  return serializableJson({
116
117
  themeStyle: genCSS(dataBuilder?.pageStyle?.data, mobileOnly),
117
118
  fontStyle,
119
+ elementFontStyle,
118
120
  builderData: pageTemplate,
119
121
  pageType,
120
122
  moneyFormat: shopMeta?.shop.moneyFormat ?? null,
@@ -1,3 +1,5 @@
1
+ import { getFontsFromDataBuilder } from './helpers/gen-fonts.js';
2
+
1
3
  const CHROME_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36';
2
4
  const IE_UA = 'Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko';
3
5
  async function getFontForUA(url, UA) {
@@ -64,5 +66,11 @@ const getFontFromGlobalStyle = (data, maxSize)=>{
64
66
  return '';
65
67
  }
66
68
  };
69
+ async function getFontStyleFromPageTemplate(pageTemplate) {
70
+ const fontStyle = pageTemplate.map((sectionData)=>{
71
+ return getFonts(getFontsFromDataBuilder(sectionData.data));
72
+ });
73
+ return await Promise.all(fontStyle);
74
+ }
67
75
 
68
- export { createFontUrl, getFontFromGlobalStyle, getFonts };
76
+ export { createFontUrl, getFontFromGlobalStyle, getFontStyleFromPageTemplate, getFonts };
@@ -0,0 +1,44 @@
1
+ const getFontsFromDataBuilder = (dataBuilder)=>{
2
+ const fonts = [];
3
+ for(const uid in dataBuilder){
4
+ if (Object.prototype.hasOwnProperty.call(dataBuilder, uid)) {
5
+ const data = dataBuilder[uid];
6
+ const styles = data.styles;
7
+ const settings = data.settings;
8
+ getFontFromGroupSetting(fonts, styles);
9
+ getFontFromGroupSetting(fonts, settings);
10
+ }
11
+ }
12
+ return fonts;
13
+ };
14
+ const getFontFromGroupSetting = (fonts, groupSetting)=>{
15
+ for(const attr in groupSetting){
16
+ if (Object.prototype.hasOwnProperty.call(groupSetting, attr)) {
17
+ const value = groupSetting[attr];
18
+ if (value) {
19
+ const customFontFamily = value.custom?.fontFamily;
20
+ const customFontWeight = value.custom?.fontWeight;
21
+ if (customFontFamily && customFontWeight) {
22
+ const isExist = fonts.find((item)=>item.family == customFontFamily && item.variants.includes(customFontWeight));
23
+ if (!isExist) {
24
+ const isFontFamily = fonts.find((item)=>item.family == customFontFamily);
25
+ if (isFontFamily) {
26
+ isFontFamily.variants.push(customFontWeight);
27
+ } else {
28
+ fonts.push({
29
+ family: customFontFamily,
30
+ variants: [
31
+ customFontWeight
32
+ ],
33
+ subsets: [],
34
+ type: 'google'
35
+ });
36
+ }
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ };
43
+
44
+ export { getFontsFromDataBuilder };
@@ -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 , customCodeHeader , shopToken , pageHandle })=>{
9
+ const StaticPageV2 = ({ components , builderData , sectionData , seo , themeStyle , fontStyle , elementFontStyle , customCodeHeader , shopToken , pageHandle })=>{
10
10
  const router = useRouter();
11
11
  useTrackingView(shopToken, pageHandle, router.isFallback);
12
12
  if (router.isFallback) {
@@ -49,7 +49,14 @@ const StaticPageV2 = ({ components , builderData , sectionData , seo , themeStyl
49
49
  dangerouslySetInnerHTML: {
50
50
  __html: fontStyle
51
51
  }
52
- })
52
+ }),
53
+ elementFontStyle && elementFontStyle.map((fontStyle)=>/*#__PURE__*/ jsx("style", {
54
+ "data-id": "google-element-fonts",
55
+ type: "text/css",
56
+ dangerouslySetInnerHTML: {
57
+ __html: fontStyle
58
+ }
59
+ }, fontStyle))
53
60
  ]
54
61
  }),
55
62
  /*#__PURE__*/ jsx(BuilderComponentProvider, {
@@ -12,6 +12,7 @@ type PageBuilderProps = {
12
12
  sectionData?: Record<string, SectionData$1>;
13
13
  themeStyle?: string | null;
14
14
  fontStyle?: string | null;
15
+ elementFontStyle?: string[] | null;
15
16
  swr?: React.ComponentProps<typeof SWRConfig>['value'];
16
17
  plugins?: string[];
17
18
  pageType?: ShopType.PublishedThemePageType;
@@ -77,8 +78,6 @@ declare const ProductDetailPage: React.FC<ProductPageProps & AdditionalPageBuild
77
78
 
78
79
  declare const getProductProps: (fetcher: FetchFunc) => (handle?: string) => Promise<ProductPageProps>;
79
80
 
80
- declare const getStaticPageProps: (fetcher: FetchFunc, shopifyFetcher: FetchFunc) => (slug?: string) => Promise<PageBuilderProps>;
81
-
82
81
  declare const getStaticPagePropsV2: (fetcher: FetchFunc, shopifyFetcher: FetchFunc) => (slug: string) => Promise<PageBuilderPropsV2>;
83
82
 
84
83
  declare const getStaticPagePropsPreview: (fetcher: FetchFunc, shopifyFetcher: FetchFunc) => (slug: string) => Promise<PageBuilderPropsV2>;
@@ -168,6 +167,8 @@ type FontOption = {
168
167
  declare function getFonts(fonts: FontItem[], option?: FontOption, maxSize?: number): Promise<string>;
169
168
  declare const getFontFromGlobalStyle: (data?: string, maxSize?: number) => Promise<string> | "";
170
169
 
170
+ declare const getFontsFromDataBuilder: (dataBuilder: Record<string, any>) => FontItem[];
171
+
171
172
  declare const getStorefrontApi: (handle: string, provider?: 'BIGCOMMERCE' | 'SHOPIFY') => string;
172
173
 
173
174
  type ErrorBoundaryProps = {
@@ -215,4 +216,4 @@ type Props = {
215
216
  };
216
217
  declare const TikTokPixel: ({ pixelId }: Props) => JSX.Element | null;
217
218
 
218
- export { AppPropsWithLayout, BuilderPage, CollectionDetailPage, CollectionPageProps, ErrorBoundary, ErrorFallback, FacebookPixel, GoogleAnalytic, NextPageWithLayout, Page404, Page500, PageBuilderProps, PreviewPage, ProductDetailPage, ProductPageProps, StaticPage, StaticPageProps, StaticPagePropsV2, StaticPageV2, TikTokPixel, createFetcher, createShopifyFetcher, genCSS, getBuilderProps, getCollectionProps, getFallbackV2, getFontFromGlobalStyle, getFonts, getHomePageProps, getHomePagePropsV2, getLayout, getPreviewProps, getProductProps, getStaticPageProps, getStaticPagePropsPreview, getStaticPagePropsV2, getStaticPaths, getStorefrontApi, isBot, normalizePageSectionResponseV2, parseBuilderTemplateV2, retryWithDelay, useTrackingView };
219
+ export { AppPropsWithLayout, BuilderPage, CollectionDetailPage, CollectionPageProps, ErrorBoundary, ErrorFallback, FacebookPixel, GoogleAnalytic, NextPageWithLayout, Page404, Page500, PageBuilderProps, PreviewPage, ProductDetailPage, ProductPageProps, StaticPage, StaticPageProps, StaticPagePropsV2, StaticPageV2, TikTokPixel, createFetcher, createShopifyFetcher, genCSS, getBuilderProps, getCollectionProps, getFallbackV2, getFontFromGlobalStyle, getFonts, getFontsFromDataBuilder, getHomePageProps, getHomePagePropsV2, getLayout, getPreviewProps, getProductProps, getStaticPagePropsPreview, getStaticPagePropsV2, getStaticPaths, getStorefrontApi, isBot, normalizePageSectionResponseV2, parseBuilderTemplateV2, retryWithDelay, useTrackingView };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gem-sdk/pages",
3
- "version": "1.12.0-next.51",
3
+ "version": "1.12.0-next.62",
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.12.0-next.50",
28
+ "@gem-sdk/core": "1.12.0-next.59",
29
29
  "@gem-sdk/plugin-cookie-bar": "*",
30
30
  "@gem-sdk/plugin-quick-view": "*",
31
31
  "@gem-sdk/plugin-sticky-add-to-cart": "*"