@jsenv/navi 0.0.1
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/index.js +51 -0
- package/package.json +38 -0
- package/src/action_private_properties.js +11 -0
- package/src/action_proxy_test.html +353 -0
- package/src/action_run_states.js +5 -0
- package/src/actions.js +1377 -0
- package/src/browser_integration/browser_integration.js +191 -0
- package/src/browser_integration/document_back_and_forward.js +17 -0
- package/src/browser_integration/document_loading_signal.js +100 -0
- package/src/browser_integration/document_state_signal.js +9 -0
- package/src/browser_integration/document_url_signal.js +9 -0
- package/src/browser_integration/use_is_visited.js +19 -0
- package/src/browser_integration/via_history.js +199 -0
- package/src/browser_integration/via_navigation.js +168 -0
- package/src/components/action_execution/form_context.js +8 -0
- package/src/components/action_execution/render_actionable_component.jsx +27 -0
- package/src/components/action_execution/use_action.js +330 -0
- package/src/components/action_execution/use_execute_action.js +161 -0
- package/src/components/action_renderer.jsx +136 -0
- package/src/components/collect_form_element_values.js +79 -0
- package/src/components/demos/0_button_demo.html +155 -0
- package/src/components/demos/1_checkbox_demo.html +257 -0
- package/src/components/demos/2_input_textual_demo.html +354 -0
- package/src/components/demos/3_radio_demo.html +222 -0
- package/src/components/demos/4_select_demo.html +104 -0
- package/src/components/demos/5_list_scrollable_demo.html +153 -0
- package/src/components/demos/action/0_button_demo.html +204 -0
- package/src/components/demos/action/10_shortcuts_demo.html +189 -0
- package/src/components/demos/action/11_nested_shortcuts_demo.html +401 -0
- package/src/components/demos/action/1_input_text_demo.html +461 -0
- package/src/components/demos/action/2_form_multiple.html +303 -0
- package/src/components/demos/action/3_details_demo.html +172 -0
- package/src/components/demos/action/4_input_checkbox_demo.html +611 -0
- package/src/components/demos/action/6_checkbox_list_demo.html +109 -0
- package/src/components/demos/action/7_radio_list_demo.html +217 -0
- package/src/components/demos/action/8_editable_text_demo.html +442 -0
- package/src/components/demos/action/9_link_demo.html +172 -0
- package/src/components/demos/demo.md +0 -0
- package/src/components/demos/route/basic/basic.html +14 -0
- package/src/components/demos/route/basic/basic_route_demo.jsx +224 -0
- package/src/components/demos/route/multi/multi.html +14 -0
- package/src/components/demos/route/multi/multi_route_demo.jsx +277 -0
- package/src/components/details/details.jsx +248 -0
- package/src/components/details/summary_marker.jsx +141 -0
- package/src/components/editable_text/editable_text.jsx +96 -0
- package/src/components/error_boundary_context.js +9 -0
- package/src/components/form.jsx +144 -0
- package/src/components/input/button.jsx +333 -0
- package/src/components/input/checkbox_list.jsx +294 -0
- package/src/components/input/field.jsx +61 -0
- package/src/components/input/field_css.js +118 -0
- package/src/components/input/input.jsx +15 -0
- package/src/components/input/input_checkbox.jsx +370 -0
- package/src/components/input/input_radio.jsx +299 -0
- package/src/components/input/input_textual.jsx +338 -0
- package/src/components/input/radio_list.jsx +283 -0
- package/src/components/input/select.jsx +273 -0
- package/src/components/input/use_form_event.js +20 -0
- package/src/components/input/use_on_change.js +12 -0
- package/src/components/link/link.jsx +291 -0
- package/src/components/loader/loader_background.jsx +324 -0
- package/src/components/loader/loading_spinner.jsx +68 -0
- package/src/components/loader/network_speed.js +83 -0
- package/src/components/loader/rectangle_loading.jsx +225 -0
- package/src/components/route.jsx +15 -0
- package/src/components/selection/selection.js +5 -0
- package/src/components/selection/selection_context.jsx +262 -0
- package/src/components/shortcut/os.js +9 -0
- package/src/components/shortcut/shortcut_context.jsx +390 -0
- package/src/components/use_action_events.js +37 -0
- package/src/components/use_auto_focus.js +43 -0
- package/src/components/use_debounce_true.js +31 -0
- package/src/components/use_focus_group.js +19 -0
- package/src/components/use_initial_value.js +104 -0
- package/src/components/use_is_visited.js +19 -0
- package/src/components/use_ref_array.js +38 -0
- package/src/components/use_signal_sync.js +50 -0
- package/src/components/use_state_array.js +40 -0
- package/src/docs/actions.md +228 -0
- package/src/docs/demos/resource/action_status.jsx +42 -0
- package/src/docs/demos/resource/demo.md +1 -0
- package/src/docs/demos/resource/resource_demo_0.html +84 -0
- package/src/docs/demos/resource/resource_demo_10_post_gc.html +364 -0
- package/src/docs/demos/resource/resource_demo_11_describe_many.html +362 -0
- package/src/docs/demos/resource/resource_demo_2.html +173 -0
- package/src/docs/demos/resource/resource_demo_3_filtered_users.html +415 -0
- package/src/docs/demos/resource/resource_demo_4_details.html +284 -0
- package/src/docs/demos/resource/resource_demo_5_renderer_lazy.html +115 -0
- package/src/docs/demos/resource/resource_demo_6_gc.html +217 -0
- package/src/docs/demos/resource/resource_demo_7_child_gc.html +240 -0
- package/src/docs/demos/resource/resource_demo_8_proxy_gc.html +319 -0
- package/src/docs/demos/resource/resource_demo_9_describe_one.html +472 -0
- package/src/docs/demos/resource/tata.jsx +3 -0
- package/src/docs/demos/resource/toto.jsx +3 -0
- package/src/docs/demos/user_nav/user_nav.html +12 -0
- package/src/docs/demos/user_nav/user_nav.jsx +330 -0
- package/src/docs/resource_dependencies.md +103 -0
- package/src/docs/resource_with_params.md +80 -0
- package/src/notes.md +13 -0
- package/src/route/route.js +518 -0
- package/src/route/route.test.html +228 -0
- package/src/store/array_signal_store.js +537 -0
- package/src/store/local_storage_signal.js +17 -0
- package/src/store/resource_graph.js +1303 -0
- package/src/store/tests/resource_graph_autoreload_demo.html +12 -0
- package/src/store/tests/resource_graph_autoreload_demo.jsx +964 -0
- package/src/store/tests/resource_graph_dependencies.test.js +95 -0
- package/src/store/value_in_local_storage.js +187 -0
- package/src/symbol_object_signal.js +1 -0
- package/src/use_action_data.js +10 -0
- package/src/use_action_status.js +47 -0
- package/src/utils/add_many_event_listeners.js +15 -0
- package/src/utils/array_add_remove.js +61 -0
- package/src/utils/array_signal.js +15 -0
- package/src/utils/compare_two_js_values.js +172 -0
- package/src/utils/execute_with_cleanup.js +21 -0
- package/src/utils/get_caller_info.js +85 -0
- package/src/utils/iterable_weak_set.js +62 -0
- package/src/utils/js_value_weak_map.js +162 -0
- package/src/utils/js_value_weak_map_demo.html +690 -0
- package/src/utils/merge_two_js_values.js +53 -0
- package/src/utils/stringify_for_display.js +150 -0
- package/src/utils/weak_effect.js +48 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TODO: when switching from one state to another we should preserve the dimensions to prevent layout shift
|
|
3
|
+
* the exact way to do this is not yet clear but I suspect something as follow:
|
|
4
|
+
*
|
|
5
|
+
*
|
|
6
|
+
* While content is loading we don't know (except if we are given an size)
|
|
7
|
+
* When reloading the content will be gone, we should keep a placeholder taking the same space
|
|
8
|
+
* When there is an error the error should take the same space as the content
|
|
9
|
+
* but be displayed on top
|
|
10
|
+
* (If error is bigger it can take more space? I guess so, maybe an overflow would be better to prevent layout shit again)
|
|
11
|
+
*
|
|
12
|
+
* And once we know the new content size ideally we could have some sort of transition
|
|
13
|
+
* (like an height transition from current height to new height)
|
|
14
|
+
*
|
|
15
|
+
* consider https://motion.dev/docs/react-layout-animations
|
|
16
|
+
*
|
|
17
|
+
* but might be too complexe for what we want.
|
|
18
|
+
* we want ability to transit from anything to anything, it's not a layout change
|
|
19
|
+
* it's more view transition but with a very simple behavior
|
|
20
|
+
*
|
|
21
|
+
* And certainly this https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API#pseudo-elements
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { useErrorBoundary, useLayoutEffect } from "preact/hooks";
|
|
26
|
+
import { getActionPrivateProperties } from "../action_private_properties.js";
|
|
27
|
+
import { useActionStatus } from "../use_action_status.js";
|
|
28
|
+
|
|
29
|
+
import.meta.css = /* css */ `
|
|
30
|
+
.action_error {
|
|
31
|
+
padding: 20px;
|
|
32
|
+
background: #fdd;
|
|
33
|
+
border: 1px solid red;
|
|
34
|
+
margin-top: 0;
|
|
35
|
+
margin-bottom: 20px;
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const renderIdleDefault = () => null;
|
|
40
|
+
const renderLoadingDefault = () => null;
|
|
41
|
+
const renderAbortedDefault = () => null;
|
|
42
|
+
const renderErrorDefault = (error) => {
|
|
43
|
+
let routeErrorText = error && error.message ? error.message : error;
|
|
44
|
+
return <p className="action_error">An error occured: {routeErrorText}</p>;
|
|
45
|
+
};
|
|
46
|
+
const renderCompletedDefault = () => null;
|
|
47
|
+
|
|
48
|
+
export const ActionRenderer = ({ action, children }) => {
|
|
49
|
+
const {
|
|
50
|
+
idle: renderIdle = renderIdleDefault,
|
|
51
|
+
loading: renderLoading = renderLoadingDefault,
|
|
52
|
+
aborted: renderAborted = renderAbortedDefault,
|
|
53
|
+
error: renderError = renderErrorDefault,
|
|
54
|
+
completed: renderCompleted,
|
|
55
|
+
always: renderAlways,
|
|
56
|
+
} = typeof children === "function" ? { completed: children } : children || {};
|
|
57
|
+
if (!action) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"ActionRenderer requires an action to render, but none was provided.",
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const { idle, loading, aborted, error, data } = useActionStatus(action);
|
|
63
|
+
const UIRenderedPromise = useUIRenderedPromise(action);
|
|
64
|
+
const [errorBoundary, resetErrorBoundary] = useErrorBoundary();
|
|
65
|
+
|
|
66
|
+
// Mark this action as bound to UI components (has renderers)
|
|
67
|
+
// This tells the action system that errors should be caught and stored
|
|
68
|
+
// in the action's error state rather than bubbling up
|
|
69
|
+
useLayoutEffect(() => {
|
|
70
|
+
const { ui } = getActionPrivateProperties(action);
|
|
71
|
+
ui.hasRenderers = true;
|
|
72
|
+
}, [action]);
|
|
73
|
+
|
|
74
|
+
useLayoutEffect(() => {
|
|
75
|
+
resetErrorBoundary();
|
|
76
|
+
}, [action, loading, idle, resetErrorBoundary]);
|
|
77
|
+
|
|
78
|
+
useLayoutEffect(() => {
|
|
79
|
+
UIRenderedPromise.resolve();
|
|
80
|
+
return () => {
|
|
81
|
+
actionUIRenderedPromiseWeakMap.delete(action);
|
|
82
|
+
};
|
|
83
|
+
}, []);
|
|
84
|
+
|
|
85
|
+
// If renderAlways is provided, it wins and handles all rendering
|
|
86
|
+
if (renderAlways) {
|
|
87
|
+
return renderAlways({ idle, loading, aborted, error, data });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (idle) {
|
|
91
|
+
return renderIdle(action);
|
|
92
|
+
}
|
|
93
|
+
if (errorBoundary) {
|
|
94
|
+
return renderError(errorBoundary, "ui_error", action);
|
|
95
|
+
}
|
|
96
|
+
if (error) {
|
|
97
|
+
return renderError(error, "action_error", action);
|
|
98
|
+
}
|
|
99
|
+
if (aborted) {
|
|
100
|
+
return renderAborted(action);
|
|
101
|
+
}
|
|
102
|
+
let renderCompletedSafe;
|
|
103
|
+
if (renderCompleted) {
|
|
104
|
+
renderCompletedSafe = renderCompleted;
|
|
105
|
+
} else {
|
|
106
|
+
const { ui } = getActionPrivateProperties(action);
|
|
107
|
+
if (ui.renderCompleted) {
|
|
108
|
+
renderCompletedSafe = ui.renderCompleted;
|
|
109
|
+
} else {
|
|
110
|
+
renderCompletedSafe = renderCompletedDefault;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (loading) {
|
|
114
|
+
if (action.canDisplayOldData && data !== undefined) {
|
|
115
|
+
return renderCompletedSafe(data, action);
|
|
116
|
+
}
|
|
117
|
+
return renderLoading(action);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return renderCompletedSafe(data, action);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const actionUIRenderedPromiseWeakMap = new WeakMap();
|
|
124
|
+
const useUIRenderedPromise = (route) => {
|
|
125
|
+
const actionUIRenderedPromise = actionUIRenderedPromiseWeakMap.get(route);
|
|
126
|
+
if (actionUIRenderedPromise) {
|
|
127
|
+
return actionUIRenderedPromise;
|
|
128
|
+
}
|
|
129
|
+
let resolve;
|
|
130
|
+
const promise = new Promise((res) => {
|
|
131
|
+
resolve = res;
|
|
132
|
+
});
|
|
133
|
+
promise.resolve = resolve;
|
|
134
|
+
actionUIRenderedPromiseWeakMap.set(route, promise);
|
|
135
|
+
return promise;
|
|
136
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export const collectFormElementValues = (element) => {
|
|
2
|
+
let formElements;
|
|
3
|
+
if (element.tagName === "FORM") {
|
|
4
|
+
formElements = element.elements;
|
|
5
|
+
} else {
|
|
6
|
+
// fieldset or anything else
|
|
7
|
+
formElements = element.querySelectorAll(
|
|
8
|
+
"input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button[name]:not([disabled])",
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const values = {};
|
|
13
|
+
const checkboxArrayNameSet = new Set();
|
|
14
|
+
for (const formElement of formElements) {
|
|
15
|
+
if (formElement.type === "checkbox" && formElement.name) {
|
|
16
|
+
const name = formElement.name;
|
|
17
|
+
const endsWithBrackets = name.endsWith("[]");
|
|
18
|
+
if (endsWithBrackets) {
|
|
19
|
+
checkboxArrayNameSet.add(name);
|
|
20
|
+
values[name] = [];
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const closestDataCheckboxList = formElement.closest(
|
|
24
|
+
"[data-checkbox-list]",
|
|
25
|
+
);
|
|
26
|
+
if (closestDataCheckboxList) {
|
|
27
|
+
checkboxArrayNameSet.add(name);
|
|
28
|
+
values[name] = [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const formElement of formElements) {
|
|
34
|
+
const name = formElement.name;
|
|
35
|
+
if (!name) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const value = getFormElementValue(formElement);
|
|
39
|
+
if (value === undefined) {
|
|
40
|
+
continue; // Skip unchecked checkboxes/radios
|
|
41
|
+
}
|
|
42
|
+
if (formElement.type === "checkbox" && checkboxArrayNameSet.has(name)) {
|
|
43
|
+
values[name].push(value);
|
|
44
|
+
} else {
|
|
45
|
+
values[name] = value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return values;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getFormElementValue = (formElement) => {
|
|
52
|
+
const { type, tagName } = formElement;
|
|
53
|
+
|
|
54
|
+
if (tagName === "SELECT") {
|
|
55
|
+
if (formElement.multiple) {
|
|
56
|
+
return Array.from(formElement.selectedOptions, (option) =>
|
|
57
|
+
getValue(option),
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return formElement.value;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (type === "checkbox" || type === "radio") {
|
|
64
|
+
return formElement.checked ? getValue(formElement) : undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (type === "file") {
|
|
68
|
+
return formElement.files; // Return FileList for special handling
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return getValue(formElement);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const getValue = (formElement) => {
|
|
75
|
+
const hasDataFormValueAttribute = formElement.hasAttribute("data-form-value");
|
|
76
|
+
return hasDataFormValueAttribute
|
|
77
|
+
? formElement.getAttribute("data-form-value")
|
|
78
|
+
: formElement.value;
|
|
79
|
+
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" href="data:," />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Button loading demo</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root" style="position: relative"></div>
|
|
11
|
+
|
|
12
|
+
<script type="module" jsenv-type="module/jsx">
|
|
13
|
+
import { render } from "preact";
|
|
14
|
+
import { useState } from "preact/hooks";
|
|
15
|
+
import {
|
|
16
|
+
// eslint-disable-next-line no-unused-vars
|
|
17
|
+
Button,
|
|
18
|
+
// eslint-disable-next-line no-unused-vars
|
|
19
|
+
Field,
|
|
20
|
+
} from "@jsenv/navi";
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line no-unused-vars
|
|
23
|
+
const App = () => {
|
|
24
|
+
const [loading, setLoading] = useState(false);
|
|
25
|
+
const [loadingB, setLoadingB] = useState(false);
|
|
26
|
+
const [loadingC, setLoadingC] = useState(false);
|
|
27
|
+
const [loadingD, setLoadingD] = useState(false);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div>
|
|
31
|
+
<div style="display: flex; flex-direction: row; gap: 30px">
|
|
32
|
+
<div>
|
|
33
|
+
<p>
|
|
34
|
+
<strong>Loading</strong>
|
|
35
|
+
</p>
|
|
36
|
+
<Button loading={loading}>Toggle</Button>
|
|
37
|
+
<br />
|
|
38
|
+
<button
|
|
39
|
+
onClick={() => {
|
|
40
|
+
setLoading(!loading);
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
Toggle
|
|
44
|
+
</button>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div>
|
|
48
|
+
<p>
|
|
49
|
+
<strong>Loading (action)</strong>
|
|
50
|
+
</p>
|
|
51
|
+
<Button loading={loadingB} action={() => {}}>
|
|
52
|
+
B
|
|
53
|
+
</Button>
|
|
54
|
+
<br /> <br />
|
|
55
|
+
<button
|
|
56
|
+
onClick={() => {
|
|
57
|
+
setLoadingB(!loadingB);
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
Toggle
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div>
|
|
65
|
+
<p>
|
|
66
|
+
<strong>Readonly</strong>
|
|
67
|
+
</p>
|
|
68
|
+
<Button loading={loadingC} readOnly>
|
|
69
|
+
C
|
|
70
|
+
</Button>
|
|
71
|
+
<br />
|
|
72
|
+
<button data-readonly>C</button>
|
|
73
|
+
<br />
|
|
74
|
+
<br />
|
|
75
|
+
<button>
|
|
76
|
+
<span
|
|
77
|
+
onClick={() => {
|
|
78
|
+
setLoadingC(!loadingC);
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
Toggle
|
|
82
|
+
</span>
|
|
83
|
+
</button>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div>
|
|
87
|
+
<p>
|
|
88
|
+
<strong>Disabled</strong>
|
|
89
|
+
</p>
|
|
90
|
+
<Button loading={loadingD} disabled>
|
|
91
|
+
D
|
|
92
|
+
</Button>
|
|
93
|
+
<br />
|
|
94
|
+
<button disabled>D</button>
|
|
95
|
+
<br /> <br />
|
|
96
|
+
<button>
|
|
97
|
+
<span
|
|
98
|
+
onClick={() => {
|
|
99
|
+
setLoadingD(!loadingD);
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
Toggle
|
|
103
|
+
</span>
|
|
104
|
+
</button>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div style="display: flex; flex-direction: row; gap: 30px">
|
|
109
|
+
<div>
|
|
110
|
+
<p>
|
|
111
|
+
<strong>Large button (anim speed shoud be consistent)</strong>
|
|
112
|
+
</p>
|
|
113
|
+
<Button loading={loading}>
|
|
114
|
+
This button is quite large but loader speed should be same as
|
|
115
|
+
small buttons
|
|
116
|
+
</Button>
|
|
117
|
+
<br />
|
|
118
|
+
<button
|
|
119
|
+
onClick={() => {
|
|
120
|
+
setLoading(!loading);
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
Toggle
|
|
124
|
+
</button>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div>
|
|
129
|
+
<div>
|
|
130
|
+
<p>
|
|
131
|
+
<strong>Big borders</strong>
|
|
132
|
+
</p>
|
|
133
|
+
<Button loading={loadingC} borderWidth={10}>
|
|
134
|
+
C
|
|
135
|
+
</Button>
|
|
136
|
+
<br /> <br />
|
|
137
|
+
<button>
|
|
138
|
+
<span
|
|
139
|
+
onClick={() => {
|
|
140
|
+
setLoadingC(!loadingC);
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
143
|
+
Toggle
|
|
144
|
+
</span>
|
|
145
|
+
</button>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
render(<App />, document.querySelector("#root"));
|
|
153
|
+
</script>
|
|
154
|
+
</body>
|
|
155
|
+
</html>
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" href="data:," />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Checkbox demo</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root" style="position: relative"></div>
|
|
11
|
+
|
|
12
|
+
<script type="module" jsenv-type="module/jsx">
|
|
13
|
+
import { render } from "preact";
|
|
14
|
+
import { useState } from "preact/hooks";
|
|
15
|
+
import {
|
|
16
|
+
// eslint-disable-next-line no-unused-vars
|
|
17
|
+
Input,
|
|
18
|
+
// eslint-disable-next-line no-unused-vars
|
|
19
|
+
Field,
|
|
20
|
+
} from "@jsenv/navi";
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line no-unused-vars
|
|
23
|
+
const App = () => {
|
|
24
|
+
const [loading, setLoading] = useState(false);
|
|
25
|
+
const [loadingB, setLoadingB] = useState(false);
|
|
26
|
+
const [loadingC, setLoadingC] = useState(false);
|
|
27
|
+
const [loadingD, setLoadingD] = useState(false);
|
|
28
|
+
const [loadingE, setLoadingE] = useState(false);
|
|
29
|
+
const [loadingF, setLoadingF] = useState(false);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div>
|
|
33
|
+
<div style="display: flex; flex-direction: row; gap: 30px">
|
|
34
|
+
<div>
|
|
35
|
+
<p>
|
|
36
|
+
<strong>Loading</strong>
|
|
37
|
+
</p>
|
|
38
|
+
|
|
39
|
+
<Field
|
|
40
|
+
input={
|
|
41
|
+
<Input
|
|
42
|
+
id="test"
|
|
43
|
+
type="checkbox"
|
|
44
|
+
name="colors"
|
|
45
|
+
loading={loading}
|
|
46
|
+
/>
|
|
47
|
+
}
|
|
48
|
+
label="Coucou"
|
|
49
|
+
></Field>
|
|
50
|
+
|
|
51
|
+
<button>
|
|
52
|
+
<span
|
|
53
|
+
onClick={() => {
|
|
54
|
+
setLoading(!loading);
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
Toggle
|
|
58
|
+
</span>
|
|
59
|
+
</button>
|
|
60
|
+
|
|
61
|
+
<div>
|
|
62
|
+
<label>
|
|
63
|
+
<input type="checkbox" name="colors" checked />
|
|
64
|
+
Coucou
|
|
65
|
+
</label>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div>
|
|
71
|
+
<div>
|
|
72
|
+
<p>
|
|
73
|
+
<strong>Loading with action</strong>
|
|
74
|
+
</p>
|
|
75
|
+
|
|
76
|
+
<Field
|
|
77
|
+
input={
|
|
78
|
+
<Input
|
|
79
|
+
id="test"
|
|
80
|
+
action={() => {}}
|
|
81
|
+
type="checkbox"
|
|
82
|
+
name="colors"
|
|
83
|
+
loading={loadingB}
|
|
84
|
+
/>
|
|
85
|
+
}
|
|
86
|
+
label="Coucou"
|
|
87
|
+
></Field>
|
|
88
|
+
|
|
89
|
+
<button>
|
|
90
|
+
<span
|
|
91
|
+
onClick={() => {
|
|
92
|
+
setLoadingB(!loadingB);
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
Toggle
|
|
96
|
+
</span>
|
|
97
|
+
</button>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<div>
|
|
102
|
+
<div>
|
|
103
|
+
<p>
|
|
104
|
+
<strong>Read Only</strong>
|
|
105
|
+
</p>
|
|
106
|
+
|
|
107
|
+
<Field
|
|
108
|
+
input={
|
|
109
|
+
<Input
|
|
110
|
+
id="test"
|
|
111
|
+
readOnly
|
|
112
|
+
type="checkbox"
|
|
113
|
+
name="colors"
|
|
114
|
+
loading={loadingC}
|
|
115
|
+
/>
|
|
116
|
+
}
|
|
117
|
+
label="C"
|
|
118
|
+
/>
|
|
119
|
+
|
|
120
|
+
<button>
|
|
121
|
+
<span
|
|
122
|
+
onClick={() => {
|
|
123
|
+
setLoadingC(!loadingC);
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
Toggle
|
|
127
|
+
</span>
|
|
128
|
+
</button>
|
|
129
|
+
|
|
130
|
+
<div>
|
|
131
|
+
<label>
|
|
132
|
+
<input type="checkbox" name="colors" readonly />C
|
|
133
|
+
</label>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<div>
|
|
139
|
+
<div>
|
|
140
|
+
<p>
|
|
141
|
+
<strong>Read Only + checked</strong>
|
|
142
|
+
</p>
|
|
143
|
+
|
|
144
|
+
<Field
|
|
145
|
+
input={
|
|
146
|
+
<Input
|
|
147
|
+
id="test"
|
|
148
|
+
readOnly
|
|
149
|
+
checked
|
|
150
|
+
type="checkbox"
|
|
151
|
+
name="colors"
|
|
152
|
+
loading={loadingD}
|
|
153
|
+
/>
|
|
154
|
+
}
|
|
155
|
+
label="D"
|
|
156
|
+
/>
|
|
157
|
+
|
|
158
|
+
<button>
|
|
159
|
+
<span
|
|
160
|
+
onClick={() => {
|
|
161
|
+
setLoadingD(!loadingD);
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
Toggle
|
|
165
|
+
</span>
|
|
166
|
+
</button>
|
|
167
|
+
|
|
168
|
+
<div>
|
|
169
|
+
<label>
|
|
170
|
+
<input type="checkbox" name="colors" readonly />D
|
|
171
|
+
</label>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div>
|
|
177
|
+
<div>
|
|
178
|
+
<p>
|
|
179
|
+
<strong>Disabled</strong>
|
|
180
|
+
</p>
|
|
181
|
+
|
|
182
|
+
<Field
|
|
183
|
+
input={
|
|
184
|
+
<Input
|
|
185
|
+
id="test"
|
|
186
|
+
disabled
|
|
187
|
+
type="checkbox"
|
|
188
|
+
name="colors"
|
|
189
|
+
loading={loadingE}
|
|
190
|
+
/>
|
|
191
|
+
}
|
|
192
|
+
label="E"
|
|
193
|
+
/>
|
|
194
|
+
|
|
195
|
+
<button>
|
|
196
|
+
<span
|
|
197
|
+
onClick={() => {
|
|
198
|
+
setLoadingE(!loadingE);
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
Toggle
|
|
202
|
+
</span>
|
|
203
|
+
</button>
|
|
204
|
+
|
|
205
|
+
<div>
|
|
206
|
+
<label>
|
|
207
|
+
<input type="checkbox" name="colors" disabled />E
|
|
208
|
+
</label>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<div>
|
|
214
|
+
<div>
|
|
215
|
+
<p>
|
|
216
|
+
<strong>Disabled + checked</strong>
|
|
217
|
+
</p>
|
|
218
|
+
|
|
219
|
+
<Field
|
|
220
|
+
input={
|
|
221
|
+
<Input
|
|
222
|
+
id="test"
|
|
223
|
+
disabled
|
|
224
|
+
checked
|
|
225
|
+
type="checkbox"
|
|
226
|
+
name="colors"
|
|
227
|
+
loading={loadingF}
|
|
228
|
+
/>
|
|
229
|
+
}
|
|
230
|
+
label="Coucou"
|
|
231
|
+
/>
|
|
232
|
+
|
|
233
|
+
<button>
|
|
234
|
+
<span
|
|
235
|
+
onClick={() => {
|
|
236
|
+
setLoadingF(!loadingF);
|
|
237
|
+
}}
|
|
238
|
+
>
|
|
239
|
+
Toggle
|
|
240
|
+
</span>
|
|
241
|
+
</button>
|
|
242
|
+
|
|
243
|
+
<div>
|
|
244
|
+
<label>
|
|
245
|
+
<input type="checkbox" name="colors" disabled checked />F
|
|
246
|
+
</label>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
render(<App />, document.querySelector("#root"));
|
|
255
|
+
</script>
|
|
256
|
+
</body>
|
|
257
|
+
</html>
|