@syware/jlib 0.6.0 → 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 (114) hide show
  1. package/dist/all.js +9 -4
  2. package/dist/atoms/jl-app-icon.d.ts +5 -0
  3. package/dist/atoms/jl-app-icon.js +26 -4
  4. package/dist/atoms/jl-badge.js +1 -1
  5. package/dist/atoms/jl-button.d.ts +5 -0
  6. package/dist/atoms/jl-button.js +4 -1
  7. package/dist/atoms/jl-chip.js +3 -2
  8. package/dist/atoms/jl-input.js +4 -3
  9. package/dist/atoms/jl-link.js +1 -1
  10. package/dist/atoms/jl-rating.js +4 -5
  11. package/dist/atoms/jl-select.css_raw.js +1 -1
  12. package/dist/atoms/jl-select.d.ts +3 -2
  13. package/dist/atoms/jl-select.js +5 -4
  14. package/dist/atoms/jl-spinner.js +2 -2
  15. package/dist/base/jl-form-control.js +2 -1
  16. package/dist/index.d.ts +8 -0
  17. package/dist/index.js +8 -3
  18. package/dist/lib/app-icon.d.ts +8 -0
  19. package/dist/lib/app-icon.js +37 -0
  20. package/dist/lib/dom.d.ts +2 -0
  21. package/dist/lib/dom.js +3 -1
  22. package/dist/lib/download.d.ts +10 -0
  23. package/dist/lib/download.js +62 -0
  24. package/dist/lib/icons.d.ts +4 -0
  25. package/dist/lib/icons.js +7 -4
  26. package/dist/lib/input-list.d.ts +12 -0
  27. package/dist/lib/input-list.js +14 -0
  28. package/dist/lib/internals.d.ts +5 -0
  29. package/dist/lib/internals.js +18 -0
  30. package/dist/lib/palette.js +6 -0
  31. package/dist/lib/resize.d.ts +2 -0
  32. package/dist/lib/resize.js +10 -0
  33. package/dist/lib/table.d.ts +6 -1
  34. package/dist/lib/table.js +16 -1
  35. package/dist/lib/text.d.ts +2 -0
  36. package/dist/lib/text.js +4 -1
  37. package/dist/lib/texts.d.ts +97 -0
  38. package/dist/lib/texts.js +102 -0
  39. package/dist/lib/tree.d.ts +10 -1
  40. package/dist/lib/tree.js +37 -3
  41. package/dist/molecules/jl-account-button.js +1 -1
  42. package/dist/molecules/jl-alert.js +2 -1
  43. package/dist/molecules/jl-breadcrumb.js +3 -3
  44. package/dist/molecules/jl-calendar.js +4 -4
  45. package/dist/molecules/jl-chip-group.js +2 -2
  46. package/dist/molecules/jl-code-block.css_raw.js +1 -1
  47. package/dist/molecules/jl-code-block.d.ts +5 -0
  48. package/dist/molecules/jl-code-block.js +5 -2
  49. package/dist/molecules/jl-combobox.js +5 -6
  50. package/dist/molecules/jl-copy-button.js +5 -4
  51. package/dist/molecules/jl-date-picker.js +6 -7
  52. package/dist/molecules/jl-empty.js +1 -1
  53. package/dist/molecules/jl-facts.css_raw.js +3 -0
  54. package/dist/molecules/jl-facts.d.ts +35 -0
  55. package/dist/molecules/jl-facts.js +64 -0
  56. package/dist/molecules/jl-file-drop.d.ts +5 -0
  57. package/dist/molecules/jl-file-drop.js +17 -14
  58. package/dist/molecules/jl-input-list.css_raw.js +3 -0
  59. package/dist/molecules/jl-input-list.d.ts +79 -0
  60. package/dist/molecules/jl-input-list.js +177 -0
  61. package/dist/molecules/jl-load-more.css_raw.js +3 -0
  62. package/dist/molecules/jl-load-more.d.ts +41 -0
  63. package/dist/molecules/jl-load-more.js +62 -0
  64. package/dist/molecules/jl-load-state.js +6 -7
  65. package/dist/molecules/jl-number-input.js +4 -4
  66. package/dist/molecules/jl-pagination.js +5 -5
  67. package/dist/molecules/jl-radio-group.js +2 -2
  68. package/dist/molecules/jl-search-button.js +4 -4
  69. package/dist/molecules/jl-search.js +4 -4
  70. package/dist/molecules/jl-segmented.js +2 -1
  71. package/dist/molecules/jl-steps.css_raw.js +1 -1
  72. package/dist/molecules/jl-steps.d.ts +2 -0
  73. package/dist/molecules/jl-steps.js +30 -14
  74. package/dist/molecules/jl-tabs.js +2 -1
  75. package/dist/molecules/jl-tag-input.js +2 -2
  76. package/dist/molecules/jl-toast.css_raw.js +1 -1
  77. package/dist/molecules/jl-toast.d.ts +13 -1
  78. package/dist/molecules/jl-toast.js +23 -3
  79. package/dist/organisms/jl-command-palette.css_raw.js +1 -1
  80. package/dist/organisms/jl-command-palette.d.ts +14 -0
  81. package/dist/organisms/jl-command-palette.js +39 -9
  82. package/dist/organisms/jl-confirm.js +5 -7
  83. package/dist/organisms/jl-dialog.js +2 -1
  84. package/dist/organisms/jl-drawer.js +2 -1
  85. package/dist/organisms/jl-flow.js +12 -12
  86. package/dist/organisms/jl-navbar.js +6 -6
  87. package/dist/organisms/jl-side-nav.css_raw.js +1 -1
  88. package/dist/organisms/jl-side-nav.d.ts +33 -1
  89. package/dist/organisms/jl-side-nav.js +70 -13
  90. package/dist/organisms/jl-tab-bar.js +2 -2
  91. package/dist/organisms/jl-table.css_raw.js +1 -1
  92. package/dist/organisms/jl-table.d.ts +9 -0
  93. package/dist/organisms/jl-table.js +35 -5
  94. package/dist/organisms/jl-toaster.d.ts +9 -1
  95. package/dist/organisms/jl-toaster.js +148 -7
  96. package/dist/organisms/jl-tree.css_raw.js +1 -1
  97. package/dist/organisms/jl-tree.d.ts +71 -4
  98. package/dist/organisms/jl-tree.js +303 -40
  99. package/dist/templates/jl-app-shell.css_raw.js +1 -1
  100. package/dist/templates/jl-app-shell.d.ts +10 -0
  101. package/dist/templates/jl-app-shell.js +16 -2
  102. package/dist/templates/jl-auth-layout.js +1 -1
  103. package/dist/templates/jl-page.js +3 -3
  104. package/dist/templates/jl-split-view.js +5 -4
  105. package/dist/types/jl-app-icon.types.d.ts +5 -0
  106. package/dist/types/jl-command-palette.types.d.ts +7 -0
  107. package/dist/types/jl-facts.types.d.ts +17 -0
  108. package/dist/types/jl-side-nav.types.d.ts +2 -0
  109. package/dist/types/jl-steps.types.d.ts +2 -0
  110. package/dist/types/jl-table.types.d.ts +9 -2
  111. package/dist/types/jl-toaster.types.d.ts +42 -1
  112. package/dist/types/jl-tree.types.d.ts +26 -0
  113. package/dist/types/texts.types.d.ts +5 -0
  114. package/package.json +3 -3
package/dist/all.js CHANGED
@@ -8,9 +8,11 @@ import { PLACEMENTS, computePosition, followAnchor, placeFloating } from "./lib/
8
8
  import { COMMON_ICONS } from "./lib/icon-set.js";
9
9
  import { hasIcon, iconElement, iconNames, registerIcons } from "./lib/icons.js";
10
10
  import { CONTROL_SIZES, TONES, TONE_ICONS, TONE_ICON_FILLED } from "./lib/options.js";
