@esportsplus/ui 0.56.2 → 0.56.3
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/build/components/index.d.ts +1 -0
- package/build/components/index.js +1 -0
- package/build/components/surface-field/index.d.ts +2200 -0
- package/build/components/surface-field/index.js +474 -0
- package/build/components/surface-field/scss/index.scss +2 -0
- package/package.json +1 -1
- package/src/components/index.ts +1 -0
- package/src/components/surface-field/index.ts +816 -0
- package/src/components/surface-field/scss/index.scss +115 -0
- package/src/components/surface-field/scss/variables.scss +11 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import * as template_u1 from '@esportsplus/template';
|
|
2
|
+
import { component } from '@esportsplus/template';
|
|
3
|
+
import './scss/index.scss';
|
|
4
|
+
const template_1a5zh7n1 = template_u1.template(`<div class='surface-field-surface'><button class='surface-field-surface-handle' type='button'></button><div class='surface-field-surface-content'></div><button class='surface-field-surface-resize' type='button'>`);
|
|
5
|
+
const template_1a5zh7n7 = template_u1.template(`<div class='surface-field'><canvas aria-hidden='true' class='surface-field-canvas'></canvas><div class='surface-field-content'>`);
|
|
6
|
+
const ALPHA_LEVELS = 32;
|
|
7
|
+
const DEFAULTS = {
|
|
8
|
+
accent: '#6683e8',
|
|
9
|
+
base: 0.14,
|
|
10
|
+
breathe: 0,
|
|
11
|
+
brightness: 0.2,
|
|
12
|
+
camera: null,
|
|
13
|
+
connected: true,
|
|
14
|
+
gap: 22,
|
|
15
|
+
lineRadius: 200,
|
|
16
|
+
pointerPush: 2,
|
|
17
|
+
radius: 250,
|
|
18
|
+
ripplePush: 6,
|
|
19
|
+
still: false,
|
|
20
|
+
surfacePadding: 0,
|
|
21
|
+
tint: 0,
|
|
22
|
+
wander: false
|
|
23
|
+
};
|
|
24
|
+
const IDLE = 1500;
|
|
25
|
+
const KEYS = Object.keys(DEFAULTS);
|
|
26
|
+
const HALO = 0.75;
|
|
27
|
+
const RIPPLE_LIFE = 1.4;
|
|
28
|
+
const RIPPLE_SPEED = 520;
|
|
29
|
+
const RIPPLE_WIDTH = 36;
|
|
30
|
+
const SELECTOR = '.surface-field-surface, [data-surface-field]';
|
|
31
|
+
const TAU = Math.PI * 2;
|
|
32
|
+
const TINT_LEVELS = 5;
|
|
33
|
+
let colors = new Map(), probe = null, z = 1;
|
|
34
|
+
function clamp(value, min, max) {
|
|
35
|
+
return Math.min(max, Math.max(min, value));
|
|
36
|
+
}
|
|
37
|
+
function falloff(distance, radius) {
|
|
38
|
+
if (radius <= 0 || distance >= radius) {
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
let t = distance / radius;
|
|
42
|
+
return (1 - t * t) * (1 - t * t);
|
|
43
|
+
}
|
|
44
|
+
function rgb(color) {
|
|
45
|
+
let cached = colors.get(color);
|
|
46
|
+
if (cached) {
|
|
47
|
+
return cached;
|
|
48
|
+
}
|
|
49
|
+
probe ??= document.createElement('canvas').getContext('2d', { willReadFrequently: true });
|
|
50
|
+
probe.clearRect(0, 0, 1, 1);
|
|
51
|
+
probe.fillStyle = '#000';
|
|
52
|
+
probe.fillStyle = color;
|
|
53
|
+
probe.fillRect(0, 0, 1, 1);
|
|
54
|
+
let data = probe.getImageData(0, 0, 1, 1).data, value = [data[0], data[1], data[2]];
|
|
55
|
+
if (colors.size > 64) {
|
|
56
|
+
colors.clear();
|
|
57
|
+
}
|
|
58
|
+
colors.set(color, value);
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
const surface = component(function ({ bounded = true, height = 160, label = 'surface', minHeight = 72, minWidth = 120, state, width = 240, x = 0, y = 0, ...attributes }, content) {
|
|
62
|
+
let element, start = null;
|
|
63
|
+
if (state) {
|
|
64
|
+
({ height, width, x, y } = state);
|
|
65
|
+
}
|
|
66
|
+
function apply() {
|
|
67
|
+
if (!element) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
let parent = element.parentElement;
|
|
71
|
+
width = Math.max(minWidth, width);
|
|
72
|
+
height = Math.max(minHeight, height);
|
|
73
|
+
if (bounded && parent) {
|
|
74
|
+
let pw = parent.clientWidth, ph = parent.clientHeight;
|
|
75
|
+
width = clamp(width, minWidth, Math.max(minWidth, pw));
|
|
76
|
+
height = clamp(height, minHeight, Math.max(minHeight, ph));
|
|
77
|
+
x = clamp(x, 0, Math.max(0, pw - width));
|
|
78
|
+
y = clamp(y, 0, Math.max(0, ph - height));
|
|
79
|
+
}
|
|
80
|
+
element.style.height = `${height}px`;
|
|
81
|
+
element.style.translate = `${x}px ${y}px`;
|
|
82
|
+
element.style.width = `${width}px`;
|
|
83
|
+
if (state && (state.height !== height || state.width !== width || state.x !== x || state.y !== y)) {
|
|
84
|
+
state.height = height;
|
|
85
|
+
state.width = width;
|
|
86
|
+
state.x = x;
|
|
87
|
+
state.y = y;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function begin(event) {
|
|
91
|
+
if (event.button !== 0 || !element) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
95
|
+
element.classList.add('--active');
|
|
96
|
+
element.style.zIndex = String(++z);
|
|
97
|
+
start = {
|
|
98
|
+
h: height,
|
|
99
|
+
pointerX: event.clientX,
|
|
100
|
+
pointerY: event.clientY,
|
|
101
|
+
scale: element.getBoundingClientRect().width / element.offsetWidth || 1,
|
|
102
|
+
w: width,
|
|
103
|
+
x,
|
|
104
|
+
y
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function end() {
|
|
108
|
+
element?.classList.remove('--active');
|
|
109
|
+
start = null;
|
|
110
|
+
}
|
|
111
|
+
function nudge(event, resize) {
|
|
112
|
+
let step = event.shiftKey ? 32 : 8, dx = event.key === 'ArrowLeft' ? -step : event.key === 'ArrowRight' ? step : 0, dy = event.key === 'ArrowUp' ? -step : event.key === 'ArrowDown' ? step : 0;
|
|
113
|
+
if (!dx && !dy) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
if (resize) {
|
|
118
|
+
width += dx;
|
|
119
|
+
height += dy;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
x += dx;
|
|
123
|
+
y += dy;
|
|
124
|
+
}
|
|
125
|
+
apply();
|
|
126
|
+
}
|
|
127
|
+
return (() => {
|
|
128
|
+
let root_1a5zh7n0 = template_1a5zh7n1(), element_1a5zh7n2 = root_1a5zh7n0.firstChild, element_1a5zh7n3 = element_1a5zh7n2.firstElementChild, element_1a5zh7n4 = element_1a5zh7n3.nextElementSibling, element_1a5zh7n5 = element_1a5zh7n4.nextElementSibling;
|
|
129
|
+
template_u1.setProperties(element_1a5zh7n2, this?.attributes);
|
|
130
|
+
template_u1.setProperties(element_1a5zh7n2, attributes);
|
|
131
|
+
template_u1.onconnect(element_1a5zh7n2, (el) => {
|
|
132
|
+
element = el;
|
|
133
|
+
apply();
|
|
134
|
+
});
|
|
135
|
+
template_u1.setProperty(element_1a5zh7n3, 'aria-label', `Move ${label} surface with arrow keys`);
|
|
136
|
+
template_u1.delegate(element_1a5zh7n3, 'keydown', (event) => nudge(event, false));
|
|
137
|
+
template_u1.delegate(element_1a5zh7n3, 'lostpointercapture', end);
|
|
138
|
+
template_u1.delegate(element_1a5zh7n3, 'pointerdown', begin);
|
|
139
|
+
template_u1.delegate(element_1a5zh7n3, 'pointermove', (event) => {
|
|
140
|
+
if (!start) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
x = start.x + (event.clientX - start.pointerX) / start.scale;
|
|
144
|
+
y = start.y + (event.clientY - start.pointerY) / start.scale;
|
|
145
|
+
apply();
|
|
146
|
+
});
|
|
147
|
+
template_u1.slot(element_1a5zh7n4, content, 1);
|
|
148
|
+
template_u1.setProperty(element_1a5zh7n5, 'aria-label', `Resize ${label} surface with arrow keys; right and down make it larger`);
|
|
149
|
+
template_u1.delegate(element_1a5zh7n5, 'keydown', (event) => nudge(event, true));
|
|
150
|
+
template_u1.delegate(element_1a5zh7n5, 'lostpointercapture', end);
|
|
151
|
+
template_u1.delegate(element_1a5zh7n5, 'pointerdown', begin);
|
|
152
|
+
template_u1.delegate(element_1a5zh7n5, 'pointermove', (event) => {
|
|
153
|
+
if (!start) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
width = start.w + (event.clientX - start.pointerX) / start.scale;
|
|
157
|
+
height = start.h + (event.clientY - start.pointerY) / start.scale;
|
|
158
|
+
apply();
|
|
159
|
+
});
|
|
160
|
+
return root_1a5zh7n0;
|
|
161
|
+
})();
|
|
162
|
+
});
|
|
163
|
+
export default Object.assign(component(function (attributes, content) {
|
|
164
|
+
let given = {}, rest = {}, state = attributes.state;
|
|
165
|
+
for (let key in attributes) {
|
|
166
|
+
if (key === 'state') {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (KEYS.includes(key)) {
|
|
170
|
+
given[key] = attributes[key];
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
rest[key] = attributes[key];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
let canvas, context = null, frame = 0, host, key = '', last = 0, light = { intensity: 0, x: 0, y: 0 }, reduced = matchMedia('(prefers-reduced-motion: reduce)'), observer, pointer = { at: -Infinity, inside: false, x: 0, y: 0 }, ripples = [], size = { dpr: 0, height: 0, width: 0 }, visible = false;
|
|
177
|
+
let bucket = new Uint16Array(0), dotAlpha = new Float32Array(0), dotLight = new Float32Array(0), lineLight = new Float32Array(0), order = new Uint32Array(0), px = new Float32Array(0), py = new Float32Array(0), segment = new Uint32Array(0), segmentBucket = new Uint16Array(0), segmentOrder = new Uint32Array(0), seed = new Float32Array(0);
|
|
178
|
+
function options() {
|
|
179
|
+
let snapshot = {};
|
|
180
|
+
for (let i = 0, n = KEYS.length; i < n; i++) {
|
|
181
|
+
let k = KEYS[i];
|
|
182
|
+
snapshot[k] = state?.[k] ?? given[k] ?? DEFAULTS[k];
|
|
183
|
+
}
|
|
184
|
+
let camera = snapshot.camera;
|
|
185
|
+
if (camera) {
|
|
186
|
+
snapshot.camera = { x: camera.x, y: camera.y, zoom: camera.zoom };
|
|
187
|
+
}
|
|
188
|
+
return snapshot;
|
|
189
|
+
}
|
|
190
|
+
function grow(n) {
|
|
191
|
+
if (px.length >= n) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
bucket = new Uint16Array(n);
|
|
195
|
+
dotAlpha = new Float32Array(n);
|
|
196
|
+
dotLight = new Float32Array(n);
|
|
197
|
+
lineLight = new Float32Array(n);
|
|
198
|
+
order = new Uint32Array(n);
|
|
199
|
+
px = new Float32Array(n);
|
|
200
|
+
py = new Float32Array(n);
|
|
201
|
+
segment = new Uint32Array(n * 4);
|
|
202
|
+
segmentBucket = new Uint16Array(n * 2);
|
|
203
|
+
segmentOrder = new Uint32Array(n * 2);
|
|
204
|
+
seed = new Float32Array(n);
|
|
205
|
+
}
|
|
206
|
+
function start() {
|
|
207
|
+
if (!frame && visible && host?.isConnected) {
|
|
208
|
+
last = performance.now();
|
|
209
|
+
frame = requestAnimationFrame(tick);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function stop() {
|
|
213
|
+
cancelAnimationFrame(frame);
|
|
214
|
+
frame = 0;
|
|
215
|
+
}
|
|
216
|
+
function tick(now) {
|
|
217
|
+
frame = requestAnimationFrame(tick);
|
|
218
|
+
if (!host || !canvas || !context) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
let dt = Math.min(64, now - last) / 1000, o = options(), width = host.clientWidth, height = host.clientHeight, dpr = Math.min(2, devicePixelRatio || 1);
|
|
222
|
+
last = now;
|
|
223
|
+
if (width !== size.width || height !== size.height || dpr !== size.dpr) {
|
|
224
|
+
canvas.height = Math.max(1, Math.round(height * dpr));
|
|
225
|
+
canvas.width = Math.max(1, Math.round(width * dpr));
|
|
226
|
+
size = { dpr, height, width };
|
|
227
|
+
key = '';
|
|
228
|
+
}
|
|
229
|
+
let bounds = host.getBoundingClientRect(), elements = host.querySelectorAll(SELECTOR), rects = [];
|
|
230
|
+
for (let i = 0, n = elements.length; i < n; i++) {
|
|
231
|
+
let r = elements[i].getBoundingClientRect();
|
|
232
|
+
if (r.width && r.height) {
|
|
233
|
+
rects.push({
|
|
234
|
+
bottom: r.bottom - bounds.top,
|
|
235
|
+
left: r.left - bounds.left,
|
|
236
|
+
right: r.right - bounds.left,
|
|
237
|
+
top: r.top - bounds.top
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
let animate = !o.still && !reduced.matches, intensity = 0, target = { x: light.x, y: light.y }, wandering = false;
|
|
242
|
+
if (animate && pointer.inside) {
|
|
243
|
+
intensity = 1;
|
|
244
|
+
target = { x: pointer.x, y: pointer.y };
|
|
245
|
+
}
|
|
246
|
+
else if (animate && o.wander && now - pointer.at > IDLE) {
|
|
247
|
+
let t = now / 1000;
|
|
248
|
+
intensity = 0.85;
|
|
249
|
+
target = {
|
|
250
|
+
x: width / 2 + Math.cos(t * 0.23) * width * 0.34 + Math.sin(t * 0.57) * width * 0.08,
|
|
251
|
+
y: height / 2 + Math.sin(t * 0.31) * height * 0.3 + Math.cos(t * 0.49) * height * 0.08
|
|
252
|
+
};
|
|
253
|
+
wandering = true;
|
|
254
|
+
}
|
|
255
|
+
if (light.intensity < 0.01 && intensity > 0) {
|
|
256
|
+
light.x = target.x;
|
|
257
|
+
light.y = target.y;
|
|
258
|
+
}
|
|
259
|
+
let follow = 1 - Math.exp(-dt * (wandering ? 2.5 : 16)), fade = 1 - Math.exp(-dt * 6);
|
|
260
|
+
light.intensity += (intensity - light.intensity) * fade;
|
|
261
|
+
light.x += (target.x - light.x) * follow;
|
|
262
|
+
light.y += (target.y - light.y) * follow;
|
|
263
|
+
if (!animate) {
|
|
264
|
+
light.intensity = 0;
|
|
265
|
+
ripples.length = 0;
|
|
266
|
+
}
|
|
267
|
+
else if (ripples.length) {
|
|
268
|
+
ripples = ripples.filter((ripple) => (now - ripple.born) / 1000 < RIPPLE_LIFE);
|
|
269
|
+
}
|
|
270
|
+
let color = getComputedStyle(canvas).color, settled = Math.abs(intensity - light.intensity) < 0.002
|
|
271
|
+
&& (light.intensity < 0.002 || (Math.abs(target.x - light.x) < 0.1 && Math.abs(target.y - light.y) < 0.1)), signature = JSON.stringify([o, color, rects, pointer.inside && animate ? [pointer.x, pointer.y] : 0]);
|
|
272
|
+
if (settled && !wandering && !ripples.length && !(animate && o.breathe > 0) && signature === key) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
key = signature;
|
|
276
|
+
draw(o, rects, color, animate ? now : 0, animate);
|
|
277
|
+
}
|
|
278
|
+
function draw(o, rects, color, now, animate) {
|
|
279
|
+
let ctx = context, { dpr, height, width } = size, camera = o.camera, gap = Math.max(6, o.gap) * (camera ? Math.max(0.01, camera.zoom) : 1), zoom = camera ? Math.max(0.01, camera.zoom) : 1;
|
|
280
|
+
while (gap < 8) {
|
|
281
|
+
gap *= 2;
|
|
282
|
+
}
|
|
283
|
+
let cols = Math.floor(width / gap) + 2, rows = Math.floor(height / gap) + 2, n = cols * rows, i0 = camera ? Math.floor(-camera.x / gap) : 0, j0 = camera ? Math.floor(-camera.y / gap) : 0, ox = camera ? camera.x + i0 * gap : (width - (cols - 1) * gap) / 2, oy = camera ? camera.y + j0 * gap : (height - (rows - 1) * gap) / 2, padding = o.surfacePadding * zoom, clear = Math.max(2, gap * 0.8 + padding), band = Math.max(clear + gap, gap * 3 + padding), halo = band + gap * 1.5, push = Math.min(o.radius, gap * 5), peak = Math.min(1, o.brightness * 2.5), time = now / 1000;
|
|
284
|
+
grow(n);
|
|
285
|
+
for (let j = 0; j < rows; j++) {
|
|
286
|
+
for (let i = 0; i < cols; i++) {
|
|
287
|
+
let k = j * cols + i, x = ox + i * gap, y = oy + j * gap, glow = 0, shown = 1;
|
|
288
|
+
seed[k] = ((Math.sin((i + i0) * 12.9898 + (j + j0) * 78.233) * 43758.5453) % 1 + 1) % 1;
|
|
289
|
+
for (let r = 0, m = rects.length; r < m; r++) {
|
|
290
|
+
let rect = rects[r], cx = clamp(x, rect.left, rect.right), cy = clamp(y, rect.top, rect.bottom), dx = x - cx, dy = y - cy, d = Math.sqrt(dx * dx + dy * dy);
|
|
291
|
+
if (d === 0) {
|
|
292
|
+
shown = 0;
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
if (d < halo) {
|
|
296
|
+
glow = Math.max(glow, 1 - d / halo);
|
|
297
|
+
}
|
|
298
|
+
if (d < band) {
|
|
299
|
+
let scale = (clear + d * (band - clear) / band) / d;
|
|
300
|
+
x = cx + dx * scale;
|
|
301
|
+
y = cy + dy * scale;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
let dot = glow * glow * HALO, line = dot;
|
|
305
|
+
if (shown && light.intensity > 0.001) {
|
|
306
|
+
let dx = x - light.x, dy = y - light.y, d = Math.sqrt(dx * dx + dy * dy);
|
|
307
|
+
dot += falloff(d, o.radius) * light.intensity;
|
|
308
|
+
line += falloff(d, o.lineRadius) * light.intensity;
|
|
309
|
+
if (pointer.inside && o.pointerPush && d > 0.001 && d < push) {
|
|
310
|
+
let s = o.pointerPush * (1 - d / push) * (1 - d / push) * light.intensity / d;
|
|
311
|
+
x += dx * s;
|
|
312
|
+
y += dy * s;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
for (let r = 0, m = shown ? ripples.length : 0; r < m; r++) {
|
|
316
|
+
let ripple = ripples[r], age = (now - ripple.born) / 1000, dx = x - ripple.x, dy = y - ripple.y, d = Math.sqrt(dx * dx + dy * dy), life = 1 - age / RIPPLE_LIFE, wave = (d - age * RIPPLE_SPEED) / RIPPLE_WIDTH, strength = Math.exp(-wave * wave) * life * life;
|
|
317
|
+
if (d > 0.001) {
|
|
318
|
+
x += dx / d * o.ripplePush * strength;
|
|
319
|
+
y += dy / d * o.ripplePush * strength;
|
|
320
|
+
}
|
|
321
|
+
dot += strength * 0.8;
|
|
322
|
+
line += strength * 0.6;
|
|
323
|
+
}
|
|
324
|
+
let breath = animate && o.breathe > 0
|
|
325
|
+
? 1 - o.breathe * 0.5 * (1 + Math.sin(time * 1.3 + seed[k] * TAU))
|
|
326
|
+
: 1;
|
|
327
|
+
dot = Math.min(1, dot);
|
|
328
|
+
px[k] = x;
|
|
329
|
+
py[k] = y;
|
|
330
|
+
dotLight[k] = dot;
|
|
331
|
+
dotAlpha[k] = shown * breath * (o.base + (peak - o.base) * Math.max(0, dot));
|
|
332
|
+
lineLight[k] = shown * Math.min(1, line);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
let accent = rgb(o.accent), base = rgb(color), buckets = ALPHA_LEVELS * TINT_LEVELS, counts = new Uint32Array(buckets + 1), fills = [], tint = clamp(o.tint, 0, 1);
|
|
336
|
+
for (let t = 0; t < TINT_LEVELS; t++) {
|
|
337
|
+
let mix = tint * t / (TINT_LEVELS - 1);
|
|
338
|
+
fills[t] = `rgb(${Math.round(base[0] + (accent[0] - base[0]) * mix)} ${Math.round(base[1] + (accent[1] - base[1]) * mix)} ${Math.round(base[2] + (accent[2] - base[2]) * mix)})`;
|
|
339
|
+
}
|
|
340
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
341
|
+
ctx.clearRect(0, 0, width, height);
|
|
342
|
+
if (o.connected) {
|
|
343
|
+
let segments = 0;
|
|
344
|
+
for (let j = 0; j < rows; j++) {
|
|
345
|
+
for (let i = 0; i < cols; i++) {
|
|
346
|
+
let k = j * cols + i;
|
|
347
|
+
for (let neighbour of [i + 1 < cols ? k + 1 : -1, j + 1 < rows ? k + cols : -1]) {
|
|
348
|
+
if (neighbour < 0) {
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
let lit = Math.min(lineLight[k], lineLight[neighbour]), alpha = Math.round(lit * o.brightness * (ALPHA_LEVELS - 1));
|
|
352
|
+
if (alpha < 1) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
segment[segments * 2] = k;
|
|
356
|
+
segment[segments * 2 + 1] = neighbour;
|
|
357
|
+
segmentBucket[segments] = Math.round(lit * (TINT_LEVELS - 1)) * ALPHA_LEVELS + Math.min(alpha, ALPHA_LEVELS - 1);
|
|
358
|
+
counts[segmentBucket[segments] + 1]++;
|
|
359
|
+
segments++;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
sort(counts, segmentBucket, segmentOrder, segments);
|
|
364
|
+
ctx.lineWidth = 1;
|
|
365
|
+
for (let b = 0; b < buckets; b++) {
|
|
366
|
+
let from = counts[b], to = counts[b + 1];
|
|
367
|
+
if (from === to) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
ctx.beginPath();
|
|
371
|
+
for (let s = from; s < to; s++) {
|
|
372
|
+
let a = segment[segmentOrder[s] * 2], c = segment[segmentOrder[s] * 2 + 1];
|
|
373
|
+
ctx.moveTo(px[a], py[a]);
|
|
374
|
+
ctx.lineTo(px[c], py[c]);
|
|
375
|
+
}
|
|
376
|
+
ctx.globalAlpha = (b % ALPHA_LEVELS) / (ALPHA_LEVELS - 1);
|
|
377
|
+
ctx.strokeStyle = fills[Math.floor(b / ALPHA_LEVELS)];
|
|
378
|
+
ctx.stroke();
|
|
379
|
+
}
|
|
380
|
+
counts.fill(0);
|
|
381
|
+
}
|
|
382
|
+
let dots = 0;
|
|
383
|
+
for (let k = 0; k < n; k++) {
|
|
384
|
+
let alpha = Math.round(dotAlpha[k] * (ALPHA_LEVELS - 1));
|
|
385
|
+
if (alpha < 1) {
|
|
386
|
+
bucket[k] = buckets;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
bucket[k] = Math.round(dotLight[k] * (TINT_LEVELS - 1)) * ALPHA_LEVELS + Math.min(alpha, ALPHA_LEVELS - 1);
|
|
390
|
+
counts[bucket[k] + 1]++;
|
|
391
|
+
dots++;
|
|
392
|
+
}
|
|
393
|
+
sort(counts, bucket, order, n);
|
|
394
|
+
for (let b = 0; b < buckets; b++) {
|
|
395
|
+
let from = counts[b], to = counts[b + 1];
|
|
396
|
+
if (from === to) {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
let r = 0.85 + 0.45 * Math.floor(b / ALPHA_LEVELS) / (TINT_LEVELS - 1);
|
|
400
|
+
ctx.beginPath();
|
|
401
|
+
for (let s = from; s < to; s++) {
|
|
402
|
+
let k = order[s];
|
|
403
|
+
ctx.moveTo(px[k] + r, py[k]);
|
|
404
|
+
ctx.arc(px[k], py[k], r, 0, TAU);
|
|
405
|
+
}
|
|
406
|
+
ctx.globalAlpha = (b % ALPHA_LEVELS) / (ALPHA_LEVELS - 1);
|
|
407
|
+
ctx.fillStyle = fills[Math.floor(b / ALPHA_LEVELS)];
|
|
408
|
+
ctx.fill();
|
|
409
|
+
}
|
|
410
|
+
ctx.globalAlpha = 1;
|
|
411
|
+
}
|
|
412
|
+
function sort(counts, keys, into, n) {
|
|
413
|
+
for (let b = 1, m = counts.length; b < m; b++) {
|
|
414
|
+
counts[b] += counts[b - 1];
|
|
415
|
+
}
|
|
416
|
+
let cursor = counts.slice();
|
|
417
|
+
for (let i = 0; i < n; i++) {
|
|
418
|
+
if (keys[i] < counts.length - 1) {
|
|
419
|
+
into[cursor[keys[i]]++] = i;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function track(event) {
|
|
424
|
+
if (!host) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
let bounds = host.getBoundingClientRect();
|
|
428
|
+
pointer.at = performance.now();
|
|
429
|
+
pointer.inside = true;
|
|
430
|
+
pointer.x = event.clientX - bounds.left;
|
|
431
|
+
pointer.y = event.clientY - bounds.top;
|
|
432
|
+
}
|
|
433
|
+
return (() => {
|
|
434
|
+
let root_1a5zh7n6 = template_1a5zh7n7(), element_1a5zh7n8 = root_1a5zh7n6.firstChild, element_1a5zh7n9 = element_1a5zh7n8.firstElementChild.nextElementSibling;
|
|
435
|
+
template_u1.setProperties(element_1a5zh7n8, this?.attributes);
|
|
436
|
+
template_u1.setProperties(element_1a5zh7n8, rest);
|
|
437
|
+
template_u1.onconnect(element_1a5zh7n8, (element) => {
|
|
438
|
+
host = element;
|
|
439
|
+
canvas = element.querySelector('.surface-field-canvas');
|
|
440
|
+
context = canvas.getContext('2d');
|
|
441
|
+
observer = new IntersectionObserver((entries) => {
|
|
442
|
+
visible = entries[entries.length - 1].isIntersecting;
|
|
443
|
+
if (visible) {
|
|
444
|
+
start();
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
stop();
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
observer.observe(element);
|
|
451
|
+
});
|
|
452
|
+
template_u1.ondisconnect(element_1a5zh7n8, () => {
|
|
453
|
+
observer?.disconnect();
|
|
454
|
+
stop();
|
|
455
|
+
});
|
|
456
|
+
template_u1.delegate(element_1a5zh7n8, 'pointerdown', (event) => {
|
|
457
|
+
track(event);
|
|
458
|
+
if (event.button !== 0 || event.target.closest?.(SELECTOR)) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
ripples.push({ born: performance.now(), x: pointer.x, y: pointer.y });
|
|
462
|
+
if (ripples.length > 6) {
|
|
463
|
+
ripples.shift();
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
template_u1.on(element_1a5zh7n8, 'pointerleave', () => {
|
|
467
|
+
pointer.at = performance.now();
|
|
468
|
+
pointer.inside = false;
|
|
469
|
+
});
|
|
470
|
+
template_u1.delegate(element_1a5zh7n8, 'pointermove', track);
|
|
471
|
+
template_u1.slot(element_1a5zh7n9, content, 1);
|
|
472
|
+
return root_1a5zh7n6;
|
|
473
|
+
})();
|
|
474
|
+
}), { surface });
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
@layer components {.surface-field{--color:var(--color-text-500);--surface-background:var(--color-white-300);--surface-border-color:oklch(from var(--color-black-300) l c h / .06);--surface-border-radius:14px;--surface-box-shadow:0 1px 2px oklch(from var(--color-black-300) l c h / .06), 0 8px 24px -8px oklch(from var(--color-black-300) l c h / .14);--surface-box-shadow-active:0 2px 4px oklch(from var(--color-black-300) l c h / .08), 0 20px 40px -12px oklch(from var(--color-black-300) l c h / .24);--surface-grip-color:var(--color-text-300);--surface-handle-height:28px;--surface-lift-duration:.2s;isolation:isolate;position:relative}.surface-field-canvas{color:var(--color);pointer-events:none;z-index:0;width:100%;height:100%;transition:none;position:absolute;inset:0}.surface-field-content{z-index:1;height:100%;position:relative}.surface-field-surface{background:var(--surface-background);border-radius:var(--surface-border-radius);box-shadow:0 0 0 1px var(--surface-border-color), var(--surface-box-shadow);transition:box-shadow var(--surface-lift-duration) ease, scale var(--surface-lift-duration) ease;flex-direction:column;display:flex;position:absolute;top:0;left:0}.surface-field-surface.--active{box-shadow:0 0 0 1px var(--surface-border-color), var(--surface-box-shadow-active);scale:1.01}.surface-field-surface-content{flex:1;min-height:0;padding:0 16px 16px;overflow:hidden}.surface-field-surface-handle,.surface-field-surface-resize{color:var(--surface-grip-color);touch-action:none;background:0 0;border:0;padding:0}.surface-field-surface-handle:focus-visible,.surface-field-surface-resize:focus-visible{outline:2px solid oklch(from var(--color) l c h/.4);outline-offset:-2px}.surface-field-surface-handle{border-radius:var(--surface-border-radius) var(--surface-border-radius) 0 0;cursor:grab;height:var(--surface-handle-height);flex-shrink:0;width:100%;position:relative}.surface-field-surface-handle:before{content:"";opacity:.7;background:currentColor;border-radius:999px;width:18px;height:2px;position:absolute;top:50%;left:50%;translate:-50% -50%}.surface-field-surface.--active .surface-field-surface-handle{cursor:grabbing}.surface-field-surface-resize{border-radius:0 0 var(--surface-border-radius) 0;cursor:nwse-resize;opacity:.7;background:linear-gradient(135deg,#0000 45%,currentColor 45% 52%,#0000 52%) 4px 4px/12px 12px no-repeat,linear-gradient(135deg,#0000 70%,currentColor 70% 77%,#0000 77%) 4px 4px/12px 12px no-repeat;width:22px;height:22px;position:absolute;bottom:0;right:0}@media (prefers-reduced-motion:reduce){.surface-field-surface{transition:box-shadow var(--surface-lift-duration) ease}.surface-field-surface.--active{scale:1}}}
|
|
2
|
+
/*$vite$:1*/
|
package/package.json
CHANGED
package/src/components/index.ts
CHANGED
|
@@ -92,6 +92,7 @@ export { default as statusPicker } from './status-picker';
|
|
|
92
92
|
export { default as stickyHeader } from './sticky-header';
|
|
93
93
|
export { default as stickyStack } from './sticky-stack';
|
|
94
94
|
export { default as storyProgress } from './story-progress';
|
|
95
|
+
export { default as surfaceField } from './surface-field';
|
|
95
96
|
export { default as swipeDeck } from './swipe-deck';
|
|
96
97
|
export { default as switch } from './switch';
|
|
97
98
|
export { default as tagInput } from './tag-input';
|