@jsenv/navi 0.1.0 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Library of components including navigation to create frontend applications",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,7 +13,8 @@
13
13
  "email": "dmaillard06@gmail.com"
14
14
  },
15
15
  "sideEffects": [
16
- "./dist/jsenv_navi.js"
16
+ "./dist/jsenv_navi.js",
17
+ "./src/components/field/field_css.js"
17
18
  ],
18
19
  "type": "module",
19
20
  "exports": {
@@ -33,7 +34,7 @@
33
34
  "prepublishOnly": "npm run build"
34
35
  },
35
36
  "dependencies": {
36
- "@jsenv/dom": "0.1.0",
37
+ "@jsenv/dom": "0.3.0",
37
38
  "@jsenv/humanize": "1.6.0"
38
39
  },
39
40
  "devDependencies": {
@@ -1,8 +1,5 @@
1
1
  import { createContext } from "preact";
2
- import { useContext } from "preact/hooks";
3
2
 
4
3
  export const FormContext = createContext();
5
4
 
6
- export const useFormContext = () => {
7
- return useContext(FormContext);
8
- };
5
+ export const FormActionContext = createContext();
@@ -29,7 +29,7 @@ export const useActionBoundToOneArrayParam = (
29
29
  fallbackValue,
30
30
  defaultValue,
31
31
  ) => {
32
- const [boundAction, value, setValue, initialValue] = useActionBoundToOneParam(
32
+ const [boundAction, value, setValue] = useActionBoundToOneParam(
33
33
  action,
34
34
  name,
35
35
  externalValue,
@@ -45,7 +45,7 @@ export const useActionBoundToOneArrayParam = (
45
45
  setValue(removeFromArray(valueArray, valueToRemove));
46
46
  };
47
47
 
48
- const result = [boundAction, value, setValue, initialValue];
48
+ const result = [boundAction, value, setValue];
49
49
  result.add = add;
50
50
  result.remove = remove;
51
51
  return result;
@@ -7,72 +7,72 @@
7
7
  <title>Button Demo</title>
8
8
  <style>
9
9
  body {
10
- font-family:
11
- -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
12
10
  max-width: 1400px;
11
+ min-height: 100vh;
13
12
  margin: 0 auto;
14
13
  padding: 20px;
14
+ font-family:
15
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
15
16
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
16
- min-height: 100vh;
17
17
  }
18
18
 
19
19
  h1 {
20
- text-align: center;
21
- color: white;
22
20
  margin-bottom: 30px;
21
+ color: white;
23
22
  font-size: 2.5rem;
23
+ text-align: center;
24
24
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
25
25
  }
26
26
 
27
27
  .demo-grid {
28
28
  display: grid;
29
+ margin-bottom: 30px;
29
30
  grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
30
31
  gap: 20px;
31
- margin-bottom: 30px;
32
32
  }
33
33
 
34
34
  .demo-card {
35
+ padding: 25px;
35
36
  background: white;
37
+ border: 1px solid rgba(255, 255, 255, 0.2);
36
38
  border-radius: 12px;
37
- padding: 25px;
38
39
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
39
- border: 1px solid rgba(255, 255, 255, 0.2);
40
40
  transition:
41
41
  transform 0.2s ease,
42
42
  box-shadow 0.2s ease;
43
43
  }
44
44
 
45
45
  .demo-card:hover {
46
- transform: translateY(-2px);
47
46
  box-shadow: 0 12px 35px rgba(0, 0, 0, 0.2);
47
+ transform: translateY(-2px);
48
48
  }
49
49
 
50
50
  .demo-title {
51
- color: #2c3e50;
51
+ display: flex;
52
52
  margin: 0 0 20px 0;
53
- font-size: 1.3rem;
54
- font-weight: 600;
55
- border-bottom: 3px solid #667eea;
56
53
  padding-bottom: 10px;
57
- display: flex;
58
54
  align-items: center;
59
55
  gap: 10px;
56
+ color: #2c3e50;
57
+ font-weight: 600;
58
+ font-size: 1.3rem;
59
+ border-bottom: 3px solid #667eea;
60
60
  }
61
61
 
62
62
  .demo-title::before {
63
- content: "🔘";
64
63
  font-size: 1.2rem;
64
+ content: "🔘";
65
65
  }
66
66
 
67
67
  .button-row {
68
68
  display: flex;
69
- align-items: center;
70
- gap: 15px;
71
69
  margin: 15px 0;
72
70
  padding: 12px;
71
+ align-items: center;
72
+ gap: 15px;
73
73
  background: #f8f9fa;
74
- border-radius: 8px;
75
74
  border: 1px solid #e9ecef;
75
+ border-radius: 8px;
76
76
  transition: background 0.2s ease;
77
77
  }
78
78
 
@@ -82,49 +82,49 @@
82
82
 
83
83
  .button-container {
84
84
  display: flex;
85
+ min-width: 0;
86
+ flex: 1;
85
87
  align-items: center;
86
88
  gap: 10px;
87
- flex: 1;
88
- min-width: 0;
89
89
  }
90
90
 
91
91
  .toggle-button {
92
92
  padding: 8px 16px;
93
- background: linear-gradient(45deg, #667eea, #764ba2);
93
+ flex-shrink: 0;
94
94
  color: white;
95
+ font-weight: 500;
96
+ font-size: 0.9rem;
97
+ white-space: nowrap;
98
+ background: linear-gradient(45deg, #667eea, #764ba2);
95
99
  border: none;
96
100
  border-radius: 6px;
97
- cursor: pointer;
98
- font-size: 0.9rem;
99
- font-weight: 500;
100
- transition: all 0.2s ease;
101
101
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
102
- flex-shrink: 0;
103
- white-space: nowrap;
102
+ transition: all 0.2s ease;
103
+ cursor: pointer;
104
104
  }
105
105
 
106
106
  .toggle-button:hover {
107
107
  background: linear-gradient(45deg, #5a6fd8, #6b3fa0);
108
- transform: translateY(-1px);
109
108
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
109
+ transform: translateY(-1px);
110
110
  }
111
111
 
112
112
  .comparison {
113
113
  display: flex;
114
- flex-direction: column;
115
- gap: 8px;
116
114
  margin-top: 15px;
117
115
  padding: 12px;
116
+ flex-direction: column;
117
+ gap: 8px;
118
118
  background: #fff3cd;
119
- border-radius: 6px;
120
119
  border-left: 4px solid #ffc107;
120
+ border-radius: 6px;
121
121
  }
122
122
 
123
123
  .comparison-label {
124
- font-size: 0.85rem;
124
+ margin-bottom: 5px;
125
125
  color: #856404;
126
126
  font-weight: 500;
127
- margin-bottom: 5px;
127
+ font-size: 0.85rem;
128
128
  }
129
129
 
130
130
  .native-examples {
@@ -134,15 +134,15 @@
134
134
  }
135
135
 
136
136
  .native-examples button {
137
- align-self: flex-start;
138
137
  padding: 8px 16px;
138
+ align-self: flex-start;
139
139
  font-size: 0.9rem;
140
140
  }
141
141
 
142
142
  .prop-control-demo {
143
143
  grid-column: 1 / -1;
144
- background: linear-gradient(45deg, #ff6b6b 0%, #feca57 100%);
145
144
  color: white;
145
+ background: linear-gradient(45deg, #ff6b6b 0%, #feca57 100%);
146
146
  }
147
147
 
148
148
  .prop-control-demo .demo-title {
@@ -155,17 +155,17 @@
155
155
  }
156
156
 
157
157
  .prop-controls {
158
- margin-bottom: 25px;
159
158
  display: grid;
159
+ margin-bottom: 25px;
160
160
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
161
161
  gap: 15px;
162
162
  }
163
163
 
164
164
  .prop-select-group {
165
- background: rgba(255, 255, 255, 0.1);
166
165
  padding: 15px;
167
- border-radius: 8px;
166
+ background: rgba(255, 255, 255, 0.1);
168
167
  border: 1px solid rgba(255, 255, 255, 0.2);
168
+ border-radius: 8px;
169
169
  }
170
170
 
171
171
  .prop-select-label {
@@ -178,65 +178,65 @@
178
178
  .prop-select {
179
179
  width: 100%;
180
180
  padding: 8px 12px;
181
- border: none;
182
- border-radius: 6px;
183
- background: white;
184
181
  color: #333;
185
182
  font-size: 0.9rem;
186
- cursor: pointer;
183
+ background: white;
184
+ border: none;
185
+ border-radius: 6px;
187
186
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
187
+ cursor: pointer;
188
188
  }
189
189
 
190
190
  .state-controls {
191
- background: rgba(255, 255, 255, 0.1);
191
+ margin-bottom: 20px;
192
192
  padding: 15px;
193
- border-radius: 8px;
193
+ background: rgba(255, 255, 255, 0.1);
194
194
  border: 1px solid rgba(255, 255, 255, 0.2);
195
- margin-bottom: 20px;
195
+ border-radius: 8px;
196
196
  }
197
197
 
198
198
  .state-controls h4 {
199
199
  margin: 0 0 15px 0;
200
- font-size: 1rem;
201
200
  color: white;
201
+ font-size: 1rem;
202
202
  }
203
203
 
204
204
  .control-row {
205
205
  display: flex;
206
- gap: 15px;
207
- flex-wrap: wrap;
208
206
  margin-bottom: 10px;
207
+ flex-wrap: wrap;
208
+ gap: 15px;
209
209
  }
210
210
 
211
211
  .control-group {
212
212
  display: flex;
213
+ min-width: 100px;
214
+ padding: 12px;
213
215
  flex-direction: column;
214
216
  align-items: center;
215
217
  gap: 8px;
216
218
  background: rgba(255, 255, 255, 0.05);
217
- padding: 12px;
218
- border-radius: 8px;
219
219
  border: 1px solid rgba(255, 255, 255, 0.1);
220
- min-width: 100px;
220
+ border-radius: 8px;
221
221
  }
222
222
 
223
223
  .control-toggle {
224
+ min-width: 80px;
224
225
  padding: 8px 16px;
225
- border: 2px solid rgba(255, 255, 255, 0.3);
226
- background: rgba(255, 255, 255, 0.1);
227
226
  color: white;
228
- border-radius: 6px;
229
- cursor: pointer;
230
- font-size: 0.85rem;
231
227
  font-weight: 500;
232
- transition: all 0.2s ease;
233
- min-width: 80px;
228
+ font-size: 0.85rem;
234
229
  text-align: center;
230
+ background: rgba(255, 255, 255, 0.1);
231
+ border: 2px solid rgba(255, 255, 255, 0.3);
232
+ border-radius: 6px;
233
+ transition: all 0.2s ease;
234
+ cursor: pointer;
235
235
  }
236
236
 
237
237
  .control-toggle.active {
238
- background: rgba(255, 255, 255, 0.9);
239
238
  color: #333;
239
+ background: rgba(255, 255, 255, 0.9);
240
240
  border-color: white;
241
241
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
242
242
  }
@@ -251,36 +251,36 @@
251
251
  }
252
252
 
253
253
  .control-label {
254
- font-size: 0.9rem;
255
- color: white;
256
254
  margin: 0;
255
+ color: white;
256
+ font-size: 0.9rem;
257
257
  }
258
258
 
259
259
  .button-test-grid {
260
260
  display: grid;
261
+ padding: 20px;
261
262
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
262
263
  gap: 15px;
263
264
  background: rgba(255, 255, 255, 0.1);
264
- padding: 20px;
265
- border-radius: 8px;
266
265
  border: 1px solid rgba(255, 255, 255, 0.2);
266
+ border-radius: 8px;
267
267
  }
268
268
 
269
269
  .button-test-item {
270
270
  display: flex;
271
+ padding: 15px;
271
272
  flex-direction: column;
272
273
  align-items: center;
273
274
  gap: 10px;
274
- padding: 15px;
275
+ text-align: center;
275
276
  background: rgba(255, 255, 255, 0.05);
276
277
  border-radius: 6px;
277
- text-align: center;
278
278
  }
279
279
 
280
280
  .button-label {
281
- font-size: 0.9rem;
282
- color: white;
283
281
  margin-bottom: 8px;
282
+ color: white;
283
+ font-size: 0.9rem;
284
284
  }
285
285
 
286
286
  @media (max-width: 768px) {
@@ -320,6 +320,7 @@
320
320
  <ReadOnlyDemo />
321
321
  <DisabledDemo />
322
322
  <SizeVariationsDemo />
323
+ <DiscreteDemo />
323
324
  <PropControlDemo />
324
325
  </div>
325
326
  );
@@ -448,7 +449,11 @@
448
449
  <div className="button-container">
449
450
  <Button
450
451
  loading={loading}
451
- style={{ borderWidth: 8, padding: "12px 24px" }}
452
+ style={{
453
+ "--border-width": "8px",
454
+ "--padding-x": "12px",
455
+ "--padding-y": "24px",
456
+ }}
452
457
  >
453
458
  Thick Border Button
454
459
  </Button>
@@ -474,6 +479,82 @@
474
479
  );
475
480
  };
476
481
 
482
+ // eslint-disable-next-line no-unused-vars
483
+ const DiscreteDemo = () => {
484
+ const [loading, setLoading] = useState(false);
485
+
486
+ return (
487
+ <div className="demo-card">
488
+ <h3 className="demo-title">Discrete Buttons</h3>
489
+
490
+ <div className="button-row">
491
+ <div className="button-container">
492
+ <Button discrete loading={loading}>
493
+ Discrete Button
494
+ </Button>
495
+ <span style={{ fontSize: "0.9rem", color: "#666" }}>
496
+ Subtle, minimal styling
497
+ </span>
498
+ </div>
499
+ <button
500
+ className="toggle-button"
501
+ onClick={() => setLoading(!loading)}
502
+ >
503
+ {loading ? "Stop Loading" : "Start Loading"}
504
+ </button>
505
+ </div>
506
+
507
+ <div className="button-row">
508
+ <div className="button-container">
509
+ <Button discrete disabled>
510
+ Disabled Discrete
511
+ </Button>
512
+ <span style={{ fontSize: "0.9rem", color: "#666" }}>
513
+ Disabled state
514
+ </span>
515
+ </div>
516
+ </div>
517
+
518
+ <div className="button-row">
519
+ <div className="button-container">
520
+ <Button discrete readOnly>
521
+ Read-Only Discrete
522
+ </Button>
523
+ <span style={{ fontSize: "0.9rem", color: "#666" }}>
524
+ Read-only state
525
+ </span>
526
+ </div>
527
+ </div>
528
+
529
+ <div className="comparison">
530
+ <span className="comparison-label">Comparison:</span>
531
+ <div
532
+ style={{
533
+ display: "flex",
534
+ gap: "15px",
535
+ alignItems: "center",
536
+ marginTop: "10px",
537
+ }}
538
+ >
539
+ <Button appearance="default" loading={loading}>
540
+ Regular Button
541
+ </Button>
542
+ <Button appearance="default" discrete loading={loading}>
543
+ Discrete Button
544
+ </Button>
545
+ </div>
546
+ <div className="native-examples">
547
+ <small style={{ color: "#666" }}>
548
+ Discrete buttons have minimal styling and blend better with
549
+ content. They're perfect for secondary actions or when you
550
+ want less visual emphasis.
551
+ </small>
552
+ </div>
553
+ </div>
554
+ </div>
555
+ );
556
+ };
557
+
477
558
  // eslint-disable-next-line no-unused-vars
478
559
  const PropControlDemo = () => {
479
560
  const [loading, setLoading] = useState(false);
@@ -281,6 +281,55 @@
281
281
  font-size: 0.95rem;
282
282
  }
283
283
 
284
+ /* Custom styles override section */
285
+ .my_styles .navi_checkbox {
286
+ --accent-color: green;
287
+ --width: 20px;
288
+ --height: 20px;
289
+ }
290
+ .my_styles .navi_checkbox[data-hover] {
291
+ --accent-color: yellow;
292
+ }
293
+
294
+ .custom-styles-demo {
295
+ margin-top: 30px;
296
+ }
297
+
298
+ .custom-styles-test {
299
+ background: rgba(255, 255, 255, 0.1);
300
+ padding: 20px;
301
+ border-radius: 8px;
302
+ border: 1px solid rgba(255, 255, 255, 0.2);
303
+ margin: 15px 0;
304
+ }
305
+
306
+ .custom-styles-test h4 {
307
+ margin-top: 0;
308
+ color: #2c3e50;
309
+ font-size: 1.1rem;
310
+ }
311
+
312
+ .custom-styles-comparison {
313
+ display: grid;
314
+ grid-template-columns: 1fr 1fr;
315
+ gap: 20px;
316
+ margin-top: 15px;
317
+ }
318
+
319
+ .comparison-item {
320
+ background: rgba(255, 255, 255, 0.05);
321
+ padding: 15px;
322
+ border-radius: 6px;
323
+ border: 1px solid rgba(255, 255, 255, 0.1);
324
+ }
325
+
326
+ .comparison-item h5 {
327
+ margin-top: 0;
328
+ margin-bottom: 10px;
329
+ color: #34495e;
330
+ font-size: 0.9rem;
331
+ }
332
+
284
333
  @media (max-width: 768px) {
285
334
  .demo-grid {
286
335
  grid-template-columns: 1fr;
@@ -320,6 +369,7 @@
320
369
  <ReadOnlyDemo />
321
370
  <DisabledDemo />
322
371
  <PropControlDemo />
372
+ <CustomStylesDemo />
323
373
  </div>
324
374
  );
325
375
  };
@@ -493,7 +543,7 @@
493
543
  const [isLoading, setIsLoading] = useState(false);
494
544
 
495
545
  const colorOptions = [
496
- { name: "Default (blue)", value: undefined },
546
+ { name: "Default (blue)", value: "default" },
497
547
  { name: "Orange", value: "#ff8c00" },
498
548
  { name: "Green", value: "#28a745" },
499
549
  { name: "Purple", value: "#6f42c1" },
@@ -511,7 +561,11 @@
511
561
  <label className="color-select-label">Accent Color:</label>
512
562
  <select
513
563
  value={accentColor}
514
- onChange={(e) => setAccentColor(e.target.value)}
564
+ onChange={(e) => {
565
+ setAccentColor(
566
+ e.target.value === "default" ? undefined : e.target.value,
567
+ );
568
+ }}
515
569
  className="color-select"
516
570
  >
517
571
  {colorOptions.map(({ name, value }) => (
@@ -628,6 +682,72 @@
628
682
  );
629
683
  };
630
684
 
685
+ // eslint-disable-next-line no-unused-vars
686
+ const CustomStylesDemo = () => {
687
+ return (
688
+ <div className="custom-styles-demo">
689
+ <h2 style={{ color: "#2c3e50", marginBottom: "20px" }}>
690
+ Ability to Override with Custom Styles
691
+ </h2>
692
+
693
+ <div className="custom-styles-test">
694
+ <h4>Accent Color Override Test</h4>
695
+ <p>
696
+ Testing if CSS <code>accent-color</code> property can override
697
+ the component's styling:
698
+ </p>
699
+
700
+ <div className="custom-styles-comparison">
701
+ <div className="comparison-item">
702
+ <h5>Default Checkbox</h5>
703
+ <Label className="checkbox-label">
704
+ <Input
705
+ type="checkbox"
706
+ name="default-checkbox"
707
+ defaultChecked={true}
708
+ />
709
+ Default styling
710
+ </Label>
711
+ </div>
712
+
713
+ <div className="comparison-item my_styles">
714
+ <h5>Custom Styled Checkbox (.my_styles)</h5>
715
+ <Label className="checkbox-label">
716
+ <Input
717
+ type="checkbox"
718
+ name="custom-styled-checkbox"
719
+ defaultChecked={true}
720
+ />
721
+ Should be green accent
722
+ </Label>
723
+ </div>
724
+ </div>
725
+
726
+ <div
727
+ style={{
728
+ marginTop: "15px",
729
+ padding: "10px",
730
+ background: "rgba(255,255,255,0.1)",
731
+ borderRadius: "4px",
732
+ }}
733
+ >
734
+ <p
735
+ style={{
736
+ marginTop: "10px",
737
+ fontSize: "0.9em",
738
+ color: "#666",
739
+ }}
740
+ >
741
+ If the custom styling works, the checkbox on the right should
742
+ have a green accent color instead of the component's default
743
+ accent color.
744
+ </p>
745
+ </div>
746
+ </div>
747
+ </div>
748
+ );
749
+ };
750
+
631
751
  render(<App />, document.querySelector("#root"));
632
752
  </script>
633
753
  </body>