@jsenv/navi 0.1.1 → 0.2.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.
@@ -1,121 +1,87 @@
1
1
  import.meta.css = /* css */ `
2
- [data-field],
3
- [data-field-wrapper] {
4
- --field-border-width: 1px;
5
- --field-outline-width: 1px;
2
+ :root {
3
+ --navi-field-border-width: 1px;
4
+ --navi-field-outline-width: 1px;
6
5
 
7
- --field-strong-color: light-dark(#355fcc, #3b82f6);
8
- --field-outline-color: var(--field-strong-color);
9
- --field-background-color: light-dark(#f3f4f6, #2d3748);
10
- --field-border-color: light-dark(#767676, #8e8e93);
6
+ --navi-field-border-color: light-dark(#767676, #8e8e93);
7
+ --navi-field-outline-color: light-dark(#355fcc, #3b82f6);
8
+ --navi-field-background-color: light-dark(#f3f4f6, #2d3748);
9
+ --navi-field-accent-color: light-dark(#355fcc, #3b82f6);
11
10
 
12
- --field-disabled-border-color: color-mix(
11
+ --navi-field-disabled-border-color: color-mix(
13
12
  in srgb,
14
- var(--field-border-color) 30%,
13
+ var(--navi-field-border-color) 30%,
15
14
  white
16
15
  );
17
- --field-readonly-border-color: var(--field-disabled-border-color);
18
- --field-active-border-color: color-mix(
16
+ --navi-field-readonly-border-color: var(--navi-field-disabled-border-color);
17
+ --navi-field-active-border-color: color-mix(
19
18
  in srgb,
20
- var(--field-border-color) 90%,
19
+ var(--navi-field-border-color) 90%,
21
20
  black
22
21
  );
23
- --field-hover-border-color: color-mix(
22
+ --navi-field-hover-border-color: color-mix(
24
23
  in srgb,
25
- var(--field-border-color) 70%,
24
+ var(--navi-field-border-color) 70%,
26
25
  black
27
26
  );
28
27
 
29
- --field-disabled-background-color: var(--field-background-color);
30
- --field-readonly-background-color: var(--field-disabled-background-color);
31
- --field-hover-background-color: color-mix(
28
+ --navi-field-disabled-background-color: var(--navi-field-background-color);
29
+ --navi-field-readonly-background-color: var(
30
+ --navi-field-disabled-background-color
31
+ );
32
+ --navi-field-hover-background-color: color-mix(
32
33
  in srgb,
33
- var(--field-background-color) 95%,
34
+ var(--navi-field-background-color) 95%,
34
35
  black
35
36
  );
36
37
 
37
- --field-disabled-text-color: color-mix(
38
+ --navi-field-readonly-color: color-mix(
38
39
  in srgb,
39
40
  currentColor 30%,
40
41
  transparent
41
42
  );
42
- --field-readonly-text-color: var(--field-disabled-text-color);
43
+ --navi-field-disabled-color: var(--navi-field-readonly-color);
43
44
  }
44
45
 
45
- [data-field] {
46
- border-radius: 2px;
47
- outline-width: var(--field-border-width);
48
- outline-style: solid;
49
- outline-color: transparent;
50
- outline-offset: calc(-1 * (var(--field-border-width)));
51
- }
46
+ [data-field-border-and-outline] {
47
+ border-width: calc(
48
+ var(--navi-field-border-width) + var(--navi-field-outline-width)
49
+ );
52
50
 
53
- [data-field][data-field-with-border] {
54
- border-width: calc(var(--field-border-width) + var(--field-outline-width));
55
51
  border-style: solid;
56
- border-color: transparent;
57
- outline-color: var(--field-border-color);
58
- }
59
-
60
- [data-field-with-border-hover] {
61
- border: 0;
62
- }
63
-
64
- [data-field-with-background] {
65
- background-color: var(--field-background-color);
66
- }
67
- [data-field-with-background-hover] {
68
- background: none;
69
- }
70
-
71
- [data-field-with-background]:hover {
72
- background-color: var(--field-hover-background-color);
73
- }
74
52
 
75
- [data-field-with-hover]:hover {
76
- outline-color: var(--field-hover-border-color);
53
+ border-color: transparent;
54
+ outline-width: var(--navi-field-border-width);
55
+ outline-style: none;
56
+ outline-color: var(--navi-field-border-color);
57
+ outline-offset: calc(-1 * (var(--navi-field-border-width)));
77
58
  }
78
-
79
- [data-field-with-border]:active,
80
- [data-field][data-field-with-border][data-active] {
81
- outline-color: var(--field-active-border-color);
82
- background-color: none;
59
+ [data-field-wrapper][data-focus-visible] [data-field-border-and-outline] {
60
+ outline-width: calc(
61
+ var(--navi-field-border-width) + var(--navi-field-outline-width)
62
+ );
63
+ outline-style: solid;
64
+ outline-offset: calc(
65
+ -1 * (var(--navi-field-border-width) + var(--navi-field-outline-width))
66
+ );
83
67
  }
84
-
85
- [data-field-with-border][readonly],
86
- [data-field-with-border][data-readonly] {
87
- outline-color: var(--field-readonly-border-color);
68
+ [data-field-wrapper][data-readonly] [data-field-border-and-outline] {
69
+ --navi-field-outline-color: var(--navi-field-readonly-border-color);
70
+ --navi-field-background-color: none;
88
71
  }
89
-
90
- [data-field][readonly],
91
- [data-field][data-readonly] {
92
- color: var(--field-readonly-text-color);
72
+ [data-field-wrapper][data-active] [data-field-border-and-outline] {
73
+ --navi-field-outline-color: var(--navi-field-active-border-color);
74
+ --navi-field-background-color: none;
93
75
  }
94
76
 
95
- [data-field-with-background][readonly],
96
- [data-field-with-background][data-readonly] {
97
- background-color: var(--field-readonly-background-color);
77
+ [data-field-border-hover-only] {
78
+ border: 0;
98
79
  }
99
-
100
- [data-field]:focus-visible,
101
- [data-field][data-focus-visible]:focus {
102
- outline-style: solid;
103
- outline-width: calc(var(--field-border-width) + var(--field-outline-width));
104
- outline-offset: calc(
105
- -1 * (var(--field-border-width) + var(--field-outline-width))
106
- );
107
- outline-color: var(--field-outline-color);
80
+ [data-field-wrapper][data-hover] [data-field-with-hover-effect-on-border] {
81
+ outline-color: var(--navi-field-hover-border-color);
108
82
  }
109
83
 
110
- [data-field]:disabled,
111
- [data-field][data-disabled],
112
- [data-field-with-hover]:disabled:hover,
113
- [data-field-with-hover][data-disabled]:hover {
114
- outline-color: var(--field-disabled-border-color);
115
- color: var(--field-disabled-text-color);
116
- }
117
- [data-field-with-background]:disabled,
118
- [data-field-with-background][disabled] {
119
- background-color: var(--field-disabled-background-color);
84
+ [data-field-wrapper][data-readonly] [data-field] {
85
+ --navi-field-color: var(--navi-field-readonly-color);
120
86
  }
121
87
  `;
