@hina-ui/vue 1.7.10 → 1.7.12
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/index.js +901 -815
- package/dist/index.js.map +1 -1
- package/dist/shared/behavior/spoiler/painter.ts +87 -60
- package/dist/shared/behavior/spoiler/worklet.ts +7 -9
- package/dist/shared/lib/css-time.ts +6 -0
- package/dist/shared/lib/line-clamp.ts +8 -8
- package/dist/shared/primitives/pin-input/index.ts +394 -97
- package/dist/shared/styles/interaction.css +6 -2
- package/dist/shared/styles/line-clamp.css +6 -2
- package/dist/shared/styles/motion.css +7 -1
- package/dist/shared/styles/pin-input.css +84 -0
- package/dist/shared/styles/spoiler.css +6 -0
- package/dist/shared/styles/tokens.css +1 -0
- package/dist/shared/styles/typography.css +18 -3
- package/dist/shared/variants/pin-input.ts +26 -7
- package/dist/types/shared/src/behavior/spoiler/painter.d.ts +10 -9
- package/dist/types/shared/src/behavior/spoiler/painter.d.ts.map +1 -1
- package/dist/types/shared/src/behavior/spoiler/worklet.d.ts.map +1 -1
- package/dist/types/shared/src/lib/css-time.d.ts +2 -0
- package/dist/types/shared/src/lib/css-time.d.ts.map +1 -0
- package/dist/types/shared/src/lib/line-clamp.d.ts.map +1 -1
- package/dist/types/shared/src/primitives/pin-input/index.d.ts +35 -27
- package/dist/types/shared/src/primitives/pin-input/index.d.ts.map +1 -1
- package/dist/types/shared/src/variants/pin-input.d.ts +30 -10
- package/dist/types/shared/src/variants/pin-input.d.ts.map +1 -1
- package/dist/types/vue/src/components/image/composables/useImage.d.ts.map +1 -1
- package/dist/types/vue/src/components/pin-input/PinInput.vue.d.ts +2 -0
- package/dist/types/vue/src/components/pin-input/PinInput.vue.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/image/composables/useImage.ts +2 -1
- package/src/components/pin-input/PinInput.vue +103 -33
- package/dist/shared/primitives/pin-input/LICENSE +0 -21
- package/dist/types/vue/src/primitives/pin-input/PinInputInput.d.ts +0 -52
- package/dist/types/vue/src/primitives/pin-input/PinInputInput.d.ts.map +0 -1
- package/dist/types/vue/src/primitives/pin-input/PinInputRoot.d.ts +0 -149
- package/dist/types/vue/src/primitives/pin-input/PinInputRoot.d.ts.map +0 -1
- package/dist/types/vue/src/primitives/pin-input/context.d.ts +0 -20
- package/dist/types/vue/src/primitives/pin-input/context.d.ts.map +0 -1
- package/dist/types/vue/src/primitives/pin-input/index.d.ts +0 -4
- package/dist/types/vue/src/primitives/pin-input/index.d.ts.map +0 -1
- package/src/primitives/pin-input/LICENSE +0 -44
- package/src/primitives/pin-input/PinInputInput.ts +0 -218
- package/src/primitives/pin-input/PinInputRoot.ts +0 -136
- package/src/primitives/pin-input/context.ts +0 -24
- package/src/primitives/pin-input/index.ts +0 -3
|
@@ -32,26 +32,50 @@ const DEFAULTS = {
|
|
|
32
32
|
accent: '0 0% 20%',
|
|
33
33
|
} as const
|
|
34
34
|
|
|
35
|
+
export const SPOILER_WORKLET_INPUTS = [
|
|
36
|
+
'--hn-nz-t',
|
|
37
|
+
'--hn-nz-stop',
|
|
38
|
+
'--hn-nz-fade',
|
|
39
|
+
'--hn-nz-gap',
|
|
40
|
+
'--hn-nz-accent',
|
|
41
|
+
'--hn-nz-words',
|
|
42
|
+
'--hn-nz-density',
|
|
43
|
+
] as const
|
|
44
|
+
|
|
35
45
|
const BLOCK_MAX_TILE = 293
|
|
36
46
|
const INLINE_MAX_TILE = 333
|
|
37
47
|
const GAP_RATIO = 8
|
|
38
48
|
const DEFAULT_FADE_S = 1.5
|
|
49
|
+
const CLOCK_S = 3600
|
|
50
|
+
|
|
51
|
+
const painters = new WeakMap<Element, SpoilerPainter>()
|
|
52
|
+
let viewport: IntersectionObserver | undefined
|
|
53
|
+
|
|
54
|
+
function watchViewport() {
|
|
55
|
+
viewport ??= new IntersectionObserver(entries => {
|
|
56
|
+
for (const entry of entries) painters.get(entry.target)?.setInView(entry.isIntersecting)
|
|
57
|
+
})
|
|
58
|
+
return viewport
|
|
59
|
+
}
|
|
39
60
|
|
|
40
61
|
export class SpoilerPainter {
|
|
41
62
|
readonly el: HTMLElement
|
|
42
63
|
maxFPS: number = DEFAULTS.fps
|
|
43
64
|
|
|
44
|
-
private
|
|
45
|
-
private
|
|
46
|
-
private
|
|
47
|
-
private
|
|
48
|
-
private fadeS = 0
|
|
65
|
+
private clock: Animation | null = null
|
|
66
|
+
private settle: ReturnType<typeof setTimeout> | undefined
|
|
67
|
+
private running = false
|
|
68
|
+
private inView = false
|
|
49
69
|
private hidden = false
|
|
50
70
|
private destroyed = false
|
|
51
71
|
|
|
52
72
|
constructor(el: HTMLElement, options: SpoilerPainterOptions = {}) {
|
|
53
73
|
registerSpoilerWorklet()
|
|
54
74
|
this.el = el
|
|
75
|
+
painters.set(el, this)
|
|
76
|
+
watchViewport().observe(el)
|
|
77
|
+
this.setFade(0)
|
|
78
|
+
this.setTstop(null)
|
|
55
79
|
this.update(options)
|
|
56
80
|
}
|
|
57
81
|
|
|
@@ -68,13 +92,19 @@ export class SpoilerPainter {
|
|
|
68
92
|
}: SpoilerPainterOptions = {}) {
|
|
69
93
|
if (this.destroyed) return
|
|
70
94
|
|
|
71
|
-
|
|
95
|
+
const maxFPS = prefersReducedMotion() ? 0 : fps
|
|
96
|
+
if (maxFPS !== this.maxFPS) {
|
|
97
|
+
this.maxFPS = maxFPS
|
|
98
|
+
if (maxFPS > 0) this.clock?.effect?.updateTiming({ easing: this.easing() })
|
|
99
|
+
else this.stopClock()
|
|
100
|
+
}
|
|
72
101
|
|
|
73
102
|
this.el.style.setProperty('--hn-nz-density', String(density))
|
|
74
103
|
this.el.style.setProperty('--hn-nz-accent', accent)
|
|
75
104
|
|
|
76
105
|
const isBlockElement = getComputedStyle(this.el).getPropertyValue('display') !== 'inline'
|
|
77
106
|
this.useBackgroundStyle('auto', 'auto', false)
|
|
107
|
+
this.el.style.setProperty('--hn-nz-gap', '0px 0px')
|
|
78
108
|
|
|
79
109
|
if (isBlockElement) {
|
|
80
110
|
this.el.style.setProperty('--hn-nz-words', 'false')
|
|
@@ -109,23 +139,21 @@ export class SpoilerPainter {
|
|
|
109
139
|
this.el.style.background = `paint(hn-spoiler) ${repeatAndPosition} / ${w} ${h}`
|
|
110
140
|
}
|
|
111
141
|
|
|
112
|
-
private
|
|
113
|
-
|
|
114
|
-
this.el.style.setProperty('--hn-nz-fade', `${value}s`)
|
|
142
|
+
private easing() {
|
|
143
|
+
return `steps(${Math.round(CLOCK_S * this.maxFPS)})`
|
|
115
144
|
}
|
|
116
145
|
|
|
117
|
-
private
|
|
118
|
-
this.
|
|
119
|
-
this.
|
|
146
|
+
private time() {
|
|
147
|
+
const elapsed = Number(this.clock?.currentTime ?? 0) / 1000
|
|
148
|
+
return this.maxFPS > 0 ? Math.floor(elapsed * this.maxFPS) / this.maxFPS : 0
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private setFade(value: number) {
|
|
152
|
+
this.el.style.setProperty('--hn-nz-fade', `${value}s`)
|
|
120
153
|
}
|
|
121
154
|
|
|
122
155
|
private setTstop(value: number | null) {
|
|
123
|
-
this.
|
|
124
|
-
if (value !== null) {
|
|
125
|
-
this.el.style.setProperty('--hn-nz-stop', value.toFixed(3))
|
|
126
|
-
} else {
|
|
127
|
-
this.el.style.removeProperty('--hn-nz-stop')
|
|
128
|
-
}
|
|
156
|
+
this.el.style.setProperty('--hn-nz-stop', value === null ? 'Infinity' : value.toFixed(3))
|
|
129
157
|
}
|
|
130
158
|
|
|
131
159
|
private parseFade(value: number | boolean | undefined) {
|
|
@@ -133,13 +161,40 @@ export class SpoilerPainter {
|
|
|
133
161
|
return value === true || value === undefined ? DEFAULT_FADE_S : Number(value)
|
|
134
162
|
}
|
|
135
163
|
|
|
164
|
+
private sync() {
|
|
165
|
+
if (!this.clock) return
|
|
166
|
+
if (this.running && this.inView) this.clock.play()
|
|
167
|
+
else this.clock.pause()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private stopClock() {
|
|
171
|
+
clearTimeout(this.settle)
|
|
172
|
+
this.settle = undefined
|
|
173
|
+
this.clock?.cancel()
|
|
174
|
+
this.clock = null
|
|
175
|
+
this.running = false
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
setInView(value: boolean) {
|
|
179
|
+
if (this.destroyed || value === this.inView) return
|
|
180
|
+
this.inView = value
|
|
181
|
+
this.sync()
|
|
182
|
+
}
|
|
183
|
+
|
|
136
184
|
hide(animate?: number | boolean) {
|
|
137
185
|
if (this.destroyed) return
|
|
138
186
|
this.hidden = true
|
|
139
187
|
this.setFade(this.parseFade(animate))
|
|
140
188
|
this.setTstop(null)
|
|
141
|
-
this.
|
|
142
|
-
this.
|
|
189
|
+
this.stopClock()
|
|
190
|
+
if (this.maxFPS <= 0) return
|
|
191
|
+
this.clock = this.el.animate({ '--hn-nz-t': [0, CLOCK_S] } as PropertyIndexedKeyframes, {
|
|
192
|
+
duration: CLOCK_S * 1000,
|
|
193
|
+
easing: this.easing(),
|
|
194
|
+
iterations: Infinity,
|
|
195
|
+
})
|
|
196
|
+
this.running = true
|
|
197
|
+
this.sync()
|
|
143
198
|
}
|
|
144
199
|
|
|
145
200
|
reveal(animate?: number | boolean) {
|
|
@@ -147,51 +202,23 @@ export class SpoilerPainter {
|
|
|
147
202
|
this.hidden = false
|
|
148
203
|
const duration = this.parseFade(animate)
|
|
149
204
|
this.setFade(duration)
|
|
150
|
-
this.setTstop(this.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const shouldStop = this.tstop !== null && this.t > this.tstop + this.fadeS
|
|
157
|
-
|
|
158
|
-
if (this.maxFPS > 0 && !shouldStop) {
|
|
159
|
-
this.raf = requestAnimationFrame(this.frame)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const dt = now - this.t0
|
|
163
|
-
if (dt > 0 && dt < 1000 / this.maxFPS) return
|
|
164
|
-
|
|
165
|
-
this.setT(this.t + dt / 1000)
|
|
166
|
-
this.t0 = now
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
private startAnimation() {
|
|
170
|
-
this.t0 = performance.now()
|
|
171
|
-
this.frame(this.t0)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
private stopAnimation() {
|
|
175
|
-
if (this.raf !== null) {
|
|
176
|
-
cancelAnimationFrame(this.raf)
|
|
177
|
-
this.raf = null
|
|
205
|
+
this.setTstop(this.time())
|
|
206
|
+
clearTimeout(this.settle)
|
|
207
|
+
const stop = () => {
|
|
208
|
+
this.settle = undefined
|
|
209
|
+
this.running = false
|
|
210
|
+
this.sync()
|
|
178
211
|
}
|
|
212
|
+
if (duration <= 0) stop()
|
|
213
|
+
else this.settle = setTimeout(stop, duration * 1000 + 1000 / this.maxFPS)
|
|
179
214
|
}
|
|
180
215
|
|
|
181
216
|
destroy() {
|
|
182
217
|
this.destroyed = true
|
|
183
|
-
this.
|
|
218
|
+
this.stopClock()
|
|
219
|
+
viewport?.unobserve(this.el)
|
|
220
|
+
painters.delete(this.el)
|
|
184
221
|
this.el.style.removeProperty('background')
|
|
185
|
-
for (const prop of
|
|
186
|
-
'--hn-nz-t',
|
|
187
|
-
'--hn-nz-stop',
|
|
188
|
-
'--hn-nz-fade',
|
|
189
|
-
'--hn-nz-gap',
|
|
190
|
-
'--hn-nz-words',
|
|
191
|
-
'--hn-nz-density',
|
|
192
|
-
'--hn-nz-accent',
|
|
193
|
-
]) {
|
|
194
|
-
this.el.style.removeProperty(prop)
|
|
195
|
-
}
|
|
222
|
+
for (const prop of SPOILER_WORKLET_INPUTS) this.el.style.removeProperty(prop)
|
|
196
223
|
}
|
|
197
224
|
}
|
|
@@ -11,10 +11,6 @@ const workletMain = () => {
|
|
|
11
11
|
0x7fffffff
|
|
12
12
|
|
|
13
13
|
const pol2vec = (l: number, a = 0) => [l * M.cos(a), l * M.sin(a)] as const
|
|
14
|
-
const vecmag = ([x, y]: readonly number[]) => M.sqrt(x! * x! + y! * y!)
|
|
15
|
-
const vecnorm = ([x, y]: readonly number[], l = vecmag([x!, y!])) =>
|
|
16
|
-
l === 0 ? ([0, 0] as const) : ([x! / l, y! / l] as const)
|
|
17
|
-
|
|
18
14
|
const trapezoidalWave = (l: number, a: number, b: number) => {
|
|
19
15
|
const s = M.max(a, l - b)
|
|
20
16
|
return (t: number) => {
|
|
@@ -144,20 +140,21 @@ const workletMain = () => {
|
|
|
144
140
|
|
|
145
141
|
ctx.clearRect(0, 0, size.width, size.height)
|
|
146
142
|
|
|
143
|
+
const l = parseInt(accent[2] ?? '0')
|
|
144
|
+
const ldir = l > 50 ? -1 : 1
|
|
145
|
+
const hue = `hsl(${accent[0]} ${accent[1]} `
|
|
146
|
+
|
|
147
147
|
for (let i = 0; i < world.n; ++i) {
|
|
148
148
|
const x0 = hgap + wordDist(rand())
|
|
149
149
|
const y0 = rand(vgap, height - vgap)
|
|
150
150
|
const v0mag = rand(vmin, vmax)
|
|
151
151
|
const size0 = rand(1, 1 + sizedev)
|
|
152
152
|
|
|
153
|
-
const l = parseInt(accent[2] ?? '0')
|
|
154
|
-
const ldir = l > 50 ? -1 : 1
|
|
155
153
|
const lightness = M.floor(clamp(0, l + ldir * rand(0, 30), 100))
|
|
156
154
|
|
|
157
155
|
const v0 = pol2vec(v0mag, rand(0, M.PI * 2))
|
|
158
156
|
const vx0 = v0[0]
|
|
159
157
|
const vy0 = v0[1]
|
|
160
|
-
vecnorm(v0)
|
|
161
158
|
|
|
162
159
|
const shape = rand() > 0.5 ? 'square' : 'circle'
|
|
163
160
|
const lifetime = rand(0.3, 1.5)
|
|
@@ -175,12 +172,13 @@ const workletMain = () => {
|
|
|
175
172
|
const y = y0 + vy0 * t
|
|
176
173
|
|
|
177
174
|
const fade = animateFadeInOut(world, i, fadeDuration)
|
|
178
|
-
const alpha = fade * (1 - t / lifetime)
|
|
175
|
+
const alpha = M.round(fade * (1 - t / lifetime) * 100)
|
|
179
176
|
const psize = fade * (size0 * visibilityFn(t))
|
|
177
|
+
if (alpha <= 0 || psize <= 0) continue
|
|
180
178
|
|
|
179
|
+
ctx.fillStyle = `${hue}${lightness}% / ${alpha}%)`
|
|
181
180
|
for (const [wx, wy] of cycleBounds([x, y], [width, height], psize / 2)) {
|
|
182
181
|
ctx.beginPath()
|
|
183
|
-
ctx.fillStyle = `hsl(${accent[0]} ${accent[1]} ${lightness}% / ${M.round(alpha * 100)}%)`
|
|
184
182
|
if (shape === 'square') {
|
|
185
183
|
ctx.rect(wx!, wy!, psize, psize)
|
|
186
184
|
} else {
|
|
@@ -5,18 +5,17 @@ export function lineClampCount(lines: number | undefined) {
|
|
|
5
5
|
return Math.max(1, Math.floor(lines))
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
function
|
|
9
|
-
content.setAttribute('data-measuring',
|
|
10
|
-
const
|
|
11
|
-
const full = content.scrollHeight
|
|
8
|
+
function measure(content: HTMLElement, layout: 'clamped' | 'full') {
|
|
9
|
+
content.setAttribute('data-measuring', layout)
|
|
10
|
+
const size = { height: content.clientHeight, overflow: content.scrollHeight }
|
|
12
11
|
content.removeAttribute('data-measuring')
|
|
13
|
-
return
|
|
12
|
+
return size
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
export function measureLineClamp(content: HTMLElement) {
|
|
17
16
|
if (content.hasAttribute('data-animating')) return null
|
|
18
|
-
const {
|
|
19
|
-
return
|
|
17
|
+
const { height, overflow } = measure(content, 'clamped')
|
|
18
|
+
return overflow - height > 1
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
function toMs(value: string) {
|
|
@@ -33,7 +32,8 @@ export function animateLineClamp(content: HTMLElement, expanded: boolean, done:
|
|
|
33
32
|
const fade = running ? getComputedStyle(content).getPropertyValue(FADE) : ''
|
|
34
33
|
|
|
35
34
|
content.style.transition = 'none'
|
|
36
|
-
const
|
|
35
|
+
const collapsed = measure(content, 'clamped').height
|
|
36
|
+
const full = measure(content, 'full').height
|
|
37
37
|
const from = current ?? (expanded ? collapsed : full)
|
|
38
38
|
const to = expanded ? full : collapsed
|
|
39
39
|
content.toggleAttribute('data-expanded', !expanded)
|