@nexterajs/ui 0.0.1 → 0.0.2
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/fesm2022/nexterajs-ui-core.mjs +189 -5
- package/fesm2022/nexterajs-ui-core.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-dropdowns.mjs +314 -50
- package/fesm2022/nexterajs-ui-dropdowns.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-file-uploader.mjs +6 -3
- package/fesm2022/nexterajs-ui-file-uploader.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-forms.mjs +729 -370
- package/fesm2022/nexterajs-ui-forms.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-grid.mjs +1188 -208
- package/fesm2022/nexterajs-ui-grid.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-intl.mjs +15 -5
- package/fesm2022/nexterajs-ui-intl.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-notify.mjs +351 -0
- package/fesm2022/nexterajs-ui-notify.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-pivot.mjs +146 -116
- package/fesm2022/nexterajs-ui-pivot.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-styling.mjs +568 -3
- package/fesm2022/nexterajs-ui-styling.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-tree-view.mjs +147 -18
- package/fesm2022/nexterajs-ui-tree-view.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-validation.mjs +247 -92
- package/fesm2022/nexterajs-ui-validation.mjs.map +1 -1
- package/fesm2022/nexterajs-ui.mjs +1 -0
- package/fesm2022/nexterajs-ui.mjs.map +1 -1
- package/package.json +5 -1
- package/styles/_dropdown.scss +149 -0
- package/styles/_editor.scss +23 -4
- package/styles/_icons.scss +92 -0
- package/styles/_mixins.scss +104 -1
- package/styles/_scrollbar.scss +134 -0
- package/styles/_toast.scss +243 -0
- package/styles/_tokens.scss +48 -0
- package/styles/index.scss +4 -0
- package/types/nexterajs-ui-core.d.ts +142 -22
- package/types/nexterajs-ui-dropdowns.d.ts +145 -27
- package/types/nexterajs-ui-file-uploader.d.ts +4 -1
- package/types/nexterajs-ui-forms.d.ts +103 -19
- package/types/nexterajs-ui-grid.d.ts +445 -57
- package/types/nexterajs-ui-notify.d.ts +147 -0
- package/types/nexterajs-ui-pivot.d.ts +35 -22
- package/types/nexterajs-ui-styling.d.ts +163 -3
- package/types/nexterajs-ui-tree-view.d.ts +50 -1
- package/types/nexterajs-ui-validation.d.ts +94 -15
- package/types/nexterajs-ui.d.ts +1 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// @nexterajs/ui — NxeToastService chrome.
|
|
2
|
+
//
|
|
3
|
+
// A value-for-value port of ngx-toastr's `toastr.css`, which this replaces. Every number, colour
|
|
4
|
+
// and easing here is copied deliberately rather than chosen: applications were pinned to that
|
|
5
|
+
// rendering, and "close enough" on a toast is a visible regression nothing tests.
|
|
6
|
+
//
|
|
7
|
+
// Load this where `ngx-toastr/toastr.css` used to sit in the cascade — BEFORE the application's
|
|
8
|
+
// own overrides — or a rule like `width: 350px` on a position class stops winning.
|
|
9
|
+
|
|
10
|
+
/* position */
|
|
11
|
+
.nxe-toast-center-center {
|
|
12
|
+
top: 50%;
|
|
13
|
+
left: 50%;
|
|
14
|
+
transform: translate(-50%, -50%);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.toast-top-center {
|
|
18
|
+
top: 0;
|
|
19
|
+
right: 0;
|
|
20
|
+
width: 100%;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.toast-bottom-center {
|
|
24
|
+
bottom: 0;
|
|
25
|
+
right: 0;
|
|
26
|
+
width: 100%;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.toast-top-full-width {
|
|
30
|
+
top: 0;
|
|
31
|
+
right: 0;
|
|
32
|
+
width: 100%;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.toast-bottom-full-width {
|
|
36
|
+
bottom: 0;
|
|
37
|
+
right: 0;
|
|
38
|
+
width: 100%;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.toast-top-left {
|
|
42
|
+
top: 12px;
|
|
43
|
+
left: 12px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.toast-top-right {
|
|
47
|
+
top: 12px;
|
|
48
|
+
right: 12px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.toast-bottom-right {
|
|
52
|
+
right: 12px;
|
|
53
|
+
bottom: 12px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.toast-bottom-left {
|
|
57
|
+
bottom: 12px;
|
|
58
|
+
left: 12px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* toast styles */
|
|
62
|
+
.nxe-toast-title {
|
|
63
|
+
font-weight: bold;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.nxe-toast-message {
|
|
67
|
+
word-wrap: break-word;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.nxe-toast-message a,
|
|
71
|
+
.nxe-toast-message label {
|
|
72
|
+
color: #ffffff;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.nxe-toast-message a:hover {
|
|
76
|
+
color: #cccccc;
|
|
77
|
+
text-decoration: none;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.nxe-toast-close {
|
|
81
|
+
position: relative;
|
|
82
|
+
right: -0.3em;
|
|
83
|
+
top: -0.3em;
|
|
84
|
+
float: right;
|
|
85
|
+
font-size: 20px;
|
|
86
|
+
font-weight: bold;
|
|
87
|
+
color: #ffffff;
|
|
88
|
+
text-shadow: 0 1px 0 #ffffff;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.nxe-toast-close:hover,
|
|
92
|
+
.nxe-toast-close:focus {
|
|
93
|
+
color: #000000;
|
|
94
|
+
text-decoration: none;
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
opacity: 0.4;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
button.nxe-toast-close {
|
|
100
|
+
padding: 0;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
background: transparent;
|
|
103
|
+
border: 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.nxe-toast-container {
|
|
107
|
+
pointer-events: none;
|
|
108
|
+
position: fixed;
|
|
109
|
+
z-index: 999999;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.nxe-toast-container * {
|
|
113
|
+
box-sizing: border-box;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.nxe-toast-container .nxe-toast {
|
|
117
|
+
position: relative;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
margin: 0 0 6px;
|
|
120
|
+
padding: 15px 15px 15px 50px;
|
|
121
|
+
width: 300px;
|
|
122
|
+
border-radius: 3px 3px 3px 3px;
|
|
123
|
+
background-position: 15px center;
|
|
124
|
+
background-repeat: no-repeat;
|
|
125
|
+
background-size: 24px;
|
|
126
|
+
box-shadow: 0 0 12px #999999;
|
|
127
|
+
color: #ffffff;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.nxe-toast-container .nxe-toast:hover {
|
|
131
|
+
box-shadow: 0 0 12px #000000;
|
|
132
|
+
opacity: 1;
|
|
133
|
+
cursor: pointer;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* The four icons, as shipped: Font Awesome info-circle, times-circle, check and
|
|
137
|
+
exclamation-triangle, inlined so the toast needs no icon font and no network request. */
|
|
138
|
+
.nxe-toast-info {
|
|
139
|
+
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTI1NiA4QzExOS4wNDMgOCA4IDExOS4wODMgOCAyNTZjMCAxMzYuOTk3IDExMS4wNDMgMjQ4IDI0OCAyNDhzMjQ4LTExMS4wMDMgMjQ4LTI0OEM1MDQgMTE5LjA4MyAzOTIuOTU3IDggMjU2IDh6bTAgMTEwYzIzLjE5NiAwIDQyIDE4LjgwNCA0MiA0MnMtMTguODA0IDQyLTQyIDQyLTQyLTE4LjgwNC00Mi00MiAxOC44MDQtNDIgNDItNDJ6bTU2IDI1NGMwIDYuNjI3LTUuMzczIDEyLTEyIDEyaC04OGMtNi42MjcgMC0xMi01LjM3My0xMi0xMnYtMjRjMC02LjYyNyA1LjM3My0xMiAxMi0xMmgxMnYtNjRoLTEyYy02LjYyNyAwLTEyLTUuMzczLTEyLTEydi0yNGMwLTYuNjI3IDUuMzczLTEyIDEyLTEyaDY0YzYuNjI3IDAgMTIgNS4zNzMgMTIgMTJ2MTAwaDEyYzYuNjI3IDAgMTIgNS4zNzMgMTIgMTJ2MjR6Jy8+PC9zdmc+");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.nxe-toast-error {
|
|
143
|
+
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTI1NiA4QzExOSA4IDggMTE5IDggMjU2czExMSAyNDggMjQ4IDI0OCAyNDgtMTExIDI0OC0yNDhTMzkzIDggMjU2IDh6bTEyMS42IDMxMy4xYzQuNyA0LjcgNC43IDEyLjMgMCAxN0wzMzggMzc3LjZjLTQuNyA0LjctMTIuMyA0LjctMTcgMEwyNTYgMzEybC02NS4xIDY1LjZjLTQuNyA0LjctMTIuMyA0LjctMTcgMEwxMzQuNCAzMzhjLTQuNy00LjctNC43LTEyLjMgMC0xN2w2NS42LTY1LTY1LjYtNjUuMWMtNC43LTQuNy00LjctMTIuMyAwLTE3bDM5LjYtMzkuNmM0LjctNC43IDEyLjMtNC43IDE3IDBsNjUgNjUuNyA2NS4xLTY1LjZjNC43LTQuNyAxMi4zLTQuNyAxNyAwbDM5LjYgMzkuNmM0LjcgNC43IDQuNyAxMi4zIDAgMTdMMzEyIDI1Nmw2NS42IDY1LjF6Jy8+PC9zdmc+");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.nxe-toast-success {
|
|
147
|
+
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTE3My44OTggNDM5LjQwNGwtMTY2LjQtMTY2LjRjLTkuOTk3LTkuOTk3LTkuOTk3LTI2LjIwNiAwLTM2LjIwNGwzNi4yMDMtMzYuMjA0YzkuOTk3LTkuOTk4IDI2LjIwNy05Ljk5OCAzNi4yMDQgMEwxOTIgMzEyLjY5IDQzMi4wOTUgNzIuNTk2YzkuOTk3LTkuOTk3IDI2LjIwNy05Ljk5NyAzNi4yMDQgMGwzNi4yMDMgMzYuMjA0YzkuOTk3IDkuOTk3IDkuOTk3IDI2LjIwNiAwIDM2LjIwNGwtMjk0LjQgMjk0LjQwMWMtOS45OTggOS45OTctMjYuMjA3IDkuOTk3LTM2LjIwNC0uMDAxeicvPjwvc3ZnPg==");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.nxe-toast-warning {
|
|
151
|
+
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1NzYgNTEyJyB3aWR0aD0nNTc2JyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTU2OS41MTcgNDQwLjAxM0M1ODcuOTc1IDQ3Mi4wMDcgNTY0LjgwNiA1MTIgNTI3Ljk0IDUxMkg0OC4wNTRjLTM2LjkzNyAwLTU5Ljk5OS00MC4wNTUtNDEuNTc3LTcxLjk4N0wyNDYuNDIzIDIzLjk4NWMxOC40NjctMzIuMDA5IDY0LjcyLTMxLjk1MSA4My4xNTQgMGwyMzkuOTQgNDE2LjAyOHpNMjg4IDM1NGMtMjUuNDA1IDAtNDYgMjAuNTk1LTQ2IDQ2czIwLjU5NSA0NiA0NiA0NiA0Ni0yMC41OTUgNDYtNDYtMjAuNTk1LTQ2LTQ2LTQ2em0tNDMuNjczLTE2NS4zNDZsNy40MTggMTM2Yy4zNDcgNi4zNjQgNS42MDkgMTEuMzQ2IDExLjk4MiAxMS4zNDZoNDguNTQ2YzYuMzczIDAgMTEuNjM1LTQuOTgyIDExLjk4Mi0xMS4zNDZsNy40MTgtMTM2Yy4zNzUtNi44NzQtNS4wOTgtMTIuNjU0LTExLjk4Mi0xMi42NTRoLTYzLjM4M2MtNi44ODQgMC0xMi4zNTYgNS43OC0xMS45ODEgMTIuNjU0eicvPjwvc3ZnPg==");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.nxe-toast-container.toast-top-center .nxe-toast,
|
|
155
|
+
.nxe-toast-container.toast-bottom-center .nxe-toast {
|
|
156
|
+
width: 300px;
|
|
157
|
+
margin-left: auto;
|
|
158
|
+
margin-right: auto;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.nxe-toast-container.toast-top-full-width .nxe-toast,
|
|
162
|
+
.nxe-toast-container.toast-bottom-full-width .nxe-toast {
|
|
163
|
+
width: 96%;
|
|
164
|
+
margin-left: auto;
|
|
165
|
+
margin-right: auto;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.nxe-toast {
|
|
169
|
+
// Required for parity, not a change to it. The original toast was a <div>; this one is a
|
|
170
|
+
// custom element, and an unknown element is `display: inline` by default — which would make
|
|
171
|
+
// the `width`, `padding` and `margin` above do nothing at all, silently.
|
|
172
|
+
display: block;
|
|
173
|
+
|
|
174
|
+
background-color: #030303;
|
|
175
|
+
pointer-events: auto;
|
|
176
|
+
|
|
177
|
+
// ngx-toastr animated between `inactive`/`active`/`removed` states that differ only in
|
|
178
|
+
// opacity, over 300ms ease-in. No movement — matching that exactly, not improving on it.
|
|
179
|
+
opacity: 1;
|
|
180
|
+
transition: opacity 300ms ease-in;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.nxe-toast-entering,
|
|
184
|
+
.nxe-toast-leaving {
|
|
185
|
+
opacity: 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.nxe-toast-success {
|
|
189
|
+
background-color: #51a351;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.nxe-toast-error {
|
|
193
|
+
background-color: #bd362f;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.nxe-toast-info {
|
|
197
|
+
background-color: #2f96b4;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.nxe-toast-warning {
|
|
201
|
+
background-color: #f89406;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.nxe-toast-progress {
|
|
205
|
+
position: absolute;
|
|
206
|
+
left: 0;
|
|
207
|
+
bottom: 0;
|
|
208
|
+
height: 4px;
|
|
209
|
+
background-color: #000000;
|
|
210
|
+
opacity: 0.4;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Responsive Design */
|
|
214
|
+
@media all and (max-width: 240px) {
|
|
215
|
+
.nxe-toast-container .nxe-toast.div {
|
|
216
|
+
padding: 8px 8px 8px 50px;
|
|
217
|
+
width: 11em;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.nxe-toast-container .nxe-toast-close {
|
|
221
|
+
right: -0.2em;
|
|
222
|
+
top: -0.2em;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
@media all and (min-width: 241px) and (max-width: 480px) {
|
|
227
|
+
.nxe-toast-container .nxe-toast.div {
|
|
228
|
+
padding: 8px 8px 8px 50px;
|
|
229
|
+
width: 18em;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.nxe-toast-container .nxe-toast-close {
|
|
233
|
+
right: -0.2em;
|
|
234
|
+
top: -0.2em;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
@media all and (min-width: 481px) and (max-width: 768px) {
|
|
239
|
+
.nxe-toast-container .nxe-toast.div {
|
|
240
|
+
padding: 15px 15px 15px 50px;
|
|
241
|
+
width: 25em;
|
|
242
|
+
}
|
|
243
|
+
}
|
package/styles/_tokens.scss
CHANGED
|
@@ -66,6 +66,54 @@
|
|
|
66
66
|
// carried the dx-era `.dx-overlay-content { min-width: 400px !important }` here until
|
|
67
67
|
// 2026-07-29 and dropped it — see nxe-ui/DEFAULTS.md.
|
|
68
68
|
--nxe-ui-overlay-min-width: 0px;
|
|
69
|
+
// Header-filter popup floor. The popup is dx's own 252x325; broker's global
|
|
70
|
+
// `.dx-overlay-content{min-width:400px}` made it 400px wide in the dx era, which an app can
|
|
71
|
+
// restore here without widening every dropdown panel.
|
|
72
|
+
--nxe-ui-header-filter-min-width: 0px;
|
|
73
|
+
// Field-chooser popup floor. dx's own field chooser popup has no 400px floor; broker's global
|
|
74
|
+
// `.dx-overlay-content{min-width:400px}` made it 400px wide in the dx era, which an app can
|
|
75
|
+
// restore here without widening every dropdown panel.
|
|
76
|
+
--nxe-ui-fieldchooser-min-width: 0px;
|
|
77
|
+
|
|
78
|
+
// Check box hover border - dx `.dx-checkbox.dx-state-hover .dx-checkbox-icon{border-color:#265a87}`.
|
|
79
|
+
--nxe-ui-checkbox-hover-border: #265a87;
|
|
80
|
+
|
|
81
|
+
// Gap between a button's projected children. dx `.dx-button-content` has none; an app whose
|
|
82
|
+
// icon + text buttons relied on the library's former 4px can set it back.
|
|
83
|
+
--nxe-ui-button-gap: 0px;
|
|
84
|
+
|
|
85
|
+
// Data grid empty state — dx `.dx-datagrid-rowsview.dx-empty{height:100px}` and
|
|
86
|
+
// `.dx-datagrid-nodata{font-size:17px}`.
|
|
87
|
+
--nxe-ui-grid-empty-height: 100px;
|
|
88
|
+
--nxe-ui-grid-nodata-font-size: 17px;
|
|
89
|
+
|
|
90
|
+
// Data grid and tree list lines - dx.light's values. Whether a grid draws them is per grid
|
|
91
|
+
// ([showRowLines], [showColumnLines], [showBorders], [rowAlternationEnabled]) or app-wide
|
|
92
|
+
// (NXE_DEFAULT_OPTIONS.dataGrid / .treeList); these set how they look. Override them at :root
|
|
93
|
+
// for the whole app, or on one grid, e.g.
|
|
94
|
+
// nxe-data-grid.quotes { --nxe-ui-grid-column-line-color: #e2e8f0; }
|
|
95
|
+
// The grid's rules outweigh dx-compat's #ddd copies, so an override holds on selected, edited
|
|
96
|
+
// and group rows too. Literals, not var(--nxe-ui-border): a var() here resolves at :root, and a
|
|
97
|
+
// subtree override of --nxe-ui-border would never reach them.
|
|
98
|
+
--nxe-ui-grid-line-width: 1px; // every grid line and the showBorders box
|
|
99
|
+
--nxe-ui-grid-row-line-color: #ddd; // `.dx-row-lines>td{border-bottom}`, group rows and footers
|
|
100
|
+
--nxe-ui-grid-column-line-color: #ddd; // `.dx-column-lines>td{border-left;border-right}`
|
|
101
|
+
--nxe-ui-grid-border-color: #ddd; // showBorders: `.dx-datagrid-borders>.dx-datagrid-rowsview`,
|
|
102
|
+
// and the pager's top line (`.dx-pager{border-top}`), which
|
|
103
|
+
// lies on the box's bottom edge in a bordered grid
|
|
104
|
+
--nxe-ui-grid-header-line-color: #ddd; // `.dx-datagrid-headers .dx-datagrid-table .dx-row>td`
|
|
105
|
+
--nxe-ui-grid-alt-row-bg: #f5f5f5; // rowAlternationEnabled: `.dx-row-alt>td`
|
|
106
|
+
|
|
107
|
+
// Data grid group rows and expand cells - dx.light's values.
|
|
108
|
+
// group-row-color: `.dx-datagrid-rowsview .dx-row.dx-group-row:not(.dx-row-focused){color:#222}`
|
|
109
|
+
// summary-color: `.dx-datagrid-summary-item{color:rgba(51,51,51,.7)}` (aligned group sums)
|
|
110
|
+
// expand-offset: `.dx-command-expand.dx-datagrid-group-space{padding-top:7px}` - how far down
|
|
111
|
+
// the cell the 18px group / master-detail toggle starts. dx leaves it about
|
|
112
|
+
// 3.3px below the row's middle; an app that wants it centred sets
|
|
113
|
+
// (row height - 18px) / 2 here.
|
|
114
|
+
--nxe-ui-grid-group-row-color: #222;
|
|
115
|
+
--nxe-ui-grid-summary-color: rgba(51, 51, 51, 0.7);
|
|
116
|
+
--nxe-ui-grid-expand-offset: 7px;
|
|
69
117
|
|
|
70
118
|
// foreground.divider — slate.200
|
|
71
119
|
--nxe-divider-color: #e2e8f0;
|
package/styles/index.scss
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
@use 'editor';
|
|
11
11
|
@use 'dropdown';
|
|
12
12
|
@use 'button-directives';
|
|
13
|
+
@use 'icons';
|
|
13
14
|
|
|
14
15
|
// The widget base (font family/size/weight/line-height). Must come before the component chrome
|
|
15
16
|
// below so those can override it, and it is what carries the contract across a CDK portal.
|
|
@@ -23,6 +24,9 @@
|
|
|
23
24
|
// Shared select-family dropdown panel (select-box, tag-box).
|
|
24
25
|
@include dropdown.nxe-dropdown;
|
|
25
26
|
|
|
27
|
+
// The `<i class="nxe-icon nxe-icon-NAME">` glyph layer (nxe-button's `icon`).
|
|
28
|
+
@include icons.nxe-icons;
|
|
29
|
+
|
|
26
30
|
// Pointer-drag chrome for NxeDragGesture, shared by the data grid's column/group drags and the
|
|
27
31
|
// pivot's field-panel/chooser chip drags. Global because a page can render a pivot and NO data
|
|
28
32
|
// grid (an ANALYTIC report is exactly that), and component styles only ship while an instance of
|
|
@@ -22,6 +22,17 @@ interface NxeValueChangedEvent<T> {
|
|
|
22
22
|
/** The component host element (DevExtreme `element`). */
|
|
23
23
|
element: HTMLElement;
|
|
24
24
|
}
|
|
25
|
+
/** Which native event an editor is re-emitting in dx's shape. */
|
|
26
|
+
type NxeEditorEventKind = 'keydown' | 'keyup' | 'input' | 'change' | 'focusin' | 'focusout';
|
|
27
|
+
/**
|
|
28
|
+
* dx's payload for the keyboard, focus and input events: the native event plus the widget and
|
|
29
|
+
* its host element, which is what handlers written against DevExtreme expect to receive.
|
|
30
|
+
*/
|
|
31
|
+
interface NxeEditorEvent {
|
|
32
|
+
event: Event;
|
|
33
|
+
component: unknown;
|
|
34
|
+
element: HTMLElement;
|
|
35
|
+
}
|
|
25
36
|
|
|
26
37
|
/** A single failed validation rule. */
|
|
27
38
|
interface NxeBrokenRule {
|
|
@@ -47,35 +58,40 @@ interface NxeValidationTarget {
|
|
|
47
58
|
getValue(): unknown;
|
|
48
59
|
validationGroup(): string | undefined;
|
|
49
60
|
setValidationError(message: string | null): void;
|
|
61
|
+
/**
|
|
62
|
+
* Tell the editor whether a `required` rule applies, so it can render `aria-required`.
|
|
63
|
+
* Optional: a target predating this simply does not advertise the requirement.
|
|
64
|
+
*/
|
|
65
|
+
setRequired?(required: boolean): void;
|
|
50
66
|
}
|
|
51
67
|
/** DI token the editor provides so its nested `nxe-validator` can adapt it. */
|
|
52
68
|
declare const NXE_VALIDATION_TARGET: InjectionToken<NxeValidationTarget>;
|
|
53
69
|
/** A member registered in a validation group (an `nxe-validator`). */
|
|
54
70
|
interface NxeValidationGroupMember {
|
|
55
71
|
validate(): NxeValidationResult;
|
|
72
|
+
/** Clear the member's error state without validating. Optional for older members. */
|
|
73
|
+
reset?(): void;
|
|
56
74
|
}
|
|
57
|
-
|
|
58
75
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* `nxe-select-box`, `nxe-tag-box`, `nxe-radio`, ...).
|
|
76
|
+
* An `nxe-validation-group` ancestor, exposed so a validator or a button inside it can join it
|
|
77
|
+
* without naming a group.
|
|
62
78
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* Concrete components MUST register themselves as the value accessor, e.g.:
|
|
68
|
-
* ```ts
|
|
69
|
-
* @Component({
|
|
70
|
-
* selector: 'nxe-text-box',
|
|
71
|
-
* providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NxeTextBoxComponent), multi: true }],
|
|
72
|
-
* ...
|
|
73
|
-
* })
|
|
74
|
-
* export class NxeTextBoxComponent extends NxeFormControlBase<string> { ... }
|
|
75
|
-
* ```
|
|
76
|
-
*
|
|
77
|
-
* Subclasses call {@link commitValue} on user interaction and {@link markAsTouched} on blur.
|
|
79
|
+
* DevExtreme lets a group be addressed two ways: by a string name shared between a validator and
|
|
80
|
+
* a button, or structurally by wrapping both in a `dxValidationGroup`. The string form is what
|
|
81
|
+
* the consuming applications use; this token is the structural one, and an explicit string
|
|
82
|
+
* always wins over it, as in dx.
|
|
78
83
|
*/
|
|
84
|
+
interface NxeValidationGroupScope {
|
|
85
|
+
/** Opaque, unique per instance — never collides with an application's own group names. */
|
|
86
|
+
readonly groupKey: string;
|
|
87
|
+
}
|
|
88
|
+
declare const NXE_VALIDATION_GROUP: InjectionToken<NxeValidationGroupScope>;
|
|
89
|
+
/** DevExtreme-shaped `onValidated` payload. */
|
|
90
|
+
interface NxeValidatedEvent extends NxeValidationResult {
|
|
91
|
+
value: unknown;
|
|
92
|
+
validationGroup: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
79
95
|
declare abstract class NxeFormControlBase<T> implements ControlValueAccessor, NxeValidationTarget {
|
|
80
96
|
protected readonly elementRef: ElementRef<HTMLElement>;
|
|
81
97
|
/**
|
|
@@ -99,6 +115,35 @@ declare abstract class NxeFormControlBase<T> implements ControlValueAccessor, Nx
|
|
|
99
115
|
readonly readOnly: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
100
116
|
/** Validation group this editor belongs to (its nested `nxe-validator` registers here). */
|
|
101
117
|
readonly validationGroupInput: _angular_core.InputSignal<string>;
|
|
118
|
+
/**
|
|
119
|
+
* `id` for the INNER control, not the host.
|
|
120
|
+
*
|
|
121
|
+
* A caption written as `<label for="x">` has to reach the focusable element, and the host of
|
|
122
|
+
* a custom element is not it. Without this an editor cannot be labelled at all, which is why
|
|
123
|
+
* `aria-label` sits beside it: one of the two has to be available.
|
|
124
|
+
*/
|
|
125
|
+
readonly inputId: _angular_core.InputSignal<string>;
|
|
126
|
+
readonly ariaLabel: _angular_core.InputSignal<string>;
|
|
127
|
+
readonly ariaLabelledBy: _angular_core.InputSignal<string>;
|
|
128
|
+
/** Stable per instance, so the inner control always has an id something can point at. */
|
|
129
|
+
private readonly fallbackId;
|
|
130
|
+
readonly controlId: _angular_core.Signal<string>;
|
|
131
|
+
/**
|
|
132
|
+
* Ids for a popup listbox and its options.
|
|
133
|
+
*
|
|
134
|
+
* `aria-activedescendant` is how a combobox says which option is current while focus stays in
|
|
135
|
+
* the text field, and it can only point at an id — so the options need stable ones.
|
|
136
|
+
*/
|
|
137
|
+
panelId(): string;
|
|
138
|
+
optionId(index: number): string;
|
|
139
|
+
/**
|
|
140
|
+
* Whether a nested validator carries a `required` rule.
|
|
141
|
+
*
|
|
142
|
+
* Set by the validator rather than declared on the editor: the rule is where the requirement
|
|
143
|
+
* is actually stated, and duplicating it as an input would let the two disagree.
|
|
144
|
+
*/
|
|
145
|
+
readonly required: _angular_core.WritableSignal<boolean>;
|
|
146
|
+
setRequired(value: boolean): void;
|
|
102
147
|
/** The current validation error message (set by a nested `nxe-validator`), or null. */
|
|
103
148
|
readonly validationError: _angular_core.WritableSignal<string>;
|
|
104
149
|
/** Whether the control has been blurred at least once. */
|
|
@@ -107,6 +152,39 @@ declare abstract class NxeFormControlBase<T> implements ControlValueAccessor, Nx
|
|
|
107
152
|
readonly dirty: _angular_core.WritableSignal<boolean>;
|
|
108
153
|
/** DevExtreme-compatible value-change event. */
|
|
109
154
|
readonly onValueChanged: _angular_core.OutputEmitterRef<NxeValueChangedEvent<T>>;
|
|
155
|
+
/**
|
|
156
|
+
* dx's keyboard, focus and input events.
|
|
157
|
+
*
|
|
158
|
+
* Native events already reach the host, so these are not about capability — they are about
|
|
159
|
+
* the call site. A migrated `(onEnterKey)` handler should keep working without being
|
|
160
|
+
* rewritten as a `(keydown)` with a key check, and `onEnterKey` in particular has no native
|
|
161
|
+
* equivalent at all.
|
|
162
|
+
*/
|
|
163
|
+
readonly onKeyDown: _angular_core.OutputEmitterRef<NxeEditorEvent>;
|
|
164
|
+
readonly onKeyUp: _angular_core.OutputEmitterRef<NxeEditorEvent>;
|
|
165
|
+
readonly onEnterKey: _angular_core.OutputEmitterRef<NxeEditorEvent>;
|
|
166
|
+
readonly onInput: _angular_core.OutputEmitterRef<NxeEditorEvent>;
|
|
167
|
+
readonly onChange: _angular_core.OutputEmitterRef<NxeEditorEvent>;
|
|
168
|
+
readonly onFocusIn: _angular_core.OutputEmitterRef<NxeEditorEvent>;
|
|
169
|
+
readonly onFocusOut: _angular_core.OutputEmitterRef<NxeEditorEvent>;
|
|
170
|
+
/**
|
|
171
|
+
* When a typed value is committed: dx's `'change'` (on blur), `'input'` (per keystroke) or
|
|
172
|
+
* `'keyup'`.
|
|
173
|
+
*
|
|
174
|
+
* Honoured by the text editors. The number and date boxes stay on `'change'` whatever this
|
|
175
|
+
* says, because their displayed text is derived from the parsed value: committing mid-entry
|
|
176
|
+
* would rewrite the field while the user is still typing into it.
|
|
177
|
+
*/
|
|
178
|
+
readonly valueChangeEvent: _angular_core.InputSignal<"keyup" | "input" | "change">;
|
|
179
|
+
/** dx universal widget options. */
|
|
180
|
+
readonly hint: _angular_core.InputSignal<string>;
|
|
181
|
+
readonly width: _angular_core.InputSignal<string | number>;
|
|
182
|
+
readonly visible: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
183
|
+
readonly tabIndex: _angular_core.InputSignal<number>;
|
|
184
|
+
readonly elementAttr: _angular_core.InputSignal<Record<string, string>>;
|
|
185
|
+
protected readonly widthStyle: _angular_core.Signal<string>;
|
|
186
|
+
protected readonly elementAttrClass: _angular_core.Signal<string>;
|
|
187
|
+
protected readonly elementAttrId: _angular_core.Signal<string>;
|
|
110
188
|
private _onChange;
|
|
111
189
|
private _onTouched;
|
|
112
190
|
/** Host element (the DevExtreme event payload `element`). */
|
|
@@ -124,10 +202,24 @@ declare abstract class NxeFormControlBase<T> implements ControlValueAccessor, Nx
|
|
|
124
202
|
* DevExtreme-shaped `onValueChanged`. No-op when the value is unchanged.
|
|
125
203
|
*/
|
|
126
204
|
protected commitValue(next: T | null, event?: Event | null): void;
|
|
205
|
+
/**
|
|
206
|
+
* Re-emit a native event in dx's shape.
|
|
207
|
+
*
|
|
208
|
+
* `onEnterKey` is the one that earns its keep: it has no native equivalent, so every call
|
|
209
|
+
* site would otherwise carry its own key check.
|
|
210
|
+
*/
|
|
211
|
+
protected emitEditorEvent(kind: NxeEditorEventKind, event: Event): void;
|
|
212
|
+
/**
|
|
213
|
+
* Commit `next` if `valueChangeEvent` says this is the moment.
|
|
214
|
+
*
|
|
215
|
+
* Blur always commits, whatever the mode: a value the user typed and then left must not be
|
|
216
|
+
* discarded because the configured event never fired.
|
|
217
|
+
*/
|
|
218
|
+
protected commitOn(kind: 'input' | 'keyup' | 'change', next: T | null, event: Event): void;
|
|
127
219
|
/** Mark the control as touched (e.g. on blur), notifying Angular forms exactly once. */
|
|
128
220
|
protected markAsTouched(): void;
|
|
129
221
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeFormControlBase<any>, never>;
|
|
130
|
-
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NxeFormControlBase<any>, never, never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "disabledInput": { "alias": "disabled"; "required": false; "isSignal": true; }; "readOnly": { "alias": "readOnly"; "required": false; "isSignal": true; }; "validationGroupInput": { "alias": "validationGroup"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "onValueChanged": "onValueChanged"; }, never, never, true, never>;
|
|
222
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NxeFormControlBase<any>, never, never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "disabledInput": { "alias": "disabled"; "required": false; "isSignal": true; }; "readOnly": { "alias": "readOnly"; "required": false; "isSignal": true; }; "validationGroupInput": { "alias": "validationGroup"; "required": false; "isSignal": true; }; "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "aria-label"; "required": false; "isSignal": true; }; "ariaLabelledBy": { "alias": "aria-labelledby"; "required": false; "isSignal": true; }; "valueChangeEvent": { "alias": "valueChangeEvent"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "visible": { "alias": "visible"; "required": false; "isSignal": true; }; "tabIndex": { "alias": "tabIndex"; "required": false; "isSignal": true; }; "elementAttr": { "alias": "elementAttr"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "onValueChanged": "onValueChanged"; "onKeyDown": "onKeyDown"; "onKeyUp": "onKeyUp"; "onEnterKey": "onEnterKey"; "onInput": "onInput"; "onChange": "onChange"; "onFocusIn": "onFocusIn"; "onFocusOut": "onFocusOut"; }, never, never, true, never>;
|
|
131
223
|
}
|
|
132
224
|
|
|
133
225
|
/**
|
|
@@ -219,10 +311,12 @@ interface NxeDefaultOptions {
|
|
|
219
311
|
readonly dateBox?: {
|
|
220
312
|
displayFormat?: string;
|
|
221
313
|
placeholder?: string;
|
|
314
|
+
showClearButton?: boolean;
|
|
222
315
|
};
|
|
223
316
|
readonly textBox?: {
|
|
224
317
|
placeholder?: string;
|
|
225
318
|
hoverStateEnabled?: boolean;
|
|
319
|
+
showClearButton?: boolean;
|
|
226
320
|
};
|
|
227
321
|
readonly textArea?: {
|
|
228
322
|
placeholder?: string;
|
|
@@ -230,6 +324,7 @@ interface NxeDefaultOptions {
|
|
|
230
324
|
};
|
|
231
325
|
readonly numberBox?: {
|
|
232
326
|
format?: string;
|
|
327
|
+
showClearButton?: boolean;
|
|
233
328
|
};
|
|
234
329
|
readonly button?: {
|
|
235
330
|
focusStateEnabled?: boolean;
|
|
@@ -306,6 +401,24 @@ declare function coerceArray<T>(value: T | readonly T[] | null | undefined): T[]
|
|
|
306
401
|
*/
|
|
307
402
|
declare function nxeOptionalBoolean(value: unknown): boolean | undefined;
|
|
308
403
|
|
|
404
|
+
/**
|
|
405
|
+
* DevExtreme's `displayExpr` / `valueExpr` / `searchExpr` accessor.
|
|
406
|
+
*
|
|
407
|
+
* dx accepts four spellings and the library accepted one — a flat property name — so a dotted
|
|
408
|
+
* path silently read `item['user.name']` and returned undefined. Silent, because an accessor
|
|
409
|
+
* that misses looks exactly like a field that is empty.
|
|
410
|
+
*
|
|
411
|
+
* undefined / '' the item itself (dx: no expression)
|
|
412
|
+
* 'this' the item itself (dx's documented sentinel)
|
|
413
|
+
* 'user.name' a dotted path
|
|
414
|
+
* (item) => ... a function accessor
|
|
415
|
+
*/
|
|
416
|
+
type NxeExpr<T = unknown> = string | ((item: T) => unknown) | undefined | null;
|
|
417
|
+
/** Resolve `expr` against `item`. */
|
|
418
|
+
declare function nxeResolveExpr<T = unknown>(expr: NxeExpr<T>, item: T): unknown;
|
|
419
|
+
/** Resolve `expr` and render it as display text: `null`/`undefined` become the empty string. */
|
|
420
|
+
declare function nxeResolveText<T = unknown>(expr: NxeExpr<T>, item: T): string;
|
|
421
|
+
|
|
309
422
|
/**
|
|
310
423
|
* Root registry mapping a `validationGroup` string to the `nxe-validator`s in it, mirroring
|
|
311
424
|
* DevExtreme's global ValidationEngine.
|
|
@@ -318,11 +431,18 @@ declare class NxeValidationGroupRegistry {
|
|
|
318
431
|
private readonly groups;
|
|
319
432
|
register(group: string, member: NxeValidationGroupMember): void;
|
|
320
433
|
unregister(group: string, member: NxeValidationGroupMember): void;
|
|
434
|
+
/**
|
|
435
|
+
* Clear every member's error state without validating.
|
|
436
|
+
*
|
|
437
|
+
* dx's `ValidationGroup.reset()`. The distinction from `validate()` matters after a Cancel:
|
|
438
|
+
* the fields should stop showing errors, not be re-checked and light up again.
|
|
439
|
+
*/
|
|
440
|
+
reset(group: string): void;
|
|
321
441
|
/** Validate every member of the group; valid (and empty) groups return `isValid: true`. */
|
|
322
442
|
validate(group: string): NxeValidationResult;
|
|
323
443
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeValidationGroupRegistry, never>;
|
|
324
444
|
static ɵprov: _angular_core.ɵɵInjectableDeclaration<NxeValidationGroupRegistry>;
|
|
325
445
|
}
|
|
326
446
|
|
|
327
|
-
export { NXE_DEFAULT_OPTIONS, NXE_OPTION, NXE_VALIDATION_TARGET, NxeConfigOptionBase, NxeFormControlBase, NxeTemplateDirective, NxeValidationGroupRegistry, coerceArray, nxeOptionalBoolean };
|
|
328
|
-
export type { NxeBrokenRule, NxeConfigOption, NxeDefaultOptions, NxeGridDefaultOptions, NxePickerDefaultOptions, NxeValidationGroupMember, NxeValidationResult, NxeValidationTarget, NxeValueChangedEvent };
|
|
447
|
+
export { NXE_DEFAULT_OPTIONS, NXE_OPTION, NXE_VALIDATION_GROUP, NXE_VALIDATION_TARGET, NxeConfigOptionBase, NxeFormControlBase, NxeTemplateDirective, NxeValidationGroupRegistry, coerceArray, nxeOptionalBoolean, nxeResolveExpr, nxeResolveText };
|
|
448
|
+
export type { NxeBrokenRule, NxeConfigOption, NxeDefaultOptions, NxeEditorEvent, NxeEditorEventKind, NxeExpr, NxeGridDefaultOptions, NxePickerDefaultOptions, NxeValidatedEvent, NxeValidationGroupMember, NxeValidationGroupScope, NxeValidationResult, NxeValidationTarget, NxeValueChangedEvent };
|