@code-collective/booking-widget 1.0.3 → 1.0.6
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 +353 -282
- package/dist/booking-widget.css +1 -1
- package/dist/booking-widget.js +1219 -885
- package/dist/booking-widget.min.css +1 -1
- package/dist/booking-widget.min.js +9 -6
- package/dist/booking-widget.umd.cjs +2 -2
- package/package.json +1 -1
- package/src/lib/CartBar.svelte +14 -0
- package/src/lib/CartBarView.svelte +1 -1
- package/src/lib/CartOverviewButton.svelte +15 -1
- package/src/lib/Checkout.svelte +42 -27
- package/src/lib/CheckoutModal.svelte +26 -43
- package/src/lib/EditBookingView.svelte +5 -1
- package/src/lib/TicketConfigurator.svelte +31 -16
- package/src/lib/WizardPage.svelte +371 -96
- package/src/lib/api.ts +2 -1
- package/src/lib/app.css +12 -4
- package/src/lib/cart-manager.ts +1 -0
- package/src/lib/config.ts +72 -14
- package/src/lib/elements/bw-checkout.svelte +9 -4
- package/src/lib/elements/bw-configurator.svelte +2 -2
- package/src/lib/elements/register.ts +12 -1
- package/src/lib/fake-api.ts +1 -1
- package/src/lib/index.ts +6 -1
package/README.md
CHANGED
|
@@ -1,282 +1,353 @@
|
|
|
1
|
-
# Booking Widget
|
|
2
|
-
|
|
3
|
-
A Svelte 5 widget library for embedding ticket sales on tourism websites. Three custom HTML elements handle product selection, cart management, and payment via Peach Payments.
|
|
4
|
-
|
|
5
|
-
## Option 1: HTML Custom Elements (CDN)
|
|
6
|
-
|
|
7
|
-
Drop in a script tag and use the custom elements directly. No framework or npm install required.
|
|
8
|
-
|
|
9
|
-
```html
|
|
10
|
-
<head>
|
|
11
|
-
<link rel="stylesheet"
|
|
12
|
-
href="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.css"
|
|
13
|
-
integrity="sha384-Chz7xKNGRdJXbNwkaJzy45I9DiwPStkzNQV9VgWMNKgnlHCpgnhbiukEJEtbHt+v"
|
|
14
|
-
crossorigin="anonymous" />
|
|
15
|
-
</head>
|
|
16
|
-
<body>
|
|
17
|
-
<bw-configurator product-id="your-product-id" checkout-key="your-checkout-key"></bw-configurator>
|
|
18
|
-
<bw-cart display="button"></bw-cart>
|
|
19
|
-
<bw-cart display="bar"></bw-cart>
|
|
20
|
-
<bw-checkout></bw-checkout>
|
|
21
|
-
|
|
22
|
-
<script
|
|
23
|
-
src="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.js"
|
|
24
|
-
integrity="sha384-y2Ncw7PWI9NxiwKR0nhDk+ddkYmUyl+kxFyU+B4eDXWt3bfcMk1CRVTXXUwhTgJd"
|
|
25
|
-
crossorigin="anonymous"></script>
|
|
26
|
-
</body>
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
> **Note:** Pin to a specific version (e.g. `@1.0.0`) when using SRI. The hashes change with every release. Run `npm run build:elements` to see the current hashes.
|
|
30
|
-
|
|
31
|
-
The script auto-wires the elements together:
|
|
32
|
-
- Adding an item shows the cart and opens the checkout overlay
|
|
33
|
-
- Clicking checkout in the cart opens the overlay
|
|
34
|
-
- Closing or completing checkout closes the overlay
|
|
35
|
-
|
|
36
|
-
### Element attributes
|
|
37
|
-
|
|
38
|
-
#### `<bw-configurator>`
|
|
39
|
-
|
|
40
|
-
| Attribute | Required | Description |
|
|
41
|
-
|---|---|---|
|
|
42
|
-
| `product-id` | Yes | OCTO product ID |
|
|
43
|
-
| `checkout-key` | Yes | Public key identifying the supplier |
|
|
44
|
-
| `wizard-pages` | No | JSON string for wizard step ordering |
|
|
45
|
-
| `auto-select-single-time-slot` | No | Boolean. Skips time picker if only one slot |
|
|
46
|
-
| `cancelable` | No | Boolean. Shows a cancel button |
|
|
47
|
-
| `no-auto-checkout` | No | Boolean. Prevents auto-opening checkout on add |
|
|
48
|
-
| `on-cart-change` | No | Name of a global JS function to call on add |
|
|
49
|
-
|
|
50
|
-
#### `<bw-cart>`
|
|
51
|
-
|
|
52
|
-
| Attribute | Required | Description |
|
|
53
|
-
|---|---|---|
|
|
54
|
-
| `checkout-key` | Yes | Public key identifying the supplier |
|
|
55
|
-
| `display` | No | `bar` (default) or `button` |
|
|
56
|
-
|
|
57
|
-
#### `<bw-checkout>`
|
|
58
|
-
|
|
59
|
-
| Attribute | Required | Description |
|
|
60
|
-
|---|---|---|
|
|
61
|
-
| `checkout-key` | Yes | Public key identifying the supplier |
|
|
62
|
-
| `wizard-pages` | No | JSON string for wizard step ordering
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
|
102
|
-
|
|
103
|
-
| `bw:
|
|
104
|
-
| `bw:
|
|
105
|
-
| `bw:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
|
140
|
-
|
|
141
|
-
| `
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
{
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
integrity="sha384-
|
|
215
|
-
crossorigin="anonymous"
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
1
|
+
# Booking Widget
|
|
2
|
+
|
|
3
|
+
A Svelte 5 widget library for embedding ticket sales on tourism websites. Three custom HTML elements handle product selection, cart management, and payment via Peach Payments.
|
|
4
|
+
|
|
5
|
+
## Option 1: HTML Custom Elements (CDN)
|
|
6
|
+
|
|
7
|
+
Drop in a script tag and use the custom elements directly. No framework or npm install required.
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<head>
|
|
11
|
+
<link rel="stylesheet"
|
|
12
|
+
href="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.css"
|
|
13
|
+
integrity="sha384-Chz7xKNGRdJXbNwkaJzy45I9DiwPStkzNQV9VgWMNKgnlHCpgnhbiukEJEtbHt+v"
|
|
14
|
+
crossorigin="anonymous" />
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
<bw-configurator product-id="your-product-id" checkout-key="your-checkout-key"></bw-configurator>
|
|
18
|
+
<bw-cart display="button"></bw-cart>
|
|
19
|
+
<bw-cart display="bar"></bw-cart>
|
|
20
|
+
<bw-checkout></bw-checkout>
|
|
21
|
+
|
|
22
|
+
<script
|
|
23
|
+
src="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.js"
|
|
24
|
+
integrity="sha384-y2Ncw7PWI9NxiwKR0nhDk+ddkYmUyl+kxFyU+B4eDXWt3bfcMk1CRVTXXUwhTgJd"
|
|
25
|
+
crossorigin="anonymous"></script>
|
|
26
|
+
</body>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> **Note:** Pin to a specific version (e.g. `@1.0.0`) when using SRI. The hashes change with every release. Run `npm run build:elements` to see the current hashes.
|
|
30
|
+
|
|
31
|
+
The script auto-wires the elements together:
|
|
32
|
+
- Adding an item shows the cart and opens the checkout overlay
|
|
33
|
+
- Clicking checkout in the cart opens the overlay
|
|
34
|
+
- Closing or completing checkout closes the overlay
|
|
35
|
+
|
|
36
|
+
### Element attributes
|
|
37
|
+
|
|
38
|
+
#### `<bw-configurator>`
|
|
39
|
+
|
|
40
|
+
| Attribute | Required | Description |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `product-id` | Yes | OCTO product ID |
|
|
43
|
+
| `checkout-key` | Yes | Public key identifying the supplier |
|
|
44
|
+
| `wizard-pages` | No | JSON string for wizard step ordering |
|
|
45
|
+
| `auto-select-single-time-slot` | No | Boolean. Skips the time picker if only one slot |
|
|
46
|
+
| `cancelable` | No | Boolean. Shows a cancel button |
|
|
47
|
+
| `no-auto-checkout` | No | Boolean. Prevents auto-opening checkout on add |
|
|
48
|
+
| `on-cart-change` | No | Name of a global JS function to call on add |
|
|
49
|
+
|
|
50
|
+
#### `<bw-cart>`
|
|
51
|
+
|
|
52
|
+
| Attribute | Required | Description |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `checkout-key` | Yes | Public key identifying the supplier |
|
|
55
|
+
| `display` | No | `bar` (default) or `button` |
|
|
56
|
+
|
|
57
|
+
#### `<bw-checkout>`
|
|
58
|
+
|
|
59
|
+
| Attribute | Required | Description |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `checkout-key` | Yes | Public key identifying the supplier |
|
|
62
|
+
| `wizard-pages` | No | JSON string for wizard step ordering |
|
|
63
|
+
| `edit-pages` | No | JSON string for how fields are grouped in the cart edit view |
|
|
64
|
+
| `auto-select-single-time-slot` | No | Boolean. Skips the time picker if only one slot |
|
|
65
|
+
|
|
66
|
+
### Global options
|
|
67
|
+
|
|
68
|
+
Set `window.bwOptions` before the widget script loads:
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<script>
|
|
72
|
+
window.bwOptions = {
|
|
73
|
+
shouldBottomCloseOnModal: true,
|
|
74
|
+
autoSelectSingleTimeSlot: false,
|
|
75
|
+
wizardPages: '',
|
|
76
|
+
editPages: '',
|
|
77
|
+
};
|
|
78
|
+
</script>
|
|
79
|
+
<script src="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.js"
|
|
80
|
+
integrity="sha384-y2Ncw7PWI9NxiwKR0nhDk+ddkYmUyl+kxFyU+B4eDXWt3bfcMk1CRVTXXUwhTgJd"
|
|
81
|
+
crossorigin="anonymous"></script>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Option | Default | Description |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `shouldBottomCloseOnModal` | `true` | Hide bar carts when checkout modal opens |
|
|
87
|
+
| `autoSelectSingleTimeSlot` | `false` | Auto-select (and hide) the time picker when only one slot |
|
|
88
|
+
| `wizardPages` | `''` | JSON wizard step ordering for all elements |
|
|
89
|
+
| `editPages` | `''` | JSON field grouping for the checkout edit view (`bw-checkout` only) |
|
|
90
|
+
|
|
91
|
+
### Window events
|
|
92
|
+
|
|
93
|
+
All `bw:*` events are re-dispatched on `window`:
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
window.addEventListener('bw:order-confirmed', (e) => {
|
|
97
|
+
console.log(e.detail); // { cartToken, value, currency }
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
| Event | Detail | When |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| `bw:cart-change` | `{ itemCount, cartItemId, totalFormatted }` | Item added to cart |
|
|
104
|
+
| `bw:order-confirmed` | `{ cartToken, value, currency }` | Payment completed |
|
|
105
|
+
| `bw:modal-open` | -- | Checkout modal opens |
|
|
106
|
+
| `bw:modal-close` | -- | Checkout modal closes |
|
|
107
|
+
| `bw:checkout` | -- | Cart checkout button clicked |
|
|
108
|
+
| `bw:close` | -- | Checkout dismissed |
|
|
109
|
+
| `bw:cancel` | -- | Configurator cancelled |
|
|
110
|
+
|
|
111
|
+
## Option 2: JavaScript mount functions
|
|
112
|
+
|
|
113
|
+
For programmatic control in an Astro, Vite, or bundled project, install the package and import the ES module:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm install @code-collective/booking-widget
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
import { mountConfigurator, mountCheckout, mountCartOverview } from '@code-collective/booking-widget';
|
|
121
|
+
import '@code-collective/booking-widget/style.css';
|
|
122
|
+
|
|
123
|
+
const widget = await mountConfigurator(document.getElementById('configurator'), {
|
|
124
|
+
productId: 'your-product-id',
|
|
125
|
+
checkoutKey: 'your-checkout-key',
|
|
126
|
+
apiBaseUrl: 'https://checkout.yourdomain.com',
|
|
127
|
+
onCartChange({ itemCount, totalFormatted }) {
|
|
128
|
+
console.log(`${itemCount} items, ${totalFormatted}`);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Later: widget.destroy();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Mount functions
|
|
136
|
+
|
|
137
|
+
**`mountConfigurator(target, config)`** — Product selection wizard
|
|
138
|
+
|
|
139
|
+
| Config | Required | Description |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| `productId` | Yes | OCTO product ID |
|
|
142
|
+
| `checkoutKey` | No | Supplier checkout key |
|
|
143
|
+
| `apiBaseUrl` | No | Checkout API URL |
|
|
144
|
+
| `wizardPages` | No | Wizard step ordering |
|
|
145
|
+
| `autoSelectSingleTimeSlot` | No | Skip time picker if single slot |
|
|
146
|
+
| `onCartChange` | No | Callback: `({ itemCount, cartItemId, totalFormatted })` |
|
|
147
|
+
| `onCancel` | No | Callback when cancelled |
|
|
148
|
+
|
|
149
|
+
**`mountCheckout(target, config)`** — Cart review, contact form, payment
|
|
150
|
+
|
|
151
|
+
| Config | Required | Description |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| `checkoutKey` | No | Supplier checkout key |
|
|
154
|
+
| `apiBaseUrl` | No | Checkout API URL |
|
|
155
|
+
| `onClose` | No | Callback when closed |
|
|
156
|
+
| `onOrderConfirmed` | No | Callback: `({ cartToken, value, currency })` |
|
|
157
|
+
|
|
158
|
+
**`mountCartOverview(target, config)`** — Cart summary
|
|
159
|
+
|
|
160
|
+
| Config | Required | Description |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| `checkoutKey` | No | Supplier checkout key |
|
|
163
|
+
| `apiBaseUrl` | No | Checkout API URL |
|
|
164
|
+
| `display` | No | `bar` or `button` |
|
|
165
|
+
| `onCheckout` | No | Callback when checkout clicked |
|
|
166
|
+
|
|
167
|
+
All mount functions return `Promise<{ destroy(): void }>`.
|
|
168
|
+
|
|
169
|
+
## Option 3: Svelte components
|
|
170
|
+
|
|
171
|
+
Install the package and import Svelte components directly for full reactivity:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npm install @code-collective/booking-widget
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
```svelte
|
|
178
|
+
<script lang="ts">
|
|
179
|
+
import { TicketConfigurator, Checkout, CartBar, CartOverviewButton } from '@code-collective/booking-widget';
|
|
180
|
+
import { ApiClient, SessionManager, CartManager } from '@code-collective/booking-widget';
|
|
181
|
+
import '@code-collective/booking-widget/style.css';
|
|
182
|
+
|
|
183
|
+
const api = new ApiClient('https://checkout.yourdomain.com');
|
|
184
|
+
const sessionManager = new SessionManager(api);
|
|
185
|
+
const cartManager = new CartManager(api);
|
|
186
|
+
sessionManager.startBackgroundRefresh();
|
|
187
|
+
|
|
188
|
+
let ready = $state(false);
|
|
189
|
+
sessionManager.ensureSession('your-checkout-key').then(() => { ready = true; });
|
|
190
|
+
</script>
|
|
191
|
+
|
|
192
|
+
{#if ready}
|
|
193
|
+
<TicketConfigurator {api} {cartManager} productId="your-product-id" />
|
|
194
|
+
<CartOverviewButton {api} {cartManager} />
|
|
195
|
+
<Checkout {api} />
|
|
196
|
+
{/if}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Astro integration
|
|
200
|
+
|
|
201
|
+
### Script tag approach
|
|
202
|
+
|
|
203
|
+
```astro
|
|
204
|
+
---
|
|
205
|
+
const product = await getProduct(Astro.params.slug);
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
<bw-configurator product-id={product.id} checkout-key={product.checkoutKey}></bw-configurator>
|
|
209
|
+
<bw-cart display="bar" checkout-key={product.checkoutKey}></bw-cart>
|
|
210
|
+
<bw-checkout checkout-key={product.checkoutKey}></bw-checkout>
|
|
211
|
+
|
|
212
|
+
<link rel="stylesheet"
|
|
213
|
+
href="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.css"
|
|
214
|
+
integrity="sha384-Chz7xKNGRdJXbNwkaJzy45I9DiwPStkzNQV9VgWMNKgnlHCpgnhbiukEJEtbHt+v"
|
|
215
|
+
crossorigin="anonymous" />
|
|
216
|
+
<script is:inline
|
|
217
|
+
src="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.js"
|
|
218
|
+
integrity="sha384-y2Ncw7PWI9NxiwKR0nhDk+ddkYmUyl+kxFyU+B4eDXWt3bfcMk1CRVTXXUwhTgJd"
|
|
219
|
+
crossorigin="anonymous"></script>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Svelte island approach
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
npx astro add svelte
|
|
226
|
+
npm install @code-collective/booking-widget
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
```astro
|
|
230
|
+
---
|
|
231
|
+
import BookingWidget from '../components/BookingWidget.svelte';
|
|
232
|
+
const product = await getProduct(Astro.params.slug);
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
<BookingWidget
|
|
236
|
+
client:load
|
|
237
|
+
productId={product.id}
|
|
238
|
+
checkoutKey={product.checkoutKey}
|
|
239
|
+
apiBaseUrl="https://checkout.yourdomain.com"
|
|
240
|
+
/>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Wizard pages
|
|
244
|
+
|
|
245
|
+
Each wizard page is an object with a `title` (shown as the page heading and accordion label) and a `widgets` array that controls which sections appear on that page.
|
|
246
|
+
|
|
247
|
+
**Option-first (default):**
|
|
248
|
+
```json
|
|
249
|
+
[
|
|
250
|
+
{ "title": "Option", "widgets": ["option", "age-category"] },
|
|
251
|
+
{ "title": "Schedule", "widgets": ["date", "time"] },
|
|
252
|
+
{ "title": "Pickup", "widgets": ["pickup"] }
|
|
253
|
+
]
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Date-first:**
|
|
257
|
+
```json
|
|
258
|
+
[
|
|
259
|
+
{ "title": "Age & capacity", "widgets": ["age-category"] },
|
|
260
|
+
{ "title": "Schedule", "widgets": ["date", "time"] },
|
|
261
|
+
{ "title": "Option", "widgets": ["option"] },
|
|
262
|
+
{ "title": "Pickup", "widgets": ["pickup"] }
|
|
263
|
+
]
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Available widget types: `option`, `age-category`, `date`, `time`, `pickup`.
|
|
267
|
+
|
|
268
|
+
### Setting wizard pages
|
|
269
|
+
|
|
270
|
+
**Using a preset** (simplest):
|
|
271
|
+
```html
|
|
272
|
+
<bw-configurator product-id="..." checkout-key="..." wizard-pages="option-first"></bw-configurator>
|
|
273
|
+
|
|
274
|
+
<bw-configurator product-id="..." checkout-key="..." wizard-pages="date-first"></bw-configurator>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Using a custom JSON array** (full control):
|
|
278
|
+
```html
|
|
279
|
+
<bw-configurator product-id="..." checkout-key="..."
|
|
280
|
+
wizard-pages='[
|
|
281
|
+
{"title":"Age & capacity","widgets":["age-category"]},
|
|
282
|
+
{"title":"Schedule","widgets":["date","time"]},
|
|
283
|
+
{"title":"Option","widgets":["option"]},
|
|
284
|
+
{"title":"Pickup","widgets":["pickup"]}
|
|
285
|
+
]'>
|
|
286
|
+
</bw-configurator>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
If `wizard-pages` is omitted, the default is `option-first`.
|
|
290
|
+
|
|
291
|
+
In the configurator, pages are shown as a step-by-step wizard with dot indicators. In the checkout edit view, pages are shown as collapsible accordion sections with a summary of the selected values when collapsed.
|
|
292
|
+
|
|
293
|
+
### Edit pages (checkout accordion grouping)
|
|
294
|
+
|
|
295
|
+
`edit-pages` controls the same thing as `wizard-pages` — which widgets appear together, and in what order — but for the collapsible accordion shown when editing an item already in the cart (`<bw-checkout>` only; it has no effect on `<bw-configurator>`'s step wizard). It accepts the same two shapes as `wizard-pages`: a preset name (`"option-first"` / `"date-first"`) or a custom JSON array of `{ "title", "widgets" }` objects.
|
|
296
|
+
|
|
297
|
+
```html
|
|
298
|
+
<bw-checkout checkout-key="..." wizard-pages="date-first" edit-pages="date-first"></bw-checkout>
|
|
299
|
+
|
|
300
|
+
<bw-checkout checkout-key="..."
|
|
301
|
+
edit-pages='[
|
|
302
|
+
{"title":"Age, Date & Time","widgets":["age-category","date","time"]},
|
|
303
|
+
{"title":"Option","widgets":["option"]},
|
|
304
|
+
{"title":"Pickup","widgets":["pickup"]}
|
|
305
|
+
]'>
|
|
306
|
+
</bw-checkout>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
If `edit-pages` is omitted, it's derived from `wizard-pages`: age/date/time are grouped into one accordion section, positioned first for `date-first` and second (after Option) for `option-first`. If `wizard-pages` is itself a custom JSON array rather than a named preset, there's no sensible grouping to infer, so `edit-pages` just mirrors it directly — set `edit-pages` explicitly in that case if you want different grouping in the edit view.
|
|
310
|
+
|
|
311
|
+
While an item is being edited, changing a field can invalidate an earlier selection (e.g. increasing ticket quantity beyond the previously-selected time slot's vacancies resets the date/time). An invalidated accordion section is shown with a red border and a "Changes made require new configuration" summary — even while collapsed — until it's reselected. Fields that don't need to change are preserved automatically (e.g. an already-chosen pickup point stays selected across a date change, since pickup is a property of the option, not the date/time).
|
|
312
|
+
|
|
313
|
+
### Hiding the time selector
|
|
314
|
+
|
|
315
|
+
Some products only ever offer a single time slot per day (or an all-day slot with no specific time). For these, showing a time picker step with only one option to click is pointless friction. Set `auto-select-single-time-slot` to skip it:
|
|
316
|
+
|
|
317
|
+
```html
|
|
318
|
+
<bw-configurator product-id="..." checkout-key="..." auto-select-single-time-slot></bw-configurator>
|
|
319
|
+
<bw-checkout checkout-key="..." auto-select-single-time-slot></bw-checkout>
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
When enabled, and the selected date has exactly one available time slot, that slot is selected automatically and the time-picker widget is hidden entirely — the flow moves straight from date to the next widget (option or pickup, depending on `wizard-pages`). A date with a genuine all-day slot (`allDay: true` from the availability API) always behaves this way, regardless of this flag. Set it on both `<bw-configurator>` and `<bw-checkout>` (or via `window.bwOptions.autoSelectSingleTimeSlot`, which wires it onto every element automatically) so the behavior is consistent between adding an item and editing one already in the cart.
|
|
323
|
+
|
|
324
|
+
## Theming
|
|
325
|
+
|
|
326
|
+
The widget reads CSS custom properties. It automatically picks up site variables (`--header-background`, `--default-font-family`, `--radius`) or can be themed directly:
|
|
327
|
+
|
|
328
|
+
```css
|
|
329
|
+
:root {
|
|
330
|
+
--bw-color-primary: #0066CC;
|
|
331
|
+
--bw-color-primary-dark: #004C99;
|
|
332
|
+
--bw-font-family: 'Inter', sans-serif;
|
|
333
|
+
--bw-radius-md: 4px;
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
| Variable | Fallback | Default |
|
|
338
|
+
|---|---|---|
|
|
339
|
+
| `--bw-color-primary` | `--header-background` | `#E30613` |
|
|
340
|
+
| `--bw-color-primary-dark` | `--footer-background` | `#C00510` |
|
|
341
|
+
| `--bw-color-primary-light` | -- | `rgba(227,6,19,0.08)` |
|
|
342
|
+
| `--bw-color-text` | -- | `#212121` |
|
|
343
|
+
| `--bw-color-text-secondary` | -- | `#757575` |
|
|
344
|
+
| `--bw-color-border` | -- | `#E0E0E0` |
|
|
345
|
+
| `--bw-color-bg` | -- | `#FFFFFF` |
|
|
346
|
+
| `--bw-color-surface` | -- | `#F5F5F5` |
|
|
347
|
+
| `--bw-color-success` | -- | `#4CAF50` |
|
|
348
|
+
| `--bw-color-error` | `--header-background` | `#E30613` |
|
|
349
|
+
| `--bw-font-family` | `--default-font-family` | `Roboto, system` |
|
|
350
|
+
| `--bw-radius-sm` | -- | `4px` |
|
|
351
|
+
| `--bw-radius-md` | `--radius` | `8px` |
|
|
352
|
+
| `--bw-radius-lg` | -- | `12px` |
|
|
353
|
+
| `--bw-transition` | -- | `0.15s ease` |
|