DuyTa's picture
Upload folder using huggingface_hub
bc20498 verified
import { addEventListener, addMeltEventListener, effect, executeCallbacks, isFirefox, isHTMLElement, isTouchDevice, makeElement, noop, styleToString, } from '../../internal/helpers/index.js';
import { createStateMachine } from '../../internal/helpers/store/stateMachine.js';
import { name } from './create.js';
import { debounceCallback, getThumbSize, resizeObserver } from './helpers.js';
/**
* The base scrollbar action is used for all scrollbar types,
* and provides the basic functionality for dragging the scrollbar
* thumb and scrolling the content.
*
* The other scrollbar actions will extend this one, preventing a ton
* of code duplication.
*/
export function createBaseScrollbarAction(state) {
const { rootState, scrollbarState } = state;
scrollbarState.isVisible.set(true);
function handleDragScroll(e) {
const $domRect = scrollbarState.domRect.get();
if (!$domRect)
return;
const x = e.clientX - $domRect.left;
const y = e.clientY - $domRect.top;
const $isHorizontal = scrollbarState.isHorizontal.get();
if ($isHorizontal) {
scrollbarState.onDragScroll(x);
}
else {
scrollbarState.onDragScroll(y);
}
}
function handlePointerDown(e) {
if (e.button !== 0)
return;
const target = e.target;
if (!isHTMLElement(target))
return;
target.setPointerCapture(e.pointerId);
const currentTarget = e.currentTarget;
if (!isHTMLElement(currentTarget))
return;
scrollbarState.domRect.set(currentTarget.getBoundingClientRect());
scrollbarState.prevWebkitUserSelect.set(document.body.style.webkitUserSelect);
document.body.style.webkitUserSelect = 'none';
const $viewportEl = rootState.viewportEl.get();
if ($viewportEl) {
$viewportEl.style.scrollBehavior = 'auto';
}
handleDragScroll(e);
}
function handlePointerMove(e) {
handleDragScroll(e);
}
function handlePointerUp(e) {
const target = e.target;
if (!isHTMLElement(target))
return;
if (target.hasPointerCapture(e.pointerId)) {
target.releasePointerCapture(e.pointerId);
}
document.body.style.webkitUserSelect = scrollbarState.prevWebkitUserSelect.get();
const $viewportEl = rootState.viewportEl.get();
if ($viewportEl) {
$viewportEl.style.scrollBehavior = '';
}
scrollbarState.domRect.set(null);
}
function handleWheel(e) {
const target = e.target;
const currentTarget = e.currentTarget;
if (!isHTMLElement(target) || !isHTMLElement(currentTarget))
return;
const isScrollbarWheel = currentTarget.contains(target);
if (!isScrollbarWheel)
return;
const $sizes = scrollbarState.sizes.get();
if (!$sizes)
return;
const maxScrollPos = $sizes.content - $sizes.viewport;
scrollbarState.handleWheelScroll(e, maxScrollPos);
}
function baseAction(node) {
scrollbarState.scrollbarEl.set(node);
const unsubEvents = executeCallbacks(addMeltEventListener(node, 'pointerdown', handlePointerDown), addMeltEventListener(node, 'pointermove', handlePointerMove), addMeltEventListener(node, 'pointerup', handlePointerUp), addEventListener(document, 'wheel', handleWheel, { passive: false }));
const unsubResizeContent = effect([rootState.contentEl], ([$contentEl]) => {
if (!$contentEl)
return noop;
return resizeObserver($contentEl, scrollbarState.handleSizeChange);
});
return {
destroy() {
unsubEvents();
unsubResizeContent();
},
};
}
return baseAction;
}
/**
* The auto scrollbar action will show the scrollbar when the content
* overflows the viewport, and hide it when it doesn't.
*/
export function createAutoScrollbarAction(state) {
// always create the base action first, so we can override any
// state mutations that occur there
const baseAction = createBaseScrollbarAction(state);
const { rootState, scrollbarState } = state;
const handleResize = debounceCallback(() => {
const $viewportEl = rootState.viewportEl.get();
if (!$viewportEl)
return;
const isOverflowX = $viewportEl.offsetWidth < $viewportEl.scrollWidth;
const isOverflowY = $viewportEl.offsetHeight < $viewportEl.scrollHeight;
scrollbarState.isVisible.set(scrollbarState.isHorizontal.get() ? isOverflowX : isOverflowY);
}, 10);
function scrollbarAutoAction(node) {
const unsubBaseAction = baseAction(node)?.destroy;
handleResize();
const unsubObservers = [];
const $viewportEl = rootState.viewportEl.get();
if ($viewportEl) {
unsubObservers.push(resizeObserver($viewportEl, handleResize));
}
const $contentEl = rootState.contentEl.get();
if ($contentEl) {
unsubObservers.push(resizeObserver($contentEl, handleResize));
}
return {
destroy() {
unsubObservers.forEach((unsub) => unsub());
unsubBaseAction();
},
};
}
return scrollbarAutoAction;
}
/**
* The hover scrollbar action will show the scrollbar when the user
* hovers over the scroll area, and hide it when they leave after
* an optionally specified delay.
*/
export function createHoverScrollbarAction(state) {
// always create the base action first, so we can override any
// state mutations that occur there
const baseAction = createBaseScrollbarAction(state);
const { rootState, scrollbarState } = state;
// with the hover scrollbar, we want it to be hidden by default
// and only show it when the user hovers over the scroll area
scrollbarState.isVisible.set(false);
let timeout;
function handlePointerEnter() {
window.clearTimeout(timeout);
if (scrollbarState.isVisible.get())
return;
const $viewportEl = rootState.viewportEl.get();
if (!$viewportEl)
return;
const isOverflowX = $viewportEl.offsetWidth < $viewportEl.scrollWidth;
const isOverflowY = $viewportEl.offsetHeight < $viewportEl.scrollHeight;
scrollbarState.isVisible.set(scrollbarState.isHorizontal.get() ? isOverflowX : isOverflowY);
}
function handlePointerLeave() {
timeout = window.setTimeout(() => {
if (!scrollbarState.isVisible.get())
return;
scrollbarState.isVisible.set(false);
}, rootState.options.hideDelay.get());
}
function scrollbarHoverAction(node) {
const unsubBaseAction = baseAction(node)?.destroy;
const scrollAreaEl = node.closest('[data-melt-scroll-area]');
let unsubScrollAreaListeners = noop;
if (scrollAreaEl) {
if (isTouchDevice()) {
unsubScrollAreaListeners = executeCallbacks(addEventListener(scrollAreaEl, 'touchstart', handlePointerEnter), addEventListener(scrollAreaEl, 'touchend', handlePointerLeave));
}
else if (isFirefox()) {
/**
* Firefox triggers pointerleave events if you tab away from the window
* without moving the pointer, so we use mouseenter/mouseleave instead
* which works as expected.
*
* In Firefox, mouseenter is not triggered if the pointer was over the scroll area
* before events were loaded and then starts moving,
* so we use pointerenter which works as expected.
*/
unsubScrollAreaListeners = executeCallbacks(addEventListener(scrollAreaEl, 'pointerenter', handlePointerEnter), addEventListener(scrollAreaEl, 'mouseenter', handlePointerEnter), addEventListener(scrollAreaEl, 'mouseleave', handlePointerLeave));
}
else {
unsubScrollAreaListeners = executeCallbacks(addEventListener(scrollAreaEl, 'pointerenter', handlePointerEnter), addEventListener(scrollAreaEl, 'pointerleave', handlePointerLeave));
}
}
return {
destroy() {
unsubBaseAction?.();
unsubScrollAreaListeners();
},
};
}
return scrollbarHoverAction;
}
/**
* The scroll scrollbar action will only show the scrollbar
* when the user is actively scrolling the content.
*/
export function createScrollScrollbarAction(state) {
// always create the base action first, so we can
// override any state mutations that occur there
const baseAction = createBaseScrollbarAction(state);
const { rootState, scrollbarState } = state;
const machine = createStateMachine('hidden', {
hidden: {
SCROLL: 'scrolling',
},
scrolling: {
SCROLL_END: 'idle',
POINTER_ENTER: 'interacting',
},
interacting: {
SCROLL: 'interacting',
POINTER_LEAVE: 'idle',
},
idle: {
HIDE: 'hidden',
SCROLL: 'scrolling',
POINTER_ENTER: 'interacting',
},
});
effect([machine.state], ([$status]) => {
if ($status === 'idle') {
window.setTimeout(() => {
machine.dispatch('HIDE');
}, rootState.options.hideDelay.get());
}
if ($status === 'hidden') {
scrollbarState.isVisible.set(false);
}
else {
scrollbarState.isVisible.set(true);
}
});
const debounceScrollEnd = debounceCallback(() => machine.dispatch('SCROLL_END'), 100);
effect([rootState.viewportEl, scrollbarState.isHorizontal], ([$viewportEl, $isHorizontal]) => {
const scrollDirection = $isHorizontal ? 'scrollLeft' : 'scrollTop';
let unsub = noop;
if ($viewportEl) {
let prevScrollPos = $viewportEl[scrollDirection];
const handleScroll = () => {
const scrollPos = $viewportEl[scrollDirection];
const hasScrollInDirectionChanged = prevScrollPos !== scrollPos;
if (hasScrollInDirectionChanged) {
machine.dispatch('SCROLL');
debounceScrollEnd();
}
prevScrollPos = scrollPos;
};
unsub = addEventListener($viewportEl, 'scroll', handleScroll);
}
return () => {
unsub();
};
});
function scrollbarScrollAction(node) {
const unsubBaseAction = baseAction(node)?.destroy;
const unsubListeners = executeCallbacks(addEventListener(node, 'pointerenter', () => machine.dispatch('POINTER_ENTER')), addEventListener(node, 'pointerleave', () => machine.dispatch('POINTER_LEAVE')));
return {
destroy() {
unsubBaseAction?.();
unsubListeners();
},
};
}
return scrollbarScrollAction;
}
/**
* Creates the horizontal/x-axis scrollbar builder element.
*/
export function createScrollbarX(state, createAction) {
const action = createAction(state);
const { rootState, scrollbarState } = state;
return makeElement(name('scrollbar'), {
stores: [scrollbarState.sizes, rootState.options.dir, scrollbarState.isVisible],
returned: ([$sizes, $dir, $isVisible]) => {
return {
style: styleToString({
position: 'absolute',
bottom: 0,
left: $dir === 'rtl' ? 'var(--melt-scroll-area-corner-width)' : 0,
right: $dir === 'ltr' ? 'var(--melt-scroll-area-corner-width)' : 0,
'--melt-scroll-area-thumb-width': `${getThumbSize($sizes)}px`,
visibility: !$isVisible ? 'hidden' : undefined,
}),
'data-state': $isVisible ? 'visible' : 'hidden',
};
},
action: (node) => {
const unsubAction = action(node)?.destroy;
rootState.scrollbarXEl.set(node);
rootState.scrollbarXEnabled.set(true);
return {
destroy() {
unsubAction?.();
rootState.scrollbarXEl.set(null);
},
};
},
});
}
/**
* Creates the vertical/y-axis scrollbar builder element.
*/
export function createScrollbarY(state, createAction) {
const action = createAction(state);
const { rootState, scrollbarState } = state;
return makeElement(name('scrollbar'), {
stores: [scrollbarState.sizes, rootState.options.dir, scrollbarState.isVisible],
returned: ([$sizes, $dir, $isVisible]) => {
return {
style: styleToString({
position: 'absolute',
top: 0,
right: $dir === 'ltr' ? 0 : undefined,
left: $dir === 'rtl' ? 0 : undefined,
bottom: 'var(--melt-scroll-area-corner-height)',
'--melt-scroll-area-thumb-height': `${getThumbSize($sizes)}px`,
visibility: !$isVisible ? 'hidden' : undefined,
}),
'data-state': $isVisible ? 'visible' : 'hidden',
};
},
action: (node) => {
const unsubAction = action(node)?.destroy;
rootState.scrollbarYEl.set(node);
rootState.scrollbarYEnabled.set(true);
return {
destroy() {
unsubAction?.();
rootState.scrollbarYEl.set(null);
},
};
},
});
}
export function getScrollbarActionByType(type) {
switch (type) {
case 'always':
return createBaseScrollbarAction;
case 'auto':
return createAutoScrollbarAction;
case 'hover':
return createHoverScrollbarAction;
case 'scroll':
return createScrollScrollbarAction;
default:
return createBaseScrollbarAction;
}
}