@jsenv/navi 0.25.7 → 0.25.8
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 +1758 -1519
- package/dist/jsenv_navi.js.map +107 -118
- package/dist/jsenv_navi_side_effects.js +10 -0
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -3,7 +3,7 @@ import { isValidElement, createContext, h, options, toChildArray, render, cloneE
|
|
|
3
3
|
import { useErrorBoundary, useLayoutEffect, useEffect, useContext, useMemo, useRef, useState, useCallback, useImperativeHandle, useId } from "preact/hooks";
|
|
4
4
|
import { jsxs, jsx, Fragment } from "preact/jsx-runtime";
|
|
5
5
|
import { signal, effect, computed, batch, useSignal } from "@preact/signals";
|
|
6
|
-
import { createIterableWeakSet, mergeOneStyle, stringifyStyle, createPubSub, mergeTwoStyles, normalizeStyles, createGroupTransitionController, getElementSignature, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, findBefore, findAfter, createValueEffect, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, allowWheelThrough, resolveCSSColor, createStyleController, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, resolveCSSSize, canInterceptKeys, activeElementSignal,
|
|
6
|
+
import { createIterableWeakSet, mergeOneStyle, stringifyStyle, createPubSub, mergeTwoStyles, normalizeStyles, createGroupTransitionController, getElementSignature, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, findBefore, findAfter, createValueEffect, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, allowWheelThrough, resolveCSSColor, createStyleController, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, resolveCSSSize, canInterceptKeys, activeElementSignal, hasCSSSizeUnit, resolveOklchLightness, contrastColor, initFocusGroup, elementIsFocusable, scrollIntoViewScoped, findFocusable, trapScrollInside, trapFocusInside, dragAfterThreshold, getScrollContainer, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement } from "@jsenv/dom";
|
|
7
7
|
export { contrastColor } from "@jsenv/dom";
|
|
8
8
|
import { prefixFirstAndIndentRemainingLines } from "@jsenv/humanize";
|
|
9
9
|
import { createValidity } from "@jsenv/validity";
|
|
@@ -6286,6 +6286,7 @@ const FLOW_PROPS = {
|
|
|
6286
6286
|
block: () => {},
|
|
6287
6287
|
flex: () => {},
|
|
6288
6288
|
grid: () => {},
|
|
6289
|
+
gridTemplateColumns: PASS_THROUGH,
|
|
6289
6290
|
row: () => {},
|
|
6290
6291
|
column: () => {},
|
|
6291
6292
|
};
|
|
@@ -6560,7 +6561,9 @@ const TYPO_PROPS = {
|
|
|
6560
6561
|
underlineColor: applyOnCSSProp("textDecorationColor"),
|
|
6561
6562
|
textShadow: PASS_THROUGH,
|
|
6562
6563
|
lineHeight: PASS_THROUGH,
|
|
6563
|
-
color:
|
|
6564
|
+
color: (value) => {
|
|
6565
|
+
return { color: resolveColorKeyword(value) };
|
|
6566
|
+
},
|
|
6564
6567
|
noWrap: applyToCssPropWhenTruthy("whiteSpace", "nowrap", "normal"),
|
|
6565
6568
|
pre: applyToCssPropWhenTruthy("whiteSpace", "pre", "normal"),
|
|
6566
6569
|
preWrap: applyToCssPropWhenTruthy("whiteSpace", "pre-wrap", "normal"),
|
|
@@ -6840,6 +6843,16 @@ const resolveSpacingSize = (size, property = "padding") => {
|
|
|
6840
6843
|
return stringifyStyle(SIZE_MAP[size] || size, property);
|
|
6841
6844
|
};
|
|
6842
6845
|
|
|
6846
|
+
const COLOR_KEYWORD_MAP = {
|
|
6847
|
+
secondary: "var(--navi-color-secondary)",
|
|
6848
|
+
emphasis: "var(--navi-color-emphasis)",
|
|
6849
|
+
discrete: "var(--navi-color-discrete)",
|
|
6850
|
+
hint: "var(--navi-color-hint)",
|
|
6851
|
+
};
|
|
6852
|
+
const resolveColorKeyword = (value) => {
|
|
6853
|
+
return COLOR_KEYWORD_MAP[value] || value;
|
|
6854
|
+
};
|
|
6855
|
+
|
|
6843
6856
|
const DEFAULT_DISPLAY_BY_TAG_NAME = {
|
|
6844
6857
|
"inline": new Set([
|
|
6845
6858
|
"a",
|
|
@@ -7202,212 +7215,361 @@ const listenInputValueChange = (input, callback) => {
|
|
|
7202
7215
|
return teardown;
|
|
7203
7216
|
};
|
|
7204
7217
|
|
|
7205
|
-
|
|
7218
|
+
/**
|
|
7219
|
+
* Navi uses three categories of custom events:
|
|
7220
|
+
*
|
|
7221
|
+
* 1. **Internal events** (`dispatchInternalCustomEvent`) — a component communicates
|
|
7222
|
+
* with other navi components internally. Not meant to be observed from outside.
|
|
7223
|
+
* They do not bubble so they stay contained within the subtree that handles them.
|
|
7224
|
+
* Names often reflect their internal nature (e.g. `navi_pseudo_state_request_check`).
|
|
7225
|
+
*
|
|
7226
|
+
* 2. **Public events** (`dispatchPublicCustomEvent`) — a component exposes information
|
|
7227
|
+
* about something that happened (e.g. `navi_list_select`). They bubble so any
|
|
7228
|
+
* ancestor can observe them. These are part of the public API and should be documented.
|
|
7229
|
+
*
|
|
7230
|
+
* 3. **Request events** (`dispatchCustomEvent`) — code *outside* a component asks it
|
|
7231
|
+
* to perform an action (e.g. `navi_list_request_open`). They are cancelable so the
|
|
7232
|
+
* component can signal whether it handled the request. Names are prefixed
|
|
7233
|
+
* with `request_` by convention.
|
|
7234
|
+
*/
|
|
7206
7235
|
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7236
|
+
/**
|
|
7237
|
+
* Dispatches an internal event on `el`.
|
|
7238
|
+
* Does not bubble — stays within the local subtree.
|
|
7239
|
+
*/
|
|
7240
|
+
const dispatchInternalCustomEvent = (
|
|
7241
|
+
el,
|
|
7242
|
+
customEventName,
|
|
7243
|
+
customEventDetail,
|
|
7244
|
+
) => {
|
|
7245
|
+
const customEvent = new CustomEvent(customEventName, {
|
|
7246
|
+
detail: customEventDetail,
|
|
7247
|
+
});
|
|
7248
|
+
return el.dispatchEvent(customEvent);
|
|
7249
|
+
};
|
|
7250
|
+
|
|
7251
|
+
/**
|
|
7252
|
+
* Dispatches a public event from `el`, announcing something that happened.
|
|
7253
|
+
* Bubbles so any ancestor can observe it.
|
|
7254
|
+
*/
|
|
7255
|
+
const dispatchPublicCustomEvent = (
|
|
7256
|
+
el,
|
|
7257
|
+
customEventName,
|
|
7258
|
+
customEventDetail,
|
|
7259
|
+
) => {
|
|
7260
|
+
const customEvent = new CustomEvent(customEventName, {
|
|
7261
|
+
detail: resolveEventDetail(customEventDetail),
|
|
7262
|
+
bubbles: true,
|
|
7263
|
+
});
|
|
7264
|
+
return el.dispatchEvent(customEvent);
|
|
7265
|
+
};
|
|
7266
|
+
|
|
7267
|
+
/**
|
|
7268
|
+
* Dispatches a request event *at* `el`, asking it to perform an action.
|
|
7269
|
+
* Cancelable — returns `false` if the component called `preventDefault()`,
|
|
7270
|
+
* indicating it did not (or could not) handle the request.
|
|
7271
|
+
* Names are conventionally prefixed with `request_` (e.g. `navi_list_request_open`).
|
|
7272
|
+
*/
|
|
7273
|
+
const dispatchCustomEvent = (el, customEventName, customEventDetail) => {
|
|
7274
|
+
const customEvent = new CustomEvent(customEventName, {
|
|
7275
|
+
detail: resolveEventDetail(customEventDetail),
|
|
7276
|
+
cancelable: true,
|
|
7277
|
+
});
|
|
7278
|
+
const result = el.dispatchEvent(customEvent);
|
|
7279
|
+
return result;
|
|
7280
|
+
};
|
|
7281
|
+
|
|
7282
|
+
const resolveEventDetail = (customEventDetail) => {
|
|
7283
|
+
const { event, ...rest } = customEventDetail ?? {};
|
|
7284
|
+
let resolvedEvent;
|
|
7285
|
+
if (event?.detail?.event !== undefined) {
|
|
7286
|
+
resolvedEvent = event.detail.event;
|
|
7287
|
+
} else if (event !== undefined) {
|
|
7288
|
+
resolvedEvent = event;
|
|
7289
|
+
}
|
|
7290
|
+
return { ...rest, event: resolvedEvent };
|
|
7291
|
+
};
|
|
7292
|
+
|
|
7293
|
+
const requestPseudoStateCheck = (element, detail) => {
|
|
7294
|
+
dispatchInternalCustomEvent(
|
|
7295
|
+
element,
|
|
7296
|
+
"navi_pseudo_state_request_check",
|
|
7297
|
+
detail,
|
|
7298
|
+
);
|
|
7299
|
+
};
|
|
7300
|
+
const NAVI_PSEUDO_STATE_CUSTOM_EVENT = "navi_pseudo_state";
|
|
7301
|
+
const dispatchPseudoStateCustomEvent = (element, value, oldValue) => {
|
|
7302
|
+
dispatchInternalCustomEvent(element, NAVI_PSEUDO_STATE_CUSTOM_EVENT, {
|
|
7303
|
+
pseudoState: value,
|
|
7304
|
+
oldPseudoState: oldValue,
|
|
7305
|
+
});
|
|
7306
|
+
};
|
|
7307
|
+
|
|
7308
|
+
const PSEUDO_CLASSES = {};
|
|
7309
|
+
Object.assign(PSEUDO_CLASSES, {
|
|
7310
|
+
":valid": {
|
|
7311
|
+
attribute: "data-valid",
|
|
7312
|
+
test: (el) => el.matches(":valid"),
|
|
7313
|
+
},
|
|
7314
|
+
":invalid": {
|
|
7315
|
+
attribute: "data-invalid",
|
|
7316
|
+
test: (el) => el.matches(":invalid"),
|
|
7317
|
+
},
|
|
7318
|
+
":visited": {
|
|
7319
|
+
attribute: "data-visited",
|
|
7320
|
+
},
|
|
7321
|
+
});
|
|
7322
|
+
const definePseudoClass = (pseudoClass, definition) => {
|
|
7323
|
+
PSEUDO_CLASSES[pseudoClass] = definition;
|
|
7324
|
+
};
|
|
7325
|
+
|
|
7326
|
+
definePseudoClass(":hover", {
|
|
7327
|
+
attribute: "data-hover",
|
|
7328
|
+
setup: (el, callback) => {
|
|
7329
|
+
let onmouseenter = () => {
|
|
7330
|
+
callback();
|
|
7331
|
+
};
|
|
7332
|
+
let onmouseleave = () => {
|
|
7333
|
+
callback();
|
|
7334
|
+
};
|
|
7335
|
+
|
|
7336
|
+
if (el.tagName === "LABEL") {
|
|
7337
|
+
// input.matches(":hover") is true when hovering the label
|
|
7338
|
+
// so when label is hovered/not hovered we need to recheck the input too
|
|
7339
|
+
const recheckInput = (e) => {
|
|
7340
|
+
if (el.htmlFor) {
|
|
7341
|
+
const input = document.getElementById(el.htmlFor);
|
|
7342
|
+
if (!input) {
|
|
7343
|
+
// cannot find the input for this label in the DOM
|
|
7344
|
+
return;
|
|
7345
|
+
}
|
|
7346
|
+
requestPseudoStateCheck(input, { event: e });
|
|
7347
|
+
return;
|
|
7348
|
+
}
|
|
7349
|
+
const input = el.querySelector("input, textarea, select");
|
|
7350
|
+
if (!input) {
|
|
7351
|
+
// label does not contain an input
|
|
7352
|
+
return;
|
|
7353
|
+
}
|
|
7354
|
+
requestPseudoStateCheck(input, { event: e });
|
|
7355
|
+
};
|
|
7356
|
+
onmouseenter = (e) => {
|
|
7212
7357
|
callback();
|
|
7358
|
+
recheckInput(e);
|
|
7213
7359
|
};
|
|
7214
|
-
|
|
7360
|
+
onmouseleave = (e) => {
|
|
7215
7361
|
callback();
|
|
7362
|
+
recheckInput(e);
|
|
7216
7363
|
};
|
|
7364
|
+
}
|
|
7217
7365
|
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7366
|
+
el.addEventListener("mouseenter", onmouseenter);
|
|
7367
|
+
el.addEventListener("mouseleave", onmouseleave);
|
|
7368
|
+
return () => {
|
|
7369
|
+
el.removeEventListener("mouseenter", onmouseenter);
|
|
7370
|
+
el.removeEventListener("mouseleave", onmouseleave);
|
|
7371
|
+
};
|
|
7372
|
+
},
|
|
7373
|
+
test: (el) => el.matches(":hover"),
|
|
7374
|
+
});
|
|
7375
|
+
definePseudoClass(":disabled", {
|
|
7376
|
+
attribute: "data-disabled",
|
|
7377
|
+
add: (el) => {
|
|
7378
|
+
if (
|
|
7379
|
+
el.tagName === "BUTTON" ||
|
|
7380
|
+
el.tagName === "INPUT" ||
|
|
7381
|
+
el.tagName === "SELECT" ||
|
|
7382
|
+
el.tagName === "TEXTAREA"
|
|
7383
|
+
) {
|
|
7384
|
+
el.disabled = true;
|
|
7385
|
+
}
|
|
7386
|
+
},
|
|
7387
|
+
remove: (el) => {
|
|
7388
|
+
if (
|
|
7389
|
+
el.tagName === "BUTTON" ||
|
|
7390
|
+
el.tagName === "INPUT" ||
|
|
7391
|
+
el.tagName === "SELECT" ||
|
|
7392
|
+
el.tagName === "TEXTAREA"
|
|
7393
|
+
) {
|
|
7394
|
+
el.disabled = false;
|
|
7395
|
+
}
|
|
7396
|
+
},
|
|
7397
|
+
});
|
|
7398
|
+
definePseudoClass(":read-only", {
|
|
7399
|
+
attribute: "data-readonly",
|
|
7400
|
+
add: (el) => {
|
|
7401
|
+
if (
|
|
7402
|
+
el.tagName === "INPUT" ||
|
|
7403
|
+
el.tagName === "SELECT" ||
|
|
7404
|
+
el.tagName === "TEXTAREA"
|
|
7405
|
+
) {
|
|
7406
|
+
if (el.type === "checkbox" || el.type === "radio") {
|
|
7407
|
+
// there is no readOnly for checkboxes/radios
|
|
7408
|
+
return;
|
|
7250
7409
|
}
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
el.addEventListener("mouseleave", onmouseleave);
|
|
7254
|
-
return () => {
|
|
7255
|
-
el.removeEventListener("mouseenter", onmouseenter);
|
|
7256
|
-
el.removeEventListener("mouseleave", onmouseleave);
|
|
7257
|
-
};
|
|
7258
|
-
},
|
|
7259
|
-
test: (el) => el.matches(":hover"),
|
|
7410
|
+
el.readOnly = true;
|
|
7411
|
+
}
|
|
7260
7412
|
},
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7413
|
+
remove: (el) => {
|
|
7414
|
+
if (
|
|
7415
|
+
el.tagName === "INPUT" ||
|
|
7416
|
+
el.tagName === "SELECT" ||
|
|
7417
|
+
el.tagName === "TEXTAREA"
|
|
7418
|
+
) {
|
|
7419
|
+
if (el.type === "checkbox" || el.type === "radio") {
|
|
7420
|
+
// there is no readOnly for checkboxes/radios
|
|
7421
|
+
return;
|
|
7422
|
+
}
|
|
7423
|
+
el.readOnly = false;
|
|
7424
|
+
}
|
|
7425
|
+
},
|
|
7426
|
+
});
|
|
7427
|
+
definePseudoClass(":checked", {
|
|
7428
|
+
attribute: "data-checked",
|
|
7429
|
+
setup: (el, callback) => {
|
|
7430
|
+
if (el.type === "checkbox") {
|
|
7431
|
+
// Listen to user interactions
|
|
7432
|
+
el.addEventListener("input", callback);
|
|
7433
|
+
// Intercept programmatic changes to .checked property
|
|
7434
|
+
const originalDescriptor = Object.getOwnPropertyDescriptor(
|
|
7435
|
+
HTMLInputElement.prototype,
|
|
7436
|
+
"checked",
|
|
7437
|
+
);
|
|
7438
|
+
Object.defineProperty(el, "checked", {
|
|
7439
|
+
get: originalDescriptor.get,
|
|
7440
|
+
set(value) {
|
|
7441
|
+
originalDescriptor.set.call(this, value);
|
|
7279
7442
|
callback();
|
|
7280
|
-
}
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
callback();
|
|
7284
|
-
};
|
|
7285
|
-
el.addEventListener("pointerdown", onPointerDown);
|
|
7443
|
+
},
|
|
7444
|
+
configurable: true,
|
|
7445
|
+
});
|
|
7286
7446
|
return () => {
|
|
7287
|
-
|
|
7447
|
+
// Restore original property descriptor
|
|
7448
|
+
Object.defineProperty(el, "checked", originalDescriptor);
|
|
7449
|
+
el.removeEventListener("input", callback);
|
|
7288
7450
|
};
|
|
7289
|
-
}
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
//
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
pressedElements.add(el);
|
|
7306
|
-
const onRelease = () => {
|
|
7307
|
-
pressedElements.delete(el);
|
|
7308
|
-
document.removeEventListener("pointercancel", onRelease, true);
|
|
7309
|
-
document.removeEventListener("pointerup", onRelease, true);
|
|
7310
|
-
document.removeEventListener("contextmenu", onContextMenu, true);
|
|
7451
|
+
}
|
|
7452
|
+
if (el.type === "radio") {
|
|
7453
|
+
// Listen to changes on the radio group
|
|
7454
|
+
const radioSet =
|
|
7455
|
+
el.closest("[data-radio-list], fieldset, form") || document;
|
|
7456
|
+
radioSet.addEventListener("input", callback);
|
|
7457
|
+
|
|
7458
|
+
// Intercept programmatic changes to .checked property
|
|
7459
|
+
const originalDescriptor = Object.getOwnPropertyDescriptor(
|
|
7460
|
+
HTMLInputElement.prototype,
|
|
7461
|
+
"checked",
|
|
7462
|
+
);
|
|
7463
|
+
Object.defineProperty(el, "checked", {
|
|
7464
|
+
get: originalDescriptor.get,
|
|
7465
|
+
set(value) {
|
|
7466
|
+
originalDescriptor.set.call(this, value);
|
|
7311
7467
|
callback();
|
|
7312
|
-
}
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
if (e.button === -1 && !e.defaultPrevented) {
|
|
7320
|
-
pressedElements.delete(el);
|
|
7321
|
-
document.removeEventListener("pointercancel", onRelease, true);
|
|
7322
|
-
document.removeEventListener("pointerup", onRelease, true);
|
|
7323
|
-
document.removeEventListener("contextmenu", onContextMenu, true);
|
|
7324
|
-
callback();
|
|
7325
|
-
}
|
|
7326
|
-
};
|
|
7327
|
-
document.addEventListener("pointercancel", onRelease, true);
|
|
7328
|
-
document.addEventListener("pointerup", onRelease, true);
|
|
7329
|
-
document.addEventListener("contextmenu", onContextMenu, true);
|
|
7330
|
-
callback();
|
|
7468
|
+
},
|
|
7469
|
+
configurable: true,
|
|
7470
|
+
});
|
|
7471
|
+
return () => {
|
|
7472
|
+
radioSet.removeEventListener("input", callback);
|
|
7473
|
+
// Restore original property descriptor
|
|
7474
|
+
Object.defineProperty(el, "checked", originalDescriptor);
|
|
7331
7475
|
};
|
|
7332
|
-
|
|
7476
|
+
}
|
|
7477
|
+
if (el.tagName === "INPUT") {
|
|
7478
|
+
el.addEventListener("input", callback);
|
|
7333
7479
|
return () => {
|
|
7334
|
-
el.removeEventListener("
|
|
7335
|
-
pressedElements.delete(el);
|
|
7480
|
+
el.removeEventListener("input", callback);
|
|
7336
7481
|
};
|
|
7337
|
-
}
|
|
7338
|
-
|
|
7482
|
+
}
|
|
7483
|
+
return () => {};
|
|
7339
7484
|
},
|
|
7340
|
-
":
|
|
7341
|
-
|
|
7485
|
+
test: (el) => el.matches(":checked"),
|
|
7486
|
+
});
|
|
7487
|
+
definePseudoClass(":active", {
|
|
7488
|
+
attribute: "data-active",
|
|
7489
|
+
setup: (el, callback) => {
|
|
7490
|
+
// I'ts recommended to use :-navi-pressed over :active for interactive elements.
|
|
7491
|
+
const onPointerDown = () => {
|
|
7492
|
+
const onRelease = () => {
|
|
7493
|
+
document.removeEventListener("pointercancel", onRelease, true);
|
|
7494
|
+
document.removeEventListener("pointerup", onRelease, true);
|
|
7495
|
+
callback();
|
|
7496
|
+
};
|
|
7497
|
+
document.addEventListener("pointercancel", onRelease, true);
|
|
7498
|
+
document.addEventListener("pointerup", onRelease, true);
|
|
7499
|
+
callback();
|
|
7500
|
+
};
|
|
7501
|
+
el.addEventListener("pointerdown", onPointerDown);
|
|
7502
|
+
return () => {
|
|
7503
|
+
el.removeEventListener("pointerdown", onPointerDown);
|
|
7504
|
+
};
|
|
7342
7505
|
},
|
|
7343
|
-
":
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7506
|
+
test: (el) => el.matches(":active"),
|
|
7507
|
+
});
|
|
7508
|
+
{
|
|
7509
|
+
// We implement :focus and :focus-visible with enriched semantics:
|
|
7510
|
+
// an element is considered focused not only when it natively has focus, but also
|
|
7511
|
+
// when a "focus proxy" element has focus (e.g. a read-only range input delegates
|
|
7512
|
+
// focus to a sibling span) or when a controlling element has focus (e.g. a combobox
|
|
7513
|
+
// input with aria-controls pointing to a listbox — the listbox should appear focused
|
|
7514
|
+
// while the input is focused).
|
|
7515
|
+
//
|
|
7516
|
+
// We intentionally reuse the native :focus / :focus-visible names rather than
|
|
7517
|
+
// introducing new navi-specific pseudo-classes (e.g. :-navi-focus). This is a
|
|
7518
|
+
// deliberate exception: all existing CSS and code written as [data-focus] or
|
|
7519
|
+
// [data-focus-visible] automatically benefits from the enriched behavior without
|
|
7520
|
+
// any changes. A separate navi-specific class would require updating every
|
|
7521
|
+
// component.
|
|
7522
|
+
//
|
|
7523
|
+
// When a controller element (e.g. combobox input) gains or loses focus,
|
|
7524
|
+
// notify the elements it controls via aria-controls so they re-check their focus state.
|
|
7525
|
+
const notifyAriaControlled = (el, e) => {
|
|
7526
|
+
const controlledIds = el.getAttribute("aria-controls");
|
|
7527
|
+
if (!controlledIds) {
|
|
7528
|
+
return;
|
|
7529
|
+
}
|
|
7530
|
+
for (const id of controlledIds.split(" ")) {
|
|
7531
|
+
const controlled = document.getElementById(id);
|
|
7532
|
+
if (controlled) {
|
|
7533
|
+
requestPseudoStateCheck(controlled, { event: e });
|
|
7367
7534
|
}
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
configurable: true,
|
|
7386
|
-
});
|
|
7387
|
-
return () => {
|
|
7388
|
-
radioSet.removeEventListener("input", callback);
|
|
7389
|
-
// Restore original property descriptor
|
|
7390
|
-
Object.defineProperty(el, "checked", originalDescriptor);
|
|
7391
|
-
};
|
|
7535
|
+
}
|
|
7536
|
+
};
|
|
7537
|
+
// Check if any element whose aria-controls includes el's id currently has focus.
|
|
7538
|
+
const isControlledByFocusedElement = (
|
|
7539
|
+
el,
|
|
7540
|
+
{ requireFocusVisible = false } = {},
|
|
7541
|
+
) => {
|
|
7542
|
+
const id = el.id;
|
|
7543
|
+
if (!id) {
|
|
7544
|
+
return false;
|
|
7545
|
+
}
|
|
7546
|
+
const controllers = document.querySelectorAll(`[aria-controls~="${id}"]`);
|
|
7547
|
+
for (const controller of controllers) {
|
|
7548
|
+
// If the controller is inside the element it controls, the element already
|
|
7549
|
+
// receives native :focus/:focus-within — no need to inherit focus from it.
|
|
7550
|
+
if (el.contains(controller)) {
|
|
7551
|
+
continue;
|
|
7392
7552
|
}
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
return
|
|
7396
|
-
el.removeEventListener("input", callback);
|
|
7397
|
-
};
|
|
7553
|
+
const pseudoClass = requireFocusVisible ? ":focus-visible" : ":focus";
|
|
7554
|
+
if (controller.matches(pseudoClass)) {
|
|
7555
|
+
return true;
|
|
7398
7556
|
}
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
":focus"
|
|
7557
|
+
}
|
|
7558
|
+
return false;
|
|
7559
|
+
};
|
|
7560
|
+
|
|
7561
|
+
definePseudoClass(":focus", {
|
|
7404
7562
|
attribute: "data-focus",
|
|
7405
7563
|
setup: (el, callback) => {
|
|
7406
|
-
|
|
7407
|
-
|
|
7564
|
+
const onFocusChange = (e) => {
|
|
7565
|
+
callback();
|
|
7566
|
+
notifyAriaControlled(el, e);
|
|
7567
|
+
};
|
|
7568
|
+
el.addEventListener("focusin", onFocusChange);
|
|
7569
|
+
el.addEventListener("focusout", onFocusChange);
|
|
7408
7570
|
return () => {
|
|
7409
|
-
el.removeEventListener("focusin",
|
|
7410
|
-
el.removeEventListener("focusout",
|
|
7571
|
+
el.removeEventListener("focusin", onFocusChange);
|
|
7572
|
+
el.removeEventListener("focusout", onFocusChange);
|
|
7411
7573
|
};
|
|
7412
7574
|
},
|
|
7413
7575
|
test: (el) => {
|
|
@@ -7418,17 +7580,28 @@ const PSEUDO_CLASSES = {
|
|
|
7418
7580
|
if (focusProxy) {
|
|
7419
7581
|
return document.querySelector(`#${focusProxy}`).matches(":focus");
|
|
7420
7582
|
}
|
|
7583
|
+
if (isControlledByFocusedElement(el)) {
|
|
7584
|
+
return true;
|
|
7585
|
+
}
|
|
7421
7586
|
return false;
|
|
7422
7587
|
},
|
|
7423
|
-
}
|
|
7424
|
-
":focus-visible"
|
|
7588
|
+
});
|
|
7589
|
+
definePseudoClass(":focus-visible", {
|
|
7425
7590
|
attribute: "data-focus-visible",
|
|
7426
7591
|
setup: (el, callback) => {
|
|
7592
|
+
const onFocusChange = (e) => {
|
|
7593
|
+
callback();
|
|
7594
|
+
notifyAriaControlled(el, e);
|
|
7595
|
+
};
|
|
7427
7596
|
document.addEventListener("keydown", callback);
|
|
7428
7597
|
document.addEventListener("keyup", callback);
|
|
7598
|
+
el.addEventListener("focusin", onFocusChange);
|
|
7599
|
+
el.addEventListener("focusout", onFocusChange);
|
|
7429
7600
|
return () => {
|
|
7430
7601
|
document.removeEventListener("keydown", callback);
|
|
7431
7602
|
document.removeEventListener("keyup", callback);
|
|
7603
|
+
el.removeEventListener("focusin", onFocusChange);
|
|
7604
|
+
el.removeEventListener("focusout", onFocusChange);
|
|
7432
7605
|
};
|
|
7433
7606
|
},
|
|
7434
7607
|
test: (el) => {
|
|
@@ -7441,70 +7614,39 @@ const PSEUDO_CLASSES = {
|
|
|
7441
7614
|
.querySelector(`#${focusProxy}`)
|
|
7442
7615
|
.matches(":focus-visible");
|
|
7443
7616
|
}
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
},
|
|
7447
|
-
":disabled": {
|
|
7448
|
-
attribute: "data-disabled",
|
|
7449
|
-
add: (el) => {
|
|
7450
|
-
if (
|
|
7451
|
-
el.tagName === "BUTTON" ||
|
|
7452
|
-
el.tagName === "INPUT" ||
|
|
7453
|
-
el.tagName === "SELECT" ||
|
|
7454
|
-
el.tagName === "TEXTAREA"
|
|
7455
|
-
) {
|
|
7456
|
-
el.disabled = true;
|
|
7617
|
+
if (isControlledByFocusedElement(el, { requireFocusVisible: true })) {
|
|
7618
|
+
return true;
|
|
7457
7619
|
}
|
|
7620
|
+
return false;
|
|
7458
7621
|
},
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7622
|
+
});
|
|
7623
|
+
definePseudoClass(":focus-within", {
|
|
7624
|
+
attribute: "data-focus-within",
|
|
7625
|
+
setup: (el, callback) => {
|
|
7626
|
+
const onFocusChange = (e) => {
|
|
7627
|
+
callback();
|
|
7628
|
+
notifyAriaControlled(el, e);
|
|
7629
|
+
};
|
|
7630
|
+
el.addEventListener("focusin", onFocusChange);
|
|
7631
|
+
el.addEventListener("focusout", onFocusChange);
|
|
7632
|
+
return () => {
|
|
7633
|
+
el.removeEventListener("focusin", onFocusChange);
|
|
7634
|
+
el.removeEventListener("focusout", onFocusChange);
|
|
7635
|
+
};
|
|
7468
7636
|
},
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
add: (el) => {
|
|
7473
|
-
if (
|
|
7474
|
-
el.tagName === "INPUT" ||
|
|
7475
|
-
el.tagName === "SELECT" ||
|
|
7476
|
-
el.tagName === "TEXTAREA"
|
|
7477
|
-
) {
|
|
7478
|
-
if (el.type === "checkbox" || el.type === "radio") {
|
|
7479
|
-
// there is no readOnly for checkboxes/radios
|
|
7480
|
-
return;
|
|
7481
|
-
}
|
|
7482
|
-
el.readOnly = true;
|
|
7637
|
+
test: (el) => {
|
|
7638
|
+
if (el.matches(":focus-within")) {
|
|
7639
|
+
return true;
|
|
7483
7640
|
}
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
if (
|
|
7487
|
-
el.tagName === "INPUT" ||
|
|
7488
|
-
el.tagName === "SELECT" ||
|
|
7489
|
-
el.tagName === "TEXTAREA"
|
|
7490
|
-
) {
|
|
7491
|
-
if (el.type === "checkbox" || el.type === "radio") {
|
|
7492
|
-
// there is no readOnly for checkboxes/radios
|
|
7493
|
-
return;
|
|
7494
|
-
}
|
|
7495
|
-
el.readOnly = false;
|
|
7641
|
+
if (isControlledByFocusedElement(el)) {
|
|
7642
|
+
return true;
|
|
7496
7643
|
}
|
|
7644
|
+
return false;
|
|
7497
7645
|
},
|
|
7498
|
-
}
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
},
|
|
7503
|
-
":invalid": {
|
|
7504
|
-
attribute: "data-invalid",
|
|
7505
|
-
test: (el) => el.matches(":invalid"),
|
|
7506
|
-
},
|
|
7507
|
-
"::highlight": {},
|
|
7646
|
+
});
|
|
7647
|
+
}
|
|
7648
|
+
|
|
7649
|
+
Object.assign(PSEUDO_CLASSES, {
|
|
7508
7650
|
":-navi-pointed": {
|
|
7509
7651
|
attribute: "data-pointed",
|
|
7510
7652
|
},
|
|
@@ -7541,35 +7683,84 @@ const PSEUDO_CLASSES = {
|
|
|
7541
7683
|
":-navi-void": {
|
|
7542
7684
|
attribute: "data-void",
|
|
7543
7685
|
},
|
|
7544
|
-
"
|
|
7545
|
-
|
|
7686
|
+
"::highlight": {},
|
|
7687
|
+
});
|
|
7688
|
+
definePseudoClass(":-navi-has-value", {
|
|
7689
|
+
attribute: "data-has-value",
|
|
7690
|
+
setup: (el, callback) => {
|
|
7691
|
+
return listenInputValue(el, callback);
|
|
7692
|
+
},
|
|
7693
|
+
test: (el) => {
|
|
7694
|
+
if (el.value === "") {
|
|
7695
|
+
return false;
|
|
7696
|
+
}
|
|
7697
|
+
return true;
|
|
7698
|
+
},
|
|
7699
|
+
});
|
|
7700
|
+
{
|
|
7701
|
+
const pressedElements = new WeakSet();
|
|
7702
|
+
definePseudoClass(":-navi-pressed", {
|
|
7703
|
+
attribute: "data-pressed",
|
|
7546
7704
|
setup: (el, callback) => {
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7705
|
+
// Prefer :-navi-pressed over :active for interactive elements because:
|
|
7706
|
+
// - :active only tracks the primary (left) button; right-click and touch
|
|
7707
|
+
// long-press do not trigger :active reliably across browsers.
|
|
7708
|
+
// - :-navi-pressed explicitly ignores non-primary buttons (e.g. right-click)
|
|
7709
|
+
// and correctly clears pressed state when a context menu opens on long-press,
|
|
7710
|
+
// which would otherwise leave the element stuck in a pressed appearance.
|
|
7711
|
+
|
|
7712
|
+
// Note: it might be tempting to use el.setPointerCapture() here so that pointerup
|
|
7713
|
+
// always fires on el regardless of where the pointer is released. However,
|
|
7714
|
+
// pointer capture routes all subsequent pointer events to the capturing element,
|
|
7715
|
+
// which means any other element in the tree that expects to receive pointerup,
|
|
7716
|
+
// mouseup, click, etc. after a pointerdown will silently not get them.
|
|
7717
|
+
// For example a <label> that reacts to mousedown + click, or a third-party
|
|
7718
|
+
// library that attaches its own listeners, would break because an ancestor
|
|
7719
|
+
// grabbed the pointer out from under them.
|
|
7720
|
+
// To avoid forcing every such element to declare an opt-out attribute
|
|
7721
|
+
// (e.g. navi-own-pointer-capture) we simply listen on document instead,
|
|
7722
|
+
// which is safe and does not interfere with anyone else's event flow.
|
|
7723
|
+
const onPointerDown = (e) => {
|
|
7724
|
+
if (e.button !== 0) {
|
|
7725
|
+
// only left pointer (mouse left click, touch, pen)
|
|
7726
|
+
return;
|
|
7727
|
+
}
|
|
7728
|
+
pressedElements.add(el);
|
|
7729
|
+
const onRelease = () => {
|
|
7730
|
+
pressedElements.delete(el);
|
|
7731
|
+
document.removeEventListener("pointercancel", onRelease, true);
|
|
7732
|
+
document.removeEventListener("pointerup", onRelease, true);
|
|
7733
|
+
document.removeEventListener("contextmenu", onContextMenu, true);
|
|
7734
|
+
callback();
|
|
7735
|
+
};
|
|
7736
|
+
const onContextMenu = (e) => {
|
|
7737
|
+
// On touch devices, a long-press triggers the context menu.
|
|
7738
|
+
// If the context menu is not prevented, it means it will open and the
|
|
7739
|
+
// pointer events (pointerup, lostpointercapture) won't fire normally,
|
|
7740
|
+
// leaving the element stuck in pressed state. We clear it manually.
|
|
7741
|
+
// e.button === -1 means the event was synthesized from a long-press (not a real mouse click).
|
|
7742
|
+
if (e.button === -1 && !e.defaultPrevented) {
|
|
7743
|
+
pressedElements.delete(el);
|
|
7744
|
+
document.removeEventListener("pointercancel", onRelease, true);
|
|
7745
|
+
document.removeEventListener("pointerup", onRelease, true);
|
|
7746
|
+
document.removeEventListener("contextmenu", onContextMenu, true);
|
|
7747
|
+
callback();
|
|
7748
|
+
}
|
|
7749
|
+
};
|
|
7750
|
+
document.addEventListener("pointercancel", onRelease, true);
|
|
7751
|
+
document.addEventListener("pointerup", onRelease, true);
|
|
7752
|
+
document.addEventListener("contextmenu", onContextMenu, true);
|
|
7753
|
+
callback();
|
|
7754
|
+
};
|
|
7755
|
+
el.addEventListener("pointerdown", onPointerDown);
|
|
7756
|
+
return () => {
|
|
7757
|
+
el.removeEventListener("pointerdown", onPointerDown);
|
|
7758
|
+
pressedElements.delete(el);
|
|
7759
|
+
};
|
|
7554
7760
|
},
|
|
7555
|
-
|
|
7556
|
-
};
|
|
7557
|
-
|
|
7558
|
-
const NAVI_PSEUDO_STATE_CUSTOM_EVENT = "navi_pseudo_state";
|
|
7559
|
-
const NAVI_CHECK_PSEUDO_STATE_CUSTOM_EVENT = "navi_check_pseudo_state";
|
|
7560
|
-
const dispatchNaviPseudoStateEvent = (element, value, oldValue) => {
|
|
7561
|
-
if (!element) {
|
|
7562
|
-
return;
|
|
7563
|
-
}
|
|
7564
|
-
element.dispatchEvent(
|
|
7565
|
-
new CustomEvent(NAVI_PSEUDO_STATE_CUSTOM_EVENT, {
|
|
7566
|
-
detail: {
|
|
7567
|
-
pseudoState: value,
|
|
7568
|
-
oldPseudoState: oldValue,
|
|
7569
|
-
},
|
|
7570
|
-
}),
|
|
7571
|
-
);
|
|
7572
|
-
};
|
|
7761
|
+
test: (el) => pressedElements.has(el),
|
|
7762
|
+
});
|
|
7763
|
+
}
|
|
7573
7764
|
|
|
7574
7765
|
const EMPTY_STATE = {};
|
|
7575
7766
|
const initPseudoStyles = (
|
|
@@ -7592,7 +7783,7 @@ const initPseudoStyles = (
|
|
|
7592
7783
|
const onStateChange = (value, oldValue) => {
|
|
7593
7784
|
effect?.(value, oldValue);
|
|
7594
7785
|
if (elementListeningPseudoState) {
|
|
7595
|
-
|
|
7786
|
+
dispatchPseudoStateCustomEvent(
|
|
7596
7787
|
elementListeningPseudoState,
|
|
7597
7788
|
value,
|
|
7598
7789
|
oldValue,
|
|
@@ -7661,7 +7852,7 @@ const initPseudoStyles = (
|
|
|
7661
7852
|
state = event.detail.pseudoState;
|
|
7662
7853
|
onStateChange(state, oldState);
|
|
7663
7854
|
});
|
|
7664
|
-
element.addEventListener(
|
|
7855
|
+
element.addEventListener("navi_pseudo_state_request_check", () => {
|
|
7665
7856
|
checkPseudoClasses();
|
|
7666
7857
|
});
|
|
7667
7858
|
|
|
@@ -8037,6 +8228,10 @@ const PSEUDO_CLASSES_DEFAULT = [];
|
|
|
8037
8228
|
const PSEUDO_ELEMENTS_DEFAULT = [];
|
|
8038
8229
|
const STYLE_CSS_VARS_DEFAULT = {};
|
|
8039
8230
|
const PROPS_CSS_VARS_DEFAULT = {};
|
|
8231
|
+
// When only pseudoStateSelector is set (no visualSelector), the box owns its
|
|
8232
|
+
// visual identity. Only event handlers and these explicit props are forwarded
|
|
8233
|
+
// to the inner semantic/interactive child element.
|
|
8234
|
+
const PSEUDO_STATE_CHILD_PROP_SET = new Set(["tabIndex", "tabindex"]);
|
|
8040
8235
|
const Box = props => {
|
|
8041
8236
|
const {
|
|
8042
8237
|
as = "div",
|
|
@@ -8331,14 +8526,21 @@ const Box = props => {
|
|
|
8331
8526
|
return;
|
|
8332
8527
|
}
|
|
8333
8528
|
// not a style prop what do we do with it?
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8529
|
+
// When pseudoStateSelector is set, the child element is the semantic/interactive one
|
|
8530
|
+
// When both selectors are set the child IS the component (e.g. Button with scale
|
|
8531
|
+
// transform) — forward everything so it behaves like a normal element.
|
|
8532
|
+
// When only pseudoStateSelector is set, the box keeps its own visual identity
|
|
8533
|
+
// (border, background, overflow…) and the child is just the interactive/semantic
|
|
8534
|
+
// element inside it. Only event handlers (onXxx) belong on that child; everything
|
|
8535
|
+
// else stays on the box.
|
|
8536
|
+
if (isPseudoStyle) {
|
|
8537
|
+
if (shouldForwardAllToChild) ; else {
|
|
8340
8538
|
console.warn(`unsupported pseudo style key "${name}"`);
|
|
8539
|
+
selfForwardedProps[name] = value;
|
|
8341
8540
|
}
|
|
8541
|
+
} else if (shouldForwardAllToChild) {
|
|
8542
|
+
childForwardedProps[name] = value;
|
|
8543
|
+
} else {
|
|
8342
8544
|
selfForwardedProps[name] = value;
|
|
8343
8545
|
}
|
|
8344
8546
|
return;
|
|
@@ -8369,6 +8571,19 @@ const Box = props => {
|
|
|
8369
8571
|
selfForwardedProps[propName] = propValue;
|
|
8370
8572
|
continue;
|
|
8371
8573
|
}
|
|
8574
|
+
const isEventHandler = propName.startsWith("on");
|
|
8575
|
+
if (isEventHandler) {
|
|
8576
|
+
if (pseudoStateSelector) {
|
|
8577
|
+
childForwardedProps[propName] = propValue;
|
|
8578
|
+
continue;
|
|
8579
|
+
}
|
|
8580
|
+
selfForwardedProps[propName] = propValue;
|
|
8581
|
+
continue;
|
|
8582
|
+
}
|
|
8583
|
+
if (pseudoStateSelector && PSEUDO_STATE_CHILD_PROP_SET.has(propName)) {
|
|
8584
|
+
childForwardedProps[propName] = propValue;
|
|
8585
|
+
continue;
|
|
8586
|
+
}
|
|
8372
8587
|
visitProp(propValue, propName, styleContext, boxStyles, "prop");
|
|
8373
8588
|
}
|
|
8374
8589
|
if (typeof style === "string") {
|
|
@@ -15904,50 +16119,50 @@ const useActionEvents = (
|
|
|
15904
16119
|
}
|
|
15905
16120
|
|
|
15906
16121
|
return addManyEventListeners(element, {
|
|
15907
|
-
|
|
16122
|
+
navi_cancel: (e) => {
|
|
15908
16123
|
// cancel don't need to check for actionOrigin because
|
|
15909
16124
|
// it's actually unrelated to a specific actions
|
|
15910
16125
|
// in that sense it should likely be moved elsewhere as it's related to
|
|
15911
16126
|
// interaction and constraint validation, not to a specific action
|
|
15912
16127
|
onCancel?.(e, e.detail.reason);
|
|
15913
16128
|
},
|
|
15914
|
-
|
|
16129
|
+
navi_action_requested: (e) => {
|
|
15915
16130
|
if (e.detail.actionOrigin !== actionOrigin) {
|
|
15916
16131
|
return;
|
|
15917
16132
|
}
|
|
15918
16133
|
onRequested?.(e);
|
|
15919
16134
|
},
|
|
15920
|
-
|
|
16135
|
+
navi_action_prevented: (e) => {
|
|
15921
16136
|
if (e.detail.actionOrigin !== actionOrigin) {
|
|
15922
16137
|
return;
|
|
15923
16138
|
}
|
|
15924
16139
|
onPrevented?.(e);
|
|
15925
16140
|
},
|
|
15926
|
-
|
|
16141
|
+
navi_action: (e) => {
|
|
15927
16142
|
if (e.detail.actionOrigin !== actionOrigin) {
|
|
15928
16143
|
return;
|
|
15929
16144
|
}
|
|
15930
16145
|
onAction?.(e);
|
|
15931
16146
|
},
|
|
15932
|
-
|
|
16147
|
+
navi_action_start: (e) => {
|
|
15933
16148
|
if (e.detail.actionOrigin !== actionOrigin) {
|
|
15934
16149
|
return;
|
|
15935
16150
|
}
|
|
15936
16151
|
onStart?.(e);
|
|
15937
16152
|
},
|
|
15938
|
-
|
|
16153
|
+
navi_action_abort: (e) => {
|
|
15939
16154
|
if (e.detail.actionOrigin !== actionOrigin) {
|
|
15940
16155
|
return;
|
|
15941
16156
|
}
|
|
15942
16157
|
onAbort?.(e);
|
|
15943
16158
|
},
|
|
15944
|
-
|
|
16159
|
+
navi_action_error: (e) => {
|
|
15945
16160
|
if (e.detail.actionOrigin !== actionOrigin) {
|
|
15946
16161
|
return;
|
|
15947
16162
|
}
|
|
15948
16163
|
onError?.(e.detail.error, e);
|
|
15949
16164
|
},
|
|
15950
|
-
|
|
16165
|
+
navi_action_end: onEnd,
|
|
15951
16166
|
});
|
|
15952
16167
|
}, [
|
|
15953
16168
|
actionOrigin,
|
|
@@ -16024,7 +16239,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
16024
16239
|
* - Centers in viewport when no anchor element provided or anchor is too big
|
|
16025
16240
|
*/
|
|
16026
16241
|
|
|
16027
|
-
const css$
|
|
16242
|
+
const css$B = /* css */`
|
|
16028
16243
|
@layer navi {
|
|
16029
16244
|
.navi_callout {
|
|
16030
16245
|
--callout-success-color: #4caf50;
|
|
@@ -16198,7 +16413,7 @@ const openCallout = (message, {
|
|
|
16198
16413
|
showErrorStack,
|
|
16199
16414
|
debug = false
|
|
16200
16415
|
} = {}) => {
|
|
16201
|
-
import.meta.css = [css$
|
|
16416
|
+
import.meta.css = [css$B, "@jsenv/navi/src/field/validation/callout/callout.js"];
|
|
16202
16417
|
const callout = {
|
|
16203
16418
|
opened: true,
|
|
16204
16419
|
close: null,
|
|
@@ -18082,9 +18297,12 @@ const requestAction = (
|
|
|
18082
18297
|
|
|
18083
18298
|
// If validation failed, dispatch actionprevented and return
|
|
18084
18299
|
if (!isValid) {
|
|
18085
|
-
const actionPreventedCustomEvent = new CustomEvent(
|
|
18086
|
-
|
|
18087
|
-
|
|
18300
|
+
const actionPreventedCustomEvent = new CustomEvent(
|
|
18301
|
+
"navi_action_prevented",
|
|
18302
|
+
{
|
|
18303
|
+
detail: customEventDetail,
|
|
18304
|
+
},
|
|
18305
|
+
);
|
|
18088
18306
|
elementForDispatch.dispatchEvent(actionPreventedCustomEvent);
|
|
18089
18307
|
return false;
|
|
18090
18308
|
}
|
|
@@ -18096,16 +18314,19 @@ const requestAction = (
|
|
|
18096
18314
|
if (confirmMessage) {
|
|
18097
18315
|
// eslint-disable-next-line no-alert
|
|
18098
18316
|
if (!window.confirm(confirmMessage)) {
|
|
18099
|
-
const actionPreventedCustomEvent = new CustomEvent(
|
|
18100
|
-
|
|
18101
|
-
|
|
18317
|
+
const actionPreventedCustomEvent = new CustomEvent(
|
|
18318
|
+
"navi_action_prevented",
|
|
18319
|
+
{
|
|
18320
|
+
detail: customEventDetail,
|
|
18321
|
+
},
|
|
18322
|
+
);
|
|
18102
18323
|
elementForDispatch.dispatchEvent(actionPreventedCustomEvent);
|
|
18103
18324
|
return false;
|
|
18104
18325
|
}
|
|
18105
18326
|
}
|
|
18106
18327
|
|
|
18107
18328
|
// All good, dispatch the action
|
|
18108
|
-
const actionCustomEvent = new CustomEvent("
|
|
18329
|
+
const actionCustomEvent = new CustomEvent("navi_action", {
|
|
18109
18330
|
detail: customEventDetail,
|
|
18110
18331
|
});
|
|
18111
18332
|
elementForDispatch.dispatchEvent(actionCustomEvent);
|
|
@@ -18175,7 +18396,7 @@ const installCustomConstraintValidation = (
|
|
|
18175
18396
|
}
|
|
18176
18397
|
|
|
18177
18398
|
const dispatchCancelCustomEvent = (options) => {
|
|
18178
|
-
const cancelEvent = new CustomEvent("
|
|
18399
|
+
const cancelEvent = new CustomEvent("navi_cancel", options);
|
|
18179
18400
|
element.dispatchEvent(cancelEvent);
|
|
18180
18401
|
};
|
|
18181
18402
|
const closeElementValidationMessage = (reason) => {
|
|
@@ -18234,14 +18455,7 @@ const installCustomConstraintValidation = (
|
|
|
18234
18455
|
|
|
18235
18456
|
resetValidity({ fromRequestAction });
|
|
18236
18457
|
for (const constraint of constraintSet) {
|
|
18237
|
-
|
|
18238
|
-
// identified by data-input-proxy. When present, constraints run against
|
|
18239
|
-
// that proxy element so they can read standard properties like .value,
|
|
18240
|
-
// .required, .name without needing to know about each component's internals.
|
|
18241
|
-
const inputProxySelector = element.getAttribute("data-input-proxy");
|
|
18242
|
-
const fieldForConstraint = inputProxySelector
|
|
18243
|
-
? (element.ownerDocument.querySelector(inputProxySelector) ?? element)
|
|
18244
|
-
: element;
|
|
18458
|
+
const fieldForConstraint = element;
|
|
18245
18459
|
const constraintCleanupSet = new Set();
|
|
18246
18460
|
const registerChange = (register) => {
|
|
18247
18461
|
const registerResult = register(() => {
|
|
@@ -18440,11 +18654,16 @@ const installCustomConstraintValidation = (
|
|
|
18440
18654
|
}
|
|
18441
18655
|
|
|
18442
18656
|
checkValidity();
|
|
18657
|
+
|
|
18658
|
+
const resetOnInteraction = (e) => {
|
|
18659
|
+
customMessageMap.clear();
|
|
18660
|
+
closeElementValidationMessage(e.type);
|
|
18661
|
+
checkValidity();
|
|
18662
|
+
};
|
|
18663
|
+
|
|
18443
18664
|
{
|
|
18444
|
-
const oninput = () => {
|
|
18445
|
-
|
|
18446
|
-
closeElementValidationMessage("input_event");
|
|
18447
|
-
checkValidity();
|
|
18665
|
+
const oninput = (e) => {
|
|
18666
|
+
resetOnInteraction(e);
|
|
18448
18667
|
};
|
|
18449
18668
|
element.addEventListener("input", oninput);
|
|
18450
18669
|
addTeardown(() => {
|
|
@@ -18452,15 +18671,67 @@ const installCustomConstraintValidation = (
|
|
|
18452
18671
|
});
|
|
18453
18672
|
}
|
|
18454
18673
|
|
|
18674
|
+
{
|
|
18675
|
+
// When the user clicks the field (or the interactive element rendered in place of it,
|
|
18676
|
+
// e.g. the .navi_select button for a hidden input), treat it as intent to fix the issue
|
|
18677
|
+
// and dismiss the callout — unless the status is "error", which requires explicit action.
|
|
18678
|
+
const interactionTarget = (() => {
|
|
18679
|
+
const renderedBy = element.getAttribute("data-rendered-by");
|
|
18680
|
+
if (renderedBy) {
|
|
18681
|
+
return element.closest(renderedBy) || element;
|
|
18682
|
+
}
|
|
18683
|
+
return element;
|
|
18684
|
+
})();
|
|
18685
|
+
const onmousedown = (e) => {
|
|
18686
|
+
if (!validationInterface.validationMessage) {
|
|
18687
|
+
return;
|
|
18688
|
+
}
|
|
18689
|
+
if (failedConstraintInfo && failedConstraintInfo.status === "error") {
|
|
18690
|
+
return;
|
|
18691
|
+
}
|
|
18692
|
+
resetOnInteraction(e);
|
|
18693
|
+
};
|
|
18694
|
+
interactionTarget.addEventListener("mousedown", onmousedown);
|
|
18695
|
+
addTeardown(() => {
|
|
18696
|
+
interactionTarget.removeEventListener("mousedown", onmousedown);
|
|
18697
|
+
});
|
|
18698
|
+
}
|
|
18699
|
+
|
|
18700
|
+
check_on_hidden_input_value: {
|
|
18701
|
+
// Hidden inputs (used by Select, List) don't fire "input" or "change" events
|
|
18702
|
+
// when their value is set programmatically. We intercept the value setter to
|
|
18703
|
+
// detect those changes and re-run validation.
|
|
18704
|
+
if (element.type !== "hidden") {
|
|
18705
|
+
break check_on_hidden_input_value;
|
|
18706
|
+
}
|
|
18707
|
+
const nativeDescriptor = Object.getOwnPropertyDescriptor(
|
|
18708
|
+
HTMLInputElement.prototype,
|
|
18709
|
+
"value",
|
|
18710
|
+
);
|
|
18711
|
+
Object.defineProperty(element, "value", {
|
|
18712
|
+
get() {
|
|
18713
|
+
return nativeDescriptor.get.call(this);
|
|
18714
|
+
},
|
|
18715
|
+
set(newValue) {
|
|
18716
|
+
nativeDescriptor.set.call(this, newValue);
|
|
18717
|
+
resetOnInteraction(new CustomEvent("programmatic_value_change"));
|
|
18718
|
+
},
|
|
18719
|
+
configurable: true,
|
|
18720
|
+
});
|
|
18721
|
+
addTeardown(() => {
|
|
18722
|
+
delete element.value;
|
|
18723
|
+
});
|
|
18724
|
+
}
|
|
18725
|
+
|
|
18455
18726
|
{
|
|
18456
18727
|
// this ensure we re-check validity (and remove message no longer relevant)
|
|
18457
18728
|
// once the action ends (used to remove the NOT_BUSY_CONSTRAINT message)
|
|
18458
|
-
const
|
|
18729
|
+
const onNaviActionEnd = () => {
|
|
18459
18730
|
checkValidity();
|
|
18460
18731
|
};
|
|
18461
|
-
element.addEventListener("
|
|
18732
|
+
element.addEventListener("navi_action_end", onNaviActionEnd);
|
|
18462
18733
|
addTeardown(() => {
|
|
18463
|
-
element.removeEventListener("
|
|
18734
|
+
element.removeEventListener("navi_action_end", onNaviActionEnd);
|
|
18464
18735
|
});
|
|
18465
18736
|
}
|
|
18466
18737
|
|
|
@@ -18678,7 +18949,7 @@ const installCustomConstraintValidation = (
|
|
|
18678
18949
|
form.setAttribute("novalidate", ""); // make sure browser don't prevent "submit" nor display messages
|
|
18679
18950
|
const removeListener = addEventListener(form, "submit", (e) => {
|
|
18680
18951
|
e.preventDefault();
|
|
18681
|
-
const actionCustomEvent = new CustomEvent("
|
|
18952
|
+
const actionCustomEvent = new CustomEvent("navi_action", {
|
|
18682
18953
|
detail: {
|
|
18683
18954
|
action: null,
|
|
18684
18955
|
event: e,
|
|
@@ -18799,7 +19070,7 @@ const dispatchActionRequestedCustomEvent = (
|
|
|
18799
19070
|
elementWithAction,
|
|
18800
19071
|
{ actionOrigin = "action_prop", event, requester },
|
|
18801
19072
|
) => {
|
|
18802
|
-
const actionRequestedCustomEvent = new CustomEvent("
|
|
19073
|
+
const actionRequestedCustomEvent = new CustomEvent("navi_action_requested", {
|
|
18803
19074
|
cancelable: true,
|
|
18804
19075
|
detail: {
|
|
18805
19076
|
actionOrigin,
|
|
@@ -19712,7 +19983,16 @@ const useBoundAction = (action, actionParamsSignal) => {
|
|
|
19712
19983
|
const actionCallbackRef = useRef();
|
|
19713
19984
|
|
|
19714
19985
|
if (!action) {
|
|
19715
|
-
|
|
19986
|
+
const existingAction = actionRef.current;
|
|
19987
|
+
if (existingAction) {
|
|
19988
|
+
return existingAction;
|
|
19989
|
+
}
|
|
19990
|
+
const noopAction = createAction(() => {}, { params: undefined });
|
|
19991
|
+
const noopActionBound = actionParamsSignal
|
|
19992
|
+
? noopAction.bindParams(actionParamsSignal)
|
|
19993
|
+
: noopAction;
|
|
19994
|
+
actionRef.current = noopActionBound;
|
|
19995
|
+
return noopActionBound;
|
|
19716
19996
|
}
|
|
19717
19997
|
if (isFunctionButNotAnActionFunction(action)) {
|
|
19718
19998
|
actionCallbackRef.current = action;
|
|
@@ -19875,7 +20155,7 @@ const useExecuteAction = (
|
|
|
19875
20155
|
const validationMessageTarget = requester || elementRef.current;
|
|
19876
20156
|
validationMessageTargetRef.current = validationMessageTarget;
|
|
19877
20157
|
|
|
19878
|
-
dispatchCustomEvent("
|
|
20158
|
+
dispatchCustomEvent("navi_action_start", {
|
|
19879
20159
|
detail: sharedActionEventDetail,
|
|
19880
20160
|
});
|
|
19881
20161
|
|
|
@@ -19902,7 +20182,7 @@ const useExecuteAction = (
|
|
|
19902
20182
|
// but other side effects might do this
|
|
19903
20183
|
elementRef.current
|
|
19904
20184
|
) {
|
|
19905
|
-
dispatchCustomEvent("
|
|
20185
|
+
dispatchCustomEvent("navi_action_abort", {
|
|
19906
20186
|
detail: {
|
|
19907
20187
|
...sharedActionEventDetail,
|
|
19908
20188
|
reason,
|
|
@@ -19917,7 +20197,7 @@ const useExecuteAction = (
|
|
|
19917
20197
|
// but other side effects might do this
|
|
19918
20198
|
elementRef.current
|
|
19919
20199
|
) {
|
|
19920
|
-
dispatchCustomEvent("
|
|
20200
|
+
dispatchCustomEvent("navi_action_error", {
|
|
19921
20201
|
detail: {
|
|
19922
20202
|
...sharedActionEventDetail,
|
|
19923
20203
|
error,
|
|
@@ -19937,7 +20217,7 @@ const useExecuteAction = (
|
|
|
19937
20217
|
// but other side effects might do this
|
|
19938
20218
|
elementRef.current
|
|
19939
20219
|
) {
|
|
19940
|
-
dispatchCustomEvent("
|
|
20220
|
+
dispatchCustomEvent("navi_action_end", {
|
|
19941
20221
|
detail: {
|
|
19942
20222
|
...sharedActionEventDetail,
|
|
19943
20223
|
data,
|
|
@@ -20245,7 +20525,7 @@ const applyKeyboardShortcuts = (shortcuts, keyboardEvent) => {
|
|
|
20245
20525
|
continue;
|
|
20246
20526
|
}
|
|
20247
20527
|
const returnValue = shortcutCandidate.handler(keyboardEvent);
|
|
20248
|
-
if (returnValue) {
|
|
20528
|
+
if (returnValue === false) {
|
|
20249
20529
|
keyboardEvent.preventDefault();
|
|
20250
20530
|
}
|
|
20251
20531
|
return shortcutCandidate;
|
|
@@ -20372,121 +20652,6 @@ const isSameKey = (browserEventKey, key) => {
|
|
|
20372
20652
|
return false;
|
|
20373
20653
|
};
|
|
20374
20654
|
|
|
20375
|
-
/**
|
|
20376
|
-
* Toggles a `data-dark-background` attribute on the referenced element based on its
|
|
20377
|
-
* computed background color. Pair it with a CSS variable to get automatic
|
|
20378
|
-
* light/dark text without hard-coding colors:
|
|
20379
|
-
*
|
|
20380
|
-
* ```css
|
|
20381
|
-
* .my-element {
|
|
20382
|
-
* --color-contrasting: black;
|
|
20383
|
-
* &[data-dark-background] {
|
|
20384
|
-
* --color-contrasting: white;
|
|
20385
|
-
* }
|
|
20386
|
-
* color: var(--color-contrasting);
|
|
20387
|
-
* }
|
|
20388
|
-
* ```
|
|
20389
|
-
*
|
|
20390
|
-
* - `data-dark-background` is **set** when the background is dark enough that white text
|
|
20391
|
-
* provides better (or equal) contrast.
|
|
20392
|
-
* - `data-dark-background` is **absent** when black text is the better choice.
|
|
20393
|
-
*
|
|
20394
|
-
* @param {import("preact").RefObject} ref - Ref to the element that receives
|
|
20395
|
-
* the `data-dark-background` attribute and is also passed to `contrastColor` for
|
|
20396
|
-
* resolving CSS variables.
|
|
20397
|
-
* @param {object} [options]
|
|
20398
|
-
* @param {string} [options.backgroundElementSelector] - CSS selector relative
|
|
20399
|
-
* to `ref.current` pointing to a child element whose `background-color`
|
|
20400
|
-
* should be tested instead of the element itself. Useful when the element
|
|
20401
|
-
* has a transparent background but contains a coloured child (e.g. a fill
|
|
20402
|
-
* bar inside a track).
|
|
20403
|
-
* @param {string} [options.colorProperty] - CSS property to read instead of
|
|
20404
|
-
* `background-color`. Useful for SVG elements where the color is expressed
|
|
20405
|
-
* as `fill` or `stroke`.
|
|
20406
|
-
*/
|
|
20407
|
-
const useDarkBackgroundAttribute = (
|
|
20408
|
-
ref,
|
|
20409
|
-
deps = [],
|
|
20410
|
-
{
|
|
20411
|
-
backgroundElementSelector,
|
|
20412
|
-
colorProperty = "backgroundColor",
|
|
20413
|
-
attributeName = "data-dark-background",
|
|
20414
|
-
luminanceThreshold,
|
|
20415
|
-
hardcoded = {},
|
|
20416
|
-
} = {},
|
|
20417
|
-
) => {
|
|
20418
|
-
const innerDeps = [
|
|
20419
|
-
...deps,
|
|
20420
|
-
// ref can change if the component passes a different ref on different renders
|
|
20421
|
-
// (e.g. to control which element's color is being checked by switching the ref)
|
|
20422
|
-
ref,
|
|
20423
|
-
// backgroundElementSelector can change if the component passes a different selector on different renders
|
|
20424
|
-
// (e.g. to control which child element's color is being checked by switching the selector)
|
|
20425
|
-
backgroundElementSelector,
|
|
20426
|
-
colorProperty,
|
|
20427
|
-
];
|
|
20428
|
-
|
|
20429
|
-
const hardcodedMap = new Map();
|
|
20430
|
-
for (const key of Object.keys(hardcoded)) {
|
|
20431
|
-
const value = hardcoded[key];
|
|
20432
|
-
innerDeps.push(key, value);
|
|
20433
|
-
const colorString = normalizeColorString(key);
|
|
20434
|
-
hardcodedMap.set(colorString, value);
|
|
20435
|
-
}
|
|
20436
|
-
|
|
20437
|
-
useLayoutEffect(() => {
|
|
20438
|
-
const el = ref.current;
|
|
20439
|
-
if (!el) {
|
|
20440
|
-
return undefined;
|
|
20441
|
-
}
|
|
20442
|
-
let elementToCheck = el;
|
|
20443
|
-
if (backgroundElementSelector) {
|
|
20444
|
-
elementToCheck = el.querySelector(backgroundElementSelector);
|
|
20445
|
-
if (!elementToCheck) {
|
|
20446
|
-
return undefined;
|
|
20447
|
-
}
|
|
20448
|
-
}
|
|
20449
|
-
const updateAttribute = () => {
|
|
20450
|
-
const computedStyle = getComputedStyle(elementToCheck);
|
|
20451
|
-
const color = computedStyle[colorProperty];
|
|
20452
|
-
if (!color) {
|
|
20453
|
-
el.removeAttribute(attributeName);
|
|
20454
|
-
return;
|
|
20455
|
-
}
|
|
20456
|
-
const colorString = normalizeColorString(color, el);
|
|
20457
|
-
const hardcodedContrast = hardcodedMap.get(colorString);
|
|
20458
|
-
let isDark;
|
|
20459
|
-
if (hardcodedContrast) {
|
|
20460
|
-
isDark = hardcodedContrast === "white";
|
|
20461
|
-
} else if (luminanceThreshold !== undefined) {
|
|
20462
|
-
const luminance = resolveColorLuminance(color, el);
|
|
20463
|
-
isDark = luminance !== undefined && luminance <= luminanceThreshold;
|
|
20464
|
-
} else {
|
|
20465
|
-
isDark = contrastColor(color, el) === "white";
|
|
20466
|
-
}
|
|
20467
|
-
if (isDark) {
|
|
20468
|
-
el.setAttribute(attributeName, "");
|
|
20469
|
-
} else {
|
|
20470
|
-
el.removeAttribute(attributeName);
|
|
20471
|
-
}
|
|
20472
|
-
};
|
|
20473
|
-
updateAttribute();
|
|
20474
|
-
el.addEventListener(NAVI_PSEUDO_STATE_CUSTOM_EVENT, updateAttribute);
|
|
20475
|
-
return () => {
|
|
20476
|
-
el.removeEventListener(NAVI_PSEUDO_STATE_CUSTOM_EVENT, updateAttribute);
|
|
20477
|
-
el.removeAttribute(attributeName);
|
|
20478
|
-
};
|
|
20479
|
-
}, innerDeps);
|
|
20480
|
-
};
|
|
20481
|
-
|
|
20482
|
-
const normalizeColorString = (color, el) => {
|
|
20483
|
-
const colorRgba = resolveCSSColor(color, el);
|
|
20484
|
-
if (!colorRgba) {
|
|
20485
|
-
return "";
|
|
20486
|
-
}
|
|
20487
|
-
return String(colorRgba);
|
|
20488
|
-
};
|
|
20489
|
-
|
|
20490
20655
|
const useInitialTextSelection = (ref, textSelection) => {
|
|
20491
20656
|
const deps = [];
|
|
20492
20657
|
if (Array.isArray(textSelection)) {
|
|
@@ -20586,7 +20751,8 @@ const selectByTextStrings = (element, range, startText, endText) => {
|
|
|
20586
20751
|
}
|
|
20587
20752
|
};
|
|
20588
20753
|
|
|
20589
|
-
installImportMetaCssBuild(import.meta)
|
|
20754
|
+
installImportMetaCssBuild(import.meta);// https://jsfiddle.net/v5xzJ/4/
|
|
20755
|
+
const css$A = /* css */`
|
|
20590
20756
|
@layer navi {
|
|
20591
20757
|
.navi_text {
|
|
20592
20758
|
&[data-skeleton] {
|
|
@@ -20601,10 +20767,6 @@ installImportMetaCssBuild(import.meta);const css$y = /* css */`
|
|
|
20601
20767
|
.navi_text {
|
|
20602
20768
|
position: relative;
|
|
20603
20769
|
|
|
20604
|
-
&[data-dark-background] {
|
|
20605
|
-
color: white;
|
|
20606
|
-
}
|
|
20607
|
-
|
|
20608
20770
|
/* There is a chrome specific bug that prevents text-transform: capitalize to be applied in nested DOM structure */
|
|
20609
20771
|
/* The CSS below ensure capitalize is propagated to the bold clones */
|
|
20610
20772
|
&[data-capitalize] {
|
|
@@ -20943,7 +21105,14 @@ const OverflowPinnedElementContext = createContext(null);
|
|
|
20943
21105
|
* like the skeleton container).
|
|
20944
21106
|
*/
|
|
20945
21107
|
const Text = props => {
|
|
20946
|
-
|
|
21108
|
+
const defaultRef = useRef();
|
|
21109
|
+
const ref = props.ref || defaultRef;
|
|
21110
|
+
return jsx(TextDispatcher, {
|
|
21111
|
+
...props,
|
|
21112
|
+
ref: ref
|
|
21113
|
+
});
|
|
21114
|
+
};
|
|
21115
|
+
const TextDispatcher = props => {
|
|
20947
21116
|
if (props.loading || props.skeleton) {
|
|
20948
21117
|
return jsx(TextSkeleton, {
|
|
20949
21118
|
...props
|
|
@@ -20964,10 +21133,81 @@ const Text = props => {
|
|
|
20964
21133
|
...props
|
|
20965
21134
|
});
|
|
20966
21135
|
}
|
|
20967
|
-
return jsx(
|
|
21136
|
+
return jsx(TextUI, {
|
|
20968
21137
|
...props
|
|
20969
21138
|
});
|
|
20970
21139
|
};
|
|
21140
|
+
const TextUI = props => {
|
|
21141
|
+
import.meta.css = [css$A, "@jsenv/navi/src/text/text.jsx"];
|
|
21142
|
+
let {
|
|
21143
|
+
ref,
|
|
21144
|
+
spacing,
|
|
21145
|
+
preventSpaceUnderlines = false,
|
|
21146
|
+
boldStable,
|
|
21147
|
+
holdSpaceForStyle,
|
|
21148
|
+
capitalize,
|
|
21149
|
+
children,
|
|
21150
|
+
childrenOutsideFlow,
|
|
21151
|
+
...rest
|
|
21152
|
+
} = props;
|
|
21153
|
+
const defaultSpace = preventSpaceUnderlines ? FAKE_SPACE : REGULAR_SPACE;
|
|
21154
|
+
const resolvedSpacing = spacing ?? defaultSpace;
|
|
21155
|
+
const boxProps = {
|
|
21156
|
+
"as": "span",
|
|
21157
|
+
"data-capitalize": capitalize ? "" : undefined,
|
|
21158
|
+
...rest,
|
|
21159
|
+
ref,
|
|
21160
|
+
"baseClassName": withPropsClassName("navi_text", rest.baseClassName)
|
|
21161
|
+
};
|
|
21162
|
+
const shouldPreserveSpacing = rest.as === "pre" || rest.flex || rest.grid;
|
|
21163
|
+
if (shouldPreserveSpacing) {
|
|
21164
|
+
boxProps.spacing = resolvedSpacing;
|
|
21165
|
+
} else {
|
|
21166
|
+
children = applySpacingOnTextChildren(children, resolvedSpacing, defaultSpace);
|
|
21167
|
+
}
|
|
21168
|
+
if (boldStable) {
|
|
21169
|
+
const {
|
|
21170
|
+
bold
|
|
21171
|
+
} = boxProps;
|
|
21172
|
+
return jsxs(Box, {
|
|
21173
|
+
...boxProps,
|
|
21174
|
+
bold: undefined,
|
|
21175
|
+
"data-bold": bold ? "" : undefined,
|
|
21176
|
+
"data-contains-absolute-child": "",
|
|
21177
|
+
children: [jsx("span", {
|
|
21178
|
+
className: "navi_text_bold_background",
|
|
21179
|
+
"aria-hidden": "true",
|
|
21180
|
+
children: children
|
|
21181
|
+
}), children, childrenOutsideFlow]
|
|
21182
|
+
});
|
|
21183
|
+
}
|
|
21184
|
+
if (holdSpaceForStyle) {
|
|
21185
|
+
// The sizer technique prevents layout shifts when styles that affect text dimensions change.
|
|
21186
|
+
// - navi_text_sizer_placeholder: invisible, rendered with holdSpaceForStyle applied so it
|
|
21187
|
+
// always occupies the "maximum" dimensions (e.g. bold + larger font-size).
|
|
21188
|
+
// - navi_text_sizer_overlay: absolutely positioned on top, renders the actual visible text
|
|
21189
|
+
// with its current style. Transitions can be applied on this element from the outside.
|
|
21190
|
+
return jsxs(Box, {
|
|
21191
|
+
...boxProps,
|
|
21192
|
+
children: [jsxs("span", {
|
|
21193
|
+
className: "navi_text_sizer",
|
|
21194
|
+
children: [jsx("span", {
|
|
21195
|
+
className: "navi_text_sizer_placeholder",
|
|
21196
|
+
"aria-hidden": "true",
|
|
21197
|
+
style: holdSpaceForStyle,
|
|
21198
|
+
children: children
|
|
21199
|
+
}), jsx("span", {
|
|
21200
|
+
className: "navi_text_sizer_overlay",
|
|
21201
|
+
children: children
|
|
21202
|
+
})]
|
|
21203
|
+
}), childrenOutsideFlow]
|
|
21204
|
+
});
|
|
21205
|
+
}
|
|
21206
|
+
return jsxs(Box, {
|
|
21207
|
+
...boxProps,
|
|
21208
|
+
children: [children, childrenOutsideFlow]
|
|
21209
|
+
});
|
|
21210
|
+
};
|
|
20971
21211
|
const TextSkeleton = ({
|
|
20972
21212
|
loading,
|
|
20973
21213
|
children,
|
|
@@ -20993,7 +21233,7 @@ const TextSkeleton = ({
|
|
|
20993
21233
|
"aria-hidden": "true",
|
|
20994
21234
|
children: "W"
|
|
20995
21235
|
});
|
|
20996
|
-
return jsx(
|
|
21236
|
+
return jsx(TextDispatcher, {
|
|
20997
21237
|
"data-skeleton": "",
|
|
20998
21238
|
"data-loading": loading ? "" : undefined,
|
|
20999
21239
|
...props,
|
|
@@ -21009,7 +21249,7 @@ const TextOverflow = ({
|
|
|
21009
21249
|
...rest
|
|
21010
21250
|
}) => {
|
|
21011
21251
|
const [OverflowPinnedElement, setOverflowPinnedElement] = useState(null);
|
|
21012
|
-
return jsx(
|
|
21252
|
+
return jsx(TextDispatcher, {
|
|
21013
21253
|
flex: true,
|
|
21014
21254
|
block: true,
|
|
21015
21255
|
as: "div",
|
|
@@ -21057,92 +21297,19 @@ const TextOverflowPinned = ({
|
|
|
21057
21297
|
return text;
|
|
21058
21298
|
};
|
|
21059
21299
|
const TextWithSelectRange = ({
|
|
21300
|
+
ref,
|
|
21060
21301
|
selectRange,
|
|
21061
21302
|
...props
|
|
21062
21303
|
}) => {
|
|
21063
|
-
const defaultRef = useRef();
|
|
21064
|
-
const ref = props.ref || defaultRef;
|
|
21065
21304
|
useInitialTextSelection(ref, selectRange);
|
|
21066
|
-
return jsx(
|
|
21305
|
+
return jsx(TextDispatcher, {
|
|
21306
|
+
...props,
|
|
21067
21307
|
ref: ref,
|
|
21068
|
-
|
|
21069
|
-
});
|
|
21070
|
-
};
|
|
21071
|
-
const TextBasic = ({
|
|
21072
|
-
spacing,
|
|
21073
|
-
preventSpaceUnderlines = false,
|
|
21074
|
-
boldStable,
|
|
21075
|
-
holdSpaceForStyle,
|
|
21076
|
-
capitalize,
|
|
21077
|
-
children,
|
|
21078
|
-
childrenOutsideFlow,
|
|
21079
|
-
basePseudoState,
|
|
21080
|
-
...rest
|
|
21081
|
-
}) => {
|
|
21082
|
-
const defaultRef = useRef();
|
|
21083
|
-
const ref = rest.ref || defaultRef;
|
|
21084
|
-
const bgDeps = basePseudoState ? Object.values(basePseudoState) : [];
|
|
21085
|
-
useDarkBackgroundAttribute(ref, bgDeps);
|
|
21086
|
-
const defaultSpace = preventSpaceUnderlines ? FAKE_SPACE : REGULAR_SPACE;
|
|
21087
|
-
const resolvedSpacing = spacing ?? defaultSpace;
|
|
21088
|
-
const boxProps = {
|
|
21089
|
-
"as": "span",
|
|
21090
|
-
"data-capitalize": capitalize ? "" : undefined,
|
|
21091
|
-
...rest,
|
|
21092
|
-
ref,
|
|
21093
|
-
"baseClassName": withPropsClassName("navi_text", rest.baseClassName)
|
|
21094
|
-
};
|
|
21095
|
-
const shouldPreserveSpacing = rest.as === "pre" || rest.flex || rest.grid;
|
|
21096
|
-
if (shouldPreserveSpacing) {
|
|
21097
|
-
boxProps.spacing = resolvedSpacing;
|
|
21098
|
-
} else {
|
|
21099
|
-
children = applySpacingOnTextChildren(children, resolvedSpacing, defaultSpace);
|
|
21100
|
-
}
|
|
21101
|
-
if (boldStable) {
|
|
21102
|
-
const {
|
|
21103
|
-
bold
|
|
21104
|
-
} = boxProps;
|
|
21105
|
-
return jsxs(Box, {
|
|
21106
|
-
...boxProps,
|
|
21107
|
-
bold: undefined,
|
|
21108
|
-
"data-bold": bold ? "" : undefined,
|
|
21109
|
-
"data-contains-absolute-child": "",
|
|
21110
|
-
children: [jsx("span", {
|
|
21111
|
-
className: "navi_text_bold_background",
|
|
21112
|
-
"aria-hidden": "true",
|
|
21113
|
-
children: children
|
|
21114
|
-
}), children, childrenOutsideFlow]
|
|
21115
|
-
});
|
|
21116
|
-
}
|
|
21117
|
-
if (holdSpaceForStyle) {
|
|
21118
|
-
// The sizer technique prevents layout shifts when styles that affect text dimensions change.
|
|
21119
|
-
// - navi_text_sizer_placeholder: invisible, rendered with holdSpaceForStyle applied so it
|
|
21120
|
-
// always occupies the "maximum" dimensions (e.g. bold + larger font-size).
|
|
21121
|
-
// - navi_text_sizer_overlay: absolutely positioned on top, renders the actual visible text
|
|
21122
|
-
// with its current style. Transitions can be applied on this element from the outside.
|
|
21123
|
-
return jsxs(Box, {
|
|
21124
|
-
...boxProps,
|
|
21125
|
-
children: [jsxs("span", {
|
|
21126
|
-
className: "navi_text_sizer",
|
|
21127
|
-
children: [jsx("span", {
|
|
21128
|
-
className: "navi_text_sizer_placeholder",
|
|
21129
|
-
"aria-hidden": "true",
|
|
21130
|
-
style: holdSpaceForStyle,
|
|
21131
|
-
children: children
|
|
21132
|
-
}), jsx("span", {
|
|
21133
|
-
className: "navi_text_sizer_overlay",
|
|
21134
|
-
children: children
|
|
21135
|
-
})]
|
|
21136
|
-
}), childrenOutsideFlow]
|
|
21137
|
-
});
|
|
21138
|
-
}
|
|
21139
|
-
return jsxs(Box, {
|
|
21140
|
-
...boxProps,
|
|
21141
|
-
children: [children, childrenOutsideFlow]
|
|
21308
|
+
selectRange: undefined
|
|
21142
21309
|
});
|
|
21143
21310
|
};
|
|
21144
21311
|
|
|
21145
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
21312
|
+
installImportMetaCssBuild(import.meta);const css$z = /* css */`
|
|
21146
21313
|
.navi_text_anchor {
|
|
21147
21314
|
vertical-align: baseline;
|
|
21148
21315
|
user-select: none;
|
|
@@ -21177,7 +21344,7 @@ const TextAnchor = ({
|
|
|
21177
21344
|
textSize,
|
|
21178
21345
|
lineLayout
|
|
21179
21346
|
}) => {
|
|
21180
|
-
import.meta.css = [css$
|
|
21347
|
+
import.meta.css = [css$z, "@jsenv/navi/src/text/text_anchor.jsx"];
|
|
21181
21348
|
const anchorRef = useRef();
|
|
21182
21349
|
useLayoutEffect(() => {
|
|
21183
21350
|
const anchorEl = anchorRef.current;
|
|
@@ -21275,7 +21442,7 @@ const computeTopOffset = ({
|
|
|
21275
21442
|
};
|
|
21276
21443
|
const charTopCanvas = document.createElement("canvas");
|
|
21277
21444
|
|
|
21278
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
21445
|
+
installImportMetaCssBuild(import.meta);const css$y = /* css */`
|
|
21279
21446
|
@layer navi {
|
|
21280
21447
|
/* Ensure data attributes from box.jsx can win to update display */
|
|
21281
21448
|
.navi_icon {
|
|
@@ -21348,7 +21515,7 @@ const Icon = ({
|
|
|
21348
21515
|
lineLayout,
|
|
21349
21516
|
...props
|
|
21350
21517
|
}) => {
|
|
21351
|
-
import.meta.css = [css$
|
|
21518
|
+
import.meta.css = [css$y, "@jsenv/navi/src/text/icon.jsx"];
|
|
21352
21519
|
const innerChildren = href ? jsx("svg", {
|
|
21353
21520
|
width: "100%",
|
|
21354
21521
|
height: "100%",
|
|
@@ -21676,6 +21843,100 @@ document.body.addEventListener(
|
|
|
21676
21843
|
{ capture: true },
|
|
21677
21844
|
);
|
|
21678
21845
|
|
|
21846
|
+
const LIGHT_ACCENT_ATTRIBUTE = "data-accent-light";
|
|
21847
|
+
const VERY_LIGHT_ACCENT_ATTRIBUTE = "data-accent-very-light";
|
|
21848
|
+
const DARK_CONTRAST_ATTRIBUTE = "data-accent-needs-dark-fg";
|
|
21849
|
+
const LIGHT_LUMINANCE_THRESHOLD = 0.5;
|
|
21850
|
+
const VERY_LIGHT_LUMINANCE_THRESHOLD = 0.92;
|
|
21851
|
+
const DARK_CONTRAST_LIGHTNESS_THRESHOLD = 0.65;
|
|
21852
|
+
|
|
21853
|
+
/**
|
|
21854
|
+
* Sets data attributes on an element based on the OKLCH lightness and contrast
|
|
21855
|
+
* of a CSS color (typically an accent/brand color). All thresholds use OKLCH L
|
|
21856
|
+
* (0–1, perceptually uniform scale).
|
|
21857
|
+
*
|
|
21858
|
+
* Three boolean attributes are managed independently:
|
|
21859
|
+
*
|
|
21860
|
+
* ## `data-accent-light` (set when OKLCH L > 0.5)
|
|
21861
|
+
* The accent color is perceptually light (orange, green, pink, yellow…).
|
|
21862
|
+
* Use to adjust color-mix direction so hover/active effects darken toward
|
|
21863
|
+
* black instead of lightening toward white.
|
|
21864
|
+
*
|
|
21865
|
+
* ## `data-accent-very-light` (set when OKLCH L > 0.92)
|
|
21866
|
+
* The accent color is near-white or white. Use to show a grey background on
|
|
21867
|
+
* unchecked state so the component boundary remains visible against white
|
|
21868
|
+
* page backgrounds.
|
|
21869
|
+
*
|
|
21870
|
+
* ## `data-accent-needs-dark-fg` (set when OKLCH L > 0.65)
|
|
21871
|
+
* The best contrasting foreground color against the accent is dark (black).
|
|
21872
|
+
* Use to render checkmarks, icons, or text in a dark color instead of white.
|
|
21873
|
+
*
|
|
21874
|
+
* @param {import("preact").RefObject} ref - Ref to the root element that receives the attributes.
|
|
21875
|
+
* @param {string} accentColor - The accent color value. When it changes, attributes are recomputed.
|
|
21876
|
+
* @param {object} [options]
|
|
21877
|
+
* @param {string} [options.elementSelector] - CSS selector to find the element whose computed color is read.
|
|
21878
|
+
* Defaults to the root element itself. Useful when the color is applied to a probe/child element.
|
|
21879
|
+
* @param {string} [options.colorProperty="backgroundColor"] - Computed style property to read (e.g. "color", "borderColor").
|
|
21880
|
+
*/
|
|
21881
|
+
const useAccentColorAttributes = (
|
|
21882
|
+
ref,
|
|
21883
|
+
accentColor,
|
|
21884
|
+
{ elementSelector, colorProperty = "backgroundColor" } = {},
|
|
21885
|
+
) => {
|
|
21886
|
+
useLayoutEffect(() => {
|
|
21887
|
+
const el = ref.current;
|
|
21888
|
+
if (!el) {
|
|
21889
|
+
return undefined;
|
|
21890
|
+
}
|
|
21891
|
+
let elementToCheck = el;
|
|
21892
|
+
if (elementSelector) {
|
|
21893
|
+
elementToCheck = el.querySelector(elementSelector);
|
|
21894
|
+
if (!elementToCheck) {
|
|
21895
|
+
return undefined;
|
|
21896
|
+
}
|
|
21897
|
+
}
|
|
21898
|
+
const updateAttributes = () => {
|
|
21899
|
+
const computedStyle = getComputedStyle(elementToCheck);
|
|
21900
|
+
const color = computedStyle[colorProperty];
|
|
21901
|
+
if (!color) {
|
|
21902
|
+
el.removeAttribute(LIGHT_ACCENT_ATTRIBUTE);
|
|
21903
|
+
el.removeAttribute(VERY_LIGHT_ACCENT_ATTRIBUTE);
|
|
21904
|
+
el.removeAttribute(DARK_CONTRAST_ATTRIBUTE);
|
|
21905
|
+
return;
|
|
21906
|
+
}
|
|
21907
|
+
const luminance = resolveOklchLightness(color, el);
|
|
21908
|
+
if (luminance !== null && luminance > LIGHT_LUMINANCE_THRESHOLD) {
|
|
21909
|
+
el.setAttribute(LIGHT_ACCENT_ATTRIBUTE, "");
|
|
21910
|
+
} else {
|
|
21911
|
+
el.removeAttribute(LIGHT_ACCENT_ATTRIBUTE);
|
|
21912
|
+
}
|
|
21913
|
+
if (luminance !== null && luminance > VERY_LIGHT_LUMINANCE_THRESHOLD) {
|
|
21914
|
+
el.setAttribute(VERY_LIGHT_ACCENT_ATTRIBUTE, "");
|
|
21915
|
+
} else {
|
|
21916
|
+
el.removeAttribute(VERY_LIGHT_ACCENT_ATTRIBUTE);
|
|
21917
|
+
}
|
|
21918
|
+
const bestContrast = contrastColor(
|
|
21919
|
+
color,
|
|
21920
|
+
el,
|
|
21921
|
+
DARK_CONTRAST_LIGHTNESS_THRESHOLD,
|
|
21922
|
+
);
|
|
21923
|
+
if (bestContrast === "black") {
|
|
21924
|
+
el.setAttribute(DARK_CONTRAST_ATTRIBUTE, "");
|
|
21925
|
+
} else {
|
|
21926
|
+
el.removeAttribute(DARK_CONTRAST_ATTRIBUTE);
|
|
21927
|
+
}
|
|
21928
|
+
};
|
|
21929
|
+
updateAttributes();
|
|
21930
|
+
el.addEventListener(NAVI_PSEUDO_STATE_CUSTOM_EVENT, updateAttributes);
|
|
21931
|
+
return () => {
|
|
21932
|
+
el.removeEventListener(NAVI_PSEUDO_STATE_CUSTOM_EVENT, updateAttributes);
|
|
21933
|
+
el.removeAttribute(LIGHT_ACCENT_ATTRIBUTE);
|
|
21934
|
+
el.removeAttribute(VERY_LIGHT_ACCENT_ATTRIBUTE);
|
|
21935
|
+
el.removeAttribute(DARK_CONTRAST_ATTRIBUTE);
|
|
21936
|
+
};
|
|
21937
|
+
}, [ref, accentColor, elementSelector, colorProperty]);
|
|
21938
|
+
};
|
|
21939
|
+
|
|
21679
21940
|
const useFormEvents = (
|
|
21680
21941
|
elementRef,
|
|
21681
21942
|
{
|
|
@@ -21713,12 +21974,12 @@ const useFormEvents = (
|
|
|
21713
21974
|
}
|
|
21714
21975
|
return addManyEventListeners(form, {
|
|
21715
21976
|
reset: onFormReset,
|
|
21716
|
-
|
|
21717
|
-
|
|
21718
|
-
|
|
21719
|
-
|
|
21720
|
-
|
|
21721
|
-
|
|
21977
|
+
navi_action_requested: onFormActionRequested,
|
|
21978
|
+
navi_action_prevented: onFormActionPrevented,
|
|
21979
|
+
navi_action_start: onFormActionStart,
|
|
21980
|
+
navi_action_abort: onFormActionAbort,
|
|
21981
|
+
navi_action_error: onFormActionError,
|
|
21982
|
+
navi_action_end: onFormActionEnd,
|
|
21722
21983
|
});
|
|
21723
21984
|
}, [
|
|
21724
21985
|
onFormReset,
|
|
@@ -22053,7 +22314,7 @@ const useUIGroupStateController = (
|
|
|
22053
22314
|
return notifyParentAboutChildUnmount;
|
|
22054
22315
|
}, []);
|
|
22055
22316
|
|
|
22056
|
-
const onChange = (_, e) => {
|
|
22317
|
+
const onChange = (_, e, { notifyExternal = true } = {}) => {
|
|
22057
22318
|
if (groupIsRenderingRef.current) {
|
|
22058
22319
|
pendingChangeRef.current = true;
|
|
22059
22320
|
return;
|
|
@@ -22063,7 +22324,7 @@ const useUIGroupStateController = (
|
|
|
22063
22324
|
emptyState,
|
|
22064
22325
|
);
|
|
22065
22326
|
const uiStateController = uiStateControllerRef.current;
|
|
22066
|
-
uiStateController.setUIState(newUIState, e);
|
|
22327
|
+
uiStateController.setUIState(newUIState, e, { notifyExternal });
|
|
22067
22328
|
};
|
|
22068
22329
|
|
|
22069
22330
|
useLayoutEffect(() => {
|
|
@@ -22073,6 +22334,7 @@ const useUIGroupStateController = (
|
|
|
22073
22334
|
onChange(
|
|
22074
22335
|
null,
|
|
22075
22336
|
new CustomEvent(`${componentType}_batched_ui_state_update`),
|
|
22337
|
+
{ notifyExternal: false },
|
|
22076
22338
|
);
|
|
22077
22339
|
}
|
|
22078
22340
|
});
|
|
@@ -22098,7 +22360,7 @@ const useUIGroupStateController = (
|
|
|
22098
22360
|
value,
|
|
22099
22361
|
uiAction,
|
|
22100
22362
|
uiState: emptyState,
|
|
22101
|
-
setUIState: (newUIState, e) => {
|
|
22363
|
+
setUIState: (newUIState, e, { notifyExternal = true } = {}) => {
|
|
22102
22364
|
const currentUIState = uiStateController.uiState;
|
|
22103
22365
|
if (newUIState === currentUIState) {
|
|
22104
22366
|
return;
|
|
@@ -22108,7 +22370,9 @@ const useUIGroupStateController = (
|
|
|
22108
22370
|
`${componentType}.setUIState(${JSON.stringify(newUIState)}, "${e.type}") -> updates from ${JSON.stringify(currentUIState)} to ${JSON.stringify(newUIState)}`,
|
|
22109
22371
|
);
|
|
22110
22372
|
publishUIState(newUIState);
|
|
22111
|
-
|
|
22373
|
+
if (notifyExternal) {
|
|
22374
|
+
uiStateController.uiAction?.(newUIState, e);
|
|
22375
|
+
}
|
|
22112
22376
|
notifyParentAboutChildUIStateChange(e);
|
|
22113
22377
|
},
|
|
22114
22378
|
registerChild: (childUIStateController) => {
|
|
@@ -22123,6 +22387,7 @@ const useUIGroupStateController = (
|
|
|
22123
22387
|
onChange(
|
|
22124
22388
|
childUIStateController,
|
|
22125
22389
|
new CustomEvent(`${childComponentType}_mount`),
|
|
22390
|
+
{ notifyExternal: false },
|
|
22126
22391
|
);
|
|
22127
22392
|
},
|
|
22128
22393
|
onChildUIStateChange: (childUIStateController, e) => {
|
|
@@ -22152,6 +22417,7 @@ const useUIGroupStateController = (
|
|
|
22152
22417
|
onChange(
|
|
22153
22418
|
childUIStateController,
|
|
22154
22419
|
new CustomEvent(`${childComponentType}_unmount`),
|
|
22420
|
+
{ notifyExternal: false },
|
|
22155
22421
|
);
|
|
22156
22422
|
},
|
|
22157
22423
|
resetUIState: (e) => {
|
|
@@ -22199,7 +22465,7 @@ const useUIState = (uiStateController) => {
|
|
|
22199
22465
|
return trackedUIState;
|
|
22200
22466
|
};
|
|
22201
22467
|
|
|
22202
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
22468
|
+
installImportMetaCssBuild(import.meta);const css$x = /* css */`
|
|
22203
22469
|
@layer navi {
|
|
22204
22470
|
.navi_button {
|
|
22205
22471
|
--button-outline-width: 1px;
|
|
@@ -22294,8 +22560,8 @@ installImportMetaCssBuild(import.meta);const css$v = /* css */`
|
|
|
22294
22560
|
touch-action: manipulation;
|
|
22295
22561
|
user-select: none;
|
|
22296
22562
|
|
|
22297
|
-
&[data-dark-
|
|
22298
|
-
--button-color:
|
|
22563
|
+
&[data-accent-needs-dark-fg] {
|
|
22564
|
+
--button-color: black;
|
|
22299
22565
|
}
|
|
22300
22566
|
|
|
22301
22567
|
&[data-icon] {
|
|
@@ -22513,7 +22779,7 @@ const ButtonUI = props => {
|
|
|
22513
22779
|
children,
|
|
22514
22780
|
...rest
|
|
22515
22781
|
} = props;
|
|
22516
|
-
import.meta.css = [css$
|
|
22782
|
+
import.meta.css = [css$x, "@jsenv/navi/src/field/button.jsx"];
|
|
22517
22783
|
const contextLoading = useContext(LoadingContext);
|
|
22518
22784
|
const contextLoadingElement = useContext(LoadingElementContext);
|
|
22519
22785
|
const contextReadOnly = useContext(ReadOnlyContext);
|
|
@@ -22537,7 +22803,7 @@ const ButtonUI = props => {
|
|
|
22537
22803
|
innerTarget = target === undefined ? isSameSite ? undefined : "_blank" : target;
|
|
22538
22804
|
innerRel = rel === undefined ? isSameSite ? undefined : "noopener noreferrer" : rel;
|
|
22539
22805
|
}
|
|
22540
|
-
|
|
22806
|
+
useAccentColorAttributes(ref, null);
|
|
22541
22807
|
const renderButtonContent = buttonProps => {
|
|
22542
22808
|
return jsxs(Text, {
|
|
22543
22809
|
...buttonProps,
|
|
@@ -22760,7 +23026,7 @@ const ButtonWithActionInsideForm = props => {
|
|
|
22760
23026
|
action: undefined,
|
|
22761
23027
|
type: type,
|
|
22762
23028
|
loading: innerLoading,
|
|
22763
|
-
|
|
23029
|
+
onnavi_action_requested: e => {
|
|
22764
23030
|
forwardActionRequested(e, actionBoundToFormParams, e.target.form);
|
|
22765
23031
|
},
|
|
22766
23032
|
children: children
|
|
@@ -22818,7 +23084,7 @@ const WarningSvg = () => {
|
|
|
22818
23084
|
});
|
|
22819
23085
|
};
|
|
22820
23086
|
|
|
22821
|
-
installImportMetaCssBuild(import.meta);
|
|
23087
|
+
installImportMetaCssBuild(import.meta);const css$w = /* css */`
|
|
22822
23088
|
@layer navi {
|
|
22823
23089
|
.navi_message_box {
|
|
22824
23090
|
--background-color-info: var(--navi-info-color-light);
|
|
@@ -22861,10 +23127,7 @@ installImportMetaCssBuild(import.meta);import.meta.css = [/* css */`
|
|
|
22861
23127
|
border-top-left-radius: 6px;
|
|
22862
23128
|
border-bottom-left-radius: 6px;
|
|
22863
23129
|
}
|
|
22864
|
-
|
|
22865
|
-
const MessageBoxPseudoClasses = [":-navi-status-info", ":-navi-status-success", ":-navi-status-warning", ":-navi-status-error"];
|
|
22866
|
-
const MessageBoxStatusContext = createContext();
|
|
22867
|
-
const MessageBoxReportTitleChildContext = createContext();
|
|
23130
|
+
`;
|
|
22868
23131
|
const MessageBox = ({
|
|
22869
23132
|
status = "info",
|
|
22870
23133
|
padding = "sm",
|
|
@@ -22874,6 +23137,7 @@ const MessageBox = ({
|
|
|
22874
23137
|
onClose,
|
|
22875
23138
|
...rest
|
|
22876
23139
|
}) => {
|
|
23140
|
+
import.meta.css = [css$w, "@jsenv/navi/src/text/message_box.jsx"];
|
|
22877
23141
|
const [hasTitleChild, setHasTitleChild] = useState(false);
|
|
22878
23142
|
const innerLeftStripe = leftStripe === undefined ? hasTitleChild : leftStripe;
|
|
22879
23143
|
if (icon === true) {
|
|
@@ -22928,8 +23192,11 @@ const MessageBox = ({
|
|
|
22928
23192
|
})
|
|
22929
23193
|
});
|
|
22930
23194
|
};
|
|
23195
|
+
const MessageBoxPseudoClasses = [":-navi-status-info", ":-navi-status-success", ":-navi-status-warning", ":-navi-status-error"];
|
|
23196
|
+
const MessageBoxStatusContext = createContext();
|
|
23197
|
+
const MessageBoxReportTitleChildContext = createContext();
|
|
22931
23198
|
|
|
22932
|
-
installImportMetaCssBuild(import.meta);
|
|
23199
|
+
installImportMetaCssBuild(import.meta);const css$v = /* css */`
|
|
22933
23200
|
.navi_message_box {
|
|
22934
23201
|
.navi_title {
|
|
22935
23202
|
margin-top: 0;
|
|
@@ -22937,13 +23204,9 @@ installImportMetaCssBuild(import.meta);import.meta.css = [/* css */`
|
|
|
22937
23204
|
color: var(--x-message-color);
|
|
22938
23205
|
}
|
|
22939
23206
|
}
|
|
22940
|
-
|
|
22941
|
-
const TitleLevelContext = createContext();
|
|
22942
|
-
const useTitleLevel = () => {
|
|
22943
|
-
return useContext(TitleLevelContext);
|
|
22944
|
-
};
|
|
22945
|
-
const TitlePseudoClasses = [":hover"];
|
|
23207
|
+
`;
|
|
22946
23208
|
const Title = props => {
|
|
23209
|
+
import.meta.css = [css$v, "@jsenv/navi/src/text/title.jsx"];
|
|
22947
23210
|
const messageBoxStatus = useContext(MessageBoxStatusContext);
|
|
22948
23211
|
const innerAs = props.as || (messageBoxStatus ? "h4" : "h1");
|
|
22949
23212
|
const titleLevel = parseInt(innerAs.slice(1));
|
|
@@ -22961,6 +23224,11 @@ const Title = props => {
|
|
|
22961
23224
|
})
|
|
22962
23225
|
});
|
|
22963
23226
|
};
|
|
23227
|
+
const TitleLevelContext = createContext();
|
|
23228
|
+
const useTitleLevel = () => {
|
|
23229
|
+
return useContext(TitleLevelContext);
|
|
23230
|
+
};
|
|
23231
|
+
const TitlePseudoClasses = [":hover"];
|
|
22964
23232
|
|
|
22965
23233
|
/**
|
|
22966
23234
|
* Hook that reactively checks if a URL is visited.
|
|
@@ -23161,11 +23429,6 @@ installImportMetaCssBuild(import.meta);const css$u = /* css */`
|
|
|
23161
23429
|
}
|
|
23162
23430
|
}
|
|
23163
23431
|
|
|
23164
|
-
/* Dark background */
|
|
23165
|
-
&[data-dark-background].navi_text {
|
|
23166
|
-
--x-link-contrasting-color: white;
|
|
23167
|
-
--x-link-color: var(--link-color, white);
|
|
23168
|
-
}
|
|
23169
23432
|
/* Interactive */
|
|
23170
23433
|
&[data-interactive] {
|
|
23171
23434
|
cursor: pointer;
|
|
@@ -24331,22 +24594,6 @@ installImportMetaCssBuild(import.meta);const css$q = /* css */`
|
|
|
24331
24594
|
}
|
|
24332
24595
|
}
|
|
24333
24596
|
`;
|
|
24334
|
-
const ReportReadOnlyOnLabelContext = createContext();
|
|
24335
|
-
const ReportDisabledOnLabelContext = createContext();
|
|
24336
|
-
const ReportInteractiveOnLabelContext = createContext();
|
|
24337
|
-
const reportReadOnlyToLabel = value => {
|
|
24338
|
-
const reportReadOnly = useContext(ReportReadOnlyOnLabelContext);
|
|
24339
|
-
reportReadOnly?.(value);
|
|
24340
|
-
};
|
|
24341
|
-
const reportInteractiveToLabel = value => {
|
|
24342
|
-
const reportInteractive = useContext(ReportInteractiveOnLabelContext);
|
|
24343
|
-
reportInteractive?.(value);
|
|
24344
|
-
};
|
|
24345
|
-
const reportDisabledToLabel = value => {
|
|
24346
|
-
const reportDisabled = useContext(ReportDisabledOnLabelContext);
|
|
24347
|
-
reportDisabled?.(value);
|
|
24348
|
-
};
|
|
24349
|
-
const LabelPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading"];
|
|
24350
24597
|
const Label = props => {
|
|
24351
24598
|
import.meta.css = [css$q, "@jsenv/navi/src/field/label.jsx"];
|
|
24352
24599
|
const {
|
|
@@ -24381,6 +24628,22 @@ const Label = props => {
|
|
|
24381
24628
|
})
|
|
24382
24629
|
});
|
|
24383
24630
|
};
|
|
24631
|
+
const LabelPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading"];
|
|
24632
|
+
const ReportReadOnlyOnLabelContext = createContext();
|
|
24633
|
+
const ReportDisabledOnLabelContext = createContext();
|
|
24634
|
+
const ReportInteractiveOnLabelContext = createContext();
|
|
24635
|
+
const reportReadOnlyToLabel = value => {
|
|
24636
|
+
const reportReadOnly = useContext(ReportReadOnlyOnLabelContext);
|
|
24637
|
+
reportReadOnly?.(value);
|
|
24638
|
+
};
|
|
24639
|
+
const reportInteractiveToLabel = value => {
|
|
24640
|
+
const reportInteractive = useContext(ReportInteractiveOnLabelContext);
|
|
24641
|
+
reportInteractive?.(value);
|
|
24642
|
+
};
|
|
24643
|
+
const reportDisabledToLabel = value => {
|
|
24644
|
+
const reportDisabled = useContext(ReportDisabledOnLabelContext);
|
|
24645
|
+
reportDisabled?.(value);
|
|
24646
|
+
};
|
|
24384
24647
|
|
|
24385
24648
|
installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
24386
24649
|
@layer navi {
|
|
@@ -24396,15 +24659,15 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
24396
24659
|
--outline-color: var(--navi-focus-outline-color);
|
|
24397
24660
|
--loader-color: var(--navi-loader-color);
|
|
24398
24661
|
--border-color: light-dark(#767676, #8e8e93);
|
|
24399
|
-
--background-color:
|
|
24662
|
+
--background-color: white;
|
|
24400
24663
|
--accent-color: light-dark(#4476ff, #3b82f6);
|
|
24401
24664
|
--background-color-checked: var(--accent-color);
|
|
24402
24665
|
--border-color-checked: var(--accent-color);
|
|
24403
|
-
--checkmark-color:
|
|
24666
|
+
--checkmark-color: white;
|
|
24404
24667
|
--cursor: pointer;
|
|
24405
24668
|
--color-mix-light: black;
|
|
24406
24669
|
--color-mix-dark: white;
|
|
24407
|
-
--color-mix: var(--color-mix-
|
|
24670
|
+
--color-mix: var(--color-mix-dark);
|
|
24408
24671
|
|
|
24409
24672
|
/* Hover */
|
|
24410
24673
|
--border-color-hover: color-mix(in srgb, var(--border-color) 60%, black);
|
|
@@ -24497,11 +24760,6 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
24497
24760
|
var(--button-background-color) 95%,
|
|
24498
24761
|
black
|
|
24499
24762
|
);
|
|
24500
|
-
|
|
24501
|
-
&[data-dark-background] {
|
|
24502
|
-
--color-mix: var(--color-mix-dark);
|
|
24503
|
-
--checkmark-color: white;
|
|
24504
|
-
}
|
|
24505
24763
|
}
|
|
24506
24764
|
}
|
|
24507
24765
|
|
|
@@ -24602,12 +24860,9 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
24602
24860
|
}
|
|
24603
24861
|
}
|
|
24604
24862
|
|
|
24605
|
-
|
|
24606
|
-
|
|
24607
|
-
--
|
|
24608
|
-
&[data-checked] {
|
|
24609
|
-
--x-background-color: var(--background-color-checked);
|
|
24610
|
-
}
|
|
24863
|
+
/* Accent color adaptations */
|
|
24864
|
+
&[data-accent-light] {
|
|
24865
|
+
--color-mix: var(--color-mix-light);
|
|
24611
24866
|
}
|
|
24612
24867
|
|
|
24613
24868
|
/* Checkbox appearance */
|
|
@@ -24629,6 +24884,16 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
24629
24884
|
transition-timing-function: ease;
|
|
24630
24885
|
}
|
|
24631
24886
|
}
|
|
24887
|
+
|
|
24888
|
+
&[data-accent-very-light] {
|
|
24889
|
+
--x-background-color: rgba(0, 0, 0, 0.15);
|
|
24890
|
+
&[data-checked] {
|
|
24891
|
+
--x-background-color: var(--background-color-checked);
|
|
24892
|
+
}
|
|
24893
|
+
}
|
|
24894
|
+
&[data-accent-needs-dark-fg] {
|
|
24895
|
+
--x-checkmark-color: rgb(55, 55, 55);
|
|
24896
|
+
}
|
|
24632
24897
|
}
|
|
24633
24898
|
|
|
24634
24899
|
/* Toggle appearance */
|
|
@@ -24689,6 +24954,10 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
24689
24954
|
);
|
|
24690
24955
|
}
|
|
24691
24956
|
}
|
|
24957
|
+
|
|
24958
|
+
&[data-accent-very-light] {
|
|
24959
|
+
--toggle-thumb-color: rgb(55, 55, 55);
|
|
24960
|
+
}
|
|
24692
24961
|
}
|
|
24693
24962
|
|
|
24694
24963
|
&[data-appearance="icon"] {
|
|
@@ -24839,9 +25108,8 @@ const InputCheckboxUI = props => {
|
|
|
24839
25108
|
});
|
|
24840
25109
|
const renderCheckboxMemoized = useCallback(renderCheckbox, [id, innerName, checked, innerRequired]);
|
|
24841
25110
|
const boxRef = useRef();
|
|
24842
|
-
|
|
24843
|
-
|
|
24844
|
-
luminanceThreshold: 0.82
|
|
25111
|
+
useAccentColorAttributes(boxRef, accentColor, {
|
|
25112
|
+
elementSelector: ".navi_checkbox_accent_probe"
|
|
24845
25113
|
});
|
|
24846
25114
|
return jsxs(Box, {
|
|
24847
25115
|
as: "span",
|
|
@@ -25164,12 +25432,12 @@ installImportMetaCssBuild(import.meta);const css$o = /* css */`
|
|
|
25164
25432
|
|
|
25165
25433
|
--color-mix-light: black;
|
|
25166
25434
|
--color-mix-dark: white;
|
|
25167
|
-
--color-mix: var(--color-mix-
|
|
25435
|
+
--color-mix: var(--color-mix-dark);
|
|
25168
25436
|
|
|
25169
25437
|
--outline-color: var(--navi-focus-outline-color);
|
|
25170
25438
|
--loader-color: var(--navi-loader-color);
|
|
25171
25439
|
--border-color: light-dark(#767676, #8e8e93);
|
|
25172
|
-
--
|
|
25440
|
+
--background-color: white;
|
|
25173
25441
|
--accent-color: light-dark(#4476ff, #3b82f6);
|
|
25174
25442
|
--radiomark-color: var(--accent-color);
|
|
25175
25443
|
--border-color-checked: var(--accent-color);
|
|
@@ -25287,7 +25555,6 @@ installImportMetaCssBuild(import.meta);const css$o = /* css */`
|
|
|
25287
25555
|
}
|
|
25288
25556
|
/* Checked */
|
|
25289
25557
|
&[data-checked] {
|
|
25290
|
-
--x-background-color: transparent;
|
|
25291
25558
|
--x-border-color: var(--border-color-checked);
|
|
25292
25559
|
|
|
25293
25560
|
&[data-hover] {
|
|
@@ -25324,11 +25591,14 @@ installImportMetaCssBuild(import.meta);const css$o = /* css */`
|
|
|
25324
25591
|
}
|
|
25325
25592
|
}
|
|
25326
25593
|
|
|
25327
|
-
&[data-
|
|
25328
|
-
--
|
|
25329
|
-
|
|
25594
|
+
&[data-accent-light] {
|
|
25595
|
+
--color-mix: var(--color-mix-light);
|
|
25596
|
+
}
|
|
25597
|
+
|
|
25598
|
+
&[data-accent-very-light] {
|
|
25599
|
+
--x-background-color: rgba(0, 0, 0, 0.15);
|
|
25330
25600
|
&[data-checked] {
|
|
25331
|
-
--x-background-color:
|
|
25601
|
+
--x-background-color: rgba(0, 0, 0, 0.15);
|
|
25332
25602
|
}
|
|
25333
25603
|
}
|
|
25334
25604
|
|
|
@@ -25599,9 +25869,8 @@ const InputRadioUI = props => {
|
|
|
25599
25869
|
});
|
|
25600
25870
|
const renderRadioMemoized = useCallback(renderRadio, [innerName, checked, innerRequired]);
|
|
25601
25871
|
const boxRef = useRef();
|
|
25602
|
-
|
|
25603
|
-
|
|
25604
|
-
luminanceThreshold: 0.82
|
|
25872
|
+
useAccentColorAttributes(boxRef, remainingProps.accentColor, {
|
|
25873
|
+
elementSelector: ".navi_radio_accent_probe"
|
|
25605
25874
|
});
|
|
25606
25875
|
return jsxs(Box, {
|
|
25607
25876
|
as: "span",
|
|
@@ -25730,6 +25999,9 @@ installImportMetaCssBuild(import.meta);const css$n = /* css */`
|
|
|
25730
25999
|
--outline-color: var(--navi-focus-outline-color);
|
|
25731
26000
|
--loader-color: var(--navi-loader-color);
|
|
25732
26001
|
--accent-color: rgb(24, 117, 255);
|
|
26002
|
+
--color-mix-light: black;
|
|
26003
|
+
--color-mix-dark: white;
|
|
26004
|
+
--color-mix: var(--color-mix-dark);
|
|
25733
26005
|
|
|
25734
26006
|
--border-color: rgb(150, 150, 150);
|
|
25735
26007
|
--track-border-color: color-mix(
|
|
@@ -25747,9 +26019,21 @@ installImportMetaCssBuild(import.meta);const css$n = /* css */`
|
|
|
25747
26019
|
var(--track-border-color) 75%,
|
|
25748
26020
|
black
|
|
25749
26021
|
);
|
|
25750
|
-
--track-color-hover: color-mix(
|
|
25751
|
-
|
|
25752
|
-
|
|
26022
|
+
--track-color-hover: color-mix(
|
|
26023
|
+
in srgb,
|
|
26024
|
+
var(--fill-color) 95%,
|
|
26025
|
+
var(--color-mix)
|
|
26026
|
+
);
|
|
26027
|
+
--fill-color-hover: color-mix(
|
|
26028
|
+
in srgb,
|
|
26029
|
+
var(--fill-color) 80%,
|
|
26030
|
+
var(--color-mix)
|
|
26031
|
+
);
|
|
26032
|
+
--thumb-color-hover: color-mix(
|
|
26033
|
+
in srgb,
|
|
26034
|
+
var(--thumb-color) 80%,
|
|
26035
|
+
var(--color-mix)
|
|
26036
|
+
);
|
|
25753
26037
|
/* Pressed */
|
|
25754
26038
|
--border-color-pressed: color-mix(
|
|
25755
26039
|
in srgb,
|
|
@@ -25826,6 +26110,15 @@ installImportMetaCssBuild(import.meta);const css$n = /* css */`
|
|
|
25826
26110
|
}
|
|
25827
26111
|
}
|
|
25828
26112
|
|
|
26113
|
+
.navi_input_range_accent_probe {
|
|
26114
|
+
position: absolute;
|
|
26115
|
+
width: 0;
|
|
26116
|
+
height: 0;
|
|
26117
|
+
background-color: var(--accent-color);
|
|
26118
|
+
visibility: hidden;
|
|
26119
|
+
pointer-events: none;
|
|
26120
|
+
}
|
|
26121
|
+
|
|
25829
26122
|
.navi_input_range_background {
|
|
25830
26123
|
position: absolute;
|
|
25831
26124
|
width: 100%;
|
|
@@ -25910,77 +26203,51 @@ installImportMetaCssBuild(import.meta);const css$n = /* css */`
|
|
|
25910
26203
|
--x-fill-color: var(--fill-color-disabled);
|
|
25911
26204
|
--x-thumb-color: var(--thumb-color-disabled);
|
|
25912
26205
|
--x-thumb-cursor: default;
|
|
26206
|
+
--x-accent-color: var(--accent-color-disabled);
|
|
26207
|
+
}
|
|
26208
|
+
/* Callout (info, warning, error) */
|
|
26209
|
+
&[data-callout] {
|
|
25913
26210
|
}
|
|
25914
|
-
}
|
|
25915
26211
|
|
|
25916
|
-
|
|
25917
|
-
|
|
25918
|
-
|
|
25919
|
-
|
|
25920
|
-
|
|
25921
|
-
|
|
25922
|
-
|
|
25923
|
-
/* What can we do? */
|
|
26212
|
+
&[data-accent-light] {
|
|
26213
|
+
--color-mix: var(--color-mix-light);
|
|
26214
|
+
}
|
|
26215
|
+
&[data-accent-very-light] {
|
|
26216
|
+
--background-color: rgba(0, 0, 0, 0.15);
|
|
26217
|
+
--track-border-color: rgba(0, 0, 0, 0.25);
|
|
26218
|
+
}
|
|
25924
26219
|
}
|
|
25925
26220
|
`;
|
|
25926
26221
|
const InputRange = props => {
|
|
25927
|
-
|
|
26222
|
+
const defaultRef = useRef();
|
|
26223
|
+
const ref = props.ref || defaultRef;
|
|
25928
26224
|
const uiStateController = useUIStateController(props, "input");
|
|
25929
26225
|
const uiState = useUIState(uiStateController);
|
|
25930
|
-
const input = renderActionableComponent(props, {
|
|
25931
|
-
Basic: InputRangeBasic,
|
|
25932
|
-
WithAction: InputRangeWithAction
|
|
25933
|
-
});
|
|
25934
26226
|
return jsx(UIStateControllerContext.Provider, {
|
|
25935
26227
|
value: uiStateController,
|
|
25936
26228
|
children: jsx(UIStateContext.Provider, {
|
|
25937
26229
|
value: uiState,
|
|
25938
|
-
children:
|
|
26230
|
+
children: jsx(InputRangeDispatcher, {
|
|
26231
|
+
...props,
|
|
26232
|
+
ref: ref
|
|
26233
|
+
})
|
|
25939
26234
|
})
|
|
25940
26235
|
});
|
|
25941
26236
|
};
|
|
25942
|
-
const
|
|
25943
|
-
|
|
25944
|
-
|
|
25945
|
-
|
|
25946
|
-
|
|
25947
|
-
"accentColor": "--accent-color",
|
|
25948
|
-
":hover": {
|
|
25949
|
-
borderColor: "--border-color-hover",
|
|
25950
|
-
backgroundColor: "--background-color-hover",
|
|
25951
|
-
fillColor: "--fill-color-hover",
|
|
25952
|
-
thumbColor: "--thumb-color-hover"
|
|
25953
|
-
},
|
|
25954
|
-
":-navi-pressed": {
|
|
25955
|
-
borderColor: "--border-color-hover",
|
|
25956
|
-
backgroundColor: "--background-color-hover",
|
|
25957
|
-
fillColor: "--fill-color-pressed",
|
|
25958
|
-
thumbColor: "--thumb-color-pressed"
|
|
25959
|
-
},
|
|
25960
|
-
":read-only": {
|
|
25961
|
-
borderColor: "--border-color-readonly",
|
|
25962
|
-
backgroundColor: "--background-color-readonly",
|
|
25963
|
-
fillColor: "--fill-color-readonly",
|
|
25964
|
-
thumbColor: "--thumb-color-readonly"
|
|
25965
|
-
},
|
|
25966
|
-
":disabled": {
|
|
25967
|
-
borderColor: "--border-color-disabled",
|
|
25968
|
-
backgroundColor: "--background-color-disabled",
|
|
25969
|
-
fillColor: "--fill-color-disabled",
|
|
25970
|
-
thumbColor: "--thumb-color-disabled"
|
|
26237
|
+
const InputRangeDispatcher = props => {
|
|
26238
|
+
if (props.action) {
|
|
26239
|
+
return jsx(InputRangeWithAction, {
|
|
26240
|
+
...props
|
|
26241
|
+
});
|
|
25971
26242
|
}
|
|
26243
|
+
return jsx(InputRangeUI, {
|
|
26244
|
+
...props
|
|
26245
|
+
});
|
|
25972
26246
|
};
|
|
25973
|
-
const
|
|
25974
|
-
|
|
25975
|
-
const RangeChildPropSet = new Set([...fieldPropSet]);
|
|
25976
|
-
const InputRangeBasic = props => {
|
|
25977
|
-
const contextReadOnly = useContext(ReadOnlyContext);
|
|
25978
|
-
const contextDisabled = useContext(DisabledContext);
|
|
25979
|
-
const contextLoading = useContext(LoadingContext);
|
|
25980
|
-
const contextLoadingElement = useContext(LoadingElementContext);
|
|
25981
|
-
const uiStateController = useContext(UIStateControllerContext);
|
|
25982
|
-
const uiState = useContext(UIStateContext);
|
|
26247
|
+
const InputRangeUI = props => {
|
|
26248
|
+
import.meta.css = [css$n, "@jsenv/navi/src/field/input_range.jsx"];
|
|
25983
26249
|
const {
|
|
26250
|
+
ref,
|
|
25984
26251
|
onInput,
|
|
25985
26252
|
readOnly,
|
|
25986
26253
|
disabled,
|
|
@@ -25990,8 +26257,12 @@ const InputRangeBasic = props => {
|
|
|
25990
26257
|
autoSelect,
|
|
25991
26258
|
...rest
|
|
25992
26259
|
} = props;
|
|
25993
|
-
const
|
|
25994
|
-
const
|
|
26260
|
+
const contextReadOnly = useContext(ReadOnlyContext);
|
|
26261
|
+
const contextDisabled = useContext(DisabledContext);
|
|
26262
|
+
const contextLoading = useContext(LoadingContext);
|
|
26263
|
+
const contextLoadingElement = useContext(LoadingElementContext);
|
|
26264
|
+
const uiStateController = useContext(UIStateControllerContext);
|
|
26265
|
+
const uiState = useContext(UIStateContext);
|
|
25995
26266
|
const innerValue = uiState;
|
|
25996
26267
|
const innerLoading = loading || contextLoading && contextLoadingElement === ref.current;
|
|
25997
26268
|
const innerReadOnly = readOnly || contextReadOnly || innerLoading || uiStateController.readOnly;
|
|
@@ -26005,6 +26276,13 @@ const InputRangeBasic = props => {
|
|
|
26005
26276
|
autoSelect
|
|
26006
26277
|
});
|
|
26007
26278
|
const remainingProps = useConstraints(ref, rest);
|
|
26279
|
+
const {
|
|
26280
|
+
accentColor
|
|
26281
|
+
} = remainingProps;
|
|
26282
|
+
const boxRef = useRef();
|
|
26283
|
+
useAccentColorAttributes(boxRef, accentColor, {
|
|
26284
|
+
elementSelector: ".navi_input_range_accent_probe"
|
|
26285
|
+
});
|
|
26008
26286
|
const innerOnInput = useStableCallback(onInput);
|
|
26009
26287
|
const focusProxyId = `input_range_focus_proxy_${useId()}`;
|
|
26010
26288
|
const inertButFocusable = innerReadOnly && !innerDisabled;
|
|
@@ -26097,11 +26375,14 @@ const InputRangeBasic = props => {
|
|
|
26097
26375
|
hasChildFunction: true,
|
|
26098
26376
|
baseChildPropSet: RangeChildPropSet,
|
|
26099
26377
|
...remainingProps,
|
|
26100
|
-
ref:
|
|
26378
|
+
ref: boxRef,
|
|
26101
26379
|
autoFocus: undefined // See use_auto_focus.js
|
|
26102
26380
|
,
|
|
26103
26381
|
|
|
26104
|
-
children: [jsx(
|
|
26382
|
+
children: [jsx("span", {
|
|
26383
|
+
className: "navi_input_range_accent_probe",
|
|
26384
|
+
"aria-hidden": "true"
|
|
26385
|
+
}), jsx(LoaderBackground, {
|
|
26105
26386
|
loading: innerLoading,
|
|
26106
26387
|
color: "var(--loader-color)",
|
|
26107
26388
|
inset: -1
|
|
@@ -26120,9 +26401,43 @@ const InputRangeBasic = props => {
|
|
|
26120
26401
|
}), renderInputMemoized]
|
|
26121
26402
|
});
|
|
26122
26403
|
};
|
|
26404
|
+
const RangeStyleCSSVars = {
|
|
26405
|
+
"outlineWidth": "--outline-width",
|
|
26406
|
+
"borderRadius": "--border-radius",
|
|
26407
|
+
"borderColor": "--border-color",
|
|
26408
|
+
"backgroundColor": "--background-color",
|
|
26409
|
+
"accentColor": "--accent-color",
|
|
26410
|
+
":hover": {
|
|
26411
|
+
borderColor: "--border-color-hover",
|
|
26412
|
+
backgroundColor: "--background-color-hover",
|
|
26413
|
+
fillColor: "--fill-color-hover",
|
|
26414
|
+
thumbColor: "--thumb-color-hover"
|
|
26415
|
+
},
|
|
26416
|
+
":-navi-pressed": {
|
|
26417
|
+
borderColor: "--border-color-hover",
|
|
26418
|
+
backgroundColor: "--background-color-hover",
|
|
26419
|
+
fillColor: "--fill-color-pressed",
|
|
26420
|
+
thumbColor: "--thumb-color-pressed"
|
|
26421
|
+
},
|
|
26422
|
+
":read-only": {
|
|
26423
|
+
borderColor: "--border-color-readonly",
|
|
26424
|
+
backgroundColor: "--background-color-readonly",
|
|
26425
|
+
fillColor: "--fill-color-readonly",
|
|
26426
|
+
thumbColor: "--thumb-color-readonly"
|
|
26427
|
+
},
|
|
26428
|
+
":disabled": {
|
|
26429
|
+
borderColor: "--border-color-disabled",
|
|
26430
|
+
backgroundColor: "--background-color-disabled",
|
|
26431
|
+
fillColor: "--fill-color-disabled",
|
|
26432
|
+
thumbColor: "--thumb-color-disabled"
|
|
26433
|
+
}
|
|
26434
|
+
};
|
|
26435
|
+
const RangePseudoClasses = [":hover", ":active", ":-navi-pressed", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading"];
|
|
26436
|
+
const RangePseudoElements = ["::-navi-loader"];
|
|
26437
|
+
const RangeChildPropSet = new Set([...fieldPropSet]);
|
|
26123
26438
|
const InputRangeWithAction = props => {
|
|
26124
|
-
const uiState = useContext(UIStateContext);
|
|
26125
26439
|
const {
|
|
26440
|
+
ref,
|
|
26126
26441
|
action,
|
|
26127
26442
|
actionDebounce,
|
|
26128
26443
|
actionAfterChange,
|
|
@@ -26137,8 +26452,7 @@ const InputRangeWithAction = props => {
|
|
|
26137
26452
|
actionErrorEffect,
|
|
26138
26453
|
...rest
|
|
26139
26454
|
} = props;
|
|
26140
|
-
const
|
|
26141
|
-
const ref = props.ref || defaultRef;
|
|
26455
|
+
const uiState = useContext(UIStateContext);
|
|
26142
26456
|
const [boundAction] = useActionBoundToOneParam(action, uiState);
|
|
26143
26457
|
const {
|
|
26144
26458
|
loading: actionLoading
|
|
@@ -26178,12 +26492,13 @@ const InputRangeWithAction = props => {
|
|
|
26178
26492
|
onError: onActionError,
|
|
26179
26493
|
onEnd: onActionEnd
|
|
26180
26494
|
});
|
|
26181
|
-
return jsx(
|
|
26495
|
+
return jsx(InputRangeDispatcher, {
|
|
26182
26496
|
"data-action": boundAction.name,
|
|
26183
26497
|
"data-action-debounce": actionDebounce,
|
|
26184
26498
|
"data-action-after-change": actionAfterChange ? "" : undefined,
|
|
26185
26499
|
...rest,
|
|
26186
26500
|
ref: ref,
|
|
26501
|
+
action: undefined,
|
|
26187
26502
|
loading: loading || actionLoading
|
|
26188
26503
|
});
|
|
26189
26504
|
};
|
|
@@ -26207,64 +26522,54 @@ const SearchSvg = () => jsx("svg", {
|
|
|
26207
26522
|
})
|
|
26208
26523
|
});
|
|
26209
26524
|
|
|
26210
|
-
|
|
26211
|
-
|
|
26212
|
-
|
|
26213
|
-
|
|
26214
|
-
|
|
26215
|
-
|
|
26216
|
-
|
|
26217
|
-
|
|
26218
|
-
|
|
26219
|
-
|
|
26220
|
-
* ancestor can observe them. These are part of the public API and should be documented.
|
|
26221
|
-
*
|
|
26222
|
-
* 3. **Request events** (`dispatchCustomEvent`) — code *outside* a component asks it
|
|
26223
|
-
* to perform an action (e.g. `navi_list_request_open`). They are cancelable so the
|
|
26224
|
-
* component can signal whether it handled the request. Names are prefixed
|
|
26225
|
-
* with `request_` by convention.
|
|
26226
|
-
*/
|
|
26227
|
-
|
|
26525
|
+
installImportMetaCssBuild(import.meta);const css$m = /* css */`
|
|
26526
|
+
@layer navi {
|
|
26527
|
+
.navi_separator {
|
|
26528
|
+
--size: 1px;
|
|
26529
|
+
--color: #e4e4e7;
|
|
26530
|
+
--spacing: 0.5em;
|
|
26531
|
+
--spacing-start: 0.5em;
|
|
26532
|
+
--spacing-end: 0.5em;
|
|
26533
|
+
}
|
|
26534
|
+
}
|
|
26228
26535
|
|
|
26229
|
-
|
|
26230
|
-
|
|
26231
|
-
|
|
26232
|
-
|
|
26233
|
-
|
|
26234
|
-
|
|
26235
|
-
|
|
26236
|
-
|
|
26237
|
-
) => {
|
|
26238
|
-
const customEvent = new CustomEvent(customEventName, {
|
|
26239
|
-
detail: resolveEventDetail(customEventDetail),
|
|
26240
|
-
bubbles: true,
|
|
26241
|
-
});
|
|
26242
|
-
return el.dispatchEvent(customEvent);
|
|
26243
|
-
};
|
|
26536
|
+
.navi_separator {
|
|
26537
|
+
width: 100%;
|
|
26538
|
+
height: var(--size);
|
|
26539
|
+
margin-top: var(--spacing-start, var(--spacing));
|
|
26540
|
+
margin-bottom: var(--spacing-end, var(--spacing));
|
|
26541
|
+
flex-shrink: 0;
|
|
26542
|
+
background: var(--color);
|
|
26543
|
+
border: none;
|
|
26244
26544
|
|
|
26245
|
-
|
|
26246
|
-
|
|
26247
|
-
* Cancelable — returns `false` if the component called `preventDefault()`,
|
|
26248
|
-
* indicating it did not (or could not) handle the request.
|
|
26249
|
-
* Names are conventionally prefixed with `request_` (e.g. `navi_list_request_open`).
|
|
26250
|
-
*/
|
|
26251
|
-
const dispatchCustomEvent = (el, customEventName, customEventDetail) => {
|
|
26252
|
-
const customEvent = new CustomEvent(customEventName, {
|
|
26253
|
-
detail: resolveEventDetail(customEventDetail),
|
|
26254
|
-
cancelable: true,
|
|
26255
|
-
});
|
|
26256
|
-
return el.dispatchEvent(customEvent);
|
|
26257
|
-
};
|
|
26545
|
+
&[data-vertical] {
|
|
26546
|
+
display: inline-block;
|
|
26258
26547
|
|
|
26259
|
-
|
|
26260
|
-
|
|
26261
|
-
|
|
26262
|
-
|
|
26263
|
-
|
|
26264
|
-
|
|
26265
|
-
|
|
26548
|
+
width: var(--size);
|
|
26549
|
+
height: 1lh;
|
|
26550
|
+
margin-top: 0;
|
|
26551
|
+
margin-right: var(--spacing-end, var(--spacing));
|
|
26552
|
+
margin-bottom: 0;
|
|
26553
|
+
margin-left: var(--spacing-start, var(--spacing));
|
|
26554
|
+
vertical-align: bottom;
|
|
26555
|
+
}
|
|
26266
26556
|
}
|
|
26267
|
-
|
|
26557
|
+
`;
|
|
26558
|
+
const SeparatorStyleCSSVars = {
|
|
26559
|
+
color: "--color"
|
|
26560
|
+
};
|
|
26561
|
+
const Separator = ({
|
|
26562
|
+
vertical,
|
|
26563
|
+
...props
|
|
26564
|
+
}) => {
|
|
26565
|
+
import.meta.css = [css$m, "@jsenv/navi/src/layout/separator.jsx"];
|
|
26566
|
+
return jsx(Box, {
|
|
26567
|
+
as: vertical ? "span" : "hr",
|
|
26568
|
+
...props,
|
|
26569
|
+
"data-vertical": vertical ? "" : undefined,
|
|
26570
|
+
baseClassName: "navi_separator",
|
|
26571
|
+
styleCSSVars: SeparatorStyleCSSVars
|
|
26572
|
+
});
|
|
26268
26573
|
};
|
|
26269
26574
|
|
|
26270
26575
|
/*
|
|
@@ -26669,9 +26974,10 @@ const RenderWindowContext = createContext(null);
|
|
|
26669
26974
|
// Carries the separator element/function down to each ListItem so separators
|
|
26670
26975
|
// are only rendered between items that actually mount (post-filter, post-window).
|
|
26671
26976
|
const SeparatorContext = createContext(null);
|
|
26672
|
-
const css$
|
|
26977
|
+
const css$l = /* css */`
|
|
26673
26978
|
@layer navi {
|
|
26674
26979
|
.navi_list_container {
|
|
26980
|
+
--list-outline-width: 1px;
|
|
26675
26981
|
--list-border-radius: 4px;
|
|
26676
26982
|
--list-border-width: 1px;
|
|
26677
26983
|
--list-border-color: light-dark(#ccc, #555);
|
|
@@ -26688,23 +26994,21 @@ const css$m = /* css */`
|
|
|
26688
26994
|
--list-item-color-hover: var(--list-item-color);
|
|
26689
26995
|
--list-item-background-color-hover: light-dark(#f5f5f5, #2a2a2a);
|
|
26690
26996
|
|
|
26691
|
-
/* Pointed
|
|
26692
|
-
--list-item-color-pointed: var(--list-item-color);
|
|
26693
|
-
--list-item-background-color-pointed: light-dark(#
|
|
26694
|
-
|
|
26695
|
-
/* Selected */
|
|
26696
|
-
--list-item-color-selected: light-dark(#1a73e8, #7baaf7);
|
|
26697
|
-
--list-item-background-color-selected: light-dark(#e8f0fe, #1c3a6e);
|
|
26698
|
-
--list-item-font-weight-selected: 500;
|
|
26997
|
+
/* Pointed by mouse — subtle, just a shade above background */
|
|
26998
|
+
--list-item-color-mouse-pointed: var(--list-item-color);
|
|
26999
|
+
--list-item-background-color-mouse-pointed: light-dark(#ebebeb, #303030);
|
|
26699
27000
|
|
|
26700
|
-
/* Pointed
|
|
26701
|
-
--list-item-color-pointed
|
|
26702
|
-
--list-item-background-color-pointed
|
|
26703
|
-
|
|
26704
|
-
|
|
26705
|
-
black
|
|
27001
|
+
/* Pointed by keyboard — subtle light blue highlight */
|
|
27002
|
+
--list-item-color-keyboard-pointed: var(--list-item-color);
|
|
27003
|
+
--list-item-background-color-keyboard-pointed: light-dark(
|
|
27004
|
+
#c2dcff,
|
|
27005
|
+
#1c3a6e
|
|
26706
27006
|
);
|
|
26707
27007
|
|
|
27008
|
+
/* Selected — vivid blue accent */
|
|
27009
|
+
--list-item-color-selected: light-dark(#ffffff, #ffffff);
|
|
27010
|
+
--list-item-background-color-selected: light-dark(#1a73e8, #2b5fcc);
|
|
27011
|
+
|
|
26708
27012
|
/* Disabled */
|
|
26709
27013
|
--list-item-color-disabled: light-dark(#aaa, #555);
|
|
26710
27014
|
--list-item-background-color-disabled: var(--list-item-background-color);
|
|
@@ -26728,11 +27032,11 @@ const css$m = /* css */`
|
|
|
26728
27032
|
}
|
|
26729
27033
|
|
|
26730
27034
|
.navi_list_container {
|
|
26731
|
-
--x-border-radius: var(--list-border-radius);
|
|
26732
|
-
--x-border-width: var(--list-border-width);
|
|
26733
|
-
--x-border-color: var(--list-border-color);
|
|
26734
|
-
--x-border-style: var(--list-border-style);
|
|
26735
|
-
--x-background-color: var(--list-background-color);
|
|
27035
|
+
--x-list-border-radius: var(--list-border-radius);
|
|
27036
|
+
--x-list-border-width: var(--list-border-width);
|
|
27037
|
+
--x-list-border-color: var(--list-border-color);
|
|
27038
|
+
--x-list-border-style: var(--list-border-style);
|
|
27039
|
+
--x-list-background-color: var(--list-background-color);
|
|
26736
27040
|
/* When typing inside an input browser tries to keep caret visible */
|
|
26737
27041
|
/* For input within a sticky element inside a scrollable container */
|
|
26738
27042
|
/* Browser will try to scroll that input into view */
|
|
@@ -26747,15 +27051,33 @@ const css$m = /* css */`
|
|
|
26747
27051
|
var(--list-footer-height, 0px) + var(--list-scroll-padding-bottom, 0px)
|
|
26748
27052
|
);
|
|
26749
27053
|
|
|
27054
|
+
display: flex;
|
|
26750
27055
|
width: fit-content;
|
|
26751
27056
|
max-width: 100%;
|
|
26752
|
-
|
|
26753
|
-
background-color: var(--x-background-color);
|
|
26754
|
-
|
|
26755
|
-
|
|
27057
|
+
flex-direction: column;
|
|
27058
|
+
background-color: var(--x-list-background-color);
|
|
27059
|
+
/* Use a transparent real border to reserve layout space, and draw the
|
|
27060
|
+
visible border via outline (inset via negative offset). This way the
|
|
27061
|
+
focus ring can simply widen the outline without shifting layout. */
|
|
27062
|
+
border: var(--x-list-border-width) solid transparent;
|
|
27063
|
+
border-radius: var(--x-list-border-radius);
|
|
27064
|
+
outline: var(--x-list-border-width) var(--x-list-border-style)
|
|
27065
|
+
var(--x-list-border-color);
|
|
27066
|
+
outline-offset: calc(-1 * var(--x-list-border-width));
|
|
26756
27067
|
transition: opacity 0.2s ease;
|
|
26757
|
-
overflow:
|
|
26758
|
-
|
|
27068
|
+
/* overflow:hidden is required on the container (not the inner scroll element)
|
|
27069
|
+
so that border-radius clips the content correctly. Without it, items near
|
|
27070
|
+
the corners would visually overflow the rounded corners during scroll. */
|
|
27071
|
+
overflow: hidden;
|
|
27072
|
+
|
|
27073
|
+
.navi_list_scroll_container {
|
|
27074
|
+
width: inherit;
|
|
27075
|
+
min-width: inherit;
|
|
27076
|
+
max-width: inherit;
|
|
27077
|
+
max-height: var(--list-max-height);
|
|
27078
|
+
overflow: auto;
|
|
27079
|
+
overscroll-behavior: inherit; /* inherit select behavior */
|
|
27080
|
+
}
|
|
26759
27081
|
|
|
26760
27082
|
&[data-expand-x] {
|
|
26761
27083
|
width: 100%;
|
|
@@ -26763,18 +27085,42 @@ const css$m = /* css */`
|
|
|
26763
27085
|
&[popover] {
|
|
26764
27086
|
position: absolute;
|
|
26765
27087
|
inset: unset;
|
|
27088
|
+
display: none;
|
|
26766
27089
|
min-width: var(--list-anchor-width, 0px);
|
|
26767
27090
|
max-width: 95vw;
|
|
26768
27091
|
margin: 0;
|
|
26769
27092
|
padding: 0;
|
|
27093
|
+
|
|
27094
|
+
&:popover-open {
|
|
27095
|
+
display: flex;
|
|
27096
|
+
}
|
|
27097
|
+
.navi_list {
|
|
27098
|
+
width: 100%;
|
|
27099
|
+
}
|
|
27100
|
+
|
|
27101
|
+
&[data-anchor-hidden] {
|
|
27102
|
+
opacity: 0;
|
|
27103
|
+
pointer-events: none;
|
|
27104
|
+
}
|
|
26770
27105
|
}
|
|
26771
|
-
|
|
26772
|
-
|
|
26773
|
-
|
|
27106
|
+
|
|
27107
|
+
&[data-focus] {
|
|
27108
|
+
/* outline: var(--list-outline-width) solid var(--navi-focus-outline-color);
|
|
27109
|
+
outline-offset: calc(-1 * var(--list-outline-width)); */
|
|
26774
27110
|
}
|
|
27111
|
+
&[data-focus-visible] {
|
|
27112
|
+
outline-width: calc(var(--list-border-width) + var(--list-outline-width));
|
|
27113
|
+
outline-color: var(--navi-focus-outline-color);
|
|
27114
|
+
outline-offset: calc(
|
|
27115
|
+
-1 * (var(--list-border-width) + var(--list-outline-width))
|
|
27116
|
+
);
|
|
27117
|
+
.navi_list {
|
|
27118
|
+
outline: none;
|
|
27119
|
+
}
|
|
27120
|
+
}
|
|
27121
|
+
|
|
26775
27122
|
&[data-callout] {
|
|
26776
|
-
--x-border-color: var(--callout-color);
|
|
26777
|
-
--x-outline-color: var(--callout-color);
|
|
27123
|
+
--x-list-border-color: var(--callout-color);
|
|
26778
27124
|
}
|
|
26779
27125
|
}
|
|
26780
27126
|
|
|
@@ -26797,15 +27143,15 @@ const css$m = /* css */`
|
|
|
26797
27143
|
}
|
|
26798
27144
|
|
|
26799
27145
|
.navi_list_item {
|
|
26800
|
-
--x-color: var(--list-item-color);
|
|
26801
|
-
--x-background-color: var(--list-item-background-color);
|
|
26802
|
-
--x-font-weight: var(--list-item-font-weight);
|
|
27146
|
+
--x-list-item-color: var(--list-item-color);
|
|
27147
|
+
--x-list-item-background-color: var(--list-item-background-color);
|
|
27148
|
+
--x-list-item-font-weight: var(--list-item-font-weight);
|
|
26803
27149
|
box-sizing: border-box;
|
|
26804
27150
|
min-width: 100%;
|
|
26805
27151
|
padding: var(--list-item-padding);
|
|
26806
|
-
color: var(--x-color);
|
|
26807
|
-
font-weight: var(--x-font-weight);
|
|
26808
|
-
background-color: var(--x-background-color);
|
|
27152
|
+
color: var(--x-list-item-color);
|
|
27153
|
+
font-weight: var(--x-list-item-font-weight);
|
|
27154
|
+
background-color: var(--x-list-item-background-color);
|
|
26809
27155
|
/*
|
|
26810
27156
|
CSS impossible d'obtenir un layout qui ferait en gros:
|
|
26811
27157
|
width = max(min(max-content, 100%), unbreakable-content)
|
|
@@ -26829,27 +27175,31 @@ const css$m = /* css */`
|
|
|
26829
27175
|
&[data-interactive] {
|
|
26830
27176
|
cursor: pointer;
|
|
26831
27177
|
user-select: none;
|
|
26832
|
-
/* &:hover {
|
|
26833
|
-
--x-color: var(--list-item-color-hover);
|
|
26834
|
-
--x-background-color: var(--list-item-background-color-hover);
|
|
26835
|
-
} */
|
|
26836
27178
|
}
|
|
26837
27179
|
&[data-pointed] {
|
|
26838
|
-
--x-color: var(--list-item-color-pointed);
|
|
26839
|
-
--x-background-color: var(
|
|
27180
|
+
--x-list-item-color: var(--list-item-color-mouse-pointed);
|
|
27181
|
+
--x-list-item-background-color: var(
|
|
27182
|
+
--list-item-background-color-mouse-pointed
|
|
27183
|
+
);
|
|
26840
27184
|
}
|
|
26841
27185
|
&[data-selected] {
|
|
26842
|
-
--x-color: var(--list-item-color-selected);
|
|
26843
|
-
--x-background-color: var(
|
|
26844
|
-
|
|
26845
|
-
|
|
26846
|
-
|
|
26847
|
-
|
|
26848
|
-
|
|
27186
|
+
--x-list-item-color: var(--list-item-color-selected);
|
|
27187
|
+
--x-list-item-background-color: var(
|
|
27188
|
+
--list-item-background-color-selected
|
|
27189
|
+
);
|
|
27190
|
+
&[data-pointed] {
|
|
27191
|
+
/* Here important should no beed need, but for some reason it is */
|
|
27192
|
+
--x-list-item-background-color: var(
|
|
27193
|
+
--list-item-background-color-selected,
|
|
27194
|
+
var(--list-item-background-color-mouse-pointed)
|
|
27195
|
+
) !important;
|
|
27196
|
+
}
|
|
26849
27197
|
}
|
|
26850
27198
|
&[data-disabled] {
|
|
26851
|
-
--x-color: var(--list-item-color-disabled);
|
|
26852
|
-
--x-background-color: var(
|
|
27199
|
+
--x-list-item-color: var(--list-item-color-disabled);
|
|
27200
|
+
--x-list-item-background-color: var(
|
|
27201
|
+
--list-item-background-color-disabled
|
|
27202
|
+
);
|
|
26853
27203
|
cursor: not-allowed;
|
|
26854
27204
|
pointer-events: none;
|
|
26855
27205
|
}
|
|
@@ -26858,10 +27208,41 @@ const css$m = /* css */`
|
|
|
26858
27208
|
}
|
|
26859
27209
|
}
|
|
26860
27210
|
|
|
27211
|
+
.navi_list_container {
|
|
27212
|
+
&[data-focus-within] {
|
|
27213
|
+
.navi_list_item {
|
|
27214
|
+
&[data-pointed-by-keyboard] {
|
|
27215
|
+
--x-list-item-color: var(--list-item-color-keyboard-pointed);
|
|
27216
|
+
--x-list-item-background-color: var(
|
|
27217
|
+
--list-item-background-color-keyboard-pointed
|
|
27218
|
+
);
|
|
27219
|
+
}
|
|
27220
|
+
|
|
27221
|
+
/* Selected must win over pointed-by-keyboard */
|
|
27222
|
+
&[data-selected] {
|
|
27223
|
+
--x-list-item-color: var(--list-item-color-selected);
|
|
27224
|
+
--x-list-item-background-color: var(
|
|
27225
|
+
--list-item-background-color-selected
|
|
27226
|
+
);
|
|
27227
|
+
/* Selected + pointed by keyboard: use keyboard color as fallback
|
|
27228
|
+
so that if --list-item-background-color-selected is reset the
|
|
27229
|
+
keyboard-pointed highlight still shows. */
|
|
27230
|
+
&[data-pointed-by-keyboard] {
|
|
27231
|
+
--x-list-item-background-color: var(
|
|
27232
|
+
--list-item-background-color-selected,
|
|
27233
|
+
var(--list-item-background-color-keyboard-pointed)
|
|
27234
|
+
);
|
|
27235
|
+
}
|
|
27236
|
+
}
|
|
27237
|
+
}
|
|
27238
|
+
}
|
|
27239
|
+
}
|
|
27240
|
+
|
|
26861
27241
|
/* Virtual scroll fillers — must remain invisible.
|
|
26862
27242
|
The browser may briefly flash them during scroll before the render window
|
|
26863
27243
|
updates, so giving them a visible background would cause visual glitches. */
|
|
26864
27244
|
.navi_list_virtual_filler {
|
|
27245
|
+
display: inline-block;
|
|
26865
27246
|
height: 0px;
|
|
26866
27247
|
list-style: none;
|
|
26867
27248
|
/* background: pink; */
|
|
@@ -27012,16 +27393,11 @@ const List = props => {
|
|
|
27012
27393
|
const ListDispatcher = props => {
|
|
27013
27394
|
const alreadyInteractive = useContext(ListInteractiveContext);
|
|
27014
27395
|
const parentUIStateController = useContext(ParentUIStateControllerContext);
|
|
27015
|
-
if (!alreadyInteractive && props.action) {
|
|
27396
|
+
if (!alreadyInteractive && (props.action || props.uiAction || parentUIStateController)) {
|
|
27016
27397
|
return jsx(ListWithAction, {
|
|
27017
27398
|
...props
|
|
27018
27399
|
});
|
|
27019
27400
|
}
|
|
27020
|
-
if (!alreadyInteractive && (props.uiAction || parentUIStateController)) {
|
|
27021
|
-
return jsx(ListInteractive, {
|
|
27022
|
-
...props
|
|
27023
|
-
});
|
|
27024
|
-
}
|
|
27025
27401
|
if (props.popover === true) {
|
|
27026
27402
|
return jsx(ListWithPopover, {
|
|
27027
27403
|
...props
|
|
@@ -27037,18 +27413,19 @@ const ListDispatcher = props => {
|
|
|
27037
27413
|
});
|
|
27038
27414
|
};
|
|
27039
27415
|
const ListUI = props => {
|
|
27040
|
-
import.meta.css = [css$
|
|
27416
|
+
import.meta.css = [css$l, "@jsenv/navi/src/field/list/list.jsx"];
|
|
27041
27417
|
const {
|
|
27042
27418
|
ref,
|
|
27043
27419
|
renderBudget = RENDER_BUDGET_DEFAULT,
|
|
27044
|
-
|
|
27045
|
-
|
|
27420
|
+
id,
|
|
27421
|
+
role,
|
|
27046
27422
|
fallback,
|
|
27047
27423
|
noMatchFallback,
|
|
27048
27424
|
separator,
|
|
27049
27425
|
children,
|
|
27050
27426
|
popover,
|
|
27051
27427
|
expandX,
|
|
27428
|
+
expand,
|
|
27052
27429
|
maxHeight,
|
|
27053
27430
|
onListVisibleItemsChange,
|
|
27054
27431
|
virtualItemHeight,
|
|
@@ -27059,13 +27436,17 @@ const ListUI = props => {
|
|
|
27059
27436
|
required,
|
|
27060
27437
|
...rest
|
|
27061
27438
|
} = props;
|
|
27439
|
+
if (renderBudget < 30) {
|
|
27440
|
+
console.warn(`List: renderBudget=${renderBudget} is too low. A renderBudget below 30 is not supported: on large screens or when the list grows, items outside the window would appear as blank space instead of rendered content. Use a value of at least 30, or omit the prop to use the default (${RENDER_BUDGET_DEFAULT}).`);
|
|
27441
|
+
}
|
|
27062
27442
|
const hiddenInputId = useId();
|
|
27063
27443
|
|
|
27064
27444
|
// lockSize: capture the container's dimensions on first render so filtering
|
|
27065
27445
|
// cannot collapse the layout. Measurement happens on the initial (unfiltered)
|
|
27066
27446
|
// state because the parent controls hidden props before any search is applied.
|
|
27447
|
+
const containerRef = useRef(null);
|
|
27067
27448
|
const sizeLocked = useRef(false);
|
|
27068
|
-
useDisplayedLayoutEffect(
|
|
27449
|
+
useDisplayedLayoutEffect(containerRef, listContainerEl => {
|
|
27069
27450
|
if (!lockSize) {
|
|
27070
27451
|
return undefined;
|
|
27071
27452
|
}
|
|
@@ -27096,63 +27477,68 @@ const ListUI = props => {
|
|
|
27096
27477
|
onListVisibleItemsChange?.(tracker.visibleItemsSignal.peek());
|
|
27097
27478
|
}
|
|
27098
27479
|
});
|
|
27099
|
-
const ulRef = useRef(null);
|
|
27100
27480
|
const {
|
|
27101
27481
|
virtualItemHeightSignal,
|
|
27102
27482
|
renderWindow,
|
|
27103
27483
|
scrollToItem,
|
|
27104
27484
|
pendingScrollRef
|
|
27105
27485
|
} = useListScrollSync({
|
|
27486
|
+
containerRef,
|
|
27106
27487
|
ref,
|
|
27107
|
-
ulRef,
|
|
27108
27488
|
tracker,
|
|
27109
27489
|
renderBudget,
|
|
27110
27490
|
virtualItemHeight,
|
|
27111
27491
|
searchText
|
|
27112
27492
|
});
|
|
27493
|
+
const idDefault = useId();
|
|
27494
|
+
const innerId = id || idDefault;
|
|
27113
27495
|
const renderList = listProps => {
|
|
27114
|
-
|
|
27115
|
-
|
|
27116
|
-
|
|
27117
|
-
|
|
27118
|
-
|
|
27119
|
-
|
|
27120
|
-
|
|
27121
|
-
|
|
27122
|
-
|
|
27123
|
-
|
|
27124
|
-
|
|
27125
|
-
|
|
27126
|
-
|
|
27127
|
-
|
|
27128
|
-
|
|
27129
|
-
|
|
27130
|
-
|
|
27131
|
-
children: jsx(
|
|
27132
|
-
value:
|
|
27133
|
-
children:
|
|
27496
|
+
return jsx("div", {
|
|
27497
|
+
className: "navi_list_scroll_container",
|
|
27498
|
+
children: jsx(UnorderedList, {
|
|
27499
|
+
ref: ref,
|
|
27500
|
+
id: innerId,
|
|
27501
|
+
role: role,
|
|
27502
|
+
fallback: fallback,
|
|
27503
|
+
noMatchFallback: noMatchFallback,
|
|
27504
|
+
searchText: searchText,
|
|
27505
|
+
separator: separator === true ? jsx(Separator, {
|
|
27506
|
+
margin: "0"
|
|
27507
|
+
}) : separator,
|
|
27508
|
+
expandX: expandX || expand,
|
|
27509
|
+
...listProps,
|
|
27510
|
+
tracker: tracker,
|
|
27511
|
+
renderWindow: renderWindow,
|
|
27512
|
+
virtualItemHeightSignal: virtualItemHeightSignal,
|
|
27513
|
+
children: jsx(PendingScrollRefContext.Provider, {
|
|
27514
|
+
value: pendingScrollRef,
|
|
27515
|
+
children: jsx(ListIdContext.Provider, {
|
|
27516
|
+
value: innerId,
|
|
27517
|
+
children: children
|
|
27518
|
+
})
|
|
27134
27519
|
})
|
|
27135
27520
|
})
|
|
27136
27521
|
});
|
|
27137
27522
|
};
|
|
27138
|
-
const renderListMemoized = useCallback(renderList, [
|
|
27523
|
+
const renderListMemoized = useCallback(renderList, [innerId, role, fallback, noMatchFallback, searchText, separator, expandX, expand, renderWindow, children]);
|
|
27139
27524
|
const inputRef = useRef(null);
|
|
27140
27525
|
const remainingProps = useConstraints(inputRef, rest, {
|
|
27141
27526
|
disabled: !name
|
|
27142
27527
|
});
|
|
27143
27528
|
return jsxs(Box, {
|
|
27144
27529
|
...remainingProps,
|
|
27145
|
-
ref:
|
|
27530
|
+
ref: containerRef,
|
|
27146
27531
|
baseClassName: "navi_list_container",
|
|
27147
27532
|
popover: popover,
|
|
27148
|
-
"data-expand-x": expandX ? "" : undefined,
|
|
27533
|
+
"data-expand-x": expandX || expand ? "" : undefined,
|
|
27149
27534
|
expandX: expandX,
|
|
27535
|
+
expand: expand,
|
|
27150
27536
|
maxHeight: maxHeight,
|
|
27151
27537
|
styleCSSVars: LIST_STYLE_CSS_VARS,
|
|
27152
27538
|
pseudoClasses: LIST_PSEUDO_CLASSES,
|
|
27539
|
+
pseudoStateSelector: ".navi_list",
|
|
27153
27540
|
hasChildFunction: true,
|
|
27154
27541
|
"data-navi-value": value || undefined,
|
|
27155
|
-
"data-input-proxy": name ? `#${CSS.escape(hiddenInputId)}` : undefined,
|
|
27156
27542
|
onnavi_list_request_nav: e => {
|
|
27157
27543
|
const {
|
|
27158
27544
|
item
|
|
@@ -27160,8 +27546,7 @@ const ListUI = props => {
|
|
|
27160
27546
|
if (!item) {
|
|
27161
27547
|
return;
|
|
27162
27548
|
}
|
|
27163
|
-
// navi_list_nav is dispatched by scrollToItem
|
|
27164
|
-
// completes (including the async path via pendingScrollRef).
|
|
27549
|
+
// navi_list_nav is dispatched immediately by scrollToItem (before scroll).
|
|
27165
27550
|
scrollToItem(item, {
|
|
27166
27551
|
reason: "navi_list_request_nav",
|
|
27167
27552
|
event: e.detail.event
|
|
@@ -27169,12 +27554,19 @@ const ListUI = props => {
|
|
|
27169
27554
|
},
|
|
27170
27555
|
onnavi_list_request_select: e => {
|
|
27171
27556
|
const {
|
|
27172
|
-
item
|
|
27557
|
+
item,
|
|
27558
|
+
event
|
|
27173
27559
|
} = e.detail;
|
|
27174
27560
|
if (!item) {
|
|
27175
27561
|
return;
|
|
27176
27562
|
}
|
|
27177
|
-
|
|
27563
|
+
// Nav to the selected item first (updates uiState, scrolls, etc.)
|
|
27564
|
+
scrollToItem(item, {
|
|
27565
|
+
reason: "navi_list_request_select",
|
|
27566
|
+
event: event || e
|
|
27567
|
+
});
|
|
27568
|
+
const listEl = e.currentTarget;
|
|
27569
|
+
dispatchPublicCustomEvent(listEl, "navi_list_select", {
|
|
27178
27570
|
item,
|
|
27179
27571
|
event: e
|
|
27180
27572
|
});
|
|
@@ -27190,16 +27582,23 @@ const ListUI = props => {
|
|
|
27190
27582
|
}), renderListMemoized]
|
|
27191
27583
|
});
|
|
27192
27584
|
};
|
|
27585
|
+
const LIST_STYLE_CSS_VARS = {
|
|
27586
|
+
maxHeight: "--list-max-height",
|
|
27587
|
+
borderColor: "--list-border-color",
|
|
27588
|
+
borderRadius: "--list-border-radius",
|
|
27589
|
+
borderWidth: "--list-border-width"
|
|
27590
|
+
};
|
|
27591
|
+
const LIST_PSEUDO_CLASSES = [":hover", ":focus", ":focus-visible", ":focus-within", ":read-only", ":disabled", ":-navi-void", ":-navi-has-value", ":-navi-expanded"];
|
|
27193
27592
|
const useListScrollSync = ({
|
|
27593
|
+
containerRef,
|
|
27194
27594
|
ref,
|
|
27195
|
-
ulRef,
|
|
27196
27595
|
tracker,
|
|
27197
27596
|
renderBudget,
|
|
27198
27597
|
virtualItemHeight,
|
|
27199
27598
|
searchText
|
|
27200
27599
|
}) => {
|
|
27201
27600
|
const debugScroll = useDebugScroll();
|
|
27202
|
-
const virtualItemHeightSignal = useVirtualItemHeightSignal(
|
|
27601
|
+
const virtualItemHeightSignal = useVirtualItemHeightSignal(ref, virtualItemHeight);
|
|
27203
27602
|
const [renderWindow, setRenderWindow] = useState({
|
|
27204
27603
|
start: 0,
|
|
27205
27604
|
end: renderBudget
|
|
@@ -27242,21 +27641,28 @@ const useListScrollSync = ({
|
|
|
27242
27641
|
if (index >= itemCount) {
|
|
27243
27642
|
index = itemCount - 1;
|
|
27244
27643
|
}
|
|
27245
|
-
const
|
|
27644
|
+
const scrollItemIntoView = itemEl => {
|
|
27246
27645
|
const trigger = `"${event.type}" on ${getElementSignature(event.target)} (${reason})`;
|
|
27247
27646
|
const block = event.type === "keydown" ? "nearest" : "center";
|
|
27248
27647
|
const scrollToItemCall = `${getElementSignature(itemEl)}.scrollIntoView({ block: "${block}", container: "nearest" })`;
|
|
27648
|
+
const listScrollContainerEl = containerRef.current.querySelector(`.navi_list_scroll_container`);
|
|
27249
27649
|
debugScroll(`${trigger} -> ${scrollToItemCall}`);
|
|
27250
27650
|
scrollIntoViewScoped(itemEl, {
|
|
27251
|
-
container:
|
|
27651
|
+
container: listScrollContainerEl,
|
|
27252
27652
|
block
|
|
27253
27653
|
});
|
|
27254
|
-
|
|
27255
|
-
dispatchPublicCustomEvent(listContainerEl, "navi_list_nav", {
|
|
27654
|
+
dispatchPublicCustomEvent(listEl, "navi_list_scroll", {
|
|
27256
27655
|
event,
|
|
27257
27656
|
item
|
|
27258
27657
|
});
|
|
27259
27658
|
};
|
|
27659
|
+
|
|
27660
|
+
// Dispatch navi_list_nav immediately — do not wait for scroll to complete.
|
|
27661
|
+
const listEl = ref.current;
|
|
27662
|
+
dispatchPublicCustomEvent(listEl, "navi_list_nav", {
|
|
27663
|
+
event,
|
|
27664
|
+
item
|
|
27665
|
+
});
|
|
27260
27666
|
const {
|
|
27261
27667
|
start,
|
|
27262
27668
|
end
|
|
@@ -27265,30 +27671,30 @@ const useListScrollSync = ({
|
|
|
27265
27671
|
if (isInWindow) {
|
|
27266
27672
|
const itemEl = document.getElementById(item.id);
|
|
27267
27673
|
if (itemEl) {
|
|
27268
|
-
|
|
27674
|
+
scrollItemIntoView(itemEl);
|
|
27269
27675
|
return;
|
|
27270
27676
|
}
|
|
27271
27677
|
}
|
|
27272
27678
|
// Not in DOM — shift the render window. The item will read
|
|
27273
|
-
// pendingScrollRef on mount and
|
|
27274
|
-
// then call onScrolled so we can dispatch navi_list_scroll.
|
|
27679
|
+
// pendingScrollRef on mount and scroll into view.
|
|
27275
27680
|
pendingScrollRef.current = {
|
|
27276
27681
|
id: item.id,
|
|
27277
27682
|
resolve: itemEl => {
|
|
27278
27683
|
pendingScrollRef.current = null;
|
|
27279
|
-
|
|
27684
|
+
scrollItemIntoView(itemEl);
|
|
27280
27685
|
}
|
|
27281
27686
|
};
|
|
27282
27687
|
const half = Math.floor(renderBudget / 2);
|
|
27283
27688
|
const newStart = Math.max(0, index - half);
|
|
27284
27689
|
const newEnd = newStart + renderBudget;
|
|
27285
|
-
updateRenderWindow(newStart, newEnd, `item to scroll out of render window`);
|
|
27690
|
+
updateRenderWindow(newStart, newEnd, `item to scroll (at ${index}) is out of render window`);
|
|
27286
27691
|
};
|
|
27287
27692
|
const currentScrollRef = useRef(null);
|
|
27288
27693
|
const updateCurrentScroll = () => {
|
|
27289
|
-
const listContainerEl =
|
|
27290
|
-
const
|
|
27291
|
-
const
|
|
27694
|
+
const listContainerEl = containerRef.current;
|
|
27695
|
+
const listScrollContainerEl = listContainerEl.querySelector(`.navi_list_scroll_container`);
|
|
27696
|
+
const currentScrollLeft = listScrollContainerEl.scrollLeft;
|
|
27697
|
+
const currentScrollTop = listScrollContainerEl.scrollTop;
|
|
27292
27698
|
const renderWindow = renderWindowRef.current;
|
|
27293
27699
|
currentScrollRef.current = {
|
|
27294
27700
|
left: currentScrollLeft,
|
|
@@ -27313,7 +27719,7 @@ const useListScrollSync = ({
|
|
|
27313
27719
|
// Scroll to the selected item when the list is first presented on screen.
|
|
27314
27720
|
// Skipped when inside a closed <dialog>/<details> (scrollIntoView is a no-op
|
|
27315
27721
|
// on hidden elements); re-runs automatically every time the ancestor opens.
|
|
27316
|
-
useDisplayedLayoutEffect(
|
|
27722
|
+
useDisplayedLayoutEffect(containerRef, (el, openEvent) => {
|
|
27317
27723
|
updateCurrentScroll();
|
|
27318
27724
|
const items = tracker.itemsSignal.peek();
|
|
27319
27725
|
const firstSelected = items.find(i => i.selected);
|
|
@@ -27346,7 +27752,8 @@ const useListScrollSync = ({
|
|
|
27346
27752
|
const savedScrollRef = useRef(null);
|
|
27347
27753
|
const topMatchScoresKeyRef = useRef("");
|
|
27348
27754
|
useLayoutEffect(() => {
|
|
27349
|
-
const listContainerEl =
|
|
27755
|
+
const listContainerEl = containerRef.current;
|
|
27756
|
+
const listScrollContainerEl = listContainerEl.querySelector(`.navi_list_scroll_container`);
|
|
27350
27757
|
if (!searchText) {
|
|
27351
27758
|
// no search -> try to restore scroll position
|
|
27352
27759
|
topMatchScoresKeyRef.current = "";
|
|
@@ -27362,8 +27769,8 @@ const useListScrollSync = ({
|
|
|
27362
27769
|
const left = savedScroll.left;
|
|
27363
27770
|
const top = savedScroll.top;
|
|
27364
27771
|
// use scrollTo to respect eventual css scroll-behavior: smooth;
|
|
27365
|
-
debugScroll(`restore scroll: ${getElementSignature(
|
|
27366
|
-
|
|
27772
|
+
debugScroll(`restore scroll: ${getElementSignature(listScrollContainerEl)}.scrollTo({ left: ${left}, top: ${top} })`);
|
|
27773
|
+
listScrollContainerEl.scrollTo({
|
|
27367
27774
|
left: savedScroll.left,
|
|
27368
27775
|
top: savedScroll.top
|
|
27369
27776
|
});
|
|
@@ -27376,15 +27783,15 @@ const useListScrollSync = ({
|
|
|
27376
27783
|
item
|
|
27377
27784
|
} = getScrollInfo({
|
|
27378
27785
|
scrollTop: savedScroll.top
|
|
27379
|
-
},
|
|
27380
|
-
|
|
27786
|
+
}, listScrollContainerEl, tracker, virtualItemHeightSignal, renderWindowRef);
|
|
27787
|
+
const listEl = ref.current;
|
|
27788
|
+
dispatchPublicCustomEvent(listEl, "navi_list_nav", {
|
|
27381
27789
|
item,
|
|
27382
27790
|
event: new CustomEvent("navi_scroll_restore")
|
|
27383
27791
|
});
|
|
27384
27792
|
});
|
|
27793
|
+
return;
|
|
27385
27794
|
}
|
|
27386
|
-
|
|
27387
|
-
// During search -> watch for changes in the top items or their scores.
|
|
27388
27795
|
const visibleItems = tracker.visibleItemsSignal.peek();
|
|
27389
27796
|
const topItems = visibleItems.slice(0, renderBudget);
|
|
27390
27797
|
const topMatchScoresKey = topItems.map(i => `${i.id}:${i.matchScore ?? ""}`).join(",");
|
|
@@ -27410,12 +27817,12 @@ const useListScrollSync = ({
|
|
|
27410
27817
|
|
|
27411
27818
|
// Scroll listener — slides the window as the user scrolls.
|
|
27412
27819
|
useLayoutEffect(() => {
|
|
27413
|
-
const listContainerEl =
|
|
27820
|
+
const listContainerEl = containerRef.current;
|
|
27414
27821
|
if (!listContainerEl) {
|
|
27415
27822
|
return undefined;
|
|
27416
27823
|
}
|
|
27824
|
+
const listScrollContainerEl = listContainerEl.querySelector(`.navi_list_scroll_container`);
|
|
27417
27825
|
const listEl = listContainerEl.querySelector(".navi_list");
|
|
27418
|
-
const scrollContainer = getScrollContainer(listEl);
|
|
27419
27826
|
const onScroll = () => {
|
|
27420
27827
|
updateCurrentScroll();
|
|
27421
27828
|
const visibleItemCount = tracker.visibleCountSignal.peek();
|
|
@@ -27428,8 +27835,8 @@ const useListScrollSync = ({
|
|
|
27428
27835
|
}
|
|
27429
27836
|
let reason = "";
|
|
27430
27837
|
const scrollInfo = getScrollInfo({
|
|
27431
|
-
scrollTop:
|
|
27432
|
-
},
|
|
27838
|
+
scrollTop: listScrollContainerEl.scrollTop
|
|
27839
|
+
}, listScrollContainerEl, tracker, virtualItemHeightSignal, renderWindowRef);
|
|
27433
27840
|
if (!scrollInfo) {
|
|
27434
27841
|
return;
|
|
27435
27842
|
}
|
|
@@ -27446,11 +27853,11 @@ const useListScrollSync = ({
|
|
|
27446
27853
|
}
|
|
27447
27854
|
updateRenderWindow(newStart, newEnd, reason);
|
|
27448
27855
|
};
|
|
27449
|
-
|
|
27856
|
+
listScrollContainerEl.addEventListener("scroll", onScroll, {
|
|
27450
27857
|
passive: true
|
|
27451
27858
|
});
|
|
27452
27859
|
return () => {
|
|
27453
|
-
|
|
27860
|
+
listScrollContainerEl.removeEventListener("scroll", onScroll);
|
|
27454
27861
|
};
|
|
27455
27862
|
}, [renderBudget]);
|
|
27456
27863
|
return {
|
|
@@ -27466,10 +27873,10 @@ const useListScrollSync = ({
|
|
|
27466
27873
|
// Returns { index, item, reason } or null if nothing can be determined.
|
|
27467
27874
|
const getScrollInfo = ({
|
|
27468
27875
|
scrollTop
|
|
27469
|
-
},
|
|
27470
|
-
const listEl =
|
|
27876
|
+
}, listScrollContainerEl, tracker, virtualItemHeightSignal, renderWindowRef) => {
|
|
27877
|
+
const listEl = listScrollContainerEl.querySelector(".navi_list");
|
|
27471
27878
|
const items = tracker.itemsSignal.peek();
|
|
27472
|
-
const containerRect =
|
|
27879
|
+
const containerRect = listScrollContainerEl.getBoundingClientRect();
|
|
27473
27880
|
let hitEl = null;
|
|
27474
27881
|
let hitFiller = null;
|
|
27475
27882
|
for (let y = containerRect.top + 1; y < containerRect.bottom; y += 4) {
|
|
@@ -27520,7 +27927,7 @@ const getScrollInfo = ({
|
|
|
27520
27927
|
reason: "no hit"
|
|
27521
27928
|
};
|
|
27522
27929
|
};
|
|
27523
|
-
const useVirtualItemHeightSignal = (
|
|
27930
|
+
const useVirtualItemHeightSignal = (ref, virtualItemHeightProp = 0) => {
|
|
27524
27931
|
const virtualHeightSignalRef = useRef(null);
|
|
27525
27932
|
if (!virtualHeightSignalRef.current) {
|
|
27526
27933
|
virtualHeightSignalRef.current = signal(virtualItemHeightProp);
|
|
@@ -27534,11 +27941,11 @@ const useVirtualItemHeightSignal = (ulRef, virtualItemHeightProp = 0) => {
|
|
|
27534
27941
|
if (virtualHeightSignal.peek() !== 0) {
|
|
27535
27942
|
return;
|
|
27536
27943
|
}
|
|
27537
|
-
const
|
|
27538
|
-
if (!
|
|
27944
|
+
const listEl = ref.current;
|
|
27945
|
+
if (!listEl) {
|
|
27539
27946
|
return;
|
|
27540
27947
|
}
|
|
27541
|
-
const firstListItem =
|
|
27948
|
+
const firstListItem = listEl.querySelector(REAL_LIST_ITEM_SELECTOR);
|
|
27542
27949
|
if (!firstListItem) {
|
|
27543
27950
|
return;
|
|
27544
27951
|
}
|
|
@@ -27547,13 +27954,7 @@ const useVirtualItemHeightSignal = (ulRef, virtualItemHeightProp = 0) => {
|
|
|
27547
27954
|
});
|
|
27548
27955
|
return virtualHeightSignal;
|
|
27549
27956
|
};
|
|
27550
|
-
|
|
27551
|
-
maxHeight: "--list-max-height",
|
|
27552
|
-
borderColor: "--list-border-color",
|
|
27553
|
-
borderRadius: "--list-border-radius",
|
|
27554
|
-
borderWidth: "--list-border-width"
|
|
27555
|
-
};
|
|
27556
|
-
const LIST_PSEUDO_CLASSES = [":-navi-void"];
|
|
27957
|
+
|
|
27557
27958
|
// Inner <ul> — hosts the fillers + items.
|
|
27558
27959
|
// Creates a virtualItemHeight signal so TopFiller and BottomFiller can
|
|
27559
27960
|
// subscribe to it independently. When virtualItemHeight is passed as a prop it
|
|
@@ -27696,7 +28097,8 @@ const ListWithPopover = props => {
|
|
|
27696
28097
|
...props,
|
|
27697
28098
|
popover: "manual",
|
|
27698
28099
|
onnavi_list_request_open: e => {
|
|
27699
|
-
const
|
|
28100
|
+
const listEl = e.currentTarget;
|
|
28101
|
+
const listContainerEl = listEl.closest(".navi_list_container");
|
|
27700
28102
|
const anchor = e.detail?.anchor;
|
|
27701
28103
|
listContainerEl.showPopover();
|
|
27702
28104
|
const positionPopover = () => {
|
|
@@ -27732,23 +28134,26 @@ const ListWithPopover = props => {
|
|
|
27732
28134
|
positionPopover();
|
|
27733
28135
|
});
|
|
27734
28136
|
cleanupRef.current = () => cleanup.disconnect();
|
|
27735
|
-
dispatchPublicCustomEvent(
|
|
28137
|
+
dispatchPublicCustomEvent(listEl, "navi_list_open", {
|
|
27736
28138
|
event: e
|
|
27737
28139
|
});
|
|
27738
28140
|
},
|
|
27739
28141
|
onnavi_list_request_close: e => {
|
|
27740
|
-
const
|
|
28142
|
+
const listEl = e.currentTarget;
|
|
28143
|
+
const listContainerEl = listEl.closest(".navi_list_container");
|
|
27741
28144
|
cleanupRef.current?.();
|
|
27742
28145
|
listContainerEl.removeAttribute("data-anchor-hidden");
|
|
27743
28146
|
listContainerEl.hidePopover();
|
|
27744
|
-
dispatchPublicCustomEvent(
|
|
28147
|
+
dispatchPublicCustomEvent(listEl, "navi_list_close", {
|
|
27745
28148
|
event: e
|
|
27746
28149
|
});
|
|
27747
28150
|
}
|
|
27748
28151
|
});
|
|
27749
28152
|
};
|
|
27750
28153
|
|
|
27751
|
-
// Interactive variant
|
|
28154
|
+
// Interactive variant: manages hover/keyboard/selection state and handles the
|
|
28155
|
+
// navi event protocol. When an action is provided it binds the action to ui state
|
|
28156
|
+
// and fires it on select. When only uiAction is provided it calls it directly.
|
|
27752
28157
|
const ListWithAction = props => {
|
|
27753
28158
|
const {
|
|
27754
28159
|
ref,
|
|
@@ -27760,10 +28165,15 @@ const ListWithAction = props => {
|
|
|
27760
28165
|
onActionAbort,
|
|
27761
28166
|
onActionError,
|
|
27762
28167
|
onActionEnd,
|
|
27763
|
-
uiAction,
|
|
27764
28168
|
...rest
|
|
27765
28169
|
} = props;
|
|
27766
|
-
|
|
28170
|
+
// Setup uiStateController and bind action to uiState
|
|
28171
|
+
const uiStateController = useUIStateController(props, "list", {
|
|
28172
|
+
allowNameless: true
|
|
28173
|
+
});
|
|
28174
|
+
const uiState = useUIState(uiStateController);
|
|
28175
|
+
// Bind action to uiState (like InputTextualWithAction, null-safe when no action)
|
|
28176
|
+
const [boundAction] = useActionBoundToOneParam(action, uiState);
|
|
27767
28177
|
const {
|
|
27768
28178
|
loading: actionLoading
|
|
27769
28179
|
} = useActionStatus(boundAction);
|
|
@@ -27779,34 +28189,8 @@ const ListWithAction = props => {
|
|
|
27779
28189
|
onError: onActionError,
|
|
27780
28190
|
onEnd: onActionEnd
|
|
27781
28191
|
});
|
|
27782
|
-
const innerUiAction = (value, event) => {
|
|
27783
|
-
uiAction?.(value, event);
|
|
27784
|
-
// Dispatch action request so useActionEvents can pick it up
|
|
27785
|
-
if (ref && ref.current) {
|
|
27786
|
-
dispatchCustomEvent(ref.current, "navi_action_requested", {
|
|
27787
|
-
bubbles: true,
|
|
27788
|
-
detail: {
|
|
27789
|
-
value
|
|
27790
|
-
}
|
|
27791
|
-
});
|
|
27792
|
-
}
|
|
27793
|
-
};
|
|
27794
|
-
return jsx(List, {
|
|
27795
|
-
"data-action": boundAction.name,
|
|
27796
|
-
...rest,
|
|
27797
|
-
ref: ref,
|
|
27798
|
-
action: undefined,
|
|
27799
|
-
loading: loading || actionLoading,
|
|
27800
|
-
uiAction: innerUiAction
|
|
27801
|
-
});
|
|
27802
|
-
};
|
|
27803
28192
|
|
|
27804
|
-
//
|
|
27805
|
-
// navi event protocol, then delegates rendering to ListUI.
|
|
27806
|
-
const ListInteractive = props => {
|
|
27807
|
-
const uiStateController = useUIStateController(props, "list", {
|
|
27808
|
-
allowNameless: true
|
|
27809
|
-
});
|
|
28193
|
+
// Mouse/keyboard pointed state
|
|
27810
28194
|
const [mousePointedId, setMousePointedId] = useState(null);
|
|
27811
28195
|
const [keyboardPointedId, setKeyboardPointedId] = useState(null);
|
|
27812
28196
|
const anchorIdRef = useRef(null);
|
|
@@ -27828,146 +28212,163 @@ const ListInteractive = props => {
|
|
|
27828
28212
|
}
|
|
27829
28213
|
return visibleItemsRef.current.find(i => i.id === anchorId);
|
|
27830
28214
|
};
|
|
27831
|
-
|
|
27832
|
-
|
|
27833
|
-
|
|
27834
|
-
|
|
27835
|
-
|
|
27836
|
-
|
|
27837
|
-
|
|
27838
|
-
|
|
27839
|
-
|
|
27840
|
-
|
|
27841
|
-
|
|
27842
|
-
|
|
27843
|
-
|
|
27844
|
-
|
|
27845
|
-
|
|
27846
|
-
|
|
27847
|
-
|
|
27848
|
-
|
|
27849
|
-
|
|
27850
|
-
|
|
27851
|
-
|
|
27852
|
-
|
|
27853
|
-
|
|
27854
|
-
|
|
27855
|
-
|
|
27856
|
-
|
|
27857
|
-
|
|
27858
|
-
|
|
27859
|
-
|
|
27860
|
-
|
|
27861
|
-
|
|
27862
|
-
|
|
27863
|
-
|
|
27864
|
-
|
|
27865
|
-
|
|
27866
|
-
|
|
27867
|
-
|
|
27868
|
-
i++;
|
|
27869
|
-
}
|
|
27870
|
-
return i < visibleItemCount ? i : anchorIndex;
|
|
27871
|
-
}
|
|
27872
|
-
let belowIndex = anchorIndex + 1;
|
|
27873
|
-
while (belowIndex < visibleItemCount && isDisabledIndex(belowIndex)) {
|
|
27874
|
-
belowIndex++;
|
|
27875
|
-
}
|
|
27876
|
-
return belowIndex < visibleItemCount ? belowIndex : anchorIndex;
|
|
27877
|
-
}
|
|
27878
|
-
if (goal === "up") {
|
|
27879
|
-
if (anchorIndex === -1) {
|
|
27880
|
-
let i = visibleItemCount - 1;
|
|
27881
|
-
while (i >= 0 && isDisabledIndex(i)) {
|
|
27882
|
-
i--;
|
|
27883
|
-
}
|
|
27884
|
-
return i >= 0 ? i : anchorIndex;
|
|
27885
|
-
}
|
|
27886
|
-
let aboveIndex = anchorIndex - 1;
|
|
27887
|
-
while (aboveIndex >= 0 && isDisabledIndex(aboveIndex)) {
|
|
27888
|
-
aboveIndex--;
|
|
27889
|
-
}
|
|
27890
|
-
return aboveIndex >= 0 ? aboveIndex : anchorIndex;
|
|
27891
|
-
}
|
|
27892
|
-
if (goal === "first") {
|
|
27893
|
-
let i = 0;
|
|
27894
|
-
while (i < visibleItemCount && isDisabledIndex(i)) {
|
|
27895
|
-
i++;
|
|
27896
|
-
}
|
|
27897
|
-
return i < visibleItemCount ? i : anchorIndex;
|
|
27898
|
-
}
|
|
27899
|
-
if (goal === "last") {
|
|
27900
|
-
let i = visibleItemCount - 1;
|
|
27901
|
-
while (i >= 0 && isDisabledIndex(i)) {
|
|
27902
|
-
i--;
|
|
27903
|
-
}
|
|
27904
|
-
return i >= 0 ? i : anchorIndex;
|
|
27905
|
-
}
|
|
27906
|
-
return anchorIndex;
|
|
27907
|
-
};
|
|
27908
|
-
const index = resolveIndex();
|
|
27909
|
-
if (index === anchorIndex) {
|
|
27910
|
-
return;
|
|
27911
|
-
}
|
|
27912
|
-
if (event.type === "keydown") {
|
|
27913
|
-
event.preventDefault();
|
|
27914
|
-
}
|
|
27915
|
-
const item = visibleItems[index];
|
|
27916
|
-
dispatchCustomEvent(e.currentTarget, "navi_list_request_nav", {
|
|
27917
|
-
event: e,
|
|
27918
|
-
item
|
|
27919
|
-
});
|
|
27920
|
-
},
|
|
27921
|
-
onnavi_list_request_interaction_state_reset: () => {
|
|
27922
|
-
setAnchorId(null);
|
|
27923
|
-
setKeyboardPointedId(null);
|
|
27924
|
-
setMousePointedId(null);
|
|
27925
|
-
},
|
|
27926
|
-
onnavi_list_request_select_current: e => {
|
|
27927
|
-
const item = getAnchorItem();
|
|
27928
|
-
dispatchCustomEvent(e.currentTarget, "navi_list_request_select", {
|
|
27929
|
-
event: e,
|
|
27930
|
-
item
|
|
27931
|
-
});
|
|
27932
|
-
},
|
|
27933
|
-
onnavi_list_nav: e => {
|
|
27934
|
-
const {
|
|
27935
|
-
item,
|
|
27936
|
-
event
|
|
27937
|
-
} = e.detail;
|
|
27938
|
-
const id = item.id;
|
|
27939
|
-
if (event.type === "navi_list_nav_top_on_displayed") {
|
|
27940
|
-
// arrow down should focus first item for instance
|
|
27941
|
-
setAnchorId(null);
|
|
27942
|
-
} else {
|
|
27943
|
-
setAnchorId(id);
|
|
27944
|
-
}
|
|
27945
|
-
if (event.type === "keydown") {
|
|
27946
|
-
setKeyboardPointedId(id);
|
|
27947
|
-
} else {
|
|
27948
|
-
setKeyboardPointedId(null);
|
|
28215
|
+
const listVnode = jsx(List, {
|
|
28216
|
+
keyboardInteractions: true,
|
|
28217
|
+
"data-action": boundAction.name,
|
|
28218
|
+
...rest,
|
|
28219
|
+
ref: ref,
|
|
28220
|
+
action: undefined,
|
|
28221
|
+
uiAction: undefined,
|
|
28222
|
+
loading: loading || actionLoading,
|
|
28223
|
+
value: uiState,
|
|
28224
|
+
onListVisibleItemsChange: visibleItems => {
|
|
28225
|
+
props.onListVisibleItemsChange?.(visibleItems);
|
|
28226
|
+
visibleItemsRef.current = visibleItems;
|
|
28227
|
+
},
|
|
28228
|
+
onnavi_list_request_hover: e => {
|
|
28229
|
+
const {
|
|
28230
|
+
item
|
|
28231
|
+
} = e.detail;
|
|
28232
|
+
setMousePointedId(item ? item.id : null);
|
|
28233
|
+
},
|
|
28234
|
+
onnavi_list_request_nav_from_current: e => {
|
|
28235
|
+
const {
|
|
28236
|
+
event = e,
|
|
28237
|
+
goal
|
|
28238
|
+
} = e.detail;
|
|
28239
|
+
const visibleItems = visibleItemsRef.current;
|
|
28240
|
+
const visibleItemCount = visibleItems.length;
|
|
28241
|
+
if (visibleItemCount === 0) {
|
|
28242
|
+
return;
|
|
28243
|
+
}
|
|
28244
|
+
const anchorIndex = getAnchorIndex();
|
|
28245
|
+
const isDisabledIndex = i => Boolean(visibleItems[i]?.disabled);
|
|
28246
|
+
const resolveIndex = () => {
|
|
28247
|
+
if (goal === "down") {
|
|
28248
|
+
if (anchorIndex === -1) {
|
|
28249
|
+
let i = 0;
|
|
28250
|
+
while (i < visibleItemCount && isDisabledIndex(i)) {
|
|
28251
|
+
i++;
|
|
27949
28252
|
}
|
|
27950
|
-
|
|
27951
|
-
|
|
27952
|
-
|
|
27953
|
-
|
|
27954
|
-
|
|
27955
|
-
|
|
27956
|
-
|
|
27957
|
-
|
|
27958
|
-
|
|
27959
|
-
|
|
27960
|
-
|
|
27961
|
-
|
|
28253
|
+
return i < visibleItemCount ? i : anchorIndex;
|
|
28254
|
+
}
|
|
28255
|
+
let belowIndex = anchorIndex + 1;
|
|
28256
|
+
while (belowIndex < visibleItemCount && isDisabledIndex(belowIndex)) {
|
|
28257
|
+
belowIndex++;
|
|
28258
|
+
}
|
|
28259
|
+
return belowIndex < visibleItemCount ? belowIndex : anchorIndex;
|
|
28260
|
+
}
|
|
28261
|
+
if (goal === "up") {
|
|
28262
|
+
if (anchorIndex === -1) {
|
|
28263
|
+
let i = visibleItemCount - 1;
|
|
28264
|
+
while (i >= 0 && isDisabledIndex(i)) {
|
|
28265
|
+
i--;
|
|
27962
28266
|
}
|
|
27963
|
-
|
|
27964
|
-
|
|
28267
|
+
return i >= 0 ? i : anchorIndex;
|
|
28268
|
+
}
|
|
28269
|
+
let aboveIndex = anchorIndex - 1;
|
|
28270
|
+
while (aboveIndex >= 0 && isDisabledIndex(aboveIndex)) {
|
|
28271
|
+
aboveIndex--;
|
|
28272
|
+
}
|
|
28273
|
+
return aboveIndex >= 0 ? aboveIndex : anchorIndex;
|
|
28274
|
+
}
|
|
28275
|
+
if (goal === "first") {
|
|
28276
|
+
let i = 0;
|
|
28277
|
+
while (i < visibleItemCount && isDisabledIndex(i)) {
|
|
28278
|
+
i++;
|
|
27965
28279
|
}
|
|
28280
|
+
return i < visibleItemCount ? i : anchorIndex;
|
|
28281
|
+
}
|
|
28282
|
+
if (goal === "last") {
|
|
28283
|
+
let i = visibleItemCount - 1;
|
|
28284
|
+
while (i >= 0 && isDisabledIndex(i)) {
|
|
28285
|
+
i--;
|
|
28286
|
+
}
|
|
28287
|
+
return i >= 0 ? i : anchorIndex;
|
|
28288
|
+
}
|
|
28289
|
+
return anchorIndex;
|
|
28290
|
+
};
|
|
28291
|
+
const index = resolveIndex();
|
|
28292
|
+
if (index === anchorIndex) {
|
|
28293
|
+
if (event.type === "keydown") {
|
|
28294
|
+
event.preventDefault();
|
|
28295
|
+
}
|
|
28296
|
+
return;
|
|
28297
|
+
}
|
|
28298
|
+
if (event.type === "keydown") {
|
|
28299
|
+
event.preventDefault();
|
|
28300
|
+
}
|
|
28301
|
+
const item = visibleItems[index];
|
|
28302
|
+
dispatchCustomEvent(e.currentTarget, "navi_list_request_nav", {
|
|
28303
|
+
event: e,
|
|
28304
|
+
item
|
|
28305
|
+
});
|
|
28306
|
+
},
|
|
28307
|
+
onnavi_list_request_interaction_state_reset: () => {
|
|
28308
|
+
setAnchorId(null);
|
|
28309
|
+
setKeyboardPointedId(null);
|
|
28310
|
+
setMousePointedId(null);
|
|
28311
|
+
},
|
|
28312
|
+
onnavi_list_request_select_current: e => {
|
|
28313
|
+
const item = getAnchorItem();
|
|
28314
|
+
dispatchCustomEvent(e.currentTarget, "navi_list_request_select", {
|
|
28315
|
+
event: e,
|
|
28316
|
+
item
|
|
28317
|
+
});
|
|
28318
|
+
},
|
|
28319
|
+
onnavi_list_nav: e => {
|
|
28320
|
+
const {
|
|
28321
|
+
item,
|
|
28322
|
+
event
|
|
28323
|
+
} = e.detail;
|
|
28324
|
+
const id = item ? item.id : null;
|
|
28325
|
+
const isNonUserNav = event.type === "navi_list_nav_top_on_displayed" || event.type === "navi_list_top_match_change";
|
|
28326
|
+
if (isNonUserNav) {
|
|
28327
|
+
setAnchorId(null);
|
|
28328
|
+
} else {
|
|
28329
|
+
setAnchorId(id);
|
|
28330
|
+
}
|
|
28331
|
+
if (event.type === "keydown") {
|
|
28332
|
+
setKeyboardPointedId(id);
|
|
28333
|
+
} else {
|
|
28334
|
+
setKeyboardPointedId(null);
|
|
28335
|
+
}
|
|
28336
|
+
const isAutomaticNav = event.type === "navi_list_nav_top_on_displayed" || event.type === "navi_list_top_match_change" || event.type === "navi_scroll_restore";
|
|
28337
|
+
if (item && !isAutomaticNav) {
|
|
28338
|
+
uiStateController.setUIState(item.value, event);
|
|
28339
|
+
}
|
|
28340
|
+
}
|
|
28341
|
+
// Dispatch action request on select
|
|
28342
|
+
,
|
|
28343
|
+
|
|
28344
|
+
onnavi_list_select: e => {
|
|
28345
|
+
const listEl = e.currentTarget;
|
|
28346
|
+
dispatchActionRequestedCustomEvent(listEl, {
|
|
28347
|
+
event: e,
|
|
28348
|
+
requester: e.target
|
|
28349
|
+
});
|
|
28350
|
+
}
|
|
28351
|
+
});
|
|
28352
|
+
return jsx(UIStateControllerContext.Provider, {
|
|
28353
|
+
value: uiStateController,
|
|
28354
|
+
children: jsx(UIStateContext.Provider, {
|
|
28355
|
+
value: uiState,
|
|
28356
|
+
children: jsx(ListInteractiveContext.Provider, {
|
|
28357
|
+
value: true,
|
|
28358
|
+
children: jsx(ListMousePointedIdContext.Provider, {
|
|
28359
|
+
value: mousePointedId,
|
|
28360
|
+
children: jsx(ListKeyboardPointedIdContext.Provider, {
|
|
28361
|
+
value: keyboardPointedId,
|
|
28362
|
+
children: listVnode
|
|
28363
|
+
})
|
|
27966
28364
|
})
|
|
27967
28365
|
})
|
|
27968
28366
|
})
|
|
27969
28367
|
});
|
|
27970
28368
|
};
|
|
28369
|
+
|
|
28370
|
+
// Interactive variant: manages hover/keyboard/selection state and handles the
|
|
28371
|
+
// navi event protocol, then delegates rendering to ListUI.
|
|
27971
28372
|
const ListWithKeyboardInteractions = props => {
|
|
27972
28373
|
const {
|
|
27973
28374
|
autoFocus,
|
|
@@ -28207,7 +28608,19 @@ const ListItemReal = ({
|
|
|
28207
28608
|
// directly to the correct text node positions without re-searching.
|
|
28208
28609
|
const textNodes = [];
|
|
28209
28610
|
let totalLength = 0;
|
|
28210
|
-
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT
|
|
28611
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
|
28612
|
+
acceptNode: node => {
|
|
28613
|
+
// Skip text nodes inside aria-hidden elements (icons, decorative emoji, etc.)
|
|
28614
|
+
let el = node.parentElement;
|
|
28615
|
+
while (el && el !== root) {
|
|
28616
|
+
if (el.getAttribute("aria-hidden") === "true") {
|
|
28617
|
+
return NodeFilter.FILTER_REJECT;
|
|
28618
|
+
}
|
|
28619
|
+
el = el.parentElement;
|
|
28620
|
+
}
|
|
28621
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
28622
|
+
}
|
|
28623
|
+
});
|
|
28211
28624
|
let node;
|
|
28212
28625
|
while (node = walker.nextNode()) {
|
|
28213
28626
|
textNodes.push({
|
|
@@ -28254,13 +28667,14 @@ const ListItemReal = ({
|
|
|
28254
28667
|
"navi-list-item-real": "",
|
|
28255
28668
|
"data-interactive": isInteractive ? "" : undefined,
|
|
28256
28669
|
"data-anchor": isPointedByKeyboard ? "" : undefined,
|
|
28257
|
-
|
|
28670
|
+
...rest,
|
|
28671
|
+
ref: ref,
|
|
28258
28672
|
onMouseEnter: e => {
|
|
28259
28673
|
if (disabled) {
|
|
28260
28674
|
return;
|
|
28261
28675
|
}
|
|
28262
|
-
const
|
|
28263
|
-
dispatchCustomEvent(
|
|
28676
|
+
const listEl = e.currentTarget.closest(".navi_list");
|
|
28677
|
+
dispatchCustomEvent(listEl, "navi_list_request_hover", {
|
|
28264
28678
|
item,
|
|
28265
28679
|
event: e
|
|
28266
28680
|
});
|
|
@@ -28270,8 +28684,8 @@ const ListItemReal = ({
|
|
|
28270
28684
|
if (disabled) {
|
|
28271
28685
|
return;
|
|
28272
28686
|
}
|
|
28273
|
-
const
|
|
28274
|
-
dispatchCustomEvent(
|
|
28687
|
+
const listEl = e.currentTarget.closest(".navi_list");
|
|
28688
|
+
dispatchCustomEvent(listEl, "navi_list_request_hover", {
|
|
28275
28689
|
item: null,
|
|
28276
28690
|
event: e
|
|
28277
28691
|
});
|
|
@@ -28284,23 +28698,21 @@ const ListItemReal = ({
|
|
|
28284
28698
|
if (e.button !== 0) {
|
|
28285
28699
|
return;
|
|
28286
28700
|
}
|
|
28287
|
-
const
|
|
28288
|
-
dispatchCustomEvent(
|
|
28701
|
+
const listEl = e.currentTarget.closest(".navi_list");
|
|
28702
|
+
dispatchCustomEvent(listEl, "navi_list_request_select", {
|
|
28289
28703
|
item,
|
|
28290
28704
|
event: e
|
|
28291
28705
|
});
|
|
28292
28706
|
rest.onMouseDown?.(e);
|
|
28293
28707
|
},
|
|
28294
|
-
...rest,
|
|
28295
|
-
ref: ref,
|
|
28296
28708
|
basePseudoState: {
|
|
28297
|
-
...rest.basePseudoState,
|
|
28298
28709
|
":disabled": Boolean(disabled),
|
|
28299
28710
|
":-navi-pointed": isPointed,
|
|
28300
28711
|
":-navi-pointed-by-mouse": isPointedByMouse,
|
|
28301
28712
|
":-navi-pointed-by-keyboard": isPointedByKeyboard,
|
|
28302
28713
|
":-navi-pointed-by-proxy": isPointedByProxy,
|
|
28303
|
-
":-navi-selected": selected
|
|
28714
|
+
":-navi-selected": selected,
|
|
28715
|
+
...rest.basePseudoState
|
|
28304
28716
|
},
|
|
28305
28717
|
children: children
|
|
28306
28718
|
});
|
|
@@ -28310,9 +28722,13 @@ const LIST_ITEM_STYLE_CSS_VARS = {
|
|
|
28310
28722
|
"color": "--list-item-color",
|
|
28311
28723
|
"backgroundColor": "--list-item-background-color",
|
|
28312
28724
|
"fontWeight": "--list-item-font-weight",
|
|
28313
|
-
":-navi-pointed": {
|
|
28314
|
-
color: "--list-item-color-pointed",
|
|
28315
|
-
backgroundColor: "--list-item-background-color-pointed"
|
|
28725
|
+
":-navi-pointed-by-keyboard": {
|
|
28726
|
+
color: "--list-item-color-keyboard-pointed",
|
|
28727
|
+
backgroundColor: "--list-item-background-color-keyboard-pointed"
|
|
28728
|
+
},
|
|
28729
|
+
":-navi-pointed-by-mouse": {
|
|
28730
|
+
color: "--list-item-color-mouse-pointed",
|
|
28731
|
+
backgroundColor: "--list-item-background-color-mouse-pointed"
|
|
28316
28732
|
},
|
|
28317
28733
|
":hover": {
|
|
28318
28734
|
color: "--list-item-color-hover",
|
|
@@ -28320,8 +28736,7 @@ const LIST_ITEM_STYLE_CSS_VARS = {
|
|
|
28320
28736
|
},
|
|
28321
28737
|
":-navi-selected": {
|
|
28322
28738
|
color: "--list-item-color-selected",
|
|
28323
|
-
backgroundColor: "--list-item-background-color-selected"
|
|
28324
|
-
fontWeight: "--list-item-font-weight-selected"
|
|
28739
|
+
backgroundColor: "--list-item-background-color-selected"
|
|
28325
28740
|
},
|
|
28326
28741
|
":disabled": {
|
|
28327
28742
|
color: "--list-item-color-disabled",
|
|
@@ -28395,9 +28810,9 @@ const ListItemHeader = props => {
|
|
|
28395
28810
|
const defaultRef = useRef(null);
|
|
28396
28811
|
const ref = props.ref || defaultRef;
|
|
28397
28812
|
useDisplayedLayoutEffect(ref, headerEl => {
|
|
28398
|
-
const
|
|
28813
|
+
const listContainerEl = headerEl.closest(".navi_list_container");
|
|
28399
28814
|
const headerHeight = headerEl.getBoundingClientRect().height;
|
|
28400
|
-
|
|
28815
|
+
listContainerEl.style.setProperty("--list-header-height", `${headerHeight}px`);
|
|
28401
28816
|
}, []);
|
|
28402
28817
|
return jsx(ListItem, {
|
|
28403
28818
|
...props,
|
|
@@ -28410,9 +28825,9 @@ const ListItemFooter = props => {
|
|
|
28410
28825
|
const defaultRef = useRef(null);
|
|
28411
28826
|
const ref = props.ref || defaultRef;
|
|
28412
28827
|
useDisplayedLayoutEffect(ref, headerEl => {
|
|
28413
|
-
const
|
|
28828
|
+
const listContainerEl = headerEl.closest(".navi_list_container");
|
|
28414
28829
|
const headerHeight = headerEl.getBoundingClientRect().height;
|
|
28415
|
-
|
|
28830
|
+
listContainerEl.style.setProperty("--list-footer-height", `${headerHeight}px`);
|
|
28416
28831
|
}, []);
|
|
28417
28832
|
return jsx(ListItem, {
|
|
28418
28833
|
...props,
|
|
@@ -28421,42 +28836,42 @@ const ListItemFooter = props => {
|
|
|
28421
28836
|
baseClassName: "navi_list_item_footer"
|
|
28422
28837
|
});
|
|
28423
28838
|
};
|
|
28424
|
-
const requestListNavFromCurrent = (
|
|
28839
|
+
const requestListNavFromCurrent = (listEl, {
|
|
28425
28840
|
event,
|
|
28426
28841
|
goal
|
|
28427
28842
|
}) => {
|
|
28428
|
-
return dispatchCustomEvent(
|
|
28843
|
+
return dispatchCustomEvent(listEl, "navi_list_request_nav_from_current", {
|
|
28429
28844
|
event,
|
|
28430
28845
|
goal
|
|
28431
28846
|
});
|
|
28432
28847
|
};
|
|
28433
|
-
const requestListSelectCurrent = (
|
|
28848
|
+
const requestListSelectCurrent = (listEl, {
|
|
28434
28849
|
event
|
|
28435
28850
|
}) => {
|
|
28436
|
-
return dispatchCustomEvent(
|
|
28851
|
+
return dispatchCustomEvent(listEl, "navi_list_request_select_current", {
|
|
28437
28852
|
event
|
|
28438
28853
|
});
|
|
28439
28854
|
};
|
|
28440
|
-
const requestListInteractionStateReset = (
|
|
28855
|
+
const requestListInteractionStateReset = (listEl, {
|
|
28441
28856
|
event
|
|
28442
28857
|
}) => {
|
|
28443
|
-
return dispatchCustomEvent(
|
|
28858
|
+
return dispatchCustomEvent(listEl, "navi_list_request_interaction_state_reset", {
|
|
28444
28859
|
event
|
|
28445
28860
|
});
|
|
28446
28861
|
};
|
|
28447
|
-
const requestListOpen = (
|
|
28862
|
+
const requestListOpen = (listEl, {
|
|
28448
28863
|
event,
|
|
28449
28864
|
anchor
|
|
28450
28865
|
}) => {
|
|
28451
|
-
return dispatchCustomEvent(
|
|
28866
|
+
return dispatchCustomEvent(listEl, "navi_list_request_open", {
|
|
28452
28867
|
event,
|
|
28453
28868
|
anchor
|
|
28454
28869
|
});
|
|
28455
28870
|
};
|
|
28456
|
-
const requestListClose = (
|
|
28871
|
+
const requestListClose = (listEl, {
|
|
28457
28872
|
event
|
|
28458
28873
|
}) => {
|
|
28459
|
-
return dispatchCustomEvent(
|
|
28874
|
+
return dispatchCustomEvent(listEl, "navi_list_request_close", {
|
|
28460
28875
|
event
|
|
28461
28876
|
});
|
|
28462
28877
|
};
|
|
@@ -28478,7 +28893,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
28478
28893
|
* - <InputCheckbox /> for type="checkbox"
|
|
28479
28894
|
* - <InputRadio /> for type="radio"
|
|
28480
28895
|
*/
|
|
28481
|
-
const css$
|
|
28896
|
+
const css$k = /* css */`
|
|
28482
28897
|
@layer navi {
|
|
28483
28898
|
.navi_input {
|
|
28484
28899
|
--border-radius: 2px;
|
|
@@ -28739,7 +29154,7 @@ const InputTextualDispatcher = props => {
|
|
|
28739
29154
|
};
|
|
28740
29155
|
const InputNativeContext = createContext(null);
|
|
28741
29156
|
const InputTextualUI = props => {
|
|
28742
|
-
import.meta.css = [css$
|
|
29157
|
+
import.meta.css = [css$k, "@jsenv/navi/src/field/input_textual.jsx"];
|
|
28743
29158
|
const {
|
|
28744
29159
|
ref,
|
|
28745
29160
|
type,
|
|
@@ -28993,18 +29408,16 @@ const InputControllingList = props => {
|
|
|
28993
29408
|
onKeyDown,
|
|
28994
29409
|
...rest
|
|
28995
29410
|
} = props;
|
|
28996
|
-
const
|
|
28997
|
-
|
|
28998
|
-
const listContainerEl = listEl.parentNode;
|
|
28999
|
-
return listContainerEl;
|
|
29411
|
+
const getListEl = () => {
|
|
29412
|
+
return document.getElementById(listId);
|
|
29000
29413
|
};
|
|
29001
29414
|
useEffect(() => {
|
|
29002
29415
|
const inputEl = ref.current;
|
|
29003
29416
|
if (!inputEl) {
|
|
29004
29417
|
return undefined;
|
|
29005
29418
|
}
|
|
29006
|
-
const
|
|
29007
|
-
if (!
|
|
29419
|
+
const listEl = getListEl();
|
|
29420
|
+
if (!listEl) {
|
|
29008
29421
|
return undefined;
|
|
29009
29422
|
}
|
|
29010
29423
|
const onListSelect = e => {
|
|
@@ -29020,48 +29433,48 @@ const InputControllingList = props => {
|
|
|
29020
29433
|
}
|
|
29021
29434
|
}
|
|
29022
29435
|
};
|
|
29023
|
-
|
|
29436
|
+
listEl.addEventListener("navi_list_select", onListSelect);
|
|
29024
29437
|
return () => {
|
|
29025
|
-
|
|
29438
|
+
listEl.removeEventListener("navi_list_select", onListSelect);
|
|
29026
29439
|
};
|
|
29027
29440
|
}, []);
|
|
29028
29441
|
const onKeyDownWithShortcuts = shortcutsViaOnKeyDown({
|
|
29029
29442
|
arrowdown: e => {
|
|
29030
|
-
const
|
|
29443
|
+
const listEl = getListEl();
|
|
29031
29444
|
e.stopPropagation(); // when within a list, prevent list from handling it twice
|
|
29032
|
-
return requestListNavFromCurrent(
|
|
29445
|
+
return requestListNavFromCurrent(listEl, {
|
|
29033
29446
|
event: e,
|
|
29034
29447
|
goal: "down"
|
|
29035
29448
|
});
|
|
29036
29449
|
},
|
|
29037
29450
|
arrowup: e => {
|
|
29038
|
-
const
|
|
29451
|
+
const listEl = getListEl();
|
|
29039
29452
|
e.stopPropagation(); // when within a list, prevent list from handling it twice
|
|
29040
|
-
return requestListNavFromCurrent(
|
|
29453
|
+
return requestListNavFromCurrent(listEl, {
|
|
29041
29454
|
event: e,
|
|
29042
29455
|
goal: "up"
|
|
29043
29456
|
});
|
|
29044
29457
|
},
|
|
29045
29458
|
home: e => {
|
|
29046
|
-
const
|
|
29459
|
+
const listEl = getListEl();
|
|
29047
29460
|
e.stopPropagation(); // when within a list, prevent list from handling it twice
|
|
29048
|
-
return requestListNavFromCurrent(
|
|
29461
|
+
return requestListNavFromCurrent(listEl, {
|
|
29049
29462
|
event: e,
|
|
29050
29463
|
goal: "first"
|
|
29051
29464
|
});
|
|
29052
29465
|
},
|
|
29053
29466
|
end: e => {
|
|
29054
|
-
const
|
|
29467
|
+
const listEl = getListEl();
|
|
29055
29468
|
e.stopPropagation(); // when within a list, prevent list from handling it twice
|
|
29056
|
-
return requestListNavFromCurrent(
|
|
29469
|
+
return requestListNavFromCurrent(listEl, {
|
|
29057
29470
|
event: e,
|
|
29058
29471
|
goal: "last"
|
|
29059
29472
|
});
|
|
29060
29473
|
},
|
|
29061
29474
|
enter: e => {
|
|
29062
|
-
const
|
|
29475
|
+
const listEl = getListEl();
|
|
29063
29476
|
e.stopPropagation(); // when within a list, prevent list from handling it twice
|
|
29064
|
-
return requestListSelectCurrent(
|
|
29477
|
+
return requestListSelectCurrent(listEl, {
|
|
29065
29478
|
event: e
|
|
29066
29479
|
});
|
|
29067
29480
|
},
|
|
@@ -29070,14 +29483,14 @@ const InputControllingList = props => {
|
|
|
29070
29483
|
// when the escape is meant to clear the search input (otherwise it would close the select too)
|
|
29071
29484
|
if (e.currentTarget.type === "search" && e.currentTarget.value !== "") {
|
|
29072
29485
|
e.stopPropagation();
|
|
29073
|
-
return
|
|
29486
|
+
return true;
|
|
29074
29487
|
}
|
|
29075
|
-
const
|
|
29488
|
+
const listEl = getListEl();
|
|
29076
29489
|
// here we allow propagation of escape up to the <select> to allow closing if within a select
|
|
29077
29490
|
// it also means list might catch escape and reset again but it's ok to reset twice here as it won't cause side effects
|
|
29078
29491
|
// (if we need the same pattern for other events where it could be problematic we would have to mark
|
|
29079
29492
|
// event as handled somehow to prevent list containing input to react to it)
|
|
29080
|
-
return requestListInteractionStateReset(
|
|
29493
|
+
return requestListInteractionStateReset(listEl, {
|
|
29081
29494
|
event: e
|
|
29082
29495
|
});
|
|
29083
29496
|
}
|
|
@@ -29119,18 +29532,16 @@ const InputTextualWithSuggestions = props => {
|
|
|
29119
29532
|
expandedRef.current = false;
|
|
29120
29533
|
setExpanded(false);
|
|
29121
29534
|
};
|
|
29122
|
-
const
|
|
29123
|
-
|
|
29124
|
-
const listContainerEl = listEl.parentNode;
|
|
29125
|
-
return listContainerEl;
|
|
29535
|
+
const getListEl = () => {
|
|
29536
|
+
return document.getElementById(suggestions);
|
|
29126
29537
|
};
|
|
29127
29538
|
const showSuggestions = e => {
|
|
29128
29539
|
if (expandedRef.current) {
|
|
29129
29540
|
return;
|
|
29130
29541
|
}
|
|
29131
|
-
const
|
|
29132
|
-
if (
|
|
29133
|
-
requestListOpen(
|
|
29542
|
+
const listEl = getListEl();
|
|
29543
|
+
if (listEl) {
|
|
29544
|
+
requestListOpen(listEl, {
|
|
29134
29545
|
event: e,
|
|
29135
29546
|
anchor: ref.current
|
|
29136
29547
|
});
|
|
@@ -29141,9 +29552,9 @@ const InputTextualWithSuggestions = props => {
|
|
|
29141
29552
|
if (!expandedRef.current) {
|
|
29142
29553
|
return;
|
|
29143
29554
|
}
|
|
29144
|
-
const
|
|
29145
|
-
if (
|
|
29146
|
-
requestListClose(
|
|
29555
|
+
const listEl = getListEl();
|
|
29556
|
+
if (listEl) {
|
|
29557
|
+
requestListClose(listEl, {
|
|
29147
29558
|
event: e
|
|
29148
29559
|
});
|
|
29149
29560
|
collapse();
|
|
@@ -29151,8 +29562,8 @@ const InputTextualWithSuggestions = props => {
|
|
|
29151
29562
|
};
|
|
29152
29563
|
useEffect(() => {
|
|
29153
29564
|
const inputEl = ref.current;
|
|
29154
|
-
const
|
|
29155
|
-
if (!
|
|
29565
|
+
const listEl = getListEl();
|
|
29566
|
+
if (!listEl) {
|
|
29156
29567
|
return undefined;
|
|
29157
29568
|
}
|
|
29158
29569
|
const onSelect = e => {
|
|
@@ -29168,9 +29579,9 @@ const InputTextualWithSuggestions = props => {
|
|
|
29168
29579
|
}));
|
|
29169
29580
|
hideSuggestions(e);
|
|
29170
29581
|
};
|
|
29171
|
-
|
|
29582
|
+
listEl.addEventListener("navi_list_select", onSelect);
|
|
29172
29583
|
return () => {
|
|
29173
|
-
|
|
29584
|
+
listEl.removeEventListener("navi_list_select", onSelect);
|
|
29174
29585
|
};
|
|
29175
29586
|
}, [suggestions]);
|
|
29176
29587
|
return jsx(ListIdContext.Provider, {
|
|
@@ -29382,7 +29793,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
29382
29793
|
* This means an editable thing MUST have a parent with position relative that wraps the content and the eventual editable input
|
|
29383
29794
|
*
|
|
29384
29795
|
*/
|
|
29385
|
-
const css$
|
|
29796
|
+
const css$j = /* css */`
|
|
29386
29797
|
.navi_editable_wrapper {
|
|
29387
29798
|
--inset-top: 0px;
|
|
29388
29799
|
--inset-right: 0px;
|
|
@@ -29431,7 +29842,7 @@ const useEditionController = () => {
|
|
|
29431
29842
|
};
|
|
29432
29843
|
};
|
|
29433
29844
|
const Editable = props => {
|
|
29434
|
-
import.meta.css = [css$
|
|
29845
|
+
import.meta.css = [css$j, "@jsenv/navi/src/field/edition/editable.jsx"];
|
|
29435
29846
|
let {
|
|
29436
29847
|
children,
|
|
29437
29848
|
action,
|
|
@@ -29700,7 +30111,7 @@ const Form = props => {
|
|
|
29700
30111
|
});
|
|
29701
30112
|
const uiState = useUIState(uiStateController);
|
|
29702
30113
|
const form = renderActionableComponent(props, {
|
|
29703
|
-
Basic:
|
|
30114
|
+
Basic: FormUI,
|
|
29704
30115
|
WithAction: FormWithAction
|
|
29705
30116
|
});
|
|
29706
30117
|
return jsx(UIStateControllerContext.Provider, {
|
|
@@ -29711,7 +30122,7 @@ const Form = props => {
|
|
|
29711
30122
|
})
|
|
29712
30123
|
});
|
|
29713
30124
|
};
|
|
29714
|
-
const
|
|
30125
|
+
const FormUI = props => {
|
|
29715
30126
|
const uiStateController = useContext(UIStateControllerContext);
|
|
29716
30127
|
const {
|
|
29717
30128
|
readOnly,
|
|
@@ -29819,7 +30230,7 @@ const FormWithAction = props => {
|
|
|
29819
30230
|
}
|
|
29820
30231
|
});
|
|
29821
30232
|
const innerLoading = loading || actionPending;
|
|
29822
|
-
return jsx(
|
|
30233
|
+
return jsx(FormUI, {
|
|
29823
30234
|
"data-action": actionBoundToUIState.name,
|
|
29824
30235
|
"data-method": action.meta?.httpVerb || method || "GET",
|
|
29825
30236
|
...rest,
|
|
@@ -29845,7 +30256,7 @@ const FormWithAction = props => {
|
|
|
29845
30256
|
// form.dispatchEvent(customEvent);
|
|
29846
30257
|
// };
|
|
29847
30258
|
|
|
29848
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
30259
|
+
installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
29849
30260
|
.navi_group {
|
|
29850
30261
|
--border-width: 1px;
|
|
29851
30262
|
|
|
@@ -29942,7 +30353,7 @@ const Group = ({
|
|
|
29942
30353
|
vertical = row,
|
|
29943
30354
|
...props
|
|
29944
30355
|
}) => {
|
|
29945
|
-
import.meta.css = [css$
|
|
30356
|
+
import.meta.css = [css$i, "@jsenv/navi/src/field/group.jsx"];
|
|
29946
30357
|
if (typeof borderWidth === "string") {
|
|
29947
30358
|
borderWidth = parseFloat(borderWidth);
|
|
29948
30359
|
}
|
|
@@ -30133,7 +30544,7 @@ const useCleanup = () => {
|
|
|
30133
30544
|
return cleanupMethods;
|
|
30134
30545
|
};
|
|
30135
30546
|
|
|
30136
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
30547
|
+
installImportMetaCssBuild(import.meta);const css$h = /* css */`
|
|
30137
30548
|
.navi_dialog {
|
|
30138
30549
|
&[open] {
|
|
30139
30550
|
display: flex;
|
|
@@ -30146,7 +30557,7 @@ installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
|
30146
30557
|
}
|
|
30147
30558
|
`;
|
|
30148
30559
|
const Dialog = props => {
|
|
30149
|
-
import.meta.css = [css$
|
|
30560
|
+
import.meta.css = [css$h, "@jsenv/navi/src/popup/dialog.jsx"];
|
|
30150
30561
|
const {
|
|
30151
30562
|
children,
|
|
30152
30563
|
scrollTrap,
|
|
@@ -30214,12 +30625,18 @@ const Dialog = props => {
|
|
|
30214
30625
|
ref: ref,
|
|
30215
30626
|
baseClassName: "navi_dialog",
|
|
30216
30627
|
onMouseDown: e => {
|
|
30628
|
+
rest.onMouseDown?.(e);
|
|
30217
30629
|
// The <dialog> element covers the full viewport; clicking the backdrop
|
|
30218
30630
|
// hits the dialog itself (not any child). Close when that happens.
|
|
30219
|
-
if (!pointerTrap && e.target === ref.current) {
|
|
30631
|
+
if (!pointerTrap && e.button === 0 && e.target === ref.current) {
|
|
30220
30632
|
onRequestClose(e);
|
|
30221
30633
|
}
|
|
30222
|
-
|
|
30634
|
+
},
|
|
30635
|
+
onCancel: e => {
|
|
30636
|
+
// The browser fires "cancel" (then closes the dialog) when the user presses Escape.
|
|
30637
|
+
// Prevent the native close so we control the close flow and dispatch navi_dialog_close.
|
|
30638
|
+
e.preventDefault();
|
|
30639
|
+
onRequestClose(e);
|
|
30223
30640
|
},
|
|
30224
30641
|
onnavi_dialog_request_open: e => {
|
|
30225
30642
|
const {
|
|
@@ -30251,7 +30668,7 @@ const requestDialogClose = (popoverElement, {
|
|
|
30251
30668
|
});
|
|
30252
30669
|
};
|
|
30253
30670
|
|
|
30254
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
30671
|
+
installImportMetaCssBuild(import.meta);const css$g = /* css */`
|
|
30255
30672
|
.navi_popover_backdrop {
|
|
30256
30673
|
position: fixed;
|
|
30257
30674
|
inset: 0;
|
|
@@ -30267,7 +30684,7 @@ installImportMetaCssBuild(import.meta);const css$h = /* css */`
|
|
|
30267
30684
|
}
|
|
30268
30685
|
`;
|
|
30269
30686
|
const Popover = props => {
|
|
30270
|
-
import.meta.css = [css$
|
|
30687
|
+
import.meta.css = [css$g, "@jsenv/navi/src/popup/popover.jsx"];
|
|
30271
30688
|
const {
|
|
30272
30689
|
scrollTrap,
|
|
30273
30690
|
pointerTrap,
|
|
@@ -30443,7 +30860,7 @@ const requestPopoverClose = (popoverElement, {
|
|
|
30443
30860
|
});
|
|
30444
30861
|
};
|
|
30445
30862
|
|
|
30446
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
30863
|
+
installImportMetaCssBuild(import.meta);const css$f = /* css */`
|
|
30447
30864
|
@layer navi {
|
|
30448
30865
|
.navi_select {
|
|
30449
30866
|
--select-border-radius: 2px;
|
|
@@ -30503,23 +30920,25 @@ installImportMetaCssBuild(import.meta);const css$g = /* css */`
|
|
|
30503
30920
|
outline-offset: calc(-1 * var(--select-outline-width));
|
|
30504
30921
|
user-select: none;
|
|
30505
30922
|
|
|
30506
|
-
|
|
30923
|
+
--x-select-outline-width-focus-visible: calc(
|
|
30924
|
+
var(--select-border-width) + var(--select-outline-width)
|
|
30925
|
+
);
|
|
30926
|
+
--x-select-outline-offset-focus-visible: calc(
|
|
30927
|
+
-1 * (var(--select-border-width) + var(--select-outline-width))
|
|
30928
|
+
);
|
|
30929
|
+
|
|
30930
|
+
&[data-hover] {
|
|
30507
30931
|
background-color: var(--select-background-color-hover);
|
|
30508
30932
|
outline-color: var(--select-border-color-hover);
|
|
30509
30933
|
}
|
|
30510
30934
|
|
|
30511
|
-
|
|
30512
|
-
|
|
30513
|
-
outline-
|
|
30514
|
-
|
|
30515
|
-
);
|
|
30516
|
-
outline-color: var(--navi-focus-outline-color, #005fcc);
|
|
30517
|
-
outline-offset: calc(
|
|
30518
|
-
-1 * (var(--select-border-width) + var(--select-outline-width))
|
|
30519
|
-
);
|
|
30935
|
+
&[data-focus-visible] {
|
|
30936
|
+
outline-width: var(--x-select-outline-width-focus-visible);
|
|
30937
|
+
outline-color: var(--navi-focus-outline-color);
|
|
30938
|
+
outline-offset: var(--x-select-outline-offset-focus-visible);
|
|
30520
30939
|
}
|
|
30521
30940
|
|
|
30522
|
-
|
|
30941
|
+
&[data-disabled] {
|
|
30523
30942
|
opacity: 0.5;
|
|
30524
30943
|
cursor: default;
|
|
30525
30944
|
}
|
|
@@ -30558,64 +30977,75 @@ installImportMetaCssBuild(import.meta);const css$g = /* css */`
|
|
|
30558
30977
|
opacity: 0.6;
|
|
30559
30978
|
}
|
|
30560
30979
|
|
|
30561
|
-
/*
|
|
30562
|
-
|
|
30563
|
-
|
|
30564
|
-
|
|
30565
|
-
|
|
30566
|
-
|
|
30567
|
-
|
|
30568
|
-
|
|
30569
|
-
|
|
30570
|
-
|
|
30571
|
-
-1 * (var(--select-border-width) + var(--select-outline-width))
|
|
30572
|
-
);
|
|
30573
|
-
}
|
|
30574
|
-
.navi_list_container:focus {
|
|
30575
|
-
outline: none;
|
|
30576
|
-
}
|
|
30980
|
+
/* popover */
|
|
30981
|
+
&[aria-haspopup="listbox"] {
|
|
30982
|
+
&:has(.navi_list_container[data-focus-visible]) {
|
|
30983
|
+
outline-width: var(--x-select-outline-width-focus-visible);
|
|
30984
|
+
outline-color: var(--navi-focus-outline-color);
|
|
30985
|
+
outline-offset: var(--x-select-outline-offset-focus-visible);
|
|
30986
|
+
.navi_list_container {
|
|
30987
|
+
outline: none;
|
|
30988
|
+
}
|
|
30989
|
+
}
|
|
30577
30990
|
|
|
30578
|
-
|
|
30579
|
-
|
|
30580
|
-
|
|
30581
|
-
|
|
30582
|
-
|
|
30583
|
-
|
|
30991
|
+
.navi_select_popover {
|
|
30992
|
+
position: absolute;
|
|
30993
|
+
inset: unset;
|
|
30994
|
+
min-width: var(--anchor-width, 0px);
|
|
30995
|
+
max-width: 95vw;
|
|
30996
|
+
max-height: 95dvh;
|
|
30997
|
+
margin: 0;
|
|
30998
|
+
padding: 0;
|
|
30999
|
+
background: white;
|
|
31000
|
+
border: none;
|
|
31001
|
+
border-radius: 0;
|
|
31002
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
|
|
31003
|
+
cursor: default; /* Reset pointer cursor within the select */
|
|
31004
|
+
overflow: auto;
|
|
31005
|
+
overscroll-behavior: none;
|
|
30584
31006
|
|
|
30585
|
-
|
|
30586
|
-
|
|
30587
|
-
|
|
30588
|
-
|
|
30589
|
-
|
|
30590
|
-
max-height: 95dvh;
|
|
30591
|
-
margin: 0;
|
|
30592
|
-
padding: 0;
|
|
30593
|
-
background: white;
|
|
30594
|
-
border: none;
|
|
30595
|
-
border-radius: 0;
|
|
30596
|
-
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
|
|
30597
|
-
cursor: default; /* Reset pointer cursor within the select */
|
|
30598
|
-
overflow: auto;
|
|
30599
|
-
overscroll-behavior: none;
|
|
31007
|
+
&:popover-open {
|
|
31008
|
+
display: flex;
|
|
31009
|
+
flex-direction: column;
|
|
31010
|
+
}
|
|
31011
|
+
}
|
|
30600
31012
|
}
|
|
30601
31013
|
|
|
30602
|
-
|
|
30603
|
-
|
|
30604
|
-
|
|
30605
|
-
|
|
30606
|
-
|
|
30607
|
-
border: none;
|
|
30608
|
-
border-radius: 8px;
|
|
30609
|
-
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
|
|
30610
|
-
cursor: default; /* Reset pointer cursor within the select */
|
|
31014
|
+
/* dialog */
|
|
31015
|
+
&[aria-haspopup="dialog"] {
|
|
31016
|
+
.navi_list_container {
|
|
31017
|
+
--list-max-height: none;
|
|
31018
|
+
}
|
|
30611
31019
|
|
|
30612
|
-
|
|
30613
|
-
|
|
30614
|
-
|
|
31020
|
+
/* When the list inside the dialog has keyboard focus, show the focus ring
|
|
31021
|
+
on the dialog instead */
|
|
31022
|
+
&:has(.navi_list_container[data-focus-visible]) {
|
|
31023
|
+
outline-width: var(--x-select-outline-width-focus-visible);
|
|
31024
|
+
outline-color: var(--navi-focus-outline-color);
|
|
31025
|
+
outline-offset: var(--x-select-outline-offset-focus-visible);
|
|
31026
|
+
.navi_list_container {
|
|
31027
|
+
outline: none;
|
|
31028
|
+
}
|
|
30615
31029
|
}
|
|
30616
31030
|
|
|
30617
|
-
|
|
30618
|
-
|
|
31031
|
+
.navi_select_dialog {
|
|
31032
|
+
max-height: 95dvh;
|
|
31033
|
+
margin: auto;
|
|
31034
|
+
padding: 0;
|
|
31035
|
+
background: white;
|
|
31036
|
+
border: none;
|
|
31037
|
+
border-radius: 8px;
|
|
31038
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
|
|
31039
|
+
cursor: default; /* Reset pointer cursor within the select */
|
|
31040
|
+
|
|
31041
|
+
&[open] {
|
|
31042
|
+
display: flex;
|
|
31043
|
+
flex-direction: column;
|
|
31044
|
+
}
|
|
31045
|
+
|
|
31046
|
+
&::backdrop {
|
|
31047
|
+
background: rgba(0, 0, 0, 0.4);
|
|
31048
|
+
}
|
|
30619
31049
|
}
|
|
30620
31050
|
}
|
|
30621
31051
|
}
|
|
@@ -30655,9 +31085,6 @@ const Select = props => {
|
|
|
30655
31085
|
},
|
|
30656
31086
|
emptyState: undefined
|
|
30657
31087
|
});
|
|
30658
|
-
uiStateController.onUIStateChange = (value, e) => {
|
|
30659
|
-
uiStateController.uiAction?.(value, e);
|
|
30660
|
-
};
|
|
30661
31088
|
const uiState = useUIState(uiStateController);
|
|
30662
31089
|
const value = Object.hasOwn(props, "value") ? props.value : uiState;
|
|
30663
31090
|
return jsx(ParentUIStateControllerContext.Provider, {
|
|
@@ -30689,10 +31116,8 @@ const SelectDispatcher = props => {
|
|
|
30689
31116
|
...props
|
|
30690
31117
|
});
|
|
30691
31118
|
};
|
|
30692
|
-
const SelectPlaceholderContext = createContext();
|
|
30693
|
-
const SelectValueContext = createContext(null);
|
|
30694
31119
|
const SelectUI = props => {
|
|
30695
|
-
import.meta.css = [css$
|
|
31120
|
+
import.meta.css = [css$f, "@jsenv/navi/src/field/select.jsx"];
|
|
30696
31121
|
let {
|
|
30697
31122
|
placeholder = "Select…",
|
|
30698
31123
|
trigger,
|
|
@@ -30713,7 +31138,8 @@ const SelectUI = props => {
|
|
|
30713
31138
|
const defaultRef = useRef();
|
|
30714
31139
|
const ref = rest.ref || defaultRef;
|
|
30715
31140
|
const hiddenInputId = useId();
|
|
30716
|
-
const
|
|
31141
|
+
const hiddenInputRef = useRef(null);
|
|
31142
|
+
const remainingProps = useConstraints(hiddenInputRef, rest);
|
|
30717
31143
|
const innerLoading = loading || contextLoading && contextLoadingElement === ref.current;
|
|
30718
31144
|
const innerReadOnly = readOnly || contextReadOnly || innerLoading;
|
|
30719
31145
|
const innerDisabled = disabled || contextDisabled;
|
|
@@ -30723,19 +31149,6 @@ const SelectUI = props => {
|
|
|
30723
31149
|
useAutoFocus(ref, autoFocus, {
|
|
30724
31150
|
preventScroll: autoFocusPreventScroll
|
|
30725
31151
|
});
|
|
30726
|
-
|
|
30727
|
-
// Re-run constraint validation when value changes (e.g. required constraint reads data-navi-value)
|
|
30728
|
-
useLayoutEffect(() => {
|
|
30729
|
-
const el = ref.current;
|
|
30730
|
-
if (!el) {
|
|
30731
|
-
return;
|
|
30732
|
-
}
|
|
30733
|
-
const validationInterface = el.__validationInterface__;
|
|
30734
|
-
if (!validationInterface) {
|
|
30735
|
-
return;
|
|
30736
|
-
}
|
|
30737
|
-
validationInterface.checkValidity();
|
|
30738
|
-
}, [value]);
|
|
30739
31152
|
if (trigger === undefined) {
|
|
30740
31153
|
trigger = jsx(SelectTrigger, {});
|
|
30741
31154
|
}
|
|
@@ -30748,7 +31161,6 @@ const SelectUI = props => {
|
|
|
30748
31161
|
,
|
|
30749
31162
|
|
|
30750
31163
|
"data-navi-value": value || undefined,
|
|
30751
|
-
"data-input-proxy": name ? `#${CSS.escape(hiddenInputId)}` : undefined,
|
|
30752
31164
|
styleCSSVars: SelectStyleCSSVars,
|
|
30753
31165
|
basePseudoState: {
|
|
30754
31166
|
...remainingProps.basePseudoState,
|
|
@@ -30763,6 +31175,7 @@ const SelectUI = props => {
|
|
|
30763
31175
|
color: "var(--loader-color)",
|
|
30764
31176
|
inset: -1
|
|
30765
31177
|
}), jsx("input", {
|
|
31178
|
+
ref: hiddenInputRef,
|
|
30766
31179
|
id: hiddenInputId,
|
|
30767
31180
|
type: "hidden",
|
|
30768
31181
|
name: name,
|
|
@@ -30778,6 +31191,8 @@ const SelectUI = props => {
|
|
|
30778
31191
|
})]
|
|
30779
31192
|
});
|
|
30780
31193
|
};
|
|
31194
|
+
const SelectPlaceholderContext = createContext();
|
|
31195
|
+
const SelectValueContext = createContext(null);
|
|
30781
31196
|
const SelectStyleCSSVars = {
|
|
30782
31197
|
"borderWidth": "--select-border-width",
|
|
30783
31198
|
"borderRadius": "--select-border-radius",
|
|
@@ -30875,7 +31290,7 @@ const SelectWithPopover = props => {
|
|
|
30875
31290
|
anchor: ref.current
|
|
30876
31291
|
});
|
|
30877
31292
|
};
|
|
30878
|
-
const requestClose = e => {
|
|
31293
|
+
const requestClose = (e = new CustomEvent("programmatic")) => {
|
|
30879
31294
|
return requestPopoverClose(popoverRef.current, {
|
|
30880
31295
|
event: e
|
|
30881
31296
|
});
|
|
@@ -30884,10 +31299,10 @@ const SelectWithPopover = props => {
|
|
|
30884
31299
|
const select = ref.current;
|
|
30885
31300
|
debugFocus(`moveFocusToSelect("${e.type}")`);
|
|
30886
31301
|
select.focus({
|
|
30887
|
-
preventScroll: true
|
|
30888
|
-
focusVisible: true
|
|
31302
|
+
preventScroll: true
|
|
30889
31303
|
});
|
|
30890
31304
|
};
|
|
31305
|
+
const [shouldIgnoreThatClick, disableClickFor] = useIgnoreClickForMousedown();
|
|
30891
31306
|
return jsx(SelectDispatcher, {
|
|
30892
31307
|
disabled: disabled,
|
|
30893
31308
|
"aria-haspopup": "listbox",
|
|
@@ -30913,6 +31328,9 @@ const SelectWithPopover = props => {
|
|
|
30913
31328
|
// click triggered by enter won't open the popover
|
|
30914
31329
|
return;
|
|
30915
31330
|
}
|
|
31331
|
+
if (shouldIgnoreThatClick) {
|
|
31332
|
+
return;
|
|
31333
|
+
}
|
|
30916
31334
|
// When a label is clicked it transfers focus to the select
|
|
30917
31335
|
// in that case we want to open it (otherwise we have already opened on mousedown interaction)
|
|
30918
31336
|
requestOpen(e);
|
|
@@ -30980,6 +31398,7 @@ const SelectWithPopover = props => {
|
|
|
30980
31398
|
}
|
|
30981
31399
|
// mousedown inside popover should not bubble to the select (would re-open it if that mousedown closes it)
|
|
30982
31400
|
e.stopPropagation();
|
|
31401
|
+
disableClickFor(e);
|
|
30983
31402
|
},
|
|
30984
31403
|
onnavi_popover_open: e => {
|
|
30985
31404
|
onOpen();
|
|
@@ -30997,7 +31416,10 @@ const SelectWithPopover = props => {
|
|
|
30997
31416
|
scrollTrap: scrollTrap,
|
|
30998
31417
|
pointerTrap: pointerTrap,
|
|
30999
31418
|
focusTrap: focusTrap,
|
|
31000
|
-
children:
|
|
31419
|
+
children: jsx(SelectRequestCloseContext.Provider, {
|
|
31420
|
+
value: requestClose,
|
|
31421
|
+
children: children
|
|
31422
|
+
})
|
|
31001
31423
|
})
|
|
31002
31424
|
});
|
|
31003
31425
|
};
|
|
@@ -31031,7 +31453,7 @@ const SelectWithDialog = props => {
|
|
|
31031
31453
|
event: e
|
|
31032
31454
|
});
|
|
31033
31455
|
};
|
|
31034
|
-
const requestClose = e => {
|
|
31456
|
+
const requestClose = (e = new CustomEvent("programmatic")) => {
|
|
31035
31457
|
return requestDialogClose(dialogRef.current, {
|
|
31036
31458
|
event: e
|
|
31037
31459
|
});
|
|
@@ -31039,10 +31461,10 @@ const SelectWithDialog = props => {
|
|
|
31039
31461
|
const moveFocusToSelect = e => {
|
|
31040
31462
|
debugFocus(`moveFocusToSelect("${e.type}")`);
|
|
31041
31463
|
ref.current.focus({
|
|
31042
|
-
preventScroll: true
|
|
31043
|
-
focusVisible: true
|
|
31464
|
+
preventScroll: true
|
|
31044
31465
|
});
|
|
31045
31466
|
};
|
|
31467
|
+
const [shouldIgnoreThatClick, disableClickFor] = useIgnoreClickForMousedown();
|
|
31046
31468
|
return jsx(SelectDispatcher, {
|
|
31047
31469
|
disabled: disabled,
|
|
31048
31470
|
"aria-haspopup": "dialog",
|
|
@@ -31068,6 +31490,11 @@ const SelectWithDialog = props => {
|
|
|
31068
31490
|
// click triggered by enter won't open the dialog
|
|
31069
31491
|
return;
|
|
31070
31492
|
}
|
|
31493
|
+
if (shouldIgnoreThatClick) {
|
|
31494
|
+
// mousedown on the select already handled open/close; ignore this click
|
|
31495
|
+
// to avoid toggling the dialog again on mouseup
|
|
31496
|
+
return;
|
|
31497
|
+
}
|
|
31071
31498
|
// When a label is clicked it transfers focus to the select, in that case we want to open it
|
|
31072
31499
|
requestOpen(e);
|
|
31073
31500
|
},
|
|
@@ -31123,13 +31550,74 @@ const SelectWithDialog = props => {
|
|
|
31123
31550
|
}
|
|
31124
31551
|
// mousedown inside dialog should not bubble to the select (would re-open it if that mousedown closes it)
|
|
31125
31552
|
e.stopPropagation();
|
|
31553
|
+
disableClickFor(e);
|
|
31126
31554
|
},
|
|
31127
31555
|
scrollTrap: scrollTrap,
|
|
31128
31556
|
pointerTrap: pointerTrap,
|
|
31129
|
-
children:
|
|
31557
|
+
children: jsx(SelectRequestCloseContext.Provider, {
|
|
31558
|
+
value: requestClose,
|
|
31559
|
+
children: children
|
|
31560
|
+
})
|
|
31130
31561
|
})
|
|
31131
31562
|
});
|
|
31132
31563
|
};
|
|
31564
|
+
const SelectRequestCloseContext = createContext();
|
|
31565
|
+
const useSelectRequestClose = () => {
|
|
31566
|
+
return useContext(SelectRequestCloseContext);
|
|
31567
|
+
};
|
|
31568
|
+
|
|
31569
|
+
/**
|
|
31570
|
+
* Hook to prevent a `click` event from firing after a `mousedown` that already
|
|
31571
|
+
* handled an open/close action.
|
|
31572
|
+
*
|
|
31573
|
+
* Problem: when the user clicks a dialog's backdrop to close it, the browser
|
|
31574
|
+
* fires `mousedown` on the backdrop (which closes the dialog), then fires
|
|
31575
|
+
* `click` on whatever element is underneath once the dialog is gone. If that
|
|
31576
|
+
* element is the trigger button that originally opened the dialog, the `click`
|
|
31577
|
+
* would immediately re-open it.
|
|
31578
|
+
*
|
|
31579
|
+
* This problem only occurs when the dialog is closed on `mousedown`. If the
|
|
31580
|
+
* dialog were closed on `click` instead, the dialog would still be open when
|
|
31581
|
+
* the `click` fires on the backdrop, so the trigger button underneath would
|
|
31582
|
+
* never receive that `click`.
|
|
31583
|
+
*
|
|
31584
|
+
* Calling `stopPropagation()` or `preventDefault()` on the backdrop `mousedown`
|
|
31585
|
+
* does not help: the browser dispatches the subsequent `click` regardless,
|
|
31586
|
+
* targeting whichever element ends up under the pointer after the dialog closes.
|
|
31587
|
+
*
|
|
31588
|
+
* Usage:
|
|
31589
|
+
* const [shouldIgnoreThatClick, disableClickFor] = useIgnoreClickForMousedown();
|
|
31590
|
+
* // In onMouseDown (e.g. on the dialog backdrop): disableClickFor(e)
|
|
31591
|
+
* // In onClick (on the trigger): if (shouldIgnoreThatClick) return;
|
|
31592
|
+
*
|
|
31593
|
+
* `disableClickFor` arms the guard until the next `mouseup` on the document
|
|
31594
|
+
* (with a 1 s safety-net fallback), using `requestAnimationFrame` so the
|
|
31595
|
+
* `click` event — which fires synchronously after `mouseup` — is still blocked.
|
|
31596
|
+
*/
|
|
31597
|
+
const useIgnoreClickForMousedown = () => {
|
|
31598
|
+
const pendingMousedownRef = useRef(false);
|
|
31599
|
+
const shouldIgnore = pendingMousedownRef.current;
|
|
31600
|
+
const disableClickFor = () => {
|
|
31601
|
+
pendingMousedownRef.current = true;
|
|
31602
|
+
const restoreClick = () => {
|
|
31603
|
+
clearTimeout(safetyTimeout);
|
|
31604
|
+
pendingMousedownRef.current = false;
|
|
31605
|
+
};
|
|
31606
|
+
const safetyTimeout = setTimeout(() => {
|
|
31607
|
+
pendingMousedownRef.current = false;
|
|
31608
|
+
restoreClick();
|
|
31609
|
+
}, 1000);
|
|
31610
|
+
document.addEventListener("mouseup", () => {
|
|
31611
|
+
requestAnimationFrame(() => {
|
|
31612
|
+
restoreClick();
|
|
31613
|
+
});
|
|
31614
|
+
}, {
|
|
31615
|
+
once: true,
|
|
31616
|
+
capture: true
|
|
31617
|
+
});
|
|
31618
|
+
};
|
|
31619
|
+
return [shouldIgnore, disableClickFor];
|
|
31620
|
+
};
|
|
31133
31621
|
|
|
31134
31622
|
/**
|
|
31135
31623
|
* applySearch — matches value against searchText.
|
|
@@ -31369,207 +31857,6 @@ const createSearch = (fields) => {
|
|
|
31369
31857
|
};
|
|
31370
31858
|
};
|
|
31371
31859
|
|
|
31372
|
-
installImportMetaCssBuild(import.meta);const css$f = /* css */`
|
|
31373
|
-
@layer navi {
|
|
31374
|
-
.navi_dropdown_trigger {
|
|
31375
|
-
--border-radius: 2px;
|
|
31376
|
-
--border-width: 1px;
|
|
31377
|
-
--outline-width: 1px;
|
|
31378
|
-
--font-size: 14px;
|
|
31379
|
-
--padding: 5px 8px;
|
|
31380
|
-
--border-color: light-dark(#767676, #8e8e93);
|
|
31381
|
-
--background-color: white;
|
|
31382
|
-
--color: currentColor;
|
|
31383
|
-
--placeholder-color: color-mix(in srgb, currentColor 60%, transparent);
|
|
31384
|
-
--border-color-hover: color-mix(in srgb, var(--border-color) 70%, black);
|
|
31385
|
-
--background-color-hover: color-mix(
|
|
31386
|
-
in srgb,
|
|
31387
|
-
var(--background-color) 95%,
|
|
31388
|
-
black
|
|
31389
|
-
);
|
|
31390
|
-
}
|
|
31391
|
-
}
|
|
31392
|
-
|
|
31393
|
-
.navi_dropdown_trigger {
|
|
31394
|
-
display: inline-flex;
|
|
31395
|
-
box-sizing: border-box;
|
|
31396
|
-
padding: var(--padding);
|
|
31397
|
-
align-items: center;
|
|
31398
|
-
gap: 6px;
|
|
31399
|
-
color: var(--color);
|
|
31400
|
-
font-size: var(--font-size);
|
|
31401
|
-
text-align: left;
|
|
31402
|
-
background-color: var(--background-color);
|
|
31403
|
-
border: var(--border-width) solid transparent;
|
|
31404
|
-
border-radius: var(--border-radius);
|
|
31405
|
-
outline: var(--outline-width) solid var(--border-color);
|
|
31406
|
-
outline-offset: calc(-1 * var(--outline-width));
|
|
31407
|
-
cursor: pointer;
|
|
31408
|
-
user-select: none;
|
|
31409
|
-
|
|
31410
|
-
&:hover {
|
|
31411
|
-
background-color: var(--background-color-hover);
|
|
31412
|
-
outline-color: var(--border-color-hover);
|
|
31413
|
-
}
|
|
31414
|
-
|
|
31415
|
-
&:focus-visible {
|
|
31416
|
-
outline-width: calc(var(--border-width) + var(--outline-width));
|
|
31417
|
-
outline-color: var(--navi-focus-outline-color, #005fcc);
|
|
31418
|
-
outline-offset: calc(-1 * (var(--border-width) + var(--outline-width)));
|
|
31419
|
-
}
|
|
31420
|
-
}
|
|
31421
|
-
|
|
31422
|
-
.navi_dropdown_trigger_label {
|
|
31423
|
-
min-width: 0;
|
|
31424
|
-
flex: 1;
|
|
31425
|
-
text-overflow: ellipsis;
|
|
31426
|
-
white-space: nowrap;
|
|
31427
|
-
overflow: hidden;
|
|
31428
|
-
}
|
|
31429
|
-
|
|
31430
|
-
.navi_dropdown_trigger_label[data-placeholder] {
|
|
31431
|
-
color: var(--placeholder-color);
|
|
31432
|
-
}
|
|
31433
|
-
|
|
31434
|
-
.navi_dropdown_trigger_icon {
|
|
31435
|
-
flex-shrink: 0;
|
|
31436
|
-
opacity: 0.6;
|
|
31437
|
-
}
|
|
31438
|
-
|
|
31439
|
-
.navi_dropdown_dialog {
|
|
31440
|
-
max-height: 95dvh;
|
|
31441
|
-
margin: auto;
|
|
31442
|
-
padding: 0;
|
|
31443
|
-
background: white;
|
|
31444
|
-
border: none;
|
|
31445
|
-
border-radius: 8px;
|
|
31446
|
-
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
|
|
31447
|
-
|
|
31448
|
-
&[open] {
|
|
31449
|
-
display: flex;
|
|
31450
|
-
flex-direction: column;
|
|
31451
|
-
}
|
|
31452
|
-
|
|
31453
|
-
&::backdrop {
|
|
31454
|
-
background: rgba(0, 0, 0, 0.4);
|
|
31455
|
-
}
|
|
31456
|
-
|
|
31457
|
-
/* When the suggestion list inside the dialog has keyboard focus, show the
|
|
31458
|
-
focus ring on the dialog itself and suppress it on the list container.
|
|
31459
|
-
It's visually better */
|
|
31460
|
-
&:has(.navi_list_container:focus-visible) {
|
|
31461
|
-
outline: 1px solid var(--navi-focus-outline-color, #005fcc);
|
|
31462
|
-
outline-offset: 1px;
|
|
31463
|
-
}
|
|
31464
|
-
& .navi_list_container:focus-visible {
|
|
31465
|
-
outline: none;
|
|
31466
|
-
}
|
|
31467
|
-
}
|
|
31468
|
-
`;
|
|
31469
|
-
const DropdownCloseContext = createContext(null);
|
|
31470
|
-
|
|
31471
|
-
/**
|
|
31472
|
-
* Dropdown — a select-like trigger that opens a centered dialog.
|
|
31473
|
-
*
|
|
31474
|
-
* Props:
|
|
31475
|
-
* value — the currently selected value (displayed in the trigger)
|
|
31476
|
-
* placeholder — text shown when value is null/undefined/empty
|
|
31477
|
-
* disabled — disable the trigger
|
|
31478
|
-
* capturePointer — when true, clicking the backdrop does NOT close the dialog
|
|
31479
|
-
* onOpen — called when the dialog opens
|
|
31480
|
-
* onClose — called when the dialog closes
|
|
31481
|
-
* children — content rendered inside the dialog
|
|
31482
|
-
* ...rest — forwarded to the trigger <button>
|
|
31483
|
-
*/
|
|
31484
|
-
const Dropdown = ({
|
|
31485
|
-
value,
|
|
31486
|
-
placeholder = "Select…",
|
|
31487
|
-
disabled,
|
|
31488
|
-
capturePointer,
|
|
31489
|
-
captureScroll,
|
|
31490
|
-
onOpen,
|
|
31491
|
-
onClose,
|
|
31492
|
-
children,
|
|
31493
|
-
...rest
|
|
31494
|
-
}) => {
|
|
31495
|
-
import.meta.css = [css$f, "@jsenv/navi/src/field/list/dropdown.jsx"];
|
|
31496
|
-
const dialogRef = useRef(null);
|
|
31497
|
-
const [open, setOpen] = useState(false);
|
|
31498
|
-
const scrollTrapCleanupRef = useRef(null);
|
|
31499
|
-
const openDialog = () => {
|
|
31500
|
-
if (disabled) {
|
|
31501
|
-
return;
|
|
31502
|
-
}
|
|
31503
|
-
const dialog = dialogRef.current;
|
|
31504
|
-
if (!dialog || open) {
|
|
31505
|
-
return;
|
|
31506
|
-
}
|
|
31507
|
-
dialog.showModal();
|
|
31508
|
-
if (captureScroll) {
|
|
31509
|
-
scrollTrapCleanupRef.current = trapScrollInside(dialog);
|
|
31510
|
-
}
|
|
31511
|
-
setOpen(true);
|
|
31512
|
-
onOpen?.();
|
|
31513
|
-
};
|
|
31514
|
-
const closeDialog = () => {
|
|
31515
|
-
const dialog = dialogRef.current;
|
|
31516
|
-
if (!dialog || !open) {
|
|
31517
|
-
return;
|
|
31518
|
-
}
|
|
31519
|
-
dialog.close();
|
|
31520
|
-
setOpen(false);
|
|
31521
|
-
if (captureScroll && scrollTrapCleanupRef.current) {
|
|
31522
|
-
scrollTrapCleanupRef.current();
|
|
31523
|
-
scrollTrapCleanupRef.current = null;
|
|
31524
|
-
}
|
|
31525
|
-
onClose?.();
|
|
31526
|
-
};
|
|
31527
|
-
const onDialogClick = e => {
|
|
31528
|
-
// The <dialog> element itself is the backdrop area. Clicking directly on it
|
|
31529
|
-
// (not on its content child) closes the dialog — unless capturePointer is set.
|
|
31530
|
-
if (!capturePointer && e.target === dialogRef.current) {
|
|
31531
|
-
closeDialog();
|
|
31532
|
-
}
|
|
31533
|
-
};
|
|
31534
|
-
const hasValue = value !== null && value !== undefined && value !== "";
|
|
31535
|
-
return jsxs(Fragment, {
|
|
31536
|
-
children: [jsxs(Box, {
|
|
31537
|
-
as: "button",
|
|
31538
|
-
type: "button",
|
|
31539
|
-
baseClassName: "navi_dropdown_trigger",
|
|
31540
|
-
disabled: disabled,
|
|
31541
|
-
onClick: openDialog,
|
|
31542
|
-
...rest,
|
|
31543
|
-
children: [jsx("span", {
|
|
31544
|
-
className: "navi_dropdown_trigger_label",
|
|
31545
|
-
"data-placeholder": hasValue ? undefined : "",
|
|
31546
|
-
children: hasValue ? String(value) : placeholder
|
|
31547
|
-
}), jsx("span", {
|
|
31548
|
-
className: "navi_dropdown_trigger_icon",
|
|
31549
|
-
children: jsx(Icon, {
|
|
31550
|
-
children: jsx(ChevronDownSvg, {})
|
|
31551
|
-
})
|
|
31552
|
-
})]
|
|
31553
|
-
}), jsx("dialog", {
|
|
31554
|
-
ref: dialogRef,
|
|
31555
|
-
className: "navi_dropdown_dialog",
|
|
31556
|
-
onClick: onDialogClick,
|
|
31557
|
-
onClose: closeDialog,
|
|
31558
|
-
children: jsx(DropdownCloseContext.Provider, {
|
|
31559
|
-
value: closeDialog,
|
|
31560
|
-
children: children
|
|
31561
|
-
})
|
|
31562
|
-
})]
|
|
31563
|
-
});
|
|
31564
|
-
};
|
|
31565
|
-
|
|
31566
|
-
/**
|
|
31567
|
-
* Hook to close the enclosing Dropdown dialog from inside its content.
|
|
31568
|
-
*/
|
|
31569
|
-
const useDropdownClose = () => {
|
|
31570
|
-
return useContext(DropdownCloseContext);
|
|
31571
|
-
};
|
|
31572
|
-
|
|
31573
31860
|
/**
|
|
31574
31861
|
* useSearch — reorders items so matched ones come first (sorted by score desc),
|
|
31575
31862
|
* followed by non-matched items in their natural order. No item is hidden.
|
|
@@ -35422,7 +35709,7 @@ installImportMetaCssBuild(import.meta);const css$7 = /* css */`
|
|
|
35422
35709
|
--font-size: 0.7em;
|
|
35423
35710
|
--x-background: var(--background);
|
|
35424
35711
|
--x-background-color: var(--background-color, var(--x-background));
|
|
35425
|
-
--x-color-contrasting: var(--navi-color-
|
|
35712
|
+
--x-color-contrasting: var(--navi-color-white);
|
|
35426
35713
|
--x-color: var(--color, var(--x-color-contrasting));
|
|
35427
35714
|
--padding-x: 0.8em;
|
|
35428
35715
|
--padding-y: 0.4em;
|
|
@@ -35439,22 +35726,11 @@ installImportMetaCssBuild(import.meta);const css$7 = /* css */`
|
|
|
35439
35726
|
background-color: var(--x-background-color);
|
|
35440
35727
|
border-radius: 1em;
|
|
35441
35728
|
|
|
35442
|
-
&[data-dark-
|
|
35443
|
-
--x-color-contrasting: var(--navi-color-
|
|
35729
|
+
&[data-accent-needs-dark-fg] {
|
|
35730
|
+
--x-color-contrasting: var(--navi-color-black);
|
|
35444
35731
|
}
|
|
35445
35732
|
}
|
|
35446
35733
|
`;
|
|
35447
|
-
const BadgeStyleCSSVars$1 = {
|
|
35448
|
-
borderWidth: "--border-width",
|
|
35449
|
-
borderRadius: "--border-radius",
|
|
35450
|
-
paddingRight: "--padding-right",
|
|
35451
|
-
paddingLeft: "--padding-left",
|
|
35452
|
-
backgroundColor: "--background-color",
|
|
35453
|
-
background: "--background",
|
|
35454
|
-
borderColor: "--border-color",
|
|
35455
|
-
color: "--color",
|
|
35456
|
-
fontSize: "--font-size"
|
|
35457
|
-
};
|
|
35458
35734
|
const Badge = ({
|
|
35459
35735
|
children,
|
|
35460
35736
|
className,
|
|
@@ -35463,7 +35739,7 @@ const Badge = ({
|
|
|
35463
35739
|
import.meta.css = [css$7, "@jsenv/navi/src/text/badge.jsx"];
|
|
35464
35740
|
const defaultRef = useRef();
|
|
35465
35741
|
const ref = props.ref || defaultRef;
|
|
35466
|
-
|
|
35742
|
+
useAccentColorAttributes(ref, null);
|
|
35467
35743
|
return jsx(Text, {
|
|
35468
35744
|
ref: ref,
|
|
35469
35745
|
className: withPropsClassName("navi_badge", className),
|
|
@@ -35473,6 +35749,17 @@ const Badge = ({
|
|
|
35473
35749
|
children: children
|
|
35474
35750
|
});
|
|
35475
35751
|
};
|
|
35752
|
+
const BadgeStyleCSSVars$1 = {
|
|
35753
|
+
borderWidth: "--border-width",
|
|
35754
|
+
borderRadius: "--border-radius",
|
|
35755
|
+
paddingRight: "--padding-right",
|
|
35756
|
+
paddingLeft: "--padding-left",
|
|
35757
|
+
backgroundColor: "--background-color",
|
|
35758
|
+
background: "--background",
|
|
35759
|
+
borderColor: "--border-color",
|
|
35760
|
+
color: "--color",
|
|
35761
|
+
fontSize: "--font-size"
|
|
35762
|
+
};
|
|
35476
35763
|
|
|
35477
35764
|
const LoadingDots = () => {
|
|
35478
35765
|
return jsxs("svg", {
|
|
@@ -35548,7 +35835,7 @@ installImportMetaCssBuild(import.meta);const css$6 = /* css */`
|
|
|
35548
35835
|
--font-size: 0.7em;
|
|
35549
35836
|
--x-background: var(--background);
|
|
35550
35837
|
--x-background-color: var(--background-color, var(--x-background));
|
|
35551
|
-
--x-color-contrasting: var(--navi-color-
|
|
35838
|
+
--x-color-contrasting: var(--navi-color-white);
|
|
35552
35839
|
--x-color: var(--color, var(--x-color-contrasting));
|
|
35553
35840
|
--padding-x: 0.5em;
|
|
35554
35841
|
--padding-y: 0.2em;
|
|
@@ -35557,8 +35844,8 @@ installImportMetaCssBuild(import.meta);const css$6 = /* css */`
|
|
|
35557
35844
|
font-size: var(--font-size);
|
|
35558
35845
|
vertical-align: inherit;
|
|
35559
35846
|
|
|
35560
|
-
&[data-dark-
|
|
35561
|
-
--x-color-contrasting: var(--navi-color-
|
|
35847
|
+
&[data-accent-needs-dark-fg] {
|
|
35848
|
+
--x-color-contrasting: var(--navi-color-black);
|
|
35562
35849
|
}
|
|
35563
35850
|
|
|
35564
35851
|
&[data-loading] {
|
|
@@ -35632,17 +35919,6 @@ installImportMetaCssBuild(import.meta);const css$6 = /* css */`
|
|
|
35632
35919
|
}
|
|
35633
35920
|
}
|
|
35634
35921
|
`;
|
|
35635
|
-
const BadgeStyleCSSVars = {
|
|
35636
|
-
borderWidth: "--border-width",
|
|
35637
|
-
borderRadius: "--border-radius",
|
|
35638
|
-
paddingRight: "--padding-right",
|
|
35639
|
-
paddingLeft: "--padding-left",
|
|
35640
|
-
backgroundColor: "--background-color",
|
|
35641
|
-
background: "--background",
|
|
35642
|
-
borderColor: "--border-color",
|
|
35643
|
-
color: "--color",
|
|
35644
|
-
fontSize: "--font-size"
|
|
35645
|
-
};
|
|
35646
35922
|
const BadgeCountOverflow = () => jsx("span", {
|
|
35647
35923
|
className: "navi_count_badge_overflow",
|
|
35648
35924
|
children: "+"
|
|
@@ -35666,7 +35942,7 @@ const BadgeCount = ({
|
|
|
35666
35942
|
import.meta.css = [css$6, "@jsenv/navi/src/text/badge_count.jsx"];
|
|
35667
35943
|
const defaultRef = useRef();
|
|
35668
35944
|
const ref = props.ref || defaultRef;
|
|
35669
|
-
|
|
35945
|
+
useAccentColorAttributes(ref, null);
|
|
35670
35946
|
let valueRequested = (() => {
|
|
35671
35947
|
if (typeof children !== "string") return children;
|
|
35672
35948
|
const parsed = Number(children);
|
|
@@ -35718,6 +35994,17 @@ const BadgeCount = ({
|
|
|
35718
35994
|
})
|
|
35719
35995
|
});
|
|
35720
35996
|
};
|
|
35997
|
+
const BadgeStyleCSSVars = {
|
|
35998
|
+
borderWidth: "--border-width",
|
|
35999
|
+
borderRadius: "--border-radius",
|
|
36000
|
+
paddingRight: "--padding-right",
|
|
36001
|
+
paddingLeft: "--padding-left",
|
|
36002
|
+
backgroundColor: "--background-color",
|
|
36003
|
+
background: "--background",
|
|
36004
|
+
borderColor: "--border-color",
|
|
36005
|
+
color: "--color",
|
|
36006
|
+
fontSize: "--font-size"
|
|
36007
|
+
};
|
|
35721
36008
|
const applyMaxToValue = (max, value) => {
|
|
35722
36009
|
if (isNaN(value)) {
|
|
35723
36010
|
return value;
|
|
@@ -35791,7 +36078,7 @@ const BadgeCountCircle = ({
|
|
|
35791
36078
|
});
|
|
35792
36079
|
};
|
|
35793
36080
|
|
|
35794
|
-
installImportMetaCssBuild(import.meta);
|
|
36081
|
+
installImportMetaCssBuild(import.meta);const css$5 = /* css */`
|
|
35795
36082
|
@layer navi {
|
|
35796
36083
|
.navi_caption {
|
|
35797
36084
|
--color: #6b7280;
|
|
@@ -35807,14 +36094,12 @@ installImportMetaCssBuild(import.meta);import.meta.css = [/* css */`
|
|
|
35807
36094
|
.navi_caption {
|
|
35808
36095
|
color: var(--color);
|
|
35809
36096
|
}
|
|
35810
|
-
|
|
35811
|
-
const CaptionStyleCSSVars = {
|
|
35812
|
-
color: "--color"
|
|
35813
|
-
};
|
|
36097
|
+
`;
|
|
35814
36098
|
const Caption = ({
|
|
35815
36099
|
className,
|
|
35816
36100
|
...rest
|
|
35817
36101
|
}) => {
|
|
36102
|
+
import.meta.css = [css$5, "@jsenv/navi/src/text/caption.jsx"];
|
|
35818
36103
|
return jsx(Text, {
|
|
35819
36104
|
as: "small",
|
|
35820
36105
|
size: "0.8em" // We use em to be relative to the parent (we want to be smaller than the surrounding text)
|
|
@@ -35825,6 +36110,9 @@ const Caption = ({
|
|
|
35825
36110
|
styleCSSVars: CaptionStyleCSSVars
|
|
35826
36111
|
});
|
|
35827
36112
|
};
|
|
36113
|
+
const CaptionStyleCSSVars = {
|
|
36114
|
+
color: "--color"
|
|
36115
|
+
};
|
|
35828
36116
|
|
|
35829
36117
|
/**
|
|
35830
36118
|
* Example of how you'd use this:
|
|
@@ -36194,7 +36482,7 @@ const interpolate = (template, values) => {
|
|
|
36194
36482
|
});
|
|
36195
36483
|
};
|
|
36196
36484
|
|
|
36197
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
36485
|
+
installImportMetaCssBuild(import.meta);const css$4 = /* css */`
|
|
36198
36486
|
@layer navi {
|
|
36199
36487
|
.navi_quantity {
|
|
36200
36488
|
--unit-color: color-mix(in srgb, currentColor 50%, white);
|
|
@@ -36285,11 +36573,6 @@ QuantityIntl.addUnit = (unitName, langTranslations) => {
|
|
|
36285
36573
|
});
|
|
36286
36574
|
}
|
|
36287
36575
|
};
|
|
36288
|
-
const QuantityPropsCSSVars = {
|
|
36289
|
-
unitColor: "--unit-color",
|
|
36290
|
-
unitSizeRatio: "--unit-size-ratio"
|
|
36291
|
-
};
|
|
36292
|
-
const QuantityPseudoClasses = [":hover", ":active", ":read-only", ":disabled", ":-navi-loading"];
|
|
36293
36576
|
const Quantity = ({
|
|
36294
36577
|
children,
|
|
36295
36578
|
unit,
|
|
@@ -36304,7 +36587,7 @@ const Quantity = ({
|
|
|
36304
36587
|
bold = true,
|
|
36305
36588
|
...props
|
|
36306
36589
|
}) => {
|
|
36307
|
-
import.meta.css = [css$
|
|
36590
|
+
import.meta.css = [css$4, "@jsenv/navi/src/text/quantity.jsx"];
|
|
36308
36591
|
const value = parseQuantityValue(children);
|
|
36309
36592
|
const valueRounded = integer && typeof value === "number" ? Math.round(value) : value;
|
|
36310
36593
|
const valueFormatted = typeof valueRounded === "number" ? formatNumber(valueRounded, {
|
|
@@ -36346,6 +36629,11 @@ const Quantity = ({
|
|
|
36346
36629
|
});
|
|
36347
36630
|
};
|
|
36348
36631
|
Quantity.Intl = QuantityIntl;
|
|
36632
|
+
const QuantityPropsCSSVars = {
|
|
36633
|
+
unitColor: "--unit-color",
|
|
36634
|
+
unitSizeRatio: "--unit-size-ratio"
|
|
36635
|
+
};
|
|
36636
|
+
const QuantityPseudoClasses = [":hover", ":active", ":read-only", ":disabled", ":-navi-loading"];
|
|
36349
36637
|
const Unit = ({
|
|
36350
36638
|
value,
|
|
36351
36639
|
unit,
|
|
@@ -36388,7 +36676,7 @@ const parseQuantityValue = children => {
|
|
|
36388
36676
|
return Number.isNaN(parsed) ? children : parsed;
|
|
36389
36677
|
};
|
|
36390
36678
|
|
|
36391
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
36679
|
+
installImportMetaCssBuild(import.meta);const css$3 = /* css */`
|
|
36392
36680
|
@layer navi {
|
|
36393
36681
|
.navi_meter {
|
|
36394
36682
|
--loader-color: var(--navi-loader-color);
|
|
@@ -36404,13 +36692,9 @@ installImportMetaCssBuild(import.meta);const css$4 = /* css */`
|
|
|
36404
36692
|
--fill-color-suboptimum: light-dark(#fdb900, #ffc107);
|
|
36405
36693
|
--fill-color-even-less-good: light-dark(#d83b01, #f44336);
|
|
36406
36694
|
|
|
36407
|
-
--x-color:
|
|
36408
|
-
--x-shadow-color:
|
|
36695
|
+
--x-color: white;
|
|
36696
|
+
--x-shadow-color: black;
|
|
36409
36697
|
--shadow-size: 0.5em;
|
|
36410
|
-
&[data-dark-background] {
|
|
36411
|
-
--x-color: white;
|
|
36412
|
-
--x-shadow-color: black;
|
|
36413
|
-
}
|
|
36414
36698
|
}
|
|
36415
36699
|
}
|
|
36416
36700
|
|
|
@@ -36471,6 +36755,11 @@ installImportMetaCssBuild(import.meta);const css$4 = /* css */`
|
|
|
36471
36755
|
opacity: 0.4;
|
|
36472
36756
|
}
|
|
36473
36757
|
|
|
36758
|
+
&[data-accent-needs-dark-fg] {
|
|
36759
|
+
--x-color: white;
|
|
36760
|
+
--x-shadow-color: black;
|
|
36761
|
+
}
|
|
36762
|
+
|
|
36474
36763
|
/* When caption is shown, the track takes the full height */
|
|
36475
36764
|
&[data-has-caption] {
|
|
36476
36765
|
.navi_meter_track_container {
|
|
@@ -36506,25 +36795,6 @@ installImportMetaCssBuild(import.meta);const css$4 = /* css */`
|
|
|
36506
36795
|
}
|
|
36507
36796
|
}
|
|
36508
36797
|
`;
|
|
36509
|
-
const MeterStyleCSSVars = {
|
|
36510
|
-
trackColor: "--track-color",
|
|
36511
|
-
borderColor: "--border-color",
|
|
36512
|
-
borderRadius: "--border-radius",
|
|
36513
|
-
height: "--height",
|
|
36514
|
-
width: "--width"
|
|
36515
|
-
};
|
|
36516
|
-
const MeterPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading", ":-navi-meter-optimum", ":-navi-meter-suboptimum", ":-navi-meter-even-less-good"];
|
|
36517
|
-
Object.assign(PSEUDO_CLASSES, {
|
|
36518
|
-
":-navi-meter-optimum": {
|
|
36519
|
-
attribute: "data-optimum"
|
|
36520
|
-
},
|
|
36521
|
-
":-navi-meter-suboptimum": {
|
|
36522
|
-
attribute: "data-suboptimum"
|
|
36523
|
-
},
|
|
36524
|
-
":-navi-meter-even-less-good": {
|
|
36525
|
-
attribute: "data-even-less-good"
|
|
36526
|
-
}
|
|
36527
|
-
});
|
|
36528
36798
|
const Meter = ({
|
|
36529
36799
|
value = 0,
|
|
36530
36800
|
min = 0,
|
|
@@ -36544,7 +36814,7 @@ const Meter = ({
|
|
|
36544
36814
|
style,
|
|
36545
36815
|
...rest
|
|
36546
36816
|
}) => {
|
|
36547
|
-
import.meta.css = [css$
|
|
36817
|
+
import.meta.css = [css$3, "@jsenv/navi/src/text/meter.jsx"];
|
|
36548
36818
|
const defaultRef = useRef();
|
|
36549
36819
|
const ref = rest.ref || defaultRef;
|
|
36550
36820
|
value = Number(value);
|
|
@@ -36573,8 +36843,8 @@ const Meter = ({
|
|
|
36573
36843
|
// When fill covers less than half the track, the text center sits on the
|
|
36574
36844
|
// empty track — use the track color for contrast. Otherwise use fill color.
|
|
36575
36845
|
const backgroundElementSelector = fillRatio >= 0.5 ? ".navi_meter_fill" : ".navi_meter_track";
|
|
36576
|
-
|
|
36577
|
-
backgroundElementSelector
|
|
36846
|
+
useAccentColorAttributes(ref, null, {
|
|
36847
|
+
elementSelector: backgroundElementSelector
|
|
36578
36848
|
});
|
|
36579
36849
|
return jsx(Box, {
|
|
36580
36850
|
ref: ref,
|
|
@@ -36623,6 +36893,25 @@ const Meter = ({
|
|
|
36623
36893
|
})
|
|
36624
36894
|
});
|
|
36625
36895
|
};
|
|
36896
|
+
const MeterStyleCSSVars = {
|
|
36897
|
+
trackColor: "--track-color",
|
|
36898
|
+
borderColor: "--border-color",
|
|
36899
|
+
borderRadius: "--border-radius",
|
|
36900
|
+
height: "--height",
|
|
36901
|
+
width: "--width"
|
|
36902
|
+
};
|
|
36903
|
+
const MeterPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading", ":-navi-meter-optimum", ":-navi-meter-suboptimum", ":-navi-meter-even-less-good"];
|
|
36904
|
+
Object.assign(PSEUDO_CLASSES, {
|
|
36905
|
+
":-navi-meter-optimum": {
|
|
36906
|
+
attribute: "data-optimum"
|
|
36907
|
+
},
|
|
36908
|
+
":-navi-meter-suboptimum": {
|
|
36909
|
+
attribute: "data-suboptimum"
|
|
36910
|
+
},
|
|
36911
|
+
":-navi-meter-even-less-good": {
|
|
36912
|
+
attribute: "data-even-less-good"
|
|
36913
|
+
}
|
|
36914
|
+
});
|
|
36626
36915
|
const getMeterLevel = (value, min, max, low, high, optimum) => {
|
|
36627
36916
|
// Without low/high thresholds the whole range is one region → always optimum
|
|
36628
36917
|
if (low === undefined && high === undefined) {
|
|
@@ -36767,7 +37056,7 @@ const SVGMaskOverlay = ({
|
|
|
36767
37056
|
};
|
|
36768
37057
|
|
|
36769
37058
|
installImportMetaCssBuild(import.meta);// We HAVE TO put paddings around the dialog to ensure window resizing respects this space
|
|
36770
|
-
const css$
|
|
37059
|
+
const css$2 = /* css */`
|
|
36771
37060
|
@layer navi {
|
|
36772
37061
|
.navi_dialog_layout {
|
|
36773
37062
|
--layout-margin: 30px;
|
|
@@ -36851,7 +37140,7 @@ const DialogLayout = ({
|
|
|
36851
37140
|
alignY = "center",
|
|
36852
37141
|
...props
|
|
36853
37142
|
}) => {
|
|
36854
|
-
import.meta.css = [css$
|
|
37143
|
+
import.meta.css = [css$2, "@jsenv/navi/src/layout/dialog_layout.jsx"];
|
|
36855
37144
|
return jsx(Box, {
|
|
36856
37145
|
baseClassName: "navi_dialog_layout",
|
|
36857
37146
|
styleCSSVars: DialogLayoutStyleCSSVars,
|
|
@@ -36867,56 +37156,6 @@ const DialogLayout = ({
|
|
|
36867
37156
|
});
|
|
36868
37157
|
};
|
|
36869
37158
|
|
|
36870
|
-
installImportMetaCssBuild(import.meta);const css$2 = /* css */`
|
|
36871
|
-
@layer navi {
|
|
36872
|
-
.navi_separator {
|
|
36873
|
-
--size: 1px;
|
|
36874
|
-
--color: #e4e4e7;
|
|
36875
|
-
--spacing: 0.5em;
|
|
36876
|
-
--spacing-start: 0.5em;
|
|
36877
|
-
--spacing-end: 0.5em;
|
|
36878
|
-
}
|
|
36879
|
-
}
|
|
36880
|
-
|
|
36881
|
-
.navi_separator {
|
|
36882
|
-
width: 100%;
|
|
36883
|
-
height: var(--size);
|
|
36884
|
-
margin-top: var(--spacing-start, var(--spacing));
|
|
36885
|
-
margin-bottom: var(--spacing-end, var(--spacing));
|
|
36886
|
-
flex-shrink: 0;
|
|
36887
|
-
background: var(--color);
|
|
36888
|
-
border: none;
|
|
36889
|
-
|
|
36890
|
-
&[data-vertical] {
|
|
36891
|
-
display: inline-block;
|
|
36892
|
-
|
|
36893
|
-
width: var(--size);
|
|
36894
|
-
height: 1lh;
|
|
36895
|
-
margin-top: 0;
|
|
36896
|
-
margin-right: var(--spacing-end, var(--spacing));
|
|
36897
|
-
margin-bottom: 0;
|
|
36898
|
-
margin-left: var(--spacing-start, var(--spacing));
|
|
36899
|
-
vertical-align: bottom;
|
|
36900
|
-
}
|
|
36901
|
-
}
|
|
36902
|
-
`;
|
|
36903
|
-
const SeparatorStyleCSSVars = {
|
|
36904
|
-
color: "--color"
|
|
36905
|
-
};
|
|
36906
|
-
const Separator = ({
|
|
36907
|
-
vertical,
|
|
36908
|
-
...props
|
|
36909
|
-
}) => {
|
|
36910
|
-
import.meta.css = [css$2, "@jsenv/navi/src/layout/separator.jsx"];
|
|
36911
|
-
return jsx(Box, {
|
|
36912
|
-
as: vertical ? "span" : "hr",
|
|
36913
|
-
...props,
|
|
36914
|
-
"data-vertical": vertical ? "" : undefined,
|
|
36915
|
-
baseClassName: "navi_separator",
|
|
36916
|
-
styleCSSVars: SeparatorStyleCSSVars
|
|
36917
|
-
});
|
|
36918
|
-
};
|
|
36919
|
-
|
|
36920
37159
|
installImportMetaCssBuild(import.meta);const css$1 = /* css */`
|
|
36921
37160
|
@layer navi {
|
|
36922
37161
|
.navi_viewport_layout {
|
|
@@ -37299,5 +37538,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
37299
37538
|
})
|
|
37300
37539
|
});
|
|
37301
37540
|
|
|
37302
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, Box, Button, ButtonCopyToClipboard, Caption, CheckSvg, Checkbox, CheckboxList, CloseSvg, Code, Col, Colgroup, ConstructionSvg, Details, Dialog, DialogLayout,
|
|
37541
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, Box, Button, ButtonCopyToClipboard, Caption, CheckSvg, Checkbox, CheckboxList, CloseSvg, Code, Col, Colgroup, ConstructionSvg, Details, Dialog, DialogLayout, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemFooter, ListItemGroup, ListItemHeader, Loading, MessageBox, Meter, Nav, NaviDebug, Paragraph, Popover, Quantity, QuantityIntl, Radio, RadioList, Route, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectionContext, Separator, SettingsSvg, SidePanel, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, Thead, Title, Tr, UITransition, UserSvg, ViewportLayout, actionIntegratedVia, actionRunEffect, addCustomMessage, applySearch, arraySignalMembership, compareTwoJsValues, createAction, createAvailableConstraint, createIntl, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, enableDebugActions, enableDebugOnDocumentLoading, filterTableSelection, forwardActionRequested, installCustomConstraintValidation, isCellSelected, isColumnSelected, isRowSelected, localStorageSignal, navBack, navForward, navTo, openCallout, rawUrlPart, reload, removeCustomMessage, requestAction, requestListClose, requestListOpen, rerunActions, resource, route, routeAction, setBaseUrl, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, syncOwnedResourceToSignals, syncResourceToSignals, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, useRouteStatus, useRunOnMount, useSearchText, useSelectRequestClose, useSelectableElement, useSelectionController, useSidePanelClose, useSignalSync, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage, windowWidthSignal };
|
|
37303
37542
|
//# sourceMappingURL=jsenv_navi.js.map
|