@jsenv/navi 0.28.9 → 0.28.10
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 +247 -37
- package/dist/jsenv_navi.js.map +17 -4
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -6521,6 +6521,13 @@ const DIMENSION_PROPS = {
|
|
|
6521
6521
|
const inHorizontalFlexFlow =
|
|
6522
6522
|
parentBoxFlow === "flex-x" || parentBoxFlow === "inline-flex-x";
|
|
6523
6523
|
if (inHorizontalFlexFlow) {
|
|
6524
|
+
if (value === "content") {
|
|
6525
|
+
// flex-basis stays auto: the item still takes the free space, but its
|
|
6526
|
+
// content size is what line-breaking sees — so in a flexWrap parent a
|
|
6527
|
+
// neighbor that no longer fits wraps to the next line. With basis 0%
|
|
6528
|
+
// (below) nothing ever wraps: every item claims a size of zero.
|
|
6529
|
+
return { flexGrow: 1 };
|
|
6530
|
+
}
|
|
6524
6531
|
// Parent is flex-x: grow as flex item
|
|
6525
6532
|
return { flexGrow: 1, flexBasis: "0%" };
|
|
6526
6533
|
}
|
|
@@ -6540,6 +6547,11 @@ const DIMENSION_PROPS = {
|
|
|
6540
6547
|
const inVerticalFlexFlow =
|
|
6541
6548
|
parentBoxFlow === "flex-y" || parentBoxFlow === "inline-flex-y";
|
|
6542
6549
|
if (inVerticalFlexFlow) {
|
|
6550
|
+
if (value === "content") {
|
|
6551
|
+
// Same as expandX="content": grow from the content size, so a
|
|
6552
|
+
// flexWrap parent can wrap.
|
|
6553
|
+
return { flexGrow: 1 };
|
|
6554
|
+
}
|
|
6543
6555
|
// Parent is flex-y: grow as flex item
|
|
6544
6556
|
return { flexGrow: 1, flexBasis: "0%" };
|
|
6545
6557
|
}
|
|
@@ -17435,8 +17447,12 @@ const canContainCallout = element => {
|
|
|
17435
17447
|
if (VOID_ELEMENT_TAG_NAMES.has(element.tagName)) {
|
|
17436
17448
|
return false;
|
|
17437
17449
|
}
|
|
17438
|
-
if (element.tagName === "BUTTON") {
|
|
17439
|
-
// callout
|
|
17450
|
+
if (element.tagName === "BUTTON" || element.getAttribute("role") === "button") {
|
|
17451
|
+
// Never mount the callout inside a button, native or role="button":
|
|
17452
|
+
// - a <button> cannot contain the callout's own close button
|
|
17453
|
+
// - presses inside the callout would bubble into the button's own press
|
|
17454
|
+
// handlers: a preventDefault there makes the callout text unselectable,
|
|
17455
|
+
// and a handler that opens the callout re-opens it (visible blink)
|
|
17440
17456
|
return false;
|
|
17441
17457
|
}
|
|
17442
17458
|
return true;
|
|
@@ -35070,7 +35086,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
35070
35086
|
* - variant="discrete" + backgroundColor: the color applies at rest and hover,
|
|
35071
35087
|
* and the field goes transparent while focused.
|
|
35072
35088
|
*/
|
|
35073
|
-
const
|
|
35089
|
+
const inputCss = /* css */`
|
|
35074
35090
|
@layer navi {
|
|
35075
35091
|
.navi_input {
|
|
35076
35092
|
--border-radius: var(--navi-control-border-radius);
|
|
@@ -35401,7 +35417,7 @@ const useInputTextualProps = props => {
|
|
|
35401
35417
|
});
|
|
35402
35418
|
};
|
|
35403
35419
|
const InputTextualUI = props => {
|
|
35404
|
-
import.meta.css = [
|
|
35420
|
+
import.meta.css = [inputCss, "@jsenv/navi/src/control/input/input_textual.jsx"];
|
|
35405
35421
|
// Spacing props travel to CSS as a raw custom property value, so the size
|
|
35406
35422
|
// keywords have to become lengths here — "s" reaching CSS untouched makes the
|
|
35407
35423
|
// declaration invalid, silently, and the gap just goes away.
|
|
@@ -35520,6 +35536,9 @@ const RealInput = ({
|
|
|
35520
35536
|
"navi-max-length": maxLength
|
|
35521
35537
|
});
|
|
35522
35538
|
};
|
|
35539
|
+
|
|
35540
|
+
// Shared with textarea.jsx: a textarea is styled as a .navi_input box, so the
|
|
35541
|
+
// two read the same style props and pseudo states.
|
|
35523
35542
|
const InputStyleCSSVars = {
|
|
35524
35543
|
"slotSpacing": "--slot-spacing",
|
|
35525
35544
|
"outlineWidth": "--outline-width",
|
|
@@ -36058,7 +36077,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
36058
36077
|
* This means an editable thing MUST have a parent with position relative that wraps the content and the eventual editable input
|
|
36059
36078
|
*
|
|
36060
36079
|
*/
|
|
36061
|
-
const css$
|
|
36080
|
+
const css$D = /* css */`
|
|
36062
36081
|
.navi_editable_wrapper {
|
|
36063
36082
|
--inset-top: 0px;
|
|
36064
36083
|
--inset-right: 0px;
|
|
@@ -36107,7 +36126,7 @@ const useEditionController = () => {
|
|
|
36107
36126
|
};
|
|
36108
36127
|
};
|
|
36109
36128
|
const Editable = props => {
|
|
36110
|
-
import.meta.css = [css$
|
|
36129
|
+
import.meta.css = [css$D, "@jsenv/navi/src/control/edition/editable.jsx"];
|
|
36111
36130
|
let {
|
|
36112
36131
|
children,
|
|
36113
36132
|
action,
|
|
@@ -36521,7 +36540,7 @@ HTMLFormElement.prototype.requestSubmit = function (submitter) {
|
|
|
36521
36540
|
// form.dispatchEvent(customEvent);
|
|
36522
36541
|
// };
|
|
36523
36542
|
|
|
36524
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
36543
|
+
installImportMetaCssBuild(import.meta);const css$C = /* css */`
|
|
36525
36544
|
.navi_group {
|
|
36526
36545
|
--group-border-width: 1px;
|
|
36527
36546
|
|
|
@@ -36617,7 +36636,7 @@ const Group = ({
|
|
|
36617
36636
|
vertical = row,
|
|
36618
36637
|
...props
|
|
36619
36638
|
}) => {
|
|
36620
|
-
import.meta.css = [css$
|
|
36639
|
+
import.meta.css = [css$C, "@jsenv/navi/src/control/group.jsx"];
|
|
36621
36640
|
return jsx(Box, {
|
|
36622
36641
|
baseClassName: "navi_group",
|
|
36623
36642
|
"data-vertical": vertical ? "" : undefined,
|
|
@@ -37027,7 +37046,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
37027
37046
|
* pair. One popup holding slides of its own contents has no such problem — and
|
|
37028
37047
|
* it is the same component in the document, in a dialog or in a popover.
|
|
37029
37048
|
*/
|
|
37030
|
-
const css$
|
|
37049
|
+
const css$B = /* css */`
|
|
37031
37050
|
/* Every slide in the same grid cell: the box then measures itself on the
|
|
37032
37051
|
LARGEST of them, in both directions, without anything being measured by
|
|
37033
37052
|
hand — which is also why nothing here resizes as the slides change. Each
|
|
@@ -37325,7 +37344,7 @@ const SlideContainer = ({
|
|
|
37325
37344
|
children,
|
|
37326
37345
|
...rest
|
|
37327
37346
|
}) => {
|
|
37328
|
-
import.meta.css = [css$
|
|
37347
|
+
import.meta.css = [css$B, "@jsenv/navi/src/layout/slide_container.jsx"];
|
|
37329
37348
|
const debugFocus = useDebugFocus();
|
|
37330
37349
|
const trackRef = useRef();
|
|
37331
37350
|
// The box itself: it is what takes the keyboard when what is on screen holds
|
|
@@ -39925,7 +39944,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
39925
39944
|
* reaches the real container.
|
|
39926
39945
|
*/
|
|
39927
39946
|
let openLocalDialogCount = 0;
|
|
39928
|
-
const css$
|
|
39947
|
+
const css$A = /* css */`
|
|
39929
39948
|
@layer navi {
|
|
39930
39949
|
.navi_dialog {
|
|
39931
39950
|
/* Min gap between the dialog and the edges of its container. Written
|
|
@@ -40332,7 +40351,7 @@ const css$z = /* css */`
|
|
|
40332
40351
|
* @param {import("ignore:preact").ComponentChildren} props.children
|
|
40333
40352
|
*/
|
|
40334
40353
|
const Dialog = props => {
|
|
40335
|
-
import.meta.css = [css$
|
|
40354
|
+
import.meta.css = [css$A, "@jsenv/navi/src/layout/dialog.jsx"];
|
|
40336
40355
|
if (props.openController) {
|
|
40337
40356
|
return jsx(ControlledDialog, {
|
|
40338
40357
|
...props
|
|
@@ -41150,7 +41169,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
41150
41169
|
* and applied.
|
|
41151
41170
|
*/
|
|
41152
41171
|
let openLocalPopoverCount = 0;
|
|
41153
|
-
const css$
|
|
41172
|
+
const css$z = /* css */`
|
|
41154
41173
|
@layer navi {
|
|
41155
41174
|
.navi_popover {
|
|
41156
41175
|
/* soft: user-configurable preferred max-height. Kept as a *default*
|
|
@@ -41517,7 +41536,7 @@ const css$y = /* css */`
|
|
|
41517
41536
|
* @param {import("ignore:preact").ComponentChildren} props.children
|
|
41518
41537
|
*/
|
|
41519
41538
|
const Popover = props => {
|
|
41520
|
-
import.meta.css = [css$
|
|
41539
|
+
import.meta.css = [css$z, "@jsenv/navi/src/layout/popover.jsx"];
|
|
41521
41540
|
if (props.openController) {
|
|
41522
41541
|
return jsx(ControlledPopover, {
|
|
41523
41542
|
...props
|
|
@@ -42481,7 +42500,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
42481
42500
|
* pass through untouched via `...rest` to whichever of Popover/Dialog
|
|
42482
42501
|
* actually renders.
|
|
42483
42502
|
*/
|
|
42484
|
-
const css$
|
|
42503
|
+
const css$y = /* css */`
|
|
42485
42504
|
@layer navi {
|
|
42486
42505
|
.navi_popup {
|
|
42487
42506
|
--popup-border-radius: var(--navi-popup-border-radius);
|
|
@@ -42575,7 +42594,7 @@ const css$x = /* css */`
|
|
|
42575
42594
|
* @param {import("ignore:preact").ComponentChildren} props.children
|
|
42576
42595
|
*/
|
|
42577
42596
|
const Popup = props => {
|
|
42578
|
-
import.meta.css = [css$
|
|
42597
|
+
import.meta.css = [css$y, "@jsenv/navi/src/layout/popup.jsx"];
|
|
42579
42598
|
const {
|
|
42580
42599
|
mode: modeProp,
|
|
42581
42600
|
maxWidth,
|
|
@@ -42718,7 +42737,7 @@ const commitSubtree = (controller, e) => {
|
|
|
42718
42737
|
}
|
|
42719
42738
|
};
|
|
42720
42739
|
|
|
42721
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
42740
|
+
installImportMetaCssBuild(import.meta);const css$x = /* css */`
|
|
42722
42741
|
.navi_picker {
|
|
42723
42742
|
/* Sizing ceilings (maxmax), background, box-shadow, outline, padding,
|
|
42724
42743
|
overflow... are already handled correctly by Popup/Popover/Dialog
|
|
@@ -42830,7 +42849,7 @@ installImportMetaCssBuild(import.meta);const css$w = /* css */`
|
|
|
42830
42849
|
}
|
|
42831
42850
|
`;
|
|
42832
42851
|
const PickerCustomResolver = props => {
|
|
42833
|
-
import.meta.css = [css$
|
|
42852
|
+
import.meta.css = [css$x, "@jsenv/navi/src/control/picker/picker_custom.jsx"];
|
|
42834
42853
|
if (props.children === undefined) {
|
|
42835
42854
|
return jsx(PickerNative, {
|
|
42836
42855
|
...props
|
|
@@ -43475,7 +43494,7 @@ const LoadingIndicator = ({
|
|
|
43475
43494
|
});
|
|
43476
43495
|
};
|
|
43477
43496
|
|
|
43478
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
43497
|
+
installImportMetaCssBuild(import.meta);const css$w = /* css */`
|
|
43479
43498
|
@layer navi {
|
|
43480
43499
|
.navi_separator {
|
|
43481
43500
|
--size: 1px;
|
|
@@ -43553,7 +43572,7 @@ const Separator = ({
|
|
|
43553
43572
|
style,
|
|
43554
43573
|
...props
|
|
43555
43574
|
}) => {
|
|
43556
|
-
import.meta.css = [css$
|
|
43575
|
+
import.meta.css = [css$w, "@jsenv/navi/src/layout/separator.jsx"];
|
|
43557
43576
|
return jsx(Box, {
|
|
43558
43577
|
as: vertical ? "span" : "hr",
|
|
43559
43578
|
...props,
|
|
@@ -44010,7 +44029,7 @@ const ListItemFooter = props => {
|
|
|
44010
44029
|
});
|
|
44011
44030
|
};
|
|
44012
44031
|
|
|
44013
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
44032
|
+
installImportMetaCssBuild(import.meta);const css$v = /* css */`
|
|
44014
44033
|
@layer navi {
|
|
44015
44034
|
.navi_list_container[navi-selectable] {
|
|
44016
44035
|
/* Focus outline */
|
|
@@ -44214,7 +44233,7 @@ const ListSelectableResolver = props => {
|
|
|
44214
44233
|
};
|
|
44215
44234
|
const ListSelectable = props => {
|
|
44216
44235
|
const Next = useNextResolver();
|
|
44217
|
-
import.meta.css = [css$
|
|
44236
|
+
import.meta.css = [css$v, "@jsenv/navi/src/control/list/list_selectable.jsx"];
|
|
44218
44237
|
// we allow ourselves to auto-generate a name
|
|
44219
44238
|
const defaultName = useId();
|
|
44220
44239
|
props.name = props.name || `listbox_${defaultName}`;
|
|
@@ -44756,7 +44775,7 @@ const SeparatorContext = createContext(null);
|
|
|
44756
44775
|
// Set by <List itemTransition>: each row then gets a view-transition-name of
|
|
44757
44776
|
// its own, so a change wrapped in a view transition animates row by row.
|
|
44758
44777
|
const ItemTransitionContext = createContext(false);
|
|
44759
|
-
const css$
|
|
44778
|
+
const css$u = /* css */`
|
|
44760
44779
|
@layer navi {
|
|
44761
44780
|
.navi_list_container {
|
|
44762
44781
|
--list-outline-width: 1px;
|
|
@@ -45244,7 +45263,7 @@ const css$t = /* css */`
|
|
|
45244
45263
|
}
|
|
45245
45264
|
`;
|
|
45246
45265
|
const ListUI = props => {
|
|
45247
|
-
import.meta.css = [css$
|
|
45266
|
+
import.meta.css = [css$u, "@jsenv/navi/src/control/list/list.jsx"];
|
|
45248
45267
|
const {
|
|
45249
45268
|
ref,
|
|
45250
45269
|
renderBudget: renderBudgetProp = RENDER_BUDGET_DEFAULT,
|
|
@@ -46809,7 +46828,7 @@ const PickerPresetResolver = props => {
|
|
|
46809
46828
|
});
|
|
46810
46829
|
};
|
|
46811
46830
|
|
|
46812
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
46831
|
+
installImportMetaCssBuild(import.meta);const css$t = /* css */`
|
|
46813
46832
|
@layer navi {
|
|
46814
46833
|
}
|
|
46815
46834
|
.navi_badge {
|
|
@@ -46888,7 +46907,7 @@ const Badge = ({
|
|
|
46888
46907
|
className,
|
|
46889
46908
|
...props
|
|
46890
46909
|
}) => {
|
|
46891
|
-
import.meta.css = [css$
|
|
46910
|
+
import.meta.css = [css$t, "@jsenv/navi/src/text/badge.jsx"];
|
|
46892
46911
|
const defaultRef = useRef();
|
|
46893
46912
|
props.ref = props.ref || defaultRef;
|
|
46894
46913
|
const {
|
|
@@ -46935,7 +46954,7 @@ const BadgeButton = props => {
|
|
|
46935
46954
|
};
|
|
46936
46955
|
Badge.Button = BadgeButton;
|
|
46937
46956
|
|
|
46938
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
46957
|
+
installImportMetaCssBuild(import.meta);const css$s = /* css */`
|
|
46939
46958
|
@layer navi {
|
|
46940
46959
|
}
|
|
46941
46960
|
.navi_badge_list {
|
|
@@ -46960,7 +46979,7 @@ const BadgeList = ({
|
|
|
46960
46979
|
max,
|
|
46961
46980
|
...props
|
|
46962
46981
|
}) => {
|
|
46963
|
-
import.meta.css = [css$
|
|
46982
|
+
import.meta.css = [css$s, "@jsenv/navi/src/text/badge_list.jsx"];
|
|
46964
46983
|
const measureRef = useRef();
|
|
46965
46984
|
const visibleRef = useRef();
|
|
46966
46985
|
useLayoutEffect(() => {
|
|
@@ -47035,7 +47054,7 @@ const BadgeList = ({
|
|
|
47035
47054
|
});
|
|
47036
47055
|
};
|
|
47037
47056
|
|
|
47038
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
47057
|
+
installImportMetaCssBuild(import.meta);const css$r = /* css */`
|
|
47039
47058
|
.navi_color {
|
|
47040
47059
|
display: block;
|
|
47041
47060
|
aspect-ratio: 1/1;
|
|
@@ -47066,7 +47085,7 @@ const Color = ({
|
|
|
47066
47085
|
children,
|
|
47067
47086
|
...rest
|
|
47068
47087
|
}) => {
|
|
47069
|
-
import.meta.css = [css$
|
|
47088
|
+
import.meta.css = [css$r, "@jsenv/navi/src/text/color.jsx"];
|
|
47070
47089
|
const color = children || undefined;
|
|
47071
47090
|
return jsx(Box, {
|
|
47072
47091
|
as: "span",
|
|
@@ -47521,7 +47540,7 @@ const PickerFileUI = () => {
|
|
|
47521
47540
|
return String(value);
|
|
47522
47541
|
};
|
|
47523
47542
|
|
|
47524
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
47543
|
+
installImportMetaCssBuild(import.meta);const css$q = /* css */`
|
|
47525
47544
|
@layer navi {
|
|
47526
47545
|
.navi_picker {
|
|
47527
47546
|
--picker-border-radius: var(--navi-control-border-radius);
|
|
@@ -47847,7 +47866,7 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
47847
47866
|
}
|
|
47848
47867
|
`;
|
|
47849
47868
|
const PickerButton = props => {
|
|
47850
|
-
import.meta.css = [css$
|
|
47869
|
+
import.meta.css = [css$q, "@jsenv/navi/src/control/picker/picker.jsx"];
|
|
47851
47870
|
if (typeof props.maxLines === "string") {
|
|
47852
47871
|
props.maxLines = parseInt(props.maxLines);
|
|
47853
47872
|
}
|
|
@@ -48305,7 +48324,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
48305
48324
|
* is what keeps the focus where the travel happens instead of moving it into a
|
|
48306
48325
|
* slide that is about to leave.
|
|
48307
48326
|
*/
|
|
48308
|
-
const css$
|
|
48327
|
+
const css$p = /* css */`
|
|
48309
48328
|
.navi_picker_spin {
|
|
48310
48329
|
/* What the loading outline is drawn around. */
|
|
48311
48330
|
position: relative;
|
|
@@ -48544,7 +48563,7 @@ const DaySpin = ({
|
|
|
48544
48563
|
nextLabel,
|
|
48545
48564
|
...rest
|
|
48546
48565
|
}) => {
|
|
48547
|
-
import.meta.css = [css$
|
|
48566
|
+
import.meta.css = [css$p, "@jsenv/navi/src/control/picker/picker_spin.jsx"];
|
|
48548
48567
|
const id = useId();
|
|
48549
48568
|
const containerId = `${id}_days`;
|
|
48550
48569
|
const pickerId = `${id}_picker`;
|
|
@@ -48939,7 +48958,7 @@ const addDays = (day, count) => {
|
|
|
48939
48958
|
};
|
|
48940
48959
|
|
|
48941
48960
|
installImportMetaCssBuild(import.meta);// TOFIX: select in data then reset, it reset to red/blue instead of red/blue/green
|
|
48942
|
-
const css$
|
|
48961
|
+
const css$o = /* css */`
|
|
48943
48962
|
.navi_checkbox_group {
|
|
48944
48963
|
border-style: solid;
|
|
48945
48964
|
|
|
@@ -48959,7 +48978,7 @@ const CheckboxGroup = props => {
|
|
|
48959
48978
|
return checkboxGroup;
|
|
48960
48979
|
};
|
|
48961
48980
|
const CheckboxGroupInterface = props => {
|
|
48962
|
-
import.meta.css = [css$
|
|
48981
|
+
import.meta.css = [css$o, "@jsenv/navi/src/control/input/checkbox_group.jsx"];
|
|
48963
48982
|
const {
|
|
48964
48983
|
ref
|
|
48965
48984
|
} = props;
|
|
@@ -48990,6 +49009,197 @@ const CheckboxGroupInterface = props => {
|
|
|
48990
49009
|
});
|
|
48991
49010
|
};
|
|
48992
49011
|
|
|
49012
|
+
installImportMetaCssBuild(import.meta);/**
|
|
49013
|
+
* Multiline text control that grows with what is typed.
|
|
49014
|
+
*
|
|
49015
|
+
* Autosize is native: `field-sizing: content` lets the browser size the
|
|
49016
|
+
* textarea from its value — no hidden mirror textarea to measure against (the
|
|
49017
|
+
* technique libraries used before the property existed). `minRows`/`maxRows`
|
|
49018
|
+
* become min/max heights in `lh` units on top of it; past `maxRows` the
|
|
49019
|
+
* content scrolls.
|
|
49020
|
+
*
|
|
49021
|
+
* TextareaCharCount is the counter that goes with it, and the caller places
|
|
49022
|
+
* it: under the box, in a form footer, next to a label — fed with the same
|
|
49023
|
+
* value/signal as the textarea. The textarea draws no counter of its own.
|
|
49024
|
+
*
|
|
49025
|
+
* Styled as a `.navi_input` box (border, background, focus ring, readonly and
|
|
49026
|
+
* disabled fades, variants): one look for everything one types into. The
|
|
49027
|
+
* shared sheet is registered here too — a page may render a Textarea without
|
|
49028
|
+
* any Input.
|
|
49029
|
+
*/
|
|
49030
|
+
const css$n = /* css */`
|
|
49031
|
+
.navi_input.navi_textarea {
|
|
49032
|
+
.navi_control_input {
|
|
49033
|
+
min-height: calc(var(--textarea-min-rows, 1.5) * 1lh);
|
|
49034
|
+
/* Above maxRows the box stops growing and the content scrolls. The
|
|
49035
|
+
99999 fallback means "no cap" without needing a conditional rule. */
|
|
49036
|
+
max-height: calc(var(--textarea-max-rows, 99999) * 1lh);
|
|
49037
|
+
field-sizing: content;
|
|
49038
|
+
/* Explicit, never normal: minRows/maxRows are lengths in lh, and with
|
|
49039
|
+
line-height normal the lh unit resolves to a theoretical value that
|
|
49040
|
+
does not match the real rendered line — the box then jumps by a few
|
|
49041
|
+
pixels the moment the first character replaces the theory with a real
|
|
49042
|
+
line. One number for both keeps every row count exact. */
|
|
49043
|
+
line-height: 1.5;
|
|
49044
|
+
/* The control grows itself; resizable below hands the handle back. */
|
|
49045
|
+
resize: none;
|
|
49046
|
+
overflow: auto;
|
|
49047
|
+
}
|
|
49048
|
+
&[data-resizable] .navi_control_input {
|
|
49049
|
+
height: calc(var(--textarea-min-rows, 1.5) * 1lh);
|
|
49050
|
+
/* The two are exclusive: with field-sizing content the browser removes
|
|
49051
|
+
the resize handle (the size follows the content, there is nothing to
|
|
49052
|
+
drag). resizable means the hand takes over — fixed sizing, starting
|
|
49053
|
+
at minRows, and the drag writes its own inline height from there. */
|
|
49054
|
+
field-sizing: fixed;
|
|
49055
|
+
resize: vertical;
|
|
49056
|
+
}
|
|
49057
|
+
}
|
|
49058
|
+
.navi_textarea_char_count {
|
|
49059
|
+
color: color-mix(in srgb, currentColor 60%, transparent);
|
|
49060
|
+
font-size: 0.75em;
|
|
49061
|
+
user-select: none;
|
|
49062
|
+
}
|
|
49063
|
+
`;
|
|
49064
|
+
|
|
49065
|
+
/**
|
|
49066
|
+
* @type {import("ignore:preact").FunctionComponent<{
|
|
49067
|
+
* value?: string,
|
|
49068
|
+
* defaultValue?: string,
|
|
49069
|
+
* signal?: import("@preact/signals").Signal<string>,
|
|
49070
|
+
* name?: string,
|
|
49071
|
+
* minRows?: number,
|
|
49072
|
+
* maxRows?: number,
|
|
49073
|
+
* resizable?: boolean,
|
|
49074
|
+
* maxLength?: number,
|
|
49075
|
+
* width?: string,
|
|
49076
|
+
* [key: string]: any,
|
|
49077
|
+
* }>}
|
|
49078
|
+
* @param {number} [minRows=1.5] Height the empty control starts at, in lines.
|
|
49079
|
+
* The default shows half of a second line: enough to read "multiline" at a
|
|
49080
|
+
* glance without the height of a full extra row.
|
|
49081
|
+
* @param {number} [maxRows] Lines after which the control stops growing and
|
|
49082
|
+
* scrolls instead. Without it the control grows with its content.
|
|
49083
|
+
* @param {boolean} [resizable] Give the browser's vertical resize handle back.
|
|
49084
|
+
* A manual resize takes over from the automatic growth.
|
|
49085
|
+
* @param {number} [maxLength] The character limit, validated at submit. Pair
|
|
49086
|
+
* with `maxLengthGuard` to block typing past it, and render a
|
|
49087
|
+
* TextareaCharCount to show it.
|
|
49088
|
+
* @param {string} [width="35ch"] The control's width. Fixed on purpose: with
|
|
49089
|
+
* field-sizing the width would otherwise follow the longest line, and a box
|
|
49090
|
+
* that widens while one types is a box one chases.
|
|
49091
|
+
*/
|
|
49092
|
+
const Textarea = ({
|
|
49093
|
+
// Destructured, never deleted off the props object: Preact reuses the same
|
|
49094
|
+
// props object when an internal state update re-renders the component, so a
|
|
49095
|
+
// delete would make these props vanish from the second render on (the box
|
|
49096
|
+
// then jumps back to the default minRows at the first keystroke).
|
|
49097
|
+
minRows = 1.5,
|
|
49098
|
+
maxRows,
|
|
49099
|
+
resizable,
|
|
49100
|
+
width = "35ch",
|
|
49101
|
+
...props
|
|
49102
|
+
}) => {
|
|
49103
|
+
import.meta.css = [inputCss + css$n, "@jsenv/navi/src/control/input/textarea.jsx"];
|
|
49104
|
+
const defaultRef = useRef(null);
|
|
49105
|
+
props.ref = props.ref || defaultRef;
|
|
49106
|
+
const [rootProps, hostProps, childrenWrapperProps] = useControlProps(props, {
|
|
49107
|
+
controlType: "input"
|
|
49108
|
+
});
|
|
49109
|
+
const {
|
|
49110
|
+
basePseudoState,
|
|
49111
|
+
children
|
|
49112
|
+
} = hostProps;
|
|
49113
|
+
// Children go through ControlChildrenWrapper below; inside the <textarea>
|
|
49114
|
+
// element they would become its text content.
|
|
49115
|
+
delete hostProps.children;
|
|
49116
|
+
const loading = basePseudoState[":-navi-loading"];
|
|
49117
|
+
delete rootProps.width;
|
|
49118
|
+
hostProps.width = width;
|
|
49119
|
+
return jsxs(Box, {
|
|
49120
|
+
as: "span",
|
|
49121
|
+
inline: true,
|
|
49122
|
+
flex: true,
|
|
49123
|
+
baseClassName: "navi_input",
|
|
49124
|
+
className: "navi_textarea",
|
|
49125
|
+
...rootProps,
|
|
49126
|
+
basePseudoState: basePseudoState,
|
|
49127
|
+
"data-resizable": resizable ? "" : undefined,
|
|
49128
|
+
styleCSSVars: InputStyleCSSVars,
|
|
49129
|
+
pseudoStateSelector: ".navi_control_input",
|
|
49130
|
+
pseudoClasses: InputPseudoClasses,
|
|
49131
|
+
pseudoElements: InputPseudoElements,
|
|
49132
|
+
"data-callout-anchor": ".navi_control_input",
|
|
49133
|
+
style: {
|
|
49134
|
+
"--textarea-min-rows": minRows,
|
|
49135
|
+
"--textarea-max-rows": maxRows,
|
|
49136
|
+
...rootProps.style
|
|
49137
|
+
},
|
|
49138
|
+
children: [jsx(LoadingOutline, {
|
|
49139
|
+
loading: loading,
|
|
49140
|
+
color: "var(--loader-color)",
|
|
49141
|
+
inset: -1
|
|
49142
|
+
}), jsx(RealTextarea, {
|
|
49143
|
+
...hostProps
|
|
49144
|
+
}), jsx(ControlChildrenWrapper, {
|
|
49145
|
+
...childrenWrapperProps,
|
|
49146
|
+
children: children
|
|
49147
|
+
})]
|
|
49148
|
+
});
|
|
49149
|
+
};
|
|
49150
|
+
|
|
49151
|
+
/**
|
|
49152
|
+
* The counter that goes with a Textarea: "50/200" — how many characters are
|
|
49153
|
+
* typed over how many the limit allows, the way Material writes it (just the
|
|
49154
|
+
* count when there is no `maxLength`). Where it goes is the caller's call,
|
|
49155
|
+
* which is why it is a separate component rather than something the textarea
|
|
49156
|
+
* draws: put it under the box, in a form footer, next to a label, and feed it
|
|
49157
|
+
* the same value or signal as the textarea.
|
|
49158
|
+
*
|
|
49159
|
+
* @type {import("ignore:preact").FunctionComponent<{
|
|
49160
|
+
* value?: string,
|
|
49161
|
+
* signal?: import("@preact/signals").Signal<string>,
|
|
49162
|
+
* maxLength?: number,
|
|
49163
|
+
* [key: string]: any,
|
|
49164
|
+
* }>}
|
|
49165
|
+
* @param {string} [value] The text being counted. Say `signal` instead for a
|
|
49166
|
+
* two-way bound textarea: reading it here subscribes the count to it.
|
|
49167
|
+
* @param {number} [maxLength] The limit, shown after the count ("50/200").
|
|
49168
|
+
* Without it the count stands alone.
|
|
49169
|
+
*/
|
|
49170
|
+
const TextareaCharCount = ({
|
|
49171
|
+
value,
|
|
49172
|
+
signal,
|
|
49173
|
+
maxLength,
|
|
49174
|
+
...rest
|
|
49175
|
+
}) => {
|
|
49176
|
+
import.meta.css = [css$n, "@jsenv/navi/src/control/input/textarea.jsx"];
|
|
49177
|
+
const resolvedValue = signal ? signal.value : value;
|
|
49178
|
+
const length = typeof resolvedValue === "string" ? resolvedValue.length : 0;
|
|
49179
|
+
return jsx(Box, {
|
|
49180
|
+
as: "span",
|
|
49181
|
+
baseClassName: "navi_textarea_char_count",
|
|
49182
|
+
...rest,
|
|
49183
|
+
children: maxLength === undefined ? length : `${length}/${maxLength}`
|
|
49184
|
+
});
|
|
49185
|
+
};
|
|
49186
|
+
const RealTextarea = ({
|
|
49187
|
+
maxLength,
|
|
49188
|
+
...domProps
|
|
49189
|
+
}) => {
|
|
49190
|
+
const autoSelectReadOnlyProps = useAutoSelectReadOnly(domProps);
|
|
49191
|
+
return jsx(Box, {
|
|
49192
|
+
...domProps,
|
|
49193
|
+
as: "textarea",
|
|
49194
|
+
baseClassName: "navi_control_input",
|
|
49195
|
+
...autoSelectReadOnlyProps,
|
|
49196
|
+
// Native maxLength stays off, like RealInput in input_textual.jsx: the
|
|
49197
|
+
// maxLengthGuard handles live blocking, the constraint validates at
|
|
49198
|
+
// submit, and navi-max-length keeps the value readable from the DOM.
|
|
49199
|
+
"navi-max-length": maxLength
|
|
49200
|
+
});
|
|
49201
|
+
};
|
|
49202
|
+
|
|
48993
49203
|
const Unit = ({
|
|
48994
49204
|
unit,
|
|
48995
49205
|
plural,
|
|
@@ -58411,5 +58621,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
58411
58621
|
})
|
|
58412
58622
|
});
|
|
58413
58623
|
|
|
58414
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage, windowWidthSignal };
|
|
58624
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage, windowWidthSignal };
|
|
58415
58625
|
//# sourceMappingURL=jsenv_navi.js.map
|