@jsenv/navi 0.29.299 → 0.29.301

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.
@@ -157,7 +157,8 @@ to start when unsure which export solves a problem.
157
157
 
158
158
  - `popup_open.md` — a popup owns its open state; `command` + `commandfor`;
159
159
  `triggerNaviCommand` as the last resort, with the event forwarded and never
160
- invented; opening ON something; a press that opens and acts is a `Picker`;
160
+ invented; opening ON something; a press that opens and acts is a `Picker`,
161
+ and its trigger wears the wait and the error (a card that lifts, `openOn`);
161
162
  Escape cancels; the close cross; a popup that loads data. Read before passing
162
163
  `open`, calling `triggerNaviCommand`, or writing a close button.
163
164
  - `popup_backdrop.md` — three independent questions: a wall or not
@@ -98,6 +98,12 @@ which the carrying interactions answer with).
98
98
  | `pan` `zoom` | a surface under the hand, or under a wheel |
99
99
  | `"keyboard:<shortcut>"` | keys, e.g. `"keyboard:ctrl+backspace"` |
100
100
 
101
+ Two holds on one press — a `longpress` declared on something inside an
102
+ element that declares one too — are answered by the nearer one, the way a
103
+ click is the innermost target's: the inner hold fires, the outer wait is given
104
+ up. Delays being equal; an outer hold made shorter than the inner one fires
105
+ first.
106
+
101
107
  A name nothing knows how to detect produces a dev warning naming the detectors
102
108
  that exist. The carrying family and the two surface streams each have a file of
103
109
  their own — [drag_interactions.md](./drag_interactions.md) and
@@ -403,6 +403,102 @@ dialog hides: a popup written once, far from every press that opens it, has to
403
403
  be told what it is about and has to answer somebody — and neither question
404
404
  exists once the popup is written where it is used.
405
405
 
