From 4323266d58796743fd1ebc8e43d5b052c68f3c83 Mon Sep 17 00:00:00 2001 From: Valentin Silytuin Date: Wed, 12 Feb 2025 21:30:54 +0400 Subject: [PATCH] v2.0.0 --- CHANGELOG.md | 12 +++ README.md | 40 ++++++++++ index.js | 215 +++++++++++++++++++++++++++------------------------ 3 files changed, 166 insertions(+), 101 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..745b00b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +## v2.0.0 + +- К `_tooltip.$tooltip` добавлено свойство `_reference` для доступа к элементу, на котором был вызван `createTooltip` +- Для функций `show/hide` добавлен параметр `immediately` для мнгновенного открытия/закрытия всплывающей подсказки (`_tooltip.hide({ immediately: true })`) +- Добавлена опция `shiftPadding` для добавления отступов от краёв области видимости по осям x/y +- Добавлена опция `animation` для кастомизации анимации при открытии/закрытии всплывающей подсказки (для этого всё внутри `.tooltip` было обёрнуто в `.tooltip__root` и анимация применяется именно к этому блоку) +- Опция `_tooltip._options` переименована в `_tooltip.options` +- Оптимизция кода, исправление ошибок +- Доработки по README.md +- Доработки по .npmignore + +## v1.0.0 diff --git a/README.md b/README.md index 8a03202..c16c872 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ createTooltip(document.querySelector('button'), 'Подсказка'); import { createTooltip } from '@advdominion/tooltip'; createTooltip(document.querySelector('button'), 'Подсказка', { + animation: [ + [{ opacity: 0 }, { opacity: 1 }], + [{ opacity: 1 }, { opacity: 0 }], + ], appendTo: document.body, arrow: true, delay: [0, 0], @@ -37,6 +41,7 @@ createTooltip(document.querySelector('button'), 'Подсказка', { interactive: true, offset: [0, 8], placement: 'top', + shiftPadding: [8, 0], theme: 'light', trigger: 'mouseenter', virtialReference: undefined, @@ -76,8 +81,34 @@ createTooltip(document.querySelector('button'), 'Подсказка', { }); ``` +#### Свойства + +#### $tooltip + +DOM-элемент всплывающей подсказки (имеет свойство `_reference` для доступа к элементу, на котором был вызван `createTooltip`) + +#### options + +Текущие настройки + +#### isVisible + +Видимость всплывающей подсказки - `true`/`false` + #### Методы +##### show + +```js +document.querySelector('button')._tooltip.show(); +``` + +##### hide + +```js +document.querySelector('button')._tooltip.hide(); +``` + ##### setContent ```js @@ -96,6 +127,12 @@ document.querySelector('button')._tooltip.updateOptions({ placement: 'bottom' }) ``` +##### destroy + +```js +document.querySelector('button')._tooltip.destroy(); +``` + ### Стили ```scss @@ -113,6 +150,9 @@ $b: '.tooltip'; max-width: 300px; } + &__root { + } + &__arrow { #{$b}_theme_light & { background-color: white; diff --git a/index.js b/index.js index f4f42b2..c1537de 100644 --- a/index.js +++ b/index.js @@ -3,17 +3,27 @@ const roundByDPR = (value) => { return Math.round(value * dpr) / dpr; }; +const attributeToOption = (attribute) => { + attribute = attribute.replace('tooltip', ''); + return attribute.charAt(0).toLowerCase() + attribute.slice(1); +}; + export const createTooltip = ($el, content, options) => { options = { + animation: [ + [{ opacity: 0 }, { opacity: 1 }], + [{ opacity: 1 }, { opacity: 0 }], + ], appendTo: document.body, arrow: true, delay: [0, 0], duration: [0, 0], easing: ['linear', 'linear'], - hideOnClick: true, + hideOnClick: true, // Возможные значения: true, 'all', 'trigger' interactive: true, offset: [0, 8], placement: 'top', + shiftPadding: [8, 0], theme: 'light', trigger: 'mouseenter', virtialReference: undefined, @@ -21,46 +31,29 @@ export const createTooltip = ($el, content, options) => { ...options, }; - if ($el.dataset.tooltipArrow !== undefined) { - options.arrow = $el.dataset.tooltipArrow === 'true'; - } - - if ($el.dataset.tooltipHideOnClick !== undefined) { - switch ($el.dataset.tooltipHideOnClick) { - case 'all': - case 'toggle': { - options.hideOnClick = $el.dataset.tooltipHideOnClick; - break; - } - default: { - options.hideOnClick = $el.dataset.tooltipHideOnClick === 'true'; - } + const setOptionFromDataAttribute = (attribute) => { + if ($el.dataset[attribute] !== undefined) { + let value = $el.dataset[attribute]; + try { + value = JSON.parse($el.dataset[attribute]); + } catch {} + options[attributeToOption(attribute)] = value; } - } + }; - if ($el.dataset.tooltipInteractive !== undefined) { - options.interactive = $el.dataset.tooltipInteractive === 'true'; - } - - if ($el.dataset.tooltipOffset !== undefined) { - options.offset = JSON.parse($el.dataset.tooltipOffset); - } - - if ($el.dataset.tooltipPlacement !== undefined) { - options.placement = $el.dataset.tooltipPlacement; - } - - if ($el.dataset.tooltipTheme !== undefined) { - options.theme = $el.dataset.tooltipTheme; - } - - if ($el.dataset.tooltipTrigger !== undefined) { - options.trigger = $el.dataset.tooltipTrigger; - } - - if ($el.dataset.tooltipZIndex !== undefined) { - options.zIndex = $el.dataset.tooltipZIndex; - } + setOptionFromDataAttribute('tooltipAnimation'); + setOptionFromDataAttribute('tooltipArrow'); + setOptionFromDataAttribute('tooltipDelay'); + setOptionFromDataAttribute('tooltipDuration'); + setOptionFromDataAttribute('tooltipEasing'); + setOptionFromDataAttribute('tooltipHideOnClick'); + setOptionFromDataAttribute('tooltipInteractive'); + setOptionFromDataAttribute('tooltipOffset'); + setOptionFromDataAttribute('tooltipPlacement'); + setOptionFromDataAttribute('tooltipShiftPadding'); + setOptionFromDataAttribute('tooltipTheme'); + setOptionFromDataAttribute('tooltipTrigger'); + setOptionFromDataAttribute('tooltipZIndex'); let showTimeout; let hideTimeout; @@ -69,7 +62,7 @@ export const createTooltip = ($el, content, options) => { const listeners = []; $el._tooltip = { - _options: options, + options, isVisible: false, setContent(updatedContent) { if (updatedContent !== undefined) { @@ -159,13 +152,16 @@ export const createTooltip = ($el, content, options) => { options.onCreate($el._tooltip); } - $el._tooltip.show = async () => { + $el._tooltip.show = async (params = {}) => { + const { immediately } = params; + clearTimeout(hideTimeout); if (!$el._tooltip.$tooltip) { const { computePosition, offset, flip, shift, arrow } = await import('@floating-ui/dom'); $el._tooltip.$tooltip = document.createElement('div'); + $el._tooltip.$tooltip._reference = $el; $el._tooltip.updatePosition = async () => { const $arrow = $el._tooltip.$tooltip.querySelector('.tooltip__arrow'); @@ -180,7 +176,14 @@ export const createTooltip = ($el, content, options) => { crossAxis: options.offset[0], }), flip(), - shift(), + shift({ + padding: { + top: options.shiftPadding[1], + right: options.shiftPadding[0], + bottom: options.shiftPadding[1], + left: options.shiftPadding[0], + }, + }), arrow({ element: $arrow, padding: Number.parseInt( @@ -241,27 +244,29 @@ export const createTooltip = ($el, content, options) => { $el._tooltip.$tooltip.classList.add('tooltip', `tooltip_theme_${options.theme}`); Object.assign($el._tooltip.$tooltip.style, { zIndex: options.zIndex }); $el._tooltip.$tooltip.innerHTML = ` - ${ - options.interactive - ? ` -
-
- ` - : '' - } - ${ - options.arrow - ? ` -
-
- ` - : '' - } -
+
+ ${ + options.interactive + ? ` +
+
+ ` + : '' + } + ${ + options.arrow + ? ` +
+
+ ` + : '' + } +
+
`; $el._tooltip.setContent(); @@ -272,51 +277,59 @@ export const createTooltip = ($el, content, options) => { const { autoUpdate } = await import('@floating-ui/dom'); - showTimeout = setTimeout(async () => { - if (!$el._tooltip.isVisible) { - options.appendTo.append($el._tooltip.$tooltip); - $el._tooltip.isVisible = true; - autoUpdateCleanup = autoUpdate($el, $el._tooltip.$tooltip, $el._tooltip.updatePosition); + showTimeout = setTimeout( + async () => { + if (!$el._tooltip.isVisible) { + options.appendTo.append($el._tooltip.$tooltip); + $el._tooltip.isVisible = true; + autoUpdateCleanup = autoUpdate($el, $el._tooltip.$tooltip, $el._tooltip.updatePosition); - if (options.onShow) { - options.onShow($el._tooltip); + if (options.onShow) { + options.onShow($el._tooltip); + } + + await $el._tooltip.$tooltip.querySelector('.tooltip__root').animate(options.animation[0], { + duration: immediately ? 0 : options.duration[0], + easing: options.easing[0], + }).finished; + + if (options.onShown) { + options.onShown($el._tooltip); + } } - - await $el._tooltip.$tooltip.animate([{ opacity: 0 }, { opacity: 1 }], { - duration: options.duration[1], - easing: options.easing[1], - }).finished; - - if (options.onShown) { - options.onShown($el._tooltip); - } - } - }, options.delay[0]); + }, + immediately ? 0 : options.delay[0], + ); }; - $el._tooltip.hide = () => { + $el._tooltip.hide = (params = {}) => { + const { immediately } = params; + clearTimeout(showTimeout); - hideTimeout = setTimeout(async () => { - if ($el._tooltip.isVisible) { - if (options.onHide) { - options.onHide($el._tooltip); + hideTimeout = setTimeout( + async () => { + if ($el._tooltip.isVisible) { + if (options.onHide) { + options.onHide($el._tooltip); + } + + await $el._tooltip.$tooltip.querySelector('.tooltip__root').animate(options.animation[1], { + duration: immediately ? 0 : options.duration[1], + easing: options.easing[1], + }).finished; + + $el._tooltip.$tooltip.remove(); + $el._tooltip.isVisible = false; + autoUpdateCleanup(); + + if (options.onHidden) { + options.onHidden($el._tooltip); + } } - - await $el._tooltip.$tooltip.animate([{ opacity: 1 }, { opacity: 0 }], { - duration: options.duration[1], - easing: options.easing[1], - }).finished; - - $el._tooltip.$tooltip.remove(); - $el._tooltip.isVisible = false; - autoUpdateCleanup(); - - if (options.onHidden) { - options.onHidden($el._tooltip); - } - } - }, options.delay[1]); + }, + immediately ? 0 : options.delay[1], + ); }; const hideOnClickListener = ({ target }) => {