@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 +21 -0
- package/README.md +152 -0
- package/fesm2022/tzslot-angular.mjs +875 -0
- package/fesm2022/tzslot-angular.mjs.map +1 -0
- package/package.json +47 -0
- package/types/tzslot-angular.d.ts +370 -0
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.
|