@hina-ui/vue 1.2.0 → 1.3.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.
Files changed (99) hide show
  1. package/dist/index.js +880 -347
  2. package/dist/index.js.map +1 -1
  3. package/dist/types/components/alert-dialog/AlertDialog.vue.d.ts +6 -2
  4. package/dist/types/components/alert-dialog/AlertDialog.vue.d.ts.map +1 -1
  5. package/dist/types/components/alert-dialog/alert-dialog.variants.d.ts +5 -0
  6. package/dist/types/components/alert-dialog/alert-dialog.variants.d.ts.map +1 -0
  7. package/dist/types/components/alert-dialog/composables/useAlertDialogConfirm.d.ts +9 -0
  8. package/dist/types/components/alert-dialog/composables/useAlertDialogConfirm.d.ts.map +1 -0
  9. package/dist/types/components/alert-dialog/composables/useConfirmDelay.d.ts +3 -0
  10. package/dist/types/components/alert-dialog/composables/useConfirmDelay.d.ts.map +1 -0
  11. package/dist/types/components/anchor/Anchor.vue.d.ts.map +1 -1
  12. package/dist/types/components/code-block/CodeBlock.vue.d.ts.map +1 -1
  13. package/dist/types/components/code-block/code-block.variants.d.ts +6 -0
  14. package/dist/types/components/code-block/code-block.variants.d.ts.map +1 -0
  15. package/dist/types/components/code-block/composables/useCodeHighlight.d.ts +7 -0
  16. package/dist/types/components/code-block/composables/useCodeHighlight.d.ts.map +1 -0
  17. package/dist/types/components/dialog/Dialog.vue.d.ts.map +1 -1
  18. package/dist/types/components/dialog/ModalContent.vue.d.ts +21 -0
  19. package/dist/types/components/dialog/ModalContent.vue.d.ts.map +1 -0
  20. package/dist/types/components/dropdown-menu/DropdownMenu.vue.d.ts +18 -6
  21. package/dist/types/components/dropdown-menu/DropdownMenu.vue.d.ts.map +1 -1
  22. package/dist/types/components/file-upload/FileUpload.vue.d.ts +1 -1
  23. package/dist/types/components/file-upload/FileUpload.vue.d.ts.map +1 -1
  24. package/dist/types/components/highlight/Highlight.vue.d.ts +2 -0
  25. package/dist/types/components/highlight/Highlight.vue.d.ts.map +1 -1
  26. package/dist/types/components/highlight/composables/useHighlightMotion.d.ts +10 -0
  27. package/dist/types/components/highlight/composables/useHighlightMotion.d.ts.map +1 -0
  28. package/dist/types/components/image/Image.vue.d.ts +5 -2
  29. package/dist/types/components/image/Image.vue.d.ts.map +1 -1
  30. package/dist/types/components/image/composables/useImage.d.ts.map +1 -1
  31. package/dist/types/components/image/image.variants.d.ts +23 -1
  32. package/dist/types/components/image/image.variants.d.ts.map +1 -1
  33. package/dist/types/components/lightbox/LightboxThumbs.vue.d.ts.map +1 -1
  34. package/dist/types/components/lightbox/composables/useLightboxMotion.d.ts.map +1 -1
  35. package/dist/types/components/lightbox/composables/useLightboxPose.d.ts.map +1 -1
  36. package/dist/types/components/lightbox/utils/clip.d.ts +16 -0
  37. package/dist/types/components/lightbox/utils/clip.d.ts.map +1 -0
  38. package/dist/types/components/lightbox/utils/pose.d.ts +2 -2
  39. package/dist/types/components/lightbox/utils/pose.d.ts.map +1 -1
  40. package/dist/types/components/popover/Popover.vue.d.ts +19 -6
  41. package/dist/types/components/popover/Popover.vue.d.ts.map +1 -1
  42. package/dist/types/components/popover/PopoverContent.vue.d.ts +29 -0
  43. package/dist/types/components/popover/PopoverContent.vue.d.ts.map +1 -0
  44. package/dist/types/components/popover/composables/usePopoverCloseFocus.d.ts +2 -0
  45. package/dist/types/components/popover/composables/usePopoverCloseFocus.d.ts.map +1 -0
  46. package/dist/types/components/scroll-area/ScrollArea.vue.d.ts.map +1 -1
  47. package/dist/types/components/scroll-area/composables/useLayerLock.d.ts.map +1 -1
  48. package/dist/types/components/scroll-area/composables/useScrollFocus.d.ts +17 -0
  49. package/dist/types/components/scroll-area/composables/useScrollFocus.d.ts.map +1 -0
  50. package/dist/types/components/segmented-control/SegmentedControl.vue.d.ts.map +1 -1
  51. package/dist/types/components/tabs/TabsTrigger.vue.d.ts.map +1 -1
  52. package/dist/types/index.d.ts +2 -0
  53. package/dist/types/index.d.ts.map +1 -1
  54. package/dist/types/lib/anchored-overlay.d.ts +19 -0
  55. package/dist/types/lib/anchored-overlay.d.ts.map +1 -0
  56. package/dist/types/lib/dialog-focus.d.ts +2 -0
  57. package/dist/types/lib/dialog-focus.d.ts.map +1 -0
  58. package/dist/types/lib/overlay-portal.d.ts +6 -0
  59. package/dist/types/lib/overlay-portal.d.ts.map +1 -0
  60. package/package.json +1 -1
  61. package/src/components/alert-dialog/AlertDialog.vue +31 -40
  62. package/src/components/alert-dialog/alert-dialog.variants.ts +17 -0
  63. package/src/components/alert-dialog/composables/useAlertDialogConfirm.ts +29 -0
  64. package/src/components/alert-dialog/composables/useConfirmDelay.ts +37 -0
  65. package/src/components/anchor/Anchor.vue +1 -0
  66. package/src/components/code-block/CodeBlock.vue +15 -30
  67. package/src/components/code-block/code-block.variants.ts +21 -0
  68. package/src/components/code-block/composables/useCodeHighlight.ts +30 -0
  69. package/src/components/dialog/Dialog.vue +7 -4
  70. package/src/components/dialog/ModalContent.vue +19 -0
  71. package/src/components/dropdown-menu/DropdownMenu.vue +16 -4
  72. package/src/components/highlight/Highlight.vue +5 -4
  73. package/src/components/highlight/composables/useHighlightMotion.ts +22 -0
  74. package/src/components/image/Image.vue +9 -12
  75. package/src/components/image/composables/useImage.ts +6 -3
  76. package/src/components/image/image.variants.ts +13 -1
  77. package/src/components/lightbox/LightboxThumbs.vue +1 -0
  78. package/src/components/lightbox/composables/useLightboxMotion.ts +3 -1
  79. package/src/components/lightbox/composables/useLightboxPose.ts +5 -2
  80. package/src/components/lightbox/utils/clip.ts +117 -0
  81. package/src/components/lightbox/utils/pose.ts +23 -13
  82. package/src/components/popover/Popover.vue +26 -6
  83. package/src/components/popover/PopoverContent.vue +25 -0
  84. package/src/components/popover/composables/usePopoverCloseFocus.ts +17 -0
  85. package/src/components/scroll-area/ScrollArea.vue +7 -5
  86. package/src/components/scroll-area/composables/useLayerLock.ts +8 -9
  87. package/src/components/scroll-area/composables/useScrollFocus.ts +39 -0
  88. package/src/components/scroll-area/composables/useWheelRedirect.ts +1 -1
  89. package/src/components/segmented-control/SegmentedControl.vue +6 -1
  90. package/src/components/skeleton/LICENSE +24 -0
  91. package/src/components/tabs/TabsTrigger.vue +6 -1
  92. package/src/index.ts +2 -0
  93. package/src/lib/anchored-overlay.ts +107 -0
  94. package/src/lib/dialog-focus.ts +13 -0
  95. package/src/lib/overlay-portal.ts +8 -0
  96. package/src/styles/interaction.css +26 -3
  97. package/src/styles/motion.css +14 -2
  98. package/src/styles/semantic.css +5 -1
  99. package/src/styles/typography.css +9 -4
