@zappie/theme-base 0.1.0 → 0.1.2
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.
- package/CHANGELOG.md +39 -0
- package/README.md +26 -53
- package/dist/address.js +99 -2
- package/dist/address.js.map +7 -1
- package/dist/cart/address-sheet.js +143 -0
- package/dist/cart/address-sheet.js.map +7 -0
- package/dist/cart/cart-issues.js +72 -0
- package/dist/cart/cart-issues.js.map +7 -0
- package/dist/cart/cart-line-groups.js +34 -0
- package/dist/cart/cart-line-groups.js.map +7 -0
- package/dist/{chunk-FB5DRCU7.js → cart/cart-line.js} +9 -70
- package/dist/cart/cart-line.js.map +7 -0
- package/dist/cart/cart-lines.js +27 -0
- package/dist/cart/cart-lines.js.map +7 -0
- package/dist/cart/cart-schedule.js +24 -0
- package/dist/cart/cart-schedule.js.map +7 -0
- package/dist/cart/cart-sheet.js +35 -0
- package/dist/cart/cart-sheet.js.map +7 -0
- package/dist/cart/checkout-cta.js +81 -0
- package/dist/cart/checkout-cta.js.map +7 -0
- package/dist/cart/checkout-state.js +74 -0
- package/dist/cart/checkout-state.js.map +7 -0
- package/dist/cart/clear-cart.js +44 -0
- package/dist/cart/clear-cart.js.map +7 -0
- package/dist/cart/customer-details.js +70 -0
- package/dist/cart/customer-details.js.map +7 -0
- package/dist/cart/delivery-distance.js +22 -0
- package/dist/cart/delivery-distance.js.map +7 -0
- package/dist/cart/empty-cart.js +14 -0
- package/dist/cart/empty-cart.js.map +7 -0
- package/dist/cart/fulfilment-panel.js +83 -0
- package/dist/cart/fulfilment-panel.js.map +7 -0
- package/dist/cart/line-editor.js +35 -0
- package/dist/cart/line-editor.js.map +7 -0
- package/dist/cart/line-options.js +96 -0
- package/dist/cart/line-options.js.map +7 -0
- package/dist/cart/loading.js +17 -0
- package/dist/cart/loading.js.map +7 -0
- package/dist/cart/promo-sheet.js +103 -0
- package/dist/cart/promo-sheet.js.map +7 -0
- package/dist/cart/region-picker.js +40 -0
- package/dist/cart/region-picker.js.map +7 -0
- package/dist/cart/rewards-sheet.js +86 -0
- package/dist/cart/rewards-sheet.js.map +7 -0
- package/dist/cart/schedule-sheet.js +87 -0
- package/dist/cart/schedule-sheet.js.map +7 -0
- package/dist/{chunk-JFEI2OUZ.js → cart/theme-class.js} +7 -5
- package/dist/cart/theme-class.js.map +7 -0
- package/dist/cart/totals.js +81 -0
- package/dist/cart/totals.js.map +7 -0
- package/dist/cart/use-address-selection.js +130 -0
- package/dist/cart/use-address-selection.js.map +7 -0
- package/dist/cart.js +21 -1069
- package/dist/cart.js.map +7 -1
- package/dist/catalogue/catalogue-cart.js +25 -0
- package/dist/catalogue/catalogue-cart.js.map +7 -0
- package/dist/catalogue/catalogue-header.js +88 -0
- package/dist/catalogue/catalogue-header.js.map +7 -0
- package/dist/catalogue/catalogue-pages.js +68 -0
- package/dist/catalogue/catalogue-pages.js.map +7 -0
- package/dist/catalogue/catalogue-search.js +89 -0
- package/dist/catalogue/catalogue-search.js.map +7 -0
- package/dist/catalogue/category-navigation.js +98 -0
- package/dist/catalogue/category-navigation.js.map +7 -0
- package/dist/catalogue/category-section.js +113 -0
- package/dist/catalogue/category-section.js.map +7 -0
- package/dist/catalogue/info-banners.js +148 -0
- package/dist/catalogue/info-banners.js.map +7 -0
- package/dist/catalogue/item-tile.js +55 -0
- package/dist/catalogue/item-tile.js.map +7 -0
- package/dist/catalogue/loader.js +136 -0
- package/dist/catalogue/loader.js.map +7 -0
- package/dist/{chunk-USSLDBDY.js → catalogue/presentation.js} +12 -9
- package/dist/catalogue/presentation.js.map +7 -0
- package/dist/catalogue/saved-catalogue-sync.js +115 -0
- package/dist/catalogue/saved-catalogue-sync.js.map +7 -0
- package/dist/catalogue/types.js +1 -0
- package/dist/catalogue/types.js.map +7 -0
- package/dist/catalogue/use-catalogue-data.js +88 -0
- package/dist/catalogue/use-catalogue-data.js.map +7 -0
- package/dist/catalogue/use-category-scroll.js +115 -0
- package/dist/catalogue/use-category-scroll.js.map +7 -0
- package/dist/catalogue/use-item-opener.js +46 -0
- package/dist/catalogue/use-item-opener.js.map +7 -0
- package/dist/catalogue/use-search-source.js +132 -0
- package/dist/catalogue/use-search-source.js.map +7 -0
- package/dist/catalogue.js +26 -1216
- package/dist/catalogue.js.map +7 -1
- package/dist/checkout.js +149 -2
- package/dist/checkout.js.map +7 -1
- package/dist/host-bridge.js +22 -2
- package/dist/host-bridge.js.map +7 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +7 -1
- package/dist/item/configurator-commit.js +15 -0
- package/dist/item/configurator-commit.js.map +7 -0
- package/dist/item/configurator-draft.js +63 -0
- package/dist/item/configurator-draft.js.map +7 -0
- package/dist/item/configurator.js +135 -0
- package/dist/item/configurator.js.map +7 -0
- package/dist/item/note.js +65 -0
- package/dist/item/note.js.map +7 -0
- package/dist/item/variant-status.js +23 -0
- package/dist/item/variant-status.js.map +7 -0
- package/dist/item-frame.js +60 -3
- package/dist/item-frame.js.map +7 -1
- package/dist/item.js +11 -11
- package/dist/item.js.map +7 -1
- package/dist/navigation.js +39 -2
- package/dist/navigation.js.map +7 -1
- package/dist/url-navigation-state.js +61 -0
- package/dist/url-navigation-state.js.map +7 -0
- package/dist/{chunk-ILOCSCZL.js → url-navigation.js} +12 -89
- package/dist/url-navigation.js.map +7 -0
- package/package.json +8 -7
- package/dist/chunk-2MV6KU3R.js +0 -352
- package/dist/chunk-2MV6KU3R.js.map +0 -1
- package/dist/chunk-FB5DRCU7.js.map +0 -1
- package/dist/chunk-IG63ONID.js +0 -139
- package/dist/chunk-IG63ONID.js.map +0 -1
- package/dist/chunk-ILOCSCZL.js.map +0 -1
- package/dist/chunk-JFEI2OUZ.js.map +0 -1
- package/dist/chunk-NTJONY3N.js +0 -95
- package/dist/chunk-NTJONY3N.js.map +0 -1
- package/dist/chunk-QWST2TA2.js +0 -22
- package/dist/chunk-QWST2TA2.js.map +0 -1
- package/dist/chunk-USSLDBDY.js.map +0 -1
- package/dist/chunk-VIAJ27YC.js +0 -59
- package/dist/chunk-VIAJ27YC.js.map +0 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// src/catalogue/category-navigation.tsx
|
|
2
|
+
import { Overlay, useOverlay } from "@zappie/foundations";
|
|
3
|
+
import { Fragment, useEffect, useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
function CategoryNavigation({
|
|
6
|
+
categories,
|
|
7
|
+
active,
|
|
8
|
+
themeClass,
|
|
9
|
+
slots,
|
|
10
|
+
onSelect,
|
|
11
|
+
onFocusCategory
|
|
12
|
+
}) {
|
|
13
|
+
const overlay = useOverlay("catalogue_categories", "categories");
|
|
14
|
+
const [filter, setFilter] = useState("");
|
|
15
|
+
const [pending, setPending] = useState();
|
|
16
|
+
const CategoryTab = slots?.CategoryTab;
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (pending && !overlay.isOpen) {
|
|
19
|
+
const frame = requestAnimationFrame(() => {
|
|
20
|
+
setPending(void 0);
|
|
21
|
+
onSelect(pending);
|
|
22
|
+
onFocusCategory(pending);
|
|
23
|
+
});
|
|
24
|
+
return () => cancelAnimationFrame(frame);
|
|
25
|
+
}
|
|
26
|
+
}, [pending, overlay.isOpen, onSelect, onFocusCategory]);
|
|
27
|
+
const filtered = categories.filter(
|
|
28
|
+
(category) => category.name.toLowerCase().includes(filter.toLowerCase())
|
|
29
|
+
);
|
|
30
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
31
|
+
/* @__PURE__ */ jsxs(
|
|
32
|
+
Overlay.Root,
|
|
33
|
+
{
|
|
34
|
+
param: "catalogue_categories",
|
|
35
|
+
id: "categories",
|
|
36
|
+
presentation: { base: "sheet", lg: "dialog" },
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ jsx(Overlay.Trigger, { children: "Browse categories" }),
|
|
39
|
+
/* @__PURE__ */ jsxs(
|
|
40
|
+
Overlay.Content,
|
|
41
|
+
{
|
|
42
|
+
"data-zappie-theme": "base",
|
|
43
|
+
className: ["z-base-cart z-base-editor", themeClass].filter(Boolean).join(" "),
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ jsx(Overlay.Title, { children: "Browse categories" }),
|
|
46
|
+
/* @__PURE__ */ jsx(Overlay.Close, { children: "Close categories" }),
|
|
47
|
+
/* @__PURE__ */ jsx(
|
|
48
|
+
"input",
|
|
49
|
+
{
|
|
50
|
+
"aria-label": "Filter categories",
|
|
51
|
+
value: filter,
|
|
52
|
+
onChange: (event) => setFilter(event.target.value)
|
|
53
|
+
}
|
|
54
|
+
),
|
|
55
|
+
filtered.map((category) => /* @__PURE__ */ jsx(
|
|
56
|
+
"button",
|
|
57
|
+
{
|
|
58
|
+
type: "button",
|
|
59
|
+
onClick: () => {
|
|
60
|
+
setPending(category.id);
|
|
61
|
+
overlay.close();
|
|
62
|
+
},
|
|
63
|
+
children: category.name
|
|
64
|
+
},
|
|
65
|
+
category.id
|
|
66
|
+
)),
|
|
67
|
+
filtered.length === 0 && /* @__PURE__ */ jsx("p", { children: "No matching categories" })
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
),
|
|
74
|
+
/* @__PURE__ */ jsx("nav", { "aria-label": "Categories", className: "z-base-category-nav", children: categories.map((category) => /* @__PURE__ */ jsx(
|
|
75
|
+
"a",
|
|
76
|
+
{
|
|
77
|
+
href: `#base-category-${category.id}`,
|
|
78
|
+
"aria-current": active === category.id ? "true" : void 0,
|
|
79
|
+
onClick: (event) => {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
onSelect(category.id);
|
|
82
|
+
},
|
|
83
|
+
children: CategoryTab ? /* @__PURE__ */ jsx(
|
|
84
|
+
CategoryTab,
|
|
85
|
+
{
|
|
86
|
+
category,
|
|
87
|
+
active: active === category.id
|
|
88
|
+
}
|
|
89
|
+
) : category.name
|
|
90
|
+
},
|
|
91
|
+
category.id
|
|
92
|
+
)) })
|
|
93
|
+
] });
|
|
94
|
+
}
|
|
95
|
+
export {
|
|
96
|
+
CategoryNavigation
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=category-navigation.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/catalogue/category-navigation.tsx"],
|
|
4
|
+
"sourcesContent": ["import { Overlay, useOverlay } from \"@zappie/foundations\";\nimport type { Category } from \"@zappie/sdk\";\nimport { Fragment, useEffect, useState } from \"react\";\nimport type { CatalogueSlots } from \"./types\";\n\nexport function CategoryNavigation({\n categories,\n active,\n themeClass,\n slots,\n onSelect,\n onFocusCategory,\n}: {\n categories: readonly Category[];\n active?: string;\n themeClass?: string;\n slots?: CatalogueSlots;\n onSelect(categoryId: string): void;\n onFocusCategory(categoryId: string): void;\n}) {\n const overlay = useOverlay(\"catalogue_categories\", \"categories\");\n const [filter, setFilter] = useState(\"\");\n const [pending, setPending] = useState<string>();\n const CategoryTab = slots?.CategoryTab;\n useEffect(() => {\n if (pending && !overlay.isOpen) {\n const frame = requestAnimationFrame(() => {\n setPending(undefined);\n onSelect(pending);\n onFocusCategory(pending);\n });\n return () => cancelAnimationFrame(frame);\n }\n }, [pending, overlay.isOpen, onSelect, onFocusCategory]);\n const filtered = categories.filter((category) =>\n category.name.toLowerCase().includes(filter.toLowerCase()),\n );\n return (\n <Fragment>\n <Overlay.Root\n param=\"catalogue_categories\"\n id=\"categories\"\n presentation={{ base: \"sheet\", lg: \"dialog\" }}\n >\n <Overlay.Trigger>Browse categories</Overlay.Trigger>\n <Overlay.Content\n data-zappie-theme=\"base\"\n className={[\"z-base-cart z-base-editor\", themeClass]\n .filter(Boolean)\n .join(\" \")}\n >\n <Overlay.Title>Browse categories</Overlay.Title>\n <Overlay.Close>Close categories</Overlay.Close>\n <input\n aria-label=\"Filter categories\"\n value={filter}\n onChange={(event) => setFilter(event.target.value)}\n />\n {filtered.map((category) => (\n <button\n key={category.id}\n type=\"button\"\n onClick={() => {\n setPending(category.id);\n overlay.close();\n }}\n >\n {category.name}\n </button>\n ))}\n {filtered.length === 0 && <p>No matching categories</p>}\n </Overlay.Content>\n </Overlay.Root>\n <nav aria-label=\"Categories\" className=\"z-base-category-nav\">\n {categories.map((category) => (\n <a\n key={category.id}\n href={`#base-category-${category.id}`}\n aria-current={active === category.id ? \"true\" : undefined}\n onClick={(event) => {\n event.preventDefault();\n onSelect(category.id);\n }}\n >\n {CategoryTab ? (\n <CategoryTab\n category={category}\n active={active === category.id}\n />\n ) : (\n category.name\n )}\n </a>\n ))}\n </nav>\n </Fragment>\n );\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,SAAS,kBAAkB;AAEpC,SAAS,UAAU,WAAW,gBAAgB;AA0CtC,cACA,YADA;AAvCD,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,QAAM,UAAU,WAAW,wBAAwB,YAAY;AAC/D,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,EAAE;AACvC,QAAM,CAAC,SAAS,UAAU,IAAI,SAAiB;AAC/C,QAAM,cAAc,OAAO;AAC3B,YAAU,MAAM;AACd,QAAI,WAAW,CAAC,QAAQ,QAAQ;AAC9B,YAAM,QAAQ,sBAAsB,MAAM;AACxC,mBAAW,MAAS;AACpB,iBAAS,OAAO;AAChB,wBAAgB,OAAO;AAAA,MACzB,CAAC;AACD,aAAO,MAAM,qBAAqB,KAAK;AAAA,IACzC;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,QAAQ,UAAU,eAAe,CAAC;AACvD,QAAM,WAAW,WAAW;AAAA,IAAO,CAAC,aAClC,SAAS,KAAK,YAAY,EAAE,SAAS,OAAO,YAAY,CAAC;AAAA,EAC3D;AACA,SACE,qBAAC,YACC;AAAA;AAAA,MAAC,QAAQ;AAAA,MAAR;AAAA,QACC,OAAM;AAAA,QACN,IAAG;AAAA,QACH,cAAc,EAAE,MAAM,SAAS,IAAI,SAAS;AAAA,QAE5C;AAAA,8BAAC,QAAQ,SAAR,EAAgB,+BAAiB;AAAA,UAClC;AAAA,YAAC,QAAQ;AAAA,YAAR;AAAA,cACC,qBAAkB;AAAA,cAClB,WAAW,CAAC,6BAA6B,UAAU,EAChD,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,cAEX;AAAA,oCAAC,QAAQ,OAAR,EAAc,+BAAiB;AAAA,gBAChC,oBAAC,QAAQ,OAAR,EAAc,8BAAgB;AAAA,gBAC/B;AAAA,kBAAC;AAAA;AAAA,oBACC,cAAW;AAAA,oBACX,OAAO;AAAA,oBACP,UAAU,CAAC,UAAU,UAAU,MAAM,OAAO,KAAK;AAAA;AAAA,gBACnD;AAAA,gBACC,SAAS,IAAI,CAAC,aACb;AAAA,kBAAC;AAAA;AAAA,oBAEC,MAAK;AAAA,oBACL,SAAS,MAAM;AACb,iCAAW,SAAS,EAAE;AACtB,8BAAQ,MAAM;AAAA,oBAChB;AAAA,oBAEC,mBAAS;AAAA;AAAA,kBAPL,SAAS;AAAA,gBAQhB,CACD;AAAA,gBACA,SAAS,WAAW,KAAK,oBAAC,OAAE,oCAAsB;AAAA;AAAA;AAAA,UACrD;AAAA;AAAA;AAAA,IACF;AAAA,IACA,oBAAC,SAAI,cAAW,cAAa,WAAU,uBACpC,qBAAW,IAAI,CAAC,aACf;AAAA,MAAC;AAAA;AAAA,QAEC,MAAM,kBAAkB,SAAS,EAAE;AAAA,QACnC,gBAAc,WAAW,SAAS,KAAK,SAAS;AAAA,QAChD,SAAS,CAAC,UAAU;AAClB,gBAAM,eAAe;AACrB,mBAAS,SAAS,EAAE;AAAA,QACtB;AAAA,QAEC,wBACC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,QAAQ,WAAW,SAAS;AAAA;AAAA,QAC9B,IAEA,SAAS;AAAA;AAAA,MAdN,SAAS;AAAA,IAgBhB,CACD,GACH;AAAA,KACF;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// src/catalogue/category-section.tsx
|
|
2
|
+
import { Cart } from "@zappie/foundations";
|
|
3
|
+
import { countEvent } from "@zappie/sdk/visits";
|
|
4
|
+
import { CartLine } from "../cart/cart-line.js";
|
|
5
|
+
import { BaseItemTile } from "./item-tile.js";
|
|
6
|
+
import { Description } from "./presentation.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
function CategorySection({
|
|
9
|
+
category,
|
|
10
|
+
categoryIndex,
|
|
11
|
+
catalogue,
|
|
12
|
+
menuId,
|
|
13
|
+
layout,
|
|
14
|
+
slots,
|
|
15
|
+
sections,
|
|
16
|
+
cartOpen,
|
|
17
|
+
client,
|
|
18
|
+
busy,
|
|
19
|
+
hasMore,
|
|
20
|
+
itemHref,
|
|
21
|
+
onOpen,
|
|
22
|
+
onLoadMore
|
|
23
|
+
}) {
|
|
24
|
+
const CategoryHeader = slots?.CategoryHeader;
|
|
25
|
+
const Tile = slots?.Tile;
|
|
26
|
+
const columns = slots?.layout?.columns;
|
|
27
|
+
const baseColumns = typeof columns === "object" ? columns.base : columns;
|
|
28
|
+
const mediumColumns = typeof columns === "object" ? columns.medium : columns;
|
|
29
|
+
const largeColumns = typeof columns === "object" ? columns.large : columns;
|
|
30
|
+
return /* @__PURE__ */ jsxs(
|
|
31
|
+
"section",
|
|
32
|
+
{
|
|
33
|
+
id: `base-category-${category.id}`,
|
|
34
|
+
className: "z-base-category",
|
|
35
|
+
"data-unloaded": category.itemIds.length === 0,
|
|
36
|
+
ref: (node) => {
|
|
37
|
+
if (node) {
|
|
38
|
+
sections.current.set(category.id, node);
|
|
39
|
+
} else {
|
|
40
|
+
sections.current.delete(category.id);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
children: [
|
|
44
|
+
/* @__PURE__ */ jsx("h3", { tabIndex: -1, children: CategoryHeader ? /* @__PURE__ */ jsx(CategoryHeader, { category }) : category.name }),
|
|
45
|
+
category.description && /* @__PURE__ */ jsx(Description, { value: category.description }),
|
|
46
|
+
/* @__PURE__ */ jsx(
|
|
47
|
+
Cart.Root,
|
|
48
|
+
{
|
|
49
|
+
className: "z-base-item-list",
|
|
50
|
+
"data-layout": layout,
|
|
51
|
+
"data-columns": baseColumns,
|
|
52
|
+
"data-columns-medium": mediumColumns,
|
|
53
|
+
"data-columns-large": largeColumns,
|
|
54
|
+
"data-density": slots?.layout?.density,
|
|
55
|
+
children: category.itemIds.map((id, index) => {
|
|
56
|
+
const item = catalogue.items[id];
|
|
57
|
+
if (!item) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const lines = client.cart.lines.filter((line) => line.itemId === id);
|
|
61
|
+
const href = itemHref(item.id, menuId);
|
|
62
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
63
|
+
Tile ? /* @__PURE__ */ jsx(
|
|
64
|
+
Tile,
|
|
65
|
+
{
|
|
66
|
+
item,
|
|
67
|
+
...menuId !== void 0 ? { menuId } : {},
|
|
68
|
+
href,
|
|
69
|
+
quantityInCart: lines.reduce(
|
|
70
|
+
(sum, line) => sum + line.quantity,
|
|
71
|
+
0
|
|
72
|
+
),
|
|
73
|
+
onAdd: () => {
|
|
74
|
+
try {
|
|
75
|
+
client.cart.add(item.id, {
|
|
76
|
+
...menuId !== void 0 ? { menuId } : {}
|
|
77
|
+
});
|
|
78
|
+
countEvent("add");
|
|
79
|
+
} catch {
|
|
80
|
+
onOpen(item);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
onOpen: () => onOpen(item)
|
|
84
|
+
}
|
|
85
|
+
) : /* @__PURE__ */ jsx(
|
|
86
|
+
BaseItemTile,
|
|
87
|
+
{
|
|
88
|
+
item,
|
|
89
|
+
lines,
|
|
90
|
+
href,
|
|
91
|
+
layout,
|
|
92
|
+
priority: categoryIndex === 0 && index < 4,
|
|
93
|
+
client,
|
|
94
|
+
onOpen: () => onOpen(item)
|
|
95
|
+
}
|
|
96
|
+
),
|
|
97
|
+
!cartOpen && lines.map((line) => /* @__PURE__ */ jsx(CartLine, { line }, line.id))
|
|
98
|
+
] }, id);
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
),
|
|
102
|
+
hasMore && /* @__PURE__ */ jsxs("button", { type: "button", disabled: busy, onClick: onLoadMore, children: [
|
|
103
|
+
"Load more ",
|
|
104
|
+
category.name
|
|
105
|
+
] })
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
export {
|
|
111
|
+
CategorySection
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=category-section.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/catalogue/category-section.tsx"],
|
|
4
|
+
"sourcesContent": ["import { Cart } from \"@zappie/foundations\";\nimport type { Category, Item, Menu, Zappie } from \"@zappie/sdk\";\nimport { countEvent } from \"@zappie/sdk/visits\";\nimport type { MutableRefObject } from \"react\";\nimport { CartLine } from \"../cart/cart-line\";\nimport { BaseItemTile } from \"./item-tile\";\nimport { Description } from \"./presentation\";\nimport type { CatalogueLayoutMode, CatalogueSlots } from \"./types\";\n\nexport function CategorySection({\n category,\n categoryIndex,\n catalogue,\n menuId,\n layout,\n slots,\n sections,\n cartOpen,\n client,\n busy,\n hasMore,\n itemHref,\n onOpen,\n onLoadMore,\n}: {\n category: Category;\n categoryIndex: number;\n catalogue: Menu;\n menuId?: string;\n layout: CatalogueLayoutMode;\n slots?: CatalogueSlots;\n sections: MutableRefObject<Map<string, HTMLElement>>;\n cartOpen: boolean;\n client: Zappie;\n busy: boolean;\n hasMore: boolean;\n itemHref(itemId: string, menuId?: string): string;\n onOpen(item: Item): void;\n onLoadMore(): void;\n}) {\n const CategoryHeader = slots?.CategoryHeader;\n const Tile = slots?.Tile;\n const columns = slots?.layout?.columns;\n const baseColumns = typeof columns === \"object\" ? columns.base : columns;\n const mediumColumns = typeof columns === \"object\" ? columns.medium : columns;\n const largeColumns = typeof columns === \"object\" ? columns.large : columns;\n return (\n <section\n id={`base-category-${category.id}`}\n className=\"z-base-category\"\n data-unloaded={category.itemIds.length === 0}\n ref={(node) => {\n if (node) {\n sections.current.set(category.id, node);\n } else {\n sections.current.delete(category.id);\n }\n }}\n >\n <h3 tabIndex={-1}>\n {CategoryHeader ? (\n <CategoryHeader category={category} />\n ) : (\n category.name\n )}\n </h3>\n {category.description && <Description value={category.description} />}\n <Cart.Root\n className=\"z-base-item-list\"\n data-layout={layout}\n data-columns={baseColumns}\n data-columns-medium={mediumColumns}\n data-columns-large={largeColumns}\n data-density={slots?.layout?.density}\n >\n {category.itemIds.map((id, index) => {\n const item = catalogue.items[id];\n if (!item) {\n return null;\n }\n const lines = client.cart.lines.filter((line) => line.itemId === id);\n const href = itemHref(item.id, menuId);\n return (\n <div key={id}>\n {Tile ? (\n <Tile\n item={item}\n {...(menuId !== undefined ? { menuId } : {})}\n href={href}\n quantityInCart={lines.reduce(\n (sum, line) => sum + line.quantity,\n 0,\n )}\n onAdd={() => {\n // An item the cart refuses as-is (required choices, an\n // ambiguous variant) opens the item to be configured.\n try {\n client.cart.add(item.id, {\n ...(menuId !== undefined ? { menuId } : {}),\n });\n countEvent(\"add\");\n } catch {\n onOpen(item);\n }\n }}\n onOpen={() => onOpen(item)}\n />\n ) : (\n <BaseItemTile\n item={item}\n lines={lines}\n href={href}\n layout={layout}\n priority={categoryIndex === 0 && index < 4}\n client={client}\n onOpen={() => onOpen(item)}\n />\n )}\n {!cartOpen &&\n lines.map((line) => <CartLine key={line.id} line={line} />)}\n </div>\n );\n })}\n </Cart.Root>\n {hasMore && (\n <button type=\"button\" disabled={busy} onClick={onLoadMore}>\n Load more {category.name}\n </button>\n )}\n </section>\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/catalogue/info-banners.tsx
|
|
4
|
+
import {
|
|
5
|
+
createSanitizeRichText,
|
|
6
|
+
splitInfoBanner
|
|
7
|
+
} from "@zappie/foundations/rich-text";
|
|
8
|
+
import { parseRichText } from "@zappie/foundations/rich-text/browser";
|
|
9
|
+
import { useEffect, useRef, useState } from "react";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
var sanitize = createSanitizeRichText(parseRichText);
|
|
12
|
+
function InfoBanners({ value }) {
|
|
13
|
+
const [banners, setBanners] = useState([]);
|
|
14
|
+
const [index, setIndex] = useState(0);
|
|
15
|
+
const [expanded, setExpanded] = useState({});
|
|
16
|
+
const [paused, setPaused] = useState(false);
|
|
17
|
+
const viewport = useRef(null);
|
|
18
|
+
const interacted = useRef(false);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
setBanners(
|
|
21
|
+
splitInfoBanner(value).map(sanitize).filter((banner) => banner.replace(/<[^>]*>/g, "").trim())
|
|
22
|
+
);
|
|
23
|
+
setIndex(0);
|
|
24
|
+
setExpanded({});
|
|
25
|
+
interacted.current = false;
|
|
26
|
+
}, [value]);
|
|
27
|
+
function select(next, interaction = true) {
|
|
28
|
+
interacted.current || (interacted.current = interaction);
|
|
29
|
+
setExpanded({});
|
|
30
|
+
viewport.current?.children[next]?.scrollIntoView({
|
|
31
|
+
block: "nearest",
|
|
32
|
+
inline: "start",
|
|
33
|
+
behavior: "instant"
|
|
34
|
+
});
|
|
35
|
+
setIndex(next);
|
|
36
|
+
}
|
|
37
|
+
const isExpanded = Object.values(expanded).some(Boolean);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (paused || isExpanded || banners.length < 2) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const interval = setInterval(() => {
|
|
43
|
+
const node = viewport.current;
|
|
44
|
+
if (!node || interacted.current || window.matchMedia("(prefers-reduced-motion: reduce)").matches || node.scrollWidth <= node.clientWidth) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
select((index + 1) % banners.length, false);
|
|
48
|
+
}, 5e3);
|
|
49
|
+
return () => clearInterval(interval);
|
|
50
|
+
}, [index, paused, isExpanded, banners.length]);
|
|
51
|
+
if (!banners.length) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return /* @__PURE__ */ jsxs(
|
|
55
|
+
"section",
|
|
56
|
+
{
|
|
57
|
+
"aria-label": "Menu information",
|
|
58
|
+
onMouseEnter: () => setPaused(true),
|
|
59
|
+
onMouseLeave: () => setPaused(false),
|
|
60
|
+
onFocus: () => setPaused(true),
|
|
61
|
+
onBlur: () => setPaused(false),
|
|
62
|
+
children: [
|
|
63
|
+
/* @__PURE__ */ jsx(
|
|
64
|
+
"div",
|
|
65
|
+
{
|
|
66
|
+
className: "z-base-info-banners",
|
|
67
|
+
ref: viewport,
|
|
68
|
+
onPointerDown: () => {
|
|
69
|
+
interacted.current = true;
|
|
70
|
+
},
|
|
71
|
+
onScroll: () => {
|
|
72
|
+
const node = viewport.current;
|
|
73
|
+
if (node?.children[0]) {
|
|
74
|
+
setIndex(
|
|
75
|
+
Math.round(
|
|
76
|
+
node.scrollLeft / node.children[0].getBoundingClientRect().width
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
children: banners.map((banner, position) => /* @__PURE__ */ jsxs("article", { children: [
|
|
82
|
+
/* @__PURE__ */ jsx(
|
|
83
|
+
"div",
|
|
84
|
+
{
|
|
85
|
+
className: "z-base-info-copy",
|
|
86
|
+
"data-expanded": Boolean(expanded[position]),
|
|
87
|
+
dangerouslySetInnerHTML: { __html: banner }
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
/* @__PURE__ */ jsxs(
|
|
91
|
+
"button",
|
|
92
|
+
{
|
|
93
|
+
type: "button",
|
|
94
|
+
"aria-expanded": Boolean(expanded[position]),
|
|
95
|
+
onClick: () => setExpanded((current) => ({
|
|
96
|
+
...current,
|
|
97
|
+
[position]: !current[position]
|
|
98
|
+
})),
|
|
99
|
+
children: [
|
|
100
|
+
"Show ",
|
|
101
|
+
expanded[position] ? "less" : "more"
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
] }, banner + position))
|
|
106
|
+
}
|
|
107
|
+
),
|
|
108
|
+
banners.length > 1 && /* @__PURE__ */ jsxs("div", { children: [
|
|
109
|
+
/* @__PURE__ */ jsx(
|
|
110
|
+
"button",
|
|
111
|
+
{
|
|
112
|
+
type: "button",
|
|
113
|
+
"aria-label": "Previous information",
|
|
114
|
+
disabled: index === 0,
|
|
115
|
+
onClick: () => select(index - 1),
|
|
116
|
+
children: "Previous"
|
|
117
|
+
}
|
|
118
|
+
),
|
|
119
|
+
/* @__PURE__ */ jsx(
|
|
120
|
+
"button",
|
|
121
|
+
{
|
|
122
|
+
type: "button",
|
|
123
|
+
"aria-label": "Next information",
|
|
124
|
+
disabled: index >= banners.length - 1,
|
|
125
|
+
onClick: () => select(index + 1),
|
|
126
|
+
children: "Next"
|
|
127
|
+
}
|
|
128
|
+
),
|
|
129
|
+
banners.map((_, position) => /* @__PURE__ */ jsx(
|
|
130
|
+
"button",
|
|
131
|
+
{
|
|
132
|
+
type: "button",
|
|
133
|
+
"aria-label": `Go to banner ${position + 1}`,
|
|
134
|
+
"aria-current": index === position ? "true" : void 0,
|
|
135
|
+
onClick: () => select(position),
|
|
136
|
+
children: position + 1
|
|
137
|
+
},
|
|
138
|
+
position
|
|
139
|
+
))
|
|
140
|
+
] })
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
export {
|
|
146
|
+
InfoBanners
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=info-banners.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/catalogue/info-banners.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\";\nimport {\n createSanitizeRichText,\n splitInfoBanner,\n} from \"@zappie/foundations/rich-text\";\nimport { parseRichText } from \"@zappie/foundations/rich-text/browser\";\nimport { useEffect, useRef, useState } from \"react\";\nconst sanitize = createSanitizeRichText(parseRichText);\n/** Safe rich text with touch scrolling, explicit controls and pausable rotation. */\nexport function InfoBanners({ value }: { value?: string }) {\n const [banners, setBanners] = useState<string[]>([]);\n const [index, setIndex] = useState(0);\n const [expanded, setExpanded] = useState<Record<number, boolean>>({});\n const [paused, setPaused] = useState(false);\n const viewport = useRef<HTMLDivElement>(null);\n const interacted = useRef(false);\n useEffect(() => {\n setBanners(\n splitInfoBanner(value)\n .map(sanitize)\n .filter((banner) => banner.replace(/<[^>]*>/g, \"\").trim()),\n );\n setIndex(0);\n setExpanded({});\n interacted.current = false;\n }, [value]);\n function select(next: number, interaction = true) {\n interacted.current ||= interaction;\n setExpanded({});\n viewport.current?.children[next]?.scrollIntoView({\n block: \"nearest\",\n inline: \"start\",\n behavior: \"instant\",\n });\n setIndex(next);\n }\n const isExpanded = Object.values(expanded).some(Boolean);\n useEffect(() => {\n if (paused || isExpanded || banners.length < 2) {\n return;\n }\n const interval = setInterval(() => {\n const node = viewport.current;\n if (\n !node ||\n interacted.current ||\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ||\n node.scrollWidth <= node.clientWidth\n ) {\n return;\n }\n select((index + 1) % banners.length, false);\n }, 5000);\n return () => clearInterval(interval);\n }, [index, paused, isExpanded, banners.length]);\n if (!banners.length) {\n return null;\n }\n return (\n <section\n aria-label=\"Menu information\"\n onMouseEnter={() => setPaused(true)}\n onMouseLeave={() => setPaused(false)}\n onFocus={() => setPaused(true)}\n onBlur={() => setPaused(false)}\n >\n <div\n className=\"z-base-info-banners\"\n ref={viewport}\n onPointerDown={() => {\n interacted.current = true;\n }}\n onScroll={() => {\n const node = viewport.current;\n if (node?.children[0]) {\n setIndex(\n Math.round(\n node.scrollLeft /\n node.children[0].getBoundingClientRect().width,\n ),\n );\n }\n }}\n >\n {banners.map((banner, position) => (\n <article key={banner + position}>\n <div\n className=\"z-base-info-copy\"\n data-expanded={Boolean(expanded[position])}\n dangerouslySetInnerHTML={{ __html: banner }}\n />\n <button\n type=\"button\"\n aria-expanded={Boolean(expanded[position])}\n onClick={() =>\n setExpanded((current) => ({\n ...current,\n [position]: !current[position],\n }))\n }\n >\n Show {expanded[position] ? \"less\" : \"more\"}\n </button>\n </article>\n ))}\n </div>\n {banners.length > 1 && (\n <div>\n <button\n type=\"button\"\n aria-label=\"Previous information\"\n disabled={index === 0}\n onClick={() => select(index - 1)}\n >\n Previous\n </button>\n <button\n type=\"button\"\n aria-label=\"Next information\"\n disabled={index >= banners.length - 1}\n onClick={() => select(index + 1)}\n >\n Next\n </button>\n {banners.map((_, position) => (\n <button\n key={position}\n type=\"button\"\n aria-label={`Go to banner ${position + 1}`}\n aria-current={index === position ? \"true\" : undefined}\n onClick={() => select(position)}\n >\n {position + 1}\n </button>\n ))}\n </div>\n )}\n </section>\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/catalogue/item-tile.tsx
|
|
2
|
+
import { Description, ItemImage } from "./presentation.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
function BaseItemTile({
|
|
5
|
+
item,
|
|
6
|
+
lines,
|
|
7
|
+
href,
|
|
8
|
+
layout,
|
|
9
|
+
priority,
|
|
10
|
+
client,
|
|
11
|
+
onOpen
|
|
12
|
+
}) {
|
|
13
|
+
const price = item.price;
|
|
14
|
+
return /* @__PURE__ */ jsxs(
|
|
15
|
+
"a",
|
|
16
|
+
{
|
|
17
|
+
href,
|
|
18
|
+
className: "z-base-item-tile",
|
|
19
|
+
onClick: (event) => {
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
event.currentTarget.focus({ preventScroll: true });
|
|
22
|
+
onOpen();
|
|
23
|
+
},
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ jsx(
|
|
26
|
+
ItemImage,
|
|
27
|
+
{
|
|
28
|
+
src: item.images[0],
|
|
29
|
+
sizes: layout === "LIST" ? "100vw" : "(min-width: 768px) 33vw, 50vw",
|
|
30
|
+
priority
|
|
31
|
+
}
|
|
32
|
+
),
|
|
33
|
+
/* @__PURE__ */ jsx("span", { className: "z-base-item-name", children: item.name }),
|
|
34
|
+
item.description && /* @__PURE__ */ jsx(Description, { value: item.description }),
|
|
35
|
+
item.promo && /* @__PURE__ */ jsx("span", { children: item.promo.name }),
|
|
36
|
+
item.compareAtPrice && /* @__PURE__ */ jsx("del", { children: client.format.money(item.compareAtPrice) }),
|
|
37
|
+
price && /* @__PURE__ */ jsxs("span", { className: "z-base-amount", children: [
|
|
38
|
+
item.variants.some(
|
|
39
|
+
(variant) => variant.price.amountMinor !== price.amountMinor
|
|
40
|
+
) ? "From " : "",
|
|
41
|
+
client.format.money(price)
|
|
42
|
+
] }),
|
|
43
|
+
(item.stockLabel || !item.isAvailable) && /* @__PURE__ */ jsx("span", { children: item.stockLabel ?? "Unavailable" }),
|
|
44
|
+
lines.map((line) => /* @__PURE__ */ jsxs("span", { children: [
|
|
45
|
+
line.quantity,
|
|
46
|
+
" added"
|
|
47
|
+
] }, line.id))
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
BaseItemTile
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=item-tile.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/catalogue/item-tile.tsx"],
|
|
4
|
+
"sourcesContent": ["import type { CartLine, Item, Zappie } from \"@zappie/sdk\";\nimport { Description, ItemImage } from \"./presentation\";\nimport type { CatalogueLayoutMode } from \"./types\";\n\nexport function BaseItemTile({\n item,\n lines,\n href,\n layout,\n priority,\n client,\n onOpen,\n}: {\n item: Item;\n lines: readonly CartLine[];\n href: string;\n layout: CatalogueLayoutMode;\n priority: boolean;\n client: Zappie;\n onOpen(): void;\n}) {\n const price = item.price;\n return (\n <a\n href={href}\n className=\"z-base-item-tile\"\n onClick={(event) => {\n event.preventDefault();\n event.currentTarget.focus({ preventScroll: true });\n onOpen();\n }}\n >\n <ItemImage\n src={item.images[0]}\n sizes={layout === \"LIST\" ? \"100vw\" : \"(min-width: 768px) 33vw, 50vw\"}\n priority={priority}\n />\n <span className=\"z-base-item-name\">{item.name}</span>\n {item.description && <Description value={item.description} />}\n {item.promo && <span>{item.promo.name}</span>}\n {item.compareAtPrice && (\n <del>{client.format.money(item.compareAtPrice)}</del>\n )}\n {price && (\n <span className=\"z-base-amount\">\n {item.variants.some(\n (variant) => variant.price.amountMinor !== price.amountMinor,\n )\n ? \"From \"\n : \"\"}\n {client.format.money(price)}\n </span>\n )}\n {(item.stockLabel || !item.isAvailable) && (\n <span>{item.stockLabel ?? \"Unavailable\"}</span>\n )}\n {lines.map((line) => (\n <span key={line.id}>{line.quantity} added</span>\n ))}\n </a>\n );\n}\n"],
|
|
5
|
+
"mappings": ";AACA,SAAS,aAAa,iBAAiB;AA+BjC,cAYE,YAZF;AA5BC,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQG;AACD,QAAM,QAAQ,KAAK;AACnB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAU;AAAA,MACV,SAAS,CAAC,UAAU;AAClB,cAAM,eAAe;AACrB,cAAM,cAAc,MAAM,EAAE,eAAe,KAAK,CAAC;AACjD,eAAO;AAAA,MACT;AAAA,MAEA;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,KAAK,KAAK,OAAO,CAAC;AAAA,YAClB,OAAO,WAAW,SAAS,UAAU;AAAA,YACrC;AAAA;AAAA,QACF;AAAA,QACA,oBAAC,UAAK,WAAU,oBAAoB,eAAK,MAAK;AAAA,QAC7C,KAAK,eAAe,oBAAC,eAAY,OAAO,KAAK,aAAa;AAAA,QAC1D,KAAK,SAAS,oBAAC,UAAM,eAAK,MAAM,MAAK;AAAA,QACrC,KAAK,kBACJ,oBAAC,SAAK,iBAAO,OAAO,MAAM,KAAK,cAAc,GAAE;AAAA,QAEhD,SACC,qBAAC,UAAK,WAAU,iBACb;AAAA,eAAK,SAAS;AAAA,YACb,CAAC,YAAY,QAAQ,MAAM,gBAAgB,MAAM;AAAA,UACnD,IACI,UACA;AAAA,UACH,OAAO,OAAO,MAAM,KAAK;AAAA,WAC5B;AAAA,SAEA,KAAK,cAAc,CAAC,KAAK,gBACzB,oBAAC,UAAM,eAAK,cAAc,eAAc;AAAA,QAEzC,MAAM,IAAI,CAAC,SACV,qBAAC,UAAoB;AAAA,eAAK;AAAA,UAAS;AAAA,aAAxB,KAAK,EAAyB,CAC1C;AAAA;AAAA;AAAA,EACH;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// src/catalogue/loader.ts
|
|
2
|
+
import { mergePage, pageKey, pageState } from "./catalogue-pages.js";
|
|
3
|
+
import {
|
|
4
|
+
createSavedCatalogueSync
|
|
5
|
+
} from "./saved-catalogue-sync.js";
|
|
6
|
+
function createCatalogueLoader(client, metadata, resolve, publish, orderContext = "delivery", revision) {
|
|
7
|
+
const state = {
|
|
8
|
+
menu: metadata,
|
|
9
|
+
mode: resolve(metadata),
|
|
10
|
+
complete: false,
|
|
11
|
+
abort: new AbortController(),
|
|
12
|
+
pages: /* @__PURE__ */ new Map(),
|
|
13
|
+
savedPages: []
|
|
14
|
+
};
|
|
15
|
+
const scope = {
|
|
16
|
+
store: client.config.store,
|
|
17
|
+
branchId: metadata.branchId,
|
|
18
|
+
orderContext,
|
|
19
|
+
menuId: metadata.menus?.[0]?.id ?? metadata.id
|
|
20
|
+
};
|
|
21
|
+
const saved = createSavedCatalogueSync({
|
|
22
|
+
client,
|
|
23
|
+
state,
|
|
24
|
+
scope,
|
|
25
|
+
revision,
|
|
26
|
+
publish
|
|
27
|
+
});
|
|
28
|
+
const pending = /* @__PURE__ */ new Map();
|
|
29
|
+
let eager;
|
|
30
|
+
let speculative;
|
|
31
|
+
function readEager(signal = state.abort.signal) {
|
|
32
|
+
eager ?? (eager = saved.resources.selected.read(scope, { signal }).then(async (response) => {
|
|
33
|
+
signal.throwIfAborted();
|
|
34
|
+
if ("notModified" in response) {
|
|
35
|
+
throw new Error("Unexpected conditional selected-menu response");
|
|
36
|
+
}
|
|
37
|
+
state.complete = true;
|
|
38
|
+
state.menu = response.menu;
|
|
39
|
+
saved.emit();
|
|
40
|
+
return state.menu;
|
|
41
|
+
}).catch((failure) => {
|
|
42
|
+
eager = void 0;
|
|
43
|
+
throw failure;
|
|
44
|
+
}));
|
|
45
|
+
return eager;
|
|
46
|
+
}
|
|
47
|
+
function speculate() {
|
|
48
|
+
if (eager) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
speculative = new AbortController();
|
|
52
|
+
const cancel = () => speculative?.abort();
|
|
53
|
+
state.abort.signal.addEventListener("abort", cancel, { once: true });
|
|
54
|
+
void readEager(speculative.signal).catch(() => {
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function readPage(menuId, categoryId, key, cursor) {
|
|
58
|
+
const page = await client.menu.page(menuId, categoryId, {
|
|
59
|
+
branch: metadata.branchId,
|
|
60
|
+
signal: state.abort.signal,
|
|
61
|
+
...cursor ? { cursor } : {}
|
|
62
|
+
});
|
|
63
|
+
state.abort.signal.throwIfAborted();
|
|
64
|
+
if (!state.complete) {
|
|
65
|
+
state.menu = mergePage(state.menu, page);
|
|
66
|
+
}
|
|
67
|
+
state.savedPages.push({ categoryId, ...cursor ? { cursor } : {}, page });
|
|
68
|
+
saved.emit();
|
|
69
|
+
state.pages.set(key, pageState(page.nextCursor));
|
|
70
|
+
if (state.mode === null) {
|
|
71
|
+
state.mode = resolve(metadata, page.menu) ?? "progressive";
|
|
72
|
+
if (state.mode !== "eager") {
|
|
73
|
+
speculative?.abort();
|
|
74
|
+
eager = void 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return state.menu;
|
|
78
|
+
}
|
|
79
|
+
const initialized = saved.restore().catch(() => false);
|
|
80
|
+
return {
|
|
81
|
+
get value() {
|
|
82
|
+
return state.menu;
|
|
83
|
+
},
|
|
84
|
+
get mode() {
|
|
85
|
+
return state.mode;
|
|
86
|
+
},
|
|
87
|
+
hasMore(menuId, categoryId) {
|
|
88
|
+
return state.mode !== "eager" && !state.pages.get(pageKey(menuId, categoryId))?.done;
|
|
89
|
+
},
|
|
90
|
+
/** True when reaching this category would neither read nor change. */
|
|
91
|
+
isLoaded(menuId, categoryId) {
|
|
92
|
+
return state.complete || state.mode !== "eager" && state.pages.has(pageKey(menuId, categoryId));
|
|
93
|
+
},
|
|
94
|
+
async reach(menuId, categoryId, more = false) {
|
|
95
|
+
const key = pageKey(menuId, categoryId);
|
|
96
|
+
const warm = await initialized;
|
|
97
|
+
if (state.complete || warm && state.pages.has(key) && !more) {
|
|
98
|
+
return state.menu;
|
|
99
|
+
}
|
|
100
|
+
if (state.mode === "eager") {
|
|
101
|
+
return readEager();
|
|
102
|
+
}
|
|
103
|
+
const existing = pending.get(key);
|
|
104
|
+
if (existing) {
|
|
105
|
+
return existing;
|
|
106
|
+
}
|
|
107
|
+
const loaded = state.pages.get(key);
|
|
108
|
+
if (loaded && (!more || loaded.done)) {
|
|
109
|
+
return state.menu;
|
|
110
|
+
}
|
|
111
|
+
if (state.mode === null) {
|
|
112
|
+
speculate();
|
|
113
|
+
}
|
|
114
|
+
const request = readPage(menuId, categoryId, key, loaded?.next);
|
|
115
|
+
pending.set(key, request);
|
|
116
|
+
try {
|
|
117
|
+
return await request;
|
|
118
|
+
} finally {
|
|
119
|
+
pending.delete(key);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
/** Aborts in-flight reads; `resume()` makes the loader usable again. */
|
|
123
|
+
dispose() {
|
|
124
|
+
state.abort.abort();
|
|
125
|
+
},
|
|
126
|
+
resume() {
|
|
127
|
+
if (state.abort.signal.aborted) {
|
|
128
|
+
state.abort = new AbortController();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
createCatalogueLoader
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/catalogue/loader.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Menu, Zappie } from \"@zappie/sdk\";\nimport { mergePage, pageKey, pageState } from \"./catalogue-pages\";\nimport {\n createSavedCatalogueSync,\n type LoaderState,\n} from \"./saved-catalogue-sync\";\nexport type CatalogueStrategy = (\n metadata: Menu,\n sample?: Menu,\n) => \"eager\" | \"progressive\" | null;\n/** Screen-local read state; never marks a progressive catalogue as complete. */\nexport function createCatalogueLoader(\n client: Zappie,\n metadata: Menu,\n resolve: CatalogueStrategy,\n publish?: (menu: Menu) => void,\n orderContext: \"delivery\" | \"pickup\" | \"dineIn\" = \"delivery\",\n /** Host catalogue revision; a saved copy at this revision is not refreshed. */\n revision?: string,\n) {\n const state: LoaderState = {\n menu: metadata,\n mode: resolve(metadata),\n complete: false,\n abort: new AbortController(),\n pages: new Map(),\n savedPages: [],\n };\n const scope = {\n store: client.config.store,\n branchId: metadata.branchId,\n orderContext,\n menuId: metadata.menus?.[0]?.id ?? metadata.id,\n };\n const saved = createSavedCatalogueSync({\n client,\n state,\n scope,\n revision,\n publish,\n });\n const pending = new Map<string, Promise<Menu>>();\n let eager: Promise<Menu> | undefined;\n // The complete read starts beside the size probe rather than after it, and\n // is cancelled if the probe's measurement rejects eager loading.\n let speculative: AbortController | undefined;\n function readEager(signal: AbortSignal = state.abort.signal) {\n eager ??= saved\n .resources!.selected.read(scope, { signal })\n .then(async (response) => {\n signal.throwIfAborted();\n if (\"notModified\" in response) {\n throw new Error(\"Unexpected conditional selected-menu response\");\n }\n state.complete = true;\n state.menu = response.menu;\n saved.emit();\n return state.menu;\n })\n .catch((failure: unknown) => {\n eager = undefined;\n throw failure;\n });\n return eager;\n }\n function speculate() {\n if (eager) {\n return;\n }\n speculative = new AbortController();\n const cancel = () => speculative?.abort();\n state.abort.signal.addEventListener(\"abort\", cancel, { once: true });\n void readEager(speculative.signal).catch(() => {});\n }\n async function readPage(\n menuId: string,\n categoryId: string,\n key: string,\n cursor: string | undefined,\n ) {\n const page = await client.menu.page(menuId, categoryId, {\n branch: metadata.branchId,\n signal: state.abort.signal,\n ...(cursor ? { cursor } : {}),\n });\n state.abort.signal.throwIfAborted();\n // The size probe is a real first page: it renders before any\n // complete read the measurement approves.\n if (!state.complete) {\n state.menu = mergePage(state.menu, page);\n }\n state.savedPages.push({ categoryId, ...(cursor ? { cursor } : {}), page });\n saved.emit();\n state.pages.set(key, pageState(page.nextCursor));\n if (state.mode === null) {\n state.mode = resolve(metadata, page.menu) ?? \"progressive\";\n if (state.mode !== \"eager\") {\n speculative?.abort();\n eager = undefined;\n }\n }\n return state.menu;\n }\n // Restoring starts with the loader, not at the first reach; any failure to\n // restore (no IndexedDB, a chunk error) falls back to the network.\n const initialized = saved.restore().catch(() => false);\n return {\n get value() {\n return state.menu;\n },\n get mode() {\n return state.mode;\n },\n hasMore(menuId: string, categoryId: string) {\n return (\n state.mode !== \"eager\" &&\n !state.pages.get(pageKey(menuId, categoryId))?.done\n );\n },\n /** True when reaching this category would neither read nor change. */\n isLoaded(menuId: string, categoryId: string) {\n return (\n state.complete ||\n (state.mode !== \"eager\" && state.pages.has(pageKey(menuId, categoryId)))\n );\n },\n async reach(\n menuId: string,\n categoryId: string,\n more = false,\n ): Promise<Menu> {\n const key = pageKey(menuId, categoryId);\n const warm = await initialized;\n if (state.complete || (warm && state.pages.has(key) && !more)) {\n return state.menu;\n }\n if (state.mode === \"eager\") {\n return readEager();\n }\n const existing = pending.get(key);\n if (existing) {\n return existing;\n }\n const loaded = state.pages.get(key);\n if (loaded && (!more || loaded.done)) {\n return state.menu;\n }\n if (state.mode === null) {\n speculate();\n }\n const request = readPage(menuId, categoryId, key, loaded?.next);\n pending.set(key, request);\n try {\n return await request;\n } finally {\n pending.delete(key);\n }\n },\n /** Aborts in-flight reads; `resume()` makes the loader usable again. */\n dispose() {\n state.abort.abort();\n },\n resume() {\n if (state.abort.signal.aborted) {\n state.abort = new AbortController();\n }\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|