@lilydesignsystem/angular-time-zone-picker 0.0.0-stage → 0.1.1

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.
@@ -0,0 +1,931 @@
1
+ import { NgTemplateOutlet } from "@angular/common";
2
+ import * as i0 from "@angular/core";
3
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Directive, TemplateRef, computed, contentChild, effect, inject, input, model, output, signal, viewChild } from "@angular/core";
4
+ import { IconButton, Listbox } from "@lilydesignsystem/angular-headless";
5
+ const DEFAULT_ZONES = [
6
+ "Pacific/Pago_Pago",
7
+ "Pacific/Honolulu",
8
+ "Pacific/Marquesas",
9
+ "America/Anchorage",
10
+ "America/Los_Angeles",
11
+ "America/Phoenix",
12
+ "America/Denver",
13
+ "America/Chicago",
14
+ "America/Mexico_City",
15
+ "America/New_York",
16
+ "America/Bogota",
17
+ "America/Halifax",
18
+ "America/Caracas",
19
+ "America/St_Johns",
20
+ "America/Sao_Paulo",
21
+ "America/Argentina/Buenos_Aires",
22
+ "Atlantic/South_Georgia",
23
+ "Atlantic/Azores",
24
+ "UTC",
25
+ "Africa/Abidjan",
26
+ "Europe/London",
27
+ "Europe/Lisbon",
28
+ "Africa/Casablanca",
29
+ "Africa/Lagos",
30
+ "Europe/Paris",
31
+ "Europe/Athens",
32
+ "Africa/Cairo",
33
+ "Africa/Maputo",
34
+ "Africa/Johannesburg",
35
+ "Europe/Moscow",
36
+ "Africa/Nairobi",
37
+ "Asia/Riyadh",
38
+ "Asia/Tehran",
39
+ "Asia/Dubai",
40
+ "Asia/Kabul",
41
+ "Asia/Karachi",
42
+ "Asia/Kolkata",
43
+ "Asia/Kathmandu",
44
+ "Asia/Dhaka",
45
+ "Asia/Yangon",
46
+ "Asia/Bangkok",
47
+ "Asia/Jakarta",
48
+ "Asia/Shanghai",
49
+ "Asia/Singapore",
50
+ "Asia/Kuala_Lumpur",
51
+ "Asia/Manila",
52
+ "Asia/Makassar",
53
+ "Australia/Perth",
54
+ "Australia/Eucla",
55
+ "Asia/Tokyo",
56
+ "Asia/Seoul",
57
+ "Asia/Jayapura",
58
+ "Australia/Darwin",
59
+ "Australia/Adelaide",
60
+ "Australia/Brisbane",
61
+ "Australia/Sydney",
62
+ "Australia/Lord_Howe",
63
+ "Pacific/Guadalcanal",
64
+ "Pacific/Auckland",
65
+ "Pacific/Fiji",
66
+ "Pacific/Chatham",
67
+ "Pacific/Tongatapu",
68
+ "Pacific/Kiritimati"
69
+ ];
70
+ const offsetFormatters = /* @__PURE__ */ new Map();
71
+ function offsetMinutes(zone, date) {
72
+ try {
73
+ let f = offsetFormatters.get(zone);
74
+ if (!f) {
75
+ f = new Intl.DateTimeFormat("en-US", {
76
+ timeZone: zone,
77
+ hourCycle: "h23",
78
+ year: "numeric",
79
+ month: "numeric",
80
+ day: "numeric",
81
+ hour: "numeric",
82
+ minute: "numeric",
83
+ second: "numeric"
84
+ });
85
+ offsetFormatters.set(zone, f);
86
+ }
87
+ const p = {};
88
+ for (const part of f.formatToParts(date)) p[part.type] = part.value;
89
+ const wall = Date.UTC(Number(p["year"]), Number(p["month"]) - 1, Number(p["day"]), Number(p["hour"]), Number(p["minute"]), Number(p["second"]));
90
+ const instant = Math.floor(date.getTime() / 1e3) * 1e3;
91
+ return Math.round((wall - instant) / 6e4);
92
+ } catch {
93
+ return;
94
+ }
95
+ }
96
+ function isDaylightTime(zone, date) {
97
+ const now = offsetMinutes(zone, date);
98
+ if (now === void 0) return false;
99
+ const year = date.getUTCFullYear();
100
+ const jan = offsetMinutes(zone, new Date(Date.UTC(year, 0, 1, 12)));
101
+ const jul = offsetMinutes(zone, new Date(Date.UTC(year, 6, 1, 12)));
102
+ if (jan === void 0 || jul === void 0) return false;
103
+ return now > Math.min(jan, jul);
104
+ }
105
+ function formatOffset(minutes) {
106
+ const sign = minutes < 0 ? "-" : "+";
107
+ const abs = Math.abs(minutes);
108
+ return `UTC${sign}${String(Math.floor(abs / 60)).padStart(2, "0")}:${String(abs % 60).padStart(2, "0")}`;
109
+ }
110
+ function zoneName(zone, locale, date, names) {
111
+ const given = names?.[zone];
112
+ if (typeof given === "string") return given;
113
+ if (given) return isDaylightTime(zone, date) ? given[1] : given[0];
114
+ try {
115
+ return new Intl.DateTimeFormat(locale, {
116
+ timeZone: zone,
117
+ timeZoneName: "long"
118
+ }).formatToParts(date).find((p) => p.type === "timeZoneName")?.value;
119
+ } catch {
120
+ return;
121
+ }
122
+ }
123
+ const defaultLabel = (zone) => `${zone.offset} – ${zone.name}`;
124
+ function zoneInfo(zone, locale, date, labelFor = defaultLabel, names) {
125
+ const minutes = offsetMinutes(zone, date);
126
+ if (minutes === void 0) return void 0;
127
+ const name = zoneName(zone, locale, date, names) ?? zone;
128
+ const info = {
129
+ id: zone,
130
+ offsetMinutes: minutes,
131
+ offset: formatOffset(minutes),
132
+ name,
133
+ label: ""
134
+ };
135
+ info.label = labelFor(info);
136
+ return info;
137
+ }
138
+ function orderedZones(zones, locale, date, labelFor, names) {
139
+ const seen = /* @__PURE__ */ new Set();
140
+ const out = [];
141
+ for (const id of zones) {
142
+ if (seen.has(id)) continue;
143
+ seen.add(id);
144
+ const info = zoneInfo(id, locale, date, labelFor, names);
145
+ if (info) out.push(info);
146
+ }
147
+ const collator = new Intl.Collator(locale);
148
+ return out.sort((a, b) => a.offsetMinutes - b.offsetMinutes || collator.compare(a.name, b.name) || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
149
+ }
150
+ function systemZone() {
151
+ try {
152
+ return new Intl.DateTimeFormat().resolvedOptions().timeZone || void 0;
153
+ } catch {
154
+ return;
155
+ }
156
+ }
157
+ function matchZone(system, zones, locale, date, names) {
158
+ if (!system) return void 0;
159
+ if (zones.some((z) => z.id === system)) return system;
160
+ const info = zoneInfo(system, locale, date, void 0, names);
161
+ if (!info) return void 0;
162
+ return zones.find((z) => z.offsetMinutes === info.offsetMinutes && z.name === info.name)?.id;
163
+ }
164
+ let uid = 0;
165
+ function nextTimeZonePickerId() {
166
+ uid += 1;
167
+ return `time-zone-picker-${uid}`;
168
+ }
169
+ var TimeZonePickerIcon = class TimeZonePickerIcon {
170
+ static ngTemplateContextGuard(_dir, _ctx) {
171
+ return true;
172
+ }
173
+ static {
174
+ this.ɵfac = i0.ɵɵngDeclareFactory({
175
+ minVersion: "12.0.0",
176
+ version: "22.2.4",
177
+ ngImport: i0,
178
+ type: TimeZonePickerIcon,
179
+ deps: [],
180
+ target: i0.ɵɵFactoryTarget.Directive
181
+ });
182
+ }
183
+ static {
184
+ this.ɵdir = i0.ɵɵngDeclareDirective({
185
+ minVersion: "14.0.0",
186
+ version: "22.2.4",
187
+ type: TimeZonePickerIcon,
188
+ isStandalone: true,
189
+ selector: "ng-template[lilyTimeZonePickerIcon]",
190
+ ngImport: i0
191
+ });
192
+ }
193
+ };
194
+ i0.ɵɵngDeclareClassMetadata({
195
+ minVersion: "12.0.0",
196
+ version: "22.2.4",
197
+ ngImport: i0,
198
+ type: TimeZonePickerIcon,
199
+ decorators: [{
200
+ type: Directive,
201
+ args: [{
202
+ selector: "ng-template[lilyTimeZonePickerIcon]",
203
+ standalone: true
204
+ }]
205
+ }]
206
+ });
207
+ var TimeZonePicker = class TimeZonePicker {
208
+ onTooltipButtonEnter() {
209
+ this.hoverButton.set(true);
210
+ this.dismissed.set(false);
211
+ }
212
+ onTooltipButtonFocusIn() {
213
+ try {
214
+ this.focusButton.set(this.buttonRef().element?.matches(":focus-visible") ?? false);
215
+ } catch {
216
+ this.focusButton.set(true);
217
+ }
218
+ }
219
+ onTooltipButtonFocusOut() {
220
+ this.focusButton.set(false);
221
+ this.dismissed.set(false);
222
+ }
223
+ onTooltipKeydown(event) {
224
+ if (event.key === "Escape" && this.tooltipVisible()) this.dismissed.set(true);
225
+ }
226
+ infoFor(zone) {
227
+ return zoneInfo(zone, this.locale(), this.when(), this.labelFor(), this.names());
228
+ }
229
+ constructor() {
230
+ this.label = input.required(...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
231
+ this.locale = input.required(...ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []);
232
+ this.zones = input(DEFAULT_ZONES, ...ngDevMode ? [{ debugName: "zones" }] : /* istanbul ignore next */ []);
233
+ this.date = input(void 0, ...ngDevMode ? [{ debugName: "date" }] : /* istanbul ignore next */ []);
234
+ this.labelFor = input(void 0, ...ngDevMode ? [{ debugName: "labelFor" }] : /* istanbul ignore next */ []);
235
+ this.names = input(void 0, ...ngDevMode ? [{ debugName: "names" }] : /* istanbul ignore next */ []);
236
+ this.value = model("", ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
237
+ this.defaultValue = input("", ...ngDevMode ? [{ debugName: "defaultValue" }] : /* istanbul ignore next */ []);
238
+ this.storageKey = input("", ...ngDevMode ? [{ debugName: "storageKey" }] : /* istanbul ignore next */ []);
239
+ this.name = input("time-zone", ...ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []);
240
+ this.target = input(null, ...ngDevMode ? [{ debugName: "target" }] : /* istanbul ignore next */ []);
241
+ this.className = input("", ...ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []);
242
+ this.zoneChange = output();
243
+ this.iconTemplate = contentChild(TemplateRef, ...ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []);
244
+ this.rootRef = viewChild.required("rootEl", ...ngDevMode ? [{ debugName: "rootRef" }] : /* istanbul ignore next */ []);
245
+ this.buttonRef = viewChild.required("buttonEl", ...ngDevMode ? [{ debugName: "buttonRef" }] : /* istanbul ignore next */ []);
246
+ this.listRef = viewChild.required("listEl", ...ngDevMode ? [{ debugName: "listRef" }] : /* istanbul ignore next */ []);
247
+ this.baseId = nextTimeZonePickerId();
248
+ this.listId = `${this.baseId}-list`;
249
+ this.open = model(false, ...ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []);
250
+ this.disabled = input(false, ...ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []);
251
+ this.closeWhenDisabled = effect(() => {
252
+ if (this.disabled() && this.open()) this.open.set(false);
253
+ }, ...ngDevMode ? [{ debugName: "closeWhenDisabled" }] : /* istanbul ignore next */ []);
254
+ this.tooltipId = `${this.baseId}-tooltip`;
255
+ this.hoverButton = signal(false, ...ngDevMode ? [{ debugName: "hoverButton" }] : /* istanbul ignore next */ []);
256
+ this.hoverTooltip = signal(false, ...ngDevMode ? [{ debugName: "hoverTooltip" }] : /* istanbul ignore next */ []);
257
+ this.focusButton = signal(false, ...ngDevMode ? [{ debugName: "focusButton" }] : /* istanbul ignore next */ []);
258
+ this.dismissed = signal(false, ...ngDevMode ? [{ debugName: "dismissed" }] : /* istanbul ignore next */ []);
259
+ this.tooltipVisible = computed(() => !this.open() && !this.dismissed() && (this.hoverButton() || this.hoverTooltip() || this.focusButton()), ...ngDevMode ? [{ debugName: "tooltipVisible" }] : /* istanbul ignore next */ []);
260
+ this.tooltipEscapeListener = effect((onCleanup) => {
261
+ if (!this.tooltipVisible() || typeof document === "undefined") return;
262
+ const onKey = (event) => {
263
+ if (event.key === "Escape") this.dismissed.set(true);
264
+ };
265
+ document.addEventListener("keydown", onKey);
266
+ onCleanup(() => document.removeEventListener("keydown", onKey));
267
+ }, ...ngDevMode ? [{ debugName: "tooltipEscapeListener" }] : /* istanbul ignore next */ []);
268
+ this.activeIndex = signal(-1, ...ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []);
269
+ this.whenInitial = /* @__PURE__ */ new Date();
270
+ this.when = computed(() => this.date() ?? this.whenInitial, ...ngDevMode ? [{ debugName: "when" }] : /* istanbul ignore next */ []);
271
+ this.infos = computed(() => orderedZones(this.zones(), this.locale(), this.when(), this.labelFor(), this.names()), ...ngDevMode ? [{ debugName: "infos" }] : /* istanbul ignore next */ []);
272
+ this.ids = computed(() => this.infos().map((z) => z.id), ...ngDevMode ? [{ debugName: "ids" }] : /* istanbul ignore next */ []);
273
+ this.childContext = computed(() => {
274
+ const args = {
275
+ value: this.value(),
276
+ open: this.open(),
277
+ infoFor: (zone) => this.infoFor(zone)
278
+ };
279
+ return {
280
+ $implicit: args,
281
+ ...args
282
+ };
283
+ }, ...ngDevMode ? [{ debugName: "childContext" }] : /* istanbul ignore next */ []);
284
+ this.initialised = false;
285
+ this.cdr = inject(ChangeDetectorRef);
286
+ this.scrollOnActiveIndexChange = effect(() => {
287
+ this.activeIndex();
288
+ this.scrollActiveIntoView();
289
+ }, ...ngDevMode ? [{ debugName: "scrollOnActiveIndexChange" }] : /* istanbul ignore next */ []);
290
+ effect(() => {
291
+ const current = this.value();
292
+ if (!this.initialised) {
293
+ this.initialised = true;
294
+ let initial = current;
295
+ const sk = this.storageKey();
296
+ if (!initial && sk) try {
297
+ const stored = (typeof localStorage !== "undefined" ? localStorage.getItem(sk) : null) ?? "";
298
+ if (this.ids().includes(stored)) initial = stored;
299
+ } catch {}
300
+ if (!initial) {
301
+ const ids = this.ids();
302
+ initial = this.defaultValue() || matchZone(systemZone(), this.infos(), this.locale(), this.when(), this.names()) || (ids.includes("UTC") ? "UTC" : ids[0]) || "";
303
+ }
304
+ if (initial && initial !== current) {
305
+ this.value.set(initial);
306
+ return;
307
+ }
308
+ }
309
+ if (current) this.applyZone(current);
310
+ });
311
+ }
312
+ optionId(index) {
313
+ return `${this.baseId}-option-${index}`;
314
+ }
315
+ toggle() {
316
+ if (this.open()) this.closeList();
317
+ else this.openList();
318
+ }
319
+ openList(startIndex) {
320
+ const selected = this.ids().indexOf(this.value());
321
+ this.activeIndex.set(this.ids().length === 0 ? -1 : startIndex ?? (selected >= 0 ? selected : 0));
322
+ this.open.set(true);
323
+ this.cdr.detectChanges();
324
+ queueMicrotask(() => {
325
+ this.listRef().focus({ preventScroll: true });
326
+ this.scrollActiveIntoView();
327
+ });
328
+ }
329
+ closeList(refocus = true) {
330
+ if (!this.open()) return;
331
+ this.open.set(false);
332
+ this.activeIndex.set(-1);
333
+ if (refocus) queueMicrotask(() => this.buttonRef().focus({ preventScroll: true }));
334
+ }
335
+ choose(index) {
336
+ const slug = this.ids()[index];
337
+ if (slug) this.value.set(slug);
338
+ this.closeList();
339
+ }
340
+ scrollActiveIntoView() {
341
+ const i = this.activeIndex();
342
+ if (i < 0) return;
343
+ document.getElementById(this.optionId(i))?.scrollIntoView?.({ block: "nearest" });
344
+ }
345
+ onButtonKeydown(event) {
346
+ switch (event.key) {
347
+ case "ArrowDown":
348
+ case "Enter":
349
+ case " ":
350
+ event.preventDefault();
351
+ this.openList();
352
+ break;
353
+ case "ArrowUp":
354
+ event.preventDefault();
355
+ this.openList(this.ids().length - 1);
356
+ }
357
+ }
358
+ onListTabOut() {
359
+ this.buttonRef().focus({ preventScroll: true });
360
+ this.closeList(false);
361
+ }
362
+ onRootFocusOut(event) {
363
+ const next = event.relatedTarget;
364
+ if (next && this.rootRef().nativeElement.contains(next)) return;
365
+ this.closeList(false);
366
+ }
367
+ onDocumentClick(event) {
368
+ if (!this.open()) return;
369
+ const t = event.target;
370
+ if (t && !this.rootRef().nativeElement.contains(t)) this.closeList(false);
371
+ }
372
+ applyZone(slug) {
373
+ if (typeof document === "undefined" || !slug) return;
374
+ (this.target() ?? document.documentElement).setAttribute("data-time-zone", slug);
375
+ const sk = this.storageKey();
376
+ if (sk) try {
377
+ localStorage.setItem(sk, slug);
378
+ } catch {}
379
+ this.zoneChange.emit(slug);
380
+ }
381
+ static {
382
+ this.ɵfac = i0.ɵɵngDeclareFactory({
383
+ minVersion: "12.0.0",
384
+ version: "22.2.4",
385
+ ngImport: i0,
386
+ type: TimeZonePicker,
387
+ deps: [],
388
+ target: i0.ɵɵFactoryTarget.Component
389
+ });
390
+ }
391
+ static {
392
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
393
+ minVersion: "17.0.0",
394
+ version: "22.2.4",
395
+ type: TimeZonePicker,
396
+ isStandalone: true,
397
+ selector: "lily-time-zone-picker",
398
+ inputs: {
399
+ label: {
400
+ classPropertyName: "label",
401
+ publicName: "label",
402
+ isSignal: true,
403
+ isRequired: true,
404
+ transformFunction: null
405
+ },
406
+ locale: {
407
+ classPropertyName: "locale",
408
+ publicName: "locale",
409
+ isSignal: true,
410
+ isRequired: true,
411
+ transformFunction: null
412
+ },
413
+ zones: {
414
+ classPropertyName: "zones",
415
+ publicName: "zones",
416
+ isSignal: true,
417
+ isRequired: false,
418
+ transformFunction: null
419
+ },
420
+ date: {
421
+ classPropertyName: "date",
422
+ publicName: "date",
423
+ isSignal: true,
424
+ isRequired: false,
425
+ transformFunction: null
426
+ },
427
+ labelFor: {
428
+ classPropertyName: "labelFor",
429
+ publicName: "labelFor",
430
+ isSignal: true,
431
+ isRequired: false,
432
+ transformFunction: null
433
+ },
434
+ names: {
435
+ classPropertyName: "names",
436
+ publicName: "names",
437
+ isSignal: true,
438
+ isRequired: false,
439
+ transformFunction: null
440
+ },
441
+ value: {
442
+ classPropertyName: "value",
443
+ publicName: "value",
444
+ isSignal: true,
445
+ isRequired: false,
446
+ transformFunction: null
447
+ },
448
+ defaultValue: {
449
+ classPropertyName: "defaultValue",
450
+ publicName: "defaultValue",
451
+ isSignal: true,
452
+ isRequired: false,
453
+ transformFunction: null
454
+ },
455
+ storageKey: {
456
+ classPropertyName: "storageKey",
457
+ publicName: "storageKey",
458
+ isSignal: true,
459
+ isRequired: false,
460
+ transformFunction: null
461
+ },
462
+ name: {
463
+ classPropertyName: "name",
464
+ publicName: "name",
465
+ isSignal: true,
466
+ isRequired: false,
467
+ transformFunction: null
468
+ },
469
+ target: {
470
+ classPropertyName: "target",
471
+ publicName: "target",
472
+ isSignal: true,
473
+ isRequired: false,
474
+ transformFunction: null
475
+ },
476
+ className: {
477
+ classPropertyName: "className",
478
+ publicName: "className",
479
+ isSignal: true,
480
+ isRequired: false,
481
+ transformFunction: null
482
+ },
483
+ open: {
484
+ classPropertyName: "open",
485
+ publicName: "open",
486
+ isSignal: true,
487
+ isRequired: false,
488
+ transformFunction: null
489
+ },
490
+ disabled: {
491
+ classPropertyName: "disabled",
492
+ publicName: "disabled",
493
+ isSignal: true,
494
+ isRequired: false,
495
+ transformFunction: null
496
+ }
497
+ },
498
+ outputs: {
499
+ value: "valueChange",
500
+ zoneChange: "zoneChange",
501
+ open: "openChange"
502
+ },
503
+ host: { listeners: { "document:click": "onDocumentClick($event)" } },
504
+ queries: [{
505
+ propertyName: "iconTemplate",
506
+ first: true,
507
+ predicate: TemplateRef,
508
+ descendants: true,
509
+ isSignal: true
510
+ }],
511
+ viewQueries: [
512
+ {
513
+ propertyName: "rootRef",
514
+ first: true,
515
+ predicate: ["rootEl"],
516
+ descendants: true,
517
+ isSignal: true
518
+ },
519
+ {
520
+ propertyName: "buttonRef",
521
+ first: true,
522
+ predicate: ["buttonEl"],
523
+ descendants: true,
524
+ isSignal: true
525
+ },
526
+ {
527
+ propertyName: "listRef",
528
+ first: true,
529
+ predicate: ["listEl"],
530
+ descendants: true,
531
+ isSignal: true
532
+ }
533
+ ],
534
+ ngImport: i0,
535
+ template: `
536
+ <div
537
+ #rootEl
538
+ class="time-zone-picker {{ className() }}"
539
+ (focusout)="onRootFocusOut($event)"
540
+ >
541
+ <input type="hidden" [name]="name()" [value]="value()" />
542
+
543
+ <lily-icon-button
544
+ [disabled]="disabled()"
545
+ #buttonEl
546
+ [label]="label()"
547
+ baseClass="time-zone-picker-button"
548
+ ariaHaspopup="listbox"
549
+ [ariaExpanded]="open()"
550
+ [ariaControls]="listId"
551
+ (click)="toggle(); hoverButton.set(false)"
552
+ (mouseenter)="onTooltipButtonEnter()"
553
+ (mouseleave)="hoverButton.set(false)"
554
+ (focusin)="onTooltipButtonFocusIn()"
555
+ (focusout)="onTooltipButtonFocusOut()"
556
+ (keydown)="onButtonKeydown($event); onTooltipKeydown($event)"
557
+ >
558
+ @if (iconTemplate(); as tpl) {
559
+ <ng-container
560
+ [ngTemplateOutlet]="tpl"
561
+ [ngTemplateOutletContext]="childContext()"
562
+ />
563
+ } @else {
564
+ <svg
565
+ class="time-zone-picker-icon"
566
+ viewBox="0 0 16 16"
567
+ width="1.05rem"
568
+ height="1.05rem"
569
+ aria-hidden="true"
570
+ fill="none"
571
+ stroke="currentColor"
572
+ stroke-width="1.6"
573
+ stroke-linecap="round"
574
+ stroke-linejoin="round"
575
+ >
576
+ <path d="M8 2.5a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11zM8 5v3l2 1.5" />
577
+ </svg>
578
+ }
579
+ </lily-icon-button>
580
+
581
+ <!-- Purely visual: the same text is already the button's aria-label,
582
+ so it is not wired with aria-describedby (that would announce
583
+ the name twice). -->
584
+ <div
585
+ class="time-zone-picker-tooltip"
586
+ role="tooltip"
587
+ [id]="tooltipId"
588
+ [attr.hidden]="tooltipVisible() ? null : ''"
589
+ (mouseenter)="hoverTooltip.set(true)"
590
+ (mouseleave)="hoverTooltip.set(false)"
591
+ >{{ label() }}</div>
592
+
593
+ <lily-listbox
594
+ #listEl
595
+ [label]="label()"
596
+ baseClass="time-zone-picker-list"
597
+ [elementId]="listId"
598
+ navigation="active-descendant"
599
+ [clamp]="true"
600
+ [typeahead]="true"
601
+ [pageSize]="10"
602
+ [(activeIndex)]="activeIndex"
603
+ [hidden]="!open()"
604
+ (activate)="choose($event)"
605
+ (escape)="closeList()"
606
+ (tabOut)="onListTabOut()"
607
+ >
608
+ @for (zone of infos(); track zone.id; let i = $index) {
609
+ <li
610
+ class="time-zone-picker-option"
611
+ [id]="optionId(i)"
612
+ role="option"
613
+ [attr.aria-selected]="zone.id === value()"
614
+ [attr.data-active]="i === activeIndex() ? '' : null"
615
+ [attr.data-offset]="zone.offset"
616
+ (click)="choose(i)"
617
+ >
618
+ {{ zone.label }}
619
+ </li>
620
+ }
621
+ </lily-listbox>
622
+ </div>
623
+ `,
624
+ isInline: true,
625
+ dependencies: [
626
+ {
627
+ kind: "directive",
628
+ type: NgTemplateOutlet,
629
+ selector: "[ngTemplateOutlet]",
630
+ inputs: [
631
+ "ngTemplateOutletContext",
632
+ "ngTemplateOutlet",
633
+ "ngTemplateOutletInjector"
634
+ ]
635
+ },
636
+ {
637
+ kind: "component",
638
+ type: IconButton,
639
+ selector: "lily-icon-button",
640
+ inputs: [
641
+ "label",
642
+ "className",
643
+ "baseClass",
644
+ "ariaHaspopup",
645
+ "ariaExpanded",
646
+ "ariaControls",
647
+ "tabIndex",
648
+ "disabled"
649
+ ]
650
+ },
651
+ {
652
+ kind: "component",
653
+ type: Listbox,
654
+ selector: "lily-listbox",
655
+ inputs: [
656
+ "label",
657
+ "className",
658
+ "baseClass",
659
+ "elementId",
660
+ "hidden",
661
+ "navigation",
662
+ "clamp",
663
+ "typeahead",
664
+ "pageSize",
665
+ "activeIndex"
666
+ ],
667
+ outputs: [
668
+ "activeIndexChange",
669
+ "activate",
670
+ "escape",
671
+ "tabOut"
672
+ ]
673
+ }
674
+ ],
675
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
676
+ });
677
+ }
678
+ };
679
+ i0.ɵɵngDeclareClassMetadata({
680
+ minVersion: "12.0.0",
681
+ version: "22.2.4",
682
+ ngImport: i0,
683
+ type: TimeZonePicker,
684
+ decorators: [{
685
+ type: Component,
686
+ args: [{
687
+ selector: "lily-time-zone-picker",
688
+ standalone: true,
689
+ imports: [
690
+ NgTemplateOutlet,
691
+ IconButton,
692
+ Listbox
693
+ ],
694
+ changeDetection: ChangeDetectionStrategy.OnPush,
695
+ host: { "(document:click)": "onDocumentClick($event)" },
696
+ template: `
697
+ <div
698
+ #rootEl
699
+ class="time-zone-picker {{ className() }}"
700
+ (focusout)="onRootFocusOut($event)"
701
+ >
702
+ <input type="hidden" [name]="name()" [value]="value()" />
703
+
704
+ <lily-icon-button
705
+ [disabled]="disabled()"
706
+ #buttonEl
707
+ [label]="label()"
708
+ baseClass="time-zone-picker-button"
709
+ ariaHaspopup="listbox"
710
+ [ariaExpanded]="open()"
711
+ [ariaControls]="listId"
712
+ (click)="toggle(); hoverButton.set(false)"
713
+ (mouseenter)="onTooltipButtonEnter()"
714
+ (mouseleave)="hoverButton.set(false)"
715
+ (focusin)="onTooltipButtonFocusIn()"
716
+ (focusout)="onTooltipButtonFocusOut()"
717
+ (keydown)="onButtonKeydown($event); onTooltipKeydown($event)"
718
+ >
719
+ @if (iconTemplate(); as tpl) {
720
+ <ng-container
721
+ [ngTemplateOutlet]="tpl"
722
+ [ngTemplateOutletContext]="childContext()"
723
+ />
724
+ } @else {
725
+ <svg
726
+ class="time-zone-picker-icon"
727
+ viewBox="0 0 16 16"
728
+ width="1.05rem"
729
+ height="1.05rem"
730
+ aria-hidden="true"
731
+ fill="none"
732
+ stroke="currentColor"
733
+ stroke-width="1.6"
734
+ stroke-linecap="round"
735
+ stroke-linejoin="round"
736
+ >
737
+ <path d="M8 2.5a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11zM8 5v3l2 1.5" />
738
+ </svg>
739
+ }
740
+ </lily-icon-button>
741
+
742
+ <!-- Purely visual: the same text is already the button's aria-label,
743
+ so it is not wired with aria-describedby (that would announce
744
+ the name twice). -->
745
+ <div
746
+ class="time-zone-picker-tooltip"
747
+ role="tooltip"
748
+ [id]="tooltipId"
749
+ [attr.hidden]="tooltipVisible() ? null : ''"
750
+ (mouseenter)="hoverTooltip.set(true)"
751
+ (mouseleave)="hoverTooltip.set(false)"
752
+ >{{ label() }}</div>
753
+
754
+ <lily-listbox
755
+ #listEl
756
+ [label]="label()"
757
+ baseClass="time-zone-picker-list"
758
+ [elementId]="listId"
759
+ navigation="active-descendant"
760
+ [clamp]="true"
761
+ [typeahead]="true"
762
+ [pageSize]="10"
763
+ [(activeIndex)]="activeIndex"
764
+ [hidden]="!open()"
765
+ (activate)="choose($event)"
766
+ (escape)="closeList()"
767
+ (tabOut)="onListTabOut()"
768
+ >
769
+ @for (zone of infos(); track zone.id; let i = $index) {
770
+ <li
771
+ class="time-zone-picker-option"
772
+ [id]="optionId(i)"
773
+ role="option"
774
+ [attr.aria-selected]="zone.id === value()"
775
+ [attr.data-active]="i === activeIndex() ? '' : null"
776
+ [attr.data-offset]="zone.offset"
777
+ (click)="choose(i)"
778
+ >
779
+ {{ zone.label }}
780
+ </li>
781
+ }
782
+ </lily-listbox>
783
+ </div>
784
+ `
785
+ }]
786
+ }],
787
+ ctorParameters: () => [],
788
+ propDecorators: {
789
+ label: [{
790
+ type: i0.Input,
791
+ args: [{
792
+ isSignal: true,
793
+ alias: "label",
794
+ required: true
795
+ }]
796
+ }],
797
+ locale: [{
798
+ type: i0.Input,
799
+ args: [{
800
+ isSignal: true,
801
+ alias: "locale",
802
+ required: true
803
+ }]
804
+ }],
805
+ zones: [{
806
+ type: i0.Input,
807
+ args: [{
808
+ isSignal: true,
809
+ alias: "zones",
810
+ required: false
811
+ }]
812
+ }],
813
+ date: [{
814
+ type: i0.Input,
815
+ args: [{
816
+ isSignal: true,
817
+ alias: "date",
818
+ required: false
819
+ }]
820
+ }],
821
+ labelFor: [{
822
+ type: i0.Input,
823
+ args: [{
824
+ isSignal: true,
825
+ alias: "labelFor",
826
+ required: false
827
+ }]
828
+ }],
829
+ names: [{
830
+ type: i0.Input,
831
+ args: [{
832
+ isSignal: true,
833
+ alias: "names",
834
+ required: false
835
+ }]
836
+ }],
837
+ value: [{
838
+ type: i0.Input,
839
+ args: [{
840
+ isSignal: true,
841
+ alias: "value",
842
+ required: false
843
+ }]
844
+ }, {
845
+ type: i0.Output,
846
+ args: ["valueChange"]
847
+ }],
848
+ defaultValue: [{
849
+ type: i0.Input,
850
+ args: [{
851
+ isSignal: true,
852
+ alias: "defaultValue",
853
+ required: false
854
+ }]
855
+ }],
856
+ storageKey: [{
857
+ type: i0.Input,
858
+ args: [{
859
+ isSignal: true,
860
+ alias: "storageKey",
861
+ required: false
862
+ }]
863
+ }],
864
+ name: [{
865
+ type: i0.Input,
866
+ args: [{
867
+ isSignal: true,
868
+ alias: "name",
869
+ required: false
870
+ }]
871
+ }],
872
+ target: [{
873
+ type: i0.Input,
874
+ args: [{
875
+ isSignal: true,
876
+ alias: "target",
877
+ required: false
878
+ }]
879
+ }],
880
+ className: [{
881
+ type: i0.Input,
882
+ args: [{
883
+ isSignal: true,
884
+ alias: "className",
885
+ required: false
886
+ }]
887
+ }],
888
+ zoneChange: [{
889
+ type: i0.Output,
890
+ args: ["zoneChange"]
891
+ }],
892
+ iconTemplate: [{
893
+ type: i0.ContentChild,
894
+ args: [i0.forwardRef(() => TemplateRef), { isSignal: true }]
895
+ }],
896
+ rootRef: [{
897
+ type: i0.ViewChild,
898
+ args: ["rootEl", { isSignal: true }]
899
+ }],
900
+ buttonRef: [{
901
+ type: i0.ViewChild,
902
+ args: ["buttonEl", { isSignal: true }]
903
+ }],
904
+ listRef: [{
905
+ type: i0.ViewChild,
906
+ args: ["listEl", { isSignal: true }]
907
+ }],
908
+ open: [{
909
+ type: i0.Input,
910
+ args: [{
911
+ isSignal: true,
912
+ alias: "open",
913
+ required: false
914
+ }]
915
+ }, {
916
+ type: i0.Output,
917
+ args: ["openChange"]
918
+ }],
919
+ disabled: [{
920
+ type: i0.Input,
921
+ args: [{
922
+ isSignal: true,
923
+ alias: "disabled",
924
+ required: false
925
+ }]
926
+ }]
927
+ }
928
+ });
929
+ export { DEFAULT_ZONES, TimeZonePicker, TimeZonePickerIcon, defaultLabel, formatOffset, matchZone, nextTimeZonePickerId, offsetMinutes, orderedZones, systemZone, zoneInfo, zoneName };
930
+
931
+ //# sourceMappingURL=lilydesignsystem-angular-time-zone-picker.mjs.map