renommage de lib, rendre buildable
This commit is contained in:
parent
1706c64713
commit
a89007a81b
9896 changed files with 478996 additions and 496 deletions
67
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-button.svelte
generated
vendored
Normal file
67
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-button.svelte
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
<script>
|
||||
import { isFunction } from '../utils/type-check.ts';
|
||||
|
||||
export let config, step;
|
||||
let action, classes, disabled, label, secondary, text;
|
||||
|
||||
$: {
|
||||
action = config.action ? config.action.bind(step.tour) : null;
|
||||
classes = config.classes;
|
||||
disabled = config.disabled ? getConfigOption(config.disabled) : false;
|
||||
label = config.label ? getConfigOption(config.label) : null;
|
||||
secondary = config.secondary;
|
||||
text = config.text ? getConfigOption(config.text) : null;
|
||||
}
|
||||
|
||||
function getConfigOption(option) {
|
||||
if (isFunction(option)) {
|
||||
return (option = option.call(step));
|
||||
}
|
||||
return option;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
aria-label={label ? label : null}
|
||||
class={`${classes || ''} shepherd-button ${
|
||||
secondary ? 'shepherd-button-secondary' : ''
|
||||
}`}
|
||||
{disabled}
|
||||
on:click={action}
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
{@html text}
|
||||
</button>
|
||||
|
||||
<style global>
|
||||
.shepherd-button {
|
||||
background: rgb(50, 136, 230);
|
||||
border: 0;
|
||||
border-radius: 3px;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
cursor: pointer;
|
||||
margin-right: 0.5rem;
|
||||
padding: 0.5rem 1.5rem;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.shepherd-button:not(:disabled):hover {
|
||||
background: rgb(25, 111, 204);
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.shepherd-button.shepherd-button-secondary {
|
||||
background: rgb(241, 242, 243);
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.shepherd-button.shepherd-button-secondary:not(:disabled):hover {
|
||||
background: rgb(214, 217, 219);
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.shepherd-button:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
46
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-cancel-icon.svelte
generated
vendored
Normal file
46
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-cancel-icon.svelte
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
<script>
|
||||
export let cancelIcon, step;
|
||||
|
||||
/**
|
||||
* Add a click listener to the cancel link that cancels the tour
|
||||
*/
|
||||
const handleCancelClick = (e) => {
|
||||
e.preventDefault();
|
||||
step.cancel();
|
||||
};
|
||||
</script>
|
||||
|
||||
<button
|
||||
aria-label={cancelIcon.label ? cancelIcon.label : 'Close Tour'}
|
||||
class="shepherd-cancel-icon"
|
||||
on:click={handleCancelClick}
|
||||
type="button"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
|
||||
<style global>
|
||||
.shepherd-cancel-icon {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: rgba(128, 128, 128, 0.75);
|
||||
font-size: 2em;
|
||||
cursor: pointer;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
transition: color 0.5s ease;
|
||||
}
|
||||
|
||||
.shepherd-cancel-icon:hover {
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.shepherd-has-title .shepherd-content .shepherd-cancel-icon {
|
||||
color: rgba(128, 128, 128, 0.75);
|
||||
}
|
||||
|
||||
.shepherd-has-title .shepherd-content .shepherd-cancel-icon:hover {
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
</style>
|
30
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-content.svelte
generated
vendored
Normal file
30
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-content.svelte
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
<script>
|
||||
import ShepherdFooter from './shepherd-footer.svelte';
|
||||
import ShepherdHeader from './shepherd-header.svelte';
|
||||
import ShepherdText from './shepherd-text.svelte';
|
||||
import { isUndefined } from '../utils/type-check.ts';
|
||||
|
||||
export let descriptionId, labelId, step;
|
||||
</script>
|
||||
|
||||
<div class="shepherd-content">
|
||||
{#if !isUndefined(step.options.title) || (step.options.cancelIcon && step.options.cancelIcon.enabled)}
|
||||
<ShepherdHeader {labelId} {step} />
|
||||
{/if}
|
||||
|
||||
{#if !isUndefined(step.options.text)}
|
||||
<ShepherdText {descriptionId} {step} />
|
||||
{/if}
|
||||
|
||||
{#if Array.isArray(step.options.buttons) && step.options.buttons.length}
|
||||
<ShepherdFooter {step} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style global>
|
||||
.shepherd-content {
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
286
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-element.svelte
generated
vendored
Normal file
286
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-element.svelte
generated
vendored
Normal file
|
@ -0,0 +1,286 @@
|
|||
<script>
|
||||
import { onDestroy, onMount, afterUpdate } from 'svelte';
|
||||
import ShepherdContent from './shepherd-content.svelte';
|
||||
import { isUndefined, isString } from '../utils/type-check.ts';
|
||||
|
||||
const KEY_TAB = 9;
|
||||
const KEY_ESC = 27;
|
||||
const LEFT_ARROW = 37;
|
||||
const RIGHT_ARROW = 39;
|
||||
|
||||
export let attachToElement,
|
||||
attachTofocusableDialogElements,
|
||||
classPrefix,
|
||||
element,
|
||||
descriptionId,
|
||||
// Focusable attachTo elements
|
||||
focusableAttachToElements,
|
||||
firstFocusableAttachToElement,
|
||||
lastFocusableAttachToElement,
|
||||
// Focusable dialog elements
|
||||
firstFocusableDialogElement,
|
||||
focusableDialogElements,
|
||||
lastFocusableDialogElement,
|
||||
labelId,
|
||||
step,
|
||||
dataStepId;
|
||||
|
||||
let hasCancelIcon, hasTitle, classes;
|
||||
|
||||
$: {
|
||||
hasCancelIcon =
|
||||
step.options &&
|
||||
step.options.cancelIcon &&
|
||||
step.options.cancelIcon.enabled;
|
||||
hasTitle = step.options && step.options.title;
|
||||
}
|
||||
|
||||
export const getElement = () => element;
|
||||
|
||||
onMount(() => {
|
||||
// Get all elements that are focusable
|
||||
dataStepId = { [`data-${classPrefix}shepherd-step-id`]: step.id };
|
||||
focusableDialogElements = [
|
||||
...element.querySelectorAll(
|
||||
'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]'
|
||||
)
|
||||
];
|
||||
firstFocusableDialogElement = focusableDialogElements[0];
|
||||
lastFocusableDialogElement =
|
||||
focusableDialogElements[focusableDialogElements.length - 1];
|
||||
|
||||
const attachTo = step._getResolvedAttachToOptions();
|
||||
if (attachTo?.element) {
|
||||
attachToElement = attachTo.element;
|
||||
attachToElement.tabIndex = 0;
|
||||
focusableAttachToElements = [
|
||||
attachToElement,
|
||||
...attachToElement.querySelectorAll(
|
||||
'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]'
|
||||
)
|
||||
];
|
||||
firstFocusableAttachToElement = focusableAttachToElements[0];
|
||||
lastFocusableAttachToElement =
|
||||
focusableAttachToElements[focusableAttachToElements.length - 1];
|
||||
// Add keydown listener to attachTo element
|
||||
attachToElement.addEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
attachToElement?.removeEventListener('keydown', handleKeyDown);
|
||||
});
|
||||
|
||||
afterUpdate(() => {
|
||||
if (classes !== step.options.classes) {
|
||||
updateDynamicClasses();
|
||||
}
|
||||
});
|
||||
|
||||
function updateDynamicClasses() {
|
||||
removeClasses(classes);
|
||||
classes = step.options.classes;
|
||||
addClasses(classes);
|
||||
}
|
||||
|
||||
function removeClasses(classes) {
|
||||
if (isString(classes)) {
|
||||
const oldClasses = getClassesArray(classes);
|
||||
if (oldClasses.length) {
|
||||
element.classList.remove(...oldClasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addClasses(classes) {
|
||||
if (isString(classes)) {
|
||||
const newClasses = getClassesArray(classes);
|
||||
if (newClasses.length) {
|
||||
element.classList.add(...newClasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getClassesArray(classes) {
|
||||
return classes.split(' ').filter((className) => !!className.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup keydown events to allow closing the modal with ESC
|
||||
*
|
||||
* Borrowed from this great post! https://bitsofco.de/accessible-modal-dialog/
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const handleKeyDown = (e) => {
|
||||
const { tour } = step;
|
||||
switch (e.keyCode) {
|
||||
case KEY_TAB:
|
||||
if (
|
||||
(!focusableAttachToElements ||
|
||||
focusableAttachToElements.length === 0) &&
|
||||
focusableDialogElements.length === 0
|
||||
) {
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
// Backward tab
|
||||
if (e.shiftKey) {
|
||||
// If at the beginning of elements in the dialog, go to last element in attachTo
|
||||
// If attachToElement is undefined, circle around to the last element in the dialog.
|
||||
if (
|
||||
document.activeElement === firstFocusableDialogElement ||
|
||||
document.activeElement.classList.contains('shepherd-element')
|
||||
) {
|
||||
e.preventDefault();
|
||||
(
|
||||
lastFocusableAttachToElement ?? lastFocusableDialogElement
|
||||
).focus();
|
||||
}
|
||||
// If at the beginning of elements in attachTo
|
||||
else if (document.activeElement === firstFocusableAttachToElement) {
|
||||
e.preventDefault();
|
||||
lastFocusableDialogElement.focus();
|
||||
}
|
||||
} else {
|
||||
if (document.activeElement === lastFocusableDialogElement) {
|
||||
e.preventDefault();
|
||||
(
|
||||
firstFocusableAttachToElement ?? firstFocusableDialogElement
|
||||
).focus();
|
||||
}
|
||||
// If at the end of elements in attachTo
|
||||
else if (document.activeElement === lastFocusableAttachToElement) {
|
||||
e.preventDefault();
|
||||
firstFocusableDialogElement.focus();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case KEY_ESC:
|
||||
if (tour.options.exitOnEsc) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
step.cancel();
|
||||
}
|
||||
break;
|
||||
case LEFT_ARROW:
|
||||
if (tour.options.keyboardNavigation) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
tour.back();
|
||||
}
|
||||
break;
|
||||
case RIGHT_ARROW:
|
||||
if (tour.options.keyboardNavigation) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
tour.next();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<dialog
|
||||
aria-describedby={!isUndefined(step.options.text) ? descriptionId : null}
|
||||
aria-labelledby={step.options.title ? labelId : null}
|
||||
bind:this={element}
|
||||
class:shepherd-has-cancel-icon={hasCancelIcon}
|
||||
class:shepherd-has-title={hasTitle}
|
||||
class:shepherd-element={true}
|
||||
{...dataStepId}
|
||||
on:keydown={handleKeyDown}
|
||||
open="true"
|
||||
>
|
||||
{#if step.options.arrow && step.options.attachTo && step.options.attachTo.element && step.options.attachTo.on}
|
||||
<div class="shepherd-arrow" data-popper-arrow></div>
|
||||
{/if}
|
||||
<ShepherdContent {descriptionId} {labelId} {step} />
|
||||
</dialog>
|
||||
|
||||
<style global>
|
||||
.shepherd-element {
|
||||
background: #fff;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
|
||||
margin: 0;
|
||||
max-width: 400px;
|
||||
opacity: 0;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
transition:
|
||||
opacity 0.3s,
|
||||
visibility 0.3s;
|
||||
visibility: hidden;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.shepherd-enabled.shepherd-element {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.shepherd-element[data-popper-reference-hidden]:not(.shepherd-centered) {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.shepherd-element,
|
||||
.shepherd-element *,
|
||||
.shepherd-element *:after,
|
||||
.shepherd-element *:before {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.shepherd-arrow,
|
||||
.shepherd-arrow::before {
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.shepherd-arrow:before {
|
||||
content: '';
|
||||
transform: rotate(45deg);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.shepherd-element[data-popper-placement^='top'] > .shepherd-arrow {
|
||||
bottom: -8px;
|
||||
}
|
||||
|
||||
.shepherd-element[data-popper-placement^='bottom'] > .shepherd-arrow {
|
||||
top: -8px;
|
||||
}
|
||||
|
||||
.shepherd-element[data-popper-placement^='left'] > .shepherd-arrow {
|
||||
right: -8px;
|
||||
}
|
||||
|
||||
.shepherd-element[data-popper-placement^='right'] > .shepherd-arrow {
|
||||
left: -8px;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-centered > .shepherd-arrow {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arrow on top of tooltip centered horizontally, with title color
|
||||
*/
|
||||
.shepherd-element.shepherd-has-title[data-popper-placement^='bottom']
|
||||
> .shepherd-arrow::before {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.shepherd-target-click-disabled.shepherd-enabled.shepherd-target,
|
||||
.shepherd-target-click-disabled.shepherd-enabled.shepherd-target * {
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
29
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-footer.svelte
generated
vendored
Normal file
29
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-footer.svelte
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
<script>
|
||||
import ShepherdButton from './shepherd-button.svelte';
|
||||
|
||||
export let step;
|
||||
|
||||
$: buttons = step.options.buttons;
|
||||
</script>
|
||||
|
||||
<footer class="shepherd-footer">
|
||||
{#if buttons}
|
||||
{#each buttons as config}
|
||||
<ShepherdButton {config} {step} />
|
||||
{/each}
|
||||
{/if}
|
||||
</footer>
|
||||
|
||||
<style global>
|
||||
.shepherd-footer {
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 0 0.75rem 0.75rem;
|
||||
}
|
||||
|
||||
.shepherd-footer .shepherd-button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
</style>
|
39
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-header.svelte
generated
vendored
Normal file
39
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-header.svelte
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
<script>
|
||||
import ShepherdCancelIcon from './shepherd-cancel-icon.svelte';
|
||||
import ShepherdTitle from './shepherd-title.svelte';
|
||||
|
||||
export let labelId, step;
|
||||
let title, cancelIcon;
|
||||
|
||||
$: {
|
||||
title = step.options.title;
|
||||
cancelIcon = step.options.cancelIcon;
|
||||
}
|
||||
</script>
|
||||
|
||||
<header class="shepherd-header">
|
||||
{#if title}
|
||||
<ShepherdTitle {labelId} {title} />
|
||||
{/if}
|
||||
|
||||
{#if cancelIcon && cancelIcon.enabled}
|
||||
<ShepherdCancelIcon {cancelIcon} {step} />
|
||||
{/if}
|
||||
</header>
|
||||
|
||||
<style global>
|
||||
.shepherd-header {
|
||||
align-items: center;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
line-height: 2em;
|
||||
padding: 0.75rem 0.75rem 0;
|
||||
}
|
||||
|
||||
.shepherd-has-title .shepherd-content .shepherd-header {
|
||||
background: #e6e6e6;
|
||||
padding: 1em;
|
||||
}
|
||||
</style>
|
324
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-modal.svelte
generated
vendored
Normal file
324
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-modal.svelte
generated
vendored
Normal file
|
@ -0,0 +1,324 @@
|
|||
<script>
|
||||
import { makeOverlayPath } from '../utils/overlay-path.ts';
|
||||
|
||||
export let element, openingProperties;
|
||||
let modalIsVisible = false;
|
||||
let rafId = undefined;
|
||||
let pathDefinition;
|
||||
|
||||
$: pathDefinition = makeOverlayPath(openingProperties);
|
||||
|
||||
closeModalOpening();
|
||||
|
||||
export const getElement = () => element;
|
||||
|
||||
export function closeModalOpening() {
|
||||
openingProperties = [
|
||||
{
|
||||
width: 0,
|
||||
height: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
r: 0
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the modal overlay
|
||||
*/
|
||||
export function hide() {
|
||||
modalIsVisible = false;
|
||||
|
||||
// Ensure we cleanup all event listeners when we hide the modal
|
||||
_cleanupStepEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the bounds of the element we want the opening overtop of to set the dimensions of the opening and position it
|
||||
* @param {Number} modalOverlayOpeningPadding An amount of padding to add around the modal overlay opening
|
||||
* @param {Number | { topLeft: Number, bottomLeft: Number, bottomRight: Number, topRight: Number }} modalOverlayOpeningRadius An amount of border radius to add around the modal overlay opening
|
||||
* @param {Number} modalOverlayOpeningXOffset An amount to offset the modal overlay opening in the x-direction
|
||||
* @param {Number} modalOverlayOpeningYOffset An amount to offset the modal overlay opening in the y-direction
|
||||
* @param {HTMLElement} scrollParent The scrollable parent of the target element
|
||||
* @param {HTMLElement} targetElement The element the opening will expose
|
||||
*/
|
||||
export function positionModal(
|
||||
modalOverlayOpeningPadding = 0,
|
||||
modalOverlayOpeningRadius = 0,
|
||||
modalOverlayOpeningXOffset = 0,
|
||||
modalOverlayOpeningYOffset = 0,
|
||||
scrollParent,
|
||||
targetElement,
|
||||
extraHighlights
|
||||
) {
|
||||
if (targetElement) {
|
||||
const elementsToHighlight = [targetElement, ...(extraHighlights || [])];
|
||||
openingProperties = [];
|
||||
|
||||
for (const element of elementsToHighlight) {
|
||||
if (!element) continue;
|
||||
|
||||
// Skip duplicate elements
|
||||
if (
|
||||
elementsToHighlight.indexOf(element) !==
|
||||
elementsToHighlight.lastIndexOf(element)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { y, height } = _getVisibleHeight(element, scrollParent);
|
||||
const { x, width, left } = element.getBoundingClientRect();
|
||||
|
||||
// Check if the element is contained by another element
|
||||
const isContained = elementsToHighlight.some((otherElement) => {
|
||||
if (otherElement === element) return false;
|
||||
const otherRect = otherElement.getBoundingClientRect();
|
||||
return (
|
||||
x >= otherRect.left &&
|
||||
x + width <= otherRect.right &&
|
||||
y >= otherRect.top &&
|
||||
y + height <= otherRect.bottom
|
||||
);
|
||||
});
|
||||
|
||||
if (isContained) continue;
|
||||
|
||||
// getBoundingClientRect is not consistent. Some browsers use x and y, while others use left and top
|
||||
openingProperties.push({
|
||||
width: width + modalOverlayOpeningPadding * 2,
|
||||
height: height + modalOverlayOpeningPadding * 2,
|
||||
x:
|
||||
(x || left) +
|
||||
modalOverlayOpeningXOffset -
|
||||
modalOverlayOpeningPadding,
|
||||
y: y + modalOverlayOpeningYOffset - modalOverlayOpeningPadding,
|
||||
r: modalOverlayOpeningRadius
|
||||
});
|
||||
}
|
||||
} else {
|
||||
closeModalOpening();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If modal is enabled, setup the svg mask opening and modal overlay for the step
|
||||
* @param {Step} step The step instance
|
||||
*/
|
||||
export function setupForStep(step) {
|
||||
// Ensure we move listeners from the previous step, before we setup new ones
|
||||
_cleanupStepEventListeners();
|
||||
|
||||
if (step.tour.options.useModalOverlay) {
|
||||
_styleForStep(step);
|
||||
show();
|
||||
} else {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the modal overlay
|
||||
*/
|
||||
export function show() {
|
||||
modalIsVisible = true;
|
||||
}
|
||||
|
||||
const _preventModalBodyTouch = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const _preventModalOverlayTouch = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add touchmove event listener
|
||||
* @private
|
||||
*/
|
||||
function _addStepEventListeners() {
|
||||
// Prevents window from moving on touch.
|
||||
window.addEventListener('touchmove', _preventModalBodyTouch, {
|
||||
passive: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the requestAnimationFrame loop and remove touchmove event listeners
|
||||
* @private
|
||||
*/
|
||||
function _cleanupStepEventListeners() {
|
||||
if (rafId) {
|
||||
cancelAnimationFrame(rafId);
|
||||
rafId = undefined;
|
||||
}
|
||||
|
||||
window.removeEventListener('touchmove', _preventModalBodyTouch, {
|
||||
passive: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Style the modal for the step
|
||||
* @param {Step} step The step to style the opening for
|
||||
* @private
|
||||
*/
|
||||
function _styleForStep(step) {
|
||||
const {
|
||||
modalOverlayOpeningPadding,
|
||||
modalOverlayOpeningRadius,
|
||||
modalOverlayOpeningXOffset = 0,
|
||||
modalOverlayOpeningYOffset = 0
|
||||
} = step.options;
|
||||
|
||||
const iframeOffset = _getIframeOffset(step.target);
|
||||
const scrollParent = _getScrollParent(step.target);
|
||||
|
||||
// Setup recursive function to call requestAnimationFrame to update the modal opening position
|
||||
const rafLoop = () => {
|
||||
rafId = undefined;
|
||||
positionModal(
|
||||
modalOverlayOpeningPadding,
|
||||
modalOverlayOpeningRadius,
|
||||
modalOverlayOpeningXOffset + iframeOffset.left,
|
||||
modalOverlayOpeningYOffset + iframeOffset.top,
|
||||
scrollParent,
|
||||
step.target,
|
||||
step._resolvedExtraHighlightElements
|
||||
);
|
||||
rafId = requestAnimationFrame(rafLoop);
|
||||
};
|
||||
|
||||
rafLoop();
|
||||
|
||||
_addStepEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the closest scrollable parent element
|
||||
* @param {HTMLElement} element The target element
|
||||
* @returns {HTMLElement}
|
||||
* @private
|
||||
*/
|
||||
function _getScrollParent(element) {
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isHtmlElement = element instanceof HTMLElement;
|
||||
const overflowY =
|
||||
isHtmlElement && window.getComputedStyle(element).overflowY;
|
||||
const isScrollable = overflowY !== 'hidden' && overflowY !== 'visible';
|
||||
|
||||
if (isScrollable && element.scrollHeight >= element.clientHeight) {
|
||||
return element;
|
||||
}
|
||||
|
||||
return _getScrollParent(element.parentElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the top and left offset required to position the modal overlay cutout
|
||||
* when the target element is within an iframe
|
||||
* @param {HTMLElement} element The target element
|
||||
* @private
|
||||
*/
|
||||
function _getIframeOffset(element) {
|
||||
let offset = {
|
||||
top: 0,
|
||||
left: 0
|
||||
};
|
||||
|
||||
if (!element) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
let targetWindow = element.ownerDocument.defaultView;
|
||||
|
||||
while (targetWindow !== window.top) {
|
||||
const targetIframe = targetWindow?.frameElement;
|
||||
|
||||
if (targetIframe) {
|
||||
const targetIframeRect = targetIframe.getBoundingClientRect();
|
||||
|
||||
offset.top += targetIframeRect.top + (targetIframeRect.scrollTop ?? 0);
|
||||
offset.left +=
|
||||
targetIframeRect.left + (targetIframeRect.scrollLeft ?? 0);
|
||||
}
|
||||
|
||||
targetWindow = targetWindow.parent;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visible height of the target element relative to its scrollParent.
|
||||
* If there is no scroll parent, the height of the element is returned.
|
||||
*
|
||||
* @param {HTMLElement} element The target element
|
||||
* @param {HTMLElement} [scrollParent] The scrollable parent element
|
||||
* @returns {{y: number, height: number}}
|
||||
* @private
|
||||
*/
|
||||
function _getVisibleHeight(element, scrollParent) {
|
||||
const elementRect = element.getBoundingClientRect();
|
||||
let top = elementRect.y || elementRect.top;
|
||||
let bottom = elementRect.bottom || top + elementRect.height;
|
||||
|
||||
if (scrollParent) {
|
||||
const scrollRect = scrollParent.getBoundingClientRect();
|
||||
const scrollTop = scrollRect.y || scrollRect.top;
|
||||
const scrollBottom = scrollRect.bottom || scrollTop + scrollRect.height;
|
||||
|
||||
top = Math.max(top, scrollTop);
|
||||
bottom = Math.min(bottom, scrollBottom);
|
||||
}
|
||||
|
||||
const height = Math.max(bottom - top, 0); // Default to 0 if height is negative
|
||||
|
||||
return { y: top, height };
|
||||
}
|
||||
</script>
|
||||
|
||||
<svg
|
||||
bind:this={element}
|
||||
class={`${
|
||||
modalIsVisible ? 'shepherd-modal-is-visible' : ''
|
||||
} shepherd-modal-overlay-container`}
|
||||
on:touchmove={_preventModalOverlayTouch}
|
||||
>
|
||||
<path d={pathDefinition} />
|
||||
</svg>
|
||||
|
||||
<style global>
|
||||
.shepherd-modal-overlay-container {
|
||||
height: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
transition:
|
||||
all 0.3s ease-out,
|
||||
height 0ms 0.3s,
|
||||
opacity 0.3s 0ms;
|
||||
width: 100vw;
|
||||
z-index: 9997;
|
||||
}
|
||||
|
||||
.shepherd-modal-overlay-container.shepherd-modal-is-visible {
|
||||
height: 100vh;
|
||||
opacity: 0.5;
|
||||
transition:
|
||||
all 0.3s ease-out,
|
||||
height 0s 0s,
|
||||
opacity 0.3s 0s;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.shepherd-modal-overlay-container.shepherd-modal-is-visible path {
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
39
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-text.svelte
generated
vendored
Normal file
39
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-text.svelte
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
<script>
|
||||
import { afterUpdate } from 'svelte';
|
||||
import { isHTMLElement, isFunction } from '../utils/type-check.ts';
|
||||
|
||||
export let descriptionId, element, step;
|
||||
|
||||
afterUpdate(() => {
|
||||
let { text } = step.options;
|
||||
|
||||
if (isFunction(text)) {
|
||||
text = text.call(step);
|
||||
}
|
||||
|
||||
if (isHTMLElement(text)) {
|
||||
element.appendChild(text);
|
||||
} else {
|
||||
element.innerHTML = text;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={element} class="shepherd-text" id={descriptionId}></div>
|
||||
|
||||
<style global>
|
||||
.shepherd-text {
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
font-size: 1rem;
|
||||
line-height: 1.3em;
|
||||
padding: 0.75em;
|
||||
}
|
||||
|
||||
.shepherd-text p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.shepherd-text p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
29
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-title.svelte
generated
vendored
Normal file
29
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/components/shepherd-title.svelte
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
<script>
|
||||
import { afterUpdate } from 'svelte';
|
||||
import { isFunction } from '../utils/type-check.ts';
|
||||
|
||||
export let labelId, element, title;
|
||||
|
||||
afterUpdate(() => {
|
||||
if (isFunction(title)) {
|
||||
title = title();
|
||||
}
|
||||
|
||||
element.innerHTML = title;
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-missing-content -->
|
||||
<h3 bind:this={element} id={labelId} class="shepherd-title"></h3>
|
||||
|
||||
<style global>
|
||||
.shepherd-title {
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
display: flex;
|
||||
font-size: 1rem;
|
||||
font-weight: normal;
|
||||
flex: 1 0 auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
95
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/evented.ts
generated
vendored
Normal file
95
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/evented.ts
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
import { isUndefined } from './utils/type-check.ts';
|
||||
|
||||
export type Bindings = {
|
||||
[key: string]: Array<{ handler: () => void; ctx?: unknown; once?: boolean }>;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type AnyHandler = (...args: any[]) => void;
|
||||
|
||||
export class Evented {
|
||||
declare bindings: Bindings;
|
||||
|
||||
/**
|
||||
* Adds an event listener for the given event string.
|
||||
*
|
||||
* @param {string} event
|
||||
* @param {Function} handler
|
||||
* @param ctx
|
||||
* @param {boolean} once
|
||||
* @returns
|
||||
*/
|
||||
on(event: string, handler: AnyHandler, ctx?: unknown, once = false) {
|
||||
if (isUndefined(this.bindings)) {
|
||||
this.bindings = {};
|
||||
}
|
||||
if (isUndefined(this.bindings[event])) {
|
||||
this.bindings[event] = [];
|
||||
}
|
||||
this.bindings[event]?.push({ handler, ctx, once });
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event listener that only fires once for the given event string.
|
||||
*
|
||||
* @param {string} event
|
||||
* @param {Function} handler
|
||||
* @param ctx
|
||||
* @returns
|
||||
*/
|
||||
once(event: string, handler: AnyHandler, ctx?: unknown) {
|
||||
return this.on(event, handler, ctx, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an event listener for the given event string.
|
||||
*
|
||||
* @param {string} event
|
||||
* @param {Function} handler
|
||||
* @returns
|
||||
*/
|
||||
off(event: string, handler?: AnyHandler) {
|
||||
if (isUndefined(this.bindings) || isUndefined(this.bindings[event])) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (isUndefined(handler)) {
|
||||
delete this.bindings[event];
|
||||
} else {
|
||||
this.bindings[event]?.forEach((binding, index) => {
|
||||
if (binding.handler === handler) {
|
||||
this.bindings[event]?.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers an event listener for the given event string.
|
||||
*
|
||||
* @param {string} event
|
||||
* @returns
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
trigger(event: string, ...args: any[]) {
|
||||
if (!isUndefined(this.bindings) && this.bindings[event]) {
|
||||
this.bindings[event]?.forEach((binding, index) => {
|
||||
const { ctx, handler, once } = binding;
|
||||
|
||||
const context = ctx || this;
|
||||
|
||||
handler.apply(context, args as []);
|
||||
|
||||
if (once) {
|
||||
this.bindings[event]?.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
15
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/shepherd.ts
generated
vendored
Normal file
15
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/shepherd.ts
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Shepherd, Tour } from './tour.ts';
|
||||
import { StepNoOp, TourNoOp } from './utils/general.ts';
|
||||
import { Step } from './step.ts';
|
||||
|
||||
const isServerSide = typeof window === 'undefined';
|
||||
|
||||
Shepherd.Step = (isServerSide ? StepNoOp : Step) as unknown as typeof Step;
|
||||
Shepherd.Tour = (isServerSide ? TourNoOp : Tour) as unknown as typeof Tour;
|
||||
|
||||
export { ShepherdBase } from './tour.ts';
|
||||
export default Shepherd;
|
||||
// Reexport types so they can be more easily used.
|
||||
export type * from './evented.ts';
|
||||
export type * from './step.ts';
|
||||
export type * from './tour.ts';
|
726
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/step.ts
generated
vendored
Normal file
726
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/step.ts
generated
vendored
Normal file
|
@ -0,0 +1,726 @@
|
|||
import { deepmerge } from 'deepmerge-ts';
|
||||
import { Evented } from './evented.ts';
|
||||
import autoBind from './utils/auto-bind.ts';
|
||||
import {
|
||||
isElement,
|
||||
isHTMLElement,
|
||||
isFunction,
|
||||
isUndefined
|
||||
} from './utils/type-check.ts';
|
||||
import { bindAdvance } from './utils/bind.ts';
|
||||
import {
|
||||
parseAttachTo,
|
||||
normalizePrefix,
|
||||
uuid,
|
||||
parseExtraHighlights
|
||||
} from './utils/general.ts';
|
||||
import {
|
||||
setupTooltip,
|
||||
destroyTooltip,
|
||||
mergeTooltipConfig
|
||||
} from './utils/floating-ui.ts';
|
||||
// @ts-expect-error TODO: we don't have Svelte .d.ts files until we generate the dist
|
||||
import ShepherdElement from './components/shepherd-element.svelte';
|
||||
import { type Tour } from './tour.ts';
|
||||
import type { ComputePositionConfig } from '@floating-ui/dom';
|
||||
|
||||
export type StepText =
|
||||
| string
|
||||
| ReadonlyArray<string>
|
||||
| HTMLElement
|
||||
| (() => string | ReadonlyArray<string> | HTMLElement);
|
||||
|
||||
export type StringOrStringFunction = string | (() => string);
|
||||
|
||||
/**
|
||||
* The options for the step
|
||||
*/
|
||||
export interface StepOptions {
|
||||
/**
|
||||
* The element the step should be attached to on the page.
|
||||
* An object with properties `element` and `on`.
|
||||
*
|
||||
* ```js
|
||||
* const step = new Step(tour, {
|
||||
* attachTo: { element: '.some .selector-path', on: 'left' },
|
||||
* ...moreOptions
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* If you don’t specify an attachTo the element will appear in the middle of the screen.
|
||||
* If you omit the `on` portion of `attachTo`, the element will still be highlighted, but the tooltip will appear
|
||||
* in the middle of the screen, without an arrow pointing to the target.
|
||||
*/
|
||||
attachTo?: StepOptionsAttachTo;
|
||||
|
||||
/**
|
||||
* An action on the page which should advance shepherd to the next step.
|
||||
* It should be an object with a string `selector` and an `event` name
|
||||
* ```js
|
||||
* const step = new Step(tour, {
|
||||
* advanceOn: { selector: '.some .selector-path', event: 'click' },
|
||||
* ...moreOptions
|
||||
* });
|
||||
* ```
|
||||
* `event` doesn’t have to be an event inside the tour, it can be any event fired on any element on the page.
|
||||
* You can also always manually advance the Tour by calling `myTour.next()`.
|
||||
*/
|
||||
advanceOn?: StepOptionsAdvanceOn;
|
||||
|
||||
/**
|
||||
* Whether to display the arrow for the tooltip or not, or options for the arrow.
|
||||
*/
|
||||
arrow?: boolean | StepOptionsArrow;
|
||||
|
||||
/**
|
||||
* A function that returns a promise.
|
||||
* When the promise resolves, the rest of the `show` code for the step will execute.
|
||||
*/
|
||||
beforeShowPromise?: () => Promise<unknown>;
|
||||
|
||||
/**
|
||||
* An array of buttons to add to the step. These will be rendered in a
|
||||
* footer below the main body text.
|
||||
*/
|
||||
buttons?: ReadonlyArray<StepOptionsButton>;
|
||||
|
||||
/**
|
||||
* Should a cancel “✕” be shown in the header of the step?
|
||||
*/
|
||||
cancelIcon?: StepOptionsCancelIcon;
|
||||
|
||||
/**
|
||||
* A boolean, that when set to false, will set `pointer-events: none` on the target.
|
||||
*/
|
||||
canClickTarget?: boolean;
|
||||
|
||||
/**
|
||||
* A string of extra classes to add to the step's content element.
|
||||
*/
|
||||
classes?: string;
|
||||
|
||||
/**
|
||||
* An array of extra element selectors to highlight when the overlay is shown
|
||||
* The tooltip won't be fixed to these elements, but they will be highlighted
|
||||
* just like the `attachTo` element.
|
||||
* ```js
|
||||
* const step = new Step(tour, {
|
||||
* extraHighlights: [ '.pricing', '#docs' ],
|
||||
* ...moreOptions
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
extraHighlights?: ReadonlyArray<string>;
|
||||
|
||||
/**
|
||||
* An extra class to apply to the `attachTo` element when it is
|
||||
* highlighted (that is, when its step is active). You can then target that selector in your CSS.
|
||||
*/
|
||||
highlightClass?: string;
|
||||
|
||||
/**
|
||||
* The string to use as the `id` for the step.
|
||||
*/
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* An amount of padding to add around the modal overlay opening
|
||||
*/
|
||||
modalOverlayOpeningPadding?: number;
|
||||
|
||||
/**
|
||||
* An amount of border radius to add around the modal overlay opening
|
||||
*/
|
||||
modalOverlayOpeningRadius?:
|
||||
| number
|
||||
| {
|
||||
topLeft?: number;
|
||||
bottomLeft?: number;
|
||||
bottomRight?: number;
|
||||
topRight?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* An amount to offset the modal overlay opening in the x-direction
|
||||
*/
|
||||
modalOverlayOpeningXOffset?: number;
|
||||
|
||||
/**
|
||||
* An amount to offset the modal overlay opening in the y-direction
|
||||
*/
|
||||
modalOverlayOpeningYOffset?: number;
|
||||
|
||||
/**
|
||||
* Extra [options to pass to FloatingUI]{@link https://floating-ui.com/docs/tutorial/}
|
||||
*/
|
||||
floatingUIOptions?: ComputePositionConfig;
|
||||
|
||||
/**
|
||||
* Should the element be scrolled to when this step is shown?
|
||||
*/
|
||||
scrollTo?: boolean | ScrollIntoViewOptions;
|
||||
|
||||
/**
|
||||
* A function that lets you override the default scrollTo behavior and
|
||||
* define a custom action to do the scrolling, and possibly other logic.
|
||||
*/
|
||||
scrollToHandler?: (element: HTMLElement) => void;
|
||||
|
||||
/**
|
||||
* A function that, when it returns `true`, will show the step.
|
||||
* If it returns `false`, the step will be skipped.
|
||||
*/
|
||||
showOn?: () => boolean;
|
||||
|
||||
/**
|
||||
* The text in the body of the step. It can be one of four types:
|
||||
* ```
|
||||
* - HTML string
|
||||
* - Array of HTML strings
|
||||
* - `HTMLElement` object
|
||||
* - `Function` to be executed when the step is built. It must return one of the three options above.
|
||||
* ```
|
||||
*/
|
||||
text?: StepText;
|
||||
|
||||
/**
|
||||
* The step's title. It becomes an `h3` at the top of the step.
|
||||
* ```
|
||||
* - HTML string
|
||||
* - `Function` to be executed when the step is built. It must return HTML string.
|
||||
* ```
|
||||
*/
|
||||
title?: StringOrStringFunction;
|
||||
|
||||
/**
|
||||
* You can define `show`, `hide`, etc events inside `when`. For example:
|
||||
* ```js
|
||||
* when: {
|
||||
* show: function() {
|
||||
* window.scrollTo(0, 0);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
when?: StepOptionsWhen;
|
||||
}
|
||||
|
||||
export type PopperPlacement =
|
||||
| 'auto'
|
||||
| 'auto-start'
|
||||
| 'auto-end'
|
||||
| 'top'
|
||||
| 'top-start'
|
||||
| 'top-end'
|
||||
| 'bottom'
|
||||
| 'bottom-start'
|
||||
| 'bottom-end'
|
||||
| 'right'
|
||||
| 'right-start'
|
||||
| 'right-end'
|
||||
| 'left'
|
||||
| 'left-start'
|
||||
| 'left-end';
|
||||
|
||||
export interface StepOptionsArrow {
|
||||
/*
|
||||
* The padding from the edge for the arrow.
|
||||
* Not used if this is not a -start or -end placement.
|
||||
*/
|
||||
padding?: number;
|
||||
}
|
||||
|
||||
export interface StepOptionsAttachTo {
|
||||
element?:
|
||||
| HTMLElement
|
||||
| string
|
||||
| null
|
||||
| (() => HTMLElement | string | null | undefined);
|
||||
on?: PopperPlacement;
|
||||
}
|
||||
|
||||
export interface StepOptionsAdvanceOn {
|
||||
event: string;
|
||||
selector: string;
|
||||
}
|
||||
|
||||
export interface StepOptionsButton {
|
||||
/**
|
||||
* A function executed when the button is clicked on
|
||||
* It is automatically bound to the `tour` the step is associated with, so things like `this.next` will
|
||||
* work inside the action.
|
||||
* You can use action to skip steps or navigate to specific steps, with something like:
|
||||
* ```js
|
||||
* action() {
|
||||
* return this.show('some_step_name');
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
action?: (this: Tour) => void;
|
||||
|
||||
/**
|
||||
* Extra classes to apply to the `<a>`
|
||||
*/
|
||||
classes?: string;
|
||||
|
||||
/**
|
||||
* Whether the button should be disabled
|
||||
* When the value is `true`, or the function returns `true` the button will be disabled
|
||||
*/
|
||||
disabled?: boolean | (() => boolean);
|
||||
|
||||
/**
|
||||
* The aria-label text of the button
|
||||
*/
|
||||
label?: StringOrStringFunction;
|
||||
|
||||
/**
|
||||
* A boolean, that when true, adds a `shepherd-button-secondary` class to the button.
|
||||
*/
|
||||
secondary?: boolean;
|
||||
|
||||
/**
|
||||
* The HTML text of the button
|
||||
*/
|
||||
text?: StringOrStringFunction;
|
||||
}
|
||||
|
||||
export interface StepOptionsButtonEvent {
|
||||
[key: string]: () => void;
|
||||
}
|
||||
|
||||
export interface StepOptionsCancelIcon {
|
||||
enabled?: boolean;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export interface StepOptionsWhen {
|
||||
[key: string]: (this: Step) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A class representing steps to be added to a tour.
|
||||
* @extends {Evented}
|
||||
*/
|
||||
export class Step extends Evented {
|
||||
_resolvedAttachTo: StepOptionsAttachTo | null;
|
||||
_resolvedExtraHighlightElements?: HTMLElement[];
|
||||
classPrefix?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
declare cleanup: Function | null;
|
||||
el?: HTMLElement | null;
|
||||
declare id: string;
|
||||
declare options: StepOptions;
|
||||
target?: HTMLElement | null;
|
||||
tour: Tour;
|
||||
|
||||
constructor(tour: Tour, options: StepOptions = {}) {
|
||||
super();
|
||||
|
||||
this.tour = tour;
|
||||
this.classPrefix = this.tour.options
|
||||
? normalizePrefix(this.tour.options.classPrefix)
|
||||
: '';
|
||||
// @ts-expect-error TODO: investigate where styles comes from
|
||||
this.styles = tour.styles;
|
||||
|
||||
/**
|
||||
* Resolved attachTo options. Due to lazy evaluation, we only resolve the options during `before-show` phase.
|
||||
* Do not use this directly, use the _getResolvedAttachToOptions method instead.
|
||||
* @type {StepOptionsAttachTo | null}
|
||||
* @private
|
||||
*/
|
||||
this._resolvedAttachTo = null;
|
||||
|
||||
autoBind(this);
|
||||
|
||||
this._setOptions(options);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the tour
|
||||
* Triggers the `cancel` event
|
||||
*/
|
||||
cancel() {
|
||||
this.tour.cancel();
|
||||
this.trigger('cancel');
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the tour
|
||||
* Triggers the `complete` event
|
||||
*/
|
||||
complete() {
|
||||
this.tour.complete();
|
||||
this.trigger('complete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the step, delete the step's element, and destroy the FloatingUI instance for the step.
|
||||
* Triggers `destroy` event
|
||||
*/
|
||||
destroy() {
|
||||
destroyTooltip(this);
|
||||
|
||||
if (isHTMLElement(this.el)) {
|
||||
this.el.remove();
|
||||
this.el = null;
|
||||
}
|
||||
|
||||
this._updateStepTargetOnHide();
|
||||
|
||||
this.trigger('destroy');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tour for the step
|
||||
* @return The tour instance
|
||||
*/
|
||||
getTour() {
|
||||
return this.tour;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the step
|
||||
*/
|
||||
hide() {
|
||||
this.tour.modal?.hide();
|
||||
|
||||
this.trigger('before-hide');
|
||||
|
||||
if (this.el) {
|
||||
this.el.hidden = true;
|
||||
}
|
||||
|
||||
this._updateStepTargetOnHide();
|
||||
|
||||
this.trigger('hide');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves attachTo options.
|
||||
* @returns {{}|{element, on}}
|
||||
*/
|
||||
_resolveExtraHiglightElements() {
|
||||
this._resolvedExtraHighlightElements = parseExtraHighlights(this);
|
||||
return this._resolvedExtraHighlightElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves attachTo options.
|
||||
* @returns {{}|{element, on}}
|
||||
*/
|
||||
_resolveAttachToOptions() {
|
||||
this._resolvedAttachTo = parseAttachTo(this);
|
||||
return this._resolvedAttachTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* A selector for resolved attachTo options.
|
||||
* @returns {{}|{element, on}}
|
||||
* @private
|
||||
*/
|
||||
_getResolvedAttachToOptions() {
|
||||
if (this._resolvedAttachTo === null) {
|
||||
return this._resolveAttachToOptions();
|
||||
}
|
||||
|
||||
return this._resolvedAttachTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the step is open and visible
|
||||
* @return True if the step is open and visible
|
||||
*/
|
||||
isOpen() {
|
||||
return Boolean(this.el && !this.el.hidden);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps `_show` and ensures `beforeShowPromise` resolves before calling show
|
||||
*/
|
||||
show() {
|
||||
if (isFunction(this.options.beforeShowPromise)) {
|
||||
return Promise.resolve(this.options.beforeShowPromise()).then(() =>
|
||||
this._show()
|
||||
);
|
||||
}
|
||||
return Promise.resolve(this._show());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the options of the step.
|
||||
*
|
||||
* @param {StepOptions} options The options for the step
|
||||
*/
|
||||
updateStepOptions(options: StepOptions) {
|
||||
Object.assign(this.options, options);
|
||||
|
||||
// @ts-expect-error TODO: get types for Svelte components
|
||||
if (this.shepherdElementComponent) {
|
||||
// @ts-expect-error TODO: get types for Svelte components
|
||||
this.shepherdElementComponent.$set({ step: this });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element for the step
|
||||
* @return {HTMLElement|null|undefined} The element instance. undefined if it has never been shown, null if it has been destroyed
|
||||
*/
|
||||
getElement() {
|
||||
return this.el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target for the step
|
||||
* @return {HTMLElement|null|undefined} The element instance. undefined if it has never been shown, null if query string has not been found
|
||||
*/
|
||||
getTarget() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Shepherd element for step based on options
|
||||
*
|
||||
* @return {HTMLElement} The DOM element for the step tooltip
|
||||
* @private
|
||||
*/
|
||||
_createTooltipContent() {
|
||||
const descriptionId = `${this.id}-description`;
|
||||
const labelId = `${this.id}-label`;
|
||||
|
||||
// @ts-expect-error TODO: get types for Svelte components
|
||||
this.shepherdElementComponent = new ShepherdElement({
|
||||
target: this.tour.options.stepsContainer || document.body,
|
||||
props: {
|
||||
classPrefix: this.classPrefix,
|
||||
descriptionId,
|
||||
labelId,
|
||||
step: this,
|
||||
// @ts-expect-error TODO: investigate where styles comes from
|
||||
styles: this.styles
|
||||
}
|
||||
});
|
||||
|
||||
// @ts-expect-error TODO: get types for Svelte components
|
||||
return this.shepherdElementComponent.getElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* If a custom scrollToHandler is defined, call that, otherwise do the generic
|
||||
* scrollIntoView call.
|
||||
*
|
||||
* @param {boolean | ScrollIntoViewOptions} scrollToOptions - If true, uses the default `scrollIntoView`,
|
||||
* if an object, passes that object as the params to `scrollIntoView` i.e. `{ behavior: 'smooth', block: 'center' }`
|
||||
* @private
|
||||
*/
|
||||
_scrollTo(scrollToOptions: boolean | ScrollIntoViewOptions) {
|
||||
const { element } = this._getResolvedAttachToOptions();
|
||||
|
||||
if (isFunction(this.options.scrollToHandler)) {
|
||||
this.options.scrollToHandler(element as HTMLElement);
|
||||
} else if (
|
||||
isElement(element) &&
|
||||
typeof element.scrollIntoView === 'function'
|
||||
) {
|
||||
element.scrollIntoView(scrollToOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _getClassOptions gets all possible classes for the step
|
||||
* @param {StepOptions} stepOptions The step specific options
|
||||
* @returns {string} unique string from array of classes
|
||||
*/
|
||||
_getClassOptions(stepOptions: StepOptions) {
|
||||
const defaultStepOptions =
|
||||
this.tour && this.tour.options && this.tour.options.defaultStepOptions;
|
||||
const stepClasses = stepOptions.classes ? stepOptions.classes : '';
|
||||
const defaultStepOptionsClasses =
|
||||
defaultStepOptions && defaultStepOptions.classes
|
||||
? defaultStepOptions.classes
|
||||
: '';
|
||||
const allClasses = [
|
||||
...stepClasses.split(' '),
|
||||
...defaultStepOptionsClasses.split(' ')
|
||||
];
|
||||
const uniqClasses = new Set(allClasses);
|
||||
|
||||
return Array.from(uniqClasses).join(' ').trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the options for the step, maps `when` to events, sets up buttons
|
||||
* @param options - The options for the step
|
||||
*/
|
||||
_setOptions(options: StepOptions = {}) {
|
||||
let tourOptions =
|
||||
this.tour && this.tour.options && this.tour.options.defaultStepOptions;
|
||||
|
||||
tourOptions = deepmerge({}, tourOptions || {});
|
||||
|
||||
this.options = Object.assign(
|
||||
{
|
||||
arrow: true
|
||||
},
|
||||
tourOptions,
|
||||
options,
|
||||
mergeTooltipConfig(tourOptions, options)
|
||||
);
|
||||
|
||||
const { when } = this.options;
|
||||
|
||||
this.options.classes = this._getClassOptions(options);
|
||||
|
||||
this.destroy();
|
||||
this.id = this.options.id || `step-${uuid()}`;
|
||||
|
||||
if (when) {
|
||||
Object.keys(when).forEach((event) => {
|
||||
// @ts-expect-error TODO: fix this type error
|
||||
this.on(event, when[event], this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the element and set up the FloatingUI instance
|
||||
* @private
|
||||
*/
|
||||
_setupElements() {
|
||||
if (!isUndefined(this.el)) {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
this.el = this._createTooltipContent();
|
||||
|
||||
if (this.options.advanceOn) {
|
||||
bindAdvance(this);
|
||||
}
|
||||
|
||||
// The tooltip implementation details are handled outside of the Step
|
||||
// object.
|
||||
setupTooltip(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers `before-show`, generates the tooltip DOM content,
|
||||
* sets up a FloatingUI instance for the tooltip, then triggers `show`.
|
||||
* @private
|
||||
*/
|
||||
_show() {
|
||||
this.trigger('before-show');
|
||||
|
||||
// Force resolve to make sure the options are updated on subsequent shows.
|
||||
this._resolveAttachToOptions();
|
||||
this._resolveExtraHiglightElements();
|
||||
this._setupElements();
|
||||
|
||||
if (!this.tour.modal) {
|
||||
this.tour.setupModal();
|
||||
}
|
||||
|
||||
this.tour.modal?.setupForStep(this);
|
||||
this._styleTargetElementForStep(this);
|
||||
|
||||
if (this.el) {
|
||||
this.el.hidden = false;
|
||||
}
|
||||
|
||||
// start scrolling to target before showing the step
|
||||
if (this.options.scrollTo) {
|
||||
setTimeout(() => {
|
||||
this._scrollTo(
|
||||
this.options.scrollTo as boolean | ScrollIntoViewOptions
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.el) {
|
||||
this.el.hidden = false;
|
||||
}
|
||||
|
||||
// @ts-expect-error TODO: get types for Svelte components
|
||||
const content = this.shepherdElementComponent.getElement();
|
||||
const target = this.target || document.body;
|
||||
const extraHighlightElements = this._resolvedExtraHighlightElements;
|
||||
|
||||
target.classList.add(`${this.classPrefix}shepherd-enabled`);
|
||||
target.classList.add(`${this.classPrefix}shepherd-target`);
|
||||
content.classList.add('shepherd-enabled');
|
||||
|
||||
extraHighlightElements?.forEach((el) => {
|
||||
el.classList.add(`${this.classPrefix}shepherd-enabled`);
|
||||
el.classList.add(`${this.classPrefix}shepherd-target`);
|
||||
});
|
||||
|
||||
this.trigger('show');
|
||||
}
|
||||
|
||||
/**
|
||||
* Modulates the styles of the passed step's target element, based on the step's options and
|
||||
* the tour's `modal` option, to visually emphasize the element
|
||||
*
|
||||
* @param {Step} step The step object that attaches to the element
|
||||
* @private
|
||||
*/
|
||||
_styleTargetElementForStep(step: Step) {
|
||||
const targetElement = step.target;
|
||||
const extraHighlightElements = step._resolvedExtraHighlightElements;
|
||||
|
||||
if (!targetElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const highlightClass = step.options.highlightClass;
|
||||
if (highlightClass) {
|
||||
targetElement.classList.add(highlightClass);
|
||||
extraHighlightElements?.forEach((el) => el.classList.add(highlightClass));
|
||||
}
|
||||
|
||||
targetElement.classList.remove('shepherd-target-click-disabled');
|
||||
extraHighlightElements?.forEach((el) =>
|
||||
el.classList.remove('shepherd-target-click-disabled')
|
||||
);
|
||||
|
||||
if (step.options.canClickTarget === false) {
|
||||
targetElement.classList.add('shepherd-target-click-disabled');
|
||||
extraHighlightElements?.forEach((el) =>
|
||||
el.classList.add('shepherd-target-click-disabled')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When a step is hidden, remove the highlightClass and 'shepherd-enabled'
|
||||
* and 'shepherd-target' classes
|
||||
* @private
|
||||
*/
|
||||
_updateStepTargetOnHide() {
|
||||
const target = this.target || document.body;
|
||||
const extraHighlightElements = this._resolvedExtraHighlightElements;
|
||||
|
||||
const highlightClass = this.options.highlightClass;
|
||||
if (highlightClass) {
|
||||
target.classList.remove(highlightClass);
|
||||
extraHighlightElements?.forEach((el) =>
|
||||
el.classList.remove(highlightClass)
|
||||
);
|
||||
}
|
||||
|
||||
target.classList.remove(
|
||||
'shepherd-target-click-disabled',
|
||||
`${this.classPrefix}shepherd-enabled`,
|
||||
`${this.classPrefix}shepherd-target`
|
||||
);
|
||||
extraHighlightElements?.forEach((el) => {
|
||||
el.classList.remove(
|
||||
'shepherd-target-click-disabled',
|
||||
`${this.classPrefix}shepherd-enabled`,
|
||||
`${this.classPrefix}shepherd-target`
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
476
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/tour.ts
generated
vendored
Normal file
476
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/tour.ts
generated
vendored
Normal file
|
@ -0,0 +1,476 @@
|
|||
import { Evented } from './evented.ts';
|
||||
import { Step, type StepOptions } from './step.ts';
|
||||
import autoBind from './utils/auto-bind.ts';
|
||||
import {
|
||||
isHTMLElement,
|
||||
isFunction,
|
||||
isString,
|
||||
isUndefined
|
||||
} from './utils/type-check.ts';
|
||||
import { cleanupSteps } from './utils/cleanup.ts';
|
||||
import { normalizePrefix, uuid } from './utils/general.ts';
|
||||
// @ts-expect-error TODO: we don't have Svelte .d.ts files until we generate the dist
|
||||
import ShepherdModal from './components/shepherd-modal.svelte';
|
||||
|
||||
export interface EventOptions {
|
||||
previous?: Step | null;
|
||||
step?: Step | null;
|
||||
tour: Tour;
|
||||
}
|
||||
|
||||
export type TourConfirmCancel =
|
||||
| boolean
|
||||
| (() => boolean)
|
||||
| Promise<boolean>
|
||||
| (() => Promise<boolean>);
|
||||
|
||||
/**
|
||||
* The options for the tour
|
||||
*/
|
||||
export interface TourOptions {
|
||||
/**
|
||||
* If true, will issue a `window.confirm` before cancelling.
|
||||
* If it is a function(support Async Function), it will be called and wait for the return value,
|
||||
* and will only be cancelled if the value returned is true.
|
||||
*/
|
||||
confirmCancel?: TourConfirmCancel;
|
||||
/**
|
||||
* The message to display in the `window.confirm` dialog.
|
||||
*/
|
||||
confirmCancelMessage?: string;
|
||||
/**
|
||||
* The prefix to add to the `shepherd-enabled` and `shepherd-target` class names as well as the `data-shepherd-step-id`.
|
||||
*/
|
||||
classPrefix?: string;
|
||||
/**
|
||||
* Default options for Steps ({@link Step#constructor}), created through `addStep`.
|
||||
*/
|
||||
defaultStepOptions?: StepOptions;
|
||||
/**
|
||||
* Exiting the tour with the escape key will be enabled unless this is explicitly
|
||||
* set to false.
|
||||
*/
|
||||
exitOnEsc?: boolean;
|
||||
/**
|
||||
* Explicitly set the id for the tour. If not set, the id will be a generated uuid.
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* Navigating the tour via left and right arrow keys will be enabled
|
||||
* unless this is explicitly set to false.
|
||||
*/
|
||||
keyboardNavigation?: boolean;
|
||||
/**
|
||||
* An optional container element for the modal.
|
||||
* If not set, the modal will be appended to `document.body`.
|
||||
*/
|
||||
modalContainer?: HTMLElement;
|
||||
/**
|
||||
* An optional container element for the steps.
|
||||
* If not set, the steps will be appended to `document.body`.
|
||||
*/
|
||||
stepsContainer?: HTMLElement;
|
||||
/**
|
||||
* An array of step options objects or Step instances to initialize the tour with.
|
||||
*/
|
||||
steps?: Array<StepOptions> | Array<Step>;
|
||||
/**
|
||||
* An optional "name" for the tour. This will be appended to the the tour's
|
||||
* dynamically generated `id` property.
|
||||
*/
|
||||
tourName?: string;
|
||||
/**
|
||||
* Whether or not steps should be placed above a darkened
|
||||
* modal overlay. If true, the overlay will create an opening around the target element so that it
|
||||
* can remain interactive
|
||||
*/
|
||||
useModalOverlay?: boolean;
|
||||
}
|
||||
|
||||
export class ShepherdBase extends Evented {
|
||||
activeTour?: Tour | null;
|
||||
declare Step: typeof Step;
|
||||
declare Tour: typeof Tour;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
autoBind(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing the site tour
|
||||
* @extends {Evented}
|
||||
*/
|
||||
export class Tour extends Evented {
|
||||
trackedEvents = ['active', 'cancel', 'complete', 'show'];
|
||||
|
||||
classPrefix: string;
|
||||
currentStep?: Step | null;
|
||||
focusedElBeforeOpen?: HTMLElement | null;
|
||||
id?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
modal?: any | null;
|
||||
options: TourOptions;
|
||||
steps: Array<Step>;
|
||||
|
||||
constructor(options: TourOptions = {}) {
|
||||
super();
|
||||
|
||||
autoBind(this);
|
||||
|
||||
const defaultTourOptions = {
|
||||
exitOnEsc: true,
|
||||
keyboardNavigation: true
|
||||
};
|
||||
|
||||
this.options = Object.assign({}, defaultTourOptions, options);
|
||||
this.classPrefix = normalizePrefix(this.options.classPrefix);
|
||||
this.steps = [];
|
||||
this.addSteps(this.options.steps);
|
||||
|
||||
// Pass these events onto the global Shepherd object
|
||||
const events = [
|
||||
'active',
|
||||
'cancel',
|
||||
'complete',
|
||||
'inactive',
|
||||
'show',
|
||||
'start'
|
||||
];
|
||||
events.map((event) => {
|
||||
((e) => {
|
||||
this.on(e, (opts?: { [key: string]: unknown }) => {
|
||||
opts = opts || {};
|
||||
opts['tour'] = this;
|
||||
Shepherd.trigger(e, opts);
|
||||
});
|
||||
})(event);
|
||||
});
|
||||
|
||||
this._setTourID(options.id);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new step to the tour
|
||||
* @param {StepOptions} options - An object containing step options or a Step instance
|
||||
* @param {number | undefined} index - The optional index to insert the step at. If undefined, the step
|
||||
* is added to the end of the array.
|
||||
* @return The newly added step
|
||||
*/
|
||||
addStep(options: StepOptions | Step, index?: number) {
|
||||
let step = options;
|
||||
|
||||
if (!(step instanceof Step)) {
|
||||
step = new Step(this, step);
|
||||
} else {
|
||||
step.tour = this;
|
||||
}
|
||||
|
||||
if (!isUndefined(index)) {
|
||||
this.steps.splice(index, 0, step as Step);
|
||||
} else {
|
||||
this.steps.push(step as Step);
|
||||
}
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple steps to the tour
|
||||
* @param {Array<StepOptions> | Array<Step> | undefined} steps - The steps to add to the tour
|
||||
*/
|
||||
addSteps(steps?: Array<StepOptions> | Array<Step>) {
|
||||
if (Array.isArray(steps)) {
|
||||
steps.forEach((step) => {
|
||||
this.addStep(step);
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the previous step in the tour
|
||||
*/
|
||||
back() {
|
||||
const index = this.steps.indexOf(this.currentStep as Step);
|
||||
this.show(index - 1, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls _done() triggering the 'cancel' event
|
||||
* If `confirmCancel` is true, will show a window.confirm before cancelling
|
||||
* If `confirmCancel` is a function, will call it and wait for the return value,
|
||||
* and only cancel when the value returned is true
|
||||
*/
|
||||
async cancel() {
|
||||
if (this.options.confirmCancel) {
|
||||
const cancelMessage =
|
||||
this.options.confirmCancelMessage ||
|
||||
'Are you sure you want to stop the tour?';
|
||||
let stopTour;
|
||||
|
||||
if (isFunction(this.options.confirmCancel)) {
|
||||
stopTour = await this.options.confirmCancel();
|
||||
} else {
|
||||
stopTour = window.confirm(cancelMessage);
|
||||
}
|
||||
|
||||
if (stopTour) {
|
||||
this._done('cancel');
|
||||
}
|
||||
} else {
|
||||
this._done('cancel');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls _done() triggering the `complete` event
|
||||
*/
|
||||
complete() {
|
||||
this._done('complete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the step from a given id
|
||||
* @param {number | string} id - The id of the step to retrieve
|
||||
* @return The step corresponding to the `id`
|
||||
*/
|
||||
getById(id: number | string) {
|
||||
return this.steps.find((step) => {
|
||||
return step.id === id;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current step
|
||||
*/
|
||||
getCurrentStep() {
|
||||
return this.currentStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the current step
|
||||
*/
|
||||
hide() {
|
||||
const currentStep = this.getCurrentStep();
|
||||
|
||||
if (currentStep) {
|
||||
return currentStep.hide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the tour is active
|
||||
*/
|
||||
isActive() {
|
||||
return Shepherd.activeTour === this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the next step in the tour
|
||||
* If we are at the end, call `complete`
|
||||
*/
|
||||
next() {
|
||||
const index = this.steps.indexOf(this.currentStep as Step);
|
||||
|
||||
if (index === this.steps.length - 1) {
|
||||
this.complete();
|
||||
} else {
|
||||
this.show(index + 1, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the step from the tour
|
||||
* @param {string} name - The id for the step to remove
|
||||
*/
|
||||
removeStep(name: string) {
|
||||
const current = this.getCurrentStep();
|
||||
|
||||
// Find the step, destroy it and remove it from this.steps
|
||||
this.steps.some((step, i) => {
|
||||
if (step.id === name) {
|
||||
if (step.isOpen()) {
|
||||
step.hide();
|
||||
}
|
||||
|
||||
step.destroy();
|
||||
this.steps.splice(i, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (current && current.id === name) {
|
||||
this.currentStep = undefined;
|
||||
|
||||
// If we have steps left, show the first one, otherwise just cancel the tour
|
||||
this.steps.length ? this.show(0) : this.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a specific step in the tour
|
||||
* @param {number | string} key - The key to look up the step by
|
||||
* @param {boolean} forward - True if we are going forward, false if backward
|
||||
*/
|
||||
show(key: number | string = 0, forward = true) {
|
||||
const step = isString(key) ? this.getById(key) : this.steps[key];
|
||||
|
||||
if (step) {
|
||||
this._updateStateBeforeShow();
|
||||
|
||||
const shouldSkipStep =
|
||||
isFunction(step.options.showOn) && !step.options.showOn();
|
||||
|
||||
// If `showOn` returns false, we want to skip the step, otherwise, show the step like normal
|
||||
if (shouldSkipStep) {
|
||||
this._skipStep(step, forward);
|
||||
} else {
|
||||
this.currentStep = step;
|
||||
this.trigger('show', {
|
||||
step,
|
||||
previous: this.currentStep
|
||||
});
|
||||
|
||||
step.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the tour
|
||||
*/
|
||||
async start() {
|
||||
this.trigger('start');
|
||||
|
||||
// Save the focused element before the tour opens
|
||||
this.focusedElBeforeOpen = document.activeElement as HTMLElement | null;
|
||||
|
||||
this.currentStep = null;
|
||||
|
||||
this.setupModal();
|
||||
|
||||
this._setupActiveTour();
|
||||
this.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the tour is cancelled or completed, basically anytime we exit the tour
|
||||
* @param {string} event - The event name to trigger
|
||||
* @private
|
||||
*/
|
||||
_done(event: string) {
|
||||
const index = this.steps.indexOf(this.currentStep as Step);
|
||||
if (Array.isArray(this.steps)) {
|
||||
this.steps.forEach((step) => step.destroy());
|
||||
}
|
||||
|
||||
cleanupSteps(this);
|
||||
|
||||
this.trigger(event, { index });
|
||||
|
||||
Shepherd.activeTour = null;
|
||||
this.trigger('inactive', { tour: this });
|
||||
|
||||
if (this.modal) {
|
||||
this.modal.hide();
|
||||
}
|
||||
|
||||
if (event === 'cancel' || event === 'complete') {
|
||||
if (this.modal) {
|
||||
const modalContainer = document.querySelector(
|
||||
'.shepherd-modal-overlay-container'
|
||||
);
|
||||
|
||||
if (modalContainer) {
|
||||
modalContainer.remove();
|
||||
this.modal = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Focus the element that was focused before the tour started
|
||||
if (isHTMLElement(this.focusedElBeforeOpen)) {
|
||||
this.focusedElBeforeOpen.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make this tour "active"
|
||||
*/
|
||||
_setupActiveTour() {
|
||||
this.trigger('active', { tour: this });
|
||||
|
||||
Shepherd.activeTour = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* setupModal create the modal container and instance
|
||||
*/
|
||||
setupModal() {
|
||||
this.modal = new ShepherdModal({
|
||||
target: this.options.modalContainer || document.body,
|
||||
props: {
|
||||
// @ts-expect-error TODO: investigate where styles comes from
|
||||
styles: this.styles
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when `showOn` evaluates to false, to skip the step or complete the tour if it's the last step
|
||||
* @param {Step} step - The step to skip
|
||||
* @param {boolean} forward - True if we are going forward, false if backward
|
||||
* @private
|
||||
*/
|
||||
_skipStep(step: Step, forward: boolean) {
|
||||
const index = this.steps.indexOf(step);
|
||||
|
||||
if (index === this.steps.length - 1) {
|
||||
this.complete();
|
||||
} else {
|
||||
const nextIndex = forward ? index + 1 : index - 1;
|
||||
this.show(nextIndex, forward);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Before showing, hide the current step and if the tour is not
|
||||
* already active, call `this._setupActiveTour`.
|
||||
* @private
|
||||
*/
|
||||
_updateStateBeforeShow() {
|
||||
if (this.currentStep) {
|
||||
this.currentStep.hide();
|
||||
}
|
||||
|
||||
if (!this.isActive()) {
|
||||
this._setupActiveTour();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this.id to a provided tourName and id or `${tourName}--${uuid}`
|
||||
* @param {string} optionsId - True if we are going forward, false if backward
|
||||
* @private
|
||||
*/
|
||||
_setTourID(optionsId: string | undefined) {
|
||||
const tourName = this.options.tourName || 'tour';
|
||||
const tourId = optionsId || uuid();
|
||||
|
||||
this.id = `${tourName}--${tourId}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
const Shepherd = new ShepherdBase();
|
||||
|
||||
export { Shepherd };
|
19
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/auto-bind.ts
generated
vendored
Normal file
19
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/auto-bind.ts
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Binds all the methods on a JS Class to the `this` context of the class.
|
||||
* Adapted from https://github.com/sindresorhus/auto-bind
|
||||
* @param self The `this` context of the class
|
||||
* @return The `this` context of the class
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export default function autoBind(self: any) {
|
||||
const keys = Object.getOwnPropertyNames(self.constructor.prototype);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i] as keyof typeof self;
|
||||
const val = self[key];
|
||||
if (key !== 'constructor' && typeof val === 'function') {
|
||||
self[key] = val.bind(self);
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
64
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/bind.ts
generated
vendored
Normal file
64
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/bind.ts
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
import type { Step } from '../step.ts';
|
||||
import { isUndefined } from './type-check.ts';
|
||||
|
||||
/**
|
||||
* Sets up the handler to determine if we should advance the tour
|
||||
* @param step The step instance
|
||||
* @param selector
|
||||
* @private
|
||||
*/
|
||||
function _setupAdvanceOnHandler(step: Step, selector?: string) {
|
||||
return (event: Event) => {
|
||||
if (step.isOpen()) {
|
||||
const targetIsEl = step.el && event.currentTarget === step.el;
|
||||
const targetIsSelector =
|
||||
!isUndefined(selector) &&
|
||||
(event.currentTarget as HTMLElement).matches(selector);
|
||||
|
||||
if (targetIsSelector || targetIsEl) {
|
||||
step.tour.next();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the event handler for advanceOn
|
||||
* @param step The step instance
|
||||
*/
|
||||
export function bindAdvance(step: Step) {
|
||||
// An empty selector matches the step element
|
||||
const { event, selector } = step.options.advanceOn || {};
|
||||
if (event) {
|
||||
const handler = _setupAdvanceOnHandler(step, selector);
|
||||
|
||||
// TODO: this should also bind/unbind on show/hide
|
||||
let el: Element | null = null;
|
||||
|
||||
if (!isUndefined(selector)) {
|
||||
el = document.querySelector(selector);
|
||||
|
||||
if (!el) {
|
||||
return console.error(
|
||||
`No element was found for the selector supplied to advanceOn: ${selector}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (el) {
|
||||
el.addEventListener(event, handler);
|
||||
step.on('destroy', () => {
|
||||
return (el as HTMLElement).removeEventListener(event, handler);
|
||||
});
|
||||
} else {
|
||||
document.body.addEventListener(event, handler, true);
|
||||
step.on('destroy', () => {
|
||||
return document.body.removeEventListener(event, handler, true);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return console.error(
|
||||
'advanceOn was defined, but no event name was passed.'
|
||||
);
|
||||
}
|
||||
}
|
32
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/cleanup.ts
generated
vendored
Normal file
32
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/cleanup.ts
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
import type { Tour } from '../tour.ts';
|
||||
import { isHTMLElement } from './type-check.ts';
|
||||
|
||||
/**
|
||||
* Cleanup the steps and set pointerEvents back to 'auto'
|
||||
* @param tour The tour object
|
||||
*/
|
||||
export function cleanupSteps(tour: Tour) {
|
||||
if (tour) {
|
||||
const { steps } = tour;
|
||||
|
||||
steps.forEach((step) => {
|
||||
if (
|
||||
step.options &&
|
||||
step.options.canClickTarget === false &&
|
||||
step.options.attachTo
|
||||
) {
|
||||
if (isHTMLElement(step.target)) {
|
||||
step.target.classList.remove('shepherd-target-click-disabled');
|
||||
}
|
||||
|
||||
if (step._resolvedExtraHighlightElements) {
|
||||
step._resolvedExtraHighlightElements.forEach((element) => {
|
||||
if (isHTMLElement(element)) {
|
||||
element.classList.remove('shepherd-target-click-disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
240
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/floating-ui.ts
generated
vendored
Normal file
240
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/floating-ui.ts
generated
vendored
Normal file
|
@ -0,0 +1,240 @@
|
|||
import { deepmerge } from 'deepmerge-ts';
|
||||
import { shouldCenterStep } from './general.ts';
|
||||
import {
|
||||
autoUpdate,
|
||||
arrow,
|
||||
computePosition,
|
||||
flip,
|
||||
autoPlacement,
|
||||
limitShift,
|
||||
shift,
|
||||
type ComputePositionConfig,
|
||||
type MiddlewareData,
|
||||
type Placement,
|
||||
type Alignment
|
||||
} from '@floating-ui/dom';
|
||||
import type { Step, StepOptions, StepOptionsAttachTo } from '../step.ts';
|
||||
import { isHTMLElement } from './type-check.ts';
|
||||
|
||||
/**
|
||||
* Determines options for the tooltip and initializes event listeners.
|
||||
*
|
||||
* @param step The step instance
|
||||
*/
|
||||
export function setupTooltip(step: Step): ComputePositionConfig {
|
||||
if (step.cleanup) {
|
||||
step.cleanup();
|
||||
}
|
||||
|
||||
const attachToOptions = step._getResolvedAttachToOptions();
|
||||
|
||||
let target = attachToOptions.element as HTMLElement;
|
||||
const floatingUIOptions = getFloatingUIOptions(attachToOptions, step);
|
||||
const shouldCenter = shouldCenterStep(attachToOptions);
|
||||
|
||||
if (shouldCenter) {
|
||||
target = document.body;
|
||||
// @ts-expect-error TODO: fix this type error when we type Svelte
|
||||
const content = step.shepherdElementComponent.getElement();
|
||||
content.classList.add('shepherd-centered');
|
||||
}
|
||||
|
||||
step.cleanup = autoUpdate(target, step.el as HTMLElement, () => {
|
||||
// The element might have already been removed by the end of the tour.
|
||||
if (!step.el) {
|
||||
step.cleanup?.();
|
||||
return;
|
||||
}
|
||||
|
||||
setPosition(target, step, floatingUIOptions, shouldCenter);
|
||||
});
|
||||
|
||||
step.target = attachToOptions.element as HTMLElement;
|
||||
|
||||
return floatingUIOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge tooltip options handling nested keys.
|
||||
*
|
||||
* @param tourOptions - The default tour options.
|
||||
* @param options - Step specific options.
|
||||
*
|
||||
* @return {floatingUIOptions: FloatingUIOptions}
|
||||
*/
|
||||
export function mergeTooltipConfig(
|
||||
tourOptions: StepOptions,
|
||||
options: StepOptions
|
||||
): { floatingUIOptions: ComputePositionConfig } {
|
||||
return {
|
||||
floatingUIOptions: deepmerge(
|
||||
tourOptions.floatingUIOptions || {},
|
||||
options.floatingUIOptions || {}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup function called when the step is closed/destroyed.
|
||||
*
|
||||
* @param step
|
||||
*/
|
||||
export function destroyTooltip(step: Step) {
|
||||
if (step.cleanup) {
|
||||
step.cleanup();
|
||||
}
|
||||
|
||||
step.cleanup = null;
|
||||
}
|
||||
|
||||
function setPosition(
|
||||
target: HTMLElement,
|
||||
step: Step,
|
||||
floatingUIOptions: ComputePositionConfig,
|
||||
shouldCenter: boolean
|
||||
) {
|
||||
return (
|
||||
computePosition(target, step.el as HTMLElement, floatingUIOptions)
|
||||
.then(floatingUIposition(step, shouldCenter))
|
||||
// Wait before forcing focus.
|
||||
.then(
|
||||
(step: Step) =>
|
||||
new Promise<Step>((resolve) => {
|
||||
setTimeout(() => resolve(step), 300);
|
||||
})
|
||||
)
|
||||
// Replaces focusAfterRender modifier.
|
||||
.then((step: Step) => {
|
||||
if (step?.el) {
|
||||
step.el.tabIndex = 0;
|
||||
step.el.focus({ preventScroll: true });
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function floatingUIposition(step: Step, shouldCenter: boolean) {
|
||||
return ({
|
||||
x,
|
||||
y,
|
||||
placement,
|
||||
middlewareData
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
placement: Placement;
|
||||
middlewareData: MiddlewareData;
|
||||
}) => {
|
||||
if (!step.el) {
|
||||
return step;
|
||||
}
|
||||
|
||||
if (shouldCenter) {
|
||||
Object.assign(step.el.style, {
|
||||
position: 'fixed',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -50%)'
|
||||
});
|
||||
} else {
|
||||
Object.assign(step.el.style, {
|
||||
position: 'absolute',
|
||||
left: `${x}px`,
|
||||
top: `${y}px`
|
||||
});
|
||||
}
|
||||
|
||||
step.el.dataset['popperPlacement'] = placement;
|
||||
|
||||
placeArrow(step.el, middlewareData);
|
||||
|
||||
return step;
|
||||
};
|
||||
}
|
||||
|
||||
function placeArrow(el: HTMLElement, middlewareData: MiddlewareData) {
|
||||
const arrowEl = el.querySelector('.shepherd-arrow');
|
||||
if (isHTMLElement(arrowEl) && middlewareData.arrow) {
|
||||
const { x: arrowX, y: arrowY } = middlewareData.arrow;
|
||||
Object.assign(arrowEl.style, {
|
||||
left: arrowX != null ? `${arrowX}px` : '',
|
||||
top: arrowY != null ? `${arrowY}px` : ''
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Floating UI` options from a set of base `attachTo` options
|
||||
* @param attachToOptions
|
||||
* @param step The step instance
|
||||
* @private
|
||||
*/
|
||||
export function getFloatingUIOptions(
|
||||
attachToOptions: StepOptionsAttachTo,
|
||||
step: Step
|
||||
): ComputePositionConfig {
|
||||
const options: ComputePositionConfig = {
|
||||
strategy: 'absolute'
|
||||
};
|
||||
|
||||
options.middleware = [];
|
||||
|
||||
const arrowEl = addArrow(step);
|
||||
|
||||
const shouldCenter = shouldCenterStep(attachToOptions);
|
||||
|
||||
const hasAutoPlacement = attachToOptions.on?.includes('auto');
|
||||
|
||||
const hasEdgeAlignment =
|
||||
attachToOptions?.on?.includes('-start') ||
|
||||
attachToOptions?.on?.includes('-end');
|
||||
|
||||
if (!shouldCenter) {
|
||||
if (hasAutoPlacement) {
|
||||
options.middleware.push(
|
||||
autoPlacement({
|
||||
crossAxis: true,
|
||||
alignment: hasEdgeAlignment
|
||||
? (attachToOptions?.on?.split('-').pop() as Alignment)
|
||||
: null
|
||||
})
|
||||
);
|
||||
} else {
|
||||
options.middleware.push(flip());
|
||||
}
|
||||
|
||||
options.middleware.push(
|
||||
// Replicate PopperJS default behavior.
|
||||
shift({
|
||||
limiter: limitShift(),
|
||||
crossAxis: true
|
||||
})
|
||||
);
|
||||
|
||||
if (arrowEl) {
|
||||
const arrowOptions =
|
||||
typeof step.options.arrow === 'object'
|
||||
? step.options.arrow
|
||||
: { padding: 4 };
|
||||
|
||||
options.middleware.push(
|
||||
arrow({
|
||||
element: arrowEl,
|
||||
padding: hasEdgeAlignment ? arrowOptions.padding : 0
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!hasAutoPlacement) options.placement = attachToOptions.on as Placement;
|
||||
}
|
||||
|
||||
return deepmerge(options, step.options.floatingUIOptions || {});
|
||||
}
|
||||
|
||||
function addArrow(step: Step) {
|
||||
if (step.options.arrow && step.el) {
|
||||
return step.el.querySelector('.shepherd-arrow');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
103
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/general.ts
generated
vendored
Normal file
103
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/general.ts
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
import { type Tour, type TourOptions } from '../tour.ts';
|
||||
import {
|
||||
type StepOptionsAttachTo,
|
||||
type Step,
|
||||
type StepOptions
|
||||
} from '../step.ts';
|
||||
import { isFunction, isString } from './type-check.ts';
|
||||
|
||||
export class StepNoOp {
|
||||
constructor(_options: StepOptions) {}
|
||||
}
|
||||
|
||||
export class TourNoOp {
|
||||
constructor(_tour: Tour, _options: TourOptions) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure class prefix ends in `-`
|
||||
* @param prefix - The prefix to prepend to the class names generated by nano-css
|
||||
* @return The prefix ending in `-`
|
||||
*/
|
||||
export function normalizePrefix(prefix?: string) {
|
||||
if (!isString(prefix) || prefix === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return prefix.charAt(prefix.length - 1) !== '-' ? `${prefix}-` : prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves attachTo options, converting element option value to a qualified HTMLElement.
|
||||
* @param step - The step instance
|
||||
* @returns {{}|{element, on}}
|
||||
* `element` is a qualified HTML Element
|
||||
* `on` is a string position value
|
||||
*/
|
||||
export function parseAttachTo(step: Step) {
|
||||
const options = step.options.attachTo || {};
|
||||
const returnOpts = Object.assign({}, options);
|
||||
|
||||
if (isFunction(returnOpts.element)) {
|
||||
// Bind the callback to step so that it has access to the object, to enable running additional logic
|
||||
returnOpts.element = returnOpts.element.call(step);
|
||||
}
|
||||
|
||||
if (isString(returnOpts.element)) {
|
||||
// Can't override the element in user opts reference because we can't
|
||||
// guarantee that the element will exist in the future.
|
||||
try {
|
||||
returnOpts.element = document.querySelector(
|
||||
returnOpts.element
|
||||
) as HTMLElement;
|
||||
} catch (e) {
|
||||
// TODO
|
||||
}
|
||||
if (!returnOpts.element) {
|
||||
console.error(
|
||||
`The element for this Shepherd step was not found ${options.element}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return returnOpts;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolves the step's `extraHighlights` option, converting any locator values to HTMLElements.
|
||||
*/
|
||||
export function parseExtraHighlights(step: Step): HTMLElement[] {
|
||||
if (step.options.extraHighlights) {
|
||||
return step.options.extraHighlights.flatMap((highlight) => {
|
||||
return Array.from(document.querySelectorAll(highlight)) as HTMLElement[];
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the step should be centered or not. Does not trigger attachTo.element evaluation, making it a pure
|
||||
* alternative for the deprecated step.isCentered() method.
|
||||
*/
|
||||
export function shouldCenterStep(resolvedAttachToOptions: StepOptionsAttachTo) {
|
||||
if (
|
||||
resolvedAttachToOptions === undefined ||
|
||||
resolvedAttachToOptions === null
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !resolvedAttachToOptions.element || !resolvedAttachToOptions.on;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a unique id for steps, tours, modals, etc
|
||||
*/
|
||||
export function uuid() {
|
||||
let d = Date.now();
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = (d + Math.random() * 16) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16);
|
||||
});
|
||||
}
|
59
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/overlay-path.ts
generated
vendored
Normal file
59
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/overlay-path.ts
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
interface OverlayPathParams {
|
||||
height: number;
|
||||
r?:
|
||||
| number
|
||||
| {
|
||||
bottomLeft: number;
|
||||
bottomRight: number;
|
||||
topLeft: number;
|
||||
topRight: number;
|
||||
};
|
||||
x?: number;
|
||||
y?: number;
|
||||
width: number;
|
||||
}
|
||||
/**
|
||||
* Generates the svg path data for a rounded rectangle overlay
|
||||
* @param dimension - Dimensions of rectangle.
|
||||
* @param dimension.width - Width.
|
||||
* @param dimension.height - Height.
|
||||
* @param dimension.x - Offset from top left corner in x axis. default 0.
|
||||
* @param dimension.y - Offset from top left corner in y axis. default 0.
|
||||
* @param dimension.r - Corner Radius. Keep this smaller than half of width or height.
|
||||
* @returns Rounded rectangle overlay path data.
|
||||
*/
|
||||
export function makeOverlayPath(overlayPaths: OverlayPathParams[]) {
|
||||
let openings = '';
|
||||
|
||||
const { innerWidth: w, innerHeight: h } = window;
|
||||
|
||||
overlayPaths.forEach((overlayPath) => {
|
||||
const { width, height, x = 0, y = 0, r = 0 } = overlayPath;
|
||||
const {
|
||||
topLeft = 0,
|
||||
topRight = 0,
|
||||
bottomRight = 0,
|
||||
bottomLeft = 0
|
||||
} = typeof r === 'number'
|
||||
? { topLeft: r, topRight: r, bottomRight: r, bottomLeft: r }
|
||||
: r;
|
||||
|
||||
openings += `M${x + topLeft},${y}\
|
||||
a${topLeft},${topLeft},0,0,0-${topLeft},${topLeft}\
|
||||
V${height + y - bottomLeft}\
|
||||
a${bottomLeft},${bottomLeft},0,0,0,${bottomLeft},${bottomLeft}\
|
||||
H${width + x - bottomRight}\
|
||||
a${bottomRight},${bottomRight},0,0,0,${bottomRight}-${bottomRight}\
|
||||
V${y + topRight}\
|
||||
a${topRight},${topRight},0,0,0-${topRight}-${topRight}\
|
||||
Z`;
|
||||
});
|
||||
|
||||
return `M${w},${h}\
|
||||
H0\
|
||||
V0\
|
||||
H${w}\
|
||||
V${h}\
|
||||
Z\
|
||||
${openings}`.replace(/\s/g, '');
|
||||
}
|
40
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/type-check.ts
generated
vendored
Normal file
40
my-workspace/projects/sae-lib/node_modules/shepherd.js/src/utils/type-check.ts
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Checks if `value` is classified as an `Element`.
|
||||
* @param value The param to check if it is an Element
|
||||
*/
|
||||
export function isElement<T>(value: T | Element): value is Element {
|
||||
return value instanceof Element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is classified as an `HTMLElement`.
|
||||
* @param value The param to check if it is an HTMLElement
|
||||
*/
|
||||
export function isHTMLElement<T>(value: T | HTMLElement): value is HTMLElement {
|
||||
return value instanceof HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is classified as a `Function` object.
|
||||
* @param value The param to check if it is a function
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export function isFunction<T>(value: T | Function): value is Function {
|
||||
return typeof value === 'function';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is classified as a `String` object.
|
||||
* @param value The param to check if it is a string
|
||||
*/
|
||||
export function isString<T>(value: T | string): value is string {
|
||||
return typeof value === 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is undefined.
|
||||
* @param value The param to check if it is undefined
|
||||
*/
|
||||
export function isUndefined<T>(value: T | undefined): value is undefined {
|
||||
return value === undefined;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue