@jsenv/navi 0.18.9 → 0.18.11

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.
@@ -1,6 +1,6 @@
1
1
  import { installImportMetaCss } from "./jsenv_navi_side_effects.js";
2
2
  import { isValidElement, createContext, toChildArray, render, createRef, cloneElement } from "preact";
3
- import { useErrorBoundary, useLayoutEffect, useEffect, useCallback, useRef, useState, useContext, useMemo, useImperativeHandle, useId } from "preact/hooks";
3
+ import { useErrorBoundary, useLayoutEffect, useEffect, useMemo, useRef, useState, useCallback, useContext, 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
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, hasCSSSizeUnit, resolveCSSSize, activeElementSignal, canInterceptKeys, initFocusGroup, elementIsFocusable, pickLightOrDark, resolveColorLuminance, dragAfterThreshold, getScrollContainer, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement } from "@jsenv/dom";
@@ -2402,6 +2402,105 @@ const useRunOnMount = (action, Component) => {
2402
2402
  }, []);
2403
2403
  };
2404
2404
 
2405
+ const addIntoArray = (array, ...valuesToAdd) => {
2406
+ if (valuesToAdd.length === 1) {
2407
+ const [valueToAdd] = valuesToAdd;
2408
+ const arrayWithThisValue = [];
2409
+ for (const value of array) {
2410
+ if (value === valueToAdd) {
2411
+ return array;
2412
+ }
2413
+ arrayWithThisValue.push(value);
2414
+ }
2415
+ arrayWithThisValue.push(valueToAdd);
2416
+ return arrayWithThisValue;
2417
+ }
2418
+
2419
+ const existingValueSet = new Set();
2420
+ const arrayWithTheseValues = [];
2421
+ for (const existingValue of array) {
2422
+ arrayWithTheseValues.push(existingValue);
2423
+ existingValueSet.add(existingValue);
2424
+ }
2425
+ let hasNewValues = false;
2426
+ for (const valueToAdd of valuesToAdd) {
2427
+ if (existingValueSet.has(valueToAdd)) {
2428
+ continue;
2429
+ }
2430
+ arrayWithTheseValues.push(valueToAdd);
2431
+ hasNewValues = true;
2432
+ }
2433
+ return hasNewValues ? arrayWithTheseValues : array;
2434
+ };
2435
+
2436
+ const removeFromArray = (array, ...valuesToRemove) => {
2437
+ if (valuesToRemove.length === 1) {
2438
+ const [valueToRemove] = valuesToRemove;
2439
+ const arrayWithoutThisValue = [];
2440
+ let found = false;
2441
+ for (const value of array) {
2442
+ if (value === valueToRemove) {
2443
+ found = true;
2444
+ continue;
2445
+ }
2446
+ arrayWithoutThisValue.push(value);
2447
+ }
2448
+ if (!found) {
2449
+ return array;
2450
+ }
2451
+ return arrayWithoutThisValue;
2452
+ }
2453
+
2454
+ const valuesToRemoveSet = new Set(valuesToRemove);
2455
+ const arrayWithoutTheseValues = [];
2456
+ let hasRemovedValues = false;
2457
+ for (const value of array) {
2458
+ if (valuesToRemoveSet.has(value)) {
2459
+ hasRemovedValues = true;
2460
+ continue;
2461
+ }
2462
+ arrayWithoutTheseValues.push(value);
2463
+ }
2464
+ return hasRemovedValues ? arrayWithoutTheseValues : array;
2465
+ };
2466
+
2467
+ const useArraySignalMembership = (...args) => {
2468
+ if (args.length < 2) {
2469
+ throw new Error(
2470
+ "useArraySignalMembership requires at least 2 arguments: [arraySignal, id]",
2471
+ );
2472
+ }
2473
+
2474
+ return useMemo(() => {
2475
+ return arraySignalMembership(...args);
2476
+ }, args);
2477
+ };
2478
+
2479
+ const arraySignalMembership = (...args) => {
2480
+ if (args.length < 2) {
2481
+ throw new Error(
2482
+ "arraySignalMemberShip requires at least 2 arguments: [arraySignal, id]",
2483
+ );
2484
+ }
2485
+ const [arraySignal, id] = args;
2486
+ const array = arraySignal.value;
2487
+ const isMember = array.includes(id);
2488
+
2489
+ const add = () => {
2490
+ const arrayWithId = addIntoArray(arraySignal.peek(), id);
2491
+ arraySignal.value = arrayWithId;
2492
+ return arrayWithId;
2493
+ };
2494
+
2495
+ const remove = () => {
2496
+ const arrayWithoutId = removeFromArray(arraySignal.peek(), id);
2497
+ arraySignal.value = arrayWithoutId;
2498
+ return arrayWithoutId;
2499
+ };
2500
+
2501
+ return [isMember, add, remove];
2502
+ };
2503
+
2405
2504
  const localStorageSignal = (key) => {
2406
2505
  const initialValue = localStorage.getItem(key);
2407
2506
 
@@ -4852,87 +4951,6 @@ const isProps = (value) => {
4852
4951
  return value !== null && typeof value === "object";
4853
4952
  };
4854
4953
 
4855
- const addIntoArray = (array, ...valuesToAdd) => {
4856
- if (valuesToAdd.length === 1) {
4857
- const [valueToAdd] = valuesToAdd;
4858
- const arrayWithThisValue = [];
4859
- for (const value of array) {
4860
- if (value === valueToAdd) {
4861
- return array;
4862
- }
4863
- arrayWithThisValue.push(value);
4864
- }
4865
- arrayWithThisValue.push(valueToAdd);
4866
- return arrayWithThisValue;
4867
- }
4868
-
4869
- const existingValueSet = new Set();
4870
- const arrayWithTheseValues = [];
4871
- for (const existingValue of array) {
4872
- arrayWithTheseValues.push(existingValue);
4873
- existingValueSet.add(existingValue);
4874
- }
4875
- let hasNewValues = false;
4876
- for (const valueToAdd of valuesToAdd) {
4877
- if (existingValueSet.has(valueToAdd)) {
4878
- continue;
4879
- }
4880
- arrayWithTheseValues.push(valueToAdd);
4881
- hasNewValues = true;
4882
- }
4883
- return hasNewValues ? arrayWithTheseValues : array;
4884
- };
4885
-
4886
- const removeFromArray = (array, ...valuesToRemove) => {
4887
- if (valuesToRemove.length === 1) {
4888
- const [valueToRemove] = valuesToRemove;
4889
- const arrayWithoutThisValue = [];
4890
- let found = false;
4891
- for (const value of array) {
4892
- if (value === valueToRemove) {
4893
- found = true;
4894
- continue;
4895
- }
4896
- arrayWithoutThisValue.push(value);
4897
- }
4898
- if (!found) {
4899
- return array;
4900
- }
4901
- return arrayWithoutThisValue;
4902
- }
4903
-
4904
- const valuesToRemoveSet = new Set(valuesToRemove);
4905
- const arrayWithoutTheseValues = [];
4906
- let hasRemovedValues = false;
4907
- for (const value of array) {
4908
- if (valuesToRemoveSet.has(value)) {
4909
- hasRemovedValues = true;
4910
- continue;
4911
- }
4912
- arrayWithoutTheseValues.push(value);
4913
- }
4914
- return hasRemovedValues ? arrayWithoutTheseValues : array;
4915
- };
4916
-
4917
- const useArraySignalMembership = (arraySignal, id) => {
4918
- const array = arraySignal.value;
4919
- const isMember = array.includes(id);
4920
-
4921
- const add = useCallback(() => {
4922
- const arrayWithId = addIntoArray(arraySignal.peek(), id);
4923
- arraySignal.value = arrayWithId;
4924
- return arrayWithId;
4925
- }, []);
4926
-
4927
- const remove = useCallback(() => {
4928
- const arrayWithoutId = removeFromArray(arraySignal.peek(), id);
4929
- arraySignal.value = arrayWithoutId;
4930
- return arrayWithoutId;
4931
- }, []);
4932
-
4933
- return [isMember, add, remove];
4934
- };
4935
-
4936
4954
  /**
4937
4955
  * Creates a signal that stays synchronized with an external value,
4938
4956
  * only updating the signal when the value actually changes.
@@ -14364,10 +14382,7 @@ const useAutoFocus = (
14364
14382
  autoFocus,
14365
14383
  { autoFocusVisible, autoSelect } = {},
14366
14384
  ) => {
14367
- useLayoutEffect(() => {
14368
- if (!autoFocus) {
14369
- return null;
14370
- }
14385
+ const triggerAutofocus = () => {
14371
14386
  const activeElement = document.activeElement;
14372
14387
  const focusableElement = focusableElementRef.current;
14373
14388
  focusableElement.focus({ focusVisible: autoFocusVisible });
@@ -14418,7 +14433,14 @@ const useAutoFocus = (
14418
14433
 
14419
14434
  activeElement.focus();
14420
14435
  };
14421
- }, []);
14436
+ };
14437
+
14438
+ useLayoutEffect(() => {
14439
+ if (!autoFocus) {
14440
+ return null;
14441
+ }
14442
+ return triggerAutofocus();
14443
+ }, [autoFocus]);
14422
14444
 
14423
14445
  useEffect(() => {
14424
14446
  if (autoFocus) {
@@ -14426,6 +14448,8 @@ const useAutoFocus = (
14426
14448
  focusableElement.scrollIntoView({ inline: "nearest", block: "nearest" });
14427
14449
  }
14428
14450
  }, []);
14451
+
14452
+ return triggerAutofocus;
14429
14453
  };
14430
14454
 
14431
14455
  const CalloutCloseContext = createContext();
@@ -17738,7 +17762,7 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
17738
17762
 
17739
17763
  .navi_text_overflow_wrapper {
17740
17764
  display: flex;
17741
- width: 0;
17765
+ width: 100%;
17742
17766
  flex-grow: 1;
17743
17767
  gap: 0.3em;
17744
17768
  }
@@ -17940,7 +17964,6 @@ const TextOverflow = ({
17940
17964
  preLine: rest.as === "p",
17941
17965
  ...rest,
17942
17966
  className: "navi_text_overflow",
17943
- expandX: true,
17944
17967
  spacing: "pre",
17945
17968
  children: jsxs("span", {
17946
17969
  className: "navi_text_overflow_wrapper",
@@ -20691,7 +20714,8 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
20691
20714
  --link-outline-color: var(--navi-focus-outline-color);
20692
20715
  --link-loader-color: var(--navi-loader-color);
20693
20716
  --link-color: rgb(0, 0, 238);
20694
- --link-color-visited: light-dark(#6a1b9a, #ab47bc);
20717
+ --link-color-visited: color-mix(in srgb, var(--link-color), black 40%);
20718
+
20695
20719
  --link-color-active: red;
20696
20720
  --link-text-decoration: underline;
20697
20721
  --link-text-decoration-hover: var(--link-text-decoration);
@@ -20738,22 +20762,6 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
20738
20762
  --x-link-color: var(--x-link-color-hover);
20739
20763
  --x-link-text-decoration: var(--x-link-text-decoration-hover);
20740
20764
  }
20741
- /* Current */
20742
- &[data-href-current] {
20743
- --x-link-color: var(--link-color-current);
20744
- --x-link-cursor: default;
20745
- &[data-anchor] {
20746
- /* For anchor links, we want to keep the pointer cursor to indicate interactivity */
20747
- /* as anchor link will still scroll to the section even if it's the current page */
20748
- --x-link-cursor: pointer;
20749
- }
20750
- }
20751
- /* Focus */
20752
- &[data-focus],
20753
- &[data-focus-visible] {
20754
- position: relative;
20755
- z-index: 1; /* Ensure focus outline is above other elements */
20756
- }
20757
20765
  &[data-focus-visible] {
20758
20766
  outline-width: 2px;
20759
20767
  }
@@ -20773,6 +20781,22 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
20773
20781
  /* Redefine it otherwise [data-visited] prevails */
20774
20782
  --x-link-color: var(--x-link-color-active);
20775
20783
  }
