@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.
Files changed (45) hide show
  1. package/dist/index.js +901 -815
  2. package/dist/index.js.map +1 -1
  3. package/dist/shared/behavior/spoiler/painter.ts +87 -60
  4. package/dist/shared/behavior/spoiler/worklet.ts +7 -9
  5. package/dist/shared/lib/css-time.ts +6 -0
  6. package/dist/shared/lib/line-clamp.ts +8 -8
  7. package/dist/shared/primitives/pin-input/index.ts +394 -97
  8. package/dist/shared/styles/interaction.css +6 -2
  9. package/dist/shared/styles/line-clamp.css +6 -2
  10. package/dist/shared/styles/motion.css +7 -1
  11. package/dist/shared/styles/pin-input.css +84 -0
  12. package/dist/shared/styles/spoiler.css +6 -0
  13. package/dist/shared/styles/tokens.css +1 -0
  14. package/dist/shared/styles/typography.css +18 -3
  15. package/dist/shared/variants/pin-input.ts +26 -7
  16. package/dist/types/shared/src/behavior/spoiler/painter.d.ts +10 -9
  17. package/dist/types/shared/src/behavior/spoiler/painter.d.ts.map +1 -1
  18. package/dist/types/shared/src/behavior/spoiler/worklet.d.ts.map +1 -1
  19. package/dist/types/shared/src/lib/css-time.d.ts +2 -0
  20. package/dist/types/shared/src/lib/css-time.d.ts.map +1 -0
  21. package/dist/types/shared/src/lib/line-clamp.d.ts.map +1 -1
  22. package/dist/types/shared/src/primitives/pin-input/index.d.ts +35 -27
  23. package/dist/types/shared/src/primitives/pin-input/index.d.ts.map +1 -1
  24. package/dist/types/shared/src/variants/pin-input.d.ts +30 -10
  25. package/dist/types/shared/src/variants/pin-input.d.ts.map +1 -1
  26. package/dist/types/vue/src/components/image/composables/useImage.d.ts.map +1 -1
  27. package/dist/types/vue/src/components/pin-input/PinInput.vue.d.ts +2 -0
  28. package/dist/types/vue/src/components/pin-input/PinInput.vue.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/src/components/image/composables/useImage.ts +2 -1
  31. package/src/components/pin-input/PinInput.vue +103 -33
  32. package/dist/shared/primitives/pin-input/LICENSE +0 -21
  33. package/dist/types/vue/src/primitives/pin-input/PinInputInput.d.ts +0 -52
  34. package/dist/types/vue/src/primitives/pin-input/PinInputInput.d.ts.map +0 -1
  35. package/dist/types/vue/src/primitives/pin-input/PinInputRoot.d.ts +0 -149
  36. package/dist/types/vue/src/primitives/pin-input/PinInputRoot.d.ts.map +0 -1
  37. package/dist/types/vue/src/primitives/pin-input/context.d.ts +0 -20
  38. package/dist/types/vue/src/primitives/pin-input/context.d.ts.map +0 -1
  39. package/dist/types/vue/src/primitives/pin-input/index.d.ts +0 -4
  40. package/dist/types/vue/src/primitives/pin-input/index.d.ts.map +0 -1
  41. package/src/primitives/pin-input/LICENSE +0 -44
  42. package/src/primitives/pin-input/PinInputInput.ts +0 -218
  43. package/src/primitives/pin-input/PinInputRoot.ts +0 -136
  44. package/src/primitives/pin-input/context.ts +0 -24
  45. package/src/primitives/pin-input/index.ts +0 -3
@@ -1,126 +1,423 @@
1
- import { arrowNavigation } from '../arrow-navigation'
2
-
3
- export type PinInputValue = Array<string | number | undefined>
1
+ export interface PinRange {
2
+ start: number
3
+ end: number
4
+ }
4
5
 
5
- const NUMBER = /^\d*$/
6
- const NON_NUMBER = /\D/g
7
- const NAVIGATION_KEYS = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End']
6
+ export interface PinInputSelection extends PinRange {
7
+ focused: boolean
8
+ }
8
9
 