11
+ import { cellOf } from "./lib/table.js";
11
12
  import { formatDuration, formatRelativeTime } from "./lib/time.js";
13
+ import { DEFAULT_TEXTS, jlText, setTexts } from "./lib/texts.js";
12
14
  import { JlIcon } from "./atoms/jl-icon.js";
13
- import { JlAppIcon } from "./atoms/jl-app-icon.js";
15
+ import { JlAppIcon, setFavicon } from "./atoms/jl-app-icon.js";
14
16
  import { AVATAR_SHAPES, AVATAR_SIZES, AVATAR_STATUSES, JlAvatar } from "./atoms/jl-avatar.js";
15
17
  import { JlBadge } from "./atoms/jl-badge.js";
16
18
  import { JlSpinner } from "./atoms/jl-spinner.js";
@@ -43,9 +45,12 @@ import { CODE_LANGUAGES, JlCodeBlock } from "./molecules/jl-code-block.js";
43
45
  import { JlCombobox } from "./molecules/jl-combobox.js";
44
46
  import { JlDatePicker } from "./molecules/jl-date-picker.js";
45
47
  import { EMPTY_SIZES, JlEmpty } from "./molecules/jl-empty.js";
48
+ import { FACTS_ORIENTATIONS, JlFacts } from "./molecules/jl-facts.js";
46
49
  import { FIELD_ORIENTATIONS, JlField } from "./molecules/jl-field.js";
47
50
  import { JlFileDrop } from "./molecules/jl-file-drop.js";
51
+ import { JlInputList } from "./molecules/jl-input-list.js";
48
52
  import { JlListItem } from "./molecules/jl-list-item.js";
53
+ import { JlLoadMore } from "./molecules/jl-load-more.js";
49
54
  import { JlLoadState, LOAD_SKELETONS, LOAD_STATES } from "./molecules/jl-load-state.js";
50
55
  import { JlMenu } from "./molecules/jl-menu.js";
51
56
  import { JlNumberInput } from "./molecules/jl-number-input.js";
@@ -73,7 +78,7 @@ import { JlPageHeader } from "./organisms/jl-page-header.js";
73
78
  import { JlSideNav } from "./organisms/jl-side-nav.js";
74
79
  import { JlTabBar } from "./organisms/jl-tab-bar.js";
75
80
  import { JlTable, TABLE_SORT_DIRECTIONS } from "./organisms/jl-table.js";
76
- import { JlToaster, TOASTER_PLACEMENTS, toast } from "./organisms/jl-toaster.js";
81
+ import { JlToaster, TOASTER_PLACEMENTS, download, downloadTexts, toast } from "./organisms/jl-toaster.js";
77
82
  import { JlTree } from "./organisms/jl-tree.js";
78
83
  import { JlAppShell } from "./templates/jl-app-shell.js";
79
84
  import { JlAuthLayout } from "./templates/jl-auth-layout.js";
@@ -81,5 +86,5 @@ import { JlPage, PAGE_WIDTHS } from "./templates/jl-page.js";
81
86
  import { JlSplitView, SPLIT_ORIENTATIONS, SPLIT_PANES } from "./templates/jl-split-view.js";
82
87
  // src/all.ts
83
88
  registerIcons(COMMON_ICONS);
84
- define(JlAccordion, JlAccordionItem, JlAccountButton, JlAlert, JlAppIcon, JlAppShell, JlAuthLayout, JlAvatar, JlAvatarGroup, JlBadge, JlBreadcrumb, JlButton, JlCalendar, JlCard, JlCheckbox, JlChip, JlChipGroup, JlCodeBlock, JlCombobox, JlCommandPalette, JlConfirm, JlCopyButton, JlDatePicker, JlDialog, JlDivider, JlDrawer, JlEmpty, JlField, JlFileDrop, JlFlow, JlIcon, JlInput, JlKbd, JlLink, JlListItem, JlLoadState, JlMenu, JlNavbar, JlNumberInput, JlPage, JlPageHeader, JlPagination, JlPopover, JlProgress, JlProgressRing, JlRadioGroup, JlRating, JlSearch, JlSearchButton, JlSegmented, JlSelect, JlSideNav, JlSkeleton, JlSlider, JlSpinner, JlSplitView, JlStat, JlSteps, JlSwitch, JlTabBar, JlTable, JlTabPanel, JlTabs, JlTagInput, JlTextarea, JlTimeline, JlToast, JlToaster, JlTooltip, JlTree);
85
- export { ACCORDION_VARIANTS, AVATAR_SHAPES, AVATAR_SIZES, AVATAR_STATUSES, BASE_STYLES, BUTTON_TYPES, BUTTON_VARIANTS, CARD_PADDINGS, CHIP_SIZES, CODE_LANGUAGES, COMMON_ICONS, CONFIRM_TONES, CONTROL_SIZES, CONTROL_STYLES, DIALOG_SIZES, DIVIDER_ALIGNS, DRAWER_PLACEMENTS, DRAWER_SIZES, EMPTY_SIZES, FIELD_ORIENTATIONS, FLOATING_STYLES, INPUT_TYPES, JlAccordion, JlAccordionItem, JlAccountButton, JlAlert, JlAppIcon, JlAppShell, JlAuthLayout, JlAvatar, JlAvatarGroup, JlBadge, JlBreadcrumb, JlButton, JlCalendar, JlCard, JlCheckbox, JlChip, JlChipGroup, JlCodeBlock, JlCombobox, JlCommandPalette, JlConfirm, JlCopyButton, JlDatePicker, JlDialog, JlDivider, JlDrawer, JlElement, JlEmpty, JlField, JlFileDrop, JlFlow, JlFormControl, JlIcon, JlInput, JlKbd, JlLink, JlListItem, JlLoadState, JlMenu, JlNavbar, JlNumberInput, JlPage, JlPageHeader, JlPagination, JlPopover, JlProgress, JlProgressRing, JlRadioGroup, JlRating, JlSearch, JlSearchButton, JlSegmented, JlSelect, JlSideNav, JlSkeleton, JlSlider, JlSpinner, JlSplitView, JlStat, JlSteps, JlSwitch, JlTabBar, JlTabPanel, JlTable, JlTabs, JlTagInput, JlTextarea, JlTimeline, JlToast, JlToaster, JlTooltip, JlTree, LINK_VARIANTS, LOAD_SKELETONS, LOAD_STATES, PAGE_WIDTHS, PLACEMENTS, PROGRESS_SIZES, PROGRESS_TONES, RADIO_GROUP_ORIENTATIONS, RADIO_GROUP_VARIANTS, SKELETON_SHAPES, SPLIT_ORIENTATIONS, SPLIT_PANES, STAT_TRENDS, STEPS_ORIENTATIONS, SWITCH_SIZES, TABLE_SORT_DIRECTIONS, TAB_VARIANTS, TOASTER_PLACEMENTS, TOAST_TONES, TOKEN_STYLES, TONES, TONE_ICONS, TONE_ICON_FILLED, adoptTokens, clampedNumber, computePosition, confirmDialog, define, followAnchor, formatDuration, formatRelativeTime, guardLeave, hasIcon, iconElement, iconNames, jsonOf, oneOf, placeFloating, registerIcons, toast };
89
+ define(JlAccordion, JlAccordionItem, JlAccountButton, JlAlert, JlAppIcon, JlAppShell, JlAuthLayout, JlAvatar, JlAvatarGroup, JlBadge, JlBreadcrumb, JlButton, JlCalendar, JlCard, JlCheckbox, JlChip, JlChipGroup, JlCodeBlock, JlCombobox, JlCommandPalette, JlConfirm, JlCopyButton, JlDatePicker, JlDialog, JlDivider, JlDrawer, JlEmpty, JlFacts, JlField, JlFileDrop, JlFlow, JlIcon, JlInput, JlInputList, JlKbd, JlLink, JlListItem, JlLoadMore, JlLoadState, JlMenu, JlNavbar, JlNumberInput, JlPage, JlPageHeader, JlPagination, JlPopover, JlProgress, JlProgressRing, JlRadioGroup, JlRating, JlSearch, JlSearchButton, JlSegmented, JlSelect, JlSideNav, JlSkeleton, JlSlider, JlSpinner, JlSplitView, JlStat, JlSteps, JlSwitch, JlTabBar, JlTable, JlTabPanel, JlTabs, JlTagInput, JlTextarea, JlTimeline, JlToast, JlToaster, JlTooltip, JlTree);
90
+ export { ACCORDION_VARIANTS, AVATAR_SHAPES, AVATAR_SIZES, AVATAR_STATUSES, BASE_STYLES, BUTTON_TYPES, BUTTON_VARIANTS, CARD_PADDINGS, CHIP_SIZES, CODE_LANGUAGES, COMMON_ICONS, CONFIRM_TONES, CONTROL_SIZES, CONTROL_STYLES, DEFAULT_TEXTS, DIALOG_SIZES, DIVIDER_ALIGNS, DRAWER_PLACEMENTS, DRAWER_SIZES, EMPTY_SIZES, FACTS_ORIENTATIONS, FIELD_ORIENTATIONS, FLOATING_STYLES, INPUT_TYPES, JlAccordion, JlAccordionItem, JlAccountButton, JlAlert, JlAppIcon, JlAppShell, JlAuthLayout, JlAvatar, JlAvatarGroup, JlBadge, JlBreadcrumb, JlButton, JlCalendar, JlCard, JlCheckbox, JlChip, JlChipGroup, JlCodeBlock, JlCombobox, JlCommandPalette, JlConfirm, JlCopyButton, JlDatePicker, JlDialog, JlDivider, JlDrawer, JlElement, JlEmpty, JlFacts, JlField, JlFileDrop, JlFlow, JlFormControl, JlIcon, JlInput, JlInputList, JlKbd, JlLink, JlListItem, JlLoadMore, JlLoadState, JlMenu, JlNavbar, JlNumberInput, JlPage, JlPageHeader, JlPagination, JlPopover, JlProgress, JlProgressRing, JlRadioGroup, JlRating, JlSearch, JlSearchButton, JlSegmented, JlSelect, JlSideNav, JlSkeleton, JlSlider, JlSpinner, JlSplitView, JlStat, JlSteps, JlSwitch, JlTabBar, JlTabPanel, JlTable, JlTabs, JlTagInput, JlTextarea, JlTimeline, JlToast, JlToaster, JlTooltip, JlTree, LINK_VARIANTS, LOAD_SKELETONS, LOAD_STATES, PAGE_WIDTHS, PLACEMENTS, PROGRESS_SIZES, PROGRESS_TONES, RADIO_GROUP_ORIENTATIONS, RADIO_GROUP_VARIANTS, SKELETON_SHAPES, SPLIT_ORIENTATIONS, SPLIT_PANES, STAT_TRENDS, STEPS_ORIENTATIONS, SWITCH_SIZES, TABLE_SORT_DIRECTIONS, TAB_VARIANTS, TOASTER_PLACEMENTS, TOAST_TONES, TOKEN_STYLES, TONES, TONE_ICONS, TONE_ICON_FILLED, adoptTokens, cellOf, clampedNumber, computePosition, confirmDialog, define, download, downloadTexts, followAnchor, formatDuration, formatRelativeTime, guardLeave, hasIcon, iconElement, iconNames, jlText, jsonOf, oneOf, placeFloating, registerIcons, setFavicon, setTexts, toast };
@@ -1,4 +1,9 @@
1
1
  import { JlElement } from '../base/jl-element';
