DuyTa's picture
Upload folder using huggingface_hub
bc20498 verified
import { addEventListener, addMeltEventListener, makeElement, createElHelpers, effect, executeCallbacks, getPortalDestination, isBrowser, isDocument, isElement, isTouch, kbd, makeHullFromElements, noop, omit, overridable, pointInPolygon, styleToString, toWritableStores, removeUndefined, portalAttr, } from '../../internal/helpers/index.js';
import { useFloating, usePortal } from '../../internal/actions/index.js';
import { derived, writable } from 'svelte/store';
import { generateIds } from '../../internal/helpers/id.js';
import { tick } from 'svelte';
const defaults = {
positioning: {
placement: 'bottom',
},
arrowSize: 8,
defaultOpen: false,
closeOnPointerDown: true,
openDelay: 1000,
closeDelay: 0,
forceVisible: false,
portal: undefined,
closeOnEscape: true,
disableHoverableContent: false,
group: undefined,
};
const { name } = createElHelpers('tooltip');
// Store a global map to get the currently open tooltip.
const groupMap = new Map();
export const tooltipIdParts = ['trigger', 'content'];
export function createTooltip(props) {
const withDefaults = { ...defaults, ...props };
const options = toWritableStores(omit(withDefaults, 'open', 'ids'));
const { positioning, arrowSize, closeOnPointerDown, openDelay, closeDelay, forceVisible, portal, closeOnEscape, disableHoverableContent, group, } = options;
const openWritable = withDefaults.open ?? writable(withDefaults.defaultOpen);
const open = overridable(openWritable, withDefaults?.onOpenChange);
const openReason = writable(null);
const ids = toWritableStores({ ...generateIds(tooltipIdParts), ...withDefaults.ids });
let clickedTrigger = false;
const getEl = (part) => {
if (!isBrowser)
return null;
return document.getElementById(ids[part].get());
};
let openTimeout = null;
let closeTimeout = null;
function openTooltip(reason) {
if (closeTimeout) {
window.clearTimeout(closeTimeout);
closeTimeout = null;
}
if (!openTimeout) {
openTimeout = window.setTimeout(() => {
open.set(true);
// Don't override the reason if it's already set.
openReason.update((prev) => prev ?? reason);
openTimeout = null;
}, openDelay.get());
}
}
function closeTooltip(isBlur) {
if (openTimeout) {
window.clearTimeout(openTimeout);
openTimeout = null;
}
if (isBlur && isMouseInTooltipArea) {
// Normally when blurring the trigger, we want to close the tooltip.
// The exception is when the mouse is still in the tooltip area.
// In that case, we have to set the openReason to pointer, so that
// it can close when the mouse leaves the tooltip area.
openReason.set('pointer');
return;
}
if (!closeTimeout) {
closeTimeout = window.setTimeout(() => {
open.set(false);
openReason.set(null);
if (isBlur)
clickedTrigger = false;
closeTimeout = null;
}, closeDelay.get());
}
}
const isVisible = derived([open, forceVisible], ([$open, $forceVisible]) => {
return $open || $forceVisible;
});
const trigger = makeElement(name('trigger'), {
stores: [ids.content, ids.trigger, open],
returned: ([$contentId, $triggerId, $open]) => {
return {
'aria-describedby': $contentId,
id: $triggerId,
'data-state': $open ? 'open' : 'closed',
};
},
action: (node) => {
const keydownHandler = (e) => {
if (closeOnEscape.get() && e.key === kbd.ESCAPE) {
if (openTimeout) {
window.clearTimeout(openTimeout);
openTimeout = null;
}
open.set(false);
}
};
const unsub = executeCallbacks(addMeltEventListener(node, 'pointerdown', () => {
const $closeOnPointerDown = closeOnPointerDown.get();
if (!$closeOnPointerDown)
return;
open.set(false);
clickedTrigger = true;
if (openTimeout) {
window.clearTimeout(openTimeout);
openTimeout = null;
}
}), addMeltEventListener(node, 'pointerenter', (e) => {
if (isTouch(e))
return;
openTooltip('pointer');
}), addMeltEventListener(node, 'pointerleave', (e) => {
if (isTouch(e))
return;
if (openTimeout) {
window.clearTimeout(openTimeout);
openTimeout = null;
}
}), addMeltEventListener(node, 'focus', () => {
if (clickedTrigger)
return;
openTooltip('focus');
}), addMeltEventListener(node, 'blur', () => closeTooltip(true)), addMeltEventListener(node, 'keydown', keydownHandler), addEventListener(document, 'keydown', keydownHandler));
return {
destroy: unsub,
};
},
});
const content = makeElement(name('content'), {
stores: [isVisible, open, portal, ids.content],
returned: ([$isVisible, $open, $portal, $contentId]) => {
return removeUndefined({
role: 'tooltip',
hidden: $isVisible ? undefined : true,
tabindex: -1,
style: $isVisible ? undefined : styleToString({ display: 'none' }),
id: $contentId,
'data-portal': portalAttr($portal),
'data-state': $open ? 'open' : 'closed',
});
},
action: (node) => {
let unsubFloating = noop;
let unsubPortal = noop;
const unsubDerived = effect([isVisible, positioning, portal], ([$isVisible, $positioning, $portal]) => {
const triggerEl = getEl('trigger');
if (!$isVisible || !triggerEl) {
unsubPortal();
unsubFloating();
return;
}
tick().then(() => {
if ($portal === null) {
unsubPortal();
}
else {
const portalDest = getPortalDestination(node, $portal);
if (portalDest) {
const portalReturn = usePortal(node, portalDest);
if (portalReturn && portalReturn.destroy) {
unsubPortal = portalReturn.destroy;
}
}
}
const floatingReturn = useFloating(triggerEl, node, $positioning);
unsubFloating = floatingReturn.destroy;
});
});
/**
* We don't want the tooltip to remain open if the user starts scrolling
* while their pointer is over the tooltip, so we close it.
*/
function handleScroll(e) {
if (!open.get())
return;
const target = e.target;
if (!isElement(target) && !isDocument(target))
return;
const triggerEl = getEl('trigger');
if (triggerEl && target.contains(triggerEl)) {
closeTooltip();
}
}
const unsubEvents = executeCallbacks(addMeltEventListener(node, 'pointerenter', () => openTooltip('pointer')), addMeltEventListener(node, 'pointerdown', () => openTooltip('pointer')), addEventListener(window, 'scroll', handleScroll, { capture: true }));
return {
destroy() {
unsubEvents();
unsubPortal();
unsubFloating();
unsubDerived();
},
};
},
});
const arrow = makeElement(name('arrow'), {
stores: arrowSize,
returned: ($arrowSize) => ({
'data-arrow': true,
style: styleToString({
position: 'absolute',
width: `var(--arrow-size, ${$arrowSize}px)`,
height: `var(--arrow-size, ${$arrowSize}px)`,
}),
}),
});
let isMouseInTooltipArea = false;
effect(open, ($open) => {
const currentGroup = group.get();
if (currentGroup === undefined || currentGroup === false) {
return;
}
if (!$open) {
if (groupMap.get(currentGroup) === open) {
// Tooltip is no longer open
groupMap.delete(currentGroup);
}
return;
}
// Close the currently open tooltip in the same group
// and set this tooltip as the open one.
const currentOpen = groupMap.get(currentGroup);
currentOpen?.set(false);
groupMap.set(currentGroup, open);
});
effect([open, openReason], ([$open, $openReason]) => {
if (!$open || !isBrowser)
return;
return executeCallbacks(addEventListener(document, 'mousemove', (e) => {
const contentEl = getEl('content');
const triggerEl = getEl('trigger');
if (!contentEl || !triggerEl)
return;
const polygonElements = disableHoverableContent.get()
? [triggerEl]
: [triggerEl, contentEl];
const polygon = makeHullFromElements(polygonElements);
isMouseInTooltipArea = pointInPolygon({
x: e.clientX,
y: e.clientY,
}, polygon);
if ($openReason !== 'pointer')
return;
if (!isMouseInTooltipArea) {
closeTooltip();
}
}));
});
return {
ids,
elements: {
trigger,
content,
arrow,
},
states: { open },
options,
};
}