cerxos-ui 0.0.17 → 0.0.19
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/cerxos-ui.mjs +258 -5
- package/fesm2022/cerxos-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/cerxos-ui.d.ts +89 -2
package/fesm2022/cerxos-ui.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Component, EventEmitter, ElementRef, HostListener, ViewChildren, ViewChild, Output, Input, ChangeDetectionStrategy, Attribute, forwardRef, Directive, ContentChildren, ContentChild, HostBinding, signal, Injectable,
|
|
2
|
+
import { Component, EventEmitter, ElementRef, HostListener, ViewChildren, ViewChild, Output, Input, ChangeDetectionStrategy, Attribute, forwardRef, Directive, ContentChildren, inject, ContentChild, HostBinding, signal, Injectable, Pipe } from '@angular/core';
|
|
3
3
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
4
4
|
import * as i1 from '@angular/common';
|
|
5
5
|
import { NgTemplateOutlet, CommonModule } from '@angular/common';
|
|
@@ -1922,6 +1922,7 @@ class CxsDialogComponent {
|
|
|
1922
1922
|
dialogPanel;
|
|
1923
1923
|
titleId = `cxs-dialog-title-${CxsDialogComponent.nextId++}`;
|
|
1924
1924
|
descriptionId = `cxs-dialog-description-${CxsDialogComponent.nextId++}`;
|
|
1925
|
+
pressStartedOnBackdrop = false;
|
|
1925
1926
|
constructor(hostClass) {
|
|
1926
1927
|
this.hostClass = hostClass;
|
|
1927
1928
|
}
|
|
@@ -1968,11 +1969,18 @@ class CxsDialogComponent {
|
|
|
1968
1969
|
this.openChange.emit(false);
|
|
1969
1970
|
this.closed.emit(reason);
|
|
1970
1971
|
}
|
|
1972
|
+
onBackdropPointerDown(event) {
|
|
1973
|
+
this.pressStartedOnBackdrop = event.target === event.currentTarget;
|
|
1974
|
+
}
|
|
1971
1975
|
onBackdropClick(event) {
|
|
1976
|
+
// A click only counts as a backdrop click when the press also started on the backdrop,
|
|
1977
|
+
// so dragging a text selection out of the panel does not close the dialog.
|
|
1978
|
+
const pressStartedOnBackdrop = this.pressStartedOnBackdrop;
|
|
1979
|
+
this.pressStartedOnBackdrop = false;
|
|
1972
1980
|
if (!this.closeOnBackdrop || !this.open) {
|
|
1973
1981
|
return;
|
|
1974
1982
|
}
|
|
1975
|
-
if (event.target === event.currentTarget) {
|
|
1983
|
+
if (pressStartedOnBackdrop && event.target === event.currentTarget) {
|
|
1976
1984
|
this.requestClose('backdrop');
|
|
1977
1985
|
}
|
|
1978
1986
|
}
|
|
@@ -1991,11 +1999,11 @@ class CxsDialogComponent {
|
|
|
1991
1999
|
});
|
|
1992
2000
|
}
|
|
1993
2001
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CxsDialogComponent, deps: [{ token: 'class', attribute: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
1994
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: CxsDialogComponent, isStandalone: true, selector: "cxs-dialog", inputs: { open: "open", title: "title", description: "description", ariaLabel: "ariaLabel", size: "size", dismissible: "dismissible", closeOnBackdrop: "closeOnBackdrop", closeOnEscape: "closeOnEscape" }, outputs: { openChange: "openChange", closed: "closed" }, viewQueries: [{ propertyName: "dialogPanel", first: true, predicate: ["dialogPanel"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (open) {\n <div
|
|
2002
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: CxsDialogComponent, isStandalone: true, selector: "cxs-dialog", inputs: { open: "open", title: "title", description: "description", ariaLabel: "ariaLabel", size: "size", dismissible: "dismissible", closeOnBackdrop: "closeOnBackdrop", closeOnEscape: "closeOnEscape" }, outputs: { openChange: "openChange", closed: "closed" }, viewQueries: [{ propertyName: "dialogPanel", first: true, predicate: ["dialogPanel"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (open) {\n <div\n [class]=\"overlayClass\"\n (pointerdown)=\"onBackdropPointerDown($event)\"\n (click)=\"onBackdropClick($event)\"\n >\n <div\n #dialogPanel\n [class]=\"panelClass\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"title ? titleId : null\"\n [attr.aria-describedby]=\"description ? descriptionId : null\"\n [attr.aria-label]=\"ariaLabelValue\"\n tabindex=\"-1\"\n (keydown)=\"onKeydown($event)\"\n >\n <div class=\"flex items-start gap-3\">\n <div class=\"flex-1\">\n @if (title) {\n <h2 [id]=\"titleId\" [class]=\"titleClass\">{{ title }}</h2>\n }\n @if (description) {\n <p [id]=\"descriptionId\" [class]=\"descriptionClass\">{{ description }}</p>\n }\n </div>\n @if (dismissible) {\n <button\n type=\"button\"\n [class]=\"closeButtonClass\"\n aria-label=\"Close dialog\"\n (click)=\"requestClose('dismiss')\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-6\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18 18 6M6 6l12 12\" />\n </svg>\n </button>\n }\n </div>\n\n <div [class]=\"bodyClass\">\n <ng-content></ng-content>\n </div>\n\n <div [class]=\"actionsClass\">\n <ng-content select=\"[cxsDialogActions]\"></ng-content>\n </div>\n </div>\n </div>\n}\n", styles: [":host{display:contents}.cxs-dialog-overlay{animation:cxs-dialog-overlay-appear .22s ease-out}.cxs-dialog-panel{animation:cxs-dialog-panel-appear .26s cubic-bezier(.16,1,.3,1);transform-origin:center;will-change:opacity,transform}@keyframes cxs-dialog-overlay-appear{0%{opacity:0}to{opacity:1}}@keyframes cxs-dialog-panel-appear{0%{opacity:0;transform:scale(.82)}to{opacity:1;transform:scale(1)}}@media(prefers-reduced-motion:reduce){.cxs-dialog-overlay,.cxs-dialog-panel{animation:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1995
2003
|
}
|
|
1996
2004
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CxsDialogComponent, decorators: [{
|
|
1997
2005
|
type: Component,
|
|
1998
|
-
args: [{ selector: 'cxs-dialog', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open) {\n <div
|
|
2006
|
+
args: [{ selector: 'cxs-dialog', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open) {\n <div\n [class]=\"overlayClass\"\n (pointerdown)=\"onBackdropPointerDown($event)\"\n (click)=\"onBackdropClick($event)\"\n >\n <div\n #dialogPanel\n [class]=\"panelClass\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"title ? titleId : null\"\n [attr.aria-describedby]=\"description ? descriptionId : null\"\n [attr.aria-label]=\"ariaLabelValue\"\n tabindex=\"-1\"\n (keydown)=\"onKeydown($event)\"\n >\n <div class=\"flex items-start gap-3\">\n <div class=\"flex-1\">\n @if (title) {\n <h2 [id]=\"titleId\" [class]=\"titleClass\">{{ title }}</h2>\n }\n @if (description) {\n <p [id]=\"descriptionId\" [class]=\"descriptionClass\">{{ description }}</p>\n }\n </div>\n @if (dismissible) {\n <button\n type=\"button\"\n [class]=\"closeButtonClass\"\n aria-label=\"Close dialog\"\n (click)=\"requestClose('dismiss')\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-6\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18 18 6M6 6l12 12\" />\n </svg>\n </button>\n }\n </div>\n\n <div [class]=\"bodyClass\">\n <ng-content></ng-content>\n </div>\n\n <div [class]=\"actionsClass\">\n <ng-content select=\"[cxsDialogActions]\"></ng-content>\n </div>\n </div>\n </div>\n}\n", styles: [":host{display:contents}.cxs-dialog-overlay{animation:cxs-dialog-overlay-appear .22s ease-out}.cxs-dialog-panel{animation:cxs-dialog-panel-appear .26s cubic-bezier(.16,1,.3,1);transform-origin:center;will-change:opacity,transform}@keyframes cxs-dialog-overlay-appear{0%{opacity:0}to{opacity:1}}@keyframes cxs-dialog-panel-appear{0%{opacity:0;transform:scale(.82)}to{opacity:1;transform:scale(1)}}@media(prefers-reduced-motion:reduce){.cxs-dialog-overlay,.cxs-dialog-panel{animation:none}}\n"] }]
|
|
1999
2007
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
2000
2008
|
type: Attribute,
|
|
2001
2009
|
args: ['class']
|
|
@@ -2380,6 +2388,251 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
|
|
|
2380
2388
|
args: ['document:keydown.escape']
|
|
2381
2389
|
}] } });
|
|
2382
2390
|
|
|
2391
|
+
const DAY_MS = 86_400_000;
|
|
2392
|
+
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
2393
|
+
const ISO_DAYS = [1, 2, 3, 4, 5, 6, 7];
|
|
2394
|
+
const STATE_TEXT = {
|
|
2395
|
+
done: 'done',
|
|
2396
|
+
empty: 'not done',
|
|
2397
|
+
off: 'not scheduled',
|
|
2398
|
+
'out-of-range': 'unavailable',
|
|
2399
|
+
padding: ''
|
|
2400
|
+
};
|
|
2401
|
+
const CELL_BASE_CLASSES = 'block h-[var(--cxs-habit-grid-cell-size,0.875rem)] w-[var(--cxs-habit-grid-cell-size,0.875rem)] shrink-0 rounded-[3px] ' +
|
|
2402
|
+
'outline-none focus-visible:ring-2 focus-visible:ring-[var(--cxs-color-focus)] focus-visible:ring-offset-1';
|
|
2403
|
+
const CELL_STATE_CLASSES = {
|
|
2404
|
+
done: 'bg-[var(--cxs-habit-grid-accent,var(--cxs-color-primary))]',
|
|
2405
|
+
empty: 'border border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface-hover)]',
|
|
2406
|
+
off: 'border border-dashed border-[var(--cxs-color-border)] bg-transparent opacity-50',
|
|
2407
|
+
'out-of-range': 'bg-[var(--cxs-color-surface-hover)] opacity-30',
|
|
2408
|
+
padding: 'invisible'
|
|
2409
|
+
};
|
|
2410
|
+
const INTERACTIVE_CLASSES = 'cursor-pointer hover:opacity-80';
|
|
2411
|
+
function parseIsoDate(value) {
|
|
2412
|
+
if (!ISO_DATE.test(value)) {
|
|
2413
|
+
return null;
|
|
2414
|
+
}
|
|
2415
|
+
const [year, month, day] = value.split('-').map(Number);
|
|
2416
|
+
const time = Date.UTC(year, month - 1, day);
|
|
2417
|
+
return formatIsoDate(time) === value ? time : null;
|
|
2418
|
+
}
|
|
2419
|
+
function formatIsoDate(time) {
|
|
2420
|
+
return new Date(time).toISOString().slice(0, 10);
|
|
2421
|
+
}
|
|
2422
|
+
function isoDayOf(time) {
|
|
2423
|
+
return (((new Date(time).getUTCDay() + 6) % 7) + 1);
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* GitHub-style grid of days for habit tracking. Columns are Monday–Sunday weeks; rows are days of the week.
|
|
2427
|
+
* Presentational only: it renders the dates it is given and emits `dayToggle`; the parent owns the data.
|
|
2428
|
+
*/
|
|
2429
|
+
class CxsHabitGridComponent {
|
|
2430
|
+
elementRef = inject(ElementRef);
|
|
2431
|
+
/** First date shown, as `yyyy-MM-dd`. */
|
|
2432
|
+
from = '';
|
|
2433
|
+
/** Last date shown, as `yyyy-MM-dd`. */
|
|
2434
|
+
to = '';
|
|
2435
|
+
/** The user's local today; later dates are out of range. */
|
|
2436
|
+
today = '';
|
|
2437
|
+
/** The habit's start date; earlier dates are out of range. */
|
|
2438
|
+
startDate = null;
|
|
2439
|
+
/** Dates marked done, as `yyyy-MM-dd`. */
|
|
2440
|
+
doneDates = [];
|
|
2441
|
+
/** Scheduled ISO days of week; `null` means every day is scheduled. */
|
|
2442
|
+
scheduledDays = null;
|
|
2443
|
+
/** Weekly target; when set, each week shows `done/target`. */
|
|
2444
|
+
weeklyTarget = null;
|
|
2445
|
+
/** Optional colour for done cells; defaults to `--cxs-color-primary`. */
|
|
2446
|
+
accentColor = null;
|
|
2447
|
+
/** Whether done and empty cells can be toggled. */
|
|
2448
|
+
interactive = false;
|
|
2449
|
+
/** Accessible name of the grid. */
|
|
2450
|
+
ariaLabel = 'Habit history';
|
|
2451
|
+
/** Locale for accessible date labels. */
|
|
2452
|
+
locale = 'en-GB';
|
|
2453
|
+
/** Emitted when the user toggles a `done` or `empty` cell in interactive mode. */
|
|
2454
|
+
dayToggle = new EventEmitter();
|
|
2455
|
+
dayRows = ISO_DAYS;
|
|
2456
|
+
weeks = [];
|
|
2457
|
+
focusedDate = null;
|
|
2458
|
+
rangeStart = null;
|
|
2459
|
+
rangeEnd = null;
|
|
2460
|
+
get hasWeeklyProgress() {
|
|
2461
|
+
return this.weeklyTarget != null && this.weeklyTarget > 0;
|
|
2462
|
+
}
|
|
2463
|
+
ngOnChanges() {
|
|
2464
|
+
this.weeks = this.buildWeeks();
|
|
2465
|
+
if (!this.focusedDate || !this.isWithinRange(this.focusedDate)) {
|
|
2466
|
+
this.focusedDate = this.initialFocusDate();
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
weekdayLabel(isoDay) {
|
|
2470
|
+
// 2024-01-01 is a Monday.
|
|
2471
|
+
return new Intl.DateTimeFormat(this.locale, { weekday: 'short', timeZone: 'UTC' }).format(new Date(Date.UTC(2024, 0, isoDay)));
|
|
2472
|
+
}
|
|
2473
|
+
dayAt(week, isoDay) {
|
|
2474
|
+
return week.days[isoDay - 1];
|
|
2475
|
+
}
|
|
2476
|
+
cellClass(day) {
|
|
2477
|
+
const classes = [CELL_BASE_CLASSES, CELL_STATE_CLASSES[day.state]];
|
|
2478
|
+
if (this.isToggleable(day)) {
|
|
2479
|
+
classes.push(INTERACTIVE_CLASSES);
|
|
2480
|
+
}
|
|
2481
|
+
return classes.join(' ');
|
|
2482
|
+
}
|
|
2483
|
+
tabIndexFor(day) {
|
|
2484
|
+
if (day.state === 'padding') {
|
|
2485
|
+
return null;
|
|
2486
|
+
}
|
|
2487
|
+
return day.date === this.focusedDate ? 0 : -1;
|
|
2488
|
+
}
|
|
2489
|
+
isToggleable(day) {
|
|
2490
|
+
return this.interactive && (day.state === 'done' || day.state === 'empty');
|
|
2491
|
+
}
|
|
2492
|
+
onCellClick(day) {
|
|
2493
|
+
if (day.state === 'padding') {
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
this.focusedDate = day.date;
|
|
2497
|
+
this.toggle(day);
|
|
2498
|
+
}
|
|
2499
|
+
onCellKeydown(event, day) {
|
|
2500
|
+
const moves = { ArrowUp: -1, ArrowDown: 1, ArrowLeft: -7, ArrowRight: 7 };
|
|
2501
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
2502
|
+
event.preventDefault();
|
|
2503
|
+
this.toggle(day);
|
|
2504
|
+
return;
|
|
2505
|
+
}
|
|
2506
|
+
const delta = moves[event.key];
|
|
2507
|
+
if (delta === undefined) {
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
event.preventDefault();
|
|
2511
|
+
const current = parseIsoDate(day.date);
|
|
2512
|
+
if (current === null || this.rangeStart === null || this.rangeEnd === null) {
|
|
2513
|
+
return;
|
|
2514
|
+
}
|
|
2515
|
+
const target = Math.min(Math.max(current + delta * DAY_MS, this.rangeStart), this.rangeEnd);
|
|
2516
|
+
this.focusedDate = formatIsoDate(target);
|
|
2517
|
+
queueMicrotask(() => this.focusCell(this.focusedDate));
|
|
2518
|
+
}
|
|
2519
|
+
toggle(day) {
|
|
2520
|
+
if (!this.isToggleable(day)) {
|
|
2521
|
+
return;
|
|
2522
|
+
}
|
|
2523
|
+
this.dayToggle.emit({ date: day.date, done: day.state === 'empty' });
|
|
2524
|
+
}
|
|
2525
|
+
focusCell(date) {
|
|
2526
|
+
if (!date) {
|
|
2527
|
+
return;
|
|
2528
|
+
}
|
|
2529
|
+
this.elementRef.nativeElement.querySelector(`[data-date="${date}"]`)?.focus();
|
|
2530
|
+
}
|
|
2531
|
+
isWithinRange(date) {
|
|
2532
|
+
const time = parseIsoDate(date);
|
|
2533
|
+
return time !== null && this.rangeStart !== null && this.rangeEnd !== null
|
|
2534
|
+
&& time >= this.rangeStart && time <= this.rangeEnd;
|
|
2535
|
+
}
|
|
2536
|
+
initialFocusDate() {
|
|
2537
|
+
if (this.rangeStart === null || this.rangeEnd === null) {
|
|
2538
|
+
return null;
|
|
2539
|
+
}
|
|
2540
|
+
const today = parseIsoDate(this.today);
|
|
2541
|
+
const target = today === null ? this.rangeEnd : Math.min(Math.max(today, this.rangeStart), this.rangeEnd);
|
|
2542
|
+
return formatIsoDate(target);
|
|
2543
|
+
}
|
|
2544
|
+
buildWeeks() {
|
|
2545
|
+
this.rangeStart = parseIsoDate(this.from);
|
|
2546
|
+
this.rangeEnd = parseIsoDate(this.to);
|
|
2547
|
+
if (this.rangeStart === null || this.rangeEnd === null || this.rangeStart > this.rangeEnd) {
|
|
2548
|
+
this.rangeStart = null;
|
|
2549
|
+
this.rangeEnd = null;
|
|
2550
|
+
return [];
|
|
2551
|
+
}
|
|
2552
|
+
const done = new Set(this.doneDates);
|
|
2553
|
+
const scheduled = this.scheduledDays ? new Set(this.scheduledDays) : null;
|
|
2554
|
+
const today = parseIsoDate(this.today);
|
|
2555
|
+
const start = this.startDate ? parseIsoDate(this.startDate) : null;
|
|
2556
|
+
const labelFormat = new Intl.DateTimeFormat(this.locale, {
|
|
2557
|
+
weekday: 'long',
|
|
2558
|
+
day: 'numeric',
|
|
2559
|
+
month: 'long',
|
|
2560
|
+
year: 'numeric',
|
|
2561
|
+
timeZone: 'UTC'
|
|
2562
|
+
});
|
|
2563
|
+
const weekFormat = new Intl.DateTimeFormat(this.locale, { day: 'numeric', month: 'short', timeZone: 'UTC' });
|
|
2564
|
+
const firstMonday = this.rangeStart - (isoDayOf(this.rangeStart) - 1) * DAY_MS;
|
|
2565
|
+
const weeks = [];
|
|
2566
|
+
for (let weekStart = firstMonday; weekStart <= this.rangeEnd; weekStart += 7 * DAY_MS) {
|
|
2567
|
+
const days = ISO_DAYS.map((isoDay) => {
|
|
2568
|
+
const time = weekStart + (isoDay - 1) * DAY_MS;
|
|
2569
|
+
const date = formatIsoDate(time);
|
|
2570
|
+
const state = this.stateOf(time, date, isoDay, done, scheduled, start, today);
|
|
2571
|
+
const label = state === 'padding' ? '' : `${labelFormat.format(time)}, ${STATE_TEXT[state]}`;
|
|
2572
|
+
return { date, isoDay, state, label };
|
|
2573
|
+
});
|
|
2574
|
+
const doneCount = days.filter((d) => d.state === 'done').length;
|
|
2575
|
+
const target = this.hasWeeklyProgress ? this.weeklyTarget : null;
|
|
2576
|
+
weeks.push({
|
|
2577
|
+
start: formatIsoDate(weekStart),
|
|
2578
|
+
days,
|
|
2579
|
+
doneCount,
|
|
2580
|
+
progressLabel: target === null ? null : `${doneCount}/${target}`,
|
|
2581
|
+
progressAriaLabel: target === null ? null : `Week of ${weekFormat.format(weekStart)}: ${doneCount} of ${target}`
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
return weeks;
|
|
2585
|
+
}
|
|
2586
|
+
stateOf(time, date, isoDay, done, scheduled, start, today) {
|
|
2587
|
+
if (time < this.rangeStart || time > this.rangeEnd) {
|
|
2588
|
+
return 'padding';
|
|
2589
|
+
}
|
|
2590
|
+
if ((start !== null && time < start) || (today !== null && time > today)) {
|
|
2591
|
+
return 'out-of-range';
|
|
2592
|
+
}
|
|
2593
|
+
if (scheduled && !scheduled.has(isoDay)) {
|
|
2594
|
+
return 'off';
|
|
2595
|
+
}
|
|
2596
|
+
return done.has(date) ? 'done' : 'empty';
|
|
2597
|
+
}
|
|
2598
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CxsHabitGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2599
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: CxsHabitGridComponent, isStandalone: true, selector: "cxs-habit-grid", inputs: { from: "from", to: "to", today: "today", startDate: "startDate", doneDates: "doneDates", scheduledDays: "scheduledDays", weeklyTarget: "weeklyTarget", accentColor: "accentColor", interactive: "interactive", ariaLabel: "ariaLabel", locale: "locale" }, outputs: { dayToggle: "dayToggle" }, host: { properties: { "style.--cxs-habit-grid-accent": "accentColor || null" }, classAttribute: "block" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"inline-flex max-w-full flex-col gap-1 overflow-x-auto\">\n <div\n role=\"grid\"\n class=\"flex flex-col gap-[var(--cxs-habit-grid-gap,3px)]\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-readonly]=\"interactive ? null : 'true'\"\n >\n @for (isoDay of dayRows; track isoDay) {\n <div role=\"row\" class=\"flex items-center gap-[var(--cxs-habit-grid-gap,3px)]\">\n <span\n role=\"rowheader\"\n class=\"w-8 shrink-0 text-[10px] leading-none text-[var(--cxs-color-on-surface-muted)]\"\n >\n {{ weekdayLabel(isoDay) }}\n </span>\n @for (week of weeks; track week.start) {\n @let day = dayAt(week, isoDay);\n @if (day.state === 'padding') {\n <span role=\"gridcell\" aria-hidden=\"true\" [class]=\"cellClass(day)\"></span>\n } @else {\n <span\n role=\"gridcell\"\n [class]=\"cellClass(day)\"\n [attr.data-date]=\"day.date\"\n [attr.data-state]=\"day.state\"\n [attr.aria-label]=\"day.label\"\n [attr.aria-selected]=\"day.state === 'done'\"\n [attr.aria-disabled]=\"interactive && !isToggleable(day) ? 'true' : null\"\n [attr.tabindex]=\"tabIndexFor(day)\"\n (click)=\"onCellClick(day)\"\n (keydown)=\"onCellKeydown($event, day)\"\n ></span>\n }\n }\n </div>\n }\n </div>\n\n @if (hasWeeklyProgress) {\n <div class=\"flex items-center gap-[var(--cxs-habit-grid-gap,3px)]\" data-testid=\"cxs-habit-grid-progress\">\n <span class=\"w-8 shrink-0\" aria-hidden=\"true\"></span>\n @for (week of weeks; track week.start) {\n <span\n class=\"w-[var(--cxs-habit-grid-cell-size,0.875rem)] shrink-0 overflow-visible whitespace-nowrap text-center text-[9px] leading-none text-[var(--cxs-color-on-surface-muted)]\"\n [class.font-semibold]=\"week.doneCount >= (weeklyTarget ?? 0)\"\n [attr.aria-label]=\"week.progressAriaLabel\"\n [attr.title]=\"week.progressAriaLabel\"\n >\n {{ week.progressLabel }}\n </span>\n }\n </div>\n }\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2600
|
+
}
|
|
2601
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CxsHabitGridComponent, decorators: [{
|
|
2602
|
+
type: Component,
|
|
2603
|
+
args: [{ selector: 'cxs-habit-grid', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
2604
|
+
class: 'block',
|
|
2605
|
+
'[style.--cxs-habit-grid-accent]': 'accentColor || null'
|
|
2606
|
+
}, template: "<div class=\"inline-flex max-w-full flex-col gap-1 overflow-x-auto\">\n <div\n role=\"grid\"\n class=\"flex flex-col gap-[var(--cxs-habit-grid-gap,3px)]\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-readonly]=\"interactive ? null : 'true'\"\n >\n @for (isoDay of dayRows; track isoDay) {\n <div role=\"row\" class=\"flex items-center gap-[var(--cxs-habit-grid-gap,3px)]\">\n <span\n role=\"rowheader\"\n class=\"w-8 shrink-0 text-[10px] leading-none text-[var(--cxs-color-on-surface-muted)]\"\n >\n {{ weekdayLabel(isoDay) }}\n </span>\n @for (week of weeks; track week.start) {\n @let day = dayAt(week, isoDay);\n @if (day.state === 'padding') {\n <span role=\"gridcell\" aria-hidden=\"true\" [class]=\"cellClass(day)\"></span>\n } @else {\n <span\n role=\"gridcell\"\n [class]=\"cellClass(day)\"\n [attr.data-date]=\"day.date\"\n [attr.data-state]=\"day.state\"\n [attr.aria-label]=\"day.label\"\n [attr.aria-selected]=\"day.state === 'done'\"\n [attr.aria-disabled]=\"interactive && !isToggleable(day) ? 'true' : null\"\n [attr.tabindex]=\"tabIndexFor(day)\"\n (click)=\"onCellClick(day)\"\n (keydown)=\"onCellKeydown($event, day)\"\n ></span>\n }\n }\n </div>\n }\n </div>\n\n @if (hasWeeklyProgress) {\n <div class=\"flex items-center gap-[var(--cxs-habit-grid-gap,3px)]\" data-testid=\"cxs-habit-grid-progress\">\n <span class=\"w-8 shrink-0\" aria-hidden=\"true\"></span>\n @for (week of weeks; track week.start) {\n <span\n class=\"w-[var(--cxs-habit-grid-cell-size,0.875rem)] shrink-0 overflow-visible whitespace-nowrap text-center text-[9px] leading-none text-[var(--cxs-color-on-surface-muted)]\"\n [class.font-semibold]=\"week.doneCount >= (weeklyTarget ?? 0)\"\n [attr.aria-label]=\"week.progressAriaLabel\"\n [attr.title]=\"week.progressAriaLabel\"\n >\n {{ week.progressLabel }}\n </span>\n }\n </div>\n }\n</div>\n" }]
|
|
2607
|
+
}], propDecorators: { from: [{
|
|
2608
|
+
type: Input,
|
|
2609
|
+
args: [{ required: true }]
|
|
2610
|
+
}], to: [{
|
|
2611
|
+
type: Input,
|
|
2612
|
+
args: [{ required: true }]
|
|
2613
|
+
}], today: [{
|
|
2614
|
+
type: Input,
|
|
2615
|
+
args: [{ required: true }]
|
|
2616
|
+
}], startDate: [{
|
|
2617
|
+
type: Input
|
|
2618
|
+
}], doneDates: [{
|
|
2619
|
+
type: Input
|
|
2620
|
+
}], scheduledDays: [{
|
|
2621
|
+
type: Input
|
|
2622
|
+
}], weeklyTarget: [{
|
|
2623
|
+
type: Input
|
|
2624
|
+
}], accentColor: [{
|
|
2625
|
+
type: Input
|
|
2626
|
+
}], interactive: [{
|
|
2627
|
+
type: Input
|
|
2628
|
+
}], ariaLabel: [{
|
|
2629
|
+
type: Input
|
|
2630
|
+
}], locale: [{
|
|
2631
|
+
type: Input
|
|
2632
|
+
}], dayToggle: [{
|
|
2633
|
+
type: Output
|
|
2634
|
+
}] } });
|
|
2635
|
+
|
|
2383
2636
|
const BASE_CLASSES$3 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
|
|
2384
2637
|
'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
|
|
2385
2638
|
'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
|
|
@@ -4620,5 +4873,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
|
|
|
4620
4873
|
* Generated bundle index. Do not edit.
|
|
4621
4874
|
*/
|
|
4622
4875
|
|
|
4623
|
-
export { CXS_USER_PREFERENCE_CHANGED_EVENT, CXS_USER_SESSION_STORAGE_KEY, CerxosUi, CxsAccordionComponent, CxsAccordionItemComponent, CxsActionMenuComponent, CxsAlertComponent, CxsAvatarComponent, CxsBadgeComponent, CxsBreadcrumbComponent, CxsButtonComponent, CxsCardComponent, CxsCarouselComponent, CxsCheckboxComponent, CxsCurrencyPipe, CxsDataTableCellDirective, CxsDataTableComponent, CxsDatePipe, CxsDatepickerComponent, CxsDialogComponent, CxsIconComponent, CxsInputComponent, CxsMenubarComponent, CxsMultiSelectComponent, CxsPreferenceFormattingService, CxsProgressBarComponent, CxsRadioComponent, CxsSelectComponent, CxsSkeletonComponent, CxsSliderComponent, CxsTabLabelDirective, CxsTabPanelComponent, CxsTableComponent, CxsTabsComponent, CxsThemeDirective, CxsToastComponent, CxsToggleComponent, CxsTooltipComponent };
|
|
4876
|
+
export { CXS_USER_PREFERENCE_CHANGED_EVENT, CXS_USER_SESSION_STORAGE_KEY, CerxosUi, CxsAccordionComponent, CxsAccordionItemComponent, CxsActionMenuComponent, CxsAlertComponent, CxsAvatarComponent, CxsBadgeComponent, CxsBreadcrumbComponent, CxsButtonComponent, CxsCardComponent, CxsCarouselComponent, CxsCheckboxComponent, CxsCurrencyPipe, CxsDataTableCellDirective, CxsDataTableComponent, CxsDatePipe, CxsDatepickerComponent, CxsDialogComponent, CxsHabitGridComponent, CxsIconComponent, CxsInputComponent, CxsMenubarComponent, CxsMultiSelectComponent, CxsPreferenceFormattingService, CxsProgressBarComponent, CxsRadioComponent, CxsSelectComponent, CxsSkeletonComponent, CxsSliderComponent, CxsTabLabelDirective, CxsTabPanelComponent, CxsTableComponent, CxsTabsComponent, CxsThemeDirective, CxsToastComponent, CxsToggleComponent, CxsTooltipComponent };
|
|
4624
4877
|
//# sourceMappingURL=cerxos-ui.mjs.map
|