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