@thednp/shorty 2.0.3 → 2.0.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/README.md +7 -5
- package/dist/shorty.cjs +17 -1
- package/dist/shorty.cjs.map +1 -1
- package/dist/shorty.d.ts +20 -8
- package/dist/shorty.js +17 -1
- package/dist/shorty.js.map +1 -1
- package/dist/shorty.mjs +423 -379
- package/dist/shorty.mjs.map +1 -1
- package/dts.config.ts +1 -7
- package/package.json +16 -16
- package/src/boolean/isApple.ts +1 -1
- package/src/boolean/isFirefox.ts +3 -1
- package/src/boolean/isMobile.ts +1 -1
- package/src/boolean/supportPassive.ts +1 -1
- package/src/event/one.ts +1 -1
- package/src/get/getRectRelativeToOffsetParent.ts +1 -1
- package/src/index.ts +6 -0
- package/src/misc/createCustomEvent.ts +5 -5
- package/src/misc/createElementNS.ts +1 -1
- package/src/misc/data.ts +2 -5
- package/src/misc/emulateAnimationEnd.ts +4 -3
- package/src/misc/emulateTransitionEnd.ts +3 -3
- package/src/misc/focusTrap.ts +64 -0
- package/src/misc/normalizeOptions.ts +2 -2
- package/src/misc/timer.ts +3 -3
- package/src/selectors/closest.ts +5 -2
- package/src/selectors/getCustomElements.ts +1 -2
- package/src/selectors/getElementById.ts +2 -2
- package/src/selectors/getElementsByClassName.ts +2 -7
- package/src/selectors/getElementsByTagName.ts +2 -5
- package/src/selectors/querySelector.ts +6 -3
- package/src/selectors/querySelectorAll.ts +4 -1
- package/src/strings/focusableSelector.ts +4 -0
- package/test/att.test.ts +43 -0
- package/test/boolean.test.ts +30 -0
- package/test/class.test.ts +26 -0
- package/test/event.test.ts +39 -0
- package/{cypress/test.html → test/fixtures/getExampleDom.ts} +21 -32
- package/test/fixtures/style.css +18 -0
- package/test/get.test.ts +150 -0
- package/{cypress/e2e/is.cy.ts → test/is.test.ts} +77 -70
- package/test/misc.test.ts +400 -0
- package/test/selectors.test.ts +90 -0
- package/tsconfig.json +6 -1
- package/{vite.config.ts → vite.config.mts} +3 -8
- package/vitest.config-ui.mts +21 -0
- package/vitest.config.mts +20 -0
- package/cypress/e2e/att.cy.ts +0 -46
- package/cypress/e2e/boolean.cy.ts +0 -44
- package/cypress/e2e/class.cy.ts +0 -28
- package/cypress/e2e/event.cy.ts +0 -51
- package/cypress/e2e/get.cy.ts +0 -168
- package/cypress/e2e/misc.cy.ts +0 -354
- package/cypress/e2e/selectors.cy.ts +0 -85
- package/cypress/plugins/esbuild-istanbul.ts +0 -50
- package/cypress/plugins/tsCompile.ts +0 -34
- package/cypress/support/commands.ts +0 -37
- package/cypress/support/e2e.ts +0 -21
- package/cypress/support/index.js +0 -22
- package/cypress.config.ts +0 -30
- /package/{cypress → test}/fixtures/custom-elem.js +0 -0
package/dts.config.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
const packageJson = require("./package.json");
|
|
2
|
-
|
|
3
|
-
const getPackageName = () => {
|
|
4
|
-
return packageJson.name.includes('@') ? packageJson.name.split('/')[1] : packageJson.name;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
1
|
const config = {
|
|
8
2
|
entries: [
|
|
9
3
|
{
|
|
10
4
|
filePath: "./src/index.ts",
|
|
11
|
-
outFile: `./dist
|
|
5
|
+
outFile: `./dist/shorty.d.ts`,
|
|
12
6
|
noCheck: false,
|
|
13
7
|
output: {
|
|
14
8
|
exportReferencedTypes: false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thednp/shorty",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "TypeScript shorties for the web",
|
|
5
5
|
"source": "./src/index.ts",
|
|
6
6
|
"main": "./dist/shorty.js",
|
|
@@ -32,25 +32,25 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/thednp/shorty",
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@
|
|
36
|
-
"@cypress/code-coverage": "^3.12.44",
|
|
37
|
-
"@types/istanbul-lib-instrument": "^1.7.7",
|
|
38
|
-
"@types/node": "^20.14.11",
|
|
35
|
+
"@types/node": "^20.16.15",
|
|
39
36
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
40
37
|
"@typescript-eslint/parser": "^5.62.0",
|
|
41
|
-
"
|
|
38
|
+
"@vitest/browser": "^2.1.3",
|
|
39
|
+
"@vitest/coverage-istanbul": "^2.1.3",
|
|
40
|
+
"@vitest/ui": "^2.1.3",
|
|
42
41
|
"dts-bundle-generator": "^9.5.1",
|
|
43
|
-
"eslint": "^8.57.
|
|
42
|
+
"eslint": "^8.57.1",
|
|
44
43
|
"eslint-plugin-jsdoc": "^46.10.1",
|
|
45
44
|
"eslint-plugin-prefer-arrow": "^1.2.3",
|
|
46
45
|
"eslint-plugin-prettier": "^4.2.1",
|
|
47
46
|
"istanbul-lib-coverage": "^3.2.2",
|
|
48
47
|
"istanbul-lib-instrument": "^5.2.1",
|
|
49
48
|
"nyc": "^15.1.0",
|
|
49
|
+
"playwright": "^1.48.1",
|
|
50
50
|
"prettier": "^2.8.8",
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
51
|
+
"typescript": "^5.6.3",
|
|
52
|
+
"vite": "^5.4.10",
|
|
53
|
+
"vitest": "^2.1.3"
|
|
54
54
|
},
|
|
55
55
|
"engines": {
|
|
56
56
|
"node": ">=16",
|
|
@@ -58,16 +58,16 @@
|
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"pre-test": "pnpm clean-coverage",
|
|
61
|
-
"badges": "npx -p dependency-version-badge update-badge typescript prettier
|
|
62
|
-
"test": "pnpm pre-test &&
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"coverage:report": "nyc report --reporter=lcov --reporter=json --reporter=text --reporter=json-summary",
|
|
61
|
+
"badges": "npx -p dependency-version-badge update-badge typescript prettier eslint vitest vite",
|
|
62
|
+
"test": "pnpm pre-test && vitest --config vitest.config.mts",
|
|
63
|
+
"test-ui": "pnpm pre-test && vitest --config vitest.config-ui.mts",
|
|
64
|
+
"clean-coverage": "rm -rf coverage .nyc_output",
|
|
66
65
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
66
|
+
"lint": "pnpm lint:ts && pnpm check:ts",
|
|
67
67
|
"lint:ts": "eslint -c .eslintrc.cjs --ext .ts src",
|
|
68
68
|
"fix:ts": "eslint -c .eslintrc.cjs --ext .ts src --fix",
|
|
69
69
|
"check:ts": "tsc -noEmit",
|
|
70
|
-
"build": "
|
|
70
|
+
"build": "vite build && pnpm dts",
|
|
71
71
|
"dts": "dts-bundle-generator --config ./dts.config.ts"
|
|
72
72
|
}
|
|
73
73
|
}
|
package/src/boolean/isApple.ts
CHANGED
|
@@ -9,6 +9,6 @@ const appleBrands = /(iPhone|iPod|iPad)/;
|
|
|
9
9
|
*/
|
|
10
10
|
const isApple: boolean = userAgentData
|
|
11
11
|
? userAgentData.brands.some((x: NavigatorUABrand) => appleBrands.test(x.brand))
|
|
12
|
-
: /* istanbul ignore next */ appleBrands.test(userAgent);
|
|
12
|
+
: /* istanbul ignore next @preserve */ appleBrands.test(userAgent);
|
|
13
13
|
|
|
14
14
|
export default isApple;
|
package/src/boolean/isFirefox.ts
CHANGED
|
@@ -4,6 +4,8 @@ import userAgent from '../strings/userAgent';
|
|
|
4
4
|
* A global boolean for Gecko browsers. When writing this file,
|
|
5
5
|
* Gecko was not supporting `userAgentData`.
|
|
6
6
|
*/
|
|
7
|
-
const isFirefox = userAgent
|
|
7
|
+
const isFirefox = userAgent
|
|
8
|
+
? userAgent.includes('Firefox')
|
|
9
|
+
: /* istanbul ignore next @preserve */ false;
|
|
8
10
|
|
|
9
11
|
export default isFirefox;
|
package/src/boolean/isMobile.ts
CHANGED
|
@@ -4,7 +4,7 @@ import userAgent from '../strings/userAgent';
|
|
|
4
4
|
const mobileBrands = /iPhone|iPad|iPod|Android/i;
|
|
5
5
|
let isMobileCheck = false;
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// istanbul ignore else @preserve
|
|
8
8
|
if (userAgentData) {
|
|
9
9
|
isMobileCheck = userAgentData.brands.some(x => mobileBrands.test(x.brand));
|
|
10
10
|
} else {
|
package/src/event/one.ts
CHANGED
|
@@ -14,7 +14,7 @@ const one = <T extends EventTarget, L = EventListener>(
|
|
|
14
14
|
) => {
|
|
15
15
|
/** Wrap the listener for easy on -> off */
|
|
16
16
|
const handlerWrapper: NativeEventHandler<T> = e => {
|
|
17
|
-
/* istanbul ignore else */
|
|
17
|
+
/* istanbul ignore else @preserve */
|
|
18
18
|
if (e.target === element || e.currentTarget === element) {
|
|
19
19
|
(listener as NativeEventHandler<T>).apply(element, [e]);
|
|
20
20
|
off(element, eventName, handlerWrapper, options);
|
|
@@ -22,7 +22,7 @@ const getRectRelativeToOffsetParent = (
|
|
|
22
22
|
const rect = getBoundingClientRect(element, isParentAnElement && isScaledElement(offsetParent));
|
|
23
23
|
const offsets = { x: 0, y: 0 };
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
// istanbul ignore else @preserve
|
|
26
26
|
if (isParentAnElement) {
|
|
27
27
|
const offsetRect = getBoundingClientRect(offsetParent, true);
|
|
28
28
|
offsets.x = offsetRect.x + offsetParent.clientLeft;
|
package/src/index.ts
CHANGED
|
@@ -77,6 +77,7 @@ import focusEvents from './strings/focusEvents';
|
|
|
77
77
|
import focusEvent from './strings/focusEvent';
|
|
78
78
|
import focusinEvent from './strings/focusinEvent';
|
|
79
79
|
import focusoutEvent from './strings/focusoutEvent';
|
|
80
|
+
import focusableSelector from './strings/focusableSelector';
|
|
80
81
|
|
|
81
82
|
import gesturechangeEvent from './strings/gesturechangeEvent';
|
|
82
83
|
import gestureendEvent from './strings/gestureendEvent';
|
|
@@ -194,6 +195,7 @@ import setElementStyle from './misc/setElementStyle';
|
|
|
194
195
|
import Timer from './misc/timer';
|
|
195
196
|
import toLowerCase from './misc/toLowerCase';
|
|
196
197
|
import toUpperCase from './misc/toUpperCase';
|
|
198
|
+
import { type FocusableElement, hasFocusTrap, toggleFocusTrap } from './misc/focusTrap';
|
|
197
199
|
|
|
198
200
|
// get
|
|
199
201
|
import getBoundingClientRect from './get/getBoundingClientRect';
|
|
@@ -397,6 +399,9 @@ export {
|
|
|
397
399
|
createCustomEvent,
|
|
398
400
|
toUpperCase,
|
|
399
401
|
toLowerCase,
|
|
402
|
+
focusableSelector,
|
|
403
|
+
hasFocusTrap,
|
|
404
|
+
toggleFocusTrap,
|
|
400
405
|
Timer,
|
|
401
406
|
emulateAnimationEnd,
|
|
402
407
|
emulateTransitionEnd,
|
|
@@ -480,6 +485,7 @@ export {
|
|
|
480
485
|
export * from './interface/navigatorUA.d';
|
|
481
486
|
export * from './interface/offsetRect.d';
|
|
482
487
|
|
|
488
|
+
export type { FocusableElement };
|
|
483
489
|
export type { OriginalEvent } from './interface/originalEvent.d';
|
|
484
490
|
|
|
485
491
|
export type { BoundingClientRect } from './interface/boundingClientRect.d';
|
|
@@ -9,16 +9,16 @@ import ObjectAssign from './ObjectAssign';
|
|
|
9
9
|
* @param config Event.options | Event.properties
|
|
10
10
|
* @returns a new namespaced event
|
|
11
11
|
*/
|
|
12
|
-
const createCustomEvent = <T extends OriginalEvent>(
|
|
12
|
+
const createCustomEvent = <O extends unknown & Record<string, unknown>, T extends OriginalEvent>(
|
|
13
13
|
eventType: string,
|
|
14
|
-
config?:
|
|
14
|
+
config?: O,
|
|
15
15
|
): T => {
|
|
16
|
-
const OriginalCustomEvent = new CustomEvent<
|
|
16
|
+
const OriginalCustomEvent = new CustomEvent<O>(eventType, {
|
|
17
17
|
cancelable: true,
|
|
18
18
|
bubbles: true,
|
|
19
|
-
}
|
|
19
|
+
}) as T;
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
// istanbul ignore else @preserve
|
|
22
22
|
if (isObject(config)) {
|
|
23
23
|
ObjectAssign(OriginalCustomEvent, config);
|
|
24
24
|
}
|
|
@@ -16,7 +16,7 @@ import isString from '../is/isString';
|
|
|
16
16
|
* @param param `tagName` or object
|
|
17
17
|
* @return a new `HTMLElement`
|
|
18
18
|
*/
|
|
19
|
-
const createElementNS = <T extends HTMLElement>(
|
|
19
|
+
const createElementNS = <T extends Element = HTMLElement>(
|
|
20
20
|
ns: string,
|
|
21
21
|
param?: string | Partial<T>,
|
|
22
22
|
): T | undefined => {
|
package/src/misc/data.ts
CHANGED
|
@@ -19,7 +19,7 @@ const Data = {
|
|
|
19
19
|
set: <T>(element: HTMLElement, component: string, instance: T): void => {
|
|
20
20
|
if (!isHTMLElement(element)) return;
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// istanbul ignore else @preserve
|
|
23
23
|
if (!componentData.has(component)) {
|
|
24
24
|
componentData.set(component, new Map<HTMLElement, T>());
|
|
25
25
|
}
|
|
@@ -51,11 +51,9 @@ const Data = {
|
|
|
51
51
|
get: <T>(element: HTMLElement, component: string): T | null => {
|
|
52
52
|
if (!isHTMLElement(element) || !component) return null;
|
|
53
53
|
const instanceMap = Data.getAllFor<T>(component);
|
|
54
|
-
// const instanceMap = componentData.get(component) as Map<HTMLElement, InstanceType<T>>;
|
|
55
54
|
|
|
56
55
|
const instance = element && instanceMap && instanceMap.get(element);
|
|
57
56
|
|
|
58
|
-
// return (instance as T) || null;
|
|
59
57
|
return instance || null;
|
|
60
58
|
},
|
|
61
59
|
|
|
@@ -67,13 +65,12 @@ const Data = {
|
|
|
67
65
|
*/
|
|
68
66
|
remove: <T>(element: HTMLElement, component: string): void => {
|
|
69
67
|
const instanceMap = Data.getAllFor<T>(component);
|
|
70
|
-
// const instanceMap = componentData.get(component) as Map<HTMLElement, InstanceType<T>>;
|
|
71
68
|
|
|
72
69
|
if (!instanceMap || !isHTMLElement(element)) return;
|
|
73
70
|
|
|
74
71
|
instanceMap.delete(element);
|
|
75
72
|
|
|
76
|
-
|
|
73
|
+
// istanbul ignore else @preserve
|
|
77
74
|
if (instanceMap.size === 0) {
|
|
78
75
|
componentData.delete(component);
|
|
79
76
|
}
|
|
@@ -17,9 +17,9 @@ const emulateAnimationEnd = (element: HTMLElement, handler: EventListener): void
|
|
|
17
17
|
const delay = getElementAnimationDelay(element);
|
|
18
18
|
|
|
19
19
|
if (duration) {
|
|
20
|
-
|
|
20
|
+
// Wrap the handler in on -> off callback
|
|
21
21
|
const animationEndWrapper = (e: Event): void => {
|
|
22
|
-
|
|
22
|
+
// istanbul ignore else @preserve
|
|
23
23
|
if (e.target === element) {
|
|
24
24
|
handler.apply(element, [e]);
|
|
25
25
|
element.removeEventListener(animationEndEvent, animationEndWrapper);
|
|
@@ -28,10 +28,11 @@ const emulateAnimationEnd = (element: HTMLElement, handler: EventListener): void
|
|
|
28
28
|
};
|
|
29
29
|
element.addEventListener(animationEndEvent, animationEndWrapper);
|
|
30
30
|
setTimeout(() => {
|
|
31
|
-
|
|
31
|
+
// istanbul ignore next @preserve
|
|
32
32
|
if (!called) dispatchEvent(element, endEvent);
|
|
33
33
|
}, duration + delay + 17);
|
|
34
34
|
} else {
|
|
35
|
+
// istanbul ignore next @preserve
|
|
35
36
|
handler.apply(element, [endEvent]);
|
|
36
37
|
}
|
|
37
38
|
};
|
|
@@ -17,9 +17,9 @@ const emulateTransitionEnd = (element: HTMLElement, handler: EventListener): voi
|
|
|
17
17
|
const delay = getElementTransitionDelay(element);
|
|
18
18
|
|
|
19
19
|
if (duration) {
|
|
20
|
-
|
|
20
|
+
// Wrap the handler in on -> off callback
|
|
21
21
|
const transitionEndWrapper = (e: Event): void => {
|
|
22
|
-
|
|
22
|
+
// istanbul ignore else @preserve
|
|
23
23
|
if (e.target === element) {
|
|
24
24
|
handler.apply(element, [e]);
|
|
25
25
|
element.removeEventListener(transitionEndEvent, transitionEndWrapper);
|
|
@@ -28,7 +28,7 @@ const emulateTransitionEnd = (element: HTMLElement, handler: EventListener): voi
|
|
|
28
28
|
};
|
|
29
29
|
element.addEventListener(transitionEndEvent, transitionEndWrapper);
|
|
30
30
|
setTimeout(() => {
|
|
31
|
-
|
|
31
|
+
// istanbul ignore next @preserve
|
|
32
32
|
if (!called) dispatchEvent(element, endEvent);
|
|
33
33
|
}, duration + delay + 17);
|
|
34
34
|
} else {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import ariaHidden from '../strings/ariaHidden';
|
|
2
|
+
import focusableSelector from '../strings/focusableSelector';
|
|
3
|
+
import querySelectorAll from '../selectors/querySelectorAll';
|
|
4
|
+
import getAttribute from '../attr/getAttribute';
|
|
5
|
+
import hasAttribute from '../attr/hasAttribute';
|
|
6
|
+
import off from '../event/off';
|
|
7
|
+
import on from '../event/on';
|
|
8
|
+
import getDocument from '../get/getDocument';
|
|
9
|
+
import { KeyboardEvent } from '../interface/event';
|
|
10
|
+
|
|
11
|
+
const focusTrapMap = new Map<HTMLElement, boolean>();
|
|
12
|
+
|
|
13
|
+
export type FocusableElement =
|
|
14
|
+
| HTMLAnchorElement
|
|
15
|
+
| HTMLButtonElement
|
|
16
|
+
| HTMLInputElement
|
|
17
|
+
| HTMLTextAreaElement
|
|
18
|
+
| HTMLDataListElement
|
|
19
|
+
| HTMLDetailsElement
|
|
20
|
+
| HTMLSelectElement;
|
|
21
|
+
|
|
22
|
+
function handleKeyboardNavigation<T extends HTMLElement & EventTarget>(
|
|
23
|
+
this: T,
|
|
24
|
+
event: KeyboardEvent<T>,
|
|
25
|
+
) {
|
|
26
|
+
const { shiftKey, code } = event;
|
|
27
|
+
const doc = getDocument(this);
|
|
28
|
+
const focusableElements = [...querySelectorAll<FocusableElement>(focusableSelector, this)].filter(
|
|
29
|
+
el => !hasAttribute(el, 'disabled') && !getAttribute(el, ariaHidden),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (!focusableElements.length) return;
|
|
33
|
+
const firstFocusable = focusableElements[0];
|
|
34
|
+
const lastFocusable = focusableElements[focusableElements.length - 1];
|
|
35
|
+
|
|
36
|
+
// istanbul ignore else @preserve
|
|
37
|
+
if (code === 'Tab') {
|
|
38
|
+
if (shiftKey && doc.activeElement === firstFocusable) {
|
|
39
|
+
lastFocusable.focus();
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
} else if (!shiftKey && doc.activeElement === lastFocusable) {
|
|
42
|
+
firstFocusable.focus();
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Utility to check if a designated element is affected by focus trap;
|
|
50
|
+
* @param target
|
|
51
|
+
*/
|
|
52
|
+
export const hasFocusTrap = (target: HTMLElement) => focusTrapMap.has(target) === true;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Utility to toggle focus trap inside a designated target element;
|
|
56
|
+
* @param target
|
|
57
|
+
*/
|
|
58
|
+
export const toggleFocusTrap = (target: HTMLElement) => {
|
|
59
|
+
const isCurrentlyTrapped = hasFocusTrap(target);
|
|
60
|
+
const action = !isCurrentlyTrapped ? on : off;
|
|
61
|
+
action(target, 'keydown', handleKeyboardNavigation);
|
|
62
|
+
if (isCurrentlyTrapped) focusTrapMap.delete(target);
|
|
63
|
+
else focusTrapMap.set(target, true);
|
|
64
|
+
};
|
|
@@ -29,7 +29,7 @@ const normalizeOptions = <T extends { [key: string]: any }>(
|
|
|
29
29
|
const key: keyof T =
|
|
30
30
|
ns && typeof k === 'string' && k.includes(ns)
|
|
31
31
|
? k.replace(ns, '').replace(/[A-Z]/g, (match: string) => toLowerCase(match))
|
|
32
|
-
: k;
|
|
32
|
+
: /* istanbul ignore next @preserve */ k;
|
|
33
33
|
|
|
34
34
|
dataOps[key] = normalizeValue(v) as T[keyof T];
|
|
35
35
|
});
|
|
@@ -39,7 +39,7 @@ const normalizeOptions = <T extends { [key: string]: any }>(
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
ObjectEntries(defaultOps).forEach(([k, v]) => {
|
|
42
|
-
|
|
42
|
+
// istanbul ignore else @preserve
|
|
43
43
|
if (k in INPUT) {
|
|
44
44
|
normalOps[k] = INPUT[k] as T[keyof T];
|
|
45
45
|
} else if (k in dataOps) {
|
package/src/misc/timer.ts
CHANGED
|
@@ -23,9 +23,9 @@ const Timer = {
|
|
|
23
23
|
set: (element: HTMLElement, callback: TimerHandler, delay: number, key?: string): void => {
|
|
24
24
|
if (!isHTMLElement(element)) return;
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
// istanbul ignore else @preserve
|
|
27
27
|
if (key && key.length) {
|
|
28
|
-
|
|
28
|
+
// istanbul ignore else @preserve
|
|
29
29
|
if (!TimeCache.has(element)) {
|
|
30
30
|
TimeCache.set(element, new Map());
|
|
31
31
|
}
|
|
@@ -70,7 +70,7 @@ const Timer = {
|
|
|
70
70
|
if (key && key.length && isMap(keyTimers as KeyMap)) {
|
|
71
71
|
clearTimeout((keyTimers as KeyMap).get(key));
|
|
72
72
|
(keyTimers as KeyMap).delete(key);
|
|
73
|
-
|
|
73
|
+
// istanbul ignore else @preserve
|
|
74
74
|
if ((keyTimers as KeyMap).size === 0) {
|
|
75
75
|
TimeCache.delete(element);
|
|
76
76
|
}
|
package/src/selectors/closest.ts
CHANGED
|
@@ -9,11 +9,14 @@
|
|
|
9
9
|
* @param selector the selector name
|
|
10
10
|
* @return the query result
|
|
11
11
|
*/
|
|
12
|
-
const closest =
|
|
12
|
+
const closest = <T extends Element = HTMLElement>(
|
|
13
|
+
element: T,
|
|
14
|
+
selector: string,
|
|
15
|
+
): HTMLElement | null => {
|
|
13
16
|
return element
|
|
14
17
|
? element.closest(selector) ||
|
|
15
18
|
// break out of `ShadowRoot`
|
|
16
|
-
closest((element.getRootNode() as
|
|
19
|
+
closest((element.getRootNode() as ShadowRoot).host, selector)
|
|
17
20
|
: null;
|
|
18
21
|
};
|
|
19
22
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import isCustomElement from '../is/isCustomElement';
|
|
2
2
|
import getElementsByTagName from './getElementsByTagName';
|
|
3
|
-
import type { CustomElement } from '../interface/customElement';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Returns an `Array` of `Node` elements that are registered as
|
|
@@ -11,7 +10,7 @@ import type { CustomElement } from '../interface/customElement';
|
|
|
11
10
|
* @param parent parent to look into
|
|
12
11
|
* @returns the query result
|
|
13
12
|
*/
|
|
14
|
-
const getCustomElements = (parent?: ParentNode)
|
|
13
|
+
const getCustomElements = (parent?: ParentNode) => {
|
|
15
14
|
const collection = getElementsByTagName('*', parent);
|
|
16
15
|
|
|
17
16
|
return [...collection].filter(isCustomElement);
|
|
@@ -9,8 +9,8 @@ import getDocument from '../get/getDocument';
|
|
|
9
9
|
* @param context an element in it's document or document
|
|
10
10
|
* @returns the requested element
|
|
11
11
|
*/
|
|
12
|
-
const getElementById = (id: string, context?: Node)
|
|
13
|
-
return getDocument(context).getElementById(id) || null;
|
|
12
|
+
const getElementById = <T extends HTMLElement>(id: string, context?: Node) => {
|
|
13
|
+
return (getDocument(context).getElementById(id) as T) || null;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
export default getElementById;
|
|
@@ -9,14 +9,9 @@ import isNode from '../is/isNode';
|
|
|
9
9
|
* @param parent optional Element to look into
|
|
10
10
|
* @return the 'HTMLCollection'
|
|
11
11
|
*/
|
|
12
|
-
const getElementsByClassName = (
|
|
13
|
-
selector: string,
|
|
14
|
-
parent?: ParentNode,
|
|
15
|
-
): HTMLCollectionOf<HTMLElement> => {
|
|
12
|
+
const getElementsByClassName = <T extends HTMLElement>(selector: string, parent?: ParentNode) => {
|
|
16
13
|
const lookUp = parent && isNode(parent) ? parent : getDocument();
|
|
17
|
-
return (lookUp as HTMLElement | Document).getElementsByClassName(
|
|
18
|
-
selector,
|
|
19
|
-
) as HTMLCollectionOf<HTMLElement>;
|
|
14
|
+
return (lookUp as HTMLElement | Document).getElementsByClassName(selector) as HTMLCollectionOf<T>;
|
|
20
15
|
};
|
|
21
16
|
|
|
22
17
|
export default getElementsByClassName;
|
|
@@ -9,12 +9,9 @@ import isNode from '../is/isNode';
|
|
|
9
9
|
* @param parent optional Element to look into
|
|
10
10
|
* @return the 'HTMLCollection'
|
|
11
11
|
*/
|
|
12
|
-
const getElementsByTagName = (
|
|
13
|
-
selector: string,
|
|
14
|
-
parent?: ParentNode,
|
|
15
|
-
): HTMLCollectionOf<HTMLElement> => {
|
|
12
|
+
const getElementsByTagName = <T extends HTMLElement>(selector: string, parent?: ParentNode) => {
|
|
16
13
|
const lookUp = isNode(parent) ? parent : getDocument();
|
|
17
|
-
return (lookUp as Document).getElementsByTagName(selector) as HTMLCollectionOf<
|
|
14
|
+
return (lookUp as Document).getElementsByTagName(selector) as HTMLCollectionOf<T>;
|
|
18
15
|
};
|
|
19
16
|
|
|
20
17
|
export default getElementsByTagName;
|
|
@@ -10,13 +10,16 @@ import isHTMLElement from '../is/isHTMLElement';
|
|
|
10
10
|
* @param parent optional node to look into
|
|
11
11
|
* @return the `HTMLElement` or `querySelector` result
|
|
12
12
|
*/
|
|
13
|
-
const querySelector =
|
|
13
|
+
const querySelector = <T extends HTMLElement>(
|
|
14
|
+
selector: HTMLElement | string,
|
|
15
|
+
parent?: ParentNode,
|
|
16
|
+
): T | null => {
|
|
14
17
|
if (isHTMLElement(selector)) {
|
|
15
|
-
return selector;
|
|
18
|
+
return selector as T;
|
|
16
19
|
}
|
|
17
20
|
const lookUp = isNode(parent) ? parent : getDocument();
|
|
18
21
|
|
|
19
|
-
return lookUp.querySelector(selector);
|
|
22
|
+
return lookUp.querySelector<T>(selector);
|
|
20
23
|
};
|
|
21
24
|
|
|
22
25
|
export default querySelector;
|
|
@@ -8,7 +8,10 @@ import isNode from '../is/isNode';
|
|
|
8
8
|
* @param parent optional node to look into
|
|
9
9
|
* @return the query result
|
|
10
10
|
*/
|
|
11
|
-
const querySelectorAll =
|
|
11
|
+
const querySelectorAll = <T extends HTMLElement>(
|
|
12
|
+
selector: string,
|
|
13
|
+
parent?: ParentNode,
|
|
14
|
+
): NodeListOf<T> => {
|
|
12
15
|
const lookUp = isNode(parent) ? parent : getDocument();
|
|
13
16
|
return lookUp.querySelectorAll(selector);
|
|
14
17
|
};
|
package/test/att.test.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { expect, it, describe, afterEach } from 'vitest';
|
|
2
|
+
import { getExampleDOM } from './fixtures/getExampleDom';
|
|
3
|
+
import * as SHORTY from '../src/index';
|
|
4
|
+
import "./fixtures/style.css";
|
|
5
|
+
|
|
6
|
+
describe('Shorty Library Tests - ATT', () => {
|
|
7
|
+
const wrapper = document.createElement('div');
|
|
8
|
+
afterEach(async () => {
|
|
9
|
+
wrapper.innerHTML = '';
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('Test attr folder', () => {
|
|
13
|
+
const container = getExampleDOM();
|
|
14
|
+
wrapper.append(container);
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
getAttribute,
|
|
18
|
+
setAttribute,
|
|
19
|
+
hasAttribute,
|
|
20
|
+
removeAttribute,
|
|
21
|
+
getAttributeNS,
|
|
22
|
+
setAttributeNS,
|
|
23
|
+
hasAttributeNS,
|
|
24
|
+
removeAttributeNS,
|
|
25
|
+
querySelector,
|
|
26
|
+
} = SHORTY;
|
|
27
|
+
|
|
28
|
+
const el = querySelector('.alert', container)!;
|
|
29
|
+
expect(getAttribute(el, 'class')).to.have.length.above(0);
|
|
30
|
+
setAttribute(el, 'data-att', 'momo');
|
|
31
|
+
expect(hasAttribute(el, 'data-att')).to.be.true;
|
|
32
|
+
removeAttribute(el, 'data-att');
|
|
33
|
+
expect(hasAttribute(el, 'data-att')).to.be.false;
|
|
34
|
+
|
|
35
|
+
const svg = querySelector('svg', container)!;
|
|
36
|
+
const ns = 'http://www.w3.org/2000/svg';
|
|
37
|
+
expect(hasAttributeNS(ns, svg, 'transform')).to.be.false;
|
|
38
|
+
setAttributeNS(ns, svg, 'transform', 'scale(0.99)');
|
|
39
|
+
expect(getAttributeNS(ns, svg, 'transform')).to.eq('scale(0.99)');
|
|
40
|
+
removeAttributeNS(ns, svg, 'transform');
|
|
41
|
+
expect(getAttributeNS(ns, svg, 'transform')).to.be.null;
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { expect, it, describe } from 'vitest';
|
|
2
|
+
import * as SHORTY from '../src/index';
|
|
3
|
+
|
|
4
|
+
describe('Shorty Library Tests - BOOL', () => {
|
|
5
|
+
it('Test boolean folder', () => {
|
|
6
|
+
const {
|
|
7
|
+
// these are impossible to test 100% of the branches
|
|
8
|
+
isApple,
|
|
9
|
+
isMobile,
|
|
10
|
+
isFirefox,
|
|
11
|
+
support3DTransform,
|
|
12
|
+
supportAnimation,
|
|
13
|
+
supportPassive,
|
|
14
|
+
supportTouch,
|
|
15
|
+
supportTransform,
|
|
16
|
+
supportTransition,
|
|
17
|
+
// querySelectorAll, getWindow
|
|
18
|
+
} = SHORTY;
|
|
19
|
+
|
|
20
|
+
expect(isApple, 'isApple').to.be.false;
|
|
21
|
+
expect(isMobile, 'isMobile').to.be.false;
|
|
22
|
+
expect(isFirefox, 'isFirefox').to.be.false;
|
|
23
|
+
expect(support3DTransform, 'support3DTransform').to.be.true;
|
|
24
|
+
expect(supportAnimation, 'supportAnimation').to.be.true;
|
|
25
|
+
expect(supportPassive, 'supportPassive').to.be.true;
|
|
26
|
+
expect(supportTouch, 'supportTouch').to.be.false;
|
|
27
|
+
expect(supportTransform, 'supportTransform').to.be.true;
|
|
28
|
+
expect(supportTransition, 'supportTransition').to.be.true;
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { expect, it, describe, beforeEach, vi, afterEach } from 'vitest';
|
|
2
|
+
import { getExampleDOM } from './fixtures/getExampleDom';
|
|
3
|
+
import * as SHORTY from '../src/index';
|
|
4
|
+
import "./fixtures/style.css";
|
|
5
|
+
|
|
6
|
+
describe('Shorty Library Tests - CLASS', () => {
|
|
7
|
+
const wrapper = document.createElement('div');
|
|
8
|
+
document.body.append(wrapper);
|
|
9
|
+
afterEach(async () => {
|
|
10
|
+
wrapper.innerHTML = '';
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('Test class folder', () => {
|
|
14
|
+
const { addClass, hasClass, removeClass, querySelector } = SHORTY;
|
|
15
|
+
const container = getExampleDOM();
|
|
16
|
+
wrapper.append(container);
|
|
17
|
+
|
|
18
|
+
const alert = querySelector('.alert', container);
|
|
19
|
+
if (!alert) return;
|
|
20
|
+
|
|
21
|
+
addClass(alert, 'to-be-removed');
|
|
22
|
+
expect(hasClass(alert, 'to-be-removed')).to.be.true;
|
|
23
|
+
removeClass(alert, 'show');
|
|
24
|
+
expect(hasClass(alert, 'show')).to.be.false;
|
|
25
|
+
});
|
|
26
|
+
});
|