@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
JavaScript
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