@studiometa/js-toolkit 2.6.3 → 2.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studiometa/js-toolkit",
3
- "version": "2.6.3",
3
+ "version": "2.6.5",
4
4
  "description": "A set of useful little bits of JavaScript to boost your project! 🚀",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/services/drag.cjs CHANGED
@@ -40,7 +40,7 @@ var MODES = {
40
40
  STOP: "stop"
41
41
  };
42
42
  var count = 0;
43
- var events = ["pointerdown", "pointerup"];
43
+ var events = ["pointerdown", "pointerup", "touchend"];
44
44
  var passiveEventOptions = { passive: true };
45
45
  function getEventPosition(event) {
46
46
  const eventOrTouch = (0, import_utils.isDefined)(event.touches) ? event.touches[0] : event;
@@ -149,6 +149,7 @@ function createDragService(target, { dampFactor = 0.85, dragTreshold = 10 } = {}
149
149
  }
150
150
  break;
151
151
  case "pointerup":
152
+ case "touchend":
152
153
  drop();
153
154
  break;
154
155
  case "touchmove":
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../packages/js-toolkit/services/drag.js"],
4
- "sourcesContent": ["/* eslint-disable no-use-before-define */\nimport { useService } from './service.js';\nimport useRaf from './raf.js';\nimport inertiaFinalValue from '../utils/math/inertiaFinalValue.js';\n// eslint-disable-next-line import/extensions\nimport { isDefined } from '../utils/index.js';\n\n/**\n * @typedef {import('./index').ServiceInterface<DragServiceProps>} DragService\n * @typedef {import('./pointer.js').PointerServiceProps} PointerServiceProps\n * @typedef {'start'|'drag'|'drop'|'inertia'|'stop'} DragLifecycle\n * @typedef {Object} DragServiceProps\n * @property {DragLifecycle} mode\n * The current mode of the dragging logic.\n * @property {HTMLElement} target\n * The target element of the drag.\n * @property {boolean} isGrabbing\n * Whether the user is currently grabbing the targeted element or not.\n * @property {boolean} hasInertia\n * Whether the drag currently has inertia or not.\n * @property {number} x\n * The current horizontal position in the viewport.\n * @property {number} y\n * The current vertical position in the viewport.\n * @property {{ x: number, y: number }} delta\n * The delta between the current position and the last.\n * @property {{ x: number, y: number }} origin\n * The initial position where the dragging started.\n * @property {{ x: number, y: number }} distance\n * The distance from the current position to the origin.\n * @property {{ x: number, y: number }} final\n * The final position that will be reached at the end of the inertia.\n * @typedef {Object} DragServiceOptions\n * @property {number} [dampFactor]\n * @property {number} [dragTreshold]\n */\n\n/**\n * @type {Record<Uppercase<DragLifecycle>, DragLifecycle>}\n */\nconst MODES = {\n START: 'start',\n DRAG: 'drag',\n DROP: 'drop',\n INERTIA: 'inertia',\n STOP: 'stop',\n};\n\nlet count = 0;\nconst events = ['pointerdown', 'pointerup'];\nconst passiveEventOptions = { passive: true };\n\n/**\n * Get the client value for the given axis.\n * @param {MouseEvent|TouchEvent} event\n * @returns {{ x: number, y: number }}\n */\nfunction getEventPosition(event) {\n // @ts-ignore\n const eventOrTouch = isDefined(event.touches) ? event.touches[0] : event;\n return {\n x: eventOrTouch.clientX,\n y: eventOrTouch.clientY,\n };\n}\n\n/**\n * Get drag service.\n * @param {HTMLElement} target\n * @param {Object} options\n * @param {number} [options.dampFactor]\n * @param {number} [options.dragTreshold]\n * @returns {DragService}\n */\nfunction createDragService(target, { dampFactor = 0.85, dragTreshold = 10 } = {}) {\n count += 1;\n const id = `drag-${count}`;\n let previousEvent;\n\n /**\n * Test if we should allow click on links and buttons.\n *\n * @private\n * @returns {boolean}\n */\n function shouldPreventClick() {\n return Math.abs(props.distance.x) > dragTreshold || Math.abs(props.distance.y) > dragTreshold;\n }\n\n /**\n * Start the drag.\n * @param {number} x The initial horizontal position.\n * @param {number} y The initial vertical position.\n * @returns {void}\n */\n function start(x, y) {\n if (props.isGrabbing) {\n return;\n }\n\n // Reset state\n props.x = x;\n props.y = y;\n props.origin.x = x;\n props.origin.y = y;\n props.delta.x = 0;\n props.delta.y = 0;\n props.distance.x = 0;\n props.distance.y = 0;\n props.final.x = x;\n props.final.y = y;\n\n props.mode = MODES.START;\n\n // Enable grabbing\n props.isGrabbing = true;\n\n trigger(props);\n\n document.addEventListener('touchmove', handleEvent, passiveEventOptions);\n document.addEventListener('mousemove', handleEvent, passiveEventOptions);\n }\n\n /**\n * Stop the drag, or drop.\n * @returns {void}\n */\n function drop() {\n document.removeEventListener('touchmove', handleEvent);\n document.removeEventListener('mousemove', handleEvent);\n props.isGrabbing = false;\n props.mode = MODES.DROP;\n\n props.hasInertia = true;\n props.final.x = inertiaFinalValue(props.x, props.delta.x, dampFactor);\n props.final.y = inertiaFinalValue(props.y, props.delta.y, dampFactor);\n\n trigger(props);\n\n setTimeout(() => {\n const raf = useRaf();\n raf.remove(id);\n raf.add(id, rafHandler);\n }, 0);\n }\n\n /**\n * Stop the drag.\n */\n function stop() {\n useRaf().remove(id);\n props.isGrabbing = false;\n props.hasInertia = false;\n props.mode = MODES.STOP;\n trigger(props);\n }\n\n /**\n * Raf service handler.\n * @returns {void}\n */\n function rafHandler() {\n if (!props.isGrabbing) {\n props.x += props.delta.x;\n props.y += props.delta.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n\n props.delta.x *= dampFactor;\n props.delta.y *= dampFactor;\n\n if (props.mode !== MODES.INERTIA) {\n props.mode = MODES.INERTIA;\n }\n\n trigger(props);\n\n if (Math.abs(props.delta.x) < 0.1 && Math.abs(props.delta.y) < 0.1) {\n stop();\n }\n }\n }\n\n /**\n * Pointer service handler.\n * @param {TouchEvent|MouseEvent} event\n * @returns {void}\n */\n function drag(event) {\n if (props.isGrabbing) {\n const position = getEventPosition(event);\n props.x = position.x;\n props.y = position.y;\n\n if (previousEvent) {\n const previousPosition = getEventPosition(previousEvent);\n props.delta.x = position.x - previousPosition.x;\n props.delta.y = position.y - previousPosition.y;\n }\n\n props.final.x = position.x;\n props.final.y = position.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n\n if (props.mode !== MODES.DRAG) {\n props.mode = MODES.DRAG;\n }\n\n trigger(props);\n previousEvent = event;\n }\n }\n\n /**\n * Handle any event.\n * @param {MouseEvent|PointerEvent|DragEvent} event\n */\n function handleEvent(event) {\n switch (event.type) {\n case 'dragstart':\n event.preventDefault();\n break;\n case 'click':\n if (shouldPreventClick()) {\n event.stopImmediatePropagation();\n event.stopPropagation();\n event.preventDefault();\n }\n break;\n case 'pointerup':\n drop();\n break;\n case 'touchmove':\n case 'mousemove':\n drag(event);\n break;\n default:\n if (event.button === 0) {\n start(event.x, event.y);\n }\n }\n }\n\n const { add, remove, has, trigger, props } = useService({\n /**\n * @type {DragServiceProps}\n */\n props: {\n target,\n mode: undefined,\n isGrabbing: false,\n hasInertia: false,\n x: 0,\n y: 0,\n delta: {\n x: 0,\n y: 0,\n },\n origin: {\n x: 0,\n y: 0,\n },\n distance: {\n x: 0,\n y: 0,\n },\n final: {\n x: 0,\n y: 0,\n },\n },\n init() {\n events.forEach((event) => {\n target.addEventListener(event, handleEvent, passiveEventOptions);\n });\n target.addEventListener('dragstart', handleEvent, { capture: true });\n target.addEventListener('click', handleEvent, { capture: true });\n },\n kill() {\n events.forEach((event) => {\n target.removeEventListener(event, handleEvent);\n });\n target.removeEventListener('dragstart', handleEvent);\n target.removeEventListener('click', handleEvent);\n },\n });\n\n return {\n add,\n remove,\n has,\n props: () => props,\n };\n}\n\n/**\n * @type {WeakMap<HTMLElement, DragService | undefined>}\n */\nconst instances = new WeakMap();\n\n/**\n * Use the drag service.\n *\n * ```js\n * import { useDrag } from '@studiometa/js-toolkit/services';\n * const { add, remove, props } = useDrag();\n * const draggableElement = document.querySelector('.draggable');\n * add(draggableElement, (props) => {}, { factor: 0.8 });\n * remove(draggableElement);\n * props(draggableElement);\n * ```\n *\n * @param {HTMLElement} target\n * @param {DragServiceOptions} options\n * @returns {DragService}\n */\nexport default function useDrag(target, options) {\n if (!instances.has(target)) {\n instances.set(target, createDragService(target, options));\n }\n\n return instances.get(target);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/* eslint-disable no-use-before-define */\nimport { useService } from './service.js';\nimport useRaf from './raf.js';\nimport inertiaFinalValue from '../utils/math/inertiaFinalValue.js';\n// eslint-disable-next-line import/extensions\nimport { isDefined } from '../utils/index.js';\n\n/**\n * @typedef {import('./index').ServiceInterface<DragServiceProps>} DragService\n * @typedef {import('./pointer.js').PointerServiceProps} PointerServiceProps\n * @typedef {'start'|'drag'|'drop'|'inertia'|'stop'} DragLifecycle\n * @typedef {Object} DragServiceProps\n * @property {DragLifecycle} mode\n * The current mode of the dragging logic.\n * @property {HTMLElement} target\n * The target element of the drag.\n * @property {boolean} isGrabbing\n * Whether the user is currently grabbing the targeted element or not.\n * @property {boolean} hasInertia\n * Whether the drag currently has inertia or not.\n * @property {number} x\n * The current horizontal position in the viewport.\n * @property {number} y\n * The current vertical position in the viewport.\n * @property {{ x: number, y: number }} delta\n * The delta between the current position and the last.\n * @property {{ x: number, y: number }} origin\n * The initial position where the dragging started.\n * @property {{ x: number, y: number }} distance\n * The distance from the current position to the origin.\n * @property {{ x: number, y: number }} final\n * The final position that will be reached at the end of the inertia.\n * @typedef {Object} DragServiceOptions\n * @property {number} [dampFactor]\n * @property {number} [dragTreshold]\n */\n\n/**\n * @type {Record<Uppercase<DragLifecycle>, DragLifecycle>}\n */\nconst MODES = {\n START: 'start',\n DRAG: 'drag',\n DROP: 'drop',\n INERTIA: 'inertia',\n STOP: 'stop',\n};\n\nlet count = 0;\nconst events = ['pointerdown', 'pointerup', 'touchend'];\nconst passiveEventOptions = { passive: true };\n\n/**\n * Get the client value for the given axis.\n * @param {MouseEvent|TouchEvent} event\n * @returns {{ x: number, y: number }}\n */\nfunction getEventPosition(event) {\n // @ts-ignore\n const eventOrTouch = isDefined(event.touches) ? event.touches[0] : event;\n return {\n x: eventOrTouch.clientX,\n y: eventOrTouch.clientY,\n };\n}\n\n/**\n * Get drag service.\n * @param {HTMLElement} target\n * @param {Object} options\n * @param {number} [options.dampFactor]\n * @param {number} [options.dragTreshold]\n * @returns {DragService}\n */\nfunction createDragService(target, { dampFactor = 0.85, dragTreshold = 10 } = {}) {\n count += 1;\n const id = `drag-${count}`;\n let previousEvent;\n\n /**\n * Test if we should allow click on links and buttons.\n *\n * @private\n * @returns {boolean}\n */\n function shouldPreventClick() {\n return Math.abs(props.distance.x) > dragTreshold || Math.abs(props.distance.y) > dragTreshold;\n }\n\n /**\n * Start the drag.\n * @param {number} x The initial horizontal position.\n * @param {number} y The initial vertical position.\n * @returns {void}\n */\n function start(x, y) {\n if (props.isGrabbing) {\n return;\n }\n\n // Reset state\n props.x = x;\n props.y = y;\n props.origin.x = x;\n props.origin.y = y;\n props.delta.x = 0;\n props.delta.y = 0;\n props.distance.x = 0;\n props.distance.y = 0;\n props.final.x = x;\n props.final.y = y;\n\n props.mode = MODES.START;\n\n // Enable grabbing\n props.isGrabbing = true;\n\n trigger(props);\n\n document.addEventListener('touchmove', handleEvent, passiveEventOptions);\n document.addEventListener('mousemove', handleEvent, passiveEventOptions);\n }\n\n /**\n * Stop the drag, or drop.\n * @returns {void}\n */\n function drop() {\n document.removeEventListener('touchmove', handleEvent);\n document.removeEventListener('mousemove', handleEvent);\n props.isGrabbing = false;\n props.mode = MODES.DROP;\n\n props.hasInertia = true;\n props.final.x = inertiaFinalValue(props.x, props.delta.x, dampFactor);\n props.final.y = inertiaFinalValue(props.y, props.delta.y, dampFactor);\n\n trigger(props);\n\n setTimeout(() => {\n const raf = useRaf();\n raf.remove(id);\n raf.add(id, rafHandler);\n }, 0);\n }\n\n /**\n * Stop the drag.\n */\n function stop() {\n useRaf().remove(id);\n props.isGrabbing = false;\n props.hasInertia = false;\n props.mode = MODES.STOP;\n trigger(props);\n }\n\n /**\n * Raf service handler.\n * @returns {void}\n */\n function rafHandler() {\n if (!props.isGrabbing) {\n props.x += props.delta.x;\n props.y += props.delta.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n\n props.delta.x *= dampFactor;\n props.delta.y *= dampFactor;\n\n if (props.mode !== MODES.INERTIA) {\n props.mode = MODES.INERTIA;\n }\n\n trigger(props);\n\n if (Math.abs(props.delta.x) < 0.1 && Math.abs(props.delta.y) < 0.1) {\n stop();\n }\n }\n }\n\n /**\n * Pointer service handler.\n * @param {TouchEvent|MouseEvent} event\n * @returns {void}\n */\n function drag(event) {\n if (props.isGrabbing) {\n const position = getEventPosition(event);\n props.x = position.x;\n props.y = position.y;\n\n if (previousEvent) {\n const previousPosition = getEventPosition(previousEvent);\n props.delta.x = position.x - previousPosition.x;\n props.delta.y = position.y - previousPosition.y;\n }\n\n props.final.x = position.x;\n props.final.y = position.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n\n if (props.mode !== MODES.DRAG) {\n props.mode = MODES.DRAG;\n }\n\n trigger(props);\n previousEvent = event;\n }\n }\n\n /**\n * Handle any event.\n * @param {MouseEvent|PointerEvent|DragEvent} event\n */\n function handleEvent(event) {\n switch (event.type) {\n case 'dragstart':\n event.preventDefault();\n break;\n case 'click':\n if (shouldPreventClick()) {\n event.stopImmediatePropagation();\n event.stopPropagation();\n event.preventDefault();\n }\n break;\n case 'pointerup':\n case 'touchend':\n drop();\n break;\n case 'touchmove':\n case 'mousemove':\n drag(event);\n break;\n default:\n if (event.button === 0) {\n start(event.x, event.y);\n }\n }\n }\n\n const { add, remove, has, trigger, props } = useService({\n /**\n * @type {DragServiceProps}\n */\n props: {\n target,\n mode: undefined,\n isGrabbing: false,\n hasInertia: false,\n x: 0,\n y: 0,\n delta: {\n x: 0,\n y: 0,\n },\n origin: {\n x: 0,\n y: 0,\n },\n distance: {\n x: 0,\n y: 0,\n },\n final: {\n x: 0,\n y: 0,\n },\n },\n init() {\n events.forEach((event) => {\n target.addEventListener(event, handleEvent, passiveEventOptions);\n });\n target.addEventListener('dragstart', handleEvent, { capture: true });\n target.addEventListener('click', handleEvent, { capture: true });\n },\n kill() {\n events.forEach((event) => {\n target.removeEventListener(event, handleEvent);\n });\n target.removeEventListener('dragstart', handleEvent);\n target.removeEventListener('click', handleEvent);\n },\n });\n\n return {\n add,\n remove,\n has,\n props: () => props,\n };\n}\n\n/**\n * @type {WeakMap<HTMLElement, DragService | undefined>}\n */\nconst instances = new WeakMap();\n\n/**\n * Use the drag service.\n *\n * ```js\n * import { useDrag } from '@studiometa/js-toolkit/services';\n * const { add, remove, props } = useDrag();\n * const draggableElement = document.querySelector('.draggable');\n * add(draggableElement, (props) => {}, { factor: 0.8 });\n * remove(draggableElement);\n * props(draggableElement);\n * ```\n *\n * @param {HTMLElement} target\n * @param {DragServiceOptions} options\n * @returns {DragService}\n */\nexport default function useDrag(target, options) {\n if (!instances.has(target)) {\n instances.set(target, createDragService(target, options));\n }\n\n return instances.get(target);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["inertiaFinalValue", "useRaf"]
7
7
  }
package/services/drag.js CHANGED
@@ -10,7 +10,7 @@ const MODES = {
10
10
  STOP: "stop"
11
11
  };
12
12
  let count = 0;
13
- const events = ["pointerdown", "pointerup"];
13
+ const events = ["pointerdown", "pointerup", "touchend"];
14
14
  const passiveEventOptions = { passive: true };
15
15
  function getEventPosition(event) {
16
16
  const eventOrTouch = isDefined(event.touches) ? event.touches[0] : event;
@@ -119,6 +119,7 @@ function createDragService(target, { dampFactor = 0.85, dragTreshold = 10 } = {}
119
119
  }
120
120
  break;
121
121
  case "pointerup":
122
+ case "touchend":
122
123
  drop();
123
124
  break;
124
125
  case "touchmove":
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../packages/js-toolkit/services/drag.js"],
4
- "sourcesContent": ["/* eslint-disable no-use-before-define */\nimport { useService } from './service.js';\nimport useRaf from './raf.js';\nimport inertiaFinalValue from '../utils/math/inertiaFinalValue.js';\n// eslint-disable-next-line import/extensions\nimport { isDefined } from '../utils/index.js';\n\n/**\n * @typedef {import('./index').ServiceInterface<DragServiceProps>} DragService\n * @typedef {import('./pointer.js').PointerServiceProps} PointerServiceProps\n * @typedef {'start'|'drag'|'drop'|'inertia'|'stop'} DragLifecycle\n * @typedef {Object} DragServiceProps\n * @property {DragLifecycle} mode\n * The current mode of the dragging logic.\n * @property {HTMLElement} target\n * The target element of the drag.\n * @property {boolean} isGrabbing\n * Whether the user is currently grabbing the targeted element or not.\n * @property {boolean} hasInertia\n * Whether the drag currently has inertia or not.\n * @property {number} x\n * The current horizontal position in the viewport.\n * @property {number} y\n * The current vertical position in the viewport.\n * @property {{ x: number, y: number }} delta\n * The delta between the current position and the last.\n * @property {{ x: number, y: number }} origin\n * The initial position where the dragging started.\n * @property {{ x: number, y: number }} distance\n * The distance from the current position to the origin.\n * @property {{ x: number, y: number }} final\n * The final position that will be reached at the end of the inertia.\n * @typedef {Object} DragServiceOptions\n * @property {number} [dampFactor]\n * @property {number} [dragTreshold]\n */\n\n/**\n * @type {Record<Uppercase<DragLifecycle>, DragLifecycle>}\n */\nconst MODES = {\n START: 'start',\n DRAG: 'drag',\n DROP: 'drop',\n INERTIA: 'inertia',\n STOP: 'stop',\n};\n\nlet count = 0;\nconst events = ['pointerdown', 'pointerup'];\nconst passiveEventOptions = { passive: true };\n\n/**\n * Get the client value for the given axis.\n * @param {MouseEvent|TouchEvent} event\n * @returns {{ x: number, y: number }}\n */\nfunction getEventPosition(event) {\n // @ts-ignore\n const eventOrTouch = isDefined(event.touches) ? event.touches[0] : event;\n return {\n x: eventOrTouch.clientX,\n y: eventOrTouch.clientY,\n };\n}\n\n/**\n * Get drag service.\n * @param {HTMLElement} target\n * @param {Object} options\n * @param {number} [options.dampFactor]\n * @param {number} [options.dragTreshold]\n * @returns {DragService}\n */\nfunction createDragService(target, { dampFactor = 0.85, dragTreshold = 10 } = {}) {\n count += 1;\n const id = `drag-${count}`;\n let previousEvent;\n\n /**\n * Test if we should allow click on links and buttons.\n *\n * @private\n * @returns {boolean}\n */\n function shouldPreventClick() {\n return Math.abs(props.distance.x) > dragTreshold || Math.abs(props.distance.y) > dragTreshold;\n }\n\n /**\n * Start the drag.\n * @param {number} x The initial horizontal position.\n * @param {number} y The initial vertical position.\n * @returns {void}\n */\n function start(x, y) {\n if (props.isGrabbing) {\n return;\n }\n\n // Reset state\n props.x = x;\n props.y = y;\n props.origin.x = x;\n props.origin.y = y;\n props.delta.x = 0;\n props.delta.y = 0;\n props.distance.x = 0;\n props.distance.y = 0;\n props.final.x = x;\n props.final.y = y;\n\n props.mode = MODES.START;\n\n // Enable grabbing\n props.isGrabbing = true;\n\n trigger(props);\n\n document.addEventListener('touchmove', handleEvent, passiveEventOptions);\n document.addEventListener('mousemove', handleEvent, passiveEventOptions);\n }\n\n /**\n * Stop the drag, or drop.\n * @returns {void}\n */\n function drop() {\n document.removeEventListener('touchmove', handleEvent);\n document.removeEventListener('mousemove', handleEvent);\n props.isGrabbing = false;\n props.mode = MODES.DROP;\n\n props.hasInertia = true;\n props.final.x = inertiaFinalValue(props.x, props.delta.x, dampFactor);\n props.final.y = inertiaFinalValue(props.y, props.delta.y, dampFactor);\n\n trigger(props);\n\n setTimeout(() => {\n const raf = useRaf();\n raf.remove(id);\n raf.add(id, rafHandler);\n }, 0);\n }\n\n /**\n * Stop the drag.\n */\n function stop() {\n useRaf().remove(id);\n props.isGrabbing = false;\n props.hasInertia = false;\n props.mode = MODES.STOP;\n trigger(props);\n }\n\n /**\n * Raf service handler.\n * @returns {void}\n */\n function rafHandler() {\n if (!props.isGrabbing) {\n props.x += props.delta.x;\n props.y += props.delta.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n\n props.delta.x *= dampFactor;\n props.delta.y *= dampFactor;\n\n if (props.mode !== MODES.INERTIA) {\n props.mode = MODES.INERTIA;\n }\n\n trigger(props);\n\n if (Math.abs(props.delta.x) < 0.1 && Math.abs(props.delta.y) < 0.1) {\n stop();\n }\n }\n }\n\n /**\n * Pointer service handler.\n * @param {TouchEvent|MouseEvent} event\n * @returns {void}\n */\n function drag(event) {\n if (props.isGrabbing) {\n const position = getEventPosition(event);\n props.x = position.x;\n props.y = position.y;\n\n if (previousEvent) {\n const previousPosition = getEventPosition(previousEvent);\n props.delta.x = position.x - previousPosition.x;\n props.delta.y = position.y - previousPosition.y;\n }\n\n props.final.x = position.x;\n props.final.y = position.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n\n if (props.mode !== MODES.DRAG) {\n props.mode = MODES.DRAG;\n }\n\n trigger(props);\n previousEvent = event;\n }\n }\n\n /**\n * Handle any event.\n * @param {MouseEvent|PointerEvent|DragEvent} event\n */\n function handleEvent(event) {\n switch (event.type) {\n case 'dragstart':\n event.preventDefault();\n break;\n case 'click':\n if (shouldPreventClick()) {\n event.stopImmediatePropagation();\n event.stopPropagation();\n event.preventDefault();\n }\n break;\n case 'pointerup':\n drop();\n break;\n case 'touchmove':\n case 'mousemove':\n drag(event);\n break;\n default:\n if (event.button === 0) {\n start(event.x, event.y);\n }\n }\n }\n\n const { add, remove, has, trigger, props } = useService({\n /**\n * @type {DragServiceProps}\n */\n props: {\n target,\n mode: undefined,\n isGrabbing: false,\n hasInertia: false,\n x: 0,\n y: 0,\n delta: {\n x: 0,\n y: 0,\n },\n origin: {\n x: 0,\n y: 0,\n },\n distance: {\n x: 0,\n y: 0,\n },\n final: {\n x: 0,\n y: 0,\n },\n },\n init() {\n events.forEach((event) => {\n target.addEventListener(event, handleEvent, passiveEventOptions);\n });\n target.addEventListener('dragstart', handleEvent, { capture: true });\n target.addEventListener('click', handleEvent, { capture: true });\n },\n kill() {\n events.forEach((event) => {\n target.removeEventListener(event, handleEvent);\n });\n target.removeEventListener('dragstart', handleEvent);\n target.removeEventListener('click', handleEvent);\n },\n });\n\n return {\n add,\n remove,\n has,\n props: () => props,\n };\n}\n\n/**\n * @type {WeakMap<HTMLElement, DragService | undefined>}\n */\nconst instances = new WeakMap();\n\n/**\n * Use the drag service.\n *\n * ```js\n * import { useDrag } from '@studiometa/js-toolkit/services';\n * const { add, remove, props } = useDrag();\n * const draggableElement = document.querySelector('.draggable');\n * add(draggableElement, (props) => {}, { factor: 0.8 });\n * remove(draggableElement);\n * props(draggableElement);\n * ```\n *\n * @param {HTMLElement} target\n * @param {DragServiceOptions} options\n * @returns {DragService}\n */\nexport default function useDrag(target, options) {\n if (!instances.has(target)) {\n instances.set(target, createDragService(target, options));\n }\n\n return instances.get(target);\n}\n"],
5
- "mappings": "AACA,SAAS,kBAAkB;AAC3B,OAAO,YAAY;AACnB,OAAO,uBAAuB;AAE9B,SAAS,iBAAiB;AAmC1B,MAAM,QAAQ;AAAA,EACZ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AACR;AAEA,IAAI,QAAQ;AACZ,MAAM,SAAS,CAAC,eAAe,WAAW;AAC1C,MAAM,sBAAsB,EAAE,SAAS,KAAK;AAO5C,SAAS,iBAAiB,OAAO;AAE/B,QAAM,eAAe,UAAU,MAAM,OAAO,IAAI,MAAM,QAAQ,KAAK;AACnE,SAAO;AAAA,IACL,GAAG,aAAa;AAAA,IAChB,GAAG,aAAa;AAAA,EAClB;AACF;AAUA,SAAS,kBAAkB,QAAQ,EAAE,aAAa,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG;AAChF,WAAS;AACT,QAAM,KAAK,QAAQ;AACnB,MAAI;AAQJ,WAAS,qBAAqB;AAC5B,WAAO,KAAK,IAAI,MAAM,SAAS,CAAC,IAAI,gBAAgB,KAAK,IAAI,MAAM,SAAS,CAAC,IAAI;AAAA,EACnF;AAQA,WAAS,MAAM,GAAG,GAAG;AACnB,QAAI,MAAM,YAAY;AACpB;AAAA,IACF;AAGA,UAAM,IAAI;AACV,UAAM,IAAI;AACV,UAAM,OAAO,IAAI;AACjB,UAAM,OAAO,IAAI;AACjB,UAAM,MAAM,IAAI;AAChB,UAAM,MAAM,IAAI;AAChB,UAAM,SAAS,IAAI;AACnB,UAAM,SAAS,IAAI;AACnB,UAAM,MAAM,IAAI;AAChB,UAAM,MAAM,IAAI;AAEhB,UAAM,OAAO,MAAM;AAGnB,UAAM,aAAa;AAEnB,YAAQ,KAAK;AAEb,aAAS,iBAAiB,aAAa,aAAa,mBAAmB;AACvE,aAAS,iBAAiB,aAAa,aAAa,mBAAmB;AAAA,EACzE;AAMA,WAAS,OAAO;AACd,aAAS,oBAAoB,aAAa,WAAW;AACrD,aAAS,oBAAoB,aAAa,WAAW;AACrD,UAAM,aAAa;AACnB,UAAM,OAAO,MAAM;AAEnB,UAAM,aAAa;AACnB,UAAM,MAAM,IAAI,kBAAkB,MAAM,GAAG,MAAM,MAAM,GAAG,UAAU;AACpE,UAAM,MAAM,IAAI,kBAAkB,MAAM,GAAG,MAAM,MAAM,GAAG,UAAU;AAEpE,YAAQ,KAAK;AAEb,eAAW,MAAM;AACf,YAAM,MAAM,OAAO;AACnB,UAAI,OAAO,EAAE;AACb,UAAI,IAAI,IAAI,UAAU;AAAA,IACxB,GAAG,CAAC;AAAA,EACN;AAKA,WAAS,OAAO;AACd,WAAO,EAAE,OAAO,EAAE;AAClB,UAAM,aAAa;AACnB,UAAM,aAAa;AACnB,UAAM,OAAO,MAAM;AACnB,YAAQ,KAAK;AAAA,EACf;AAMA,WAAS,aAAa;AACpB,QAAI,CAAC,MAAM,YAAY;AACrB,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAC1C,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAE1C,YAAM,MAAM,KAAK;AACjB,YAAM,MAAM,KAAK;AAEjB,UAAI,MAAM,SAAS,MAAM,SAAS;AAChC,cAAM,OAAO,MAAM;AAAA,MACrB;AAEA,cAAQ,KAAK;AAEb,UAAI,KAAK,IAAI,MAAM,MAAM,CAAC,IAAI,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC,IAAI,KAAK;AAClE,aAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAOA,WAAS,KAAK,OAAO;AACnB,QAAI,MAAM,YAAY;AACpB,YAAM,WAAW,iBAAiB,KAAK;AACvC,YAAM,IAAI,SAAS;AACnB,YAAM,IAAI,SAAS;AAEnB,UAAI,eAAe;AACjB,cAAM,mBAAmB,iBAAiB,aAAa;AACvD,cAAM,MAAM,IAAI,SAAS,IAAI,iBAAiB;AAC9C,cAAM,MAAM,IAAI,SAAS,IAAI,iBAAiB;AAAA,MAChD;AAEA,YAAM,MAAM,IAAI,SAAS;AACzB,YAAM,MAAM,IAAI,SAAS;AACzB,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAC1C,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAE1C,UAAI,MAAM,SAAS,MAAM,MAAM;AAC7B,cAAM,OAAO,MAAM;AAAA,MACrB;AAEA,cAAQ,KAAK;AACb,sBAAgB;AAAA,IAClB;AAAA,EACF;AAMA,WAAS,YAAY,OAAO;AAC1B,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AACH,cAAM,eAAe;AACrB;AAAA,MACF,KAAK;AACH,YAAI,mBAAmB,GAAG;AACxB,gBAAM,yBAAyB;AAC/B,gBAAM,gBAAgB;AACtB,gBAAM,eAAe;AAAA,QACvB;AACA;AAAA,MACF,KAAK;AACH,aAAK;AACL;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,aAAK,KAAK;AACV;AAAA,MACF;AACE,YAAI,MAAM,WAAW,GAAG;AACtB,gBAAM,MAAM,GAAG,MAAM,CAAC;AAAA,QACxB;AAAA,IACJ;AAAA,EACF;AAEA,QAAM,EAAE,KAAK,QAAQ,KAAK,SAAS,MAAM,IAAI,WAAW;AAAA,IAItD,OAAO;AAAA,MACL;AAAA,MACA,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,GAAG;AAAA,MACH,GAAG;AAAA,MACH,OAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,MACA,UAAU;AAAA,QACR,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,MACA,OAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,IACF;AAAA,IACA,OAAO;AACL,aAAO,QAAQ,CAAC,UAAU;AACxB,eAAO,iBAAiB,OAAO,aAAa,mBAAmB;AAAA,MACjE,CAAC;AACD,aAAO,iBAAiB,aAAa,aAAa,EAAE,SAAS,KAAK,CAAC;AACnE,aAAO,iBAAiB,SAAS,aAAa,EAAE,SAAS,KAAK,CAAC;AAAA,IACjE;AAAA,IACA,OAAO;AACL,aAAO,QAAQ,CAAC,UAAU;AACxB,eAAO,oBAAoB,OAAO,WAAW;AAAA,MAC/C,CAAC;AACD,aAAO,oBAAoB,aAAa,WAAW;AACnD,aAAO,oBAAoB,SAAS,WAAW;AAAA,IACjD;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,MAAM;AAAA,EACf;AACF;AAKA,MAAM,YAAY,oBAAI,QAAQ;AAkBf,SAAR,QAAyB,QAAQ,SAAS;AAC/C,MAAI,CAAC,UAAU,IAAI,MAAM,GAAG;AAC1B,cAAU,IAAI,QAAQ,kBAAkB,QAAQ,OAAO,CAAC;AAAA,EAC1D;AAEA,SAAO,UAAU,IAAI,MAAM;AAC7B;",
4
+ "sourcesContent": ["/* eslint-disable no-use-before-define */\nimport { useService } from './service.js';\nimport useRaf from './raf.js';\nimport inertiaFinalValue from '../utils/math/inertiaFinalValue.js';\n// eslint-disable-next-line import/extensions\nimport { isDefined } from '../utils/index.js';\n\n/**\n * @typedef {import('./index').ServiceInterface<DragServiceProps>} DragService\n * @typedef {import('./pointer.js').PointerServiceProps} PointerServiceProps\n * @typedef {'start'|'drag'|'drop'|'inertia'|'stop'} DragLifecycle\n * @typedef {Object} DragServiceProps\n * @property {DragLifecycle} mode\n * The current mode of the dragging logic.\n * @property {HTMLElement} target\n * The target element of the drag.\n * @property {boolean} isGrabbing\n * Whether the user is currently grabbing the targeted element or not.\n * @property {boolean} hasInertia\n * Whether the drag currently has inertia or not.\n * @property {number} x\n * The current horizontal position in the viewport.\n * @property {number} y\n * The current vertical position in the viewport.\n * @property {{ x: number, y: number }} delta\n * The delta between the current position and the last.\n * @property {{ x: number, y: number }} origin\n * The initial position where the dragging started.\n * @property {{ x: number, y: number }} distance\n * The distance from the current position to the origin.\n * @property {{ x: number, y: number }} final\n * The final position that will be reached at the end of the inertia.\n * @typedef {Object} DragServiceOptions\n * @property {number} [dampFactor]\n * @property {number} [dragTreshold]\n */\n\n/**\n * @type {Record<Uppercase<DragLifecycle>, DragLifecycle>}\n */\nconst MODES = {\n START: 'start',\n DRAG: 'drag',\n DROP: 'drop',\n INERTIA: 'inertia',\n STOP: 'stop',\n};\n\nlet count = 0;\nconst events = ['pointerdown', 'pointerup', 'touchend'];\nconst passiveEventOptions = { passive: true };\n\n/**\n * Get the client value for the given axis.\n * @param {MouseEvent|TouchEvent} event\n * @returns {{ x: number, y: number }}\n */\nfunction getEventPosition(event) {\n // @ts-ignore\n const eventOrTouch = isDefined(event.touches) ? event.touches[0] : event;\n return {\n x: eventOrTouch.clientX,\n y: eventOrTouch.clientY,\n };\n}\n\n/**\n * Get drag service.\n * @param {HTMLElement} target\n * @param {Object} options\n * @param {number} [options.dampFactor]\n * @param {number} [options.dragTreshold]\n * @returns {DragService}\n */\nfunction createDragService(target, { dampFactor = 0.85, dragTreshold = 10 } = {}) {\n count += 1;\n const id = `drag-${count}`;\n let previousEvent;\n\n /**\n * Test if we should allow click on links and buttons.\n *\n * @private\n * @returns {boolean}\n */\n function shouldPreventClick() {\n return Math.abs(props.distance.x) > dragTreshold || Math.abs(props.distance.y) > dragTreshold;\n }\n\n /**\n * Start the drag.\n * @param {number} x The initial horizontal position.\n * @param {number} y The initial vertical position.\n * @returns {void}\n */\n function start(x, y) {\n if (props.isGrabbing) {\n return;\n }\n\n // Reset state\n props.x = x;\n props.y = y;\n props.origin.x = x;\n props.origin.y = y;\n props.delta.x = 0;\n props.delta.y = 0;\n props.distance.x = 0;\n props.distance.y = 0;\n props.final.x = x;\n props.final.y = y;\n\n props.mode = MODES.START;\n\n // Enable grabbing\n props.isGrabbing = true;\n\n trigger(props);\n\n document.addEventListener('touchmove', handleEvent, passiveEventOptions);\n document.addEventListener('mousemove', handleEvent, passiveEventOptions);\n }\n\n /**\n * Stop the drag, or drop.\n * @returns {void}\n */\n function drop() {\n document.removeEventListener('touchmove', handleEvent);\n document.removeEventListener('mousemove', handleEvent);\n props.isGrabbing = false;\n props.mode = MODES.DROP;\n\n props.hasInertia = true;\n props.final.x = inertiaFinalValue(props.x, props.delta.x, dampFactor);\n props.final.y = inertiaFinalValue(props.y, props.delta.y, dampFactor);\n\n trigger(props);\n\n setTimeout(() => {\n const raf = useRaf();\n raf.remove(id);\n raf.add(id, rafHandler);\n }, 0);\n }\n\n /**\n * Stop the drag.\n */\n function stop() {\n useRaf().remove(id);\n props.isGrabbing = false;\n props.hasInertia = false;\n props.mode = MODES.STOP;\n trigger(props);\n }\n\n /**\n * Raf service handler.\n * @returns {void}\n */\n function rafHandler() {\n if (!props.isGrabbing) {\n props.x += props.delta.x;\n props.y += props.delta.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n\n props.delta.x *= dampFactor;\n props.delta.y *= dampFactor;\n\n if (props.mode !== MODES.INERTIA) {\n props.mode = MODES.INERTIA;\n }\n\n trigger(props);\n\n if (Math.abs(props.delta.x) < 0.1 && Math.abs(props.delta.y) < 0.1) {\n stop();\n }\n }\n }\n\n /**\n * Pointer service handler.\n * @param {TouchEvent|MouseEvent} event\n * @returns {void}\n */\n function drag(event) {\n if (props.isGrabbing) {\n const position = getEventPosition(event);\n props.x = position.x;\n props.y = position.y;\n\n if (previousEvent) {\n const previousPosition = getEventPosition(previousEvent);\n props.delta.x = position.x - previousPosition.x;\n props.delta.y = position.y - previousPosition.y;\n }\n\n props.final.x = position.x;\n props.final.y = position.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n\n if (props.mode !== MODES.DRAG) {\n props.mode = MODES.DRAG;\n }\n\n trigger(props);\n previousEvent = event;\n }\n }\n\n /**\n * Handle any event.\n * @param {MouseEvent|PointerEvent|DragEvent} event\n */\n function handleEvent(event) {\n switch (event.type) {\n case 'dragstart':\n event.preventDefault();\n break;\n case 'click':\n if (shouldPreventClick()) {\n event.stopImmediatePropagation();\n event.stopPropagation();\n event.preventDefault();\n }\n break;\n case 'pointerup':\n case 'touchend':\n drop();\n break;\n case 'touchmove':\n case 'mousemove':\n drag(event);\n break;\n default:\n if (event.button === 0) {\n start(event.x, event.y);\n }\n }\n }\n\n const { add, remove, has, trigger, props } = useService({\n /**\n * @type {DragServiceProps}\n */\n props: {\n target,\n mode: undefined,\n isGrabbing: false,\n hasInertia: false,\n x: 0,\n y: 0,\n delta: {\n x: 0,\n y: 0,\n },\n origin: {\n x: 0,\n y: 0,\n },\n distance: {\n x: 0,\n y: 0,\n },\n final: {\n x: 0,\n y: 0,\n },\n },\n init() {\n events.forEach((event) => {\n target.addEventListener(event, handleEvent, passiveEventOptions);\n });\n target.addEventListener('dragstart', handleEvent, { capture: true });\n target.addEventListener('click', handleEvent, { capture: true });\n },\n kill() {\n events.forEach((event) => {\n target.removeEventListener(event, handleEvent);\n });\n target.removeEventListener('dragstart', handleEvent);\n target.removeEventListener('click', handleEvent);\n },\n });\n\n return {\n add,\n remove,\n has,\n props: () => props,\n };\n}\n\n/**\n * @type {WeakMap<HTMLElement, DragService | undefined>}\n */\nconst instances = new WeakMap();\n\n/**\n * Use the drag service.\n *\n * ```js\n * import { useDrag } from '@studiometa/js-toolkit/services';\n * const { add, remove, props } = useDrag();\n * const draggableElement = document.querySelector('.draggable');\n * add(draggableElement, (props) => {}, { factor: 0.8 });\n * remove(draggableElement);\n * props(draggableElement);\n * ```\n *\n * @param {HTMLElement} target\n * @param {DragServiceOptions} options\n * @returns {DragService}\n */\nexport default function useDrag(target, options) {\n if (!instances.has(target)) {\n instances.set(target, createDragService(target, options));\n }\n\n return instances.get(target);\n}\n"],
5
+ "mappings": "AACA,SAAS,kBAAkB;AAC3B,OAAO,YAAY;AACnB,OAAO,uBAAuB;AAE9B,SAAS,iBAAiB;AAmC1B,MAAM,QAAQ;AAAA,EACZ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AACR;AAEA,IAAI,QAAQ;AACZ,MAAM,SAAS,CAAC,eAAe,aAAa,UAAU;AACtD,MAAM,sBAAsB,EAAE,SAAS,KAAK;AAO5C,SAAS,iBAAiB,OAAO;AAE/B,QAAM,eAAe,UAAU,MAAM,OAAO,IAAI,MAAM,QAAQ,KAAK;AACnE,SAAO;AAAA,IACL,GAAG,aAAa;AAAA,IAChB,GAAG,aAAa;AAAA,EAClB;AACF;AAUA,SAAS,kBAAkB,QAAQ,EAAE,aAAa,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG;AAChF,WAAS;AACT,QAAM,KAAK,QAAQ;AACnB,MAAI;AAQJ,WAAS,qBAAqB;AAC5B,WAAO,KAAK,IAAI,MAAM,SAAS,CAAC,IAAI,gBAAgB,KAAK,IAAI,MAAM,SAAS,CAAC,IAAI;AAAA,EACnF;AAQA,WAAS,MAAM,GAAG,GAAG;AACnB,QAAI,MAAM,YAAY;AACpB;AAAA,IACF;AAGA,UAAM,IAAI;AACV,UAAM,IAAI;AACV,UAAM,OAAO,IAAI;AACjB,UAAM,OAAO,IAAI;AACjB,UAAM,MAAM,IAAI;AAChB,UAAM,MAAM,IAAI;AAChB,UAAM,SAAS,IAAI;AACnB,UAAM,SAAS,IAAI;AACnB,UAAM,MAAM,IAAI;AAChB,UAAM,MAAM,IAAI;AAEhB,UAAM,OAAO,MAAM;AAGnB,UAAM,aAAa;AAEnB,YAAQ,KAAK;AAEb,aAAS,iBAAiB,aAAa,aAAa,mBAAmB;AACvE,aAAS,iBAAiB,aAAa,aAAa,mBAAmB;AAAA,EACzE;AAMA,WAAS,OAAO;AACd,aAAS,oBAAoB,aAAa,WAAW;AACrD,aAAS,oBAAoB,aAAa,WAAW;AACrD,UAAM,aAAa;AACnB,UAAM,OAAO,MAAM;AAEnB,UAAM,aAAa;AACnB,UAAM,MAAM,IAAI,kBAAkB,MAAM,GAAG,MAAM,MAAM,GAAG,UAAU;AACpE,UAAM,MAAM,IAAI,kBAAkB,MAAM,GAAG,MAAM,MAAM,GAAG,UAAU;AAEpE,YAAQ,KAAK;AAEb,eAAW,MAAM;AACf,YAAM,MAAM,OAAO;AACnB,UAAI,OAAO,EAAE;AACb,UAAI,IAAI,IAAI,UAAU;AAAA,IACxB,GAAG,CAAC;AAAA,EACN;AAKA,WAAS,OAAO;AACd,WAAO,EAAE,OAAO,EAAE;AAClB,UAAM,aAAa;AACnB,UAAM,aAAa;AACnB,UAAM,OAAO,MAAM;AACnB,YAAQ,KAAK;AAAA,EACf;AAMA,WAAS,aAAa;AACpB,QAAI,CAAC,MAAM,YAAY;AACrB,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAC1C,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAE1C,YAAM,MAAM,KAAK;AACjB,YAAM,MAAM,KAAK;AAEjB,UAAI,MAAM,SAAS,MAAM,SAAS;AAChC,cAAM,OAAO,MAAM;AAAA,MACrB;AAEA,cAAQ,KAAK;AAEb,UAAI,KAAK,IAAI,MAAM,MAAM,CAAC,IAAI,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC,IAAI,KAAK;AAClE,aAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAOA,WAAS,KAAK,OAAO;AACnB,QAAI,MAAM,YAAY;AACpB,YAAM,WAAW,iBAAiB,KAAK;AACvC,YAAM,IAAI,SAAS;AACnB,YAAM,IAAI,SAAS;AAEnB,UAAI,eAAe;AACjB,cAAM,mBAAmB,iBAAiB,aAAa;AACvD,cAAM,MAAM,IAAI,SAAS,IAAI,iBAAiB;AAC9C,cAAM,MAAM,IAAI,SAAS,IAAI,iBAAiB;AAAA,MAChD;AAEA,YAAM,MAAM,IAAI,SAAS;AACzB,YAAM,MAAM,IAAI,SAAS;AACzB,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAC1C,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAE1C,UAAI,MAAM,SAAS,MAAM,MAAM;AAC7B,cAAM,OAAO,MAAM;AAAA,MACrB;AAEA,cAAQ,KAAK;AACb,sBAAgB;AAAA,IAClB;AAAA,EACF;AAMA,WAAS,YAAY,OAAO;AAC1B,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AACH,cAAM,eAAe;AACrB;AAAA,MACF,KAAK;AACH,YAAI,mBAAmB,GAAG;AACxB,gBAAM,yBAAyB;AAC/B,gBAAM,gBAAgB;AACtB,gBAAM,eAAe;AAAA,QACvB;AACA;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,aAAK;AACL;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,aAAK,KAAK;AACV;AAAA,MACF;AACE,YAAI,MAAM,WAAW,GAAG;AACtB,gBAAM,MAAM,GAAG,MAAM,CAAC;AAAA,QACxB;AAAA,IACJ;AAAA,EACF;AAEA,QAAM,EAAE,KAAK,QAAQ,KAAK,SAAS,MAAM,IAAI,WAAW;AAAA,IAItD,OAAO;AAAA,MACL;AAAA,MACA,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,GAAG;AAAA,MACH,GAAG;AAAA,MACH,OAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,MACA,UAAU;AAAA,QACR,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,MACA,OAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,IACF;AAAA,IACA,OAAO;AACL,aAAO,QAAQ,CAAC,UAAU;AACxB,eAAO,iBAAiB,OAAO,aAAa,mBAAmB;AAAA,MACjE,CAAC;AACD,aAAO,iBAAiB,aAAa,aAAa,EAAE,SAAS,KAAK,CAAC;AACnE,aAAO,iBAAiB,SAAS,aAAa,EAAE,SAAS,KAAK,CAAC;AAAA,IACjE;AAAA,IACA,OAAO;AACL,aAAO,QAAQ,CAAC,UAAU;AACxB,eAAO,oBAAoB,OAAO,WAAW;AAAA,MAC/C,CAAC;AACD,aAAO,oBAAoB,aAAa,WAAW;AACnD,aAAO,oBAAoB,SAAS,WAAW;AAAA,IACjD;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,MAAM;AAAA,EACf;AACF;AAKA,MAAM,YAAY,oBAAI,QAAQ;AAkBf,SAAR,QAAyB,QAAQ,SAAS;AAC/C,MAAI,CAAC,UAAU,IAAI,MAAM,GAAG;AAC1B,cAAU,IAAI,QAAQ,kBAAkB,QAAQ,OAAO,CAAC;AAAA,EAC1D;AAEA,SAAO,UAAU,IAAI,MAAM;AAC7B;",
6
6
  "names": []
7
7
  }
package/utils/tween.cjs CHANGED
@@ -35,7 +35,7 @@ var import_is = require("./is.cjs");
35
35
  var import_noop = require("./noop.cjs");
36
36
  var import_raf = __toESM(require("../services/raf.cjs"), 1);
37
37
  var id = 0;
38
- var PROGRESS_PRECISION = 1e-4;
38
+ var PROGRESS_PRECISION = 0.01;
39
39
  function normalizeEase(ease) {
40
40
  if (!(0, import_is.isDefined)(ease)) {
41
41
  return import_noop.noopValue;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../packages/js-toolkit/utils/tween.js"],
4
- "sourcesContent": ["import { cubicBezier } from '@motionone/easing';\nimport { lerp, map, clamp01 } from './math/index.js';\nimport { isDefined, isArray } from './is.js';\nimport { noop, noopValue as linear } from './noop.js';\nimport useRaf from '../services/raf.js';\n\nlet id = 0;\nconst PROGRESS_PRECISION = 0.0001;\n\n/**\n * @typedef {[number, number, number, number]} BezierCurve\n * @typedef {{\n * duration?: number;\n * easing?: import('./math/createEases.js').EasingFunction|BezierCurve;\n * onStart?: () => void;\n * onProgress?: (progress: number, easedProgress: number) => void;\n * onFinish?: (progress: number, easedProgress: number) => void;\n * }} TweenOptions\n */\n\n/**\n * Normalize a easing function with default fallbacks.\n * @param {((p:number) => number)|[number,number,number,number]} ease\n * @returns {(p:number) => number}\n */\nexport function normalizeEase(ease) {\n if (!isDefined(ease)) {\n return linear;\n }\n\n if (isArray(ease)) {\n return cubicBezier(...ease);\n }\n\n return ease;\n}\n\n/**\n * Tween from 0 to 1.\n * @param {(progress:number)=>any} callback\n * @param {TweenOptions} [options]\n * @returns {any}\n */\nexport function tween(callback, options = {}) {\n const raf = useRaf();\n\n let progressValue = 0;\n let easedProgress = 0;\n\n const ease = normalizeEase(options.easing);\n let duration = options.duration ?? 1;\n duration *= 1000;\n\n let startTime = performance.now();\n let endTime = startTime + duration;\n\n const key = `tw-${id}`;\n id += 1;\n\n const { onStart = noop, onProgress = noop, onFinish = noop } = options;\n let isRunning = false;\n\n /**\n * Pause the animation.\n *\n * @returns {void}\n */\n function pause() {\n isRunning = false;\n raf.remove(key);\n }\n\n /**\n * Set the progress value.\n *\n * @param {number} newProgress The new progress value.\n * @returns {number}\n */\n function progress(newProgress) {\n if (typeof newProgress === 'undefined') {\n return easedProgress;\n }\n\n progressValue = newProgress;\n easedProgress = ease(progressValue);\n\n // Stop when reaching precision\n if (Math.abs(1 - easedProgress) < PROGRESS_PRECISION) {\n progressValue = 1;\n easedProgress = 1;\n }\n\n callback(easedProgress);\n onProgress(progressValue, easedProgress);\n\n if (easedProgress === 1) {\n pause();\n requestAnimationFrame(() => onFinish(progressValue, easedProgress));\n }\n\n return progressValue;\n }\n\n /**\n * Loop for rendering the animation.\n *\n * @param {Object} props\n * @param {number} props.time\n * @returns {void}\n */\n function tick(props) {\n if (!isRunning) {\n raf.remove(key);\n return;\n }\n\n progress(clamp01(map(props.time, startTime, endTime, 0, 1)));\n }\n\n /**\n * Play the animation.\n *\n * @returns {void}\n */\n function start() {\n onStart();\n startTime = performance.now();\n endTime = startTime + duration;\n progressValue = 0;\n easedProgress = 0;\n isRunning = true;\n\n raf.add(key, tick);\n }\n\n /**\n * Play a paused animation.\n * @returns {void}\n */\n function play() {\n if (isRunning) {\n return;\n }\n\n startTime = performance.now() - lerp(0, duration, progressValue);\n endTime = startTime + duration;\n isRunning = true;\n\n raf.add(key, tick);\n }\n\n return {\n start,\n finish: () => progress(1),\n pause,\n play,\n progress,\n };\n}\n"],
4
+ "sourcesContent": ["import { cubicBezier } from '@motionone/easing';\nimport { lerp, map, clamp01 } from './math/index.js';\nimport { isDefined, isArray } from './is.js';\nimport { noop, noopValue as linear } from './noop.js';\nimport useRaf from '../services/raf.js';\n\nlet id = 0;\nconst PROGRESS_PRECISION = 0.01;\n\n/**\n * @typedef {[number, number, number, number]} BezierCurve\n * @typedef {{\n * duration?: number;\n * easing?: import('./math/createEases.js').EasingFunction|BezierCurve;\n * onStart?: () => void;\n * onProgress?: (progress: number, easedProgress: number) => void;\n * onFinish?: (progress: number, easedProgress: number) => void;\n * }} TweenOptions\n */\n\n/**\n * Normalize a easing function with default fallbacks.\n * @param {((p:number) => number)|[number,number,number,number]} ease\n * @returns {(p:number) => number}\n */\nexport function normalizeEase(ease) {\n if (!isDefined(ease)) {\n return linear;\n }\n\n if (isArray(ease)) {\n return cubicBezier(...ease);\n }\n\n return ease;\n}\n\n/**\n * Tween from 0 to 1.\n * @param {(progress:number)=>any} callback\n * @param {TweenOptions} [options]\n * @returns {any}\n */\nexport function tween(callback, options = {}) {\n const raf = useRaf();\n\n let progressValue = 0;\n let easedProgress = 0;\n\n const ease = normalizeEase(options.easing);\n let duration = options.duration ?? 1;\n duration *= 1000;\n\n let startTime = performance.now();\n let endTime = startTime + duration;\n\n const key = `tw-${id}`;\n id += 1;\n\n const { onStart = noop, onProgress = noop, onFinish = noop } = options;\n let isRunning = false;\n\n /**\n * Pause the animation.\n *\n * @returns {void}\n */\n function pause() {\n isRunning = false;\n raf.remove(key);\n }\n\n /**\n * Set the progress value.\n *\n * @param {number} newProgress The new progress value.\n * @returns {number}\n */\n function progress(newProgress) {\n if (typeof newProgress === 'undefined') {\n return easedProgress;\n }\n\n progressValue = newProgress;\n easedProgress = ease(progressValue);\n\n // Stop when reaching precision\n if (Math.abs(1 - easedProgress) < PROGRESS_PRECISION) {\n progressValue = 1;\n easedProgress = 1;\n }\n\n callback(easedProgress);\n onProgress(progressValue, easedProgress);\n\n if (easedProgress === 1) {\n pause();\n requestAnimationFrame(() => onFinish(progressValue, easedProgress));\n }\n\n return progressValue;\n }\n\n /**\n * Loop for rendering the animation.\n *\n * @param {Object} props\n * @param {number} props.time\n * @returns {void}\n */\n function tick(props) {\n if (!isRunning) {\n raf.remove(key);\n return;\n }\n\n progress(clamp01(map(props.time, startTime, endTime, 0, 1)));\n }\n\n /**\n * Play the animation.\n *\n * @returns {void}\n */\n function start() {\n onStart();\n startTime = performance.now();\n endTime = startTime + duration;\n progressValue = 0;\n easedProgress = 0;\n isRunning = true;\n\n raf.add(key, tick);\n }\n\n /**\n * Play a paused animation.\n * @returns {void}\n */\n function play() {\n if (isRunning) {\n return;\n }\n\n startTime = performance.now() - lerp(0, duration, progressValue);\n endTime = startTime + duration;\n isRunning = true;\n\n raf.add(key, tick);\n }\n\n return {\n start,\n finish: () => progress(1),\n pause,\n play,\n progress,\n };\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA4B;AAC5B,kBAAmC;AACnC,gBAAmC;AACnC,kBAA0C;AAC1C,iBAAmB;AAEnB,IAAI,KAAK;AACT,IAAM,qBAAqB;AAkBpB,SAAS,cAAc,MAAM;AAClC,MAAI,KAAC,qBAAU,IAAI,GAAG;AACpB,WAAO,YAAAA;AAAA,EACT;AAEA,UAAI,mBAAQ,IAAI,GAAG;AACjB,eAAO,2BAAY,GAAG,IAAI;AAAA,EAC5B;AAEA,SAAO;AACT;AAQO,SAAS,MAAM,UAAU,UAAU,CAAC,GAAG;AAC5C,QAAM,UAAM,WAAAC,SAAO;AAEnB,MAAI,gBAAgB;AACpB,MAAI,gBAAgB;AAEpB,QAAM,OAAO,cAAc,QAAQ,MAAM;AACzC,MAAI,WAAW,QAAQ,YAAY;AACnC,cAAY;AAEZ,MAAI,YAAY,YAAY,IAAI;AAChC,MAAI,UAAU,YAAY;AAE1B,QAAM,MAAM,MAAM;AAClB,QAAM;AAEN,QAAM,EAAE,UAAU,kBAAM,aAAa,kBAAM,WAAW,iBAAK,IAAI;AAC/D,MAAI,YAAY;AAOhB,WAAS,QAAQ;AACf,gBAAY;AACZ,QAAI,OAAO,GAAG;AAAA,EAChB;AAQA,WAAS,SAAS,aAAa;AAC7B,QAAI,OAAO,gBAAgB,aAAa;AACtC,aAAO;AAAA,IACT;AAEA,oBAAgB;AAChB,oBAAgB,KAAK,aAAa;AAGlC,QAAI,KAAK,IAAI,IAAI,aAAa,IAAI,oBAAoB;AACpD,sBAAgB;AAChB,sBAAgB;AAAA,IAClB;AAEA,aAAS,aAAa;AACtB,eAAW,eAAe,aAAa;AAEvC,QAAI,kBAAkB,GAAG;AACvB,YAAM;AACN,4BAAsB,MAAM,SAAS,eAAe,aAAa,CAAC;AAAA,IACpE;AAEA,WAAO;AAAA,EACT;AASA,WAAS,KAAK,OAAO;AACnB,QAAI,CAAC,WAAW;AACd,UAAI,OAAO,GAAG;AACd;AAAA,IACF;AAEA,iBAAS,yBAAQ,iBAAI,MAAM,MAAM,WAAW,SAAS,GAAG,CAAC,CAAC,CAAC;AAAA,EAC7D;AAOA,WAAS,QAAQ;AACf,YAAQ;AACR,gBAAY,YAAY,IAAI;AAC5B,cAAU,YAAY;AACtB,oBAAgB;AAChB,oBAAgB;AAChB,gBAAY;AAEZ,QAAI,IAAI,KAAK,IAAI;AAAA,EACnB;AAMA,WAAS,OAAO;AACd,QAAI,WAAW;AACb;AAAA,IACF;AAEA,gBAAY,YAAY,IAAI,QAAI,kBAAK,GAAG,UAAU,aAAa;AAC/D,cAAU,YAAY;AACtB,gBAAY;AAEZ,QAAI,IAAI,KAAK,IAAI;AAAA,EACnB;AAEA,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,MAAM,SAAS,CAAC;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
6
  "names": ["linear", "useRaf"]
7
7
  }
package/utils/tween.js CHANGED
@@ -4,7 +4,7 @@ import { isDefined, isArray } from "./is.js";
4
4
  import { noop, noopValue as linear } from "./noop.js";
5
5
  import useRaf from "../services/raf.js";
6
6
  let id = 0;
7
- const PROGRESS_PRECISION = 1e-4;
7
+ const PROGRESS_PRECISION = 0.01;
8
8
  function normalizeEase(ease) {
9
9
  if (!isDefined(ease)) {
10
10
  return linear;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../packages/js-toolkit/utils/tween.js"],
4
- "sourcesContent": ["import { cubicBezier } from '@motionone/easing';\nimport { lerp, map, clamp01 } from './math/index.js';\nimport { isDefined, isArray } from './is.js';\nimport { noop, noopValue as linear } from './noop.js';\nimport useRaf from '../services/raf.js';\n\nlet id = 0;\nconst PROGRESS_PRECISION = 0.0001;\n\n/**\n * @typedef {[number, number, number, number]} BezierCurve\n * @typedef {{\n * duration?: number;\n * easing?: import('./math/createEases.js').EasingFunction|BezierCurve;\n * onStart?: () => void;\n * onProgress?: (progress: number, easedProgress: number) => void;\n * onFinish?: (progress: number, easedProgress: number) => void;\n * }} TweenOptions\n */\n\n/**\n * Normalize a easing function with default fallbacks.\n * @param {((p:number) => number)|[number,number,number,number]} ease\n * @returns {(p:number) => number}\n */\nexport function normalizeEase(ease) {\n if (!isDefined(ease)) {\n return linear;\n }\n\n if (isArray(ease)) {\n return cubicBezier(...ease);\n }\n\n return ease;\n}\n\n/**\n * Tween from 0 to 1.\n * @param {(progress:number)=>any} callback\n * @param {TweenOptions} [options]\n * @returns {any}\n */\nexport function tween(callback, options = {}) {\n const raf = useRaf();\n\n let progressValue = 0;\n let easedProgress = 0;\n\n const ease = normalizeEase(options.easing);\n let duration = options.duration ?? 1;\n duration *= 1000;\n\n let startTime = performance.now();\n let endTime = startTime + duration;\n\n const key = `tw-${id}`;\n id += 1;\n\n const { onStart = noop, onProgress = noop, onFinish = noop } = options;\n let isRunning = false;\n\n /**\n * Pause the animation.\n *\n * @returns {void}\n */\n function pause() {\n isRunning = false;\n raf.remove(key);\n }\n\n /**\n * Set the progress value.\n *\n * @param {number} newProgress The new progress value.\n * @returns {number}\n */\n function progress(newProgress) {\n if (typeof newProgress === 'undefined') {\n return easedProgress;\n }\n\n progressValue = newProgress;\n easedProgress = ease(progressValue);\n\n // Stop when reaching precision\n if (Math.abs(1 - easedProgress) < PROGRESS_PRECISION) {\n progressValue = 1;\n easedProgress = 1;\n }\n\n callback(easedProgress);\n onProgress(progressValue, easedProgress);\n\n if (easedProgress === 1) {\n pause();\n requestAnimationFrame(() => onFinish(progressValue, easedProgress));\n }\n\n return progressValue;\n }\n\n /**\n * Loop for rendering the animation.\n *\n * @param {Object} props\n * @param {number} props.time\n * @returns {void}\n */\n function tick(props) {\n if (!isRunning) {\n raf.remove(key);\n return;\n }\n\n progress(clamp01(map(props.time, startTime, endTime, 0, 1)));\n }\n\n /**\n * Play the animation.\n *\n * @returns {void}\n */\n function start() {\n onStart();\n startTime = performance.now();\n endTime = startTime + duration;\n progressValue = 0;\n easedProgress = 0;\n isRunning = true;\n\n raf.add(key, tick);\n }\n\n /**\n * Play a paused animation.\n * @returns {void}\n */\n function play() {\n if (isRunning) {\n return;\n }\n\n startTime = performance.now() - lerp(0, duration, progressValue);\n endTime = startTime + duration;\n isRunning = true;\n\n raf.add(key, tick);\n }\n\n return {\n start,\n finish: () => progress(1),\n pause,\n play,\n progress,\n };\n}\n"],
4
+ "sourcesContent": ["import { cubicBezier } from '@motionone/easing';\nimport { lerp, map, clamp01 } from './math/index.js';\nimport { isDefined, isArray } from './is.js';\nimport { noop, noopValue as linear } from './noop.js';\nimport useRaf from '../services/raf.js';\n\nlet id = 0;\nconst PROGRESS_PRECISION = 0.01;\n\n/**\n * @typedef {[number, number, number, number]} BezierCurve\n * @typedef {{\n * duration?: number;\n * easing?: import('./math/createEases.js').EasingFunction|BezierCurve;\n * onStart?: () => void;\n * onProgress?: (progress: number, easedProgress: number) => void;\n * onFinish?: (progress: number, easedProgress: number) => void;\n * }} TweenOptions\n */\n\n/**\n * Normalize a easing function with default fallbacks.\n * @param {((p:number) => number)|[number,number,number,number]} ease\n * @returns {(p:number) => number}\n */\nexport function normalizeEase(ease) {\n if (!isDefined(ease)) {\n return linear;\n }\n\n if (isArray(ease)) {\n return cubicBezier(...ease);\n }\n\n return ease;\n}\n\n/**\n * Tween from 0 to 1.\n * @param {(progress:number)=>any} callback\n * @param {TweenOptions} [options]\n * @returns {any}\n */\nexport function tween(callback, options = {}) {\n const raf = useRaf();\n\n let progressValue = 0;\n let easedProgress = 0;\n\n const ease = normalizeEase(options.easing);\n let duration = options.duration ?? 1;\n duration *= 1000;\n\n let startTime = performance.now();\n let endTime = startTime + duration;\n\n const key = `tw-${id}`;\n id += 1;\n\n const { onStart = noop, onProgress = noop, onFinish = noop } = options;\n let isRunning = false;\n\n /**\n * Pause the animation.\n *\n * @returns {void}\n */\n function pause() {\n isRunning = false;\n raf.remove(key);\n }\n\n /**\n * Set the progress value.\n *\n * @param {number} newProgress The new progress value.\n * @returns {number}\n */\n function progress(newProgress) {\n if (typeof newProgress === 'undefined') {\n return easedProgress;\n }\n\n progressValue = newProgress;\n easedProgress = ease(progressValue);\n\n // Stop when reaching precision\n if (Math.abs(1 - easedProgress) < PROGRESS_PRECISION) {\n progressValue = 1;\n easedProgress = 1;\n }\n\n callback(easedProgress);\n onProgress(progressValue, easedProgress);\n\n if (easedProgress === 1) {\n pause();\n requestAnimationFrame(() => onFinish(progressValue, easedProgress));\n }\n\n return progressValue;\n }\n\n /**\n * Loop for rendering the animation.\n *\n * @param {Object} props\n * @param {number} props.time\n * @returns {void}\n */\n function tick(props) {\n if (!isRunning) {\n raf.remove(key);\n return;\n }\n\n progress(clamp01(map(props.time, startTime, endTime, 0, 1)));\n }\n\n /**\n * Play the animation.\n *\n * @returns {void}\n */\n function start() {\n onStart();\n startTime = performance.now();\n endTime = startTime + duration;\n progressValue = 0;\n easedProgress = 0;\n isRunning = true;\n\n raf.add(key, tick);\n }\n\n /**\n * Play a paused animation.\n * @returns {void}\n */\n function play() {\n if (isRunning) {\n return;\n }\n\n startTime = performance.now() - lerp(0, duration, progressValue);\n endTime = startTime + duration;\n isRunning = true;\n\n raf.add(key, tick);\n }\n\n return {\n start,\n finish: () => progress(1),\n pause,\n play,\n progress,\n };\n}\n"],
5
5
  "mappings": "AAAA,SAAS,mBAAmB;AAC5B,SAAS,MAAM,KAAK,eAAe;AACnC,SAAS,WAAW,eAAe;AACnC,SAAS,MAAM,aAAa,cAAc;AAC1C,OAAO,YAAY;AAEnB,IAAI,KAAK;AACT,MAAM,qBAAqB;AAkBpB,SAAS,cAAc,MAAM;AAClC,MAAI,CAAC,UAAU,IAAI,GAAG;AACpB,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,IAAI,GAAG;AACjB,WAAO,YAAY,GAAG,IAAI;AAAA,EAC5B;AAEA,SAAO;AACT;AAQO,SAAS,MAAM,UAAU,UAAU,CAAC,GAAG;AAC5C,QAAM,MAAM,OAAO;AAEnB,MAAI,gBAAgB;AACpB,MAAI,gBAAgB;AAEpB,QAAM,OAAO,cAAc,QAAQ,MAAM;AACzC,MAAI,WAAW,QAAQ,YAAY;AACnC,cAAY;AAEZ,MAAI,YAAY,YAAY,IAAI;AAChC,MAAI,UAAU,YAAY;AAE1B,QAAM,MAAM,MAAM;AAClB,QAAM;AAEN,QAAM,EAAE,UAAU,MAAM,aAAa,MAAM,WAAW,KAAK,IAAI;AAC/D,MAAI,YAAY;AAOhB,WAAS,QAAQ;AACf,gBAAY;AACZ,QAAI,OAAO,GAAG;AAAA,EAChB;AAQA,WAAS,SAAS,aAAa;AAC7B,QAAI,OAAO,gBAAgB,aAAa;AACtC,aAAO;AAAA,IACT;AAEA,oBAAgB;AAChB,oBAAgB,KAAK,aAAa;AAGlC,QAAI,KAAK,IAAI,IAAI,aAAa,IAAI,oBAAoB;AACpD,sBAAgB;AAChB,sBAAgB;AAAA,IAClB;AAEA,aAAS,aAAa;AACtB,eAAW,eAAe,aAAa;AAEvC,QAAI,kBAAkB,GAAG;AACvB,YAAM;AACN,4BAAsB,MAAM,SAAS,eAAe,aAAa,CAAC;AAAA,IACpE;AAEA,WAAO;AAAA,EACT;AASA,WAAS,KAAK,OAAO;AACnB,QAAI,CAAC,WAAW;AACd,UAAI,OAAO,GAAG;AACd;AAAA,IACF;AAEA,aAAS,QAAQ,IAAI,MAAM,MAAM,WAAW,SAAS,GAAG,CAAC,CAAC,CAAC;AAAA,EAC7D;AAOA,WAAS,QAAQ;AACf,YAAQ;AACR,gBAAY,YAAY,IAAI;AAC5B,cAAU,YAAY;AACtB,oBAAgB;AAChB,oBAAgB;AAChB,gBAAY;AAEZ,QAAI,IAAI,KAAK,IAAI;AAAA,EACnB;AAMA,WAAS,OAAO;AACd,QAAI,WAAW;AACb;AAAA,IACF;AAEA,gBAAY,YAAY,IAAI,IAAI,KAAK,GAAG,UAAU,aAAa;AAC/D,cAAU,YAAY;AACtB,gBAAY;AAEZ,QAAI,IAAI,KAAK,IAAI;AAAA,EACnB;AAEA,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,MAAM,SAAS,CAAC;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }