@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/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 the map fails to load. */
72
- type BranchMapLoadErrorState = {
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
- /** The shopper's coordinates. The map fits them with the branches. */
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
- /** Show the branch name above the selected pin, or above every pin. Off by default. */
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
- * Set the container height with CSS. Style pins with --branch-map-pin,
108
- * --branch-map-pin-selected, --branch-map-pin-border and --branch-map-pin-size.
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 { BranchMapLoadErrorState, BranchMapProps } 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,iBAAiB;AAE1B,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;AAzCpB,IAAM,sBAA8B;",
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
  }
@@ -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 [engine, setEngine] = useState(engineModule);
190
- const [opened, setOpened] = useState(ctx.isOpen);
191
- if (ctx.isOpen && !opened) {
192
- setOpened(true);
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
- if (!engine || !opened && !forceMount) {
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
- return /* @__PURE__ */ jsx(EngineContext.Provider, { value: engine, children: /* @__PURE__ */ jsx(
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: ref
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 engine = useContext(EngineContext);
244
- if (!engine) {
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 engine;
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,OACK;AACP,SAAS,aAAa,MAAM,cAA8B;AAE1D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AA+LH;AA5LJ,IAAI;AACJ,IAAI;AAMG,SAAS,oBAA2C;AACzD,oCAAkB,OAAO,aAAU,EAAE;AAAA,IACnC,CAAC,WAAY,eAAe;AAAA,IAC5B,CAAC,UAAmB;AAElB,sBAAgB;AAChB,YAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO;AACT;AAUO,SAAS,WAAW,OAAe,IAAY,SAAwB;AAC5E,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA,MAAM;AAAA,EACR;AACA,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,OAAO;AAAA,IACX,CAAC,UAAyB,YAAY,OAAO,IAAI,KAAK;AAAA,IACtD,CAAC,OAAO,EAAE;AAAA,EACZ;AACA,QAAM,QAAQ;AAAA,IACZ,MAAM,aAAa,OAAO,IAAI,WAAW,OAAO;AAAA,IAChD,CAAC,OAAO,EAAE;AAAA,EACZ;AACA,SAAO;AAAA,IACL,OAAO;AAAA,MACL,QAAQ,IAAI,gBAAgB,MAAM,EAAE,IAAI,KAAK,MAAM;AAAA,MACnD;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,OAAO,IAAI,MAAM,KAAK;AAAA,EACjC;AACF;AAGA,IAAM,cAAc,EAAE,IAAI,KAAK,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK;AAO3D,SAAS,gBAAgB,OAA6C;AAEpE,QAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AACpE,QAAM,UAAU,YAAY,MAAoB;AAC9C,UAAM,SACJ,IAAI,WAAW,GAAG,IAAI,KAAK,MAAM,GAAG,IAAI;AAE1C,QAAI,OAAO,WAAW,UAAU;AAC9B,aAAO;AAAA,IACT;AACA,QAAI,UAAU,OAAO;AACrB,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,WAAW,GAAG;AACvD,YAAM,KAAK,OAAO,IAAgC;AAClD,UAAI,MAAM,OAAO,WAAW,eAAe,KAAK,KAAK,EAAE,SAAS;AAC9D,kBAAU;AAAA,MACZ;AAAA,IACF;AACA,WAAO;AAAA,EACT,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,YAAY;AAAA,IAChB,CAAC,aAAyB;AACxB,UAAI,CAAC,IAAI,WAAW,GAAG,GAAG;AACxB,eAAO,MAAM;AAAA,QAAC;AAAA,MAChB;AACA,YAAM,UAAU,OAAO,OAAO,WAAW,EAAE;AAAA,QAAI,CAAC,UAC9C,OAAO,WAAW,eAAe,KAAK,KAAK;AAAA,MAC7C;AACA,cAAQ,QAAQ,CAAC,UAAU,MAAM,iBAAiB,UAAU,QAAQ,CAAC;AACrE,aAAO,MACL,QAAQ;AAAA,QAAQ,CAAC,UACf,MAAM,oBAAoB,UAAU,QAAQ;AAAA,MAC9C;AAAA,IACJ;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AACA,SAAO;AAAA,IAAqB;AAAA,IAAW;AAAA,IAAS,MAC9C,OAAO,UAAU,WAAW,QAAQ,MAAM;AAAA,EAC5C;AACF;AAUA,IAAM,UAAU,cAAqC,IAAI;AACzD,IAAM,gBAAgB,cAAmC,IAAI;AAC7D,SAAS,kBAAkB;AACzB,QAAM,QAAQ,WAAW,OAAO;AAChC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AACA,SAAO;AACT;AACA,IAAM,WAAW,MAAM,KAAK,kBAAkB,EAAE,MAAM,MAAM;AAAC,CAAC;AAK9D,SAAS,mBAAmB;AAC1B,MAAI,OAAO,WAAW,aAAa;AACjC;AAAA,EACF;AACA,QAAM,WAAW,MAAM;AACrB,QAAI,cAAc;AAChB;AAAA,IACF;AACA,QAAI,OAAO,OAAO,wBAAwB,YAAY;AACpD,aAAO,oBAAoB,QAAQ;AAAA,IACrC,OAAO;AACL,aAAO,WAAW,UAAU,IAAI;AAAA,IAClC;AAAA,EACF;AACA,MAAI,SAAS,eAAe,YAAY;AACtC,aAAS;AAAA,EACX,OAAO;AACL,WAAO,iBAAiB,QAAQ,UAAU,EAAE,MAAM,KAAK,CAAC;AAAA,EAC1D;AACF;AACA,iBAAiB;AAOjB,SAAS,KAAK;AAAA,EACZ;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GASkB;AAChB,QAAM,eAAe,WAAW,OAAO,IAAI,EAAE,UAAU,WAAW,CAAC;AACnE,QAAM,UAAU,cAAc;AAC9B,QAAM,WAAW,gBAAgB,YAAY;AAC7C,QAAM,YAAY,MAAM;AACxB,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,kBAAkB,SAAS,EAAE;AACxE,QAAM,UAAU,OAA2B,IAAI;AAC/C,QAAM,UAAU;AAAA,IACd,OAAO;AAAA,MACL,GAAG;AAAA,MACH,cAAc;AAAA,MACd,gBACE,mBAAmB,aAAa,UAAU,UAAU;AAAA,MACtD;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,SAAS,UAAU,gBAAgB,SAAS;AAAA,EAC/C;AACA,SACE,oBAAC,QAAQ,UAAR,EAAiB,OAAO,SACtB,eAAK,OAAO,OAAO,SAAS;AAAA,IAC3B,cAAc,QAAQ,SAAS,SAAS;AAAA,IACxC,qBAAqB;AAAA,EACvB,CAAC,GACH;AAEJ;AACA,IAAM,UAAU;AAAA,EACd,SAASA,SAAQ,OAAO,KAAK;AAC3B,UAAM,MAAM,gBAAgB;AAC5B,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,iBAAiB;AAAA,QACjB,iBAAiB,IAAI;AAAA,QACrB,iBAAiB,IAAI;AAAA,QACrB,cAAc,IAAI,SAAS,SAAS;AAAA,QACpC,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,SAAS,MAAM,IAAI,KAAK;AAAA,MAC1B;AAAA,MACA,YAAY,KAAK,CAAC,SAA6B;AAC7C,YAAI,QAAQ,UAAU;AAAA,MACxB,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAWA,IAAM,UAAU,WAA6C,SAASC,SACpE,EAAE,UAAU,mBAAmB,YAAY,WAAW,GAAG,MAAM,GAC/D,KACA;AACA,QAAM,MAAM,gBAAgB;AAC5B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,YAAY;AAEjD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,IAAI,MAAM;AAC/C,MAAI,IAAI,UAAU,CAAC,QAAQ;AACzB,cAAU,IAAI;AAAA,EAChB;AACA,QAAM,WAAW,OAAO,KAAqC;AAC7D,QAAM,EAAE,aAAa,IAAI;AACzB,YAAU,MAAM;AACd,QAAI,UAAU;AACZ,mBAAa,QAAQ;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,UAAU,YAAY,CAAC;AAC3B,YAAU,MAAM;AACd,QAAI,UAAU,EAAE,IAAI,UAAU,aAAa;AACzC,aAAO;AAAA,IACT;AACA,QAAI,OAAO;AACX,SAAK,kBAAkB,EAAE;AAAA,MACvB,CAAC,WAAW;AACV,YAAI,MAAM;AACR,oBAAU,MAAM;AAAA,QAClB;AAAA,MACF;AAAA,MACA,MAAM;AAAA,MAAC;AAAA,IACT;AACA,WAAO,MAAM;AACX,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,QAAQ,IAAI,QAAQ,UAAU,CAAC;AACnC,MAAI,CAAC,UAAW,CAAC,UAAU,CAAC,YAAa;AACvC,WAAO;AAAA,EACT;AACA,SACE,oBAAC,cAAc,UAAd,EAAuB,OAAO,QAC7B;AAAA,IAAC,OAAO;AAAA,IAAP;AAAA,MACC,MAAM,IAAI;AAAA,MACV,cAAc,IAAI;AAAA,MAClB,gBAAgB,IAAI;AAAA,MACpB,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,MACb,QAAQ,MAAM,IAAI,KAAK;AAAA,MACvB,SAAS,IAAI;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,cAAc;AAAA,MACd,YAAY;AAAA;AAAA,EACd,GACF;AAEJ,CAAC;AACD,SAAS,YAAY;AACnB,QAAM,SAAS,WAAW,aAAa;AACvC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AACA,IAAM,QAAQ;AAAA,EACZ,SAASC,OAAM,OAAO,KAAK;AACzB,UAAM,MAAM,gBAAgB;AAC5B,UAAM,EAAE,YAAY,IAAI,UAAU;AAClC,WACE,oBAAC,eAAa,GAAI,OAA8B,OAAO,KAAK,KAAU;AAAA,EAE1E;AACF;AACA,IAAM,QAAQ;AAAA,EACZ,SAASC,OAAM,OAAO,KAAK;AACzB,UAAM,MAAM,gBAAgB;AAC5B,UAAM,EAAE,YAAY,IAAI,UAAU;AAClC,WACE,oBAAC,eAAa,GAAI,OAA8B,OAAO,KAAK,KAAU;AAAA,EAE1E;AACF;AAKO,IAAM,UAAU,EAAE,MAAM,SAAS,SAAS,OAAO,MAAM;",
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
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var PACKAGE_VERSION = true ? "0.1.4" : "0.0.0-development";
2
+ var PACKAGE_VERSION = true ? "0.1.5" : "0.0.0-development";
3
3
  export {
4
4
  PACKAGE_VERSION
5
5
  };