@gjermundgaraba-designs/pixel-paper 0.22.2 → 0.23.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/DESIGN.md +13 -13
- package/dist/src/adapters/mermaid.d.ts.map +1 -1
- package/dist/src/adapters/mermaid.js +0 -24
- package/dist/src/announcer/announcer.d.ts.map +1 -1
- package/dist/src/announcer/announcer.js +12 -12
- package/dist/src/board/board.d.ts +0 -10
- package/dist/src/board/board.d.ts.map +1 -1
- package/dist/src/board/board.js +36 -64
- package/dist/src/board/index.d.ts +2 -1
- package/dist/src/board/index.d.ts.map +1 -1
- package/dist/src/board/index.js +2 -1
- package/dist/src/button/button.d.ts +1 -1
- package/dist/src/button/button.d.ts.map +1 -1
- package/dist/src/commandPalette/commandPalette.d.ts.map +1 -1
- package/dist/src/commandPalette/commandPalette.js +78 -83
- package/dist/src/commandPalette/keys.d.ts +2 -2
- package/dist/src/commandPalette/keys.d.ts.map +1 -1
- package/dist/src/commandPalette/keys.js +2 -4
- package/dist/src/commandPalette/match.d.ts.map +1 -1
- package/dist/src/commandPalette/match.js +2 -2
- package/dist/src/confirmDialog/confirmDialog.d.ts.map +1 -1
- package/dist/src/confirmDialog/confirmDialog.js +1 -2
- package/dist/src/copyButton/copyButton.d.ts +0 -5
- package/dist/src/copyButton/copyButton.d.ts.map +1 -1
- package/dist/src/copyButton/copyButton.js +2 -2
- package/dist/src/crumbs/crumbs.d.ts +0 -2
- package/dist/src/crumbs/crumbs.d.ts.map +1 -1
- package/dist/src/crumbs/crumbs.js +2 -2
- package/dist/src/dialog/dialog.d.ts +2 -0
- package/dist/src/dialog/dialog.d.ts.map +1 -1
- package/dist/src/dialog/dialog.js +3 -2
- package/dist/src/drag/drag.d.ts +28 -0
- package/dist/src/drag/drag.d.ts.map +1 -1
- package/dist/src/drag/drag.js +39 -17
- package/dist/src/editableTitle/editableTitle.d.ts +4 -4
- package/dist/src/field/field.d.ts +7 -1
- package/dist/src/field/field.d.ts.map +1 -1
- package/dist/src/field/field.js +14 -6
- package/dist/src/fieldset/fieldset.d.ts.map +1 -1
- package/dist/src/fieldset/fieldset.js +2 -4
- package/dist/src/fileTree/fileTree.d.ts.map +1 -1
- package/dist/src/fileTree/fileTree.js +16 -16
- package/dist/src/inlineInput/inlineInput.d.ts +4 -0
- package/dist/src/inlineInput/inlineInput.d.ts.map +1 -1
- package/dist/src/inlineInput/inlineInput.js +9 -6
- package/dist/src/keyHelp/index.d.ts +1 -1
- package/dist/src/keyHelp/index.d.ts.map +1 -1
- package/dist/src/keyHelp/index.js +1 -1
- package/dist/src/keyHelp/keyHelp.d.ts +1 -3
- package/dist/src/keyHelp/keyHelp.d.ts.map +1 -1
- package/dist/src/keyHelp/keyHelp.js +3 -6
- package/dist/src/keyHint/keyLabel.d.ts +2 -0
- package/dist/src/keyHint/keyLabel.d.ts.map +1 -1
- package/dist/src/keyHint/keyLabel.js +3 -2
- package/dist/src/lineDiff/diff.d.ts +6 -3
- package/dist/src/lineDiff/diff.d.ts.map +1 -1
- package/dist/src/lineDiff/diff.js +40 -28
- package/dist/src/lineDiff/patch.d.ts.map +1 -1
- package/dist/src/lineDiff/patch.js +18 -13
- package/dist/src/linkState/linkState.d.ts.map +1 -1
- package/dist/src/linkState/linkState.js +1 -7
- package/dist/src/menu/slip.js +2 -2
- package/dist/src/meter/meter.d.ts.map +1 -1
- package/dist/src/meter/meter.js +22 -17
- package/dist/src/notice/notice.d.ts +2 -0
- package/dist/src/notice/notice.d.ts.map +1 -1
- package/dist/src/notice/notice.js +2 -1
- package/dist/src/passwordField/passwordField.d.ts.map +1 -1
- package/dist/src/passwordField/passwordField.js +3 -7
- package/dist/src/rail/rail.d.ts +17 -3
- package/dist/src/rail/rail.d.ts.map +1 -1
- package/dist/src/rail/rail.js +6 -6
- package/dist/src/reel/layout.d.ts +9 -5
- package/dist/src/reel/layout.d.ts.map +1 -1
- package/dist/src/reel/layout.js +11 -5
- package/dist/src/reel/reel.d.ts.map +1 -1
- package/dist/src/reel/reel.js +122 -68
- package/dist/src/scheme/scheme.d.ts +1 -1
- package/dist/src/scheme/scheme.d.ts.map +1 -1
- package/dist/src/segmented/segmented.d.ts +0 -3
- package/dist/src/segmented/segmented.d.ts.map +1 -1
- package/dist/src/segmented/segmented.js +1 -6
- package/dist/src/selectField/selectField.d.ts.map +1 -1
- package/dist/src/selectField/selectField.js +3 -10
- package/dist/src/sortable/index.d.ts +1 -1
- package/dist/src/sortable/index.d.ts.map +1 -1
- package/dist/src/sortable/index.js +1 -1
- package/dist/src/sortable/sortable.d.ts +39 -40
- package/dist/src/sortable/sortable.d.ts.map +1 -1
- package/dist/src/sortable/sortable.js +14 -31
- package/dist/src/stack/stack.d.ts +1 -1
- package/dist/src/stack/stack.d.ts.map +1 -1
- package/dist/src/stack/stack.js +1 -1
- package/dist/src/tabs/tabs.d.ts +1 -1
- package/dist/src/tabs/tabs.d.ts.map +1 -1
- package/dist/src/tabs/tabs.js +1 -1
- package/dist/src/textareaField/textareaField.d.ts.map +1 -1
- package/dist/src/textareaField/textareaField.js +3 -7
- package/dist/src/time/time.d.ts.map +1 -1
- package/dist/src/time/time.js +4 -2
- package/dist/src/tips/index.d.ts +1 -1
- package/dist/src/tips/index.d.ts.map +1 -1
- package/dist/src/toast/index.d.ts +1 -1
- package/dist/src/toast/index.d.ts.map +1 -1
- package/dist/src/toast/toast.d.ts +5 -5
- package/dist/src/toast/toast.d.ts.map +1 -1
- package/dist/src/toast/toast.js +1 -6
- package/dist/src/toolbar/toolbar.d.ts +0 -3
- package/dist/src/toolbar/toolbar.d.ts.map +1 -1
- package/dist/src/toolbar/toolbar.js +1 -2
- package/package.json +1 -1
- package/src/adapters/mermaid.ts +0 -24
- package/src/announcer/announcer.ts +13 -17
- package/src/board/board.ts +32 -80
- package/src/board/index.ts +1 -1
- package/src/button/button.ts +1 -1
- package/src/commandPalette/commandPalette.ts +21 -38
- package/src/commandPalette/keys.ts +2 -4
- package/src/commandPalette/match.ts +3 -3
- package/src/confirmDialog/confirmDialog.ts +5 -3
- package/src/copyButton/copyButton.ts +2 -5
- package/src/crumbs/crumbs.ts +2 -4
- package/src/dialog/dialog.ts +3 -2
- package/src/drag/drag.ts +54 -5
- package/src/field/field.ts +29 -7
- package/src/fieldset/fieldset.ts +2 -4
- package/src/fileTree/fileTree.ts +10 -12
- package/src/inlineInput/inlineInput.ts +11 -2
- package/src/keyHelp/index.ts +0 -1
- package/src/keyHelp/keyHelp.ts +3 -9
- package/src/keyHint/keyLabel.ts +5 -2
- package/src/lineDiff/diff.ts +70 -54
- package/src/lineDiff/patch.ts +27 -14
- package/src/linkState/linkState.ts +1 -8
- package/src/menu/slip.ts +2 -2
- package/src/meter/meter.ts +25 -15
- package/src/notice/notice.ts +2 -1
- package/src/passwordField/passwordField.ts +3 -7
- package/src/pixel-paper.css +82 -131
- package/src/rail/rail.ts +8 -6
- package/src/reel/layout.ts +14 -7
- package/src/reel/reel.ts +132 -63
- package/src/scheme/scheme.ts +1 -1
- package/src/segmented/segmented.ts +1 -9
- package/src/selectField/selectField.ts +3 -10
- package/src/sortable/index.ts +0 -1
- package/src/sortable/sortable.ts +35 -54
- package/src/stack/stack.ts +1 -1
- package/src/tabs/tabs.ts +1 -1
- package/src/textareaField/textareaField.ts +3 -7
- package/src/time/time.ts +6 -7
- package/src/tips/index.ts +1 -1
- package/src/toast/index.ts +1 -1
- package/src/toast/toast.ts +2 -8
- package/src/tokens.css +6 -0
- package/src/toolbar/toolbar.ts +0 -5
- package/dist/src/inlineInput/inline.d.ts +0 -6
- package/dist/src/inlineInput/inline.d.ts.map +0 -1
- package/dist/src/inlineInput/inline.js +0 -7
- package/src/inlineInput/inline.ts +0 -13
package/dist/src/toast/toast.js
CHANGED
|
@@ -215,14 +215,9 @@ export const show = (model, config) => {
|
|
|
215
215
|
/** Takes a slip back by its id: what it reported has recovered. */
|
|
216
216
|
export const dismiss = (model, entryId) => __foldkitBrandViewResult((dismissKeepingFocus(model, entryId)), "@gjermundgaraba-designs/pixel-paper/src/toast/toast.js#dismiss");
|
|
217
217
|
// VIEW
|
|
218
|
-
const TONE_CLASS = {
|
|
219
|
-
Ok: "notice toast ok",
|
|
220
|
-
Error: "notice toast error",
|
|
221
|
-
Quiet: "notice toast quiet",
|
|
222
|
-
};
|
|
223
218
|
const slipView = (entry, h) => {
|
|
224
219
|
const { tone, title, maybeBody, maybeAction } = entry.payload;
|
|
225
|
-
return __foldkitBrandViewResult((h.div([h.Class(TONE_CLASS[tone])], [
|
|
220
|
+
return __foldkitBrandViewResult((h.div([h.Class(`notice toast${Notice.TONE_CLASS[tone]}`)], [
|
|
226
221
|
Notice.toneGlyphView({ tone }, h),
|
|
227
222
|
h.div([], [h.strong([], [title]), ...Option.toArray(maybeBody)]),
|
|
228
223
|
...Array.fromOption(Option.map(maybeAction, (action) => __foldkitBrandViewResult((Button.view({
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import type { Html, HtmlBuilder } from "foldkit/html";
|
|
3
3
|
import * as Mount from "foldkit/mount";
|
|
4
|
-
import type { Attributes } from "../attributes/attributes.ts";
|
|
5
4
|
/** Which way the strip lies. A `Vertical` strip stands on end: a canvas's tools down its left
|
|
6
5
|
* edge. The arrows of both axes move in either. */
|
|
7
6
|
export declare const Orientation: Schema.Literals<readonly ["Horizontal", "Vertical"]>;
|
|
@@ -38,8 +37,6 @@ export type ViewConfig<ParentMessage> = Readonly<{
|
|
|
38
37
|
* Scene, resolve the Mount by name with the lifted Message:
|
|
39
38
|
* `Scene.Mount.resolve({ name: Toolbar.BindToolKeys.name }, toParentMessage(Toolbar.Message.MovedToolFocus({ move })))`. */
|
|
40
39
|
toParentMessage: (message: Message) => ParentMessage;
|
|
41
|
-
/** Further attributes for the toolbar, never a class. */
|
|
42
|
-
attributes?: Attributes<ParentMessage>;
|
|
43
40
|
}>;
|
|
44
41
|
/** A strip of tools that is one stop for Tab (`role="toolbar"`, named by its label, with its
|
|
45
42
|
* `aria-orientation`): the arrows move between the tools and stop at the ends, Home and End
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolbar.d.ts","sourceRoot":"","sources":["../../../src/toolbar/toolbar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAEpF,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,KAAK,KAAK,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"toolbar.d.ts","sourceRoot":"","sources":["../../../src/toolbar/toolbar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAEpF,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,KAAK,KAAK,MAAM,eAAe,CAAC;AAMvC;oDACoD;AACpD,eAAO,MAAM,WAAW,sDAA8C,CAAC;AACvE,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAC;AAQlD,6FAA6F;AAC7F,eAAO,MAAM,OAAO;;QAA0C,IAAI;;EAAe,CAAC;AAClF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAsG1C;;;;;;yDAMyD;AACzD,eAAO,MAAM,YAAY;;;EAuBvB,CAAC;AAIH,yDAAyD;AACzD,MAAM,MAAM,UAAU,CAAC,aAAa,IAAI,QAAQ,CAAC;IAC/C,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAC3B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;iIAG6H;IAC7H,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,aAAa,CAAC;CACtD,CAAC,CAAC;AAEH;;gEAEgE;AAChE,eAAO,MAAM,IAAI,GAAI,aAAa,UACxB,UAAU,CAAC,aAAa,CAAC,KAC9B,WAAW,CAAC,aAAa,CAAC,KAC5B,IAmBF,CAAC"}
|
|
@@ -90,9 +90,8 @@ export const BindToolKeys = Mount.defineStream("BindToolKeys", {
|
|
|
90
90
|
* `aria-orientation`): the arrows move between the tools and stop at the ends, Home and End
|
|
91
91
|
* go to the ends, and Tab comes back to the tool used last. */
|
|
92
92
|
export const view = (config, h) => {
|
|
93
|
-
const { label, tools: toolViews, orientation = "Horizontal", isFloating = false, toParentMessage,
|
|
93
|
+
const { label, tools: toolViews, orientation = "Horizontal", isFloating = false, toParentMessage, } = config;
|
|
94
94
|
return __foldkitBrandViewResult((h.div([
|
|
95
|
-
...attributes,
|
|
96
95
|
h.Role("toolbar"),
|
|
97
96
|
h.AriaLabel(label),
|
|
98
97
|
h.AriaOrientation(orientation === "Vertical" ? "vertical" : "horizontal"),
|
package/package.json
CHANGED
package/src/adapters/mermaid.ts
CHANGED
|
@@ -2,30 +2,6 @@
|
|
|
2
2
|
// faces, in the page's palette. An optional entry point ("./adapters/mermaid"):
|
|
3
3
|
// mermaid is the adopter's own dependency, loaded here only when a diagram is drawn.
|
|
4
4
|
//
|
|
5
|
-
// `view` renders the diagram's box and draws into it from a Mount (`DrawDiagram`), so it
|
|
6
|
-
// needs the DOM only once the box is on the page, never while rendering on the server. The
|
|
7
|
-
// box is keyed by the palette the page prints in, from the application's SchemeSwitch
|
|
8
|
-
// (`Scheme.SchemeSwitch.palette`, which under "System" follows the reader's system as it
|
|
9
|
-
// changes), so a new palette mounts a new box and draws the diagram again; a new source does
|
|
10
|
-
// the same. The box reports `SucceededDrawDiagram`, `FailedLoadMermaid` or `FailedDrawDiagram`
|
|
11
|
-
// through the caller's `toParentMessage`, and the caller keeps what it shows meanwhile (a
|
|
12
|
-
// Loading line, an error Notice) in its own Model:
|
|
13
|
-
//
|
|
14
|
-
// Mermaid.view(
|
|
15
|
-
// {
|
|
16
|
-
// source,
|
|
17
|
-
// palette: Scheme.SchemeSwitch.palette(model.schemeSwitch),
|
|
18
|
-
// toParentMessage: (message) => GotDiagramMessage({ message }),
|
|
19
|
-
// },
|
|
20
|
-
// h,
|
|
21
|
-
// );
|
|
22
|
-
//
|
|
23
|
-
// `renderDiagram` waits for the faces, so words are measured in them and not a fallback;
|
|
24
|
-
// renders in the box (it must be on the page), so labels are measured where they will print;
|
|
25
|
-
// and returns the SVG. Mermaid's settings are global, so draws run one at a time. It fails with
|
|
26
|
-
// `MermaidMissing` when mermaid itself does not load, and with `DiagramError` when mermaid
|
|
27
|
-
// loads but the diagram is not drawn.
|
|
28
|
-
//
|
|
29
5
|
// Two layers. themeCSS repaints with var(…), so the parts it names (nodes, edges, labels,
|
|
30
6
|
// clusters, actors, messages, notes) follow a palette switch in place. themeVariables must be
|
|
31
7
|
// colours, not var() strings (mermaid mixes its own shades from them), so they are read from
|
|
@@ -35,7 +35,7 @@ export const view = <Message>(h: HtmlBuilder<Message>): Html =>
|
|
|
35
35
|
h.Class("sr-only"),
|
|
36
36
|
h.Role("status"),
|
|
37
37
|
h.AriaLive("polite"),
|
|
38
|
-
h.
|
|
38
|
+
h.Attribute(ANNOUNCER_ATTRIBUTE, ""),
|
|
39
39
|
]);
|
|
40
40
|
|
|
41
41
|
// ANNOUNCE
|
|
@@ -97,28 +97,21 @@ export const announce = (text: string): Effect.Effect<void> =>
|
|
|
97
97
|
// FLASH
|
|
98
98
|
|
|
99
99
|
const FLASH_ID = "pixel-paper-flash";
|
|
100
|
-
const DEFAULT_BEAT = Duration.millis(700);
|
|
101
100
|
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
/** The beat in milliseconds, from the stylesheet's `--motion-flash`: none on a page that has
|
|
102
|
+
* not loaded the stylesheet, which has nothing to flash with. */
|
|
103
|
+
const beatOf = (element: Element): Option.Option<number> => {
|
|
104
|
+
const time = getComputedStyle(element).getPropertyValue("--motion-flash").trim();
|
|
105
|
+
return pipe(
|
|
104
106
|
Option.liftPredicate(Number.parseFloat(time), Number.isFinite),
|
|
105
107
|
Option.map((value) => (time.endsWith("ms") ? value : value * 1000)),
|
|
106
108
|
);
|
|
107
|
-
|
|
108
|
-
const beatOf = (element: Element): number =>
|
|
109
|
-
Option.getOrElse(
|
|
110
|
-
toMillis(getComputedStyle(element).getPropertyValue("--motion-flash").trim()),
|
|
111
|
-
() => Duration.toMillis(DEFAULT_BEAT),
|
|
112
|
-
);
|
|
109
|
+
};
|
|
113
110
|
|
|
114
111
|
const prefersReducedMotion = (): boolean =>
|
|
115
112
|
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
116
113
|
|
|
117
|
-
const runFlash = (element: Element): void => {
|
|
118
|
-
if (!("animate" in element)) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
const beat = beatOf(element);
|
|
114
|
+
const runFlash = (element: Element, beat: number): void => {
|
|
122
115
|
Array.forEach(
|
|
123
116
|
Array.filter(element.getAnimations(), (animation) => animation.id === FLASH_ID),
|
|
124
117
|
(animation) => animation.cancel(),
|
|
@@ -141,8 +134,11 @@ const runFlash = (element: Element): void => {
|
|
|
141
134
|
export const flash = (selector: string): Effect.Effect<void> =>
|
|
142
135
|
Effect.sync(() => {
|
|
143
136
|
const maybeElement = Option.fromNullishOr(document.querySelector(selector));
|
|
144
|
-
|
|
145
|
-
|
|
137
|
+
const maybeFlash = Option.flatMap(maybeElement, (element) =>
|
|
138
|
+
Option.map(beatOf(element), (beat) => ({ element, beat })),
|
|
139
|
+
);
|
|
140
|
+
if (Option.isSome(maybeFlash)) {
|
|
141
|
+
runFlash(maybeFlash.value.element, maybeFlash.value.beat);
|
|
146
142
|
}
|
|
147
143
|
});
|
|
148
144
|
|
package/src/board/board.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Button as UiButton, DragAndDrop as UiDragAndDrop } from "@foldkit/ui";
|
|
2
|
-
import { Array, Effect, Equal, Match, Option, Schema,
|
|
2
|
+
import { Array, Effect, Equal, Match, Option, Schema, pipe } from "effect";
|
|
3
3
|
import * as Command from "foldkit/command";
|
|
4
4
|
import {
|
|
5
5
|
type Attribute,
|
|
@@ -26,13 +26,15 @@ import { prefixKeys } from "../subscription/prefixKeys.ts";
|
|
|
26
26
|
import {
|
|
27
27
|
LEFT_BUTTON,
|
|
28
28
|
activatedKeyboardDrag,
|
|
29
|
+
canLift,
|
|
29
30
|
cancelledDrag,
|
|
30
|
-
|
|
31
|
+
escapeSubscriptions,
|
|
31
32
|
innerControls,
|
|
32
33
|
isCarrying,
|
|
33
34
|
measureLift,
|
|
34
35
|
phaseOf,
|
|
35
36
|
pressedDraggable,
|
|
37
|
+
settle as settleDrag,
|
|
36
38
|
} from "../drag/drag.ts";
|
|
37
39
|
|
|
38
40
|
// MODEL
|
|
@@ -50,19 +52,20 @@ export const Column = Schema.Struct({
|
|
|
50
52
|
});
|
|
51
53
|
export type Column = typeof Column.Type;
|
|
52
54
|
|
|
55
|
+
/** How the keyboard carries what it holds: said on the grip and once it is picked up. */
|
|
56
|
+
const KEYBOARD_MOVES = "Arrow keys move it to another column, space drops it, escape cancels.";
|
|
57
|
+
|
|
53
58
|
/** The words a board says. `{card}` stands for the card's label and `{column}` for a
|
|
54
59
|
* column's title. */
|
|
55
60
|
const COPY = {
|
|
56
61
|
/** The grip's name. */
|
|
57
62
|
gripLabel: "Move {card}",
|
|
58
63
|
/** What the grip describes: how to move a card from the keyboard. */
|
|
59
|
-
gripHelp:
|
|
60
|
-
"Press space to pick the card up. Arrow keys move it to another column, space drops it, escape cancels.",
|
|
64
|
+
gripHelp: `Press space to pick the card up. ${KEYBOARD_MOVES}`,
|
|
61
65
|
/** A pointer has lifted the card. */
|
|
62
66
|
pickedUp: "Picked up {card}.",
|
|
63
67
|
/** The keyboard has lifted the card: say how to go on. */
|
|
64
|
-
pickedUpByKeyboard:
|
|
65
|
-
"Picked up {card}. Arrow keys move it to another column, space drops it, escape cancels.",
|
|
68
|
+
pickedUpByKeyboard: `Picked up {card}. ${KEYBOARD_MOVES}`,
|
|
66
69
|
/** The held card is over another column. */
|
|
67
70
|
over: "{card} is over {column}.",
|
|
68
71
|
/** The card was dropped in a column. */
|
|
@@ -177,12 +180,6 @@ export const id = (model: Model): string => model.dragAndDrop.id;
|
|
|
177
180
|
/** Whether a card is held by a pointer or the keyboard. */
|
|
178
181
|
export const isDragging = (model: Model): boolean => UiDragAndDrop.isDragging(model.dragAndDrop);
|
|
179
182
|
|
|
180
|
-
/** Whether a card is pressed or held. While it is, the arrow keys, Space and Escape belong to
|
|
181
|
-
* the board: pause the page's own bindings for them. The board takes Escape before anything
|
|
182
|
-
* else sees it. */
|
|
183
|
-
export const isHolding = (model: Model): boolean =>
|
|
184
|
-
Option.isSome(UiDragAndDrop.maybeDraggedItemId(model.dragAndDrop));
|
|
185
|
-
|
|
186
183
|
// DOM HOOKS
|
|
187
184
|
|
|
188
185
|
const hookId = (boardId: string, localId: string): string => `${boardId}/${localId}`;
|
|
@@ -234,9 +231,6 @@ const overColumn = (model: Model): Option.Option<Column> =>
|
|
|
234
231
|
),
|
|
235
232
|
);
|
|
236
233
|
|
|
237
|
-
const announceMove = (maybeText: Option.Option<string>): ReadonlyArray<Command.Command<Message>> =>
|
|
238
|
-
Array.fromOption(Option.map(maybeText, (text) => AnnounceMove({ text })));
|
|
239
|
-
|
|
240
234
|
const placementOf = (
|
|
241
235
|
model: Model,
|
|
242
236
|
held: Held,
|
|
@@ -263,10 +257,7 @@ const placementOf = (
|
|
|
263
257
|
fromIndex: from.index,
|
|
264
258
|
toColumnId: to.id,
|
|
265
259
|
toIndex,
|
|
266
|
-
maybeAfterCardId: Option.map(
|
|
267
|
-
toIndex > 0 ? Array.get(others, toIndex - 1) : Option.none(),
|
|
268
|
-
(card) => card.id,
|
|
269
|
-
),
|
|
260
|
+
maybeAfterCardId: Option.map(Array.get(others, toIndex - 1), (card) => card.id),
|
|
270
261
|
maybeBeforeCardId: Option.map(Array.get(others, toIndex), (card) => card.id),
|
|
271
262
|
});
|
|
272
263
|
});
|
|
@@ -349,35 +340,26 @@ const moveText = (
|
|
|
349
340
|
* A card set down without a move says `putBack`. */
|
|
350
341
|
const settle =
|
|
351
342
|
(previous: Model, putBack = COPY.putBack) =>
|
|
352
|
-
(dragAndDropUpdate: UpdateReturn): UpdateReturn =>
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
343
|
+
(dragAndDropUpdate: UpdateReturn): UpdateReturn =>
|
|
344
|
+
settleDrag(
|
|
345
|
+
dragAndDropUpdate,
|
|
346
|
+
Option.map(
|
|
347
|
+
Option.flatMap(previous.maybeHeld, (held) =>
|
|
348
|
+
moveText(
|
|
349
|
+
held,
|
|
350
|
+
previous,
|
|
351
|
+
dragAndDropUpdate.model,
|
|
352
|
+
Option.fromNullishOr(dragAndDropUpdate.outMessage),
|
|
353
|
+
putBack,
|
|
354
|
+
),
|
|
355
|
+
),
|
|
356
|
+
(text) => AnnounceMove({ text }),
|
|
364
357
|
),
|
|
365
358
|
);
|
|
366
359
|
|
|
367
|
-
return Update.withOutMessage(
|
|
368
|
-
{
|
|
369
|
-
model: nextModel,
|
|
370
|
-
commands: [...(dragAndDropUpdate.commands ?? []), ...announceMove(maybeText)],
|
|
371
|
-
},
|
|
372
|
-
dragAndDropUpdate.outMessage,
|
|
373
|
-
);
|
|
374
|
-
};
|
|
375
|
-
|
|
376
360
|
const holding = (model: Model, held: Held): Model =>
|
|
377
361
|
modifyFields(model, { maybeHeld: () => Option.some(held) });
|
|
378
362
|
|
|
379
|
-
const canLift = (model: Model): boolean => model.isMovable && phaseOf(model.dragAndDrop) === "Idle";
|
|
380
|
-
|
|
381
363
|
const liftByKeyboard = (model: Model, place: Place, columns: ReadonlyArray<Column>) => {
|
|
382
364
|
const pressed = holding(model, { card: place.card, columns, maybePressPoint: Option.none() });
|
|
383
365
|
return settle(pressed)(
|
|
@@ -545,17 +527,6 @@ export const setMovable = (model: Model, isMovable: boolean): UpdateReturn => {
|
|
|
545
527
|
|
|
546
528
|
// SUBSCRIPTION
|
|
547
529
|
|
|
548
|
-
const holdingSubscriptions = Subscription.make<Model, Message>()((entry) => ({
|
|
549
|
-
escape: entry(
|
|
550
|
-
{ isHolding: Schema.Boolean },
|
|
551
|
-
{
|
|
552
|
-
modelToDependencies: (model) => ({ isHolding: isHolding(model) }),
|
|
553
|
-
dependenciesToStream: ({ isHolding: isHoldingNow }) =>
|
|
554
|
-
isHoldingNow ? escapeKeys(Message.PressedEscape()) : Stream.empty,
|
|
555
|
-
},
|
|
556
|
-
),
|
|
557
|
-
}));
|
|
558
|
-
|
|
559
530
|
// NOTE: @foldkit/ui gap: DragAndDrop's Subscription keys are fixed, so two of its users on a
|
|
560
531
|
// page would share them. The board lifts them under its own names, behind the parent's prefix.
|
|
561
532
|
const dragSubscriptions = Subscription.lift({
|
|
@@ -567,7 +538,10 @@ const dragSubscriptions = Subscription.lift({
|
|
|
567
538
|
toParentMessage: toDragAndDropMessage,
|
|
568
539
|
});
|
|
569
540
|
|
|
570
|
-
const boardSubscriptions = Subscription.aggregate(
|
|
541
|
+
const boardSubscriptions = Subscription.aggregate(
|
|
542
|
+
escapeSubscriptions<Model, Message>(Message.PressedEscape()),
|
|
543
|
+
dragSubscriptions,
|
|
544
|
+
);
|
|
571
545
|
|
|
572
546
|
/** A held card follows the pointer, the arrows and Space move and drop it, Escape puts it
|
|
573
547
|
* back and the page scrolls near its edges, all from listeners that run only while a card is
|
|
@@ -581,9 +555,6 @@ export const subscriptions = <Prefix extends string>(keyPrefix: Prefix) =>
|
|
|
581
555
|
|
|
582
556
|
// LIFTED CARD
|
|
583
557
|
|
|
584
|
-
const pressPointOf = (model: Model): Option.Option<typeof PressPoint.Type> =>
|
|
585
|
-
Option.flatMap(model.maybeHeld, (held) => held.maybePressPoint);
|
|
586
|
-
|
|
587
558
|
/** Sizes the copy that follows the pointer to the card it lifts, and keeps the pointer where
|
|
588
559
|
* it pressed the card. Measures the card once, when the copy appears. */
|
|
589
560
|
export const SizeLiftedCard = Mount.define("SizeLiftedCard", {
|
|
@@ -627,9 +598,6 @@ export type ViewInputs = Readonly<{
|
|
|
627
598
|
toEmptyView?: (column: Column) => ReadonlyArray<Html>;
|
|
628
599
|
/** Controls at the right of a column's head, after its count ("capture here"). */
|
|
629
600
|
toColumnActions?: (column: Column) => ReadonlyArray<Html>;
|
|
630
|
-
/** Further attributes for a column's `div.board-col` (`data-status`), never a class. Wrap
|
|
631
|
-
* them with `childAttributes` so their handlers dispatch the parent's Messages. */
|
|
632
|
-
toColumnAttributes?: (column: Column) => ReadonlyArray<ChildAttribute>;
|
|
633
601
|
}>;
|
|
634
602
|
|
|
635
603
|
const INNER_CONTROL = innerControls(`[data-${TITLE_ATTRIBUTE}], [data-${GRIP_ATTRIBUTE}]`);
|
|
@@ -664,7 +632,6 @@ type PlaceInfo = Readonly<{
|
|
|
664
632
|
toCardView: (card: Card, render: CardRender) => Html;
|
|
665
633
|
toEmptyView: (column: Column) => ReadonlyArray<Html>;
|
|
666
634
|
toColumnActions: (column: Column) => ReadonlyArray<Html>;
|
|
667
|
-
toColumnAttributes: (column: Column) => ReadonlyArray<ChildAttribute>;
|
|
668
635
|
}>;
|
|
669
636
|
|
|
670
637
|
const gripHelpId = (model: Model): string => `${id(model)}-grip-help`;
|
|
@@ -872,7 +839,6 @@ const columnView = (info: PlaceInfo, column: Column, h: HtmlBuilder<Message>): H
|
|
|
872
839
|
return h.keyed("div")(
|
|
873
840
|
column.id,
|
|
874
841
|
[
|
|
875
|
-
...info.toColumnAttributes(column),
|
|
876
842
|
h.DataAttribute("droppable-id", columnHook(model, column.id)),
|
|
877
843
|
h.Class(isTarget ? "board-col drop-target" : "board-col"),
|
|
878
844
|
],
|
|
@@ -905,12 +871,11 @@ const liftedCopyView = (info: PlaceInfo, h: HtmlBuilder<Message>): ReadonlyArray
|
|
|
905
871
|
const maybeLifted = Option.all({
|
|
906
872
|
style: UiDragAndDrop.ghostStyle(model.dragAndDrop),
|
|
907
873
|
held: model.maybeHeld,
|
|
874
|
+
pressPoint: Option.flatMap(model.maybeHeld, (held) => held.maybePressPoint),
|
|
908
875
|
});
|
|
909
876
|
|
|
910
|
-
const pressPoint = Option.getOrElse(pressPointOf(model), () => ({ clientX: 0, clientY: 0 }));
|
|
911
|
-
|
|
912
877
|
return Array.fromOption(
|
|
913
|
-
Option.map(maybeLifted, ({ style, held }) =>
|
|
878
|
+
Option.map(maybeLifted, ({ style, held, pressPoint }) =>
|
|
914
879
|
h.div(
|
|
915
880
|
[
|
|
916
881
|
h.Class("board-lift"),
|
|
@@ -946,21 +911,8 @@ const liftedCopyView = (info: PlaceInfo, h: HtmlBuilder<Message>): ReadonlyArray
|
|
|
946
911
|
* follows the pointer or the keys.
|
|
947
912
|
*/
|
|
948
913
|
export const view = defineView<Model, Message, ViewInputs>((model, viewInputs, h): Html => {
|
|
949
|
-
const {
|
|
950
|
-
|
|
951
|
-
toCardView,
|
|
952
|
-
toEmptyView = () => [],
|
|
953
|
-
toColumnActions = () => [],
|
|
954
|
-
toColumnAttributes = () => [],
|
|
955
|
-
} = viewInputs;
|
|
956
|
-
const info: PlaceInfo = {
|
|
957
|
-
model,
|
|
958
|
-
columns,
|
|
959
|
-
toCardView,
|
|
960
|
-
toEmptyView,
|
|
961
|
-
toColumnActions,
|
|
962
|
-
toColumnAttributes,
|
|
963
|
-
};
|
|
914
|
+
const { columns, toCardView, toEmptyView = () => [], toColumnActions = () => [] } = viewInputs;
|
|
915
|
+
const info: PlaceInfo = { model, columns, toCardView, toEmptyView, toColumnActions };
|
|
964
916
|
|
|
965
917
|
return h.div(
|
|
966
918
|
[h.Class("board"), h.Style({ "--cols": `${columns.length}` })],
|
package/src/board/index.ts
CHANGED
|
@@ -10,10 +10,10 @@ export {
|
|
|
10
10
|
cardView,
|
|
11
11
|
id,
|
|
12
12
|
init,
|
|
13
|
-
isHolding,
|
|
14
13
|
setMovable,
|
|
15
14
|
subscriptions,
|
|
16
15
|
update,
|
|
17
16
|
view,
|
|
18
17
|
} from "./board.ts";
|
|
18
|
+
export { isHolding } from "../drag/drag.ts";
|
|
19
19
|
export type { CardRender, CardViewConfig, InitConfig, ViewInputs } from "./board.ts";
|
package/src/button/button.ts
CHANGED
|
@@ -76,7 +76,7 @@ export type ViewConfig<Message> = Look &
|
|
|
76
76
|
isDisabled?: boolean;
|
|
77
77
|
keyHint?: KeyHintConfig;
|
|
78
78
|
/** `submit` makes it its form's submit button while it is not held. */
|
|
79
|
-
type?: "button" | "submit"
|
|
79
|
+
type?: "button" | "submit";
|
|
80
80
|
/** Further attributes for the `<button>`, such as an id or a Submodel's attribute group. */
|
|
81
81
|
attributes?: Attributes<Message>;
|
|
82
82
|
}> &
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Input as UiInput } from "@foldkit/ui";
|
|
2
|
-
import { Array, Effect, HashSet, Match, Option, Schema, Stream, String, pipe } from "effect";
|
|
2
|
+
import { Array, Effect, Equal, HashSet, Match, Option, Schema, Stream, String, pipe } from "effect";
|
|
3
3
|
import * as FoldkitCommand from "foldkit/command";
|
|
4
4
|
import * as Dom from "foldkit/dom";
|
|
5
5
|
import type { Attribute, Html, HtmlBuilder, KeyboardModifiers } from "foldkit/html";
|
|
@@ -230,19 +230,10 @@ const fresh = (model: Model, maybePage: Option.Option<OpenPage>): Model =>
|
|
|
230
230
|
}),
|
|
231
231
|
);
|
|
232
232
|
|
|
233
|
-
const isSamePage = (
|
|
234
|
-
maybeFirst: Option.Option<string>,
|
|
235
|
-
maybeSecond: Option.Option<string>,
|
|
236
|
-
): boolean =>
|
|
237
|
-
Option.match(maybeFirst, {
|
|
238
|
-
onNone: () => Option.isNone(maybeSecond),
|
|
239
|
-
onSome: (commandId) => Option.contains(maybeSecond, commandId),
|
|
240
|
-
});
|
|
241
|
-
|
|
242
233
|
/** Makes the Model follow what the view showed: a page whose command is gone is dropped (with
|
|
243
234
|
* a pick out on it), so the commands' query and cursor take the reader's input again. */
|
|
244
235
|
const followShown = (model: Model, maybeShownPage: Option.Option<string>): Model =>
|
|
245
|
-
|
|
236
|
+
Equal.equals(maybePageId(model), maybeShownPage)
|
|
246
237
|
? model
|
|
247
238
|
: clearPendingKeys(
|
|
248
239
|
modifyFields(model, {
|
|
@@ -265,13 +256,12 @@ const leavePage = (model: Model): UpdateReturn =>
|
|
|
265
256
|
commands: [focusField(model), scrollPaletteToCursor(model)],
|
|
266
257
|
};
|
|
267
258
|
|
|
268
|
-
const openPage = (model: Model, commandId: string): UpdateReturn =>
|
|
269
|
-
|
|
270
|
-
model:
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}));
|
|
259
|
+
const openPage = (model: Model, commandId: string): UpdateReturn => ({
|
|
260
|
+
model: clearPendingKeys(
|
|
261
|
+
modifyFields(model, { maybePage: () => Option.some(freshPage(commandId)) }),
|
|
262
|
+
),
|
|
263
|
+
commands: [focusField(model), scrollPaletteToCursor(model)],
|
|
264
|
+
});
|
|
275
265
|
|
|
276
266
|
const selectCommand = (model: Model, commandId: string): UpdateReturn =>
|
|
277
267
|
pipe(
|
|
@@ -292,18 +282,16 @@ const moveCursor = (model: Model, cursorId: string): Model =>
|
|
|
292
282
|
);
|
|
293
283
|
|
|
294
284
|
// A pick drops the keys waiting for the next render: the row picked is the one acted on.
|
|
295
|
-
const pick = (model: Model, commandId: string, choiceId: string): UpdateReturn =>
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
),
|
|
302
|
-
outMessage: OutMessage.SelectedChoice({ commandId, choiceId }),
|
|
303
|
-
};
|
|
285
|
+
const pick = (model: Model, commandId: string, choiceId: string): UpdateReturn => ({
|
|
286
|
+
model: clearPendingKeys(
|
|
287
|
+
modifyFields(moveCursor(model, choiceId), { maybePicking: () => Option.some(choiceId) }),
|
|
288
|
+
),
|
|
289
|
+
outMessage: OutMessage.SelectedChoice({ commandId, choiceId }),
|
|
290
|
+
});
|
|
304
291
|
|
|
305
292
|
// What a click on a row or Enter on the cursor's does: nothing when it is held, else pick it
|
|
306
|
-
// on a page, open its page, or select it.
|
|
293
|
+
// on a page, open its page, or select it. Never while a pick is out: the click and the keys
|
|
294
|
+
// come through `unlessPicking`, and a pick clears the waiting Enter `settlePending` runs.
|
|
307
295
|
const runRow = (model: Model, row: ShownRow): UpdateReturn => {
|
|
308
296
|
if (row.isHeld) {
|
|
309
297
|
return { model };
|
|
@@ -322,12 +310,7 @@ const maybeCurrentCursor = (model: Model): Option.Option<string> =>
|
|
|
322
310
|
|
|
323
311
|
const ARROW_STEP: Readonly<Record<ArrowKey, number>> = { ArrowUp: -1, ArrowDown: 1 };
|
|
324
312
|
|
|
325
|
-
type ShownList =
|
|
326
|
-
rows: ReadonlyArray<ShownRow>;
|
|
327
|
-
maybeShownCursor: Option.Option<string>;
|
|
328
|
-
shownQuery: string;
|
|
329
|
-
shownShift: number;
|
|
330
|
-
}>;
|
|
313
|
+
type ShownList = Omit<typeof Message.PressedEnter.Type, "_tag" | "maybeShownPage">;
|
|
331
314
|
|
|
332
315
|
const isListed = (rows: ReadonlyArray<Readonly<{ id: string }>>, rowId: string): boolean =>
|
|
333
316
|
Array.some(rows, (row) => row.id === rowId);
|
|
@@ -435,7 +418,7 @@ type PendingReport = typeof Message.CompletedReportPendingKeys.Type;
|
|
|
435
418
|
const settlePending = (model: Model, report: PendingReport): UpdateReturn => {
|
|
436
419
|
const { maybeShownPage, shownQuery, shownShift, maybeRow } = report;
|
|
437
420
|
const isCurrent =
|
|
438
|
-
|
|
421
|
+
Equal.equals(maybePageId(model), maybeShownPage) &&
|
|
439
422
|
shownShift === model.cursorShift &&
|
|
440
423
|
shownQuery === query(model);
|
|
441
424
|
if (!isPending(model) || !isCurrent) {
|
|
@@ -566,7 +549,7 @@ export type ViewInputs = Readonly<{
|
|
|
566
549
|
toLiveRegionView?: (() => Html) | undefined;
|
|
567
550
|
}>;
|
|
568
551
|
|
|
569
|
-
type ShownPageState = Readonly<{
|
|
552
|
+
type ShownPageState = Readonly<{ page: Page; state: OpenPage }>;
|
|
570
553
|
|
|
571
554
|
type Shown = Readonly<{
|
|
572
555
|
maybePage: Option.Option<ShownPageState>;
|
|
@@ -580,7 +563,7 @@ const pageOf = (commands: ReadonlyArray<Command>, state: OpenPage): Option.Optio
|
|
|
580
563
|
pipe(
|
|
581
564
|
Array.findFirst(commands, (command) => command.id === state.commandId),
|
|
582
565
|
Option.flatMap((command) => Option.fromNullishOr(command.page)),
|
|
583
|
-
Option.map((page) => ({
|
|
566
|
+
Option.map((page) => ({ page, state })),
|
|
584
567
|
);
|
|
585
568
|
|
|
586
569
|
const firstListed = (
|
|
@@ -686,7 +669,7 @@ const shownQueryOf = (model: Model, shown: Shown): string =>
|
|
|
686
669
|
|
|
687
670
|
// The command whose page the view shows.
|
|
688
671
|
const shownPageIdOf = (shown: Shown): Option.Option<string> =>
|
|
689
|
-
Option.map(shown.maybePage, ({
|
|
672
|
+
Option.map(shown.maybePage, ({ state }) => state.commandId);
|
|
690
673
|
|
|
691
674
|
// NOTE: the field's keys that act on the rows are taken here, where the rows are known, as a
|
|
692
675
|
// Mount on the document cannot know them: ↑, ↓ and Enter carry what this render shows (see
|
|
@@ -24,14 +24,13 @@ const isEmptyFieldOnPage = (root: Element, field: Element): boolean =>
|
|
|
24
24
|
|
|
25
25
|
/** What a Backspace in the palette's field does: in an empty field on a page it goes back.
|
|
26
26
|
* It reads the field as it is at the press, which a render cannot know. A key from elsewhere,
|
|
27
|
-
* a key an IME is composing, a modified key
|
|
28
|
-
* while a pick is out
|
|
27
|
+
* a key an IME is composing, a modified key and a key someone already handled are left
|
|
28
|
+
* alone; while a pick is out the update refuses it. */
|
|
29
29
|
export const fieldKey = (
|
|
30
30
|
root: Element,
|
|
31
31
|
event: KeyboardEvent,
|
|
32
32
|
): Option.Option<typeof Message.PressedBackspace.Type> => {
|
|
33
33
|
const field = root.querySelector(`[data-${FIELD_ATTRIBUTE}]`);
|
|
34
|
-
const isWaiting = root.querySelector('[aria-busy="true"]') !== null;
|
|
35
34
|
if (
|
|
36
35
|
field === null ||
|
|
37
36
|
event.target !== field ||
|
|
@@ -39,7 +38,6 @@ export const fieldKey = (
|
|
|
39
38
|
event.isComposing ||
|
|
40
39
|
event.defaultPrevented ||
|
|
41
40
|
!isPlain(event) ||
|
|
42
|
-
isWaiting ||
|
|
43
41
|
!isEmptyFieldOnPage(root, field)
|
|
44
42
|
) {
|
|
45
43
|
return Option.none();
|
|
@@ -56,13 +56,15 @@ export const matches = (query: string, label: string): boolean => {
|
|
|
56
56
|
return isSubsequence(needle, String.toLowerCase(label));
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
+
const isBlank = (query: string): boolean => String.isEmpty(String.trim(query));
|
|
60
|
+
|
|
59
61
|
/** The commands the query finds, under their groups in the order the groups first appear,
|
|
60
62
|
* authored titles after the commands. With no query, authored titles wait. */
|
|
61
63
|
export const findCommands = (
|
|
62
64
|
commands: ReadonlyArray<Command>,
|
|
63
65
|
query: string,
|
|
64
66
|
): ReadonlyArray<Command> => {
|
|
65
|
-
const hits =
|
|
67
|
+
const hits = isBlank(query)
|
|
66
68
|
? Array.filter(commands, (command) => command.isAuthored !== true)
|
|
67
69
|
: Array.filter(commands, (command) => matches(query, command.label));
|
|
68
70
|
const ordered = [
|
|
@@ -78,8 +80,6 @@ export const findCommands = (
|
|
|
78
80
|
return Array.sort(ordered, Order.mapInput(Order.Number, groupIndex));
|
|
79
81
|
};
|
|
80
82
|
|
|
81
|
-
const isBlank = (query: string): boolean => String.isEmpty(String.trim(query));
|
|
82
|
-
|
|
83
83
|
/** The choices a page's query finds. */
|
|
84
84
|
export const findChoices = (page: Page, query: string): ReadonlyArray<Choice> =>
|
|
85
85
|
isBlank(query)
|
|
@@ -65,11 +65,13 @@ const dialogFold = foldDialog(Dialog, {
|
|
|
65
65
|
toParentOutMessage: toCancelled,
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
const FREE: Dialog.UpdateContext = { isCloseHeld: false };
|
|
69
|
-
|
|
70
68
|
/** Processes a confirm dialog Message and returns the next Model, Commands and OutMessage.
|
|
71
69
|
* While `context.isCloseHeld`, cancel, Escape and the veil change nothing. */
|
|
72
|
-
export const update = (
|
|
70
|
+
export const update = (
|
|
71
|
+
model: Model,
|
|
72
|
+
message: Message,
|
|
73
|
+
context: Dialog.UpdateContext = Dialog.FREE,
|
|
74
|
+
) =>
|
|
73
75
|
Message.match<Update.ReturnWithOutMessage<Model, Message, OutMessage>>(message, {
|
|
74
76
|
GotDialogMessage: ({ message: dialogMessage }) =>
|
|
75
77
|
dialogFold.updateWith(context)(model, dialogMessage),
|
|
@@ -10,7 +10,6 @@ import type * as Update from "foldkit/update";
|
|
|
10
10
|
|
|
11
11
|
import * as Announcer from "../announcer/announcer.ts";
|
|
12
12
|
import * as Button from "../button/button.ts";
|
|
13
|
-
import type { Attributes } from "../attributes/attributes.ts";
|
|
14
13
|
import * as IconButton from "../iconButton/iconButton.ts";
|
|
15
14
|
|
|
16
15
|
// MODEL
|
|
@@ -139,8 +138,6 @@ export type ViewInputs = Readonly<{
|
|
|
139
138
|
* word (`"copy"`). */
|
|
140
139
|
label: string;
|
|
141
140
|
variant?: Variant;
|
|
142
|
-
/** Further attributes for the `<button>`, never a class. */
|
|
143
|
-
attributes?: Attributes<Message>;
|
|
144
141
|
}>;
|
|
145
142
|
|
|
146
143
|
/**
|
|
@@ -151,9 +148,9 @@ export type ViewInputs = Readonly<{
|
|
|
151
148
|
* resources.
|
|
152
149
|
*/
|
|
153
150
|
export const view = defineView<Model, Message, ViewInputs>((model, viewInputs, h): Html => {
|
|
154
|
-
const { text, label, variant = "Icon"
|
|
151
|
+
const { text, label, variant = "Icon" } = viewInputs;
|
|
155
152
|
const isDone = isCopied(model);
|
|
156
|
-
const buttonAttributes = [
|
|
153
|
+
const buttonAttributes = [h.Id(id(model))];
|
|
157
154
|
const onClick = Message.ClickedCopy({ text });
|
|
158
155
|
|
|
159
156
|
return variant === "Icon"
|
package/src/crumbs/crumbs.ts
CHANGED
|
@@ -14,8 +14,6 @@ export type Crumb = Readonly<{
|
|
|
14
14
|
/** Configuration for rendering crumbs with `view`. */
|
|
15
15
|
export type ViewConfig = Readonly<{
|
|
16
16
|
crumbs: ReadonlyArray<Crumb>;
|
|
17
|
-
/** Names the navigation for screen readers. */
|
|
18
|
-
label?: string;
|
|
19
17
|
}>;
|
|
20
18
|
|
|
21
19
|
const crumbView =
|
|
@@ -34,10 +32,10 @@ const crumbView =
|
|
|
34
32
|
/** The path to where you are, in the pixel face, split by muted slashes: a navigation
|
|
35
33
|
* landmark named "Breadcrumb", its steps an ordered list, the last `aria-current="page"`. */
|
|
36
34
|
export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html => {
|
|
37
|
-
const { crumbs
|
|
35
|
+
const { crumbs } = config;
|
|
38
36
|
|
|
39
37
|
return h.nav(
|
|
40
|
-
[h.AriaLabel(
|
|
38
|
+
[h.AriaLabel("Breadcrumb"), h.Class("crumbs")],
|
|
41
39
|
[h.ol([], Array.map(crumbs, crumbView(crumbs.length - 1, h)))],
|
|
42
40
|
);
|
|
43
41
|
};
|