@datlv-trustshop/shopify-inapp-components 0.2.13 → 0.2.14

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.
@@ -12,4 +12,5 @@ export interface TopBannerProps extends UseSDKOptions {
12
12
  onAction?: (banner: BannerItem) => void;
13
13
  }
14
14
  export declare const TopBanner: React.FC<TopBannerProps>;
15
+ export default TopBanner;
15
16
  //# sourceMappingURL=TopBanner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TopBanner.d.ts","sourceRoot":"","sources":["../../src/components/TopBanner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AASnD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGhE,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,yCAAyC;IACzC,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,yBAAyB;IACzB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;CACzC;AA6FD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA8J9C,CAAC"}
1
+ {"version":3,"file":"TopBanner.d.ts","sourceRoot":"","sources":["../../src/components/TopBanner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AASnD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGhE,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,yCAAyC;IACzC,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,yBAAyB;IACzB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;CACzC;AAED,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA6H9C,CAAC;AAEF,eAAe,SAAS,CAAC"}
@@ -5,81 +5,32 @@ import { ExternalIcon } from "@shopify/polaris-icons";
5
5
  import { useTopBanner } from "../hooks/useBanner";
6
6
  import { useSDK } from "../core/SDKManager";
7
7
  import { COMPONENT_DEFAULTS } from "../config/component-defaults";
8
- const BANNER_MIN_HEIGHT = 100;
9
- const TopBannerSkeleton = ({ className, visible }) => {
10
- // When not visible, return zero-space container
11
- if (!visible) {
12
- return (_jsx("div", { className: className, style: {
13
- width: "100%",
14
- height: 0,
15
- overflow: "hidden",
16
- position: "absolute",
17
- pointerEvents: "none",
18
- }, "aria-hidden": "true" }));
19
- }
20
- return (_jsxs("div", { className: className, style: {
21
- width: "100%",
22
- }, children: [_jsxs("div", { style: {
23
- minHeight: `${BANNER_MIN_HEIGHT}px`,
24
- padding: "16px",
25
- borderRadius: "8px",
26
- border: "1px solid var(--p-color-border-subdued)",
27
- backgroundColor: "var(--p-color-bg-surface)",
28
- display: "flex",
29
- alignItems: "center",
30
- gap: "16px",
31
- }, children: [_jsx("div", { style: {
32
- width: 72,
33
- height: 72,
34
- borderRadius: "8px",
35
- backgroundColor: "var(--p-color-bg-surface-secondary)",
36
- flexShrink: 0,
37
- animation: "pulse 1.5s ease-in-out infinite",
38
- } }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: {
39
- height: 20,
40
- width: "30%",
41
- backgroundColor: "var(--p-color-bg-surface-secondary)",
42
- borderRadius: "4px",
43
- marginBottom: "8px",
44
- animation: "pulse 1.5s ease-in-out infinite",
45
- } }), _jsx("div", { style: {
46
- height: 16,
47
- width: "70%",
48
- backgroundColor: "var(--p-color-bg-surface-secondary)",
49
- borderRadius: "4px",
50
- animation: "pulse 1.5s ease-in-out infinite",
51
- } })] })] }), _jsx("style", { children: `
52
- @keyframes pulse {
53
- 0% {
54
- opacity: 1;
55
- }
56
- 50% {
57
- opacity: 0.6;
58
- }
59
- 100% {
60
- opacity: 1;
61
- }
62
- }
63
- ` })] }));
64
- };
65
8
  export const TopBanner = ({ className = "", open = true, onClose, onAction,
66
- // SDK options (optional - for customization)
9
+ // SDK options (optional)
67
10
  shopInfo, locale, translations, config, }) => {
68
- // Connect to SDK Manager (auto-initializes)
69
- const sdk = useSDK({ shopInfo, locale, translations, config });
11
+ // Early return if not open - no rendering at all to prevent CLS
12
+ if (!open) {
13
+ return null;
14
+ }
15
+ // Initialize SDK (auto-initializes if needed)
16
+ useSDK({ shopInfo, locale, translations, config });
70
17
  const banner = useTopBanner();
71
- const [isDismissed, setIsDismissed] = useState(false);
72
- const [dismissChecked, setDismissChecked] = useState(false);
73
18
  // Use internal defaults
74
19
  const { closable } = COMPONENT_DEFAULTS.topBanner;
75
- const DISMISS_KEY = "ts-top-banner-dismissed";
76
- const DISMISS_DURATION = 7 * 24 * 60 * 60 * 1000;
20
+ // Dismiss state management
21
+ const [isDismissed, setIsDismissed] = useState(false);
22
+ const DISMISS_KEY = `trustshop_banner_dismissed_${banner?.id || "default"}`;
23
+ const DISMISS_DURATION = 7 * 24 * 60 * 60 * 1000; // 7 days
24
+ // Check dismiss state on mount
77
25
  useEffect(() => {
78
- const dismissedData = localStorage.getItem(DISMISS_KEY);
79
- if (dismissedData) {
26
+ if (!banner)
27
+ return;
28
+ const dismissDataStr = localStorage.getItem(DISMISS_KEY);
29
+ if (dismissDataStr) {
80
30
  try {
81
- const { timestamp } = JSON.parse(dismissedData);
31
+ const dismissData = JSON.parse(dismissDataStr);
82
32
  const now = Date.now();
33
+ const timestamp = dismissData.timestamp || 0;
83
34
  if (now - timestamp < DISMISS_DURATION) {
84
35
  setIsDismissed(true);
85
36
  }
@@ -91,8 +42,7 @@ shopInfo, locale, translations, config, }) => {
91
42
  localStorage.removeItem(DISMISS_KEY);
92
43
  }
93
44
  }
94
- setDismissChecked(true);
95
- }, []);
45
+ }, [banner, DISMISS_KEY, DISMISS_DURATION]);
96
46
  const handleClose = () => {
97
47
  const dismissData = {
98
48
  timestamp: Date.now(),
@@ -110,38 +60,14 @@ shopInfo, locale, translations, config, }) => {
110
60
  onAction?.(banner);
111
61
  }
