@jsenv/navi 0.29.443 → 0.29.445
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/dev/jsenv_navi.js +121 -36
- package/dist/dev/jsenv_navi.js.map +12 -12
- package/dist/jsenv_navi.js +109 -35
- package/dist/jsenv_navi.js.map +12 -12
- package/docs/AI_INSTRUCTIONS.md +6 -4
- package/docs/actions.md +6 -2
- package/docs/control_value.md +28 -8
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -6502,6 +6502,10 @@ const CONTROL_PROP_SET = new Set([
|
|
|
6502
6502
|
"charGuard",
|
|
6503
6503
|
"maxLengthGuard",
|
|
6504
6504
|
|
|
6505
|
+
// A checkbox that is a yes/no: `true` checked, `false` unchecked, instead of
|
|
6506
|
+
// HTML's value-or-nothing (see resolveEmptyUIState).
|
|
6507
|
+
"boolean",
|
|
6508
|
+
|
|
6505
6509
|
// This control answers for itself: it registers into no group, no form and no
|
|
6506
6510
|
// picker around it (see useUIStateController). It belongs here rather than
|
|
6507
6511
|
// being deleted off `props` where it is read: props is the vnode's own object
|
|
@@ -43198,6 +43202,14 @@ const useUIStateController = (
|
|
|
43198
43202
|
return ownUIState || parentUIState;
|
|
43199
43203
|
})
|
|
43200
43204
|
: ownUIStateSignal;
|
|
43205
|
+
// What the control is worth, which its action is bound to: what it
|
|
43206
|
+
// holds, or its own empty when it holds nothing. Apart from
|
|
43207
|
+
// uiStateSignal because unchecked is `undefined` there (the DOM reads any
|
|
43208
|
+
// other value as checked), while a yes/no unchecked is worth `false`.
|
|
43209
|
+
const emptyUIStateSignal = signal(emptyUIState);
|
|
43210
|
+
const valueSignal = computed(() =>
|
|
43211
|
+
uiStateOrEmpty(uiStateSignal.value, emptyUIStateSignal.value),
|
|
43212
|
+
);
|
|
43201
43213
|
|
|
43202
43214
|
// The two-way half of a bound `signal` prop: setting it re-renders and
|
|
43203
43215
|
// re-syncs via state_prop_change, but with the same value → guarded as a
|
|
@@ -43246,6 +43258,7 @@ const useUIStateController = (
|
|
|
43246
43258
|
// travel command ended up carrying a picker's selection: see
|
|
43247
43259
|
// resolveCommandValue in commands.js.
|
|
43248
43260
|
ownUIStateSignal,
|
|
43261
|
+
valueSignal,
|
|
43249
43262
|
value: controlInfo.value,
|
|
43250
43263
|
// The suggestion this control started on — what tells a field showing
|
|
43251
43264
|
// its default from one carrying an answer (see isUIStateHeld).
|
|
@@ -43309,8 +43322,12 @@ const useUIStateController = (
|
|
|
43309
43322
|
}
|
|
43310
43323
|
s.uiActionInternal?.(currentUIState, e);
|
|
43311
43324
|
if (s.uiAction) {
|
|
43312
|
-
|
|
43313
|
-
|
|
43325
|
+
const value = uiStateOrEmpty(
|
|
43326
|
+
currentUIState,
|
|
43327
|
+
controller.emptyUIState,
|
|
43328
|
+
);
|
|
43329
|
+
debugUIState(`calling uiAction for ${controlType}`, value);
|
|
43330
|
+
s.uiAction(value, e);
|
|
43314
43331
|
}
|
|
43315
43332
|
if (skipCommand) ; else {
|
|
43316
43333
|
const command = controller.controlHostProps.command;
|
|
@@ -43814,6 +43831,7 @@ const useUIStateController = (
|
|
|
43814
43831
|
return {
|
|
43815
43832
|
controller,
|
|
43816
43833
|
parentUiStateSignalHolder,
|
|
43834
|
+
emptyUIStateSignal,
|
|
43817
43835
|
...liveValues(),
|
|
43818
43836
|
};
|
|
43819
43837
|
},
|
|
@@ -43935,6 +43953,7 @@ const useUIStateController = (
|
|
|
43935
43953
|
);
|
|
43936
43954
|
scope.parentUiStateSignalHolder.value =
|
|
43937
43955
|
parentUIStateController?.uiStateSignal ?? null;
|
|
43956
|
+
scope.emptyUIStateSignal.value = emptyUIState;
|
|
43938
43957
|
|
|
43939
43958
|
const { controller } = scope;
|
|
43940
43959
|
const controllerRef = controller.ref;
|
|
@@ -44145,14 +44164,11 @@ const keptAndNamedChildUIStates = (children, fallbackState, kept) => {
|
|
|
44145
44164
|
const readNamedChildUIStates = (children, { warnNameless }) => {
|
|
44146
44165
|
const values = {};
|
|
44147
44166
|
for (const child of children) {
|
|
44148
|
-
const { name
|
|
44167
|
+
const { name } = child;
|
|
44149
44168
|
// A control holding nothing writes its own empty, not a hole: the key is in
|
|
44150
44169
|
// the object either way, and what is read from it keeps the shape the
|
|
44151
44170
|
// reader was promised (see resolveEmptyUIState).
|
|
44152
|
-
const uiState =
|
|
44153
|
-
child.uiState === undefined && emptyUIState !== undefined
|
|
44154
|
-
? emptyUIState
|
|
44155
|
-
: child.uiState;
|
|
44171
|
+
const uiState = uiStateOrEmpty(child.uiState, child.emptyUIState);
|
|
44156
44172
|
if (!name) {
|
|
44157
44173
|
if (isNamelessGrouping(child, uiState)) {
|
|
44158
44174
|
Object.assign(
|
|
@@ -45499,11 +45515,10 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
45499
45515
|
// whose last item was unselected holds [], the same thing clearing it
|
|
45500
45516
|
// leaves — not a value that changes type on its owner the moment it
|
|
45501
45517
|
// empties.
|
|
45502
|
-
const
|
|
45503
|
-
|
|
45504
|
-
|
|
45505
|
-
|
|
45506
|
-
: child.uiState;
|
|
45518
|
+
const uiStateToAdopt = uiStateOrEmpty(
|
|
45519
|
+
child.uiState,
|
|
45520
|
+
s.realUIStateController.emptyUIState,
|
|
45521
|
+
);
|
|
45507
45522
|
s.realUIStateController.setUIState(uiStateToAdopt, propagateUpEvent);
|
|
45508
45523
|
updatingRef.current = false;
|
|
45509
45524
|
},
|
|
@@ -45821,8 +45836,9 @@ const resolveEmptyUIState = (props, controlType) => {
|
|
|
45821
45836
|
if (controlType === "input" && props.type === "checkbox") {
|
|
45822
45837
|
// A checkbox is a member of a set, the way HTML has it: checked it sends
|
|
45823
45838
|
// its value ("on" by default), unchecked it sends nothing at all. Only one
|
|
45824
|
-
//
|
|
45825
|
-
|
|
45839
|
+
// declared `boolean` is a yes/no, and a yes/no nobody said yes to is
|
|
45840
|
+
// `false`.
|
|
45841
|
+
return props.boolean ? false : undefined;
|
|
45826
45842
|
}
|
|
45827
45843
|
const stateShape = props["navi-state-shape"];
|
|
45828
45844
|
if (stateShape === "array") {
|
|
@@ -45834,6 +45850,15 @@ const resolveEmptyUIState = (props, controlType) => {
|
|
|
45834
45850
|
return undefined;
|
|
45835
45851
|
};
|
|
45836
45852
|
|
|
45853
|
+
// What a control holding `uiState` is worth: its own empty when it holds
|
|
45854
|
+
// nothing, so the reader gets the shape it was promised.
|
|
45855
|
+
const uiStateOrEmpty = (uiState, emptyUIState) => {
|
|
45856
|
+
if (uiState === undefined && emptyUIState !== undefined) {
|
|
45857
|
+
return emptyUIState;
|
|
45858
|
+
}
|
|
45859
|
+
return uiState;
|
|
45860
|
+
};
|
|
45861
|
+
|
|
45837
45862
|
// What a cleared control shows: its own empty, kept in the shape it was holding.
|
|
45838
45863
|
const resolveClearedUIState = (controller) => {
|
|
45839
45864
|
const { controlType, props } = controller;
|
|
@@ -46069,7 +46094,7 @@ const useControlProps = (props, {
|
|
|
46069
46094
|
// of a form would re-render at each change of any of its fields.
|
|
46070
46095
|
// eslint-disable-next-line no-unused-expressions
|
|
46071
46096
|
uiStateController.ownUIStateSignal.value;
|
|
46072
|
-
const boundAction = useAction(props.action, uiStateController.
|
|
46097
|
+
const boundAction = useAction(props.action, uiStateController.valueSignal);
|
|
46073
46098
|
const [controlRootProps, controlHostProps] = useInteractiveProps(props, {
|
|
46074
46099
|
uiStateController,
|
|
46075
46100
|
boundAction,
|
|
@@ -56643,7 +56668,8 @@ a.navi_button {
|
|
|
56643
56668
|
|
|
56644
56669
|
&[data-icon] {
|
|
56645
56670
|
--button-padding: 0;
|
|
56646
|
-
|
|
56671
|
+
justify-content: center;
|
|
56672
|
+
align-items: center;
|
|
56647
56673
|
}
|
|
56648
56674
|
|
|
56649
56675
|
&[data-cta] {
|
|
@@ -56741,7 +56767,12 @@ const ButtonUI = props => {
|
|
|
56741
56767
|
});
|
|
56742
56768
|
return jsxs(Box, {
|
|
56743
56769
|
inline: true,
|
|
56744
|
-
block: true
|
|
56770
|
+
block: true
|
|
56771
|
+
// An icon is laid out as a box rather than on a line of text that is not
|
|
56772
|
+
// there, which is also what makes alignX/alignY place it. Before the
|
|
56773
|
+
// caller's props, so their own flex direction wins.
|
|
56774
|
+
,
|
|
56775
|
+
flex: icon ? "x" : undefined,
|
|
56745
56776
|
...buttonControlRootProps,
|
|
56746
56777
|
...buttonControlHostProps,
|
|
56747
56778
|
// eslint-disable-next-line react/no-children-prop
|
|
@@ -58190,7 +58221,23 @@ const css$M = /* css */`@layer navi {
|
|
|
58190
58221
|
const InputCheckbox = props => {
|
|
58191
58222
|
const defaultRef = useRef();
|
|
58192
58223
|
props.ref = props.ref || defaultRef;
|
|
58193
|
-
|
|
58224
|
+
const valueWarnedRef = useRef(false);
|
|
58225
|
+
if (!valueWarnedRef.current) {
|
|
58226
|
+
// `true` is what `boolean` writes into `value` below, and Preact hands the
|
|
58227
|
+
// same props object back on a re-render the component triggers itself.
|
|
58228
|
+
if (props.boolean && props.value !== undefined && props.value !== true) {
|
|
58229
|
+
valueWarnedRef.current = true;
|
|
58230
|
+
console.warn(`[checkbox] "boolean" is true or false, its "value" is ignored: remove value={${JSON.stringify(props.value)}}.`);
|
|
58231
|
+
} else if (!props.boolean && props.value === true) {
|
|
58232
|
+
valueWarnedRef.current = true;
|
|
58233
|
+
console.warn(`[checkbox] value={true} is a value like any other: unchecked is undefined. For a yes/no (true or false), use the "boolean" prop.`);
|
|
58234
|
+
}
|
|
58235
|
+
}
|
|
58236
|
+
if (props.boolean) {
|
|
58237
|
+
props.value = true;
|
|
58238
|
+
} else if (props.value === undefined) {
|
|
58239
|
+
props.value = "on";
|
|
58240
|
+
}
|
|
58194
58241
|
if (props.headless) {
|
|
58195
58242
|
return jsx(InputCheckboxHeadless, {
|
|
58196
58243
|
...props,
|
|
@@ -81316,7 +81363,6 @@ const css$w = /* css */`@layer navi;
|
|
|
81316
81363
|
background: var(--x-background);
|
|
81317
81364
|
background-color: var(--x-background-color);
|
|
81318
81365
|
border-radius: 1em;
|
|
81319
|
-
align-items: stretch;
|
|
81320
81366
|
display: inline;
|
|
81321
81367
|
position: relative;
|
|
81322
81368
|
|
|
@@ -81326,13 +81372,25 @@ const css$w = /* css */`@layer navi;
|
|
|
81326
81372
|
|
|
81327
81373
|
&[data-text-overflow] {
|
|
81328
81374
|
display: inline;
|
|
81375
|
+
}
|
|
81329
81376
|
|
|
81330
|
-
|
|
81331
|
-
|
|
81332
|
-
|
|
81377
|
+
&[data-has-button] {
|
|
81378
|
+
align-items: center;
|
|
81379
|
+
display: inline-flex;
|
|
81380
|
+
}
|
|
81381
|
+
|
|
81382
|
+
& .navi_badge_label {
|
|
81383
|
+
text-overflow: ellipsis;
|
|
81384
|
+
text-box: trim-both cap alphabetic;
|
|
81385
|
+
overflow-clip-margin: padding-box calc((1lh - 1em) / 2);
|
|
81386
|
+
align-self: baseline;
|
|
81387
|
+
min-width: 0;
|
|
81388
|
+
overflow-x: clip;
|
|
81333
81389
|
}
|
|
81334
81390
|
|
|
81335
81391
|
& [role="button"] {
|
|
81392
|
+
box-sizing: content-box;
|
|
81393
|
+
height: 1cap;
|
|
81336
81394
|
margin-top: calc(-1 * var(--x-badge-padding-top));
|
|
81337
81395
|
margin-bottom: calc(-1 * var(--x-badge-padding-bottom));
|
|
81338
81396
|
padding-top: var(--x-badge-padding-top);
|
|
@@ -81342,6 +81400,7 @@ const css$w = /* css */`@layer navi;
|
|
|
81342
81400
|
cursor: pointer;
|
|
81343
81401
|
pointer-events: auto;
|
|
81344
81402
|
user-select: none;
|
|
81403
|
+
flex-shrink: 0;
|
|
81345
81404
|
align-items: center;
|
|
81346
81405
|
display: inline-flex;
|
|
81347
81406
|
|
|
@@ -81390,6 +81449,16 @@ const BadgeUI = ({
|
|
|
81390
81449
|
ref
|
|
81391
81450
|
} = props;
|
|
81392
81451
|
useAccentColorAttributes(ref, null);
|
|
81452
|
+
const childArray = toChildArray(children);
|
|
81453
|
+
if (childArray.some(isBadgeButton)) {
|
|
81454
|
+
props = {
|
|
81455
|
+
"inline": true,
|
|
81456
|
+
"flex": true,
|
|
81457
|
+
...props,
|
|
81458
|
+
"data-has-button": ""
|
|
81459
|
+
};
|
|
81460
|
+
children = wrapLabels(childArray);
|
|
81461
|
+
}
|
|
81393
81462
|
return jsx(Text, {
|
|
81394
81463
|
className: withPropsClassName("navi_badge", className),
|
|
81395
81464
|
bold: true,
|
|
@@ -81450,6 +81519,35 @@ const BadgeButtonUI = props => {
|
|
|
81450
81519
|
});
|
|
81451
81520
|
};
|
|
81452
81521
|
Badge.Button = BadgeButton;
|
|
81522
|
+
const isBadgeButton = child => {
|
|
81523
|
+
return isValidElement(child) && child.type === BadgeButton;
|
|
81524
|
+
};
|
|
81525
|
+
// Each run of children between buttons becomes one label: a single flex item
|
|
81526
|
+
// holding its text, so the trim reaches a bare string and the text keeps its
|
|
81527
|
+
// own baseline alignment inside.
|
|
81528
|
+
const wrapLabels = childArray => {
|
|
81529
|
+
const items = [];
|
|
81530
|
+
let run = [];
|
|
81531
|
+
const closeRun = () => {
|
|
81532
|
+
if (run.length > 0) {
|
|
81533
|
+
items.push(jsx("span", {
|
|
81534
|
+
className: "navi_badge_label",
|
|
81535
|
+
children: run
|
|
81536
|
+
}));
|
|
81537
|
+
run = [];
|
|
81538
|
+
}
|
|
81539
|
+
};
|
|
81540
|
+
for (const child of childArray) {
|
|
81541
|
+
if (isBadgeButton(child)) {
|
|
81542
|
+
closeRun();
|
|
81543
|
+
items.push(child);
|
|
81544
|
+
} else {
|
|
81545
|
+
run.push(child);
|
|
81546
|
+
}
|
|
81547
|
+
}
|
|
81548
|
+
closeRun();
|
|
81549
|
+
return items;
|
|
81550
|
+
};
|
|
81453
81551
|
|
|
81454
81552
|
installImportMetaCssBuild(import.meta);
|
|
81455
81553
|
const css$v = /* css */`@layer navi;
|
|
@@ -82189,8 +82287,6 @@ const PickerChip = ({
|
|
|
82189
82287
|
...rest
|
|
82190
82288
|
}) => {
|
|
82191
82289
|
return jsxs(Badge, {
|
|
82192
|
-
inline: true,
|
|
82193
|
-
flex: true,
|
|
82194
82290
|
...rest,
|
|
82195
82291
|
children: [children, jsx(Badge.Button, {
|
|
82196
82292
|
selfInteractions: "click",
|
|
@@ -87002,14 +87098,7 @@ const SplitButton = props => {
|
|
|
87002
87098
|
children: [jsx(Button, {
|
|
87003
87099
|
icon: true,
|
|
87004
87100
|
paddingX: "s",
|
|
87005
|
-
expandY: true
|
|
87006
|
-
// flex + align: the icon is the whole content, so it is laid out as
|
|
87007
|
-
// its own box in the middle of the button. Left to flow inline it
|
|
87008
|
-
// would sit on the baseline of a line of text that is not there,
|
|
87009
|
-
// with the descender space still kept below it.
|
|
87010
|
-
,
|
|
87011
|
-
flex: true,
|
|
87012
|
-
align: "center",
|
|
87101
|
+
expandY: true,
|
|
87013
87102
|
"aria-label": menuLabel,
|
|
87014
87103
|
command: "--navi-open",
|
|
87015
87104
|
commandFor: menuId,
|
|
@@ -94128,8 +94217,6 @@ const ButtonCopyToClipboard = ({
|
|
|
94128
94217
|
children: "Copié !"
|
|
94129
94218
|
}), jsx(Button, {
|
|
94130
94219
|
className: "navi_copy_button",
|
|
94131
|
-
flex: "y",
|
|
94132
|
-
alignY: "center",
|
|
94133
94220
|
icon: true,
|
|
94134
94221
|
revealOnInteraction: true,
|
|
94135
94222
|
square: true,
|
|
@@ -95018,8 +95105,6 @@ const MessageBox = ({
|
|
|
95018
95105
|
action: onClose,
|
|
95019
95106
|
icon: true,
|
|
95020
95107
|
border: "none",
|
|
95021
|
-
alignX: "center",
|
|
95022
|
-
alignY: "center",
|
|
95023
95108
|
style: {
|
|
95024
95109
|
":hover": {
|
|
95025
95110
|
backgroundColor: "rgba(0, 0, 0, 0.1)"
|