@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.
- package/dist/jsenv_navi.js +4487 -4178
- package/index.js +5 -2
- package/package.json +4 -3
- package/src/components/callout/callout.js +944 -0
- package/src/{validation/demos/validation_message_demo.html → components/callout/callout_demo.html} +40 -42
- package/src/components/callout/test_dynamic_positioning.html +161 -0
- package/src/components/field/button.jsx +4 -5
- package/src/components/field/input_checkbox.jsx +1 -2
- package/src/components/field/input_radio.jsx +1 -1
- package/src/components/field/input_textual.jsx +3 -3
- package/src/{components/field/navi_css_vars.js → navi_css_vars.js} +4 -0
- package/src/validation/custom_constraint_validation.js +3 -3
- package/src/validation/demos/form_validation_vs_native_demo.html +2 -2
- package/src/validation/validation_message.js +0 -753
package/src/{validation/demos/validation_message_demo.html → components/callout/callout_demo.html}
RENAMED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
|
-
<title>Simple
|
|
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
|
-
|
|
69
|
-
color: white;
|
|
68
|
+
z-index: 1000;
|
|
70
69
|
padding: 10px;
|
|
71
|
-
|
|
70
|
+
color: white;
|
|
72
71
|
font-size: 14px;
|
|
73
|
-
|
|
72
|
+
background: #333;
|
|
73
|
+
border-radius: 4px;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
.scrollable-container {
|
|
77
|
-
height: 300px;
|
|
78
77
|
width: 100%;
|
|
79
|
-
|
|
78
|
+
height: 300px;
|
|
79
|
+
padding: 20px;
|
|
80
|
+
background: #f9f9f9;
|
|
80
81
|
border: 2px solid #ddd;
|
|
81
82
|
border-radius: 8px;
|
|
82
|
-
|
|
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
|
|
107
|
+
<h1>Simple Callout Demo</h1>
|
|
110
108
|
<p>
|
|
111
|
-
This demo shows how the
|
|
112
|
-
|
|
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
|
|
117
|
-
<p>This
|
|
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
|
|
143
|
-
|
|
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
|
|
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
|
|
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 {
|
|
177
|
+
import { openCallout } from "@jsenv/navi";
|
|
180
178
|
|
|
181
|
-
// Auto-open
|
|
179
|
+
// Auto-open callout on page load
|
|
182
180
|
window.addEventListener("load", () => {
|
|
183
181
|
const autoTarget = document.getElementById("auto-target");
|
|
184
|
-
|
|
185
|
-
|
|
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("
|
|
190
|
+
console.log("Callout was closed");
|
|
193
191
|
},
|
|
194
192
|
},
|
|
195
193
|
);
|
|
196
194
|
});
|
|
197
195
|
|
|
198
196
|
console.log(
|
|
199
|
-
"Simple
|
|
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
|
-
/*
|
|
152
|
-
.navi_button[
|
|
153
|
-
--border-color: var(--
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
/*
|
|
124
|
-
.navi_input[
|
|
125
|
-
border-color: var(--
|
|
123
|
+
/* Callout (info, warning, error) */
|
|
124
|
+
.navi_input[data-callout] {
|
|
125
|
+
border-color: var(--callout-color);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
`;
|
|
@@ -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 =
|
|
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 {
|
|
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
|
-
|
|
162
|
+
openCallout(input, "Veuillez respecter le format requis.");
|
|
163
163
|
});
|
|
164
164
|
}
|
|
165
165
|
|