shirkasoft-ui-components 1.3.1 → 1.3.3
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.
- package/fesm2022/shirkasoft-ui-components-date-picker.mjs +325 -2
- package/fesm2022/shirkasoft-ui-components-date-picker.mjs.map +1 -1
- package/fesm2022/shirkasoft-ui-components-table.mjs +61 -5
- package/fesm2022/shirkasoft-ui-components-table.mjs.map +1 -1
- package/fesm2022/shirkasoft-ui-components-timeline.mjs +4 -4
- package/fesm2022/shirkasoft-ui-components-timeline.mjs.map +1 -1
- package/package.json +1 -1
- package/types/shirkasoft-ui-components-date-picker.d.ts +92 -1
- package/types/shirkasoft-ui-components-table.d.ts +16 -0
- package/types/shirkasoft-ui-components-timeline.d.ts +2 -2
|
@@ -75,6 +75,16 @@ class DatePickerComponent {
|
|
|
75
75
|
this.viewMeridiem = signal('AM', ...(ngDevMode ? [{ debugName: "viewMeridiem" }] : /* istanbul ignore next */ []));
|
|
76
76
|
/** Date part selected but not yet committed until the time is confirmed. */
|
|
77
77
|
this.pendingDate = signal(null, ...(ngDevMode ? [{ debugName: "pendingDate" }] : /* istanbul ignore next */ []));
|
|
78
|
+
/** Analog clock mode: 'hours' or 'minutes'. */
|
|
79
|
+
this.clockMode = signal('hours', ...(ngDevMode ? [{ debugName: "clockMode" }] : /* istanbul ignore next */ []));
|
|
80
|
+
/** Internal view state for drill-down navigation (separates from input `view`). */
|
|
81
|
+
this.currentView = signal('date', ...(ngDevMode ? [{ debugName: "currentView" }] : /* istanbul ignore next */ []));
|
|
82
|
+
/** Whether the user is typing in the input field. */
|
|
83
|
+
this.isTyping = signal(false, ...(ngDevMode ? [{ debugName: "isTyping" }] : /* istanbul ignore next */ []));
|
|
84
|
+
/** The raw text being typed by the user. */
|
|
85
|
+
this.typedValue = signal('', ...(ngDevMode ? [{ debugName: "typedValue" }] : /* istanbul ignore next */ []));
|
|
86
|
+
/** Whether the typed value is valid. */
|
|
87
|
+
this.typingValid = signal(null, ...(ngDevMode ? [{ debugName: "typingValid" }] : /* istanbul ignore next */ []));
|
|
78
88
|
/** Underlying value (mirrors the reactive control). */
|
|
79
89
|
this._value = signal(null, ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
|
|
80
90
|
/** Disabled state coming from the reactive form infrastructure. */
|
|
@@ -274,6 +284,26 @@ class DatePickerComponent {
|
|
|
274
284
|
const start = this.viewDecade();
|
|
275
285
|
return Array.from({ length: 12 }, (_, i) => start + i);
|
|
276
286
|
}, ...(ngDevMode ? [{ debugName: "decadeYears" }] : /* istanbul ignore next */ []));
|
|
287
|
+
/** Angle (in degrees) of the analog clock hand based on current mode. */
|
|
288
|
+
this.clockHandAngle = computed(() => {
|
|
289
|
+
if (this.clockMode() === 'hours') {
|
|
290
|
+
const h = this.viewHour();
|
|
291
|
+
const display = this.hourFormat() === '12' ? h % 12 || 12 : h % 12 || 12;
|
|
292
|
+
return (display % 12) * 30;
|
|
293
|
+
}
|
|
294
|
+
return this.viewMinute() * 6;
|
|
295
|
+
}, ...(ngDevMode ? [{ debugName: "clockHandAngle" }] : /* istanbul ignore next */ []));
|
|
296
|
+
/** Hours to display on the analog clock face (1-12). */
|
|
297
|
+
this.clockHours = computed(() => Array.from({ length: 12 }, (_, i) => i + 1), ...(ngDevMode ? [{ debugName: "clockHours" }] : /* istanbul ignore next */ []));
|
|
298
|
+
/** Minutes to display on the analog clock face (0-55, step 5). */
|
|
299
|
+
this.clockMinutes = computed(() => Array.from({ length: 12 }, (_, i) => i * 5), ...(ngDevMode ? [{ debugName: "clockMinutes" }] : /* istanbul ignore next */ []));
|
|
300
|
+
/** The input format string based on locale. */
|
|
301
|
+
this.inputFormat = computed(() => {
|
|
302
|
+
const loc = this.locale();
|
|
303
|
+
return loc === 'en' ? 'MM/DD/YYYY' : 'DD/MM/YYYY';
|
|
304
|
+
}, ...(ngDevMode ? [{ debugName: "inputFormat" }] : /* istanbul ignore next */ []));
|
|
305
|
+
/** Whether the drill-down navigation is active (view differs from input). */
|
|
306
|
+
this.isDrilledDown = computed(() => this.currentView() !== this.view(), ...(ngDevMode ? [{ debugName: "isDrilledDown" }] : /* istanbul ignore next */ []));
|
|
277
307
|
/** (Unused legacy placeholder) plain year list. */
|
|
278
308
|
this.years = [];
|
|
279
309
|
// Attach error-state subscriptions to the validated control once found.
|
|
@@ -333,6 +363,9 @@ class DatePickerComponent {
|
|
|
333
363
|
}
|
|
334
364
|
else {
|
|
335
365
|
this.syncViewToValue();
|
|
366
|
+
this.currentView.set(this.view());
|
|
367
|
+
this.clockMode.set('hours');
|
|
368
|
+
this.isTyping.set(false);
|
|
336
369
|
this.isOpen.set(true);
|
|
337
370
|
}
|
|
338
371
|
}
|
|
@@ -569,6 +602,296 @@ class DatePickerComponent {
|
|
|
569
602
|
minutesLabel(value) {
|
|
570
603
|
return String(value).padStart(2, '0');
|
|
571
604
|
}
|
|
605
|
+
/** Returns the position (x, y) for a clock number at the given index (0-11). */
|
|
606
|
+
getClockNumberPosition(index) {
|
|
607
|
+
const angle = (index * 30 - 90) * (Math.PI / 180);
|
|
608
|
+
const radius = 76;
|
|
609
|
+
const x = 100 + radius * Math.cos(angle);
|
|
610
|
+
const y = 100 + radius * Math.sin(angle);
|
|
611
|
+
return { x, y };
|
|
612
|
+
}
|
|
613
|
+
/** Returns the position (x, y) for a minute marker at the given index (0-59). */
|
|
614
|
+
getMinuteMarkerPosition(index) {
|
|
615
|
+
const angle = (index * 6 - 90) * (Math.PI / 180);
|
|
616
|
+
const radius = 88;
|
|
617
|
+
const x = 100 + radius * Math.cos(angle);
|
|
618
|
+
const y = 100 + radius * Math.sin(angle);
|
|
619
|
+
return { x, y, rotation: index * 6 };
|
|
620
|
+
}
|
|
621
|
+
/** Handles click on the analog clock face. */
|
|
622
|
+
onClockClick(event, mode) {
|
|
623
|
+
const target = event.currentTarget;
|
|
624
|
+
const rect = target.getBoundingClientRect();
|
|
625
|
+
const centerX = rect.left + rect.width / 2;
|
|
626
|
+
const centerY = rect.top + rect.height / 2;
|
|
627
|
+
const dx = event.clientX - centerX;
|
|
628
|
+
const dy = event.clientY - centerY;
|
|
629
|
+
let angle = Math.atan2(dy, dx) * (180 / Math.PI) + 90;
|
|
630
|
+
if (angle < 0)
|
|
631
|
+
angle += 360;
|
|
632
|
+
if (mode === 'hours') {
|
|
633
|
+
let hour = Math.round(angle / 30) % 12;
|
|
634
|
+
if (hour === 0)
|
|
635
|
+
hour = 12;
|
|
636
|
+
let actualHour = hour;
|
|
637
|
+
if (this.hourFormat() === '12') {
|
|
638
|
+
if (this.viewMeridiem() === 'PM' && hour !== 12) {
|
|
639
|
+
actualHour = hour + 12;
|
|
640
|
+
}
|
|
641
|
+
else if (this.viewMeridiem() === 'AM' && hour === 12) {
|
|
642
|
+
actualHour = 0;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
actualHour = hour === 12 ? 12 : hour;
|
|
647
|
+
if (this.viewHour() >= 12 && hour !== 12) {
|
|
648
|
+
actualHour = hour + 12;
|
|
649
|
+
}
|
|
650
|
+
else if (this.viewHour() < 12 && hour === 12) {
|
|
651
|
+
actualHour = 12;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
this.viewHour.set(actualHour);
|
|
655
|
+
this.clockMode.set('minutes');
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
const minute = Math.round(angle / 6) % 60;
|
|
659
|
+
const stepped = Math.round(minute / this.minuteStep()) * this.minuteStep();
|
|
660
|
+
this.viewMinute.set(stepped % 60);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
/** Switches the clock mode between hours and minutes. */
|
|
664
|
+
toggleClockMode() {
|
|
665
|
+
this.clockMode.update((m) => (m === 'hours' ? 'minutes' : 'hours'));
|
|
666
|
+
}
|
|
667
|
+
/** Whether the given hour number matches the current selection on the clock face. */
|
|
668
|
+
isClockHourSelected(hour12) {
|
|
669
|
+
const h = this.viewHour();
|
|
670
|
+
if (this.hourFormat() === '12') {
|
|
671
|
+
const display = h % 12 || 12;
|
|
672
|
+
return display === hour12;
|
|
673
|
+
}
|
|
674
|
+
const display = h % 12 || 12;
|
|
675
|
+
return display === hour12;
|
|
676
|
+
}
|
|
677
|
+
/** Whether the given minute value matches the current selection. */
|
|
678
|
+
isClockMinuteSelected(minute) {
|
|
679
|
+
return this.viewMinute() === minute;
|
|
680
|
+
}
|
|
681
|
+
/** Formats the clock display time for the header above the clock. */
|
|
682
|
+
clockDisplayTime() {
|
|
683
|
+
return this.formatTime(this.viewHour(), this.viewMinute(), this.viewSecond());
|
|
684
|
+
}
|
|
685
|
+
/** Returns SVG line coordinates for a clock hand from center to angle. */
|
|
686
|
+
clockHandLine(length = 60) {
|
|
687
|
+
const angle = this.clockHandAngle();
|
|
688
|
+
// clockHandAngle: 0=12h, 90=3h, 180=6h, 270=9h
|
|
689
|
+
// SVG cos/sin: 0=3h, 90=6h, 180=9h, 270=12h → offset -90°
|
|
690
|
+
const rad = ((angle - 90) * Math.PI) / 180;
|
|
691
|
+
return {
|
|
692
|
+
x2: 100 + length * Math.cos(rad),
|
|
693
|
+
y2: 100 + length * Math.sin(rad),
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
/** Returns SVG text position for a clock number at the given index (0-11). */
|
|
697
|
+
clockNumberPos(index) {
|
|
698
|
+
const angle = ((index % 12) * 30 - 90) * (Math.PI / 180);
|
|
699
|
+
return {
|
|
700
|
+
x: 100 + 68 * Math.cos(angle),
|
|
701
|
+
y: 100 + 68 * Math.sin(angle),
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
/** Returns SVG text position for a minute label at the given minute (0-55). */
|
|
705
|
+
clockMinutePos(minute) {
|
|
706
|
+
const angle = (minute * 6 - 90) * (Math.PI / 180);
|
|
707
|
+
return {
|
|
708
|
+
x: 100 + 68 * Math.cos(angle),
|
|
709
|
+
y: 100 + 68 * Math.sin(angle),
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
/** Returns SVG line coordinates for a minute marker. */
|
|
713
|
+
clockMinuteMarker(i) {
|
|
714
|
+
const angle = (i * 6 - 90) * (Math.PI / 180);
|
|
715
|
+
const outerR = 84;
|
|
716
|
+
const innerR = i % 5 === 0 ? 78 : 82;
|
|
717
|
+
return {
|
|
718
|
+
x1: 100 + outerR * Math.cos(angle),
|
|
719
|
+
y1: 100 + outerR * Math.sin(angle),
|
|
720
|
+
x2: 100 + innerR * Math.cos(angle),
|
|
721
|
+
y2: 100 + innerR * Math.sin(angle),
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
/** Switches the drill-down view to months (from date view). */
|
|
725
|
+
drillToMonths() {
|
|
726
|
+
this.currentView.set('month');
|
|
727
|
+
}
|
|
728
|
+
/** Switches the drill-down view to years (from month view). */
|
|
729
|
+
drillToYears() {
|
|
730
|
+
this.currentView.set('year');
|
|
731
|
+
}
|
|
732
|
+
/** Drills down from year view to month view for the selected year. */
|
|
733
|
+
drillDownToMonth(year) {
|
|
734
|
+
this.viewYear.set(year);
|
|
735
|
+
this.currentView.set('month');
|
|
736
|
+
}
|
|
737
|
+
/** Drills down from month view to date view for the selected month. */
|
|
738
|
+
drillDownToDate(monthIndex) {
|
|
739
|
+
this.viewMonth.set(monthIndex);
|
|
740
|
+
this.currentView.set('date');
|
|
741
|
+
}
|
|
742
|
+
/** Goes back one level in drill-down navigation. */
|
|
743
|
+
drillUp() {
|
|
744
|
+
const v = this.currentView();
|
|
745
|
+
if (v === 'date') {
|
|
746
|
+
this.currentView.set('month');
|
|
747
|
+
}
|
|
748
|
+
else if (v === 'month') {
|
|
749
|
+
this.currentView.set('year');
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
/** Returns to the initial view from any drill-down level. */
|
|
753
|
+
drillToInitial() {
|
|
754
|
+
this.currentView.set(this.view());
|
|
755
|
+
}
|
|
756
|
+
/** Formats input placeholder based on locale. */
|
|
757
|
+
inputPlaceholder() {
|
|
758
|
+
if (this.timeOnly())
|
|
759
|
+
return this.inputFormat();
|
|
760
|
+
if (this.view() === 'year')
|
|
761
|
+
return 'YYYY';
|
|
762
|
+
if (this.view() === 'month')
|
|
763
|
+
return 'MM/YYYY';
|
|
764
|
+
return this.inputFormat();
|
|
765
|
+
}
|
|
766
|
+
/** Handles text input in the date field. */
|
|
767
|
+
onInputText(event) {
|
|
768
|
+
const value = event.target.value;
|
|
769
|
+
this.typedValue.set(value);
|
|
770
|
+
this.typingValid.set(null);
|
|
771
|
+
}
|
|
772
|
+
/** Handles focus on the input field. */
|
|
773
|
+
onInputFocus() {
|
|
774
|
+
this.isTyping.set(true);
|
|
775
|
+
const val = this._value();
|
|
776
|
+
if (val) {
|
|
777
|
+
if (this.timeOnly()) {
|
|
778
|
+
this.typedValue.set(val);
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
this.typedValue.set(val);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
else {
|
|
785
|
+
this.typedValue.set('');
|
|
786
|
+
}
|
|
787
|
+
this.typingValid.set(null);
|
|
788
|
+
}
|
|
789
|
+
/** Validates and commits the typed value on blur or Enter. */
|
|
790
|
+
onInputBlur() {
|
|
791
|
+
const text = this.typedValue().trim();
|
|
792
|
+
if (!text) {
|
|
793
|
+
this.isTyping.set(false);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
const parsed = this.parseTypedDate(text);
|
|
797
|
+
if (parsed) {
|
|
798
|
+
this.setValue(parsed);
|
|
799
|
+
this.typingValid.set(true);
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
this.typingValid.set(false);
|
|
803
|
+
setTimeout(() => this.typingValid.set(null), 1500);
|
|
804
|
+
}
|
|
805
|
+
this.isTyping.set(false);
|
|
806
|
+
}
|
|
807
|
+
/** Cancels typing and reverts to the previous value. */
|
|
808
|
+
cancelTyping() {
|
|
809
|
+
this.isTyping.set(false);
|
|
810
|
+
this.typedValue.set('');
|
|
811
|
+
this.typingValid.set(null);
|
|
812
|
+
}
|
|
813
|
+
/** Parses a typed date string according to the locale format. */
|
|
814
|
+
parseTypedDate(text) {
|
|
815
|
+
const clean = text.replace(/[\/\-\.\s]/g, '/').trim();
|
|
816
|
+
const parts = clean.split('/').filter(Boolean);
|
|
817
|
+
if (this.timeOnly()) {
|
|
818
|
+
return this.parseTimeOnly(clean);
|
|
819
|
+
}
|
|
820
|
+
if (this.view() === 'year') {
|
|
821
|
+
const y = Number(parts[0]);
|
|
822
|
+
if (parts.length === 1 && y >= 1900 && y <= 2100)
|
|
823
|
+
return String(y);
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
if (this.view() === 'month') {
|
|
827
|
+
if (parts.length === 2) {
|
|
828
|
+
let m, y;
|
|
829
|
+
if (this.locale() === 'en') {
|
|
830
|
+
m = Number(parts[0]);
|
|
831
|
+
y = Number(parts[1]);
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
m = Number(parts[0]);
|
|
835
|
+
y = Number(parts[1]);
|
|
836
|
+
}
|
|
837
|
+
if (m >= 1 && m <= 12 && y >= 1900 && y <= 2100) {
|
|
838
|
+
return `${y}-${String(m).padStart(2, '0')}`;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
// date view
|
|
844
|
+
if (parts.length >= 3) {
|
|
845
|
+
let d, m, y;
|
|
846
|
+
if (this.locale() === 'en') {
|
|
847
|
+
m = Number(parts[0]);
|
|
848
|
+
d = Number(parts[1]);
|
|
849
|
+
y = Number(parts[2]);
|
|
850
|
+
}
|
|
851
|
+
else {
|
|
852
|
+
d = Number(parts[0]);
|
|
853
|
+
m = Number(parts[1]);
|
|
854
|
+
y = Number(parts[2]);
|
|
855
|
+
}
|
|
856
|
+
if (y >= 1900 && y <= 2100 && m >= 1 && m <= 12 && d >= 1 && d <= 31) {
|
|
857
|
+
const date = new Date(y, m - 1, d);
|
|
858
|
+
if (date.getMonth() === m - 1 && date.getDate() === d) {
|
|
859
|
+
let result = `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
|
|
860
|
+
if (this.showTime()) {
|
|
861
|
+
const time = `${String(this.viewHour()).padStart(2, '0')}:${String(this.viewMinute()).padStart(2, '0')}`;
|
|
862
|
+
result += `T${time}`;
|
|
863
|
+
}
|
|
864
|
+
return result;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
/** Parses time-only input. */
|
|
871
|
+
parseTimeOnly(text) {
|
|
872
|
+
const clean = text.replace(/[\.]/g, ':').trim();
|
|
873
|
+
const parts = clean.split(':').filter(Boolean);
|
|
874
|
+
if (parts.length >= 2) {
|
|
875
|
+
let h = Number(parts[0]);
|
|
876
|
+
const m = Number(parts[1]);
|
|
877
|
+
const s = parts.length >= 3 ? Number(parts[2]) : 0;
|
|
878
|
+
if (this.hourFormat() === '12') {
|
|
879
|
+
const isPM = /pm/i.test(text);
|
|
880
|
+
const isAM = /am/i.test(text);
|
|
881
|
+
if (isPM && h < 12)
|
|
882
|
+
h += 12;
|
|
883
|
+
if (isAM && h === 12)
|
|
884
|
+
h = 0;
|
|
885
|
+
}
|
|
886
|
+
if (h >= 0 && h <= 23 && m >= 0 && m <= 59 && s >= 0 && s <= 59) {
|
|
887
|
+
let result = `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
|
|
888
|
+
if (this.showSeconds())
|
|
889
|
+
result += `:${String(s).padStart(2, '0')}`;
|
|
890
|
+
return result;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
return null;
|
|
894
|
+
}
|
|
572
895
|
/** Whether the given day is today's date. */
|
|
573
896
|
isToday(day) {
|
|
574
897
|
const now = new Date();
|
|
@@ -611,7 +934,7 @@ class DatePickerComponent {
|
|
|
611
934
|
useExisting: forwardRef(() => DatePickerComponent),
|
|
612
935
|
multi: true,
|
|
613
936
|
},
|
|
614
|
-
], ngImport: i0, template: "<div class=\"relative mb-4\">\n @if (label()) {\n <div class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] mb-1\">\n {{ label() }}\n </div>\n }\n\n <div class=\"relative\">\n <button\n type=\"button\"\n [id]=\"id()\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggle()\"\n class=\"flex items-center justify-between w-full px-3 py-2 text-left _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border rounded-lg transition-colors sm:text-sm focus:outline-none focus:ring-1 disabled:cursor-not-allowed disabled:[color:var(--shk-surface-400)] dark:disabled:[color:var(--shk-surface-500)]\"\n [ngClass]=\"{\n 'border-red-500 focus:border-red-500 focus:ring-red-500': shouldShowError(),\n 'border-green-500 focus:border-green-500 focus:ring-green-500': wasFixedError(),\n '_shk-border dark:[border-color:var(--shk-surface-200)] focus:[border-color:var(--shk-primary-500)] focus:[--tw-ring-color:var(--shk-primary-500)]': !shouldShowError() && !wasFixedError(),\n }\"\n >\n <span\n [ngClass]=\"value() ? '_shk-text-surface-700 dark:[color:var(--shk-surface-700)]' : '_shk-text-surface-400 dark:[color:var(--shk-surface-400)]'\"\n >\n {{ displayLabel() || placeholder() || label() }}\n </span>\n <svg\n class=\"w-4 h-4 _shk-text-surface-400 dark:[color:var(--shk-surface-400)] shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"></rect>\n <path d=\"M16 2v4M8 2v4M3 10h18\"></path>\n </svg>\n </button>\n\n @if (isOpen()) {\n <div class=\"fixed inset-0 z-40 bg-black/30\" (click)=\"close()\"></div>\n <div\n class=\"fixed z-50 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-72 p-4 bg-white dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-lg shadow-xl\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (!timeOnly() && view() === 'date') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ monthNames()[viewMonth()] }} {{ viewYear() }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0 mb-1\">\n @for (name of dayNames(); track $index) {\n <div class=\"text-center text-xs font-medium _shk-text-surface-400 dark:[color:var(--shk-surface-400)] py-1\">\n {{ name }}\n </div>\n }\n </div>\n\n <div class=\"grid grid-cols-7 gap-0\">\n @for (day of calendarDays(); track $index) {\n @if (day !== null) {\n <button\n type=\"button\"\n (click)=\"selectDay(day)\"\n class=\"py-1.5 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedDay(day),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isToday(day) && !isSelectedDay(day),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedDay(day) && !isToday(day),\n }\"\n >\n {{ day }}\n </button>\n } @else {\n <div></div>\n }\n }\n </div>\n }\n\n @if (!timeOnly() && view() === 'month') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">{{ viewYear() }}</span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (name of monthNames(); track $index) {\n <button\n type=\"button\"\n (click)=\"selectMonth($index)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedMonth($index),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentMonth($index) && !isSelectedMonth($index),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedMonth($index) && !isCurrentMonth($index),\n }\"\n >\n {{ name }}\n </button>\n }\n </div>\n }\n\n @if (!timeOnly() && view() === 'year') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ viewDecade() }} - {{ viewDecade() + 11 }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (year of decadeYears(); track year) {\n <button\n type=\"button\"\n (click)=\"selectYear(year)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedYear(year),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentYear(year) && !isSelectedYear(year),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedYear(year) && !isCurrentYear(year),\n }\"\n >\n {{ year }}\n </button>\n }\n </div>\n }\n\n @if (timeOnly() || showTime()) {\n <div class=\"mt-3 pt-3 _shk-border dark:[border-color:var(--shk-surface-200)]\">\n <div class=\"flex items-center justify-center gap-1\">\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewHour()\"\n (change)=\"onHourChange($event)\"\n aria-label=\"Hora\"\n >\n @for (h of hoursRange(); track h) {\n <option [value]=\"h\">{{ hourDisplay(h) }}</option>\n }\n </select>\n <span class=\"_shk-text-surface-400\">:</span>\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewMinute()\"\n (change)=\"onMinuteChange($event)\"\n aria-label=\"Minutos\"\n >\n @for (minutes of minutesRange(); track minutes) {\n <option [value]=\"minutes\">{{ minutesLabel(minutes) }}</option>\n }\n </select>\n @if (showSeconds()) {\n <span class=\"text-sm _shk-text-surface-400\">:</span>\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewSecond()\"\n (change)=\"onSecondChange($event)\"\n aria-label=\"Segundos\"\n >\n @for (seconds of secondsRange(); track seconds) {\n <option [value]=\"seconds\">{{ minutesLabel(seconds) }}</option>\n }\n </select>\n }\n @if (hourFormat() === '12') {\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"meridiem()\"\n (change)=\"onMeridiemChange($event)\"\n aria-label=\"AM/PM\"\n >\n <option value=\"AM\">AM</option>\n <option value=\"PM\">PM</option>\n </select>\n }\n </div>\n <button\n type=\"button\"\n (click)=\"confirmTime()\"\n class=\"mt-3 w-full py-2 text-sm _shk-bg-primary _shk-text-white rounded-lg hover:[background-color:var(--shk-primary-700)] transition-colors\"\n >\n {{ timeOnly() ? 'Confirmar' : 'Introducir hora' }}\n </button>\n </div>\n }\n </div>\n }\n </div>\n\n @if (shouldShowError()) {\n <div class=\"mt-1 text-xs text-red-500 max-h-20 overflow-y-auto _shk-scrollbar\">\n <ul class=\"list-disc pl-4 space-y-1\">\n @for (error of getErrorMessages(); track error) {\n <li>{{ error }}</li>\n }\n </ul>\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
937
|
+
], ngImport: i0, template: "<div class=\"relative mb-4\">\n @if (label()) {\n <div class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] mb-1\">\n {{ label() }}\n </div>\n }\n\n <div class=\"relative\">\n <div class=\"relative\">\n <input\n [id]=\"id()\"\n type=\"text\"\n [disabled]=\"isDisabled()\"\n [placeholder]=\"inputPlaceholder()\"\n [value]=\"isTyping() ? typedValue() : displayLabel()\"\n (input)=\"onInputText($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown.enter)=\"$any($event.target).blur()\"\n (keydown.escape)=\"cancelTyping()\"\n (click)=\"toggle()\"\n class=\"date-input-field\"\n [ngClass]=\"{\n 'input-error': shouldShowError() || typingValid() === false,\n 'input-valid': wasFixedError() || typingValid() === true,\n }\"\n [attr.aria-invalid]=\"shouldShowError()\"\n [attr.aria-describedby]=\"shouldShowError() ? id() + '-error' : null\"\n />\n <button\n type=\"button\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggle()\"\n class=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 _shk-text-surface-400 dark:[color:var(--shk-surface-400)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)] transition-colors\"\n >\n <svg\n class=\"w-4 h-4\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"></rect>\n <path d=\"M16 2v4M8 2v4M3 10h18\"></path>\n </svg>\n </button>\n </div>\n\n @if (isOpen()) {\n <div class=\"fixed inset-0 z-40 bg-black/30\" (click)=\"close()\"></div>\n <div\n class=\"fixed z-50 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-72 p-4 bg-white dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-lg shadow-xl\"\n (click)=\"$event.stopPropagation()\"\n >\n <!-- DATE VIEW -->\n @if (!timeOnly() && currentView() === 'date') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <button\n type=\"button\"\n (click)=\"drillToMonths()\"\n class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] hover:_shk-text-primary cursor-pointer hover:underline transition-colors\"\n >\n {{ monthNames()[viewMonth()] }} {{ viewYear() }}\n </button>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0 mb-1\">\n @for (name of dayNames(); track $index) {\n <div class=\"text-center text-xs font-medium _shk-text-surface-400 dark:[color:var(--shk-surface-400)] py-1\">\n {{ name }}\n </div>\n }\n </div>\n\n <div class=\"grid grid-cols-7 gap-0\">\n @for (day of calendarDays(); track $index) {\n @if (day !== null) {\n <button\n type=\"button\"\n (click)=\"selectDay(day)\"\n class=\"py-1.5 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedDay(day),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isToday(day) && !isSelectedDay(day),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedDay(day) && !isToday(day),\n }\"\n >\n {{ day }}\n </button>\n } @else {\n <div></div>\n }\n }\n </div>\n }\n\n <!-- MONTH VIEW -->\n @if (!timeOnly() && currentView() === 'month') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <button\n type=\"button\"\n (click)=\"drillToYears()\"\n class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] hover:_shk-text-primary cursor-pointer hover:underline transition-colors\"\n >\n {{ viewYear() }}\n </button>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (name of monthNames(); track $index) {\n <button\n type=\"button\"\n (click)=\"drillDownToDate($index)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedMonth($index),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentMonth($index) && !isSelectedMonth($index),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedMonth($index) && !isCurrentMonth($index),\n }\"\n >\n {{ name }}\n </button>\n }\n </div>\n }\n\n <!-- YEAR VIEW -->\n @if (!timeOnly() && currentView() === 'year') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ viewDecade() }} - {{ viewDecade() + 11 }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (year of decadeYears(); track year) {\n <button\n type=\"button\"\n (click)=\"drillDownToMonth(year)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedYear(year),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentYear(year) && !isSelectedYear(year),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedYear(year) && !isCurrentYear(year),\n }\"\n >\n {{ year }}\n </button>\n }\n </div>\n }\n\n <!-- ANALOG CLOCK TIME PICKER -->\n @if (timeOnly() || showTime()) {\n <div class=\"mt-3 pt-3 _shk-border dark:[border-color:var(--shk-surface-200)]\">\n <!-- Clock display time -->\n <div class=\"text-center mb-2\">\n <span class=\"text-lg font-semibold _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\">\n {{ clockDisplayTime() }}\n </span>\n </div>\n\n <!-- Analog clock face -->\n <div class=\"clock-container\">\n <svg\n viewBox=\"0 0 200 200\"\n class=\"w-full h-full cursor-pointer\"\n (click)=\"onClockClick($event, clockMode())\"\n >\n <!-- Clock face background -->\n <circle cx=\"100\" cy=\"100\" r=\"96\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1\"\n class=\"_shk-text-surface-200 dark:[color:var(--shk-surface-200)]\" />\n\n <!-- Minute markers -->\n @if (clockMode() === 'minutes') {\n @for (i of [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59]; track i) {\n <line\n [attr.x1]=\"clockMinuteMarker(i).x1\"\n [attr.y1]=\"clockMinuteMarker(i).y1\"\n [attr.x2]=\"clockMinuteMarker(i).x2\"\n [attr.y2]=\"clockMinuteMarker(i).y2\"\n [attr.stroke-width]=\"i % 5 === 0 ? 2 : 1\"\n [class]=\"i % 5 === 0 ? '_shk-text-surface-500 dark:[color:var(--shk-surface-500)]' : '_shk-text-surface-300 dark:[color:var(--shk-surface-400)]'\"\n stroke=\"currentColor\"\n />\n }\n <!-- Minute numbers (00, 05, 10, ...) -->\n @for (m of [0,5,10,15,20,25,30,35,40,45,50,55]; track m) {\n <text\n [attr.x]=\"clockMinutePos(m).x\"\n [attr.y]=\"clockMinutePos(m).y\"\n text-anchor=\"middle\"\n dominant-baseline=\"central\"\n class=\"text-[9px] _shk-text-surface-500 dark:[color:var(--shk-surface-400)]\"\n [class._shk-text-primary]=\"viewMinute() === m\"\n >\n {{ m.toString().padStart(2, '0') }}\n </text>\n }\n }\n\n <!-- Hour numbers -->\n @if (clockMode() === 'hours') {\n @for (h of clockHours(); track h) {\n <text\n [attr.x]=\"clockNumberPos(h).x\"\n [attr.y]=\"clockNumberPos(h).y\"\n text-anchor=\"middle\"\n dominant-baseline=\"central\"\n class=\"text-sm font-medium\"\n [class._shk-text-primary]=\"isClockHourSelected(h)\"\n [class._shk-text-surface-700]=\"!isClockHourSelected(h)\"\n >\n {{ h }}\n </text>\n }\n }\n\n <!-- Clock hand -->\n <line\n x1=\"100\" y1=\"100\"\n [attr.x2]=\"clockHandLine(60).x2\"\n [attr.y2]=\"clockHandLine(60).y2\"\n class=\"_shk-text-primary\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n\n <!-- Center dot -->\n <circle cx=\"100\" cy=\"100\" r=\"4\" class=\"fill-current _shk-text-primary\" />\n </svg>\n </div>\n\n <!-- Mode toggle: Hours / Minutes -->\n <div class=\"clock-mode-toggle\">\n <button\n type=\"button\"\n (click)=\"clockMode.set('hours')\"\n class=\"clock-mode-btn\"\n [class.active]=\"clockMode() === 'hours'\"\n >\n {{ 'Horas' }}\n </button>\n <button\n type=\"button\"\n (click)=\"clockMode.set('minutes')\"\n class=\"clock-mode-btn\"\n [class.active]=\"clockMode() === 'minutes'\"\n >\n {{ 'Minutos' }}\n </button>\n </div>\n\n <!-- AM/PM toggle for 12h format -->\n @if (hourFormat() === '12') {\n <div class=\"flex items-center justify-center gap-2 mt-2\">\n <button\n type=\"button\"\n (click)=\"viewMeridiem.set('AM')\"\n class=\"px-3 py-1 text-xs font-medium rounded-full transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white': viewMeridiem() === 'AM',\n '_shk-text-surface-500 hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': viewMeridiem() !== 'AM'\n }\"\n >\n AM\n </button>\n <button\n type=\"button\"\n (click)=\"viewMeridiem.set('PM')\"\n class=\"px-3 py-1 text-xs font-medium rounded-full transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white': viewMeridiem() === 'PM',\n '_shk-text-surface-500 hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': viewMeridiem() !== 'PM'\n }\"\n >\n PM\n </button>\n </div>\n }\n\n <!-- Seconds selector (if enabled) -->\n @if (showSeconds()) {\n <div class=\"flex items-center justify-center gap-1 mt-2\">\n <span class=\"text-xs _shk-text-surface-400\">Seg:</span>\n <select\n class=\"px-2 py-1 text-xs _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewSecond()\"\n (change)=\"onSecondChange($event)\"\n aria-label=\"Segundos\"\n >\n @for (seconds of secondsRange(); track seconds) {\n <option [value]=\"seconds\">{{ minutesLabel(seconds) }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Confirm button -->\n <button\n type=\"button\"\n (click)=\"confirmTime()\"\n class=\"mt-3 w-full py-2 text-sm _shk-bg-primary _shk-text-white rounded-lg hover:[background-color:var(--shk-primary-700)] transition-colors\"\n >\n {{ timeOnly() ? 'Confirmar' : 'Introducir hora' }}\n </button>\n </div>\n }\n </div>\n }\n </div>\n\n @if (shouldShowError()) {\n <div [id]=\"id() + '-error'\" class=\"mt-1 text-xs text-red-500 max-h-20 overflow-y-auto _shk-scrollbar\">\n <ul class=\"list-disc pl-4 space-y-1\">\n @for (error of getErrorMessages(); track error) {\n <li>{{ error }}</li>\n }\n </ul>\n </div>\n }\n</div>\n", styles: [".clock-container{position:relative;width:200px;height:200px;margin:0 auto}.clock-face{width:100%;height:100%;border-radius:50%;border:2px solid var(--shk-surface-200);position:relative;background-color:var(--shk-surface-50)}.dark .clock-face{border-color:var(--shk-surface-200);background-color:var(--shk-surface-100)}.clock-number{position:absolute;width:32px;height:32px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:500;border-radius:50%;transform:translate(-50%,-50%);cursor:pointer;transition:background-color .15s,color .15s;-webkit-user-select:none;user-select:none;color:var(--shk-surface-700)}.dark .clock-number{color:var(--shk-surface-700)}.clock-number:hover{background-color:var(--shk-surface-100)}.dark .clock-number:hover{background-color:var(--shk-surface-200)}.clock-number.selected{background-color:var(--shk-primary-500);color:#fff}.clock-number.current{font-weight:700}.clock-hand{position:absolute;bottom:50%;left:50%;width:2px;background-color:var(--shk-primary-500);transform-origin:bottom center;border-radius:1px;pointer-events:none;transition:transform .2s ease-out}.clock-center{position:absolute;top:50%;left:50%;width:8px;height:8px;border-radius:50%;background-color:var(--shk-primary-500);transform:translate(-50%,-50%);pointer-events:none;z-index:2}.clock-minute-marker{position:absolute;width:1px;height:8px;background-color:var(--shk-surface-300);top:6px;left:50%;transform-origin:center 94px}.dark .clock-minute-marker{background-color:var(--shk-surface-400)}.clock-minute-marker.major{width:2px;height:12px;background-color:var(--shk-surface-400)}.dark .clock-minute-marker.major{background-color:var(--shk-surface-500)}.clock-mode-toggle{display:flex;align-items:center;justify-content:center;gap:.5rem;margin-top:.75rem}.clock-mode-btn{padding:.25rem .75rem;font-size:.75rem;font-weight:500;border-radius:9999px;cursor:pointer;transition:background-color .15s,color .15s;border:none;background:transparent;color:var(--shk-surface-500)}.dark .clock-mode-btn{color:var(--shk-surface-400)}.clock-mode-btn.active{background-color:var(--shk-primary-500);color:#fff}.clock-mode-btn:hover:not(.active){background-color:var(--shk-surface-100)}.dark .clock-mode-btn:hover:not(.active){background-color:var(--shk-surface-200)}.date-input-field{width:100%;padding:.5rem 2.5rem .5rem .75rem;font-size:.875rem;border-radius:.5rem;outline:none;transition:border-color .15s,box-shadow .15s;background-color:var(--shk-surface-50);color:var(--shk-surface-700);border:1px solid var(--shk-surface-200)}.dark .date-input-field{background-color:var(--shk-surface-100);color:var(--shk-surface-700);border-color:var(--shk-surface-200)}.date-input-field::placeholder{color:var(--shk-surface-400)}.dark .date-input-field::placeholder{color:var(--shk-surface-400)}.date-input-field:focus{border-color:var(--shk-primary-500);box-shadow:0 0 0 1px var(--shk-primary-500)}.date-input-field:disabled{opacity:.5;cursor:not-allowed}.date-input-field.input-error{border-color:#ef4444}.date-input-field.input-error:focus{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.date-input-field.input-valid{border-color:#22c55e}.date-input-field.input-valid:focus{border-color:#22c55e;box-shadow:0 0 0 1px #22c55e}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
615
938
|
}
|
|
616
939
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: DatePickerComponent, decorators: [{
|
|
617
940
|
type: Component,
|
|
@@ -621,7 +944,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
621
944
|
useExisting: forwardRef(() => DatePickerComponent),
|
|
622
945
|
multi: true,
|
|
623
946
|
},
|
|
624
|
-
], template: "<div class=\"relative mb-4\">\n @if (label()) {\n <div class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] mb-1\">\n {{ label() }}\n </div>\n }\n\n <div class=\"relative\">\n <button\n type=\"button\"\n [id]=\"id()\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggle()\"\n class=\"flex items-center justify-between w-full px-3 py-2 text-left _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border rounded-lg transition-colors sm:text-sm focus:outline-none focus:ring-1 disabled:cursor-not-allowed disabled:[color:var(--shk-surface-400)] dark:disabled:[color:var(--shk-surface-500)]\"\n [ngClass]=\"{\n 'border-red-500 focus:border-red-500 focus:ring-red-500': shouldShowError(),\n 'border-green-500 focus:border-green-500 focus:ring-green-500': wasFixedError(),\n '_shk-border dark:[border-color:var(--shk-surface-200)] focus:[border-color:var(--shk-primary-500)] focus:[--tw-ring-color:var(--shk-primary-500)]': !shouldShowError() && !wasFixedError(),\n }\"\n >\n <span\n [ngClass]=\"value() ? '_shk-text-surface-700 dark:[color:var(--shk-surface-700)]' : '_shk-text-surface-400 dark:[color:var(--shk-surface-400)]'\"\n >\n {{ displayLabel() || placeholder() || label() }}\n </span>\n <svg\n class=\"w-4 h-4 _shk-text-surface-400 dark:[color:var(--shk-surface-400)] shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"></rect>\n <path d=\"M16 2v4M8 2v4M3 10h18\"></path>\n </svg>\n </button>\n\n @if (isOpen()) {\n <div class=\"fixed inset-0 z-40 bg-black/30\" (click)=\"close()\"></div>\n <div\n class=\"fixed z-50 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-72 p-4 bg-white dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-lg shadow-xl\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (!timeOnly() && view() === 'date') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ monthNames()[viewMonth()] }} {{ viewYear() }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0 mb-1\">\n @for (name of dayNames(); track $index) {\n <div class=\"text-center text-xs font-medium _shk-text-surface-400 dark:[color:var(--shk-surface-400)] py-1\">\n {{ name }}\n </div>\n }\n </div>\n\n <div class=\"grid grid-cols-7 gap-0\">\n @for (day of calendarDays(); track $index) {\n @if (day !== null) {\n <button\n type=\"button\"\n (click)=\"selectDay(day)\"\n class=\"py-1.5 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedDay(day),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isToday(day) && !isSelectedDay(day),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedDay(day) && !isToday(day),\n }\"\n >\n {{ day }}\n </button>\n } @else {\n <div></div>\n }\n }\n </div>\n }\n\n @if (!timeOnly() && view() === 'month') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">{{ viewYear() }}</span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (name of monthNames(); track $index) {\n <button\n type=\"button\"\n (click)=\"selectMonth($index)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedMonth($index),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentMonth($index) && !isSelectedMonth($index),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedMonth($index) && !isCurrentMonth($index),\n }\"\n >\n {{ name }}\n </button>\n }\n </div>\n }\n\n @if (!timeOnly() && view() === 'year') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ viewDecade() }} - {{ viewDecade() + 11 }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (year of decadeYears(); track year) {\n <button\n type=\"button\"\n (click)=\"selectYear(year)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedYear(year),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentYear(year) && !isSelectedYear(year),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedYear(year) && !isCurrentYear(year),\n }\"\n >\n {{ year }}\n </button>\n }\n </div>\n }\n\n @if (timeOnly() || showTime()) {\n <div class=\"mt-3 pt-3 _shk-border dark:[border-color:var(--shk-surface-200)]\">\n <div class=\"flex items-center justify-center gap-1\">\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewHour()\"\n (change)=\"onHourChange($event)\"\n aria-label=\"Hora\"\n >\n @for (h of hoursRange(); track h) {\n <option [value]=\"h\">{{ hourDisplay(h) }}</option>\n }\n </select>\n <span class=\"_shk-text-surface-400\">:</span>\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewMinute()\"\n (change)=\"onMinuteChange($event)\"\n aria-label=\"Minutos\"\n >\n @for (minutes of minutesRange(); track minutes) {\n <option [value]=\"minutes\">{{ minutesLabel(minutes) }}</option>\n }\n </select>\n @if (showSeconds()) {\n <span class=\"text-sm _shk-text-surface-400\">:</span>\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewSecond()\"\n (change)=\"onSecondChange($event)\"\n aria-label=\"Segundos\"\n >\n @for (seconds of secondsRange(); track seconds) {\n <option [value]=\"seconds\">{{ minutesLabel(seconds) }}</option>\n }\n </select>\n }\n @if (hourFormat() === '12') {\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"meridiem()\"\n (change)=\"onMeridiemChange($event)\"\n aria-label=\"AM/PM\"\n >\n <option value=\"AM\">AM</option>\n <option value=\"PM\">PM</option>\n </select>\n }\n </div>\n <button\n type=\"button\"\n (click)=\"confirmTime()\"\n class=\"mt-3 w-full py-2 text-sm _shk-bg-primary _shk-text-white rounded-lg hover:[background-color:var(--shk-primary-700)] transition-colors\"\n >\n {{ timeOnly() ? 'Confirmar' : 'Introducir hora' }}\n </button>\n </div>\n }\n </div>\n }\n </div>\n\n @if (shouldShowError()) {\n <div class=\"mt-1 text-xs text-red-500 max-h-20 overflow-y-auto _shk-scrollbar\">\n <ul class=\"list-disc pl-4 space-y-1\">\n @for (error of getErrorMessages(); track error) {\n <li>{{ error }}</li>\n }\n </ul>\n </div>\n }\n</div>\n" }]
|
|
947
|
+
], template: "<div class=\"relative mb-4\">\n @if (label()) {\n <div class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] mb-1\">\n {{ label() }}\n </div>\n }\n\n <div class=\"relative\">\n <div class=\"relative\">\n <input\n [id]=\"id()\"\n type=\"text\"\n [disabled]=\"isDisabled()\"\n [placeholder]=\"inputPlaceholder()\"\n [value]=\"isTyping() ? typedValue() : displayLabel()\"\n (input)=\"onInputText($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown.enter)=\"$any($event.target).blur()\"\n (keydown.escape)=\"cancelTyping()\"\n (click)=\"toggle()\"\n class=\"date-input-field\"\n [ngClass]=\"{\n 'input-error': shouldShowError() || typingValid() === false,\n 'input-valid': wasFixedError() || typingValid() === true,\n }\"\n [attr.aria-invalid]=\"shouldShowError()\"\n [attr.aria-describedby]=\"shouldShowError() ? id() + '-error' : null\"\n />\n <button\n type=\"button\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggle()\"\n class=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 _shk-text-surface-400 dark:[color:var(--shk-surface-400)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)] transition-colors\"\n >\n <svg\n class=\"w-4 h-4\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"></rect>\n <path d=\"M16 2v4M8 2v4M3 10h18\"></path>\n </svg>\n </button>\n </div>\n\n @if (isOpen()) {\n <div class=\"fixed inset-0 z-40 bg-black/30\" (click)=\"close()\"></div>\n <div\n class=\"fixed z-50 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-72 p-4 bg-white dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-lg shadow-xl\"\n (click)=\"$event.stopPropagation()\"\n >\n <!-- DATE VIEW -->\n @if (!timeOnly() && currentView() === 'date') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <button\n type=\"button\"\n (click)=\"drillToMonths()\"\n class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] hover:_shk-text-primary cursor-pointer hover:underline transition-colors\"\n >\n {{ monthNames()[viewMonth()] }} {{ viewYear() }}\n </button>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0 mb-1\">\n @for (name of dayNames(); track $index) {\n <div class=\"text-center text-xs font-medium _shk-text-surface-400 dark:[color:var(--shk-surface-400)] py-1\">\n {{ name }}\n </div>\n }\n </div>\n\n <div class=\"grid grid-cols-7 gap-0\">\n @for (day of calendarDays(); track $index) {\n @if (day !== null) {\n <button\n type=\"button\"\n (click)=\"selectDay(day)\"\n class=\"py-1.5 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedDay(day),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isToday(day) && !isSelectedDay(day),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedDay(day) && !isToday(day),\n }\"\n >\n {{ day }}\n </button>\n } @else {\n <div></div>\n }\n }\n </div>\n }\n\n <!-- MONTH VIEW -->\n @if (!timeOnly() && currentView() === 'month') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <button\n type=\"button\"\n (click)=\"drillToYears()\"\n class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] hover:_shk-text-primary cursor-pointer hover:underline transition-colors\"\n >\n {{ viewYear() }}\n </button>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (name of monthNames(); track $index) {\n <button\n type=\"button\"\n (click)=\"drillDownToDate($index)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedMonth($index),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentMonth($index) && !isSelectedMonth($index),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedMonth($index) && !isCurrentMonth($index),\n }\"\n >\n {{ name }}\n </button>\n }\n </div>\n }\n\n <!-- YEAR VIEW -->\n @if (!timeOnly() && currentView() === 'year') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ viewDecade() }} - {{ viewDecade() + 11 }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (year of decadeYears(); track year) {\n <button\n type=\"button\"\n (click)=\"drillDownToMonth(year)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedYear(year),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentYear(year) && !isSelectedYear(year),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedYear(year) && !isCurrentYear(year),\n }\"\n >\n {{ year }}\n </button>\n }\n </div>\n }\n\n <!-- ANALOG CLOCK TIME PICKER -->\n @if (timeOnly() || showTime()) {\n <div class=\"mt-3 pt-3 _shk-border dark:[border-color:var(--shk-surface-200)]\">\n <!-- Clock display time -->\n <div class=\"text-center mb-2\">\n <span class=\"text-lg font-semibold _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\">\n {{ clockDisplayTime() }}\n </span>\n </div>\n\n <!-- Analog clock face -->\n <div class=\"clock-container\">\n <svg\n viewBox=\"0 0 200 200\"\n class=\"w-full h-full cursor-pointer\"\n (click)=\"onClockClick($event, clockMode())\"\n >\n <!-- Clock face background -->\n <circle cx=\"100\" cy=\"100\" r=\"96\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1\"\n class=\"_shk-text-surface-200 dark:[color:var(--shk-surface-200)]\" />\n\n <!-- Minute markers -->\n @if (clockMode() === 'minutes') {\n @for (i of [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59]; track i) {\n <line\n [attr.x1]=\"clockMinuteMarker(i).x1\"\n [attr.y1]=\"clockMinuteMarker(i).y1\"\n [attr.x2]=\"clockMinuteMarker(i).x2\"\n [attr.y2]=\"clockMinuteMarker(i).y2\"\n [attr.stroke-width]=\"i % 5 === 0 ? 2 : 1\"\n [class]=\"i % 5 === 0 ? '_shk-text-surface-500 dark:[color:var(--shk-surface-500)]' : '_shk-text-surface-300 dark:[color:var(--shk-surface-400)]'\"\n stroke=\"currentColor\"\n />\n }\n <!-- Minute numbers (00, 05, 10, ...) -->\n @for (m of [0,5,10,15,20,25,30,35,40,45,50,55]; track m) {\n <text\n [attr.x]=\"clockMinutePos(m).x\"\n [attr.y]=\"clockMinutePos(m).y\"\n text-anchor=\"middle\"\n dominant-baseline=\"central\"\n class=\"text-[9px] _shk-text-surface-500 dark:[color:var(--shk-surface-400)]\"\n [class._shk-text-primary]=\"viewMinute() === m\"\n >\n {{ m.toString().padStart(2, '0') }}\n </text>\n }\n }\n\n <!-- Hour numbers -->\n @if (clockMode() === 'hours') {\n @for (h of clockHours(); track h) {\n <text\n [attr.x]=\"clockNumberPos(h).x\"\n [attr.y]=\"clockNumberPos(h).y\"\n text-anchor=\"middle\"\n dominant-baseline=\"central\"\n class=\"text-sm font-medium\"\n [class._shk-text-primary]=\"isClockHourSelected(h)\"\n [class._shk-text-surface-700]=\"!isClockHourSelected(h)\"\n >\n {{ h }}\n </text>\n }\n }\n\n <!-- Clock hand -->\n <line\n x1=\"100\" y1=\"100\"\n [attr.x2]=\"clockHandLine(60).x2\"\n [attr.y2]=\"clockHandLine(60).y2\"\n class=\"_shk-text-primary\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n\n <!-- Center dot -->\n <circle cx=\"100\" cy=\"100\" r=\"4\" class=\"fill-current _shk-text-primary\" />\n </svg>\n </div>\n\n <!-- Mode toggle: Hours / Minutes -->\n <div class=\"clock-mode-toggle\">\n <button\n type=\"button\"\n (click)=\"clockMode.set('hours')\"\n class=\"clock-mode-btn\"\n [class.active]=\"clockMode() === 'hours'\"\n >\n {{ 'Horas' }}\n </button>\n <button\n type=\"button\"\n (click)=\"clockMode.set('minutes')\"\n class=\"clock-mode-btn\"\n [class.active]=\"clockMode() === 'minutes'\"\n >\n {{ 'Minutos' }}\n </button>\n </div>\n\n <!-- AM/PM toggle for 12h format -->\n @if (hourFormat() === '12') {\n <div class=\"flex items-center justify-center gap-2 mt-2\">\n <button\n type=\"button\"\n (click)=\"viewMeridiem.set('AM')\"\n class=\"px-3 py-1 text-xs font-medium rounded-full transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white': viewMeridiem() === 'AM',\n '_shk-text-surface-500 hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': viewMeridiem() !== 'AM'\n }\"\n >\n AM\n </button>\n <button\n type=\"button\"\n (click)=\"viewMeridiem.set('PM')\"\n class=\"px-3 py-1 text-xs font-medium rounded-full transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white': viewMeridiem() === 'PM',\n '_shk-text-surface-500 hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': viewMeridiem() !== 'PM'\n }\"\n >\n PM\n </button>\n </div>\n }\n\n <!-- Seconds selector (if enabled) -->\n @if (showSeconds()) {\n <div class=\"flex items-center justify-center gap-1 mt-2\">\n <span class=\"text-xs _shk-text-surface-400\">Seg:</span>\n <select\n class=\"px-2 py-1 text-xs _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewSecond()\"\n (change)=\"onSecondChange($event)\"\n aria-label=\"Segundos\"\n >\n @for (seconds of secondsRange(); track seconds) {\n <option [value]=\"seconds\">{{ minutesLabel(seconds) }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Confirm button -->\n <button\n type=\"button\"\n (click)=\"confirmTime()\"\n class=\"mt-3 w-full py-2 text-sm _shk-bg-primary _shk-text-white rounded-lg hover:[background-color:var(--shk-primary-700)] transition-colors\"\n >\n {{ timeOnly() ? 'Confirmar' : 'Introducir hora' }}\n </button>\n </div>\n }\n </div>\n }\n </div>\n\n @if (shouldShowError()) {\n <div [id]=\"id() + '-error'\" class=\"mt-1 text-xs text-red-500 max-h-20 overflow-y-auto _shk-scrollbar\">\n <ul class=\"list-disc pl-4 space-y-1\">\n @for (error of getErrorMessages(); track error) {\n <li>{{ error }}</li>\n }\n </ul>\n </div>\n }\n</div>\n", styles: [".clock-container{position:relative;width:200px;height:200px;margin:0 auto}.clock-face{width:100%;height:100%;border-radius:50%;border:2px solid var(--shk-surface-200);position:relative;background-color:var(--shk-surface-50)}.dark .clock-face{border-color:var(--shk-surface-200);background-color:var(--shk-surface-100)}.clock-number{position:absolute;width:32px;height:32px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:500;border-radius:50%;transform:translate(-50%,-50%);cursor:pointer;transition:background-color .15s,color .15s;-webkit-user-select:none;user-select:none;color:var(--shk-surface-700)}.dark .clock-number{color:var(--shk-surface-700)}.clock-number:hover{background-color:var(--shk-surface-100)}.dark .clock-number:hover{background-color:var(--shk-surface-200)}.clock-number.selected{background-color:var(--shk-primary-500);color:#fff}.clock-number.current{font-weight:700}.clock-hand{position:absolute;bottom:50%;left:50%;width:2px;background-color:var(--shk-primary-500);transform-origin:bottom center;border-radius:1px;pointer-events:none;transition:transform .2s ease-out}.clock-center{position:absolute;top:50%;left:50%;width:8px;height:8px;border-radius:50%;background-color:var(--shk-primary-500);transform:translate(-50%,-50%);pointer-events:none;z-index:2}.clock-minute-marker{position:absolute;width:1px;height:8px;background-color:var(--shk-surface-300);top:6px;left:50%;transform-origin:center 94px}.dark .clock-minute-marker{background-color:var(--shk-surface-400)}.clock-minute-marker.major{width:2px;height:12px;background-color:var(--shk-surface-400)}.dark .clock-minute-marker.major{background-color:var(--shk-surface-500)}.clock-mode-toggle{display:flex;align-items:center;justify-content:center;gap:.5rem;margin-top:.75rem}.clock-mode-btn{padding:.25rem .75rem;font-size:.75rem;font-weight:500;border-radius:9999px;cursor:pointer;transition:background-color .15s,color .15s;border:none;background:transparent;color:var(--shk-surface-500)}.dark .clock-mode-btn{color:var(--shk-surface-400)}.clock-mode-btn.active{background-color:var(--shk-primary-500);color:#fff}.clock-mode-btn:hover:not(.active){background-color:var(--shk-surface-100)}.dark .clock-mode-btn:hover:not(.active){background-color:var(--shk-surface-200)}.date-input-field{width:100%;padding:.5rem 2.5rem .5rem .75rem;font-size:.875rem;border-radius:.5rem;outline:none;transition:border-color .15s,box-shadow .15s;background-color:var(--shk-surface-50);color:var(--shk-surface-700);border:1px solid var(--shk-surface-200)}.dark .date-input-field{background-color:var(--shk-surface-100);color:var(--shk-surface-700);border-color:var(--shk-surface-200)}.date-input-field::placeholder{color:var(--shk-surface-400)}.dark .date-input-field::placeholder{color:var(--shk-surface-400)}.date-input-field:focus{border-color:var(--shk-primary-500);box-shadow:0 0 0 1px var(--shk-primary-500)}.date-input-field:disabled{opacity:.5;cursor:not-allowed}.date-input-field.input-error{border-color:#ef4444}.date-input-field.input-error:focus{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.date-input-field.input-valid{border-color:#22c55e}.date-input-field.input-valid:focus{border-color:#22c55e;box-shadow:0 0 0 1px #22c55e}\n"] }]
|
|
625
948
|
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], view: [{ type: i0.Input, args: [{ isSignal: true, alias: "view", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], formGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "formGroup", required: false }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], errorMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessages", required: false }] }], minValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "minValue", required: false }] }], maxValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxValue", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], timeOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeOnly", required: false }] }], showTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTime", required: false }] }], hourFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "hourFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], onDocumentClick: [{
|
|
626
949
|
type: HostListener,
|
|
627
950
|
args: ['document:click', ['$event']]
|