Skip to content

Commit dc6fbed

Browse files
committed
[add] Page Vector class & utility
[optimize] Return Value type of importCSS() [optimize] update Toolchain packages
1 parent 9a64451 commit dc6fbed

File tree

6 files changed

+198
-115
lines changed

6 files changed

+198
-115
lines changed

ReadMe.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,35 @@
77

88
[![NPM](https://nodei.co/npm/web-utility.png?downloads=true&downloadRank=true&stars=true)][4]
99

10+
## Installation
11+
12+
```Shell
13+
npm install web-utility
14+
```
15+
16+
`index.html`
17+
18+
```HTML
19+
<head>
20+
<script src="https://polyfill.app/api/polyfill?features=regenerator-runtime,url,scroll-behavior,intersection-observer"></script>
21+
</head>
22+
```
23+
1024
## Usage
1125

1226
[API document](https://web-cell.dev/web-utility/)
1327

1428
### CSS Animation
1529

16-
1. [Simple hover](https://github.com/EasyWebApp/BootCell/blob/a41bbc1/source/Prompt/Tooltip.tsx#L38-L43)
30+
1. [Watch Swipe](https://github.com/EasyWebApp/BootCell/blob/11c5d6f/source%2FMedia%2FCarousel.tsx#L200-L214)
31+
32+
2. [Simple Hover](https://github.com/EasyWebApp/BootCell/blob/a41bbc1/source/Prompt/Tooltip.tsx#L38-L43)
1733

18-
2. [Switch with `await`](https://github.com/EasyWebApp/BootCell/blob/a41bbc1/source/Content/TabList.tsx#L77-85)
34+
3. [Switch with `await`](https://github.com/EasyWebApp/BootCell/blob/a41bbc1/source/Content/TabList.tsx#L77-85)
1935

20-
3. [Toggle with Inline Styles](https://github.com/EasyWebApp/BootCell/blob/a41bbc1/source/Content/Collapse.tsx#L19-L38)
36+
4. [Toggle with Inline Styles](https://github.com/EasyWebApp/BootCell/blob/a41bbc1/source/Content/Collapse.tsx#L19-L38)
2137

22-
4. [Work with Existed Classes](https://github.com/EasyWebApp/BootCell/blob/a41bbc1/source/Content/Carousel.tsx#L82-L99)
38+
5. [Work with Existed Classes](https://github.com/EasyWebApp/BootCell/blob/a41bbc1/source/Content/Carousel.tsx#L82-L99)
2339

2440
### Message Channel
2541

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "web-utility",
3-
"version": "1.9.0",
3+
"version": "2.0.0",
44
"license": "LGPL-3.0",
55
"author": "[email protected]",
66
"description": "Web front-end toolkit based on TypeScript",
@@ -25,17 +25,17 @@
2525
"module": "dist/web-utility.js",
2626
"devDependencies": {
2727
"@types/jest": "^26.0.15",
28-
"@types/node": "^14.14.8",
28+
"@types/node": "^14.14.10",
2929
"husky": "^4.3.0",
3030
"intersection-observer": "^0.11.0",
3131
"jest": "^26.6.3",
32-
"lint-staged": "^10.5.1",
32+
"lint-staged": "^10.5.2",
3333
"microbundle": "^0.12.4",
3434
"open-cli": "^6.0.1",
35-
"prettier": "^2.1.2",
35+
"prettier": "^2.2.0",
3636
"ts-jest": "^26.4.4",
3737
"typedoc": "^0.19.2",
38-
"typescript": "^4.0.5"
38+
"typescript": "^4.1.2"
3939
},
4040
"prettier": {
4141
"singleQuote": true,

source/DOM.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,20 @@ export function importCSS(
3434
URI: string,
3535
{ alternate, ...options }: CSSOptions = {} as CSSOptions
3636
) {
37-
if ([...document.styleSheets].find(({ href }) => href === URI))
38-
throw Error(`${URI} has been loaded`);
37+
const style = [...document.styleSheets].find(({ href }) => href === URI);
38+
39+
if (style) return Promise.resolve(style);
3940

4041
const link = document.createElement('link');
4142

42-
return new Promise((resolve, reject) => {
43-
(link.onload = resolve), (link.onerror = reject);
43+
return new Promise<CSSStyleSheet>((resolve, reject) => {
44+
link.onload = () => resolve(link.sheet);
45+
link.onerror = (_1, _2, _3, _4, error) => reject(error);
4446

4547
Object.assign(link, options);
4648

47-
(link.rel = (alternate ? 'alternate ' : '') + 'stylesheet'),
48-
(link.href = URI);
49+
link.rel = (alternate ? 'alternate ' : '') + 'stylesheet';
50+
link.href = URI;
4951

5052
document.head.append(link);
5153
});

source/animation.ts

Lines changed: 146 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,146 @@
1-
import { promisify } from './event';
2-
3-
export function durationOf(
4-
type: 'transition' | 'animation',
5-
element: HTMLElement
6-
) {
7-
const { transitionDuration, animationDuration } = getComputedStyle(element);
8-
9-
const duration =
10-
type === 'animation' ? animationDuration : transitionDuration;
11-
12-
return parseFloat(duration) * (duration.slice(-2) === 'ms' ? 1 : 1000);
13-
}
14-
15-
export function watchMotion<T extends Event>(
16-
type: 'transition' | 'animation',
17-
element: HTMLElement
18-
) {
19-
return Promise.race([
20-
promisify<T>(type, element).catch(event => Promise.resolve(event)),
21-
new Promise(resolve => setTimeout(resolve, durationOf(type, element)))
22-
]);
23-
}
24-
25-
function fadeIn<T extends Event>(
26-
type: 'transition' | 'animation',
27-
element: HTMLElement,
28-
className: string,
29-
display: string
30-
) {
31-
element.style.display = display;
32-
33-
const end = watchMotion<T>(type, element);
34-
35-
return new Promise<T>(resolve =>
36-
self.requestAnimationFrame(() => {
37-
element.classList.add(className);
38-
39-
end.then(resolve);
40-
})
41-
);
42-
}
43-
44-
async function fadeOut<T extends Event>(
45-
type: 'transition' | 'animation',
46-
element: HTMLElement,
47-
className: string,
48-
remove?: boolean
49-
) {
50-
const end = watchMotion<T>(type, element);
51-
52-
element.classList.remove(className);
53-
54-
await end;
55-
56-
if (remove) element.remove();
57-
else element.style.display = 'none';
58-
}
59-
60-
export function transitIn(
61-
element: HTMLElement,
62-
className: string,
63-
display = 'block'
64-
) {
65-
return fadeIn<TransitionEvent>('transition', element, className, display);
66-
}
67-
68-
export function animateIn(
69-
element: HTMLElement,
70-
className: string,
71-
display = 'block'
72-
) {
73-
return fadeIn<AnimationEvent>('animation', element, className, display);
74-
}
75-
76-
export function transitOut(
77-
element: HTMLElement,
78-
className: string,
79-
remove?: boolean
80-
) {
81-
return fadeOut<TransitionEvent>('transition', element, className, remove);
82-
}
83-
84-
export function animateOut(
85-
element: HTMLElement,
86-
className: string,
87-
remove?: boolean
88-
) {
89-
return fadeOut<AnimationEvent>('animation', element, className, remove);
90-
}
1+
import { promisify } from './event';
2+
3+
export interface CartesianCoordinate {
4+
x: number;
5+
y: number;
6+
z?: number;
7+
}
8+
9+
export class PageVector {
10+
from: CartesianCoordinate;
11+
to: CartesianCoordinate;
12+
13+
constructor(from: CartesianCoordinate, to: CartesianCoordinate) {
14+
this.from = from;
15+
this.to = to;
16+
}
17+
18+
get length() {
19+
const { from, to } = this;
20+
21+
return Math.sqrt(
22+
Math.pow(to.x - from.x, 2) +
23+
Math.pow(to.y - from.y, 2) +
24+
(to.z != null ? Math.pow(to.z - from.z, 2) : 0)
25+
);
26+
}
27+
28+
get direction() {
29+
const { from, to } = this;
30+
const XD = to.x - from.x,
31+
YD = to.y - from.y,
32+
ZD = to.z - from.z;
33+
const XL = Math.abs(XD),
34+
YL = Math.abs(YD),
35+
ZL = Math.abs(ZD);
36+
37+
switch (isNaN(ZL) ? Math.max(XL, YL) : Math.max(XL, YL, ZL)) {
38+
case XL:
39+
return XD > 0 ? 'right' : 'left';
40+
case YL:
41+
return YD > 0 ? 'forward' : 'backward';
42+
case ZL:
43+
return ZD > 0 ? 'up' : 'down';
44+
}
45+
}
46+
}
47+
48+
export function getSwipeVector(
49+
from: CartesianCoordinate,
50+
to: CartesianCoordinate,
51+
threshold = parseInt(self.getComputedStyle(document.body).fontSize) * 6
52+
) {
53+
const vector = new PageVector(from, to);
54+
55+
if (vector.length >= threshold && !self.getSelection()?.toString().trim())
56+
return vector;
57+
}
58+
59+
export function durationOf(
60+
type: 'transition' | 'animation',
61+
element: HTMLElement
62+
) {
63+
const { transitionDuration, animationDuration } = getComputedStyle(element);
64+
65+
const duration =
66+
type === 'animation' ? animationDuration : transitionDuration;
67+
68+
return parseFloat(duration) * (duration.slice(-2) === 'ms' ? 1 : 1000);
69+
}
70+
71+
export function watchMotion<T extends Event>(
72+
type: 'transition' | 'animation',
73+
element: HTMLElement
74+
) {
75+
return Promise.race([
76+
promisify<T>(type, element).catch(event => Promise.resolve(event)),
77+
new Promise(resolve => setTimeout(resolve, durationOf(type, element)))
78+
]);
79+
}
80+
81+
function fadeIn<T extends Event>(
82+
type: 'transition' | 'animation',
83+
element: HTMLElement,
84+
className: string,
85+
display: string
86+
) {
87+
element.style.display = display;
88+
89+
const end = watchMotion<T>(type, element);
90+
91+
return new Promise<T>(resolve =>
92+
self.requestAnimationFrame(() => {
93+
element.classList.add(className);
94+
95+
end.then(resolve);
96+
})
97+
);
98+
}
99+
100+
async function fadeOut<T extends Event>(
101+
type: 'transition' | 'animation',
102+
element: HTMLElement,
103+
className: string,
104+
remove?: boolean
105+
) {
106+
const end = watchMotion<T>(type, element);
107+
108+
element.classList.remove(className);
109+
110+
await end;
111+
112+
if (remove) element.remove();
113+
else element.style.display = 'none';
114+
}
115+
116+
export function transitIn(
117+
element: HTMLElement,
118+
className: string,
119+
display = 'block'
120+
) {
121+
return fadeIn<TransitionEvent>('transition', element, className, display);
122+
}
123+
124+
export function animateIn(
125+
element: HTMLElement,
126+
className: string,
127+
display = 'block'
128+
) {
129+
return fadeIn<AnimationEvent>('animation', element, className, display);
130+
}
131+
132+
export function transitOut(
133+
element: HTMLElement,
134+
className: string,
135+
remove?: boolean
136+
) {
137+
return fadeOut<TransitionEvent>('transition', element, className, remove);
138+
}
139+
140+
export function animateOut(
141+
element: HTMLElement,
142+
className: string,
143+
remove?: boolean
144+
) {
145+
return fadeOut<AnimationEvent>('animation', element, className, remove);
146+
}

test/animation.spec.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
import { durationOf } from '../source';
2-
3-
describe('Animation', () => {
4-
it('should return Millisecond Duration of Transition Style', () => {
5-
document.body.style.transitionDuration = '0.25s';
6-
7-
expect(durationOf('transition', document.body)).toBe(250);
8-
});
9-
});
1+
import { PageVector, durationOf } from '../source/animation';
2+
3+
describe('Animation', () => {
4+
it('should calculate the Length & Direction of a Page Vector', () => {
5+
const { length, direction } = new PageVector(
6+
{ x: 0, y: 0 },
7+
{ x: 3, y: 4 }
8+
);
9+
expect(length).toBe(5);
10+
expect(direction).toBe('forward');
11+
});
12+
13+
it('should return Millisecond Duration of Transition Style', () => {
14+
document.body.style.transitionDuration = '0.25s';
15+
16+
expect(durationOf('transition', document.body)).toBe(250);
17+
});
18+
});

test/event.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { promisify } from '../source';
1+
import { promisify } from '../source/event';
22

33
describe('Event', () => {
44
it('should convert an End Event to a Promise resolve', async () => {

0 commit comments

Comments
 (0)