@syware/jlib 0.5.1 → 0.6.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 +5 -3
- package/dist/atoms/jl-app-icon.css_raw.js +3 -0
- package/dist/atoms/jl-app-icon.d.ts +29 -0
- package/dist/atoms/jl-app-icon.js +64 -0
- package/dist/atoms/jl-avatar.js +1 -1
- package/dist/atoms/jl-badge.js +8 -1
- package/dist/atoms/jl-button.js +1 -1
- package/dist/atoms/jl-input.js +1 -1
- package/dist/atoms/jl-link.js +1 -1
- package/dist/atoms/jl-select.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -2
- package/dist/lib/text.d.ts +10 -0
- package/dist/lib/text.js +13 -1
- package/dist/lib/time.d.ts +5 -3
- package/dist/lib/time.js +23 -15
- package/dist/lib/trigger.js +1 -1
- package/dist/molecules/jl-account-button.css_raw.js +1 -1
- package/dist/molecules/jl-account-button.d.ts +8 -1
- package/dist/molecules/jl-account-button.js +8 -1
- package/dist/molecules/jl-alert.js +1 -1
- package/dist/molecules/jl-calendar.js +1 -1
- package/dist/molecules/jl-chip-group.js +1 -1
- package/dist/molecules/jl-combobox.js +1 -1
- package/dist/molecules/jl-date-picker.js +1 -1
- package/dist/molecules/jl-empty.js +1 -1
- package/dist/molecules/jl-file-drop.js +1 -1
- package/dist/molecules/jl-list-item.js +1 -1
- package/dist/molecules/jl-load-state.d.ts +4 -1
- package/dist/molecules/jl-load-state.js +14 -5
- package/dist/molecules/jl-menu.js +1 -1
- package/dist/molecules/jl-number-input.js +1 -1
- package/dist/molecules/jl-search-button.js +1 -1
- package/dist/molecules/jl-search.js +1 -1
- package/dist/molecules/jl-segmented.js +1 -1
- package/dist/molecules/jl-tabs.js +1 -1
- package/dist/molecules/jl-tag-input.js +1 -1
- package/dist/molecules/jl-timeline.js +1 -1
- package/dist/molecules/jl-tooltip.d.ts +4 -1
- package/dist/molecules/jl-tooltip.js +27 -15
- package/dist/organisms/jl-command-palette.css_raw.js +1 -1
- package/dist/organisms/jl-command-palette.js +1 -1
- package/dist/organisms/jl-confirm.d.ts +2 -1
- package/dist/organisms/jl-confirm.js +16 -8
- package/dist/organisms/jl-flow.js +1 -1
- package/dist/organisms/jl-side-nav.js +1 -1
- package/dist/organisms/jl-tab-bar.js +1 -1
- package/dist/organisms/jl-table.js +1 -1
- package/dist/organisms/jl-toaster.css_raw.js +1 -1
- package/dist/organisms/jl-tree.js +1 -1
- package/dist/styles/tokens.css_raw.js +1 -1
- package/dist/templates/jl-app-shell.d.ts +6 -1
- package/dist/templates/jl-app-shell.js +27 -2
- package/dist/templates/jl-auth-layout.css_raw.js +3 -0
- package/dist/templates/jl-auth-layout.d.ts +35 -0
- package/dist/templates/jl-auth-layout.js +139 -0
- package/dist/templates/jl-page.css_raw.js +1 -1
- package/dist/templates/jl-split-view.css_raw.js +1 -1
- package/dist/templates/jl-split-view.d.ts +21 -1
- package/dist/templates/jl-split-view.js +33 -1
- package/dist/tokens.css +2 -0
- package/dist/types/jl-split-view.types.d.ts +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { BASE_STYLES, TOKEN_STYLES } from "../styles/shared.js";
|
|
2
|
+
import { JlElement } from "../base/jl-element.js";
|
|
3
|
+
import { jsonOf } from "../lib/attributes.js";
|
|
4
|
+
import { registerIcons } from "../lib/icons.js";
|
|
5
|
+
import { JlIcon } from "../atoms/jl-icon.js";
|
|
6
|
+
import { starredParts } from "../lib/text.js";
|
|
7
|
+
import jl_auth_layout_default from "./jl-auth-layout.css_raw.js";
|
|
8
|
+
import { css } from "@jadis/core";
|
|
9
|
+
import { Check } from "lucide";
|
|
10
|
+
import { jsx, jsxs } from "@jadis/core/jsx-runtime";
|
|
11
|
+
// src/templates/jl-auth-layout.tsx
|
|
12
|
+
registerIcons({ check: Check });
|
|
13
|
+
var DEFAULT_ORIGIN = "Made in Switzerland by SyWare";
|
|
14
|
+
var POINT_ICON_SIZE = 12;
|
|
15
|
+
var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
16
|
+
var MARK_VIEW_BOX = "429 1092 2155 2358";
|
|
17
|
+
var MARK_TRANSFORM = "matrix(1.3333333 0 0 -1.3333333 0 4535.4267)";
|
|
18
|
+
var MARK_PARTS = [{
|
|
19
|
+
colour: "var(--jl-text)",
|
|
20
|
+
d: "m 0,0 -786.134,459.252 c -29.13,18.997 -62.026,44.131 -85.701,73.026 V 335.352 c 0,-112.197 60.333,-216.758 157.489,-272.837 l 492.661,-284.389 c 47.878,-27.653 102.358,-42.252 157.563,-42.252 55.12,0 109.562,14.599 157.459,42.252 l 45.359,26.187 C 133.464,-116.391 99.209,-62.514 0,0",
|
|
21
|
+
transform: "translate(1193.8955 1078.5752)"
|
|
22
|
+
}, {
|
|
23
|
+
colour: "var(--jl-brand)",
|
|
24
|
+
d: "m 0,0 c 0,112.186 -60.359,216.747 -157.526,272.845 l -492.639,284.407 c -47.822,27.634 -102.294,42.222 -157.544,42.222 -55.254,0 -109.715,-14.588 -157.489,-42.222 l -492.107,-284.098 c -12.716,-6.17 -157.459,-80.217 -157.459,-245.04 0,-164.205 138.525,-232.919 152.041,-239.219 l 937.433,-543.23 c -0.007,0 -0.063,-0.074 -0.063,-0.074 116.786,-68.629 155.718,-124.426 161.333,-209.494 153.313,93.103 206.494,122.148 206.494,122.148 65.696,37.908 228.603,135.168 -55.964,299.474 -284.567,164.309 -940.314,542.843 -940.314,542.843 -9.958,6.408 -15.905,17.159 -15.905,28.736 0,9.567 4.112,18.681 11.316,25.219 l 296.55,171.559 c 32.93,19.019 75.427,18.982 108.286,0.018 L -392.633,17.747 c 33.41,-19.303 54.16,-50.963 54.16,-118.386 0,-32.996 -5.679,-70.886 56.626,-107.08 47.577,-27.645 186.668,-107.832 249.792,-144.237 12.34,-9.065 22.934,-18.301 31.999,-27.597 L 0,-247.648 c 0,1.213 -0.093,2.397 -0.111,3.609 z",
|
|
25
|
+
transform: "translate(1937.4087 1982.8447)"
|
|
26
|
+
}];
|
|
27
|
+
/** The SyWare mark, drawn large and faint in the panel's corner: the company behind the app. JSX makes HTML only. */
|
|
28
|
+
function backdrop() {
|
|
29
|
+
const svg = document.createElementNS(SVG_NAMESPACE, "svg");
|
|
30
|
+
svg.setAttribute("viewBox", MARK_VIEW_BOX);
|
|
31
|
+
svg.setAttribute("class", "backdrop");
|
|
32
|
+
svg.setAttribute("aria-hidden", "true");
|
|
33
|
+
const group = document.createElementNS(SVG_NAMESPACE, "g");
|
|
34
|
+
group.setAttribute("transform", MARK_TRANSFORM);
|
|
35
|
+
for (const part of MARK_PARTS) {
|
|
36
|
+
const path = document.createElementNS(SVG_NAMESPACE, "path");
|
|
37
|
+
path.setAttribute("transform", part.transform);
|
|
38
|
+
path.setAttribute("d", part.d);
|
|
39
|
+
path.style.fill = part.colour;
|
|
40
|
+
group.append(path);
|
|
41
|
+
}
|
|
42
|
+
svg.append(group);
|
|
43
|
+
return svg;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The screen before an app, as every SyWare app shows it: on a dark panel, the app's brand (slot `brand`), a
|
|
47
|
+
* headline whose words between asterisks take the brand's blue ("Workflows that *ask* before they act."), a
|
|
48
|
+
* sentence, a few points, and where it comes from; beside it, the form (the default slot). On a phone, the panel
|
|
49
|
+
* becomes a band with the brand, above the form.
|
|
50
|
+
*/
|
|
51
|
+
var JlAuthLayout = class extends JlElement {
|
|
52
|
+
static selector = "jl-auth-layout";
|
|
53
|
+
/** What the app does, in a few words; `*words*` take the brand's blue. */
|
|
54
|
+
pitch = this.useChange("", (value) => this.renderPitch(value), { attribute: "pitch" });
|
|
55
|
+
/** One or two sentences under the headline. */
|
|
56
|
+
text = this.useChange("", (value) => {
|
|
57
|
+
this._refs.text.textContent = value;
|
|
58
|
+
this._refs.text.hidden = value === "";
|
|
59
|
+
}, { attribute: "text" });
|
|
60
|
+
/** What the app is good at, three points at most; HTML writes them as JSON. */
|
|
61
|
+
points = this.useChange([], (value) => this.renderPoints(value), {
|
|
62
|
+
attribute: "points",
|
|
63
|
+
parse: jsonOf([])
|
|
64
|
+
});
|
|
65
|
+
/** Where the app comes from, at the foot of the panel. */
|
|
66
|
+
origin = this.useChange(DEFAULT_ORIGIN, (value) => {
|
|
67
|
+
this._refs.origin.textContent = value;
|
|
68
|
+
this._refs.origin.hidden = value === "";
|
|
69
|
+
}, { attribute: "origin" });
|
|
70
|
+
_refs = this.useRefs((ref) => ({
|
|
71
|
+
headline: ref(".headline"),
|
|
72
|
+
origin: ref(".origin"),
|
|
73
|
+
points: ref(".points"),
|
|
74
|
+
text: ref(".text")
|
|
75
|
+
}));
|
|
76
|
+
templateHtml() {
|
|
77
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
78
|
+
class: "screen",
|
|
79
|
+
attrs: { part: "base" },
|
|
80
|
+
children: [/* @__PURE__ */ jsxs("section", {
|
|
81
|
+
class: "story",
|
|
82
|
+
"data-theme": "dark",
|
|
83
|
+
attrs: { part: "story" },
|
|
84
|
+
children: [
|
|
85
|
+
/* @__PURE__ */ jsx("div", {
|
|
86
|
+
class: "brand",
|
|
87
|
+
children: /* @__PURE__ */ jsx("slot", { name: "brand" })
|
|
88
|
+
}),
|
|
89
|
+
/* @__PURE__ */ jsxs("div", {
|
|
90
|
+
class: "pitch",
|
|
91
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
92
|
+
class: "headline",
|
|
93
|
+
hidden: true
|
|
94
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
95
|
+
class: "text",
|
|
96
|
+
hidden: true
|
|
97
|
+
})]
|
|
98
|
+
}),
|
|
99
|
+
/* @__PURE__ */ jsx("ul", {
|
|
100
|
+
class: "points",
|
|
101
|
+
hidden: true
|
|
102
|
+
}),
|
|
103
|
+
/* @__PURE__ */ jsx("p", {
|
|
104
|
+
class: "origin",
|
|
105
|
+
children: DEFAULT_ORIGIN
|
|
106
|
+
}),
|
|
107
|
+
backdrop()
|
|
108
|
+
]
|
|
109
|
+
}), /* @__PURE__ */ jsx("main", {
|
|
110
|
+
class: "side",
|
|
111
|
+
attrs: { part: "side" },
|
|
112
|
+
children: /* @__PURE__ */ jsx("slot", {})
|
|
113
|
+
})]
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
templateCss() {
|
|
117
|
+
return css`${TOKEN_STYLES}${BASE_STYLES}${jl_auth_layout_default}`;
|
|
118
|
+
}
|
|
119
|
+
renderPitch(pitch) {
|
|
120
|
+
const parts = starredParts(pitch).map((part) => part.accent ? /* @__PURE__ */ jsx("span", {
|
|
121
|
+
class: "accent",
|
|
122
|
+
children: part.text
|
|
123
|
+
}) : part.text);
|
|
124
|
+
this._refs.headline.replaceChildren(...parts);
|
|
125
|
+
this._refs.headline.hidden = pitch === "";
|
|
126
|
+
}
|
|
127
|
+
renderPoints(points) {
|
|
128
|
+
this._refs.points.replaceChildren(...points.map((point) => /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("span", {
|
|
129
|
+
class: "tick",
|
|
130
|
+
children: /* @__PURE__ */ jsx(JlIcon, {
|
|
131
|
+
name: "check",
|
|
132
|
+
size: POINT_ICON_SIZE
|
|
133
|
+
})
|
|
134
|
+
}), /* @__PURE__ */ jsx("span", { children: point })] })));
|
|
135
|
+
this._refs.points.hidden = points.length === 0;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
JlAuthLayout.register();
|
|
139
|
+
export { JlAuthLayout };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// src/templates/jl-page.css?raw
|
|
2
|
-
var jl_page_default = ":host {\n display: block;\n
|
|
2
|
+
var jl_page_default = ":host {\n display: block;\n --page-width: var(--jl-page-standard);\n}\n\n:host([width=\"form\"]) {\n --page-width: var(--jl-page-form);\n}\n\n:host([width=\"wide\"]) {\n --page-width: var(--jl-page-wide);\n}\n\n:host([width=\"full\"]) {\n --page-width: none;\n}\n\n.page {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--jl-space-6);\n max-width: var(--page-width);\n margin: 0 auto;\n padding: var(--jl-space-8) var(--jl-space-10) var(--jl-space-12);\n}\n\n/* A grid's items grow to their content otherwise: a row of chips would widen the page. */\n::slotted(*) {\n min-width: 0;\n}\n\n.back {\n margin-left: -8px;\n}\n\n/*\n * Narrow screens. A media query, not a container query: a size container is the containing block of what is\n * fixed inside it, and a page's floating bars and buttons must stay fixed to the screen.\n */\n@media (max-width: 720px) {\n .page {\n padding: var(--jl-space-6) var(--jl-space-4) var(--jl-space-12);\n }\n}\n";
|
|
3
3
|
export { jl_page_default as default };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// src/templates/jl-split-view.css?raw
|
|
2
|
-
var jl_split_view_default = ":host {\n display: block;\n height: 100%;\n min-height: 0;\n}\n\n.split {\n display: grid;\n grid-template-columns: minmax(0, var(--position, 50%)) 1px minmax(0, 1fr);\n height: 100%;\n}\n\n:host([orientation=\"vertical\"]) .split {\n grid-template-columns: none;\n grid-template-rows: minmax(0, var(--position, 50%)) 1px minmax(0, 1fr);\n}\n\n.pane {\n min-width: 0;\n min-height: 0;\n overflow: auto;\n}\n\n.divider {\n position: relative;\n z-index: 1;\n background: var(--jl-line);\n cursor: col-resize;\n touch-action: none;\n transition: background var(--jl-fast) var(--jl-ease);\n}\n\n:host([orientation=\"vertical\"]) .divider {\n cursor: row-resize;\n}\n\n/* The line is one pixel; the pointer gets a wider band to catch. */\n.divider::before {\n content: \"\";\n position: absolute;\n inset: 0 -5px;\n}\n\n:host([orientation=\"vertical\"]) .divider::before {\n inset: -5px 0;\n}\n\n.divider:hover,\n.divider:focus-visible,\n:host([dragging]) .divider {\n background: var(--jl-accent);\n}\n\n.divider:focus-visible {\n box-shadow: var(--jl-focus-ring);\n}\n\n.grip {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 6px;\n height: 28px;\n border: 1px solid var(--jl-line-strong);\n border-radius: var(--jl-radius-pill);\n background: var(--jl-surface);\n transform: translate(-50%, -50%);\n transition: border-color var(--jl-fast) var(--jl-ease);\n}\n\n:host([orientation=\"vertical\"]) .grip {\n width: 28px;\n height: 6px;\n}\n\n.divider:hover .grip,\n.divider:focus-visible .grip,\n:host([dragging]) .grip {\n border-color: var(--jl-accent);\n}\n\n:host([dragging]) {\n cursor: col-resize;\n user-select: none;\n}\n\n:host([dragging][orientation=\"vertical\"]) {\n cursor: row-resize;\n}\n\n/* While dragging, the panes ignore the pointer: a frame inside one would otherwise swallow it. */\n:host([dragging]) .pane {\n pointer-events: none;\n}\n";
|
|
2
|
+
var jl_split_view_default = ":host {\n display: block;\n height: 100%;\n min-height: 0;\n}\n\n.split {\n display: grid;\n grid-template-columns: minmax(0, var(--position, 50%)) 1px minmax(0, 1fr);\n height: 100%;\n}\n\n:host([orientation=\"vertical\"]) .split {\n grid-template-columns: none;\n grid-template-rows: minmax(0, var(--position, 50%)) 1px minmax(0, 1fr);\n}\n\n.pane {\n min-width: 0;\n min-height: 0;\n overflow: auto;\n}\n\n.divider {\n position: relative;\n z-index: 1;\n background: var(--jl-line);\n cursor: col-resize;\n touch-action: none;\n transition: background var(--jl-fast) var(--jl-ease);\n}\n\n:host([orientation=\"vertical\"]) .divider {\n cursor: row-resize;\n}\n\n/* The line is one pixel; the pointer gets a wider band to catch. */\n.divider::before {\n content: \"\";\n position: absolute;\n inset: 0 -5px;\n}\n\n:host([orientation=\"vertical\"]) .divider::before {\n inset: -5px 0;\n}\n\n.divider:hover,\n.divider:focus-visible,\n:host([dragging]) .divider {\n background: var(--jl-accent);\n}\n\n.divider:focus-visible {\n box-shadow: var(--jl-focus-ring);\n}\n\n.grip {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 6px;\n height: 28px;\n border: 1px solid var(--jl-line-strong);\n border-radius: var(--jl-radius-pill);\n background: var(--jl-surface);\n transform: translate(-50%, -50%);\n transition: border-color var(--jl-fast) var(--jl-ease);\n}\n\n:host([orientation=\"vertical\"]) .grip {\n width: 28px;\n height: 6px;\n}\n\n.divider:hover .grip,\n.divider:focus-visible .grip,\n:host([dragging]) .grip {\n border-color: var(--jl-accent);\n}\n\n:host([dragging]) {\n cursor: col-resize;\n user-select: none;\n}\n\n:host([dragging][orientation=\"vertical\"]) {\n cursor: row-resize;\n}\n\n/* While dragging, the panes ignore the pointer: a frame inside one would otherwise swallow it. */\n:host([dragging]) .pane {\n pointer-events: none;\n}\n\n/* Stacked: one pane at a time, the whole room, no divider. */\n:host([stacked]) .split {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: minmax(0, 1fr);\n}\n\n:host([stacked]) .divider,\n:host([stacked]:not([pane=\"end\"])) .end,\n:host([stacked][pane=\"end\"]) .start {\n display: none;\n}\n";
|
|
3
3
|
export { jl_split_view_default as default };
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { JlElement } from '../base/jl-element';
|
|
2
|
-
import type { SplitOrientation } from '../types/jl-split-view.types';
|
|
2
|
+
import type { SplitOrientation, SplitPane } from '../types/jl-split-view.types';
|
|
3
3
|
export declare const SPLIT_ORIENTATIONS: readonly SplitOrientation[];
|
|
4
|
+
export declare const SPLIT_PANES: readonly SplitPane[];
|
|
4
5
|
/**
|
|
5
6
|
* Two panes, `start` and `end`, side by side or one above the other, with a divider to drag between them. The
|
|
6
7
|
* divider also moves with the arrows (Shift for large steps), Home and End; a double click puts it back. `position`
|
|
7
8
|
* is the start pane's share, in percent; each pane keeps at least `min` percent.
|
|
9
|
+
*
|
|
10
|
+
* Narrower than `stack-below` pixels, it shows one pane at a time, the one `pane` names: a list and the item opened
|
|
11
|
+
* from it become two pages, as a phone shows them. It then has the attribute `stacked`, and fires `stack`.
|
|
8
12
|
*/
|
|
9
13
|
export declare class JlSplitView extends JlElement {
|
|
10
14
|
static readonly selector = "jl-split-view";
|
|
11
15
|
/** The new position, in percent, once a drag ends or a key moved the divider. */
|
|
12
16
|
readonly events: Readonly<import("@jadis/core").UseEventsHandler<{
|
|
13
17
|
change: number;
|
|
18
|
+
stack: boolean;
|
|
14
19
|
}>>;
|
|
15
20
|
readonly orientation: Readonly<Readonly<{
|
|
16
21
|
get(): SplitOrientation;
|
|
@@ -26,18 +31,33 @@ export declare class JlSplitView extends JlElement {
|
|
|
26
31
|
get(): number;
|
|
27
32
|
set(setter: number | ((prevState: number) => number)): void;
|
|
28
33
|
}>>;
|
|
34
|
+
/** Under this width, in pixels, it shows one pane at a time; 0 never does. */
|
|
35
|
+
readonly stackBelow: Readonly<Readonly<{
|
|
36
|
+
get(): number;
|
|
37
|
+
set(setter: number | ((prevState: number) => number)): void;
|
|
38
|
+
}>>;
|
|
39
|
+
/** The pane it shows while stacked: `start`, or `end` once an item is open. */
|
|
40
|
+
readonly pane: Readonly<Readonly<{
|
|
41
|
+
get(): SplitPane;
|
|
42
|
+
set(setter: ((prevState: SplitPane) => SplitPane) | SplitPane): void;
|
|
43
|
+
}>>;
|
|
29
44
|
/** The divider's name for screen readers. */
|
|
30
45
|
readonly label: Readonly<Readonly<{
|
|
31
46
|
get(): string;
|
|
32
47
|
set(setter: string | ((prevState: string) => string)): void;
|
|
33
48
|
}>>;
|
|
34
49
|
private readonly _drag;
|
|
50
|
+
private readonly _resize;
|
|
51
|
+
/** Whether it shows one pane at a time now. */
|
|
52
|
+
isStacked(): boolean;
|
|
35
53
|
private readonly _refs;
|
|
36
54
|
/** Puts the divider back where the page first placed it. */
|
|
37
55
|
reset(): void;
|
|
38
56
|
templateHtml(): Node;
|
|
39
57
|
templateCss(): string;
|
|
40
58
|
onConnect(): void;
|
|
59
|
+
onDisconnect(): void;
|
|
60
|
+
private renderStacked;
|
|
41
61
|
private current;
|
|
42
62
|
private startDrag;
|
|
43
63
|
private drag;
|
|
@@ -7,12 +7,15 @@ import { css } from "@jadis/core";
|
|
|
7
7
|
import { jsx, jsxs } from "@jadis/core/jsx-runtime";
|
|
8
8
|
// src/templates/jl-split-view.tsx
|
|
9
9
|
var SPLIT_ORIENTATIONS = ["horizontal", "vertical"];
|
|
10
|
+
var SPLIT_PANES = ["start", "end"];
|
|
10
11
|
var DEFAULT_POSITION = 50;
|
|
11
12
|
var DEFAULT_MIN = 15;
|
|
12
13
|
var FULL = 100;
|
|
13
14
|
var MAX_MIN = 50;
|
|
14
15
|
var PRIMARY_BUTTON = 0;
|
|
15
16
|
var DEFAULT_LABEL = "Resize the panes";
|
|
17
|
+
var STACKED = "stacked";
|
|
18
|
+
var MAX_STACK_WIDTH = 4e3;
|
|
16
19
|
var SEPARATOR_ORIENTATION = {
|
|
17
20
|
horizontal: "vertical",
|
|
18
21
|
vertical: "horizontal"
|
|
@@ -22,6 +25,9 @@ var parsePosition = clampedNumber(DEFAULT_POSITION, 0, FULL);
|
|
|
22
25
|
* Two panes, `start` and `end`, side by side or one above the other, with a divider to drag between them. The
|
|
23
26
|
* divider also moves with the arrows (Shift for large steps), Home and End; a double click puts it back. `position`
|
|
24
27
|
* is the start pane's share, in percent; each pane keeps at least `min` percent.
|
|
28
|
+
*
|
|
29
|
+
* Narrower than `stack-below` pixels, it shows one pane at a time, the one `pane` names: a list and the item opened
|
|
30
|
+
* from it become two pages, as a phone shows them. It then has the attribute `stacked`, and fires `stack`.
|
|
25
31
|
*/
|
|
26
32
|
var JlSplitView = class extends JlElement {
|
|
27
33
|
static selector = "jl-split-view";
|
|
@@ -44,11 +50,26 @@ var JlSplitView = class extends JlElement {
|
|
|
44
50
|
attribute: "min",
|
|
45
51
|
parse: clampedNumber(DEFAULT_MIN, 0, MAX_MIN)
|
|
46
52
|
});
|
|
53
|
+
/** Under this width, in pixels, it shows one pane at a time; 0 never does. */
|
|
54
|
+
stackBelow = this.useChange(0, () => this.renderStacked(), {
|
|
55
|
+
attribute: "stack-below",
|
|
56
|
+
parse: clampedNumber(0, 0, MAX_STACK_WIDTH)
|
|
57
|
+
});
|
|
58
|
+
/** The pane it shows while stacked: `start`, or `end` once an item is open. */
|
|
59
|
+
pane = this.useChange("start", (value) => this.reflect("pane", value), {
|
|
60
|
+
attribute: "pane",
|
|
61
|
+
parse: oneOf(SPLIT_PANES, "start")
|
|
62
|
+
});
|
|
47
63
|
/** The divider's name for screen readers. */
|
|
48
64
|
label = this.useChange(DEFAULT_LABEL, (value) => {
|
|
49
65
|
this._refs.divider.setAttribute("aria-label", value === "" ? DEFAULT_LABEL : value);
|
|
50
66
|
}, { attribute: "label" });
|
|
51
67
|
_drag = { from: DEFAULT_POSITION };
|
|
68
|
+
_resize = new ResizeObserver(() => this.renderStacked());
|
|
69
|
+
/** Whether it shows one pane at a time now. */
|
|
70
|
+
isStacked() {
|
|
71
|
+
return this.hasAttribute(STACKED);
|
|
72
|
+
}
|
|
52
73
|
_refs = this.useRefs((ref) => ({
|
|
53
74
|
divider: ref(".divider"),
|
|
54
75
|
split: ref(".split")
|
|
@@ -91,6 +112,7 @@ var JlSplitView = class extends JlElement {
|
|
|
91
112
|
return css`${BASE_STYLES}${jl_split_view_default}`;
|
|
92
113
|
}
|
|
93
114
|
onConnect() {
|
|
115
|
+
this._resize.observe(this);
|
|
94
116
|
const divider = this._refs.divider;
|
|
95
117
|
this.on(divider, "pointerdown", (event) => this.startDrag(event));
|
|
96
118
|
this.on(divider, "pointermove", (event) => this.drag(event));
|
|
@@ -99,6 +121,16 @@ var JlSplitView = class extends JlElement {
|
|
|
99
121
|
this.on(divider, "keydown", (event) => this.onKey(event));
|
|
100
122
|
this.on(divider, "dblclick", () => this.reset());
|
|
101
123
|
}
|
|
124
|
+
onDisconnect() {
|
|
125
|
+
this._resize.disconnect();
|
|
126
|
+
}
|
|
127
|
+
renderStacked() {
|
|
128
|
+
const below = this.stackBelow.get();
|
|
129
|
+
const stacked = below > 0 && this.getBoundingClientRect().width < below;
|
|
130
|
+
if (stacked === this.isStacked()) return;
|
|
131
|
+
this.reflect(STACKED, stacked);
|
|
132
|
+
this.events.emit("stack", stacked);
|
|
133
|
+
}
|
|
102
134
|
current() {
|
|
103
135
|
return clampSplit(this.position.get(), this.min.get());
|
|
104
136
|
}
|
|
@@ -146,4 +178,4 @@ var JlSplitView = class extends JlElement {
|
|
|
146
178
|
}
|
|
147
179
|
};
|
|
148
180
|
JlSplitView.register();
|
|
149
|
-
export { JlSplitView, SPLIT_ORIENTATIONS };
|
|
181
|
+
export { JlSplitView, SPLIT_ORIENTATIONS, SPLIT_PANES };
|
package/dist/tokens.css
CHANGED
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
--jl-accent-soft: #e9f4fb;
|
|
42
42
|
--jl-accent-line: #b7dbf2;
|
|
43
43
|
--jl-on-accent: #ffffff;
|
|
44
|
+
/* What is drawn on the brand's blue, such as an app's icon: white in both themes, as a favicon is. */
|
|
45
|
+
--jl-on-brand: #ffffff;
|
|
44
46
|
|
|
45
47
|
--jl-ok: #17784a;
|
|
46
48
|
--jl-ok-soft: #ecf6f0;
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
/** `horizontal` puts the panes side by side, `vertical` one above the other. */
|
|
2
2
|
export type SplitOrientation = 'horizontal' | 'vertical';
|
|
3
|
+
/** The pane a stacked split view shows: the list (`start`), or the item opened from it (`end`). */
|
|
4
|
+
export type SplitPane = 'start' | 'end';
|