@gjermundgaraba-designs/pixel-paper 0.18.0 → 0.20.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 +26 -5
- package/dist/src/commandPalette/commandPalette.d.ts +2 -2
- package/dist/src/commandPalette/commandPalette.d.ts.map +1 -1
- package/dist/src/commandPalette/commandPalette.js +66 -42
- package/dist/src/commandPalette/match.d.ts +15 -5
- package/dist/src/commandPalette/match.d.ts.map +1 -1
- package/dist/src/commandPalette/match.js +17 -2
- package/dist/src/drag/drag.d.ts +3 -0
- package/dist/src/drag/drag.d.ts.map +1 -1
- package/dist/src/drag/drag.js +14 -10
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/reel/index.d.ts +4 -0
- package/dist/src/reel/index.d.ts.map +1 -0
- package/dist/src/reel/index.js +2 -0
- package/dist/src/reel/layout.d.ts +180 -0
- package/dist/src/reel/layout.d.ts.map +1 -0
- package/dist/src/reel/layout.js +370 -0
- package/dist/src/reel/reel.d.ts +1158 -0
- package/dist/src/reel/reel.d.ts.map +1 -0
- package/dist/src/reel/reel.js +922 -0
- package/dist/src/sortable/sortable.d.ts +30 -30
- package/dist/src/sortable/sortable.d.ts.map +1 -1
- package/dist/src/sortable/sortable.js +2 -10
- package/dist/src/toast/toast.d.ts +12 -12
- package/dist/src/window/window.d.ts +2 -0
- package/dist/src/window/window.d.ts.map +1 -1
- package/dist/src/window/window.js +2 -1
- package/package.json +1 -1
- package/src/commandPalette/commandPalette.ts +71 -24
- package/src/commandPalette/match.ts +35 -6
- package/src/drag/drag.ts +19 -4
- package/src/index.ts +1 -0
- package/src/pixel-paper.css +236 -2
- package/src/reel/index.ts +20 -0
- package/src/reel/layout.ts +602 -0
- package/src/reel/reel.ts +1597 -0
- package/src/sortable/sortable.ts +8 -16
- package/src/tokens.css +5 -0
- package/src/window/window.ts +4 -0
package/DESIGN.md
CHANGED
|
@@ -212,6 +212,9 @@ spacing:
|
|
|
212
212
|
row-min: "34px"
|
|
213
213
|
reading-measure: "68ch"
|
|
214
214
|
chart-plot: "160px"
|
|
215
|
+
reel-gutter: "16px"
|
|
216
|
+
reel-column-min: "280px"
|
|
217
|
+
reel-pane-min: "120px"
|
|
215
218
|
components:
|
|
216
219
|
button:
|
|
217
220
|
backgroundColor: "{colors.paper}"
|
|
@@ -914,6 +917,8 @@ The Deck is one centred column, `max-width: 1160px` (`--deck`), padded 24px top,
|
|
|
914
917
|
|
|
915
918
|
**Toasts:** the stack sits 16px in from the bottom right, `min(360px, 100vw - 32px)` wide, newest lowest, 10px apart, standing 12px clear of a showing Banner (anchored to it); on a phone it spans the bottom. While the system's Dialog is open the stack moves inside it, so a toast raised from a card prints above the card and can be used; a raw `<dialog>` an adopter opens with `showModal()` is not followed.
|
|
916
919
|
|
|
920
|
+
**Reel:** a row of columns of windows that fills whatever holds it and scrolls sideways when the columns are wider than it, and down where a column's panes need more than its height, `--reel-gutter` (16px) between columns, between a column's panes and at either end. A column's width is a share of the reel's (`width × (view − gutter) − gutter`, so columns whose shares add up to one fill it exactly), never under `--reel-column-min` (280px); a column is the reel's height and a pane's height its share of its column by weight, never under `--reel-pane-min` (120px), a column of more panes than fit growing taller. The shares a width steps through (a third, half, two thirds, the whole) are the component's, in its code, not tokens: they are the words it says as much as the sizes it prints.
|
|
921
|
+
|
|
917
922
|
**Rail:** a column of panels beside the deck (`--rail`, 272px, then the deck centred in what is left), fixed from the top bar's lower edge (hung from it by anchor positioning, like the stage) to the window's bottom, scrolling itself, padded `24px 10px 24px 20px` so its panels' shadows print. At 900px and under it leaves the page: the deck starts with a small button (the `sidebar` glyph and the rail's name) that opens the same element as a drawer.
|
|
918
923
|
|
|
919
924
|
Frames with no surface of their own (Deck, Wall, Stage) are described here rather than among the components.
|
|
@@ -926,7 +931,7 @@ Breakpoints: 600px (phone; the page title steps to 18px, stats two across, the w
|
|
|
926
931
|
|
|
927
932
|
### Named Rules
|
|
928
933
|
|
|
929
|
-
**The Panel Scroll Rule.** The page scrolls; a panel does not, except a board column above 900px, a card body inside a dialog, the rail,
|
|
934
|
+
**The Panel Scroll Rule.** The page scrolls; a panel does not, except a board column above 900px, a card body inside a dialog, the rail, a window's body (a window is sized by the canvas, not by its content), and a reel, which scrolls sideways, and down where a column is taller than it, its windows sized by its columns. A stage never scrolls. A wide table, a long code line or a run of bar columns scrolls horizontally inside its own panel and never widens the deck.
|
|
930
935
|
|
|
931
936
|
**The 24px Target Rule.** Every target is at least 24px square. A control drawn smaller (a tab, a glyph-switch option, a filter chip, a row toggle, a clickable mark or tag, a text button, a crumb, an error-summary link) keeps its look and takes the pointer over an invisible 24px square centred on it; an icon button never gives up its 24px to a full line (a tag beside it clips first), and links in a list stand 24px apart so no two squares overlap.
|
|
932
937
|
|
|
@@ -1052,7 +1057,7 @@ The printed button: a paper rectangle in a 2px ink frame that casts a 3px shadow
|
|
|
1052
1057
|
- **Banner** → `div.banner > span.glyph (.error, .ok, .quiet) + span`: a fixed paper slip on the 3px shadow with a 26px glyph box in the Notice's tones and a sentence that says what happens to the reader's work. It holds a state the whole interface is in, for as long as it lasts, one at a time. It is not a live region (it arrives already holding its sentence, which would go unheard): the application says the state once, when it begins, with the `Announcer.Announce` Command. `"Error"` (the default; red, bang): a state that stops work (offline). `"Warn"` (amber): a state that is passing (reconnecting). `"Ok"` (ink, check): the state just ended (back online), shown briefly and then taken away. `"Quiet"` (head, info): news. With the connection word it is one pair: LinkState `"Reconnecting"` beside a warn Banner, `"Offline"` beside an error Banner with a retry, then `"Online"` beside an ok Banner for a moment; `"Connecting"` takes none.
|
|
1053
1058
|
- **Rail** (Submodel) → `div.railed > main.deck#main + aside.rail[popover="auto"][aria-label]`: the rail's panels (a tree, places, filters) beside the deck's, after it in the markup so the page's main content and its h1 come first in reading order; the stylesheet fixes the rail at the left under the top bar. One element serves both layouts, so nothing in it renders twice or loses its state: at rest it stands there (its `popover` attribute is inert until opened), and at 900px and under it is hidden and `span.rail-open > button.btn.small[aria-expanded]` at the top of the deck opens it as a drawer: a slice of the dithered ground, `min(320px, 100vw - 40px)` wide from the left edge, closed by a 2px ink rule and the 8px card shadow, entering from the left in three steps over a veil. Its head (`div.rail-head`: the name in the label register and a close IconButton) shows only in the drawer. Opening focuses its first control; Escape, a click on the veil, the close glyph or focus leaving closes it, and all but the last return focus to the button. Widening the window past 900px closes it. The parent reads whether the drawer is open with `Rail.isOpen`, to hold its own page keys while the drawer covers the page. A file tree flush in a rail panel (or any panel) drops its own border.
|
|
1054
1059
|
- **Stage** → `main.stage#main > div.stage-work > div.stage-canvas + div.stage-tools + div.stage-bar`: see Layout. `paused` (a Notice saying why: the link dropped, the page is read-only) veils the work when editing stops: `div.stage-paused[role="status"]` lays the dialog veil and its ink grain over everything and centres that Notice on the 3px button shadow, while the work beneath is `inert`. It lifts when `paused` is not given; the connection word says reconnecting meanwhile.
|
|
1055
|
-
- **Window** → `section.panel.window (.flush) > header.panel-tb + div.window-body + div.panel-foot`: a Panel for a stage, as tall and wide as whatever places it (and moved by it, if anything moves it). Its title bar is the panel's, trimmed to hold tools: 30px tall (`3px 3px 3px 10px`), centred, with an optional kind glyph, the title (clipped with an ellipsis), a status chip (its border turns paper so it reads on ink), tools and a close IconButton, both in the bar's paper. The body scrolls inside (`16px`, a 14px grid); flush, what fills it (a code surface, a file tree, a diff) drops its border and fills it. A foot is a panel foot; a notice there takes the whole line.
|
|
1060
|
+
- **Window** → `section.panel.window (.flush) > header.panel-tb + div.window-body + div.panel-foot`: a Panel for a stage or a reel, as tall and wide as whatever places it (and moved by it, if anything moves it). Its title bar is the panel's, trimmed to hold tools: 30px tall (`3px 3px 3px 10px`, its left trimmed to 3px under a `lead`), centred, with an optional `lead` (`span.panel-lead`, a quiet IconButton: a Reel's grip), an optional kind glyph, the title (clipped with an ellipsis), a status chip (its border turns paper so it reads on ink), tools and a close IconButton, both in the bar's paper. The body scrolls inside (`16px`, a 14px grid); flush, what fills it (a code surface, a file tree, a diff) drops its border and fills it. A foot is a panel foot; a notice there takes the whole line.
|
|
1056
1061
|
- **Loading** → `div.loading[role="status"]`: where content is on its way, centred like an EmptyState at `26px 16px`, a 12px uppercase 0.08em (`--tracking-title`) muted line ("loading the items") ending in the 6×11px working cursor. Its status element stays in the page and only its words change: with `isLoading` false it is `div.loading-idle[role="status"]`, empty and out of the flow (`position: absolute`, never `display: none`, which would drop the live region), so it takes no room and no gap at the top of a panel's or a card's body, and a screen reader hears the line when it arrives. The content or an EmptyState takes its place on screen; the line never prints beside either. It is not the page's live region (the Announcer's).
|
|
1057
1062
|
- **Remote** → spread where content goes: data that loads, in each of Foldkit's `AsyncData` states, with the retry built in, so a failure is never shown without its way back. A Loading line (`loading <label>`) leads in every state, in place, so its status is heard, and holds its words only while the first load runs: a refresh over data already shown is quiet, so a page that polls is not heard each time. Retry takes its button away with the notice, so the update returns focus to the line (its `id`) with the retry, where a first load is heard. A failure with nothing to show is an error Notice titled "<Label> not loaded", the reason (`toErrorText`) under it and a small "retry" button (`onRetry`); a refresh that failed over data already shown (stale) is a warn Notice titled "<Label> not refreshed" with the same reason and retry, above the last data; loaded and refreshing data is the caller's `toDataView` alone, an EmptyState for none included.
|
|
1058
1063
|
- **Disclosure** (a View over `@foldkit/ui`'s Disclosure; the caller keeps whether it is open) → `div.disclosure > button.disclosure-summary[aria-expanded] + div.disclosure-body`. The summary is a control-register line (`3px 0`) of a 12px `caret-right` glyph, `span.disclosure-label` (a dotted underline on hover) and an optional `span.meta` pushed right; open, the caret turns a quarter in one step (a right angle keeps the grid). The body hangs from the caret on a 1px Rule Grey guide, its content level with the summary's words (`margin-left: 5px`, `padding-left: 14px`), in a 14px grid. For what a reader opens on purpose (grants to edit, details, a danger zone), never to hide the one thing the screen is for.
|
|
@@ -1068,7 +1073,7 @@ The printed button: a paper rectangle in a 2px ink frame that casts a 3px shadow
|
|
|
1068
1073
|
**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.
|
|
1069
1074
|
|
|
1070
1075
|
- **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.
|
|
1071
|
-
- **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.
|
|
1076
|
+
- **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. A page can be a tree: a choice's `level` (1 at the roots) puts it under the nearest choice before it one level up and indents it a step a level (`--palette-level`, at most a third of the row); a query then keeps each choice it finds with the choices it sits under, muted (`.palette-row.context`, "(context)" for screen readers) and still pickable, so a found choice shows where it is; the status counts only the choices found. The indent is not read out, so a choice whose place matters names its path in its hint. 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, each only among the choices the query finds, 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.
|
|
1072
1077
|
- **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.
|
|
1073
1078
|
|
|
1074
1079
|
### Keys
|
|
@@ -1107,6 +1112,22 @@ One keyboard for the page, Vim first, with the arrows beside the letters. The ke
|
|
|
1107
1112
|
|
|
1108
1113
|
**Sortable** (Submodel over `@foldkit/ui`'s DragAndDrop, for items the reader puts in order: a list of rows, a grid of tiles, or a grid of the page's panels) → `div.sortable (.grid, .panels)` holding `ul.sortable-items[aria-label]` (a plain list: no listbox, so an item's content may hold anything) of `li.sortable-item (.movable, .drag-dim, .drop-before, .drop-after)`, each a grip, then `div.sortable-body` with the parent's content, and the list's keyboard help in a `p.sr-only`. `"List"` (the default) prints rows edge to edge between 1px rules, for a flush panel, `8px 16px 8px 10px`; `"Grid"` prints tiles that wrap, 180px and up, 12px apart: paper slips in a 2px ink border on the 3px print shadow. `"Panels"` prints each item as the parent's own Panel, up to three across in the deck's 24px gutter and 300px and up, so two across and then one as the column narrows; a row takes its tallest panel's height, each panel's foot sits at its bottom, and the list hands each item its grip (`render.grip`) for the panel's `lead` instead of printing it beside the item. The held copy is the whole panel, tinted head on the 8px card shadow, under ids of its own, and the drop line runs down the middle of the gutter. `isReadOnly`, for a reader who may look but not reorder, prints every grip held and lifts nothing, as holding moves does. Each item starts with a quiet `button.iconbtn` grip (in a panel's title bar, for `"Panels"`) (the grip glyph, named "Move <label>", `aria-roledescription="draggable"`, `aria-pressed`, described by the keyboard help); while moves are held it stays, held (`aria-disabled`, in the keyboard), so focus on it stays put when holding moves cancels one. Only the grip lifts an item, under a mouse, a finger or a pen (`cursor: grab` and `touch-action: none` on a movable grip only), so the rest of an item selects its text and a finger on it scrolls the page; the keyboard takes the grip too: Space or Enter picks the item up, every arrow moves it one place in reading order, Space or Enter drops it, Escape puts it back (taken before a dialog or the page's keys see it), and Tab leaves it where it is. Only the pointer that pressed moves the item; the browser cancelling it puts the item back. The held item dims to 40% in place (`.drag-dim`) and keeps its own print; a copy of it, `.lifted`, tinted head on the 8px card shadow in a 2px ink border (`aria-hidden`, `inert`, rendered with `isLifted`, so it carries no ids), follows the pointer in `div.sortable-lift`, or, held by the keyboard, prints in the list where it would land, in `li.sortable-slot`, counted as no item. Where a pointer would drop it, a 2px dashed ink line prints beside the item it lands next to (`.drop-before`, or `.drop-after` past the last), over the layout, so nothing moves under the pointer. The pointer's place is read from the item whose centre is closest, across where items sit side by side and down a column, and read again where the pointer is let go, so a page that scrolled under a still pointer drops where the pointer is. Each step is said through the page's live region in words from the item's `label` and its place, counted from one ("Beta, 3 of 4."). The items are the parent's: a drop reports `Moved`, and the parent moves the item, or decides not to, and flashes it; a drop where the item was, outside the list, or while moves are held reports nothing. While a pointer presses or carries an item the page's root carries `data-pixel-paper-dragging`, which prints the grabbing cursor everywhere and holds text selection.
|
|
1109
1114
|
|
|
1115
|
+
### Reel
|
|
1116
|
+
|
|
1117
|
+
**Reel** (Submodel, holding its own drag state rather than `@foldkit/ui`'s DragAndDrop, whose targets are positions in a list and whose pointer reads the items under it; for a workspace of windows side by side: terminals, an editor, a browser, a canvas, in columns the reader arranges) → `div.reel[role="group"][aria-label][data-reel]` holding the scroller `div.reel-view`, whose `div.reel-track` holds, in order, `div.reel-gap` (the gap before the first column), then per column `div.reel-column[data-reel-column]` (the column's id; its share in `--reel-width`) with `div.reel-column-head` (the column's grip and `span.reel-width`, its share in the state register: `1/2`, `full`), a `div.reel-slot` per pane (`--reel-weight`, `--reel-anchor`) and between two slots `div.reel-split[role="separator"][aria-orientation="horizontal"]` ("Height of <pane>", `data-reel-split` the id of the pane above it), and after each column `div.reel-gap.reel-resize[role="separator"][aria-orientation="vertical"]` ("Width of column 2", its share as `aria-valuenow` and in words as `aria-valuetext`, `data-reel-resize` its column's id), then `div.reel-panes` with a `div.reel-pane[data-reel-pane][tabindex="-1"]` per pane, each the parent's Window with the pane's grip as its `lead`; after the scroller, the grips' keyboard help in a `p.sr-only` and, while a pointer carries a pane or a column, `div.reel-lift` with its copy. The reel takes its height from whatever holds it. The layout is the parent's (`Reel.Layout`: columns, each an id, a width and its panes, each an id, a label and a weight), passed to the view and, as `{ layout }`, to `update`, so the reel never holds a copy that could go stale. Whenever the parent changes the layout (a request it applied, another client's, a pane it closed), it calls `Reel.reconcile` with the layout as it is now.
|
|
1118
|
+
|
|
1119
|
+
**The Pane Order Rule.** The panes are in the page in one order, by id, whatever their layout, and each is drawn over its slot by anchor positioning (`anchor-name` on the slot, `position-anchor` and `anchor()` insets on the pane; the track is the containing block and the pane layer is `display: contents`). A move changes where a pane is drawn and never its place in the page, so a terminal, a video or an embedded browser keeps its focus, its scroll and its state through any move, and a new pane is inserted among the others without moving them. So Tab follows ids, not columns: give panes ids that sort by when they were made (ULID, UUIDv7) and the reading order is the order they were opened; the keys below move by place.
|
|
1120
|
+
|
|
1121
|
+
**The Id Rule.** Everything the reel asks of the layout names panes and columns by id, never by position: a pane into a column at a place among its panes, a pane into a column of its own before a column (or after the last), a column before another, a column's width, its panes' weights by pane id. So a request means what it meant after the layout changed under it, applied at once, later, on a server or after another client's move, and what is held keeps its target the same way. A request naming a pane or column the layout no longer has changes nothing. A column `Reel.apply` makes for a pane pulled out takes the pane's id (`notes`, or `notes/2` where a column has it), so pane and column ids share one namespace.
|
|
1122
|
+
|
|
1123
|
+
Each pane's grip, and each column's, is a quiet `button.iconbtn[data-reel-grip]` (the grip glyph, "Move <pane>" or "Move column 2", `aria-roledescription="draggable"`, `aria-pressed`, described by the help). Only a grip lifts, under a mouse, a finger or a pen (`cursor: grab`, `touch-action: none`), so a pane's text selects and a terminal takes the pointer; it carries once the pointer has moved 5px. A held pane dims to 40% in place (`.drag-dim`); its copy, drawn by the reel from the labels (the title bars alone, tinted head on the 8px card shadow, `aria-hidden`, `inert`, no ids), follows the pointer, the panes of a held column stacked in it, so whatever a pane holds is never built twice. Where it would drop, a 2px dashed ink line prints down the gap it would open (`.drop-here`, a new column there for a pane, the column's new place for a column) or across the gutter between the panes it would join (`.drop-before`, `.drop-after`), over the layout, so nothing moves under the pointer; near either edge of the reel the reel scrolls under a carried pane, by time, not frames. On the keyboard, Space or Enter on a grip picks it up and the grip takes the keys while it holds it: ← and → step from a column to the gap beside it and on to the next, ↑ and ↓ move among a column's panes, and Space or Enter drops it. Escape puts it back, taken before a dialog sees it, and so does focus leaving the grip (Tab, a click elsewhere), so a field reached by Tab types as it should. A stop where the drop would change nothing is passed over, but for where it started. A column dropped by the keyboard gets its grip's focus back, since a column's element moves with it. Each step is said through the page's live region ("Notes, 2 of 2 in column 2 of 3.", and at a gap "Notes, column 4 of 4.", the new column it would make). A held pane or column that leaves the layout cancels the move ("Move cancelled."), and one held over a column that leaves aims back where it started, so the arrows go on from there.
|
|
1124
|
+
|
|
1125
|
+
The splitters size by pointer or by the side arrows (a width) and up and down (a height), 5% of the reel or the column a press, unmodified only: a modified arrow is the application's. A splitter moves a share of the reel's width, or of its column's height, from the sizes the layout holds, never the sizes printed, which the stylesheet may hold at their least: a width goes no narrower than a tenth, and a pane keeps a twentieth of its column, and the stylesheet keeps both at their least on screen. A height splitter sizes the pane above it, named by id, against whichever pane is below it in the layout when the drag moves, so a reorder while it is dragged never resizes a pane in its place; it lets go when the pane above it has no pane below. While a splitter is dragged the reel prints the new sizes as a preview and asks for the size only when let go; the browser cancelling the pointer, or Escape, puts it back.
|
|
1126
|
+
|
|
1127
|
+
The reel binds no page keys: the application binds its own and calls `Reel.perform` with an `Action` (focus a pane to any side, move the focused pane to any side, into a column of its own, move its column, or step its column through the shares). Moving a pane sideways past the last column, from a column it shares, opens a new column for it. The specimen binds herdr's on the reel's frame, so they act while focus is in the reel and leave the page's keys alone: Command (Super) and Option for focus (H J K L and the arrows), with Shift to move the pane, with Control too (H, L) to move its column, E to pull a pane into a column of its own and R to step its width. They act while a field in the reel has focus, as a terminal's input does, and are matched by the key (`KeyboardEvent.code`), since Option on a Mac turns a letter into another character. A browser keeps some (Chrome on a Mac opens its developer tools on ⌘⌥J), and an Electron application's default menu takes ⌘⌥H for Hide Others: drop that role to bind it.
|
|
1128
|
+
|
|
1129
|
+
Focus is the reel's: one pane is focused (`.focused`, its window on the 8px card shadow), the one that last took focus or that a key moved to; `reconcile` reads its place again from each changed layout, so when it goes, focus finds the pane now nearest where it was, whoever moved it. A focus key puts the keyboard in the pane's control marked `data-reel-focus` (a terminal's input), else on the pane itself (ringed outside its window), and the browser scrolls the pane into view by the least distance either way, its gutter kept by `scroll-margin` (motion is stepped or instant, and a follow is instant); a pointer focusing a pane does not scroll the reel. When the focused pane leaves the layout with the keyboard in it, `reconcile` puts the keyboard in the pane now nearest where it was; focus the reader put elsewhere stays there. Each focus and move is said ("Diff, column 2 of 3.", "Shell moved to 1 of 2 in column 2 of 3.", "Column 1 takes two thirds of the width."). Every move and resize reports what it asks (`MovedPane`, `PulledOutPane`, `MovedColumn`, `ResizedColumn`, `ResizedPanes`), which `Reel.apply` applies to a layout, so the parent can apply it at once and keep or replace it when its source answers; a pane that joins a column takes that column's mean weight and an emptied column is dropped. An application that draws something of its own over a pane (a native view) measures the pane's bounds itself, which follows the reel's scroll, moves and resizes alike.
|
|
1130
|
+
|
|
1110
1131
|
### Inputs / Fields
|
|
1111
1132
|
|
|
1112
1133
|
- **Field** (a View over `@foldkit/ui`'s Input) → `div.field (.boxed, .mono) > label + input + p.field-error + p.field-help`, the label naming the input and the help describing it; a failed field prints why in `p.field-error` (sans, Urgent Red) under the control, which is `aria-invalid` and described by it before its help, and the ErrorSummary above the fields says it again; a required field's control is `aria-required`; the input's `type` the platform's (`"date"` for the browser's date control) when given. **SelectField** (over `@foldkit/ui`'s Select) → `div.field > label + select + p.field-error + p.field-help`, its options in order and a group's under an `optgroup` heading, an option that is listed but not choosable `disabled`; **TextareaField** (over `@foldkit/ui`'s Textarea) → `div.field (.mono) > label + textarea + p.field-error + p.field-help`, opening at `rows` lines. Both name, describe and mark their control as a Field does. **Fieldset** → `fieldset.field > legend.field-label`, then the controls, `p.field-error` and `p.field-help`, its error and its help linked with `aria-describedby`; **FieldGrid** → `div.field-grid` (auto-fit, 160px minimum); **Fields** → `fieldset.stack.gap-m`: a form's fields as one column `--space-m` (14px) apart, a body's gap, on the layout primitives with no rule of its own. `isBusy` (a save in flight) sets `disabled` and `aria-busy` on it, so every control holds at once and nothing changes under the save; the submit Button stays outside it, `isBusy`, so it keeps focus. A disabled control prints its value in Muted Ink; a boxless one also drops its dotted baseline (the mark of a place to type), a boxed one or a select mutes its box and caret.
|
|
@@ -1200,7 +1221,7 @@ Installing: see README.md beside this file. The package is a Foldkit library. It
|
|
|
1200
1221
|
|
|
1201
1222
|
The peers in `package.json` are pinned to the Foldkit line pixel-paper is built and tested against. Foldkit is pre-1.0, so a caret range admits patches only; a new Foldkit minor is a new pixel-paper release.
|
|
1202
1223
|
|
|
1203
|
-
Without Foldkit: link `pixel-paper.css` and write the markup named in each component above. The stylesheet relies on these attributes: `aria-current` (`="page"` on a nav segment, a crumb, a menu link and a place's button; `="true"` on `.row` and `.board-card` for the cursor, given `tabindex="0"` while the others take `-1`, on the open file and on a history's current entry), `aria-selected`, `aria-pressed`, `aria-checked`, `aria-expanded`, `aria-disabled` (on buttons, on a board card's `.title`, on menu items, on a select-all box), `aria-invalid`, `disabled` and `aria-busy` on the Fields `fieldset` while a save is in flight, `aria-hidden="true"` on a button's or menu item's `kbd.key-hint`, `data-label` on stacked-table cells, `data-drag-over` on a drop zone while a file hovers, `data-pixel-paper-dragging` on the root while a sortable's item is pressed or carried, `data-tip` on glyph-only controls (the words the page's one tip shows), native `<dialog>` for overlays and native `[popover]` for the rail's drawer and tips. Behaviour the components own (board moves, a menu's placement and arrow keys, a context menu's placement and focus, tips, a toolbar's roving focus, the palette cursor, tab arrows, focus into dialogs, an error summary's focus, the flash, the remembered palette, the toast stack's hold and focus, a player's controls, the key help) is the adopter's to write.
|
|
1224
|
+
Without Foldkit: link `pixel-paper.css` and write the markup named in each component above. The stylesheet relies on these attributes: `aria-current` (`="page"` on a nav segment, a crumb, a menu link and a place's button; `="true"` on `.row` and `.board-card` for the cursor, given `tabindex="0"` while the others take `-1`, on the open file and on a history's current entry), `aria-selected`, `aria-pressed`, `aria-checked`, `aria-expanded`, `aria-disabled` (on buttons, on a board card's `.title`, on menu items, on a select-all box), `aria-invalid`, `disabled` and `aria-busy` on the Fields `fieldset` while a save is in flight, `aria-hidden="true"` on a button's or menu item's `kbd.key-hint`, `data-label` on stacked-table cells, `data-drag-over` on a drop zone while a file hovers, `data-pixel-paper-dragging` on the root while a sortable's item or a reel's pane is pressed or carried, `--reel-width`, `--reel-weight` and `--reel-anchor` on a reel's columns, slots and panes, `data-reel-focus` on the control in a pane focus lands on, `data-tip` on glyph-only controls (the words the page's one tip shows), native `<dialog>` for overlays and native `[popover]` for the rail's drawer and tips. Behaviour the components own (board moves, a reel's moves, sizes and focus, a menu's placement and arrow keys, a context menu's placement and focus, tips, a toolbar's roving focus, the palette cursor, tab arrows, focus into dialogs, an error summary's focus, the flash, the remembered palette, the toast stack's hold and focus, a player's controls, the key help) is the adopter's to write.
|
|
1204
1225
|
|
|
1205
1226
|
Palette: with no `data-scheme` the page follows `prefers-color-scheme`, live, in CSS alone. `Scheme.SchemeSwitch` is a Submodel with no service to provide: choosing a scheme returns its `PinScheme` Command, which sets or clears `data-scheme` on `<html>` (`"light"` for paper, `"dark"` for plum) and remembers the choice in `localStorage` (storage that refuses still switches the page; the choice is just not kept); `SchemeSwitch.select` does the same from outside the switch (a palette entry). `SchemeSwitch.init()` returns the switch on `"System"`, as the server renders it, with a `ReadScheme` Command the client runs after hydration, which reads the scheme `schemeScript` pinned before first paint; map its Commands into the application's `init`. Its Subscription follows `prefers-color-scheme` (Foldkit's `Dom.streamFromMediaQuery`), so the switch knows the palette `"System"` prints in. Read the choice with `SchemeSwitch.scheme` and the palette the page prints in, `"Paper"` or `"Plum"` (`Scheme.Palette`), with `SchemeSwitch.palette`: the pinned one, or under `"System"` the system's, followed as it changes (`"Paper"` until the browser reports, as the server renders). A scheme chosen in another tab is seen on that page's next load, not live. Put `schemeScript` inline in the page's `<head>` so a remembered palette applies before first paint. A subtree with `color-scheme: dark` prints plum colours inside a paper page, the dither too, which is how the specimen shows both palettes side by side.
|
|
1206
1227
|
|
|
@@ -1208,7 +1229,7 @@ Palette: with no `data-scheme` the page follows `prefers-color-scheme`, live, in
|
|
|
1208
1229
|
|
|
1209
1230
|
How a component in this package is made. The components follow these rules, and a new one follows them too; a review finding against one of them is a defect, not a style note.
|
|
1210
1231
|
|
|
1211
|
-
**The two kinds.** A **View** is a stateless render helper, `X.view(config, h)`: the caller owns any state and passes its Messages in the config. Copy `src/button/`, `src/notice/` or `src/card/` (a container with slots). A **Submodel** has a Model, Message, OutMessage, `init`, `update` and a `view` made with `defineView`, embedded with `h.submodel`. Build it over the matching `@foldkit/ui` component when there is one. Copy `src/dialog/` (over a `@foldkit/ui` component, with a Mount that reads real key events), `src/confirmDialog/` (over another Submodel, with OutMessages) or `src/keyHelp/` (with its own Subscription). Clocks come from the Model (`Time.clockEntry`), keys from `Dom.streamFromKeyBindings`, and browser services through an Effect service the application provides as a Layer in its `resources` (CopyButton's `Clipboard`, from `@effect/platform-browser`); a Command that only touches the page it runs in does so directly, inside its Effect (SchemeSwitch's `PinScheme`).
|
|
1232
|
+
**The two kinds.** A **View** is a stateless render helper, `X.view(config, h)`: the caller owns any state and passes its Messages in the config. Copy `src/button/`, `src/notice/` or `src/card/` (a container with slots). A **Submodel** has a Model, Message, OutMessage, `init`, `update` and a `view` made with `defineView`, embedded with `h.submodel`. Build it over the matching `@foldkit/ui` component when there is one and it fits; one that would need more than one `@foldkit/ui gap` note to fit (the Reel, beside DragAndDrop) holds its own state. Copy `src/dialog/` (over a `@foldkit/ui` component, with a Mount that reads real key events), `src/confirmDialog/` (over another Submodel, with OutMessages) or `src/keyHelp/` (with its own Subscription). Clocks come from the Model (`Time.clockEntry`), keys from `Dom.streamFromKeyBindings`, and browser services through an Effect service the application provides as a Layer in its `resources` (CopyButton's `Clipboard`, from `@effect/platform-browser`); a Command that only touches the page it runs in does so directly, inside its Effect (SchemeSwitch's `PinScheme`).
|
|
1212
1233
|
|
|
1213
1234
|
Each component is `src/<name>/<name>.ts` with an explicit `index.ts`, a colocated `<name>.test.ts` (a Story for update, a Scene for view), an `export * as` line in `src/index.ts`, a section with an example file on the specimen's components page (`site/src/example/`, and its name in `site/src/covers.ts`, which a test holds against `src/index.ts`), and an entry in this document. The `index.ts` is the namespace's public surface and nothing more: `view` and its config types, and for a Submodel its Model, Message, `init`, `update`, Commands, Mounts and accessors. Only `src/index.ts` imports it: inside `src/` a file, a test included, imports the file that defines what it needs (`../button/button.ts`, `../glyph/glyphs.ts`). What several components share and no application imports has a folder with no index: `src/attributes/` (the `Attributes` type), `src/slot/` (`slotView`, an optional slot in its one element), `src/locale/`, `src/keys/`, `src/subscription/`. `src/focus/` has one: its `Focus` Command, which focuses an element by id, is a namespace of its own for the application's focus moves, with the `Message` it reports for code that returns it without a union of its own, and each Submodel that returns it re-exports it too, so a parent's test resolves it by the child's name.
|
|
1214
1235
|
|
|
@@ -424,8 +424,8 @@ export type ViewInputs = Readonly<{
|
|
|
424
424
|
toLiveRegionView?: (() => Html) | undefined;
|
|
425
425
|
}>;
|
|
426
426
|
/** The row the cursor is on as the view shows it with `commands`: the row the reader moved it
|
|
427
|
-
* to while that row is listed, else the page's `start` or `current
|
|
428
|
-
* when nothing is listed. Compare it before and after the caller's commands change to know
|
|
427
|
+
* to while that row is listed, else the page's `start` or `current` while the query finds it,
|
|
428
|
+
* else the first row the query finds; none when nothing is listed. Compare it before and after the caller's commands change to know
|
|
429
429
|
* when the cursor fell to another row (its row left) and should be scrolled into view with
|
|
430
430
|
* `ScrollToCursor`; a cursor whose row is still listed keeps it, and the list its scroll. */
|
|
431
431
|
export declare const maybeCursorId: (model: Model, commands: ReadonlyArray<Command>) => Option.Option<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commandPalette.d.ts","sourceRoot":"","sources":["../../../src/commandPalette/commandPalette.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"commandPalette.d.ts","sourceRoot":"","sources":["../../../src/commandPalette/commandPalette.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,MAAM,EAAkB,MAAM,EAAE,MAAM,EAAwB,MAAM,QAAQ,CAAC;AAC7F,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;;;;;;;;;;;;;;;;;;;;;;;;;EAQhB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC;AAItC;;;;;2EAK2E;AAC3E,eAAO,MAAM,UAAU;;QACF,EAAE;;;QACH,SAAS;QAAiB,QAAQ;;;EAEpD,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;AAIhD;gBACgB;AAChB,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAKlD,wCAAwC;AACxC,eAAO,MAAM,IAAI,WAAY,UAAU,KAAG,KAQxC,CAAC;AAIH,gDAAgD;AAChD,eAAO,MAAM,EAAE,UAAW,KAAK,KAAG,MAAiC,CAAC;AAEpE,mCAAmC;AACnC,eAAO,MAAM,MAAM,UAAW,KAAK,KAAG,OAAsC,CAAC;AAE7E;+CAC+C;AAC/C,eAAO,MAAM,KAAK,UAAW,KAAK,KAAG,MACuD,CAAC;AAE7F,sCAAsC;AACtC,eAAO,MAAM,WAAW,UAAW,KAAK,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CACR,CAAC;AAExD,kEAAkE;AAClE,eAAO,MAAM,SAAS,UAAW,KAAK,KAAG,OAA4C,CAAC;AAStF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc;;;;iBAOzB,CAAC;AAyBH;;;mFAGmF;AACnF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;iBAS5B,CAAC;AA4RH,+FAA+F;AAC/F,eAAO,MAAM,MAAM,UAAW,KAAK,WAAW,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuBjD,CAAC;AAEL;;2FAE2F;AAC3F,eAAO,MAAM,IAAI,UAAW,KAAK,KAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAClB,CAAC;AAE/C,kCAAkC;AAClC,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAClC;8BAC0B;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AAEH,wFAAwF;AACxF,eAAO,MAAM,MAAM,UAAW,KAAK,UAAU,YAAY,KAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CACf,CAAC;AAE1E;;mDAEmD;AACnD,eAAO,MAAM,aAAa,UAAW,KAAK,YAAY,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAG5E,CAAC;AAEhB;;uBAEuB;AACvB,eAAO,MAAM,UAAU,UAAW,KAAK,YAAY,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAGzE,CAAC;AAiBhB;;;;6FAI6F;AAC7F,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,aAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mGACxB,CAAC;AAI/C,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC;oFACgF;IAChF,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACjC;wBACoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;kCAE8B;IAC9B,YAAY,CAAC,EAAE,MAAM,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IAClD;0CACsC;IACtC,gBAAgB,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;CAC7C,CAAC,CAAC;AA2FH;;;;8FAI8F;AAC9F,eAAO,MAAM,aAAa,UACjB,KAAK,YACF,aAAa,CAAC,OAAO,CAAC,KAC/B,MAAM,CAAC,MAAM,CAAC,MAAM,CAA2C,CAAC;AA4TnE;;;;;;;;;;;;;GAaG;AAOH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAlcf;oFACgF;cACtE,aAAa,CAAC,OAAO,CAAC;IAChC;wBACoB;YACZ,MAAM;IACd,mFAAmF;WAC5E,MAAM;IACb;;kCAE8B;mBACf,MAAM,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC;IACjD;0CACsC;uBACnB,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS;GAic5C,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
|
|
2
2
|
import { Input as UiInput } from "@foldkit/ui";
|
|
3
|
-
import { Array, Effect, Match, Option, Schema, Stream, String, pipe } from "effect";
|
|
3
|
+
import { Array, Effect, HashSet, Match, Option, Schema, Stream, String, pipe } from "effect";
|
|
4
4
|
import * as FoldkitCommand from "foldkit/command";
|
|
5
5
|
import * as Dom from "foldkit/dom";
|
|
6
6
|
import { defineMessageUnion } from "foldkit/message";
|
|
@@ -19,7 +19,7 @@ import { LOCALE } from "../locale/locale.js";
|
|
|
19
19
|
import { slotView } from "../slot/slot.js";
|
|
20
20
|
import { prefixKeys } from "../subscription/prefixKeys.js";
|
|
21
21
|
import { BindPaletteKeys, FIELD_ATTRIBUTE, OPTION_SELECTOR, PAGE_ATTRIBUTE, earlyKeys, shownPageOf, } from "./keys.js";
|
|
22
|
-
import {
|
|
22
|
+
import { findCommands, pageChoices } from "./match.js";
|
|
23
23
|
import { Message } from "./message.js";
|
|
24
24
|
// MODEL
|
|
25
25
|
/** An open page: its command, its own query, and its cursor once the reader moved it. */
|
|
@@ -347,10 +347,12 @@ const earlyKeySubscriptions = Subscription.make()((entry) => (__foldkitBrandView
|
|
|
347
347
|
* other record on the page uses: `subscriptions("palette")` is keyed `paletteEarlyKeys`. */
|
|
348
348
|
export const subscriptions = (keyPrefix) => __foldkitBrandViewResult((prefixKeys(keyPrefix, earlyKeySubscriptions)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#subscriptions");
|
|
349
349
|
const pageOf = (commands, state) => __foldkitBrandViewResult((pipe(Array.findFirst(commands, (command) => __foldkitBrandViewResult((command.id === state.commandId), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~23")), Option.flatMap((command) => __foldkitBrandViewResult((Option.fromNullishOr(command.page)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~24")), Option.map((page) => (__foldkitBrandViewResult(({ commandId: state.commandId, page, state }), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~25"))))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#pageOf");
|
|
350
|
-
const
|
|
350
|
+
const isListed = (rows, rowId) => __foldkitBrandViewResult((Array.some(rows, (row) => __foldkitBrandViewResult((row.id === rowId), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~26"))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#isListed");
|
|
351
|
+
const firstListed = (rows, candidates) => __foldkitBrandViewResult((Array.findFirst(Array.getSomes(candidates), (cursorId) => __foldkitBrandViewResult((isListed(rows, cursorId)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~27"))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#firstListed");
|
|
352
|
+
const firstId = (rows) => __foldkitBrandViewResult((Option.map(Array.head(rows), (row) => __foldkitBrandViewResult((row.id), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~28"))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#firstId");
|
|
351
353
|
const shifted = (shown, shift) => (__foldkitBrandViewResult(({
|
|
352
354
|
...shown,
|
|
353
|
-
maybeCursorId: Option.map(rowAfter(shown.rows, shown.maybeCursorId, shift), (row) => __foldkitBrandViewResult((row.id), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
355
|
+
maybeCursorId: Option.map(rowAfter(shown.rows, shown.maybeCursorId, shift), (row) => __foldkitBrandViewResult((row.id), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~29")),
|
|
354
356
|
}), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#shifted"));
|
|
355
357
|
// NOTE: arrows waiting for the view count from the cursor's row. When that row has left the
|
|
356
358
|
// list (the application changed it in between), they are dropped rather than counted from
|
|
@@ -364,38 +366,47 @@ const shownOf = (model, commands) => {
|
|
|
364
366
|
return __foldkitBrandViewResult((isAnchored ? shifted(unshifted, model.cursorShift) : unshifted), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#shownOf");
|
|
365
367
|
};
|
|
366
368
|
// What the view shows of the Model with `commands`: the open page while its command is
|
|
367
|
-
// listed, the rows its query finds
|
|
368
|
-
// while that row is listed, else the page's
|
|
369
|
-
//
|
|
369
|
+
// listed, the rows its query finds (on a tree page, with the rows they sit under), and the
|
|
370
|
+
// cursor's row: the row the reader moved it to while that row is listed, else the page's
|
|
371
|
+
// `start`, else its `current`, else the first row, these three only among the rows the query
|
|
372
|
+
// finds; none when nothing is listed.
|
|
370
373
|
const unshiftedOf = (model, commands) => {
|
|
371
|
-
const maybePage = Option.flatMap(model.maybePage, (state) => __foldkitBrandViewResult((pageOf(commands, state)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
374
|
+
const maybePage = Option.flatMap(model.maybePage, (state) => __foldkitBrandViewResult((pageOf(commands, state)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~30"));
|
|
372
375
|
return __foldkitBrandViewResult((Option.match(maybePage, {
|
|
373
376
|
onNone: () => {
|
|
374
377
|
const rows = findCommands(commands, model.query);
|
|
375
|
-
return __foldkitBrandViewResult(({
|
|
378
|
+
return __foldkitBrandViewResult(({
|
|
379
|
+
maybePage,
|
|
380
|
+
rows,
|
|
381
|
+
contextIds: HashSet.empty(),
|
|
382
|
+
maybeCursorId: pipe(Option.filter(model.maybeCursor, (rowId) => __foldkitBrandViewResult((isListed(rows, rowId)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~31")), Option.orElse(() => __foldkitBrandViewResult((firstId(rows)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~32"))),
|
|
383
|
+
}), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~14");
|
|
376
384
|
},
|
|
377
385
|
onSome: ({ page, state }) => {
|
|
378
|
-
const rows =
|
|
386
|
+
const { rows, found } = pageChoices(page, state.query);
|
|
387
|
+
const idsOf = (choices) => __foldkitBrandViewResult((HashSet.fromIterable(Array.map(choices, ({ id }) => __foldkitBrandViewResult((id), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~33")))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#idsOf");
|
|
379
388
|
return __foldkitBrandViewResult(({
|
|
380
389
|
maybePage,
|
|
381
390
|
rows,
|
|
382
|
-
|
|
383
|
-
|
|
391
|
+
contextIds: HashSet.difference(idsOf(rows), idsOf(found)),
|
|
392
|
+
maybeCursorId: pipe(Option.filter(state.maybeCursor, (rowId) => __foldkitBrandViewResult((isListed(rows, rowId)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~34")), Option.orElse(() => __foldkitBrandViewResult((firstListed(found, [
|
|
384
393
|
Option.fromNullishOr(page.start),
|
|
385
394
|
Option.fromNullishOr(page.current),
|
|
386
|
-
]),
|
|
395
|
+
])), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~35")), Option.orElse(() => __foldkitBrandViewResult((firstId(found)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~36"))),
|
|
387
396
|
}), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onSome~13");
|
|
388
397
|
},
|
|
389
398
|
})), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#unshiftedOf");
|
|
390
399
|
};
|
|
391
400
|
/** The row the cursor is on as the view shows it with `commands`: the row the reader moved it
|
|
392
|
-
* to while that row is listed, else the page's `start` or `current
|
|
393
|
-
* when nothing is listed. Compare it before and after the caller's commands change to know
|
|
401
|
+
* to while that row is listed, else the page's `start` or `current` while the query finds it,
|
|
402
|
+
* else the first row the query finds; none when nothing is listed. Compare it before and after the caller's commands change to know
|
|
394
403
|
* when the cursor fell to another row (its row left) and should be scrolled into view with
|
|
395
404
|
* `ScrollToCursor`; a cursor whose row is still listed keeps it, and the list its scroll. */
|
|
396
405
|
export const maybeCursorId = (model, commands) => __foldkitBrandViewResult((shownOf(model, commands).maybeCursorId), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#maybeCursorId");
|
|
397
406
|
const isCommand = (row) => __foldkitBrandViewResult(("group" in row), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#isCommand");
|
|
398
|
-
const isCurrentRow = (shown, row) => __foldkitBrandViewResult((Option.exists(shown.maybePage, ({ page }) => __foldkitBrandViewResult((page.current === row.id), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
407
|
+
const isCurrentRow = (shown, row) => __foldkitBrandViewResult((Option.exists(shown.maybePage, ({ page }) => __foldkitBrandViewResult((page.current === row.id), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~37"))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#isCurrentRow");
|
|
408
|
+
const isContextRow = (shown, row) => __foldkitBrandViewResult((HashSet.has(shown.contextIds, row.id)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#isContextRow");
|
|
409
|
+
const levelOf = (row) => __foldkitBrandViewResult(("level" in row ? Option.fromNullishOr(row.level) : Option.none()), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#levelOf");
|
|
399
410
|
// What a row is as this render shows it, which Enter acts on as a click would.
|
|
400
411
|
const shownRowFor = (shown, row) => (__foldkitBrandViewResult(({
|
|
401
412
|
id: row.id,
|
|
@@ -406,14 +417,14 @@ const shownRowFor = (shown, row) => (__foldkitBrandViewResult(({
|
|
|
406
417
|
// shows them (also for a page whose command left).
|
|
407
418
|
const shownQueryOf = (model, shown) => __foldkitBrandViewResult((Option.match(shown.maybePage, { onNone: () => __foldkitBrandViewResult((model.query), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~15"), onSome: ({ state }) => __foldkitBrandViewResult((state.query), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onSome~14") })), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#shownQueryOf");
|
|
408
419
|
// The command whose page the view shows.
|
|
409
|
-
const shownPageIdOf = (shown) => __foldkitBrandViewResult((Option.map(shown.maybePage, ({ commandId }) => __foldkitBrandViewResult((commandId), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
420
|
+
const shownPageIdOf = (shown) => __foldkitBrandViewResult((Option.map(shown.maybePage, ({ commandId }) => __foldkitBrandViewResult((commandId), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~38"))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#shownPageIdOf");
|
|
410
421
|
// NOTE: the field's keys that act on the rows are taken here, where the rows are known, as a
|
|
411
422
|
// Mount on the document cannot know them: ↑, ↓ and Enter carry what this render shows (see
|
|
412
423
|
// `Model`), and each is taken from the field (its default prevented). A modified key and every
|
|
413
424
|
// key while a pick is out are left to the field.
|
|
414
425
|
const fieldKeyDown = (model, shown) => __foldkitBrandViewResult(((key, modifiers) => {
|
|
415
426
|
const list = {
|
|
416
|
-
rows: Array.map(shown.rows, (row) => __foldkitBrandViewResult((shownRowFor(shown, row)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
427
|
+
rows: Array.map(shown.rows, (row) => __foldkitBrandViewResult((shownRowFor(shown, row)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~40")),
|
|
417
428
|
maybeShownCursor: shown.maybeCursorId,
|
|
418
429
|
maybeShownPage: shownPageIdOf(shown),
|
|
419
430
|
shownQuery: shownQueryOf(model, shown),
|
|
@@ -421,7 +432,7 @@ const fieldKeyDown = (model, shown) => __foldkitBrandViewResult(((key, modifiers
|
|
|
421
432
|
};
|
|
422
433
|
return __foldkitBrandViewResult((isPicking(model) || !isPlain(modifiers)
|
|
423
434
|
? Option.none()
|
|
424
|
-
: Match.value(key).pipe(Match.withReturnType(), Match.whenOr("ArrowDown", "ArrowUp", (arrow) => __foldkitBrandViewResult((Option.some(Message.PressedArrowKey({ key: arrow, ...list }))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
435
|
+
: Match.value(key).pipe(Match.withReturnType(), Match.whenOr("ArrowDown", "ArrowUp", (arrow) => __foldkitBrandViewResult((Option.some(Message.PressedArrowKey({ key: arrow, ...list }))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~41")), Match.when("Enter", () => __foldkitBrandViewResult((Option.some(Message.PressedEnter(list))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~42")), Match.orElse(() => __foldkitBrandViewResult((Option.none()), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~43")))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~39");
|
|
425
436
|
}), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#fieldKeyDown");
|
|
426
437
|
const optionId = (paletteId, index) => __foldkitBrandViewResult((`${paletteId}-option-${index}`), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#optionId");
|
|
427
438
|
const NOTHING_FOUND = "nothing matches · try fewer letters";
|
|
@@ -436,7 +447,10 @@ const rowPartsView = (model, shown, row, h) => {
|
|
|
436
447
|
...(row.glyph === undefined
|
|
437
448
|
? []
|
|
438
449
|
: [h.span([h.Class("glyph")], [Glyph.view({ name: row.glyph }, h)])]),
|
|
439
|
-
h.span([h.Class("palette-label")], [
|
|
450
|
+
h.span([h.Class("palette-label")], [
|
|
451
|
+
row.label,
|
|
452
|
+
...(isContextRow(shown, row) ? [h.span([h.Class("sr-only")], [" (context)"])] : []),
|
|
453
|
+
]),
|
|
440
454
|
...(Option.contains(model.maybePicking, row.id)
|
|
441
455
|
? [h.span([h.Class("cursor"), h.AriaHidden(true)])]
|
|
442
456
|
: []),
|
|
@@ -454,41 +468,49 @@ const rowView = (model, shown, row, index, h) => {
|
|
|
454
468
|
...(shownRow.isHeld ? [h.AriaDisabled(true)] : []),
|
|
455
469
|
h.DataAttribute(ROW_ATTRIBUTE, row.id),
|
|
456
470
|
...(shownRow.hasPage ? [h.DataAttribute(OPENS_PAGE_ATTRIBUTE, "")] : []),
|
|
457
|
-
h.Class(
|
|
471
|
+
h.Class(Array.join([
|
|
472
|
+
"palette-row",
|
|
473
|
+
...(row.isAuthored === true ? ["authored"] : []),
|
|
474
|
+
...(isContextRow(shown, row) ? ["context"] : []),
|
|
475
|
+
], " ")),
|
|
476
|
+
...Option.match(levelOf(row), {
|
|
477
|
+
onNone: () => __foldkitBrandViewResult(([]), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~16"),
|
|
478
|
+
onSome: (level) => __foldkitBrandViewResult(([h.Style({ "--palette-level": `${level}` })]), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onSome~15"),
|
|
479
|
+
}),
|
|
458
480
|
...(shownRow.isHeld || isPicking(model)
|
|
459
481
|
? []
|
|
460
482
|
: [h.OnClick(Message.ClickedRow({ row: shownRow, maybeShownPage: shownPageIdOf(shown) }))]),
|
|
461
483
|
], rowPartsView(model, shown, row, h))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#rowView");
|
|
462
484
|
};
|
|
463
485
|
const groupedRows = (model, shown, h) => {
|
|
464
|
-
const indexed = Array.map(shown.rows, (row, index) => (__foldkitBrandViewResult(({ row, index }), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
486
|
+
const indexed = Array.map(shown.rows, (row, index) => (__foldkitBrandViewResult(({ row, index }), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~44")));
|
|
465
487
|
return __foldkitBrandViewResult((Option.match(shown.maybePage, {
|
|
466
|
-
onSome: () => __foldkitBrandViewResult((Array.map(indexed, ({ row, index }) => __foldkitBrandViewResult((rowView(model, shown, row, index, h)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
488
|
+
onSome: () => __foldkitBrandViewResult((Array.map(indexed, ({ row, index }) => __foldkitBrandViewResult((rowView(model, shown, row, index, h)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~45"))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onSome~16"),
|
|
467
489
|
onNone: () => {
|
|
468
|
-
const groups = Array.dedupe(Array.map(shown.rows, (row) => (__foldkitBrandViewResult((isCommand(row) ? row.group : ""), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
490
|
+
const groups = Array.dedupe(Array.map(shown.rows, (row) => (__foldkitBrandViewResult((isCommand(row) ? row.group : ""), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~46"))));
|
|
469
491
|
return __foldkitBrandViewResult((Array.map(groups, (group, groupIndex) => {
|
|
470
492
|
const headingId = `${id(model)}-group-${groupIndex}`;
|
|
471
493
|
return __foldkitBrandViewResult((h.keyed("div")(`group-${group}`, [h.Role("group"), h.AriaLabelledBy(headingId)], [
|
|
472
494
|
h.div([h.Id(headingId), h.Role("presentation"), h.Class("palette-group")], [group]),
|
|
473
|
-
...Array.map(Array.filter(indexed, ({ row }) => __foldkitBrandViewResult((isCommand(row) && row.group === group), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
474
|
-
])), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
475
|
-
})), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~
|
|
495
|
+
...Array.map(Array.filter(indexed, ({ row }) => __foldkitBrandViewResult((isCommand(row) && row.group === group), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~48")), ({ row, index }) => __foldkitBrandViewResult((rowView(model, shown, row, index, h)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~49")),
|
|
496
|
+
])), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~47");
|
|
497
|
+
})), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~17");
|
|
476
498
|
},
|
|
477
499
|
})), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#groupedRows");
|
|
478
500
|
};
|
|
479
501
|
const comboboxAttributes = (model, shown, noun, h) => {
|
|
480
502
|
const paletteId = id(model);
|
|
481
|
-
const maybeCursorIndex = Option.flatMap(shown.maybeCursorId, (cursorId) => __foldkitBrandViewResult((Array.findFirstIndex(shown.rows, (row) => __foldkitBrandViewResult((row.id === cursorId), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
503
|
+
const maybeCursorIndex = Option.flatMap(shown.maybeCursorId, (cursorId) => __foldkitBrandViewResult((Array.findFirstIndex(shown.rows, (row) => __foldkitBrandViewResult((row.id === cursorId), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~51"))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~50"));
|
|
482
504
|
return __foldkitBrandViewResult(([
|
|
483
505
|
h.OnKeyDownPreventDefault(fieldKeyDown(model, shown)),
|
|
484
506
|
h.Role("combobox"),
|
|
485
507
|
h.AriaExpanded(true),
|
|
486
508
|
h.AriaControls(listIdOf(paletteId)),
|
|
487
509
|
h.AriaAutocomplete("list"),
|
|
488
|
-
...Option.toArray(Option.map(maybeCursorIndex, (index) => __foldkitBrandViewResult((h.AriaActiveDescendant(optionId(paletteId, index))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
510
|
+
...Option.toArray(Option.map(maybeCursorIndex, (index) => __foldkitBrandViewResult((h.AriaActiveDescendant(optionId(paletteId, index))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~52"))),
|
|
489
511
|
h.AriaLabel(Option.match(shown.maybePage, {
|
|
490
|
-
onNone: () => __foldkitBrandViewResult((`Command or ${noun}`), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~
|
|
491
|
-
onSome: ({ page }) => __foldkitBrandViewResult((page.title), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onSome~
|
|
512
|
+
onNone: () => __foldkitBrandViewResult((`Command or ${noun}`), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~18"),
|
|
513
|
+
onSome: ({ page }) => __foldkitBrandViewResult((page.title), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onSome~17"),
|
|
492
514
|
})),
|
|
493
515
|
h.Autocomplete("off"),
|
|
494
516
|
h.DataAttribute(FIELD_ATTRIBUTE, ""),
|
|
@@ -502,8 +524,8 @@ const fieldView = (model, shown, noun, render, h) => {
|
|
|
502
524
|
onInput: (value) => __foldkitBrandViewResult((Message.UpdatedQuery({ value, maybeShownPage })), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onInput"),
|
|
503
525
|
isReadOnly: isPicking(model),
|
|
504
526
|
placeholder: Option.match(shown.maybePage, {
|
|
505
|
-
onNone: () => __foldkitBrandViewResult((`type a command or a ${noun}`), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~
|
|
506
|
-
onSome: ({ page }) => __foldkitBrandViewResult((page.find ?? "type to narrow"), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onSome~
|
|
527
|
+
onNone: () => __foldkitBrandViewResult((`type a command or a ${noun}`), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~19"),
|
|
528
|
+
onSome: ({ page }) => __foldkitBrandViewResult((page.find ?? "type to narrow"), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onSome~18"),
|
|
507
529
|
}),
|
|
508
530
|
toView: (groups) => __foldkitBrandViewResult((h.input([
|
|
509
531
|
...render.initialFocus,
|
|
@@ -519,17 +541,17 @@ const headView = (model, shown, scope, field, h) => __foldkitBrandViewResult((h.
|
|
|
519
541
|
isQuiet: true,
|
|
520
542
|
isDisabled: isPicking(model),
|
|
521
543
|
onClick: Message.ClickedBack(),
|
|
522
|
-
}, h)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
544
|
+
}, h)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~53"))),
|
|
523
545
|
h.span([h.Class("palette-scope")], [scope]),
|
|
524
|
-
...Option.toArray(Option.map(shown.maybePage, ({ page }) => __foldkitBrandViewResult((h.span([h.Class("palette-title")], [page.title])), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
546
|
+
...Option.toArray(Option.map(shown.maybePage, ({ page }) => __foldkitBrandViewResult((h.span([h.Class("palette-title")], [page.title])), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~54"))),
|
|
525
547
|
field,
|
|
526
548
|
])), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#headView");
|
|
527
549
|
const listView = (model, shown, h) => __foldkitBrandViewResult((h.div([
|
|
528
550
|
h.Id(listIdOf(id(model))),
|
|
529
551
|
h.Role("listbox"),
|
|
530
552
|
h.AriaLabel(Option.match(shown.maybePage, {
|
|
531
|
-
onNone: () => __foldkitBrandViewResult(("Results"), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~
|
|
532
|
-
onSome: ({ page }) => __foldkitBrandViewResult((page.title), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onSome~
|
|
553
|
+
onNone: () => __foldkitBrandViewResult(("Results"), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~20"),
|
|
554
|
+
onSome: ({ page }) => __foldkitBrandViewResult((page.title), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onSome~19"),
|
|
533
555
|
})),
|
|
534
556
|
...(isPicking(model) ? [h.AriaBusy(true)] : []),
|
|
535
557
|
h.Class("palette-list"),
|
|
@@ -542,13 +564,15 @@ const listView = (model, shown, h) => __foldkitBrandViewResult((h.div([
|
|
|
542
564
|
// count.
|
|
543
565
|
const countView = (model, shown, h) => {
|
|
544
566
|
const shownQuery = shownQueryOf(model, shown);
|
|
545
|
-
return __foldkitBrandViewResult((h.div([h.Role("status"), h.Class("sr-only")], String.isEmpty(shownQuery)
|
|
567
|
+
return __foldkitBrandViewResult((h.div([h.Role("status"), h.Class("sr-only")], String.isEmpty(shownQuery)
|
|
568
|
+
? []
|
|
569
|
+
: [countWords(shown.rows.length - HashSet.size(shown.contextIds))])), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#countView");
|
|
546
570
|
};
|
|
547
571
|
const cardView = (model, viewInputs, render, h) => {
|
|
548
|
-
const { commands, scope = "commands", noun = "title", toDetailView = () => __foldkitBrandViewResult(([]), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
572
|
+
const { commands, scope = "commands", noun = "title", toDetailView = () => __foldkitBrandViewResult(([]), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~55") } = viewInputs;
|
|
549
573
|
const shown = shownOf(model, commands);
|
|
550
574
|
return __foldkitBrandViewResult((h.div([
|
|
551
|
-
...Option.toArray(Option.map(shownPageIdOf(shown), (commandId) => __foldkitBrandViewResult((h.DataAttribute(PAGE_ATTRIBUTE, commandId)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
575
|
+
...Option.toArray(Option.map(shownPageIdOf(shown), (commandId) => __foldkitBrandViewResult((h.DataAttribute(PAGE_ATTRIBUTE, commandId)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~56"))),
|
|
552
576
|
h.OnMount(BindPaletteKeys()),
|
|
553
577
|
h.Class("card palette"),
|
|
554
578
|
], [
|
|
@@ -556,7 +580,7 @@ const cardView = (model, viewInputs, render, h) => {
|
|
|
556
580
|
...slotView("div", "palette-detail", toDetailView(), h),
|
|
557
581
|
listView(model, shown, h),
|
|
558
582
|
countView(model, shown, h),
|
|
559
|
-
h.div([h.Class("palette-more")], [Option.match(shown.maybePage, { onNone: () => __foldkitBrandViewResult((COMMANDS_FOOT), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~
|
|
583
|
+
h.div([h.Class("palette-more")], [Option.match(shown.maybePage, { onNone: () => __foldkitBrandViewResult((COMMANDS_FOOT), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onNone~21"), onSome: () => __foldkitBrandViewResult((PAGE_FOOT), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#onSome~20") })]),
|
|
560
584
|
])), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#cardView");
|
|
561
585
|
};
|
|
562
586
|
/**
|
|
@@ -590,4 +614,4 @@ export const view = defineView((model, viewInputs, h) => __foldkitBrandViewResul
|
|
|
590
614
|
toView: (render) => __foldkitBrandViewResult((cardView(model, viewInputs, render, h)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#toView~2"),
|
|
591
615
|
},
|
|
592
616
|
toParentMessage: (message) => __foldkitBrandViewResult((Message.GotDialogMessage({ message })), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#toParentMessage~2"),
|
|
593
|
-
})), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~
|
|
617
|
+
})), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/commandPalette.js#anonymous~57"));
|
|
@@ -14,6 +14,9 @@ export type Choice = Readonly<{
|
|
|
14
14
|
isDisabled?: boolean;
|
|
15
15
|
/** Why it cannot run now (`"No item open"`), printed in place of the hint while held. */
|
|
16
16
|
reason?: string;
|
|
17
|
+
/** On a page, its depth in the page's tree, from 1 at the roots: it sits under the nearest
|
|
18
|
+
* choice before it one level up, and indents by its depth. A page without levels is flat. */
|
|
19
|
+
level?: number;
|
|
17
20
|
}>;
|
|
18
21
|
/** A page of choices a command opens in place (a status, a date, a destination). */
|
|
19
22
|
export type Page = Readonly<{
|
|
@@ -24,14 +27,14 @@ export type Page = Readonly<{
|
|
|
24
27
|
find?: string;
|
|
25
28
|
/** The choice in force now: tagged current, and not picked again. */
|
|
26
29
|
current?: string;
|
|
27
|
-
/** Where the cursor starts while the reader has not moved it: this choice while
|
|
28
|
-
*
|
|
29
|
-
* first a typed query finds). */
|
|
30
|
+
/** Where the cursor starts while the reader has not moved it: this choice while the query
|
|
31
|
+
* finds it, else `current` while the query finds it, else the first choice the query finds
|
|
32
|
+
* (the next status after the current one; the first a typed query finds). */
|
|
30
33
|
start?: string;
|
|
31
34
|
}>;
|
|
32
35
|
/** One command, under its group's heading. With a `page` it opens that page in place instead
|
|
33
36
|
* of being selected. */
|
|
34
|
-
export type Command = Choice & Readonly<{
|
|
37
|
+
export type Command = Omit<Choice, "level"> & Readonly<{
|
|
35
38
|
group: string;
|
|
36
39
|
page?: Page;
|
|
37
40
|
}>;
|
|
@@ -40,6 +43,13 @@ export declare const matches: (query: string, label: string) => boolean;
|
|
|
40
43
|
/** The commands the query finds, under their groups in the order the groups first appear,
|
|
41
44
|
* authored titles after the commands. With no query, authored titles wait. */
|
|
42
45
|
export declare const findCommands: (commands: ReadonlyArray<Command>, query: string) => ReadonlyArray<Command>;
|
|
43
|
-
/** The choices a page
|
|
46
|
+
/** The choices a page's query finds. */
|
|
44
47
|
export declare const findChoices: (page: Page, query: string) => ReadonlyArray<Choice>;
|
|
48
|
+
/** What a page shows for its query: the choices it finds, and the rows, which are those
|
|
49
|
+
* choices each with the choices it sits under in the page's tree, in the page's order. */
|
|
50
|
+
export type PageChoices = Readonly<{
|
|
51
|
+
rows: ReadonlyArray<Choice>;
|
|
52
|
+
found: ReadonlyArray<Choice>;
|
|
53
|
+
}>;
|
|
54
|
+
export declare const pageChoices: (page: Page, query: string) => PageChoices;
|
|
45
55
|
//# sourceMappingURL=match.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../../../src/commandPalette/match.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAIpD,gEAAgE;AAChE,MAAM,MAAM,MAAM,GAAG,QAAQ,CAAC;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;uBACmB;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;2CACuC;IACvC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../../../src/commandPalette/match.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAIpD,gEAAgE;AAChE,MAAM,MAAM,MAAM,GAAG,QAAQ,CAAC;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;uBACmB;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;2CACuC;IACvC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;kGAC8F;IAC9F,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,oFAAoF;AACpF,MAAM,MAAM,IAAI,GAAG,QAAQ,CAAC;IAC1B,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/B,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;kFAE8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH;yBACyB;AACzB,MAAM,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AASvF,sFAAsF;AACtF,eAAO,MAAM,OAAO,UAAW,MAAM,SAAS,MAAM,KAAG,OAGtD,CAAC;AAEF;+EAC+E;AAC/E,eAAO,MAAM,YAAY,aACb,aAAa,CAAC,OAAO,CAAC,SACzB,MAAM,KACZ,aAAa,CAAC,OAAO,CAevB,CAAC;AAEF,wCAAwC;AACxC,eAAO,MAAM,WAAW,SAAU,IAAI,SAAS,MAAM,KAAG,aAAa,CAAC,MAAM,CAGF,CAAC;AAM3E;2FAC2F;AAC3F,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5B,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9B,CAAC,CAAC;AAEH,eAAO,MAAM,WAAW,SAAU,IAAI,SAAS,MAAM,KAAG,WAavD,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
|
|
2
|
-
import { Array, Option, Order, String, pipe } from "effect";
|
|
2
|
+
import { Array, HashSet, Option, Order, String, pipe } from "effect";
|
|
3
3
|
// MATCHING
|
|
4
4
|
const isSubsequence = (needle, haystack) => __foldkitBrandViewResult((Array.reduce(Array.fromIterable(haystack), 0, (found, character) => __foldkitBrandViewResult((needle.charAt(found) === character ? found + 1 : found), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#anonymous")) === needle.length), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#isSubsequence");
|
|
5
5
|
/** Whether the query finds a label: its letters in order, anywhere, ignoring case. */
|
|
@@ -21,7 +21,22 @@ export const findCommands = (commands, query) => {
|
|
|
21
21
|
const groupIndex = (command) => __foldkitBrandViewResult((Option.getOrElse(Array.findFirstIndex(groups, (group) => __foldkitBrandViewResult((group === command.group), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#anonymous~7")), () => __foldkitBrandViewResult((groups.length), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#anonymous~8"))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#groupIndex");
|
|
22
22
|
return __foldkitBrandViewResult((Array.sort(ordered, Order.mapInput(Order.Number, groupIndex))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#findCommands");
|
|
23
23
|
};
|
|
24
|
-
/** The choices a page
|
|
24
|
+
/** The choices a page's query finds. */
|
|
25
25
|
export const findChoices = (page, query) => __foldkitBrandViewResult((String.isEmpty(String.trim(query))
|
|
26
26
|
? page.choices
|
|
27
27
|
: Array.filter(page.choices, (choice) => __foldkitBrandViewResult((matches(query, choice.label)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#anonymous~9"))), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#findChoices");
|
|
28
|
+
const WALK_START = { chain: [], keptIds: HashSet.empty() };
|
|
29
|
+
export const pageChoices = (page, query) => {
|
|
30
|
+
const found = findChoices(page, query);
|
|
31
|
+
const foundIds = HashSet.fromIterable(Array.map(found, ({ id }) => __foldkitBrandViewResult((id), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#anonymous~10")));
|
|
32
|
+
const { keptIds } = Array.reduce(page.choices, WALK_START, ({ chain, keptIds }, choice) => {
|
|
33
|
+
const nextChain = Array.append(Array.take(chain, (choice.level ?? 1) - 1), choice);
|
|
34
|
+
return __foldkitBrandViewResult(({
|
|
35
|
+
chain: nextChain,
|
|
36
|
+
keptIds: HashSet.has(foundIds, choice.id)
|
|
37
|
+
? HashSet.union(keptIds, HashSet.fromIterable(Array.map(nextChain, ({ id }) => __foldkitBrandViewResult((id), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#anonymous~12"))))
|
|
38
|
+
: keptIds,
|
|
39
|
+
}), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#anonymous~11");
|
|
40
|
+
});
|
|
41
|
+
return __foldkitBrandViewResult(({ rows: Array.filter(page.choices, ({ id }) => __foldkitBrandViewResult((HashSet.has(keptIds, id)), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#anonymous~13")), found }), "@gjermundgaraba-designs/pixel-paper/src/commandPalette/match.js#pageChoices");
|
|
42
|
+
};
|
package/dist/src/drag/drag.d.ts
CHANGED
|
@@ -21,6 +21,9 @@ export declare const innerControls: (liftingButtons: string) => string;
|
|
|
21
21
|
export declare const takeEscape: <Message>(event: KeyboardEvent, message: Message) => Option.Option<Message>;
|
|
22
22
|
/** Escape on the window, in the capture phase, as `message`. */
|
|
23
23
|
export declare const escapeKeys: <Message>(message: Message) => Stream.Stream<Message>;
|
|
24
|
+
/** The moves, release and cancel of the pointer `pointerId`, on the document; another
|
|
25
|
+
* pointer's are not the drag's. */
|
|
26
|
+
export declare const pressingPointer: (pointerId: number) => Stream.Stream<PointerEvent>;
|
|
24
27
|
/** Sizes the copy that follows the pointer to the element `sourceSelector` finds, and keeps the
|
|
25
28
|
* pointer where it pressed that element. */
|
|
26
29
|
export declare const measureLift: (element: Element, sourceSelector: string, pressX: number, pressY: number) => void;
|