UNPKG

@varlet/ui

Version:

A Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop.

187 lines (186 loc) • 6.82 kB
import { computed, defineComponent, ref, watch } from "vue"; import { call, isEmpty, preventDefault, toNumber } from "@varlet/shared"; import { useTouch, useVModel, useWindowSize } from "@varlet/use"; import { useLock } from "../context/lock.mjs"; import { createNamespace, formatElevation, useTeleport } from "../utils/components.mjs"; import { toSizeUnit } from "../utils/elements.mjs"; import { props } from "./props.mjs"; const { name, n, classes } = createNamespace("floating-panel"); const DEFAULT_START_ANCHOR = 100; const OVERFLOW_REDUCE_RATIO = 0.2; import { renderSlot as _renderSlot, normalizeClass as _normalizeClass, createElementVNode as _createElementVNode, normalizeStyle as _normalizeStyle, Teleport as _Teleport, openBlock as _openBlock, createBlock as _createBlock } from "vue"; function __render__(_ctx, _cache) { return _openBlock(), _createBlock(_Teleport, { to: _ctx.teleport === false ? void 0 : _ctx.teleport, disabled: _ctx.teleportDisabled || _ctx.teleport === false }, [ _createElementVNode( "div", { class: _normalizeClass(_ctx.classes(_ctx.n(), [_ctx.safeArea, _ctx.n("--safe-area")], _ctx.formatElevation(_ctx.elevation, 3))), style: _normalizeStyle({ height: `${_ctx.toSizeUnit(_ctx.maxAnchor)}`, transform: `translateY(calc(100% - ${_ctx.toSizeUnit(_ctx.visibleHeight)}))`, transition: _ctx.touching ? "none" : `transform ${_ctx.toNumber( _ctx.duration )}ms var(--floating-panel-transition-timing-function), background-color 0.25s` }), onTouchstart: _cache[0] || (_cache[0] = (...args) => _ctx.handleTouchstart && _ctx.handleTouchstart(...args)), onTouchmove: _cache[1] || (_cache[1] = (...args) => _ctx.handleTouchmove && _ctx.handleTouchmove(...args)), onTouchend: _cache[2] || (_cache[2] = (...args) => _ctx.handleTouchend && _ctx.handleTouchend(...args)), onTouchcancel: _cache[3] || (_cache[3] = (...args) => _ctx.handleTouchend && _ctx.handleTouchend(...args)) }, [ _renderSlot(_ctx.$slots, "header", {}, () => [ _createElementVNode( "div", { class: _normalizeClass(_ctx.n("header")) }, [ _createElementVNode( "div", { class: _normalizeClass(_ctx.n("header-toolbar")) }, null, 2 /* CLASS */ ) ], 2 /* CLASS */ ) ]), _createElementVNode( "div", { ref: "contentRef", class: _normalizeClass(_ctx.n("content")) }, [ _renderSlot(_ctx.$slots, "default") ], 2 /* CLASS */ ) ], 38 /* CLASS, STYLE, NEED_HYDRATION */ ) ], 8, ["to", "disabled"]); } const __sfc__ = defineComponent({ name, props, setup(props2) { const visibleHeight = ref(0); const contentRef = ref(null); const { height: windowHeight } = useWindowSize(); const defaultEndAnchor = computed(() => windowHeight.value * 0.6); const anchor = useVModel(props2, "anchor", { defaultValue: DEFAULT_START_ANCHOR }); const anchors = computed(() => { const defaultAnchors = [DEFAULT_START_ANCHOR, defaultEndAnchor.value]; const { anchors: anchors2 } = props2; return isEmpty(anchors2) ? defaultAnchors : anchors2; }); const minAnchor = computed(() => Math.min(...anchors.value)); const maxAnchor = computed(() => Math.max(...anchors.value)); const { disabled: teleportDisabled } = useTeleport(); const { deltaY, touching, startTouch, moveTouch, endTouch, isReachTop, isReachBottom } = useTouch(); let startVisibleHeight; useLock(() => touching.value); watch(() => anchor.value, matchAnchor, { immediate: true }); watch( () => anchors.value, () => { matchAnchor(anchor.value); }, { immediate: true } ); function matchAnchor(anchor2) { setVisibleHeight(anchor2 != null ? anchor2 : minAnchor.value); } function handleTouchstart(event) { startTouch(event); startVisibleHeight = visibleHeight.value; } function handleTouchmove(event) { var _a; moveTouch(event); const target = event.target; const eventFromContent = contentRef.value === target || ((_a = contentRef.value) == null ? void 0 : _a.contains(target)); if (eventFromContent && !props2.contentDraggable) { return; } if (eventFromContent && props2.contentDraggable && visibleHeight.value >= maxAnchor.value && !isReachTop(contentRef.value)) { if (isReachBottom(contentRef.value)) { preventDefault(event); } return; } const targetVisibleHeight = startVisibleHeight - deltaY.value; setVisibleHeight(clampVisibleHeight(targetVisibleHeight)); preventDefault(event); } function handleTouchend() { endTouch(); const oldAnchor = anchor.value; setVisibleHeight(visibleHeight.value); anchor.value = visibleHeight.value; if (anchor.value !== oldAnchor) { call(props2.onAnchorChange, visibleHeight.value); } } function setVisibleHeight(targetVisibleHeight) { visibleHeight.value = touching.value ? targetVisibleHeight : findNearestAnchor(targetVisibleHeight); } function clampVisibleHeight(visibleHeight2) { if (visibleHeight2 > maxAnchor.value) { const overflowHeight = visibleHeight2 - maxAnchor.value; return maxAnchor.value + overflowHeight * OVERFLOW_REDUCE_RATIO; } if (visibleHeight2 < minAnchor.value) { const overflowHeight = minAnchor.value - visibleHeight2; return minAnchor.value - overflowHeight * OVERFLOW_REDUCE_RATIO; } return visibleHeight2; } function findNearestAnchor(targetAnchor) { if (anchors.value.includes(targetAnchor)) { return targetAnchor; } let minDifference = Infinity; let nearestAnchor = 0; anchors.value.forEach((anchor2) => { const difference = Math.abs(anchor2 - targetAnchor); if (difference < minDifference) { minDifference = difference; nearestAnchor = anchor2; } }); return nearestAnchor; } return { contentRef, teleportDisabled, touching, minAnchor, maxAnchor, visibleHeight, n, classes, toSizeUnit, toNumber, formatElevation, handleTouchstart, handleTouchmove, handleTouchend }; } }); __sfc__.render = __render__; var stdin_default = __sfc__; export { stdin_default as default };