@jsenv/navi 0.27.82 → 0.27.83
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 +86 -35
- package/dist/jsenv_navi.js.map +5 -5
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -18968,6 +18968,17 @@ CONSTRAINT_ATTRIBUTE_SET.add("data-single-space");
|
|
|
18968
18968
|
*/
|
|
18969
18969
|
|
|
18970
18970
|
|
|
18971
|
+
// Our own compact/custom duration notation interpolates raw numbers
|
|
18972
|
+
// directly (unlike Intl.DurationFormat, which groups thousands on its own,
|
|
18973
|
+
// e.g. "5 400 secondes") — this keeps that consistent without reimplementing
|
|
18974
|
+
// locale-aware grouping. Falls back to the raw value as-is for a
|
|
18975
|
+
// non-numeric mid-edit value (e.g. "2a"), which Intl.NumberFormat can't
|
|
18976
|
+
// format anyway.
|
|
18977
|
+
const formatCompactNumber = (value, lang) => {
|
|
18978
|
+
const n = Number(value);
|
|
18979
|
+
return Number.isFinite(n) ? new Intl.NumberFormat(lang).format(n) : value;
|
|
18980
|
+
};
|
|
18981
|
+
|
|
18971
18982
|
/**
|
|
18972
18983
|
* Formats a date as a human-readable day string.
|
|
18973
18984
|
*
|
|
@@ -19221,15 +19232,20 @@ const formatTime = (date, lang) => {
|
|
|
19221
19232
|
* "compact" uses our own notation that omits the minute symbol when hours are present.
|
|
19222
19233
|
*
|
|
19223
19234
|
* @param {number} minutes
|
|
19224
|
-
* @param {{ lang?: string, format?: "long"|"short"|"narrow"|"compact",
|
|
19225
|
-
* @param {boolean} [options.
|
|
19226
|
-
*
|
|
19227
|
-
*
|
|
19228
|
-
*
|
|
19229
|
-
*
|
|
19230
|
-
*
|
|
19231
|
-
*
|
|
19232
|
-
*
|
|
19235
|
+
* @param {{ lang?: string, format?: "long"|"short"|"narrow"|"compact", clockStyle?: boolean }} [options]
|
|
19236
|
+
* @param {boolean} [options.clockStyle=false] - Set this when `minutes`
|
|
19237
|
+
* represents a time-of-day rather than a real duration (used by
|
|
19238
|
+
* `<Time type="time">`, see time.jsx's own TimeTime) — affects two
|
|
19239
|
+
* things at once, both consequences of a clock's "0" being a meaningful
|
|
19240
|
+
* hour rather than "no hours":
|
|
19241
|
+
* - a zero-hours component is normally dropped entirely (a real 5-minute
|
|
19242
|
+
* duration should print as "5 minutes", not "0 hours 5 minutes"); this
|
|
19243
|
+
* keeps it instead (e.g. "0 h et 5 min"/"0h 5min"/"00h05") so midnight
|
|
19244
|
+
* doesn't collapse to something indistinguishable from an actual
|
|
19245
|
+
* 5-minute duration.
|
|
19246
|
+
* - `format: "compact"` also zero-pads a single-digit hour to 2 digits
|
|
19247
|
+
* (e.g. "5h30" → "05h30"), so it reads closer to a "05:30" clock.
|
|
19248
|
+
* Must not be set for plain duration formatting.
|
|
19233
19249
|
*
|
|
19234
19250
|
* @example
|
|
19235
19251
|
* formatMinuteDuration(90, { lang: "fr" }) // "1 heure 30 minutes" (long, default)
|
|
@@ -19237,20 +19253,21 @@ const formatTime = (date, lang) => {
|
|
|
19237
19253
|
* formatMinuteDuration(90, { lang: "fr", format: "narrow" }) // "1h 30min" (Intl narrow)
|
|
19238
19254
|
* formatMinuteDuration(90, { lang: "fr", format: "compact" }) // "1h30" (custom, no minute symbol)
|
|
19239
19255
|
* formatMinuteDuration(45, { lang: "en", format: "compact" }) // "45min"
|
|
19240
|
-
* formatMinuteDuration(5, { lang: "fr", format: "narrow",
|
|
19256
|
+
* formatMinuteDuration(5, { lang: "fr", format: "narrow", clockStyle: true }) // "0h 5min"
|
|
19257
|
+
* formatMinuteDuration(330, { lang: "fr", format: "compact", clockStyle: true }) // "05h30"
|
|
19241
19258
|
*/
|
|
19242
19259
|
const formatMinuteDuration = (
|
|
19243
19260
|
minutes,
|
|
19244
|
-
{ lang = languagesSignal.value, format = "long",
|
|
19261
|
+
{ lang = languagesSignal.value, format = "long", clockStyle = false } = {},
|
|
19245
19262
|
) => {
|
|
19246
19263
|
const h = Math.floor(minutes / 60);
|
|
19247
19264
|
const m = minutes % 60;
|
|
19248
19265
|
if (format !== "compact" && typeof Intl.DurationFormat !== "undefined") {
|
|
19249
19266
|
const fmt = new Intl.DurationFormat(lang, {
|
|
19250
19267
|
style: format, // "long", "short", or "narrow"
|
|
19251
|
-
...(
|
|
19268
|
+
...(clockStyle ? { hoursDisplay: "always" } : {}),
|
|
19252
19269
|
});
|
|
19253
|
-
if (h === 0 && !
|
|
19270
|
+
if (h === 0 && !clockStyle) {
|
|
19254
19271
|
return fmt.format({ minutes: m });
|
|
19255
19272
|
}
|
|
19256
19273
|
if (m === 0) {
|
|
@@ -19261,13 +19278,16 @@ const formatMinuteDuration = (
|
|
|
19261
19278
|
// format="compact": "1h30", "45min", "2h" — no minute symbol when hours are present
|
|
19262
19279
|
const hSym = naviI18n("time.duration.hour_symbol", undefined, { lang });
|
|
19263
19280
|
const mSym = naviI18n("time.duration.minute_symbol", undefined, { lang });
|
|
19264
|
-
|
|
19281
|
+
const hStr = clockStyle
|
|
19282
|
+
? String(h).padStart(2, "0")
|
|
19283
|
+
: formatCompactNumber(h, lang);
|
|
19284
|
+
if (h === 0 && !clockStyle) {
|
|
19265
19285
|
return `${m}${mSym}`;
|
|
19266
19286
|
}
|
|
19267
19287
|
if (m === 0) {
|
|
19268
|
-
return `${
|
|
19288
|
+
return `${hStr}${hSym}`;
|
|
19269
19289
|
}
|
|
19270
|
-
return `${
|
|
19290
|
+
return `${hStr}${hSym}${String(m).padStart(2, "0")}`;
|
|
19271
19291
|
};
|
|
19272
19292
|
|
|
19273
19293
|
/**
|
|
@@ -19322,7 +19342,9 @@ const formatSecondDuration = (
|
|
|
19322
19342
|
const mSym = naviI18n("time.duration.minute_symbol", undefined, { lang });
|
|
19323
19343
|
const sSym = naviI18n("time.duration.second_symbol", undefined, { lang });
|
|
19324
19344
|
const parts = [];
|
|
19325
|
-
|
|
19345
|
+
// m/s are always 0-59 by construction (never need grouping); h can be
|
|
19346
|
+
// arbitrarily large for a long duration.
|
|
19347
|
+
if (h > 0) parts.push(`${formatCompactNumber(h, lang)}${hSym}`);
|
|
19326
19348
|
if (m > 0) parts.push(`${m}${mSym}`);
|
|
19327
19349
|
if (s > 0 || parts.length === 0) parts.push(`${s}${sSym}`);
|
|
19328
19350
|
return parts.join("");
|
|
@@ -19423,36 +19445,40 @@ const formatDuration = (
|
|
|
19423
19445
|
const parts = [];
|
|
19424
19446
|
|
|
19425
19447
|
if (hasNonZero("years")) {
|
|
19426
|
-
parts.push(`${duration.years}${sym("year")}`);
|
|
19448
|
+
parts.push(`${formatCompactNumber(duration.years, lang)}${sym("year")}`);
|
|
19427
19449
|
}
|
|
19428
19450
|
if (hasNonZero("months")) {
|
|
19429
|
-
parts.push(`${duration.months}${sym("month")}`);
|
|
19451
|
+
parts.push(`${formatCompactNumber(duration.months, lang)}${sym("month")}`);
|
|
19430
19452
|
}
|
|
19431
19453
|
if (hasNonZero("weeks")) {
|
|
19432
|
-
parts.push(`${duration.weeks}${sym("week")}`);
|
|
19454
|
+
parts.push(`${formatCompactNumber(duration.weeks, lang)}${sym("week")}`);
|
|
19433
19455
|
}
|
|
19434
19456
|
if (hasNonZero("days")) {
|
|
19435
|
-
parts.push(`${duration.days}${sym("day")}`);
|
|
19457
|
+
parts.push(`${formatCompactNumber(duration.days, lang)}${sym("day")}`);
|
|
19436
19458
|
}
|
|
19437
19459
|
|
|
19438
|
-
// Hours + minutes: when both present, pad minutes to 2 digits after the h
|
|
19460
|
+
// Hours + minutes: when both present, pad minutes to 2 digits after the h
|
|
19461
|
+
// symbol — minutes stays a plain 2-digit pad (it's always 0-59 by
|
|
19462
|
+
// convention), only hours goes through grouping.
|
|
19439
19463
|
const hSym = sym("hour");
|
|
19440
19464
|
const mSym = sym("minute");
|
|
19441
19465
|
if (hasNonZero("hours") && hasNonZero("minutes")) {
|
|
19442
19466
|
parts.push(
|
|
19443
|
-
`${duration.hours}${hSym}${String(duration.minutes).padStart(2, "0")}`,
|
|
19467
|
+
`${formatCompactNumber(duration.hours, lang)}${hSym}${String(duration.minutes).padStart(2, "0")}`,
|
|
19444
19468
|
);
|
|
19445
19469
|
} else if (hasNonZero("hours")) {
|
|
19446
|
-
parts.push(`${duration.hours}${hSym}`);
|
|
19470
|
+
parts.push(`${formatCompactNumber(duration.hours, lang)}${hSym}`);
|
|
19447
19471
|
} else if (hasNonZero("minutes")) {
|
|
19448
|
-
parts.push(`${duration.minutes}${mSym}`);
|
|
19472
|
+
parts.push(`${formatCompactNumber(duration.minutes, lang)}${mSym}`);
|
|
19449
19473
|
}
|
|
19450
19474
|
|
|
19451
19475
|
if (hasNonZero("seconds")) {
|
|
19452
|
-
parts.push(`${duration.seconds}${sym("second")}`);
|
|
19476
|
+
parts.push(`${formatCompactNumber(duration.seconds, lang)}${sym("second")}`);
|
|
19453
19477
|
}
|
|
19454
19478
|
if (hasNonZero("milliseconds")) {
|
|
19455
|
-
parts.push(
|
|
19479
|
+
parts.push(
|
|
19480
|
+
`${formatCompactNumber(duration.milliseconds, lang)}${sym("millisecond")}`,
|
|
19481
|
+
);
|
|
19456
19482
|
}
|
|
19457
19483
|
return parts.join("") || "0";
|
|
19458
19484
|
};
|
|
@@ -33397,7 +33423,21 @@ const InputTextualWithSuggestions = props => {
|
|
|
33397
33423
|
// Nice side effect is that input_group.jsx will see all input is selected
|
|
33398
33424
|
// and arrow left/right will always nav between inputs.
|
|
33399
33425
|
// (Otherwise we would prevent left/right + show calllout about readonly)
|
|
33426
|
+
//
|
|
33427
|
+
// Not on touch: .select() there triggers the native mobile text-selection UI
|
|
33428
|
+
// (handles + magnifier), which makes no sense for a picker (opens a
|
|
33429
|
+
// popover/dialog on tap, not meant for text selection) and isn't wanted on a
|
|
33430
|
+
// plain readonly text input either. onPointerDown tracks the pointer type
|
|
33431
|
+
// that initiated the interaction (same convention as button_ui.jsx's own
|
|
33432
|
+
// `e.pointerType !== "touch"` check) so the focus handler — which fires
|
|
33433
|
+
// right after, but as a plain FocusEvent with no pointerType of its own —
|
|
33434
|
+
// can also skip select() for that same interaction.
|
|
33400
33435
|
const useAutoSelectReadOnly = (props) => {
|
|
33436
|
+
const lastPointerTypeRef = useRef(null);
|
|
33437
|
+
const onPointerDown = (e) => {
|
|
33438
|
+
props.onPointerDown?.(e);
|
|
33439
|
+
lastPointerTypeRef.current = e.pointerType;
|
|
33440
|
+
};
|
|
33401
33441
|
const onFocus = (e) => {
|
|
33402
33442
|
props.onFocus(e);
|
|
33403
33443
|
if (e.defaultPrevented) {
|
|
@@ -33406,6 +33446,9 @@ const useAutoSelectReadOnly = (props) => {
|
|
|
33406
33446
|
if (!e.target.readOnly) {
|
|
33407
33447
|
return;
|
|
33408
33448
|
}
|
|
33449
|
+
if (lastPointerTypeRef.current === "touch") {
|
|
33450
|
+
return;
|
|
33451
|
+
}
|
|
33409
33452
|
e.preventDefault();
|
|
33410
33453
|
e.target.select();
|
|
33411
33454
|
};
|
|
@@ -33417,11 +33460,14 @@ const useAutoSelectReadOnly = (props) => {
|
|
|
33417
33460
|
if (!e.target.readOnly) {
|
|
33418
33461
|
return;
|
|
33419
33462
|
}
|
|
33463
|
+
if (lastPointerTypeRef.current === "touch") {
|
|
33464
|
+
return;
|
|
33465
|
+
}
|
|
33420
33466
|
e.preventDefault();
|
|
33421
33467
|
e.target.select();
|
|
33422
33468
|
};
|
|
33423
33469
|
|
|
33424
|
-
return { onFocus, onMouseDown };
|
|
33470
|
+
return { onFocus, onMouseDown, onPointerDown };
|
|
33425
33471
|
};
|
|
33426
33472
|
|
|
33427
33473
|
installImportMetaCssBuild(import.meta);/**
|
|
@@ -39974,22 +40020,27 @@ const TimeTime = ({
|
|
|
39974
40020
|
// other hour keeps at least its own "N hour(s)" wording as a hint that
|
|
39975
40021
|
// this is a time-of-day, not a duration — only hour 0 loses that hint
|
|
39976
40022
|
// entirely.
|
|
40023
|
+
// clockStyle: this is always a time-of-day here, never a duration — keeps
|
|
40024
|
+
// a zero hour instead of dropping it (midnight would otherwise be
|
|
40025
|
+
// indistinguishable from an actual 5-minute duration), and in
|
|
40026
|
+
// format="compact" also zero-pads a single-digit hour so "5h30"/"0h05"
|
|
40027
|
+
// read as "05h30"/"00h05", closer to a "HH:MM" clock.
|
|
39977
40028
|
let text;
|
|
39978
40029
|
if (date.getHours() !== 0) {
|
|
39979
40030
|
text = formatMinuteDuration(totalMinutes, {
|
|
39980
40031
|
lang,
|
|
39981
|
-
format
|
|
40032
|
+
format,
|
|
40033
|
+
clockStyle: true
|
|
39982
40034
|
});
|
|
39983
40035
|
} else if (format !== "long") {
|
|
39984
40036
|
// short/narrow/compact: keep the "0 h"/"0h" hour part instead of
|
|
39985
|
-
// dropping it
|
|
39986
|
-
//
|
|
39987
|
-
//
|
|
39988
|
-
// into these otherwise terse, symbol-based formats.
|
|
40037
|
+
// dropping it — e.g. "0 h et 5 min"/"0h 5min"/"00h05" — rather than
|
|
40038
|
+
// substituting a translated "midnight" word, which would look out of
|
|
40039
|
+
// place squeezed into these otherwise terse, symbol-based formats.
|
|
39989
40040
|
text = formatMinuteDuration(totalMinutes, {
|
|
39990
40041
|
lang,
|
|
39991
40042
|
format,
|
|
39992
|
-
|
|
40043
|
+
clockStyle: true
|
|
39993
40044
|
});
|
|
39994
40045
|
} else {
|
|
39995
40046
|
const midnightWord = naviI18n("time.midnight", undefined, {
|
|
@@ -40004,7 +40055,7 @@ const TimeTime = ({
|
|
|
40004
40055
|
text = formatMinuteDuration(totalMinutes, {
|
|
40005
40056
|
lang,
|
|
40006
40057
|
format,
|
|
40007
|
-
|
|
40058
|
+
clockStyle: true
|
|
40008
40059
|
});
|
|
40009
40060
|
} else {
|
|
40010
40061
|
// Swap just the "0 heure(s)" part of the Intl-generated duration
|