@syware/jlib 0.5.1 → 0.7.0

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.
Files changed (137) hide show
  1. package/dist/all.js +11 -4
  2. package/dist/atoms/jl-app-icon.css_raw.js +3 -0
  3. package/dist/atoms/jl-app-icon.d.ts +34 -0
  4. package/dist/atoms/jl-app-icon.js +86 -0
  5. package/dist/atoms/jl-avatar.js +1 -1
  6. package/dist/atoms/jl-badge.js +9 -2
  7. package/dist/atoms/jl-button.d.ts +5 -0
  8. package/dist/atoms/jl-button.js +5 -2
  9. package/dist/atoms/jl-chip.js +3 -2
  10. package/dist/atoms/jl-input.js +5 -4
  11. package/dist/atoms/jl-link.js +2 -2
  12. package/dist/atoms/jl-rating.js +4 -5
  13. package/dist/atoms/jl-select.css_raw.js +1 -1
  14. package/dist/atoms/jl-select.d.ts +3 -2
  15. package/dist/atoms/jl-select.js +6 -5
  16. package/dist/atoms/jl-spinner.js +2 -2
  17. package/dist/base/jl-form-control.js +2 -1
  18. package/dist/index.d.ts +10 -0
  19. package/dist/index.js +10 -3
  20. package/dist/lib/app-icon.d.ts +8 -0
  21. package/dist/lib/app-icon.js +37 -0
  22. package/dist/lib/dom.d.ts +2 -0
  23. package/dist/lib/dom.js +3 -1
  24. package/dist/lib/download.d.ts +10 -0
  25. package/dist/lib/download.js +62 -0
  26. package/dist/lib/icons.d.ts +4 -0
  27. package/dist/lib/icons.js +7 -4
  28. package/dist/lib/input-list.d.ts +12 -0
  29. package/dist/lib/input-list.js +14 -0
  30. package/dist/lib/internals.d.ts +5 -0
  31. package/dist/lib/internals.js +18 -0
  32. package/dist/lib/palette.js +6 -0
  33. package/dist/lib/resize.d.ts +2 -0
  34. package/dist/lib/resize.js +10 -0
  35. package/dist/lib/table.d.ts +6 -1
  36. package/dist/lib/table.js +16 -1
  37. package/dist/lib/text.d.ts +12 -0
  38. package/dist/lib/text.js +16 -1
  39. package/dist/lib/texts.d.ts +97 -0
  40. package/dist/lib/texts.js +102 -0
  41. package/dist/lib/time.d.ts +5 -3
  42. package/dist/lib/time.js +23 -15
  43. package/dist/lib/tree.d.ts +10 -1
  44. package/dist/lib/tree.js +37 -3
  45. package/dist/lib/trigger.js +1 -1
  46. package/dist/molecules/jl-account-button.css_raw.js +1 -1
  47. package/dist/molecules/jl-account-button.d.ts +8 -1
  48. package/dist/molecules/jl-account-button.js +9 -2
  49. package/dist/molecules/jl-alert.js +3 -2
  50. package/dist/molecules/jl-breadcrumb.js +3 -3
  51. package/dist/molecules/jl-calendar.js +5 -5
  52. package/dist/molecules/jl-chip-group.js +3 -3
  53. package/dist/molecules/jl-code-block.css_raw.js +1 -1
  54. package/dist/molecules/jl-code-block.d.ts +5 -0
  55. package/dist/molecules/jl-code-block.js +5 -2
  56. package/dist/molecules/jl-combobox.js +6 -7
  57. package/dist/molecules/jl-copy-button.js +5 -4
  58. package/dist/molecules/jl-date-picker.js +7 -8
  59. package/dist/molecules/jl-empty.js +2 -2
  60. package/dist/molecules/jl-facts.css_raw.js +3 -0
  61. package/dist/molecules/jl-facts.d.ts +35 -0
  62. package/dist/molecules/jl-facts.js +64 -0
  63. package/dist/molecules/jl-file-drop.d.ts +5 -0
  64. package/dist/molecules/jl-file-drop.js +18 -15
  65. package/dist/molecules/jl-input-list.css_raw.js +3 -0
  66. package/dist/molecules/jl-input-list.d.ts +79 -0
  67. package/dist/molecules/jl-input-list.js +177 -0
  68. package/dist/molecules/jl-list-item.js +1 -1
  69. package/dist/molecules/jl-load-more.css_raw.js +3 -0
  70. package/dist/molecules/jl-load-more.d.ts +41 -0
  71. package/dist/molecules/jl-load-more.js +62 -0
  72. package/dist/molecules/jl-load-state.d.ts +4 -1
  73. package/dist/molecules/jl-load-state.js +20 -12
  74. package/dist/molecules/jl-menu.js +1 -1
  75. package/dist/molecules/jl-number-input.js +5 -5
  76. package/dist/molecules/jl-pagination.js +5 -5
  77. package/dist/molecules/jl-radio-group.js +2 -2
  78. package/dist/molecules/jl-search-button.js +5 -5
  79. package/dist/molecules/jl-search.js +5 -5
  80. package/dist/molecules/jl-segmented.js +3 -2
  81. package/dist/molecules/jl-steps.css_raw.js +1 -1
  82. package/dist/molecules/jl-steps.d.ts +2 -0
  83. package/dist/molecules/jl-steps.js +30 -14
  84. package/dist/molecules/jl-tabs.js +3 -2
  85. package/dist/molecules/jl-tag-input.js +3 -3
  86. package/dist/molecules/jl-timeline.js +1 -1
  87. package/dist/molecules/jl-toast.css_raw.js +1 -1
  88. package/dist/molecules/jl-toast.d.ts +13 -1
  89. package/dist/molecules/jl-toast.js +23 -3
  90. package/dist/molecules/jl-tooltip.d.ts +4 -1
  91. package/dist/molecules/jl-tooltip.js +27 -15
  92. package/dist/organisms/jl-command-palette.css_raw.js +1 -1
  93. package/dist/organisms/jl-command-palette.d.ts +14 -0
  94. package/dist/organisms/jl-command-palette.js +40 -10
  95. package/dist/organisms/jl-confirm.d.ts +2 -1
  96. package/dist/organisms/jl-confirm.js +18 -12
  97. package/dist/organisms/jl-dialog.js +2 -1
  98. package/dist/organisms/jl-drawer.js +2 -1
  99. package/dist/organisms/jl-flow.js +13 -13
  100. package/dist/organisms/jl-navbar.js +6 -6
  101. package/dist/organisms/jl-side-nav.css_raw.js +1 -1
  102. package/dist/organisms/jl-side-nav.d.ts +33 -1
  103. package/dist/organisms/jl-side-nav.js +71 -14
  104. package/dist/organisms/jl-tab-bar.js +3 -3
  105. package/dist/organisms/jl-table.css_raw.js +1 -1
  106. package/dist/organisms/jl-table.d.ts +9 -0
  107. package/dist/organisms/jl-table.js +36 -6
  108. package/dist/organisms/jl-toaster.css_raw.js +1 -1
  109. package/dist/organisms/jl-toaster.d.ts +9 -1
  110. package/dist/organisms/jl-toaster.js +148 -7
  111. package/dist/organisms/jl-tree.css_raw.js +1 -1
  112. package/dist/organisms/jl-tree.d.ts +71 -4
  113. package/dist/organisms/jl-tree.js +303 -40
  114. package/dist/styles/tokens.css_raw.js +1 -1
  115. package/dist/templates/jl-app-shell.css_raw.js +1 -1
  116. package/dist/templates/jl-app-shell.d.ts +16 -1
  117. package/dist/templates/jl-app-shell.js +42 -3
  118. package/dist/templates/jl-auth-layout.css_raw.js +3 -0
  119. package/dist/templates/jl-auth-layout.d.ts +35 -0
  120. package/dist/templates/jl-auth-layout.js +139 -0
  121. package/dist/templates/jl-page.css_raw.js +1 -1
  122. package/dist/templates/jl-page.js +3 -3
  123. package/dist/templates/jl-split-view.css_raw.js +1 -1
  124. package/dist/templates/jl-split-view.d.ts +21 -1
  125. package/dist/templates/jl-split-view.js +37 -4
  126. package/dist/tokens.css +2 -0
  127. package/dist/types/jl-app-icon.types.d.ts +5 -0
  128. package/dist/types/jl-command-palette.types.d.ts +7 -0
  129. package/dist/types/jl-facts.types.d.ts +17 -0
  130. package/dist/types/jl-side-nav.types.d.ts +2 -0
  131. package/dist/types/jl-split-view.types.d.ts +2 -0
  132. package/dist/types/jl-steps.types.d.ts +2 -0
  133. package/dist/types/jl-table.types.d.ts +9 -2
  134. package/dist/types/jl-toaster.types.d.ts +42 -1
  135. package/dist/types/jl-tree.types.d.ts +26 -0
  136. package/dist/types/texts.types.d.ts +5 -0
  137. package/package.json +3 -3
