@f-ewald/components 0.7.0 → 1.0.2
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/README.md +76 -47
- package/custom-elements.json +3049 -910
- package/dist/address-autocomplete.d.ts +8 -0
- package/dist/address-autocomplete.d.ts.map +1 -1
- package/dist/address-autocomplete.js +165 -15
- package/dist/address-autocomplete.js.map +1 -1
- package/dist/animate-confetti.d.ts +8 -5
- package/dist/animate-confetti.d.ts.map +1 -1
- package/dist/animate-confetti.js +46 -19
- package/dist/animate-confetti.js.map +1 -1
- package/dist/autocomplete-input.d.ts +8 -0
- package/dist/autocomplete-input.d.ts.map +1 -1
- package/dist/autocomplete-input.js +165 -15
- package/dist/autocomplete-input.js.map +1 -1
- package/dist/calendar-entry.d.ts +33 -0
- package/dist/calendar-entry.d.ts.map +1 -0
- package/dist/calendar-entry.js +64 -0
- package/dist/calendar-entry.js.map +1 -0
- package/dist/calendar-month.d.ts +67 -0
- package/dist/calendar-month.d.ts.map +1 -0
- package/dist/calendar-month.js +469 -0
- package/dist/calendar-month.js.map +1 -0
- package/dist/calendar-year.d.ts +43 -0
- package/dist/calendar-year.d.ts.map +1 -0
- package/dist/calendar-year.js +145 -0
- package/dist/calendar-year.js.map +1 -0
- package/dist/chat-message.d.ts.map +1 -1
- package/dist/chat-message.js +77 -24
- package/dist/chat-message.js.map +1 -1
- package/dist/confirm-dialog.d.ts +8 -1
- package/dist/confirm-dialog.d.ts.map +1 -1
- package/dist/confirm-dialog.js +130 -18
- package/dist/confirm-dialog.js.map +1 -1
- package/dist/copy-link-button.d.ts +2 -0
- package/dist/copy-link-button.d.ts.map +1 -1
- package/dist/copy-link-button.js +35 -3
- package/dist/copy-link-button.js.map +1 -1
- package/dist/data-table.d.ts +2 -0
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +83 -11
- package/dist/data-table.js.map +1 -1
- package/dist/distance-value.d.ts +0 -1
- package/dist/distance-value.d.ts.map +1 -1
- package/dist/distance-value.js +3 -8
- package/dist/distance-value.js.map +1 -1
- package/dist/distribution-chart.d.ts.map +1 -1
- package/dist/distribution-chart.js +54 -17
- package/dist/distribution-chart.js.map +1 -1
- package/dist/dropdown-button.d.ts.map +1 -1
- package/dist/dropdown-button.js +60 -11
- package/dist/dropdown-button.js.map +1 -1
- package/dist/editable-text.d.ts.map +1 -1
- package/dist/editable-text.js +46 -13
- package/dist/editable-text.js.map +1 -1
- package/dist/form-select.d.ts +21 -2
- package/dist/form-select.d.ts.map +1 -1
- package/dist/form-select.js +489 -37
- package/dist/form-select.js.map +1 -1
- package/dist/frame-box.d.ts.map +1 -1
- package/dist/frame-box.js +20 -12
- package/dist/frame-box.js.map +1 -1
- package/dist/icon-button.d.ts.map +1 -1
- package/dist/icon-button.js +11 -0
- package/dist/icon-button.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/kbd-hint.d.ts +30 -0
- package/dist/kbd-hint.d.ts.map +1 -0
- package/dist/kbd-hint.js +184 -0
- package/dist/kbd-hint.js.map +1 -0
- package/dist/live-timer.d.ts +0 -1
- package/dist/live-timer.d.ts.map +1 -1
- package/dist/live-timer.js +1 -6
- package/dist/live-timer.js.map +1 -1
- package/dist/map-circle.d.ts +3 -0
- package/dist/map-circle.d.ts.map +1 -1
- package/dist/map-circle.js +19 -1
- package/dist/map-circle.js.map +1 -1
- package/dist/map-pin.d.ts.map +1 -1
- package/dist/map-pin.js +16 -1
- package/dist/map-pin.js.map +1 -1
- package/dist/percent-bar-chart.d.ts +3 -0
- package/dist/percent-bar-chart.d.ts.map +1 -1
- package/dist/percent-bar-chart.js +60 -4
- package/dist/percent-bar-chart.js.map +1 -1
- package/dist/photo-gallery.d.ts.map +1 -1
- package/dist/photo-gallery.js +12 -3
- package/dist/photo-gallery.js.map +1 -1
- package/dist/popover-panel.d.ts.map +1 -1
- package/dist/popover-panel.js +132 -23
- package/dist/popover-panel.js.map +1 -1
- package/dist/price-history-chart.d.ts.map +1 -1
- package/dist/price-history-chart.js +52 -18
- package/dist/price-history-chart.js.map +1 -1
- package/dist/radio-cards.d.ts +2 -0
- package/dist/radio-cards.d.ts.map +1 -1
- package/dist/radio-cards.js +51 -5
- package/dist/radio-cards.js.map +1 -1
- package/dist/radio-pills.d.ts +2 -0
- package/dist/radio-pills.d.ts.map +1 -1
- package/dist/radio-pills.js +46 -4
- package/dist/radio-pills.js.map +1 -1
- package/dist/relative-time.d.ts +0 -1
- package/dist/relative-time.d.ts.map +1 -1
- package/dist/relative-time.js +1 -6
- package/dist/relative-time.js.map +1 -1
- package/dist/reveal-button.d.ts +2 -0
- package/dist/reveal-button.d.ts.map +1 -1
- package/dist/reveal-button.js +42 -5
- package/dist/reveal-button.js.map +1 -1
- package/dist/roman-numeral.js +1 -1
- package/dist/roman-numeral.js.map +1 -1
- package/dist/slide-panel.d.ts +7 -3
- package/dist/slide-panel.d.ts.map +1 -1
- package/dist/slide-panel.js +95 -23
- package/dist/slide-panel.js.map +1 -1
- package/dist/stat-meter.d.ts.map +1 -1
- package/dist/stat-meter.js +17 -7
- package/dist/stat-meter.js.map +1 -1
- package/dist/status-pill.d.ts.map +1 -1
- package/dist/status-pill.js +26 -12
- package/dist/status-pill.js.map +1 -1
- package/dist/tile-grid.d.ts +8 -0
- package/dist/tile-grid.d.ts.map +1 -1
- package/dist/tile-grid.js +88 -12
- package/dist/tile-grid.js.map +1 -1
- package/dist/toast-notification.d.ts.map +1 -1
- package/dist/toast-notification.js +40 -11
- package/dist/toast-notification.js.map +1 -1
- package/dist/tokens.css +23 -0
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +16 -0
- package/dist/tokens.js.map +1 -1
- package/dist/ui-button.d.ts +2 -0
- package/dist/ui-button.d.ts.map +1 -1
- package/dist/ui-button.js +58 -9
- package/dist/ui-button.js.map +1 -1
- package/dist/user-avatar.d.ts.map +1 -1
- package/dist/user-avatar.js +18 -3
- package/dist/user-avatar.js.map +1 -1
- package/dist/utils/calendar.d.ts +77 -0
- package/dist/utils/calendar.d.ts.map +1 -0
- package/dist/utils/calendar.js +135 -0
- package/dist/utils/calendar.js.map +1 -0
- package/dist/utils/form.d.ts +3 -0
- package/dist/utils/form.d.ts.map +1 -0
- package/dist/utils/form.js +14 -0
- package/dist/utils/form.js.map +1 -0
- package/dist/utils/layer-stack.d.ts +9 -0
- package/dist/utils/layer-stack.d.ts.map +1 -0
- package/dist/utils/layer-stack.js +35 -0
- package/dist/utils/layer-stack.js.map +1 -0
- package/dist/weight-bar-chart.d.ts.map +1 -1
- package/dist/weight-bar-chart.js +27 -8
- package/dist/weight-bar-chart.js.map +1 -1
- package/docs/address-autocomplete.md +5 -0
- package/docs/autocomplete-input.md +5 -0
- package/docs/calendar-entry.md +53 -0
- package/docs/calendar-month.md +79 -0
- package/docs/calendar-year.md +53 -0
- package/docs/chat-message.md +4 -0
- package/docs/confirm-dialog.md +2 -0
- package/docs/copy-link-button.md +2 -0
- package/docs/data-table.md +2 -0
- package/docs/design-language.md +149 -0
- package/docs/distribution-chart.md +4 -0
- package/docs/dropdown-button.md +1 -0
- package/docs/form-select.md +15 -3
- package/docs/frame-box.md +2 -0
- package/docs/kbd-hint.md +42 -0
- package/docs/map-circle.md +4 -0
- package/docs/percent-bar-chart.md +4 -1
- package/docs/photo-gallery.md +1 -0
- package/docs/popover-panel.md +2 -0
- package/docs/price-history-chart.md +8 -1
- package/docs/radio-cards.md +2 -0
- package/docs/radio-pills.md +2 -0
- package/docs/reveal-button.md +3 -0
- package/docs/slide-panel.md +4 -2
- package/docs/status-pill.md +3 -0
- package/docs/tile-grid.md +10 -1
- package/docs/toast-notification.md +4 -1
- package/docs/ui-button.md +2 -0
- package/docs/user-avatar.md +1 -0
- package/llms.txt +175 -61
- package/package.json +14 -2
- package/dist/map-point.d.ts +0 -31
- package/dist/map-point.d.ts.map +0 -1
- package/dist/map-point.js +0 -83
- package/dist/map-point.js.map +0 -1
- package/docs/map-point.md +0 -44
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-entry.js","sourceRoot":"","sources":["../src/calendar-entry.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE5D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;;GASG;AAEI,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,UAAU;IAAtC;;QACL,4CAA4C;QACf,UAAK,GAAG,EAAE,CAAC;QAExC,+FAA+F;QAClE,QAAG,GAAG,EAAE,CAAC;QAEtC,4DAA4D;QAC/B,UAAK,GAAG,EAAE,CAAC;QAExC,sDAAsD;QACzB,UAAK,GAAoB,SAAS,CAAC;IAkBlE,CAAC;aAbiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;KAIF;KACF,AAPqB,CAOpB;IAEF,6FAA6F;IAC1E,MAAM;QACvB,OAAO,IAAI,CAAA,kFAAkF,CAAC;IAChG,CAAC;CACF,CAAA;AA3B8B;IAA5B,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;4CAAY;AAGX;IAA5B,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;0CAAU;AAGT;IAA5B,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;4CAAY;AAGX;IAA5B,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;4CAAoC;AAGnC;IAA5B,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;2CAAe;AAdhC,aAAa;IADzB,aAAa,CAAC,gBAAgB,CAAC;GACnB,aAAa,CA6BzB","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport type { StatusPillColor } from \"./status-pill.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * Declarative metadata for one calendar event, consumed by a parent\n * `calendar-month` or `calendar-year`. Read-only/non-interactive; renders\n * nothing itself.\n *\n * @element calendar-entry\n * @slot title - Plain-text title shown instead of the `label` fallback.\n * @slot detail - Repeatable plain-text details rendered inside the shared body spanning all remaining days.\n * @slot footer - Plain-text ending note pinned to the bottom of the shared body.\n */\n@customElement(\"calendar-entry\")\nexport class CalendarEntry extends LitElement {\n /** Inclusive start date, `\"YYYY-MM-DD\"`. */\n @property({ reflect: true }) start = \"\";\n\n /** Inclusive end date, `\"YYYY-MM-DD\"`. Falls back to `start` when unset (single-day entry). */\n @property({ reflect: true }) end = \"\";\n\n /** Fallback title used when no `title` slot is provided. */\n @property({ reflect: true }) label = \"\";\n\n /** Color variant, reusing `status-pill`'s palette. */\n @property({ reflect: true }) color: StatusPillColor = \"neutral\";\n\n /** Optional link target; the parent renders the entry as an `<a>` when set. */\n @property({ reflect: true }) href?: string;\n\n static override styles = [\n tokens,\n css`\n :host {\n display: none;\n }\n `,\n ];\n\n /** Keeps the metadata host hidden while exposing named text slots to the parent calendar. */\n protected override render() {\n return html`<slot name=\"title\"></slot><slot name=\"detail\"></slot><slot name=\"footer\"></slot>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"calendar-entry\": CalendarEntry;\n }\n}\n"]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
/**
|
|
3
|
+
* One month rendered as a top-to-bottom list of days — weekends and today
|
|
4
|
+
* highlighted, with declarative `calendar-entry` children shown as colored
|
|
5
|
+
* bars spanning the days they cover. An entry's title uses its first visible
|
|
6
|
+
* day; every remaining visible day becomes one shared body for wrapped
|
|
7
|
+
* details and an optional ending footer. Overlapping entries stack into
|
|
8
|
+
* side-by-side lanes rather than being layered/hidden. Read-only.
|
|
9
|
+
*
|
|
10
|
+
* Lanes are computed independently per instance, from only the entries
|
|
11
|
+
* overlapping this month — an entry spanning a month boundary may therefore
|
|
12
|
+
* land in a different lane index in the adjacent month's `calendar-month`.
|
|
13
|
+
* This is an accepted v1 limitation: cross-month lane continuity would
|
|
14
|
+
* require a shared parent (`calendar-year`) to assign lanes globally.
|
|
15
|
+
*
|
|
16
|
+
* Entry attributes and slotted title/detail text are observed, so a
|
|
17
|
+
* standalone month re-renders when consumers update declarative metadata.
|
|
18
|
+
*
|
|
19
|
+
* @element calendar-month
|
|
20
|
+
* @slot - Declarative `calendar-entry` elements to render for this month.
|
|
21
|
+
*/
|
|
22
|
+
export declare class CalendarMonth extends LitElement {
|
|
23
|
+
/** Calendar year, e.g. `2026`. */
|
|
24
|
+
year: number;
|
|
25
|
+
/** Calendar month, 1-12 (January = 1). */
|
|
26
|
+
month: number;
|
|
27
|
+
private readonly _entryElements;
|
|
28
|
+
/** Bumped when entry elements, attributes, or slotted text change to force a re-render. */
|
|
29
|
+
private _entriesVersion;
|
|
30
|
+
private _entriesObserver?;
|
|
31
|
+
static styles: import("lit").CSSResult[];
|
|
32
|
+
/** Observes declarative entry attributes and slotted text for live updates. */
|
|
33
|
+
connectedCallback(): void;
|
|
34
|
+
/** Releases the entry observer when this month disconnects. */
|
|
35
|
+
disconnectedCallback(): void;
|
|
36
|
+
/** Restores hover/focus classes from the live DOM after Lit updates or reuses cells. */
|
|
37
|
+
protected updated(): void;
|
|
38
|
+
/** Re-renders when declarative `calendar-entry` children are added or removed. */
|
|
39
|
+
private _handleSlotChange;
|
|
40
|
+
/** Finds the laned entry (if any) covering `date` in a given lane. */
|
|
41
|
+
private _entryFor;
|
|
42
|
+
/** Classifies one day of an entry so only the outer endpoints are rounded. */
|
|
43
|
+
private _segmentClass;
|
|
44
|
+
/** Current render key shared by an entry's title and body cells. */
|
|
45
|
+
private _entryKey;
|
|
46
|
+
/** Toggles one interaction class on every rendered cell belonging to an entry. */
|
|
47
|
+
private _setEntryInteraction;
|
|
48
|
+
/** Reconciles classes against the links actually hovered or focused after a render. */
|
|
49
|
+
private _syncEntryInteractions;
|
|
50
|
+
/** Renders a transparent full-cell link without wrapping the visible text. */
|
|
51
|
+
private _renderEntryLink;
|
|
52
|
+
/** Joins the event title, details, and visible footer for tooltips and accessible link names. */
|
|
53
|
+
private _entryBodyText;
|
|
54
|
+
/** Renders the shared details body, with the footer reserved at its bottom edge. */
|
|
55
|
+
private _renderEntryBody;
|
|
56
|
+
/** Renders a row-spanning body cell with whole-line detail and footer budgets. */
|
|
57
|
+
private _renderEntryBodyCell;
|
|
58
|
+
/** Renders a lane's title/body cell, skips rows covered by a body rowspan, or emits an empty spacer. */
|
|
59
|
+
private _renderLaneCell;
|
|
60
|
+
protected render(): import("lit-html").TemplateResult<1>;
|
|
61
|
+
}
|
|
62
|
+
declare global {
|
|
63
|
+
interface HTMLElementTagNameMap {
|
|
64
|
+
"calendar-month": CalendarMonth;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=calendar-month.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-month.d.ts","sourceRoot":"","sources":["../src/calendar-month.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAoBrD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC3C,kCAAkC;IACS,IAAI,EAAE,MAAM,CAA4B;IAEnF,0CAA0C;IACC,KAAK,SAAK;IAGrD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmB;IAElD,2FAA2F;IAClF,OAAO,CAAC,eAAe,CAAK;IAErC,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAE5C,OAAgB,MAAM,4BA6MpB;IAEF,+EAA+E;IACtE,iBAAiB,IAAI,IAAI,CAWjC;IAED,+DAA+D;IACtD,oBAAoB,IAAI,IAAI,CAGpC;IAED,wFAAwF;IACxF,UAAmB,OAAO,IAAI,IAAI,CAEjC;IAED,kFAAkF;IAClF,OAAO,CAAC,iBAAiB;IAIzB,sEAAsE;IACtE,OAAO,CAAC,SAAS;IAIjB,8EAA8E;IAC9E,OAAO,CAAC,aAAa;IASrB,oEAAoE;IACpE,OAAO,CAAC,SAAS;IAIjB,kFAAkF;IAClF,OAAO,CAAC,oBAAoB;IAQ5B,uFAAuF;IACvF,OAAO,CAAC,sBAAsB;IAa9B,8EAA8E;IAC9E,OAAO,CAAC,gBAAgB;IAgBxB,iGAAiG;IACjG,OAAO,CAAC,cAAc;IAMtB,oFAAoF;IACpF,OAAO,CAAC,gBAAgB;IAiCxB,kFAAkF;IAClF,OAAO,CAAC,oBAAoB;IAsB5B,wGAAwG;IACxG,OAAO,CAAC,eAAe;IA8CvB,UAAmB,MAAM,yCAyCxB;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,gBAAgB,EAAE,aAAa,CAAC;KACjC;CACF"}
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { LitElement, css, html, nothing } from "lit";
|
|
8
|
+
import { customElement, property, queryAssignedElements, state } from "lit/decorators.js";
|
|
9
|
+
import { repeat } from "lit/directives/repeat.js";
|
|
10
|
+
import { tokens } from "./tokens.js";
|
|
11
|
+
import { CALENDAR_ENTRY_ATTRIBUTES, WEEKDAY_ABBR, assignLanes, daysInMonth, isWeekend, monthName, overlapsRange, readCalendarEntryElement, resolveEntry, toIsoDate, } from "./utils/calendar.js";
|
|
12
|
+
/**
|
|
13
|
+
* One month rendered as a top-to-bottom list of days — weekends and today
|
|
14
|
+
* highlighted, with declarative `calendar-entry` children shown as colored
|
|
15
|
+
* bars spanning the days they cover. An entry's title uses its first visible
|
|
16
|
+
* day; every remaining visible day becomes one shared body for wrapped
|
|
17
|
+
* details and an optional ending footer. Overlapping entries stack into
|
|
18
|
+
* side-by-side lanes rather than being layered/hidden. Read-only.
|
|
19
|
+
*
|
|
20
|
+
* Lanes are computed independently per instance, from only the entries
|
|
21
|
+
* overlapping this month — an entry spanning a month boundary may therefore
|
|
22
|
+
* land in a different lane index in the adjacent month's `calendar-month`.
|
|
23
|
+
* This is an accepted v1 limitation: cross-month lane continuity would
|
|
24
|
+
* require a shared parent (`calendar-year`) to assign lanes globally.
|
|
25
|
+
*
|
|
26
|
+
* Entry attributes and slotted title/detail text are observed, so a
|
|
27
|
+
* standalone month re-renders when consumers update declarative metadata.
|
|
28
|
+
*
|
|
29
|
+
* @element calendar-month
|
|
30
|
+
* @slot - Declarative `calendar-entry` elements to render for this month.
|
|
31
|
+
*/
|
|
32
|
+
let CalendarMonth = class CalendarMonth extends LitElement {
|
|
33
|
+
constructor() {
|
|
34
|
+
super(...arguments);
|
|
35
|
+
/** Calendar year, e.g. `2026`. */
|
|
36
|
+
this.year = new Date().getFullYear();
|
|
37
|
+
/** Calendar month, 1-12 (January = 1). */
|
|
38
|
+
this.month = 1;
|
|
39
|
+
/** Bumped when entry elements, attributes, or slotted text change to force a re-render. */
|
|
40
|
+
this._entriesVersion = 0;
|
|
41
|
+
}
|
|
42
|
+
static { this.styles = [
|
|
43
|
+
tokens,
|
|
44
|
+
css `
|
|
45
|
+
:host {
|
|
46
|
+
display: block;
|
|
47
|
+
font-family: var(
|
|
48
|
+
--ui-font,
|
|
49
|
+
ui-sans-serif,
|
|
50
|
+
system-ui,
|
|
51
|
+
sans-serif,
|
|
52
|
+
"Apple Color Emoji",
|
|
53
|
+
"Segoe UI Emoji",
|
|
54
|
+
"Segoe UI Symbol",
|
|
55
|
+
"Noto Color Emoji"
|
|
56
|
+
);
|
|
57
|
+
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
58
|
+
}
|
|
59
|
+
.month-name {
|
|
60
|
+
margin: 0 0 0.5rem;
|
|
61
|
+
color: var(--ui-text, #0f172a);
|
|
62
|
+
font-size: var(--ui-font-size, 0.875rem);
|
|
63
|
+
font-weight: 600;
|
|
64
|
+
}
|
|
65
|
+
table {
|
|
66
|
+
width: 100%;
|
|
67
|
+
border-collapse: collapse;
|
|
68
|
+
table-layout: fixed;
|
|
69
|
+
}
|
|
70
|
+
.day-row td {
|
|
71
|
+
padding: 0.25rem 0.5rem;
|
|
72
|
+
border-bottom: 1px solid var(--ui-border, #e2e8f0);
|
|
73
|
+
}
|
|
74
|
+
.day-row.weekend {
|
|
75
|
+
background: var(--ui-surface-muted, #f8fafc);
|
|
76
|
+
}
|
|
77
|
+
.day-row.today {
|
|
78
|
+
background: color-mix(in srgb, var(--ui-primary, #4f46e5) 10%, transparent);
|
|
79
|
+
}
|
|
80
|
+
.day-number {
|
|
81
|
+
width: 1.5rem;
|
|
82
|
+
color: var(--ui-text-muted, #64748b);
|
|
83
|
+
font-variant-numeric: tabular-nums;
|
|
84
|
+
text-align: right;
|
|
85
|
+
white-space: nowrap;
|
|
86
|
+
}
|
|
87
|
+
.day-row.today .day-number {
|
|
88
|
+
color: var(--ui-primary, #4f46e5);
|
|
89
|
+
font-weight: 700;
|
|
90
|
+
}
|
|
91
|
+
.day-weekday {
|
|
92
|
+
width: 2.25rem;
|
|
93
|
+
color: var(--ui-text-muted, #64748b);
|
|
94
|
+
white-space: nowrap;
|
|
95
|
+
}
|
|
96
|
+
.day-row.today .day-weekday {
|
|
97
|
+
color: var(--ui-primary, #4f46e5);
|
|
98
|
+
font-weight: 600;
|
|
99
|
+
}
|
|
100
|
+
.day-row.weekend .day-weekday {
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
}
|
|
103
|
+
.lane-cell {
|
|
104
|
+
padding: 0.25rem 0.5rem;
|
|
105
|
+
}
|
|
106
|
+
.entry-bar {
|
|
107
|
+
border-radius: 0;
|
|
108
|
+
overflow: hidden;
|
|
109
|
+
text-overflow: ellipsis;
|
|
110
|
+
transition: box-shadow 120ms ease;
|
|
111
|
+
white-space: nowrap;
|
|
112
|
+
}
|
|
113
|
+
.entry-bar.entry-hovered,
|
|
114
|
+
.entry-bar.entry-focused {
|
|
115
|
+
box-shadow: inset 0 0 0 100vmax var(--ui-hover-overlay, rgb(255 255 255 / 0.32));
|
|
116
|
+
}
|
|
117
|
+
.entry-bar.segment-single {
|
|
118
|
+
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
119
|
+
}
|
|
120
|
+
.entry-bar.segment-start {
|
|
121
|
+
border-radius: var(--ui-radius-sm, 0.25rem) var(--ui-radius-sm, 0.25rem) 0 0;
|
|
122
|
+
}
|
|
123
|
+
.entry-bar.segment-end {
|
|
124
|
+
border-radius: 0 0 var(--ui-radius-sm, 0.25rem) var(--ui-radius-sm, 0.25rem);
|
|
125
|
+
}
|
|
126
|
+
.entry-bar.segment-start,
|
|
127
|
+
.entry-bar.segment-middle {
|
|
128
|
+
border-bottom: 0;
|
|
129
|
+
}
|
|
130
|
+
.entry-bar.neutral {
|
|
131
|
+
background: color-mix(in srgb, var(--ui-text-muted, #64748b) 15%, var(--ui-surface, #ffffff));
|
|
132
|
+
color: var(--ui-text-muted, #64748b);
|
|
133
|
+
}
|
|
134
|
+
.entry-bar.info {
|
|
135
|
+
background: color-mix(in srgb, var(--ui-info, #0ea5e9) 15%, var(--ui-surface, #ffffff));
|
|
136
|
+
color: var(--ui-info, #0ea5e9);
|
|
137
|
+
}
|
|
138
|
+
.entry-bar.primary {
|
|
139
|
+
background: color-mix(in srgb, var(--ui-primary, #4f46e5) 15%, var(--ui-surface, #ffffff));
|
|
140
|
+
color: var(--ui-primary, #4f46e5);
|
|
141
|
+
}
|
|
142
|
+
.entry-bar.success {
|
|
143
|
+
background: color-mix(in srgb, var(--ui-success, #16a34a) 15%, var(--ui-surface, #ffffff));
|
|
144
|
+
color: var(--ui-success, #16a34a);
|
|
145
|
+
}
|
|
146
|
+
.entry-bar.warning {
|
|
147
|
+
background: color-mix(in srgb, var(--ui-warning, #d97706) 15%, var(--ui-surface, #ffffff));
|
|
148
|
+
color: var(--ui-warning, #d97706);
|
|
149
|
+
}
|
|
150
|
+
.entry-bar.danger {
|
|
151
|
+
background: color-mix(in srgb, var(--ui-danger, #dc2626) 15%, var(--ui-surface, #ffffff));
|
|
152
|
+
color: var(--ui-danger, #dc2626);
|
|
153
|
+
}
|
|
154
|
+
.entry-line {
|
|
155
|
+
display: block;
|
|
156
|
+
overflow: hidden;
|
|
157
|
+
}
|
|
158
|
+
.day-row td.entry-title-cell {
|
|
159
|
+
position: relative;
|
|
160
|
+
padding: 0;
|
|
161
|
+
}
|
|
162
|
+
.entry-title {
|
|
163
|
+
box-sizing: border-box;
|
|
164
|
+
font-weight: 500;
|
|
165
|
+
padding: 0.25rem 0.5rem;
|
|
166
|
+
pointer-events: none;
|
|
167
|
+
white-space: nowrap;
|
|
168
|
+
text-overflow: ellipsis;
|
|
169
|
+
}
|
|
170
|
+
.entry-details {
|
|
171
|
+
display: -webkit-box;
|
|
172
|
+
min-height: 0;
|
|
173
|
+
max-height: var(--entry-detail-max-height, 1.15em);
|
|
174
|
+
flex: 0 0 auto;
|
|
175
|
+
overflow: hidden;
|
|
176
|
+
-webkit-box-orient: vertical;
|
|
177
|
+
-webkit-line-clamp: var(--entry-detail-lines, 1);
|
|
178
|
+
font-size: var(--ui-font-size-xs, 0.6875rem);
|
|
179
|
+
font-weight: 400;
|
|
180
|
+
line-height: 1.15;
|
|
181
|
+
overflow-wrap: anywhere;
|
|
182
|
+
text-overflow: ellipsis;
|
|
183
|
+
white-space: normal;
|
|
184
|
+
}
|
|
185
|
+
.entry-detail {
|
|
186
|
+
min-height: 1.15em;
|
|
187
|
+
}
|
|
188
|
+
.day-row td.entry-body-cell {
|
|
189
|
+
position: relative;
|
|
190
|
+
padding: 0;
|
|
191
|
+
vertical-align: top;
|
|
192
|
+
}
|
|
193
|
+
.entry-body {
|
|
194
|
+
position: absolute;
|
|
195
|
+
inset: 0;
|
|
196
|
+
box-sizing: border-box;
|
|
197
|
+
display: flex;
|
|
198
|
+
min-height: 0;
|
|
199
|
+
flex-direction: column;
|
|
200
|
+
overflow: hidden;
|
|
201
|
+
padding: 0.25rem 0.5rem;
|
|
202
|
+
color: inherit;
|
|
203
|
+
pointer-events: none;
|
|
204
|
+
}
|
|
205
|
+
.entry-footer {
|
|
206
|
+
flex: 0 0 auto;
|
|
207
|
+
min-height: 1.15em;
|
|
208
|
+
margin-top: auto;
|
|
209
|
+
border-top: 1px solid color-mix(in srgb, currentColor 20%, transparent);
|
|
210
|
+
padding-top: 0.25rem;
|
|
211
|
+
font-size: var(--ui-font-size-xs, 0.6875rem);
|
|
212
|
+
font-weight: 500;
|
|
213
|
+
line-height: 1.15;
|
|
214
|
+
overflow: hidden;
|
|
215
|
+
text-overflow: ellipsis;
|
|
216
|
+
white-space: nowrap;
|
|
217
|
+
}
|
|
218
|
+
.entry-link {
|
|
219
|
+
position: absolute;
|
|
220
|
+
z-index: 1;
|
|
221
|
+
inset: 0;
|
|
222
|
+
border-radius: inherit;
|
|
223
|
+
color: inherit;
|
|
224
|
+
cursor: pointer;
|
|
225
|
+
text-decoration: none;
|
|
226
|
+
}
|
|
227
|
+
.entry-link:focus-visible {
|
|
228
|
+
outline: 2px solid var(--ui-primary, #4f46e5);
|
|
229
|
+
outline-offset: -2px;
|
|
230
|
+
box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35)) inset;
|
|
231
|
+
}
|
|
232
|
+
@media (forced-colors: active) {
|
|
233
|
+
.entry-link:focus-visible {
|
|
234
|
+
outline-color: Highlight;
|
|
235
|
+
box-shadow: none;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
@media (prefers-reduced-motion: reduce) {
|
|
239
|
+
.entry-bar {
|
|
240
|
+
transition: none;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
slot {
|
|
244
|
+
display: none;
|
|
245
|
+
}
|
|
246
|
+
`,
|
|
247
|
+
]; }
|
|
248
|
+
/** Observes declarative entry attributes and slotted text for live updates. */
|
|
249
|
+
connectedCallback() {
|
|
250
|
+
super.connectedCallback();
|
|
251
|
+
this._entriesVersion++;
|
|
252
|
+
this._entriesObserver ??= new MutationObserver(() => this._entriesVersion++);
|
|
253
|
+
this._entriesObserver.observe(this, {
|
|
254
|
+
attributes: true,
|
|
255
|
+
attributeFilter: CALENDAR_ENTRY_ATTRIBUTES,
|
|
256
|
+
characterData: true,
|
|
257
|
+
childList: true,
|
|
258
|
+
subtree: true,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
/** Releases the entry observer when this month disconnects. */
|
|
262
|
+
disconnectedCallback() {
|
|
263
|
+
this._entriesObserver?.disconnect();
|
|
264
|
+
super.disconnectedCallback();
|
|
265
|
+
}
|
|
266
|
+
/** Restores hover/focus classes from the live DOM after Lit updates or reuses cells. */
|
|
267
|
+
updated() {
|
|
268
|
+
this._syncEntryInteractions();
|
|
269
|
+
}
|
|
270
|
+
/** Re-renders when declarative `calendar-entry` children are added or removed. */
|
|
271
|
+
_handleSlotChange() {
|
|
272
|
+
this._entriesVersion++;
|
|
273
|
+
}
|
|
274
|
+
/** Finds the laned entry (if any) covering `date` in a given lane. */
|
|
275
|
+
_entryFor(laned, lane, date) {
|
|
276
|
+
return laned.find((entry) => entry.lane === lane && entry.startDate <= date && date <= entry.endDate);
|
|
277
|
+
}
|
|
278
|
+
/** Classifies one day of an entry so only the outer endpoints are rounded. */
|
|
279
|
+
_segmentClass(entry, date) {
|
|
280
|
+
const startsHere = date.getTime() === entry.startDate.getTime();
|
|
281
|
+
const endsHere = date.getTime() === entry.endDate.getTime();
|
|
282
|
+
if (startsHere && endsHere)
|
|
283
|
+
return "segment-single";
|
|
284
|
+
if (startsHere)
|
|
285
|
+
return "segment-start";
|
|
286
|
+
if (endsHere)
|
|
287
|
+
return "segment-end";
|
|
288
|
+
return "segment-middle";
|
|
289
|
+
}
|
|
290
|
+
/** Current render key shared by an entry's title and body cells. */
|
|
291
|
+
_entryKey(entry) {
|
|
292
|
+
return `${entry.lane}|${entry.start}|${entry.end}|${entry.label}|${entry.href ?? ""}`;
|
|
293
|
+
}
|
|
294
|
+
/** Toggles one interaction class on every rendered cell belonging to an entry. */
|
|
295
|
+
_setEntryInteraction(entryKey, className, active) {
|
|
296
|
+
for (const cell of this.renderRoot.querySelectorAll(".entry-bar")) {
|
|
297
|
+
if (cell.dataset.entryKey === entryKey) {
|
|
298
|
+
cell.classList.toggle(className, active);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/** Reconciles classes against the links actually hovered or focused after a render. */
|
|
303
|
+
_syncEntryInteractions() {
|
|
304
|
+
for (const cell of this.renderRoot.querySelectorAll(".entry-bar")) {
|
|
305
|
+
cell.classList.remove("entry-hovered", "entry-focused");
|
|
306
|
+
}
|
|
307
|
+
for (const link of this.renderRoot.querySelectorAll(".entry-link:hover")) {
|
|
308
|
+
this._setEntryInteraction(link.dataset.entryKey ?? "", "entry-hovered", true);
|
|
309
|
+
}
|
|
310
|
+
const activeElement = this.shadowRoot?.activeElement;
|
|
311
|
+
if (activeElement instanceof HTMLElement && activeElement.matches(".entry-link")) {
|
|
312
|
+
this._setEntryInteraction(activeElement.dataset.entryKey ?? "", "entry-focused", true);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/** Renders a transparent full-cell link without wrapping the visible text. */
|
|
316
|
+
_renderEntryLink(entry, accessibleText) {
|
|
317
|
+
const entryKey = this._entryKey(entry);
|
|
318
|
+
return entry.href
|
|
319
|
+
? html `<a
|
|
320
|
+
class="entry-link"
|
|
321
|
+
href=${entry.href}
|
|
322
|
+
aria-label=${accessibleText}
|
|
323
|
+
data-entry-key=${entryKey}
|
|
324
|
+
@pointerenter=${() => this._setEntryInteraction(entryKey, "entry-hovered", true)}
|
|
325
|
+
@pointerleave=${() => this._setEntryInteraction(entryKey, "entry-hovered", false)}
|
|
326
|
+
@focus=${() => this._setEntryInteraction(entryKey, "entry-focused", true)}
|
|
327
|
+
@blur=${() => this._setEntryInteraction(entryKey, "entry-focused", false)}
|
|
328
|
+
></a>`
|
|
329
|
+
: nothing;
|
|
330
|
+
}
|
|
331
|
+
/** Joins the event title, details, and visible footer for tooltips and accessible link names. */
|
|
332
|
+
_entryBodyText(entry, showFooter) {
|
|
333
|
+
return [entry.label, ...(entry.details ?? []), showFooter ? entry.footer : undefined]
|
|
334
|
+
.filter((line) => Boolean(line))
|
|
335
|
+
.join("\n");
|
|
336
|
+
}
|
|
337
|
+
/** Renders the shared details body, with the footer reserved at its bottom edge. */
|
|
338
|
+
_renderEntryBody(entry, showFooter, detailLineClamp, showTitleFallback = false) {
|
|
339
|
+
const sourceDetails = entry.details ?? [];
|
|
340
|
+
const footer = showFooter ? entry.footer : undefined;
|
|
341
|
+
const details = showTitleFallback && sourceDetails.length === 0 && !footer
|
|
342
|
+
? [entry.label]
|
|
343
|
+
: sourceDetails;
|
|
344
|
+
const bodyText = this._entryBodyText(entry, showFooter);
|
|
345
|
+
return html `
|
|
346
|
+
${this._renderEntryLink(entry, bodyText)}
|
|
347
|
+
<div class="entry-body" aria-hidden=${entry.href ? "true" : nothing}>
|
|
348
|
+
${detailLineClamp > 0 && details.length > 0
|
|
349
|
+
? html `
|
|
350
|
+
<div
|
|
351
|
+
class="entry-details"
|
|
352
|
+
style=${`--entry-detail-lines: ${detailLineClamp}; --entry-detail-max-height: ${detailLineClamp * 1.15}em`}
|
|
353
|
+
>
|
|
354
|
+
${details.map((detail) => html `<div class="entry-detail">${detail}</div>`)}
|
|
355
|
+
</div>
|
|
356
|
+
`
|
|
357
|
+
: nothing}
|
|
358
|
+
${footer ? html `<div class="entry-footer" title=${footer}>${footer}</div>` : nothing}
|
|
359
|
+
</div>
|
|
360
|
+
`;
|
|
361
|
+
}
|
|
362
|
+
/** Renders a row-spanning body cell with whole-line detail and footer budgets. */
|
|
363
|
+
_renderEntryBodyCell(entry, bodyRows, reachesEventEnd, showTitleFallback = false) {
|
|
364
|
+
const segmentClass = reachesEventEnd ? "segment-end" : "segment-middle";
|
|
365
|
+
const footerLineBudget = reachesEventEnd && entry.footer ? 1 : 0;
|
|
366
|
+
const detailLineClamp = Math.max(0, bodyRows - footerLineBudget);
|
|
367
|
+
const bodyText = this._entryBodyText(entry, reachesEventEnd);
|
|
368
|
+
return html `
|
|
369
|
+
<td
|
|
370
|
+
class="lane-cell entry-bar entry-body-cell ${entry.color} ${segmentClass}"
|
|
371
|
+
data-entry-key=${this._entryKey(entry)}
|
|
372
|
+
rowspan=${bodyRows}
|
|
373
|
+
title=${entry.href ? nothing : bodyText}
|
|
374
|
+
>
|
|
375
|
+
${this._renderEntryBody(entry, reachesEventEnd, detailLineClamp, showTitleFallback)}
|
|
376
|
+
</td>
|
|
377
|
+
`;
|
|
378
|
+
}
|
|
379
|
+
/** Renders a lane's title/body cell, skips rows covered by a body rowspan, or emits an empty spacer. */
|
|
380
|
+
_renderLaneCell(laned, lane, date, monthStart, monthEnd) {
|
|
381
|
+
const entry = this._entryFor(laned, lane, date);
|
|
382
|
+
if (!entry) {
|
|
383
|
+
return html `<td class="lane-cell empty"></td>`;
|
|
384
|
+
}
|
|
385
|
+
const firstVisibleDay = entry.startDate > monthStart ? entry.startDate : monthStart;
|
|
386
|
+
const lastVisibleDay = entry.endDate < monthEnd ? entry.endDate : monthEnd;
|
|
387
|
+
const dayOffset = date.getDate() - firstVisibleDay.getDate();
|
|
388
|
+
const visibleDayCount = lastVisibleDay.getDate() - firstVisibleDay.getDate() + 1;
|
|
389
|
+
const isMultiDayEntry = entry.startDate.getTime() !== entry.endDate.getTime();
|
|
390
|
+
const isEndingOnlyClip = visibleDayCount === 1 &&
|
|
391
|
+
isMultiDayEntry &&
|
|
392
|
+
date.getTime() === entry.endDate.getTime();
|
|
393
|
+
if (isEndingOnlyClip) {
|
|
394
|
+
return this._renderEntryBodyCell(entry, 1, true, true);
|
|
395
|
+
}
|
|
396
|
+
if (dayOffset > 1)
|
|
397
|
+
return nothing;
|
|
398
|
+
if (dayOffset === 1) {
|
|
399
|
+
const bodyRows = visibleDayCount - 1;
|
|
400
|
+
const reachesEventEnd = lastVisibleDay.getTime() === entry.endDate.getTime();
|
|
401
|
+
return this._renderEntryBodyCell(entry, bodyRows, reachesEventEnd);
|
|
402
|
+
}
|
|
403
|
+
return html `
|
|
404
|
+
<td
|
|
405
|
+
class="lane-cell entry-bar entry-title-cell ${entry.color} ${this._segmentClass(entry, date)}"
|
|
406
|
+
data-entry-key=${this._entryKey(entry)}
|
|
407
|
+
title=${entry.href ? nothing : entry.label}
|
|
408
|
+
>
|
|
409
|
+
${this._renderEntryLink(entry, entry.label)}
|
|
410
|
+
<span class="entry-line entry-title" aria-hidden=${entry.href ? "true" : nothing}>
|
|
411
|
+
${entry.label}
|
|
412
|
+
</span>
|
|
413
|
+
</td>
|
|
414
|
+
`;
|
|
415
|
+
}
|
|
416
|
+
render() {
|
|
417
|
+
const totalDays = daysInMonth(this.year, this.month);
|
|
418
|
+
const monthStart = new Date(this.year, this.month - 1, 1);
|
|
419
|
+
const monthEnd = new Date(this.year, this.month - 1, totalDays);
|
|
420
|
+
const resolved = this._entryElements
|
|
421
|
+
.map(readCalendarEntryElement)
|
|
422
|
+
.map(resolveEntry)
|
|
423
|
+
.filter((entry) => entry !== null)
|
|
424
|
+
.filter((entry) => overlapsRange(entry, monthStart, monthEnd));
|
|
425
|
+
const { entries: laned, laneCount } = assignLanes(resolved);
|
|
426
|
+
const todayIso = toIsoDate(new Date());
|
|
427
|
+
const lanes = Array.from({ length: Math.max(laneCount, 1) }, (_, i) => i);
|
|
428
|
+
return html `
|
|
429
|
+
<div class="month">
|
|
430
|
+
<h4 class="month-name">${monthName(this.month)} ${this.year}</h4>
|
|
431
|
+
<table>
|
|
432
|
+
<tbody>
|
|
433
|
+
${repeat(Array.from({ length: totalDays }, (_, i) => i + 1), (d) => d, (d) => {
|
|
434
|
+
const date = new Date(this.year, this.month - 1, d);
|
|
435
|
+
const dow = date.getDay();
|
|
436
|
+
const weekendCls = isWeekend(date) ? "weekend" : "";
|
|
437
|
+
const todayCls = toIsoDate(date) === todayIso ? "today" : "";
|
|
438
|
+
return html `
|
|
439
|
+
<tr class="day-row ${weekendCls} ${todayCls}">
|
|
440
|
+
<td class="day-number">${d}</td>
|
|
441
|
+
<td class="day-weekday">${WEEKDAY_ABBR[dow]}</td>
|
|
442
|
+
${lanes.map((lane) => this._renderLaneCell(laned, lane, date, monthStart, monthEnd))}
|
|
443
|
+
</tr>
|
|
444
|
+
`;
|
|
445
|
+
})}
|
|
446
|
+
</tbody>
|
|
447
|
+
</table>
|
|
448
|
+
<slot @slotchange=${this._handleSlotChange}></slot>
|
|
449
|
+
</div>
|
|
450
|
+
`;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
__decorate([
|
|
454
|
+
property({ type: Number, reflect: true })
|
|
455
|
+
], CalendarMonth.prototype, "year", void 0);
|
|
456
|
+
__decorate([
|
|
457
|
+
property({ type: Number, reflect: true })
|
|
458
|
+
], CalendarMonth.prototype, "month", void 0);
|
|
459
|
+
__decorate([
|
|
460
|
+
queryAssignedElements({ selector: "calendar-entry" })
|
|
461
|
+
], CalendarMonth.prototype, "_entryElements", void 0);
|
|
462
|
+
__decorate([
|
|
463
|
+
state()
|
|
464
|
+
], CalendarMonth.prototype, "_entriesVersion", void 0);
|
|
465
|
+
CalendarMonth = __decorate([
|
|
466
|
+
customElement("calendar-month")
|
|
467
|
+
], CalendarMonth);
|
|
468
|
+
export { CalendarMonth };
|
|
469
|
+
//# sourceMappingURL=calendar-month.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-month.js","sourceRoot":"","sources":["../src/calendar-month.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport { customElement, property, queryAssignedElements, state } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport { CalendarEntry } from \"./calendar-entry.js\";\nimport { tokens } from \"./tokens.js\";\nimport {\n CALENDAR_ENTRY_ATTRIBUTES,\n WEEKDAY_ABBR,\n assignLanes,\n daysInMonth,\n isWeekend,\n monthName,\n overlapsRange,\n readCalendarEntryElement,\n resolveEntry,\n toIsoDate,\n type LanedEntry,\n type ResolvedCalendarEntry,\n} from \"./utils/calendar.js\";\n\n/**\n * One month rendered as a top-to-bottom list of days — weekends and today\n * highlighted, with declarative `calendar-entry` children shown as colored\n * bars spanning the days they cover. An entry's title uses its first visible\n * day; every remaining visible day becomes one shared body for wrapped\n * details and an optional ending footer. Overlapping entries stack into\n * side-by-side lanes rather than being layered/hidden. Read-only.\n *\n * Lanes are computed independently per instance, from only the entries\n * overlapping this month — an entry spanning a month boundary may therefore\n * land in a different lane index in the adjacent month's `calendar-month`.\n * This is an accepted v1 limitation: cross-month lane continuity would\n * require a shared parent (`calendar-year`) to assign lanes globally.\n *\n * Entry attributes and slotted title/detail text are observed, so a\n * standalone month re-renders when consumers update declarative metadata.\n *\n * @element calendar-month\n * @slot - Declarative `calendar-entry` elements to render for this month.\n */\n@customElement(\"calendar-month\")\nexport class CalendarMonth extends LitElement {\n /** Calendar year, e.g. `2026`. */\n @property({ type: Number, reflect: true }) year: number = new Date().getFullYear();\n\n /** Calendar month, 1-12 (January = 1). */\n @property({ type: Number, reflect: true }) month = 1;\n\n @queryAssignedElements({ selector: \"calendar-entry\" })\n private readonly _entryElements!: CalendarEntry[];\n\n /** Bumped when entry elements, attributes, or slotted text change to force a re-render. */\n @state() private _entriesVersion = 0;\n\n private _entriesObserver?: MutationObserver;\n\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n font-size: var(--ui-font-size-sm, 0.75rem);\n }\n .month-name {\n margin: 0 0 0.5rem;\n color: var(--ui-text, #0f172a);\n font-size: var(--ui-font-size, 0.875rem);\n font-weight: 600;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n table-layout: fixed;\n }\n .day-row td {\n padding: 0.25rem 0.5rem;\n border-bottom: 1px solid var(--ui-border, #e2e8f0);\n }\n .day-row.weekend {\n background: var(--ui-surface-muted, #f8fafc);\n }\n .day-row.today {\n background: color-mix(in srgb, var(--ui-primary, #4f46e5) 10%, transparent);\n }\n .day-number {\n width: 1.5rem;\n color: var(--ui-text-muted, #64748b);\n font-variant-numeric: tabular-nums;\n text-align: right;\n white-space: nowrap;\n }\n .day-row.today .day-number {\n color: var(--ui-primary, #4f46e5);\n font-weight: 700;\n }\n .day-weekday {\n width: 2.25rem;\n color: var(--ui-text-muted, #64748b);\n white-space: nowrap;\n }\n .day-row.today .day-weekday {\n color: var(--ui-primary, #4f46e5);\n font-weight: 600;\n }\n .day-row.weekend .day-weekday {\n font-weight: 600;\n }\n .lane-cell {\n padding: 0.25rem 0.5rem;\n }\n .entry-bar {\n border-radius: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n transition: box-shadow 120ms ease;\n white-space: nowrap;\n }\n .entry-bar.entry-hovered,\n .entry-bar.entry-focused {\n box-shadow: inset 0 0 0 100vmax var(--ui-hover-overlay, rgb(255 255 255 / 0.32));\n }\n .entry-bar.segment-single {\n border-radius: var(--ui-radius-sm, 0.25rem);\n }\n .entry-bar.segment-start {\n border-radius: var(--ui-radius-sm, 0.25rem) var(--ui-radius-sm, 0.25rem) 0 0;\n }\n .entry-bar.segment-end {\n border-radius: 0 0 var(--ui-radius-sm, 0.25rem) var(--ui-radius-sm, 0.25rem);\n }\n .entry-bar.segment-start,\n .entry-bar.segment-middle {\n border-bottom: 0;\n }\n .entry-bar.neutral {\n background: color-mix(in srgb, var(--ui-text-muted, #64748b) 15%, var(--ui-surface, #ffffff));\n color: var(--ui-text-muted, #64748b);\n }\n .entry-bar.info {\n background: color-mix(in srgb, var(--ui-info, #0ea5e9) 15%, var(--ui-surface, #ffffff));\n color: var(--ui-info, #0ea5e9);\n }\n .entry-bar.primary {\n background: color-mix(in srgb, var(--ui-primary, #4f46e5) 15%, var(--ui-surface, #ffffff));\n color: var(--ui-primary, #4f46e5);\n }\n .entry-bar.success {\n background: color-mix(in srgb, var(--ui-success, #16a34a) 15%, var(--ui-surface, #ffffff));\n color: var(--ui-success, #16a34a);\n }\n .entry-bar.warning {\n background: color-mix(in srgb, var(--ui-warning, #d97706) 15%, var(--ui-surface, #ffffff));\n color: var(--ui-warning, #d97706);\n }\n .entry-bar.danger {\n background: color-mix(in srgb, var(--ui-danger, #dc2626) 15%, var(--ui-surface, #ffffff));\n color: var(--ui-danger, #dc2626);\n }\n .entry-line {\n display: block;\n overflow: hidden;\n }\n .day-row td.entry-title-cell {\n position: relative;\n padding: 0;\n }\n .entry-title {\n box-sizing: border-box;\n font-weight: 500;\n padding: 0.25rem 0.5rem;\n pointer-events: none;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n .entry-details {\n display: -webkit-box;\n min-height: 0;\n max-height: var(--entry-detail-max-height, 1.15em);\n flex: 0 0 auto;\n overflow: hidden;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: var(--entry-detail-lines, 1);\n font-size: var(--ui-font-size-xs, 0.6875rem);\n font-weight: 400;\n line-height: 1.15;\n overflow-wrap: anywhere;\n text-overflow: ellipsis;\n white-space: normal;\n }\n .entry-detail {\n min-height: 1.15em;\n }\n .day-row td.entry-body-cell {\n position: relative;\n padding: 0;\n vertical-align: top;\n }\n .entry-body {\n position: absolute;\n inset: 0;\n box-sizing: border-box;\n display: flex;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n padding: 0.25rem 0.5rem;\n color: inherit;\n pointer-events: none;\n }\n .entry-footer {\n flex: 0 0 auto;\n min-height: 1.15em;\n margin-top: auto;\n border-top: 1px solid color-mix(in srgb, currentColor 20%, transparent);\n padding-top: 0.25rem;\n font-size: var(--ui-font-size-xs, 0.6875rem);\n font-weight: 500;\n line-height: 1.15;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .entry-link {\n position: absolute;\n z-index: 1;\n inset: 0;\n border-radius: inherit;\n color: inherit;\n cursor: pointer;\n text-decoration: none;\n }\n .entry-link:focus-visible {\n outline: 2px solid var(--ui-primary, #4f46e5);\n outline-offset: -2px;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35)) inset;\n }\n @media (forced-colors: active) {\n .entry-link:focus-visible {\n outline-color: Highlight;\n box-shadow: none;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .entry-bar {\n transition: none;\n }\n }\n slot {\n display: none;\n }\n `,\n ];\n\n /** Observes declarative entry attributes and slotted text for live updates. */\n override connectedCallback(): void {\n super.connectedCallback();\n this._entriesVersion++;\n this._entriesObserver ??= new MutationObserver(() => this._entriesVersion++);\n this._entriesObserver.observe(this, {\n attributes: true,\n attributeFilter: CALENDAR_ENTRY_ATTRIBUTES,\n characterData: true,\n childList: true,\n subtree: true,\n });\n }\n\n /** Releases the entry observer when this month disconnects. */\n override disconnectedCallback(): void {\n this._entriesObserver?.disconnect();\n super.disconnectedCallback();\n }\n\n /** Restores hover/focus classes from the live DOM after Lit updates or reuses cells. */\n protected override updated(): void {\n this._syncEntryInteractions();\n }\n\n /** Re-renders when declarative `calendar-entry` children are added or removed. */\n private _handleSlotChange(): void {\n this._entriesVersion++;\n }\n\n /** Finds the laned entry (if any) covering `date` in a given lane. */\n private _entryFor(laned: LanedEntry[], lane: number, date: Date): LanedEntry | undefined {\n return laned.find((entry) => entry.lane === lane && entry.startDate <= date && date <= entry.endDate);\n }\n\n /** Classifies one day of an entry so only the outer endpoints are rounded. */\n private _segmentClass(entry: LanedEntry, date: Date): string {\n const startsHere = date.getTime() === entry.startDate.getTime();\n const endsHere = date.getTime() === entry.endDate.getTime();\n if (startsHere && endsHere) return \"segment-single\";\n if (startsHere) return \"segment-start\";\n if (endsHere) return \"segment-end\";\n return \"segment-middle\";\n }\n\n /** Current render key shared by an entry's title and body cells. */\n private _entryKey(entry: LanedEntry): string {\n return `${entry.lane}|${entry.start}|${entry.end}|${entry.label}|${entry.href ?? \"\"}`;\n }\n\n /** Toggles one interaction class on every rendered cell belonging to an entry. */\n private _setEntryInteraction(entryKey: string, className: string, active: boolean): void {\n for (const cell of this.renderRoot.querySelectorAll<HTMLElement>(\".entry-bar\")) {\n if (cell.dataset.entryKey === entryKey) {\n cell.classList.toggle(className, active);\n }\n }\n }\n\n /** Reconciles classes against the links actually hovered or focused after a render. */\n private _syncEntryInteractions(): void {\n for (const cell of this.renderRoot.querySelectorAll<HTMLElement>(\".entry-bar\")) {\n cell.classList.remove(\"entry-hovered\", \"entry-focused\");\n }\n for (const link of this.renderRoot.querySelectorAll<HTMLElement>(\".entry-link:hover\")) {\n this._setEntryInteraction(link.dataset.entryKey ?? \"\", \"entry-hovered\", true);\n }\n const activeElement = this.shadowRoot?.activeElement;\n if (activeElement instanceof HTMLElement && activeElement.matches(\".entry-link\")) {\n this._setEntryInteraction(activeElement.dataset.entryKey ?? \"\", \"entry-focused\", true);\n }\n }\n\n /** Renders a transparent full-cell link without wrapping the visible text. */\n private _renderEntryLink(entry: LanedEntry, accessibleText: string) {\n const entryKey = this._entryKey(entry);\n return entry.href\n ? html`<a\n class=\"entry-link\"\n href=${entry.href}\n aria-label=${accessibleText}\n data-entry-key=${entryKey}\n @pointerenter=${() => this._setEntryInteraction(entryKey, \"entry-hovered\", true)}\n @pointerleave=${() => this._setEntryInteraction(entryKey, \"entry-hovered\", false)}\n @focus=${() => this._setEntryInteraction(entryKey, \"entry-focused\", true)}\n @blur=${() => this._setEntryInteraction(entryKey, \"entry-focused\", false)}\n ></a>`\n : nothing;\n }\n\n /** Joins the event title, details, and visible footer for tooltips and accessible link names. */\n private _entryBodyText(entry: LanedEntry, showFooter: boolean): string {\n return [entry.label, ...(entry.details ?? []), showFooter ? entry.footer : undefined]\n .filter((line): line is string => Boolean(line))\n .join(\"\\n\");\n }\n\n /** Renders the shared details body, with the footer reserved at its bottom edge. */\n private _renderEntryBody(\n entry: LanedEntry,\n showFooter: boolean,\n detailLineClamp: number,\n showTitleFallback = false,\n ) {\n const sourceDetails = entry.details ?? [];\n const footer = showFooter ? entry.footer : undefined;\n const details =\n showTitleFallback && sourceDetails.length === 0 && !footer\n ? [entry.label]\n : sourceDetails;\n const bodyText = this._entryBodyText(entry, showFooter);\n return html`\n ${this._renderEntryLink(entry, bodyText)}\n <div class=\"entry-body\" aria-hidden=${entry.href ? \"true\" : nothing}>\n ${detailLineClamp > 0 && details.length > 0\n ? html`\n <div\n class=\"entry-details\"\n style=${`--entry-detail-lines: ${detailLineClamp}; --entry-detail-max-height: ${\n detailLineClamp * 1.15\n }em`}\n >\n ${details.map((detail) => html`<div class=\"entry-detail\">${detail}</div>`)}\n </div>\n `\n : nothing}\n ${footer ? html`<div class=\"entry-footer\" title=${footer}>${footer}</div>` : nothing}\n </div>\n `;\n }\n\n /** Renders a row-spanning body cell with whole-line detail and footer budgets. */\n private _renderEntryBodyCell(\n entry: LanedEntry,\n bodyRows: number,\n reachesEventEnd: boolean,\n showTitleFallback = false,\n ) {\n const segmentClass = reachesEventEnd ? \"segment-end\" : \"segment-middle\";\n const footerLineBudget = reachesEventEnd && entry.footer ? 1 : 0;\n const detailLineClamp = Math.max(0, bodyRows - footerLineBudget);\n const bodyText = this._entryBodyText(entry, reachesEventEnd);\n return html`\n <td\n class=\"lane-cell entry-bar entry-body-cell ${entry.color} ${segmentClass}\"\n data-entry-key=${this._entryKey(entry)}\n rowspan=${bodyRows}\n title=${entry.href ? nothing : bodyText}\n >\n ${this._renderEntryBody(entry, reachesEventEnd, detailLineClamp, showTitleFallback)}\n </td>\n `;\n }\n\n /** Renders a lane's title/body cell, skips rows covered by a body rowspan, or emits an empty spacer. */\n private _renderLaneCell(\n laned: LanedEntry[],\n lane: number,\n date: Date,\n monthStart: Date,\n monthEnd: Date,\n ) {\n const entry = this._entryFor(laned, lane, date);\n if (!entry) {\n return html`<td class=\"lane-cell empty\"></td>`;\n }\n\n const firstVisibleDay = entry.startDate > monthStart ? entry.startDate : monthStart;\n const lastVisibleDay = entry.endDate < monthEnd ? entry.endDate : monthEnd;\n const dayOffset = date.getDate() - firstVisibleDay.getDate();\n const visibleDayCount = lastVisibleDay.getDate() - firstVisibleDay.getDate() + 1;\n const isMultiDayEntry = entry.startDate.getTime() !== entry.endDate.getTime();\n const isEndingOnlyClip =\n visibleDayCount === 1 &&\n isMultiDayEntry &&\n date.getTime() === entry.endDate.getTime();\n if (isEndingOnlyClip) {\n return this._renderEntryBodyCell(entry, 1, true, true);\n }\n if (dayOffset > 1) return nothing;\n\n if (dayOffset === 1) {\n const bodyRows = visibleDayCount - 1;\n const reachesEventEnd = lastVisibleDay.getTime() === entry.endDate.getTime();\n return this._renderEntryBodyCell(entry, bodyRows, reachesEventEnd);\n }\n\n return html`\n <td\n class=\"lane-cell entry-bar entry-title-cell ${entry.color} ${this._segmentClass(entry, date)}\"\n data-entry-key=${this._entryKey(entry)}\n title=${entry.href ? nothing : entry.label}\n >\n ${this._renderEntryLink(entry, entry.label)}\n <span class=\"entry-line entry-title\" aria-hidden=${entry.href ? \"true\" : nothing}>\n ${entry.label}\n </span>\n </td>\n `;\n }\n\n protected override render() {\n const totalDays = daysInMonth(this.year, this.month);\n const monthStart = new Date(this.year, this.month - 1, 1);\n const monthEnd = new Date(this.year, this.month - 1, totalDays);\n\n const resolved = this._entryElements\n .map(readCalendarEntryElement)\n .map(resolveEntry)\n .filter((entry): entry is ResolvedCalendarEntry => entry !== null)\n .filter((entry) => overlapsRange(entry, monthStart, monthEnd));\n const { entries: laned, laneCount } = assignLanes(resolved);\n const todayIso = toIsoDate(new Date());\n const lanes = Array.from({ length: Math.max(laneCount, 1) }, (_, i) => i);\n\n return html`\n <div class=\"month\">\n <h4 class=\"month-name\">${monthName(this.month)} ${this.year}</h4>\n <table>\n <tbody>\n ${repeat(\n Array.from({ length: totalDays }, (_, i) => i + 1),\n (d) => d,\n (d) => {\n const date = new Date(this.year, this.month - 1, d);\n const dow = date.getDay();\n const weekendCls = isWeekend(date) ? \"weekend\" : \"\";\n const todayCls = toIsoDate(date) === todayIso ? \"today\" : \"\";\n return html`\n <tr class=\"day-row ${weekendCls} ${todayCls}\">\n <td class=\"day-number\">${d}</td>\n <td class=\"day-weekday\">${WEEKDAY_ABBR[dow]}</td>\n ${lanes.map((lane) => this._renderLaneCell(laned, lane, date, monthStart, monthEnd))}\n </tr>\n `;\n },\n )}\n </tbody>\n </table>\n <slot @slotchange=${this._handleSlotChange}></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"calendar-month\": CalendarMonth;\n }\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import "./calendar-month.js";
|
|
3
|
+
/**
|
|
4
|
+
* A full year of `calendar-month` blocks, generated from declarative
|
|
5
|
+
* `calendar-entry` children. Each entry is re-projected into the
|
|
6
|
+
* `calendar-month` blocks it overlaps as a freshly-created `calendar-entry`
|
|
7
|
+
* element — the original elements stay slotted here and are never moved,
|
|
8
|
+
* since a DOM node can only have one parent. Read-only.
|
|
9
|
+
*
|
|
10
|
+
* @element calendar-year
|
|
11
|
+
* @slot - Declarative `calendar-entry` elements spanning the displayed year.
|
|
12
|
+
*/
|
|
13
|
+
export declare class CalendarYear extends LitElement {
|
|
14
|
+
/** Calendar year to render all 12 months for, e.g. `2026`. */
|
|
15
|
+
year: number;
|
|
16
|
+
private readonly _entryElements;
|
|
17
|
+
/** Bumped whenever entry elements, attributes, or slotted text change to force a re-render. */
|
|
18
|
+
private _entriesVersion;
|
|
19
|
+
private _entriesObserver?;
|
|
20
|
+
private readonly _entryKeys;
|
|
21
|
+
private _nextEntryKey;
|
|
22
|
+
static styles: import("lit").CSSResult[];
|
|
23
|
+
/**
|
|
24
|
+
* Watches slotted `calendar-entry` attributes and title/detail text.
|
|
25
|
+
* Neither reflected-property changes nor edits inside assigned elements
|
|
26
|
+
* fire this component's `slotchange`, so a subtree observer keeps the
|
|
27
|
+
* projected month entries synchronized.
|
|
28
|
+
*/
|
|
29
|
+
connectedCallback(): void;
|
|
30
|
+
/** Releases the mutation observer when the element disconnects. */
|
|
31
|
+
disconnectedCallback(): void;
|
|
32
|
+
/** Re-renders when declarative `calendar-entry` children are added or removed. */
|
|
33
|
+
private _handleSlotChange;
|
|
34
|
+
/** Returns an immutable identity for each original slotted entry. */
|
|
35
|
+
private _entryKey;
|
|
36
|
+
protected render(): import("lit-html").TemplateResult<1>;
|
|
37
|
+
}
|
|
38
|
+
declare global {
|
|
39
|
+
interface HTMLElementTagNameMap {
|
|
40
|
+
"calendar-year": CalendarYear;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=calendar-year.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-year.d.ts","sourceRoot":"","sources":["../src/calendar-year.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAKrD,OAAO,qBAAqB,CAAC;AAc7B;;;;;;;;;GASG;AACH,qBACa,YAAa,SAAQ,UAAU;IAC1C,8DAA8D;IACnB,IAAI,EAAE,MAAM,CAA4B;IAGnF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmB;IAElD,+FAA+F;IACtF,OAAO,CAAC,eAAe,CAAK;IAErC,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwC;IACnE,OAAO,CAAC,aAAa,CAAK;IAE1B,OAAgB,MAAM,4BAyBpB;IAEF;;;;;OAKG;IACM,iBAAiB,IAAI,IAAI,CAWjC;IAED,mEAAmE;IAC1D,oBAAoB,IAAI,IAAI,CAGpC;IAED,kFAAkF;IAClF,OAAO,CAAC,iBAAiB;IAIzB,qEAAqE;IACrE,OAAO,CAAC,SAAS;IASjB,UAAmB,MAAM,yCA0CxB;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,eAAe,EAAE,YAAY,CAAC;KAC/B;CACF"}
|