112
62
  };
113
- const shouldShow = open && !isDismissed;
114
- const showSkeleton = shouldShow && (!dismissChecked || !sdk.initialized || sdk.loading || sdk.isInitializing);
115
- const showBanner = shouldShow && dismissChecked && sdk.initialized && !sdk.loading && !sdk.isInitializing && banner;
116
- // Always render skeleton during loading, but control visibility
117
- if (!dismissChecked || !sdk.initialized || sdk.loading || sdk.isInitializing) {
118
- return _jsx(TopBannerSkeleton, { className: className, visible: showSkeleton });
119
- }
120
- // No banner data available - render invisible container to maintain mount
121
- if (!banner) {
122
- return (_jsx("div", { className: className, style: {
123
- width: "100%",
124
- height: 0,
125
- overflow: "hidden",
126
- position: "absolute",
127
- pointerEvents: "none",
128
- }, "aria-hidden": "true" }));
129
- }
130
- // Removed custom render function - using SDK default rendering
131
- // Default banner render with visibility control
132
- // When not showing, render zero-space container
133
- if (!showBanner) {
134
- return (_jsx("div", { className: className, style: {
135
- width: "100%",
136
- height: 0,
137
- overflow: "hidden",
138
- position: "absolute",
139
- pointerEvents: "none",
140
- }, "aria-hidden": "true" }));
63
+ // Don't render if dismissed or no banner data
64
+ if (isDismissed || !banner) {
65
+ return null;
141
66
  }
67
+ // Render the actual banner
142
68
  return (_jsx("div", { className: className, style: {
143
69
  width: "100%",
144
- minHeight: `${BANNER_MIN_HEIGHT}px`,
145
70
  }, children: _jsx(Banner, { hideIcon: true, onDismiss: closable ? handleClose : undefined, children: _jsxs(InlineStack, { gap: "400", blockAlign: "center", wrap: false, children: [banner.icon && (_jsx("div", { style: { flexShrink: 0 }, children: _jsx("img", { src: banner.icon, alt: "", width: 72, height: 72, style: { borderRadius: "8px" }, loading: "eager" }) })), _jsx("div", { style: { flex: 1, minWidth: 0 }, children: _jsxs(BlockStack, { gap: "100", align: "space-between", children: [_jsxs(BlockStack, { gap: "100", children: [_jsx(Text, { variant: "headingSm", as: "h3", children: banner.title }), banner.description && (_jsx(Text, { variant: "bodyMd", tone: "subdued", as: "p", children: banner.description }))] }), banner.link && (_jsx("div", { style: { flexShrink: 0 }, children: _jsx(Button, { icon: ExternalIcon, onClick: handleAction, children: banner.linkText || "Learn More" }) }))] }) })] }) }) }));
146
71
  };
