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