@code-collective/booking-widget 1.0.12 → 1.0.13
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 +48 -46
- package/README.md +500 -500
- package/dist/booking-widget.css +1 -1
- package/dist/booking-widget.js +581 -538
- package/dist/booking-widget.min.js +24 -22
- package/dist/booking-widget.umd.cjs +4 -4
- package/package.json +58 -58
- package/src/lib/BookingProvider.svelte +21 -21
- package/src/lib/CartBar.svelte +26 -26
- package/src/lib/CartBarView.svelte +78 -78
- package/src/lib/CartExpiryGuard.svelte +416 -416
- package/src/lib/CartOverview.svelte +30 -30
- package/src/lib/CartOverviewButton.svelte +104 -104
- package/src/lib/Checkout.svelte +141 -141
- package/src/lib/CheckoutModal.svelte +179 -71
- package/src/lib/CheckoutPanel.svelte +121 -121
- package/src/lib/PaymentPage.svelte +191 -191
- package/src/lib/ResultView.svelte +24 -4
- package/src/lib/TicketConfigurator.svelte +166 -166
- package/src/lib/booking-context.ts +33 -33
- package/src/lib/cart-overview.svelte.ts +97 -97
- package/src/lib/client-types.ts +10 -6
- package/src/lib/config.ts +162 -162
- package/src/lib/elements/bw-cart.svelte +49 -49
- package/src/lib/elements/bw-checkout.svelte +97 -97
- package/src/lib/elements/bw-configurator.svelte +72 -72
- package/src/lib/elements/register.ts +171 -171
- package/src/lib/elements/shared.ts +18 -18
- package/src/lib/host.svelte.ts +336 -336
- package/src/lib/index.ts +242 -242
- package/src/lib/messages.ts +157 -157
- package/src/lib/peach-sdk.ts +86 -86
- package/src/lib/portal.ts +23 -23
package/package.json
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@code-collective/booking-widget",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"main": "dist/booking-widget.umd.cjs",
|
|
6
|
-
"module": "dist/booking-widget.js",
|
|
7
|
-
"svelte": "src/lib/index.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"svelte": "./src/lib/index.ts",
|
|
11
|
-
"import": "./dist/booking-widget.js",
|
|
12
|
-
"require": "./dist/booking-widget.umd.cjs"
|
|
13
|
-
},
|
|
14
|
-
"./style.css": "./dist/booking-widget.css",
|
|
15
|
-
"./elements": "./dist/booking-widget.min.js"
|
|
16
|
-
},
|
|
17
|
-
"files": [
|
|
18
|
-
"dist",
|
|
19
|
-
"src/lib",
|
|
20
|
-
"!src/lib/**/*.test.ts",
|
|
21
|
-
"!src/lib/test",
|
|
22
|
-
"CHANGELOG.md"
|
|
23
|
-
],
|
|
24
|
-
"scripts": {
|
|
25
|
-
"dev": "vite",
|
|
26
|
-
"build": "vite build",
|
|
27
|
-
"prepublishOnly": "npm run build && npm run build:elements",
|
|
28
|
-
"build:elements": "vite build --mode elements",
|
|
29
|
-
"postbuild:elements": "node -e \"const{readFileSync:r}=require('fs'),{createHash:h}=require('crypto');['dist/booking-widget.min.js','dist/booking-widget.min.css'].forEach(f=>{const d=h('sha384').update(r(f)).digest('base64');console.log(f+' -> sha384-'+d)})\"",
|
|
30
|
-
"build:demo": "vite build --mode demo",
|
|
31
|
-
"preview": "vite preview",
|
|
32
|
-
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
33
|
-
"test": "vitest run",
|
|
34
|
-
"release": "node scripts/publish.cjs"
|
|
35
|
-
},
|
|
36
|
-
"devDependencies": {
|
|
37
|
-
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
38
|
-
"@testing-library/svelte": "^5.4.2",
|
|
39
|
-
"@tsconfig/svelte": "^5.0.0",
|
|
40
|
-
"jsdom": "^30.0.1",
|
|
41
|
-
"svelte": "^5.0.0",
|
|
42
|
-
"svelte-check": "^4.0.0",
|
|
43
|
-
"typescript": "^5.5.0",
|
|
44
|
-
"vite": "^8.2.2",
|
|
45
|
-
"vitest": "^5.0.0"
|
|
46
|
-
},
|
|
47
|
-
"peerDependencies": {
|
|
48
|
-
"svelte": "^5.0.0"
|
|
49
|
-
},
|
|
50
|
-
"peerDependenciesMeta": {
|
|
51
|
-
"svelte": {
|
|
52
|
-
"optional": true
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
"dependencies": {
|
|
56
|
-
"openapi-fetch": "^0.17.0"
|
|
57
|
-
}
|
|
58
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@code-collective/booking-widget",
|
|
3
|
+
"version": "1.0.13",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/booking-widget.umd.cjs",
|
|
6
|
+
"module": "dist/booking-widget.js",
|
|
7
|
+
"svelte": "src/lib/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"svelte": "./src/lib/index.ts",
|
|
11
|
+
"import": "./dist/booking-widget.js",
|
|
12
|
+
"require": "./dist/booking-widget.umd.cjs"
|
|
13
|
+
},
|
|
14
|
+
"./style.css": "./dist/booking-widget.css",
|
|
15
|
+
"./elements": "./dist/booking-widget.min.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"src/lib",
|
|
20
|
+
"!src/lib/**/*.test.ts",
|
|
21
|
+
"!src/lib/test",
|
|
22
|
+
"CHANGELOG.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "vite",
|
|
26
|
+
"build": "vite build",
|
|
27
|
+
"prepublishOnly": "npm run build && npm run build:elements",
|
|
28
|
+
"build:elements": "vite build --mode elements",
|
|
29
|
+
"postbuild:elements": "node -e \"const{readFileSync:r}=require('fs'),{createHash:h}=require('crypto');['dist/booking-widget.min.js','dist/booking-widget.min.css'].forEach(f=>{const d=h('sha384').update(r(f)).digest('base64');console.log(f+' -> sha384-'+d)})\"",
|
|
30
|
+
"build:demo": "vite build --mode demo",
|
|
31
|
+
"preview": "vite preview",
|
|
32
|
+
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"release": "node scripts/publish.cjs"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
38
|
+
"@testing-library/svelte": "^5.4.2",
|
|
39
|
+
"@tsconfig/svelte": "^5.0.0",
|
|
40
|
+
"jsdom": "^30.0.1",
|
|
41
|
+
"svelte": "^5.0.0",
|
|
42
|
+
"svelte-check": "^4.0.0",
|
|
43
|
+
"typescript": "^5.5.0",
|
|
44
|
+
"vite": "^8.2.2",
|
|
45
|
+
"vitest": "^5.0.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"svelte": "^5.0.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"svelte": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"openapi-fetch": "^0.17.0"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
// Puts a host in context for everything beneath it. The host itself is still built by the consumer -
|
|
3
|
-
// createBookingHost() works anywhere, including a module shared across several Astro islands, and this
|
|
4
|
-
// only decides which components can see it.
|
|
5
|
-
import type { Snippet } from 'svelte';
|
|
6
|
-
import type { BookingHost } from './host.svelte';
|
|
7
|
-
import { setBookingHostContext } from './booking-context';
|
|
8
|
-
|
|
9
|
-
interface Props {
|
|
10
|
-
host: BookingHost;
|
|
11
|
-
children: Snippet;
|
|
12
|
-
}
|
|
13
|
-
let { host, children }: Props = $props();
|
|
14
|
-
|
|
15
|
-
// Read once, deliberately: context is set at initialisation and a provider serves one host for its
|
|
16
|
-
// lifetime. Swapping hosts mid-flight would strand every component already reading the old one.
|
|
17
|
-
// svelte-ignore state_referenced_locally
|
|
18
|
-
setBookingHostContext(host);
|
|
19
|
-
</script>
|
|
20
|
-
|
|
21
|
-
{@render children()}
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Puts a host in context for everything beneath it. The host itself is still built by the consumer -
|
|
3
|
+
// createBookingHost() works anywhere, including a module shared across several Astro islands, and this
|
|
4
|
+
// only decides which components can see it.
|
|
5
|
+
import type { Snippet } from 'svelte';
|
|
6
|
+
import type { BookingHost } from './host.svelte';
|
|
7
|
+
import { setBookingHostContext } from './booking-context';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
host: BookingHost;
|
|
11
|
+
children: Snippet;
|
|
12
|
+
}
|
|
13
|
+
let { host, children }: Props = $props();
|
|
14
|
+
|
|
15
|
+
// Read once, deliberately: context is set at initialisation and a provider serves one host for its
|
|
16
|
+
// lifetime. Swapping hosts mid-flight would strand every component already reading the old one.
|
|
17
|
+
// svelte-ignore state_referenced_locally
|
|
18
|
+
setBookingHostContext(host);
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
{@render children()}
|
package/src/lib/CartBar.svelte
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { BookingApi } from './api';
|
|
3
|
-
import type { CartOverviewState } from './config';
|
|
4
|
-
import { CartManager } from './cart-manager';
|
|
5
|
-
import { createCartOverview } from './cart-overview.svelte';
|
|
6
|
-
import CartBarView from './CartBarView.svelte';
|
|
7
|
-
import { getBookingHostContext, requireBookingService } from './booking-context';
|
|
8
|
-
|
|
9
|
-
interface Props {
|
|
10
|
-
api?: BookingApi;
|
|
11
|
-
cartManager?: CartManager;
|
|
12
|
-
// Lets a host render its own summary against the same state the bar is in, rather than inferring it from
|
|
13
|
-
// an empty DOM - see cart-overview.svelte.ts.
|
|
14
|
-
onStateChange?: (state: CartOverviewState) => void;
|
|
15
|
-
}
|
|
16
|
-
let { api, cartManager, onStateChange }: Props = $props();
|
|
17
|
-
|
|
18
|
-
const bookingHost = getBookingHostContext();
|
|
19
|
-
const overview = createCartOverview(
|
|
20
|
-
() => requireBookingService(api ?? bookingHost?.api, 'CartBar', 'api'),
|
|
21
|
-
() => requireBookingService(cartManager ?? bookingHost?.cartManager, 'CartBar', 'cartManager'),
|
|
22
|
-
(s) => onStateChange?.(s),
|
|
23
|
-
);
|
|
24
|
-
</script>
|
|
25
|
-
|
|
26
|
-
<CartBarView cart={overview.cart} state={overview.state} onRetry={overview.reload} />
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { BookingApi } from './api';
|
|
3
|
+
import type { CartOverviewState } from './config';
|
|
4
|
+
import { CartManager } from './cart-manager';
|
|
5
|
+
import { createCartOverview } from './cart-overview.svelte';
|
|
6
|
+
import CartBarView from './CartBarView.svelte';
|
|
7
|
+
import { getBookingHostContext, requireBookingService } from './booking-context';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
api?: BookingApi;
|
|
11
|
+
cartManager?: CartManager;
|
|
12
|
+
// Lets a host render its own summary against the same state the bar is in, rather than inferring it from
|
|
13
|
+
// an empty DOM - see cart-overview.svelte.ts.
|
|
14
|
+
onStateChange?: (state: CartOverviewState) => void;
|
|
15
|
+
}
|
|
16
|
+
let { api, cartManager, onStateChange }: Props = $props();
|
|
17
|
+
|
|
18
|
+
const bookingHost = getBookingHostContext();
|
|
19
|
+
const overview = createCartOverview(
|
|
20
|
+
() => requireBookingService(api ?? bookingHost?.api, 'CartBar', 'api'),
|
|
21
|
+
() => requireBookingService(cartManager ?? bookingHost?.cartManager, 'CartBar', 'cartManager'),
|
|
22
|
+
(s) => onStateChange?.(s),
|
|
23
|
+
);
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<CartBarView cart={overview.cart} state={overview.state} onRetry={overview.reload} />
|
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { CheckoutCartDetailDto } from './client-types';
|
|
3
|
-
import type { CartOverviewState } from './config';
|
|
4
|
-
import { formatCurrency } from './currency';
|
|
5
|
-
import { postMessage } from './messages';
|
|
6
|
-
import CountdownTimer from './CountdownTimer.svelte';
|
|
7
|
-
import { cartDeadline } from './cart-expiry';
|
|
8
|
-
|
|
9
|
-
interface Props {
|
|
10
|
-
cart: CheckoutCartDetailDto | null;
|
|
11
|
-
state?: CartOverviewState;
|
|
12
|
-
onRetry?: () => void;
|
|
13
|
-
}
|
|
14
|
-
let { cart, state = 'ready', onRetry }: Props = $props();
|
|
15
|
-
|
|
16
|
-
let itemCount = $derived(cart?.items.length ?? 0);
|
|
17
|
-
|
|
18
|
-
let totalLabel = $derived(() => {
|
|
19
|
-
if (!cart || cart.items.length === 0) return '';
|
|
20
|
-
const currency = cart.items[0].currencyCode;
|
|
21
|
-
const total = cart.items.reduce((sum, i) => sum + i.amount, 0);
|
|
22
|
-
return formatCurrency(total, currency, 2, 2);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
let expiresAt = $derived(() => cart ? cartDeadline(cart) : new Date());
|
|
26
|
-
</script>
|
|
27
|
-
|
|
28
|
-
{#if state === 'error'}
|
|
29
|
-
<!-- An empty cart and a cart that could not be read used to render identically (as nothing), which left a
|
|
30
|
-
shopper on a narrow layout with no route to checkout and no sign that anything had gone wrong. -->
|
|
31
|
-
<div class="cart-bar bw-widget" role="alert">
|
|
32
|
-
<div class="cart-info">
|
|
33
|
-
<span class="item-count">Cart unavailable</span>
|
|
34
|
-
<span class="error-detail">We could not load your cart just now.</span>
|
|
35
|
-
</div>
|
|
36
|
-
<button class="btn btn-primary" onclick={() => onRetry?.()}>Retry</button>
|
|
37
|
-
</div>
|
|
38
|
-
{:else if cart && cart.items.length > 0}
|
|
39
|
-
<div class="cart-bar bw-widget">
|
|
40
|
-
<div class="cart-info">
|
|
41
|
-
<span class="item-count">{itemCount === 1 ? '1 item' : `${itemCount} items`} <CountdownTimer expiresAt={expiresAt()} /></span>
|
|
42
|
-
<span class="total">{totalLabel()}</span>
|
|
43
|
-
</div>
|
|
44
|
-
<button class="btn btn-primary" onclick={() => postMessage({ type: 'modal:open' })}>
|
|
45
|
-
Checkout
|
|
46
|
-
</button>
|
|
47
|
-
</div>
|
|
48
|
-
{/if}
|
|
49
|
-
|
|
50
|
-
<style>
|
|
51
|
-
.cart-bar {
|
|
52
|
-
display: flex;
|
|
53
|
-
align-items: center;
|
|
54
|
-
justify-content: space-between;
|
|
55
|
-
padding: 12px 16px;
|
|
56
|
-
background: white;
|
|
57
|
-
border-top: 1px solid var(--bw-color-border);
|
|
58
|
-
box-shadow: 0 -2px 8px rgba(0,0,0,0.06);
|
|
59
|
-
width: 100%;
|
|
60
|
-
}
|
|
61
|
-
.cart-info {
|
|
62
|
-
display: flex;
|
|
63
|
-
flex-direction: column;
|
|
64
|
-
gap: 2px;
|
|
65
|
-
}
|
|
66
|
-
.item-count {
|
|
67
|
-
font-size: 12px;
|
|
68
|
-
color: var(--bw-color-text-secondary);
|
|
69
|
-
}
|
|
70
|
-
.error-detail {
|
|
71
|
-
font-size: 14px;
|
|
72
|
-
font-weight: 600;
|
|
73
|
-
}
|
|
74
|
-
.total {
|
|
75
|
-
font-size: 16px;
|
|
76
|
-
font-weight: 700;
|
|
77
|
-
}
|
|
78
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { CheckoutCartDetailDto } from './client-types';
|
|
3
|
+
import type { CartOverviewState } from './config';
|
|
4
|
+
import { formatCurrency } from './currency';
|
|
5
|
+
import { postMessage } from './messages';
|
|
6
|
+
import CountdownTimer from './CountdownTimer.svelte';
|
|
7
|
+
import { cartDeadline } from './cart-expiry';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
cart: CheckoutCartDetailDto | null;
|
|
11
|
+
state?: CartOverviewState;
|
|
12
|
+
onRetry?: () => void;
|
|
13
|
+
}
|
|
14
|
+
let { cart, state = 'ready', onRetry }: Props = $props();
|
|
15
|
+
|
|
16
|
+
let itemCount = $derived(cart?.items.length ?? 0);
|
|
17
|
+
|
|
18
|
+
let totalLabel = $derived(() => {
|
|
19
|
+
if (!cart || cart.items.length === 0) return '';
|
|
20
|
+
const currency = cart.items[0].currencyCode;
|
|
21
|
+
const total = cart.items.reduce((sum, i) => sum + i.amount, 0);
|
|
22
|
+
return formatCurrency(total, currency, 2, 2);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
let expiresAt = $derived(() => cart ? cartDeadline(cart) : new Date());
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
{#if state === 'error'}
|
|
29
|
+
<!-- An empty cart and a cart that could not be read used to render identically (as nothing), which left a
|
|
30
|
+
shopper on a narrow layout with no route to checkout and no sign that anything had gone wrong. -->
|
|
31
|
+
<div class="cart-bar bw-widget" role="alert">
|
|
32
|
+
<div class="cart-info">
|
|
33
|
+
<span class="item-count">Cart unavailable</span>
|
|
34
|
+
<span class="error-detail">We could not load your cart just now.</span>
|
|
35
|
+
</div>
|
|
36
|
+
<button class="btn btn-primary" onclick={() => onRetry?.()}>Retry</button>
|
|
37
|
+
</div>
|
|
38
|
+
{:else if cart && cart.items.length > 0}
|
|
39
|
+
<div class="cart-bar bw-widget">
|
|
40
|
+
<div class="cart-info">
|
|
41
|
+
<span class="item-count">{itemCount === 1 ? '1 item' : `${itemCount} items`} <CountdownTimer expiresAt={expiresAt()} /></span>
|
|
42
|
+
<span class="total">{totalLabel()}</span>
|
|
43
|
+
</div>
|
|
44
|
+
<button class="btn btn-primary" onclick={() => postMessage({ type: 'modal:open' })}>
|
|
45
|
+
Checkout
|
|
46
|
+
</button>
|
|
47
|
+
</div>
|
|
48
|
+
{/if}
|
|
49
|
+
|
|
50
|
+
<style>
|
|
51
|
+
.cart-bar {
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
justify-content: space-between;
|
|
55
|
+
padding: 12px 16px;
|
|
56
|
+
background: white;
|
|
57
|
+
border-top: 1px solid var(--bw-color-border);
|
|
58
|
+
box-shadow: 0 -2px 8px rgba(0,0,0,0.06);
|
|
59
|
+
width: 100%;
|
|
60
|
+
}
|
|
61
|
+
.cart-info {
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
gap: 2px;
|
|
65
|
+
}
|
|
66
|
+
.item-count {
|
|
67
|
+
font-size: 12px;
|
|
68
|
+
color: var(--bw-color-text-secondary);
|
|
69
|
+
}
|
|
70
|
+
.error-detail {
|
|
71
|
+
font-size: 14px;
|
|
72
|
+
font-weight: 600;
|
|
73
|
+
}
|
|
74
|
+
.total {
|
|
75
|
+
font-size: 16px;
|
|
76
|
+
font-weight: 700;
|
|
77
|
+
}
|
|
78
|
+
</style>
|