@hina-ui/vue 1.4.0 → 1.5.0
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 +330 -148
- package/dist/index.js.map +1 -1
- package/dist/types/components/dialog/Dialog.vue.d.ts +3 -1
- package/dist/types/components/dialog/Dialog.vue.d.ts.map +1 -1
- package/dist/types/components/drawer/Drawer.vue.d.ts +21 -14
- package/dist/types/components/drawer/Drawer.vue.d.ts.map +1 -1
- package/dist/types/components/drawer/drawer.variants.d.ts +10 -1
- package/dist/types/components/drawer/drawer.variants.d.ts.map +1 -1
- package/dist/types/components/dropdown-menu/DropdownMenu.vue.d.ts +4 -1
- package/dist/types/components/dropdown-menu/DropdownMenu.vue.d.ts.map +1 -1
- package/dist/types/components/hover-card/HoverCard.vue.d.ts +4 -1
- package/dist/types/components/hover-card/HoverCard.vue.d.ts.map +1 -1
- package/dist/types/components/hover-card/HoverCardAnchor.vue.d.ts +6 -4
- package/dist/types/components/hover-card/HoverCardAnchor.vue.d.ts.map +1 -1
- package/dist/types/components/hover-card/composables/useHoverCardAnchor.d.ts +9 -4
- package/dist/types/components/hover-card/composables/useHoverCardAnchor.d.ts.map +1 -1
- package/dist/types/components/popover/Popover.vue.d.ts +4 -1
- package/dist/types/components/popover/Popover.vue.d.ts.map +1 -1
- package/dist/types/components/sheet/Sheet.vue.d.ts +21 -14
- package/dist/types/components/sheet/Sheet.vue.d.ts.map +1 -1
- package/dist/types/components/sheet/sheet.variants.d.ts +54 -3
- package/dist/types/components/sheet/sheet.variants.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/anchored-overlay.d.ts +12 -2
- package/dist/types/lib/anchored-overlay.d.ts.map +1 -1
- package/dist/types/lib/overlay-anchor.d.ts +16 -0
- package/dist/types/lib/overlay-anchor.d.ts.map +1 -0
- package/dist/types/lib/scroll-viewport.d.ts +9 -0
- package/dist/types/lib/scroll-viewport.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/dialog/Dialog.vue +5 -1
- package/src/components/drawer/Drawer.vue +69 -32
- package/src/components/drawer/drawer.variants.ts +6 -2
- package/src/components/dropdown-menu/DropdownMenu.vue +18 -5
- package/src/components/hover-card/HoverCard.vue +6 -1
- package/src/components/hover-card/HoverCardAnchor.vue +3 -1
- package/src/components/hover-card/composables/useHoverCardAnchor.ts +33 -42
- package/src/components/popover/Popover.vue +17 -7
- package/src/components/sheet/Sheet.vue +67 -24
- package/src/components/sheet/sheet.variants.ts +17 -4
- package/src/index.ts +1 -0
- package/src/lib/anchored-overlay.ts +28 -19
- package/src/lib/overlay-anchor.ts +65 -0
- package/src/lib/scroll-viewport.ts +8 -0
|
@@ -3,17 +3,30 @@ import { tv } from '../../lib/tv'
|
|
|
3
3
|
export const sheetPanel = tv({
|
|
4
4
|
base: [
|
|
5
5
|
'hn-anim-sheet-bottom hn-transition pointer-events-auto fixed inset-x-0 bottom-0 z-(--hn-z-overlay)',
|
|
6
|
-
'mx-auto flex max-h-[calc(100dvh-3rem)] w-full flex-col
|
|
7
|
-
'rounded-t-xl rounded-b-none border-b-0
|
|
8
|
-
'pb-[max(var(--hn-panel-p),env(safe-area-inset-bottom))]',
|
|
6
|
+
'mx-auto flex max-h-[calc(100dvh-3rem)] w-full flex-col sm:max-w-xl',
|
|
7
|
+
'rounded-t-xl rounded-b-none border-b-0 shadow-lg outline-none',
|
|
9
8
|
'data-dragging:transition-none',
|
|
10
9
|
],
|
|
10
|
+
variants: {
|
|
11
|
+
padded: { true: 'gap-4 pb-[max(var(--hn-panel-p),env(safe-area-inset-bottom))]' },
|
|
12
|
+
grip: { true: '', false: '' },
|
|
13
|
+
},
|
|
14
|
+
compoundVariants: [
|
|
15
|
+
{ padded: true, grip: true, class: 'pt-2' },
|
|
16
|
+
{ padded: true, grip: false, class: 'pt-(--hn-panel-p)' },
|
|
17
|
+
],
|
|
18
|
+
defaultVariants: { grip: true, padded: true },
|
|
11
19
|
})
|
|
12
20
|
|
|
13
21
|
export const sheetGrip = tv({
|
|
14
22
|
base: 'flex shrink-0 cursor-grab touch-none flex-col select-none data-dragging:cursor-grabbing',
|
|
23
|
+
variants: { standalone: { true: 'py-2' } },
|
|
15
24
|
})
|
|
16
25
|
|
|
17
26
|
export const sheetHandle = tv({
|
|
18
|
-
base: 'bg-line-strong hn-transition-base mx-auto
|
|
27
|
+
base: 'bg-line-strong hn-transition-base mx-auto h-1.5 w-10 rounded-full data-disabled:opacity-50',
|
|
28
|
+
variants: {
|
|
29
|
+
header: { true: 'mb-3', false: '' },
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: { header: true },
|
|
19
32
|
})
|
package/src/index.ts
CHANGED
|
@@ -214,6 +214,7 @@ export { default as TooltipProvider } from './components/tooltip/TooltipProvider
|
|
|
214
214
|
export { default as Tooltip } from './components/tooltip/Tooltip.vue'
|
|
215
215
|
export { vTooltip } from './components/tooltip/directive'
|
|
216
216
|
export type { TooltipDirectiveOptions, TooltipDirectiveValue } from './components/tooltip/types'
|
|
217
|
+
export type { OverlayAnchor, OverlayPositionStrategy } from './lib/overlay-anchor'
|
|
217
218
|
export { default as Popover } from './components/popover/Popover.vue'
|
|
218
219
|
export { default as DropdownMenu } from './components/dropdown-menu/DropdownMenu.vue'
|
|
219
220
|
export { default as DropdownMenuItem } from './components/dropdown-menu/DropdownMenuItem.vue'
|
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
import { computed, nextTick,
|
|
1
|
+
import { computed, nextTick, watch, type Ref } from 'vue'
|
|
2
|
+
import { defaultDocument } from '@vueuse/core'
|
|
3
|
+
import { useOverlayPortal } from './overlay-portal'
|
|
4
|
+
import {
|
|
5
|
+
overlayAnchorContext,
|
|
6
|
+
overlayAnchorElement,
|
|
7
|
+
useOverlayAnchor,
|
|
8
|
+
type OverlayAnchor,
|
|
9
|
+
type OverlayPositionStrategy,
|
|
10
|
+
} from './overlay-anchor'
|
|
2
11
|
import { useForwardExpose, type DropdownMenuContentEmits } from 'reka-ui'
|
|
3
12
|
|
|
4
13
|
export function useAnchoredOverlay(
|
|
5
|
-
props: {
|
|
14
|
+
props: {
|
|
15
|
+
anchor?: OverlayAnchor | null
|
|
16
|
+
modal: boolean
|
|
17
|
+
updatePositionStrategy: OverlayPositionStrategy
|
|
18
|
+
},
|
|
6
19
|
open: Ref<boolean | undefined>,
|
|
7
20
|
emit: <K extends keyof DropdownMenuContentEmits>(
|
|
8
21
|
event: K,
|
|
@@ -11,14 +24,6 @@ export function useAnchoredOverlay(
|
|
|
11
24
|
openAutoFocus?: (event: Event) => void,
|
|
12
25
|
) {
|
|
13
26
|
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
27
|
const visible = computed({
|
|
23
28
|
get: () => !!open.value && !!(props.anchor || triggerElement.value),
|
|
24
29
|
set: value => {
|
|
@@ -26,12 +31,12 @@ export function useAnchoredOverlay(
|
|
|
26
31
|
},
|
|
27
32
|
})
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
const { content, present } = useOverlayPortal(visible)
|
|
35
|
+
const reference = useOverlayAnchor(
|
|
36
|
+
() => props.anchor ?? triggerElement.value,
|
|
37
|
+
visible,
|
|
38
|
+
present,
|
|
39
|
+
() => props.updatePositionStrategy,
|
|
35
40
|
)
|
|
36
41
|
|
|
37
42
|
let previousFocus: HTMLElement | null = null
|
|
@@ -50,7 +55,10 @@ export function useAnchoredOverlay(
|
|
|
50
55
|
visible,
|
|
51
56
|
active => {
|
|
52
57
|
if (!active) return
|
|
53
|
-
const document =
|
|
58
|
+
const document =
|
|
59
|
+
overlayAnchorContext(props.anchor)?.ownerDocument ??
|
|
60
|
+
triggerElement.value?.ownerDocument ??
|
|
61
|
+
defaultDocument
|
|
54
62
|
previousFocus = document?.activeElement as HTMLElement | null
|
|
55
63
|
interactedOutside = false
|
|
56
64
|
},
|
|
@@ -73,12 +81,11 @@ export function useAnchoredOverlay(
|
|
|
73
81
|
(active === panel.ownerDocument.body || panel.contains(active))
|
|
74
82
|
)
|
|
75
83
|
target.focus({ preventScroll: true })
|
|
76
|
-
if (!visible.value) retainedAnchor.value = undefined
|
|
77
84
|
})
|
|
78
85
|
})
|
|
79
86
|
|
|
80
87
|
const onInteractOutside = once((event: DropdownMenuContentEmits['interactOutside'][0]) => {
|
|
81
|
-
if (!triggerElement.value && props.anchor?.contains(event.target as Node))
|
|
88
|
+
if (!triggerElement.value && overlayAnchorElement(props.anchor)?.contains(event.target as Node))
|
|
82
89
|
event.preventDefault()
|
|
83
90
|
emit('interactOutside', event)
|
|
84
91
|
const original = event.detail.originalEvent
|
|
@@ -89,6 +96,8 @@ export function useAnchoredOverlay(
|
|
|
89
96
|
|
|
90
97
|
return {
|
|
91
98
|
trigger,
|
|
99
|
+
content,
|
|
100
|
+
present,
|
|
92
101
|
reference,
|
|
93
102
|
visible,
|
|
94
103
|
events: {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { shallowRef, watch, type Ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
export type OverlayAnchor =
|
|
4
|
+
| HTMLElement
|
|
5
|
+
| {
|
|
6
|
+
getBoundingClientRect: () => Pick<
|
|
7
|
+
DOMRect,
|
|
8
|
+
'x' | 'y' | 'width' | 'height' | 'top' | 'right' | 'bottom' | 'left'
|
|
9
|
+
>
|
|
10
|
+
contextElement?: Element
|
|
11
|
+
}
|
|
12
|
+
export type OverlayPositionStrategy = 'optimized' | 'always'
|
|
13
|
+
|
|
14
|
+
export function overlayAnchorElement(anchor?: OverlayAnchor | null): HTMLElement | undefined {
|
|
15
|
+
return anchor && 'nodeType' in anchor ? anchor : undefined
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function overlayAnchorContext(anchor?: OverlayAnchor | null): Element | undefined {
|
|
19
|
+
return (
|
|
20
|
+
overlayAnchorElement(anchor) ??
|
|
21
|
+
(anchor && 'contextElement' in anchor ? anchor.contextElement : undefined)
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useOverlayAnchor(
|
|
26
|
+
source: () => OverlayAnchor | null | undefined,
|
|
27
|
+
open: Ref<boolean | undefined>,
|
|
28
|
+
present: Ref<boolean>,
|
|
29
|
+
strategy: () => OverlayPositionStrategy,
|
|
30
|
+
) {
|
|
31
|
+
const reference = shallowRef<Exclude<OverlayAnchor, HTMLElement>>()
|
|
32
|
+
|
|
33
|
+
let current: OverlayAnchor | undefined
|
|
34
|
+
|
|
35
|
+
watch(
|
|
36
|
+
[source, open, present, strategy],
|
|
37
|
+
([anchor, active, mounted]) => {
|
|
38
|
+
if (!mounted) {
|
|
39
|
+
current = undefined
|
|
40
|
+
reference.value = undefined
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
if (!anchor || (!active && reference.value && anchor !== current)) return
|
|
44
|
+
const contextElement = overlayAnchorContext(anchor)
|
|
45
|
+
if (contextElement && !contextElement.isConnected) return
|
|
46
|
+
current = anchor
|
|
47
|
+
const measure = () => {
|
|
48
|
+
const { x, y, width, height, top, right, bottom, left } = anchor.getBoundingClientRect()
|
|
49
|
+
return { x, y, width, height, top, right, bottom, left }
|
|
50
|
+
}
|
|
51
|
+
let rect = measure()
|
|
52
|
+
reference.value = {
|
|
53
|
+
contextElement,
|
|
54
|
+
getBoundingClientRect: () => {
|
|
55
|
+
if (source() === anchor && (!contextElement || contextElement.isConnected))
|
|
56
|
+
rect = measure()
|
|
57
|
+
return rect
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{ immediate: true, flush: 'post' },
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return reference
|
|
65
|
+
}
|