@syware/jlib 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/all.js +11 -4
- package/dist/atoms/jl-app-icon.css_raw.js +3 -0
- package/dist/atoms/jl-app-icon.d.ts +34 -0
- package/dist/atoms/jl-app-icon.js +86 -0
- package/dist/atoms/jl-avatar.js +1 -1
- package/dist/atoms/jl-badge.js +9 -2
- package/dist/atoms/jl-button.d.ts +5 -0
- package/dist/atoms/jl-button.js +5 -2
- package/dist/atoms/jl-chip.js +3 -2
- package/dist/atoms/jl-input.js +5 -4
- package/dist/atoms/jl-link.js +2 -2
- 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 +6 -5
- package/dist/atoms/jl-spinner.js +2 -2
- package/dist/base/jl-form-control.js +2 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -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 +12 -0
- package/dist/lib/text.js +16 -1
- package/dist/lib/texts.d.ts +97 -0
- package/dist/lib/texts.js +102 -0
- package/dist/lib/time.d.ts +5 -3
- package/dist/lib/time.js +23 -15
- package/dist/lib/tree.d.ts +10 -1
- package/dist/lib/tree.js +37 -3
- 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 +9 -2
- package/dist/molecules/jl-alert.js +3 -2
- package/dist/molecules/jl-breadcrumb.js +3 -3
- package/dist/molecules/jl-calendar.js +5 -5
- package/dist/molecules/jl-chip-group.js +3 -3
- 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 +6 -7
- package/dist/molecules/jl-copy-button.js +5 -4
- package/dist/molecules/jl-date-picker.js +7 -8
- package/dist/molecules/jl-empty.js +2 -2
- 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 +18 -15
- 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-list-item.js +1 -1
- 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.d.ts +4 -1
- package/dist/molecules/jl-load-state.js +20 -12
- package/dist/molecules/jl-menu.js +1 -1
- package/dist/molecules/jl-number-input.js +5 -5
- package/dist/molecules/jl-pagination.js +5 -5
- package/dist/molecules/jl-radio-group.js +2 -2
- package/dist/molecules/jl-search-button.js +5 -5
- package/dist/molecules/jl-search.js +5 -5
- package/dist/molecules/jl-segmented.js +3 -2
- 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 +3 -2
- package/dist/molecules/jl-tag-input.js +3 -3
- package/dist/molecules/jl-timeline.js +1 -1
- 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/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.d.ts +14 -0
- package/dist/organisms/jl-command-palette.js +40 -10
- package/dist/organisms/jl-confirm.d.ts +2 -1
- package/dist/organisms/jl-confirm.js +18 -12
- package/dist/organisms/jl-dialog.js +2 -1
- package/dist/organisms/jl-drawer.js +2 -1
- package/dist/organisms/jl-flow.js +13 -13
- 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 +71 -14
- package/dist/organisms/jl-tab-bar.js +3 -3
- 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 +36 -6
- package/dist/organisms/jl-toaster.css_raw.js +1 -1
- 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/styles/tokens.css_raw.js +1 -1
- package/dist/templates/jl-app-shell.css_raw.js +1 -1
- package/dist/templates/jl-app-shell.d.ts +16 -1
- package/dist/templates/jl-app-shell.js +42 -3
- 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-page.js +3 -3
- 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 +37 -4
- package/dist/tokens.css +2 -0
- 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-split-view.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
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { BASE_STYLES } from "../styles/shared.js";
|
|
2
2
|
import { JlElement } from "../base/jl-element.js";
|
|
3
3
|
import { registerIcons } from "../lib/icons.js";
|
|
4
|
+
import { jlText } from "../lib/texts.js";
|
|
4
5
|
import { JlIcon } from "../atoms/jl-icon.js";
|
|
6
|
+
import { watchSizes } from "../lib/resize.js";
|
|
5
7
|
import jl_app_shell_default from "./jl-app-shell.css_raw.js";
|
|
6
8
|
import { css } from "@jadis/core";
|
|
7
9
|
import { Menu } from "lucide";
|
|
@@ -10,6 +12,7 @@ import { jsx, jsxs } from "@jadis/core/jsx-runtime";
|
|
|
10
12
|
registerIcons({ menu: Menu });
|
|
11
13
|
var MENU_ICON_SIZE = 18;
|
|
12
14
|
var HIDDEN = "none";
|
|
15
|
+
var SHELL_BOTTOM = "--jl-shell-bottom";
|
|
13
16
|
/** Whether a click inside the drawer followed a link: the page changes, so the drawer gets out of the way. */
|
|
14
17
|
var followsLink = (event) => event.composedPath().some((node) => node instanceof HTMLAnchorElement && node.href !== "");
|
|
15
18
|
/**
|
|
@@ -17,18 +20,30 @@ var followsLink = (event) => event.composedPath().some((node) => node instanceof
|
|
|
17
20
|
* on its own, and an optional panel on the right (`aside`). Narrower than 960 pixels, the sidebar becomes a drawer
|
|
18
21
|
* over the content, opened by a menu button in the top bar (or `nav-open`, or `toggleNav()`), the panel hides, and
|
|
19
22
|
* a bar along the bottom shows (`tabs`, such as a `jl-tab-bar`, whose `more` opens the drawer).
|
|
20
|
-
* It takes the height of its container: give that one a height (`100dvh` for a whole page).
|
|
23
|
+
* It takes the height of its container: give that one a height (`100dvh` for a whole page). A shell that fills the
|
|
24
|
+
* window writes the height of its tab bar, while it shows, as `--jl-shell-bottom` on the document: toasts keep
|
|
25
|
+
* above it, and a page's floating bars can (`bottom: calc(var(--jl-shell-bottom, 0px) + 16px)`).
|
|
21
26
|
*/
|
|
22
27
|
var JlAppShell = class extends JlElement {
|
|
23
28
|
static selector = "jl-app-shell";
|
|
24
29
|
events = this.useEvents();
|
|
25
30
|
/** Whether the drawer is open, on narrow screens; wide ones always show the sidebar. */
|
|
26
31
|
navOpen = this.useChange(false, (value) => this.renderNavOpen(value), { attribute: "nav-open" });
|
|
32
|
+
/**
|
|
33
|
+
* The top bar only on narrow screens, as the navigation pattern lays it out: there it holds the menu button and
|
|
34
|
+
* the app's name; wide, the side nav's own header names the app.
|
|
35
|
+
*/
|
|
36
|
+
narrowHeader = this.useChange(false, (value) => this.reflect("narrow-header", value), { attribute: "narrow-header" });
|
|
27
37
|
_state = {
|
|
28
38
|
navListeners: new AbortController(),
|
|
39
|
+
publishedBottom: false,
|
|
29
40
|
tabsListeners: new AbortController()
|
|
30
41
|
};
|
|
31
|
-
_resize =
|
|
42
|
+
_resize = watchSizes(() => {
|
|
43
|
+
this.renderInert();
|
|
44
|
+
this.renderShellBottom();
|
|
45
|
+
this.renderDrawer();
|
|
46
|
+
});
|
|
32
47
|
_refs = this.useRefs((ref) => ({
|
|
33
48
|
aside: ref(".aside"),
|
|
34
49
|
asideSlot: ref("slot[name=\"aside\"]"),
|
|
@@ -56,7 +71,7 @@ var JlAppShell = class extends JlElement {
|
|
|
56
71
|
children: [/* @__PURE__ */ jsx("button", {
|
|
57
72
|
class: "menu",
|
|
58
73
|
type: "button",
|
|
59
|
-
"aria-label": "
|
|
74
|
+
"aria-label": jlText("navigation"),
|
|
60
75
|
"aria-expanded": "false",
|
|
61
76
|
"aria-controls": "nav",
|
|
62
77
|
children: /* @__PURE__ */ jsx(JlIcon, {
|
|
@@ -96,6 +111,7 @@ var JlAppShell = class extends JlElement {
|
|
|
96
111
|
}
|
|
97
112
|
onConnect() {
|
|
98
113
|
this._resize.observe(this);
|
|
114
|
+
this._resize.observe(this._refs.tabs);
|
|
99
115
|
this.on(this._refs.menu, "click", () => this.onMenu());
|
|
100
116
|
this.on(this._refs.scrim, "click", () => this.setNavOpen(false));
|
|
101
117
|
this.on(this._refs.nav, "click", (event) => {
|
|
@@ -114,6 +130,23 @@ var JlAppShell = class extends JlElement {
|
|
|
114
130
|
this._resize.disconnect();
|
|
115
131
|
this._state.navListeners.abort();
|
|
116
132
|
this._state.tabsListeners.abort();
|
|
133
|
+
this.withdrawShellBottom();
|
|
134
|
+
}
|
|
135
|
+
/** Tells the page how much the tab bar covers at the bottom of the window, while it shows. */
|
|
136
|
+
renderShellBottom() {
|
|
137
|
+
const height = Math.round(this._refs.tabs.getBoundingClientRect().height);
|
|
138
|
+
const fillsWindow = this.getBoundingClientRect().bottom >= window.innerHeight - 1;
|
|
139
|
+
if (height === 0 || !fillsWindow) {
|
|
140
|
+
this.withdrawShellBottom();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
document.documentElement.style.setProperty(SHELL_BOTTOM, `${height}px`);
|
|
144
|
+
this._state.publishedBottom = true;
|
|
145
|
+
}
|
|
146
|
+
withdrawShellBottom() {
|
|
147
|
+
if (!this._state.publishedBottom) return;
|
|
148
|
+
document.documentElement.style.removeProperty(SHELL_BOTTOM);
|
|
149
|
+
this._state.publishedBottom = false;
|
|
117
150
|
}
|
|
118
151
|
/** Whether the shell is narrow: the menu button shows only then. */
|
|
119
152
|
isNarrow() {
|
|
@@ -171,6 +204,12 @@ var JlAppShell = class extends JlElement {
|
|
|
171
204
|
const signal = this._state.navListeners.signal;
|
|
172
205
|
for (const element of navElements) element.addEventListener("select", () => this.setNavOpen(false), { signal });
|
|
173
206
|
this.renderInert();
|
|
207
|
+
this.renderDrawer();
|
|
208
|
+
}
|
|
209
|
+
/** Narrow, the nav is a drawer: a side nav shows whole there, whether it is folded on a wide screen or not. */
|
|
210
|
+
renderDrawer() {
|
|
211
|
+
const narrow = this.isNarrow();
|
|
212
|
+
for (const element of this._refs.navSlot.assignedElements()) element.toggleAttribute("in-drawer", narrow);
|
|
174
213
|
}
|
|
175
214
|
};
|
|
176
215
|
JlAppShell.register();
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// src/templates/jl-auth-layout.css?raw
|
|
2
|
+
var jl_auth_layout_default = ":host {\n display: block;\n min-height: var(--min-height, 100dvh);\n}\n\n.screen {\n display: grid;\n grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);\n min-height: var(--min-height, 100dvh);\n}\n\n/* The brand's dark panel: the dark tokens apply inside it (data-theme), whatever the page's theme. */\n.story {\n position: relative;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n gap: 32px;\n overflow: hidden;\n padding: 40px 48px;\n background: var(--jl-bg);\n color: var(--jl-text);\n}\n\n.brand,\n.pitch,\n.points,\n.origin {\n position: relative;\n z-index: 1;\n}\n\n.pitch {\n display: grid;\n gap: 16px;\n max-width: 520px;\n}\n\n.headline {\n font-size: clamp(30px, 3.4vw, 46px);\n font-weight: 800;\n letter-spacing: -0.03em;\n line-height: 1.08;\n text-wrap: balance;\n}\n\n.accent {\n color: var(--jl-brand);\n}\n\n.text {\n color: var(--jl-muted);\n font-size: 15.5px;\n line-height: 1.6;\n}\n\n.points {\n display: grid;\n gap: 10px;\n margin: 0;\n padding: 0;\n list-style: none;\n font-size: var(--jl-font-md);\n}\n\n.points li {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.tick {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: var(--jl-brand);\n color: var(--jl-on-brand);\n}\n\n.origin {\n color: var(--jl-muted);\n font-size: var(--jl-font-xs);\n font-weight: 650;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n/* The SyWare mark, large and faint, leaning out of the corner. */\n.backdrop {\n position: absolute;\n right: -90px;\n bottom: -110px;\n width: 420px;\n height: 460px;\n opacity: 0.07;\n pointer-events: none;\n}\n\n.side {\n display: grid;\n place-items: center;\n padding: 32px 24px;\n background: var(--jl-bg);\n}\n\n/* A phone: the panel is a band with the brand, above the form. */\n@media (max-width: 860px) {\n .screen {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n }\n\n .story {\n padding: 18px 24px;\n }\n\n .pitch,\n .points,\n .origin,\n .backdrop {\n display: none;\n }\n}\n";
|
|
3
|
+
export { jl_auth_layout_default as default };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { JlElement } from '../base/jl-element';
|
|
2
|
+
/**
|
|
3
|
+
* The screen before an app, as every SyWare app shows it: on a dark panel, the app's brand (slot `brand`), a
|
|
4
|
+
* headline whose words between asterisks take the brand's blue ("Workflows that *ask* before they act."), a
|
|
5
|
+
* sentence, a few points, and where it comes from; beside it, the form (the default slot). On a phone, the panel
|
|
6
|
+
* becomes a band with the brand, above the form.
|
|
7
|
+
*/
|
|
8
|
+
export declare class JlAuthLayout extends JlElement {
|
|
9
|
+
static readonly selector = "jl-auth-layout";
|
|
10
|
+
/** What the app does, in a few words; `*words*` take the brand's blue. */
|
|
11
|
+
readonly pitch: Readonly<Readonly<{
|
|
12
|
+
get(): string;
|
|
13
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
14
|
+
}>>;
|
|
15
|
+
/** One or two sentences under the headline. */
|
|
16
|
+
readonly text: Readonly<Readonly<{
|
|
17
|
+
get(): string;
|
|
18
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
19
|
+
}>>;
|
|
20
|
+
/** What the app is good at, three points at most; HTML writes them as JSON. */
|
|
21
|
+
readonly points: Readonly<Readonly<{
|
|
22
|
+
get(): string[];
|
|
23
|
+
set(setter: string[] | ((prevState: string[]) => string[])): void;
|
|
24
|
+
}>>;
|
|
25
|
+
/** Where the app comes from, at the foot of the panel. */
|
|
26
|
+
readonly origin: 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 renderPitch;
|
|
34
|
+
private renderPoints;
|
|
35
|
+
}
|
|
@@ -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 { starredParts } from "../lib/text.js";
|
|
6
|
+
import { JlIcon } from "../atoms/jl-icon.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 };
|
|
@@ -3,6 +3,7 @@ import { JlElement } from "../base/jl-element.js";
|
|
|
3
3
|
import { oneOf } from "../lib/attributes.js";
|
|
4
4
|
import { define } from "../lib/define.js";
|
|
5
5
|
import { registerIcons } from "../lib/icons.js";
|
|
6
|
+
import { jlText } from "../lib/texts.js";
|
|
6
7
|
import { setOrRemove } from "../lib/dom.js";
|
|
7
8
|
import { JlButton } from "../atoms/jl-button.js";
|
|
8
9
|
import { JlPageHeader } from "../organisms/jl-page-header.js";
|
|
@@ -20,7 +21,6 @@ var PAGE_WIDTHS = [
|
|
|
20
21
|
"full"
|
|
21
22
|
];
|
|
22
23
|
var BREADCRUMB_SLOT = "breadcrumb";
|
|
23
|
-
var DEFAULT_BACK_LABEL = "Back";
|
|
24
24
|
var TITLE_SEPARATOR = " · ";
|
|
25
25
|
/**
|
|
26
26
|
* A page of an app: its header (a back link named after the page it leads to, the heading, a sentence on what the
|
|
@@ -48,7 +48,7 @@ var JlPage = class extends JlElement {
|
|
|
48
48
|
appName = this.useChange("", () => this.renderTitle(), { attribute: "app-name" });
|
|
49
49
|
_refs = this.useRefs((ref) => ({
|
|
50
50
|
back: ref(".back"),
|
|
51
|
-
header: ref(
|
|
51
|
+
header: ref(JlPageHeader)
|
|
52
52
|
}));
|
|
53
53
|
templateHtml() {
|
|
54
54
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -88,7 +88,7 @@ var JlPage = class extends JlElement {
|
|
|
88
88
|
const back = this._refs.back;
|
|
89
89
|
const href = this.back.get();
|
|
90
90
|
back.href.set(href);
|
|
91
|
-
back.label.set(this.backLabel.get() ||
|
|
91
|
+
back.label.set(this.backLabel.get() || jlText("back"));
|
|
92
92
|
setOrRemove(back, "slot", href === "" ? "" : BREADCRUMB_SLOT);
|
|
93
93
|
}
|
|
94
94
|
renderTitle() {
|
|
@@ -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;
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { BASE_STYLES } from "../styles/shared.js";
|
|
2
2
|
import { JlElement } from "../base/jl-element.js";
|
|
3
3
|
import { clampedNumber, oneOf } from "../lib/attributes.js";
|
|
4
|
+
import { jlText } from "../lib/texts.js";
|
|
5
|
+
import { watchSizes } from "../lib/resize.js";
|
|
4
6
|
import { clampMin, clampSplit, positionAfterKey, positionAt } from "../lib/split.js";
|
|
5
7
|
import jl_split_view_default from "./jl-split-view.css_raw.js";
|
|
6
8
|
import { css } from "@jadis/core";
|
|
7
9
|
import { jsx, jsxs } from "@jadis/core/jsx-runtime";
|
|
8
10
|
// src/templates/jl-split-view.tsx
|
|
9
11
|
var SPLIT_ORIENTATIONS = ["horizontal", "vertical"];
|
|
12
|
+
var SPLIT_PANES = ["start", "end"];
|
|
10
13
|
var DEFAULT_POSITION = 50;
|
|
11
14
|
var DEFAULT_MIN = 15;
|
|
12
15
|
var FULL = 100;
|
|
13
16
|
var MAX_MIN = 50;
|
|
14
17
|
var PRIMARY_BUTTON = 0;
|
|
15
|
-
var
|
|
18
|
+
var STACKED = "stacked";
|
|
19
|
+
var MAX_STACK_WIDTH = 4e3;
|
|
16
20
|
var SEPARATOR_ORIENTATION = {
|
|
17
21
|
horizontal: "vertical",
|
|
18
22
|
vertical: "horizontal"
|
|
@@ -22,6 +26,9 @@ var parsePosition = clampedNumber(DEFAULT_POSITION, 0, FULL);
|
|
|
22
26
|
* Two panes, `start` and `end`, side by side or one above the other, with a divider to drag between them. The
|
|
23
27
|
* divider also moves with the arrows (Shift for large steps), Home and End; a double click puts it back. `position`
|
|
24
28
|
* is the start pane's share, in percent; each pane keeps at least `min` percent.
|
|
29
|
+
*
|
|
30
|
+
* Narrower than `stack-below` pixels, it shows one pane at a time, the one `pane` names: a list and the item opened
|
|
31
|
+
* from it become two pages, as a phone shows them. It then has the attribute `stacked`, and fires `stack`.
|
|
25
32
|
*/
|
|
26
33
|
var JlSplitView = class extends JlElement {
|
|
27
34
|
static selector = "jl-split-view";
|
|
@@ -44,11 +51,26 @@ var JlSplitView = class extends JlElement {
|
|
|
44
51
|
attribute: "min",
|
|
45
52
|
parse: clampedNumber(DEFAULT_MIN, 0, MAX_MIN)
|
|
46
53
|
});
|
|
54
|
+
/** Under this width, in pixels, it shows one pane at a time; 0 never does. */
|
|
55
|
+
stackBelow = this.useChange(0, () => this.renderStacked(), {
|
|
56
|
+
attribute: "stack-below",
|
|
57
|
+
parse: clampedNumber(0, 0, MAX_STACK_WIDTH)
|
|
58
|
+
});
|
|
59
|
+
/** The pane it shows while stacked: `start`, or `end` once an item is open. */
|
|
60
|
+
pane = this.useChange("start", (value) => this.reflect("pane", value), {
|
|
61
|
+
attribute: "pane",
|
|
62
|
+
parse: oneOf(SPLIT_PANES, "start")
|
|
63
|
+
});
|
|
47
64
|
/** The divider's name for screen readers. */
|
|
48
|
-
label = this.useChange(
|
|
49
|
-
this._refs.divider.setAttribute("aria-label", value === "" ?
|
|
65
|
+
label = this.useChange(jlText("resizePanes"), (value) => {
|
|
66
|
+
this._refs.divider.setAttribute("aria-label", value === "" ? jlText("resizePanes") : value);
|
|
50
67
|
}, { attribute: "label" });
|
|
51
68
|
_drag = { from: DEFAULT_POSITION };
|
|
69
|
+
_resize = watchSizes(() => this.renderStacked());
|
|
70
|
+
/** Whether it shows one pane at a time now. */
|
|
71
|
+
isStacked() {
|
|
72
|
+
return this.hasAttribute(STACKED);
|
|
73
|
+
}
|
|
52
74
|
_refs = this.useRefs((ref) => ({
|
|
53
75
|
divider: ref(".divider"),
|
|
54
76
|
split: ref(".split")
|
|
@@ -91,6 +113,7 @@ var JlSplitView = class extends JlElement {
|
|
|
91
113
|
return css`${BASE_STYLES}${jl_split_view_default}`;
|
|
92
114
|
}
|
|
93
115
|
onConnect() {
|
|
116
|
+
this._resize.observe(this);
|
|
94
117
|
const divider = this._refs.divider;
|
|
95
118
|
this.on(divider, "pointerdown", (event) => this.startDrag(event));
|
|
96
119
|
this.on(divider, "pointermove", (event) => this.drag(event));
|
|
@@ -99,6 +122,16 @@ var JlSplitView = class extends JlElement {
|
|
|
99
122
|
this.on(divider, "keydown", (event) => this.onKey(event));
|
|
100
123
|
this.on(divider, "dblclick", () => this.reset());
|
|
101
124
|
}
|
|
125
|
+
onDisconnect() {
|
|
126
|
+
this._resize.disconnect();
|
|
127
|
+
}
|
|
128
|
+
renderStacked() {
|
|
129
|
+
const below = this.stackBelow.get();
|
|
130
|
+
const stacked = below > 0 && this.getBoundingClientRect().width < below;
|
|
131
|
+
if (stacked === this.isStacked()) return;
|
|
132
|
+
this.reflect(STACKED, stacked);
|
|
133
|
+
this.events.emit("stack", stacked);
|
|
134
|
+
}
|
|
102
135
|
current() {
|
|
103
136
|
return clampSplit(this.position.get(), this.min.get());
|
|
104
137
|
}
|
|
@@ -146,4 +179,4 @@ var JlSplitView = class extends JlElement {
|
|
|
146
179
|
}
|
|
147
180
|
};
|
|
148
181
|
JlSplitView.register();
|
|
149
|
-
export { JlSplitView, SPLIT_ORIENTATIONS };
|
|
182
|
+
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;
|
|
@@ -4,10 +4,17 @@ export type PaletteCommand = {
|
|
|
4
4
|
/** The heading the command is listed under. */
|
|
5
5
|
group?: string;
|
|
6
6
|
icon?: string;
|
|
7
|
+
/** What it is, beside its label and quieter: a record's type, a person's username. Tells apart two of one name. */
|
|
8
|
+
description?: string;
|
|
7
9
|
/** Keys shown beside it, joined by `+`: `mod+shift+p` (`mod` is ⌘ on Apple, Ctrl elsewhere). */
|
|
8
10
|
shortcut?: string;
|
|
9
11
|
/** Other words that find it: `["preferences", "options"]` for "Settings". */
|
|
10
12
|
keywords?: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Found by the page for what was typed (a record, a person, "Open" with the ID pasted): listed whatever the
|
|
15
|
+
* search, since the page chose it already, after the commands the search matches.
|
|
16
|
+
*/
|
|
17
|
+
result?: boolean;
|
|
11
18
|
};
|
|
12
19
|
/** A command that matches the search, with the places of the matched letters in its label. */
|
|
13
20
|
export type PaletteMatch = {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** One fact about a thing: its label and value. A fact without a value is left out. */
|
|
2
|
+
export type Fact = {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string | number | null;
|
|
5
|
+
/** Drawn in the monospace font and broken anywhere: an identifier, a digest, a path. */
|
|
6
|
+
code?: boolean;
|
|
7
|
+
/** An address: the value is a link to it. */
|
|
8
|
+
href?: string;
|
|
9
|
+
/** The value's tooltip. */
|
|
10
|
+
hint?: string;
|
|
11
|
+
/** Names the fact for a renderer of `renderers`, which then draws its value. */
|
|
12
|
+
key?: string;
|
|
13
|
+
};
|
|
14
|
+
/** Draws a fact's value: a badge, links, a list. */
|
|
15
|
+
export type FactRenderer = (fact: Fact) => Node;
|
|
16
|
+
/** `horizontal` puts each label beside its value, `vertical` over it, the facts side by side. */
|
|
17
|
+
export type FactsOrientation = 'horizontal' | 'vertical';
|
|
@@ -6,6 +6,8 @@ export type SideNavItem = {
|
|
|
6
6
|
icon?: string;
|
|
7
7
|
/** A link to follow; without it the item is a button that fires `select`. */
|
|
8
8
|
href?: string;
|
|
9
|
+
/** Where the link opens: `_blank` for a new tab (the API's documentation beside the app). */
|
|
10
|
+
target?: string;
|
|
9
11
|
/** A count or a short word beside the label: `3`, `New`. */
|
|
10
12
|
badge?: string | number;
|
|
11
13
|
children?: SideNavItem[];
|
|
@@ -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';
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
export type StepItem = {
|
|
3
3
|
label: string;
|
|
4
4
|
description?: string;
|
|
5
|
+
/** The step ended in failure (a payment refused, a transaction rolled back): an × in the danger colour. */
|
|
6
|
+
failed?: boolean;
|
|
5
7
|
};
|
|
6
8
|
export type StepsOrientation = 'horizontal' | 'vertical';
|
|
7
9
|
/** Where a step stands against the current one. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Tone } from './options.types';
|
|
2
|
-
/** How a cell shows its value: as it is, a localised number or date, a badge, or code. */
|
|
3
|
-
export type TableCellFormat = 'text' | 'number' | 'date' | 'badge' | 'code';
|
|
2
|
+
/** How a cell shows its value: as it is, a localised number, date or date and time, a badge, or code. */
|
|
3
|
+
export type TableCellFormat = 'text' | 'number' | 'date' | 'datetime' | 'badge' | 'code';
|
|
4
4
|
export type TableAlign = 'left' | 'center' | 'right';
|
|
5
5
|
/** The order of a sorted column; `none` keeps the rows as they came. */
|
|
6
6
|
export type TableSortDirection = 'asc' | 'desc' | 'none';
|
|
@@ -16,6 +16,13 @@ export type TableColumn = {
|
|
|
16
16
|
format?: TableCellFormat;
|
|
17
17
|
/** For badges: the tone of each value (`{ "Active": "ok" }`); neutral otherwise. */
|
|
18
18
|
tones?: Record<string, Tone>;
|
|
19
|
+
/** The row's field shown as a quieter line under the value: a package's format under its name. */
|
|
20
|
+
description?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Long text, a description or a note, breaks over lines instead of widening the table; cells stay on one line
|
|
23
|
+
* otherwise.
|
|
24
|
+
*/
|
|
25
|
+
wrap?: boolean;
|
|
19
26
|
};
|
|
20
27
|
export type TableRow = Record<string, unknown>;
|
|
21
28
|
/** A row's identity, the value of its `row-key` field. */
|
|
@@ -16,8 +16,49 @@ export type ToastOptions = {
|
|
|
16
16
|
actionLabel?: string;
|
|
17
17
|
/** Called when that button is pressed; the toast then closes. */
|
|
18
18
|
onAction?: () => void;
|
|
19
|
+
/** Shows a progress bar under the text, while the work it tells about runs. */
|
|
20
|
+
working?: boolean;
|
|
21
|
+
/** How far that work is, in percent; `null` for work of unknown length. */
|
|
22
|
+
progress?: number | null;
|
|
19
23
|
};
|
|
20
|
-
/** What `toast()` returns: a way to
|
|
24
|
+
/** What `toast()` returns: a way to change the notification while it shows, and to close it before its time. */
|
|
21
25
|
export type ToastHandle = {
|
|
26
|
+
/** Changes what the toast says (its text, tone, progress, action…): the options given replace those it had. */
|
|
27
|
+
update(changes: ToastOptions): void;
|
|
22
28
|
dismiss(): void;
|
|
23
29
|
};
|
|
30
|
+
/** The texts a download's toast shows: `{time}`, `{received}`, `{total}`, `{name}`, `{size}` and `{error}` are filled. */
|
|
31
|
+
export type DownloadTexts = {
|
|
32
|
+
/** While the server prepares the file: "Preparing the file… {time}". */
|
|
33
|
+
preparing: string;
|
|
34
|
+
/** Bytes received, of a size the server did not say: "{received} received". */
|
|
35
|
+
received: string;
|
|
36
|
+
/** Bytes received of the size the server said: "{received} of {total}". */
|
|
37
|
+
receivedOf: string;
|
|
38
|
+
/** Saved: "Saved as {name}, {size}". */
|
|
39
|
+
saved: string;
|
|
40
|
+
/** Failed: "Not downloaded: {error}". */
|
|
41
|
+
failed: string;
|
|
42
|
+
/** The action that stops it. */
|
|
43
|
+
cancel: string;
|
|
44
|
+
};
|
|
45
|
+
export type DownloadOptions = {
|
|
46
|
+
/** The toast's heading: what is downloaded ("METS of Letters 1902"); the file's name otherwise. */
|
|
47
|
+
title?: string;
|
|
48
|
+
/** The name to save under when the server gives none; the address's last segment otherwise. */
|
|
49
|
+
fileName?: string;
|
|
50
|
+
/** The request's options: headers, credentials… */
|
|
51
|
+
init?: RequestInit;
|
|
52
|
+
texts?: Partial<DownloadTexts>;
|
|
53
|
+
};
|
|
54
|
+
/** How a download ended: saved (under a name, its size in bytes), failed (why), or canceled. */
|
|
55
|
+
export type DownloadResult = {
|
|
56
|
+
outcome: 'saved';
|
|
57
|
+
name: string;
|
|
58
|
+
size: number;
|
|
59
|
+
} | {
|
|
60
|
+
outcome: 'failed';
|
|
61
|
+
error: string;
|
|
62
|
+
} | {
|
|
63
|
+
outcome: 'canceled';
|
|
64
|
+
};
|