typeit
Advanced tools
Comparing version 8.7.1 to 8.8.0
@@ -15,2 +15,2 @@ import { CursorOptions, Options } from "./types"; | ||
}; | ||
export declare const PLACEHOLDER_CSS: string; | ||
export declare const PLACEHOLDER_CSS = "[data-typeit-id]:before {content: '.'; display: inline-block; width: 0; visibility: hidden;}"; |
/** | ||
* Literally just wraps toArray() to save a few bytes | ||
* when it's repeatedly used. | ||
* | ||
* @param {any} | ||
* @return {array} | ||
*/ | ||
declare const _default: (val: any) => any[]; | ||
export default _default; |
1139
dist/index.es.js
// TypeIt by Alex MacArthur - https://typeitjs.com | ||
const isArray = (thing) => Array.isArray(thing); | ||
const asArray = (value) => { | ||
return isArray(value) ? value : [value]; | ||
}; | ||
const asArray = (value) => isArray(value) ? value : [value]; | ||
let Queue = function(initialItems) { | ||
@@ -42,5 +42,5 @@ let add = function(steps) { | ||
wipe, | ||
done, | ||
reset, | ||
destroy, | ||
done, | ||
getItems, | ||
@@ -51,22 +51,3 @@ getQueue, | ||
}; | ||
const toArray = (val) => Array.from(val); | ||
const createTextNode = (content) => document.createTextNode(content); | ||
let expandTextNodes = (element) => { | ||
[...element.childNodes].forEach((child) => { | ||
if (child.nodeValue) { | ||
[...child.nodeValue].forEach((c) => { | ||
child.parentNode.insertBefore(createTextNode(c), child); | ||
}); | ||
child.remove(); | ||
return; | ||
} | ||
expandTextNodes(child); | ||
}); | ||
return element; | ||
}; | ||
const getParsedBody = (content) => { | ||
let doc = document.implementation.createHTMLDocument(); | ||
doc.body.innerHTML = content; | ||
return expandTextNodes(doc.body); | ||
}; | ||
const DATA_ATTRIBUTE = "data-typeit-id"; | ||
@@ -122,2 +103,59 @@ const CURSOR_CLASS = "ti-cursor"; | ||
const PLACEHOLDER_CSS = `[${DATA_ATTRIBUTE}]:before {content: '.'; display: inline-block; width: 0; visibility: hidden;}`; | ||
const createElement = (el) => document.createElement(el); | ||
const createTextNode = (content) => document.createTextNode(content); | ||
const appendStyleBlock = (styles, id = "") => { | ||
let styleBlock = createElement("style"); | ||
styleBlock.id = id; | ||
styleBlock.appendChild(createTextNode(styles)); | ||
document.head.appendChild(styleBlock); | ||
}; | ||
const calculateDelay = (delayArg) => { | ||
if (!isArray(delayArg)) { | ||
delayArg = [delayArg / 2, delayArg / 2]; | ||
} | ||
return delayArg; | ||
}; | ||
const randomInRange = (value, range) => { | ||
return Math.abs( | ||
Math.random() * (value + range - (value - range)) + (value - range) | ||
); | ||
}; | ||
let range = (val) => val / 2; | ||
function calculatePace(options) { | ||
let { speed, deleteSpeed, lifeLike } = options; | ||
deleteSpeed = deleteSpeed !== null ? deleteSpeed : speed / 3; | ||
return lifeLike ? [ | ||
randomInRange(speed, range(speed)), | ||
randomInRange(deleteSpeed, range(deleteSpeed)) | ||
] : [speed, deleteSpeed]; | ||
} | ||
const toArray = (val) => Array.from(val); | ||
let expandTextNodes = (element) => { | ||
[...element.childNodes].forEach((child) => { | ||
if (child.nodeValue) { | ||
[...child.nodeValue].forEach((c) => { | ||
child.parentNode.insertBefore(createTextNode(c), child); | ||
}); | ||
child.remove(); | ||
return; | ||
} | ||
expandTextNodes(child); | ||
}); | ||
return element; | ||
}; | ||
const getParsedBody = (content) => { | ||
let doc = document.implementation.createHTMLDocument(); | ||
doc.body.innerHTML = content; | ||
return expandTextNodes(doc.body); | ||
}; | ||
function walkElementNodes(element, shouldReverse = false, shouldIncludeCursor = false) { | ||
@@ -154,135 +192,5 @@ let cursor = element.querySelector(`.${CURSOR_CLASS}`); | ||
} | ||
const createElement = (el) => document.createElement(el); | ||
const appendStyleBlock = (styles, id = "") => { | ||
let styleBlock = createElement("style"); | ||
styleBlock.id = id; | ||
styleBlock.appendChild(createTextNode(styles)); | ||
document.head.appendChild(styleBlock); | ||
}; | ||
const calculateDelay = (delayArg) => { | ||
if (!isArray(delayArg)) { | ||
delayArg = [delayArg / 2, delayArg / 2]; | ||
} | ||
return delayArg; | ||
}; | ||
const randomInRange = (value, range2) => { | ||
return Math.abs( | ||
Math.random() * (value + range2 - (value - range2)) + (value - range2) | ||
); | ||
}; | ||
let range = (val) => val / 2; | ||
function calculatePace(options) { | ||
let { speed, deleteSpeed, lifeLike } = options; | ||
deleteSpeed = deleteSpeed !== null ? deleteSpeed : speed / 3; | ||
return lifeLike ? [ | ||
randomInRange(speed, range(speed)), | ||
randomInRange(deleteSpeed, range(deleteSpeed)) | ||
] : [speed, deleteSpeed]; | ||
} | ||
const destroyTimeouts = (timeouts) => { | ||
timeouts.forEach(clearTimeout); | ||
return []; | ||
}; | ||
const generateHash = () => Math.random().toString().substring(2, 9); | ||
const isInput = (el) => "value" in el; | ||
let getAllChars = (element) => { | ||
if (isInput(element)) { | ||
return toArray(element.value); | ||
} | ||
return walkElementNodes(element, true).filter( | ||
(c) => !(c.childNodes.length > 0) | ||
); | ||
}; | ||
const fireWhenVisible = (element, func) => { | ||
let observer = new IntersectionObserver( | ||
(entries, observer2) => { | ||
entries.forEach((entry) => { | ||
if (entry.isIntersecting) { | ||
func(); | ||
observer2.unobserve(element); | ||
} | ||
}); | ||
}, | ||
{ threshold: 1 } | ||
); | ||
observer.observe(element); | ||
}; | ||
let handleFunctionalArg = (arg) => { | ||
return typeof arg === "function" ? arg() : arg; | ||
}; | ||
const isNumber = (value) => Number.isInteger(value); | ||
let select = (selector, element = document, all = false) => { | ||
return element[`querySelector${all ? "All" : ""}`](selector); | ||
}; | ||
let isBodyElement = (node) => /body/i.test(node?.tagName); | ||
let insertIntoElement = (originalTarget, character) => { | ||
if (isInput(originalTarget)) { | ||
originalTarget.value = `${originalTarget.value}${character.textContent}`; | ||
return; | ||
} | ||
character.innerHTML = ""; | ||
let target = isBodyElement(character.originalParent) ? originalTarget : character.originalParent || originalTarget; | ||
target.insertBefore( | ||
character, | ||
select("." + CURSOR_CLASS, target) || null | ||
); | ||
}; | ||
let updateCursorPosition = (steps, cursorPosition, printedCharacters) => { | ||
return Math.min( | ||
Math.max(cursorPosition + steps, 0), | ||
printedCharacters.length | ||
); | ||
}; | ||
const merge = (originalObj, newObj) => Object.assign({}, originalObj, newObj); | ||
const removeNode = (node, rootElement) => { | ||
if (!node) | ||
return; | ||
let nodeParent = node.parentNode; | ||
let nodeToRemove = nodeParent.childNodes.length > 1 || nodeParent.isSameNode(rootElement) ? node : nodeParent; | ||
nodeToRemove.remove(); | ||
}; | ||
const repositionCursor = (element, allChars, newCursorPosition) => { | ||
let nodeToInsertBefore = allChars[newCursorPosition - 1]; | ||
let cursor = select(`.${CURSOR_CLASS}`, element); | ||
element = nodeToInsertBefore?.parentNode || element; | ||
element.insertBefore(cursor, nodeToInsertBefore || null); | ||
}; | ||
function selectorToElement(thing) { | ||
return typeof thing === "string" ? select(thing) : thing; | ||
} | ||
const isNonVoidElement = (el) => /<(.+)>(.*?)<\/(.+)>/.test(el.outerHTML); | ||
let wait = (callback, delay, timeouts) => { | ||
return new Promise((resolve) => { | ||
let cb = async () => { | ||
await callback(); | ||
resolve(); | ||
}; | ||
timeouts.push(setTimeout(cb, delay || 0)); | ||
}); | ||
}; | ||
let cursorFontStyles = { | ||
"font-family": "", | ||
"font-weight": "", | ||
"font-size": "", | ||
"font-style": "", | ||
"line-height": "", | ||
color: "", | ||
transform: "translateX(-.125em)" | ||
}; | ||
let setCursorStyles = (id, element) => { | ||
let rootSelector = `[${DATA_ATTRIBUTE}='${id}']`; | ||
let cursorSelector = `${rootSelector} .${CURSOR_CLASS}`; | ||
let computedStyles = getComputedStyle(element); | ||
let customProperties = Object.entries(cursorFontStyles).reduce( | ||
(accumulator, [item, value]) => { | ||
return `${accumulator} ${item}: var(--ti-cursor-${item}, ${value || computedStyles[item]});`; | ||
}, | ||
"" | ||
); | ||
appendStyleBlock( | ||
`${cursorSelector} { display: inline-block; width: 0; ${customProperties} }`, | ||
id | ||
); | ||
}; | ||
const duplicate = (value, times) => new Array(times).fill(value); | ||
const countStepsToSelector = ({ | ||
@@ -312,2 +220,10 @@ queueItems, | ||
}; | ||
const destroyTimeouts = (timeouts) => { | ||
timeouts.forEach(clearTimeout); | ||
return []; | ||
}; | ||
const duplicate = (value, times) => new Array(times).fill(value); | ||
let beforePaint = (cb) => { | ||
@@ -320,2 +236,3 @@ return new Promise((resolve) => { | ||
}; | ||
let getAnimationFromElement = (element) => { | ||
@@ -326,2 +243,3 @@ return element?.getAnimations().find((animation) => { | ||
}; | ||
let setCursorAnimation = ({ | ||
@@ -342,2 +260,3 @@ cursor, | ||
}; | ||
let rebuildCursorAnimation = ({ | ||
@@ -367,2 +286,3 @@ cursor, | ||
}; | ||
let execute = (queueItem) => queueItem.func?.call(null); | ||
@@ -372,3 +292,3 @@ let fireItem = async ({ | ||
queueItems, | ||
wait: wait2, | ||
wait, | ||
cursor, | ||
@@ -404,3 +324,3 @@ cursorOptions | ||
} | ||
await wait2(async () => { | ||
await wait(async () => { | ||
if (animation && shouldPauseCursor) { | ||
@@ -420,2 +340,62 @@ animation.cancel(); | ||
}; | ||
const fireWhenVisible = (element, func) => { | ||
let observer = new IntersectionObserver( | ||
(entries, observer2) => { | ||
entries.forEach((entry) => { | ||
if (entry.isIntersecting) { | ||
func(); | ||
observer2.unobserve(element); | ||
} | ||
}); | ||
}, | ||
{ threshold: 1 } | ||
); | ||
observer.observe(element); | ||
}; | ||
const generateHash = () => Math.random().toString().substring(2, 9); | ||
const isInput = (el) => "value" in el; | ||
let getAllChars = (element) => { | ||
if (isInput(element)) { | ||
return toArray(element.value); | ||
} | ||
return walkElementNodes(element, true).filter( | ||
(c) => !(c.childNodes.length > 0) | ||
); | ||
}; | ||
let handleFunctionalArg = (arg) => { | ||
return typeof arg === "function" ? arg() : arg; | ||
}; | ||
let select = (selector, element = document, all = false) => { | ||
return element[`querySelector${all ? "All" : ""}`](selector); | ||
}; | ||
let isBodyElement = (node) => /body/i.test(node?.tagName); | ||
let insertIntoElement = (originalTarget, character) => { | ||
if (isInput(originalTarget)) { | ||
originalTarget.value = `${originalTarget.value}${character.textContent}`; | ||
return; | ||
} | ||
character.innerHTML = ""; | ||
let target = isBodyElement(character.originalParent) ? originalTarget : ( | ||
// If we add one-off fresh elements, there will be no | ||
// "originalParent", so always fall back to the default target. | ||
character.originalParent || originalTarget | ||
); | ||
target.insertBefore( | ||
character, | ||
select("." + CURSOR_CLASS, target) || null | ||
); | ||
}; | ||
const isNonVoidElement = (el) => /<(.+)>(.*?)<\/(.+)>/.test(el.outerHTML); | ||
const merge = (originalObj, newObj) => Object.assign({}, originalObj, newObj); | ||
let processCursorOptions = (cursorOptions) => { | ||
@@ -440,210 +420,175 @@ if (typeof cursorOptions === "object") { | ||
}; | ||
const TypeIt = function(element, options = {}) { | ||
let _wait = async (callback, delay, silent = false) => { | ||
if (_statuses.frozen) { | ||
await new Promise((resolve) => { | ||
this.unfreeze = () => { | ||
_statuses.frozen = false; | ||
resolve(); | ||
}; | ||
}); | ||
} | ||
silent || await _opts.beforeStep(this); | ||
await wait(callback, delay, _timeouts); | ||
silent || await _opts.afterStep(this); | ||
const removeNode = (node, rootElement) => { | ||
if (!node) | ||
return; | ||
let nodeParent = node.parentNode; | ||
let nodeToRemove = nodeParent.childNodes.length > 1 || nodeParent.isSameNode(rootElement) ? ( | ||
// This parent still needs to exist. | ||
node | ||
) : ( | ||
// There's nothing else in there, so just delete the entire thing. | ||
// By doing this, we clean up markup as we go along. | ||
nodeParent | ||
); | ||
nodeToRemove.remove(); | ||
}; | ||
const repositionCursor = (element, allChars, newCursorPosition) => { | ||
let nodeToInsertBefore = allChars[newCursorPosition - 1]; | ||
let cursor = select(`.${CURSOR_CLASS}`, element); | ||
element = nodeToInsertBefore?.parentNode || element; | ||
element.insertBefore(cursor, nodeToInsertBefore || null); | ||
}; | ||
function selectorToElement(thing) { | ||
return typeof thing === "string" ? select(thing) : thing; | ||
} | ||
let cursorFontStyles = { | ||
"font-family": "", | ||
"font-weight": "", | ||
"font-size": "", | ||
"font-style": "", | ||
"line-height": "", | ||
color: "", | ||
transform: "translateX(-.125em)" | ||
}; | ||
let setCursorStyles = (id, element) => { | ||
let rootSelector = `[${DATA_ATTRIBUTE}='${id}']`; | ||
let cursorSelector = `${rootSelector} .${CURSOR_CLASS}`; | ||
let computedStyles = getComputedStyle(element); | ||
let customProperties = Object.entries(cursorFontStyles).reduce( | ||
(accumulator, [item, value]) => { | ||
return `${accumulator} ${item}: var(--ti-cursor-${item}, ${value || computedStyles[item]});`; | ||
}, | ||
"" | ||
); | ||
appendStyleBlock( | ||
`${cursorSelector} { display: inline-block; width: 0; ${customProperties} }`, | ||
id | ||
); | ||
}; | ||
function splitOnBreak(str) { | ||
return str.replace(/<!--(.+?)-->/g, "").trim().split(/<br(?:\s*?)(?:\/)?>/); | ||
} | ||
let updateCursorPosition = (steps, cursorPosition, printedCharacters) => { | ||
return Math.min( | ||
Math.max(cursorPosition + steps, 0), | ||
printedCharacters.length | ||
); | ||
}; | ||
let wait = (callback, delay, timeouts) => { | ||
return new Promise((resolve) => { | ||
let cb = async () => { | ||
await callback(); | ||
resolve(); | ||
}; | ||
timeouts.push(setTimeout(cb, delay || 0)); | ||
}); | ||
}; | ||
class TypeIt { | ||
element; | ||
timeouts; | ||
cursorPosition; | ||
predictedCursorPosition; | ||
statuses = { | ||
started: false, | ||
completed: false, | ||
frozen: false, | ||
destroyed: false | ||
}; | ||
let _fireItemWithContext = (index, queueItems) => { | ||
return fireItem({ | ||
index, | ||
queueItems, | ||
wait: _wait, | ||
cursor: _cursor, | ||
cursorOptions: _opts.cursor | ||
}); | ||
opts; | ||
id; | ||
queue; | ||
cursor; | ||
unfreeze = () => { | ||
}; | ||
let _removeNode = (node) => removeNode(node, _element); | ||
let _elementIsInput = () => isInput(_element); | ||
let _getPace = (index = 0) => calculatePace(_opts)[index]; | ||
let _getAllChars = () => getAllChars(_element); | ||
let _maybeAppendPause = (opts = {}) => { | ||
let delay = opts.delay; | ||
delay && _queue.add({ delay }); | ||
}; | ||
let _queueAndReturn = (steps, opts) => { | ||
_queue.add(steps); | ||
_maybeAppendPause(opts); | ||
return this; | ||
}; | ||
let _getDerivedCursorPosition = () => _predictedCursorPosition ?? _cursorPosition; | ||
let _generateTemporaryOptionQueueItems = (newOptions = {}) => { | ||
return [ | ||
{ func: () => _options(newOptions) }, | ||
{ func: () => _options(_opts) } | ||
]; | ||
}; | ||
let _addSplitPause = (items) => { | ||
let delay = _opts.nextStringDelay; | ||
_queue.add([{ delay: delay[0] }, ...items, { delay: delay[1] }]); | ||
}; | ||
let _setUpCursor = () => { | ||
if (_elementIsInput()) { | ||
return; | ||
constructor(element, options = {}) { | ||
this.opts = merge(DEFAULT_OPTIONS, options); | ||
this.element = selectorToElement(element); | ||
this.timeouts = []; | ||
this.cursorPosition = 0; | ||
this.unfreeze = () => { | ||
}; | ||
this.predictedCursorPosition = null; | ||
this.statuses = merge({}, DEFAULT_STATUSES); | ||
this.id = generateHash(); | ||
this.queue = Queue([{ delay: this.opts.startDelay }]); | ||
this.#buildOptions(options); | ||
this.cursor = this.#setUpCursor(); | ||
this.element.dataset.typeitId = this.id; | ||
appendStyleBlock(PLACEHOLDER_CSS); | ||
if (this.opts.strings.length) { | ||
this.#generateQueue(); | ||
} | ||
let cursor = createElement("span"); | ||
cursor.className = CURSOR_CLASS; | ||
if (!_shouldRenderCursor) { | ||
cursor.style.visibility = "hidden"; | ||
return cursor; | ||
} | ||
/** | ||
* Can only be called once. | ||
*/ | ||
go() { | ||
if (this.statuses.started) { | ||
return this; | ||
} | ||
cursor.innerHTML = getParsedBody(_opts.cursorChar).innerHTML; | ||
return cursor; | ||
}; | ||
let _attachCursor = async () => { | ||
!_elementIsInput() && _cursor && _element.appendChild(_cursor); | ||
if (_shouldRenderCursor) { | ||
setCursorStyles(_id, _element); | ||
_cursor.dataset.tiAnimationId = _id; | ||
let { animation } = _opts.cursor; | ||
let { frames, options: options2 } = animation; | ||
setCursorAnimation({ | ||
frames, | ||
cursor: _cursor, | ||
options: { | ||
duration: _opts.cursorSpeed, | ||
...options2 | ||
} | ||
}); | ||
this.#attachCursor(); | ||
if (!this.opts.waitUntilVisible) { | ||
this.#fire(); | ||
return this; | ||
} | ||
}; | ||
let _generateQueue = () => { | ||
let strings = _opts.strings.filter((string) => !!string); | ||
strings.forEach((string, index) => { | ||
this.type(string); | ||
if (index + 1 === strings.length) { | ||
return; | ||
} | ||
let splitItems = _opts.breakLines ? [{ func: () => _type(createElement("BR")), typeable: true }] : duplicate( | ||
{ | ||
func: _delete, | ||
delay: _getPace(1) | ||
}, | ||
_queue.getTypeable().length | ||
); | ||
_addSplitPause(splitItems); | ||
}); | ||
}; | ||
let _prepLoop = async (delay) => { | ||
let derivedCursorPosition = _getDerivedCursorPosition(); | ||
derivedCursorPosition && await _move({ value: derivedCursorPosition }); | ||
let queueItems = _getAllChars().map((c) => { | ||
return [ | ||
Symbol(), | ||
{ | ||
func: _delete, | ||
delay: _getPace(1), | ||
deletable: true, | ||
shouldPauseCursor: () => true | ||
} | ||
]; | ||
}); | ||
for (let index = 0; index < queueItems.length; index++) { | ||
await _fireItemWithContext(index, queueItems); | ||
fireWhenVisible(this.element, this.#fire.bind(this)); | ||
return this; | ||
} | ||
destroy(shouldRemoveCursor = true) { | ||
this.timeouts = destroyTimeouts(this.timeouts); | ||
handleFunctionalArg(shouldRemoveCursor) && this.cursor && this.#removeNode(this.cursor); | ||
this.statuses.destroyed = true; | ||
} | ||
reset(rebuild) { | ||
!this.is("destroyed") && this.destroy(); | ||
if (rebuild) { | ||
this.queue.wipe(); | ||
rebuild(this); | ||
} else { | ||
this.queue.reset(); | ||
} | ||
_queue.reset(); | ||
_queue.set(0, { delay }); | ||
}; | ||
let _maybePrependHardcodedStrings = (strings) => { | ||
let existingMarkup = _element.innerHTML; | ||
if (!existingMarkup) { | ||
return strings; | ||
this.cursorPosition = 0; | ||
for (let property in this.statuses) { | ||
this.statuses[property] = false; | ||
} | ||
_element.innerHTML = ""; | ||
if (_opts.startDelete) { | ||
_element.innerHTML = existingMarkup; | ||
expandTextNodes(_element); | ||
_addSplitPause( | ||
duplicate( | ||
{ | ||
func: _delete, | ||
delay: _getPace(1), | ||
deletable: true | ||
}, | ||
_getAllChars().length | ||
) | ||
); | ||
return strings; | ||
} | ||
let hardCodedStrings = existingMarkup.replace(/<!--(.+?)-->/g, "").trim().split(/<br(?:\s*?)(?:\/)?>/); | ||
return hardCodedStrings.concat(strings); | ||
}; | ||
let _fire = async (remember = true) => { | ||
_statuses.started = true; | ||
let cleanUp = (qKey) => { | ||
_queue.done(qKey, !remember); | ||
}; | ||
try { | ||
let queueItems = [..._queue.getQueue()]; | ||
for (let index = 0; index < queueItems.length; index++) { | ||
let [queueKey, queueItem] = queueItems[index]; | ||
if (queueItem.done) | ||
continue; | ||
if (!queueItem.deletable || queueItem.deletable && _getAllChars().length) { | ||
let newIndex = await _fireItemWithContext(index, queueItems); | ||
Array(newIndex - index).fill(index + 1).map((x, y) => x + y).forEach((i) => { | ||
let [key] = queueItems[i]; | ||
cleanUp(key); | ||
}); | ||
index = newIndex; | ||
} | ||
cleanUp(queueKey); | ||
} | ||
if (!remember) { | ||
return this; | ||
} | ||
_statuses.completed = true; | ||
await _opts.afterComplete(this); | ||
if (!_opts.loop) { | ||
throw ""; | ||
} | ||
let delay = _opts.loopDelay; | ||
_wait(async () => { | ||
await _prepLoop(delay[0]); | ||
_fire(); | ||
}, delay[1]); | ||
} catch (e) { | ||
} | ||
this.element[this.#elementIsInput() ? "value" : "innerHTML"] = ""; | ||
return this; | ||
} | ||
is = function(key) { | ||
return this.statuses[key]; | ||
}; | ||
let _move = async (step) => { | ||
_cursorPosition = updateCursorPosition( | ||
step, | ||
_cursorPosition, | ||
_getAllChars() | ||
); | ||
repositionCursor(_element, _getAllChars(), _cursorPosition); | ||
}; | ||
let _type = (char) => insertIntoElement(_element, char); | ||
let _options = async (opts) => _opts = merge(_opts, opts); | ||
let _empty = async () => { | ||
if (_elementIsInput()) { | ||
_element.value = ""; | ||
return; | ||
} | ||
_getAllChars().forEach(_removeNode); | ||
return; | ||
}; | ||
let _delete = () => { | ||
let allChars = _getAllChars(); | ||
if (!allChars.length) | ||
return; | ||
if (_elementIsInput()) { | ||
_element.value = _element.value.slice(0, -1); | ||
} else { | ||
_removeNode(allChars[_cursorPosition]); | ||
} | ||
}; | ||
this.break = function(actionOpts) { | ||
return _queueAndReturn( | ||
type(string, actionOpts = {}) { | ||
string = handleFunctionalArg(string); | ||
let { instant } = actionOpts; | ||
let bookEndQueueItems = this.#generateTemporaryOptionQueueItems(actionOpts); | ||
let chars = maybeChunkStringAsHtml(string, this.opts.html); | ||
let charsAsQueueItems = chars.map((char) => { | ||
return { | ||
func: () => this.#type(char), | ||
char, | ||
delay: instant || isNonVoidElement(char) ? 0 : this.#getPace(), | ||
typeable: char.nodeType === Node.TEXT_NODE | ||
}; | ||
}); | ||
let itemsToQueue = [ | ||
bookEndQueueItems[0], | ||
{ func: async () => await this.opts.beforeString(string, this) }, | ||
...charsAsQueueItems, | ||
{ func: async () => await this.opts.afterString(string, this) }, | ||
bookEndQueueItems[1] | ||
]; | ||
return this.#queueAndReturn(itemsToQueue, actionOpts); | ||
} | ||
break(actionOpts = {}) { | ||
return this.#queueAndReturn( | ||
{ | ||
func: () => _type(createElement("BR")), | ||
func: () => this.#type(createElement("BR")), | ||
typeable: true | ||
@@ -653,9 +598,55 @@ }, | ||
); | ||
}; | ||
this.delete = function(numCharacters = null, actionOpts = {}) { | ||
} | ||
move(movementArg, actionOpts = {}) { | ||
movementArg = handleFunctionalArg(movementArg); | ||
let bookEndQueueItems = this.#generateTemporaryOptionQueueItems(actionOpts); | ||
let { instant, to } = actionOpts; | ||
let numberOfSteps = countStepsToSelector({ | ||
queueItems: this.queue.getTypeable(), | ||
selector: movementArg === null ? "" : movementArg, | ||
to, | ||
cursorPosition: this.#derivedCursorPosition | ||
}); | ||
let directionalStep = numberOfSteps < 0 ? -1 : 1; | ||
this.predictedCursorPosition = this.#derivedCursorPosition + numberOfSteps; | ||
return this.#queueAndReturn( | ||
[ | ||
bookEndQueueItems[0], | ||
...duplicate( | ||
{ | ||
func: () => this.#move(directionalStep), | ||
delay: instant ? 0 : this.#getPace(), | ||
cursorable: true | ||
}, | ||
Math.abs(numberOfSteps) | ||
), | ||
bookEndQueueItems[1] | ||
], | ||
actionOpts | ||
); | ||
} | ||
exec(func, actionOpts = {}) { | ||
let bookEndQueueItems = this.#generateTemporaryOptionQueueItems(actionOpts); | ||
return this.#queueAndReturn( | ||
[bookEndQueueItems[0], { func: () => func(this) }, bookEndQueueItems[1]], | ||
actionOpts | ||
); | ||
} | ||
options(opts, actionOpts = {}) { | ||
opts = handleFunctionalArg(opts); | ||
this.#updateOptions(opts); | ||
return this.#queueAndReturn({}, actionOpts); | ||
} | ||
pause(milliseconds, actionOpts = {}) { | ||
return this.#queueAndReturn( | ||
{ delay: handleFunctionalArg(milliseconds) }, | ||
actionOpts | ||
); | ||
} | ||
delete(numCharacters = null, actionOpts = {}) { | ||
numCharacters = handleFunctionalArg(numCharacters); | ||
let bookEndQueueItems = _generateTemporaryOptionQueueItems(actionOpts); | ||
let bookEndQueueItems = this.#generateTemporaryOptionQueueItems(actionOpts); | ||
let num = numCharacters; | ||
let { instant, to } = actionOpts; | ||
let typeableQueueItems = _queue.getTypeable(); | ||
let typeableQueueItems = this.queue.getTypeable(); | ||
let rounds = (() => { | ||
@@ -671,7 +662,7 @@ if (num === null) { | ||
selector: num, | ||
cursorPosition: _getDerivedCursorPosition(), | ||
cursorPosition: this.#derivedCursorPosition, | ||
to | ||
}); | ||
})(); | ||
return _queueAndReturn( | ||
return this.#queueAndReturn( | ||
[ | ||
@@ -681,4 +672,4 @@ bookEndQueueItems[0], | ||
{ | ||
func: _delete, | ||
delay: instant ? 0 : _getPace(1), | ||
func: this.#delete.bind(this), | ||
delay: instant ? 0 : this.#getPace(1), | ||
deletable: true | ||
@@ -692,151 +683,293 @@ }, | ||
); | ||
}; | ||
this.empty = function(actionOpts = {}) { | ||
return _queueAndReturn({ func: _empty }, actionOpts); | ||
}; | ||
this.exec = function(func, actionOpts = {}) { | ||
let bookEndQueueItems = _generateTemporaryOptionQueueItems(actionOpts); | ||
return _queueAndReturn( | ||
[bookEndQueueItems[0], { func: () => func(this) }, bookEndQueueItems[1]], | ||
actionOpts | ||
} | ||
freeze() { | ||
this.statuses.frozen = true; | ||
} | ||
/** | ||
* Like `.go()`, but more... "off the grid." | ||
* | ||
* - won't trigger `afterComplete` callback | ||
* - items won't be replayed after `.reset()` | ||
* | ||
* When called, all non-done items will be "flushed" -- | ||
* that is, executed, but not remembered. | ||
*/ | ||
flush(cb = () => { | ||
}) { | ||
this.#attachCursor(); | ||
this.#fire(false).then(cb); | ||
return this; | ||
} | ||
getQueue() { | ||
return this.queue; | ||
} | ||
getOptions() { | ||
return this.opts; | ||
} | ||
updateOptions(options) { | ||
return this.#updateOptions(options); | ||
} | ||
getElement() { | ||
return this.element; | ||
} | ||
empty(actionOpts = {}) { | ||
return this.#queueAndReturn({ func: this.#empty.bind(this) }, actionOpts); | ||
} | ||
async #empty() { | ||
if (this.#elementIsInput()) { | ||
this.element.value = ""; | ||
return; | ||
} | ||
this.#allChars.forEach(this.#removeNode.bind(this)); | ||
return; | ||
} | ||
/** | ||
* Execute items in the queue. | ||
* | ||
* @param remember If false, each queue item will be destroyed once executed. | ||
* @returns | ||
*/ | ||
async #fire(remember = true) { | ||
this.statuses.started = true; | ||
let cleanUp = (qKey) => { | ||
this.queue.done(qKey, !remember); | ||
}; | ||
try { | ||
let queueItems = [...this.queue.getQueue()]; | ||
for (let index = 0; index < queueItems.length; index++) { | ||
let [queueKey, queueItem] = queueItems[index]; | ||
if (queueItem.done) | ||
continue; | ||
if (!queueItem.deletable || queueItem.deletable && this.#allChars.length) { | ||
let newIndex = await this.#fireItemWithContext(index, queueItems); | ||
Array(newIndex - index).fill(index + 1).map((x, y) => x + y).forEach((i) => { | ||
let [key] = queueItems[i]; | ||
cleanUp(key); | ||
}); | ||
index = newIndex; | ||
} | ||
cleanUp(queueKey); | ||
} | ||
if (!remember) { | ||
return this; | ||
} | ||
this.statuses.completed = true; | ||
await this.opts.afterComplete(this); | ||
if (!this.opts.loop) { | ||
throw ""; | ||
} | ||
let delay = this.opts.loopDelay; | ||
this.#wait(async () => { | ||
await this.#prepLoop(delay[0]); | ||
this.#fire(); | ||
}, delay[1]); | ||
} catch (e) { | ||
} | ||
return this; | ||
} | ||
async #move(step) { | ||
this.cursorPosition = updateCursorPosition( | ||
step, | ||
this.cursorPosition, | ||
this.#allChars | ||
); | ||
}; | ||
this.move = function(movementArg, actionOpts = {}) { | ||
movementArg = handleFunctionalArg(movementArg); | ||
let bookEndQueueItems = _generateTemporaryOptionQueueItems(actionOpts); | ||
let { instant, to } = actionOpts; | ||
let numberOfSteps = countStepsToSelector({ | ||
queueItems: _queue.getTypeable(), | ||
selector: movementArg === null ? "" : movementArg, | ||
to, | ||
cursorPosition: _getDerivedCursorPosition() | ||
repositionCursor(this.element, this.#allChars, this.cursorPosition); | ||
} | ||
/** | ||
* 1. Reset queue. | ||
* 2. Reset initial pause. | ||
*/ | ||
async #prepLoop(delay) { | ||
let derivedCursorPosition = this.#derivedCursorPosition; | ||
derivedCursorPosition && await this.#move({ value: derivedCursorPosition }); | ||
let queueItems = this.#allChars.map((c) => { | ||
return [ | ||
Symbol(), | ||
{ | ||
func: this.#delete.bind(this), | ||
delay: this.#getPace(1), | ||
deletable: true, | ||
shouldPauseCursor: () => true | ||
} | ||
]; | ||
}); | ||
let directionalStep = numberOfSteps < 0 ? -1 : 1; | ||
_predictedCursorPosition = _getDerivedCursorPosition() + numberOfSteps; | ||
return _queueAndReturn( | ||
[ | ||
bookEndQueueItems[0], | ||
...duplicate( | ||
{ | ||
func: () => _move(directionalStep), | ||
delay: instant ? 0 : _getPace(), | ||
cursorable: true | ||
}, | ||
Math.abs(numberOfSteps) | ||
), | ||
bookEndQueueItems[1] | ||
], | ||
actionOpts | ||
for (let index = 0; index < queueItems.length; index++) { | ||
await this.#fireItemWithContext(index, queueItems); | ||
} | ||
this.queue.reset(); | ||
this.queue.set(0, { delay }); | ||
} | ||
#fireItemWithContext(index, queueItems) { | ||
return fireItem({ | ||
index, | ||
queueItems, | ||
wait: this.#wait.bind(this), | ||
cursor: this.cursor, | ||
cursorOptions: this.opts.cursor | ||
}); | ||
} | ||
async #wait(callback, delay, silent = false) { | ||
if (this.statuses.frozen) { | ||
await new Promise((resolve) => { | ||
this.unfreeze = () => { | ||
this.statuses.frozen = false; | ||
resolve(); | ||
}; | ||
}); | ||
} | ||
silent || await this.opts.beforeStep(this); | ||
await wait(callback, delay, this.timeouts); | ||
silent || await this.opts.afterStep(this); | ||
} | ||
/** | ||
* Attach it to the DOM so, along with the required CSS transition. | ||
*/ | ||
async #attachCursor() { | ||
!this.#elementIsInput() && this.cursor && this.element.appendChild(this.cursor); | ||
if (this.#shouldRenderCursor) { | ||
setCursorStyles(this.id, this.element); | ||
this.cursor.dataset.tiAnimationId = this.id; | ||
let { animation } = this.opts.cursor; | ||
let { frames, options } = animation; | ||
setCursorAnimation({ | ||
frames, | ||
cursor: this.cursor, | ||
options: { | ||
duration: this.opts.cursorSpeed, | ||
...options | ||
} | ||
}); | ||
} | ||
} | ||
#elementIsInput() { | ||
return isInput(this.element); | ||
} | ||
#queueAndReturn(steps, opts) { | ||
this.queue.add(steps); | ||
this.#maybeAppendPause(opts); | ||
return this; | ||
} | ||
#maybeAppendPause(opts = {}) { | ||
let delay = opts.delay; | ||
delay && this.queue.add({ delay }); | ||
} | ||
#generateTemporaryOptionQueueItems(newOptions = {}) { | ||
return [ | ||
{ func: () => this.#updateOptions(newOptions) }, | ||
{ func: () => this.#updateOptions(this.opts) } | ||
]; | ||
} | ||
async #updateOptions(opts) { | ||
this.opts = merge(this.opts, opts); | ||
} | ||
/** | ||
* Based on provided strings, generate a TypeIt queue | ||
* to be fired for each character in the string. | ||
*/ | ||
#generateQueue() { | ||
let strings = this.opts.strings.filter((string) => !!string); | ||
strings.forEach((string, index) => { | ||
this.type(string); | ||
if (index + 1 === strings.length) { | ||
return; | ||
} | ||
let splitItems = this.opts.breakLines ? [{ func: () => this.#type(createElement("BR")), typeable: true }] : duplicate( | ||
{ | ||
func: this.#delete.bind(this), | ||
delay: this.#getPace(1) | ||
}, | ||
this.queue.getTypeable().length | ||
); | ||
this.#addSplitPause(splitItems); | ||
}); | ||
} | ||
#buildOptions = (options) => { | ||
options.cursor = processCursorOptions( | ||
options.cursor ?? DEFAULT_OPTIONS.cursor | ||
); | ||
}; | ||
this.options = function(opts, actionOpts = {}) { | ||
opts = handleFunctionalArg(opts); | ||
_options(opts); | ||
return _queueAndReturn({}, actionOpts); | ||
}; | ||
this.pause = function(milliseconds, actionOpts = {}) { | ||
return _queueAndReturn( | ||
{ delay: handleFunctionalArg(milliseconds) }, | ||
actionOpts | ||
this.opts.strings = this.#prependHardcodedStrings( | ||
asArray(this.opts.strings) | ||
); | ||
}; | ||
this.type = function(string, actionOpts = {}) { | ||
string = handleFunctionalArg(string); | ||
let { instant } = actionOpts; | ||
let bookEndQueueItems = _generateTemporaryOptionQueueItems(actionOpts); | ||
let chars = maybeChunkStringAsHtml(string, _opts.html); | ||
let charsAsQueueItems = chars.map((char) => { | ||
return { | ||
func: () => _type(char), | ||
char, | ||
delay: instant || isNonVoidElement(char) ? 0 : _getPace(), | ||
typeable: char.nodeType === Node.TEXT_NODE | ||
}; | ||
this.opts = merge(this.opts, { | ||
html: !this.#isInput && this.opts.html, | ||
nextStringDelay: calculateDelay(this.opts.nextStringDelay), | ||
loopDelay: calculateDelay(this.opts.loopDelay) | ||
}); | ||
let itemsToQueue = [ | ||
bookEndQueueItems[0], | ||
{ func: async () => await _opts.beforeString(string, this) }, | ||
...charsAsQueueItems, | ||
{ func: async () => await _opts.afterString(string, this) }, | ||
bookEndQueueItems[1] | ||
]; | ||
return _queueAndReturn(itemsToQueue, actionOpts); | ||
}; | ||
this.is = function(key) { | ||
return _statuses[key]; | ||
}; | ||
this.destroy = function(shouldRemoveCursor = true) { | ||
_timeouts = destroyTimeouts(_timeouts); | ||
handleFunctionalArg(shouldRemoveCursor) && _cursor && _removeNode(_cursor); | ||
_statuses.destroyed = true; | ||
}; | ||
this.freeze = function() { | ||
_statuses.frozen = true; | ||
}; | ||
this.unfreeze = () => { | ||
}; | ||
this.reset = function(rebuild) { | ||
!this.is("destroyed") && this.destroy(); | ||
if (rebuild) { | ||
_queue.wipe(); | ||
rebuild(this); | ||
} else { | ||
_queue.reset(); | ||
#prependHardcodedStrings(strings) { | ||
let existingMarkup = this.element.innerHTML; | ||
if (!existingMarkup) { | ||
return strings; | ||
} | ||
_cursorPosition = 0; | ||
for (let property in _statuses) { | ||
_statuses[property] = false; | ||
this.element.innerHTML = ""; | ||
if (this.opts.startDelete) { | ||
this.element.innerHTML = existingMarkup; | ||
expandTextNodes(this.element); | ||
this.#addSplitPause( | ||
duplicate( | ||
{ | ||
func: this.#delete.bind(this), | ||
delay: this.#getPace(1), | ||
deletable: true | ||
}, | ||
this.#allChars.length | ||
) | ||
); | ||
return strings; | ||
} | ||
_element[_elementIsInput() ? "value" : "innerHTML"] = ""; | ||
return this; | ||
}; | ||
this.go = function() { | ||
if (_statuses.started) { | ||
return this; | ||
return splitOnBreak(existingMarkup).concat(strings); | ||
} | ||
/** | ||
* Provided it's a non-form element and the options is provided, | ||
* set up the cursor element for the animation. | ||
*/ | ||
#setUpCursor() { | ||
if (this.#isInput) { | ||
return null; | ||
} | ||
_attachCursor(); | ||
if (!_opts.waitUntilVisible) { | ||
_fire(); | ||
return this; | ||
let cursor = createElement("span"); | ||
cursor.className = CURSOR_CLASS; | ||
if (!this.#shouldRenderCursor) { | ||
cursor.style.visibility = "hidden"; | ||
return cursor; | ||
} | ||
fireWhenVisible(_element, _fire.bind(this)); | ||
return this; | ||
}; | ||
this.flush = function(cb = () => { | ||
}) { | ||
_attachCursor(); | ||
_fire(false).then(cb); | ||
return this; | ||
}; | ||
this.getQueue = () => _queue; | ||
this.getOptions = () => _opts; | ||
this.updateOptions = (options2) => _options(options2); | ||
this.getElement = () => _element; | ||
let _element = selectorToElement(element); | ||
let _timeouts = []; | ||
let _cursorPosition = 0; | ||
let _predictedCursorPosition = null; | ||
let _statuses = merge({}, DEFAULT_STATUSES); | ||
options.cursor = processCursorOptions( | ||
options.cursor ?? DEFAULT_OPTIONS.cursor | ||
); | ||
let _opts = merge(DEFAULT_OPTIONS, options); | ||
_opts = merge(_opts, { | ||
html: !_elementIsInput() && _opts.html, | ||
nextStringDelay: calculateDelay(_opts.nextStringDelay), | ||
loopDelay: calculateDelay(_opts.loopDelay) | ||
}); | ||
let _id = generateHash(); | ||
let _queue = Queue([{ delay: _opts.startDelay }]); | ||
_element.dataset.typeitId = _id; | ||
appendStyleBlock(PLACEHOLDER_CSS); | ||
let _shouldRenderCursor = !!_opts.cursor && !_elementIsInput(); | ||
let _cursor = _setUpCursor(); | ||
_opts.strings = _maybePrependHardcodedStrings(asArray(_opts.strings)); | ||
if (_opts.strings.length) { | ||
_generateQueue(); | ||
cursor.innerHTML = getParsedBody(this.opts.cursorChar).innerHTML; | ||
return cursor; | ||
} | ||
}; | ||
export { | ||
TypeIt as default | ||
}; | ||
#addSplitPause(items) { | ||
let delay = this.opts.nextStringDelay; | ||
this.queue.add([{ delay: delay[0] }, ...items, { delay: delay[1] }]); | ||
} | ||
#type(char) { | ||
insertIntoElement(this.element, char); | ||
} | ||
#delete() { | ||
if (!this.#allChars.length) | ||
return; | ||
if (this.#isInput) { | ||
this.element.value = this.element.value.slice(0, -1); | ||
} else { | ||
this.#removeNode(this.#allChars[this.cursorPosition]); | ||
} | ||
} | ||
#removeNode(node) { | ||
removeNode(node, this.element); | ||
} | ||
#getPace(index = 0) { | ||
return calculatePace(this.opts)[index]; | ||
} | ||
get #derivedCursorPosition() { | ||
return this.predictedCursorPosition ?? this.cursorPosition; | ||
} | ||
get #isInput() { | ||
return isInput(this.element); | ||
} | ||
get #shouldRenderCursor() { | ||
return !!this.opts.cursor && !this.#isInput; | ||
} | ||
get #allChars() { | ||
return getAllChars(this.element); | ||
} | ||
} | ||
export { TypeIt as default }; |
// TypeIt by Alex MacArthur - https://typeitjs.com | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).TypeIt=t()}(this,(function(){"use strict";const e=e=>Array.isArray(e),t=t=>e(t)?t:[t];const n=e=>Array.from(e),r=e=>document.createTextNode(e);let i=e=>([...e.childNodes].forEach((e=>{if(e.nodeValue)return[...e.nodeValue].forEach((t=>{e.parentNode.insertBefore(r(t),e)})),void e.remove();i(e)})),e);const a=e=>{let t=document.implementation.createHTMLDocument();return t.body.innerHTML=e,i(t.body)},o="data-typeit-id",s="ti-cursor",l={started:!1,completed:!1,frozen:!1,destroyed:!1},u={breakLines:!0,cursor:{autoPause:!0,autoPauseDelay:500,animation:{frames:[0,0,1].map((e=>({opacity:e}))),options:{iterations:1/0,easing:"steps(2, start)",fill:"forwards"}}},cursorChar:"|",cursorSpeed:1e3,deleteSpeed:null,html:!0,lifeLike:!0,loop:!1,loopDelay:750,nextStringDelay:750,speed:100,startDelay:250,startDelete:!1,strings:[],waitUntilVisible:!1,beforeString:()=>{},afterString:()=>{},beforeStep:()=>{},afterStep:()=>{},afterComplete:()=>{}},c=`[${o}]:before {content: '.'; display: inline-block; width: 0; visibility: hidden;}`;function d(e,t=!1,n=!1){let r,i=e.querySelector(`.${s}`),a=document.createTreeWalker(e,NodeFilter.SHOW_ALL,{acceptNode:e=>{if(i&&n){if(e.classList?.contains(s))return NodeFilter.FILTER_ACCEPT;if(i.contains(e))return NodeFilter.FILTER_REJECT}return e.classList?.contains(s)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}}),o=[];for(;r=a.nextNode();)r.originalParent||(r.originalParent=r.parentNode),o.push(r);return t?o.reverse():o}function f(e,t=!0){return t?d(a(e)):n(e).map(r)}const h=e=>document.createElement(e),y=(e,t="")=>{let n=h("style");n.id=t,n.appendChild(r(e)),document.head.appendChild(n)},p=t=>(e(t)||(t=[t/2,t/2]),t),m=(e,t)=>Math.abs(Math.random()*(e+t-(e-t))+(e-t));let g=e=>e/2;const b=e=>"value"in e;let w=e=>"function"==typeof e?e():e;const T=e=>Number.isInteger(e);let v=(e,t=document,n=!1)=>t["querySelector"+(n?"All":"")](e);const E=(e,t)=>Object.assign({},e,t);let P={"font-family":"","font-weight":"","font-size":"","font-style":"","line-height":"",color:"",transform:"translateX(-.125em)"};const S=(e,t)=>new Array(t).fill(e),N=({queueItems:e,selector:t,cursorPosition:n,to:r})=>{if(T(t))return-1*t;let i=new RegExp("END","i").test(r),a=t?[...e].reverse().findIndex((({char:e})=>{let n=e.parentElement,r=n.matches(t);return!(!i||!r)||r&&n.firstChild.isSameNode(e)})):-1;return a<0&&(a=i?0:e.length-1),a-n+(i?0:1)};let L=e=>new Promise((t=>{requestAnimationFrame((async()=>{t(await e())}))})),C=e=>e?.getAnimations().find((t=>t.id===e.dataset.tiAnimationId)),D=({cursor:e,frames:t,options:n})=>{let r=e.animate(t,n);return r.pause(),r.id=e.dataset.tiAnimationId,L((()=>{L((()=>{r.play()}))})),r},I=e=>e.func?.call(null),M=async({index:e,queueItems:t,wait:n,cursor:r,cursorOptions:i})=>{let a=t[e][1],o=[],s=e,l=a,u=()=>l&&!l.delay,c=a.shouldPauseCursor()&&i.autoPause;for(;u();)o.push(l),u()&&s++,l=t[s]?t[s][1]:null;if(o.length)return await L((async()=>{for(let e of o)await I(e)})),s-1;let d,f=C(r);return f&&(d={...f.effect.getComputedTiming(),delay:c?i.autoPauseDelay:0}),await n((async()=>{f&&c&&f.cancel(),await L((()=>{I(a)}))}),a.delay),await(({cursor:e,options:t,cursorOptions:n})=>{if(!e||!n)return;let r,i=C(e);i&&(t.delay=i.effect.getComputedTiming().delay,r=i.currentTime,i.cancel());let a=D({cursor:e,frames:n.animation.frames,options:t});return r&&(a.currentTime=r),a})({cursor:r,options:d,cursorOptions:i}),e};return function(e,r={}){let L=async(e,t,n=!1)=>{K.frozen&&await new Promise((e=>{this.unfreeze=()=>{K.frozen=!1,e()}})),n||await Y.beforeStep(this),await((e,t,n)=>new Promise((r=>{n.push(setTimeout((async()=>{await e(),r()}),t||0))})))(e,t,W),n||await Y.afterStep(this)},C=(e,t)=>M({index:e,queueItems:t,wait:L,cursor:ne,cursorOptions:Y.cursor}),I=e=>((e,t)=>{if(!e)return;let n=e.parentNode;(n.childNodes.length>1||n.isSameNode(t)?e:n).remove()})(e,J),x=()=>b(J),A=(e=0)=>function(e){let{speed:t,deleteSpeed:n,lifeLike:r}=e;return n=null!==n?n:t/3,r?[m(t,g(t)),m(n,g(n))]:[t,n]}(Y)[e],$=()=>(e=>b(e)?n(e.value):d(e,!0).filter((e=>!(e.childNodes.length>0))))(J),H=(e,t)=>(ee.add(e),((e={})=>{let t=e.delay;t&&ee.add({delay:t})})(t),this),O=()=>G??X,F=(e={})=>[{func:()=>j(e)},{func:()=>j(Y)}],k=e=>{let t=Y.nextStringDelay;ee.add([{delay:t[0]},...e,{delay:t[1]}])},R=async()=>{if(!x()&&ne&&J.appendChild(ne),te){((e,t)=>{let n=`[${o}='${e}'] .${s}`,r=getComputedStyle(t),i=Object.entries(P).reduce(((e,[t,n])=>`${e} ${t}: var(--ti-cursor-${t}, ${n||r[t]});`),"");y(`${n} { display: inline-block; width: 0; ${i} }`,e)})(Z,J),ne.dataset.tiAnimationId=Z;let{animation:e}=Y.cursor,{frames:t,options:n}=e;D({frames:t,cursor:ne,options:{duration:Y.cursorSpeed,...n}})}},q=()=>{let e=Y.strings.filter((e=>!!e));e.forEach(((t,n)=>{if(this.type(t),n+1===e.length)return;let r=Y.breakLines?[{func:()=>_(h("BR")),typeable:!0}]:S({func:Q,delay:A(1)},ee.getTypeable().length);k(r)}))},z=async(e=!0)=>{K.started=!0;let t=t=>{ee.done(t,!e)};try{let n=[...ee.getQueue()];for(let e=0;e<n.length;e++){let[r,i]=n[e];if(!i.done){if(!i.deletable||i.deletable&&$().length){let r=await C(e,n);Array(r-e).fill(e+1).map(((e,t)=>e+t)).forEach((e=>{let[r]=n[e];t(r)})),e=r}t(r)}}if(!e)return this;if(K.completed=!0,await Y.afterComplete(this),!Y.loop)throw"";let r=Y.loopDelay;L((async()=>{await(async e=>{let t=O();t&&await B({value:t});let n=$().map((e=>[Symbol(),{func:Q,delay:A(1),deletable:!0,shouldPauseCursor:()=>!0}]));for(let r=0;r<n.length;r++)await C(r,n);ee.reset(),ee.set(0,{delay:e})})(r[0]),z()}),r[1])}catch(n){}return this},B=async e=>{var t,n,r;t=e,n=X,r=$(),X=Math.min(Math.max(n+t,0),r.length),((e,t,n)=>{let r=t[n-1],i=v(`.${s}`,e);(e=r?.parentNode||e).insertBefore(i,r||null)})(J,$(),X)},_=e=>((e,t)=>{if(b(e))return void(e.value=`${e.value}${t.textContent}`);t.innerHTML="";let n=(r=t.originalParent,/body/i.test(r?.tagName)?e:t.originalParent||e);var r;n.insertBefore(t,v("."+s,n)||null)})(J,e),j=async e=>Y=E(Y,e),V=async()=>{x()?J.value="":$().forEach(I)},Q=()=>{let e=$();e.length&&(x()?J.value=J.value.slice(0,-1):I(e[X]))};this.break=function(e){return H({func:()=>_(h("BR")),typeable:!0},e)},this.delete=function(e=null,t={}){e=w(e);let n=F(t),r=e,{instant:i,to:a}=t,o=ee.getTypeable(),s=null===r?o.length:T(r)?r:N({queueItems:o,selector:r,cursorPosition:O(),to:a});return H([n[0],...S({func:Q,delay:i?0:A(1),deletable:!0},s),n[1]],t)},this.empty=function(e={}){return H({func:V},e)},this.exec=function(e,t={}){let n=F(t);return H([n[0],{func:()=>e(this)},n[1]],t)},this.move=function(e,t={}){e=w(e);let n=F(t),{instant:r,to:i}=t,a=N({queueItems:ee.getTypeable(),selector:null===e?"":e,to:i,cursorPosition:O()}),o=a<0?-1:1;return G=O()+a,H([n[0],...S({func:()=>B(o),delay:r?0:A(),cursorable:!0},Math.abs(a)),n[1]],t)},this.options=function(e,t={}){return e=w(e),j(e),H({},t)},this.pause=function(e,t={}){return H({delay:w(e)},t)},this.type=function(e,t={}){e=w(e);let{instant:n}=t,r=F(t),i=f(e,Y.html).map((e=>{return{func:()=>_(e),char:e,delay:n||(t=e,/<(.+)>(.*?)<\/(.+)>/.test(t.outerHTML))?0:A(),typeable:e.nodeType===Node.TEXT_NODE};var t})),a=[r[0],{func:async()=>await Y.beforeString(e,this)},...i,{func:async()=>await Y.afterString(e,this)},r[1]];return H(a,t)},this.is=function(e){return K[e]},this.destroy=function(e=!0){W.forEach(clearTimeout),W=[],w(e)&&ne&&I(ne),K.destroyed=!0},this.freeze=function(){K.frozen=!0},this.unfreeze=()=>{},this.reset=function(e){!this.is("destroyed")&&this.destroy(),e?(ee.wipe(),e(this)):ee.reset(),X=0;for(let t in K)K[t]=!1;return J[x()?"value":"innerHTML"]="",this},this.go=function(){return K.started?this:(R(),Y.waitUntilVisible?(((e,t)=>{new IntersectionObserver(((n,r)=>{n.forEach((n=>{n.isIntersecting&&(t(),r.unobserve(e))}))}),{threshold:1}).observe(e)})(J,z.bind(this)),this):(z(),this))},this.flush=function(e=(()=>{})){return R(),z(!1).then(e),this},this.getQueue=()=>ee,this.getOptions=()=>Y,this.updateOptions=e=>j(e),this.getElement=()=>J;let J="string"==typeof(U=e)?v(U):U;var U;let W=[],X=0,G=null,K=E({},l);r.cursor=(e=>{if("object"==typeof e){let t={},{frames:n,options:r}=u.cursor.animation;return t.animation=e.animation||{},t.animation.frames=e.animation?.frames||n,t.animation.options=E(r,e.animation?.options||{}),t.autoPause=e.autoPause??u.cursor.autoPause,t.autoPauseDelay=e.autoPauseDelay||u.cursor.autoPauseDelay,t}return!0===e?u.cursor:e})(r.cursor??u.cursor);let Y=E(u,r);Y=E(Y,{html:!x()&&Y.html,nextStringDelay:p(Y.nextStringDelay),loopDelay:p(Y.loopDelay)});let Z=Math.random().toString().substring(2,9),ee=function(e){let n=function(e){return t(e).forEach((e=>a.set(Symbol(e.char?.innerText),r({...e})))),this},r=e=>(e.shouldPauseCursor=function(){return Boolean(this.typeable||this.cursorable||this.deletable)},e),i=()=>Array.from(a.values()),a=new Map;return n(e),{add:n,set:function(e,t){let n=[...a.keys()];a.set(n[e],r(t))},wipe:function(){a=new Map,n(e)},reset:function(){a.forEach((e=>delete e.done))},destroy:e=>a.delete(e),done:(e,t=!1)=>t?a.delete(e):a.get(e).done=!0,getItems:(e=!1)=>e?i():i().filter((e=>!e.done)),getQueue:()=>a,getTypeable:()=>i().filter((e=>e.typeable))}}([{delay:Y.startDelay}]);J.dataset.typeitId=Z,y(c);let te=!!Y.cursor&&!x(),ne=(()=>{if(x())return;let e=h("span");return e.className=s,te?(e.innerHTML=a(Y.cursorChar).innerHTML,e):(e.style.visibility="hidden",e)})();Y.strings=(e=>{let t=J.innerHTML;return t?(J.innerHTML="",Y.startDelete?(J.innerHTML=t,i(J),k(S({func:Q,delay:A(1),deletable:!0},$().length)),e):t.replace(/<!--(.+?)-->/g,"").trim().split(/<br(?:\s*?)(?:\/)?>/).concat(e)):e})(t(Y.strings)),Y.strings.length&&q()}})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).TypeIt=t()}(this,(function(){"use strict";const e=e=>Array.isArray(e),t=t=>e(t)?t:[t];const s="data-typeit-id",i="ti-cursor",r={started:!1,completed:!1,frozen:!1,destroyed:!1},n={breakLines:!0,cursor:{autoPause:!0,autoPauseDelay:500,animation:{frames:[0,0,1].map((e=>({opacity:e}))),options:{iterations:1/0,easing:"steps(2, start)",fill:"forwards"}}},cursorChar:"|",cursorSpeed:1e3,deleteSpeed:null,html:!0,lifeLike:!0,loop:!1,loopDelay:750,nextStringDelay:750,speed:100,startDelay:250,startDelete:!1,strings:[],waitUntilVisible:!1,beforeString:()=>{},afterString:()=>{},beforeStep:()=>{},afterStep:()=>{},afterComplete:()=>{}},o=`[${s}]:before {content: '.'; display: inline-block; width: 0; visibility: hidden;}`,a=e=>document.createElement(e),u=e=>document.createTextNode(e),l=(e,t="")=>{let s=a("style");s.id=t,s.appendChild(u(e)),document.head.appendChild(s)},h=t=>(e(t)||(t=[t/2,t/2]),t),d=(e,t)=>Math.abs(Math.random()*(e+t-(e-t))+(e-t));let p=e=>e/2;const c=e=>Array.from(e);let m=e=>([...e.childNodes].forEach((e=>{if(e.nodeValue)return[...e.nodeValue].forEach((t=>{e.parentNode.insertBefore(u(t),e)})),void e.remove();m(e)})),e);const f=e=>{let t=document.implementation.createHTMLDocument();return t.body.innerHTML=e,m(t.body)};function y(e,t=!1,s=!1){let r,n=e.querySelector(`.${i}`),o=document.createTreeWalker(e,NodeFilter.SHOW_ALL,{acceptNode:e=>{if(n&&s){if(e.classList?.contains(i))return NodeFilter.FILTER_ACCEPT;if(n.contains(e))return NodeFilter.FILTER_REJECT}return e.classList?.contains(i)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}}),a=[];for(;r=o.nextNode();)r.originalParent||(r.originalParent=r.parentNode),a.push(r);return t?a.reverse():a}function g(e,t=!0){return t?y(f(e)):c(e).map(u)}const b=e=>Number.isInteger(e),P=({queueItems:e,selector:t,cursorPosition:s,to:i})=>{if(b(t))return-1*t;let r=new RegExp("END","i").test(i),n=t?[...e].reverse().findIndex((({char:e})=>{let s=e.parentElement,i=s.matches(t);return!(!r||!i)||i&&s.firstChild.isSameNode(e)})):-1;return n<0&&(n=r?0:e.length-1),n-s+(r?0:1)},C=(e,t)=>new Array(t).fill(e);let v=e=>new Promise((t=>{requestAnimationFrame((async()=>{t(await e())}))})),T=e=>e?.getAnimations().find((t=>t.id===e.dataset.tiAnimationId)),w=({cursor:e,frames:t,options:s})=>{let i=e.animate(t,s);return i.pause(),i.id=e.dataset.tiAnimationId,v((()=>{v((()=>{i.play()}))})),i},I=e=>e.func?.call(null),q=async({index:e,queueItems:t,wait:s,cursor:i,cursorOptions:r})=>{let n=t[e][1],o=[],a=e,u=n,l=()=>u&&!u.delay,h=n.shouldPauseCursor()&&r.autoPause;for(;l();)o.push(u),l()&&a++,u=t[a]?t[a][1]:null;if(o.length)return await v((async()=>{for(let e of o)await I(e)})),a-1;let d,p=T(i);return p&&(d={...p.effect.getComputedTiming(),delay:h?r.autoPauseDelay:0}),await s((async()=>{p&&h&&p.cancel(),await v((()=>{I(n)}))}),n.delay),await(({cursor:e,options:t,cursorOptions:s})=>{if(!e||!s)return;let i,r=T(e);r&&(t.delay=r.effect.getComputedTiming().delay,i=r.currentTime,r.cancel());let n=w({cursor:e,frames:s.animation.frames,options:t});return i&&(n.currentTime=i),n})({cursor:i,options:d,cursorOptions:r}),e};const S=e=>"value"in e;let N=e=>"function"==typeof e?e():e,A=(e,t=document,s=!1)=>t["querySelector"+(s?"All":"")](e);const E=(e,t)=>Object.assign({},e,t);let L={"font-family":"","font-weight":"","font-size":"","font-style":"","line-height":"",color:"",transform:"translateX(-.125em)"};return class{element;timeouts;cursorPosition;predictedCursorPosition;statuses={started:!1,completed:!1,frozen:!1,destroyed:!1};opts;id;queue;cursor;unfreeze=()=>{};constructor(e,s={}){var i;this.opts=E(n,s),this.element="string"==typeof(i=e)?A(i):i,this.timeouts=[],this.cursorPosition=0,this.unfreeze=()=>{},this.predictedCursorPosition=null,this.statuses=E({},r),this.id=Math.random().toString().substring(2,9),this.queue=function(e){let s=function(e){return t(e).forEach((e=>n.set(Symbol(e.char?.innerText),i({...e})))),this},i=e=>(e.shouldPauseCursor=function(){return Boolean(this.typeable||this.cursorable||this.deletable)},e),r=()=>Array.from(n.values()),n=new Map;return s(e),{add:s,set:function(e,t){let s=[...n.keys()];n.set(s[e],i(t))},wipe:function(){n=new Map,s(e)},done:(e,t=!1)=>t?n.delete(e):n.get(e).done=!0,reset:function(){n.forEach((e=>delete e.done))},destroy:e=>n.delete(e),getItems:(e=!1)=>e?r():r().filter((e=>!e.done)),getQueue:()=>n,getTypeable:()=>r().filter((e=>e.typeable))}}([{delay:this.opts.startDelay}]),this.#e(s),this.cursor=this.#t(),this.element.dataset.typeitId=this.id,l(o),this.opts.strings.length&&this.#s()}go(){return this.statuses.started?this:(this.#i(),this.opts.waitUntilVisible?(e=this.element,t=this.#r.bind(this),new IntersectionObserver(((s,i)=>{s.forEach((s=>{s.isIntersecting&&(t(),i.unobserve(e))}))}),{threshold:1}).observe(e),this):(this.#r(),this));var e,t}destroy(e=!0){this.timeouts=(this.timeouts.forEach(clearTimeout),[]),N(e)&&this.cursor&&this.#n(this.cursor),this.statuses.destroyed=!0}reset(e){!this.is("destroyed")&&this.destroy(),e?(this.queue.wipe(),e(this)):this.queue.reset(),this.cursorPosition=0;for(let t in this.statuses)this.statuses[t]=!1;return this.element[this.#o()?"value":"innerHTML"]="",this}is=function(e){return this.statuses[e]};type(e,t={}){e=N(e);let{instant:s}=t,i=this.#a(t),r=g(e,this.opts.html).map((e=>{return{func:()=>this.#u(e),char:e,delay:s||(t=e,/<(.+)>(.*?)<\/(.+)>/.test(t.outerHTML))?0:this.#l(),typeable:e.nodeType===Node.TEXT_NODE};var t})),n=[i[0],{func:async()=>await this.opts.beforeString(e,this)},...r,{func:async()=>await this.opts.afterString(e,this)},i[1]];return this.#h(n,t)}break(e={}){return this.#h({func:()=>this.#u(a("BR")),typeable:!0},e)}move(e,t={}){e=N(e);let s=this.#a(t),{instant:i,to:r}=t,n=P({queueItems:this.queue.getTypeable(),selector:null===e?"":e,to:r,cursorPosition:this.#d}),o=n<0?-1:1;return this.predictedCursorPosition=this.#d+n,this.#h([s[0],...C({func:()=>this.#p(o),delay:i?0:this.#l(),cursorable:!0},Math.abs(n)),s[1]],t)}exec(e,t={}){let s=this.#a(t);return this.#h([s[0],{func:()=>e(this)},s[1]],t)}options(e,t={}){return e=N(e),this.#c(e),this.#h({},t)}pause(e,t={}){return this.#h({delay:N(e)},t)}delete(e=null,t={}){e=N(e);let s=this.#a(t),i=e,{instant:r,to:n}=t,o=this.queue.getTypeable(),a=(()=>null===i?o.length:b(i)?i:P({queueItems:o,selector:i,cursorPosition:this.#d,to:n}))();return this.#h([s[0],...C({func:this.#m.bind(this),delay:r?0:this.#l(1),deletable:!0},a),s[1]],t)}freeze(){this.statuses.frozen=!0}flush(e=(()=>{})){return this.#i(),this.#r(!1).then(e),this}getQueue(){return this.queue}getOptions(){return this.opts}updateOptions(e){return this.#c(e)}getElement(){return this.element}empty(e={}){return this.#h({func:this.#f.bind(this)},e)}async#f(){this.#o()?this.element.value="":this.#y.forEach(this.#n.bind(this))}async#r(e=!0){this.statuses.started=!0;let t=t=>{this.queue.done(t,!e)};try{let s=[...this.queue.getQueue()];for(let e=0;e<s.length;e++){let[i,r]=s[e];if(!r.done){if(!r.deletable||r.deletable&&this.#y.length){let i=await this.#g(e,s);Array(i-e).fill(e+1).map(((e,t)=>e+t)).forEach((e=>{let[i]=s[e];t(i)})),e=i}t(i)}}if(!e)return this;if(this.statuses.completed=!0,await this.opts.afterComplete(this),!this.opts.loop)throw"";let i=this.opts.loopDelay;this.#b((async()=>{await this.#P(i[0]),this.#r()}),i[1])}catch(s){}return this}async#p(e){var t,s,r;this.cursorPosition=(t=e,s=this.cursorPosition,r=this.#y,Math.min(Math.max(s+t,0),r.length)),((e,t,s)=>{let r=t[s-1],n=A(`.${i}`,e);(e=r?.parentNode||e).insertBefore(n,r||null)})(this.element,this.#y,this.cursorPosition)}async#P(e){let t=this.#d;t&&await this.#p({value:t});let s=this.#y.map((e=>[Symbol(),{func:this.#m.bind(this),delay:this.#l(1),deletable:!0,shouldPauseCursor:()=>!0}]));for(let i=0;i<s.length;i++)await this.#g(i,s);this.queue.reset(),this.queue.set(0,{delay:e})}#g(e,t){return q({index:e,queueItems:t,wait:this.#b.bind(this),cursor:this.cursor,cursorOptions:this.opts.cursor})}async#b(e,t,s=!1){this.statuses.frozen&&await new Promise((e=>{this.unfreeze=()=>{this.statuses.frozen=!1,e()}})),s||await this.opts.beforeStep(this),await((e,t,s)=>new Promise((i=>{s.push(setTimeout((async()=>{await e(),i()}),t||0))})))(e,t,this.timeouts),s||await this.opts.afterStep(this)}async#i(){if(!this.#o()&&this.cursor&&this.element.appendChild(this.cursor),this.#C){((e,t)=>{let r=`[${s}='${e}'] .${i}`,n=getComputedStyle(t),o=Object.entries(L).reduce(((e,[t,s])=>`${e} ${t}: var(--ti-cursor-${t}, ${s||n[t]});`),"");l(`${r} { display: inline-block; width: 0; ${o} }`,e)})(this.id,this.element),this.cursor.dataset.tiAnimationId=this.id;let{animation:e}=this.opts.cursor,{frames:t,options:r}=e;w({frames:t,cursor:this.cursor,options:{duration:this.opts.cursorSpeed,...r}})}}#o(){return S(this.element)}#h(e,t){return this.queue.add(e),this.#v(t),this}#v(e={}){let t=e.delay;t&&this.queue.add({delay:t})}#a(e={}){return[{func:()=>this.#c(e)},{func:()=>this.#c(this.opts)}]}async#c(e){this.opts=E(this.opts,e)}#s(){let e=this.opts.strings.filter((e=>!!e));e.forEach(((t,s)=>{if(this.type(t),s+1===e.length)return;let i=this.opts.breakLines?[{func:()=>this.#u(a("BR")),typeable:!0}]:C({func:this.#m.bind(this),delay:this.#l(1)},this.queue.getTypeable().length);this.#T(i)}))}#e=e=>{e.cursor=(e=>{if("object"==typeof e){let t={},{frames:s,options:i}=n.cursor.animation;return t.animation=e.animation||{},t.animation.frames=e.animation?.frames||s,t.animation.options=E(i,e.animation?.options||{}),t.autoPause=e.autoPause??n.cursor.autoPause,t.autoPauseDelay=e.autoPauseDelay||n.cursor.autoPauseDelay,t}return!0===e?n.cursor:e})(e.cursor??n.cursor),this.opts.strings=this.#w(t(this.opts.strings)),this.opts=E(this.opts,{html:!this.#I&&this.opts.html,nextStringDelay:h(this.opts.nextStringDelay),loopDelay:h(this.opts.loopDelay)})};#w(e){let t=this.element.innerHTML;return t?(this.element.innerHTML="",this.opts.startDelete?(this.element.innerHTML=t,m(this.element),this.#T(C({func:this.#m.bind(this),delay:this.#l(1),deletable:!0},this.#y.length)),e):(s=t,s.replace(/<!--(.+?)-->/g,"").trim().split(/<br(?:\s*?)(?:\/)?>/)).concat(e)):e;var s}#t(){if(this.#I)return null;let e=a("span");return e.className=i,this.#C?(e.innerHTML=f(this.opts.cursorChar).innerHTML,e):(e.style.visibility="hidden",e)}#T(e){let t=this.opts.nextStringDelay;this.queue.add([{delay:t[0]},...e,{delay:t[1]}])}#u(e){((e,t)=>{if(S(e))return void(e.value=`${e.value}${t.textContent}`);t.innerHTML="";let s=(r=t.originalParent,/body/i.test(r?.tagName)?e:t.originalParent||e);var r;s.insertBefore(t,A("."+i,s)||null)})(this.element,e)}#m(){this.#y.length&&(this.#I?this.element.value=this.element.value.slice(0,-1):this.#n(this.#y[this.cursorPosition]))}#n(e){((e,t)=>{if(!e)return;let s=e.parentNode;(s.childNodes.length>1||s.isSameNode(t)?e:s).remove()})(e,this.element)}#l(e=0){return function(e){let{speed:t,deleteSpeed:s,lifeLike:i}=e;return s=null!==s?s:t/3,i?[d(t,p(t)),d(s,p(s))]:[t,s]}(this.opts)[e]}get#d(){return this.predictedCursorPosition??this.cursorPosition}get#I(){return S(this.element)}get#C(){return!!this.opts.cursor&&!this.#I}get#y(){return e=this.element,S(e)?c(e.value):y(e,!0).filter((e=>!(e.childNodes.length>0)));var e}}})); |
import { QueueItem } from "./types"; | ||
declare let Queue: (initialItems: QueueItem[]) => { | ||
export interface QueueI { | ||
add: (steps: QueueItem[] | QueueItem) => typeof Queue; | ||
set: (index: number, item: QueueItem) => void; | ||
wipe: () => void; | ||
done: (key: Symbol, shouldDestroy?: boolean) => void; | ||
reset: () => void; | ||
destroy: (key: Symbol) => boolean; | ||
done: (key: Symbol, shouldDestroy?: boolean) => boolean; | ||
destroy: (key: Symbol) => void; | ||
getItems: (all?: boolean) => QueueItem[]; | ||
getQueue: () => Map<any, any>; | ||
getQueue: () => Map<Symbol, QueueItem>; | ||
getTypeable: () => QueueItem[]; | ||
}; | ||
} | ||
declare let Queue: (initialItems: QueueItem[]) => QueueI; | ||
export default Queue; |
/// <reference types="web-animations-js" /> | ||
export type TypeItInstance = (element: El | string, options: Options) => void; | ||
export type Character = { | ||
@@ -25,4 +24,4 @@ node: El | null; | ||
loop?: boolean; | ||
loopDelay?: number; | ||
nextStringDelay?: number; | ||
loopDelay?: number | number[]; | ||
nextStringDelay?: number | number[]; | ||
speed?: number; | ||
@@ -39,2 +38,8 @@ startDelay?: number; | ||
} | ||
export interface Statuses { | ||
started: boolean; | ||
completed: boolean; | ||
frozen: boolean; | ||
destroyed: boolean; | ||
} | ||
export type ActionOpts = Options & { | ||
@@ -41,0 +46,0 @@ to?: Sides; |
{ | ||
"name": "typeit", | ||
"version": "8.7.1", | ||
"version": "8.8.0", | ||
"description": "The most versatile animated typing utility on the planet.", | ||
@@ -23,3 +23,3 @@ "author": "Alex MacArthur <alex@macarthur.me> (https://macarthur.me)", | ||
"start": "vite serve examples --host 0.0.0.0", | ||
"test": "jest", | ||
"test": "vitest run", | ||
"postinstall": "node ./scripts/notice.js", | ||
@@ -43,22 +43,10 @@ "prepare": "npm run build" | ||
"devDependencies": { | ||
"@babel/preset-env": "^7.20.2", | ||
"@babel/preset-typescript": "^7.18.6", | ||
"@types/web-animations-js": "^2.2.12", | ||
"jest": "^29.3.1", | ||
"jest-cli": "^29.3.1", | ||
"jest-environment-jsdom": "^29.3.1", | ||
"terser": "^5.16.1", | ||
"typescript": "^4.9.4", | ||
"vite": "^4.0.1" | ||
"terser": "^5.24.0", | ||
"typescript": "^5.3.2", | ||
"vite": "^5.0.2", | ||
"vitest": "^0.34.6" | ||
}, | ||
"jest": { | ||
"clearMocks": true, | ||
"testPathIgnorePatterns": [ | ||
"<rootDir>/__tests__/setup.js" | ||
], | ||
"setupFilesAfterEnv": [ | ||
"./__tests__/setup.js" | ||
], | ||
"testEnvironment": "jsdom" | ||
"dependencies": { | ||
"@types/web-animations-js": "^2.2.16" | ||
} | ||
} |
import createElement from "./createElement"; | ||
import createTextNode from "./createTextNode"; | ||
export default (styles: string, id = ""): void => { | ||
export default (styles: string, id: string = ""): void => { | ||
let styleBlock: HTMLElement = createElement("style"); | ||
@@ -6,0 +6,0 @@ styleBlock.id = id; |
@@ -6,4 +6,2 @@ import isArray from "./isArray"; | ||
*/ | ||
export default <T>(value): T[] => { | ||
return isArray(value) ? value : [value]; | ||
}; | ||
export default <T>(value): T[] => (isArray(value) ? value : [value]); |
@@ -1,1 +0,1 @@ | ||
export default (el) => /<(.+)>(.*?)<\/(.+)>/.test(el.outerHTML); | ||
export default (el: any) => /<(.+)>(.*?)<\/(.+)>/.test(el.outerHTML); |
@@ -19,3 +19,3 @@ import { El, CursorOptions } from "../types"; | ||
let animation = getAnimationFromElement(cursor); | ||
let oldCurrentTime: number; | ||
let oldCurrentTime: CSSNumberish; | ||
@@ -22,0 +22,0 @@ // An existing animation is actively running... |
/** | ||
* Literally just wraps toArray() to save a few bytes | ||
* when it's repeatedly used. | ||
* | ||
* @param {any} | ||
* @return {array} | ||
*/ | ||
export default (val): any[] => Array.from(val); |
import asArray from "./helpers/asArray"; | ||
import { QueueItem } from "./types"; | ||
let Queue = function (initialItems: QueueItem[]) { | ||
export interface QueueI { | ||
add: (steps: QueueItem[] | QueueItem) => typeof Queue; | ||
set: (index: number, item: QueueItem) => void; | ||
wipe: () => void; | ||
done: (key: Symbol, shouldDestroy?: boolean) => void; | ||
reset: () => void; | ||
destroy: (key: Symbol) => void; | ||
getItems: (all?: boolean) => QueueItem[]; | ||
getQueue: () => Map<Symbol, QueueItem>; | ||
getTypeable: () => QueueItem[]; | ||
} | ||
let Queue = function (initialItems: QueueItem[]): QueueI { | ||
/** | ||
@@ -70,5 +82,5 @@ * Add a single or several steps onto the `waiting` queue. | ||
wipe, | ||
done, | ||
reset, | ||
destroy, | ||
done, | ||
getItems, | ||
@@ -75,0 +87,0 @@ getQueue, |
@@ -1,3 +0,1 @@ | ||
export type TypeItInstance = (element: El | string, options: Options) => void; | ||
export type Character = { | ||
@@ -30,4 +28,6 @@ node: El | null; | ||
loop?: boolean; | ||
loopDelay?: number; | ||
nextStringDelay?: number; | ||
loopDelay?: number | number[]; | ||
// @todo Rename to something like "betweenStringDelay" | ||
nextStringDelay?: number | number[]; | ||
speed?: number; | ||
@@ -45,2 +45,9 @@ startDelay?: number; | ||
export interface Statuses { | ||
started: boolean; | ||
completed: boolean; | ||
frozen: boolean; | ||
destroyed: boolean; | ||
} | ||
export type ActionOpts = Options & { | ||
@@ -47,0 +54,0 @@ to?: Sides; |
91576
4
2595
1
+ Added@types/web-animations-js@2.2.16(transitive)