20784
+ /* Current */
20785
+ &[data-href-current] {
20786
+ --x-link-color: var(--link-color-current);
20787
+ --x-link-cursor: default;
20788
+ &[data-anchor] {
20789
+ /* For anchor links, we want to keep the pointer cursor to indicate interactivity */
20790
+ /* as anchor link will still scroll to the section even if it's the current page */
20791
+ --x-link-cursor: pointer;
20792
+ }
20793
+ }
20794
+ /* Focus */
20795
+ &[data-focus],
20796
+ &[data-focus-visible] {
20797
+ position: relative;
20798
+ z-index: 1; /* Ensure focus outline is above other elements */
20799
+ }
20776
20800
  /* Readonly */
20777
20801
  &[data-readonly] > * {
20778
20802
  opacity: 0.5;
@@ -24471,8 +24495,21 @@ const Input = props => {
24471
24495
 
24472
24496
  installImportMetaCss(import.meta);import.meta.css = /* css */`
24473
24497
  .navi_editable_wrapper {
24498
+ --inset-top: 0px;
24499
+ --inset-right: 0px;
24500
+ --inset-bottom: 0px;
24501
+ --inset-left: 0px;
24502
+
24474
24503
  position: absolute;
24475
- inset: 0;
24504
+ top: var(--inset-top);
24505
+ right: var(--inset-right);
24506
+ bottom: var(--inset-bottom);
24507
+ left: var(--inset-left);
24508
+
24509
+ opacity: 0;
24510
+ &[data-editing] {
24511
+ opacity: 1;
24512
+ }
24476
24513
  }
24477
24514
  `;
24478
24515
  const useEditionController = () => {
@@ -24562,7 +24599,7 @@ const Editable = props => {
24562
24599
  name: name,
24563
24600
  value: value,
24564
24601
  valueSignal: valueSignal,
24565
- autoFocus: true,
24602
+ autoFocus: editing,
24566
24603
  autoFocusVisible: true,
24567
24604
  autoSelect: autoSelect,
24568
24605
  cancelOnEscape: true,
@@ -24607,12 +24644,32 @@ const Editable = props => {
24607
24644
  });
24608
24645
  }
24609
24646
  });
