@brickclay-org/ui 0.1.71 → 0.1.73
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/brickclay-org-ui.mjs +382 -19
- package/fesm2022/brickclay-org-ui.mjs.map +1 -1
- package/index.d.ts +143 -5
- package/package.json +1 -1
- package/src/assets/icons/alert-circle-red.svg +12 -0
- package/src/lib/menu/menu.css +226 -0
- package/src/lib/tabs/tabs.css +22 -1
|
@@ -43,6 +43,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
43
43
|
}] });
|
|
44
44
|
|
|
45
45
|
class BkTimePicker {
|
|
46
|
+
renderer;
|
|
46
47
|
required = false;
|
|
47
48
|
/** @deprecated Prefer [(ngModel)] */
|
|
48
49
|
value = null;
|
|
@@ -100,6 +101,40 @@ class BkTimePicker {
|
|
|
100
101
|
activeSecond = 0;
|
|
101
102
|
_modelValue = null;
|
|
102
103
|
brickclayIcons = BrickclayIcons;
|
|
104
|
+
constructor(renderer) {
|
|
105
|
+
this.renderer = renderer;
|
|
106
|
+
}
|
|
107
|
+
/* ---------------- appendToBody dropdown relocation ---------------- */
|
|
108
|
+
/**
|
|
109
|
+
* `appendToBody` anchors the dropdown with `position: fixed`, which only tracks the viewport when
|
|
110
|
+
* NO ancestor establishes a containing block (transform / filter / perspective / will-change /
|
|
111
|
+
* contain / backdrop-filter). Inside such an ancestor the fixed coordinates resolve against that
|
|
112
|
+
* ancestor instead and the dropdown drifts off-position. To be robust we physically move the panel
|
|
113
|
+
* to <body> while open (Angular keeps managing it by reference) and move it back before *ngIf
|
|
114
|
+
* tears it down. The panel carries a `tp-compact` self-class so its `default`-variation sizing —
|
|
115
|
+
* otherwise scoped under the `.time-input-group.default` ancestor it just left — still applies.
|
|
116
|
+
*/
|
|
117
|
+
dropdownMovedToBody = false;
|
|
118
|
+
dropdownOriginalParent = null;
|
|
119
|
+
moveDropdownToBody() {
|
|
120
|
+
const el = this.tpDropdown?.nativeElement;
|
|
121
|
+
if (!el || this.dropdownMovedToBody)
|
|
122
|
+
return;
|
|
123
|
+
this.dropdownOriginalParent = el.parentElement;
|
|
124
|
+
this.renderer.appendChild(document.body, el);
|
|
125
|
+
this.dropdownMovedToBody = true;
|
|
126
|
+
}
|
|
127
|
+
/** Return the dropdown to its original slot so *ngIf can destroy it cleanly (safe to call twice). */
|
|
128
|
+
restoreDropdownFromBody() {
|
|
129
|
+
if (!this.dropdownMovedToBody)
|
|
130
|
+
return;
|
|
131
|
+
const el = this.tpDropdown?.nativeElement;
|
|
132
|
+
if (el && this.dropdownOriginalParent) {
|
|
133
|
+
this.renderer.appendChild(this.dropdownOriginalParent, el);
|
|
134
|
+
}
|
|
135
|
+
this.dropdownMovedToBody = false;
|
|
136
|
+
this.dropdownOriginalParent = null;
|
|
137
|
+
}
|
|
103
138
|
/* ---------------- CVA ---------------- */
|
|
104
139
|
writeValue(value) {
|
|
105
140
|
if (!value) {
|
|
@@ -254,7 +289,13 @@ class BkTimePicker {
|
|
|
254
289
|
if (this.autoPosition || this.appendToBody) {
|
|
255
290
|
this.attachViewportListeners();
|
|
256
291
|
// Measure once the dropdown is in the DOM, before it becomes visible to the user.
|
|
257
|
-
setTimeout(() =>
|
|
292
|
+
setTimeout(() => {
|
|
293
|
+
// Relocate to <body> first so no transformed/overflow ancestor can re-anchor the fixed
|
|
294
|
+
// dropdown, then compute viewport coordinates.
|
|
295
|
+
if (this.appendToBody)
|
|
296
|
+
this.moveDropdownToBody();
|
|
297
|
+
this.updatePosition();
|
|
298
|
+
}, 0);
|
|
258
299
|
}
|
|
259
300
|
setTimeout(() => this.scrollToSelectedTimes(), 100);
|
|
260
301
|
}
|
|
@@ -266,6 +307,9 @@ class BkTimePicker {
|
|
|
266
307
|
dismiss() {
|
|
267
308
|
if (!this.showPicker)
|
|
268
309
|
return;
|
|
310
|
+
// Put the dropdown back before showPicker=false so *ngIf removes it from the right parent on the
|
|
311
|
+
// next change-detection pass (avoids leaving an orphan node in <body>).
|
|
312
|
+
this.restoreDropdownFromBody();
|
|
269
313
|
this.showPicker = false;
|
|
270
314
|
this.detachViewportListeners();
|
|
271
315
|
this.markAsTouched();
|
|
@@ -326,8 +370,14 @@ class BkTimePicker {
|
|
|
326
370
|
}
|
|
327
371
|
/* ---------------- Outside click ---------------- */
|
|
328
372
|
onDocumentClick(event) {
|
|
373
|
+
if (!this.showPicker)
|
|
374
|
+
return;
|
|
329
375
|
const target = event.target;
|
|
330
|
-
|
|
376
|
+
// When appendToBody has relocated the dropdown to <body>, it is no longer inside
|
|
377
|
+
// .time-picker-wrapper, so also treat clicks within the dropdown itself as "inside".
|
|
378
|
+
const dropdownEl = this.tpDropdown?.nativeElement;
|
|
379
|
+
const insideDropdown = !!dropdownEl && (dropdownEl === target || dropdownEl.contains(target));
|
|
380
|
+
if (!target.closest('.time-picker-wrapper') && !insideDropdown) {
|
|
331
381
|
this.dismiss();
|
|
332
382
|
}
|
|
333
383
|
}
|
|
@@ -407,14 +457,14 @@ class BkTimePicker {
|
|
|
407
457
|
bottom: `${window.innerHeight - rect.top + gap}px`,
|
|
408
458
|
left: `${left}px`,
|
|
409
459
|
right: 'auto',
|
|
410
|
-
'z-index': '
|
|
460
|
+
'z-index': '100000',
|
|
411
461
|
}
|
|
412
462
|
: {
|
|
413
463
|
position: 'fixed',
|
|
414
464
|
top: `${rect.bottom + gap}px`,
|
|
415
465
|
left: `${left}px`,
|
|
416
466
|
right: 'auto',
|
|
417
|
-
'z-index': '
|
|
467
|
+
'z-index': '100000',
|
|
418
468
|
};
|
|
419
469
|
}
|
|
420
470
|
else {
|
|
@@ -489,15 +539,18 @@ class BkTimePicker {
|
|
|
489
539
|
}
|
|
490
540
|
ngOnDestroy() {
|
|
491
541
|
this.detachViewportListeners();
|
|
542
|
+
// If destroyed while open with the dropdown relocated to <body>, put it back so Angular's
|
|
543
|
+
// teardown removes it from the expected parent instead of leaving an orphan node in <body>.
|
|
544
|
+
this.restoreDropdownFromBody();
|
|
492
545
|
}
|
|
493
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkTimePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
546
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkTimePicker, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
|
|
494
547
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: BkTimePicker, isStandalone: true, selector: "bk-time-picker", inputs: { required: "required", value: "value", label: "label", placeholder: "placeholder", clearable: "clearable", position: "position", variation: "variation", pickerId: "pickerId", closePicker: "closePicker", timeFormat: "timeFormat", showSeconds: "showSeconds", autoPosition: "autoPosition", appendToBody: "appendToBody", disabled: "disabled" }, outputs: { change: "change", timeChange: "timeChange", pickerOpened: "pickerOpened", pickerClosed: "pickerClosed" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, providers: [
|
|
495
548
|
{
|
|
496
549
|
provide: NG_VALUE_ACCESSOR,
|
|
497
550
|
useExisting: forwardRef(() => BkTimePicker),
|
|
498
551
|
multi: true,
|
|
499
552
|
},
|
|
500
|
-
], viewQueries: [{ propertyName: "hourScrollEl", first: true, predicate: ["hourScroll"], descendants: true }, { propertyName: "minuteScrollEl", first: true, predicate: ["minuteScroll"], descendants: true }, { propertyName: "secondScrollEl", first: true, predicate: ["secondScroll"], descendants: true }, { propertyName: "tpWrapper", first: true, predicate: ["tpWrapper"], descendants: true }, { propertyName: "tpDropdown", first: true, predicate: ["tpDropdown"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"time-picker-wrapper\">\r\n <div class=\"time-input-group\" [ngClass]=\"variation\">\r\n @if (label) {\r\n <label>{{ label }}</label>\r\n }\r\n <div class=\"time-input-wrapper\" [class.has-value]=\"hasValue\" #tpWrapper>\r\n <input\r\n type=\"text\"\r\n class=\"time-input\"\r\n [value]=\"getDisplayValue()\"\r\n [placeholder]=\"placeholder\"\r\n [disabled]=\"disabled ? true : null\"\r\n readonly\r\n (click)=\"!disabled && togglePicker()\"\r\n (blur)=\"markAsTouched()\"\r\n />\r\n <span class=\"time-icon\">\r\n <img alt=\"timer\" class=\"timer-icon\" [src]=\"brickclayIcons.timerIcon\" />\r\n </span>\r\n <button\r\n type=\"button\"\r\n class=\"time-clear-btn\"\r\n *ngIf=\"clearable && hasValue && !disabled\"\r\n (click)=\"clear(); $event.stopPropagation()\"\r\n title=\"Clear time\"\r\n aria-label=\"Clear time\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"14\"\r\n height=\"14\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n >\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n </button>\r\n <div class=\"custom-time-picker-dropdown\" *ngIf=\"showPicker\">\r\n <div\r\n #tpDropdown\r\n class=\"custom-time-picker\"\r\n [ngStyle]=\"dropdownStyle\"\r\n [class.tp-above]=\"placeAbove\"\r\n [class.append-to-body]=\"appendToBody\"\r\n [ngClass]=\"{\r\n 'left-position': resolvedPosition === 'left',\r\n 'right-position': resolvedPosition === 'right',\r\n 'format-24': timeFormat === 24,\r\n }\"\r\n >\r\n <!-- Hours Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #hourScroll (scroll)=\"onHourScroll()\">\r\n <div\r\n *ngFor=\"let h of getHours()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentHour === h\"\r\n [class.active]=\"activeHour === h\"\r\n (click)=\"onHourChange(h)\"\r\n >\r\n {{ h.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <!-- Minutes Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #minuteScroll (scroll)=\"onMinuteScroll()\">\r\n <div\r\n *ngFor=\"let m of minutes\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentMinute === m\"\r\n [class.active]=\"activeMinute === m\"\r\n (click)=\"onMinuteChange(m)\"\r\n >\r\n {{ m.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n @if (showSeconds) {\r\n <span class=\"time-separator\">:</span>\r\n <!-- Seconds Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #secondScroll (scroll)=\"onSecondScroll()\">\r\n <div\r\n *ngFor=\"let s of seconds\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentSecond === s\"\r\n [class.active]=\"activeSecond === s\"\r\n (click)=\"onSecondChange(s)\"\r\n >\r\n {{ s.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n <!-- AM/PM Column (only in 12-hour format) -->\r\n @if (timeFormat === 12) {\r\n <span class=\"time-separator\">:</span>\r\n <div class=\"time-column ampm-column\">\r\n <div class=\"time-scroll\">\r\n <div\r\n *ngFor=\"let ap of getAMPMOptions()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentAMPM === ap\"\r\n (click)=\"onAMPMChange(ap)\"\r\n >\r\n {{ ap }}\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".time-picker-wrapper{width:100%;font-family:Inter,sans-serif}.time-input-group{display:flex;flex-direction:column;gap:4px}.time-input-group label{font-size:11px;font-weight:500;color:#15191e;text-transform:uppercase;letter-spacing:-.28px}.time-input-wrapper{position:relative;display:flex;align-items:center}.time-input-wrapper.has-value .time-input{padding-right:56px}.time-input{border:1px solid #d1d5db;border-radius:4px;font-family:Inter,sans-serif;color:#707280;background:#fff;transition:all .2s;width:100%;box-sizing:border-box;cursor:pointer}.time-input::placeholder{color:#6b7080}.time-input-group.default .time-input{font-size:12px;padding:6px 40px 5px 12px}.time-input-group.lg .time-input{font-size:14px;line-height:18px;padding:10px 40px 10px 12px}.time-input:focus{outline:none;border-color:#111827}.time-input:hover{border-color:#9ca3af}.time-clear-btn{position:absolute;right:26px;background:none;border:none;font-size:18px;color:#9ca3af;cursor:pointer;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;line-height:1}.time-clear-btn:hover{color:#374151}.time-input-group.default .time-clear-btn{top:5px}.time-input-group.lg .time-clear-btn{top:10px}.time-icon{position:absolute;right:12px;font-size:16px;pointer-events:none;color:#9ca3af;cursor:pointer}.custom-time-picker-wrapper{width:100%;display:flex;justify-content:center}.custom-time-picker{display:flex;align-items:flex-start;gap:8px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:10px;box-shadow:0 4px 12px #00000026;width:182px;position:absolute;top:calc(100% + 4px);z-index:1000}.custom-time-picker.format-24{width:124px}.custom-time-picker.left-position{left:0}.custom-time-picker.right-position{right:0}.custom-time-picker.tp-above:not(.append-to-body){top:auto;bottom:calc(100% + 4px)}.time-column{display:flex;flex-direction:column;position:relative}.time-scroll{display:flex;flex-direction:column;max-height:96px;overflow-y:auto;overflow-x:hidden;scroll-snap-type:y proximity;scrollbar-width:thin;scrollbar-color:#cbd5e1 transparent;scrollbar-width:none;-ms-overflow-style:none}.time-scroll::-webkit-scrollbar{display:none}.time-scroll::-webkit-scrollbar-track{background:transparent}.time-scroll::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#94a3b8}.time-item{min-width:40px;width:40px;height:32px;min-height:32px;scroll-snap-align:center;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:400;color:#374151;cursor:pointer;border-radius:4px;transition:all .15s ease;-webkit-user-select:none;user-select:none;font-family:Inter,sans-serif}.time-item:hover{background:#f3f4f6}.time-item.selected{background:#111827;color:#fff;font-weight:500}.time-item.active:not(.selected){background:#eef2ff;color:#111827;font-weight:500}.ampm-column .time-item{min-width:40px;width:40px}.time-separator{font-size:16px;font-weight:600;color:#6b7280;margin:5px 0 0}.time-input:disabled{cursor:not-allowed;border-color:#e3e3e7;background-color:#f4f4f6;color:#a1a3ae}.time-input-group.default .custom-time-picker{width:fit-content;gap:6px;padding:8px}.time-input-group.default .custom-time-picker.format-24{width:fit-content}.time-input-group.default .time-scroll{max-height:84px}.time-input-group.default .time-item{min-width:26px;width:26px;height:28px;min-height:28px;font-size:12px}.time-input-group.default .ampm-column .time-item{min-width:26px;width:26px}.time-input-group.default .time-separator{font-size:14px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }] });
|
|
553
|
+
], viewQueries: [{ propertyName: "hourScrollEl", first: true, predicate: ["hourScroll"], descendants: true }, { propertyName: "minuteScrollEl", first: true, predicate: ["minuteScroll"], descendants: true }, { propertyName: "secondScrollEl", first: true, predicate: ["secondScroll"], descendants: true }, { propertyName: "tpWrapper", first: true, predicate: ["tpWrapper"], descendants: true }, { propertyName: "tpDropdown", first: true, predicate: ["tpDropdown"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"time-picker-wrapper\">\r\n <div class=\"time-input-group\" [ngClass]=\"variation\">\r\n @if (label) {\r\n <label>{{ label }}</label>\r\n }\r\n <div class=\"time-input-wrapper\" [class.has-value]=\"hasValue\" #tpWrapper>\r\n <input\r\n type=\"text\"\r\n class=\"time-input\"\r\n [value]=\"getDisplayValue()\"\r\n [placeholder]=\"placeholder\"\r\n [disabled]=\"disabled ? true : null\"\r\n readonly\r\n (click)=\"!disabled && togglePicker()\"\r\n (blur)=\"markAsTouched()\"\r\n />\r\n <span class=\"time-icon\">\r\n <img alt=\"timer\" class=\"timer-icon\" [src]=\"brickclayIcons.timerIcon\" />\r\n </span>\r\n <button\r\n type=\"button\"\r\n class=\"time-clear-btn\"\r\n *ngIf=\"clearable && hasValue && !disabled\"\r\n (click)=\"clear(); $event.stopPropagation()\"\r\n title=\"Clear time\"\r\n aria-label=\"Clear time\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"14\"\r\n height=\"14\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n >\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n </button>\r\n <div class=\"custom-time-picker-dropdown\" *ngIf=\"showPicker\">\r\n <div\r\n #tpDropdown\r\n class=\"custom-time-picker\"\r\n [ngStyle]=\"dropdownStyle\"\r\n [class.tp-above]=\"placeAbove\"\r\n [class.append-to-body]=\"appendToBody\"\r\n [ngClass]=\"{\r\n 'left-position': resolvedPosition === 'left',\r\n 'right-position': resolvedPosition === 'right',\r\n 'format-24': timeFormat === 24,\r\n 'tp-compact': variation === 'default',\r\n }\"\r\n >\r\n <!-- Hours Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #hourScroll (scroll)=\"onHourScroll()\">\r\n <div\r\n *ngFor=\"let h of getHours()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentHour === h\"\r\n [class.active]=\"activeHour === h\"\r\n (click)=\"onHourChange(h)\"\r\n >\r\n {{ h.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <!-- Minutes Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #minuteScroll (scroll)=\"onMinuteScroll()\">\r\n <div\r\n *ngFor=\"let m of minutes\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentMinute === m\"\r\n [class.active]=\"activeMinute === m\"\r\n (click)=\"onMinuteChange(m)\"\r\n >\r\n {{ m.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n @if (showSeconds) {\r\n <span class=\"time-separator\">:</span>\r\n <!-- Seconds Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #secondScroll (scroll)=\"onSecondScroll()\">\r\n <div\r\n *ngFor=\"let s of seconds\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentSecond === s\"\r\n [class.active]=\"activeSecond === s\"\r\n (click)=\"onSecondChange(s)\"\r\n >\r\n {{ s.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n <!-- AM/PM Column (only in 12-hour format) -->\r\n @if (timeFormat === 12) {\r\n <span class=\"time-separator\">:</span>\r\n <div class=\"time-column ampm-column\">\r\n <div class=\"time-scroll\">\r\n <div\r\n *ngFor=\"let ap of getAMPMOptions()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentAMPM === ap\"\r\n (click)=\"onAMPMChange(ap)\"\r\n >\r\n {{ ap }}\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".time-picker-wrapper{width:100%;font-family:Inter,sans-serif}.time-input-group{display:flex;flex-direction:column;gap:4px}.time-input-group label{font-size:11px;font-weight:500;color:#15191e;text-transform:uppercase;letter-spacing:-.28px}.time-input-wrapper{position:relative;display:flex;align-items:center}.time-input-wrapper.has-value .time-input{padding-right:56px}.time-input{border:1px solid #d1d5db;border-radius:4px;font-family:Inter,sans-serif;color:#707280;background:#fff;transition:all .2s;width:100%;box-sizing:border-box;cursor:pointer}.time-input::placeholder{color:#6b7080}.time-input-group.default .time-input{font-size:12px;padding:6px 40px 5px 12px}.time-input-group.lg .time-input{font-size:14px;line-height:18px;padding:10px 40px 10px 12px}.time-input:focus{outline:none;border-color:#111827}.time-input:hover{border-color:#9ca3af}.time-clear-btn{position:absolute;right:26px;background:none;border:none;font-size:18px;color:#9ca3af;cursor:pointer;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;line-height:1}.time-clear-btn:hover{color:#374151}.time-input-group.default .time-clear-btn{top:5px}.time-input-group.lg .time-clear-btn{top:10px}.time-icon{position:absolute;right:12px;font-size:16px;pointer-events:none;color:#9ca3af;cursor:pointer}.custom-time-picker-wrapper{width:100%;display:flex;justify-content:center}.custom-time-picker{display:flex;align-items:flex-start;gap:8px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:10px;box-shadow:0 4px 12px #00000026;width:182px;position:absolute;top:calc(100% + 4px);z-index:1000}.custom-time-picker.format-24{width:124px}.custom-time-picker.left-position{left:0}.custom-time-picker.right-position{right:0}.custom-time-picker.tp-above:not(.append-to-body){top:auto;bottom:calc(100% + 4px)}.time-column{display:flex;flex-direction:column;position:relative}.time-scroll{display:flex;flex-direction:column;max-height:96px;overflow-y:auto;overflow-x:hidden;scroll-snap-type:y proximity;scrollbar-width:thin;scrollbar-color:#cbd5e1 transparent;scrollbar-width:none;-ms-overflow-style:none}.time-scroll::-webkit-scrollbar{display:none}.time-scroll::-webkit-scrollbar-track{background:transparent}.time-scroll::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#94a3b8}.time-item{min-width:40px;width:40px;height:32px;min-height:32px;scroll-snap-align:center;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:400;color:#374151;cursor:pointer;border-radius:4px;transition:all .15s ease;-webkit-user-select:none;user-select:none;font-family:Inter,sans-serif}.time-item:hover{background:#f3f4f6}.time-item.selected{background:#111827;color:#fff;font-weight:500}.time-item.active:not(.selected){background:#eef2ff;color:#111827;font-weight:500}.ampm-column .time-item{min-width:40px;width:40px}.time-separator{font-size:16px;font-weight:600;color:#6b7280;margin:5px 0 0}.time-input:disabled{cursor:not-allowed;border-color:#e3e3e7;background-color:#f4f4f6;color:#a1a3ae}.time-input-group.default .custom-time-picker{width:fit-content;gap:6px;padding:8px}.time-input-group.default .custom-time-picker.format-24{width:fit-content}.time-input-group.default .time-scroll{max-height:84px}.time-input-group.default .time-item{min-width:26px;width:26px;height:28px;min-height:28px;font-size:12px}.time-input-group.default .ampm-column .time-item{min-width:26px;width:26px}.time-input-group.default .time-separator{font-size:14px}.custom-time-picker.append-to-body{font-family:Inter,sans-serif}.custom-time-picker.tp-compact{width:fit-content;gap:6px;padding:8px}.custom-time-picker.tp-compact.format-24{width:fit-content}.custom-time-picker.tp-compact .time-scroll{max-height:84px}.custom-time-picker.tp-compact .time-item{min-width:26px;width:26px;height:28px;min-height:28px;font-size:12px}.custom-time-picker.tp-compact .ampm-column .time-item{min-width:26px;width:26px}.custom-time-picker.tp-compact .time-separator{font-size:14px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }] });
|
|
501
554
|
}
|
|
502
555
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkTimePicker, decorators: [{
|
|
503
556
|
type: Component,
|
|
@@ -507,8 +560,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
507
560
|
useExisting: forwardRef(() => BkTimePicker),
|
|
508
561
|
multi: true,
|
|
509
562
|
},
|
|
510
|
-
], template: "<div class=\"time-picker-wrapper\">\r\n <div class=\"time-input-group\" [ngClass]=\"variation\">\r\n @if (label) {\r\n <label>{{ label }}</label>\r\n }\r\n <div class=\"time-input-wrapper\" [class.has-value]=\"hasValue\" #tpWrapper>\r\n <input\r\n type=\"text\"\r\n class=\"time-input\"\r\n [value]=\"getDisplayValue()\"\r\n [placeholder]=\"placeholder\"\r\n [disabled]=\"disabled ? true : null\"\r\n readonly\r\n (click)=\"!disabled && togglePicker()\"\r\n (blur)=\"markAsTouched()\"\r\n />\r\n <span class=\"time-icon\">\r\n <img alt=\"timer\" class=\"timer-icon\" [src]=\"brickclayIcons.timerIcon\" />\r\n </span>\r\n <button\r\n type=\"button\"\r\n class=\"time-clear-btn\"\r\n *ngIf=\"clearable && hasValue && !disabled\"\r\n (click)=\"clear(); $event.stopPropagation()\"\r\n title=\"Clear time\"\r\n aria-label=\"Clear time\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"14\"\r\n height=\"14\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n >\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n </button>\r\n <div class=\"custom-time-picker-dropdown\" *ngIf=\"showPicker\">\r\n <div\r\n #tpDropdown\r\n class=\"custom-time-picker\"\r\n [ngStyle]=\"dropdownStyle\"\r\n [class.tp-above]=\"placeAbove\"\r\n [class.append-to-body]=\"appendToBody\"\r\n [ngClass]=\"{\r\n 'left-position': resolvedPosition === 'left',\r\n 'right-position': resolvedPosition === 'right',\r\n 'format-24': timeFormat === 24,\r\n }\"\r\n >\r\n <!-- Hours Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #hourScroll (scroll)=\"onHourScroll()\">\r\n <div\r\n *ngFor=\"let h of getHours()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentHour === h\"\r\n [class.active]=\"activeHour === h\"\r\n (click)=\"onHourChange(h)\"\r\n >\r\n {{ h.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <!-- Minutes Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #minuteScroll (scroll)=\"onMinuteScroll()\">\r\n <div\r\n *ngFor=\"let m of minutes\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentMinute === m\"\r\n [class.active]=\"activeMinute === m\"\r\n (click)=\"onMinuteChange(m)\"\r\n >\r\n {{ m.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n @if (showSeconds) {\r\n <span class=\"time-separator\">:</span>\r\n <!-- Seconds Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #secondScroll (scroll)=\"onSecondScroll()\">\r\n <div\r\n *ngFor=\"let s of seconds\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentSecond === s\"\r\n [class.active]=\"activeSecond === s\"\r\n (click)=\"onSecondChange(s)\"\r\n >\r\n {{ s.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n <!-- AM/PM Column (only in 12-hour format) -->\r\n @if (timeFormat === 12) {\r\n <span class=\"time-separator\">:</span>\r\n <div class=\"time-column ampm-column\">\r\n <div class=\"time-scroll\">\r\n <div\r\n *ngFor=\"let ap of getAMPMOptions()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentAMPM === ap\"\r\n (click)=\"onAMPMChange(ap)\"\r\n >\r\n {{ ap }}\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".time-picker-wrapper{width:100%;font-family:Inter,sans-serif}.time-input-group{display:flex;flex-direction:column;gap:4px}.time-input-group label{font-size:11px;font-weight:500;color:#15191e;text-transform:uppercase;letter-spacing:-.28px}.time-input-wrapper{position:relative;display:flex;align-items:center}.time-input-wrapper.has-value .time-input{padding-right:56px}.time-input{border:1px solid #d1d5db;border-radius:4px;font-family:Inter,sans-serif;color:#707280;background:#fff;transition:all .2s;width:100%;box-sizing:border-box;cursor:pointer}.time-input::placeholder{color:#6b7080}.time-input-group.default .time-input{font-size:12px;padding:6px 40px 5px 12px}.time-input-group.lg .time-input{font-size:14px;line-height:18px;padding:10px 40px 10px 12px}.time-input:focus{outline:none;border-color:#111827}.time-input:hover{border-color:#9ca3af}.time-clear-btn{position:absolute;right:26px;background:none;border:none;font-size:18px;color:#9ca3af;cursor:pointer;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;line-height:1}.time-clear-btn:hover{color:#374151}.time-input-group.default .time-clear-btn{top:5px}.time-input-group.lg .time-clear-btn{top:10px}.time-icon{position:absolute;right:12px;font-size:16px;pointer-events:none;color:#9ca3af;cursor:pointer}.custom-time-picker-wrapper{width:100%;display:flex;justify-content:center}.custom-time-picker{display:flex;align-items:flex-start;gap:8px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:10px;box-shadow:0 4px 12px #00000026;width:182px;position:absolute;top:calc(100% + 4px);z-index:1000}.custom-time-picker.format-24{width:124px}.custom-time-picker.left-position{left:0}.custom-time-picker.right-position{right:0}.custom-time-picker.tp-above:not(.append-to-body){top:auto;bottom:calc(100% + 4px)}.time-column{display:flex;flex-direction:column;position:relative}.time-scroll{display:flex;flex-direction:column;max-height:96px;overflow-y:auto;overflow-x:hidden;scroll-snap-type:y proximity;scrollbar-width:thin;scrollbar-color:#cbd5e1 transparent;scrollbar-width:none;-ms-overflow-style:none}.time-scroll::-webkit-scrollbar{display:none}.time-scroll::-webkit-scrollbar-track{background:transparent}.time-scroll::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#94a3b8}.time-item{min-width:40px;width:40px;height:32px;min-height:32px;scroll-snap-align:center;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:400;color:#374151;cursor:pointer;border-radius:4px;transition:all .15s ease;-webkit-user-select:none;user-select:none;font-family:Inter,sans-serif}.time-item:hover{background:#f3f4f6}.time-item.selected{background:#111827;color:#fff;font-weight:500}.time-item.active:not(.selected){background:#eef2ff;color:#111827;font-weight:500}.ampm-column .time-item{min-width:40px;width:40px}.time-separator{font-size:16px;font-weight:600;color:#6b7280;margin:5px 0 0}.time-input:disabled{cursor:not-allowed;border-color:#e3e3e7;background-color:#f4f4f6;color:#a1a3ae}.time-input-group.default .custom-time-picker{width:fit-content;gap:6px;padding:8px}.time-input-group.default .custom-time-picker.format-24{width:fit-content}.time-input-group.default .time-scroll{max-height:84px}.time-input-group.default .time-item{min-width:26px;width:26px;height:28px;min-height:28px;font-size:12px}.time-input-group.default .ampm-column .time-item{min-width:26px;width:26px}.time-input-group.default .time-separator{font-size:14px}\n"] }]
|
|
511
|
-
}], propDecorators: { required: [{
|
|
563
|
+
], template: "<div class=\"time-picker-wrapper\">\r\n <div class=\"time-input-group\" [ngClass]=\"variation\">\r\n @if (label) {\r\n <label>{{ label }}</label>\r\n }\r\n <div class=\"time-input-wrapper\" [class.has-value]=\"hasValue\" #tpWrapper>\r\n <input\r\n type=\"text\"\r\n class=\"time-input\"\r\n [value]=\"getDisplayValue()\"\r\n [placeholder]=\"placeholder\"\r\n [disabled]=\"disabled ? true : null\"\r\n readonly\r\n (click)=\"!disabled && togglePicker()\"\r\n (blur)=\"markAsTouched()\"\r\n />\r\n <span class=\"time-icon\">\r\n <img alt=\"timer\" class=\"timer-icon\" [src]=\"brickclayIcons.timerIcon\" />\r\n </span>\r\n <button\r\n type=\"button\"\r\n class=\"time-clear-btn\"\r\n *ngIf=\"clearable && hasValue && !disabled\"\r\n (click)=\"clear(); $event.stopPropagation()\"\r\n title=\"Clear time\"\r\n aria-label=\"Clear time\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"14\"\r\n height=\"14\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n >\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n </button>\r\n <div class=\"custom-time-picker-dropdown\" *ngIf=\"showPicker\">\r\n <div\r\n #tpDropdown\r\n class=\"custom-time-picker\"\r\n [ngStyle]=\"dropdownStyle\"\r\n [class.tp-above]=\"placeAbove\"\r\n [class.append-to-body]=\"appendToBody\"\r\n [ngClass]=\"{\r\n 'left-position': resolvedPosition === 'left',\r\n 'right-position': resolvedPosition === 'right',\r\n 'format-24': timeFormat === 24,\r\n 'tp-compact': variation === 'default',\r\n }\"\r\n >\r\n <!-- Hours Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #hourScroll (scroll)=\"onHourScroll()\">\r\n <div\r\n *ngFor=\"let h of getHours()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentHour === h\"\r\n [class.active]=\"activeHour === h\"\r\n (click)=\"onHourChange(h)\"\r\n >\r\n {{ h.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <!-- Minutes Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #minuteScroll (scroll)=\"onMinuteScroll()\">\r\n <div\r\n *ngFor=\"let m of minutes\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentMinute === m\"\r\n [class.active]=\"activeMinute === m\"\r\n (click)=\"onMinuteChange(m)\"\r\n >\r\n {{ m.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n @if (showSeconds) {\r\n <span class=\"time-separator\">:</span>\r\n <!-- Seconds Column (finite list, no loop) -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #secondScroll (scroll)=\"onSecondScroll()\">\r\n <div\r\n *ngFor=\"let s of seconds\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentSecond === s\"\r\n [class.active]=\"activeSecond === s\"\r\n (click)=\"onSecondChange(s)\"\r\n >\r\n {{ s.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n <!-- AM/PM Column (only in 12-hour format) -->\r\n @if (timeFormat === 12) {\r\n <span class=\"time-separator\">:</span>\r\n <div class=\"time-column ampm-column\">\r\n <div class=\"time-scroll\">\r\n <div\r\n *ngFor=\"let ap of getAMPMOptions()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentAMPM === ap\"\r\n (click)=\"onAMPMChange(ap)\"\r\n >\r\n {{ ap }}\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".time-picker-wrapper{width:100%;font-family:Inter,sans-serif}.time-input-group{display:flex;flex-direction:column;gap:4px}.time-input-group label{font-size:11px;font-weight:500;color:#15191e;text-transform:uppercase;letter-spacing:-.28px}.time-input-wrapper{position:relative;display:flex;align-items:center}.time-input-wrapper.has-value .time-input{padding-right:56px}.time-input{border:1px solid #d1d5db;border-radius:4px;font-family:Inter,sans-serif;color:#707280;background:#fff;transition:all .2s;width:100%;box-sizing:border-box;cursor:pointer}.time-input::placeholder{color:#6b7080}.time-input-group.default .time-input{font-size:12px;padding:6px 40px 5px 12px}.time-input-group.lg .time-input{font-size:14px;line-height:18px;padding:10px 40px 10px 12px}.time-input:focus{outline:none;border-color:#111827}.time-input:hover{border-color:#9ca3af}.time-clear-btn{position:absolute;right:26px;background:none;border:none;font-size:18px;color:#9ca3af;cursor:pointer;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;line-height:1}.time-clear-btn:hover{color:#374151}.time-input-group.default .time-clear-btn{top:5px}.time-input-group.lg .time-clear-btn{top:10px}.time-icon{position:absolute;right:12px;font-size:16px;pointer-events:none;color:#9ca3af;cursor:pointer}.custom-time-picker-wrapper{width:100%;display:flex;justify-content:center}.custom-time-picker{display:flex;align-items:flex-start;gap:8px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:10px;box-shadow:0 4px 12px #00000026;width:182px;position:absolute;top:calc(100% + 4px);z-index:1000}.custom-time-picker.format-24{width:124px}.custom-time-picker.left-position{left:0}.custom-time-picker.right-position{right:0}.custom-time-picker.tp-above:not(.append-to-body){top:auto;bottom:calc(100% + 4px)}.time-column{display:flex;flex-direction:column;position:relative}.time-scroll{display:flex;flex-direction:column;max-height:96px;overflow-y:auto;overflow-x:hidden;scroll-snap-type:y proximity;scrollbar-width:thin;scrollbar-color:#cbd5e1 transparent;scrollbar-width:none;-ms-overflow-style:none}.time-scroll::-webkit-scrollbar{display:none}.time-scroll::-webkit-scrollbar-track{background:transparent}.time-scroll::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#94a3b8}.time-item{min-width:40px;width:40px;height:32px;min-height:32px;scroll-snap-align:center;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:400;color:#374151;cursor:pointer;border-radius:4px;transition:all .15s ease;-webkit-user-select:none;user-select:none;font-family:Inter,sans-serif}.time-item:hover{background:#f3f4f6}.time-item.selected{background:#111827;color:#fff;font-weight:500}.time-item.active:not(.selected){background:#eef2ff;color:#111827;font-weight:500}.ampm-column .time-item{min-width:40px;width:40px}.time-separator{font-size:16px;font-weight:600;color:#6b7280;margin:5px 0 0}.time-input:disabled{cursor:not-allowed;border-color:#e3e3e7;background-color:#f4f4f6;color:#a1a3ae}.time-input-group.default .custom-time-picker{width:fit-content;gap:6px;padding:8px}.time-input-group.default .custom-time-picker.format-24{width:fit-content}.time-input-group.default .time-scroll{max-height:84px}.time-input-group.default .time-item{min-width:26px;width:26px;height:28px;min-height:28px;font-size:12px}.time-input-group.default .ampm-column .time-item{min-width:26px;width:26px}.time-input-group.default .time-separator{font-size:14px}.custom-time-picker.append-to-body{font-family:Inter,sans-serif}.custom-time-picker.tp-compact{width:fit-content;gap:6px;padding:8px}.custom-time-picker.tp-compact.format-24{width:fit-content}.custom-time-picker.tp-compact .time-scroll{max-height:84px}.custom-time-picker.tp-compact .time-item{min-width:26px;width:26px;height:28px;min-height:28px;font-size:12px}.custom-time-picker.tp-compact .ampm-column .time-item{min-width:26px;width:26px}.custom-time-picker.tp-compact .time-separator{font-size:14px}\n"] }]
|
|
564
|
+
}], ctorParameters: () => [{ type: i0.Renderer2 }], propDecorators: { required: [{
|
|
512
565
|
type: Input
|
|
513
566
|
}], value: [{
|
|
514
567
|
type: Input
|
|
@@ -686,6 +739,7 @@ class CalendarSelection {
|
|
|
686
739
|
}
|
|
687
740
|
class BkCustomCalendar {
|
|
688
741
|
calendarManager;
|
|
742
|
+
renderer;
|
|
689
743
|
// Basic Options
|
|
690
744
|
enableTimepicker = false;
|
|
691
745
|
autoApply = false;
|
|
@@ -798,8 +852,52 @@ class BkCustomCalendar {
|
|
|
798
852
|
unregisterFn;
|
|
799
853
|
closeAllSubscription;
|
|
800
854
|
closeFn;
|
|
801
|
-
constructor(calendarManager) {
|
|
855
|
+
constructor(calendarManager, renderer) {
|
|
802
856
|
this.calendarManager = calendarManager;
|
|
857
|
+
this.renderer = renderer;
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* appendToBody popup relocation.
|
|
861
|
+
*
|
|
862
|
+
* `appendToBody` uses `position: fixed`, which only anchors to the viewport when NO ancestor
|
|
863
|
+
* establishes a containing block (transform / filter / perspective / will-change / contain /
|
|
864
|
+
* backdrop-filter). Consumers frequently place the calendar inside such an ancestor (an animated
|
|
865
|
+
* panel, a CSS-transformed modal, etc.), which silently re-anchors the fixed popup and pushes it
|
|
866
|
+
* off-position. To be robust everywhere we physically move the popup node to <body> while it is
|
|
867
|
+
* open, then move it back before Angular's *ngIf tears it down. Angular keeps managing the node by
|
|
868
|
+
* reference after the move, so its event bindings, change detection, and emulated-encapsulation
|
|
869
|
+
* styles all continue to work.
|
|
870
|
+
*/
|
|
871
|
+
popupMovedToBody = false;
|
|
872
|
+
popupOriginalParent = null;
|
|
873
|
+
popupOriginalNextSibling = null;
|
|
874
|
+
movePopupToBody() {
|
|
875
|
+
const el = this.calendarPopupRef?.nativeElement;
|
|
876
|
+
if (!el || this.popupMovedToBody)
|
|
877
|
+
return;
|
|
878
|
+
this.popupOriginalParent = el.parentElement;
|
|
879
|
+
this.popupOriginalNextSibling = el.nextSibling;
|
|
880
|
+
this.renderer.appendChild(document.body, el);
|
|
881
|
+
this.popupMovedToBody = true;
|
|
882
|
+
}
|
|
883
|
+
/** Return the popup to its original DOM slot so *ngIf can destroy it cleanly (safe to call twice). */
|
|
884
|
+
restorePopupFromBody() {
|
|
885
|
+
if (!this.popupMovedToBody)
|
|
886
|
+
return;
|
|
887
|
+
const el = this.calendarPopupRef?.nativeElement;
|
|
888
|
+
const parent = this.popupOriginalParent;
|
|
889
|
+
if (el && parent) {
|
|
890
|
+
const ref = this.popupOriginalNextSibling;
|
|
891
|
+
if (ref && ref.parentNode === parent) {
|
|
892
|
+
this.renderer.insertBefore(parent, el, ref);
|
|
893
|
+
}
|
|
894
|
+
else {
|
|
895
|
+
this.renderer.appendChild(parent, el);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
this.popupMovedToBody = false;
|
|
899
|
+
this.popupOriginalParent = null;
|
|
900
|
+
this.popupOriginalNextSibling = null;
|
|
803
901
|
}
|
|
804
902
|
/** Weekday headers; falls back to Mon–Sun if the input is not length 7. */
|
|
805
903
|
get resolvedWeekDayLabels() {
|
|
@@ -837,6 +935,9 @@ class BkCustomCalendar {
|
|
|
837
935
|
return;
|
|
838
936
|
this.detachViewportListeners();
|
|
839
937
|
this.revertSingleDateDraftIfNeeded();
|
|
938
|
+
// Put the popup back in its original slot BEFORE show=false so *ngIf removes it from the right
|
|
939
|
+
// parent on the next change-detection pass (avoids leaving an orphan node in <body>).
|
|
940
|
+
this.restorePopupFromBody();
|
|
840
941
|
this.show = false;
|
|
841
942
|
this.onTouched();
|
|
842
943
|
this.closed.emit();
|
|
@@ -1070,8 +1171,14 @@ class BkCustomCalendar {
|
|
|
1070
1171
|
if (this.inline) {
|
|
1071
1172
|
return;
|
|
1072
1173
|
}
|
|
1174
|
+
if (!this.show)
|
|
1175
|
+
return;
|
|
1073
1176
|
const target = event.target;
|
|
1074
|
-
|
|
1177
|
+
// When appendToBody has relocated the popup to <body>, it is no longer a descendant of
|
|
1178
|
+
// .calendar-container, so also treat clicks within the popup element itself as "inside".
|
|
1179
|
+
const popupEl = this.calendarPopupRef?.nativeElement;
|
|
1180
|
+
const insidePopup = !!popupEl && (popupEl === target || popupEl.contains(target));
|
|
1181
|
+
if (!target.closest('.calendar-container') && !insidePopup) {
|
|
1075
1182
|
this.close();
|
|
1076
1183
|
}
|
|
1077
1184
|
}
|
|
@@ -1228,6 +1335,9 @@ class BkCustomCalendar {
|
|
|
1228
1335
|
ngOnDestroy() {
|
|
1229
1336
|
// Ensure global scroll/resize listeners never outlive the component
|
|
1230
1337
|
this.detachViewportListeners();
|
|
1338
|
+
// If destroyed while open with the popup relocated to <body>, put it back so Angular's teardown
|
|
1339
|
+
// removes it from the expected parent instead of leaving an orphan node in <body>.
|
|
1340
|
+
this.restorePopupFromBody();
|
|
1231
1341
|
// Unregister this calendar instance
|
|
1232
1342
|
if (this.unregisterFn) {
|
|
1233
1343
|
this.unregisterFn();
|
|
@@ -1315,6 +1425,9 @@ class BkCustomCalendar {
|
|
|
1315
1425
|
this.initKeyboardFocus();
|
|
1316
1426
|
this.calendarPopupRef?.nativeElement?.focus({ preventScroll: true });
|
|
1317
1427
|
if (this.appendToBody && this.inputWrapper?.nativeElement) {
|
|
1428
|
+
// Move the popup out to <body> first so any transformed/overflow ancestor can no longer
|
|
1429
|
+
// re-anchor or clip its fixed positioning, then compute viewport coordinates.
|
|
1430
|
+
this.movePopupToBody();
|
|
1318
1431
|
this.updatePosition();
|
|
1319
1432
|
}
|
|
1320
1433
|
else if (!this.inline) {
|
|
@@ -2931,7 +3044,7 @@ class BkCustomCalendar {
|
|
|
2931
3044
|
const dd = date.getDate().toString().padStart(2, '0');
|
|
2932
3045
|
return `${yyyy}-${mm}-${dd}`;
|
|
2933
3046
|
}
|
|
2934
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkCustomCalendar, deps: [{ token: BkCalendarManagerService }], target: i0.ɵɵFactoryTarget.Component });
|
|
3047
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkCustomCalendar, deps: [{ token: BkCalendarManagerService }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
|
|
2935
3048
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: BkCustomCalendar, isStandalone: true, selector: "bk-custom-calendar", inputs: { enableTimepicker: "enableTimepicker", autoApply: "autoApply", closeOnAutoApply: "closeOnAutoApply", showCancel: "showCancel", linkedCalendars: "linkedCalendars", singleDatePicker: "singleDatePicker", showWeekNumbers: "showWeekNumbers", showISOWeekNumbers: "showISOWeekNumbers", customRangeDirection: "customRangeDirection", lockStartDate: "lockStartDate", position: "position", popupPosition: "popupPosition", drop: "drop", dualCalendar: "dualCalendar", showRanges: "showRanges", timeFormat: "timeFormat", clearableTime: "clearableTime", enableSeconds: "enableSeconds", customRanges: "customRanges", weekDayLabels: "weekDayLabels", multiDateSelection: "multiDateSelection", maxDate: "maxDate", minDate: "minDate", placeholder: "placeholder", opens: "opens", inline: "inline", compact: "compact", autoPosition: "autoPosition", appendToBody: "appendToBody", isDisplayCrossIcon: "isDisplayCrossIcon", hasError: "hasError", errorMessage: "errorMessage", showCancelApply: "showCancelApply", selectedValue: "selectedValue", displayFormat: "displayFormat", required: "required", rangeOrder: "rangeOrder" }, outputs: { selected: "selected", opened: "opened", closed: "closed" }, host: { listeners: { "document:click": "onClickOutside($event)" } }, providers: [
|
|
2936
3049
|
{
|
|
2937
3050
|
provide: NG_VALUE_ACCESSOR,
|
|
@@ -2943,7 +3056,7 @@ class BkCustomCalendar {
|
|
|
2943
3056
|
useExisting: forwardRef(() => BkCustomCalendar),
|
|
2944
3057
|
multi: true,
|
|
2945
3058
|
},
|
|
2946
|
-
], viewQueries: [{ propertyName: "inputWrapper", first: true, predicate: ["inputWrapper"], descendants: true }, { propertyName: "calendarPopupRef", first: true, predicate: ["calendarPopup"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"calendar-container relative\" [class.open]=\"show\" [class.inline-mode]=\"inline\" [class.disabled]=\"disabled\">\r\n <!-- Input field -->\r\n <div #inputWrapper class=\"input-wrapper\" *ngIf=\"!inline\">\r\n <input\r\n type=\"text\"\r\n (click)=\"!disabled && toggle()\"\r\n (keydown.enter)=\"$event.preventDefault()\"\r\n (blur)=\"markAsTouched()\"\r\n readonly\r\n [value]=\"getDisplayValue()\"\r\n [placeholder]=\"placeholder\"\r\n [attr.disabled]=\"disabled ? true : null\"\r\n [class.hasError]=\"hasError\"\r\n class=\"calendar-input\">\r\n <!-- *ngIf=\"!getDisplayValue()\" -->\r\n\r\n <span class=\"calendar-icon\" >\r\n <img alt=\"calendar\" class=\"calendar-icon-img\" [src]='brickclayIcons.calenderIcon'/>\r\n </span>\r\n <button type=\"button\" class=\"clear-btn\" *ngIf=\"getDisplayValue() && isDisplayCrossIcon && !disabled\" (click)=\"clear(); $event.stopPropagation()\" title=\"Clear\">\u00D7</button>\r\n </div>\r\n\r\n <!-- Calendar Popup / Inline -->\r\n <div #calendarPopup\r\n class=\"calendar-popup\"\r\n [class.inline-calendar]=\"inline\"\r\n [class.append-to-body]=\"appendToBody && !inline\"\r\n [style.position]=\"appendToBody && !inline ? 'fixed' : null\"\r\n [style.top]=\"appendToBody && !inline && !popupPlacementAbove ? dropdownStyle.top : null\"\r\n [style.bottom]=\"appendToBody && !inline && popupPlacementAbove ? dropdownStyle.bottom : null\"\r\n [style.left]=\"appendToBody && !inline ? dropdownStyle.left : null\"\r\n tabindex=\"0\"\r\n (keydown)=\"onCalendarPopupKeydown($event)\"\r\n [ngClass]=\"{\r\n 'position-right': !inline && !appendToBody && opens === 'right',\r\n 'position-center': !inline && !appendToBody && opens === 'center',\r\n 'drop-up': !inline && popupPlacementAbove,\r\n 'has-ranges': showRanges && customRanges,\r\n 'dual-calendar-mode': dualCalendar,\r\n 'compact': compact\r\n }\"\r\n *ngIf=\"inline || show\">\r\n\r\n <!-- RANGES -->\r\n <div class=\"ranges\" *ngIf=\"showRanges && customRanges\" role=\"listbox\" aria-label=\"Date ranges\">\r\n <button\r\n type=\"button\"\r\n *ngFor=\"let rangeKey of rangeOrder\"\r\n (click)=\"chooseRange(rangeKey)\"\r\n (keydown)=\"onRangeButtonKeydown($event, rangeKey)\"\r\n [class.active]=\"activeRange === rangeKey\"\r\n [class.custom-range]=\"rangeKey === 'Custom Range'\"\r\n class=\"range-btn\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"activeRange === rangeKey\">\r\n {{ rangeKey }}\r\n </button>\r\n </div>\r\n<div class=\"\" [ngClass]=\"showRanges ? 'w-100 flex-grow-1 border-l border-[#eee]' : ''\">\r\n\r\n\r\n <!-- SINGLE CALENDAR -->\r\n <div *ngIf=\"!dualCalendar\" class=\"calendar-wrapper\">\r\n <div class=\"header\">\r\n <!-- <button (click)=\"prevMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-left-gray.svg\" alt=\"arrow-left\" class=\"arrow-left\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"prevMonth()\" matTooltip=\"Prev month\"\r\n >\r\n <img alt=\"prev\" class=\"h-3 w-3\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(month) }} {{ year }}</span>\r\n <!-- <button (click)=\"nextMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-right-gray.svg\" alt=\"arrow-right\" class=\"arrow-right\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"nextMonth()\" matTooltip=\"Next month\"\r\n >\r\n <img alt=\"next\" class=\"h-3 w-3\" [src]='brickclayIcons.arrowRight'/>\r\n <!--<img src=\"assets/calender/pagination-right-gray.svg\" alt=\"next\" class=\"h-3 w-3\" /> -->\r\n </button>\r\n </div>\r\n\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(month) + ' ' + year\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of calendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && selectDate(dayObj.day)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(year, month, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(year, month, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(year, month, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(year, month, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(year, month, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(year, month, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Single Calendar Time Picker -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"single-time\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"singleTimeModel\"\r\n (ngModelChange)=\"onSingleTimePickerChange($event); singleTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('single-time')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- DUAL CALENDAR -->\r\n <div class=\"dual-calendar\" *ngIf=\"dualCalendar\">\r\n <!-- LEFT CALENDAR -->\r\n <div class=\"calendar-left\">\r\n <div class=\"header\">\r\n <button (click)=\"prevLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-left\" class=\"arrow-left\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(leftMonth) }} {{ leftYear }}</span>\r\n <button (click)=\"nextLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-right\" class=\"arrow-right\" [src]='brickclayIcons.arrowRight'/>\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(leftMonth) + ' ' + leftYear\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of leftCalendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && selectDate(dayObj.day, false)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(leftYear, leftMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(leftYear, leftMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(leftYear, leftMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(leftYear, leftMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(leftYear, leftMonth, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(leftYear, leftMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Start Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Start Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"dual-start\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"startTimeModel\"\r\n (ngModelChange)=\"onDualTimePickerChange($event, true); startTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('dual-start')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- RIGHT CALENDAR -->\r\n <div class=\"calendar-right\">\r\n <div class=\"header\">\r\n <button (click)=\"prevRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-left\" class=\"arrow-left\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(rightMonth) }} {{ rightYear }}</span>\r\n <button (click)=\"nextRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-right\" class=\"arrow-right\" [src]='brickclayIcons.arrowRight'/>\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(rightMonth) + ' ' + rightYear\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of rightCalendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && selectDate(dayObj.day, true)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && onDateHover(dayObj.day, true)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(rightYear, rightMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(rightYear, rightMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(rightYear, rightMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(rightYear, rightMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(rightYear, rightMonth, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(rightYear, rightMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- End Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">End Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"dual-end\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"endTimeModel\"\r\n (ngModelChange)=\"onDualTimePickerChange($event, false); endTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('dual-end')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- FOOTER -->\r\n <div class=\"footer\" *ngIf=\"!inline && showCancelApply\">\r\n <button *ngIf=\"showCancel\" (click)=\"cancel()\" class=\"btn-cancel\" type=\"button\">Cancel</button>\r\n <button (click)=\"apply()\" class=\"btn-apply\" type=\"button\" [disabled]=\"disabled || isDualRangeApplyBlocked\">Apply</button>\r\n </div>\r\n\r\n </div>\r\n\r\n </div>\r\n</div>\r\n\r\n@if (hasError){\r\n<p class=\"calender-error\">{{errorMessage}}</p>\r\n}\r\n", styles: [".calendar-container,.calendar-container *{font-family:Inter,sans-serif!important}.calendar-container{position:relative;display:inline-block;width:100%}.input-wrapper{position:relative;display:flex;align-items:center}.calendar-input{width:100%;padding:9px 14px 9px 40px;border:1px solid #ddd;border-radius:8px;font-size:14px;cursor:pointer;background:#fff;transition:all .2s}.calendar-input:hover{border-color:#999}.calendar-input:focus{outline:none;border-color:#999}.calendar-icon{position:absolute;left:12px;pointer-events:none;font-size:18px}.clear-btn{position:absolute;right:9px;background:none;border:none;font-size:20px;color:#999;cursor:pointer;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;line-height:1;transition:color .2s;top:8px}.clear-btn:hover{color:#333}.calendar-popup{position:absolute;top:110%;left:0;width:320px;background:#fff;border-radius:12px;box-shadow:0 10px 40px #00000026;z-index:1000;animation:slideDown .2s ease-out}.calendar-popup:focus-visible{outline:0!important}.calendar-popup.inline-calendar{position:relative;top:0;left:0;width:100%;margin-top:0;animation:none;box-shadow:0 2px 8px #0000001a}.calendar-container.inline-mode{display:block;width:100%}.calendar-popup.dual-calendar-mode{width:600px}.calendar-popup.dual-calendar-mode.has-ranges{width:730px}.calendar-popup.has-ranges{width:450px}.calendar-popup.drop-up{top:auto;bottom:110%;animation:slideUp .2s ease-out}.calendar-popup.position-right{left:auto;right:0}.calendar-popup.position-center{left:50%;transform:translate(-50%)}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.ranges{display:flex;flex-direction:column;gap:4px;margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid #eee;min-width:150px;padding-right:8px}.range-btn{padding:7px 10px;border:1px solid transparent;background:transparent;border-radius:4px;cursor:pointer;text-align:left;font-size:14px;transition:all .2s;color:#838383;font-weight:500}.range-btn:hover{background:#f5f5f5;color:#000}.range-btn.active{background:#f0f0f0;color:#000;font-weight:500}.calendar-wrapper{padding:0 12px 12px}.header{display:flex;justify-content:space-between;align-items:center;padding:12px 0}.month-year{font-size:15px;font-weight:500;color:#333;flex:1;text-align:center;text-transform:capitalize}.nav-btn{background:none;border:none;font-size:24px;cursor:pointer;padding:11.5px 14px;color:#666;border-radius:4px;transition:all .2s;line-height:1;height:30px;width:30px;display:flex;justify-content:center;align-items:center}.nav-btn:hover{background:#f0f0f0;color:#000}.nav-btn img{width:auto;max-width:none!important}.calendar-table{width:100%;border-collapse:collapse;text-align:center}.weekday-header{font-size:12px;color:#7e7e7e;font-weight:600;padding:8px 4px;letter-spacing:.3px}.calendar-day{padding:8px 4px;font-size:14px;cursor:pointer;border-radius:6px;transition:all .2s;position:relative;color:#333;font-weight:500;line-height:1.5}.calendar-day:hover:not(.disabled):not(.other-month){background:#efefef;color:#000}.calendar-day.other-month{color:#ccc;cursor:default}.calendar-day.disabled{color:#ddd;cursor:not-allowed;opacity:.5}.calendar-day.active{background:#000!important;color:#fff!important;font-weight:600}.calendar-day.today{font-weight:600}.calendar-day.today:not(.active){background:#e5e4e4}.calendar-day.active:hover{background:#000!important}.calendar-day.in-range{background:#f5f5f5;color:#333;border-radius:0;position:relative}.calendar-day.in-range:hover{background:#e8e8e8}.calendar-day.in-range:before{content:\"\";position:absolute;inset:0;background:#f5f5f5;z-index:-1}.calendar-day.in-range:hover:before{background:#e8e8e8}.calendar-day.multi-selected{background:#4caf50;color:#fff;font-weight:600;border-radius:6px}.calendar-day.multi-selected:hover{background:#45a049}.dual-calendar{display:flex;width:100%}.calendar-left,.calendar-right{flex:1;min-width:0;padding:0 12px 12px}.calendar-popup.has-ranges{display:flex;flex-direction:row}.calendar-popup.has-ranges .ranges{margin-bottom:0;border-bottom:none;padding:10px}.calendar-popup.has-ranges .dual-calendar,.calendar-popup.has-ranges .calendar-wrapper{flex:1}.calendar-right .header{justify-content:space-between}.calendar-right .header .month-year{text-align:center;flex:1}.timepicker-section{margin-top:12px;padding-top:12px;border-top:1px solid #eee}.timepicker-label{font-size:12px;font-weight:500;color:#000;margin-bottom:4px;letter-spacing:-.28px}.custom-time-picker{display:flex;flex-direction:column;gap:8px;align-items:start}.time-input-group{display:flex;align-items:center;justify-content:center;gap:8px;background:#f8f9fa;padding:12px;border-radius:8px;border:1px solid #e0e0e0}.time-control{display:flex;flex-direction:column;align-items:center}.time-btn{background:#fff;border:1px solid #ddd;width:28px;height:20px;cursor:pointer;font-size:10px;color:#666;border-radius:4px;transition:all .2s;display:flex;align-items:center;justify-content:center;padding:0;line-height:1}.time-btn:hover{background:#e4e4e4;color:#fff;border-color:#e4e4e4}.time-btn.up{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom:none}.time-btn.down{border-top-left-radius:0;border-top-right-radius:0;border-top:none}.time-input{width:40px;height:32px;text-align:center;border:1px solid #ddd;border-radius:4px;font-size:16px;font-weight:600;background:#fff;color:#333}.time-separator{font-size:18px;font-weight:600;color:#666;margin:0 2px}.ampm-control{display:flex;flex-direction:column;gap:4px;margin-left:8px}.ampm-btn{padding:6px 12px;border:1px solid #ddd;background:#fff;border-radius:4px;cursor:pointer;font-size:12px;font-weight:600;color:#666;transition:all .2s;min-width:45px}.ampm-btn:hover{background:#f0f0f0}.ampm-btn.active{background:#000;color:#fff;border-color:#000}.html5-time-input{margin-top:8px;padding:8px;border:1px solid #ddd;border-radius:6px;font-size:14px;width:100%;max-width:120px}.footer{padding:12px;display:flex;justify-content:flex-end;gap:8px;border-top:1px solid #eee}.btn-cancel,.btn-apply{padding:8px 16px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;min-width:80px}.btn-cancel{background:#fff;color:#666;border:1px solid #ddd}.btn-apply{background:#000;color:#fff}.btn-apply:active{transform:translateY(0)}@media (max-width: 768px){.calendar-popup{width:100%;max-width:320px;max-height:300px;overflow:auto}.calendar-popup.dual-calendar-mode{width:100%;max-width:100%}.calendar-popup.has-ranges{flex-direction:column}.calendar-popup.has-ranges .ranges{border-right:none;border-bottom:1px solid #eee;padding-right:0;margin-right:0;padding-bottom:16px;margin-bottom:16px}.dual-calendar{flex-direction:column}.time-input-group{flex-wrap:wrap;justify-content:center}}.ranges::-webkit-scrollbar{width:6px}.ranges::-webkit-scrollbar-track{background:#f1f1f1;border-radius:3px}.ranges::-webkit-scrollbar-thumb{background:#888;border-radius:3px}.ranges::-webkit-scrollbar-thumb:hover{background:#555}.w-100{width:100%}.flex-grow-1{flex-grow:1}.calendar-input.calendar-input-has-error{@apply border-[#d11e14];}.calendar-input:disabled{cursor:not-allowed;border-color:#e3e3e7;background-color:#f4f4f6;color:#a1a3ae}.btn-apply:disabled{cursor:not-allowed!important;opacity:.7}.calendar-day.calendar-day-keyboard-focus:not(.disabled):not(.other-month){outline:0px solid #000}.calendar-popup.compact{width:240px}.calendar-popup.compact.has-ranges{width:380px}.calendar-popup.compact.dual-calendar-mode{width:480px}.calendar-popup.compact.dual-calendar-mode.has-ranges{width:600px}.calendar-popup.compact .header{padding:8px 0}.calendar-popup.compact .month-year{font-size:13px}.calendar-popup.compact .nav-btn{height:24px;width:24px;padding:6px 8px}.calendar-popup.compact .calendar-wrapper,.calendar-popup.compact .calendar-left,.calendar-popup.compact .calendar-right{padding:0 8px 8px}.calendar-popup.compact .weekday-header{font-size:11px;padding:4px 2px}.calendar-popup.compact .calendar-day{font-size:12px;padding:5px 2px;line-height:1.3}.calendar-popup.compact .ranges{min-width:120px;gap:2px;margin-bottom:8px;padding-bottom:8px}.calendar-popup.compact.has-ranges .ranges{padding:6px}.calendar-popup.compact .range-btn{font-size:12px;padding:5px 8px}.calendar-popup.compact .timepicker-section{margin-top:8px;padding-top:8px}.calendar-popup.compact .timepicker-label{font-size:11px}.calendar-popup.compact .footer{padding:8px;gap:6px}.calendar-popup.compact .btn-cancel,.calendar-popup.compact .btn-apply{font-size:11px;padding:6px 12px;min-width:64px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: BkTimePicker, selector: "bk-time-picker", inputs: ["required", "value", "label", "placeholder", "clearable", "position", "variation", "pickerId", "closePicker", "timeFormat", "showSeconds", "autoPosition", "appendToBody", "disabled"], outputs: ["change", "timeChange", "pickerOpened", "pickerClosed"] }] });
|
|
3059
|
+
], viewQueries: [{ propertyName: "inputWrapper", first: true, predicate: ["inputWrapper"], descendants: true }, { propertyName: "calendarPopupRef", first: true, predicate: ["calendarPopup"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"calendar-container relative\" [class.open]=\"show\" [class.inline-mode]=\"inline\" [class.disabled]=\"disabled\">\r\n <!-- Input field -->\r\n <div #inputWrapper class=\"input-wrapper\" *ngIf=\"!inline\">\r\n <input\r\n type=\"text\"\r\n (click)=\"!disabled && toggle()\"\r\n (keydown.enter)=\"$event.preventDefault()\"\r\n (blur)=\"markAsTouched()\"\r\n readonly\r\n [value]=\"getDisplayValue()\"\r\n [placeholder]=\"placeholder\"\r\n [attr.disabled]=\"disabled ? true : null\"\r\n [class.hasError]=\"hasError\"\r\n class=\"calendar-input\">\r\n <!-- *ngIf=\"!getDisplayValue()\" -->\r\n\r\n <span class=\"calendar-icon\" >\r\n <img alt=\"calendar\" class=\"calendar-icon-img\" [src]='brickclayIcons.calenderIcon'/>\r\n </span>\r\n <button type=\"button\" class=\"clear-btn\" *ngIf=\"getDisplayValue() && isDisplayCrossIcon && !disabled\" (click)=\"clear(); $event.stopPropagation()\" title=\"Clear\">\u00D7</button>\r\n </div>\r\n\r\n <!-- Calendar Popup / Inline -->\r\n <div #calendarPopup\r\n class=\"calendar-popup\"\r\n [class.inline-calendar]=\"inline\"\r\n [class.append-to-body]=\"appendToBody && !inline\"\r\n [style.position]=\"appendToBody && !inline ? 'fixed' : null\"\r\n [style.top]=\"appendToBody && !inline && !popupPlacementAbove ? dropdownStyle.top : null\"\r\n [style.bottom]=\"appendToBody && !inline && popupPlacementAbove ? dropdownStyle.bottom : null\"\r\n [style.left]=\"appendToBody && !inline ? dropdownStyle.left : null\"\r\n tabindex=\"0\"\r\n (keydown)=\"onCalendarPopupKeydown($event)\"\r\n [ngClass]=\"{\r\n 'position-right': !inline && !appendToBody && opens === 'right',\r\n 'position-center': !inline && !appendToBody && opens === 'center',\r\n 'drop-up': !inline && popupPlacementAbove,\r\n 'has-ranges': showRanges && customRanges,\r\n 'dual-calendar-mode': dualCalendar,\r\n 'compact': compact\r\n }\"\r\n *ngIf=\"inline || show\">\r\n\r\n <!-- RANGES -->\r\n <div class=\"ranges\" *ngIf=\"showRanges && customRanges\" role=\"listbox\" aria-label=\"Date ranges\">\r\n <button\r\n type=\"button\"\r\n *ngFor=\"let rangeKey of rangeOrder\"\r\n (click)=\"chooseRange(rangeKey)\"\r\n (keydown)=\"onRangeButtonKeydown($event, rangeKey)\"\r\n [class.active]=\"activeRange === rangeKey\"\r\n [class.custom-range]=\"rangeKey === 'Custom Range'\"\r\n class=\"range-btn\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"activeRange === rangeKey\">\r\n {{ rangeKey }}\r\n </button>\r\n </div>\r\n<div class=\"\" [ngClass]=\"showRanges ? 'w-100 flex-grow-1 border-l border-[#eee]' : ''\">\r\n\r\n\r\n <!-- SINGLE CALENDAR -->\r\n <div *ngIf=\"!dualCalendar\" class=\"calendar-wrapper\">\r\n <div class=\"header\">\r\n <!-- <button (click)=\"prevMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-left-gray.svg\" alt=\"arrow-left\" class=\"arrow-left\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"prevMonth()\" matTooltip=\"Prev month\"\r\n >\r\n <img alt=\"prev\" class=\"h-3 w-3\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(month) }} {{ year }}</span>\r\n <!-- <button (click)=\"nextMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-right-gray.svg\" alt=\"arrow-right\" class=\"arrow-right\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"nextMonth()\" matTooltip=\"Next month\"\r\n >\r\n <img alt=\"next\" class=\"h-3 w-3\" [src]='brickclayIcons.arrowRight'/>\r\n <!--<img src=\"assets/calender/pagination-right-gray.svg\" alt=\"next\" class=\"h-3 w-3\" /> -->\r\n </button>\r\n </div>\r\n\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(month) + ' ' + year\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of calendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && selectDate(dayObj.day)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(year, month, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(year, month, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(year, month, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(year, month, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(year, month, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(year, month, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Single Calendar Time Picker -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"single-time\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"singleTimeModel\"\r\n (ngModelChange)=\"onSingleTimePickerChange($event); singleTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('single-time')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- DUAL CALENDAR -->\r\n <div class=\"dual-calendar\" *ngIf=\"dualCalendar\">\r\n <!-- LEFT CALENDAR -->\r\n <div class=\"calendar-left\">\r\n <div class=\"header\">\r\n <button (click)=\"prevLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-left\" class=\"arrow-left\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(leftMonth) }} {{ leftYear }}</span>\r\n <button (click)=\"nextLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-right\" class=\"arrow-right\" [src]='brickclayIcons.arrowRight'/>\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(leftMonth) + ' ' + leftYear\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of leftCalendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && selectDate(dayObj.day, false)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(leftYear, leftMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(leftYear, leftMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(leftYear, leftMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(leftYear, leftMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(leftYear, leftMonth, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(leftYear, leftMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Start Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Start Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"dual-start\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"startTimeModel\"\r\n (ngModelChange)=\"onDualTimePickerChange($event, true); startTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('dual-start')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- RIGHT CALENDAR -->\r\n <div class=\"calendar-right\">\r\n <div class=\"header\">\r\n <button (click)=\"prevRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-left\" class=\"arrow-left\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(rightMonth) }} {{ rightYear }}</span>\r\n <button (click)=\"nextRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-right\" class=\"arrow-right\" [src]='brickclayIcons.arrowRight'/>\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(rightMonth) + ' ' + rightYear\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of rightCalendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && selectDate(dayObj.day, true)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && onDateHover(dayObj.day, true)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(rightYear, rightMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(rightYear, rightMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(rightYear, rightMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(rightYear, rightMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(rightYear, rightMonth, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(rightYear, rightMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- End Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">End Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"dual-end\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"endTimeModel\"\r\n (ngModelChange)=\"onDualTimePickerChange($event, false); endTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('dual-end')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- FOOTER -->\r\n <div class=\"footer\" *ngIf=\"!inline && showCancelApply\">\r\n <button *ngIf=\"showCancel\" (click)=\"cancel()\" class=\"btn-cancel\" type=\"button\">Cancel</button>\r\n <button (click)=\"apply()\" class=\"btn-apply\" type=\"button\" [disabled]=\"disabled || isDualRangeApplyBlocked\">Apply</button>\r\n </div>\r\n\r\n </div>\r\n\r\n </div>\r\n</div>\r\n\r\n@if (hasError){\r\n<p class=\"calender-error\">{{errorMessage}}</p>\r\n}\r\n", styles: [".calendar-container,.calendar-container *{font-family:Inter,sans-serif!important}.calendar-container{position:relative;display:inline-block;width:100%}.input-wrapper{position:relative;display:flex;align-items:center}.calendar-input{width:100%;padding:9px 14px 9px 40px;border:1px solid #ddd;border-radius:8px;font-size:14px;cursor:pointer;background:#fff;transition:all .2s}.calendar-input:hover{border-color:#999}.calendar-input:focus{outline:none;border-color:#999}.calendar-icon{position:absolute;left:12px;pointer-events:none;font-size:18px}.clear-btn{position:absolute;right:9px;background:none;border:none;font-size:20px;color:#999;cursor:pointer;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;line-height:1;transition:color .2s;top:8px}.clear-btn:hover{color:#333}.calendar-popup{position:absolute;top:110%;left:0;width:320px;background:#fff;border-radius:12px;box-shadow:0 10px 40px #00000026;z-index:1000;animation:slideDown .2s ease-out}.calendar-popup:focus-visible{outline:0!important}.calendar-popup.append-to-body{font-family:Inter,sans-serif;z-index:100000}.calendar-popup.inline-calendar{position:relative;top:0;left:0;width:100%;margin-top:0;animation:none;box-shadow:0 2px 8px #0000001a}.calendar-container.inline-mode{display:block;width:100%}.calendar-popup.dual-calendar-mode{width:600px}.calendar-popup.dual-calendar-mode.has-ranges{width:730px}.calendar-popup.has-ranges{width:450px}.calendar-popup.drop-up{top:auto;bottom:110%;animation:slideUp .2s ease-out}.calendar-popup.position-right{left:auto;right:0}.calendar-popup.position-center{left:50%;transform:translate(-50%)}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.ranges{display:flex;flex-direction:column;gap:4px;margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid #eee;min-width:150px;padding-right:8px}.range-btn{padding:7px 10px;border:1px solid transparent;background:transparent;border-radius:4px;cursor:pointer;text-align:left;font-size:14px;transition:all .2s;color:#838383;font-weight:500}.range-btn:hover{background:#f5f5f5;color:#000}.range-btn.active{background:#f0f0f0;color:#000;font-weight:500}.calendar-wrapper{padding:0 12px 12px}.header{display:flex;justify-content:space-between;align-items:center;padding:12px 0}.month-year{font-size:15px;font-weight:500;color:#333;flex:1;text-align:center;text-transform:capitalize}.nav-btn{background:none;border:none;font-size:24px;cursor:pointer;padding:11.5px 14px;color:#666;border-radius:4px;transition:all .2s;line-height:1;height:30px;width:30px;display:flex;justify-content:center;align-items:center}.nav-btn:hover{background:#f0f0f0;color:#000}.nav-btn img{width:auto;max-width:none!important}.calendar-table{width:100%;border-collapse:collapse;text-align:center}.weekday-header{font-size:12px;color:#7e7e7e;font-weight:600;padding:8px 4px;letter-spacing:.3px}.calendar-day{padding:8px 4px;font-size:14px;cursor:pointer;border-radius:6px;transition:all .2s;position:relative;color:#333;font-weight:500;line-height:1.5}.calendar-day:hover:not(.disabled):not(.other-month){background:#efefef;color:#000}.calendar-day.other-month{color:#ccc;cursor:default}.calendar-day.disabled{color:#ddd;cursor:not-allowed;opacity:.5}.calendar-day.active{background:#000!important;color:#fff!important;font-weight:600}.calendar-day.today{font-weight:600}.calendar-day.today:not(.active){background:#e5e4e4}.calendar-day.active:hover{background:#000!important}.calendar-day.in-range{background:#f5f5f5;color:#333;border-radius:0;position:relative}.calendar-day.in-range:hover{background:#e8e8e8}.calendar-day.in-range:before{content:\"\";position:absolute;inset:0;background:#f5f5f5;z-index:-1}.calendar-day.in-range:hover:before{background:#e8e8e8}.calendar-day.multi-selected{background:#4caf50;color:#fff;font-weight:600;border-radius:6px}.calendar-day.multi-selected:hover{background:#45a049}.dual-calendar{display:flex;width:100%}.calendar-left,.calendar-right{flex:1;min-width:0;padding:0 12px 12px}.calendar-popup.has-ranges{display:flex;flex-direction:row}.calendar-popup.has-ranges .ranges{margin-bottom:0;border-bottom:none;padding:10px}.calendar-popup.has-ranges .dual-calendar,.calendar-popup.has-ranges .calendar-wrapper{flex:1}.calendar-right .header{justify-content:space-between}.calendar-right .header .month-year{text-align:center;flex:1}.timepicker-section{margin-top:12px;padding-top:12px;border-top:1px solid #eee}.timepicker-label{font-size:12px;font-weight:500;color:#000;margin-bottom:4px;letter-spacing:-.28px}.custom-time-picker{display:flex;flex-direction:column;gap:8px;align-items:start}.time-input-group{display:flex;align-items:center;justify-content:center;gap:8px;background:#f8f9fa;padding:12px;border-radius:8px;border:1px solid #e0e0e0}.time-control{display:flex;flex-direction:column;align-items:center}.time-btn{background:#fff;border:1px solid #ddd;width:28px;height:20px;cursor:pointer;font-size:10px;color:#666;border-radius:4px;transition:all .2s;display:flex;align-items:center;justify-content:center;padding:0;line-height:1}.time-btn:hover{background:#e4e4e4;color:#fff;border-color:#e4e4e4}.time-btn.up{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom:none}.time-btn.down{border-top-left-radius:0;border-top-right-radius:0;border-top:none}.time-input{width:40px;height:32px;text-align:center;border:1px solid #ddd;border-radius:4px;font-size:16px;font-weight:600;background:#fff;color:#333}.time-separator{font-size:18px;font-weight:600;color:#666;margin:0 2px}.ampm-control{display:flex;flex-direction:column;gap:4px;margin-left:8px}.ampm-btn{padding:6px 12px;border:1px solid #ddd;background:#fff;border-radius:4px;cursor:pointer;font-size:12px;font-weight:600;color:#666;transition:all .2s;min-width:45px}.ampm-btn:hover{background:#f0f0f0}.ampm-btn.active{background:#000;color:#fff;border-color:#000}.html5-time-input{margin-top:8px;padding:8px;border:1px solid #ddd;border-radius:6px;font-size:14px;width:100%;max-width:120px}.footer{padding:12px;display:flex;justify-content:flex-end;gap:8px;border-top:1px solid #eee}.btn-cancel,.btn-apply{padding:8px 16px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;min-width:80px}.btn-cancel{background:#fff;color:#666;border:1px solid #ddd}.btn-apply{background:#000;color:#fff}.btn-apply:active{transform:translateY(0)}@media (max-width: 768px){.calendar-popup{width:100%;max-width:320px;max-height:300px;overflow:auto}.calendar-popup.dual-calendar-mode{width:100%;max-width:100%}.calendar-popup.has-ranges{flex-direction:column}.calendar-popup.has-ranges .ranges{border-right:none;border-bottom:1px solid #eee;padding-right:0;margin-right:0;padding-bottom:16px;margin-bottom:16px}.dual-calendar{flex-direction:column}.time-input-group{flex-wrap:wrap;justify-content:center}}.ranges::-webkit-scrollbar{width:6px}.ranges::-webkit-scrollbar-track{background:#f1f1f1;border-radius:3px}.ranges::-webkit-scrollbar-thumb{background:#888;border-radius:3px}.ranges::-webkit-scrollbar-thumb:hover{background:#555}.w-100{width:100%}.flex-grow-1{flex-grow:1}.calendar-input.calendar-input-has-error{@apply border-[#d11e14];}.calendar-input:disabled{cursor:not-allowed;border-color:#e3e3e7;background-color:#f4f4f6;color:#a1a3ae}.btn-apply:disabled{cursor:not-allowed!important;opacity:.7}.calendar-day.calendar-day-keyboard-focus:not(.disabled):not(.other-month){outline:0px solid #000}.calendar-popup.compact{width:240px}.calendar-popup.compact.has-ranges{width:380px}.calendar-popup.compact.dual-calendar-mode{width:480px}.calendar-popup.compact.dual-calendar-mode.has-ranges{width:600px}.calendar-popup.compact .header{padding:8px 0}.calendar-popup.compact .month-year{font-size:13px}.calendar-popup.compact .nav-btn{height:24px;width:24px;padding:6px 8px}.calendar-popup.compact .calendar-wrapper,.calendar-popup.compact .calendar-left,.calendar-popup.compact .calendar-right{padding:0 8px 8px}.calendar-popup.compact .weekday-header{font-size:11px;padding:4px 2px}.calendar-popup.compact .calendar-day{font-size:12px;padding:5px 2px;line-height:1.3}.calendar-popup.compact .ranges{min-width:120px;gap:2px;margin-bottom:8px;padding-bottom:8px}.calendar-popup.compact.has-ranges .ranges{padding:6px}.calendar-popup.compact .range-btn{font-size:12px;padding:5px 8px}.calendar-popup.compact .timepicker-section{margin-top:8px;padding-top:8px}.calendar-popup.compact .timepicker-label{font-size:11px}.calendar-popup.compact .footer{padding:8px;gap:6px}.calendar-popup.compact .btn-cancel,.calendar-popup.compact .btn-apply{font-size:11px;padding:6px 12px;min-width:64px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: BkTimePicker, selector: "bk-time-picker", inputs: ["required", "value", "label", "placeholder", "clearable", "position", "variation", "pickerId", "closePicker", "timeFormat", "showSeconds", "autoPosition", "appendToBody", "disabled"], outputs: ["change", "timeChange", "pickerOpened", "pickerClosed"] }] });
|
|
2947
3060
|
}
|
|
2948
3061
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkCustomCalendar, decorators: [{
|
|
2949
3062
|
type: Component,
|
|
@@ -2958,8 +3071,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
2958
3071
|
useExisting: forwardRef(() => BkCustomCalendar),
|
|
2959
3072
|
multi: true,
|
|
2960
3073
|
},
|
|
2961
|
-
], template: "<div class=\"calendar-container relative\" [class.open]=\"show\" [class.inline-mode]=\"inline\" [class.disabled]=\"disabled\">\r\n <!-- Input field -->\r\n <div #inputWrapper class=\"input-wrapper\" *ngIf=\"!inline\">\r\n <input\r\n type=\"text\"\r\n (click)=\"!disabled && toggle()\"\r\n (keydown.enter)=\"$event.preventDefault()\"\r\n (blur)=\"markAsTouched()\"\r\n readonly\r\n [value]=\"getDisplayValue()\"\r\n [placeholder]=\"placeholder\"\r\n [attr.disabled]=\"disabled ? true : null\"\r\n [class.hasError]=\"hasError\"\r\n class=\"calendar-input\">\r\n <!-- *ngIf=\"!getDisplayValue()\" -->\r\n\r\n <span class=\"calendar-icon\" >\r\n <img alt=\"calendar\" class=\"calendar-icon-img\" [src]='brickclayIcons.calenderIcon'/>\r\n </span>\r\n <button type=\"button\" class=\"clear-btn\" *ngIf=\"getDisplayValue() && isDisplayCrossIcon && !disabled\" (click)=\"clear(); $event.stopPropagation()\" title=\"Clear\">\u00D7</button>\r\n </div>\r\n\r\n <!-- Calendar Popup / Inline -->\r\n <div #calendarPopup\r\n class=\"calendar-popup\"\r\n [class.inline-calendar]=\"inline\"\r\n [class.append-to-body]=\"appendToBody && !inline\"\r\n [style.position]=\"appendToBody && !inline ? 'fixed' : null\"\r\n [style.top]=\"appendToBody && !inline && !popupPlacementAbove ? dropdownStyle.top : null\"\r\n [style.bottom]=\"appendToBody && !inline && popupPlacementAbove ? dropdownStyle.bottom : null\"\r\n [style.left]=\"appendToBody && !inline ? dropdownStyle.left : null\"\r\n tabindex=\"0\"\r\n (keydown)=\"onCalendarPopupKeydown($event)\"\r\n [ngClass]=\"{\r\n 'position-right': !inline && !appendToBody && opens === 'right',\r\n 'position-center': !inline && !appendToBody && opens === 'center',\r\n 'drop-up': !inline && popupPlacementAbove,\r\n 'has-ranges': showRanges && customRanges,\r\n 'dual-calendar-mode': dualCalendar,\r\n 'compact': compact\r\n }\"\r\n *ngIf=\"inline || show\">\r\n\r\n <!-- RANGES -->\r\n <div class=\"ranges\" *ngIf=\"showRanges && customRanges\" role=\"listbox\" aria-label=\"Date ranges\">\r\n <button\r\n type=\"button\"\r\n *ngFor=\"let rangeKey of rangeOrder\"\r\n (click)=\"chooseRange(rangeKey)\"\r\n (keydown)=\"onRangeButtonKeydown($event, rangeKey)\"\r\n [class.active]=\"activeRange === rangeKey\"\r\n [class.custom-range]=\"rangeKey === 'Custom Range'\"\r\n class=\"range-btn\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"activeRange === rangeKey\">\r\n {{ rangeKey }}\r\n </button>\r\n </div>\r\n<div class=\"\" [ngClass]=\"showRanges ? 'w-100 flex-grow-1 border-l border-[#eee]' : ''\">\r\n\r\n\r\n <!-- SINGLE CALENDAR -->\r\n <div *ngIf=\"!dualCalendar\" class=\"calendar-wrapper\">\r\n <div class=\"header\">\r\n <!-- <button (click)=\"prevMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-left-gray.svg\" alt=\"arrow-left\" class=\"arrow-left\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"prevMonth()\" matTooltip=\"Prev month\"\r\n >\r\n <img alt=\"prev\" class=\"h-3 w-3\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(month) }} {{ year }}</span>\r\n <!-- <button (click)=\"nextMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-right-gray.svg\" alt=\"arrow-right\" class=\"arrow-right\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"nextMonth()\" matTooltip=\"Next month\"\r\n >\r\n <img alt=\"next\" class=\"h-3 w-3\" [src]='brickclayIcons.arrowRight'/>\r\n <!--<img src=\"assets/calender/pagination-right-gray.svg\" alt=\"next\" class=\"h-3 w-3\" /> -->\r\n </button>\r\n </div>\r\n\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(month) + ' ' + year\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of calendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && selectDate(dayObj.day)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(year, month, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(year, month, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(year, month, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(year, month, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(year, month, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(year, month, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Single Calendar Time Picker -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"single-time\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"singleTimeModel\"\r\n (ngModelChange)=\"onSingleTimePickerChange($event); singleTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('single-time')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- DUAL CALENDAR -->\r\n <div class=\"dual-calendar\" *ngIf=\"dualCalendar\">\r\n <!-- LEFT CALENDAR -->\r\n <div class=\"calendar-left\">\r\n <div class=\"header\">\r\n <button (click)=\"prevLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-left\" class=\"arrow-left\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(leftMonth) }} {{ leftYear }}</span>\r\n <button (click)=\"nextLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-right\" class=\"arrow-right\" [src]='brickclayIcons.arrowRight'/>\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(leftMonth) + ' ' + leftYear\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of leftCalendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && selectDate(dayObj.day, false)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(leftYear, leftMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(leftYear, leftMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(leftYear, leftMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(leftYear, leftMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(leftYear, leftMonth, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(leftYear, leftMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Start Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Start Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"dual-start\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"startTimeModel\"\r\n (ngModelChange)=\"onDualTimePickerChange($event, true); startTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('dual-start')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- RIGHT CALENDAR -->\r\n <div class=\"calendar-right\">\r\n <div class=\"header\">\r\n <button (click)=\"prevRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-left\" class=\"arrow-left\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(rightMonth) }} {{ rightYear }}</span>\r\n <button (click)=\"nextRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-right\" class=\"arrow-right\" [src]='brickclayIcons.arrowRight'/>\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(rightMonth) + ' ' + rightYear\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of rightCalendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && selectDate(dayObj.day, true)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && onDateHover(dayObj.day, true)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(rightYear, rightMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(rightYear, rightMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(rightYear, rightMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(rightYear, rightMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(rightYear, rightMonth, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(rightYear, rightMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- End Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">End Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"dual-end\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"endTimeModel\"\r\n (ngModelChange)=\"onDualTimePickerChange($event, false); endTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('dual-end')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- FOOTER -->\r\n <div class=\"footer\" *ngIf=\"!inline && showCancelApply\">\r\n <button *ngIf=\"showCancel\" (click)=\"cancel()\" class=\"btn-cancel\" type=\"button\">Cancel</button>\r\n <button (click)=\"apply()\" class=\"btn-apply\" type=\"button\" [disabled]=\"disabled || isDualRangeApplyBlocked\">Apply</button>\r\n </div>\r\n\r\n </div>\r\n\r\n </div>\r\n</div>\r\n\r\n@if (hasError){\r\n<p class=\"calender-error\">{{errorMessage}}</p>\r\n}\r\n", styles: [".calendar-container,.calendar-container *{font-family:Inter,sans-serif!important}.calendar-container{position:relative;display:inline-block;width:100%}.input-wrapper{position:relative;display:flex;align-items:center}.calendar-input{width:100%;padding:9px 14px 9px 40px;border:1px solid #ddd;border-radius:8px;font-size:14px;cursor:pointer;background:#fff;transition:all .2s}.calendar-input:hover{border-color:#999}.calendar-input:focus{outline:none;border-color:#999}.calendar-icon{position:absolute;left:12px;pointer-events:none;font-size:18px}.clear-btn{position:absolute;right:9px;background:none;border:none;font-size:20px;color:#999;cursor:pointer;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;line-height:1;transition:color .2s;top:8px}.clear-btn:hover{color:#333}.calendar-popup{position:absolute;top:110%;left:0;width:320px;background:#fff;border-radius:12px;box-shadow:0 10px 40px #00000026;z-index:1000;animation:slideDown .2s ease-out}.calendar-popup:focus-visible{outline:0!important}.calendar-popup.inline-calendar{position:relative;top:0;left:0;width:100%;margin-top:0;animation:none;box-shadow:0 2px 8px #0000001a}.calendar-container.inline-mode{display:block;width:100%}.calendar-popup.dual-calendar-mode{width:600px}.calendar-popup.dual-calendar-mode.has-ranges{width:730px}.calendar-popup.has-ranges{width:450px}.calendar-popup.drop-up{top:auto;bottom:110%;animation:slideUp .2s ease-out}.calendar-popup.position-right{left:auto;right:0}.calendar-popup.position-center{left:50%;transform:translate(-50%)}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.ranges{display:flex;flex-direction:column;gap:4px;margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid #eee;min-width:150px;padding-right:8px}.range-btn{padding:7px 10px;border:1px solid transparent;background:transparent;border-radius:4px;cursor:pointer;text-align:left;font-size:14px;transition:all .2s;color:#838383;font-weight:500}.range-btn:hover{background:#f5f5f5;color:#000}.range-btn.active{background:#f0f0f0;color:#000;font-weight:500}.calendar-wrapper{padding:0 12px 12px}.header{display:flex;justify-content:space-between;align-items:center;padding:12px 0}.month-year{font-size:15px;font-weight:500;color:#333;flex:1;text-align:center;text-transform:capitalize}.nav-btn{background:none;border:none;font-size:24px;cursor:pointer;padding:11.5px 14px;color:#666;border-radius:4px;transition:all .2s;line-height:1;height:30px;width:30px;display:flex;justify-content:center;align-items:center}.nav-btn:hover{background:#f0f0f0;color:#000}.nav-btn img{width:auto;max-width:none!important}.calendar-table{width:100%;border-collapse:collapse;text-align:center}.weekday-header{font-size:12px;color:#7e7e7e;font-weight:600;padding:8px 4px;letter-spacing:.3px}.calendar-day{padding:8px 4px;font-size:14px;cursor:pointer;border-radius:6px;transition:all .2s;position:relative;color:#333;font-weight:500;line-height:1.5}.calendar-day:hover:not(.disabled):not(.other-month){background:#efefef;color:#000}.calendar-day.other-month{color:#ccc;cursor:default}.calendar-day.disabled{color:#ddd;cursor:not-allowed;opacity:.5}.calendar-day.active{background:#000!important;color:#fff!important;font-weight:600}.calendar-day.today{font-weight:600}.calendar-day.today:not(.active){background:#e5e4e4}.calendar-day.active:hover{background:#000!important}.calendar-day.in-range{background:#f5f5f5;color:#333;border-radius:0;position:relative}.calendar-day.in-range:hover{background:#e8e8e8}.calendar-day.in-range:before{content:\"\";position:absolute;inset:0;background:#f5f5f5;z-index:-1}.calendar-day.in-range:hover:before{background:#e8e8e8}.calendar-day.multi-selected{background:#4caf50;color:#fff;font-weight:600;border-radius:6px}.calendar-day.multi-selected:hover{background:#45a049}.dual-calendar{display:flex;width:100%}.calendar-left,.calendar-right{flex:1;min-width:0;padding:0 12px 12px}.calendar-popup.has-ranges{display:flex;flex-direction:row}.calendar-popup.has-ranges .ranges{margin-bottom:0;border-bottom:none;padding:10px}.calendar-popup.has-ranges .dual-calendar,.calendar-popup.has-ranges .calendar-wrapper{flex:1}.calendar-right .header{justify-content:space-between}.calendar-right .header .month-year{text-align:center;flex:1}.timepicker-section{margin-top:12px;padding-top:12px;border-top:1px solid #eee}.timepicker-label{font-size:12px;font-weight:500;color:#000;margin-bottom:4px;letter-spacing:-.28px}.custom-time-picker{display:flex;flex-direction:column;gap:8px;align-items:start}.time-input-group{display:flex;align-items:center;justify-content:center;gap:8px;background:#f8f9fa;padding:12px;border-radius:8px;border:1px solid #e0e0e0}.time-control{display:flex;flex-direction:column;align-items:center}.time-btn{background:#fff;border:1px solid #ddd;width:28px;height:20px;cursor:pointer;font-size:10px;color:#666;border-radius:4px;transition:all .2s;display:flex;align-items:center;justify-content:center;padding:0;line-height:1}.time-btn:hover{background:#e4e4e4;color:#fff;border-color:#e4e4e4}.time-btn.up{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom:none}.time-btn.down{border-top-left-radius:0;border-top-right-radius:0;border-top:none}.time-input{width:40px;height:32px;text-align:center;border:1px solid #ddd;border-radius:4px;font-size:16px;font-weight:600;background:#fff;color:#333}.time-separator{font-size:18px;font-weight:600;color:#666;margin:0 2px}.ampm-control{display:flex;flex-direction:column;gap:4px;margin-left:8px}.ampm-btn{padding:6px 12px;border:1px solid #ddd;background:#fff;border-radius:4px;cursor:pointer;font-size:12px;font-weight:600;color:#666;transition:all .2s;min-width:45px}.ampm-btn:hover{background:#f0f0f0}.ampm-btn.active{background:#000;color:#fff;border-color:#000}.html5-time-input{margin-top:8px;padding:8px;border:1px solid #ddd;border-radius:6px;font-size:14px;width:100%;max-width:120px}.footer{padding:12px;display:flex;justify-content:flex-end;gap:8px;border-top:1px solid #eee}.btn-cancel,.btn-apply{padding:8px 16px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;min-width:80px}.btn-cancel{background:#fff;color:#666;border:1px solid #ddd}.btn-apply{background:#000;color:#fff}.btn-apply:active{transform:translateY(0)}@media (max-width: 768px){.calendar-popup{width:100%;max-width:320px;max-height:300px;overflow:auto}.calendar-popup.dual-calendar-mode{width:100%;max-width:100%}.calendar-popup.has-ranges{flex-direction:column}.calendar-popup.has-ranges .ranges{border-right:none;border-bottom:1px solid #eee;padding-right:0;margin-right:0;padding-bottom:16px;margin-bottom:16px}.dual-calendar{flex-direction:column}.time-input-group{flex-wrap:wrap;justify-content:center}}.ranges::-webkit-scrollbar{width:6px}.ranges::-webkit-scrollbar-track{background:#f1f1f1;border-radius:3px}.ranges::-webkit-scrollbar-thumb{background:#888;border-radius:3px}.ranges::-webkit-scrollbar-thumb:hover{background:#555}.w-100{width:100%}.flex-grow-1{flex-grow:1}.calendar-input.calendar-input-has-error{@apply border-[#d11e14];}.calendar-input:disabled{cursor:not-allowed;border-color:#e3e3e7;background-color:#f4f4f6;color:#a1a3ae}.btn-apply:disabled{cursor:not-allowed!important;opacity:.7}.calendar-day.calendar-day-keyboard-focus:not(.disabled):not(.other-month){outline:0px solid #000}.calendar-popup.compact{width:240px}.calendar-popup.compact.has-ranges{width:380px}.calendar-popup.compact.dual-calendar-mode{width:480px}.calendar-popup.compact.dual-calendar-mode.has-ranges{width:600px}.calendar-popup.compact .header{padding:8px 0}.calendar-popup.compact .month-year{font-size:13px}.calendar-popup.compact .nav-btn{height:24px;width:24px;padding:6px 8px}.calendar-popup.compact .calendar-wrapper,.calendar-popup.compact .calendar-left,.calendar-popup.compact .calendar-right{padding:0 8px 8px}.calendar-popup.compact .weekday-header{font-size:11px;padding:4px 2px}.calendar-popup.compact .calendar-day{font-size:12px;padding:5px 2px;line-height:1.3}.calendar-popup.compact .ranges{min-width:120px;gap:2px;margin-bottom:8px;padding-bottom:8px}.calendar-popup.compact.has-ranges .ranges{padding:6px}.calendar-popup.compact .range-btn{font-size:12px;padding:5px 8px}.calendar-popup.compact .timepicker-section{margin-top:8px;padding-top:8px}.calendar-popup.compact .timepicker-label{font-size:11px}.calendar-popup.compact .footer{padding:8px;gap:6px}.calendar-popup.compact .btn-cancel,.calendar-popup.compact .btn-apply{font-size:11px;padding:6px 12px;min-width:64px}\n"] }]
|
|
2962
|
-
}], ctorParameters: () => [{ type: BkCalendarManagerService }], propDecorators: { enableTimepicker: [{
|
|
3074
|
+
], template: "<div class=\"calendar-container relative\" [class.open]=\"show\" [class.inline-mode]=\"inline\" [class.disabled]=\"disabled\">\r\n <!-- Input field -->\r\n <div #inputWrapper class=\"input-wrapper\" *ngIf=\"!inline\">\r\n <input\r\n type=\"text\"\r\n (click)=\"!disabled && toggle()\"\r\n (keydown.enter)=\"$event.preventDefault()\"\r\n (blur)=\"markAsTouched()\"\r\n readonly\r\n [value]=\"getDisplayValue()\"\r\n [placeholder]=\"placeholder\"\r\n [attr.disabled]=\"disabled ? true : null\"\r\n [class.hasError]=\"hasError\"\r\n class=\"calendar-input\">\r\n <!-- *ngIf=\"!getDisplayValue()\" -->\r\n\r\n <span class=\"calendar-icon\" >\r\n <img alt=\"calendar\" class=\"calendar-icon-img\" [src]='brickclayIcons.calenderIcon'/>\r\n </span>\r\n <button type=\"button\" class=\"clear-btn\" *ngIf=\"getDisplayValue() && isDisplayCrossIcon && !disabled\" (click)=\"clear(); $event.stopPropagation()\" title=\"Clear\">\u00D7</button>\r\n </div>\r\n\r\n <!-- Calendar Popup / Inline -->\r\n <div #calendarPopup\r\n class=\"calendar-popup\"\r\n [class.inline-calendar]=\"inline\"\r\n [class.append-to-body]=\"appendToBody && !inline\"\r\n [style.position]=\"appendToBody && !inline ? 'fixed' : null\"\r\n [style.top]=\"appendToBody && !inline && !popupPlacementAbove ? dropdownStyle.top : null\"\r\n [style.bottom]=\"appendToBody && !inline && popupPlacementAbove ? dropdownStyle.bottom : null\"\r\n [style.left]=\"appendToBody && !inline ? dropdownStyle.left : null\"\r\n tabindex=\"0\"\r\n (keydown)=\"onCalendarPopupKeydown($event)\"\r\n [ngClass]=\"{\r\n 'position-right': !inline && !appendToBody && opens === 'right',\r\n 'position-center': !inline && !appendToBody && opens === 'center',\r\n 'drop-up': !inline && popupPlacementAbove,\r\n 'has-ranges': showRanges && customRanges,\r\n 'dual-calendar-mode': dualCalendar,\r\n 'compact': compact\r\n }\"\r\n *ngIf=\"inline || show\">\r\n\r\n <!-- RANGES -->\r\n <div class=\"ranges\" *ngIf=\"showRanges && customRanges\" role=\"listbox\" aria-label=\"Date ranges\">\r\n <button\r\n type=\"button\"\r\n *ngFor=\"let rangeKey of rangeOrder\"\r\n (click)=\"chooseRange(rangeKey)\"\r\n (keydown)=\"onRangeButtonKeydown($event, rangeKey)\"\r\n [class.active]=\"activeRange === rangeKey\"\r\n [class.custom-range]=\"rangeKey === 'Custom Range'\"\r\n class=\"range-btn\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"activeRange === rangeKey\">\r\n {{ rangeKey }}\r\n </button>\r\n </div>\r\n<div class=\"\" [ngClass]=\"showRanges ? 'w-100 flex-grow-1 border-l border-[#eee]' : ''\">\r\n\r\n\r\n <!-- SINGLE CALENDAR -->\r\n <div *ngIf=\"!dualCalendar\" class=\"calendar-wrapper\">\r\n <div class=\"header\">\r\n <!-- <button (click)=\"prevMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-left-gray.svg\" alt=\"arrow-left\" class=\"arrow-left\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"prevMonth()\" matTooltip=\"Prev month\"\r\n >\r\n <img alt=\"prev\" class=\"h-3 w-3\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(month) }} {{ year }}</span>\r\n <!-- <button (click)=\"nextMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-right-gray.svg\" alt=\"arrow-right\" class=\"arrow-right\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"nextMonth()\" matTooltip=\"Next month\"\r\n >\r\n <img alt=\"next\" class=\"h-3 w-3\" [src]='brickclayIcons.arrowRight'/>\r\n <!--<img src=\"assets/calender/pagination-right-gray.svg\" alt=\"next\" class=\"h-3 w-3\" /> -->\r\n </button>\r\n </div>\r\n\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(month) + ' ' + year\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of calendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && selectDate(dayObj.day)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(year, month, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(year, month, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(year, month, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(year, month, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(year, month, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(year, month, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Single Calendar Time Picker -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"single-time\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"singleTimeModel\"\r\n (ngModelChange)=\"onSingleTimePickerChange($event); singleTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('single-time')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- DUAL CALENDAR -->\r\n <div class=\"dual-calendar\" *ngIf=\"dualCalendar\">\r\n <!-- LEFT CALENDAR -->\r\n <div class=\"calendar-left\">\r\n <div class=\"header\">\r\n <button (click)=\"prevLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-left\" class=\"arrow-left\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(leftMonth) }} {{ leftYear }}</span>\r\n <button (click)=\"nextLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-right\" class=\"arrow-right\" [src]='brickclayIcons.arrowRight'/>\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(leftMonth) + ' ' + leftYear\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of leftCalendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && selectDate(dayObj.day, false)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(leftYear, leftMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(leftYear, leftMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(leftYear, leftMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(leftYear, leftMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(leftYear, leftMonth, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(leftYear, leftMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Start Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Start Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"dual-start\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"startTimeModel\"\r\n (ngModelChange)=\"onDualTimePickerChange($event, true); startTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('dual-start')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- RIGHT CALENDAR -->\r\n <div class=\"calendar-right\">\r\n <div class=\"header\">\r\n <button (click)=\"prevRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-left\" class=\"arrow-left\" [src]=\"brickclayIcons.arrowleft\"/>\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(rightMonth) }} {{ rightYear }}</span>\r\n <button (click)=\"nextRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img alt=\"arrow-right\" class=\"arrow-right\" [src]='brickclayIcons.arrowRight'/>\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\" role=\"grid\" [attr.aria-label]=\"getMonthName(rightMonth) + ' ' + rightYear\">\r\n <thead>\r\n <tr role=\"row\">\r\n <th *ngFor=\"let d of resolvedWeekDayLabels\" class=\"weekday-header\" scope=\"col\" role=\"columnheader\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of rightCalendar\" role=\"row\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n role=\"gridcell\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && selectDate(dayObj.day, true)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && onDateHover(dayObj.day, true)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(rightYear, rightMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(rightYear, rightMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(rightYear, rightMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(rightYear, rightMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(rightYear, rightMonth, dayObj.day)\"\r\n [class.calendar-day-keyboard-focus]=\"dayObj.currentMonth && isKeyboardFocusedCell(rightYear, rightMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- End Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">End Time</div>\r\n <div class=\"timepicker-controls\">\r\n <bk-time-picker\r\n pickerId=\"dual-end\"\r\n [variation]=\"compact ? 'default' : 'lg'\"\r\n [timeFormat]=\"timeFormat\"\r\n [clearable]=\"clearableTime\"\r\n [label]=\"''\"\r\n [ngModel]=\"endTimeModel\"\r\n (ngModelChange)=\"onDualTimePickerChange($event, false); endTimeModel=$event\"\r\n [closePicker]=\"shouldClosePicker('dual-end')\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </bk-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- FOOTER -->\r\n <div class=\"footer\" *ngIf=\"!inline && showCancelApply\">\r\n <button *ngIf=\"showCancel\" (click)=\"cancel()\" class=\"btn-cancel\" type=\"button\">Cancel</button>\r\n <button (click)=\"apply()\" class=\"btn-apply\" type=\"button\" [disabled]=\"disabled || isDualRangeApplyBlocked\">Apply</button>\r\n </div>\r\n\r\n </div>\r\n\r\n </div>\r\n</div>\r\n\r\n@if (hasError){\r\n<p class=\"calender-error\">{{errorMessage}}</p>\r\n}\r\n", styles: [".calendar-container,.calendar-container *{font-family:Inter,sans-serif!important}.calendar-container{position:relative;display:inline-block;width:100%}.input-wrapper{position:relative;display:flex;align-items:center}.calendar-input{width:100%;padding:9px 14px 9px 40px;border:1px solid #ddd;border-radius:8px;font-size:14px;cursor:pointer;background:#fff;transition:all .2s}.calendar-input:hover{border-color:#999}.calendar-input:focus{outline:none;border-color:#999}.calendar-icon{position:absolute;left:12px;pointer-events:none;font-size:18px}.clear-btn{position:absolute;right:9px;background:none;border:none;font-size:20px;color:#999;cursor:pointer;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;line-height:1;transition:color .2s;top:8px}.clear-btn:hover{color:#333}.calendar-popup{position:absolute;top:110%;left:0;width:320px;background:#fff;border-radius:12px;box-shadow:0 10px 40px #00000026;z-index:1000;animation:slideDown .2s ease-out}.calendar-popup:focus-visible{outline:0!important}.calendar-popup.append-to-body{font-family:Inter,sans-serif;z-index:100000}.calendar-popup.inline-calendar{position:relative;top:0;left:0;width:100%;margin-top:0;animation:none;box-shadow:0 2px 8px #0000001a}.calendar-container.inline-mode{display:block;width:100%}.calendar-popup.dual-calendar-mode{width:600px}.calendar-popup.dual-calendar-mode.has-ranges{width:730px}.calendar-popup.has-ranges{width:450px}.calendar-popup.drop-up{top:auto;bottom:110%;animation:slideUp .2s ease-out}.calendar-popup.position-right{left:auto;right:0}.calendar-popup.position-center{left:50%;transform:translate(-50%)}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.ranges{display:flex;flex-direction:column;gap:4px;margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid #eee;min-width:150px;padding-right:8px}.range-btn{padding:7px 10px;border:1px solid transparent;background:transparent;border-radius:4px;cursor:pointer;text-align:left;font-size:14px;transition:all .2s;color:#838383;font-weight:500}.range-btn:hover{background:#f5f5f5;color:#000}.range-btn.active{background:#f0f0f0;color:#000;font-weight:500}.calendar-wrapper{padding:0 12px 12px}.header{display:flex;justify-content:space-between;align-items:center;padding:12px 0}.month-year{font-size:15px;font-weight:500;color:#333;flex:1;text-align:center;text-transform:capitalize}.nav-btn{background:none;border:none;font-size:24px;cursor:pointer;padding:11.5px 14px;color:#666;border-radius:4px;transition:all .2s;line-height:1;height:30px;width:30px;display:flex;justify-content:center;align-items:center}.nav-btn:hover{background:#f0f0f0;color:#000}.nav-btn img{width:auto;max-width:none!important}.calendar-table{width:100%;border-collapse:collapse;text-align:center}.weekday-header{font-size:12px;color:#7e7e7e;font-weight:600;padding:8px 4px;letter-spacing:.3px}.calendar-day{padding:8px 4px;font-size:14px;cursor:pointer;border-radius:6px;transition:all .2s;position:relative;color:#333;font-weight:500;line-height:1.5}.calendar-day:hover:not(.disabled):not(.other-month){background:#efefef;color:#000}.calendar-day.other-month{color:#ccc;cursor:default}.calendar-day.disabled{color:#ddd;cursor:not-allowed;opacity:.5}.calendar-day.active{background:#000!important;color:#fff!important;font-weight:600}.calendar-day.today{font-weight:600}.calendar-day.today:not(.active){background:#e5e4e4}.calendar-day.active:hover{background:#000!important}.calendar-day.in-range{background:#f5f5f5;color:#333;border-radius:0;position:relative}.calendar-day.in-range:hover{background:#e8e8e8}.calendar-day.in-range:before{content:\"\";position:absolute;inset:0;background:#f5f5f5;z-index:-1}.calendar-day.in-range:hover:before{background:#e8e8e8}.calendar-day.multi-selected{background:#4caf50;color:#fff;font-weight:600;border-radius:6px}.calendar-day.multi-selected:hover{background:#45a049}.dual-calendar{display:flex;width:100%}.calendar-left,.calendar-right{flex:1;min-width:0;padding:0 12px 12px}.calendar-popup.has-ranges{display:flex;flex-direction:row}.calendar-popup.has-ranges .ranges{margin-bottom:0;border-bottom:none;padding:10px}.calendar-popup.has-ranges .dual-calendar,.calendar-popup.has-ranges .calendar-wrapper{flex:1}.calendar-right .header{justify-content:space-between}.calendar-right .header .month-year{text-align:center;flex:1}.timepicker-section{margin-top:12px;padding-top:12px;border-top:1px solid #eee}.timepicker-label{font-size:12px;font-weight:500;color:#000;margin-bottom:4px;letter-spacing:-.28px}.custom-time-picker{display:flex;flex-direction:column;gap:8px;align-items:start}.time-input-group{display:flex;align-items:center;justify-content:center;gap:8px;background:#f8f9fa;padding:12px;border-radius:8px;border:1px solid #e0e0e0}.time-control{display:flex;flex-direction:column;align-items:center}.time-btn{background:#fff;border:1px solid #ddd;width:28px;height:20px;cursor:pointer;font-size:10px;color:#666;border-radius:4px;transition:all .2s;display:flex;align-items:center;justify-content:center;padding:0;line-height:1}.time-btn:hover{background:#e4e4e4;color:#fff;border-color:#e4e4e4}.time-btn.up{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom:none}.time-btn.down{border-top-left-radius:0;border-top-right-radius:0;border-top:none}.time-input{width:40px;height:32px;text-align:center;border:1px solid #ddd;border-radius:4px;font-size:16px;font-weight:600;background:#fff;color:#333}.time-separator{font-size:18px;font-weight:600;color:#666;margin:0 2px}.ampm-control{display:flex;flex-direction:column;gap:4px;margin-left:8px}.ampm-btn{padding:6px 12px;border:1px solid #ddd;background:#fff;border-radius:4px;cursor:pointer;font-size:12px;font-weight:600;color:#666;transition:all .2s;min-width:45px}.ampm-btn:hover{background:#f0f0f0}.ampm-btn.active{background:#000;color:#fff;border-color:#000}.html5-time-input{margin-top:8px;padding:8px;border:1px solid #ddd;border-radius:6px;font-size:14px;width:100%;max-width:120px}.footer{padding:12px;display:flex;justify-content:flex-end;gap:8px;border-top:1px solid #eee}.btn-cancel,.btn-apply{padding:8px 16px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;min-width:80px}.btn-cancel{background:#fff;color:#666;border:1px solid #ddd}.btn-apply{background:#000;color:#fff}.btn-apply:active{transform:translateY(0)}@media (max-width: 768px){.calendar-popup{width:100%;max-width:320px;max-height:300px;overflow:auto}.calendar-popup.dual-calendar-mode{width:100%;max-width:100%}.calendar-popup.has-ranges{flex-direction:column}.calendar-popup.has-ranges .ranges{border-right:none;border-bottom:1px solid #eee;padding-right:0;margin-right:0;padding-bottom:16px;margin-bottom:16px}.dual-calendar{flex-direction:column}.time-input-group{flex-wrap:wrap;justify-content:center}}.ranges::-webkit-scrollbar{width:6px}.ranges::-webkit-scrollbar-track{background:#f1f1f1;border-radius:3px}.ranges::-webkit-scrollbar-thumb{background:#888;border-radius:3px}.ranges::-webkit-scrollbar-thumb:hover{background:#555}.w-100{width:100%}.flex-grow-1{flex-grow:1}.calendar-input.calendar-input-has-error{@apply border-[#d11e14];}.calendar-input:disabled{cursor:not-allowed;border-color:#e3e3e7;background-color:#f4f4f6;color:#a1a3ae}.btn-apply:disabled{cursor:not-allowed!important;opacity:.7}.calendar-day.calendar-day-keyboard-focus:not(.disabled):not(.other-month){outline:0px solid #000}.calendar-popup.compact{width:240px}.calendar-popup.compact.has-ranges{width:380px}.calendar-popup.compact.dual-calendar-mode{width:480px}.calendar-popup.compact.dual-calendar-mode.has-ranges{width:600px}.calendar-popup.compact .header{padding:8px 0}.calendar-popup.compact .month-year{font-size:13px}.calendar-popup.compact .nav-btn{height:24px;width:24px;padding:6px 8px}.calendar-popup.compact .calendar-wrapper,.calendar-popup.compact .calendar-left,.calendar-popup.compact .calendar-right{padding:0 8px 8px}.calendar-popup.compact .weekday-header{font-size:11px;padding:4px 2px}.calendar-popup.compact .calendar-day{font-size:12px;padding:5px 2px;line-height:1.3}.calendar-popup.compact .ranges{min-width:120px;gap:2px;margin-bottom:8px;padding-bottom:8px}.calendar-popup.compact.has-ranges .ranges{padding:6px}.calendar-popup.compact .range-btn{font-size:12px;padding:5px 8px}.calendar-popup.compact .timepicker-section{margin-top:8px;padding-top:8px}.calendar-popup.compact .timepicker-label{font-size:11px}.calendar-popup.compact .footer{padding:8px;gap:6px}.calendar-popup.compact .btn-cancel,.calendar-popup.compact .btn-apply{font-size:11px;padding:6px 12px;min-width:64px}\n"] }]
|
|
3075
|
+
}], ctorParameters: () => [{ type: BkCalendarManagerService }, { type: i0.Renderer2 }], propDecorators: { enableTimepicker: [{
|
|
2963
3076
|
type: Input
|
|
2964
3077
|
}], autoApply: [{
|
|
2965
3078
|
type: Input
|
|
@@ -6107,6 +6220,14 @@ class BkTabs {
|
|
|
6107
6220
|
orientation = 'horizontal';
|
|
6108
6221
|
variant = 'default';
|
|
6109
6222
|
colors = {};
|
|
6223
|
+
/**
|
|
6224
|
+
* When true, if the currently active tab is in an error state, navigation to
|
|
6225
|
+
* the other tabs is blocked (they render disabled) until the error clears.
|
|
6226
|
+
* Optional — defaults to false, so navigation stays free unless opted in.
|
|
6227
|
+
*/
|
|
6228
|
+
lockNavigationOnError = false;
|
|
6229
|
+
/** Fallback error icon used when a tab has `showErrorIcon` but no `errorIcon`. */
|
|
6230
|
+
defaultErrorIcon = '../../assets/icons/alert-circle-red.svg';
|
|
6110
6231
|
// Convenience getters for template class bindings
|
|
6111
6232
|
get isVertical() {
|
|
6112
6233
|
return this.orientation === 'vertical';
|
|
@@ -6130,9 +6251,18 @@ class BkTabs {
|
|
|
6130
6251
|
return this.colors.inactive ?? '';
|
|
6131
6252
|
}
|
|
6132
6253
|
change = new EventEmitter();
|
|
6254
|
+
// True when the currently active tab is flagged with an error.
|
|
6255
|
+
get activeTabHasError() {
|
|
6256
|
+
return this.list.some(t => t.id === this.activeTabId && !!t.error);
|
|
6257
|
+
}
|
|
6258
|
+
// A tab is locked when navigation-on-error is enabled, the active tab has an
|
|
6259
|
+
// error, and this tab is not the active one. Locked tabs render disabled.
|
|
6260
|
+
isLocked(tab) {
|
|
6261
|
+
return this.lockNavigationOnError && this.activeTabHasError && tab.id !== this.activeTabId;
|
|
6262
|
+
}
|
|
6133
6263
|
// Set active tab and emit change event
|
|
6134
6264
|
setActiveTab(tab) {
|
|
6135
|
-
if (tab?.disabled || this.disabled)
|
|
6265
|
+
if (tab?.disabled || this.disabled || this.isLocked(tab))
|
|
6136
6266
|
return;
|
|
6137
6267
|
this.activeTabId = tab.id;
|
|
6138
6268
|
this.change.emit(tab);
|
|
@@ -6141,19 +6271,25 @@ class BkTabs {
|
|
|
6141
6271
|
isActive(tabId) {
|
|
6142
6272
|
return this.activeTabId === tabId;
|
|
6143
6273
|
}
|
|
6144
|
-
// Get the appropriate icon for a tab based on its active state
|
|
6274
|
+
// Get the appropriate icon for a tab based on its active/error state.
|
|
6275
|
+
// In an error state, when the tab opts into an error icon, show the custom
|
|
6276
|
+
// `errorIcon` or the component default error icon — this replaces the tab's
|
|
6277
|
+
// own decorative `icon` so the error is always what's surfaced.
|
|
6145
6278
|
getTabIcon(tab) {
|
|
6279
|
+
if (tab.error && tab.showErrorIcon) {
|
|
6280
|
+
return tab.errorIcon ?? this.defaultErrorIcon;
|
|
6281
|
+
}
|
|
6146
6282
|
if (this.isActive(tab.id) && tab.iconActive) {
|
|
6147
6283
|
return tab.iconActive;
|
|
6148
6284
|
}
|
|
6149
6285
|
return tab.icon;
|
|
6150
6286
|
}
|
|
6151
6287
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkTabs, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6152
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: BkTabs, isStandalone: true, selector: "bk-tabs", inputs: { list: "list", activeTabId: "activeTabId", disabled: "disabled", orientation: "orientation", variant: "variant", colors: "colors" }, outputs: { change: "change" }, ngImport: i0, template: "<div
|
|
6288
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: BkTabs, isStandalone: true, selector: "bk-tabs", inputs: { list: "list", activeTabId: "activeTabId", disabled: "disabled", orientation: "orientation", variant: "variant", colors: "colors", lockNavigationOnError: "lockNavigationOnError" }, outputs: { change: "change" }, ngImport: i0, template: "<div\r\n class=\"tabs-container\"\r\n [class.tabs-container--segmented]=\"isSegmented\"\r\n [ngClass]=\"isSegmented ? wrapperClass : (isVertical ? 'w-auto' : 'overflow-x-auto whitespace-nowrap')\">\r\n <ul\r\n class=\"tabs-list\"\r\n [class.tabs-list--segmented]=\"isSegmented\"\r\n [class.tabs-list--segmented-vertical]=\"isSegmented && isVertical\"\r\n [class.tabs-list--vertical]=\"isVertical && !isSegmented\"\r\n [attr.aria-orientation]=\"orientation\"\r\n role=\"tablist\">\r\n @for (tab of list; track tab.id; let i = $index) {\r\n <li class=\"tabs-item\" role=\"presentation\">\r\n <!--\r\n Tooltip logic:\r\n - Icon present (error or not) \u2192 tooltip on the icon, button gets no tooltip\r\n - No icon + error state \u2192 tooltip on the button\r\n - No icon + no error \u2192 no tooltip\r\n -->\r\n <button\r\n type=\"button\"\r\n [id]=\"'tab-' + tab.id\"\r\n [attr.aria-selected]=\"isActive(tab.id)\"\r\n [attr.aria-controls]=\"'panel-' + tab.id\"\r\n [disabled]=\"tab.disabled || isLocked(tab)\"\r\n [class.tabs-button--active]=\"isActive(tab.id)\"\r\n [class.tabs-button--error]=\"tab.error\"\r\n [class.tabs-button--disabled]=\"disabled || tab.disabled || isLocked(tab)\"\r\n [class.tabs-button--icon-right]=\"tab.direction === 'right'\"\r\n [class.tabs-button--segmented]=\"isSegmented\"\r\n [ngClass]=\"[\r\n isSegmented ? segmentedTabClass(tab) : '',\r\n (isVertical && !isSegmented) ? 'w-full justify-start' : ''\r\n ]\"\r\n [bkTooltip]=\"(!getTabIcon(tab) && tab.error) ? (tab.iconTooltip || []) : []\"\r\n [bkTooltipPosition]=\"tab.iconTooltipDirection || 'top'\"\r\n class=\"tabs-button\"\r\n (click)=\"setActiveTab(tab)\"\r\n role=\"tab\">\r\n <!-- Left icon: tooltip lives on the icon when an icon is present -->\r\n <img\r\n [bkTooltip]=\"getTabIcon(tab) ? (tab.iconTooltip || []) : []\" [bkTooltipPosition]=\"tab.iconTooltipDirection || 'top'\"\r\n [attr.src]=\"getTabIcon(tab)\"\r\n [alt]=\"tab.iconAlt || tab.label\"\r\n [class.tabs-icon--hidden]=\"!getTabIcon(tab) || tab.direction === 'right'\"\r\n class=\"tabs-icon\">\r\n <span class=\"tabs-label\">{{ tab.label }}</span>\r\n <!-- Right icon: same tooltip rule, shown when direction is 'right' -->\r\n <img\r\n [bkTooltip]=\"getTabIcon(tab) ? (tab.iconTooltip || []) : []\" [bkTooltipPosition]=\"tab.iconTooltipDirection || 'top'\"\r\n [attr.src]=\"getTabIcon(tab)\"\r\n [alt]=\"tab.iconAlt || tab.label\"\r\n [class.tabs-icon--hidden]=\"!getTabIcon(tab) || tab.direction !== 'right'\"\r\n class=\"tabs-icon\">\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n</div>\r\n\r\n", styles: [".tabs-container{@apply w-full;}.tabs-list{@apply grid grid-flow-col auto-cols-max gap-[4px] list-none m-0 px-0 py-0.5;}.tabs-item{@apply min-w-0;}.tabs-list--vertical{@apply grid-cols-1 grid-flow-row auto-rows-max;}.tabs-button{@apply flex items-center gap-[6px] px-3 py-2 rounded-md border-0 bg-transparent cursor-pointer transition-all duration-100 outline-none;@apply text-sm leading-[11px] font-medium text-[#141414] tracking-[-.28];}.tabs-button:hover:not(:disabled):not(.tabs-button--active){@apply text-[#141414] bg-[#EDEEF0];}.tabs-list--vertical .tabs-button{@apply py-[9px];}.tabs-button--active{@apply bg-[#141414] text-white;}.tabs-button--error{@apply bg-[#C10007] text-white border-0;}.tabs-button--disabled{@apply opacity-50 cursor-not-allowed;}.tabs-icon{@apply size-4 flex-shrink-0;max-width:1rem;opacity:1;overflow:hidden;transition:max-width .2s ease,opacity .2s ease}.tabs-icon--hidden{max-width:0;opacity:0}.tabs-label{@apply whitespace-nowrap;}.tabs-container--segmented{@apply p-[3px] rounded-md w-full md:w-auto overflow-x-auto;background:#54578e12}.tabs-list--segmented{@apply grid grid-flow-col items-center gap-2 rounded-md font-medium text-sm leading-4 w-full sm:w-auto py-0;grid-auto-columns:minmax(max-content,1fr);color:#6b7080}.tabs-button--segmented{@apply border border-transparent px-3 sm:min-w-[5rem] py-1.5 w-full text-center justify-center rounded;color:#6b7080}.tabs-button--segmented:hover:not(:disabled):not(.tabs-button--active){border-color:#42578a26;background:#fff;color:#15191e}.tabs-button--segmented.tabs-button--active{background:#fff;color:#15191e}.tabs-button--segmented.tabs-button--error{background:#fee2e2;border-color:#fca5a5;color:#c10007}.tabs-button--segmented.tabs-button--error.tabs-button--active{background:#fff;border-color:#c10007;color:#c10007}.tabs-button--segmented.tabs-button--error:hover:not(:disabled):not(.tabs-button--active){background:#fee2e2;border-color:#c10007;color:#c10007}.tabs-list--segmented-vertical{@apply grid-cols-1 grid-flow-row auto-rows-max gap-1;}.tabs-list--segmented-vertical .tabs-button--segmented{@apply justify-start text-left;}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: BKTooltipDirective, selector: "[bkTooltip]", inputs: ["bkTooltip", "bkTooltipPosition", "bkTooltipScrollable", "bkTooltipMaxHeight", "bkTooltipSize", "bkTooltipAutoHeight"] }] });
|
|
6153
6289
|
}
|
|
6154
6290
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkTabs, decorators: [{
|
|
6155
6291
|
type: Component,
|
|
6156
|
-
args: [{ selector: 'bk-tabs', standalone: true, imports: [CommonModule, BKTooltipDirective], template: "<div
|
|
6292
|
+
args: [{ selector: 'bk-tabs', standalone: true, imports: [CommonModule, BKTooltipDirective], template: "<div\r\n class=\"tabs-container\"\r\n [class.tabs-container--segmented]=\"isSegmented\"\r\n [ngClass]=\"isSegmented ? wrapperClass : (isVertical ? 'w-auto' : 'overflow-x-auto whitespace-nowrap')\">\r\n <ul\r\n class=\"tabs-list\"\r\n [class.tabs-list--segmented]=\"isSegmented\"\r\n [class.tabs-list--segmented-vertical]=\"isSegmented && isVertical\"\r\n [class.tabs-list--vertical]=\"isVertical && !isSegmented\"\r\n [attr.aria-orientation]=\"orientation\"\r\n role=\"tablist\">\r\n @for (tab of list; track tab.id; let i = $index) {\r\n <li class=\"tabs-item\" role=\"presentation\">\r\n <!--\r\n Tooltip logic:\r\n - Icon present (error or not) \u2192 tooltip on the icon, button gets no tooltip\r\n - No icon + error state \u2192 tooltip on the button\r\n - No icon + no error \u2192 no tooltip\r\n -->\r\n <button\r\n type=\"button\"\r\n [id]=\"'tab-' + tab.id\"\r\n [attr.aria-selected]=\"isActive(tab.id)\"\r\n [attr.aria-controls]=\"'panel-' + tab.id\"\r\n [disabled]=\"tab.disabled || isLocked(tab)\"\r\n [class.tabs-button--active]=\"isActive(tab.id)\"\r\n [class.tabs-button--error]=\"tab.error\"\r\n [class.tabs-button--disabled]=\"disabled || tab.disabled || isLocked(tab)\"\r\n [class.tabs-button--icon-right]=\"tab.direction === 'right'\"\r\n [class.tabs-button--segmented]=\"isSegmented\"\r\n [ngClass]=\"[\r\n isSegmented ? segmentedTabClass(tab) : '',\r\n (isVertical && !isSegmented) ? 'w-full justify-start' : ''\r\n ]\"\r\n [bkTooltip]=\"(!getTabIcon(tab) && tab.error) ? (tab.iconTooltip || []) : []\"\r\n [bkTooltipPosition]=\"tab.iconTooltipDirection || 'top'\"\r\n class=\"tabs-button\"\r\n (click)=\"setActiveTab(tab)\"\r\n role=\"tab\">\r\n <!-- Left icon: tooltip lives on the icon when an icon is present -->\r\n <img\r\n [bkTooltip]=\"getTabIcon(tab) ? (tab.iconTooltip || []) : []\" [bkTooltipPosition]=\"tab.iconTooltipDirection || 'top'\"\r\n [attr.src]=\"getTabIcon(tab)\"\r\n [alt]=\"tab.iconAlt || tab.label\"\r\n [class.tabs-icon--hidden]=\"!getTabIcon(tab) || tab.direction === 'right'\"\r\n class=\"tabs-icon\">\r\n <span class=\"tabs-label\">{{ tab.label }}</span>\r\n <!-- Right icon: same tooltip rule, shown when direction is 'right' -->\r\n <img\r\n [bkTooltip]=\"getTabIcon(tab) ? (tab.iconTooltip || []) : []\" [bkTooltipPosition]=\"tab.iconTooltipDirection || 'top'\"\r\n [attr.src]=\"getTabIcon(tab)\"\r\n [alt]=\"tab.iconAlt || tab.label\"\r\n [class.tabs-icon--hidden]=\"!getTabIcon(tab) || tab.direction !== 'right'\"\r\n class=\"tabs-icon\">\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n</div>\r\n\r\n", styles: [".tabs-container{@apply w-full;}.tabs-list{@apply grid grid-flow-col auto-cols-max gap-[4px] list-none m-0 px-0 py-0.5;}.tabs-item{@apply min-w-0;}.tabs-list--vertical{@apply grid-cols-1 grid-flow-row auto-rows-max;}.tabs-button{@apply flex items-center gap-[6px] px-3 py-2 rounded-md border-0 bg-transparent cursor-pointer transition-all duration-100 outline-none;@apply text-sm leading-[11px] font-medium text-[#141414] tracking-[-.28];}.tabs-button:hover:not(:disabled):not(.tabs-button--active){@apply text-[#141414] bg-[#EDEEF0];}.tabs-list--vertical .tabs-button{@apply py-[9px];}.tabs-button--active{@apply bg-[#141414] text-white;}.tabs-button--error{@apply bg-[#C10007] text-white border-0;}.tabs-button--disabled{@apply opacity-50 cursor-not-allowed;}.tabs-icon{@apply size-4 flex-shrink-0;max-width:1rem;opacity:1;overflow:hidden;transition:max-width .2s ease,opacity .2s ease}.tabs-icon--hidden{max-width:0;opacity:0}.tabs-label{@apply whitespace-nowrap;}.tabs-container--segmented{@apply p-[3px] rounded-md w-full md:w-auto overflow-x-auto;background:#54578e12}.tabs-list--segmented{@apply grid grid-flow-col items-center gap-2 rounded-md font-medium text-sm leading-4 w-full sm:w-auto py-0;grid-auto-columns:minmax(max-content,1fr);color:#6b7080}.tabs-button--segmented{@apply border border-transparent px-3 sm:min-w-[5rem] py-1.5 w-full text-center justify-center rounded;color:#6b7080}.tabs-button--segmented:hover:not(:disabled):not(.tabs-button--active){border-color:#42578a26;background:#fff;color:#15191e}.tabs-button--segmented.tabs-button--active{background:#fff;color:#15191e}.tabs-button--segmented.tabs-button--error{background:#fee2e2;border-color:#fca5a5;color:#c10007}.tabs-button--segmented.tabs-button--error.tabs-button--active{background:#fff;border-color:#c10007;color:#c10007}.tabs-button--segmented.tabs-button--error:hover:not(:disabled):not(.tabs-button--active){background:#fee2e2;border-color:#c10007;color:#c10007}.tabs-list--segmented-vertical{@apply grid-cols-1 grid-flow-row auto-rows-max gap-1;}.tabs-list--segmented-vertical .tabs-button--segmented{@apply justify-start text-left;}\n"] }]
|
|
6157
6293
|
}], propDecorators: { list: [{
|
|
6158
6294
|
type: Input
|
|
6159
6295
|
}], activeTabId: [{
|
|
@@ -6166,6 +6302,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
6166
6302
|
type: Input
|
|
6167
6303
|
}], colors: [{
|
|
6168
6304
|
type: Input
|
|
6305
|
+
}], lockNavigationOnError: [{
|
|
6306
|
+
type: Input
|
|
6169
6307
|
}], change: [{
|
|
6170
6308
|
type: Output
|
|
6171
6309
|
}] } });
|
|
@@ -9399,6 +9537,231 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
9399
9537
|
args: ['document:keydown.escape']
|
|
9400
9538
|
}] } });
|
|
9401
9539
|
|
|
9540
|
+
/**
|
|
9541
|
+
* A simple, responsive navigation menu. Styled to match the default `bk-tabs`
|
|
9542
|
+
* theme (dark active pill, light hover).
|
|
9543
|
+
*
|
|
9544
|
+
* - Orientation: `vertical` (stacked) / `horizontal` (menubar row).
|
|
9545
|
+
* - Items with `children` are expandable, shown with a chevron that rotates
|
|
9546
|
+
* open/closed.
|
|
9547
|
+
* - Submenus render inline (accordion) or as pop-ups. Pop-ups adapt to the
|
|
9548
|
+
* orientation: they drop *below* a horizontal top-level item and fly out to
|
|
9549
|
+
* the *right* for vertical menus / deeper levels.
|
|
9550
|
+
*/
|
|
9551
|
+
class BkMenu {
|
|
9552
|
+
host;
|
|
9553
|
+
items = [];
|
|
9554
|
+
orientation = 'vertical';
|
|
9555
|
+
submenuMode = 'inline';
|
|
9556
|
+
/**
|
|
9557
|
+
* Keep only one submenu open per level: opening a parent collapses its
|
|
9558
|
+
* siblings (and their descendants) so the menu stays compact. Pop-up mode
|
|
9559
|
+
* always behaves this way; this input adds it to inline (accordion) mode.
|
|
9560
|
+
*/
|
|
9561
|
+
singleOpen = false;
|
|
9562
|
+
/** Id of the currently selected leaf item. Two-way bindable. */
|
|
9563
|
+
activeItemId = '';
|
|
9564
|
+
/** Tint item icons: dark by default, white on the active (dark) row. */
|
|
9565
|
+
tintIcons = true;
|
|
9566
|
+
/**
|
|
9567
|
+
* Optional max height for the menu (e.g. '320px', '60vh', or a number of px).
|
|
9568
|
+
* When set, the item list scrolls vertically once it exceeds this height.
|
|
9569
|
+
* Pop-up fly-outs are positioned `fixed`, so they still escape the scroll box.
|
|
9570
|
+
*/
|
|
9571
|
+
maxHeight = null;
|
|
9572
|
+
activeItemIdChange = new EventEmitter();
|
|
9573
|
+
itemClick = new EventEmitter();
|
|
9574
|
+
/** Ids of currently expanded/opened parent nodes. */
|
|
9575
|
+
openIds = new Set();
|
|
9576
|
+
/** Viewport coordinates for each open fixed-position fly-out, keyed by id. */
|
|
9577
|
+
popupPos = new Map();
|
|
9578
|
+
constructor(host) {
|
|
9579
|
+
this.host = host;
|
|
9580
|
+
}
|
|
9581
|
+
get isVertical() { return this.orientation === 'vertical'; }
|
|
9582
|
+
get isPopup() { return this.submenuMode === 'popup'; }
|
|
9583
|
+
// CSS max-height value for the scroll container (accepts px number or string).
|
|
9584
|
+
get maxHeightStyle() {
|
|
9585
|
+
if (this.maxHeight == null)
|
|
9586
|
+
return null;
|
|
9587
|
+
return typeof this.maxHeight === 'number' ? `${this.maxHeight}px` : this.maxHeight;
|
|
9588
|
+
}
|
|
9589
|
+
// Fixed-position coordinates for an open fly-out (or null when not open/popup).
|
|
9590
|
+
popupTop(item) {
|
|
9591
|
+
return this.isPopup ? (this.popupPos.get(item.id)?.top ?? null) : null;
|
|
9592
|
+
}
|
|
9593
|
+
popupLeft(item) {
|
|
9594
|
+
return this.isPopup ? (this.popupPos.get(item.id)?.left ?? null) : null;
|
|
9595
|
+
}
|
|
9596
|
+
hasChildren(item) {
|
|
9597
|
+
return !!item.children && item.children.length > 0;
|
|
9598
|
+
}
|
|
9599
|
+
isOpen(item) {
|
|
9600
|
+
return this.openIds.has(item.id);
|
|
9601
|
+
}
|
|
9602
|
+
isActive(item) {
|
|
9603
|
+
return this.activeItemId === item.id;
|
|
9604
|
+
}
|
|
9605
|
+
// True when the active leaf lives somewhere inside this item's subtree, so a
|
|
9606
|
+
// parent shows the active state even after its pop-up closes.
|
|
9607
|
+
isActiveAncestor(item) {
|
|
9608
|
+
return !!item.children && item.children.some(child => child.id === this.activeItemId || this.isActiveAncestor(child));
|
|
9609
|
+
}
|
|
9610
|
+
// Highlight = the item itself is active. In pop-up mode a parent also lights
|
|
9611
|
+
// up when one of its descendants is active (so the section stays marked after
|
|
9612
|
+
// the fly-out closes); inline mode highlights the selected leaf only.
|
|
9613
|
+
isHighlighted(item) {
|
|
9614
|
+
return this.isActive(item) || (this.isPopup && this.isActiveAncestor(item));
|
|
9615
|
+
}
|
|
9616
|
+
// Left indent for inline nested items (vertical only).
|
|
9617
|
+
inlineIndent(level) {
|
|
9618
|
+
return this.isVertical && !this.isPopup ? 16 + level * 16 : null;
|
|
9619
|
+
}
|
|
9620
|
+
// Handle a click on any menu button.
|
|
9621
|
+
// Leaves select + emit. Parents toggle in inline mode; in pop-up mode they
|
|
9622
|
+
// are opened by hover, so a click on a parent does nothing.
|
|
9623
|
+
onItemClick(item, siblings, event) {
|
|
9624
|
+
event.stopPropagation();
|
|
9625
|
+
if (item.disabled)
|
|
9626
|
+
return;
|
|
9627
|
+
if (this.hasChildren(item)) {
|
|
9628
|
+
if (!this.isPopup)
|
|
9629
|
+
this.toggle(item, siblings);
|
|
9630
|
+
return;
|
|
9631
|
+
}
|
|
9632
|
+
this.activeItemId = item.id;
|
|
9633
|
+
this.activeItemIdChange.emit(item.id);
|
|
9634
|
+
this.itemClick.emit(item);
|
|
9635
|
+
if (this.isPopup)
|
|
9636
|
+
this.openIds.clear(); // dismiss pop-ups after picking a leaf
|
|
9637
|
+
}
|
|
9638
|
+
// Pop-up mode: open a parent's fly-out on hover (and close its siblings).
|
|
9639
|
+
onItemEnter(item, siblings, event, level) {
|
|
9640
|
+
if (!this.isPopup || item.disabled || !this.hasChildren(item))
|
|
9641
|
+
return;
|
|
9642
|
+
for (const sibling of siblings) {
|
|
9643
|
+
if (sibling.id !== item.id)
|
|
9644
|
+
this.closeBranch(sibling);
|
|
9645
|
+
}
|
|
9646
|
+
this.openIds.add(item.id);
|
|
9647
|
+
const li = event.currentTarget;
|
|
9648
|
+
const trigger = li.querySelector(':scope > .bk-menu__button') ?? li;
|
|
9649
|
+
const rect = trigger.getBoundingClientRect();
|
|
9650
|
+
const flyRight = this.isVertical || level > 0;
|
|
9651
|
+
// First pass with an estimated size, then refine once the panel has rendered
|
|
9652
|
+
// so we can flip/clamp against the real viewport edges.
|
|
9653
|
+
this.popupPos.set(item.id, this.computePopupPos(rect, flyRight, null));
|
|
9654
|
+
requestAnimationFrame(() => {
|
|
9655
|
+
if (!this.openIds.has(item.id))
|
|
9656
|
+
return;
|
|
9657
|
+
const panel = li.querySelector(':scope > .bk-menu__submenu');
|
|
9658
|
+
if (!panel)
|
|
9659
|
+
return;
|
|
9660
|
+
this.popupPos.set(item.id, this.computePopupPos(rect, flyRight, { w: panel.offsetWidth, h: panel.offsetHeight }));
|
|
9661
|
+
});
|
|
9662
|
+
}
|
|
9663
|
+
// Compute a fixed-position for a fly-out, flipping/clamping to stay on-screen.
|
|
9664
|
+
computePopupPos(rect, flyRight, size) {
|
|
9665
|
+
const gap = 4;
|
|
9666
|
+
const pad = 8;
|
|
9667
|
+
const vw = window.innerWidth;
|
|
9668
|
+
const vh = window.innerHeight;
|
|
9669
|
+
const w = size?.w ?? 220;
|
|
9670
|
+
const h = size?.h ?? 0;
|
|
9671
|
+
let left;
|
|
9672
|
+
let top;
|
|
9673
|
+
if (flyRight) {
|
|
9674
|
+
left = rect.right + gap;
|
|
9675
|
+
if (left + w > vw - pad)
|
|
9676
|
+
left = rect.left - w - gap; // flip to the left
|
|
9677
|
+
top = rect.top;
|
|
9678
|
+
}
|
|
9679
|
+
else {
|
|
9680
|
+
top = rect.bottom + gap;
|
|
9681
|
+
left = rect.left;
|
|
9682
|
+
if (left + w > vw - pad)
|
|
9683
|
+
left = Math.max(pad, vw - pad - w);
|
|
9684
|
+
}
|
|
9685
|
+
if (left < pad)
|
|
9686
|
+
left = pad;
|
|
9687
|
+
if (h && top + h > vh - pad)
|
|
9688
|
+
top = Math.max(pad, vh - pad - h);
|
|
9689
|
+
return { top, left };
|
|
9690
|
+
}
|
|
9691
|
+
// Pop-up mode: close the fly-out (and any nested ones) when the pointer leaves.
|
|
9692
|
+
onItemLeave(item) {
|
|
9693
|
+
if (!this.isPopup || !this.hasChildren(item))
|
|
9694
|
+
return;
|
|
9695
|
+
this.closeBranch(item);
|
|
9696
|
+
}
|
|
9697
|
+
toggle(item, siblings) {
|
|
9698
|
+
const wasOpen = this.openIds.has(item.id);
|
|
9699
|
+
if (this.isPopup || this.singleOpen) {
|
|
9700
|
+
// Keep a single open branch per level — collapse siblings (and their
|
|
9701
|
+
// descendants) first so the menu stays compact.
|
|
9702
|
+
for (const sibling of siblings)
|
|
9703
|
+
this.closeBranch(sibling);
|
|
9704
|
+
}
|
|
9705
|
+
if (wasOpen)
|
|
9706
|
+
this.closeBranch(item);
|
|
9707
|
+
else
|
|
9708
|
+
this.openIds.add(item.id);
|
|
9709
|
+
}
|
|
9710
|
+
// Remove an item and all of its descendants from the open set.
|
|
9711
|
+
closeBranch(item) {
|
|
9712
|
+
this.openIds.delete(item.id);
|
|
9713
|
+
this.popupPos.delete(item.id);
|
|
9714
|
+
item.children?.forEach(child => this.closeBranch(child));
|
|
9715
|
+
}
|
|
9716
|
+
// Pop-up mode: click outside the menu closes any open fly-outs.
|
|
9717
|
+
onDocumentClick(event) {
|
|
9718
|
+
if (this.isPopup && !this.host.nativeElement.contains(event.target)) {
|
|
9719
|
+
this.openIds.clear();
|
|
9720
|
+
this.popupPos.clear();
|
|
9721
|
+
}
|
|
9722
|
+
}
|
|
9723
|
+
// Positions become stale on scroll/resize — close fly-outs to avoid drift.
|
|
9724
|
+
onViewportChange() {
|
|
9725
|
+
if (this.isPopup && this.openIds.size) {
|
|
9726
|
+
this.openIds.clear();
|
|
9727
|
+
this.popupPos.clear();
|
|
9728
|
+
}
|
|
9729
|
+
}
|
|
9730
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkMenu, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
9731
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: BkMenu, isStandalone: true, selector: "bk-menu", inputs: { items: "items", orientation: "orientation", submenuMode: "submenuMode", singleOpen: "singleOpen", activeItemId: "activeItemId", tintIcons: "tintIcons", maxHeight: "maxHeight" }, outputs: { activeItemIdChange: "activeItemIdChange", itemClick: "itemClick" }, host: { listeners: { "document:click": "onDocumentClick($event)", "window:scroll": "onViewportChange()", "window:resize": "onViewportChange()" } }, ngImport: i0, template: "<nav\r\n class=\"bk-menu\"\r\n [class.bk-menu--vertical]=\"isVertical\"\r\n [class.bk-menu--horizontal]=\"!isVertical\"\r\n [class.bk-menu--popup]=\"isPopup\"\r\n [class.bk-menu--tint]=\"tintIcons\"\r\n role=\"menubar\"\r\n [attr.aria-orientation]=\"orientation\">\r\n <ul\r\n class=\"bk-menu__list\"\r\n [class.bk-menu__list--scroll]=\"maxHeightStyle\"\r\n [style.maxHeight]=\"maxHeightStyle\">\r\n <ng-container *ngTemplateOutlet=\"itemTpl; context: { $implicit: items, level: 0 }\"></ng-container>\r\n </ul>\r\n</nav>\r\n\r\n<!-- Recursive item renderer. Context: $implicit = items at this level, level = depth. -->\r\n<ng-template #itemTpl let-items let-level=\"level\">\r\n @for (item of items; track item.id) {\r\n <li\r\n class=\"bk-menu__item\"\r\n [class.bk-menu__item--has-children]=\"hasChildren(item)\"\r\n [class.bk-menu__item--open]=\"isOpen(item)\"\r\n (mouseenter)=\"onItemEnter(item, items, $event, level)\"\r\n (mouseleave)=\"onItemLeave(item)\"\r\n role=\"none\">\r\n <button\r\n type=\"button\"\r\n class=\"bk-menu__button\"\r\n [class.bk-menu__button--active]=\"isHighlighted(item)\"\r\n [class.bk-menu__button--disabled]=\"item.disabled\"\r\n [class.bk-menu__button--parent]=\"hasChildren(item)\"\r\n [style.paddingLeft.px]=\"inlineIndent(level)\"\r\n [disabled]=\"item.disabled\"\r\n [attr.aria-haspopup]=\"hasChildren(item) ? 'true' : null\"\r\n [attr.aria-expanded]=\"hasChildren(item) ? isOpen(item) : null\"\r\n role=\"menuitem\"\r\n (click)=\"onItemClick(item, items, $event)\">\r\n @if (item.icon) {\r\n <img class=\"bk-menu__icon\" [attr.src]=\"item.icon\" [alt]=\"item.iconAlt || item.label\" />\r\n }\r\n <span class=\"bk-menu__label\">{{ item.label }}</span>\r\n @if (hasChildren(item)) {\r\n <svg\r\n class=\"bk-menu__chevron\"\r\n [class.bk-menu__chevron--open]=\"!isPopup && isOpen(item)\"\r\n [class.bk-menu__chevron--right]=\"isPopup && (isVertical || level > 0)\"\r\n viewBox=\"0 0 24 24\"\r\n width=\"16\"\r\n height=\"16\"\r\n fill=\"none\"\r\n aria-hidden=\"true\">\r\n <path d=\"M6 9L12 15L18 9\" stroke=\"currentColor\" stroke-width=\"2\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n }\r\n </button>\r\n\r\n @if (hasChildren(item)) {\r\n <ul\r\n class=\"bk-menu__submenu\"\r\n [class.bk-menu__submenu--inline]=\"!isPopup\"\r\n [class.bk-menu__submenu--popup]=\"isPopup\"\r\n [class.bk-menu__submenu--open]=\"isOpen(item)\"\r\n [class.bk-menu__submenu--popup-right]=\"isPopup && (isVertical || level > 0)\"\r\n [class.bk-menu__submenu--popup-down]=\"isPopup && !isVertical && level === 0\"\r\n [style.top.px]=\"popupTop(item)\"\r\n [style.left.px]=\"popupLeft(item)\"\r\n role=\"menu\">\r\n <ng-container\r\n *ngTemplateOutlet=\"itemTpl; context: { $implicit: item.children, level: level + 1 }\">\r\n </ng-container>\r\n </ul>\r\n }\r\n </li>\r\n }\r\n</ng-template>\r\n", styles: [".bk-menu{--menu-bg: #ffffff;--menu-text: #141414;--menu-subtext: #6b7080;--menu-hover-bg: #edeef0;--menu-hover-text: #141414;--menu-active-bg: #141414;--menu-active-text: #ffffff;--menu-border: #efeff1;--menu-shadow: 0 7px 18px 0 rgba(0, 0, 0, .09);--menu-radius: 8px;--menu-disabled: .5;display:block;width:100%;max-width:100%;font-size:.875rem;line-height:1.25rem;font-weight:500;letter-spacing:-.28px;background:var(--menu-bg);color:var(--menu-text);border:1px solid var(--menu-border);border-radius:var(--menu-radius);overflow:hidden}.bk-menu__list,.bk-menu__submenu{list-style:none;margin:0;padding:0}.bk-menu__list--scroll{overflow:auto}.bk-menu--horizontal>.bk-menu__list{display:flex;flex-direction:row;align-items:stretch;overflow-x:auto}.bk-menu__item{position:relative}.bk-menu__button{display:flex;align-items:center;gap:8px;width:100%;padding:10px 12px;margin:0;border:0;background:transparent;color:inherit;font:inherit;letter-spacing:inherit;text-align:left;cursor:pointer;white-space:nowrap;border-radius:6px;transition:background .1s ease,color .1s ease}.bk-menu--horizontal>.bk-menu__list>.bk-menu__item>.bk-menu__button{width:auto}.bk-menu__button:hover:not(:disabled):not(.bk-menu__button--active){background:var(--menu-hover-bg);color:var(--menu-hover-text)}.bk-menu__button--active{background:var(--menu-active-bg);color:var(--menu-active-text)}.bk-menu__button--disabled,.bk-menu__button:disabled{opacity:var(--menu-disabled);cursor:not-allowed}.bk-menu__submenu .bk-menu__button{color:var(--menu-subtext);font-weight:400}.bk-menu__submenu .bk-menu__button--active{color:var(--menu-active-text)}.bk-menu__icon{width:16px;height:16px;flex-shrink:0;object-fit:contain}.bk-menu--tint .bk-menu__icon{filter:brightness(0) saturate(0)}.bk-menu--tint .bk-menu__button--active .bk-menu__icon{filter:brightness(0) invert(1)}.bk-menu__label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.bk-menu--horizontal>.bk-menu__list>.bk-menu__item>.bk-menu__button>.bk-menu__label{flex:0 0 auto;overflow:visible}.bk-menu__chevron{flex-shrink:0;transition:transform .18s ease;opacity:.8}.bk-menu__chevron--open{transform:rotate(180deg)}.bk-menu__chevron--right{transform:rotate(-90deg)}.bk-menu__submenu--inline{overflow:hidden;max-height:0;transition:max-height .2s ease}.bk-menu__submenu--inline.bk-menu__submenu--open{max-height:1000px}.bk-menu__submenu--popup{position:fixed;z-index:1000;min-width:200px;max-width:280px;max-height:calc(100vh - 16px);overflow-y:auto;background:var(--menu-bg);border:1px solid var(--menu-border);border-radius:12px;box-shadow:var(--menu-shadow);padding:4px;display:none}.bk-menu__submenu--popup.bk-menu__submenu--open{display:block}.bk-menu__submenu--popup-right:before{content:\"\";position:absolute;top:0;left:-6px;width:6px;height:100%}.bk-menu__submenu--popup-down:before{content:\"\";position:absolute;top:-6px;left:0;width:100%;height:6px}@media (max-width: 640px){.bk-menu--horizontal>.bk-menu__list{flex-direction:column;overflow-x:visible}.bk-menu--horizontal>.bk-menu__list>.bk-menu__item>.bk-menu__button{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
9732
|
+
}
|
|
9733
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkMenu, decorators: [{
|
|
9734
|
+
type: Component,
|
|
9735
|
+
args: [{ selector: 'bk-menu', imports: [CommonModule], template: "<nav\r\n class=\"bk-menu\"\r\n [class.bk-menu--vertical]=\"isVertical\"\r\n [class.bk-menu--horizontal]=\"!isVertical\"\r\n [class.bk-menu--popup]=\"isPopup\"\r\n [class.bk-menu--tint]=\"tintIcons\"\r\n role=\"menubar\"\r\n [attr.aria-orientation]=\"orientation\">\r\n <ul\r\n class=\"bk-menu__list\"\r\n [class.bk-menu__list--scroll]=\"maxHeightStyle\"\r\n [style.maxHeight]=\"maxHeightStyle\">\r\n <ng-container *ngTemplateOutlet=\"itemTpl; context: { $implicit: items, level: 0 }\"></ng-container>\r\n </ul>\r\n</nav>\r\n\r\n<!-- Recursive item renderer. Context: $implicit = items at this level, level = depth. -->\r\n<ng-template #itemTpl let-items let-level=\"level\">\r\n @for (item of items; track item.id) {\r\n <li\r\n class=\"bk-menu__item\"\r\n [class.bk-menu__item--has-children]=\"hasChildren(item)\"\r\n [class.bk-menu__item--open]=\"isOpen(item)\"\r\n (mouseenter)=\"onItemEnter(item, items, $event, level)\"\r\n (mouseleave)=\"onItemLeave(item)\"\r\n role=\"none\">\r\n <button\r\n type=\"button\"\r\n class=\"bk-menu__button\"\r\n [class.bk-menu__button--active]=\"isHighlighted(item)\"\r\n [class.bk-menu__button--disabled]=\"item.disabled\"\r\n [class.bk-menu__button--parent]=\"hasChildren(item)\"\r\n [style.paddingLeft.px]=\"inlineIndent(level)\"\r\n [disabled]=\"item.disabled\"\r\n [attr.aria-haspopup]=\"hasChildren(item) ? 'true' : null\"\r\n [attr.aria-expanded]=\"hasChildren(item) ? isOpen(item) : null\"\r\n role=\"menuitem\"\r\n (click)=\"onItemClick(item, items, $event)\">\r\n @if (item.icon) {\r\n <img class=\"bk-menu__icon\" [attr.src]=\"item.icon\" [alt]=\"item.iconAlt || item.label\" />\r\n }\r\n <span class=\"bk-menu__label\">{{ item.label }}</span>\r\n @if (hasChildren(item)) {\r\n <svg\r\n class=\"bk-menu__chevron\"\r\n [class.bk-menu__chevron--open]=\"!isPopup && isOpen(item)\"\r\n [class.bk-menu__chevron--right]=\"isPopup && (isVertical || level > 0)\"\r\n viewBox=\"0 0 24 24\"\r\n width=\"16\"\r\n height=\"16\"\r\n fill=\"none\"\r\n aria-hidden=\"true\">\r\n <path d=\"M6 9L12 15L18 9\" stroke=\"currentColor\" stroke-width=\"2\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n }\r\n </button>\r\n\r\n @if (hasChildren(item)) {\r\n <ul\r\n class=\"bk-menu__submenu\"\r\n [class.bk-menu__submenu--inline]=\"!isPopup\"\r\n [class.bk-menu__submenu--popup]=\"isPopup\"\r\n [class.bk-menu__submenu--open]=\"isOpen(item)\"\r\n [class.bk-menu__submenu--popup-right]=\"isPopup && (isVertical || level > 0)\"\r\n [class.bk-menu__submenu--popup-down]=\"isPopup && !isVertical && level === 0\"\r\n [style.top.px]=\"popupTop(item)\"\r\n [style.left.px]=\"popupLeft(item)\"\r\n role=\"menu\">\r\n <ng-container\r\n *ngTemplateOutlet=\"itemTpl; context: { $implicit: item.children, level: level + 1 }\">\r\n </ng-container>\r\n </ul>\r\n }\r\n </li>\r\n }\r\n</ng-template>\r\n", styles: [".bk-menu{--menu-bg: #ffffff;--menu-text: #141414;--menu-subtext: #6b7080;--menu-hover-bg: #edeef0;--menu-hover-text: #141414;--menu-active-bg: #141414;--menu-active-text: #ffffff;--menu-border: #efeff1;--menu-shadow: 0 7px 18px 0 rgba(0, 0, 0, .09);--menu-radius: 8px;--menu-disabled: .5;display:block;width:100%;max-width:100%;font-size:.875rem;line-height:1.25rem;font-weight:500;letter-spacing:-.28px;background:var(--menu-bg);color:var(--menu-text);border:1px solid var(--menu-border);border-radius:var(--menu-radius);overflow:hidden}.bk-menu__list,.bk-menu__submenu{list-style:none;margin:0;padding:0}.bk-menu__list--scroll{overflow:auto}.bk-menu--horizontal>.bk-menu__list{display:flex;flex-direction:row;align-items:stretch;overflow-x:auto}.bk-menu__item{position:relative}.bk-menu__button{display:flex;align-items:center;gap:8px;width:100%;padding:10px 12px;margin:0;border:0;background:transparent;color:inherit;font:inherit;letter-spacing:inherit;text-align:left;cursor:pointer;white-space:nowrap;border-radius:6px;transition:background .1s ease,color .1s ease}.bk-menu--horizontal>.bk-menu__list>.bk-menu__item>.bk-menu__button{width:auto}.bk-menu__button:hover:not(:disabled):not(.bk-menu__button--active){background:var(--menu-hover-bg);color:var(--menu-hover-text)}.bk-menu__button--active{background:var(--menu-active-bg);color:var(--menu-active-text)}.bk-menu__button--disabled,.bk-menu__button:disabled{opacity:var(--menu-disabled);cursor:not-allowed}.bk-menu__submenu .bk-menu__button{color:var(--menu-subtext);font-weight:400}.bk-menu__submenu .bk-menu__button--active{color:var(--menu-active-text)}.bk-menu__icon{width:16px;height:16px;flex-shrink:0;object-fit:contain}.bk-menu--tint .bk-menu__icon{filter:brightness(0) saturate(0)}.bk-menu--tint .bk-menu__button--active .bk-menu__icon{filter:brightness(0) invert(1)}.bk-menu__label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.bk-menu--horizontal>.bk-menu__list>.bk-menu__item>.bk-menu__button>.bk-menu__label{flex:0 0 auto;overflow:visible}.bk-menu__chevron{flex-shrink:0;transition:transform .18s ease;opacity:.8}.bk-menu__chevron--open{transform:rotate(180deg)}.bk-menu__chevron--right{transform:rotate(-90deg)}.bk-menu__submenu--inline{overflow:hidden;max-height:0;transition:max-height .2s ease}.bk-menu__submenu--inline.bk-menu__submenu--open{max-height:1000px}.bk-menu__submenu--popup{position:fixed;z-index:1000;min-width:200px;max-width:280px;max-height:calc(100vh - 16px);overflow-y:auto;background:var(--menu-bg);border:1px solid var(--menu-border);border-radius:12px;box-shadow:var(--menu-shadow);padding:4px;display:none}.bk-menu__submenu--popup.bk-menu__submenu--open{display:block}.bk-menu__submenu--popup-right:before{content:\"\";position:absolute;top:0;left:-6px;width:6px;height:100%}.bk-menu__submenu--popup-down:before{content:\"\";position:absolute;top:-6px;left:0;width:100%;height:6px}@media (max-width: 640px){.bk-menu--horizontal>.bk-menu__list{flex-direction:column;overflow-x:visible}.bk-menu--horizontal>.bk-menu__list>.bk-menu__item>.bk-menu__button{width:100%}}\n"] }]
|
|
9736
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { items: [{
|
|
9737
|
+
type: Input
|
|
9738
|
+
}], orientation: [{
|
|
9739
|
+
type: Input
|
|
9740
|
+
}], submenuMode: [{
|
|
9741
|
+
type: Input
|
|
9742
|
+
}], singleOpen: [{
|
|
9743
|
+
type: Input
|
|
9744
|
+
}], activeItemId: [{
|
|
9745
|
+
type: Input
|
|
9746
|
+
}], tintIcons: [{
|
|
9747
|
+
type: Input
|
|
9748
|
+
}], maxHeight: [{
|
|
9749
|
+
type: Input
|
|
9750
|
+
}], activeItemIdChange: [{
|
|
9751
|
+
type: Output
|
|
9752
|
+
}], itemClick: [{
|
|
9753
|
+
type: Output
|
|
9754
|
+
}], onDocumentClick: [{
|
|
9755
|
+
type: HostListener,
|
|
9756
|
+
args: ['document:click', ['$event']]
|
|
9757
|
+
}], onViewportChange: [{
|
|
9758
|
+
type: HostListener,
|
|
9759
|
+
args: ['window:scroll']
|
|
9760
|
+
}, {
|
|
9761
|
+
type: HostListener,
|
|
9762
|
+
args: ['window:resize']
|
|
9763
|
+
}] } });
|
|
9764
|
+
|
|
9402
9765
|
/*
|
|
9403
9766
|
* Public API Surface of brickclay-lib
|
|
9404
9767
|
*/
|
|
@@ -9408,5 +9771,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
9408
9771
|
* Generated bundle index. Do not edit.
|
|
9409
9772
|
*/
|
|
9410
9773
|
|
|
9411
|
-
export { BKTooltipDirective, BK_DEFAULT_DIALOG_CONFIG, BK_DIALOG_DATA, BK_DIALOG_GLOBAL_CONFIG, BkAvatar, BkAvatarUploader, BkBadge, BkButton, BkButtonGroup, BkCalendarManagerService, BkCheckbox, BkColumnFilterService, BkColumnSelect, BkCustomCalendar, BkDialogActions, BkDialogClose, BkDialogContent, BkDialogModule, BkDialogRef, BkDialogService, BkDialogTitle, BkDropdown, BkFileCard, BkFilePicker, BkGrid, BkHierarchicalSelect, BkIconButton, BkInput, BkInputChips, BkLoader, BkPagination, BkPill, BkRadioButton, BkScheduledDatePicker, BkSelect, BkSpinner, BkTabs, BkTextarea, BkTimePicker, BkToastr, BkToastrService, BkToggle, BkValidator, BrickclayIcons, BrickclayLib, CalendarModule, CalendarSelection, ColumnFilterOption, getDialogBackdropAnimation, getDialogPanelAnimation };
|
|
9774
|
+
export { BKTooltipDirective, BK_DEFAULT_DIALOG_CONFIG, BK_DIALOG_DATA, BK_DIALOG_GLOBAL_CONFIG, BkAvatar, BkAvatarUploader, BkBadge, BkButton, BkButtonGroup, BkCalendarManagerService, BkCheckbox, BkColumnFilterService, BkColumnSelect, BkCustomCalendar, BkDialogActions, BkDialogClose, BkDialogContent, BkDialogModule, BkDialogRef, BkDialogService, BkDialogTitle, BkDropdown, BkFileCard, BkFilePicker, BkGrid, BkHierarchicalSelect, BkIconButton, BkInput, BkInputChips, BkLoader, BkMenu, BkPagination, BkPill, BkRadioButton, BkScheduledDatePicker, BkSelect, BkSpinner, BkTabs, BkTextarea, BkTimePicker, BkToastr, BkToastrService, BkToggle, BkValidator, BrickclayIcons, BrickclayLib, CalendarModule, CalendarSelection, ColumnFilterOption, getDialogBackdropAnimation, getDialogPanelAnimation };
|
|
9412
9775
|
//# sourceMappingURL=brickclay-org-ui.mjs.map
|