@bookslot/react 0.0.0-stage → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Taulant Sela
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,67 @@
1
- # Temporary Holding Version
1
+ # @bookslot/react
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The [BookSlot](https://bookslot.taulantsela.com) booking widget as a React component. Works in Next.js (App Router and Pages Router), Vite, Remix and any other React app.
4
+
5
+ ```bash
6
+ npm install @bookslot/react
7
+ ```
8
+
9
+ ```tsx
10
+ import { BookSlotWidget } from "@bookslot/react";
11
+
12
+ export function Booking() {
13
+ return <BookSlotWidget shop="your-shop-slug" />;
14
+ }
15
+ ```
16
+
17
+ That's a working booking form. It loads the widget from BookSlot, resizes itself to fit, and shows the shop's services and free times. Find your shop slug in the BookSlot dashboard.
18
+
19
+ The package is already marked `"use client"`, so in the Next.js App Router a server component can render it with `shop`, `theme`, `service`, `staff` and `customer`. Callbacks such as `onBooked` are functions, which React can't send from the server, so pass those from a client component.
20
+
21
+ ## Match your site, prefill, react to bookings (Pro)
22
+
23
+ ```tsx
24
+ <BookSlotWidget
25
+ shop="your-shop-slug"
26
+ theme={{ primary: "#2dd4bf", scheme: "dark", radius: 6, font: "system" }}
27
+ service="svc_…" // preselect; can change any time
28
+ staff="stf_…" // preselect a staff member
29
+ customer={{ name: user.name, email: user.email, fields: { any_allergies: "None" } }}
30
+ onBooked={(booking) => router.push(`/thanks?id=${booking.appointmentId}`)}
31
+ />
32
+ ```
33
+
34
+ On the Free plan the widget still works, but it ignores these options and shows a "Powered by BookSlot" badge. BookSlot enforces this on its server, so editing the props can't unlock them.
35
+
36
+ ## Props
37
+
38
+ | Prop | Type | |
39
+ |---|---|---|
40
+ | `shop` | `string` | Required. The shop's slug. |
41
+ | `bookslotUrl` | `string` | Where BookSlot runs. Defaults to the hosted service. |
42
+ | `theme` | `{ primary?, background?, text?, radius?, font?, scheme? }` | Pro. Hex colours, radius 0–32, font `sans` / `system` / `serif` / `mono`, scheme `light` / `dark` / `auto`. Changing it reloads the widget. |
43
+ | `service` | `string` | Pro. Service id to preselect, listed in **Dashboard → Widget**. |
44
+ | `staff` | `string` | Pro. Staff member id to preselect, listed in **Dashboard → Widget**; `""` switches back to anyone. |
45
+ | `customer` | `{ name?, email?, phone?, notes?, fields? }` | Pro. Fills in the form; `fields` answers custom questions by key. Sent with `postMessage`, never in the URL. |
46
+ | `initialHeight` | `number` | Height before the widget reports its size. Default 720. |
47
+ | `maxWidth` | `number \| string` | Default 480px. |
48
+ | `title` | `string` | Accessible title of the iframe. |
49
+ | `className`, `style` | | Applied to the wrapper `div`. |
50
+ | `onReady` | `({ shop }) => void` | The widget loaded. |
51
+ | `onResize` | `({ height }) => void` | The widget changed height. |
52
+ | `onBooked` | `(booking) => void` | Pro. `{ appointmentId, serviceId, serviceName, staffId, staffName, startsAt, endsAt, when }`. |
53
+ | `onError` | `({ message }) => void` | Pro for booking errors; also called if the widget script can't load. |
54
+
55
+ ## Without React
56
+
57
+ Load the script and call it directly:
58
+
59
+ ```html
60
+ <script src="https://bookslot.taulantsela.com/widget.js" data-shop="your-shop-slug" async></script>
61
+ ```
62
+
63
+ `loadBookSlot(url?)` is exported too, if you want the underlying `BookSlot.mount` API in React.
64
+
65
+ ## License
66
+
67
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.tsx
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ BookSlotWidget: () => BookSlotWidget,
25
+ DEFAULT_BOOKSLOT_URL: () => DEFAULT_BOOKSLOT_URL,
26
+ loadBookSlot: () => loadBookSlot
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+ var import_react = require("react");
30
+ var import_jsx_runtime = require("react/jsx-runtime");
31
+ var DEFAULT_BOOKSLOT_URL = "https://bookslot.taulantsela.com";
32
+ function apiFor(origin, { justLoaded = false } = {}) {
33
+ var _a;
34
+ const globals = window;
35
+ const registered = (_a = globals.__bookslot) == null ? void 0 : _a[origin];
36
+ if (registered) return registered;
37
+ const legacy = globals.BookSlot;
38
+ return justLoaded && legacy && !legacy.origin ? legacy : void 0;
39
+ }
40
+ var loading = /* @__PURE__ */ new Map();
41
+ function loadBookSlot(bookslotUrl = DEFAULT_BOOKSLOT_URL) {
42
+ if (typeof window === "undefined") {
43
+ return Promise.reject(new Error("BookSlot can only load in the browser"));
44
+ }
45
+ const base = bookslotUrl.replace(/\/+$/, "");
46
+ const origin = new URL(base || "/", window.location.href).origin;
47
+ const src = `${base}/widget.js`;
48
+ const loaded = loading.has(src) ? void 0 : apiFor(origin);
49
+ if (loaded) return Promise.resolve(loaded);
50
+ let promise = loading.get(src);
51
+ if (!promise) {
52
+ promise = new Promise((resolve, reject) => {
53
+ const script = document.createElement("script");
54
+ script.src = src;
55
+ script.async = true;
56
+ script.onload = () => {
57
+ const api = apiFor(origin, { justLoaded: true });
58
+ if (api) resolve(api);
59
+ else reject(new Error(`BookSlot: ${src} loaded but did not define BookSlot`));
60
+ };
61
+ script.onerror = () => {
62
+ loading.delete(src);
63
+ script.remove();
64
+ reject(new Error(`BookSlot: could not load ${src}`));
65
+ };
66
+ document.head.appendChild(script);
67
+ });
68
+ loading.set(src, promise);
69
+ }
70
+ return promise;
71
+ }
72
+ function BookSlotWidget({
73
+ shop,
74
+ bookslotUrl = DEFAULT_BOOKSLOT_URL,
75
+ theme,
76
+ service,
77
+ staff,
78
+ customer,
79
+ initialHeight,
80
+ maxWidth,
81
+ title,
82
+ className,
83
+ style,
84
+ onReady,
85
+ onResize,
86
+ onBooked,
87
+ onError
88
+ }) {
89
+ const container = (0, import_react.useRef)(null);
90
+ const widget = (0, import_react.useRef)(null);
91
+ const latest = (0, import_react.useRef)({ service, staff, customer, onReady, onResize, onBooked, onError });
92
+ (0, import_react.useEffect)(() => {
93
+ latest.current = { service, staff, customer, onReady, onResize, onBooked, onError };
94
+ });
95
+ const themeKey = JSON.stringify(theme != null ? theme : {});
96
+ (0, import_react.useEffect)(() => {
97
+ let cancelled = false;
98
+ let instance = null;
99
+ loadBookSlot(bookslotUrl).then(
100
+ (api) => {
101
+ if (cancelled || !container.current) return;
102
+ const { service: service2, staff: staff2, customer: customer2 } = latest.current;
103
+ instance = api.mount(container.current, {
104
+ shop,
105
+ theme: JSON.parse(themeKey),
106
+ service: service2,
107
+ staff: staff2 || void 0,
108
+ prefill: customer2,
109
+ height: initialHeight,
110
+ maxWidth,
111
+ title
112
+ });
113
+ instance.on("ready", (d) => {
114
+ var _a, _b;
115
+ return (_b = (_a = latest.current).onReady) == null ? void 0 : _b.call(_a, d);
116
+ }).on("resize", (d) => {
117
+ var _a, _b;
118
+ return (_b = (_a = latest.current).onResize) == null ? void 0 : _b.call(_a, d);
119
+ }).on("booked", (d) => {
120
+ var _a, _b;
121
+ return (_b = (_a = latest.current).onBooked) == null ? void 0 : _b.call(_a, d);
122
+ }).on("error", (d) => {
123
+ var _a, _b;
124
+ return (_b = (_a = latest.current).onError) == null ? void 0 : _b.call(_a, d);
125
+ });
126
+ widget.current = instance;
127
+ },
128
+ (error) => {
129
+ var _a, _b;
130
+ if (!cancelled) (_b = (_a = latest.current).onError) == null ? void 0 : _b.call(_a, { message: error.message });
131
+ }
132
+ );
133
+ return () => {
134
+ cancelled = true;
135
+ instance == null ? void 0 : instance.destroy();
136
+ widget.current = null;
137
+ };
138
+ }, [shop, bookslotUrl, themeKey, initialHeight, maxWidth, title]);
139
+ (0, import_react.useEffect)(() => {
140
+ var _a;
141
+ if (service || staff !== void 0) (_a = widget.current) == null ? void 0 : _a.prefill({}, service, staff);
142
+ }, [service, staff]);
143
+ const customerKey = JSON.stringify(customer != null ? customer : {});
144
+ (0, import_react.useEffect)(() => {
145
+ var _a;
146
+ if (customerKey !== "{}") (_a = widget.current) == null ? void 0 : _a.prefill(JSON.parse(customerKey));
147
+ }, [customerKey]);
148
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: container, className, style });
149
+ }
150
+ // Annotate the CommonJS export names for ESM import in node:
151
+ 0 && (module.exports = {
152
+ BookSlotWidget,
153
+ DEFAULT_BOOKSLOT_URL,
154
+ loadBookSlot
155
+ });
@@ -0,0 +1,89 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+
4
+ declare const DEFAULT_BOOKSLOT_URL = "https://bookslot.taulantsela.com";
5
+ type BookSlotTheme = {
6
+ /** Hex colour for buttons and highlights. */
7
+ primary?: string;
8
+ /** Hex colour for the widget background. */
9
+ background?: string;
10
+ /** Hex colour for text. */
11
+ text?: string;
12
+ /** Corner radius in pixels, 0–32. */
13
+ radius?: number;
14
+ font?: "sans" | "system" | "serif" | "mono";
15
+ scheme?: "light" | "dark" | "auto";
16
+ };
17
+ type BookSlotCustomer = {
18
+ name?: string;
19
+ email?: string;
20
+ phone?: string;
21
+ notes?: string;
22
+ /** Answers to the shop's custom questions, keyed by question key (Dashboard → Booking form). */
23
+ fields?: Record<string, string | number | boolean>;
24
+ };
25
+ type BookSlotBooking = {
26
+ appointmentId: string;
27
+ serviceId: string;
28
+ serviceName: string;
29
+ /** Who it's with; null when the shop has no staff. */
30
+ staffId: string | null;
31
+ staffName: string | null;
32
+ /** ISO timestamp. */
33
+ startsAt: string;
34
+ /** ISO timestamp. */
35
+ endsAt: string;
36
+ /** Human-readable time in the shop's timezone, e.g. "Thu, 12 Nov 2026 · 11:00–12:00". */
37
+ when: string;
38
+ };
39
+ type BookSlotWidgetProps = {
40
+ /** The shop's slug from the BookSlot dashboard. */
41
+ shop: string;
42
+ /** Where BookSlot runs. Defaults to the hosted service. */
43
+ bookslotUrl?: string;
44
+ /** Pro: match your site's look. Changing it reloads the widget. */
45
+ theme?: BookSlotTheme;
46
+ /** Pro: service id to preselect (listed in Dashboard → Widget). Can change at any time. */
47
+ service?: string;
48
+ /** Pro: staff member id to preselect (listed in Dashboard → Widget), or "" for anyone. Can change at any time. */
49
+ staff?: string;
50
+ /** Pro: details of the signed-in user. Sent by postMessage, never in the URL. */
51
+ customer?: BookSlotCustomer;
52
+ /** Height in pixels before the widget reports its own size. Default 720. */
53
+ initialHeight?: number;
54
+ /** Max width of the frame. Default 480px. */
55
+ maxWidth?: number | string;
56
+ /** Accessible title of the iframe. */
57
+ title?: string;
58
+ className?: string;
59
+ style?: CSSProperties;
60
+ onReady?: (detail: {
61
+ shop: string;
62
+ }) => void;
63
+ onResize?: (detail: {
64
+ height: number;
65
+ }) => void;
66
+ /** Pro: a booking was made. */
67
+ onBooked?: (booking: BookSlotBooking) => void;
68
+ /** Pro: a booking failed. */
69
+ onError?: (detail: {
70
+ message: string;
71
+ }) => void;
72
+ };
73
+ type EventName = "ready" | "resize" | "booked" | "error";
74
+ type BookSlotInstance = {
75
+ on(type: EventName, fn: (detail: unknown) => void): BookSlotInstance;
76
+ prefill(customer: BookSlotCustomer, service?: string, staff?: string): BookSlotInstance;
77
+ destroy(): void;
78
+ };
79
+ type BookSlotApi = {
80
+ version: number;
81
+ /** The BookSlot address this API talks to. */
82
+ origin?: string;
83
+ mount(target: HTMLElement, options: Record<string, unknown>): BookSlotInstance;
84
+ };
85
+ /** Loads widget.js once per BookSlot address and resolves with that address's API. */
86
+ declare function loadBookSlot(bookslotUrl?: string): Promise<BookSlotApi>;
87
+ declare function BookSlotWidget({ shop, bookslotUrl, theme, service, staff, customer, initialHeight, maxWidth, title, className, style, onReady, onResize, onBooked, onError, }: BookSlotWidgetProps): react.JSX.Element;
88
+
89
+ export { type BookSlotBooking, type BookSlotCustomer, type BookSlotTheme, BookSlotWidget, type BookSlotWidgetProps, DEFAULT_BOOKSLOT_URL, loadBookSlot };
@@ -0,0 +1,89 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+
4
+ declare const DEFAULT_BOOKSLOT_URL = "https://bookslot.taulantsela.com";
5
+ type BookSlotTheme = {
6
+ /** Hex colour for buttons and highlights. */
7
+ primary?: string;
8
+ /** Hex colour for the widget background. */
9
+ background?: string;
10
+ /** Hex colour for text. */
11
+ text?: string;
12
+ /** Corner radius in pixels, 0–32. */
13
+ radius?: number;
14
+ font?: "sans" | "system" | "serif" | "mono";
15
+ scheme?: "light" | "dark" | "auto";
16
+ };
17
+ type BookSlotCustomer = {
18
+ name?: string;
19
+ email?: string;
20
+ phone?: string;
21
+ notes?: string;
22
+ /** Answers to the shop's custom questions, keyed by question key (Dashboard → Booking form). */
23
+ fields?: Record<string, string | number | boolean>;
24
+ };
25
+ type BookSlotBooking = {
26
+ appointmentId: string;
27
+ serviceId: string;
28
+ serviceName: string;
29
+ /** Who it's with; null when the shop has no staff. */
30
+ staffId: string | null;
31
+ staffName: string | null;
32
+ /** ISO timestamp. */
33
+ startsAt: string;
34
+ /** ISO timestamp. */
35
+ endsAt: string;
36
+ /** Human-readable time in the shop's timezone, e.g. "Thu, 12 Nov 2026 · 11:00–12:00". */
37
+ when: string;
38
+ };
39
+ type BookSlotWidgetProps = {
40
+ /** The shop's slug from the BookSlot dashboard. */
41
+ shop: string;
42
+ /** Where BookSlot runs. Defaults to the hosted service. */
43
+ bookslotUrl?: string;
44
+ /** Pro: match your site's look. Changing it reloads the widget. */
45
+ theme?: BookSlotTheme;
46
+ /** Pro: service id to preselect (listed in Dashboard → Widget). Can change at any time. */
47
+ service?: string;
48
+ /** Pro: staff member id to preselect (listed in Dashboard → Widget), or "" for anyone. Can change at any time. */
49
+ staff?: string;
50
+ /** Pro: details of the signed-in user. Sent by postMessage, never in the URL. */
51
+ customer?: BookSlotCustomer;
52
+ /** Height in pixels before the widget reports its own size. Default 720. */
53
+ initialHeight?: number;
54
+ /** Max width of the frame. Default 480px. */
55
+ maxWidth?: number | string;
56
+ /** Accessible title of the iframe. */
57
+ title?: string;
58
+ className?: string;
59
+ style?: CSSProperties;
60
+ onReady?: (detail: {
61
+ shop: string;
62
+ }) => void;
63
+ onResize?: (detail: {
64
+ height: number;
65
+ }) => void;
66
+ /** Pro: a booking was made. */
67
+ onBooked?: (booking: BookSlotBooking) => void;
68
+ /** Pro: a booking failed. */
69
+ onError?: (detail: {
70
+ message: string;
71
+ }) => void;
72
+ };
73
+ type EventName = "ready" | "resize" | "booked" | "error";
74
+ type BookSlotInstance = {
75
+ on(type: EventName, fn: (detail: unknown) => void): BookSlotInstance;
76
+ prefill(customer: BookSlotCustomer, service?: string, staff?: string): BookSlotInstance;
77
+ destroy(): void;
78
+ };
79
+ type BookSlotApi = {
80
+ version: number;
81
+ /** The BookSlot address this API talks to. */
82
+ origin?: string;
83
+ mount(target: HTMLElement, options: Record<string, unknown>): BookSlotInstance;
84
+ };
85
+ /** Loads widget.js once per BookSlot address and resolves with that address's API. */
86
+ declare function loadBookSlot(bookslotUrl?: string): Promise<BookSlotApi>;
87
+ declare function BookSlotWidget({ shop, bookslotUrl, theme, service, staff, customer, initialHeight, maxWidth, title, className, style, onReady, onResize, onBooked, onError, }: BookSlotWidgetProps): react.JSX.Element;
88
+
89
+ export { type BookSlotBooking, type BookSlotCustomer, type BookSlotTheme, BookSlotWidget, type BookSlotWidgetProps, DEFAULT_BOOKSLOT_URL, loadBookSlot };
package/dist/index.js ADDED
@@ -0,0 +1,129 @@
1
+ "use client";
2
+
3
+ // src/index.tsx
4
+ import { useEffect, useRef } from "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ var DEFAULT_BOOKSLOT_URL = "https://bookslot.taulantsela.com";
7
+ function apiFor(origin, { justLoaded = false } = {}) {
8
+ var _a;
9
+ const globals = window;
10
+ const registered = (_a = globals.__bookslot) == null ? void 0 : _a[origin];
11
+ if (registered) return registered;
12
+ const legacy = globals.BookSlot;
13
+ return justLoaded && legacy && !legacy.origin ? legacy : void 0;
14
+ }
15
+ var loading = /* @__PURE__ */ new Map();
16
+ function loadBookSlot(bookslotUrl = DEFAULT_BOOKSLOT_URL) {
17
+ if (typeof window === "undefined") {
18
+ return Promise.reject(new Error("BookSlot can only load in the browser"));
19
+ }
20
+ const base = bookslotUrl.replace(/\/+$/, "");
21
+ const origin = new URL(base || "/", window.location.href).origin;
22
+ const src = `${base}/widget.js`;
23
+ const loaded = loading.has(src) ? void 0 : apiFor(origin);
24
+ if (loaded) return Promise.resolve(loaded);
25
+ let promise = loading.get(src);
26
+ if (!promise) {
27
+ promise = new Promise((resolve, reject) => {
28
+ const script = document.createElement("script");
29
+ script.src = src;
30
+ script.async = true;
31
+ script.onload = () => {
32
+ const api = apiFor(origin, { justLoaded: true });
33
+ if (api) resolve(api);
34
+ else reject(new Error(`BookSlot: ${src} loaded but did not define BookSlot`));
35
+ };
36
+ script.onerror = () => {
37
+ loading.delete(src);
38
+ script.remove();
39
+ reject(new Error(`BookSlot: could not load ${src}`));
40
+ };
41
+ document.head.appendChild(script);
42
+ });
43
+ loading.set(src, promise);
44
+ }
45
+ return promise;
46
+ }
47
+ function BookSlotWidget({
48
+ shop,
49
+ bookslotUrl = DEFAULT_BOOKSLOT_URL,
50
+ theme,
51
+ service,
52
+ staff,
53
+ customer,
54
+ initialHeight,
55
+ maxWidth,
56
+ title,
57
+ className,
58
+ style,
59
+ onReady,
60
+ onResize,
61
+ onBooked,
62
+ onError
63
+ }) {
64
+ const container = useRef(null);
65
+ const widget = useRef(null);
66
+ const latest = useRef({ service, staff, customer, onReady, onResize, onBooked, onError });
67
+ useEffect(() => {
68
+ latest.current = { service, staff, customer, onReady, onResize, onBooked, onError };
69
+ });
70
+ const themeKey = JSON.stringify(theme != null ? theme : {});
71
+ useEffect(() => {
72
+ let cancelled = false;
73
+ let instance = null;
74
+ loadBookSlot(bookslotUrl).then(
75
+ (api) => {
76
+ if (cancelled || !container.current) return;
77
+ const { service: service2, staff: staff2, customer: customer2 } = latest.current;
78
+ instance = api.mount(container.current, {
79
+ shop,
80
+ theme: JSON.parse(themeKey),
81
+ service: service2,
82
+ staff: staff2 || void 0,
83
+ prefill: customer2,
84
+ height: initialHeight,
85
+ maxWidth,
86
+ title
87
+ });
88
+ instance.on("ready", (d) => {
89
+ var _a, _b;
90
+ return (_b = (_a = latest.current).onReady) == null ? void 0 : _b.call(_a, d);
91
+ }).on("resize", (d) => {
92
+ var _a, _b;
93
+ return (_b = (_a = latest.current).onResize) == null ? void 0 : _b.call(_a, d);
94
+ }).on("booked", (d) => {
95
+ var _a, _b;
96
+ return (_b = (_a = latest.current).onBooked) == null ? void 0 : _b.call(_a, d);
97
+ }).on("error", (d) => {
98
+ var _a, _b;
99
+ return (_b = (_a = latest.current).onError) == null ? void 0 : _b.call(_a, d);
100
+ });
101
+ widget.current = instance;
102
+ },
103
+ (error) => {
104
+ var _a, _b;
105
+ if (!cancelled) (_b = (_a = latest.current).onError) == null ? void 0 : _b.call(_a, { message: error.message });
106
+ }
107
+ );
108
+ return () => {
109
+ cancelled = true;
110
+ instance == null ? void 0 : instance.destroy();
111
+ widget.current = null;
112
+ };
113
+ }, [shop, bookslotUrl, themeKey, initialHeight, maxWidth, title]);
114
+ useEffect(() => {
115
+ var _a;
116
+ if (service || staff !== void 0) (_a = widget.current) == null ? void 0 : _a.prefill({}, service, staff);
117
+ }, [service, staff]);
118
+ const customerKey = JSON.stringify(customer != null ? customer : {});
119
+ useEffect(() => {
120
+ var _a;
121
+ if (customerKey !== "{}") (_a = widget.current) == null ? void 0 : _a.prefill(JSON.parse(customerKey));
122
+ }, [customerKey]);
123
+ return /* @__PURE__ */ jsx("div", { ref: container, className, style });
124
+ }
125
+ export {
126
+ BookSlotWidget,
127
+ DEFAULT_BOOKSLOT_URL,
128
+ loadBookSlot
129
+ };
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "@bookslot/react",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "React component for the BookSlot booking widget",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "require": {
18
+ "types": "./dist/index.d.cts",
19
+ "default": "./dist/index.cjs"
20
+ }
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "keywords": [
29
+ "booking",
30
+ "appointments",
31
+ "scheduling",
32
+ "widget",
33
+ "react",
34
+ "nextjs",
35
+ "bookslot"
36
+ ],
37
+ "homepage": "https://bookslot.taulantsela.com",
38
+ "peerDependencies": {
39
+ "react": ">=18"
40
+ },
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "prepublishOnly": "npm run build"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ }
48
+ }