@jsenv/navi 0.9.2 → 0.10.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 +581 -408
- package/index.js +5 -0
- package/package.json +1 -1
- package/src/components/callout/callout.js +3 -1
- package/src/components/details/details.jsx +7 -8
- package/src/components/field/button.jsx +6 -63
- package/src/components/field/checkbox_list.jsx +0 -1
- package/src/components/field/form.jsx +2 -17
- package/src/components/field/input_checkbox.jsx +0 -1
- package/src/components/field/input_textual.jsx +5 -142
- package/src/components/field/radio_list.jsx +0 -1
- package/src/components/field/select.jsx +0 -2
- package/src/components/field/use_form_events.js +4 -0
- package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +1 -1
- package/src/components/link/link.jsx +27 -25
- package/src/components/text/text.jsx +28 -18
- package/src/validation/constraints/native_constraints.js +43 -18
- package/src/validation/constraints/same_as_constraint.js +42 -0
- package/src/validation/custom_constraint_validation.js +262 -59
- package/src/validation/demos/demo_same_as_constraint.html +259 -0
- package/src/validation/input_change_effect.js +106 -0
package/index.js
CHANGED
|
@@ -107,6 +107,11 @@ export {
|
|
|
107
107
|
addCustomMessage,
|
|
108
108
|
removeCustomMessage,
|
|
109
109
|
} from "./src/validation/custom_message.js";
|
|
110
|
+
// advanced constraint validation functions
|
|
111
|
+
export {
|
|
112
|
+
forwardActionRequested,
|
|
113
|
+
installCustomConstraintValidation,
|
|
114
|
+
} from "./src/validation/custom_constraint_validation.js";
|
|
110
115
|
|
|
111
116
|
// Other
|
|
112
117
|
export { useDependenciesDiff } from "./src/components/use_dependencies_diff.js";
|
package/package.json
CHANGED
|
@@ -86,7 +86,9 @@ export const openCallout = (
|
|
|
86
86
|
addTeardown(onClose);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
const [updateLevel, addLevelEffect] =
|
|
89
|
+
const [updateLevel, addLevelEffect, cleanupLevelEffects] =
|
|
90
|
+
createValueEffect(undefined);
|
|
91
|
+
addTeardown(cleanupLevelEffects);
|
|
90
92
|
|
|
91
93
|
// Create and add callout to document
|
|
92
94
|
const calloutElement = createCalloutElement();
|
|
@@ -16,33 +16,33 @@ import { SummaryMarker } from "./summary_marker.jsx";
|
|
|
16
16
|
|
|
17
17
|
import.meta.css = /* css */ `
|
|
18
18
|
.navi_details {
|
|
19
|
-
display: flex;
|
|
20
|
-
flex-direction: column;
|
|
21
19
|
position: relative;
|
|
22
20
|
z-index: 1;
|
|
21
|
+
display: flex;
|
|
23
22
|
flex-shrink: 0;
|
|
23
|
+
flex-direction: column;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
.navi_details > summary {
|
|
27
|
-
flex-shrink: 0;
|
|
28
|
-
cursor: pointer;
|
|
29
27
|
display: flex;
|
|
28
|
+
flex-shrink: 0;
|
|
30
29
|
flex-direction: column;
|
|
30
|
+
cursor: pointer;
|
|
31
31
|
user-select: none;
|
|
32
32
|
}
|
|
33
33
|
.summary_body {
|
|
34
34
|
display: flex;
|
|
35
|
+
width: 100%;
|
|
35
36
|
flex-direction: row;
|
|
36
37
|
align-items: center;
|
|
37
|
-
width: 100%;
|
|
38
38
|
gap: 0.2em;
|
|
39
39
|
}
|
|
40
40
|
.summary_label {
|
|
41
41
|
display: flex;
|
|
42
|
+
padding-right: 10px;
|
|
42
43
|
flex: 1;
|
|
43
|
-
gap: 0.2em;
|
|
44
44
|
align-items: center;
|
|
45
|
-
|
|
45
|
+
gap: 0.2em;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
.navi_details > summary:focus {
|
|
@@ -230,7 +230,6 @@ const DetailsWithAction = forwardRef((props, ref) => {
|
|
|
230
230
|
const isOpen = toggleEvent.newState === "open";
|
|
231
231
|
if (isOpen) {
|
|
232
232
|
requestAction(toggleEvent.target, effectiveAction, {
|
|
233
|
-
actionOrigin: "action_prop",
|
|
234
233
|
event: toggleEvent,
|
|
235
234
|
method: "run",
|
|
236
235
|
});
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
|
|
9
9
|
import { getActionPrivateProperties } from "../../action_private_properties.js";
|
|
10
10
|
import { useActionStatus } from "../../use_action_status.js";
|
|
11
|
-
import {
|
|
11
|
+
import { forwardActionRequested } from "../../validation/custom_constraint_validation.js";
|
|
12
12
|
import { useConstraints } from "../../validation/hooks/use_constraints.js";
|
|
13
13
|
import { FormActionContext } from "../action_execution/form_context.js";
|
|
14
14
|
import { renderActionableComponent } from "../action_execution/render_actionable_component.jsx";
|
|
@@ -285,7 +285,6 @@ const ButtonWithAction = forwardRef((props, ref) => {
|
|
|
285
285
|
const {
|
|
286
286
|
action,
|
|
287
287
|
loading,
|
|
288
|
-
onClick,
|
|
289
288
|
actionErrorEffect,
|
|
290
289
|
onActionPrevented,
|
|
291
290
|
onActionStart,
|
|
@@ -302,22 +301,16 @@ const ButtonWithAction = forwardRef((props, ref) => {
|
|
|
302
301
|
errorEffect: actionErrorEffect,
|
|
303
302
|
});
|
|
304
303
|
|
|
304
|
+
const innerLoading = loading || actionLoading;
|
|
305
|
+
|
|
305
306
|
useActionEvents(innerRef, {
|
|
306
307
|
onPrevented: onActionPrevented,
|
|
308
|
+
onRequested: (e) => forwardActionRequested(e, boundAction),
|
|
307
309
|
onAction: executeAction,
|
|
308
310
|
onStart: onActionStart,
|
|
309
311
|
onError: onActionError,
|
|
310
312
|
onEnd: onActionEnd,
|
|
311
313
|
});
|
|
312
|
-
const handleClick = (event) => {
|
|
313
|
-
event.preventDefault();
|
|
314
|
-
const button = innerRef.current;
|
|
315
|
-
requestAction(button, boundAction, {
|
|
316
|
-
event,
|
|
317
|
-
actionOrigin: "action_prop",
|
|
318
|
-
});
|
|
319
|
-
};
|
|
320
|
-
const innerLoading = loading || actionLoading;
|
|
321
314
|
|
|
322
315
|
return (
|
|
323
316
|
<ButtonBasic
|
|
@@ -326,10 +319,6 @@ const ButtonWithAction = forwardRef((props, ref) => {
|
|
|
326
319
|
{...rest}
|
|
327
320
|
ref={innerRef}
|
|
328
321
|
loading={innerLoading}
|
|
329
|
-
onClick={(event) => {
|
|
330
|
-
handleClick(event);
|
|
331
|
-
onClick?.(event);
|
|
332
|
-
}}
|
|
333
322
|
>
|
|
334
323
|
{children}
|
|
335
324
|
</ButtonBasic>
|
|
@@ -341,7 +330,6 @@ const ButtonInsideForm = forwardRef((props, ref) => {
|
|
|
341
330
|
// eslint-disable-next-line no-unused-vars
|
|
342
331
|
formContext,
|
|
343
332
|
type,
|
|
344
|
-
onClick,
|
|
345
333
|
children,
|
|
346
334
|
loading,
|
|
347
335
|
readOnly,
|
|
@@ -350,40 +338,8 @@ const ButtonInsideForm = forwardRef((props, ref) => {
|
|
|
350
338
|
const innerRef = useRef();
|
|
351
339
|
useImperativeHandle(ref, () => innerRef.current);
|
|
352
340
|
|
|
353
|
-
const wouldSubmitFormByType = type === "submit" || type === "image";
|
|
354
341
|
const innerLoading = loading;
|
|
355
342
|
const innerReadOnly = readOnly;
|
|
356
|
-
const handleClick = (event) => {
|
|
357
|
-
const buttonElement = innerRef.current;
|
|
358
|
-
const { form } = buttonElement;
|
|
359
|
-
let wouldSubmitForm = wouldSubmitFormByType;
|
|
360
|
-
if (!wouldSubmitForm && type === undefined) {
|
|
361
|
-
const formSubmitButton = form.querySelector(
|
|
362
|
-
"button[type='submit'], input[type='submit'], input[type='image']",
|
|
363
|
-
);
|
|
364
|
-
const wouldSubmitFormBecauseSingleButtonWithoutType = !formSubmitButton;
|
|
365
|
-
wouldSubmitForm = wouldSubmitFormBecauseSingleButtonWithoutType;
|
|
366
|
-
}
|
|
367
|
-
if (!wouldSubmitForm) {
|
|
368
|
-
if (buttonElement.hasAttribute("data-readonly")) {
|
|
369
|
-
event.preventDefault();
|
|
370
|
-
}
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
// prevent default behavior that would submit the form
|
|
374
|
-
// we want to go through the action execution process (with validation and all)
|
|
375
|
-
event.preventDefault();
|
|
376
|
-
form.dispatchEvent(
|
|
377
|
-
new CustomEvent("actionrequested", {
|
|
378
|
-
detail: {
|
|
379
|
-
requester: buttonElement,
|
|
380
|
-
event,
|
|
381
|
-
meta: { isSubmit: true },
|
|
382
|
-
actionOrigin: "action_prop",
|
|
383
|
-
},
|
|
384
|
-
}),
|
|
385
|
-
);
|
|
386
|
-
};
|
|
387
343
|
|
|
388
344
|
return (
|
|
389
345
|
<ButtonBasic
|
|
@@ -392,10 +348,6 @@ const ButtonInsideForm = forwardRef((props, ref) => {
|
|
|
392
348
|
type={type}
|
|
393
349
|
loading={innerLoading}
|
|
394
350
|
readOnly={innerReadOnly}
|
|
395
|
-
onClick={(event) => {
|
|
396
|
-
handleClick(event);
|
|
397
|
-
onClick?.(event);
|
|
398
|
-
}}
|
|
399
351
|
>
|
|
400
352
|
{children}
|
|
401
353
|
</ButtonBasic>
|
|
@@ -411,7 +363,6 @@ const ButtonWithActionInsideForm = forwardRef((props, ref) => {
|
|
|
411
363
|
action,
|
|
412
364
|
loading,
|
|
413
365
|
children,
|
|
414
|
-
onClick,
|
|
415
366
|
onActionPrevented,
|
|
416
367
|
onActionStart,
|
|
417
368
|
onActionAbort,
|
|
@@ -468,16 +419,8 @@ const ButtonWithActionInsideForm = forwardRef((props, ref) => {
|
|
|
468
419
|
ref={innerRef}
|
|
469
420
|
type={type}
|
|
470
421
|
loading={innerLoading}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
const form = button.form;
|
|
474
|
-
event.preventDefault();
|
|
475
|
-
requestAction(form, actionBoundToFormParams, {
|
|
476
|
-
event,
|
|
477
|
-
requester: button,
|
|
478
|
-
actionOrigin: "action_prop",
|
|
479
|
-
});
|
|
480
|
-
onClick?.(event);
|
|
422
|
+
onactionrequested={(e) => {
|
|
423
|
+
forwardActionRequested(e, actionBoundToFormParams, e.target.form);
|
|
481
424
|
}}
|
|
482
425
|
>
|
|
483
426
|
{children}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { forwardRef } from "preact/compat";
|
|
17
17
|
import { useContext, useImperativeHandle, useMemo, useRef } from "preact/hooks";
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { forwardActionRequested } from "../../validation/custom_constraint_validation.js";
|
|
20
20
|
import { useConstraints } from "../../validation/hooks/use_constraints.js";
|
|
21
21
|
import {
|
|
22
22
|
FormActionContext,
|
|
@@ -150,13 +150,7 @@ const FormWithAction = forwardRef((props, ref) => {
|
|
|
150
150
|
useActionEvents(innerRef, {
|
|
151
151
|
onPrevented: onActionPrevented,
|
|
152
152
|
onRequested: (e) => {
|
|
153
|
-
|
|
154
|
-
requestAction(form, actionBoundToUIState, {
|
|
155
|
-
requester: e.detail?.requester,
|
|
156
|
-
event: e.detail?.event || e,
|
|
157
|
-
meta: e.detail?.meta,
|
|
158
|
-
actionOrigin: e.detail?.actionOrigin,
|
|
159
|
-
});
|
|
153
|
+
forwardActionRequested(e, actionBoundToUIState);
|
|
160
154
|
},
|
|
161
155
|
onAction: (e) => {
|
|
162
156
|
const form = innerRef.current;
|
|
@@ -194,15 +188,6 @@ const FormWithAction = forwardRef((props, ref) => {
|
|
|
194
188
|
{...rest}
|
|
195
189
|
ref={innerRef}
|
|
196
190
|
loading={innerLoading}
|
|
197
|
-
onrequestsubmit={(e) => {
|
|
198
|
-
// prevent "submit" event that would be dispatched by the browser after form.requestSubmit()
|
|
199
|
-
// (not super important because our <form> listen the "action" and do does preventDefault on "submit")
|
|
200
|
-
e.preventDefault();
|
|
201
|
-
requestAction(e.target, actionBoundToUIState, {
|
|
202
|
-
event: e,
|
|
203
|
-
actionOrigin: "action_prop",
|
|
204
|
-
});
|
|
205
|
-
}}
|
|
206
191
|
>
|
|
207
192
|
<FormActionContext.Provider value={actionBoundToUIState}>
|
|
208
193
|
<LoadingElementContext.Provider value={formActionRequester}>
|
|
@@ -19,14 +19,13 @@
|
|
|
19
19
|
import { forwardRef } from "preact/compat";
|
|
20
20
|
import {
|
|
21
21
|
useContext,
|
|
22
|
-
useEffect,
|
|
23
22
|
useImperativeHandle,
|
|
24
23
|
useLayoutEffect,
|
|
25
24
|
useRef,
|
|
26
25
|
} from "preact/hooks";
|
|
27
26
|
|
|
28
27
|
import { useActionStatus } from "../../use_action_status.js";
|
|
29
|
-
import {
|
|
28
|
+
import { forwardActionRequested } from "../../validation/custom_constraint_validation.js";
|
|
30
29
|
import { useConstraints } from "../../validation/hooks/use_constraints.js";
|
|
31
30
|
import { renderActionableComponent } from "../action_execution/render_actionable_component.jsx";
|
|
32
31
|
import { useActionBoundToOneParam } from "../action_execution/use_action.js";
|
|
@@ -274,8 +273,6 @@ const InputTextualWithAction = forwardRef((props, ref) => {
|
|
|
274
273
|
cancelOnBlurInvalid,
|
|
275
274
|
cancelOnEscape,
|
|
276
275
|
actionErrorEffect,
|
|
277
|
-
onInput,
|
|
278
|
-
onKeyDown,
|
|
279
276
|
...rest
|
|
280
277
|
} = props;
|
|
281
278
|
const innerRef = useRef(null);
|
|
@@ -285,21 +282,6 @@ const InputTextualWithAction = forwardRef((props, ref) => {
|
|
|
285
282
|
const executeAction = useExecuteAction(innerRef, {
|
|
286
283
|
errorEffect: actionErrorEffect,
|
|
287
284
|
});
|
|
288
|
-
const valueAtInteractionRef = useRef(null);
|
|
289
|
-
|
|
290
|
-
useOnInputChange(innerRef, (e) => {
|
|
291
|
-
if (
|
|
292
|
-
valueAtInteractionRef.current !== null &&
|
|
293
|
-
e.target.value === valueAtInteractionRef.current
|
|
294
|
-
) {
|
|
295
|
-
valueAtInteractionRef.current = null;
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
requestAction(e.target, boundAction, {
|
|
299
|
-
event: e,
|
|
300
|
-
actionOrigin: "action_prop",
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
285
|
// here updating the input won't call the associated action
|
|
304
286
|
// (user have to blur or press enter for this to happen)
|
|
305
287
|
// so we can keep the ui state on cancel/abort/error and let user decide
|
|
@@ -322,16 +304,12 @@ const InputTextualWithAction = forwardRef((props, ref) => {
|
|
|
322
304
|
if (!cancelOnEscape) {
|
|
323
305
|
return;
|
|
324
306
|
}
|
|
325
|
-
/**
|
|
326
|
-
* Browser trigger a "change" event right after the escape is pressed
|
|
327
|
-
* if the input value has changed.
|
|
328
|
-
* We need to prevent the next change event otherwise we would request action when
|
|
329
|
-
* we actually want to cancel
|
|
330
|
-
*/
|
|
331
|
-
valueAtInteractionRef.current = e.target.value;
|
|
332
307
|
}
|
|
333
308
|
onCancel?.(e, reason);
|
|
334
309
|
},
|
|
310
|
+
onRequested: (e) => {
|
|
311
|
+
forwardActionRequested(e, boundAction);
|
|
312
|
+
},
|
|
335
313
|
onPrevented: onActionPrevented,
|
|
336
314
|
onAction: executeAction,
|
|
337
315
|
onStart: onActionStart,
|
|
@@ -345,135 +323,20 @@ const InputTextualWithAction = forwardRef((props, ref) => {
|
|
|
345
323
|
{...rest}
|
|
346
324
|
ref={innerRef}
|
|
347
325
|
loading={loading || actionLoading}
|
|
348
|
-
onInput={(e) => {
|
|
349
|
-
valueAtInteractionRef.current = null;
|
|
350
|
-
onInput?.(e);
|
|
351
|
-
}}
|
|
352
|
-
onKeyDown={(e) => {
|
|
353
|
-
if (e.key !== "Enter") {
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
e.preventDefault();
|
|
357
|
-
/**
|
|
358
|
-
* Browser trigger a "change" event right after the enter is pressed
|
|
359
|
-
* if the input value has changed.
|
|
360
|
-
* We need to prevent the next change event otherwise we would request action twice
|
|
361
|
-
*/
|
|
362
|
-
valueAtInteractionRef.current = e.target.value;
|
|
363
|
-
requestAction(e.target, boundAction, {
|
|
364
|
-
event: e,
|
|
365
|
-
actionOrigin: "action_prop",
|
|
366
|
-
});
|
|
367
|
-
onKeyDown?.(e);
|
|
368
|
-
}}
|
|
369
326
|
/>
|
|
370
327
|
);
|
|
371
328
|
});
|
|
372
329
|
const InputTextualInsideForm = forwardRef((props, ref) => {
|
|
373
330
|
const {
|
|
374
|
-
onKeyDown,
|
|
375
331
|
// We destructure formContext to avoid passing it to the underlying input element
|
|
376
332
|
// eslint-disable-next-line no-unused-vars
|
|
377
333
|
formContext,
|
|
378
334
|
...rest
|
|
379
335
|
} = props;
|
|
380
336
|
|
|
381
|
-
return
|
|
382
|
-
<InputTextualBasic
|
|
383
|
-
{...rest}
|
|
384
|
-
ref={ref}
|
|
385
|
-
onKeyDown={(e) => {
|
|
386
|
-
if (e.key === "Enter") {
|
|
387
|
-
const inputElement = e.target;
|
|
388
|
-
const { form } = inputElement;
|
|
389
|
-
const formSubmitButton = form.querySelector(
|
|
390
|
-
"button[type='submit'], input[type='submit'], input[type='image']",
|
|
391
|
-
);
|
|
392
|
-
e.preventDefault();
|
|
393
|
-
form.dispatchEvent(
|
|
394
|
-
new CustomEvent("actionrequested", {
|
|
395
|
-
detail: {
|
|
396
|
-
requester: formSubmitButton ? formSubmitButton : inputElement,
|
|
397
|
-
event: e,
|
|
398
|
-
meta: { isSubmit: true },
|
|
399
|
-
actionOrigin: "action_prop",
|
|
400
|
-
},
|
|
401
|
-
}),
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
onKeyDown?.(e);
|
|
405
|
-
}}
|
|
406
|
-
/>
|
|
407
|
-
);
|
|
337
|
+
return <InputTextualBasic {...rest} ref={ref} />;
|
|
408
338
|
});
|
|
409
339
|
|
|
410
|
-
const useOnInputChange = (inputRef, callback) => {
|
|
411
|
-
// we must use a custom event listener because preact bind onChange to onInput for compat with react
|
|
412
|
-
useEffect(() => {
|
|
413
|
-
const input = inputRef.current;
|
|
414
|
-
input.addEventListener("change", callback);
|
|
415
|
-
return () => {
|
|
416
|
-
input.removeEventListener("change", callback);
|
|
417
|
-
};
|
|
418
|
-
}, [callback]);
|
|
419
|
-
|
|
420
|
-
// Handle programmatic value changes that don't trigger browser change events
|
|
421
|
-
//
|
|
422
|
-
// Problem: When input values are set programmatically (not by user typing),
|
|
423
|
-
// browsers don't fire the 'change' event. However, our application logic
|
|
424
|
-
// still needs to detect these changes.
|
|
425
|
-
//
|
|
426
|
-
// Example scenario:
|
|
427
|
-
// 1. User starts editing (letter key pressed, value set programmatically)
|
|
428
|
-
// 2. User doesn't type anything additional (this is the key part)
|
|
429
|
-
// 3. User clicks outside to finish editing
|
|
430
|
-
// 4. Without this code, no change event would fire despite the fact that the input value did change from its original state
|
|
431
|
-
//
|
|
432
|
-
// This distinction is crucial because:
|
|
433
|
-
//
|
|
434
|
-
// - If the user typed additional text after the initial programmatic value,
|
|
435
|
-
// the browser would fire change events normally
|
|
436
|
-
// - But when they don't type anything else, the browser considers it as "no user interaction"
|
|
437
|
-
// even though the programmatic initial value represents a meaningful change
|
|
438
|
-
const valueAtStartRef = useRef();
|
|
439
|
-
const interactedRef = useRef(false);
|
|
440
|
-
useLayoutEffect(() => {
|
|
441
|
-
const input = inputRef.current;
|
|
442
|
-
valueAtStartRef.current = input.value;
|
|
443
|
-
|
|
444
|
-
const onfocus = () => {
|
|
445
|
-
interactedRef.current = false;
|
|
446
|
-
valueAtStartRef.current = input.value;
|
|
447
|
-
};
|
|
448
|
-
const oninput = (e) => {
|
|
449
|
-
if (!e.isTrusted) {
|
|
450
|
-
// non trusted "input" events will be ignored by the browser when deciding to fire "change" event
|
|
451
|
-
// we ignore them too
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
interactedRef.current = true;
|
|
455
|
-
};
|
|
456
|
-
const onblur = (e) => {
|
|
457
|
-
if (interactedRef.current) {
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
if (valueAtStartRef.current === input.value) {
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
callback(e);
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
input.addEventListener("focus", onfocus);
|
|
467
|
-
input.addEventListener("input", oninput);
|
|
468
|
-
input.addEventListener("blur", onblur);
|
|
469
|
-
|
|
470
|
-
return () => {
|
|
471
|
-
input.removeEventListener("focus", onfocus);
|
|
472
|
-
input.removeEventListener("input", oninput);
|
|
473
|
-
input.removeEventListener("blur", onblur);
|
|
474
|
-
};
|
|
475
|
-
}, []);
|
|
476
|
-
};
|
|
477
340
|
// As explained in https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/datetime-local#setting_timezones
|
|
478
341
|
// datetime-local does not support timezones
|
|
479
342
|
const convertToLocalTimezone = (dateTimeString) => {
|
|
@@ -37,7 +37,6 @@ const SelectControlled = forwardRef((props, ref) => {
|
|
|
37
37
|
<select
|
|
38
38
|
className="navi_select"
|
|
39
39
|
ref={innerRef}
|
|
40
|
-
data-field=""
|
|
41
40
|
data-readonly={readOnly && !disabled ? "" : undefined}
|
|
42
41
|
onKeyDown={(e) => {
|
|
43
42
|
if (readOnly) {
|
|
@@ -196,7 +195,6 @@ const SelectWithAction = forwardRef((props, ref) => {
|
|
|
196
195
|
requestAction(radioListContainer, boundAction, {
|
|
197
196
|
event,
|
|
198
197
|
requester: optionSelected,
|
|
199
|
-
actionOrigin: "action_prop",
|
|
200
198
|
});
|
|
201
199
|
}}
|
|
202
200
|
{...rest}
|
|
@@ -7,6 +7,7 @@ export const useFormEvents = (
|
|
|
7
7
|
elementRef,
|
|
8
8
|
{
|
|
9
9
|
onFormReset,
|
|
10
|
+
onFormActionRequested,
|
|
10
11
|
onFormActionPrevented,
|
|
11
12
|
onFormActionStart,
|
|
12
13
|
onFormActionAbort,
|
|
@@ -15,6 +16,7 @@ export const useFormEvents = (
|
|
|
15
16
|
},
|
|
16
17
|
) => {
|
|
17
18
|
onFormReset = useStableCallback(onFormReset);
|
|
19
|
+
onFormActionRequested = useStableCallback(onFormActionRequested);
|
|
18
20
|
onFormActionPrevented = useStableCallback(onFormActionPrevented);
|
|
19
21
|
onFormActionStart = useStableCallback(onFormActionStart);
|
|
20
22
|
onFormActionAbort = useStableCallback(onFormActionAbort);
|
|
@@ -38,6 +40,7 @@ export const useFormEvents = (
|
|
|
38
40
|
}
|
|
39
41
|
return addManyEventListeners(form, {
|
|
40
42
|
reset: onFormReset,
|
|
43
|
+
actionrequested: onFormActionRequested,
|
|
41
44
|
actionprevented: onFormActionPrevented,
|
|
42
45
|
actionstart: onFormActionStart,
|
|
43
46
|
actionabort: onFormActionAbort,
|
|
@@ -46,6 +49,7 @@ export const useFormEvents = (
|
|
|
46
49
|
});
|
|
47
50
|
}, [
|
|
48
51
|
onFormReset,
|
|
52
|
+
onFormActionRequested,
|
|
49
53
|
onFormActionPrevented,
|
|
50
54
|
onFormActionStart,
|
|
51
55
|
onFormActionAbort,
|
|
@@ -165,10 +165,10 @@ export const useKeyboardShortcuts = (
|
|
|
165
165
|
}
|
|
166
166
|
const { action } = shortcutCandidate;
|
|
167
167
|
return requestAction(element, action, {
|
|
168
|
+
actionOrigin: "keyboard_shortcut",
|
|
168
169
|
event: keyboardEvent,
|
|
169
170
|
requester: document.activeElement,
|
|
170
171
|
confirmMessage: shortcutCandidate.confirmMessage,
|
|
171
|
-
actionOrigin: "keyboard_shortcut",
|
|
172
172
|
meta: {
|
|
173
173
|
shortcut: shortcutCandidate,
|
|
174
174
|
},
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
SelectionContext,
|
|
21
21
|
useSelectableElement,
|
|
22
22
|
} from "../selection/selection.jsx";
|
|
23
|
+
import { useTypographyStyle } from "../text/text.jsx";
|
|
23
24
|
import { useAutoFocus } from "../use_auto_focus.js";
|
|
24
25
|
|
|
25
26
|
/*
|
|
@@ -35,45 +36,40 @@ import.meta.css = /* css */ `
|
|
|
35
36
|
.navi_link {
|
|
36
37
|
border-radius: 2px;
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
+
/* Focus */
|
|
39
40
|
.navi_link:focus {
|
|
40
41
|
position: relative;
|
|
41
42
|
z-index: 1; /* Ensure focus outline is above other elements */
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
-
.navi_link[data-
|
|
45
|
-
|
|
46
|
-
opacity: 0.5;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.navi_link[inert] {
|
|
50
|
-
pointer-events: none;
|
|
44
|
+
/* Visited */
|
|
45
|
+
.navi_link[data-visited] {
|
|
46
|
+
color: light-dark(#6a1b9a, #ab47bc);
|
|
51
47
|
}
|
|
52
|
-
|
|
48
|
+
/* Selected */
|
|
53
49
|
.navi_link[aria-selected] {
|
|
54
50
|
position: relative;
|
|
55
51
|
}
|
|
56
|
-
|
|
52
|
+
.navi_link[aria-selected="true"] {
|
|
53
|
+
background-color: light-dark(#bbdefb, #2563eb);
|
|
54
|
+
}
|
|
57
55
|
.navi_link[aria-selected] input[type="checkbox"] {
|
|
58
56
|
position: absolute;
|
|
59
57
|
opacity: 0;
|
|
60
58
|
}
|
|
61
|
-
|
|
62
|
-
/* Visual feedback for selected state */
|
|
63
|
-
.navi_link[aria-selected="true"] {
|
|
64
|
-
background-color: light-dark(#bbdefb, #2563eb);
|
|
65
|
-
}
|
|
66
|
-
|
|
59
|
+
/* Active */
|
|
67
60
|
.navi_link[data-active] {
|
|
68
61
|
font-weight: bold;
|
|
69
62
|
}
|
|
70
|
-
|
|
71
|
-
.navi_link[data-
|
|
72
|
-
|
|
63
|
+
/* Readonly */
|
|
64
|
+
.navi_link[data-readonly] > * {
|
|
65
|
+
opacity: 0.5;
|
|
73
66
|
}
|
|
74
|
-
|
|
75
|
-
.navi_link[
|
|
76
|
-
|
|
67
|
+
/* Disabled */
|
|
68
|
+
.navi_link[inert] {
|
|
69
|
+
pointer-events: none;
|
|
70
|
+
}
|
|
71
|
+
.navi_link[inert] > * {
|
|
72
|
+
opacity: 0.5;
|
|
77
73
|
}
|
|
78
74
|
`;
|
|
79
75
|
|
|
@@ -122,7 +118,13 @@ const LinkPlain = forwardRef((props, ref) => {
|
|
|
122
118
|
|
|
123
119
|
const innerClassName = withPropsClassName("navi_link", className);
|
|
124
120
|
const { all } = useLayoutStyle(rest);
|
|
125
|
-
const innerStyle = withPropsStyle(
|
|
121
|
+
const innerStyle = withPropsStyle(
|
|
122
|
+
{
|
|
123
|
+
...all,
|
|
124
|
+
...useTypographyStyle(rest),
|
|
125
|
+
},
|
|
126
|
+
style,
|
|
127
|
+
);
|
|
126
128
|
|
|
127
129
|
return (
|
|
128
130
|
<LoadableInlineElement
|
|
@@ -137,7 +139,7 @@ const LinkPlain = forwardRef((props, ref) => {
|
|
|
137
139
|
style={innerStyle}
|
|
138
140
|
aria-busy={loading}
|
|
139
141
|
inert={disabled}
|
|
140
|
-
data-
|
|
142
|
+
data-disabled={disabled ? "" : undefined}
|
|
141
143
|
data-readonly={readOnly ? "" : undefined}
|
|
142
144
|
data-active={active ? "" : undefined}
|
|
143
145
|
data-visited={visited || isVisited ? "" : undefined}
|