9
- export function pinInputValues(value: unknown): PinInputValue {
10
- return Array.isArray(value) ? [...value] : []
10
+ export interface PinEdit extends PinRange {
11
+ value: string
11
12
  }
12
13
 
13
- export function isPinInputComplete(values: PinInputValue, count: number, numeric: boolean) {
14
- return values.filter(value => !!value || (numeric && value === 0)).length === count
14
+ export interface PinCell {
15
+ char: string
16
+ active: boolean
17
+ caret: boolean
15
18
  }
16
19
 
17
- export function stripPinNonNumbers(value: string) {
18
- return value.replace(NON_NUMBER, '')
20
+ export interface PinInputOptions {
21
+ length: () => number
22
+ numeric: () => boolean
23
+ value: () => string
24
+ rtl: () => boolean
25
+ cells: () => ArrayLike<Element>
26
+ change: (value: string) => void
27
+ select: (selection: PinInputSelection) => void
19
28
  }
20
29
 
21
- function removeTrailingEmptyStrings(values: PinInputValue) {
22
- while (values.length && values[values.length - 1] === '') values.pop()
23
- return values
30
+ export interface PinInputController {
31
+ sync: () => void
32
+ dispose: () => void
24
33
  }
25
34
 
26
- export function setPinValueAt(
27
- current: PinInputValue,
28
- index: number,
29
- value: string,
30
- numeric: boolean,
31
- ) {
32
- const values = [...current]
33
- if (numeric) {
34
- const number = +value
35
- if (value === '' || Number.isNaN(number)) delete values[index]
36
- else values[index] = number
37
- } else values[index] = value
38
- return removeTrailingEmptyStrings(values)
35
+ const FULL_WIDTH = /[!-~]/g
36
+ const UNSUPPORTED = /[\s\p{C}]|[\u{10000}-\u{10FFFF}]/gu
37
+ const NON_DIGIT = /\D/g
38
+ const IDLE: PinInputSelection = { focused: false, start: 0, end: 0 }
39
+
40
+ export function cleanPinText(text: string, numeric: boolean) {
41
+ const folded = text.replace(FULL_WIDTH, character =>
42
+ String.fromCharCode(character.charCodeAt(0) - 0xfee0),
43
+ )
44
+ return folded.replace(numeric ? NON_DIGIT : UNSUPPORTED, '')
39
45
  }
40
46
 
41
- export function spreadPinValue(
42
- current: PinInputValue,
43
- characters: string,
44
- index: number,
45
- count: number,
46
- numeric: boolean,
47
- ) {
48
- const values = [...current]
49
- const start = characters.length >= count ? 0 : index
50
- const end = Math.min(start + characters.length, count)
51
- const filled: number[] = []
52
- for (let at = start; at < end; at++) {
53
- const character = characters[at - start]!
54
- if (numeric) {
55
- const number = Number.parseInt(character)
56
- if (Number.isNaN(number)) continue
57
- values[at] = number
58
- } else values[at] = character
59
- filled.push(at)
60
- }
61
- return { values, filled, end }
47
+ export function pinInputValue(value: string, length: number) {
48
+ return value.slice(0, Math.max(0, length))
62
49
  }
63
50
 
64
- export type PinTextAction =
65
- | { kind: 'clear' }
66
- | { kind: 'filter'; value: string }
67
- | { kind: 'spread'; characters: string }
68
- | { kind: 'single'; character: string }
69
-
70
- export function resolvePinComposition(value: string, numeric: boolean): PinTextAction {
71
- const text = numeric ? stripPinNonNumbers(value) : value
72
- if (numeric && !text) return { kind: 'clear' }
73
- if (text.length > 1) return { kind: 'spread', characters: text }
74
- return { kind: 'single', character: text }
51
+ export function pinSelectionAt(
52
+ caret: number,
53
+ size: number,
54
+ length: number,
55
+ backward = false,
56
+ ): [number, number] {
57
+ if (caret >= size) return size < length || !size ? [size, size] : [size - 1, size]
58
+ if (backward && caret > 0) return [caret - 1, caret]
59
+ return [caret, caret + 1]
75
60
  }
76
61
 
77
- export function resolvePinInput(
78
- data: string | null | undefined,
79
- value: string,
62
+ export function editPinValue(
63
+ base: string,
64
+ raw: string,
65
+ selection: PinRange | null,
66
+ length: number,
80
67
  numeric: boolean,
81
- ): PinTextAction {
82
- if ((data?.length ?? 0) > 1) return { kind: 'spread', characters: value }
83
- if (numeric && !NUMBER.test(value)) return { kind: 'filter', value: stripPinNonNumbers(value) }
84
- return { kind: 'single', character: data || value.slice(-1) }
68
+ type = '',
69
+ ): PinEdit {
70
+ const limit = Math.min(base.length, raw.length)
71
+ let prefix = 0
72
+ while (prefix < limit && base[prefix] === raw[prefix]) prefix++
73
+ if (selection) prefix = Math.min(prefix, selection.start)
74
+ let suffix = 0
75
+ while (suffix < limit - prefix && base[base.length - 1 - suffix] === raw[raw.length - 1 - suffix])
76
+ suffix++
77
+ if (selection) suffix = Math.max(0, Math.min(suffix, base.length - selection.end))
78
+ const inserted = raw.slice(prefix, raw.length - suffix)
79
+ const removed = base.length - suffix - prefix
80
+ const kept = cleanPinText(inserted, numeric)
81
+ if (inserted && !kept)
82
+ return {
83
+ value: base,
84
+ start: selection?.start ?? prefix,
85
+ end: selection?.end ?? prefix + removed,
86
+ }
87
+ const value =
88
+ kept.length >= length
89
+ ? kept.slice(0, length)
90
+ : (base.slice(0, prefix) + kept + base.slice(prefix + Math.max(removed, kept.length))).slice(
91
+ 0,
92
+ length,
93
+ )
94
+ const caret = kept.length >= length ? length : prefix + kept.length
95
+ const [start, end] = pinSelectionAt(
96
+ caret,
97
+ value.length,
98
+ length,
99
+ !kept && type.includes('Backward'),
100
+ )
101
+ return { value, start, end }
85
102
  }
86
103
 
87
- export function resolvePinPaste(text: string, numeric: boolean) {
88
- return numeric ? stripPinNonNumbers(text) : text
104
+ export function movePinSelection(
105
+ selection: PinRange,
106
+ backward: boolean,
107
+ size: number,
108
+ length: number,
109
+ target: number | 'previous' | 'next',
110
+ extend: boolean,
111
+ ): PinRange & { backward: boolean } {
112
+ const last = Math.max(0, Math.min(size, length - 1))
113
+ const collapsed = selection.start === selection.end
114
+ const focus = collapsed || backward ? selection.start : selection.end - 1
115
+ const anchor = collapsed ? selection.start : backward ? selection.end - 1 : selection.start
116
+ const wide = selection.end - selection.start > 1
117
+ const stepped =
118
+ target === 'previous'
119
+ ? wide && !extend
120
+ ? selection.start
121
+ : focus - 1
122
+ : target === 'next'
123
+ ? wide && !extend
124
+ ? selection.end
125
+ : focus + 1
126
+ : target
127
+ const next = Math.max(0, Math.min(stepped, last))
128
+ if (!extend) {
129
+ const [start, end] = pinSelectionAt(next, size, length)
130
+ return { start, end, backward: false }
131
+ }
132
+ const start = Math.min(anchor, next)
133
+ const end = Math.min(Math.max(anchor, next) + 1, size)
134
+ return { start: Math.min(start, end), end, backward: next < anchor }
89
135
  }
90
136
 
91
- export function pinFocusRedirect(values: PinInputValue, count: number, index: number) {
92
- for (let at = 0; at < count; at++)
93
- if (values[at] === '' || values[at] === undefined) return at < index ? at : -1
94
- return -1
137
+ export function pinCells(value: string, length: number, selection: PinInputSelection): PinCell[] {
138
+ const collapsed = selection.start === selection.end
139
+ return Array.from({ length: Math.max(0, length) }, (_, index) => {
140
+ const char = value[index] ?? ''
141
+ const active =
142
+ selection.focused &&
143
+ (collapsed ? index === selection.start : index >= selection.start && index < selection.end)
144
+ return { char, active, caret: active && !char }
145
+ })
95
146
  }
96
147
 
97
- export function isPinNavigationKey(key: string) {
98
- return NAVIGATION_KEYS.includes(key)
148
+ export function pinCellAt(cells: ArrayLike<Element>, x: number) {
149
+ let nearest = 0
150
+ let distance = Infinity
151
+ for (let index = 0; index < cells.length; index++) {
152
+ const rect = cells[index]!.getBoundingClientRect()
153
+ const gap = x < rect.left ? rect.left - x : x > rect.right ? x - rect.right : 0
154
+ if (gap < distance) {
155
+ distance = gap
156
+ nearest = index
157
+ }
158
+ }
159
+ return nearest
99
160
  }
100
161
 
101
- export function navigatePin(
102
- event: KeyboardEvent,
103
- current: HTMLElement | null,
104
- inputs: HTMLElement[],
105
- dir: 'ltr' | 'rtl',
106
- ) {
107
- arrowNavigation(event, current, undefined, {
108
- itemsArray: inputs,
109
- focus: true,
110
- loop: false,
111
- arrowKeyOptions: 'horizontal',
112
- dir,
113
- })
162
+ function activeElement(document: Document) {
163
+ let active = document.activeElement
164
+ while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement
165
+ return active
114
166
  }
115
167
 
116
- export function pinPlaceholder(
117
- element: HTMLInputElement,
118
- active: Element | null,
119
- placeholder: string,
120
- ) {
121
- element.placeholder = !element.value && element === active ? '' : placeholder
122
- }
168
+ export function createPinInput(
169
+ input: HTMLInputElement,
170
+ options: PinInputOptions,
171
+ ): PinInputController {
172
+ const document = input.ownerDocument
173
+ const shown = () => pinInputValue(options.value(), options.length())
174
+ let text = shown()
175
+ let base: string | null = null
176
+ let anchor: PinRange | null = null
177
+ let live: PinEdit | null = null
178
+ let composing = false
179
+ let pointer = 'mouse'
180
+ let pressed = false
181
+ let away = false
182
+ let landing = false
183
+ let disposed = false
184
+ let view = IDLE
185
+
186
+ const focused = () => activeElement(document) === input
187
+
188
+ function range(): PinRange {
189
+ const start = Math.min(input.selectionStart ?? text.length, text.length)
190
+ return { start, end: Math.max(start, Math.min(input.selectionEnd ?? start, text.length)) }
191
+ }
192
+
193
+ function publish() {
194
+ const next = !focused()
195
+ ? IDLE
196
+ : { focused: true, ...(composing && live ? { start: live.start, end: live.end } : range()) }
197
+ if (next.focused === view.focused && next.start === view.start && next.end === view.end) return
198
+ view = next
199
+ options.select(next)
200
+ }
201
+
202
+ function place(start: number, end: number, backward = false) {
203
+ if (!focused()) return
204
+ if (input.selectionStart === start && input.selectionEnd === end) return
205
+ input.setSelectionRange(start, end, backward ? 'backward' : 'forward')
206
+ }
207
+
208
+ function settle() {
209
+ const { start, end } = range()
210
+ if (start !== end) return
211
+ const [from, to] = pinSelectionAt(start, text.length, options.length())
212
+ place(from, to)
213
+ }
214
+
215
+ function emit(value: string) {
216
+ if (value === text) return
217
+ text = value
218
+ options.change(value)
219
+ queueMicrotask(sync)
220
+ }
221
+
222
+ function sync() {
223
+ if (disposed) return
224
+ const next = shown()
225
+ if (next !== text) {
226
+ text = next
227
+ composing = false
228
+ base = anchor = live = null
229
+ } else if (composing || input.value === text) return
230
+ if (input.value !== text) {
231
+ const caret = Math.min(input.selectionStart ?? text.length, text.length)
232
+ input.value = text
233
+ const [start, end] = pinSelectionAt(caret, text.length, options.length())
234
+ place(start, end)
235
+ }
236
+ publish()
237
+ }
238
+
239
+ function derive(type: string): PinEdit {
240
+ const length = options.length()
241
+ const numeric = options.numeric()
242
+ if (type.startsWith('history')) {
243
+ const value = cleanPinText(input.value, numeric).slice(0, length)
244
+ const caret = Math.min(input.selectionStart ?? value.length, value.length)
245
+ const [start, end] = pinSelectionAt(caret, value.length, length)
246
+ return { value, start, end }
247
+ }
248
+ return editPinValue(base ?? text, input.value, anchor, length, numeric, type)
249
+ }
250
+
251
+ function preview(type: string) {
252
+ composing = true
253
+ base ??= text
254
+ live = derive(type)
255
+ emit(live.value)
256
+ publish()
257
+ }
258
+
259
+ function commit(type: string) {
260
+ const edit = derive(type)
261
+ composing = false
262
+ base = anchor = live = null
263
+ if (input.value !== edit.value) input.value = edit.value
264
+ emit(edit.value)
265
+ place(edit.start, edit.end, type.includes('Backward'))
266
+ publish()
267
+ }
123
268
 
124
- export function pinInputLabel(index: number, count: number) {
125
- return `pin input ${index + 1} of ${count}`
269
+ function onBeforeInput(event: InputEvent) {
270
+ landing = false
271
+ if (composing || event.isComposing) return
272
+ if (
273
+ event.inputType === 'insertText' &&
274
+ event.data &&
275
+ !cleanPinText(event.data, options.numeric())
276
+ ) {
277
+ event.preventDefault()
278
+ return
279
+ }
280
+ base = input.value
281
+ anchor = range()
282
+ }
283
+
284
+ function onInput(event: Event) {
285
+ const { inputType = '', isComposing } = event as InputEvent
286
+ if (composing || isComposing) preview(inputType)
287
+ else if (base !== null || input.value !== text) commit(inputType)
288
+ }
289
+
290
+ function onCompositionStart() {
291
+ landing = false
292
+ composing = true
293
+ base = input.value
294
+ anchor = range()
295
+ }
296
+
297
+ function onCompositionEnd() {
298
+ if (composing) commit('insertCompositionText')
299
+ }
300
+
301
+ function land() {
302
+ const size = text.length
303
+ if (size && size >= options.length()) place(0, size)
304
+ else place(size, size)
305
+ }
306
+
307
+ function onSelection() {
308
+ if (composing) return
309
+ if (focused()) {
310
+ if (landing) land()
311
+ else settle()
312
+ }
313
+ publish()
314
+ }
315
+
316
+ function onFocus() {
317
+ landing = !pressed && !away && !composing
318
+ if (landing) land()
319
+ away = false
320
+ publish()
321
+ }
322
+
323
+ function onBlur() {
324
+ away = !document.hasFocus()
325
+ pressed = false
326
+ landing = false
327
+ if (composing) commit('insertCompositionText')
328
+ publish()
329
+ }
330
+
331
+ function onKeydown(event: KeyboardEvent) {
332
+ landing = false
333
+ if (composing || event.isComposing || event.keyCode === 229) return
334
+ if (event.metaKey || event.ctrlKey || event.altKey) return
335
+ const { key } = event
336
+ const forward = options.rtl() ? 'ArrowLeft' : 'ArrowRight'
337
+ const back = options.rtl() ? 'ArrowRight' : 'ArrowLeft'
338
+ const target =
339
+ key === forward
340
+ ? 'next'
341
+ : key === back
342
+ ? 'previous'
343
+ : key === 'Home'
344
+ ? 0
345
+ : key === 'End'
346
+ ? options.length()
347
+ : null
348
+ if (target === null) return
349
+ event.preventDefault()
350
+ const next = movePinSelection(
351
+ range(),
352
+ input.selectionDirection === 'backward',
353
+ text.length,
354
+ options.length(),
355
+ target,
356
+ event.shiftKey,
357
+ )
358
+ place(next.start, next.end, next.backward)
359
+ publish()
360
+ }
361
+
362
+ function pick(event: MouseEvent) {
363
+ if (composing) return
364
+ if (event.detail > 1 && text) place(0, text.length)
365
+ else {
366
+ const index = Math.min(pinCellAt(options.cells(), event.clientX), text.length)
367
+ const [start, end] = pinSelectionAt(index, text.length, options.length())
368
+ place(start, end)
369
+ }
370
+ publish()
371
+ }
372
+
373
+ function onPointerDown(event: PointerEvent) {
374
+ landing = false
375
+ pointer = event.pointerType
376
+ pressed = pointer !== 'mouse'
377
+ }
378
+
379
+ function onMouseDown(event: MouseEvent) {
380
+ if (event.button !== 0 || pointer !== 'mouse' || input.disabled) return
381
+ event.preventDefault()
382
+ pressed = true
383
+ input.focus()
384
+ pressed = false
385
+ pick(event)
386
+ }
387
+
388
+ function onClick(event: MouseEvent) {
389
+ const touched = pressed
390
+ pressed = false
391
+ if (touched && pointer !== 'mouse') pick(event)
392
+ }
393
+
394
+ const listeners: Array<[EventTarget, string, (event: never) => void]> = [
395
+ [input, 'beforeinput', onBeforeInput],
396
+ [input, 'input', onInput],
397
+ [input, 'compositionstart', onCompositionStart],
398
+ [input, 'compositionend', onCompositionEnd],
399
+ [input, 'select', onSelection],
400
+ [input, 'focus', onFocus],
401
+ [input, 'blur', onBlur],
402
+ [input, 'keydown', onKeydown],
403
+ [input, 'pointerdown', onPointerDown],
404
+ [input, 'mousedown', onMouseDown],
405
+ [input, 'click', onClick],
406
+ [document, 'selectionchange', onSelection],
407
+ ]
408
+ for (const [target, type, listener] of listeners)
409
+ target.addEventListener(type, listener as EventListener)
410
+
411
+ if (input.value !== text) input.value = text
412
+ if (focused()) settle()
413
+ publish()
414
+
415
+ return {
416
+ sync,
417
+ dispose() {
418
+ disposed = true
419
+ for (const [target, type, listener] of listeners)
420
+ target.removeEventListener(type, listener as EventListener)
421
+ },
422
+ }
126
423
  }
@@ -132,7 +132,9 @@
132
132
  @media (hover: hover) {
133
133
  &:hover:not(:disabled):not([data-disabled]):not(
134
134
  :has(:is(input, textarea, [data-hn-segment]):focus-visible)
135
- ):not([data-invalid]) {
135
+ ):not([data-invalid]),
136
+ :where([data-hn-field-group]:hover:not(:has(input:focus-visible)))
137
+ &:not([data-disabled]):not([data-invalid]) {
136
138
  background-color: color-mix(
137
139
  in oklab,
138
140
  var(--hn-field-bg, var(--hn-surface)) calc(100% - var(--hn-state-hover-opacity) * 100%),
@@ -142,7 +144,9 @@
142
144
 
143
145
  &[data-invalid]:hover:not(:disabled):not([data-disabled]):not(
144
146
  :has(:is(input, textarea, [data-hn-segment]):focus-visible)
145
- ) {
147
+ ),
148
+ :where([data-hn-field-group]:hover:not(:has(input:focus-visible)))
149
+ &[data-invalid]:not([data-disabled]) {
146
150
  background-color: color-mix(
147
151
  in oklab,
148
152
  color-mix(
@@ -9,14 +9,18 @@
9
9
  min-width: 0;
10
10
  }
11
11
 
12
+ .hn-line-clamp {
13
+ display: flow-root;
14
+ }
15
+
12
16
  .hn-line-clamp:not([data-expanded]),
13
17
  .hn-line-clamp[data-animating],
14
18
  .hn-line-clamp[data-measuring] {
15
19
  overflow: hidden;
16
20
  }
17
21
 
18
- .hn-line-clamp:not([data-expanded]):not([data-animating]),
19
- .hn-line-clamp[data-measuring] {
22
+ .hn-line-clamp:not([data-expanded]):not([data-animating]):not([data-measuring='full']),
23
+ .hn-line-clamp[data-measuring='clamped'] {
20
24
  display: -webkit-box;
21
25
  -webkit-box-orient: vertical;
22
26
  -webkit-line-clamp: var(--hn-line-clamp, 3);
@@ -510,8 +510,14 @@
510
510
  }
511
511
  }
512
512
 
513
+ @layer components {
514
+ .hn-anim-collapse {
515
+ display: flow-root;
516
+ }
517
+ }
518
+
513
519
  @utility hn-anim-collapse {
514
- overflow: hidden;
520
+ overflow: clip;
515
521
  &[data-state='open'] {
516
522
  animation: hn-collapse-in var(--hn-duration-base) var(--hn-ease-move);
517
523
  }
@@ -0,0 +1,84 @@
1
+ .hn-pin-native {
2
+ position: absolute;
3
+ inset: 0;
4
+ width: 100%;
5
+ height: 100%;
6
+ margin: 0;
7
+ padding: 0;
8
+ border: 0;
9
+ border-radius: 0;
10
+ outline: 0;
11
+ background: transparent;
12
+ box-shadow: none;
13
+ color: transparent;
14
+ caret-color: transparent;
15
+ cursor: text;
16
+ font-family: ui-monospace, monospace;
17
+ font-size: var(--hn-pin-size, 2.5rem);
18
+ line-height: 1;
19
+ letter-spacing: -0.5em;
20
+ appearance: none;
21
+ forced-color-adjust: none;
22
+ }
23
+
24
+ @supports (-webkit-touch-callout: none) {
25
+ .hn-pin-native {
26
+ left: -1px;
27
+ right: 1px;
28
+ font-weight: 100;
29
+ font-stretch: ultra-condensed;
30
+ font-optical-sizing: none;
31
+ letter-spacing: -0.6em;
32
+ }
33
+ }
34
+
35
+ .hn-pin-native:disabled {
36
+ cursor: not-allowed;
37
+ }
38
+
39
+ .hn-pin-native::selection {
40
+ background: transparent;
41
+ color: transparent;
42
+ }
43
+
44
+ .hn-pin-native::-ms-reveal {
45
+ display: none;
46
+ }
47
+
48
+ .hn-pin-native::-ms-clear {
49
+ display: none;
50
+ }
51
+
52
+ .hn-pin-native::-webkit-credentials-auto-fill-button,
53
+ .hn-pin-native::-webkit-contacts-auto-fill-button,
54
+ .hn-pin-native::-webkit-strong-password-auto-fill-button,
55
+ .hn-pin-native::-webkit-caps-lock-indicator {
56
+ display: none !important;
57
+ visibility: hidden;
58
+ pointer-events: none;
59
+ }
60
+
61
+ .hn-pin-native:-webkit-autofill {
62
+ opacity: 0;
63
+ }
64
+
65
+ .hn-pin-native:autofill {
66
+ opacity: 0;
67
+ }
68
+
69
+ [data-hn-pin-cell]::before {
70
+ content: '\200b';
71
+ }
72
+
73
+ .hn-pin-caret {
74
+ width: 1px;
75
+ height: 1.25em;
76
+ background-color: var(--hn-fg-default);
77
+ animation: hn-pin-caret 1.2s step-end infinite;
78
+ }
79
+
80
+ @keyframes hn-pin-caret {
81
+ 50% {
82
+ opacity: 0;
83
+ }
84
+ }
@@ -1,3 +1,9 @@
1
+ @property --hn-nz-t {
2
+ syntax: '<number>';
3
+ inherits: false;
4
+ initial-value: 0;
5
+ }
6
+
1
7
  @property --hn-iris-len {
2
8
  syntax: '<percentage>';
3
9
  inherits: false;
@@ -13,6 +13,7 @@
13
13
  @import './stepper.css';
14
14
  @import './float-button.css';
15
15
  @import './line-clamp.css';
16
+ @import './pin-input.css';
16
17
  @import './masonry.css';
17
18
  @import './carousel.css';
18
19
  @import './base.css';