@nickkupriyanov/slotline-react 0.1.1 → 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/README.md +39 -21
- package/dist/index.cjs +544 -642
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -77
- package/dist/index.d.ts +76 -77
- package/dist/index.js +378 -634
- package/dist/index.js.map +1 -1
- package/dist/motion.cjs +544 -642
- package/dist/motion.cjs.map +1 -1
- package/dist/motion.d.cts +4 -3
- package/dist/motion.d.ts +4 -3
- package/dist/motion.js +378 -634
- package/dist/motion.js.map +1 -1
- package/dist/slotline.css +18 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -30,17 +30,17 @@ export const Booking = () => <Slotline adapter={createMockAdapter()} theme="dark
|
|
|
30
30
|
- **Soft animations with zero runtime**: direction-aware step transitions, animated height, staggered slot reveals, a self-drawing check mark. Optional `@nickkupriyanov/slotline-react/motion` entry for spring transitions.
|
|
31
31
|
- **Accessible**: ARIA grid with roving tabindex, keyboard navigation, live-region step announcements, focus management, labelled controls, `prefers-reduced-motion` support.
|
|
32
32
|
- **Correct dates**: day keys are time-zone aware, slots are instants, everything formats through `Intl`. `en` and `ru` copy included, any locale tag works.
|
|
33
|
-
- **Small**: ~
|
|
33
|
+
- **Small**: ~29 kB gzip ESM (this package plus the core it depends on) + ~4.5 kB gzip CSS, no third-party runtime dependencies.
|
|
34
34
|
|
|
35
35
|
## Entries
|
|
36
36
|
|
|
37
|
-
| Import
|
|
38
|
-
|
|
|
39
|
-
| `@nickkupriyanov/slotline-react`
|
|
40
|
-
| `@nickkupriyanov/slotline-react/motion`
|
|
41
|
-
| `@nickkupriyanov/slotline-react/styles.css` | The stylesheet. Import it once.
|
|
37
|
+
| Import | What you get |
|
|
38
|
+
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
39
|
+
| `@nickkupriyanov/slotline-react` | `Slotline`, `SlotlineProvider`, `SlotlineView`, `useBookingFlow`, `useSlotlineContext`, `createMockAdapter`, types, date and i18n helpers. |
|
|
40
|
+
| `@nickkupriyanov/slotline-react/motion` | The same API with a spring-based animation kit. Requires the optional `motion` peer dependency. |
|
|
41
|
+
| `@nickkupriyanov/slotline-react/styles.css` | The stylesheet. Import it once. |
|
|
42
42
|
|
|
43
|
-
The
|
|
43
|
+
The controller, the derived view model, the reducer, the `Intl` date helpers, the copy, the class names and the stylesheet come from **`@nickkupriyanov/slotline-core`**, a dependency of this package. React users rarely import it directly — it exists so another binding renders the same flow against the same core.
|
|
44
44
|
|
|
45
45
|
## Adapter
|
|
46
46
|
|
|
@@ -48,25 +48,36 @@ The state machine, the `Intl` date helpers, the copy, the class names and the st
|
|
|
48
48
|
import type { BookingAdapter } from '@nickkupriyanov/slotline-react';
|
|
49
49
|
|
|
50
50
|
export const adapter: BookingAdapter = {
|
|
51
|
-
async getAvailability({ from, to, duration, timeZone, signal }) {
|
|
52
|
-
const res = await fetch(
|
|
51
|
+
async getAvailability({ from, to, duration, timeZone, context, signal }) {
|
|
52
|
+
const res = await fetch(
|
|
53
|
+
`/api/availability?from=${from}&to=${to}&duration=${duration}&tz=${timeZone}&schedule=${context.key}`,
|
|
54
|
+
{ signal },
|
|
55
|
+
);
|
|
53
56
|
if (!res.ok) throw new Error('Could not load availability');
|
|
54
57
|
return res.json(); // [{ date: '2026-05-04', slotCount: 8 }, …]
|
|
55
58
|
},
|
|
56
59
|
async getSlots({ date, duration, timeZone, signal }) {
|
|
57
|
-
const res = await fetch(`/api/slots?date=${date}&duration=${duration}&tz=${timeZone}`, {
|
|
60
|
+
const res = await fetch(`/api/slots?date=${date}&duration=${duration}&tz=${timeZone}`, {
|
|
61
|
+
signal,
|
|
62
|
+
});
|
|
58
63
|
if (!res.ok) throw new Error('Could not load times');
|
|
59
|
-
return res.json(); // [{ start: '2026-05-04T07:15:00Z' }, …]
|
|
64
|
+
return res.json(); // [{ id: '2026-05-04#0915', start: '2026-05-04T07:15:00Z' }, …]
|
|
60
65
|
},
|
|
61
|
-
async createBooking(
|
|
62
|
-
const res = await fetch('/api/bookings', {
|
|
66
|
+
async createBooking({ slot, values, idempotencyKey }) {
|
|
67
|
+
const res = await fetch('/api/bookings', {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'idempotency-key': idempotencyKey },
|
|
70
|
+
body: JSON.stringify({ slotId: slot.id, values }),
|
|
71
|
+
});
|
|
63
72
|
if (!res.ok) throw new Error('Booking failed');
|
|
64
|
-
return res.json(); // { id,
|
|
73
|
+
return res.json(); // { id, status: 'confirmed', invitation? }
|
|
65
74
|
},
|
|
75
|
+
// Optional: a repeat of an unknown outcome stays safe for this long.
|
|
76
|
+
capabilities: { idempotentCreate: true, idempotencyRetentionMs: 86_400_000 },
|
|
66
77
|
};
|
|
67
78
|
```
|
|
68
79
|
|
|
69
|
-
`request` is `{ slot, date, duration, timeZone,
|
|
80
|
+
`request` is `{ slot, date, duration, timeZone, context, values, idempotencyKey }`: every offer has a stable `id`, and `values` is the typed form payload. Every query receives an `AbortSignal` tied to the widget's lifetime.
|
|
70
81
|
|
|
71
82
|
## Theming
|
|
72
83
|
|
|
@@ -89,19 +100,26 @@ Set `theme="light" | "dark" | "system"` on the component; the resolved theme lan
|
|
|
89
100
|
import { SlotlineProvider, useSlotlineContext } from '@nickkupriyanov/slotline-react';
|
|
90
101
|
|
|
91
102
|
const Panel = () => {
|
|
92
|
-
const {
|
|
103
|
+
const { snapshot, actions } = useSlotlineContext();
|
|
104
|
+
const view = snapshot.view;
|
|
93
105
|
return (
|
|
94
106
|
<>
|
|
95
|
-
<h2>{
|
|
96
|
-
{
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
<h2>{view.month.label}</h2>
|
|
108
|
+
{view.month.grid
|
|
109
|
+
.filter((day) => view.days[day]?.bookable)
|
|
110
|
+
.map((day) => (
|
|
111
|
+
<button key={day} onClick={() => actions.selectDate(day)}>
|
|
112
|
+
{day}
|
|
113
|
+
</button>
|
|
114
|
+
))}
|
|
99
115
|
</>
|
|
100
116
|
);
|
|
101
117
|
};
|
|
102
118
|
|
|
103
119
|
export const Custom = () => (
|
|
104
|
-
<SlotlineProvider adapter={adapter} theme="dark"
|
|
120
|
+
<SlotlineProvider adapter={adapter} theme="dark">
|
|
121
|
+
<Panel />
|
|
122
|
+
</SlotlineProvider>
|
|
105
123
|
);
|
|
106
124
|
```
|
|
107
125
|
|