72
+ export default TopBanner;
147
73
  //# sourceMappingURL=TopBanner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TopBanner.js","sourceRoot":"","sources":["../../src/components/TopBanner.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useState, useEffect } from \"react\";\nimport {\n Banner,\n Button,\n Text,\n BlockStack,\n InlineStack,\n} from \"@shopify/polaris\";\nimport { ExternalIcon } from \"@shopify/polaris-icons\";\nimport { BannerItem } from \"../types\";\nimport { useTopBanner } from \"../hooks/useBanner\";\nimport { useSDK, type UseSDKOptions } from \"../core/SDKManager\";\nimport { COMPONENT_DEFAULTS } from \"../config/component-defaults\";\n\nexport interface TopBannerProps extends UseSDKOptions {\n /** Optional CSS class for styling */\n className?: string;\n \n /** Control visibility (default: true) */\n open?: boolean;\n \n /** Handle close event */\n onClose?: () => void;\n \n /** Handle primary action click */\n onAction?: (banner: BannerItem) => void;\n}\n\nconst BANNER_MIN_HEIGHT = 100;\n\nconst TopBannerSkeleton: React.FC<{ \n className?: string;\n visible: boolean;\n}> = ({ className, visible }) => {\n // When not visible, return zero-space container\n if (!visible) {\n return (\n <div \n className={className}\n style={{\n width: \"100%\",\n height: 0,\n overflow: \"hidden\",\n position: \"absolute\",\n pointerEvents: \"none\",\n }}\n aria-hidden=\"true\"\n />\n );\n }\n\n return (\n <div \n className={className} \n style={{ \n width: \"100%\",\n }}\n >\n <div\n style={{\n minHeight: `${BANNER_MIN_HEIGHT}px`,\n padding: \"16px\",\n borderRadius: \"8px\",\n border: \"1px solid var(--p-color-border-subdued)\",\n backgroundColor: \"var(--p-color-bg-surface)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: \"16px\",\n }}\n >\n <div\n style={{\n width: 72,\n height: 72,\n borderRadius: \"8px\",\n backgroundColor: \"var(--p-color-bg-surface-secondary)\",\n flexShrink: 0,\n animation: \"pulse 1.5s ease-in-out infinite\",\n }}\n />\n <div style={{ flex: 1, minWidth: 0 }}>\n <div\n style={{\n height: 20,\n width: \"30%\",\n backgroundColor: \"var(--p-color-bg-surface-secondary)\",\n borderRadius: \"4px\",\n marginBottom: \"8px\",\n animation: \"pulse 1.5s ease-in-out infinite\",\n }}\n />\n <div\n style={{\n height: 16,\n width: \"70%\",\n backgroundColor: \"var(--p-color-bg-surface-secondary)\",\n borderRadius: \"4px\",\n animation: \"pulse 1.5s ease-in-out infinite\",\n }}\n />\n </div>\n </div>\n <style>{`\n @keyframes pulse {\n 0% {\n opacity: 1;\n }\n 50% {\n opacity: 0.6;\n }\n 100% {\n opacity: 1;\n }\n }\n `}</style>\n </div>\n );\n};\n\nexport const TopBanner: React.FC<TopBannerProps> = ({\n className = \"\",\n open = true,\n onClose,\n onAction,\n // SDK options (optional - for customization)\n shopInfo,\n locale,\n translations,\n config,\n}) => {\n // Connect to SDK Manager (auto-initializes)\n const sdk = useSDK({ shopInfo, locale, translations, config });\n \n const banner = useTopBanner();\n const [isDismissed, setIsDismissed] = useState(false);\n const [dismissChecked, setDismissChecked] = useState(false);\n\n // Use internal defaults\n const { closable } = COMPONENT_DEFAULTS.topBanner;\n const DISMISS_KEY = \"ts-top-banner-dismissed\";\n const DISMISS_DURATION = 7 * 24 * 60 * 60 * 1000;\n\n useEffect(() => {\n const dismissedData = localStorage.getItem(DISMISS_KEY);\n if (dismissedData) {\n try {\n const { timestamp } = JSON.parse(dismissedData);\n const now = Date.now();\n\n if (now - timestamp < DISMISS_DURATION) {\n setIsDismissed(true);\n } else {\n localStorage.removeItem(DISMISS_KEY);\n }\n } catch (error) {\n localStorage.removeItem(DISMISS_KEY);\n }\n }\n setDismissChecked(true);\n }, []);\n\n const handleClose = () => {\n const dismissData = {\n timestamp: Date.now(),\n dismissed: true,\n };\n localStorage.setItem(DISMISS_KEY, JSON.stringify(dismissData));\n\n setIsDismissed(true);\n onClose?.();\n };\n\n const handleAction = () => {\n if (banner) {\n if (banner.link) {\n window.open(\n banner.link,\n banner.openType === \"new_tab\" ? \"_blank\" : \"_self\",\n );\n }\n onAction?.(banner);\n }\n };\n\n const shouldShow = open && !isDismissed;\n const showSkeleton = shouldShow && (!dismissChecked || !sdk.initialized || sdk.loading || sdk.isInitializing);\n const showBanner = shouldShow && dismissChecked && sdk.initialized && !sdk.loading && !sdk.isInitializing && banner;\n\n // Always render skeleton during loading, but control visibility\n if (!dismissChecked || !sdk.initialized || sdk.loading || sdk.isInitializing) {\n return <TopBannerSkeleton className={className} visible={showSkeleton} />;\n }\n\n // No banner data available - render invisible container to maintain mount\n if (!banner) {\n return (\n <div \n className={className}\n style={{\n width: \"100%\",\n height: 0,\n overflow: \"hidden\",\n position: \"absolute\",\n pointerEvents: \"none\",\n }}\n aria-hidden=\"true\"\n />\n );\n }\n\n // Removed custom render function - using SDK default rendering\n\n // Default banner render with visibility control\n // When not showing, render zero-space container\n if (!showBanner) {\n return (\n <div \n className={className}\n style={{\n width: \"100%\",\n height: 0,\n overflow: \"hidden\",\n position: \"absolute\",\n pointerEvents: \"none\",\n }}\n aria-hidden=\"true\"\n />\n );\n }\n\n return (\n <div\n className={className}\n style={{\n width: \"100%\",\n minHeight: `${BANNER_MIN_HEIGHT}px`,\n }}\n >\n <Banner hideIcon onDismiss={closable ? handleClose : undefined}>\n <InlineStack gap=\"400\" blockAlign=\"center\" wrap={false}>\n {banner.icon && (\n <div style={{ flexShrink: 0 }}>\n <img\n src={banner.icon}\n alt=\"\"\n width={72}\n height={72}\n style={{ borderRadius: \"8px\" }}\n loading=\"eager\"\n />\n </div>\n )}\n <div style={{ flex: 1, minWidth: 0 }}>\n <BlockStack gap=\"100\" align=\"space-between\">\n <BlockStack gap=\"100\">\n <Text variant=\"headingSm\" as=\"h3\">\n {banner.title}\n </Text>\n {banner.description && (\n <Text variant=\"bodyMd\" tone=\"subdued\" as=\"p\">\n {banner.description}\n </Text>\n )}\n </BlockStack>\n {banner.link && (\n <div style={{ flexShrink: 0 }}>\n <Button icon={ExternalIcon} onClick={handleAction}>\n {banner.linkText || \"Learn More\"}\n </Button>\n </div>\n )}\n </BlockStack>\n </div>\n </InlineStack>\n </Banner>\n </div>\n );\n};"]}
