@gloww/gloww 20.0.0-beta.32 → 20.0.0-beta.34

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.
@@ -3626,6 +3626,10 @@ class DatetimeComponent {
3626
3626
  'YYYY-MM-DDTHH:mm:ss',
3627
3627
  'YYYY-MM-DDTHH:mm'
3628
3628
  ]; }
3629
+ static { this.CLOCK_CENTER = 50; }
3630
+ static { this.CLOCK_OUTER_RADIUS = 40; }
3631
+ static { this.CLOCK_INNER_RADIUS = 27; }
3632
+ static { this.CLOCK_TICK_RADIUS = 42; }
3629
3633
  constructor(locale) {
3630
3634
  this.locale = locale;
3631
3635
  this._value = null;
@@ -3637,7 +3641,12 @@ class DatetimeComponent {
3637
3641
  this.hourControl = new FormControl('00', { nonNullable: true });
3638
3642
  this.minuteControl = new FormControl('00', { nonNullable: true });
3639
3643
  this.secondControl = new FormControl('00', { nonNullable: true });
3644
+ this.hourMarkers = this.buildHourMarkers();
3645
+ this.minuteTicks = this.buildUnitTicks();
3646
+ this.secondTicks = this.buildUnitTicks();
3640
3647
  this.isDisabled = false;
3648
+ this.isClockOpen = false;
3649
+ this.clockStep = 'hour';
3641
3650
  this.onChange = () => { };
3642
3651
  this.onTouched = () => { };
3643
3652
  this.dateControl.valueChanges.subscribe(() => this.emitCurrentValue());
@@ -3673,6 +3682,7 @@ class DatetimeComponent {
3673
3682
  }
3674
3683
  clear() {
3675
3684
  this._value = null;
3685
+ this.isClockOpen = false;
3676
3686
  this.dateControl.setValue(null, { emitEvent: false });
3677
3687
  this.hourControl.setValue('00', { emitEvent: false });
3678
3688
  this.minuteControl.setValue('00', { emitEvent: false });
@@ -3683,37 +3693,31 @@ class DatetimeComponent {
3683
3693
  markTouched() {
3684
3694
  this.onTouched();
3685
3695
  }
3686
- get timePickerValue() {
3687
- const hour = this.normalizeTimePartValue(this.hourControl.value, 23, true);
3688
- const minute = this.normalizeTimePartValue(this.minuteControl.value, 59, true);
3689
- const second = this.normalizeTimePartValue(this.secondControl.value, 59, true);
3690
- return this.showSeconds ? `${hour}:${minute}:${second}` : `${hour}:${minute}`;
3696
+ get timeSummary() {
3697
+ return this.showSeconds
3698
+ ? `${this.normalizeTimePartValue(this.hourControl.value, 23, true)}:${this.normalizeTimePartValue(this.minuteControl.value, 59, true)}:${this.normalizeTimePartValue(this.secondControl.value, 59, true)}`
3699
+ : `${this.normalizeTimePartValue(this.hourControl.value, 23, true)}:${this.normalizeTimePartValue(this.minuteControl.value, 59, true)}`;
3691
3700
  }
3692
- openNativeTimePicker(input) {
3701
+ get clockTitle() {
3702
+ switch (this.clockStep) {
3703
+ case 'hour':
3704
+ return 'Hours';
3705
+ case 'minute':
3706
+ return 'Minutes';
3707
+ default:
3708
+ return 'Seconds';
3709
+ }
3710
+ }
3711
+ openClock() {
3693
3712
  if (this.isDisabled) {
3694
3713
  return;
3695
3714
  }
3696
- const picker = input;
3697
- if (typeof picker.showPicker === 'function') {
3698
- picker.showPicker();
3699
- }
3700
- else {
3701
- picker.focus();
3702
- picker.click();
3703
- }
3715
+ this.isClockOpen = true;
3716
+ this.clockStep = 'hour';
3704
3717
  this.onTouched();
3705
3718
  }
3706
- onNativeTimeSelected(value) {
3707
- if (!value) {
3708
- return;
3709
- }
3710
- const parts = value.split(':');
3711
- this.hourControl.setValue(this.normalizeTimePartValue(parts[0], 23, true), { emitEvent: false });
3712
- this.minuteControl.setValue(this.normalizeTimePartValue(parts[1], 59, true), { emitEvent: false });
3713
- if (this.showSeconds) {
3714
- this.secondControl.setValue(this.normalizeTimePartValue(parts[2], 59, true), { emitEvent: false });
3715
- }
3716
- this.emitCurrentValue();
3719
+ closeClock() {
3720
+ this.isClockOpen = false;
3717
3721
  this.onTouched();
3718
3722
  }
3719
3723
  onTimePartInput(part) {
@@ -3729,6 +3733,64 @@ class DatetimeComponent {
3729
3733
  this.emitCurrentValue();
3730
3734
  this.onTouched();
3731
3735
  }
3736
+ selectClockHour(value) {
3737
+ this.hourControl.setValue(value.toString().padStart(2, '0'), { emitEvent: false });
3738
+ this.emitCurrentValue();
3739
+ this.clockStep = 'minute';
3740
+ this.onTouched();
3741
+ }
3742
+ selectClockMinute(value) {
3743
+ this.minuteControl.setValue(value.toString().padStart(2, '0'), { emitEvent: false });
3744
+ this.emitCurrentValue();
3745
+ if (this.showSeconds) {
3746
+ this.clockStep = 'second';
3747
+ }
3748
+ else {
3749
+ this.closeClock();
3750
+ }
3751
+ this.onTouched();
3752
+ }
3753
+ selectClockSecond(value) {
3754
+ this.secondControl.setValue(value.toString().padStart(2, '0'), { emitEvent: false });
3755
+ this.emitCurrentValue();
3756
+ this.closeClock();
3757
+ this.onTouched();
3758
+ }
3759
+ goToClockStep(step) {
3760
+ this.clockStep = step;
3761
+ }
3762
+ onClockFaceClick(event) {
3763
+ if (this.clockStep === 'hour') {
3764
+ return;
3765
+ }
3766
+ const target = event.currentTarget;
3767
+ if (!target) {
3768
+ return;
3769
+ }
3770
+ const rect = target.getBoundingClientRect();
3771
+ const centerX = rect.left + (rect.width / 2);
3772
+ const centerY = rect.top + (rect.height / 2);
3773
+ const dx = event.clientX - centerX;
3774
+ const dy = event.clientY - centerY;
3775
+ const angle = Math.atan2(dy, dx) + (Math.PI / 2);
3776
+ const normalized = angle < 0 ? angle + (Math.PI * 2) : angle;
3777
+ const rawValue = Math.round((normalized / (Math.PI * 2)) * 60) % 60;
3778
+ const snappedValue = Math.round(rawValue / 5) * 5 % 60;
3779
+ if (this.clockStep === 'minute') {
3780
+ this.selectClockMinute(snappedValue);
3781
+ return;
3782
+ }
3783
+ this.selectClockSecond(snappedValue);
3784
+ }
3785
+ isHourSelected(value) {
3786
+ return this.toNumber(this.hourControl.value) === value;
3787
+ }
3788
+ isMinuteSelected(value) {
3789
+ return this.toNumber(this.minuteControl.value) === value;
3790
+ }
3791
+ isSecondSelected(value) {
3792
+ return this.toNumber(this.secondControl.value) === value;
3793
+ }
3732
3794
  setValueFromOutside(value, emit) {
3733
3795
  const normalized = this.normalizeDateValue(value);
3734
3796
  this._value = normalized;
@@ -3804,6 +3866,35 @@ class DatetimeComponent {
3804
3866
  }
3805
3867
  return null;
3806
3868
  }
3869
+ buildHourMarkers() {
3870
+ return Array.from({ length: 24 }, (_, index) => this.buildClockMarker(index, index < 12 ? DatetimeComponent.CLOCK_OUTER_RADIUS : DatetimeComponent.CLOCK_INNER_RADIUS, index.toString().padStart(2, '0')));
3871
+ }
3872
+ buildUnitTicks() {
3873
+ return Array.from({ length: 12 }, (_, index) => {
3874
+ const value = index * 5;
3875
+ const angle = (index / 12) * (Math.PI * 2) - (Math.PI / 2);
3876
+ const x = DatetimeComponent.CLOCK_CENTER + Math.cos(angle) * DatetimeComponent.CLOCK_TICK_RADIUS;
3877
+ const y = DatetimeComponent.CLOCK_CENTER + Math.sin(angle) * DatetimeComponent.CLOCK_TICK_RADIUS;
3878
+ return {
3879
+ value,
3880
+ label: value.toString().padStart(2, '0'),
3881
+ left: `${x}%`,
3882
+ top: `${y}%`,
3883
+ rotate: `${angle + (Math.PI / 2)}rad`
3884
+ };
3885
+ });
3886
+ }
3887
+ buildClockMarker(index, radius, label) {
3888
+ const angle = ((index % 12) / 12) * (Math.PI * 2) - (Math.PI / 2);
3889
+ const x = DatetimeComponent.CLOCK_CENTER + Math.cos(angle) * radius;
3890
+ const y = DatetimeComponent.CLOCK_CENTER + Math.sin(angle) * radius;
3891
+ return {
3892
+ value: index,
3893
+ label,
3894
+ left: `${x}%`,
3895
+ top: `${y}%`
3896
+ };
3897
+ }
3807
3898
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatetimeComponent, deps: [{ token: MAT_DATE_LOCALE, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
3808
3899
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: DatetimeComponent, isStandalone: true, selector: "gloww-datetime", inputs: { _value: ["value", "_value"], display: "display", placeHolder: "placeHolder", mode: "mode", showSeconds: "showSeconds" }, providers: [
3809
3900
  {
@@ -3811,7 +3902,7 @@ class DatetimeComponent {
3811
3902
  multi: true,
3812
3903
  useExisting: forwardRef(() => DatetimeComponent)
3813
3904
  }
3814
- ], ngImport: i0, template: "<div class=\"datetime-wrapper\" [class.datetime-mode]=\"isDateTime\" [class.has-seconds]=\"showSeconds\">\n <mat-form-field class=\"date-field\">\n @if (display) {\n <mat-label>{{ display }}</mat-label>\n }\n <input\n matInput\n [matDatepicker]=\"picker\"\n [placeholder]=\"placeHolder\"\n [formControl]=\"dateControl\"\n [disabled]=\"isDisabled\"\n (dateChange)=\"markTouched()\"\n (blur)=\"markTouched()\">\n <mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n </mat-form-field>\n\n @if (isDateTime) {\n <div class=\"middle-actions\">\n <button\n mat-icon-button\n type=\"button\"\n class=\"icon-action\"\n [disabled]=\"isDisabled\"\n title=\"Clear date and time\"\n aria-label=\"Clear date and time\"\n (click)=\"clear()\">\n <mat-icon>ink_eraser</mat-icon>\n </button>\n\n <input\n #nativeTimePicker\n class=\"native-time-picker\"\n type=\"time\"\n [step]=\"showSeconds ? 1 : 60\"\n [value]=\"timePickerValue\"\n [disabled]=\"isDisabled\"\n (change)=\"onNativeTimeSelected(nativeTimePicker.value)\">\n\n <button\n mat-icon-button\n type=\"button\"\n class=\"icon-action\"\n [disabled]=\"isDisabled\"\n title=\"Open time picker\"\n aria-label=\"Open time picker\"\n (click)=\"openNativeTimePicker(nativeTimePicker)\">\n <mat-icon>schedule</mat-icon>\n </button>\n </div>\n\n <div class=\"time-row\">\n <mat-form-field class=\"time-field\">\n <mat-label>HH</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"23\"\n inputmode=\"numeric\"\n [formControl]=\"hourControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('hour')\"\n (blur)=\"onTimePartBlur('hour')\">\n </mat-form-field>\n\n <mat-form-field class=\"time-field\">\n <mat-label>MM</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"59\"\n inputmode=\"numeric\"\n [formControl]=\"minuteControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('minute')\"\n (blur)=\"onTimePartBlur('minute')\">\n </mat-form-field>\n\n @if (showSeconds) {\n <mat-form-field class=\"time-field\">\n <mat-label>SS</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"59\"\n inputmode=\"numeric\"\n [formControl]=\"secondControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('second')\"\n (blur)=\"onTimePartBlur('second')\">\n </mat-form-field>\n }\n </div>\n }\n</div>\n", styles: [".datetime-wrapper{display:flex;flex-direction:column;gap:8px;width:100%}.date-field,.time-field{width:100%}.middle-actions{display:flex;align-items:center;gap:4px;justify-content:flex-start}.icon-action{flex:0 0 auto}.time-row{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}.native-time-picker{position:absolute;pointer-events:none;opacity:0;width:0;height:0}@media(min-width:720px){.time-row{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(min-width:960px){.datetime-wrapper.datetime-mode{display:grid;align-items:start;gap:8px;grid-template-columns:minmax(240px,2fr) auto repeat(2,minmax(84px,96px))}.datetime-wrapper.datetime-mode.has-seconds{grid-template-columns:minmax(240px,2fr) auto repeat(3,minmax(84px,96px))}.datetime-wrapper.datetime-mode .date-field{margin-bottom:0}.datetime-wrapper.datetime-mode .middle-actions{align-self:start;padding-top:4px}.datetime-wrapper.datetime-mode .time-row{display:contents}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: MatFormField$1, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel$1, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "component", type: MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); }
3905
+ ], ngImport: i0, template: "<div class=\"datetime-wrapper\" [class.datetime-mode]=\"isDateTime\" [class.has-seconds]=\"showSeconds\">\n <mat-form-field class=\"date-field\">\n @if (display) {\n <mat-label>{{ display }}</mat-label>\n }\n <input\n matInput\n [matDatepicker]=\"picker\"\n [placeholder]=\"placeHolder\"\n [formControl]=\"dateControl\"\n [disabled]=\"isDisabled\"\n (dateChange)=\"markTouched()\"\n (blur)=\"markTouched()\">\n <mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n </mat-form-field>\n\n @if (isDateTime) {\n <div class=\"middle-actions\">\n <button\n mat-icon-button\n type=\"button\"\n class=\"icon-action\"\n [disabled]=\"isDisabled\"\n title=\"Clear date and time\"\n aria-label=\"Clear date and time\"\n (click)=\"clear()\">\n <i class=\"fal fa-eraser\"></i>\n </button>\n\n <button\n mat-icon-button\n type=\"button\"\n class=\"icon-action\"\n [disabled]=\"isDisabled\"\n title=\"Open time clock\"\n aria-label=\"Open time clock\"\n (click)=\"openClock()\">\n <i class=\"fal fa-clock\"></i>\n </button>\n </div>\n\n <div class=\"time-row\">\n <mat-form-field class=\"time-field\">\n <mat-label>HH</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"23\"\n inputmode=\"numeric\"\n [formControl]=\"hourControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('hour')\"\n (blur)=\"onTimePartBlur('hour')\">\n </mat-form-field>\n\n <mat-form-field class=\"time-field\">\n <mat-label>MM</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"59\"\n inputmode=\"numeric\"\n [formControl]=\"minuteControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('minute')\"\n (blur)=\"onTimePartBlur('minute')\">\n </mat-form-field>\n\n @if (showSeconds) {\n <mat-form-field class=\"time-field\">\n <mat-label>SS</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"59\"\n inputmode=\"numeric\"\n [formControl]=\"secondControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('second')\"\n (blur)=\"onTimePartBlur('second')\">\n </mat-form-field>\n }\n </div>\n\n @if (isClockOpen) {\n <div class=\"clock-backdrop\" (click)=\"closeClock()\"></div>\n\n <div class=\"clock-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"clock-panel\">\n <div class=\"clock-panel-header\">\n <div class=\"clock-panel-title\">{{ clockTitle }}</div>\n <button\n mat-button\n type=\"button\"\n class=\"clock-close\"\n [disabled]=\"isDisabled\"\n (click)=\"closeClock()\">\n Close\n </button>\n </div>\n\n <div class=\"clock-summary\">\n <button mat-button type=\"button\" class=\"clock-step\" [class.active]=\"clockStep === 'hour'\" (click)=\"goToClockStep('hour')\">\n {{ hourControl.value || '00' }}\n </button>\n <span>:</span>\n <button mat-button type=\"button\" class=\"clock-step\" [class.active]=\"clockStep === 'minute'\" (click)=\"goToClockStep('minute')\">\n {{ minuteControl.value || '00' }}\n </button>\n @if (showSeconds) {\n <span>:</span>\n <button mat-button type=\"button\" class=\"clock-step\" [class.active]=\"clockStep === 'second'\" (click)=\"goToClockStep('second')\">\n {{ secondControl.value || '00' }}\n </button>\n }\n </div>\n\n <div class=\"clock-face\" [class.clickable-face]=\"clockStep !== 'hour'\" (click)=\"onClockFaceClick($event)\">\n @if (clockStep === 'hour') {\n @for (marker of hourMarkers; track marker.value) {\n <button\n mat-icon-button\n type=\"button\"\n class=\"clock-marker\"\n [class.selected]=\"isHourSelected(marker.value)\"\n [class.inner-ring]=\"marker.value >= 12\"\n [style.left]=\"marker.left\"\n [style.top]=\"marker.top\"\n (click)=\"selectClockHour(marker.value)\">\n {{ marker.label }}\n </button>\n }\n }\n\n @if (clockStep === 'minute') {\n @for (tick of minuteTicks; track tick.value) {\n <div\n class=\"clock-tick\"\n [class.selected]=\"isMinuteSelected(tick.value)\"\n [style.left]=\"tick.left\"\n [style.top]=\"tick.top\"\n [style.rotate]=\"tick.rotate\">\n </div>\n <div\n class=\"clock-tick-label\"\n [class.selected]=\"isMinuteSelected(tick.value)\"\n [style.left]=\"tick.left\"\n [style.top]=\"tick.top\">\n {{ tick.label }}\n </div>\n }\n }\n\n @if (clockStep === 'second') {\n @for (tick of secondTicks; track tick.value) {\n <div\n class=\"clock-tick\"\n [class.selected]=\"isSecondSelected(tick.value)\"\n [style.left]=\"tick.left\"\n [style.top]=\"tick.top\"\n [style.rotate]=\"tick.rotate\">\n </div>\n <div\n class=\"clock-tick-label\"\n [class.selected]=\"isSecondSelected(tick.value)\"\n [style.left]=\"tick.left\"\n [style.top]=\"tick.top\">\n {{ tick.label }}\n </div>\n }\n }\n\n <div class=\"clock-center-dot\"></div>\n </div>\n\n <div class=\"clock-help\">\n @if (clockStep === 'hour') {\n <span>Outer ring 00-11, inner ring 12-23.</span>\n }\n @if (clockStep === 'minute') {\n <span>Click the dial. The nearest 5-minute mark will be selected.</span>\n }\n @if (clockStep === 'second') {\n <span>Click the dial. The nearest 5-second mark will be selected.</span>\n }\n </div>\n\n <div class=\"clock-footer\">\n <span>{{ timeSummary }}</span>\n </div>\n </div>\n </div>\n }\n }\n</div>\n", styles: [".datetime-wrapper{display:flex;flex-direction:column;gap:8px;width:100%}.date-field,.time-field{width:100%}.middle-actions{display:flex;align-items:center;gap:4px;justify-content:flex-start}.icon-action{flex:0 0 auto}.icon-action i{font-size:1rem;line-height:1}.time-row{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}.clock-backdrop{position:fixed;inset:0;z-index:1000;background:#0000002e}.clock-dialog{position:fixed;z-index:1001;top:50%;left:50%;width:min(92vw,360px);transform:translate(-50%,-50%)}.clock-panel{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid rgba(0,0,0,.12);border-radius:16px;background:#fff;box-shadow:0 18px 36px #0003}.clock-panel-header,.clock-summary,.clock-footer{display:flex;align-items:center;justify-content:space-between;gap:8px}.clock-panel-title{font-size:.95rem;font-weight:600}.clock-summary{justify-content:center;font-size:1.1rem}.clock-step{min-width:52px;font-weight:600}.clock-step.active{background:#1976d21f}.clock-face{position:relative;align-self:center;width:min(100%,280px);aspect-ratio:1;border-radius:50%;background:radial-gradient(circle at center,rgba(25,118,210,.08) 0 16%,transparent 16% 100%),radial-gradient(circle at center,transparent 0 61%,rgba(25,118,210,.08) 61% 62%,transparent 62% 100%),radial-gradient(circle at center,#0000000a 0 100%)}.clock-face.clickable-face{cursor:crosshair}.clock-marker{position:absolute;transform:translate(-50%,-50%);width:38px;height:38px;min-width:38px;padding:0;border-radius:50%;font-size:.72rem;line-height:1}.clock-marker.inner-ring{width:34px;height:34px;min-width:34px;font-size:.68rem}.clock-unit-marker{width:24px;height:24px;min-width:24px;font-size:.58rem;color:transparent}.clock-unit-marker.major{width:34px;height:34px;min-width:34px;font-size:.68rem;color:inherit}.clock-marker.selected{background:#1976d2;color:#fff}.clock-tick{position:absolute;transform:translate(-50%,-50%);width:2px;height:16px;border-radius:999px;background:#00000057;transform-origin:center center}.clock-tick.selected{width:4px;height:22px;background:#1976d2}.clock-tick-label{position:absolute;transform:translate(-50%,-50%);font-size:.68rem;line-height:1;color:#000000b8}.clock-tick-label.selected{color:#1976d2;font-weight:700}.clock-center-dot{position:absolute;top:50%;left:50%;width:12px;height:12px;border-radius:50%;transform:translate(-50%,-50%);background:#1976d2}.clock-help,.clock-footer{font-size:.78rem;color:#000000ad}@media(min-width:720px){.time-row{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(min-width:960px){.datetime-wrapper.datetime-mode{display:grid;align-items:start;gap:8px;grid-template-columns:minmax(240px,2fr) auto repeat(2,minmax(84px,96px))}.datetime-wrapper.datetime-mode.has-seconds{grid-template-columns:minmax(240px,2fr) auto repeat(3,minmax(84px,96px))}.datetime-wrapper.datetime-mode .date-field{margin-bottom:0}.datetime-wrapper.datetime-mode .middle-actions{align-self:start;padding-top:4px}.datetime-wrapper.datetime-mode .time-row{display:contents}.datetime-wrapper.datetime-mode .clock-panel{grid-column:1/-1}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: MatFormField$1, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel$1, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "component", type: MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { 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"] }] }); }
3815
3906
  }
3816
3907
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatetimeComponent, decorators: [{
3817
3908
  type: Component,
@@ -3821,7 +3912,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
3821
3912
  multi: true,
3822
3913
  useExisting: forwardRef(() => DatetimeComponent)
3823
3914
  }
3824
- ], imports: [ReactiveFormsModule, MatFormField$1, MatLabel$1, MatInput, MatDatepickerInput, MatDatepickerToggle, MatDatepicker, MatSuffix, MatButton, MatIcon], template: "<div class=\"datetime-wrapper\" [class.datetime-mode]=\"isDateTime\" [class.has-seconds]=\"showSeconds\">\n <mat-form-field class=\"date-field\">\n @if (display) {\n <mat-label>{{ display }}</mat-label>\n }\n <input\n matInput\n [matDatepicker]=\"picker\"\n [placeholder]=\"placeHolder\"\n [formControl]=\"dateControl\"\n [disabled]=\"isDisabled\"\n (dateChange)=\"markTouched()\"\n (blur)=\"markTouched()\">\n <mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n </mat-form-field>\n\n @if (isDateTime) {\n <div class=\"middle-actions\">\n <button\n mat-icon-button\n type=\"button\"\n class=\"icon-action\"\n [disabled]=\"isDisabled\"\n title=\"Clear date and time\"\n aria-label=\"Clear date and time\"\n (click)=\"clear()\">\n <mat-icon>ink_eraser</mat-icon>\n </button>\n\n <input\n #nativeTimePicker\n class=\"native-time-picker\"\n type=\"time\"\n [step]=\"showSeconds ? 1 : 60\"\n [value]=\"timePickerValue\"\n [disabled]=\"isDisabled\"\n (change)=\"onNativeTimeSelected(nativeTimePicker.value)\">\n\n <button\n mat-icon-button\n type=\"button\"\n class=\"icon-action\"\n [disabled]=\"isDisabled\"\n title=\"Open time picker\"\n aria-label=\"Open time picker\"\n (click)=\"openNativeTimePicker(nativeTimePicker)\">\n <mat-icon>schedule</mat-icon>\n </button>\n </div>\n\n <div class=\"time-row\">\n <mat-form-field class=\"time-field\">\n <mat-label>HH</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"23\"\n inputmode=\"numeric\"\n [formControl]=\"hourControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('hour')\"\n (blur)=\"onTimePartBlur('hour')\">\n </mat-form-field>\n\n <mat-form-field class=\"time-field\">\n <mat-label>MM</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"59\"\n inputmode=\"numeric\"\n [formControl]=\"minuteControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('minute')\"\n (blur)=\"onTimePartBlur('minute')\">\n </mat-form-field>\n\n @if (showSeconds) {\n <mat-form-field class=\"time-field\">\n <mat-label>SS</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"59\"\n inputmode=\"numeric\"\n [formControl]=\"secondControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('second')\"\n (blur)=\"onTimePartBlur('second')\">\n </mat-form-field>\n }\n </div>\n }\n</div>\n", styles: [".datetime-wrapper{display:flex;flex-direction:column;gap:8px;width:100%}.date-field,.time-field{width:100%}.middle-actions{display:flex;align-items:center;gap:4px;justify-content:flex-start}.icon-action{flex:0 0 auto}.time-row{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}.native-time-picker{position:absolute;pointer-events:none;opacity:0;width:0;height:0}@media(min-width:720px){.time-row{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(min-width:960px){.datetime-wrapper.datetime-mode{display:grid;align-items:start;gap:8px;grid-template-columns:minmax(240px,2fr) auto repeat(2,minmax(84px,96px))}.datetime-wrapper.datetime-mode.has-seconds{grid-template-columns:minmax(240px,2fr) auto repeat(3,minmax(84px,96px))}.datetime-wrapper.datetime-mode .date-field{margin-bottom:0}.datetime-wrapper.datetime-mode .middle-actions{align-self:start;padding-top:4px}.datetime-wrapper.datetime-mode .time-row{display:contents}}\n"] }]
3915
+ ], imports: [ReactiveFormsModule, MatFormField$1, MatLabel$1, MatInput, MatDatepickerInput, MatDatepickerToggle, MatDatepicker, MatSuffix, MatButton], template: "<div class=\"datetime-wrapper\" [class.datetime-mode]=\"isDateTime\" [class.has-seconds]=\"showSeconds\">\n <mat-form-field class=\"date-field\">\n @if (display) {\n <mat-label>{{ display }}</mat-label>\n }\n <input\n matInput\n [matDatepicker]=\"picker\"\n [placeholder]=\"placeHolder\"\n [formControl]=\"dateControl\"\n [disabled]=\"isDisabled\"\n (dateChange)=\"markTouched()\"\n (blur)=\"markTouched()\">\n <mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n </mat-form-field>\n\n @if (isDateTime) {\n <div class=\"middle-actions\">\n <button\n mat-icon-button\n type=\"button\"\n class=\"icon-action\"\n [disabled]=\"isDisabled\"\n title=\"Clear date and time\"\n aria-label=\"Clear date and time\"\n (click)=\"clear()\">\n <i class=\"fal fa-eraser\"></i>\n </button>\n\n <button\n mat-icon-button\n type=\"button\"\n class=\"icon-action\"\n [disabled]=\"isDisabled\"\n title=\"Open time clock\"\n aria-label=\"Open time clock\"\n (click)=\"openClock()\">\n <i class=\"fal fa-clock\"></i>\n </button>\n </div>\n\n <div class=\"time-row\">\n <mat-form-field class=\"time-field\">\n <mat-label>HH</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"23\"\n inputmode=\"numeric\"\n [formControl]=\"hourControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('hour')\"\n (blur)=\"onTimePartBlur('hour')\">\n </mat-form-field>\n\n <mat-form-field class=\"time-field\">\n <mat-label>MM</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"59\"\n inputmode=\"numeric\"\n [formControl]=\"minuteControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('minute')\"\n (blur)=\"onTimePartBlur('minute')\">\n </mat-form-field>\n\n @if (showSeconds) {\n <mat-form-field class=\"time-field\">\n <mat-label>SS</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"59\"\n inputmode=\"numeric\"\n [formControl]=\"secondControl\"\n [disabled]=\"isDisabled\"\n (input)=\"onTimePartInput('second')\"\n (blur)=\"onTimePartBlur('second')\">\n </mat-form-field>\n }\n </div>\n\n @if (isClockOpen) {\n <div class=\"clock-backdrop\" (click)=\"closeClock()\"></div>\n\n <div class=\"clock-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"clock-panel\">\n <div class=\"clock-panel-header\">\n <div class=\"clock-panel-title\">{{ clockTitle }}</div>\n <button\n mat-button\n type=\"button\"\n class=\"clock-close\"\n [disabled]=\"isDisabled\"\n (click)=\"closeClock()\">\n Close\n </button>\n </div>\n\n <div class=\"clock-summary\">\n <button mat-button type=\"button\" class=\"clock-step\" [class.active]=\"clockStep === 'hour'\" (click)=\"goToClockStep('hour')\">\n {{ hourControl.value || '00' }}\n </button>\n <span>:</span>\n <button mat-button type=\"button\" class=\"clock-step\" [class.active]=\"clockStep === 'minute'\" (click)=\"goToClockStep('minute')\">\n {{ minuteControl.value || '00' }}\n </button>\n @if (showSeconds) {\n <span>:</span>\n <button mat-button type=\"button\" class=\"clock-step\" [class.active]=\"clockStep === 'second'\" (click)=\"goToClockStep('second')\">\n {{ secondControl.value || '00' }}\n </button>\n }\n </div>\n\n <div class=\"clock-face\" [class.clickable-face]=\"clockStep !== 'hour'\" (click)=\"onClockFaceClick($event)\">\n @if (clockStep === 'hour') {\n @for (marker of hourMarkers; track marker.value) {\n <button\n mat-icon-button\n type=\"button\"\n class=\"clock-marker\"\n [class.selected]=\"isHourSelected(marker.value)\"\n [class.inner-ring]=\"marker.value >= 12\"\n [style.left]=\"marker.left\"\n [style.top]=\"marker.top\"\n (click)=\"selectClockHour(marker.value)\">\n {{ marker.label }}\n </button>\n }\n }\n\n @if (clockStep === 'minute') {\n @for (tick of minuteTicks; track tick.value) {\n <div\n class=\"clock-tick\"\n [class.selected]=\"isMinuteSelected(tick.value)\"\n [style.left]=\"tick.left\"\n [style.top]=\"tick.top\"\n [style.rotate]=\"tick.rotate\">\n </div>\n <div\n class=\"clock-tick-label\"\n [class.selected]=\"isMinuteSelected(tick.value)\"\n [style.left]=\"tick.left\"\n [style.top]=\"tick.top\">\n {{ tick.label }}\n </div>\n }\n }\n\n @if (clockStep === 'second') {\n @for (tick of secondTicks; track tick.value) {\n <div\n class=\"clock-tick\"\n [class.selected]=\"isSecondSelected(tick.value)\"\n [style.left]=\"tick.left\"\n [style.top]=\"tick.top\"\n [style.rotate]=\"tick.rotate\">\n </div>\n <div\n class=\"clock-tick-label\"\n [class.selected]=\"isSecondSelected(tick.value)\"\n [style.left]=\"tick.left\"\n [style.top]=\"tick.top\">\n {{ tick.label }}\n </div>\n }\n }\n\n <div class=\"clock-center-dot\"></div>\n </div>\n\n <div class=\"clock-help\">\n @if (clockStep === 'hour') {\n <span>Outer ring 00-11, inner ring 12-23.</span>\n }\n @if (clockStep === 'minute') {\n <span>Click the dial. The nearest 5-minute mark will be selected.</span>\n }\n @if (clockStep === 'second') {\n <span>Click the dial. The nearest 5-second mark will be selected.</span>\n }\n </div>\n\n <div class=\"clock-footer\">\n <span>{{ timeSummary }}</span>\n </div>\n </div>\n </div>\n }\n }\n</div>\n", styles: [".datetime-wrapper{display:flex;flex-direction:column;gap:8px;width:100%}.date-field,.time-field{width:100%}.middle-actions{display:flex;align-items:center;gap:4px;justify-content:flex-start}.icon-action{flex:0 0 auto}.icon-action i{font-size:1rem;line-height:1}.time-row{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}.clock-backdrop{position:fixed;inset:0;z-index:1000;background:#0000002e}.clock-dialog{position:fixed;z-index:1001;top:50%;left:50%;width:min(92vw,360px);transform:translate(-50%,-50%)}.clock-panel{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid rgba(0,0,0,.12);border-radius:16px;background:#fff;box-shadow:0 18px 36px #0003}.clock-panel-header,.clock-summary,.clock-footer{display:flex;align-items:center;justify-content:space-between;gap:8px}.clock-panel-title{font-size:.95rem;font-weight:600}.clock-summary{justify-content:center;font-size:1.1rem}.clock-step{min-width:52px;font-weight:600}.clock-step.active{background:#1976d21f}.clock-face{position:relative;align-self:center;width:min(100%,280px);aspect-ratio:1;border-radius:50%;background:radial-gradient(circle at center,rgba(25,118,210,.08) 0 16%,transparent 16% 100%),radial-gradient(circle at center,transparent 0 61%,rgba(25,118,210,.08) 61% 62%,transparent 62% 100%),radial-gradient(circle at center,#0000000a 0 100%)}.clock-face.clickable-face{cursor:crosshair}.clock-marker{position:absolute;transform:translate(-50%,-50%);width:38px;height:38px;min-width:38px;padding:0;border-radius:50%;font-size:.72rem;line-height:1}.clock-marker.inner-ring{width:34px;height:34px;min-width:34px;font-size:.68rem}.clock-unit-marker{width:24px;height:24px;min-width:24px;font-size:.58rem;color:transparent}.clock-unit-marker.major{width:34px;height:34px;min-width:34px;font-size:.68rem;color:inherit}.clock-marker.selected{background:#1976d2;color:#fff}.clock-tick{position:absolute;transform:translate(-50%,-50%);width:2px;height:16px;border-radius:999px;background:#00000057;transform-origin:center center}.clock-tick.selected{width:4px;height:22px;background:#1976d2}.clock-tick-label{position:absolute;transform:translate(-50%,-50%);font-size:.68rem;line-height:1;color:#000000b8}.clock-tick-label.selected{color:#1976d2;font-weight:700}.clock-center-dot{position:absolute;top:50%;left:50%;width:12px;height:12px;border-radius:50%;transform:translate(-50%,-50%);background:#1976d2}.clock-help,.clock-footer{font-size:.78rem;color:#000000ad}@media(min-width:720px){.time-row{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(min-width:960px){.datetime-wrapper.datetime-mode{display:grid;align-items:start;gap:8px;grid-template-columns:minmax(240px,2fr) auto repeat(2,minmax(84px,96px))}.datetime-wrapper.datetime-mode.has-seconds{grid-template-columns:minmax(240px,2fr) auto repeat(3,minmax(84px,96px))}.datetime-wrapper.datetime-mode .date-field{margin-bottom:0}.datetime-wrapper.datetime-mode .middle-actions{align-self:start;padding-top:4px}.datetime-wrapper.datetime-mode .time-row{display:contents}.datetime-wrapper.datetime-mode .clock-panel{grid-column:1/-1}}\n"] }]
3825
3916
  }], ctorParameters: () => [{ type: undefined, decorators: [{
3826
3917
  type: Optional
3827
3918
  }, {