@graview/primitives 0.1.16 → 0.1.17
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 +44 -2
- package/dist/app-bar.d.ts +41 -19
- package/dist/app-bar.d.ts.map +1 -1
- package/dist/app-bar.js +149 -121
- package/dist/app-bar.js.map +1 -1
- package/dist/bar-panes.d.ts.map +1 -1
- package/dist/bar-panes.js +3 -27
- package/dist/bar-panes.js.map +1 -1
- package/dist/choice.d.ts +9 -0
- package/dist/choice.d.ts.map +1 -0
- package/dist/choice.js +21 -0
- package/dist/choice.js.map +1 -0
- package/dist/declared-lens-doors.d.ts.map +1 -1
- package/dist/declared-lens-doors.js +8 -1
- package/dist/declared-lens-doors.js.map +1 -1
- package/dist/frame.d.ts +2 -2
- package/dist/frame.d.ts.map +1 -1
- package/dist/frame.js +1 -1
- package/dist/frame.js.map +1 -1
- package/dist/graview-mark.d.ts +21 -0
- package/dist/graview-mark.d.ts.map +1 -0
- package/dist/graview-mark.js +24 -0
- package/dist/graview-mark.js.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/ladder.d.ts.map +1 -1
- package/dist/ladder.js +4 -10
- package/dist/ladder.js.map +1 -1
- package/dist/pages.d.ts +3 -3
- package/dist/pages.d.ts.map +1 -1
- package/dist/pages.js +2 -2
- package/dist/pages.js.map +1 -1
- package/dist/scene-css.js +2 -2
- package/dist/scene.d.ts +0 -1
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +0 -1
- package/dist/scene.js.map +1 -1
- package/dist/seats.d.ts.map +1 -1
- package/dist/seats.js +2 -11
- package/dist/seats.js.map +1 -1
- package/dist/shell.d.ts.map +1 -1
- package/dist/shell.js +1 -2
- package/dist/shell.js.map +1 -1
- package/dist/spec-views.d.ts +8 -0
- package/dist/spec-views.d.ts.map +1 -1
- package/dist/spec-views.js +17 -3
- package/dist/spec-views.js.map +1 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +13 -7
- package/dist/theme.js.map +1 -1
- package/fonts/OFL-Montserrat.txt +93 -0
- package/fonts/montserrat-latin.woff2 +0 -0
- package/fonts/montserrat.css +23 -0
- package/package.json +13 -8
- package/dist/home-landing.d.ts +0 -18
- package/dist/home-landing.d.ts.map +0 -1
- package/dist/home-landing.js +0 -59
- package/dist/home-landing.js.map +0 -1
package/README.md
CHANGED
|
@@ -40,8 +40,10 @@ the schema, the invariants and the op log.
|
|
|
40
40
|
|
|
41
41
|
**The app, said once** — `AppBar`: one bar over every face (FR-131) — the
|
|
42
42
|
brand's mark (`AppMark`, drawn as given, never an SVG that could act) and
|
|
43
|
-
the app's name as the page's heading, the
|
|
44
|
-
|
|
43
|
+
the app's name as the page's heading, the switch between the scene and the
|
|
44
|
+
pages (`faces`, FR-137), on Pages the place you are on as one control that
|
|
45
|
+
opens every place (`barPlaces`: the home, the Lists, the Pictures, FR-138),
|
|
46
|
+
and three tools of one size: the Find a
|
|
45
47
|
face puts in it (`useBarFind`), `Standing` and `Profile`. `AppTitle` draws
|
|
46
48
|
the mark, the name and the line under it elsewhere; `useFavicon` is for a
|
|
47
49
|
face that owns the page.
|
|
@@ -54,3 +56,43 @@ a view spec is drawn with, which each face that draws a view draws beside it;
|
|
|
54
56
|
and `sceneCss`, the rules only the scene draws (the districts from altitude,
|
|
55
57
|
the plots, the village, the billboards), which an embed's scene face draws
|
|
56
58
|
beside it so a page on the pages face carries none of them.
|
|
59
|
+
|
|
60
|
+
## Design language
|
|
61
|
+
|
|
62
|
+
Three layers, and a component reads only the middle one:
|
|
63
|
+
|
|
64
|
+
| Layer | Where | What it holds |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| The identity | @graview/core (the design kit, revision 03) | En Dash navy #001769 and turquoise #00E5B9, paper #F7F7F2, ink #18213A, muted #586174; Montserrat at 550, 450 and 600; the shared-plane symbol |
|
|
67
|
+
| The semantic tokens | `themeCss` writes them as custom properties | surfaces (`--graview-ground`, `-panel`, `-panel-muted`, `-panel-warning`, `-bar`, `-float`), text (`--graview-ink`, `-ink-muted`, `-ink-faint`), interaction (`--graview-accent`, `-accent-dim`, `-accent-ink`, `-edge`, `-edge-bright`), state (`--graview-warn`, `-good`, `-bad`), type (`--graview-font-body`, `-display`, `-mono`, `--graview-weight-display`, `-body`, `-label`), shape (`--graview-radius`, `-radius-sm`, `-pad`, `-pad-sm`, `-gap`), depth (`--graview-lift-low`, `-lift-high`, `--graview-layer-*`) and the kit (`--graview-kit-*`) |
|
|
68
|
+
| The app's overrides | the app's brand | name, logo, page icon, both schemes' tokens, faces and weights, radius and density, a hue per kind, the kit |
|
|
69
|
+
|
|
70
|
+
The shipped schemes are built on the identity: paper, reading ink and navy in
|
|
71
|
+
the light, a deep navy ground with the navy's hue made light in the dark.
|
|
72
|
+
Neither paints a wash or a glow; hover firms an edge rather than glowing.
|
|
73
|
+
Turquoise is not a token at all: it is the symbol's point, a fill that is
|
|
74
|
+
never text (1.5:1 on paper; navy reads on it at 9.7:1), so nothing the
|
|
75
|
+
framework draws depends on it. An app that declares its own brand keeps its
|
|
76
|
+
own colors, faces and logo, and the framework never puts Graview's mark in it.
|
|
77
|
+
|
|
78
|
+
How the scene reads against any district's color: a kind's hue is the
|
|
79
|
+
brand's (`accents`) or a stable hash, and only the hue: the lighting of a
|
|
80
|
+
block's three faces and the plot under it is the scene's, so a block reads
|
|
81
|
+
as a block on both grounds. Selection and the keyboard's place are drawn in
|
|
82
|
+
the accent, as an outline set off from the block, never as a change of the
|
|
83
|
+
block's own hue, so a selected block in a navy-ish district still reads as
|
|
84
|
+
selected. A relation is told from another by its pattern, its end cap and its
|
|
85
|
+
width as well as its color, and every district, block and relation carries
|
|
86
|
+
its name in words, so nothing is said by color alone. Disabled is a dimmed
|
|
87
|
+
control that cannot be pressed; empty, loading and refused are said in
|
|
88
|
+
sentences.
|
|
89
|
+
|
|
90
|
+
The words are set in Montserrat when the host has it, and in the reader's
|
|
91
|
+
system sans otherwise: the framework names the face and never fetches it.
|
|
92
|
+
A host that wants it imports the optional copy once —
|
|
93
|
+
`import "@graview/primitives/montserrat.css"`, 32 KB of woff2, weights
|
|
94
|
+
400–700, Latin, under the SIL Open Font License (beside it in the package).
|
|
95
|
+
`GraviewMark` is the symbol for a host drawing Graview's own identity — a
|
|
96
|
+
signature, a hosting service's chrome — inline in the ink it stands in, the
|
|
97
|
+
point in `--graview-mark-point` when set, decorative unless given a `title`,
|
|
98
|
+
and the optical micro cut under 28 px.
|
package/dist/app-bar.d.ts
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import { type AnySchema, type Brand, type PagesArrangement, type Place, type Principal, type Store } from "@graview/core";
|
|
2
2
|
import { type CSSProperties, type ReactNode } from "react";
|
|
3
|
-
/**
|
|
3
|
+
/** Which of the places a place is: the home, a kind's list, or a picture. */
|
|
4
|
+
export type BarPlaceGroup = "home" | "lists" | "pictures";
|
|
5
|
+
/** One of the app's places, as the bar's place control lists it: what it is called, and where it is on the routed face. */
|
|
4
6
|
export interface BarPlace {
|
|
5
7
|
readonly key: string;
|
|
6
8
|
readonly label: string;
|
|
7
|
-
/** Its path on the routed face
|
|
9
|
+
/** Its path on the routed face. */
|
|
8
10
|
readonly path: string;
|
|
11
|
+
readonly group: BarPlaceGroup;
|
|
12
|
+
/** The kind it is a list or a picture of, for its mark. */
|
|
13
|
+
readonly kind?: string;
|
|
9
14
|
}
|
|
10
|
-
/** The key the
|
|
11
|
-
export declare const
|
|
15
|
+
/** The key the home's place has (FR-136): the app's front page, first in the list, at the app's own address. */
|
|
16
|
+
export declare const HOME_KEY = "home";
|
|
17
|
+
/** The home's address on the routed face: the app's own. */
|
|
18
|
+
export declare const HOME_PATH = "/";
|
|
12
19
|
/**
|
|
13
|
-
* THE APP'S PLACES, IN THE
|
|
14
|
-
* the
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
20
|
+
* THE APP'S PLACES, IN THE LIST'S ORDER (FR-138): the home; each kind's list
|
|
21
|
+
* in the declaration's order; each named picture, then how the kinds
|
|
22
|
+
* connect, when they do. What a seat may not see is not offered: a kind
|
|
23
|
+
* kept from it, a disabled module's kind, a picture over either. The scene
|
|
24
|
+
* is not a place here: it is the switch's (FR-137).
|
|
18
25
|
*/
|
|
19
26
|
export declare function barPlaces(input: {
|
|
20
27
|
readonly store: Store<AnySchema>;
|
|
@@ -23,10 +30,8 @@ export declare function barPlaces(input: {
|
|
|
23
30
|
places(): readonly Place[];
|
|
24
31
|
arrangement?(): PagesArrangement | undefined;
|
|
25
32
|
} | undefined;
|
|
26
|
-
/** Whether the scene is one of the places here. */
|
|
27
|
-
readonly overview: boolean;
|
|
28
33
|
}): readonly BarPlace[];
|
|
29
|
-
/** Which of the places a path on the routed face is: its own, a record's kind's, or none (
|
|
34
|
+
/** Which of the places a path on the routed face is: its own, a record's kind's, or none (Find, the problems). */
|
|
30
35
|
export declare function barPlaceAt(places: readonly BarPlace[], path: string): string | null;
|
|
31
36
|
/**
|
|
32
37
|
* WHERE A FACE PUTS ITS FIND BOX (FR-131). The bar draws the place; the
|
|
@@ -35,7 +40,7 @@ export declare function barPlaceAt(places: readonly BarPlace[], path: string): s
|
|
|
35
40
|
*/
|
|
36
41
|
export interface BarFind {
|
|
37
42
|
readonly slot: HTMLElement;
|
|
38
|
-
/** A phone's bar: the box opens over the bar's
|
|
43
|
+
/** A phone's bar: the box opens over the bar's row when it is asked for. */
|
|
39
44
|
readonly compact: boolean;
|
|
40
45
|
}
|
|
41
46
|
export declare const BarFindContext: import("react").Context<BarFind | null>;
|
|
@@ -43,22 +48,37 @@ export declare const BarFindContext: import("react").Context<BarFind | null>;
|
|
|
43
48
|
export declare const useBarFind: () => BarFind | null;
|
|
44
49
|
/** How a place is reached: a link when it has an address, a press when the bar decides. */
|
|
45
50
|
export interface BarGo {
|
|
46
|
-
/** The address a place's link carries; none, and the
|
|
51
|
+
/** The address a place's link carries; none, and the entry is a button. */
|
|
47
52
|
readonly href?: (path: string) => string | undefined;
|
|
48
53
|
/** Goes to a place; a link's ordinary press is handed here rather than followed. */
|
|
49
54
|
readonly go?: (place: BarPlace) => void;
|
|
50
55
|
}
|
|
56
|
+
/** One of the switch's two faces: what it is called, whether it is drawn, and the way to it. */
|
|
57
|
+
export interface BarFace {
|
|
58
|
+
readonly label: string;
|
|
59
|
+
readonly current: boolean;
|
|
60
|
+
readonly go: () => void;
|
|
61
|
+
}
|
|
62
|
+
/** THE SWITCH (FR-137): the scene and the pages, the app's two faces. */
|
|
63
|
+
export interface BarFaces {
|
|
64
|
+
readonly scene: BarFace;
|
|
65
|
+
readonly pages: BarFace;
|
|
66
|
+
}
|
|
51
67
|
/** One size for every tool on the bar (FR-131). */
|
|
52
68
|
export declare const TOOL = 30;
|
|
69
|
+
/** The bar's one row, its rule under it included, on a desk and a phone alike (FR-138). */
|
|
70
|
+
export declare const BAR_HEIGHT = 48;
|
|
53
71
|
/** A tool's own box: one size, square, its name in words for whoever cannot see the mark. */
|
|
54
72
|
export declare const toolStyle: CSSProperties;
|
|
55
73
|
/**
|
|
56
74
|
* THE BAR. The app at the left — its mark, its name as the page's heading,
|
|
57
|
-
* the way home —
|
|
58
|
-
*
|
|
59
|
-
*
|
|
75
|
+
* the way home — then the switch between the scene and the pages (FR-137),
|
|
76
|
+
* then, on Pages, the place you are on (FR-138), and the tools at the
|
|
77
|
+
* right: the Find a face puts in it, then `tools` (the standing and the
|
|
78
|
+
* person). One row of one height on a desk and a phone; on a phone the
|
|
79
|
+
* place is the page's first line, under the bar.
|
|
60
80
|
*/
|
|
61
|
-
export declare function AppBar({ brand, name, heading, home, places, current, reach, tools, find, description, findBox, onFind, }: {
|
|
81
|
+
export declare function AppBar({ brand, name, heading, home, faces, places, current, reach, tools, find, description, findBox, onFind, }: {
|
|
62
82
|
readonly brand: Brand | undefined;
|
|
63
83
|
readonly name: string;
|
|
64
84
|
/** The level the app's name is said at: 1 when the page is the app's, `false` when the host's own heading already says it. */
|
|
@@ -69,8 +89,10 @@ export declare function AppBar({ brand, name, heading, home, places, current, re
|
|
|
69
89
|
readonly go?: (() => void) | undefined;
|
|
70
90
|
readonly current: boolean;
|
|
71
91
|
};
|
|
92
|
+
/** The switch between the scene and the pages; none where the face cannot draw the scene. */
|
|
93
|
+
readonly faces?: BarFaces | undefined;
|
|
72
94
|
readonly places: readonly BarPlace[];
|
|
73
|
-
/** The key of the place the reader is on; null at
|
|
95
|
+
/** The key of the place the reader is on; null at Find, at the problems. */
|
|
74
96
|
readonly current: string | null;
|
|
75
97
|
readonly reach: BarGo;
|
|
76
98
|
/** The standing and the person, after Find. */
|
package/dist/app-bar.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-bar.d.ts","sourceRoot":"","sources":["../src/app-bar.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"app-bar.d.ts","sourceRoot":"","sources":["../src/app-bar.tsx"],"names":[],"mappings":"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"}
|
package/dist/app-bar.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { hueFor, orderKinds, OVERVIEW_SLUG, placeSlug } from "@graview/core";
|
|
3
3
|
import { POPOVER_STYLE, usePopover } from "@graview/react/provider";
|
|
4
4
|
import { createContext, useCallback, useContext, useLayoutEffect, useRef, useState } from "react";
|
|
5
5
|
import { AppMark } from "./app-title.js";
|
|
6
|
-
/** The key the
|
|
7
|
-
export const
|
|
6
|
+
/** The key the home's place has (FR-136): the app's front page, first in the list, at the app's own address. */
|
|
7
|
+
export const HOME_KEY = "home";
|
|
8
|
+
/** The home's address on the routed face: the app's own. */
|
|
9
|
+
export const HOME_PATH = "/";
|
|
8
10
|
const upper = (word) => word.charAt(0).toUpperCase() + word.slice(1);
|
|
9
11
|
/**
|
|
10
|
-
* THE APP'S PLACES, IN THE
|
|
11
|
-
* the
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* THE APP'S PLACES, IN THE LIST'S ORDER (FR-138): the home; each kind's list
|
|
13
|
+
* in the declaration's order; each named picture, then how the kinds
|
|
14
|
+
* connect, when they do. What a seat may not see is not offered: a kind
|
|
15
|
+
* kept from it, a disabled module's kind, a picture over either. The scene
|
|
16
|
+
* is not a place here: it is the switch's (FR-137).
|
|
15
17
|
*/
|
|
16
18
|
export function barPlaces(input) {
|
|
17
19
|
const { store, principal, views } = input;
|
|
@@ -19,27 +21,26 @@ export function barPlaces(input) {
|
|
|
19
21
|
const live = store.schema.kinds.filter((kind) => !store.modules.disabledKinds.has(kind) && !kept.has(kind));
|
|
20
22
|
const arrangement = views?.arrangement?.();
|
|
21
23
|
const plural = (kind) => store.schema.tryDefinition(kind)?.plural ?? `${kind}s`;
|
|
22
|
-
const out = [];
|
|
23
|
-
|
|
24
|
-
out.push({ key:
|
|
24
|
+
const out = [{ key: HOME_KEY, label: "Home", path: HOME_PATH, group: "home" }];
|
|
25
|
+
for (const kind of orderKinds(live, arrangement?.order))
|
|
26
|
+
out.push({ key: `kind:${kind}`, label: upper(plural(kind)), path: `/${placeSlug(plural(kind))}`, group: "lists", kind });
|
|
25
27
|
const all = views?.places() ?? [];
|
|
26
28
|
for (const place of all) {
|
|
29
|
+
// A place at the scene's address is not offered: the address is the scene's (FR-132).
|
|
27
30
|
if (!live.includes(place.kind) || place.as === OVERVIEW_SLUG)
|
|
28
31
|
continue;
|
|
29
32
|
// Two kinds' pictures of one name say whose with `?of=` (as the routed face addresses them).
|
|
30
33
|
const shared = all.some((other) => other.as === place.as && other.kind !== place.kind);
|
|
31
34
|
const path = `/places/${encodeURIComponent(place.as)}${shared ? `?of=${placeSlug(plural(place.kind))}` : ""}`;
|
|
32
35
|
if (!out.some((held) => held.path === path))
|
|
33
|
-
out.push({ key: `place:${place.kind}:${place.as}`, label: place.title, path });
|
|
36
|
+
out.push({ key: `place:${place.kind}:${place.as}`, label: place.title, path, group: "pictures", kind: place.kind });
|
|
34
37
|
}
|
|
35
|
-
for (const kind of orderKinds(live, arrangement?.order))
|
|
36
|
-
out.push({ key: `kind:${kind}`, label: upper(plural(kind)), path: `/${placeSlug(plural(kind))}` });
|
|
37
38
|
const connects = live.some((kind) => Object.keys((store.schema.tryDefinition(kind)?.edges ?? {})).length > 0);
|
|
38
39
|
if (connects)
|
|
39
|
-
out.push({ key: "connections", label: "Connections", path: "/map" });
|
|
40
|
+
out.push({ key: "connections", label: "Connections", path: "/map", group: "pictures" });
|
|
40
41
|
return out;
|
|
41
42
|
}
|
|
42
|
-
/** Which of the places a path on the routed face is: its own, a record's kind's, or none (
|
|
43
|
+
/** Which of the places a path on the routed face is: its own, a record's kind's, or none (Find, the problems). */
|
|
43
44
|
export function barPlaceAt(places, path) {
|
|
44
45
|
const [pathname = "/", search = ""] = path.split("?");
|
|
45
46
|
const exact = places.find((place) => place.path === (search ? `${pathname}?${search}` : pathname)) ?? places.find((place) => place.path === pathname);
|
|
@@ -47,43 +48,57 @@ export function barPlaceAt(places, path) {
|
|
|
47
48
|
return exact.key;
|
|
48
49
|
const first = `/${pathname.split("/").filter(Boolean)[0] ?? ""}`;
|
|
49
50
|
// A record's page is under its kind's list.
|
|
50
|
-
return places.find((place) => place.
|
|
51
|
+
return places.find((place) => place.group === "lists" && place.path === first)?.key ?? null;
|
|
51
52
|
}
|
|
52
53
|
export const BarFindContext = createContext(null);
|
|
53
54
|
/** The bar's place for a Find box, when a bar above this face has one. */
|
|
54
55
|
export const useBarFind = () => useContext(BarFindContext);
|
|
55
56
|
/** One size for every tool on the bar (FR-131). */
|
|
56
57
|
export const TOOL = 30;
|
|
58
|
+
/** The bar's one row, its rule under it included, on a desk and a phone alike (FR-138). */
|
|
59
|
+
export const BAR_HEIGHT = 48;
|
|
57
60
|
const BAR_CSS = `
|
|
58
|
-
.graview-bar{container-type:inline-size;flex:0 0 auto;position:relative;background:var(--graview-bar);border-bottom:1px solid var(--graview-edge);color:var(--graview-ink);font-family:var(--graview-font-body,system-ui)}
|
|
59
|
-
.graview-bar-row{display:
|
|
60
|
-
.graview-bar-
|
|
61
|
-
.graview-bar-
|
|
62
|
-
.graview-bar-
|
|
63
|
-
.graview-bar-
|
|
64
|
-
.graview-bar-
|
|
65
|
-
.graview-bar-
|
|
66
|
-
.graview-bar-
|
|
67
|
-
.graview-bar-
|
|
68
|
-
.graview-bar-
|
|
69
|
-
.graview-bar-
|
|
70
|
-
.graview-bar-
|
|
71
|
-
.graview-bar-
|
|
72
|
-
.graview-bar-
|
|
73
|
-
.graview-bar-
|
|
74
|
-
.graview-bar-
|
|
75
|
-
.graview-bar-
|
|
61
|
+
.graview-bar{display:block;margin:0;padding:0;container-type:inline-size;flex:0 0 auto;position:relative;background:var(--graview-bar);border-bottom:1px solid var(--graview-edge);color:var(--graview-ink);font-family:var(--graview-font-body,system-ui)}
|
|
62
|
+
.graview-bar-row{display:flex;flex-wrap:nowrap;align-items:center;gap:16px;height:${BAR_HEIGHT - 1}px;margin:0;padding:0 16px}
|
|
63
|
+
.graview-bar-home,.graview-bar-face,.graview-bar-place,.graview-bar-item{display:inline-flex;align-items:center;box-sizing:border-box;min-width:0;margin:0;border:0;background:none;box-shadow:none;font:inherit;letter-spacing:normal;text-transform:none;text-decoration:none;color:var(--graview-ink);cursor:pointer;text-align:left}
|
|
64
|
+
.graview-bar-app{display:flex;flex:0 1 auto;min-width:0;max-width:34%}
|
|
65
|
+
.graview-bar-name{margin:0;min-width:0;display:flex;font-family:var(--graview-font-display,var(--graview-font-body,system-ui));font-size:.9375rem;font-weight:600;line-height:1.25}
|
|
66
|
+
.graview-bar-home{gap:8px;min-height:30px;padding:0;letter-spacing:-.005em;overflow-wrap:anywhere}
|
|
67
|
+
.graview-bar-faces{display:inline-flex;flex:0 0 auto;box-sizing:border-box;height:30px;margin:0;padding:2px;gap:2px;border:1px solid var(--graview-edge);border-radius:8px}
|
|
68
|
+
.graview-bar-face{gap:6px;padding:0 10px;border-radius:6px;font-size:.8125rem;white-space:nowrap;color:var(--graview-ink-muted)}
|
|
69
|
+
.graview-bar-face span{min-width:0;max-width:11em;overflow:hidden;text-overflow:ellipsis}
|
|
70
|
+
.graview-bar-face[aria-pressed=true]{color:var(--graview-ink);font-weight:600;background:color-mix(in srgb,var(--graview-accent) 16%,transparent)}
|
|
71
|
+
.graview-bar-mid{display:flex;flex:1 1 auto;min-width:0}
|
|
72
|
+
.graview-bar-place-at{position:relative;display:inline-flex;min-width:0;max-width:100%}
|
|
73
|
+
.graview-bar-place{gap:6px;height:30px;max-width:100%;padding:0 8px;border:1px solid transparent;border-radius:8px;font-size:.875rem;font-weight:600}
|
|
74
|
+
.graview-bar-face:hover,.graview-bar-place:hover{border-color:var(--graview-edge);color:var(--graview-ink)}
|
|
75
|
+
.graview-bar-place-words{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
76
|
+
.graview-bar-list{display:grid;width:min(320px,calc(100vw - 24px));max-height:min(70vh,560px);overflow:auto;margin:0;padding:6px;border-radius:10px;border:1px solid var(--graview-edge);background:var(--graview-float);box-shadow:var(--graview-lift-high);color:var(--graview-ink)}
|
|
77
|
+
.graview-bar-list[hidden]{display:none}
|
|
78
|
+
.graview-bar-list p{margin:8px 8px 2px;font-size:.6875rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--graview-ink-muted)}
|
|
79
|
+
.graview-bar-list ul,.graview-bar-list li{display:grid;margin:0;padding:0;list-style:none}
|
|
80
|
+
.graview-bar-item{align-items:flex-start;gap:10px;width:100%;padding:7px 8px;border-radius:7px;font-size:.875rem;line-height:1.3;overflow-wrap:anywhere}
|
|
81
|
+
.graview-bar-item:hover{background:color-mix(in srgb,var(--graview-ink) 7%,transparent)}
|
|
82
|
+
.graview-bar-item[aria-current]{font-weight:600;background:color-mix(in srgb,var(--graview-accent) 14%,transparent)}
|
|
83
|
+
.graview-bar-mark{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:14px;height:1.3em;color:var(--graview-ink-muted)}
|
|
84
|
+
.graview-bar :focus-visible{outline:2px solid var(--graview-accent);outline-offset:1px}
|
|
85
|
+
.graview-bar-tools{display:flex;align-items:center;flex:0 0 auto;gap:8px;margin-left:auto}
|
|
86
|
+
.graview-bar-find{display:flex;width:15rem;min-width:7rem;flex:0 1 auto}
|
|
87
|
+
.graview-bar-find-open{display:none}
|
|
88
|
+
.graview-bar-line{display:flex;flex:0 0 auto;padding:6px 8px;background:var(--graview-ground);border-bottom:1px solid var(--graview-edge);font-family:var(--graview-font-body,system-ui)}
|
|
89
|
+
.graview-bar-line .graview-bar-place{height:auto;min-height:32px}
|
|
90
|
+
.graview-bar-line .graview-bar-place-words{white-space:normal}
|
|
76
91
|
@container (max-width: 639px){
|
|
77
|
-
.graview-bar-row{
|
|
78
|
-
.graview-bar-app
|
|
79
|
-
.graview-bar-
|
|
80
|
-
.graview-bar-
|
|
92
|
+
.graview-bar-row{gap:10px;padding:0 12px}
|
|
93
|
+
.graview-bar-app{max-width:none;flex:1 1 auto}
|
|
94
|
+
.graview-bar-app-name{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
|
|
95
|
+
.graview-bar-face{padding:0 8px}
|
|
96
|
+
.graview-bar-face span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
97
|
+
.graview-bar-mid,.graview-bar-find{display:none}
|
|
81
98
|
.graview-bar-find-open{display:inline-flex}
|
|
82
|
-
.graview-bar[data-finding] .graview-bar-app{display:none}
|
|
83
|
-
.graview-bar[data-finding] .graview-bar-row{grid-template-areas:"tools tools" "places places"}
|
|
99
|
+
.graview-bar[data-finding] :is(.graview-bar-app,.graview-bar-faces,.graview-bar-find-open){display:none}
|
|
84
100
|
.graview-bar[data-finding] .graview-bar-tools{flex:1 1 auto}
|
|
85
|
-
.graview-bar[data-finding] .graview-bar-find{display:flex}
|
|
86
|
-
.graview-bar[data-finding] .graview-bar-find-open{display:none}
|
|
101
|
+
.graview-bar[data-finding] .graview-bar-find{display:flex;flex:1 1 auto;width:auto;min-width:0}
|
|
87
102
|
}`;
|
|
88
103
|
/** A tool's own box: one size, square, its name in words for whoever cannot see the mark. */
|
|
89
104
|
export const toolStyle = {
|
|
@@ -106,11 +121,13 @@ export const toolStyle = {
|
|
|
106
121
|
};
|
|
107
122
|
/**
|
|
108
123
|
* THE BAR. The app at the left — its mark, its name as the page's heading,
|
|
109
|
-
* the way home —
|
|
110
|
-
*
|
|
111
|
-
*
|
|
124
|
+
* the way home — then the switch between the scene and the pages (FR-137),
|
|
125
|
+
* then, on Pages, the place you are on (FR-138), and the tools at the
|
|
126
|
+
* right: the Find a face puts in it, then `tools` (the standing and the
|
|
127
|
+
* person). One row of one height on a desk and a phone; on a phone the
|
|
128
|
+
* place is the page's first line, under the bar.
|
|
112
129
|
*/
|
|
113
|
-
export function AppBar({ brand, name, heading = 1, home, places, current, reach, tools, find = true, description, findBox, onFind, }) {
|
|
130
|
+
export function AppBar({ brand, name, heading = 1, home, faces, places, current, reach, tools, find = true, description, findBox, onFind, }) {
|
|
114
131
|
const bar = useRef(null);
|
|
115
132
|
const [slot, setSlot] = useState(null);
|
|
116
133
|
const [compact, setCompact] = useState(false);
|
|
@@ -151,31 +168,59 @@ export function AppBar({ brand, name, heading = 1, home, places, current, reach,
|
|
|
151
168
|
requestAnimationFrame(() => opener.current?.focus());
|
|
152
169
|
}, []);
|
|
153
170
|
const Heading = heading === false ? "span" : `h${heading}`;
|
|
171
|
+
const Home = (home.href !== undefined ? "a" : "button");
|
|
154
172
|
const press = (go) => (event) => {
|
|
155
173
|
if (!go || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
|
|
156
174
|
return;
|
|
157
175
|
event.preventDefault();
|
|
158
176
|
go();
|
|
159
177
|
};
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
// The place control stands on Pages: in the bar on a desk, as the page's first line on a phone.
|
|
179
|
+
const onPages = !faces || faces.pages.current;
|
|
180
|
+
const placeControl = onPages && places.length > 0 ? _jsx(PlaceControl, { places: places, current: current, reach: reach }) : null;
|
|
181
|
+
return (_jsxs(_Fragment, { children: [_jsxs("header", { ref: bar, className: "graview-bar", "data-testid": "app-bar", "data-graview-app-bar": "", ...(finding && compact ? { "data-finding": "" } : {}), children: [_jsx("style", { children: BAR_CSS }), _jsxs("div", { className: "graview-bar-row", children: [_jsx("div", { className: "graview-bar-app", children: _jsx(Heading, { className: "graview-bar-name", children: _jsxs(Home, { ...(home.href !== undefined ? { href: home.href, onClick: press(home.go) } : { type: "button", onClick: home.go }), "data-testid": "app-home", ...(home.current ? { "aria-current": "page" } : {}), title: description ? `${name} — ${description}` : `${name} — home`, className: "graview-bar-home", children: [_jsx(AppMark, { brand: brand, size: 20 }), _jsx("span", { className: "graview-bar-app-name", "data-testid": "app-name", children: name })] }) }) }), faces ? _jsx(FaceSwitch, { faces: faces }) : null, _jsx("div", { className: "graview-bar-mid", children: compact ? null : placeControl }), _jsxs("div", { className: "graview-bar-tools", children: [find ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "graview-bar-find-open", ref: opener, "data-testid": "app-find-open", "aria-label": "Find", title: "Find", onClick: openFind, style: { ...toolStyle, display: undefined }, children: _jsx(FindMark, {}) }), _jsx("div", { ref: setSlot, className: "graview-bar-find", "data-testid": "app-find", onBlur: (event) => {
|
|
182
|
+
// Put away on a phone when the keyboard leaves it with nothing typed.
|
|
183
|
+
const next = event.relatedTarget;
|
|
184
|
+
if (next instanceof Node && event.currentTarget.contains(next))
|
|
185
|
+
return;
|
|
186
|
+
if (!event.currentTarget.querySelector("input")?.value)
|
|
187
|
+
putAway();
|
|
188
|
+
}, onKeyDownCapture: (event) => {
|
|
189
|
+
if (event.key === "Escape" && compact)
|
|
190
|
+
escaping.current = true;
|
|
191
|
+
}, onKeyDown: (event) => {
|
|
192
|
+
if (event.key !== "Escape" || !compact)
|
|
193
|
+
return;
|
|
194
|
+
// The scene's Find takes Escape for its words first; an empty box is put away.
|
|
195
|
+
if (!event.defaultPrevented || !event.currentTarget.querySelector("input")?.value)
|
|
196
|
+
putAway();
|
|
197
|
+
else
|
|
198
|
+
escaping.current = false;
|
|
199
|
+
}, children: findBox })] })) : null, tools] })] })] }), compact && placeControl ? (_jsx("div", { className: "graview-bar-line", "data-graview-place-line": "", "data-testid": "app-place-line", children: placeControl })) : null] }));
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* THE SWITCH (FR-137): the scene and the pages, side by side, an icon and a
|
|
203
|
+
* word each — the words the accessible names alone on a phone — the one
|
|
204
|
+
* drawn pressed. Choosing the scene draws it under the bar; choosing the
|
|
205
|
+
* pages goes back to the page you were on.
|
|
206
|
+
*/
|
|
207
|
+
function FaceSwitch({ faces }) {
|
|
208
|
+
const face = (which) => {
|
|
209
|
+
const one = faces[which];
|
|
210
|
+
return (_jsxs("button", { type: "button", className: "graview-bar-face", "data-testid": `app-face-${which}`, "aria-pressed": one.current, title: one.label, onClick: () => {
|
|
211
|
+
if (!one.current)
|
|
212
|
+
one.go();
|
|
213
|
+
}, children: [which === "scene" ? _jsx(SceneMark, {}) : _jsx(PagesMark, {}), _jsx("span", { children: one.label })] }));
|
|
214
|
+
};
|
|
215
|
+
return (_jsxs("div", { className: "graview-bar-faces", role: "group", "aria-label": `${faces.scene.label} or ${faces.pages.label}`, "data-testid": "app-faces", children: [face("scene"), face("pages")] }));
|
|
216
|
+
}
|
|
217
|
+
/** The scene's mark: a plot of the city, seen from above at an angle. */
|
|
218
|
+
function SceneMark() {
|
|
219
|
+
return (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", focusable: "false", style: { flex: "0 0 auto" }, children: [_jsx("path", { d: "M7 1.5 L12.5 4.5 L7 7.5 L1.5 4.5 Z", fill: "none", stroke: "currentColor", strokeWidth: "1.3", strokeLinejoin: "round" }), _jsx("path", { d: "M1.5 7.5 L7 10.5 L12.5 7.5", fill: "none", stroke: "currentColor", strokeWidth: "1.3", strokeLinejoin: "round", strokeLinecap: "round" })] }));
|
|
220
|
+
}
|
|
221
|
+
/** The pages' mark: a page of lines. */
|
|
222
|
+
function PagesMark() {
|
|
223
|
+
return (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", focusable: "false", style: { flex: "0 0 auto" }, children: [_jsx("rect", { x: "2.25", y: "1.75", width: "9.5", height: "10.5", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "1.3" }), _jsx("path", { d: "M4.5 5 H9.5 M4.5 7.25 H9.5 M4.5 9.5 H7.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })] }));
|
|
179
224
|
}
|
|
180
225
|
/** The way a menu opens: a chevron drawn, not a glyph a face may draw as a dot. */
|
|
181
226
|
function Chevron() {
|
|
@@ -185,67 +230,43 @@ function Chevron() {
|
|
|
185
230
|
function FindMark() {
|
|
186
231
|
return (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", focusable: "false", children: [_jsx("circle", { cx: "7", cy: "7", r: "4.75", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }), _jsx("path", { d: "M10.5 10.5 L14 14", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })] }));
|
|
187
232
|
}
|
|
233
|
+
/** A place's mark in the list: the home's house, a kind's plot in its own hue. */
|
|
234
|
+
function PlaceMark({ place }) {
|
|
235
|
+
if (place.group === "home") {
|
|
236
|
+
return (_jsx("svg", { width: "13", height: "13", viewBox: "0 0 14 14", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M2 6.5 L7 2 L12 6.5 V12 H2 Z", fill: "none", stroke: "currentColor", strokeWidth: "1.3", strokeLinejoin: "round" }) }));
|
|
237
|
+
}
|
|
238
|
+
// A kind's plot, as the scene draws it from altitude (the mark FR-117 gave a kind with no drawing); a picture of it wears its outline; how the kinds connect, the ink's.
|
|
239
|
+
const hue = place.kind ? `hsl(${Math.round(hueFor(place.kind))} 55% 52%` : "rgb(128 128 128";
|
|
240
|
+
return (_jsx("span", { "aria-hidden": "true", style: {
|
|
241
|
+
display: "inline-block",
|
|
242
|
+
width: 12,
|
|
243
|
+
height: 8,
|
|
244
|
+
clipPath: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)",
|
|
245
|
+
background: place.group === "lists" ? `${hue})` : `linear-gradient(${hue}), ${hue})) center / 6px 4px no-repeat, ${hue} / 0.35)`,
|
|
246
|
+
} }));
|
|
247
|
+
}
|
|
248
|
+
const GROUPS = [{ group: "home" }, { group: "lists", heading: "Lists" }, { group: "pictures", heading: "Pictures" }];
|
|
188
249
|
/**
|
|
189
|
-
* THE
|
|
190
|
-
*
|
|
191
|
-
* the
|
|
192
|
-
*
|
|
250
|
+
* THE PLACE YOU ARE ON, AND EVERY OTHER (FR-138): one control, the place's
|
|
251
|
+
* name and a chevron, that opens the app's places — the home first, then
|
|
252
|
+
* the Lists and the Pictures, each with its mark, a long name wrapped
|
|
253
|
+
* rather than cut. Every place is two presses away, and the bar is one row
|
|
254
|
+
* of one height however many there are.
|
|
193
255
|
*/
|
|
194
|
-
function
|
|
195
|
-
const row = useRef(null);
|
|
196
|
-
const measure = useRef(null);
|
|
197
|
-
const [fits, setFits] = useState(places.length);
|
|
256
|
+
function PlaceControl({ places, current, reach }) {
|
|
198
257
|
const popover = usePopover("places");
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
const ruler = measure.current;
|
|
204
|
-
if (!element || !ruler)
|
|
205
|
-
return;
|
|
206
|
-
const fit = () => {
|
|
207
|
-
const room = element.getBoundingClientRect().width + 0.5;
|
|
208
|
-
const widths = [...ruler.children].map((child) => child.getBoundingClientRect().width);
|
|
209
|
-
const more = widths.pop() ?? 0;
|
|
210
|
-
const sum = (list) => list.reduce((total, width) => total + width, 0);
|
|
211
|
-
let count = widths.length;
|
|
212
|
-
if (sum(widths) > room) {
|
|
213
|
-
// As many as fit beside "More" — the place you are on among them, in the last one's room when it is further along.
|
|
214
|
-
count = widths.length - 1;
|
|
215
|
-
while (count > 0) {
|
|
216
|
-
const seen = currentAt >= count ? [...widths.slice(0, count - 1), widths[currentAt]] : widths.slice(0, count);
|
|
217
|
-
if (sum(seen) + more <= room)
|
|
218
|
-
break;
|
|
219
|
-
count -= 1;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
setFits((was) => (was === count ? was : count));
|
|
223
|
-
};
|
|
224
|
-
fit();
|
|
225
|
-
if (typeof ResizeObserver === "undefined")
|
|
226
|
-
return;
|
|
227
|
-
const watch = new ResizeObserver(fit);
|
|
228
|
-
watch.observe(element);
|
|
229
|
-
// The ruler too: a brand's web font arriving after the first measure widens every tab.
|
|
230
|
-
watch.observe(ruler);
|
|
231
|
-
return () => watch.disconnect();
|
|
232
|
-
}, [signature, currentAt]);
|
|
233
|
-
if (places.length === 0)
|
|
234
|
-
return _jsx("div", { className: "graview-bar-places", "data-testid": "app-places", ref: row });
|
|
235
|
-
// The place you are on is on the row: in the last tab's room when it is one the row could not otherwise hold, alone when it holds none.
|
|
236
|
-
const shown = currentAt >= fits ? [...places.slice(0, Math.max(0, fits - 1)), places[currentAt]] : places.slice(0, fits);
|
|
237
|
-
const rest = places.filter((place) => !shown.includes(place));
|
|
238
|
-
const tab = (place, inMenu) => {
|
|
239
|
-
const here = place.key === current;
|
|
258
|
+
const here = places.find((place) => place.key === current);
|
|
259
|
+
const said = here?.label ?? "Places";
|
|
260
|
+
const entry = (place) => {
|
|
261
|
+
const at = place.key === current;
|
|
240
262
|
const href = reach.href?.(place.path);
|
|
241
263
|
const common = {
|
|
242
|
-
className:
|
|
264
|
+
className: "graview-bar-item",
|
|
243
265
|
"data-testid": `app-place-${place.key}`,
|
|
244
266
|
"data-place-path": place.path,
|
|
245
|
-
...(
|
|
267
|
+
...(at ? { "aria-current": "page" } : {}),
|
|
246
268
|
onClick: (event) => {
|
|
247
|
-
|
|
248
|
-
popover.setOpen(false);
|
|
269
|
+
popover.setOpen(false);
|
|
249
270
|
if (!reach.go)
|
|
250
271
|
return;
|
|
251
272
|
if (href !== undefined && (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey))
|
|
@@ -254,8 +275,15 @@ function BarPlaces({ places, current, reach }) {
|
|
|
254
275
|
reach.go(place);
|
|
255
276
|
},
|
|
256
277
|
};
|
|
257
|
-
|
|
278
|
+
const Entry = (href !== undefined ? "a" : "button");
|
|
279
|
+
return (_jsxs(Entry, { ...common, ...(href !== undefined ? { href } : { type: "button" }), children: [_jsx("span", { className: "graview-bar-mark", children: _jsx(PlaceMark, { place: place }) }), _jsx("span", { children: place.label })] }, place.key));
|
|
258
280
|
};
|
|
259
|
-
return (_jsxs("
|
|
281
|
+
return (_jsxs("span", { className: "graview-bar-place-at", children: [_jsxs("button", { type: "button", className: "graview-bar-place", "data-testid": "app-places-open", ...popover.trigger, onClick: popover.toggle, "aria-label": `${said} — every place`, title: said, children: [_jsx("span", { className: "graview-bar-place-words", "data-testid": "app-place-current", children: said }), _jsx(Chevron, {})] }), _jsx("nav", { ...popover.pane, "data-testid": "app-places", "aria-label": "The app\u2019s places", hidden: !popover.open, className: "graview-bar-list", style: POPOVER_STYLE, children: GROUPS.map(({ group, heading }) => {
|
|
282
|
+
const listed = places.filter((place) => place.group === group);
|
|
283
|
+
if (listed.length === 0)
|
|
284
|
+
return null;
|
|
285
|
+
const id = `${popover.pane.id}-${group}`;
|
|
286
|
+
return (_jsxs("div", { role: "group", ...(heading ? { "aria-labelledby": id } : { "aria-label": "Home" }), "data-place-group": group, children: [heading ? (_jsx("p", { id: id, children: heading })) : null, _jsx("ul", { children: listed.map((place) => (_jsx("li", { children: entry(place) }, place.key))) })] }, group));
|
|
287
|
+
}) })] }));
|
|
260
288
|
}
|
|
261
289
|
//# sourceMappingURL=app-bar.js.map
|