@graview/pages 0.1.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/LICENSE +96 -0
- package/README.md +47 -0
- package/dist/ask.d.ts +21 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/ask.js +109 -0
- package/dist/ask.js.map +1 -0
- package/dist/face-controls.d.ts +68 -0
- package/dist/face-controls.d.ts.map +1 -0
- package/dist/face-controls.js +231 -0
- package/dist/face-controls.js.map +1 -0
- package/dist/face-placed.d.ts +16 -0
- package/dist/face-placed.d.ts.map +1 -0
- package/dist/face-placed.js +34 -0
- package/dist/face-placed.js.map +1 -0
- package/dist/facts.d.ts +115 -0
- package/dist/facts.d.ts.map +1 -0
- package/dist/facts.js +180 -0
- package/dist/facts.js.map +1 -0
- package/dist/form.d.ts +53 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +183 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/page-context.d.ts +74 -0
- package/dist/page-context.d.ts.map +1 -0
- package/dist/page-context.js +38 -0
- package/dist/page-context.js.map +1 -0
- package/dist/page-home.d.ts +19 -0
- package/dist/page-home.d.ts.map +1 -0
- package/dist/page-home.js +67 -0
- package/dist/page-home.js.map +1 -0
- package/dist/page-list.d.ts +23 -0
- package/dist/page-list.d.ts.map +1 -0
- package/dist/page-list.js +196 -0
- package/dist/page-list.js.map +1 -0
- package/dist/page-map.d.ts +20 -0
- package/dist/page-map.d.ts.map +1 -0
- package/dist/page-map.js +71 -0
- package/dist/page-map.js.map +1 -0
- package/dist/page-places.d.ts +72 -0
- package/dist/page-places.d.ts.map +1 -0
- package/dist/page-places.js +304 -0
- package/dist/page-places.js.map +1 -0
- package/dist/page-problems.d.ts +58 -0
- package/dist/page-problems.d.ts.map +1 -0
- package/dist/page-problems.js +113 -0
- package/dist/page-problems.js.map +1 -0
- package/dist/page-record.d.ts +14 -0
- package/dist/page-record.d.ts.map +1 -0
- package/dist/page-record.js +111 -0
- package/dist/page-record.js.map +1 -0
- package/dist/page-search.d.ts +50 -0
- package/dist/page-search.d.ts.map +1 -0
- package/dist/page-search.js +108 -0
- package/dist/page-search.js.map +1 -0
- package/dist/page-shell.d.ts +43 -0
- package/dist/page-shell.d.ts.map +1 -0
- package/dist/page-shell.js +171 -0
- package/dist/page-shell.js.map +1 -0
- package/dist/page-typography.d.ts +54 -0
- package/dist/page-typography.d.ts.map +1 -0
- package/dist/page-typography.js +166 -0
- package/dist/page-typography.js.map +1 -0
- package/dist/pages.d.ts +19 -0
- package/dist/pages.d.ts.map +1 -0
- package/dist/pages.js +19 -0
- package/dist/pages.js.map +1 -0
- package/dist/registry.d.ts +111 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +105 -0
- package/dist/registry.js.map +1 -0
- package/dist/router.d.ts +35 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +155 -0
- package/dist/router.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Elastic License 2.0
|
|
2
|
+
|
|
3
|
+
## Acceptance
|
|
4
|
+
|
|
5
|
+
By using the software, you agree to all of the terms and conditions below.
|
|
6
|
+
|
|
7
|
+
## Copyright License
|
|
8
|
+
|
|
9
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
|
10
|
+
non-sublicensable, non-transferable license to use, copy, distribute, make
|
|
11
|
+
available, and prepare derivative works of the software, in each case subject
|
|
12
|
+
to the limitations and conditions below.
|
|
13
|
+
|
|
14
|
+
## Limitations
|
|
15
|
+
|
|
16
|
+
You may not provide the software to third parties as a hosted or managed
|
|
17
|
+
service, where the service provides users with access to any substantial set
|
|
18
|
+
of the features or functionality of the software.
|
|
19
|
+
|
|
20
|
+
You may not move, change, disable, or circumvent the license key
|
|
21
|
+
functionality in the software, and you may not remove or obscure any
|
|
22
|
+
functionality in the software that is protected by the license key.
|
|
23
|
+
|
|
24
|
+
You may not alter, remove, or obscure any licensing, copyright, or other
|
|
25
|
+
notices of the licensor in the software. Any use of the licensor's trademarks
|
|
26
|
+
is subject to applicable law.
|
|
27
|
+
|
|
28
|
+
## Patents
|
|
29
|
+
|
|
30
|
+
The licensor grants you a license, under any patent claims the licensor can
|
|
31
|
+
license, or becomes able to license, to make, have made, use, sell, offer for
|
|
32
|
+
sale, import and have imported the software, in each case subject to the
|
|
33
|
+
limitations and conditions in this license. This license does not cover any
|
|
34
|
+
patent claims that you cause to be infringed by modifications or additions to
|
|
35
|
+
the software. If you or your company make any written claim that the software
|
|
36
|
+
infringes or contributes to infringement of any patent, your patent license
|
|
37
|
+
for the software granted under these terms ends immediately. If your company
|
|
38
|
+
makes such a claim, your patent license ends immediately for work on behalf
|
|
39
|
+
of your company.
|
|
40
|
+
|
|
41
|
+
## Notices
|
|
42
|
+
|
|
43
|
+
You must ensure that anyone who gets a copy of any part of the software from
|
|
44
|
+
you also gets a copy of these terms.
|
|
45
|
+
|
|
46
|
+
If you modify the software, you must include in any modified copies of the
|
|
47
|
+
software prominent notices stating that you have modified the software.
|
|
48
|
+
|
|
49
|
+
## No Other Rights
|
|
50
|
+
|
|
51
|
+
These terms do not imply any licenses other than those expressly granted in
|
|
52
|
+
these terms.
|
|
53
|
+
|
|
54
|
+
## Termination
|
|
55
|
+
|
|
56
|
+
If you use the software in violation of these terms, such use is not
|
|
57
|
+
licensed, and your licenses will automatically terminate. If the licensor
|
|
58
|
+
provides you with a notice of your violation, and you cease all violation of
|
|
59
|
+
this license no later than 30 days after you receive that notice, your
|
|
60
|
+
licenses will be reinstated retroactively. However, if you violate these
|
|
61
|
+
terms after such reinstatement, any additional violation of these terms will
|
|
62
|
+
cause your licenses to terminate automatically and permanently.
|
|
63
|
+
|
|
64
|
+
## No Liability
|
|
65
|
+
|
|
66
|
+
*As far as the law allows, the software comes as is, without any warranty or
|
|
67
|
+
condition, and the licensor will not be liable to you for any damages arising
|
|
68
|
+
out of these terms or the use or nature of the software, under any kind of
|
|
69
|
+
legal claim.*
|
|
70
|
+
|
|
71
|
+
## Definitions
|
|
72
|
+
|
|
73
|
+
The **licensor** is the entity offering these terms, and the **software** is
|
|
74
|
+
the software the licensor makes available under these terms, including any
|
|
75
|
+
portion of it.
|
|
76
|
+
|
|
77
|
+
**you** refers to the individual or entity agreeing to these terms.
|
|
78
|
+
|
|
79
|
+
**your company** is any legal entity, sole proprietorship, or other kind of
|
|
80
|
+
organization that you work for, plus all organizations that have control
|
|
81
|
+
over, are under the control of, or are under common control with that
|
|
82
|
+
organization. **control** means ownership of substantially all the assets of
|
|
83
|
+
an entity, or the power to direct its management and policies by vote,
|
|
84
|
+
contract, or otherwise. Control can be direct or indirect.
|
|
85
|
+
|
|
86
|
+
**your licenses** are all the licenses granted to you for the software under
|
|
87
|
+
these terms.
|
|
88
|
+
|
|
89
|
+
**use** means anything you do with the software requiring one of your
|
|
90
|
+
licenses.
|
|
91
|
+
|
|
92
|
+
**trademark** means trademarks, service marks, and similar rights.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
Copyright 2025-2026 En Dash Consulting
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @graview/pages
|
|
2
|
+
|
|
3
|
+
The traditional face: a routed webapp derived from the same declaration that drives the
|
|
4
|
+
spatial scene. From one `defineApp` — schema, mutations, invariants, permissions, op log —
|
|
5
|
+
this package serves lists, records, forms, problems and history as ordinary linked pages:
|
|
6
|
+
`/` the gallery, `/:plural` per kind, `/:plural/:id` per node, `/places/:as` per picture,
|
|
7
|
+
`/map` the relations, `/problems`.
|
|
8
|
+
|
|
9
|
+
- **One store, no second path.** Pages read through the same `Store` and write through the
|
|
10
|
+
same mutations with the same principal; withheld actions render disabled with their
|
|
11
|
+
refusal. `recordFacts` composes the derivations the scene already uses (labels, readable
|
|
12
|
+
fields, violations touching, derived affordances) — the parity tests hold a record page
|
|
13
|
+
and the spatial detail to identical facts.
|
|
14
|
+
- **The page registry mirrors the view registry.** `(kind × page-type) → component`,
|
|
15
|
+
defaults overridable per cell; the shell, home and problems pages are themselves
|
|
16
|
+
registrations (`registry.surface(...)`).
|
|
17
|
+
- **Forms are derived.** `formFields` (in `@graview/core`) walks a mutation's zod input into
|
|
18
|
+
a control tree — scalars, dates, node pickers over real candidates, nested objects,
|
|
19
|
+
discriminated unions as a type picker plus that arm's fields, arrays as repeatable rows —
|
|
20
|
+
and `DerivedForm` renders it. Nothing renderable is hand-written; anything unrenderable
|
|
21
|
+
says so instead of hiding.
|
|
22
|
+
- **Two faces, one application.** Record pages link to their spatial stop
|
|
23
|
+
(`spatialHref(id)`); the scene links to the pages; the ids are shared. See
|
|
24
|
+
`apps/todo` for both faces mounted from one declaration (`/` scene, `/pages` routed).
|
|
25
|
+
- **Mobile is an acceptance criterion.** `pnpm pages` runs the phone-width harness
|
|
26
|
+
(390×844): no sideways scroll, named links, labelled controls, and a derived form that
|
|
27
|
+
actually applies.
|
|
28
|
+
|
|
29
|
+
- **It lands on a gallery.** The home opens with the standing as its headline — "2 gardeners,
|
|
30
|
+
3 plots and 1 planting.", or "Nothing here yet." and which act begins it — then every
|
|
31
|
+
picture the app has as a large live card, two across at a desk and one on a phone: the
|
|
32
|
+
lens itself, drawn inert at a scale measured from the card, captioned with its name and how
|
|
33
|
+
much it is over. Handed the scene's view registry (`views` in the context), every titled
|
|
34
|
+
lens is a page at `/places/<as>` and a card on the home; a kind with no titled lens is
|
|
35
|
+
drawn anyway, as a contact sheet of its members, so a new app lands on a gallery on its
|
|
36
|
+
first afternoon. The kinds follow as one row of counts, the relations as one line that
|
|
37
|
+
opens `/map`, and Recently stays short at the foot. The shell is one row of navigation
|
|
38
|
+
that scrolls sideways on a phone; a new address opens at its top.
|
|
39
|
+
- **It reads like the product's own site.** A list opens with the plural and its
|
|
40
|
+
description, a record with its title and its kind's `describe`, controls receding below
|
|
41
|
+
the content. Typography rides the brand's display and body faces at a real scale; the
|
|
42
|
+
brand's mark, name and per-kind accents (`hueFor`, from `@graview/core`) carry through
|
|
43
|
+
every page. Still derivation: nothing here is a per-app template, and an app overrides a
|
|
44
|
+
cell the same way it overrides a view.
|
|
45
|
+
|
|
46
|
+
The host applies `themeCss` from `@graview/primitives` (or supplies its own `--graview-*`
|
|
47
|
+
tokens); the default pages render entirely from those tokens.
|
package/dist/ask.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AnySchema } from "@graview/core";
|
|
2
|
+
import type { PageContext } from "./pages.js";
|
|
3
|
+
/**
|
|
4
|
+
* THE ASSISTANT ON EVERY PAGE — the same panel, not a second one.
|
|
5
|
+
*
|
|
6
|
+
* The scene keeps the seat in a rail on the left: a subject, its acts, its
|
|
7
|
+
* relations, the conversation. A routed face that grew its own chat box
|
|
8
|
+
* would be two assistants with two habits over one graph, so this is the
|
|
9
|
+
* companion, in a drawer beside the reading column (a sheet on a phone),
|
|
10
|
+
* opened by one control in the shell.
|
|
11
|
+
*
|
|
12
|
+
* WHAT "THIS" MEANS HERE IS THE ROUTE. On a record it is that record; on a
|
|
13
|
+
* kind's page, that kind; on a picture, the kind it is a picture of. The
|
|
14
|
+
* provider's selection is set from the address, so the same question means
|
|
15
|
+
* the same thing on both faces — and the companion's own subject rule (the
|
|
16
|
+
* selection first) does the rest.
|
|
17
|
+
*/
|
|
18
|
+
export declare function PageAsk<S extends AnySchema>({ context }: {
|
|
19
|
+
readonly context: PageContext<S>;
|
|
20
|
+
}): import("react").JSX.Element | null;
|
|
21
|
+
//# sourceMappingURL=ask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../src/ask.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAOxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;CAAE,sCAqE7F"}
|
package/dist/ask.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { labelOf } from "@graview/core";
|
|
3
|
+
import { aggregateId, kindCardId } from "@graview/layout";
|
|
4
|
+
import { Companion } from "@graview/primitives";
|
|
5
|
+
import { useGraviewIfAny } from "@graview/react";
|
|
6
|
+
import { useEffect, useRef, useState } from "react";
|
|
7
|
+
import { useLocation, useNavigate } from "react-router-dom";
|
|
8
|
+
import { kindOfSlug, recordPath } from "./registry.js";
|
|
9
|
+
/**
|
|
10
|
+
* THE ASSISTANT ON EVERY PAGE — the same panel, not a second one.
|
|
11
|
+
*
|
|
12
|
+
* The scene keeps the seat in a rail on the left: a subject, its acts, its
|
|
13
|
+
* relations, the conversation. A routed face that grew its own chat box
|
|
14
|
+
* would be two assistants with two habits over one graph, so this is the
|
|
15
|
+
* companion, in a drawer beside the reading column (a sheet on a phone),
|
|
16
|
+
* opened by one control in the shell.
|
|
17
|
+
*
|
|
18
|
+
* WHAT "THIS" MEANS HERE IS THE ROUTE. On a record it is that record; on a
|
|
19
|
+
* kind's page, that kind; on a picture, the kind it is a picture of. The
|
|
20
|
+
* provider's selection is set from the address, so the same question means
|
|
21
|
+
* the same thing on both faces — and the companion's own subject rule (the
|
|
22
|
+
* selection first) does the rest.
|
|
23
|
+
*/
|
|
24
|
+
export function PageAsk({ context }) {
|
|
25
|
+
const [open, setOpen] = useState(false);
|
|
26
|
+
const navigate = useNavigate();
|
|
27
|
+
const here = useGraviewIfAny();
|
|
28
|
+
if (!here)
|
|
29
|
+
return null;
|
|
30
|
+
return (_jsxs(_Fragment, { children: [_jsx(RouteSubject, { context: context }), _jsxs("button", { type: "button", "data-testid": "page-ask", "aria-expanded": open, onClick: () => setOpen((current) => !current), title: "Talk to the seat about this page", style: {
|
|
31
|
+
position: "fixed",
|
|
32
|
+
left: 16,
|
|
33
|
+
bottom: 16,
|
|
34
|
+
zIndex: 30,
|
|
35
|
+
display: "inline-flex",
|
|
36
|
+
alignItems: "center",
|
|
37
|
+
gap: 6,
|
|
38
|
+
minHeight: 40,
|
|
39
|
+
padding: "0 14px",
|
|
40
|
+
borderRadius: 999,
|
|
41
|
+
border: "1px solid var(--graview-edge)",
|
|
42
|
+
background: "var(--graview-float)",
|
|
43
|
+
color: "var(--graview-ink)",
|
|
44
|
+
font: "inherit",
|
|
45
|
+
fontSize: "0.9375rem",
|
|
46
|
+
boxShadow: "var(--graview-lift-low)",
|
|
47
|
+
cursor: "pointer",
|
|
48
|
+
}, children: [_jsx("span", { "aria-hidden": "true", children: "\u25C6" }), open ? "Close" : "Ask"] }), open ? (_jsx("div", { "data-testid": "page-ask-drawer",
|
|
49
|
+
/*
|
|
50
|
+
* The scene's companion positions itself inside the scene's box;
|
|
51
|
+
* here that box is this drawer, pinned beside the reading column
|
|
52
|
+
* on a desk and along the bottom on a phone.
|
|
53
|
+
*/
|
|
54
|
+
style: {
|
|
55
|
+
position: "fixed",
|
|
56
|
+
left: 0,
|
|
57
|
+
top: 0,
|
|
58
|
+
bottom: 0,
|
|
59
|
+
width: "min(320px, 100vw)",
|
|
60
|
+
zIndex: 29,
|
|
61
|
+
pointerEvents: "none",
|
|
62
|
+
}, children: _jsx("div", { style: { position: "absolute", inset: "12px 12px 64px 12px", pointerEvents: "auto", display: "grid" }, children: _jsx(Companion, { framed: true, onPick: (id) => {
|
|
63
|
+
const node = context.store.graph.getNode(id);
|
|
64
|
+
if (node)
|
|
65
|
+
navigate(recordPath(context.store.schema, node.kind, id));
|
|
66
|
+
} }) }) })) : null] }));
|
|
67
|
+
}
|
|
68
|
+
/** The page's own subject, put where every surface reads it: the selection. */
|
|
69
|
+
function RouteSubject({ context }) {
|
|
70
|
+
const { store } = context;
|
|
71
|
+
const here = useGraviewIfAny();
|
|
72
|
+
const { pathname } = useLocation();
|
|
73
|
+
/*
|
|
74
|
+
* HELD IN A REF, because the setter is not stable: it closes over the
|
|
75
|
+
* current view, so a new one arrives on every render — and an effect
|
|
76
|
+
* that depended on it set the selection, re-rendered the provider, got a
|
|
77
|
+
* new setter and set the selection again, nine hundred times a second.
|
|
78
|
+
* The address is the only thing that decides the subject.
|
|
79
|
+
*/
|
|
80
|
+
const set = useRef(here?.setSelection);
|
|
81
|
+
set.current = here?.setSelection;
|
|
82
|
+
/*
|
|
83
|
+
* THE ADDRESS, ONCE PER ADDRESS. `useParams()` hands back a fresh object
|
|
84
|
+
* every render, so putting it in the dependencies made this run on every
|
|
85
|
+
* render, set the selection, and render again — a loop that took the
|
|
86
|
+
* whole page down with it the moment the drawer opened. The path is the
|
|
87
|
+
* only thing that decides the subject, so the path is the dependency.
|
|
88
|
+
*/
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const setSelection = set.current;
|
|
91
|
+
if (!setSelection)
|
|
92
|
+
return;
|
|
93
|
+
const [, slug, id] = pathname.split("/");
|
|
94
|
+
const chosen = (() => {
|
|
95
|
+
if (slug === "places") {
|
|
96
|
+
const place = context.views?.places().find((one) => one.as === decodeURIComponent(id ?? ""));
|
|
97
|
+
return place ? [aggregateId(place.kind)] : [];
|
|
98
|
+
}
|
|
99
|
+
const kind = slug ? kindOfSlug(store.schema, slug) : undefined;
|
|
100
|
+
if (kind && id)
|
|
101
|
+
return [decodeURIComponent(id)];
|
|
102
|
+
return kind ? [kindCardId(kind)] : [];
|
|
103
|
+
})();
|
|
104
|
+
setSelection((current) => current.length === chosen.length && current.every((one, at) => one === chosen[at]) ? current : chosen);
|
|
105
|
+
}, [pathname]);
|
|
106
|
+
void labelOf;
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=ask.js.map
|
package/dist/ask.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.js","sourceRoot":"","sources":["../src/ask.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { AnySchema, Principal, Store } from "@graview/core";
|
|
2
|
+
import { type ReactNode, type RefObject } from "react";
|
|
3
|
+
import type { PageContext } from "./page-context.js";
|
|
4
|
+
import { type PageRegistry } from "./registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* WHAT THE ROUTED FACE OFFERS WHICHEVER SHELL DRAWS IT.
|
|
7
|
+
*
|
|
8
|
+
* Finding a record by part of its name and taking the last change back are
|
|
9
|
+
* core jobs, not chrome a design may happen to remember. The Find box lived
|
|
10
|
+
* in the derived shell only, so an app that replaced the shell — every
|
|
11
|
+
* example does — had Find exactly when its author thought to put
|
|
12
|
+
* `<PageFind>` in, and rota had none. Undo lived nowhere on this face at
|
|
13
|
+
* all: the scene's Activity rail takes a turn back, and on the pages a
|
|
14
|
+
* person who renamed the wrong record could not.
|
|
15
|
+
*
|
|
16
|
+
* So the face's root owns both. A shell that places `<PageFind>` or
|
|
17
|
+
* `<PageUndo>` itself says where they go, and the root sees them placed and
|
|
18
|
+
* draws nothing more; a shell that places neither still gets them — Find in
|
|
19
|
+
* a bar above it, the way back in a dock at the corner. Only a registry that
|
|
20
|
+
* says so (`surface("shell", Shell, { without: ["find"] })`) goes without.
|
|
21
|
+
*/
|
|
22
|
+
export type { FaceControl } from "./face-placed.js";
|
|
23
|
+
export interface LastChange {
|
|
24
|
+
readonly batch: string;
|
|
25
|
+
/** What it did, in the store's own words — the line the Activity rail shows. */
|
|
26
|
+
readonly intent: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The change this person can take back: their own latest turn that is not
|
|
30
|
+
* already taken back, is not itself a take-back, that the log lets go of on
|
|
31
|
+
* its own, and that the policy lets them take back — undo is judged like a
|
|
32
|
+
* change ("what you may undo is what you may have done"), so a turn the
|
|
33
|
+
* store would refuse is never offered.
|
|
34
|
+
*
|
|
35
|
+
* "Their own" is the rule the Activity rail uses for "you": a human author
|
|
36
|
+
* who is this principal, or either side has no id to tell them apart. On a
|
|
37
|
+
* served store two seats share one log, and one person's corner should not
|
|
38
|
+
* offer to take back another's work.
|
|
39
|
+
*/
|
|
40
|
+
export declare function lastChangeOf<S extends AnySchema>(store: Store<S>, principal?: Principal): LastChange | undefined;
|
|
41
|
+
/** "Take back “Rename to …”" — the words on the control and in the announcement. */
|
|
42
|
+
export declare const takeBackWords: (change: LastChange) => string;
|
|
43
|
+
/**
|
|
44
|
+
* THE WAY BACK, on the routed face: one control that says what it takes
|
|
45
|
+
* back — "Take back “Rename to …”" — and takes it back as the person at the
|
|
46
|
+
* keyboard, judged by the policy like any change.
|
|
47
|
+
*
|
|
48
|
+
* Exported for a shell that wants it somewhere of its own; a shell that
|
|
49
|
+
* does not place it gets it anyway, docked at the corner of the face.
|
|
50
|
+
*/
|
|
51
|
+
export declare function PageUndo<S extends AnySchema>({ context, docked, }: {
|
|
52
|
+
readonly context: PageContext<S>;
|
|
53
|
+
/** Held at the corner of the face rather than in a shell's own row. Set by the face's root. */
|
|
54
|
+
readonly docked?: boolean;
|
|
55
|
+
}): import("react").JSX.Element;
|
|
56
|
+
/**
|
|
57
|
+
* The face's root: it knows which of the face's controls a shell placed,
|
|
58
|
+
* and draws the rest — Find in a bar above the shell, the way back docked
|
|
59
|
+
* at the corner — before the shell in the document, so the keyboard reaches
|
|
60
|
+
* them first.
|
|
61
|
+
*/
|
|
62
|
+
export declare function FaceControlsRoot<S extends AnySchema>({ context, registry, root, children, }: {
|
|
63
|
+
readonly context: PageContext<S>;
|
|
64
|
+
readonly registry: PageRegistry<S, unknown> | undefined;
|
|
65
|
+
readonly root: RefObject<HTMLElement | null>;
|
|
66
|
+
readonly children: ReactNode;
|
|
67
|
+
}): import("react").JSX.Element;
|
|
68
|
+
//# sourceMappingURL=face-controls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"face-controls.d.ts","sourceRoot":"","sources":["../src/face-controls.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAA8D,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEnH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAI9D;;;;;;;;;;;;;;;;GAgBG;AACH,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAMpD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAgBhH;AAED,oFAAoF;AACpF,eAAO,MAAM,aAAa,GAAI,QAAQ,UAAU,WAAmC,CAAC;AAwCpF;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,SAAS,EAAE,EAC5C,OAAO,EACP,MAAc,GACf,EAAE;IACD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACjC,+FAA+F;IAC/F,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B,+BAGA;AAmID;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,SAAS,EAAE,EACpD,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IACxD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B,+BAUA"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from "react";
|
|
3
|
+
import { useLocation } from "react-router-dom";
|
|
4
|
+
import { useStoreTick } from "./page-context.js";
|
|
5
|
+
import { kindOfSlug } from "./registry.js";
|
|
6
|
+
import { PageFind } from "./page-shell.js";
|
|
7
|
+
import { createPlaced, FaceControls, usePlaced, usePlacedOnTheFace } from "./face-placed.js";
|
|
8
|
+
/**
|
|
9
|
+
* The change this person can take back: their own latest turn that is not
|
|
10
|
+
* already taken back, is not itself a take-back, that the log lets go of on
|
|
11
|
+
* its own, and that the policy lets them take back — undo is judged like a
|
|
12
|
+
* change ("what you may undo is what you may have done"), so a turn the
|
|
13
|
+
* store would refuse is never offered.
|
|
14
|
+
*
|
|
15
|
+
* "Their own" is the rule the Activity rail uses for "you": a human author
|
|
16
|
+
* who is this principal, or either side has no id to tell them apart. On a
|
|
17
|
+
* served store two seats share one log, and one person's corner should not
|
|
18
|
+
* offer to take back another's work.
|
|
19
|
+
*/
|
|
20
|
+
export function lastChangeOf(store, principal) {
|
|
21
|
+
const batches = store.batches();
|
|
22
|
+
for (let at = batches.length - 1; at >= 0; at--) {
|
|
23
|
+
const batch = batches[at];
|
|
24
|
+
if (batch.undone || batch.ops.length === 0)
|
|
25
|
+
continue;
|
|
26
|
+
if (batch.ops.every((op) => op.undoes !== undefined))
|
|
27
|
+
continue;
|
|
28
|
+
const author = batch.author;
|
|
29
|
+
const mine = author.kind === "human" && (principal?.id === undefined || author.id === undefined || author.id === principal.id);
|
|
30
|
+
if (!mine)
|
|
31
|
+
continue;
|
|
32
|
+
const check = store.canUndo(batch.id);
|
|
33
|
+
if (!check.ok)
|
|
34
|
+
continue;
|
|
35
|
+
const permitted = check.ops.every((op) => !op.mutation || store.permits(op.mutation, principal).ok);
|
|
36
|
+
if (!permitted)
|
|
37
|
+
continue;
|
|
38
|
+
return { batch: batch.id, intent: batch.intent };
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
/** "Take back “Rename to …”" — the words on the control and in the announcement. */
|
|
43
|
+
export const takeBackWords = (change) => `Take back “${change.intent}”`;
|
|
44
|
+
const editable = (target) => target instanceof HTMLElement &&
|
|
45
|
+
(target.isContentEditable || target.matches("input, textarea, select, [contenteditable]:not([contenteditable='false'])"));
|
|
46
|
+
/** Where the keyboard goes when the control it pressed is gone: the page's heading, where the change shows. */
|
|
47
|
+
function landOnThePage(face) {
|
|
48
|
+
const active = document.activeElement;
|
|
49
|
+
if (active !== null && active !== document.body && active !== document.documentElement && active.isConnected)
|
|
50
|
+
return;
|
|
51
|
+
const root = face ?? document;
|
|
52
|
+
const main = root.querySelector("main") ?? root.querySelector("[data-graview-page]") ?? null;
|
|
53
|
+
const target = main?.querySelector("h1") ?? root.querySelector("h1") ?? main;
|
|
54
|
+
if (!(target instanceof HTMLElement))
|
|
55
|
+
return;
|
|
56
|
+
if (!target.hasAttribute("tabindex"))
|
|
57
|
+
target.setAttribute("tabindex", "-1");
|
|
58
|
+
target.focus({ preventScroll: true });
|
|
59
|
+
}
|
|
60
|
+
function useTakeBack(context) {
|
|
61
|
+
const { store, principal } = context;
|
|
62
|
+
const tick = useStoreTick(store);
|
|
63
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
64
|
+
const change = useMemo(() => lastChangeOf(store, principal), [store, principal, tick]);
|
|
65
|
+
const [refused, setRefused] = useState(null);
|
|
66
|
+
const [said, setSaid] = useState("");
|
|
67
|
+
const takeBack = useCallback(() => {
|
|
68
|
+
const now = lastChangeOf(store, principal);
|
|
69
|
+
if (!now)
|
|
70
|
+
return false;
|
|
71
|
+
setRefused(null);
|
|
72
|
+
try {
|
|
73
|
+
store.undo(now.batch, principal ? { author: principal } : {});
|
|
74
|
+
setSaid(`Took back “${now.intent}”`);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
setRefused(error instanceof Error ? error.message : String(error));
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}, [store, principal]);
|
|
81
|
+
return { change, takeBack, refused, said };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* THE WAY BACK, on the routed face: one control that says what it takes
|
|
85
|
+
* back — "Take back “Rename to …”" — and takes it back as the person at the
|
|
86
|
+
* keyboard, judged by the policy like any change.
|
|
87
|
+
*
|
|
88
|
+
* Exported for a shell that wants it somewhere of its own; a shell that
|
|
89
|
+
* does not place it gets it anyway, docked at the corner of the face.
|
|
90
|
+
*/
|
|
91
|
+
export function PageUndo({ context, docked = false, }) {
|
|
92
|
+
usePlacedOnTheFace("undo");
|
|
93
|
+
return _jsx(TakeBack, { context: context, docked: docked });
|
|
94
|
+
}
|
|
95
|
+
function TakeBack({ context, docked }) {
|
|
96
|
+
const { change, takeBack, refused, said } = useTakeBack(context);
|
|
97
|
+
const corner = docked
|
|
98
|
+
? context.embedded
|
|
99
|
+
? { position: "sticky", bottom: 12, marginLeft: "auto", marginRight: 12, width: "fit-content" }
|
|
100
|
+
: { position: "fixed", right: 16, bottom: 16, zIndex: 30 }
|
|
101
|
+
: {};
|
|
102
|
+
return (_jsxs("div", { "data-testid": docked ? "face-undo-dock" : "page-undo-place", style: {
|
|
103
|
+
...corner,
|
|
104
|
+
display: "grid",
|
|
105
|
+
justifyItems: "end",
|
|
106
|
+
gap: 4,
|
|
107
|
+
// Clear of the Ask control at the other corner on a phone.
|
|
108
|
+
maxWidth: docked ? "min(30rem, calc(100vw - 140px))" : "100%",
|
|
109
|
+
minWidth: 0,
|
|
110
|
+
}, children: [_jsx("span", { role: "status", style: visuallyHidden, children: said }), change ? (_jsxs("button", { type: "button", "data-testid": "page-undo", title: `${takeBackWords(change)} — ⌘Z or Ctrl+Z`, "aria-keyshortcuts": "Meta+Z Control+Z", onClick: (event) => {
|
|
111
|
+
const face = event.currentTarget.closest("[data-graview-face]");
|
|
112
|
+
takeBack();
|
|
113
|
+
// After the face has drawn the change gone: if this control went with it, the keyboard lands on the page.
|
|
114
|
+
requestAnimationFrame(() => landOnThePage(face));
|
|
115
|
+
}, style: {
|
|
116
|
+
display: "inline-flex",
|
|
117
|
+
alignItems: "center",
|
|
118
|
+
gap: 6,
|
|
119
|
+
minHeight: 36,
|
|
120
|
+
maxWidth: "100%",
|
|
121
|
+
minWidth: 0,
|
|
122
|
+
padding: "0 14px",
|
|
123
|
+
borderRadius: 999,
|
|
124
|
+
border: "1px solid var(--graview-edge)",
|
|
125
|
+
background: "var(--graview-float)",
|
|
126
|
+
color: "var(--graview-ink)",
|
|
127
|
+
font: "inherit",
|
|
128
|
+
fontSize: "0.875rem",
|
|
129
|
+
boxShadow: docked ? "var(--graview-lift-low)" : undefined,
|
|
130
|
+
cursor: "pointer",
|
|
131
|
+
}, children: [_jsx("span", { "aria-hidden": "true", children: "\u21B6" }), _jsx("span", { style: { minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: takeBackWords(change) })] })) : null, refused ? (_jsx("span", { "data-testid": "page-undo-refused", role: "alert", style: {
|
|
132
|
+
fontSize: "0.8125rem",
|
|
133
|
+
lineHeight: 1.4,
|
|
134
|
+
color: "var(--graview-warn)",
|
|
135
|
+
background: docked ? "var(--graview-float)" : undefined,
|
|
136
|
+
padding: docked ? "4px 8px" : undefined,
|
|
137
|
+
borderRadius: 6,
|
|
138
|
+
}, children: refused })) : null] }));
|
|
139
|
+
}
|
|
140
|
+
const visuallyHidden = {
|
|
141
|
+
position: "absolute",
|
|
142
|
+
width: 1,
|
|
143
|
+
height: 1,
|
|
144
|
+
margin: -1,
|
|
145
|
+
padding: 0,
|
|
146
|
+
overflow: "hidden",
|
|
147
|
+
clip: "rect(0 0 0 0)",
|
|
148
|
+
whiteSpace: "nowrap",
|
|
149
|
+
border: 0,
|
|
150
|
+
};
|
|
151
|
+
/* ------------------------------------------------------------------ */
|
|
152
|
+
/* The root */
|
|
153
|
+
/* ------------------------------------------------------------------ */
|
|
154
|
+
/**
|
|
155
|
+
* ⌘Z AND CTRL+Z, on the face — not in a text field, where they are the
|
|
156
|
+
* field's own undo and a person typing a name expects the letters back, not
|
|
157
|
+
* the last change. Heard on the document while the keyboard is in the face,
|
|
158
|
+
* or on nothing at all when the face owns the document; never while it is
|
|
159
|
+
* on the host page around an embed.
|
|
160
|
+
*/
|
|
161
|
+
function useTakeBackKeys(context, root, on) {
|
|
162
|
+
const { store, principal, embedded } = context;
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (!on)
|
|
165
|
+
return;
|
|
166
|
+
const key = (event) => {
|
|
167
|
+
if (event.defaultPrevented || event.altKey || event.shiftKey)
|
|
168
|
+
return;
|
|
169
|
+
if (!(event.metaKey || event.ctrlKey) || event.key.toLowerCase() !== "z")
|
|
170
|
+
return;
|
|
171
|
+
if (editable(event.target))
|
|
172
|
+
return;
|
|
173
|
+
const target = event.target;
|
|
174
|
+
const inFace = target instanceof Node && root.current?.contains(target);
|
|
175
|
+
const onNothing = target === document.body || target === document.documentElement || target === document;
|
|
176
|
+
if (!inFace && !(onNothing && !embedded))
|
|
177
|
+
return;
|
|
178
|
+
const change = lastChangeOf(store, principal);
|
|
179
|
+
if (!change)
|
|
180
|
+
return;
|
|
181
|
+
event.preventDefault();
|
|
182
|
+
try {
|
|
183
|
+
store.undo(change.batch, principal ? { author: principal } : {});
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// The control says a refusal where it was asked; a key has nowhere to say it.
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
document.addEventListener("keydown", key);
|
|
190
|
+
return () => document.removeEventListener("keydown", key);
|
|
191
|
+
}, [store, principal, embedded, root, on]);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The face's root: it knows which of the face's controls a shell placed,
|
|
195
|
+
* and draws the rest — Find in a bar above the shell, the way back docked
|
|
196
|
+
* at the corner — before the shell in the document, so the keyboard reaches
|
|
197
|
+
* them first.
|
|
198
|
+
*/
|
|
199
|
+
export function FaceControlsRoot({ context, registry, root, children, }) {
|
|
200
|
+
const placed = useMemo(createPlaced, []);
|
|
201
|
+
const without = registry?.without() ?? new Set();
|
|
202
|
+
useTakeBackKeys(context, root, !without.has("undo"));
|
|
203
|
+
return (_jsxs(FaceControls.Provider, { value: placed, children: [_jsx(Fallbacks, { context: context, registry: registry, placed: placed, without: without }), children] }));
|
|
204
|
+
}
|
|
205
|
+
function Fallbacks({ context, registry, placed, without, }) {
|
|
206
|
+
// Drawn once the shell has had its say: its controls are placed in the same commit, before paint.
|
|
207
|
+
const [ready, setReady] = useState(false);
|
|
208
|
+
useLayoutEffect(() => setReady(true), []);
|
|
209
|
+
const findPlaced = usePlaced(placed, "find");
|
|
210
|
+
const undoPlaced = usePlaced(placed, "undo");
|
|
211
|
+
const location = useLocation();
|
|
212
|
+
if (!ready)
|
|
213
|
+
return null;
|
|
214
|
+
const segments = location.pathname.split("/").filter(Boolean);
|
|
215
|
+
const listKind = segments.length === 1 ? kindOfSlug(context.store.schema, segments[0]) : undefined;
|
|
216
|
+
// `?q=` narrows a list the face derived; a list of the app's own has its own words for that.
|
|
217
|
+
const narrows = listKind !== undefined && registry?.lookup(listKind, "list") === undefined;
|
|
218
|
+
return (_jsxs(_Fragment, { children: [!findPlaced && !without.has("find") ? (_jsx("div", { "data-testid": "face-find-bar", style: {
|
|
219
|
+
display: "flex",
|
|
220
|
+
alignItems: "center",
|
|
221
|
+
padding: "8px 16px",
|
|
222
|
+
borderBottom: "1px solid var(--graview-edge)",
|
|
223
|
+
background: "var(--graview-bar)",
|
|
224
|
+
minWidth: 0,
|
|
225
|
+
}, children: _jsx(FindInBar, { context: context, narrowsLists: narrows }) })) : null, !undoPlaced && !without.has("undo") ? _jsx(TakeBack, { context: context, docked: true }) : null] }));
|
|
226
|
+
}
|
|
227
|
+
/** The bar's Find box: `PageFind` without counting as one the shell placed. */
|
|
228
|
+
function FindInBar({ context, narrowsLists }) {
|
|
229
|
+
return (_jsx(FaceControls.Provider, { value: null, children: _jsx(PageFind, { context: context, narrowsLists: narrowsLists }) }));
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=face-controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"face-controls.js","sourceRoot":"","sources":["../src/face-controls.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which of the face's own controls a shell placed itself — so the face's
|
|
3
|
+
* root (face-controls.tsx) draws only the ones it did not.
|
|
4
|
+
*/
|
|
5
|
+
export type FaceControl = "find" | "undo";
|
|
6
|
+
export interface Placed {
|
|
7
|
+
readonly count: (control: FaceControl) => number;
|
|
8
|
+
readonly place: (control: FaceControl) => () => void;
|
|
9
|
+
readonly subscribe: (listener: () => void) => () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare const FaceControls: import("react").Context<Placed | null>;
|
|
12
|
+
export declare function createPlaced(): Placed;
|
|
13
|
+
/** A control a shell drew itself: the face's root then draws no second one. */
|
|
14
|
+
export declare function usePlacedOnTheFace(control: FaceControl): void;
|
|
15
|
+
export declare function usePlaced(placed: Placed, control: FaceControl): boolean;
|
|
16
|
+
//# sourceMappingURL=face-placed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"face-placed.d.ts","sourceRoot":"","sources":["../src/face-placed.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1C,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,MAAM,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,MAAM,IAAI,CAAC;IACrD,QAAQ,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;CAC1D;AAED,eAAO,MAAM,YAAY,wCAAqC,CAAC;AAE/D,wBAAgB,YAAY,IAAI,MAAM,CAqBrC;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAG7D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAMvE"}
|