@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/jsenv_navi.js
CHANGED
|
@@ -6281,6 +6281,10 @@ const CONTROL_PROP_SET = new Set([
|
|
|
6281
6281
|
"charGuard",
|
|
6282
6282
|
"maxLengthGuard",
|
|
6283
6283
|
|
|
6284
|
+
// A checkbox that is a yes/no: `true` checked, `false` unchecked, instead of
|
|
6285
|
+
// HTML's value-or-nothing (see resolveEmptyUIState).
|
|
6286
|
+
"boolean",
|
|
6287
|
+
|
|
6284
6288
|
// This control answers for itself: it registers into no group, no form and no
|
|
6285
6289
|
// picker around it (see useUIStateController). It belongs here rather than
|
|
6286
6290
|
// being deleted off `props` where it is read: props is the vnode's own object
|
|
@@ -42149,6 +42153,14 @@ const useUIStateController = (
|
|
|
42149
42153
|
return ownUIState || parentUIState;
|
|
42150
42154
|
})
|
|
42151
42155
|
: ownUIStateSignal;
|
|
42156
|
+
// What the control is worth, which its action is bound to: what it
|
|
42157
|
+
// holds, or its own empty when it holds nothing. Apart from
|
|
42158
|
+
// uiStateSignal because unchecked is `undefined` there (the DOM reads any
|
|
42159
|
+
// other value as checked), while a yes/no unchecked is worth `false`.
|
|
42160
|
+
const emptyUIStateSignal = signal(emptyUIState);
|
|
42161
|
+
const valueSignal = computed(() =>
|
|
42162
|
+
uiStateOrEmpty(uiStateSignal.value, emptyUIStateSignal.value),
|
|
42163
|
+
);
|
|
42152
42164
|
|
|
42153
42165
|
// The two-way half of a bound `signal` prop: setting it re-renders and
|
|
42154
42166
|
// re-syncs via state_prop_change, but with the same value → guarded as a
|
|
@@ -42197,6 +42209,7 @@ const useUIStateController = (
|
|
|
42197
42209
|
// travel command ended up carrying a picker's selection: see
|
|
42198
42210
|
// resolveCommandValue in commands.js.
|
|
42199
42211
|
ownUIStateSignal,
|
|
42212
|
+
valueSignal,
|
|
42200
42213
|
value: controlInfo.value,
|
|
42201
42214
|
// The suggestion this control started on — what tells a field showing
|
|
42202
42215
|
// its default from one carrying an answer (see isUIStateHeld).
|
|
@@ -42256,7 +42269,11 @@ const useUIStateController = (
|
|
|
42256
42269
|
}
|
|
42257
42270
|
s.uiActionInternal?.(currentUIState, e);
|
|
42258
42271
|
if (s.uiAction) {
|
|
42259
|
-
|
|
42272
|
+
const value = uiStateOrEmpty(
|
|
42273
|
+
currentUIState,
|
|
42274
|
+
controller.emptyUIState,
|
|
42275
|
+
);
|
|
42276
|
+
s.uiAction(value, e);
|
|
42260
42277
|
}
|
|
42261
42278
|
if (skipCommand) ; else {
|
|
42262
42279
|
const command = controller.controlHostProps.command;
|
|
@@ -42743,6 +42760,7 @@ const useUIStateController = (
|
|
|
42743
42760
|
return {
|
|
42744
42761
|
controller,
|
|
42745
42762
|
parentUiStateSignalHolder,
|
|
42763
|
+
emptyUIStateSignal,
|
|
42746
42764
|
...liveValues(),
|
|
42747
42765
|
};
|
|
42748
42766
|
},
|
|
@@ -42864,6 +42882,7 @@ const useUIStateController = (
|
|
|
42864
42882
|
);
|
|
42865
42883
|
scope.parentUiStateSignalHolder.value =
|
|
42866
42884
|
parentUIStateController?.uiStateSignal ?? null;
|
|
42885
|
+
scope.emptyUIStateSignal.value = emptyUIState;
|
|
42867
42886
|
|
|
42868
42887
|
const { controller } = scope;
|
|
42869
42888
|
const controllerRef = controller.ref;
|
|
@@ -43074,14 +43093,11 @@ const keptAndNamedChildUIStates = (children, fallbackState, kept) => {
|
|
|
43074
43093
|
const readNamedChildUIStates = (children, { warnNameless }) => {
|
|
43075
43094
|
const values = {};
|
|
43076
43095
|
for (const child of children) {
|
|
43077
|
-
const { name
|
|
43096
|
+
const { name } = child;
|
|
43078
43097
|
// A control holding nothing writes its own empty, not a hole: the key is in
|
|
43079
43098
|
// the object either way, and what is read from it keeps the shape the
|
|
43080
43099
|
// reader was promised (see resolveEmptyUIState).
|
|
43081
|
-
const uiState =
|
|
43082
|
-
child.uiState === undefined && emptyUIState !== undefined
|
|
43083
|
-
? emptyUIState
|
|
43084
|
-
: child.uiState;
|
|
43100
|
+
const uiState = uiStateOrEmpty(child.uiState, child.emptyUIState);
|
|
43085
43101
|
if (!name) {
|
|
43086
43102
|
if (isNamelessGrouping(child, uiState)) {
|
|
43087
43103
|
Object.assign(
|
|
@@ -44413,11 +44429,10 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
44413
44429
|
// whose last item was unselected holds [], the same thing clearing it
|
|
44414
44430
|
// leaves — not a value that changes type on its owner the moment it
|
|
44415
44431
|
// empties.
|
|
44416
|
-
const
|
|
44417
|
-
|
|
44418
|
-
|
|
44419
|
-
|
|
44420
|
-
: child.uiState;
|
|
44432
|
+
const uiStateToAdopt = uiStateOrEmpty(
|
|
44433
|
+
child.uiState,
|
|
44434
|
+
s.realUIStateController.emptyUIState,
|
|
44435
|
+
);
|
|
44421
44436
|
s.realUIStateController.setUIState(uiStateToAdopt, propagateUpEvent);
|
|
44422
44437
|
updatingRef.current = false;
|
|
44423
44438
|
},
|
|
@@ -44657,8 +44672,9 @@ const resolveEmptyUIState = (props, controlType) => {
|
|
|
44657
44672
|
if (controlType === "input" && props.type === "checkbox") {
|
|
44658
44673
|
// A checkbox is a member of a set, the way HTML has it: checked it sends
|
|
44659
44674
|
// its value ("on" by default), unchecked it sends nothing at all. Only one
|
|
44660
|
-
//
|
|
44661
|
-
|
|
44675
|
+
// declared `boolean` is a yes/no, and a yes/no nobody said yes to is
|
|
44676
|
+
// `false`.
|
|
44677
|
+
return props.boolean ? false : undefined;
|
|
44662
44678
|
}
|
|
44663
44679
|
const stateShape = props["navi-state-shape"];
|
|
44664
44680
|
if (stateShape === "array") {
|
|
@@ -44670,6 +44686,15 @@ const resolveEmptyUIState = (props, controlType) => {
|
|
|
44670
44686
|
return undefined;
|
|
44671
44687
|
};
|
|
44672
44688
|
|
|
44689
|
+
// What a control holding `uiState` is worth: its own empty when it holds
|
|
44690
|
+
// nothing, so the reader gets the shape it was promised.
|
|
44691
|
+
const uiStateOrEmpty = (uiState, emptyUIState) => {
|
|
44692
|
+
if (uiState === undefined && emptyUIState !== undefined) {
|
|
44693
|
+
return emptyUIState;
|
|
44694
|
+
}
|
|
44695
|
+
return uiState;
|
|
44696
|
+
};
|
|
44697
|
+
|
|
44673
44698
|
// What a cleared control shows: its own empty, kept in the shape it was holding.
|
|
44674
44699
|
const resolveClearedUIState = (controller) => {
|
|
44675
44700
|
const { controlType, props } = controller;
|
|
@@ -44904,7 +44929,7 @@ const useControlProps = (props, {
|
|
|
44904
44929
|
// of a form would re-render at each change of any of its fields.
|
|
44905
44930
|
// eslint-disable-next-line no-unused-expressions
|
|
44906
44931
|
uiStateController.ownUIStateSignal.value;
|
|
44907
|
-
const boundAction = useAction(props.action, uiStateController.
|
|
44932
|
+
const boundAction = useAction(props.action, uiStateController.valueSignal);
|
|
44908
44933
|
const [controlRootProps, controlHostProps] = useInteractiveProps(props, {
|
|
44909
44934
|
uiStateController,
|
|
44910
44935
|
boundAction,
|
|
@@ -55440,7 +55465,8 @@ a.navi_button {
|
|
|
55440
55465
|
|
|
55441
55466
|
&[data-icon] {
|
|
55442
55467
|
--button-padding: 0;
|
|
55443
|
-
|
|
55468
|
+
justify-content: center;
|
|
55469
|
+
align-items: center;
|
|
55444
55470
|
}
|
|
55445
55471
|
|
|
55446
55472
|
&[data-cta] {
|
|
@@ -55538,7 +55564,12 @@ const ButtonUI = props => {
|
|
|
55538
55564
|
});
|
|
55539
55565
|
return jsxs(Box, {
|
|
55540
55566
|
inline: true,
|
|
55541
|
-
block: true
|
|
55567
|
+
block: true
|
|
55568
|
+
// An icon is laid out as a box rather than on a line of text that is not
|
|
55569
|
+
// there, which is also what makes alignX/alignY place it. Before the
|
|
55570
|
+
// caller's props, so their own flex direction wins.
|
|
55571
|
+
,
|
|
55572
|
+
flex: icon ? "x" : undefined,
|
|
55542
55573
|
...buttonControlRootProps,
|
|
55543
55574
|
...buttonControlHostProps,
|
|
55544
55575
|
// eslint-disable-next-line react/no-children-prop
|
|
@@ -56981,7 +57012,12 @@ const css$M = /* css */`@layer navi {
|
|
|
56981
57012
|
const InputCheckbox = props => {
|
|
56982
57013
|
const defaultRef = useRef();
|
|
56983
57014
|
props.ref = props.ref || defaultRef;
|
|
56984
|
-
|
|
57015
|
+
useRef(false);
|
|
57016
|
+
if (props.boolean) {
|
|
57017
|
+
props.value = true;
|
|
57018
|
+
} else if (props.value === undefined) {
|
|
57019
|
+
props.value = "on";
|
|
57020
|
+
}
|
|
56985
57021
|
if (props.headless) {
|
|
56986
57022
|
return jsx(InputCheckboxHeadless, {
|
|
56987
57023
|
...props,
|
|
@@ -79762,7 +79798,6 @@ const css$w = /* css */`@layer navi;
|
|
|
79762
79798
|
background: var(--x-background);
|
|
79763
79799
|
background-color: var(--x-background-color);
|
|
79764
79800
|
border-radius: 1em;
|
|
79765
|
-
align-items: stretch;
|
|
79766
79801
|
display: inline;
|
|
79767
79802
|
position: relative;
|
|
79768
79803
|
|
|
@@ -79772,13 +79807,25 @@ const css$w = /* css */`@layer navi;
|
|
|
79772
79807
|
|
|
79773
79808
|
&[data-text-overflow] {
|
|
79774
79809
|
display: inline;
|
|
79810
|
+
}
|
|
79775
79811
|
|
|
79776
|
-
|
|
79777
|
-
|
|
79778
|
-
|
|
79812
|
+
&[data-has-button] {
|
|
79813
|
+
align-items: center;
|
|
79814
|
+
display: inline-flex;
|
|
79815
|
+
}
|
|
79816
|
+
|
|
79817
|
+
& .navi_badge_label {
|
|
79818
|
+
text-overflow: ellipsis;
|
|
79819
|
+
text-box: trim-both cap alphabetic;
|
|
79820
|
+
overflow-clip-margin: padding-box calc((1lh - 1em) / 2);
|
|
79821
|
+
align-self: baseline;
|
|
79822
|
+
min-width: 0;
|
|
79823
|
+
overflow-x: clip;
|
|
79779
79824
|
}
|
|
79780
79825
|
|
|
79781
79826
|
& [role="button"] {
|
|
79827
|
+
box-sizing: content-box;
|
|
79828
|
+
height: 1cap;
|
|
79782
79829
|
margin-top: calc(-1 * var(--x-badge-padding-top));
|
|
79783
79830
|
margin-bottom: calc(-1 * var(--x-badge-padding-bottom));
|
|
79784
79831
|
padding-top: var(--x-badge-padding-top);
|
|
@@ -79788,6 +79835,7 @@ const css$w = /* css */`@layer navi;
|
|
|
79788
79835
|
cursor: pointer;
|
|
79789
79836
|
pointer-events: auto;
|
|
79790
79837
|
user-select: none;
|
|
79838
|
+
flex-shrink: 0;
|
|
79791
79839
|
align-items: center;
|
|
79792
79840
|
display: inline-flex;
|
|
79793
79841
|
|
|
@@ -79836,6 +79884,16 @@ const BadgeUI = ({
|
|
|
79836
79884
|
ref
|
|
79837
79885
|
} = props;
|
|
79838
79886
|
useAccentColorAttributes(ref, null);
|
|
79887
|
+
const childArray = toChildArray(children);
|
|
79888
|
+
if (childArray.some(isBadgeButton)) {
|
|
79889
|
+
props = {
|
|
79890
|
+
"inline": true,
|
|
79891
|
+
"flex": true,
|
|
79892
|
+
...props,
|
|
79893
|
+
"data-has-button": ""
|
|
79894
|
+
};
|
|
79895
|
+
children = wrapLabels(childArray);
|
|
79896
|
+
}
|
|
79839
79897
|
return jsx(Text, {
|
|
79840
79898
|
className: withPropsClassName("navi_badge", className),
|
|
79841
79899
|
bold: true,
|
|
@@ -79896,6 +79954,35 @@ const BadgeButtonUI = props => {
|
|
|
79896
79954
|
});
|
|
79897
79955
|
};
|
|
79898
79956
|
Badge.Button = BadgeButton;
|
|
79957
|
+
const isBadgeButton = child => {
|
|
79958
|
+
return isValidElement(child) && child.type === BadgeButton;
|
|
79959
|
+
};
|
|
79960
|
+
// Each run of children between buttons becomes one label: a single flex item
|
|
79961
|
+
// holding its text, so the trim reaches a bare string and the text keeps its
|
|
79962
|
+
// own baseline alignment inside.
|
|
79963
|
+
const wrapLabels = childArray => {
|
|
79964
|
+
const items = [];
|
|
79965
|
+
let run = [];
|
|
79966
|
+
const closeRun = () => {
|
|
79967
|
+
if (run.length > 0) {
|
|
79968
|
+
items.push(jsx("span", {
|
|
79969
|
+
className: "navi_badge_label",
|
|
79970
|
+
children: run
|
|
79971
|
+
}));
|
|
79972
|
+
run = [];
|
|
79973
|
+
}
|
|
79974
|
+
};
|
|
79975
|
+
for (const child of childArray) {
|
|
79976
|
+
if (isBadgeButton(child)) {
|
|
79977
|
+
closeRun();
|
|
79978
|
+
items.push(child);
|
|
79979
|
+
} else {
|
|
79980
|
+
run.push(child);
|
|
79981
|
+
}
|
|
79982
|
+
}
|
|
79983
|
+
closeRun();
|
|
79984
|
+
return items;
|
|
79985
|
+
};
|
|
79899
79986
|
|
|
79900
79987
|
installImportMetaCssBuild(import.meta);
|
|
79901
79988
|
const css$v = /* css */`@layer navi;
|
|
@@ -80635,8 +80722,6 @@ const PickerChip = ({
|
|
|
80635
80722
|
...rest
|
|
80636
80723
|
}) => {
|
|
80637
80724
|
return jsxs(Badge, {
|
|
80638
|
-
inline: true,
|
|
80639
|
-
flex: true,
|
|
80640
80725
|
...rest,
|
|
80641
80726
|
children: [children, jsx(Badge.Button, {
|
|
80642
80727
|
selfInteractions: "click",
|
|
@@ -84782,14 +84867,7 @@ const SplitButton = props => {
|
|
|
84782
84867
|
children: [jsx(Button, {
|
|
84783
84868
|
icon: true,
|
|
84784
84869
|
paddingX: "s",
|
|
84785
|
-
expandY: true
|
|
84786
|
-
// flex + align: the icon is the whole content, so it is laid out as
|
|
84787
|
-
// its own box in the middle of the button. Left to flow inline it
|
|
84788
|
-
// would sit on the baseline of a line of text that is not there,
|
|
84789
|
-
// with the descender space still kept below it.
|
|
84790
|
-
,
|
|
84791
|
-
flex: true,
|
|
84792
|
-
align: "center",
|
|
84870
|
+
expandY: true,
|
|
84793
84871
|
"aria-label": menuLabel,
|
|
84794
84872
|
command: "--navi-open",
|
|
84795
84873
|
commandFor: menuId,
|
|
@@ -91900,8 +91978,6 @@ const ButtonCopyToClipboard = ({
|
|
|
91900
91978
|
children: "Copié !"
|
|
91901
91979
|
}), jsx(Button, {
|
|
91902
91980
|
className: "navi_copy_button",
|
|
91903
|
-
flex: "y",
|
|
91904
|
-
alignY: "center",
|
|
91905
91981
|
icon: true,
|
|
91906
91982
|
revealOnInteraction: true,
|
|
91907
91983
|
square: true,
|
|
@@ -92790,8 +92866,6 @@ const MessageBox = ({
|
|
|
92790
92866
|
action: onClose,
|
|
92791
92867
|
icon: true,
|
|
92792
92868
|
border: "none",
|
|
92793
|
-
alignX: "center",
|
|
92794
|
-
alignY: "center",
|
|
92795
92869
|
style: {
|
|
92796
92870
|
":hover": {
|
|
92797
92871
|
backgroundColor: "rgba(0, 0, 0, 0.1)"
|