UNPKG

@fastkit/vue-scroller

Version:

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

314 lines (313 loc) 7.5 kB
import { Scroller } from "@fastkit/scroller"; import { computed, createVNode, defineComponent, onBeforeUnmount, onMounted, reactive, ref } from "vue"; import { defineTypedComponent } from "@fastkit/vue-utils"; export * from "@fastkit/scroller"; //#region src/composables/scroller.ts const INITIAL_VALUE = null; const OBSERVER_READONLY_PROPS = [ "containerHeight", "containerWidth", "isDestroyed", "isPending", "isReady", "isRunning", "lastAxis", "lastDirection", "lastXDirection", "lastYDirection", "nowScrolling", "scrollEnabled", "scrollHeight", "scrollWidth", "scrollableX", "scrollableY", "state" ]; const OBSERVER_WRITABLE_PROPS = [ "scrollBottom", "scrollLeft", "scrollRight", "scrollTop" ]; const OBSERVER_PROPS = [...OBSERVER_READONLY_PROPS, ...OBSERVER_WRITABLE_PROPS]; function createDefaultState() { return reactive(Object.fromEntries(OBSERVER_PROPS.map((prop) => [prop, INITIAL_VALUE]))); } function useScrollerControl(setting) { const isSelf = setting.el === "self"; const $target = isSelf ? null : void 0; const state = createDefaultState(); const elementRef = ref(null); const scroller = new Scroller({ ...setting, el: $target }); scroller.observe(state); const _scroller = { get elementRef() { return elementRef; }, get scroller() { return scroller; }, get containerHeight() { return state.containerHeight; }, get containerWidth() { return state.containerWidth; }, get isDestroyed() { return state.isDestroyed; }, get isPending() { return state.isPending; }, get isReady() { return state.isReady; }, get isRunning() { return state.isRunning; }, get lastAxis() { return state.lastAxis; }, get lastDirection() { return state.lastDirection; }, get lastXDirection() { return state.lastXDirection; }, get lastYDirection() { return state.lastYDirection; }, get nowScrolling() { return state.nowScrolling; }, get scrollEnabled() { return state.scrollEnabled; }, get scrollHeight() { return state.scrollHeight; }, get scrollWidth() { return state.scrollWidth; }, get scrollableX() { return state.scrollableX; }, get scrollableY() { return state.scrollableY; }, get state() { return state.state; }, get scrollBottom() { return state.scrollBottom; }, set scrollBottom(scrollBottom) { state.scrollBottom = scrollBottom; }, get scrollLeft() { return state.scrollLeft; }, set scrollLeft(scrollLeft) { state.scrollLeft = scrollLeft; }, get scrollRight() { return state.scrollRight; }, set scrollRight(scrollRight) { state.scrollRight = scrollRight; }, get scrollTop() { return state.scrollTop; }, set scrollTop(scrollTop) { state.scrollTop = scrollTop; }, ready() { return scroller.ready(); }, element() { return elementRef.value; }, start() { return scroller.start(); }, stop() { return scroller.stop(); }, destroy() { return scroller.destroy(); }, update() { return scroller.update(); }, toJSON() { return scroller.toJSON(); }, toString() { return scroller.toString(); }, cancel() { return scroller.cancel(); }, by(diffX, diffY, options) { return scroller.by(diffX, diffY, options); }, to(scrollPosition, options) { return scroller.to(scrollPosition, options); }, toElement(target, options) { return scroller.toElement(target, options); }, toSide(targets, options) { return scroller.toSide(targets, options); }, toTop(options) { return scroller.toTop(options); }, toRight(options) { return scroller.toRight(options); }, toBottom(options) { return scroller.toBottom(options); }, toLeft(options) { return scroller.toLeft(options); }, toLeftTop(options) { return scroller.toLeftTop(options); }, toLeftBottom(options) { return scroller.toLeftBottom(options); }, toRightTop(options) { return scroller.toRightTop(options); }, toRightBottom(options) { return scroller.toRightBottom(options); }, pushScrollStopper(stopper) { return scroller.pushScrollStopper(stopper); }, removeScrollStopper(stopper) { return scroller.removeScrollStopper(stopper); }, setScrollToElementAdditionalOffset(offset) { return scroller.setScrollToElementAdditionalOffset(offset); }, deleteScrollToElementAdditionalOffset() { return scroller.deleteScrollToElementAdditionalOffset(); } }; if (isSelf) { onMounted(() => { let el = elementRef.value; if (!el) return; if (!(el instanceof Element)) el = el.$el; if (!(el instanceof Element)) return; scroller.setElement(el); }); onBeforeUnmount(() => { _scroller.destroy(); }); } return _scroller; } let _documentScroller; function getDocumentScroller() { if (!_documentScroller) _documentScroller = useScrollerControl({ el: "body" }); return _documentScroller; } //#endregion //#region src/components/VScroller.tsx const DEFAULT_GUIDE_OFFSET = 20; const GuideTypes = [ "top", "right", "bottom", "left" ]; const scrollerProps = { settings: { type: Object, default: null }, /** * スクロール可能な辺にガイドとして影を表示する場合に設定する */ guide: [Boolean, Number], /** * スクロールするコンテナ要素のクラス名 */ containerClass: String }; const _ScrollerI = defineComponent({ name: "VScroller", props: scrollerProps, setup(props, ctx) { const settings = props.settings || {}; const scroller = useScrollerControl({ ...settings, el: settings.el || "self" }); const guideOffsetRef = computed(() => { const { guide } = props; if (typeof guide === "number") return guide; if (guide === true) return DEFAULT_GUIDE_OFFSET; }); const scrollability = computed(() => { const guideOffset = guideOffsetRef.value || DEFAULT_GUIDE_OFFSET; const { scrollLeft, scrollTop, scrollRight, scrollBottom } = scroller; return { left: scrollLeft >= guideOffset, right: scrollRight >= guideOffset, top: scrollTop >= guideOffset, bottom: scrollBottom >= guideOffset, strict: { left: scrollLeft > 0, right: scrollRight > 0, top: scrollTop > 0, bottom: scrollBottom > 0 } }; }); const scrollerRef = { scroller, get scrollable() { return scrollability.value; } }; const guidesRef = computed(() => { if (guideOffsetRef.value === void 0) return; const guides = []; const scrollable = scrollability.value; scrollable.left && guides.push("left"); scrollable.top && guides.push("top"); scrollable.right && guides.push("right"); scrollable.bottom && guides.push("bottom"); return guides; }); ctx.expose(scrollerRef); return () => { const guides = guidesRef.value; const { containerClass } = props; const $guides = guides && GuideTypes.map((type) => createVNode("div", { "class": ["v-scroller__guide", { [`v-scroller__guide--${type}`]: true, [`v-scroller__guide--active`]: guides.includes(type) }], "key": type }, null)); return createVNode("div", { "class": "v-scroller" }, [createVNode("div", { "class": ["v-scroller__container", containerClass], "ref": scroller.elementRef }, [ctx.slots.default?.()]), $guides]); }; } }); const VScroller = defineTypedComponent(_ScrollerI).$expose(); //#endregion export { VScroller, _ScrollerI, getDocumentScroller, scrollerProps, useScrollerControl }; //# sourceMappingURL=vue-scroller.mjs.map