@jsenv/navi 0.29.107 → 0.29.109
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/dist/jsenv_navi.js +780 -142
- package/dist/jsenv_navi.js.map +70 -41
- package/dist/jsenv_navi_side_effects.js +11 -0
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/docs/form_changed.md +2 -1
- package/docs/navigation.md +9 -1
- package/docs/popup_open.md +3 -1
- package/docs/testid.md +20 -2
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { installImportMetaCssBuild, windowHeightSignal, windowWidthSignal, visualViewportHeightSignal, visualViewportWidthSignal, getAppHeight, getAppWidth, coarsePointerSignal, smallTouchScreenSignal } from "./jsenv_navi_side_effects.js";
|
|
6
6
|
export { disableVirtualKeyboardOverlay } from "./jsenv_navi_side_effects.js";
|
|
7
|
-
import { elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, getElementSignature, createPubSub, findEvent, createValueEffect, findFocusDelegateTarget, findFocusable, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, startDragTo, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition,
|
|
7
|
+
import { elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, getElementSignature, createPubSub, findEvent, createValueEffect, findFocusDelegateTarget, findFocusable, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, startDragTo, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, scrollRoomTowards, closestOpenableAncestor, isAncestorOpen, observeAncestorOpenState, getAncestorOpenType, findBefore, findAfter, resolveCSSSize, hasCSSSizeUnit, initFocusGroup, scrollIntoViewScoped, stringifyStyle as stringifyStyle$1, resolveOklchLightness, contrastColor, isTouchDrivenEvent, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, onAncestorReopen, getScrollContainer, canScroll, measureWidestChildRow, performTabNavigation, wheelGestureIsTakenFrom, releaseWheelGesture, claimWheelGesture, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement } from "@jsenv/dom";
|
|
8
8
|
export { clickIsSuppressed, contrastColor, findEvent, startDragTo } from "@jsenv/dom";
|
|
9
9
|
import { signal, computed, effect, batch, untracked, useSignal } from "@preact/signals";
|
|
10
10
|
import { isValidElement, createContext, render, h, Fragment, toChildArray, options, cloneElement } from "preact";
|
|
@@ -38,7 +38,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
38
38
|
* any of these, and a number is the last resort, not the first tool.
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
const css$
|
|
41
|
+
const css$15 = /* css */`
|
|
42
42
|
@layer navi {
|
|
43
43
|
:root {
|
|
44
44
|
/* A control that overlaps its neighbours (the members of a Group share
|
|
@@ -102,7 +102,7 @@ const css$14 = /* css */`
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
`;
|
|
105
|
-
import.meta.css = [css$
|
|
105
|
+
import.meta.css = [css$15, "@jsenv/navi/src/navi_z_indexes.js"];
|
|
106
106
|
|
|
107
107
|
const addIntoArray = (array, ...valuesToAdd) => {
|
|
108
108
|
if (valuesToAdd.length === 1) {
|
|
@@ -371,7 +371,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
371
371
|
* the very first render and the browser does everything on its own.
|
|
372
372
|
*/
|
|
373
373
|
const URL_TARGET_ATTRIBUTE = "data-url-target";
|
|
374
|
-
const css$
|
|
374
|
+
const css$14 = /* css */`
|
|
375
375
|
@layer navi {
|
|
376
376
|
[${URL_TARGET_ATTRIBUTE}] {
|
|
377
377
|
animation: navi_url_target var(--navi-url-target-duration, 2000ms)
|
|
@@ -389,7 +389,7 @@ const css$13 = /* css */`
|
|
|
389
389
|
}
|
|
390
390
|
}
|
|
391
391
|
`;
|
|
392
|
-
import.meta.css = [css$
|
|
392
|
+
import.meta.css = [css$14, "@jsenv/navi/src/nav/url_target/url_target.js"];
|
|
393
393
|
let urlTargetOptions = {
|
|
394
394
|
block: "start",
|
|
395
395
|
behavior: "instant",
|
|
@@ -750,7 +750,7 @@ const useActionStatus = (action) => {
|
|
|
750
750
|
};
|
|
751
751
|
};
|
|
752
752
|
|
|
753
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
753
|
+
installImportMetaCssBuild(import.meta);const css$13 = /* css */`
|
|
754
754
|
.action_error {
|
|
755
755
|
margin-top: 0;
|
|
756
756
|
margin-bottom: 20px;
|
|
@@ -775,7 +775,7 @@ const ActionRenderer = ({
|
|
|
775
775
|
children,
|
|
776
776
|
disabled
|
|
777
777
|
}) => {
|
|
778
|
-
import.meta.css = [css$
|
|
778
|
+
import.meta.css = [css$13, "@jsenv/navi/src/action/action_renderer.jsx"];
|
|
779
779
|
if (action === undefined) {
|
|
780
780
|
throw new Error("ActionRenderer requires an action to render, but none was provided.");
|
|
781
781
|
}
|
|
@@ -3813,6 +3813,9 @@ const CONTROL_PROP_SET = new Set([
|
|
|
3813
3813
|
"signal",
|
|
3814
3814
|
"defaultValue",
|
|
3815
3815
|
"defaultChecked",
|
|
3816
|
+
// A checked radio pressed again lets its value go — a group allowed to hold
|
|
3817
|
+
// nothing. Answered by the radio's own reactions (see control_hooks.jsx).
|
|
3818
|
+
"deselectable",
|
|
3816
3819
|
"readOnly", // will depend wether readOnly is supported
|
|
3817
3820
|
|
|
3818
3821
|
"loading",
|
|
@@ -4185,15 +4188,26 @@ let renderMessageText = text => text;
|
|
|
4185
4188
|
const setCalloutMessageTextRenderer = renderer => {
|
|
4186
4189
|
renderMessageText = renderer;
|
|
4187
4190
|
};
|
|
4188
|
-
|
|
4191
|
+
|
|
4192
|
+
// What a callout's own JSX may know about the callout around it: how to close
|
|
4193
|
+
// it, and the element — whose id is what `commandFor` needs when the button
|
|
4194
|
+
// closing it is not inside it (inside, `--navi-close` finds it on its own).
|
|
4195
|
+
const CalloutContext = createContext();
|
|
4189
4196
|
const useCalloutRequestClose = () => {
|
|
4190
|
-
return useContext(
|
|
4197
|
+
return useContext(CalloutContext)?.requestClose;
|
|
4198
|
+
};
|
|
4199
|
+
const useCalloutElement = () => {
|
|
4200
|
+
return useContext(CalloutContext)?.element;
|
|
4191
4201
|
};
|
|
4192
4202
|
const renderIntoCallout = (jsx$1, calloutMessageElement, {
|
|
4193
|
-
requestClose
|
|
4203
|
+
requestClose,
|
|
4204
|
+
element
|
|
4194
4205
|
}) => {
|
|
4195
|
-
const calloutJsx = jsx(
|
|
4196
|
-
value:
|
|
4206
|
+
const calloutJsx = jsx(CalloutContext.Provider, {
|
|
4207
|
+
value: {
|
|
4208
|
+
requestClose,
|
|
4209
|
+
element
|
|
4210
|
+
},
|
|
4197
4211
|
children: jsx$1
|
|
4198
4212
|
});
|
|
4199
4213
|
render(calloutJsx, calloutMessageElement);
|
|
@@ -4203,12 +4217,14 @@ const renderIntoCallout = (jsx$1, calloutMessageElement, {
|
|
|
4203
4217
|
// text can go through renderMessageText: an emoji in a validation message must
|
|
4204
4218
|
// not make the first line taller than the icon and close button beside it.
|
|
4205
4219
|
const renderHtmlIntoCallout = (html, calloutMessageElement, {
|
|
4206
|
-
requestClose
|
|
4220
|
+
requestClose,
|
|
4221
|
+
element
|
|
4207
4222
|
}) => {
|
|
4208
4223
|
const template = document.createElement("template");
|
|
4209
4224
|
template.innerHTML = html;
|
|
4210
4225
|
renderIntoCallout(domToVNodes(template.content), calloutMessageElement, {
|
|
4211
|
-
requestClose
|
|
4226
|
+
requestClose,
|
|
4227
|
+
element
|
|
4212
4228
|
});
|
|
4213
4229
|
};
|
|
4214
4230
|
|
|
@@ -4241,6 +4257,98 @@ const domToVNodes = node => {
|
|
|
4241
4257
|
return vnodes;
|
|
4242
4258
|
};
|
|
4243
4259
|
|
|
4260
|
+
installImportMetaCssBuild(import.meta);/**
|
|
4261
|
+
* The icon a callout shows beside its message — a square in the status color
|
|
4262
|
+
* with the "!" glyph — drawable on its own, where the callout is not: on the
|
|
4263
|
+
* trigger that opens one (a picker in callout mode), so what one presses looks
|
|
4264
|
+
* like what it opens. The callout's own template draws from the same glyph
|
|
4265
|
+
* (see calloutTemplate in callout.js).
|
|
4266
|
+
*/
|
|
4267
|
+
|
|
4268
|
+
const css$12 = /* css */`
|
|
4269
|
+
.navi_callout_status_icon {
|
|
4270
|
+
--x-callout-status-icon-color: var(--navi-callout-neutral-color);
|
|
4271
|
+
|
|
4272
|
+
display: inline-flex;
|
|
4273
|
+
box-sizing: border-box;
|
|
4274
|
+
aspect-ratio: 1 / 1;
|
|
4275
|
+
width: 1em;
|
|
4276
|
+
height: 1em;
|
|
4277
|
+
flex-shrink: 0;
|
|
4278
|
+
align-items: center;
|
|
4279
|
+
justify-content: center;
|
|
4280
|
+
color: white;
|
|
4281
|
+
vertical-align: middle;
|
|
4282
|
+
background-color: var(--x-callout-status-icon-color);
|
|
4283
|
+
border-radius: 2px;
|
|
4284
|
+
|
|
4285
|
+
&[data-status="success"] {
|
|
4286
|
+
--x-callout-status-icon-color: var(--navi-callout-success-color);
|
|
4287
|
+
}
|
|
4288
|
+
&[data-status="info"] {
|
|
4289
|
+
--x-callout-status-icon-color: var(--navi-callout-info-color);
|
|
4290
|
+
}
|
|
4291
|
+
&[data-status="warning"] {
|
|
4292
|
+
--x-callout-status-icon-color: var(--navi-callout-warning-color);
|
|
4293
|
+
}
|
|
4294
|
+
&[data-status="error"] {
|
|
4295
|
+
--x-callout-status-icon-color: var(--navi-callout-error-color);
|
|
4296
|
+
}
|
|
4297
|
+
&[data-shape="circle"] {
|
|
4298
|
+
border-radius: 50%;
|
|
4299
|
+
}
|
|
4300
|
+
|
|
4301
|
+
svg {
|
|
4302
|
+
width: auto;
|
|
4303
|
+
height: 55%;
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
/* Inside an <Icon>, the icon box is the size: fill it. The glyph keeps its
|
|
4307
|
+
own share of it — the Icon's rule sizing any svg it holds to the whole
|
|
4308
|
+
box is for an svg that IS the icon, and this one sits in a square. */
|
|
4309
|
+
.navi_icon > .navi_callout_status_icon {
|
|
4310
|
+
width: 100%;
|
|
4311
|
+
height: 100%;
|
|
4312
|
+
}
|
|
4313
|
+
.navi_icon[data-icon-char] .navi_callout_status_icon svg {
|
|
4314
|
+
width: auto;
|
|
4315
|
+
height: 55%;
|
|
4316
|
+
}
|
|
4317
|
+
`;
|
|
4318
|
+
const CALLOUT_STATUS_GLYPH_VIEWBOX = "0 0 125 300";
|
|
4319
|
+
const CALLOUT_STATUS_GLYPH_PATH = "m25,1 8,196h59l8-196zm37,224a37,37 0 1,0 2,0z";
|
|
4320
|
+
|
|
4321
|
+
/**
|
|
4322
|
+
* @type {import("ignore:preact").FunctionComponent<{
|
|
4323
|
+
* status?: "info" | "warning" | "error" | "success" | "none",
|
|
4324
|
+
* shape?: "square" | "circle",
|
|
4325
|
+
* }>}
|
|
4326
|
+
* @param {"info"|"warning"|"error"|"success"|"none"} [status="info"] The color
|
|
4327
|
+
* — the callout's own for that status. `"none"` is the neutral one.
|
|
4328
|
+
* @param {"square"|"circle"} [shape="square"] Square like the callout's own
|
|
4329
|
+
* icon, or a circle.
|
|
4330
|
+
*/
|
|
4331
|
+
const CalloutStatusIcon = ({
|
|
4332
|
+
status = "info",
|
|
4333
|
+
shape = "square"
|
|
4334
|
+
}) => {
|
|
4335
|
+
import.meta.css = [css$12, "@jsenv/navi/src/control/rules/callout/callout_status_icon.jsx"];
|
|
4336
|
+
return jsx("span", {
|
|
4337
|
+
className: "navi_callout_status_icon",
|
|
4338
|
+
"data-status": status === "none" ? undefined : status,
|
|
4339
|
+
"data-shape": shape === "circle" ? "circle" : undefined,
|
|
4340
|
+
"aria-hidden": "true",
|
|
4341
|
+
children: jsx("svg", {
|
|
4342
|
+
viewBox: CALLOUT_STATUS_GLYPH_VIEWBOX,
|
|
4343
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
4344
|
+
children: jsx("path", {
|
|
4345
|
+
fill: "currentColor",
|
|
4346
|
+
d: CALLOUT_STATUS_GLYPH_PATH
|
|
4347
|
+
})
|
|
4348
|
+
})
|
|
4349
|
+
});
|
|
4350
|
+
};
|
|
4351
|
+
|
|
4244
4352
|
installImportMetaCssBuild(import.meta);/**
|
|
4245
4353
|
* A callout component that mimics native browser validation messages.
|
|
4246
4354
|
* Features:
|
|
@@ -4250,6 +4358,9 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
4250
4358
|
* - Arrow automatically shows when pointing at a valid anchor element
|
|
4251
4359
|
* - Centers in viewport when no anchor element provided or anchor is too big
|
|
4252
4360
|
*/
|
|
4361
|
+
|
|
4362
|
+
// Unique for the page's lifetime: a caller may write the id in a commandfor.
|
|
4363
|
+
let calloutCount = 0;
|
|
4253
4364
|
const css$11 = /* css */`
|
|
4254
4365
|
@layer navi {
|
|
4255
4366
|
.navi_callout {
|
|
@@ -4259,19 +4370,37 @@ const css$11 = /* css */`
|
|
|
4259
4370
|
copies. */
|
|
4260
4371
|
user-select: text;
|
|
4261
4372
|
|
|
4262
|
-
--callout-success-color:
|
|
4263
|
-
--callout-info-color:
|
|
4264
|
-
--callout-warning-color:
|
|
4265
|
-
--callout-error-color:
|
|
4373
|
+
--callout-success-color: var(--navi-callout-success-color);
|
|
4374
|
+
--callout-info-color: var(--navi-callout-info-color);
|
|
4375
|
+
--callout-warning-color: var(--navi-callout-warning-color);
|
|
4376
|
+
--callout-error-color: var(--navi-callout-error-color);
|
|
4377
|
+
--callout-neutral-color: var(--navi-callout-neutral-color);
|
|
4266
4378
|
|
|
4267
4379
|
--callout-background-color: white;
|
|
4268
4380
|
--callout-icon-color: black;
|
|
4269
4381
|
--callout-padding: 8px;
|
|
4270
4382
|
--callout-z-index: var(--navi-z-index-callout);
|
|
4383
|
+
/* The callout's own, like its font: the icon and the cross are columns
|
|
4384
|
+
one line tall (1lh), and that line has to be the message's first
|
|
4385
|
+
line — which it is only if both read the same line-height, rather
|
|
4386
|
+
than whatever the element the callout sits in happens to use. */
|
|
4387
|
+
--callout-line-height: 1.5;
|
|
4388
|
+
/* The cross is furniture, not content: quieter than the message beside
|
|
4389
|
+
it, and the size of a glyph on the message's first line. */
|
|
4390
|
+
--callout-close-button-color: color-mix(
|
|
4391
|
+
in srgb,
|
|
4392
|
+
currentColor 45%,
|
|
4393
|
+
transparent
|
|
4394
|
+
);
|
|
4395
|
+
--callout-close-button-color-hover: currentColor;
|
|
4396
|
+
--callout-close-button-size: 0.7em;
|
|
4271
4397
|
}
|
|
4272
4398
|
}
|
|
4273
4399
|
|
|
4274
4400
|
.navi_callout {
|
|
4401
|
+
/* No status until one is said (the data-status blocks below): a plain
|
|
4402
|
+
tooltip, framed in the neutral color and shown without an icon. */
|
|
4403
|
+
--x-callout-status-color: var(--callout-neutral-color);
|
|
4275
4404
|
--x-callout-border-color: var(--x-callout-status-color);
|
|
4276
4405
|
--x-callout-background-color: var(--callout-background-color);
|
|
4277
4406
|
--x-callout-icon-color: var(--x-callout-status-color);
|
|
@@ -4297,6 +4426,9 @@ const css$11 = /* css */`
|
|
|
4297
4426
|
color: revert; /* Do no inherit element color, callout is inside the element it should use document color though */
|
|
4298
4427
|
font-weight: initial; /* Callout fells disconnected from the element, font weight should be predictible and stable */
|
|
4299
4428
|
font-size: initial; /* Callout fells disconnected from the element, font size should be predictible and stable */
|
|
4429
|
+
line-height: var(
|
|
4430
|
+
--callout-line-height
|
|
4431
|
+
); /* Same reason — and what the columns measure their 1lh against */
|
|
4300
4432
|
background: transparent;
|
|
4301
4433
|
border: none;
|
|
4302
4434
|
outline: none; /* programmatic focus may land here briefly before being redirected to close button */
|
|
@@ -4326,6 +4458,11 @@ const css$11 = /* css */`
|
|
|
4326
4458
|
&[data-status="error"] {
|
|
4327
4459
|
--x-callout-status-color: var(--callout-error-color);
|
|
4328
4460
|
}
|
|
4461
|
+
&[data-close-button="none"] {
|
|
4462
|
+
.navi_callout_close_button_column {
|
|
4463
|
+
display: none;
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4329
4466
|
|
|
4330
4467
|
.navi_callout_box {
|
|
4331
4468
|
position: relative;
|
|
@@ -4376,6 +4513,15 @@ const css$11 = /* css */`
|
|
|
4376
4513
|
background-color: var(--x-callout-icon-color);
|
|
4377
4514
|
border-radius: 2px;
|
|
4378
4515
|
|
|
4516
|
+
/* Not drawn without a status (nothing to say about the message),
|
|
4517
|
+
nor when the message asked for none (icon: false). Nested in
|
|
4518
|
+
here rather than written beside this block: a rule one level up
|
|
4519
|
+
would lose to the display above on specificity alone. */
|
|
4520
|
+
.navi_callout:not([data-status]) &,
|
|
4521
|
+
.navi_callout[data-icon="none"] & {
|
|
4522
|
+
display: none;
|
|
4523
|
+
}
|
|
4524
|
+
|
|
4379
4525
|
svg {
|
|
4380
4526
|
width: 16px;
|
|
4381
4527
|
height: 12px;
|
|
@@ -4408,37 +4554,52 @@ const css$11 = /* css */`
|
|
|
4408
4554
|
.navi_callout_close_button_column {
|
|
4409
4555
|
display: flex;
|
|
4410
4556
|
height: var(--callout-icon-height);
|
|
4557
|
+
/* The button is a square one line tall around a cross of glyph size,
|
|
4558
|
+
so most of it is empty. Taken back on both sides: what is drawn then
|
|
4559
|
+
sits one gap from the text and one padding from the edge — where the
|
|
4560
|
+
icon sits on the other side — instead of that plus the button's own
|
|
4561
|
+
margin. The target keeps its full size; only the space it claims in
|
|
4562
|
+
the row shrinks. */
|
|
4563
|
+
margin-inline: calc(
|
|
4564
|
+
-1 * (var(--callout-icon-height) - var(--callout-close-button-size)) / 2
|
|
4565
|
+
);
|
|
4566
|
+
/* Whatever the cross measures, it sits on the middle of the first line
|
|
4567
|
+
— the line it is read with. */
|
|
4568
|
+
align-items: center;
|
|
4411
4569
|
align-self: flex-start;
|
|
4412
4570
|
|
|
4413
4571
|
.navi_callout_close_button {
|
|
4414
4572
|
/* A square filling the column, so the whole first line is the target;
|
|
4415
|
-
the
|
|
4573
|
+
the cross itself is drawn at glyph size in the middle of it. */
|
|
4416
4574
|
display: inline-flex;
|
|
4417
4575
|
box-sizing: border-box;
|
|
4418
4576
|
aspect-ratio: 1 / 1;
|
|
4419
4577
|
height: 100%;
|
|
4420
|
-
padding: 0
|
|
4578
|
+
padding: 0;
|
|
4421
4579
|
align-items: center;
|
|
4422
4580
|
justify-content: center;
|
|
4423
|
-
color:
|
|
4581
|
+
color: var(--callout-close-button-color);
|
|
4424
4582
|
font-size: inherit;
|
|
4425
4583
|
background: none;
|
|
4426
4584
|
border: none;
|
|
4427
4585
|
border-radius: 0.2em;
|
|
4586
|
+
/* transition: color 0.15s ease-in-out; */
|
|
4428
4587
|
cursor: pointer;
|
|
4429
4588
|
|
|
4430
4589
|
&:hover {
|
|
4431
|
-
|
|
4590
|
+
color: var(--callout-close-button-color-hover);
|
|
4591
|
+
background: rgba(0, 0, 0, 0.06);
|
|
4432
4592
|
}
|
|
4433
4593
|
|
|
4434
4594
|
&:focus-visible,
|
|
4435
4595
|
.navi_callout:focus-visible & {
|
|
4596
|
+
color: var(--callout-close-button-color-hover);
|
|
4436
4597
|
outline: auto;
|
|
4437
4598
|
}
|
|
4438
4599
|
|
|
4439
4600
|
.navi_callout_close_button_svg {
|
|
4440
|
-
width:
|
|
4441
|
-
height:
|
|
4601
|
+
width: var(--callout-close-button-size);
|
|
4602
|
+
height: var(--callout-close-button-size);
|
|
4442
4603
|
}
|
|
4443
4604
|
}
|
|
4444
4605
|
}
|
|
@@ -4453,6 +4614,11 @@ const css$11 = /* css */`
|
|
|
4453
4614
|
* @param {string} [options.status=""] - Callout status: "info" | "warning" | "error" | "success"
|
|
4454
4615
|
* @param {Function} [options.onClose] - Callback when callout is closed
|
|
4455
4616
|
* @param {boolean} [options.closeOnClickOutside] - Whether to close on outside clicks (defaults to true for "info" status)
|
|
4617
|
+
* @param {boolean} [options.icon=true] - Whether the status icon is shown beside the message.
|
|
4618
|
+
* Never shown without a status either way (see the CSS).
|
|
4619
|
+
* @param {boolean} [options.closeButton=true] - Whether the cross is shown. Without it the callout
|
|
4620
|
+
* still closes on Escape, a click outside and its own `--navi-close` — for a tooltip that is
|
|
4621
|
+
* read rather than dismissed
|
|
4456
4622
|
* @param {string} [options.reopen="toggle"] - What to do when the anchor already has an open callout:
|
|
4457
4623
|
* "toggle" closes it (a second press on what opened it closes it), "update" replaces its message
|
|
4458
4624
|
* in place, "replace" tears it down and opens a new one
|
|
@@ -4499,6 +4665,8 @@ const openCallout = (message, {
|
|
|
4499
4665
|
reopen = "toggle",
|
|
4500
4666
|
showErrorStack,
|
|
4501
4667
|
skipFocus = false,
|
|
4668
|
+
icon = true,
|
|
4669
|
+
closeButton = true,
|
|
4502
4670
|
debug = () => {}
|
|
4503
4671
|
} = {}) => {
|
|
4504
4672
|
import.meta.css = [css$11, "@jsenv/navi/src/control/rules/callout/callout.js"];
|
|
@@ -4655,10 +4823,27 @@ const openCallout = (message, {
|
|
|
4655
4823
|
}
|
|
4656
4824
|
requestClose(e, "click_close_button");
|
|
4657
4825
|
};
|
|
4658
|
-
|
|
4826
|
+
calloutCount++;
|
|
4827
|
+
const calloutId = `navi_callout_${calloutCount}`;
|
|
4659
4828
|
calloutElement.id = calloutId;
|
|
4660
4829
|
calloutElement.style.opacity = 0;
|
|
4661
4830
|
const update = (newMessage, options = {}) => {
|
|
4831
|
+
if (Object.hasOwn(options, "icon")) {
|
|
4832
|
+
if (options.icon === false) {
|
|
4833
|
+
calloutElement.setAttribute("data-icon", "none");
|
|
4834
|
+
} else {
|
|
4835
|
+
calloutElement.removeAttribute("data-icon");
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
if (Object.hasOwn(options, "closeButton")) {
|
|
4839
|
+
// Per message rather than per callout: what replaces a message (a
|
|
4840
|
+
// constraint taking over a tooltip's callout) brings its own cross back.
|
|
4841
|
+
if (options.closeButton === false) {
|
|
4842
|
+
calloutElement.setAttribute("data-close-button", "none");
|
|
4843
|
+
} else {
|
|
4844
|
+
calloutElement.removeAttribute("data-close-button");
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4662
4847
|
const prevStatus = callout.status;
|
|
4663
4848
|
// Connect callout with target element for accessibility
|
|
4664
4849
|
if (options.status && options.status !== callout.status) {
|
|
@@ -4678,7 +4863,8 @@ const openCallout = (message, {
|
|
|
4678
4863
|
if (isValidElement(newMessage)) {
|
|
4679
4864
|
debug(`callout update message (jsx)`);
|
|
4680
4865
|
renderIntoCallout(newMessage, calloutMessageElement, {
|
|
4681
|
-
requestClose
|
|
4866
|
+
requestClose,
|
|
4867
|
+
element: calloutElement
|
|
4682
4868
|
});
|
|
4683
4869
|
} else if (newMessage instanceof Node) {
|
|
4684
4870
|
// Handle DOM node (cloned from CSS selector)
|
|
@@ -4690,9 +4876,11 @@ const openCallout = (message, {
|
|
|
4690
4876
|
clearCalloutMessage(calloutMessageElement);
|
|
4691
4877
|
newMessage({
|
|
4692
4878
|
renderIntoCallout: jsx => renderIntoCallout(jsx, calloutMessageElement, {
|
|
4693
|
-
requestClose
|
|
4879
|
+
requestClose,
|
|
4880
|
+
element: calloutElement
|
|
4694
4881
|
}),
|
|
4695
|
-
requestClose
|
|
4882
|
+
requestClose,
|
|
4883
|
+
element: calloutElement
|
|
4696
4884
|
});
|
|
4697
4885
|
} else {
|
|
4698
4886
|
if (Error.isError(newMessage)) {
|
|
@@ -4717,7 +4905,8 @@ const openCallout = (message, {
|
|
|
4717
4905
|
} else {
|
|
4718
4906
|
debug(`callout update message: ${typeof newMessage === "string" ? newMessage.slice(0, 80) : String(newMessage)}`);
|
|
4719
4907
|
renderHtmlIntoCallout(String(newMessage), calloutMessageElement, {
|
|
4720
|
-
requestClose
|
|
4908
|
+
requestClose,
|
|
4909
|
+
element: calloutElement
|
|
4721
4910
|
});
|
|
4722
4911
|
}
|
|
4723
4912
|
}
|
|
@@ -4932,6 +5121,21 @@ const openCallout = (message, {
|
|
|
4932
5121
|
};
|
|
4933
5122
|
calloutElement.addEventListener("navi_request_close", handleCustomCloseEvent);
|
|
4934
5123
|
}
|
|
5124
|
+
{
|
|
5125
|
+
// What a command said inside the callout is aimed at, once aria-expanded
|
|
5126
|
+
// made the callout its target (see calloutTemplate): run it, the way a
|
|
5127
|
+
// popup runs the commands aimed at it. Inlined rather than onNaviCommand
|
|
5128
|
+
// from commands.js, which already reaches this module through the callout
|
|
5129
|
+
// manager and must not be reached back.
|
|
5130
|
+
calloutElement.addEventListener("navi_command", e => {
|
|
5131
|
+
const {
|
|
5132
|
+
implementation
|
|
5133
|
+
} = e.detail;
|
|
5134
|
+
if (typeof implementation === "function") {
|
|
5135
|
+
implementation();
|
|
5136
|
+
}
|
|
5137
|
+
});
|
|
5138
|
+
}
|
|
4935
5139
|
Object.assign(callout, {
|
|
4936
5140
|
element: calloutElement,
|
|
4937
5141
|
update,
|
|
@@ -5030,7 +5234,9 @@ const openCallout = (message, {
|
|
|
5030
5234
|
});
|
|
5031
5235
|
}
|
|
5032
5236
|
update(message, {
|
|
5033
|
-
status
|
|
5237
|
+
status,
|
|
5238
|
+
icon,
|
|
5239
|
+
closeButton
|
|
5034
5240
|
});
|
|
5035
5241
|
|
|
5036
5242
|
// positionCallout itself handles both "no anchorElement at all" and "a
|
|
@@ -5056,21 +5262,25 @@ const ARROW_WIDTH = 16;
|
|
|
5056
5262
|
const ARROW_HEIGHT = 8;
|
|
5057
5263
|
const ARROW_SPACING = 8;
|
|
5058
5264
|
|
|
5059
|
-
//
|
|
5265
|
+
// aria-expanded is what --navi-close/--navi-cancel resolve their target with
|
|
5266
|
+
// (closest "[aria-expanded]", see commands.js): a button inside the callout
|
|
5267
|
+
// closes the callout, not the picker or dialog around it. Never "false" — a
|
|
5268
|
+
// closed callout is removed, not kept.
|
|
5060
5269
|
const calloutTemplate = /* html */`
|
|
5061
5270
|
<div
|
|
5062
5271
|
class="navi_callout"
|
|
5063
5272
|
popover="manual"
|
|
5273
|
+
aria-expanded="true"
|
|
5064
5274
|
>
|
|
5065
5275
|
<div class="navi_callout_box">
|
|
5066
5276
|
<div class="navi_callout_frame"></div>
|
|
5067
5277
|
<div class="navi_callout_body">
|
|
5068
5278
|
<div class="navi_callout_icon">
|
|
5069
|
-
<svg
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
/>
|
|
5279
|
+
<svg
|
|
5280
|
+
viewBox="${CALLOUT_STATUS_GLYPH_VIEWBOX}"
|
|
5281
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5282
|
+
>
|
|
5283
|
+
<path fill="currentColor" d="${CALLOUT_STATUS_GLYPH_PATH}" />
|
|
5074
5284
|
</svg>
|
|
5075
5285
|
</div>
|
|
5076
5286
|
<!-- Keep .navi_callout_message so preact controls it -->
|
|
@@ -5719,6 +5929,8 @@ const createCalloutManager = (
|
|
|
5719
5929
|
const [, remainingTokenData] = tokens.entries().next().value;
|
|
5720
5930
|
callout.update(remainingTokenData.message, {
|
|
5721
5931
|
status: remainingTokenData.status,
|
|
5932
|
+
icon: remainingTokenData.icon,
|
|
5933
|
+
closeButton: remainingTokenData.closeButton,
|
|
5722
5934
|
});
|
|
5723
5935
|
}
|
|
5724
5936
|
return false;
|
|
@@ -5740,7 +5952,16 @@ const createCalloutManager = (
|
|
|
5740
5952
|
|
|
5741
5953
|
const addOpenToken = (
|
|
5742
5954
|
token,
|
|
5743
|
-
{
|
|
5955
|
+
{
|
|
5956
|
+
message,
|
|
5957
|
+
status,
|
|
5958
|
+
icon,
|
|
5959
|
+
closeButton,
|
|
5960
|
+
anchorElement,
|
|
5961
|
+
event,
|
|
5962
|
+
skipFocus,
|
|
5963
|
+
onClose,
|
|
5964
|
+
} = {},
|
|
5744
5965
|
) => {
|
|
5745
5966
|
if (!message) {
|
|
5746
5967
|
removeOpenToken(token, event);
|
|
@@ -5748,10 +5969,12 @@ const createCalloutManager = (
|
|
|
5748
5969
|
}
|
|
5749
5970
|
const calloutOptions = {
|
|
5750
5971
|
status,
|
|
5972
|
+
icon,
|
|
5973
|
+
closeButton,
|
|
5751
5974
|
closeOnClickOutside: status !== "error",
|
|
5752
5975
|
};
|
|
5753
5976
|
|
|
5754
|
-
tokens.set(token, { message, status, onClose });
|
|
5977
|
+
tokens.set(token, { message, status, icon, closeButton, onClose });
|
|
5755
5978
|
if (callout) {
|
|
5756
5979
|
callout.update(message, calloutOptions);
|
|
5757
5980
|
return;
|
|
@@ -5768,7 +5991,11 @@ const createCalloutManager = (
|
|
|
5768
5991
|
openingEvent: event,
|
|
5769
5992
|
skipFocus,
|
|
5770
5993
|
debug: debugPopup,
|
|
5771
|
-
onClose: ({
|
|
5994
|
+
onClose: ({
|
|
5995
|
+
event: closeEvent,
|
|
5996
|
+
reason,
|
|
5997
|
+
shouldTransferFocusFromCallout,
|
|
5998
|
+
}) => {
|
|
5772
5999
|
removeCloseOnCleanup?.();
|
|
5773
6000
|
for (const result of openResults) {
|
|
5774
6001
|
if (typeof result === "function") {
|
|
@@ -5777,8 +6004,10 @@ const createCalloutManager = (
|
|
|
5777
6004
|
}
|
|
5778
6005
|
callout = null;
|
|
5779
6006
|
// User dismissed the callout — notify all active tokens then clear.
|
|
6007
|
+
// Told what closed it: a token whose content is a popup of its own (a
|
|
6008
|
+
// picker in callout mode) closes that popup on the same event.
|
|
5780
6009
|
for (const [, tokenData] of tokens) {
|
|
5781
|
-
tokenData.onClose?.();
|
|
6010
|
+
tokenData.onClose?.({ event: closeEvent, reason });
|
|
5782
6011
|
}
|
|
5783
6012
|
tokens.clear();
|
|
5784
6013
|
const element = controller.ref.current;
|
|
@@ -24826,9 +25055,11 @@ const getHrefTargetInfo = (href) => {
|
|
|
24826
25055
|
* leave by where the reader came in, and the swipe (which replaces already, see
|
|
24827
25056
|
* route_travel.jsx) and the press say the same thing.
|
|
24828
25057
|
*
|
|
24829
|
-
* `<Link replace>` is that, and
|
|
24830
|
-
*
|
|
24831
|
-
*
|
|
25058
|
+
* `<Link replace>` is that, and so is `<Button replace>` — on an `href`, or on
|
|
25059
|
+
* a `--navi-nav-to` command. It travels as an attribute because whoever
|
|
25060
|
+
* answers the press sees the element, not the component that rendered it: the
|
|
25061
|
+
* click handler reads it off the anchor, the command off its source — the same
|
|
25062
|
+
* mouth as what a link asks of a route transition.
|
|
24832
25063
|
*/
|
|
24833
25064
|
|
|
24834
25065
|
const LINK_REPLACE_ATTRIBUTE = "data-navi-replace";
|
|
@@ -25099,75 +25330,85 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
25099
25330
|
return requestedResult;
|
|
25100
25331
|
};
|
|
25101
25332
|
|
|
25102
|
-
//
|
|
25333
|
+
// A click on a link is answered on the link itself, after the link's own
|
|
25334
|
+
// listeners: a navi control decides there whether the press is allowed — it
|
|
25335
|
+
// refuses with preventDefault from its click reaction — and runs its command
|
|
25336
|
+
// and its action; only then does the routing take the browser's place. This
|
|
25337
|
+
// is the order the Navigation API integration gets for free, where the
|
|
25338
|
+
// navigation starts once the click has been dispatched.
|
|
25339
|
+
const onLinkClick = (e) => {
|
|
25340
|
+
if (e.button !== 0) {
|
|
25341
|
+
// Ignore non-left clicks
|
|
25342
|
+
return;
|
|
25343
|
+
}
|
|
25344
|
+
if (e.metaKey) {
|
|
25345
|
+
// Ignore clicks with meta key (e.g. open in new tab)
|
|
25346
|
+
return;
|
|
25347
|
+
}
|
|
25348
|
+
if (e.defaultPrevented) {
|
|
25349
|
+
// Refused — by the link itself, or by whoever came before it.
|
|
25350
|
+
return;
|
|
25351
|
+
}
|
|
25352
|
+
const linkElement = e.currentTarget;
|
|
25353
|
+
if (linkElement.hasAttribute("data-readonly")) {
|
|
25354
|
+
return;
|
|
25355
|
+
}
|
|
25356
|
+
const href = linkElement.href;
|
|
25357
|
+
const { isEmpty, isCurrent, isSameOrigin, isAnchor } =
|
|
25358
|
+
getHrefTargetInfo(href);
|
|
25359
|
+
if (isEmpty || !isSameOrigin) {
|
|
25360
|
+
// Let link to other origins be handled by the browser
|
|
25361
|
+
return;
|
|
25362
|
+
}
|
|
25363
|
+
if (isAnchor) {
|
|
25364
|
+
// Fragment navigation belongs to the browser: it owns the indicated
|
|
25365
|
+
// part of the document, and taking it over would cost `:target` and the
|
|
25366
|
+
// focus handling that come with it.
|
|
25367
|
+
if (isCurrent) {
|
|
25368
|
+
// Except this one, which the browser answers with a scroll and
|
|
25369
|
+
// nothing else: same pathname, same hash, so no event and no url
|
|
25370
|
+
// change reaches whoever is waiting on the designated element.
|
|
25371
|
+
rearmUrlTarget();
|
|
25372
|
+
}
|
|
25373
|
+
return;
|
|
25374
|
+
}
|
|
25375
|
+
// Nothing here declared a route, so there is nothing to route to: the
|
|
25376
|
+
// page is a plain document and a link in it is a plain link. Taking it
|
|
25377
|
+
// over anyway would push the url and then have nothing to show for it —
|
|
25378
|
+
// the address bar moves and the page does not (see applyRouting's own
|
|
25379
|
+
// "not called yet" branch, which is where that used to end up).
|
|
25380
|
+
if (!isRouting()) {
|
|
25381
|
+
return;
|
|
25382
|
+
}
|
|
25383
|
+
e.preventDefault();
|
|
25384
|
+
handleRoutingTask(href, {
|
|
25385
|
+
reason: `"click" on a[href="${href}"]`,
|
|
25386
|
+
// A link that takes the place of the current entry instead of stacking
|
|
25387
|
+
// on it says so on itself (see link_replace.js).
|
|
25388
|
+
navigationType: linkAsksForReplace(linkElement) ? "replace" : "push",
|
|
25389
|
+
// Who started it. Announced with the navigation because a press
|
|
25390
|
+
// carries things the url does not: what a link asks of a route
|
|
25391
|
+
// transition is the first of them (see route_transition.jsx). Read by
|
|
25392
|
+
// whoever knows what to do with it, and it is the anchor itself —
|
|
25393
|
+
// resolved here, where it already is.
|
|
25394
|
+
element: linkElement,
|
|
25395
|
+
});
|
|
25396
|
+
};
|
|
25397
|
+
// Wired from the capture phase on window, the first place a click is seen,
|
|
25398
|
+
// so that a link is answered whatever handler stops the click on its way up
|
|
25399
|
+
// (a card's own onClick). Wired on every click: a listener an element
|
|
25400
|
+
// already has is not added twice, and a link seen for the first time is
|
|
25401
|
+
// wired before the click reaches it. The click a gesture leaves behind
|
|
25402
|
+
// never gets this far — its suppressor (click_suppression.js in
|
|
25403
|
+
// @jsenv/dom) stops it on window, before anything below.
|
|
25103
25404
|
window.addEventListener(
|
|
25104
25405
|
"click",
|
|
25105
25406
|
(e) => {
|
|
25106
|
-
if (e.button !== 0) {
|
|
25107
|
-
// Ignore non-left clicks
|
|
25108
|
-
return;
|
|
25109
|
-
}
|
|
25110
|
-
if (e.metaKey) {
|
|
25111
|
-
// Ignore clicks with meta key (e.g. open in new tab)
|
|
25112
|
-
return;
|
|
25113
|
-
}
|
|
25114
|
-
if (e.defaultPrevented) {
|
|
25115
|
-
return;
|
|
25116
|
-
}
|
|
25117
|
-
if (clickIsSuppressed()) {
|
|
25118
|
-
// The click that ends a gesture (click_suppression.js in @jsenv/dom).
|
|
25119
|
-
// Its suppressor also listens on window in capture, so whichever
|
|
25120
|
-
// module registered first runs first — asked explicitly, the order
|
|
25121
|
-
// stops mattering.
|
|
25122
|
-
return;
|
|
25123
|
-
}
|
|
25124
25407
|
const linkElement = e.target.closest("a");
|
|
25125
25408
|
if (!linkElement) {
|
|
25126
25409
|
return;
|
|
25127
25410
|
}
|
|
25128
|
-
|
|
25129
|
-
return;
|
|
25130
|
-
}
|
|
25131
|
-
const href = linkElement.href;
|
|
25132
|
-
const { isEmpty, isCurrent, isSameOrigin, isAnchor } =
|
|
25133
|
-
getHrefTargetInfo(href);
|
|
25134
|
-
if (isEmpty || !isSameOrigin) {
|
|
25135
|
-
// Let link to other origins be handled by the browser
|
|
25136
|
-
return;
|
|
25137
|
-
}
|
|
25138
|
-
if (isAnchor) {
|
|
25139
|
-
// Fragment navigation belongs to the browser: it owns the indicated
|
|
25140
|
-
// part of the document, and taking it over would cost `:target` and the
|
|
25141
|
-
// focus handling that come with it.
|
|
25142
|
-
if (isCurrent) {
|
|
25143
|
-
// Except this one, which the browser answers with a scroll and
|
|
25144
|
-
// nothing else: same pathname, same hash, so no event and no url
|
|
25145
|
-
// change reaches whoever is waiting on the designated element.
|
|
25146
|
-
rearmUrlTarget();
|
|
25147
|
-
}
|
|
25148
|
-
return;
|
|
25149
|
-
}
|
|
25150
|
-
// Nothing here declared a route, so there is nothing to route to: the
|
|
25151
|
-
// page is a plain document and a link in it is a plain link. Taking it
|
|
25152
|
-
// over anyway would push the url and then have nothing to show for it —
|
|
25153
|
-
// the address bar moves and the page does not (see applyRouting's own
|
|
25154
|
-
// "not called yet" branch, which is where that used to end up).
|
|
25155
|
-
if (!isRouting()) {
|
|
25156
|
-
return;
|
|
25157
|
-
}
|
|
25158
|
-
e.preventDefault();
|
|
25159
|
-
handleRoutingTask(href, {
|
|
25160
|
-
reason: `"click" on a[href="${href}"]`,
|
|
25161
|
-
// A link that takes the place of the current entry instead of stacking
|
|
25162
|
-
// on it says so on itself (see link_replace.js).
|
|
25163
|
-
navigationType: linkAsksForReplace(linkElement) ? "replace" : "push",
|
|
25164
|
-
// Who started it. Announced with the navigation because a press
|
|
25165
|
-
// carries things the url does not: what a link asks of a route
|
|
25166
|
-
// transition is the first of them (see route_transition.jsx). Read by
|
|
25167
|
-
// whoever knows what to do with it, and it is the anchor itself —
|
|
25168
|
-
// resolved here, where it already is.
|
|
25169
|
-
element: linkElement,
|
|
25170
|
-
});
|
|
25411
|
+
linkElement.addEventListener("click", onLinkClick);
|
|
25171
25412
|
},
|
|
25172
25413
|
{ capture: true },
|
|
25173
25414
|
);
|
|
@@ -29529,7 +29770,25 @@ registerNaviCommand("--navi-nav-to", (source, event, { argument }) => {
|
|
|
29529
29770
|
const target = resolveExplicitTarget(source) || source;
|
|
29530
29771
|
return {
|
|
29531
29772
|
target,
|
|
29532
|
-
|
|
29773
|
+
// Which way there is worn by the source, as a link wears it (see
|
|
29774
|
+
// link_replace.js): `<Button command="--navi-nav-to:/done" replace>`.
|
|
29775
|
+
implementation: () =>
|
|
29776
|
+
navTo(
|
|
29777
|
+
argument,
|
|
29778
|
+
linkAsksForReplace(source) ? { replace: true } : undefined,
|
|
29779
|
+
),
|
|
29780
|
+
};
|
|
29781
|
+
});
|
|
29782
|
+
|
|
29783
|
+
// Back to the screen the reader came from, with somewhere to land when there
|
|
29784
|
+
// is none of ours behind — a url opened cold (see navBack). The fallback is
|
|
29785
|
+
// the argument for the reason --navi-nav-to's destination is: it says WHAT the
|
|
29786
|
+
// command does when there is nothing to go back to.
|
|
29787
|
+
registerNaviCommand("--navi-nav-back", (source, event, { argument }) => {
|
|
29788
|
+
const target = resolveExplicitTarget(source) || source;
|
|
29789
|
+
return {
|
|
29790
|
+
target,
|
|
29791
|
+
implementation: () => navBack({ fallback: argument }),
|
|
29533
29792
|
};
|
|
29534
29793
|
});
|
|
29535
29794
|
|
|
@@ -29589,7 +29848,11 @@ registerNaviCommand("--navi-open", (source, event, { anchor, value } = {}) => {
|
|
|
29589
29848
|
},
|
|
29590
29849
|
};
|
|
29591
29850
|
});
|
|
29592
|
-
|
|
29851
|
+
// "--navi-close:all" closes every expandable above the source, nearest first —
|
|
29852
|
+
// a link leaving from a badge shown over a sheet leaves both. A surface that
|
|
29853
|
+
// refuses (a form asking about its changes) keeps what is above it open too:
|
|
29854
|
+
// one cannot be out of the sheet while still in the badge.
|
|
29855
|
+
registerNaviCommand("--navi-close", (source, event, { argument }) => {
|
|
29593
29856
|
const target =
|
|
29594
29857
|
resolveExplicitTarget(source) || resolveClosestExpandable(source);
|
|
29595
29858
|
if (!target) {
|
|
@@ -29598,13 +29861,30 @@ registerNaviCommand("--navi-close", (source, event) => {
|
|
|
29598
29861
|
return {
|
|
29599
29862
|
target,
|
|
29600
29863
|
implementation: () => {
|
|
29601
|
-
|
|
29602
|
-
|
|
29603
|
-
|
|
29604
|
-
}
|
|
29864
|
+
const detail = { event, source: resolveCommandProxySource(source) };
|
|
29865
|
+
if (argument === "all") {
|
|
29866
|
+
return requestCloseUpward(target, detail);
|
|
29867
|
+
}
|
|
29868
|
+
return dispatchCustomEvent(target, "navi_request_close", detail);
|
|
29605
29869
|
},
|
|
29606
29870
|
};
|
|
29607
29871
|
});
|
|
29872
|
+
const requestCloseUpward = (target, detail) => {
|
|
29873
|
+
let expandable = target;
|
|
29874
|
+
while (expandable) {
|
|
29875
|
+
const closing = dispatchCustomEvent(
|
|
29876
|
+
expandable,
|
|
29877
|
+
"navi_request_close",
|
|
29878
|
+
detail,
|
|
29879
|
+
);
|
|
29880
|
+
if (!closing) {
|
|
29881
|
+
return false;
|
|
29882
|
+
}
|
|
29883
|
+
const parent = expandable.parentElement;
|
|
29884
|
+
expandable = parent ? parent.closest("[aria-expanded]") : null;
|
|
29885
|
+
}
|
|
29886
|
+
return true;
|
|
29887
|
+
};
|
|
29608
29888
|
registerNaviCommand("--navi-cancel", (source, event) => {
|
|
29609
29889
|
const target =
|
|
29610
29890
|
resolveExplicitTarget(source) || resolveClosestExpandable(source);
|
|
@@ -33296,6 +33576,21 @@ const useControlProps = (props, {
|
|
|
33296
33576
|
return defaultAction !== "type" && defaultAction !== "value_change";
|
|
33297
33577
|
};
|
|
33298
33578
|
if (controlType === "link") {
|
|
33579
|
+
// Nothing waits for a link's action: the press is a departure, and
|
|
33580
|
+
// the navigation goes whatever the action does — so its command goes
|
|
33581
|
+
// too, unlike a button's, which follows its action (see below).
|
|
33582
|
+
const onLinkInteractionAllowed = e => {
|
|
33583
|
+
triggerUIAction(e);
|
|
33584
|
+
const control = ref.current;
|
|
33585
|
+
if (!control) {
|
|
33586
|
+
return;
|
|
33587
|
+
}
|
|
33588
|
+
tryActionAfterInteractionAllowed(control, {
|
|
33589
|
+
event: e,
|
|
33590
|
+
action: boundAction,
|
|
33591
|
+
requester: control
|
|
33592
|
+
});
|
|
33593
|
+
};
|
|
33299
33594
|
return {
|
|
33300
33595
|
keyDown: e => {
|
|
33301
33596
|
if (isSpaceToActivate(e)) {
|
|
@@ -33309,11 +33604,19 @@ const useControlProps = (props, {
|
|
|
33309
33604
|
}
|
|
33310
33605
|
};
|
|
33311
33606
|
}
|
|
33607
|
+
if (getKeyboardEventDefaultAction(e) === "activate") {
|
|
33608
|
+
// Enter: the browser presses the link itself, with a click that
|
|
33609
|
+
// follows this keydown — and the click reaction below is where
|
|
33610
|
+
// the press is answered, once. A tab for a slide (no href) gets
|
|
33611
|
+
// no such click; Link answers Enter on its own there.
|
|
33612
|
+
return null;
|
|
33613
|
+
}
|
|
33312
33614
|
return keyDownDefault(e);
|
|
33313
33615
|
},
|
|
33314
33616
|
click: e => {
|
|
33315
33617
|
return {
|
|
33316
33618
|
name: "click",
|
|
33619
|
+
allowed: () => onLinkInteractionAllowed(e),
|
|
33317
33620
|
prevented: () => {
|
|
33318
33621
|
e.preventDefault();
|
|
33319
33622
|
}
|
|
@@ -33541,6 +33844,15 @@ const useControlProps = (props, {
|
|
|
33541
33844
|
};
|
|
33542
33845
|
if (isRadio) {
|
|
33543
33846
|
if (checked) {
|
|
33847
|
+
if (props.deselectable) {
|
|
33848
|
+
return {
|
|
33849
|
+
name: "enter to uncheck radio",
|
|
33850
|
+
allowed: () => dispatchRequestSetUIState(inputEl, undefined, {
|
|
33851
|
+
event: e
|
|
33852
|
+
}),
|
|
33853
|
+
always
|
|
33854
|
+
};
|
|
33855
|
+
}
|
|
33544
33856
|
return {
|
|
33545
33857
|
name: "enter on checked radio",
|
|
33546
33858
|
allowed: () => triggerUIAction(e),
|
|
@@ -33566,6 +33878,14 @@ const useControlProps = (props, {
|
|
|
33566
33878
|
if (isRadio && e.key === " ") {
|
|
33567
33879
|
const inputEl = e.currentTarget;
|
|
33568
33880
|
if (inputEl.checked) {
|
|
33881
|
+
if (props.deselectable) {
|
|
33882
|
+
return {
|
|
33883
|
+
name: "space to uncheck radio",
|
|
33884
|
+
allowed: () => dispatchRequestSetUIState(inputEl, undefined, {
|
|
33885
|
+
event: e
|
|
33886
|
+
})
|
|
33887
|
+
};
|
|
33888
|
+
}
|
|
33569
33889
|
// allow space to still trigger uiState and commands
|
|
33570
33890
|
// on checked radios (won't update the ui state but will notify of interaction)
|
|
33571
33891
|
return {
|
|
@@ -33591,6 +33911,20 @@ const useControlProps = (props, {
|
|
|
33591
33911
|
// that lands on the <label> (not the <input>) never fires "mousedown" on
|
|
33592
33912
|
// the input at all, so a DOM-snapshot-at-mousedown approach misses it.
|
|
33593
33913
|
if (isRadio && uiStateController.uiState !== undefined) {
|
|
33914
|
+
if (props.deselectable) {
|
|
33915
|
+
// Not prevented: a prevented click has the browser put the
|
|
33916
|
+
// radio back to checked once the listeners are done. Left to
|
|
33917
|
+
// run, it changes nothing (the radio was checked already) and
|
|
33918
|
+
// reports nothing — a radio getting unchecked fires no input
|
|
33919
|
+
// event — so this is the one place the uncheck is said.
|
|
33920
|
+
return {
|
|
33921
|
+
name: `click to uncheck radio`,
|
|
33922
|
+
allowed: () => dispatchRequestSetUIState(ref.current, undefined, {
|
|
33923
|
+
event: e
|
|
33924
|
+
}),
|
|
33925
|
+
prevented: () => e.preventDefault()
|
|
33926
|
+
};
|
|
33927
|
+
}
|
|
33594
33928
|
return {
|
|
33595
33929
|
name: `click on checked radio`,
|
|
33596
33930
|
allowed: () => triggerUIAction(e),
|
|
@@ -38584,6 +38918,14 @@ Object.assign(PSEUDO_CLASSES, {
|
|
|
38584
38918
|
* way there changes. What a row of tabs wants — the neighbour is a lateral
|
|
38585
38919
|
* move, not a step deeper, so the whole row weighs one entry and the back
|
|
38586
38920
|
* button leaves by where the reader came in.
|
|
38921
|
+
* @param {string} [props.command] - What the press asks of a control around
|
|
38922
|
+
* the link — `"--navi-close"` on a link that leaves the sheet it is in.
|
|
38923
|
+
* Triggered on the press, before the navigation.
|
|
38924
|
+
* @param {Function} [props.action] - Work the press runs, before the
|
|
38925
|
+
* navigation. Nothing waits for it — not the navigation, not `command`: what
|
|
38926
|
+
* the next page must find has to be written synchronously (a draft in a
|
|
38927
|
+
* signal), and a request goes on its own while the page changes. Work that
|
|
38928
|
+
* decides the destination navigates itself, from a `<Button action>`.
|
|
38587
38929
|
* @param {boolean} [props.preventDefault] - Call `event.preventDefault()` on
|
|
38588
38930
|
* click (navigation suppressed; `onClick` still runs).
|
|
38589
38931
|
* @param {(event: MouseEvent) => void} [props.onClick]
|
|
@@ -38823,9 +39165,12 @@ const LinkPlain = props => {
|
|
|
38823
39165
|
replace: undefined,
|
|
38824
39166
|
"data-navi-route-transition-request": routeTransitionRequest,
|
|
38825
39167
|
...replaceRequest,
|
|
39168
|
+
// The control's own handlers first — the interaction gate, the caller's
|
|
39169
|
+
// onClick/onKeyDown, the command and the action — then what only a link
|
|
39170
|
+
// does. Written over the spread above, so they have to be called here.
|
|
38826
39171
|
onClick: e => {
|
|
38827
|
-
onClick
|
|
38828
|
-
if (slide) {
|
|
39172
|
+
controlHostProps.onClick(e);
|
|
39173
|
+
if (slide && !e.defaultPrevented) {
|
|
38829
39174
|
goToSlide(e.currentTarget, e);
|
|
38830
39175
|
}
|
|
38831
39176
|
if (preventDefault) {
|
|
@@ -38838,7 +39183,7 @@ const LinkPlain = props => {
|
|
|
38838
39183
|
,
|
|
38839
39184
|
|
|
38840
39185
|
onKeyDown: e => {
|
|
38841
|
-
|
|
39186
|
+
controlHostProps.onKeyDown(e);
|
|
38842
39187
|
if (!slide || e.defaultPrevented) {
|
|
38843
39188
|
return;
|
|
38844
39189
|
}
|
|
@@ -38884,7 +39229,7 @@ const LinkPlain = props => {
|
|
|
38884
39229
|
"data-current-effect-shadow": currentEffectShadow ? "" : undefined,
|
|
38885
39230
|
"data-current-indicator-position": currentIndicatorPosition,
|
|
38886
39231
|
"data-anchor": anchor ? "" : undefined,
|
|
38887
|
-
"data-interactive": onClick ? "" : undefined,
|
|
39232
|
+
"data-interactive": onClick || props.command || props.action ? "" : undefined,
|
|
38888
39233
|
"data-reveal-on-interaction": revealOnInteraction ? "" : undefined,
|
|
38889
39234
|
baseClassName: "navi_link",
|
|
38890
39235
|
styleCSSVars: LinkStyleCSSVars,
|
|
@@ -45160,6 +45505,7 @@ const ButtonUI = props => {
|
|
|
45160
45505
|
href,
|
|
45161
45506
|
target,
|
|
45162
45507
|
rel,
|
|
45508
|
+
replace,
|
|
45163
45509
|
// visual
|
|
45164
45510
|
variant,
|
|
45165
45511
|
pressEffect,
|
|
@@ -45206,6 +45552,12 @@ const ButtonUI = props => {
|
|
|
45206
45552
|
...basePseudoState,
|
|
45207
45553
|
":-navi-href-current": innerCurrent
|
|
45208
45554
|
};
|
|
45555
|
+
|
|
45556
|
+
// Worn as an attribute, like a link's (see link_replace.js): read off the
|
|
45557
|
+
// anchor by the click handler, off the source by --navi-nav-to.
|
|
45558
|
+
const replaceRequest = replace ? {
|
|
45559
|
+
[LINK_REPLACE_ATTRIBUTE]: ""
|
|
45560
|
+
} : null;
|
|
45209
45561
|
const visualSelector = ".navi_button_content";
|
|
45210
45562
|
useAccentColorAttributes(ref, null, {
|
|
45211
45563
|
elementSelector: visualSelector
|
|
@@ -45232,11 +45584,11 @@ const ButtonUI = props => {
|
|
|
45232
45584
|
as: as,
|
|
45233
45585
|
href: href,
|
|
45234
45586
|
target: innerTarget,
|
|
45235
|
-
rel: innerRel
|
|
45587
|
+
rel: innerRel,
|
|
45588
|
+
replace: undefined,
|
|
45589
|
+
...replaceRequest,
|
|
45236
45590
|
// Respond with the JS prop value directly so callers (e.g. resolveCommandValue)
|
|
45237
45591
|
// get the original type instead of the DOM-coerced string (e.g. "[object Object]").
|
|
45238
|
-
,
|
|
45239
|
-
|
|
45240
45592
|
onnavi_get_value: e => {
|
|
45241
45593
|
e.detail.respondWith(props.value);
|
|
45242
45594
|
},
|
|
@@ -45454,8 +45806,16 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
|
|
|
45454
45806
|
* @type {import("ignore:preact").FunctionComponent<{
|
|
45455
45807
|
* ownTarget?: boolean | "refuse" | "always",
|
|
45456
45808
|
* emojiAsIcon?: boolean,
|
|
45809
|
+
* replace?: boolean,
|
|
45457
45810
|
* [key: string]: any,
|
|
45458
45811
|
* }>}
|
|
45812
|
+
* @param {boolean} [replace] Go where the press leads — an `href`, a
|
|
45813
|
+
* `--navi-nav-to` command — by TAKING THE PLACE of the current history entry
|
|
45814
|
+
* rather than stacking on it: what `<Link replace>` says, for a press drawn
|
|
45815
|
+
* as a button.
|
|
45816
|
+
* @param {Function} [action] On a button with an `href` or a `route`, the
|
|
45817
|
+
* same order as a Link's: it runs on the press, before the navigation, and
|
|
45818
|
+
* the navigation does not wait for it (see Link's `action`).
|
|
45459
45819
|
* @param {boolean} [emojiAsIcon=true] Renders the emoji of the label as icons
|
|
45460
45820
|
* so the button keeps the height of its text — `Text`'s prop, on by default
|
|
45461
45821
|
* here. Pass `false` to let an emoji draw at its natural size.
|
|
@@ -51465,7 +51825,8 @@ const FOCUS_DELAY_ON_KEYBOARD_MS = 250;
|
|
|
51465
51825
|
* - `open()`: requests opening — calls the caller's `onOpen` (see below), then
|
|
51466
51826
|
* `mountContent`/`openEffect`, then `openHandler`.
|
|
51467
51827
|
* - `requestClose()`: requests closing — calls `onRequestClose` then `onClose`,
|
|
51468
|
-
* stopping after the first if denied. The popup may choose to stay open
|
|
51828
|
+
* stopping after the first if denied. The popup may choose to stay open,
|
|
51829
|
+
* which is what a `false` return says (`true`: closed, or closed already).
|
|
51469
51830
|
* - `close()`: closes for real — calls only `onClose`, skipping
|
|
51470
51831
|
* `onRequestClose` entirely. Used when there really is no choice (e.g. the
|
|
51471
51832
|
* popup unmounting).
|
|
@@ -51758,7 +52119,7 @@ const createOpenController = (
|
|
|
51758
52119
|
detail,
|
|
51759
52120
|
) => {
|
|
51760
52121
|
if (!controller.opened) {
|
|
51761
|
-
return;
|
|
52122
|
+
return true;
|
|
51762
52123
|
}
|
|
51763
52124
|
const requestCloseEvent = new CustomEvent("navi_request_close", {
|
|
51764
52125
|
detail: { event: e, ...detail },
|
|
@@ -51773,9 +52134,10 @@ const createOpenController = (
|
|
|
51773
52134
|
if (nativeCancelEvent) {
|
|
51774
52135
|
nativeCancelEvent.preventDefault();
|
|
51775
52136
|
}
|
|
51776
|
-
return;
|
|
52137
|
+
return false;
|
|
51777
52138
|
}
|
|
51778
52139
|
performClose(requestCloseEvent);
|
|
52140
|
+
return true;
|
|
51779
52141
|
},
|
|
51780
52142
|
close: (e = new CustomEvent("programmatic", { detail: {} }), detail) => {
|
|
51781
52143
|
if (!controller.opened) {
|
|
@@ -53342,10 +53704,14 @@ const UncontrolledDialog = props => {
|
|
|
53342
53704
|
});
|
|
53343
53705
|
},
|
|
53344
53706
|
onnavi_request_close: e => {
|
|
53345
|
-
openController.requestClose(e, {
|
|
53707
|
+
const closing = openController.requestClose(e, {
|
|
53346
53708
|
isCancel: e.detail?.isCancel,
|
|
53347
53709
|
requester: e.detail?.source
|
|
53348
53710
|
});
|
|
53711
|
+
if (!closing) {
|
|
53712
|
+
// Said back to whoever asked: --navi-close:all stops climbing here.
|
|
53713
|
+
e.preventDefault();
|
|
53714
|
+
}
|
|
53349
53715
|
}
|
|
53350
53716
|
});
|
|
53351
53717
|
};
|
|
@@ -54792,9 +55158,13 @@ const UncontrolledPopover = props => {
|
|
|
54792
55158
|
});
|
|
54793
55159
|
},
|
|
54794
55160
|
onnavi_request_close: e => {
|
|
54795
|
-
openController.requestClose(e, {
|
|
55161
|
+
const closing = openController.requestClose(e, {
|
|
54796
55162
|
isCancel: e.detail?.isCancel
|
|
54797
55163
|
});
|
|
55164
|
+
if (!closing) {
|
|
55165
|
+
// Said back to whoever asked: --navi-close:all stops climbing here.
|
|
55166
|
+
e.preventDefault();
|
|
55167
|
+
}
|
|
54798
55168
|
}
|
|
54799
55169
|
});
|
|
54800
55170
|
};
|
|
@@ -56105,6 +56475,43 @@ const PickerCustomResolver = props => {
|
|
|
56105
56475
|
...props
|
|
56106
56476
|
});
|
|
56107
56477
|
}
|
|
56478
|
+
if (props.mode === "callout") {
|
|
56479
|
+
// A tooltip is an icon one presses, unless told otherwise. Own-property
|
|
56480
|
+
// rather than undefined: an explicit variant={undefined} asks for the
|
|
56481
|
+
// field-like drawing back. "circle" is the icon variant with the status
|
|
56482
|
+
// icon drawn round — a word about the trigger, not a drawing of its own.
|
|
56483
|
+
if (!Object.hasOwn(props, "variant")) {
|
|
56484
|
+
props.variant = "icon";
|
|
56485
|
+
}
|
|
56486
|
+
const circle = props.variant === "circle";
|
|
56487
|
+
if (circle) {
|
|
56488
|
+
props.variant = "icon";
|
|
56489
|
+
}
|
|
56490
|
+
// A door, never a field (see `allowNameless`): the form around it expects
|
|
56491
|
+
// no value from it, and no name.
|
|
56492
|
+
props.allowNameless = true;
|
|
56493
|
+
// A word in a sentence asks for a plain tooltip — no icon in the callout,
|
|
56494
|
+
// no status color; an icon one presses is the callout's own status icon,
|
|
56495
|
+
// and says "info" like the callout it opens.
|
|
56496
|
+
if (props.calloutStatus === undefined) {
|
|
56497
|
+
props.calloutStatus = props.variant === "text" ? "none" : "info";
|
|
56498
|
+
}
|
|
56499
|
+
if (props.calloutIcon === undefined) {
|
|
56500
|
+
props.calloutIcon = props.variant !== "text";
|
|
56501
|
+
}
|
|
56502
|
+
if (props.rightSlotIcon === undefined) {
|
|
56503
|
+
props.rightSlotIcon = jsx(CalloutStatusIcon, {
|
|
56504
|
+
status: props.calloutStatus,
|
|
56505
|
+
shape: circle ? "circle" : "square"
|
|
56506
|
+
});
|
|
56507
|
+
}
|
|
56508
|
+
// The arrow on the middle of what was pressed — an icon, a word — rather
|
|
56509
|
+
// than on where its text starts, which is where a callout points at a
|
|
56510
|
+
// field by default (see the anchor attributes in callout.js).
|
|
56511
|
+
if (props["data-callout-arrow-x"] === undefined) {
|
|
56512
|
+
props["data-callout-arrow-x"] = "center";
|
|
56513
|
+
}
|
|
56514
|
+
}
|
|
56108
56515
|
if (props.type === undefined) {
|
|
56109
56516
|
// A picker with a popup of its own holds whatever the control inside it
|
|
56110
56517
|
// holds — a boolean, a number, an id — and a field with no type is read
|
|
@@ -56432,7 +56839,10 @@ const PickerCustom = props => {
|
|
|
56432
56839
|
dispatchCustomEvent(popupRef.current, "navi_request_open", e.detail);
|
|
56433
56840
|
},
|
|
56434
56841
|
"onnavi_request_close": e => {
|
|
56435
|
-
dispatchCustomEvent(popupRef.current, "navi_request_close", e.detail);
|
|
56842
|
+
const closing = dispatchCustomEvent(popupRef.current, "navi_request_close", e.detail);
|
|
56843
|
+
if (!closing) {
|
|
56844
|
+
e.preventDefault();
|
|
56845
|
+
}
|
|
56436
56846
|
},
|
|
56437
56847
|
children
|
|
56438
56848
|
});
|
|
@@ -56448,7 +56858,9 @@ const PickerCustom = props => {
|
|
|
56448
56858
|
// opens anything. Told a value — even an empty one — the picker owns it
|
|
56449
56859
|
// and pushes it down instead, leaving the popup free to build its
|
|
56450
56860
|
// content only when it is first opened (see popup_content_mount.js).
|
|
56451
|
-
|
|
56861
|
+
// A caller who knows better says so with the popup's own props.
|
|
56862
|
+
mountWhenClosed: props.mountWhenClosed ?? !isControlValueGivenByProps(props),
|
|
56863
|
+
unmountWhenClosed: props.unmountWhenClosed,
|
|
56452
56864
|
// Not on pickerProps (the trigger): commands.js's own
|
|
56453
56865
|
// resolveClosestExpandable() does `el.closest("[aria-expanded]")` to
|
|
56454
56866
|
// find where to dispatch navi_request_open/navi_request_close — and
|
|
@@ -56482,12 +56894,17 @@ const PickerCustom = props => {
|
|
|
56482
56894
|
event: e,
|
|
56483
56895
|
intent: "read",
|
|
56484
56896
|
allowed: () => {
|
|
56485
|
-
requestClose(e, {
|
|
56897
|
+
const closing = requestClose(e, {
|
|
56486
56898
|
isCancel: e.detail.isCancel
|
|
56487
56899
|
});
|
|
56900
|
+
if (!closing) {
|
|
56901
|
+
e.preventDefault();
|
|
56902
|
+
}
|
|
56488
56903
|
},
|
|
56489
56904
|
prevented: () => {
|
|
56490
56905
|
confirmEventRef.current = null;
|
|
56906
|
+
// Not closing either way; said back to whoever asked.
|
|
56907
|
+
e.preventDefault();
|
|
56491
56908
|
}
|
|
56492
56909
|
});
|
|
56493
56910
|
},
|
|
@@ -56687,6 +57104,9 @@ const PickerContentInsidePopup = props => {
|
|
|
56687
57104
|
// but popupProps is built explicitly here, so it only travels if named.
|
|
56688
57105
|
// "popupLayer" rather than "layer": the picker itself is not the popup.
|
|
56689
57106
|
popupLayer,
|
|
57107
|
+
// Same reason: a `data-testid` on the picker names the trigger (see
|
|
57108
|
+
// docs/testid.md) — this one names the popup.
|
|
57109
|
+
popupTestId,
|
|
56690
57110
|
positionArea,
|
|
56691
57111
|
popoverMode = "nearby",
|
|
56692
57112
|
popoverSpacing = popoverMode === "nearby" ? 5 : 0,
|
|
@@ -56708,13 +57128,26 @@ const PickerContentInsidePopup = props => {
|
|
|
56708
57128
|
// marginWithAnchor.
|
|
56709
57129
|
dockedOnSmallTouchScreen,
|
|
56710
57130
|
animation,
|
|
57131
|
+
// mode="callout": what the callout says about what it holds, and paints
|
|
57132
|
+
// in its border and icon — "none" for a plain tooltip (see the callout
|
|
57133
|
+
// defaults in PickerCustomResolver). And whether it wears a cross: without
|
|
57134
|
+
// one it still closes on Escape, a click outside, or a --navi-close of the
|
|
57135
|
+
// content's own.
|
|
57136
|
+
calloutStatus,
|
|
57137
|
+
calloutIcon,
|
|
57138
|
+
calloutCloseButton,
|
|
56711
57139
|
...rest
|
|
56712
57140
|
} = props;
|
|
56713
57141
|
const isPopover = mode === "popover";
|
|
57142
|
+
const isCallout = mode === "callout";
|
|
56714
57143
|
return jsx(Next, {
|
|
56715
57144
|
"aria-haspopup": isPopover ? "listbox" : "dialog",
|
|
56716
57145
|
"navi-popover-mode": isPopover ? popoverMode : undefined,
|
|
56717
57146
|
...rest,
|
|
57147
|
+
// On popupProps already (see the picker's popup assembly); they mean
|
|
57148
|
+
// nothing to the picker element.
|
|
57149
|
+
mountWhenClosed: undefined,
|
|
57150
|
+
unmountWhenClosed: undefined,
|
|
56718
57151
|
onFocusOut: e => {
|
|
56719
57152
|
if (!isPopover || !closeOnFocusOut) {
|
|
56720
57153
|
return;
|
|
@@ -56739,8 +57172,19 @@ const PickerContentInsidePopup = props => {
|
|
|
56739
57172
|
}
|
|
56740
57173
|
});
|
|
56741
57174
|
},
|
|
56742
|
-
children: jsx(
|
|
57175
|
+
children: isCallout ? jsx(PickerCalloutPopup, {
|
|
57176
|
+
...popupProps,
|
|
57177
|
+
pickerRef: props.ref,
|
|
57178
|
+
status: calloutStatus,
|
|
57179
|
+
icon: calloutIcon,
|
|
57180
|
+
closeButton: calloutCloseButton,
|
|
57181
|
+
children: jsx(PopupModeContext.Provider, {
|
|
57182
|
+
value: mode,
|
|
57183
|
+
children: children
|
|
57184
|
+
})
|
|
57185
|
+
}) : jsx(Popup, {
|
|
56743
57186
|
...popupProps,
|
|
57187
|
+
"data-testid": popupTestId,
|
|
56744
57188
|
mode: mode,
|
|
56745
57189
|
layer: popupLayer,
|
|
56746
57190
|
animation: animation,
|
|
@@ -56763,6 +57207,112 @@ const PickerContentInsidePopup = props => {
|
|
|
56763
57207
|
});
|
|
56764
57208
|
};
|
|
56765
57209
|
|
|
57210
|
+
// One token per picker rather than per instance: a callout manager belongs to
|
|
57211
|
+
// one control, so the key only has to be distinct from the other reasons that
|
|
57212
|
+
// control may have to show a callout (a failing constraint, a busy refusal, its
|
|
57213
|
+
// `error` prop). Those keep working on top of this one: opened while the
|
|
57214
|
+
// content is up they take the callout over, and give it back when they go.
|
|
57215
|
+
const PICKER_CALLOUT_CONTENT_TOKEN = createOpenToken();
|
|
57216
|
+
|
|
57217
|
+
/**
|
|
57218
|
+
* The popup of a `mode="callout"` picker: the picker's own callout — the one
|
|
57219
|
+
* its constraints speak in — showing the picker's children instead of a
|
|
57220
|
+
* message. A speech bubble on the trigger, for a tooltip that opens on a press.
|
|
57221
|
+
*
|
|
57222
|
+
* Wired the way Popover and Dialog are, through `openController.openEffect`:
|
|
57223
|
+
* opening adds a token to the picker's callout manager, whose cleanup removes
|
|
57224
|
+
* it. The callout has ways out of its own (its cross, a click outside, Escape,
|
|
57225
|
+
* focus leaving the picker) and says so through the token's `onClose`, which
|
|
57226
|
+
* closes the controller for real — the popup is already gone, there is no
|
|
57227
|
+
* choice left to offer `requestClose`.
|
|
57228
|
+
*
|
|
57229
|
+
* The content is rendered through a portal into an element this component
|
|
57230
|
+
* owns, handed to the callout as its message (a Node, appended as-is). It is
|
|
57231
|
+
* rendered whether the callout is open or not, so what the content holds
|
|
57232
|
+
* survives a close, the way a popup's `mountWhenClosed` keeps it. The element
|
|
57233
|
+
* carries data-picker-content: the callout is appended inside the picker root,
|
|
57234
|
+
* and a press in there must read as inside the popup, not on the trigger.
|
|
57235
|
+
*/
|
|
57236
|
+
const PickerCalloutPopup = ({
|
|
57237
|
+
ref,
|
|
57238
|
+
id,
|
|
57239
|
+
anchor,
|
|
57240
|
+
openController,
|
|
57241
|
+
pickerRef,
|
|
57242
|
+
status,
|
|
57243
|
+
icon,
|
|
57244
|
+
closeButton,
|
|
57245
|
+
onnavi_request_open,
|
|
57246
|
+
onnavi_request_close,
|
|
57247
|
+
onnavi_request_confirm,
|
|
57248
|
+
children
|
|
57249
|
+
}) => {
|
|
57250
|
+
const hostRef = useRef(null);
|
|
57251
|
+
if (!hostRef.current) {
|
|
57252
|
+
const host = document.createElement("div");
|
|
57253
|
+
host.setAttribute("data-picker-content", "");
|
|
57254
|
+
hostRef.current = host;
|
|
57255
|
+
}
|
|
57256
|
+
// Reassigned on every render, like Popover's own, so it closes over the
|
|
57257
|
+
// latest props.
|
|
57258
|
+
openController.openEffect = openEvent => {
|
|
57259
|
+
const pickerEl = pickerRef.current;
|
|
57260
|
+
const calloutManager = getPickerInput(pickerEl).__uiStateController__.rules.callout;
|
|
57261
|
+
// Only an anchor the caller named: left unsaid, the manager anchors on the
|
|
57262
|
+
// picker's own input — which is where the data-callout-* attributes a
|
|
57263
|
+
// caller puts on the picker land, and where the callout reads them.
|
|
57264
|
+
const anchorElement = anchor === pickerRef ? undefined : anchor && "current" in anchor ? anchor.current : anchor;
|
|
57265
|
+
calloutManager.addOpenToken(PICKER_CALLOUT_CONTENT_TOKEN, {
|
|
57266
|
+
message: hostRef.current,
|
|
57267
|
+
// "none" is the picker's word for it; the callout's is no status at all.
|
|
57268
|
+
status: status === "none" ? undefined : status,
|
|
57269
|
+
icon,
|
|
57270
|
+
closeButton,
|
|
57271
|
+
anchorElement,
|
|
57272
|
+
// The request, chained to the press that made it: the callout reads the
|
|
57273
|
+
// mousedown off it to wait for the release before listening for a click
|
|
57274
|
+
// outside — the same gesture's own click would close it otherwise.
|
|
57275
|
+
event: openEvent,
|
|
57276
|
+
// Not skipped: the callout moves the focus into the picker when it is
|
|
57277
|
+
// elsewhere, which is what lets Escape find the callout right away.
|
|
57278
|
+
skipFocus: false,
|
|
57279
|
+
onClose: ({
|
|
57280
|
+
event
|
|
57281
|
+
}) => {
|
|
57282
|
+
openController.close(event);
|
|
57283
|
+
}
|
|
57284
|
+
});
|
|
57285
|
+
// A --navi-confirm said inside the callout is aimed at the callout (its
|
|
57286
|
+
// aria-expanded), not at the element the picker listens on: carried over,
|
|
57287
|
+
// for a confirm picker whose question is a speech bubble.
|
|
57288
|
+
const calloutElement = calloutManager.callout.element;
|
|
57289
|
+
const forwardConfirm = e => {
|
|
57290
|
+
onnavi_request_confirm?.(e);
|
|
57291
|
+
};
|
|
57292
|
+
calloutElement.addEventListener("navi_request_confirm", forwardConfirm);
|
|
57293
|
+
return closeEvent => {
|
|
57294
|
+
calloutElement.removeEventListener("navi_request_confirm", forwardConfirm);
|
|
57295
|
+
calloutManager.removeOpenToken(PICKER_CALLOUT_CONTENT_TOKEN, closeEvent);
|
|
57296
|
+
};
|
|
57297
|
+
};
|
|
57298
|
+
return (
|
|
57299
|
+
// What the picker addresses (aria-controls, the request events it
|
|
57300
|
+
// forwards); the callout itself lives where the callout manager puts it.
|
|
57301
|
+
jsx(Box, {
|
|
57302
|
+
as: "span",
|
|
57303
|
+
ref: ref,
|
|
57304
|
+
id: id,
|
|
57305
|
+
style: {
|
|
57306
|
+
display: "contents"
|
|
57307
|
+
},
|
|
57308
|
+
onnavi_request_open: onnavi_request_open,
|
|
57309
|
+
onnavi_request_close: onnavi_request_close,
|
|
57310
|
+
onnavi_request_confirm: onnavi_request_confirm,
|
|
57311
|
+
children: createPortal(children, hostRef.current)
|
|
57312
|
+
})
|
|
57313
|
+
);
|
|
57314
|
+
};
|
|
57315
|
+
|
|
56766
57316
|
installImportMetaCssBuild(import.meta);/**
|
|
56767
57317
|
* A trigger that asks "are you sure?" before doing what it stands for.
|
|
56768
57318
|
*
|
|
@@ -56811,6 +57361,8 @@ const PickerConfirmResolver = props => {
|
|
|
56811
57361
|
message,
|
|
56812
57362
|
confirmLabel,
|
|
56813
57363
|
cancelLabel,
|
|
57364
|
+
confirmTestId,
|
|
57365
|
+
cancelTestId,
|
|
56814
57366
|
focusOnOpen = "confirm",
|
|
56815
57367
|
// A popover whatever the screen: the question is short and about the
|
|
56816
57368
|
// control it points at, and a sheet sliding up for one sentence is too
|
|
@@ -56854,15 +57406,25 @@ const PickerConfirmResolver = props => {
|
|
|
56854
57406
|
mode: mode,
|
|
56855
57407
|
focusCapture: focusCapture,
|
|
56856
57408
|
openWhileReadOnly: openWhileReadOnly,
|
|
56857
|
-
onConfirm: onConfirm
|
|
57409
|
+
onConfirm: onConfirm
|
|
57410
|
+
// A question holds no value to read before it is asked: the popup is
|
|
57411
|
+
// built on the first open, like that of a picker told its value (see
|
|
57412
|
+
// mountWhenClosed in picker_custom.jsx).
|
|
57413
|
+
,
|
|
57414
|
+
|
|
57415
|
+
mountWhenClosed: false,
|
|
56858
57416
|
message: undefined,
|
|
56859
57417
|
confirmLabel: undefined,
|
|
56860
57418
|
cancelLabel: undefined,
|
|
57419
|
+
confirmTestId: undefined,
|
|
57420
|
+
cancelTestId: undefined,
|
|
56861
57421
|
focusOnOpen: undefined,
|
|
56862
57422
|
children: children === undefined ? jsx(PickerConfirmBody, {
|
|
56863
57423
|
message: message,
|
|
56864
57424
|
confirmLabel: confirmLabel,
|
|
56865
57425
|
cancelLabel: cancelLabel,
|
|
57426
|
+
confirmTestId: confirmTestId,
|
|
57427
|
+
cancelTestId: cancelTestId,
|
|
56866
57428
|
focusOnOpen: focusOnOpen
|
|
56867
57429
|
}) : children
|
|
56868
57430
|
});
|
|
@@ -56871,6 +57433,8 @@ const PickerConfirmBody = ({
|
|
|
56871
57433
|
message,
|
|
56872
57434
|
confirmLabel,
|
|
56873
57435
|
cancelLabel,
|
|
57436
|
+
confirmTestId,
|
|
57437
|
+
cancelTestId,
|
|
56874
57438
|
focusOnOpen
|
|
56875
57439
|
}) => {
|
|
56876
57440
|
return jsxs("div", {
|
|
@@ -56882,10 +57446,12 @@ const PickerConfirmBody = ({
|
|
|
56882
57446
|
children: [jsx(Button, {
|
|
56883
57447
|
command: "--navi-cancel",
|
|
56884
57448
|
autoFocus: focusOnOpen === "cancel",
|
|
57449
|
+
"data-testid": cancelTestId,
|
|
56885
57450
|
children: cancelLabel
|
|
56886
57451
|
}), jsx(Button, {
|
|
56887
57452
|
command: "--navi-confirm",
|
|
56888
57453
|
autoFocus: focusOnOpen === "confirm",
|
|
57454
|
+
"data-testid": confirmTestId,
|
|
56889
57455
|
children: confirmLabel
|
|
56890
57456
|
})]
|
|
56891
57457
|
})]
|
|
@@ -57847,6 +58413,9 @@ installImportMetaCssBuild(import.meta);const css$z = /* css */`
|
|
|
57847
58413
|
}
|
|
57848
58414
|
`;
|
|
57849
58415
|
const SelectableListMultipleContext = createContext(false);
|
|
58416
|
+
// A single-select list whose selected row, pressed again, lets go: what its
|
|
58417
|
+
// rows' radios need to know to answer the press (see `deselectable` on Input).
|
|
58418
|
+
const SelectableListDeselectableContext = createContext(false);
|
|
57850
58419
|
// A row of a selectable list is selectable — the list is what decides, and a
|
|
57851
58420
|
// row says nothing unless it wants out (`selectable={false}` on a row that is
|
|
57852
58421
|
// only there to be read). Also set to false by a non-selectable list, so a list
|
|
@@ -57878,6 +58447,7 @@ const ListSelectable = props => {
|
|
|
57878
58447
|
const {
|
|
57879
58448
|
ref,
|
|
57880
58449
|
multiple,
|
|
58450
|
+
deselectable,
|
|
57881
58451
|
focusGroupDirection,
|
|
57882
58452
|
focusGroupWrap
|
|
57883
58453
|
} = props;
|
|
@@ -58014,6 +58584,7 @@ const ListSelectable = props => {
|
|
|
58014
58584
|
defaultValue: undefined,
|
|
58015
58585
|
selectable: undefined,
|
|
58016
58586
|
multiple: undefined,
|
|
58587
|
+
deselectable: undefined,
|
|
58017
58588
|
focusGroupDirection: undefined,
|
|
58018
58589
|
focusGroupWrap: undefined
|
|
58019
58590
|
// Track focus inside the list: whichever item gets focus becomes current.
|
|
@@ -58136,7 +58707,7 @@ const ListSelectable = props => {
|
|
|
58136
58707
|
if (!currentId) {
|
|
58137
58708
|
return;
|
|
58138
58709
|
}
|
|
58139
|
-
if (multiple) {
|
|
58710
|
+
if (multiple || deselectable) {
|
|
58140
58711
|
const inputId = `${currentId}_input`;
|
|
58141
58712
|
const childController = uiGroupStateController.findChildById(inputId);
|
|
58142
58713
|
const isSelected = childController && childController.uiState;
|
|
@@ -58160,7 +58731,10 @@ const ListSelectable = props => {
|
|
|
58160
58731
|
value: true,
|
|
58161
58732
|
children: jsx(SelectableListMultipleContext.Provider, {
|
|
58162
58733
|
value: multiple,
|
|
58163
|
-
children:
|
|
58734
|
+
children: jsx(SelectableListDeselectableContext.Provider, {
|
|
58735
|
+
value: Boolean(deselectable),
|
|
58736
|
+
children: listVnode
|
|
58737
|
+
})
|
|
58164
58738
|
})
|
|
58165
58739
|
});
|
|
58166
58740
|
};
|
|
@@ -58209,6 +58783,8 @@ const ListItemSelectable = props => {
|
|
|
58209
58783
|
...rest
|
|
58210
58784
|
} = props;
|
|
58211
58785
|
const multiple = useContext(SelectableListMultipleContext);
|
|
58786
|
+
// A checkbox toggles on its own; only a radio has to be told it may let go.
|
|
58787
|
+
const deselectable = useContext(SelectableListDeselectableContext) && !multiple;
|
|
58212
58788
|
// Whose reason it is that this row cannot be taken. Read-only reaching it
|
|
58213
58789
|
// from above is the LIST's, and what is settled is then the whole answer —
|
|
58214
58790
|
// said as the selection where several things are taken, as the choice where
|
|
@@ -58231,6 +58807,7 @@ const ListItemSelectable = props => {
|
|
|
58231
58807
|
ref: inputRef,
|
|
58232
58808
|
id: inputId,
|
|
58233
58809
|
type: inputType,
|
|
58810
|
+
deselectable,
|
|
58234
58811
|
defaultChecked: defaultSelected,
|
|
58235
58812
|
...(hasSelectedProp ? {
|
|
58236
58813
|
checked: selected
|
|
@@ -58249,9 +58826,10 @@ const ListItemSelectable = props => {
|
|
|
58249
58826
|
type: inputType,
|
|
58250
58827
|
checked,
|
|
58251
58828
|
readOnly,
|
|
58252
|
-
value
|
|
58829
|
+
value,
|
|
58830
|
+
deselectable
|
|
58253
58831
|
};
|
|
58254
|
-
}, [inputId, inputType, checked, readOnly, value]);
|
|
58832
|
+
}, [inputId, inputType, checked, readOnly, value, deselectable]);
|
|
58255
58833
|
return jsxs(Next, {
|
|
58256
58834
|
id: id,
|
|
58257
58835
|
index: index,
|
|
@@ -59489,6 +60067,7 @@ const ListFirstResolver = props => {
|
|
|
59489
60067
|
* @type {import("ignore:preact").FunctionComponent<{
|
|
59490
60068
|
* selectable?: boolean,
|
|
59491
60069
|
* multiple?: boolean,
|
|
60070
|
+
* deselectable?: boolean,
|
|
59492
60071
|
* maxLength?: number,
|
|
59493
60072
|
* maxLengthGuard?: number,
|
|
59494
60073
|
* action?: (value: any) => void,
|
|
@@ -59613,6 +60192,11 @@ const ListFirstResolver = props => {
|
|
|
59613
60192
|
* Pass `true` for a list whose rows must stay live under the pointer while
|
|
59614
60193
|
* it scrolls. The trade of the default is the mirror one: right after a
|
|
59615
60194
|
* scroll, the row under the pointer lights up only once the pointer moves.
|
|
60195
|
+
* @param {boolean} [props.deselectable]
|
|
60196
|
+
* A single-select list allowed to hold nothing: the selected row, pressed
|
|
60197
|
+
* again, lets go. Without it the list is a radio group — a choice, once
|
|
60198
|
+
* made, moves to another row but never goes away. A `multiple` list toggles
|
|
60199
|
+
* its rows already.
|
|
59616
60200
|
* @param {number} [props.maxLength]
|
|
59617
60201
|
* How many items a `selectable multiple` list accepts — the same word, and
|
|
59618
60202
|
* the same behaviour, as `maxLength` on a text field: a rule the list is
|
|
@@ -64625,6 +65209,31 @@ installImportMetaCssBuild(import.meta);const css$u = /* css */`
|
|
|
64625
65209
|
|
|
64626
65210
|
text-align: center;
|
|
64627
65211
|
}
|
|
65212
|
+
/* text: a word in a sentence, marked by the dotted line under it — the way
|
|
65213
|
+
a term one can ask about is marked. No box, no slot; the font is the
|
|
65214
|
+
sentence's own, so the word sits in its line like the ones around it. */
|
|
65215
|
+
&[data-variant="text"] {
|
|
65216
|
+
--picker-padding-x-default: 0;
|
|
65217
|
+
--picker-padding-y-default: 0;
|
|
65218
|
+
--picker-border-width: 0px; /* must carry a unit (px) — used in calc() to offset the custom input overlay */
|
|
65219
|
+
--picker-border-color: transparent;
|
|
65220
|
+
--picker-border-color-hover: var(--picker-border-color);
|
|
65221
|
+
--picker-border-color-readonly: var(--picker-border-color);
|
|
65222
|
+
--picker-border-color-disabled: var(--picker-border-color);
|
|
65223
|
+
--picker-background-color: transparent;
|
|
65224
|
+
--picker-background-color-hover: var(--picker-background-color);
|
|
65225
|
+
--picker-background-color-readonly: var(--picker-background-color);
|
|
65226
|
+
--picker-background-color-disabled: var(--picker-background-color);
|
|
65227
|
+
|
|
65228
|
+
font-size: inherit;
|
|
65229
|
+
font-family: inherit;
|
|
65230
|
+
text-decoration: underline dotted;
|
|
65231
|
+
text-underline-offset: 0.2em;
|
|
65232
|
+
|
|
65233
|
+
&[data-hover] {
|
|
65234
|
+
text-decoration-style: solid;
|
|
65235
|
+
}
|
|
65236
|
+
}
|
|
64628
65237
|
}
|
|
64629
65238
|
`;
|
|
64630
65239
|
const PickerButton = props => {
|
|
@@ -64668,6 +65277,8 @@ const PickerButton = props => {
|
|
|
64668
65277
|
error
|
|
64669
65278
|
} = props;
|
|
64670
65279
|
const isSingleLine = maxLines === 1;
|
|
65280
|
+
// Same rule as the root: phrasing content inside a sentence.
|
|
65281
|
+
const ContentTag = variant === "text" ? "span" : "div";
|
|
64671
65282
|
const inputRef = useRef(null);
|
|
64672
65283
|
const [pickerRemainingProps, inputProps, facadeChildrenProps] = useControlFacadeProps({
|
|
64673
65284
|
...props,
|
|
@@ -64705,8 +65316,11 @@ const PickerButton = props => {
|
|
|
64705
65316
|
as a value nobody may change. */
|
|
64706
65317
|
jsx(ReadOnlyContext.Provider, {
|
|
64707
65318
|
value: readOnlyResolved,
|
|
64708
|
-
children: jsxs(Box
|
|
64709
|
-
|
|
65319
|
+
children: jsxs(Box
|
|
65320
|
+
// A word in a sentence (variant="text") sits in a <p>, where a <div> is
|
|
65321
|
+
// not allowed: phrasing content there, block content elsewhere.
|
|
65322
|
+
, {
|
|
65323
|
+
as: variant === "text" ? "span" : "div",
|
|
64710
65324
|
ref: ref
|
|
64711
65325
|
// The flow this element really has (.navi_picker is display:inline-flex).
|
|
64712
65326
|
// Left unsaid, Box reads a <div> as block and resolves alignX into a
|
|
@@ -64856,7 +65470,7 @@ const PickerButton = props => {
|
|
|
64856
65470
|
// picker behind it is.
|
|
64857
65471
|
,
|
|
64858
65472
|
|
|
64859
|
-
"navi-placeholder": variant !== "button" && uiStateHoldsNothing(value) ? "" : undefined,
|
|
65473
|
+
"navi-placeholder": variant !== "button" && variant !== "text" && uiStateHoldsNothing(value) ? "" : undefined,
|
|
64860
65474
|
maxLines: maxLines,
|
|
64861
65475
|
children: jsx(PickerOwnContent, {
|
|
64862
65476
|
children: jsx(PickerContext.Provider, {
|
|
@@ -64880,7 +65494,7 @@ const PickerButton = props => {
|
|
|
64880
65494
|
})
|
|
64881
65495
|
})
|
|
64882
65496
|
})
|
|
64883
|
-
}), variant === "icon" || variant === "headless" || variant === "button" || ui === "default" ? null : jsx("span", {
|
|
65497
|
+
}), variant === "icon" || variant === "headless" || variant === "button" || variant === "text" || ui === "default" ? null : jsx("span", {
|
|
64884
65498
|
className: "navi_picker_right_slot",
|
|
64885
65499
|
children: jsx(PickerOwnContent, {
|
|
64886
65500
|
children: clearable && interactive && value !== undefined && value !== "" && clearConfirm !== undefined ?
|
|
@@ -64946,7 +65560,7 @@ const PickerButton = props => {
|
|
|
64946
65560
|
})]
|
|
64947
65561
|
}), jsx(ControlFacadeChildrenWrapper, {
|
|
64948
65562
|
...facadeChildrenProps,
|
|
64949
|
-
children: jsx(
|
|
65563
|
+
children: jsx(ContentTag, {
|
|
64950
65564
|
className: "navi_picker_content",
|
|
64951
65565
|
"data-picker-content": "",
|
|
64952
65566
|
children: children
|
|
@@ -68027,6 +68641,7 @@ const css$n = /* css */`
|
|
|
68027
68641
|
* pointerInteractionOutsideEffect?: "close" | "cancel" | "capture",
|
|
68028
68642
|
* escapeEffect?: "cancel" | "close",
|
|
68029
68643
|
* popupLayer?: "top" | "local",
|
|
68644
|
+
* popupTestId?: string,
|
|
68030
68645
|
* [key: string]: any,
|
|
68031
68646
|
* }>}
|
|
68032
68647
|
* @param {Array<{value: any, label: import("ignore:preact").ComponentChildren}>} options
|
|
@@ -68068,7 +68683,7 @@ const css$n = /* css */`
|
|
|
68068
68683
|
* `dockedOnSmallTouchScreen`, `dialogExpand*`, `dialogMinWidth`/`Height`,
|
|
68069
68684
|
* `dialogMaxWidth`/`Height`,
|
|
68070
68685
|
* `marginWithContainer`, `popoverMode`, `popoverSpacing`, `popupLayer`,
|
|
68071
|
-
* `popupWidthFitContent`, `popoverMaxHeight`, `backdropVariant`,
|
|
68686
|
+
* `popupTestId`, `popupWidthFitContent`, `popoverMaxHeight`, `backdropVariant`,
|
|
68072
68687
|
* `pointerInteractionOutsideEffect`, `escapeEffect`, `closeOnFocusOut`,
|
|
68073
68688
|
* `scrollCapture`, `focusCapture`, `popupBackgroundColor`,
|
|
68074
68689
|
* `popupBorderRadius`, `animation`. See picker.jsx for what each one says.
|
|
@@ -68270,7 +68885,7 @@ const SplitButton = props => {
|
|
|
68270
68885
|
// What the Picker's popup answers to — Picker's own popup props, named here so
|
|
68271
68886
|
// a caller reaches all of them through the split button (see picker.jsx's JSDoc
|
|
68272
68887
|
// for what each one says).
|
|
68273
|
-
const POPUP_PROP_SET = new Set(["mode", "popupLayer", "positionArea", "popoverMode", "popoverSpacing", "popupWidthFitContent", "popoverMaxHeight", "dialogMinWidth", "dialogMinHeight", "dialogMaxWidth", "dialogMaxHeight", "dialogExpand", "dialogExpandX", "dialogExpandY", "dockedOnSmallTouchScreen", "marginWithContainer", "backdropVariant", "pointerInteractionOutsideEffect", "escapeEffect", "closeOnFocusOut", "scrollCapture", "focusCapture", "popupBackgroundColor", "popupBorderRadius", "animation"]);
|
|
68888
|
+
const POPUP_PROP_SET = new Set(["mode", "popupLayer", "popupTestId", "positionArea", "popoverMode", "popoverSpacing", "popupWidthFitContent", "popoverMaxHeight", "dialogMinWidth", "dialogMinHeight", "dialogMaxWidth", "dialogMaxHeight", "dialogExpand", "dialogExpandX", "dialogExpandY", "dockedOnSmallTouchScreen", "marginWithContainer", "backdropVariant", "pointerInteractionOutsideEffect", "escapeEffect", "closeOnFocusOut", "scrollCapture", "focusCapture", "popupBackgroundColor", "popupBorderRadius", "animation"]);
|
|
68274
68889
|
const splitPopupProps = props => {
|
|
68275
68890
|
const popupProps = {};
|
|
68276
68891
|
const boxProps = {};
|
|
@@ -75869,7 +76484,7 @@ const ErrorSvg = () => {
|
|
|
75869
76484
|
})
|
|
75870
76485
|
});
|
|
75871
76486
|
};
|
|
75872
|
-
const InfoSvg = () => {
|
|
76487
|
+
const InfoSvg$1 = () => {
|
|
75873
76488
|
return jsx("svg", {
|
|
75874
76489
|
viewBox: "0 0 16 16",
|
|
75875
76490
|
fill: "currentColor",
|
|
@@ -75952,7 +76567,7 @@ const MessageBox = ({
|
|
|
75952
76567
|
const [hasTitleChild, setHasTitleChild] = useState(false);
|
|
75953
76568
|
const innerLeftStripe = leftStripe === undefined ? hasTitleChild : leftStripe;
|
|
75954
76569
|
if (icon === true) {
|
|
75955
|
-
icon = status === "info" ? jsx(InfoSvg, {}) : status === "success" ? jsx(SuccessSvg, {}) : status === "warning" ? jsx(WarningSvg, {}) : status === "error" ? jsx(ErrorSvg, {}) : null;
|
|
76570
|
+
icon = status === "info" ? jsx(InfoSvg$1, {}) : status === "success" ? jsx(SuccessSvg, {}) : status === "warning" ? jsx(WarningSvg, {}) : status === "error" ? jsx(ErrorSvg, {}) : null;
|
|
75956
76571
|
} else if (typeof icon === "function") {
|
|
75957
76572
|
const Comp = icon;
|
|
75958
76573
|
icon = jsx(Comp, {});
|
|
@@ -78026,6 +78641,29 @@ const ExclamationSvg = () => {
|
|
|
78026
78641
|
});
|
|
78027
78642
|
};
|
|
78028
78643
|
|
|
78644
|
+
const InfoSvg = () => {
|
|
78645
|
+
return jsxs("svg", {
|
|
78646
|
+
viewBox: "0 0 24 24",
|
|
78647
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
78648
|
+
children: [jsx("circle", {
|
|
78649
|
+
cx: "12",
|
|
78650
|
+
cy: "12",
|
|
78651
|
+
r: "10",
|
|
78652
|
+
fill: "none",
|
|
78653
|
+
stroke: "currentColor",
|
|
78654
|
+
strokeWidth: "2"
|
|
78655
|
+
}), jsx("circle", {
|
|
78656
|
+
cx: "12",
|
|
78657
|
+
cy: "7.6",
|
|
78658
|
+
r: "1.4",
|
|
78659
|
+
fill: "currentColor"
|
|
78660
|
+
}), jsx("path", {
|
|
78661
|
+
fill: "currentColor",
|
|
78662
|
+
d: "M10.6 10.6h2.8V18h-2.8z"
|
|
78663
|
+
})]
|
|
78664
|
+
});
|
|
78665
|
+
};
|
|
78666
|
+
|
|
78029
78667
|
const EyeClosedSvg = () => {
|
|
78030
78668
|
return jsx("svg", {
|
|
78031
78669
|
viewBox: "0 0 24 24",
|
|
@@ -78101,5 +78739,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
78101
78739
|
})
|
|
78102
78740
|
});
|
|
78103
78741
|
|
|
78104
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, Expandable, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTransitionArea, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, SplitButton, StarSvg, Step, StepList, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeRangeWheel, TimeSpin, TimeWheel, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, canNavBackSignal, canNavForwardSignal, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineRouteDefaultTransition, defineRouteTransition, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markErrorAsDisplayedBy, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, renderEmojiAsIcon, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCanNavBack, useCanNavForward, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
78742
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, CalloutStatusIcon, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, Expandable, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, InfoSvg, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTransitionArea, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, SplitButton, StarSvg, Step, StepList, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeRangeWheel, TimeSpin, TimeWheel, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, canNavBackSignal, canNavForwardSignal, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineRouteDefaultTransition, defineRouteTransition, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markErrorAsDisplayedBy, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, renderEmojiAsIcon, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutElement, useCalloutRequestClose, useCanNavBack, useCanNavForward, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
78105
78743
|
//# sourceMappingURL=jsenv_navi.js.map
|