@@ -18,7 +18,10 @@ import { useContext, useImperativeHandle, useMemo, useRef } from "preact/hooks";
18
18
 
19
19
  import { requestAction } from "../../validation/custom_constraint_validation.js";
20
20
  import { useConstraints } from "../../validation/hooks/use_constraints.js";
21
- import { FormContext } from "../action_execution/form_context.js";
21
+ import {
22
+ FormActionContext,
23
+ FormContext,
24
+ } from "../action_execution/form_context.js";
22
25
  import { renderActionableComponent } from "../action_execution/render_actionable_component.jsx";
23
26
  import { useActionBoundToOneParam } from "../action_execution/use_action.js";
24
27
  import { useExecuteAction } from "../action_execution/use_execute_action.js";
@@ -193,9 +196,11 @@ const FormWithAction = forwardRef((props, ref) => {
193
196
  });
194
197
  }}
195
198
  >
196
- <LoadingElementContext.Provider value={formActionRequester}>
197
- {children}
198
- </LoadingElementContext.Provider>
199
+ <FormActionContext.Provider value={actionBoundToUIState}>
200
+ <LoadingElementContext.Provider value={formActionRequester}>
201
+ {children}
202
+ </LoadingElementContext.Provider>
203
+ </FormActionContext.Provider>
199
204
  </FormBasic>
