@recursyve/ngx-material-components 22.1.0 → 22.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.
@@ -0,0 +1,550 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, input, viewChild, signal, effect, untracked, ViewEncapsulation, Component, output, computed, HostListener, ChangeDetectionStrategy, inject, DestroyRef } from '@angular/core';
3
+ import { validate, FormField } from '@angular/forms/signals';
4
+ import { MatFormFieldControl } from '@angular/material/form-field';
5
+ import { Subject } from 'rxjs';
6
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
+ import { MatMiniFabButton, MatButton, MatIconButton } from '@angular/material/button';
8
+ import * as i1 from '@angular/material/dialog';
9
+ import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialog } from '@angular/material/dialog';
10
+ import { MatIcon } from '@angular/material/icon';
11
+ import { MatToolbar } from '@angular/material/toolbar';
12
+ import { NiceTranslatePipe } from '@recursyve/ngx-material-components/common';
13
+
14
+ const NICE_TIMEPICKER_CONFIG = new InjectionToken("nice_timepicker_config");
15
+
16
+ const DEFAULT_MINUTES_GAP = 1;
17
+ const DEFAULT_MINUTE_LABEL_GAP = 5;
18
+ const TWELVE_HOUR_PATTERN = /^(\d{1,2}):(\d{2})\s*(AM|PM)$/i;
19
+ const TWENTY_FOUR_HOUR_PATTERN = /^(\d{1,2}):(\d{2})$/;
20
+ function parseTime(value) {
21
+ const normalized = value?.trim();
22
+ if (!normalized) {
23
+ return null;
24
+ }
25
+ const twelveHourMatch = normalized.match(TWELVE_HOUR_PATTERN);
26
+ if (twelveHourMatch) {
27
+ const hour = Number(twelveHourMatch[1]);
28
+ const minute = Number(twelveHourMatch[2]);
29
+ const period = twelveHourMatch[3].toUpperCase();
30
+ if (!isValidTwelveHourTime(hour, minute)) {
31
+ return null;
32
+ }
33
+ return { hour, minute, period };
34
+ }
35
+ const twentyFourHourMatch = normalized.match(TWENTY_FOUR_HOUR_PATTERN);
36
+ if (twentyFourHourMatch) {
37
+ const hour24 = Number(twentyFourHourMatch[1]);
38
+ const minute = Number(twentyFourHourMatch[2]);
39
+ if (hour24 > 23 || minute > 59) {
40
+ return null;
41
+ }
42
+ const period = hour24 >= 12 ? "PM" : "AM";
43
+ const hour = toTwelveHour(hour24);
44
+ return { hour, minute, period };
45
+ }
46
+ return null;
47
+ }
48
+ function formatTime(time) {
49
+ return `${time.hour}:${pad(time.minute)} ${time.period}`;
50
+ }
51
+ function getDefaultTime() {
52
+ const now = new Date();
53
+ const hour24 = now.getHours();
54
+ return {
55
+ hour: toTwelveHour(hour24),
56
+ minute: now.getMinutes(),
57
+ period: hour24 >= 12 ? "PM" : "AM"
58
+ };
59
+ }
60
+ function resolveTime(value) {
61
+ return parseTime(value) ?? getDefaultTime();
62
+ }
63
+ function getHourFaceOptions() {
64
+ return Array.from({ length: 12 }, (_, index) => {
65
+ const value = index + 1;
66
+ const angle = 30 * value;
67
+ return {
68
+ value,
69
+ angle
70
+ };
71
+ });
72
+ }
73
+ function getMinuteFaceOptions(minutesGap = DEFAULT_MINUTES_GAP) {
74
+ const options = [];
75
+ const angleStep = 360 / 60;
76
+ for (let minute = 0; minute < 60; minute++) {
77
+ if (minute % minutesGap !== 0) {
78
+ continue;
79
+ }
80
+ const angle = angleStep * minute;
81
+ options.push({
82
+ value: minute,
83
+ angle: angle === 0 ? 360 : angle
84
+ });
85
+ }
86
+ return options;
87
+ }
88
+ function isMinuteLabelVisible(minute, minutesGap = DEFAULT_MINUTES_GAP) {
89
+ return minute % minutesGap === 0;
90
+ }
91
+ function getMinuteLabelGap(minutesGap, dottedMinutesInGap) {
92
+ if (minutesGap > 1) {
93
+ return minutesGap;
94
+ }
95
+ return dottedMinutesInGap ? DEFAULT_MINUTE_LABEL_GAP : 1;
96
+ }
97
+ function isValidTwelveHourTime(hour, minute) {
98
+ return hour >= 1 && hour <= 12 && minute >= 0 && minute <= 59;
99
+ }
100
+ function toTwelveHour(hour24) {
101
+ const hour = hour24 % 12;
102
+ return hour === 0 ? 12 : hour;
103
+ }
104
+ function pad(value) {
105
+ return value.toString().padStart(2, "0");
106
+ }
107
+
108
+ const defaultTranslationKeys = {
109
+ cancel: "components.timepicker.cancel",
110
+ confirm: "components.timepicker.confirm",
111
+ title: "components.timepicker.title"
112
+ };
113
+ const defaultConfig = {
114
+ dottedMinutesInGap: true,
115
+ minutesGap: DEFAULT_MINUTES_GAP,
116
+ invalidFormatKey: "errors.timepicker.invalidFormat",
117
+ translationKeys: defaultTranslationKeys
118
+ };
119
+ function provideTimepicker(options) {
120
+ return [
121
+ {
122
+ provide: NICE_TIMEPICKER_CONFIG,
123
+ useValue: {
124
+ ...defaultConfig,
125
+ ...options?.config,
126
+ translationKeys: {
127
+ ...defaultTranslationKeys,
128
+ ...options?.config?.translationKeys
129
+ }
130
+ }
131
+ }
132
+ ];
133
+ }
134
+
135
+ const registeredFields = new WeakSet();
136
+ const NICE_TIME_PARSE_ERROR_KIND = "parse";
137
+ function isValidNiceTime(value) {
138
+ const normalized = value?.trim();
139
+ if (!normalized) {
140
+ return true;
141
+ }
142
+ return parseTime(normalized) !== null;
143
+ }
144
+ function normalizeNiceTime(value) {
145
+ const parsed = parseTime(value);
146
+ return parsed ? formatTime(parsed) : null;
147
+ }
148
+ function niceTimeFormat(path, message) {
149
+ validate(path, ({ value }) => {
150
+ const current = value()?.trim() ?? "";
151
+ if (!current || isValidNiceTime(current)) {
152
+ return undefined;
153
+ }
154
+ return { kind: NICE_TIME_PARSE_ERROR_KIND, message };
155
+ });
156
+ }
157
+ function registerNiceTimeFormatValidation(field, message) {
158
+ if (registeredFields.has(field)) {
159
+ return;
160
+ }
161
+ registeredFields.add(field);
162
+ niceTimeFormat(field, message);
163
+ }
164
+
165
+ function hasNiceTimeFormatError(value) {
166
+ const normalized = value?.trim() ?? "";
167
+ return normalized !== "" && !isValidNiceTime(normalized);
168
+ }
169
+ function niceTimeFieldErrorState(state) {
170
+ if (!state.touched() && !state.dirty()) {
171
+ return false;
172
+ }
173
+ if (hasNiceTimeFormatError(state.value())) {
174
+ return true;
175
+ }
176
+ return state.invalid();
177
+ }
178
+
179
+ class NiceTimepicker {
180
+ static nextId = 0;
181
+ field = input.required(/* @ts-ignore */
182
+ ...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
183
+ id = `nice-timepicker-${NiceTimepicker.nextId++}`;
184
+ controlType = "nice-timepicker";
185
+ stateChanges = new Subject();
186
+ ngControl = null;
187
+ placeholder = "";
188
+ required = false;
189
+ textInput = viewChild("textInput", /* @ts-ignore */
190
+ ...(ngDevMode ? [{ debugName: "textInput" }] : /* istanbul ignore next */ []));
191
+ isInputFocused = signal(false, /* @ts-ignore */
192
+ ...(ngDevMode ? [{ debugName: "isInputFocused" }] : /* istanbul ignore next */ []));
193
+ get value() {
194
+ return this.field()().value() || null;
195
+ }
196
+ get focused() {
197
+ return this.isInputFocused();
198
+ }
199
+ get empty() {
200
+ return !this.field()().value()?.trim();
201
+ }
202
+ get shouldLabelFloat() {
203
+ return this.focused || !this.empty;
204
+ }
205
+ get disabled() {
206
+ return this.field()().disabled();
207
+ }
208
+ get errorState() {
209
+ return niceTimeFieldErrorState(this.field()());
210
+ }
211
+ constructor() {
212
+ effect(() => {
213
+ const state = this.field()();
214
+ state.value();
215
+ state.disabled();
216
+ state.invalid();
217
+ state.touched();
218
+ state.dirty();
219
+ this.isInputFocused();
220
+ untracked(() => this.stateChanges.next());
221
+ });
222
+ }
223
+ setDescribedByIds(ids) {
224
+ const input = this.textInput()?.nativeElement;
225
+ if (!input) {
226
+ return;
227
+ }
228
+ if (ids.length) {
229
+ input.setAttribute("aria-describedby", ids.join(" "));
230
+ }
231
+ else {
232
+ input.removeAttribute("aria-describedby");
233
+ }
234
+ }
235
+ onContainerClick() {
236
+ if (this.disabled) {
237
+ return;
238
+ }
239
+ this.textInput()?.nativeElement.focus();
240
+ }
241
+ onInputBlur() {
242
+ this.isInputFocused.set(false);
243
+ const state = this.field()();
244
+ const normalized = normalizeNiceTime(state.value() ?? "");
245
+ if (normalized) {
246
+ state.value.set(normalized);
247
+ }
248
+ }
249
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NiceTimepicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
250
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.0", type: NiceTimepicker, isStandalone: true, selector: "nice-timepicker", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.nice-timepicker--focused": "focused", "class.nice-timepicker--empty": "empty", "class.nice-timepicker--invalid": "errorState", "attr.aria-invalid": "errorState", "id": "id" }, classAttribute: "nice-timepicker" }, providers: [{ provide: MatFormFieldControl, useExisting: NiceTimepicker }], viewQueries: [{ propertyName: "textInput", first: true, predicate: ["textInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<input\n #textInput\n class=\"nice-timepicker__text-input\"\n [formField]=\"field()\"\n (blur)=\"onInputBlur()\"\n (focus)=\"isInputFocused.set(true)\"\n/>\n", styles: ["nice-timepicker{display:block;width:100%;min-width:0}.mat-mdc-form-field:has(nice-timepicker){width:var(--nice-timepicker-width, 100%)}.mat-mdc-form-field:has(nice-timepicker) .nice-timepicker__text-input{display:block;width:100%;min-width:0;padding:0;border:none;outline:none;background:transparent;color:inherit;font:inherit}.mat-mdc-form-field:has(nice-timepicker-toggle){--nice-field-suffix-icon-size: var(--nice-timepicker-icon-size, 1.5rem)}.mat-mdc-form-field:has(nice-timepicker-toggle) .nice-timepicker-toggle{color:var(--nice-timepicker-icon-color, inherit)}.mat-mdc-form-field:has(nice-timepicker-toggle) .nice-timepicker-toggle[disabled]{color:var(--nice-timepicker-disabled-icon-color, inherit)}.mat-mdc-form-field:has(nice-timepicker-toggle).mat-form-field-invalid .nice-timepicker-toggle{color:var(--nice-timepicker-invalid-icon-color, inherit)}body div.nice-timepicker-dialog{--nice-timepicker-dialog-actions-gap: 8px;--nice-timepicker-dialog-actions-padding-block-end: 20px;--nice-timepicker-dialog-actions-padding-inline: 24px;--nice-timepicker-dialog-border-radius: 8px;--nice-timepicker-dialog-content-gap: 36px;--nice-timepicker-dialog-control-height: 80px;--nice-timepicker-dialog-control-radius: 8px;--nice-timepicker-dialog-control-surface: color-mix(in srgb, var(--mat-sys-primary, #6750a4) 5%, transparent);--nice-timepicker-dialog-control-width: 96px;--nice-timepicker-dialog-colon-width: 24px;--nice-timepicker-dialog-dial-font-size: 48px;--nice-timepicker-dialog-dial-line-height: 60px;--nice-timepicker-dialog-dial-padding-block-end: 0;--nice-timepicker-dialog-face-size: 290px;--nice-timepicker-dialog-face-button-size: 48px;--nice-timepicker-dialog-face-edge-offset: 26px;--nice-timepicker-dialog-face-arm-length: calc( var(--nice-timepicker-dialog-face-size) / 2 - var(--nice-timepicker-dialog-face-edge-offset) );--nice-timepicker-dialog-face-hand-length: calc(var(--nice-timepicker-dialog-face-arm-length) - 22px);--nice-timepicker-dialog-face-hand-fab-offset: -22px;--nice-timepicker-dialog-min-width: 328px;--nice-timepicker-dialog-padding-inline: 24px;--nice-timepicker-dialog-period-btn-height: 40px;--nice-timepicker-dialog-period-btn-width: 52px;--nice-timepicker-dialog-period-font-size: 18px;--nice-timepicker-dialog-period-line-height: 22px;--nice-timepicker-dialog-period-inactive-surface: color-mix( in srgb, var(--mat-sys-tertiary, #7d5260) 5%, transparent );--nice-timepicker-dialog-title-gap: 12px}body div.nice-timepicker-dialog mat-dialog-container,body div.nice-timepicker-dialog .mat-mdc-dialog-surface{min-width:var(--nice-timepicker-dialog-min-width)}body div.nice-timepicker-dialog mat-dialog-container{padding-top:0;border-radius:var(--nice-timepicker-dialog-border-radius)}body div.nice-timepicker-dialog [mat-dialog-content]{padding:0;max-height:none!important;overflow:visible}body div.nice-timepicker-dialog .timepicker{display:flex;flex-direction:column}body div.nice-timepicker-dialog mat-toolbar.timepicker-header{display:block;width:100%;min-height:unset;height:auto!important;padding:0;box-shadow:none;--mat-toolbar-container-background-color: transparent;background:transparent;color:inherit}body div.nice-timepicker-dialog mat-toolbar.timepicker-header .timepicker-dial{width:100%}body div.nice-timepicker-dialog .timepicker-dial{display:flex;flex-direction:column;gap:var(--nice-timepicker-dialog-title-gap);padding-bottom:var(--nice-timepicker-dialog-dial-padding-block-end)}body div.nice-timepicker-dialog .timepicker-dial__container{display:flex;align-items:center;gap:12px;padding-inline:var(--nice-timepicker-dialog-padding-inline)}body div.nice-timepicker-dialog .nice-timepicker-dialog-title{box-sizing:border-box;width:100%;margin:0;padding:var(--nice-timepicker-dialog-padding-inline) var(--nice-timepicker-dialog-padding-inline) 0;text-align:start;font-size:18px;line-height:22px;font-weight:400}body div.nice-timepicker-dialog .timepicker-dial__time{display:flex;align-items:center;gap:0}body div.nice-timepicker-dialog .timepicker-dial__time>span{display:flex;flex-shrink:0;align-items:center;justify-content:center;width:var(--nice-timepicker-dialog-colon-width);font-size:var(--nice-timepicker-dialog-dial-font-size);line-height:var(--nice-timepicker-dialog-dial-line-height)}body div.nice-timepicker-dialog .timepicker-dial__control{box-sizing:border-box;width:var(--nice-timepicker-dialog-control-width);height:var(--nice-timepicker-dialog-control-height);border:0;background-color:var(--nice-timepicker-dialog-control-surface);border-radius:var(--nice-timepicker-dialog-control-radius);color:inherit;font-size:var(--nice-timepicker-dialog-dial-font-size);line-height:var(--nice-timepicker-dialog-dial-line-height);cursor:pointer}body div.nice-timepicker-dialog .timepicker-dial__control:focus{background-color:var(--nice-timepicker-dialog-control-surface);outline:none}body div.nice-timepicker-dialog .timepicker-dial__control.active{color:var(--mat-sys-primary, #6750a4)}body div.nice-timepicker-dialog .timepicker-dial__period{display:flex;flex-direction:column;gap:0;width:var(--nice-timepicker-dialog-period-btn-width);height:var(--nice-timepicker-dialog-control-height);border-radius:var(--nice-timepicker-dialog-control-radius);overflow:hidden;background-color:var(--nice-timepicker-dialog-period-inactive-surface)}body div.nice-timepicker-dialog .timepicker-period__btn{box-sizing:border-box;flex:1;width:100%;min-height:0;height:auto;padding:0;border:0;border-radius:0;opacity:1;font-size:var(--nice-timepicker-dialog-period-font-size);font-weight:400;line-height:var(--nice-timepicker-dialog-period-line-height);cursor:pointer}body div.nice-timepicker-dialog .timepicker-period__btn.active{color:var(--mat-sys-primary, #6750a4);background-color:var(--nice-timepicker-dialog-control-surface)}body div.nice-timepicker-dialog .timepicker-period__btn:not(.active){color:inherit;background-color:var(--nice-timepicker-dialog-period-inactive-surface)}body div.nice-timepicker-dialog .timepicker__main-content{padding-inline:var(--nice-timepicker-dialog-padding-inline);margin-top:var(--nice-timepicker-dialog-content-gap)}body div.nice-timepicker-dialog .timepicker__body{display:flex;justify-content:center;padding-inline:0;padding-block:16px}body div.nice-timepicker-dialog nice-timepicker-face [mat-mini-fab]{--mat-fab-small-container-color: transparent;--mat-fab-small-container-shape: 50%;--mat-fab-small-disabled-state-container-color: transparent;--mat-fab-small-container-elevation-shadow: none;--mat-fab-small-focus-container-elevation-shadow: none;--mat-fab-small-hover-container-elevation-shadow: none;--mat-fab-small-pressed-container-elevation-shadow: none;--mat-fab-hover-state-layer-opacity: 0;--mat-fab-focus-state-layer-opacity: 0;--mat-fab-pressed-state-layer-opacity: 0;border-radius:50%;box-shadow:none!important}body div.nice-timepicker-dialog nice-timepicker-face [mat-mini-fab].mat-mdc-mini-fab{border-radius:50%}body div.nice-timepicker-dialog nice-timepicker-face [mat-mini-fab] .mat-mdc-button-persistent-ripple{display:none}body div.nice-timepicker-dialog nice-timepicker-face [mat-mini-fab].mat-mdc-mini-fab.mat-unthemed{--mat-fab-small-foreground-color: inherit;color:inherit}body div.nice-timepicker-dialog nice-timepicker-face [mat-mini-fab].mat-mdc-mini-fab.mat-primary{--mat-fab-small-container-color: var(--mat-sys-primary, #6750a4);--mat-fab-small-foreground-color: var(--mat-sys-on-primary, #fff);box-shadow:none!important}body div.nice-timepicker-dialog nice-timepicker-face .clock-face{position:relative;display:flex;justify-content:center;box-sizing:border-box;width:var(--nice-timepicker-dialog-face-size, 256px);height:var(--nice-timepicker-dialog-face-size, 256px);border-radius:50%;background-color:var(--nice-timepicker-dialog-control-surface)}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__container{display:contents}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__number{position:absolute;z-index:3;top:calc(50% - var(--nice-timepicker-dialog-face-arm-length));left:calc(50% - var(--nice-timepicker-dialog-face-button-size) / 2);width:var(--nice-timepicker-dialog-face-button-size);text-align:center;transform-origin:calc(var(--nice-timepicker-dialog-face-button-size) / 2) 100%;transition:none}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__number--outer{height:var(--nice-timepicker-dialog-face-arm-length)}body div.nice-timepicker-dialog nice-timepicker-face .nice-timepicker-face__button{width:var(--nice-timepicker-dialog-face-button-size);height:var(--nice-timepicker-dialog-face-button-size);font-size:18px;line-height:22px;transition:none;pointer-events:none;cursor:pointer}body div.nice-timepicker-dialog nice-timepicker-face .nice-timepicker-face__button--under-fab{color:var(--mat-sys-on-primary, #fff)!important}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__clock-hand{position:absolute;top:calc(50% - var(--nice-timepicker-dialog-face-hand-length));left:calc(50% - 1px);z-index:1;width:2px;min-height:0;height:var(--nice-timepicker-dialog-face-hand-length);padding:0;transform-origin:1px 100%;transition:none;--mat-toolbar-container-background-color: var(--mat-sys-primary, #6750a4);color:var(--mat-sys-on-primary, #fff)}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__clock-hand.mat-toolbar,body div.nice-timepicker-dialog nice-timepicker-face .clock-face__clock-hand.mat-mdc-toolbar{min-height:0}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__center{position:absolute;top:50%;left:50%;z-index:1;width:8px;min-height:0;height:8px;margin:-4px;padding:0;border-radius:50%;--mat-toolbar-container-background-color: var(--mat-sys-primary, #6750a4)}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__center.mat-toolbar,body div.nice-timepicker-dialog nice-timepicker-face .clock-face__center.mat-mdc-toolbar{min-height:0}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__hand-fab-arm{position:absolute;top:calc(50% - var(--nice-timepicker-dialog-face-hand-length));left:calc(50% - 1px);z-index:2;width:2px;height:var(--nice-timepicker-dialog-face-hand-length);transform-origin:1px 100%;pointer-events:none}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__clock-hand_minute-fab{position:absolute;top:var(--nice-timepicker-dialog-face-hand-fab-offset);left:calc(50% - var(--nice-timepicker-dialog-face-button-size) / 2);box-sizing:border-box;display:flex;align-items:center;justify-content:center;width:var(--nice-timepicker-dialog-face-button-size);height:var(--nice-timepicker-dialog-face-button-size);border-radius:50%;box-shadow:none!important}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__clock-hand_minute-fab .clock-face__clock-hand_minute_dot{display:block;width:4px;height:4px;border-radius:50%;background:var(--mat-sys-on-primary, #fff)}body div.nice-timepicker-dialog [mat-dialog-actions]{display:flex;padding:0 var(--nice-timepicker-dialog-actions-padding-inline) var(--nice-timepicker-dialog-actions-padding-block-end);gap:var(--nice-timepicker-dialog-actions-gap);justify-content:flex-end}body div.nice-timepicker-dialog [mat-dialog-actions] button{font-size:18px;line-height:22px}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }], encapsulation: i0.ViewEncapsulation.None });
251
+ }
252
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NiceTimepicker, decorators: [{
253
+ type: Component,
254
+ args: [{ selector: "nice-timepicker", imports: [FormField], encapsulation: ViewEncapsulation.None, providers: [{ provide: MatFormFieldControl, useExisting: NiceTimepicker }], host: {
255
+ class: "nice-timepicker",
256
+ "[class.nice-timepicker--focused]": "focused",
257
+ "[class.nice-timepicker--empty]": "empty",
258
+ "[class.nice-timepicker--invalid]": "errorState",
259
+ "[attr.aria-invalid]": "errorState",
260
+ "[id]": "id"
261
+ }, template: "<input\n #textInput\n class=\"nice-timepicker__text-input\"\n [formField]=\"field()\"\n (blur)=\"onInputBlur()\"\n (focus)=\"isInputFocused.set(true)\"\n/>\n", styles: ["nice-timepicker{display:block;width:100%;min-width:0}.mat-mdc-form-field:has(nice-timepicker){width:var(--nice-timepicker-width, 100%)}.mat-mdc-form-field:has(nice-timepicker) .nice-timepicker__text-input{display:block;width:100%;min-width:0;padding:0;border:none;outline:none;background:transparent;color:inherit;font:inherit}.mat-mdc-form-field:has(nice-timepicker-toggle){--nice-field-suffix-icon-size: var(--nice-timepicker-icon-size, 1.5rem)}.mat-mdc-form-field:has(nice-timepicker-toggle) .nice-timepicker-toggle{color:var(--nice-timepicker-icon-color, inherit)}.mat-mdc-form-field:has(nice-timepicker-toggle) .nice-timepicker-toggle[disabled]{color:var(--nice-timepicker-disabled-icon-color, inherit)}.mat-mdc-form-field:has(nice-timepicker-toggle).mat-form-field-invalid .nice-timepicker-toggle{color:var(--nice-timepicker-invalid-icon-color, inherit)}body div.nice-timepicker-dialog{--nice-timepicker-dialog-actions-gap: 8px;--nice-timepicker-dialog-actions-padding-block-end: 20px;--nice-timepicker-dialog-actions-padding-inline: 24px;--nice-timepicker-dialog-border-radius: 8px;--nice-timepicker-dialog-content-gap: 36px;--nice-timepicker-dialog-control-height: 80px;--nice-timepicker-dialog-control-radius: 8px;--nice-timepicker-dialog-control-surface: color-mix(in srgb, var(--mat-sys-primary, #6750a4) 5%, transparent);--nice-timepicker-dialog-control-width: 96px;--nice-timepicker-dialog-colon-width: 24px;--nice-timepicker-dialog-dial-font-size: 48px;--nice-timepicker-dialog-dial-line-height: 60px;--nice-timepicker-dialog-dial-padding-block-end: 0;--nice-timepicker-dialog-face-size: 290px;--nice-timepicker-dialog-face-button-size: 48px;--nice-timepicker-dialog-face-edge-offset: 26px;--nice-timepicker-dialog-face-arm-length: calc( var(--nice-timepicker-dialog-face-size) / 2 - var(--nice-timepicker-dialog-face-edge-offset) );--nice-timepicker-dialog-face-hand-length: calc(var(--nice-timepicker-dialog-face-arm-length) - 22px);--nice-timepicker-dialog-face-hand-fab-offset: -22px;--nice-timepicker-dialog-min-width: 328px;--nice-timepicker-dialog-padding-inline: 24px;--nice-timepicker-dialog-period-btn-height: 40px;--nice-timepicker-dialog-period-btn-width: 52px;--nice-timepicker-dialog-period-font-size: 18px;--nice-timepicker-dialog-period-line-height: 22px;--nice-timepicker-dialog-period-inactive-surface: color-mix( in srgb, var(--mat-sys-tertiary, #7d5260) 5%, transparent );--nice-timepicker-dialog-title-gap: 12px}body div.nice-timepicker-dialog mat-dialog-container,body div.nice-timepicker-dialog .mat-mdc-dialog-surface{min-width:var(--nice-timepicker-dialog-min-width)}body div.nice-timepicker-dialog mat-dialog-container{padding-top:0;border-radius:var(--nice-timepicker-dialog-border-radius)}body div.nice-timepicker-dialog [mat-dialog-content]{padding:0;max-height:none!important;overflow:visible}body div.nice-timepicker-dialog .timepicker{display:flex;flex-direction:column}body div.nice-timepicker-dialog mat-toolbar.timepicker-header{display:block;width:100%;min-height:unset;height:auto!important;padding:0;box-shadow:none;--mat-toolbar-container-background-color: transparent;background:transparent;color:inherit}body div.nice-timepicker-dialog mat-toolbar.timepicker-header .timepicker-dial{width:100%}body div.nice-timepicker-dialog .timepicker-dial{display:flex;flex-direction:column;gap:var(--nice-timepicker-dialog-title-gap);padding-bottom:var(--nice-timepicker-dialog-dial-padding-block-end)}body div.nice-timepicker-dialog .timepicker-dial__container{display:flex;align-items:center;gap:12px;padding-inline:var(--nice-timepicker-dialog-padding-inline)}body div.nice-timepicker-dialog .nice-timepicker-dialog-title{box-sizing:border-box;width:100%;margin:0;padding:var(--nice-timepicker-dialog-padding-inline) var(--nice-timepicker-dialog-padding-inline) 0;text-align:start;font-size:18px;line-height:22px;font-weight:400}body div.nice-timepicker-dialog .timepicker-dial__time{display:flex;align-items:center;gap:0}body div.nice-timepicker-dialog .timepicker-dial__time>span{display:flex;flex-shrink:0;align-items:center;justify-content:center;width:var(--nice-timepicker-dialog-colon-width);font-size:var(--nice-timepicker-dialog-dial-font-size);line-height:var(--nice-timepicker-dialog-dial-line-height)}body div.nice-timepicker-dialog .timepicker-dial__control{box-sizing:border-box;width:var(--nice-timepicker-dialog-control-width);height:var(--nice-timepicker-dialog-control-height);border:0;background-color:var(--nice-timepicker-dialog-control-surface);border-radius:var(--nice-timepicker-dialog-control-radius);color:inherit;font-size:var(--nice-timepicker-dialog-dial-font-size);line-height:var(--nice-timepicker-dialog-dial-line-height);cursor:pointer}body div.nice-timepicker-dialog .timepicker-dial__control:focus{background-color:var(--nice-timepicker-dialog-control-surface);outline:none}body div.nice-timepicker-dialog .timepicker-dial__control.active{color:var(--mat-sys-primary, #6750a4)}body div.nice-timepicker-dialog .timepicker-dial__period{display:flex;flex-direction:column;gap:0;width:var(--nice-timepicker-dialog-period-btn-width);height:var(--nice-timepicker-dialog-control-height);border-radius:var(--nice-timepicker-dialog-control-radius);overflow:hidden;background-color:var(--nice-timepicker-dialog-period-inactive-surface)}body div.nice-timepicker-dialog .timepicker-period__btn{box-sizing:border-box;flex:1;width:100%;min-height:0;height:auto;padding:0;border:0;border-radius:0;opacity:1;font-size:var(--nice-timepicker-dialog-period-font-size);font-weight:400;line-height:var(--nice-timepicker-dialog-period-line-height);cursor:pointer}body div.nice-timepicker-dialog .timepicker-period__btn.active{color:var(--mat-sys-primary, #6750a4);background-color:var(--nice-timepicker-dialog-control-surface)}body div.nice-timepicker-dialog .timepicker-period__btn:not(.active){color:inherit;background-color:var(--nice-timepicker-dialog-period-inactive-surface)}body div.nice-timepicker-dialog .timepicker__main-content{padding-inline:var(--nice-timepicker-dialog-padding-inline);margin-top:var(--nice-timepicker-dialog-content-gap)}body div.nice-timepicker-dialog .timepicker__body{display:flex;justify-content:center;padding-inline:0;padding-block:16px}body div.nice-timepicker-dialog nice-timepicker-face [mat-mini-fab]{--mat-fab-small-container-color: transparent;--mat-fab-small-container-shape: 50%;--mat-fab-small-disabled-state-container-color: transparent;--mat-fab-small-container-elevation-shadow: none;--mat-fab-small-focus-container-elevation-shadow: none;--mat-fab-small-hover-container-elevation-shadow: none;--mat-fab-small-pressed-container-elevation-shadow: none;--mat-fab-hover-state-layer-opacity: 0;--mat-fab-focus-state-layer-opacity: 0;--mat-fab-pressed-state-layer-opacity: 0;border-radius:50%;box-shadow:none!important}body div.nice-timepicker-dialog nice-timepicker-face [mat-mini-fab].mat-mdc-mini-fab{border-radius:50%}body div.nice-timepicker-dialog nice-timepicker-face [mat-mini-fab] .mat-mdc-button-persistent-ripple{display:none}body div.nice-timepicker-dialog nice-timepicker-face [mat-mini-fab].mat-mdc-mini-fab.mat-unthemed{--mat-fab-small-foreground-color: inherit;color:inherit}body div.nice-timepicker-dialog nice-timepicker-face [mat-mini-fab].mat-mdc-mini-fab.mat-primary{--mat-fab-small-container-color: var(--mat-sys-primary, #6750a4);--mat-fab-small-foreground-color: var(--mat-sys-on-primary, #fff);box-shadow:none!important}body div.nice-timepicker-dialog nice-timepicker-face .clock-face{position:relative;display:flex;justify-content:center;box-sizing:border-box;width:var(--nice-timepicker-dialog-face-size, 256px);height:var(--nice-timepicker-dialog-face-size, 256px);border-radius:50%;background-color:var(--nice-timepicker-dialog-control-surface)}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__container{display:contents}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__number{position:absolute;z-index:3;top:calc(50% - var(--nice-timepicker-dialog-face-arm-length));left:calc(50% - var(--nice-timepicker-dialog-face-button-size) / 2);width:var(--nice-timepicker-dialog-face-button-size);text-align:center;transform-origin:calc(var(--nice-timepicker-dialog-face-button-size) / 2) 100%;transition:none}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__number--outer{height:var(--nice-timepicker-dialog-face-arm-length)}body div.nice-timepicker-dialog nice-timepicker-face .nice-timepicker-face__button{width:var(--nice-timepicker-dialog-face-button-size);height:var(--nice-timepicker-dialog-face-button-size);font-size:18px;line-height:22px;transition:none;pointer-events:none;cursor:pointer}body div.nice-timepicker-dialog nice-timepicker-face .nice-timepicker-face__button--under-fab{color:var(--mat-sys-on-primary, #fff)!important}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__clock-hand{position:absolute;top:calc(50% - var(--nice-timepicker-dialog-face-hand-length));left:calc(50% - 1px);z-index:1;width:2px;min-height:0;height:var(--nice-timepicker-dialog-face-hand-length);padding:0;transform-origin:1px 100%;transition:none;--mat-toolbar-container-background-color: var(--mat-sys-primary, #6750a4);color:var(--mat-sys-on-primary, #fff)}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__clock-hand.mat-toolbar,body div.nice-timepicker-dialog nice-timepicker-face .clock-face__clock-hand.mat-mdc-toolbar{min-height:0}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__center{position:absolute;top:50%;left:50%;z-index:1;width:8px;min-height:0;height:8px;margin:-4px;padding:0;border-radius:50%;--mat-toolbar-container-background-color: var(--mat-sys-primary, #6750a4)}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__center.mat-toolbar,body div.nice-timepicker-dialog nice-timepicker-face .clock-face__center.mat-mdc-toolbar{min-height:0}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__hand-fab-arm{position:absolute;top:calc(50% - var(--nice-timepicker-dialog-face-hand-length));left:calc(50% - 1px);z-index:2;width:2px;height:var(--nice-timepicker-dialog-face-hand-length);transform-origin:1px 100%;pointer-events:none}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__clock-hand_minute-fab{position:absolute;top:var(--nice-timepicker-dialog-face-hand-fab-offset);left:calc(50% - var(--nice-timepicker-dialog-face-button-size) / 2);box-sizing:border-box;display:flex;align-items:center;justify-content:center;width:var(--nice-timepicker-dialog-face-button-size);height:var(--nice-timepicker-dialog-face-button-size);border-radius:50%;box-shadow:none!important}body div.nice-timepicker-dialog nice-timepicker-face .clock-face__clock-hand_minute-fab .clock-face__clock-hand_minute_dot{display:block;width:4px;height:4px;border-radius:50%;background:var(--mat-sys-on-primary, #fff)}body div.nice-timepicker-dialog [mat-dialog-actions]{display:flex;padding:0 var(--nice-timepicker-dialog-actions-padding-inline) var(--nice-timepicker-dialog-actions-padding-block-end);gap:var(--nice-timepicker-dialog-actions-gap);justify-content:flex-end}body div.nice-timepicker-dialog [mat-dialog-actions] button{font-size:18px;line-height:22px}\n"] }]
262
+ }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], textInput: [{ type: i0.ViewChild, args: ["textInput", { isSignal: true }] }] } });
263
+
264
+ function roundAngle(angle, step) {
265
+ return Math.round(angle / step) * step;
266
+ }
267
+ function countAngleByCords(x0, y0, x, y, currentAngle) {
268
+ if (y > y0 && x >= x0) {
269
+ return 180 - currentAngle;
270
+ }
271
+ if (y > y0 && x < x0) {
272
+ return 180 + currentAngle;
273
+ }
274
+ if (y < y0 && x < x0) {
275
+ return 360 - currentAngle;
276
+ }
277
+ return currentAngle;
278
+ }
279
+ class NiceTimepickerFace {
280
+ dottedMinutesInGap = input(true, /* @ts-ignore */
281
+ ...(ngDevMode ? [{ debugName: "dottedMinutesInGap" }] : /* istanbul ignore next */ []));
282
+ faceTime = input.required(/* @ts-ignore */
283
+ ...(ngDevMode ? [{ debugName: "faceTime" }] : /* istanbul ignore next */ []));
284
+ minutesGap = input(DEFAULT_MINUTES_GAP, /* @ts-ignore */
285
+ ...(ngDevMode ? [{ debugName: "minutesGap" }] : /* istanbul ignore next */ []));
286
+ selectedValue = input.required(/* @ts-ignore */
287
+ ...(ngDevMode ? [{ debugName: "selectedValue" }] : /* istanbul ignore next */ []));
288
+ unit = input.required(/* @ts-ignore */
289
+ ...(ngDevMode ? [{ debugName: "unit" }] : /* istanbul ignore next */ []));
290
+ timeChange = output();
291
+ timeSelected = output();
292
+ clockFace = viewChild.required("clockFace");
293
+ isDragging = false;
294
+ touchEndHandler;
295
+ touchStartHandler;
296
+ selectedTime = computed(() => {
297
+ const match = this.faceTime().find((time) => time.value === this.selectedValue());
298
+ if (match) {
299
+ return match;
300
+ }
301
+ const fallbackAngle = this.unit() === "hour" ? 30 * this.selectedValue() : 6 * this.selectedValue();
302
+ return {
303
+ value: this.selectedValue(),
304
+ angle: fallbackAngle === 0 ? 360 : fallbackAngle
305
+ };
306
+ }, /* @ts-ignore */
307
+ ...(ngDevMode ? [{ debugName: "selectedTime" }] : /* istanbul ignore next */ []));
308
+ showSelectionFab = computed(() => {
309
+ if (this.unit() !== "minute") {
310
+ return false;
311
+ }
312
+ const labelGap = getMinuteLabelGap(this.minutesGap(), this.dottedMinutesInGap());
313
+ return !isMinuteLabelVisible(this.selectedValue(), labelGap);
314
+ }, /* @ts-ignore */
315
+ ...(ngDevMode ? [{ debugName: "showSelectionFab" }] : /* istanbul ignore next */ []));
316
+ static FAB_OVERLAP_THRESHOLD_DEG = 13;
317
+ optionsUnderFab = computed(() => {
318
+ const result = new Set();
319
+ if (!this.showSelectionFab()) {
320
+ return result;
321
+ }
322
+ const fabAngle = this.selectedTime().angle;
323
+ for (const option of this.faceTime()) {
324
+ let diff = Math.abs(fabAngle - option.angle);
325
+ if (diff > 180) {
326
+ diff = 360 - diff;
327
+ }
328
+ if (diff <= NiceTimepickerFace.FAB_OVERLAP_THRESHOLD_DEG) {
329
+ result.add(option.value);
330
+ }
331
+ }
332
+ return result;
333
+ }, /* @ts-ignore */
334
+ ...(ngDevMode ? [{ debugName: "optionsUnderFab" }] : /* istanbul ignore next */ []));
335
+ ngAfterViewInit() {
336
+ this.addTouchEvents();
337
+ }
338
+ onMouseDown(event) {
339
+ event.preventDefault();
340
+ this.isDragging = true;
341
+ }
342
+ onMouseUp(event) {
343
+ event.preventDefault();
344
+ this.isDragging = false;
345
+ }
346
+ onMousePointer(event) {
347
+ if (!this.isDragging && event.type !== "click") {
348
+ return;
349
+ }
350
+ this.selectTime(event.clientX, event.clientY, !this.isDragging);
351
+ }
352
+ onTouchPointer(event) {
353
+ const touch = event.changedTouches[0];
354
+ if (!touch) {
355
+ return;
356
+ }
357
+ if (!this.isDragging && event.type !== "touchend") {
358
+ return;
359
+ }
360
+ this.selectTime(touch.clientX, touch.clientY, !this.isDragging);
361
+ }
362
+ formatOption(option) {
363
+ if (this.unit() === "minute") {
364
+ const labelGap = getMinuteLabelGap(this.minutesGap(), this.dottedMinutesInGap());
365
+ if (!isMinuteLabelVisible(option.value, labelGap)) {
366
+ return null;
367
+ }
368
+ return option.value.toString().padStart(2, "0");
369
+ }
370
+ return option.value.toString();
371
+ }
372
+ isFaceOptionHighlighted(option) {
373
+ if (option.value !== this.selectedValue()) {
374
+ return false;
375
+ }
376
+ if (this.unit() === "minute") {
377
+ const labelGap = getMinuteLabelGap(this.minutesGap(), this.dottedMinutesInGap());
378
+ return isMinuteLabelVisible(option.value, labelGap);
379
+ }
380
+ return true;
381
+ }
382
+ selectTime(clientX, clientY, finalizeSelection) {
383
+ const clockFaceBounds = this.clockFace().nativeElement.getBoundingClientRect();
384
+ const centerX = clockFaceBounds.left + clockFaceBounds.width / 2;
385
+ const centerY = clockFaceBounds.top + clockFaceBounds.height / 2;
386
+ const arctangent = (Math.atan(Math.abs(clientX - centerX) / Math.abs(clientY - centerY)) * 180) / Math.PI;
387
+ const circleAngle = countAngleByCords(centerX, centerY, clientX, clientY, arctangent);
388
+ const angleStep = this.unit() === "minute" ? 6 * (this.minutesGap() || 1) : 30;
389
+ const roundedAngle = roundAngle(circleAngle, angleStep);
390
+ const angle = roundedAngle || 360;
391
+ const selectedTime = this.faceTime().find((time) => time.angle === angle);
392
+ if (!selectedTime) {
393
+ return;
394
+ }
395
+ this.timeChange.emit(selectedTime.value);
396
+ if (finalizeSelection) {
397
+ this.timeSelected.emit(selectedTime.value);
398
+ }
399
+ }
400
+ addTouchEvents() {
401
+ const element = this.clockFace().nativeElement;
402
+ this.touchStartHandler = (event) => this.onMouseDown(event);
403
+ this.touchEndHandler = (event) => this.onMouseUp(event);
404
+ element.addEventListener("touchstart", this.touchStartHandler, { passive: false });
405
+ element.addEventListener("touchend", this.touchEndHandler);
406
+ }
407
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NiceTimepickerFace, deps: [], target: i0.ɵɵFactoryTarget.Component });
408
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: NiceTimepickerFace, isStandalone: true, selector: "nice-timepicker-face", inputs: { dottedMinutesInGap: { classPropertyName: "dottedMinutesInGap", publicName: "dottedMinutesInGap", isSignal: true, isRequired: false, transformFunction: null }, faceTime: { classPropertyName: "faceTime", publicName: "faceTime", isSignal: true, isRequired: true, transformFunction: null }, minutesGap: { classPropertyName: "minutesGap", publicName: "minutesGap", isSignal: true, isRequired: false, transformFunction: null }, selectedValue: { classPropertyName: "selectedValue", publicName: "selectedValue", isSignal: true, isRequired: true, transformFunction: null }, unit: { classPropertyName: "unit", publicName: "unit", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { timeChange: "timeChange", timeSelected: "timeSelected" }, host: { listeners: { "mousedown": "onMouseDown($event)", "mouseup": "onMouseUp($event)", "click": "onMousePointer($event)", "mousemove": "onMousePointer($event)", "touchmove": "onTouchPointer($event)", "touchend": "onTouchPointer($event)" } }, viewQueries: [{ propertyName: "clockFace", first: true, predicate: ["clockFace"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"clock-face\" #clockFace>\n <mat-toolbar\n class=\"clock-face__clock-hand\"\n [class.clock-face__clock-hand_minute]=\"unit() === 'minute'\"\n [style.transform]=\"'rotate(' + selectedTime().angle + 'deg)'\"\n ></mat-toolbar>\n\n <mat-toolbar class=\"clock-face__center\" color=\"primary\"></mat-toolbar>\n\n <div class=\"clock-face__container\">\n @if (unit() === \"hour\") {\n @for (option of faceTime(); track option.value) {\n <div\n class=\"clock-face__number clock-face__number--outer\"\n [style.transform]=\"'rotateZ(' + option.angle + 'deg)'\"\n >\n <button\n mat-mini-fab\n disableRipple\n type=\"button\"\n tabindex=\"-1\"\n class=\"mat-elevation-z0 nice-timepicker-face__button\"\n [color]=\"isFaceOptionHighlighted(option) ? 'primary' : undefined\"\n [style.transform]=\"'rotateZ(-' + option.angle + 'deg)'\"\n >\n {{ formatOption(option) }}\n </button>\n </div>\n }\n } @else {\n @for (option of faceTime(); track option.value) {\n @if (formatOption(option); as label) {\n <div\n class=\"clock-face__number clock-face__number--outer clock-face__number--labeled\"\n [style.transform]=\"'rotateZ(' + option.angle + 'deg)'\"\n >\n <button\n mat-mini-fab\n disableRipple\n type=\"button\"\n tabindex=\"-1\"\n class=\"mat-elevation-z0 nice-timepicker-face__button\"\n [class.nice-timepicker-face__button--under-fab]=\"optionsUnderFab().has(option.value)\"\n [color]=\"isFaceOptionHighlighted(option) ? 'primary' : undefined\"\n [style.transform]=\"'rotateZ(-' + option.angle + 'deg)'\"\n >\n {{ label }}\n </button>\n </div>\n }\n }\n }\n </div>\n\n @if (unit() === \"minute\" && showSelectionFab()) {\n <div\n class=\"clock-face__hand-fab-arm\"\n [style.transform]=\"'rotate(' + selectedTime().angle + 'deg)'\"\n >\n <button\n mat-mini-fab\n disableRipple\n color=\"primary\"\n type=\"button\"\n tabindex=\"-1\"\n class=\"mat-elevation-z0 clock-face__clock-hand_minute-fab\"\n >\n <span class=\"clock-face__clock-hand_minute_dot\"></span>\n </button>\n </div>\n }\n</div>\n", dependencies: [{ kind: "component", type: MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
409
+ }
410
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NiceTimepickerFace, decorators: [{
411
+ type: Component,
412
+ args: [{ selector: "nice-timepicker-face", imports: [MatMiniFabButton, MatToolbar], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"clock-face\" #clockFace>\n <mat-toolbar\n class=\"clock-face__clock-hand\"\n [class.clock-face__clock-hand_minute]=\"unit() === 'minute'\"\n [style.transform]=\"'rotate(' + selectedTime().angle + 'deg)'\"\n ></mat-toolbar>\n\n <mat-toolbar class=\"clock-face__center\" color=\"primary\"></mat-toolbar>\n\n <div class=\"clock-face__container\">\n @if (unit() === \"hour\") {\n @for (option of faceTime(); track option.value) {\n <div\n class=\"clock-face__number clock-face__number--outer\"\n [style.transform]=\"'rotateZ(' + option.angle + 'deg)'\"\n >\n <button\n mat-mini-fab\n disableRipple\n type=\"button\"\n tabindex=\"-1\"\n class=\"mat-elevation-z0 nice-timepicker-face__button\"\n [color]=\"isFaceOptionHighlighted(option) ? 'primary' : undefined\"\n [style.transform]=\"'rotateZ(-' + option.angle + 'deg)'\"\n >\n {{ formatOption(option) }}\n </button>\n </div>\n }\n } @else {\n @for (option of faceTime(); track option.value) {\n @if (formatOption(option); as label) {\n <div\n class=\"clock-face__number clock-face__number--outer clock-face__number--labeled\"\n [style.transform]=\"'rotateZ(' + option.angle + 'deg)'\"\n >\n <button\n mat-mini-fab\n disableRipple\n type=\"button\"\n tabindex=\"-1\"\n class=\"mat-elevation-z0 nice-timepicker-face__button\"\n [class.nice-timepicker-face__button--under-fab]=\"optionsUnderFab().has(option.value)\"\n [color]=\"isFaceOptionHighlighted(option) ? 'primary' : undefined\"\n [style.transform]=\"'rotateZ(-' + option.angle + 'deg)'\"\n >\n {{ label }}\n </button>\n </div>\n }\n }\n }\n </div>\n\n @if (unit() === \"minute\" && showSelectionFab()) {\n <div\n class=\"clock-face__hand-fab-arm\"\n [style.transform]=\"'rotate(' + selectedTime().angle + 'deg)'\"\n >\n <button\n mat-mini-fab\n disableRipple\n color=\"primary\"\n type=\"button\"\n tabindex=\"-1\"\n class=\"mat-elevation-z0 clock-face__clock-hand_minute-fab\"\n >\n <span class=\"clock-face__clock-hand_minute_dot\"></span>\n </button>\n </div>\n }\n</div>\n" }]
413
+ }], propDecorators: { dottedMinutesInGap: [{ type: i0.Input, args: [{ isSignal: true, alias: "dottedMinutesInGap", required: false }] }], faceTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "faceTime", required: true }] }], minutesGap: [{ type: i0.Input, args: [{ isSignal: true, alias: "minutesGap", required: false }] }], selectedValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedValue", required: true }] }], unit: [{ type: i0.Input, args: [{ isSignal: true, alias: "unit", required: true }] }], timeChange: [{ type: i0.Output, args: ["timeChange"] }], timeSelected: [{ type: i0.Output, args: ["timeSelected"] }], clockFace: [{ type: i0.ViewChild, args: ["clockFace", { isSignal: true }] }], onMouseDown: [{
414
+ type: HostListener,
415
+ args: ["mousedown", ["$event"]]
416
+ }], onMouseUp: [{
417
+ type: HostListener,
418
+ args: ["mouseup", ["$event"]]
419
+ }], onMousePointer: [{
420
+ type: HostListener,
421
+ args: ["click", ["$event"]]
422
+ }, {
423
+ type: HostListener,
424
+ args: ["mousemove", ["$event"]]
425
+ }], onTouchPointer: [{
426
+ type: HostListener,
427
+ args: ["touchmove", ["$event"]]
428
+ }, {
429
+ type: HostListener,
430
+ args: ["touchend", ["$event"]]
431
+ }] } });
432
+
433
+ class NiceTimepickerDialog {
434
+ dialogRef = inject((MatDialogRef));
435
+ data = inject(MAT_DIALOG_DATA);
436
+ parsedTime = resolveTime(this.data.time);
437
+ activeUnit = signal("hour", /* @ts-ignore */
438
+ ...(ngDevMode ? [{ debugName: "activeUnit" }] : /* istanbul ignore next */ []));
439
+ hour = signal(this.parsedTime.hour, /* @ts-ignore */
440
+ ...(ngDevMode ? [{ debugName: "hour" }] : /* istanbul ignore next */ []));
441
+ minute = signal(this.parsedTime.minute, /* @ts-ignore */
442
+ ...(ngDevMode ? [{ debugName: "minute" }] : /* istanbul ignore next */ []));
443
+ period = signal(this.parsedTime.period, /* @ts-ignore */
444
+ ...(ngDevMode ? [{ debugName: "period" }] : /* istanbul ignore next */ []));
445
+ selectedHour = computed(() => this.hour().toString().padStart(2, "0"), /* @ts-ignore */
446
+ ...(ngDevMode ? [{ debugName: "selectedHour" }] : /* istanbul ignore next */ []));
447
+ selectedMinute = computed(() => this.minute().toString().padStart(2, "0"), /* @ts-ignore */
448
+ ...(ngDevMode ? [{ debugName: "selectedMinute" }] : /* istanbul ignore next */ []));
449
+ selectedPeriod = computed(() => this.period(), /* @ts-ignore */
450
+ ...(ngDevMode ? [{ debugName: "selectedPeriod" }] : /* istanbul ignore next */ []));
451
+ faceOptions = computed(() => {
452
+ if (this.activeUnit() === "hour") {
453
+ return getHourFaceOptions();
454
+ }
455
+ return getMinuteFaceOptions(this.data.minutesGap);
456
+ }, /* @ts-ignore */
457
+ ...(ngDevMode ? [{ debugName: "faceOptions" }] : /* istanbul ignore next */ []));
458
+ cancel() {
459
+ this.dialogRef.close();
460
+ }
461
+ confirm() {
462
+ const time = {
463
+ hour: this.hour(),
464
+ minute: this.minute(),
465
+ period: this.period()
466
+ };
467
+ this.dialogRef.close(formatTime(time));
468
+ }
469
+ onFaceTimeChange(value) {
470
+ if (this.activeUnit() === "hour") {
471
+ this.hour.set(value);
472
+ return;
473
+ }
474
+ this.minute.set(value);
475
+ }
476
+ onFaceTimeSelected(value) {
477
+ this.onFaceTimeChange(value);
478
+ if (this.activeUnit() === "hour") {
479
+ this.activeUnit.set("minute");
480
+ }
481
+ }
482
+ setActiveUnit(unit) {
483
+ this.activeUnit.set(unit);
484
+ }
485
+ setPeriod(period) {
486
+ this.period.set(period);
487
+ }
488
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NiceTimepickerDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
489
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.0", type: NiceTimepickerDialog, isStandalone: true, selector: "nice-timepicker-dialog", ngImport: i0, template: "<div mat-dialog-content>\n <div class=\"timepicker\">\n <mat-toolbar class=\"timepicker-header\">\n <div class=\"timepicker-dial\">\n <p class=\"nice-timepicker-dialog-title\">{{ data.titleKey | niceTranslate }}</p>\n\n <div class=\"timepicker-dial__container\">\n <div class=\"timepicker-dial__time\">\n <button\n type=\"button\"\n class=\"timepicker-dial__control\"\n [class.active]=\"activeUnit() === 'hour'\"\n (click)=\"setActiveUnit('hour')\"\n >\n {{ selectedHour() }}\n </button>\n\n <span>:</span>\n\n <button\n type=\"button\"\n class=\"timepicker-dial__control\"\n [class.active]=\"activeUnit() === 'minute'\"\n (click)=\"setActiveUnit('minute')\"\n >\n {{ selectedMinute() }}\n </button>\n </div>\n\n <div class=\"timepicker-dial__period\">\n <button\n type=\"button\"\n class=\"timepicker-period__btn\"\n [class.active]=\"selectedPeriod() === 'AM'\"\n (click)=\"setPeriod('AM')\"\n >\n AM\n </button>\n\n <button\n type=\"button\"\n class=\"timepicker-period__btn\"\n [class.active]=\"selectedPeriod() === 'PM'\"\n (click)=\"setPeriod('PM')\"\n >\n PM\n </button>\n </div>\n </div>\n </div>\n </mat-toolbar>\n\n <div class=\"timepicker__main-content\">\n <div class=\"timepicker__body\">\n <nice-timepicker-face\n [dottedMinutesInGap]=\"data.dottedMinutesInGap\"\n [faceTime]=\"faceOptions()\"\n [minutesGap]=\"data.minutesGap\"\n [selectedValue]=\"activeUnit() === 'hour' ? hour() : minute()\"\n [unit]=\"activeUnit()\"\n (timeChange)=\"onFaceTimeChange($event)\"\n (timeSelected)=\"onFaceTimeSelected($event)\"\n />\n </div>\n </div>\n </div>\n</div>\n\n<div mat-dialog-actions>\n <button mat-button type=\"button\" (click)=\"cancel()\">\n {{ data.cancelKey | niceTranslate }}\n </button>\n\n <button mat-flat-button type=\"button\" (click)=\"confirm()\">\n {{ data.confirmKey | niceTranslate }}\n </button>\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: NiceTimepickerFace, selector: "nice-timepicker-face", inputs: ["dottedMinutesInGap", "faceTime", "minutesGap", "selectedValue", "unit"], outputs: ["timeChange", "timeSelected"] }, { kind: "pipe", type: NiceTranslatePipe, name: "niceTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
490
+ }
491
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NiceTimepickerDialog, decorators: [{
492
+ type: Component,
493
+ args: [{ selector: "nice-timepicker-dialog", imports: [MatDialogModule, MatToolbar, MatButton, NiceTimepickerFace, NiceTranslatePipe], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div mat-dialog-content>\n <div class=\"timepicker\">\n <mat-toolbar class=\"timepicker-header\">\n <div class=\"timepicker-dial\">\n <p class=\"nice-timepicker-dialog-title\">{{ data.titleKey | niceTranslate }}</p>\n\n <div class=\"timepicker-dial__container\">\n <div class=\"timepicker-dial__time\">\n <button\n type=\"button\"\n class=\"timepicker-dial__control\"\n [class.active]=\"activeUnit() === 'hour'\"\n (click)=\"setActiveUnit('hour')\"\n >\n {{ selectedHour() }}\n </button>\n\n <span>:</span>\n\n <button\n type=\"button\"\n class=\"timepicker-dial__control\"\n [class.active]=\"activeUnit() === 'minute'\"\n (click)=\"setActiveUnit('minute')\"\n >\n {{ selectedMinute() }}\n </button>\n </div>\n\n <div class=\"timepicker-dial__period\">\n <button\n type=\"button\"\n class=\"timepicker-period__btn\"\n [class.active]=\"selectedPeriod() === 'AM'\"\n (click)=\"setPeriod('AM')\"\n >\n AM\n </button>\n\n <button\n type=\"button\"\n class=\"timepicker-period__btn\"\n [class.active]=\"selectedPeriod() === 'PM'\"\n (click)=\"setPeriod('PM')\"\n >\n PM\n </button>\n </div>\n </div>\n </div>\n </mat-toolbar>\n\n <div class=\"timepicker__main-content\">\n <div class=\"timepicker__body\">\n <nice-timepicker-face\n [dottedMinutesInGap]=\"data.dottedMinutesInGap\"\n [faceTime]=\"faceOptions()\"\n [minutesGap]=\"data.minutesGap\"\n [selectedValue]=\"activeUnit() === 'hour' ? hour() : minute()\"\n [unit]=\"activeUnit()\"\n (timeChange)=\"onFaceTimeChange($event)\"\n (timeSelected)=\"onFaceTimeSelected($event)\"\n />\n </div>\n </div>\n </div>\n</div>\n\n<div mat-dialog-actions>\n <button mat-button type=\"button\" (click)=\"cancel()\">\n {{ data.cancelKey | niceTranslate }}\n </button>\n\n <button mat-flat-button type=\"button\" (click)=\"confirm()\">\n {{ data.confirmKey | niceTranslate }}\n </button>\n</div>\n" }]
494
+ }] });
495
+
496
+ class NiceTimepickerToggle {
497
+ config = inject(NICE_TIMEPICKER_CONFIG);
498
+ dialog = inject(MatDialog);
499
+ destroyRef = inject(DestroyRef);
500
+ field = input.required(/* @ts-ignore */
501
+ ...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
502
+ toggleIcon = this.config.toggleIcon;
503
+ translationKeys = this.config.translationKeys;
504
+ isDisabled = computed(() => this.field()().disabled(), /* @ts-ignore */
505
+ ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
506
+ openPicker(event) {
507
+ event.stopPropagation();
508
+ if (this.isDisabled()) {
509
+ return;
510
+ }
511
+ const dialogRef = this.dialog.open(NiceTimepickerDialog, {
512
+ panelClass: "nice-timepicker-dialog",
513
+ data: {
514
+ time: this.field()().value() || "",
515
+ titleKey: this.translationKeys.title,
516
+ cancelKey: this.translationKeys.cancel,
517
+ confirmKey: this.translationKeys.confirm,
518
+ minutesGap: this.config.minutesGap ?? 1,
519
+ dottedMinutesInGap: this.config.dottedMinutesInGap ?? true
520
+ }
521
+ });
522
+ dialogRef
523
+ .afterClosed()
524
+ .pipe(takeUntilDestroyed(this.destroyRef))
525
+ .subscribe((time) => {
526
+ if (time !== undefined) {
527
+ this.field()().value.set(time);
528
+ }
529
+ });
530
+ }
531
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NiceTimepickerToggle, deps: [], target: i0.ɵɵFactoryTarget.Component });
532
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: NiceTimepickerToggle, isStandalone: true, selector: "nice-timepicker-toggle", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "nice-timepicker-toggle" }, ngImport: i0, template: "<button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"'Open time picker'\"\n [disabled]=\"isDisabled()\"\n (click)=\"openPicker($event)\"\n>\n @if (toggleIcon) {\n <mat-icon [svgIcon]=\"toggleIcon\" />\n } @else {\n <mat-icon>schedule</mat-icon>\n }\n</button>\n", styles: [".nice-timepicker-toggle{display:inline-flex}\n"], dependencies: [{ kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }], encapsulation: i0.ViewEncapsulation.None });
533
+ }
534
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NiceTimepickerToggle, decorators: [{
535
+ type: Component,
536
+ args: [{ selector: "nice-timepicker-toggle", imports: [MatIcon, MatIconButton], encapsulation: ViewEncapsulation.None, host: {
537
+ class: "nice-timepicker-toggle"
538
+ }, template: "<button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"'Open time picker'\"\n [disabled]=\"isDisabled()\"\n (click)=\"openPicker($event)\"\n>\n @if (toggleIcon) {\n <mat-icon [svgIcon]=\"toggleIcon\" />\n } @else {\n <mat-icon>schedule</mat-icon>\n }\n</button>\n", styles: [".nice-timepicker-toggle{display:inline-flex}\n"] }]
539
+ }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }] } });
540
+
541
+ /*
542
+ * Public API Surface of timepicker
543
+ */
544
+
545
+ /**
546
+ * Generated bundle index. Do not edit.
547
+ */
548
+
549
+ export { DEFAULT_MINUTES_GAP, DEFAULT_MINUTE_LABEL_GAP, NICE_TIME_PARSE_ERROR_KIND, NiceTimepicker, NiceTimepickerToggle, defaultConfig as defaultTimepickerConfig, formatTime, getDefaultTime, getHourFaceOptions, getMinuteFaceOptions, getMinuteLabelGap, hasNiceTimeFormatError, isMinuteLabelVisible, isValidNiceTime, niceTimeFieldErrorState, niceTimeFormat, normalizeNiceTime, parseTime, provideTimepicker, registerNiceTimeFormatValidation, resolveTime };
550
+ //# sourceMappingURL=recursyve-ngx-material-components-timepicker.mjs.map