@vio-live/web-sdk 0.26.1 → 0.28.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 +289 -0
- package/README.md +184 -0
- package/dist/chunk-67QESEV2.js +3 -0
- package/dist/{chunk-NCP76AXW.js.map → chunk-67QESEV2.js.map} +1 -1
- package/dist/{chunk-NXIYIY2D.js → chunk-AFJ5M53F.js} +492 -57
- package/dist/chunk-AFJ5M53F.js.map +1 -0
- package/dist/{chunk-6Q2M3MIK.js → chunk-Q2NALS7P.js} +1053 -165
- package/dist/chunk-Q2NALS7P.js.map +1 -0
- package/dist/core/index.d.ts +229 -19
- 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 -2
- package/dist/react/index.js +3 -3
- package/dist/{tokens-Bn7fK9ta.d.ts → tokens-y0KzN7bP.d.ts} +153 -3
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +3 -3
- package/package.json +3 -2
- package/dist/chunk-6Q2M3MIK.js.map +0 -1
- package/dist/chunk-NCP76AXW.js +0 -3
- package/dist/chunk-NXIYIY2D.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# Changelog — `@vio-live/web-sdk`
|
|
2
|
+
|
|
3
|
+
What changed in each version, and **what a host has to know before upgrading**.
|
|
4
|
+
Every version has up to four parts:
|
|
5
|
+
|
|
6
|
+
- **Added** — new things you can use.
|
|
7
|
+
- **Changed** — the same feature behaves differently (screens, events, data on the wire).
|
|
8
|
+
- **Fixed** — bugs that are gone.
|
|
9
|
+
- **⚠️ Upgrade notes** — what a host (Vev, Replit, a custom page) must do, check or
|
|
10
|
+
watch for. Read these before bumping, even for a patch.
|
|
11
|
+
|
|
12
|
+
Newest first. `Unreleased` is merged on `main` but **not published to npm** yet.
|
|
13
|
+
How to keep this file: see [CONTRIBUTING → Changelog](CONTRIBUTING.md#changelog).
|
|
14
|
+
The details of each feature live in the [README](README.md); this file says *what*
|
|
15
|
+
and *when*, and links there.
|
|
16
|
+
|
|
17
|
+
## Unreleased
|
|
18
|
+
|
|
19
|
+
Nothing yet.
|
|
20
|
+
|
|
21
|
+
## 0.28.0 — 2026-10-22
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- **The detail's layout is public**: 31 new `applyVioTheme` keys, each a `--vio-*` property
|
|
25
|
+
(logo height and zoom, image fit / aspect / max height / zoom / side margin, thumbnail size and gap, option
|
|
26
|
+
height and gap, button height and radius, a sticky buy zone, cart / checkout widths,
|
|
27
|
+
confirmation logo and title, the redirect overlay), plus `::part` names on every piece of
|
|
28
|
+
the detail and the confirmation. The same keys serve our demo, Replit and Vev's Config block.
|
|
29
|
+
See [README → Layout](README.md#layout-of-the-detail-cart-checkout-and-confirmation-028).
|
|
30
|
+
- **The hand-over to Vipps**: a full-screen «Åpner Vipps …» with a soft fade from the tap to
|
|
31
|
+
the Vipps page (detail, cart, checkout), which also stops a second tap; events
|
|
32
|
+
`vio:payment-redirect-start` (cancelable — draw your own loader), `vio:payment-redirect-error`
|
|
33
|
+
and `vio:payment-redirect-cancel`. See [README → The hand-over to Vipps](README.md#the-hand-over-to-vipps-028).
|
|
34
|
+
- `commerce.checkout_id` on every `purchase` the SDK can tie to a checkout, and
|
|
35
|
+
`props.order_id_kind` (`'order'` | `'checkout'`).
|
|
36
|
+
- `vio:payment-success` may carry `orderNumber` when the checkout already knows it.
|
|
37
|
+
- `purchaseIdsOf(detail)` → `{ orderId, checkoutId, orderIdKind }` (from `/core`) for
|
|
38
|
+
hosts with their own analytics sink.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- **Product detail** (asked by Mote & Livsstil): the selling brand's logo (its name without
|
|
42
|
+
one; a square logo with the name beside it) sits **top left beside ✕**, no longer above the
|
|
43
|
+
image; every main image shows in **one 4:5 box it fills** (`cover`) — no white bars, no jump
|
|
44
|
+
between photos of different shapes; thumbnails are **one row** (swipe, arrows with a mouse,
|
|
45
|
+
the selected one kept in view) instead of wrapping; sizes are **compact** (36px); **Vipps
|
|
46
|
+
comes first**, above quantity and «Legg i handlekurv»; the **description folds** («Beskrivelse»
|
|
47
|
+
opens on a tap); under the buy buttons, **«Du handler direkte hos {brand}.»**, with links to the
|
|
48
|
+
brand's «Kjøpsvilkår» and «Angrerett og retur» when its sponsor block carries `termsUrl` /
|
|
49
|
+
`refundPolicyUrl` (https only; `Vio.sponsorIdentity()` returns them too). On desktop the
|
|
50
|
+
description comes before the buy zone, which stays pinned at the bottom of the panel; phones
|
|
51
|
+
keep buy zone, then description.
|
|
52
|
+
- The confirmation's brand logo is 40px tall (was max 32px).
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
- **Phones: the detail, the cart and the checkout close by pulling them down**, from the grab bar
|
|
56
|
+
or the top bar (logo / title and ✕): the sheet follows the finger, and closes when let go far
|
|
57
|
+
enough or with a flick, or springs back. Before, the grab bar only answered a tap — pulling it
|
|
58
|
+
scrolled the sheet. The checkout never closes this way mid-payment.
|
|
59
|
+
- **Phones: the page behind stays put while a sheet is open.** A swipe past the end of the
|
|
60
|
+
detail, cart or checkout used to scroll the article underneath. The page is locked while any
|
|
61
|
+
sheet is open and comes back to the same place when the last one closes. Desktop drawers are
|
|
62
|
+
unchanged.
|
|
63
|
+
- **The `purchase` event's `commerce.order_id` is Vio's order number** (`4415`), not the
|
|
64
|
+
checkout's uuid. Before, every purchase paid through a redirect (Vipps, Klarna, hosted
|
|
65
|
+
Stripe) or whose order a webhook creates carried the uuid, so Commerce could never net
|
|
66
|
+
a cancellation or refund (`order_cancelled` / `order_refunded`, ADR-0036) against it.
|
|
67
|
+
([#88](https://github.com/vio-live/vio-web-sdk/pull/88))
|
|
68
|
+
|
|
69
|
+
### ⚠️ Upgrade notes
|
|
70
|
+
- **The product detail looks different** after upgrading (see *Changed*). A host that styled
|
|
71
|
+
`.sponsor-header` / `.topbar-brand` from outside: they are gone — use `::part(sponsor)`.
|
|
72
|
+
Hosts that want the old look back: `detailImageAspect: '1 / 1'`, `detailImageFit: 'contain'`,
|
|
73
|
+
`detailOptionHeight: '42px'`, `detailBuyPosition: 'static'`. The `4 / 5` box crops photos of
|
|
74
|
+
other shapes: pick the proportion that fits the catalog, or `contain`.
|
|
75
|
+
- A host with its own «loading Vipps» screen: call `preventDefault()` on
|
|
76
|
+
`vio:payment-redirect-start`, or the SDK's overlay shows on top of yours.
|
|
77
|
+
- **The `purchase` may leave up to 10 s after `vio:payment-success`**: the SDK asks the
|
|
78
|
+
checkout for the order number at once and then after 0.5, 1, 2, 3 and 3 s. The confirmation screen, the
|
|
79
|
+
cart and the payment never wait — only the analytics event. If the page hides first,
|
|
80
|
+
the event leaves at once with the checkout id (`order_id_kind: 'checkout'`).
|
|
81
|
+
- **Hosts with their own sink (Vev):** read the ids with `purchaseIdsOf(detail)` and wait
|
|
82
|
+
for the number the same way (`Vio.checkout.getOrderReference(checkoutId)`), or your
|
|
83
|
+
purchases keep the uuid.
|
|
84
|
+
- **Dashboards / queries** that joined purchases by checkout uuid: use `checkout_id`.
|
|
85
|
+
- **Hosts that lock the page themselves when a Vio sheet opens** (phones): the SDK does it now —
|
|
86
|
+
`position: fixed` on `<body>` at the current offset, its own inline styles restored and the
|
|
87
|
+
scroll put back on close. Drop yours, or make sure it does not undo the body's styles while a
|
|
88
|
+
sheet is still open.
|
|
89
|
+
- One `purchase` per checkout per page load: a second `vio:payment-success` for the same
|
|
90
|
+
checkout adds nothing.
|
|
91
|
+
- **«Kjøpsvilkår · Angrerett og retur» need the backend**: the links show only when the
|
|
92
|
+
sponsor block carries `termsUrl` / `refundPolicyUrl`, which the campaigns service sends
|
|
93
|
+
once the brand fills them in (Settings → Brand → Shopper policies). Until then the
|
|
94
|
+
sentence «Du handler direkte hos {brand}.» shows alone. Nothing to do on the host.
|
|
95
|
+
|
|
96
|
+
## 0.27.0 — 2026-10-21
|
|
97
|
+
|
|
98
|
+
### Added
|
|
99
|
+
- **The brand that sells is named** — name and logo — in the product detail (top),
|
|
100
|
+
the checkout header («Du handler hos …») and the confirmation («Du har handlet hos
|
|
101
|
+
…»), also after a Vipps return and a reload. ([#91](https://github.com/vio-live/vio-web-sdk/pull/91))
|
|
102
|
+
- `Vio.sponsorIdentity(sponsorId, { checkoutId? })` → `{ id, name, logoUrl, avatarUrl } | null`.
|
|
103
|
+
- `vio:checkout-open` and `vio:payment-success` carry `sponsor: { id, name, logoUrl }`.
|
|
104
|
+
- `Vio.checkout.getOrderReference(checkoutId, sponsorId?)` also answers `sponsor`.
|
|
105
|
+
- `sponsorIdentityFrom(bootstrap, sponsorId)` exported from `/core`.
|
|
106
|
+
|
|
107
|
+
### Changed
|
|
108
|
+
- The detail's top line names the seller only once (no repeated seller name).
|
|
109
|
+
- The checkout remembers, per tab, which sponsor each checkout belongs to
|
|
110
|
+
(`sessionStorage` `vio.checkoutSponsor.v1`, last 20 checkouts).
|
|
111
|
+
|
|
112
|
+
### ⚠️ Upgrade notes
|
|
113
|
+
- The sponsor in the checkout and its confirmation is **the checkout's** (the cart's
|
|
114
|
+
brand), never the article's: an article of one brand can sell another brand's product.
|
|
115
|
+
- A host confirmation page that must survive a reload keeps `checkoutId` and `sponsorId`
|
|
116
|
+
in its own URL and asks `getOrderReference` again: the SDK cleans its return
|
|
117
|
+
parameters after using them. Recipe in [README → Sponsor identity](README.md#sponsor-identity--name-and-logo).
|
|
118
|
+
- New marks are styled with `--vio-*` tokens and exposed as `part="sponsor"`.
|
|
119
|
+
|
|
120
|
+
## 0.26.1 — 2026-10-21
|
|
121
|
+
|
|
122
|
+
### Fixed
|
|
123
|
+
- A surface with **no active campaign** still names its channel sponsor in the
|
|
124
|
+
attribution (before, the sponsor came out empty). ([#89](https://github.com/vio-live/vio-web-sdk/pull/89))
|
|
125
|
+
|
|
126
|
+
## 0.26.0 — 2026-10-20
|
|
127
|
+
|
|
128
|
+
### Added
|
|
129
|
+
- **The order is born attributed** (ADR-0035 B): every `AddItem` / quantity increase
|
|
130
|
+
carries the line's `origin` (`campaign_id`, slot, `component_kind`, `content_url`,
|
|
131
|
+
`content_title`), and `CreateCheckout` / the last `UpdateCheckout` carry the checkout's
|
|
132
|
+
`attribution`. See [README → Attribution on the order](README.md#attribution-on-the-order--what-commerce-receives-026).
|
|
133
|
+
|
|
134
|
+
### Changed
|
|
135
|
+
- `content_url` is sent **without query string or fragment**.
|
|
136
|
+
- Privacy mode `minimal` sends neither the page nor the slot.
|
|
137
|
+
- The surface's privacy mode is preloaded, so the first events already respect it.
|
|
138
|
+
- `vio:payment-success` never waits longer than the page stays open; one reference
|
|
139
|
+
request per checkout.
|
|
140
|
+
- The landing `referrer` has no query and is dropped when it is a self-referral;
|
|
141
|
+
`buyer_id` only with consent.
|
|
142
|
+
|
|
143
|
+
### ⚠️ Upgrade notes
|
|
144
|
+
- **Against a gateway without the new fields** (`origin` on `LineItemInput` / `UpdateItem`,
|
|
145
|
+
`attribution` on the checkout) the SDK resends the operation without them once per page
|
|
146
|
+
load and says so in the console: the cart works as in 0.25, **but the order is not
|
|
147
|
+
attributed**. Deploy the backend for the attribution to reach Commerce.
|
|
148
|
+
- Hosts that add to the cart with their own button should pass
|
|
149
|
+
`origin: Vio.attribution(slot)` (or `e.detail.origin`) to `Vio.cart.addProduct`.
|
|
150
|
+
|
|
151
|
+
## 0.25.0 — 2026-10-19
|
|
152
|
+
|
|
153
|
+
### Added
|
|
154
|
+
- The checkout asks the gateway for **`buyer_ref`** (a keyed hash of the buyer's email)
|
|
155
|
+
and puts it on `vio:payment-success` → analytics `buyer_id`.
|
|
156
|
+
- Every event carries a **`client`** block: language, the landing's referrer and utm
|
|
157
|
+
parameters, screen size and time zone (see [README → The visit and the buyer](README.md#the-visit-and-the-buyer--client-and-buyer_id-025)).
|
|
158
|
+
- Slot manifests carry `kind`; every element carries `component_template_id`.
|
|
159
|
+
|
|
160
|
+
### Changed
|
|
161
|
+
- A slot assigned `page` counts as the page's campaign when its sponsor is known.
|
|
162
|
+
|
|
163
|
+
### ⚠️ Upgrade notes
|
|
164
|
+
- Never store an email from the events: `buyerRef` is the reference to keep.
|
|
165
|
+
- With `requireConsent`, nothing is written (landing memory, ids) until `setConsent(true)`,
|
|
166
|
+
and `buyer_id` is not sent.
|
|
167
|
+
|
|
168
|
+
## 0.24.0 — 2026-10-16
|
|
169
|
+
|
|
170
|
+
### Added
|
|
171
|
+
- **The page says which campaign it belongs to** (ADR-0034, amendment 3):
|
|
172
|
+
`Vio.init({ campaignId })`, `<body data-vio-campaign="…">`, or `vio-campaign` on one
|
|
173
|
+
element. See [README → Page campaign](README.md#page-campaign--an-editorial-page-that-belongs-to-one-campaign).
|
|
174
|
+
|
|
175
|
+
### Fixed
|
|
176
|
+
- The cart's reconcile skips the mutation that is answering; card context per product;
|
|
177
|
+
title and URL capped to the collector's limits.
|
|
178
|
+
|
|
179
|
+
### ⚠️ Upgrade notes
|
|
180
|
+
- The attributes are read **when an element mounts**: in an SPA, update `data-vio-campaign`
|
|
181
|
+
(or call `Vio.init` again) before mounting the next article's blocks.
|
|
182
|
+
- A paused, unknown or non-numeric campaign falls through to the next level.
|
|
183
|
+
|
|
184
|
+
## 0.23.1 — 2026-10-16
|
|
185
|
+
|
|
186
|
+
### Added
|
|
187
|
+
- **Automatic slot identity**: with `data-vio-scope` (and `data-vio-scope-name`) on an
|
|
188
|
+
ancestor, every element registers itself (`article-123-carousel-1`); `Vio.attribution()`.
|
|
189
|
+
- The cart remembers the origin of each unit; a purchase is split per campaign.
|
|
190
|
+
|
|
191
|
+
### Fixed
|
|
192
|
+
- Collector limits respected; a one-campaign purchase is one slice; stable origins.
|
|
193
|
+
|
|
194
|
+
## 0.23.0 — 2026-10-16
|
|
195
|
+
|
|
196
|
+
### Added
|
|
197
|
+
- **Slots**: the page declares its slots and each one resolves its campaign from the
|
|
198
|
+
dashboard (ADR-0027 / 0034). See [README → Slots](README.md#slots--where-things-happen-on-a-page).
|
|
199
|
+
|
|
200
|
+
### Fixed
|
|
201
|
+
- The checkout waits for pending cart mutations before creating a checkout or a payment link.
|
|
202
|
+
- GraphQL cart calls time out after 15 s.
|
|
203
|
+
|
|
204
|
+
### ⚠️ Upgrade notes
|
|
205
|
+
- `Vio.init({ platform: 'web' | 'custom-web' | 'vev' })` — default `'web'`. Only that
|
|
206
|
+
platform's assignments come back.
|
|
207
|
+
|
|
208
|
+
## 0.22.0 — 2026-10-16
|
|
209
|
+
Version bump only; same code as 0.21.1.
|
|
210
|
+
|
|
211
|
+
## 0.21.1 — 2026-10-16
|
|
212
|
+
|
|
213
|
+
### Fixed
|
|
214
|
+
- Quantity changes are serialized and coalesced per cart line: quick taps on + / − end on
|
|
215
|
+
the quantity the shopper sees, with one request in flight at a time.
|
|
216
|
+
|
|
217
|
+
## 0.21.0 — 2026-10-15
|
|
218
|
+
|
|
219
|
+
### Fixed
|
|
220
|
+
- Analytics: `order_id` only with Vio ids, the cart's `value`, and `checkout_id` on
|
|
221
|
+
payment returns.
|
|
222
|
+
|
|
223
|
+
## 0.20.5 — 2026-10-15
|
|
224
|
+
|
|
225
|
+
### Fixed
|
|
226
|
+
- Analytics: the real `campaign_id`, a consistent `order_id` on `purchase`, and
|
|
227
|
+
`X-Api-Key` on the events request.
|
|
228
|
+
|
|
229
|
+
## 0.20.4 — 2026-10-14
|
|
230
|
+
|
|
231
|
+
### Fixed
|
|
232
|
+
- A bootstrap request started before a re-init no longer lands in the new configuration.
|
|
233
|
+
|
|
234
|
+
## 0.20.3 — 2026-10-12
|
|
235
|
+
|
|
236
|
+
### Changed
|
|
237
|
+
- A cart already paid in another tab reads «already paid», not «failed».
|
|
238
|
+
|
|
239
|
+
## 0.20.1 / 0.20.2 — 2026-10-12
|
|
240
|
+
|
|
241
|
+
### Fixed
|
|
242
|
+
- The dialogs' focus trap keeps keyboard focus inside in Safari, and pulls it back when the
|
|
243
|
+
browser walks out of the dialog.
|
|
244
|
+
|
|
245
|
+
## 0.20.0 — 2026-10-09
|
|
246
|
+
|
|
247
|
+
### Added
|
|
248
|
+
- Keyboard and focus in the detail, cart and checkout: focus moves in on open, Tab stays
|
|
249
|
+
inside, Esc closes, focus returns on close.
|
|
250
|
+
|
|
251
|
+
### Changed
|
|
252
|
+
- The detail and the cart read the corner radius from the theme (`--vio-radius-*`).
|
|
253
|
+
- When a payment cannot start, the shopper is told what to do (empty cart, cart changed in
|
|
254
|
+
another tab, already paid) instead of a raw error.
|
|
255
|
+
|
|
256
|
+
## 0.19.0 — 2026-10-08
|
|
257
|
+
|
|
258
|
+
### Changed
|
|
259
|
+
- The product detail's header shows the seller's **Company name** (Vio Commerce →
|
|
260
|
+
Settings → Company), the brand when there is none. *(Superseded in 0.27.0 by the
|
|
261
|
+
sponsor's name and logo.)*
|
|
262
|
+
|
|
263
|
+
### ⚠️ Upgrade notes
|
|
264
|
+
- The name is read with its own query outside the product cache (`useCache: false`): a
|
|
265
|
+
narrow query written to the gateway's product cache would hand the next product query a
|
|
266
|
+
product without price or images.
|
|
267
|
+
|
|
268
|
+
## 0.18.0 — 2026-10-08
|
|
269
|
+
|
|
270
|
+
### Added
|
|
271
|
+
- Vipps' **official button** in the detail, the cart and the checkout; **Vipps Express**
|
|
272
|
+
from the product and the cart (the app collects address and delivery).
|
|
273
|
+
- Stripe Connect in the embedded payment and Apple Pay (ADR-0022).
|
|
274
|
+
|
|
275
|
+
### Changed
|
|
276
|
+
- Back from a payment page the drawer shows **only** «Bekrefter betalingen…» until it
|
|
277
|
+
knows the outcome — never the checkout behind it.
|
|
278
|
+
- The receipt's «Ordrenummer» is **Vio's order number** (`#4497`), never the checkout's uuid.
|
|
279
|
+
|
|
280
|
+
### ⚠️ Upgrade notes
|
|
281
|
+
- Payment returns come back to **the same URL** with `vio_payment`, `vio_method`,
|
|
282
|
+
`vio_sponsor`, `checkout_id`: keep `<vio-checkout>` mounted on every route and do not
|
|
283
|
+
strip the query string on load.
|
|
284
|
+
- The Vipps button loads `cdn.vippsmobilepay.com`: allow it in a CSP.
|
|
285
|
+
|
|
286
|
+
## 0.17.0 and earlier
|
|
287
|
+
Not recorded here — see the git history and the [releases](https://github.com/vio-live/vio-web-sdk/commits/main).
|
|
288
|
+
Highlights: Stripe on our own page (0.16), payment method first (0.15), Kustom (0.14),
|
|
289
|
+
Adyen (0.13), Nexi (0.12), analytics with consent (0.6 → 0.11).
|
package/README.md
CHANGED
|
@@ -453,6 +453,113 @@ confirmation screen never waits. A host with its own checkout passes `buyerRef`
|
|
|
453
453
|
(`requireConsent` until `setConsent(true)`) `buyer_id` is not sent at all: a visit
|
|
454
454
|
measured with ephemeral ids carries no stable identifier of a person.
|
|
455
455
|
|
|
456
|
+
### The purchase — `order_id` is Vio's order number, `checkout_id` beside it
|
|
457
|
+
|
|
458
|
+
A `purchase`'s **`commerce.order_id` is Vio's order number** — `4415`, the number
|
|
459
|
+
on the receipt, in the dashboard and in Commerce. Commerce's cancellations and
|
|
460
|
+
refunds (`order_cancelled` / `order_refunded`, ADR-0036) name the purchase they
|
|
461
|
+
compensate by that number: a purchase that carried the checkout's uuid instead
|
|
462
|
+
could never be netted.
|
|
463
|
+
|
|
464
|
+
- **Where the number comes from.** The `vio:payment-success` detail's
|
|
465
|
+
`orderNumber` (the SDK's checkout adds it when its ask for the buyer reference
|
|
466
|
+
already found the order), Apple Pay's backend confirmation
|
|
467
|
+
(`result.confirmation.orderId`), or an `orderId` that is a whole number (a host
|
|
468
|
+
passing the number; a checkout id is a uuid, never one). A redirect return
|
|
469
|
+
(Vipps, Klarna, hosted Stripe…) or an order a webhook creates usually fires the
|
|
470
|
+
event before the order exists: the SDK then **asks the checkout** —
|
|
471
|
+
`Vio.checkout.getOrderReference(checkoutId)`, the receipt's own path — at once
|
|
472
|
+
and after 0.5 / 1 / 2 / 3 / 3 s, for at most 10 s. Only the event waits: the
|
|
473
|
+
confirmation screen, the cart and the payment flow never do. Apple Pay and the
|
|
474
|
+
embedded Stripe Element (the drawer waits for the webhook's order before it
|
|
475
|
+
confirms) have the number at once.
|
|
476
|
+
- **`commerce.checkout_id`** — Vio's checkout id (uuid) — is always there when the
|
|
477
|
+
SDK knows the checkout (every SDK path, Apple Pay from the cart included): the
|
|
478
|
+
second key the collector can match a purchase by. It is new and additive; a
|
|
479
|
+
collector that predates it drops the key (its schema strips unknown fields) and
|
|
480
|
+
keeps the event.
|
|
481
|
+
- **`props.order_id_kind`** says what `order_id` is: `'order'` (the number) or
|
|
482
|
+
`'checkout'` — the number did not come within the 10 s, the page was hidden or
|
|
483
|
+
closed while waiting (the event leaves at once, by beacon), or the page has no
|
|
484
|
+
checkout manager to ask; then `order_id` is the checkout id, as before.
|
|
485
|
+
- **Once per checkout.** One `purchase` per checkout (or order) per page load: a
|
|
486
|
+
second `vio:payment-success` for the same one adds nothing, and a number that
|
|
487
|
+
arrives after the event left with the checkout id adds no second event. `ts`
|
|
488
|
+
and `event_id` are fixed at the payment, whenever the event leaves; a purchase
|
|
489
|
+
split per campaign waits once and every slice carries the same number.
|
|
490
|
+
- **A host with its own sink** (Vev) reads the same ids from the detail with
|
|
491
|
+
`purchaseIdsOf(detail)` → `{ orderId, checkoutId, orderIdKind }` (or
|
|
492
|
+
`purchaseFromPaymentSuccess(detail)`, exported from `/core`), and waits for the
|
|
493
|
+
number the same way through `Vio.checkout.getOrderReference(checkoutId)`.
|
|
494
|
+
|
|
495
|
+
## Sponsor identity — name and logo
|
|
496
|
+
|
|
497
|
+
The elements say **who sells**: the sponsor's (brand's) display name and logo.
|
|
498
|
+
|
|
499
|
+
| Where | What it shows |
|
|
500
|
+
|---|---|
|
|
501
|
+
| `<vio-product-detail>` | a small brand header at the top of the detail — the logo (the name as its `alt`), or the name — for the sponsor the product was fetched with |
|
|
502
|
+
| `<vio-checkout>` header | «Du handler hos ‹logo \| name›» — the checkout's sponsor («Du har handlet hos …» over a provider's own receipt) |
|
|
503
|
+
| `<vio-checkout>` confirmation | «Takk for bestillingen! Du har handlet hos Careless.» next to the order number, with the logo above when there is one |
|
|
504
|
+
| `<vio-cart>` / carousel | the sponsor label over a sponsor's lines, the `retailer` line of a carousel's cards (by name) |
|
|
505
|
+
|
|
506
|
+
No logo → the name. A logo that fails to load → the name. Nothing known → nothing
|
|
507
|
+
(the product detail shows no header; the confirmation keeps «Takk for bestillingen!»
|
|
508
|
+
without the sentence). The marks are styled with the `--vio-*` tokens and exposed as
|
|
509
|
+
`part="sponsor"` (`vio-checkout::part(sponsor)`).
|
|
510
|
+
|
|
511
|
+
**One rule.** `Vio.sponsorIdentity(sponsorId, { checkoutId? })` answers
|
|
512
|
+
`{ id, name, logoUrl, avatarUrl } | null` — and every element uses it:
|
|
513
|
+
|
|
514
|
+
1. the live bootstrap's sponsor blocks: the config's `primarySponsor` and
|
|
515
|
+
`secondarySponsors`, then every `campaigns[]` entry's primary and secondaries,
|
|
516
|
+
first match (a surface with no active campaign still names its channel brands);
|
|
517
|
+
2. when the bootstrap does not know the id (not loaded yet, a redirect return): what
|
|
518
|
+
this tab remembered of the checkout — when the SDK creates a checkout it keeps
|
|
519
|
+
`checkoutId → { sponsorId, name, logoUrl }` in `sessionStorage`
|
|
520
|
+
(`vio.checkoutSponsor.v1`, the last 20 checkouts; it works, for that page,
|
|
521
|
+
without storage) — then the latest remembered checkout of that sponsor;
|
|
522
|
+
3. the cart's `sponsorName`;
|
|
523
|
+
4. `null`.
|
|
524
|
+
|
|
525
|
+
With only `{ checkoutId }` the sponsor is the one remembered for that checkout. URLs
|
|
526
|
+
are kept as given; an empty `logoUrl` is `null`. `sponsorIdentityFrom(bootstrap,
|
|
527
|
+
sponsorId)` is the pure step 1, exported from `/core`.
|
|
528
|
+
|
|
529
|
+
**From the checkout, never from the page.** In the checkout and on its confirmation
|
|
530
|
+
the sponsor is the **checkout's** — the cart's sponsor, the one the order is with —
|
|
531
|
+
from the checkout state or the return URL (`vio_sponsor`, `checkout_id`), never
|
|
532
|
+
`Vio.attribution()`: an article of one brand may sell another brand's product. Back
|
|
533
|
+
from Vipps the confirmation names the brand from the memory at once, or from the
|
|
534
|
+
bootstrap as soon as it lands (the thanks is shown first, the name follows).
|
|
535
|
+
|
|
536
|
+
**For hosts.** `vio:checkout-open` and `vio:payment-success` carry
|
|
537
|
+
`sponsor: { id, name, logoUrl }` in their detail when it can be named (the key is
|
|
538
|
+
absent otherwise), and `Vio.checkout.getOrderReference(checkoutId, sponsorId?)`
|
|
539
|
+
answers `{ orderNumber, buyerRef, sponsor? }` by the same rule. The SDK cleans its
|
|
540
|
+
return parameters from the URL once it has used them, so its own confirmation does
|
|
541
|
+
not come back on a reload — a host that wants a confirmation page that survives one
|
|
542
|
+
keeps the two ids in its own URL and asks again:
|
|
543
|
+
|
|
544
|
+
```ts
|
|
545
|
+
// On the purchase: keep what the confirmation needs in YOUR url.
|
|
546
|
+
document.addEventListener('vio:payment-success', (e) => {
|
|
547
|
+
const d = (e as CustomEvent).detail
|
|
548
|
+
const checkoutId = String(d.checkoutId ?? d.orderId ?? '')
|
|
549
|
+
history.replaceState(null, '', `/takk?checkout=${checkoutId}&sponsor=${d.sponsorId ?? ''}`)
|
|
550
|
+
render(d.sponsor)
|
|
551
|
+
})
|
|
552
|
+
|
|
553
|
+
// On /takk (also after a reload, or in another browser):
|
|
554
|
+
const params = new URLSearchParams(location.search)
|
|
555
|
+
const ref = await Vio.checkout.getOrderReference(params.get('checkout') ?? '', Number(params.get('sponsor')) || undefined)
|
|
556
|
+
render(ref.sponsor, ref.orderNumber) // waits for Vio.init (up to 2 s) and the bootstrap; never throws
|
|
557
|
+
|
|
558
|
+
function render(sponsor?: { name: string; logoUrl: string | null }, orderNumber?: number | null) {
|
|
559
|
+
// «Takk for bestillingen! Du har handlet hos {sponsor.name}.» — the logo when sponsor.logoUrl
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
456
563
|
## Theming
|
|
457
564
|
|
|
458
565
|
Every component reads its colors and fonts from `--vio-*` CSS custom properties
|
|
@@ -490,6 +597,83 @@ useful if you show multiple differently-branded sponsors on the same page:
|
|
|
490
597
|
applyVioTheme({ colorAccent: '#ff6600' }, document.getElementById('sponsor-2'))
|
|
491
598
|
```
|
|
492
599
|
|
|
600
|
+
### Layout of the detail, cart, checkout and confirmation (0.28)
|
|
601
|
+
|
|
602
|
+
Beyond colors and fonts, the layout a host needs to fit its design is public too —
|
|
603
|
+
**one key in `applyVioTheme`, one `--vio-*` property behind it**. Our demo, Replit and
|
|
604
|
+
Vev's Config block set the same keys, so a page looks the same in the editor, the
|
|
605
|
+
preview and published. Set them with `applyVioTheme` (preferred) or as CSS on `:root`
|
|
606
|
+
or on the element (`vio-product-detail { --vio-detail-logo-height: 48px; }`).
|
|
607
|
+
|
|
608
|
+
| `applyVioTheme` key | Property | Default | What it does |
|
|
609
|
+
|---|---|---|---|
|
|
610
|
+
| `detailWidth` | `--vio-detail-width` | `440px` | Detail panel width (desktop) |
|
|
611
|
+
| `detailPaddingX` | `--vio-detail-padding-x` | `24px` | Side padding of the detail |
|
|
612
|
+
| `detailLogoHeight` | `--vio-detail-logo-height` | `44px` | Selling brand's logo, top left beside ✕ (width keeps its proportions; the name when there is no logo; a square logo — a mark — gets the name beside it) |
|
|
613
|
+
| `detailLogoZoom` | `--vio-detail-logo-zoom` | `1` | Zoom inside the logo's box: `1.8` crops the white around a mark in a square file |
|
|
614
|
+
| `detailTitleSize` / `detailPriceSize` | `--vio-detail-title-size` / `--vio-detail-price-size` | `26px` / `24px` | Name and price |
|
|
615
|
+
| `detailImageFit` | `--vio-detail-image-fit` | `cover` | `cover` = fills the box, crops the overflow; `contain` = whole image, bars where shapes differ |
|
|
616
|
+
| `detailImageAspect` | `--vio-detail-image-aspect` | `4 / 5` | One proportion for every image, so the panel never jumps: `1 / 1`, `3 / 4`… or `auto` (each image its own) |
|
|
617
|
+
| `detailImageMaxHeight` | `--vio-detail-image-max-height` | `85vh` | Cap on the main image's height |
|
|
618
|
+
| `detailImageZoom` | `--vio-detail-image-zoom` | `1` | Zoom; e.g. `1.15` crops a white margin baked into the file |
|
|
619
|
+
| `detailImageInset` | `--vio-detail-image-inset` | the padding | Side margin of the image; `0` = edge to edge |
|
|
620
|
+
| `detailImageBg` | `--vio-detail-image-bg` | muted surface | Behind the image |
|
|
621
|
+
| `detailThumbSize` / `detailThumbGap` | `--vio-detail-thumb-size` / `--vio-detail-thumb-gap` | `64px` / `8px` | Thumbnails — always one row: swipe on touch, arrows with a mouse, the selected one kept in view |
|
|
622
|
+
| `detailThumbRadius` / `detailThumbActiveColor` | `--vio-detail-thumb-radius` / `--vio-detail-thumb-active-color` | `0` / text color | Thumbnail corners, selected border |
|
|
623
|
+
| `detailOptionHeight` / `detailOptionGap` | `--vio-detail-option-height` / `--vio-detail-option-gap` | `36px` / `6px` | Size / option buttons (height is also their min width) |
|
|
624
|
+
| `detailOptionRadius` / `detailOptionWrap` | `--vio-detail-option-radius` / `--vio-detail-option-wrap` | `0` / `wrap` | `nowrap` = one line that scrolls sideways |
|
|
625
|
+
| `detailButtonHeight` / `detailButtonRadius` | `--vio-detail-button-height` / `--vio-detail-button-radius` | `52px` / `0` | Quantity stepper and «Legg i handlekurv» |
|
|
626
|
+
| `detailBuyPosition` / `detailBuyShadow` | `--vio-detail-buy-position` / `--vio-detail-buy-shadow` | phones `static`, desktop `sticky` | Whether the buy zone (Vipps, quantity, add to cart) stays at the bottom of the panel while scrolling |
|
|
627
|
+
| `cartWidth` / `checkoutWidth` | `--vio-cart-width` / `--vio-checkout-width` | `440px` | Drawer widths (desktop) |
|
|
628
|
+
| `confirmLogoHeight` / `confirmTitleSize` / `confirmCheckBg` | `--vio-confirm-logo-height` / `--vio-confirm-title-size` / `--vio-confirm-check-bg` | `40px` / `24px` / green | The confirmation |
|
|
629
|
+
| `redirectBg` / `redirectSpinner` | `--vio-redirect-bg` / `--vio-redirect-spinner` | translucent white / Vipps orange | The «Åpner Vipps …» overlay |
|
|
630
|
+
|
|
631
|
+
Also settable as CSS only: `--vio-detail-logo-max-width` (`220px`), `--vio-detail-image-position`,
|
|
632
|
+
`--vio-detail-image-radius`, `--vio-detail-thumb-fit` (`cover`), `--vio-detail-thumb-border-width`,
|
|
633
|
+
`--vio-detail-option-padding-x` (`12px`), `--vio-detail-option-font-size`, `--vio-detail-buy-gap`,
|
|
634
|
+
`--vio-detail-buy-padding`, `--vio-detail-buy-bg`, `--vio-detail-info-gap`, `--vio-detail-gallery-gap`,
|
|
635
|
+
`--vio-detail-title-weight`, `--vio-detail-price-weight`, `--vio-confirm-logo-max-width`,
|
|
636
|
+
`--vio-confirm-title-weight`, `--vio-confirm-text-size`, `--vio-redirect-color`, `--vio-redirect-font`,
|
|
637
|
+
`--vio-detail-seller-note-size` (`12px`), `--vio-detail-seller-note-align` (`center`).
|
|
638
|
+
|
|
639
|
+
**`::part` names**, for what the properties do not cover:
|
|
640
|
+
|
|
641
|
+
- `<vio-product-detail>`: `topbar`, `sponsor`, `close`, `gallery`, `image`, `thumbs`, `thumb`
|
|
642
|
+
(+ `thumb-active`), `thumbs-arrow`, `info`, `brand`, `title`, `price`, `stock`,
|
|
643
|
+
`option-group`, `option-label`, `options`, `option` (+ `option-selected`), `buy`,
|
|
644
|
+
`buy-vipps`, `quantity-row`, `add-to-cart`, `seller-note`, `seller-terms`, `seller-refund`,
|
|
645
|
+
`description`.
|
|
646
|
+
- `<vio-checkout>`: `sponsor`, `confirmation`, `confirm-title`, `confirm-sponsor`, `confirm-details`.
|
|
647
|
+
|
|
648
|
+
```css
|
|
649
|
+
vio-product-detail::part(add-to-cart) { letter-spacing: 0; text-transform: none; }
|
|
650
|
+
vio-product-detail::part(option-selected) { background: #2b2b2b; }
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
**The detail's order:** the selling brand top left; the main image; the thumbnails;
|
|
654
|
+
name, price, stock, sizes; then, **on phones**, the buy zone — **Vipps first** (when the
|
|
655
|
+
channel has it), quantity + «Legg i handlekurv», then the other ways to pay, and under them
|
|
656
|
+
«Du handler direkte hos {brand}.» (the product's brand, never the article's) with links to
|
|
657
|
+
the brand's «Kjøpsvilkår» and «Angrerett og retur» when it gave them (the sponsor block's
|
|
658
|
+
`termsUrl` / `refundPolicyUrl`, https only, opened in a new tab) — and the
|
|
659
|
+
description; **on desktop**, the description first and the buy zone pinned at the bottom
|
|
660
|
+
of the panel. The description folds: «Beskrivelse» opens on a tap.
|
|
661
|
+
|
|
662
|
+
### The hand-over to Vipps (0.28)
|
|
663
|
+
|
|
664
|
+
Between the tap on Vipps (detail, cart or checkout) and the Vipps page, the SDK shows
|
|
665
|
+
a full-screen «Åpner Vipps …» with a soft fade — no empty moment, and it takes every
|
|
666
|
+
tap, so a second payment cannot start. Events on `document`:
|
|
667
|
+
|
|
668
|
+
| Event | Detail | When |
|
|
669
|
+
|---|---|---|
|
|
670
|
+
| `vio:payment-redirect-start` | `{ method, sponsorId, source }` | At the tap. **Cancelable**: `preventDefault()` and the SDK draws no overlay — draw your own |
|
|
671
|
+
| `vio:payment-redirect-error` | `… , reason` | It could not start. The overlay goes; the checkout opens with Vipps preselected |
|
|
672
|
+
| `vio:payment-redirect-cancel` | `… , reason` | `'express-off'` / `'not-started'`: the checkout opens instead (no error). `'back'`: the shopper came back with the browser's back button. `'cancelled'`: back through Vipps' own cancel (with `checkoutId`) |
|
|
673
|
+
|
|
674
|
+
`source` is `'detail'`, `'cart'` or `'checkout'`. The overlay is `.vio-redirect-overlay`
|
|
675
|
+
in the light DOM.
|
|
676
|
+
|
|
493
677
|
## Headless / SSR
|
|
494
678
|
|
|
495
679
|
The root and `/core` entries export the framework-agnostic core with **no DOM side-effects** — SSR-safe and **tree-shakeable** (importing one helper pulls only that helper, ~0.5 kB):
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-67QESEV2.js"}
|