406
+ ### The trigger wears the wait
407
+
408
+ The picker's `action` runs on the trigger, not in the popup: it is dispatched
409
+ on the close that keeps, and the popup goes while it runs. So what is still on
410
+ screen answers for the write — `aria-busy` and the loading outline on the
411
+ trigger while the request is out, the error callout on it if the server
412
+ refuses, and the value rolled back to the last accepted one — on by default
413
+ for a picker, and what one wants: nobody is left mid-edit behind a closed
414
+ popup, and the card goes on saying what the server knows. A
415
+ card that IS the trigger (`variant="bare"`, the card as `ui`) therefore waits
416
+ as a card and is refused as a card, wherever in the tree the sheet was written.
417
+
418
+ ```jsx
419
+ <Picker
420
+ variant="bare"
421
+ type="object"
422
+ mode="dialog"
423
+ ui={<MatchCard match={match} />}
424
+ value={match}
425
+ action={(next) => MATCH.PATCH({ id: match.id, ...next })}
426
+ >
427
+ <ControlGroup>
428
+ <MatchCard match={match} editable />
429
+ <Button command="--navi-cancel">Cancel</Button>
430
+ <Button command="--navi-send">Save</Button>
431
+ </ControlGroup>
432
+ </Picker>
433
+ ```
434
+
435
+ The card shows the answer the moment the sheet leaves when its `ui` is drawn
436
+ from what the picker holds rather than from what the caller knows — the same
437
+ state navi's own default `ui` reads, rolled back by `resetOnError`, so nothing
438
+ about the pending value is the caller's to keep or to put back. Two ways to
439
+ read it: `ui={MatchCard}` hands the component `value`, `loading` and
440
+ `interactive` as props; `ui={<MatchCard match={match} />}` keeps the caller's
441
+ own props and reads the same three with `usePickerState()` inside. The
442
+ `loading` is the wait: navi's loading outline is a two-pixel run around the
443
+ box, which a card-sized trigger draws its own waiting state for.
444
+
445
+ What the picker measures as "changed since open" is what its mirrored group
446
+ holds. A piece of the answer living outside the controls — a seating
447
+ rearranged by drag, kept in component state — has to be held by a control in
448
+ the group too (a named control bound to that state), or a close over it reads
449
+ as nothing changed and nothing runs.
450
+
451
+ Two props finish the construct. `openOn="longpress"` (or
452
+ `["longpress", "contextmenu"]`) makes the hold what opens it, so a tap on the
453
+ card stays a tap — what a card in a list needs: the drawing gets its pointer
454
+ back, a link in it navigates, a button in it presses, a picker in it opens on
455
+ its own click, with nothing to declare. Two pickers on one card is the score
456
+ sheet inside the edit card (`12_picker_card_demo.html`): the inner one names
457
+ the card as its `anchor` so it lifts the whole card, and says `standalone` so
458
+ its value stays its own. One card, one wait: each picker wears the other's
459
+ run through `loading` (from its `onActionStart`/`onActionEnd`), so while
460
+ either write is out the hold and the click are both refused, with the busy
461
+ reason where the finger is — the click only once the score picker says
462
+ `openWhileReadOnly={false}`: a busy picker otherwise still opens, to be read,
463
+ and a sheet of fields to fill in is not that. A hold declared inside the card answers before the
464
+ card's own: the nearer hold takes the press. `animation="growing"` with
465
+ `dialogSizeFromAnchor` (as wide as the card, floor and ceiling),
466
+ `popupBackgroundColor="transparent"` and `popupBoxShadow="none"` lifts the
467
+ card out of its place and puts it back (`data-grow` on the card inside the
468
+ sheet; see Dialog's own `animation`). `12_picker_card_demo.html` shows all of
469
+ it, against a backend that answers when told to.
470
+
471
+ This is not `optimistic`. `optimistic` on a control is "draw no wait at all";
472
+ here the wait is drawn, on the trigger, and the popup was never what held it.
473
+ An optimistic picker is for a write not worth showing — the card then reads
474
+ the store and says nothing until the answer lands.
475
+
476
+ The same shape with nothing to write is a **door**: a drawing that grows to be
477
+ looked at — a weather scene, a plan — and comes back. `picksNothing`, no
478
+ `action`, no `dialogSizeFromAnchor` (it opens precisely to get bigger):
479
+
480
+ ```jsx
481
+ <Picker
482
+ picksNothing
483
+ variant="bare"
484
+ mode="dialog"
485
+ ui={scene}
486
+ aria-label="Zoom"
487
+ animation="growing"
488
+ popupBackgroundColor="transparent"
489
+ popupBoxShadow="none"
490
+ >
491
+ <Box data-grow>{scene}</Box>
492
+ </Picker>
493
+ ```
494
+
495
+ A `Button` opening a `Dialog` with an `anchor` does the same and is not wrong;
496
+ what the picker removes is the id plumbing (the trigger is the anchor, the
497
+ popup is its own), and what it keeps is the day the drawing becomes editable —
498
+ it is already the thing that wears the wait. The button + shared dialog stays
499
+ the answer for [the two cases where a popup must be
500
+ shared](#when-a-shared-popup-is-still-the-right-answer).
501
+
406
502
  ### A trigger that is only an icon
407
503
 
408
504
  `variant="icon"` draws no value, and therefore no slot beside one either: the
@@ -536,8 +632,12 @@ to be hand-written to work around it: `command` next to `action` is that
536
632
  workaround, done at the one moment where the action has settled and the button
537
633
  is no longer busy.
538
634
 
539
- To close on the press instead — the answer taken as soon as it is given, the
540
- save running on its own behind a closed popup — say so on the control:
635
+ When what opened the popup is still on screen and should answer for the write
636
+ — its wait, its refusal — the popup was a picker's all along: see [the trigger
637
+ wears the wait](#the-trigger-wears-the-wait). What follows is for the other
638
+ case, a popup nobody stands in for. To close on the press — the answer taken as
639
+ soon as it is given, the save running on its own behind a closed popup — say so
640
+ on the control:
541
641
 
542
642
  ```jsx
543
643
  <Form command="--navi-close" action={saveScore} optimistic resetOnError>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.29.299",
3
+ "version": "0.29.301",
4
4
  "type": "module",
5
5
  "description": "Library of components including navigation to create frontend applications",
6
6
  "repository": {