1
+ {"version":3,"file":"TopBanner.js","sourceRoot":"","sources":["../../src/components/TopBanner.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useState, useEffect } from \"react\";\nimport {\n Banner,\n Button,\n Text,\n BlockStack,\n InlineStack,\n} from \"@shopify/polaris\";\nimport { ExternalIcon } from \"@shopify/polaris-icons\";\nimport { BannerItem } from \"../types\";\nimport { useTopBanner } from \"../hooks/useBanner\";\nimport { useSDK, type UseSDKOptions } from \"../core/SDKManager\";\nimport { COMPONENT_DEFAULTS } from \"../config/component-defaults\";\n\nexport interface TopBannerProps extends UseSDKOptions {\n /** Optional CSS class for styling */\n className?: string;\n\n /** Control visibility (default: true) */\n open?: boolean;\n\n /** Handle close event */\n onClose?: () => void;\n\n /** Handle primary action click */\n onAction?: (banner: BannerItem) => void;\n}\n\nexport const TopBanner: React.FC<TopBannerProps> = ({\n className = \"\",\n open = true,\n onClose,\n onAction,\n // SDK options (optional)\n shopInfo,\n locale,\n translations,\n config,\n}) => {\n // Early return if not open - no rendering at all to prevent CLS\n if (!open) {\n return null;\n }\n\n // Initialize SDK (auto-initializes if needed)\n useSDK({ shopInfo, locale, translations, config });\n const banner = useTopBanner();\n\n // Use internal defaults\n const { closable } = COMPONENT_DEFAULTS.topBanner;\n\n // Dismiss state management\n const [isDismissed, setIsDismissed] = useState(false);\n const DISMISS_KEY = `trustshop_banner_dismissed_${banner?.id || \"default\"}`;\n const DISMISS_DURATION = 7 * 24 * 60 * 60 * 1000; // 7 days\n\n // Check dismiss state on mount\n useEffect(() => {\n if (!banner) return;\n\n const dismissDataStr = localStorage.getItem(DISMISS_KEY);\n if (dismissDataStr) {\n try {\n const dismissData = JSON.parse(dismissDataStr);\n const now = Date.now();\n const timestamp = dismissData.timestamp || 0;\n\n if (now - timestamp < DISMISS_DURATION) {\n setIsDismissed(true);\n } else {\n localStorage.removeItem(DISMISS_KEY);\n }\n } catch (error) {\n localStorage.removeItem(DISMISS_KEY);\n }\n }\n }, [banner, DISMISS_KEY, DISMISS_DURATION]);\n\n const handleClose = () => {\n const dismissData = {\n timestamp: Date.now(),\n dismissed: true,\n };\n localStorage.setItem(DISMISS_KEY, JSON.stringify(dismissData));\n\n setIsDismissed(true);\n onClose?.();\n };\n\n const handleAction = () => {\n if (banner) {\n if (banner.link) {\n window.open(\n banner.link,\n banner.openType === \"new_tab\" ? \"_blank\" : \"_self\",\n );\n }\n onAction?.(banner);\n }\n };\n\n // Don't render if dismissed or no banner data\n if (isDismissed || !banner) {\n return null;\n }\n\n // Render the actual banner\n return (\n <div\n className={className}\n style={{\n width: \"100%\",\n }}\n >\n <Banner hideIcon onDismiss={closable ? handleClose : undefined}>\n <InlineStack gap=\"400\" blockAlign=\"center\" wrap={false}>\n {banner.icon && (\n <div style={{ flexShrink: 0 }}>\n <img\n src={banner.icon}\n alt=\"\"\n width={72}\n height={72}\n style={{ borderRadius: \"8px\" }}\n loading=\"eager\"\n />\n </div>\n )}\n <div style={{ flex: 1, minWidth: 0 }}>\n <BlockStack gap=\"100\" align=\"space-between\">\n <BlockStack gap=\"100\">\n <Text variant=\"headingSm\" as=\"h3\">\n {banner.title}\n </Text>\n {banner.description && (\n <Text variant=\"bodyMd\" tone=\"subdued\" as=\"p\">\n {banner.description}\n </Text>\n )}\n </BlockStack>\n {banner.link && (\n <div style={{ flexShrink: 0 }}>\n <Button icon={ExternalIcon} onClick={handleAction}>\n {banner.linkText || \"Learn More\"}\n </Button>\n </div>\n )}\n </BlockStack>\n </div>\n </InlineStack>\n </Banner>\n </div>\n );\n};\n\nexport default TopBanner;"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datlv-trustshop/shopify-inapp-components",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "private": false,
5
5
  "description": "React TypeScript components for Shopify in-app dashboard content",
6
6
  "main": "dist/index.js",