@jsenv/navi 0.2.1 → 0.3.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,17 +1,17 @@
1
1
  <!doctype html>
2
2
  <html>
3
3
  <head>
4
- <title>Simple Validation Message Demo</title>
4
+ <title>Simple Callout Demo</title>
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <meta charset="utf-8" />
7
7
  <link rel="icon" href="data:," />
8
8
  <style>
9
9
  body {
10
+ min-height: 300vh; /* Make page scrollable */
10
11
  margin: 0;
11
12
  padding: 20px;
12
13
  font-family: Arial, sans-serif;
13
14
  background: linear-gradient(45deg, #f0f8ff, #e6f3ff);
14
- min-height: 300vh; /* Make page scrollable */
15
15
  }
16
16
 
17
17
  .container {
@@ -29,14 +29,14 @@
29
29
 
30
30
  .target-element {
31
31
  display: inline-block;
32
+ margin: 10px;
32
33
  padding: 12px 20px;
33
- background: #4caf50;
34
34
  color: white;
35
+ font-size: 16px;
36
+ background: #4caf50;
35
37
  border: none;
36
38
  border-radius: 4px;
37
39
  cursor: pointer;
38
- margin: 10px;
39
- font-size: 16px;
40
40
  }
41
41
 
42
42
  .target-element:hover {
@@ -44,7 +44,14 @@
44
44
  }
45
45
 
46
46
  .spacer {
47
+ display: flex;
47
48
  height: 200px;
49
+ margin: 40px 0;
50
+ align-items: center;
51
+ justify-content: center;
52
+ color: #666;
53
+ font-weight: bold;
54
+ font-size: 18px;
48
55
  background: repeating-linear-gradient(
49
56
  45deg,
50
57
  transparent,
@@ -52,44 +59,37 @@
52
59
  rgba(0, 0, 0, 0.1) 20px,
53
60
  rgba(0, 0, 0, 0.1) 40px
54
61
  );
55
- margin: 40px 0;
56
- display: flex;
57
- align-items: center;
58
- justify-content: center;
59
- color: #666;
60
- font-weight: bold;
61
- font-size: 18px;
62
62
  }
63
63
 
64
64
  .scroll-instruction {
65
65
  position: fixed;
66
66
  top: 10px;
67
67
  right: 10px;
68
- background: #333;
69
- color: white;
68
+ z-index: 1000;
70
69
  padding: 10px;
71
- border-radius: 4px;
70
+ color: white;
72
71
  font-size: 14px;
73
- z-index: 1000;
72
+ background: #333;
73
+ border-radius: 4px;
74
74
  }
75
75
 
76
76
  .scrollable-container {
77
- height: 300px;
78
77
  width: 100%;
79
- overflow: auto;
78
+ height: 300px;
79
+ padding: 20px;
80
+ background: #f9f9f9;
80
81
  border: 2px solid #ddd;
81
82
  border-radius: 8px;
82
- background: #f9f9f9;
83
- padding: 20px;
83
+ overflow: auto;
84
84
  }
85
85
 
86
86
  .scrollable-content {
87
- height: 600px;
88
- width: 800px;
89
87
  display: flex;
88
+ width: 800px;
89
+ height: 600px;
90
90
  flex-direction: column;
91
- justify-content: space-around;
92
91
  align-items: center;
92
+ justify-content: space-around;
93
93
  background: repeating-linear-gradient(
94
94
  45deg,
95
95
  transparent,
@@ -101,20 +101,18 @@
101
101
  </style>
102
102
  </head>
103
103
  <body>
104
- <div class="scroll-instruction">
105
- Scroll to test validation message positioning
106
- </div>
104
+ <div class="scroll-instruction">Scroll to test callout positioning</div>
107
105
 
108
106
  <div class="container">
109
- <h1>Simple Validation Message Demo</h1>
107
+ <h1>Simple Callout Demo</h1>
110
108
  <p>
111
- This demo shows how the validation message follows its target element
112
- during scrolling.
109
+ This demo shows how the callout follows its target element during
110
+ scrolling.
113
111
  </p>
114
112
 
115
113
  <div class="section">
116
- <h2>Auto-opened Validation Message</h2>
117
- <p>This validation message opens automatically when the page loads:</p>
114
+ <h2>Auto-opened Callout</h2>
115
+ <p>This callout opens automatically when the page loads:</p>
118
116
  <p>
119
117
  <strong
120
118
  >Scroll inside the container below to test positioning within a
@@ -139,8 +137,8 @@
139
137
  <div class="section">
140
138
  <h2>Middle Section</h2>
141
139
  <p>
142
- The validation message should follow the target element as you scroll
143
- past this section.
140
+ The callout should follow the target element as you scroll past this
141
+ section.
144
142
  </p>
145
143
  </div>
146
144
 
@@ -158,7 +156,7 @@
158
156
 
159
157
  <div class="section">
160
158
  <h2>Final Section</h2>
161
- <p>Scroll back up to see how the validation message behaves.</p>
159
+ <p>Scroll back up to see how the callout behaves.</p>
162
160
  </div>
163
161
 
164
162
  <div
@@ -171,32 +169,32 @@
171
169
  margin-top: 40px;
172
170
  "
173
171
  >
174
- <p>Bottom spacer - scroll back up to see validation messages</p>
172
+ <p>Bottom spacer - scroll back up to see callouts</p>
175
173
  </div>
176
174
  </div>
177
175
 
178
176
  <script type="module">
179
- import { openValidationMessage } from "../validation_message.js";
177
+ import { openCallout } from "@jsenv/navi";
180
178
 
181
- // Auto-open validation message on page load
179
+ // Auto-open callout on page load
182
180
  window.addEventListener("load", () => {
183
181
  const autoTarget = document.getElementById("auto-target");
184
- openValidationMessage(
185
- autoTarget,
186
- `This is a very long validation message that was opened automatically when the page loaded. <strong>Try scrolling both horizontally and vertically</strong> within the container to see how the validation message follows the target element! This message is intentionally made very long to test how the positioning system handles messages that exceed the visible area of their container. The message should properly position itself even when parts of it would normally be clipped by the container boundaries. You can scroll in any direction - up, down, left, right - to test the robustness of the positioning algorithm. This lengthy content helps verify that the validation message system can handle edge cases where the message content is larger than the available viewport space within scrollable containers.`,
182
+ openCallout(
183
+ `This is a very long callout that was opened automatically when the page loaded. <strong>Try scrolling both horizontally and vertically</strong> within the container to see how the callout follows the target element! This message is intentionally made very long to test how the positioning system handles callouts that exceed the visible area of their container. The callout should properly position itself even when parts of it would normally be clipped by the container boundaries. You can scroll in any direction - up, down, left, right - to test the robustness of the positioning algorithm. This lengthy content helps verify that the callout system can handle edge cases where the callout content is larger than the available viewport space within scrollable containers.`,
187
184
  {
185
+ anchorElement: autoTarget,
188
186
  level: "warning",
189
187
  debug: true,
190
188
  canClickThrough: true,
191
189
  onClose: () => {
192
- console.log("Validation message was closed");
190
+ console.log("Callout was closed");
193
191
  },
194
192
  },
195
193
  );
196
194
  });
197
195
 
198
196
  console.log(
199
- "Simple validation message demo loaded. Check console for debug output.",
197
+ "Simple callout demo loaded. Check console for debug output.",
200
198
  );
201
199
  </script>
202
200
  </body>
@@ -0,0 +1,161 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Dynamic Callout Positioning Test</title>
6
+ <style>
7
+ body {
8
+ margin: 0;
9
+ padding: 20px;
10
+ font-family: Arial, sans-serif;
11
+ }
12
+ .anchor {
13
+ display: inline-block;
14
+ margin: 10px;
15
+ padding: 10px 20px;
16
+ color: white;
17
+ background: #007acc;
18
+ border-radius: 4px;
19
+ cursor: pointer;
20
+ }
21
+ .test-section {
22
+ margin: 40px 0;
23
+ padding: 20px;
24
+ border: 1px solid #ddd;
25
+ border-radius: 4px;
26
+ }
27
+ .resize-controls {
28
+ position: fixed;
29
+ top: 10px;
30
+ right: 10px;
31
+ z-index: 1000;
32
+ padding: 10px;
33
+ background: white;
34
+ border: 1px solid #ccc;
35
+ border-radius: 4px;
36
+ }
37
+ .large-anchor {
38
+ display: flex;
39
+ width: 300px;
40
+ height: 200px;
41
+ margin: 20px;
42
+ align-items: center;
43
+ justify-content: center;
44
+ background: #28a745;
45
+ }
46
+ .tiny-anchor {
47
+ display: flex;
48
+ width: 50px;
49
+ height: 20px;
50
+ margin: 20px;
51
+ align-items: center;
52
+ justify-content: center;
53
+ font-size: 12px;
54
+ background: #dc3545;
55
+ }
56
+ </style>
57
+ </head>
58
+ <body>
59
+ <div class="resize-controls">
60
+ <button onclick="resizeWindow(800, 600)">Small (800x600)</button>
61
+ <button onclick="resizeWindow(1200, 800)">Medium (1200x800)</button>
62
+ <button onclick="resizeWindow(1600, 1000)">Large (1600x1000)</button>
63
+ <div>Current: <span id="current-size"></span></div>
64
+ </div>
65
+
66
+ <div class="test-section">
67
+ <h2>Test 1: Normal anchor element</h2>
68
+ <div
69
+ class="anchor"
70
+ id="anchor1"
71
+ onclick="openTestCallout(this, 'Normal anchor test')"
72
+ >
73
+ Click me - Normal anchor
74
+ </div>
75
+ <p>
76
+ This should show an anchored callout with arrow when viewport is large
77
+ enough.
78
+ </p>
79
+ </div>
80
+
81
+ <div class="test-section">
82
+ <h2>Test 2: Large anchor element</h2>
83
+ <div
84
+ class="large-anchor"
85
+ id="anchor2"
86
+ onclick="openTestCallout(this, 'Large anchor test - should be centered when too big for viewport')"
87
+ >
88
+ Large Anchor Element
89
+ </div>
90
+ <p>
91
+ This should center in viewport when anchor is too large relative to
92
+ viewport height.
93
+ </p>
94
+ </div>
95
+
96
+ <div class="test-section">
97
+ <h2>Test 3: Tiny anchor element</h2>
98
+ <div
99
+ class="tiny-anchor"
100
+ id="anchor3"
101
+ onclick="openTestCallout(this, 'Tiny anchor test')"
102
+ >
103
+ Tiny
104
+ </div>
105
+ <p>This should work with anchored positioning in most cases.</p>
106
+ </div>
107
+
108
+ <script type="module">
109
+ import { openCallout } from "./callout.js";
110
+
111
+ let currentCallout = null;
112
+
113
+ window.openTestCallout = (anchorElement, message) => {
114
+ // Close existing callout
115
+ if (currentCallout) {
116
+ currentCallout.close();
117
+ }
118
+
119
+ currentCallout = openCallout({
120
+ anchorElement,
121
+ calloutElement: createCalloutContent(message),
122
+ level: "info",
123
+ debug: true,
124
+ });
125
+ };
126
+
127
+ function createCalloutContent(message) {
128
+ const div = document.createElement("div");
129
+ div.style.padding = "20px";
130
+ div.style.maxWidth = "300px";
131
+ div.style.background = "white";
132
+ div.innerHTML = `
133
+ <h3>Dynamic Positioning Test</h3>
134
+ <p>${message}</p>
135
+ <p>Window size: ${window.innerWidth} x ${window.innerHeight}</p>
136
+ <button onclick="currentCallout?.close()">Close</button>
137
+ `;
138
+ return div;
139
+ }
140
+
141
+ window.resizeWindow = (width, height) => {
142
+ window.resizeTo(width, height);
143
+ updateSizeDisplay();
144
+ };
145
+
146
+ function updateSizeDisplay() {
147
+ document.getElementById("current-size").textContent =
148
+ `${window.innerWidth} x ${window.innerHeight}`;
149
+ }
150
+
151
+ // Update size display on load and resize
152
+ window.addEventListener("load", updateSizeDisplay);
153
+ window.addEventListener("resize", updateSizeDisplay);
154
+
155
+ // Test automatic strategy changes on resize
156
+ window.addEventListener("resize", () => {
157
+ console.debug("Window resized, callout should re-evaluate strategy");
158
+ });
159
+ </script>
160
+ </body>
161
+ </html>
@@ -17,7 +17,6 @@ import { useExecuteAction } from "../action_execution/use_execute_action.js";
17
17
  import { LoaderBackground } from "../loader/loader_background.jsx";
18
18
  import { useAutoFocus } from "../use_auto_focus.js";
19
19
  import { initCustomField } from "./custom_field.js";
20
- import "./navi_css_vars.js";
21
20
  import { useActionEvents } from "./use_action_events.js";
22
21
  import { useFormEvents } from "./use_form_events.js";
23
22
  import {
@@ -148,9 +147,9 @@ import.meta.css = /* css */ `
148
147
  .navi_button[data-disabled] .navi_button_shadow {
149
148
  box-shadow: none;
150
149
  }
151
- /* Invalid */
152
- .navi_button[aria-invalid="true"] .navi_button_content {
153
- --border-color: var(--invalid-color);
150
+ /* Callout (info, warning, error) */
151
+ .navi_button[data-callout] .navi_button_content {
152
+ --border-color: var(--callout-color);
154
153
  }
155
154
 
156
155
  /* Discrete variant */
@@ -231,7 +230,7 @@ const ButtonBasic = forwardRef((props, ref) => {
231
230
  data-readonly={innerReadOnly ? "" : undefined}
232
231
  data-readonly-silent={innerLoading ? "" : undefined}
233
232
  data-disabled={innerDisabled ? "" : undefined}
234
- data-validation-message-arrow-x="center"
233
+ data-callout-arrow-x="center"
235
234
  aria-busy={innerLoading}
236
235
  >
237
236
  <LoaderBackground
@@ -23,7 +23,6 @@ import {
23
23
  ReportDisabledOnLabelContext,
24
24
  ReportReadOnlyOnLabelContext,
25
25
  } from "./label.jsx";
26
- import "./navi_css_vars.js";
27
26
  import { useActionEvents } from "./use_action_events.js";
28
27
  import {
29
28
  DisabledContext,
@@ -250,7 +249,7 @@ const InputCheckboxBasic = forwardRef((props, ref) => {
250
249
  readOnly={innerReadOnly}
251
250
  disabled={innerDisabled}
252
251
  required={innerRequired}
253
- data-validation-message-arrow-x="center"
252
+ data-callout-arrow-x="center"
254
253
  onClick={(e) => {
255
254
  if (innerReadOnly) {
256
255
  e.preventDefault();
@@ -299,7 +299,7 @@ const InputRadioBasic = forwardRef((props, ref) => {
299
299
  checked={checked}
300
300
  disabled={innerDisabled}
301
301
  required={innerRequired}
302
- data-validation-message-arrow-x="center"
302
+ data-callout-arrow-x="center"
303
303
  onClick={(e) => {
304
304
  if (innerReadOnly) {
305
305
  e.preventDefault();
@@ -120,9 +120,9 @@ import.meta.css = /* css */ `
120
120
  background-color: var(--background-color-disabled);
121
121
  outline-color: var(--border-color-disabled);
122
122
  }
123
- /* Invalid */
124
- .navi_input[aria-invalid="true"] {
125
- border-color: var(--invalid-color);
123
+ /* Callout (info, warning, error) */
124
+ .navi_input[data-callout] {
125
+ border-color: var(--callout-color);
126
126
  }
127
127
  }
128
128
  `;
@@ -5,6 +5,10 @@ import.meta.css = /* css */ `
5
5
  --navi-color-readonly: grey;
6
6
  --navi-background-color-disabled: #d3d3d3;
7
7
  --navi-color-disabled: #eeeeee;
8
+
9
+ --navi-info-color: #2196f3;
10
+ --navi-warning-color: #ff9800;
11
+ --navi-error-color: #f44336;
8
12
  }
9
13
  }
10
14
  `;
@@ -27,6 +27,7 @@
27
27
  * - Validation messages that follow the input element and adapt to viewport
28
28
  */
29
29
 
30
+ import { openCallout } from "../components/callout/callout.js";
30
31
  import {
31
32
  DISABLED_CONSTRAINT,
32
33
  MAX_CONSTRAINT,
@@ -39,7 +40,6 @@ import {
39
40
  TYPE_NUMBER_CONSTRAINT,
40
41
  } from "./constraints/native_constraints.js";
41
42
  import { READONLY_CONSTRAINT } from "./constraints/readonly_constraint.js";
42
- import { openValidationMessage } from "./validation_message.js";
43
43
 
44
44
  let debug = false;
45
45
 
@@ -373,10 +373,10 @@ export const installCustomConstraintValidation = (
373
373
  const elementTarget =
374
374
  failedConstraintInfo.target || elementReceivingValidationMessage;
375
375
 
376
- validationInterface.validationMessage = openValidationMessage(
377
- elementTarget,
376
+ validationInterface.validationMessage = openCallout(
378
377
  failedConstraintInfo.message,
379
378
  {
379
+ anchors: elementTarget,
380
380
  level: failedConstraintInfo.level,
381
381
  closeOnClickOutside: failedConstraintInfo.closeOnClickOutside,
382
382
  onClose: () => {
@@ -154,12 +154,12 @@ lorsqu'on zoom le comportement semble étrange
154
154
  <div style="background: red; width: 2000px; height: 1px"></div>
155
155
  <div style="background: blue; height: 400px"></div>
156
156
  <script type="module">
157
- import { openValidationMessage } from "../validation_message.js";
157
+ import { openCallout } from "../../components/callout/callout.js";
158
158
 
159
159
  const inputs = document.querySelectorAll("input");
160
160
  for (const input of inputs) {
161
161
  input.addEventListener("focus", () => {
162
- openValidationMessage(input, "Veuillez respecter le format requis.");
162
+ openCallout(input, "Veuillez respecter le format requis.");
163
163
  });
164
164
  }
165
165