@zappie/foundations 0.1.10 → 0.2.0
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 +28 -0
- package/dist/account/index.js.map +0 -1
- package/dist/branch-map/colors.js.map +0 -1
- package/dist/branch-map/delivery-map.js.map +0 -1
- package/dist/branch-map/delivery-runtime.js.map +0 -1
- package/dist/branch-map/fit.js.map +0 -1
- package/dist/branch-map/geometry.js.map +0 -1
- package/dist/branch-map/index.js.map +0 -1
- package/dist/branch-map/loader.js.map +0 -1
- package/dist/branch-map/markers.js.map +0 -1
- package/dist/branch-map/runtime.js.map +0 -1
- package/dist/branch-map/use-map-runtime.js.map +0 -1
- package/dist/cart/index.js.map +0 -1
- package/dist/deferred/index.js.map +0 -1
- package/dist/fulfilment/address-picker.js.map +0 -1
- package/dist/fulfilment/branch-picker.js.map +0 -1
- package/dist/fulfilment/schedule.js +13 -5
- package/dist/fulfilment/schedule.js.map +1 -2
- package/dist/fulfilment/switch.js.map +0 -1
- package/dist/index.d.ts +34 -5
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -2
- package/dist/internal/combobox.js.map +0 -1
- package/dist/internal/data-attrs.js.map +0 -1
- package/dist/internal/ids.js.map +0 -1
- package/dist/internal/slot.js.map +0 -1
- package/dist/internal/use-controllable-state.js.map +0 -1
- package/dist/item-form/index.js.map +0 -1
- package/dist/item-form/option-group.js.map +0 -1
- package/dist/item-form/options.js.map +0 -1
- package/dist/item-form/quantity.js.map +0 -1
- package/dist/item-form/root.js.map +0 -1
- package/dist/media/image.js.map +0 -1
- package/dist/money.js.map +0 -1
- package/dist/next.js.map +0 -1
- package/dist/otp/input.js.map +0 -1
- package/dist/overlay/engine.js.map +0 -1
- package/dist/overlay/history.js.map +0 -1
- package/dist/overlay/react.js.map +0 -1
- package/dist/payment/methods.js.map +0 -1
- package/dist/rich-text/banners.js.map +0 -1
- package/dist/rich-text/browser.js.map +0 -1
- package/dist/rich-text/core.js.map +0 -1
- package/dist/rich-text/index.js.map +0 -1
- package/dist/search/engine.js.map +0 -1
- package/dist/search/index.js.map +0 -1
- package/dist/status/contact.js +19 -0
- package/dist/status/contact.js.map +6 -0
- package/dist/status/hours.js +7 -1
- package/dist/status/hours.js.map +1 -2
- package/dist/status/open-status.js +4 -1
- package/dist/status/open-status.js.map +1 -2
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -2
- package/package.json +3 -3
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"
|
|
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;",
|
|
4
|
+
"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,SAAS,oBAAoB;AAE7B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAjDpB,IAAM,sBAA8B;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/internal/combobox.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n useEffect,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { usePartId } from \"./ids\";\nexport function useCombobox<T>(\n results: readonly T[],\n onSelect: (item: T) => void,\n) {\n const id = usePartId();\n const [activeIndex, setActiveIndex] = useState(-1);\n const [isOpen, setOpen] = useState(false);\n const [listId, setListId] = useState(`${id}-list`);\n const [optionIds, setOptionIds] = useState<Record<number, string>>({});\n // Reset the highlight when the result set changes size; callers reset on a new query.\n // Keying on the array itself would reset on every re-render with an unmemoised array.\n useEffect(() => setActiveIndex(-1), [results.length]);\n const registerOption = (index: number, id: string) =>\n setOptionIds((previous) =>\n previous[index] === id ? previous : { ...previous, [index]: id },\n );\n const active = activeIndex < results.length ? activeIndex : -1;\n const optionId = (index: number) =>\n optionIds[index] ?? `${id}-option-${index}`;\n const activeId = isOpen && active >= 0 ? optionId(active) : undefined;\n // aria-activedescendant doesn't move focus, so keep the highlighted option visible.\n useEffect(() => {\n if (activeId) {\n document.getElementById(activeId)?.scrollIntoView?.({ block: \"nearest\" });\n }\n }, [activeId]);\n const select = (index: number) => {\n const item = results[index];\n if (item !== undefined) {\n onSelect(item);\n setOpen(false);\n setActiveIndex(-1);\n }\n };\n return {\n setListId,\n registerOption,\n activeIndex: active,\n setActiveIndex,\n isOpen,\n setOpen,\n select,\n inputProps: {\n role: \"combobox\",\n \"aria-autocomplete\": \"list\" as const,\n \"aria-expanded\": isOpen,\n \"aria-controls\": listId,\n \"aria-activedescendant\": activeId,\n onKeyDown: (event: KeyboardEvent) => {\n // Keys pressed while an input method composes text belong to the IME, not the list.\n if (event.nativeEvent.isComposing || event.keyCode === 229) {\n return;\n }\n if (event.key === \"Escape\") {\n event.preventDefault();\n setOpen(false);\n setActiveIndex(-1);\n } else if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex(\n results.length\n ? active < 0\n ? event.key === \"ArrowDown\"\n ? 0\n : results.length - 1\n : (active +\n (event.key === \"ArrowDown\" ? 1 : -1) +\n results.length) %\n results.length\n : -1,\n );\n } else if (event.key === \"Enter\" && isOpen && active >= 0) {\n event.preventDefault();\n select(active);\n }\n },\n onFocus: () => setOpen(true),\n },\n listProps: { id: listId, role: \"listbox\", hidden: !isOpen },\n getOptionProps: (index: number) => ({\n id: optionId(index),\n role: \"option\",\n \"aria-selected\": active === index,\n \"data-selected\": active === index ? \"\" : undefined,\n onMouseDown: (event: { preventDefault: () => void }) =>\n event.preventDefault(),\n // Inside a drawer or sheet, the overlay engine starts a swipe on any\n // press that isn't a button, link or field, then captures the pointer,\n // so the option's click never fires on touch. Options aren't swipe handles.\n onPointerDown: (event: { stopPropagation: () => void }) =>\n event.stopPropagation(),\n onTouchStart: (event: { stopPropagation: () => void }) =>\n event.stopPropagation(),\n onClick: () => select(index),\n }),\n };\n}\n/** Visually hidden but announced; an accessibility mechanism, not a visual default. */\nconst visuallyHidden: CSSProperties = {\n position: \"absolute\",\n width: 1,\n height: 1,\n margin: -1,\n padding: 0,\n border: 0,\n overflow: \"hidden\",\n clip: \"rect(0 0 0 0)\",\n whiteSpace: \"nowrap\",\n};\n/** Default English announcement of how many options a combobox is showing. */\nexport function countAnnouncement(count: number) {\n return count === 0\n ? \"No results\"\n : `${count} result${count === 1 ? \"\" : \"s\"} available`;\n}\n/** A polite status region so screen readers hear how many options are listed. */\nexport function LiveRegion({ message }: { message: string }) {\n return (\n <span\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n data-zappie-live=\"\"\n style={visuallyHidden}\n >\n {message}\n </span>\n );\n}\n/** Append the live region to a root's children; with `asChild` the caller owns the single child. */\nexport function withLiveRegion<S>(\n props: { asChild?: boolean | undefined; children?: unknown },\n message: string,\n): typeof props {\n if (props.asChild) {\n return props;\n }\n const children = props.children;\n return {\n ...props,\n children: (state: S) => (\n <>\n {typeof children === \"function\"\n ? (children as (state: S) => ReactNode)(state)\n : (children as ReactNode)}\n <LiveRegion message={message} />\n </>\n ),\n };\n}\n"],
|
|
5
4
|
"mappings": "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",
|
|
6
5
|
"names": ["id"]
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/internal/data-attrs.ts"],
|
|
4
|
-
"sourcesContent": ["export function flag(value: unknown): \"\" | undefined {\n return value ? \"\" : undefined;\n}\n"],
|
|
5
4
|
"mappings": ";AAAO,SAAS,KAAK,OAAgC;AACnD,SAAO,QAAQ,KAAK;AACtB;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
package/dist/internal/ids.js.map
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/internal/ids.ts"],
|
|
4
|
-
"sourcesContent": ["import { useId } from \"react\";\nexport function usePartId(provided?: string) {\n const id = useId();\n return provided ?? `zappie-${id.replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n}\n"],
|
|
5
4
|
"mappings": ";AAAA,SAAS,aAAa;AACf,SAAS,UAAU,UAAmB;AAC3C,QAAM,KAAK,MAAM;AACjB,SAAO,YAAY,UAAU,GAAG,QAAQ,mBAAmB,EAAE,CAAC;AAChE;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/internal/slot.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n Children,\n cloneElement,\n createElement,\n isValidElement,\n type AllHTMLAttributes,\n type ElementType,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from \"react\";\nexport type PartOwnedAttributes = \"children\" | \"defaultValue\" | \"value\";\n/**\n * HTML attributes shared by foundation parts, with optional render-function children.\n * Set asChild to merge the part props and ref into a single child element.\n */\nexport type PartProps<S = Record<string, never>> = Omit<\n AllHTMLAttributes<HTMLElement>,\n PartOwnedAttributes\n> & {\n asChild?: boolean;\n children?: ReactNode | ((state: S) => ReactNode);\n};\nexport function composeRefs<T>(...refs: (Ref<T> | undefined)[]): Ref<T> {\n let cleanups: (() => void)[] = [];\n return (value) => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n cleanups = [];\n if (value === null) {\n return;\n }\n for (const ref of refs) {\n if (typeof ref === \"function\") {\n const cleanup = ref(value);\n cleanups.push(\n typeof cleanup === \"function\" ? cleanup : () => ref(null),\n );\n } else if (ref) {\n ref.current = value;\n cleanups.push(() => {\n ref.current = null;\n });\n }\n }\n };\n}\n/** Consumer handlers run first and may cancel the primitive's action. */\nexport function mergeProps(\n base: Record<string, unknown>,\n consumer: Record<string, unknown>,\n) {\n const result = { ...base, ...consumer };\n if (\n typeof base.className === \"string\" &&\n typeof consumer.className === \"string\"\n ) {\n result.className = `${base.className} ${consumer.className}`;\n }\n if (\n base.style &&\n consumer.style &&\n typeof base.style === \"object\" &&\n typeof consumer.style === \"object\"\n ) {\n result.style = { ...base.style, ...consumer.style };\n }\n for (const key of Object.keys(base)) {\n if (\n /^on[A-Z]/.test(key) &&\n typeof base[key] === \"function\" &&\n typeof consumer[key] === \"function\"\n ) {\n result[key] = (event: { defaultPrevented?: boolean }) => {\n (consumer[key] as (event: unknown) => void)(event);\n if (!event.defaultPrevented) {\n (base[key] as (event: unknown) => void)(event);\n }\n };\n }\n }\n return result;\n}\nexport function part<S>(\n tag: ElementType,\n props: PartProps<S>,\n state: S,\n defaults: Record<string, unknown> = {},\n ref?: Ref<HTMLElement>,\n) {\n const { asChild, children, ...dom } = props;\n const content = typeof children === \"function\" ? children(state) : children;\n const merged = mergeProps(defaults, dom);\n if (asChild || (tag === \"input\" && typeof children === \"function\")) {\n const child = Children.only(content);\n if (!isValidElement(child)) {\n throw new Error(\"asChild requires one React element\");\n }\n const element = child as ReactElement<Record<string, unknown>>;\n // React 18 stores ref on the element; React 19 stores it in props.\n const descriptor = Object.getOwnPropertyDescriptor(element, \"ref\");\n const childRef =\n element.props.ref ??\n (descriptor && \"value\" in descriptor ? descriptor.value : undefined);\n return cloneElement(element, {\n ...mergeProps(merged, element.props),\n ref: composeRefs(ref, childRef as Ref<HTMLElement>),\n });\n }\n return createElement(tag, { ...merged, ref }, content);\n}\n/** A part's caller-supplied id, whether passed as a prop or set on its `asChild` element. */\nexport function partId(props: {\n id?: string | undefined;\n asChild?: boolean | undefined;\n children?: unknown;\n}): string | undefined {\n if (props.id) {\n return props.id;\n }\n if (props.asChild && isValidElement(props.children)) {\n const id = (props.children.props as { id?: unknown }).id;\n return typeof id === \"string\" ? id : undefined;\n }\n return undefined;\n}\n"],
|
|
5
4
|
"mappings": ";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AAaA,SAAS,eAAkB,MAAsC;AACtE,MAAI,WAA2B,CAAC;AAChC,SAAO,CAAC,UAAU;AAChB,eAAW,WAAW,UAAU;AAC9B,cAAQ;AAAA,IACV;AACA,eAAW,CAAC;AACZ,QAAI,UAAU,MAAM;AAClB;AAAA,IACF;AACA,eAAW,OAAO,MAAM;AACtB,UAAI,OAAO,QAAQ,YAAY;AAC7B,cAAM,UAAU,IAAI,KAAK;AACzB,iBAAS;AAAA,UACP,OAAO,YAAY,aAAa,UAAU,MAAM,IAAI,IAAI;AAAA,QAC1D;AAAA,MACF,WAAW,KAAK;AACd,YAAI,UAAU;AACd,iBAAS,KAAK,MAAM;AAClB,cAAI,UAAU;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,WACd,MACA,UACA;AACA,QAAM,SAAS,EAAE,GAAG,MAAM,GAAG,SAAS;AACtC,MACE,OAAO,KAAK,cAAc,YAC1B,OAAO,SAAS,cAAc,UAC9B;AACA,WAAO,YAAY,GAAG,KAAK,SAAS,IAAI,SAAS,SAAS;AAAA,EAC5D;AACA,MACE,KAAK,SACL,SAAS,SACT,OAAO,KAAK,UAAU,YACtB,OAAO,SAAS,UAAU,UAC1B;AACA,WAAO,QAAQ,EAAE,GAAG,KAAK,OAAO,GAAG,SAAS,MAAM;AAAA,EACpD;AACA,aAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,QACE,WAAW,KAAK,GAAG,KACnB,OAAO,KAAK,GAAG,MAAM,cACrB,OAAO,SAAS,GAAG,MAAM,YACzB;AACA,aAAO,GAAG,IAAI,CAAC,UAA0C;AACvD,QAAC,SAAS,GAAG,EAA+B,KAAK;AACjD,YAAI,CAAC,MAAM,kBAAkB;AAC3B,UAAC,KAAK,GAAG,EAA+B,KAAK;AAAA,QAC/C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AACO,SAAS,KACd,KACA,OACA,OACA,WAAoC,CAAC,GACrC,KACA;AACA,QAAM,EAAE,SAAS,UAAU,GAAG,IAAI,IAAI;AACtC,QAAM,UAAU,OAAO,aAAa,aAAa,SAAS,KAAK,IAAI;AACnE,QAAM,SAAS,WAAW,UAAU,GAAG;AACvC,MAAI,WAAY,QAAQ,WAAW,OAAO,aAAa,YAAa;AAClE,UAAM,QAAQ,SAAS,KAAK,OAAO;AACnC,QAAI,CAAC,eAAe,KAAK,GAAG;AAC1B,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AACA,UAAM,UAAU;AAEhB,UAAM,aAAa,OAAO,yBAAyB,SAAS,KAAK;AACjE,UAAM,WACJ,QAAQ,MAAM,QACb,cAAc,WAAW,aAAa,WAAW,QAAQ;AAC5D,WAAO,aAAa,SAAS;AAAA,MAC3B,GAAG,WAAW,QAAQ,QAAQ,KAAK;AAAA,MACnC,KAAK,YAAY,KAAK,QAA4B;AAAA,IACpD,CAAC;AAAA,EACH;AACA,SAAO,cAAc,KAAK,EAAE,GAAG,QAAQ,IAAI,GAAG,OAAO;AACvD;AAEO,SAAS,OAAO,OAIA;AACrB,MAAI,MAAM,IAAI;AACZ,WAAO,MAAM;AAAA,EACf;AACA,MAAI,MAAM,WAAW,eAAe,MAAM,QAAQ,GAAG;AACnD,UAAM,KAAM,MAAM,SAAS,MAA2B;AACtD,WAAO,OAAO,OAAO,WAAW,KAAK;AAAA,EACvC;AACA,SAAO;AACT;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/internal/use-controllable-state.ts"],
|
|
4
|
-
"sourcesContent": ["import { useCallback, useRef, useState } from \"react\";\n/**\n * Manage either a supplied value or internal state initialized from defaultValue.\n * The setter accepts a value or updater and calls onChange only when Object.is detects a change.\n *\n * @returns The current value and a stable setter. A defined value keeps state controlled by you.\n */\nexport function useControllableState<T>({\n value,\n defaultValue,\n onChange,\n}: {\n value?: T | undefined;\n defaultValue: T;\n onChange?: ((value: T) => void) | undefined;\n}) {\n const [internal, setInternal] = useState(defaultValue);\n const current = value === undefined ? internal : value;\n const latest = useRef({ current, onChange, controlled: value !== undefined });\n latest.current = { current, onChange, controlled: value !== undefined };\n const set = useCallback((next: T | ((previous: T) => T)) => {\n const snapshot = latest.current;\n const resolved =\n typeof next === \"function\"\n ? (next as (previous: T) => T)(snapshot.current)\n : next;\n if (Object.is(resolved, snapshot.current)) {\n return;\n }\n if (!snapshot.controlled) {\n latest.current.current = resolved;\n setInternal(resolved);\n }\n snapshot.onChange?.(resolved);\n }, []);\n return [current, set] as const;\n}\n"],
|
|
5
4
|
"mappings": ";AAAA,SAAS,aAAa,QAAQ,gBAAgB;AAOvC,SAAS,qBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,YAAY;AACrD,QAAM,UAAU,UAAU,SAAY,WAAW;AACjD,QAAM,SAAS,OAAO,EAAE,SAAS,UAAU,YAAY,UAAU,OAAU,CAAC;AAC5E,SAAO,UAAU,EAAE,SAAS,UAAU,YAAY,UAAU,OAAU;AACtE,QAAM,MAAM,YAAY,CAAC,SAAmC;AAC1D,UAAM,WAAW,OAAO;AACxB,UAAM,WACJ,OAAO,SAAS,aACX,KAA4B,SAAS,OAAO,IAC7C;AACN,QAAI,OAAO,GAAG,UAAU,SAAS,OAAO,GAAG;AACzC;AAAA,IACF;AACA,QAAI,CAAC,SAAS,YAAY;AACxB,aAAO,QAAQ,UAAU;AACzB,kBAAY,QAAQ;AAAA,IACtB;AACA,aAAS,WAAW,QAAQ;AAAA,EAC9B,GAAG,CAAC,CAAC;AACL,SAAO,CAAC,SAAS,GAAG;AACtB;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/item-form/index.ts"],
|
|
4
|
-
"sourcesContent": ["import { Add, Options } from \"./options\";\nimport { Root } from \"./root\";\n/**\n * Composable item configuration parts for selecting options, validating a line and adding it to the cart.\n * Use ItemForm.Root within your ZappieProvider and place Options and Add inside it.\n */\nexport const ItemForm = { Root, Options, Add };\nexport { Choice, OptionGroup } from \"./option-group\";\nexport { Quantity } from \"./quantity\";\nexport type { QuantityProps, QuantityState } from \"./quantity\";\nexport type {\n ActiveGroup,\n ItemFormContext,\n ItemFormInvalidGroup,\n ItemFormProps,\n} from \"./root\";\n"],
|
|
5
4
|
"mappings": ";AAAA,SAAS,KAAK,eAAe;AAC7B,SAAS,YAAY;AAMrB,SAAS,QAAQ,mBAAmB;AACpC,SAAS,gBAAgB;AAFlB,IAAM,WAAW,EAAE,MAAM,SAAS,IAAI;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/item-form/option-group.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\nimport type {\n Choice as ChoiceModel,\n OptionGroup as GroupModel,\n} from \"@zappie/sdk\";\nimport {\n cloneElement,\n createContext,\n forwardRef,\n isValidElement,\n memo,\n useContext,\n useMemo,\n useRef,\n type ChangeEvent,\n type KeyboardEvent,\n} from \"react\";\nimport { flag } from \"../internal/data-attrs\";\nimport { usePartId } from \"../internal/ids\";\nimport { composeRefs, part, type PartProps } from \"../internal/slot\";\nimport { GroupRegistry, useItemForm } from \"./root\";\ntype GroupState = {\n group: GroupModel;\n selected: string[];\n invalid: boolean;\n input: string;\n setInput: (value: string) => void;\n toggle: (id: string) => void;\n quantities: Readonly<Record<string, number>>;\n /** Set a choice's quantity within its bounds. Zero deselects it. A positive value selects it. */\n setQuantity: (id: string, quantity: number) => void;\n};\nconst Context = createContext<GroupState | null>(null);\nfunction useGroup() {\n const value = useContext(Context);\n if (!value) {\n throw new Error(\"Choice and OptionGroup.Input require OptionGroup\");\n }\n return value;\n}\nconst Group = forwardRef<\n HTMLElement,\n PartProps<GroupState> & {\n group: GroupModel;\n /**\n * The hidden text that describes a required multi-select group as required, since\n * role=\"group\" can't take aria-required. Defaults to \"Required\". Pass false when the\n * group's own heading already says so to assistive technology.\n */\n requiredNote?: string | false;\n }\n>(function OptionGroup(\n {\n group: supplied,\n requiredNote = \"Required\",\n \"aria-describedby\": describedBy,\n ...props\n },\n ref,\n) {\n const ctx = useItemForm();\n const registry = useContext(GroupRegistry)!;\n const group =\n ctx.groups.find((candidate) => candidate.id === supplied.id) ?? supplied;\n const mine =\n ctx.value.choices?.filter((choice) => choice.groupId === group.id) ?? [];\n const selected = mine.map((choice) => choice.choiceId);\n const quantities = Object.fromEntries(\n mine.map((choice) => [choice.choiceId, choice.quantity ?? 1]),\n );\n const invalid = ctx.issues.some((issue) => issue.groupId === group.id);\n const input = ctx.value.inputs?.[group.id] ?? \"\";\n const latest = useRef({ ctx, group });\n latest.current = { ctx, group };\n const actions = useMemo(\n () => ({\n toggle: (id: string) => {\n const { ctx, group } = latest.current;\n const choice = group.choices.find((candidate) => candidate.id === id);\n if (!choice?.isAvailable) {\n return;\n }\n const previous = ctx.value.choices ?? [];\n const chosen = previous.some(\n (candidate) =>\n candidate.groupId === group.id && candidate.choiceId === id,\n );\n let choices = previous.filter(\n (candidate) =>\n candidate.groupId !== group.id ||\n (group.selection.max !== 1 && candidate.choiceId !== id),\n );\n if (!chosen || (group.selection.max === 1 && group.required)) {\n choices = [\n ...choices,\n {\n groupId: group.id,\n choiceId: id,\n quantity: choice.quantity?.min ?? 1,\n },\n ];\n }\n const inputs = { ...ctx.value.inputs };\n delete inputs[group.id];\n ctx.setValue({ ...ctx.value, choices, inputs });\n },\n setQuantity: (id: string, quantity: number) => {\n const { ctx, group } = latest.current;\n const choice = group.choices.find((candidate) => candidate.id === id);\n if (!choice?.isAvailable || !Number.isInteger(quantity)) {\n return;\n }\n const others = (ctx.value.choices ?? []).filter(\n (candidate) =>\n candidate.groupId !== group.id ||\n (group.selection.max !== 1 && candidate.choiceId !== id),\n );\n if (quantity <= 0) {\n ctx.setValue({\n ...ctx.value,\n choices: (ctx.value.choices ?? []).filter(\n (candidate) =>\n candidate.groupId !== group.id || candidate.choiceId !== id,\n ),\n });\n return;\n }\n const min = choice.quantity?.min ?? 1;\n const max = choice.quantity?.max ?? 99;\n ctx.setValue({\n ...ctx.value,\n choices: [\n ...others,\n {\n groupId: group.id,\n choiceId: id,\n quantity: Math.min(max, Math.max(min, quantity)),\n },\n ],\n });\n },\n setInput: (value: string) => {\n const { ctx, group } = latest.current;\n ctx.setValue({\n ...ctx.value,\n choices: (ctx.value.choices ?? []).filter(\n (choice) => choice.groupId !== group.id,\n ),\n inputs: {\n ...ctx.value.inputs,\n [group.id]: value.slice(0, group.input?.maxLength ?? 500),\n },\n });\n },\n }),\n [],\n );\n const selectionKey = JSON.stringify(quantities);\n const state = useMemo(\n () => ({ group, selected, quantities, invalid, input, ...actions }),\n [group, selectionKey, invalid, input, actions],\n );\n const single = group.kind === \"select\" && group.selection.max === 1;\n const noteId = usePartId();\n const note =\n group.required && group.kind === \"select\" && !single && requiredNote;\n // An asChild element's own description joins the note, as partId reads its id.\n const child =\n props.asChild &&\n isValidElement<{ \"aria-describedby\"?: string | undefined }>(props.children)\n ? props.children\n : undefined;\n const own = describedBy ?? child?.props[\"aria-describedby\"];\n const description = note ? `${noteId}${own ? ` ${own}` : \"\"}` : own;\n return (\n <Context.Provider value={state}>\n {note && (\n <span id={noteId} hidden>\n {note}\n </span>\n )}\n {part(\n \"div\",\n child\n ? {\n ...props,\n children: cloneElement(child, {\n \"aria-describedby\": description,\n }),\n }\n : props,\n state,\n {\n role: single ? \"radiogroup\" : \"group\",\n tabIndex: -1,\n \"aria-label\": group.name,\n \"aria-required\": single ? group.required : undefined,\n \"aria-describedby\": description,\n \"data-zappie-required\": group.required ? \"true\" : undefined,\n \"aria-invalid\": invalid || undefined,\n \"data-invalid\": flag(invalid),\n \"data-state\": invalid ? \"invalid\" : \"valid\",\n \"data-zappie-option-group\": group.id,\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => {\n if (\n !single ||\n ![\n \"ArrowDown\",\n \"ArrowRight\",\n \"ArrowUp\",\n \"ArrowLeft\",\n \"Home\",\n \"End\",\n ].includes(event.key)\n ) {\n return;\n }\n const choices = Array.from(\n event.currentTarget.querySelectorAll<HTMLElement>(\n '[role=\"radio\"]:not([aria-disabled=\"true\"])',\n ),\n );\n if (!choices.length) {\n return;\n }\n const current = choices.indexOf(\n document.activeElement as HTMLElement,\n );\n const index =\n event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? choices.length - 1\n : (current +\n (event.key === \"ArrowUp\" || event.key === \"ArrowLeft\"\n ? -1\n : 1) +\n choices.length) %\n choices.length;\n event.preventDefault();\n choices[index]?.focus();\n choices[index]?.click();\n },\n },\n composeRefs(ref, (node) => {\n if (node) {\n registry.set(group.id, node);\n } else {\n registry.delete(group.id);\n }\n }),\n )}\n </Context.Provider>\n );\n});\ntype ChoiceState = {\n choice: ChoiceModel;\n selected: boolean;\n disabled: boolean;\n toggle: () => void;\n /** Selected quantity (0 when unselected) and the choice's bounds. */\n quantity: number;\n min: number;\n max: number;\n setQuantity: (quantity: number) => void;\n};\nconst ChoiceView = memo(\n forwardRef<\n HTMLElement,\n PartProps<ChoiceState> & {\n choice: ChoiceModel;\n selected: boolean;\n single: boolean;\n tabIndex: number;\n quantity: number;\n toggleChoice: (id: string) => void;\n setChoiceQuantity: (id: string, quantity: number) => void;\n }\n >(function ChoiceView(\n {\n choice,\n selected,\n single,\n tabIndex,\n quantity,\n toggleChoice,\n setChoiceQuantity,\n ...props\n },\n ref,\n ) {\n const toggle = () => toggleChoice(choice.id);\n const state = {\n choice,\n selected,\n disabled: !choice.isAvailable,\n toggle,\n quantity,\n min: choice.quantity?.min ?? 1,\n max: choice.quantity?.max ?? 99,\n setQuantity: (next: number) => setChoiceQuantity(choice.id, next),\n };\n return part(\n \"button\",\n props,\n state,\n {\n type: \"button\",\n role: single ? \"radio\" : \"checkbox\",\n tabIndex,\n \"aria-checked\": selected,\n \"aria-disabled\": !choice.isAvailable,\n disabled: !choice.isAvailable,\n \"data-selected\": flag(selected),\n \"data-sold-out\": flag(!choice.isAvailable),\n \"data-disabled\": flag(!choice.isAvailable),\n \"data-state\": selected ? \"checked\" : \"unchecked\",\n \"data-zappie-option\": choice.id,\n onClick: toggle,\n },\n ref,\n );\n }),\n);\n/**\n * Render a selectable option within an OptionGroup, with radio or checkbox semantics.\n * Exposes selection and quantity controls and disables unavailable choices.\n */\nexport const Choice = forwardRef<\n HTMLElement,\n PartProps<ChoiceState> & { choice: ChoiceModel }\n>(function Choice(props, ref) {\n const ctx = useGroup();\n const single = ctx.group.selection.max === 1;\n const selected = ctx.selected.includes(props.choice.id);\n const first = ctx.group.choices.find((choice) => choice.isAvailable);\n const tabIndex =\n !single ||\n selected ||\n (!ctx.selected.length && first?.id === props.choice.id)\n ? 0\n : -1;\n return (\n <ChoiceView\n {...props}\n ref={ref}\n selected={selected}\n single={single}\n tabIndex={tabIndex}\n quantity={ctx.quantities[props.choice.id] ?? 0}\n toggleChoice={ctx.toggle}\n setChoiceQuantity={ctx.setQuantity}\n />\n );\n});\nconst Input = forwardRef<HTMLElement, PartProps<GroupState>>(\n function Input(props, ref) {\n const ctx = useGroup();\n const id = usePartId(props.id);\n const toggle = ctx.group.kind === \"toggle\";\n return part(\n \"input\",\n props,\n ctx,\n {\n id,\n type: toggle\n ? \"checkbox\"\n : ctx.group.kind === \"select\"\n ? \"text\"\n : ctx.group.kind,\n value: toggle ? undefined : ctx.input,\n checked: toggle ? ctx.input === \"true\" : undefined,\n required: ctx.group.required,\n \"aria-label\": ctx.group.name,\n \"aria-invalid\": ctx.invalid || undefined,\n \"data-invalid\": flag(ctx.invalid),\n maxLength: ctx.group.input?.maxLength ?? 500,\n placeholder: ctx.group.input?.placeholder,\n onChange: (event: ChangeEvent<HTMLInputElement>) =>\n ctx.setInput(\n toggle ? String(event.target.checked) : event.target.value,\n ),\n },\n ref,\n );\n },\n);\n/**\n * Group item options with selection rules, validation state and an Input part for typed responses.\n * Use within ItemForm.Root. Use Choice for options and OptionGroup.Input for typed responses.\n */\nexport const OptionGroup = Object.assign(Group, { Input });\n"],
|
|
5
4
|
"mappings": "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",
|
|
6
5
|
"names": ["ctx", "group", "ChoiceView", "Choice", "Input", "OptionGroup"]
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/item-form/options.tsx"],
|
|
4
|
-
"sourcesContent": ["import { forwardRef } from \"react\";\nimport { part, type PartProps } from \"../internal/slot\";\nimport { useItemForm, type ItemFormContext } from \"./root\";\nexport const Options = forwardRef<HTMLElement, PartProps<ItemFormContext>>(\n function Options(props, ref) {\n return part(\"div\", props, useItemForm(), {}, ref);\n },\n);\nexport const Add = forwardRef<HTMLElement, PartProps<ItemFormContext>>(\n function Add(props, ref) {\n const context = useItemForm();\n // Keep invalid configurations actionable so add() can explain and focus errors.\n return part(\n \"button\",\n props,\n context,\n {\n type: \"button\",\n onClick: context.add,\n \"data-zappie-action\": \"add-to-cart\",\n },\n ref,\n );\n },\n);\n"],
|
|
5
4
|
"mappings": ";AAAA,SAAS,kBAAkB;AAC3B,SAAS,YAA4B;AACrC,SAAS,mBAAyC;AAC3C,IAAM,UAAU;AAAA,EACrB,SAASA,SAAQ,OAAO,KAAK;AAC3B,WAAO,KAAK,OAAO,OAAO,YAAY,GAAG,CAAC,GAAG,GAAG;AAAA,EAClD;AACF;AACO,IAAM,MAAM;AAAA,EACjB,SAASC,KAAI,OAAO,KAAK;AACvB,UAAM,UAAU,YAAY;AAE5B,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,SAAS,QAAQ;AAAA,QACjB,sBAAsB;AAAA,MACxB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
5
|
"names": ["Options", "Add"]
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/item-form/quantity.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\nimport {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useMemo,\n type ChangeEvent,\n type KeyboardEvent,\n} from \"react\";\nimport { flag } from \"../internal/data-attrs\";\nimport { part, type PartProps } from \"../internal/slot\";\nimport { useControllableState } from \"../internal/use-controllable-state\";\n/**\n * The current quantity, bounds, step, disabled state and value setter exposed by a quantity control.\n */\nexport type QuantityState = {\n value: number;\n setValue: (value: number) => void;\n min: number;\n max: number;\n step: number;\n disabled: boolean;\n};\nconst Context = createContext<QuantityState | null>(null);\nfunction useQuantity() {\n const context = useContext(Context);\n if (!context) {\n throw new Error(\"Quantity parts require Quantity.Root\");\n }\n return context;\n}\n/**\n * Controlled or default quantity, bounds and change callback for Quantity.Root.\n */\nexport type QuantityProps = PartProps<QuantityState> & {\n value?: number;\n defaultValue?: number;\n onValueChange?: (value: number) => void;\n min?: number;\n max?: number;\n step?: number;\n disabled?: boolean;\n};\nfunction Root({\n value,\n defaultValue,\n onValueChange,\n min = 1,\n max = 99,\n step = 1,\n disabled = false,\n ...props\n}: QuantityProps) {\n if (\n !Number.isFinite(min) ||\n !Number.isFinite(max) ||\n !Number.isFinite(step) ||\n step <= 0 ||\n min > max\n ) {\n throw new RangeError(\n \"Quantity requires finite ordered bounds and a positive step\",\n );\n }\n const [current, set] = useControllableState({\n value,\n defaultValue: defaultValue ?? min,\n onChange: onValueChange,\n });\n const setValue = useCallback(\n (next: number) => {\n if (!disabled && Number.isFinite(next)) {\n const last = min + Math.floor((max - min) / step) * step;\n set(\n Math.max(\n min,\n Math.min(last, min + Math.round((next - min) / step) * step),\n ),\n );\n }\n },\n [min, max, step, disabled, set],\n );\n const state = useMemo(\n () => ({ value: current, setValue, min, max, step, disabled }),\n [current, setValue, min, max, step, disabled],\n );\n return (\n <Context.Provider value={state}>\n {part(\"div\", props, state, { \"data-disabled\": flag(disabled) })}\n </Context.Provider>\n );\n}\nfunction button(direction: -1 | 1) {\n return forwardRef<HTMLElement, PartProps<QuantityState>>(\n function QuantityButton(props, ref) {\n const ctx = useQuantity();\n const disabled =\n ctx.disabled ||\n (direction < 0 ? ctx.value <= ctx.min : ctx.value + ctx.step > ctx.max);\n return part(\n \"button\",\n props,\n ctx,\n {\n type: \"button\",\n disabled,\n \"aria-disabled\": disabled,\n \"data-disabled\": flag(disabled),\n onClick: () => ctx.setValue(ctx.value + direction * ctx.step),\n },\n ref,\n );\n },\n );\n}\nconst Value = forwardRef<HTMLElement, PartProps<QuantityState>>(\n function Value(props, ref) {\n const ctx = useQuantity();\n return part(\n \"input\",\n props,\n ctx,\n {\n type: \"text\",\n inputMode: \"numeric\",\n role: \"spinbutton\",\n value: String(ctx.value),\n disabled: ctx.disabled,\n \"aria-valuenow\": ctx.value,\n \"aria-valuemin\": ctx.min,\n \"aria-valuemax\": ctx.max,\n onChange: (event: ChangeEvent<HTMLInputElement>) => {\n if (event.target.value.trim()) {\n ctx.setValue(Number(event.target.value));\n }\n },\n onKeyDown: (event: KeyboardEvent) => {\n const next = {\n ArrowUp: ctx.value + ctx.step,\n ArrowDown: ctx.value - ctx.step,\n Home: ctx.min,\n End: ctx.max,\n }[event.key];\n if (next !== undefined) {\n event.preventDefault();\n ctx.setValue(next);\n }\n },\n },\n ref,\n );\n },\n);\n/**\n * Composable quantity controls with bounded step changes and keyboard support.\n * Wrap Value, Increment and Decrement in Quantity.Root.\n *\n * @example\n * ```tsx\n * <Quantity.Root min={1} max={10} defaultValue={1}>\n * <Quantity.Decrement>Less</Quantity.Decrement>\n * <Quantity.Value aria-label=\"Quantity\" />\n * <Quantity.Increment>More</Quantity.Increment>\n * </Quantity.Root>\n * ```\n */\nexport const Quantity = {\n Root,\n Value,\n Increment: button(1),\n Decrement: button(-1),\n};\n"],
|
|
5
4
|
"mappings": ";;;AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,YAAY;AACrB,SAAS,YAA4B;AACrC,SAAS,4BAA4B;AA6EjC;AAjEJ,IAAM,UAAU,cAAoC,IAAI;AACxD,SAAS,cAAc;AACrB,QAAM,UAAU,WAAW,OAAO;AAClC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,sCAAsC;AAAA,EACxD;AACA,SAAO;AACT;AAaA,SAAS,KAAK;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,WAAW;AAAA,EACX,GAAG;AACL,GAAkB;AAChB,MACE,CAAC,OAAO,SAAS,GAAG,KACpB,CAAC,OAAO,SAAS,GAAG,KACpB,CAAC,OAAO,SAAS,IAAI,KACrB,QAAQ,KACR,MAAM,KACN;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,CAAC,SAAS,GAAG,IAAI,qBAAqB;AAAA,IAC1C;AAAA,IACA,cAAc,gBAAgB;AAAA,IAC9B,UAAU;AAAA,EACZ,CAAC;AACD,QAAM,WAAW;AAAA,IACf,CAAC,SAAiB;AAChB,UAAI,CAAC,YAAY,OAAO,SAAS,IAAI,GAAG;AACtC,cAAM,OAAO,MAAM,KAAK,OAAO,MAAM,OAAO,IAAI,IAAI;AACpD;AAAA,UACE,KAAK;AAAA,YACH;AAAA,YACA,KAAK,IAAI,MAAM,MAAM,KAAK,OAAO,OAAO,OAAO,IAAI,IAAI,IAAI;AAAA,UAC7D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,KAAK,KAAK,MAAM,UAAU,GAAG;AAAA,EAChC;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO,EAAE,OAAO,SAAS,UAAU,KAAK,KAAK,MAAM,SAAS;AAAA,IAC5D,CAAC,SAAS,UAAU,KAAK,KAAK,MAAM,QAAQ;AAAA,EAC9C;AACA,SACE,oBAAC,QAAQ,UAAR,EAAiB,OAAO,OACtB,eAAK,OAAO,OAAO,OAAO,EAAE,iBAAiB,KAAK,QAAQ,EAAE,CAAC,GAChE;AAEJ;AACA,SAAS,OAAO,WAAmB;AACjC,SAAO;AAAA,IACL,SAAS,eAAe,OAAO,KAAK;AAClC,YAAM,MAAM,YAAY;AACxB,YAAM,WACJ,IAAI,aACH,YAAY,IAAI,IAAI,SAAS,IAAI,MAAM,IAAI,QAAQ,IAAI,OAAO,IAAI;AACrE,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN;AAAA,UACA,iBAAiB;AAAA,UACjB,iBAAiB,KAAK,QAAQ;AAAA,UAC9B,SAAS,MAAM,IAAI,SAAS,IAAI,QAAQ,YAAY,IAAI,IAAI;AAAA,QAC9D;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AACA,IAAM,QAAQ;AAAA,EACZ,SAASA,OAAM,OAAO,KAAK;AACzB,UAAM,MAAM,YAAY;AACxB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,WAAW;AAAA,QACX,MAAM;AAAA,QACN,OAAO,OAAO,IAAI,KAAK;AAAA,QACvB,UAAU,IAAI;AAAA,QACd,iBAAiB,IAAI;AAAA,QACrB,iBAAiB,IAAI;AAAA,QACrB,iBAAiB,IAAI;AAAA,QACrB,UAAU,CAAC,UAAyC;AAClD,cAAI,MAAM,OAAO,MAAM,KAAK,GAAG;AAC7B,gBAAI,SAAS,OAAO,MAAM,OAAO,KAAK,CAAC;AAAA,UACzC;AAAA,QACF;AAAA,QACA,WAAW,CAAC,UAAyB;AACnC,gBAAM,OAAO;AAAA,YACX,SAAS,IAAI,QAAQ,IAAI;AAAA,YACzB,WAAW,IAAI,QAAQ,IAAI;AAAA,YAC3B,MAAM,IAAI;AAAA,YACV,KAAK,IAAI;AAAA,UACX,EAAE,MAAM,GAAG;AACX,cAAI,SAAS,QAAW;AACtB,kBAAM,eAAe;AACrB,gBAAI,SAAS,IAAI;AAAA,UACnB;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAcO,IAAM,WAAW;AAAA,EACtB;AAAA,EACA;AAAA,EACA,WAAW,OAAO,CAAC;AAAA,EACnB,WAAW,OAAO,EAAE;AACtB;",
|
|
6
5
|
"names": ["Value"]
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
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 = 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
4
|
"mappings": "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",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
package/dist/media/image.js.map
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/media/image.tsx"],
|
|
4
|
-
"sourcesContent": ["import { canResize, resized, srcSet } from \"@zappie/sdk/template\";\nimport { useCallback, useState, type ImgHTMLAttributes } from \"react\";\n\n/** Props for a responsive storefront image. */\nexport type ImageProps = Omit<\n ImgHTMLAttributes<HTMLImageElement>,\n \"src\" | \"srcSet\"\n> & {\n src: string;\n alt: string;\n sizes?: string;\n priority?: boolean;\n quality?: number;\n mode?: \"resize\" | \"direct\";\n /** Lowercase hosts your zone resizes. Defaults to `RESIZE_HOSTS` from `@zappie/sdk/template`. */\n resizeHosts?: readonly string[];\n};\n\n/** Render an image through same-origin resizing, falling back once to `src` if that fails. */\nexport function Image({\n src,\n sizes = \"100vw\",\n priority = false,\n quality,\n mode = \"resize\",\n resizeHosts,\n alt,\n onError,\n ...props\n}: ImageProps) {\n const [failed, setFailed] = useState<string | null>(null);\n const responsive =\n mode === \"resize\" && failed !== src && canResize(src, resizeHosts);\n // Catches an error that fired before hydration.\n const checkLoaded = useCallback(\n (image: HTMLImageElement | null) => {\n if (responsive && image?.complete && image.naturalWidth === 0) {\n setFailed(src);\n }\n },\n [responsive, src],\n );\n return (\n <img\n {...props}\n ref={checkLoaded}\n alt={alt}\n src={responsive ? resized(src, 960, quality, resizeHosts) : src}\n {...(responsive\n ? { srcSet: srcSet(src, quality, resizeHosts), sizes }\n : {})}\n loading={priority ? \"eager\" : \"lazy\"}\n decoding={priority ? \"sync\" : \"async\"}\n {...(priority ? { fetchPriority: \"high\" as const } : {})}\n onError={(event) => {\n if (responsive) {\n setFailed(src);\n }\n onError?.(event);\n }}\n />\n );\n}\n"],
|
|
5
4
|
"mappings": ";AAAA,SAAS,WAAW,SAAS,cAAc;AAC3C,SAAS,aAAa,gBAAwC;AA0C1D;AAxBG,SAAS,MAAM;AAAA,EACpB;AAAA,EACA,QAAQ;AAAA,EACR,WAAW;AAAA,EACX;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAe;AACb,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAwB,IAAI;AACxD,QAAM,aACJ,SAAS,YAAY,WAAW,OAAO,UAAU,KAAK,WAAW;AAEnE,QAAM,cAAc;AAAA,IAClB,CAAC,UAAmC;AAClC,UAAI,cAAc,OAAO,YAAY,MAAM,iBAAiB,GAAG;AAC7D,kBAAU,GAAG;AAAA,MACf;AAAA,IACF;AAAA,IACA,CAAC,YAAY,GAAG;AAAA,EAClB;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,KAAK;AAAA,MACL;AAAA,MACA,KAAK,aAAa,QAAQ,KAAK,KAAK,SAAS,WAAW,IAAI;AAAA,MAC3D,GAAI,aACD,EAAE,QAAQ,OAAO,KAAK,SAAS,WAAW,GAAG,MAAM,IACnD,CAAC;AAAA,MACL,SAAS,WAAW,UAAU;AAAA,MAC9B,UAAU,WAAW,SAAS;AAAA,MAC7B,GAAI,WAAW,EAAE,eAAe,OAAgB,IAAI,CAAC;AAAA,MACtD,SAAS,CAAC,UAAU;AAClB,YAAI,YAAY;AACd,oBAAU,GAAG;AAAA,QACf;AACA,kBAAU,KAAK;AAAA,MACjB;AAAA;AAAA,EACF;AAEJ;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
package/dist/money.js.map
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/money.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n format,\n type MoneyFormatOptions,\n type Money as MoneyValue,\n} from \"@zappie/sdk\";\nimport { forwardRef } from \"react\";\nimport { part, type PartProps } from \"./internal/slot\";\n/**\n * Render formatted SDK money in a data element, with the minor-unit amount as its value.\n * You can override the content with children or a render function receiving the formatted string.\n */\nexport const Money = forwardRef<\n HTMLElement,\n PartProps<{ formatted: string }> & {\n value: MoneyValue;\n formatOptions?: MoneyFormatOptions;\n }\n>(function Money({ value, formatOptions, ...props }, ref) {\n const formatted = format.money(value, formatOptions);\n return part(\n \"data\",\n { ...props, children: props.children ?? formatted },\n { formatted },\n { value: String(value.amountMinor) },\n ref,\n );\n});\n"],
|
|
5
4
|
"mappings": ";AAAA;AAAA,EACE;AAAA,OAGK;AACP,SAAS,kBAAkB;AAC3B,SAAS,YAA4B;AAK9B,IAAM,QAAQ,WAMnB,SAASA,OAAM,EAAE,OAAO,eAAe,GAAG,MAAM,GAAG,KAAK;AACxD,QAAM,YAAY,OAAO,MAAM,OAAO,aAAa;AACnD,SAAO;AAAA,IACL;AAAA,IACA,EAAE,GAAG,OAAO,UAAU,MAAM,YAAY,UAAU;AAAA,IAClD,EAAE,UAAU;AAAA,IACZ,EAAE,OAAO,OAAO,MAAM,WAAW,EAAE;AAAA,IACnC;AAAA,EACF;AACF,CAAC;",
|
|
6
5
|
"names": ["Money"]
|
|
7
6
|
}
|
package/dist/next.js.map
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/overlay/next.ts"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\nimport { useEffect } from \"react\";\nimport { LOCATION_EVENT } from \"./history\";\n/**\n * The event overlays re-read the URL on. A host that intercepts `popstate`\n * for its own shallow history (and stops it) must dispatch this too, or open\n * overlays never see Back.\n */\nexport { LOCATION_EVENT as OVERLAY_LOCATION_EVENT };\nlet subscribers = 0;\nlet uninstall: (() => void) | undefined;\n/** Capture same-page shallow moves before Next attempts a server navigation. */\nexport function installShallowHistory() {\n if (typeof window === \"undefined\") {\n return () => {};\n }\n if (subscribers++ === 0) {\n let previous = { base: \"\", pathname: \"\", shallow: false };\n const record = () => {\n previous = {\n base: window.history.state?.__zappieBase ?? window.location.pathname,\n pathname: window.location.pathname,\n shallow: Boolean(window.history.state?.__zappieBase),\n };\n };\n record();\n const onPop = (event: PopStateEvent) => {\n const base = event.state?.__zappieBase ?? window.location.pathname;\n if (\n (Boolean(event.state?.__zappieBase) || previous.shallow) &&\n base === previous.base &&\n window.location.pathname === previous.pathname\n ) {\n event.stopImmediatePropagation();\n record();\n window.dispatchEvent(new Event(LOCATION_EVENT));\n return;\n }\n record();\n };\n window.addEventListener(\"popstate\", onPop, true);\n window.addEventListener(LOCATION_EVENT, record);\n uninstall = () => {\n window.removeEventListener(\"popstate\", onPop, true);\n window.removeEventListener(LOCATION_EVENT, record);\n };\n }\n let released = false;\n return () => {\n if (!released) {\n released = true;\n if (--subscribers === 0) {\n uninstall?.();\n }\n }\n };\n}\n/**\n * Install the shared shallow-history bridge on mount and release it on unmount.\n * Keeps same-page overlay history changes from triggering Next server navigation.\n */\nexport function useNextOverlayBridge() {\n useEffect(installShallowHistory, []);\n}\n"],
|
|
5
4
|
"mappings": ";;;AACA,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAO/B,IAAI,cAAc;AAClB,IAAI;AAEG,SAAS,wBAAwB;AACtC,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AACA,MAAI,kBAAkB,GAAG;AACvB,QAAI,WAAW,EAAE,MAAM,IAAI,UAAU,IAAI,SAAS,MAAM;AACxD,UAAM,SAAS,MAAM;AACnB,iBAAW;AAAA,QACT,MAAM,OAAO,QAAQ,OAAO,gBAAgB,OAAO,SAAS;AAAA,QAC5D,UAAU,OAAO,SAAS;AAAA,QAC1B,SAAS,QAAQ,OAAO,QAAQ,OAAO,YAAY;AAAA,MACrD;AAAA,IACF;AACA,WAAO;AACP,UAAM,QAAQ,CAAC,UAAyB;AACtC,YAAM,OAAO,MAAM,OAAO,gBAAgB,OAAO,SAAS;AAC1D,WACG,QAAQ,MAAM,OAAO,YAAY,KAAK,SAAS,YAChD,SAAS,SAAS,QAClB,OAAO,SAAS,aAAa,SAAS,UACtC;AACA,cAAM,yBAAyB;AAC/B,eAAO;AACP,eAAO,cAAc,IAAI,MAAM,cAAc,CAAC;AAC9C;AAAA,MACF;AACA,aAAO;AAAA,IACT;AACA,WAAO,iBAAiB,YAAY,OAAO,IAAI;AAC/C,WAAO,iBAAiB,gBAAgB,MAAM;AAC9C,gBAAY,MAAM;AAChB,aAAO,oBAAoB,YAAY,OAAO,IAAI;AAClD,aAAO,oBAAoB,gBAAgB,MAAM;AAAA,IACnD;AAAA,EACF;AACA,MAAI,WAAW;AACf,SAAO,MAAM;AACX,QAAI,CAAC,UAAU;AACb,iBAAW;AACX,UAAI,EAAE,gBAAgB,GAAG;AACvB,oBAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,uBAAuB;AACrC,YAAU,uBAAuB,CAAC,CAAC;AACrC;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
package/dist/otp/input.js.map
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/otp/input.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\nimport {\n forwardRef,\n useEffect,\n useRef,\n type ChangeEvent,\n type ClipboardEvent,\n type KeyboardEvent,\n} from \"react\";\nimport { composeRefs, part, type PartProps } from \"../internal/slot\";\nimport { useControllableState } from \"../internal/use-controllable-state\";\nexport type OtpState = {\n value: string;\n slots: string[];\n getSlotProps: (index: number) => ReturnType<typeof slotAttributes>;\n};\nfunction slotAttributes(\n index: number,\n length: number,\n value: string,\n disabled: boolean,\n update: (index: number, text: string) => void,\n focus: (index: number) => void,\n) {\n return {\n type: \"text\",\n inputMode: \"numeric\" as const,\n autoComplete: index === 0 ? \"one-time-code\" : \"off\",\n \"aria-label\": `Digit ${index + 1} of ${length}`,\n \"data-otp-slot\": index,\n value: value[index] ?? \"\",\n disabled,\n onChange: (event: ChangeEvent<HTMLInputElement>) =>\n update(index, event.target.value),\n onPaste: (event: ClipboardEvent<HTMLInputElement>) => {\n event.preventDefault();\n update(index, event.clipboardData.getData(\"text\"));\n },\n onKeyDown: (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === \"Backspace\") {\n event.preventDefault();\n const target = value[index] ? index : Math.max(0, index - 1);\n update(target, \"\");\n focus(target);\n } else if (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\") {\n event.preventDefault();\n focus(index + (event.key === \"ArrowLeft\" ? -1 : 1));\n }\n },\n onFocus: (event: { currentTarget: HTMLInputElement }) =>\n event.currentTarget.select(),\n };\n}\nconst Input = forwardRef<\n HTMLElement,\n PartProps<OtpState> & {\n length?: number;\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n onComplete?: (value: string) => void;\n disabled?: boolean;\n }\n>(function Input(\n {\n length = 6,\n value,\n defaultValue = \"\",\n onValueChange,\n onComplete,\n disabled = false,\n ...props\n },\n ref,\n) {\n if (!Number.isInteger(length) || length < 1 || length > 12) {\n throw new RangeError(\"OTP length must be 1\u201312\");\n }\n const [current, setValue] = useControllableState({\n value,\n defaultValue,\n onChange: onValueChange,\n });\n const element = useRef<HTMLElement>(null);\n const previous = useRef(\"\");\n const callback = useRef(onComplete);\n callback.current = onComplete;\n useEffect(() => {\n if (\n current.length === length &&\n /^\\d+$/.test(current) &&\n previous.current !== current\n ) {\n callback.current?.(current);\n }\n previous.current = current;\n }, [current, length]);\n const focus = (index: number) =>\n element.current\n ?.querySelector<HTMLInputElement>(\n `[data-otp-slot=\"${Math.max(0, Math.min(length - 1, index))}\"]`,\n )\n ?.focus();\n const update = (index: number, text: string) => {\n if (disabled) {\n return;\n }\n const digits = text.replace(/\\D/g, \"\");\n const start = digits.length >= length ? 0 : index;\n const next = (\n current.slice(0, start) +\n digits +\n current.slice(start + Math.max(1, digits.length))\n ).slice(0, length);\n setValue(next);\n if (digits) {\n focus(start + digits.length);\n }\n };\n // An SMS code can be autofilled (or typed) before hydration: it sits in the\n // DOM with no change event. Adopt it once the component mounts.\n const adopt = useRef(update);\n adopt.current = update;\n useEffect(() => {\n const typed = Array.from(\n element.current?.querySelectorAll<HTMLInputElement>(\"[data-otp-slot]\") ??\n [],\n (input) => input.value,\n ).join(\"\");\n if (typed && typed !== current) {\n adopt.current(0, typed);\n }\n // Mount only: later edits arrive as change events.\n }, []);\n const getSlotProps = (index: number) =>\n slotAttributes(index, length, current, disabled, update, focus);\n const slots = Array.from({ length }, (_, index) => current[index] ?? \"\");\n return part(\n \"div\",\n {\n ...props,\n children:\n props.children ??\n slots.map((_, index) => <input key={index} {...getSlotProps(index)} />),\n },\n { value: current, slots, getSlotProps },\n { role: \"group\", \"data-disabled\": disabled ? \"\" : undefined },\n composeRefs(ref, element),\n );\n});\n/**\n * A numeric one-time-code input with paste, autofill and keyboard focus handling.\n * Otp.Input defaults to six digits and calls onComplete when a new complete code is entered.\n * You supply code verification.\n */\nexport const Otp = { Input };\n"],
|
|
5
4
|
"mappings": "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",
|
|
6
5
|
"names": ["Input"]
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/overlay/engine.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\n// The overlay engine: the only module that imports Base UI. It loads on first open (or an\n// idle prefetch) so dialog and drawer mechanics stay out of the initial bundle.\nimport { Dialog } from \"@base-ui/react/dialog\";\nimport { Drawer } from \"@base-ui/react/drawer\";\nimport {\n createContext,\n forwardRef,\n useContext,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { composeRefs, part, partId, type PartProps } from \"../internal/slot\";\n\nexport type Presentation = \"dialog\" | \"drawer\" | \"sheet\";\nexport type SwipeDirection = \"up\" | \"down\" | \"left\" | \"right\";\ntype ChangeDetails = {\n reason: string;\n event: Event;\n cancel: () => void;\n};\nexport type SurfaceProps = {\n open: boolean;\n presentation: Presentation;\n swipeDirection: SwipeDirection;\n /** Ids the trigger points at; caller-supplied ids win. */\n contentId: string;\n titleId?: string | undefined;\n trigger: { current: HTMLElement | null };\n onOpen: () => void;\n onClose: () => void;\n onEscape?: ((event: KeyboardEvent) => void) | undefined;\n onInteractOutside?: ((event: Event) => void) | undefined;\n forceMount?: true | undefined;\n container?: HTMLElement | null | undefined;\n state: unknown;\n contentProps: PartProps<never>;\n contentRef: Ref<HTMLElement>;\n};\ntype Parts = { Title: typeof Dialog.Title; Close: typeof Dialog.Close };\nconst PartsContext = createContext<Parts | null>(null);\nexport function useEngineParts() {\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}\n/** Route Base UI's open changes through the URL, letting callbacks cancel a dismissal. */\nfunction changeHandler(props: SurfaceProps) {\n return (open: boolean, details: ChangeDetails) => {\n if (open) {\n props.onOpen();\n return;\n }\n const callback =\n details.reason === \"escape-key\"\n ? (props.onEscape as ((event: Event) => void) | undefined)\n : details.reason === \"outside-press\" || details.reason === \"focus-out\"\n ? props.onInteractOutside\n : undefined;\n callback?.(details.event);\n if (details.event?.defaultPrevented && callback) {\n details.cancel();\n return;\n }\n props.onClose();\n };\n}\nfunction Popup({\n props,\n Component,\n children,\n}: {\n props: SurfaceProps;\n Component: typeof Dialog.Popup;\n children?: ReactNode;\n}) {\n const { contentProps, contentRef, presentation, state } = props;\n const { id: _ownId, ...rest } = contentProps as PartProps<never> & {\n id?: string;\n };\n const id = partId(contentProps as Parameters<typeof partId>[0]);\n return (\n <Component\n id={id ?? props.contentId}\n // Prefer an explicit [data-autofocus] target, else Base UI's first tabbable.\n initialFocus={() =>\n document\n .getElementById(id ?? props.contentId)\n ?.querySelector<HTMLElement>(\"[data-autofocus]\") ?? true\n }\n finalFocus={() => props.trigger.current ?? true}\n render={(baseProps) => {\n const { ref: baseRef, ...base } = baseProps as unknown as Record<\n string,\n unknown\n > & {\n ref?: Ref<HTMLElement>;\n };\n return part(\n \"div\",\n (children === undefined\n ? rest\n : { ...rest, children }) as PartProps<unknown>,\n state,\n {\n ...base,\n \"aria-modal\": true,\n \"data-presentation\": presentation,\n \"data-state\": props.open ? \"open\" : \"closed\",\n },\n composeRefs(baseRef, contentRef),\n );\n }}\n />\n );\n}\nexport function Surface(props: SurfaceProps & { children?: ReactNode }) {\n const { children, forceMount, container, presentation } = props;\n const portal = {\n ...(forceMount ? { keepMounted: true } : {}),\n ...(container !== undefined ? { container } : {}),\n };\n if (presentation === \"dialog\") {\n return (\n <Dialog.Root\n open={props.open}\n onOpenChange={changeHandler(props) as never}\n >\n <PartsContext.Provider\n value={{ Title: Dialog.Title, Close: Dialog.Close }}\n >\n <Dialog.Portal {...portal}>\n {/* Unstyled; templates style [data-zappie-backdrop] with data-state/data-presentation. */}\n <Dialog.Backdrop\n data-zappie-backdrop=\"\"\n data-presentation={presentation}\n data-state={props.open ? \"open\" : \"closed\"}\n />\n <Popup props={props} Component={Dialog.Popup}>\n {children}\n </Popup>\n </Dialog.Portal>\n </PartsContext.Provider>\n </Dialog.Root>\n );\n }\n return (\n <Drawer.Root\n open={props.open}\n onOpenChange={changeHandler(props) as never}\n swipeDirection={props.swipeDirection}\n >\n <PartsContext.Provider\n value={{\n Title: Drawer.Title as typeof Dialog.Title,\n Close: Drawer.Close as typeof Dialog.Close,\n }}\n >\n <Drawer.Portal {...portal}>\n <Drawer.Backdrop\n data-zappie-backdrop=\"\"\n data-presentation={presentation}\n data-state={props.open ? \"open\" : \"closed\"}\n />\n {/* Keeps focused fields visible above the on-screen keyboard in bottom sheets. */}\n <Drawer.VirtualKeyboardProvider>\n <Drawer.Viewport>\n <Popup\n props={props}\n Component={Drawer.Popup as typeof Dialog.Popup}\n >\n {children}\n </Popup>\n </Drawer.Viewport>\n </Drawer.VirtualKeyboardProvider>\n </Drawer.Portal>\n </PartsContext.Provider>\n </Drawer.Root>\n );\n}\n/** Title and Close rendered with whichever engine (dialog or drawer) owns the content. */\ntype EnginePartProps = PartProps<unknown> & {\n id?: string | undefined;\n state: unknown;\n};\nexport const EngineTitle = forwardRef<HTMLElement, EnginePartProps>(\n function EngineTitle({ state, ...props }, ref) {\n const { Title } = useEngineParts();\n const id = partId(props as Parameters<typeof partId>[0]);\n return (\n <Title\n {...(id ? { id } : {})}\n render={(base) => {\n const { ref: baseRef, ...rest } = base as unknown as Record<\n string,\n unknown\n > & {\n ref?: Ref<HTMLElement>;\n };\n return part(\"h2\", props, state, rest, composeRefs(baseRef, ref));\n }}\n />\n );\n },\n);\nexport const EngineClose = forwardRef<HTMLElement, EnginePartProps>(\n function EngineClose({ state, ...props }, ref) {\n const { Close } = useEngineParts();\n return (\n <Close\n render={(base) => {\n const { ref: baseRef, ...rest } = base as unknown as Record<\n string,\n unknown\n > & {\n ref?: Ref<HTMLElement>;\n };\n return part(\n \"button\",\n props,\n state,\n { type: \"button\", ...rest },\n composeRefs(baseRef, ref),\n );\n }}\n />\n );\n },\n);\n"],
|
|
5
4
|
"mappings": "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",
|
|
6
5
|
"names": ["EngineTitle", "EngineClose"]
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/overlay/history.ts"],
|
|
4
|
-
"sourcesContent": ["export type OverlayEntry = { param: string; id: string; stack: string[] };\nexport type OverlayQuery = Record<string, string | null | undefined>;\nexport type CloseOptions = {\n fallback?: OverlayQuery | undefined;\n omitParams?: string[] | undefined;\n};\nexport const LOCATION_EVENT = \"zappie:location\";\nexport function getCurrentOverlayEntry(): OverlayEntry | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n const entry = window.history.state?.__zappieOverlay;\n if (\n !entry ||\n typeof entry.param !== \"string\" ||\n typeof entry.id !== \"string\"\n ) {\n return null;\n }\n return {\n param: entry.param,\n id: entry.id,\n stack: Array.isArray(entry.stack)\n ? entry.stack.filter((key: unknown) => typeof key === \"string\")\n : [],\n };\n}\nexport function subscribeLocation(listener: () => void) {\n window.addEventListener(\"popstate\", listener);\n window.addEventListener(LOCATION_EVENT, listener);\n return () => {\n window.removeEventListener(\"popstate\", listener);\n window.removeEventListener(LOCATION_EVENT, listener);\n };\n}\nexport function locationSnapshot() {\n return window.location.search;\n}\nfunction applyQuery(url: URL, query: OverlayQuery) {\n for (const [key, value] of Object.entries(query)) {\n if (value == null) {\n url.searchParams.delete(key);\n } else {\n url.searchParams.set(key, value);\n }\n }\n}\nfunction write(\n type: \"pushState\" | \"replaceState\",\n url: URL,\n overlay: OverlayEntry | null,\n) {\n const state = { ...window.history.state };\n delete state.__zappieOverlay;\n if (overlay) {\n state.__zappieOverlay = overlay;\n }\n state.__zappieBase ??= window.location.pathname;\n if (url.pathname !== window.location.pathname) {\n delete state.__NA;\n delete state.__PRIVATE_NEXTJS_INTERNALS_TREE;\n }\n // Bypass Next's wrapper: shallow overlay writes must not remount the page.\n const prototype = Object.getPrototypeOf(window.history) as History;\n prototype[type].call(window.history, state, \"\", url.href);\n window.dispatchEvent(new Event(LOCATION_EVENT));\n}\nexport function openOverlay(\n param: string,\n id: string,\n query: OverlayQuery = {},\n) {\n const url = new URL(window.location.href);\n applyQuery(url, query);\n url.searchParams.set(param, id);\n if (url.href === window.location.href) {\n return;\n }\n const previous = getCurrentOverlayEntry();\n write(\"pushState\", url, {\n param,\n id,\n stack: [...(previous?.stack ?? []), `${param}:${id}`],\n });\n}\nexport function closeOverlay(\n param: string,\n id?: string,\n options: CloseOptions = {},\n) {\n const url = new URL(window.location.href);\n if (!url.searchParams.has(param)) {\n return;\n }\n const entry = getCurrentOverlayEntry();\n const stack = entry?.stack ?? [];\n const index = id\n ? stack.lastIndexOf(`${param}:${id}`)\n : stack.reduce(\n (found, key, index) => (key.startsWith(`${param}:`) ? index : found),\n -1,\n );\n if (index >= 0) {\n window.history.go(-(stack.length - index));\n return;\n }\n applyQuery(url, options.fallback ?? {});\n if (!options.fallback || !(param in options.fallback)) {\n url.searchParams.delete(param);\n }\n for (const omitted of options.omitParams ?? []) {\n url.searchParams.delete(omitted);\n }\n write(\n \"replaceState\",\n url,\n entry && url.searchParams.get(entry.param) === entry.id ? entry : null,\n );\n}\n"],
|
|
5
4
|
"mappings": "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",
|
|
6
5
|
"names": ["index"]
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
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 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
4
|
"mappings": ";;;AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,cAAc,iBAAiB;AACxC,SAAS,aAAa,MAAM,cAA8B;AAE1D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AA8LH,cA4MA,YA5MA;AA3LJ,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,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;AAaA,IAAM,aAAa,CAAC,iBAClB,iBAAiB,WAAW,WAAW;AAEzC,IAAM,SAAS,oBAAI,IAAgB;AAEnC,IAAM,iBAAiB,cAA4C,IAAI;AAEvE,IAAM,cAAc;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,eAAe,CAAC,SACpB,OAAO,MAAM,kBAAkB,aAAa,KAAK,cAAc,IAAI,CAAC;AAEtE,SAAS,WAAW,MAA8C;AAChE,QAAM,QAAoB,oBAAI,IAAI;AAClC,aAAW,aAAa,aAAa,IAAI,GAAG;AAC1C,QAAI,mBAAmB,WAAW;AAChC,YAAM,IAAK,UAA2B,eAAe,UAAU,SAAS;AAAA,IAC1E;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,OAAO,MAAkC,OAAmB;AACnE,aAAW,aAAa,aAAa,IAAI,GAAG;AAC1C,UAAM,QACJ,mBAAmB,YACf,MAAM,IAAK,UAA2B,aAAa,IACnD;AACN,QAAI,UAAU,UAAa,UAAU,MAAM;AACzC,gBAAU,YAAY;AAAA,IACxB;AAAA,EACF;AACF;AAsBA,SAAS,MAAM;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAe;AACb,QAAM,WAAW,OAAuB,IAAI;AAC5C,QAAM,QAAQ,OAA2B,IAAI;AAC7C,QAAM,SAAS,OAAO,EAAE,UAAU,mBAAmB,UAAU,IAAI,CAAC;AACpE,SAAO,UAAU,EAAE,UAAU,mBAAmB,UAAU,IAAI;AAC9D,kBAAgB,MAAM;AACpB,UAAM,UAAU,CACd,UACA,UACG;AACH,iBAAW,KAAc;AACzB,UAAI,EAAE,YAAY,MAAM,mBAAmB;AACzC,eAAO,QAAQ,IAAI,MAAM;AAAA,MAC3B;AAAA,IACF;AACA,UAAM,OAAO,CAAC,UAAiB;AAC7B,YAAM,SACJ,MAAM,kBAAkB,QAAQ,MAAM,SAAS,SAAS,MAAM,MAAM;AACtE,UACE,MAAM,SAAS,aACd,MAAwB,QAAQ,UACjC;AACA,gBAAQ,OAAO,QAAQ,UAAU,KAAK;AAAA,MACxC,WAAW,MAAM,SAAS,iBAAiB,CAAC,QAAQ;AAClD,gBAAQ,OAAO,QAAQ,mBAAmB,KAAK;AAAA,MACjD;AACA,UAAI,MAAM,YAAY;AACpB,cAAM,eAAe;AAAA,MACvB;AACA,YAAM,yBAAyB;AAAA,IACjC;AACA,eAAW,QAAQ,aAAa;AAC9B,aAAO,iBAAiB,MAAM,MAAM,EAAE,SAAS,MAAM,SAAS,MAAM,CAAC;AAAA,IACvE;AAEA,QAAI,OAAO;AACX,QAAI,QAAQ,sBAAsB,MAAM;AACtC,cAAQ,sBAAsB,MAAM;AAClC,YAAI,MAAM;AACR,iBAAO,QAAQ,SAAS;AAAA,YACtB,WAAW,SAAS,OAAO;AAAA,YAC3B,WAAW,MAAM,OAAO;AAAA,UAC1B,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,WAAO,MAAM;AACX,aAAO;AACP,2BAAqB,KAAK;AAC1B,iBAAW,QAAQ,aAAa;AAC9B,eAAO,oBAAoB,MAAM,MAAM,EAAE,SAAS,KAAK,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AACL,QAAM,EAAE,cAAc,eAAe,IAAI;AACzC,QAAM,SAAS,iBAAiB;AAChC,QAAM,EAAE,IAAI,QAAQ,GAAG,KAAK,IAAI;AAGhC,QAAM,KAAK,OAAO,YAA4C;AAC9D,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,GAAI,SAAS,EAAE,wBAAwB,eAAe,IAAI,CAAC;AAAA,MAC3D,IAAI,MAAM,IAAI;AAAA,MACd,MAAM;AAAA,MACN,UAAU;AAAA,MACV,OAAO,SACH;AAAA,QACE,6BAA6B;AAAA,QAC7B,6BAA6B;AAAA,QAC7B,2BAA2B;AAAA,QAC3B,oBAAoB;AAAA,QACpB,8BAA8B;AAAA,QAC9B,2BAA2B;AAAA,MAC7B,IACA,EAAE,oBAAoB,EAAE;AAAA,MAC5B,cAAc;AAAA,MACd,qBAAqB;AAAA,MACrB,cAAc;AAAA;AAAA,MAEd,eAAe;AAAA,IACjB;AAAA,IACA,YAAY,YAAY,CAAC,SAA6B;AACpD,YAAM,UAAU;AAAA,IAClB,CAAC;AAAA,EACH;AACA,SAAO;AAAA,IACL,qBAAC,SAAI,6BAA0B,IAC7B;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,eAAY;AAAA,UACZ,OAAO,EAAE,UAAU,SAAS,OAAO,GAAG,YAAY,OAAO;AAAA;AAAA,MAC3D;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,aAAU;AAAA,UACV,MAAK;AAAA,UACL,OACE;AAAA,YACE,YAAY;AAAA,YACZ,GAAI,SACA,EAAE,2BAA2B,GAAG,2BAA2B,EAAE,IAC7D,CAAC;AAAA,UACP;AAAA,UAEF,wBAAqB;AAAA,UACrB,qBAAmB;AAAA,UACnB,cAAW;AAAA,UACX,eAAY;AAAA;AAAA,MACd;AAAA,MACC,SACC;AAAA,QAAC;AAAA;AAAA,UACC,aAAU;AAAA,UACV,MAAK;AAAA,UACL,OAAO,EAAE,2BAA2B,MAAM;AAAA,UAEzC;AAAA;AAAA,MACH,IAEA;AAAA,OAEJ;AAAA,IACA,aAAa,SAAS;AAAA,EACxB;AACF;AAGA,IAAM,UAAU,WAA6C,SAASC,SACpE,EAAE,UAAU,mBAAmB,YAAY,WAAW,GAAG,MAAM,GAC/D,KACA;AACA,QAAM,MAAM,gBAAgB;AAC5B,QAAM,SAAS,WAAW,cAAc;AACxC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,YAAY;AAEjD,QAAM,SAAS,UAAU;AAGzB,QAAM,aAAa,MACjB,CAAC,cACD,IAAI,UACJ,CAAC,UACD,CAAC,OAAO,IAAI,WAAW,IAAI,YAAY,CAAC,IACpC,UACA;AAEN,QAAM,CAAC,OAAO,QAAQ,IAAI;AAAA,IAAgB,MACxC,WAAW,IAAI,UAAU,cAAc,WAAW,IAAI;AAAA,EACxD;AACA,MAAI,UAAU,YAAY,WAAW,IAAI,UAAU,aAAa;AAC9D,aAAS,WAAW,CAAC;AAAA,EACvB;AACA,MAAI,UAAU,WAAW,CAAC,IAAI,QAAQ;AACpC,aAAS,QAAQ;AAAA,EACnB;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;AAER,oBAAU,MAAM;AAChB,mBAAS,CAAC,YAAa,YAAY,WAAW,WAAW,OAAQ;AAAA,QACnE;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,QAAM,OAAO,WAAW,IAAI,YAAY;AACxC,YAAU,MAAM;AACd,QAAI,UAAU,YAAY,IAAI,QAAQ;AACpC,aAAO,IAAI,IAAI;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,OAAO,IAAI,QAAQ,IAAI,CAAC;AAC5B,QAAM,QAAQ,OAA2B,IAAI;AAC7C,QAAM,YAAY;AAAA,IAChB,MACE,YAAY,KAAK,CAAC,SAA6B;AAC7C,YAAM,UAAU;AAAA,IAClB,CAAC;AAAA,IACH,CAAC,GAAG;AAAA,EACN;AACA,QAAM,SAAS,YAAY,CAAC,CAAC,UAAU,KAAK,MAAgC;AAC1E,cAAU,MAAM,SAAS,QAAQ,CAAC;AAClC,UAAM,OAAO,MAAM;AACnB,WAAO,MAAM,KAAK;AAClB;AAAA,MACE,MACI,QAAQ,uBAAuB,GAC/B,cAAc,wBAAwB;AAAA,MAC1C;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AACL,QAAM,WAAW,QAAQ,OAAO,EAAE,UAAU,UAAU,SAAS,IAAI,CAAC,KAAK,CAAC;AAC1E,MAAI,CAAC,UAAU,UAAU,YAAa,UAAU,CAAC,OAAO,UAAW;AACjE,WAAO;AAAA,EACT;AACA,MAAI,UAAU,SAAS;AACrB,WACE,oBAAC,eAAe,UAAf,EAAwB,OAAO,UAC9B,8BAAC,aAAa,UAAb,EAAsB,OAAO,YAC5B;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,cAAc;AAAA,QACd,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU;AAAA;AAAA,IACZ,GACF,GACF;AAAA,EAEJ;AACA,SACE,oBAAC,eAAe,UAAf,EAAwB,OAAO,UAC9B,8BAAC,aAAa,UAAb,EAAsB,OAAO,QAC5B;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,GACF;AAEJ,CAAC;AAMD,IAAM,eAAe,cAA4B,IAAI;AACrD,IAAM,aAAoB;AAAA,EACxB,aAAa,WAAwC,SAAS,WAC5D,EAAE,OAAO,GAAG,MAAM,GAClB,KACA;AACA,WAAO,KAAK,MAAM,OAAO,OAAO,CAAC,GAAG,GAAG;AAAA,EACzC,CAAC;AAAA,EACD,aAAa,WAAwC,SAAS,WAC5D,EAAE,OAAO,GAAG,MAAM,GAClB,KACA;AACA,WAAO,KAAK,UAAU,OAAO,OAAO,EAAE,MAAM,SAAS,GAAG,GAAG;AAAA,EAC7D,CAAC;AACH;AACA,SAAS,YAAY;AACnB,QAAM,QAAQ,WAAW,YAAY;AACrC,MAAI,CAAC,OAAO;AACV,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;",
|
|
6
5
|
"names": ["Trigger", "Content", "Title", "Close"]
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/payment/methods.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\nimport type { PaymentMethodKind } from \"@zappie/sdk\";\nimport {\n createContext,\n forwardRef,\n useContext,\n type KeyboardEvent,\n} from \"react\";\nimport { usePartId } from \"../internal/ids\";\nimport { part, type PartProps } from \"../internal/slot\";\nimport { useControllableState } from \"../internal/use-controllable-state\";\n/**\n * A payment-method choice with optional disabled state and an explanatory reason.\n */\nexport type PaymentMethod = {\n value: PaymentMethodKind;\n disabled?: boolean;\n reason?: string;\n};\ntype PaymentState = {\n methods: PaymentMethod[];\n value: PaymentMethodKind | null;\n setValue: (value: PaymentMethodKind) => void;\n};\nconst Context = createContext<PaymentState | null>(null);\nfunction Root({\n methods,\n value,\n defaultValue = null,\n onValueChange,\n ...props\n}: PartProps<PaymentState> & {\n methods: (PaymentMethod | PaymentMethodKind)[];\n value?: PaymentMethodKind | null;\n defaultValue?: PaymentMethodKind | null;\n onValueChange?: (value: PaymentMethodKind | null) => void;\n}) {\n const [selected, set] = useControllableState({\n value,\n defaultValue,\n onChange: onValueChange,\n });\n const normalized = methods.map((method) =>\n typeof method === \"string\" ? { value: method } : method,\n );\n const setValue = (next: PaymentMethodKind) => {\n if (\n normalized.some((method) => method.value === next && !method.disabled)\n ) {\n set(next);\n }\n };\n const state = { methods: normalized, value: selected, setValue };\n return (\n <Context.Provider value={state}>\n {part(\"div\", props, state, {\n role: \"radiogroup\",\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => {\n if (\n ![\n \"ArrowRight\",\n \"ArrowDown\",\n \"ArrowLeft\",\n \"ArrowUp\",\n \"Home\",\n \"End\",\n ].includes(event.key)\n ) {\n return;\n }\n const buttons = Array.from(\n event.currentTarget.querySelectorAll<HTMLElement>(\n '[role=\"radio\"]:not([aria-disabled=\"true\"])',\n ),\n );\n if (!buttons.length) {\n return;\n }\n event.preventDefault();\n const current = buttons.indexOf(\n document.activeElement as HTMLElement,\n );\n const index =\n event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? buttons.length - 1\n : (current +\n ([\"ArrowLeft\", \"ArrowUp\"].includes(event.key) ? -1 : 1) +\n buttons.length) %\n buttons.length;\n buttons[index]?.focus();\n buttons[index]?.click();\n },\n })}\n </Context.Provider>\n );\n}\nconst Item = forwardRef<\n HTMLElement,\n PartProps<PaymentMethod & { selected: boolean }> & {\n value: PaymentMethodKind;\n }\n>(function Item({ value, ...props }, ref) {\n const ctx = useContext(Context);\n const id = usePartId();\n if (!ctx) {\n throw new Error(\"PaymentMethods.Item requires PaymentMethods.Root\");\n }\n const method = ctx.methods.find((method) => method.value === value);\n if (!method) {\n return null;\n }\n const selected = ctx.value === value;\n const first = ctx.methods.find((method) => !method.disabled)?.value;\n return part(\n \"button\",\n props,\n { ...method, selected },\n {\n id,\n type: \"button\",\n role: \"radio\",\n \"aria-checked\": selected,\n \"aria-disabled\": method.disabled || undefined,\n \"aria-description\": method.reason,\n disabled: method.disabled,\n tabIndex: selected || (!ctx.value && first === value) ? 0 : -1,\n \"data-disabled\": method.disabled ? \"\" : undefined,\n \"data-selected\": selected ? \"\" : undefined,\n \"data-state\": selected ? \"checked\" : \"unchecked\",\n onClick: () => ctx.setValue(value),\n },\n ref,\n );\n});\n/**\n * Composable payment-method radio controls with keyboard navigation.\n * Root manages the selection. Item renders each method and prevents selection when disabled.\n */\nexport const PaymentMethods = { Root, Item };\n"],
|
|
5
4
|
"mappings": "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",
|
|
6
5
|
"names": ["Item", "method"]
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/rich-text/banners.ts"],
|
|
4
|
-
"sourcesContent": ["// Multiple menu info banners are stored as one string joined by this delimiter\n// (the dashboard editor joins on it; here we split it back out for display).\n/**\n * The separator used to store multiple menu info banners in a single string.\n */\nexport const BANNER_DELIMITER = \"<::>\";\n\n// The delimiter looks like an HTML tag, so once it lands inside the dashboard's\n// rich-text editor as content it gets serialised as escaped text \u2014 `<::>`,\n// usually on its own `<p>` line. Splitting on the literal `<::>` then finds\n// nothing and every banner collapses into one. Convert the escaped form (and its\n// `<p>` wrapper) back to the real delimiter before splitting.\nconst normalizeBannerDelimiters = (html: string): string =>\n html\n .replace(/<p>\\s*(?:<::>|<::>)\\s*<\\/p>/gi, BANNER_DELIMITER)\n .replaceAll(\"<::>\", BANNER_DELIMITER);\n\n/** Split a stored info-banner value into individual banner HTML fragments. */\nexport const splitInfoBanner = (infoBanner?: string | null): string[] =>\n normalizeBannerDelimiters(infoBanner ?? \"\").split(BANNER_DELIMITER);\n"],
|
|
5
4
|
"mappings": ";AAKO,IAAM,mBAAmB;AAOhC,IAAM,4BAA4B,CAAC,SACjC,KACG,QAAQ,uCAAuC,gBAAgB,EAC/D,WAAW,cAAc,gBAAgB;AAGvC,IAAM,kBAAkB,CAAC,eAC9B,0BAA0B,cAAc,EAAE,EAAE,MAAM,gBAAgB;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/rich-text/browser.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ParsedRichTextNode } from \"./core\";\n\nconst TEXT_NODE = 3;\nconst COMMENT_NODE = 8;\n\nconst convertNode = (node: Node): ParsedRichTextNode => {\n if (node.nodeType === TEXT_NODE) {\n return { type: \"text\", data: node.nodeValue || \"\" };\n }\n\n if (node.nodeType === COMMENT_NODE) {\n return { type: \"comment\" };\n }\n\n return {\n type: \"tag\",\n name: node.nodeName.toLowerCase(),\n children: Array.from(node.childNodes).map(convertNode),\n };\n};\n\n/**\n * Parse HTML with the browser DOMParser and convert body children to the shared rich-text node format.\n * Requires a DOM environment. Pair with createSanitizeRichText to remove unsupported content.\n */\nexport const parseRichText = (html: string): ParsedRichTextNode[] => {\n const document = new DOMParser().parseFromString(html, \"text/html\");\n return Array.from(document.body.childNodes).map(convertNode);\n};\n"],
|
|
5
4
|
"mappings": ";AAEA,IAAM,YAAY;AAClB,IAAM,eAAe;AAErB,IAAM,cAAc,CAAC,SAAmC;AACtD,MAAI,KAAK,aAAa,WAAW;AAC/B,WAAO,EAAE,MAAM,QAAQ,MAAM,KAAK,aAAa,GAAG;AAAA,EACpD;AAEA,MAAI,KAAK,aAAa,cAAc;AAClC,WAAO,EAAE,MAAM,UAAU;AAAA,EAC3B;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,KAAK,SAAS,YAAY;AAAA,IAChC,UAAU,MAAM,KAAK,KAAK,UAAU,EAAE,IAAI,WAAW;AAAA,EACvD;AACF;AAMO,IAAM,gBAAgB,CAAC,SAAuC;AACnE,QAAM,WAAW,IAAI,UAAU,EAAE,gBAAgB,MAAM,WAAW;AAClE,SAAO,MAAM,KAAK,SAAS,KAAK,UAAU,EAAE,IAAI,WAAW;AAC7D;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/rich-text/core.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * A parser-neutral rich-text node containing its type, optional tag name, text and children.\n */\nexport type ParsedRichTextNode = {\n type: string;\n name?: string;\n data?: string;\n children?: ParsedRichTextNode[];\n};\n\n/**\n * A parser adapter that converts HTML into the node tree consumed by the rich-text sanitizer.\n */\nexport type ParseRichText = (html: string) => ParsedRichTextNode[];\n\nconst ALLOWED_TAGS = new Set([\n \"b\",\n \"blockquote\",\n \"br\",\n \"code\",\n \"del\",\n \"em\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"hr\",\n \"i\",\n \"li\",\n \"ol\",\n \"p\",\n \"pre\",\n \"s\",\n \"span\",\n \"strike\",\n \"strong\",\n \"sub\",\n \"sup\",\n \"u\",\n \"ul\",\n]);\n\nconst DROP_CONTENT_TAGS = new Set([\n \"audio\",\n \"canvas\",\n \"embed\",\n \"iframe\",\n \"math\",\n \"noscript\",\n \"object\",\n \"script\",\n \"style\",\n \"svg\",\n \"template\",\n \"video\",\n]);\n\nconst VOID_TAGS = new Set([\"br\", \"hr\"]);\n\nconst escapeText = (value: string): string =>\n value\n .replaceAll(\"\\r\\n\", \"\\n\")\n .replaceAll(\"\\r\", \"\\n\")\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"\\u00a0\", \" \")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\");\n\nconst serializeNode = (node: ParsedRichTextNode): string => {\n if (node.type === \"text\") {\n return escapeText(node.data || \"\");\n }\n\n if (node.type === \"comment\" || node.type === \"directive\") {\n return \"\";\n }\n\n const tagName = node.name?.toLowerCase();\n if (tagName && DROP_CONTENT_TAGS.has(tagName)) {\n return \"\";\n }\n\n const children = (node.children || []).map(serializeNode).join(\"\");\n if (!tagName || !ALLOWED_TAGS.has(tagName)) {\n return children;\n }\n\n if (VOID_TAGS.has(tagName)) {\n return `<${tagName}>`;\n }\n\n return `<${tagName}>${children}</${tagName}>`;\n};\n\n/**\n * Create a sanitizer using your HTML parser adapter.\n * Keeps supported formatting tags without attributes, escapes text and removes unsafe element content.\n *\n * @param parseRichText - A parser that returns the shared rich-text node format.\n * @returns A sanitizer that accepts HTML and returns sanitized HTML, or an empty string for empty input.\n */\nexport const createSanitizeRichText =\n (parseRichText: ParseRichText) =>\n (html?: string | null): string => {\n if (!html) {\n return \"\";\n }\n\n return parseRichText(html).map(serializeNode).join(\"\");\n };\n"],
|
|
5
4
|
"mappings": ";AAeA,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,oBAAoB,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,YAAY,oBAAI,IAAI,CAAC,MAAM,IAAI,CAAC;AAEtC,IAAM,aAAa,CAAC,UAClB,MACG,WAAW,QAAQ,IAAI,EACvB,WAAW,MAAM,IAAI,EACrB,WAAW,KAAK,OAAO,EACvB,WAAW,QAAU,QAAQ,EAC7B,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM;AAE3B,IAAM,gBAAgB,CAAC,SAAqC;AAC1D,MAAI,KAAK,SAAS,QAAQ;AACxB,WAAO,WAAW,KAAK,QAAQ,EAAE;AAAA,EACnC;AAEA,MAAI,KAAK,SAAS,aAAa,KAAK,SAAS,aAAa;AACxD,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,KAAK,MAAM,YAAY;AACvC,MAAI,WAAW,kBAAkB,IAAI,OAAO,GAAG;AAC7C,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,KAAK,YAAY,CAAC,GAAG,IAAI,aAAa,EAAE,KAAK,EAAE;AACjE,MAAI,CAAC,WAAW,CAAC,aAAa,IAAI,OAAO,GAAG;AAC1C,WAAO;AAAA,EACT;AAEA,MAAI,UAAU,IAAI,OAAO,GAAG;AAC1B,WAAO,IAAI,OAAO;AAAA,EACpB;AAEA,SAAO,IAAI,OAAO,IAAI,QAAQ,KAAK,OAAO;AAC5C;AASO,IAAM,yBACX,CAAC,kBACD,CAAC,SAAiC;AAChC,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,SAAO,cAAc,IAAI,EAAE,IAAI,aAAa,EAAE,KAAK,EAAE;AACvD;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/search/engine.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Category, Item, Menu } from \"@zappie/sdk\";\n/**\n * A scored item or category returned by menu search, ordered by descending score.\n */\nexport type SearchResult =\n | { type: \"item\"; item: Item; score: number }\n | { type: \"category\"; category: Category; score: number };\nexport function normalizeSearch(text: string) {\n return text\n .normalize(\"NFD\")\n .replace(/\\p{M}/gu, \"\")\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean);\n}\nfunction near(a: string, b: string) {\n if (Math.abs(a.length - b.length) > 1) {\n return false;\n }\n let i = 0;\n let j = 0;\n let edits = 0;\n while (i < a.length && j < b.length) {\n if (a[i] === b[j]) {\n i++;\n j++;\n continue;\n }\n if (++edits > 1) {\n return false;\n }\n if (a.length === b.length) {\n if (a[i] === b[j + 1] && a[i + 1] === b[j]) {\n i += 2;\n j += 2;\n } else {\n i++;\n j++;\n }\n } else if (a.length > b.length) {\n i++;\n } else {\n j++;\n }\n }\n return edits + (a.length - i) + (b.length - j) <= 1;\n}\n/**\n * Index item names, tags and descriptions plus category names and descriptions for local search.\n * Matches case- and accent-insensitive prefixes and allows a single edit for query tokens of at least four characters.\n *\n * @param menu - The menu snapshot you want to search.\n * @returns A search function that requires every query token to match and sorts results by descending score.\n */\nexport function createSearchIndex(menu: Menu) {\n const entries = [\n ...Object.values(menu.items).map((item) => ({\n result: { type: \"item\" as const, item },\n fields: [\n normalizeSearch(item.name),\n normalizeSearch(item.tags.join(\" \")),\n normalizeSearch(item.description ?? \"\"),\n ],\n })),\n ...menu.categories.map((category) => ({\n result: { type: \"category\" as const, category },\n fields: [\n normalizeSearch(category.name),\n [],\n normalizeSearch(category.description ?? \"\"),\n ],\n })),\n ];\n return (query: string): SearchResult[] => {\n const tokens = normalizeSearch(query);\n if (!tokens.length) {\n return [];\n }\n const results: SearchResult[] = [];\n for (const entry of entries) {\n let score = 0;\n for (const token of tokens) {\n let best = 0;\n for (let field = 0; field < entry.fields.length; field++) {\n const weight = [30, 20, 10][field]!;\n for (const word of entry.fields[field]!) {\n if (word.startsWith(token)) {\n best = Math.max(best, weight + (word === token ? 3 : 2));\n } else if (token.length >= 4 && near(token, word)) {\n best = Math.max(best, weight);\n }\n }\n }\n if (!best) {\n score = 0;\n break;\n }\n score += best;\n }\n if (score) {\n results.push({ ...entry.result, score });\n }\n }\n return results.sort((a, b) => b.score - a.score);\n };\n}\n"],
|
|
5
4
|
"mappings": ";AAOO,SAAS,gBAAgB,MAAc;AAC5C,SAAO,KACJ,UAAU,KAAK,EACf,QAAQ,WAAW,EAAE,EACrB,YAAY,EACZ,MAAM,iBAAiB,EACvB,OAAO,OAAO;AACnB;AACA,SAAS,KAAK,GAAW,GAAW;AAClC,MAAI,KAAK,IAAI,EAAE,SAAS,EAAE,MAAM,IAAI,GAAG;AACrC,WAAO;AAAA,EACT;AACA,MAAI,IAAI;AACR,MAAI,IAAI;AACR,MAAI,QAAQ;AACZ,SAAO,IAAI,EAAE,UAAU,IAAI,EAAE,QAAQ;AACnC,QAAI,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG;AACjB;AACA;AACA;AAAA,IACF;AACA,QAAI,EAAE,QAAQ,GAAG;AACf,aAAO;AAAA,IACT;AACA,QAAI,EAAE,WAAW,EAAE,QAAQ;AACzB,UAAI,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG;AAC1C,aAAK;AACL,aAAK;AAAA,MACP,OAAO;AACL;AACA;AAAA,MACF;AAAA,IACF,WAAW,EAAE,SAAS,EAAE,QAAQ;AAC9B;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF;AACA,SAAO,SAAS,EAAE,SAAS,MAAM,EAAE,SAAS,MAAM;AACpD;AAQO,SAAS,kBAAkB,MAAY;AAC5C,QAAM,UAAU;AAAA,IACd,GAAG,OAAO,OAAO,KAAK,KAAK,EAAE,IAAI,CAAC,UAAU;AAAA,MAC1C,QAAQ,EAAE,MAAM,QAAiB,KAAK;AAAA,MACtC,QAAQ;AAAA,QACN,gBAAgB,KAAK,IAAI;AAAA,QACzB,gBAAgB,KAAK,KAAK,KAAK,GAAG,CAAC;AAAA,QACnC,gBAAgB,KAAK,eAAe,EAAE;AAAA,MACxC;AAAA,IACF,EAAE;AAAA,IACF,GAAG,KAAK,WAAW,IAAI,CAAC,cAAc;AAAA,MACpC,QAAQ,EAAE,MAAM,YAAqB,SAAS;AAAA,MAC9C,QAAQ;AAAA,QACN,gBAAgB,SAAS,IAAI;AAAA,QAC7B,CAAC;AAAA,QACD,gBAAgB,SAAS,eAAe,EAAE;AAAA,MAC5C;AAAA,IACF,EAAE;AAAA,EACJ;AACA,SAAO,CAAC,UAAkC;AACxC,UAAM,SAAS,gBAAgB,KAAK;AACpC,QAAI,CAAC,OAAO,QAAQ;AAClB,aAAO,CAAC;AAAA,IACV;AACA,UAAM,UAA0B,CAAC;AACjC,eAAW,SAAS,SAAS;AAC3B,UAAI,QAAQ;AACZ,iBAAW,SAAS,QAAQ;AAC1B,YAAI,OAAO;AACX,iBAAS,QAAQ,GAAG,QAAQ,MAAM,OAAO,QAAQ,SAAS;AACxD,gBAAM,SAAS,CAAC,IAAI,IAAI,EAAE,EAAE,KAAK;AACjC,qBAAW,QAAQ,MAAM,OAAO,KAAK,GAAI;AACvC,gBAAI,KAAK,WAAW,KAAK,GAAG;AAC1B,qBAAO,KAAK,IAAI,MAAM,UAAU,SAAS,QAAQ,IAAI,EAAE;AAAA,YACzD,WAAW,MAAM,UAAU,KAAK,KAAK,OAAO,IAAI,GAAG;AACjD,qBAAO,KAAK,IAAI,MAAM,MAAM;AAAA,YAC9B;AAAA,UACF;AAAA,QACF;AACA,YAAI,CAAC,MAAM;AACT,kBAAQ;AACR;AAAA,QACF;AACA,iBAAS;AAAA,MACX;AACA,UAAI,OAAO;AACT,gBAAQ,KAAK,EAAE,GAAG,MAAM,QAAQ,MAAM,CAAC;AAAA,MACzC;AAAA,IACF;AACA,WAAO,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAAA,EACjD;AACF;",
|
|
6
5
|
"names": []
|
|
7
6
|
}
|
package/dist/search/index.js.map
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/search/index.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\nimport { useCart, useMenu, useZappie } from \"@zappie/sdk/react\";\nimport {\n createContext,\n forwardRef,\n useContext,\n useEffect,\n useMemo,\n type ChangeEvent,\n} from \"react\";\nimport {\n countAnnouncement,\n useCombobox,\n withLiveRegion,\n} from \"../internal/combobox\";\nimport { composeRefs, part, type PartProps } from \"../internal/slot\";\nimport { useControllableState } from \"../internal/use-controllable-state\";\nimport { createSearchIndex, type SearchResult } from \"./engine\";\n/**\n * Search query, results and combobox navigation exposed to search parts and render functions.\n */\nexport type SearchState = ReturnType<typeof useCombobox<SearchResult>> & {\n query: string;\n setQuery: (query: string) => void;\n results: SearchResult[];\n setComposing: (value: boolean) => void;\n error?: string;\n retry?: () => void;\n};\nconst Context = createContext<SearchState | null>(null);\nfunction useSearch() {\n const ctx = useContext(Context);\n if (!ctx) {\n throw new Error(\"Search parts require Search.Root\");\n }\n return ctx;\n}\n/**\n * Externally supplied search results and optional loading, error, retry and composition controls.\n */\nexport type SearchSource = {\n results: SearchResult[];\n isLoading?: boolean;\n isStale?: boolean;\n error?: string;\n retry?: () => void;\n onCompositionChange?: (composing: boolean) => void;\n};\ntype RootProps = Omit<PartProps<SearchState>, \"onSelect\"> & {\n announcement?: (count: number) => string;\n query?: string;\n defaultQuery?: string;\n onQueryChange?: (query: string) => void;\n onSelect?: (result: SearchResult) => void;\n source?: SearchSource;\n};\nfunction Root(props: RootProps) {\n if (props.source) {\n return <ControlledRoot {...props} source={props.source} />;\n }\n return <UncontrolledRoot {...props} />;\n}\nfunction UncontrolledRoot(props: RootProps) {\n const cart = useCart();\n const menu = useMenu(cart.branchId ?? undefined);\n const [query, setQuery] = useControllableState({\n value: props.query,\n defaultValue: props.defaultQuery ?? \"\",\n onChange: props.onQueryChange,\n });\n const index = useMemo(\n () => (menu.data ? createSearchIndex(menu.data) : () => []),\n [menu.data],\n );\n const results = useMemo(() => index(query), [index, query]);\n return (\n <ControlledRoot\n {...props}\n query={query}\n onQueryChange={setQuery}\n source={{ results, isLoading: menu.isLoading, isStale: menu.isStale }}\n />\n );\n}\nfunction ControlledRoot({\n query,\n defaultQuery = \"\",\n onQueryChange,\n onSelect = () => {},\n announcement = countAnnouncement,\n source,\n ...props\n}: RootProps & { source: SearchSource }) {\n const client = useZappie();\n const [value, setValue] = useControllableState({\n value: query,\n defaultValue: defaultQuery,\n onChange: onQueryChange,\n });\n const results = source.results;\n const combo = useCombobox(results, onSelect);\n const setQuery = (next: string) => {\n setValue(next);\n combo.setActiveIndex(-1);\n combo.setOpen(true);\n };\n useEffect(() => {\n if (!value.trim()) {\n return;\n }\n const timer = setTimeout(\n () =>\n client._internal.emit(\"search\", {\n query: value,\n resultCount: results.length,\n }),\n 600,\n );\n return () => clearTimeout(timer);\n }, [client, value, results.length]);\n const state = {\n ...combo,\n query: value,\n setQuery,\n results,\n setComposing: (value: boolean) => source.onCompositionChange?.(value),\n ...(source.error ? { error: source.error } : {}),\n ...(source.retry ? { retry: source.retry } : {}),\n };\n return (\n <Context.Provider value={state}>\n {part(\n \"div\",\n withLiveRegion(\n props,\n combo.isOpen && value.trim() ? announcement(results.length) : \"\",\n ) as typeof props,\n state,\n {\n \"data-state\": source.error\n ? \"error\"\n : source.isLoading\n ? \"loading\"\n : source.isStale\n ? \"stale\"\n : \"valid\",\n onBlur: (event: {\n currentTarget: HTMLElement;\n relatedTarget: Node | null;\n }) => {\n if (!event.currentTarget.contains(event.relatedTarget)) {\n combo.setOpen(false);\n }\n },\n },\n )}\n </Context.Provider>\n );\n}\nconst Input = forwardRef<HTMLElement, PartProps<SearchState>>(\n function Input(props, ref) {\n const state = useSearch();\n return part(\n \"input\",\n props,\n state,\n {\n ...state.inputProps,\n type: \"search\",\n onCompositionStart: () => state.setComposing(true),\n onCompositionEnd: () => state.setComposing(false),\n value: state.query,\n onChange: (event: ChangeEvent<HTMLInputElement>) =>\n state.setQuery(event.target.value),\n },\n ref,\n );\n },\n);\nconst Results = forwardRef<HTMLElement, PartProps<SearchState>>(\n function Results(props, ref) {\n const state = useSearch();\n return part(\n \"div\",\n props,\n state,\n state.listProps,\n composeRefs(ref, (node) => {\n if (node?.id && node.id !== state.listProps.id) {\n state.setListId(node.id);\n }\n }),\n );\n },\n);\nconst Result = forwardRef<\n HTMLElement,\n PartProps<SearchState> & { index: number }\n>(function Result({ index, ...props }, ref) {\n const state = useSearch();\n return part(\n \"div\",\n props,\n state,\n state.getOptionProps(index),\n composeRefs(ref, (node) => {\n if (node?.id && node.id !== state.getOptionProps(index).id) {\n state.registerOption(index, node.id);\n }\n }),\n );\n});\n/**\n * Composable menu search parts with combobox navigation and result announcements.\n * Use Root with your own source or let it index the active branch menu through your ZappieProvider.\n */\nexport const Search = { Root, Input, Results, Result };\nexport { createSearchIndex } from \"./engine\";\nexport type { SearchResult } from \"./engine\";\n"],
|
|
5
4
|
"mappings": "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",
|
|
6
5
|
"names": ["createSearchIndex", "value", "Input", "Results", "Result"]
|
|
7
6
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/status/contact.ts
|
|
2
|
+
function contactLinks(branch) {
|
|
3
|
+
const links = [];
|
|
4
|
+
const phone = branch.phone?.replace(/[^\d+]/g, "");
|
|
5
|
+
if (phone && /\d/.test(phone)) {
|
|
6
|
+
links.push({ type: "phone", label: "Call", href: `tel:${phone}` });
|
|
7
|
+
}
|
|
8
|
+
const message = branch.socialLinks?.find(
|
|
9
|
+
(link) => link.type.toLowerCase() === "whatsapp"
|
|
10
|
+
);
|
|
11
|
+
if (message) {
|
|
12
|
+
links.push({ type: "message", label: "Send a message", href: message.url });
|
|
13
|
+
}
|
|
14
|
+
return links;
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
contactLinks
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=contact.js.map
|