@tapcart/mobile-components 0.15.2 → 0.16.1

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 (45) hide show
  1. package/dist/components/core/button.d.ts +37 -0
  2. package/dist/components/core/button.d.ts.map +1 -0
  3. package/dist/components/core/button.js +44 -0
  4. package/dist/components/core/html.d.ts +15 -0
  5. package/dist/components/core/html.d.ts.map +1 -0
  6. package/dist/components/core/html.js +45 -0
  7. package/dist/components/core/text.d.ts +29 -0
  8. package/dist/components/core/text.d.ts.map +1 -0
  9. package/dist/components/core/text.js +88 -0
  10. package/dist/components/hooks/use-nosto-recommendation.d.ts +4 -0
  11. package/dist/components/hooks/use-nosto-recommendation.d.ts.map +1 -1
  12. package/dist/components/hooks/use-nosto-recommendation.js +116 -23
  13. package/dist/components/hooks/use-nosto-recommendation.queries.d.ts +0 -1
  14. package/dist/components/hooks/use-nosto-recommendation.queries.d.ts.map +1 -1
  15. package/dist/components/hooks/use-nosto-recommendation.queries.js +0 -3
  16. package/dist/components/hooks/use-nosto-recommendation.test.js +168 -171
  17. package/dist/components/hooks/use-products.d.ts.map +1 -1
  18. package/dist/components/hooks/use-products.js +24 -4
  19. package/dist/components/hooks/use-products.test.d.ts +2 -0
  20. package/dist/components/hooks/use-products.test.d.ts.map +1 -0
  21. package/dist/components/hooks/use-products.test.js +129 -0
  22. package/dist/components/hooks/use-search-analytics-open-product.d.ts +3 -2
  23. package/dist/components/hooks/use-search-analytics-open-product.d.ts.map +1 -1
  24. package/dist/components/libs/cache/ProductsLocalStorage.d.ts.map +1 -1
  25. package/dist/components/libs/cache/ProductsLocalStorage.js +32 -0
  26. package/dist/components/libs/cache/ProductsLocalStorage.test.d.ts +2 -0
  27. package/dist/components/libs/cache/ProductsLocalStorage.test.d.ts.map +1 -0
  28. package/dist/components/libs/cache/ProductsLocalStorage.test.js +110 -0
  29. package/dist/core/index.d.ts +99 -0
  30. package/dist/core/index.d.ts.map +1 -0
  31. package/dist/core/index.js +98 -0
  32. package/dist/index.d.ts +5 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +4 -1
  35. package/dist/lib/get-data.d.ts +27 -0
  36. package/dist/lib/get-data.d.ts.map +1 -0
  37. package/dist/lib/get-data.js +65 -0
  38. package/dist/lib/get-data.test.d.ts +2 -0
  39. package/dist/lib/get-data.test.d.ts.map +1 -0
  40. package/dist/lib/get-data.test.js +74 -0
  41. package/dist/lib/to-html.d.ts +9 -0
  42. package/dist/lib/to-html.d.ts.map +1 -0
  43. package/dist/lib/to-html.js +102 -0
  44. package/dist/styles.css +6 -0
  45. package/package.json +1 -1
