@jsenv/navi 0.28.11 → 0.29.0
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 +226 -26
- package/dist/jsenv_navi.js.map +10 -4
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -35088,6 +35088,9 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
35088
35088
|
* a solid surface. variant="discrete-border" does the same but keeps the border.
|
|
35089
35089
|
* - variant="discrete" + backgroundColor: the color applies at rest and hover,
|
|
35090
35090
|
* and the field goes transparent while focused.
|
|
35091
|
+
*
|
|
35092
|
+
* variant="text" is the odd one: it renders no <input> at all, just the value
|
|
35093
|
+
* as text — see InputTextualAsText below for what it is for and what it drops.
|
|
35091
35094
|
*/
|
|
35092
35095
|
const inputCss = /* css */`
|
|
35093
35096
|
@layer navi {
|
|
@@ -35184,7 +35187,12 @@ const inputCss = /* css */`
|
|
|
35184
35187
|
cursor: inherit;
|
|
35185
35188
|
pointer-events: auto;
|
|
35186
35189
|
|
|
35187
|
-
|
|
35190
|
+
/* The text of variant="text" is measured with the real thing rather than
|
|
35191
|
+
beside it: same padding, same margins, same room — so the two are the
|
|
35192
|
+
same box and a field's style can move without one of them staying
|
|
35193
|
+
behind. */
|
|
35194
|
+
.navi_control_input,
|
|
35195
|
+
.navi_input_text {
|
|
35188
35196
|
box-sizing: content-box;
|
|
35189
35197
|
min-width: 1ch;
|
|
35190
35198
|
margin-top: calc(-1 * var(--x-padding-top));
|
|
@@ -35196,6 +35204,10 @@ const inputCss = /* css */`
|
|
|
35196
35204
|
flex-grow: 1;
|
|
35197
35205
|
color: inherit;
|
|
35198
35206
|
font-size: inherit;
|
|
35207
|
+
/* A form control does not inherit the font on its own — the browser has
|
|
35208
|
+
one of its own for it, monospace for a <textarea> — so the box's font
|
|
35209
|
+
(--navi-control-font-family) is handed down by hand. */
|
|
35210
|
+
font-family: inherit;
|
|
35199
35211
|
text-align: inherit;
|
|
35200
35212
|
background: none;
|
|
35201
35213
|
border: none;
|
|
@@ -35228,6 +35240,34 @@ const inputCss = /* css */`
|
|
|
35228
35240
|
}
|
|
35229
35241
|
}
|
|
35230
35242
|
|
|
35243
|
+
.navi_input_text {
|
|
35244
|
+
/* Nothing to read yet is still a line: the box may not collapse just
|
|
35245
|
+
because the value is empty. */
|
|
35246
|
+
min-height: 1lh;
|
|
35247
|
+
/* A form control keeps a line of its own whatever line-height the page
|
|
35248
|
+
is written in; the text that stands in for one has to say the same
|
|
35249
|
+
number, or the box it is meant to match changes height. */
|
|
35250
|
+
line-height: normal;
|
|
35251
|
+
}
|
|
35252
|
+
/* The value is cut by a box of its own, and that is the whole reason it
|
|
35253
|
+
exists: a field ends its text at the content edge, while an overflow set
|
|
35254
|
+
on the padded box would let it run through the padding — the same value
|
|
35255
|
+
would then lose a character on one side and not on the other. This box
|
|
35256
|
+
IS the content edge, so both stop on the same letter.
|
|
35257
|
+
Cut rather than wrapped (a second line would be taller than the field),
|
|
35258
|
+
and cut rather than ellipsed (an ellipsis costs a character the field
|
|
35259
|
+
does not lose). */
|
|
35260
|
+
.navi_input_text_value {
|
|
35261
|
+
display: block;
|
|
35262
|
+
/* The whole content box, short value or not: a field's text is laid out
|
|
35263
|
+
in the full width whatever it holds, so text-align lands in the same
|
|
35264
|
+
place on both. */
|
|
35265
|
+
width: 100%;
|
|
35266
|
+
text-overflow: clip;
|
|
35267
|
+
white-space: nowrap;
|
|
35268
|
+
overflow: hidden;
|
|
35269
|
+
}
|
|
35270
|
+
|
|
35231
35271
|
.navi_input_slot {
|
|
35232
35272
|
margin-right: var(--slot-spacing, calc(2px + 0.1em));
|
|
35233
35273
|
margin-left: var(--slot-spacing, calc(2px + 0.1em));
|
|
@@ -35337,6 +35377,16 @@ const inputCss = /* css */`
|
|
|
35337
35377
|
}
|
|
35338
35378
|
}
|
|
35339
35379
|
|
|
35380
|
+
/* The box of a field, without a field in it: the border is kept and made
|
|
35381
|
+
invisible rather than dropped, and the background goes with it, so what
|
|
35382
|
+
is left is text sitting exactly where the value would be — swap one for
|
|
35383
|
+
the other and nothing on the page moves. */
|
|
35384
|
+
&[data-variant="text"] {
|
|
35385
|
+
--x-background-color: transparent;
|
|
35386
|
+
--x-border-color: transparent;
|
|
35387
|
+
cursor: inherit;
|
|
35388
|
+
}
|
|
35389
|
+
|
|
35340
35390
|
&[data-variant="underline"] {
|
|
35341
35391
|
border: none;
|
|
35342
35392
|
border-radius: 0;
|
|
@@ -35462,12 +35512,9 @@ const InputTextualUI = props => {
|
|
|
35462
35512
|
// when remainingProps contains expandX which would try to set width to 100%
|
|
35463
35513
|
delete inputControlRootProps.width;
|
|
35464
35514
|
if (width === "maxLength") {
|
|
35465
|
-
const
|
|
35466
|
-
|
|
35467
|
-
|
|
35468
|
-
if (maxLength !== undefined) {
|
|
35469
|
-
const isNumeric = props.inputMode === "numeric";
|
|
35470
|
-
inputControlHostProps.width = isNumeric ? `${maxLength}ch` : `calc(${maxLength} * 1.5ch)`;
|
|
35515
|
+
const widthFromMaxLength = resolveWidthFromMaxLength(inputControlHostProps.maxLength, props.inputMode);
|
|
35516
|
+
if (widthFromMaxLength !== undefined) {
|
|
35517
|
+
inputControlHostProps.width = widthFromMaxLength;
|
|
35471
35518
|
}
|
|
35472
35519
|
} else if (width === "content") {
|
|
35473
35520
|
inputControlHostProps.fieldSizing = "content";
|
|
@@ -35510,6 +35557,96 @@ const InputTextualUI = props => {
|
|
|
35510
35557
|
}), childrenWithContext]
|
|
35511
35558
|
});
|
|
35512
35559
|
};
|
|
35560
|
+
// How wide a field is when its width is left to what it accepts: a value that
|
|
35561
|
+
// cannot exceed maxLength characters needs no more room than that. Shared with
|
|
35562
|
+
// the text variant, which must land on the same number or the two would not be
|
|
35563
|
+
// the same box.
|
|
35564
|
+
const resolveWidthFromMaxLength = (maxLength, inputMode) => {
|
|
35565
|
+
if (maxLength === undefined) {
|
|
35566
|
+
return undefined;
|
|
35567
|
+
}
|
|
35568
|
+
if (inputMode === "numeric") {
|
|
35569
|
+
return `${maxLength}ch`;
|
|
35570
|
+
}
|
|
35571
|
+
return `calc(${maxLength} * 1.5ch)`;
|
|
35572
|
+
};
|
|
35573
|
+
|
|
35574
|
+
/**
|
|
35575
|
+
* variant="text" — the value, written where the field would be and taking
|
|
35576
|
+
* exactly its room: same paddings, same font, same line, and the border kept
|
|
35577
|
+
* but made invisible, so a value one only reads and the same value being
|
|
35578
|
+
* edited are one box. What it is for: an information that is sometimes known
|
|
35579
|
+
* (a name already on the profile) and sometimes asked for. Swapping the field
|
|
35580
|
+
* for its text must move nothing under it.
|
|
35581
|
+
*
|
|
35582
|
+
* It is text, and nothing else: no <input>, so nothing to focus, nothing in
|
|
35583
|
+
* the tab order, and nothing sent when the form is submitted — a value the
|
|
35584
|
+
* form must carry goes in an <Input type="hidden"> beside this one. Not a
|
|
35585
|
+
* disabled control either: `disabled`/`aria-disabled` would announce a field
|
|
35586
|
+
* one cannot use, where there is no field at all.
|
|
35587
|
+
*
|
|
35588
|
+
* The field's own props are dropped rather than half-honoured (placeholder,
|
|
35589
|
+
* the guards, the slots): they all describe an edition that does not happen
|
|
35590
|
+
* here. What is kept is what decides the box.
|
|
35591
|
+
*/
|
|
35592
|
+
// Everything a field takes that a text does not: what the value is said with,
|
|
35593
|
+
// what the box is measured from (read below, then dropped too), and all the
|
|
35594
|
+
// rest — the guards, the slots, the constraints — which describe an edition
|
|
35595
|
+
// that does not happen here. What survives is what a Box understands: width,
|
|
35596
|
+
// spacing, colors, className, style.
|
|
35597
|
+
const INPUT_ONLY_PROPS = ["value", "defaultValue", "signal", "id", "maxLength", "inputMode", "width", "variant", "type", "name", "placeholder", "required", "readOnly", "disabled", "loading", "error", "min", "max", "step", "pattern", "autoComplete", "autoCorrect", "spellcheck", "charGuard", "maxLengthGuard", "list", "suggestions", "headless", "fieldSizing", "action", "uiAction", "ui", "children"];
|
|
35598
|
+
const InputTextualAsText = props => {
|
|
35599
|
+
import.meta.css = [inputCss, "@jsenv/navi/src/control/input/input_textual.jsx"];
|
|
35600
|
+
// The id a Field handed down, which is what its Label points at.
|
|
35601
|
+
const controlId = useContext(ControlIdContext);
|
|
35602
|
+
const {
|
|
35603
|
+
value,
|
|
35604
|
+
defaultValue,
|
|
35605
|
+
signal,
|
|
35606
|
+
id,
|
|
35607
|
+
maxLength,
|
|
35608
|
+
inputMode,
|
|
35609
|
+
width = "maxLength"
|
|
35610
|
+
} = props;
|
|
35611
|
+
const valueShown = signal ? signal.value : value ?? defaultValue;
|
|
35612
|
+
const textWidth = width === "maxLength" ? resolveWidthFromMaxLength(maxLength, inputMode) : width === "content" ? undefined : width;
|
|
35613
|
+
const boxProps = {
|
|
35614
|
+
...props
|
|
35615
|
+
};
|
|
35616
|
+
for (const inputOnlyProp of INPUT_ONLY_PROPS) {
|
|
35617
|
+
delete boxProps[inputOnlyProp];
|
|
35618
|
+
}
|
|
35619
|
+
return jsx(Box, {
|
|
35620
|
+
as: "span",
|
|
35621
|
+
inline: true,
|
|
35622
|
+
flex: true,
|
|
35623
|
+
baseClassName: "navi_input",
|
|
35624
|
+
"data-variant": "text",
|
|
35625
|
+
styleCSSVars: InputStyleCSSVars,
|
|
35626
|
+
...boxProps,
|
|
35627
|
+
children: jsx(Box, {
|
|
35628
|
+
as: "span",
|
|
35629
|
+
baseClassName: "navi_input_text",
|
|
35630
|
+
id: id || controlId,
|
|
35631
|
+
width: textWidth,
|
|
35632
|
+
children: jsx("span", {
|
|
35633
|
+
className: "navi_input_text_value",
|
|
35634
|
+
children: valueShown
|
|
35635
|
+
})
|
|
35636
|
+
})
|
|
35637
|
+
});
|
|
35638
|
+
};
|
|
35639
|
+
const InputTextualAsTextResolver = props => {
|
|
35640
|
+
const Next = useNextResolver();
|
|
35641
|
+
if (props.variant === "text") {
|
|
35642
|
+
return jsx(InputTextualAsText, {
|
|
35643
|
+
...props
|
|
35644
|
+
});
|
|
35645
|
+
}
|
|
35646
|
+
return jsx(Next, {
|
|
35647
|
+
...props
|
|
35648
|
+
});
|
|
35649
|
+
};
|
|
35513
35650
|
const InputTextualFirstResolver = props => {
|
|
35514
35651
|
const Next = useNextResolver();
|
|
35515
35652
|
const defaultRef = useRef(null);
|
|
@@ -35518,7 +35655,7 @@ const InputTextualFirstResolver = props => {
|
|
|
35518
35655
|
...props
|
|
35519
35656
|
});
|
|
35520
35657
|
};
|
|
35521
|
-
const InputTextual = createComponentResolver([InputTextualFirstResolver, InputWithListResolver, InputWithSuggestionsResolver, InputTypeResolver, InputModeResolver, InputHeadlessResolver, InputTextualUI]);
|
|
35658
|
+
const InputTextual = createComponentResolver([InputTextualAsTextResolver, InputTextualFirstResolver, InputWithListResolver, InputWithSuggestionsResolver, InputTypeResolver, InputModeResolver, InputHeadlessResolver, InputTextualUI]);
|
|
35522
35659
|
const RealInput = ({
|
|
35523
35660
|
maxLength,
|
|
35524
35661
|
...domProps
|
|
@@ -48356,7 +48493,49 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
48356
48493
|
* slide that is about to leave.
|
|
48357
48494
|
*/
|
|
48358
48495
|
const css$p = /* css */`
|
|
48496
|
+
@layer navi {
|
|
48497
|
+
.navi_picker_spin {
|
|
48498
|
+
/* A picker one steps through is still a picker: what themes every picker
|
|
48499
|
+
padding themes this one too. */
|
|
48500
|
+
--picker-spin-padding-x-default: var(--navi-picker-padding-x-default);
|
|
48501
|
+
--picker-spin-padding-y-default: var(--navi-picker-padding-y-default);
|
|
48502
|
+
}
|
|
48503
|
+
}
|
|
48504
|
+
|
|
48359
48505
|
.navi_picker_spin {
|
|
48506
|
+
/* The padding is written on what is inside the box rather than on the box
|
|
48507
|
+
— the day takes all four sides, the two chevrons only the vertical ones
|
|
48508
|
+
— so the four sides are resolved once here, in the side-then-axis-then
|
|
48509
|
+
-shorthand order a Box resolves them in. What writes them: the padding
|
|
48510
|
+
props, through PICKER_SPIN_STYLE_CSS_VARS below. */
|
|
48511
|
+
--x-picker-spin-padding-top: var(
|
|
48512
|
+
--picker-spin-padding-top,
|
|
48513
|
+
var(
|
|
48514
|
+
--picker-spin-padding-y,
|
|
48515
|
+
var(--picker-spin-padding, var(--picker-spin-padding-y-default))
|
|
48516
|
+
)
|
|
48517
|
+
);
|
|
48518
|
+
--x-picker-spin-padding-right: var(
|
|
48519
|
+
--picker-spin-padding-right,
|
|
48520
|
+
var(
|
|
48521
|
+
--picker-spin-padding-x,
|
|
48522
|
+
var(--picker-spin-padding, var(--picker-spin-padding-x-default))
|
|
48523
|
+
)
|
|
48524
|
+
);
|
|
48525
|
+
--x-picker-spin-padding-bottom: var(
|
|
48526
|
+
--picker-spin-padding-bottom,
|
|
48527
|
+
var(
|
|
48528
|
+
--picker-spin-padding-y,
|
|
48529
|
+
var(--picker-spin-padding, var(--picker-spin-padding-y-default))
|
|
48530
|
+
)
|
|
48531
|
+
);
|
|
48532
|
+
--x-picker-spin-padding-left: var(
|
|
48533
|
+
--picker-spin-padding-left,
|
|
48534
|
+
var(
|
|
48535
|
+
--picker-spin-padding-x,
|
|
48536
|
+
var(--picker-spin-padding, var(--picker-spin-padding-x-default))
|
|
48537
|
+
)
|
|
48538
|
+
);
|
|
48360
48539
|
/* What the loading outline is drawn around. */
|
|
48361
48540
|
position: relative;
|
|
48362
48541
|
/* Written in the control font, like the picker it wraps: the day and its
|
|
@@ -48437,17 +48616,19 @@ const css$p = /* css */`
|
|
|
48437
48616
|
width: min(100%, var(--picker-spin-picker-width, 12ch));
|
|
48438
48617
|
translate: -50% 0;
|
|
48439
48618
|
}
|
|
48440
|
-
/*
|
|
48441
|
-
|
|
48442
|
-
|
|
48443
|
-
|
|
48444
|
-
|
|
48445
|
-
|
|
48446
|
-
|
|
48447
|
-
}
|
|
48448
|
-
/* Centred, always: the middle holds three values of three different lengths,
|
|
48619
|
+
/* Where the padding lands: all four sides on the value, the two vertical
|
|
48620
|
+
ones on the chevrons below — the same number above and below is what makes
|
|
48621
|
+
the three one line rather than three boxes, while sideways it is the room
|
|
48622
|
+
between the value and the chevron it would otherwise touch. Not on the box
|
|
48623
|
+
itself: that would push the chevrons off the corners they are rounded by.
|
|
48624
|
+
|
|
48625
|
+
Centred, always: the middle holds three values of three different lengths,
|
|
48449
48626
|
and a value that starts where the last one ended reads as a jump. */
|
|
48450
48627
|
.navi_picker_spin [data-slide] {
|
|
48628
|
+
padding-top: var(--x-picker-spin-padding-top);
|
|
48629
|
+
padding-right: var(--x-picker-spin-padding-right);
|
|
48630
|
+
padding-bottom: var(--x-picker-spin-padding-bottom);
|
|
48631
|
+
padding-left: var(--x-picker-spin-padding-left);
|
|
48451
48632
|
text-align: center;
|
|
48452
48633
|
overflow: hidden;
|
|
48453
48634
|
}
|
|
@@ -48474,8 +48655,12 @@ const css$p = /* css */`
|
|
|
48474
48655
|
around it, so "one line" means the same on both sides. */
|
|
48475
48656
|
.navi_picker_spin > .navi_picker_spin_way_out {
|
|
48476
48657
|
box-sizing: border-box;
|
|
48477
|
-
height: calc(
|
|
48478
|
-
|
|
48658
|
+
height: calc(
|
|
48659
|
+
1lh + var(--x-picker-spin-padding-top) +
|
|
48660
|
+
var(--x-picker-spin-padding-bottom)
|
|
48661
|
+
);
|
|
48662
|
+
padding-top: var(--x-picker-spin-padding-top);
|
|
48663
|
+
padding-bottom: var(--x-picker-spin-padding-bottom);
|
|
48479
48664
|
color: inherit;
|
|
48480
48665
|
background: none;
|
|
48481
48666
|
border: none;
|
|
@@ -48556,8 +48741,13 @@ const css$p = /* css */`
|
|
|
48556
48741
|
* @param {"long"|"short"|"numeric"} [format="long"] How the date is written.
|
|
48557
48742
|
* Long by default, since this is a control one reads rather than a column
|
|
48558
48743
|
* one scans; `short` where the room is not there.
|
|
48559
|
-
* @param {string} [
|
|
48560
|
-
*
|
|
48744
|
+
* @param {string} [padding] The room around the value, `paddingX`/`paddingY`
|
|
48745
|
+
* and the four sides included. It goes on what is inside the box rather than
|
|
48746
|
+
* on the box: above and below it is taken by the day AND by the two chevrons,
|
|
48747
|
+
* which is what makes the three the same height; sideways it is the room
|
|
48748
|
+
* between the day and the chevron beside it. Left unsaid it is the padding
|
|
48749
|
+
* every picker takes (`--navi-picker-padding-x-default` and its `-y` twin),
|
|
48750
|
+
* so a theme that spaces its fields spaces this one with them.
|
|
48561
48751
|
* @param {number} [maxLines] How many lines the day may take before it is cut
|
|
48562
48752
|
* with an ellipsis — `maxLines={1}` keeps it on one line. Without it a day
|
|
48563
48753
|
* too long for the box wraps, and the box grows.
|
|
@@ -48583,7 +48773,6 @@ const DaySpin = ({
|
|
|
48583
48773
|
duration = 250,
|
|
48584
48774
|
lang,
|
|
48585
48775
|
format = "long",
|
|
48586
|
-
paddingY = "xs",
|
|
48587
48776
|
vertical,
|
|
48588
48777
|
readOnly,
|
|
48589
48778
|
disabled,
|
|
@@ -48727,13 +48916,10 @@ const DaySpin = ({
|
|
|
48727
48916
|
,
|
|
48728
48917
|
|
|
48729
48918
|
pseudoClasses: PICKER_SPIN_PSEUDO_CLASSES,
|
|
48919
|
+
styleCSSVars: PICKER_SPIN_STYLE_CSS_VARS,
|
|
48730
48920
|
"data-vertical": vertical ? "" : undefined,
|
|
48731
48921
|
"data-readonly": readOnlyResolved ? "" : undefined,
|
|
48732
48922
|
"data-disabled": disabledResolved ? "" : undefined,
|
|
48733
|
-
style: {
|
|
48734
|
-
"--picker-spin-padding-y": resolveSpacingSize(paddingY),
|
|
48735
|
-
...rest.style
|
|
48736
|
-
},
|
|
48737
48923
|
children: [jsx(LoadingOutline, {
|
|
48738
48924
|
loading: loading,
|
|
48739
48925
|
color: "var(--navi-loader-color)",
|
|
@@ -48915,6 +49101,20 @@ const WayOut = ({
|
|
|
48915
49101
|
});
|
|
48916
49102
|
const PICKER_SPIN_PSEUDO_CLASSES = [":hover", ":focus-visible"];
|
|
48917
49103
|
|
|
49104
|
+
// A padding written on this box would sit between its border and the chevrons,
|
|
49105
|
+
// which are meant to reach the corners they are rounded by — so each padding
|
|
49106
|
+
// prop becomes a variable instead, and the CSS above hands it to the day and to
|
|
49107
|
+
// the chevrons themselves. Same trick, and same var chain, as Picker's.
|
|
49108
|
+
const PICKER_SPIN_STYLE_CSS_VARS = {
|
|
49109
|
+
padding: "--picker-spin-padding",
|
|
49110
|
+
paddingX: "--picker-spin-padding-x",
|
|
49111
|
+
paddingY: "--picker-spin-padding-y",
|
|
49112
|
+
paddingTop: "--picker-spin-padding-top",
|
|
49113
|
+
paddingRight: "--picker-spin-padding-right",
|
|
49114
|
+
paddingBottom: "--picker-spin-padding-bottom",
|
|
49115
|
+
paddingLeft: "--picker-spin-padding-left"
|
|
49116
|
+
};
|
|
49117
|
+
|
|
48918
49118
|
// The date, and what it is to today when that is something one has a word for:
|
|
48919
49119
|
// "samedi 8 août (demain)" says both where one is and how far that is, and only
|
|
48920
49120
|
// the second is read at a glance.
|