UNPKG

@fastkit/vue-scroller

Version:

A library for controlling the scrolling state of elements and components in Vue applications.

1 lines 16.9 kB
{"version":3,"file":"vue-scroller.mjs","names":[],"sources":["../src/composables/scroller.ts","../src/components/VScroller.tsx"],"sourcesContent":["import { onMounted, onBeforeUnmount, reactive, ref } from 'vue';\nimport {\n Scroller,\n ScrollerSetting,\n ScrollerState,\n ScrollAxis,\n ScrollDirection,\n ScrollXDirection,\n ScrollYDirection,\n ScrollerScrollOptions,\n ScrollPosition,\n ScrollToElementTarget,\n ScrollerScrollToElementOptions,\n ScrollToSideTargets,\n ScrollStopper,\n ScrollToElementAdditionalOffset,\n} from '@fastkit/scroller';\n\nexport type UseScrollerTarget = 'self' | 'body';\n\nexport interface UseScrollerSetting extends Omit<\n Partial<ScrollerSetting>,\n 'el'\n> {\n el?: UseScrollerTarget;\n}\n\nconst INITIAL_VALUE: any = null;\n\ntype UseScrollerState = {\n containerHeight: number;\n containerWidth: number;\n isDestroyed: boolean;\n isPending: boolean;\n isReady: boolean;\n isRunning: boolean;\n lastAxis: ScrollAxis;\n lastDirection: ScrollDirection;\n lastXDirection: ScrollXDirection;\n lastYDirection: ScrollYDirection;\n nowScrolling: boolean;\n scrollEnabled: boolean;\n scrollHeight: number;\n scrollWidth: number;\n scrollableX: boolean;\n scrollableY: boolean;\n state: ScrollerState;\n scrollBottom: number;\n scrollLeft: number;\n scrollRight: number;\n scrollTop: number;\n};\n\nconst OBSERVER_READONLY_PROPS = [\n 'containerHeight',\n 'containerWidth',\n 'isDestroyed',\n 'isPending',\n 'isReady',\n 'isRunning',\n 'lastAxis',\n 'lastDirection',\n 'lastXDirection',\n 'lastYDirection',\n 'nowScrolling',\n 'scrollEnabled',\n 'scrollHeight',\n 'scrollWidth',\n 'scrollableX',\n 'scrollableY',\n 'state',\n] as const;\n\nconst OBSERVER_WRITABLE_PROPS = [\n 'scrollBottom',\n 'scrollLeft',\n 'scrollRight',\n 'scrollTop',\n] as const;\n\nconst OBSERVER_PROPS = [...OBSERVER_READONLY_PROPS, ...OBSERVER_WRITABLE_PROPS];\n\nfunction createDefaultState(): UseScrollerState {\n return reactive(\n Object.fromEntries(OBSERVER_PROPS.map((prop) => [prop, INITIAL_VALUE])),\n ) as UseScrollerState;\n}\n\nexport function useScrollerControl(setting: UseScrollerSetting) {\n const isSelf = setting.el === 'self';\n const $target = isSelf ? null : undefined;\n const state = createDefaultState();\n const elementRef = ref<null | HTMLElement>(null);\n const scroller = new Scroller({\n ...setting,\n el: $target,\n });\n scroller.observe(state);\n\n const _scroller = {\n get elementRef() {\n return elementRef;\n },\n get scroller() {\n return scroller;\n },\n get containerHeight() {\n return state.containerHeight;\n },\n get containerWidth() {\n return state.containerWidth;\n },\n get isDestroyed() {\n return state.isDestroyed;\n },\n get isPending() {\n return state.isPending;\n },\n get isReady() {\n return state.isReady;\n },\n get isRunning() {\n return state.isRunning;\n },\n get lastAxis() {\n return state.lastAxis;\n },\n get lastDirection() {\n return state.lastDirection;\n },\n get lastXDirection() {\n return state.lastXDirection;\n },\n get lastYDirection() {\n return state.lastYDirection;\n },\n get nowScrolling() {\n return state.nowScrolling;\n },\n get scrollEnabled() {\n return state.scrollEnabled;\n },\n get scrollHeight() {\n return state.scrollHeight;\n },\n get scrollWidth() {\n return state.scrollWidth;\n },\n get scrollableX() {\n return state.scrollableX;\n },\n get scrollableY() {\n return state.scrollableY;\n },\n get state() {\n return state.state;\n },\n get scrollBottom() {\n return state.scrollBottom;\n },\n set scrollBottom(scrollBottom) {\n state.scrollBottom = scrollBottom;\n },\n get scrollLeft() {\n return state.scrollLeft;\n },\n set scrollLeft(scrollLeft) {\n state.scrollLeft = scrollLeft;\n },\n get scrollRight() {\n return state.scrollRight;\n },\n set scrollRight(scrollRight) {\n state.scrollRight = scrollRight;\n },\n get scrollTop() {\n return state.scrollTop;\n },\n set scrollTop(scrollTop) {\n state.scrollTop = scrollTop;\n },\n\n ready() {\n return scroller.ready();\n },\n\n element() {\n return elementRef.value;\n },\n\n start() {\n return scroller.start();\n },\n\n stop(): void {\n return scroller.stop();\n },\n\n destroy() {\n return scroller.destroy();\n },\n\n update(): void {\n return scroller.update();\n },\n\n toJSON() {\n return scroller.toJSON();\n },\n\n toString(): string {\n return scroller.toString();\n },\n\n cancel() {\n return scroller.cancel();\n },\n\n by(diffX: number, diffY: number, options?: ScrollerScrollOptions) {\n return scroller.by(diffX, diffY, options);\n },\n\n to(\n scrollPosition: Partial<ScrollPosition>,\n options?: ScrollerScrollOptions,\n ) {\n return scroller.to(scrollPosition, options);\n },\n\n toElement(\n target: ScrollToElementTarget,\n options?: ScrollerScrollToElementOptions,\n ) {\n return scroller.toElement(target, options);\n },\n\n toSide(targets: ScrollToSideTargets, options?: ScrollerScrollOptions) {\n return scroller.toSide(targets, options);\n },\n\n toTop(options?: ScrollerScrollOptions) {\n return scroller.toTop(options);\n },\n\n toRight(options?: ScrollerScrollOptions) {\n return scroller.toRight(options);\n },\n\n toBottom(options?: ScrollerScrollOptions) {\n return scroller.toBottom(options);\n },\n\n toLeft(options?: ScrollerScrollOptions) {\n return scroller.toLeft(options);\n },\n\n toLeftTop(options?: ScrollerScrollOptions) {\n return scroller.toLeftTop(options);\n },\n\n toLeftBottom(options?: ScrollerScrollOptions) {\n return scroller.toLeftBottom(options);\n },\n\n toRightTop(options?: ScrollerScrollOptions) {\n return scroller.toRightTop(options);\n },\n\n toRightBottom(options?: ScrollerScrollOptions) {\n return scroller.toRightBottom(options);\n },\n\n pushScrollStopper(stopper: ScrollStopper) {\n return scroller.pushScrollStopper(stopper);\n },\n\n removeScrollStopper(stopper: ScrollStopper) {\n return scroller.removeScrollStopper(stopper);\n },\n\n setScrollToElementAdditionalOffset(\n offset: ScrollToElementAdditionalOffset | undefined,\n ) {\n return scroller.setScrollToElementAdditionalOffset(offset);\n },\n\n deleteScrollToElementAdditionalOffset() {\n return scroller.deleteScrollToElementAdditionalOffset();\n },\n };\n\n if (isSelf) {\n // const selfEl = ref<null | Element | ComponentPublicInstance>(null);\n onMounted(() => {\n let el = elementRef.value;\n if (!el) return;\n if (!(el instanceof Element)) {\n el = (el as any).$el;\n }\n if (!(el instanceof Element)) return;\n scroller.setElement(el);\n });\n\n onBeforeUnmount(() => {\n _scroller.destroy();\n });\n }\n\n return _scroller;\n}\n\nexport type UseScroller = ReturnType<typeof useScrollerControl>;\n\nlet _documentScroller: UseScroller | undefined;\n\nexport function getDocumentScroller(): UseScroller {\n if (!_documentScroller) {\n _documentScroller = useScrollerControl({ el: 'body' });\n }\n return _documentScroller;\n}\n\nexport type ScrollerControl = ReturnType<typeof useScrollerControl>;\n","import './VScroller.scss';\n\nimport { defineComponent, PropType, ExtractPropTypes, computed } from 'vue';\nimport { ExtractPropInput, defineTypedComponent } from '@fastkit/vue-utils';\nimport {\n useScrollerControl,\n UseScrollerSetting,\n ScrollerControl,\n} from '../composables';\n\nexport interface VScrollerSettings extends UseScrollerSetting {}\n\nconst DEFAULT_GUIDE_OFFSET = 20;\n\nconst GuideTypes = ['top', 'right', 'bottom', 'left'] as const;\n\ntype VScrollerGuideType = (typeof GuideTypes)[number];\n\nexport const scrollerProps = {\n settings: {\n type: Object as PropType<VScrollerSettings | null>,\n default: null,\n },\n /**\n * スクロール可能な辺にガイドとして影を表示する場合に設定する\n */\n guide: [Boolean, Number],\n\n /**\n * スクロールするコンテナ要素のクラス名\n */\n containerClass: String,\n};\n\nexport type VScrollerProps = ExtractPropInput<typeof scrollerProps>;\n\nexport type VScrollerResolvedProps = ExtractPropTypes<typeof scrollerProps>;\n\n/** Scrollability in each direction */\nexport interface ScrollerScrollability {\n /** Whether you can scroll to the left or not */\n left: boolean;\n /** Whether you can scroll to the right or not */\n right: boolean;\n /** Whether you can scroll to the top or not */\n top: boolean;\n /** Whether you can scroll to the bottom or not */\n bottom: boolean;\n}\n\n/**\n * Combined Scrollability\n *\n * The information immediately below is marked as scrollable to account for the margin of the `guide` prop, so `false` may be marked even if the information is actually scrollable.\n * If you want to check strict scrollability, check the `strict` property.\n */\nexport interface ScrollerCombinedScrollability extends ScrollerScrollability {\n /**\n * Strict scrollability\n *\n * Scrollable more than 0px will be marked as valid\n */\n strict: ScrollerScrollability;\n}\n\nexport interface ScrollerAPI {\n get scroller(): ScrollerControl;\n /**\n * Combined Scrollability\n *\n * @see {@link ScrollerCombinedScrollability}\n */\n get scrollable(): ScrollerCombinedScrollability;\n}\n\nexport const _ScrollerI = defineComponent({\n name: 'VScroller',\n props: scrollerProps,\n setup(props, ctx) {\n const settings = props.settings || {};\n const scroller = useScrollerControl({\n ...settings,\n el: settings.el || 'self',\n });\n\n const guideOffsetRef = computed(() => {\n const { guide } = props;\n if (typeof guide === 'number') return guide;\n if (guide === true) return DEFAULT_GUIDE_OFFSET;\n return undefined;\n });\n\n const scrollability = computed<ScrollerCombinedScrollability>(() => {\n const guideOffset = guideOffsetRef.value || DEFAULT_GUIDE_OFFSET;\n const { scrollLeft, scrollTop, scrollRight, scrollBottom } = scroller;\n return {\n left: scrollLeft >= guideOffset,\n right: scrollRight >= guideOffset,\n top: scrollTop >= guideOffset,\n bottom: scrollBottom >= guideOffset,\n strict: {\n left: scrollLeft > 0,\n right: scrollRight > 0,\n top: scrollTop > 0,\n bottom: scrollBottom > 0,\n },\n };\n });\n\n const scrollerRef: ScrollerAPI = {\n scroller,\n get scrollable() {\n return scrollability.value;\n },\n };\n\n const guidesRef = computed<VScrollerGuideType[] | undefined>(() => {\n const guideOffset = guideOffsetRef.value;\n if (guideOffset === undefined) return;\n\n const guides: VScrollerGuideType[] = [];\n\n const scrollable = scrollability.value;\n\n scrollable.left && guides.push('left');\n scrollable.top && guides.push('top');\n scrollable.right && guides.push('right');\n scrollable.bottom && guides.push('bottom');\n\n return guides;\n });\n\n ctx.expose(scrollerRef);\n\n return () => {\n const guides = guidesRef.value;\n const { containerClass } = props;\n const $guides =\n guides &&\n GuideTypes.map((type) => (\n <div\n class={[\n 'v-scroller__guide',\n {\n [`v-scroller__guide--${type}`]: true,\n [`v-scroller__guide--active`]: guides.includes(type),\n },\n ]}\n key={type}\n />\n ));\n\n return (\n <div class=\"v-scroller\">\n <div\n class={['v-scroller__container', containerClass]}\n ref={scroller.elementRef}>\n {ctx.slots.default?.()}\n </div>\n {$guides}\n </div>\n );\n };\n },\n});\n\nexport const VScroller =\n defineTypedComponent(_ScrollerI).$expose<ScrollerAPI>();\n"],"mappings":"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"}