@syware/jlib 0.2.0 → 0.4.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/README.md +1 -0
- package/dist/all.js +5 -3
- package/dist/atoms/jl-button.css_raw.js +1 -1
- package/dist/atoms/jl-button.d.ts +19 -0
- package/dist/atoms/jl-button.js +75 -23
- package/dist/atoms/jl-chip.css_raw.js +1 -1
- package/dist/atoms/jl-input.d.ts +6 -0
- package/dist/atoms/jl-input.js +4 -0
- package/dist/atoms/jl-select.d.ts +6 -0
- package/dist/atoms/jl-select.js +18 -1
- package/dist/atoms/jl-switch.d.ts +12 -1
- package/dist/atoms/jl-switch.js +11 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +4 -2
- package/dist/lib/attributes.d.ts +5 -0
- package/dist/lib/attributes.js +13 -1
- package/dist/lib/flow.d.ts +10 -1
- package/dist/lib/flow.js +26 -9
- package/dist/lib/tokens.d.ts +2 -1
- package/dist/lib/tokens.js +13 -4
- package/dist/molecules/jl-accordion-item.css_raw.js +1 -1
- package/dist/molecules/jl-accordion-item.d.ts +6 -1
- package/dist/molecules/jl-accordion-item.js +21 -6
- package/dist/molecules/jl-alert.d.ts +10 -3
- package/dist/molecules/jl-alert.js +23 -6
- package/dist/molecules/jl-chip-group.css_raw.js +3 -0
- package/dist/molecules/jl-chip-group.d.ts +81 -0
- package/dist/molecules/jl-chip-group.js +259 -0
- package/dist/molecules/jl-search.d.ts +3 -1
- package/dist/molecules/jl-search.js +8 -6
- package/dist/molecules/jl-segmented.css_raw.js +1 -1
- package/dist/molecules/jl-segmented.js +14 -7
- package/dist/molecules/jl-stat.css_raw.js +1 -1
- package/dist/molecules/jl-stat.d.ts +6 -0
- package/dist/molecules/jl-stat.js +6 -0
- package/dist/molecules/jl-tag-input.css_raw.js +1 -1
- package/dist/organisms/jl-flow.css_raw.js +1 -1
- package/dist/organisms/jl-flow.d.ts +28 -5
- package/dist/organisms/jl-flow.js +138 -33
- package/dist/organisms/jl-tab-bar.css_raw.js +3 -0
- package/dist/organisms/jl-tab-bar.d.ts +43 -0
- package/dist/organisms/jl-tab-bar.js +123 -0
- package/dist/templates/jl-app-shell.css_raw.js +1 -1
- package/dist/templates/jl-app-shell.d.ts +8 -2
- package/dist/templates/jl-app-shell.js +32 -5
- package/dist/types/jl-chip-group.types.d.ts +10 -0
- package/dist/types/jl-flow.types.d.ts +17 -0
- package/dist/types/jl-segmented.types.d.ts +2 -0
- package/dist/types/jl-tab-bar.types.d.ts +11 -0
- package/package.json +1 -1
package/dist/lib/flow.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
// src/lib/flow.ts
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* A node's box: fixed, so that every anchor and edge is computed without measuring the page. A node with a detail
|
|
4
|
+
* line is taller.
|
|
5
|
+
*/
|
|
3
6
|
var FLOW_NODE = {
|
|
4
7
|
height: 64,
|
|
8
|
+
heightWithDetail: 96,
|
|
5
9
|
width: 240
|
|
6
10
|
};
|
|
7
11
|
var FLOW_ZOOM = {
|
|
@@ -20,6 +24,7 @@ var REVEAL_MARGIN = 48;
|
|
|
20
24
|
var ROW_GAP = 72;
|
|
21
25
|
var COLUMN_GAP = 32;
|
|
22
26
|
var portsOf = (node) => node.ports ?? [DEFAULT_PORT];
|
|
27
|
+
var nodeHeight = (node) => node.detail ? FLOW_NODE.heightWithDetail : FLOW_NODE.height;
|
|
23
28
|
/** The exit an edge leaves by: its own, or its node's first. */
|
|
24
29
|
var portOfEdge = (node, edge) => edge.port ?? portsOf(node)[0]?.id ?? "";
|
|
25
30
|
var nodeById = (graph, id) => graph.nodes.find((node) => node.id === id);
|
|
@@ -36,7 +41,7 @@ function outputAnchor(node, portId) {
|
|
|
36
41
|
if (index < 0) return;
|
|
37
42
|
return {
|
|
38
43
|
x: node.position.x + portX(index, ports.length),
|
|
39
|
-
y: node.position.y +
|
|
44
|
+
y: node.position.y + nodeHeight(node) + PORT_DROP
|
|
40
45
|
};
|
|
41
46
|
}
|
|
42
47
|
var inputAnchor = (node) => ({
|
|
@@ -152,7 +157,18 @@ function edgeEnds(graph, edge) {
|
|
|
152
157
|
tone
|
|
153
158
|
};
|
|
154
159
|
}
|
|
155
|
-
var isInside = (point, node) => point.x >= node.position.x && point.x <= node.position.x + FLOW_NODE.width && point.y >= node.position.y && point.y <= node.position.y +
|
|
160
|
+
var isInside = (point, node) => point.x >= node.position.x && point.x <= node.position.x + FLOW_NODE.width && point.y >= node.position.y && point.y <= node.position.y + nodeHeight(node);
|
|
161
|
+
/** A run went this way: its node left by the edge's exit, and the node it leads to ran too. */
|
|
162
|
+
function isEdgeTaken(graph, edge) {
|
|
163
|
+
const source = nodeById(graph, edge.from);
|
|
164
|
+
const target = nodeById(graph, edge.to);
|
|
165
|
+
return source?.taken !== void 0 && source.taken === portOfEdge(source, edge) && target?.status !== void 0;
|
|
166
|
+
}
|
|
167
|
+
/** The position of a node centred on a point: where something dropped there lands. */
|
|
168
|
+
var centeredAt = (point) => snap({
|
|
169
|
+
x: point.x - FLOW_NODE.width / 2,
|
|
170
|
+
y: point.y - FLOW_NODE.height / 2
|
|
171
|
+
});
|
|
156
172
|
/** The node under a point, other than `except`. */
|
|
157
173
|
var nodeAt = (graph, point, except = "") => graph.nodes.find((node) => node.id !== except && isInside(point, node));
|
|
158
174
|
var isPortFree = (graph, node, portId) => !graph.edges.some((edge) => edge.from === node.id && portOfEdge(node, edge) === portId);
|
|
@@ -188,8 +204,8 @@ var removeEdge = (graph, id) => ({
|
|
|
188
204
|
...graph,
|
|
189
205
|
edges: graph.edges.filter((edge) => edge.id !== id)
|
|
190
206
|
});
|
|
191
|
-
var ROW = FLOW_NODE.height + ROW_GAP;
|
|
192
207
|
var COLUMN = FLOW_NODE.width + COLUMN_GAP;
|
|
208
|
+
var rowOfFlow = (graph) => Math.max(FLOW_NODE.height, ...graph.nodes.map(nodeHeight)) + ROW_GAP;
|
|
193
209
|
/** How far across a node hangs from its parent's exit: the exits share the row out, side by side, around it. */
|
|
194
210
|
function portShift(node, portId) {
|
|
195
211
|
const ports = portsOf(node);
|
|
@@ -204,7 +220,7 @@ function positionAfter(graph, from, portId) {
|
|
|
204
220
|
};
|
|
205
221
|
return snap({
|
|
206
222
|
x: source.position.x + portShift(source, portId),
|
|
207
|
-
y: source.position.y +
|
|
223
|
+
y: source.position.y + nodeHeight(source) + ROW_GAP
|
|
208
224
|
});
|
|
209
225
|
}
|
|
210
226
|
/** The edges that close a loop, found walking from the starts: the layout leaves them out, or a loop has no top. */
|
|
@@ -267,6 +283,7 @@ function layoutFlow(graph) {
|
|
|
267
283
|
const loops = loopEdges(graph);
|
|
268
284
|
const edges = graph.edges.filter((edge) => !loops.has(edge.id) && ids.has(edge.from) && ids.has(edge.to));
|
|
269
285
|
const rows = rowsOf(graph, edges);
|
|
286
|
+
const row = rowOfFlow(graph);
|
|
270
287
|
const placed = /* @__PURE__ */ new Map();
|
|
271
288
|
for (const row of [...new Set(rows.values())].sort((a, b) => a - b)) placeRow(graph.nodes.filter((node) => rows.get(node.id) === row).map((node) => ({
|
|
272
289
|
id: node.id,
|
|
@@ -278,7 +295,7 @@ function layoutFlow(graph) {
|
|
|
278
295
|
...node,
|
|
279
296
|
position: snap({
|
|
280
297
|
x: placed.get(node.id) ?? 0,
|
|
281
|
-
y: (rows.get(node.id) ?? 0) *
|
|
298
|
+
y: (rows.get(node.id) ?? 0) * row
|
|
282
299
|
})
|
|
283
300
|
}))
|
|
284
301
|
};
|
|
@@ -309,7 +326,7 @@ function fitView(nodes, width, height) {
|
|
|
309
326
|
const left = Math.min(...nodes.map((node) => node.position.x));
|
|
310
327
|
const top = Math.min(...nodes.map((node) => node.position.y));
|
|
311
328
|
const right = Math.max(...nodes.map((node) => node.position.x + FLOW_NODE.width));
|
|
312
|
-
const bottom = Math.max(...nodes.map((node) => node.position.y +
|
|
329
|
+
const bottom = Math.max(...nodes.map((node) => node.position.y + nodeHeight(node) + PORT_DROP));
|
|
313
330
|
const zoom = clampZoom(Math.min(1, (width - 96) / (right - left), (height - 96) / (bottom - top)));
|
|
314
331
|
return {
|
|
315
332
|
x: width / 2 - (left + right) / 2 * zoom,
|
|
@@ -329,8 +346,8 @@ function revealIn(viewport, node, width, height) {
|
|
|
329
346
|
const top = viewport.y + node.position.y * zoom;
|
|
330
347
|
return {
|
|
331
348
|
x: viewport.x + shiftInto(left, left + FLOW_NODE.width * zoom, width),
|
|
332
|
-
y: viewport.y + shiftInto(top, top + (
|
|
349
|
+
y: viewport.y + shiftInto(top, top + (nodeHeight(node) + PORT_DROP) * zoom, height),
|
|
333
350
|
zoom
|
|
334
351
|
};
|
|
335
352
|
}
|
|
336
|
-
export { DEFAULT_PORT, FLOW_NODE, FLOW_ZOOM, connect, edgeEnds, edgeMidpoint, edgePath, edgeRoute, fitView, inputAnchor, isPortFree, layoutFlow, moveNode, nodeAt, outputAnchor, portOfEdge, portX, portsOf, positionAfter, removeEdge, removeNode, revealIn, roundedPath, snap, toWorld, zoomAt };
|
|
353
|
+
export { DEFAULT_PORT, FLOW_NODE, FLOW_ZOOM, centeredAt, connect, edgeEnds, edgeMidpoint, edgePath, edgeRoute, fitView, inputAnchor, isEdgeTaken, isPortFree, layoutFlow, moveNode, nodeAt, nodeHeight, outputAnchor, portOfEdge, portX, portsOf, positionAfter, removeEdge, removeNode, revealIn, roundedPath, snap, toWorld, zoomAt };
|
package/dist/lib/tokens.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Gives the document the design tokens, once. They live in the `jlib` cascade layer, so the page overrides any of
|
|
3
|
-
* them with a plain rule: `:root { --jl-accent: #7c3aed; }`.
|
|
3
|
+
* them with a plain rule: `:root { --jl-accent: #7c3aed; }`. Where the document takes no constructed stylesheet, a
|
|
4
|
+
* style element in its head holds them instead.
|
|
4
5
|
*/
|
|
5
6
|
export declare function adoptTokens(): void;
|
package/dist/lib/tokens.js
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
import { TOKEN_STYLES } from "../styles/shared.js";
|
|
2
2
|
// src/lib/tokens.ts
|
|
3
|
-
var adopted = {
|
|
3
|
+
var adopted = { done: false };
|
|
4
|
+
/** Whether the document takes constructed stylesheets: jsdom, which runs many apps' unit tests, does not. */
|
|
5
|
+
var takesSheets = () => typeof CSSStyleSheet !== "undefined" && typeof CSSStyleSheet.prototype.replaceSync === "function" && Array.isArray(document.adoptedStyleSheets);
|
|
4
6
|
/**
|
|
5
7
|
* Gives the document the design tokens, once. They live in the `jlib` cascade layer, so the page overrides any of
|
|
6
|
-
* them with a plain rule: `:root { --jl-accent: #7c3aed; }`.
|
|
8
|
+
* them with a plain rule: `:root { --jl-accent: #7c3aed; }`. Where the document takes no constructed stylesheet, a
|
|
9
|
+
* style element in its head holds them instead.
|
|
7
10
|
*/
|
|
8
11
|
function adoptTokens() {
|
|
9
|
-
if (adopted.
|
|
12
|
+
if (adopted.done || typeof document === "undefined") return;
|
|
13
|
+
adopted.done = true;
|
|
14
|
+
if (!takesSheets()) {
|
|
15
|
+
const style = document.createElement("style");
|
|
16
|
+
style.textContent = TOKEN_STYLES;
|
|
17
|
+
document.head.append(style);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
10
20
|
const sheet = new CSSStyleSheet();
|
|
11
21
|
sheet.replaceSync(TOKEN_STYLES);
|
|
12
22
|
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
|
13
|
-
adopted.sheet = sheet;
|
|
14
23
|
}
|
|
15
24
|
export { adoptTokens };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// src/molecules/jl-accordion-item.css?raw
|
|
2
|
-
var jl_accordion_item_default = ":host {\n display: block;\n border-bottom: 1px solid var(--jl-line);\n /* Lets the content's height animate from 0 to auto. */\n interpolate-size: allow-keywords;\n --pad-x: 0px;\n}\n\nsummary {\n display: flex;\n align-items: center;\n gap: 10px;\n min-height: 48px;\n padding: 12px var(--pad-x);\n border-radius: var(--jl-radius-xs);\n color: var(--jl-text);\n font-size: var(--jl-font-md);\n font-weight: 600;\n line-height: 1.4;\n list-style: none;\n cursor: pointer;\n user-select: none;\n}\n\nsummary::-webkit-details-marker {\n display: none;\n}\n\n.
|
|
2
|
+
var jl_accordion_item_default = ":host {\n display: block;\n border-bottom: 1px solid var(--jl-line);\n /* Lets the content's height animate from 0 to auto. */\n interpolate-size: allow-keywords;\n --pad-x: 0px;\n}\n\nsummary {\n display: flex;\n align-items: center;\n gap: 10px;\n min-height: 48px;\n padding: 12px var(--pad-x);\n border-radius: var(--jl-radius-xs);\n color: var(--jl-text);\n font-size: var(--jl-font-md);\n font-weight: 600;\n line-height: 1.4;\n list-style: none;\n cursor: pointer;\n user-select: none;\n}\n\nsummary::-webkit-details-marker {\n display: none;\n}\n\n.titles {\n display: grid;\n flex: 1;\n min-width: 0;\n}\n\n.description {\n overflow: hidden;\n color: var(--jl-muted);\n font-size: var(--jl-font-sm);\n font-weight: 400;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.icon {\n color: var(--jl-muted);\n}\n\n.chevron {\n color: var(--jl-faint);\n transition: rotate var(--jl-slow) var(--jl-ease);\n}\n\nsummary:hover .chevron {\n color: var(--jl-text);\n}\n\ndetails[open] .chevron {\n rotate: 180deg;\n}\n\nsummary:focus-visible {\n box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--jl-brand) 60%, transparent);\n}\n\n/*\n * The content box is a pseudo-element BASE_STYLES' \"* { box-sizing: inherit }\" does not reach: without this, what\n * it holds (and the slotted fields, which inherit through it) would fall back to content-box.\n */\ndetails::details-content {\n box-sizing: inherit;\n block-size: 0;\n overflow: clip;\n transition:\n block-size var(--jl-slow) var(--jl-ease),\n content-visibility var(--jl-slow) allow-discrete;\n}\n\ndetails[open]::details-content {\n block-size: auto;\n}\n\n.content {\n padding: 0 var(--pad-x) 16px;\n color: var(--jl-text-2);\n font-size: var(--jl-font-md);\n line-height: 1.6;\n}\n\n:host([disabled]) summary {\n color: var(--jl-faint);\n cursor: not-allowed;\n}\n\n:host([disabled]) summary:hover .chevron {\n color: var(--jl-faint);\n}\n";
|
|
3
3
|
export { jl_accordion_item_default as default };
|
|
@@ -12,11 +12,16 @@ export declare class JlAccordionItem extends JlElement {
|
|
|
12
12
|
get(): string;
|
|
13
13
|
set(setter: string | ((prevState: string) => string)): void;
|
|
14
14
|
}>>;
|
|
15
|
+
/** A quieter line under the heading, shown open or closed: what the section holds ("3 fields", "2 pages"). */
|
|
16
|
+
readonly description: Readonly<Readonly<{
|
|
17
|
+
get(): string;
|
|
18
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
19
|
+
}>>;
|
|
15
20
|
readonly open: Readonly<Readonly<{
|
|
16
21
|
get(): boolean;
|
|
17
22
|
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
18
23
|
}>>;
|
|
19
|
-
/** An icon before the heading. */
|
|
24
|
+
/** An icon before the heading; the `icon` slot takes a richer one instead. */
|
|
20
25
|
readonly icon: Readonly<Readonly<{
|
|
21
26
|
get(): string;
|
|
22
27
|
set(setter: string | ((prevState: string) => string)): void;
|
|
@@ -19,8 +19,13 @@ var JlAccordionItem = class extends JlElement {
|
|
|
19
19
|
heading = this.useChange("", (value) => {
|
|
20
20
|
this._refs.heading.textContent = value;
|
|
21
21
|
}, { attribute: "heading" });
|
|
22
|
+
/** A quieter line under the heading, shown open or closed: what the section holds ("3 fields", "2 pages"). */
|
|
23
|
+
description = this.useChange("", (value) => {
|
|
24
|
+
this._refs.description.textContent = value;
|
|
25
|
+
this._refs.description.hidden = value === "";
|
|
26
|
+
}, { attribute: "description" });
|
|
22
27
|
open = this.useChange(false, (value) => this.renderOpen(value), { attribute: "open" });
|
|
23
|
-
/** An icon before the heading. */
|
|
28
|
+
/** An icon before the heading; the `icon` slot takes a richer one instead. */
|
|
24
29
|
icon = this.useChange("", (name) => {
|
|
25
30
|
this._refs.icon.name.set(name);
|
|
26
31
|
this._refs.icon.hidden = name === "";
|
|
@@ -32,6 +37,7 @@ var JlAccordionItem = class extends JlElement {
|
|
|
32
37
|
this._refs.summary.tabIndex = value ? -1 : 0;
|
|
33
38
|
}, { attribute: "disabled" });
|
|
34
39
|
_refs = this.useRefs((ref) => ({
|
|
40
|
+
description: ref(".description"),
|
|
35
41
|
details: ref("details"),
|
|
36
42
|
heading: ref(".heading"),
|
|
37
43
|
icon: ref(".icon"),
|
|
@@ -41,12 +47,21 @@ var JlAccordionItem = class extends JlElement {
|
|
|
41
47
|
return /* @__PURE__ */ jsxs("details", {
|
|
42
48
|
attrs: { part: "base" },
|
|
43
49
|
children: [/* @__PURE__ */ jsxs("summary", { children: [
|
|
44
|
-
/* @__PURE__ */ jsx(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
/* @__PURE__ */ jsx("slot", {
|
|
51
|
+
name: "icon",
|
|
52
|
+
children: /* @__PURE__ */ jsx(JlIcon, {
|
|
53
|
+
class: "icon",
|
|
54
|
+
size: ICON_SIZE,
|
|
55
|
+
hidden: true
|
|
56
|
+
})
|
|
57
|
+
}),
|
|
58
|
+
/* @__PURE__ */ jsxs("span", {
|
|
59
|
+
class: "titles",
|
|
60
|
+
children: [/* @__PURE__ */ jsx("span", { class: "heading" }), /* @__PURE__ */ jsx("span", {
|
|
61
|
+
class: "description",
|
|
62
|
+
hidden: true
|
|
63
|
+
})]
|
|
48
64
|
}),
|
|
49
|
-
/* @__PURE__ */ jsx("span", { class: "heading" }),
|
|
50
65
|
/* @__PURE__ */ jsx(JlIcon, {
|
|
51
66
|
class: "chevron",
|
|
52
67
|
name: "chevron-down",
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { JlElement } from '../base/jl-element';
|
|
2
2
|
import type { Tone } from '../types/options.types';
|
|
3
3
|
/**
|
|
4
|
-
* A message in the flow of the page, coloured by its tone: a tip, a success, a warning, an error. The text
|
|
5
|
-
* in the default slot, buttons in `slot="actions"`. A `dismissible`
|
|
4
|
+
* A message in the flow of the page, coloured by its tone: a tip, a success, a warning, an error. The text is the
|
|
5
|
+
* `description` or goes in the default slot (for links and markup), buttons in `slot="actions"`. A `dismissible`
|
|
6
|
+
* alert has an × that hides it.
|
|
6
7
|
*/
|
|
7
8
|
export declare class JlAlert extends JlElement {
|
|
8
9
|
static readonly selector = "jl-alert";
|
|
@@ -17,6 +18,11 @@ export declare class JlAlert extends JlElement {
|
|
|
17
18
|
get(): string;
|
|
18
19
|
set(setter: string | ((prevState: string) => string)): void;
|
|
19
20
|
}>>;
|
|
21
|
+
/** The message as plain text, for pages that set it from code; richer content goes in the default slot. */
|
|
22
|
+
readonly description: Readonly<Readonly<{
|
|
23
|
+
get(): string;
|
|
24
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
25
|
+
}>>;
|
|
20
26
|
/** An icon name in place of the tone's own; `none` shows no icon. */
|
|
21
27
|
readonly icon: Readonly<Readonly<{
|
|
22
28
|
get(): string;
|
|
@@ -33,7 +39,8 @@ export declare class JlAlert extends JlElement {
|
|
|
33
39
|
templateHtml(): Node;
|
|
34
40
|
templateCss(): string;
|
|
35
41
|
onConnect(): void;
|
|
36
|
-
|
|
42
|
+
/** Empty parts take no room: an alert with only a description has no blank line under it. */
|
|
43
|
+
private renderSlots;
|
|
37
44
|
/** The tone's own icon is filled, as a toast's is; an icon of the page's stays as it is drawn. */
|
|
38
45
|
private renderIcon;
|
|
39
46
|
}
|
|
@@ -31,8 +31,9 @@ var TONE_ROLES = {
|
|
|
31
31
|
warn: "alert"
|
|
32
32
|
};
|
|
33
33
|
/**
|
|
34
|
-
* A message in the flow of the page, coloured by its tone: a tip, a success, a warning, an error. The text
|
|
35
|
-
* in the default slot, buttons in `slot="actions"`. A `dismissible`
|
|
34
|
+
* A message in the flow of the page, coloured by its tone: a tip, a success, a warning, an error. The text is the
|
|
35
|
+
* `description` or goes in the default slot (for links and markup), buttons in `slot="actions"`. A `dismissible`
|
|
36
|
+
* alert has an × that hides it.
|
|
36
37
|
*/
|
|
37
38
|
var JlAlert = class extends JlElement {
|
|
38
39
|
static selector = "jl-alert";
|
|
@@ -49,6 +50,11 @@ var JlAlert = class extends JlElement {
|
|
|
49
50
|
this._refs.heading.textContent = value;
|
|
50
51
|
this._refs.heading.hidden = value === "";
|
|
51
52
|
}, { attribute: "heading" });
|
|
53
|
+
/** The message as plain text, for pages that set it from code; richer content goes in the default slot. */
|
|
54
|
+
description = this.useChange("", (value) => {
|
|
55
|
+
this._refs.description.textContent = value;
|
|
56
|
+
this._refs.description.hidden = value === "";
|
|
57
|
+
}, { attribute: "description" });
|
|
52
58
|
/** An icon name in place of the tone's own; `none` shows no icon. */
|
|
53
59
|
icon = this.useChange("", () => this.renderIcon(), { attribute: "icon" });
|
|
54
60
|
/** Shows an × that hides the alert and fires `dismiss`. */
|
|
@@ -61,8 +67,11 @@ var JlAlert = class extends JlElement {
|
|
|
61
67
|
actionsSlot: ref("slot[name=\"actions\"]"),
|
|
62
68
|
alert: ref(".alert"),
|
|
63
69
|
close: ref(".close"),
|
|
70
|
+
description: ref(".description"),
|
|
64
71
|
heading: ref(".heading"),
|
|
65
|
-
icon: ref(".icon")
|
|
72
|
+
icon: ref(".icon"),
|
|
73
|
+
text: ref(".text"),
|
|
74
|
+
textSlot: ref("slot:not([name])")
|
|
66
75
|
}));
|
|
67
76
|
/** Hides the alert as its × does, and fires `dismiss`. */
|
|
68
77
|
dismiss() {
|
|
@@ -86,8 +95,13 @@ var JlAlert = class extends JlElement {
|
|
|
86
95
|
class: "heading",
|
|
87
96
|
hidden: true
|
|
88
97
|
}),
|
|
98
|
+
/* @__PURE__ */ jsx("p", {
|
|
99
|
+
class: "description",
|
|
100
|
+
hidden: true
|
|
101
|
+
}),
|
|
89
102
|
/* @__PURE__ */ jsx("div", {
|
|
90
103
|
class: "text",
|
|
104
|
+
hidden: true,
|
|
91
105
|
children: /* @__PURE__ */ jsx("slot", {})
|
|
92
106
|
}),
|
|
93
107
|
/* @__PURE__ */ jsx("div", {
|
|
@@ -113,10 +127,13 @@ var JlAlert = class extends JlElement {
|
|
|
113
127
|
}
|
|
114
128
|
onConnect() {
|
|
115
129
|
this._refs.close.events.register("press", () => this.dismiss());
|
|
116
|
-
this.
|
|
117
|
-
this.on(this._refs.actionsSlot, "slotchange", () => this.
|
|
130
|
+
this.renderSlots();
|
|
131
|
+
this.on(this._refs.actionsSlot, "slotchange", () => this.renderSlots());
|
|
132
|
+
this.on(this._refs.textSlot, "slotchange", () => this.renderSlots());
|
|
118
133
|
}
|
|
119
|
-
|
|
134
|
+
/** Empty parts take no room: an alert with only a description has no blank line under it. */
|
|
135
|
+
renderSlots() {
|
|
136
|
+
this._refs.text.hidden = !hasSlotted(this._refs.textSlot);
|
|
120
137
|
this._refs.actions.hidden = !hasSlotted(this._refs.actionsSlot);
|
|
121
138
|
}
|
|
122
139
|
/** The tone's own icon is filled, as a toast's is; an icon of the page's stays as it is drawn. */
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// src/molecules/jl-chip-group.css?raw
|
|
2
|
+
var jl_chip_group_default = ":host {\n display: block;\n min-width: 0;\n --height: 28px;\n --pad: 10px;\n --font-size: var(--jl-font-sm);\n}\n\n:host([size=\"sm\"]) {\n --height: 24px;\n --pad: 8px;\n --font-size: var(--jl-font-xs);\n}\n\n/* Where a thumb presses it. */\n:host([size=\"lg\"]) {\n --height: 36px;\n --pad: 14px;\n --font-size: var(--jl-font-md);\n}\n\n/*\n * One row that scrolls sideways on a narrow screen. The padding keeps the focus ring inside what the scroller\n * clips; the negative margin keeps the chips aligned with the content around.\n */\n.group {\n display: flex;\n gap: 8px;\n min-width: 0;\n margin: -3px;\n padding: 3px;\n overflow-x: auto;\n scrollbar-width: none;\n contain: inline-size;\n}\n\n.group::-webkit-scrollbar {\n display: none;\n}\n\n:host([wrap]) .group {\n flex-wrap: wrap;\n overflow: visible;\n contain: none;\n}\n\n.chip {\n display: inline-flex;\n flex: none;\n align-items: center;\n gap: 6px;\n max-width: 100%;\n height: var(--height);\n padding: 0 var(--pad);\n border: 1px solid var(--jl-line-strong);\n border-radius: var(--jl-radius-pill);\n background: var(--jl-surface);\n color: var(--jl-text-2);\n font-size: var(--font-size);\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n cursor: pointer;\n transition:\n background var(--jl-fast) var(--jl-ease),\n border-color var(--jl-fast) var(--jl-ease),\n color var(--jl-fast) var(--jl-ease);\n}\n\n.chip:hover:not(:disabled) {\n background: var(--jl-surface-2);\n}\n\n.chip.selected,\n.chip.selected:hover:not(:disabled) {\n border-color: var(--jl-accent-line);\n background: var(--jl-accent-soft);\n color: var(--jl-accent);\n}\n\n.chip:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* A line box taller than the chip's own line-height of 1: the accents of É, Â, Ö stay inside what overflow clips. */\n.label {\n overflow: hidden;\n line-height: 1.4;\n text-overflow: ellipsis;\n}\n\n.badge {\n min-width: 18px;\n margin-right: -4px;\n padding: 0 6px;\n border-radius: var(--jl-radius-pill);\n background: var(--jl-surface-3);\n color: var(--jl-muted);\n font-size: var(--jl-font-xs);\n font-variant-numeric: tabular-nums;\n font-weight: 650;\n line-height: 18px;\n text-align: center;\n}\n\n.chip.selected .badge {\n background: color-mix(in srgb, var(--jl-accent) 14%, transparent);\n color: var(--jl-accent);\n}\n";
|
|
3
|
+
export { jl_chip_group_default as default };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { JlFormControl } from '../base/jl-form-control';
|
|
2
|
+
import type { ChipGroupOption } from '../types/jl-chip-group.types';
|
|
3
|
+
import type { ControlSize } from '../types/options.types';
|
|
4
|
+
/**
|
|
5
|
+
* A row of chips to choose from: a status to filter by, a category, a reason. One choice by default, as radios
|
|
6
|
+
* (choosing the chosen chip again keeps it); `multiple` lets people turn each chip on and off. On a narrow screen
|
|
7
|
+
* the row scrolls sideways; `wrap` lets it go to the next line instead. `change` fires with the chosen values.
|
|
8
|
+
*/
|
|
9
|
+
export declare class JlChipGroup extends JlFormControl {
|
|
10
|
+
static readonly selector = "jl-chip-group";
|
|
11
|
+
readonly events: Readonly<import("@jadis/core").UseEventsHandler<{
|
|
12
|
+
change: string[];
|
|
13
|
+
}>>;
|
|
14
|
+
readonly options: Readonly<Readonly<{
|
|
15
|
+
get(): ChipGroupOption[];
|
|
16
|
+
set(setter: ChipGroupOption[] | ((prevState: ChipGroupOption[]) => ChipGroupOption[])): void;
|
|
17
|
+
}>>;
|
|
18
|
+
/** The chosen values, in the options' order; with one choice, a list of at most one. */
|
|
19
|
+
readonly value: Readonly<Readonly<{
|
|
20
|
+
get(): string[];
|
|
21
|
+
set(setter: string[] | ((prevState: string[]) => string[])): void;
|
|
22
|
+
}>>;
|
|
23
|
+
/** Several chips can be chosen, each turned on and off on its own. */
|
|
24
|
+
readonly multiple: Readonly<Readonly<{
|
|
25
|
+
get(): boolean;
|
|
26
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
27
|
+
}>>;
|
|
28
|
+
/** Goes to the next line when the chips do not fit, instead of scrolling sideways. */
|
|
29
|
+
readonly wrap: Readonly<Readonly<{
|
|
30
|
+
get(): boolean;
|
|
31
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
32
|
+
}>>;
|
|
33
|
+
readonly size: Readonly<Readonly<{
|
|
34
|
+
get(): ControlSize;
|
|
35
|
+
set(setter: ((prevState: ControlSize) => ControlSize) | ControlSize): void;
|
|
36
|
+
}>>;
|
|
37
|
+
/** The group's name for screen readers, when no visible label names it. */
|
|
38
|
+
readonly label: Readonly<Readonly<{
|
|
39
|
+
get(): string;
|
|
40
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
41
|
+
}>>;
|
|
42
|
+
/** A form refuses to submit while nothing is chosen. */
|
|
43
|
+
readonly required: Readonly<Readonly<{
|
|
44
|
+
get(): boolean;
|
|
45
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
46
|
+
}>>;
|
|
47
|
+
readonly disabled: Readonly<Readonly<{
|
|
48
|
+
get(): boolean;
|
|
49
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
50
|
+
}>>;
|
|
51
|
+
/** The chip that holds the Tab stop with several choices: the last one people reached. */
|
|
52
|
+
private readonly _state;
|
|
53
|
+
private readonly _nameWatch;
|
|
54
|
+
private readonly _refs;
|
|
55
|
+
/** Focuses the chip that holds the Tab stop. */
|
|
56
|
+
focus(options?: FocusOptions): void;
|
|
57
|
+
templateHtml(): Node;
|
|
58
|
+
templateCss(): string;
|
|
59
|
+
onConnect(): void;
|
|
60
|
+
onDisconnect(): void;
|
|
61
|
+
protected resetValue(): void;
|
|
62
|
+
/** The browser calls it for a disabled fieldset and for the element's own `disabled`, even before it renders. */
|
|
63
|
+
protected setDisabledByForm(disabled: boolean): void;
|
|
64
|
+
private chips;
|
|
65
|
+
/** What is chosen: with one choice, only the first value counts. */
|
|
66
|
+
private chosen;
|
|
67
|
+
private isDisabled;
|
|
68
|
+
/** One choice: the chip becomes it. Several: the chip turns on or off, the values keeping the options' order. */
|
|
69
|
+
private press;
|
|
70
|
+
private commit;
|
|
71
|
+
private onKeydown;
|
|
72
|
+
private renderOptions;
|
|
73
|
+
private chip;
|
|
74
|
+
/** Radios with one choice, toggle buttons with several; each chosen chip shows its icon or a check. */
|
|
75
|
+
private renderValue;
|
|
76
|
+
/** Tab reaches one chip: the chosen one with one choice, the last one reached with several, else the first. */
|
|
77
|
+
private renderTabStop;
|
|
78
|
+
/** One choice gives one entry; several give one entry each under the field's name, as checkboxes would. */
|
|
79
|
+
private renderFormValue;
|
|
80
|
+
private renderValidity;
|
|
81
|
+
}
|