24647
+ const wrapperRef = useRef();
24648
+ useLayoutEffect(() => {
24649
+ const wrapper = wrapperRef.current;
24650
+ if (!wrapper) {
24651
+ return;
24652
+ }
24653
+ const parent = wrapper.parentElement;
24654
+ const borderSizes = getBorderSizes(parent);
24655
+ wrapper.style.setProperty("--inset-left", `-${borderSizes.left}px`);
24656
+ wrapper.style.setProperty("--inset-top", `-${borderSizes.top}px`);
24657
+ wrapper.style.setProperty("--inset-right", `-${borderSizes.right}px`);
24658
+ wrapper.style.setProperty("--inset-bottom", `-${borderSizes.bottom}px`);
24659
+ });
24610
24660
  return jsxs(Fragment, {
24611
24661
  children: [children || jsx("span", {
24612
24662
  children: value
24613
- }), editing && jsx(Box, {
24663
+ }), jsx(Box, {
24664
+ className: "navi_editable_wrapper",
24665
+ ref: wrapperRef,
24614
24666
  ...wrapperProps,
24615
- baseClassName: "navi_editable_wrapper",
24667
+ // inert ensure input while not editing that:
24668
+ // - input not focusable (via keyboard or anything)
24669
+ // - cannot be interacted with pointer (click, hover, etc)
24670
+ // - is ignored by screen readers
24671
+ inert: editing ? undefined : "",
24672
+ "data-editing": editing ? "" : undefined,
24616
24673
  children: input
24617
24674
  })]
24618
24675
  });
