@zappie/foundations 0.1.1 → 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.
Files changed (72) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/account/index.js +49 -0
  3. package/dist/account/index.js.map +7 -0
  4. package/dist/cart/index.js +168 -0
  5. package/dist/cart/index.js.map +7 -0
  6. package/dist/fulfilment/address-picker.js +168 -0
  7. package/dist/fulfilment/address-picker.js.map +7 -0
  8. package/dist/fulfilment/branch-picker.js +54 -0
  9. package/dist/fulfilment/branch-picker.js.map +7 -0
  10. package/dist/fulfilment/schedule.js +146 -0
  11. package/dist/fulfilment/schedule.js.map +7 -0
  12. package/dist/fulfilment/switch.js +54 -0
  13. package/dist/fulfilment/switch.js.map +7 -0
  14. package/dist/index.js +48 -2114
  15. package/dist/index.js.map +7 -1
  16. package/dist/internal/combobox.js +131 -0
  17. package/dist/internal/combobox.js.map +7 -0
  18. package/dist/internal/data-attrs.js +8 -0
  19. package/dist/internal/data-attrs.js.map +7 -0
  20. package/dist/internal/ids.js +10 -0
  21. package/dist/internal/ids.js.map +7 -0
  22. package/dist/{chunk-D3J7S33L.js → internal/slot.js} +13 -6
  23. package/dist/internal/slot.js.map +7 -0
  24. package/dist/internal/use-controllable-state.js +29 -0
  25. package/dist/internal/use-controllable-state.js.map +7 -0
  26. package/dist/item-form/index.js +13 -0
  27. package/dist/item-form/index.js.map +7 -0
  28. package/dist/item-form/option-group.js +268 -0
  29. package/dist/item-form/option-group.js.map +7 -0
  30. package/dist/item-form/options.js +30 -0
  31. package/dist/item-form/options.js.map +7 -0
  32. package/dist/item-form/quantity.js +131 -0
  33. package/dist/item-form/quantity.js.map +7 -0
  34. package/dist/item-form/root.js +151 -0
  35. package/dist/item-form/root.js.map +7 -0
  36. package/dist/media/image.js +30 -0
  37. package/dist/media/image.js.map +7 -0
  38. package/dist/money.js +20 -0
  39. package/dist/money.js.map +7 -0
  40. package/dist/next.js +8 -6
  41. package/dist/next.js.map +7 -1
  42. package/dist/otp/input.js +110 -0
  43. package/dist/otp/input.js.map +7 -0
  44. package/dist/overlay/engine.js +16 -8
  45. package/dist/overlay/engine.js.map +7 -1
  46. package/dist/{chunk-BY2WQGLO.js → overlay/history.js} +9 -4
  47. package/dist/overlay/history.js.map +7 -0
  48. package/dist/overlay/react.js +261 -0
  49. package/dist/overlay/react.js.map +7 -0
  50. package/dist/payment/methods.js +103 -0
  51. package/dist/payment/methods.js.map +7 -0
  52. package/dist/rich-text/banners.js +9 -0
  53. package/dist/rich-text/banners.js.map +7 -0
  54. package/dist/rich-text/browser.js +3 -3
  55. package/dist/rich-text/browser.js.map +7 -1
  56. package/dist/rich-text/core.js +75 -0
  57. package/dist/rich-text/core.js.map +7 -0
  58. package/dist/rich-text/index.js +3 -79
  59. package/dist/rich-text/index.js.map +7 -1
  60. package/dist/search/engine.js +93 -0
  61. package/dist/search/engine.js.map +7 -0
  62. package/dist/search/index.js +175 -0
  63. package/dist/search/index.js.map +7 -0
  64. package/dist/status/hours.js +177 -0
  65. package/dist/status/hours.js.map +7 -0
  66. package/dist/status/open-status.js +45 -0
  67. package/dist/status/open-status.js.map +7 -0
  68. package/dist/version.js +6 -0
  69. package/dist/version.js.map +7 -0
  70. package/package.json +3 -3
  71. package/dist/chunk-BY2WQGLO.js.map +0 -1
  72. package/dist/chunk-D3J7S33L.js.map +0 -1
