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