@996-design/996-happy-work-theme 1.0.0
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/LICENSE +20 -0
- package/NOTICE.md +23 -0
- package/README.md +15 -0
- package/es/DotEffect/index.d.ts +10 -0
- package/es/DotEffect/index.js +184 -0
- package/es/DotEffect/style.d.ts +4 -0
- package/es/DotEffect/style.js +100 -0
- package/es/HappyProvider.d.ts +6 -0
- package/es/HappyProvider.js +46 -0
- package/es/index.d.ts +2 -0
- package/es/index.js +2 -0
- package/lib/DotEffect/index.d.ts +10 -0
- package/lib/DotEffect/index.js +192 -0
- package/lib/DotEffect/style.d.ts +4 -0
- package/lib/DotEffect/style.js +106 -0
- package/lib/HappyProvider.d.ts +6 -0
- package/lib/HappyProvider.js +54 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +13 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in
|
|
12
|
+
all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
15
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/NOTICE.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Upstream attribution
|
|
2
|
+
|
|
3
|
+
The runtime and declaration artifacts in `es/` and `lib/` are derived from
|
|
4
|
+
`@ant-design/happy-work-theme` 2.0.0.
|
|
5
|
+
|
|
6
|
+
- Upstream: https://github.com/ant-design/happy-work-theme/tree/2.0.0
|
|
7
|
+
- License: MIT. See `LICENSE`.
|
|
8
|
+
|
|
9
|
+
Local changes are limited to the package identity, workspace scripts and
|
|
10
|
+
metadata, and rewriting Ant Design, CSS-in-JS, and FastColor imports to the
|
|
11
|
+
corresponding `@996-design/*` packages. The theme behavior is otherwise kept
|
|
12
|
+
aligned with the published 2.0.0 package.
|
|
13
|
+
|
|
14
|
+
## Mechanical parity source
|
|
15
|
+
|
|
16
|
+
- npm tarball: https://registry.npmjs.org/@ant-design/happy-work-theme/-/happy-work-theme-2.0.0.tgz
|
|
17
|
+
- Integrity: `sha512-3pvwSOGl9YKkhusTFVsSAouoQPJNsf95I2ooIiUmG/tOkayFUbFqPqMHkMc5Q0XdpfKW+sCr6bCtLdsDnWxaaA==`
|
|
18
|
+
- Guard inventory: 16 runtime files; sorted-path SHA-256
|
|
19
|
+
`15a1411dd3d7ade353f1c9ae1f33006a71b078f3a845b2c71c90e333f3d049a1`.
|
|
20
|
+
|
|
21
|
+
The parity inventory excludes package metadata, local build products,
|
|
22
|
+
`node_modules`, and caches. Every inventoried file is compared after only the
|
|
23
|
+
declared package-namespace rewrite.
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# 996 Design Happy Work Theme
|
|
2
|
+
|
|
3
|
+
Expressive interaction theme for focused, energetic product experiences.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @996-design/996-happy-work-theme
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The package supplies theme runtime files and accompanying style assets. It is designed to be applied through the 996 Design configuration provider.
|
|
12
|
+
|
|
13
|
+
## License
|
|
14
|
+
|
|
15
|
+
MIT. See `LICENSE` and `NOTICE.md` for license and source attribution.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { GlobalToken } from '@996-design/996-components';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
interface DotEffectProps {
|
|
4
|
+
target: HTMLElement;
|
|
5
|
+
token: GlobalToken;
|
|
6
|
+
hashId: string;
|
|
7
|
+
onFinish: VoidFunction;
|
|
8
|
+
}
|
|
9
|
+
export default function DotEffect({ hashId, target, token, onFinish, }: DotEffectProps): React.JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { FastColor } from '@996-design/996-fast-color';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { CSSMotionList } from '@rc-component/motion';
|
|
4
|
+
import raf from "@rc-component/util/es/raf";
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import useStyle, { TARGET_ATTR } from "./style";
|
|
7
|
+
const DOT_COUNT = 7;
|
|
8
|
+
const DOT_COUNT_LG = 10;
|
|
9
|
+
function inRange(x, y, left, top, right, bottom) {
|
|
10
|
+
return x >= left && x <= right && y >= top && y <= bottom;
|
|
11
|
+
}
|
|
12
|
+
export default function DotEffect({
|
|
13
|
+
hashId,
|
|
14
|
+
target,
|
|
15
|
+
token,
|
|
16
|
+
onFinish
|
|
17
|
+
}) {
|
|
18
|
+
const prefixCls = 'happy-wave';
|
|
19
|
+
const dotPrefixCls = `${prefixCls}-dot`;
|
|
20
|
+
const [dots, setDots] = React.useState(null);
|
|
21
|
+
const [left, setLeft] = React.useState(0);
|
|
22
|
+
const [top, setTop] = React.useState(0);
|
|
23
|
+
useStyle(prefixCls, hashId, token);
|
|
24
|
+
const targetAttrName = `${TARGET_ATTR}-${hashId}`;
|
|
25
|
+
|
|
26
|
+
// ========================= Dots =========================
|
|
27
|
+
React.useEffect(() => {
|
|
28
|
+
const id = raf(() => {
|
|
29
|
+
if (['-dangerous', '-error'].some(skipCls => target.className.includes(skipCls))) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const rect = target.getBoundingClientRect();
|
|
33
|
+
const {
|
|
34
|
+
width,
|
|
35
|
+
height
|
|
36
|
+
} = rect;
|
|
37
|
+
setLeft(rect.left + width / 2);
|
|
38
|
+
setTop(rect.top + height / 2);
|
|
39
|
+
setDots([]);
|
|
40
|
+
const minSize = Math.min(width, height);
|
|
41
|
+
const maxSize = Math.max(width, height);
|
|
42
|
+
const halfMinSize = minSize / 2;
|
|
43
|
+
const halfMaxSize = maxSize / 2;
|
|
44
|
+
const halfWidth = width / 2;
|
|
45
|
+
const halfHeight = height / 2;
|
|
46
|
+
const OFFSET_MIN = 15;
|
|
47
|
+
const OFFSET_MAX = 30;
|
|
48
|
+
const halfOffsetMinWidth = halfWidth + OFFSET_MIN;
|
|
49
|
+
const halfOffsetMinHeight = halfHeight + OFFSET_MIN;
|
|
50
|
+
const halfOffsetMaxWidth = halfWidth + OFFSET_MAX;
|
|
51
|
+
const halfOffsetMaxHeight = halfHeight + OFFSET_MAX;
|
|
52
|
+
const dotCount = minSize >= 20 ? DOT_COUNT_LG : DOT_COUNT;
|
|
53
|
+
|
|
54
|
+
// Delay to start dot motion
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
const offsetAngle = Math.random() * 360;
|
|
57
|
+
|
|
58
|
+
// Color
|
|
59
|
+
const {
|
|
60
|
+
colorPrimary
|
|
61
|
+
} = token;
|
|
62
|
+
const colorHsv = new FastColor(colorPrimary).toHsv();
|
|
63
|
+
colorHsv.h -= 30;
|
|
64
|
+
const colorPrimaryWeak = new FastColor(colorHsv).toHexString();
|
|
65
|
+
setDots(new Array(dotCount).fill(null).map((_, index) => {
|
|
66
|
+
const rotate = 360 / dotCount;
|
|
67
|
+
const randomAngle = offsetAngle + rotate * index;
|
|
68
|
+
|
|
69
|
+
// Get start XY (Which should align the rect edge)
|
|
70
|
+
let startX = 0;
|
|
71
|
+
let startY = 0;
|
|
72
|
+
for (let startDist = halfMinSize - 1; startDist <= halfMaxSize; startDist += 1) {
|
|
73
|
+
const x = Math.cos(randomAngle * Math.PI / 180) * startDist;
|
|
74
|
+
const y = Math.sin(randomAngle * Math.PI / 180) * startDist;
|
|
75
|
+
if (!inRange(x, y, -halfWidth, -halfHeight, halfWidth, halfHeight)) {
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
startX = x;
|
|
79
|
+
startY = y;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Get end XY
|
|
83
|
+
let endX = startX;
|
|
84
|
+
let endY = startY;
|
|
85
|
+
let endDist = halfMinSize;
|
|
86
|
+
const endHalfWidth = Math.random() * (halfOffsetMaxWidth - halfOffsetMinWidth) + halfOffsetMinWidth;
|
|
87
|
+
const endHalfHeight = Math.random() * (halfOffsetMaxHeight - halfOffsetMinHeight) + halfOffsetMinHeight;
|
|
88
|
+
do {
|
|
89
|
+
endX = Math.cos(randomAngle * Math.PI / 180) * endDist;
|
|
90
|
+
endY = Math.sin(randomAngle * Math.PI / 180) * endDist;
|
|
91
|
+
endDist += 1;
|
|
92
|
+
} while (inRange(endX, endY, -endHalfWidth, -endHalfHeight, endHalfWidth, endHalfHeight));
|
|
93
|
+
let size = Math.random() * 3 + 3;
|
|
94
|
+
if (height >= 20) {
|
|
95
|
+
size = Math.random() * 4 + 6;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
key: index + 1,
|
|
99
|
+
startX: `${startX}px`,
|
|
100
|
+
startY: `${startY}px`,
|
|
101
|
+
endX: `${endX}px`,
|
|
102
|
+
endY: `${endY}px`,
|
|
103
|
+
startSize: `${size}px`,
|
|
104
|
+
endSize: `${Math.random() > 0.75 ? size : 0}px`,
|
|
105
|
+
type: Math.random() > 0.6 ? 'outlined' : 'fill',
|
|
106
|
+
color: Math.random() > 0.5 ? colorPrimary : colorPrimaryWeak
|
|
107
|
+
};
|
|
108
|
+
}));
|
|
109
|
+
}, 50);
|
|
110
|
+
target.setAttribute(targetAttrName, 'true');
|
|
111
|
+
});
|
|
112
|
+
return () => {
|
|
113
|
+
raf.cancel(id);
|
|
114
|
+
};
|
|
115
|
+
}, []);
|
|
116
|
+
|
|
117
|
+
// ======================== Clean =========================
|
|
118
|
+
React.useEffect(() => {
|
|
119
|
+
const id = setTimeout(() => {
|
|
120
|
+
target.removeAttribute(targetAttrName);
|
|
121
|
+
onFinish();
|
|
122
|
+
}, 600);
|
|
123
|
+
return () => {
|
|
124
|
+
clearTimeout(id);
|
|
125
|
+
};
|
|
126
|
+
}, []);
|
|
127
|
+
|
|
128
|
+
// ======================== Render ========================
|
|
129
|
+
if (!dots) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
133
|
+
className: clsx(prefixCls, hashId),
|
|
134
|
+
style: {
|
|
135
|
+
left,
|
|
136
|
+
top
|
|
137
|
+
}
|
|
138
|
+
}, /*#__PURE__*/React.createElement(CSSMotionList, {
|
|
139
|
+
component: false,
|
|
140
|
+
keys: dots,
|
|
141
|
+
motionAppear: true,
|
|
142
|
+
motionName: "happy-in-out"
|
|
143
|
+
}, ({
|
|
144
|
+
className: motionCls,
|
|
145
|
+
style: motionStyle,
|
|
146
|
+
key,
|
|
147
|
+
startX,
|
|
148
|
+
startY,
|
|
149
|
+
endX,
|
|
150
|
+
endY,
|
|
151
|
+
startSize,
|
|
152
|
+
endSize,
|
|
153
|
+
type,
|
|
154
|
+
color
|
|
155
|
+
}) => {
|
|
156
|
+
const name = `${dotPrefixCls}-${key}`;
|
|
157
|
+
const dotCls = clsx(dotPrefixCls, motionCls, name);
|
|
158
|
+
|
|
159
|
+
// if (dotCls.includes('active')) {
|
|
160
|
+
// debugger;
|
|
161
|
+
// }
|
|
162
|
+
|
|
163
|
+
const dotStyle = {
|
|
164
|
+
[`--start-x`]: startX,
|
|
165
|
+
[`--start-y`]: startY,
|
|
166
|
+
[`--end-x`]: endX,
|
|
167
|
+
[`--end-y`]: endY,
|
|
168
|
+
[`--start-size`]: startSize,
|
|
169
|
+
[`--end-size`]: endSize
|
|
170
|
+
};
|
|
171
|
+
if (type === 'fill') {
|
|
172
|
+
dotStyle['--background'] = color;
|
|
173
|
+
} else {
|
|
174
|
+
dotStyle['--border'] = `1px solid ${color}`;
|
|
175
|
+
}
|
|
176
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
177
|
+
className: dotCls,
|
|
178
|
+
style: {
|
|
179
|
+
...motionStyle,
|
|
180
|
+
...dotStyle
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createTheme, Keyframes, useStyleRegister } from '@996-design/996-cssinjs';
|
|
2
|
+
const DEFAULT_THEME = createTheme(token => token);
|
|
3
|
+
export const TARGET_ATTR = 'data-happy-wave-target';
|
|
4
|
+
const antWaveTargetEffect = new Keyframes('antWaveTargetEffect', {
|
|
5
|
+
'0%': {
|
|
6
|
+
transform: 'scale(1)'
|
|
7
|
+
},
|
|
8
|
+
'10%': {
|
|
9
|
+
transform: 'scale(1.1)'
|
|
10
|
+
},
|
|
11
|
+
'35%': {
|
|
12
|
+
transform: 'scale(0.94)'
|
|
13
|
+
},
|
|
14
|
+
'60%': {
|
|
15
|
+
transform: 'scale(1.05)'
|
|
16
|
+
},
|
|
17
|
+
'85%': {
|
|
18
|
+
transform: 'scale(0.97)'
|
|
19
|
+
},
|
|
20
|
+
'100%': {
|
|
21
|
+
transform: 'scale(1)'
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
const antWaveDotEffect = new Keyframes('antWaveDotEffect', {
|
|
25
|
+
'0%': {
|
|
26
|
+
opacity: 0,
|
|
27
|
+
left: `var(--start-x)`,
|
|
28
|
+
top: `var(--start-y)`,
|
|
29
|
+
width: `var(--start-size)`,
|
|
30
|
+
height: `var(--start-size)`,
|
|
31
|
+
background: `var(--background)`,
|
|
32
|
+
border: `var(--border)`
|
|
33
|
+
},
|
|
34
|
+
'25%': {
|
|
35
|
+
opacity: 1
|
|
36
|
+
},
|
|
37
|
+
'50%': {
|
|
38
|
+
opacity: 0.8
|
|
39
|
+
},
|
|
40
|
+
'100%': {
|
|
41
|
+
opacity: 0,
|
|
42
|
+
left: `var(--end-x)`,
|
|
43
|
+
top: `var(--end-y)`,
|
|
44
|
+
width: `var(--end-size)`,
|
|
45
|
+
height: `var(--end-size)`,
|
|
46
|
+
background: `var(--background)`,
|
|
47
|
+
border: `var(--border)`
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const useStyle = (prefixCls, hashId, token) => {
|
|
51
|
+
const sharedConfig = {
|
|
52
|
+
theme: DEFAULT_THEME,
|
|
53
|
+
token
|
|
54
|
+
};
|
|
55
|
+
useStyleRegister({
|
|
56
|
+
...sharedConfig,
|
|
57
|
+
path: ['HappyWorkTheme', 'target']
|
|
58
|
+
}, () => ({
|
|
59
|
+
// ======================== Target ========================
|
|
60
|
+
[`[${TARGET_ATTR}-${hashId}], & [${TARGET_ATTR}-${hashId}]`]: {
|
|
61
|
+
animationName: antWaveTargetEffect,
|
|
62
|
+
animationDuration: `0.45s`,
|
|
63
|
+
animationTimingFunction: 'ease-in-out',
|
|
64
|
+
animationFillMode: 'backwards'
|
|
65
|
+
}
|
|
66
|
+
}));
|
|
67
|
+
useStyleRegister({
|
|
68
|
+
...sharedConfig,
|
|
69
|
+
hashId,
|
|
70
|
+
path: ['HappyWorkTheme', 'effect']
|
|
71
|
+
}, () => {
|
|
72
|
+
const dotPrefixCls = `.${prefixCls}`;
|
|
73
|
+
return [{
|
|
74
|
+
// ========================= Dots =========================
|
|
75
|
+
[dotPrefixCls]: {
|
|
76
|
+
position: 'fixed',
|
|
77
|
+
pointerEvents: 'none',
|
|
78
|
+
zIndex: 999999,
|
|
79
|
+
[`${dotPrefixCls}-dot`]: {
|
|
80
|
+
boxSizing: 'border-box',
|
|
81
|
+
position: 'absolute',
|
|
82
|
+
borderRadius: '100%',
|
|
83
|
+
opacity: 0,
|
|
84
|
+
transform: 'translate3d(-50%, -50%, 0)',
|
|
85
|
+
// Start Position
|
|
86
|
+
zIndex: 999999,
|
|
87
|
+
// =================== Basic Motion ===================
|
|
88
|
+
'&.happy-in-out': {
|
|
89
|
+
animationName: antWaveDotEffect,
|
|
90
|
+
animationDuration: token.motionDurationSlow,
|
|
91
|
+
// animationDuration: '10s',
|
|
92
|
+
animationTimingFunction: 'linear',
|
|
93
|
+
animationFillMode: 'backwards'
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}];
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
export default useStyle;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ConfigProvider } from '@996-design/996-components';
|
|
2
|
+
import useEvent from "@rc-component/util/es/hooks/useEvent";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import DotEffect from "./DotEffect";
|
|
5
|
+
import { render, unmount } from "@rc-component/util/es/React/render";
|
|
6
|
+
const HappyProvider = props => {
|
|
7
|
+
const {
|
|
8
|
+
children,
|
|
9
|
+
disabled
|
|
10
|
+
} = props;
|
|
11
|
+
const showEffect = useEvent((target, info) => {
|
|
12
|
+
const {
|
|
13
|
+
token,
|
|
14
|
+
hashId
|
|
15
|
+
} = info;
|
|
16
|
+
|
|
17
|
+
// Create holder
|
|
18
|
+
const holder = document.createElement('div');
|
|
19
|
+
holder.style.position = 'absolute';
|
|
20
|
+
holder.style.left = `0px`;
|
|
21
|
+
holder.style.top = `0px`;
|
|
22
|
+
document.body.appendChild(holder);
|
|
23
|
+
render( /*#__PURE__*/React.createElement(DotEffect, {
|
|
24
|
+
target: target,
|
|
25
|
+
token: token,
|
|
26
|
+
hashId: hashId,
|
|
27
|
+
onFinish: () => {
|
|
28
|
+
unmount(holder).then(() => {
|
|
29
|
+
holder.remove();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}), holder);
|
|
33
|
+
});
|
|
34
|
+
const memoizedWaveConfig = React.useMemo(() => {
|
|
35
|
+
if (disabled) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
showEffect
|
|
40
|
+
};
|
|
41
|
+
}, [disabled]);
|
|
42
|
+
return /*#__PURE__*/React.createElement(ConfigProvider, {
|
|
43
|
+
wave: memoizedWaveConfig
|
|
44
|
+
}, children);
|
|
45
|
+
};
|
|
46
|
+
export default HappyProvider;
|
package/es/index.d.ts
ADDED
package/es/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { GlobalToken } from '@996-design/996-components';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
interface DotEffectProps {
|
|
4
|
+
target: HTMLElement;
|
|
5
|
+
token: GlobalToken;
|
|
6
|
+
hashId: string;
|
|
7
|
+
onFinish: VoidFunction;
|
|
8
|
+
}
|
|
9
|
+
export default function DotEffect({ hashId, target, token, onFinish, }: DotEffectProps): React.JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
|
|
4
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = DotEffect;
|
|
9
|
+
var _fastColor = require("@996-design/996-fast-color");
|
|
10
|
+
var _clsx = require("clsx");
|
|
11
|
+
var _motion = require("@rc-component/motion");
|
|
12
|
+
var _raf = _interopRequireDefault(require("@rc-component/util/lib/raf"));
|
|
13
|
+
var React = _interopRequireWildcard(require("react"));
|
|
14
|
+
var _style = _interopRequireWildcard(require("./style"));
|
|
15
|
+
const DOT_COUNT = 7;
|
|
16
|
+
const DOT_COUNT_LG = 10;
|
|
17
|
+
function inRange(x, y, left, top, right, bottom) {
|
|
18
|
+
return x >= left && x <= right && y >= top && y <= bottom;
|
|
19
|
+
}
|
|
20
|
+
function DotEffect({
|
|
21
|
+
hashId,
|
|
22
|
+
target,
|
|
23
|
+
token,
|
|
24
|
+
onFinish
|
|
25
|
+
}) {
|
|
26
|
+
const prefixCls = 'happy-wave';
|
|
27
|
+
const dotPrefixCls = `${prefixCls}-dot`;
|
|
28
|
+
const [dots, setDots] = React.useState(null);
|
|
29
|
+
const [left, setLeft] = React.useState(0);
|
|
30
|
+
const [top, setTop] = React.useState(0);
|
|
31
|
+
(0, _style.default)(prefixCls, hashId, token);
|
|
32
|
+
const targetAttrName = `${_style.TARGET_ATTR}-${hashId}`;
|
|
33
|
+
|
|
34
|
+
// ========================= Dots =========================
|
|
35
|
+
React.useEffect(() => {
|
|
36
|
+
const id = (0, _raf.default)(() => {
|
|
37
|
+
if (['-dangerous', '-error'].some(skipCls => target.className.includes(skipCls))) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const rect = target.getBoundingClientRect();
|
|
41
|
+
const {
|
|
42
|
+
width,
|
|
43
|
+
height
|
|
44
|
+
} = rect;
|
|
45
|
+
setLeft(rect.left + width / 2);
|
|
46
|
+
setTop(rect.top + height / 2);
|
|
47
|
+
setDots([]);
|
|
48
|
+
const minSize = Math.min(width, height);
|
|
49
|
+
const maxSize = Math.max(width, height);
|
|
50
|
+
const halfMinSize = minSize / 2;
|
|
51
|
+
const halfMaxSize = maxSize / 2;
|
|
52
|
+
const halfWidth = width / 2;
|
|
53
|
+
const halfHeight = height / 2;
|
|
54
|
+
const OFFSET_MIN = 15;
|
|
55
|
+
const OFFSET_MAX = 30;
|
|
56
|
+
const halfOffsetMinWidth = halfWidth + OFFSET_MIN;
|
|
57
|
+
const halfOffsetMinHeight = halfHeight + OFFSET_MIN;
|
|
58
|
+
const halfOffsetMaxWidth = halfWidth + OFFSET_MAX;
|
|
59
|
+
const halfOffsetMaxHeight = halfHeight + OFFSET_MAX;
|
|
60
|
+
const dotCount = minSize >= 20 ? DOT_COUNT_LG : DOT_COUNT;
|
|
61
|
+
|
|
62
|
+
// Delay to start dot motion
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
const offsetAngle = Math.random() * 360;
|
|
65
|
+
|
|
66
|
+
// Color
|
|
67
|
+
const {
|
|
68
|
+
colorPrimary
|
|
69
|
+
} = token;
|
|
70
|
+
const colorHsv = new _fastColor.FastColor(colorPrimary).toHsv();
|
|
71
|
+
colorHsv.h -= 30;
|
|
72
|
+
const colorPrimaryWeak = new _fastColor.FastColor(colorHsv).toHexString();
|
|
73
|
+
setDots(new Array(dotCount).fill(null).map((_, index) => {
|
|
74
|
+
const rotate = 360 / dotCount;
|
|
75
|
+
const randomAngle = offsetAngle + rotate * index;
|
|
76
|
+
|
|
77
|
+
// Get start XY (Which should align the rect edge)
|
|
78
|
+
let startX = 0;
|
|
79
|
+
let startY = 0;
|
|
80
|
+
for (let startDist = halfMinSize - 1; startDist <= halfMaxSize; startDist += 1) {
|
|
81
|
+
const x = Math.cos(randomAngle * Math.PI / 180) * startDist;
|
|
82
|
+
const y = Math.sin(randomAngle * Math.PI / 180) * startDist;
|
|
83
|
+
if (!inRange(x, y, -halfWidth, -halfHeight, halfWidth, halfHeight)) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
startX = x;
|
|
87
|
+
startY = y;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Get end XY
|
|
91
|
+
let endX = startX;
|
|
92
|
+
let endY = startY;
|
|
93
|
+
let endDist = halfMinSize;
|
|
94
|
+
const endHalfWidth = Math.random() * (halfOffsetMaxWidth - halfOffsetMinWidth) + halfOffsetMinWidth;
|
|
95
|
+
const endHalfHeight = Math.random() * (halfOffsetMaxHeight - halfOffsetMinHeight) + halfOffsetMinHeight;
|
|
96
|
+
do {
|
|
97
|
+
endX = Math.cos(randomAngle * Math.PI / 180) * endDist;
|
|
98
|
+
endY = Math.sin(randomAngle * Math.PI / 180) * endDist;
|
|
99
|
+
endDist += 1;
|
|
100
|
+
} while (inRange(endX, endY, -endHalfWidth, -endHalfHeight, endHalfWidth, endHalfHeight));
|
|
101
|
+
let size = Math.random() * 3 + 3;
|
|
102
|
+
if (height >= 20) {
|
|
103
|
+
size = Math.random() * 4 + 6;
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
key: index + 1,
|
|
107
|
+
startX: `${startX}px`,
|
|
108
|
+
startY: `${startY}px`,
|
|
109
|
+
endX: `${endX}px`,
|
|
110
|
+
endY: `${endY}px`,
|
|
111
|
+
startSize: `${size}px`,
|
|
112
|
+
endSize: `${Math.random() > 0.75 ? size : 0}px`,
|
|
113
|
+
type: Math.random() > 0.6 ? 'outlined' : 'fill',
|
|
114
|
+
color: Math.random() > 0.5 ? colorPrimary : colorPrimaryWeak
|
|
115
|
+
};
|
|
116
|
+
}));
|
|
117
|
+
}, 50);
|
|
118
|
+
target.setAttribute(targetAttrName, 'true');
|
|
119
|
+
});
|
|
120
|
+
return () => {
|
|
121
|
+
_raf.default.cancel(id);
|
|
122
|
+
};
|
|
123
|
+
}, []);
|
|
124
|
+
|
|
125
|
+
// ======================== Clean =========================
|
|
126
|
+
React.useEffect(() => {
|
|
127
|
+
const id = setTimeout(() => {
|
|
128
|
+
target.removeAttribute(targetAttrName);
|
|
129
|
+
onFinish();
|
|
130
|
+
}, 600);
|
|
131
|
+
return () => {
|
|
132
|
+
clearTimeout(id);
|
|
133
|
+
};
|
|
134
|
+
}, []);
|
|
135
|
+
|
|
136
|
+
// ======================== Render ========================
|
|
137
|
+
if (!dots) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
141
|
+
className: (0, _clsx.clsx)(prefixCls, hashId),
|
|
142
|
+
style: {
|
|
143
|
+
left,
|
|
144
|
+
top
|
|
145
|
+
}
|
|
146
|
+
}, /*#__PURE__*/React.createElement(_motion.CSSMotionList, {
|
|
147
|
+
component: false,
|
|
148
|
+
keys: dots,
|
|
149
|
+
motionAppear: true,
|
|
150
|
+
motionName: "happy-in-out"
|
|
151
|
+
}, ({
|
|
152
|
+
className: motionCls,
|
|
153
|
+
style: motionStyle,
|
|
154
|
+
key,
|
|
155
|
+
startX,
|
|
156
|
+
startY,
|
|
157
|
+
endX,
|
|
158
|
+
endY,
|
|
159
|
+
startSize,
|
|
160
|
+
endSize,
|
|
161
|
+
type,
|
|
162
|
+
color
|
|
163
|
+
}) => {
|
|
164
|
+
const name = `${dotPrefixCls}-${key}`;
|
|
165
|
+
const dotCls = (0, _clsx.clsx)(dotPrefixCls, motionCls, name);
|
|
166
|
+
|
|
167
|
+
// if (dotCls.includes('active')) {
|
|
168
|
+
// debugger;
|
|
169
|
+
// }
|
|
170
|
+
|
|
171
|
+
const dotStyle = {
|
|
172
|
+
[`--start-x`]: startX,
|
|
173
|
+
[`--start-y`]: startY,
|
|
174
|
+
[`--end-x`]: endX,
|
|
175
|
+
[`--end-y`]: endY,
|
|
176
|
+
[`--start-size`]: startSize,
|
|
177
|
+
[`--end-size`]: endSize
|
|
178
|
+
};
|
|
179
|
+
if (type === 'fill') {
|
|
180
|
+
dotStyle['--background'] = color;
|
|
181
|
+
} else {
|
|
182
|
+
dotStyle['--border'] = `1px solid ${color}`;
|
|
183
|
+
}
|
|
184
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
185
|
+
className: dotCls,
|
|
186
|
+
style: {
|
|
187
|
+
...motionStyle,
|
|
188
|
+
...dotStyle
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.TARGET_ATTR = void 0;
|
|
7
|
+
var _cssinjs = require("@996-design/996-cssinjs");
|
|
8
|
+
const DEFAULT_THEME = (0, _cssinjs.createTheme)(token => token);
|
|
9
|
+
const TARGET_ATTR = exports.TARGET_ATTR = 'data-happy-wave-target';
|
|
10
|
+
const antWaveTargetEffect = new _cssinjs.Keyframes('antWaveTargetEffect', {
|
|
11
|
+
'0%': {
|
|
12
|
+
transform: 'scale(1)'
|
|
13
|
+
},
|
|
14
|
+
'10%': {
|
|
15
|
+
transform: 'scale(1.1)'
|
|
16
|
+
},
|
|
17
|
+
'35%': {
|
|
18
|
+
transform: 'scale(0.94)'
|
|
19
|
+
},
|
|
20
|
+
'60%': {
|
|
21
|
+
transform: 'scale(1.05)'
|
|
22
|
+
},
|
|
23
|
+
'85%': {
|
|
24
|
+
transform: 'scale(0.97)'
|
|
25
|
+
},
|
|
26
|
+
'100%': {
|
|
27
|
+
transform: 'scale(1)'
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const antWaveDotEffect = new _cssinjs.Keyframes('antWaveDotEffect', {
|
|
31
|
+
'0%': {
|
|
32
|
+
opacity: 0,
|
|
33
|
+
left: `var(--start-x)`,
|
|
34
|
+
top: `var(--start-y)`,
|
|
35
|
+
width: `var(--start-size)`,
|
|
36
|
+
height: `var(--start-size)`,
|
|
37
|
+
background: `var(--background)`,
|
|
38
|
+
border: `var(--border)`
|
|
39
|
+
},
|
|
40
|
+
'25%': {
|
|
41
|
+
opacity: 1
|
|
42
|
+
},
|
|
43
|
+
'50%': {
|
|
44
|
+
opacity: 0.8
|
|
45
|
+
},
|
|
46
|
+
'100%': {
|
|
47
|
+
opacity: 0,
|
|
48
|
+
left: `var(--end-x)`,
|
|
49
|
+
top: `var(--end-y)`,
|
|
50
|
+
width: `var(--end-size)`,
|
|
51
|
+
height: `var(--end-size)`,
|
|
52
|
+
background: `var(--background)`,
|
|
53
|
+
border: `var(--border)`
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
const useStyle = (prefixCls, hashId, token) => {
|
|
57
|
+
const sharedConfig = {
|
|
58
|
+
theme: DEFAULT_THEME,
|
|
59
|
+
token
|
|
60
|
+
};
|
|
61
|
+
(0, _cssinjs.useStyleRegister)({
|
|
62
|
+
...sharedConfig,
|
|
63
|
+
path: ['HappyWorkTheme', 'target']
|
|
64
|
+
}, () => ({
|
|
65
|
+
// ======================== Target ========================
|
|
66
|
+
[`[${TARGET_ATTR}-${hashId}], & [${TARGET_ATTR}-${hashId}]`]: {
|
|
67
|
+
animationName: antWaveTargetEffect,
|
|
68
|
+
animationDuration: `0.45s`,
|
|
69
|
+
animationTimingFunction: 'ease-in-out',
|
|
70
|
+
animationFillMode: 'backwards'
|
|
71
|
+
}
|
|
72
|
+
}));
|
|
73
|
+
(0, _cssinjs.useStyleRegister)({
|
|
74
|
+
...sharedConfig,
|
|
75
|
+
hashId,
|
|
76
|
+
path: ['HappyWorkTheme', 'effect']
|
|
77
|
+
}, () => {
|
|
78
|
+
const dotPrefixCls = `.${prefixCls}`;
|
|
79
|
+
return [{
|
|
80
|
+
// ========================= Dots =========================
|
|
81
|
+
[dotPrefixCls]: {
|
|
82
|
+
position: 'fixed',
|
|
83
|
+
pointerEvents: 'none',
|
|
84
|
+
zIndex: 999999,
|
|
85
|
+
[`${dotPrefixCls}-dot`]: {
|
|
86
|
+
boxSizing: 'border-box',
|
|
87
|
+
position: 'absolute',
|
|
88
|
+
borderRadius: '100%',
|
|
89
|
+
opacity: 0,
|
|
90
|
+
transform: 'translate3d(-50%, -50%, 0)',
|
|
91
|
+
// Start Position
|
|
92
|
+
zIndex: 999999,
|
|
93
|
+
// =================== Basic Motion ===================
|
|
94
|
+
'&.happy-in-out': {
|
|
95
|
+
animationName: antWaveDotEffect,
|
|
96
|
+
animationDuration: token.motionDurationSlow,
|
|
97
|
+
// animationDuration: '10s',
|
|
98
|
+
animationTimingFunction: 'linear',
|
|
99
|
+
animationFillMode: 'backwards'
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}];
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
var _default = exports.default = useStyle;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
|
|
4
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = void 0;
|
|
9
|
+
var _antd = require("@996-design/996-components");
|
|
10
|
+
var _useEvent = _interopRequireDefault(require("@rc-component/util/lib/hooks/useEvent"));
|
|
11
|
+
var React = _interopRequireWildcard(require("react"));
|
|
12
|
+
var _DotEffect = _interopRequireDefault(require("./DotEffect"));
|
|
13
|
+
var _render = require("@rc-component/util/lib/React/render");
|
|
14
|
+
const HappyProvider = props => {
|
|
15
|
+
const {
|
|
16
|
+
children,
|
|
17
|
+
disabled
|
|
18
|
+
} = props;
|
|
19
|
+
const showEffect = (0, _useEvent.default)((target, info) => {
|
|
20
|
+
const {
|
|
21
|
+
token,
|
|
22
|
+
hashId
|
|
23
|
+
} = info;
|
|
24
|
+
|
|
25
|
+
// Create holder
|
|
26
|
+
const holder = document.createElement('div');
|
|
27
|
+
holder.style.position = 'absolute';
|
|
28
|
+
holder.style.left = `0px`;
|
|
29
|
+
holder.style.top = `0px`;
|
|
30
|
+
document.body.appendChild(holder);
|
|
31
|
+
(0, _render.render)( /*#__PURE__*/React.createElement(_DotEffect.default, {
|
|
32
|
+
target: target,
|
|
33
|
+
token: token,
|
|
34
|
+
hashId: hashId,
|
|
35
|
+
onFinish: () => {
|
|
36
|
+
(0, _render.unmount)(holder).then(() => {
|
|
37
|
+
holder.remove();
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}), holder);
|
|
41
|
+
});
|
|
42
|
+
const memoizedWaveConfig = React.useMemo(() => {
|
|
43
|
+
if (disabled) {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
showEffect
|
|
48
|
+
};
|
|
49
|
+
}, [disabled]);
|
|
50
|
+
return /*#__PURE__*/React.createElement(_antd.ConfigProvider, {
|
|
51
|
+
wave: memoizedWaveConfig
|
|
52
|
+
}, children);
|
|
53
|
+
};
|
|
54
|
+
var _default = exports.default = HappyProvider;
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
Object.defineProperty(exports, "HappyProvider", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return _HappyProvider.default;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
var _HappyProvider = _interopRequireDefault(require("./HappyProvider"));
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@996-design/996-happy-work-theme",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Expressive interaction theme for 996 Design",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"996-design",
|
|
8
|
+
"react",
|
|
9
|
+
"theme"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"main": "./lib/index",
|
|
13
|
+
"module": "./es/index",
|
|
14
|
+
"files": [
|
|
15
|
+
"es",
|
|
16
|
+
"lib",
|
|
17
|
+
"NOTICE.md",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"check": "bun -e \"await import('./es/index.js')\"",
|
|
23
|
+
"build": "bun run check",
|
|
24
|
+
"check:standalone": "bun scripts/check-standalone.ts"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@996-design/996-cssinjs": "^1.0.0",
|
|
28
|
+
"@996-design/996-fast-color": "^1.0.0",
|
|
29
|
+
"@babel/runtime": "^7.18.3",
|
|
30
|
+
"@rc-component/motion": "^1.1.6",
|
|
31
|
+
"@rc-component/util": "^1.5.0",
|
|
32
|
+
"clsx": "^2.1.1"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@996-design/996-components": "^1.0.0",
|
|
36
|
+
"react": ">=18.0.0",
|
|
37
|
+
"react-dom": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=8.x"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public",
|
|
44
|
+
"registry": "https://registry.npmjs.org"
|
|
45
|
+
},
|
|
46
|
+
"packageManager": "pnpm@9.15.0",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+ssh://git@gitlab.asdnc.cn/996-design/996-happy-work-theme.git"
|
|
50
|
+
}
|
|
51
|
+
}
|