@@ -0,0 +1,146 @@
1
+ "use client";
2
+
3
+ // src/fulfilment/schedule.tsx
4
+ import { useMenu, useStore } from "@zappie/sdk/react";
5
+ import { forwardRef, useEffect, useMemo, useState } from "react";
6
+ import { part } from "../internal/slot.js";
7
+ import { useControllableState } from "../internal/use-controllable-state.js";
8
+ import { getOpenStatus, localParts, openingIntervals } from "../status/hours.js";
9
+ function scheduleSlots(branch, timezone, now, {
10
+ intervalMinutes = 30,
11
+ maxDays = 14,
12
+ noticeMinutes = 0,
13
+ menuSchedule
14
+ } = {}) {
15
+ if (!Number.isInteger(intervalMinutes) || intervalMinutes < 1 || !Number.isInteger(maxDays) || maxDays < 1 || maxDays > 90 || !Number.isFinite(noticeMinutes) || noticeMinutes < 0) {
16
+ throw new RangeError(
17
+ "Schedule requires a positive interval, 1\u201390 days and a non-negative notice"
18
+ );
19
+ }
20
+ if (!branch.fulfilment.scheduling) {
21
+ return [];
22
+ }
23
+ const preorder = menuSchedule?.preorder ? menuSchedule : void 0;
24
+ const days = preorder?.preorderMaxDays && preorder.preorderMaxDays > 0 ? Math.min(maxDays, preorder.preorderMaxDays) : maxDays;
25
+ const earliest = now.getTime() + Math.max(noticeMinutes, (preorder?.preorderNoticeHours ?? 0) * 60) * 6e4;
26
+ const latest = now.getTime() + days * 864e5;
27
+ const menuIntervals = menuSchedule?.openingTimes.length ? openingIntervals(
28
+ {
29
+ ...branch,
30
+ openingTimes: menuSchedule.openingTimes,
31
+ specialHours: []
32
+ },
33
+ timezone,
34
+ now,
35
+ days + 1
36
+ ) : void 0;
37
+ const slots = [];
38
+ for (const { start, end } of openingIntervals(
39
+ branch,
40
+ timezone,
41
+ now,
42
+ days + 1
43
+ )) {
44
+ const first = start + Math.max(0, Math.ceil((earliest - start) / (intervalMinutes * 6e4))) * intervalMinutes * 6e4;
45
+ for (let instant = first; instant < end && instant <= latest; instant += intervalMinutes * 6e4) {
46
+ if (menuIntervals && !menuIntervals.some(
47
+ (interval) => interval.start <= instant && interval.end > instant
48
+ )) {
49
+ continue;
50
+ }
51
+ const date = new Date(instant);
52
+ slots.push({
53
+ value: date.toISOString(),
54
+ date: localParts(date, timezone).date,
55
+ label: new Intl.DateTimeFormat("en", {
56
+ timeZone: timezone,
57
+ hour: "numeric",
58
+ minute: "2-digit",
59
+ timeZoneName: "shortOffset"
60
+ }).format(date)
61
+ });
62
+ }
63
+ }
64
+ return slots;
65
+ }
66
+ var Schedule = forwardRef(function Schedule2({
67
+ branch,
68
+ timezone: suppliedTimezone,
69
+ value,
70
+ defaultValue = null,
71
+ onValueChange,
72
+ intervalMinutes,
73
+ maxDays,
74
+ noticeMinutes,
75
+ ...props
76
+ }, ref) {
77
+ const store = useStore();
78
+ const menu = useMenu(branch.id, { metadataOnly: true });
79
+ const timezone = suppliedTimezone ?? branch.timezone ?? store.data?.timezone ?? "UTC";
80
+ const [now, setNow] = useState(() => /* @__PURE__ */ new Date());
81
+ useEffect(() => {
82
+ const timer = setTimeout(
83
+ () => setNow(/* @__PURE__ */ new Date()),
84
+ 6e4 - Date.now() % 6e4
85
+ );
86
+ return () => clearTimeout(timer);
87
+ }, [now]);
88
+ const [selection, setSelection] = useControllableState({
89
+ value,
90
+ defaultValue,
91
+ onChange: onValueChange
92
+ });
93
+ const slots = useMemo(
94
+ () => menu.isLoading || menu.error ? [] : scheduleSlots(branch, timezone, now, {
95
+ intervalMinutes,
96
+ maxDays,
97
+ noticeMinutes,
98
+ menuSchedule: menu.data?.schedule
99
+ }),
100
+ [
101
+ branch,
102
+ timezone,
103
+ now,
104
+ intervalMinutes,
105
+ maxDays,
106
+ noticeMinutes,
107
+ menu.data?.schedule,
108
+ menu.isLoading,
109
+ menu.error
110
+ ]
111
+ );
112
+ const days = Array.from(new Set(slots.map((slot) => slot.date))).map(
113
+ (date) => ({ date, slots: slots.filter((slot) => slot.date === date) })
114
+ );
115
+ const canAsap = !menu.isLoading && !menu.error && !menu.data?.schedule?.preorder && getOpenStatus(branch, timezone, now).isOpen;
116
+ const isValid = selection === null ? canAsap : slots.some((slot) => slot.value === selection);
117
+ const setValue = (next) => {
118
+ if (next === null ? canAsap : slots.some((slot) => slot.value === next)) {
119
+ setSelection(next);
120
+ }
121
+ };
122
+ return part(
123
+ "div",
124
+ props,
125
+ {
126
+ days,
127
+ slots,
128
+ value: selection,
129
+ setValue,
130
+ isAsap: selection === null,
131
+ canAsap,
132
+ isValid
133
+ },
134
+ {
135
+ role: "group",
136
+ "data-state": isValid ? "valid" : "invalid",
137
+ "data-invalid": isValid ? void 0 : ""
138
+ },
139
+ ref
140
+ );
141
+ });
142
+ export {
143
+ Schedule,
144
+ scheduleSlots
145
+ };
146
+ //# sourceMappingURL=schedule.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/fulfilment/schedule.tsx"],
4
+ "sourcesContent": ["\"use client\";\nimport type { Branch, MenuSchedule } from \"@zappie/sdk\";\nimport { useMenu, useStore } from \"@zappie/sdk/react\";\nimport { forwardRef, useEffect, useMemo, useState } from \"react\";\nimport { part, type PartProps } from \"../internal/slot\";\nimport { useControllableState } from \"../internal/use-controllable-state\";\nimport { getOpenStatus, localParts, openingIntervals } from \"../status/hours\";\n/**\n * A selectable time with an ISO instant, local date and display label including the UTC offset.\n */\nexport type ScheduleSlot = { value: string; label: string; date: string };\n/**\n * Available days and slots, the selected time and controls for choosing ASAP or a scheduled time.\n */\nexport type ScheduleState = {\n days: { date: string; slots: ScheduleSlot[] }[];\n slots: ScheduleSlot[];\n value: string | null;\n setValue: (value: string | null) => void;\n isAsap: boolean;\n canAsap: boolean;\n isValid: boolean;\n};\n/**\n * Generate scheduling slots within branch hours and any supplied menu hours and preorder limits.\n * Returns no slots when the branch does not support scheduling.\n *\n * @param branch - The branch whose hours and scheduling capability apply.\n * @param timezone - The time zone used to interpret opening hours and label slots.\n * @param now - The instant from which notice and scheduling limits are measured.\n * @returns Slots with ISO values, local dates and display labels.\n */\nexport function scheduleSlots(\n branch: Branch,\n timezone: string,\n now: Date,\n {\n intervalMinutes = 30,\n maxDays = 14,\n noticeMinutes = 0,\n menuSchedule,\n }: {\n intervalMinutes?: number | undefined;\n maxDays?: number | undefined;\n noticeMinutes?: number | undefined;\n menuSchedule?: MenuSchedule | undefined;\n } = {},\n) {\n if (\n !Number.isInteger(intervalMinutes) ||\n intervalMinutes < 1 ||\n !Number.isInteger(maxDays) ||\n maxDays < 1 ||\n maxDays > 90 ||\n !Number.isFinite(noticeMinutes) ||\n noticeMinutes < 0\n ) {\n throw new RangeError(\n \"Schedule requires a positive interval, 1\u201390 days and a non-negative notice\",\n );\n }\n if (!branch.fulfilment.scheduling) {\n return [];\n }\n // Pre-order limits apply to pre-order menus only. Other menus carry zeros\n // (the API's default), and a zero horizon would leave nothing to schedule.\n const preorder = menuSchedule?.preorder ? menuSchedule : undefined;\n const days =\n preorder?.preorderMaxDays && preorder.preorderMaxDays > 0\n ? Math.min(maxDays, preorder.preorderMaxDays)\n : maxDays;\n const earliest =\n now.getTime() +\n Math.max(noticeMinutes, (preorder?.preorderNoticeHours ?? 0) * 60) * 60000;\n const latest = now.getTime() + days * 86400000;\n const menuIntervals = menuSchedule?.openingTimes.length\n ? openingIntervals(\n {\n ...branch,\n openingTimes: menuSchedule.openingTimes,\n specialHours: [],\n },\n timezone,\n now,\n days + 1,\n )\n : undefined;\n const slots: ScheduleSlot[] = [];\n for (const { start, end } of openingIntervals(\n branch,\n timezone,\n now,\n days + 1,\n )) {\n const first =\n start +\n Math.max(0, Math.ceil((earliest - start) / (intervalMinutes * 60000))) *\n intervalMinutes *\n 60000;\n for (\n let instant = first;\n instant < end && instant <= latest;\n instant += intervalMinutes * 60000\n ) {\n if (\n menuIntervals &&\n !menuIntervals.some(\n (interval) => interval.start <= instant && interval.end > instant,\n )\n ) {\n continue;\n }\n const date = new Date(instant);\n slots.push({\n value: date.toISOString(),\n date: localParts(date, timezone).date,\n label: new Intl.DateTimeFormat(\"en\", {\n timeZone: timezone,\n hour: \"numeric\",\n minute: \"2-digit\",\n timeZoneName: \"shortOffset\",\n }).format(date),\n });\n }\n }\n return slots;\n}\n/**\n * Expose valid fulfilment slots and ASAP eligibility using branch hours and loaded menu metadata.\n * Use null for ASAP. Requires a ZappieProvider and refreshes the available times each minute.\n */\nexport const Schedule = forwardRef<\n HTMLElement,\n PartProps<ScheduleState> & {\n branch: Branch;\n timezone?: string;\n value?: string | null;\n defaultValue?: string | null;\n onValueChange?: (value: string | null) => void;\n intervalMinutes?: number | undefined;\n maxDays?: number | undefined;\n noticeMinutes?: number;\n }\n>(function Schedule(\n {\n branch,\n timezone: suppliedTimezone,\n value,\n defaultValue = null,\n onValueChange,\n intervalMinutes,\n maxDays,\n noticeMinutes,\n ...props\n },\n ref,\n) {\n const store = useStore();\n const menu = useMenu(branch.id, { metadataOnly: true });\n const timezone =\n suppliedTimezone ?? branch.timezone ?? store.data?.timezone ?? \"UTC\";\n const [now, setNow] = useState(() => new Date());\n useEffect(() => {\n const timer = setTimeout(\n () => setNow(new Date()),\n 60000 - (Date.now() % 60000),\n );\n return () => clearTimeout(timer);\n }, [now]);\n const [selection, setSelection] = useControllableState({\n value,\n defaultValue,\n onChange: onValueChange,\n });\n const slots = useMemo(\n () =>\n menu.isLoading || menu.error\n ? []\n : scheduleSlots(branch, timezone, now, {\n intervalMinutes,\n maxDays,\n noticeMinutes,\n menuSchedule: menu.data?.schedule,\n }),\n [\n branch,\n timezone,\n now,\n intervalMinutes,\n maxDays,\n noticeMinutes,\n menu.data?.schedule,\n menu.isLoading,\n menu.error,\n ],\n );\n const days = Array.from(new Set(slots.map((slot) => slot.date))).map(\n (date) => ({ date, slots: slots.filter((slot) => slot.date === date) }),\n );\n const canAsap =\n !menu.isLoading &&\n !menu.error &&\n !menu.data?.schedule?.preorder &&\n getOpenStatus(branch, timezone, now).isOpen;\n const isValid =\n selection === null\n ? canAsap\n : slots.some((slot) => slot.value === selection);\n const setValue = (next: string | null) => {\n if (next === null ? canAsap : slots.some((slot) => slot.value === next)) {\n setSelection(next);\n }\n };\n return part(\n \"div\",\n props,\n {\n days,\n slots,\n value: selection,\n setValue,\n isAsap: selection === null,\n canAsap,\n isValid,\n },\n {\n role: \"group\",\n \"data-state\": isValid ? \"valid\" : \"invalid\",\n \"data-invalid\": isValid ? undefined : \"\",\n },\n ref,\n );\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["Schedule"]
7
+ }
@@ -0,0 +1,54 @@
1
+ "use client";
2
+
3
+ // src/fulfilment/switch.tsx
4
+ import { ZappieError } from "@zappie/sdk";
5
+ import { useCart, useStore } from "@zappie/sdk/react";
6
+ import { forwardRef } from "react";
7
+ import { part } from "../internal/slot.js";
8
+ import { useControllableState } from "../internal/use-controllable-state.js";
9
+ var Switch = forwardRef(function Switch2({ value, defaultValue, onValueChange, ...props }, ref) {
10
+ const cart = useCart();
11
+ const store = useStore();
12
+ const [selection, setSelection] = useControllableState({
13
+ value,
14
+ defaultValue: defaultValue ?? cart.fulfilment,
15
+ onChange: onValueChange
16
+ });
17
+ const current = value === void 0 && defaultValue === void 0 ? cart.fulfilment : selection;
18
+ const branch = store.data?.branches.find(
19
+ (branch2) => branch2.id === (cart.branchId ?? current?.branchId)
20
+ ) ?? store.data?.branches.find((branch2) => branch2.isDefault) ?? store.data?.branches[0];
21
+ const available = branch?.fulfilment;
22
+ const options = [];
23
+ if (available?.pickup) {
24
+ options.push("pickup");
25
+ }
26
+ if (available?.delivery) {
27
+ options.push("delivery");
28
+ }
29
+ if (available?.dineIn) {
30
+ options.push("dine_in");
31
+ }
32
+ const setValue = (next) => {
33
+ if (!options.includes(next.type) || next.branchId !== branch?.id) {
34
+ throw new ZappieError(
35
+ "invalid_request",
36
+ "Select an offered fulfilment mode for this branch"
37
+ );
38
+ }
39
+ cart.actions.setFulfilment(next);
40
+ setSelection(next);
41
+ };
42
+ return part(
43
+ "div",
44
+ props,
45
+ { options, value: current, setValue },
46
+ { role: "group" },
47
+ ref
48
+ );
49
+ });
50
+ var Fulfilment = { Switch };
51
+ export {
52
+ Fulfilment
53
+ };
54
+ //# sourceMappingURL=switch.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/fulfilment/switch.tsx"],
4
+ "sourcesContent": ["\"use client\";\nimport { ZappieError, type CartFulfilment } from \"@zappie/sdk\";\nimport { useCart, useStore } from \"@zappie/sdk/react\";\nimport { forwardRef } from \"react\";\nimport { part, type PartProps } from \"../internal/slot\";\nimport { useControllableState } from \"../internal/use-controllable-state\";\nexport type FulfilmentState = {\n options: CartFulfilment[\"type\"][];\n value: CartFulfilment | null;\n setValue: (value: CartFulfilment) => void;\n};\nconst Switch = forwardRef<\n HTMLElement,\n PartProps<FulfilmentState> & {\n value?: CartFulfilment | null;\n defaultValue?: CartFulfilment | null;\n onValueChange?: (value: CartFulfilment | null) => void;\n }\n>(function Switch({ value, defaultValue, onValueChange, ...props }, ref) {\n const cart = useCart();\n const store = useStore();\n const [selection, setSelection] = useControllableState({\n value,\n defaultValue: defaultValue ?? cart.fulfilment,\n onChange: onValueChange,\n });\n const current =\n value === undefined && defaultValue === undefined\n ? cart.fulfilment\n : selection;\n const branch =\n store.data?.branches.find(\n (branch) => branch.id === (cart.branchId ?? current?.branchId),\n ) ??\n store.data?.branches.find((branch) => branch.isDefault) ??\n store.data?.branches[0];\n const available = branch?.fulfilment;\n const options: CartFulfilment[\"type\"][] = [];\n if (available?.pickup) {\n options.push(\"pickup\");\n }\n if (available?.delivery) {\n options.push(\"delivery\");\n }\n if (available?.dineIn) {\n options.push(\"dine_in\");\n }\n const setValue = (next: CartFulfilment) => {\n if (!options.includes(next.type) || next.branchId !== branch?.id) {\n throw new ZappieError(\n \"invalid_request\",\n \"Select an offered fulfilment mode for this branch\",\n );\n }\n cart.actions.setFulfilment(next);\n setSelection(next);\n };\n return part(\n \"div\",\n props,\n { options, value: current, setValue },\n { role: \"group\" },\n ref,\n );\n});\n/**\n * A fulfilment switch that exposes the active branch modes and updates the SDK cart.\n * Selections must use an offered mode and the active branch ID.\n */\nexport const Fulfilment = { Switch };\n"],
5
+ "mappings": ";;;AACA,SAAS,mBAAwC;AACjD,SAAS,SAAS,gBAAgB;AAClC,SAAS,kBAAkB;AAC3B,SAAS,YAA4B;AACrC,SAAS,4BAA4B;AAMrC,IAAM,SAAS,WAOb,SAASA,QAAO,EAAE,OAAO,cAAc,eAAe,GAAG,MAAM,GAAG,KAAK;AACvE,QAAM,OAAO,QAAQ;AACrB,QAAM,QAAQ,SAAS;AACvB,QAAM,CAAC,WAAW,YAAY,IAAI,qBAAqB;AAAA,IACrD;AAAA,IACA,cAAc,gBAAgB,KAAK;AAAA,IACnC,UAAU;AAAA,EACZ,CAAC;AACD,QAAM,UACJ,UAAU,UAAa,iBAAiB,SACpC,KAAK,aACL;AACN,QAAM,SACJ,MAAM,MAAM,SAAS;AAAA,IACnB,CAACC,YAAWA,QAAO,QAAQ,KAAK,YAAY,SAAS;AAAA,EACvD,KACA,MAAM,MAAM,SAAS,KAAK,CAACA,YAAWA,QAAO,SAAS,KACtD,MAAM,MAAM,SAAS,CAAC;AACxB,QAAM,YAAY,QAAQ;AAC1B,QAAM,UAAoC,CAAC;AAC3C,MAAI,WAAW,QAAQ;AACrB,YAAQ,KAAK,QAAQ;AAAA,EACvB;AACA,MAAI,WAAW,UAAU;AACvB,YAAQ,KAAK,UAAU;AAAA,EACzB;AACA,MAAI,WAAW,QAAQ;AACrB,YAAQ,KAAK,SAAS;AAAA,EACxB;AACA,QAAM,WAAW,CAAC,SAAyB;AACzC,QAAI,CAAC,QAAQ,SAAS,KAAK,IAAI,KAAK,KAAK,aAAa,QAAQ,IAAI;AAChE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,SAAK,QAAQ,cAAc,IAAI;AAC/B,iBAAa,IAAI;AAAA,EACnB;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,EAAE,SAAS,OAAO,SAAS,SAAS;AAAA,IACpC,EAAE,MAAM,QAAQ;AAAA,IAChB;AAAA,EACF;AACF,CAAC;AAKM,IAAM,aAAa,EAAE,OAAO;",
6
+ "names": ["Switch", "branch"]
7
+ }