@justin_evo/evo-ui 1.0.2 → 1.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/LICENSE +21 -0
- package/README.md +70 -70
- package/dist/Nav/Nav.d.ts +15 -0
- package/dist/TopNav/TopNav.d.ts +19 -0
- package/dist/evo-ui.css +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +3300 -3157
- package/package.json +1 -1
- package/src/Nav/Nav.tsx +96 -14
- package/src/RichTextArea/RichTextArea.tsx +20 -3
- package/src/TopNav/TopNav.tsx +169 -0
- package/src/css/checkbox.module.scss +8 -5
- package/src/css/nav.module.scss +170 -15
- package/src/css/topnav.module.scss +172 -0
package/src/css/nav.module.scss
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
color: $color-text-secondary;
|
|
57
57
|
background: transparent;
|
|
58
58
|
border: none;
|
|
59
|
-
border-radius: $radius
|
|
59
|
+
border-radius: $evo-border-radius;
|
|
60
60
|
cursor: pointer;
|
|
61
61
|
text-align: left;
|
|
62
62
|
text-decoration: none;
|
|
@@ -70,6 +70,11 @@
|
|
|
70
70
|
&:hover {
|
|
71
71
|
background-color: $color-surface-hover;
|
|
72
72
|
color: $color-text-primary;
|
|
73
|
+
|
|
74
|
+
.navIcon {
|
|
75
|
+
color: $color-text-primary;
|
|
76
|
+
transform: translateX(1px);
|
|
77
|
+
}
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
&:focus-visible {
|
|
@@ -80,16 +85,10 @@
|
|
|
80
85
|
&.active {
|
|
81
86
|
background-color: $evo-primary-soft;
|
|
82
87
|
color: $evo-primary-color;
|
|
88
|
+
font-weight: 600;
|
|
83
89
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
position: absolute;
|
|
87
|
-
left: 0;
|
|
88
|
-
top: 25%;
|
|
89
|
-
bottom: 25%;
|
|
90
|
-
width: 2px;
|
|
91
|
-
border-radius: $radius-full;
|
|
92
|
-
background: $evo-primary-color;
|
|
90
|
+
.navIcon {
|
|
91
|
+
color: $evo-primary-color;
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
94
|
|
|
@@ -121,6 +120,20 @@
|
|
|
121
120
|
overflow: hidden;
|
|
122
121
|
text-overflow: ellipsis;
|
|
123
122
|
white-space: nowrap;
|
|
123
|
+
clip-path: inset(0 0 0 0);
|
|
124
|
+
transition:
|
|
125
|
+
opacity 200ms ease,
|
|
126
|
+
transform 220ms ease,
|
|
127
|
+
clip-path 260ms ease;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// When the rail expands, labels reveal top-to-bottom (per-row delay) and
|
|
131
|
+
// left-to-right (clip-path sweep). Only opacity/transform/clip-path transition,
|
|
132
|
+
// so the delay is invisible in the steady expanded state.
|
|
133
|
+
@for $i from 1 through 12 {
|
|
134
|
+
.navList .navLi:nth-child(#{$i}) .navLabel {
|
|
135
|
+
transition-delay: #{($i - 1) * 14}ms;
|
|
136
|
+
}
|
|
124
137
|
}
|
|
125
138
|
|
|
126
139
|
.navIcon {
|
|
@@ -132,6 +145,15 @@
|
|
|
132
145
|
height: 1.125rem;
|
|
133
146
|
font-size: 1rem;
|
|
134
147
|
line-height: 1;
|
|
148
|
+
color: $color-text-muted;
|
|
149
|
+
transition:
|
|
150
|
+
color $transition-fast,
|
|
151
|
+
transform $transition-fast;
|
|
152
|
+
|
|
153
|
+
svg {
|
|
154
|
+
width: 1.05rem;
|
|
155
|
+
height: 1.05rem;
|
|
156
|
+
}
|
|
135
157
|
}
|
|
136
158
|
|
|
137
159
|
.chevron {
|
|
@@ -161,18 +183,96 @@
|
|
|
161
183
|
|
|
162
184
|
.navGroup {
|
|
163
185
|
list-style: none;
|
|
164
|
-
margin: 0.
|
|
186
|
+
margin: 0.85rem 0 0;
|
|
165
187
|
padding: 0;
|
|
188
|
+
|
|
189
|
+
&:first-child {
|
|
190
|
+
margin-top: 0.25rem;
|
|
191
|
+
}
|
|
166
192
|
}
|
|
167
193
|
|
|
168
194
|
.navGroupLabel {
|
|
169
|
-
display:
|
|
195
|
+
display: flex;
|
|
196
|
+
align-items: center;
|
|
197
|
+
width: 100%;
|
|
170
198
|
font-size: $text-xs;
|
|
171
|
-
font-weight:
|
|
199
|
+
font-weight: 700;
|
|
172
200
|
text-transform: uppercase;
|
|
173
|
-
letter-spacing: 0.
|
|
201
|
+
letter-spacing: 0.08em;
|
|
174
202
|
color: $color-text-muted;
|
|
175
|
-
padding: 0.
|
|
203
|
+
padding: 0.4rem 0.75rem 0.3rem;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.navGroupLabelText {
|
|
207
|
+
flex: 1;
|
|
208
|
+
min-width: 0;
|
|
209
|
+
text-align: left;
|
|
210
|
+
overflow: hidden;
|
|
211
|
+
text-overflow: ellipsis;
|
|
212
|
+
white-space: nowrap;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Collapsible group heading — same look as the static label, plus button chrome.
|
|
216
|
+
.navGroupToggle {
|
|
217
|
+
background: transparent;
|
|
218
|
+
border: none;
|
|
219
|
+
border-radius: $radius-sm;
|
|
220
|
+
cursor: pointer;
|
|
221
|
+
font-family: inherit;
|
|
222
|
+
transition: color $transition-fast;
|
|
223
|
+
|
|
224
|
+
&:hover {
|
|
225
|
+
color: $color-text-secondary;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
&:focus-visible {
|
|
229
|
+
outline: $evo-btn-outline-width solid $evo-primary-focus;
|
|
230
|
+
outline-offset: $evo-btn-outline-offset;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.chevron {
|
|
234
|
+
margin-left: 0.4rem;
|
|
235
|
+
color: $color-text-muted;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.navGroupCount {
|
|
240
|
+
display: inline-flex;
|
|
241
|
+
align-items: center;
|
|
242
|
+
justify-content: center;
|
|
243
|
+
min-width: 1.15rem;
|
|
244
|
+
height: 1.15rem;
|
|
245
|
+
padding: 0 0.35rem;
|
|
246
|
+
margin-left: 0.4rem;
|
|
247
|
+
font-size: 0.65rem;
|
|
248
|
+
font-weight: 600;
|
|
249
|
+
letter-spacing: 0;
|
|
250
|
+
color: $color-text-secondary;
|
|
251
|
+
background: $color-surface-hover;
|
|
252
|
+
border-radius: $radius-full;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Accordion panel — animates height via grid-template-rows (0fr <-> 1fr), no
|
|
256
|
+
// JS measuring. The inner list must clip its overflow for the collapse to read.
|
|
257
|
+
.navGroupPanel {
|
|
258
|
+
display: grid;
|
|
259
|
+
grid-template-rows: 1fr;
|
|
260
|
+
transition: grid-template-rows 260ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
261
|
+
|
|
262
|
+
&[data-open='false'] {
|
|
263
|
+
grid-template-rows: 0fr;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
> .navList {
|
|
267
|
+
min-height: 0;
|
|
268
|
+
overflow: hidden;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Rows inside the clipped panel draw their focus ring INSET, so the
|
|
272
|
+
// overflow:hidden needed for the height animation can't clip it.
|
|
273
|
+
.navRow:focus-visible {
|
|
274
|
+
outline-offset: -2px;
|
|
275
|
+
}
|
|
176
276
|
}
|
|
177
277
|
|
|
178
278
|
// ---------- Skeleton ----------
|
|
@@ -321,10 +421,65 @@
|
|
|
321
421
|
to { opacity: 1; }
|
|
322
422
|
}
|
|
323
423
|
|
|
424
|
+
// ---------- Collapsed (icon-only rail) ----------
|
|
425
|
+
|
|
426
|
+
.navCollapsed {
|
|
427
|
+
.navRow,
|
|
428
|
+
.navQuickAction {
|
|
429
|
+
justify-content: center;
|
|
430
|
+
gap: 0;
|
|
431
|
+
padding-left: 0.5rem;
|
|
432
|
+
padding-right: 0.5rem;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.navLabel {
|
|
436
|
+
flex: 0;
|
|
437
|
+
opacity: 0;
|
|
438
|
+
transform: translateX(-4px);
|
|
439
|
+
clip-path: inset(0 100% 0 0);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Group heading text stays in the a11y tree (it names the heading) but is
|
|
443
|
+
// visually hidden; the decorative count chip is removed entirely.
|
|
444
|
+
.navGroupLabelText {
|
|
445
|
+
position: absolute;
|
|
446
|
+
width: 1px;
|
|
447
|
+
height: 1px;
|
|
448
|
+
padding: 0;
|
|
449
|
+
margin: -1px;
|
|
450
|
+
overflow: hidden;
|
|
451
|
+
clip-path: inset(50%);
|
|
452
|
+
white-space: nowrap;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.navGroupCount {
|
|
456
|
+
display: none;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.navGroupLabel {
|
|
460
|
+
min-height: 0.5rem;
|
|
461
|
+
padding: 0.25rem 0;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Accordion is meaningless in rail mode — force panels open.
|
|
465
|
+
.navGroupPanel {
|
|
466
|
+
grid-template-rows: 1fr;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Collapsing fades labels out together — outrank the per-row expand stagger.
|
|
471
|
+
.navCollapsed .navList .navLi .navLabel {
|
|
472
|
+
transition-delay: 0ms;
|
|
473
|
+
}
|
|
474
|
+
|
|
324
475
|
// ---------- Reduced motion ----------
|
|
325
476
|
|
|
326
477
|
@media (prefers-reduced-motion: reduce) {
|
|
327
478
|
.navRow,
|
|
479
|
+
.navRow .navIcon,
|
|
480
|
+
.navLabel,
|
|
481
|
+
.navGroupPanel,
|
|
482
|
+
.navGroupToggle,
|
|
328
483
|
.navQuickAction,
|
|
329
484
|
.chevron,
|
|
330
485
|
.navTrigger,
|
|
@@ -20,6 +20,16 @@
|
|
|
20
20
|
to { opacity: 1; transform: translateY(0); }
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
@keyframes topNavRise {
|
|
24
|
+
from { opacity: 0; transform: translateY(0.5rem); }
|
|
25
|
+
to { opacity: 1; transform: translateY(0); }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@keyframes topNavFade {
|
|
29
|
+
from { opacity: 0; }
|
|
30
|
+
to { opacity: 1; }
|
|
31
|
+
}
|
|
32
|
+
|
|
23
33
|
// ---------------------------------------------------------------------------
|
|
24
34
|
// Root
|
|
25
35
|
// ---------------------------------------------------------------------------
|
|
@@ -159,6 +169,61 @@
|
|
|
159
169
|
flex-shrink: 0;
|
|
160
170
|
}
|
|
161
171
|
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Search trigger (EvoTopNav.Search) — presentational ⌘K affordance
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
.topNavSearch {
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
gap: 0.5rem;
|
|
180
|
+
height: 2rem;
|
|
181
|
+
min-width: 12rem;
|
|
182
|
+
padding: 0 0.625rem;
|
|
183
|
+
font-family: inherit;
|
|
184
|
+
font-size: $text-sm;
|
|
185
|
+
color: $color-text-muted;
|
|
186
|
+
background-color: $color-surface-sunken;
|
|
187
|
+
border: 1px solid $color-border;
|
|
188
|
+
border-radius: $radius-sm;
|
|
189
|
+
cursor: pointer;
|
|
190
|
+
transition:
|
|
191
|
+
background-color $transition-fast,
|
|
192
|
+
border-color $transition-fast,
|
|
193
|
+
color $transition-fast;
|
|
194
|
+
|
|
195
|
+
&:hover {
|
|
196
|
+
background-color: $color-surface-hover;
|
|
197
|
+
color: $color-text-secondary;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
&:focus-visible {
|
|
201
|
+
outline: 2px solid $evo-primary-focus;
|
|
202
|
+
outline-offset: 2px;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.topNavSearchIcon {
|
|
207
|
+
display: inline-flex;
|
|
208
|
+
flex-shrink: 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.topNavSearchText {
|
|
212
|
+
flex: 1;
|
|
213
|
+
text-align: left;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.topNavSearchKbd {
|
|
217
|
+
flex-shrink: 0;
|
|
218
|
+
font-family: inherit;
|
|
219
|
+
font-size: $text-xs;
|
|
220
|
+
color: $color-text-muted;
|
|
221
|
+
background-color: $color-surface;
|
|
222
|
+
border: 1px solid $color-border;
|
|
223
|
+
border-radius: 4px;
|
|
224
|
+
padding: 0.0625rem 0.3125rem;
|
|
225
|
+
}
|
|
226
|
+
|
|
162
227
|
// ---------------------------------------------------------------------------
|
|
163
228
|
// Toggle (hamburger) — hidden above the collapse breakpoint
|
|
164
229
|
// ---------------------------------------------------------------------------
|
|
@@ -360,6 +425,17 @@
|
|
|
360
425
|
z-index: 55;
|
|
361
426
|
animation: topNavOverlayFadeIn 180ms ease;
|
|
362
427
|
}
|
|
428
|
+
|
|
429
|
+
.topNavSearch {
|
|
430
|
+
min-width: 0;
|
|
431
|
+
width: 2.75rem;
|
|
432
|
+
height: 2.75rem;
|
|
433
|
+
justify-content: center;
|
|
434
|
+
padding: 0;
|
|
435
|
+
|
|
436
|
+
.topNavSearchText,
|
|
437
|
+
.topNavSearchKbd { display: none; }
|
|
438
|
+
}
|
|
363
439
|
}
|
|
364
440
|
|
|
365
441
|
// Above the breakpoint, drawer-specific bits never render visually.
|
|
@@ -371,6 +447,85 @@
|
|
|
371
447
|
}
|
|
372
448
|
}
|
|
373
449
|
|
|
450
|
+
// ---------------------------------------------------------------------------
|
|
451
|
+
// Entrance animation (opt-in via `entrance` prop → data-entrance attribute)
|
|
452
|
+
// ---------------------------------------------------------------------------
|
|
453
|
+
|
|
454
|
+
.topNav[data-entrance='rise'] .topNavBrand,
|
|
455
|
+
.topNav[data-entrance='rise'] .topNavMenu > li,
|
|
456
|
+
.topNav[data-entrance='rise'] .topNavSearch,
|
|
457
|
+
.topNav[data-entrance='rise'] .topNavActions {
|
|
458
|
+
animation: topNavRise 440ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.topNav[data-entrance='fade'] .topNavBrand,
|
|
462
|
+
.topNav[data-entrance='fade'] .topNavMenu > li,
|
|
463
|
+
.topNav[data-entrance='fade'] .topNavSearch,
|
|
464
|
+
.topNav[data-entrance='fade'] .topNavActions {
|
|
465
|
+
animation: topNavFade 440ms ease both;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Shared left-to-right stagger (applies to both variants).
|
|
469
|
+
.topNav[data-entrance] .topNavBrand { animation-delay: 40ms; }
|
|
470
|
+
.topNav[data-entrance] .topNavMenu > li:nth-child(1) { animation-delay: 110ms; }
|
|
471
|
+
.topNav[data-entrance] .topNavMenu > li:nth-child(2) { animation-delay: 160ms; }
|
|
472
|
+
.topNav[data-entrance] .topNavMenu > li:nth-child(3) { animation-delay: 210ms; }
|
|
473
|
+
.topNav[data-entrance] .topNavMenu > li:nth-child(4) { animation-delay: 260ms; }
|
|
474
|
+
.topNav[data-entrance] .topNavMenu > li:nth-child(n + 5) { animation-delay: 300ms; }
|
|
475
|
+
.topNav[data-entrance] .topNavSearch { animation-delay: 320ms; }
|
|
476
|
+
.topNav[data-entrance] .topNavActions { animation-delay: 360ms; }
|
|
477
|
+
|
|
478
|
+
// ---------------------------------------------------------------------------
|
|
479
|
+
// Sticky + scroll-aware behavior (opt-in via `sticky` / `scrollBehavior`)
|
|
480
|
+
// ---------------------------------------------------------------------------
|
|
481
|
+
|
|
482
|
+
.topNavSticky {
|
|
483
|
+
position: sticky;
|
|
484
|
+
top: 0;
|
|
485
|
+
z-index: 30;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.topNav[data-scroll] {
|
|
489
|
+
transition:
|
|
490
|
+
background-color $transition-fast,
|
|
491
|
+
box-shadow $transition-fast,
|
|
492
|
+
transform 220ms ease;
|
|
493
|
+
|
|
494
|
+
.topNavInner { transition: min-height $transition-fast; }
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.topNav[data-scrolled] {
|
|
498
|
+
background-color: $color-surface; // fallback for browsers without color-mix()
|
|
499
|
+
background-color: color-mix(in srgb, $color-surface 85%, transparent);
|
|
500
|
+
-webkit-backdrop-filter: blur(10px);
|
|
501
|
+
backdrop-filter: blur(10px);
|
|
502
|
+
box-shadow: $shadow-md;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.topNav[data-scroll='shrink'][data-scrolled] .topNavInner {
|
|
506
|
+
min-height: 2.75rem;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.topNav[data-scroll='hide'] { will-change: transform; }
|
|
510
|
+
.topNav[data-scroll='hide'][data-hidden] { transform: translateY(-100%); }
|
|
511
|
+
|
|
512
|
+
// ---------------------------------------------------------------------------
|
|
513
|
+
// Scroll-progress line (opt-in via `showProgress`)
|
|
514
|
+
// ---------------------------------------------------------------------------
|
|
515
|
+
|
|
516
|
+
.topNavProgress {
|
|
517
|
+
position: absolute;
|
|
518
|
+
left: 0;
|
|
519
|
+
bottom: 0;
|
|
520
|
+
width: 100%;
|
|
521
|
+
height: 2px;
|
|
522
|
+
background: $evo-primary-color;
|
|
523
|
+
transform: scaleX(var(--evo-topnav-progress, 0));
|
|
524
|
+
transform-origin: left center;
|
|
525
|
+
pointer-events: none;
|
|
526
|
+
z-index: 31;
|
|
527
|
+
}
|
|
528
|
+
|
|
374
529
|
// ---------------------------------------------------------------------------
|
|
375
530
|
// Reduced motion — kill animations
|
|
376
531
|
// ---------------------------------------------------------------------------
|
|
@@ -385,6 +540,16 @@
|
|
|
385
540
|
.topNavDropdownChevron {
|
|
386
541
|
transition: none;
|
|
387
542
|
}
|
|
543
|
+
|
|
544
|
+
.topNavBrand,
|
|
545
|
+
.topNavMenu > li,
|
|
546
|
+
.topNavSearch,
|
|
547
|
+
.topNavActions {
|
|
548
|
+
animation: none !important;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
&[data-scroll='hide'][data-hidden] { transition: none; }
|
|
552
|
+
.topNavProgress { transition: none; }
|
|
388
553
|
}
|
|
389
554
|
|
|
390
555
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -393,4 +558,11 @@
|
|
|
393
558
|
.topNavBackdrop {
|
|
394
559
|
animation: none;
|
|
395
560
|
}
|
|
561
|
+
|
|
562
|
+
.topNav[data-entrance] .topNavBrand,
|
|
563
|
+
.topNav[data-entrance] .topNavMenu > li,
|
|
564
|
+
.topNav[data-entrance] .topNavSearch,
|
|
565
|
+
.topNav[data-entrance] .topNavActions {
|
|
566
|
+
animation: none !important;
|
|
567
|
+
}
|
|
396
568
|
}
|