@@ -0,0 +1,107 @@
1
+ import { computed, nextTick, shallowRef, watch, type Ref } from 'vue'
2
+ import { useForwardExpose, type DropdownMenuContentEmits } from 'reka-ui'
3
+
4
+ export function useAnchoredOverlay(
5
+ props: { anchor?: HTMLElement | null; modal: boolean },
6
+ open: Ref<boolean | undefined>,
7
+ emit: <K extends keyof DropdownMenuContentEmits>(
8
+ event: K,
9
+ ...args: DropdownMenuContentEmits[K]
10
+ ) => void,
11
+ openAutoFocus?: (event: Event) => void,
12
+ ) {
13
+ const { forwardRef: trigger, currentElement: triggerElement } = useForwardExpose()
14
+ const retainedAnchor = shallowRef<HTMLElement>()
15
+ const reference = computed(
16
+ () =>
17
+ props.anchor ??
18
+ (open.value
19
+ ? (triggerElement.value ?? retainedAnchor.value)
20
+ : (retainedAnchor.value ?? triggerElement.value)),
21
+ )
22
+ const visible = computed({
23
+ get: () => !!open.value && !!(props.anchor || triggerElement.value),
24
+ set: value => {
25
+ open.value = value
26
+ },
27
+ })
28
+
29
+ watch(
30
+ () => props.anchor,
31
+ anchor => {
32
+ if (anchor) retainedAnchor.value = anchor
33
+ },
34
+ { immediate: true, flush: 'sync' },
35
+ )
36
+
37
+ let previousFocus: HTMLElement | null = null
38
+ let interactedOutside = false
39
+
40
+ function once<E extends Event>(handler: (event: E) => void) {
41
+ const handled = new WeakSet<E>()
42
+ return (event: E) => {
43
+ if (handled.has(event)) return
44
+ handled.add(event)
45
+ handler(event)
46
+ }
47
+ }
48
+
49
+ watch(
50
+ visible,
51
+ active => {
52
+ if (!active) return
53
+ const document = props.anchor?.ownerDocument ?? triggerElement.value?.ownerDocument
54
+ previousFocus = document?.activeElement as HTMLElement | null
55
+ interactedOutside = false
56
+ },
57
+ { immediate: true, flush: 'sync' },
58
+ )
59
+
60
+ const onCloseAutoFocus = once((event: Event) => {
61
+ emit('closeAutoFocus', event)
62
+ const cancelled = event.defaultPrevented
63
+ event.preventDefault()
64
+ const panel = event.target as HTMLElement
65
+ const target = triggerElement.value ?? previousFocus
66
+ const restore = !cancelled && !interactedOutside
67
+ nextTick(() => {
68
+ const active = panel.ownerDocument.activeElement
69
+ if (
70
+ restore &&
71
+ !visible.value &&
72
+ target?.isConnected &&
73
+ (active === panel.ownerDocument.body || panel.contains(active))
74
+ )
75
+ target.focus({ preventScroll: true })
76
+ if (!visible.value) retainedAnchor.value = undefined
77
+ })
78
+ })
79
+
80
+ const onInteractOutside = once((event: DropdownMenuContentEmits['interactOutside'][0]) => {
81
+ if (!triggerElement.value && props.anchor?.contains(event.target as Node))
82
+ event.preventDefault()
83
+ emit('interactOutside', event)
84
+ const original = event.detail.originalEvent
85
+ const rightClick =
86
+ 'button' in original && (original.button === 2 || (original.button === 0 && original.ctrlKey))
87
+ if (!event.defaultPrevented && (!props.modal || rightClick)) interactedOutside = true
88
+ })
89
+
90
+ return {
91
+ trigger,
92
+ reference,
93
+ visible,
94
+ events: {
95
+ ...(openAutoFocus ? { onOpenAutoFocus: once(openAutoFocus) } : {}),
96
+ onCloseAutoFocus,
97
+ onInteractOutside,
98
+ onPointerDownOutside: once((event: DropdownMenuContentEmits['pointerDownOutside'][0]) =>
99
+ emit('pointerDownOutside', event),
100
+ ),
101
+ onFocusOutside: once((event: DropdownMenuContentEmits['focusOutside'][0]) =>
102
+ emit('focusOutside', event),
103
+ ),
104
+ onEscapeKeyDown: once((event: KeyboardEvent) => emit('escapeKeyDown', event)),
105
+ },
106
+ }
107
+ }
@@ -0,0 +1,13 @@
1
+ import { injectDialogRootContext } from 'reka-ui'
2
+
3
+ export function useDialogCloseFocus(onCloseAutoFocus: (event: Event) => void) {
4
+ const context = injectDialogRootContext()
5
+
6
+ return (event: Event) => {
7
+ onCloseAutoFocus(event)
8
+ if (event.defaultPrevented) return
9
+ event.preventDefault()
10
+ const target = context.triggerElement.value
11
+ if (target?.isConnected) target.focus({ preventScroll: true })
12
+ }
13
+ }
@@ -0,0 +1,8 @@
1
+ import { computed, shallowRef, type ComponentPublicInstance, type Ref } from 'vue'
2
+
3
+ export function useOverlayPortal(open: Ref<boolean | undefined>) {
4
+ const content = shallowRef<ComponentPublicInstance | null>(null)
5
+ const present = computed(() => !!open.value || !!content.value)
6
+
7
+ return { content, present }
8
+ }
@@ -266,20 +266,43 @@
266
266
  }
