@yewdiel21/motion-kit 0.18.0 → 0.19.1
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/README.md +245 -222
- package/bin/motion-kit.mjs +0 -0
- package/dist/auto/index.d.ts +4 -3
- package/dist/auto/index.js +5 -3
- package/dist/auto/index.js.map +1 -1
- package/dist/{chunk-QFBOHPWZ.js → chunk-CQP66AZK.js} +2 -2
- package/dist/{chunk-QP4QJOEN.js → chunk-H4STLMCM.js} +2 -2
- package/dist/{chunk-AWDEKDEJ.js → chunk-LIXSOGS2.js} +8 -22
- package/dist/chunk-LIXSOGS2.js.map +1 -0
- package/dist/{chunk-Q7IW4FV3.js → chunk-NCGNIPJE.js} +1430 -968
- package/dist/chunk-NCGNIPJE.js.map +1 -0
- package/dist/{chunk-DOOMULVV.js → chunk-OLT3CWDJ.js} +4 -2
- package/dist/chunk-OLT3CWDJ.js.map +1 -0
- package/dist/{chunk-EDMXWUW3.js → chunk-PVN7YVKV.js} +3 -3
- package/dist/css/components/code-block.css +267 -0
- package/dist/css/components/command-search.css +274 -2
- package/dist/css/components/dialog.css +264 -0
- package/dist/css/components/heatmap-chart.css +267 -1
- package/dist/css/components/pill-button.css +7 -5
- package/dist/css/components/scroll-area.css +261 -0
- package/dist/css/components/scroll-island.css +6 -0
- package/dist/css/components/select.css +284 -4
- package/dist/elements/index.d.ts +15 -4
- package/dist/elements/index.js +36 -6
- package/dist/elements/index.js.map +1 -1
- package/dist/{engine-t8d7brjn.d.ts → engine-D6XV-WNt.d.ts} +50 -1
- package/dist/gl/index.js +2 -2
- package/dist/index.css +321 -12
- package/dist/index.d.ts +3 -3
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +20 -7
- package/dist/react/index.js +56 -7
- package/dist/react/index.js.map +1 -1
- package/dist/{reduced-dPsZYqtI.d.ts → reduced-4croIXl-.d.ts} +1 -1
- package/dist/{store-6hZNKiee.d.ts → store-CAHWkurn.d.ts} +1 -1
- package/package.json +117 -110
- package/registry.json +1357 -1340
- package/src/auto/auto-init.ts +341 -339
- package/src/components/code-block/README.md +1 -1
- package/src/components/code-block/code-block.css +5 -0
- package/src/components/code-block/engine.ts +5 -0
- package/src/components/command-search/README.md +1 -1
- package/src/components/command-search/command-search.css +12 -2
- package/src/components/command-search/engine.ts +6 -0
- package/src/components/dialog/README.md +104 -103
- package/src/components/dialog/dialog.css +2 -0
- package/src/components/dialog/engine.ts +465 -449
- package/src/components/heatmap-chart/README.md +48 -48
- package/src/components/heatmap-chart/engine.ts +6 -1
- package/src/components/heatmap-chart/heatmap-chart.css +5 -1
- package/src/components/icons.ts +1 -0
- package/src/components/pill-button/engine.ts +1 -1
- package/src/components/pill-button/pill-button.css +7 -5
- package/src/components/scroll-area/README.md +89 -0
- package/src/components/scroll-area/element.ts +35 -0
- package/src/components/scroll-area/engine.ts +528 -0
- package/src/components/scroll-area/react.tsx +67 -0
- package/src/components/scroll-area/scroll-area.css +260 -0
- package/src/components/scroll-island/engine.ts +10 -1
- package/src/components/scroll-island/scroll-island.css +6 -0
- package/src/components/select/README.md +1 -1
- package/src/components/select/engine.ts +16 -4
- package/src/components/select/select.css +22 -4
- package/src/core/timing.ts +2 -0
- package/src/elements/index.ts +321 -318
- package/src/index.ts +1 -0
- package/src/motion/morph.ts +5 -2
- package/src/react/index.ts +2 -1
- package/dist/chunk-AWDEKDEJ.js.map +0 -1
- package/dist/chunk-DOOMULVV.js.map +0 -1
- package/dist/chunk-Q7IW4FV3.js.map +0 -1
- /package/dist/{chunk-QFBOHPWZ.js.map → chunk-CQP66AZK.js.map} +0 -0
- /package/dist/{chunk-QP4QJOEN.js.map → chunk-H4STLMCM.js.map} +0 -0
- /package/dist/{chunk-EDMXWUW3.js.map → chunk-PVN7YVKV.js.map} +0 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/* scroll-area — the scroller keeps native scrolling with its system bar hidden; the kit's bars ride on top: a 6 px pill
|
|
2
|
+
(text at 32 %) that shows while hovered or scrolling, swells to 10 px over a tonal rail on the swing curve and turns the
|
|
3
|
+
accent while dragged. Edges with more content fade out; an optional tonal pill takes the view back to the top. */
|
|
4
|
+
@property --mk-scroll-thickness {
|
|
5
|
+
syntax: '<length>';
|
|
6
|
+
inherits: true;
|
|
7
|
+
initial-value: 6px;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.mk-scroll-area,
|
|
11
|
+
mk-scroll-area {
|
|
12
|
+
--mk-scroll-thumb: color-mix(in srgb, var(--mk-text) 32%, transparent);
|
|
13
|
+
--mk-scroll-thumb-hover: color-mix(in srgb, var(--mk-text) 48%, transparent);
|
|
14
|
+
--mk-scroll-rail: color-mix(in srgb, var(--mk-text) 6%, transparent);
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
min-width: 0;
|
|
18
|
+
min-height: 0;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Its own viewport fills the area (a height or a max-height on the area both work). */
|
|
23
|
+
:is(.mk-scroll-area, mk-scroll-area) > [data-mk-scroll-viewport] {
|
|
24
|
+
flex: 1 1 auto;
|
|
25
|
+
min-width: 0;
|
|
26
|
+
min-height: 0;
|
|
27
|
+
overflow: auto;
|
|
28
|
+
overscroll-behavior: contain;
|
|
29
|
+
border-radius: inherit;
|
|
30
|
+
outline: none;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:is(.mk-scroll-area, mk-scroll-area)[data-mk-scroll-axis='y'] > [data-mk-scroll-viewport] {
|
|
34
|
+
overflow-x: hidden;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
:is(.mk-scroll-area, mk-scroll-area)[data-mk-scroll-axis='x'] > [data-mk-scroll-viewport] {
|
|
38
|
+
overflow-y: hidden;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[data-mk-scroll-anchor] {
|
|
42
|
+
position: relative;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* The kit's bars replace the system's wherever the scroller's own styles asked for a thin one. */
|
|
46
|
+
[data-mk-scroll-viewport] {
|
|
47
|
+
scrollbar-width: none !important;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[data-mk-scroll-viewport]::-webkit-scrollbar {
|
|
51
|
+
display: none !important;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
[data-mk-scroll-area]:has(> [data-mk-scroll-viewport]:focus-visible) {
|
|
55
|
+
outline: 2px solid var(--mk-accent);
|
|
56
|
+
outline-offset: 2px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Edge fades: as deep as what is hidden past each edge, up to 24 px (written by the engine). */
|
|
60
|
+
[data-mk-fade] > [data-mk-scroll-viewport] {
|
|
61
|
+
--mk-scroll-fade-top: 0px;
|
|
62
|
+
--mk-scroll-fade-right: 0px;
|
|
63
|
+
--mk-scroll-fade-bottom: 0px;
|
|
64
|
+
--mk-scroll-fade-left: 0px;
|
|
65
|
+
-webkit-mask-image:
|
|
66
|
+
linear-gradient(to bottom, transparent, #000 var(--mk-scroll-fade-top), #000 calc(100% - var(--mk-scroll-fade-bottom)), transparent),
|
|
67
|
+
linear-gradient(to right, transparent, #000 var(--mk-scroll-fade-left), #000 calc(100% - var(--mk-scroll-fade-right)), transparent);
|
|
68
|
+
-webkit-mask-composite: source-in;
|
|
69
|
+
mask-image:
|
|
70
|
+
linear-gradient(to bottom, transparent, #000 var(--mk-scroll-fade-top), #000 calc(100% - var(--mk-scroll-fade-bottom)), transparent),
|
|
71
|
+
linear-gradient(to right, transparent, #000 var(--mk-scroll-fade-left), #000 calc(100% - var(--mk-scroll-fade-right)), transparent);
|
|
72
|
+
mask-composite: intersect;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Rails: a 14 px grip along the edge; its tonal rail and the pill inside are drawn smaller. */
|
|
76
|
+
[data-mk-scroll-rail] {
|
|
77
|
+
--mk-scroll-thickness: 6px;
|
|
78
|
+
position: absolute;
|
|
79
|
+
z-index: 2;
|
|
80
|
+
opacity: 0;
|
|
81
|
+
touch-action: none;
|
|
82
|
+
-webkit-user-select: none;
|
|
83
|
+
user-select: none;
|
|
84
|
+
transition:
|
|
85
|
+
opacity 0.2s ease,
|
|
86
|
+
--mk-scroll-thickness 0.35s var(--mk-ease-swatch);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
[data-mk-scroll-rail][hidden] {
|
|
90
|
+
display: none;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* The rails run along the viewport's box inside the root (--mk-scroll-view-*, written by the engine);
|
|
94
|
+
--mk-scroll-inset keeps their ends clear of rounded corners. */
|
|
95
|
+
[data-mk-scroll-rail][data-mk-axis='y'] {
|
|
96
|
+
top: calc(var(--mk-scroll-view-top, 0px) + var(--mk-scroll-inset, 6px));
|
|
97
|
+
right: var(--mk-scroll-view-right, 0px);
|
|
98
|
+
bottom: calc(var(--mk-scroll-view-bottom, 0px) + var(--mk-scroll-inset, 6px));
|
|
99
|
+
width: 14px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[data-mk-scroll-rail][data-mk-axis='x'] {
|
|
103
|
+
right: calc(var(--mk-scroll-view-right, 0px) + var(--mk-scroll-inset, 6px));
|
|
104
|
+
bottom: var(--mk-scroll-view-bottom, 0px);
|
|
105
|
+
left: calc(var(--mk-scroll-view-left, 0px) + var(--mk-scroll-inset, 6px));
|
|
106
|
+
height: 14px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Both bars: each stops short of the other, no corner square. */
|
|
110
|
+
[data-mk-overflow-x] > [data-mk-scroll-rail][data-mk-axis='y'] {
|
|
111
|
+
bottom: calc(var(--mk-scroll-view-bottom, 0px) + 14px);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
[data-mk-overflow-y] > [data-mk-scroll-rail][data-mk-axis='x'] {
|
|
115
|
+
right: calc(var(--mk-scroll-view-right, 0px) + 14px);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
[data-mk-active] > [data-mk-scroll-rail],
|
|
119
|
+
[data-mk-scroll-rail]:hover,
|
|
120
|
+
[data-mk-scroll-rail][data-mk-dragging] {
|
|
121
|
+
opacity: 1;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
[data-mk-scroll-rail]:hover,
|
|
125
|
+
[data-mk-scroll-rail][data-mk-dragging] {
|
|
126
|
+
--mk-scroll-thickness: 10px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
[data-mk-scroll-rail]::before {
|
|
130
|
+
content: '';
|
|
131
|
+
position: absolute;
|
|
132
|
+
border-radius: 999px;
|
|
133
|
+
background: var(--mk-scroll-rail, color-mix(in srgb, var(--mk-text) 6%, transparent));
|
|
134
|
+
opacity: 0;
|
|
135
|
+
transition: opacity 0.2s ease;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[data-mk-scroll-rail][data-mk-axis='y']::before {
|
|
139
|
+
top: 0;
|
|
140
|
+
right: 2px;
|
|
141
|
+
bottom: 0;
|
|
142
|
+
width: 10px;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
[data-mk-scroll-rail][data-mk-axis='x']::before {
|
|
146
|
+
right: 0;
|
|
147
|
+
bottom: 2px;
|
|
148
|
+
left: 0;
|
|
149
|
+
height: 10px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
[data-mk-scroll-rail]:hover::before,
|
|
153
|
+
[data-mk-scroll-rail][data-mk-dragging]::before {
|
|
154
|
+
opacity: 1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
[data-mk-scroll-thumb] {
|
|
158
|
+
position: absolute;
|
|
159
|
+
top: 0;
|
|
160
|
+
left: 0;
|
|
161
|
+
cursor: grab;
|
|
162
|
+
will-change: transform;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
[data-mk-scroll-rail][data-mk-axis='y'] [data-mk-scroll-thumb] {
|
|
166
|
+
width: 100%;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
[data-mk-scroll-rail][data-mk-axis='x'] [data-mk-scroll-thumb] {
|
|
170
|
+
height: 100%;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
[data-mk-scroll-rail][data-mk-dragging] [data-mk-scroll-thumb] {
|
|
174
|
+
cursor: grabbing;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* The pill stays centred on the 10 px rail while it swells. */
|
|
178
|
+
[data-mk-scroll-pill] {
|
|
179
|
+
position: absolute;
|
|
180
|
+
border-radius: 999px;
|
|
181
|
+
background: var(--mk-scroll-thumb, color-mix(in srgb, var(--mk-text) 32%, transparent));
|
|
182
|
+
transition: background-color 0.2s ease;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
[data-mk-scroll-rail][data-mk-axis='y'] [data-mk-scroll-pill] {
|
|
186
|
+
top: 0;
|
|
187
|
+
right: calc(2px + (10px - var(--mk-scroll-thickness)) / 2);
|
|
188
|
+
bottom: 0;
|
|
189
|
+
width: var(--mk-scroll-thickness);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
[data-mk-scroll-rail][data-mk-axis='x'] [data-mk-scroll-pill] {
|
|
193
|
+
right: 0;
|
|
194
|
+
bottom: calc(2px + (10px - var(--mk-scroll-thickness)) / 2);
|
|
195
|
+
left: 0;
|
|
196
|
+
height: var(--mk-scroll-thickness);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
[data-mk-scroll-rail]:hover [data-mk-scroll-pill] {
|
|
200
|
+
background: var(--mk-scroll-thumb-hover, color-mix(in srgb, var(--mk-text) 48%, transparent));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
[data-mk-scroll-rail][data-mk-dragging] [data-mk-scroll-pill] {
|
|
204
|
+
background: var(--mk-scroll-thumb-active, var(--mk-accent));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
[data-mk-dragging] [data-mk-scroll-viewport] {
|
|
208
|
+
-webkit-user-select: none;
|
|
209
|
+
user-select: none;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Back to top: a tonal pill in the corner, clear of the bars. */
|
|
213
|
+
[data-mk-scroll-top] {
|
|
214
|
+
position: absolute;
|
|
215
|
+
z-index: 3;
|
|
216
|
+
right: calc(var(--mk-scroll-view-right, 0px) + 20px);
|
|
217
|
+
bottom: calc(var(--mk-scroll-view-bottom, 0px) + 16px);
|
|
218
|
+
display: inline-grid;
|
|
219
|
+
place-items: center;
|
|
220
|
+
width: 40px;
|
|
221
|
+
height: 40px;
|
|
222
|
+
padding: 0;
|
|
223
|
+
border: 0;
|
|
224
|
+
border-radius: 999px;
|
|
225
|
+
background: var(--mk-surface-4);
|
|
226
|
+
box-shadow: 0 8px 24px -8px color-mix(in srgb, var(--mk-shadow-color, #000) 45%, transparent);
|
|
227
|
+
color: var(--mk-text);
|
|
228
|
+
cursor: pointer;
|
|
229
|
+
transition:
|
|
230
|
+
background-color 0.15s,
|
|
231
|
+
box-shadow 0.2s;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
[data-mk-overflow-x] > [data-mk-scroll-top] {
|
|
235
|
+
bottom: calc(var(--mk-scroll-view-bottom, 0px) + 22px);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
[data-mk-scroll-top][hidden] {
|
|
239
|
+
display: none;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
[data-mk-scroll-top]:hover {
|
|
243
|
+
background: color-mix(in srgb, var(--mk-surface-4) 80%, var(--mk-text) 10%);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
[data-mk-scroll-top]:focus-visible {
|
|
247
|
+
outline: none;
|
|
248
|
+
box-shadow:
|
|
249
|
+
0 0 0 3px color-mix(in srgb, var(--mk-accent) 45%, transparent),
|
|
250
|
+
0 8px 24px -8px color-mix(in srgb, var(--mk-shadow-color, #000) 45%, transparent);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
@media (prefers-reduced-motion: reduce) {
|
|
254
|
+
[data-mk-scroll-rail],
|
|
255
|
+
[data-mk-scroll-rail]::before,
|
|
256
|
+
[data-mk-scroll-pill],
|
|
257
|
+
[data-mk-scroll-top] {
|
|
258
|
+
transition: none;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -129,7 +129,12 @@ export function createScrollIsland(root: HTMLElement | null, options: ScrollIsla
|
|
|
129
129
|
const size = as === 'island' ? ISLAND_HEIGHT : Math.max(MIN_BAR, Math.min(height, height * (scroller.clientHeight / Math.max(1, scroller.scrollHeight))));
|
|
130
130
|
const top = (height - size) * progress();
|
|
131
131
|
if (as !== 'island') return { top, height: size, width: BAR_WIDTH, borderRadius: BAR_WIDTH / 2 };
|
|
132
|
-
|
|
132
|
+
// Measure at the content's natural width, independent of the collapsed bar
|
|
133
|
+
// or an interrupted expansion. Padding is already included in offsetWidth.
|
|
134
|
+
content.style.maxWidth = 'none';
|
|
135
|
+
const width = content.offsetWidth;
|
|
136
|
+
content.style.removeProperty('max-width');
|
|
137
|
+
return { top, height: size, width: Math.min(Math.max(ISLAND_HEIGHT, track().right - track().left - INSET * 2), Math.max(ISLAND_HEIGHT, width)), borderRadius: size / 2 };
|
|
133
138
|
};
|
|
134
139
|
|
|
135
140
|
const place = () => {
|
|
@@ -189,6 +194,10 @@ export function createScrollIsland(root: HTMLElement | null, options: ScrollIsla
|
|
|
189
194
|
scope.on(window, 'resize', () => go(state, true));
|
|
190
195
|
|
|
191
196
|
const near = (x: number, y: number) => {
|
|
197
|
+
if (state === 'island') {
|
|
198
|
+
const capsule = thumb.getBoundingClientRect();
|
|
199
|
+
if (x >= capsule.left && x <= capsule.right && y >= capsule.top && y <= capsule.bottom) return true;
|
|
200
|
+
}
|
|
192
201
|
const box = track();
|
|
193
202
|
return y >= box.top - INSET && y <= box.top + box.height + INSET && x <= box.right && box.right - x <= reach;
|
|
194
203
|
};
|
|
@@ -73,6 +73,8 @@ mk-scroll-island {
|
|
|
73
73
|
[data-mk-island-content] {
|
|
74
74
|
display: inline-flex;
|
|
75
75
|
flex: none;
|
|
76
|
+
box-sizing: border-box;
|
|
77
|
+
width: max-content;
|
|
76
78
|
max-width: 100%;
|
|
77
79
|
min-width: 0;
|
|
78
80
|
align-items: center;
|
|
@@ -87,6 +89,10 @@ mk-scroll-island {
|
|
|
87
89
|
transition: opacity 0.18s ease;
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
[data-mk-island-percent] {
|
|
93
|
+
flex: none;
|
|
94
|
+
}
|
|
95
|
+
|
|
90
96
|
[data-mk-scroll-island][data-mk-state='island'] [data-mk-island-content] {
|
|
91
97
|
opacity: 1;
|
|
92
98
|
transition-delay: 0.1s;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# select
|
|
2
2
|
|
|
3
|
-
Una elección de una lista que se abre como el `menu` del kit (crece desde su botón y las opciones entran escalonadas) en lugar de la lista del sistema. Mejora un `<select>` escrito a mano, o crea uno desde `options`, y lo mantiene sincronizado y oculto: los formularios lo envían y sus escuchas de `change` siguen funcionando. El valor elegido entra con un blur breve y el chevron gira.
|
|
3
|
+
Una elección de una lista que se abre como el `menu` del kit (crece desde su botón y las opciones entran escalonadas) en lugar de la lista del sistema. Mejora un `<select>` escrito a mano, o crea uno desde `options`, y lo mantiene sincronizado y oculto: los formularios lo envían y sus escuchas de `change` siguen funcionando. El valor elegido entra con un blur breve y el chevron gira. Una lista larga se desplaza con la barra de [`scroll-area`](../scroll-area/README.md), no con la del sistema; respeta las esquinas redondeadas del menú y funde los bordes.
|
|
4
4
|
|
|
5
5
|
## HTML / Astro
|
|
6
6
|
|
|
@@ -4,6 +4,7 @@ import { createScope } from '../../core/scope';
|
|
|
4
4
|
import { inert, type Instance } from '../../core/types';
|
|
5
5
|
import { press } from '../../motion/micro';
|
|
6
6
|
import { createMenu } from '../menu/engine';
|
|
7
|
+
import { createScrollArea } from '../scroll-area/engine';
|
|
7
8
|
|
|
8
9
|
/*
|
|
9
10
|
* Select: one choice from a list, opened as the kit's menu (it grows out of its
|
|
@@ -12,8 +13,9 @@ import { createMenu } from '../menu/engine';
|
|
|
12
13
|
* step: forms post it and its `change` listeners keep working) or builds one from
|
|
13
14
|
* `options`. The chosen label slides in out of a blur, the chevron turns, typing
|
|
14
15
|
* a letter on the closed button picks the next option that starts with it, and
|
|
15
|
-
* ↓ ↑ Enter Space open the menu.
|
|
16
|
-
*
|
|
16
|
+
* ↓ ↑ Enter Space open the menu. A long list scrolls under the kit's scroll-area
|
|
17
|
+
* bar (no system bar on the menu's rounded edge) with its edges fading out.
|
|
18
|
+
* Changes made to the select's options (by the app) show up on their own.
|
|
17
19
|
*/
|
|
18
20
|
|
|
19
21
|
export interface SelectOption {
|
|
@@ -121,6 +123,10 @@ export function createSelect(root: HTMLElement | null, options: SelectOptions =
|
|
|
121
123
|
dialog.className = 'mk-dialog mk-menu mk-select-menu';
|
|
122
124
|
dialog.dataset.mkSelectMenu = '';
|
|
123
125
|
dialog.setAttribute('aria-label', options.label ?? 'Opciones');
|
|
126
|
+
// The items scroll inside a list, so the bar rides over the menu instead of cutting its rounded edge.
|
|
127
|
+
const list = document.createElement('div');
|
|
128
|
+
list.dataset.mkSelectList = '';
|
|
129
|
+
dialog.append(list);
|
|
124
130
|
root.append(dialog);
|
|
125
131
|
made.push(dialog);
|
|
126
132
|
scope.attr(root, 'data-mk-ready', '');
|
|
@@ -161,7 +167,7 @@ export function createSelect(root: HTMLElement | null, options: SelectOptions =
|
|
|
161
167
|
nodes.push(heading, ...[...child.children].filter((node): node is HTMLOptionElement => node instanceof HTMLOptionElement).map(item));
|
|
162
168
|
} else if (child instanceof HTMLOptionElement) nodes.push(item(child));
|
|
163
169
|
}
|
|
164
|
-
|
|
170
|
+
list.replaceChildren(...nodes);
|
|
165
171
|
};
|
|
166
172
|
|
|
167
173
|
const placeholderText = () => options.placeholder ?? choices().find((option) => option.value === '')?.text ?? 'Elige…';
|
|
@@ -201,13 +207,19 @@ export function createSelect(root: HTMLElement | null, options: SelectOptions =
|
|
|
201
207
|
root.dispatchEvent(new CustomEvent('mk:change', { bubbles: true, detail: { value } }));
|
|
202
208
|
};
|
|
203
209
|
|
|
210
|
+
// The menu's children must be its items: the bar stays out of assistive tech (the list scrolls with the keys).
|
|
211
|
+
const area = createScrollArea(dialog, { viewport: list, axis: 'y', focusable: false, decorative: true });
|
|
204
212
|
const menu = createMenu(dialog, {
|
|
205
213
|
triggers: false,
|
|
206
|
-
onOpen:
|
|
214
|
+
onOpen: () => {
|
|
215
|
+
markItems();
|
|
216
|
+
area.refresh();
|
|
217
|
+
},
|
|
207
218
|
onClose: () => trigger.setAttribute('aria-expanded', 'false'),
|
|
208
219
|
onSelect: (value) => choose(value, true),
|
|
209
220
|
});
|
|
210
221
|
scope.add(() => menu.destroy());
|
|
222
|
+
scope.add(() => area.destroy());
|
|
211
223
|
// The menu's own selection stays inside: listeners on the select hear its change.
|
|
212
224
|
scope.on(dialog, 'mk:select', (event) => event.stopPropagation());
|
|
213
225
|
|
|
@@ -72,20 +72,38 @@ mk-select {
|
|
|
72
72
|
transform: rotate(180deg);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
/* The menu: like the panels of the data table (surface-1, a hairline, 8 px items).
|
|
75
|
+
/* The menu: like the panels of the data table (surface-1, a hairline, 8 px items). The items scroll in a list with the
|
|
76
|
+
kit's scroll-area bar, kept clear of the rounded corners; the system bar never shows. */
|
|
76
77
|
.mk-dialog.mk-select-menu {
|
|
77
78
|
--mk-dialog-bg: var(--mk-surface-1);
|
|
78
79
|
--mk-dialog-radius: 16px;
|
|
79
80
|
--mk-dialog-shadow: 0 0 0 1px var(--mk-outline-soft), 0 16px 48px -12px color-mix(in srgb, var(--mk-shadow-color, #000) 35%, transparent);
|
|
80
|
-
|
|
81
|
+
--mk-scroll-inset: 10px;
|
|
81
82
|
min-width: max(10rem, var(--mk-select-width, 0px));
|
|
82
83
|
max-height: min(20rem, 60dvh);
|
|
83
|
-
padding: 0
|
|
84
|
-
overflow
|
|
84
|
+
padding: 0;
|
|
85
|
+
overflow: hidden;
|
|
85
86
|
-webkit-backdrop-filter: none;
|
|
86
87
|
backdrop-filter: none;
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
.mk-dialog.mk-select-menu [data-mk-select-list] {
|
|
91
|
+
display: flex;
|
|
92
|
+
flex: 1 1 auto;
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
gap: 0.125rem;
|
|
95
|
+
min-height: 0;
|
|
96
|
+
padding: 0.375rem;
|
|
97
|
+
overflow-x: hidden;
|
|
98
|
+
overflow-y: auto;
|
|
99
|
+
overscroll-behavior: contain;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* A long list leaves the bar its own lane. */
|
|
103
|
+
.mk-dialog.mk-select-menu[data-mk-overflow-y] [data-mk-select-list] {
|
|
104
|
+
padding-inline-end: 0.875rem;
|
|
105
|
+
}
|
|
106
|
+
|
|
89
107
|
.mk-dialog.mk-select-menu [data-mk-item] {
|
|
90
108
|
flex: none;
|
|
91
109
|
gap: 0.625rem;
|
package/src/core/timing.ts
CHANGED
|
@@ -200,4 +200,6 @@ export const TIMING = Object.freeze({
|
|
|
200
200
|
repelBento: Object.freeze({ offset: 0.6, stagger: 0.006, strength: 5000, distance: 30, breakpoint: 1024 }),
|
|
201
201
|
rotator: Object.freeze({ interval: 8000, duration: 0.25, y: 5 }),
|
|
202
202
|
dragRuler: Object.freeze({ min: 0, max: 8, step: 0.1, pixelsPerUnit: 60 }),
|
|
203
|
+
/** scroll-area: bars hide after idleMs; edge fades up to `fade` px; a click on the rail glides (s); shortest thumb (px). */
|
|
204
|
+
scrollArea: Object.freeze({ idleMs: 900, fade: 24, glide: 0.6, minThumb: 24 }),
|
|
203
205
|
});
|