pref: use hooks instead of directive (#108)
- remove v-resize - add useContainerResize - remove v-container - add useContainerQueries - add useContainerScroll
This commit is contained in:
@@ -1,46 +1,27 @@
|
||||
import { defineResizeCallback } from 'hooks/resize'
|
||||
import { computed, Directive, inject, InjectionKey, provide, ref } from 'vue'
|
||||
|
||||
const globalContainerSize = ref<Record<symbol, number>>({})
|
||||
|
||||
const containerNameKey = Symbol('containerName') as InjectionKey<symbol>
|
||||
|
||||
export const containerDirective: Directive<HTMLElement, symbol> = {
|
||||
mounted: (el, binding) => {
|
||||
const containerName = binding.value || Symbol('container')
|
||||
const resizeCallback = defineResizeCallback((entries) => {
|
||||
const entry = entries[0]
|
||||
globalContainerSize.value[containerName] = entry.contentRect.width
|
||||
})
|
||||
const observer = new ResizeObserver(resizeCallback)
|
||||
observer.observe(el)
|
||||
el['_containerObserver'] = observer
|
||||
},
|
||||
unmounted: (el) => {
|
||||
const observer = el['_containerObserver']
|
||||
observer.disconnect()
|
||||
},
|
||||
}
|
||||
import { useContainerResize } from 'hooks/resize'
|
||||
import { type InjectionKey, type Ref, inject, provide, toRef } from 'vue'
|
||||
|
||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize)
|
||||
|
||||
export const useContainerQueries = (containerName?: symbol) => {
|
||||
const parentContainer = inject(containerNameKey, Symbol('unknown'))
|
||||
const containerKey = Symbol('container') as InjectionKey<
|
||||
Ref<HTMLElement | null>
|
||||
>
|
||||
|
||||
const name = containerName ?? parentContainer
|
||||
export const useContainerQueries = (
|
||||
el?: HTMLElement | null | Ref<HTMLElement | null>,
|
||||
) => {
|
||||
const container = inject(containerKey, el ? toRef(el) : toRef(document.body))
|
||||
|
||||
provide(containerNameKey, name)
|
||||
provide(containerKey, container)
|
||||
|
||||
const currentContainerSize = computed(() => {
|
||||
return globalContainerSize.value[name] ?? 0
|
||||
})
|
||||
const { width } = useContainerResize(container)
|
||||
|
||||
/**
|
||||
* @param size unit rem
|
||||
*/
|
||||
const generator = (size: number) => {
|
||||
return (content: any, defaultContent: any = undefined) => {
|
||||
return currentContainerSize.value > size * rem ? content : defaultContent
|
||||
return width.value > size * rem ? content : defaultContent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
import { throttle } from 'lodash'
|
||||
import { Directive } from 'vue'
|
||||
|
||||
export const resizeDirective: Directive<HTMLElement, ResizeObserverCallback> = {
|
||||
mounted: (el, binding) => {
|
||||
const callback = binding.value ?? (() => {})
|
||||
const observer = new ResizeObserver(callback)
|
||||
observer.observe(el)
|
||||
el['observer'] = observer
|
||||
},
|
||||
unmounted: (el) => {
|
||||
const observer = el['observer']
|
||||
observer.disconnect()
|
||||
},
|
||||
}
|
||||
import { type Ref, onUnmounted, ref, toRef, watch } from 'vue'
|
||||
|
||||
export const defineResizeCallback = (
|
||||
callback: ResizeObserverCallback,
|
||||
@@ -20,3 +7,45 @@ export const defineResizeCallback = (
|
||||
) => {
|
||||
return throttle(callback, wait ?? 100)
|
||||
}
|
||||
|
||||
export const useContainerResize = (
|
||||
el: HTMLElement | null | Ref<HTMLElement | null>,
|
||||
) => {
|
||||
const observer = ref<ResizeObserver | null>(null)
|
||||
|
||||
const width = ref(0)
|
||||
const height = ref(0)
|
||||
|
||||
watch(
|
||||
toRef(el),
|
||||
(el) => {
|
||||
if (el) {
|
||||
const onResize = defineResizeCallback((entries) => {
|
||||
const entry = entries[0]
|
||||
width.value = entry.contentRect.width
|
||||
height.value = entry.contentRect.height
|
||||
})
|
||||
|
||||
observer.value = new ResizeObserver(onResize)
|
||||
observer.value.observe(el)
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const stop = () => {
|
||||
if (observer.value) {
|
||||
observer.value.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
stop()
|
||||
})
|
||||
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
stop,
|
||||
}
|
||||
}
|
||||
|
||||
58
src/hooks/scroll.ts
Normal file
58
src/hooks/scroll.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { throttle } from 'lodash'
|
||||
import { computed, onUnmounted, ref, toRef, watch, type Ref } from 'vue'
|
||||
|
||||
export interface UseScrollOption {
|
||||
throttle?: number
|
||||
onScroll?: (e: Event) => void
|
||||
}
|
||||
|
||||
export const useContainerScroll = (
|
||||
el: HTMLElement | null | Ref<HTMLElement | null>,
|
||||
options?: UseScrollOption,
|
||||
) => {
|
||||
const scrollLeft = ref(0)
|
||||
const scrollTop = ref(0)
|
||||
|
||||
const container = toRef(el)
|
||||
|
||||
const onScroll = throttle((e: Event) => {
|
||||
options?.onScroll?.(e)
|
||||
|
||||
if (container.value) {
|
||||
scrollLeft.value = container.value.scrollLeft
|
||||
scrollTop.value = container.value.scrollTop
|
||||
}
|
||||
}, options?.throttle || 100)
|
||||
|
||||
watch(
|
||||
container,
|
||||
(el) => {
|
||||
if (el) {
|
||||
el.addEventListener('scroll', onScroll)
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const x = computed({
|
||||
get: () => scrollLeft,
|
||||
set: (val) => {
|
||||
container.value?.scrollTo({ left: val.value })
|
||||
},
|
||||
})
|
||||
|
||||
const y = computed({
|
||||
get: () => scrollTop,
|
||||
set: (val) => {
|
||||
container.value?.scrollTo({ top: val.value })
|
||||
},
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (container.value) {
|
||||
container.value.removeEventListener('scroll', onScroll)
|
||||
}
|
||||
})
|
||||
|
||||
return { x, y }
|
||||
}
|
||||
Reference in New Issue
Block a user