@tzslot/angular 0.1.0-beta.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 Plinthworks
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 ADDED
@@ -0,0 +1,152 @@
1
+ # tzslot
2
+
3
+ Date and time pickers that know what daylight saving does.
4
+
5
+ Most pickers — flatpickr, Air Datepicker, nearly every Angular wrapper — are
6
+ built on `Date`, which has no notion of an IANA time zone. They will let
7
+ someone book 02:30 on a morning when 02:30 does not happen, or on one when it
8
+ happens twice, and store whichever reading the browser guessed. They call a
9
+ night shift from 23:00 to 05:00 six hours on the night it lasts seven.
10
+
11
+ tzslot is built on [Temporal](https://tc39.es/proposal-temporal/docs/). It
12
+ shows the hour that does not exist, offers both readings of the one that
13
+ happens twice, and says in words when an interval lasts something other than
14
+ what the clock faces suggest.
15
+
16
+ ## Packages
17
+
18
+ | | |
19
+ |---|---|
20
+ | `@tzslot/core` | The arithmetic. No DOM, no framework — usable on a server too. |
21
+ | `@tzslot/dom` | Every widget, in plain DOM. Works in any framework, or none. |
22
+ | `@tzslot/angular` | Angular components over `@tzslot/dom`: signals, forms, zoneless or not. Angular 18 to 21. |
23
+ | `@tzslot/theme` | Optional. CSS (or Sass) setting the widgets' colours; light, dark, high contrast, a Tailwind v4 bridge. |
24
+
25
+ The widgets live in `@tzslot/dom`; a framework package only translates its own
26
+ idioms into calls on them. A behaviour fixed there is fixed everywhere.
27
+
28
+ ## The widgets
29
+
30
+ | Angular | Plain DOM | What it chooses |
31
+ |---|---|---|
32
+ | `<tz-calendar>` | `createCalendar` | a day — month and year views, keyboard, Today / Clear |
33
+ | `<tz-multi-date>` | `createMultiDate` | several days, not necessarily adjacent |
34
+ | `<tz-date-field>` | `createDateField` | a day, from a field that opens a panel, anchored or centred |
35
+ | `<tz-date-range>` | `createDateRange` | two days, refusing a range across a closed one |
36
+ | `<tz-time-slots>` | `createTimeSlots` | a moment on one day: the skipped hour struck through, the repeated one offered twice |
37
+ | `<tz-datetime-range>` | `createDateTimeRange` | an interval with a time at both ends, and the hour it hides |
38
+ | `<tz-daily-range>` | `createDailyRange` | a range of days with the same hours on each, overnight allowed |
39
+
40
+ `renderCell` puts a price, places left or your own class on any day.
41
+
42
+ ## Angular
43
+
44
+ ```bash
45
+ npm install @tzslot/angular @tzslot/theme
46
+ ```
47
+
48
+ ```jsonc
49
+ // angular.json → build.options
50
+ "styles": ["@tzslot/theme/tzslot.css", "src/styles.css"]
51
+ ```
52
+
53
+ ```ts
54
+ import { Component, signal } from '@angular/core';
55
+ import { Calendar, TimeSlotPicker, provideTzslotMessages, FR } from '@tzslot/angular';
56
+ import type { Instant, PlainDate } from '@tzslot/core';
57
+
58
+ @Component({
59
+ imports: [Calendar, TimeSlotPicker],
60
+ template: `
61
+ <tz-calendar [(value)]="day" [buttons]="['today', 'clear']" />
62
+ @if (day(); as d) {
63
+ <tz-time-slots [date]="d" timeZone="Europe/Paris" [(value)]="moment" />
64
+ }
65
+ `,
66
+ })
67
+ export class Booking {
68
+ readonly day = signal<PlainDate | null>(null);
69
+ readonly moment = signal<Instant | null>(null);
70
+ }
71
+
72
+ // French everywhere: bootstrapApplication(App, { providers: [provideTzslotMessages(FR)] })
73
+ ```
74
+
75
+ Every component is a `ControlValueAccessor` — `formControlName`, `ngModel` and
76
+ `[(value)]` all work — and takes `valueAs="date"` to keep an existing
77
+ `FormControl<Date>` untouched. See `docs/migrating-from-flatpickr.md`.
78
+
79
+ ## Without a framework
80
+
81
+ ```bash
82
+ npm install @tzslot/dom @tzslot/theme
83
+ ```
84
+
85
+ ```js
86
+ import { createCalendar } from '@tzslot/dom';
87
+ import { Temporal } from '@tzslot/core';
88
+ import '@tzslot/theme';
89
+
90
+ const calendar = createCalendar(document.querySelector('#day'), {
91
+ onChange: (day) => console.log(day?.toString()),
92
+ });
93
+ calendar.update({ min: Temporal.Now.plainDateISO() });
94
+ ```
95
+
96
+ Each `create…` returns an instance with `update`, `clear` and `destroy`.
97
+ Layout CSS is injected once; pass `injectStyles: false` under a strict
98
+ Content-Security-Policy and include the exported `*_CSS` strings yourself.
99
+
100
+ ## The arithmetic
101
+
102
+ ```ts
103
+ import { getDaySlots, getDailyWindows } from '@tzslot/core';
104
+
105
+ getDaySlots('2026-10-25', 'Europe/Paris').filter((s) => s.time.hour === 2);
106
+ // 02:00 is ambiguous: offsets ['+02:00', '+01:00'], two instants an hour apart
107
+
108
+ getDailyWindows('2026-10-23', '2026-10-26', '22:00', '06:00', 'Europe/Paris').minutes / 60;
109
+ // 33 — four night shifts, one of them nine hours long
110
+ ```
111
+
112
+ Store an instant, never a wall time: it is unambiguous everywhere and
113
+ survives a change of zone, of the rules, and being read back next year.
114
+
115
+ ## Theming
116
+
117
+ Nine palette colours, each `light-dark(light, dark)`. `data-theme="dark"` on
118
+ any element themes everything inside it; `--tz-accent` on any element
119
+ recolours the selection, the range tint and the focus ring beneath it.
120
+ `@tzslot/theme/contrast.css` follows `prefers-contrast: more`, and
121
+ `@tzslot/theme/tailwind.css` maps the palette to a Tailwind v4 theme. From
122
+ Sass, `@use '@tzslot/theme/tzslot' with ($accent: …)`. All of it in
123
+ `docs/customising.md`.
124
+
125
+ ## Browsers
126
+
127
+ Temporal is used natively where it exists and polyfilled where it does not.
128
+ The polyfill is a static import, so a bundle carries it for every visitor —
129
+ about 19 kB gzipped. There is deliberately no fallback to `Date`: it would
130
+ bring back the exact bug this library exists to fix.
131
+
132
+ The theme uses `light-dark()` and `color-mix()`: Chrome 123, Safari 17.5,
133
+ Firefox 120 and later.
134
+
135
+ ## Development
136
+
137
+ ```bash
138
+ npm install
139
+ npm start # playground: http://localhost:4500/playground/index.html
140
+ npm test # 263 tests, against real time zone rules
141
+ npm run typecheck
142
+ npm run build # every package into its own dist/, ready to publish
143
+ ```
144
+
145
+ The playground has a page per way of using it: Angular, Tailwind, and plain
146
+ DOM.
147
+
148
+ ## Licence
149
+
150
+ [MIT](LICENSE). Free to use, in personal and commercial projects alike, free
151
+ to copy, modify and redistribute; keep the copyright notice. It comes with no
152
+ warranty.