@@ -29595,7 +29652,7 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
29595
29652
  /* Semantic fill colors, matching native meter on Chrome/macOS */
29596
29653
  --fill-color-optimum: light-dark(#0f7c0f, #4caf50);
29597
29654
  --fill-color-suboptimum: light-dark(#fdb900, #ffc107);
29598
- --fill-color-subsuboptimum: light-dark(#d83b01, #f44336);
29655
+ --fill-color-even-less-good: light-dark(#d83b01, #f44336);
29599
29656
  }
29600
29657
  }
29601
29658
 
@@ -29698,7 +29755,18 @@ const MeterStyleCSSVars = {
29698
29755
  height: "--height",
29699
29756
  width: "--width"
29700
29757
  };
29701
- const MeterPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading"];
29758
+ const MeterPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading", ":-navi-meter-optimum", ":-navi-meter-suboptimum", ":-navi-meter-even-less-good"];
29759
+ Object.assign(PSEUDO_CLASSES, {
29760
+ ":-navi-meter-optimum": {
29761
+ attribute: "data-optimum"
29762
+ },
29763
+ ":-navi-meter-suboptimum": {
29764
+ attribute: "data-suboptimum"
29765
+ },
29766
+ ":-navi-meter-even-less-good": {
29767
+ attribute: "data-even-less-good"
29768
+ }
29769
+ });
29702
29770
  const Meter = ({
29703
29771
  value = 0,
29704
29772
  min = 0,
@@ -29729,7 +29797,7 @@ const Meter = ({
29729
29797
  });
29730
29798
  }
29731
29799
  const level = getMeterLevel(clampedValue, min, max, low, high, optimum);
29732
- const fillColorVar = level === "optimum" ? "var(--fill-color-optimum)" : level === "suboptimum" ? "var(--fill-color-suboptimum)" : "var(--fill-color-subsuboptimum)";
29800
+ const fillColorVar = level === "optimum" ? "var(--fill-color-optimum)" : level === "suboptimum" ? "var(--fill-color-suboptimum)" : "var(--fill-color-even-less-good)";
29733
29801
  reportDisabledToLabel(disabled);
29734
29802
  reportReadOnlyToLabel(readOnly);
29735
29803
  const trackContainerRef = useRef();
@@ -29764,7 +29832,10 @@ const Meter = ({
29764
29832
  basePseudoState: {
29765
29833
  ":read-only": readOnly,
29766
29834
  ":disabled": disabled,
29767
- ":-navi-loading": loading
29835
+ ":-navi-loading": loading,
29836
+ ":-navi-meter-optimum": level === "optimum",
29837
+ ":-navi-meter-suboptimum": level === "suboptimum",
29838
+ ":-navi-meter-even-less-good": level === "even-less-good"
29768
29839
  },
29769
29840
  pseudoClasses: MeterPseudoClasses,
29770
29841
  "data-has-caption": children !== undefined ? "" : undefined,
@@ -29815,7 +29886,7 @@ const getMeterLevel = (value, min, max, low, high, optimum) => {
29815
29886
  const distance = Math.abs(optimumRegion - valueRegion);
29816
29887
  if (distance === 0) return "optimum";
29817
29888
  if (distance === 1) return "suboptimum";
29818
- return "subsuboptimum";
29889
+ return "even-less-good";
29819
29890
  };
29820
29891
 
29821
29892
  const Paragraph = props => {
@@ -30241,5 +30312,5 @@ const UserSvg = () => jsx("svg", {
30241
30312
  })
30242
30313
  });
30243
30314
 
30244
- export { ActionRenderer, ActiveKeyboardShortcuts, Address, BadgeCount, Box, Button, ButtonCopyToClipboard, Caption, CheckSvg, Checkbox, CheckboxList, Code, Col, Colgroup, ConstructionSvg, Details, DialogLayout, Editable, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Form, Group, HeartSvg, HomeSvg, Icon, Image, Input, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, MessageBox, Meter, Paragraph, Radio, RadioList, Route, RouteLink, Routes, RowNumberCol, RowNumberTableCell, SINGLE_SPACE_CONSTRAINT, SVGMaskOverlay, SearchSvg, Select, SelectionContext, Separator, SettingsSvg, StarSvg, Stat, SummaryMarker, Svg, Tab, TabList, Table, TableCell, Tbody, Text, Thead, Title, Tr, UITransition, UserSvg, ViewportLayout, actionIntegratedVia, actionRunEffect, addCustomMessage, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSelectionKeyboardShortcuts, enableDebugActions, enableDebugOnDocumentLoading, forwardActionRequested, installCustomConstraintValidation, isCellSelected, isColumnSelected, isRowSelected, localStorageSignal, navBack, navForward, navTo, openCallout, rawUrlPart, reload, removeCustomMessage, requestAction, rerunActions, resource, route, routeAction, setBaseUrl, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, updateActions, useActionData, useActionStatus, useArraySignalMembership, useCalloutClose, useCancelPrevious, useCellsAndColumns, useConstraintValidityState, useDependenciesDiff, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useMatchingRouteInfo, useNavState$1 as useNavState, useRouteStatus, useRunOnMount, useSelectableElement, useSelectionController, useSignalSync, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage };
30315
+ export { ActionRenderer, ActiveKeyboardShortcuts, Address, BadgeCount, Box, Button, ButtonCopyToClipboard, Caption, CheckSvg, Checkbox, CheckboxList, Code, Col, Colgroup, ConstructionSvg, Details, DialogLayout, Editable, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Form, Group, HeartSvg, HomeSvg, Icon, Image, Input, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, MessageBox, Meter, Paragraph, Radio, RadioList, Route, RouteLink, Routes, RowNumberCol, RowNumberTableCell, SINGLE_SPACE_CONSTRAINT, SVGMaskOverlay, SearchSvg, Select, SelectionContext, Separator, SettingsSvg, StarSvg, Stat, SummaryMarker, Svg, Tab, TabList, Table, TableCell, Tbody, Text, Thead, Title, Tr, UITransition, UserSvg, ViewportLayout, actionIntegratedVia, actionRunEffect, addCustomMessage, arraySignalMembership, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSelectionKeyboardShortcuts, enableDebugActions, enableDebugOnDocumentLoading, forwardActionRequested, installCustomConstraintValidation, isCellSelected, isColumnSelected, isRowSelected, localStorageSignal, navBack, navForward, navTo, openCallout, rawUrlPart, reload, removeCustomMessage, requestAction, rerunActions, resource, route, routeAction, setBaseUrl, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, updateActions, useActionData, useActionStatus, useArraySignalMembership, useCalloutClose, useCancelPrevious, useCellsAndColumns, useConstraintValidityState, useDependenciesDiff, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useMatchingRouteInfo, useNavState$1 as useNavState, useRouteStatus, useRunOnMount, useSelectableElement, useSelectionController, useSignalSync, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage };
30245
30316
  //# sourceMappingURL=jsenv_navi.js.map