package/dist/lib/time.js CHANGED
@@ -7,8 +7,7 @@ var HOUR_S = 3600;
7
7
  var DAY_S = HOURS_PER_DAY * HOUR_S;
8
8
  var NOW_UNDER_S = 45;
9
9
  var MINUTES_UNDER_S = 2700;
10
- var HOURS_UNDER_S = 22 * HOUR_S;
11
- var DAYS_UNDER_S = 7 * DAY_S;
10
+ var DAYS_UNDER = 7;
12
11
  /** A number of one unit, as the language writes it short: "33 secs", "3 min", "2 Std.". */
13
12
  var unitText = (value, unit, locale) => new Intl.NumberFormat(locale, {
14
13
  maximumFractionDigits: 0,
@@ -27,34 +26,43 @@ function pair(first, second, locale) {
27
26
  }
28
27
  /**
29
28
  * A duration in the units that read best: "< 1 sec" under a second (never "0.0 s"), then seconds, minutes, hours
30
- * and minutes, days and hours.
29
+ * and minutes, days and hours. The unit is chosen once rounded: 59.6 seconds is "1 min", not "60 secs". Not a
30
+ * number gives "".
31
31
  */
32
32
  function formatDuration(milliseconds, locale) {
33
+ if (!Number.isFinite(milliseconds)) return "";
33
34
  const seconds = Math.max(0, milliseconds) / SECOND_MS;
34
35
  if (seconds < 1) return `< ${unitText(1, "second", locale)}`;
35
- if (seconds < MINUTE_S) return unitText(Math.round(seconds), "second", locale);
36
- if (seconds < HOUR_S) return unitText(Math.round(seconds / MINUTE_S), "minute", locale);
37
- if (seconds < DAY_S) {
38
- const minutes = Math.round(seconds / MINUTE_S);
39
- return pair([Math.floor(minutes / MINUTES_PER_HOUR), "hour"], [minutes % MINUTES_PER_HOUR, "minute"], locale);
40
- }
36
+ const wholeSeconds = Math.round(seconds);
37
+ if (wholeSeconds < MINUTE_S) return unitText(wholeSeconds, "second", locale);
38
+ const minutes = Math.round(seconds / MINUTE_S);
39
+ if (minutes < MINUTES_PER_HOUR) return unitText(minutes, "minute", locale);
40
+ if (minutes < 1440) return pair([Math.floor(minutes / MINUTES_PER_HOUR), "hour"], [minutes % MINUTES_PER_HOUR, "minute"], locale);
41
41
  const hours = Math.round(seconds / HOUR_S);
42
42
  return pair([Math.floor(hours / HOURS_PER_DAY), "day"], [hours % HOURS_PER_DAY, "hour"], locale);
43
43
  }
44
+ /** Midnight of a date's day, in the reader's time zone. */
45
+ var startOfDay = (date) => new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
46
+ /** How many days on the calendar from `now` to `then`: -1 is yesterday, whatever the hours. */
47
+ var calendarDays = (then, now) => Math.round((startOfDay(then) - startOfDay(now)) / (DAY_S * SECOND_MS));
44
48
  /**
45
- * When something happened, from `now`: "now", "5 minutes ago", "3 hours ago", "yesterday" within a week, then the
46
- * date ("12 Mar", with the year when it is not this year's). Times to come read the same way: "in 5 minutes".
49
+ * When something happened, from `now`: "now", "5 minutes ago", "3 hours ago" the same day, "yesterday" or "3 days
50
+ * ago" by the calendar within a week, then the date ("12 Mar", with the year when it is not this year's). Times to
51
+ * come read the same way: "in 5 minutes". A date that is not one gives "".
47
52
  */
48
53
  function formatRelativeTime(date, now, locale) {
49
54
  const then = new Date(date);
50
- const seconds = (then.getTime() - new Date(now).getTime()) / SECOND_MS;
55
+ const today = new Date(now);
56
+ if (Number.isNaN(then.getTime()) || Number.isNaN(today.getTime())) return "";
57
+ const seconds = (then.getTime() - today.getTime()) / SECOND_MS;
51
58
  const distance = Math.abs(seconds);
52
59
  const relative = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
53
60
  if (distance < NOW_UNDER_S) return relative.format(0, "second");
54
61
  if (distance < MINUTES_UNDER_S) return relative.format(Math.round(seconds / MINUTE_S), "minute");
55
- if (distance < HOURS_UNDER_S) return relative.format(Math.round(seconds / HOUR_S), "hour");
56
- if (distance < DAYS_UNDER_S) return relative.format(Math.round(seconds / DAY_S), "day");
57
- const sameYear = then.getFullYear() === new Date(now).getFullYear();
62
+ const days = calendarDays(then, today);
63
+ if (days === 0) return relative.format(Math.round(seconds / HOUR_S), "hour");
64
+ if (Math.abs(days) < DAYS_UNDER) return relative.format(days, "day");
65
+ const sameYear = then.getFullYear() === today.getFullYear();
58
66
  return new Intl.DateTimeFormat(locale, {
59
67
  day: "numeric",
60
68
  month: "short",
@@ -1,6 +1,15 @@
1
1
  import type { TreeAction, TreeItem, VisibleTreeItem } from '../types/jl-tree.types';
2
- /** An item with `children`, even empty, is a folder. */
2
+ /** An item with `children`, even empty, is a folder; so is a `lazy` one, whose children are not loaded yet. */
3
3
  export declare const isFolder: (item: TreeItem) => boolean;
4
+ /** The items of a level: the roots when `parent` is null, otherwise that folder's children (none when unknown). */
5
+ export declare function childrenOf(items: readonly TreeItem[], parent: string | null): readonly TreeItem[];
6
+ /** The tree with a page of items added at the end of a level: the roots when `parent` is null. */
7
+ export declare function withPage(items: readonly TreeItem[], parent: string | null, page: readonly TreeItem[]): TreeItem[];
8
+ /**
9
+ * The tree with one more row at the end of the levels that have one (`extraRow` gives its id, or null): the row
10
+ * that loads a level's next page, or loads it again after an error. The keyboard then reaches it as an item.
11
+ */
12
+ export declare function withExtraRows(items: readonly TreeItem[], extraRow: (parent: string | null) => string | null, parent?: string | null): TreeItem[];
4
13
  /** The items a person sees, in order: the roots, and the children of every open folder, with their depth. */
5
14
  export declare function visibleItems(items: readonly TreeItem[], expanded: ReadonlySet<string>, level?: number, parent?: string | null): VisibleTreeItem[];
6
15
  /** Every item of the tree with its parent, open or not. */
package/dist/lib/tree.js CHANGED
@@ -1,6 +1,40 @@
1
1
  // src/lib/tree.ts
2
- /** An item with `children`, even empty, is a folder. */
3
- var isFolder = (item) => Array.isArray(item.children);
2
+ /** An item with `children`, even empty, is a folder; so is a `lazy` one, whose children are not loaded yet. */
3
+ var isFolder = (item) => Array.isArray(item.children) || item.lazy === true;
4
+ /** The items of a level: the roots when `parent` is null, otherwise that folder's children (none when unknown). */
5
+ function childrenOf(items, parent) {
6
+ if (parent === null) return items;
7
+ return allItems(items).find((entry) => entry.item.id === parent)?.item.children ?? [];
8
+ }
9
+ /** The tree with a page of items added at the end of a level: the roots when `parent` is null. */
10
+ function withPage(items, parent, page) {
11
+ if (parent === null) return [...items, ...page];
12
+ return items.map((item) => {
13
+ if (item.id === parent) return {
14
+ ...item,
15
+ children: [...item.children ?? [], ...page]
16
+ };
17
+ return item.children === void 0 ? item : {
18
+ ...item,
19
+ children: withPage(item.children, parent, page)
20
+ };
21
+ });
22
+ }
23
+ /**
24
+ * The tree with one more row at the end of the levels that have one (`extraRow` gives its id, or null): the row
25
+ * that loads a level's next page, or loads it again after an error. The keyboard then reaches it as an item.
26
+ */
27
+ function withExtraRows(items, extraRow, parent = null) {
28
+ const own = items.map((item) => isFolder(item) ? {
29
+ ...item,
30
+ children: withExtraRows(item.children ?? [], extraRow, item.id)
31
+ } : item);
32
+ const extra = extraRow(parent);
33
+ return extra === null ? own : [...own, {
34
+ id: extra,
35
+ label: ""
36
+ }];
37
+ }
4
38
  /** The items a person sees, in order: the roots, and the children of every open folder, with their depth. */
5
39
  function visibleItems(items, expanded, level = 1, parent = null) {
6
40
  return items.flatMap((item) => {
@@ -86,4 +120,4 @@ function treeKeyAction(items, expanded, focused, key) {
86
120
  const index = visible.findIndex((entry) => entry.item.id === focused);
87
121
  return handler(visible, Math.max(0, index), expanded);
88
122
  }
89
- export { allItems, isFolder, treeKeyAction, visibleItems, withExpanded };
123
+ export { allItems, childrenOf, isFolder, treeKeyAction, visibleItems, withExpanded, withExtraRows, withPage };
@@ -1,6 +1,6 @@
1
1
  // src/lib/trigger.ts
2
2
  var NATIVE_FOCUSABLE = "a[href], button, input, select, textarea, summary, [tabindex]:not([tabindex=\"-1\"]), [contenteditable=\"true\"]";
3
- var BASE_PART = "[part~=\"base\"]";
3
+ var BASE_PART = "[part~=\"base\"]:not([hidden])";
4
4
  /**
5
5
  * The element that carries a trigger's accessible state: the inner `base` part of a component (where its focus
6
6
  * and its name are), or else the element itself.
@@ -1,3 +1,3 @@
1
1
  // src/molecules/jl-account-button.css?raw
2
- var jl_account_button_default = ":host {\n display: block;\n}\n\n.base {\n display: flex;\n width: 100%;\n padding: 6px 8px;\n border: 0;\n border-radius: var(--jl-radius-sm);\n background: transparent;\n color: var(--jl-text);\n text-align: left;\n text-decoration: none;\n cursor: pointer;\n transition: background var(--jl-fast) var(--jl-ease);\n}\n\n.base:hover {\n background: var(--jl-surface-2);\n}\n\n.content {\n display: flex;\n flex: 1;\n align-items: center;\n gap: 10px;\n min-width: 0;\n}\n\njl-avatar {\n flex: none;\n}\n\n.text {\n display: grid;\n min-width: 0;\n line-height: 1.3;\n}\n\n.name,\n.description {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.name {\n font-size: var(--jl-font-sm);\n font-weight: 650;\n}\n\n.description {\n color: var(--jl-muted);\n font-size: var(--jl-font-xs);\n}\n\n/* The avatar alone, round. */\n:host([compact]) {\n display: inline-block;\n}\n\n:host([compact]) .base {\n width: auto;\n padding: 2px;\n border-radius: var(--jl-radius-pill);\n}\n\n:host([compact]) .text {\n display: none;\n}\n";
2
+ var jl_account_button_default = ":host {\n display: block;\n}\n\n.base {\n display: flex;\n width: 100%;\n padding: 6px 8px;\n border: 0;\n border-radius: var(--jl-radius-sm);\n background: transparent;\n color: var(--jl-text);\n text-align: left;\n text-decoration: none;\n cursor: pointer;\n transition: background var(--jl-fast) var(--jl-ease);\n}\n\n.base:hover {\n background: var(--jl-surface-2);\n}\n\n.content {\n display: flex;\n flex: 1;\n align-items: center;\n gap: 10px;\n min-width: 0;\n}\n\njl-avatar {\n flex: none;\n}\n\n.text {\n display: grid;\n min-width: 0;\n line-height: 1.3;\n}\n\n.name,\n.description {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.name {\n font-size: var(--jl-font-sm);\n font-weight: 650;\n}\n\n.description {\n color: var(--jl-muted);\n font-size: var(--jl-font-xs);\n}\n\n/* The avatar alone, round. */\n:host([compact]) {\n display: inline-block;\n}\n\n:host([compact]) .base {\n width: auto;\n padding: 2px;\n border-radius: var(--jl-radius-pill);\n}\n\n:host([compact]) .text {\n display: none;\n}\n\n/* Their account page is the one shown: marked as the side nav marks its current place. */\n\n:host([current]) .base {\n background: var(--jl-accent-soft);\n}\n\n:host([current]) .name {\n color: var(--jl-accent);\n}\n\n:host([current][compact]) .base {\n box-shadow: 0 0 0 2px var(--jl-accent);\n}\n";
3
3
  export { jl_account_button_default as default };
@@ -2,7 +2,8 @@ import { JlElement } from '../base/jl-element';
2
2
  /**
3
3
  * The person signed in, where the frame of an app shows them (the foot of the side nav, a phone's top bar): their
4
4
  * avatar, their name, and a line under it such as their role. With an `href` it is a link to their account, else a
5
- * button that fires `press` (to open a menu). `compact` keeps the avatar alone, the name as its tooltip.
5
+ * button that fires `press` (to open a menu). `compact` keeps the avatar alone, the name as its tooltip; `current`
6
+ * marks it while their account page is the one shown.
6
7
  */
7
8
  export declare class JlAccountButton extends JlElement {
8
9
  static readonly selector = "jl-account-button";
@@ -33,6 +34,11 @@ export declare class JlAccountButton extends JlElement {
33
34
  get(): boolean;
34
35
  set(setter: boolean | ((prevState: boolean) => boolean)): void;
35
36
  }>>;
37
+ /** Their account page is the one shown: marked as the side nav marks its current place. */
38
+ readonly current: Readonly<Readonly<{
39
+ get(): boolean;
40
+ set(setter: boolean | ((prevState: boolean) => boolean)): void;
41
+ }>>;
36
42
  private readonly _refs;
37
43
  focus(options?: FocusOptions): void;
38
44
  templateHtml(): Node;
@@ -41,6 +47,7 @@ export declare class JlAccountButton extends JlElement {
41
47
  private control;
42
48
  /** Moves the content into the link or back into the button. */
43
49
  private renderLink;
50
+ private renderCurrent;
44
51
  /** Its name for screen readers and its tooltip: the name, then the line under it. */
45
52
  private renderLabel;
46
53
  }
@@ -9,7 +9,8 @@ import { Fragment, jsx, jsxs } from "@jadis/core/jsx-runtime";
9
9
  /**
10
10
  * The person signed in, where the frame of an app shows them (the foot of the side nav, a phone's top bar): their
11
11
  * avatar, their name, and a line under it such as their role. With an `href` it is a link to their account, else a
12
- * button that fires `press` (to open a menu). `compact` keeps the avatar alone, the name as its tooltip.
12
+ * button that fires `press` (to open a menu). `compact` keeps the avatar alone, the name as its tooltip; `current`
13
+ * marks it while their account page is the one shown.
13
14
  */
14
15
  var JlAccountButton = class extends JlElement {
15
16
  static selector = "jl-account-button";
@@ -31,8 +32,10 @@ var JlAccountButton = class extends JlElement {
31
32
  href = this.useChange("", () => this.renderLink(), { attribute: "href" });
32
33
  /** The avatar alone. */
33
34
  compact = this.useChange(false, (value) => this.reflect("compact", value), { attribute: "compact" });
35
+ /** Their account page is the one shown: marked as the side nav marks its current place. */
36
+ current = this.useChange(false, (value) => this.renderCurrent(value), { attribute: "current" });
34
37
  _refs = this.useRefs((ref) => ({
35
- avatar: ref("jl-avatar"),
38
+ avatar: ref(JlAvatar),
36
39
  button: ref("button"),
37
40
  content: ref(".content"),
38
41
  description: ref(".description"),
@@ -84,6 +87,10 @@ var JlAccountButton = class extends JlElement {
84
87
  link.hidden = href === "";
85
88
  setOrRemove(link, "href", href);
86
89
  }
90
+ renderCurrent(current) {
91
+ this.reflect("current", current);
92
+ for (const control of [this._refs.button, this._refs.link]) setOrRemove(control, "aria-current", current ? "page" : "");
93
+ }
87
94
  /** Its name for screen readers and its tooltip: the name, then the line under it. */
88
95
  renderLabel() {
89
96
  const label = [this.name.get(), this.description.get()].filter((part) => part !== "").join(", ");
@@ -3,8 +3,9 @@ import { JlElement } from "../base/jl-element.js";
3
3
  import { oneOf } from "../lib/attributes.js";
4
4
  import { registerIcons } from "../lib/icons.js";
5
5
  import { TONES, TONE_ICONS, TONE_ICON_FILLED } from "../lib/options.js";
6
- import { hasSlotted } from "../lib/slots.js";
6
+ import { jlText } from "../lib/texts.js";
7
7
  import { JlIcon } from "../atoms/jl-icon.js";
8
+ import { hasSlotted } from "../lib/slots.js";
8
9
  import { JlButton } from "../atoms/jl-button.js";
9
10
  import jl_alert_default from "./jl-alert.css_raw.js";
10
11
  import { css } from "@jadis/core";
@@ -115,7 +116,7 @@ var JlAlert = class extends JlElement {
115
116
  class: "close",
116
117
  variant: "ghost",
117
118
  icon: "x",
118
- hint: "Dismiss",
119
+ hint: jlText("dismiss"),
119
120
  size: "sm",
120
121
  hidden: true
121
122
  })
@@ -2,6 +2,7 @@ import { BASE_STYLES } from "../styles/shared.js";
2
2
  import { JlElement } from "../base/jl-element.js";
3
3
  import { jsonOf } from "../lib/attributes.js";
4
4
  import { registerIcons } from "../lib/icons.js";
5
+ import { jlText } from "../lib/texts.js";
5
6
  import { JlIcon } from "../atoms/jl-icon.js";
6
7
  import jl_breadcrumb_default from "./jl-breadcrumb.css_raw.js";
7
8
  import { css } from "@jadis/core";
@@ -14,7 +15,6 @@ registerIcons({
14
15
  });
15
16
  var ICON_SIZE = 15;
16
17
  var SEPARATOR_SIZE = 14;
17
- var DEFAULT_LABEL = "Breadcrumb";
18
18
  var MIN_MAX = 2;
19
19
  /** The crumbs shown: all of them, or the first, a gap for the middle, and the last `max - 1`. */
20
20
  function visibleCrumbs(items, max, expanded) {
@@ -43,8 +43,8 @@ var JlBreadcrumb = class extends JlElement {
43
43
  /** The most items shown before the middle folds; 0 never folds. */
44
44
  max = this.useChange(0, () => this.render(), { attribute: "max" });
45
45
  /** The trail's name for screen readers. */
46
- label = this.useChange(DEFAULT_LABEL, (value) => {
47
- this._refs.nav.setAttribute("aria-label", value || DEFAULT_LABEL);
46
+ label = this.useChange(jlText("breadcrumb"), (value) => {
47
+ this._refs.nav.setAttribute("aria-label", value || jlText("breadcrumb"));
48
48
  }, { attribute: "label" });
49
49
  _state = { expanded: false };
50
50
  _refs = this.useRefs((ref) => ({
@@ -3,8 +3,9 @@ import { JlFormControl } from "../base/jl-form-control.js";
3
3
  import { clampedNumber, oneOf } from "../lib/attributes.js";
4
4
  import { registerIcons } from "../lib/icons.js";
5
5
  import { CONTROL_SIZES } from "../lib/options.js";
6
- import { setOrRemove, uniqueId } from "../lib/dom.js";
6
+ import { jlText } from "../lib/texts.js";
7
7
  import { JlIcon } from "../atoms/jl-icon.js";
8
+ import { setOrRemove, uniqueId } from "../lib/dom.js";
8
9
  import { addMonths, calendarDateOf, clampDate, dateAfterKey, dateFormatter, formatMonthTitle, isSameDate, isSameMonth, isWithin, monthGrid, monthHasDaysWithin, parseIsoDate, toIsoDate, weekdayNames } from "../lib/calendar.js";
9
10
  import { pageLocale } from "../lib/locale.js";
10
11
  import jl_calendar_default from "./jl-calendar.css_raw.js";
@@ -21,7 +22,6 @@ var MONDAY = 1;
21
22
  var SUNDAY = 0;
22
23
  var SATURDAY = 6;
23
24
  var SELECT_KEYS = ["Enter", " "];
24
- var VALUE_MISSING = "Choose a date.";
25
25
  var FULL_DATE = { dateStyle: "full" };
26
26
  /** The first day of the week from its attribute: 0 (Sunday) to 6 (Saturday), Monday when absent. */
27
27
  var parseWeekday = (value) => Math.round(clampedNumber(MONDAY, SUNDAY, SATURDAY)(value));
@@ -95,7 +95,7 @@ var JlCalendar = class extends JlFormControl {
95
95
  /* @__PURE__ */ jsx("button", {
96
96
  class: "nav previous",
97
97
  type: "button",
98
- "aria-label": "Previous month",
98
+ "aria-label": jlText("previousMonth"),
99
99
  children: /* @__PURE__ */ jsx(JlIcon, {
100
100
  name: "chevron-left",
101
101
  size: NAV_ICON_SIZE
@@ -109,7 +109,7 @@ var JlCalendar = class extends JlFormControl {
109
109
  /* @__PURE__ */ jsx("button", {
110
110
  class: "nav next",
111
111
  type: "button",
112
- "aria-label": "Next month",
112
+ "aria-label": jlText("nextMonth"),
113
113
  children: /* @__PURE__ */ jsx(JlIcon, {
114
114
  name: "chevron-right",
115
115
  size: NAV_ICON_SIZE
@@ -299,7 +299,7 @@ var JlCalendar = class extends JlFormControl {
299
299
  }
300
300
  renderValidity() {
301
301
  if (this.required.get() && parseIsoDate(this.value.get()) === null) {
302
- this.internals.setValidity({ valueMissing: true }, VALUE_MISSING);
302
+ this.internals.setValidity({ valueMissing: true }, jlText("chooseDateMissing"));
303
303
  return;
304
304
  }
305
305
  this.internals.setValidity({});
@@ -3,8 +3,9 @@ import { JlFormControl } from "../base/jl-form-control.js";
3
3
  import { jsonOf, oneOf, stringList } from "../lib/attributes.js";
4
4
  import { registerIcons } from "../lib/icons.js";
5
5
  import { CONTROL_SIZES } from "../lib/options.js";
6
- import { setOrRemove } from "../lib/dom.js";
6
+ import { jlText } from "../lib/texts.js";
7
7
  import { JlIcon } from "../atoms/jl-icon.js";
8
+ import { setOrRemove } from "../lib/dom.js";
8
9
  import { rovingIndex } from "../lib/roving-index.js";
9
10
  import jl_chip_group_default from "./jl-chip-group.css_raw.js";
10
11
  import { css } from "@jadis/core";
@@ -19,7 +20,6 @@ var ICON_SIZES = {
19
20
  };
20
21
  var SELECTED_ICON = "check";
21
22
  var CHIP_SELECTOR = ".chip";
22
- var VALUE_MISSING = "Please select one of these options.";
23
23
  /** The keys that move between the chips; with one choice, the choice moves too, as with native radios. */
24
24
  var MOVES = /* @__PURE__ */ new Map([
25
25
  ["ArrowDown", "next"],
@@ -252,7 +252,7 @@ var JlChipGroup = class extends JlFormControl {
252
252
  this.internals.setValidity({});
253
253
  return;
254
254
  }
255
- this.internals.setValidity({ valueMissing: true }, VALUE_MISSING, this.chips()[0] ?? this._refs.group);
255
+ this.internals.setValidity({ valueMissing: true }, jlText("selectOption"), this.chips()[0] ?? this._refs.group);
256
256
  }
257
257
  };
258
258
  JlChipGroup.register();
@@ -1,3 +1,3 @@
1
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";
2
+ var jl_code_block_default = ":host {\n display: block;\n min-width: 0;\n --max-height: none;\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 max-height: var(--max-height);\n overflow-y: auto;\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
3
  export { jl_code_block_default as default };
@@ -33,6 +33,11 @@ export declare class JlCodeBlock extends JlElement {
33
33
  get(): boolean;
34
34
  set(setter: boolean | ((prevState: boolean) => boolean)): void;
35
35
  }>>;
36
+ /** A CSS height past which the code scrolls under its header: `320px`, `50vh`; a number is pixels. */
37
+ readonly maxHeight: Readonly<Readonly<{
38
+ get(): string;
39
+ set(setter: string | ((prevState: string) => string)): void;
40
+ }>>;
36
41
  private readonly _refs;
37
42
  templateHtml(): Node;
38
43
  templateCss(): string;
@@ -1,8 +1,9 @@
1
1
  import { BASE_STYLES } from "../styles/shared.js";
2
2
  import { JlElement } from "../base/jl-element.js";
3
3
  import { oneOf } from "../lib/attributes.js";
4
- import { highlightedNodes } from "../lib/highlight.js";
5
4
  import { dedent } from "../lib/text.js";
5
+ import { cssLength } from "../lib/css-length.js";
6
+ import { highlightedNodes } from "../lib/highlight.js";
6
7
  import jl_code_block_default from "./jl-code-block.css_raw.js";
7
8
  import { JlCopyButton } from "./jl-copy-button.js";
8
9
  import { css } from "@jadis/core";
@@ -41,9 +42,11 @@ var JlCodeBlock = class extends JlElement {
41
42
  }, { attribute: "hide-copy" });
42
43
  /** Wraps long lines instead of scrolling sideways. */
43
44
  wrap = this.useChange(false, (value) => this.reflect("wrap", value), { attribute: "wrap" });
45
+ /** A CSS height past which the code scrolls under its header: `320px`, `50vh`; a number is pixels. */
46
+ maxHeight = this.useChange("", (value) => this.style.setProperty("--max-height", value.trim() === "" ? "none" : cssLength(value)), { attribute: "max-height" });
44
47
  _refs = this.useRefs((ref) => ({
45
48
  code: ref("code"),
46
- copy: ref("jl-copy-button"),
49
+ copy: ref(JlCopyButton),
47
50
  gutter: ref(".gutter"),
48
51
  header: ref("header"),
49
52
  heading: ref(".heading"),
@@ -4,8 +4,9 @@ import { jsonOf, oneOf } from "../lib/attributes.js";
4
4
  import { followAnchor } from "../lib/floating.js";
5
5
  import { registerIcons } from "../lib/icons.js";
6
6
  import { CONTROL_SIZES } from "../lib/options.js";
7
- import { setOrRemove, uniqueId } from "../lib/dom.js";
7
+ import { jlText } from "../lib/texts.js";
8
8
  import { JlIcon } from "../atoms/jl-icon.js";
9
+ import { setOrRemove, uniqueId } from "../lib/dom.js";
9
10
  import { keepInView } from "../lib/keep-in-view.js";
10
11
  import { edgeEnabledIndex, filterOptions, foldText, matchParts, nextEnabledIndex } from "../lib/option-filter.js";
11
12
  import jl_combobox_default from "./jl-combobox.css_raw.js";
@@ -23,8 +24,6 @@ var ACTION_ICON_SIZE = 14;
23
24
  var CHECK_SIZE = 14;
24
25
  var NONE = -1;
25
26
  var PLACEMENT = "bottom-start";
26
- var VALUE_MISSING = "Choose an option.";
27
- var DEFAULT_EMPTY_TEXT = "No results";
28
27
  /** A JSON attribute that should hold a list of options; anything else gives none. */
29
28
  var parseOptions = (value) => {
30
29
  const parsed = jsonOf([])(value);
@@ -57,7 +56,7 @@ var JlCombobox = class extends JlFormControl {
57
56
  /** Shows a button that empties the field while it holds text. */
58
57
  clearable = this.useChange(false, () => this.renderClear(), { attribute: "clearable" });
59
58
  /** What the list says when no option matches. */
60
- emptyText = this.useChange(DEFAULT_EMPTY_TEXT, (value) => {
59
+ emptyText = this.useChange(jlText("noResults"), (value) => {
61
60
  this._refs.empty.textContent = value;
62
61
  }, { attribute: "empty-text" });
63
62
  required = this.useChange(false, (value) => {
@@ -112,7 +111,7 @@ var JlCombobox = class extends JlFormControl {
112
111
  /* @__PURE__ */ jsx("button", {
113
112
  class: "action clear",
114
113
  type: "button",
115
- "aria-label": "Clear",
114
+ "aria-label": jlText("clear"),
116
115
  tabIndex: -1,
117
116
  hidden: true,
118
117
  children: /* @__PURE__ */ jsx(JlIcon, {
@@ -123,7 +122,7 @@ var JlCombobox = class extends JlFormControl {
123
122
  /* @__PURE__ */ jsx("button", {
124
123
  class: "action toggle",
125
124
  type: "button",
126
- "aria-label": "Show the options",
125
+ "aria-label": jlText("showOptions"),
127
126
  tabIndex: -1,
128
127
  children: /* @__PURE__ */ jsx(JlIcon, {
129
128
  name: "chevron-down",
@@ -429,7 +428,7 @@ var JlCombobox = class extends JlFormControl {
429
428
  }
430
429
  renderValidity() {
431
430
  if (this.required.get() && this.value.get() === "") {
432
- this.internals.setValidity({ valueMissing: true }, VALUE_MISSING, this._refs.native);
431
+ this.internals.setValidity({ valueMissing: true }, jlText("chooseOption"), this._refs.native);
433
432
  return;
434
433
  }
435
434
  this.internals.setValidity({});
@@ -3,6 +3,7 @@ import { JlElement } from "../base/jl-element.js";
3
3
  import { oneOf } from "../lib/attributes.js";
4
4
  import { registerIcons } from "../lib/icons.js";
5
5
  import { CONTROL_SIZES } from "../lib/options.js";
6
+ import { jlText } from "../lib/texts.js";
6
7
  import { JlButton } from "../atoms/jl-button.js";
7
8
  import jl_copy_button_default from "./jl-copy-button.css_raw.js";
8
9
  import { css } from "@jadis/core";
@@ -28,12 +29,12 @@ var JlCopyButton = class extends JlElement {
28
29
  parse: oneOf(CONTROL_SIZES, "sm")
29
30
  });
30
31
  _timer = { id: 0 };
31
- _refs = this.useRefs((ref) => ({ button: ref("jl-button") }));
32
+ _refs = this.useRefs((ref) => ({ button: ref(JlButton) }));
32
33
  templateHtml() {
33
34
  return /* @__PURE__ */ jsx(JlButton, {
34
35
  variant: "ghost",
35
36
  icon: COPY_ICON,
36
- hint: "Copy"
37
+ hint: jlText("copy")
37
38
  });
38
39
  }
39
40
  templateCss() {
@@ -55,12 +56,12 @@ var JlCopyButton = class extends JlElement {
55
56
  confirm() {
56
57
  const button = this._refs.button;
57
58
  button.icon.set(DONE_ICON);
58
- button.hint.set("Copied");
59
+ button.hint.set(jlText("copied"));
59
60
  this.reflect("copied", true);
60
61
  window.clearTimeout(this._timer.id);
61
62
  this._timer.id = window.setTimeout(() => {
62
63
  button.icon.set(COPY_ICON);
63
- button.hint.set("Copy");
64
+ button.hint.set(jlText("copy"));
64
65
  this.reflect("copied", false);
65
66
  }, CONFIRM_MS);
66
67
  }
@@ -4,8 +4,9 @@ import { clampedNumber, oneOf } from "../lib/attributes.js";
4
4
  import { followAnchor } from "../lib/floating.js";
5
5
  import { registerIcons } from "../lib/icons.js";
6
6
  import { CONTROL_SIZES } from "../lib/options.js";
7
- import { setOrRemove, uniqueId } from "../lib/dom.js";
7
+ import { jlText } from "../lib/texts.js";
8
8
  import { JlIcon } from "../atoms/jl-icon.js";
9
+ import { setOrRemove, uniqueId } from "../lib/dom.js";
9
10
  import { NUMERIC_DATE, compareDates, dateOrder, datePattern, formatDate, isDateText, parseIsoDate, parseTypedDate, toIsoDate } from "../lib/calendar.js";
10
11
  import { pageLocale } from "../lib/locale.js";
11
12
  import { JlCalendar } from "./jl-calendar.js";
@@ -24,8 +25,6 @@ var PLACEMENT = "bottom-start";
24
25
  var MONDAY = 1;
25
26
  var SUNDAY = 0;
26
27
  var SATURDAY = 6;
27
- var VALUE_MISSING = "Choose a date.";
28
- var DIALOG_LABEL = "Choose a date";
29
28
  var parseWeekday = (value) => Math.round(clampedNumber(MONDAY, SUNDAY, SATURDAY)(value));
30
29
  /**
31
30
  * A field for a date: it shows the date in digits, in the `locale`'s order (`25.10.2026`, `10/25/2026`), and opens a
@@ -81,7 +80,7 @@ var JlDatePicker = class extends JlFormControl {
81
80
  openOnPress: null
82
81
  };
83
82
  _refs = this.useRefs((ref) => ({
84
- calendar: ref("jl-calendar"),
83
+ calendar: ref(JlCalendar),
85
84
  clear: ref(".clear"),
86
85
  control: ref(".control"),
87
86
  native: ref("input"),
@@ -117,7 +116,7 @@ var JlDatePicker = class extends JlFormControl {
117
116
  /* @__PURE__ */ jsx("button", {
118
117
  class: "action clear",
119
118
  type: "button",
120
- "aria-label": "Clear",
119
+ "aria-label": jlText("clear"),
121
120
  tabIndex: -1,
122
121
  hidden: true,
123
122
  children: /* @__PURE__ */ jsx(JlIcon, {
@@ -128,7 +127,7 @@ var JlDatePicker = class extends JlFormControl {
128
127
  /* @__PURE__ */ jsx("button", {
129
128
  class: "action open",
130
129
  type: "button",
131
- "aria-label": DIALOG_LABEL,
130
+ "aria-label": jlText("chooseDate"),
132
131
  "aria-haspopup": "dialog",
133
132
  "aria-expanded": "false",
134
133
  "aria-controls": this._popupId,
@@ -143,7 +142,7 @@ var JlDatePicker = class extends JlFormControl {
143
142
  id: this._popupId,
144
143
  popover: "auto",
145
144
  role: "dialog",
146
- "aria-label": DIALOG_LABEL,
145
+ "aria-label": jlText("chooseDate"),
147
146
  attrs: { part: "popup" },
148
147
  children: /* @__PURE__ */ jsx(JlCalendar, { class: "calendar" })
149
148
  })] });
@@ -314,7 +313,7 @@ var JlDatePicker = class extends JlFormControl {
314
313
  }
315
314
  renderMissing(anchor) {
316
315
  if (this.required.get()) {
317
- this.internals.setValidity({ valueMissing: true }, VALUE_MISSING, anchor);
316
+ this.internals.setValidity({ valueMissing: true }, jlText("chooseDateMissing"), anchor);
318
317
  return;
319
318
  }
320
319
  this.internals.setValidity({});
@@ -2,8 +2,8 @@ import { BASE_STYLES } from "../styles/shared.js";
2
2
  import { JlElement } from "../base/jl-element.js";
3
3
  import { oneOf } from "../lib/attributes.js";
4
4
  import { registerIcons } from "../lib/icons.js";
5
- import { hasSlotted } from "../lib/slots.js";
6
5
  import { JlIcon } from "../atoms/jl-icon.js";
6
+ import { hasSlotted } from "../lib/slots.js";
7
7
  import jl_empty_default from "./jl-empty.css_raw.js";
8
8
  import { css } from "@jadis/core";
9
9
  import { Inbox } from "lucide";
@@ -43,7 +43,7 @@ var JlEmpty = class extends JlElement {
43
43
  actions: ref(".actions"),
44
44
  description: ref(".description"),
45
45
  heading: ref(".heading"),
46
- icon: ref("jl-icon"),
46
+ icon: ref(JlIcon),
47
47
  medallion: ref(".medallion"),
48
48
  slot: ref("slot")
49
49
  }));
@@ -0,0 +1,3 @@
1
+ // src/molecules/jl-facts.css?raw
2
+ var jl_facts_default = ":host {\n display: block;\n container-type: inline-size;\n}\n\n:host([empty]) {\n display: none;\n}\n\n.facts {\n display: grid;\n grid-template-columns: max-content minmax(0, 1fr);\n gap: 6px 24px;\n margin: 0;\n font-size: var(--jl-font-md);\n}\n\n/* Each pair is a row of the grid: the labels line up in one column, the values in the other. */\n.fact {\n display: contents;\n}\n\ndt {\n color: var(--jl-muted);\n}\n\ndd {\n min-width: 0;\n margin: 0;\n color: var(--jl-text);\n overflow-wrap: anywhere;\n}\n\ncode {\n font-family: var(--jl-font-mono);\n font-size: var(--jl-font-sm);\n word-break: break-all;\n}\n\na {\n color: var(--jl-accent);\n text-decoration: none;\n}\n\na:hover {\n color: var(--jl-accent-strong);\n text-decoration: underline;\n}\n\n:host([orientation=\"vertical\"]) .facts {\n grid-template-columns: repeat(auto-fill, minmax(min(100%, 180px), 1fr));\n gap: 16px 24px;\n}\n\n:host([orientation=\"vertical\"]) .fact {\n display: grid;\n gap: 2px;\n align-content: start;\n}\n\n@container (width < 420px) {\n .facts {\n grid-template-columns: minmax(0, 1fr);\n gap: 12px;\n }\n\n .fact {\n display: grid;\n gap: 2px;\n }\n}\n";
3
+ export { jl_facts_default as default };
@@ -0,0 +1,35 @@
1
+ import { JlElement } from '../base/jl-element';
2
+ import type { Fact, FactRenderer, FactsOrientation } from '../types/jl-facts.types';
3
+ export declare const FACTS_ORIENTATIONS: readonly FactsOrientation[];
4
+ /**
5
+ * The facts about one thing, label and value: an object's size and digest, an order's customer and date. Facts
6
+ * without a value are left out, and an element with none takes no room. Values can be code (monospace, broken
7
+ * anywhere), links, or drawn by code (`renderers`, by a fact's `key`). Narrower than 420 pixels, labels go over
8
+ * their values.
9
+ */
10
+ export declare class JlFacts extends JlElement {
11
+ static readonly selector = "jl-facts";
12
+ readonly items: Readonly<Readonly<{
13
+ get(): Fact[];
14
+ set(setter: Fact[] | ((prevState: Fact[]) => Fact[])): void;
15
+ }>>;
16
+ readonly orientation: Readonly<Readonly<{
17
+ get(): FactsOrientation;
18
+ set(setter: ((prevState: FactsOrientation) => FactsOrientation) | FactsOrientation): void;
19
+ }>>;
20
+ /** Draws the values of some facts by code, by their `key`: `{ type: (fact) => <JlBadge label={…} /> }`. */
21
+ readonly renderers: Readonly<Readonly<{
22
+ get(): Record<string, FactRenderer>;
23
+ set(setter: Record<string, FactRenderer> | ((prevState: Record<string, FactRenderer>) => Record<string, FactRenderer>)): void;
24
+ }>>;
25
+ /** The list's name for screen readers. */
26
+ readonly label: Readonly<Readonly<{
27
+ get(): string;
28
+ set(setter: string | ((prevState: string) => string)): void;
29
+ }>>;
30
+ private readonly _refs;
31
+ templateHtml(): Node;
32
+ templateCss(): string;
33
+ private render;
34
+ private valueNode;
35
+ }