@@ -0,0 +1,65 @@
1
+ import { toHtml } from "./to-html";
2
+ /**
3
+ * getData — Universal content resolver for V2 blocks.
4
+ *
5
+ * Accepts either:
6
+ * - a static HTML string (typography `content: "text"`) → returned as-is
7
+ * - a DataSource `{ source, value }` (typography `content: "data"`, or a
8
+ * data-source field) → resolved against Tapcart variables
9
+ * - null/undefined (typography `content: "none"`, i.e. no `value`) → null
10
+ *
11
+ * This lets a block render every typography field identically, with no
12
+ * conditional branching on the `content` mode. `content: "none"` returns null
13
+ * (not "") so falsy guards work cleanly and no empty wrapper renders:
14
+ * {getData(variables, field?.value) && (
15
+ * <Html html={getData(variables, field?.value)} />
16
+ * )}
17
+ *
18
+ * Note: a *data-source* that resolves to nothing still returns "" (it was a
19
+ * real binding that came back empty), only an absent value returns null.
20
+ *
21
+ * Never throws — returns empty string on any error.
22
+ */
23
+ export function getData(variables, data) {
24
+ var _a, _b, _c;
25
+ try {
26
+ // No value (content: "none") — return null so falsy guards skip rendering.
27
+ if (data == null)
28
+ return null;
29
+ // Static rich-text (content: "text") — already HTML, pass through.
30
+ if (typeof data === "string")
31
+ return data;
32
+ if (!(data === null || data === void 0 ? void 0 : data.source) || !(data === null || data === void 0 ? void 0 : data.value))
33
+ return "";
34
+ if (data.source === "custom")
35
+ return data.value;
36
+ const obj = variables === null || variables === void 0 ? void 0 : variables[data.source];
37
+ if (!obj)
38
+ return "";
39
+ if (data.value.startsWith("metafields.")) {
40
+ const parts = data.value.split(".");
41
+ if (parts.length === 3) {
42
+ return toHtml(((_b = (_a = obj.metafields) === null || _a === void 0 ? void 0 : _a[parts[1]]) === null || _b === void 0 ? void 0 : _b[parts[2]]) || "");
43
+ }
44
+ return "";
45
+ }
46
+ const field = data.value;
47
+ if (field === "description")
48
+ return obj.descriptionHtml || obj.description || "";
49
+ if (field === "tags")
50
+ return (obj.tags || []).join(", ");
51
+ // Handle nested paths like priceRange.minVariantPrice.amount
52
+ if (field.includes(".")) {
53
+ const val = field.split(".").reduce((o, k) => o === null || o === void 0 ? void 0 : o[k], obj);
54
+ if (Array.isArray(val))
55
+ return val.join(", ");
56
+ return String(val !== null && val !== void 0 ? val : "");
57
+ }
58
+ // Nullish coalescing (not ||) so legitimate 0 / false values survive
59
+ // instead of being flattened to an empty string.
60
+ return String((_c = obj[field]) !== null && _c !== void 0 ? _c : "");
61
+ }
62
+ catch (_d) {
63
+ return "";
64
+ }
65
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=get-data.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-data.test.d.ts","sourceRoot":"","sources":["../../lib/get-data.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
1
+ /* eslint-env jest */
2
+ import { getData } from "./get-data";
3
+ describe("getData", () => {
4
+ const variables = {
5
+ product: {
6
+ descriptionHtml: "<p>Hello</p>",
7
+ description: "Hello",
8
+ tags: ["new", "sale"],
9
+ title: "Cool Product",
10
+ totalInventory: 0,
11
+ availableForSale: false,
12
+ metafields: {
13
+ custom: { subtitle: "A subtitle" },
14
+ },
15
+ priceRange: {
16
+ minVariantPrice: { amount: "19.99" },
17
+ },
18
+ },
19
+ };
20
+ describe("static rich-text (content: text)", () => {
21
+ it("returns a plain HTML string as-is", () => {
22
+ expect(getData(variables, "<p><strong>Hi</strong></p>")).toBe("<p><strong>Hi</strong></p>");
23
+ });
24
+ it("returns an empty string as-is", () => {
25
+ expect(getData(variables, "")).toBe("");
26
+ });
27
+ it("passes through even when variables is undefined", () => {
28
+ expect(getData(undefined, "<p>Static</p>")).toBe("<p>Static</p>");
29
+ });
30
+ });
31
+ describe("no value (content: none)", () => {
32
+ it("returns null for null (falsy-guard friendly)", () => {
33
+ expect(getData(variables, null)).toBeNull();
34
+ });
35
+ it("returns null for undefined", () => {
36
+ expect(getData(variables, undefined)).toBeNull();
37
+ });
38
+ it("returns empty string (not null) for a real data-source that came back empty", () => {
39
+ expect(getData(variables, { source: "", value: "" })).toBe("");
40
+ });
41
+ });
42
+ describe("data-source (content: data)", () => {
43
+ it("returns custom value as-is", () => {
44
+ expect(getData(variables, { source: "custom", value: "<p>x</p>" })).toBe("<p>x</p>");
45
+ });
46
+ it("resolves description", () => {
47
+ expect(getData(variables, { source: "product", value: "description" })).toBe("<p>Hello</p>");
48
+ });
49
+ it("resolves tags joined", () => {
50
+ expect(getData(variables, { source: "product", value: "tags" })).toBe("new, sale");
51
+ });
52
+ it("resolves a metafield", () => {
53
+ expect(getData(variables, {
54
+ source: "product",
55
+ value: "metafields.custom.subtitle",
56
+ })).toBe("<p>A subtitle</p>");
57
+ });
58
+ it("resolves a nested path", () => {
59
+ expect(getData(variables, {
60
+ source: "product",
61
+ value: "priceRange.minVariantPrice.amount",
62
+ })).toBe("19.99");
63
+ });
64
+ it("returns empty string when source object is missing", () => {
65
+ expect(getData(variables, { source: "customer", value: "firstName" })).toBe("");
66
+ });
67
+ it("preserves a numeric zero (not flattened to empty string)", () => {
68
+ expect(getData(variables, { source: "product", value: "totalInventory" })).toBe("0");
69
+ });
70
+ it("preserves a boolean false (not flattened to empty string)", () => {
71
+ expect(getData(variables, { source: "product", value: "availableForSale" })).toBe("false");
72
+ });
73
+ });
74
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * toHtml — Universal content-to-HTML converter.
3
+ *
4
+ * Accepts any string: Shopify rich text JSON, plain HTML, or plain text.
5
+ * Always returns safe HTML ready for the Html component.
6
+ * Never throws — returns empty string on any error.
7
+ */
8
+ export declare function toHtml(content: any): string;
9
+ //# sourceMappingURL=to-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-html.d.ts","sourceRoot":"","sources":["../../lib/to-html.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2EH,wBAAgB,MAAM,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAkB3C"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * toHtml — Universal content-to-HTML converter.
3
+ *
4
+ * Accepts any string: Shopify rich text JSON, plain HTML, or plain text.
5
+ * Always returns safe HTML ready for the Html component.
6
+ * Never throws — returns empty string on any error.
7
+ */
8
+ function escHtml(s) {
9
+ return String(s)
10
+ .replace(/&/g, "&amp;")
11
+ .replace(/</g, "&lt;")
12
+ .replace(/>/g, "&gt;")
13
+ .replace(/"/g, "&quot;");
14
+ }
15
+ function convertInline(node) {
16
+ try {
17
+ if (!(node === null || node === void 0 ? void 0 : node.type))
18
+ return "";
19
+ if (node.type === "text") {
20
+ let t = escHtml(node.value || "");
21
+ if (node.bold)
22
+ t = `<strong>${t}</strong>`;
23
+ if (node.italic)
24
+ t = `<em>${t}</em>`;
25
+ return t;
26
+ }
27
+ if (node.type === "link") {
28
+ const href = escHtml(node.url || "");
29
+ const children = (node.children || []).map(convertInline).join("");
30
+ return `<a href="${href}">${children}</a>`;
31
+ }
32
+ return "";
33
+ }
34
+ catch (_a) {
35
+ return "";
36
+ }
37
+ }
38
+ function convertNode(node) {
39
+ try {
40
+ if (!(node === null || node === void 0 ? void 0 : node.type))
41
+ return "";
42
+ switch (node.type) {
43
+ case "paragraph":
44
+ return `<p>${(node.children || []).map(convertInline).join("")}</p>`;
45
+ case "list": {
46
+ const tag = node.listType === "ordered" ? "ol" : "ul";
47
+ const items = (node.children || [])
48
+ .filter((c) => c.type === "list-item")
49
+ .map((item) => `<li>${(item.children || []).map(convertInline).join("")}</li>`)
50
+ .join("");
51
+ return `<${tag}>${items}</${tag}>`;
52
+ }
53
+ case "heading": {
54
+ const lvl = Math.min(Math.max(node.level || 1, 1), 6);
55
+ return `<h${lvl}>${(node.children || [])
56
+ .map(convertInline)
57
+ .join("")}</h${lvl}>`;
58
+ }
59
+ default:
60
+ return "";
61
+ }
62
+ }
63
+ catch (_a) {
64
+ return "";
65
+ }
66
+ }
67
+ function parseShopifyRichText(text) {
68
+ try {
69
+ const obj = JSON.parse(text);
70
+ if (!obj || obj.type !== "root" || !Array.isArray(obj.children))
71
+ return null;
72
+ return obj.children.map(convertNode).join("");
73
+ }
74
+ catch (_a) {
75
+ return null;
76
+ }
77
+ }
78
+ function isHtml(text) {
79
+ return /<[a-z][\s\S]*>/i.test(text);
80
+ }
81
+ export function toHtml(content) {
82
+ try {
83
+ if (content == null)
84
+ return "";
85
+ if (typeof content !== "string")
86
+ return "";
87
+ if (content.trim() === "")
88
+ return "";
89
+ // Try Shopify rich text JSON first
90
+ const richText = parseShopifyRichText(content);
91
+ if (richText !== null)
92
+ return richText;
93
+ // Already HTML, pass through
94
+ if (isHtml(content))
95
+ return content;
96
+ // Plain text, wrap in paragraph
97
+ return `<p>${escHtml(content)}</p>`;
98
+ }
99
+ catch (_a) {
100
+ return "";
101
+ }
102
+ }
package/dist/styles.css CHANGED
@@ -2917,6 +2917,9 @@ video {
2917
2917
  .opacity-100 {
2918
2918
  opacity: 1;
2919
2919
  }
2920
+ .opacity-25 {
2921
+ opacity: 0.25;
2922
+ }
2920
2923
  .opacity-50 {
2921
2924
  opacity: 0.5;
2922
2925
  }
@@ -2926,6 +2929,9 @@ video {
2926
2929
  .opacity-70 {
2927
2930
  opacity: 0.7;
2928
2931
  }
2932
+ .opacity-75 {
2933
+ opacity: 0.75;
2934
+ }
2929
2935
  .opacity-\[0\.42\] {
2930
2936
  opacity: 0.42;
2931
2937
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tapcart/mobile-components",
3
- "version": "0.15.2",
3
+ "version": "0.16.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "style": "dist/styles.css",