@zappie/foundations 0.1.4 → 0.1.5
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 +14 -0
- package/README.md +68 -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 +143 -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/index.d.ts +56 -10
- 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/react.js +224 -13
- package/dist/overlay/react.js.map +2 -2
- package/dist/version.js +1 -1
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -68,19 +68,56 @@ type Coordinates = {
|
|
|
68
68
|
lng: number;
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
-
/** What a template gets to render when
|
|
72
|
-
type
|
|
71
|
+
/** What a template gets to render when a map fails to load. */
|
|
72
|
+
type MapLoadErrorState = {
|
|
73
73
|
/** Why the map failed to load. */
|
|
74
74
|
error: unknown;
|
|
75
75
|
/** Load the map again. */
|
|
76
76
|
retry: () => void;
|
|
77
77
|
};
|
|
78
|
+
|
|
79
|
+
/** The branch, the shopper's address and an optional route for DeliveryMap. */
|
|
80
|
+
type DeliveryMapProps = HTMLAttributes<HTMLDivElement> & {
|
|
81
|
+
/** The branch the order leaves from. Only its coordinates are read. */
|
|
82
|
+
branch: Pick<Branch$1, "coordinates">;
|
|
83
|
+
/** Coordinates of the delivery address. */
|
|
84
|
+
shopper?: Coordinates | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* The path to draw, such as a decoded driving route, from the branch to the
|
|
87
|
+
* shopper. Without it the map draws a straight line.
|
|
88
|
+
*/
|
|
89
|
+
route?: Coordinates[];
|
|
90
|
+
browserKey?: string;
|
|
91
|
+
/** Image shown inside the branch marker, such as the business logo. */
|
|
92
|
+
pinImageUrl?: string;
|
|
93
|
+
/** Called each time the map fails to load. */
|
|
94
|
+
onLoadError?: (error: unknown) => void;
|
|
95
|
+
/**
|
|
96
|
+
* Render your own message when the map fails to load, such as a retry
|
|
97
|
+
* button that calls `retry`. Without it the map shows "Map location unavailable".
|
|
98
|
+
*/
|
|
99
|
+
renderLoadError?: (state: MapLoadErrorState) => ReactNode;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* A static map of a delivery: the branch marker, the shopper's house marker
|
|
103
|
+
* and the line between them, fitted to both. It has no controls and ignores
|
|
104
|
+
* gestures. Maps loads after mount when you supply a browser key. Set the
|
|
105
|
+
* container height with CSS. Colour the house marker with --map-accent and
|
|
106
|
+
* the line with --delivery-map-route.
|
|
107
|
+
*/
|
|
108
|
+
declare function DeliveryMap({ branch, shopper, route, browserKey, pinImageUrl, onLoadError, renderLoadError, ...props }: DeliveryMapProps): react_jsx_runtime.JSX.Element | null;
|
|
109
|
+
|
|
110
|
+
/** What a template gets to render when the map fails to load. */
|
|
111
|
+
type BranchMapLoadErrorState = MapLoadErrorState;
|
|
78
112
|
/** Branches, selection and optional shopper coordinates for BranchMap. */
|
|
79
113
|
type BranchMapProps = Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> & {
|
|
80
114
|
branches: Branch$1[];
|
|
81
115
|
selectedBranchId?: string;
|
|
82
116
|
onSelect: (branch: Branch$1) => void;
|
|
83
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* The shopper's coordinates. The map shows them with a house marker and
|
|
119
|
+
* draws a line to the selected branch, or to the nearest one.
|
|
120
|
+
*/
|
|
84
121
|
shopperLocation?: Coordinates;
|
|
85
122
|
browserKey?: string;
|
|
86
123
|
/**
|
|
@@ -88,12 +125,18 @@ type BranchMapProps = Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> & {
|
|
|
88
125
|
* is shown and 14 otherwise.
|
|
89
126
|
*/
|
|
90
127
|
maxZoom?: number;
|
|
91
|
-
/** Image shown inside each branch pin. */
|
|
128
|
+
/** Image shown inside each branch pin, such as the business logo. */
|
|
92
129
|
pinImageUrl?: string;
|
|
93
130
|
/** Show the map and satellite switch. Defaults to false. */
|
|
94
131
|
mapTypeControl?: boolean;
|
|
95
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Show the branch name above the selected pin, or above every pin. Off by
|
|
134
|
+
* default. With a shopper location and no selection, "selected" labels the
|
|
135
|
+
* nearest branch.
|
|
136
|
+
*/
|
|
96
137
|
pinLabel?: "selected" | "all";
|
|
138
|
+
/** Frame a small map, with tighter padding and a wider single-branch view. */
|
|
139
|
+
compact?: boolean;
|
|
97
140
|
/** Called each time the map fails to load. */
|
|
98
141
|
onLoadError?: (error: unknown) => void;
|
|
99
142
|
/**
|
|
@@ -104,10 +147,13 @@ type BranchMapProps = Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> & {
|
|
|
104
147
|
};
|
|
105
148
|
/**
|
|
106
149
|
* Show branch locations. Maps loads after mount when you supply a browser key.
|
|
107
|
-
*
|
|
108
|
-
*
|
|
150
|
+
* Nearby branches group into numbered clusters. Set the container height with
|
|
151
|
+
* CSS. Colour clusters, the shopper marker, labels and the route with
|
|
152
|
+
* --map-accent and --map-accent-foreground. Style dot pins with
|
|
153
|
+
* --branch-map-pin, --branch-map-pin-selected, --branch-map-pin-border and
|
|
154
|
+
* --branch-map-pin-size.
|
|
109
155
|
*/
|
|
110
|
-
declare function BranchMap({ branches, selectedBranchId, onSelect, shopperLocation, browserKey, maxZoom, pinImageUrl, mapTypeControl, pinLabel, onLoadError, renderLoadError, ...props }: BranchMapProps): react_jsx_runtime.JSX.Element | null;
|
|
156
|
+
declare function BranchMap({ branches, selectedBranchId, onSelect, shopperLocation, browserKey, maxZoom, pinImageUrl, mapTypeControl, pinLabel, compact, onLoadError, renderLoadError, ...props }: BranchMapProps): react_jsx_runtime.JSX.Element | null;
|
|
111
157
|
|
|
112
158
|
/**
|
|
113
159
|
* The current quantity, bounds, step, disabled state and value setter exposed by a quantity control.
|
|
@@ -538,7 +584,7 @@ type ItemFormInvalidGroup = {
|
|
|
538
584
|
* Item form configuration with controlled or default selections and cart commit callbacks.
|
|
539
585
|
* Supply onCommit to update an existing line instead of adding a new one.
|
|
540
586
|
*/
|
|
541
|
-
type ItemFormProps = PartProps<ItemFormContext> & {
|
|
587
|
+
type ItemFormProps = Omit<PartProps<ItemFormContext>, "onInvalid"> & {
|
|
542
588
|
item: Item;
|
|
543
589
|
value?: CartLineInput;
|
|
544
590
|
defaultValue?: Partial<CartLineInput>;
|
|
@@ -950,4 +996,4 @@ declare const OpenStatus: react.ForwardRefExoticComponent<Omit<react.AllHTMLAttr
|
|
|
950
996
|
/** The installed @zappie/foundations version. */
|
|
951
997
|
declare const FOUNDATIONS_VERSION: string;
|
|
952
998
|
|
|
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 };
|
|
999
|
+
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/react.js
CHANGED
|
@@ -8,11 +8,13 @@ import {
|
|
|
8
8
|
useContext,
|
|
9
9
|
useEffect,
|
|
10
10
|
useId,
|
|
11
|
+
useLayoutEffect,
|
|
11
12
|
useMemo,
|
|
12
13
|
useRef,
|
|
13
14
|
useState,
|
|
14
15
|
useSyncExternalStore
|
|
15
16
|
} from "react";
|
|
17
|
+
import { createPortal, flushSync } from "react-dom";
|
|
16
18
|
import { composeRefs, part, partId } from "../internal/slot.js";
|
|
17
19
|
import {
|
|
18
20
|
closeOverlay,
|
|
@@ -20,7 +22,7 @@ import {
|
|
|
20
22
|
openOverlay,
|
|
21
23
|
subscribeLocation
|
|
22
24
|
} from "./history.js";
|
|
23
|
-
import { jsx } from "react/jsx-runtime";
|
|
25
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
24
26
|
var engineModule;
|
|
25
27
|
var enginePromise;
|
|
26
28
|
function loadOverlayEngine() {
|
|
@@ -98,7 +100,6 @@ function usePresentation(value) {
|
|
|
98
100
|
);
|
|
99
101
|
}
|
|
100
102
|
var Context = createContext(null);
|
|
101
|
-
var EngineContext = createContext(null);
|
|
102
103
|
function useContextValue() {
|
|
103
104
|
const value = useContext(Context);
|
|
104
105
|
if (!value) {
|
|
@@ -184,12 +185,175 @@ var Trigger = forwardRef(
|
|
|
184
185
|
);
|
|
185
186
|
}
|
|
186
187
|
);
|
|
188
|
+
var engineKind = (presentation) => presentation === "dialog" ? "dialog" : "drawer";
|
|
189
|
+
var warmed = /* @__PURE__ */ new Set();
|
|
190
|
+
var ContentContext = createContext(null);
|
|
191
|
+
var HELD_EVENTS = [
|
|
192
|
+
"pointerdown",
|
|
193
|
+
"mousedown",
|
|
194
|
+
"touchstart",
|
|
195
|
+
"click",
|
|
196
|
+
"keydown",
|
|
197
|
+
"wheel",
|
|
198
|
+
"touchmove"
|
|
199
|
+
];
|
|
200
|
+
var animationsOf = (node) => typeof node?.getAnimations === "function" ? node.getAnimations() : [];
|
|
201
|
+
function startTimes(node) {
|
|
202
|
+
const times = /* @__PURE__ */ new Map();
|
|
203
|
+
for (const animation of animationsOf(node)) {
|
|
204
|
+
if ("animationName" in animation) {
|
|
205
|
+
times.set(animation.animationName, animation.startTime);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return times;
|
|
209
|
+
}
|
|
210
|
+
function resume(node, times) {
|
|
211
|
+
for (const animation of animationsOf(node)) {
|
|
212
|
+
const start = "animationName" in animation ? times.get(animation.animationName) : void 0;
|
|
213
|
+
if (start !== void 0 && start !== null) {
|
|
214
|
+
animation.startTime = start;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function Frame({
|
|
219
|
+
ctx,
|
|
220
|
+
contentProps,
|
|
221
|
+
contentRef,
|
|
222
|
+
container,
|
|
223
|
+
onEscape,
|
|
224
|
+
onInteractOutside,
|
|
225
|
+
onAttach
|
|
226
|
+
}) {
|
|
227
|
+
const backdrop = useRef(null);
|
|
228
|
+
const popup = useRef(null);
|
|
229
|
+
const latest = useRef({ onEscape, onInteractOutside, onAttach, ctx });
|
|
230
|
+
latest.current = { onEscape, onInteractOutside, onAttach, ctx };
|
|
231
|
+
useLayoutEffect(() => {
|
|
232
|
+
const dismiss = (callback, event) => {
|
|
233
|
+
callback?.(event);
|
|
234
|
+
if (!(callback && event.defaultPrevented)) {
|
|
235
|
+
latest.current.ctx.close();
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
const hold = (event) => {
|
|
239
|
+
const inside = event.target instanceof Node && popup.current?.contains(event.target);
|
|
240
|
+
if (event.type === "keydown" && event.key === "Escape") {
|
|
241
|
+
dismiss(latest.current.onEscape, event);
|
|
242
|
+
} else if (event.type === "pointerdown" && !inside) {
|
|
243
|
+
dismiss(latest.current.onInteractOutside, event);
|
|
244
|
+
}
|
|
245
|
+
if (event.cancelable) {
|
|
246
|
+
event.preventDefault();
|
|
247
|
+
}
|
|
248
|
+
event.stopImmediatePropagation();
|
|
249
|
+
};
|
|
250
|
+
for (const type of HELD_EVENTS) {
|
|
251
|
+
window.addEventListener(type, hold, { capture: true, passive: false });
|
|
252
|
+
}
|
|
253
|
+
let live = true;
|
|
254
|
+
let frame = requestAnimationFrame(() => {
|
|
255
|
+
frame = requestAnimationFrame(() => {
|
|
256
|
+
if (live) {
|
|
257
|
+
latest.current.onAttach([
|
|
258
|
+
startTimes(backdrop.current),
|
|
259
|
+
startTimes(popup.current)
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
return () => {
|
|
265
|
+
live = false;
|
|
266
|
+
cancelAnimationFrame(frame);
|
|
267
|
+
for (const type of HELD_EVENTS) {
|
|
268
|
+
window.removeEventListener(type, hold, { capture: true });
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}, []);
|
|
272
|
+
const { presentation, swipeDirection } = ctx;
|
|
273
|
+
const drawer = presentation !== "dialog";
|
|
274
|
+
const { id: _ownId, ...rest } = contentProps;
|
|
275
|
+
const id = partId(contentProps);
|
|
276
|
+
const panel = part(
|
|
277
|
+
"div",
|
|
278
|
+
rest,
|
|
279
|
+
ctx,
|
|
280
|
+
{
|
|
281
|
+
"data-open": "",
|
|
282
|
+
...drawer ? { "data-swipe-direction": swipeDirection } : {},
|
|
283
|
+
id: id ?? ctx.contentId,
|
|
284
|
+
role: "dialog",
|
|
285
|
+
tabIndex: -1,
|
|
286
|
+
style: drawer ? {
|
|
287
|
+
"--drawer-swipe-movement-x": "0px",
|
|
288
|
+
"--drawer-swipe-movement-y": "0px",
|
|
289
|
+
"--drawer-swipe-progress": 0,
|
|
290
|
+
"--nested-drawers": 0,
|
|
291
|
+
"--drawer-snap-point-offset": "0px",
|
|
292
|
+
"--drawer-swipe-strength": 1
|
|
293
|
+
} : { "--nested-dialogs": 0 },
|
|
294
|
+
"aria-modal": true,
|
|
295
|
+
"data-presentation": presentation,
|
|
296
|
+
"data-state": "open",
|
|
297
|
+
// The engine's panel replaces this one on the next frame and is the one announced.
|
|
298
|
+
"aria-hidden": true
|
|
299
|
+
},
|
|
300
|
+
composeRefs(contentRef, (node) => {
|
|
301
|
+
popup.current = node;
|
|
302
|
+
})
|
|
303
|
+
);
|
|
304
|
+
return createPortal(
|
|
305
|
+
/* @__PURE__ */ jsxs("div", { "data-zappie-overlay-frame": "", children: [
|
|
306
|
+
/* @__PURE__ */ jsx(
|
|
307
|
+
"div",
|
|
308
|
+
{
|
|
309
|
+
role: "presentation",
|
|
310
|
+
"aria-hidden": "true",
|
|
311
|
+
style: { position: "fixed", inset: 0, userSelect: "none" }
|
|
312
|
+
}
|
|
313
|
+
),
|
|
314
|
+
/* @__PURE__ */ jsx(
|
|
315
|
+
"div",
|
|
316
|
+
{
|
|
317
|
+
ref: backdrop,
|
|
318
|
+
"data-open": "",
|
|
319
|
+
role: "presentation",
|
|
320
|
+
style: {
|
|
321
|
+
userSelect: "none",
|
|
322
|
+
...drawer ? { "--drawer-swipe-progress": 0, "--drawer-swipe-strength": 1 } : {}
|
|
323
|
+
},
|
|
324
|
+
"data-zappie-backdrop": "",
|
|
325
|
+
"data-presentation": presentation,
|
|
326
|
+
"data-state": "open",
|
|
327
|
+
"aria-hidden": "true"
|
|
328
|
+
}
|
|
329
|
+
),
|
|
330
|
+
drawer ? /* @__PURE__ */ jsx(
|
|
331
|
+
"div",
|
|
332
|
+
{
|
|
333
|
+
"data-open": "",
|
|
334
|
+
role: "presentation",
|
|
335
|
+
style: { "--drawer-keyboard-inset": "0px" },
|
|
336
|
+
children: panel
|
|
337
|
+
}
|
|
338
|
+
) : panel
|
|
339
|
+
] }),
|
|
340
|
+
container ?? document.body
|
|
341
|
+
);
|
|
342
|
+
}
|
|
187
343
|
var Content = forwardRef(function Content2({ onEscape, onInteractOutside, forceMount, container, ...props }, ref) {
|
|
188
344
|
const ctx = useContextValue();
|
|
189
|
-
const
|
|
190
|
-
const [
|
|
191
|
-
|
|
192
|
-
|
|
345
|
+
const parent = useContext(ContentContext);
|
|
346
|
+
const [loaded, setEngine] = useState(engineModule);
|
|
347
|
+
const engine = loaded ?? engineModule;
|
|
348
|
+
const firstPhase = () => !forceMount && ctx.isOpen && !parent && !warmed.has(engineKind(ctx.presentation)) ? "frame" : "engine";
|
|
349
|
+
const [phase, setPhase] = useState(
|
|
350
|
+
() => engine && (ctx.isOpen || forceMount) ? firstPhase() : "closed"
|
|
351
|
+
);
|
|
352
|
+
if (phase === "closed" && engine && (ctx.isOpen || forceMount)) {
|
|
353
|
+
setPhase(firstPhase());
|
|
354
|
+
}
|
|
355
|
+
if (phase === "frame" && !ctx.isOpen) {
|
|
356
|
+
setPhase("closed");
|
|
193
357
|
}
|
|
194
358
|
const customId = partId(props);
|
|
195
359
|
const { setContentId } = ctx;
|
|
@@ -207,6 +371,7 @@ var Content = forwardRef(function Content2({ onEscape, onInteractOutside, forceM
|
|
|
207
371
|
(module) => {
|
|
208
372
|
if (live) {
|
|
209
373
|
setEngine(module);
|
|
374
|
+
setPhase((current) => current === "closed" ? "engine" : current);
|
|
210
375
|
}
|
|
211
376
|
},
|
|
212
377
|
() => {
|
|
@@ -216,10 +381,47 @@ var Content = forwardRef(function Content2({ onEscape, onInteractOutside, forceM
|
|
|
216
381
|
live = false;
|
|
217
382
|
};
|
|
218
383
|
}, [engine, ctx.isOpen, forceMount]);
|
|
219
|
-
|
|
384
|
+
const kind = engineKind(ctx.presentation);
|
|
385
|
+
useEffect(() => {
|
|
386
|
+
if (phase === "engine" && ctx.isOpen) {
|
|
387
|
+
warmed.add(kind);
|
|
388
|
+
}
|
|
389
|
+
}, [phase, ctx.isOpen, kind]);
|
|
390
|
+
const panel = useRef(null);
|
|
391
|
+
const engineRef = useMemo(
|
|
392
|
+
() => composeRefs(ref, (node) => {
|
|
393
|
+
panel.current = node;
|
|
394
|
+
}),
|
|
395
|
+
[ref]
|
|
396
|
+
);
|
|
397
|
+
const attach = useCallback(([backdrop, popup]) => {
|
|
398
|
+
flushSync(() => setPhase("engine"));
|
|
399
|
+
const node = panel.current;
|
|
400
|
+
resume(node, popup);
|
|
401
|
+
resume(
|
|
402
|
+
node?.closest("[data-base-ui-portal]")?.querySelector("[data-zappie-backdrop]"),
|
|
403
|
+
backdrop
|
|
404
|
+
);
|
|
405
|
+
}, []);
|
|
406
|
+
const attached = useMemo(() => ({ attached: phase === "engine" }), [phase]);
|
|
407
|
+
if (!engine || phase === "closed" || parent && !parent.attached) {
|
|
220
408
|
return null;
|
|
221
409
|
}
|
|
222
|
-
|
|
410
|
+
if (phase === "frame") {
|
|
411
|
+
return /* @__PURE__ */ jsx(ContentContext.Provider, { value: attached, children: /* @__PURE__ */ jsx(PartsContext.Provider, { value: frameParts, children: /* @__PURE__ */ jsx(
|
|
412
|
+
Frame,
|
|
413
|
+
{
|
|
414
|
+
ctx,
|
|
415
|
+
contentProps: props,
|
|
416
|
+
contentRef: ref,
|
|
417
|
+
container,
|
|
418
|
+
onEscape,
|
|
419
|
+
onInteractOutside,
|
|
420
|
+
onAttach: attach
|
|
421
|
+
}
|
|
422
|
+
) }) });
|
|
423
|
+
}
|
|
424
|
+
return /* @__PURE__ */ jsx(ContentContext.Provider, { value: attached, children: /* @__PURE__ */ jsx(PartsContext.Provider, { value: engine, children: /* @__PURE__ */ jsx(
|
|
223
425
|
engine.Surface,
|
|
224
426
|
{
|
|
225
427
|
open: ctx.isOpen,
|
|
@@ -235,18 +437,27 @@ var Content = forwardRef(function Content2({ onEscape, onInteractOutside, forceM
|
|
|
235
437
|
container,
|
|
236
438
|
state: ctx,
|
|
237
439
|
contentProps: props,
|
|
238
|
-
contentRef:
|
|
440
|
+
contentRef: engineRef
|
|
239
441
|
}
|
|
240
|
-
) });
|
|
442
|
+
) }) });
|
|
241
443
|
});
|
|
444
|
+
var PartsContext = createContext(null);
|
|
445
|
+
var frameParts = {
|
|
446
|
+
EngineTitle: forwardRef(function FrameTitle({ state, ...props }, ref) {
|
|
447
|
+
return part("h2", props, state, {}, ref);
|
|
448
|
+
}),
|
|
449
|
+
EngineClose: forwardRef(function FrameClose({ state, ...props }, ref) {
|
|
450
|
+
return part("button", props, state, { type: "button" }, ref);
|
|
451
|
+
})
|
|
452
|
+
};
|
|
242
453
|
function useEngine() {
|
|
243
|
-
const
|
|
244
|
-
if (!
|
|
454
|
+
const parts = useContext(PartsContext);
|
|
455
|
+
if (!parts) {
|
|
245
456
|
throw new Error(
|
|
246
457
|
"Overlay.Title and Overlay.Close must be inside Overlay.Content"
|
|
247
458
|
);
|
|
248
459
|
}
|
|
249
|
-
return
|
|
460
|
+
return parts;
|
|
250
461
|
}
|
|
251
462
|
var Title = forwardRef(
|
|
252
463
|
function Title2(props, ref) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/overlay/react.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\nimport {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport { composeRefs, part, partId, type PartProps } from \"../internal/slot\";\nimport type { Presentation, SwipeDirection } from \"./engine\";\nimport {\n closeOverlay,\n locationSnapshot,\n openOverlay,\n subscribeLocation,\n type CloseOptions,\n type OverlayQuery,\n} from \"./history\";\n\ntype EngineModule = typeof import(\"./engine\");\nlet engineModule: EngineModule | undefined;\nlet enginePromise: Promise<EngineModule> | undefined;\n/**\n * Load the overlay engine (Base UI dialog and drawer). It is a separate chunk, prefetched once\n * the page has loaded and the browser is idle, and on trigger hover/focus, so it is ready by\n * the first open.\n */\nexport function loadOverlayEngine(): Promise<EngineModule> {\n enginePromise ??= import(\"./engine\").then(\n (module) => (engineModule = module),\n (error: unknown) => {\n // A failed chunk load (e.g. offline) must not poison later opens.\n enginePromise = undefined;\n throw error;\n },\n );\n return enginePromise;\n}\n\n/**\n * Control an overlay whose open state is stored in a URL query parameter.\n *\n * @param param - The query parameter to read and update.\n * @param id - The value that identifies this overlay as open.\n * @param options - Fallback query values and query parameters to omit when closing.\n * @returns Reactive open state and history-backed open and close actions.\n */\nexport function useOverlay(param: string, id: string, options?: CloseOptions) {\n const search = useSyncExternalStore(\n subscribeLocation,\n locationSnapshot,\n () => \"\",\n );\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const open = useCallback(\n (query?: OverlayQuery) => openOverlay(param, id, query),\n [param, id],\n );\n const close = useCallback(\n () => closeOverlay(param, id, optionsRef.current),\n [param, id],\n );\n return useMemo(\n () => ({\n isOpen: new URLSearchParams(search).get(param) === id,\n open,\n close,\n }),\n [search, param, id, open, close],\n );\n}\n\n/** Tailwind's default min-width breakpoints. */\nconst BREAKPOINTS = { sm: 640, md: 768, lg: 1024, xl: 1280 } as const;\nexport type ResponsivePresentation =\n | Presentation\n | ({ base: Presentation } & Partial<\n Record<keyof typeof BREAKPOINTS, Presentation>\n >);\n/** Choose a presentation using media queries. The server renders `base`. */\nfunction usePresentation(value: ResponsivePresentation): Presentation {\n // Serialise so inline objects don't resubscribe on every render.\n const key = typeof value === \"string\" ? value : JSON.stringify(value);\n const resolve = useCallback((): Presentation => {\n const parsed = (\n key.startsWith(\"{\") ? JSON.parse(key) : key\n ) as ResponsivePresentation;\n if (typeof parsed === \"string\") {\n return parsed;\n }\n let current = parsed.base;\n for (const [name, width] of Object.entries(BREAKPOINTS)) {\n const at = parsed[name as keyof typeof BREAKPOINTS];\n if (at && window.matchMedia(`(min-width: ${width}px)`).matches) {\n current = at;\n }\n }\n return current;\n }, [key]);\n const subscribe = useCallback(\n (onChange: () => void) => {\n if (!key.startsWith(\"{\")) {\n return () => {};\n }\n const queries = Object.values(BREAKPOINTS).map((width) =>\n window.matchMedia(`(min-width: ${width}px)`),\n );\n queries.forEach((query) => query.addEventListener(\"change\", onChange));\n return () =>\n queries.forEach((query) =>\n query.removeEventListener(\"change\", onChange),\n );\n },\n [key],\n );\n return useSyncExternalStore(subscribe, resolve, () =>\n typeof value === \"string\" ? value : value.base,\n );\n}\n\ntype OverlayState = ReturnType<typeof useOverlay>;\ntype OverlayContext = OverlayState & {\n presentation: Presentation;\n swipeDirection: SwipeDirection;\n contentId: string;\n setContentId: (id: string) => void;\n trigger: { current: HTMLElement | null };\n};\nconst Context = createContext<OverlayContext | null>(null);\nconst EngineContext = createContext<EngineModule | null>(null);\nfunction useContextValue() {\n const value = useContext(Context);\n if (!value) {\n throw new Error(\"Overlay parts require Overlay.Root\");\n }\n return value;\n}\nconst prefetch = () => void loadOverlayEngine().catch(() => {});\n\n// The prefetch is scheduled when this module loads, not when an Overlay.Root mounts: a template\n// that loads its overlay with deferred() mounts the Root only on the tap, which would leave the\n// engine's download inside the open. Waiting for the load event keeps it out of the page load.\nfunction prefetchWhenIdle() {\n if (typeof window === \"undefined\") {\n return;\n }\n const schedule = () => {\n if (engineModule) {\n return;\n }\n if (typeof window.requestIdleCallback === \"function\") {\n window.requestIdleCallback(prefetch);\n } else {\n window.setTimeout(prefetch, 1500);\n }\n };\n if (document.readyState === \"complete\") {\n schedule();\n } else {\n window.addEventListener(\"load\", schedule, { once: true });\n }\n}\nprefetchWhenIdle();\n\n/**\n * The URL is the only open state. The engine (Base UI) supplies the modal mechanics: portal,\n * focus scope, scroll lock, outside and Escape dismissal, exit presence and, for drawers and\n * sheets, swipe-to-dismiss. Every open or close it requests goes through the history core.\n */\nfunction Root({\n param,\n id,\n presentation = \"dialog\",\n swipeDirection,\n fallback,\n omitParams,\n navigation,\n ...props\n}: PartProps<OverlayState> & {\n param: string;\n id: string;\n /** Your URL adapter for overlays whose open state lives in the path. */\n navigation?: OverlayState;\n /** A `dialog`, swipeable `drawer` or `sheet`. Use `{ base, sm, md, lg, xl }` for responsive layouts. */\n presentation?: ResponsivePresentation;\n /** Swipe direction to dismiss. Defaults to `down` for drawers and `right` for sheets. */\n swipeDirection?: SwipeDirection;\n} & CloseOptions) {\n const queryOverlay = useOverlay(param, id, { fallback, omitParams });\n const overlay = navigation ?? queryOverlay;\n const resolved = usePresentation(presentation);\n const generated = useId();\n const [contentId, setContentId] = useState(`zappie-overlay-${generated}`);\n const trigger = useRef<HTMLElement | null>(null);\n const context = useMemo(\n () => ({\n ...overlay,\n presentation: resolved,\n swipeDirection:\n swipeDirection ?? (resolved === \"sheet\" ? \"right\" : \"down\"),\n contentId,\n setContentId,\n trigger,\n }),\n [overlay, resolved, swipeDirection, contentId],\n );\n return (\n <Context.Provider value={context}>\n {part(\"div\", props, overlay, {\n \"data-state\": overlay.isOpen ? \"open\" : \"closed\",\n \"data-presentation\": resolved,\n })}\n </Context.Provider>\n );\n}\nconst Trigger = forwardRef<HTMLElement, PartProps<OverlayState>>(\n function Trigger(props, ref) {\n const ctx = useContextValue();\n return part(\n \"button\",\n props,\n ctx,\n {\n type: \"button\",\n \"aria-haspopup\": \"dialog\",\n \"aria-expanded\": ctx.isOpen,\n \"aria-controls\": ctx.contentId,\n \"data-state\": ctx.isOpen ? \"open\" : \"closed\",\n onPointerEnter: prefetch,\n onFocus: prefetch,\n onClick: () => ctx.open(),\n },\n composeRefs(ref, (node: HTMLElement | null) => {\n ctx.trigger.current = node;\n }),\n );\n },\n);\nexport type OverlayContentProps = PartProps<OverlayState> & {\n /** Runs on Escape. Call `event.preventDefault()` to keep the overlay open. */\n onEscape?: (event: KeyboardEvent) => void;\n /** Runs on an outside pointer press or focus. Call `event.preventDefault()` to keep the overlay open. */\n onInteractOutside?: (event: Event) => void;\n /** Keep the content mounted while closed, for exit animations driven by `data-state`. */\n forceMount?: true;\n /** Where to mount the content. Defaults to `document.body`. */\n container?: HTMLElement | null;\n};\nconst Content = forwardRef<HTMLElement, OverlayContentProps>(function Content(\n { onEscape, onInteractOutside, forceMount, container, ...props },\n ref,\n) {\n const ctx = useContextValue();\n const [engine, setEngine] = useState(engineModule);\n // Once opened, the surface stays mounted so the engine can play exit transitions.\n const [opened, setOpened] = useState(ctx.isOpen);\n if (ctx.isOpen && !opened) {\n setOpened(true);\n }\n const customId = partId(props as Parameters<typeof partId>[0]);\n const { setContentId } = ctx;\n useEffect(() => {\n if (customId) {\n setContentId(customId);\n }\n }, [customId, setContentId]);\n useEffect(() => {\n if (engine || !(ctx.isOpen || forceMount)) {\n return undefined;\n }\n let live = true;\n void loadOverlayEngine().then(\n (module) => {\n if (live) {\n setEngine(module);\n }\n },\n () => {},\n );\n return () => {\n live = false;\n };\n }, [engine, ctx.isOpen, forceMount]);\n if (!engine || (!opened && !forceMount)) {\n return null;\n }\n return (\n <EngineContext.Provider value={engine}>\n <engine.Surface\n open={ctx.isOpen}\n presentation={ctx.presentation}\n swipeDirection={ctx.swipeDirection}\n contentId={ctx.contentId}\n trigger={ctx.trigger}\n onOpen={() => ctx.open()}\n onClose={ctx.close}\n onEscape={onEscape}\n onInteractOutside={onInteractOutside}\n forceMount={forceMount}\n container={container}\n state={ctx}\n contentProps={props as PartProps<never>}\n contentRef={ref}\n />\n </EngineContext.Provider>\n );\n});\nfunction useEngine() {\n const engine = useContext(EngineContext);\n if (!engine) {\n throw new Error(\n \"Overlay.Title and Overlay.Close must be inside Overlay.Content\",\n );\n }\n return engine;\n}\nconst Title = forwardRef<HTMLElement, PartProps<OverlayState>>(\n function Title(props, ref) {\n const ctx = useContextValue();\n const { EngineTitle } = useEngine();\n return (\n <EngineTitle {...(props as PartProps<unknown>)} state={ctx} ref={ref} />\n );\n },\n);\nconst Close = forwardRef<HTMLElement, PartProps<OverlayState>>(\n function Close(props, ref) {\n const ctx = useContextValue();\n const { EngineClose } = useEngine();\n return (\n <EngineClose {...(props as PartProps<unknown>)} state={ctx} ref={ref} />\n );\n },\n);\n/**\n * Composable dialog, drawer and sheet parts with URL-backed open state and a lazy modal engine.\n * Use Root, Trigger, Content, Title and Close to connect history, focus handling and dismissal.\n */\nexport const Overlay = { Root, Trigger, Content, Title, Close };\n"],
|
|
5
|
-
"mappings": ";;;AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,
|
|
4
|
+
"sourcesContent": ["\"use client\";\nimport {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type CSSProperties,\n type Ref,\n} from \"react\";\nimport { createPortal, flushSync } from \"react-dom\";\nimport { composeRefs, part, partId, type PartProps } from \"../internal/slot\";\nimport type { Presentation, SwipeDirection } from \"./engine\";\nimport {\n closeOverlay,\n locationSnapshot,\n openOverlay,\n subscribeLocation,\n type CloseOptions,\n type OverlayQuery,\n} from \"./history\";\n\ntype EngineModule = typeof import(\"./engine\");\nlet engineModule: EngineModule | undefined;\nlet enginePromise: Promise<EngineModule> | undefined;\n/**\n * Load the overlay engine (Base UI dialog and drawer). It is a separate chunk, prefetched once\n * the page has loaded and the browser is idle, and on trigger hover/focus, so it is ready by\n * the first open.\n */\nexport function loadOverlayEngine(): Promise<EngineModule> {\n enginePromise ??= import(\"./engine\").then(\n (module) => (engineModule = module),\n (error: unknown) => {\n // A failed chunk load (e.g. offline) must not poison later opens.\n enginePromise = undefined;\n throw error;\n },\n );\n return enginePromise;\n}\n\n/**\n * Control an overlay whose open state is stored in a URL query parameter.\n *\n * @param param - The query parameter to read and update.\n * @param id - The value that identifies this overlay as open.\n * @param options - Fallback query values and query parameters to omit when closing.\n * @returns Reactive open state and history-backed open and close actions.\n */\nexport function useOverlay(param: string, id: string, options?: CloseOptions) {\n const search = useSyncExternalStore(\n subscribeLocation,\n locationSnapshot,\n () => \"\",\n );\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const open = useCallback(\n (query?: OverlayQuery) => openOverlay(param, id, query),\n [param, id],\n );\n const close = useCallback(\n () => closeOverlay(param, id, optionsRef.current),\n [param, id],\n );\n return useMemo(\n () => ({\n isOpen: new URLSearchParams(search).get(param) === id,\n open,\n close,\n }),\n [search, param, id, open, close],\n );\n}\n\n/** Tailwind's default min-width breakpoints. */\nconst BREAKPOINTS = { sm: 640, md: 768, lg: 1024, xl: 1280 } as const;\nexport type ResponsivePresentation =\n | Presentation\n | ({ base: Presentation } & Partial<\n Record<keyof typeof BREAKPOINTS, Presentation>\n >);\n/** Choose a presentation using media queries. The server renders `base`. */\nfunction usePresentation(value: ResponsivePresentation): Presentation {\n // Serialise so inline objects don't resubscribe on every render.\n const key = typeof value === \"string\" ? value : JSON.stringify(value);\n const resolve = useCallback((): Presentation => {\n const parsed = (\n key.startsWith(\"{\") ? JSON.parse(key) : key\n ) as ResponsivePresentation;\n if (typeof parsed === \"string\") {\n return parsed;\n }\n let current = parsed.base;\n for (const [name, width] of Object.entries(BREAKPOINTS)) {\n const at = parsed[name as keyof typeof BREAKPOINTS];\n if (at && window.matchMedia(`(min-width: ${width}px)`).matches) {\n current = at;\n }\n }\n return current;\n }, [key]);\n const subscribe = useCallback(\n (onChange: () => void) => {\n if (!key.startsWith(\"{\")) {\n return () => {};\n }\n const queries = Object.values(BREAKPOINTS).map((width) =>\n window.matchMedia(`(min-width: ${width}px)`),\n );\n queries.forEach((query) => query.addEventListener(\"change\", onChange));\n return () =>\n queries.forEach((query) =>\n query.removeEventListener(\"change\", onChange),\n );\n },\n [key],\n );\n return useSyncExternalStore(subscribe, resolve, () =>\n typeof value === \"string\" ? value : value.base,\n );\n}\n\ntype OverlayState = ReturnType<typeof useOverlay>;\ntype OverlayContext = OverlayState & {\n presentation: Presentation;\n swipeDirection: SwipeDirection;\n contentId: string;\n setContentId: (id: string) => void;\n trigger: { current: HTMLElement | null };\n};\nconst Context = createContext<OverlayContext | null>(null);\nfunction useContextValue() {\n const value = useContext(Context);\n if (!value) {\n throw new Error(\"Overlay parts require Overlay.Root\");\n }\n return value;\n}\nconst prefetch = () => void loadOverlayEngine().catch(() => {});\n\n// The prefetch is scheduled when this module loads, not when an Overlay.Root mounts: a template\n// that loads its overlay with deferred() mounts the Root only on the tap, which would leave the\n// engine's download inside the open. Waiting for the load event keeps it out of the page load.\nfunction prefetchWhenIdle() {\n if (typeof window === \"undefined\") {\n return;\n }\n const schedule = () => {\n if (engineModule) {\n return;\n }\n if (typeof window.requestIdleCallback === \"function\") {\n window.requestIdleCallback(prefetch);\n } else {\n window.setTimeout(prefetch, 1500);\n }\n };\n if (document.readyState === \"complete\") {\n schedule();\n } else {\n window.addEventListener(\"load\", schedule, { once: true });\n }\n}\nprefetchWhenIdle();\n\n/**\n * The URL is the only open state. The engine (Base UI) supplies the modal mechanics: portal,\n * focus scope, scroll lock, outside and Escape dismissal, exit presence and, for drawers and\n * sheets, swipe-to-dismiss. Every open or close it requests goes through the history core.\n */\nfunction Root({\n param,\n id,\n presentation = \"dialog\",\n swipeDirection,\n fallback,\n omitParams,\n navigation,\n ...props\n}: PartProps<OverlayState> & {\n param: string;\n id: string;\n /** Your URL adapter for overlays whose open state lives in the path. */\n navigation?: OverlayState;\n /** A `dialog`, swipeable `drawer` or `sheet`. Use `{ base, sm, md, lg, xl }` for responsive layouts. */\n presentation?: ResponsivePresentation;\n /** Swipe direction to dismiss. Defaults to `down` for drawers and `right` for sheets. */\n swipeDirection?: SwipeDirection;\n} & CloseOptions) {\n const queryOverlay = useOverlay(param, id, { fallback, omitParams });\n const overlay = navigation ?? queryOverlay;\n const resolved = usePresentation(presentation);\n const generated = useId();\n const [contentId, setContentId] = useState(`zappie-overlay-${generated}`);\n const trigger = useRef<HTMLElement | null>(null);\n const context = useMemo(\n () => ({\n ...overlay,\n presentation: resolved,\n swipeDirection:\n swipeDirection ?? (resolved === \"sheet\" ? \"right\" : \"down\"),\n contentId,\n setContentId,\n trigger,\n }),\n [overlay, resolved, swipeDirection, contentId],\n );\n return (\n <Context.Provider value={context}>\n {part(\"div\", props, overlay, {\n \"data-state\": overlay.isOpen ? \"open\" : \"closed\",\n \"data-presentation\": resolved,\n })}\n </Context.Provider>\n );\n}\nconst Trigger = forwardRef<HTMLElement, PartProps<OverlayState>>(\n function Trigger(props, ref) {\n const ctx = useContextValue();\n return part(\n \"button\",\n props,\n ctx,\n {\n type: \"button\",\n \"aria-haspopup\": \"dialog\",\n \"aria-expanded\": ctx.isOpen,\n \"aria-controls\": ctx.contentId,\n \"data-state\": ctx.isOpen ? \"open\" : \"closed\",\n onPointerEnter: prefetch,\n onFocus: prefetch,\n onClick: () => ctx.open(),\n },\n composeRefs(ref, (node: HTMLElement | null) => {\n ctx.trigger.current = node;\n }),\n );\n },\n);\nexport type OverlayContentProps = PartProps<OverlayState> & {\n /** Runs on Escape. Call `event.preventDefault()` to keep the overlay open. */\n onEscape?: (event: KeyboardEvent) => void;\n /** Runs on an outside pointer press or focus. Call `event.preventDefault()` to keep the overlay open. */\n onInteractOutside?: (event: Event) => void;\n /** Keep the content mounted while closed, for exit animations driven by `data-state`. */\n forceMount?: true;\n /** Where to mount the content. Defaults to `document.body`. */\n container?: HTMLElement | null;\n};\n\ntype EngineKind = \"dialog\" | \"drawer\";\nconst engineKind = (presentation: Presentation): EngineKind =>\n presentation === \"dialog\" ? \"dialog\" : \"drawer\";\n/** The engine paths (Base UI's dialog, or its drawer) that have opened on this page. */\nconst warmed = new Set<EngineKind>();\n/** Whether the engine owns the content around a nested Root yet. */\nconst ContentContext = createContext<{ attached: boolean } | null>(null);\n// Everything a shopper can do to the page while the frame waits for the engine.\nconst HELD_EVENTS = [\n \"pointerdown\",\n \"mousedown\",\n \"touchstart\",\n \"click\",\n \"keydown\",\n \"wheel\",\n \"touchmove\",\n] as const;\ntype StartTimes = Map<string, CSSNumberish | null>;\nconst animationsOf = (node: Element | null | undefined) =>\n typeof node?.getAnimations === \"function\" ? node.getAnimations() : [];\n/** The start time of each CSS animation running on a node, by animation name. */\nfunction startTimes(node: Element | null | undefined): StartTimes {\n const times: StartTimes = new Map();\n for (const animation of animationsOf(node)) {\n if (\"animationName\" in animation) {\n times.set((animation as CSSAnimation).animationName, animation.startTime);\n }\n }\n return times;\n}\n/** Carry the frame's running animations over to the engine's nodes, so they don't restart. */\nfunction resume(node: Element | null | undefined, times: StartTimes) {\n for (const animation of animationsOf(node)) {\n const start =\n \"animationName\" in animation\n ? times.get((animation as CSSAnimation).animationName)\n : undefined;\n if (start !== undefined && start !== null) {\n animation.startTime = start;\n }\n }\n}\n\ntype FrameProps = {\n ctx: OverlayContext;\n contentProps: PartProps<never>;\n contentRef: Ref<HTMLElement>;\n container: HTMLElement | null | undefined;\n onEscape: ((event: KeyboardEvent) => void) | undefined;\n onInteractOutside: ((event: Event) => void) | undefined;\n onAttach: (times: [StartTimes, StartTimes]) => void;\n};\n/**\n * The overlay's first frame on a page: its backdrop and its panel with the content, in the\n * engine's markup, painted without the engine. The first open on a page runs Base UI's and\n * React DOM's modal code for the first time, and V8 compiles it then, which takes several\n * frames on a slow phone. The frame paints on the tap and the engine replaces it at the start\n * of the next frame, so that work lands after the overlay is on screen.\n *\n * Until the engine is in, the frame is hidden from assistive technology and takes no input:\n * every key, press and scroll is held, so focus stays on the trigger and nothing on the page\n * can be reached. Escape and an outside press still close it, through the same callbacks.\n */\nfunction Frame({\n ctx,\n contentProps,\n contentRef,\n container,\n onEscape,\n onInteractOutside,\n onAttach,\n}: FrameProps) {\n const backdrop = useRef<HTMLDivElement>(null);\n const popup = useRef<HTMLElement | null>(null);\n const latest = useRef({ onEscape, onInteractOutside, onAttach, ctx });\n latest.current = { onEscape, onInteractOutside, onAttach, ctx };\n useLayoutEffect(() => {\n const dismiss = (\n callback: ((event: never) => void) | undefined,\n event: Event,\n ) => {\n callback?.(event as never);\n if (!(callback && event.defaultPrevented)) {\n latest.current.ctx.close();\n }\n };\n const hold = (event: Event) => {\n const inside =\n event.target instanceof Node && popup.current?.contains(event.target);\n if (\n event.type === \"keydown\" &&\n (event as KeyboardEvent).key === \"Escape\"\n ) {\n dismiss(latest.current.onEscape, event);\n } else if (event.type === \"pointerdown\" && !inside) {\n dismiss(latest.current.onInteractOutside, event);\n }\n if (event.cancelable) {\n event.preventDefault();\n }\n event.stopImmediatePropagation();\n };\n for (const type of HELD_EVENTS) {\n window.addEventListener(type, hold, { capture: true, passive: false });\n }\n // The first callback runs before this frame paints; the second starts the next frame.\n let live = true;\n let frame = requestAnimationFrame(() => {\n frame = requestAnimationFrame(() => {\n if (live) {\n latest.current.onAttach([\n startTimes(backdrop.current),\n startTimes(popup.current),\n ]);\n }\n });\n });\n return () => {\n live = false;\n cancelAnimationFrame(frame);\n for (const type of HELD_EVENTS) {\n window.removeEventListener(type, hold, { capture: true });\n }\n };\n }, []);\n const { presentation, swipeDirection } = ctx;\n const drawer = presentation !== \"dialog\";\n const { id: _ownId, ...rest } = contentProps as PartProps<never> & {\n id?: string;\n };\n const id = partId(contentProps as Parameters<typeof partId>[0]);\n const panel = part(\n \"div\",\n rest as PartProps<unknown>,\n ctx,\n {\n \"data-open\": \"\",\n ...(drawer ? { \"data-swipe-direction\": swipeDirection } : {}),\n id: id ?? ctx.contentId,\n role: \"dialog\",\n tabIndex: -1,\n style: drawer\n ? {\n \"--drawer-swipe-movement-x\": \"0px\",\n \"--drawer-swipe-movement-y\": \"0px\",\n \"--drawer-swipe-progress\": 0,\n \"--nested-drawers\": 0,\n \"--drawer-snap-point-offset\": \"0px\",\n \"--drawer-swipe-strength\": 1,\n }\n : { \"--nested-dialogs\": 0 },\n \"aria-modal\": true,\n \"data-presentation\": presentation,\n \"data-state\": \"open\",\n // The engine's panel replaces this one on the next frame and is the one announced.\n \"aria-hidden\": true,\n },\n composeRefs(contentRef, (node: HTMLElement | null) => {\n popup.current = node;\n }),\n );\n return createPortal(\n <div data-zappie-overlay-frame=\"\">\n <div\n role=\"presentation\"\n aria-hidden=\"true\"\n style={{ position: \"fixed\", inset: 0, userSelect: \"none\" }}\n />\n <div\n ref={backdrop}\n data-open=\"\"\n role=\"presentation\"\n style={\n {\n userSelect: \"none\",\n ...(drawer\n ? { \"--drawer-swipe-progress\": 0, \"--drawer-swipe-strength\": 1 }\n : {}),\n } as CSSProperties\n }\n data-zappie-backdrop=\"\"\n data-presentation={presentation}\n data-state=\"open\"\n aria-hidden=\"true\"\n />\n {drawer ? (\n <div\n data-open=\"\"\n role=\"presentation\"\n style={{ \"--drawer-keyboard-inset\": \"0px\" } as CSSProperties}\n >\n {panel}\n </div>\n ) : (\n panel\n )}\n </div>,\n container ?? document.body,\n );\n}\n\ntype Phase = \"closed\" | \"frame\" | \"engine\";\nconst Content = forwardRef<HTMLElement, OverlayContentProps>(function Content(\n { onEscape, onInteractOutside, forceMount, container, ...props },\n ref,\n) {\n const ctx = useContextValue();\n const parent = useContext(ContentContext);\n const [loaded, setEngine] = useState(engineModule);\n // Content that mounted before the idle prefetch finished still sees the engine once it is in.\n const engine = loaded ?? engineModule;\n // A page's first open of each engine path paints a frame first; see Frame. Nested overlays\n // open inside an engine that is already running, and a forced mount has no tap to answer.\n const firstPhase = (): Phase =>\n !forceMount &&\n ctx.isOpen &&\n !parent &&\n !warmed.has(engineKind(ctx.presentation))\n ? \"frame\"\n : \"engine\";\n // Once the engine is in, the surface stays mounted so it can play exit transitions.\n const [phase, setPhase] = useState<Phase>(() =>\n engine && (ctx.isOpen || forceMount) ? firstPhase() : \"closed\",\n );\n if (phase === \"closed\" && engine && (ctx.isOpen || forceMount)) {\n setPhase(firstPhase());\n }\n if (phase === \"frame\" && !ctx.isOpen) {\n setPhase(\"closed\");\n }\n const customId = partId(props as Parameters<typeof partId>[0]);\n const { setContentId } = ctx;\n useEffect(() => {\n if (customId) {\n setContentId(customId);\n }\n }, [customId, setContentId]);\n useEffect(() => {\n if (engine || !(ctx.isOpen || forceMount)) {\n return undefined;\n }\n let live = true;\n void loadOverlayEngine().then(\n (module) => {\n if (live) {\n // The engine arrived after the open, so it paints the overlay itself.\n setEngine(module);\n setPhase((current) => (current === \"closed\" ? \"engine\" : current));\n }\n },\n () => {},\n );\n return () => {\n live = false;\n };\n }, [engine, ctx.isOpen, forceMount]);\n const kind = engineKind(ctx.presentation);\n useEffect(() => {\n if (phase === \"engine\" && ctx.isOpen) {\n warmed.add(kind);\n }\n }, [phase, ctx.isOpen, kind]);\n const panel = useRef<HTMLElement | null>(null);\n const engineRef = useMemo(\n () =>\n composeRefs(ref, (node: HTMLElement | null) => {\n panel.current = node;\n }),\n [ref],\n );\n const attach = useCallback(([backdrop, popup]: [StartTimes, StartTimes]) => {\n flushSync(() => setPhase(\"engine\"));\n const node = panel.current;\n resume(node, popup);\n resume(\n node\n ?.closest(\"[data-base-ui-portal]\")\n ?.querySelector(\"[data-zappie-backdrop]\"),\n backdrop,\n );\n }, []);\n const attached = useMemo(() => ({ attached: phase === \"engine\" }), [phase]);\n if (!engine || phase === \"closed\" || (parent && !parent.attached)) {\n return null;\n }\n if (phase === \"frame\") {\n return (\n <ContentContext.Provider value={attached}>\n <PartsContext.Provider value={frameParts}>\n <Frame\n ctx={ctx}\n contentProps={props as PartProps<never>}\n contentRef={ref}\n container={container}\n onEscape={onEscape}\n onInteractOutside={onInteractOutside}\n onAttach={attach}\n />\n </PartsContext.Provider>\n </ContentContext.Provider>\n );\n }\n return (\n <ContentContext.Provider value={attached}>\n <PartsContext.Provider value={engine}>\n <engine.Surface\n open={ctx.isOpen}\n presentation={ctx.presentation}\n swipeDirection={ctx.swipeDirection}\n contentId={ctx.contentId}\n trigger={ctx.trigger}\n onOpen={() => ctx.open()}\n onClose={ctx.close}\n onEscape={onEscape}\n onInteractOutside={onInteractOutside}\n forceMount={forceMount}\n container={container}\n state={ctx}\n contentProps={props as PartProps<never>}\n contentRef={engineRef}\n />\n </PartsContext.Provider>\n </ContentContext.Provider>\n );\n});\n\ntype PartComponent = EngineModule[\"EngineTitle\"];\ntype Parts = { EngineTitle: PartComponent; EngineClose: PartComponent };\ntype FramePartProps = PartProps<unknown> & { state: unknown };\n/** Title and Close for the content: the engine's, or the frame's inert stand-ins. */\nconst PartsContext = createContext<Parts | null>(null);\nconst frameParts: Parts = {\n EngineTitle: forwardRef<HTMLElement, FramePartProps>(function FrameTitle(\n { state, ...props },\n ref,\n ) {\n return part(\"h2\", props, state, {}, ref);\n }),\n EngineClose: forwardRef<HTMLElement, FramePartProps>(function FrameClose(\n { state, ...props },\n ref,\n ) {\n return part(\"button\", props, state, { type: \"button\" }, ref);\n }),\n};\nfunction useEngine() {\n const parts = useContext(PartsContext);\n if (!parts) {\n throw new Error(\n \"Overlay.Title and Overlay.Close must be inside Overlay.Content\",\n );\n }\n return parts;\n}\nconst Title = forwardRef<HTMLElement, PartProps<OverlayState>>(\n function Title(props, ref) {\n const ctx = useContextValue();\n const { EngineTitle } = useEngine();\n return (\n <EngineTitle {...(props as PartProps<unknown>)} state={ctx} ref={ref} />\n );\n },\n);\nconst Close = forwardRef<HTMLElement, PartProps<OverlayState>>(\n function Close(props, ref) {\n const ctx = useContextValue();\n const { EngineClose } = useEngine();\n return (\n <EngineClose {...(props as PartProps<unknown>)} state={ctx} ref={ref} />\n );\n },\n);\n/**\n * Composable dialog, drawer and sheet parts with URL-backed open state and a lazy modal engine.\n * Use Root, Trigger, Content, Title and Close to connect history, focus handling and dismissal.\n */\nexport const Overlay = { Root, Trigger, Content, Title, Close };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["Trigger", "Content", "Title", "Close"]
|
|
7
7
|
}
|
package/dist/version.js
CHANGED