@recursyve/ngx-material-components 22.0.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.
Files changed (23) hide show
  1. package/_sass/_override-utils.scss +31 -0
  2. package/colorpicker/_index.scss +1 -0
  3. package/colorpicker/_theme.scss +19 -0
  4. package/fesm2022/recursyve-ngx-material-components-chip-list.mjs +2 -2
  5. package/fesm2022/recursyve-ngx-material-components-chip-list.mjs.map +1 -1
  6. package/fesm2022/recursyve-ngx-material-components-colorpicker.mjs +237 -0
  7. package/fesm2022/recursyve-ngx-material-components-colorpicker.mjs.map +1 -0
  8. package/fesm2022/recursyve-ngx-material-components-dropzone.mjs +23 -7
  9. package/fesm2022/recursyve-ngx-material-components-dropzone.mjs.map +1 -1
  10. package/fesm2022/recursyve-ngx-material-components-form-field-error.mjs +174 -33
  11. package/fesm2022/recursyve-ngx-material-components-form-field-error.mjs.map +1 -1
  12. package/fesm2022/recursyve-ngx-material-components-timepicker.mjs +550 -0
  13. package/fesm2022/recursyve-ngx-material-components-timepicker.mjs.map +1 -0
  14. package/fesm2022/recursyve-ngx-material-components-typeahead.mjs +24 -12
  15. package/fesm2022/recursyve-ngx-material-components-typeahead.mjs.map +1 -1
  16. package/package.json +16 -4
  17. package/timepicker/_index.scss +1 -0
  18. package/timepicker/_theme.scss +41 -0
  19. package/types/recursyve-ngx-material-components-colorpicker.d.ts +77 -0
  20. package/types/recursyve-ngx-material-components-dropzone.d.ts +8 -1
  21. package/types/recursyve-ngx-material-components-form-field-error.d.ts +25 -3
  22. package/types/recursyve-ngx-material-components-timepicker.d.ts +99 -0
  23. package/types/recursyve-ngx-material-components-typeahead.d.ts +3 -0
@@ -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