2
+ /**
3
+ * Makes an app's icon the page's favicon: the tile `jl-app-icon` draws, its glyph a registered icon name, in the
4
+ * theme's `--jl-brand` and `--jl-on-brand` as the document has them, so a page's own theme shows in the tab too.
5
+ */
6
+ export declare function setFavicon(icon: string): void;
2
7
  /**
3
8
  * An app's icon: its glyph in white on a rounded tile of the brand's blue, one family for every SyWare app. Beside
4
9
  * the wordmark at the top of the side nav, alone in a folded rail or a phone's top bar; the favicon and the home
@@ -1,6 +1,9 @@
1
1
  import { BASE_STYLES } from "../styles/shared.js";
2
+ import { adoptTokens } from "../lib/tokens.js";
2
3
  import { JlElement } from "../base/jl-element.js";
3
4
  import { clampedNumber } from "../lib/attributes.js";
5
+ import { iconNode } from "../lib/icons.js";
6
+ import { CORNER_SHARE, GLYPH_SHARE, appIconSvg } from "../lib/app-icon.js";
4
7
  import jl_app_icon_default from "./jl-app-icon.css_raw.js";
5
8
  import { JlIcon } from "./jl-icon.js";
6
9
  import { css } from "@jadis/core";
@@ -9,8 +12,27 @@ import { jsx } from "@jadis/core/jsx-runtime";
9
12
  var DEFAULT_SIZE = 28;
10
13
  var MIN_SIZE = 12;
11
14
  var MAX_SIZE = 512;
12
- var GLYPH_SHARE = .62;
13
- var CORNER_SHARE = .26;
15
+ var TILE_TOKEN = "--jl-brand";
16
+ var GLYPH_TOKEN = "--jl-on-brand";
17
+ var FAVICON_SELECTOR = "link[rel=\"icon\"]";
18
+ var FAVICON_REL = "icon";
19
+ var SVG_TYPE = "image/svg+xml";
20
+ /**
21
+ * Makes an app's icon the page's favicon: the tile `jl-app-icon` draws, its glyph a registered icon name, in the
22
+ * theme's `--jl-brand` and `--jl-on-brand` as the document has them, so a page's own theme shows in the tab too.
23
+ */
24
+ function setFavicon(icon) {
25
+ adoptTokens();
26
+ const style = getComputedStyle(document.documentElement);
27
+ const svg = appIconSvg(iconNode(icon), {
28
+ glyph: style.getPropertyValue(GLYPH_TOKEN).trim(),
29
+ tile: style.getPropertyValue(TILE_TOKEN).trim()
30
+ });
31
+ const link = document.head.querySelector(FAVICON_SELECTOR) ?? document.head.appendChild(document.createElement("link"));
32
+ link.rel = FAVICON_REL;
33
+ link.type = SVG_TYPE;
34
+ link.href = `data:${SVG_TYPE},${encodeURIComponent(svg)}`;
35
+ }
14
36
  /**
15
37
  * An app's icon: its glyph in white on a rounded tile of the brand's blue, one family for every SyWare app. Beside
16
38
  * the wordmark at the top of the side nav, alone in a folded rail or a phone's top bar; the favicon and the home
@@ -28,7 +50,7 @@ var JlAppIcon = class extends JlElement {
28
50
  /** The app's name, for screen readers when no wordmark beside it says it. */
29
51
  label = this.useChange("", (value) => this.renderLabel(value), { attribute: "label" });
30
52
  _refs = this.useRefs((ref) => ({
31
- glyph: ref("jl-icon"),
53
+ glyph: ref(JlIcon),
32
54
  tile: ref(".tile")
33
55
  }));
34
56
  templateHtml() {
@@ -61,4 +83,4 @@ var JlAppIcon = class extends JlElement {
61
83
  }
62
84
  };
63
85
  JlAppIcon.register();