200
205
  );
201
206
  });
@@ -1,5 +1,11 @@
1
+ import { pickLightOrDark } from "@jsenv/dom";
1
2
  import { forwardRef } from "preact/compat";
2
- import { useContext, useImperativeHandle, useRef } from "preact/hooks";
3
+ import {
4
+ useContext,
5
+ useImperativeHandle,
6
+ useLayoutEffect,
7
+ useRef,
8
+ } from "preact/hooks";
3
9
 
4
10
  import { useActionStatus } from "../../use_action_status.js";
5
11
  import { requestAction } from "../../validation/custom_constraint_validation.js";
@@ -7,9 +13,17 @@ import { useConstraints } from "../../validation/hooks/use_constraints.js";
7
13
  import { renderActionableComponent } from "../action_execution/render_actionable_component.jsx";
8
14
  import { useActionBoundToOneParam } from "../action_execution/use_action.js";
9
15
  import { useExecuteAction } from "../action_execution/use_execute_action.js";
10
- import { LoadableInlineElement } from "../loader/loader_background.jsx";
16
+ import {
17
+ LoadableInlineElement,
18
+ LoaderBackground,
19
+ } from "../loader/loader_background.jsx";
11
20
  import { useAutoFocus } from "../use_auto_focus.js";
12
- import { ReportReadOnlyOnLabelContext } from "./label.jsx";
21
+ import { initCustomField } from "./custom_field.js";
22
+ import {
23
+ ReportDisabledOnLabelContext,
24
+ ReportReadOnlyOnLabelContext,
25
+ } from "./label.jsx";
26
+ import "./navi_css_vars.js";
13
27
  import { useActionEvents } from "./use_action_events.js";
14
28
  import {
15
29
  DisabledContext,
@@ -25,38 +39,70 @@ import {
25
39
  } from "./use_ui_state_controller.js";
26
40
 
27
41
  import.meta.css = /* css */ `
28
- .custom_checkbox_wrapper[data-field-wrapper] {
42
+ :root {
43
+ --navi-checkmark-color-light: white;
44
+ --navi-checkmark-color-dark: rgb(55, 55, 55);
45
+ --navi-checkmark-color: var(--navi-checkmark-light-color);
46
+ }
47
+
48
+ .navi_checkbox {
49
+ position: relative;
29
50
  display: inline-flex;
30
51
  box-sizing: content-box;
31
52
 
32
- --checkmark-color: white;
33
- --checkmark-disabled-color: #eeeeee;
34
- --checked-color: #3b82f6;
35
- --checked-disabled-color: #d3d3d3;
53
+ --outline-offset: 1px;
54
+ --outline-width: 2px;
55
+ --border-width: 1px;
56
+ --border-radius: 2px;
57
+ --width: 13px;
58
+ --height: 13px;
36
59
 
37
- /* TODO: find a better way maybe? */
38
- --field-strong-color: var(--checked-color);
39
- }
60
+ --outline-color: light-dark(#4476ff, #3b82f6);
61
+ --border-color: light-dark(#767676, #8e8e93);
62
+ --background-color: white;
63
+ --accent-color: light-dark(#4476ff, #3b82f6);
64
+ /* --color: currentColor; */
65
+ --checkmark-color: var(--navi-checkmark-color);
40
66
 
41
- .custom_checkbox_wrapper input {
67
+ --border-color-readonly: color-mix(in srgb, var(--border-color) 30%, white);
68
+ --border-color-disabled: var(--border-color-readonly);
69
+ --border-color-hover: color-mix(in srgb, var(--border-color) 70%, black);
70
+ --border-color-checked-readonly: #d3d3d3;
71
+ --border-color-checked-disabled: #d3d3d3;
72
+ --background-color-checked-readonly: var(--navi-background-color-readonly);
73
+ --background-color-checked-disabled: var(--navi-background-color-disabled);
74
+ --checkmark-color-readonly: var(--navi-color-readonly);
75
+ --checkmark-color-disabled: var(--navi-color-disabled);
76
+ }
77
+ .navi_checkbox input {
42
78
  position: absolute;
43
- opacity: 0;
44
79
  inset: 0;
45
80
  margin: 0;
46
81
  padding: 0;
47
82
  border: none;
83
+ opacity: 0;
84
+ cursor: inherit;
48
85
  }
49
-
50
- .custom_checkbox {
51
- width: 13px;
52
- height: 13px;
53
- border: 1px solid var(--field-border-color);
54
- border-radius: 2px;
55
- box-sizing: border-box;
86
+ .navi_checkbox_field {
56
87
  display: inline-flex;
88
+ box-sizing: border-box;
89
+ width: var(--width);
90
+ height: var(--height);
57
91
  margin: 3px 3px 3px 4px;
92
+ background-color: var(--background-color);
93
+ border-width: var(--border-width);
94
+ border-style: solid;
95
+ border-color: var(--border-color);
96
+ border-radius: var(--border-radius);
97
+ outline-width: var(--outline-width);
98
+
99
+ outline-style: none;
100
+
101
+ outline-color: var(--outline-color);
102
+ outline-offset: var(--outline-offset);
103
+ /* color: var(--color); */
58
104
  }
59
- .custom_checkbox svg {
105
+ .navi_checkbox_marker {
60
106
  width: 100%;
61
107
  height: 100%;
62
108
  opacity: 0;
@@ -64,69 +110,50 @@ import.meta.css = /* css */ `
64
110
  transition: all 0.15s ease;
65
111
  pointer-events: none;
66
112
  }
