@vio-live/web-sdk 0.33.0 → 0.35.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 CHANGED
@@ -14,6 +14,81 @@ How to keep this file: see [CONTRIBUTING → Changelog](CONTRIBUTING.md#changelo
14
14
  The details of each feature live in the [README](README.md); this file says *what*
15
15
  and *when*, and links there.
16
16
 
17
+ ## 0.35.0 — 2026-10-29
18
+
19
+ > 0.34.0 was never published on its own: its changes (section order of the product detail and layout tracking,
20
+ > below) ship in this version too.
21
+
22
+ ### Added
23
+ - **`vippsCartPayment: 'express' | 'checkout'`** (theme key, `--vio-vipps-cart-payment`) and the
24
+ attribute **`vipps-cart-payment`** on `<vio-cart>` and `<vio-product-detail>`. `express` is the
25
+ default; `checkout` restores the 0.31–0.34 behaviour exactly. See
26
+ [README → The hand-over to Vipps](README.md#the-hand-over-to-vipps-028).
27
+
28
+ ### Changed
29
+ - **The Vipps buttons pay the sponsor's whole cart with Vipps Express** (Angelo, 2026-10-29: «el
30
+ botón negro va con el nuestro y el botón de Vipps paga el carrito entero»). The black «Til
31
+ kassen» is the way into our checkout; Vipps goes to Vipps.
32
+ - **`<vio-cart>`**: the Vipps button starts Express for the sponsor's whole cart — every line of
33
+ that sponsor's backend cart, never another sponsor's — the same Express as the detail: the
34
+ «Åpner Vipps …» overlay, `vio:payment-redirect-*` with `source: 'cart'`, `add_payment_info`
35
+ once, the return from Vipps and its confirmation. It no longer dispatches
36
+ `vio:checkout-open`. One payment at a time (the overlay takes every tap; a second tap while
37
+ one is starting does nothing).
38
+ - **`<vio-product-detail>`**: «Kjøp nå med Vipps» with lines already in the brand's cart adds the
39
+ product (size and quantity) and pays the **whole cart** with Express, instead of opening our
40
+ checkout. The line under the button says what will be paid before the tap: «Betaler hele
41
+ handlekurven · {n} varer · {total}» (n = every unit with this product at the chosen quantity,
42
+ total = the cart's subtotal with it; shipping is chosen in Vipps). It follows the cart and the
43
+ quantity. With an empty cart nothing changes.
44
+ - Express off for the brand, or a cart that cannot have it (several suppliers, no rate): the
45
+ checkout opens with Vipps preselected, as before.
46
+ - **A redirect payment (Vipps, Stripe link) reuses only its own sponsor's open checkout.** A
47
+ payment for one sponsor's cart started while another sponsor's checkout was open would have
48
+ gone out on that checkout; it now creates its own.
49
+
50
+ ### ⚠️ Upgrade notes
51
+ - **Hosts listening to `vio:checkout-open` from the cart's Vipps button no longer get it** by
52
+ default: the cart goes to Vipps directly. «Til kassen» still sends `vio:checkout-open`, and the
53
+ Vipps button sends it only when Express cannot start (then with `paymentMethod: 'vipps'`). To
54
+ keep the old flow, set `vipps-cart-payment="checkout"` or `applyVioTheme({ vippsCartPayment:
55
+ 'checkout' })`.
56
+ - **`vio:payment-redirect-start` / `-cancel` / `-error` with `source: 'cart'` are back** (gone
57
+ since 0.31). A host that draws its own loader (`preventDefault()` on the start) gets them from
58
+ the cart too.
59
+ - The same for the detail with lines in the cart: no `vio:checkout-open` any more, the hand-over
60
+ events (`source: 'detail'`) instead.
61
+ - **Backend:** nothing new. shopcart already builds Express on the checkout's cart, every line;
62
+ it refuses Express (`express: false`, no payment) for a cart of several suppliers, and the
63
+ checkout opens instead.
64
+
65
+ ## 0.34.0 — 2026-10-29
66
+
67
+ ### Added
68
+ - **Section order of `<vio-product-detail>` and layout tracking** (asked by Replit for Mote &
69
+ Livsstil). `Vio.configureDetail({ layout: { id, revision, sections, experimentId?, variantId? } })`
70
+ orders the sections below the gallery for the page (`{ layout: null }` resets; open details
71
+ re-render); the element's `layout` property / attribute (JSON) wins. Section ids: `brand`,
72
+ `title`, `stock`, `price`, `options`, `purchase`, `highlights`, `description`,
73
+ `shippingReturns`; unknown ids are ignored, duplicates keep the first, the essentials (`title`,
74
+ `price`, `options`, `purchase`) are appended when missing. The real DOM is reordered, the same
75
+ on every screen. `el.appliedLayout`, `Vio.detailLayout()` and the event
76
+ `vio:detail-layout-applied` say what was applied. See
77
+ [README → Section order and layout tracking](README.md#section-order-and-layout-tracking-034).
78
+ - **The layout on the funnel events.** `view_item` of a non-default layout carries `layout_id`,
79
+ `layout_revision`, `experiment_id`, `variant_id` in its `context`. The detail stores the layout
80
+ on the cart line origin (`CartOrigin.layoutId`, `layoutRevision`, `experimentId`, `variantId`),
81
+ and every item of `add_to_cart`, `remove_from_cart`, `view_cart`, `begin_checkout`,
82
+ `add_payment_info` and `purchase` carries `layout_id`, `layout_revision`, `experiment_id`,
83
+ `experiment_variant_id` (`""` when none; `items[].variant_id` stays the product variant). A line
84
+ with units from two layouts is two items. Nothing goes to Commerce.
85
+
86
+ ### ⚠️ Upgrade notes
87
+ - **No change without configuration:** the detail renders exactly as in 0.33 and `view_item`
88
+ sends nothing new. The only difference on the wire: the items of the cart events now always
89
+ carry the four layout keys, empty (`""`) when the units came from no layout — the collector
90
+ ignores keys it does not know yet.
91
+
17
92
  ## 0.33.0 — 2026-10-28
18
93
 
19
94
  ### Fixed
package/README.md CHANGED
@@ -712,6 +712,7 @@ or on the element (`vio-product-detail { --vio-detail-logo-height: 48px; }`).
712
712
  | `cartWidth` / `checkoutWidth` | `--vio-cart-width` / `--vio-checkout-width` | `440px` | Drawer widths (desktop) |
713
713
  | `confirmLogoHeight` / `confirmTitleSize` / `confirmCheckBg` | `--vio-confirm-logo-height` / `--vio-confirm-title-size` / `--vio-confirm-check-bg` | `40px` / `24px` / green | The confirmation |
714
714
  | `redirectBg` / `redirectSpinner` | `--vio-redirect-bg` / `--vio-redirect-spinner` | translucent white / Vipps orange | The «Åpner Vipps …» overlay |
715
+ | `vippsCartPayment` | `--vio-vipps-cart-payment` | `express` | What the Vipps button of `<vio-cart>` and `<vio-product-detail>` pays when the cart has lines: `express` = Vipps Express for the sponsor's whole cart; `checkout` = our checkout with Vipps preselected (0.31–0.34). See [The hand-over to Vipps](#the-hand-over-to-vipps-028) |
715
716
 
716
717
  Also settable as CSS only: `--vio-detail-logo-max-width` (`220px`), `--vio-detail-image-position`,
717
718
  `--vio-detail-image-radius`, `--vio-detail-thumb-fit` (`cover`), `--vio-detail-thumb-border-width`,
@@ -762,6 +763,7 @@ whole page through `applyVioTheme` (theme key → custom property). The element
762
763
  | `highlights` | — | `[{ icon, title, text }]` (JSON) | The brand's promises under the buy zone. By default the brand's own (dashboard Settings → Brand, sponsor block `highlights`); this list replaces them on the page. Icons: `plane heart shirt truck return leaf star shield mailbox home pin clock`, or an image URL. `detailHighlightsDisplay: 'none'` hides the strip |
763
764
  | `delivery-time` | — | text, e.g. `2-5 virkedager` | «Leveringstid …» in «Frakt & retur» for a brand whose rates do not say it (each rate's own text in Commerce — «Leveringstid 1-5 dager» — wins) |
764
765
  | `carrier-logos` | — | `{ "Bring": "https://…" }` (JSON) | Carrier logos by the name the rate starts with («Bring - Pakke i postkasse»), for rates without their own logo in Commerce (`logo_url`, which wins); otherwise a line icon by the kind of delivery |
766
+ | `vipps-cart-payment` | `vippsCartPayment` | `express` (default) · `checkout` | «Kjøp nå med Vipps» when the brand's cart already has lines: the product is added and the **whole cart** goes to Vipps Express, the line under the button saying «Betaler hele handlekurven · n varer · total»; or (`checkout`) the checkout opens first, Vipps preselected. Also on `<vio-cart>` (its Vipps button) |
765
767
  | — | `detailShippingReturnsDisplay` | `block` (default) · `none` | «Frakt & retur»: one row per carrier (logo or icon, carrier, how it delivers, price), the delivery time, the right of withdrawal and the brand's pages |
766
768
 
767
769
  The sheet closes with a pull down from its top bar, or from anywhere while its content is at the top.
@@ -785,6 +787,111 @@ line above the name). CSS only: `--vio-card-border-hover-color`, `--vio-card-add
785
787
  `--vio-card-retailer-{display,size,tracking,weight,color,margin}`,
786
788
  `--vio-card-description-{size,color,gap}`, `--vio-card-accent-text`.
787
789
 
790
+ ### Section order and layout tracking (0.34)
791
+
792
+ The sections of `<vio-product-detail>` **below the gallery** can be put in any order, and the
793
+ layout the shopper really saw travels with every event of the funnel — so a host can run an
794
+ experiment on the product page and read its conversion. The header (selling brand + close) and
795
+ the gallery stay where they are. Without any configuration nothing changes.
796
+
797
+ ```js
798
+ // The whole page (every detail open now and later):
799
+ Vio.configureDetail({
800
+ layout: {
801
+ id: 'pdp-sizes-first', // required
802
+ revision: '3',
803
+ sections: ['title', 'stock', 'price', 'options', 'purchase', 'description', 'shippingReturns'],
804
+ experimentId: 'pdp-2026-11', // optional
805
+ variantId: 'B', // optional — the experiment variant
806
+ },
807
+ })
808
+ Vio.configureDetail({ layout: null }) // back to the default
809
+
810
+ // One element (wins over the page): the property, or the attribute as JSON.
811
+ detail.layout = { id: 'pdp-b', revision: '1', sections: ['price', 'title', 'options', 'purchase'] }
812
+ // <vio-product-detail layout='{"id":"pdp-b","revision":"1","sections":["price","title","options","purchase"]}'>
813
+
814
+ detail.appliedLayout // what this detail applies
815
+ Vio.detailLayout() // what a detail without its own layout applies (the page's or the default)
816
+ ```
817
+
818
+ **Section ids**
819
+
820
+ | Id | What |
821
+ |---|---|
822
+ | `brand` | The product's brand line (`.brand`) |
823
+ | `title` | The product name |
824
+ | `stock` | «På lager» / «Få igjen» / «Utsolgt» |
825
+ | `price` | Price + compare-at price |
826
+ | `options` | The size / option groups and their prompt («Velg størrelse») |
827
+ | `purchase` | The SDK's buy group, never split: «Frakt fra…», Vipps / Apple Pay / the other ways to pay, quantity + «Legg i handlekurv», «Du handler direkte hos …» and the terms links |
828
+ | `highlights` | The brand's promises |
829
+ | `description` | «Beskrivelse» |
830
+ | `shippingReturns` | «Frakt & retur» |
831
+
832
+ Default order — exactly the DOM before 0.34: `brand, title, stock, price, options, purchase,
833
+ highlights, description, shippingReturns` (title and stock on one row; the options inside the
834
+ buy zone; highlights, description and «Frakt & retur» after it).
835
+
836
+ **Rules**
837
+
838
+ - Ids, `revision`, `experimentId`, `variantId`: strings of at most 64 characters of
839
+ `[A-Za-z0-9._:-]`. An invalid `id` (or `sections` that is not an array) ignores the layout, with
840
+ one `console.warn`; an invalid `revision` / `experimentId` / `variantId` is treated as absent.
841
+ - Unknown section ids are ignored; a duplicate: the first wins.
842
+ - Essentials — `title`, `price`, `options`, `purchase` — are appended in that order when missing.
843
+ Any other section left out is not rendered.
844
+ - `title` immediately followed by `stock` keep today's single row; otherwise each is its own block.
845
+ A run of `title` / `stock` / `price` is one `.headline` block.
846
+ - `options` immediately before `purchase` render inside the buy zone (`.buy`), as today — so a
847
+ sticky buy zone still holds them. Anywhere else they are their own block
848
+ (`.buy-options.options-apart`, same `part="buy-options"`), and the buy buttons still require the
849
+ choice: the same «Velg størrelse» prompt, and the options are scrolled into view.
850
+ - The real DOM is reordered (no CSS `order`), the same on phone and desktop. Every section after
851
+ `purchase` goes inside `.purchase`, as the description does by default: a sticky buy zone
852
+ (`detailBuyPosition: 'sticky'`) may slide over them, never over what comes before it.
853
+ `detailHighlightsDisplay`, `detailShippingReturnsDisplay`, `description-mode` and `gallery`
854
+ apply as before.
855
+
856
+ **Applied layout** — `el.appliedLayout`, `Vio.detailLayout()` and the event
857
+ `vio:detail-layout-applied` (bubbles, composed; React: `onLayoutApplied`), dispatched each time a
858
+ detail renders a product with a layout it was not yet shown with (every open, every change while
859
+ open; the detail also carries `productId` and `sponsorId`):
860
+
861
+ ```ts
862
+ {
863
+ id: string, revision: string, experimentId?: string, variantId?: string,
864
+ sections: string[], // what is rendered, in order
865
+ adjusted: Array<{ section: string, reason: 'unknown' | 'duplicate' | 'added-essential' }>,
866
+ source: 'element' | 'global' | 'default',
867
+ }
868
+ // no configuration:
869
+ { id: 'default', revision: '0', sections: [/* the default order */], adjusted: [], source: 'default' }
870
+ ```
871
+
872
+ **Tracking contract** (shared with the collector). Only a non-default layout (`source`
873
+ `element` / `global`) adds anything; the default one sends nothing new on `view_item`.
874
+
875
+ | Where | Fields | Value |
876
+ |---|---|---|
877
+ | `view_item` — `context` (next to `campaign_id`, `location_id`) | `layout_id`, `layout_revision`, `experiment_id`, `variant_id` | The layout the detail is shown with. `variant_id` here is the **experiment** variant (`context` has no product variant). Absent for the default layout; `experiment_id` / `variant_id` absent when the layout has none |
878
+ | Every item of `add_to_cart`, `remove_from_cart`, `view_cart`, `begin_checkout`, `add_payment_info`, `purchase` — `commerce.items[]` | `layout_id`, `layout_revision`, `experiment_id`, `experiment_variant_id` | The layout the units were added under (their cart line origin). Always present on these items: `""` when the units came from no layout (a card, the default layout). `items[].variant_id` stays the **product** variant — hence `experiment_variant_id` |
879
+
880
+ - The detail stores the layout on the cart line origin when it adds to the cart (any buy button):
881
+ `CartOrigin.layoutId`, `layoutRevision`, `experimentId`, `variantId` (the experiment variant),
882
+ taken from the layout shown at that moment. They persist with the cart (`localStorage`), so they
883
+ survive the return from Vipps / Stripe, a reload and another tab; changing the page's layout
884
+ later never rewrites lines already in the cart.
885
+ - A line whose units came from several layouts is split per layout into several items (each with
886
+ its quantity), the same way the units are split per campaign / slot (`Vio.cartSlices()` items
887
+ carry `layoutId`, `layoutRevision`, `experimentId`, `experimentVariantId` when they have a
888
+ layout). A purchase is never credited whole to the last product opened.
889
+ - `vio:added-to-cart` from the detail carries `layout: { layoutId, layoutRevision, experimentId?,
890
+ variantId? }` for a non-default layout (`variantId` of the event itself stays the product's).
891
+ - Internal traffic stays excluded as before; campaign, sponsor, channel, content and slot are
892
+ unchanged. The layout is **not** sent to Commerce (the line origin's wire to shopcart has no
893
+ layout fields): the collector joins `order_paid` to these events by `checkout_id`.
894
+
788
895
  ### The hand-over to Vipps (0.28)
789
896
 
790
897
  Between the tap on Vipps (detail, cart or checkout) and the Vipps page, the SDK shows
@@ -800,24 +907,41 @@ tap, so a second payment cannot start. Events on `document`:
800
907
  `source` is `'detail'`, `'cart'` or `'checkout'`. The overlay is `.vio-redirect-overlay`
801
908
  in the light DOM.
802
909
 
803
- **The detail's Vipps button pays the whole cart.** «Kjøp nå med Vipps» on the product
804
- detail adds the product to the sponsor's cart and the checkout is built on that cart —
805
- every line in it. It goes straight to Vipps only when the cart was **empty**; with lines
806
- already in it, the product is added and the checkout opens first (`vio:checkout-open`
807
- with `paymentMethod: 'vipps'`, no hand-over events), every line and the total on screen,
808
- so what the Vipps page lists is what the shopper saw.
809
-
810
- **Two Vipps paths (2026-10-28).** The detail's «Kjøp nå med Vipps» is **Express**: the app
811
- fills in name and address. The **cart's** Vipps button opens the checkout with Vipps
812
- preselected, and the checkout's Vipps is always the **standard** payment, even when the
910
+ **The Vipps buttons pay the sponsor's whole cart with Express (2026-10-29).** The black
911
+ «Til kassen» in `<vio-cart>` is the way into our checkout; the Vipps buttons go to Vipps:
912
+
913
+ - **`<vio-cart>`**: the Vipps button starts **Vipps Express for the whole cart** of the
914
+ sponsor (every line of its backend cart, never another sponsor's) — the hand-over above with
915
+ `source: 'cart'`, `add_payment_info` once, and the return from Vipps and its confirmation
916
+ exactly as from the detail. No `vio:checkout-open`.
917
+ - **`<vio-product-detail>`**: «Kjøp nå med Vipps» adds the product (size, quantity) and starts
918
+ Express for the sponsor's cart. With an **empty** cart that is the product alone (the line
919
+ under the button: «Vipps fyller inn navn og adresse – du godkjenner og betaler i appen»). With
920
+ **lines already in it**, the whole cart is paid, and the line under the button says so before
921
+ the tap: «Betaler hele handlekurven · {n} varer · {total}» — n = every unit including this
922
+ product at the chosen quantity, total = the cart's subtotal with it (shipping is chosen in
923
+ Vipps).
924
+
925
+ When the brand has Express off, or the cart cannot have it (several suppliers, digital goods,
926
+ no rate for the country — shopcart answers `express: false` and creates nothing), the checkout
927
+ opens with Vipps preselected (`vio:payment-redirect-cancel` with `'express-off'` /
928
+ `'not-started'`, then `vio:checkout-open`).
929
+
930
+ **The option: `vipps-cart-payment` / `vippsCartPayment`.** `express` is the default.
931
+ `checkout` brings back 0.31–0.34 exactly: the cart's Vipps button opens the checkout with Vipps
932
+ preselected (`vio:checkout-open`, `paymentMethod: 'vipps'`, no hand-over), and the detail's
933
+ does the same when the cart already has lines (product added first); an empty cart is Express
934
+ either way.
935
+
936
+ **The checkout's Vipps is the standard payment** (2026-10-28), even when the
813
937
  brand has Express on. Our form collects name, e-mail, **Mobilnummer**, address and the
814
938
  delivery with its price before paying. The mobile is required for Vipps only and accepts any
815
939
  market's number: the `+` prefix or the shipping country sets the calling code. The delivery is
816
940
  saved on the cart first; if that fails, no payment starts. The payment goes out with
817
941
  `express: false`, and the address (with `phone` + `phone_code`) is on the checkout's shipping
818
- and billing address. **Apple Pay on the detail follows the same
819
- rule**: its sheet's amount is fixed before the tap, so it opens only on an empty cart, for the
820
- product × the chosen quantity; otherwise the checkout opens (`paymentMethod: 'apple-pay'`).
942
+ and billing address. **Apple Pay on the detail** has its sheet's amount fixed before the tap,
943
+ so it opens only on an empty cart, for the product × the chosen quantity; otherwise the
944
+ checkout opens (`paymentMethod: 'apple-pay'`).
821
945
 
822
946
  ### What the shopper pays is what the page shows — the cart, the server, the other tabs
823
947
 
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=chunk-3YFJFDSN.js.map
3
+ //# sourceMappingURL=chunk-3YFJFDSN.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-QGJUBVH3.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-3YFJFDSN.js"}