@maple-kit/ui 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 +202 -0
- package/NOTICE +9 -0
- package/README.md +39 -0
- package/dist/composer/actions.d.ts +15 -0
- package/dist/composer/actions.js +59 -0
- package/dist/composer/attachments.d.ts +33 -0
- package/dist/composer/attachments.js +166 -0
- package/dist/composer/badge.d.ts +13 -0
- package/dist/composer/badge.js +25 -0
- package/dist/composer/body.d.ts +13 -0
- package/dist/composer/body.js +119 -0
- package/dist/composer/composer.d.ts +23 -0
- package/dist/composer/composer.js +122 -0
- package/dist/composer/css.js +559 -0
- package/dist/composer/detail.d.ts +32 -0
- package/dist/composer/detail.js +102 -0
- package/dist/composer/emoji.d.ts +54 -0
- package/dist/composer/emoji.js +131 -0
- package/dist/composer/index.d.ts +15 -0
- package/dist/composer/index.js +15 -0
- package/dist/composer/inset.js +20 -0
- package/dist/composer/leave.d.ts +19 -0
- package/dist/composer/leave.js +34 -0
- package/dist/composer/peek.d.ts +21 -0
- package/dist/composer/peek.js +29 -0
- package/dist/composer/phrase.d.ts +27 -0
- package/dist/composer/phrase.js +30 -0
- package/dist/composer/picker-emoji.d.ts +34 -0
- package/dist/composer/picker-emoji.js +48 -0
- package/dist/composer/prompt.js +31 -0
- package/dist/composer/scope.d.ts +11 -0
- package/dist/composer/scope.js +16 -0
- package/dist/composer/target.d.ts +13 -0
- package/dist/composer/target.js +45 -0
- package/dist/context.d.ts +21 -0
- package/dist/context.js +16 -0
- package/dist/data.d.ts +52 -0
- package/dist/data.js +30 -0
- package/dist/icons/branch.d.ts +4 -0
- package/dist/icons/branch.js +8 -0
- package/dist/icons/cog.d.ts +4 -0
- package/dist/icons/cog.js +8 -0
- package/dist/icons/commit.d.ts +4 -0
- package/dist/icons/commit.js +5 -0
- package/dist/icons/crossfade.d.ts +15 -0
- package/dist/icons/crossfade.js +27 -0
- package/dist/icons/element.d.ts +4 -0
- package/dist/icons/element.js +5 -0
- package/dist/icons/icon.d.ts +24 -0
- package/dist/icons/icon.js +27 -0
- package/dist/icons/index.d.ts +13 -0
- package/dist/icons/index.js +13 -0
- package/dist/icons/noplace.d.ts +4 -0
- package/dist/icons/noplace.js +5 -0
- package/dist/icons/region.d.ts +4 -0
- package/dist/icons/region.js +8 -0
- package/dist/icons/smiley.d.ts +4 -0
- package/dist/icons/smiley.js +8 -0
- package/dist/icons/sparkle.d.ts +4 -0
- package/dist/icons/sparkle.js +8 -0
- package/dist/icons/target.d.ts +4 -0
- package/dist/icons/target.js +5 -0
- package/dist/icons/text.d.ts +4 -0
- package/dist/icons/text.js +5 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/island/account.d.ts +10 -0
- package/dist/island/account.js +52 -0
- package/dist/island/comments.d.ts +24 -0
- package/dist/island/comments.js +40 -0
- package/dist/island/content.d.ts +10 -0
- package/dist/island/content.js +26 -0
- package/dist/island/context.d.ts +58 -0
- package/dist/island/context.js +23 -0
- package/dist/island/css.d.ts +13 -0
- package/dist/island/css.js +1020 -0
- package/dist/island/drag.d.ts +10 -0
- package/dist/island/drag.js +77 -0
- package/dist/island/filters.d.ts +10 -0
- package/dist/island/filters.js +63 -0
- package/dist/island/header.d.ts +32 -0
- package/dist/island/header.js +47 -0
- package/dist/island/index.d.ts +18 -0
- package/dist/island/index.js +18 -0
- package/dist/island/island.d.ts +12 -0
- package/dist/island/island.js +111 -0
- package/dist/island/item.d.ts +12 -0
- package/dist/island/item.js +158 -0
- package/dist/island/language.d.ts +55 -0
- package/dist/island/language.js +125 -0
- package/dist/island/leaf.js +17 -0
- package/dist/island/list.d.ts +12 -0
- package/dist/island/list.js +31 -0
- package/dist/island/new-comment.d.ts +18 -0
- package/dist/island/new-comment.js +56 -0
- package/dist/island/settings.d.ts +10 -0
- package/dist/island/settings.js +134 -0
- package/dist/island/stagger.d.ts +12 -0
- package/dist/island/stagger.js +4 -0
- package/dist/island/time.d.ts +17 -0
- package/dist/island/time.js +29 -0
- package/dist/island/trigger.d.ts +10 -0
- package/dist/island/trigger.js +39 -0
- package/dist/language.d.ts +45 -0
- package/dist/language.js +66 -0
- package/dist/maple.d.ts +25 -0
- package/dist/maple.js +65 -0
- package/dist/marks/avatar.d.ts +16 -0
- package/dist/marks/avatar.js +36 -0
- package/dist/marks/css.d.ts +12 -0
- package/dist/marks/css.js +371 -0
- package/dist/marks/frame.d.ts +25 -0
- package/dist/marks/frame.js +53 -0
- package/dist/marks/geometry.d.ts +55 -0
- package/dist/marks/geometry.js +61 -0
- package/dist/marks/index.d.ts +14 -0
- package/dist/marks/index.js +14 -0
- package/dist/marks/label.d.ts +23 -0
- package/dist/marks/label.js +31 -0
- package/dist/marks/layer.d.ts +16 -0
- package/dist/marks/layer.js +194 -0
- package/dist/marks/leaf.d.ts +18 -0
- package/dist/marks/leaf.js +7 -0
- package/dist/marks/mark.d.ts +28 -0
- package/dist/marks/mark.js +43 -0
- package/dist/marks/nudge.d.ts +50 -0
- package/dist/marks/nudge.js +76 -0
- package/dist/marks/paint.d.ts +11 -0
- package/dist/marks/paint.js +19 -0
- package/dist/marks/placement.d.ts +24 -0
- package/dist/marks/placement.js +28 -0
- package/dist/marks/ring.d.ts +28 -0
- package/dist/marks/ring.js +85 -0
- package/dist/marks/shape.d.ts +21 -0
- package/dist/marks/shape.js +51 -0
- package/dist/notice/css.d.ts +11 -0
- package/dist/notice/css.js +74 -0
- package/dist/notice/index.d.ts +4 -0
- package/dist/notice/index.js +4 -0
- package/dist/notice/language.d.ts +15 -0
- package/dist/notice/language.js +14 -0
- package/dist/notice/notice.d.ts +14 -0
- package/dist/notice/notice.js +49 -0
- package/dist/part.d.ts +9 -0
- package/dist/part.js +11 -0
- package/dist/picker/css.d.ts +13 -0
- package/dist/picker/css.js +141 -0
- package/dist/picker/index.d.ts +6 -0
- package/dist/picker/index.js +6 -0
- package/dist/picker/language.d.ts +14 -0
- package/dist/picker/language.js +17 -0
- package/dist/picker/picker.d.ts +18 -0
- package/dist/picker/picker.js +164 -0
- package/dist/picker/session.d.ts +16 -0
- package/dist/picker/session.js +35 -0
- package/dist/picker/target.d.ts +23 -0
- package/dist/picker/target.js +37 -0
- package/dist/root.d.ts +42 -0
- package/dist/root.js +75 -0
- package/dist/shots.js +32 -0
- package/dist/slot.d.ts +28 -0
- package/dist/slot.js +52 -0
- package/dist/slots.d.ts +24 -0
- package/dist/slots.js +75 -0
- package/dist/stylesheet.d.ts +15 -0
- package/dist/stylesheet.js +188 -0
- package/dist/theme.d.ts +20 -0
- package/dist/theme.js +13 -0
- package/dist/tip.d.ts +40 -0
- package/dist/tip.js +82 -0
- package/dist/tokens.d.ts +28 -0
- package/dist/tokens.js +180 -0
- package/package.json +56 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useMapleUi } from "../context.js";
|
|
2
|
+
import { composeRefs } from "../slot.js";
|
|
3
|
+
import { culled, ringBox, runBox } from "./geometry.js";
|
|
4
|
+
import { useFrameLoop, viewportHeight } from "./frame.js";
|
|
5
|
+
import { MOVING_ATTRIBUTE, OFF_ATTRIBUTE, flag, place } from "./paint.js";
|
|
6
|
+
import { createElement, forwardRef, useCallback, useRef, useState } from "react";
|
|
7
|
+
import { regionBox } from "@maple-kit/core/anchor";
|
|
8
|
+
//#region src/marks/ring.ts
|
|
9
|
+
const BELOW_ATTRIBUTE = "data-mk-below";
|
|
10
|
+
const LABEL_ROOM_PX = 26;
|
|
11
|
+
const NOTE_ROOM_PX = 40;
|
|
12
|
+
const PART = "Maple.TargetRing";
|
|
13
|
+
function linesOf(target) {
|
|
14
|
+
return "startContainer" in target ? [...target.getClientRects()] : [];
|
|
15
|
+
}
|
|
16
|
+
function empty(rect) {
|
|
17
|
+
return rect.width === 0 && rect.height === 0;
|
|
18
|
+
}
|
|
19
|
+
const MapleTargetRing = forwardRef(function MapleTargetRing(props, ref) {
|
|
20
|
+
const { className, label, note, region, state = "composing", target } = props;
|
|
21
|
+
const room = note === void 0 ? LABEL_ROOM_PX : NOTE_ROOM_PX;
|
|
22
|
+
const { container } = useMapleUi(PART);
|
|
23
|
+
const ring = useRef(null);
|
|
24
|
+
const cap = useRef(null);
|
|
25
|
+
const runs = useRef([]);
|
|
26
|
+
const [lines, setLines] = useState(0);
|
|
27
|
+
const paint = useCallback((moving) => {
|
|
28
|
+
const node = ring.current;
|
|
29
|
+
if (!node || !target) return;
|
|
30
|
+
const box = target.getBoundingClientRect();
|
|
31
|
+
const rect = region === void 0 ? box : regionBox(box, region);
|
|
32
|
+
const away = empty(rect) || culled(rect, viewportHeight(container));
|
|
33
|
+
flag(node, OFF_ATTRIBUTE, away);
|
|
34
|
+
flag(node, MOVING_ATTRIBUTE, moving);
|
|
35
|
+
if (away) return;
|
|
36
|
+
place(node, ringBox(rect));
|
|
37
|
+
if (cap.current) flag(cap.current, BELOW_ATTRIBUTE, rect.y < room);
|
|
38
|
+
const boxes = region === void 0 ? linesOf(target) : [];
|
|
39
|
+
if (boxes.length !== lines) setLines(boxes.length);
|
|
40
|
+
boxes.forEach((box, index) => {
|
|
41
|
+
const run = runs.current[index];
|
|
42
|
+
if (run) place(run, runBox(box, rect));
|
|
43
|
+
});
|
|
44
|
+
}, [
|
|
45
|
+
container,
|
|
46
|
+
lines,
|
|
47
|
+
region,
|
|
48
|
+
room,
|
|
49
|
+
target
|
|
50
|
+
]);
|
|
51
|
+
useFrameLoop(PART, paint);
|
|
52
|
+
if (!target) return null;
|
|
53
|
+
return createElement("div", {
|
|
54
|
+
className: className ? `mk-ring ${className}` : "mk-ring",
|
|
55
|
+
"data-mk-state": state,
|
|
56
|
+
"data-mk-passage": String(region === void 0 && "startContainer" in target),
|
|
57
|
+
"data-mk-region": String(region !== void 0),
|
|
58
|
+
ref: composeRefs(ref, (node) => {
|
|
59
|
+
ring.current = node;
|
|
60
|
+
})
|
|
61
|
+
}, label === void 0 ? null : caption(label, note, cap), ...run(lines, runs));
|
|
62
|
+
});
|
|
63
|
+
function caption(label, note, ref) {
|
|
64
|
+
return createElement("span", {
|
|
65
|
+
className: "mk-ring-label",
|
|
66
|
+
ref
|
|
67
|
+
}, createElement("span", {
|
|
68
|
+
key: "name",
|
|
69
|
+
className: "mk-ring-name mk-mono"
|
|
70
|
+
}, label), note === void 0 ? null : createElement("span", {
|
|
71
|
+
key: "note",
|
|
72
|
+
className: "mk-ring-note mk-mono"
|
|
73
|
+
}, note));
|
|
74
|
+
}
|
|
75
|
+
function run(lines, runs) {
|
|
76
|
+
return Array.from({ length: lines }, (_, index) => createElement("div", {
|
|
77
|
+
key: index,
|
|
78
|
+
className: "mk-ring-run",
|
|
79
|
+
ref: (node) => {
|
|
80
|
+
runs.current[index] = node;
|
|
81
|
+
}
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
export { BELOW_ATTRIBUTE, MapleTargetRing };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { PartForm } from "../data.js";
|
|
2
|
+
import { ReactElement, ReactNode } from "react";
|
|
3
|
+
//#region src/marks/shape.d.ts
|
|
4
|
+
/** Half, which is what a waterline means when nothing says otherwise. */
|
|
5
|
+
export declare const HALF = 0.5;
|
|
6
|
+
/** A fresh clip id. Called once per instance, never once per render. */
|
|
7
|
+
export declare function nextClipId(): string;
|
|
8
|
+
/** How one leaf is drawn. Fill says the life, the edge says the confidence. */
|
|
9
|
+
export interface LeafProps {
|
|
10
|
+
readonly form?: PartForm;
|
|
11
|
+
/** How much of the leaf the waterline leaves filled. */
|
|
12
|
+
readonly fraction?: number;
|
|
13
|
+
/** The halo behind it. Off wherever the leaf already sits on one of ours. */
|
|
14
|
+
readonly halo?: boolean;
|
|
15
|
+
/** The clip id, when a caller renders many and wants them stable. */
|
|
16
|
+
readonly clipId?: string;
|
|
17
|
+
readonly className?: string;
|
|
18
|
+
}
|
|
19
|
+
/** One leaf, drawn in the form its comment's life is at. */
|
|
20
|
+
export declare function MapleLeaf(props: LeafProps): ReactElement;
|
|
21
|
+
//#endregion
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { LEAF_OUTLINE, LEAF_ROTATION, LEAF_SOLID, LEAF_VIEW_BOX } from "./leaf.js";
|
|
2
|
+
import { waterline } from "./geometry.js";
|
|
3
|
+
import { cloneElement, createElement, isValidElement, useState } from "react";
|
|
4
|
+
//#region src/marks/shape.ts
|
|
5
|
+
const HALF = .5;
|
|
6
|
+
let sequence = 0;
|
|
7
|
+
function nextClipId() {
|
|
8
|
+
sequence += 1;
|
|
9
|
+
return `mk-leaf-${sequence}`;
|
|
10
|
+
}
|
|
11
|
+
function inside(asChild, children, face) {
|
|
12
|
+
if (!asChild) return face;
|
|
13
|
+
return isValidElement(children) ? [cloneElement(children, {}, ...face)] : [children];
|
|
14
|
+
}
|
|
15
|
+
function tilted(key, child) {
|
|
16
|
+
return createElement("g", {
|
|
17
|
+
key,
|
|
18
|
+
transform: LEAF_ROTATION
|
|
19
|
+
}, child);
|
|
20
|
+
}
|
|
21
|
+
function path(className, d) {
|
|
22
|
+
return createElement("path", {
|
|
23
|
+
className,
|
|
24
|
+
d
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function clipped(id, fraction) {
|
|
28
|
+
return [createElement("defs", { key: "defs" }, createElement("clipPath", { id }, createElement("rect", waterline(fraction)))), createElement("g", {
|
|
29
|
+
key: "fill",
|
|
30
|
+
clipPath: `url(#${id})`
|
|
31
|
+
}, tilted("tilt", path("mk-leaf-body", LEAF_SOLID)))];
|
|
32
|
+
}
|
|
33
|
+
function body(form, id, fraction) {
|
|
34
|
+
const edge = tilted("edge", path("mk-leaf-edge", LEAF_OUTLINE));
|
|
35
|
+
if (form === "outline") return [edge];
|
|
36
|
+
if (form === "partial") return [...clipped(id, fraction), edge];
|
|
37
|
+
return [tilted("body", path("mk-leaf-body", LEAF_SOLID))];
|
|
38
|
+
}
|
|
39
|
+
function MapleLeaf(props) {
|
|
40
|
+
const { className, clipId, form = "solid", fraction = HALF, halo = true } = props;
|
|
41
|
+
const [generated] = useState(nextClipId);
|
|
42
|
+
const id = clipId ?? generated;
|
|
43
|
+
return createElement("svg", {
|
|
44
|
+
className: className ? `mk-leaf ${className}` : "mk-leaf",
|
|
45
|
+
viewBox: LEAF_VIEW_BOX,
|
|
46
|
+
"aria-hidden": true,
|
|
47
|
+
focusable: false
|
|
48
|
+
}, halo ? tilted("halo", path("mk-leaf-halo", LEAF_SOLID)) : null, ...body(form, id, fraction));
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { HALF, MapleLeaf, inside, nextClipId };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/notice/css.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The notice's half of the adopted stylesheet.
|
|
4
|
+
*
|
|
5
|
+
* One band, tinted by the kind. It sits in the flow rather than over anything:
|
|
6
|
+
* a failure covered by a toast that leaves after four seconds is a failure a
|
|
7
|
+
* reviewer has to catch, and nothing here is in a hurry.
|
|
8
|
+
*/
|
|
9
|
+
/** The notice's rules. Composed by `src/stylesheet.ts`, never imported alone. */
|
|
10
|
+
export declare function noticeCss(): string;
|
|
11
|
+
//#endregion
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//#region src/notice/css.ts
|
|
2
|
+
function noticeCss() {
|
|
3
|
+
return `
|
|
4
|
+
.mk-notice {
|
|
5
|
+
flex: none;
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: flex-start;
|
|
8
|
+
gap: 8px;
|
|
9
|
+
padding: 8px 9px 8px 11px;
|
|
10
|
+
border-bottom: 1px solid var(--mk-line);
|
|
11
|
+
background: var(--mk-warn-sub);
|
|
12
|
+
color: var(--mk-fg);
|
|
13
|
+
font-size: 11.5px;
|
|
14
|
+
line-height: 1.4;
|
|
15
|
+
animation: mk-notice-in var(--mk-dur-swap) var(--mk-ease-surface);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@keyframes mk-notice-in {
|
|
19
|
+
from {
|
|
20
|
+
opacity: 0;
|
|
21
|
+
translate: 0 calc(-1 * var(--mk-rise-row));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* A 401 is a door, not a fault: the reviewer signs in and it is gone. The
|
|
26
|
+
warm tint is for the ones nobody on this page can do anything about. */
|
|
27
|
+
.mk-notice[data-mk-kind="unauthorized"] {
|
|
28
|
+
background: var(--mk-info-sub);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.mk-notice-said {
|
|
32
|
+
flex: 1 1 auto;
|
|
33
|
+
text-wrap: pretty;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.mk-notice-do {
|
|
37
|
+
flex: none;
|
|
38
|
+
padding: 3px 9px;
|
|
39
|
+
border: 1px solid var(--mk-line-firm);
|
|
40
|
+
border-radius: 999px;
|
|
41
|
+
background: var(--mk-bg);
|
|
42
|
+
color: var(--mk-fg);
|
|
43
|
+
font: inherit;
|
|
44
|
+
font-size: 11px;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.mk-notice-off {
|
|
50
|
+
flex: none;
|
|
51
|
+
padding: 0 2px;
|
|
52
|
+
border: 0;
|
|
53
|
+
background: transparent;
|
|
54
|
+
color: var(--mk-faint);
|
|
55
|
+
font: inherit;
|
|
56
|
+
font-size: 11px;
|
|
57
|
+
line-height: 1.4;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.mk-notice-off:hover {
|
|
62
|
+
color: var(--mk-fg);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* In the composer it is the last thing above the buttons, so it closes the
|
|
66
|
+
panel's bottom edge rather than cutting a line across it. */
|
|
67
|
+
.mk-composer .mk-notice {
|
|
68
|
+
border-bottom: 0;
|
|
69
|
+
border-top: 1px solid var(--mk-line);
|
|
70
|
+
}
|
|
71
|
+
`.trim();
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { noticeCss };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FailureKind } from "@maple-kit/core/client";
|
|
2
|
+
//#region src/notice/language.d.ts
|
|
3
|
+
/** The notice's own words. */
|
|
4
|
+
export declare const NOTICE_COPY: {
|
|
5
|
+
readonly label: "Something went wrong";
|
|
6
|
+
readonly retry: "Try again";
|
|
7
|
+
readonly signIn: "Sign in";
|
|
8
|
+
readonly dismiss: "Dismiss";
|
|
9
|
+
readonly dismissGlyph: "✕";
|
|
10
|
+
/** Said under a 401 on a deployment that offers no sign-in at all. */
|
|
11
|
+
readonly noSignIn: "This deployment has nowhere to put your comments yet.";
|
|
12
|
+
};
|
|
13
|
+
/** Whether the offer is a sign-in or a retry. A 401 is nobody's to retry. */
|
|
14
|
+
export declare function offersSignIn(kind: FailureKind, linkable: boolean): boolean;
|
|
15
|
+
//#endregion
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/notice/language.ts
|
|
2
|
+
const NOTICE_COPY = {
|
|
3
|
+
label: "Something went wrong",
|
|
4
|
+
retry: "Try again",
|
|
5
|
+
signIn: "Sign in",
|
|
6
|
+
dismiss: "Dismiss",
|
|
7
|
+
dismissGlyph: "✕",
|
|
8
|
+
noSignIn: "This deployment has nowhere to put your comments yet."
|
|
9
|
+
};
|
|
10
|
+
function offersSignIn(kind, linkable) {
|
|
11
|
+
return kind === "unauthorized" && linkable;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { NOTICE_COPY, offersSignIn };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PartProps } from "../part.js";
|
|
2
|
+
import { FailedCall } from "@maple-kit/core/client";
|
|
3
|
+
//#region src/notice/notice.d.ts
|
|
4
|
+
/** Which failures this notice answers for. */
|
|
5
|
+
export interface MapleNoticeProps extends PartProps {
|
|
6
|
+
/**
|
|
7
|
+
* The calls this notice speaks for. A notice beside the send button should
|
|
8
|
+
* not also announce a failed load behind it. Every call unless given.
|
|
9
|
+
*/
|
|
10
|
+
readonly during?: readonly FailedCall[];
|
|
11
|
+
}
|
|
12
|
+
/** The failure, in a line, with the one thing that would fix it. */
|
|
13
|
+
export declare const MapleNotice: import("react").ForwardRefExoticComponent<MapleNoticeProps & import("react").RefAttributes<HTMLElement>>;
|
|
14
|
+
//#endregion
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { cx, renderPart } from "../part.js";
|
|
2
|
+
import { NOTICE_COPY, offersSignIn } from "./language.js";
|
|
3
|
+
import { createElement, forwardRef } from "react";
|
|
4
|
+
import { useMaple, useMapleClient } from "@maple-kit/react";
|
|
5
|
+
//#region src/notice/notice.ts
|
|
6
|
+
const MapleNotice = forwardRef(function MapleNotice(props, ref) {
|
|
7
|
+
const { asChild, className, during, ...rest } = props;
|
|
8
|
+
const { error, github } = useMaple();
|
|
9
|
+
const client = useMapleClient();
|
|
10
|
+
if (!error || during && !during.includes(error.during)) return null;
|
|
11
|
+
return renderPart("div", asChild, {
|
|
12
|
+
...rest,
|
|
13
|
+
role: "alert",
|
|
14
|
+
"data-mk-kind": error.kind,
|
|
15
|
+
className: cx("mk-notice", className),
|
|
16
|
+
ref
|
|
17
|
+
}, [
|
|
18
|
+
createElement("span", {
|
|
19
|
+
key: "said",
|
|
20
|
+
className: "mk-notice-said"
|
|
21
|
+
}, said(error, github)),
|
|
22
|
+
offer(error, github, client),
|
|
23
|
+
createElement("button", {
|
|
24
|
+
key: "off",
|
|
25
|
+
type: "button",
|
|
26
|
+
className: "mk-notice-off mk-hit",
|
|
27
|
+
"aria-label": NOTICE_COPY.dismiss,
|
|
28
|
+
onClick: () => client.clearError()
|
|
29
|
+
}, NOTICE_COPY.dismissGlyph)
|
|
30
|
+
]);
|
|
31
|
+
});
|
|
32
|
+
function said(error, github) {
|
|
33
|
+
return error.kind === "unauthorized" && github.state === "unsupported" ? NOTICE_COPY.noSignIn : error.message;
|
|
34
|
+
}
|
|
35
|
+
function offer(error, github, client) {
|
|
36
|
+
if (offersSignIn(error.kind, github.state === "failed" || github.state === "unlinked")) return button(NOTICE_COPY.signIn, () => void client.linkGitHub());
|
|
37
|
+
if (error.kind === "unauthorized") return null;
|
|
38
|
+
return error.during === "load" ? button(NOTICE_COPY.retry, () => void client.load()) : null;
|
|
39
|
+
}
|
|
40
|
+
function button(label, onClick) {
|
|
41
|
+
return createElement("button", {
|
|
42
|
+
key: "do",
|
|
43
|
+
type: "button",
|
|
44
|
+
className: "mk-notice-do mk-press",
|
|
45
|
+
onClick
|
|
46
|
+
}, label);
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { MapleNotice };
|
package/dist/part.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ElementType, ReactNode } from "react";
|
|
2
|
+
//#region src/part.d.ts
|
|
3
|
+
/** The two props every part has beyond its own element's. */
|
|
4
|
+
export interface PartProps {
|
|
5
|
+
/** Render the single child instead of the part's own element. */
|
|
6
|
+
readonly asChild?: boolean;
|
|
7
|
+
readonly className?: string;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
package/dist/part.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Slot } from "./slot.js";
|
|
2
|
+
import { createElement } from "react";
|
|
3
|
+
//#region src/part.ts
|
|
4
|
+
function cx(...names) {
|
|
5
|
+
return names.filter(Boolean).join(" ");
|
|
6
|
+
}
|
|
7
|
+
function renderPart(tag, asChild, props, children) {
|
|
8
|
+
return createElement(asChild ? Slot : tag, props, children);
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { cx, renderPart };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/picker/css.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The picker's half of the adopted stylesheet.
|
|
4
|
+
*
|
|
5
|
+
* Two surfaces and a band. The shield is what gives element and region their
|
|
6
|
+
* cursor and keeps a click off the island, so it exists for those two and not
|
|
7
|
+
* for a passage: text under an overlay cannot be selected, whatever the
|
|
8
|
+
* cursor over it says. Every value is a token, so reduced motion is honoured
|
|
9
|
+
* by redefining the tokens rather than by switching these rules off.
|
|
10
|
+
*/
|
|
11
|
+
/** The picker's rules. Composed by `src/stylesheet.ts`, never imported alone. */
|
|
12
|
+
export declare function pickerCss(): string;
|
|
13
|
+
//#endregion
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
//#region src/picker/css.ts
|
|
2
|
+
function pickerCss() {
|
|
3
|
+
return `
|
|
4
|
+
${shield()}
|
|
5
|
+
|
|
6
|
+
${band()}
|
|
7
|
+
|
|
8
|
+
${bar()}
|
|
9
|
+
`.trim();
|
|
10
|
+
}
|
|
11
|
+
function shield() {
|
|
12
|
+
return `
|
|
13
|
+
.mk-shield {
|
|
14
|
+
position: fixed;
|
|
15
|
+
inset: 0;
|
|
16
|
+
z-index: 3;
|
|
17
|
+
pointer-events: auto;
|
|
18
|
+
cursor: crosshair;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.mk-shield-drag {
|
|
22
|
+
cursor: cell;
|
|
23
|
+
}
|
|
24
|
+
`.trim();
|
|
25
|
+
}
|
|
26
|
+
function band() {
|
|
27
|
+
return `
|
|
28
|
+
.mk-band {
|
|
29
|
+
position: fixed;
|
|
30
|
+
top: 0;
|
|
31
|
+
left: 0;
|
|
32
|
+
z-index: 4;
|
|
33
|
+
translate: var(--mk-x) var(--mk-y);
|
|
34
|
+
width: var(--mk-w);
|
|
35
|
+
height: var(--mk-h);
|
|
36
|
+
border: 1.5px solid var(--mk-accent);
|
|
37
|
+
border-radius: var(--mk-r-xs);
|
|
38
|
+
background: color-mix(in oklab, var(--mk-accent) 12%, transparent);
|
|
39
|
+
pointer-events: none;
|
|
40
|
+
}
|
|
41
|
+
`.trim();
|
|
42
|
+
}
|
|
43
|
+
function bar() {
|
|
44
|
+
return `
|
|
45
|
+
.mk-pick-bar {
|
|
46
|
+
position: fixed;
|
|
47
|
+
top: 12px;
|
|
48
|
+
left: 50%;
|
|
49
|
+
translate: -50% 0;
|
|
50
|
+
z-index: 5;
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
gap: 10px;
|
|
54
|
+
max-width: calc(100vw - 24px);
|
|
55
|
+
padding: 6px 6px 6px 12px;
|
|
56
|
+
border: 1px solid var(--mk-line-firm);
|
|
57
|
+
border-radius: 999px;
|
|
58
|
+
background: var(--mk-bg);
|
|
59
|
+
box-shadow: var(--mk-sh2);
|
|
60
|
+
color: var(--mk-fg);
|
|
61
|
+
font: inherit;
|
|
62
|
+
font-size: 12px;
|
|
63
|
+
pointer-events: auto;
|
|
64
|
+
animation: mk-pick-in var(--mk-dur-island-open) var(--mk-ease-entrance);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@keyframes mk-pick-in {
|
|
68
|
+
from {
|
|
69
|
+
opacity: 0;
|
|
70
|
+
translate: -50% calc(-1 * var(--mk-rise-row));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.mk-pick-say {
|
|
75
|
+
font-weight: 600;
|
|
76
|
+
white-space: nowrap;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.mk-pick-kinds {
|
|
80
|
+
display: flex;
|
|
81
|
+
gap: 2px;
|
|
82
|
+
padding: 2px;
|
|
83
|
+
border-radius: 999px;
|
|
84
|
+
background: var(--mk-sunk);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.mk-pick-kind {
|
|
88
|
+
padding: 2px 9px;
|
|
89
|
+
border: 0;
|
|
90
|
+
border-radius: 999px;
|
|
91
|
+
background: transparent;
|
|
92
|
+
color: var(--mk-muted);
|
|
93
|
+
font: inherit;
|
|
94
|
+
font-size: 11px;
|
|
95
|
+
font-weight: 600;
|
|
96
|
+
text-transform: capitalize;
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
transition:
|
|
99
|
+
background-color var(--mk-dur-swap) var(--mk-ease-swap),
|
|
100
|
+
color var(--mk-dur-swap) var(--mk-ease-swap);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.mk-pick-kind[aria-pressed="true"] {
|
|
104
|
+
background: var(--mk-bg);
|
|
105
|
+
box-shadow: var(--mk-sh1);
|
|
106
|
+
color: var(--mk-fg);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.mk-pick-stop {
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
gap: 5px;
|
|
113
|
+
padding: 3px 10px;
|
|
114
|
+
border: 1px solid var(--mk-line);
|
|
115
|
+
border-radius: 999px;
|
|
116
|
+
background: transparent;
|
|
117
|
+
color: var(--mk-muted);
|
|
118
|
+
font: inherit;
|
|
119
|
+
font-size: 11px;
|
|
120
|
+
font-weight: 600;
|
|
121
|
+
cursor: pointer;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.mk-pick-stop:hover {
|
|
125
|
+
border-color: var(--mk-line-firm);
|
|
126
|
+
color: var(--mk-fg);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.mk-kbd {
|
|
130
|
+
padding: 0 4px;
|
|
131
|
+
border: 1px solid var(--mk-line);
|
|
132
|
+
border-radius: var(--mk-r-xs);
|
|
133
|
+
background: var(--mk-sunk);
|
|
134
|
+
font-family: var(--mk-mono);
|
|
135
|
+
font-size: 10px;
|
|
136
|
+
line-height: 15px;
|
|
137
|
+
}
|
|
138
|
+
`.trim();
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
export { pickerCss };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { pickerCss } from "./css.js";
|
|
2
|
+
import { PICKER_COPY, PICK_HINTS, pickerLabel } from "./language.js";
|
|
3
|
+
import { MaplePicker, MaplePickerProps, onOwnControl } from "./picker.js";
|
|
4
|
+
import { SessionHandlers, startSession } from "./session.js";
|
|
5
|
+
import { TargetOptions, elementOf, targetFor } from "./target.js";
|
|
6
|
+
export { MaplePicker, type MaplePickerProps, PICKER_COPY, PICK_HINTS, MaplePicker as Picker, type SessionHandlers, type TargetOptions, elementOf, onOwnControl, pickerCss, pickerLabel, startSession, targetFor };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { pickerCss } from "./css.js";
|
|
2
|
+
import { PICKER_COPY, PICK_HINTS, pickerLabel } from "./language.js";
|
|
3
|
+
import { startSession } from "./session.js";
|
|
4
|
+
import { elementOf, targetFor } from "./target.js";
|
|
5
|
+
import { MaplePicker, onOwnControl } from "./picker.js";
|
|
6
|
+
export { MaplePicker, PICKER_COPY, PICK_HINTS, MaplePicker as Picker, elementOf, onOwnControl, pickerCss, pickerLabel, startSession, targetFor };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PickKind } from "@maple-kit/core/client";
|
|
2
|
+
//#region src/picker/language.d.ts
|
|
3
|
+
/** The instruction for each pick, which is the gesture and nothing else. */
|
|
4
|
+
export declare const PICK_HINTS: Readonly<Record<PickKind, string>>;
|
|
5
|
+
/** The picker's own words. */
|
|
6
|
+
export declare const PICKER_COPY: {
|
|
7
|
+
readonly cancel: "Cancel";
|
|
8
|
+
readonly cancelHint: "Esc";
|
|
9
|
+
readonly bar: "Choosing what to comment on";
|
|
10
|
+
readonly cycle: "Press t to cycle";
|
|
11
|
+
};
|
|
12
|
+
/** What a screen reader hears on the bar while a pick is armed. */
|
|
13
|
+
export declare function pickerLabel(kind: PickKind): string;
|
|
14
|
+
//#endregion
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/picker/language.ts
|
|
2
|
+
const PICK_HINTS = {
|
|
3
|
+
element: "Click anything on the page",
|
|
4
|
+
region: "Drag a box around the area",
|
|
5
|
+
text: "Select the passage"
|
|
6
|
+
};
|
|
7
|
+
const PICKER_COPY = {
|
|
8
|
+
cancel: "Cancel",
|
|
9
|
+
cancelHint: "Esc",
|
|
10
|
+
bar: "Choosing what to comment on",
|
|
11
|
+
cycle: "Press t to cycle"
|
|
12
|
+
};
|
|
13
|
+
function pickerLabel(kind) {
|
|
14
|
+
return `${PICKER_COPY.bar}: ${PICK_HINTS[kind]}.`;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { PICKER_COPY, PICK_HINTS, pickerLabel };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/picker/picker.d.ts
|
|
3
|
+
/** How the picker behaves. Everything it needs beyond this is the controller's. */
|
|
4
|
+
export interface MaplePickerProps {
|
|
5
|
+
/** The bar naming the gesture. On unless an application draws its own. */
|
|
6
|
+
readonly hint?: boolean;
|
|
7
|
+
readonly className?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Runs the armed pick, and closes it into the composer. */
|
|
10
|
+
export declare function MaplePicker(props: MaplePickerProps): ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* True when the event's real target is one of the overlay's own controls. The
|
|
13
|
+
* shield is the exception: it exists to be clicked through. Everything else in
|
|
14
|
+
* the shadow root is a control, and a control that also picked the page behind
|
|
15
|
+
* it would be unusable.
|
|
16
|
+
*/
|
|
17
|
+
export declare function onOwnControl(event: Event, root: ShadowRoot): boolean;
|
|
18
|
+
//#endregion
|