67
- .custom_checkbox svg path {
68
- stroke: var(--checkmark-color);
69
- }
70
113
 
71
- .custom_checkbox_wrapper:hover .custom_checkbox {
72
- border-color: var(--field-hover-border-color);
114
+ /* Focus */
115
+ .navi_checkbox[data-focus-visible] .navi_checkbox_field {
116
+ outline-style: solid;
73
117
  }
74
- .custom_checkbox_wrapper:hover input:checked + .custom_checkbox {
75
- background: var(--field-strong-color);
76
- border-color: var(--field-strong-color);
118
+ /* Hover */
119
+ .navi_checkbox[data-hover] .navi_checkbox_field {
120
+ --border-color: var(--border-color-hover);
77
121
  }
78
- .custom_checkbox_wrapper input:checked + .custom_checkbox {
79
- background: var(--checked-color);
80
- border-color: var(--checked-color);
122
+ /* Checked */
123
+ .navi_checkbox[data-checked] .navi_checkbox_field {
124
+ --background-color: var(--accent-color);
125
+ --border-color: var(--accent-color);
81
126
  }
82
- .custom_checkbox_wrapper input:checked + .custom_checkbox svg {
127
+ .navi_checkbox[data-checked] .navi_checkbox_marker {
83
128
  opacity: 1;
129
+ stroke: var(--checkmark-color);
84
130
  transform: scale(1);
85
131
  }
86
-
87
- .custom_checkbox_wrapper input[data-readonly] + .custom_checkbox {
88
- background-color: var(--field-readonly-background-color);
89
- border-color: var(--field-readonly-border-color);
90
- }
91
- .custom_checkbox_wrapper input[data-readonly]:checked + .custom_checkbox {
92
- background: var(--checked-disabled-color);
93
- border-color: var(--checked-disabled-color);
132
+ /* Readonly */
133
+ .navi_checkbox[data-readonly] .navi_checkbox_field,
134
+ .navi_checkbox[data-readonly][data-hover] .navi_checkbox_field {
135
+ --border-color: var(--border-color-readonly);
136
+ --background-color: var(--background-color-readonly);
94
137
  }
95
- .custom_checkbox_wrapper:hover input[data-readonly] + .custom_checkbox {
96
- background-color: var(--field-readonly-background-color);
97
- border-color: var(--field-readonly-border-color);
138
+ .navi_checkbox[data-checked][data-readonly] .navi_checkbox_field {
139
+ --background-color: var(--background-color-checked-readonly);
140
+ --border-color: var(--border-color-checked-readonly);
98
141
  }
99
- .custom_checkbox_wrapper:hover
100
- input[data-readonly]:checked
101
- + .custom_checkbox {
102
- background: var(--checked-disabled-color);
103
- border-color: var(--checked-disabled-color);
142
+ .navi_checkbox[data-checked][data-readonly] .navi_checkbox_marker {
143
+ stroke: var(--checkmark-color-readonly);
104
144
  }
105
- .custom_checkbox_wrapper
106
- input[data-readonly]:checked
107
- + .custom_checkbox
108
- .custom_checkbox_marker {
109
- stroke: var(--checkmark-disabled-color);
145
+ /* Disabled */
146
+ .navi_checkbox[data-disabled] .navi_checkbox_field {
147
+ --background-color: var(--background-color-disabled);
148
+ --border-color: var(--border-color-disabled);
110
149
  }
111
-
112
- .custom_checkbox_wrapper input:focus-visible + .custom_checkbox {
113
- outline: 2px solid var(--field-outline-color);
114
- outline-offset: 1px;
150
+ .navi_checkbox[data-checked][data-disabled] .navi_checkbox_field {
151
+ --border-color: var(--border-color-checked-disabled);
152
+ --background-color: var(--background-color-checked-disabled);
115
153
  }
116
154
 
117
- .custom_checkbox_wrapper input[disabled] + .custom_checkbox {
118
- background-color: var(--field-disabled-background-color);
119
- border-color: var(--field-disabled-border-color);
120
- }
121
- .custom_checkbox_wrapper input[disabled]:checked + .custom_checkbox {
122
- background: var(--checked-disabled-color);
123
- border-color: var(--checked-disabled-color);
124
- }
125
- .custom_checkbox_wrapper
126
- input[disabled]:checked
127
- + .custom_checkbox
128
- .custom_checkbox_marker {
129
- stroke: var(--checkmark-disabled-color);
155
+ .navi_checkbox[data-checked][data-disabled] .navi_checkbox_marker {
156
+ stroke: var(--checkmark-color-disabled);
130
157
  }
131
158
  `;
132
159
 
@@ -165,6 +192,7 @@ const InputCheckboxBasic = forwardRef((props, ref) => {
165
192
  const uiStateController = useContext(UIStateControllerContext);
166
193
  const uiState = useContext(UIStateContext);
167
194
  const reportReadOnlyOnLabel = useContext(ReportReadOnlyOnLabelContext);
195
+ const reportDisabledOnLabel = useContext(ReportDisabledOnLabelContext);
168
196
  const {
169
197
  name,
170
198
  readOnly,
@@ -174,10 +202,11 @@ const InputCheckboxBasic = forwardRef((props, ref) => {
174
202
 
175
203
  autoFocus,
176
204
  constraints = [],
177
- appeareance = "custom", // "custom" or "default"
205
+ appeareance = "navi", // "navi" or "default"
178
206
  accentColor,
179
207
  onClick,
180
208
  onInput,
209
+ style,
181
210
  ...rest
182
211
  } = props;
183
212
  const innerRef = useRef(null);
@@ -191,6 +220,7 @@ const InputCheckboxBasic = forwardRef((props, ref) => {
191
220
  const innerReadOnly =
192
221
  readOnly || contextReadOnly || innerLoading || uiStateController.readOnly;
193
222
  reportReadOnlyOnLabel?.(innerReadOnly);
223
+ reportDisabledOnLabel?.(innerDisabled);
194
224
  useAutoFocus(innerRef, autoFocus);
195
225
  useConstraints(innerRef, constraints);
196
226
 
@@ -204,9 +234,9 @@ const InputCheckboxBasic = forwardRef((props, ref) => {
204
234
  {...rest}
205
235
  ref={innerRef}
206
236
  type="checkbox"
237
+ style={appeareance === "default" ? style : undefined}
207
238
  name={innerName}
208
239
  checked={checked}
209
- data-readonly={innerReadOnly ? "" : undefined}
210
240
  readOnly={innerReadOnly}
211
241
  disabled={innerDisabled}
212
242
  required={innerRequired}
@@ -233,44 +263,85 @@ const InputCheckboxBasic = forwardRef((props, ref) => {
233
263
  }}
234
264
  />
235
265
  );
236
-
237
- const inputCheckboxDisplayed =
238
- appeareance === "custom" ? (
239
- <CustomCheckbox accentColor={accentColor}>{inputCheckbox}</CustomCheckbox>
240
- ) : (
241
- inputCheckbox
266
+ const loaderProps = {
267
+ loading: innerLoading,
268
+ inset: -1,
269
+ style: {
270
+ "--accent-color": accentColor || "light-dark(#355fcc, #4476ff)",
271
+ },
272
+ color: "var(--accent-color)",
273
+ };
274
+ if (appeareance === "navi") {
275
+ return (
276
+ <NaviCheckbox
277
+ data-action={actionName}
278
+ inputRef={innerRef}
279
+ accentColor={accentColor}
280
+ readOnly={readOnly}
281
+ disabled={innerDisabled}
282
+ style={style}
283
+ >
284
+ <LoaderBackground
285
+ {...loaderProps}
286
+ targetSelector=".navi_checkbox_field"
287
+ >
288
+ {inputCheckbox}
289
+ </LoaderBackground>
290
+ </NaviCheckbox>
242
291
  );
292
+ }
243
293
 
244
294
  return (
245
- <LoadableInlineElement
246
- data-action={actionName}
247
- loading={innerLoading}
248
- inset={-1}
249
- targetSelector={appeareance === "custom" ? ".custom_checkbox" : ""}
250
- color="light-dark(#355fcc, #3b82f6)"
251
- >
252
- {inputCheckboxDisplayed}
295
+ <LoadableInlineElement {...loaderProps} data-action={actionName}>
296
+ {inputCheckbox}
253
297
  </LoadableInlineElement>
254
298
  );
255
299
  });
256
- const CustomCheckbox = ({ accentColor, children }) => {
300
+ const NaviCheckbox = ({
301
+ accentColor,
302
+ readOnly,
303
+ disabled,
304
+ inputRef,
305
+ style,
306
+ children,
307
+ ...rest
308
+ }) => {
309
+ const ref = useRef();
310
+ useLayoutEffect(() => {
311
+ const naviCheckbox = ref.current;
312
+ const colorPicked = pickLightOrDark(
313
+ naviCheckbox,
314
+ "var(--accent-color)",
315
+ "var(--navi-checkmark-color-light)",
316
+ "var(--navi-checkmark-color-dark)",
317
+ );
318
+ naviCheckbox.style.setProperty("--checkmark-color", colorPicked);
319
+ }, [accentColor]);
320
+
321
+ useLayoutEffect(() => {
322
+ return initCustomField(ref.current, inputRef.current);
323
+ }, []);
324
+
257
325
  return (
258
326
  <div
259
- className="custom_checkbox_wrapper"
260
- data-field-wrapper=""
327
+ {...rest}
328
+ ref={ref}
329
+ className="navi_checkbox"
261
330
  style={{
262
- ...(accentColor ? { "--checked-color": accentColor } : {}),
331
+ ...(accentColor ? { "--accent-color": accentColor } : {}),
332
+ ...style,
263
333
  }}
334
+ data-readonly={readOnly ? "" : undefined}
335
+ data-disabled={disabled ? "" : undefined}
264
336
  >
265
337
  {children}
266
- <div className="custom_checkbox">
267
- <svg viewBox="0 0 12 12" aria-hidden="true">
268
- <path
269
- className="custom_checkbox_marker"
270
- d="M10.5 2L4.5 9L1.5 5.5"
271
- fill="none"
272
- strokeWidth="2"
273
- />
338
+ <div className="navi_checkbox_field">
339
+ <svg
340
+ viewBox="0 0 12 12"
341
+ aria-hidden="true"
342
+ className="navi_checkbox_marker"
343
+ >
344
+ <path d="M10.5 2L4.5 9L1.5 5.5" fill="none" strokeWidth="2" />
274
345
  </svg>
275
346
  </div>
276
347
  </div>