@mks2508/mks-ui 0.5.2 → 0.5.7
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/react-ui/index.js +8 -3
- package/dist/react-ui/primitives/index.js +5 -0
- package/dist/react-ui/primitives/waapi/Gooey/Gooey.types.d.ts +120 -0
- package/dist/react-ui/primitives/waapi/Gooey/Gooey.types.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Gooey/GooeyCanvas.d.ts +10 -0
- package/dist/react-ui/primitives/waapi/Gooey/GooeyCanvas.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Gooey/GooeyCanvas.js +190 -0
- package/dist/react-ui/primitives/waapi/Gooey/GooeyFilter.d.ts +7 -0
- package/dist/react-ui/primitives/waapi/Gooey/GooeyFilter.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Gooey/GooeyFilter.js +78 -0
- package/dist/react-ui/primitives/waapi/Gooey/MorphPath.d.ts +7 -0
- package/dist/react-ui/primitives/waapi/Gooey/MorphPath.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Gooey/MorphPath.js +51 -0
- package/dist/react-ui/primitives/waapi/Gooey/gooey-utils.d.ts +94 -0
- package/dist/react-ui/primitives/waapi/Gooey/gooey-utils.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Gooey/gooey-utils.js +182 -0
- package/dist/react-ui/primitives/waapi/Gooey/index.d.ts +28 -0
- package/dist/react-ui/primitives/waapi/Gooey/index.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Gooey/index.js +5 -0
- package/dist/react-ui/primitives/waapi/Gooey/useMorphPath.d.ts +7 -0
- package/dist/react-ui/primitives/waapi/Gooey/useMorphPath.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Gooey/useMorphPath.js +47 -0
- package/dist/react-ui/primitives/waapi/index.d.ts +2 -0
- package/dist/react-ui/primitives/waapi/index.d.ts.map +1 -1
- package/dist/react-ui/primitives/waapi/index.js +6 -0
- package/dist/react-ui/ui/DataCard/DataCard.styles.d.ts +26 -16
- package/dist/react-ui/ui/DataCard/DataCard.styles.d.ts.map +1 -1
- package/dist/react-ui/ui/DataCard/DataCard.styles.js +36 -74
- package/dist/react-ui/ui/DataCard/DataCard.types.d.ts +50 -70
- package/dist/react-ui/ui/DataCard/DataCard.types.d.ts.map +1 -1
- package/dist/react-ui/ui/DataCard/index.d.ts +24 -93
- package/dist/react-ui/ui/DataCard/index.d.ts.map +1 -1
- package/dist/react-ui/ui/DataCard/index.js +76 -118
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle-DOR3Ld-k.css +376 -0
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.css +376 -0
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.js +0 -0
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.styles.d.ts +20 -8
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.styles.d.ts.map +1 -1
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.styles.js +55 -27
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.types.d.ts +69 -14
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.types.d.ts.map +1 -1
- package/dist/react-ui/ui/DynamicToggle/index.d.ts +22 -20
- package/dist/react-ui/ui/DynamicToggle/index.d.ts.map +1 -1
- package/dist/react-ui/ui/DynamicToggle/index.js +133 -96
- package/dist/react-ui/ui/Switch/index.js +1 -1
- package/dist/react-ui/ui/index.js +2 -2
- package/package.json +2 -2
- package/src/css.d.ts +1 -0
- package/src/react-ui/primitives/waapi/Gooey/Gooey.types.ts +141 -0
- package/src/react-ui/primitives/waapi/Gooey/GooeyCanvas.tsx +217 -0
- package/src/react-ui/primitives/waapi/Gooey/GooeyFilter.tsx +77 -0
- package/src/react-ui/primitives/waapi/Gooey/MorphPath.tsx +58 -0
- package/src/react-ui/primitives/waapi/Gooey/gooey-utils.ts +253 -0
- package/src/react-ui/primitives/waapi/Gooey/index.ts +50 -0
- package/src/react-ui/primitives/waapi/Gooey/useMorphPath.ts +48 -0
- package/src/react-ui/primitives/waapi/index.ts +23 -0
- package/src/react-ui/ui/DataCard/DataCard.styles.ts +45 -101
- package/src/react-ui/ui/DataCard/DataCard.types.ts +52 -73
- package/src/react-ui/ui/DataCard/index.tsx +118 -184
- package/src/react-ui/ui/DynamicToggle/DynamicToggle.css +320 -94
- package/src/react-ui/ui/DynamicToggle/DynamicToggle.styles.ts +60 -40
- package/src/react-ui/ui/DynamicToggle/DynamicToggle.types.ts +101 -14
- package/src/react-ui/ui/DynamicToggle/index.tsx +172 -96
- package/src/react-ui/ui/DynamicToggle/prototype-v7-ios.html +413 -0
- package/src/react-ui/ui/DynamicToggle/prototype-v7.html +615 -0
- package/src/react-ui/ui/DynamicToggle/prototype-v8-gooey-safari.html +560 -0
- package/src/react-ui/ui/DynamicToggle/prototype-v8b-react-structure.html +227 -0
- package/src/react-ui/ui/DynamicToggle/prototype.html +419 -0
- package/src/react-ui/ui/Switch/index.tsx +1 -1
- /package/dist/react-ui/blocks/Terminal/panel/{terminal-filter-dropdown.module-DAcl_XQZ.css → terminal-filter-dropdown.module-C6oDcFBS.css} +0 -0
- /package/dist/react-ui/blocks/Terminal/panel/{terminal-session-tabs.module-DNAop5e3.css → terminal-session-tabs.module-D_-sgyza.css} +0 -0
- /package/dist/react-ui/components/MorphingPopover/{morphing-popover.module-BJrjXisF.css → morphing-popover.module-B1ftlaYj.css} +0 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>DynamicToggle — Sileo-style gooey (Safari-safe)</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--fg: #e2e8f0;
|
|
10
|
+
--bg: #0f172a;
|
|
11
|
+
--card: #1e293b;
|
|
12
|
+
--muted: #64748b;
|
|
13
|
+
--border: #334155;
|
|
14
|
+
--duration: 0.22;
|
|
15
|
+
--ease: cubic-bezier(0.22, 0.61, 0.36, 1);
|
|
16
|
+
--drop-off: 0.4;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
* { box-sizing: border-box; margin: 0; }
|
|
20
|
+
body {
|
|
21
|
+
min-height: 100vh;
|
|
22
|
+
display: flex; flex-direction: column; align-items: center;
|
|
23
|
+
padding: 4rem 1rem; gap: 2rem;
|
|
24
|
+
background: var(--bg);
|
|
25
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
26
|
+
color: var(--fg);
|
|
27
|
+
}
|
|
28
|
+
h3 { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 2px; }
|
|
29
|
+
.row { display: flex; gap: 2rem; align-items: end; flex-wrap: wrap; justify-content: center; }
|
|
30
|
+
.sr-only {
|
|
31
|
+
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
|
32
|
+
overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border-width: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* ================================================================
|
|
36
|
+
* CONTROL BASE
|
|
37
|
+
* ================================================================ */
|
|
38
|
+
.control {
|
|
39
|
+
--w: 260px;
|
|
40
|
+
--h: 38px;
|
|
41
|
+
--radius: 9999px;
|
|
42
|
+
--font: 0.75rem;
|
|
43
|
+
--bubble-h-pct: 40;
|
|
44
|
+
--bubble-inset-pct: 20;
|
|
45
|
+
--_r: min(var(--radius), calc(var(--h) / 2));
|
|
46
|
+
|
|
47
|
+
position: relative;
|
|
48
|
+
width: var(--w);
|
|
49
|
+
height: var(--h);
|
|
50
|
+
padding: 2px;
|
|
51
|
+
margin-top: calc(var(--h) * 0.5);
|
|
52
|
+
/* Sileo: GPU layer + containment on root */
|
|
53
|
+
transform: translateZ(0);
|
|
54
|
+
contain: layout style;
|
|
55
|
+
overflow: visible;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.control__track {
|
|
59
|
+
display: grid; place-items: center;
|
|
60
|
+
grid-template-columns: repeat(4, 1fr);
|
|
61
|
+
width: 100%; height: 100%;
|
|
62
|
+
position: relative;
|
|
63
|
+
z-index: 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.indicator {
|
|
67
|
+
position: absolute;
|
|
68
|
+
width: 50%; left: 0; top: 0; bottom: 0;
|
|
69
|
+
background: var(--fg);
|
|
70
|
+
border-radius: var(--radius);
|
|
71
|
+
transition: translate calc(var(--duration) * 1s) var(--ease);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
label {
|
|
75
|
+
display: inline-grid; place-items: center;
|
|
76
|
+
height: 100%; width: 100%;
|
|
77
|
+
cursor: pointer; font-size: var(--font);
|
|
78
|
+
color: var(--fg); z-index: 2; font-weight: 500;
|
|
79
|
+
}
|
|
80
|
+
.control__track > label { color: var(--card); }
|
|
81
|
+
|
|
82
|
+
.group {
|
|
83
|
+
width: 100%; height: 100%;
|
|
84
|
+
display: grid; position: relative;
|
|
85
|
+
grid-template-columns: 1fr 1fr;
|
|
86
|
+
container-type: size;
|
|
87
|
+
overflow: hidden;
|
|
88
|
+
}
|
|
89
|
+
.group, .control__track > label:nth-of-type(1) { grid-column: span 2; }
|
|
90
|
+
|
|
91
|
+
.group .indicator {
|
|
92
|
+
background: var(--fg); left: 50%; top: 0;
|
|
93
|
+
translate: -50% 0;
|
|
94
|
+
pointer-events: none;
|
|
95
|
+
transition: translate calc(var(--duration) * 1s) var(--ease),
|
|
96
|
+
clip-path calc(var(--duration) * 1s) var(--ease),
|
|
97
|
+
background calc(var(--duration) * 1s) var(--ease);
|
|
98
|
+
clip-path: inset(73cqh calc(50% + 1px) calc(27cqh - 2px) calc(50% - 3px) round var(--radius));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* ── Pseudo text ── */
|
|
102
|
+
.group::before {
|
|
103
|
+
content: attr(data-label);
|
|
104
|
+
position: absolute; left: 50%; top: 50%;
|
|
105
|
+
translate: -50% -80%;
|
|
106
|
+
color: var(--fg); font-size: var(--font); font-weight: 500;
|
|
107
|
+
z-index: 2; white-space: nowrap; pointer-events: none;
|
|
108
|
+
transition: scale calc(var(--duration) * 1s) var(--ease),
|
|
109
|
+
translate calc(var(--duration) * 1s) var(--ease),
|
|
110
|
+
opacity calc(var(--duration) * 1s) var(--ease);
|
|
111
|
+
}
|
|
112
|
+
.group::after {
|
|
113
|
+
content: attr(data-opts);
|
|
114
|
+
position: absolute; left: 50%; top: 50%;
|
|
115
|
+
translate: -50% 20%;
|
|
116
|
+
color: var(--muted); font-size: calc(var(--font) * 0.85);
|
|
117
|
+
opacity: 0.6; z-index: 2; white-space: nowrap; pointer-events: none;
|
|
118
|
+
transition: opacity calc(var(--duration) * 1s) var(--ease);
|
|
119
|
+
}
|
|
120
|
+
.group:not([data-opts])::after { content: none; }
|
|
121
|
+
|
|
122
|
+
.group label {
|
|
123
|
+
color: var(--muted); cursor: pointer; z-index: 2;
|
|
124
|
+
transition: color calc(var(--duration) * 1s) var(--ease),
|
|
125
|
+
opacity calc(var(--duration) * 1s) var(--ease),
|
|
126
|
+
translate calc(var(--duration) * 1s) var(--ease);
|
|
127
|
+
}
|
|
128
|
+
.group label span {
|
|
129
|
+
display: grid; place-items: center; height: 100%; width: 100%;
|
|
130
|
+
border-radius: var(--radius);
|
|
131
|
+
transition: scale calc(var(--duration) * 1s) var(--ease);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Collapsed modes */
|
|
135
|
+
.group[data-collapsed="title"]::before { translate: -50% -50%; }
|
|
136
|
+
.group[data-collapsed="title"]::after { display: none; }
|
|
137
|
+
.group[data-collapsed="title"]:not(:has(:checked)) label { opacity: 0; translate: 0 30%; }
|
|
138
|
+
.group[data-collapsed="title"]:not(:has(:checked)) label span { scale: 0.5; }
|
|
139
|
+
|
|
140
|
+
.group[data-collapsed="opts"]::before { display: none; }
|
|
141
|
+
.group[data-collapsed="opts"]::after { translate: -50% -50%; font-size: var(--font); opacity: 0.7; }
|
|
142
|
+
.group[data-collapsed="opts"]:not(:has(:checked)) label { opacity: 0; translate: 0 30%; }
|
|
143
|
+
.group[data-collapsed="opts"]:not(:has(:checked)) label span { scale: 0.5; }
|
|
144
|
+
|
|
145
|
+
/* Track states */
|
|
146
|
+
.control__track:has(> :checked) > label { color: var(--card); }
|
|
147
|
+
.control__track:not(:has(> :checked)) > label { color: var(--fg); opacity: var(--drop-off); }
|
|
148
|
+
.control__track:not(:has(> :checked)) > .indicator { translate: 100% 0; }
|
|
149
|
+
.control__track:has(> :checked) .group .indicator { background: transparent; }
|
|
150
|
+
|
|
151
|
+
/* Group expanded */
|
|
152
|
+
.control--goo .group:has(:checked)::before { opacity: 0; translate: -50% -80%; scale: 1; }
|
|
153
|
+
.group:has(:checked)::after { opacity: 0; }
|
|
154
|
+
.group:has(:checked) label { opacity: 0.75; color: var(--muted); translate: 0 0; }
|
|
155
|
+
.group:has(:checked) label span { scale: 1; }
|
|
156
|
+
.group:has(:checked) .indicator { background: var(--card); clip-path: inset(0 0 0 0 round var(--radius)); }
|
|
157
|
+
.group:has(:nth-of-type(1):checked) label:nth-of-type(1),
|
|
158
|
+
.group:has(:nth-of-type(2):checked) label:nth-of-type(2) { color: var(--fg); opacity: 1; }
|
|
159
|
+
.group:has(:nth-of-type(1):checked) .indicator { translate: -100% 0; }
|
|
160
|
+
.group:has(:nth-of-type(2):checked) .indicator { translate: 0 0; }
|
|
161
|
+
|
|
162
|
+
/* ================================================================
|
|
163
|
+
* SILEO-STYLE GOOEY: SVG filter on a canvas div, SVG rects inside
|
|
164
|
+
* Key: animated content is SVG <rect>, not HTML div.
|
|
165
|
+
* Safari re-renders SVG filter smoothly when SVG children change.
|
|
166
|
+
* ================================================================ */
|
|
167
|
+
.control--goo {
|
|
168
|
+
background: transparent;
|
|
169
|
+
border: none;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.goo-canvas {
|
|
173
|
+
position: absolute;
|
|
174
|
+
inset: 0;
|
|
175
|
+
pointer-events: none;
|
|
176
|
+
z-index: 0;
|
|
177
|
+
/* Sileo's Safari formula */
|
|
178
|
+
transform: translateZ(0);
|
|
179
|
+
contain: layout style;
|
|
180
|
+
overflow: visible;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.goo-svg {
|
|
184
|
+
position: absolute;
|
|
185
|
+
top: 0; left: 0;
|
|
186
|
+
overflow: visible;
|
|
187
|
+
pointer-events: none;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Bubble TEXT — outside the filtered SVG, on top */
|
|
191
|
+
.control--goo .bubble-label {
|
|
192
|
+
position: absolute;
|
|
193
|
+
z-index: 2;
|
|
194
|
+
display: flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
justify-content: center;
|
|
197
|
+
font-size: var(--font);
|
|
198
|
+
font-weight: 500;
|
|
199
|
+
color: var(--fg);
|
|
200
|
+
white-space: nowrap;
|
|
201
|
+
pointer-events: none;
|
|
202
|
+
opacity: 0;
|
|
203
|
+
transition: opacity calc(var(--duration) * 1s) var(--ease);
|
|
204
|
+
}
|
|
205
|
+
.control--goo:has(.group :checked) .bubble-label {
|
|
206
|
+
opacity: 1;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* SIZES */
|
|
210
|
+
.control.sm { --w: 210px; --h: 30px; --font: 10px; }
|
|
211
|
+
.control.lg { --w: 320px; --h: 44px; --font: 14px; }
|
|
212
|
+
.control.xl { --w: 400px; --h: 52px; --font: 16px; }
|
|
213
|
+
.control.rounded { --radius: 12px; }
|
|
214
|
+
.control.square { --radius: 6px; }
|
|
215
|
+
</style>
|
|
216
|
+
</head>
|
|
217
|
+
<body>
|
|
218
|
+
|
|
219
|
+
<h3>Sileo-style gooey — SVG rects + filter (Safari-safe)</h3>
|
|
220
|
+
<p style="color:var(--muted);font-size:12px;max-width:600px;text-align:center">
|
|
221
|
+
SVG filter applied to a canvas div. Pill + bubble are SVG <code><rect></code> elements
|
|
222
|
+
animated via WAAPI. Safari renders filter smoothly because changes are SVG-internal.
|
|
223
|
+
</p>
|
|
224
|
+
|
|
225
|
+
<h3>collapsed="title" — Sizes</h3>
|
|
226
|
+
<div class="row">
|
|
227
|
+
<div><h3>SM</h3>
|
|
228
|
+
<div class="control sm control--goo" data-goo>
|
|
229
|
+
<div class="goo-canvas"><svg class="goo-svg"></svg></div>
|
|
230
|
+
<div class="bubble-label">Changes</div>
|
|
231
|
+
<div class="control__track"><div class="indicator"></div>
|
|
232
|
+
<label for="gs-a">Tree</label><input class="sr-only" type="radio" name="gs" id="gs-a" checked>
|
|
233
|
+
<div class="group" data-collapsed="title" data-label="Changes"><div class="indicator"></div>
|
|
234
|
+
<label for="gs-b"><span>Flat</span></label><input class="sr-only" type="radio" name="gs" id="gs-b">
|
|
235
|
+
<label for="gs-c"><span>Grp</span></label><input class="sr-only" type="radio" name="gs" id="gs-c">
|
|
236
|
+
</div></div></div></div>
|
|
237
|
+
|
|
238
|
+
<div><h3>Default</h3>
|
|
239
|
+
<div class="control control--goo" data-goo>
|
|
240
|
+
<div class="goo-canvas"><svg class="goo-svg"></svg></div>
|
|
241
|
+
<div class="bubble-label">Premium</div>
|
|
242
|
+
<div class="control__track"><div class="indicator"></div>
|
|
243
|
+
<label for="gd-a">Free</label><input class="sr-only" type="radio" name="gd" id="gd-a" checked>
|
|
244
|
+
<div class="group" data-collapsed="title" data-label="Premium"><div class="indicator"></div>
|
|
245
|
+
<label for="gd-b"><span>Solo</span></label><input class="sr-only" type="radio" name="gd" id="gd-b">
|
|
246
|
+
<label for="gd-c"><span>Team</span></label><input class="sr-only" type="radio" name="gd" id="gd-c">
|
|
247
|
+
</div></div></div></div>
|
|
248
|
+
|
|
249
|
+
<div><h3>LG</h3>
|
|
250
|
+
<div class="control lg control--goo" data-goo>
|
|
251
|
+
<div class="goo-canvas"><svg class="goo-svg"></svg></div>
|
|
252
|
+
<div class="bubble-label">Billing</div>
|
|
253
|
+
<div class="control__track"><div class="indicator"></div>
|
|
254
|
+
<label for="gl-a">Annual</label><input class="sr-only" type="radio" name="gl" id="gl-a" checked>
|
|
255
|
+
<div class="group" data-collapsed="title" data-label="Billing"><div class="indicator"></div>
|
|
256
|
+
<label for="gl-b"><span>Monthly</span></label><input class="sr-only" type="radio" name="gl" id="gl-b">
|
|
257
|
+
<label for="gl-c"><span>Weekly</span></label><input class="sr-only" type="radio" name="gl" id="gl-c">
|
|
258
|
+
</div></div></div></div>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<h3>Shapes</h3>
|
|
262
|
+
<div class="row">
|
|
263
|
+
<div class="control rounded control--goo" data-goo>
|
|
264
|
+
<div class="goo-canvas"><svg class="goo-svg"></svg></div>
|
|
265
|
+
<div class="bubble-label">Theme</div>
|
|
266
|
+
<div class="control__track"><div class="indicator"></div>
|
|
267
|
+
<label for="gr-a">System</label><input class="sr-only" type="radio" name="gr" id="gr-a">
|
|
268
|
+
<div class="group" data-collapsed="title" data-label="Theme"><div class="indicator"></div>
|
|
269
|
+
<label for="gr-b"><span>Light</span></label><input class="sr-only" type="radio" name="gr" id="gr-b" checked>
|
|
270
|
+
<label for="gr-c"><span>Dark</span></label><input class="sr-only" type="radio" name="gr" id="gr-c">
|
|
271
|
+
</div></div></div>
|
|
272
|
+
|
|
273
|
+
<div class="control square control--goo" data-goo>
|
|
274
|
+
<div class="goo-canvas"><svg class="goo-svg"></svg></div>
|
|
275
|
+
<div class="bubble-label">Output</div>
|
|
276
|
+
<div class="control__track"><div class="indicator"></div>
|
|
277
|
+
<label for="gsq-a">Input</label><input class="sr-only" type="radio" name="gsq" id="gsq-a">
|
|
278
|
+
<div class="group" data-collapsed="title" data-label="Output"><div class="indicator"></div>
|
|
279
|
+
<label for="gsq-b"><span>JSON</span></label><input class="sr-only" type="radio" name="gsq" id="gsq-b" checked>
|
|
280
|
+
<label for="gsq-c"><span>XML</span></label><input class="sr-only" type="radio" name="gsq" id="gsq-c">
|
|
281
|
+
</div></div></div>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
<h3>collapsed="opts"</h3>
|
|
285
|
+
<div class="row">
|
|
286
|
+
<div class="control control--goo" data-goo>
|
|
287
|
+
<div class="goo-canvas"><svg class="goo-svg"></svg></div>
|
|
288
|
+
<div class="bubble-label">Premium</div>
|
|
289
|
+
<div class="control__track"><div class="indicator"></div>
|
|
290
|
+
<label for="go-a">Free</label><input class="sr-only" type="radio" name="go" id="go-a" checked>
|
|
291
|
+
<div class="group" data-collapsed="opts" data-label="Premium" data-opts="Solo · Team"><div class="indicator"></div>
|
|
292
|
+
<label for="go-b"><span>Solo</span></label><input class="sr-only" type="radio" name="go" id="go-b">
|
|
293
|
+
<label for="go-c"><span>Team</span></label><input class="sr-only" type="radio" name="go" id="go-c">
|
|
294
|
+
</div></div></div>
|
|
295
|
+
</div>
|
|
296
|
+
|
|
297
|
+
<script>
|
|
298
|
+
// ================================================================
|
|
299
|
+
// SILEO-STYLE GOOEY ENGINE
|
|
300
|
+
// SVG filter + animated SVG rects (not HTML divs)
|
|
301
|
+
// ================================================================
|
|
302
|
+
|
|
303
|
+
const BLUR_RATIO = 0.15;
|
|
304
|
+
const DURATION = 0.25; // seconds
|
|
305
|
+
const OUTLINE_COLOR = '#334155';
|
|
306
|
+
|
|
307
|
+
let filterCounter = 0;
|
|
308
|
+
|
|
309
|
+
document.querySelectorAll('[data-goo]').forEach(control => {
|
|
310
|
+
const canvas = control.querySelector('.goo-canvas');
|
|
311
|
+
const svg = control.querySelector('.goo-svg');
|
|
312
|
+
const group = control.querySelector('.group');
|
|
313
|
+
const bubbleLabel = control.querySelector('.bubble-label');
|
|
314
|
+
const style = getComputedStyle(control);
|
|
315
|
+
|
|
316
|
+
const w = parseFloat(style.getPropertyValue('--w'));
|
|
317
|
+
const h = parseFloat(style.getPropertyValue('--h'));
|
|
318
|
+
const r = parseFloat(style.getPropertyValue('--radius'));
|
|
319
|
+
const effectiveR = Math.min(r, h / 2);
|
|
320
|
+
const bubbleHPct = parseFloat(style.getPropertyValue('--bubble-h-pct')) || 40;
|
|
321
|
+
const bubbleInsetPct = parseFloat(style.getPropertyValue('--bubble-inset-pct')) || 20;
|
|
322
|
+
const blur = Math.round(h * BLUR_RATIO);
|
|
323
|
+
const bubbleH = h * bubbleHPct / 100;
|
|
324
|
+
const bubbleInset = w * bubbleInsetPct / 100;
|
|
325
|
+
const bubbleW = w - 2 * bubbleInset;
|
|
326
|
+
const bubbleR = Math.min(effectiveR * 0.6, bubbleH * 0.45, 12);
|
|
327
|
+
|
|
328
|
+
// Create unique filter
|
|
329
|
+
const filterId = `goo-${filterCounter++}`;
|
|
330
|
+
|
|
331
|
+
// Build SVG content: filter defs + pill rect + bubble rect
|
|
332
|
+
const totalH = h + bubbleH;
|
|
333
|
+
svg.setAttribute('width', w);
|
|
334
|
+
svg.setAttribute('height', totalH);
|
|
335
|
+
svg.setAttribute('viewBox', `0 0 ${w} ${totalH}`);
|
|
336
|
+
svg.style.top = `-${bubbleH}px`;
|
|
337
|
+
|
|
338
|
+
svg.innerHTML = `
|
|
339
|
+
<defs>
|
|
340
|
+
<filter id="${filterId}" x="-20%" y="-20%" width="140%" height="140%"
|
|
341
|
+
color-interpolation-filters="sRGB">
|
|
342
|
+
<feGaussianBlur in="SourceGraphic" stdDeviation="${blur}" result="blur"/>
|
|
343
|
+
<feColorMatrix in="blur" mode="matrix"
|
|
344
|
+
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10" result="goo"/>
|
|
345
|
+
<feComposite in="SourceGraphic" in2="goo" operator="atop"/>
|
|
346
|
+
</filter>
|
|
347
|
+
</defs>
|
|
348
|
+
<rect class="pill-rect"
|
|
349
|
+
x="2" y="${bubbleH}" width="${w - 4}" height="${h - 4}" rx="${effectiveR}" ry="${effectiveR}"
|
|
350
|
+
fill="var(--card)"/>
|
|
351
|
+
<rect class="bubble-rect"
|
|
352
|
+
x="${bubbleInset}" y="${bubbleH}" width="${bubbleW}" height="0" rx="${bubbleR}" ry="${bubbleR}"
|
|
353
|
+
fill="var(--card)"/>
|
|
354
|
+
`;
|
|
355
|
+
|
|
356
|
+
// Apply filter + drop-shadow to canvas
|
|
357
|
+
canvas.style.filter = `url(#${filterId}) drop-shadow(0 0 0.5px ${OUTLINE_COLOR}) drop-shadow(0 0 0.5px ${OUTLINE_COLOR})`;
|
|
358
|
+
|
|
359
|
+
const pillRect = svg.querySelector('.pill-rect');
|
|
360
|
+
const bubbleRect = svg.querySelector('.bubble-rect');
|
|
361
|
+
|
|
362
|
+
// Position bubble label
|
|
363
|
+
bubbleLabel.style.left = bubbleInset + 'px';
|
|
364
|
+
bubbleLabel.style.right = bubbleInset + 'px';
|
|
365
|
+
bubbleLabel.style.height = bubbleH + 'px';
|
|
366
|
+
bubbleLabel.style.bottom = h + 'px';
|
|
367
|
+
|
|
368
|
+
// Animate bubble rect via WAAPI (like Sileo uses Motion)
|
|
369
|
+
let currentAnim = null;
|
|
370
|
+
|
|
371
|
+
function animateBubble(expand) {
|
|
372
|
+
if (currentAnim) currentAnim.cancel();
|
|
373
|
+
|
|
374
|
+
const targetY = expand ? bubbleH - bubbleH : bubbleH;
|
|
375
|
+
const targetH = expand ? bubbleH : 0;
|
|
376
|
+
|
|
377
|
+
currentAnim = bubbleRect.animate([
|
|
378
|
+
{ // from current
|
|
379
|
+
y: bubbleRect.getAttribute('y'),
|
|
380
|
+
height: bubbleRect.getAttribute('height'),
|
|
381
|
+
},
|
|
382
|
+
{ // to target
|
|
383
|
+
y: targetY,
|
|
384
|
+
height: targetH,
|
|
385
|
+
}
|
|
386
|
+
], {
|
|
387
|
+
duration: DURATION * 1500,
|
|
388
|
+
easing: 'cubic-bezier(0.22, 0.61, 0.36, 1)',
|
|
389
|
+
fill: 'forwards',
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
currentAnim.onfinish = () => {
|
|
393
|
+
bubbleRect.setAttribute('y', targetY);
|
|
394
|
+
bubbleRect.setAttribute('height', targetH);
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Listen for radio changes
|
|
399
|
+
function checkState() {
|
|
400
|
+
const expanded = group.querySelector(':checked') !== null;
|
|
401
|
+
animateBubble(expanded);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
control.querySelectorAll('input[type="radio"]').forEach(input => {
|
|
405
|
+
input.addEventListener('change', checkState);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// Initial state
|
|
409
|
+
checkState();
|
|
410
|
+
});
|
|
411
|
+
</script>
|
|
412
|
+
</body>
|
|
413
|
+
</html>
|