64
- export { JlAppIcon };
86
+ export { JlAppIcon, setFavicon };
@@ -31,7 +31,7 @@ var JlBadge = class extends JlElement {
31
31
  _refs = this.useRefs((ref) => ({
32
32
  badge: ref(".badge"),
33
33
  dot: ref(".dot"),
34
- icon: ref("jl-icon"),
34
+ icon: ref(JlIcon),
35
35
  label: ref(".label")
36
36
  }));
37
37
  /** Shows a state as an app's map of states describes it: its label, tone, icon and hint. */
@@ -67,6 +67,11 @@ export declare class JlButton extends JlElement {
67
67
  get(): string;
68
68
  set(setter: string | ((prevState: string) => string)): void;
69
69
  }>>;
70
+ /** With an `href`, saves what it points to as a file of this name instead of opening it. */
71
+ readonly download: Readonly<Readonly<{
72
+ get(): string;
73
+ set(setter: string | ((prevState: string) => string)): void;
74
+ }>>;
70
75
  private readonly _internals;
71
76
  private readonly _refs;
72
77
  focus(options?: FocusOptions): void;
@@ -73,6 +73,8 @@ var JlButton = class extends JlElement {
73
73
  href = this.useChange("", () => this.renderLink(), { attribute: "href" });
74
74
  /** Where the link opens (`_blank` for a new tab), with an `href`. */
75
75
  target = this.useChange("", () => this.renderLink(), { attribute: "target" });
76
+ /** With an `href`, saves what it points to as a file of this name instead of opening it. */
77
+ download = this.useChange("", () => this.renderLink(), { attribute: "download" });
76
78
  _internals = this.attachInternals();
77
79
  _refs = this.useRefs((ref) => ({
78
80
  button: ref("button"),
@@ -81,7 +83,7 @@ var JlButton = class extends JlElement {
81
83
  label: ref(".text"),
82
84
  link: ref("a"),
83
85
  slot: ref("slot"),
84
- spinner: ref("jl-spinner"),
86
+ spinner: ref(JlSpinner),
85
87
  start: ref(".start")
86
88
  }));
87
89
  focus(options) {
@@ -154,6 +156,7 @@ var JlButton = class extends JlElement {
154
156
  link.hidden = !isLink;
155
157
  setOrRemove(link, "href", isLink && !off ? href : "");
156
158
  setOrRemove(link, "target", target);
159
+ setOrRemove(link, "download", this.download.get());
157
160
  setOrRemove(link, "rel", target === NEW_TAB ? SAFE_REL : "");
158
161
  setOrRemove(link, "role", off ? "link" : "");
159
162
  setOrRemove(link, "aria-disabled", off ? "true" : "");
@@ -2,6 +2,7 @@ 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 { jlText } from "../lib/texts.js";
5
6
  import { JlIcon } from "./jl-icon.js";
6
7
  import { isActivationKey } from "../lib/surface.js";
7
8
  import jl_chip_default from "./jl-chip.css_raw.js";
@@ -91,7 +92,7 @@ var JlChip = class extends JlElement {
91
92
  }), /* @__PURE__ */ jsx("button", {
92
93
  class: "dismiss",
93
94
  type: "button",
94
- "aria-label": "Remove",
95
+ "aria-label": jlText("remove"),
95
96
  hidden: true,
96
97
  children: /* @__PURE__ */ jsx(JlIcon, { name: "x" })
97
98
  })]
@@ -152,7 +153,7 @@ var JlChip = class extends JlElement {
152
153
  renderLabel() {
153
154
  const label = this.label.get();
154
155
  this._refs.label.textContent = label;
155
- this._refs.dismiss.setAttribute("aria-label", label === "" ? "Remove" : `Remove ${label}`);
156
+ this._refs.dismiss.setAttribute("aria-label", label === "" ? jlText("remove") : jlText("removeNamed", { name: label }));
156
157
  }
157
158
  renderSize(size) {
158
159
  this.reflect("size", size);
@@ -3,6 +3,7 @@ import { JlFormControl } from "../base/jl-form-control.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 { JlIcon } from "./jl-icon.js";
7
8
  import { setOrRemove } from "../lib/dom.js";
8
9
  import jl_input_default from "./jl-input.css_raw.js";
@@ -107,7 +108,7 @@ var JlInput = class extends JlFormControl {
107
108
  /* @__PURE__ */ jsx("button", {
108
109
  class: "action clear",
109
110
  type: "button",
110
- "aria-label": "Clear",
111
+ "aria-label": jlText("clear"),
111
112
  tabIndex: -1,
112
113
  hidden: true,
113
114
  children: /* @__PURE__ */ jsx(JlIcon, {
@@ -118,7 +119,7 @@ var JlInput = class extends JlFormControl {
118
119
  /* @__PURE__ */ jsx("button", {
119
120
  class: "action reveal",
120
121
  type: "button",
121
- "aria-label": "Show the password",
122
+ "aria-label": jlText("showPassword"),
122
123
  hidden: true,
123
124
  children: /* @__PURE__ */ jsx(JlIcon, {
124
125
  name: "eye",
@@ -163,7 +164,7 @@ var JlInput = class extends JlFormControl {
163
164
  const revealed = native.type === PASSWORD;
164
165
  native.type = revealed ? REVEALED : PASSWORD;
165
166
  this._refs.revealIcon.name.set(revealed ? "eye-off" : "eye");
166
- this._refs.reveal.setAttribute("aria-label", revealed ? "Hide the password" : "Show the password");
167
+ this._refs.reveal.setAttribute("aria-label", revealed ? jlText("hidePassword") : jlText("showPassword"));
167
168
  }
168
169
  renderValue(value) {
169
170
  const native = this._refs.native;
@@ -39,7 +39,7 @@ var JlLink = class extends JlElement {
39
39
  });
40
40
  _refs = this.useRefs((ref) => ({
41
41
  anchor: ref("a"),
42
- icon: ref("jl-icon"),
42
+ icon: ref(JlIcon),
43
43
  label: ref(".label"),
44
44
  newTab: ref(".new-tab")
45
45
  }));
@@ -3,6 +3,7 @@ import { JlFormControl } from "../base/jl-form-control.js";
3
3
  import { clampedNumber, oneOf } from "../lib/attributes.js";
4
4
  import { iconElement, registerIcons } from "../lib/icons.js";
5
5
  import { CONTROL_SIZES } from "../lib/options.js";
6
+ import { jlText } from "../lib/texts.js";
6
7
  import { ratingAfterChoice, ratingAfterKey } from "../lib/rating.js";
7
8
  import jl_rating_default from "./jl-rating.css_raw.js";
8
9
  import { css } from "@jadis/core";
@@ -18,8 +19,6 @@ var ICON_SIZES = {
18
19
  var DEFAULT_MAX = 5;
19
20
  var MAX_LIMIT = 10;
20
21
  var NO_PREVIEW = 0;
21
- var DEFAULT_LABEL = "Rating";
22
- var VALUE_MISSING = "Choose a rating.";
23
22
  /**
24
23
  * A row of stars (or hearts, or any icon) to give a score: a click, the arrows or a digit set it, and choosing the
25
24
  * score already given clears it. Hovering previews the score a click would give.
@@ -50,8 +49,8 @@ var JlRating = class extends JlFormControl {
50
49
  parse: oneOf(CONTROL_SIZES, "md")
51
50
  });
52
51
  /** The name screen readers give the scale. */
53
- label = this.useChange(DEFAULT_LABEL, (value) => {
54
- this._refs.slider.setAttribute("aria-label", value === "" ? DEFAULT_LABEL : value);
52
+ label = this.useChange(jlText("rating"), (value) => {
53
+ this._refs.slider.setAttribute("aria-label", value === "" ? jlText("rating") : value);
55
54
  }, { attribute: "label" });
56
55
  required = this.useChange(false, (value) => {
57
56
  this._refs.slider.toggleAttribute("aria-required", value);
@@ -167,7 +166,7 @@ var JlRating = class extends JlFormControl {
167
166
  }
168
167
  renderValidity() {
169
168
  if (this.required.get() && this.current() === 0) {
170
- this.internals.setValidity({ valueMissing: true }, VALUE_MISSING, this._refs.slider);
169
+ this.internals.setValidity({ valueMissing: true }, jlText("chooseRating"), this._refs.slider);
171
170
  return;
172
171
  }
173
172
  this.internals.setValidity({});
@@ -1,3 +1,3 @@
1
1
  // src/atoms/jl-select.css?raw
2
- var jl_select_default = ":host {\n display: block;\n}\n\n/* The select covers the whole frame, chevron included, so a click anywhere opens it. */\n.control {\n position: relative;\n padding: 0;\n}\n\n.native {\n padding: 0 36px 0 12px;\n border-radius: inherit;\n text-overflow: ellipsis;\n cursor: pointer;\n appearance: none;\n}\n\n.native:disabled {\n cursor: not-allowed;\n}\n\n:host([placeholder-shown]) .native {\n color: var(--jl-faint);\n}\n\n/* The list keeps the text colour even while the placeholder greys the closed field. */\noption,\noptgroup {\n background: var(--jl-surface);\n color: var(--jl-text);\n}\n\n.chevron {\n position: absolute;\n right: 12px;\n color: var(--jl-muted);\n pointer-events: none;\n}\n\n:host([disabled]) .chevron {\n color: var(--jl-faint);\n}\n";
2
+ var jl_select_default = ":host {\n display: block;\n}\n\n/* The select covers the whole frame, chevron included, so a click anywhere opens it. */\n.control {\n position: relative;\n padding: 0;\n}\n\n.native {\n padding: 0 36px 0 12px;\n border-radius: inherit;\n text-overflow: ellipsis;\n cursor: pointer;\n appearance: none;\n}\n\n.native:disabled {\n cursor: not-allowed;\n}\n\n:host([placeholder-shown]) .native {\n color: var(--jl-faint);\n}\n\n/* The list keeps the text colour even while the placeholder greys the closed field. */\noption,\noptgroup {\n background: var(--jl-surface);\n color: var(--jl-text);\n}\n\n.chevron {\n position: absolute;\n right: 12px;\n color: var(--jl-muted);\n pointer-events: none;\n transition: transform var(--jl-fast) var(--jl-ease);\n}\n\n.native:open + .chevron {\n transform: rotate(180deg);\n}\n\n:host([disabled]) .chevron {\n color: var(--jl-faint);\n}\n\n/*\n * Where the browser lets the page draw the list (customizable select), it is the combobox's: the same panel, rows\n * and check. Touch screens keep the platform's own sheet, and other browsers their own list.\n */\n@media (hover: hover) and (pointer: fine) {\n @supports (appearance: base-select) {\n /* The list lines up with the frame, not with the select inside its border. */\n .control {\n anchor-name: --frame;\n }\n\n .native,\n .native::picker(select) {\n appearance: base-select;\n }\n\n /*\n * The browser draws the chosen label in a box of its own, out of text-overflow's reach: clamped to one line and\n * clipped at the padding, it still ends with an ellipsis.\n */\n .native {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 1;\n padding-block: calc((var(--height, var(--jl-control-md)) - 2px - 1lh) / 2);\n overflow: clip;\n overflow-clip-margin: content-box;\n }\n\n .native::picker-icon {\n display: none;\n }\n\n .native::picker(select) {\n position-anchor: --frame;\n /* Under the field when it fits there, as the combobox's list. */\n position-try-order: normal;\n box-sizing: border-box;\n min-inline-size: anchor-size(self-inline);\n max-block-size: 274px;\n margin-block: 6px;\n padding: 4px;\n border: 1px solid var(--jl-line);\n border-radius: var(--jl-radius);\n background: var(--jl-surface);\n color: var(--jl-text);\n box-shadow: var(--jl-shadow-lg);\n animation: jl-float-in var(--jl-fast) var(--jl-ease);\n }\n\n /*\n * A heading as the command palette's. The browser draws the label itself, half an em in: text-indent reaches it\n * and lines it up with the options' text.\n */\n optgroup {\n padding-block-start: 8px;\n color: var(--jl-muted);\n font-size: var(--jl-font-xs);\n font-weight: 600;\n text-indent: calc(10px - 0.5em);\n }\n\n optgroup > option:first-of-type {\n margin-block-start: 4px;\n }\n\n option {\n box-sizing: border-box;\n gap: 10px;\n min-block-size: 34px;\n padding: 6px 10px;\n border-radius: var(--jl-radius-xs);\n font-size: var(--jl-font-md);\n font-weight: 400;\n line-height: 1.35;\n text-indent: 0;\n cursor: pointer;\n }\n\n option:enabled:is(:hover, :focus-visible) {\n background: var(--jl-surface-3);\n box-shadow: none;\n }\n\n option:checked {\n font-weight: 600;\n }\n\n option:disabled {\n color: var(--jl-faint);\n cursor: not-allowed;\n }\n\n /* Lucide's check, at the end of the row as in the combobox. */\n option::checkmark {\n order: 1;\n width: 14px;\n height: 14px;\n margin-inline-start: auto;\n background: var(--jl-accent);\n content: \"\";\n mask:\n url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E\")\n center / contain no-repeat;\n }\n }\n}\n";
3
3
  export { jl_select_default as default };
@@ -2,8 +2,9 @@ import { JlFormControl } from '../base/jl-form-control';
2
2
  import type { SelectOption } from '../types/jl-select.types';
3
3
  import type { ControlSize } from '../types/options.types';
4
4
  /**
5
- * A choice among a list, on the browser's own `<select>`: its list opens the way the platform does it. The choices
6
- * come from `options` (JSON) or from `<option>` and `<optgroup>` elements written inside it.
5
+ * A choice among a list, on the browser's own `<select>`: its list opens the way the platform does it on a touch
6
+ * screen, and with a mouse looks like the combobox's where the browser lets the page draw it (customizable select).
7
+ * The choices come from `options` (JSON) or from `<option>` and `<optgroup>` elements written inside it.
7
8
  */
8
9
  export declare class JlSelect extends JlFormControl {
9
10
  static readonly selector = "jl-select";
@@ -1,4 +1,4 @@
1
- import { BASE_STYLES, CONTROL_STYLES } from "../styles/shared.js";
1
+ import { BASE_STYLES, CONTROL_STYLES, FLOATING_STYLES } from "../styles/shared.js";
2
2
  import { JlFormControl } from "../base/jl-form-control.js";
3
3
  import { jsonOf, oneOf } from "../lib/attributes.js";
4
4
  import { registerIcons } from "../lib/icons.js";
@@ -29,8 +29,9 @@ var groupNodes = (group) => {
29
29
  })];
30
30
  };
31
31
  /**
32
- * A choice among a list, on the browser's own `<select>`: its list opens the way the platform does it. The choices
33
- * come from `options` (JSON) or from `<option>` and `<optgroup>` elements written inside it.
32
+ * A choice among a list, on the browser's own `<select>`: its list opens the way the platform does it on a touch
33
+ * screen, and with a mouse looks like the combobox's where the browser lets the page draw it (customizable select).
34
+ * The choices come from `options` (JSON) or from `<option>` and `<optgroup>` elements written inside it.
34
35
  */
35
36
  var JlSelect = class extends JlFormControl {
36
37
  static selector = "jl-select";
@@ -81,7 +82,7 @@ var JlSelect = class extends JlFormControl {
81
82
  });
82
83
  }
83
84
  templateCss() {
84
- return css`${BASE_STYLES}${CONTROL_STYLES}${jl_select_default}`;
85
+ return css`${BASE_STYLES}${CONTROL_STYLES}${FLOATING_STYLES}${jl_select_default}`;
85
86
  }
86
87
  onConnect() {
87
88
  const native = this._refs.native;
@@ -1,16 +1,16 @@
1
1
  import { BASE_STYLES } from "../styles/shared.js";
2
2
  import { JlElement } from "../base/jl-element.js";
3
+ import { jlText } from "../lib/texts.js";
3
4
  import jl_spinner_default from "./jl-spinner.css_raw.js";
4
5
  import { css } from "@jadis/core";
5
6
  import { jsx } from "@jadis/core/jsx-runtime";
6
7
  // src/atoms/jl-spinner.tsx
7
8
  var DEFAULT_SIZE = 16;
8
- var DEFAULT_LABEL = "Loading";
9
9
  /** A turning ring for work in progress. It follows the text colour; `label` is what screen readers say. */
10
10
  var JlSpinner = class extends JlElement {
11
11
  static selector = "jl-spinner";
12
12
  size = this.useChange(DEFAULT_SIZE, (value) => this.style.setProperty("--size", `${value}px`), { attribute: "size" });
13
- label = this.useChange(DEFAULT_LABEL, (value) => this._refs.ring.setAttribute("aria-label", value), { attribute: "label" });
13
+ label = this.useChange(jlText("loading"), (value) => this._refs.ring.setAttribute("aria-label", value), { attribute: "label" });
14
14
  _refs = this.useRefs((ref) => ({ ring: ref(".ring") }));
15
15
  templateHtml() {
16
16
  return /* @__PURE__ */ jsx("span", {
@@ -1,4 +1,5 @@
1
1
  import { JlElement } from "./jl-element.js";
2
+ import { formInternals } from "../lib/internals.js";
2
3
  // src/base/jl-form-control.ts
3
4
  /**
4
5
  * The base of the components a form submits: they take part in `<form>` like native fields (their `name`, their
@@ -6,7 +7,7 @@ import { JlElement } from "./jl-element.js";
6
7
  */
7
8
  var JlFormControl = class extends JlElement {
8
9
  static formAssociated = true;
9
- internals = this.attachInternals();
10
+ internals = formInternals(this);
10
11
  /** The field's name in the form's data. Reflected, so a name set from JSX reaches the form too. */
11
12
  name = this.useChange("", (value) => this.reflect("name", value), { attribute: "name" });
12
13
  get form() {
package/dist/index.d.ts CHANGED
@@ -6,7 +6,9 @@ export { computePosition, followAnchor, PLACEMENTS, placeFloating } from './lib/
6
6
  export { COMMON_ICONS } from './lib/icon-set';
7
7
  export { hasIcon, iconElement, iconNames, registerIcons } from './lib/icons';
8
8
  export { CONTROL_SIZES, TONE_ICON_FILLED, TONE_ICONS, TONES } from './lib/options';
9
+ export { cellOf } from './lib/table';
9
10
  export { formatDuration, formatRelativeTime } from './lib/time';
11
+ export { DEFAULT_TEXTS, jlText, setTexts } from './lib/texts';
10
12
  export { adoptTokens } from './lib/tokens';
11
13
  export { BASE_STYLES, CONTROL_STYLES, FLOATING_STYLES, TOKEN_STYLES } from './styles/shared';
12
14
  export type * from './types/calendar.types';
@@ -16,6 +18,7 @@ export type * from './types/fuzzy.types';
16
18
  export type * from './types/highlight.types';
17
19
  export type * from './types/hotkey.types';
18
20
  export type * from './types/jl-accordion.types';
21
+ export type * from './types/jl-app-icon.types';
19
22
  export type * from './types/jl-avatar.types';
20
23
  export type * from './types/jl-badge.types';
21
24
  export type * from './types/jl-breadcrumb.types';
@@ -30,6 +33,7 @@ export type * from './types/jl-dialog.types';
30
33
  export type * from './types/jl-divider.types';
31
34
  export type * from './types/jl-drawer.types';
32
35
  export type * from './types/jl-empty.types';
36
+ export type * from './types/jl-facts.types';
33
37
  export type * from './types/jl-field.types';
34
38
  export type * from './types/jl-flow.types';
35
39
  export type * from './types/jl-input.types';
@@ -63,6 +67,7 @@ export type * from './types/options.types';
63
67
  export type * from './types/pagination.types';
64
68
  export type * from './types/roving-index.types';
65
69
  export type * from './types/sparkline.types';
70
+ export type * from './types/texts.types';
66
71
  export * from './atoms/jl-app-icon';
67
72
  export * from './atoms/jl-avatar';
68
73
  export * from './atoms/jl-badge';
@@ -97,9 +102,12 @@ export * from './molecules/jl-combobox';
97
102
  export * from './molecules/jl-copy-button';
98
103
  export * from './molecules/jl-date-picker';
99
104
  export * from './molecules/jl-empty';
105
+ export * from './molecules/jl-facts';
100
106
  export * from './molecules/jl-field';
101
107
  export * from './molecules/jl-file-drop';
108
+ export * from './molecules/jl-input-list';
102
109
  export * from './molecules/jl-list-item';
110
+ export * from './molecules/jl-load-more';
103
111
  export * from './molecules/jl-load-state';
104
112
  export * from './molecules/jl-menu';
105
113
  export * from './molecules/jl-number-input';
package/dist/index.js CHANGED
@@ -8,9 +8,11 @@ import { PLACEMENTS, computePosition, followAnchor, placeFloating } from "./lib/
8
8
  import { COMMON_ICONS } from "./lib/icon-set.js";
9
9
  import { hasIcon, iconElement, iconNames, registerIcons } from "./lib/icons.js";
10
10
  import { CONTROL_SIZES, TONES, TONE_ICONS, TONE_ICON_FILLED } from "./lib/options.js";
11
+ import { cellOf } from "./lib/table.js";
11
12
  import { formatDuration, formatRelativeTime } from "./lib/time.js";
13
+ import { DEFAULT_TEXTS, jlText, setTexts } from "./lib/texts.js";
12
14
  import { JlIcon } from "./atoms/jl-icon.js";
13
- import { JlAppIcon } from "./atoms/jl-app-icon.js";
15
+ import { JlAppIcon, setFavicon } from "./atoms/jl-app-icon.js";
14
16
  import { AVATAR_SHAPES, AVATAR_SIZES, AVATAR_STATUSES, JlAvatar } from "./atoms/jl-avatar.js";
15
17
  import { JlBadge } from "./atoms/jl-badge.js";
16
18
  import { JlSpinner } from "./atoms/jl-spinner.js";
@@ -43,9 +45,12 @@ import { CODE_LANGUAGES, JlCodeBlock } from "./molecules/jl-code-block.js";
43
45
  import { JlCombobox } from "./molecules/jl-combobox.js";
44
46
  import { JlDatePicker } from "./molecules/jl-date-picker.js";
45
47
  import { EMPTY_SIZES, JlEmpty } from "./molecules/jl-empty.js";
48
+ import { FACTS_ORIENTATIONS, JlFacts } from "./molecules/jl-facts.js";
46
49
  import { FIELD_ORIENTATIONS, JlField } from "./molecules/jl-field.js";
47
50
  import { JlFileDrop } from "./molecules/jl-file-drop.js";
51
+ import { JlInputList } from "./molecules/jl-input-list.js";
48
52
  import { JlListItem } from "./molecules/jl-list-item.js";
53
+ import { JlLoadMore } from "./molecules/jl-load-more.js";
49
54
  import { JlLoadState, LOAD_SKELETONS, LOAD_STATES } from "./molecules/jl-load-state.js";
50
55
  import { JlMenu } from "./molecules/jl-menu.js";
51
56
  import { JlNumberInput } from "./molecules/jl-number-input.js";
@@ -73,10 +78,10 @@ import { JlPageHeader } from "./organisms/jl-page-header.js";
73
78
  import { JlSideNav } from "./organisms/jl-side-nav.js";
74
79
  import { JlTabBar } from "./organisms/jl-tab-bar.js";
75
80
  import { JlTable, TABLE_SORT_DIRECTIONS } from "./organisms/jl-table.js";
76
- import { JlToaster, TOASTER_PLACEMENTS, toast } from "./organisms/jl-toaster.js";
81
+ import { JlToaster, TOASTER_PLACEMENTS, download, downloadTexts, toast } from "./organisms/jl-toaster.js";
77
82
  import { JlTree } from "./organisms/jl-tree.js";
78
83
  import { JlAppShell } from "./templates/jl-app-shell.js";
79
84
  import { JlAuthLayout } from "./templates/jl-auth-layout.js";
80
85
  import { JlPage, PAGE_WIDTHS } from "./templates/jl-page.js";
81
86
  import { JlSplitView, SPLIT_ORIENTATIONS, SPLIT_PANES } from "./templates/jl-split-view.js";
82
- export { ACCORDION_VARIANTS, AVATAR_SHAPES, AVATAR_SIZES, AVATAR_STATUSES, BASE_STYLES, BUTTON_TYPES, BUTTON_VARIANTS, CARD_PADDINGS, CHIP_SIZES, CODE_LANGUAGES, COMMON_ICONS, CONFIRM_TONES, CONTROL_SIZES, CONTROL_STYLES, DIALOG_SIZES, DIVIDER_ALIGNS, DRAWER_PLACEMENTS, DRAWER_SIZES, EMPTY_SIZES, FIELD_ORIENTATIONS, FLOATING_STYLES, INPUT_TYPES, JlAccordion, JlAccordionItem, JlAccountButton, JlAlert, JlAppIcon, JlAppShell, JlAuthLayout, JlAvatar, JlAvatarGroup, JlBadge, JlBreadcrumb, JlButton, JlCalendar, JlCard, JlCheckbox, JlChip, JlChipGroup, JlCodeBlock, JlCombobox, JlCommandPalette, JlConfirm, JlCopyButton, JlDatePicker, JlDialog, JlDivider, JlDrawer, JlElement, JlEmpty, JlField, JlFileDrop, JlFlow, JlFormControl, JlIcon, JlInput, JlKbd, JlLink, JlListItem, JlLoadState, JlMenu, JlNavbar, JlNumberInput, JlPage, JlPageHeader, JlPagination, JlPopover, JlProgress, JlProgressRing, JlRadioGroup, JlRating, JlSearch, JlSearchButton, JlSegmented, JlSelect, JlSideNav, JlSkeleton, JlSlider, JlSpinner, JlSplitView, JlStat, JlSteps, JlSwitch, JlTabBar, JlTabPanel, JlTable, JlTabs, JlTagInput, JlTextarea, JlTimeline, JlToast, JlToaster, JlTooltip, JlTree, LINK_VARIANTS, LOAD_SKELETONS, LOAD_STATES, PAGE_WIDTHS, PLACEMENTS, PROGRESS_SIZES, PROGRESS_TONES, RADIO_GROUP_ORIENTATIONS, RADIO_GROUP_VARIANTS, SKELETON_SHAPES, SPLIT_ORIENTATIONS, SPLIT_PANES, STAT_TRENDS, STEPS_ORIENTATIONS, SWITCH_SIZES, TABLE_SORT_DIRECTIONS, TAB_VARIANTS, TOASTER_PLACEMENTS, TOAST_TONES, TOKEN_STYLES, TONES, TONE_ICONS, TONE_ICON_FILLED, adoptTokens, clampedNumber, computePosition, confirmDialog, define, followAnchor, formatDuration, formatRelativeTime, guardLeave, hasIcon, iconElement, iconNames, jsonOf, oneOf, placeFloating, registerIcons, toast };
87
+ export { ACCORDION_VARIANTS, AVATAR_SHAPES, AVATAR_SIZES, AVATAR_STATUSES, BASE_STYLES, BUTTON_TYPES, BUTTON_VARIANTS, CARD_PADDINGS, CHIP_SIZES, CODE_LANGUAGES, COMMON_ICONS, CONFIRM_TONES, CONTROL_SIZES, CONTROL_STYLES, DEFAULT_TEXTS, DIALOG_SIZES, DIVIDER_ALIGNS, DRAWER_PLACEMENTS, DRAWER_SIZES, EMPTY_SIZES, FACTS_ORIENTATIONS, FIELD_ORIENTATIONS, FLOATING_STYLES, INPUT_TYPES, JlAccordion, JlAccordionItem, JlAccountButton, JlAlert, JlAppIcon, JlAppShell, JlAuthLayout, JlAvatar, JlAvatarGroup, JlBadge, JlBreadcrumb, JlButton, JlCalendar, JlCard, JlCheckbox, JlChip, JlChipGroup, JlCodeBlock, JlCombobox, JlCommandPalette, JlConfirm, JlCopyButton, JlDatePicker, JlDialog, JlDivider, JlDrawer, JlElement, JlEmpty, JlFacts, JlField, JlFileDrop, JlFlow, JlFormControl, JlIcon, JlInput, JlInputList, JlKbd, JlLink, JlListItem, JlLoadMore, JlLoadState, JlMenu, JlNavbar, JlNumberInput, JlPage, JlPageHeader, JlPagination, JlPopover, JlProgress, JlProgressRing, JlRadioGroup, JlRating, JlSearch, JlSearchButton, JlSegmented, JlSelect, JlSideNav, JlSkeleton, JlSlider, JlSpinner, JlSplitView, JlStat, JlSteps, JlSwitch, JlTabBar, JlTabPanel, JlTable, JlTabs, JlTagInput, JlTextarea, JlTimeline, JlToast, JlToaster, JlTooltip, JlTree, LINK_VARIANTS, LOAD_SKELETONS, LOAD_STATES, PAGE_WIDTHS, PLACEMENTS, PROGRESS_SIZES, PROGRESS_TONES, RADIO_GROUP_ORIENTATIONS, RADIO_GROUP_VARIANTS, SKELETON_SHAPES, SPLIT_ORIENTATIONS, SPLIT_PANES, STAT_TRENDS, STEPS_ORIENTATIONS, SWITCH_SIZES, TABLE_SORT_DIRECTIONS, TAB_VARIANTS, TOASTER_PLACEMENTS, TOAST_TONES, TOKEN_STYLES, TONES, TONE_ICONS, TONE_ICON_FILLED, adoptTokens, cellOf, clampedNumber, computePosition, confirmDialog, define, download, downloadTexts, followAnchor, formatDuration, formatRelativeTime, guardLeave, hasIcon, iconElement, iconNames, jlText, jsonOf, oneOf, placeFloating, registerIcons, setFavicon, setTexts, toast };
@@ -0,0 +1,8 @@
1
+ import type { IconNode } from 'lucide';
2
+ import type { AppIconColors } from '../types/jl-app-icon.types';
3
+ /** The glyph's share of the tile's side, at every size. */
4
+ export declare const GLYPH_SHARE = 0.62;
5
+ /** The corner's share of the tile's side. */
6
+ export declare const CORNER_SHARE = 0.26;
7
+ /** An app's icon as the text of an SVG file: its glyph in `colors.glyph` on a rounded square of `colors.tile`. */
8
+ export declare function appIconSvg(glyph: IconNode, colors: AppIconColors): string;
@@ -0,0 +1,37 @@
1
+ import { ICON_STROKE_WIDTH } from "./icons.js";
2
+ // src/lib/app-icon.ts
3
+ /** The glyph's share of the tile's side, at every size. */
4
+ var GLYPH_SHARE = .62;
5
+ /** The corner's share of the tile's side. */
6
+ var CORNER_SHARE = .26;
7
+ var FILE_SIDE = 64;
8
+ var GLYPH_GRID = 24;
9
+ var DECIMALS = 2;
10
+ var ATTRIBUTE_ESCAPES = {
11
+ "\"": "&quot;",
12
+ "&": "&amp;",
13
+ "<": "&lt;"
14
+ };
15
+ var NEEDS_ESCAPE = /["&<]/g;
16
+ var round = (value) => Number(value.toFixed(DECIMALS));
17
+ var escapeAttribute = (value) => String(value).replace(NEEDS_ESCAPE, (character) => ATTRIBUTE_ESCAPES[character] ?? character);
18
+ var attributesOf = (attributes) => Object.entries(attributes).flatMap(([name, value]) => value === void 0 ? [] : [` ${name}="${escapeAttribute(value)}"`]).join("");
19
+ /** An app's icon as the text of an SVG file: its glyph in `colors.glyph` on a rounded square of `colors.tile`. */
20
+ function appIconSvg(glyph, colors) {
21
+ const glyphSide = FILE_SIDE * GLYPH_SHARE;
22
+ const offset = round(24.32 / 2);
23
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${FILE_SIDE} ${FILE_SIDE}"><rect${attributesOf({
24
+ fill: colors.tile,
25
+ height: FILE_SIDE,
26
+ rx: round(FILE_SIDE * CORNER_SHARE),
27
+ width: FILE_SIDE
28
+ })}/><g${attributesOf({
29
+ fill: "none",
30
+ stroke: colors.glyph,
31
+ "stroke-linecap": "round",
32
+ "stroke-linejoin": "round",
33
+ "stroke-width": ICON_STROKE_WIDTH,
34
+ transform: `translate(${offset} ${offset}) scale(${round(glyphSide / GLYPH_GRID)})`
35
+ })}>${glyph.map(([tag, attributes]) => `<${tag}${attributesOf(attributes)}/>`).join("")}</g></svg>`;
36
+ }
37
+ export { CORNER_SHARE, GLYPH_SHARE, appIconSvg };
package/dist/lib/dom.d.ts CHANGED
@@ -4,3 +4,5 @@ export declare function setOrRemove(element: Element, name: string, value: strin
4
4
  export declare function uniqueId(prefix: string): string;
5
5
  /** An SVG element with its attributes: JSX makes HTML elements only. */
6
6
  export declare function svgElement<Tag extends keyof SVGElementTagNameMap>(tag: Tag, attributes: Record<string, string>): SVGElementTagNameMap[Tag];
7
+ /** Whether a click asks the browser for something of its own: a new tab or window, a download. */
8
+ export declare const isModifiedClick: (event: MouseEvent) => boolean;
package/dist/lib/dom.js CHANGED
@@ -20,4 +20,6 @@ function svgElement(tag, attributes) {
20
20
  for (const [name, value] of Object.entries(attributes)) element.setAttribute(name, value);
21
21
  return element;
22
22
  }
23
- export { setOrRemove, svgElement, uniqueId };
23
+ /** Whether a click asks the browser for something of its own: a new tab or window, a download. */
24
+ var isModifiedClick = (event) => event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || event.button !== 0;
25
+ export { isModifiedClick, setOrRemove, svgElement, uniqueId };
@@ -0,0 +1,10 @@
1
+ /** The file name a Content-Disposition header gives (its UTF-8 form first), or none. */
2
+ export declare function dispositionFileName(header: string | null): string | undefined;
3
+ /** A file name for an address: its last path segment, or the fallback when it has none. */
4
+ export declare function urlFileName(url: string, fallback: string): string;
5
+ /** What a failed answer's body says: `{ error: { message } }`, `{ error: "…" }`, `{ message }`, or its status. */
6
+ export declare function answerError(body: unknown, status: number): string;
7
+ /** Reads an answer to its end, saying how many bytes came at most once per interval, and once more at the end. */
8
+ export declare function readWithProgress(response: Response, onProgress: (received: number) => void, intervalMs: number, now?: () => number): Promise<Blob>;
9
+ /** Hands a file to the browser, which saves it under that name. */
10
+ export declare function saveBlob(blob: Blob, name: string): void;
@@ -0,0 +1,62 @@
1
+ // src/lib/download.ts
2
+ var FILE_NAME_UTF8 = /filename\*\s*=\s*(?:UTF-8|utf-8)''([^;]+)/;
3
+ var FILE_NAME = /filename\s*=\s*"?([^";]+)"?/;
4
+ var REVOKE_DELAY_MS = 1e3;
5
+ var ADDRESS_BASE = "http://localhost/";
6
+ /** The file name a Content-Disposition header gives (its UTF-8 form first), or none. */
7
+ function dispositionFileName(header) {
8
+ const utf8 = FILE_NAME_UTF8.exec(header ?? "")?.[1];
9
+ if (utf8 !== void 0) return decodeURIComponent(utf8.trim());
10
+ return FILE_NAME.exec(header ?? "")?.[1]?.trim() || void 0;
11
+ }
12
+ /** A file name for an address: its last path segment, or the fallback when it has none. */
13
+ function urlFileName(url, fallback) {
14
+ const segment = new URL(url, ADDRESS_BASE).pathname.split("/").at(-1) ?? "";
15
+ return segment === "" ? fallback : decodeURIComponent(segment);
16
+ }
17
+ /** What a failed answer's body says: `{ error: { message } }`, `{ error: "…" }`, `{ message }`, or its status. */
18
+ function answerError(body, status) {
19
+ const record = typeof body === "object" && body !== null ? body : {};
20
+ const error = record.error;
21
+ return [
22
+ typeof error === "object" && error !== null ? error.message : void 0,
23
+ error,
24
+ record.message
25
+ ].find((value) => typeof value === "string") ?? `HTTP ${status}`;
26
+ }
27
+ /** Reads an answer to its end, saying how many bytes came at most once per interval, and once more at the end. */
28
+ async function readWithProgress(response, onProgress, intervalMs, now = () => performance.now()) {
29
+ const type = response.headers.get("content-type") ?? "";
30
+ if (response.body === null) return response.blob();
31
+ const reader = response.body.getReader();
32
+ const chunks = [];
33
+ const progress = {
34
+ received: 0,
35
+ reported: Number.NEGATIVE_INFINITY,
36
+ said: 0
37
+ };
38
+ for (;;) {
39
+ const chunk = await reader.read();
40
+ if (chunk.done) break;
41
+ chunks.push(chunk.value);
42
+ progress.received += chunk.value.byteLength;
43
+ if (now() - progress.reported >= intervalMs) {
44
+ progress.reported = now();
45
+ progress.said = progress.received;
46
+ onProgress(progress.received);
47
+ }
48
+ }
49
+ if (progress.said !== progress.received) onProgress(progress.received);
50
+ return new Blob(chunks, { type });
51
+ }
52
+ /** Hands a file to the browser, which saves it under that name. */
53
+ function saveBlob(blob, name) {
54
+ const link = document.createElement("a");
55
+ link.download = name;
56
+ link.href = URL.createObjectURL(blob);
57
+ document.body.append(link);
58
+ link.click();
59
+ link.remove();
60
+ setTimeout(() => URL.revokeObjectURL(link.href), REVOKE_DELAY_MS);
61
+ }
62
+ export { answerError, dispositionFileName, readWithProgress, saveBlob, urlFileName };
@@ -1,4 +1,6 @@
1
1
  import type { IconNode } from 'lucide';
2
+ /** The width of every glyph's strokes, on lucide's 24-unit grid. */
3
+ export declare const ICON_STROKE_WIDTH = 1.9;
2
4
  /**
3
5
  * Adds icons to the ones `jl-icon` knows, by name. Any lucide icon works:
4
6
  * `registerIcons({ rocket: Rocket })` after `import { Rocket } from 'lucide'`, or all the usual ones at once with
@@ -8,5 +10,7 @@ export declare function registerIcons(icons: Record<string, IconNode>): void;
8
10
  /** The names `jl-icon` can draw now, in alphabetical order. */
9
11
  export declare const iconNames: () => string[];
10
12
  export declare const hasIcon: (name: string) => boolean;
13
+ /** An icon's shapes, by its name; a name nobody registered gives a neutral mark instead of nothing. */
14
+ export declare const iconNode: (name: string) => IconNode;
11
15
  /** An icon as an SVG element; a name nobody registered draws a neutral mark instead of nothing. */
12
16
  export declare function iconElement(name: string, size: number): SVGElement;