@vio-live/web-sdk 0.2.0 → 0.4.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/README.md +29 -2
- package/dist/chunk-NP2KCM7X.js +3859 -0
- package/dist/chunk-NP2KCM7X.js.map +1 -0
- package/dist/chunk-PBQSHIID.js +3 -0
- package/dist/{chunk-GGM2AIK5.js.map → chunk-PBQSHIID.js.map} +1 -1
- package/dist/{chunk-7JSSYPRN.js → chunk-V5ZQG2H5.js} +1419 -169
- package/dist/chunk-V5ZQG2H5.js.map +1 -0
- package/dist/core/index.d.ts +314 -12
- package/dist/core/index.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +3 -2
- package/dist/react/index.js.map +1 -1
- package/dist/tokens-CvqQYURw.d.ts +555 -0
- package/dist/ui/index.d.ts +3 -83
- package/dist/ui/index.js +5 -132
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-7JSSYPRN.js.map +0 -1
- package/dist/chunk-GGM2AIK5.js +0 -3
- package/dist/chunk-SOO2G6CF.js +0 -1618
- package/dist/chunk-SOO2G6CF.js.map +0 -1
- package/dist/vio-checkout-CrC5tvmh.d.ts +0 -300
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../node_modules/@lit/react/src/create-component.ts","../../src/react/index.ts"],"names":["reservedReactProperties","Set","createComponent","react","React","tagName","elementClass","events","displayName","eventProps","Object","keys","ReactComponent","forwardRef","props","ref","useRef","Map","elementRef","reactProps","elementProps","k","v","entries","has","prototype","createElement","name","globalThis","litSsrReactEnabled","length","useCallback","node","current","VioProduct","VioProductCarousel","VioProductDetail","VioCart","VioCheckout"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @license\n * Copyright 2018 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\nimport type React from 'react';\n\nconst NODE_MODE = false;\nconst DEV_MODE = true;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DistributiveOmit<T, K extends string | number | symbol> = T extends any\n ? K extends keyof T\n ? Omit<T, K>\n : T\n : T;\ntype PropsWithoutRef<T> = DistributiveOmit<T, 'ref'>;\n\n/**\n * Creates a type to be used for the props of a web component used directly in\n * React JSX.\n *\n * Example:\n *\n * ```ts\n * declare module \"react\" {\n * namespace JSX {\n * interface IntrinsicElements {\n * 'x-foo': WebComponentProps<XFoo>;\n * }\n * }\n * }\n * ```\n */\nexport type WebComponentProps<I extends HTMLElement> = React.DetailedHTMLProps<\n React.HTMLAttributes<I>,\n I\n> &\n ElementProps<I>;\n\n/**\n * Type of the React component wrapping the web component. This is the return\n * type of `createComponent`.\n */\nexport type ReactWebComponent<\n I extends HTMLElement,\n E extends EventNames = {},\n> = React.ForwardRefExoticComponent<\n // TODO(augustjk): Remove and use `React.PropsWithoutRef` when\n // https://github.com/preactjs/preact/issues/4124 is fixed.\n PropsWithoutRef<ComponentProps<I, E>> & React.RefAttributes<I>\n>;\n\n// Props derived from custom element class. Currently has limitations of making\n// all properties optional and also surfaces life cycle methods in autocomplete.\n// TODO(augustjk) Consider omitting keyof LitElement to remove \"internal\"\n// lifecycle methods or allow user to explicitly provide props.\ntype ElementProps<I> = Partial<Omit<I, keyof HTMLElement>>;\n\n// Acceptable props to the React component.\ntype ComponentProps<I, E extends EventNames = {}> = Omit<\n React.HTMLAttributes<I>,\n // Prefer type of provided event handler props or those on element over\n // built-in HTMLAttributes\n keyof E | keyof ElementProps<I>\n> &\n EventListeners<E> &\n ElementProps<I>;\n\n/**\n * Type used to cast an event name with an event type when providing the\n * `events` option to `createComponent` for better typing of the event handler\n * prop.\n *\n * Example:\n *\n * ```ts\n * const FooComponent = createComponent({\n * ...\n * events: {\n * onfoo: 'foo' as EventName<FooEvent>,\n * }\n * });\n * ```\n *\n * `onfoo` prop will have the type `(e: FooEvent) => void`.\n */\nexport type EventName<T extends Event = Event> = string & {\n __eventType: T;\n};\n\n// A key value map matching React prop names to event names.\ntype EventNames = Record<string, EventName | string>;\n\n// A map of expected event listener types based on EventNames.\ntype EventListeners<R extends EventNames> = {\n [K in keyof R]?: R[K] extends EventName\n ? (e: R[K]['__eventType']) => void\n : (e: Event) => void;\n};\n\nexport interface Options<I extends HTMLElement, E extends EventNames = {}> {\n react: typeof React;\n tagName: string;\n elementClass: Constructor<I>;\n events?: E;\n displayName?: string;\n}\n\ntype Constructor<T> = {new (): T};\n\nconst reservedReactProperties = new Set([\n 'children',\n 'localName',\n 'ref',\n 'style',\n 'className',\n]);\n\nconst listenedEvents = new WeakMap<Element, Map<string, EventListenerObject>>();\n\n/**\n * Adds an event listener for the specified event to the given node. In the\n * React setup, there should only ever be one event listener. Thus, for\n * efficiency only one listener is added and the handler for that listener is\n * updated to point to the given listener function.\n */\nconst addOrUpdateEventListener = (\n node: Element,\n event: string,\n listener: (event?: Event) => void\n) => {\n let events = listenedEvents.get(node);\n if (events === undefined) {\n listenedEvents.set(node, (events = new Map()));\n }\n let handler = events.get(event);\n if (listener !== undefined) {\n // If necessary, add listener and track handler\n if (handler === undefined) {\n events.set(event, (handler = {handleEvent: listener}));\n node.addEventListener(event, handler);\n // Otherwise just update the listener with new value\n } else {\n handler.handleEvent = listener;\n }\n // Remove listener if one exists and value is undefined\n } else if (handler !== undefined) {\n events.delete(event);\n node.removeEventListener(event, handler);\n }\n};\n\n/**\n * Sets properties and events on custom elements. These properties and events\n * have been pre-filtered so we know they should apply to the custom element.\n */\nconst setProperty = <E extends Element>(\n node: E,\n name: string,\n value: unknown,\n old: unknown,\n events?: EventNames\n) => {\n const event = events?.[name];\n // Dirty check event value.\n if (event !== undefined) {\n if (value !== old) {\n addOrUpdateEventListener(node, event, value as (e?: Event) => void);\n }\n return;\n }\n // But don't dirty check properties; elements are assumed to do this.\n node[name as keyof E] = value as E[keyof E];\n\n // This block is to replicate React's behavior for attributes of native\n // elements where `undefined` or `null` values result in attributes being\n // removed.\n // https://github.com/facebook/react/blob/899cb95f52cc83ab5ca1eb1e268c909d3f0961e7/packages/react-dom-bindings/src/client/DOMPropertyOperations.js#L107-L141\n //\n // It's only needed here for native HTMLElement properties that reflect\n // attributes of the same name but don't have that behavior like \"id\" or\n // \"draggable\".\n if (\n (value === undefined || value === null) &&\n name in HTMLElement.prototype\n ) {\n node.removeAttribute(name);\n }\n};\n\n/**\n * Creates a React component for a custom element. Properties are distinguished\n * from attributes automatically, and events can be configured so they are added\n * to the custom element as event listeners.\n *\n * @param options An options bag containing the parameters needed to generate a\n * wrapped web component.\n *\n * @param options.react The React module, typically imported from the `react`\n * npm package.\n * @param options.tagName The custom element tag name registered via\n * `customElements.define`.\n * @param options.elementClass The custom element class registered via\n * `customElements.define`.\n * @param options.events An object listing events to which the component can\n * listen. The object keys are the event property names passed in via React\n * props and the object values are the names of the corresponding events\n * generated by the custom element. For example, given `{onactivate:\n * 'activate'}` an event function may be passed via the component's `onactivate`\n * prop and will be called when the custom element fires its `activate` event.\n * @param options.displayName A React component display name, used in debugging\n * messages. Default value is inferred from the name of custom element class\n * registered via `customElements.define`.\n */\nexport const createComponent = <\n I extends HTMLElement,\n E extends EventNames = {},\n>({\n react: React,\n tagName,\n elementClass,\n events,\n displayName,\n}: Options<I, E>): ReactWebComponent<I, E> => {\n const eventProps = new Set(Object.keys(events ?? {}));\n\n if (DEV_MODE && !NODE_MODE) {\n for (const p of reservedReactProperties) {\n if (p in elementClass.prototype && !(p in HTMLElement.prototype)) {\n // Note, this effectively warns only for `ref` since the other\n // reserved props are on HTMLElement.prototype. To address this\n // would require crawling down the prototype, which doesn't feel worth\n // it since implementing these properties on an element is extremely\n // rare.\n console.warn(\n `${tagName} contains property ${p} which is a React reserved ` +\n `property. It will be used by React and not set on the element.`\n );\n }\n }\n }\n\n type Props = ComponentProps<I, E>;\n\n const ReactComponent = React.forwardRef<I, Props>((props, ref) => {\n const prevElemPropsRef = React.useRef(new Map());\n const elementRef = React.useRef<I | null>(null);\n\n // Props to be passed to React.createElement\n const reactProps: Record<string, unknown> = {};\n // Props to be set on element with setProperty\n const elementProps: Record<string, unknown> = {};\n\n for (const [k, v] of Object.entries(props)) {\n if (reservedReactProperties.has(k)) {\n // React does *not* handle `className` for custom elements so\n // coerce it to `class` so it's handled correctly.\n reactProps[k === 'className' ? 'class' : k] = v;\n continue;\n }\n\n if (eventProps.has(k) || k in elementClass.prototype) {\n elementProps[k] = v;\n continue;\n }\n\n reactProps[k] = v;\n }\n\n // useLayoutEffect produces warnings during server rendering.\n if (!NODE_MODE) {\n // This one has no dependency array so it'll run on every re-render.\n React.useLayoutEffect(() => {\n if (elementRef.current === null) {\n return;\n }\n const newElemProps = new Map();\n for (const key in elementProps) {\n setProperty(\n elementRef.current,\n key,\n props[key],\n prevElemPropsRef.current.get(key),\n events\n );\n prevElemPropsRef.current.delete(key);\n newElemProps.set(key, props[key]);\n }\n // \"Unset\" any props from previous render that no longer exist.\n // Setting to `undefined` seems like the correct thing to \"unset\"\n // but currently React will set it as `null`.\n // See https://github.com/facebook/react/issues/28203\n for (const [key, value] of prevElemPropsRef.current) {\n setProperty(elementRef.current, key, undefined, value, events);\n }\n prevElemPropsRef.current = newElemProps;\n });\n\n // Empty dependency array so this will only run once after first render.\n React.useLayoutEffect(() => {\n elementRef.current?.removeAttribute('defer-hydration');\n }, []);\n }\n\n if (NODE_MODE) {\n // If component is to be server rendered with `@lit/ssr-react`, pass\n // element properties in a special bag to be set by the server-side\n // element renderer.\n if (\n (React.createElement.name === 'litPatchedCreateElement' ||\n globalThis.litSsrReactEnabled) &&\n Object.keys(elementProps).length\n ) {\n // This property needs to remain unminified.\n reactProps['_$litProps$'] = elementProps;\n }\n } else {\n // Suppress hydration warning for server-rendered attributes.\n // This property needs to remain unminified.\n reactProps['suppressHydrationWarning'] = true;\n }\n\n return React.createElement(tagName, {\n ...reactProps,\n ref: React.useCallback(\n (node: I) => {\n elementRef.current = node;\n if (typeof ref === 'function') {\n ref(node);\n } else if (ref !== null) {\n ref.current = node;\n }\n },\n [ref]\n ),\n });\n });\n\n ReactComponent.displayName = displayName ?? elementClass.name;\n\n return ReactComponent;\n};\n","/**\n * Vio React adapter — typed React wrappers for the `<vio-*>` web components.\n *\n * Built on `@lit/react`'s `createComponent`, which gives each wrapper:\n * - typed props derived from the element's reactive properties, and\n * - typed event props (the `vio:*` CustomEvents below) as `onX` callbacks.\n *\n * Importing this entry also REGISTERS the custom elements (via the\n * `registerVioElements()` call below), so you do NOT need a separate\n * `import '@vio-live/web-sdk/ui'` when you use these.\n *\n * React 18+ is an (optional) peer dependency — only required if you import this\n * entry. `@lit/react` is bundled in, so consumers install nothing extra.\n *\n * import { Vio } from '@vio-live/web-sdk'\n * import { VioProductCarousel } from '@vio-live/web-sdk/react'\n * Vio.init({ apiKey, apiBase, graphqlBase, stripePublishableKey })\n * <VioProductCarousel onProductAdd={(e) => console.log(e.detail)} />\n */\n\nimport * as React from 'react'\nimport { createComponent } from '@lit/react'\nimport {\n registerVioElements,\n VioProduct as VioProductEl,\n VioProductCarousel as VioProductCarouselEl,\n VioProductDetail as VioProductDetailEl,\n VioCart as VioCartEl,\n VioCheckout as VioCheckoutEl,\n} from '../ui/elements.js'\n\n// Register the underlying custom elements when this entry is imported.\nregisterVioElements()\n\nexport const VioProduct = createComponent({\n react: React,\n tagName: 'vio-product',\n elementClass: VioProductEl,\n events: {\n onProductClick: 'vio:product-click',\n onProductAdd: 'vio:product-add',\n },\n})\n\nexport const VioProductCarousel = createComponent({\n react: React,\n tagName: 'vio-product-carousel',\n elementClass: VioProductCarouselEl,\n events: {},\n})\n\nexport const VioProductDetail = createComponent({\n react: React,\n tagName: 'vio-product-detail',\n elementClass: VioProductDetailEl,\n events: {\n onAddedToCart: 'vio:added-to-cart',\n onCheckoutOpen: 'vio:checkout-open',\n },\n})\n\nexport const VioCart = createComponent({\n react: React,\n tagName: 'vio-cart',\n elementClass: VioCartEl,\n events: {\n onPaymentSuccess: 'vio:payment-success',\n onCheckoutOpen: 'vio:checkout-open',\n },\n})\n\nexport const VioCheckout = createComponent({\n react: React,\n tagName: 'vio-checkout',\n elementClass: VioCheckoutEl,\n events: {\n onPaymentSuccess: 'vio:payment-success',\n },\n})\n"]}
|
|
1
|
+
{"version":3,"sources":["../../node_modules/@lit/react/src/create-component.ts","../../src/react/index.ts"],"names":["reservedReactProperties","Set","createComponent","react","React","tagName","elementClass","events","displayName","eventProps","Object","keys","ReactComponent","forwardRef","props","ref","useRef","Map","elementRef","reactProps","elementProps","k","v","entries","has","prototype","createElement","name","globalThis","litSsrReactEnabled","length","useCallback","node","current","VioProduct","VioProductCarousel","VioProductDetail","VioCart","VioCheckout"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @license\n * Copyright 2018 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\nimport type React from 'react';\n\nconst NODE_MODE = false;\nconst DEV_MODE = true;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DistributiveOmit<T, K extends string | number | symbol> = T extends any\n ? K extends keyof T\n ? Omit<T, K>\n : T\n : T;\ntype PropsWithoutRef<T> = DistributiveOmit<T, 'ref'>;\n\n/**\n * Creates a type to be used for the props of a web component used directly in\n * React JSX.\n *\n * Example:\n *\n * ```ts\n * declare module \"react\" {\n * namespace JSX {\n * interface IntrinsicElements {\n * 'x-foo': WebComponentProps<XFoo>;\n * }\n * }\n * }\n * ```\n */\nexport type WebComponentProps<I extends HTMLElement> = React.DetailedHTMLProps<\n React.HTMLAttributes<I>,\n I\n> &\n ElementProps<I>;\n\n/**\n * Type of the React component wrapping the web component. This is the return\n * type of `createComponent`.\n */\nexport type ReactWebComponent<\n I extends HTMLElement,\n E extends EventNames = {},\n> = React.ForwardRefExoticComponent<\n // TODO(augustjk): Remove and use `React.PropsWithoutRef` when\n // https://github.com/preactjs/preact/issues/4124 is fixed.\n PropsWithoutRef<ComponentProps<I, E>> & React.RefAttributes<I>\n>;\n\n// Props derived from custom element class. Currently has limitations of making\n// all properties optional and also surfaces life cycle methods in autocomplete.\n// TODO(augustjk) Consider omitting keyof LitElement to remove \"internal\"\n// lifecycle methods or allow user to explicitly provide props.\ntype ElementProps<I> = Partial<Omit<I, keyof HTMLElement>>;\n\n// Acceptable props to the React component.\ntype ComponentProps<I, E extends EventNames = {}> = Omit<\n React.HTMLAttributes<I>,\n // Prefer type of provided event handler props or those on element over\n // built-in HTMLAttributes\n keyof E | keyof ElementProps<I>\n> &\n EventListeners<E> &\n ElementProps<I>;\n\n/**\n * Type used to cast an event name with an event type when providing the\n * `events` option to `createComponent` for better typing of the event handler\n * prop.\n *\n * Example:\n *\n * ```ts\n * const FooComponent = createComponent({\n * ...\n * events: {\n * onfoo: 'foo' as EventName<FooEvent>,\n * }\n * });\n * ```\n *\n * `onfoo` prop will have the type `(e: FooEvent) => void`.\n */\nexport type EventName<T extends Event = Event> = string & {\n __eventType: T;\n};\n\n// A key value map matching React prop names to event names.\ntype EventNames = Record<string, EventName | string>;\n\n// A map of expected event listener types based on EventNames.\ntype EventListeners<R extends EventNames> = {\n [K in keyof R]?: R[K] extends EventName\n ? (e: R[K]['__eventType']) => void\n : (e: Event) => void;\n};\n\nexport interface Options<I extends HTMLElement, E extends EventNames = {}> {\n react: typeof React;\n tagName: string;\n elementClass: Constructor<I>;\n events?: E;\n displayName?: string;\n}\n\ntype Constructor<T> = {new (): T};\n\nconst reservedReactProperties = new Set([\n 'children',\n 'localName',\n 'ref',\n 'style',\n 'className',\n]);\n\nconst listenedEvents = new WeakMap<Element, Map<string, EventListenerObject>>();\n\n/**\n * Adds an event listener for the specified event to the given node. In the\n * React setup, there should only ever be one event listener. Thus, for\n * efficiency only one listener is added and the handler for that listener is\n * updated to point to the given listener function.\n */\nconst addOrUpdateEventListener = (\n node: Element,\n event: string,\n listener: (event?: Event) => void\n) => {\n let events = listenedEvents.get(node);\n if (events === undefined) {\n listenedEvents.set(node, (events = new Map()));\n }\n let handler = events.get(event);\n if (listener !== undefined) {\n // If necessary, add listener and track handler\n if (handler === undefined) {\n events.set(event, (handler = {handleEvent: listener}));\n node.addEventListener(event, handler);\n // Otherwise just update the listener with new value\n } else {\n handler.handleEvent = listener;\n }\n // Remove listener if one exists and value is undefined\n } else if (handler !== undefined) {\n events.delete(event);\n node.removeEventListener(event, handler);\n }\n};\n\n/**\n * Sets properties and events on custom elements. These properties and events\n * have been pre-filtered so we know they should apply to the custom element.\n */\nconst setProperty = <E extends Element>(\n node: E,\n name: string,\n value: unknown,\n old: unknown,\n events?: EventNames\n) => {\n const event = events?.[name];\n // Dirty check event value.\n if (event !== undefined) {\n if (value !== old) {\n addOrUpdateEventListener(node, event, value as (e?: Event) => void);\n }\n return;\n }\n // But don't dirty check properties; elements are assumed to do this.\n node[name as keyof E] = value as E[keyof E];\n\n // This block is to replicate React's behavior for attributes of native\n // elements where `undefined` or `null` values result in attributes being\n // removed.\n // https://github.com/facebook/react/blob/899cb95f52cc83ab5ca1eb1e268c909d3f0961e7/packages/react-dom-bindings/src/client/DOMPropertyOperations.js#L107-L141\n //\n // It's only needed here for native HTMLElement properties that reflect\n // attributes of the same name but don't have that behavior like \"id\" or\n // \"draggable\".\n if (\n (value === undefined || value === null) &&\n name in HTMLElement.prototype\n ) {\n node.removeAttribute(name);\n }\n};\n\n/**\n * Creates a React component for a custom element. Properties are distinguished\n * from attributes automatically, and events can be configured so they are added\n * to the custom element as event listeners.\n *\n * @param options An options bag containing the parameters needed to generate a\n * wrapped web component.\n *\n * @param options.react The React module, typically imported from the `react`\n * npm package.\n * @param options.tagName The custom element tag name registered via\n * `customElements.define`.\n * @param options.elementClass The custom element class registered via\n * `customElements.define`.\n * @param options.events An object listing events to which the component can\n * listen. The object keys are the event property names passed in via React\n * props and the object values are the names of the corresponding events\n * generated by the custom element. For example, given `{onactivate:\n * 'activate'}` an event function may be passed via the component's `onactivate`\n * prop and will be called when the custom element fires its `activate` event.\n * @param options.displayName A React component display name, used in debugging\n * messages. Default value is inferred from the name of custom element class\n * registered via `customElements.define`.\n */\nexport const createComponent = <\n I extends HTMLElement,\n E extends EventNames = {},\n>({\n react: React,\n tagName,\n elementClass,\n events,\n displayName,\n}: Options<I, E>): ReactWebComponent<I, E> => {\n const eventProps = new Set(Object.keys(events ?? {}));\n\n if (DEV_MODE && !NODE_MODE) {\n for (const p of reservedReactProperties) {\n if (p in elementClass.prototype && !(p in HTMLElement.prototype)) {\n // Note, this effectively warns only for `ref` since the other\n // reserved props are on HTMLElement.prototype. To address this\n // would require crawling down the prototype, which doesn't feel worth\n // it since implementing these properties on an element is extremely\n // rare.\n console.warn(\n `${tagName} contains property ${p} which is a React reserved ` +\n `property. It will be used by React and not set on the element.`\n );\n }\n }\n }\n\n type Props = ComponentProps<I, E>;\n\n const ReactComponent = React.forwardRef<I, Props>((props, ref) => {\n const prevElemPropsRef = React.useRef(new Map());\n const elementRef = React.useRef<I | null>(null);\n\n // Props to be passed to React.createElement\n const reactProps: Record<string, unknown> = {};\n // Props to be set on element with setProperty\n const elementProps: Record<string, unknown> = {};\n\n for (const [k, v] of Object.entries(props)) {\n if (reservedReactProperties.has(k)) {\n // React does *not* handle `className` for custom elements so\n // coerce it to `class` so it's handled correctly.\n reactProps[k === 'className' ? 'class' : k] = v;\n continue;\n }\n\n if (eventProps.has(k) || k in elementClass.prototype) {\n elementProps[k] = v;\n continue;\n }\n\n reactProps[k] = v;\n }\n\n // useLayoutEffect produces warnings during server rendering.\n if (!NODE_MODE) {\n // This one has no dependency array so it'll run on every re-render.\n React.useLayoutEffect(() => {\n if (elementRef.current === null) {\n return;\n }\n const newElemProps = new Map();\n for (const key in elementProps) {\n setProperty(\n elementRef.current,\n key,\n props[key],\n prevElemPropsRef.current.get(key),\n events\n );\n prevElemPropsRef.current.delete(key);\n newElemProps.set(key, props[key]);\n }\n // \"Unset\" any props from previous render that no longer exist.\n // Setting to `undefined` seems like the correct thing to \"unset\"\n // but currently React will set it as `null`.\n // See https://github.com/facebook/react/issues/28203\n for (const [key, value] of prevElemPropsRef.current) {\n setProperty(elementRef.current, key, undefined, value, events);\n }\n prevElemPropsRef.current = newElemProps;\n });\n\n // Empty dependency array so this will only run once after first render.\n React.useLayoutEffect(() => {\n elementRef.current?.removeAttribute('defer-hydration');\n }, []);\n }\n\n if (NODE_MODE) {\n // If component is to be server rendered with `@lit/ssr-react`, pass\n // element properties in a special bag to be set by the server-side\n // element renderer.\n if (\n (React.createElement.name === 'litPatchedCreateElement' ||\n globalThis.litSsrReactEnabled) &&\n Object.keys(elementProps).length\n ) {\n // This property needs to remain unminified.\n reactProps['_$litProps$'] = elementProps;\n }\n } else {\n // Suppress hydration warning for server-rendered attributes.\n // This property needs to remain unminified.\n reactProps['suppressHydrationWarning'] = true;\n }\n\n return React.createElement(tagName, {\n ...reactProps,\n ref: React.useCallback(\n (node: I) => {\n elementRef.current = node;\n if (typeof ref === 'function') {\n ref(node);\n } else if (ref !== null) {\n ref.current = node;\n }\n },\n [ref]\n ),\n });\n });\n\n ReactComponent.displayName = displayName ?? elementClass.name;\n\n return ReactComponent;\n};\n","/**\n * Vio React adapter — typed React wrappers for the `<vio-*>` web components.\n *\n * Built on `@lit/react`'s `createComponent`, which gives each wrapper:\n * - typed props derived from the element's reactive properties, and\n * - typed event props (the `vio:*` CustomEvents below) as `onX` callbacks.\n *\n * Importing this entry also REGISTERS the custom elements (via the\n * `registerVioElements()` call below), so you do NOT need a separate\n * `import '@vio-live/web-sdk/ui'` when you use these.\n *\n * React 18+ is an (optional) peer dependency — only required if you import this\n * entry. `@lit/react` is bundled in, so consumers install nothing extra.\n *\n * import { Vio } from '@vio-live/web-sdk'\n * import { VioProductCarousel } from '@vio-live/web-sdk/react'\n * Vio.init({ apiKey, apiBase, graphqlBase, stripePublishableKey })\n * <VioProductCarousel onProductAdd={(e) => console.log(e.detail)} />\n */\n\nimport * as React from 'react'\nimport { createComponent } from '@lit/react'\nimport {\n registerVioElements,\n VioProduct as VioProductEl,\n VioProductCarousel as VioProductCarouselEl,\n VioProductDetail as VioProductDetailEl,\n VioCart as VioCartEl,\n VioCheckout as VioCheckoutEl,\n} from '../ui/elements.js'\n// Not just for the re-export below — the import itself injects the default\n// design tokens as CSS custom properties on `:root` (see tokens.ts). Without\n// it, `elements.js` alone never pulls tokens.ts in, so this entry (the one\n// the Vev bundle actually goes through) never ran that injection.\nimport '../ui/tokens.js'\n\n// Register the underlying custom elements when this entry is imported.\nregisterVioElements()\n\nexport const VioProduct = createComponent({\n react: React,\n tagName: 'vio-product',\n elementClass: VioProductEl,\n events: {\n onProductClick: 'vio:product-click',\n onProductAdd: 'vio:product-add',\n },\n})\n\nexport const VioProductCarousel = createComponent({\n react: React,\n tagName: 'vio-product-carousel',\n elementClass: VioProductCarouselEl,\n events: {},\n})\n\nexport const VioProductDetail = createComponent({\n react: React,\n tagName: 'vio-product-detail',\n elementClass: VioProductDetailEl,\n events: {\n onAddedToCart: 'vio:added-to-cart',\n onCheckoutOpen: 'vio:checkout-open',\n },\n})\n\nexport const VioCart = createComponent({\n react: React,\n tagName: 'vio-cart',\n elementClass: VioCartEl,\n events: {\n onPaymentSuccess: 'vio:payment-success',\n onCheckoutOpen: 'vio:checkout-open',\n },\n})\n\nexport const VioCheckout = createComponent({\n react: React,\n tagName: 'vio-checkout',\n elementClass: VioCheckoutEl,\n events: {\n onPaymentSuccess: 'vio:payment-success',\n },\n})\n\nexport { VioTokens, applyVioTheme, type VioThemeOverrides } from '../ui/tokens.js'\n"]}
|
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
import * as lit_html from 'lit-html';
|
|
2
|
+
import * as lit from 'lit';
|
|
3
|
+
import { LitElement } from 'lit';
|
|
4
|
+
|
|
5
|
+
declare class VioProduct extends LitElement {
|
|
6
|
+
/** Brand label shown at the top (uppercase, gray, small). */
|
|
7
|
+
brand: string;
|
|
8
|
+
/** Product display name. Two-line truncation. */
|
|
9
|
+
name: string;
|
|
10
|
+
/** Pre-formatted price string (e.g. "990 kr"). Bold. */
|
|
11
|
+
price: string;
|
|
12
|
+
/** Retailer / sponsor name shown at the bottom (uppercase, gray). */
|
|
13
|
+
retailer: string;
|
|
14
|
+
/** Product image URL. Square aspect, gray bg if missing. */
|
|
15
|
+
imageUrl: string;
|
|
16
|
+
/** Vio product id, propagated to click event. */
|
|
17
|
+
productId: string;
|
|
18
|
+
/** Vio sponsor id, propagated to click event. */
|
|
19
|
+
sponsorId: string;
|
|
20
|
+
/** Whether the product has selectable variants — the quick-add button then
|
|
21
|
+
* opens the detail to pick one instead of adding directly. */
|
|
22
|
+
hasVariants: boolean;
|
|
23
|
+
/** Optional raw variants/options from the host — extra signal that a
|
|
24
|
+
* variant must be picked before adding (selection is mandatory). */
|
|
25
|
+
variants: unknown[] | undefined;
|
|
26
|
+
options: unknown[] | undefined;
|
|
27
|
+
/** Image-only mode: hide the meta block (brand / name / price / retailer)
|
|
28
|
+
* and show just the image + the quick-add button. Click still opens the
|
|
29
|
+
* detail. */
|
|
30
|
+
hideMeta: boolean;
|
|
31
|
+
/** True when this product currently sits in the cart (persistent mark). */
|
|
32
|
+
private inCart;
|
|
33
|
+
/** Total units of this product in the cart (shown as a count when > 1). */
|
|
34
|
+
private cartQty;
|
|
35
|
+
/** Transient flag driving the add "pop" animation. */
|
|
36
|
+
private justAdded;
|
|
37
|
+
private boundCartChange;
|
|
38
|
+
static styles: lit.CSSResult;
|
|
39
|
+
connectedCallback(): void;
|
|
40
|
+
disconnectedCallback(): void;
|
|
41
|
+
updated(changed: Map<string, unknown>): void;
|
|
42
|
+
/** Reflect whether (and how many units of) this product sit in the cart. */
|
|
43
|
+
private updateInCart;
|
|
44
|
+
private onClick;
|
|
45
|
+
private onKeydown;
|
|
46
|
+
/** Quick add-to-cart. If the product has selectable variants, open the detail
|
|
47
|
+
* to pick one instead (the host can't choose for the user). Otherwise emit
|
|
48
|
+
* `vio:product-add` so the host adds it (bumping quantity on repeat taps). */
|
|
49
|
+
private onAdd;
|
|
50
|
+
render(): lit_html.TemplateResult<1>;
|
|
51
|
+
}
|
|
52
|
+
declare global {
|
|
53
|
+
interface HTMLElementTagNameMap {
|
|
54
|
+
'vio-product': VioProduct;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
declare class VioProductCarousel extends LitElement {
|
|
59
|
+
/** Small uppercase label in accent color (e.g. "REDAKSJONENS FAVORITTER"). */
|
|
60
|
+
label: string;
|
|
61
|
+
/** Big serif heading (e.g. "Shop Favorittene"). */
|
|
62
|
+
heading: string;
|
|
63
|
+
/** Inline gray disclaimer next to the heading (e.g. "annonselenker"). */
|
|
64
|
+
disclaimer: string;
|
|
65
|
+
/** URL for the "Se alle" link. Hidden if empty. */
|
|
66
|
+
seeAllHref: string;
|
|
67
|
+
/** Label for the see-all link. Defaults to Norwegian. */
|
|
68
|
+
seeAllLabel: string;
|
|
69
|
+
/** Auto-fetch mode: comma-separated product ids (e.g. "408895,408896"). */
|
|
70
|
+
productIds: string;
|
|
71
|
+
/** Auto-fetch mode option A: explicit per-sponsor commerce apiKey. */
|
|
72
|
+
commerceKey: string;
|
|
73
|
+
/** Auto-fetch mode option B: sponsor id looked up via Vio.bootstrap. */
|
|
74
|
+
sponsorId: number;
|
|
75
|
+
/**
|
|
76
|
+
* Multi-sponsor mode: `"sponsorId:productId,sponsorId:productId,…"`. Each
|
|
77
|
+
* pair resolves its own commerce client via `Vio.commerceFor(sponsorId)`;
|
|
78
|
+
* fetches run in parallel and results are merged preserving input order.
|
|
79
|
+
* Each rendered <vio-product> carries the per-item sponsor-id so taps go
|
|
80
|
+
* to <vio-product-detail> with the right commerce context.
|
|
81
|
+
*
|
|
82
|
+
* Takes precedence over `product-ids` if both are set.
|
|
83
|
+
*/
|
|
84
|
+
productRefs: string;
|
|
85
|
+
/** Currency for product prices. */
|
|
86
|
+
currency: string;
|
|
87
|
+
private scroller;
|
|
88
|
+
private fetched;
|
|
89
|
+
private isLoading;
|
|
90
|
+
private fetchError;
|
|
91
|
+
static styles: lit.CSSResult;
|
|
92
|
+
connectedCallback(): void;
|
|
93
|
+
updated(changed: Map<string, unknown>): void;
|
|
94
|
+
private autoFetch;
|
|
95
|
+
private parseRefs;
|
|
96
|
+
private scrollByDirection;
|
|
97
|
+
private renderScrollerContent;
|
|
98
|
+
render(): lit_html.TemplateResult<1>;
|
|
99
|
+
}
|
|
100
|
+
declare global {
|
|
101
|
+
interface HTMLElementTagNameMap {
|
|
102
|
+
'vio-product-carousel': VioProductCarousel;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
declare class VioProductDetail extends LitElement {
|
|
107
|
+
open: boolean;
|
|
108
|
+
productId: string;
|
|
109
|
+
sponsorId: number;
|
|
110
|
+
/** Subscribe to `vio:product-click` on the document and auto-open. */
|
|
111
|
+
autoOpen: boolean;
|
|
112
|
+
currency: string;
|
|
113
|
+
private product;
|
|
114
|
+
private isLoading;
|
|
115
|
+
private fetchError;
|
|
116
|
+
private selectedOptions;
|
|
117
|
+
private quantity;
|
|
118
|
+
private activeImageIndex;
|
|
119
|
+
private adding;
|
|
120
|
+
/** Apple Pay actually usable (real Stripe + registered domain) — set on load. */
|
|
121
|
+
private applePayOk;
|
|
122
|
+
/** Payment method names enabled for the sponsor (backend-configured).
|
|
123
|
+
* null = not loaded yet → all buy buttons render; [] = none enabled. */
|
|
124
|
+
private availableMethods;
|
|
125
|
+
/** Race guard: only the latest fetch may write state (two quick taps on
|
|
126
|
+
* different cards would otherwise let the slow response win). */
|
|
127
|
+
private fetchSeq;
|
|
128
|
+
/** Memoised option maps per variant object (recomputed per product). */
|
|
129
|
+
private optMapCache;
|
|
130
|
+
/** Pre-prepared Apple Pay handle — show() fires synchronously on tap. */
|
|
131
|
+
private applePayHandle;
|
|
132
|
+
private boundProductClick;
|
|
133
|
+
static styles: lit.CSSResult;
|
|
134
|
+
connectedCallback(): void;
|
|
135
|
+
disconnectedCallback(): void;
|
|
136
|
+
show(): void;
|
|
137
|
+
close(): void;
|
|
138
|
+
private fetchProduct;
|
|
139
|
+
/** Which buy buttons to render — backend-configured per sponsor. */
|
|
140
|
+
private loadAvailablePaymentMethods;
|
|
141
|
+
/** Backend names arrive as e.g. "Apple Pay" — compare letters only. */
|
|
142
|
+
private methodEnabled;
|
|
143
|
+
/**
|
|
144
|
+
* Build an option-name → value map for a variant, from every shape the feeds
|
|
145
|
+
* ship: option1/2/3 columns, options/selected_options/variant arrays, plain
|
|
146
|
+
* object maps, and finally the title (value scan first, position fallback).
|
|
147
|
+
*/
|
|
148
|
+
private getVariantOptionMap;
|
|
149
|
+
/** Option values a variant represents (map values ∪ title parts). */
|
|
150
|
+
private variantOptionValues;
|
|
151
|
+
/** Does a variant part represent this option value? Exact match, plus a
|
|
152
|
+
* whole-token match ONLY for numeric values ("30" matches "30 ml") — word
|
|
153
|
+
* subsets of text values are not matches ("Large" must NOT match
|
|
154
|
+
* "Extra Large"). */
|
|
155
|
+
private partMatchesValue;
|
|
156
|
+
/** Does a variant satisfy every currently-selected option value? */
|
|
157
|
+
private variantMatchesSelection;
|
|
158
|
+
/** Sellable quantity for a variant, across the stock field zoo. Boolean
|
|
159
|
+
* availability flags win; a numeric field wins next; `null` = the feed does
|
|
160
|
+
* not report stock for this variant (callers fall back to product stock —
|
|
161
|
+
* never assume "in stock" from absent data). */
|
|
162
|
+
private getVariantQty;
|
|
163
|
+
/** Variant stock with the product-level fallback applied (0 = not sellable). */
|
|
164
|
+
private variantSellableQty;
|
|
165
|
+
/** Resolve the active variant: full in-stock match → full match → best score
|
|
166
|
+
* (2 per mapped option match, 1 per title match, +0.5 if in stock). */
|
|
167
|
+
private selectedVariant;
|
|
168
|
+
/** True if some in-stock variant carries this option value. */
|
|
169
|
+
private isValueAvailable;
|
|
170
|
+
private get unitPrice();
|
|
171
|
+
private get compareAt();
|
|
172
|
+
private get availableQuantity();
|
|
173
|
+
private stockLabel;
|
|
174
|
+
/** Pick an option value. If the exact combination doesn't exist, jump to an
|
|
175
|
+
* in-stock variant that carries the value and adopt its full option map
|
|
176
|
+
* (keeps the selection always resolvable to a real variant). */
|
|
177
|
+
private selectOption;
|
|
178
|
+
private allImages;
|
|
179
|
+
private get activeImage();
|
|
180
|
+
/** Add the current product + selected variant + qty to the cart. Returns the variant id used. */
|
|
181
|
+
private addCurrentToCart;
|
|
182
|
+
private addToCart;
|
|
183
|
+
/**
|
|
184
|
+
* Express buy: add to cart, then open the checkout overlay with Klarna
|
|
185
|
+
* preselected. The host app handles `vio:checkout-open` (same event the cart
|
|
186
|
+
* uses) — the `paymentMethod` hint tells it to preselect Klarna so its widget
|
|
187
|
+
* loads immediately. Reuses the working Klarna Payments flow (no express SDK).
|
|
188
|
+
*/
|
|
189
|
+
private buyWithKlarna;
|
|
190
|
+
/** Buy with Vipps — opens the checkout with Vipps preselected. */
|
|
191
|
+
private buyWithVipps;
|
|
192
|
+
/** Buy with Stripe — opens the checkout with Stripe preselected. */
|
|
193
|
+
private buyWithStripe;
|
|
194
|
+
/**
|
|
195
|
+
* Express buy with Apple Pay. Runs synchronously inside the click so the
|
|
196
|
+
* Apple Pay sheet keeps the user gesture: add to cart → open checkout (sets
|
|
197
|
+
* state) → launch the sheet. On authorize, `payment-complete` drives the
|
|
198
|
+
* confirmation drawer (same path as the in-checkout Apple Pay button).
|
|
199
|
+
*/
|
|
200
|
+
private buyWithApplePay;
|
|
201
|
+
render(): lit_html.TemplateResult<1>;
|
|
202
|
+
private renderBody;
|
|
203
|
+
private renderOptionPickers;
|
|
204
|
+
/** Best-effort HTML strip for description. */
|
|
205
|
+
private stripHtml;
|
|
206
|
+
}
|
|
207
|
+
declare global {
|
|
208
|
+
interface HTMLElementTagNameMap {
|
|
209
|
+
'vio-product-detail': VioProductDetail;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
declare class VioCart extends LitElement {
|
|
214
|
+
/** Drawer visibility. Reflects to attribute for `:host([open])`. */
|
|
215
|
+
open: boolean;
|
|
216
|
+
heading: string;
|
|
217
|
+
emptyLabel: string;
|
|
218
|
+
subtotalLabel: string;
|
|
219
|
+
private carts;
|
|
220
|
+
private itemCount;
|
|
221
|
+
/** True when Apple Pay is usable (Safari + device + Stripe configured). */
|
|
222
|
+
private applePayAvailable;
|
|
223
|
+
/** Payment method names enabled for the sponsor (backend-configured).
|
|
224
|
+
* null = not loaded yet → all buttons render; [] = none enabled. */
|
|
225
|
+
private availableMethods;
|
|
226
|
+
/** Set after an Apple Pay express purchase — switches the drawer to the
|
|
227
|
+
* confirmation screen (so the rest of the cart/checkout is not shown). */
|
|
228
|
+
private confirmedOrder;
|
|
229
|
+
/** Pre-resolved Apple Pay handle; `show()` fires the sheet synchronously. */
|
|
230
|
+
private applePayHandle;
|
|
231
|
+
/** Cart total Apple Pay was last prepared for (avoids redundant prep). */
|
|
232
|
+
private preparedTotal;
|
|
233
|
+
/** In-flight guard for loadAvailablePaymentMethods. */
|
|
234
|
+
private loadingPaymentMethods;
|
|
235
|
+
private boundOnCartChange;
|
|
236
|
+
static styles: lit.CSSResult;
|
|
237
|
+
connectedCallback(): void;
|
|
238
|
+
disconnectedCallback(): void;
|
|
239
|
+
close(): void;
|
|
240
|
+
show(): void;
|
|
241
|
+
/** Which payment buttons to render — backend-configured per sponsor.
|
|
242
|
+
* Falls back to showing everything if the query fails or none is set. */
|
|
243
|
+
private loadAvailablePaymentMethods;
|
|
244
|
+
/** Backend names arrive as e.g. "Apple Pay" — compare letters only. */
|
|
245
|
+
private methodEnabled;
|
|
246
|
+
updated(changed: Map<string, unknown>): void;
|
|
247
|
+
/** Pre-resolve the Apple Pay sheet for the current cart total. */
|
|
248
|
+
private refreshApplePay;
|
|
249
|
+
/** Apple Pay express — pay the whole cart, then show the confirmation. */
|
|
250
|
+
private onApplePay;
|
|
251
|
+
private buildConfirmation;
|
|
252
|
+
private closeConfirmation;
|
|
253
|
+
/** Klarna express — opens the side-drawer Klarna checkout for the whole cart,
|
|
254
|
+
* skipping the address form (leveringsadresse) and method picker
|
|
255
|
+
* (betalingsmetode). The host's checkout-open handler closes this cart and
|
|
256
|
+
* opens <vio-checkout> in express mode with Klarna preselected. */
|
|
257
|
+
private onKlarnaExpress;
|
|
258
|
+
/** Vipps — opens the checkout with Vipps preselected. */
|
|
259
|
+
private onVipps;
|
|
260
|
+
/** Stripe — opens the checkout with Stripe preselected. */
|
|
261
|
+
private onStripe;
|
|
262
|
+
private get totalAcrossSponsors();
|
|
263
|
+
private get primaryCurrency();
|
|
264
|
+
private onIncrement;
|
|
265
|
+
private onDecrement;
|
|
266
|
+
private onRemove;
|
|
267
|
+
render(): lit_html.TemplateResult<1>;
|
|
268
|
+
private renderConfirmation;
|
|
269
|
+
}
|
|
270
|
+
declare global {
|
|
271
|
+
interface HTMLElementTagNameMap {
|
|
272
|
+
'vio-cart': VioCart;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
declare class VioCheckout extends LitElement {
|
|
277
|
+
open: boolean;
|
|
278
|
+
heading: string;
|
|
279
|
+
shippingLabel: string;
|
|
280
|
+
/** Express mode — render only the Klarna widget (no address form / method grid). */
|
|
281
|
+
express: boolean;
|
|
282
|
+
private checkoutState;
|
|
283
|
+
private items;
|
|
284
|
+
private applePayAvailable;
|
|
285
|
+
private applePayInProgress;
|
|
286
|
+
private klarnaAvailable;
|
|
287
|
+
private orderConfirmed;
|
|
288
|
+
private confirmedMethod;
|
|
289
|
+
private paymentError;
|
|
290
|
+
/** Neutral (non-error) status line: "verifying payment…" / "still processing". */
|
|
291
|
+
private paymentNotice;
|
|
292
|
+
/** Snapshot of the placed order, for the confirmation screen. */
|
|
293
|
+
private confirmedOrder;
|
|
294
|
+
/** Active Klarna Payments widget handle + the subtotal it was mounted for. */
|
|
295
|
+
private klarnaHandle;
|
|
296
|
+
private klarnaMountedAmount;
|
|
297
|
+
private klarnaMountedShipping;
|
|
298
|
+
private klarnaMounting;
|
|
299
|
+
/** Categories offered by the active Klarna session + the selected one. */
|
|
300
|
+
private klarnaCategories;
|
|
301
|
+
private klarnaSelectedCat;
|
|
302
|
+
private klarnaAuthorizing;
|
|
303
|
+
/** Chosen shipping option id (express flow) — drives the Klarna order total. */
|
|
304
|
+
private selectedShipping;
|
|
305
|
+
/** Real per-supplier shippings from the backend (empty → static fallback). */
|
|
306
|
+
private availableShippingsList;
|
|
307
|
+
/** Payment method names enabled for the sponsor (backend-configured).
|
|
308
|
+
* null = not loaded yet → all buttons render; [] = none enabled. */
|
|
309
|
+
private availableMethods;
|
|
310
|
+
/** Redirecting to the Stripe / Vipps hosted page. */
|
|
311
|
+
private stripeLoading;
|
|
312
|
+
private vippsLoading;
|
|
313
|
+
private form;
|
|
314
|
+
/** In-flight guard for loadAvailablePaymentMethods. */
|
|
315
|
+
private loadingPaymentMethods;
|
|
316
|
+
/** In-flight + attempted guards for loadAvailableShippings (change events
|
|
317
|
+
* fire per keystroke — without these, typing an address hammers the API). */
|
|
318
|
+
private loadingShippings;
|
|
319
|
+
private shippingsAttempted;
|
|
320
|
+
private boundOnCheckoutChange;
|
|
321
|
+
private boundOnPaymentComplete;
|
|
322
|
+
private boundOnPaymentError;
|
|
323
|
+
static styles: lit.CSSResult;
|
|
324
|
+
connectedCallback(): void;
|
|
325
|
+
/**
|
|
326
|
+
* Detect the redirect return from a hosted payment page (Stripe / Vipps /
|
|
327
|
+
* Klarna): the return URL carries vio_payment/vio_method/vio_sponsor. On
|
|
328
|
+
* success: clear that sponsor's cart, show the confirmation drawer, emit
|
|
329
|
+
* vio:payment-success (analytics/host hooks) and scrub the URL params.
|
|
330
|
+
*/
|
|
331
|
+
/** Backend checkout statuses that mean the payment went through. */
|
|
332
|
+
private static readonly PAID_STATUSES;
|
|
333
|
+
/** Statuses that mean the payment definitively did NOT go through. */
|
|
334
|
+
private static readonly FAILED_STATUSES;
|
|
335
|
+
/** Waits between GetCheckout attempts — PSP webhooks (Vipps especially) can
|
|
336
|
+
* land seconds after the shopper is redirected back, so a single immediate
|
|
337
|
+
* check misreads a successful charge as unpaid. ~20s total. */
|
|
338
|
+
private static readonly RETURN_VERIFY_DELAYS_MS;
|
|
339
|
+
/** Vipps polling cadence — up to 2 minutes. Its webhook can lag well behind
|
|
340
|
+
* the redirect; this window was verified against a live checkout. */
|
|
341
|
+
private static readonly VIPPS_STATUS_MAX_ATTEMPTS;
|
|
342
|
+
private static readonly VIPPS_STATUS_DELAY_MS;
|
|
343
|
+
/**
|
|
344
|
+
* Vipps redirects to our return URL the same way on cancel as on success —
|
|
345
|
+
* the URL alone can't tell them apart, and the generic checkout status can
|
|
346
|
+
* lag behind Vipps' own webhook. Poll Vipps' own state directly instead:
|
|
347
|
+
* AUTHORIZED confirms, CREATED means still processing (keep polling),
|
|
348
|
+
* anything else (including an unrecognized state) is treated as failed —
|
|
349
|
+
* fail-closed by default rather than guessing.
|
|
350
|
+
*/
|
|
351
|
+
private pollVippsStatus;
|
|
352
|
+
/** Shared outcome handler for both the generic (GetCheckout) and the
|
|
353
|
+
* Vipps-specific (GetVippsStatus) return-verification paths. */
|
|
354
|
+
private applyReturnOutcome;
|
|
355
|
+
private checkReturnPaymentStatus;
|
|
356
|
+
/** Remove our payment-return params from the URL without a reload. */
|
|
357
|
+
private cleanReturnQueryParams;
|
|
358
|
+
disconnectedCallback(): void;
|
|
359
|
+
updated(changed: Map<string, unknown>): void;
|
|
360
|
+
close(): void;
|
|
361
|
+
show(): void;
|
|
362
|
+
/** Fetch the real per-supplier shippings and preselect + persist the first. */
|
|
363
|
+
private loadAvailableShippings;
|
|
364
|
+
/** Whether all mandatory Leveringsadresse fields are filled. */
|
|
365
|
+
private get isAddressValid();
|
|
366
|
+
/** Whether a valid shipping option is selected (when shippings exist). */
|
|
367
|
+
private get hasSelectedShipping();
|
|
368
|
+
/** Which payment buttons to render — backend-configured per sponsor. */
|
|
369
|
+
private loadAvailablePaymentMethods;
|
|
370
|
+
/** Backend names arrive as e.g. "Apple Pay" — compare letters only. */
|
|
371
|
+
private methodEnabled;
|
|
372
|
+
private refreshApplePay;
|
|
373
|
+
private refreshKlarna;
|
|
374
|
+
/** Mount (or re-mount on amount change) the Klarna Payments widget. */
|
|
375
|
+
private mountKlarnaIfNeeded;
|
|
376
|
+
private unmountKlarna;
|
|
377
|
+
/** Shippings in effect: backend per-supplier when loaded, static otherwise. */
|
|
378
|
+
private get shippingList();
|
|
379
|
+
/** Selected shipping option (express flow). */
|
|
380
|
+
private get shippingOption();
|
|
381
|
+
/** Pick a shipping option → re-mounts the Klarna widget with the new total.
|
|
382
|
+
* Also persists the choice on the backend cart (per supplier). */
|
|
383
|
+
private onSelectShipping;
|
|
384
|
+
/** Switch the Klarna widget to a different payment_method_category. */
|
|
385
|
+
private onKlarnaCategory;
|
|
386
|
+
/** Authorize the selected Klarna method → backend creates the order. */
|
|
387
|
+
private onKlarnaAuthorize;
|
|
388
|
+
private onFieldChange;
|
|
389
|
+
private onApplePay;
|
|
390
|
+
private onPay;
|
|
391
|
+
/**
|
|
392
|
+
* Complete the order for the selected method. Apple Pay and Klarna Express
|
|
393
|
+
* finish inside their own sheet/popup; Stripe and Vipps redirect to their
|
|
394
|
+
* hosted pages (via onPay). Anything else confirms state-only.
|
|
395
|
+
*/
|
|
396
|
+
private onCompleteOrder;
|
|
397
|
+
/**
|
|
398
|
+
* Shared order confirmation — reached from both the plain "Betal" CTA
|
|
399
|
+
* (Vipps/Card/Klarna) and the Klarna Express authorize callback. Shows
|
|
400
|
+
* the confirmation screen, emits `vio:payment-success`, and clears the
|
|
401
|
+
* paid sponsor's cart so the badge + drawer reflect the purchase.
|
|
402
|
+
*/
|
|
403
|
+
private confirmOrder;
|
|
404
|
+
private methodLabel;
|
|
405
|
+
/** True for methods that complete via a separate sheet/widget, not the CTA. */
|
|
406
|
+
private isExpressMethod;
|
|
407
|
+
private orderTotal;
|
|
408
|
+
/** Shipping cost (major units) whenever a shipping option is in effect —
|
|
409
|
+
* express always has one; the normal flow has one once the backend
|
|
410
|
+
* shippings loaded (the choice IS persisted on the cart and charged). */
|
|
411
|
+
private shippingMajor;
|
|
412
|
+
/** Total to charge — items + shipping (express). */
|
|
413
|
+
private payTotalLabel;
|
|
414
|
+
render(): lit_html.TemplateResult<1>;
|
|
415
|
+
private renderConfirmation;
|
|
416
|
+
/** Klarna Payments widget panel: shipping + category chips + widget + pay button. */
|
|
417
|
+
private renderKlarnaPanel;
|
|
418
|
+
/** Express layout — only the Klarna widget + a compact order summary. */
|
|
419
|
+
private renderKlarnaExpress;
|
|
420
|
+
private renderCheckoutBody;
|
|
421
|
+
}
|
|
422
|
+
declare global {
|
|
423
|
+
interface HTMLElementTagNameMap {
|
|
424
|
+
'vio-checkout': VioCheckout;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Vio Design Tokens — editorial commerce aesthetic.
|
|
430
|
+
*
|
|
431
|
+
* Extracted from the Allermedia "Mote & Livsstil" reference (the target
|
|
432
|
+
* pilot site). Mood: Vogue-like fashion magazine — serif headlines,
|
|
433
|
+
* sans-serif body, near-monochrome palette, terracotta accent for
|
|
434
|
+
* editorial labels ("REDAKSJONENS FAVORITTER").
|
|
435
|
+
*
|
|
436
|
+
* Tokens are exposed both as a typed TS object (`VioTokens`) and as
|
|
437
|
+
* CSS custom properties on `:root` (auto-injected when this module is
|
|
438
|
+
* imported in a browser). Consumers can override individual variables
|
|
439
|
+
* to re-skin without touching SDK source.
|
|
440
|
+
*/
|
|
441
|
+
declare const VioTokens: {
|
|
442
|
+
readonly color: {
|
|
443
|
+
readonly text: {
|
|
444
|
+
readonly primary: "#0a0a0a";
|
|
445
|
+
readonly secondary: "#666666";
|
|
446
|
+
readonly tertiary: "#999999";
|
|
447
|
+
readonly onPrimary: "#ffffff";
|
|
448
|
+
};
|
|
449
|
+
readonly accent: {
|
|
450
|
+
readonly editorial: "#c14a3b";
|
|
451
|
+
};
|
|
452
|
+
readonly surface: {
|
|
453
|
+
readonly base: "#ffffff";
|
|
454
|
+
readonly muted: "#f2f2f2";
|
|
455
|
+
readonly hover: "#fafafa";
|
|
456
|
+
};
|
|
457
|
+
readonly border: {
|
|
458
|
+
readonly subtle: "#e5e5e5";
|
|
459
|
+
readonly default: "#cccccc";
|
|
460
|
+
};
|
|
461
|
+
};
|
|
462
|
+
readonly font: {
|
|
463
|
+
readonly serif: "\"Tinos\", \"Playfair Display\", Georgia, serif";
|
|
464
|
+
readonly sans: "\"Inter\", -apple-system, \"Helvetica Neue\", Arial, sans-serif";
|
|
465
|
+
};
|
|
466
|
+
readonly size: {
|
|
467
|
+
readonly xs: "12px";
|
|
468
|
+
readonly sm: "14px";
|
|
469
|
+
readonly md: "16px";
|
|
470
|
+
readonly lg: "20px";
|
|
471
|
+
readonly xl: "24px";
|
|
472
|
+
readonly '2xl': "32px";
|
|
473
|
+
readonly '3xl': "40px";
|
|
474
|
+
};
|
|
475
|
+
readonly weight: {
|
|
476
|
+
readonly regular: 400;
|
|
477
|
+
readonly medium: 500;
|
|
478
|
+
readonly semibold: 600;
|
|
479
|
+
readonly bold: 700;
|
|
480
|
+
};
|
|
481
|
+
readonly tracking: {
|
|
482
|
+
readonly label: "0.1em";
|
|
483
|
+
readonly title: "0";
|
|
484
|
+
};
|
|
485
|
+
readonly lineHeight: {
|
|
486
|
+
readonly tight: 1.1;
|
|
487
|
+
readonly snug: 1.3;
|
|
488
|
+
readonly normal: 1.5;
|
|
489
|
+
};
|
|
490
|
+
readonly space: {
|
|
491
|
+
readonly xs: "4px";
|
|
492
|
+
readonly sm: "8px";
|
|
493
|
+
readonly md: "16px";
|
|
494
|
+
readonly lg: "24px";
|
|
495
|
+
readonly xl: "32px";
|
|
496
|
+
readonly '2xl': "48px";
|
|
497
|
+
readonly '3xl': "64px";
|
|
498
|
+
};
|
|
499
|
+
readonly radius: {
|
|
500
|
+
readonly none: "0";
|
|
501
|
+
readonly sm: "2px";
|
|
502
|
+
readonly md: "4px";
|
|
503
|
+
readonly lg: "8px";
|
|
504
|
+
readonly full: "9999px";
|
|
505
|
+
};
|
|
506
|
+
};
|
|
507
|
+
/**
|
|
508
|
+
* Theming — override design tokens without touching SDK source.
|
|
509
|
+
*
|
|
510
|
+
* Every `<vio-*>` component reads colors/fonts via `var(--vio-color-accent,
|
|
511
|
+
* …)` etc. (shadow DOM inherits CSS custom properties from the light DOM,
|
|
512
|
+
* so this reaches every component including Apple Pay/Klarna/Vipps panels).
|
|
513
|
+
* `applyVioTheme` is the one supported way to set them: an inline style on
|
|
514
|
+
* `:root` (or a scoped element) always wins over the stylesheet rule
|
|
515
|
+
* `injectTokens` installs above, regardless of import/injection order — no
|
|
516
|
+
* specificity fights, no reaching for `!important`.
|
|
517
|
+
*
|
|
518
|
+
* A curated subset on purpose: colors + fonts are what a merchant/publisher
|
|
519
|
+
* actually wants a "brand settings" panel for. Spacing/radius/sizes stay
|
|
520
|
+
* internal — expose them here later if a real need shows up, don't
|
|
521
|
+
* speculatively surface the whole token set as public API.
|
|
522
|
+
*
|
|
523
|
+
* Host integrations (the Vio Config panel in Vev, a future Replit CMS
|
|
524
|
+
* panel, anything else) should call this ONE function — never set
|
|
525
|
+
* `--vio-*` custom properties by hand. If the injection mechanism changes
|
|
526
|
+
* later, only this function needs updating, not every panel that theme.
|
|
527
|
+
*/
|
|
528
|
+
interface VioThemeOverrides {
|
|
529
|
+
colorText?: string;
|
|
530
|
+
colorTextSecondary?: string;
|
|
531
|
+
colorTextTertiary?: string;
|
|
532
|
+
colorTextOnPrimary?: string;
|
|
533
|
+
colorAccent?: string;
|
|
534
|
+
colorSurface?: string;
|
|
535
|
+
colorSurfaceMuted?: string;
|
|
536
|
+
colorSurfaceHover?: string;
|
|
537
|
+
colorBorder?: string;
|
|
538
|
+
colorBorderDefault?: string;
|
|
539
|
+
fontSerif?: string;
|
|
540
|
+
fontSans?: string;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Apply (or clear) theme overrides. Pass `null`/`undefined` for a key to
|
|
544
|
+
* revert it to the SDK default instead of leaving a stale override behind
|
|
545
|
+
* — lets a host re-apply a full overrides object on every save without
|
|
546
|
+
* having to track which keys it previously set.
|
|
547
|
+
*
|
|
548
|
+
* `target` defaults to `document.documentElement` (`:root`, matching where
|
|
549
|
+
* `injectTokens` installs the defaults). Pass a specific element to scope
|
|
550
|
+
* the theme to one subtree instead of the whole page — e.g. multiple
|
|
551
|
+
* differently-branded sponsors on the same page.
|
|
552
|
+
*/
|
|
553
|
+
declare function applyVioTheme(overrides: VioThemeOverrides, target?: HTMLElement | null): void;
|
|
554
|
+
|
|
555
|
+
export { VioCart as V, VioCheckout as a, VioProduct as b, VioProductCarousel as c, VioProductDetail as d, type VioThemeOverrides as e, VioTokens as f, applyVioTheme as g };
|