@syware/jlib 0.0.0-stage → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +133 -2
- package/dist/all.d.ts +1 -0
- package/dist/all.js +75 -0
- package/dist/atoms/jl-avatar.css_raw.js +3 -0
- package/dist/atoms/jl-avatar.d.ts +52 -0
- package/dist/atoms/jl-avatar.js +167 -0
- package/dist/atoms/jl-badge.css_raw.js +3 -0
- package/dist/atoms/jl-badge.d.ts +32 -0
- package/dist/atoms/jl-badge.js +60 -0
- package/dist/atoms/jl-button.css_raw.js +3 -0
- package/dist/atoms/jl-button.d.ts +70 -0
- package/dist/atoms/jl-button.js +146 -0
- package/dist/atoms/jl-checkbox.css_raw.js +3 -0
- package/dist/atoms/jl-checkbox.d.ts +61 -0
- package/dist/atoms/jl-checkbox.js +149 -0
- package/dist/atoms/jl-chip.css_raw.js +3 -0
- package/dist/atoms/jl-chip.d.ts +59 -0
- package/dist/atoms/jl-chip.js +164 -0
- package/dist/atoms/jl-divider.css_raw.js +3 -0
- package/dist/atoms/jl-divider.d.ts +26 -0
- package/dist/atoms/jl-divider.js +59 -0
- package/dist/atoms/jl-icon.css_raw.js +3 -0
- package/dist/atoms/jl-icon.d.ts +25 -0
- package/dist/atoms/jl-icon.js +41 -0
- package/dist/atoms/jl-input.css_raw.js +3 -0
- package/dist/atoms/jl-input.d.ts +112 -0
- package/dist/atoms/jl-input.js +202 -0
- package/dist/atoms/jl-kbd.css_raw.js +3 -0
- package/dist/atoms/jl-kbd.d.ts +17 -0
- package/dist/atoms/jl-kbd.js +62 -0
- package/dist/atoms/jl-link.css_raw.js +3 -0
- package/dist/atoms/jl-link.d.ts +37 -0
- package/dist/atoms/jl-link.js +86 -0
- package/dist/atoms/jl-progress-ring.css_raw.js +3 -0
- package/dist/atoms/jl-progress-ring.d.ts +49 -0
- package/dist/atoms/jl-progress-ring.js +132 -0
- package/dist/atoms/jl-progress.css_raw.js +3 -0
- package/dist/atoms/jl-progress.d.ts +43 -0
- package/dist/atoms/jl-progress.js +109 -0
- package/dist/atoms/jl-rating.css_raw.js +3 -0
- package/dist/atoms/jl-rating.d.ts +73 -0
- package/dist/atoms/jl-rating.js +184 -0
- package/dist/atoms/jl-select.css_raw.js +3 -0
- package/dist/atoms/jl-select.d.ts +70 -0
- package/dist/atoms/jl-select.js +146 -0
- package/dist/atoms/jl-skeleton.css_raw.js +3 -0
- package/dist/atoms/jl-skeleton.d.ts +34 -0
- package/dist/atoms/jl-skeleton.js +63 -0
- package/dist/atoms/jl-slider.css_raw.js +3 -0
- package/dist/atoms/jl-slider.d.ts +65 -0
- package/dist/atoms/jl-slider.js +143 -0
- package/dist/atoms/jl-spinner.css_raw.js +3 -0
- package/dist/atoms/jl-spinner.d.ts +16 -0
- package/dist/atoms/jl-spinner.js +26 -0
- package/dist/atoms/jl-switch.css_raw.js +3 -0
- package/dist/atoms/jl-switch.d.ts +57 -0
- package/dist/atoms/jl-switch.js +125 -0
- package/dist/atoms/jl-textarea.css_raw.js +3 -0
- package/dist/atoms/jl-textarea.d.ts +81 -0
- package/dist/atoms/jl-textarea.js +148 -0
- package/dist/base/jl-element.d.ts +16 -0
- package/dist/base/jl-element.js +35 -0
- package/dist/base/jl-form-control.d.ts +29 -0
- package/dist/base/jl-form-control.js +44 -0
- package/dist/index.d.ts +119 -0
- package/dist/index.js +72 -0
- package/dist/lib/arrow.d.ts +6 -0
- package/dist/lib/arrow.js +15 -0
- package/dist/lib/attributes.d.ts +9 -0
- package/dist/lib/attributes.js +22 -0
- package/dist/lib/avatar.d.ts +11 -0
- package/dist/lib/avatar.js +37 -0
- package/dist/lib/calendar.d.ts +48 -0
- package/dist/lib/calendar.js +166 -0
- package/dist/lib/css-length.d.ts +2 -0
- package/dist/lib/css-length.js +8 -0
- package/dist/lib/define.d.ts +9 -0
- package/dist/lib/define.js +9 -0
- package/dist/lib/dom.d.ts +4 -0
- package/dist/lib/dom.js +16 -0
- package/dist/lib/files.d.ts +18 -0
- package/dist/lib/files.js +89 -0
- package/dist/lib/floating.d.ts +13 -0
- package/dist/lib/floating.js +127 -0
- package/dist/lib/fuzzy.d.ts +26 -0
- package/dist/lib/fuzzy.js +149 -0
- package/dist/lib/highlight.d.ts +5 -0
- package/dist/lib/highlight.js +267 -0
- package/dist/lib/hotkey.d.ts +9 -0
- package/dist/lib/hotkey.js +101 -0
- package/dist/lib/icon-set.d.ts +95 -0
- package/dist/lib/icon-set.js +97 -0
- package/dist/lib/icons.d.ts +12 -0
- package/dist/lib/icons.js +39 -0
- package/dist/lib/indicator.d.ts +6 -0
- package/dist/lib/indicator.js +18 -0
- package/dist/lib/keep-in-view.d.ts +5 -0
- package/dist/lib/keep-in-view.js +15 -0
- package/dist/lib/key-shortcut.d.ts +20 -0
- package/dist/lib/key-shortcut.js +100 -0
- package/dist/lib/keycaps.d.ts +8 -0
- package/dist/lib/keycaps.js +162 -0
- package/dist/lib/locale.d.ts +4 -0
- package/dist/lib/locale.js +13 -0
- package/dist/lib/nav-tree.d.ts +9 -0
- package/dist/lib/nav-tree.js +30 -0
- package/dist/lib/numeric.d.ts +20 -0
- package/dist/lib/numeric.js +50 -0
- package/dist/lib/option-filter.d.ts +29 -0
- package/dist/lib/option-filter.js +108 -0
- package/dist/lib/option-groups.d.ts +6 -0
- package/dist/lib/option-groups.js +26 -0
- package/dist/lib/options.d.ts +5 -0
- package/dist/lib/options.js +24 -0
- package/dist/lib/pagination.d.ts +9 -0
- package/dist/lib/pagination.js +52 -0
- package/dist/lib/palette.d.ts +8 -0
- package/dist/lib/palette.js +46 -0
- package/dist/lib/progress.d.ts +20 -0
- package/dist/lib/progress.js +33 -0
- package/dist/lib/rating.d.ts +7 -0
- package/dist/lib/rating.js +26 -0
- package/dist/lib/roving-index.d.ts +10 -0
- package/dist/lib/roving-index.js +32 -0
- package/dist/lib/roving.d.ts +11 -0
- package/dist/lib/roving.js +21 -0
- package/dist/lib/slots.d.ts +2 -0
- package/dist/lib/slots.js +6 -0
- package/dist/lib/sparkline.d.ts +11 -0
- package/dist/lib/sparkline.js +42 -0
- package/dist/lib/split.d.ts +12 -0
- package/dist/lib/split.js +37 -0
- package/dist/lib/surface.d.ts +7 -0
- package/dist/lib/surface.js +28 -0
- package/dist/lib/table.d.ts +30 -0
- package/dist/lib/table.js +110 -0
- package/dist/lib/tags.d.ts +14 -0
- package/dist/lib/tags.js +29 -0
- package/dist/lib/text.d.ts +5 -0
- package/dist/lib/text.js +13 -0
- package/dist/lib/tokens.d.ts +5 -0
- package/dist/lib/tokens.js +15 -0
- package/dist/lib/tree.d.ts +16 -0
- package/dist/lib/tree.js +89 -0
- package/dist/lib/trend.d.ts +3 -0
- package/dist/lib/trend.js +15 -0
- package/dist/lib/trigger.d.ts +11 -0
- package/dist/lib/trigger.js +34 -0
- package/dist/molecules/jl-accordion-item.css_raw.js +3 -0
- package/dist/molecules/jl-accordion-item.d.ts +34 -0
- package/dist/molecules/jl-accordion-item.js +82 -0
- package/dist/molecules/jl-accordion.css_raw.js +3 -0
- package/dist/molecules/jl-accordion.d.ts +27 -0
- package/dist/molecules/jl-accordion.js +61 -0
- package/dist/molecules/jl-alert.css_raw.js +3 -0
- package/dist/molecules/jl-alert.d.ts +38 -0
- package/dist/molecules/jl-alert.js +131 -0
- package/dist/molecules/jl-avatar-group.css_raw.js +3 -0
- package/dist/molecules/jl-avatar-group.d.ts +28 -0
- package/dist/molecules/jl-avatar-group.js +69 -0
- package/dist/molecules/jl-breadcrumb.css_raw.js +3 -0
- package/dist/molecules/jl-breadcrumb.d.ts +35 -0
- package/dist/molecules/jl-breadcrumb.js +126 -0
- package/dist/molecules/jl-calendar.css_raw.js +3 -0
- package/dist/molecules/jl-calendar.d.ts +101 -0
- package/dist/molecules/jl-calendar.js +314 -0
- package/dist/molecules/jl-card.css_raw.js +3 -0
- package/dist/molecules/jl-card.d.ts +46 -0
- package/dist/molecules/jl-card.js +172 -0
- package/dist/molecules/jl-code-block.css_raw.js +3 -0
- package/dist/molecules/jl-code-block.d.ts +44 -0
- package/dist/molecules/jl-code-block.js +106 -0
- package/dist/molecules/jl-combobox.css_raw.js +3 -0
- package/dist/molecules/jl-combobox.d.ts +120 -0
- package/dist/molecules/jl-combobox.js +450 -0
- package/dist/molecules/jl-copy-button.css_raw.js +3 -0
- package/dist/molecules/jl-copy-button.d.ts +31 -0
- package/dist/molecules/jl-copy-button.js +69 -0
- package/dist/molecules/jl-date-picker.css_raw.js +3 -0
- package/dist/molecules/jl-date-picker.d.ts +109 -0
- package/dist/molecules/jl-date-picker.js +322 -0
- package/dist/molecules/jl-empty.css_raw.js +3 -0
- package/dist/molecules/jl-empty.d.ts +32 -0
- package/dist/molecules/jl-empty.js +94 -0
- package/dist/molecules/jl-field.css_raw.js +3 -0
- package/dist/molecules/jl-field.d.ts +49 -0
- package/dist/molecules/jl-field.js +147 -0
- package/dist/molecules/jl-file-drop.css_raw.js +3 -0
- package/dist/molecules/jl-file-drop.d.ts +93 -0
- package/dist/molecules/jl-file-drop.js +366 -0
- package/dist/molecules/jl-list-item.css_raw.js +3 -0
- package/dist/molecules/jl-list-item.d.ts +56 -0
- package/dist/molecules/jl-list-item.js +176 -0
- package/dist/molecules/jl-menu.css_raw.js +3 -0
- package/dist/molecules/jl-menu.d.ts +62 -0
- package/dist/molecules/jl-menu.js +288 -0
- package/dist/molecules/jl-number-input.css_raw.js +3 -0
- package/dist/molecules/jl-number-input.d.ts +81 -0
- package/dist/molecules/jl-number-input.js +220 -0
- package/dist/molecules/jl-pagination.css_raw.js +3 -0
- package/dist/molecules/jl-pagination.d.ts +59 -0
- package/dist/molecules/jl-pagination.js +172 -0
- package/dist/molecules/jl-popover.css_raw.js +3 -0
- package/dist/molecules/jl-popover.d.ts +49 -0
- package/dist/molecules/jl-popover.js +152 -0
- package/dist/molecules/jl-radio-group.css_raw.js +3 -0
- package/dist/molecules/jl-radio-group.d.ts +71 -0
- package/dist/molecules/jl-radio-group.js +202 -0
- package/dist/molecules/jl-search.css_raw.js +3 -0
- package/dist/molecules/jl-search.d.ts +71 -0
- package/dist/molecules/jl-search.js +210 -0
- package/dist/molecules/jl-segmented.css_raw.js +3 -0
- package/dist/molecules/jl-segmented.d.ts +65 -0
- package/dist/molecules/jl-segmented.js +187 -0
- package/dist/molecules/jl-stat.css_raw.js +3 -0
- package/dist/molecules/jl-stat.d.ts +55 -0
- package/dist/molecules/jl-stat.js +169 -0
- package/dist/molecules/jl-steps.css_raw.js +3 -0
- package/dist/molecules/jl-steps.d.ts +44 -0
- package/dist/molecules/jl-steps.js +118 -0
- package/dist/molecules/jl-tab-panel.css_raw.js +3 -0
- package/dist/molecules/jl-tab-panel.d.ts +35 -0
- package/dist/molecules/jl-tab-panel.js +35 -0
- package/dist/molecules/jl-tabs.css_raw.js +3 -0
- package/dist/molecules/jl-tabs.d.ts +51 -0
- package/dist/molecules/jl-tabs.js +198 -0
- package/dist/molecules/jl-tag-input.css_raw.js +3 -0
- package/dist/molecules/jl-tag-input.d.ts +103 -0
- package/dist/molecules/jl-tag-input.js +429 -0
- package/dist/molecules/jl-timeline.css_raw.js +3 -0
- package/dist/molecules/jl-timeline.d.ts +28 -0
- package/dist/molecules/jl-timeline.js +81 -0
- package/dist/molecules/jl-toast.css_raw.js +3 -0
- package/dist/molecules/jl-toast.d.ts +75 -0
- package/dist/molecules/jl-toast.js +274 -0
- package/dist/molecules/jl-tooltip.css_raw.js +3 -0
- package/dist/molecules/jl-tooltip.d.ts +44 -0
- package/dist/molecules/jl-tooltip.js +160 -0
- package/dist/organisms/jl-command-palette.css_raw.js +3 -0
- package/dist/organisms/jl-command-palette.d.ts +60 -0
- package/dist/organisms/jl-command-palette.js +285 -0
- package/dist/organisms/jl-confirm.css_raw.js +3 -0
- package/dist/organisms/jl-confirm.d.ts +56 -0
- package/dist/organisms/jl-confirm.js +132 -0
- package/dist/organisms/jl-dialog.css_raw.js +3 -0
- package/dist/organisms/jl-dialog.d.ts +43 -0
- package/dist/organisms/jl-dialog.js +120 -0
- package/dist/organisms/jl-drawer.css_raw.js +3 -0
- package/dist/organisms/jl-drawer.d.ts +50 -0
- package/dist/organisms/jl-drawer.js +141 -0
- package/dist/organisms/jl-navbar.css_raw.js +3 -0
- package/dist/organisms/jl-navbar.d.ts +32 -0
- package/dist/organisms/jl-navbar.js +121 -0
- package/dist/organisms/jl-page-header.css_raw.js +3 -0
- package/dist/organisms/jl-page-header.d.ts +26 -0
- package/dist/organisms/jl-page-header.js +85 -0
- package/dist/organisms/jl-side-nav.css_raw.js +3 -0
- package/dist/organisms/jl-side-nav.d.ts +60 -0
- package/dist/organisms/jl-side-nav.js +269 -0
- package/dist/organisms/jl-table.css_raw.js +3 -0
- package/dist/organisms/jl-table.d.ts +109 -0
- package/dist/organisms/jl-table.js +318 -0
- package/dist/organisms/jl-toaster.css_raw.js +3 -0
- package/dist/organisms/jl-toaster.d.ts +48 -0
- package/dist/organisms/jl-toaster.js +142 -0
- package/dist/organisms/jl-tree.css_raw.js +3 -0
- package/dist/organisms/jl-tree.d.ts +59 -0
- package/dist/organisms/jl-tree.js +198 -0
- package/dist/styles/base.css_raw.js +3 -0
- package/dist/styles/control.css_raw.js +3 -0
- package/dist/styles/floating.css_raw.js +3 -0
- package/dist/styles/shared.d.ts +8 -0
- package/dist/styles/shared.js +14 -0
- package/dist/styles/tokens.css_raw.js +3 -0
- package/dist/templates/jl-app-shell.css_raw.js +3 -0
- package/dist/templates/jl-app-shell.d.ts +37 -0
- package/dist/templates/jl-app-shell.js +150 -0
- package/dist/templates/jl-split-view.css_raw.js +3 -0
- package/dist/templates/jl-split-view.d.ts +49 -0
- package/dist/templates/jl-split-view.js +149 -0
- package/dist/tokens.css +188 -0
- package/dist/types/calendar.types.d.ts +11 -0
- package/dist/types/files.types.d.ts +23 -0
- package/dist/types/floating.types.d.ts +13 -0
- package/dist/types/fuzzy.types.d.ts +10 -0
- package/dist/types/highlight.types.d.ts +6 -0
- package/dist/types/hotkey.types.d.ts +19 -0
- package/dist/types/jl-accordion.types.d.ts +2 -0
- package/dist/types/jl-avatar.types.d.ts +10 -0
- package/dist/types/jl-breadcrumb.types.d.ts +6 -0
- package/dist/types/jl-button.types.d.ts +3 -0
- package/dist/types/jl-card.types.d.ts +2 -0
- package/dist/types/jl-chip.types.d.ts +1 -0
- package/dist/types/jl-combobox.types.d.ts +8 -0
- package/dist/types/jl-command-palette.types.d.ts +22 -0
- package/dist/types/jl-confirm.types.d.ts +10 -0
- package/dist/types/jl-dialog.types.d.ts +1 -0
- package/dist/types/jl-divider.types.d.ts +2 -0
- package/dist/types/jl-drawer.types.d.ts +4 -0
- package/dist/types/jl-empty.types.d.ts +2 -0
- package/dist/types/jl-field.types.d.ts +2 -0
- package/dist/types/jl-input.types.d.ts +1 -0
- package/dist/types/jl-link.types.d.ts +2 -0
- package/dist/types/jl-menu.types.d.ts +22 -0
- package/dist/types/jl-navbar.types.d.ts +6 -0
- package/dist/types/jl-progress.types.d.ts +3 -0
- package/dist/types/jl-radio-group.types.d.ts +10 -0
- package/dist/types/jl-segmented.types.d.ts +7 -0
- package/dist/types/jl-select.types.d.ts +12 -0
- package/dist/types/jl-side-nav.types.d.ts +17 -0
- package/dist/types/jl-skeleton.types.d.ts +2 -0
- package/dist/types/jl-slider.types.d.ts +5 -0
- package/dist/types/jl-split-view.types.d.ts +2 -0
- package/dist/types/jl-stat.types.d.ts +2 -0
- package/dist/types/jl-steps.types.d.ts +8 -0
- package/dist/types/jl-switch.types.d.ts +1 -0
- package/dist/types/jl-table.types.d.ts +30 -0
- package/dist/types/jl-tabs.types.d.ts +2 -0
- package/dist/types/jl-timeline.types.d.ts +10 -0
- package/dist/types/jl-toast.types.d.ts +2 -0
- package/dist/types/jl-toaster.types.d.ts +23 -0
- package/dist/types/jl-tree.types.d.ts +26 -0
- package/dist/types/key-shortcut.types.d.ts +9 -0
- package/dist/types/keycaps.types.d.ts +7 -0
- package/dist/types/option-filter.types.d.ts +10 -0
- package/dist/types/options.types.d.ts +4 -0
- package/dist/types/pagination.types.d.ts +8 -0
- package/dist/types/roving-index.types.d.ts +2 -0
- package/dist/types/sparkline.types.d.ts +11 -0
- package/package.json +102 -4
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { BASE_STYLES } from "../styles/shared.js";
|
|
2
|
+
import { JlFormControl } from "../base/jl-form-control.js";
|
|
3
|
+
import { clampedNumber, oneOf } from "../lib/attributes.js";
|
|
4
|
+
import { registerIcons } from "../lib/icons.js";
|
|
5
|
+
import { CONTROL_SIZES } from "../lib/options.js";
|
|
6
|
+
import { setOrRemove, uniqueId } from "../lib/dom.js";
|
|
7
|
+
import { JlIcon } from "../atoms/jl-icon.js";
|
|
8
|
+
import { addMonths, calendarDateOf, clampDate, dateAfterKey, dateFormatter, formatMonthTitle, isSameDate, isSameMonth, isWithin, monthGrid, monthHasDaysWithin, parseIsoDate, toIsoDate, weekdayNames } from "../lib/calendar.js";
|
|
9
|
+
import { pageLocale } from "../lib/locale.js";
|
|
10
|
+
import jl_calendar_default from "./jl-calendar.css_raw.js";
|
|
11
|
+
import { css } from "@jadis/core";
|
|
12
|
+
import { ChevronLeft, ChevronRight } from "lucide";
|
|
13
|
+
import { jsx, jsxs } from "@jadis/core/jsx-runtime";
|
|
14
|
+
// src/molecules/jl-calendar.tsx
|
|
15
|
+
registerIcons({
|
|
16
|
+
"chevron-left": ChevronLeft,
|
|
17
|
+
"chevron-right": ChevronRight
|
|
18
|
+
});
|
|
19
|
+
var NAV_ICON_SIZE = 16;
|
|
20
|
+
var MONDAY = 1;
|
|
21
|
+
var SUNDAY = 0;
|
|
22
|
+
var SATURDAY = 6;
|
|
23
|
+
var SELECT_KEYS = ["Enter", " "];
|
|
24
|
+
var VALUE_MISSING = "Choose a date.";
|
|
25
|
+
var FULL_DATE = { dateStyle: "full" };
|
|
26
|
+
/** The first day of the week from its attribute: 0 (Sunday) to 6 (Saturday), Monday when absent. */
|
|
27
|
+
var parseWeekday = (value) => Math.round(clampedNumber(MONDAY, SUNDAY, SATURDAY)(value));
|
|
28
|
+
/**
|
|
29
|
+
* A month to pick a day from. Arrows move by day and week, Page Up and Page Down by month (by year with Shift),
|
|
30
|
+
* Home and End to the ends of the week, Enter chooses. Month and weekday names follow the `locale`; days outside
|
|
31
|
+
* `min` and `max` cannot be chosen. `value` is an ISO date (`2026-10-25`).
|
|
32
|
+
*/
|
|
33
|
+
var JlCalendar = class extends JlFormControl {
|
|
34
|
+
static selector = "jl-calendar";
|
|
35
|
+
/** `change` when the chosen day changes; `pick` each time a day is chosen, even the one already chosen. */
|
|
36
|
+
events = this.useEvents();
|
|
37
|
+
value = this.useChange("", () => this.renderValue(), { attribute: "value" });
|
|
38
|
+
/** The first day that can be chosen, as an ISO date. */
|
|
39
|
+
min = this.useChange("", () => this.renderPage(), { attribute: "min" });
|
|
40
|
+
/** The last day that can be chosen, as an ISO date. */
|
|
41
|
+
max = this.useChange("", () => this.renderPage(), { attribute: "max" });
|
|
42
|
+
/** The day weeks start on: 0 for Sunday, 1 for Monday, up to 6 for Saturday. */
|
|
43
|
+
firstDay = this.useChange(MONDAY, () => this.renderPage(), {
|
|
44
|
+
attribute: "first-day",
|
|
45
|
+
parse: parseWeekday
|
|
46
|
+
});
|
|
47
|
+
/** The language of month and weekday names: `fr-CH`. The page's language by default. */
|
|
48
|
+
locale = this.useChange("", () => this.renderPage(), { attribute: "locale" });
|
|
49
|
+
/** The calendar's name for screen readers; the month's title names it otherwise. */
|
|
50
|
+
label = this.useChange("", (value) => setOrRemove(this._refs.base, "aria-label", value), { attribute: "label" });
|
|
51
|
+
required = this.useChange(false, () => this.renderValidity(), { attribute: "required" });
|
|
52
|
+
disabled = this.useChange(false, (value) => this.renderDisabled(value), { attribute: "disabled" });
|
|
53
|
+
/** Draws the frame in the danger colour, for an error the page found itself. */
|
|
54
|
+
invalid = this.useChange(false, (value) => this.reflect("invalid", value), { attribute: "invalid" });
|
|
55
|
+
/** The size of the days: 32, 36 or 42 pixels. */
|
|
56
|
+
size = this.useChange("md", (value) => this.reflect("size", value), {
|
|
57
|
+
attribute: "size",
|
|
58
|
+
parse: oneOf(CONTROL_SIZES, "md")
|
|
59
|
+
});
|
|
60
|
+
_titleId = uniqueId("jl-calendar-title");
|
|
61
|
+
/** The month shown, and the day the arrows move from (the one focusable cell of the grid). */
|
|
62
|
+
_state = {
|
|
63
|
+
focus: null,
|
|
64
|
+
formDisabled: false,
|
|
65
|
+
view: calendarDateOf(/* @__PURE__ */ new Date())
|
|
66
|
+
};
|
|
67
|
+
_refs = this.useRefs((ref) => ({
|
|
68
|
+
base: ref(".calendar"),
|
|
69
|
+
days: ref("tbody"),
|
|
70
|
+
grid: ref("table"),
|
|
71
|
+
next: ref(".next"),
|
|
72
|
+
previous: ref(".previous"),
|
|
73
|
+
title: ref(".title"),
|
|
74
|
+
weekdays: ref("thead tr")
|
|
75
|
+
}));
|
|
76
|
+
/** Focuses the day the arrows move from: the chosen one, or today, or the first of the month. */
|
|
77
|
+
focus(options) {
|
|
78
|
+
this._refs.days.querySelector("[tabindex=\"0\"]")?.focus(options);
|
|
79
|
+
}
|
|
80
|
+
/** Shows the month of a date (ISO), without choosing it. */
|
|
81
|
+
showMonth(iso) {
|
|
82
|
+
const date = parseIsoDate(iso);
|
|
83
|
+
if (date === null) return;
|
|
84
|
+
this._state.view = date;
|
|
85
|
+
this._state.focus = date;
|
|
86
|
+
this.whenRendered(() => this.renderPage());
|
|
87
|
+
}
|
|
88
|
+
templateHtml() {
|
|
89
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
90
|
+
class: "calendar",
|
|
91
|
+
role: "group",
|
|
92
|
+
"aria-labelledby": this._titleId,
|
|
93
|
+
attrs: { part: "base" },
|
|
94
|
+
children: [/* @__PURE__ */ jsxs("header", { children: [
|
|
95
|
+
/* @__PURE__ */ jsx("button", {
|
|
96
|
+
class: "nav previous",
|
|
97
|
+
type: "button",
|
|
98
|
+
"aria-label": "Previous month",
|
|
99
|
+
children: /* @__PURE__ */ jsx(JlIcon, {
|
|
100
|
+
name: "chevron-left",
|
|
101
|
+
size: NAV_ICON_SIZE
|
|
102
|
+
})
|
|
103
|
+
}),
|
|
104
|
+
/* @__PURE__ */ jsx("div", {
|
|
105
|
+
class: "title",
|
|
106
|
+
id: this._titleId,
|
|
107
|
+
"aria-live": "polite"
|
|
108
|
+
}),
|
|
109
|
+
/* @__PURE__ */ jsx("button", {
|
|
110
|
+
class: "nav next",
|
|
111
|
+
type: "button",
|
|
112
|
+
"aria-label": "Next month",
|
|
113
|
+
children: /* @__PURE__ */ jsx(JlIcon, {
|
|
114
|
+
name: "chevron-right",
|
|
115
|
+
size: NAV_ICON_SIZE
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
] }), /* @__PURE__ */ jsxs("table", {
|
|
119
|
+
role: "grid",
|
|
120
|
+
"aria-labelledby": this._titleId,
|
|
121
|
+
children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", {}) }), /* @__PURE__ */ jsx("tbody", {})]
|
|
122
|
+
})]
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
templateCss() {
|
|
126
|
+
return css`${BASE_STYLES}${jl_calendar_default}`;
|
|
127
|
+
}
|
|
128
|
+
onConnect() {
|
|
129
|
+
this.on(this._refs.previous, "click", () => this.turnPage(-1));
|
|
130
|
+
this.on(this._refs.next, "click", () => this.turnPage(1));
|
|
131
|
+
this.on(this._refs.days, "click", (event) => this.onDayClick(event));
|
|
132
|
+
this.on(this._refs.days, "keydown", (event) => this.onKey(event));
|
|
133
|
+
}
|
|
134
|
+
resetValue() {
|
|
135
|
+
this.value.set(this.getAttribute("value") ?? "");
|
|
136
|
+
}
|
|
137
|
+
setDisabledByForm(disabled) {
|
|
138
|
+
this._state.formDisabled = disabled;
|
|
139
|
+
this.whenRendered(() => this.renderDisabled(this.disabled.get()));
|
|
140
|
+
}
|
|
141
|
+
bounds() {
|
|
142
|
+
return {
|
|
143
|
+
max: parseIsoDate(this.max.get()),
|
|
144
|
+
min: parseIsoDate(this.min.get())
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
isOff() {
|
|
148
|
+
return this.disabled.get() || this._state.formDisabled;
|
|
149
|
+
}
|
|
150
|
+
onDayClick(event) {
|
|
151
|
+
const cell = event.target.closest("[data-date]");
|
|
152
|
+
const date = parseIsoDate(cell?.dataset.date ?? "");
|
|
153
|
+
if (date !== null) this.choose(date);
|
|
154
|
+
}
|
|
155
|
+
onKey(event) {
|
|
156
|
+
const focus = this._state.focus;
|
|
157
|
+
if (focus === null || this.isOff()) return;
|
|
158
|
+
if (SELECT_KEYS.includes(event.key)) {
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
this.choose(focus);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const next = dateAfterKey(focus, event.key, this.firstDay.get(), event.shiftKey);
|
|
164
|
+
if (next === null) return;
|
|
165
|
+
event.preventDefault();
|
|
166
|
+
const { min, max } = this.bounds();
|
|
167
|
+
this.moveFocus(clampDate(next, min, max));
|
|
168
|
+
}
|
|
169
|
+
/** Moves the focus to a day, turning the page when it is in another month. */
|
|
170
|
+
moveFocus(date) {
|
|
171
|
+
const turn = !isSameMonth(date, this._state.view);
|
|
172
|
+
this._state.focus = date;
|
|
173
|
+
if (turn) {
|
|
174
|
+
this._state.view = date;
|
|
175
|
+
this.renderPage();
|
|
176
|
+
} else this.renderFocus();
|
|
177
|
+
this.focus();
|
|
178
|
+
}
|
|
179
|
+
turnPage(months) {
|
|
180
|
+
const { min, max } = this.bounds();
|
|
181
|
+
const view = addMonths({
|
|
182
|
+
...this._state.view,
|
|
183
|
+
day: 1
|
|
184
|
+
}, months);
|
|
185
|
+
const focus = this._state.focus ?? view;
|
|
186
|
+
this._state.view = view;
|
|
187
|
+
this._state.focus = clampDate(addMonths(focus, months), min, max);
|
|
188
|
+
this.renderPage();
|
|
189
|
+
}
|
|
190
|
+
choose(date) {
|
|
191
|
+
const { min, max } = this.bounds();
|
|
192
|
+
if (!isWithin(date, min, max) || this.isOff()) return;
|
|
193
|
+
const iso = toIsoDate(date);
|
|
194
|
+
const changed = iso !== this.value.get();
|
|
195
|
+
this.value.set(iso);
|
|
196
|
+
this.focus();
|
|
197
|
+
this.events.emit("pick", iso);
|
|
198
|
+
if (changed) this.events.emit("change", iso);
|
|
199
|
+
}
|
|
200
|
+
/** A new value: its month shows, the focus moves to it, and the form gets it (or nothing, when unreadable). */
|
|
201
|
+
renderValue() {
|
|
202
|
+
const date = parseIsoDate(this.value.get());
|
|
203
|
+
if (date !== null) {
|
|
204
|
+
this._state.view = date;
|
|
205
|
+
this._state.focus = date;
|
|
206
|
+
}
|
|
207
|
+
this.internals.setFormValue(date === null ? "" : toIsoDate(date));
|
|
208
|
+
this.renderValidity();
|
|
209
|
+
this.renderPage();
|
|
210
|
+
}
|
|
211
|
+
/** The month shown, moved to the nearest one with days that can be chosen: today may be before `min`. */
|
|
212
|
+
viewWithin(min, max) {
|
|
213
|
+
const view = this._state.view;
|
|
214
|
+
return monthHasDaysWithin(view.year, view.month, min, max) ? view : clampDate(view, min, max);
|
|
215
|
+
}
|
|
216
|
+
/** The day the grid lets focus on: the remembered one when it is on the page, else the first day it can. */
|
|
217
|
+
rovingDate(min, max) {
|
|
218
|
+
const view = this._state.view;
|
|
219
|
+
const focus = this._state.focus ?? calendarDateOf(/* @__PURE__ */ new Date());
|
|
220
|
+
if (isSameMonth(focus, view) && isWithin(focus, min, max)) return focus;
|
|
221
|
+
return clampDate({
|
|
222
|
+
...view,
|
|
223
|
+
day: 1
|
|
224
|
+
}, min, max);
|
|
225
|
+
}
|
|
226
|
+
renderPage() {
|
|
227
|
+
const { min, max } = this.bounds();
|
|
228
|
+
this._state.view = this.viewWithin(min, max);
|
|
229
|
+
const { year, month } = this._state.view;
|
|
230
|
+
const locale = pageLocale(this.locale.get());
|
|
231
|
+
const firstDay = this.firstDay.get();
|
|
232
|
+
const context = {
|
|
233
|
+
focus: this.rovingDate(min, max),
|
|
234
|
+
label: dateFormatter(locale, FULL_DATE),
|
|
235
|
+
max,
|
|
236
|
+
min,
|
|
237
|
+
month,
|
|
238
|
+
selected: parseIsoDate(this.value.get()),
|
|
239
|
+
today: calendarDateOf(/* @__PURE__ */ new Date())
|
|
240
|
+
};
|
|
241
|
+
this._state.focus = context.focus;
|
|
242
|
+
this.renderTitle(formatMonthTitle(year, month, locale));
|
|
243
|
+
this.renderWeekdays(locale, firstDay);
|
|
244
|
+
this._refs.days.replaceChildren(...monthGrid(year, month, firstDay).map((week) => /* @__PURE__ */ jsx("tr", { children: week.map((date) => this.dayCell(date, context)) })));
|
|
245
|
+
this.renderNavigation(min, max);
|
|
246
|
+
}
|
|
247
|
+
/** The title is a live region: written only when the month changes, so it is announced once. */
|
|
248
|
+
renderTitle(text) {
|
|
249
|
+
const title = this._refs.title;
|
|
250
|
+
if (title.textContent !== text) title.textContent = text;
|
|
251
|
+
}
|
|
252
|
+
renderWeekdays(locale, firstDay) {
|
|
253
|
+
const short = weekdayNames(locale, firstDay, "short");
|
|
254
|
+
const long = weekdayNames(locale, firstDay, "long");
|
|
255
|
+
this._refs.weekdays.replaceChildren(...short.map((name, index) => /* @__PURE__ */ jsx("th", {
|
|
256
|
+
scope: "col",
|
|
257
|
+
abbr: long[index] ?? name,
|
|
258
|
+
children: name
|
|
259
|
+
})));
|
|
260
|
+
}
|
|
261
|
+
dayCell(date, context) {
|
|
262
|
+
const outside = date.month !== context.month;
|
|
263
|
+
const enabled = isWithin(date, context.min, context.max);
|
|
264
|
+
return /* @__PURE__ */ jsx("td", {
|
|
265
|
+
class: outside ? "day outside" : "day",
|
|
266
|
+
role: "gridcell",
|
|
267
|
+
tabIndex: isSameDate(date, context.focus) && !this.isOff() ? 0 : -1,
|
|
268
|
+
"data-date": toIsoDate(date),
|
|
269
|
+
"aria-label": context.label(date),
|
|
270
|
+
"aria-selected": String(isSameDate(date, context.selected)),
|
|
271
|
+
"aria-disabled": enabled ? void 0 : "true",
|
|
272
|
+
"aria-current": isSameDate(date, context.today) ? "date" : void 0,
|
|
273
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
274
|
+
class: "number",
|
|
275
|
+
children: String(date.day)
|
|
276
|
+
})
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
/** Only the remembered day is in the tab order: the arrows reach the others. */
|
|
280
|
+
renderFocus() {
|
|
281
|
+
const focus = this._state.focus;
|
|
282
|
+
const iso = focus === null ? "" : toIsoDate(focus);
|
|
283
|
+
for (const cell of this._refs.days.querySelectorAll("[data-date]")) cell.tabIndex = cell.dataset.date === iso && !this.isOff() ? 0 : -1;
|
|
284
|
+
}
|
|
285
|
+
/** The arrows of the header stop where no day of the next month could be chosen. */
|
|
286
|
+
renderNavigation(min, max) {
|
|
287
|
+
const view = this._state.view;
|
|
288
|
+
const previous = addMonths({
|
|
289
|
+
...view,
|
|
290
|
+
day: 1
|
|
291
|
+
}, -1);
|
|
292
|
+
const next = addMonths({
|
|
293
|
+
...view,
|
|
294
|
+
day: 1
|
|
295
|
+
}, 1);
|
|
296
|
+
const off = this.isOff();
|
|
297
|
+
this._refs.previous.disabled = off || !monthHasDaysWithin(previous.year, previous.month, min, max);
|
|
298
|
+
this._refs.next.disabled = off || !monthHasDaysWithin(next.year, next.month, min, max);
|
|
299
|
+
}
|
|
300
|
+
renderValidity() {
|
|
301
|
+
if (this.required.get() && parseIsoDate(this.value.get()) === null) {
|
|
302
|
+
this.internals.setValidity({ valueMissing: true }, VALUE_MISSING);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
this.internals.setValidity({});
|
|
306
|
+
}
|
|
307
|
+
renderDisabled(disabled) {
|
|
308
|
+
this.reflect("disabled", disabled);
|
|
309
|
+
this._refs.grid.toggleAttribute("aria-disabled", this.isOff());
|
|
310
|
+
this.renderPage();
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
JlCalendar.register();
|
|
314
|
+
export { JlCalendar };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// src/molecules/jl-card.css?raw
|
|
2
|
+
var jl_card_default = ":host {\n display: block;\n --pad: 20px;\n --edge: 20px;\n --gap: 12px;\n --radius: var(--jl-radius-lg);\n}\n\n:host([padding=\"none\"]) {\n --pad: 0px;\n --edge: 16px;\n}\n\n:host([padding=\"sm\"]) {\n --pad: 12px;\n --edge: 12px;\n --gap: 8px;\n}\n\n:host([padding=\"lg\"]) {\n --pad: 28px;\n --edge: 28px;\n --gap: 16px;\n}\n\n.card {\n position: relative;\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n border: 1px solid var(--jl-line);\n border-radius: var(--radius);\n background: var(--jl-surface);\n color: var(--jl-text);\n font-size: var(--jl-font-md);\n line-height: 1.55;\n transition: border-color var(--jl-fast) var(--jl-ease);\n}\n\n:host([clickable]) .card {\n cursor: pointer;\n}\n\n:host([clickable]) .card:hover {\n border-color: var(--jl-accent-line);\n}\n\n/* The link covers the card, so a press anywhere follows it; the header actions and the footer sit above it. */\n.link {\n position: absolute;\n inset: 0;\n z-index: 1;\n border-radius: inherit;\n}\n\n/* The card clips what overflows its corners, the link's own ring included: the card wears the ring instead. */\n.link:focus-visible {\n box-shadow: none;\n}\n\n.card:has(.link:focus-visible) {\n box-shadow: var(--jl-focus-ring);\n}\n\n.actions,\nfooter {\n position: relative;\n z-index: 2;\n}\n\n.media ::slotted(*) {\n display: block;\n width: 100%;\n}\n\n.media ::slotted(img) {\n object-fit: cover;\n}\n\nheader {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: var(--edge) var(--edge) 0;\n}\n\n/* Actions without a heading sit in the top right corner, over the content, rather than on a row of their own. */\n:host([floating-actions]) header {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n}\n\n:host([has-header]:not([has-body])) header {\n padding-bottom: var(--edge);\n}\n\n.titles {\n display: grid;\n flex: 1;\n gap: 2px;\n min-width: 0;\n}\n\nh3 {\n font-size: var(--jl-font-lg);\n font-weight: 650;\n letter-spacing: -0.01em;\n line-height: 1.35;\n}\n\n.description {\n color: var(--jl-muted);\n font-size: var(--jl-font-sm);\n line-height: 1.5;\n}\n\n.actions {\n display: flex;\n flex: none;\n align-items: center;\n gap: 6px;\n margin: -4px -6px -4px 0;\n}\n\n.body {\n flex: 1;\n padding: var(--pad);\n}\n\n:host([has-header]) .body {\n padding-top: var(--gap);\n}\n\n:host([has-header][padding=\"none\"]) .body {\n margin-top: var(--gap);\n padding-top: 0;\n border-top: 1px solid var(--jl-line);\n}\n\nfooter {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px var(--edge);\n border-top: 1px solid var(--jl-line);\n}\n\n:host(:not([has-body]):not([has-header])) footer {\n border-top: 0;\n}\n";
|
|
3
|
+
export { jl_card_default as default };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { JlElement } from '../base/jl-element';
|
|
2
|
+
import type { CardPadding } from '../types/jl-card.types';
|
|
3
|
+
export declare const CARD_PADDINGS: readonly CardPadding[];
|
|
4
|
+
/**
|
|
5
|
+
* A flat surface that groups one subject: a heading and a description, the content, and optional media on top,
|
|
6
|
+
* actions in the header and a footer. With `href` the whole card is a link; with `interactive` it fires `press`.
|
|
7
|
+
*/
|
|
8
|
+
export declare class JlCard extends JlElement {
|
|
9
|
+
static readonly selector = "jl-card";
|
|
10
|
+
readonly events: Readonly<import("@jadis/core").UseEventsHandler<{
|
|
11
|
+
press: undefined;
|
|
12
|
+
}>>;
|
|
13
|
+
readonly heading: Readonly<Readonly<{
|
|
14
|
+
get(): string;
|
|
15
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
16
|
+
}>>;
|
|
17
|
+
readonly description: Readonly<Readonly<{
|
|
18
|
+
get(): string;
|
|
19
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
20
|
+
}>>;
|
|
21
|
+
/** The room around the content; `none` leaves the body edge to edge, for a list or a table. */
|
|
22
|
+
readonly padding: Readonly<Readonly<{
|
|
23
|
+
get(): CardPadding;
|
|
24
|
+
set(setter: ((prevState: CardPadding) => CardPadding) | CardPadding): void;
|
|
25
|
+
}>>;
|
|
26
|
+
/** Can be focused and pressed as a whole: its border turns blue under the pointer, and it fires `press`. */
|
|
27
|
+
readonly interactive: Readonly<Readonly<{
|
|
28
|
+
get(): boolean;
|
|
29
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
30
|
+
}>>;
|
|
31
|
+
/** Makes the whole card a link, named by its heading. Actions and the footer stay pressable on their own. */
|
|
32
|
+
readonly href: Readonly<Readonly<{
|
|
33
|
+
get(): string;
|
|
34
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
35
|
+
}>>;
|
|
36
|
+
private readonly _refs;
|
|
37
|
+
focus(options?: FocusOptions): void;
|
|
38
|
+
templateHtml(): Node;
|
|
39
|
+
templateCss(): string;
|
|
40
|
+
onConnect(): void;
|
|
41
|
+
/** An interactive card without a link fires `press`; a link card leaves the press to the link. */
|
|
42
|
+
private pressable;
|
|
43
|
+
private renderInteraction;
|
|
44
|
+
/** Shows only the parts that have something in them, so an empty one leaves no gap. */
|
|
45
|
+
private renderSections;
|
|
46
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { BASE_STYLES } from "../styles/shared.js";
|
|
2
|
+
import { JlElement } from "../base/jl-element.js";
|
|
3
|
+
import { oneOf } from "../lib/attributes.js";
|
|
4
|
+
import { setOrRemove } from "../lib/dom.js";
|
|
5
|
+
import { hasSlotted } from "../lib/slots.js";
|
|
6
|
+
import { isActivationKey, startedOnControl } from "../lib/surface.js";
|
|
7
|
+
import jl_card_default from "./jl-card.css_raw.js";
|
|
8
|
+
import { css } from "@jadis/core";
|
|
9
|
+
import { jsx, jsxs } from "@jadis/core/jsx-runtime";
|
|
10
|
+
// src/molecules/jl-card.tsx
|
|
11
|
+
var CARD_PADDINGS = [
|
|
12
|
+
"md",
|
|
13
|
+
"none",
|
|
14
|
+
"sm",
|
|
15
|
+
"lg"
|
|
16
|
+
];
|
|
17
|
+
var DEFAULT_LINK_NAME = "Open";
|
|
18
|
+
/**
|
|
19
|
+
* A flat surface that groups one subject: a heading and a description, the content, and optional media on top,
|
|
20
|
+
* actions in the header and a footer. With `href` the whole card is a link; with `interactive` it fires `press`.
|
|
21
|
+
*/
|
|
22
|
+
var JlCard = class extends JlElement {
|
|
23
|
+
static selector = "jl-card";
|
|
24
|
+
events = this.useEvents();
|
|
25
|
+
heading = this.useChange("", (value) => {
|
|
26
|
+
this._refs.heading.textContent = value;
|
|
27
|
+
this._refs.heading.hidden = value === "";
|
|
28
|
+
this._refs.linkName.textContent = value || DEFAULT_LINK_NAME;
|
|
29
|
+
this.renderSections();
|
|
30
|
+
}, { attribute: "heading" });
|
|
31
|
+
description = this.useChange("", (value) => {
|
|
32
|
+
this._refs.description.textContent = value;
|
|
33
|
+
this._refs.description.hidden = value === "";
|
|
34
|
+
this.renderSections();
|
|
35
|
+
}, { attribute: "description" });
|
|
36
|
+
/** The room around the content; `none` leaves the body edge to edge, for a list or a table. */
|
|
37
|
+
padding = this.useChange("md", (value) => this.reflect("padding", value), {
|
|
38
|
+
attribute: "padding",
|
|
39
|
+
parse: oneOf(CARD_PADDINGS, "md")
|
|
40
|
+
});
|
|
41
|
+
/** Can be focused and pressed as a whole: its border turns blue under the pointer, and it fires `press`. */
|
|
42
|
+
interactive = this.useChange(false, () => this.renderInteraction(), { attribute: "interactive" });
|
|
43
|
+
/** Makes the whole card a link, named by its heading. Actions and the footer stay pressable on their own. */
|
|
44
|
+
href = this.useChange("", () => this.renderInteraction(), { attribute: "href" });
|
|
45
|
+
_refs = this.useRefs((ref) => ({
|
|
46
|
+
actions: ref(".actions"),
|
|
47
|
+
actionsSlot: ref("slot[name=\"actions\"]"),
|
|
48
|
+
body: ref(".body"),
|
|
49
|
+
bodySlot: ref("slot:not([name])"),
|
|
50
|
+
card: ref(".card"),
|
|
51
|
+
description: ref(".description"),
|
|
52
|
+
footer: ref("footer"),
|
|
53
|
+
footerSlot: ref("slot[name=\"footer\"]"),
|
|
54
|
+
header: ref("header"),
|
|
55
|
+
heading: ref("h3"),
|
|
56
|
+
link: ref(".link"),
|
|
57
|
+
linkName: ref(".link-name"),
|
|
58
|
+
media: ref(".media"),
|
|
59
|
+
mediaSlot: ref("slot[name=\"media\"]")
|
|
60
|
+
}));
|
|
61
|
+
focus(options) {
|
|
62
|
+
(this.href.get() === "" ? this._refs.card : this._refs.link).focus(options);
|
|
63
|
+
}
|
|
64
|
+
templateHtml() {
|
|
65
|
+
return /* @__PURE__ */ jsxs("article", {
|
|
66
|
+
class: "card",
|
|
67
|
+
attrs: { part: "base" },
|
|
68
|
+
children: [
|
|
69
|
+
/* @__PURE__ */ jsx("a", {
|
|
70
|
+
class: "link",
|
|
71
|
+
"aria-describedby": "description",
|
|
72
|
+
hidden: true,
|
|
73
|
+
children: /* @__PURE__ */ jsx("span", { class: "link-name sr-only" })
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ jsx("div", {
|
|
76
|
+
class: "media",
|
|
77
|
+
hidden: true,
|
|
78
|
+
children: /* @__PURE__ */ jsx("slot", { name: "media" })
|
|
79
|
+
}),
|
|
80
|
+
/* @__PURE__ */ jsxs("header", {
|
|
81
|
+
hidden: true,
|
|
82
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
83
|
+
class: "titles",
|
|
84
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
85
|
+
id: "heading",
|
|
86
|
+
hidden: true
|
|
87
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
88
|
+
class: "description",
|
|
89
|
+
id: "description",
|
|
90
|
+
hidden: true
|
|
91
|
+
})]
|
|
92
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
93
|
+
class: "actions",
|
|
94
|
+
children: /* @__PURE__ */ jsx("slot", { name: "actions" })
|
|
95
|
+
})]
|
|
96
|
+
}),
|
|
97
|
+
/* @__PURE__ */ jsx("div", {
|
|
98
|
+
class: "body",
|
|
99
|
+
hidden: true,
|
|
100
|
+
children: /* @__PURE__ */ jsx("slot", {})
|
|
101
|
+
}),
|
|
102
|
+
/* @__PURE__ */ jsx("footer", {
|
|
103
|
+
hidden: true,
|
|
104
|
+
children: /* @__PURE__ */ jsx("slot", { name: "footer" })
|
|
105
|
+
})
|
|
106
|
+
]
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
templateCss() {
|
|
110
|
+
return css`${BASE_STYLES}${jl_card_default}`;
|
|
111
|
+
}
|
|
112
|
+
onConnect() {
|
|
113
|
+
const card = this._refs.card;
|
|
114
|
+
this.renderSections();
|
|
115
|
+
const refs = this._refs;
|
|
116
|
+
for (const slot of [
|
|
117
|
+
refs.mediaSlot,
|
|
118
|
+
refs.actionsSlot,
|
|
119
|
+
refs.bodySlot,
|
|
120
|
+
refs.footerSlot
|
|
121
|
+
]) this.on(slot, "slotchange", () => this.renderSections());
|
|
122
|
+
this.on(card, "click", (event) => {
|
|
123
|
+
if (this.pressable() && !startedOnControl(event, card)) this.events.emit("press");
|
|
124
|
+
});
|
|
125
|
+
this.on(card, "keydown", (event) => {
|
|
126
|
+
if (this.pressable() && event.composedPath()[0] === card && isActivationKey(event)) {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
this.events.emit("press");
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** An interactive card without a link fires `press`; a link card leaves the press to the link. */
|
|
133
|
+
pressable() {
|
|
134
|
+
return this.interactive.get() && this.href.get() === "";
|
|
135
|
+
}
|
|
136
|
+
renderInteraction() {
|
|
137
|
+
const href = this.href.get();
|
|
138
|
+
const card = this._refs.card;
|
|
139
|
+
const link = this._refs.link;
|
|
140
|
+
const pressable = this.pressable();
|
|
141
|
+
setOrRemove(link, "href", href);
|
|
142
|
+
link.hidden = href === "";
|
|
143
|
+
this.reflect("clickable", this.interactive.get() || href !== "");
|
|
144
|
+
this.reflect("linked", href !== "");
|
|
145
|
+
if (!pressable) {
|
|
146
|
+
card.removeAttribute("tabindex");
|
|
147
|
+
card.removeAttribute("role");
|
|
148
|
+
card.removeAttribute("aria-labelledby");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
card.tabIndex = 0;
|
|
152
|
+
card.setAttribute("role", "button");
|
|
153
|
+
card.setAttribute("aria-labelledby", "heading");
|
|
154
|
+
}
|
|
155
|
+
/** Shows only the parts that have something in them, so an empty one leaves no gap. */
|
|
156
|
+
renderSections() {
|
|
157
|
+
const refs = this._refs;
|
|
158
|
+
const hasActions = hasSlotted(refs.actionsSlot);
|
|
159
|
+
const hasTitles = this.heading.get() !== "" || this.description.get() !== "";
|
|
160
|
+
const hasBody = hasSlotted(refs.bodySlot);
|
|
161
|
+
refs.header.hidden = !(hasTitles || hasActions);
|
|
162
|
+
refs.actions.hidden = !hasActions;
|
|
163
|
+
refs.media.hidden = !hasSlotted(refs.mediaSlot);
|
|
164
|
+
refs.body.hidden = !hasBody;
|
|
165
|
+
refs.footer.hidden = !hasSlotted(refs.footerSlot);
|
|
166
|
+
this.reflect("has-header", hasTitles);
|
|
167
|
+
this.reflect("floating-actions", hasActions && !hasTitles);
|
|
168
|
+
this.reflect("has-body", hasBody);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
JlCard.register();
|
|
172
|
+
export { CARD_PADDINGS, JlCard };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// src/molecules/jl-code-block.css?raw
|
|
2
|
+
var jl_code_block_default = ":host {\n display: block;\n min-width: 0;\n}\n\nfigure {\n position: relative;\n border: 1px solid var(--jl-line);\n border-radius: var(--jl-radius);\n background: var(--jl-surface-2);\n overflow: hidden;\n}\n\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n height: 38px;\n padding: 0 52px 0 14px;\n border-bottom: 1px solid var(--jl-line);\n color: var(--jl-muted);\n font-size: var(--jl-font-xs);\n}\n\n.heading {\n overflow: hidden;\n color: var(--jl-text-2);\n font-family: var(--jl-font-mono);\n font-weight: 500;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.language {\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n\n.body {\n display: flex;\n min-width: 0;\n}\n\n.gutter,\npre {\n padding: 14px 16px;\n font-family: var(--jl-font-mono);\n font-size: 12.5px;\n line-height: 1.7;\n font-variant-ligatures: no-contextual;\n}\n\n.gutter {\n flex: none;\n padding-right: 4px;\n color: var(--jl-faint);\n text-align: right;\n white-space: pre;\n user-select: none;\n}\n\npre {\n flex: 1;\n min-width: 0;\n margin: 0;\n padding-right: 48px;\n overflow-x: auto;\n color: var(--jl-text);\n tab-size: 2;\n}\n\n:host([wrap]) pre {\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n\n.copy {\n position: absolute;\n top: 6px;\n right: 6px;\n background: var(--jl-surface-2);\n border-radius: var(--jl-radius-sm);\n}\n\n:host(:not([has-header])) .copy {\n opacity: 0.6;\n transition: opacity var(--jl-fast) var(--jl-ease);\n}\n\n:host(:not([has-header])) figure:hover .copy,\n.copy:focus-within {\n opacity: 1;\n}\n\n.tok-comment {\n color: var(--jl-code-comment);\n font-style: italic;\n}\n\n.tok-string {\n color: var(--jl-code-string);\n}\n\n.tok-number {\n color: var(--jl-code-number);\n}\n\n.tok-keyword {\n color: var(--jl-code-keyword);\n}\n\n.tok-tag {\n color: var(--jl-code-tag);\n}\n\n.tok-attribute {\n color: var(--jl-code-attribute);\n}\n\n.tok-property {\n color: var(--jl-code-property);\n}\n\n.tok-function {\n color: var(--jl-code-function);\n}\n\n.tok-punctuation {\n color: var(--jl-muted);\n}\n";
|
|
3
|
+
export { jl_code_block_default as default };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { JlElement } from '../base/jl-element';
|
|
2
|
+
import type { CodeLanguage } from '../types/highlight.types';
|
|
3
|
+
export declare const CODE_LANGUAGES: readonly CodeLanguage[];
|
|
4
|
+
/**
|
|
5
|
+
* Source code, coloured, with a button to copy it. The code is the `code` property, or the element's own text
|
|
6
|
+
* (a `<template>` inside keeps HTML as text): `<jl-code-block language="ts">const a = 1;</jl-code-block>`.
|
|
7
|
+
*/
|
|
8
|
+
export declare class JlCodeBlock extends JlElement {
|
|
9
|
+
static readonly selector = "jl-code-block";
|
|
10
|
+
readonly code: Readonly<Readonly<{
|
|
11
|
+
get(): string;
|
|
12
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
13
|
+
}>>;
|
|
14
|
+
readonly language: Readonly<Readonly<{
|
|
15
|
+
get(): CodeLanguage;
|
|
16
|
+
set(setter: ((prevState: CodeLanguage) => CodeLanguage) | CodeLanguage): void;
|
|
17
|
+
}>>;
|
|
18
|
+
/** A file name or a title over the code. */
|
|
19
|
+
readonly heading: Readonly<Readonly<{
|
|
20
|
+
get(): string;
|
|
21
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
22
|
+
}>>;
|
|
23
|
+
readonly lineNumbers: Readonly<Readonly<{
|
|
24
|
+
get(): boolean;
|
|
25
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
26
|
+
}>>;
|
|
27
|
+
readonly hideCopy: Readonly<Readonly<{
|
|
28
|
+
get(): boolean;
|
|
29
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
30
|
+
}>>;
|
|
31
|
+
/** Wraps long lines instead of scrolling sideways. */
|
|
32
|
+
readonly wrap: Readonly<Readonly<{
|
|
33
|
+
get(): boolean;
|
|
34
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
35
|
+
}>>;
|
|
36
|
+
private readonly _refs;
|
|
37
|
+
templateHtml(): Node;
|
|
38
|
+
templateCss(): string;
|
|
39
|
+
/** The code shown: the `code` input, or else the text the page wrote inside the element. */
|
|
40
|
+
private source;
|
|
41
|
+
private render;
|
|
42
|
+
private renderGutter;
|
|
43
|
+
private renderHeader;
|
|
44
|
+
}
|