@gjermundgaraba-designs/pixel-paper 0.14.0 → 0.15.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 +5 -4
- package/dist/src/card/card.d.ts +13 -7
- package/dist/src/card/card.d.ts.map +1 -1
- package/dist/src/card/card.js +4 -4
- package/dist/src/commandPalette/commandPalette.d.ts +0 -4
- package/dist/src/commandPalette/commandPalette.d.ts.map +1 -1
- package/dist/src/confirmDialog/confirmDialog.d.ts +16 -22
- package/dist/src/confirmDialog/confirmDialog.d.ts.map +1 -1
- package/dist/src/confirmDialog/confirmDialog.js +15 -20
- package/dist/src/confirmDialog/index.d.ts +1 -1
- package/dist/src/confirmDialog/index.d.ts.map +1 -1
- package/dist/src/confirmDialog/index.js +1 -1
- package/dist/src/dialog/dialog.d.ts +12 -16
- package/dist/src/dialog/dialog.d.ts.map +1 -1
- package/dist/src/dialog/dialog.js +7 -18
- package/dist/src/dialog/fold.d.ts +9 -1
- package/dist/src/dialog/fold.d.ts.map +1 -1
- package/dist/src/dialog/fold.js +9 -5
- package/dist/src/dialog/index.d.ts +2 -2
- package/dist/src/dialog/index.d.ts.map +1 -1
- package/dist/src/dialog/index.js +1 -1
- package/dist/src/keyHelp/keyHelp.d.ts +0 -4
- package/dist/src/keyHelp/keyHelp.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/card/card.ts +15 -8
- package/src/confirmDialog/confirmDialog.ts +19 -25
- package/src/confirmDialog/index.ts +0 -2
- package/src/dialog/dialog.ts +19 -23
- package/src/dialog/fold.ts +25 -6
- package/src/dialog/index.ts +1 -3
package/DESIGN.md
CHANGED
|
@@ -1055,11 +1055,11 @@ The printed button: a paper rectangle in a 2px ink frame that casts a 3px shadow
|
|
|
1055
1055
|
|
|
1056
1056
|
### Cards (dialogs)
|
|
1057
1057
|
|
|
1058
|
-
**Dialog** is a Submodel over `@foldkit/ui`'s Dialog: a `<dialog class="veil">` that fills the viewport, opened with `show()` in place rather than `showModal()` in the top layer. That is a deliberate exception to the baseline's "prefer the platform" for overlays: Foldkit's Dialog (through `Dom.showDialog`) makes everything outside the dialog `inert` and `aria-hidden`, traps focus, handles Escape on the topmost dialog and returns focus on close, so the platform's modal behaviour is reproduced, not dropped; what is lost is the top layer and its `::backdrop`. The veil is therefore an element of its own, `div.veil-backdrop` (fixed, Veil with the ink checker grain, entering in 3 steps over 0.12s), under `div.veil-pad` (`.top` for the palette), which lets a press through everywhere but on the card it holds; a press on the veil, Escape, or a control given the `closeButton` group closes it and reports `Closed`. While its work must land in it (a save whose failure the reader has to see), the
|
|
1058
|
+
**Dialog** is a Submodel over `@foldkit/ui`'s Dialog: a `<dialog class="veil">` that fills the viewport, opened with `show()` in place rather than `showModal()` in the top layer. That is a deliberate exception to the baseline's "prefer the platform" for overlays: Foldkit's Dialog (through `Dom.showDialog`) makes everything outside the dialog `inert` and `aria-hidden`, traps focus, handles Escape on the topmost dialog and returns focus on close, so the platform's modal behaviour is reproduced, not dropped; what is lost is the top layer and its `::backdrop`. The veil is therefore an element of its own, `div.veil-backdrop` (fixed, Veil with the ink checker grain, entering in 3 steps over 0.12s), under `div.veil-pad` (`.top` for the palette), which lets a press through everywhere but on the card it holds; a press on the veil, Escape, or a control given the `closeButton` group closes it and reports `Closed`. While its work must land in it (a save whose failure the reader has to see), the parent holds those three: its update passes `isCloseHeld` to the dialog's (`Dialog.update(model, message, { isCloseHeld })`, or `foldDialog`'s `isCloseHeld: (model) => model.isSaving`), read from its own state as each Message arrives, so the hold starts with the work, ends with it, and covers a key pressed before the next render: the dialog stays open and reports no `Closed`. Nothing is stored, so there is nothing to let go of. Opening and closing are the parent's, through `Dialog.open` and `Dialog.close` in its update, and a step the parent asked for reports nothing; `Dialog.foldDialog` lifts a dialog the parent always holds (its update, `open` and `close`) into the parent at once, `Closed` reported only where the parent maps it. The content is the parent's `toView`, which receives the attribute groups to spread (`title`, `description`, `initialFocus`, `closeButton`, `actionBar`). Focus starts on the element given the `initialFocus` group, else the first control. The Card inside names the dialog through its title (the `title` group, `aria-labelledby`); a dialog without a heading takes `label`, printed for screen readers only. `isAlert` makes it an `alertdialog`, and `hasDescription` points `aria-describedby` at the element given the `description` group. Because the page outside is inert and hidden, anything that must be heard or reached while it is open renders inside it: the topmost open dialog hosts the page's live region and the toast stack, both outside the card, through its `toLiveRegionView` (see Building components).
|
|
1059
1059
|
|
|
1060
|
-
**Card** → `div.card` (`.narrow`; `form.card` from `Card.formView`, named by its title (`aria-label`), so a form landmark; no role of its own, the Dialog around it is the dialog): a paper panel with the card shadow, `min(680px, 100%)` (440px narrow), entering in 3 steps over 0.14s from `scale(0.97)`. An inverted title bar `header.card-tb` (`6px 8px 6px 12px`, 13px uppercase 0.06em (`--tracking-head`), a close IconButton when it has `closeAttributes`, held by `Card.formView`'s `isBusy` while work the reader may not walk away from runs: `aria-disabled`, in the keyboard, its `closeAttributes` and so its handler dropped, faded to 0.6 on the bar, as a held button in a panel's title bar), a scrolling `div.card-body` (16px, 14px gap; a word longer than the card, a path or an id, breaks rather than widening it), and `div.card-actions` (`12px 16px`) over a 2px ink rule: `lead` first, then `span.card-acts` holding the actions at the right, which drop below the lead together when the bar wraps. Inside a Dialog, where the page's keys wait, the bar moves focus between its actions: `h` or ← to the one before, `l` or → to the one after, in the order they print, stopping at the ends, never pressing one (Enter, Space and Tab stay the browser's). The keys are bound by a Mount on the bar (`BindActionKeys`), which reads the real key event: only a key whose target is one of that bar's own buttons or links, unmodified, outside an IME and not already handled, and only when there is an action that way, moves focus and has its default prevented. A field, the title bar's close, a menu's items and a nested dialog's bar keep their keys. A disabled action is passed over; an `aria-disabled` one (held: busy, done or disabled) is still a stop and still ignores presses. **Card.formView** is the one way to make a form card: its `body` in one Fields group, then the action bar's cancel (`cancel`, closing through `closeAttributes`) and the primary default submit button (`submit`, its id `Card.submitId(id)`). One flag, `isBusy`, holds every way the form could change or leave while its work runs: the submit button prints busy and turns `type="button"`, the close glyph and cancel are held with their close attributes dropped, and the Fields group is natively disabled and `aria-busy`, so nothing changes under the send and nothing sends twice. In a Dialog, the
|
|
1060
|
+
**Card** → `div.card` (`.narrow`; `form.card` from `Card.formView`, named by its title (`aria-label`), so a form landmark; no role of its own, the Dialog around it is the dialog): a paper panel with the card shadow, `min(680px, 100%)` (440px narrow), entering in 3 steps over 0.14s from `scale(0.97)`. An inverted title bar `header.card-tb` (`6px 8px 6px 12px`, 13px uppercase 0.06em (`--tracking-head`), a close IconButton when it has `closeAttributes`, held by `Card.formView`'s `isBusy` while work the reader may not walk away from runs: `aria-disabled`, in the keyboard, its `closeAttributes` and so its handler dropped, faded to 0.6 on the bar, as a held button in a panel's title bar), a scrolling `div.card-body` (16px, 14px gap; a word longer than the card, a path or an id, breaks rather than widening it), and `div.card-actions` (`12px 16px`) over a 2px ink rule: `lead` first, then `span.card-acts` holding the actions at the right, which drop below the lead together when the bar wraps. Inside a Dialog, where the page's keys wait, the bar moves focus between its actions: `h` or ← to the one before, `l` or → to the one after, in the order they print, stopping at the ends, never pressing one (Enter, Space and Tab stay the browser's). The keys are bound by a Mount on the bar (`BindActionKeys`), which reads the real key event: only a key whose target is one of that bar's own buttons or links, unmodified, outside an IME and not already handled, and only when there is an action that way, moves focus and has its default prevented. A field, the title bar's close, a menu's items and a nested dialog's bar keep their keys. A disabled action is passed over; an `aria-disabled` one (held: busy, done or disabled) is still a stop and still ignores presses. **Card.formView** is the one way to make a form card: its `body` in one Fields group, then the action bar's `lead` (a form's own delete, before the push), cancel (`cancel`, closing through `closeAttributes`) and the primary default submit button (`submit`, its id `Card.submitId(id)`). One flag, `isBusy`, holds every way the form could change or leave while its work runs: the submit button prints busy and turns `type="button"`, the close glyph and cancel are held with their close attributes dropped, and the Fields group is natively disabled and `aria-busy`, so nothing changes under the send and nothing sends twice. In a Dialog, the state that sets `isBusy` is the `isCloseHeld` the parent passes the Dialog's update, so Escape and the veil are held with it, and a failed send frees both, the reason on the field (the specimen's rename refuses a name with a slash). `notices` print above the Fields group, outside the hold: what the page says while the form is open (a lost connection with its retry), whose controls stay live while it sends. `isSubmitDisabled` holds the submit button alone while there is nothing to send (a name unchanged), the fields and cancel live. The fields leave the keyboard as they are held, so the update moves focus to the submit button as it sends (`Focus.Focus({ id: Card.submitId(id) })`), and it still ignores `onSubmit` while busy, for an Enter that raced the render. A ConfirmDialog with `isDanger` still opens on cancel.
|
|
1061
1061
|
|
|
1062
|
-
- **ConfirmDialog** → a Submodel over Dialog (an `alertdialog` when `isDanger`, a `dialog` otherwise) holding a narrow Card whose body is an Outcome (warn, or error when `isDanger`), the Outcome's body describing the dialog. Its view inputs follow the system's words: `title` (the title bar), `question` (the Outcome's title), `body`, and `label` (the confirming button's word). Without `isDanger` the confirm is primary and default; with it the confirm is the red button and cancel is the default, where focus starts. `Confirmed` does not close it: work that takes a while stays in the dialog. `isBusy` holds the confirming button (its word, the working cursor, focus kept, a second press ignored)
|
|
1062
|
+
- **ConfirmDialog** → a Submodel over Dialog (an `alertdialog` when `isDanger`, a `dialog` otherwise) holding a narrow Card whose body is an Outcome (warn, or error when `isDanger`), the Outcome's body describing the dialog. Its view inputs follow the system's words: `title` (the title bar), `question` (the Outcome's title), `body`, and `label` (the confirming button's word). Without `isDanger` the confirm is primary and default; with it the confirm is the red button and cancel is the default, where focus starts. `Confirmed` does not close it: work that takes a while stays in the dialog. `isBusy` holds the confirming button (its word, the working cursor, focus kept, a second press ignored) and cancel (`aria-disabled`, in the keyboard, no ring and no shadow while the busy commit keeps focus); the parent calls `ConfirmDialog.close` when the work lands, or keeps it open while it is not done. `isDisabled` holds it the same way without the working cursor, for an action that is blocked rather than running (writes not accepted yet). `toDetailView` prints the application's own content under the sentence, inside the Outcome's body and so in the dialog's description: what the work said back (a refused delete's reason as an error Notice) or a notice with its own retry. Cancel, Escape and the veil report `Cancelled`; the parent decides whether that also stops work already running, and ignores the work's result once cancelled. The parent passes the same state as `isCloseHeld` to `ConfirmDialog.update`, so cancel, Escape and a press on the veil change nothing while the work runs: the dialog stays open and reports no `Cancelled`, and the reader sees how it ends.
|
|
1063
1063
|
- **CommandPalette** (Submodel over Dialog and `@foldkit/ui`'s Input with a listbox of its own, since a combobox would not let the cursor land on held rows or Escape reach the Dialog) → a card at 18vh holding `div.palette-q` (a `span.palette-scope` and a combobox input), `div.palette-detail` when the app gives `toDetailView`, `div.palette-list[role="listbox"]` of `div.palette-group` headings and `div.palette-row[role="option"][aria-selected]` rows (control register; `isAuthored` titles, `.authored`, set in sans with a glyph and wait for a query), a `div.sr-only[role="status"]`, and `div.palette-more`. Once a query is typed the status says how many rows it finds ("3 results.", "Nothing matches."); it is empty as the card opens and on a fresh page, so it is never mounted holding its words (rule 12). Labels and hints wrap within their row, including long names and destination paths. One cursor: the inverted row, moved by the keys only. ↑ ↓ move it and stop at the ends, each key one row from where the last left it however fast they come (the field's own handler, in the view, reports `PressedArrowKey` with the rows its render showed, and update steps from the Model's cursor); Home and End stay the field's, moving the caret; Enter does what a click on its row does, decided in update from what the render showed each row to be (held, opening a page, a choice on a page): a command closes the palette first so what it opens takes focus, and reports `SelectedCommand`. An arrow or an Enter pressed before the list has caught up with the keys before it (straight after a letter, or before the arrows already waiting have moved the cursor) cannot name a row yet, so it waits for the next render: the row that render shows the cursor on becomes the cursor, and a waiting Enter acts on it as on any row. So arrows typed straight after a letter step through the new query's rows, and an Enter acts on the keys before it, never on letters typed after it. While the palette is open, ↑ ↓ and Enter pressed before its field has focus (straight after the key that opened it) are its own too: `CommandPalette.subscriptions(keyPrefix)` takes them on the window before the page's bindings see them, and they wait as above. A key an IME is composing stays the IME's. The pointer shades the row under it (Header Grey, as a list's row) and a click runs that row; it never moves the cursor and never scrolls. The cursor comes into view on every key that moves it, when the view or the query changes, and when its row leaves the list (it falls back to the first): the app, which changes the list, returns `CommandPalette.ScrollToCursor` with the palette's id; a row that only reflows is not followed. The cursor holds to its row by id while the app updates the list; `CommandPalette.maybeCursorId` reads the row it shows for a list of commands, so the app can compare it before and after its change. A row with `isDisabled` prints muted (`[aria-disabled="true"]`), its `reason` in the hint, and the cursor still lands on it but Enter and a click do nothing. **Pages:** a command with a `page` (a status, a date, a destination) ends in a caret and opens that page in place instead of closing: `div.palette-q` holds a Back IconButton, the scope chip (what the commands act on, kept on every page), the page's title (`span.palette-title`, pixel caps), then the page's own field (placeholder `find`, else "type to narrow"), focused. A long scope wraps inside its chip and the field takes its own line when too little is left beside it, so nothing leaves the card at 390px. The choices are found in like the commands. The choice in force now (`current`) carries a `current` tag and cannot be picked again. The cursor starts on the page's `start` (the next status; the first choice a typed query finds), else on `current`, else on the first choice, read while the reader has not moved it, so an application that keeps `start` steady across a change made elsewhere moves the tag, not the cursor. Back, or Backspace in an empty field, returns to the commands with their query and cursor as they were, also when the palette opened straight on a page (`CommandPalette.openAt`). A pick (`SelectedChoice`) holds the page until the parent answers: the row prints the working cursor, the list is `aria-busy`, and moving, picking, typing and Back wait. With `CommandPalette.pickSucceeded` the palette closes; with `pickFailed` the page stays with its query, focus and the cursor on that choice, and the app says why in `toDetailView`, which shows on the commands and on every page alike. Escape closes the whole palette, even while a pick is out, and reports `Closed`; an answer arriving after that, or after the page was left, changes nothing. A page whose command leaves the list gives way to the commands. One level: choices do not open pages.
|
|
1064
1064
|
- **Keymap** → `div.keymap > div > h3 + div.keymap-row > kbd + span`: keys in the 11px pixel face under 10px muted group heads, for a narrow Card.
|
|
1065
1065
|
|
|
@@ -1221,7 +1221,8 @@ Each component is `src/<name>/<name>.ts` with an explicit `index.ts`, a colocate
|
|
|
1221
1221
|
15. **A Mount reports through the parent.** A View that owns a Mount takes a required `toParentMessage: (message: X.Message) => ParentMessage` (TopBar, StatusBar, Toolbar, the mermaid adapter), and a fire-and-forget Mount maps to a caller `Completed*` Message the parent may ignore. A bare `onX: Message` is used only where the parent's Model acts on the fact (ImageFrame's `onBroken`). In a Scene, resolve the Mount by name with the lifted Message: `Scene.Mount.resolve({ name: X.Mount.name }, toParentMessage(...))`. Where every application's Scene must resolve a component's Mounts to render a page at all, the namespace exports `sceneMounts`, the matchers paired with Messages that change nothing, for `Scene.Mount.resolveAll(...X.sceneMounts)`: `TopBar.sceneMounts(toParentMessage)`, `Toast.sceneMounts` and `Dialog.sceneMounts` (which serves ConfirmDialog, KeyHelp and CommandPalette too).
|
|
1222
1222
|
16. **Fragment links focus through the application.** A link to `#id` (SkipLink, an ErrorSummary's links) needs the application's URL handler to return `SkipLink.ScrollToFragment` for the changed URL, since Foldkit routes the click.
|
|
1223
1223
|
17. **Platform services are the application's.** A component that needs a platform service says so in its TSDoc and here (CopyButton writes through Effect's `Clipboard`), and the application provides the Layer in its `resources`.
|
|
1224
|
-
18. **
|
|
1224
|
+
18. **Held work gives up.** Work that holds a dialog's ways out (a save, a delete) waits on something outside the page, which can hang, and a hang would shut the reader in. Its Command ends in a timeout mapped to its failure Message (`Effect.timeoutOrElse({ duration, orElse: () => Effect.succeed(FailedSave({ … })) })`), so the hold always ends with the reason in the dialog.
|
|
1225
|
+
19. **Upstream gaps.** Where `@foldkit/ui` lacks a capability, the workaround carries `// NOTE: @foldkit/ui gap: <what>` at its site, and that note is the record: there is no list beside it. A wrapper that must build a child Message itself says so in the same note, with a lint disable on that line alone.
|
|
1225
1226
|
|
|
1226
1227
|
## Do's and Don'ts
|
|
1227
1228
|
|
package/dist/src/card/card.d.ts
CHANGED
|
@@ -24,13 +24,18 @@ export type ViewConfig<Message> = Readonly<{
|
|
|
24
24
|
/** The raised surface: paper in a 2px ink box on the 8px print shadow, under a title bar. */
|
|
25
25
|
export declare const view: <Message>(config: ViewConfig<Message>, h: HtmlBuilder<Message>) => Html;
|
|
26
26
|
/** Configuration for rendering a form card with `formView`: a card's, but for its body, its
|
|
27
|
-
* close attributes and its
|
|
28
|
-
|
|
27
|
+
* close attributes and its actions, which the form fills. `lead` stays the card's: what
|
|
28
|
+
* goes before the push, such as a form's own delete. */
|
|
29
|
+
export type FormViewConfig<Message> = Omit<ViewConfig<Message>, "body" | "closeAttributes" | "actions"> & Readonly<{
|
|
29
30
|
/** Unique in the document: the submit button's id is `submitId(id)`. */
|
|
30
31
|
id: string;
|
|
31
32
|
/** The form's content, fields and all, in one Fields group that `isBusy` holds. A link in
|
|
32
33
|
* it (an ErrorSummary's) stays live. */
|
|
33
34
|
body: ReadonlyArray<Html | string>;
|
|
35
|
+
/** Above the fields, outside the group `isBusy` holds: what the page says about the form
|
|
36
|
+
* while it is open (a lost connection with its retry), whose controls stay live while it
|
|
37
|
+
* sends. */
|
|
38
|
+
notices?: ReadonlyArray<Html | string>;
|
|
34
39
|
/** The submit button's words. It prints primary, the card's default. */
|
|
35
40
|
submit: string;
|
|
36
41
|
/** The cancel button's words, before the submit. It closes through `closeAttributes`. */
|
|
@@ -40,9 +45,10 @@ export type FormViewConfig<Message> = Omit<ViewConfig<Message>, "body" | "closeA
|
|
|
40
45
|
closeAttributes?: Attributes<Message>;
|
|
41
46
|
/** The form's work is running (it sends): the submit button is busy, the close glyph and
|
|
42
47
|
* cancel are held without `closeAttributes`, and the body is natively disabled, so
|
|
43
|
-
* nothing changes under the send and nothing sends twice. In a Dialog,
|
|
44
|
-
*
|
|
45
|
-
* so the update still ignores `onSubmit` while
|
|
48
|
+
* nothing changes under the send and nothing sends twice. In a Dialog, pass the same state
|
|
49
|
+
* as `isCloseHeld` to its update, so Escape and the veil are held too. Enter in a field
|
|
50
|
+
* can submit before the fields are held, so the update still ignores `onSubmit` while
|
|
51
|
+
* busy. */
|
|
46
52
|
isBusy?: boolean;
|
|
47
53
|
/** The form cannot be sent yet (nothing to send): the submit button is held as a Button
|
|
48
54
|
* is, and the fields and cancel stay live. Enter in a field can still submit, so the
|
|
@@ -55,7 +61,7 @@ export type FormViewConfig<Message> = Omit<ViewConfig<Message>, "body" | "closeA
|
|
|
55
61
|
* to the page: the form's update moves it here as it sends, with
|
|
56
62
|
* `Focus.Focus({ id: Card.submitId(id) })`. */
|
|
57
63
|
export declare const submitId: (id: string) => string;
|
|
58
|
-
/** A card that is a form, named by its title, so a form landmark: its body in
|
|
59
|
-
* group, then cancel and the primary submit button in the action bar. */
|
|
64
|
+
/** A card that is a form, named by its title, so a form landmark: its notices, its body in
|
|
65
|
+
* one Fields group, then cancel and the primary submit button in the action bar. */
|
|
60
66
|
export declare const formView: <Message>(config: FormViewConfig<Message>, h: HtmlBuilder<Message>) => Html;
|
|
61
67
|
//# sourceMappingURL=card.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../../src/card/card.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAQ9D,sDAAsD;AACtD,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,eAAe,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACnC;gDAC4C;IAC5C,eAAe,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACtC,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACvC,uEAAuE;IACvE,IAAI,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACpC;6DACyD;IACzD,mBAAmB,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;CAC3C,CAAC,CAAC;AAsDH,6FAA6F;AAC7F,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IACd,CAAC;AAIxE;
|
|
1
|
+
{"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../../src/card/card.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAQ9D,sDAAsD;AACtD,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,eAAe,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACnC;gDAC4C;IAC5C,eAAe,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACtC,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACvC,uEAAuE;IACvE,IAAI,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACpC;6DACyD;IACzD,mBAAmB,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;CAC3C,CAAC,CAAC;AAsDH,6FAA6F;AAC7F,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IACd,CAAC;AAIxE;;yDAEyD;AACzD,MAAM,MAAM,cAAc,CAAC,OAAO,IAAI,IAAI,CACxC,UAAU,CAAC,OAAO,CAAC,EACnB,MAAM,GAAG,iBAAiB,GAAG,SAAS,CACvC,GACC,QAAQ,CAAC;IACP,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAC;IACX;6CACyC;IACzC,IAAI,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACnC;;iBAEa;IACb,OAAO,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACvC,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;wEACoE;IACpE,eAAe,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACtC;;;;;gBAKY;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;6DAEyD;IACzD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC,CAAC;AAEL;;gDAEgD;AAChD,eAAO,MAAM,QAAQ,OAAQ,MAAM,KAAG,MAAwB,CAAC;AAE/D;qFACqF;AACrF,eAAO,MAAM,QAAQ,GAAI,OAAO,UACtB,cAAc,CAAC,OAAO,CAAC,KAC5B,WAAW,CAAC,OAAO,CAAC,KACtB,IAyDF,CAAC"}
|
package/dist/src/card/card.js
CHANGED
|
@@ -33,10 +33,10 @@ export const view = (config, h) => __foldkitBrandViewResult((h.div([h.Class(clas
|
|
|
33
33
|
* to the page: the form's update moves it here as it sends, with
|
|
34
34
|
* `Focus.Focus({ id: Card.submitId(id) })`. */
|
|
35
35
|
export const submitId = (id) => __foldkitBrandViewResult((`${id}-submit`), "@gjermundgaraba-designs/pixel-paper/src/card/card.js#submitId");
|
|
36
|
-
/** A card that is a form, named by its title, so a form landmark: its body in
|
|
37
|
-
* group, then cancel and the primary submit button in the action bar. */
|
|
36
|
+
/** A card that is a form, named by its title, so a form landmark: its notices, its body in
|
|
37
|
+
* one Fields group, then cancel and the primary submit button in the action bar. */
|
|
38
38
|
export const formView = (config, h) => {
|
|
39
|
-
const { id, title, submit, cancel, closeAttributes, isBusy = false, isSubmitDisabled = false, onSubmit, } = config;
|
|
39
|
+
const { id, title, submit, cancel, closeAttributes, notices = [], isBusy = false, isSubmitDisabled = false, onSubmit, } = config;
|
|
40
40
|
const cancelButton = cancel === undefined
|
|
41
41
|
? []
|
|
42
42
|
: [
|
|
@@ -62,7 +62,7 @@ export const formView = (config, h) => {
|
|
|
62
62
|
h.OnSubmit(onSubmit),
|
|
63
63
|
], childrenView({
|
|
64
64
|
...config,
|
|
65
|
-
body: [Fields.view({ body: config.body, isBusy }, h)],
|
|
65
|
+
body: [...notices, Fields.view({ body: config.body, isBusy }, h)],
|
|
66
66
|
isCloseHeld: isBusy,
|
|
67
67
|
actions: [...cancelButton, submitButton],
|
|
68
68
|
}, h))), "@gjermundgaraba-designs/pixel-paper/src/card/card.js#formView");
|
|
@@ -31,7 +31,6 @@ export declare const Model: Schema.Struct<{
|
|
|
31
31
|
}>;
|
|
32
32
|
readonly maybeFocusSelector: Schema.Option<Schema.String>;
|
|
33
33
|
}>;
|
|
34
|
-
readonly isCloseHeld: Schema.Boolean;
|
|
35
34
|
}>;
|
|
36
35
|
readonly query: Schema.String;
|
|
37
36
|
readonly maybeCursor: Schema.Option<Schema.String>;
|
|
@@ -127,7 +126,6 @@ export declare const update: (model: Model, message: Message) => Readonly<{
|
|
|
127
126
|
};
|
|
128
127
|
readonly maybeFocusSelector: Option.Option<string>;
|
|
129
128
|
};
|
|
130
|
-
readonly isCloseHeld: boolean;
|
|
131
129
|
};
|
|
132
130
|
readonly query: string;
|
|
133
131
|
readonly maybeCursor: Option.Option<string>;
|
|
@@ -291,7 +289,6 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
|
|
|
291
289
|
};
|
|
292
290
|
readonly maybeFocusSelector: Option.Option<string>;
|
|
293
291
|
};
|
|
294
|
-
readonly isCloseHeld: boolean;
|
|
295
292
|
};
|
|
296
293
|
readonly query: string;
|
|
297
294
|
readonly maybeCursor: Option.Option<string>;
|
|
@@ -460,7 +457,6 @@ export declare const view: import("foldkit/submodel").View<{
|
|
|
460
457
|
};
|
|
461
458
|
readonly maybeFocusSelector: Option.Option<string>;
|
|
462
459
|
};
|
|
463
|
-
readonly isCloseHeld: boolean;
|
|
464
460
|
};
|
|
465
461
|
readonly query: string;
|
|
466
462
|
readonly maybeCursor: Option.Option<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commandPalette.d.ts","sourceRoot":"","sources":["../../../src/commandPalette/commandPalette.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,MAAM,EAAS,MAAM,EAAE,MAAM,EAAwB,MAAM,QAAQ,CAAC;AACpF,OAAO,KAAK,cAAc,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAa,IAAI,EAAkC,MAAM,cAAc,CAAC;AAKpF,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAoBzC,OAAO,KAAK,EAAU,OAAO,EAAQ,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAavC;;;;;;;;;;;kDAWkD;AAClD,eAAO,MAAM,KAAK
|
|
1
|
+
{"version":3,"file":"commandPalette.d.ts","sourceRoot":"","sources":["../../../src/commandPalette/commandPalette.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import type { Html } from "foldkit/html";
|
|
3
3
|
import * as Update from "foldkit/update";
|
|
4
|
+
import * as Dialog from "../dialog/dialog.ts";
|
|
4
5
|
/** Schema for a confirm dialog: the pixel-paper Dialog it opens in. */
|
|
5
6
|
export declare const Model: Schema.Struct<{
|
|
6
7
|
readonly dialog: Schema.Struct<{
|
|
@@ -16,7 +17,6 @@ export declare const Model: Schema.Struct<{
|
|
|
16
17
|
}>;
|
|
17
18
|
readonly maybeFocusSelector: Schema.Option<Schema.String>;
|
|
18
19
|
}>;
|
|
19
|
-
readonly isCloseHeld: Schema.Boolean;
|
|
20
20
|
}>;
|
|
21
21
|
}>;
|
|
22
22
|
export type Model = typeof Model.Type;
|
|
@@ -61,7 +61,7 @@ export type Message = typeof Message.Type;
|
|
|
61
61
|
/** Union of OutMessages a confirm dialog reports. `Confirmed`: the reader pressed the action;
|
|
62
62
|
* the dialog stays open until the parent calls `close`. `Cancelled`: cancel, Escape or a click
|
|
63
63
|
* on the veil; the dialog has closed, and whether that also stops work already running is
|
|
64
|
-
* the parent's to decide. Never reported while its ways out are held (`
|
|
64
|
+
* the parent's to decide. Never reported while its ways out are held (`Dialog.UpdateContext`). */
|
|
65
65
|
export declare const OutMessage: import("foldkit/message").MessageUnion<{
|
|
66
66
|
readonly Confirmed: {};
|
|
67
67
|
readonly Cancelled: {};
|
|
@@ -73,8 +73,9 @@ export type InitConfig = Readonly<{
|
|
|
73
73
|
}>;
|
|
74
74
|
/** Creates a closed confirm dialog. */
|
|
75
75
|
export declare const init: (config: InitConfig) => Model;
|
|
76
|
-
/** Processes a confirm dialog Message and returns the next Model, Commands and OutMessage.
|
|
77
|
-
|
|
76
|
+
/** Processes a confirm dialog Message and returns the next Model, Commands and OutMessage.
|
|
77
|
+
* While `context.isCloseHeld`, cancel, Escape and the veil change nothing. */
|
|
78
|
+
export declare const update: (model: Model, message: Message, context?: Dialog.UpdateContext) => Readonly<{
|
|
78
79
|
model: {
|
|
79
80
|
readonly dialog: {
|
|
80
81
|
readonly dialog: {
|
|
@@ -89,7 +90,6 @@ export declare const update: (model: Model, message: Message) => Readonly<{
|
|
|
89
90
|
};
|
|
90
91
|
readonly maybeFocusSelector: import("effect/Option").Option<string>;
|
|
91
92
|
};
|
|
92
|
-
readonly isCloseHeld: boolean;
|
|
93
93
|
};
|
|
94
94
|
};
|
|
95
95
|
commands?: Update.Commands<{
|
|
@@ -145,20 +145,13 @@ export declare const update: (model: Model, message: Message) => Readonly<{
|
|
|
145
145
|
}>;
|
|
146
146
|
/** Opens the confirm dialog: focus starts on the action, or on cancel when it is `isDanger`. */
|
|
147
147
|
export declare const open: (model: Model) => Update.Return<Model, Message>;
|
|
148
|
-
/** Closes the confirm dialog without reporting `Cancelled
|
|
149
|
-
*
|
|
148
|
+
/** Closes the confirm dialog without reporting `Cancelled`: call it once confirmed work has
|
|
149
|
+
* landed. */
|
|
150
150
|
export declare const close: (model: Model) => Update.Return<Model, Message>;
|
|
151
|
-
/** Holds cancel, Escape and the veil, or lets them go: while held the dialog stays open and
|
|
152
|
-
* reports no `Cancelled`, and cancel prints held (`aria-disabled`, in the keyboard). Hold it
|
|
153
|
-
* in the update that starts confirmed work whose failure must land here, and let go in the
|
|
154
|
-
* update where the work fails; `close` lets go once it lands. */
|
|
155
|
-
export declare const setCloseHeld: (model: Model, isHeld: boolean) => Model;
|
|
156
151
|
/** The confirm dialog's id, unique in the document. */
|
|
157
152
|
export declare const id: (model: Model) => string;
|
|
158
153
|
/** Whether the confirm dialog is open. */
|
|
159
154
|
export declare const isOpen: (model: Model) => boolean;
|
|
160
|
-
/** Whether its ways out are held (`setCloseHeld`). */
|
|
161
|
-
export declare const isCloseHeld: (model: Model) => boolean;
|
|
162
155
|
/** Per-render inputs passed to `view` through `h.submodel`'s `viewInputs`. */
|
|
163
156
|
export type ViewInputs = Readonly<{
|
|
164
157
|
/** The title bar: the action's name (`"Delete"`). */
|
|
@@ -178,8 +171,9 @@ export type ViewInputs = Readonly<{
|
|
|
178
171
|
/** Destructive: the confirming button turns red and cancel becomes the default. */
|
|
179
172
|
isDanger?: boolean;
|
|
180
173
|
/** The confirmed work is running: the confirming button is held (its word stays, the
|
|
181
|
-
* working cursor prints, a second press does nothing) and keeps focus
|
|
182
|
-
*
|
|
174
|
+
* working cursor prints, a second press does nothing) and keeps focus, and cancel is held
|
|
175
|
+
* with it. Pair it with `isCloseHeld` in `update`'s context, from the same state, so
|
|
176
|
+
* Escape and the veil change nothing either. */
|
|
183
177
|
isBusy?: boolean;
|
|
184
178
|
/** The action cannot run now (nothing is running; it is blocked): the confirming button is
|
|
185
179
|
* held, `aria-disabled` without the working cursor, and stays in the keyboard, so focus
|
|
@@ -198,9 +192,9 @@ export type ViewInputs = Readonly<{
|
|
|
198
192
|
* arm-then-confirm button: red never rests on a button, and an armed button is easy to
|
|
199
193
|
* double-click through.
|
|
200
194
|
*
|
|
201
|
-
* Work that takes a while stays in the dialog: set `isBusy` while it runs
|
|
202
|
-
* has landed, and on failure keep it open with the
|
|
203
|
-
*
|
|
195
|
+
* Work that takes a while stays in the dialog: set `isBusy` while it runs (and `isCloseHeld`
|
|
196
|
+
* in `update`'s context), `close` once it has landed, and on failure keep it open with the
|
|
197
|
+
* error in `toDetailView`.
|
|
204
198
|
*/
|
|
205
199
|
export declare const view: import("foldkit/submodel").View<{
|
|
206
200
|
readonly dialog: {
|
|
@@ -216,7 +210,6 @@ export declare const view: import("foldkit/submodel").View<{
|
|
|
216
210
|
};
|
|
217
211
|
readonly maybeFocusSelector: import("effect/Option").Option<string>;
|
|
218
212
|
};
|
|
219
|
-
readonly isCloseHeld: boolean;
|
|
220
213
|
};
|
|
221
214
|
}, {
|
|
222
215
|
readonly _tag: "ClickedCancel";
|
|
@@ -280,8 +273,9 @@ export declare const view: import("foldkit/submodel").View<{
|
|
|
280
273
|
/** Destructive: the confirming button turns red and cancel becomes the default. */
|
|
281
274
|
isDanger?: boolean;
|
|
282
275
|
/** The confirmed work is running: the confirming button is held (its word stays, the
|
|
283
|
-
* working cursor prints, a second press does nothing) and keeps focus
|
|
284
|
-
*
|
|
276
|
+
* working cursor prints, a second press does nothing) and keeps focus, and cancel is held
|
|
277
|
+
* with it. Pair it with `isCloseHeld` in `update`'s context, from the same state, so
|
|
278
|
+
* Escape and the veil change nothing either. */
|
|
285
279
|
isBusy?: boolean;
|
|
286
280
|
/** The action cannot run now (nothing is running; it is blocked): the confirming button is
|
|
287
281
|
* held, `aria-disabled` without the working cursor, and stays in the keyboard, so focus
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirmDialog.d.ts","sourceRoot":"","sources":["../../../src/confirmDialog/confirmDialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAQ,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAe,MAAM,cAAc,CAAC;AAItD,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"confirmDialog.d.ts","sourceRoot":"","sources":["../../../src/confirmDialog/confirmDialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAQ,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAe,MAAM,cAAc,CAAC;AAItD,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAIzC,OAAO,KAAK,MAAM,MAAM,qBAAqB,CAAC;AAM9C,uEAAuE;AACvE,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;EAEhB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC;AAItC,0DAA0D;AAC1D,eAAO,MAAM,OAAO;;QACE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG3B,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAI1C;;;mGAGmG;AACnG,eAAO,MAAM,UAAU;;;EAGrB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;AAIhD,+DAA+D;AAC/D,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAElD,uCAAuC;AACvC,eAAO,MAAM,IAAI,WAAY,UAAU,KAAG,KAExC,CAAC;AAkBH;+EAC+E;AAC/E,eAAO,MAAM,MAAM,UAAW,KAAK,WAAW,OAAO,YAAW,MAAM,CAAC,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAShF,CAAC;AAEL,gGAAgG;AAChG,eAAO,MAAM,IAAI,UAAW,KAAK,KAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAA2B,CAAC;AAE5F;cACc;AACd,eAAO,MAAM,KAAK,UAAW,KAAK,KAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAA4B,CAAC;AAI9F,uDAAuD;AACvD,eAAO,MAAM,EAAE,UAAW,KAAK,KAAG,MAAiC,CAAC;AAEpE,0CAA0C;AAC1C,eAAO,MAAM,MAAM,UAAW,KAAK,KAAG,OAAsC,CAAC;AAI7E,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,6FAA6F;IAC7F,QAAQ,EAAE,MAAM,CAAC;IACjB;;uCAEmC;IACnC,IAAI,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACnC;;oBAEgB;IAChB,YAAY,CAAC,EAAE,MAAM,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IAClD,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;qDAGiD;IACjD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;wDAEoD;IACpD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;0CACsC;IACtC,gBAAgB,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;CAC7C,CAAC,CAAC;AAwDH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAjGf,qDAAqD;WAC9C,MAAM;IACb,6FAA6F;cACnF,MAAM;IAChB;;uCAEmC;UAC7B,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC;IAClC;;oBAEgB;mBACD,MAAM,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC;IACjD,iDAAiD;WAC1C,MAAM;IACb,mFAAmF;eACxE,OAAO;IAClB;;;qDAGiD;aACxC,OAAO;IAChB;;wDAEoD;iBACvC,OAAO;IACpB;0CACsC;uBACnB,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS;GAmF5C,CAAC"}
|
|
@@ -25,7 +25,7 @@ export const Message = defineMessageUnion({
|
|
|
25
25
|
/** Union of OutMessages a confirm dialog reports. `Confirmed`: the reader pressed the action;
|
|
26
26
|
* the dialog stays open until the parent calls `close`. `Cancelled`: cancel, Escape or a click
|
|
27
27
|
* on the veil; the dialog has closed, and whether that also stops work already running is
|
|
28
|
-
* the parent's to decide. Never reported while its ways out are held (`
|
|
28
|
+
* the parent's to decide. Never reported while its ways out are held (`Dialog.UpdateContext`). */
|
|
29
29
|
export const OutMessage = defineMessageUnion({
|
|
30
30
|
Confirmed: {},
|
|
31
31
|
Cancelled: {},
|
|
@@ -44,32 +44,27 @@ const dialogFold = foldDialog(Dialog, {
|
|
|
44
44
|
toParentMessage: (message) => __foldkitBrandViewResult((Message.GotDialogMessage({ message })), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#toParentMessage"),
|
|
45
45
|
toParentOutMessage: toCancelled,
|
|
46
46
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
const FREE = { isCloseHeld: false };
|
|
48
|
+
/** Processes a confirm dialog Message and returns the next Model, Commands and OutMessage.
|
|
49
|
+
* While `context.isCloseHeld`, cancel, Escape and the veil change nothing. */
|
|
50
|
+
export const update = (model, message, context = FREE) => __foldkitBrandViewResult((Message.match(message, {
|
|
51
|
+
GotDialogMessage: ({ message: dialogMessage }) => __foldkitBrandViewResult((dialogFold.updateWith(context)(model, dialogMessage)), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#GotDialogMessage"),
|
|
50
52
|
ClickedConfirm: () => (__foldkitBrandViewResult(({ model, outMessage: OutMessage.Confirmed() }), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#ClickedConfirm")),
|
|
51
|
-
ClickedCancel: () => __foldkitBrandViewResult((
|
|
53
|
+
ClickedCancel: () => __foldkitBrandViewResult((context.isCloseHeld
|
|
52
54
|
? { model }
|
|
53
55
|
: pipe(dialogFold.close(model), Update.withOutMessage(OutMessage.Cancelled()))), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#ClickedCancel"),
|
|
54
56
|
})), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#update");
|
|
55
57
|
/** Opens the confirm dialog: focus starts on the action, or on cancel when it is `isDanger`. */
|
|
56
58
|
export const open = (model) => __foldkitBrandViewResult((dialogFold.open(model)), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#open");
|
|
57
|
-
/** Closes the confirm dialog without reporting `Cancelled
|
|
58
|
-
*
|
|
59
|
+
/** Closes the confirm dialog without reporting `Cancelled`: call it once confirmed work has
|
|
60
|
+
* landed. */
|
|
59
61
|
export const close = (model) => __foldkitBrandViewResult((dialogFold.close(model)), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#close");
|
|
60
|
-
/** Holds cancel, Escape and the veil, or lets them go: while held the dialog stays open and
|
|
61
|
-
* reports no `Cancelled`, and cancel prints held (`aria-disabled`, in the keyboard). Hold it
|
|
62
|
-
* in the update that starts confirmed work whose failure must land here, and let go in the
|
|
63
|
-
* update where the work fails; `close` lets go once it lands. */
|
|
64
|
-
export const setCloseHeld = (model, isHeld) => __foldkitBrandViewResult((modifyFields(model, { dialog: (dialog) => __foldkitBrandViewResult((Dialog.setCloseHeld(dialog, isHeld)), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#dialog~2") })), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#setCloseHeld");
|
|
65
62
|
// ACCESSORS
|
|
66
63
|
/** The confirm dialog's id, unique in the document. */
|
|
67
64
|
export const id = (model) => __foldkitBrandViewResult((Dialog.id(model.dialog)), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#id");
|
|
68
65
|
/** Whether the confirm dialog is open. */
|
|
69
66
|
export const isOpen = (model) => __foldkitBrandViewResult((Dialog.isOpen(model.dialog)), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#isOpen");
|
|
70
|
-
|
|
71
|
-
export const isCloseHeld = (model) => __foldkitBrandViewResult((Dialog.isCloseHeld(model.dialog)), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#isCloseHeld");
|
|
72
|
-
const cardView = (model, viewInputs, render, h) => {
|
|
67
|
+
const cardView = (viewInputs, render, h) => {
|
|
73
68
|
const { isDanger = false, isBusy = false, isDisabled = false, toDetailView } = viewInputs;
|
|
74
69
|
const commit = Button.view({
|
|
75
70
|
label: viewInputs.label,
|
|
@@ -83,7 +78,7 @@ const cardView = (model, viewInputs, render, h) => {
|
|
|
83
78
|
const cancel = Button.view({
|
|
84
79
|
label: "cancel",
|
|
85
80
|
isDefault: isDanger,
|
|
86
|
-
isDisabled:
|
|
81
|
+
isDisabled: isBusy,
|
|
87
82
|
onClick: Message.ClickedCancel(),
|
|
88
83
|
attributes: isDanger ? render.initialFocus : [],
|
|
89
84
|
}, h);
|
|
@@ -113,9 +108,9 @@ const cardView = (model, viewInputs, render, h) => {
|
|
|
113
108
|
* arm-then-confirm button: red never rests on a button, and an armed button is easy to
|
|
114
109
|
* double-click through.
|
|
115
110
|
*
|
|
116
|
-
* Work that takes a while stays in the dialog: set `isBusy` while it runs
|
|
117
|
-
* has landed, and on failure keep it open with the
|
|
118
|
-
*
|
|
111
|
+
* Work that takes a while stays in the dialog: set `isBusy` while it runs (and `isCloseHeld`
|
|
112
|
+
* in `update`'s context), `close` once it has landed, and on failure keep it open with the
|
|
113
|
+
* error in `toDetailView`.
|
|
119
114
|
*/
|
|
120
115
|
export const view = defineView((model, viewInputs, h) => __foldkitBrandViewResult((h.submodel({
|
|
121
116
|
slotId: id(model),
|
|
@@ -125,7 +120,7 @@ export const view = defineView((model, viewInputs, h) => __foldkitBrandViewResul
|
|
|
125
120
|
isAlert: viewInputs.isDanger ?? false,
|
|
126
121
|
hasDescription: true,
|
|
127
122
|
toLiveRegionView: viewInputs.toLiveRegionView,
|
|
128
|
-
toView: (render) => __foldkitBrandViewResult((cardView(
|
|
123
|
+
toView: (render) => __foldkitBrandViewResult((cardView(viewInputs, render, h)), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#toView"),
|
|
129
124
|
},
|
|
130
125
|
toParentMessage: (message) => __foldkitBrandViewResult((Message.GotDialogMessage({ message })), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#toParentMessage~2"),
|
|
131
126
|
})), "@gjermundgaraba-designs/pixel-paper/src/confirmDialog/confirmDialog.js#anonymous"));
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { Message, Model, OutMessage, close, id, init,
|
|
1
|
+
export { Message, Model, OutMessage, close, id, init, isOpen, open, update, view, } from "./confirmDialog.ts";
|
|
2
2
|
export type { InitConfig, ViewInputs } from "./confirmDialog.ts";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/confirmDialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,KAAK,EACL,UAAU,EACV,KAAK,EACL,EAAE,EACF,IAAI,EACJ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/confirmDialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,KAAK,EACL,UAAU,EACV,KAAK,EACL,EAAE,EACF,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,MAAM,EACN,IAAI,GACL,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Message, Model, OutMessage, close, id, init,
|
|
1
|
+
export { Message, Model, OutMessage, close, id, init, isOpen, open, update, view, } from "./confirmDialog.js";
|
|
@@ -2,8 +2,7 @@ import { Option, Schema } from "effect";
|
|
|
2
2
|
import { type ChildAttribute, type Html } from "foldkit/html";
|
|
3
3
|
import * as Mount from "foldkit/mount";
|
|
4
4
|
import type * as Update from "foldkit/update";
|
|
5
|
-
/** Schema for a pixel-paper dialog: the `@foldkit/ui` Dialog it is built on
|
|
6
|
-
* ways out are held (`setCloseHeld`). */
|
|
5
|
+
/** Schema for a pixel-paper dialog: the `@foldkit/ui` Dialog it is built on. */
|
|
7
6
|
export declare const Model: Schema.Struct<{
|
|
8
7
|
readonly dialog: Schema.Struct<{
|
|
9
8
|
readonly id: Schema.String;
|
|
@@ -17,7 +16,6 @@ export declare const Model: Schema.Struct<{
|
|
|
17
16
|
}>;
|
|
18
17
|
readonly maybeFocusSelector: Schema.Option<Schema.String>;
|
|
19
18
|
}>;
|
|
20
|
-
readonly isCloseHeld: Schema.Boolean;
|
|
21
19
|
}>;
|
|
22
20
|
export type Model = typeof Model.Type;
|
|
23
21
|
/** Which way h, l and the side arrows move focus in a dialog's action bar. */
|
|
@@ -75,10 +73,17 @@ export declare const init: (config: InitConfig) => Model;
|
|
|
75
73
|
export declare const id: (model: Model) => string;
|
|
76
74
|
/** Whether the dialog is open. */
|
|
77
75
|
export declare const isOpen: (model: Model) => boolean;
|
|
78
|
-
/**
|
|
79
|
-
export
|
|
76
|
+
/** What a dialog's update reads from its parent as a Message arrives. */
|
|
77
|
+
export type UpdateContext = Readonly<{
|
|
78
|
+
/** The reader may not walk away from the work running now (a save whose failure must land
|
|
79
|
+
* in this dialog): Escape, a press on the veil and a control given the `closeButton` group
|
|
80
|
+
* change nothing and report no `Closed`. Read it from the parent's own state as the
|
|
81
|
+
* Message arrives (`isSaving(model)`), so it holds exactly while the work runs, however
|
|
82
|
+
* soon after the start a key comes. */
|
|
83
|
+
isCloseHeld: boolean;
|
|
84
|
+
}>;
|
|
80
85
|
/** Processes a dialog Message and returns the next Model, Commands and OutMessage. */
|
|
81
|
-
export declare const update: (model: Model, message: Message) => Readonly<{
|
|
86
|
+
export declare const update: (model: Model, message: Message, context?: UpdateContext) => Readonly<{
|
|
82
87
|
model: {
|
|
83
88
|
readonly dialog: {
|
|
84
89
|
readonly id: string;
|
|
@@ -92,7 +97,6 @@ export declare const update: (model: Model, message: Message) => Readonly<{
|
|
|
92
97
|
};
|
|
93
98
|
readonly maybeFocusSelector: Option.Option<string>;
|
|
94
99
|
};
|
|
95
|
-
readonly isCloseHeld: boolean;
|
|
96
100
|
};
|
|
97
101
|
commands?: Update.Commands<{
|
|
98
102
|
readonly _tag: "GotDialogMessage";
|
|
@@ -139,15 +143,8 @@ export declare const update: (model: Model, message: Message) => Readonly<{
|
|
|
139
143
|
/** Opens the dialog: focus moves to the element given the `initialFocus` group, else to the
|
|
140
144
|
* first control, and returns to where it was when the dialog closes. */
|
|
141
145
|
export declare const open: (model: Model) => Update.Return<Model, Message>;
|
|
142
|
-
/** Closes the dialog without reporting `Closed
|
|
146
|
+
/** Closes the dialog without reporting `Closed`. */
|
|
143
147
|
export declare const close: (model: Model) => Update.Return<Model, Message>;
|
|
144
|
-
/** Holds the dialog's ways out, or lets them go: while held, Escape, a press on the veil and
|
|
145
|
-
* a control given the `closeButton` group leave it open and report no `Closed`, so the
|
|
146
|
-
* result of work the reader may not walk away from (a save whose failure must land here)
|
|
147
|
-
* lands in it. Hold it in the update that starts the work, and let go in the update where
|
|
148
|
-
* the work fails, or the reader is shut in with the failure; `close` lets go once it lands.
|
|
149
|
-
* Hold the card's own close controls too (`Card.formView`'s `isBusy`). */
|
|
150
|
-
export declare const setCloseHeld: (model: Model, isHeld: boolean) => Model;
|
|
151
148
|
/** Moves focus for one keydown in an action bar, and says which way it moved. It moves only
|
|
152
149
|
* when the key's target is one of the bar's own actions and there is an action that way:
|
|
153
150
|
* a key typed in a field, a menu's item or a nested dialog inside the bar, a key an IME is
|
|
@@ -235,7 +232,6 @@ export declare const view: import("foldkit/submodel").View<{
|
|
|
235
232
|
};
|
|
236
233
|
readonly maybeFocusSelector: Option.Option<string>;
|
|
237
234
|
};
|
|
238
|
-
readonly isCloseHeld: boolean;
|
|
239
235
|
}, {
|
|
240
236
|
readonly _tag: "GotDialogMessage";
|
|
241
237
|
readonly message: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/dialog/dialog.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,MAAM,EAAE,MAAM,EAAQ,MAAM,QAAQ,CAAC;AAE5D,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,IAAI,EAAmB,MAAM,cAAc,CAAC;AAE/E,OAAO,KAAK,KAAK,MAAM,eAAe,CAAC;AAGvC,OAAO,KAAK,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAO9C;
|
|
1
|
+
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/dialog/dialog.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,11 +10,9 @@ import { defineView } from "foldkit/submodel";
|
|
|
10
10
|
import { isPlain } from "../keys/keys.js";
|
|
11
11
|
import { foldDialog } from "./fold.js";
|
|
12
12
|
// MODEL
|
|
13
|
-
/** Schema for a pixel-paper dialog: the `@foldkit/ui` Dialog it is built on
|
|
14
|
-
* ways out are held (`setCloseHeld`). */
|
|
13
|
+
/** Schema for a pixel-paper dialog: the `@foldkit/ui` Dialog it is built on. */
|
|
15
14
|
export const Model = Schema.Struct({
|
|
16
15
|
dialog: UiDialog.Model,
|
|
17
|
-
isCloseHeld: Schema.Boolean,
|
|
18
16
|
});
|
|
19
17
|
/** Which way h, l and the side arrows move focus in a dialog's action bar. */
|
|
20
18
|
const ActionDirection = Schema.Literals(["Previous", "Next"]);
|
|
@@ -36,15 +34,12 @@ export const OutMessage = defineMessageUnion({
|
|
|
36
34
|
/** Creates a closed dialog. */
|
|
37
35
|
export const init = (config) => (__foldkitBrandViewResult(({
|
|
38
36
|
dialog: UiDialog.init({ id: config.id }),
|
|
39
|
-
isCloseHeld: false,
|
|
40
37
|
}), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#init"));
|
|
41
38
|
// ACCESSORS
|
|
42
39
|
/** The dialog's id, unique in the document. */
|
|
43
40
|
export const id = (model) => __foldkitBrandViewResult((model.dialog.id), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#id");
|
|
44
41
|
/** Whether the dialog is open. */
|
|
45
42
|
export const isOpen = (model) => __foldkitBrandViewResult((model.dialog.isOpen), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#isOpen");
|
|
46
|
-
/** Whether the dialog's ways out are held (`setCloseHeld`). */
|
|
47
|
-
export const isCloseHeld = (model) => __foldkitBrandViewResult((model.isCloseHeld), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#isCloseHeld");
|
|
48
43
|
// UPDATE
|
|
49
44
|
const toOutMessage = UiDialog.OutMessage.match({
|
|
50
45
|
Opened: () => __foldkitBrandViewResult((undefined), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#Opened"),
|
|
@@ -56,27 +51,21 @@ const dialogFold = foldDialog(UiDialog, {
|
|
|
56
51
|
toParentMessage: (message) => __foldkitBrandViewResult((Message.GotDialogMessage({ message })), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#toParentMessage"),
|
|
57
52
|
toParentOutMessage: toOutMessage,
|
|
58
53
|
});
|
|
54
|
+
const FREE = { isCloseHeld: false };
|
|
59
55
|
// NOTE: @foldkit/ui gap: Dialog has no way to hold a close. Escape, a press on the veil and a
|
|
60
56
|
// control given the `closeButton` group all send `RequestedClose`, so a held dialog reads it
|
|
61
57
|
// by its tag and leaves it out.
|
|
62
|
-
const isHeldClose = (
|
|
58
|
+
const isHeldClose = (context, message) => __foldkitBrandViewResult((context.isCloseHeld && message._tag === "RequestedClose"), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#isHeldClose");
|
|
63
59
|
/** Processes a dialog Message and returns the next Model, Commands and OutMessage. */
|
|
64
|
-
export const update = (model, message) => __foldkitBrandViewResult((Message.match(message, {
|
|
65
|
-
GotDialogMessage: ({ message: dialogMessage }) => __foldkitBrandViewResult((isHeldClose(
|
|
60
|
+
export const update = (model, message, context = FREE) => __foldkitBrandViewResult((Message.match(message, {
|
|
61
|
+
GotDialogMessage: ({ message: dialogMessage }) => __foldkitBrandViewResult((isHeldClose(context, dialogMessage) ? { model } : dialogFold.update(model, dialogMessage)), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#GotDialogMessage"),
|
|
66
62
|
PressedActionKey: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#PressedActionKey")),
|
|
67
63
|
})), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#update");
|
|
68
64
|
/** Opens the dialog: focus moves to the element given the `initialFocus` group, else to the
|
|
69
65
|
* first control, and returns to where it was when the dialog closes. */
|
|
70
66
|
export const open = (model) => __foldkitBrandViewResult((dialogFold.open(model)), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#open");
|
|
71
|
-
/** Closes the dialog without reporting `Closed
|
|
72
|
-
export const close = (model) => __foldkitBrandViewResult((dialogFold.close(
|
|
73
|
-
/** Holds the dialog's ways out, or lets them go: while held, Escape, a press on the veil and
|
|
74
|
-
* a control given the `closeButton` group leave it open and report no `Closed`, so the
|
|
75
|
-
* result of work the reader may not walk away from (a save whose failure must land here)
|
|
76
|
-
* lands in it. Hold it in the update that starts the work, and let go in the update where
|
|
77
|
-
* the work fails, or the reader is shut in with the failure; `close` lets go once it lands.
|
|
78
|
-
* Hold the card's own close controls too (`Card.formView`'s `isBusy`). */
|
|
79
|
-
export const setCloseHeld = (model, isHeld) => __foldkitBrandViewResult((modifyFields(model, { isCloseHeld: () => __foldkitBrandViewResult((isHeld), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#isCloseHeld~3") })), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#setCloseHeld");
|
|
67
|
+
/** Closes the dialog without reporting `Closed`. */
|
|
68
|
+
export const close = (model) => __foldkitBrandViewResult((dialogFold.close(model)), "@gjermundgaraba-designs/pixel-paper/src/dialog/dialog.js#close");
|
|
80
69
|
// ACTION BAR KEYS
|
|
81
70
|
const ACTION_BAR_ATTRIBUTE = "data-pixel-paper-action-bar";
|
|
82
71
|
const ACTION_SELECTOR = "button:not(:disabled), a[href]";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as Update from "foldkit/update";
|
|
2
|
+
import type { UpdateContext } from "./dialog.ts";
|
|
2
3
|
type ChildReturn<ChildModel, ChildMessage, ChildOutMessage> = Update.ReturnWithOutMessage<ChildModel, ChildMessage, ChildOutMessage>;
|
|
3
4
|
/** A dialog a parent holds: its update, and the steps that open and close it (`@foldkit/ui`'s
|
|
4
5
|
* Dialog, or pixel-paper's). */
|
|
5
6
|
type DialogChild<ChildModel, ChildMessage, ChildOutMessage> = Readonly<{
|
|
6
|
-
update: (model: ChildModel, message: ChildMessage) => ChildReturn<ChildModel, ChildMessage, ChildOutMessage>;
|
|
7
|
+
update: (model: ChildModel, message: ChildMessage, context: UpdateContext) => ChildReturn<ChildModel, ChildMessage, ChildOutMessage>;
|
|
7
8
|
open: (model: ChildModel) => ChildReturn<ChildModel, ChildMessage, ChildOutMessage>;
|
|
8
9
|
close: (model: ChildModel) => ChildReturn<ChildModel, ChildMessage, ChildOutMessage>;
|
|
9
10
|
}>;
|
|
@@ -16,10 +17,17 @@ type DialogFoldConfig<ParentModel, ParentMessage, ParentOutMessage, ChildModel,
|
|
|
16
17
|
* veil, a close button). Without it the dialog's OutMessages report nothing: a parent
|
|
17
18
|
* that reads `isOpen` when it needs to. */
|
|
18
19
|
toParentOutMessage?: (outMessage: ChildOutMessage) => ParentOutMessage | undefined;
|
|
20
|
+
/** Whether the dialog's ways out are held, read from the parent's own state as each
|
|
21
|
+
* Message arrives (`(model) => model.isSaving`). Free when not given. See
|
|
22
|
+
* `Dialog.UpdateContext`. */
|
|
23
|
+
isCloseHeld?: (model: ParentModel) => boolean;
|
|
19
24
|
}>;
|
|
20
25
|
/** The dialog folded into its parent: a dialog Message, and opening and closing it. */
|
|
21
26
|
type DialogFold<ParentModel, ParentMessage, ParentOutMessage, ChildMessage> = Readonly<{
|
|
22
27
|
update: (model: ParentModel, message: ChildMessage) => Update.ReturnWithOutMessage<ParentModel, ParentMessage, ParentOutMessage>;
|
|
28
|
+
/** The update a parent that is itself a dialog (ConfirmDialog) runs with the hold its own
|
|
29
|
+
* parent gave it. */
|
|
30
|
+
updateWith: (context: UpdateContext) => (model: ParentModel, message: ChildMessage) => Update.ReturnWithOutMessage<ParentModel, ParentMessage, ParentOutMessage>;
|
|
23
31
|
open: (model: ParentModel) => Update.Return<ParentModel, ParentMessage>;
|
|
24
32
|
close: (model: ParentModel) => Update.Return<ParentModel, ParentMessage>;
|
|
25
33
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../../../src/dialog/fold.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../../../src/dialog/fold.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAEzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,KAAK,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,eAAe,IAAI,MAAM,CAAC,oBAAoB,CACvF,UAAU,EACV,YAAY,EACZ,eAAe,CAChB,CAAC;AAEF;iCACiC;AACjC,KAAK,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,eAAe,IAAI,QAAQ,CAAC;IACrE,MAAM,EAAE,CACN,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,aAAa,KACnB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;IAC5D,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;IACpF,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;CACtF,CAAC,CAAC;AAEH,gGAAgG;AAChG,KAAK,gBAAgB,CACnB,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,eAAe,IACb,QAAQ,CAAC;IACX,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,UAAU,CAAC;IACzC,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,KAAK,WAAW,CAAC;IACnE,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,aAAa,CAAC;IAC1D;;gDAE4C;IAC5C,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,gBAAgB,GAAG,SAAS,CAAC;IACnF;;kCAE8B;IAC9B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC;CAC/C,CAAC,CAAC;AAEH,uFAAuF;AACvF,KAAK,UAAU,CAAC,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,IAAI,QAAQ,CAAC;IACrF,MAAM,EAAE,CACN,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,YAAY,KAClB,MAAM,CAAC,oBAAoB,CAAC,WAAW,EAAE,aAAa,EAAE,gBAAgB,CAAC,CAAC;IAC/E;0BACsB;IACtB,UAAU,EAAE,CACV,OAAO,EAAE,aAAa,KACnB,CACH,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,YAAY,KAClB,MAAM,CAAC,oBAAoB,CAAC,WAAW,EAAE,aAAa,EAAE,gBAAgB,CAAC,CAAC;IAC/E,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IACxE,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;CAC1E,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;uFAsBuF;AACvF,eAAO,MAAM,UAAU,GACrB,WAAW,EACX,aAAa,EACb,UAAU,EACV,YAAY,EACZ,eAAe,EACf,gBAAgB,GAAG,KAAK,SAEjB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,eAAe,CAAC,UACrD,gBAAgB,CACtB,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,eAAe,CAChB,KACA,UAAU,CAAC,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,CAyBvE,CAAC"}
|
package/dist/src/dialog/fold.js
CHANGED
|
@@ -34,12 +34,16 @@ export const foldDialog = (child, config) => {
|
|
|
34
34
|
// `RequestedOpen`, which reports `Opened`), and so can its update, so each fold needs a
|
|
35
35
|
// step for an OutMessage. This one keeps the parent's Model: a step reports nothing.
|
|
36
36
|
const foldOutMessage = () => __foldkitBrandViewResult(((model) => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/dialog/fold.js#anonymous"))), "@gjermundgaraba-designs/pixel-paper/src/dialog/fold.js#foldOutMessage");
|
|
37
|
-
const { toParentOutMessage } = config;
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
const { toParentOutMessage, isCloseHeld = () => __foldkitBrandViewResult((false), "@gjermundgaraba-designs/pixel-paper/src/dialog/fold.js#anonymous~2") } = config;
|
|
38
|
+
const updateWith = (context) => {
|
|
39
|
+
const childUpdate = (model, message) => __foldkitBrandViewResult((child.update(model, message, context)), "@gjermundgaraba-designs/pixel-paper/src/dialog/fold.js#childUpdate");
|
|
40
|
+
return __foldkitBrandViewResult((toParentOutMessage === undefined
|
|
41
|
+
? Update.foldChild({ update: childUpdate, ...lift, foldOutMessage })
|
|
42
|
+
: Update.foldChild({ update: childUpdate, ...lift, toParentOutMessage })), "@gjermundgaraba-designs/pixel-paper/src/dialog/fold.js#updateWith");
|
|
43
|
+
};
|
|
41
44
|
return __foldkitBrandViewResult(({
|
|
42
|
-
update,
|
|
45
|
+
update: (model, message) => __foldkitBrandViewResult((updateWith({ isCloseHeld: isCloseHeld(model) })(model, message)), "@gjermundgaraba-designs/pixel-paper/src/dialog/fold.js#update"),
|
|
46
|
+
updateWith,
|
|
43
47
|
open: Update.foldChildStep({ update: child.open, ...lift, foldOutMessage }),
|
|
44
48
|
close: Update.foldChildStep({ update: child.close, ...lift, foldOutMessage }),
|
|
45
49
|
}), "@gjermundgaraba-designs/pixel-paper/src/dialog/fold.js#foldDialog");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { BindActionKeys, Message, Model, OutMessage, close, id, init,
|
|
2
|
-
export type { InitConfig, Placement, RenderInfo, ViewInputs } from "./dialog.ts";
|
|
1
|
+
export { BindActionKeys, Message, Model, OutMessage, close, id, init, isOpen, open, sceneMounts, update, view, } from "./dialog.ts";
|
|
2
|
+
export type { InitConfig, Placement, RenderInfo, UpdateContext, ViewInputs } from "./dialog.ts";
|
|
3
3
|
export { foldDialog } from "./fold.ts";
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,OAAO,EACP,KAAK,EACL,UAAU,EACV,KAAK,EACL,EAAE,EACF,IAAI,EACJ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,OAAO,EACP,KAAK,EACL,UAAU,EACV,KAAK,EACL,EAAE,EACF,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,WAAW,EACX,MAAM,EACN,IAAI,GACL,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAChG,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/src/dialog/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { BindActionKeys, Message, Model, OutMessage, close, id, init,
|
|
1
|
+
export { BindActionKeys, Message, Model, OutMessage, close, id, init, isOpen, open, sceneMounts, update, view, } from "./dialog.js";
|
|
2
2
|
export { foldDialog } from "./fold.js";
|
|
@@ -20,7 +20,6 @@ export declare const Model: Schema.Struct<{
|
|
|
20
20
|
}>;
|
|
21
21
|
readonly maybeFocusSelector: Schema.Option<Schema.String>;
|
|
22
22
|
}>;
|
|
23
|
-
readonly isCloseHeld: Schema.Boolean;
|
|
24
23
|
}>;
|
|
25
24
|
readonly query: Schema.String;
|
|
26
25
|
}>;
|
|
@@ -106,7 +105,6 @@ export declare const update: (model: Model, message: Message) => Readonly<{
|
|
|
106
105
|
};
|
|
107
106
|
readonly maybeFocusSelector: import("effect/Option").Option<string>;
|
|
108
107
|
};
|
|
109
|
-
readonly isCloseHeld: boolean;
|
|
110
108
|
};
|
|
111
109
|
readonly query: string;
|
|
112
110
|
};
|
|
@@ -184,7 +182,6 @@ export declare const subscriptions: {
|
|
|
184
182
|
};
|
|
185
183
|
readonly maybeFocusSelector: import("effect/Option").Option<string>;
|
|
186
184
|
};
|
|
187
|
-
readonly isCloseHeld: boolean;
|
|
188
185
|
};
|
|
189
186
|
readonly query: string;
|
|
190
187
|
}, {
|
|
@@ -271,7 +268,6 @@ export declare const view: import("foldkit/submodel").View<{
|
|
|
271
268
|
};
|
|
272
269
|
readonly maybeFocusSelector: import("effect/Option").Option<string>;
|
|
273
270
|
};
|
|
274
|
-
readonly isCloseHeld: boolean;
|
|
275
271
|
};
|
|
276
272
|
readonly query: string;
|
|
277
273
|
}, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyHelp.d.ts","sourceRoot":"","sources":["../../../src/keyHelp/keyHelp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,IAAI,EAAe,MAAM,cAAc,CAAC;AAItD,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAOzC,OAAO,EAAE,KAAK,WAAW,EAAY,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,MAAM,MAAM,qBAAqB,CAAC;AAI9C,2EAA2E;AAC3E,eAAO,MAAM,KAAK
|
|
1
|
+
{"version":3,"file":"keyHelp.d.ts","sourceRoot":"","sources":["../../../src/keyHelp/keyHelp.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/card/card.ts
CHANGED
|
@@ -90,10 +90,11 @@ export const view = <Message>(config: ViewConfig<Message>, h: HtmlBuilder<Messag
|
|
|
90
90
|
// FORM
|
|
91
91
|
|
|
92
92
|
/** Configuration for rendering a form card with `formView`: a card's, but for its body, its
|
|
93
|
-
* close attributes and its
|
|
93
|
+
* close attributes and its actions, which the form fills. `lead` stays the card's: what
|
|
94
|
+
* goes before the push, such as a form's own delete. */
|
|
94
95
|
export type FormViewConfig<Message> = Omit<
|
|
95
96
|
ViewConfig<Message>,
|
|
96
|
-
"body" | "closeAttributes" | "actions"
|
|
97
|
+
"body" | "closeAttributes" | "actions"
|
|
97
98
|
> &
|
|
98
99
|
Readonly<{
|
|
99
100
|
/** Unique in the document: the submit button's id is `submitId(id)`. */
|
|
@@ -101,6 +102,10 @@ export type FormViewConfig<Message> = Omit<
|
|
|
101
102
|
/** The form's content, fields and all, in one Fields group that `isBusy` holds. A link in
|
|
102
103
|
* it (an ErrorSummary's) stays live. */
|
|
103
104
|
body: ReadonlyArray<Html | string>;
|
|
105
|
+
/** Above the fields, outside the group `isBusy` holds: what the page says about the form
|
|
106
|
+
* while it is open (a lost connection with its retry), whose controls stay live while it
|
|
107
|
+
* sends. */
|
|
108
|
+
notices?: ReadonlyArray<Html | string>;
|
|
104
109
|
/** The submit button's words. It prints primary, the card's default. */
|
|
105
110
|
submit: string;
|
|
106
111
|
/** The cancel button's words, before the submit. It closes through `closeAttributes`. */
|
|
@@ -110,9 +115,10 @@ export type FormViewConfig<Message> = Omit<
|
|
|
110
115
|
closeAttributes?: Attributes<Message>;
|
|
111
116
|
/** The form's work is running (it sends): the submit button is busy, the close glyph and
|
|
112
117
|
* cancel are held without `closeAttributes`, and the body is natively disabled, so
|
|
113
|
-
* nothing changes under the send and nothing sends twice. In a Dialog,
|
|
114
|
-
*
|
|
115
|
-
* so the update still ignores `onSubmit` while
|
|
118
|
+
* nothing changes under the send and nothing sends twice. In a Dialog, pass the same state
|
|
119
|
+
* as `isCloseHeld` to its update, so Escape and the veil are held too. Enter in a field
|
|
120
|
+
* can submit before the fields are held, so the update still ignores `onSubmit` while
|
|
121
|
+
* busy. */
|
|
116
122
|
isBusy?: boolean;
|
|
117
123
|
/** The form cannot be sent yet (nothing to send): the submit button is held as a Button
|
|
118
124
|
* is, and the fields and cancel stay live. Enter in a field can still submit, so the
|
|
@@ -127,8 +133,8 @@ export type FormViewConfig<Message> = Omit<
|
|
|
127
133
|
* `Focus.Focus({ id: Card.submitId(id) })`. */
|
|
128
134
|
export const submitId = (id: string): string => `${id}-submit`;
|
|
129
135
|
|
|
130
|
-
/** A card that is a form, named by its title, so a form landmark: its body in
|
|
131
|
-
* group, then cancel and the primary submit button in the action bar. */
|
|
136
|
+
/** A card that is a form, named by its title, so a form landmark: its notices, its body in
|
|
137
|
+
* one Fields group, then cancel and the primary submit button in the action bar. */
|
|
132
138
|
export const formView = <Message>(
|
|
133
139
|
config: FormViewConfig<Message>,
|
|
134
140
|
h: HtmlBuilder<Message>,
|
|
@@ -139,6 +145,7 @@ export const formView = <Message>(
|
|
|
139
145
|
submit,
|
|
140
146
|
cancel,
|
|
141
147
|
closeAttributes,
|
|
148
|
+
notices = [],
|
|
142
149
|
isBusy = false,
|
|
143
150
|
isSubmitDisabled = false,
|
|
144
151
|
onSubmit,
|
|
@@ -181,7 +188,7 @@ export const formView = <Message>(
|
|
|
181
188
|
childrenView(
|
|
182
189
|
{
|
|
183
190
|
...config,
|
|
184
|
-
body: [Fields.view({ body: config.body, isBusy }, h)],
|
|
191
|
+
body: [...notices, Fields.view({ body: config.body, isBusy }, h)],
|
|
185
192
|
isCloseHeld: isBusy,
|
|
186
193
|
actions: [...cancelButton, submitButton],
|
|
187
194
|
},
|
|
@@ -34,7 +34,7 @@ export type Message = typeof Message.Type;
|
|
|
34
34
|
/** Union of OutMessages a confirm dialog reports. `Confirmed`: the reader pressed the action;
|
|
35
35
|
* the dialog stays open until the parent calls `close`. `Cancelled`: cancel, Escape or a click
|
|
36
36
|
* on the veil; the dialog has closed, and whether that also stops work already running is
|
|
37
|
-
* the parent's to decide. Never reported while its ways out are held (`
|
|
37
|
+
* the parent's to decide. Never reported while its ways out are held (`Dialog.UpdateContext`). */
|
|
38
38
|
export const OutMessage = defineMessageUnion({
|
|
39
39
|
Confirmed: {},
|
|
40
40
|
Cancelled: {},
|
|
@@ -65,13 +65,17 @@ const dialogFold = foldDialog(Dialog, {
|
|
|
65
65
|
toParentOutMessage: toCancelled,
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
const FREE: Dialog.UpdateContext = { isCloseHeld: false };
|
|
69
|
+
|
|
70
|
+
/** Processes a confirm dialog Message and returns the next Model, Commands and OutMessage.
|
|
71
|
+
* While `context.isCloseHeld`, cancel, Escape and the veil change nothing. */
|
|
72
|
+
export const update = (model: Model, message: Message, context: Dialog.UpdateContext = FREE) =>
|
|
70
73
|
Message.match<Update.ReturnWithOutMessage<Model, Message, OutMessage>>(message, {
|
|
71
|
-
GotDialogMessage: ({ message: dialogMessage }) =>
|
|
74
|
+
GotDialogMessage: ({ message: dialogMessage }) =>
|
|
75
|
+
dialogFold.updateWith(context)(model, dialogMessage),
|
|
72
76
|
ClickedConfirm: () => ({ model, outMessage: OutMessage.Confirmed() }),
|
|
73
77
|
ClickedCancel: () =>
|
|
74
|
-
|
|
78
|
+
context.isCloseHeld
|
|
75
79
|
? { model }
|
|
76
80
|
: pipe(dialogFold.close(model), Update.withOutMessage(OutMessage.Cancelled())),
|
|
77
81
|
});
|
|
@@ -79,17 +83,10 @@ export const update = (model: Model, message: Message) =>
|
|
|
79
83
|
/** Opens the confirm dialog: focus starts on the action, or on cancel when it is `isDanger`. */
|
|
80
84
|
export const open = (model: Model): Update.Return<Model, Message> => dialogFold.open(model);
|
|
81
85
|
|
|
82
|
-
/** Closes the confirm dialog without reporting `Cancelled
|
|
83
|
-
*
|
|
86
|
+
/** Closes the confirm dialog without reporting `Cancelled`: call it once confirmed work has
|
|
87
|
+
* landed. */
|
|
84
88
|
export const close = (model: Model): Update.Return<Model, Message> => dialogFold.close(model);
|
|
85
89
|
|
|
86
|
-
/** Holds cancel, Escape and the veil, or lets them go: while held the dialog stays open and
|
|
87
|
-
* reports no `Cancelled`, and cancel prints held (`aria-disabled`, in the keyboard). Hold it
|
|
88
|
-
* in the update that starts confirmed work whose failure must land here, and let go in the
|
|
89
|
-
* update where the work fails; `close` lets go once it lands. */
|
|
90
|
-
export const setCloseHeld = (model: Model, isHeld: boolean): Model =>
|
|
91
|
-
modifyFields(model, { dialog: (dialog) => Dialog.setCloseHeld(dialog, isHeld) });
|
|
92
|
-
|
|
93
90
|
// ACCESSORS
|
|
94
91
|
|
|
95
92
|
/** The confirm dialog's id, unique in the document. */
|
|
@@ -98,9 +95,6 @@ export const id = (model: Model): string => Dialog.id(model.dialog);
|
|
|
98
95
|
/** Whether the confirm dialog is open. */
|
|
99
96
|
export const isOpen = (model: Model): boolean => Dialog.isOpen(model.dialog);
|
|
100
97
|
|
|
101
|
-
/** Whether its ways out are held (`setCloseHeld`). */
|
|
102
|
-
export const isCloseHeld = (model: Model): boolean => Dialog.isCloseHeld(model.dialog);
|
|
103
|
-
|
|
104
98
|
// VIEW
|
|
105
99
|
|
|
106
100
|
/** Per-render inputs passed to `view` through `h.submodel`'s `viewInputs`. */
|
|
@@ -122,8 +116,9 @@ export type ViewInputs = Readonly<{
|
|
|
122
116
|
/** Destructive: the confirming button turns red and cancel becomes the default. */
|
|
123
117
|
isDanger?: boolean;
|
|
124
118
|
/** The confirmed work is running: the confirming button is held (its word stays, the
|
|
125
|
-
* working cursor prints, a second press does nothing) and keeps focus
|
|
126
|
-
*
|
|
119
|
+
* working cursor prints, a second press does nothing) and keeps focus, and cancel is held
|
|
120
|
+
* with it. Pair it with `isCloseHeld` in `update`'s context, from the same state, so
|
|
121
|
+
* Escape and the veil change nothing either. */
|
|
127
122
|
isBusy?: boolean;
|
|
128
123
|
/** The action cannot run now (nothing is running; it is blocked): the confirming button is
|
|
129
124
|
* held, `aria-disabled` without the working cursor, and stays in the keyboard, so focus
|
|
@@ -135,7 +130,6 @@ export type ViewInputs = Readonly<{
|
|
|
135
130
|
}>;
|
|
136
131
|
|
|
137
132
|
const cardView = (
|
|
138
|
-
model: Model,
|
|
139
133
|
viewInputs: ViewInputs,
|
|
140
134
|
render: Dialog.RenderInfo,
|
|
141
135
|
h: HtmlBuilder<Message>,
|
|
@@ -158,7 +152,7 @@ const cardView = (
|
|
|
158
152
|
{
|
|
159
153
|
label: "cancel",
|
|
160
154
|
isDefault: isDanger,
|
|
161
|
-
isDisabled:
|
|
155
|
+
isDisabled: isBusy,
|
|
162
156
|
onClick: Message.ClickedCancel(),
|
|
163
157
|
attributes: isDanger ? render.initialFocus : [],
|
|
164
158
|
},
|
|
@@ -198,9 +192,9 @@ const cardView = (
|
|
|
198
192
|
* arm-then-confirm button: red never rests on a button, and an armed button is easy to
|
|
199
193
|
* double-click through.
|
|
200
194
|
*
|
|
201
|
-
* Work that takes a while stays in the dialog: set `isBusy` while it runs
|
|
202
|
-
* has landed, and on failure keep it open with the
|
|
203
|
-
*
|
|
195
|
+
* Work that takes a while stays in the dialog: set `isBusy` while it runs (and `isCloseHeld`
|
|
196
|
+
* in `update`'s context), `close` once it has landed, and on failure keep it open with the
|
|
197
|
+
* error in `toDetailView`.
|
|
204
198
|
*/
|
|
205
199
|
export const view = defineView<Model, Message, ViewInputs>((model, viewInputs, h): Html =>
|
|
206
200
|
h.submodel({
|
|
@@ -211,7 +205,7 @@ export const view = defineView<Model, Message, ViewInputs>((model, viewInputs, h
|
|
|
211
205
|
isAlert: viewInputs.isDanger ?? false,
|
|
212
206
|
hasDescription: true,
|
|
213
207
|
toLiveRegionView: viewInputs.toLiveRegionView,
|
|
214
|
-
toView: (render) => cardView(
|
|
208
|
+
toView: (render) => cardView(viewInputs, render, h),
|
|
215
209
|
},
|
|
216
210
|
toParentMessage: (message) => Message.GotDialogMessage({ message }),
|
|
217
211
|
}),
|
package/src/dialog/dialog.ts
CHANGED
|
@@ -13,11 +13,9 @@ import { foldDialog } from "./fold.ts";
|
|
|
13
13
|
|
|
14
14
|
// MODEL
|
|
15
15
|
|
|
16
|
-
/** Schema for a pixel-paper dialog: the `@foldkit/ui` Dialog it is built on
|
|
17
|
-
* ways out are held (`setCloseHeld`). */
|
|
16
|
+
/** Schema for a pixel-paper dialog: the `@foldkit/ui` Dialog it is built on. */
|
|
18
17
|
export const Model = Schema.Struct({
|
|
19
18
|
dialog: UiDialog.Model,
|
|
20
|
-
isCloseHeld: Schema.Boolean,
|
|
21
19
|
});
|
|
22
20
|
export type Model = typeof Model.Type;
|
|
23
21
|
|
|
@@ -56,7 +54,6 @@ export type InitConfig = Readonly<{ id: string }>;
|
|
|
56
54
|
/** Creates a closed dialog. */
|
|
57
55
|
export const init = (config: InitConfig): Model => ({
|
|
58
56
|
dialog: UiDialog.init({ id: config.id }),
|
|
59
|
-
isCloseHeld: false,
|
|
60
57
|
});
|
|
61
58
|
|
|
62
59
|
// ACCESSORS
|
|
@@ -67,9 +64,6 @@ export const id = (model: Model): string => model.dialog.id;
|
|
|
67
64
|
/** Whether the dialog is open. */
|
|
68
65
|
export const isOpen = (model: Model): boolean => model.dialog.isOpen;
|
|
69
66
|
|
|
70
|
-
/** Whether the dialog's ways out are held (`setCloseHeld`). */
|
|
71
|
-
export const isCloseHeld = (model: Model): boolean => model.isCloseHeld;
|
|
72
|
-
|
|
73
67
|
// UPDATE
|
|
74
68
|
|
|
75
69
|
const toOutMessage = UiDialog.OutMessage.match<OutMessage | undefined>({
|
|
@@ -85,17 +79,29 @@ const dialogFold = foldDialog(UiDialog, {
|
|
|
85
79
|
toParentOutMessage: toOutMessage,
|
|
86
80
|
});
|
|
87
81
|
|
|
82
|
+
/** What a dialog's update reads from its parent as a Message arrives. */
|
|
83
|
+
export type UpdateContext = Readonly<{
|
|
84
|
+
/** The reader may not walk away from the work running now (a save whose failure must land
|
|
85
|
+
* in this dialog): Escape, a press on the veil and a control given the `closeButton` group
|
|
86
|
+
* change nothing and report no `Closed`. Read it from the parent's own state as the
|
|
87
|
+
* Message arrives (`isSaving(model)`), so it holds exactly while the work runs, however
|
|
88
|
+
* soon after the start a key comes. */
|
|
89
|
+
isCloseHeld: boolean;
|
|
90
|
+
}>;
|
|
91
|
+
|
|
92
|
+
const FREE: UpdateContext = { isCloseHeld: false };
|
|
93
|
+
|
|
88
94
|
// NOTE: @foldkit/ui gap: Dialog has no way to hold a close. Escape, a press on the veil and a
|
|
89
95
|
// control given the `closeButton` group all send `RequestedClose`, so a held dialog reads it
|
|
90
96
|
// by its tag and leaves it out.
|
|
91
|
-
const isHeldClose = (
|
|
92
|
-
|
|
97
|
+
const isHeldClose = (context: UpdateContext, message: UiDialog.Message): boolean =>
|
|
98
|
+
context.isCloseHeld && message._tag === "RequestedClose";
|
|
93
99
|
|
|
94
100
|
/** Processes a dialog Message and returns the next Model, Commands and OutMessage. */
|
|
95
|
-
export const update = (model: Model, message: Message) =>
|
|
101
|
+
export const update = (model: Model, message: Message, context: UpdateContext = FREE) =>
|
|
96
102
|
Message.match<Update.ReturnWithOutMessage<Model, Message, OutMessage>>(message, {
|
|
97
103
|
GotDialogMessage: ({ message: dialogMessage }) =>
|
|
98
|
-
isHeldClose(
|
|
104
|
+
isHeldClose(context, dialogMessage) ? { model } : dialogFold.update(model, dialogMessage),
|
|
99
105
|
PressedActionKey: () => ({ model }),
|
|
100
106
|
});
|
|
101
107
|
|
|
@@ -103,18 +109,8 @@ export const update = (model: Model, message: Message) =>
|
|
|
103
109
|
* first control, and returns to where it was when the dialog closes. */
|
|
104
110
|
export const open = (model: Model): Update.Return<Model, Message> => dialogFold.open(model);
|
|
105
111
|
|
|
106
|
-
/** Closes the dialog without reporting `Closed
|
|
107
|
-
export const close = (model: Model): Update.Return<Model, Message> =>
|
|
108
|
-
dialogFold.close(modifyFields(model, { isCloseHeld: () => false }));
|
|
109
|
-
|
|
110
|
-
/** Holds the dialog's ways out, or lets them go: while held, Escape, a press on the veil and
|
|
111
|
-
* a control given the `closeButton` group leave it open and report no `Closed`, so the
|
|
112
|
-
* result of work the reader may not walk away from (a save whose failure must land here)
|
|
113
|
-
* lands in it. Hold it in the update that starts the work, and let go in the update where
|
|
114
|
-
* the work fails, or the reader is shut in with the failure; `close` lets go once it lands.
|
|
115
|
-
* Hold the card's own close controls too (`Card.formView`'s `isBusy`). */
|
|
116
|
-
export const setCloseHeld = (model: Model, isHeld: boolean): Model =>
|
|
117
|
-
modifyFields(model, { isCloseHeld: () => isHeld });
|
|
112
|
+
/** Closes the dialog without reporting `Closed`. */
|
|
113
|
+
export const close = (model: Model): Update.Return<Model, Message> => dialogFold.close(model);
|
|
118
114
|
|
|
119
115
|
// ACTION BAR KEYS
|
|
120
116
|
|
package/src/dialog/fold.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Option } from "effect";
|
|
2
2
|
import * as Update from "foldkit/update";
|
|
3
3
|
|
|
4
|
+
import type { UpdateContext } from "./dialog.ts";
|
|
5
|
+
|
|
4
6
|
// FOLD
|
|
5
7
|
|
|
6
8
|
type ChildReturn<ChildModel, ChildMessage, ChildOutMessage> = Update.ReturnWithOutMessage<
|
|
@@ -15,6 +17,7 @@ type DialogChild<ChildModel, ChildMessage, ChildOutMessage> = Readonly<{
|
|
|
15
17
|
update: (
|
|
16
18
|
model: ChildModel,
|
|
17
19
|
message: ChildMessage,
|
|
20
|
+
context: UpdateContext,
|
|
18
21
|
) => ChildReturn<ChildModel, ChildMessage, ChildOutMessage>;
|
|
19
22
|
open: (model: ChildModel) => ChildReturn<ChildModel, ChildMessage, ChildOutMessage>;
|
|
20
23
|
close: (model: ChildModel) => ChildReturn<ChildModel, ChildMessage, ChildOutMessage>;
|
|
@@ -36,6 +39,10 @@ type DialogFoldConfig<
|
|
|
36
39
|
* veil, a close button). Without it the dialog's OutMessages report nothing: a parent
|
|
37
40
|
* that reads `isOpen` when it needs to. */
|
|
38
41
|
toParentOutMessage?: (outMessage: ChildOutMessage) => ParentOutMessage | undefined;
|
|
42
|
+
/** Whether the dialog's ways out are held, read from the parent's own state as each
|
|
43
|
+
* Message arrives (`(model) => model.isSaving`). Free when not given. See
|
|
44
|
+
* `Dialog.UpdateContext`. */
|
|
45
|
+
isCloseHeld?: (model: ParentModel) => boolean;
|
|
39
46
|
}>;
|
|
40
47
|
|
|
41
48
|
/** The dialog folded into its parent: a dialog Message, and opening and closing it. */
|
|
@@ -44,6 +51,14 @@ type DialogFold<ParentModel, ParentMessage, ParentOutMessage, ChildMessage> = Re
|
|
|
44
51
|
model: ParentModel,
|
|
45
52
|
message: ChildMessage,
|
|
46
53
|
) => Update.ReturnWithOutMessage<ParentModel, ParentMessage, ParentOutMessage>;
|
|
54
|
+
/** The update a parent that is itself a dialog (ConfirmDialog) runs with the hold its own
|
|
55
|
+
* parent gave it. */
|
|
56
|
+
updateWith: (
|
|
57
|
+
context: UpdateContext,
|
|
58
|
+
) => (
|
|
59
|
+
model: ParentModel,
|
|
60
|
+
message: ChildMessage,
|
|
61
|
+
) => Update.ReturnWithOutMessage<ParentModel, ParentMessage, ParentOutMessage>;
|
|
47
62
|
open: (model: ParentModel) => Update.Return<ParentModel, ParentMessage>;
|
|
48
63
|
close: (model: ParentModel) => Update.Return<ParentModel, ParentMessage>;
|
|
49
64
|
}>;
|
|
@@ -98,14 +113,18 @@ export const foldDialog = <
|
|
|
98
113
|
// `RequestedOpen`, which reports `Opened`), and so can its update, so each fold needs a
|
|
99
114
|
// step for an OutMessage. This one keeps the parent's Model: a step reports nothing.
|
|
100
115
|
const foldOutMessage = () => (model: ParentModel) => ({ model });
|
|
101
|
-
const { toParentOutMessage } = config;
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
116
|
+
const { toParentOutMessage, isCloseHeld = () => false } = config;
|
|
117
|
+
const updateWith = (context: UpdateContext) => {
|
|
118
|
+
const childUpdate = (model: ChildModel, message: ChildMessage) =>
|
|
119
|
+
child.update(model, message, context);
|
|
120
|
+
return toParentOutMessage === undefined
|
|
121
|
+
? Update.foldChild({ update: childUpdate, ...lift, foldOutMessage })
|
|
122
|
+
: Update.foldChild({ update: childUpdate, ...lift, toParentOutMessage });
|
|
123
|
+
};
|
|
106
124
|
|
|
107
125
|
return {
|
|
108
|
-
update,
|
|
126
|
+
update: (model, message) => updateWith({ isCloseHeld: isCloseHeld(model) })(model, message),
|
|
127
|
+
updateWith,
|
|
109
128
|
open: Update.foldChildStep({ update: child.open, ...lift, foldOutMessage }),
|
|
110
129
|
close: Update.foldChildStep({ update: child.close, ...lift, foldOutMessage }),
|
|
111
130
|
};
|
package/src/dialog/index.ts
CHANGED
|
@@ -6,13 +6,11 @@ export {
|
|
|
6
6
|
close,
|
|
7
7
|
id,
|
|
8
8
|
init,
|
|
9
|
-
isCloseHeld,
|
|
10
9
|
isOpen,
|
|
11
10
|
open,
|
|
12
11
|
sceneMounts,
|
|
13
|
-
setCloseHeld,
|
|
14
12
|
update,
|
|
15
13
|
view,
|
|
16
14
|
} from "./dialog.ts";
|
|
17
|
-
export type { InitConfig, Placement, RenderInfo, ViewInputs } from "./dialog.ts";
|
|
15
|
+
export type { InitConfig, Placement, RenderInfo, UpdateContext, ViewInputs } from "./dialog.ts";
|
|
18
16
|
export { foldDialog } from "./fold.ts";
|