@zappie/foundations 0.1.4 → 0.1.6
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 +29 -0
- package/README.md +73 -13
- package/dist/branch-map/colors.js +27 -0
- package/dist/branch-map/colors.js.map +7 -0
- package/dist/branch-map/delivery-map.js +60 -0
- package/dist/branch-map/delivery-map.js.map +7 -0
- package/dist/branch-map/delivery-runtime.js +118 -0
- package/dist/branch-map/delivery-runtime.js.map +7 -0
- package/dist/branch-map/fit.js +123 -0
- package/dist/branch-map/fit.js.map +7 -0
- package/dist/branch-map/geometry.js +1 -30
- package/dist/branch-map/geometry.js.map +2 -2
- package/dist/branch-map/index.js +21 -53
- package/dist/branch-map/index.js.map +2 -2
- package/dist/branch-map/markers.js +183 -0
- package/dist/branch-map/markers.js.map +7 -0
- package/dist/branch-map/runtime.js +235 -99
- package/dist/branch-map/runtime.js.map +3 -3
- package/dist/branch-map/use-map-runtime.js +53 -0
- package/dist/branch-map/use-map-runtime.js.map +7 -0
- package/dist/deferred/index.js +42 -11
- package/dist/deferred/index.js.map +2 -2
- package/dist/{engine-XTsgCwwe.d.ts → engine-Bfbv9M0D.d.ts} +1 -2
- package/dist/index.d.ts +74 -23
- package/dist/index.js +2 -1
- package/dist/index.js.map +2 -2
- package/dist/item-form/root.js.map +1 -1
- package/dist/overlay/engine.d.ts +1 -2
- package/dist/overlay/react.js +224 -13
- package/dist/overlay/react.js.map +2 -2
- package/dist/version.js +1 -1
- package/package.json +5 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/deferred/index.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\nimport {\n Fragment,\n useEffect,\n useMemo,\n useState,\n type ComponentType,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\n/** Props for a deferred component: its own props plus `open`, which the helper reads and passes on. */\nexport type DeferredProps<P> = Omit<P, \"open\"> & {\n /**\n * Load the code if needed and render the component. Before the first open\n * it renders nothing; after it, the component stays mounted and receives\n * `open`, so it can animate out and return focus when it closes.\n */\n open: boolean;\n /** Shown while `open` is true and the code is still loading, for inline parts that should hold their place. */\n fallback?: ReactNode;\n};\n\n/** Options for `preloadWhenIdle`. */\nexport type DeferredIdleOptions = {\n /** Start the load after this many milliseconds even if the browser never goes idle. */\n timeout?: number;\n};\n\n/**\n * A component whose code loads on demand and renders without suspending.\n * Create it once at module scope with `deferred()`.\n */\nexport type Deferred<P> = {\n /**\n * Start loading the code. Repeated calls share one load. A failed load is\n * forgotten, so the next call tries again.\n * @returns The loaded component, or the load error.\n */\n preload(): Promise<ComponentType<P>>;\n /**\n * Load the code once the browser is idle. Use it only for small chunks.\n * Does nothing on the server.\n * @returns A function that cancels the scheduled load.\n */\n preloadWhenIdle(options?: DeferredIdleOptions): () => void;\n /**\n * Renders nothing until `open` is first true and the code is in. When\n * `open` turns true it loads the code and renders as soon as it arrives.
|
|
5
|
-
"mappings": ";;;AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;
|
|
4
|
+
"sourcesContent": ["\"use client\";\nimport {\n Fragment,\n useEffect,\n useMemo,\n useState,\n type ComponentType,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\n/** Props for a deferred component: its own props plus `open`, which the helper reads and passes on. */\nexport type DeferredProps<P> = Omit<P, \"open\"> & {\n /**\n * Load the code if needed and render the component. Before the first open\n * it renders nothing; after it, the component stays mounted and receives\n * `open`, so it can animate out and return focus when it closes.\n */\n open: boolean;\n /** Shown while `open` is true and the code is still loading, for inline parts that should hold their place. */\n fallback?: ReactNode;\n /**\n * Shown while `open` is true and the code failed to load. Call `retry` to load it again.\n * A later `preload()`, such as the next tap on the trigger, also retries and renders on success.\n */\n failed?: (retry: () => void) => ReactNode;\n};\n\n/** Options for `preloadWhenIdle`. */\nexport type DeferredIdleOptions = {\n /** Start the load after this many milliseconds even if the browser never goes idle. */\n timeout?: number;\n};\n\n/**\n * A component whose code loads on demand and renders without suspending.\n * Create it once at module scope with `deferred()`.\n */\nexport type Deferred<P> = {\n /**\n * Start loading the code. Repeated calls share one load. A failed load is\n * forgotten, so the next call tries again.\n * @returns The loaded component, or the load error.\n */\n preload(): Promise<ComponentType<P>>;\n /**\n * Load the code once the browser is idle. Use it only for small chunks.\n * Does nothing on the server.\n * @returns A function that cancels the scheduled load.\n */\n preloadWhenIdle(options?: DeferredIdleOptions): () => void;\n /**\n * Renders nothing until `open` is first true and the code is in. When\n * `open` turns true it loads the code and renders as soon as it arrives. If\n * the load fails it shows `failed` and renders once a retry or a later\n * `preload()` succeeds. It never suspends. After the first open the component stays mounted and\n * every prop passes through, `open` included, so it can run its own exit.\n */\n Component: (props: DeferredProps<P>) => ReactElement | null;\n};\n\n/** Event handlers that start loading a deferred component when a shopper reaches for its trigger. */\nexport type IntentPreloadHandlers = {\n onPointerEnter: () => void;\n onPointerDown: () => void;\n onFocus: () => void;\n};\n\n/** What a deferred load resolves to: a component, or a module whose default export is one. */\nexport type DeferredModule =\n ComponentType<never> | { default: ComponentType<never> };\n\n/** The props of the component a deferred load resolves to. */\nexport type DeferredModuleProps<T> = T extends {\n default: ComponentType<infer P>;\n}\n ? P\n : T extends ComponentType<infer P>\n ? P\n : never;\n\ntype AnyComponent = ComponentType<Record<string, unknown>>;\n\nfunction component(value: DeferredModule): AnyComponent {\n if (\n typeof value === \"object\" &&\n value !== null &&\n \"default\" in value &&\n !(\"$$typeof\" in value)\n ) {\n return value.default as AnyComponent;\n }\n return value as AnyComponent;\n}\n\nconst ignore = () => {};\n\n/**\n * Load a component's code on demand without `React.lazy` or `<Suspense>`.\n * React holds a suspended boundary's first reveal for about 300 ms, so a\n * lazy overlay opens late. A deferred one renders on the frame its code\n * arrives. Pass a module whose default export is the component, or resolve\n * to the component itself.\n *\n * @example\n * const Bag = deferred(() => import(\"./bag-sheet\"));\n * const Finder = deferred(() => import(\"./finder\").then((m) => m.Finder));\n * <Bag.Component open={open} onClose={close} />\n */\nexport function deferred<T extends DeferredModule>(\n load: () => Promise<T>,\n): Deferred<DeferredModuleProps<T>> {\n type P = DeferredModuleProps<T>;\n let loaded: AnyComponent | undefined;\n let pending: Promise<AnyComponent> | undefined;\n // Mounted instances that are waiting for the code, told when any load succeeds.\n const waiting = new Set<(value: AnyComponent) => void>();\n\n const preload = (): Promise<AnyComponent> => {\n if (loaded) {\n return Promise.resolve(loaded);\n }\n if (!pending) {\n // Starts the import now. A load that throws, or a module whose component\n // cannot be read, becomes a rejection the next call retries.\n const attempt = new Promise<T>((resolve) => resolve(load()))\n .then(component)\n .then(\n (result) => {\n loaded = result;\n for (const notify of waiting) {\n notify(result);\n }\n return result;\n },\n (error: unknown) => {\n // Forget the failure so the next open or preload tries again.\n if (pending === attempt) {\n pending = undefined;\n }\n throw error;\n },\n );\n // Callers may ignore the promise; the component reports nothing either.\n attempt.catch(ignore);\n pending = attempt;\n }\n return pending;\n };\n\n const preloadWhenIdle = (options: DeferredIdleOptions = {}) => {\n if (typeof window === \"undefined\" || loaded) {\n return ignore;\n }\n const start = () => {\n preload().catch(ignore);\n };\n if (typeof window.requestIdleCallback === \"function\") {\n const handle = window.requestIdleCallback(\n start,\n options.timeout === undefined\n ? undefined\n : { timeout: options.timeout },\n );\n return () => window.cancelIdleCallback(handle);\n }\n const handle = window.setTimeout(start, options.timeout ?? 1);\n return () => window.clearTimeout(handle);\n };\n\n function DeferredComponent({\n fallback = null,\n failed,\n ...props\n }: DeferredProps<P>) {\n const { open } = props;\n const [ready, setReady] = useState<AnyComponent | undefined>(() => loaded);\n const [opened, setOpened] = useState<boolean>(open);\n // A failed load shows `failed` until a retry or a new open starts another.\n const [failure, setFailure] = useState(false);\n const [retries, setRetries] = useState(0);\n if (open && !opened) {\n setOpened(true);\n }\n const Loaded = ready ?? loaded;\n useEffect(() => {\n if (Loaded) {\n return;\n }\n const notify = (value: AnyComponent) => setReady(() => value);\n waiting.add(notify);\n return () => {\n waiting.delete(notify);\n };\n }, [Loaded]);\n useEffect(() => {\n if (!open || Loaded) {\n return;\n }\n let active = true;\n setFailure(false);\n preload().then(\n (value) => {\n if (active) {\n setReady(() => value);\n }\n },\n () => {\n if (active) {\n setFailure(true);\n }\n },\n );\n return () => {\n active = false;\n };\n }, [open, Loaded, retries]);\n if (!(open || opened)) {\n return null;\n }\n if (!Loaded) {\n if (!open) {\n return null;\n }\n return (\n <Fragment>\n {failure && failed\n ? failed(() => setRetries((count) => count + 1))\n : fallback}\n </Fragment>\n );\n }\n return <Loaded {...(props as Record<string, unknown>)} />;\n }\n DeferredComponent.displayName = \"Deferred\";\n\n return {\n // The load resolved to the component typed by T.\n preload: preload as () => Promise<ComponentType<P>>,\n preloadWhenIdle,\n Component: DeferredComponent,\n };\n}\n\n/**\n * Start loading a deferred component when the shopper points at, presses or\n * focuses its trigger, so the code is usually in by the click. Spread the\n * result on the trigger.\n *\n * @example\n * const intent = useIntentPreload(Bag);\n * <button {...intent} onClick={openBag}>Bag</button>\n */\nexport function useIntentPreload(target: {\n preload(): Promise<unknown>;\n}): IntentPreloadHandlers {\n return useMemo(() => {\n const start = () => {\n target.preload().catch(ignore);\n };\n return { onPointerEnter: start, onPointerDown: start, onFocus: start };\n }, [target]);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["handle"]
|
|
7
7
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { AllHTMLAttributes, ReactNode, Ref } from 'react';
|
|
3
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
3
|
import { Dialog } from '@base-ui/react/dialog';
|
|
5
4
|
|
|
6
5
|
/**
|
|
@@ -41,7 +40,7 @@ type Parts = {
|
|
|
41
40
|
declare function useEngineParts(): Parts;
|
|
42
41
|
declare function Surface(props: SurfaceProps & {
|
|
43
42
|
children?: ReactNode;
|
|
44
|
-
}):
|
|
43
|
+
}): react.JSX.Element;
|
|
45
44
|
declare const EngineTitle: react.ForwardRefExoticComponent<Omit<react.AllHTMLAttributes<HTMLElement>, "children" | "defaultValue" | "value"> & {
|
|
46
45
|
asChild?: boolean;
|
|
47
46
|
children?: ReactNode | ((state: unknown) => ReactNode);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { HTMLAttributes, ReactNode, ComponentType, ReactElement, KeyboardEvent as KeyboardEvent$1, ImgHTMLAttributes, ChangeEvent, ClipboardEvent } from 'react';
|
|
3
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
3
|
import * as _zappie_sdk from '@zappie/sdk';
|
|
5
4
|
import { Branch as Branch$1, CartLine, CartLineInput, CartIssue, CartFix, Quote, DeliveryAddress, MenuSchedule, CartFulfilment, OptionGroup as OptionGroup$1, Item, Money as Money$1, Choice as Choice$1, MoneyFormatOptions, PaymentMethodKind, Category, Menu, OpeningTime } from '@zappie/sdk';
|
|
6
5
|
import { useCart } from '@zappie/sdk/react';
|
|
7
|
-
import { P as PartProps, a as Presentation, S as SwipeDirection } from './engine-
|
|
6
|
+
import { P as PartProps, a as Presentation, S as SwipeDirection } from './engine-Bfbv9M0D.js';
|
|
8
7
|
import { C as CloseOptions, O as OverlayQuery } from './history-Bqu12Vkz.js';
|
|
9
8
|
import '@base-ui/react/dialog';
|
|
10
9
|
|
|
@@ -68,19 +67,56 @@ type Coordinates = {
|
|
|
68
67
|
lng: number;
|
|
69
68
|
};
|
|
70
69
|
|
|
71
|
-
/** What a template gets to render when
|
|
72
|
-
type
|
|
70
|
+
/** What a template gets to render when a map fails to load. */
|
|
71
|
+
type MapLoadErrorState = {
|
|
73
72
|
/** Why the map failed to load. */
|
|
74
73
|
error: unknown;
|
|
75
74
|
/** Load the map again. */
|
|
76
75
|
retry: () => void;
|
|
77
76
|
};
|
|
77
|
+
|
|
78
|
+
/** The branch, the shopper's address and an optional route for DeliveryMap. */
|
|
79
|
+
type DeliveryMapProps = HTMLAttributes<HTMLDivElement> & {
|
|
80
|
+
/** The branch the order leaves from. Only its coordinates are read. */
|
|
81
|
+
branch: Pick<Branch$1, "coordinates">;
|
|
82
|
+
/** Coordinates of the delivery address. */
|
|
83
|
+
shopper?: Coordinates | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* The path to draw, such as a decoded driving route, from the branch to the
|
|
86
|
+
* shopper. Without it the map draws a straight line.
|
|
87
|
+
*/
|
|
88
|
+
route?: Coordinates[];
|
|
89
|
+
browserKey?: string;
|
|
90
|
+
/** Image shown inside the branch marker, such as the business logo. */
|
|
91
|
+
pinImageUrl?: string;
|
|
92
|
+
/** Called each time the map fails to load. */
|
|
93
|
+
onLoadError?: (error: unknown) => void;
|
|
94
|
+
/**
|
|
95
|
+
* Render your own message when the map fails to load, such as a retry
|
|
96
|
+
* button that calls `retry`. Without it the map shows "Map location unavailable".
|
|
97
|
+
*/
|
|
98
|
+
renderLoadError?: (state: MapLoadErrorState) => ReactNode;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* A static map of a delivery: the branch marker, the shopper's house marker
|
|
102
|
+
* and the line between them, fitted to both. It has no controls and ignores
|
|
103
|
+
* gestures. Maps loads after mount when you supply a browser key. Set the
|
|
104
|
+
* container height with CSS. Colour the house marker with --map-accent and
|
|
105
|
+
* the line with --delivery-map-route.
|
|
106
|
+
*/
|
|
107
|
+
declare function DeliveryMap({ branch, shopper, route, browserKey, pinImageUrl, onLoadError, renderLoadError, ...props }: DeliveryMapProps): react.JSX.Element | null;
|
|
108
|
+
|
|
109
|
+
/** What a template gets to render when the map fails to load. */
|
|
110
|
+
type BranchMapLoadErrorState = MapLoadErrorState;
|
|
78
111
|
/** Branches, selection and optional shopper coordinates for BranchMap. */
|
|
79
112
|
type BranchMapProps = Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> & {
|
|
80
113
|
branches: Branch$1[];
|
|
81
114
|
selectedBranchId?: string;
|
|
82
115
|
onSelect: (branch: Branch$1) => void;
|
|
83
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* The shopper's coordinates. The map shows them with a house marker and
|
|
118
|
+
* draws a line to the selected branch, or to the nearest one.
|
|
119
|
+
*/
|
|
84
120
|
shopperLocation?: Coordinates;
|
|
85
121
|
browserKey?: string;
|
|
86
122
|
/**
|
|
@@ -88,12 +124,18 @@ type BranchMapProps = Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> & {
|
|
|
88
124
|
* is shown and 14 otherwise.
|
|
89
125
|
*/
|
|
90
126
|
maxZoom?: number;
|
|
91
|
-
/** Image shown inside each branch pin. */
|
|
127
|
+
/** Image shown inside each branch pin, such as the business logo. */
|
|
92
128
|
pinImageUrl?: string;
|
|
93
129
|
/** Show the map and satellite switch. Defaults to false. */
|
|
94
130
|
mapTypeControl?: boolean;
|
|
95
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* Show the branch name above the selected pin, or above every pin. Off by
|
|
133
|
+
* default. With a shopper location and no selection, "selected" labels the
|
|
134
|
+
* nearest branch.
|
|
135
|
+
*/
|
|
96
136
|
pinLabel?: "selected" | "all";
|
|
137
|
+
/** Frame a small map, with tighter padding and a wider single-branch view. */
|
|
138
|
+
compact?: boolean;
|
|
97
139
|
/** Called each time the map fails to load. */
|
|
98
140
|
onLoadError?: (error: unknown) => void;
|
|
99
141
|
/**
|
|
@@ -104,10 +146,13 @@ type BranchMapProps = Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> & {
|
|
|
104
146
|
};
|
|
105
147
|
/**
|
|
106
148
|
* Show branch locations. Maps loads after mount when you supply a browser key.
|
|
107
|
-
*
|
|
108
|
-
*
|
|
149
|
+
* Nearby branches group into numbered clusters. Set the container height with
|
|
150
|
+
* CSS. Colour clusters, the shopper marker, labels and the route with
|
|
151
|
+
* --map-accent and --map-accent-foreground. Style dot pins with
|
|
152
|
+
* --branch-map-pin, --branch-map-pin-selected, --branch-map-pin-border and
|
|
153
|
+
* --branch-map-pin-size.
|
|
109
154
|
*/
|
|
110
|
-
declare function BranchMap({ branches, selectedBranchId, onSelect, shopperLocation, browserKey, maxZoom, pinImageUrl, mapTypeControl, pinLabel, onLoadError, renderLoadError, ...props }: BranchMapProps):
|
|
155
|
+
declare function BranchMap({ branches, selectedBranchId, onSelect, shopperLocation, browserKey, maxZoom, pinImageUrl, mapTypeControl, pinLabel, compact, onLoadError, renderLoadError, ...props }: BranchMapProps): react.JSX.Element | null;
|
|
111
156
|
|
|
112
157
|
/**
|
|
113
158
|
* The current quantity, bounds, step, disabled state and value setter exposed by a quantity control.
|
|
@@ -132,7 +177,7 @@ type QuantityProps = PartProps<QuantityState> & {
|
|
|
132
177
|
step?: number;
|
|
133
178
|
disabled?: boolean;
|
|
134
179
|
};
|
|
135
|
-
declare function Root$5({ value, defaultValue, onValueChange, min, max, step, disabled, ...props }: QuantityProps):
|
|
180
|
+
declare function Root$5({ value, defaultValue, onValueChange, min, max, step, disabled, ...props }: QuantityProps): react.JSX.Element;
|
|
136
181
|
/**
|
|
137
182
|
* Composable quantity controls with bounded step changes and keyboard support.
|
|
138
183
|
* Wrap Value, Increment and Decrement in Quantity.Root.
|
|
@@ -163,7 +208,7 @@ declare const Quantity: {
|
|
|
163
208
|
};
|
|
164
209
|
|
|
165
210
|
type CartContext = ReturnType<typeof useCart>;
|
|
166
|
-
declare function Root$4(props: PartProps<CartContext>):
|
|
211
|
+
declare function Root$4(props: PartProps<CartContext>): react.JSX.Element;
|
|
167
212
|
type LineContext = {
|
|
168
213
|
line: CartLine;
|
|
169
214
|
item: _zappie_sdk.Item | undefined;
|
|
@@ -172,7 +217,7 @@ type LineContext = {
|
|
|
172
217
|
};
|
|
173
218
|
declare function CartQuantity({ lineId, ...props }: Omit<QuantityProps, "value" | "defaultValue" | "onValueChange"> & {
|
|
174
219
|
lineId: string;
|
|
175
|
-
}):
|
|
220
|
+
}): react.JSX.Element | null;
|
|
176
221
|
/**
|
|
177
222
|
* Composable cart parts for lines, quantity changes, removal, empty state and validation issues.
|
|
178
223
|
* Wrap the parts in Cart.Root inside your ZappieProvider.
|
|
@@ -230,6 +275,11 @@ type DeferredProps<P> = Omit<P, "open"> & {
|
|
|
230
275
|
open: boolean;
|
|
231
276
|
/** Shown while `open` is true and the code is still loading, for inline parts that should hold their place. */
|
|
232
277
|
fallback?: ReactNode;
|
|
278
|
+
/**
|
|
279
|
+
* Shown while `open` is true and the code failed to load. Call `retry` to load it again.
|
|
280
|
+
* A later `preload()`, such as the next tap on the trigger, also retries and renders on success.
|
|
281
|
+
*/
|
|
282
|
+
failed?: (retry: () => void) => ReactNode;
|
|
233
283
|
};
|
|
234
284
|
/** Options for `preloadWhenIdle`. */
|
|
235
285
|
type DeferredIdleOptions = {
|
|
@@ -255,8 +305,9 @@ type Deferred<P> = {
|
|
|
255
305
|
preloadWhenIdle(options?: DeferredIdleOptions): () => void;
|
|
256
306
|
/**
|
|
257
307
|
* Renders nothing until `open` is first true and the code is in. When
|
|
258
|
-
* `open` turns true it loads the code and renders as soon as it arrives.
|
|
259
|
-
*
|
|
308
|
+
* `open` turns true it loads the code and renders as soon as it arrives. If
|
|
309
|
+
* the load fails it shows `failed` and renders once a retry or a later
|
|
310
|
+
* `preload()` succeeds. It never suspends. After the first open the component stays mounted and
|
|
260
311
|
* every prop passes through, `open` included, so it can run its own exit.
|
|
261
312
|
*/
|
|
262
313
|
Component: (props: DeferredProps<P>) => ReactElement | null;
|
|
@@ -368,7 +419,7 @@ declare function Picker({ getSuggestions, onSelect, query, defaultQuery, onQuery
|
|
|
368
419
|
defaultQuery?: string;
|
|
369
420
|
onQueryChange?: (query: string) => void;
|
|
370
421
|
debounceMs?: number;
|
|
371
|
-
}):
|
|
422
|
+
}): react.JSX.Element;
|
|
372
423
|
/**
|
|
373
424
|
* Composable address picker parts with debounced suggestions and keyboard navigation.
|
|
374
425
|
* Supply getSuggestions and onSelect to Address.Picker. It cancels outdated requests.
|
|
@@ -538,7 +589,7 @@ type ItemFormInvalidGroup = {
|
|
|
538
589
|
* Item form configuration with controlled or default selections and cart commit callbacks.
|
|
539
590
|
* Supply onCommit to update an existing line instead of adding a new one.
|
|
540
591
|
*/
|
|
541
|
-
type ItemFormProps = PartProps<ItemFormContext> & {
|
|
592
|
+
type ItemFormProps = Omit<PartProps<ItemFormContext>, "onInvalid"> & {
|
|
542
593
|
item: Item;
|
|
543
594
|
value?: CartLineInput;
|
|
544
595
|
defaultValue?: Partial<CartLineInput>;
|
|
@@ -555,7 +606,7 @@ type ItemFormProps = PartProps<ItemFormContext> & {
|
|
|
555
606
|
*/
|
|
556
607
|
onInvalid?: (groups: ItemFormInvalidGroup[]) => void;
|
|
557
608
|
};
|
|
558
|
-
declare function Root$3({ item, value, defaultValue, onValueChange, defaultQuantity, onAdded, onCommit, onInvalid, ...props }: ItemFormProps):
|
|
609
|
+
declare function Root$3({ item, value, defaultValue, onValueChange, defaultQuantity, onAdded, onCommit, onInvalid, ...props }: ItemFormProps): react.JSX.Element;
|
|
559
610
|
|
|
560
611
|
type GroupState = {
|
|
561
612
|
group: OptionGroup$1;
|
|
@@ -631,7 +682,7 @@ type ImageProps = Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "srcSet"> &
|
|
|
631
682
|
mode?: "resize" | "direct";
|
|
632
683
|
};
|
|
633
684
|
/** Render an image through same-origin resizing, or directly when unavailable. */
|
|
634
|
-
declare function Image({ src, sizes, priority, quality, mode, alt, ...props }: ImageProps):
|
|
685
|
+
declare function Image({ src, sizes, priority, quality, mode, alt, ...props }: ImageProps): react.JSX.Element;
|
|
635
686
|
|
|
636
687
|
/**
|
|
637
688
|
* Render formatted SDK money in a data element, with the minor-unit amount as its value.
|
|
@@ -724,7 +775,7 @@ declare function Root$2({ param, id, presentation, swipeDirection, fallback, omi
|
|
|
724
775
|
presentation?: ResponsivePresentation;
|
|
725
776
|
/** Swipe direction to dismiss. Defaults to `down` for drawers and `right` for sheets. */
|
|
726
777
|
swipeDirection?: SwipeDirection;
|
|
727
|
-
} & CloseOptions):
|
|
778
|
+
} & CloseOptions): react.JSX.Element;
|
|
728
779
|
/**
|
|
729
780
|
* Composable dialog, drawer and sheet parts with URL-backed open state and a lazy modal engine.
|
|
730
781
|
* Use Root, Trigger, Content, Title and Close to connect history, focus handling and dismissal.
|
|
@@ -792,7 +843,7 @@ declare function Root$1({ methods, value, defaultValue, onValueChange, ...props
|
|
|
792
843
|
value?: PaymentMethodKind | null;
|
|
793
844
|
defaultValue?: PaymentMethodKind | null;
|
|
794
845
|
onValueChange?: (value: PaymentMethodKind | null) => void;
|
|
795
|
-
}):
|
|
846
|
+
}): react.JSX.Element;
|
|
796
847
|
/**
|
|
797
848
|
* Composable payment-method radio controls with keyboard navigation.
|
|
798
849
|
* Root manages the selection. Item renders each method and prevents selection when disabled.
|
|
@@ -860,7 +911,7 @@ type RootProps = Omit<PartProps<SearchState>, "onSelect"> & {
|
|
|
860
911
|
onSelect?: (result: SearchResult) => void;
|
|
861
912
|
source?: SearchSource;
|
|
862
913
|
};
|
|
863
|
-
declare function Root(props: RootProps):
|
|
914
|
+
declare function Root(props: RootProps): react.JSX.Element;
|
|
864
915
|
/**
|
|
865
916
|
* Composable menu search parts with combobox navigation and result announcements.
|
|
866
917
|
* Use Root with your own source or let it index the active branch menu through your ZappieProvider.
|
|
@@ -950,4 +1001,4 @@ declare const OpenStatus: react.ForwardRefExoticComponent<Omit<react.AllHTMLAttr
|
|
|
950
1001
|
/** The installed @zappie/foundations version. */
|
|
951
1002
|
declare const FOUNDATIONS_VERSION: string;
|
|
952
1003
|
|
|
953
|
-
export { Account, AccountNotConfiguredError, type ActiveGroup, Address, type AddressState, type AddressSuggestion, Branch, BranchMap, type BranchMapLoadErrorState, type BranchMapProps, Cart, Choice, type Customer, type Deferred, type DeferredIdleOptions, type DeferredProps, FOUNDATIONS_VERSION, Fulfilment, Image, type ImageProps, type IntentPreloadHandlers, ItemForm, type ItemFormContext, type ItemFormInvalidGroup, type ItemFormProps, Money, OpenStatus, OptionGroup, Otp, Overlay, PartProps, type PaymentMethod, PaymentMethods, Quantity, type QuantityProps, type QuantityState, Schedule, type ScheduleSlot, type ScheduleState, Search, type SearchResult, type SearchSource, type SearchState, type SignInOptions, Totals, type TotalsState, createSearchIndex, deferred, formatOpeningHours, formatWallClock, getOpenStatus, parseWallClock, scheduleSlots, useControllableState, useIntentPreload, useOverlay };
|
|
1004
|
+
export { Account, AccountNotConfiguredError, type ActiveGroup, Address, type AddressState, type AddressSuggestion, Branch, BranchMap, type BranchMapLoadErrorState, type BranchMapProps, Cart, Choice, type Customer, type Deferred, type DeferredIdleOptions, type DeferredProps, DeliveryMap, type DeliveryMapProps, FOUNDATIONS_VERSION, Fulfilment, Image, type ImageProps, type IntentPreloadHandlers, ItemForm, type ItemFormContext, type ItemFormInvalidGroup, type ItemFormProps, type MapLoadErrorState, Money, OpenStatus, OptionGroup, Otp, Overlay, PartProps, type PaymentMethod, PaymentMethods, Quantity, type QuantityProps, type QuantityState, Schedule, type ScheduleSlot, type ScheduleState, Search, type SearchResult, type SearchSource, type SearchState, type SignInOptions, Totals, type TotalsState, createSearchIndex, deferred, formatOpeningHours, formatWallClock, getOpenStatus, parseWallClock, scheduleSlots, useControllableState, useIntentPreload, useOverlay };
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { PACKAGE_VERSION } from "./version.js";
|
|
5
5
|
import { Account, AccountNotConfiguredError } from "./account/index.js";
|
|
6
|
-
import { BranchMap } from "./branch-map/index.js";
|
|
6
|
+
import { BranchMap, DeliveryMap } from "./branch-map/index.js";
|
|
7
7
|
import { Cart, Totals } from "./cart/index.js";
|
|
8
8
|
import { deferred, useIntentPreload } from "./deferred/index.js";
|
|
9
9
|
import { Address } from "./fulfilment/address-picker.js";
|
|
@@ -33,6 +33,7 @@ export {
|
|
|
33
33
|
Branch,
|
|
34
34
|
BranchMap,
|
|
35
35
|
Cart,
|
|
36
|
+
DeliveryMap,
|
|
36
37
|
FOUNDATIONS_VERSION,
|
|
37
38
|
Fulfilment,
|
|
38
39
|
Image,
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\nimport { PACKAGE_VERSION } from \"./version\";\n\n/** The installed @zappie/foundations version. */\nexport const FOUNDATIONS_VERSION: string = PACKAGE_VERSION;\nexport { Account, AccountNotConfiguredError } from \"./account\";\nexport type { Customer, SignInOptions } from \"./account\";\nexport { BranchMap } from \"./branch-map\";\nexport type {
|
|
5
|
-
"mappings": ";;;AACA,SAAS,uBAAuB;AAIhC,SAAS,SAAS,iCAAiC;AAEnD,SAAS,
|
|
4
|
+
"sourcesContent": ["\"use client\";\nimport { PACKAGE_VERSION } from \"./version\";\n\n/** The installed @zappie/foundations version. */\nexport const FOUNDATIONS_VERSION: string = PACKAGE_VERSION;\nexport { Account, AccountNotConfiguredError } from \"./account\";\nexport type { Customer, SignInOptions } from \"./account\";\nexport { BranchMap, DeliveryMap } from \"./branch-map\";\nexport type {\n BranchMapLoadErrorState,\n BranchMapProps,\n DeliveryMapProps,\n MapLoadErrorState,\n} from \"./branch-map\";\nexport { Cart, Totals } from \"./cart\";\nexport type { TotalsState } from \"./cart\";\nexport { deferred, useIntentPreload } from \"./deferred\";\nexport type {\n Deferred,\n DeferredIdleOptions,\n DeferredProps,\n IntentPreloadHandlers,\n} from \"./deferred\";\nexport { Address } from \"./fulfilment/address-picker\";\nexport type {\n AddressState,\n AddressSuggestion,\n} from \"./fulfilment/address-picker\";\nexport { Branch } from \"./fulfilment/branch-picker\";\nexport { Schedule, scheduleSlots } from \"./fulfilment/schedule\";\nexport type { ScheduleSlot, ScheduleState } from \"./fulfilment/schedule\";\nexport { Fulfilment } from \"./fulfilment/switch\";\nexport type { PartProps } from \"./internal/slot\";\nexport { useControllableState } from \"./internal/use-controllable-state\";\nexport * from \"./item-form\";\nexport { Image } from \"./media/image\";\nexport type { ImageProps } from \"./media/image\";\nexport { Money } from \"./money\";\nexport { Otp } from \"./otp/input\";\nexport { Overlay, useOverlay } from \"./overlay/react\";\nexport { PaymentMethods } from \"./payment/methods\";\nexport type { PaymentMethod } from \"./payment/methods\";\nexport { createSearchIndex, Search } from \"./search\";\nexport type { SearchResult, SearchSource, SearchState } from \"./search\";\nexport {\n formatOpeningHours,\n formatWallClock,\n getOpenStatus,\n parseWallClock,\n} from \"./status/hours\";\nexport { OpenStatus } from \"./status/open-status\";\n"],
|
|
5
|
+
"mappings": ";;;AACA,SAAS,uBAAuB;AAIhC,SAAS,SAAS,iCAAiC;AAEnD,SAAS,WAAW,mBAAmB;AAOvC,SAAS,MAAM,cAAc;AAE7B,SAAS,UAAU,wBAAwB;AAO3C,SAAS,eAAe;AAKxB,SAAS,cAAc;AACvB,SAAS,UAAU,qBAAqB;AAExC,SAAS,kBAAkB;AAE3B,SAAS,4BAA4B;AACrC,cAAc;AACd,SAAS,aAAa;AAEtB,SAAS,aAAa;AACtB,SAAS,WAAW;AACpB,SAAS,SAAS,kBAAkB;AACpC,SAAS,sBAAsB;AAE/B,SAAS,mBAAmB,cAAc;AAE1C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AA9CpB,IAAM,sBAA8B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/item-form/root.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\nimport {\n previewItem,\n ZappieCartError,\n type CartIssue,\n type CartLine,\n type CartLineInput,\n type Item,\n type Money,\n type OptionGroup,\n} from \"@zappie/sdk\";\nimport { useCartCatalogue, useZappie } from \"@zappie/sdk/react\";\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { part, type PartProps } from \"../internal/slot\";\nimport { useControllableState } from \"../internal/use-controllable-state\";\n/**\n * An SDK option group with the effective rules exposed by the item preview.\n */\nexport type ActiveGroup = OptionGroup;\n/**\n * The current item selection, preview prices, validation issues and actions supplied to item form parts.\n */\nexport type ItemFormContext = {\n item: Item;\n groups: ActiveGroup[];\n quantity: number;\n setQuantity: (quantity: number) => void;\n unitPrice: Money;\n total: Money;\n issues: CartIssue[];\n canAdd: boolean;\n add: () => CartLine | null;\n status: \"incomplete\" | \"valid\" | \"added\";\n value: CartLineInput;\n setValue: (value: CartLineInput) => void;\n};\nexport const ItemFormStateContext = createContext<ItemFormContext | null>(null);\nexport function useItemForm() {\n const value = useContext(ItemFormStateContext);\n if (!value) {\n throw new Error(\"ItemForm parts require ItemForm.Root\");\n }\n return value;\n}\n/**\n * An option group that failed validation when the shopper tried to add, with its element and issues.\n */\nexport type ItemFormInvalidGroup = {\n groupId: string;\n /** The item's option group, or undefined for a group the template rendered from its own model. */\n group: ActiveGroup | undefined;\n /** The group's rendered element, or undefined when the group is not on screen. */\n element: HTMLElement | undefined;\n issues: CartIssue[];\n};\nexport const GroupRegistry = createContext<Map<string, HTMLElement> | null>(\n null,\n);\n/**\n * Item form configuration with controlled or default selections and cart commit callbacks.\n * Supply onCommit to update an existing line instead of adding a new one.\n */\nexport type ItemFormProps = PartProps<ItemFormContext> & {\n item: Item;\n value?: CartLineInput;\n defaultValue?: Partial<CartLineInput>;\n onValueChange?: (value: CartLineInput) => void;\n defaultQuantity?: number;\n onAdded?: (line: CartLine) => void;\n /** Update an existing line through the same validation and focus workflow. */\n onCommit?: (input: CartLineInput) => CartLine;\n /**\n * Called when an add fails validation, after the first invalid group gets\n * focus without scrolling. Groups are in display order. Scroll to\n * `groups[0].element` yourself, for example smoothly. The list is empty\n * when no issue belongs to a group.\n */\n onInvalid?: (groups: ItemFormInvalidGroup[]) => void;\n};\nexport function Root({\n item,\n value,\n defaultValue,\n onValueChange,\n defaultQuantity,\n onAdded,\n onCommit,\n onInvalid,\n ...props\n}: ItemFormProps) {\n const client = useZappie();\n const menu = useCartCatalogue();\n const [input, setInput] = useControllableState({\n value,\n defaultValue: {\n ...defaultValue,\n itemId: item.id,\n quantity: defaultQuantity ?? defaultValue?.quantity ?? item.quantity.min,\n },\n onChange: onValueChange,\n });\n const registry = useRef(new Map<string, HTMLElement>());\n const [added, setAdded] = useState(false);\n // Errors show only after the shopper tries to add (PRD 05 5.3), then track live validation.\n const [attempted, setAttempted] = useState(false);\n const [failure, setFailure] = useState<CartIssue[] | null>(null);\n const normalized = useMemo(\n () => ({ ...input, itemId: item.id }),\n [input, item.id],\n );\n const preview = useMemo(\n () => previewItem(item, normalized),\n [item, normalized],\n );\n const validation = useMemo(\n () => client.cart.validate(normalized),\n [client, normalized, menu],\n );\n const issues =\n failure ?? (attempted && !validation.ok ? validation.issues : []);\n const setValue = useCallback(\n (next: CartLineInput) => {\n setAdded(false);\n setFailure(null);\n setInput({ ...next, itemId: item.id });\n },\n [setInput, item.id],\n );\n const setQuantity = useCallback(\n (quantity: number) => setValue({ ...normalized, quantity }),\n [setValue, normalized],\n );\n const focusIssue = useCallback(\n (errors: CartIssue[]) => {\n const issuesOf = (groupId: string) =>\n errors.filter((issue) => issue.groupId === groupId);\n const group = (groupId: string) =>\n preview.groups.find((candidate) => candidate.id === groupId);\n // Rendered groups first, in the order they mounted, then any invalid group not on screen.\n const rendered = [...registry.current]\n .filter(([groupId]) => issuesOf(groupId).length > 0)\n .map(([groupId, element]) => ({\n groupId,\n group: group(groupId),\n element: element as HTMLElement | undefined,\n issues: issuesOf(groupId),\n }));\n const offscreen = preview.groups\n .filter(\n (candidate) =>\n !registry.current.has(candidate.id) &&\n issuesOf(candidate.id).length > 0,\n )\n .map((candidate) => ({\n groupId: candidate.id,\n group: candidate,\n element: undefined,\n issues: issuesOf(candidate.id),\n }));\n const invalid = [...rendered, ...offscreen];\n // Focus moves for keyboard and screen reader users; the template decides how to scroll.\n invalid[0]?.element?.focus({ preventScroll: true });\n onInvalid?.(invalid);\n },\n [preview.groups, onInvalid],\n );\n const add = useCallback(() => {\n const result = client.cart.validate(normalized);\n if (!result.ok) {\n setAttempted(true);\n setFailure(result.issues);\n focusIssue(result.issues);\n return null;\n }\n try {\n const line = onCommit\n ? onCommit(normalized)\n : client.cart.add(item, normalized);\n setAttempted(false);\n setAdded(true);\n onAdded?.(line);\n return line;\n } catch (error) {\n if (error instanceof ZappieCartError) {\n setAttempted(true);\n setFailure([...error.issues]);\n focusIssue([...error.issues]);\n return null;\n }\n throw error;\n }\n }, [client, normalized, item, onAdded, onCommit, focusIssue]);\n const context: ItemFormContext = useMemo(\n () => ({\n item,\n ...preview,\n quantity: normalized.quantity ?? item.quantity.min,\n setQuantity,\n issues,\n canAdd: validation.ok && !failure,\n add,\n status: added ? \"added\" : validation.ok ? \"valid\" : \"incomplete\",\n value: normalized,\n setValue,\n }),\n [\n item,\n preview,\n normalized,\n setQuantity,\n issues,\n validation.ok,\n failure,\n add,\n added,\n setValue,\n ],\n );\n return (\n <ItemFormStateContext.Provider value={context}>\n <GroupRegistry.Provider value={registry.current}>\n {part(\"div\", props, context, {\n \"data-zappie-item\": item.id,\n \"data-state\": issues.length ? \"invalid\" : \"valid\",\n \"data-sold-out\": item.isAvailable ? undefined : \"\",\n })}\n </GroupRegistry.Provider>\n </ItemFormStateContext.Provider>\n );\n}\n"],
|
|
4
|
+
"sourcesContent": ["\"use client\";\nimport {\n previewItem,\n ZappieCartError,\n type CartIssue,\n type CartLine,\n type CartLineInput,\n type Item,\n type Money,\n type OptionGroup,\n} from \"@zappie/sdk\";\nimport { useCartCatalogue, useZappie } from \"@zappie/sdk/react\";\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { part, type PartProps } from \"../internal/slot\";\nimport { useControllableState } from \"../internal/use-controllable-state\";\n/**\n * An SDK option group with the effective rules exposed by the item preview.\n */\nexport type ActiveGroup = OptionGroup;\n/**\n * The current item selection, preview prices, validation issues and actions supplied to item form parts.\n */\nexport type ItemFormContext = {\n item: Item;\n groups: ActiveGroup[];\n quantity: number;\n setQuantity: (quantity: number) => void;\n unitPrice: Money;\n total: Money;\n issues: CartIssue[];\n canAdd: boolean;\n add: () => CartLine | null;\n status: \"incomplete\" | \"valid\" | \"added\";\n value: CartLineInput;\n setValue: (value: CartLineInput) => void;\n};\nexport const ItemFormStateContext = createContext<ItemFormContext | null>(null);\nexport function useItemForm() {\n const value = useContext(ItemFormStateContext);\n if (!value) {\n throw new Error(\"ItemForm parts require ItemForm.Root\");\n }\n return value;\n}\n/**\n * An option group that failed validation when the shopper tried to add, with its element and issues.\n */\nexport type ItemFormInvalidGroup = {\n groupId: string;\n /** The item's option group, or undefined for a group the template rendered from its own model. */\n group: ActiveGroup | undefined;\n /** The group's rendered element, or undefined when the group is not on screen. */\n element: HTMLElement | undefined;\n issues: CartIssue[];\n};\nexport const GroupRegistry = createContext<Map<string, HTMLElement> | null>(\n null,\n);\n/**\n * Item form configuration with controlled or default selections and cart commit callbacks.\n * Supply onCommit to update an existing line instead of adding a new one.\n */\nexport type ItemFormProps = Omit<PartProps<ItemFormContext>, \"onInvalid\"> & {\n item: Item;\n value?: CartLineInput;\n defaultValue?: Partial<CartLineInput>;\n onValueChange?: (value: CartLineInput) => void;\n defaultQuantity?: number;\n onAdded?: (line: CartLine) => void;\n /** Update an existing line through the same validation and focus workflow. */\n onCommit?: (input: CartLineInput) => CartLine;\n /**\n * Called when an add fails validation, after the first invalid group gets\n * focus without scrolling. Groups are in display order. Scroll to\n * `groups[0].element` yourself, for example smoothly. The list is empty\n * when no issue belongs to a group.\n */\n onInvalid?: (groups: ItemFormInvalidGroup[]) => void;\n};\nexport function Root({\n item,\n value,\n defaultValue,\n onValueChange,\n defaultQuantity,\n onAdded,\n onCommit,\n onInvalid,\n ...props\n}: ItemFormProps) {\n const client = useZappie();\n const menu = useCartCatalogue();\n const [input, setInput] = useControllableState({\n value,\n defaultValue: {\n ...defaultValue,\n itemId: item.id,\n quantity: defaultQuantity ?? defaultValue?.quantity ?? item.quantity.min,\n },\n onChange: onValueChange,\n });\n const registry = useRef(new Map<string, HTMLElement>());\n const [added, setAdded] = useState(false);\n // Errors show only after the shopper tries to add (PRD 05 5.3), then track live validation.\n const [attempted, setAttempted] = useState(false);\n const [failure, setFailure] = useState<CartIssue[] | null>(null);\n const normalized = useMemo(\n () => ({ ...input, itemId: item.id }),\n [input, item.id],\n );\n const preview = useMemo(\n () => previewItem(item, normalized),\n [item, normalized],\n );\n const validation = useMemo(\n () => client.cart.validate(normalized),\n [client, normalized, menu],\n );\n const issues =\n failure ?? (attempted && !validation.ok ? validation.issues : []);\n const setValue = useCallback(\n (next: CartLineInput) => {\n setAdded(false);\n setFailure(null);\n setInput({ ...next, itemId: item.id });\n },\n [setInput, item.id],\n );\n const setQuantity = useCallback(\n (quantity: number) => setValue({ ...normalized, quantity }),\n [setValue, normalized],\n );\n const focusIssue = useCallback(\n (errors: CartIssue[]) => {\n const issuesOf = (groupId: string) =>\n errors.filter((issue) => issue.groupId === groupId);\n const group = (groupId: string) =>\n preview.groups.find((candidate) => candidate.id === groupId);\n // Rendered groups first, in the order they mounted, then any invalid group not on screen.\n const rendered = [...registry.current]\n .filter(([groupId]) => issuesOf(groupId).length > 0)\n .map(([groupId, element]) => ({\n groupId,\n group: group(groupId),\n element: element as HTMLElement | undefined,\n issues: issuesOf(groupId),\n }));\n const offscreen = preview.groups\n .filter(\n (candidate) =>\n !registry.current.has(candidate.id) &&\n issuesOf(candidate.id).length > 0,\n )\n .map((candidate) => ({\n groupId: candidate.id,\n group: candidate,\n element: undefined,\n issues: issuesOf(candidate.id),\n }));\n const invalid = [...rendered, ...offscreen];\n // Focus moves for keyboard and screen reader users; the template decides how to scroll.\n invalid[0]?.element?.focus({ preventScroll: true });\n onInvalid?.(invalid);\n },\n [preview.groups, onInvalid],\n );\n const add = useCallback(() => {\n const result = client.cart.validate(normalized);\n if (!result.ok) {\n setAttempted(true);\n setFailure(result.issues);\n focusIssue(result.issues);\n return null;\n }\n try {\n const line = onCommit\n ? onCommit(normalized)\n : client.cart.add(item, normalized);\n setAttempted(false);\n setAdded(true);\n onAdded?.(line);\n return line;\n } catch (error) {\n if (error instanceof ZappieCartError) {\n setAttempted(true);\n setFailure([...error.issues]);\n focusIssue([...error.issues]);\n return null;\n }\n throw error;\n }\n }, [client, normalized, item, onAdded, onCommit, focusIssue]);\n const context: ItemFormContext = useMemo(\n () => ({\n item,\n ...preview,\n quantity: normalized.quantity ?? item.quantity.min,\n setQuantity,\n issues,\n canAdd: validation.ok && !failure,\n add,\n status: added ? \"added\" : validation.ok ? \"valid\" : \"incomplete\",\n value: normalized,\n setValue,\n }),\n [\n item,\n preview,\n normalized,\n setQuantity,\n issues,\n validation.ok,\n failure,\n add,\n added,\n setValue,\n ],\n );\n return (\n <ItemFormStateContext.Provider value={context}>\n <GroupRegistry.Provider value={registry.current}>\n {part(\"div\", props, context, {\n \"data-zappie-item\": item.id,\n \"data-state\": issues.length ? \"invalid\" : \"valid\",\n \"data-sold-out\": item.isAvailable ? undefined : \"\",\n })}\n </GroupRegistry.Provider>\n </ItemFormStateContext.Provider>\n );\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/overlay/engine.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import 'react';
|
|
2
|
-
import 'react/jsx-runtime';
|
|
3
2
|
import '@base-ui/react/dialog';
|
|
4
|
-
export { E as EngineClose, b as EngineTitle, a as Presentation, c as Surface, d as SurfaceProps, S as SwipeDirection, u as useEngineParts } from '../engine-
|
|
3
|
+
export { E as EngineClose, b as EngineTitle, a as Presentation, c as Surface, d as SurfaceProps, S as SwipeDirection, u as useEngineParts } from '../engine-Bfbv9M0D.js';
|