@jsenv/navi 0.29.299 → 0.29.300

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.
@@ -62349,6 +62349,7 @@ const css$B = /* css */`.navi_picker {
62349
62349
  --popover-background-color: var(--picker-popup-background-color, var(--navi-popup-background-color));
62350
62350
  --popover-outline-width: var(--picker-outline-width);
62351
62351
  --popover-outline-color: var(--picker-outline-color);
62352
+ --popover-box-shadow: var(--picker-popup-box-shadow, var(--navi-popup-box-shadow));
62352
62353
  --popover-max-height: var(--picker-popover-max-height);
62353
62354
  min-width: var(--picker-popover-min-width, var(--anchor-width, 0px));
62354
62355
  cursor: default;
@@ -62385,6 +62386,7 @@ const css$B = /* css */`.navi_picker {
62385
62386
  --dialog-background-color: var(--picker-popup-background-color, var(--navi-popup-background-color));
62386
62387
  --dialog-outline-width: var(--picker-outline-width);
62387
62388
  --dialog-outline-color: var(--picker-outline-color);
62389
+ --dialog-box-shadow: var(--picker-popup-box-shadow, var(--navi-popup-box-shadow));
62388
62390
  --dialog-min-width: var(--picker-dialog-min-width);
62389
62391
  --dialog-min-height: var(--picker-dialog-min-height);
62390
62392
  --dialog-max-width: var(--picker-dialog-max-width);
@@ -62569,7 +62571,10 @@ const PickerCustom = props => {
62569
62571
  // hands the popup that controller, and a controlled Dialog/Popover reads
62570
62572
  // no onOpen/onClose of its own.
62571
62573
  onOpen,
62572
- onClose
62574
+ onClose,
62575
+ // What opens the popup — the press, or an interaction navi detects
62576
+ // ("longpress", "contextmenu"…, or a list of them). See the JSDoc.
62577
+ openOn = "press"
62573
62578
  } = props;
62574
62579
  // Resolve the id the same way useControlProps does (own id > Field's id > generated id)
62575
62580
  // before computing popupId below, so two Pickers without an explicit id never collide.
@@ -62605,6 +62610,7 @@ const PickerCustom = props => {
62605
62610
  delete pickerProps.onConfirm;
62606
62611
  delete pickerProps.onOpen;
62607
62612
  delete pickerProps.onClose;
62613
+ delete pickerProps.openOn;
62608
62614
  // Read below for the popup alone; on the trigger it would land on the DOM as
62609
62615
  // an unknown attribute holding a ref object.
62610
62616
  delete pickerProps.anchor;
@@ -63002,7 +63008,29 @@ const PickerCustom = props => {
63002
63008
  // could then never form. So the picker steps back and opens on the click,
63003
63009
  // which the browser only delivers if the press stayed a press (a gesture
63004
63010
  // swallows the click it leaves behind).
63005
- const interactionsDispute = interactionsDisputeThePress(props.interactions);
63011
+ // Opening on something else than the press: the hold, the right click.
63012
+ // Declared on the picker itself, as the caller would declare their own,
63013
+ // so it is read and arbitrated like any other gesture — a swipe on the
63014
+ // same card takes the press a hold would have taken — and gated like one:
63015
+ // a read-only picker refuses it where the finger is. The press is then
63016
+ // nobody's: a tap on the card opens nothing (see the two reactions
63017
+ // below), and the keyboard keeps its own ways in (the shortcuts above).
63018
+ const openOnList = Array.isArray(openOn) ? openOn : [openOn];
63019
+ const opensOnPress = openOnList.includes("press");
63020
+ let interactions = props.interactions;
63021
+ if (!opensOnPress) {
63022
+ interactions = {
63023
+ ...interactions
63024
+ };
63025
+ for (const type of openOnList) {
63026
+ interactions[type] = e => {
63027
+ requestOpen(e);
63028
+ };
63029
+ }
63030
+ pickerProps.interactions = interactions;
63031
+ pickerProps["data-open-on"] = openOnList.join(" ");
63032
+ }
63033
+ const interactionsDispute = interactionsDisputeThePress(interactions);
63006
63034
  Object.assign(pickerProps, {
63007
63035
  eventReactionDefinitions: {
63008
63036
  mouseDown: e => {
@@ -63028,7 +63056,7 @@ const PickerCustom = props => {
63028
63056
  // because that is where such a box says what it travels by — and
63029
63057
  // asked at every press, since what the picker sits in is not the
63030
63058
  // picker's to know at mount.
63031
- if (interactionsDispute || isPressDisputedByDrag(e.target)) {
63059
+ if (!opensOnPress || interactionsDispute || isPressDisputedByDrag(e.target)) {
63032
63060
  return null;
63033
63061
  }
63034
63062
  return {
@@ -63045,6 +63073,11 @@ const PickerCustom = props => {
63045
63073
  if (isWithinPickerContent(e.target)) {
63046
63074
  return null;
63047
63075
  }
63076
+ if (!opensOnPress) {
63077
+ // Neither the tap, nor the click a hold leaves behind: the press
63078
+ // is not what opens this picker.
63079
+ return null;
63080
+ }
63048
63081
  // When a label is clicked it transfers focus to the select
63049
63082
  // in that case we want to open it (otherwise we have already opened on mousedown interaction)
63050
63083
  // And when a gesture disputes the press (see the two disputes
@@ -63125,7 +63158,12 @@ const PickerContentInsidePopup = props => {
63125
63158
  // itself, and this one does not. Popover ignores it, same as Dialog ignores
63126
63159
  // marginWithAnchor.
63127
63160
  dockedOnSmallTouchScreen,
63161
+ // Same again: the dialog takes the trigger's box as a floor (and, with
63162
+ // dialogMaxWidth="var(--anchor-width)", as a ceiling) — what keeps a card
63163
+ // its own width once lifted. Dialog's own `sizeFromAnchor`.
63164
+ dialogSizeFromAnchor,
63128
63165
  animation,
63166
+ animationDuration,
63129
63167
  // mode="callout": what the callout says about what it holds, and paints
63130
63168
  // in its border and icon — "none" for a plain tooltip (see the callout
63131
63169
  // defaults in PickerCustomResolver). And whether it wears a cross: without
@@ -63186,6 +63224,7 @@ const PickerContentInsidePopup = props => {
63186
63224
  mode: mode,
63187
63225
  layer: popupLayer,
63188
63226
  animation: animation,
63227
+ animationDuration: animationDuration,
63189
63228
  positionArea: isPopover ? positionArea ?? (popoverMode === "nearby" ? "bottom-start" : "inset(top-left)") : positionArea,
63190
63229
  marginWithAnchor: isPopover ? popoverSpacing : undefined,
63191
63230
  marginWithContainer: marginWithContainer === undefined && isPopover ? popoverSpacing : marginWithContainer,
@@ -63200,6 +63239,7 @@ const PickerContentInsidePopup = props => {
63200
63239
  expandX: isPopover ? undefined : dialogExpandX,
63201
63240
  expandY: isPopover ? undefined : dialogExpandY,
63202
63241
  dockedOnSmallTouchScreen: isPopover ? undefined : dockedOnSmallTouchScreen,
63242
+ sizeFromAnchor: isPopover ? undefined : dialogSizeFromAnchor,
63203
63243
  children: jsx(PopupModeContext.Provider, {
63204
63244
  value: mode,
63205
63245
  children: children
@@ -71217,6 +71257,11 @@ const css$t = /* css */`@layer navi {
71217
71257
  }
71218
71258
  }
71219
71259
 
71260
+ &[data-open-on~="longpress"] {
71261
+ -webkit-touch-callout: none;
71262
+ user-select: none;
71263
+ }
71264
+
71220
71265
  &[navi-ui-custom] {
71221
71266
  & .navi_picker_input {
71222
71267
  top: calc(-1 * (var(--picker-border-width) + var(--x-picker-press-padding-top)));
@@ -72045,6 +72090,7 @@ const PickerStyleCSSVars = {
72045
72090
  "dialogMaxHeight": ["--picker-dialog-max-height", "maxHeight"],
72046
72091
  "popupBackgroundColor": "--picker-popup-background-color",
72047
72092
  "popupBorderRadius": ["--picker-popup-border-radius", "borderRadius"],
72093
+ "popupBoxShadow": ["--picker-popup-box-shadow", "boxShadow"],
72048
72094
  "dialogBorderWidth": ["--picker-dialog-border-width", "borderWidth"],
72049
72095
  "slotSpacing": ["--picker-slot-spacing", "margin"],
72050
72096
  "pressPadding": ["--picker-press-padding", "padding"],
@@ -72142,6 +72188,9 @@ const PickerFirstResolver = props => {
72142
72188
  * action?: (value: any, event: Event) => void,
72143
72189
  * children?: import("ignore:preact").ComponentChildren,
72144
72190
  * mode?: "popover" | "dialog" | "callout",
72191
+ * openOn?: "press" | "longpress" | "contextmenu" | string | string[],
72192
+ * animation?: boolean | "auto" | "fading" | "scaling" | "sliding" | "growing" | `slide-from-${string}`,
72193
+ * animationDuration?: string,
72145
72194
  * calloutStatus?: "info" | "warning" | "error" | "success" | "none",
72146
72195
  * calloutIcon?: boolean,
72147
72196
  * calloutCloseButton?: boolean,
@@ -72172,6 +72221,7 @@ const PickerFirstResolver = props => {
72172
72221
  * dialogMaxHeight?: number | string,
72173
72222
  * popupBackgroundColor?: string,
72174
72223
  * popupBorderRadius?: number | string,
72224
+ * popupBoxShadow?: string,
72175
72225
  * dialogBorderWidth?: number | string,
72176
72226
  * clearable?: boolean,
72177
72227
  * picksNothing?: boolean,
@@ -72184,6 +72234,7 @@ const PickerFirstResolver = props => {
72184
72234
  * dialogExpand?: boolean,
72185
72235
  * dialogExpandX?: boolean,
72186
72236
  * dialogExpandY?: boolean,
72237
+ * dialogSizeFromAnchor?: boolean,
72187
72238
  * marginWithContainer?: number | string,
72188
72239
  * anchor?: import("ignore:preact").RefObject<HTMLElement> | HTMLElement,
72189
72240
  * escapeEffect?: "cancel" | "close",
@@ -72201,6 +72252,23 @@ const PickerFirstResolver = props => {
72201
72252
  * variant that paints the trigger transparent leaves the popup a real sheet.
72202
72253
  * @param {number|string} [popupBorderRadius] The popup's corners. Left out they
72203
72254
  * echo the trigger's `borderRadius`.
72255
+ * @param {string} [popupBoxShadow] The popup's shadow, popover or dialog. Left
72256
+ * out it is the one every navi popup casts. `"none"` for a popup that is
72257
+ * not a sheet — a card lifted out of a page draws its own edges, and the
72258
+ * popup only carries it (see `dialogSizeFromAnchor`).
72259
+ * @param {"press"|"longpress"|"contextmenu"|string|string[]} [openOn="press"]
72260
+ * What opens the popup. The press, by default — the way a native select
72261
+ * opens. Any other name is an interaction navi detects (see
72262
+ * docs/interactions.md: `"longpress"`, `"contextmenu"`, `"dblclick"`…), or
72263
+ * a list of them — `["longpress", "contextmenu"]` is the hold on a phone and
72264
+ * the right click at a desk. The picker declares it on itself, so it is
72265
+ * arbitrated like any other gesture: a swipe on the same card takes the
72266
+ * press a hold would have taken. A tap then opens nothing — the press stays
72267
+ * free for what the `ui` holds, which is what lets a whole card be a picker
72268
+ * without every touch on it opening the sheet — and the keyboard keeps its
72269
+ * ways in (Enter, Space, the arrows). Being a gesture, the open goes through
72270
+ * the interaction gate as one: a `readOnly` picker refuses it and says so
72271
+ * where the finger is, whatever `openWhileReadOnly` says.
72204
72272
  * @param {import("ignore:preact").ComponentChildren | "default"} [ui] What the
72205
72273
  * trigger draws in place of the value's default rendering (a date, a list
72206
72274
  * joined by commas…) — and then all it draws: `placeholder` is not shown
@@ -72312,7 +72380,14 @@ const PickerFirstResolver = props => {
72312
72380
  * @param {boolean} [picksNothing] The popup asks something rather than holding
72313
72381
  * an answer, so the trigger draws a label and never a value: no chevron, no
72314
72382
  * clear cross, in any variant. `type="confirm"` says it for itself; say it
72315
- * for a popup of your own that is a menu of actions rather than a choice.
72383
+ * for a popup of your own that is a menu of actions rather than a choice —
72384
+ * or a DOOR: a drawing that grows to be looked at (a weather scene, a plan),
72385
+ * with no `action` at all. The picker is then a trigger and a popup, and
72386
+ * nothing more: its close does nothing, and what it keeps from being a
72387
+ * control is what a door still needs — the role of a button, the keyboard
72388
+ * opening it, the anchor being itself, and one popup per instance without
72389
+ * an id to wire. See "The trigger wears the wait" in docs/popup_open.md for
72390
+ * when that beats a `Button` opening a shared `Dialog`.
72316
72391
  * @param {string|import("ignore:preact").ComponentChildren} [clearConfirm] The
72317
72392
  * question asked before the clear cross clears anything — the `message` of
72318
72393
  * the `<Picker type="confirm">` the cross then is, plain text or JSX. Asked
@@ -72419,6 +72494,21 @@ const PickerFirstResolver = props => {
72419
72494
  * a dialog docked by `dockedOnSmallTouchScreen` withdraws it and stays
72420
72495
  * container-wide, so both can be stated at once.
72421
72496
  * @param {number|string} [dialogMaxHeight] Same, on the height.
72497
+ * @param {boolean|"auto"|"fading"|"scaling"|"sliding"|"growing"|`slide-from-${string}`} [animation]
72498
+ * The popup's own (see Popover/Dialog). `"growing"` is the one a picker
72499
+ * whose `ui` is a whole piece of the page wants: the popup is the browser
72500
+ * morphing the trigger's box into what carries `data-grow` inside the popup
72501
+ * (the popup itself when nothing does), and back on close. The trigger IS
72502
+ * the anchor, so nothing has to be named for it — see `dialogSizeFromAnchor`
72503
+ * for a card that must keep its width on the way, and leave it out for a
72504
+ * drawing that opens precisely to get bigger.
72505
+ * @param {string} [animationDuration] The popup's own (`--popup-animation-duration`).
72506
+ * @param {boolean} [dialogSizeFromAnchor] Dialog mode: the dialog takes the
72507
+ * trigger's box as a floor (`--anchor-width`/`--anchor-height`, Dialog's
72508
+ * own `sizeFromAnchor`) — and, with `dialogMaxWidth="var(--anchor-width)"`,
72509
+ * as a ceiling too. That pair is what keeps a card its own width once
72510
+ * lifted out of the page (`animation="growing"`, with `data-grow` on the
72511
+ * card inside the popup): it moves, and nothing else about it changes.
72422
72512
  * @param {"cancel"|"close"} [escapeEffect="cancel"] What Escape does to an open
72423
72513
  * picker. "cancel" puts back the value the picker had at open, and a dialog
72424
72514
  * picker also goes back in history — so anything written to the url while it