@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
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
import { createSanitizeRichText } from '@zappie/foundations/rich-text';
|
|
3
|
-
import { parseRichText } from '@zappie/foundations/rich-text/browser';
|
|
4
|
-
import { useState, useEffect } from 'react';
|
|
5
|
-
import { jsx } from 'react/jsx-runtime';
|
|
1
|
+
"use client";
|
|
6
2
|
|
|
3
|
+
// src/catalogue/presentation.tsx
|
|
4
|
+
import { createSanitizeRichText } from "@zappie/foundations/rich-text";
|
|
5
|
+
import { parseRichText } from "@zappie/foundations/rich-text/browser";
|
|
6
|
+
import { useEffect, useState } from "react";
|
|
7
|
+
import { useBaseHostBridge } from "../host-bridge.js";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
9
|
var sanitize = createSanitizeRichText(parseRichText);
|
|
8
10
|
function Description({ value }) {
|
|
9
11
|
const [html, setHtml] = useState("");
|
|
@@ -42,7 +44,8 @@ function ItemImage({
|
|
|
42
44
|
)
|
|
43
45
|
) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "Image unavailable" }) });
|
|
44
46
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
export {
|
|
48
|
+
Description,
|
|
49
|
+
ItemImage
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=presentation.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/catalogue/presentation.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\";\nimport { createSanitizeRichText } from \"@zappie/foundations/rich-text\";\nimport { parseRichText } from \"@zappie/foundations/rich-text/browser\";\nimport { useEffect, useState } from \"react\";\nimport { useBaseHostBridge } from \"../host-bridge\";\nconst sanitize = createSanitizeRichText(parseRichText);\nexport function Description({ value }: { value: string }) {\n const [html, setHtml] = useState(\"\");\n useEffect(() => setHtml(sanitize(value)), [value]);\n return (\n <div\n className=\"z-base-description\"\n dangerouslySetInnerHTML={{ __html: html }}\n />\n );\n}\nexport function ItemImage({\n src,\n sizes,\n priority = false,\n}: {\n src?: string | undefined;\n /** Rendered width hint for responsive host images. */\n sizes: string;\n /** Tiles likely to be the largest paint load first instead of lazily. */\n priority?: boolean;\n}) {\n const bridge = useBaseHostBridge();\n const [failed, setFailed] = useState(false);\n useEffect(() => setFailed(false), [src]);\n const image = src ? (bridge.imageProps?.(src, sizes) ?? { src }) : undefined;\n return (\n <div className=\"z-base-item-image\">\n {image && !failed ? (\n // Public themes remain framework independent; the host may optimise\n // image URLs through the bridge.\n // eslint-disable-next-line @next/next/no-img-element\n <img\n {...image}\n loading={priority ? \"eager\" : \"lazy\"}\n fetchPriority={priority ? \"high\" : \"auto\"}\n decoding=\"async\"\n alt=\"\"\n onError={() => setFailed(true)}\n />\n ) : (\n <span aria-hidden=\"true\">Image unavailable</span>\n )}\n </div>\n );\n}\n"],
|
|
5
|
+
"mappings": ";;;AACA,SAAS,8BAA8B;AACvC,SAAS,qBAAqB;AAC9B,SAAS,WAAW,gBAAgB;AACpC,SAAS,yBAAyB;AAM9B;AALJ,IAAM,WAAW,uBAAuB,aAAa;AAC9C,SAAS,YAAY,EAAE,MAAM,GAAsB;AACxD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,EAAE;AACnC,YAAU,MAAM,QAAQ,SAAS,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;AACjD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,yBAAyB,EAAE,QAAQ,KAAK;AAAA;AAAA,EAC1C;AAEJ;AACO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAMG;AACD,QAAM,SAAS,kBAAkB;AACjC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,YAAU,MAAM,UAAU,KAAK,GAAG,CAAC,GAAG,CAAC;AACvC,QAAM,QAAQ,MAAO,OAAO,aAAa,KAAK,KAAK,KAAK,EAAE,IAAI,IAAK;AACnE,SACE,oBAAC,SAAI,WAAU,qBACZ,mBAAS,CAAC;AAAA;AAAA;AAAA;AAAA,IAIT;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,SAAS,WAAW,UAAU;AAAA,QAC9B,eAAe,WAAW,SAAS;AAAA,QACnC,UAAS;AAAA,QACT,KAAI;AAAA,QACJ,SAAS,MAAM,UAAU,IAAI;AAAA;AAAA,IAC/B;AAAA,MAEA,oBAAC,UAAK,eAAY,QAAO,+BAAiB,GAE9C;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// src/catalogue/saved-catalogue-sync.ts
|
|
2
|
+
import {
|
|
3
|
+
pageKey,
|
|
4
|
+
pageState,
|
|
5
|
+
readSavedPages,
|
|
6
|
+
replaceRefreshedCategories,
|
|
7
|
+
replaceSavedPages
|
|
8
|
+
} from "./catalogue-pages.js";
|
|
9
|
+
async function loadResources(client, scope) {
|
|
10
|
+
const [persistence, selected] = await Promise.all([
|
|
11
|
+
import("@zappie/sdk/persist/catalogue"),
|
|
12
|
+
import("@zappie/sdk/transport/selected-menu")
|
|
13
|
+
]);
|
|
14
|
+
return {
|
|
15
|
+
saved: persistence.createSavedCatalogue(client, scope),
|
|
16
|
+
selected: selected.createSelectedMenuReader(client)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function createSavedCatalogueSync({
|
|
20
|
+
client,
|
|
21
|
+
state,
|
|
22
|
+
scope,
|
|
23
|
+
revision,
|
|
24
|
+
publish
|
|
25
|
+
}) {
|
|
26
|
+
let resources;
|
|
27
|
+
function snapshot() {
|
|
28
|
+
return {
|
|
29
|
+
scope,
|
|
30
|
+
menu: state.menu,
|
|
31
|
+
coverage: { menuIds: [scope.menuId], complete: state.complete },
|
|
32
|
+
pages: [...state.savedPages],
|
|
33
|
+
etag: state.menu.version,
|
|
34
|
+
savedAt: Date.now(),
|
|
35
|
+
...revision ? { revision } : {}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async function save() {
|
|
39
|
+
if (await resources.saved.publish(snapshot()).catch(() => true)) {
|
|
40
|
+
state.abort.signal.throwIfAborted();
|
|
41
|
+
publish?.(state.menu);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function refresh() {
|
|
45
|
+
if (state.complete) {
|
|
46
|
+
const response = await resources.selected.read(scope, {
|
|
47
|
+
signal: state.abort.signal
|
|
48
|
+
});
|
|
49
|
+
state.abort.signal.throwIfAborted();
|
|
50
|
+
if ("notModified" in response) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
state.menu = response.menu;
|
|
54
|
+
await save();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const refreshed = await readSavedPages(
|
|
58
|
+
client,
|
|
59
|
+
scope.menuId,
|
|
60
|
+
scope.branchId,
|
|
61
|
+
state.savedPages,
|
|
62
|
+
state.abort.signal
|
|
63
|
+
);
|
|
64
|
+
state.abort.signal.throwIfAborted();
|
|
65
|
+
state.menu = replaceRefreshedCategories(state.menu, refreshed);
|
|
66
|
+
for (const { categoryId, page } of refreshed) {
|
|
67
|
+
state.pages.set(
|
|
68
|
+
pageKey(scope.menuId, categoryId),
|
|
69
|
+
pageState(page.nextCursor)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
replaceSavedPages(state.savedPages, refreshed);
|
|
73
|
+
await save();
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
get resources() {
|
|
77
|
+
return resources;
|
|
78
|
+
},
|
|
79
|
+
/** Shows the current menu now; saving runs behind it and never fails a read. */
|
|
80
|
+
emit() {
|
|
81
|
+
state.abort.signal.throwIfAborted();
|
|
82
|
+
publish?.(state.menu);
|
|
83
|
+
void resources?.saved.publish(snapshot()).catch(() => {
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
async restore() {
|
|
87
|
+
resources = await loadResources(client, scope);
|
|
88
|
+
const value = await resources.saved.restore();
|
|
89
|
+
state.abort.signal.throwIfAborted();
|
|
90
|
+
if (!value) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
state.menu = value.menu;
|
|
94
|
+
state.complete = value.coverage.complete;
|
|
95
|
+
state.mode = state.complete ? "eager" : "progressive";
|
|
96
|
+
state.savedPages.push(...value.pages);
|
|
97
|
+
for (const saved of value.pages) {
|
|
98
|
+
state.pages.set(
|
|
99
|
+
pageKey(scope.menuId, saved.categoryId),
|
|
100
|
+
pageState(saved.page.nextCursor)
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
publish?.(state.menu);
|
|
104
|
+
if (!revision || value.revision !== revision) {
|
|
105
|
+
void refresh().catch(() => {
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
createSavedCatalogueSync
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=saved-catalogue-sync.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/catalogue/saved-catalogue-sync.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Menu, Zappie } from \"@zappie/sdk\";\nimport type { SavedCatalogue } from \"@zappie/sdk/persist/catalogue\";\nimport {\n pageKey,\n pageState,\n readSavedPages,\n replaceRefreshedCategories,\n replaceSavedPages,\n type PageState,\n type SavedPage,\n} from \"./catalogue-pages\";\n\n/** The loader's mutable read state, shared with the saved-catalogue sync. */\nexport type LoaderState = {\n menu: Menu;\n mode: \"eager\" | \"progressive\" | null;\n complete: boolean;\n // Replaced on resume: an effect can be cleaned up and run again (React\n // StrictMode, <Activity>) without the loader being recreated.\n abort: AbortController;\n readonly pages: Map<string, PageState>;\n readonly savedPages: SavedPage[];\n};\n\ntype Scope = SavedCatalogue[\"scope\"];\n\nasync function loadResources(client: Zappie, scope: Scope) {\n const [persistence, selected] = await Promise.all([\n import(\"@zappie/sdk/persist/catalogue\"),\n import(\"@zappie/sdk/transport/selected-menu\"),\n ]);\n return {\n saved: persistence.createSavedCatalogue(client, scope),\n selected: selected.createSelectedMenuReader(client),\n };\n}\n\nexport type CatalogueResources = Awaited<ReturnType<typeof loadResources>>;\n\nexport function createSavedCatalogueSync({\n client,\n state,\n scope,\n revision,\n publish,\n}: {\n client: Zappie;\n state: LoaderState;\n scope: Scope;\n /** Host catalogue revision; a saved copy at this revision is not refreshed. */\n revision?: string | undefined;\n publish?: ((menu: Menu) => void) | undefined;\n}) {\n let resources: CatalogueResources | undefined;\n function snapshot(): SavedCatalogue {\n return {\n scope,\n menu: state.menu,\n coverage: { menuIds: [scope.menuId], complete: state.complete },\n pages: [...state.savedPages],\n etag: state.menu.version,\n savedAt: Date.now(),\n ...(revision ? { revision } : {}),\n };\n }\n async function save() {\n if (await resources!.saved.publish(snapshot()).catch(() => true)) {\n state.abort.signal.throwIfAborted();\n publish?.(state.menu);\n }\n }\n async function refresh() {\n if (state.complete) {\n const response = await resources!.selected.read(scope, {\n signal: state.abort.signal,\n });\n state.abort.signal.throwIfAborted();\n if (\"notModified\" in response) {\n return;\n }\n state.menu = response.menu;\n await save();\n return;\n }\n const refreshed = await readSavedPages(\n client,\n scope.menuId,\n scope.branchId,\n state.savedPages,\n state.abort.signal,\n );\n state.abort.signal.throwIfAborted();\n state.menu = replaceRefreshedCategories(state.menu, refreshed);\n for (const { categoryId, page } of refreshed) {\n state.pages.set(\n pageKey(scope.menuId, categoryId),\n pageState(page.nextCursor),\n );\n }\n replaceSavedPages(state.savedPages, refreshed);\n await save();\n }\n return {\n get resources() {\n return resources;\n },\n /** Shows the current menu now; saving runs behind it and never fails a read. */\n emit() {\n state.abort.signal.throwIfAborted();\n publish?.(state.menu);\n void resources?.saved.publish(snapshot()).catch(() => {});\n },\n async restore() {\n resources = await loadResources(client, scope);\n const value = await resources.saved.restore();\n state.abort.signal.throwIfAborted();\n if (!value) {\n return false;\n }\n state.menu = value.menu;\n state.complete = value.coverage.complete;\n state.mode = state.complete ? \"eager\" : \"progressive\";\n state.savedPages.push(...value.pages);\n for (const saved of value.pages) {\n state.pages.set(\n pageKey(scope.menuId, saved.categoryId),\n pageState(saved.page.nextCursor),\n );\n }\n publish?.(state.menu);\n if (!revision || value.revision !== revision) {\n void refresh().catch(() => {});\n }\n return true;\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// src/catalogue/use-catalogue-data.ts
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { createCatalogueLoader } from "./loader.js";
|
|
4
|
+
function useCatalogueData({
|
|
5
|
+
client,
|
|
6
|
+
initialMenu,
|
|
7
|
+
selectedMenuId,
|
|
8
|
+
fulfilmentMode,
|
|
9
|
+
resolveStrategy,
|
|
10
|
+
revision
|
|
11
|
+
}) {
|
|
12
|
+
const [catalogue, setCatalogue] = useState(initialMenu);
|
|
13
|
+
const show = useCallback(
|
|
14
|
+
(value) => setCatalogue({
|
|
15
|
+
...value,
|
|
16
|
+
...initialMenu.menus ? { menus: initialMenu.menus } : {},
|
|
17
|
+
categories: initialMenu.categories.map(
|
|
18
|
+
(category) => value.categories.find((loaded) => loaded.id === category.id) ?? category
|
|
19
|
+
)
|
|
20
|
+
}),
|
|
21
|
+
[initialMenu]
|
|
22
|
+
);
|
|
23
|
+
const loader = useMemo(() => {
|
|
24
|
+
const selected = initialMenu.menus?.find(
|
|
25
|
+
(source2) => source2.id === selectedMenuId
|
|
26
|
+
);
|
|
27
|
+
const scoped = {
|
|
28
|
+
...initialMenu,
|
|
29
|
+
...selected ? { menus: [selected] } : {},
|
|
30
|
+
categories: initialMenu.categories.filter(
|
|
31
|
+
(category) => !selected || selected.categoryIds.includes(category.id)
|
|
32
|
+
)
|
|
33
|
+
};
|
|
34
|
+
return createCatalogueLoader(
|
|
35
|
+
client,
|
|
36
|
+
scoped,
|
|
37
|
+
resolveStrategy,
|
|
38
|
+
show,
|
|
39
|
+
fulfilmentMode,
|
|
40
|
+
revision
|
|
41
|
+
);
|
|
42
|
+
}, [
|
|
43
|
+
client,
|
|
44
|
+
initialMenu,
|
|
45
|
+
selectedMenuId,
|
|
46
|
+
fulfilmentMode,
|
|
47
|
+
resolveStrategy,
|
|
48
|
+
revision,
|
|
49
|
+
show
|
|
50
|
+
]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
loader.resume();
|
|
53
|
+
return () => loader.dispose();
|
|
54
|
+
}, [loader]);
|
|
55
|
+
const source = catalogue.menus?.find((value) => value.id === selectedMenuId) ?? catalogue.menus?.[0];
|
|
56
|
+
const categories = useMemo(
|
|
57
|
+
() => catalogue.categories.filter(
|
|
58
|
+
(category) => source?.categoryIds.includes(category.id)
|
|
59
|
+
),
|
|
60
|
+
[catalogue.categories, source]
|
|
61
|
+
);
|
|
62
|
+
const [error, setError] = useState();
|
|
63
|
+
const [busy, setBusy] = useState(false);
|
|
64
|
+
const reach = useCallback(
|
|
65
|
+
async (categoryId, more = false) => {
|
|
66
|
+
if (!source || !more && loader.isLoaded(source.id, categoryId)) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
setBusy(true);
|
|
70
|
+
setError(void 0);
|
|
71
|
+
try {
|
|
72
|
+
show(await loader.reach(source.id, categoryId, more));
|
|
73
|
+
} catch (failure) {
|
|
74
|
+
if (!(failure instanceof Error) || failure.name !== "AbortError") {
|
|
75
|
+
setError("Catalogue could not load. Try again.");
|
|
76
|
+
}
|
|
77
|
+
} finally {
|
|
78
|
+
setBusy(false);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
[loader, source, show]
|
|
82
|
+
);
|
|
83
|
+
return { catalogue, source, categories, loader, error, busy, reach };
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
useCatalogueData
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=use-catalogue-data.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/catalogue/use-catalogue-data.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Menu, Zappie } from \"@zappie/sdk\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { createCatalogueLoader, type CatalogueStrategy } from \"./loader\";\n\nexport function useCatalogueData({\n client,\n initialMenu,\n selectedMenuId,\n fulfilmentMode,\n resolveStrategy,\n revision,\n}: {\n client: Zappie;\n initialMenu: Menu;\n selectedMenuId: string;\n fulfilmentMode: \"pickup\" | \"delivery\";\n resolveStrategy: CatalogueStrategy;\n revision?: string;\n}) {\n const [catalogue, setCatalogue] = useState(initialMenu);\n const show = useCallback(\n (value: Menu) =>\n setCatalogue({\n ...value,\n ...(initialMenu.menus ? { menus: initialMenu.menus } : {}),\n categories: initialMenu.categories.map(\n (category) =>\n value.categories.find((loaded) => loaded.id === category.id) ??\n category,\n ),\n }),\n [initialMenu],\n );\n const loader = useMemo(() => {\n const selected = initialMenu.menus?.find(\n (source) => source.id === selectedMenuId,\n );\n const scoped = {\n ...initialMenu,\n ...(selected ? { menus: [selected] } : {}),\n categories: initialMenu.categories.filter(\n (category) => !selected || selected.categoryIds.includes(category.id),\n ),\n };\n return createCatalogueLoader(\n client,\n scoped,\n resolveStrategy,\n show,\n fulfilmentMode,\n revision,\n );\n }, [\n client,\n initialMenu,\n selectedMenuId,\n fulfilmentMode,\n resolveStrategy,\n revision,\n show,\n ]);\n useEffect(() => {\n loader.resume();\n return () => loader.dispose();\n }, [loader]);\n const source =\n catalogue.menus?.find((value) => value.id === selectedMenuId) ??\n catalogue.menus?.[0];\n const categories = useMemo(\n () =>\n catalogue.categories.filter((category) =>\n source?.categoryIds.includes(category.id),\n ),\n [catalogue.categories, source],\n );\n const [error, setError] = useState<string>();\n const [busy, setBusy] = useState(false);\n const reach = useCallback(\n async (categoryId: string, more = false) => {\n if (!source || (!more && loader.isLoaded(source.id, categoryId))) {\n return;\n }\n setBusy(true);\n setError(undefined);\n try {\n show(await loader.reach(source.id, categoryId, more));\n } catch (failure) {\n if (!(failure instanceof Error) || failure.name !== \"AbortError\") {\n setError(\"Catalogue could not load. Try again.\");\n }\n } finally {\n setBusy(false);\n }\n },\n [loader, source, show],\n );\n return { catalogue, source, categories, loader, error, busy, reach };\n}\n"],
|
|
5
|
+
"mappings": ";AACA,SAAS,aAAa,WAAW,SAAS,gBAAgB;AAC1D,SAAS,6BAAqD;AAEvD,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,WAAW;AACtD,QAAM,OAAO;AAAA,IACX,CAAC,UACC,aAAa;AAAA,MACX,GAAG;AAAA,MACH,GAAI,YAAY,QAAQ,EAAE,OAAO,YAAY,MAAM,IAAI,CAAC;AAAA,MACxD,YAAY,YAAY,WAAW;AAAA,QACjC,CAAC,aACC,MAAM,WAAW,KAAK,CAAC,WAAW,OAAO,OAAO,SAAS,EAAE,KAC3D;AAAA,MACJ;AAAA,IACF,CAAC;AAAA,IACH,CAAC,WAAW;AAAA,EACd;AACA,QAAM,SAAS,QAAQ,MAAM;AAC3B,UAAM,WAAW,YAAY,OAAO;AAAA,MAClC,CAACA,YAAWA,QAAO,OAAO;AAAA,IAC5B;AACA,UAAM,SAAS;AAAA,MACb,GAAG;AAAA,MACH,GAAI,WAAW,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC;AAAA,MACxC,YAAY,YAAY,WAAW;AAAA,QACjC,CAAC,aAAa,CAAC,YAAY,SAAS,YAAY,SAAS,SAAS,EAAE;AAAA,MACtE;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,YAAU,MAAM;AACd,WAAO,OAAO;AACd,WAAO,MAAM,OAAO,QAAQ;AAAA,EAC9B,GAAG,CAAC,MAAM,CAAC;AACX,QAAM,SACJ,UAAU,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,cAAc,KAC5D,UAAU,QAAQ,CAAC;AACrB,QAAM,aAAa;AAAA,IACjB,MACE,UAAU,WAAW;AAAA,MAAO,CAAC,aAC3B,QAAQ,YAAY,SAAS,SAAS,EAAE;AAAA,IAC1C;AAAA,IACF,CAAC,UAAU,YAAY,MAAM;AAAA,EAC/B;AACA,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAiB;AAC3C,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,QAAQ;AAAA,IACZ,OAAO,YAAoB,OAAO,UAAU;AAC1C,UAAI,CAAC,UAAW,CAAC,QAAQ,OAAO,SAAS,OAAO,IAAI,UAAU,GAAI;AAChE;AAAA,MACF;AACA,cAAQ,IAAI;AACZ,eAAS,MAAS;AAClB,UAAI;AACF,aAAK,MAAM,OAAO,MAAM,OAAO,IAAI,YAAY,IAAI,CAAC;AAAA,MACtD,SAAS,SAAS;AAChB,YAAI,EAAE,mBAAmB,UAAU,QAAQ,SAAS,cAAc;AAChE,mBAAS,sCAAsC;AAAA,QACjD;AAAA,MACF,UAAE;AACA,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,QAAQ,IAAI;AAAA,EACvB;AACA,SAAO,EAAE,WAAW,QAAQ,YAAY,QAAQ,OAAO,MAAM,MAAM;AACrE;",
|
|
6
|
+
"names": ["source"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// src/catalogue/use-category-scroll.ts
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
function useCategoryScroll({
|
|
4
|
+
categories,
|
|
5
|
+
selectedCategoryId,
|
|
6
|
+
itemOpen,
|
|
7
|
+
menuId,
|
|
8
|
+
menuLayout,
|
|
9
|
+
reach,
|
|
10
|
+
onSelectCategory
|
|
11
|
+
}) {
|
|
12
|
+
const [active, setActive] = useState(selectedCategoryId ?? categories[0]?.id);
|
|
13
|
+
const activeRef = useRef(active);
|
|
14
|
+
activeRef.current = active;
|
|
15
|
+
const sections = useRef(/* @__PURE__ */ new Map());
|
|
16
|
+
const wanted = useRef(void 0);
|
|
17
|
+
const reading = useRef(false);
|
|
18
|
+
const reachLatest = useCallback(
|
|
19
|
+
(categoryId) => {
|
|
20
|
+
wanted.current = categoryId;
|
|
21
|
+
if (reading.current) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
reading.current = true;
|
|
25
|
+
void (async () => {
|
|
26
|
+
try {
|
|
27
|
+
while (wanted.current) {
|
|
28
|
+
const next = wanted.current;
|
|
29
|
+
wanted.current = void 0;
|
|
30
|
+
await reach(next);
|
|
31
|
+
}
|
|
32
|
+
} finally {
|
|
33
|
+
reading.current = false;
|
|
34
|
+
}
|
|
35
|
+
})();
|
|
36
|
+
},
|
|
37
|
+
[reach]
|
|
38
|
+
);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (itemOpen) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const id = selectedCategoryId ?? categories[0]?.id;
|
|
44
|
+
if (id) {
|
|
45
|
+
setActive(id);
|
|
46
|
+
void reach(id);
|
|
47
|
+
}
|
|
48
|
+
}, [menuId, selectedCategoryId, itemOpen, reach, categories]);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (itemOpen) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
let frame = 0;
|
|
54
|
+
const select = () => {
|
|
55
|
+
frame = 0;
|
|
56
|
+
let id = categories[0]?.id;
|
|
57
|
+
for (const category of categories) {
|
|
58
|
+
const top = sections.current.get(category.id)?.getBoundingClientRect().top;
|
|
59
|
+
if (top === void 0) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (top > 200) {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
id = category.id;
|
|
66
|
+
}
|
|
67
|
+
if (window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 2) {
|
|
68
|
+
id = categories.at(-1)?.id ?? id;
|
|
69
|
+
}
|
|
70
|
+
if (id && id !== activeRef.current) {
|
|
71
|
+
setActive(id);
|
|
72
|
+
reachLatest(id);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const onScroll = () => {
|
|
76
|
+
if (!frame) {
|
|
77
|
+
frame = requestAnimationFrame(select);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
window.addEventListener("scroll", onScroll, { passive: true });
|
|
81
|
+
return () => {
|
|
82
|
+
window.removeEventListener("scroll", onScroll);
|
|
83
|
+
cancelAnimationFrame(frame);
|
|
84
|
+
};
|
|
85
|
+
}, [categories, itemOpen, reachLatest]);
|
|
86
|
+
const selectCategory = useCallback(
|
|
87
|
+
(id) => {
|
|
88
|
+
setActive(id);
|
|
89
|
+
onSelectCategory(id);
|
|
90
|
+
sections.current.get(id)?.scrollIntoView({ block: "start" });
|
|
91
|
+
void reach(id);
|
|
92
|
+
},
|
|
93
|
+
[onSelectCategory, reach]
|
|
94
|
+
);
|
|
95
|
+
const previousLayout = useRef(menuLayout);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (previousLayout.current !== menuLayout && active) {
|
|
98
|
+
sections.current.get(active)?.scrollIntoView({ block: "start" });
|
|
99
|
+
}
|
|
100
|
+
previousLayout.current = menuLayout;
|
|
101
|
+
}, [menuLayout, active]);
|
|
102
|
+
const focusCategory = useCallback((id) => {
|
|
103
|
+
sections.current.get(id)?.querySelector("h3")?.focus();
|
|
104
|
+
}, []);
|
|
105
|
+
return {
|
|
106
|
+
active,
|
|
107
|
+
sections,
|
|
108
|
+
selectCategory,
|
|
109
|
+
focusCategory
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
useCategoryScroll
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=use-category-scroll.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/catalogue/use-category-scroll.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Category } from \"@zappie/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport function useCategoryScroll({\n categories,\n selectedCategoryId,\n itemOpen,\n menuId,\n menuLayout,\n reach,\n onSelectCategory,\n}: {\n categories: readonly Category[];\n selectedCategoryId?: string;\n itemOpen: boolean;\n menuId?: string;\n menuLayout?: string;\n reach(categoryId: string): Promise<void>;\n onSelectCategory(categoryId: string): void;\n}) {\n const [active, setActive] = useState(selectedCategoryId ?? categories[0]?.id);\n const activeRef = useRef(active);\n activeRef.current = active;\n const sections = useRef(new Map<string, HTMLElement>());\n const wanted = useRef<string>(undefined);\n const reading = useRef(false);\n const reachLatest = useCallback(\n (categoryId: string) => {\n wanted.current = categoryId;\n if (reading.current) {\n return;\n }\n reading.current = true;\n void (async () => {\n try {\n while (wanted.current) {\n const next = wanted.current;\n wanted.current = undefined;\n await reach(next);\n }\n } finally {\n reading.current = false;\n }\n })();\n },\n [reach],\n );\n useEffect(() => {\n if (itemOpen) {\n return;\n }\n const id = selectedCategoryId ?? categories[0]?.id;\n if (id) {\n setActive(id);\n void reach(id);\n }\n }, [menuId, selectedCategoryId, itemOpen, reach, categories]);\n useEffect(() => {\n if (itemOpen) {\n return;\n }\n let frame = 0;\n const select = () => {\n frame = 0;\n let id = categories[0]?.id;\n for (const category of categories) {\n const top = sections.current\n .get(category.id)\n ?.getBoundingClientRect().top;\n if (top === undefined) {\n continue;\n }\n if (top > 200) {\n break;\n }\n id = category.id;\n }\n if (\n window.scrollY + window.innerHeight >=\n document.documentElement.scrollHeight - 2\n ) {\n id = categories.at(-1)?.id ?? id;\n }\n if (id && id !== activeRef.current) {\n setActive(id);\n reachLatest(id);\n }\n };\n const onScroll = () => {\n if (!frame) {\n frame = requestAnimationFrame(select);\n }\n };\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n return () => {\n window.removeEventListener(\"scroll\", onScroll);\n cancelAnimationFrame(frame);\n };\n }, [categories, itemOpen, reachLatest]);\n const selectCategory = useCallback(\n (id: string) => {\n setActive(id);\n onSelectCategory(id);\n sections.current.get(id)?.scrollIntoView({ block: \"start\" });\n void reach(id);\n },\n [onSelectCategory, reach],\n );\n const previousLayout = useRef(menuLayout);\n useEffect(() => {\n if (previousLayout.current !== menuLayout && active) {\n sections.current.get(active)?.scrollIntoView({ block: \"start\" });\n }\n previousLayout.current = menuLayout;\n }, [menuLayout, active]);\n const focusCategory = useCallback((id: string) => {\n sections.current.get(id)?.querySelector(\"h3\")?.focus();\n }, []);\n return {\n active,\n sections,\n selectCategory,\n focusCategory,\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/catalogue/use-item-opener.ts
|
|
2
|
+
import { useOverlay } from "@zappie/foundations";
|
|
3
|
+
import { useCallback, useEffect, useState } from "react";
|
|
4
|
+
function useItemOpener({
|
|
5
|
+
navigation,
|
|
6
|
+
source,
|
|
7
|
+
clearSearch
|
|
8
|
+
}) {
|
|
9
|
+
const searchOverlay = useOverlay("catalogue_search", "search");
|
|
10
|
+
const [pendingItem, setPendingItem] = useState();
|
|
11
|
+
const open = useCallback(
|
|
12
|
+
async (item) => {
|
|
13
|
+
if (searchOverlay.isOpen) {
|
|
14
|
+
setPendingItem(item);
|
|
15
|
+
searchOverlay.close();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const placement = item.placements?.find((value) => value.menuId === source?.id) ?? item.placements?.[0];
|
|
19
|
+
const menuId = placement?.menuId ?? source?.id;
|
|
20
|
+
if (menuId) {
|
|
21
|
+
clearSearch();
|
|
22
|
+
await navigation.openItem({
|
|
23
|
+
itemId: item.id,
|
|
24
|
+
item,
|
|
25
|
+
menuId,
|
|
26
|
+
...placement ? { categoryId: placement.categoryId } : {}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
[clearSearch, navigation, searchOverlay, source]
|
|
31
|
+
);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (pendingItem && !searchOverlay.isOpen) {
|
|
34
|
+
const frame = requestAnimationFrame(() => {
|
|
35
|
+
setPendingItem(void 0);
|
|
36
|
+
void open(pendingItem);
|
|
37
|
+
});
|
|
38
|
+
return () => cancelAnimationFrame(frame);
|
|
39
|
+
}
|
|
40
|
+
}, [pendingItem, searchOverlay.isOpen, open]);
|
|
41
|
+
return open;
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
useItemOpener
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=use-item-opener.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/catalogue/use-item-opener.ts"],
|
|
4
|
+
"sourcesContent": ["import { useOverlay } from \"@zappie/foundations\";\nimport type { Item, MenuSection } from \"@zappie/sdk\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport type { CatalogueNavigation } from \"../navigation\";\n\nexport function useItemOpener({\n navigation,\n source,\n clearSearch,\n}: {\n navigation: CatalogueNavigation;\n source?: MenuSection;\n clearSearch(): void;\n}) {\n const searchOverlay = useOverlay(\"catalogue_search\", \"search\");\n const [pendingItem, setPendingItem] = useState<Item>();\n const open = useCallback(\n async (item: Item) => {\n if (searchOverlay.isOpen) {\n setPendingItem(item);\n searchOverlay.close();\n return;\n }\n const placement =\n item.placements?.find((value) => value.menuId === source?.id) ??\n item.placements?.[0];\n const menuId = placement?.menuId ?? source?.id;\n if (menuId) {\n clearSearch();\n await navigation.openItem({\n itemId: item.id,\n item,\n menuId,\n ...(placement ? { categoryId: placement.categoryId } : {}),\n });\n }\n },\n [clearSearch, navigation, searchOverlay, source],\n );\n useEffect(() => {\n if (pendingItem && !searchOverlay.isOpen) {\n const frame = requestAnimationFrame(() => {\n setPendingItem(undefined);\n void open(pendingItem);\n });\n return () => cancelAnimationFrame(frame);\n }\n }, [pendingItem, searchOverlay.isOpen, open]);\n return open;\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,kBAAkB;AAE3B,SAAS,aAAa,WAAW,gBAAgB;AAG1C,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,gBAAgB,WAAW,oBAAoB,QAAQ;AAC7D,QAAM,CAAC,aAAa,cAAc,IAAI,SAAe;AACrD,QAAM,OAAO;AAAA,IACX,OAAO,SAAe;AACpB,UAAI,cAAc,QAAQ;AACxB,uBAAe,IAAI;AACnB,sBAAc,MAAM;AACpB;AAAA,MACF;AACA,YAAM,YACJ,KAAK,YAAY,KAAK,CAAC,UAAU,MAAM,WAAW,QAAQ,EAAE,KAC5D,KAAK,aAAa,CAAC;AACrB,YAAM,SAAS,WAAW,UAAU,QAAQ;AAC5C,UAAI,QAAQ;AACV,oBAAY;AACZ,cAAM,WAAW,SAAS;AAAA,UACxB,QAAQ,KAAK;AAAA,UACb;AAAA,UACA;AAAA,UACA,GAAI,YAAY,EAAE,YAAY,UAAU,WAAW,IAAI,CAAC;AAAA,QAC1D,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,CAAC,aAAa,YAAY,eAAe,MAAM;AAAA,EACjD;AACA,YAAU,MAAM;AACd,QAAI,eAAe,CAAC,cAAc,QAAQ;AACxC,YAAM,QAAQ,sBAAsB,MAAM;AACxC,uBAAe,MAAS;AACxB,aAAK,KAAK,WAAW;AAAA,MACvB,CAAC;AACD,aAAO,MAAM,qBAAqB,KAAK;AAAA,IACzC;AAAA,EACF,GAAG,CAAC,aAAa,cAAc,QAAQ,IAAI,CAAC;AAC5C,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/catalogue/use-search-source.ts
|
|
4
|
+
import { createSearchIndex } from "@zappie/foundations";
|
|
5
|
+
import { useZappie } from "@zappie/sdk/react";
|
|
6
|
+
import { useEffect, useMemo, useState } from "react";
|
|
7
|
+
function useSearchSource(menu, enabled) {
|
|
8
|
+
const client = useZappie();
|
|
9
|
+
const [query, setQuery] = useState("");
|
|
10
|
+
const [composing, setComposing] = useState(false);
|
|
11
|
+
const [attempt, setAttempt] = useState(0);
|
|
12
|
+
const [cursor, setCursor] = useState();
|
|
13
|
+
const [remote, setRemote] = useState({ query: "" });
|
|
14
|
+
const index = useMemo(() => createSearchIndex(menu), [menu]);
|
|
15
|
+
const local = useMemo(() => index(query), [index, query]);
|
|
16
|
+
const hasLocalItems = local.some((result) => result.type === "item");
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!enabled || composing || !query.trim() || hasLocalItems) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const abort = new AbortController();
|
|
22
|
+
const timer = setTimeout(() => {
|
|
23
|
+
setRemote((previous) => ({
|
|
24
|
+
...cursor && previous.query === query ? previous : {},
|
|
25
|
+
query,
|
|
26
|
+
loading: true
|
|
27
|
+
}));
|
|
28
|
+
void client.menu.search(query, {
|
|
29
|
+
branch: menu.branchId,
|
|
30
|
+
signal: abort.signal,
|
|
31
|
+
...cursor ? { cursor } : {}
|
|
32
|
+
}).then(
|
|
33
|
+
(page) => {
|
|
34
|
+
if (!abort.signal.aborted) {
|
|
35
|
+
setRemote((previous) => {
|
|
36
|
+
if (!cursor || previous.query !== query || !previous.page) {
|
|
37
|
+
return { query, page };
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
query,
|
|
41
|
+
page: {
|
|
42
|
+
...page,
|
|
43
|
+
menu: {
|
|
44
|
+
...page.menu,
|
|
45
|
+
items: {
|
|
46
|
+
...previous.page.menu.items,
|
|
47
|
+
...page.menu.items
|
|
48
|
+
},
|
|
49
|
+
categories: [
|
|
50
|
+
...new Map(
|
|
51
|
+
[
|
|
52
|
+
...previous.page.menu.categories,
|
|
53
|
+
...page.menu.categories
|
|
54
|
+
].map((category) => [category.id, category])
|
|
55
|
+
).values()
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
() => {
|
|
64
|
+
if (!abort.signal.aborted) {
|
|
65
|
+
setRemote((previous) => ({
|
|
66
|
+
...cursor && previous.query === query ? previous : {},
|
|
67
|
+
query,
|
|
68
|
+
loading: false,
|
|
69
|
+
error: "Search could not load. Try again."
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
}, 500);
|
|
75
|
+
return () => {
|
|
76
|
+
clearTimeout(timer);
|
|
77
|
+
abort.abort();
|
|
78
|
+
};
|
|
79
|
+
}, [
|
|
80
|
+
client,
|
|
81
|
+
menu.branchId,
|
|
82
|
+
query,
|
|
83
|
+
enabled,
|
|
84
|
+
composing,
|
|
85
|
+
hasLocalItems,
|
|
86
|
+
attempt,
|
|
87
|
+
cursor
|
|
88
|
+
]);
|
|
89
|
+
const current = !composing && remote.query === query && !hasLocalItems ? remote : void 0;
|
|
90
|
+
const results = useMemo(() => {
|
|
91
|
+
const values = [...local];
|
|
92
|
+
for (const item of Object.values(current?.page?.menu.items ?? {})) {
|
|
93
|
+
if (!values.some(
|
|
94
|
+
(result) => result.type === "item" && result.item.id === item.id
|
|
95
|
+
)) {
|
|
96
|
+
values.push({ type: "item", item, score: 0 });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const category of current?.page?.menu.categories ?? []) {
|
|
100
|
+
if (category.name.toLocaleLowerCase().includes(query.trim().toLocaleLowerCase()) && !values.some(
|
|
101
|
+
(result) => result.type === "category" && result.category.id === category.id
|
|
102
|
+
)) {
|
|
103
|
+
values.push({ type: "category", category, score: 0 });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return values;
|
|
107
|
+
}, [local, current?.page, query]);
|
|
108
|
+
return {
|
|
109
|
+
query,
|
|
110
|
+
setQuery: (value) => {
|
|
111
|
+
setCursor(void 0);
|
|
112
|
+
setQuery(value);
|
|
113
|
+
},
|
|
114
|
+
hasMore: Boolean(current?.page?.nextCursor),
|
|
115
|
+
loadMore: () => {
|
|
116
|
+
if (!current?.loading && current?.page?.nextCursor) {
|
|
117
|
+
setCursor(current.page.nextCursor);
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
source: {
|
|
121
|
+
results,
|
|
122
|
+
isLoading: current?.loading ?? false,
|
|
123
|
+
...current?.error ? { error: current.error } : {},
|
|
124
|
+
retry: () => setAttempt((value) => value + 1),
|
|
125
|
+
onCompositionChange: setComposing
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
useSearchSource
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=use-search-source.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/catalogue/use-search-source.ts"],
|
|
4
|
+
"sourcesContent": ["\"use client\";\nimport { createSearchIndex, type SearchResult } from \"@zappie/foundations\";\nimport type { Menu, SearchPage } from \"@zappie/sdk\";\nimport { useZappie } from \"@zappie/sdk/react\";\nimport { useEffect, useMemo, useState } from \"react\";\n\n/** Loaded matches win; remote fallback never hydrates a complete catalogue. */\nexport function useSearchSource(menu: Menu, enabled: boolean) {\n const client = useZappie();\n const [query, setQuery] = useState(\"\");\n const [composing, setComposing] = useState(false);\n const [attempt, setAttempt] = useState(0);\n const [cursor, setCursor] = useState<string>();\n const [remote, setRemote] = useState<{\n query: string;\n page?: SearchPage;\n error?: string;\n loading?: boolean;\n }>({ query: \"\" });\n const index = useMemo(() => createSearchIndex(menu), [menu]);\n const local = useMemo(() => index(query), [index, query]);\n const hasLocalItems = local.some((result) => result.type === \"item\");\n useEffect(() => {\n if (!enabled || composing || !query.trim() || hasLocalItems) {\n return;\n }\n const abort = new AbortController();\n const timer = setTimeout(() => {\n setRemote((previous) => ({\n ...(cursor && previous.query === query ? previous : {}),\n query,\n loading: true,\n }));\n void client.menu\n .search(query, {\n branch: menu.branchId,\n signal: abort.signal,\n ...(cursor ? { cursor } : {}),\n })\n .then(\n (page) => {\n if (!abort.signal.aborted) {\n setRemote((previous) => {\n if (!cursor || previous.query !== query || !previous.page) {\n return { query, page };\n }\n return {\n query,\n page: {\n ...page,\n menu: {\n ...page.menu,\n items: {\n ...previous.page.menu.items,\n ...page.menu.items,\n },\n categories: [\n ...new Map(\n [\n ...previous.page.menu.categories,\n ...page.menu.categories,\n ].map((category) => [category.id, category]),\n ).values(),\n ],\n },\n },\n };\n });\n }\n },\n () => {\n if (!abort.signal.aborted) {\n setRemote((previous) => ({\n ...(cursor && previous.query === query ? previous : {}),\n query,\n loading: false,\n error: \"Search could not load. Try again.\",\n }));\n }\n },\n );\n }, 500);\n return () => {\n clearTimeout(timer);\n abort.abort();\n };\n }, [\n client,\n menu.branchId,\n query,\n enabled,\n composing,\n hasLocalItems,\n attempt,\n cursor,\n ]);\n const current =\n !composing && remote.query === query && !hasLocalItems ? remote : undefined;\n const results = useMemo(() => {\n const values: SearchResult[] = [...local];\n for (const item of Object.values(current?.page?.menu.items ?? {})) {\n if (\n !values.some(\n (result) => result.type === \"item\" && result.item.id === item.id,\n )\n ) {\n values.push({ type: \"item\", item, score: 0 });\n }\n }\n for (const category of current?.page?.menu.categories ?? []) {\n if (\n category.name\n .toLocaleLowerCase()\n .includes(query.trim().toLocaleLowerCase()) &&\n !values.some(\n (result) =>\n result.type === \"category\" && result.category.id === category.id,\n )\n ) {\n values.push({ type: \"category\", category, score: 0 });\n }\n }\n return values;\n }, [local, current?.page, query]);\n return {\n query,\n setQuery: (value: string) => {\n setCursor(undefined);\n setQuery(value);\n },\n hasMore: Boolean(current?.page?.nextCursor),\n loadMore: () => {\n if (!current?.loading && current?.page?.nextCursor) {\n setCursor(current.page.nextCursor);\n }\n },\n source: {\n results,\n isLoading: current?.loading ?? false,\n ...(current?.error ? { error: current.error } : {}),\n retry: () => setAttempt((value) => value + 1),\n onCompositionChange: setComposing,\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|