267
267
 
268
268
  @utility hn-skeleton {
269
- background-color: var(--hn-bg-subtle);
269
+ position: relative;
270
+ overflow: hidden;
271
+ background-color: var(--hn-skeleton-bg);
270
272
  border-radius: var(--hn-radius-md);
271
273
  color: transparent;
272
274
  cursor: default;
273
275
  pointer-events: none;
274
276
  user-select: none;
275
- animation: hn-skeleton var(--hn-skeleton-duration) var(--hn-ease-move) infinite;
277
+
278
+ &::after {
279
+ content: '';
280
+ position: absolute;
281
+ inset: 0;
282
+ z-index: 1;
283
+ transform: translateX(-100%);
284
+ background: linear-gradient(
285
+ 90deg,
286
+ rgb(255 255 255 / 0),
287
+ var(--hn-skeleton-wave),
288
+ rgb(255 255 255 / 0)
289
+ );
290
+ animation: hn-skeleton var(--hn-skeleton-duration) var(--hn-ease-move) infinite;
291
+ }
292
+
293
+ &:dir(rtl)::after {
294
+ animation-name: hn-skeleton-rtl;
295
+ }
276
296
 
277
297
  & * {
278
298
  visibility: hidden;
279
299
  }
280
300
 
281
301
  @media (prefers-reduced-motion: reduce) {
282
- animation: none;
302
+ &::after,
303
+ &:dir(rtl)::after {
304
+ animation: none;
305
+ }
283
306
  }
284
307
  }
