From 26b469b2a7a5d6671491c3e80ab423892cd5ab21 Mon Sep 17 00:00:00 2001 From: Alex Iglesias Date: Sun, 24 May 2026 19:40:44 +0200 Subject: [PATCH 1/4] wip --- packages/attributes/package.json | 1 + packages/attributes/src/load.ts | 4 + packages/safetriangles/README.md | 25 ++ packages/safetriangles/package.json | 22 ++ packages/safetriangles/src/factory.ts | 207 ++++++++++++++++ packages/safetriangles/src/index.ts | 3 + packages/safetriangles/src/init.ts | 38 +++ packages/safetriangles/src/utils/constants.ts | 32 +++ packages/safetriangles/src/utils/selectors.ts | 9 + packages/safetriangles/tsconfig.json | 3 + packages/utils/src/constants/attributes.ts | 2 + pnpm-lock.yaml | 225 +++++++++--------- 12 files changed, 463 insertions(+), 108 deletions(-) create mode 100644 packages/safetriangles/README.md create mode 100644 packages/safetriangles/package.json create mode 100644 packages/safetriangles/src/factory.ts create mode 100644 packages/safetriangles/src/index.ts create mode 100644 packages/safetriangles/src/init.ts create mode 100644 packages/safetriangles/src/utils/constants.ts create mode 100644 packages/safetriangles/src/utils/selectors.ts create mode 100644 packages/safetriangles/tsconfig.json diff --git a/packages/attributes/package.json b/packages/attributes/package.json index 4cf732083..636308f7c 100644 --- a/packages/attributes/package.json +++ b/packages/attributes/package.json @@ -57,6 +57,7 @@ "@finsweet/attributes-rangeslider": "workspace:*", "@finsweet/attributes-readtime": "workspace:*", "@finsweet/attributes-removequery": "workspace:*", + "@finsweet/attributes-safetriangles": "workspace:*", "@finsweet/attributes-scrolldisable": "workspace:*", "@finsweet/attributes-selectcustom": "workspace:*", "@finsweet/attributes-sliderdots": "workspace:*", diff --git a/packages/attributes/src/load.ts b/packages/attributes/src/load.ts index 1f2a29607..a42864728 100644 --- a/packages/attributes/src/load.ts +++ b/packages/attributes/src/load.ts @@ -86,6 +86,10 @@ export const loadAttribute = async (key: FinsweetAttributeKey) => { return import('@finsweet/attributes-removequery'); } + case 'safetriangles': { + return import('@finsweet/attributes-safetriangles'); + } + case 'scrolldisable': { return import('@finsweet/attributes-scrolldisable'); } diff --git a/packages/safetriangles/README.md b/packages/safetriangles/README.md new file mode 100644 index 000000000..04096cdb3 --- /dev/null +++ b/packages/safetriangles/README.md @@ -0,0 +1,25 @@ +# `safetriangles` Attribute + +Generate safe triangles between trigger and target elements to keep context menus open while users move the pointer. + +## Getting Started + +Please follow the documentation at [finsweet.com/attributes](https://www.finsweet.com/attributes) to learn how to use Attributes in your Webflow projects. + +## Accessing the API + +To learn how to access the API, please check the general [API Reference](../attributes/README.md#api-reference) documentation: + +```javascript +window.FinsweetAttributes ||= []; +window.FinsweetAttributes.push([ + 'safetriangles', + () => { + // Your code goes here. + }, +]); +``` + +## License + +[Apache 2.0](../../LICENSE.md) diff --git a/packages/safetriangles/package.json b/packages/safetriangles/package.json new file mode 100644 index 000000000..74edfbd8c --- /dev/null +++ b/packages/safetriangles/package.json @@ -0,0 +1,22 @@ +{ + "name": "@finsweet/attributes-safetriangles", + "version": "0.0.0", + "description": "Generate safe triangles between trigger and target elements to keep context menus open while users move the pointer.", + "private": true, + "type": "module", + "types": "src/index.ts", + "scripts": { + "lint": "eslint ./src && prettier --check ./src", + "lint:fix": "eslint ./src --fix && prettier --write ./src", + "check": "tsc --noEmit" + }, + "exports": { + ".": { + "types": "./src/index.ts", + "import": "./src/index.ts" + } + }, + "dependencies": { + "@finsweet/attributes-utils": "workspace:*" + } +} diff --git a/packages/safetriangles/src/factory.ts b/packages/safetriangles/src/factory.ts new file mode 100644 index 000000000..0c4ea19ed --- /dev/null +++ b/packages/safetriangles/src/factory.ts @@ -0,0 +1,207 @@ +type Point = { + x: number; + y: number; +}; + +type Direction = 'left' | 'right' | 'up' | 'down'; + +type SafeTriangleOptions = { + delay: number; + debug: boolean; +}; + +const SVG_NS = 'http://www.w3.org/2000/svg'; + +/** + * Inits a safe triangle between a trigger and target element. + * @param trigger The trigger element. + * @param target The target element. + * @param options Triangle options. + * @returns A cleanup callback. + */ +export const initSafeTriangle = (trigger: HTMLElement, target: HTMLElement, { delay, debug }: SafeTriangleOptions) => { + const previousInlinePosition = trigger.style.position; + + if (window.getComputedStyle(trigger).position === 'static') { + trigger.style.position = 'relative'; + } + + const svg = document.createElementNS(SVG_NS, 'svg'); + svg.setAttribute('fill', 'none'); + svg.setAttribute('aria-hidden', 'true'); + svg.style.position = 'absolute'; + svg.style.pointerEvents = 'none'; + svg.style.inset = 'auto'; + svg.style.overflow = 'visible'; + svg.dataset.fsSafeTriangles = 'svg'; + + const path = document.createElementNS(SVG_NS, 'path'); + path.style.pointerEvents = 'auto'; + path.dataset.fsSafeTriangles = 'path'; + + applyPathStyles(path, debug); + + svg.appendChild(path); + trigger.appendChild(svg); + + let rects = getRects(trigger, target); + + const updateBounds = () => { + rects = getRects(trigger, target); + applySvgBounds(svg, rects); + }; + + const updateTriangle = (cursor: Point) => { + const { triggerRect, targetRect, unionRect } = rects; + const direction = getDirection(triggerRect, targetRect); + const [edgeA, edgeB] = getTargetEdge(targetRect, direction); + + const cursorLocal = toLocalPoint(cursor, unionRect); + const edgeALocal = toLocalPoint(edgeA, unionRect); + const edgeBLocal = toLocalPoint(edgeB, unionRect); + + path.setAttribute( + 'd', + `M ${cursorLocal.x} ${cursorLocal.y} L ${edgeALocal.x} ${edgeALocal.y} L ${edgeBLocal.x} ${edgeBLocal.y} Z` + ); + }; + + const handleMouseEnter = () => { + updateBounds(); + + if (delay <= 0) { + const { triggerRect } = rects; + updateTriangle({ x: triggerRect.left + triggerRect.width / 2, y: triggerRect.top + triggerRect.height / 2 }); + } + }; + + const handleMouseMove = (event: MouseEvent) => { + window.setTimeout( + () => { + updateTriangle({ x: event.clientX, y: event.clientY }); + }, + Math.max(0, delay) + ); + }; + + const handleWindowResize = () => { + updateBounds(); + }; + + trigger.addEventListener('mouseenter', handleMouseEnter); + trigger.addEventListener('mousemove', handleMouseMove); + window.addEventListener('resize', handleWindowResize); + + updateBounds(); + + return () => { + trigger.removeEventListener('mouseenter', handleMouseEnter); + trigger.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('resize', handleWindowResize); + + svg.remove(); + + if (previousInlinePosition) { + trigger.style.position = previousInlinePosition; + } else { + trigger.style.removeProperty('position'); + } + }; +}; + +const applyPathStyles = (path: SVGPathElement, debug: boolean) => { + if (debug) { + path.setAttribute('fill', 'rgb(0 255 0 / 0.1)'); + path.setAttribute('stroke', 'green'); + path.setAttribute('stroke-width', '1'); + return; + } + + path.setAttribute('fill', 'transparent'); + path.removeAttribute('stroke'); + path.removeAttribute('stroke-width'); +}; + +const getRects = (trigger: HTMLElement, target: HTMLElement) => { + const triggerRect = trigger.getBoundingClientRect(); + const targetRect = target.getBoundingClientRect(); + const unionRect = getUnionRect(triggerRect, targetRect); + + return { + triggerRect, + targetRect, + unionRect, + }; +}; + +const applySvgBounds = ( + svg: SVGSVGElement, + { triggerRect, unionRect }: { triggerRect: DOMRect; unionRect: DOMRect } +) => { + const width = Math.max(1, unionRect.width); + const height = Math.max(1, unionRect.height); + + svg.style.left = `${unionRect.left - triggerRect.left}px`; + svg.style.top = `${unionRect.top - triggerRect.top}px`; + svg.style.width = `${width}px`; + svg.style.height = `${height}px`; + svg.setAttribute('viewBox', `0 0 ${width} ${height}`); +}; + +const getUnionRect = (a: DOMRect, b: DOMRect) => { + const left = Math.min(a.left, b.left); + const top = Math.min(a.top, b.top); + const right = Math.max(a.right, b.right); + const bottom = Math.max(a.bottom, b.bottom); + + return new DOMRect(left, top, right - left, bottom - top); +}; + +const getDirection = (triggerRect: DOMRect, targetRect: DOMRect): Direction => { + const triggerCenterX = triggerRect.left + triggerRect.width / 2; + const triggerCenterY = triggerRect.top + triggerRect.height / 2; + const targetCenterX = targetRect.left + targetRect.width / 2; + const targetCenterY = targetRect.top + targetRect.height / 2; + + const dx = targetCenterX - triggerCenterX; + const dy = targetCenterY - triggerCenterY; + + if (Math.abs(dx) > Math.abs(dy)) { + return dx >= 0 ? 'right' : 'left'; + } + + return dy >= 0 ? 'down' : 'up'; +}; + +const getTargetEdge = (targetRect: DOMRect, direction: Direction): [Point, Point] => { + if (direction === 'right') { + return [ + { x: targetRect.left, y: targetRect.top }, + { x: targetRect.left, y: targetRect.bottom }, + ]; + } + + if (direction === 'left') { + return [ + { x: targetRect.right, y: targetRect.top }, + { x: targetRect.right, y: targetRect.bottom }, + ]; + } + + if (direction === 'down') { + return [ + { x: targetRect.left, y: targetRect.top }, + { x: targetRect.right, y: targetRect.top }, + ]; + } + + return [ + { x: targetRect.left, y: targetRect.bottom }, + { x: targetRect.right, y: targetRect.bottom }, + ]; +}; + +const toLocalPoint = (point: Point, rect: DOMRect): Point => ({ + x: point.x - rect.left, + y: point.y - rect.top, +}); diff --git a/packages/safetriangles/src/index.ts b/packages/safetriangles/src/index.ts new file mode 100644 index 000000000..76864f6e2 --- /dev/null +++ b/packages/safetriangles/src/index.ts @@ -0,0 +1,3 @@ +export { version } from '../package.json'; +export { init } from './init'; +export { ELEMENTS, SETTINGS } from './utils/constants'; diff --git a/packages/safetriangles/src/init.ts b/packages/safetriangles/src/init.ts new file mode 100644 index 000000000..433f7c0e8 --- /dev/null +++ b/packages/safetriangles/src/init.ts @@ -0,0 +1,38 @@ +import { type FinsweetAttributeInit, waitWebflowReady } from '@finsweet/attributes-utils'; + +import { initSafeTriangle } from './factory'; +import { getAttribute, getInstance, hasAttributeValue, queryAllElements, queryElement } from './utils/selectors'; + +/** + * Inits the attribute. + */ +export const init: FinsweetAttributeInit = async () => { + await waitWebflowReady(); + + const triggers = queryAllElements('trigger'); + const cleanups: Array<() => void> = []; + + triggers.forEach((trigger) => { + const instance = getInstance(trigger); + + const target = queryElement('target', { instance }); + if (!target) return; + + const delay = getAttribute(trigger, 'delay') ?? 100; + const debug = hasAttributeValue(trigger, 'debug', 'true'); + + const cleanup = initSafeTriangle(trigger, target, { + delay, + debug, + }); + + cleanups.push(cleanup); + }); + + return { + result: triggers, + destroy() { + for (const cleanup of cleanups) cleanup(); + }, + }; +}; diff --git a/packages/safetriangles/src/utils/constants.ts b/packages/safetriangles/src/utils/constants.ts new file mode 100644 index 000000000..a751ead42 --- /dev/null +++ b/packages/safetriangles/src/utils/constants.ts @@ -0,0 +1,32 @@ +import { type AttributeElements, type AttributeSettings } from '@finsweet/attributes-utils'; + +export const ELEMENTS = [ + /** + * Defines the trigger element for the safe polygon. + */ + 'trigger', + + /** + * Defines the target element for the safe polygon. + */ + 'target', +] as const satisfies AttributeElements; + +export const SETTINGS = { + /** + * Defines a delay in milliseconds before the safe polygon is applied. + */ + delay: { + key: 'delay', + isNumeric: true, + defaultValue: '100', + }, + + /** + * Defines whether to enable debug mode. + */ + debug: { + key: 'debug', + values: ['true'], + }, +} as const satisfies AttributeSettings; diff --git a/packages/safetriangles/src/utils/selectors.ts b/packages/safetriangles/src/utils/selectors.ts new file mode 100644 index 000000000..502efd0b9 --- /dev/null +++ b/packages/safetriangles/src/utils/selectors.ts @@ -0,0 +1,9 @@ +import { generateSelectors, SAFE_POLYGON_ATTRIBUTE } from '@finsweet/attributes-utils'; + +import { ELEMENTS, SETTINGS } from './constants'; + +export const { queryAllElements, queryElement, getInstance, getAttribute, hasAttributeValue } = generateSelectors( + SAFE_POLYGON_ATTRIBUTE, + ELEMENTS, + SETTINGS +); diff --git a/packages/safetriangles/tsconfig.json b/packages/safetriangles/tsconfig.json new file mode 100644 index 000000000..4082f16a5 --- /dev/null +++ b/packages/safetriangles/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/packages/utils/src/constants/attributes.ts b/packages/utils/src/constants/attributes.ts index 3f4871fb6..892060df9 100644 --- a/packages/utils/src/constants/attributes.ts +++ b/packages/utils/src/constants/attributes.ts @@ -36,6 +36,8 @@ export const QUERY_PARAM_ATTRIBUTE = 'queryparam'; export const RANGE_SLIDER_ATTRIBUTE = 'rangeslider'; +export const SAFE_POLYGON_ATTRIBUTE = 'safetriangles'; + export const SCROLL_DISABLE_ATTRIBUTE = 'scrolldisable'; export const SELECT_CUSTOM_ATTRIBUTE = 'selectcustom'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d803e58a..98ec7c475 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,6 +117,9 @@ importers: '@finsweet/attributes-removequery': specifier: workspace:* version: link:../removequery + '@finsweet/attributes-safetriangles': + specifier: workspace:* + version: link:../safetriangles '@finsweet/attributes-scrolldisable': specifier: workspace:* version: link:../scrolldisable @@ -328,6 +331,12 @@ importers: specifier: workspace:* version: link:../utils + packages/safetriangles: + dependencies: + '@finsweet/attributes-utils': + specifier: workspace:* + version: link:../utils + packages/scrolldisable: dependencies: '@finsweet/attributes-utils': @@ -482,8 +491,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.27.2': - resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + '@esbuild/aix-ppc64@0.28.0': + resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -494,8 +503,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.2': - resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + '@esbuild/android-arm64@0.28.0': + resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -506,8 +515,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.2': - resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + '@esbuild/android-arm@0.28.0': + resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -518,8 +527,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.2': - resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + '@esbuild/android-x64@0.28.0': + resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -530,8 +539,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.2': - resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + '@esbuild/darwin-arm64@0.28.0': + resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -542,8 +551,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.2': - resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + '@esbuild/darwin-x64@0.28.0': + resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -554,8 +563,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.2': - resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + '@esbuild/freebsd-arm64@0.28.0': + resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -566,8 +575,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.2': - resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + '@esbuild/freebsd-x64@0.28.0': + resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -578,8 +587,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.2': - resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + '@esbuild/linux-arm64@0.28.0': + resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -590,8 +599,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.2': - resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + '@esbuild/linux-arm@0.28.0': + resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -602,8 +611,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.27.2': - resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + '@esbuild/linux-ia32@0.28.0': + resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -614,8 +623,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.2': - resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + '@esbuild/linux-loong64@0.28.0': + resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -626,8 +635,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.2': - resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + '@esbuild/linux-mips64el@0.28.0': + resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -638,8 +647,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.2': - resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + '@esbuild/linux-ppc64@0.28.0': + resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -650,8 +659,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.2': - resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + '@esbuild/linux-riscv64@0.28.0': + resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -662,8 +671,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.27.2': - resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + '@esbuild/linux-s390x@0.28.0': + resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -674,8 +683,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.2': - resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + '@esbuild/linux-x64@0.28.0': + resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -686,8 +695,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.2': - resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + '@esbuild/netbsd-arm64@0.28.0': + resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -698,8 +707,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.2': - resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + '@esbuild/netbsd-x64@0.28.0': + resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -710,8 +719,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.2': - resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + '@esbuild/openbsd-arm64@0.28.0': + resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -722,14 +731,14 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.2': - resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + '@esbuild/openbsd-x64@0.28.0': + resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.2': - resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + '@esbuild/openharmony-arm64@0.28.0': + resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -740,8 +749,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.2': - resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + '@esbuild/sunos-x64@0.28.0': + resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -752,8 +761,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.2': - resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + '@esbuild/win32-arm64@0.28.0': + resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -764,8 +773,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.2': - resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + '@esbuild/win32-ia32@0.28.0': + resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -776,8 +785,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.27.2': - resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + '@esbuild/win32-x64@0.28.0': + resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1188,8 +1197,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.27.2: - resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + esbuild@0.28.0: + resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==} engines: {node: '>=18'} hasBin: true @@ -2145,154 +2154,154 @@ snapshots: '@esbuild/aix-ppc64@0.25.4': optional: true - '@esbuild/aix-ppc64@0.27.2': + '@esbuild/aix-ppc64@0.28.0': optional: true '@esbuild/android-arm64@0.25.4': optional: true - '@esbuild/android-arm64@0.27.2': + '@esbuild/android-arm64@0.28.0': optional: true '@esbuild/android-arm@0.25.4': optional: true - '@esbuild/android-arm@0.27.2': + '@esbuild/android-arm@0.28.0': optional: true '@esbuild/android-x64@0.25.4': optional: true - '@esbuild/android-x64@0.27.2': + '@esbuild/android-x64@0.28.0': optional: true '@esbuild/darwin-arm64@0.25.4': optional: true - '@esbuild/darwin-arm64@0.27.2': + '@esbuild/darwin-arm64@0.28.0': optional: true '@esbuild/darwin-x64@0.25.4': optional: true - '@esbuild/darwin-x64@0.27.2': + '@esbuild/darwin-x64@0.28.0': optional: true '@esbuild/freebsd-arm64@0.25.4': optional: true - '@esbuild/freebsd-arm64@0.27.2': + '@esbuild/freebsd-arm64@0.28.0': optional: true '@esbuild/freebsd-x64@0.25.4': optional: true - '@esbuild/freebsd-x64@0.27.2': + '@esbuild/freebsd-x64@0.28.0': optional: true '@esbuild/linux-arm64@0.25.4': optional: true - '@esbuild/linux-arm64@0.27.2': + '@esbuild/linux-arm64@0.28.0': optional: true '@esbuild/linux-arm@0.25.4': optional: true - '@esbuild/linux-arm@0.27.2': + '@esbuild/linux-arm@0.28.0': optional: true '@esbuild/linux-ia32@0.25.4': optional: true - '@esbuild/linux-ia32@0.27.2': + '@esbuild/linux-ia32@0.28.0': optional: true '@esbuild/linux-loong64@0.25.4': optional: true - '@esbuild/linux-loong64@0.27.2': + '@esbuild/linux-loong64@0.28.0': optional: true '@esbuild/linux-mips64el@0.25.4': optional: true - '@esbuild/linux-mips64el@0.27.2': + '@esbuild/linux-mips64el@0.28.0': optional: true '@esbuild/linux-ppc64@0.25.4': optional: true - '@esbuild/linux-ppc64@0.27.2': + '@esbuild/linux-ppc64@0.28.0': optional: true '@esbuild/linux-riscv64@0.25.4': optional: true - '@esbuild/linux-riscv64@0.27.2': + '@esbuild/linux-riscv64@0.28.0': optional: true '@esbuild/linux-s390x@0.25.4': optional: true - '@esbuild/linux-s390x@0.27.2': + '@esbuild/linux-s390x@0.28.0': optional: true '@esbuild/linux-x64@0.25.4': optional: true - '@esbuild/linux-x64@0.27.2': + '@esbuild/linux-x64@0.28.0': optional: true '@esbuild/netbsd-arm64@0.25.4': optional: true - '@esbuild/netbsd-arm64@0.27.2': + '@esbuild/netbsd-arm64@0.28.0': optional: true '@esbuild/netbsd-x64@0.25.4': optional: true - '@esbuild/netbsd-x64@0.27.2': + '@esbuild/netbsd-x64@0.28.0': optional: true '@esbuild/openbsd-arm64@0.25.4': optional: true - '@esbuild/openbsd-arm64@0.27.2': + '@esbuild/openbsd-arm64@0.28.0': optional: true '@esbuild/openbsd-x64@0.25.4': optional: true - '@esbuild/openbsd-x64@0.27.2': + '@esbuild/openbsd-x64@0.28.0': optional: true - '@esbuild/openharmony-arm64@0.27.2': + '@esbuild/openharmony-arm64@0.28.0': optional: true '@esbuild/sunos-x64@0.25.4': optional: true - '@esbuild/sunos-x64@0.27.2': + '@esbuild/sunos-x64@0.28.0': optional: true '@esbuild/win32-arm64@0.25.4': optional: true - '@esbuild/win32-arm64@0.27.2': + '@esbuild/win32-arm64@0.28.0': optional: true '@esbuild/win32-ia32@0.25.4': optional: true - '@esbuild/win32-ia32@0.27.2': + '@esbuild/win32-ia32@0.28.0': optional: true '@esbuild/win32-x64@0.25.4': optional: true - '@esbuild/win32-x64@0.27.2': + '@esbuild/win32-x64@0.28.0': optional: true '@eslint-community/eslint-utils@4.7.0(eslint@9.26.0)': @@ -2745,7 +2754,7 @@ snapshots: esbuild-plugin-inline-worker@0.1.1: dependencies: - esbuild: 0.27.2 + esbuild: 0.28.0 find-cache-dir: 3.3.2 esbuild@0.25.4: @@ -2776,34 +2785,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.4 '@esbuild/win32-x64': 0.25.4 - esbuild@0.27.2: + esbuild@0.28.0: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.2 - '@esbuild/android-arm': 0.27.2 - '@esbuild/android-arm64': 0.27.2 - '@esbuild/android-x64': 0.27.2 - '@esbuild/darwin-arm64': 0.27.2 - '@esbuild/darwin-x64': 0.27.2 - '@esbuild/freebsd-arm64': 0.27.2 - '@esbuild/freebsd-x64': 0.27.2 - '@esbuild/linux-arm': 0.27.2 - '@esbuild/linux-arm64': 0.27.2 - '@esbuild/linux-ia32': 0.27.2 - '@esbuild/linux-loong64': 0.27.2 - '@esbuild/linux-mips64el': 0.27.2 - '@esbuild/linux-ppc64': 0.27.2 - '@esbuild/linux-riscv64': 0.27.2 - '@esbuild/linux-s390x': 0.27.2 - '@esbuild/linux-x64': 0.27.2 - '@esbuild/netbsd-arm64': 0.27.2 - '@esbuild/netbsd-x64': 0.27.2 - '@esbuild/openbsd-arm64': 0.27.2 - '@esbuild/openbsd-x64': 0.27.2 - '@esbuild/openharmony-arm64': 0.27.2 - '@esbuild/sunos-x64': 0.27.2 - '@esbuild/win32-arm64': 0.27.2 - '@esbuild/win32-ia32': 0.27.2 - '@esbuild/win32-x64': 0.27.2 + '@esbuild/aix-ppc64': 0.28.0 + '@esbuild/android-arm': 0.28.0 + '@esbuild/android-arm64': 0.28.0 + '@esbuild/android-x64': 0.28.0 + '@esbuild/darwin-arm64': 0.28.0 + '@esbuild/darwin-x64': 0.28.0 + '@esbuild/freebsd-arm64': 0.28.0 + '@esbuild/freebsd-x64': 0.28.0 + '@esbuild/linux-arm': 0.28.0 + '@esbuild/linux-arm64': 0.28.0 + '@esbuild/linux-ia32': 0.28.0 + '@esbuild/linux-loong64': 0.28.0 + '@esbuild/linux-mips64el': 0.28.0 + '@esbuild/linux-ppc64': 0.28.0 + '@esbuild/linux-riscv64': 0.28.0 + '@esbuild/linux-s390x': 0.28.0 + '@esbuild/linux-x64': 0.28.0 + '@esbuild/netbsd-arm64': 0.28.0 + '@esbuild/netbsd-x64': 0.28.0 + '@esbuild/openbsd-arm64': 0.28.0 + '@esbuild/openbsd-x64': 0.28.0 + '@esbuild/openharmony-arm64': 0.28.0 + '@esbuild/sunos-x64': 0.28.0 + '@esbuild/win32-arm64': 0.28.0 + '@esbuild/win32-ia32': 0.28.0 + '@esbuild/win32-x64': 0.28.0 escape-html@1.0.3: {} From e9edf71c401940a09238879a8d788f7c6bbf65a7 Mon Sep 17 00:00:00 2001 From: Alex Iglesias Date: Sun, 24 May 2026 19:43:48 +0200 Subject: [PATCH 2/4] Update factory.ts --- packages/safetriangles/src/factory.ts | 62 ++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/packages/safetriangles/src/factory.ts b/packages/safetriangles/src/factory.ts index 0c4ea19ed..59f33d9ce 100644 --- a/packages/safetriangles/src/factory.ts +++ b/packages/safetriangles/src/factory.ts @@ -33,6 +33,7 @@ export const initSafeTriangle = (trigger: HTMLElement, target: HTMLElement, { de svg.style.pointerEvents = 'none'; svg.style.inset = 'auto'; svg.style.overflow = 'visible'; + svg.style.visibility = 'hidden'; svg.dataset.fsSafeTriangles = 'svg'; const path = document.createElementNS(SVG_NS, 'path'); @@ -45,6 +46,16 @@ export const initSafeTriangle = (trigger: HTMLElement, target: HTMLElement, { de trigger.appendChild(svg); let rects = getRects(trigger, target); + const pendingTimeouts = new Set>(); + + const showTriangle = () => { + svg.style.visibility = 'visible'; + }; + + const hideTriangle = () => { + svg.style.visibility = 'hidden'; + path.removeAttribute('d'); + }; const updateBounds = () => { rects = getRects(trigger, target); @@ -68,6 +79,7 @@ export const initSafeTriangle = (trigger: HTMLElement, target: HTMLElement, { de const handleMouseEnter = () => { updateBounds(); + showTriangle(); if (delay <= 0) { const { triggerRect } = rects; @@ -76,12 +88,47 @@ export const initSafeTriangle = (trigger: HTMLElement, target: HTMLElement, { de }; const handleMouseMove = (event: MouseEvent) => { - window.setTimeout( + showTriangle(); + + const timeoutId = window.setTimeout( () => { + pendingTimeouts.delete(timeoutId); updateTriangle({ x: event.clientX, y: event.clientY }); }, Math.max(0, delay) ); + + pendingTimeouts.add(timeoutId); + }; + + const handleTriggerMouseLeave = (event: MouseEvent) => { + const nextHoveredElement = event.relatedTarget; + + if ( + nextHoveredElement instanceof Node && + (trigger.contains(nextHoveredElement) || target.contains(nextHoveredElement)) + ) { + return; + } + + hideTriangle(); + }; + + const handleTargetMouseEnter = () => { + hideTriangle(); + }; + + const handleTargetMouseLeave = (event: MouseEvent) => { + const nextHoveredElement = event.relatedTarget; + + if ( + nextHoveredElement instanceof Node && + (trigger.contains(nextHoveredElement) || target.contains(nextHoveredElement)) + ) { + return; + } + + hideTriangle(); }; const handleWindowResize = () => { @@ -90,15 +137,28 @@ export const initSafeTriangle = (trigger: HTMLElement, target: HTMLElement, { de trigger.addEventListener('mouseenter', handleMouseEnter); trigger.addEventListener('mousemove', handleMouseMove); + trigger.addEventListener('mouseleave', handleTriggerMouseLeave); + target.addEventListener('mouseenter', handleTargetMouseEnter); + target.addEventListener('mouseleave', handleTargetMouseLeave); window.addEventListener('resize', handleWindowResize); updateBounds(); + hideTriangle(); return () => { trigger.removeEventListener('mouseenter', handleMouseEnter); trigger.removeEventListener('mousemove', handleMouseMove); + trigger.removeEventListener('mouseleave', handleTriggerMouseLeave); + target.removeEventListener('mouseenter', handleTargetMouseEnter); + target.removeEventListener('mouseleave', handleTargetMouseLeave); window.removeEventListener('resize', handleWindowResize); + for (const timeoutId of pendingTimeouts) { + window.clearTimeout(timeoutId); + } + + pendingTimeouts.clear(); + svg.remove(); if (previousInlinePosition) { From fad860e4786dafec9e116f0c88cbca5d8d6de550 Mon Sep 17 00:00:00 2001 From: Alex Iglesias Date: Sun, 24 May 2026 19:47:01 +0200 Subject: [PATCH 3/4] Update factory.ts --- packages/safetriangles/src/factory.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/safetriangles/src/factory.ts b/packages/safetriangles/src/factory.ts index 59f33d9ce..ffa722926 100644 --- a/packages/safetriangles/src/factory.ts +++ b/packages/safetriangles/src/factory.ts @@ -46,7 +46,6 @@ export const initSafeTriangle = (trigger: HTMLElement, target: HTMLElement, { de trigger.appendChild(svg); let rects = getRects(trigger, target); - const pendingTimeouts = new Set>(); const showTriangle = () => { svg.style.visibility = 'visible'; @@ -90,15 +89,12 @@ export const initSafeTriangle = (trigger: HTMLElement, target: HTMLElement, { de const handleMouseMove = (event: MouseEvent) => { showTriangle(); - const timeoutId = window.setTimeout( + window.setTimeout( () => { - pendingTimeouts.delete(timeoutId); updateTriangle({ x: event.clientX, y: event.clientY }); }, Math.max(0, delay) ); - - pendingTimeouts.add(timeoutId); }; const handleTriggerMouseLeave = (event: MouseEvent) => { @@ -153,12 +149,6 @@ export const initSafeTriangle = (trigger: HTMLElement, target: HTMLElement, { de target.removeEventListener('mouseleave', handleTargetMouseLeave); window.removeEventListener('resize', handleWindowResize); - for (const timeoutId of pendingTimeouts) { - window.clearTimeout(timeoutId); - } - - pendingTimeouts.clear(); - svg.remove(); if (previousInlinePosition) { From debef2956d987e48faf18a9fe91910829479cdfb Mon Sep 17 00:00:00 2001 From: Alex Iglesias Date: Sun, 24 May 2026 19:51:35 +0200 Subject: [PATCH 4/4] fixes --- packages/safetriangles/src/utils/selectors.ts | 4 ++-- packages/utils/src/constants/attributes.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/safetriangles/src/utils/selectors.ts b/packages/safetriangles/src/utils/selectors.ts index 502efd0b9..916c60627 100644 --- a/packages/safetriangles/src/utils/selectors.ts +++ b/packages/safetriangles/src/utils/selectors.ts @@ -1,9 +1,9 @@ -import { generateSelectors, SAFE_POLYGON_ATTRIBUTE } from '@finsweet/attributes-utils'; +import { generateSelectors, SAFE_TRIANGLES_ATTRIBUTE } from '@finsweet/attributes-utils'; import { ELEMENTS, SETTINGS } from './constants'; export const { queryAllElements, queryElement, getInstance, getAttribute, hasAttributeValue } = generateSelectors( - SAFE_POLYGON_ATTRIBUTE, + SAFE_TRIANGLES_ATTRIBUTE, ELEMENTS, SETTINGS ); diff --git a/packages/utils/src/constants/attributes.ts b/packages/utils/src/constants/attributes.ts index 892060df9..79930fb1f 100644 --- a/packages/utils/src/constants/attributes.ts +++ b/packages/utils/src/constants/attributes.ts @@ -36,7 +36,7 @@ export const QUERY_PARAM_ATTRIBUTE = 'queryparam'; export const RANGE_SLIDER_ATTRIBUTE = 'rangeslider'; -export const SAFE_POLYGON_ATTRIBUTE = 'safetriangles'; +export const SAFE_TRIANGLES_ATTRIBUTE = 'safetriangles'; export const SCROLL_DISABLE_ATTRIBUTE = 'scrolldisable';