@vialiq/flux-ui 0.11.0 → 0.13.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/components/_index.scss +2 -0
- package/components/_modal.scss +217 -0
- package/components/_switch.scss +126 -0
- package/components/_tooltip.scss +1 -1
- package/package.json +1 -1
- package/styles/_variables.scss +23 -0
package/components/_index.scss
CHANGED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modal component base styles.
|
|
3
|
+
*
|
|
4
|
+
* Shared foundation styles for the modal component.
|
|
5
|
+
* Provides styling for the native <dialog> element, backdrop,
|
|
6
|
+
* layout variants (default, drawer, alert), and sizing.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
@use '../styles/variables' as tokens;
|
|
10
|
+
|
|
11
|
+
@layer components {
|
|
12
|
+
/* Core Dialog Element */
|
|
13
|
+
.modal-dialog {
|
|
14
|
+
/* Reset native dialog defaults */
|
|
15
|
+
border: none;
|
|
16
|
+
color: inherit;
|
|
17
|
+
display: none; // native behavior handles display:block when open
|
|
18
|
+
|
|
19
|
+
background-color: tokens.$modal-bg;
|
|
20
|
+
border-radius: tokens.$modal-border-radius;
|
|
21
|
+
box-shadow: tokens.$modal-shadow;
|
|
22
|
+
padding: 0; // handled by internal slots
|
|
23
|
+
|
|
24
|
+
// Default positioning/sizing (override in variants)
|
|
25
|
+
margin: auto;
|
|
26
|
+
width: 100%;
|
|
27
|
+
max-width: tokens.$modal-max-width-md;
|
|
28
|
+
max-height: 90vh; // Fallback max-height
|
|
29
|
+
|
|
30
|
+
z-index: tokens.$modal-z-index;
|
|
31
|
+
|
|
32
|
+
font-family: tokens.$font-family-base;
|
|
33
|
+
font-size: tokens.$font-size-base;
|
|
34
|
+
line-height: tokens.$line-height-normal;
|
|
35
|
+
color: tokens.$text-primary;
|
|
36
|
+
|
|
37
|
+
&[open] {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
animation: vi-modal-enter tokens.$modal-animation-duration ease-out;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
&::backdrop {
|
|
44
|
+
background-color: tokens.$modal-backdrop-bg;
|
|
45
|
+
// Optional: add backdrop animation if desired in future
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Structural Areas */
|
|
50
|
+
.modal-header {
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: space-between;
|
|
54
|
+
padding: tokens.$modal-header-padding;
|
|
55
|
+
flex-shrink: 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.modal-title {
|
|
59
|
+
font-size: tokens.$font-size-lg;
|
|
60
|
+
font-weight: tokens.$font-weight-semibold;
|
|
61
|
+
margin: 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.modal-header-actions {
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
gap: tokens.$spacing-xs;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.modal-body {
|
|
71
|
+
padding: tokens.$modal-body-padding;
|
|
72
|
+
// By default body stretches but can scroll if constrained by max-height
|
|
73
|
+
flex-grow: 1;
|
|
74
|
+
overflow-y: auto;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.modal-footer {
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
justify-content: flex-end;
|
|
81
|
+
gap: tokens.$spacing-sm;
|
|
82
|
+
padding: tokens.$modal-footer-padding;
|
|
83
|
+
background-color: tokens.$modal-footer-bg;
|
|
84
|
+
border-top: 1px solid tokens.$modal-footer-border-color;
|
|
85
|
+
flex-shrink: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Variants */
|
|
89
|
+
.modal-variant-drawer {
|
|
90
|
+
margin: 0;
|
|
91
|
+
max-height: none;
|
|
92
|
+
height: 100vh;
|
|
93
|
+
height: 100dvh; // modern viewport support
|
|
94
|
+
max-width: none;
|
|
95
|
+
width: tokens.$modal-drawer-width;
|
|
96
|
+
border-radius: 0;
|
|
97
|
+
|
|
98
|
+
&.placement-right {
|
|
99
|
+
margin-left: auto;
|
|
100
|
+
|
|
101
|
+
&[open] {
|
|
102
|
+
animation: vi-modal-drawer-enter-right tokens.$modal-animation-duration ease-out;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
&.placement-left {
|
|
107
|
+
margin-right: auto;
|
|
108
|
+
|
|
109
|
+
&[open] {
|
|
110
|
+
animation: vi-modal-drawer-enter-left tokens.$modal-animation-duration ease-out;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.modal-variant-alert {
|
|
116
|
+
flex-direction: row; // icon alongside content
|
|
117
|
+
padding: tokens.$modal-body-padding; // alerts might not use standard header/footer
|
|
118
|
+
max-width: tokens.$modal-max-width-sm;
|
|
119
|
+
|
|
120
|
+
// The alert content structure is slightly different
|
|
121
|
+
.modal-alert-icon {
|
|
122
|
+
flex-shrink: 0;
|
|
123
|
+
margin-right: tokens.$spacing-md;
|
|
124
|
+
display: flex;
|
|
125
|
+
align-items: flex-start;
|
|
126
|
+
padding-top: 2px; // align with text
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.modal-alert-content {
|
|
130
|
+
flex-grow: 1;
|
|
131
|
+
display: flex;
|
|
132
|
+
flex-direction: column;
|
|
133
|
+
|
|
134
|
+
.modal-header {
|
|
135
|
+
padding: 0;
|
|
136
|
+
margin-bottom: tokens.$spacing-xs;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.modal-body {
|
|
140
|
+
padding: 0;
|
|
141
|
+
margin-bottom: tokens.$spacing-sm;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.modal-footer {
|
|
145
|
+
padding: 0;
|
|
146
|
+
background: transparent;
|
|
147
|
+
border-top: none;
|
|
148
|
+
margin-top: auto;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
&[open] {
|
|
153
|
+
animation: vi-modal-alert-enter tokens.$modal-animation-duration cubic-bezier(0.175, 0.885, 0.32, 1.275); // spring-like
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Sizes (Default Variant Only) */
|
|
158
|
+
.modal-size-xs { max-width: tokens.$modal-max-width-xs; }
|
|
159
|
+
.modal-size-sm { max-width: tokens.$modal-max-width-sm; }
|
|
160
|
+
.modal-size-md { max-width: tokens.$modal-max-width-md; }
|
|
161
|
+
.modal-size-lg { max-width: tokens.$modal-max-width-lg; }
|
|
162
|
+
.modal-size-xl { max-width: tokens.$modal-max-width-xl; }
|
|
163
|
+
|
|
164
|
+
.modal-size-fullscreen {
|
|
165
|
+
max-width: none;
|
|
166
|
+
width: 100vw;
|
|
167
|
+
width: 100dvw;
|
|
168
|
+
max-height: none;
|
|
169
|
+
height: 100vh;
|
|
170
|
+
height: 100dvh;
|
|
171
|
+
border-radius: 0;
|
|
172
|
+
margin: 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.modal-size-full-width {
|
|
176
|
+
max-width: none;
|
|
177
|
+
width: 100vw;
|
|
178
|
+
width: 100dvw;
|
|
179
|
+
border-radius: 0;
|
|
180
|
+
margin-left: 0;
|
|
181
|
+
margin-right: 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Scrollable constraint */
|
|
185
|
+
.modal-scrollable-false .modal-body {
|
|
186
|
+
overflow-y: visible; // Or hidden, depending on exact need. Usually it means the modal grows to fit content.
|
|
187
|
+
// If dialog has no max-height, it just grows.
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Animations */
|
|
191
|
+
@keyframes vi-modal-enter {
|
|
192
|
+
from { opacity: 0; transform: scale(0.96); }
|
|
193
|
+
to { opacity: 1; transform: scale(1); }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@keyframes vi-modal-drawer-enter-right {
|
|
197
|
+
from { transform: translateX(100%); }
|
|
198
|
+
to { transform: translateX(0); }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@keyframes vi-modal-drawer-enter-left {
|
|
202
|
+
from { transform: translateX(-100%); }
|
|
203
|
+
to { transform: translateX(0); }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@keyframes vi-modal-alert-enter {
|
|
207
|
+
from { opacity: 0; transform: scale(0.8); }
|
|
208
|
+
to { opacity: 1; transform: scale(1); }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* Reduced Motion */
|
|
212
|
+
@media (prefers-reduced-motion: reduce) {
|
|
213
|
+
.modal-dialog[open] {
|
|
214
|
+
animation: none !important;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
@use '../styles/variables' as tokens;
|
|
2
|
+
|
|
3
|
+
@layer components {
|
|
4
|
+
// ── Wrapper ──────────────────────────────────────────────────────────────────
|
|
5
|
+
// Uses flex-start so the track top-aligns with the first line when the label
|
|
6
|
+
// wraps across multiple lines. The track itself is nudged down by half the
|
|
7
|
+
// difference between one line-height and its own height so it optically sits
|
|
8
|
+
// on the cap-height of the first text line.
|
|
9
|
+
|
|
10
|
+
.switch-wrapper {
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
align-items: flex-start;
|
|
13
|
+
gap: var(--vi-switch-label-gap, 8px);
|
|
14
|
+
cursor: pointer;
|
|
15
|
+
font-family: #{tokens.$font-family-base};
|
|
16
|
+
font-size: var(--vi-switch-label-font-size, #{tokens.$font-size-base});
|
|
17
|
+
line-height: #{tokens.$line-height-normal};
|
|
18
|
+
color: var(--vi-switch-label-color, #{tokens.$text-primary});
|
|
19
|
+
user-select: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ── Label text ────────────────────────────────────────────────────────────────
|
|
23
|
+
// flex: 1 lets it grow past the fixed-width track; min-width: 0 is required on
|
|
24
|
+
// a flex child so that word-break / overflow-wrap actually take effect.
|
|
25
|
+
|
|
26
|
+
.switch-label {
|
|
27
|
+
flex: 1 1 auto;
|
|
28
|
+
min-width: 0;
|
|
29
|
+
word-break: break-word;
|
|
30
|
+
overflow-wrap: break-word;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── Hidden native input ────────────────────────────────────────────────────────
|
|
34
|
+
// Visually hidden but still a full keyboard / AT participant.
|
|
35
|
+
|
|
36
|
+
.switch-input {
|
|
37
|
+
position: absolute !important;
|
|
38
|
+
clip-path: inset(50%) !important;
|
|
39
|
+
overflow: hidden !important;
|
|
40
|
+
width: 1px !important;
|
|
41
|
+
height: 1px !important;
|
|
42
|
+
margin: -1px !important;
|
|
43
|
+
padding: 0 !important;
|
|
44
|
+
border: 0 !important;
|
|
45
|
+
white-space: nowrap !important;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ── Track ─────────────────────────────────────────────────────────────────────
|
|
49
|
+
// margin-top offsets the track downward so its visual centre aligns with the
|
|
50
|
+
// cap-height of the first text line.
|
|
51
|
+
|
|
52
|
+
.switch-track {
|
|
53
|
+
box-sizing: border-box;
|
|
54
|
+
position: relative;
|
|
55
|
+
width: var(--vi-switch-track-width, 44px);
|
|
56
|
+
height: var(--vi-switch-track-height, 24px);
|
|
57
|
+
background-color: var(--vi-switch-track-color-off, #{tokens.$border-03});
|
|
58
|
+
border-radius: var(--vi-border-radius-full, 9999px);
|
|
59
|
+
transition: background-color var(--vi-switch-transition-duration, 150ms) ease;
|
|
60
|
+
flex-shrink: 0;
|
|
61
|
+
// Align track centre with first-line cap-height
|
|
62
|
+
margin-top: calc(
|
|
63
|
+
(1em * #{tokens.$line-height-normal} - var(--vi-switch-track-height, 24px)) / 2
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── Thumb ─────────────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
.switch-thumb {
|
|
70
|
+
position: absolute;
|
|
71
|
+
top: 50%;
|
|
72
|
+
left: var(--vi-switch-thumb-offset, 2px);
|
|
73
|
+
transform: translateY(-50%);
|
|
74
|
+
width: var(--vi-switch-thumb-size, 18px);
|
|
75
|
+
height: var(--vi-switch-thumb-size, 18px);
|
|
76
|
+
background-color: var(--vi-switch-thumb-color, #{tokens.$text-primary-inverse});
|
|
77
|
+
border-radius: 50%;
|
|
78
|
+
box-shadow: var(--vi-switch-thumb-shadow, 0 1px 3px rgba(0, 0, 0, 0.2));
|
|
79
|
+
transition: left var(--vi-switch-transition-duration, 150ms) ease;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Checked state ─────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
.switch-input:checked ~ .switch-track {
|
|
85
|
+
background-color: var(--vi-switch-track-color-on, #{tokens.$color-primary});
|
|
86
|
+
|
|
87
|
+
.switch-thumb {
|
|
88
|
+
left: calc(
|
|
89
|
+
100% - var(--vi-switch-thumb-size, 18px) - var(--vi-switch-thumb-offset, 2px)
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Focus ring ────────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
.switch-input:focus-visible ~ .switch-track {
|
|
97
|
+
outline: #{tokens.$border-width-base} solid
|
|
98
|
+
var(--vi-switch-focus-ring-color, #{tokens.$focus});
|
|
99
|
+
outline-offset: 2px;
|
|
100
|
+
box-shadow: 0 0 0 3px var(--vi-switch-focus-ring-glow, #{tokens.$color-blue-200});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ── Disabled state ────────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
.switch-wrapper--disabled {
|
|
106
|
+
cursor: not-allowed;
|
|
107
|
+
opacity: var(--vi-switch-disabled-opacity, 0.5);
|
|
108
|
+
|
|
109
|
+
.switch-track {
|
|
110
|
+
background-color: #{tokens.$border-03};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.switch-input:checked ~ .switch-track {
|
|
114
|
+
background-color: #{tokens.$text-disabled};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── Reduced motion ────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
@media (prefers-reduced-motion: reduce) {
|
|
121
|
+
.switch-track,
|
|
122
|
+
.switch-thumb {
|
|
123
|
+
transition: none;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
package/components/_tooltip.scss
CHANGED
|
@@ -7,7 +7,7 @@ $vi-tooltip-border-radius: var(--vi-tooltip-border-radius, 4px);
|
|
|
7
7
|
$vi-tooltip-padding: var(--vi-tooltip-padding, 6px 10px);
|
|
8
8
|
$vi-tooltip-max-width: var(--vi-tooltip-max-width, 240px);
|
|
9
9
|
$vi-tooltip-arrow-size: var(--vi-tooltip-arrow-size, 6px);
|
|
10
|
-
$vi-tooltip-z-index: var(--vi-tooltip-z-index,
|
|
10
|
+
$vi-tooltip-z-index: var(--vi-tooltip-z-index, tokens.$tooltip-z-index);
|
|
11
11
|
$vi-tooltip-shadow: var(--vi-tooltip-shadow, tokens.$shadow-md);
|
|
12
12
|
|
|
13
13
|
.tooltip-panel {
|
package/package.json
CHANGED
package/styles/_variables.scss
CHANGED
|
@@ -395,3 +395,26 @@ $z-index-map: (
|
|
|
395
395
|
'popover': $z-index-popover,
|
|
396
396
|
'tooltip': $z-index-tooltip,
|
|
397
397
|
);
|
|
398
|
+
|
|
399
|
+
// -- Component: Modal --------------------------------------------------------
|
|
400
|
+
|
|
401
|
+
$modal-bg: var(--vi-modal-bg, $color-background);
|
|
402
|
+
$modal-border-radius: var(--vi-modal-border-radius, $border-radius-lg);
|
|
403
|
+
$modal-shadow: var(--vi-modal-shadow, $shadow-xl);
|
|
404
|
+
$modal-header-padding: var(--vi-modal-header-padding, 20px 24px);
|
|
405
|
+
$modal-body-padding: var(--vi-modal-body-padding, 24px);
|
|
406
|
+
$modal-footer-padding: var(--vi-modal-footer-padding, 16px 24px);
|
|
407
|
+
$modal-footer-bg: var(--vi-modal-footer-bg, $layer-02);
|
|
408
|
+
$modal-footer-border-color: var(--vi-modal-footer-border-color, $border-02);
|
|
409
|
+
$modal-backdrop-bg: var(--vi-modal-backdrop-bg, rgba(0, 0, 0, 0.5));
|
|
410
|
+
$modal-drawer-width: var(--vi-modal-drawer-width, 480px);
|
|
411
|
+
$modal-z-index: var(--vi-modal-z-index, $z-index-modal);
|
|
412
|
+
$tooltip-z-index: var(--vi-tooltip-z-index, $z-index-tooltip);
|
|
413
|
+
$alert-floating-z-index: var(--vi-alert-floating-z-index, 1080);
|
|
414
|
+
$modal-animation-duration: var(--vi-modal-animation-duration, 200ms);
|
|
415
|
+
|
|
416
|
+
$modal-max-width-xs: var(--vi-modal-max-width-xs, 320px);
|
|
417
|
+
$modal-max-width-sm: var(--vi-modal-max-width-sm, 480px);
|
|
418
|
+
$modal-max-width-md: var(--vi-modal-max-width-md, 640px);
|
|
419
|
+
$modal-max-width-lg: var(--vi-modal-max-width-lg, 800px);
|
|
420
|
+
$modal-max-width-xl: var(--vi-modal-max-width-xl, 960px);
|