285
308
 
@@ -5,8 +5,20 @@
5
5
  }
6
6
 
7
7
  @keyframes hn-skeleton {
8
- 50% {
9
- background-color: var(--hn-bg-inset);
8
+ from {
9
+ transform: translateX(-100%);
10
+ }
11
+ to {
12
+ transform: translateX(100%);
13
+ }
14
+ }
15
+
16
+ @keyframes hn-skeleton-rtl {
17
+ from {
18
+ transform: translateX(100%);
19
+ }
20
+ to {
21
+ transform: translateX(-100%);
10
22
  }
11
23
  }
12
24
 
@@ -72,6 +72,8 @@
72
72
  0 40px 80px -24px rgb(0 0 0 / 0.16);
73
73
 
74
74
  --hn-scroll-shadow: rgb(0 0 0 / 0.1);
75
+ --hn-skeleton-bg: var(--color-neutral-200);
76
+ --hn-skeleton-wave: rgb(255 255 255 / 0.4);
75
77
 
76
78
  --hn-z-overlay: 100;
77
79
  --hn-z-toast: 110;
@@ -146,6 +148,8 @@
146
148
  0 32px 64px -16px rgb(0 0 0 / 0.55);
147
149
 
148
150
  --hn-scroll-shadow: rgb(0 0 0 / 0.5);
151
+ --hn-skeleton-bg: rgb(255 255 255 / 0.06);
152
+ --hn-skeleton-wave: rgb(255 255 255 / 0.04);
149
153
  }
150
154
 
151
155
  :root {
@@ -202,7 +206,7 @@
202
206
  --hn-travel-md: 10px;
203
207
  --hn-stagger: 24ms;
204
208
  --hn-spin-duration: 700ms;
205
- --hn-skeleton-duration: 2200ms;
209
+ --hn-skeleton-duration: 1200ms;
206
210
  --hn-ripple-in: 75ms;
207
211
  --hn-ripple-out: 375ms;
208
212
  --hn-pulse-duration: 1500ms;
@@ -74,14 +74,19 @@
74
74
  }
75
75
  }
76
76
 
77
+ @utility hn-pre-content {
78
+ padding: 1em 1.25em;
79
+ }
80
+
77
81
  @utility hn-pre {
82
+ @apply hn-pre-content;
78
83
  font-family: var(--hn-font-mono);
79
84
  background-color: var(--hn-bg-inset);
80
85
  border-radius: var(--hn-radius-lg);
81
- padding: 1em 1.25em;
82
86
 
83
87
  &:focus-visible,
84
- &:has(> [data-overlayscrollbars-initialize]:focus-visible) {
88
+ &:has(> [data-overlayscrollbars-initialize]:focus-visible),
89
+ &:has(> [data-overlayscrollbars-initialize] > [data-overlayscrollbars-contents]:focus-visible) {
85
90
  outline: var(--hn-focus-ring-width) solid var(--hn-focus-ring);
86
91
  outline-offset: var(--hn-focus-ring-offset);
87
92
  }
@@ -144,7 +149,7 @@
144
149
  & ol,
145
150
  & dl,
146
151
  & blockquote,
147
- & pre,
152
+ & pre:not(:where(.hn-pre-content)),
148
153
  & table,
149
154
  & figure {
150
155
  margin-block: 0.75em 0;
@@ -228,7 +233,7 @@
228
233
  @apply hn-code;
229
234
  }
230
235
 
231
- & pre {
236
+ & pre:not(:where(.hn-pre-content)) {
232
237
  @apply hn-pre;
233
238
  overflow-x: auto;
234
239
  }