@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.
- package/dist/all.js +9 -4
- package/dist/atoms/jl-app-icon.d.ts +5 -0
- package/dist/atoms/jl-app-icon.js +26 -4
- package/dist/atoms/jl-badge.js +1 -1
- package/dist/atoms/jl-button.d.ts +5 -0
- package/dist/atoms/jl-button.js +4 -1
- package/dist/atoms/jl-chip.js +3 -2
- package/dist/atoms/jl-input.js +4 -3
- package/dist/atoms/jl-link.js +1 -1
- package/dist/atoms/jl-rating.js +4 -5
- package/dist/atoms/jl-select.css_raw.js +1 -1
- package/dist/atoms/jl-select.d.ts +3 -2
- package/dist/atoms/jl-select.js +5 -4
- package/dist/atoms/jl-spinner.js +2 -2
- package/dist/base/jl-form-control.js +2 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -3
- package/dist/lib/app-icon.d.ts +8 -0
- package/dist/lib/app-icon.js +37 -0
- package/dist/lib/dom.d.ts +2 -0
- package/dist/lib/dom.js +3 -1
- package/dist/lib/download.d.ts +10 -0
- package/dist/lib/download.js +62 -0
- package/dist/lib/icons.d.ts +4 -0
- package/dist/lib/icons.js +7 -4
- package/dist/lib/input-list.d.ts +12 -0
- package/dist/lib/input-list.js +14 -0
- package/dist/lib/internals.d.ts +5 -0
- package/dist/lib/internals.js +18 -0
- package/dist/lib/palette.js +6 -0
- package/dist/lib/resize.d.ts +2 -0
- package/dist/lib/resize.js +10 -0
- package/dist/lib/table.d.ts +6 -1
- package/dist/lib/table.js +16 -1
- package/dist/lib/text.d.ts +2 -0
- package/dist/lib/text.js +4 -1
- package/dist/lib/texts.d.ts +97 -0
- package/dist/lib/texts.js +102 -0
- package/dist/lib/tree.d.ts +10 -1
- package/dist/lib/tree.js +37 -3
- package/dist/molecules/jl-account-button.js +1 -1
- package/dist/molecules/jl-alert.js +2 -1
- package/dist/molecules/jl-breadcrumb.js +3 -3
- package/dist/molecules/jl-calendar.js +4 -4
- package/dist/molecules/jl-chip-group.js +2 -2
- package/dist/molecules/jl-code-block.css_raw.js +1 -1
- package/dist/molecules/jl-code-block.d.ts +5 -0
- package/dist/molecules/jl-code-block.js +5 -2
- package/dist/molecules/jl-combobox.js +5 -6
- package/dist/molecules/jl-copy-button.js +5 -4
- package/dist/molecules/jl-date-picker.js +6 -7
- package/dist/molecules/jl-empty.js +1 -1
- package/dist/molecules/jl-facts.css_raw.js +3 -0
- package/dist/molecules/jl-facts.d.ts +35 -0
- package/dist/molecules/jl-facts.js +64 -0
- package/dist/molecules/jl-file-drop.d.ts +5 -0
- package/dist/molecules/jl-file-drop.js +17 -14
- package/dist/molecules/jl-input-list.css_raw.js +3 -0
- package/dist/molecules/jl-input-list.d.ts +79 -0
- package/dist/molecules/jl-input-list.js +177 -0
- package/dist/molecules/jl-load-more.css_raw.js +3 -0
- package/dist/molecules/jl-load-more.d.ts +41 -0
- package/dist/molecules/jl-load-more.js +62 -0
- package/dist/molecules/jl-load-state.js +6 -7
- package/dist/molecules/jl-number-input.js +4 -4
- package/dist/molecules/jl-pagination.js +5 -5
- package/dist/molecules/jl-radio-group.js +2 -2
- package/dist/molecules/jl-search-button.js +4 -4
- package/dist/molecules/jl-search.js +4 -4
- package/dist/molecules/jl-segmented.js +2 -1
- package/dist/molecules/jl-steps.css_raw.js +1 -1
- package/dist/molecules/jl-steps.d.ts +2 -0
- package/dist/molecules/jl-steps.js +30 -14
- package/dist/molecules/jl-tabs.js +2 -1
- package/dist/molecules/jl-tag-input.js +2 -2
- package/dist/molecules/jl-toast.css_raw.js +1 -1
- package/dist/molecules/jl-toast.d.ts +13 -1
- package/dist/molecules/jl-toast.js +23 -3
- package/dist/organisms/jl-command-palette.css_raw.js +1 -1
- package/dist/organisms/jl-command-palette.d.ts +14 -0
- package/dist/organisms/jl-command-palette.js +39 -9
- package/dist/organisms/jl-confirm.js +5 -7
- package/dist/organisms/jl-dialog.js +2 -1
- package/dist/organisms/jl-drawer.js +2 -1
- package/dist/organisms/jl-flow.js +12 -12
- package/dist/organisms/jl-navbar.js +6 -6
- package/dist/organisms/jl-side-nav.css_raw.js +1 -1
- package/dist/organisms/jl-side-nav.d.ts +33 -1
- package/dist/organisms/jl-side-nav.js +70 -13
- package/dist/organisms/jl-tab-bar.js +2 -2
- package/dist/organisms/jl-table.css_raw.js +1 -1
- package/dist/organisms/jl-table.d.ts +9 -0
- package/dist/organisms/jl-table.js +35 -5
- package/dist/organisms/jl-toaster.d.ts +9 -1
- package/dist/organisms/jl-toaster.js +148 -7
- package/dist/organisms/jl-tree.css_raw.js +1 -1
- package/dist/organisms/jl-tree.d.ts +71 -4
- package/dist/organisms/jl-tree.js +303 -40
- package/dist/templates/jl-app-shell.css_raw.js +1 -1
- package/dist/templates/jl-app-shell.d.ts +10 -0
- package/dist/templates/jl-app-shell.js +16 -2
- package/dist/templates/jl-auth-layout.js +1 -1
- package/dist/templates/jl-page.js +3 -3
- package/dist/templates/jl-split-view.js +5 -4
- package/dist/types/jl-app-icon.types.d.ts +5 -0
- package/dist/types/jl-command-palette.types.d.ts +7 -0
- package/dist/types/jl-facts.types.d.ts +17 -0
- package/dist/types/jl-side-nav.types.d.ts +2 -0
- package/dist/types/jl-steps.types.d.ts +2 -0
- package/dist/types/jl-table.types.d.ts +9 -2
- package/dist/types/jl-toaster.types.d.ts +42 -1
- package/dist/types/jl-tree.types.d.ts +26 -0
- package/dist/types/texts.types.d.ts +5 -0
- package/package.json +3 -3
|
@@ -4,6 +4,7 @@ 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 { jlText } from "../lib/texts.js";
|
|
7
8
|
import { JlIcon } from "../atoms/jl-icon.js";
|
|
8
9
|
import { setOrRemove, uniqueId } from "../lib/dom.js";
|
|
9
10
|
import { keepInView } from "../lib/keep-in-view.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(
|
|
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": "
|
|
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": "
|
|
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 },
|
|
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(
|
|
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: "
|
|
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("
|
|
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("
|
|
64
|
+
button.hint.set(jlText("copy"));
|
|
64
65
|
this.reflect("copied", false);
|
|
65
66
|
}, CONFIRM_MS);
|
|
66
67
|
}
|
|
@@ -4,6 +4,7 @@ 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 { jlText } from "../lib/texts.js";
|
|
7
8
|
import { JlIcon } from "../atoms/jl-icon.js";
|
|
8
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";
|
|
@@ -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(
|
|
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": "
|
|
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":
|
|
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":
|
|
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 },
|
|
316
|
+
this.internals.setValidity({ valueMissing: true }, jlText("chooseDateMissing"), anchor);
|
|
318
317
|
return;
|
|
319
318
|
}
|
|
320
319
|
this.internals.setValidity({});
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { BASE_STYLES } from "../styles/shared.js";
|
|
2
|
+
import { JlElement } from "../base/jl-element.js";
|
|
3
|
+
import { jsonOf, oneOf } from "../lib/attributes.js";
|
|
4
|
+
import { setOrRemove } from "../lib/dom.js";
|
|
5
|
+
import jl_facts_default from "./jl-facts.css_raw.js";
|
|
6
|
+
import { css } from "@jadis/core";
|
|
7
|
+
import { jsx, jsxs } from "@jadis/core/jsx-runtime";
|
|
8
|
+
// src/molecules/jl-facts.tsx
|
|
9
|
+
var FACTS_ORIENTATIONS = ["horizontal", "vertical"];
|
|
10
|
+
var hasValue = (fact) => fact.value !== null && fact.value !== void 0 && fact.value !== "";
|
|
11
|
+
/**
|
|
12
|
+
* The facts about one thing, label and value: an object's size and digest, an order's customer and date. Facts
|
|
13
|
+
* without a value are left out, and an element with none takes no room. Values can be code (monospace, broken
|
|
14
|
+
* anywhere), links, or drawn by code (`renderers`, by a fact's `key`). Narrower than 420 pixels, labels go over
|
|
15
|
+
* their values.
|
|
16
|
+
*/
|
|
17
|
+
var JlFacts = class extends JlElement {
|
|
18
|
+
static selector = "jl-facts";
|
|
19
|
+
items = this.useChange([], () => this.render(), {
|
|
20
|
+
attribute: "items",
|
|
21
|
+
parse: jsonOf([])
|
|
22
|
+
});
|
|
23
|
+
orientation = this.useChange("horizontal", (value) => this.reflect("orientation", value), {
|
|
24
|
+
attribute: "orientation",
|
|
25
|
+
parse: oneOf(FACTS_ORIENTATIONS, "horizontal")
|
|
26
|
+
});
|
|
27
|
+
/** Draws the values of some facts by code, by their `key`: `{ type: (fact) => <JlBadge label={…} /> }`. */
|
|
28
|
+
renderers = this.useChange({}, () => this.render());
|
|
29
|
+
/** The list's name for screen readers. */
|
|
30
|
+
label = this.useChange("", (value) => setOrRemove(this._refs.list, "aria-label", value), { attribute: "label" });
|
|
31
|
+
_refs = this.useRefs((ref) => ({ list: ref("dl") }));
|
|
32
|
+
templateHtml() {
|
|
33
|
+
return /* @__PURE__ */ jsx("dl", {
|
|
34
|
+
class: "facts",
|
|
35
|
+
attrs: { part: "base" }
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
templateCss() {
|
|
39
|
+
return css`${BASE_STYLES}${jl_facts_default}`;
|
|
40
|
+
}
|
|
41
|
+
render() {
|
|
42
|
+
const shown = this.items.get().filter(hasValue);
|
|
43
|
+
this._refs.list.replaceChildren(...shown.map((fact) => /* @__PURE__ */ jsxs("div", {
|
|
44
|
+
class: "fact",
|
|
45
|
+
children: [/* @__PURE__ */ jsx("dt", { children: fact.label }), /* @__PURE__ */ jsx("dd", {
|
|
46
|
+
title: fact.hint,
|
|
47
|
+
children: this.valueNode(fact)
|
|
48
|
+
})]
|
|
49
|
+
})));
|
|
50
|
+
this.reflect("empty", shown.length === 0);
|
|
51
|
+
}
|
|
52
|
+
valueNode(fact) {
|
|
53
|
+
const renderer = fact.key === void 0 ? void 0 : this.renderers.get()[fact.key];
|
|
54
|
+
if (renderer !== void 0) return renderer(fact);
|
|
55
|
+
const text = String(fact.value);
|
|
56
|
+
const content = fact.code ? /* @__PURE__ */ jsx("code", { children: text }) : document.createTextNode(text);
|
|
57
|
+
return fact.href === void 0 ? content : /* @__PURE__ */ jsx("a", {
|
|
58
|
+
href: fact.href,
|
|
59
|
+
children: content
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
JlFacts.register();
|
|
64
|
+
export { FACTS_ORIENTATIONS, JlFacts };
|
|
@@ -61,6 +61,11 @@ export declare class JlFileDrop extends JlFormControl {
|
|
|
61
61
|
focus(options?: FocusOptions): void;
|
|
62
62
|
/** Opens the file chooser. */
|
|
63
63
|
browse(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Takes files as a drop would, such as files dropped elsewhere on the page: the ones it accepts join the list (or
|
|
66
|
+
* replace the file there) and fire `change`, the others say why not.
|
|
67
|
+
*/
|
|
68
|
+
add(files: readonly File[]): void;
|
|
64
69
|
/** Takes every file back, and empties the list of refused ones. */
|
|
65
70
|
clear(): void;
|
|
66
71
|
templateHtml(): Node;
|
|
@@ -3,6 +3,7 @@ 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 { jlText } from "../lib/texts.js";
|
|
6
7
|
import { JlIcon } from "../atoms/jl-icon.js";
|
|
7
8
|
import { setOrRemove, uniqueId } from "../lib/dom.js";
|
|
8
9
|
import { pageLocale } from "../lib/locale.js";
|
|
@@ -30,15 +31,10 @@ var ALERT_ICON_SIZE = 14;
|
|
|
30
31
|
var NO_LIMIT = 0;
|
|
31
32
|
var FILES_TYPE = "Files";
|
|
32
33
|
var IMAGE_FAMILY = "image/";
|
|
33
|
-
var VALUE_MISSING = "Choose a file.";
|
|
34
|
-
var DEFAULT_TEXT = {
|
|
35
|
-
multiple: "Drop files here",
|
|
36
|
-
single: "Drop a file here"
|
|
37
|
-
};
|
|
38
34
|
var REASONS = {
|
|
39
|
-
count: () => "
|
|
40
|
-
size: (rules) =>
|
|
41
|
-
type: (rules) =>
|
|
35
|
+
count: () => jlText("fileTooMany"),
|
|
36
|
+
size: (rules) => jlText("fileTooLarge", { size: formatFileSize(rules.maxSize, pageLocale()) }),
|
|
37
|
+
type: (rules) => jlText("fileWrongType", { types: describeAccept(rules.accept) })
|
|
42
38
|
};
|
|
43
39
|
var hasFiles = (event) => event.dataTransfer?.types.includes(FILES_TYPE) ?? false;
|
|
44
40
|
/**
|
|
@@ -114,6 +110,13 @@ var JlFileDrop = class extends JlFormControl {
|
|
|
114
110
|
if (!this.disabled.get()) this._refs.picker.click();
|
|
115
111
|
});
|
|
116
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Takes files as a drop would, such as files dropped elsewhere on the page: the ones it accepts join the list (or
|
|
115
|
+
* replace the file there) and fire `change`, the others say why not.
|
|
116
|
+
*/
|
|
117
|
+
add(files) {
|
|
118
|
+
this.whenRendered(() => this.take([...files]));
|
|
119
|
+
}
|
|
117
120
|
/** Takes every file back, and empties the list of refused ones. */
|
|
118
121
|
clear() {
|
|
119
122
|
this.whenRendered(() => {
|
|
@@ -148,7 +151,7 @@ var JlFileDrop = class extends JlFormControl {
|
|
|
148
151
|
class: "browse",
|
|
149
152
|
type: "button",
|
|
150
153
|
"aria-describedby": this._ids.hint,
|
|
151
|
-
children: "
|
|
154
|
+
children: jlText("browse")
|
|
152
155
|
})
|
|
153
156
|
]
|
|
154
157
|
}),
|
|
@@ -165,7 +168,7 @@ var JlFileDrop = class extends JlFormControl {
|
|
|
165
168
|
}),
|
|
166
169
|
/* @__PURE__ */ jsx("ul", {
|
|
167
170
|
class: "files",
|
|
168
|
-
"aria-label": "
|
|
171
|
+
"aria-label": jlText("chosenFiles"),
|
|
169
172
|
hidden: true
|
|
170
173
|
})
|
|
171
174
|
] });
|
|
@@ -297,7 +300,7 @@ var JlFileDrop = class extends JlFormControl {
|
|
|
297
300
|
editable ? /* @__PURE__ */ jsx("button", {
|
|
298
301
|
class: "file-remove",
|
|
299
302
|
type: "button",
|
|
300
|
-
"aria-label":
|
|
303
|
+
"aria-label": jlText("removeNamed", { name: file.name }),
|
|
301
304
|
"data-index": String(index),
|
|
302
305
|
children: /* @__PURE__ */ jsx(JlIcon, {
|
|
303
306
|
name: "x",
|
|
@@ -327,14 +330,14 @@ var JlFileDrop = class extends JlFormControl {
|
|
|
327
330
|
}
|
|
328
331
|
renderText() {
|
|
329
332
|
const text = this.text.get();
|
|
330
|
-
const fallback = this.multiple.get() ?
|
|
333
|
+
const fallback = jlText(this.multiple.get() ? "dropFiles" : "dropFile");
|
|
331
334
|
this._refs.text.textContent = text === "" ? fallback : text;
|
|
332
335
|
}
|
|
333
336
|
/** What it takes, under the text: `PNG, JPG · up to 5 MB`. */
|
|
334
337
|
renderHint() {
|
|
335
338
|
const types = describeAccept(this.accept.get());
|
|
336
339
|
const maxSize = this.maxSize.get();
|
|
337
|
-
const hint = [types, maxSize === NO_LIMIT ? "" :
|
|
340
|
+
const hint = [types, maxSize === NO_LIMIT ? "" : jlText("fileUpTo", { size: formatFileSize(maxSize, pageLocale()) })].filter((part) => part !== "").join(" · ");
|
|
338
341
|
this._refs.hint.textContent = hint;
|
|
339
342
|
this._refs.hint.hidden = hint === "";
|
|
340
343
|
}
|
|
@@ -351,7 +354,7 @@ var JlFileDrop = class extends JlFormControl {
|
|
|
351
354
|
}
|
|
352
355
|
renderValidity() {
|
|
353
356
|
if (this.required.get() && this._state.files.length === 0) {
|
|
354
|
-
this.internals.setValidity({ valueMissing: true },
|
|
357
|
+
this.internals.setValidity({ valueMissing: true }, jlText("chooseFile"), this._refs.browse);
|
|
355
358
|
return;
|
|
356
359
|
}
|
|
357
360
|
this.internals.setValidity({});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// src/molecules/jl-input-list.css?raw
|
|
2
|
+
var jl_input_list_default = ":host {\n display: block;\n}\n\n.list,\n.rows {\n display: grid;\n gap: var(--jl-space-2);\n}\n\n.row {\n display: flex;\n align-items: start;\n gap: var(--jl-space-1);\n}\n\n.row > :first-child {\n flex: 1;\n min-width: 0;\n}\n\n[hidden] {\n display: none;\n}\n";
|
|
3
|
+
export { jl_input_list_default as default };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { JlElement } from '../base/jl-element';
|
|
2
|
+
import type { InputType } from '../types/jl-input.types';
|
|
3
|
+
import type { SelectOption } from '../types/jl-select.types';
|
|
4
|
+
/**
|
|
5
|
+
* A field with several values, each in its own row: typed in an input (or a text area when `multiline`), or picked
|
|
6
|
+
* from `options`. People add a row and remove one; `max` caps how many. `values` holds those filled in, in their
|
|
7
|
+
* order, and `change` fires with them. Put it in a `jl-field` for its label, hint and error.
|
|
8
|
+
*/
|
|
9
|
+
export declare class JlInputList extends JlElement {
|
|
10
|
+
static readonly selector = "jl-input-list";
|
|
11
|
+
readonly events: Readonly<import("@jadis/core").UseEventsHandler<{
|
|
12
|
+
change: string[];
|
|
13
|
+
}>>;
|
|
14
|
+
/** The values filled in, in their order; rows left empty are not among them. */
|
|
15
|
+
readonly values: Readonly<Readonly<{
|
|
16
|
+
get(): string[];
|
|
17
|
+
set(setter: string[] | ((prevState: string[]) => string[])): void;
|
|
18
|
+
}>>;
|
|
19
|
+
/** The input of each row: text, a number, a date, a link… */
|
|
20
|
+
readonly type: Readonly<Readonly<{
|
|
21
|
+
get(): InputType;
|
|
22
|
+
set(setter: ((prevState: InputType) => InputType) | InputType): void;
|
|
23
|
+
}>>;
|
|
24
|
+
/** Each value on several lines, in a text area. */
|
|
25
|
+
readonly multiline: Readonly<Readonly<{
|
|
26
|
+
get(): boolean;
|
|
27
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
28
|
+
}>>;
|
|
29
|
+
/** The values to pick from, each row a select; none lets people type. */
|
|
30
|
+
readonly options: Readonly<Readonly<{
|
|
31
|
+
get(): SelectOption[];
|
|
32
|
+
set(setter: SelectOption[] | ((prevState: SelectOption[]) => SelectOption[])): void;
|
|
33
|
+
}>>;
|
|
34
|
+
/** How many values it takes at most; 0 takes any number. */
|
|
35
|
+
readonly max: Readonly<Readonly<{
|
|
36
|
+
get(): number;
|
|
37
|
+
set(setter: number | ((prevState: number) => number)): void;
|
|
38
|
+
}>>;
|
|
39
|
+
readonly placeholder: Readonly<Readonly<{
|
|
40
|
+
get(): string;
|
|
41
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
42
|
+
}>>;
|
|
43
|
+
/** The name of each row for screen readers: the field's label. */
|
|
44
|
+
readonly label: Readonly<Readonly<{
|
|
45
|
+
get(): string;
|
|
46
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
47
|
+
}>>;
|
|
48
|
+
readonly disabled: Readonly<Readonly<{
|
|
49
|
+
get(): boolean;
|
|
50
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
51
|
+
}>>;
|
|
52
|
+
/** The add button's text. */
|
|
53
|
+
readonly addLabel: Readonly<Readonly<{
|
|
54
|
+
get(): string;
|
|
55
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
56
|
+
}>>;
|
|
57
|
+
/** The text a screen reader says for each row's remove button. */
|
|
58
|
+
readonly removeLabel: Readonly<Readonly<{
|
|
59
|
+
get(): string;
|
|
60
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
61
|
+
}>>;
|
|
62
|
+
private readonly _state;
|
|
63
|
+
private readonly _refs;
|
|
64
|
+
templateHtml(): Node;
|
|
65
|
+
templateCss(): string;
|
|
66
|
+
onConnect(): void;
|
|
67
|
+
/** Values set from outside start the rows again; those the rows themselves gave change nothing. */
|
|
68
|
+
private follow;
|
|
69
|
+
private renderRows;
|
|
70
|
+
private renderAdd;
|
|
71
|
+
private renderDisabled;
|
|
72
|
+
private row;
|
|
73
|
+
/** The control of a row: a select among the options, a text area, or an input of the type; `onInput` hears it. */
|
|
74
|
+
private control;
|
|
75
|
+
private change;
|
|
76
|
+
private addRow;
|
|
77
|
+
private removeRow;
|
|
78
|
+
private publish;
|
|
79
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { BASE_STYLES } from "../styles/shared.js";
|
|
2
|
+
import { JlElement } from "../base/jl-element.js";
|
|
3
|
+
import { clampedNumber, jsonOf, oneOf } from "../lib/attributes.js";
|
|
4
|
+
import { registerIcons } from "../lib/icons.js";
|
|
5
|
+
import { jlText } from "../lib/texts.js";
|
|
6
|
+
import { JlButton } from "../atoms/jl-button.js";
|
|
7
|
+
import { INPUT_TYPES, JlInput } from "../atoms/jl-input.js";
|
|
8
|
+
import { JlSelect } from "../atoms/jl-select.js";
|
|
9
|
+
import { JlTextarea } from "../atoms/jl-textarea.js";
|
|
10
|
+
import { canAddRow, filledValues, rowsOf, sameValues, withRowAt, withoutRowAt } from "../lib/input-list.js";
|
|
11
|
+
import jl_input_list_default from "./jl-input-list.css_raw.js";
|
|
12
|
+
import { css } from "@jadis/core";
|
|
13
|
+
import { Plus, X } from "lucide";
|
|
14
|
+
import { jsx, jsxs } from "@jadis/core/jsx-runtime";
|
|
15
|
+
// src/molecules/jl-input-list.tsx
|
|
16
|
+
registerIcons({
|
|
17
|
+
plus: Plus,
|
|
18
|
+
x: X
|
|
19
|
+
});
|
|
20
|
+
var NO_LIMIT = 0;
|
|
21
|
+
var MAX_LIMIT = 1e3;
|
|
22
|
+
/**
|
|
23
|
+
* A field with several values, each in its own row: typed in an input (or a text area when `multiline`), or picked
|
|
24
|
+
* from `options`. People add a row and remove one; `max` caps how many. `values` holds those filled in, in their
|
|
25
|
+
* order, and `change` fires with them. Put it in a `jl-field` for its label, hint and error.
|
|
26
|
+
*/
|
|
27
|
+
var JlInputList = class extends JlElement {
|
|
28
|
+
static selector = "jl-input-list";
|
|
29
|
+
events = this.useEvents();
|
|
30
|
+
/** The values filled in, in their order; rows left empty are not among them. */
|
|
31
|
+
values = this.useChange([], (values) => this.follow(values), {
|
|
32
|
+
attribute: "values",
|
|
33
|
+
parse: jsonOf([])
|
|
34
|
+
});
|
|
35
|
+
/** The input of each row: text, a number, a date, a link… */
|
|
36
|
+
type = this.useChange("text", () => this.renderRows(), {
|
|
37
|
+
attribute: "type",
|
|
38
|
+
parse: oneOf(INPUT_TYPES, "text")
|
|
39
|
+
});
|
|
40
|
+
/** Each value on several lines, in a text area. */
|
|
41
|
+
multiline = this.useChange(false, () => this.renderRows(), { attribute: "multiline" });
|
|
42
|
+
/** The values to pick from, each row a select; none lets people type. */
|
|
43
|
+
options = this.useChange([], () => this.renderRows(), {
|
|
44
|
+
attribute: "options",
|
|
45
|
+
parse: jsonOf([])
|
|
46
|
+
});
|
|
47
|
+
/** How many values it takes at most; 0 takes any number. */
|
|
48
|
+
max = this.useChange(NO_LIMIT, () => this.renderAdd(), {
|
|
49
|
+
attribute: "max",
|
|
50
|
+
parse: clampedNumber(NO_LIMIT, NO_LIMIT, MAX_LIMIT)
|
|
51
|
+
});
|
|
52
|
+
placeholder = this.useChange("", () => this.renderRows(), { attribute: "placeholder" });
|
|
53
|
+
/** The name of each row for screen readers: the field's label. */
|
|
54
|
+
label = this.useChange("", () => this.renderRows(), { attribute: "label" });
|
|
55
|
+
disabled = this.useChange(false, (value) => this.renderDisabled(value), { attribute: "disabled" });
|
|
56
|
+
/** The add button's text. */
|
|
57
|
+
addLabel = this.useChange(jlText("addValue"), () => this.renderAdd(), { attribute: "add-label" });
|
|
58
|
+
/** The text a screen reader says for each row's remove button. */
|
|
59
|
+
removeLabel = this.useChange(jlText("removeValue"), () => this.renderRows(), { attribute: "remove-label" });
|
|
60
|
+
_state = { rows: [""] };
|
|
61
|
+
_refs = this.useRefs((ref) => ({
|
|
62
|
+
add: ref(".add"),
|
|
63
|
+
rows: ref(".rows")
|
|
64
|
+
}));
|
|
65
|
+
templateHtml() {
|
|
66
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
67
|
+
class: "list",
|
|
68
|
+
attrs: { part: "base" },
|
|
69
|
+
children: [/* @__PURE__ */ jsx("div", { class: "rows" }), /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(JlButton, {
|
|
70
|
+
class: "add",
|
|
71
|
+
variant: "ghost",
|
|
72
|
+
size: "sm",
|
|
73
|
+
icon: "plus"
|
|
74
|
+
}) })]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
templateCss() {
|
|
78
|
+
return css`${BASE_STYLES}${jl_input_list_default}`;
|
|
79
|
+
}
|
|
80
|
+
onConnect() {
|
|
81
|
+
this._refs.add.events.register("press", () => this.addRow());
|
|
82
|
+
}
|
|
83
|
+
/** Values set from outside start the rows again; those the rows themselves gave change nothing. */
|
|
84
|
+
follow(values) {
|
|
85
|
+
if (sameValues(values, filledValues(this._state.rows))) return;
|
|
86
|
+
this._state.rows = rowsOf(values);
|
|
87
|
+
this.renderRows();
|
|
88
|
+
}
|
|
89
|
+
renderRows() {
|
|
90
|
+
this._refs.rows.replaceChildren(...this._state.rows.map((value, index) => this.row(value, index)));
|
|
91
|
+
this.renderAdd();
|
|
92
|
+
}
|
|
93
|
+
renderAdd() {
|
|
94
|
+
const add = this._refs.add;
|
|
95
|
+
add.label.set(this.addLabel.get());
|
|
96
|
+
add.hidden = this.disabled.get() || !canAddRow(this._state.rows.length, this.max.get());
|
|
97
|
+
}
|
|
98
|
+
renderDisabled(disabled) {
|
|
99
|
+
this.reflect("disabled", disabled);
|
|
100
|
+
this.renderRows();
|
|
101
|
+
}
|
|
102
|
+
row(value, index) {
|
|
103
|
+
const control = this.control(value, (text) => this.change(index, text));
|
|
104
|
+
const remove = /* @__PURE__ */ jsx(JlButton, {
|
|
105
|
+
class: "remove",
|
|
106
|
+
variant: "ghost",
|
|
107
|
+
size: "sm",
|
|
108
|
+
icon: "x",
|
|
109
|
+
hint: this.removeLabel.get()
|
|
110
|
+
});
|
|
111
|
+
remove.events.register("press", () => this.removeRow(index));
|
|
112
|
+
remove.hidden = this.disabled.get() || this._state.rows.length === 1;
|
|
113
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
114
|
+
class: "row",
|
|
115
|
+
children: [control, remove]
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/** The control of a row: a select among the options, a text area, or an input of the type; `onInput` hears it. */
|
|
119
|
+
control(value, onInput) {
|
|
120
|
+
const label = this.label.get();
|
|
121
|
+
const disabled = this.disabled.get();
|
|
122
|
+
const options = this.options.get();
|
|
123
|
+
if (options.length > 0) {
|
|
124
|
+
const select = /* @__PURE__ */ jsx(JlSelect, {
|
|
125
|
+
label,
|
|
126
|
+
options,
|
|
127
|
+
disabled
|
|
128
|
+
});
|
|
129
|
+
select.value.set(value);
|
|
130
|
+
select.events.register("change", onInput);
|
|
131
|
+
return select;
|
|
132
|
+
}
|
|
133
|
+
if (this.multiline.get()) {
|
|
134
|
+
const area = /* @__PURE__ */ jsx(JlTextarea, {
|
|
135
|
+
label,
|
|
136
|
+
value,
|
|
137
|
+
placeholder: this.placeholder.get(),
|
|
138
|
+
disabled,
|
|
139
|
+
autosize: true
|
|
140
|
+
});
|
|
141
|
+
area.events.register("input", onInput);
|
|
142
|
+
return area;
|
|
143
|
+
}
|
|
144
|
+
const input = /* @__PURE__ */ jsx(JlInput, {
|
|
145
|
+
label,
|
|
146
|
+
value,
|
|
147
|
+
type: this.type.get(),
|
|
148
|
+
placeholder: this.placeholder.get(),
|
|
149
|
+
disabled
|
|
150
|
+
});
|
|
151
|
+
input.events.register("input", onInput);
|
|
152
|
+
return input;
|
|
153
|
+
}
|
|
154
|
+
change(index, text) {
|
|
155
|
+
this._state.rows = withRowAt(this._state.rows, index, text);
|
|
156
|
+
this.publish();
|
|
157
|
+
}
|
|
158
|
+
addRow() {
|
|
159
|
+
if (!canAddRow(this._state.rows.length, this.max.get())) return;
|
|
160
|
+
this._state.rows = [...this._state.rows, ""];
|
|
161
|
+
this.renderRows();
|
|
162
|
+
const added = this._refs.rows.lastElementChild?.firstElementChild;
|
|
163
|
+
if (added instanceof HTMLElement) added.focus();
|
|
164
|
+
}
|
|
165
|
+
removeRow(index) {
|
|
166
|
+
this._state.rows = withoutRowAt(this._state.rows, index);
|
|
167
|
+
this.renderRows();
|
|
168
|
+
this.publish();
|
|
169
|
+
}
|
|
170
|
+
publish() {
|
|
171
|
+
const values = filledValues(this._state.rows);
|
|
172
|
+
this.values.set(values);
|
|
173
|
+
this.events.emit("change", values);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
JlInputList.register();
|
|
177
|
+
export { JlInputList };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// src/molecules/jl-load-more.css?raw
|
|
2
|
+
var jl_load_more_default = ":host {\n display: block;\n}\n\n:host([complete]) {\n display: none;\n}\n\n.more {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n align-items: center;\n justify-content: center;\n padding: 12px 0;\n color: var(--jl-muted);\n font-size: var(--jl-font-sm);\n}\n";
|
|
3
|
+
export { jl_load_more_default as default };
|