Compare commits
633 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
2cda7bde09 | |
|
|
6255f1632c | |
|
|
ef51f7f1cc | |
|
|
252a0e2563 | |
|
|
bbb7670df1 | |
|
|
fcdda4a0be | |
|
|
79b63b41c6 | |
|
|
56d9b358f2 | |
|
|
b58e73d51a | |
|
|
e8ec520d9a | |
|
|
ce56f0c8f6 | |
|
|
723bb47a42 | |
|
|
57ea8a65d4 | |
|
|
7e5008080d | |
|
|
74b3018945 | |
|
|
28b1c4f62d | |
|
|
04f6ba33c7 | |
|
|
35c1ad9c80 | |
|
|
e9f8ea7903 | |
|
|
27acc9a80e | |
|
|
43ad5dec90 | |
|
|
aa8802679f | |
|
|
aa211fd789 | |
|
|
61d39be86f | |
|
|
b80bd37f6e | |
|
|
3db5e191b8 | |
|
|
45c7109212 | |
|
|
b58d0fb723 | |
|
|
a21a1ca533 | |
|
|
4a37016f4e | |
|
|
5d6ebb30ac | |
|
|
888457238d | |
|
|
b0d9309471 | |
|
|
f64d0718ae | |
|
|
968317ca9f | |
|
|
22dad3ba6d | |
|
|
4f7bd6f28d | |
|
|
12fcfa15b1 | |
|
|
e46d537d45 | |
|
|
ac9d1b0a7f | |
|
|
2c7008d786 | |
|
|
49d4b3166e | |
|
|
339fb4a230 | |
|
|
bb443a05e2 | |
|
|
3a79f72816 | |
|
|
89eec07145 | |
|
|
c69a6d29b2 | |
|
|
2666cb79ab | |
|
|
35d5185634 | |
|
|
4492086aac | |
|
|
4b7c6ac02c | |
|
|
208b8d3085 | |
|
|
f41fec26ba | |
|
|
9118d6cd42 | |
|
|
5c7aaf0dd6 | |
|
|
b82d8dd2ad | |
|
|
1d0fa8533a | |
|
|
4318147fc6 | |
|
|
312bcc5127 | |
|
|
6594fe3964 | |
|
|
82f28ce3d0 | |
|
|
6e2c5a6a83 | |
|
|
307148e2df | |
|
|
afebeb9ffa | |
|
|
aeda2637f6 | |
|
|
f42d8ad1ce | |
|
|
33a0708eb4 | |
|
|
085eb398a4 | |
|
|
9c27414cd0 | |
|
|
014e86bd27 | |
|
|
40ad45bc05 | |
|
|
1c82940160 | |
|
|
7ce0f115d5 | |
|
|
54434b0931 | |
|
|
e01f26c541 | |
|
|
9a45b35511 | |
|
|
8e8073d17e | |
|
|
d6cc262c3a | |
|
|
9d7c171940 | |
|
|
48a3ceb921 | |
|
|
23c620ea3a | |
|
|
752686e334 | |
|
|
9b0f0e71e7 | |
|
|
42d33e963c | |
|
|
ed27700ef4 | |
|
|
eedd7f3302 | |
|
|
c28c38d02e | |
|
|
162d1fcf95 | |
|
|
4815ee6f20 | |
|
|
2b41e56520 | |
|
|
ab874ffd4b | |
|
|
61ade6b8ec | |
|
|
4d35b8caa3 | |
|
|
040af82eb9 | |
|
|
a8c72fc5e7 | |
|
|
c25736d57d | |
|
|
4a48bfc66b | |
|
|
4138d3c822 | |
|
|
dec67a6d65 | |
|
|
85c48c0566 | |
|
|
966bc1004c | |
|
|
457d0fde0b | |
|
|
ff184b3969 | |
|
|
75886a8cbf | |
|
|
2b0c2da232 | |
|
|
ffd4d8fe92 | |
|
|
49e1323baa | |
|
|
35d1de9ea6 | |
|
|
fa46999963 | |
|
|
01300d01da | |
|
|
9a5f83ed06 | |
|
|
ce6b7f3b4e | |
|
|
4a2f95fe88 | |
|
|
7c73beb309 | |
|
|
0cbf3ca354 | |
|
|
7afb7ce465 | |
|
|
9cc73011c5 | |
|
|
4b21210700 | |
|
|
a80ca17461 | |
|
|
ad8d32ab09 | |
|
|
d870f3f8e0 | |
|
|
c717473568 | |
|
|
1e07544e74 | |
|
|
8ab008d255 | |
|
|
f034a7759e | |
|
|
3f33cefa81 | |
|
|
81e43c56ef | |
|
|
f400c803e1 | |
|
|
502c11cc4c | |
|
|
2cdb69f706 | |
|
|
2eed8e62ec | |
|
|
61c0d0cc9d | |
|
|
d7f9bd27dc | |
|
|
4ed2868137 | |
|
|
8696e01039 | |
|
|
a3fd390619 | |
|
|
a9198e44df | |
|
|
509ec682f2 | |
|
|
73f0a29acf | |
|
|
f93dd9170d | |
|
|
cfa0a68568 | |
|
|
afcff32fcc | |
|
|
b989cf2d97 | |
|
|
0e6fd652b8 | |
|
|
6625d39118 | |
|
|
67efafca4a | |
|
|
a8a774a5ff | |
|
|
1fc109704b | |
|
|
d140523c89 | |
|
|
91fe1b0d71 | |
|
|
c1b4941def | |
|
|
af383a34bc | |
|
|
1dccce4e16 | |
|
|
8aa8e5a778 | |
|
|
42952b713a | |
|
|
07c36192b8 | |
|
|
562623c091 | |
|
|
e362571719 | |
|
|
e32ae99bfd | |
|
|
ccbeb44264 | |
|
|
3499b11a47 | |
|
|
dae7262f13 | |
|
|
8cf6be11f7 | |
|
|
2510608665 | |
|
|
b22875db33 | |
|
|
2bdeed1432 | |
|
|
49a6b588c1 | |
|
|
bd080cde2a | |
|
|
c1454bc7b5 | |
|
|
61402142f8 | |
|
|
242982301d | |
|
|
9bdf882517 | |
|
|
88ec06b717 | |
|
|
6c39bec277 | |
|
|
82f407d396 | |
|
|
bfcdba251f | |
|
|
0abcd25b2a | |
|
|
72b8d09b92 | |
|
|
b4c29d927b | |
|
|
bf917f36eb | |
|
|
0f9c80b86e | |
|
|
61e5746b0d | |
|
|
7786a81e76 | |
|
|
6f89185fe1 | |
|
|
449adb03df | |
|
|
4428423be4 | |
|
|
380fcd4aa9 | |
|
|
4a1296bb3f | |
|
|
b9c65656db | |
|
|
f1bcf2093a | |
|
|
398710cf1b | |
|
|
858c4ec409 | |
|
|
77eab3637c | |
|
|
664711373c | |
|
|
21d85ea82c | |
|
|
b3ef24963f | |
|
|
f953482a4d | |
|
|
a9f9899185 | |
|
|
c99e9b00bd | |
|
|
3084ced55f | |
|
|
c5ea668e88 | |
|
|
3c5fb84542 | |
|
|
2f7f0e6928 | |
|
|
2c4d4650da | |
|
|
0f1ffc99cd | |
|
|
83d6c93765 | |
|
|
a6c945caaa | |
|
|
d0f7c34497 | |
|
|
2b8f2adcbf | |
|
|
f25bf6e189 | |
|
|
93415d0442 | |
|
|
d185a3af21 | |
|
|
d3800b54a3 | |
|
|
b7eed32484 | |
|
|
46be19531d | |
|
|
089548ed74 | |
|
|
ce72484623 | |
|
|
31d00bf7d1 | |
|
|
deada0aeb8 | |
|
|
418d8b33dd | |
|
|
3c05b7704b | |
|
|
1254677805 | |
|
|
f7cc27b7ad | |
|
|
9855e43805 | |
|
|
1f01ae0728 | |
|
|
9464158c0c | |
|
|
13a4470fd8 | |
|
|
84fb4b4b3d | |
|
|
106750ed62 | |
|
|
cac650737c | |
|
|
95fa2547fe | |
|
|
70102039c4 | |
|
|
7a0c2a384c | |
|
|
72e148cae8 | |
|
|
6c13c964c5 | |
|
|
367ddff30e | |
|
|
e3ad856543 | |
|
|
61df5e656f | |
|
|
439fe840b4 | |
|
|
197a00f6ea | |
|
|
3f99a7e985 | |
|
|
9c0311546b | |
|
|
7f5b630d72 | |
|
|
65ccb447c9 | |
|
|
18405e13b2 | |
|
|
9e78b4a985 | |
|
|
6cf5d79e2e | |
|
|
1abc33a9e7 | |
|
|
e39e998ecc | |
|
|
cfd4aadc29 | |
|
|
38dd977b4c | |
|
|
72f9262d0a | |
|
|
c2d71fa7c3 | |
|
|
969dadb265 | |
|
|
9c3bf46108 | |
|
|
141f0100d2 | |
|
|
6ced156342 | |
|
|
42608a5121 | |
|
|
22b21cdac8 | |
|
|
80a5b531f6 | |
|
|
cacbde3e03 | |
|
|
e3f09a18a1 | |
|
|
7bfe30d280 | |
|
|
53dc5daaaf | |
|
|
531ae16553 | |
|
|
d2b6eb9d94 | |
|
|
27a4d8ceff | |
|
|
12074a0dca | |
|
|
8f95cdd5a3 | |
|
|
cb0abe5fd5 | |
|
|
ed87500e77 | |
|
|
c01630113d | |
|
|
5279d1289a | |
|
|
9d406ab7ba | |
|
|
826c183d0b | |
|
|
bc576d6255 | |
|
|
2c39273cc6 | |
|
|
b49540a339 | |
|
|
37059c38fd | |
|
|
4fe57fa322 | |
|
|
a3ccdeffc2 | |
|
|
e8c2860c7d | |
|
|
f1f89191ee | |
|
|
0c863c1803 | |
|
|
7591d5c3e6 | |
|
|
4ea318be30 | |
|
|
5fd00c05b5 | |
|
|
a2e50dc43e | |
|
|
0a738a36b6 | |
|
|
10f990d534 | |
|
|
8b5d71663a | |
|
|
058e8ec542 | |
|
|
f70b04c74f | |
|
|
2ce4e7da82 | |
|
|
fc5181d1d8 | |
|
|
994707bd50 | |
|
|
e9d26f2a7d | |
|
|
7fd0e4a225 | |
|
|
e229248053 | |
|
|
10159941b9 | |
|
|
9c26ab05d3 | |
|
|
6861a77d29 | |
|
|
308c30c738 | |
|
|
afc1b84ed0 | |
|
|
58f6d6ee28 | |
|
|
7270855ce8 | |
|
|
19f0aea965 | |
|
|
6f032c9722 | |
|
|
6be947aeb9 | |
|
|
45ede0853b | |
|
|
afb2619a81 | |
|
|
2bf85998ba | |
|
|
42cb6871ee | |
|
|
508e8e3706 | |
|
|
fe3c2bdf89 | |
|
|
64b9347fce | |
|
|
96f940d834 | |
|
|
d3d37f08a2 | |
|
|
d49082008e | |
|
|
1d0a2737a4 | |
|
|
c9f0841628 | |
|
|
03760e3e3d | |
|
|
c36f7e701f | |
|
|
83017269ab | |
|
|
1c7b7a35f6 | |
|
|
28263bbe33 | |
|
|
5aad611d4c | |
|
|
429172b345 | |
|
|
e5787c2ed2 | |
|
|
698c0ff3b4 | |
|
|
db4148af5a | |
|
|
21cf533664 | |
|
|
4c8cc0129f | |
|
|
a694167f37 | |
|
|
3ed455b3f7 | |
|
|
e49e72d24a | |
|
|
149cedd262 | |
|
|
8bf8515933 | |
|
|
0d24755ee3 | |
|
|
f2f7cfd94d | |
|
|
d933605b56 | |
|
|
266482deca | |
|
|
fd8af2322c | |
|
|
9fdc297686 | |
|
|
fded418b8a | |
|
|
feffe70ebb | |
|
|
02ed988cba | |
|
|
6eb4d8f5c5 | |
|
|
b61c88e5df | |
|
|
ac93c476d1 | |
|
|
db46965761 | |
|
|
5578c14a8e | |
|
|
69640c0af8 | |
|
|
0df6abe3a7 | |
|
|
b103b7570d | |
|
|
61d6312783 | |
|
|
98be7071e5 | |
|
|
5439e0c164 | |
|
|
9be58078d2 | |
|
|
063c06f9c6 | |
|
|
43009f0c59 | |
|
|
678ec079bc | |
|
|
eba8f6b960 | |
|
|
760c4c2695 | |
|
|
8ab50b1757 | |
|
|
b0990b7323 | |
|
|
5376014b33 | |
|
|
bccd9204e8 | |
|
|
8932aff13f | |
|
|
de00607dc7 | |
|
|
118ae9a50b | |
|
|
0683ede0fb | |
|
|
838aa71b4b | |
|
|
2bf654da71 | |
|
|
515ab9e38c | |
|
|
d4f2e97ca9 | |
|
|
be90b53527 | |
|
|
0b85b84dcf | |
|
|
4e277ea9bb | |
|
|
bf97057c2c | |
|
|
637656b0c3 | |
|
|
0e1b7fd94f | |
|
|
33d1553908 | |
|
|
2eaad16331 | |
|
|
58998c4978 | |
|
|
7db4265616 | |
|
|
8a3ed32254 | |
|
|
1151bdad0f | |
|
|
84037f8eef | |
|
|
3e46f27b59 | |
|
|
719847901e | |
|
|
fbfec0a062 | |
|
|
9a5cbeee50 | |
|
|
b0125de963 | |
|
|
bf1e6fec95 | |
|
|
5f1cb0d240 | |
|
|
f6daa8d28f | |
|
|
48ab5a2f99 | |
|
|
ae27d430e5 | |
|
|
9dbe035efe | |
|
|
6d60eba281 | |
|
|
bfcbd5bdef | |
|
|
4d2a37fcd3 | |
|
|
dcc3bb10cb | |
|
|
7d1418d9f4 | |
|
|
3653f37d6b | |
|
|
f265a7bb02 | |
|
|
89314fb4d0 | |
|
|
b1f38eb2ec | |
|
|
d95861aa90 | |
|
|
5cc85f3cc1 | |
|
|
f3935ebb4f | |
|
|
11c52d487d | |
|
|
295c417f49 | |
|
|
27c5a6c96a | |
|
|
fc7cadb3f9 | |
|
|
ad1d6443f2 | |
|
|
77d0d188f0 | |
|
|
b22bd85828 | |
|
|
f819a1b209 | |
|
|
dde2ff140d | |
|
|
f429148734 | |
|
|
23a213acda | |
|
|
d241a06f9d | |
|
|
1d9a521669 | |
|
|
a04cae1221 | |
|
|
add208aefb | |
|
|
355c41b4aa | |
|
|
3f5f3ecabf | |
|
|
41a455f881 | |
|
|
eda7247c2c | |
|
|
f85485e733 | |
|
|
a1e967dfc2 | |
|
|
6058ca5576 | |
|
|
dd063b8275 | |
|
|
9c6a9fb47c | |
|
|
516b2e5a0c | |
|
|
d3a919b0bf | |
|
|
261e7d0857 | |
|
|
b393a0a2dd | |
|
|
2b80870461 | |
|
|
33a47bc27a | |
|
|
53b146ab88 | |
|
|
f3ffcdbc7e | |
|
|
37b5d02b6d | |
|
|
1b51e6ffb3 | |
|
|
f39d4894e4 | |
|
|
975d70e7ed | |
|
|
c301c63e8b | |
|
|
e04f73dfef | |
|
|
8fcb3fdfe3 | |
|
|
c7b15a96a8 | |
|
|
e8918ce589 | |
|
|
62dda88ea0 | |
|
|
3741931363 | |
|
|
7af22a70f9 | |
|
|
087dfa2f1b | |
|
|
799eeed346 | |
|
|
124aae72a4 | |
|
|
39e5824699 | |
|
|
9df8317ece | |
|
|
62e7f94aba | |
|
|
47385347ee | |
|
|
6240ab2885 | |
|
|
328e42a9be | |
|
|
634675e032 | |
|
|
7ddf882a99 | |
|
|
895b43338d | |
|
|
9f53d53cb8 | |
|
|
cd47a277da | |
|
|
adec5211f1 | |
|
|
a6a270b44a | |
|
|
8472c25633 | |
|
|
c1ed77f67a | |
|
|
df6a1fdb71 | |
|
|
321989b9c2 | |
|
|
2f51f2d3af | |
|
|
dc9987aea8 | |
|
|
3715ded674 | |
|
|
04e3819b0b | |
|
|
587c1ca89d | |
|
|
8a233d7c3a | |
|
|
0464c84afc | |
|
|
7e29eb2163 | |
|
|
92795a828f | |
|
|
44e5d09f22 | |
|
|
f6afd7fffc | |
|
|
7de1be6a9a | |
|
|
19ec975deb | |
|
|
4ccb1c3e19 | |
|
|
68d295d7ef | |
|
|
bbfb3cef7d | |
|
|
e9ce4eb2d5 | |
|
|
cb08f8551a | |
|
|
f0e5da3b69 | |
|
|
2f049329a5 | |
|
|
350dbfedbb | |
|
|
4b2ffd7127 | |
|
|
a5389a22ea | |
|
|
7a34c99935 | |
|
|
dc480bd4b3 | |
|
|
1d774507c0 | |
|
|
80918cba98 | |
|
|
197e209b4e | |
|
|
82c4c8f0d3 | |
|
|
23b81b0e17 | |
|
|
f0649999fb | |
|
|
e2d4f8c2e3 | |
|
|
de77b0175d | |
|
|
26f98b7b10 | |
|
|
a9fbf98f0e | |
|
|
fee7c04d67 | |
|
|
8658806e3f | |
|
|
4f3ce35e74 | |
|
|
69c17dc255 | |
|
|
a40816880a | |
|
|
2c1afa5e72 | |
|
|
81e26a900e | |
|
|
fc0f0d8a96 | |
|
|
a205615af9 | |
|
|
9b5a07220b | |
|
|
47c84cdbad | |
|
|
989bedda47 | |
|
|
dd57d1e305 | |
|
|
158cca7f8a | |
|
|
8099391e97 | |
|
|
7939eb1718 | |
|
|
34373ca05d | |
|
|
4ef70b1503 | |
|
|
31776a2cf8 | |
|
|
31ca070a18 | |
|
|
1d01df4b85 | |
|
|
337d958c67 | |
|
|
94e981e00b | |
|
|
221b203cbb | |
|
|
dfe462ec6f | |
|
|
8110d8cbbd | |
|
|
c903de3c0e | |
|
|
44d7917afd | |
|
|
73ce708868 | |
|
|
d211688059 | |
|
|
9d5343ae9c | |
|
|
2d54e2124f | |
|
|
b5d7d582cd | |
|
|
0399ce0ec7 | |
|
|
94c2887c81 | |
|
|
75e4b72c21 | |
|
|
287a8d0c4e | |
|
|
1e4e3cb3b4 | |
|
|
e64a19a05a | |
|
|
6c735fee67 | |
|
|
f8ddc430cf | |
|
|
641714734a | |
|
|
46862d749b | |
|
|
1f33babc89 | |
|
|
b2841a27ae | |
|
|
c0d7d041b4 | |
|
|
10b52e0072 | |
|
|
d64c6a8d53 | |
|
|
2eb071ce30 | |
|
|
6bd8a9b984 | |
|
|
040956819f | |
|
|
0f634e27d5 | |
|
|
9815f817d4 | |
|
|
d933f3edd5 | |
|
|
dd60465373 | |
|
|
60a8be3014 | |
|
|
a26ac3e9f4 | |
|
|
e7fdb128e3 | |
|
|
8d0155878e | |
|
|
e5e98e5776 | |
|
|
05357a8c3f | |
|
|
86450c2a1b | |
|
|
9ea87fb973 | |
|
|
dae579a88e | |
|
|
58e8fd3e2e | |
|
|
aae9a48dd0 | |
|
|
09c5a3e53f | |
|
|
e42fdd7a06 | |
|
|
8b6a771318 | |
|
|
bb4b86a82e | |
|
|
9280b418d7 | |
|
|
efe600e89e | |
|
|
8b34bdfa5e | |
|
|
3b1d48b4b4 | |
|
|
dd1a535943 | |
|
|
5b583962db | |
|
|
37d35f7801 | |
|
|
a4b6c0aee4 | |
|
|
03f559a0dc | |
|
|
bf1226d3bb | |
|
|
527dec6078 | |
|
|
af9371fe6f | |
|
|
5c136e0286 | |
|
|
a32d29149f | |
|
|
e6a46b43ac | |
|
|
9d1e5d72b5 | |
|
|
63a688f94c | |
|
|
21f9a1178a | |
|
|
76573fc252 | |
|
|
208c7ac6de | |
|
|
44be8722f8 | |
|
|
a435e2c090 | |
|
|
8475542b2f | |
|
|
40a65d19da | |
|
|
8ad9317d11 | |
|
|
76585f75b9 | |
|
|
3656dd5dbb | |
|
|
7b60f2a8e6 | |
|
|
5cf2707e11 | |
|
|
d716421745 | |
|
|
c6f692222d | |
|
|
c1a1b93553 | |
|
|
6e41fbd01f | |
|
|
093fa555ba | |
|
|
e9d41efcec | |
|
|
d5348f0361 | |
|
|
e4f678f665 | |
|
|
f00b9535d2 | |
|
|
a1ac22443e | |
|
|
4eb8088645 | |
|
|
353f470c11 | |
|
|
73f7f9e3b9 | |
|
|
5bc17f0578 | |
|
|
dbe7fe78ce | |
|
|
6e1f30666b | |
|
|
c4a61f210f | |
|
|
0244e7f5b4 | |
|
|
3aedf48eaf | |
|
|
78045b4b5b | |
|
|
894a5b955c | |
|
|
a8dbea7c32 | |
|
|
1d08e80165 |
|
|
@ -1,195 +1,36 @@
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const defaultVars = require('./scripts/default-vars');
|
|
||||||
const darkVars = require('./scripts/dark-vars');
|
|
||||||
const compactVars = require('./scripts/compact-vars');
|
|
||||||
|
|
||||||
function generateThemeFileContent(theme) {
|
const restCssPath = path.join(process.cwd(), 'components', 'style', 'reset.css');
|
||||||
return `const { ${theme}ThemeSingle } = require('./theme');\nconst defaultTheme = require('./default-theme');\n
|
const tokenStatisticPath = path.join(process.cwd(), 'components', 'version', 'token.json');
|
||||||
module.exports = {
|
const tokenMetaPath = path.join(process.cwd(), 'components', 'version', 'token-meta.json');
|
||||||
...defaultTheme,
|
|
||||||
...${theme}ThemeSingle
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need compile additional content for antd user
|
|
||||||
function finalizeCompile() {
|
function finalizeCompile() {
|
||||||
|
if (fs.existsSync(path.join(__dirname, './es'))) {
|
||||||
|
fs.copyFileSync(restCssPath, path.join(process.cwd(), 'es', 'style', 'reset.css'));
|
||||||
|
fs.copyFileSync(tokenStatisticPath, path.join(process.cwd(), 'es', 'version', 'token.json'));
|
||||||
|
fs.copyFileSync(tokenMetaPath, path.join(process.cwd(), 'es', 'version', 'token-meta.json'));
|
||||||
|
}
|
||||||
|
|
||||||
if (fs.existsSync(path.join(__dirname, './lib'))) {
|
if (fs.existsSync(path.join(__dirname, './lib'))) {
|
||||||
// Build a entry less file to dist/antd.less
|
fs.copyFileSync(restCssPath, path.join(process.cwd(), 'lib', 'style', 'reset.css'));
|
||||||
const componentsPath = path.join(process.cwd(), 'components');
|
fs.copyFileSync(tokenStatisticPath, path.join(process.cwd(), 'lib', 'version', 'token.json'));
|
||||||
let componentsLessContent = '';
|
fs.copyFileSync(tokenMetaPath, path.join(process.cwd(), 'lib', 'version', 'token-meta.json'));
|
||||||
// Build components in one file: lib/style/components.less
|
|
||||||
fs.readdir(componentsPath, (err, files) => {
|
|
||||||
files.forEach(file => {
|
|
||||||
if (fs.existsSync(path.join(componentsPath, file, 'style', 'index.less'))) {
|
|
||||||
componentsLessContent += `@import "../${path.posix.join(
|
|
||||||
file,
|
|
||||||
'style',
|
|
||||||
'index-pure.less',
|
|
||||||
)}";\n`;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(process.cwd(), 'lib', 'style', 'components.less'),
|
|
||||||
componentsLessContent,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildThemeFile(theme, vars) {
|
|
||||||
// Build less entry file: dist/antd.${theme}.less
|
|
||||||
if (theme !== 'default') {
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(process.cwd(), 'dist', `antd.${theme}.less`),
|
|
||||||
`@import "../lib/style/${theme}.less";\n@import "../lib/style/components.less";`,
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(`Built a entry less file to dist/antd.${theme}.less`);
|
|
||||||
} else {
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(process.cwd(), 'dist', `default-theme.js`),
|
|
||||||
`module.exports = ${JSON.stringify(vars, null, 2)};\n`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build ${theme}.js: dist/${theme}-theme.js, for less-loader
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(process.cwd(), 'dist', `theme.js`),
|
|
||||||
`const ${theme}ThemeSingle = ${JSON.stringify(vars, null, 2)};\n`,
|
|
||||||
{
|
|
||||||
flag: 'a',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(process.cwd(), 'dist', `${theme}-theme.js`),
|
|
||||||
generateThemeFileContent(theme),
|
|
||||||
);
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(`Built a ${theme} theme js file to dist/${theme}-theme.js`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function finalizeDist() {
|
function finalizeDist() {
|
||||||
if (fs.existsSync(path.join(__dirname, './dist'))) {
|
if (fs.existsSync(path.join(__dirname, './dist'))) {
|
||||||
// Build less entry file: dist/antd.less
|
fs.copyFileSync(restCssPath, path.join(process.cwd(), 'dist', 'reset.css'));
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(process.cwd(), 'dist', 'antd.less'),
|
|
||||||
'@import "../lib/style/default.less";\n@import "../lib/style/components.less";',
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(process.cwd(), 'dist', 'theme.js'),
|
|
||||||
`const defaultTheme = require('./default-theme.js');\n`,
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('Built a entry less file to dist/antd.less');
|
|
||||||
buildThemeFile('default', defaultVars);
|
|
||||||
buildThemeFile('dark', darkVars);
|
|
||||||
buildThemeFile('compact', compactVars);
|
|
||||||
buildThemeFile('variable', {});
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(process.cwd(), 'dist', `theme.js`),
|
|
||||||
`
|
|
||||||
function getThemeVariables(options = {}) {
|
|
||||||
let themeVar = {
|
|
||||||
'hack': \`true;@import "\${require.resolve('ant-design-vue/lib/style/color/colorPalette.less')}";\`,
|
|
||||||
...defaultTheme
|
|
||||||
};
|
|
||||||
if(options.dark) {
|
|
||||||
themeVar = {
|
|
||||||
...themeVar,
|
|
||||||
...darkThemeSingle
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if(options.compact){
|
|
||||||
themeVar = {
|
|
||||||
...themeVar,
|
|
||||||
...compactThemeSingle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return themeVar;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
darkThemeSingle,
|
|
||||||
compactThemeSingle,
|
|
||||||
getThemeVariables
|
|
||||||
}`,
|
|
||||||
{
|
|
||||||
flag: 'a',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isComponentStyleEntry(file) {
|
|
||||||
return file.path.match(/style(\/|\\)index\.tsx/);
|
|
||||||
}
|
|
||||||
|
|
||||||
function needTransformStyle(content) {
|
|
||||||
return content.includes('../../style/index.less') || content.includes('./index.less');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
compile: {
|
compile: {
|
||||||
includeLessFile: [/(\/|\\)components(\/|\\)style(\/|\\)default.less$/],
|
|
||||||
transformTSFile(file) {
|
|
||||||
if (isComponentStyleEntry(file)) {
|
|
||||||
let content = file.contents.toString();
|
|
||||||
|
|
||||||
if (needTransformStyle(content)) {
|
|
||||||
const cloneFile = file.clone();
|
|
||||||
|
|
||||||
// Origin
|
|
||||||
content = content.replace('../../style/index.less', '../../style/default.less');
|
|
||||||
cloneFile.contents = Buffer.from(content);
|
|
||||||
|
|
||||||
return cloneFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
transformFile(file) {
|
|
||||||
if (isComponentStyleEntry(file)) {
|
|
||||||
const indexLessFilePath = file.path.replace('index.tsx', 'index.less');
|
|
||||||
|
|
||||||
if (fs.existsSync(indexLessFilePath)) {
|
|
||||||
// We put origin `index.less` file to `index-pure.less`
|
|
||||||
const pureFile = file.clone();
|
|
||||||
pureFile.contents = Buffer.from(fs.readFileSync(indexLessFilePath, 'utf8'));
|
|
||||||
pureFile.path = pureFile.path.replace('index.tsx', 'index-pure.less');
|
|
||||||
|
|
||||||
// Rewrite `index.less` file with `root-entry-name`
|
|
||||||
const indexLessFile = file.clone();
|
|
||||||
indexLessFile.contents = Buffer.from(
|
|
||||||
[
|
|
||||||
// Inject variable
|
|
||||||
'@root-entry-name: default;',
|
|
||||||
// Point to origin file
|
|
||||||
"@import './index-pure.less';",
|
|
||||||
].join('\n\n'),
|
|
||||||
);
|
|
||||||
indexLessFile.path = indexLessFile.path.replace('index.tsx', 'index.less');
|
|
||||||
|
|
||||||
return [indexLessFile, pureFile];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
lessConfig: {
|
|
||||||
modifyVars: {
|
|
||||||
'root-entry-name': 'default',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
finalize: finalizeCompile,
|
finalize: finalizeCompile,
|
||||||
},
|
},
|
||||||
dist: {
|
dist: {
|
||||||
finalize: finalizeDist,
|
finalize: finalizeDist,
|
||||||
},
|
},
|
||||||
generateThemeFileContent,
|
|
||||||
bail: true,
|
bail: true,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,7 @@ es/
|
||||||
lib/
|
lib/
|
||||||
_site/
|
_site/
|
||||||
dist/
|
dist/
|
||||||
components/version/version.tsx
|
site/dist/
|
||||||
|
components/version/version.ts
|
||||||
|
site/src/router/demoRoutes.js
|
||||||
|
locale/
|
||||||
32
.eslintrc.js
32
.eslintrc.js
|
|
@ -15,9 +15,21 @@ module.exports = {
|
||||||
'plugin:vue/vue3-recommended',
|
'plugin:vue/vue3-recommended',
|
||||||
'plugin:import/recommended',
|
'plugin:import/recommended',
|
||||||
'plugin:import/typescript',
|
'plugin:import/typescript',
|
||||||
'prettier',
|
'@vue/typescript/recommended',
|
||||||
|
'@vue/prettier',
|
||||||
|
// 'prettier',
|
||||||
],
|
],
|
||||||
|
// extends: [
|
||||||
|
// 'eslint:recommended',
|
||||||
|
// 'plugin:vue/vue3-recommended',
|
||||||
|
// '@vue/typescript/recommended',
|
||||||
|
// '@vue/prettier',
|
||||||
|
// ],
|
||||||
plugins: ['markdown', 'jest', '@typescript-eslint', 'import'],
|
plugins: ['markdown', 'jest', '@typescript-eslint', 'import'],
|
||||||
|
globals: {
|
||||||
|
h: true,
|
||||||
|
defineProps: 'readonly',
|
||||||
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ['*.md'],
|
files: ['*.md'],
|
||||||
|
|
@ -28,12 +40,11 @@ module.exports = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ['*.ts', '*.tsx'],
|
files: ['*.ts', '*.tsx'],
|
||||||
extends: ['@vue/typescript/recommended', '@vue/prettier', '@vue/prettier/@typescript-eslint'],
|
// extends: ['@vue/typescript/recommended', '@vue/prettier'],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
project: './tsconfig.json',
|
project: './tsconfig.json',
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'@typescript-eslint/no-explicit-any': 0,
|
|
||||||
'@typescript-eslint/ban-types': 0,
|
'@typescript-eslint/ban-types': 0,
|
||||||
'@typescript-eslint/consistent-type-imports': 'error',
|
'@typescript-eslint/consistent-type-imports': 'error',
|
||||||
'@typescript-eslint/explicit-module-boundary-types': 0,
|
'@typescript-eslint/explicit-module-boundary-types': 0,
|
||||||
|
|
@ -51,17 +62,21 @@ module.exports = {
|
||||||
parser: 'vue-eslint-parser',
|
parser: 'vue-eslint-parser',
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
|
ecmaVersion: 2021,
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': 'off',
|
'no-console': 'off',
|
||||||
'@typescript-eslint/no-unused-vars': [
|
'vue/no-reserved-component-names': 'off',
|
||||||
'error',
|
|
||||||
{ vars: 'all', args: 'after-used', ignoreRestSiblings: true },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
|
'@typescript-eslint/no-explicit-any': 0,
|
||||||
|
'@typescript-eslint/no-empty-function': 0,
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{ vars: 'all', args: 'after-used', ignoreRestSiblings: true, argsIgnorePattern: '^_' },
|
||||||
|
],
|
||||||
'import/no-named-as-default': 'off',
|
'import/no-named-as-default': 'off',
|
||||||
'import/namespace': [2, { allowComputed: true }],
|
'import/namespace': [2, { allowComputed: true }],
|
||||||
'import/no-named-as-default-member': 'off',
|
'import/no-named-as-default-member': 'off',
|
||||||
|
|
@ -94,7 +109,4 @@ module.exports = {
|
||||||
],
|
],
|
||||||
'vue/multi-word-component-names': 'off',
|
'vue/multi-word-component-names': 'off',
|
||||||
},
|
},
|
||||||
globals: {
|
|
||||||
h: true,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
blank_issues_enabled: false
|
blank_issues_enabled: true
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Create new issue
|
- name: Create new issue
|
||||||
url: https://vuecomponent.github.io/issue-helper/
|
url: https://vuecomponent.github.io/issue-helper/
|
||||||
|
|
@ -12,6 +12,3 @@ contact_links:
|
||||||
- name: Paypal
|
- name: Paypal
|
||||||
url: https://www.paypal.me/tangjinzhou
|
url: https://www.paypal.me/tangjinzhou
|
||||||
about: Love Ant Design Vue? Please consider supporting us via Paypal.
|
about: Love Ant Design Vue? Please consider supporting us via Paypal.
|
||||||
- name: 支付宝/微信 赞助
|
|
||||||
url: https://qn.antdv.com/alipay-and-wechat.png
|
|
||||||
about: Ant Design Vue 的健康持续发展需要您的支持,🙏
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
name: Build and Deploy to Cloudflare
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Build Application
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm install --force
|
||||||
|
|
||||||
|
- name: Build application
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: build-files
|
||||||
|
path: site/dist/
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Deploy to Cloudflare
|
||||||
|
needs: build
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
deployments: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout (for config files)
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
sparse-checkout: |
|
||||||
|
wrangler.jsonc
|
||||||
|
sparse-checkout-cone-mode: false
|
||||||
|
|
||||||
|
- name: Download build artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: build-files
|
||||||
|
path: site/dist/
|
||||||
|
|
||||||
|
- name: Deploy (Workers + Static Assets)
|
||||||
|
uses: cloudflare/wrangler-action@v3.14.1
|
||||||
|
with:
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
command: deploy --config wrangler.jsonc
|
||||||
|
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -10,7 +10,7 @@ jobs:
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: cache package-lock.json
|
- name: cache package-lock.json
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: package-temp-dir
|
path: package-temp-dir
|
||||||
key: lock-${{ github.sha }}
|
key: lock-${{ github.sha }}
|
||||||
|
|
@ -27,7 +27,7 @@ jobs:
|
||||||
|
|
||||||
- name: cache node_modules
|
- name: cache node_modules
|
||||||
id: node_modules_cache_id
|
id: node_modules_cache_id
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: node_modules
|
path: node_modules
|
||||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||||
|
|
@ -52,13 +52,13 @@ jobs:
|
||||||
# submodules: true
|
# submodules: true
|
||||||
|
|
||||||
- name: restore cache from package-lock.json
|
- name: restore cache from package-lock.json
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: package-temp-dir
|
path: package-temp-dir
|
||||||
key: lock-${{ github.sha }}
|
key: lock-${{ github.sha }}
|
||||||
|
|
||||||
- name: restore cache from node_modules
|
- name: restore cache from node_modules
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: node_modules
|
path: node_modules
|
||||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,15 @@ on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * *"
|
- cron: "0 0 * * *"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
close-issues:
|
close-issues:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: need reproduce
|
- name: need reproduce
|
||||||
uses: actions-cool/issues-helper@v1.7
|
uses: actions-cool/issues-helper@v3
|
||||||
with:
|
with:
|
||||||
actions: 'close-issues'
|
actions: 'close-issues'
|
||||||
labels: '🤔 Need Reproduce'
|
labels: '🤔 Need Reproduce'
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
name: Issue Reply
|
name: Issue Labeled
|
||||||
|
|
||||||
on:
|
on:
|
||||||
issues:
|
issues:
|
||||||
types: [labeled]
|
types: [labeled]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
issue-reply:
|
issue-labeled:
|
||||||
|
permissions:
|
||||||
|
issues: write # for actions-cool/issues-helper to update issues
|
||||||
|
pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Need Reproduce
|
- name: Need Reproduce
|
||||||
if: github.event.label.name == '🤔 Need Reproduce'
|
if: github.event.label.name == '🤔 Need Reproduce'
|
||||||
uses: actions-cool/issues-helper@v1.2
|
uses: actions-cool/issues-helper@v3
|
||||||
with:
|
with:
|
||||||
actions: 'create-comment'
|
actions: 'create-comment'
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
issue-number: ${{ github.event.issue.number }}
|
issue-number: ${{ github.event.issue.number }}
|
||||||
body: |
|
body: |
|
||||||
Hello @${{ github.event.issue.user.login }}. Please provide a online reproduction by forking this [link for vue2](https://codesandbox.io/s/2wpk21kzvr)、 [link for vue3](https://codesandbox.io/s/agitated-franklin-1w72v) or a minimal GitHub repository. Make sure to choose the correct version.
|
Hello @${{ github.event.issue.user.login }}. Please provide a online reproduction by forking this [link for vue2](https://codesandbox.io/s/2wpk21kzvr)、 [link for vue3](https://codesandbox.io/s/agitated-franklin-1w72v) or a minimal GitHub repository. Make sure to choose the correct version.
|
||||||
|
|
@ -21,9 +28,10 @@ jobs:
|
||||||
|
|
||||||
- name: help wanted
|
- name: help wanted
|
||||||
if: github.event.label.name == 'help wanted'
|
if: github.event.label.name == 'help wanted'
|
||||||
uses: actions-cool/issues-helper@v1.2
|
uses: actions-cool/issues-helper@v3
|
||||||
with:
|
with:
|
||||||
actions: 'create-comment'
|
actions: 'create-comment'
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
issue-number: ${{ github.event.issue.number }}
|
issue-number: ${{ github.event.issue.number }}
|
||||||
body: |
|
body: |
|
||||||
Hello @${{ github.event.issue.user.login }}. We totally like your proposal/feedback, welcome to send us a Pull Request for it. Please send your Pull Request to proper branch, fill the Pull Request Template here, provide changelog/TypeScript/documentation/test cases if needed and make sure CI passed, we will review it soon. We appreciate your effort in advance and looking forward to your contribution!
|
Hello @${{ github.event.issue.user.login }}. We totally like your proposal/feedback, welcome to send us a Pull Request for it. Please send your Pull Request to proper branch, fill the Pull Request Template here, provide changelog/TypeScript/documentation/test cases if needed and make sure CI passed, we will review it soon. We appreciate your effort in advance and looking forward to your contribution!
|
||||||
|
|
@ -32,12 +40,37 @@ jobs:
|
||||||
|
|
||||||
- name: Usage
|
- name: Usage
|
||||||
if: github.event.label.name == 'Usage'
|
if: github.event.label.name == 'Usage'
|
||||||
uses: actions-cool/issues-helper@v1.2
|
uses: actions-cool/issues-helper@v3
|
||||||
with:
|
with:
|
||||||
actions: 'create-comment, close-issue'
|
actions: 'create-comment, close-issue'
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
issue-number: ${{ github.event.issue.number }}
|
issue-number: ${{ github.event.issue.number }}
|
||||||
body: |
|
body: |
|
||||||
Hello @${{ github.event.issue.user.login }}, we use GitHub issues to trace bugs or discuss plans of Ant Design Vue. So, please don't ask usage questions here. You can try to open a new discussion in [antdv discussions](https://github.com/vueComponent/ant-design-vue/discussions), select `Q&A` to ask questions, also can ask questions on [Stack Overflow](http://stackoverflow.com/questions/) or [Segment Fault](https://segmentfault.com).
|
Hello @${{ github.event.issue.user.login }}, we use GitHub issues to trace bugs or discuss plans of Ant Design Vue. So, please don't ask usage questions here. You can try to open a new discussion in [antdv discussions](https://github.com/vueComponent/ant-design-vue/discussions), select `Q&A` to ask questions, also can ask questions on [Stack Overflow](http://stackoverflow.com/questions/) or [Segment Fault](https://segmentfault.com).
|
||||||
|
|
||||||
你好 @${{ github.event.issue.user.login }},Ant Design Vue Issue 板块是用于 bug 反馈与需求讨论的地方。请勿询问如何使用的问题,你可以试着在 [antdv discussions](https://github.com/vueComponent/ant-design-vue/discussions) 新开一个 discussion,选择 `Q&A` 类别进行提问,也可以在 [Stack Overflow](http://stackoverflow.com/questions/) 或者 [Segment Fault](https://segmentfault.com/) 中提问。
|
你好 @${{ github.event.issue.user.login }},Ant Design Vue Issue 板块是用于 bug 反馈与需求讨论的地方。请勿询问如何使用的问题,你可以试着在 [antdv discussions](https://github.com/vueComponent/ant-design-vue/discussions) 新开一个 discussion,选择 `Q&A` 类别进行提问,也可以在 [Stack Overflow](http://stackoverflow.com/questions/) 或者 [Segment Fault](https://segmentfault.com/) 中提问。
|
||||||
|
|
||||||
|
- name: 1.x
|
||||||
|
if: github.event.label.name == '1.x'
|
||||||
|
uses: actions-cool/issues-helper@v3
|
||||||
|
with:
|
||||||
|
actions: 'create-comment,close-issue'
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
issue-number: ${{ github.event.issue.number }}
|
||||||
|
body: |
|
||||||
|
Hi @${{ github.event.issue.user.login }}. Current version (1.x) is off the maintenance period. We may not accept pull request or fix bug with it anymore. This topic will be auto closed.
|
||||||
|
|
||||||
|
你好 @${{ github.event.issue.user.login }},当前版本(1.x)已经过了维护期。我们不会再接受对其的相关 PR 与 issue。当前 topic 会被自动关闭。
|
||||||
|
|
||||||
|
- name: 2.x
|
||||||
|
if: github.event.label.name == '2.x'
|
||||||
|
uses: actions-cool/issues-helper@v3
|
||||||
|
with:
|
||||||
|
actions: 'create-comment,close-issue'
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
issue-number: ${{ github.event.issue.number }}
|
||||||
|
body: |
|
||||||
|
Hi @${{ github.event.issue.user.login }}. Current version (2.x) is off the maintenance period. We may not accept pull request or fix bug with it anymore. This topic will be auto closed.
|
||||||
|
|
||||||
|
你好 @${{ github.event.issue.user.login }},当前版本(2.x)已经过了维护期。我们不会再接受对其的相关 PR 与 issue。当前 topic 会被自动关闭。
|
||||||
|
|
||||||
|
|
@ -4,8 +4,16 @@ on:
|
||||||
issues:
|
issues:
|
||||||
types: [opened]
|
types: [opened]
|
||||||
|
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-issue:
|
issue-open-check:
|
||||||
|
permissions:
|
||||||
|
contents: read # for visiky/dingtalk-release-notify to get latest release
|
||||||
|
issues: write # for actions-cool/issues-helper to update issues
|
||||||
|
pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions-cool/check-user-permission@v1.0.0
|
- uses: actions-cool/check-user-permission@v1.0.0
|
||||||
|
|
@ -15,7 +23,7 @@ jobs:
|
||||||
|
|
||||||
- name: check invalid
|
- name: check invalid
|
||||||
if: (contains(github.event.issue.body, 'issue-helper') == false) && (steps.checkUser.outputs.result == 'false')
|
if: (contains(github.event.issue.body, 'issue-helper') == false) && (steps.checkUser.outputs.result == 'false')
|
||||||
uses: actions-cool/issues-helper@v1.2
|
uses: actions-cool/issues-helper@v3
|
||||||
with:
|
with:
|
||||||
actions: 'create-comment,add-labels,close-issue'
|
actions: 'create-comment,add-labels,close-issue'
|
||||||
issue-number: ${{ github.event.issue.number }}
|
issue-number: ${{ github.event.issue.number }}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
name: PR Labeled
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [labeled]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
reply:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Usage
|
|
||||||
if: github.event.label.name == 'Usage'
|
|
||||||
uses: actions-cool/issues-helper@v1.2
|
|
||||||
with:
|
|
||||||
actions: 'create-comment, close-issue'
|
|
||||||
issue-number: ${{ github.event.pull_request.number }}
|
|
||||||
body: |
|
|
||||||
Hello @${{ github.event.pull_request.user.login }}, we use GitHub PR to build and perfect of Ant Design Vue. So, please don't ask usage questions here. You can try to open a new discussion in [antdv discussions](https://github.com/vueComponent/ant-design-vue/discussions), select `Q&A` to ask questions, also can ask questions on [Stack Overflow](http://stackoverflow.com/questions/) or [Segment Fault](https://segmentfault.com).
|
|
||||||
|
|
||||||
你好 @${{ github.event.pull_request.user.login }},Ant Design Vue PR 是用于建设、完善项目的地方。请勿询问如何使用的问题,你可以试着在 [antdv discussions](https://github.com/vueComponent/ant-design-vue/discussions) 新开一个 discussion,选择 `Q&A` 类别进行提问,也可以在 [Stack Overflow](http://stackoverflow.com/questions/) 或者 [Segment Fault](https://segmentfault.com/) 中提问。
|
|
||||||
|
|
@ -10,7 +10,7 @@ jobs:
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: cache package-lock.json
|
- name: cache package-lock.json
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: package-temp-dir
|
path: package-temp-dir
|
||||||
key: lock-${{ github.sha }}
|
key: lock-${{ github.sha }}
|
||||||
|
|
@ -27,7 +27,7 @@ jobs:
|
||||||
|
|
||||||
- name: cache node_modules
|
- name: cache node_modules
|
||||||
id: node_modules_cache_id
|
id: node_modules_cache_id
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: node_modules
|
path: node_modules
|
||||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||||
|
|
@ -43,25 +43,25 @@ jobs:
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: restore cache from package-lock.json
|
- name: restore cache from package-lock.json
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: package-temp-dir
|
path: package-temp-dir
|
||||||
key: lock-${{ github.sha }}
|
key: lock-${{ github.sha }}
|
||||||
|
|
||||||
- name: restore cache from node_modules
|
- name: restore cache from node_modules
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: node_modules
|
path: node_modules
|
||||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||||
|
|
||||||
- name: cache lib
|
- name: cache lib
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: lib
|
path: lib
|
||||||
key: lib-${{ github.sha }}
|
key: lib-${{ github.sha }}
|
||||||
|
|
||||||
- name: cache es
|
- name: cache es
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: es
|
path: es
|
||||||
key: es-${{ github.sha }}
|
key: es-${{ github.sha }}
|
||||||
|
|
@ -77,13 +77,13 @@ jobs:
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: restore cache from package-lock.json
|
- name: restore cache from package-lock.json
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: package-temp-dir
|
path: package-temp-dir
|
||||||
key: lock-${{ github.sha }}
|
key: lock-${{ github.sha }}
|
||||||
|
|
||||||
- name: restore cache from node_modules
|
- name: restore cache from node_modules
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: node_modules
|
path: node_modules
|
||||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||||
|
|
@ -99,13 +99,13 @@ jobs:
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: restore cache from package-lock.json
|
- name: restore cache from package-lock.json
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: package-temp-dir
|
path: package-temp-dir
|
||||||
key: lock-${{ github.sha }}
|
key: lock-${{ github.sha }}
|
||||||
|
|
||||||
- name: restore cache from node_modules
|
- name: restore cache from node_modules
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: node_modules
|
path: node_modules
|
||||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ jspm_packages/
|
||||||
dist
|
dist
|
||||||
lib
|
lib
|
||||||
es
|
es
|
||||||
|
/locale
|
||||||
_site
|
_site
|
||||||
yarn.lock
|
yarn.lock
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
@ -78,5 +79,8 @@ report.html
|
||||||
|
|
||||||
site/src/router/demoRoutes.js
|
site/src/router/demoRoutes.js
|
||||||
|
|
||||||
|
components/version/version.ts
|
||||||
components/version/version.tsx
|
components/version/version.tsx
|
||||||
|
components/version/token.json
|
||||||
|
components/version/token-meta.json
|
||||||
~component-api.json
|
~component-api.json
|
||||||
|
|
|
||||||
9
.jest.js
9
.jest.js
|
|
@ -18,6 +18,7 @@ function getTestRegex(libDir) {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
verbose: true,
|
verbose: true,
|
||||||
setupFiles: ['./tests/setup.js'],
|
setupFiles: ['./tests/setup.js'],
|
||||||
|
setupFilesAfterEnv: ['./tests/setupAfterEnv.ts'],
|
||||||
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'vue', 'md', 'jpg'],
|
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'vue', 'md', 'jpg'],
|
||||||
modulePathIgnorePatterns: ['/_site/'],
|
modulePathIgnorePatterns: ['/_site/'],
|
||||||
testPathIgnorePatterns: testPathIgnorePatterns,
|
testPathIgnorePatterns: testPathIgnorePatterns,
|
||||||
|
|
@ -30,23 +31,19 @@ module.exports = {
|
||||||
testRegex: getTestRegex(libDir),
|
testRegex: getTestRegex(libDir),
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'^@/(.*)$/': '<rootDir>/$1',
|
'^@/(.*)$/': '<rootDir>/$1',
|
||||||
'ant-design-vue$/': '<rootDir>/components/index.ts',
|
'^ant-design-vue$': '<rootDir>/components/index',
|
||||||
'ant-design-vue/es/': '<rootDir>/components',
|
'^ant-design-vue/es/(.*)$': '<rootDir>/components/$1',
|
||||||
},
|
},
|
||||||
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
|
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
|
||||||
collectCoverage: process.env.COVERAGE === 'true',
|
collectCoverage: process.env.COVERAGE === 'true',
|
||||||
collectCoverageFrom: [
|
collectCoverageFrom: [
|
||||||
'components/**/*.{js,jsx,vue}',
|
'components/**/*.{js,jsx,vue}',
|
||||||
'!components/*/style/index.{js,jsx}',
|
|
||||||
'!components/style/*.{js,jsx}',
|
|
||||||
'!components/*/locale/*.{js,jsx}',
|
|
||||||
'!components/*/__tests__/**/type.{js,jsx}',
|
'!components/*/__tests__/**/type.{js,jsx}',
|
||||||
'!components/vc-*/**/*',
|
'!components/vc-*/**/*',
|
||||||
'!components/*/demo/**/*',
|
'!components/*/demo/**/*',
|
||||||
'!components/_util/**/*',
|
'!components/_util/**/*',
|
||||||
'!components/align/**/*',
|
'!components/align/**/*',
|
||||||
'!components/trigger/**/*',
|
'!components/trigger/**/*',
|
||||||
'!components/style.js',
|
|
||||||
'!**/node_modules/**',
|
'!**/node_modules/**',
|
||||||
],
|
],
|
||||||
testEnvironment: 'jsdom',
|
testEnvironment: 'jsdom',
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ yarn-error.log
|
||||||
.editorconfig
|
.editorconfig
|
||||||
.eslintignore
|
.eslintignore
|
||||||
**/*.yml
|
**/*.yml
|
||||||
components/style/color/*.less
|
|
||||||
**/assets
|
**/assets
|
||||||
.gitattributes
|
.gitattributes
|
||||||
.stylelintrc
|
.stylelintrc
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,40 @@
|
||||||
"stylelint-config-rational-order",
|
"stylelint-config-rational-order",
|
||||||
"stylelint-config-prettier"
|
"stylelint-config-prettier"
|
||||||
],
|
],
|
||||||
"plugins": ["stylelint-order", "stylelint-declaration-block-no-ignored-properties"],
|
"customSyntax": "postcss-less",
|
||||||
|
"plugins": ["stylelint-declaration-block-no-ignored-properties"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"comment-empty-line-before": null,
|
"function-name-case": ["lower"],
|
||||||
"function-name-case": ["lower", { "ignoreFunctions": ["/colorPalette/"] }],
|
"function-no-unknown": [
|
||||||
"no-invalid-double-slash-comments": null,
|
true,
|
||||||
|
{
|
||||||
|
"ignoreFunctions": [
|
||||||
|
"fade",
|
||||||
|
"fadeout",
|
||||||
|
"tint",
|
||||||
|
"darken",
|
||||||
|
"ceil",
|
||||||
|
"fadein",
|
||||||
|
"floor",
|
||||||
|
"unit",
|
||||||
|
"shade",
|
||||||
|
"lighten",
|
||||||
|
"percentage",
|
||||||
|
"-"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"import-notation": null,
|
||||||
"no-descending-specificity": null,
|
"no-descending-specificity": null,
|
||||||
"declaration-empty-line-before": null
|
"no-invalid-position-at-import-rule": null,
|
||||||
},
|
"declaration-empty-line-before": null,
|
||||||
"ignoreFiles": ["components/style/color/{bezierEasing,colorPalette,tinyColor}.less"]
|
"keyframes-name-pattern": null,
|
||||||
|
"custom-property-pattern": null,
|
||||||
|
"number-max-precision": 8,
|
||||||
|
"alpha-value-notation": "number",
|
||||||
|
"color-function-notation": "legacy",
|
||||||
|
"selector-class-pattern": null,
|
||||||
|
"selector-id-pattern": null,
|
||||||
|
"selector-not-notation": null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1486
CHANGELOG.en-US.md
1486
CHANGELOG.en-US.md
File diff suppressed because it is too large
Load Diff
1485
CHANGELOG.zh-CN.md
1485
CHANGELOG.zh-CN.md
File diff suppressed because it is too large
Load Diff
|
|
@ -1,16 +1,10 @@
|
||||||
<p align="center">
|
|
||||||
<a href="https://www.antdv.com/">
|
|
||||||
<img width="200" src="https://qn.antdv.com/logo.png">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
<a href="https://www.antdv.com/" target="_blank">Ant Design Vue</a>
|
<a href="https://www.antdv.com/" target="_blank">Ant Design Vue</a>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
An enterprise-class UI components based on Ant Design and Vue 3.
|
基于 Ant Design 和 Vue 3 的企业级 UI 组件库。
|
||||||
|
|
||||||
 [](https://codecov.io/gh/vueComponent/ant-design-vue) [](https://www.npmjs.org/package/ant-design-vue) [](http://www.npmtrends.com/ant-design-vue) [](#backers) [](#sponsors) [](https://marketplace.visualstudio.com/items?itemName=ant-design-vue.vscode-ant-design-vue-helper) [](https://github.com/actions-cool/issues-helper)
|
 [](https://codecov.io/gh/vueComponent/ant-design-vue) [](https://www.npmjs.org/package/ant-design-vue) [](http://www.npmtrends.com/ant-design-vue) [](#backers) [](#sponsors) [](https://marketplace.visualstudio.com/items?itemName=ant-design-vue.vscode-ant-design-vue-helper) [](https://github.com/actions-cool/issues-helper)
|
||||||
|
|
||||||
|
|
@ -18,6 +12,14 @@ An enterprise-class UI components based on Ant Design and Vue 3.
|
||||||
|
|
||||||
[](https://www.antdv.com/)
|
[](https://www.antdv.com/)
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<sup><strong>赞助商</strong></sup>
|
||||||
|
<br>
|
||||||
|
<a href="https://mentorbook.ai/" target="_blank">
|
||||||
|
<img src="/site/public/mentorbook_banner_zh.svg" alt="Mentorbook.AI - 你的 AI 导师,你的学习之旅" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
[English](./README.md) | 简体中文
|
[English](./README.md) | 简体中文
|
||||||
|
|
||||||
## 特性
|
## 特性
|
||||||
|
|
@ -26,6 +28,12 @@ An enterprise-class UI components based on Ant Design and Vue 3.
|
||||||
- 开箱即用的高质量 Vue 组件。
|
- 开箱即用的高质量 Vue 组件。
|
||||||
- 共享 [Ant Design of React](http://ant-design.gitee.io/docs/spec/introduce-cn) 设计工具体系。
|
- 共享 [Ant Design of React](http://ant-design.gitee.io/docs/spec/introduce-cn) 设计工具体系。
|
||||||
|
|
||||||
|
## 关注我们
|
||||||
|
|
||||||
|
收藏加关注,第一时间获取更新动态!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 支持环境
|
## 支持环境
|
||||||
|
|
||||||
- 现代浏览器。1.x 版本支持 IE 9+(需要 [polyfills](https://www.antdv.com/docs/vue/getting-started-cn/#兼容性))
|
- 现代浏览器。1.x 版本支持 IE 9+(需要 [polyfills](https://www.antdv.com/docs/vue/getting-started-cn/#兼容性))
|
||||||
|
|
@ -66,6 +74,8 @@ $ yarn add ant-design-vue
|
||||||
| [vue-cli-plugin-ant-design](https://github.com/vueComponent/vue-cli-plugin-ant-design) | 使用 vue-cli3 快速使用 ant-design-vue 组件库 |
|
| [vue-cli-plugin-ant-design](https://github.com/vueComponent/vue-cli-plugin-ant-design) | 使用 vue-cli3 快速使用 ant-design-vue 组件库 |
|
||||||
| [vue-dash-event](https://github.com/vueComponent/vue-dash-event) | 在 DOM 模板中,您可以使用 ant-design-vue 组件的自定义事件(camelCase) |
|
| [vue-dash-event](https://github.com/vueComponent/vue-dash-event) | 在 DOM 模板中,您可以使用 ant-design-vue 组件的自定义事件(camelCase) |
|
||||||
| [@formily/antdv](https://github.com/formilyjs/antdv) | 这是一个结合了 Formily 和 ant-design-vue 的组件库 |
|
| [@formily/antdv](https://github.com/formilyjs/antdv) | 这是一个结合了 Formily 和 ant-design-vue 的组件库 |
|
||||||
|
| [@ant-design-vue/nuxt](https://github.com/vueComponent/ant-design-vue-nuxt) | ant-design-vue 的 nuxt 模块扩展 |
|
||||||
|
| [ant-design-x-vue](https://github.com/wzc520pyfm/ant-design-x-vue) | 基于 Ant Design X 设计规范的 Vue AI 界面解决方案 |
|
||||||
|
|
||||||
## 问答
|
## 问答
|
||||||
|
|
||||||
|
|
@ -80,24 +90,32 @@ ant-design-vue 是 MIT 协议的开源项目。为了项目能够更好的持续
|
||||||
- [Patreon](https://www.patreon.com/tangjinzhou)
|
- [Patreon](https://www.patreon.com/tangjinzhou)
|
||||||
- [opencollective](https://opencollective.com/ant-design-vue)
|
- [opencollective](https://opencollective.com/ant-design-vue)
|
||||||
- [paypal](https://www.paypal.me/tangjinzhou)
|
- [paypal](https://www.paypal.me/tangjinzhou)
|
||||||
- [支付宝或微信](https://qn.antdv.com/alipay-and-wechat.png)
|
- ETH: 0x30cc48515d8ae9fefa20ab87226ad7e8ab9c3bc2
|
||||||
|
|
||||||
## Sponsors
|
## 赞助商
|
||||||
|
|
||||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/ant-design-vue#sponsor)]
|
成为赞助商,并在 Github 上的自述文件上获得您的徽标,并链接到您的网站。 [[成为赞助商](https://opencollective.com/ant-design-vue#sponsor)]
|
||||||
|
|
||||||
<a href="http://www.jeecg.com/" target="_blank"><img src="https://aliyuncdn.antdv.com/jeecg-logo.png" height="64"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/10/avatar.svg"></a>
|
<a href="http://www.jeecg.com/" target="_blank"><img src="https://www.antdv.com/jeecg-logo.png" height="64"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/10/avatar.svg"></a>
|
||||||
|
|
||||||
## Backers
|
## 支持者
|
||||||
|
|
||||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/ant-design-vue#backer)]
|
每月捐款支持我们,帮助我们继续我们的活动。 [[成为支持者](https://opencollective.com/ant-design-vue#backer)]
|
||||||
|
|
||||||
<a href="https://github.com/chuzhixin/vue-admin-beautiful" target="_blank"><img width="64" style="border-radius: 50%;" src="https://gitee.com/chu1204505056/image/raw/master/vue-admin-beautiful.png" title="vue-admin-beautiful"></a> <a href="https://opencollective.com/ant-design-vue/backer/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/10/avatar.svg"></a>
|
<a href="https://github.com/chuzhixin/vue-admin-beautiful" target="_blank"><img width="64" style="border-radius: 50%;" src="https://gitee.com/chu1204505056/image/raw/master/vue-admin-beautiful.png" title="vue-admin-beautiful"></a> <a href="https://opencollective.com/ant-design-vue/backer/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/10/avatar.svg"></a>
|
||||||
|
|
||||||
## Patreon
|
## Patreon
|
||||||
|
|
||||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://www.patreon.com/tangjinzhou)]
|
每月捐款支持我们,帮助我们继续我们的活动。 [[成为支持者](https://www.patreon.com/tangjinzhou)]
|
||||||
|
|
||||||
<a href="https://www.mokeyjay.com" target="_blank"><img width="64" style="border-radius: 50%;" src="https://www.mokeyjay.com/headimg.png" title="donation by Patreon"></a>
|
<a href="https://www.mokeyjay.com" target="_blank"><img width="64" style="border-radius: 50%;" src="https://www.mokeyjay.com/headimg.png" title="donation by Patreon"></a>
|
||||||
|
|
||||||
## [更多赞助者 (通过 Patreon、支付宝、微信、paypal 等等)](https://github.com/vueComponent/ant-design-vue/blob/master/BACKERS.md)
|
## [更多赞助者 (通过 Patreon、支付宝、微信、paypal 等等)](https://github.com/vueComponent/ant-design-vue/blob/master/BACKERS.md)
|
||||||
|
|
||||||
|
## 贡献者
|
||||||
|
|
||||||
|
感谢所有为 ant-design-vue 做出贡献的人!
|
||||||
|
|
||||||
|
<a href="https://github.com/vueComponent/ant-design-vue/graphs/contributors">
|
||||||
|
<img src="https://contrib.rocks/image?repo=vueComponent/ant-design-vue&max=100&columns=15" />
|
||||||
|
</a>
|
||||||
|
|
|
||||||
36
README.md
36
README.md
|
|
@ -1,6 +1,6 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://www.antdv.com/">
|
<a href="https://www.antdv.com/">
|
||||||
<img width="200" src="https://qn.antdv.com/logo.png">
|
<img width="200" src="https://www.antdv.com/logo.png">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -18,6 +18,14 @@ An enterprise-class UI components based on Ant Design and Vue.
|
||||||
|
|
||||||
[](https://www.antdv.com/)
|
[](https://www.antdv.com/)
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<sup><strong>Sponsored by</strong></sup>
|
||||||
|
<br>
|
||||||
|
<a href="https://mentorbook.ai/" target="_blank">
|
||||||
|
<img src="/site/public/mentorbook_banner_en.svg" alt="Mentorbook.AI - Your AI Mentor, Your Learning Journey" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
English | [简体中文](./README-zh_CN.md)
|
English | [简体中文](./README-zh_CN.md)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
@ -26,9 +34,15 @@ English | [简体中文](./README-zh_CN.md)
|
||||||
- A set of high-quality Vue components out of the box.
|
- A set of high-quality Vue components out of the box.
|
||||||
- Shared [Ant Design of React](https://ant.design/docs/spec/introduce) design resources.
|
- Shared [Ant Design of React](https://ant.design/docs/spec/introduce) design resources.
|
||||||
|
|
||||||
|
## Getting started & staying tuned with us.
|
||||||
|
|
||||||
|
Star us, and you will receive all releases notifications from GitHub without any delay!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Environment Support
|
## Environment Support
|
||||||
|
|
||||||
- Modern browsers. v1.x support Internet Explorer 9+ (with [polyfills](https://www.antdv.com/docs/vue/getting-started/#Compatibility))
|
- Modern browsers. v1.x support Internet Explorer 9+ (with [polyfills](https://www.antdv.com/docs/vue/getting-started/#compatibility))
|
||||||
- Server-side Rendering
|
- Server-side Rendering
|
||||||
- Support Vue 2 & Vue 3
|
- Support Vue 2 & Vue 3
|
||||||
- [Electron](https://electronjs.org/)
|
- [Electron](https://electronjs.org/)
|
||||||
|
|
@ -39,7 +53,7 @@ English | [简体中文](./README-zh_CN.md)
|
||||||
|
|
||||||
## Using npm or yarn
|
## Using npm or yarn
|
||||||
|
|
||||||
**We recommend using npm or yarn to install**,it not only makes development easier,but also allow you to take advantage of the rich ecosystem of Javascript packages and tooling.
|
**We recommend using npm or yarn to install**, it not only makes development easier, but also allow you to take advantage of the rich ecosystem of Javascript packages and tooling.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ npm install ant-design-vue --save
|
$ npm install ant-design-vue --save
|
||||||
|
|
@ -49,7 +63,7 @@ $ npm install ant-design-vue --save
|
||||||
$ yarn add ant-design-vue
|
$ yarn add ant-design-vue
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are in a bad network environment,you can try other registries and tools like [cnpm](https://github.com/cnpm/cnpm).
|
If you are in a bad network environment, you can try other registries and tools like [cnpm](https://github.com/cnpm/cnpm).
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
|
|
@ -66,6 +80,8 @@ If you are in a bad network environment,you can try other registries and tools
|
||||||
| [vue-cli-plugin-ant-design](https://github.com/vueComponent/vue-cli-plugin-ant-design) | Vue-cli 3 plugin to add ant-design-vue |
|
| [vue-cli-plugin-ant-design](https://github.com/vueComponent/vue-cli-plugin-ant-design) | Vue-cli 3 plugin to add ant-design-vue |
|
||||||
| [vue-dash-event](https://github.com/vueComponent/vue-dash-event) | The library function, implemented in the DOM template, can use the custom event of the ant-design-vue component (camelCase) |
|
| [vue-dash-event](https://github.com/vueComponent/vue-dash-event) | The library function, implemented in the DOM template, can use the custom event of the ant-design-vue component (camelCase) |
|
||||||
| [@formily/antdv](https://github.com/formilyjs/antdv) | The Library with Formily and ant-design-vue |
|
| [@formily/antdv](https://github.com/formilyjs/antdv) | The Library with Formily and ant-design-vue |
|
||||||
|
| [@ant-design-vue/nuxt](https://github.com/vueComponent/ant-design-vue-nuxt) | A nuxt module for ant-design-vue |
|
||||||
|
| [ant-design-x-vue](https://github.com/wzc520pyfm/ant-design-x-vue) | A Vue AI interface solutions base on the Ant Design X design specification |
|
||||||
|
|
||||||
## Donation
|
## Donation
|
||||||
|
|
||||||
|
|
@ -74,16 +90,24 @@ ant-design-vue is an MIT-licensed open source project. In order to achieve bette
|
||||||
- [Patreon](https://www.patreon.com/tangjinzhou)
|
- [Patreon](https://www.patreon.com/tangjinzhou)
|
||||||
- [opencollective](https://opencollective.com/ant-design-vue)
|
- [opencollective](https://opencollective.com/ant-design-vue)
|
||||||
- [paypal](https://www.paypal.me/tangjinzhou)
|
- [paypal](https://www.paypal.me/tangjinzhou)
|
||||||
- [支付宝或微信](https://qn.antdv.com/alipay-and-wechat.png)
|
- ETH: 0x30cc48515d8ae9fefa20ab87226ad7e8ab9c3bc2
|
||||||
|
|
||||||
## Sponsors
|
## Sponsors
|
||||||
|
|
||||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/ant-design-vue#sponsor)]
|
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/ant-design-vue#sponsor)]
|
||||||
|
|
||||||
<a href="http://www.jeecg.com/" target="_blank"><img src="https://aliyuncdn.antdv.com/jeecg-logo.png" height="64"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/10/avatar.svg"></a>
|
<a href="http://www.jeecg.com/" target="_blank"><img src="https://www.antdv.com/jeecg-logo.png" height="64"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/10/avatar.svg"></a>
|
||||||
|
|
||||||
## [More Sponsor (From Patreon、alipay、wechat、paypal...)](https://github.com/vueComponent/ant-design-vue/blob/master/BACKERS.md)
|
## [More Sponsor (From Patreon、alipay、wechat、paypal...)](https://github.com/vueComponent/ant-design-vue/blob/master/BACKERS.md)
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
Thank you to all the people who already contributed to ant-design-vue!
|
||||||
|
|
||||||
|
<a href="https://github.com/vueComponent/ant-design-vue/graphs/contributors">
|
||||||
|
<img src="https://contrib.rocks/image?repo=vueComponent/ant-design-vue&max=100&columns=15" />
|
||||||
|
</a>
|
||||||
|
|
||||||
[](https://issuehunt.io/repos/104172832)
|
[](https://issuehunt.io/repos/104172832)
|
||||||
|
|
||||||
This project is tested with BrowserStack.
|
This project is tested with BrowserStack.
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ export function formatter(
|
||||||
!tableTitle.includes('()')
|
!tableTitle.includes('()')
|
||||||
) {
|
) {
|
||||||
const childTag: VueTag = {
|
const childTag: VueTag = {
|
||||||
name: getComponentName(tableTitle.replaceAll('.', '').replaceAll('/', ''), tagPrefix),
|
name: getComponentName(tableTitle.replace(/\.|\//g, ''), tagPrefix),
|
||||||
slots: [],
|
slots: [],
|
||||||
events: [],
|
events: [],
|
||||||
attributes: [],
|
attributes: [],
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { genWebTypes } from './web-types';
|
||||||
import { outputFileSync, readFileSync } from 'fs-extra';
|
import { outputFileSync, readFileSync } from 'fs-extra';
|
||||||
import type { Options, VueTag } from './type';
|
import type { Options, VueTag } from './type';
|
||||||
import { getComponentName, normalizePath, toKebabCase } from './utils';
|
import { getComponentName, normalizePath, toKebabCase } from './utils';
|
||||||
import { genVeturAttributes, genVeturTags } from './vetur';
|
import { flatMap } from 'lodash';
|
||||||
|
|
||||||
async function readMarkdown(options: Options): Promise<Map<String, VueTag>> {
|
async function readMarkdown(options: Options): Promise<Map<String, VueTag>> {
|
||||||
const mdPaths = await glob(normalizePath(`${options.path}/**/*.md`));
|
const mdPaths = await glob(normalizePath(`${options.path}/**/*.md`));
|
||||||
|
|
@ -21,13 +21,13 @@ async function readMarkdown(options: Options): Promise<Map<String, VueTag>> {
|
||||||
return formatter(mdParser(fileContent), componentName, kebabComponentName, options.tagPrefix);
|
return formatter(mdParser(fileContent), componentName, kebabComponentName, options.tagPrefix);
|
||||||
})
|
})
|
||||||
.filter(item => item) as VueTag[][];
|
.filter(item => item) as VueTag[][];
|
||||||
const tags: Map<String, VueTag> = new Map();
|
const tags = new Map<String, VueTag>();
|
||||||
data.flatMap(item => item).forEach(mergedTag => mergeTag(tags, mergedTag));
|
flatMap(data, item => item).forEach(mergedTag => mergeTag(tags, mergedTag));
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readTypings(options: Options): Map<String, VueTag> {
|
function readTypings(options: Options): Map<String, VueTag> {
|
||||||
const tags: Map<String, VueTag> = new Map();
|
const tags = new Map<String, VueTag>();
|
||||||
const fileContent = readFileSync(options.typingsPath, 'utf-8');
|
const fileContent = readFileSync(options.typingsPath, 'utf-8');
|
||||||
fileContent
|
fileContent
|
||||||
.split('\n')
|
.split('\n')
|
||||||
|
|
@ -61,7 +61,7 @@ function mergeTag(tags: Map<String, VueTag>, mergedTag: VueTag) {
|
||||||
|
|
||||||
function mergeTags(mergedTagsArr: Map<String, VueTag>[]): VueTag[] {
|
function mergeTags(mergedTagsArr: Map<String, VueTag>[]): VueTag[] {
|
||||||
if (mergedTagsArr.length === 1) return [...mergedTagsArr[0].values()];
|
if (mergedTagsArr.length === 1) return [...mergedTagsArr[0].values()];
|
||||||
const tags: Map<String, VueTag> = new Map();
|
const tags = new Map<String, VueTag>();
|
||||||
if (mergedTagsArr.length === 0) return [];
|
if (mergedTagsArr.length === 0) return [];
|
||||||
mergedTagsArr.forEach(mergedTags => {
|
mergedTagsArr.forEach(mergedTags => {
|
||||||
mergedTags.forEach(mergedTag => mergeTag(tags, mergedTag));
|
mergedTags.forEach(mergedTag => mergeTag(tags, mergedTag));
|
||||||
|
|
@ -77,13 +77,6 @@ export async function parseAndWrite(options: Options): Promise<Number> {
|
||||||
const tagsFromTypings = await readTypings(options);
|
const tagsFromTypings = await readTypings(options);
|
||||||
const tags = mergeTags([tagsFromMarkdown, tagsFromTypings]);
|
const tags = mergeTags([tagsFromMarkdown, tagsFromTypings]);
|
||||||
const webTypes = genWebTypes(tags, options);
|
const webTypes = genWebTypes(tags, options);
|
||||||
const veturTags = genVeturTags(tags);
|
|
||||||
const veturAttributes = genVeturAttributes(tags);
|
|
||||||
outputFileSync(join(options.outputDir, 'tags.json'), JSON.stringify(veturTags, null, 2));
|
|
||||||
outputFileSync(
|
|
||||||
join(options.outputDir, 'attributes.json'),
|
|
||||||
JSON.stringify(veturAttributes, null, 2),
|
|
||||||
);
|
|
||||||
outputFileSync(join(options.outputDir, 'web-types.json'), JSON.stringify(webTypes, null, 2));
|
outputFileSync(join(options.outputDir, 'web-types.json'), JSON.stringify(webTypes, null, 2));
|
||||||
return tags.length;
|
return tags.length;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,13 @@ export type Articals = Artical[];
|
||||||
function readLine(input: string) {
|
function readLine(input: string) {
|
||||||
const end = input.indexOf('\n');
|
const end = input.indexOf('\n');
|
||||||
|
|
||||||
return input.substr(0, end !== -1 ? end : input.length);
|
return input.substring(0, end !== -1 ? end : input.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
function splitTableLine(line: string) {
|
function splitTableLine(line: string) {
|
||||||
line = line.replace(/\\\|/g, 'JOIN');
|
line = line.replace(/\\\|/g, 'JOIN');
|
||||||
|
|
||||||
const items = line.split('|').map(item => item.trim().replaceAll('JOIN', '|'));
|
const items = line.split('|').map(item => item.trim().replace(/JOIN/g, '|'));
|
||||||
|
|
||||||
// remove pipe character on both sides
|
// remove pipe character on both sides
|
||||||
items.pop();
|
items.pop();
|
||||||
|
|
@ -47,7 +47,7 @@ function tableParse(input: string) {
|
||||||
};
|
};
|
||||||
|
|
||||||
while (start < end) {
|
while (start < end) {
|
||||||
const target = input.substr(start);
|
const target = input.substring(start);
|
||||||
const line = readLine(target);
|
const line = readLine(target);
|
||||||
|
|
||||||
if (!/^\|/.test(target)) {
|
if (!/^\|/.test(target)) {
|
||||||
|
|
@ -79,7 +79,7 @@ export function mdParser(input: string): Articals {
|
||||||
const end = input.length;
|
const end = input.length;
|
||||||
|
|
||||||
while (start < end) {
|
while (start < end) {
|
||||||
const target = input.substr(start);
|
const target = input.substring(start);
|
||||||
|
|
||||||
let match;
|
let match;
|
||||||
if ((match = TITLE_REG.exec(target))) {
|
if ((match = TITLE_REG.exec(target))) {
|
||||||
|
|
@ -91,7 +91,7 @@ export function mdParser(input: string): Articals {
|
||||||
|
|
||||||
start += match.index + match[0].length;
|
start += match.index + match[0].length;
|
||||||
} else if ((match = TABLE_REG.exec(target))) {
|
} else if ((match = TABLE_REG.exec(target))) {
|
||||||
const { table, usedLength } = tableParse(target.substr(match.index));
|
const { table, usedLength } = tableParse(target.substring(match.index));
|
||||||
artical.push({
|
artical.push({
|
||||||
type: 'table',
|
type: 'table',
|
||||||
table,
|
table,
|
||||||
|
|
|
||||||
|
|
@ -34,25 +34,6 @@ export type VueTag = {
|
||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VeturTag = {
|
|
||||||
description?: string;
|
|
||||||
attributes: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type VeturTags = Record<string, VeturTag>;
|
|
||||||
|
|
||||||
export type VeturAttribute = {
|
|
||||||
type: string;
|
|
||||||
description: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type VeturAttributes = Record<string, VeturAttribute>;
|
|
||||||
|
|
||||||
export type VeturResult = {
|
|
||||||
tags: VeturTags;
|
|
||||||
attributes: VeturAttributes;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Options = {
|
export type Options = {
|
||||||
name: string;
|
name: string;
|
||||||
path: PathLike;
|
path: PathLike;
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
import type { VueTag, VeturTags, VeturAttributes } from './type';
|
|
||||||
|
|
||||||
export function genVeturTags(tags: VueTag[]) {
|
|
||||||
const veturTags: VeturTags = {};
|
|
||||||
|
|
||||||
tags.forEach(tag => {
|
|
||||||
veturTags[tag.name] = {
|
|
||||||
attributes: tag.attributes ? tag.attributes.map(item => item.name) : [],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return veturTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function genVeturAttributes(tags: VueTag[]) {
|
|
||||||
const veturAttributes: VeturAttributes = {};
|
|
||||||
|
|
||||||
tags.forEach(tag => {
|
|
||||||
if (tag.attributes) {
|
|
||||||
tag.attributes.forEach(attr => {
|
|
||||||
veturAttributes[`${tag.name}/${attr.name}`] = {
|
|
||||||
type: attr.value.type,
|
|
||||||
description: `${attr.description}, Default: ${attr.default}`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return veturAttributes;
|
|
||||||
}
|
|
||||||
|
|
@ -20,7 +20,8 @@ module.exports = function (modules) {
|
||||||
resolve('@babel/plugin-transform-runtime'),
|
resolve('@babel/plugin-transform-runtime'),
|
||||||
{
|
{
|
||||||
useESModules: modules === false,
|
useESModules: modules === false,
|
||||||
version: '^7.10.4',
|
version:
|
||||||
|
require(`${process.cwd()}/package.json`).dependencies['@babel/runtime'] || '^7.10.4',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// resolve('babel-plugin-inline-import-data-uri'),
|
// resolve('babel-plugin-inline-import-data-uri'),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const assign = require('object-assign');
|
|
||||||
const { getProjectPath } = require('./utils/projectHelper');
|
const { getProjectPath } = require('./utils/projectHelper');
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
|
|
@ -9,7 +8,7 @@ module.exports = function () {
|
||||||
if (fs.existsSync(getProjectPath('tsconfig.json'))) {
|
if (fs.existsSync(getProjectPath('tsconfig.json'))) {
|
||||||
my = require(getProjectPath('tsconfig.json'));
|
my = require(getProjectPath('tsconfig.json'));
|
||||||
}
|
}
|
||||||
return assign(
|
return Object.assign(
|
||||||
{
|
{
|
||||||
noUnusedParameters: true,
|
noUnusedParameters: true,
|
||||||
noUnusedLocals: true,
|
noUnusedLocals: true,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ const imageOptions = {
|
||||||
limit: 10000,
|
limit: 10000,
|
||||||
};
|
};
|
||||||
|
|
||||||
function getWebpackConfig(modules) {
|
function getWebpackConfig(modules, esm = false) {
|
||||||
const pkg = require(getProjectPath('package.json'));
|
const pkg = require(getProjectPath('package.json'));
|
||||||
const babelConfig = require('./getBabelCommonConfig')(modules || false);
|
const babelConfig = require('./getBabelCommonConfig')(modules || false);
|
||||||
|
|
||||||
|
|
@ -150,36 +150,6 @@ function getWebpackConfig(modules) {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
test: /\.less$/,
|
|
||||||
use: [
|
|
||||||
MiniCssExtractPlugin.loader,
|
|
||||||
{
|
|
||||||
loader: 'css-loader',
|
|
||||||
options: {
|
|
||||||
sourceMap: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
loader: 'postcss-loader',
|
|
||||||
options: {
|
|
||||||
postcssOptions: {
|
|
||||||
plugins: ['autoprefixer'],
|
|
||||||
},
|
|
||||||
sourceMap: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
loader: 'less-loader',
|
|
||||||
options: {
|
|
||||||
lessOptions: {
|
|
||||||
javascriptEnabled: true,
|
|
||||||
},
|
|
||||||
sourceMap: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// Images
|
// Images
|
||||||
{
|
{
|
||||||
test: svgRegex,
|
test: svgRegex,
|
||||||
|
|
@ -200,7 +170,7 @@ function getWebpackConfig(modules) {
|
||||||
new webpack.BannerPlugin(`
|
new webpack.BannerPlugin(`
|
||||||
${pkg.name} v${pkg.version}
|
${pkg.name} v${pkg.version}
|
||||||
|
|
||||||
Copyright 2017-present, ant-design-vue.
|
Copyright 2017-present, Ant Design Vue.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
`),
|
`),
|
||||||
new WebpackBar({
|
new WebpackBar({
|
||||||
|
|
@ -215,7 +185,7 @@ All rights reserved.
|
||||||
};
|
};
|
||||||
|
|
||||||
if (process.env.RUN_ENV === 'PRODUCTION') {
|
if (process.env.RUN_ENV === 'PRODUCTION') {
|
||||||
const entry = ['./index'];
|
let entry = ['./index'];
|
||||||
config.externals = [
|
config.externals = [
|
||||||
{
|
{
|
||||||
vue: {
|
vue: {
|
||||||
|
|
@ -223,11 +193,29 @@ All rights reserved.
|
||||||
commonjs2: 'vue',
|
commonjs2: 'vue',
|
||||||
commonjs: 'vue',
|
commonjs: 'vue',
|
||||||
amd: 'vue',
|
amd: 'vue',
|
||||||
|
module: 'vue',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
config.output.library = distFileBaseName;
|
if (esm) {
|
||||||
|
entry = ['./index.esm'];
|
||||||
|
config.experiments = {
|
||||||
|
...config.experiments,
|
||||||
|
outputModule: true,
|
||||||
|
};
|
||||||
|
config.output.chunkFormat = 'module';
|
||||||
|
config.output.library = {
|
||||||
|
type: 'module',
|
||||||
|
};
|
||||||
|
config.target = 'es2019';
|
||||||
|
} else {
|
||||||
config.output.libraryTarget = 'umd';
|
config.output.libraryTarget = 'umd';
|
||||||
|
config.output.library = distFileBaseName;
|
||||||
|
config.output.globalObject = 'this';
|
||||||
|
}
|
||||||
|
|
||||||
|
const entryName = esm ? `${distFileBaseName}.esm` : distFileBaseName;
|
||||||
|
|
||||||
config.optimization = {
|
config.optimization = {
|
||||||
minimizer: [
|
minimizer: [
|
||||||
new TerserPlugin({
|
new TerserPlugin({
|
||||||
|
|
@ -238,11 +226,10 @@ All rights reserved.
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Development
|
// Development
|
||||||
const uncompressedConfig = merge({}, config, {
|
const uncompressedConfig = merge({}, config, {
|
||||||
entry: {
|
entry: {
|
||||||
[distFileBaseName]: entry,
|
[entryName]: entry,
|
||||||
},
|
},
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
@ -255,7 +242,7 @@ All rights reserved.
|
||||||
// Production
|
// Production
|
||||||
const prodConfig = merge({}, config, {
|
const prodConfig = merge({}, config, {
|
||||||
entry: {
|
entry: {
|
||||||
[`${distFileBaseName}.min`]: entry,
|
[`${entryName}.min`]: entry,
|
||||||
},
|
},
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ const getBabelCommonConfig = require('./getBabelCommonConfig');
|
||||||
const merge2 = require('merge2');
|
const merge2 = require('merge2');
|
||||||
const { execSync } = require('child_process');
|
const { execSync } = require('child_process');
|
||||||
const through2 = require('through2');
|
const through2 = require('through2');
|
||||||
const transformLess = require('./transformLess');
|
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const babel = require('gulp-babel');
|
const babel = require('gulp-babel');
|
||||||
const argv = require('minimist')(process.argv.slice(2));
|
const argv = require('minimist')(process.argv.slice(2));
|
||||||
|
|
@ -27,15 +26,22 @@ const compareVersions = require('compare-versions');
|
||||||
const getTSCommonConfig = require('./getTSCommonConfig');
|
const getTSCommonConfig = require('./getTSCommonConfig');
|
||||||
const replaceLib = require('./replaceLib');
|
const replaceLib = require('./replaceLib');
|
||||||
const sortApiTable = require('./sortApiTable');
|
const sortApiTable = require('./sortApiTable');
|
||||||
|
const { glob } = require('glob');
|
||||||
|
|
||||||
const packageJson = require(getProjectPath('package.json'));
|
const packageJson = require(getProjectPath('package.json'));
|
||||||
const tsDefaultReporter = ts.reporter.defaultReporter();
|
const tsDefaultReporter = ts.reporter.defaultReporter();
|
||||||
const cwd = process.cwd();
|
const cwd = process.cwd();
|
||||||
const libDir = getProjectPath('lib');
|
const libDir = getProjectPath('lib');
|
||||||
const esDir = getProjectPath('es');
|
const esDir = getProjectPath('es');
|
||||||
|
const localeDir = getProjectPath('locale');
|
||||||
|
|
||||||
const tsConfig = getTSCommonConfig();
|
const tsConfig = getTSCommonConfig();
|
||||||
|
|
||||||
|
// FIXME: hard code, not find typescript can modify the path resolution
|
||||||
|
const localeDts = `import type { Locale } from '../lib/locale-provider';
|
||||||
|
declare const localeValues: Locale;
|
||||||
|
export default localeValues;`;
|
||||||
|
|
||||||
function dist(done) {
|
function dist(done) {
|
||||||
rimraf.sync(path.join(cwd, 'dist'));
|
rimraf.sync(path.join(cwd, 'dist'));
|
||||||
process.env.RUN_ENV = 'PRODUCTION';
|
process.env.RUN_ENV = 'PRODUCTION';
|
||||||
|
|
@ -108,6 +114,11 @@ gulp.task('tsc', () =>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
gulp.task('clean', () => {
|
||||||
|
rimraf.sync(getProjectPath('_site'));
|
||||||
|
rimraf.sync(getProjectPath('_data'));
|
||||||
|
});
|
||||||
|
|
||||||
function babelify(js, modules) {
|
function babelify(js, modules) {
|
||||||
const babelConfig = getBabelCommonConfig(modules);
|
const babelConfig = getBabelCommonConfig(modules);
|
||||||
babelConfig.babelrc = false;
|
babelConfig.babelrc = false;
|
||||||
|
|
@ -118,17 +129,7 @@ function babelify(js, modules) {
|
||||||
const stream = js.pipe(babel(babelConfig)).pipe(
|
const stream = js.pipe(babel(babelConfig)).pipe(
|
||||||
through2.obj(function z(file, encoding, next) {
|
through2.obj(function z(file, encoding, next) {
|
||||||
this.push(file.clone());
|
this.push(file.clone());
|
||||||
if (file.path.match(/\/style\/index\.(js|jsx|ts|tsx)$/)) {
|
if (modules !== false) {
|
||||||
const content = file.contents.toString(encoding);
|
|
||||||
file.contents = Buffer.from(
|
|
||||||
content
|
|
||||||
.replace(/\/style\/?'/g, "/style/css'")
|
|
||||||
.replace(/\/style\/?"/g, '/style/css"')
|
|
||||||
.replace(/\.less/g, '.css'),
|
|
||||||
);
|
|
||||||
file.path = file.path.replace(/index\.(js|jsx|ts|tsx)$/, 'css.js');
|
|
||||||
this.push(file);
|
|
||||||
} else if (modules !== false) {
|
|
||||||
const content = file.contents.toString(encoding);
|
const content = file.contents.toString(encoding);
|
||||||
file.contents = Buffer.from(
|
file.contents = Buffer.from(
|
||||||
content
|
content
|
||||||
|
|
@ -144,47 +145,9 @@ function babelify(js, modules) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function compile(modules) {
|
function compile(modules) {
|
||||||
const { compile: { transformTSFile, transformFile, includeLessFile = [] } = {} } = getConfig();
|
const { compile: { transformTSFile, transformFile } = {} } = getConfig();
|
||||||
rimraf.sync(modules !== false ? libDir : esDir);
|
rimraf.sync(modules !== false ? libDir : esDir);
|
||||||
|
|
||||||
// =============================== LESS ===============================
|
|
||||||
const less = gulp
|
|
||||||
.src(['components/**/*.less'])
|
|
||||||
.pipe(
|
|
||||||
through2.obj(function (file, encoding, next) {
|
|
||||||
// Replace content
|
|
||||||
const cloneFile = file.clone();
|
|
||||||
const content = file.contents.toString().replace(/^\uFEFF/, '');
|
|
||||||
|
|
||||||
cloneFile.contents = Buffer.from(content);
|
|
||||||
|
|
||||||
// Clone for css here since `this.push` will modify file.path
|
|
||||||
const cloneCssFile = cloneFile.clone();
|
|
||||||
|
|
||||||
this.push(cloneFile);
|
|
||||||
|
|
||||||
// Transform less file
|
|
||||||
if (
|
|
||||||
file.path.match(/(\/|\\)style(\/|\\)index\.less$/) ||
|
|
||||||
file.path.match(/(\/|\\)style(\/|\\)v2-compatible-reset\.less$/) ||
|
|
||||||
includeLessFile.some(regex => file.path.match(regex))
|
|
||||||
) {
|
|
||||||
transformLess(cloneCssFile.contents.toString(), cloneCssFile.path)
|
|
||||||
.then(css => {
|
|
||||||
cloneCssFile.contents = Buffer.from(css);
|
|
||||||
cloneCssFile.path = cloneCssFile.path.replace(/\.less$/, '.css');
|
|
||||||
this.push(cloneCssFile);
|
|
||||||
next();
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
console.error(e);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.pipe(gulp.dest(modules === false ? esDir : libDir));
|
|
||||||
const assets = gulp
|
const assets = gulp
|
||||||
.src(['components/**/*.@(png|svg)'])
|
.src(['components/**/*.@(png|svg)'])
|
||||||
.pipe(gulp.dest(modules === false ? esDir : libDir));
|
.pipe(gulp.dest(modules === false ? esDir : libDir));
|
||||||
|
|
@ -259,7 +222,26 @@ function compile(modules) {
|
||||||
tsResult.on('end', check);
|
tsResult.on('end', check);
|
||||||
const tsFilesStream = babelify(tsResult.js, modules);
|
const tsFilesStream = babelify(tsResult.js, modules);
|
||||||
const tsd = tsResult.dts.pipe(gulp.dest(modules === false ? esDir : libDir));
|
const tsd = tsResult.dts.pipe(gulp.dest(modules === false ? esDir : libDir));
|
||||||
return merge2([less, tsFilesStream, tsd, assets, transformFileStream].filter(s => s));
|
return merge2([tsFilesStream, tsd, assets, transformFileStream].filter(s => s));
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateLocale() {
|
||||||
|
if (!fs.existsSync(localeDir)) {
|
||||||
|
fs.mkdirSync(localeDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
const localeFiles = glob.sync('components/locale/*.ts?(x)');
|
||||||
|
localeFiles.forEach(item => {
|
||||||
|
const match = item.match(/components\/locale\/(.*)\.tsx?/);
|
||||||
|
if (match) {
|
||||||
|
const locale = match[1];
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(localeDir, `${locale}.js`),
|
||||||
|
`module.exports = require('../lib/locale/${locale}');`,
|
||||||
|
);
|
||||||
|
fs.writeFileSync(path.join(localeDir, `${locale}.d.ts`), localeDts);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function tag() {
|
function tag() {
|
||||||
|
|
@ -386,7 +368,7 @@ function pub(done) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let startTime = new Date();
|
const startTime = new Date();
|
||||||
gulp.task('compile-with-es', done => {
|
gulp.task('compile-with-es', done => {
|
||||||
console.log('start compile at ', startTime);
|
console.log('start compile at ', startTime);
|
||||||
console.log('[Parallel] Compile to es...');
|
console.log('[Parallel] Compile to es...');
|
||||||
|
|
@ -395,7 +377,10 @@ gulp.task('compile-with-es', done => {
|
||||||
|
|
||||||
gulp.task('compile-with-lib', done => {
|
gulp.task('compile-with-lib', done => {
|
||||||
console.log('[Parallel] Compile to js...');
|
console.log('[Parallel] Compile to js...');
|
||||||
compile().on('finish', done);
|
compile().on('finish', () => {
|
||||||
|
generateLocale();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('compile-finalize', done => {
|
gulp.task('compile-finalize', done => {
|
||||||
|
|
@ -467,7 +452,7 @@ gulp.task(
|
||||||
newVersion.trim() === version
|
newVersion.trim() === version
|
||||||
) {
|
) {
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
runCmd('npm', ['run', 'pub'], code => {
|
runCmd('npm', ['run', 'pub'], _code => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
const less = require('less');
|
|
||||||
const path = require('path');
|
|
||||||
const postcss = require('postcss');
|
|
||||||
const autoprefixer = require('autoprefixer');
|
|
||||||
const NpmImportPlugin = require('less-plugin-npm-import');
|
|
||||||
const { getConfig } = require('./utils/projectHelper');
|
|
||||||
|
|
||||||
function transformLess(lessContent, lessFilePath, config = {}) {
|
|
||||||
const { cwd = process.cwd() } = config;
|
|
||||||
const { compile: { lessConfig } = {} } = getConfig();
|
|
||||||
const resolvedLessFile = path.resolve(cwd, lessFilePath);
|
|
||||||
|
|
||||||
// Do less compile
|
|
||||||
const lessOpts = {
|
|
||||||
paths: [path.dirname(resolvedLessFile)],
|
|
||||||
filename: resolvedLessFile,
|
|
||||||
plugins: [new NpmImportPlugin({ prefix: '~' })],
|
|
||||||
javascriptEnabled: true,
|
|
||||||
...lessConfig,
|
|
||||||
};
|
|
||||||
return less
|
|
||||||
.render(lessContent, lessOpts)
|
|
||||||
.then(result => postcss([autoprefixer]).process(result.css, { from: undefined }))
|
|
||||||
.then(r => r.css);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = transformLess;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
// We convert less import in es/lib to css file path
|
|
||||||
function cssInjection(content) {
|
|
||||||
return content
|
|
||||||
.replace(/\/style\/?'/g, "/style/css'")
|
|
||||||
.replace(/\/style\/?"/g, '/style/css"')
|
|
||||||
.replace(/\.less/g, '.css');
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
cssInjection,
|
|
||||||
};
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import type { ExtractPropTypes, PropType } from 'vue';
|
import type { ExtractPropTypes, PropType } from 'vue';
|
||||||
import { onMounted, ref, defineComponent, onBeforeUnmount } from 'vue';
|
import { shallowRef, onMounted, defineComponent, onBeforeUnmount } from 'vue';
|
||||||
import Button from '../button';
|
import Button from '../button';
|
||||||
import type { ButtonProps } from '../button';
|
import type { ButtonProps } from '../button';
|
||||||
import type { LegacyButtonType } from '../button/buttonTypes';
|
import type { LegacyButtonType } from '../button/buttonTypes';
|
||||||
import { convertLegacyProps } from '../button/buttonTypes';
|
import { convertLegacyProps } from '../button/buttonTypes';
|
||||||
import useDestroyed from './hooks/useDestroyed';
|
import useDestroyed from './hooks/useDestroyed';
|
||||||
|
import { objectType } from './type';
|
||||||
|
import { findDOMNode } from './props-util';
|
||||||
|
|
||||||
const actionButtonProps = {
|
const actionButtonProps = {
|
||||||
type: {
|
type: {
|
||||||
|
|
@ -14,15 +16,15 @@ const actionButtonProps = {
|
||||||
close: Function,
|
close: Function,
|
||||||
autofocus: Boolean,
|
autofocus: Boolean,
|
||||||
prefixCls: String,
|
prefixCls: String,
|
||||||
buttonProps: Object as PropType<ButtonProps>,
|
buttonProps: objectType<ButtonProps>(),
|
||||||
emitEvent: Boolean,
|
emitEvent: Boolean,
|
||||||
quitOnNullishReturnValue: Boolean,
|
quitOnNullishReturnValue: Boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ActionButtonProps = ExtractPropTypes<typeof actionButtonProps>;
|
export type ActionButtonProps = ExtractPropTypes<typeof actionButtonProps>;
|
||||||
|
|
||||||
function isThenable(thing?: PromiseLike<any>): boolean {
|
function isThenable<T>(thing?: PromiseLike<T>): boolean {
|
||||||
return !!(thing && !!thing.then);
|
return !!(thing && thing.then);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
|
@ -30,22 +32,25 @@ export default defineComponent({
|
||||||
name: 'ActionButton',
|
name: 'ActionButton',
|
||||||
props: actionButtonProps,
|
props: actionButtonProps,
|
||||||
setup(props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const clickedRef = ref<boolean>(false);
|
const clickedRef = shallowRef<boolean>(false);
|
||||||
const buttonRef = ref();
|
const buttonRef = shallowRef();
|
||||||
const loading = ref(false);
|
const loading = shallowRef(false);
|
||||||
let timeoutId: any;
|
let timeoutId: any;
|
||||||
const isDestroyed = useDestroyed();
|
const isDestroyed = useDestroyed();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.autofocus) {
|
if (props.autofocus) {
|
||||||
timeoutId = setTimeout(() => buttonRef.value.$el?.focus());
|
timeoutId = setTimeout(() => findDOMNode(buttonRef.value)?.focus?.());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onInternalClose = (...args: any[]) => {
|
||||||
|
props.close?.(...args);
|
||||||
|
};
|
||||||
|
|
||||||
const handlePromiseOnOk = (returnValueOfOnOk?: PromiseLike<any>) => {
|
const handlePromiseOnOk = (returnValueOfOnOk?: PromiseLike<any>) => {
|
||||||
const { close } = props;
|
|
||||||
if (!isThenable(returnValueOfOnOk)) {
|
if (!isThenable(returnValueOfOnOk)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -55,48 +60,46 @@ export default defineComponent({
|
||||||
if (!isDestroyed.value) {
|
if (!isDestroyed.value) {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
close(...args);
|
onInternalClose(...args);
|
||||||
clickedRef.value = false;
|
clickedRef.value = false;
|
||||||
},
|
},
|
||||||
(e: Error) => {
|
(e: Error) => {
|
||||||
// Emit error when catch promise reject
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(e);
|
|
||||||
// See: https://github.com/ant-design/ant-design/issues/6183
|
// See: https://github.com/ant-design/ant-design/issues/6183
|
||||||
if (!isDestroyed.value) {
|
if (!isDestroyed.value) {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
clickedRef.value = false;
|
clickedRef.value = false;
|
||||||
|
return Promise.reject(e);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClick = (e: MouseEvent) => {
|
const onClick = (e: MouseEvent) => {
|
||||||
const { actionFn, close = () => {} } = props;
|
const { actionFn } = props;
|
||||||
if (clickedRef.value) {
|
if (clickedRef.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clickedRef.value = true;
|
clickedRef.value = true;
|
||||||
if (!actionFn) {
|
if (!actionFn) {
|
||||||
close();
|
onInternalClose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let returnValueOfOnOk;
|
let returnValueOfOnOk: PromiseLike<any>;
|
||||||
if (props.emitEvent) {
|
if (props.emitEvent) {
|
||||||
returnValueOfOnOk = actionFn(e);
|
returnValueOfOnOk = actionFn(e);
|
||||||
if (props.quitOnNullishReturnValue && !isThenable(returnValueOfOnOk)) {
|
if (props.quitOnNullishReturnValue && !isThenable(returnValueOfOnOk)) {
|
||||||
clickedRef.value = false;
|
clickedRef.value = false;
|
||||||
close(e);
|
onInternalClose(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (actionFn.length) {
|
} else if (actionFn.length) {
|
||||||
returnValueOfOnOk = actionFn(close);
|
returnValueOfOnOk = actionFn(props.close);
|
||||||
// https://github.com/ant-design/ant-design/issues/23358
|
// https://github.com/ant-design/ant-design/issues/23358
|
||||||
clickedRef.value = false;
|
clickedRef.value = false;
|
||||||
} else {
|
} else {
|
||||||
returnValueOfOnOk = actionFn();
|
returnValueOfOnOk = actionFn();
|
||||||
if (!returnValueOfOnOk) {
|
if (!returnValueOfOnOk) {
|
||||||
close();
|
onInternalClose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,167 @@
|
||||||
import { defineComponent, ref, withDirectives } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import antInput from './antInputDirective';
|
import { computed, defineComponent, shallowRef, ref, watch } from 'vue';
|
||||||
import PropTypes from './vue-types';
|
import PropTypes from './vue-types';
|
||||||
|
import type { BaseInputInnerExpose } from './BaseInputInner';
|
||||||
|
import BaseInputInner from './BaseInputInner';
|
||||||
|
import { styleObjectToString } from '../vc-util/Dom/css';
|
||||||
|
|
||||||
|
export interface BaseInputExpose {
|
||||||
|
focus: () => void;
|
||||||
|
blur: () => void;
|
||||||
|
input: HTMLInputElement | HTMLTextAreaElement | null;
|
||||||
|
setSelectionRange: (
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
direction?: 'forward' | 'backward' | 'none',
|
||||||
|
) => void;
|
||||||
|
select: () => void;
|
||||||
|
getSelectionStart: () => number | null;
|
||||||
|
getSelectionEnd: () => number | null;
|
||||||
|
getScrollTop: () => number | null;
|
||||||
|
setScrollTop: (scrollTop: number) => void;
|
||||||
|
}
|
||||||
const BaseInput = defineComponent({
|
const BaseInput = defineComponent({
|
||||||
compatConfig: { MODE: 3 },
|
compatConfig: { MODE: 3 },
|
||||||
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
value: PropTypes.string.def(''),
|
disabled: PropTypes.looseBool,
|
||||||
|
type: PropTypes.string,
|
||||||
|
value: PropTypes.any,
|
||||||
|
lazy: PropTypes.bool.def(true),
|
||||||
|
tag: {
|
||||||
|
type: String as PropType<'input' | 'textarea'>,
|
||||||
|
default: 'input',
|
||||||
},
|
},
|
||||||
emits: ['change', 'input'],
|
size: PropTypes.string,
|
||||||
setup(_p, { emit }) {
|
style: PropTypes.oneOfType([String, Object]),
|
||||||
const inputRef = ref(null);
|
class: PropTypes.string,
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
'change',
|
||||||
|
'input',
|
||||||
|
'blur',
|
||||||
|
'keydown',
|
||||||
|
'focus',
|
||||||
|
'compositionstart',
|
||||||
|
'compositionend',
|
||||||
|
'keyup',
|
||||||
|
'paste',
|
||||||
|
'mousedown',
|
||||||
|
],
|
||||||
|
setup(props, { emit, attrs, expose }) {
|
||||||
|
const inputRef = shallowRef<BaseInputInnerExpose>(null);
|
||||||
|
const renderValue = ref();
|
||||||
|
const isComposing = ref(false);
|
||||||
|
watch(
|
||||||
|
[() => props.value, isComposing],
|
||||||
|
() => {
|
||||||
|
if (isComposing.value) return;
|
||||||
|
renderValue.value = props.value;
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
const handleChange = (e: Event) => {
|
const handleChange = (e: Event) => {
|
||||||
const { composing } = e.target as any;
|
|
||||||
if ((e as any).isComposing || composing) {
|
|
||||||
emit('input', e);
|
|
||||||
} else {
|
|
||||||
emit('input', e);
|
|
||||||
emit('change', e);
|
emit('change', e);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
return {
|
const onCompositionstart = (e: CompositionEvent) => {
|
||||||
inputRef,
|
isComposing.value = true;
|
||||||
focus: () => {
|
(e.target as any).composing = true;
|
||||||
|
emit('compositionstart', e);
|
||||||
|
};
|
||||||
|
const onCompositionend = (e: CompositionEvent) => {
|
||||||
|
isComposing.value = false;
|
||||||
|
(e.target as any).composing = false;
|
||||||
|
emit('compositionend', e);
|
||||||
|
const event = document.createEvent('HTMLEvents');
|
||||||
|
event.initEvent('input', true, true);
|
||||||
|
e.target.dispatchEvent(event);
|
||||||
|
handleChange(e);
|
||||||
|
};
|
||||||
|
const handleInput = (e: Event) => {
|
||||||
|
if (isComposing.value && props.lazy) {
|
||||||
|
renderValue.value = (e.target as HTMLInputElement).value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit('input', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBlur = (e: Event) => {
|
||||||
|
emit('blur', e);
|
||||||
|
};
|
||||||
|
const handleFocus = (e: Event) => {
|
||||||
|
emit('focus', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
const focus = () => {
|
||||||
if (inputRef.value) {
|
if (inputRef.value) {
|
||||||
inputRef.value.focus();
|
inputRef.value.focus();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
blur: () => {
|
const blur = () => {
|
||||||
if (inputRef.value) {
|
if (inputRef.value) {
|
||||||
inputRef.value.blur();
|
inputRef.value.blur();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
handleChange,
|
|
||||||
};
|
};
|
||||||
},
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
render() {
|
emit('keydown', e);
|
||||||
return withDirectives(
|
};
|
||||||
(
|
const handleKeyUp = (e: KeyboardEvent) => {
|
||||||
<input
|
emit('keyup', e);
|
||||||
{...this.$props}
|
};
|
||||||
{...this.$attrs}
|
const setSelectionRange = (
|
||||||
onInput={this.handleChange}
|
start: number,
|
||||||
onChange={this.handleChange}
|
end: number,
|
||||||
ref="inputRef"
|
direction?: 'forward' | 'backward' | 'none',
|
||||||
|
) => {
|
||||||
|
inputRef.value?.setSelectionRange(start, end, direction);
|
||||||
|
};
|
||||||
|
|
||||||
|
const select = () => {
|
||||||
|
inputRef.value?.select();
|
||||||
|
};
|
||||||
|
expose({
|
||||||
|
focus,
|
||||||
|
blur,
|
||||||
|
input: computed(() => inputRef.value?.input),
|
||||||
|
setSelectionRange,
|
||||||
|
select,
|
||||||
|
getSelectionStart: () => inputRef.value?.getSelectionStart(),
|
||||||
|
getSelectionEnd: () => inputRef.value?.getSelectionEnd(),
|
||||||
|
getScrollTop: () => inputRef.value?.getScrollTop(),
|
||||||
|
});
|
||||||
|
const handleMousedown = (e: MouseEvent) => {
|
||||||
|
emit('mousedown', e);
|
||||||
|
};
|
||||||
|
const handlePaste = (e: ClipboardEvent) => {
|
||||||
|
emit('paste', e);
|
||||||
|
};
|
||||||
|
const styleString = computed(() => {
|
||||||
|
return props.style && typeof props.style !== 'string'
|
||||||
|
? styleObjectToString(props.style)
|
||||||
|
: props.style;
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
const { style, lazy, ...restProps } = props;
|
||||||
|
return (
|
||||||
|
<BaseInputInner
|
||||||
|
{...restProps}
|
||||||
|
{...attrs}
|
||||||
|
style={styleString.value}
|
||||||
|
onInput={handleInput}
|
||||||
|
onChange={handleChange}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
ref={inputRef}
|
||||||
|
value={renderValue.value}
|
||||||
|
onCompositionstart={onCompositionstart}
|
||||||
|
onCompositionend={onCompositionend}
|
||||||
|
onKeyup={handleKeyUp}
|
||||||
|
onKeydown={handleKeyDown}
|
||||||
|
onPaste={handlePaste}
|
||||||
|
onMousedown={handleMousedown}
|
||||||
/>
|
/>
|
||||||
) as any,
|
|
||||||
[[antInput]],
|
|
||||||
);
|
);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
import type { PropType } from 'vue';
|
||||||
|
import { defineComponent, shallowRef } from 'vue';
|
||||||
|
import PropTypes from './vue-types';
|
||||||
|
|
||||||
|
export interface BaseInputInnerExpose {
|
||||||
|
focus: () => void;
|
||||||
|
blur: () => void;
|
||||||
|
input: HTMLInputElement | HTMLTextAreaElement | null;
|
||||||
|
setSelectionRange: (
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
direction?: 'forward' | 'backward' | 'none',
|
||||||
|
) => void;
|
||||||
|
select: () => void;
|
||||||
|
getSelectionStart: () => number | null;
|
||||||
|
getSelectionEnd: () => number | null;
|
||||||
|
getScrollTop: () => number | null;
|
||||||
|
setScrollTop: (scrollTop: number) => void;
|
||||||
|
}
|
||||||
|
const BaseInputInner = defineComponent({
|
||||||
|
compatConfig: { MODE: 3 },
|
||||||
|
// inheritAttrs: false,
|
||||||
|
props: {
|
||||||
|
disabled: PropTypes.looseBool,
|
||||||
|
type: PropTypes.string,
|
||||||
|
value: PropTypes.any,
|
||||||
|
tag: {
|
||||||
|
type: String as PropType<'input' | 'textarea'>,
|
||||||
|
default: 'input',
|
||||||
|
},
|
||||||
|
size: PropTypes.string,
|
||||||
|
onChange: Function as PropType<(e: Event) => void>,
|
||||||
|
onInput: Function as PropType<(e: Event) => void>,
|
||||||
|
onBlur: Function as PropType<(e: Event) => void>,
|
||||||
|
onFocus: Function as PropType<(e: Event) => void>,
|
||||||
|
onKeydown: Function as PropType<(e: Event) => void>,
|
||||||
|
onCompositionstart: Function as PropType<(e: Event) => void>,
|
||||||
|
onCompositionend: Function as PropType<(e: Event) => void>,
|
||||||
|
onKeyup: Function as PropType<(e: Event) => void>,
|
||||||
|
onPaste: Function as PropType<(e: Event) => void>,
|
||||||
|
onMousedown: Function as PropType<(e: Event) => void>,
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
'change',
|
||||||
|
'input',
|
||||||
|
'blur',
|
||||||
|
'keydown',
|
||||||
|
'focus',
|
||||||
|
'compositionstart',
|
||||||
|
'compositionend',
|
||||||
|
'keyup',
|
||||||
|
'paste',
|
||||||
|
'mousedown',
|
||||||
|
],
|
||||||
|
setup(props, { expose }) {
|
||||||
|
const inputRef = shallowRef(null);
|
||||||
|
|
||||||
|
const focus = () => {
|
||||||
|
if (inputRef.value) {
|
||||||
|
inputRef.value.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const blur = () => {
|
||||||
|
if (inputRef.value) {
|
||||||
|
inputRef.value.blur();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const setSelectionRange = (
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
direction?: 'forward' | 'backward' | 'none',
|
||||||
|
) => {
|
||||||
|
inputRef.value?.setSelectionRange(start, end, direction);
|
||||||
|
};
|
||||||
|
|
||||||
|
const select = () => {
|
||||||
|
inputRef.value?.select();
|
||||||
|
};
|
||||||
|
expose({
|
||||||
|
focus,
|
||||||
|
blur,
|
||||||
|
input: inputRef,
|
||||||
|
setSelectionRange,
|
||||||
|
select,
|
||||||
|
getSelectionStart: () => inputRef.value?.selectionStart,
|
||||||
|
getSelectionEnd: () => inputRef.value?.selectionEnd,
|
||||||
|
getScrollTop: () => inputRef.value?.scrollTop,
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
const { tag: Tag, value, ...restProps } = props;
|
||||||
|
return <Tag {...restProps} ref={inputRef} value={value} />;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default BaseInputInner;
|
||||||
|
|
@ -3,7 +3,7 @@ import { getOptionProps } from './props-util';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
setState(state = {}, callback) {
|
setState(state = {}, callback: () => any) {
|
||||||
let newState = typeof state === 'function' ? state(this.$data, this.$props) : state;
|
let newState = typeof state === 'function' ? state(this.$data, this.$props) : state;
|
||||||
if (this.getDerivedStateFromProps) {
|
if (this.getDerivedStateFromProps) {
|
||||||
const s = this.getDerivedStateFromProps(getOptionProps(this), {
|
const s = this.getDerivedStateFromProps(getOptionProps(this), {
|
||||||
|
|
@ -26,6 +26,7 @@ export default {
|
||||||
},
|
},
|
||||||
__emit() {
|
__emit() {
|
||||||
// 直接调用事件,底层组件不需要vueTool记录events
|
// 直接调用事件,底层组件不需要vueTool记录events
|
||||||
|
// eslint-disable-next-line prefer-rest-params
|
||||||
const args = [].slice.call(arguments, 0);
|
const args = [].slice.call(arguments, 0);
|
||||||
let eventName = args[0];
|
let eventName = args[0];
|
||||||
eventName = `on${eventName[0].toUpperCase()}${eventName.substring(1)}`;
|
eventName = `on${eventName[0].toUpperCase()}${eventName.substring(1)}`;
|
||||||
|
|
@ -3,7 +3,7 @@ import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
nextTick,
|
nextTick,
|
||||||
onBeforeMount,
|
onBeforeMount,
|
||||||
onBeforeUnmount,
|
onMounted,
|
||||||
onUpdated,
|
onUpdated,
|
||||||
Teleport,
|
Teleport,
|
||||||
watch,
|
watch,
|
||||||
|
|
@ -23,12 +23,24 @@ export default defineComponent({
|
||||||
// getContainer 不会改变,不用响应式
|
// getContainer 不会改变,不用响应式
|
||||||
let container: HTMLElement;
|
let container: HTMLElement;
|
||||||
const { shouldRender } = useInjectPortal();
|
const { shouldRender } = useInjectPortal();
|
||||||
onBeforeMount(() => {
|
|
||||||
isSSR = false;
|
function setContainer() {
|
||||||
if (shouldRender.value) {
|
if (shouldRender.value) {
|
||||||
container = props.getContainer();
|
container = props.getContainer();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
isSSR = false;
|
||||||
|
// drawer
|
||||||
|
setContainer();
|
||||||
});
|
});
|
||||||
|
onMounted(() => {
|
||||||
|
if (container) return;
|
||||||
|
// https://github.com/vueComponent/ant-design-vue/issues/6937
|
||||||
|
setContainer();
|
||||||
|
});
|
||||||
|
|
||||||
const stopWatch = watch(shouldRender, () => {
|
const stopWatch = watch(shouldRender, () => {
|
||||||
if (shouldRender.value && !container) {
|
if (shouldRender.value && !container) {
|
||||||
container = props.getContainer();
|
container = props.getContainer();
|
||||||
|
|
@ -44,11 +56,11 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
onBeforeUnmount(() => {
|
// onBeforeUnmount(() => {
|
||||||
if (container && container.parentNode) {
|
// if (container && container.parentNode) {
|
||||||
container.parentNode.removeChild(container);
|
// container.parentNode.removeChild(container);
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
return () => {
|
return () => {
|
||||||
if (!shouldRender.value) return null;
|
if (!shouldRender.value) return null;
|
||||||
if (isSSR) {
|
if (isSSR) {
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
import PropTypes from './vue-types';
|
import PropTypes from './vue-types';
|
||||||
import switchScrollingEffect from './switchScrollingEffect';
|
|
||||||
import setStyle from './setStyle';
|
|
||||||
import Portal from './Portal';
|
import Portal from './Portal';
|
||||||
import {
|
import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
ref,
|
shallowRef,
|
||||||
watch,
|
watch,
|
||||||
onMounted,
|
onMounted,
|
||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
onUpdated,
|
onUpdated,
|
||||||
getCurrentInstance,
|
|
||||||
nextTick,
|
nextTick,
|
||||||
|
computed,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import canUseDom from './canUseDom';
|
import canUseDom from './canUseDom';
|
||||||
import ScrollLocker from '../vc-util/Dom/scrollLocker';
|
|
||||||
import raf from './raf';
|
import raf from './raf';
|
||||||
|
import { booleanType } from './type';
|
||||||
|
import useScrollLocker from './hooks/useScrollLocker';
|
||||||
|
|
||||||
let openCount = 0;
|
let openCount = 0;
|
||||||
const supportDom = canUseDom();
|
const supportDom = canUseDom();
|
||||||
|
|
@ -24,17 +23,13 @@ export function getOpenCount() {
|
||||||
return process.env.NODE_ENV === 'test' ? openCount : 0;
|
return process.env.NODE_ENV === 'test' ? openCount : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/ant-design/ant-design/issues/19340
|
|
||||||
// https://github.com/ant-design/ant-design/issues/19332
|
|
||||||
let cacheOverflow = {};
|
|
||||||
|
|
||||||
const getParent = (getContainer: GetContainer) => {
|
const getParent = (getContainer: GetContainer) => {
|
||||||
if (!supportDom) {
|
if (!supportDom) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (getContainer) {
|
if (getContainer) {
|
||||||
if (typeof getContainer === 'string') {
|
if (typeof getContainer === 'string') {
|
||||||
return document.querySelectorAll(getContainer)[0];
|
return document.querySelectorAll(getContainer)[0] as HTMLElement;
|
||||||
}
|
}
|
||||||
if (typeof getContainer === 'function') {
|
if (typeof getContainer === 'function') {
|
||||||
return getContainer();
|
return getContainer();
|
||||||
|
|
@ -57,24 +52,28 @@ export default defineComponent({
|
||||||
forceRender: { type: Boolean, default: undefined },
|
forceRender: { type: Boolean, default: undefined },
|
||||||
getContainer: PropTypes.any,
|
getContainer: PropTypes.any,
|
||||||
visible: { type: Boolean, default: undefined },
|
visible: { type: Boolean, default: undefined },
|
||||||
|
autoLock: booleanType(),
|
||||||
|
didUpdate: Function,
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const container = ref<HTMLElement>();
|
const container = shallowRef<HTMLElement>();
|
||||||
const componentRef = ref();
|
const componentRef = shallowRef();
|
||||||
const rafId = ref<number>();
|
const rafId = shallowRef<number>();
|
||||||
const scrollLocker = new ScrollLocker({
|
const triggerUpdate = shallowRef(1);
|
||||||
container: getParent(props.getContainer) as HTMLElement,
|
const defaultContainer = canUseDom() && document.createElement('div');
|
||||||
});
|
|
||||||
|
|
||||||
const removeCurrentContainer = () => {
|
const removeCurrentContainer = () => {
|
||||||
// Portal will remove from `parentNode`.
|
// Portal will remove from `parentNode`.
|
||||||
// Let's handle this again to avoid refactor issue.
|
// Let's handle this again to avoid refactor issue.
|
||||||
|
if (container.value === defaultContainer) {
|
||||||
container.value?.parentNode?.removeChild(container.value);
|
container.value?.parentNode?.removeChild(container.value);
|
||||||
|
}
|
||||||
|
container.value = null;
|
||||||
};
|
};
|
||||||
|
let parent: HTMLElement = null;
|
||||||
const attachToParent = (force = false) => {
|
const attachToParent = (force = false) => {
|
||||||
if (force || (container.value && !container.value.parentNode)) {
|
if (force || (container.value && !container.value.parentNode)) {
|
||||||
const parent = getParent(props.getContainer);
|
parent = getParent(props.getContainer);
|
||||||
if (parent) {
|
if (parent) {
|
||||||
parent.appendChild(container.value);
|
parent.appendChild(container.value);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -85,14 +84,12 @@ export default defineComponent({
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
// attachToParent();
|
|
||||||
|
|
||||||
const getContainer = () => {
|
const getContainer = () => {
|
||||||
if (!supportDom) {
|
if (!supportDom) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!container.value) {
|
if (!container.value) {
|
||||||
container.value = document.createElement('div');
|
container.value = defaultContainer;
|
||||||
attachToParent(true);
|
attachToParent(true);
|
||||||
}
|
}
|
||||||
setWrapperClassName();
|
setWrapperClassName();
|
||||||
|
|
@ -108,43 +105,33 @@ export default defineComponent({
|
||||||
setWrapperClassName();
|
setWrapperClassName();
|
||||||
attachToParent();
|
attachToParent();
|
||||||
});
|
});
|
||||||
/**
|
|
||||||
* Enhance ./switchScrollingEffect
|
useScrollLocker(
|
||||||
* 1. Simulate document body scroll bar with
|
computed(() => {
|
||||||
* 2. Record body has overflow style and recover when all of PortalWrapper invisible
|
return (
|
||||||
* 3. Disable body scroll when PortalWrapper has open
|
props.autoLock &&
|
||||||
*
|
props.visible &&
|
||||||
* @memberof PortalWrapper
|
canUseDom() &&
|
||||||
*/
|
(container.value === document.body || container.value === defaultContainer)
|
||||||
const switchScrolling = () => {
|
);
|
||||||
if (openCount === 1 && !Object.keys(cacheOverflow).length) {
|
}),
|
||||||
switchScrollingEffect();
|
);
|
||||||
// Must be set after switchScrollingEffect
|
|
||||||
cacheOverflow = setStyle({
|
|
||||||
overflow: 'hidden',
|
|
||||||
overflowX: 'hidden',
|
|
||||||
overflowY: 'hidden',
|
|
||||||
});
|
|
||||||
} else if (!openCount) {
|
|
||||||
setStyle(cacheOverflow);
|
|
||||||
cacheOverflow = {};
|
|
||||||
switchScrollingEffect(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const instance = getCurrentInstance();
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let init = false;
|
let init = false;
|
||||||
watch(
|
watch(
|
||||||
[() => props.visible, () => props.getContainer],
|
[() => props.visible, () => props.getContainer],
|
||||||
([visible, getContainer], [prevVisible, prevGetContainer]) => {
|
([visible, getContainer], [prevVisible, prevGetContainer]) => {
|
||||||
// Update count
|
// Update count
|
||||||
if (supportDom && getParent(props.getContainer) === document.body) {
|
if (supportDom) {
|
||||||
|
parent = getParent(props.getContainer);
|
||||||
|
if (parent === document.body) {
|
||||||
if (visible && !prevVisible) {
|
if (visible && !prevVisible) {
|
||||||
openCount += 1;
|
openCount += 1;
|
||||||
} else if (init) {
|
} else if (init) {
|
||||||
openCount -= 1;
|
openCount -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (init) {
|
if (init) {
|
||||||
// Clean up container if needed
|
// Clean up container if needed
|
||||||
|
|
@ -157,17 +144,6 @@ export default defineComponent({
|
||||||
) {
|
) {
|
||||||
removeCurrentContainer();
|
removeCurrentContainer();
|
||||||
}
|
}
|
||||||
// updateScrollLocker
|
|
||||||
if (
|
|
||||||
visible &&
|
|
||||||
visible !== prevVisible &&
|
|
||||||
supportDom &&
|
|
||||||
getParent(getContainer) !== scrollLocker.getContainer()
|
|
||||||
) {
|
|
||||||
scrollLocker.reLock({
|
|
||||||
container: getParent(getContainer) as HTMLElement,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
init = true;
|
init = true;
|
||||||
},
|
},
|
||||||
|
|
@ -177,37 +153,34 @@ export default defineComponent({
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (!attachToParent()) {
|
if (!attachToParent()) {
|
||||||
rafId.value = raf(() => {
|
rafId.value = raf(() => {
|
||||||
instance.update();
|
triggerUpdate.value += 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
const { visible, getContainer } = props;
|
const { visible } = props;
|
||||||
if (supportDom && getParent(getContainer) === document.body) {
|
if (supportDom && parent === document.body) {
|
||||||
// 离开时不会 render, 导到离开时数值不变,改用 func 。。
|
// 离开时不会 render, 导到离开时数值不变,改用 func 。。
|
||||||
openCount = visible && openCount ? openCount - 1 : openCount;
|
openCount = visible && openCount ? openCount - 1 : openCount;
|
||||||
}
|
}
|
||||||
removeCurrentContainer();
|
removeCurrentContainer();
|
||||||
raf.cancel(rafId.value);
|
raf.cancel(rafId.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const { forceRender, visible } = props;
|
const { forceRender, visible } = props;
|
||||||
let portal = null;
|
let portal = null;
|
||||||
const childProps = {
|
const childProps = {
|
||||||
getOpenCount: () => openCount,
|
getOpenCount: () => openCount,
|
||||||
getContainer,
|
getContainer,
|
||||||
switchScrollingEffect: switchScrolling,
|
|
||||||
scrollLocker,
|
|
||||||
};
|
};
|
||||||
|
if (triggerUpdate.value && (forceRender || visible || componentRef.value)) {
|
||||||
if (forceRender || visible || componentRef.value) {
|
|
||||||
portal = (
|
portal = (
|
||||||
<Portal
|
<Portal
|
||||||
getContainer={getContainer}
|
getContainer={getContainer}
|
||||||
ref={componentRef}
|
ref={componentRef}
|
||||||
|
didUpdate={props.didUpdate}
|
||||||
v-slots={{ default: () => slots.default?.(childProps) }}
|
v-slots={{ default: () => slots.default?.(childProps) }}
|
||||||
></Portal>
|
></Portal>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { customRenderSlot } from '../vnode';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'RenderSlot',
|
||||||
|
setup(_props, { slots }) {
|
||||||
|
return () => {
|
||||||
|
return customRenderSlot(slots, 'default', {}, () => ['default value']);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import RenderSlot from '../__mocks__/RenderSlot';
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import { nextTick } from 'vue';
|
||||||
|
|
||||||
|
describe('render slot content', () => {
|
||||||
|
it('renders slot content', () => {
|
||||||
|
const wrapper = mount(RenderSlot, {
|
||||||
|
slots: {
|
||||||
|
default: () => 'This is slot content',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.html()).toContain('This is slot content');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('render default value when slot is fragment', async () => {
|
||||||
|
const wrapper = mount(RenderSlot, {
|
||||||
|
slots: {
|
||||||
|
default: () => <></>,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
expect(wrapper.html()).toContain('default value');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
function onCompositionStart(e) {
|
|
||||||
e.target.composing = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onCompositionEnd(e) {
|
|
||||||
// prevent triggering an input event for no reason
|
|
||||||
if (!e.target.composing) return;
|
|
||||||
e.target.composing = false;
|
|
||||||
trigger(e.target, 'input');
|
|
||||||
}
|
|
||||||
|
|
||||||
function trigger(el, type) {
|
|
||||||
const e = document.createEvent('HTMLEvents');
|
|
||||||
e.initEvent(type, true, true);
|
|
||||||
el.dispatchEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addEventListener(el, event, handler, options) {
|
|
||||||
el.addEventListener(event, handler, options);
|
|
||||||
}
|
|
||||||
const antInput = {
|
|
||||||
created(el, binding) {
|
|
||||||
if (!binding.modifiers || !binding.modifiers.lazy) {
|
|
||||||
addEventListener(el, 'compositionstart', onCompositionStart);
|
|
||||||
addEventListener(el, 'compositionend', onCompositionEnd);
|
|
||||||
// Safari < 10.2 & UIWebView doesn't fire compositionend when
|
|
||||||
// switching focus before confirming composition choice
|
|
||||||
// this also fixes the issue where some browsers e.g. iOS Chrome
|
|
||||||
// fires "change" instead of "input" on autocomplete.
|
|
||||||
addEventListener(el, 'change', onCompositionEnd);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default antInput;
|
|
||||||
|
|
@ -1,23 +1,34 @@
|
||||||
import type { ElementOf } from './type';
|
import type { PresetColorKey } from '../theme/interface';
|
||||||
import { tuple } from './type';
|
import { PresetColors } from '../theme/interface';
|
||||||
|
|
||||||
export const PresetStatusColorTypes = tuple('success', 'processing', 'error', 'default', 'warning');
|
type InverseColor = `${PresetColorKey}-inverse`;
|
||||||
|
const inverseColors = PresetColors.map<InverseColor>(color => `${color}-inverse`);
|
||||||
|
|
||||||
export const PresetColorTypes = tuple(
|
export const PresetStatusColorTypes = [
|
||||||
'pink',
|
'success',
|
||||||
'red',
|
'processing',
|
||||||
'yellow',
|
'error',
|
||||||
'orange',
|
'default',
|
||||||
'cyan',
|
'warning',
|
||||||
'green',
|
] as const;
|
||||||
'blue',
|
|
||||||
'purple',
|
|
||||||
'geekblue',
|
|
||||||
'magenta',
|
|
||||||
'volcano',
|
|
||||||
'gold',
|
|
||||||
'lime',
|
|
||||||
);
|
|
||||||
|
|
||||||
export type PresetColorType = ElementOf<typeof PresetColorTypes>;
|
export type PresetColorType = PresetColorKey | InverseColor;
|
||||||
export type PresetStatusColorType = ElementOf<typeof PresetStatusColorTypes>;
|
|
||||||
|
export type PresetStatusColorType = (typeof PresetStatusColorTypes)[number];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determine if the color keyword belongs to the `Ant Design` {@link PresetColors}.
|
||||||
|
* @param color color to be judged
|
||||||
|
* @param includeInverse whether to include reversed colors
|
||||||
|
*/
|
||||||
|
export function isPresetColor(color?: any, includeInverse = true) {
|
||||||
|
if (includeInverse) {
|
||||||
|
return [...inverseColors, ...PresetColors].includes(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PresetColors.includes(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPresetStatusColor(color?: any): color is PresetStatusColorType {
|
||||||
|
return PresetStatusColorTypes.includes(color);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { inject, provide, reactive, watchEffect } from 'vue';
|
||||||
|
|
||||||
|
function createContext<T extends Record<string, any>>(defaultValue?: T) {
|
||||||
|
const contextKey = Symbol('contextKey');
|
||||||
|
const useProvide = (props: T, newProps?: T) => {
|
||||||
|
const mergedProps = reactive<T>({} as T);
|
||||||
|
provide(contextKey, mergedProps);
|
||||||
|
watchEffect(() => {
|
||||||
|
Object.assign(mergedProps, props, newProps || {});
|
||||||
|
});
|
||||||
|
return mergedProps;
|
||||||
|
};
|
||||||
|
const useInject = () => {
|
||||||
|
return inject(contextKey, defaultValue as T) || ({} as T);
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
useProvide,
|
||||||
|
useInject,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createContext;
|
||||||
|
|
@ -1,130 +0,0 @@
|
||||||
const START_EVENT_NAME_MAP = {
|
|
||||||
transitionstart: {
|
|
||||||
transition: 'transitionstart',
|
|
||||||
WebkitTransition: 'webkitTransitionStart',
|
|
||||||
MozTransition: 'mozTransitionStart',
|
|
||||||
OTransition: 'oTransitionStart',
|
|
||||||
msTransition: 'MSTransitionStart',
|
|
||||||
},
|
|
||||||
|
|
||||||
animationstart: {
|
|
||||||
animation: 'animationstart',
|
|
||||||
WebkitAnimation: 'webkitAnimationStart',
|
|
||||||
MozAnimation: 'mozAnimationStart',
|
|
||||||
OAnimation: 'oAnimationStart',
|
|
||||||
msAnimation: 'MSAnimationStart',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const END_EVENT_NAME_MAP = {
|
|
||||||
transitionend: {
|
|
||||||
transition: 'transitionend',
|
|
||||||
WebkitTransition: 'webkitTransitionEnd',
|
|
||||||
MozTransition: 'mozTransitionEnd',
|
|
||||||
OTransition: 'oTransitionEnd',
|
|
||||||
msTransition: 'MSTransitionEnd',
|
|
||||||
},
|
|
||||||
|
|
||||||
animationend: {
|
|
||||||
animation: 'animationend',
|
|
||||||
WebkitAnimation: 'webkitAnimationEnd',
|
|
||||||
MozAnimation: 'mozAnimationEnd',
|
|
||||||
OAnimation: 'oAnimationEnd',
|
|
||||||
msAnimation: 'MSAnimationEnd',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const startEvents = [];
|
|
||||||
const endEvents = [];
|
|
||||||
|
|
||||||
function detectEvents() {
|
|
||||||
const testEl = document.createElement('div');
|
|
||||||
const style = testEl.style;
|
|
||||||
|
|
||||||
if (!('AnimationEvent' in window)) {
|
|
||||||
delete START_EVENT_NAME_MAP.animationstart.animation;
|
|
||||||
delete END_EVENT_NAME_MAP.animationend.animation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!('TransitionEvent' in window)) {
|
|
||||||
delete START_EVENT_NAME_MAP.transitionstart.transition;
|
|
||||||
delete END_EVENT_NAME_MAP.transitionend.transition;
|
|
||||||
}
|
|
||||||
|
|
||||||
function process(EVENT_NAME_MAP, events) {
|
|
||||||
for (const baseEventName in EVENT_NAME_MAP) {
|
|
||||||
if (EVENT_NAME_MAP.hasOwnProperty(baseEventName)) {
|
|
||||||
const baseEvents = EVENT_NAME_MAP[baseEventName];
|
|
||||||
for (const styleName in baseEvents) {
|
|
||||||
if (styleName in style) {
|
|
||||||
events.push(baseEvents[styleName]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
process(START_EVENT_NAME_MAP, startEvents);
|
|
||||||
process(END_EVENT_NAME_MAP, endEvents);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
||||||
detectEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
function addEventListener(node, eventName, eventListener) {
|
|
||||||
node.addEventListener(eventName, eventListener, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeEventListener(node, eventName, eventListener) {
|
|
||||||
node.removeEventListener(eventName, eventListener, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const TransitionEvents = {
|
|
||||||
// Start events
|
|
||||||
startEvents,
|
|
||||||
|
|
||||||
addStartEventListener(node, eventListener) {
|
|
||||||
if (startEvents.length === 0) {
|
|
||||||
setTimeout(eventListener, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
startEvents.forEach(startEvent => {
|
|
||||||
addEventListener(node, startEvent, eventListener);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
removeStartEventListener(node, eventListener) {
|
|
||||||
if (startEvents.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
startEvents.forEach(startEvent => {
|
|
||||||
removeEventListener(node, startEvent, eventListener);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// End events
|
|
||||||
endEvents,
|
|
||||||
|
|
||||||
addEndEventListener(node, eventListener) {
|
|
||||||
if (endEvents.length === 0) {
|
|
||||||
setTimeout(eventListener, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
endEvents.forEach(endEvent => {
|
|
||||||
addEventListener(node, endEvent, eventListener);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
removeEndEventListener(node, eventListener) {
|
|
||||||
if (endEvents.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
endEvents.forEach(endEvent => {
|
|
||||||
removeEventListener(node, endEvent, eventListener);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TransitionEvents;
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
// https://github.com/yiminghe/css-animation 1.5.0
|
|
||||||
|
|
||||||
import Event from './Event';
|
|
||||||
import classes from '../component-classes';
|
|
||||||
import { requestAnimationTimeout, cancelAnimationTimeout } from '../requestAnimationTimeout';
|
|
||||||
import { inBrowser } from '../env';
|
|
||||||
|
|
||||||
const isCssAnimationSupported = Event.endEvents.length !== 0;
|
|
||||||
const capitalPrefixes = [
|
|
||||||
'Webkit',
|
|
||||||
'Moz',
|
|
||||||
'O',
|
|
||||||
// ms is special .... !
|
|
||||||
'ms',
|
|
||||||
];
|
|
||||||
const prefixes = ['-webkit-', '-moz-', '-o-', 'ms-', ''];
|
|
||||||
|
|
||||||
function getStyleProperty(node, name) {
|
|
||||||
if (inBrowser) return '';
|
|
||||||
// old ff need null, https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
|
|
||||||
const style = window.getComputedStyle(node, null);
|
|
||||||
let ret = '';
|
|
||||||
for (let i = 0; i < prefixes.length; i++) {
|
|
||||||
ret = style.getPropertyValue(prefixes[i] + name);
|
|
||||||
if (ret) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fixBrowserByTimeout(node) {
|
|
||||||
if (isCssAnimationSupported) {
|
|
||||||
const transitionDelay = parseFloat(getStyleProperty(node, 'transition-delay')) || 0;
|
|
||||||
const transitionDuration = parseFloat(getStyleProperty(node, 'transition-duration')) || 0;
|
|
||||||
const animationDelay = parseFloat(getStyleProperty(node, 'animation-delay')) || 0;
|
|
||||||
const animationDuration = parseFloat(getStyleProperty(node, 'animation-duration')) || 0;
|
|
||||||
const time = Math.max(transitionDuration + transitionDelay, animationDuration + animationDelay);
|
|
||||||
// sometimes, browser bug
|
|
||||||
node.rcEndAnimTimeout = setTimeout(() => {
|
|
||||||
node.rcEndAnimTimeout = null;
|
|
||||||
if (node.rcEndListener) {
|
|
||||||
node.rcEndListener();
|
|
||||||
}
|
|
||||||
}, time * 1000 + 200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearBrowserBugTimeout(node) {
|
|
||||||
if (node.rcEndAnimTimeout) {
|
|
||||||
clearTimeout(node.rcEndAnimTimeout);
|
|
||||||
node.rcEndAnimTimeout = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cssAnimation = (node, transitionName, endCallback) => {
|
|
||||||
const nameIsObj = typeof transitionName === 'object';
|
|
||||||
const className = nameIsObj ? transitionName.name : transitionName;
|
|
||||||
const activeClassName = nameIsObj ? transitionName.active : `${transitionName}-active`;
|
|
||||||
let end = endCallback;
|
|
||||||
let start;
|
|
||||||
let active;
|
|
||||||
const nodeClasses = classes(node);
|
|
||||||
|
|
||||||
if (endCallback && Object.prototype.toString.call(endCallback) === '[object Object]') {
|
|
||||||
end = endCallback.end;
|
|
||||||
start = endCallback.start;
|
|
||||||
active = endCallback.active;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.rcEndListener) {
|
|
||||||
node.rcEndListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
node.rcEndListener = e => {
|
|
||||||
if (e && e.target !== node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.rcAnimTimeout) {
|
|
||||||
cancelAnimationTimeout(node.rcAnimTimeout);
|
|
||||||
node.rcAnimTimeout = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearBrowserBugTimeout(node);
|
|
||||||
|
|
||||||
nodeClasses.remove(className);
|
|
||||||
nodeClasses.remove(activeClassName);
|
|
||||||
|
|
||||||
Event.removeEndEventListener(node, node.rcEndListener);
|
|
||||||
node.rcEndListener = null;
|
|
||||||
|
|
||||||
// Usually this optional end is used for informing an owner of
|
|
||||||
// a leave animation and telling it to remove the child.
|
|
||||||
if (end) {
|
|
||||||
end();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Event.addEndEventListener(node, node.rcEndListener);
|
|
||||||
|
|
||||||
if (start) {
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
nodeClasses.add(className);
|
|
||||||
|
|
||||||
node.rcAnimTimeout = requestAnimationTimeout(() => {
|
|
||||||
node.rcAnimTimeout = null;
|
|
||||||
|
|
||||||
nodeClasses.add(className);
|
|
||||||
nodeClasses.add(activeClassName);
|
|
||||||
|
|
||||||
if (active) {
|
|
||||||
requestAnimationTimeout(active, 0);
|
|
||||||
}
|
|
||||||
fixBrowserByTimeout(node);
|
|
||||||
// 30ms for firefox
|
|
||||||
}, 30);
|
|
||||||
|
|
||||||
return {
|
|
||||||
stop() {
|
|
||||||
if (node.rcEndListener) {
|
|
||||||
node.rcEndListener();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
cssAnimation.style = (node, style, callback) => {
|
|
||||||
if (node.rcEndListener) {
|
|
||||||
node.rcEndListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
node.rcEndListener = e => {
|
|
||||||
if (e && e.target !== node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.rcAnimTimeout) {
|
|
||||||
cancelAnimationTimeout(node.rcAnimTimeout);
|
|
||||||
node.rcAnimTimeout = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearBrowserBugTimeout(node);
|
|
||||||
|
|
||||||
Event.removeEndEventListener(node, node.rcEndListener);
|
|
||||||
node.rcEndListener = null;
|
|
||||||
|
|
||||||
// Usually this optional callback is used for informing an owner of
|
|
||||||
// a leave animation and telling it to remove the child.
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Event.addEndEventListener(node, node.rcEndListener);
|
|
||||||
|
|
||||||
node.rcAnimTimeout = requestAnimationTimeout(() => {
|
|
||||||
for (const s in style) {
|
|
||||||
if (style.hasOwnProperty(s)) {
|
|
||||||
node.style[s] = style[s];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node.rcAnimTimeout = null;
|
|
||||||
fixBrowserByTimeout(node);
|
|
||||||
}, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
cssAnimation.setTransition = (node, p, value) => {
|
|
||||||
let property = p;
|
|
||||||
let v = value;
|
|
||||||
if (value === undefined) {
|
|
||||||
v = property;
|
|
||||||
property = '';
|
|
||||||
}
|
|
||||||
property = property || '';
|
|
||||||
capitalPrefixes.forEach(prefix => {
|
|
||||||
node.style[`${prefix}Transition${property}`] = v;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
cssAnimation.isCssAnimationSupported = isCssAnimationSupported;
|
|
||||||
|
|
||||||
export { isCssAnimationSupported };
|
|
||||||
|
|
||||||
export default cssAnimation;
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
export type KeyType = string | number;
|
||||||
|
type ValueType = [number, any]; // [times, realValue]
|
||||||
|
const SPLIT = '%';
|
||||||
|
class Entity {
|
||||||
|
instanceId: string;
|
||||||
|
constructor(instanceId: string) {
|
||||||
|
this.instanceId = instanceId;
|
||||||
|
}
|
||||||
|
/** @private Internal cache map. Do not access this directly */
|
||||||
|
cache = new Map<string, ValueType>();
|
||||||
|
|
||||||
|
get(keys: KeyType[] | string): ValueType | null {
|
||||||
|
return this.cache.get(Array.isArray(keys) ? keys.join(SPLIT) : keys) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(keys: KeyType[] | string, valueFn: (origin: ValueType | null) => ValueType | null) {
|
||||||
|
const path = Array.isArray(keys) ? keys.join(SPLIT) : keys;
|
||||||
|
const prevValue = this.cache.get(path)!;
|
||||||
|
const nextValue = valueFn(prevValue);
|
||||||
|
|
||||||
|
if (nextValue === null) {
|
||||||
|
this.cache.delete(path);
|
||||||
|
} else {
|
||||||
|
this.cache.set(path, nextValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Entity;
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import type { CSSInterpolation } from './hooks/useStyleRegister';
|
||||||
|
|
||||||
|
class Keyframe {
|
||||||
|
private name: string;
|
||||||
|
style: CSSInterpolation;
|
||||||
|
|
||||||
|
constructor(name: string, style: CSSInterpolation) {
|
||||||
|
this.name = name;
|
||||||
|
this.style = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
getName(hashId = ''): string {
|
||||||
|
return hashId ? `${hashId}-${this.name}` : this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
_keyframe = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Keyframe;
|
||||||
|
|
@ -0,0 +1,195 @@
|
||||||
|
import type { ShallowRef, ExtractPropTypes, InjectionKey, Ref } from 'vue';
|
||||||
|
import {
|
||||||
|
provide,
|
||||||
|
defineComponent,
|
||||||
|
unref,
|
||||||
|
inject,
|
||||||
|
watch,
|
||||||
|
shallowRef,
|
||||||
|
getCurrentInstance,
|
||||||
|
} from 'vue';
|
||||||
|
import CacheEntity from './Cache';
|
||||||
|
import type { Linter } from './linters/interface';
|
||||||
|
import type { Transformer } from './transformers/interface';
|
||||||
|
import { arrayType, booleanType, objectType, someType, stringType, withInstall } from '../type';
|
||||||
|
export const ATTR_TOKEN = 'data-token-hash';
|
||||||
|
export const ATTR_MARK = 'data-css-hash';
|
||||||
|
export const ATTR_CACHE_PATH = 'data-cache-path';
|
||||||
|
|
||||||
|
// Mark css-in-js instance in style element
|
||||||
|
export const CSS_IN_JS_INSTANCE = '__cssinjs_instance__';
|
||||||
|
|
||||||
|
export function createCache() {
|
||||||
|
const cssinjsInstanceId = Math.random().toString(12).slice(2);
|
||||||
|
|
||||||
|
// Tricky SSR: Move all inline style to the head.
|
||||||
|
// PS: We do not recommend tricky mode.
|
||||||
|
if (typeof document !== 'undefined' && document.head && document.body) {
|
||||||
|
const styles = document.body.querySelectorAll(`style[${ATTR_MARK}]`) || [];
|
||||||
|
const { firstChild } = document.head;
|
||||||
|
|
||||||
|
Array.from(styles).forEach(style => {
|
||||||
|
(style as any)[CSS_IN_JS_INSTANCE] = (style as any)[CSS_IN_JS_INSTANCE] || cssinjsInstanceId;
|
||||||
|
|
||||||
|
// Not force move if no head
|
||||||
|
// Not force move if no head
|
||||||
|
if ((style as any)[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
|
||||||
|
document.head.insertBefore(style, firstChild);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deduplicate of moved styles
|
||||||
|
const styleHash: Record<string, boolean> = {};
|
||||||
|
Array.from(document.querySelectorAll(`style[${ATTR_MARK}]`)).forEach(style => {
|
||||||
|
const hash = style.getAttribute(ATTR_MARK)!;
|
||||||
|
if (styleHash[hash]) {
|
||||||
|
if ((style as any)[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
|
||||||
|
style.parentNode?.removeChild(style);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
styleHash[hash] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CacheEntity(cssinjsInstanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type HashPriority = 'low' | 'high';
|
||||||
|
|
||||||
|
export interface StyleContextProps {
|
||||||
|
autoClear?: boolean;
|
||||||
|
/** @private Test only. Not work in production. */
|
||||||
|
mock?: 'server' | 'client';
|
||||||
|
/**
|
||||||
|
* Only set when you need ssr to extract style on you own.
|
||||||
|
* If not provided, it will auto create <style /> on the end of Provider in server side.
|
||||||
|
*/
|
||||||
|
cache: CacheEntity;
|
||||||
|
/** Tell children that this context is default generated context */
|
||||||
|
defaultCache: boolean;
|
||||||
|
/** Use `:where` selector to reduce hashId css selector priority */
|
||||||
|
hashPriority?: HashPriority;
|
||||||
|
/** Tell cssinjs where to inject style in */
|
||||||
|
container?: Element | ShadowRoot;
|
||||||
|
/** Component wil render inline `<style />` for fallback in SSR. Not recommend. */
|
||||||
|
ssrInline?: boolean;
|
||||||
|
/** Transform css before inject in document. Please note that `transformers` do not support dynamic update */
|
||||||
|
transformers?: Transformer[];
|
||||||
|
/**
|
||||||
|
* Linters to lint css before inject in document.
|
||||||
|
* Styles will be linted after transforming.
|
||||||
|
* Please note that `linters` do not support dynamic update.
|
||||||
|
*/
|
||||||
|
linters?: Linter[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const StyleContextKey: InjectionKey<ShallowRef<Partial<StyleContextProps>>> =
|
||||||
|
Symbol('StyleContextKey');
|
||||||
|
|
||||||
|
export type UseStyleProviderProps = Partial<StyleContextProps> | Ref<Partial<StyleContextProps>>;
|
||||||
|
|
||||||
|
// fix: https://github.com/vueComponent/ant-design-vue/issues/7023
|
||||||
|
const getCache = () => {
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
let cache: CacheEntity;
|
||||||
|
if (instance && instance.appContext) {
|
||||||
|
const globalCache = instance.appContext?.config?.globalProperties?.__ANTDV_CSSINJS_CACHE__;
|
||||||
|
if (globalCache) {
|
||||||
|
cache = globalCache;
|
||||||
|
} else {
|
||||||
|
cache = createCache();
|
||||||
|
if (instance.appContext.config.globalProperties) {
|
||||||
|
instance.appContext.config.globalProperties.__ANTDV_CSSINJS_CACHE__ = cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cache = createCache();
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultStyleContext: StyleContextProps = {
|
||||||
|
cache: createCache(),
|
||||||
|
defaultCache: true,
|
||||||
|
hashPriority: 'low',
|
||||||
|
};
|
||||||
|
// fix: https://github.com/vueComponent/ant-design-vue/issues/6912
|
||||||
|
export const useStyleInject = () => {
|
||||||
|
const cache = getCache();
|
||||||
|
return inject(StyleContextKey, shallowRef({ ...defaultStyleContext, cache }));
|
||||||
|
};
|
||||||
|
export const useStyleProvider = (props: UseStyleProviderProps) => {
|
||||||
|
const parentContext = useStyleInject();
|
||||||
|
const context = shallowRef<Partial<StyleContextProps>>({
|
||||||
|
...defaultStyleContext,
|
||||||
|
cache: createCache(),
|
||||||
|
});
|
||||||
|
watch(
|
||||||
|
[() => unref(props), parentContext],
|
||||||
|
() => {
|
||||||
|
const mergedContext: Partial<StyleContextProps> = {
|
||||||
|
...parentContext.value,
|
||||||
|
};
|
||||||
|
const propsValue = unref(props);
|
||||||
|
Object.keys(propsValue).forEach(key => {
|
||||||
|
const value = propsValue[key];
|
||||||
|
if (propsValue[key] !== undefined) {
|
||||||
|
mergedContext[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { cache } = propsValue;
|
||||||
|
mergedContext.cache = mergedContext.cache || createCache();
|
||||||
|
mergedContext.defaultCache = !cache && parentContext.value.defaultCache;
|
||||||
|
context.value = mergedContext;
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
provide(StyleContextKey, context);
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
export const styleProviderProps = () => ({
|
||||||
|
autoClear: booleanType(),
|
||||||
|
/** @private Test only. Not work in production. */
|
||||||
|
mock: stringType<'server' | 'client'>(),
|
||||||
|
/**
|
||||||
|
* Only set when you need ssr to extract style on you own.
|
||||||
|
* If not provided, it will auto create <style /> on the end of Provider in server side.
|
||||||
|
*/
|
||||||
|
cache: objectType<CacheEntity>(),
|
||||||
|
/** Tell children that this context is default generated context */
|
||||||
|
defaultCache: booleanType(),
|
||||||
|
/** Use `:where` selector to reduce hashId css selector priority */
|
||||||
|
hashPriority: stringType<HashPriority>(),
|
||||||
|
/** Tell cssinjs where to inject style in */
|
||||||
|
container: someType<Element | ShadowRoot>(),
|
||||||
|
/** Component wil render inline `<style />` for fallback in SSR. Not recommend. */
|
||||||
|
ssrInline: booleanType(),
|
||||||
|
/** Transform css before inject in document. Please note that `transformers` do not support dynamic update */
|
||||||
|
transformers: arrayType<Transformer[]>(),
|
||||||
|
/**
|
||||||
|
* Linters to lint css before inject in document.
|
||||||
|
* Styles will be linted after transforming.
|
||||||
|
* Please note that `linters` do not support dynamic update.
|
||||||
|
*/
|
||||||
|
linters: arrayType<Linter[]>(),
|
||||||
|
});
|
||||||
|
export type StyleProviderProps = Partial<ExtractPropTypes<ReturnType<typeof styleProviderProps>>>;
|
||||||
|
export const StyleProvider = withInstall(
|
||||||
|
defineComponent({
|
||||||
|
name: 'AStyleProvider',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: styleProviderProps(),
|
||||||
|
setup(props, { slots }) {
|
||||||
|
useStyleProvider(props);
|
||||||
|
return () => slots.default?.();
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
useStyleInject,
|
||||||
|
useStyleProvider,
|
||||||
|
StyleProvider,
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
import hash from '@emotion/hash';
|
||||||
|
import { ATTR_TOKEN, CSS_IN_JS_INSTANCE, useStyleInject } from '../StyleContext';
|
||||||
|
import type Theme from '../theme/Theme';
|
||||||
|
import useGlobalCache from './useGlobalCache';
|
||||||
|
import { flattenToken, token2key } from '../util';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
|
const EMPTY_OVERRIDE = {};
|
||||||
|
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
// nuxt generate when NODE_ENV is prerender
|
||||||
|
const isPrerender = process.env.NODE_ENV === 'prerender';
|
||||||
|
|
||||||
|
// Generate different prefix to make user selector break in production env.
|
||||||
|
// This helps developer not to do style override directly on the hash id.
|
||||||
|
const hashPrefix = !isProduction && !isPrerender ? 'css-dev-only-do-not-override' : 'css';
|
||||||
|
|
||||||
|
export interface Option<DerivativeToken, DesignToken> {
|
||||||
|
/**
|
||||||
|
* Generate token with salt.
|
||||||
|
* This is used to generate different hashId even same derivative token for different version.
|
||||||
|
*/
|
||||||
|
salt?: string;
|
||||||
|
override?: object;
|
||||||
|
/**
|
||||||
|
* Format token as you need. Such as:
|
||||||
|
*
|
||||||
|
* - rename token
|
||||||
|
* - merge token
|
||||||
|
* - delete token
|
||||||
|
*
|
||||||
|
* This should always be the same since it's one time process.
|
||||||
|
* It's ok to useMemo outside but this has better cache strategy.
|
||||||
|
*/
|
||||||
|
formatToken?: (mergedToken: any) => DerivativeToken;
|
||||||
|
/**
|
||||||
|
* Get final token with origin token, override token and theme.
|
||||||
|
* The parameters do not contain formatToken since it's passed by user.
|
||||||
|
* @param origin The original token.
|
||||||
|
* @param override Extra tokens to override.
|
||||||
|
* @param theme Theme instance. Could get derivative token by `theme.getDerivativeToken`
|
||||||
|
*/
|
||||||
|
getComputedToken?: (
|
||||||
|
origin: DesignToken,
|
||||||
|
override: object,
|
||||||
|
theme: Theme<any, any>,
|
||||||
|
) => DerivativeToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenKeys = new Map<string, number>();
|
||||||
|
function recordCleanToken(tokenKey: string) {
|
||||||
|
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeStyleTags(key: string, instanceId: string) {
|
||||||
|
if (typeof document !== 'undefined') {
|
||||||
|
const styles = document.querySelectorAll(`style[${ATTR_TOKEN}="${key}"]`);
|
||||||
|
|
||||||
|
styles.forEach(style => {
|
||||||
|
if ((style as any)[CSS_IN_JS_INSTANCE] === instanceId) {
|
||||||
|
style.parentNode?.removeChild(style);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const TOKEN_THRESHOLD = 0;
|
||||||
|
|
||||||
|
// Remove will check current keys first
|
||||||
|
function cleanTokenStyle(tokenKey: string, instanceId: string) {
|
||||||
|
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) - 1);
|
||||||
|
|
||||||
|
const tokenKeyList = Array.from(tokenKeys.keys());
|
||||||
|
const cleanableKeyList = tokenKeyList.filter(key => {
|
||||||
|
const count = tokenKeys.get(key) || 0;
|
||||||
|
|
||||||
|
return count <= 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should keep tokens under threshold for not to insert style too often
|
||||||
|
if (tokenKeyList.length - cleanableKeyList.length > TOKEN_THRESHOLD) {
|
||||||
|
cleanableKeyList.forEach(key => {
|
||||||
|
removeStyleTags(key, instanceId);
|
||||||
|
tokenKeys.delete(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getComputedToken = <DerivativeToken = object, DesignToken = DerivativeToken>(
|
||||||
|
originToken: DesignToken,
|
||||||
|
overrideToken: object,
|
||||||
|
theme: Theme<any, any>,
|
||||||
|
format?: (token: DesignToken) => DerivativeToken,
|
||||||
|
) => {
|
||||||
|
const derivativeToken = theme.getDerivativeToken(originToken);
|
||||||
|
// Merge with override
|
||||||
|
let mergedDerivativeToken = {
|
||||||
|
...derivativeToken,
|
||||||
|
...overrideToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Format if needed
|
||||||
|
if (format) {
|
||||||
|
mergedDerivativeToken = format(mergedDerivativeToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergedDerivativeToken;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache theme derivative token as global shared one
|
||||||
|
* @param theme Theme entity
|
||||||
|
* @param tokens List of tokens, used for cache. Please do not dynamic generate object directly
|
||||||
|
* @param option Additional config
|
||||||
|
* @returns Call Theme.getDerivativeToken(tokenObject) to get token
|
||||||
|
*/
|
||||||
|
export default function useCacheToken<DerivativeToken = object, DesignToken = DerivativeToken>(
|
||||||
|
theme: Ref<Theme<any, any>>,
|
||||||
|
tokens: Ref<Partial<DesignToken>[]>,
|
||||||
|
option: Ref<Option<DerivativeToken, DesignToken>> = ref({}),
|
||||||
|
) {
|
||||||
|
const style = useStyleInject();
|
||||||
|
|
||||||
|
// Basic - We do basic cache here
|
||||||
|
const mergedToken = computed(() => Object.assign({}, ...tokens.value));
|
||||||
|
const tokenStr = computed(() => flattenToken(mergedToken.value));
|
||||||
|
const overrideTokenStr = computed(() => flattenToken(option.value.override || EMPTY_OVERRIDE));
|
||||||
|
|
||||||
|
const cachedToken = useGlobalCache<[DerivativeToken & { _tokenKey: string }, string]>(
|
||||||
|
'token',
|
||||||
|
computed(() => [
|
||||||
|
option.value.salt || '',
|
||||||
|
theme.value.id,
|
||||||
|
tokenStr.value,
|
||||||
|
overrideTokenStr.value,
|
||||||
|
]),
|
||||||
|
() => {
|
||||||
|
const {
|
||||||
|
salt = '',
|
||||||
|
override = EMPTY_OVERRIDE,
|
||||||
|
formatToken,
|
||||||
|
getComputedToken: compute,
|
||||||
|
} = option.value;
|
||||||
|
const mergedDerivativeToken = compute
|
||||||
|
? compute(mergedToken.value, override, theme.value)
|
||||||
|
: getComputedToken(mergedToken.value, override, theme.value, formatToken);
|
||||||
|
|
||||||
|
// Optimize for `useStyleRegister` performance
|
||||||
|
const tokenKey = token2key(mergedDerivativeToken, salt);
|
||||||
|
mergedDerivativeToken._tokenKey = tokenKey;
|
||||||
|
recordCleanToken(tokenKey);
|
||||||
|
|
||||||
|
const hashId = `${hashPrefix}-${hash(tokenKey)}`;
|
||||||
|
mergedDerivativeToken._hashId = hashId; // Not used
|
||||||
|
return [mergedDerivativeToken, hashId];
|
||||||
|
},
|
||||||
|
cache => {
|
||||||
|
// Remove token will remove all related style
|
||||||
|
cleanTokenStyle(cache[0]._tokenKey, style.value?.cache.instanceId);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return cachedToken;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { useStyleInject } from '../StyleContext';
|
||||||
|
import type { KeyType } from '../Cache';
|
||||||
|
import useHMR from './useHMR';
|
||||||
|
import type { ShallowRef, Ref } from 'vue';
|
||||||
|
import { onBeforeUnmount, watch, watchEffect, shallowRef } from 'vue';
|
||||||
|
export default function useClientCache<CacheType>(
|
||||||
|
prefix: string,
|
||||||
|
keyPath: Ref<KeyType[]>,
|
||||||
|
cacheFn: () => CacheType,
|
||||||
|
onCacheRemove?: (cache: CacheType, fromHMR: boolean) => void,
|
||||||
|
): ShallowRef<CacheType> {
|
||||||
|
const styleContext = useStyleInject();
|
||||||
|
const fullPathStr = shallowRef('');
|
||||||
|
const res = shallowRef<CacheType>();
|
||||||
|
watchEffect(() => {
|
||||||
|
fullPathStr.value = [prefix, ...keyPath.value].join('%');
|
||||||
|
});
|
||||||
|
const HMRUpdate = useHMR();
|
||||||
|
const clearCache = (pathStr: string) => {
|
||||||
|
styleContext.value.cache.update(pathStr, prevCache => {
|
||||||
|
const [times = 0, cache] = prevCache || [];
|
||||||
|
const nextCount = times - 1;
|
||||||
|
if (nextCount === 0) {
|
||||||
|
onCacheRemove?.(cache, false);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [times - 1, cache];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
fullPathStr,
|
||||||
|
(newStr, oldStr) => {
|
||||||
|
if (oldStr) clearCache(oldStr);
|
||||||
|
// Create cache
|
||||||
|
styleContext.value.cache.update(newStr, prevCache => {
|
||||||
|
const [times = 0, cache] = prevCache || [];
|
||||||
|
|
||||||
|
// HMR should always ignore cache since developer may change it
|
||||||
|
let tmpCache = cache;
|
||||||
|
if (process.env.NODE_ENV !== 'production' && cache && HMRUpdate) {
|
||||||
|
onCacheRemove?.(tmpCache, HMRUpdate);
|
||||||
|
tmpCache = null;
|
||||||
|
}
|
||||||
|
const mergedCache = tmpCache || cacheFn();
|
||||||
|
|
||||||
|
return [times + 1, mergedCache];
|
||||||
|
});
|
||||||
|
res.value = styleContext.value.cache.get(fullPathStr.value)![1];
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearCache(fullPathStr.value);
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
function useProdHMR() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let webpackHMR = false;
|
||||||
|
|
||||||
|
function useDevHMR() {
|
||||||
|
return webpackHMR;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default process.env.NODE_ENV === 'production' ? useProdHMR : useDevHMR;
|
||||||
|
|
||||||
|
// Webpack `module.hot.accept` do not support any deps update trigger
|
||||||
|
// We have to hack handler to force mark as HRM
|
||||||
|
if (
|
||||||
|
process.env.NODE_ENV !== 'production' &&
|
||||||
|
typeof module !== 'undefined' &&
|
||||||
|
module &&
|
||||||
|
(module as any).hot &&
|
||||||
|
typeof window !== 'undefined'
|
||||||
|
) {
|
||||||
|
const win = window as any;
|
||||||
|
if (typeof win.webpackHotUpdate === 'function') {
|
||||||
|
const originWebpackHotUpdate = win.webpackHotUpdate;
|
||||||
|
|
||||||
|
win.webpackHotUpdate = (...args: any[]) => {
|
||||||
|
webpackHMR = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
webpackHMR = false;
|
||||||
|
}, 0);
|
||||||
|
return originWebpackHotUpdate(...args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
import canUseDom from '../../../../_util/canUseDom';
|
||||||
|
import { ATTR_MARK } from '../../StyleContext';
|
||||||
|
|
||||||
|
export const ATTR_CACHE_MAP = 'data-ant-cssinjs-cache-path';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This marks style from the css file.
|
||||||
|
* Which means not exist in `<style />` tag.
|
||||||
|
*/
|
||||||
|
export const CSS_FILE_STYLE = '_FILE_STYLE__';
|
||||||
|
|
||||||
|
export function serialize(cachePathMap: Record<string, string>) {
|
||||||
|
return Object.keys(cachePathMap)
|
||||||
|
.map(path => {
|
||||||
|
const hash = cachePathMap[path];
|
||||||
|
return `${path}:${hash}`;
|
||||||
|
})
|
||||||
|
.join(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
let cachePathMap: Record<string, string>;
|
||||||
|
let fromCSSFile = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private Test usage only. Can save remove if no need.
|
||||||
|
*/
|
||||||
|
export function reset(mockCache?: Record<string, string>, fromFile = true) {
|
||||||
|
cachePathMap = mockCache!;
|
||||||
|
fromCSSFile = fromFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prepare() {
|
||||||
|
if (!cachePathMap) {
|
||||||
|
cachePathMap = {};
|
||||||
|
|
||||||
|
if (canUseDom()) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = ATTR_CACHE_MAP;
|
||||||
|
div.style.position = 'fixed';
|
||||||
|
div.style.visibility = 'hidden';
|
||||||
|
div.style.top = '-9999px';
|
||||||
|
document.body.appendChild(div);
|
||||||
|
|
||||||
|
let content = getComputedStyle(div).content || '';
|
||||||
|
content = content.replace(/^"/, '').replace(/"$/, '');
|
||||||
|
|
||||||
|
// Fill data
|
||||||
|
content.split(';').forEach(item => {
|
||||||
|
const [path, hash] = item.split(':');
|
||||||
|
cachePathMap[path] = hash;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove inline record style
|
||||||
|
const inlineMapStyle = document.querySelector(`style[${ATTR_CACHE_MAP}]`);
|
||||||
|
if (inlineMapStyle) {
|
||||||
|
fromCSSFile = false;
|
||||||
|
inlineMapStyle.parentNode?.removeChild(inlineMapStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.removeChild(div);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function existPath(path: string) {
|
||||||
|
prepare();
|
||||||
|
|
||||||
|
return !!cachePathMap[path];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStyleAndHash(path: string): [style: string | null, hash: string] {
|
||||||
|
const hash = cachePathMap[path];
|
||||||
|
let styleStr: string | null = null;
|
||||||
|
|
||||||
|
if (hash && canUseDom()) {
|
||||||
|
if (fromCSSFile) {
|
||||||
|
styleStr = CSS_FILE_STYLE;
|
||||||
|
} else {
|
||||||
|
const style = document.querySelector(`style[${ATTR_MARK}="${cachePathMap[path]}"]`);
|
||||||
|
|
||||||
|
if (style) {
|
||||||
|
styleStr = style.innerHTML;
|
||||||
|
} else {
|
||||||
|
// Clean up since not exist anymore
|
||||||
|
delete cachePathMap[path];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [styleStr, hash];
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,566 @@
|
||||||
|
import hash from '@emotion/hash';
|
||||||
|
import type * as CSS from 'csstype';
|
||||||
|
// @ts-ignore
|
||||||
|
import unitless from '@emotion/unitless';
|
||||||
|
import { compile, serialize, stringify } from 'stylis';
|
||||||
|
import type { Theme, Transformer } from '../..';
|
||||||
|
import type Cache from '../../Cache';
|
||||||
|
import type Keyframes from '../../Keyframes';
|
||||||
|
import type { Linter } from '../../linters';
|
||||||
|
import { contentQuotesLinter, hashedAnimationLinter } from '../../linters';
|
||||||
|
import type { HashPriority } from '../../StyleContext';
|
||||||
|
import {
|
||||||
|
useStyleInject,
|
||||||
|
ATTR_CACHE_PATH,
|
||||||
|
ATTR_MARK,
|
||||||
|
ATTR_TOKEN,
|
||||||
|
CSS_IN_JS_INSTANCE,
|
||||||
|
} from '../../StyleContext';
|
||||||
|
import { supportLayer } from '../../util';
|
||||||
|
import useGlobalCache from '../useGlobalCache';
|
||||||
|
import { removeCSS, updateCSS } from '../../../../vc-util/Dom/dynamicCSS';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import type { VueNode } from '../../../type';
|
||||||
|
import canUseDom from '../../../../_util/canUseDom';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ATTR_CACHE_MAP,
|
||||||
|
existPath,
|
||||||
|
getStyleAndHash,
|
||||||
|
serialize as serializeCacheMap,
|
||||||
|
} from './cacheMapUtil';
|
||||||
|
|
||||||
|
const isClientSide = canUseDom();
|
||||||
|
|
||||||
|
const SKIP_CHECK = '_skip_check_';
|
||||||
|
const MULTI_VALUE = '_multi_value_';
|
||||||
|
export type CSSProperties = Omit<CSS.PropertiesFallback<number | string>, 'animationName'> & {
|
||||||
|
animationName?: CSS.PropertiesFallback<number | string>['animationName'] | Keyframes;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CSSPropertiesWithMultiValues = {
|
||||||
|
[K in keyof CSSProperties]:
|
||||||
|
| CSSProperties[K]
|
||||||
|
| readonly Extract<CSSProperties[K], string>[]
|
||||||
|
| {
|
||||||
|
[SKIP_CHECK]?: boolean;
|
||||||
|
[MULTI_VALUE]?: boolean;
|
||||||
|
value: CSSProperties[K] | CSSProperties[K][];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CSSPseudos = { [K in CSS.Pseudos]?: CSSObject };
|
||||||
|
|
||||||
|
type ArrayCSSInterpolation = readonly CSSInterpolation[];
|
||||||
|
|
||||||
|
export type InterpolationPrimitive = null | undefined | boolean | number | string | CSSObject;
|
||||||
|
|
||||||
|
export type CSSInterpolation = InterpolationPrimitive | ArrayCSSInterpolation | Keyframes;
|
||||||
|
|
||||||
|
export type CSSOthersObject = Record<string, CSSInterpolation>;
|
||||||
|
|
||||||
|
export interface CSSObject extends CSSPropertiesWithMultiValues, CSSPseudos, CSSOthersObject {}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// == Parser ==
|
||||||
|
// ============================================================================
|
||||||
|
// Preprocessor style content to browser support one
|
||||||
|
export function normalizeStyle(styleStr: string): string {
|
||||||
|
const serialized = serialize(compile(styleStr), stringify);
|
||||||
|
return serialized.replace(/\{%%%\:[^;];}/g, ';');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCompoundCSSProperty(value: CSSObject[string]) {
|
||||||
|
return typeof value === 'object' && value && (SKIP_CHECK in value || MULTI_VALUE in value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注入 hash 值
|
||||||
|
function injectSelectorHash(key: string, hashId: string, hashPriority?: HashPriority) {
|
||||||
|
if (!hashId) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashClassName = `.${hashId}`;
|
||||||
|
const hashSelector = hashPriority === 'low' ? `:where(${hashClassName})` : hashClassName;
|
||||||
|
|
||||||
|
// 注入 hashId
|
||||||
|
const keys = key.split(',').map(k => {
|
||||||
|
const fullPath = k.trim().split(/\s+/);
|
||||||
|
|
||||||
|
// 如果 Selector 第一个是 HTML Element,那我们就插到它的后面。反之,就插到最前面。
|
||||||
|
let firstPath = fullPath[0] || '';
|
||||||
|
const htmlElement = firstPath.match(/^\w+/)?.[0] || '';
|
||||||
|
|
||||||
|
firstPath = `${htmlElement}${hashSelector}${firstPath.slice(htmlElement.length)}`;
|
||||||
|
|
||||||
|
return [firstPath, ...fullPath.slice(1)].join(' ');
|
||||||
|
});
|
||||||
|
return keys.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ParseConfig {
|
||||||
|
hashId?: string;
|
||||||
|
hashPriority?: HashPriority;
|
||||||
|
layer?: string;
|
||||||
|
path?: string;
|
||||||
|
transformers?: Transformer[];
|
||||||
|
linters?: Linter[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ParseInfo {
|
||||||
|
root?: boolean;
|
||||||
|
injectHash?: boolean;
|
||||||
|
parentSelectors: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global effect style will mount once and not removed
|
||||||
|
// The effect will not save in SSR cache (e.g. keyframes)
|
||||||
|
const globalEffectStyleKeys = new Set();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private Test only. Clear the global effect style keys.
|
||||||
|
*/
|
||||||
|
export const _cf =
|
||||||
|
process.env.NODE_ENV !== 'production' ? () => globalEffectStyleKeys.clear() : undefined;
|
||||||
|
|
||||||
|
// Parse CSSObject to style content
|
||||||
|
export const parseStyle = (
|
||||||
|
interpolation: CSSInterpolation,
|
||||||
|
config: ParseConfig = {},
|
||||||
|
{ root, injectHash, parentSelectors }: ParseInfo = {
|
||||||
|
root: true,
|
||||||
|
parentSelectors: [],
|
||||||
|
},
|
||||||
|
): [
|
||||||
|
parsedStr: string,
|
||||||
|
// Style content which should be unique on all of the style (e.g. Keyframes).
|
||||||
|
// Firefox will flick with same animation name when exist multiple same keyframes.
|
||||||
|
effectStyle: Record<string, string>,
|
||||||
|
] => {
|
||||||
|
const { hashId, layer, path, hashPriority, transformers = [], linters = [] } = config;
|
||||||
|
let styleStr = '';
|
||||||
|
let effectStyle: Record<string, string> = {};
|
||||||
|
|
||||||
|
function parseKeyframes(keyframes: Keyframes) {
|
||||||
|
const animationName = keyframes.getName(hashId);
|
||||||
|
if (!effectStyle[animationName]) {
|
||||||
|
const [parsedStr] = parseStyle(keyframes.style, config, {
|
||||||
|
root: false,
|
||||||
|
parentSelectors,
|
||||||
|
});
|
||||||
|
|
||||||
|
effectStyle[animationName] = `@keyframes ${keyframes.getName(hashId)}${parsedStr}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenList(list: ArrayCSSInterpolation, fullList: CSSObject[] = []) {
|
||||||
|
list.forEach(item => {
|
||||||
|
if (Array.isArray(item)) {
|
||||||
|
flattenList(item, fullList);
|
||||||
|
} else if (item) {
|
||||||
|
fullList.push(item as CSSObject);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return fullList;
|
||||||
|
}
|
||||||
|
|
||||||
|
const flattenStyleList = flattenList(
|
||||||
|
Array.isArray(interpolation) ? interpolation : [interpolation],
|
||||||
|
);
|
||||||
|
|
||||||
|
flattenStyleList.forEach(originStyle => {
|
||||||
|
// Only root level can use raw string
|
||||||
|
const style: CSSObject = typeof originStyle === 'string' && !root ? {} : originStyle;
|
||||||
|
|
||||||
|
if (typeof style === 'string') {
|
||||||
|
styleStr += `${style}\n`;
|
||||||
|
} else if ((style as any)._keyframe) {
|
||||||
|
// Keyframe
|
||||||
|
parseKeyframes(style as unknown as Keyframes);
|
||||||
|
} else {
|
||||||
|
const mergedStyle = transformers.reduce((prev, trans) => trans?.visit?.(prev) || prev, style);
|
||||||
|
|
||||||
|
// Normal CSSObject
|
||||||
|
Object.keys(mergedStyle).forEach(key => {
|
||||||
|
const value = mergedStyle[key];
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof value === 'object' &&
|
||||||
|
value &&
|
||||||
|
(key !== 'animationName' || !(value as Keyframes)._keyframe) &&
|
||||||
|
!isCompoundCSSProperty(value)
|
||||||
|
) {
|
||||||
|
let subInjectHash = false;
|
||||||
|
|
||||||
|
// 当成嵌套对象来处理
|
||||||
|
let mergedKey = key.trim();
|
||||||
|
// Whether treat child as root. In most case it is false.
|
||||||
|
let nextRoot = false;
|
||||||
|
|
||||||
|
// 拆分多个选择器
|
||||||
|
if ((root || injectHash) && hashId) {
|
||||||
|
if (mergedKey.startsWith('@')) {
|
||||||
|
// 略过媒体查询,交给子节点继续插入 hashId
|
||||||
|
subInjectHash = true;
|
||||||
|
} else {
|
||||||
|
// 注入 hashId
|
||||||
|
mergedKey = injectSelectorHash(key, hashId, hashPriority);
|
||||||
|
}
|
||||||
|
} else if (root && !hashId && (mergedKey === '&' || mergedKey === '')) {
|
||||||
|
// In case of `{ '&': { a: { color: 'red' } } }` or `{ '': { a: { color: 'red' } } }` without hashId,
|
||||||
|
// we will get `&{a:{color:red;}}` or `{a:{color:red;}}` string for stylis to compile.
|
||||||
|
// But it does not conform to stylis syntax,
|
||||||
|
// and finally we will get `{color:red;}` as css, which is wrong.
|
||||||
|
// So we need to remove key in root, and treat child `{ a: { color: 'red' } }` as root.
|
||||||
|
mergedKey = '';
|
||||||
|
nextRoot = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [parsedStr, childEffectStyle] = parseStyle(value as any, config, {
|
||||||
|
root: nextRoot,
|
||||||
|
injectHash: subInjectHash,
|
||||||
|
parentSelectors: [...parentSelectors, mergedKey],
|
||||||
|
});
|
||||||
|
|
||||||
|
effectStyle = {
|
||||||
|
...effectStyle,
|
||||||
|
...childEffectStyle,
|
||||||
|
};
|
||||||
|
|
||||||
|
styleStr += `${mergedKey}${parsedStr}`;
|
||||||
|
} else {
|
||||||
|
function appendStyle(cssKey: string, cssValue: any) {
|
||||||
|
if (
|
||||||
|
process.env.NODE_ENV !== 'production' &&
|
||||||
|
(typeof value !== 'object' || !(value as any)?.[SKIP_CHECK])
|
||||||
|
) {
|
||||||
|
[contentQuotesLinter, hashedAnimationLinter, ...linters].forEach(linter =>
|
||||||
|
linter(cssKey, cssValue, { path, hashId, parentSelectors }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是样式则直接插入
|
||||||
|
const styleName = cssKey.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
|
||||||
|
|
||||||
|
// Auto suffix with px
|
||||||
|
let formatValue = cssValue;
|
||||||
|
if (!unitless[cssKey] && typeof formatValue === 'number' && formatValue !== 0) {
|
||||||
|
formatValue = `${formatValue}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle animationName & Keyframe value
|
||||||
|
if (cssKey === 'animationName' && (cssValue as Keyframes)?._keyframe) {
|
||||||
|
parseKeyframes(cssValue as Keyframes);
|
||||||
|
formatValue = (cssValue as Keyframes).getName(hashId);
|
||||||
|
}
|
||||||
|
|
||||||
|
styleStr += `${styleName}:${formatValue};`;
|
||||||
|
}
|
||||||
|
const actualValue = (value as any)?.value ?? value;
|
||||||
|
if (
|
||||||
|
typeof value === 'object' &&
|
||||||
|
(value as any)?.[MULTI_VALUE] &&
|
||||||
|
Array.isArray(actualValue)
|
||||||
|
) {
|
||||||
|
actualValue.forEach(item => {
|
||||||
|
appendStyle(key, item);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
appendStyle(key, actualValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!root) {
|
||||||
|
styleStr = `{${styleStr}}`;
|
||||||
|
} else if (layer && supportLayer()) {
|
||||||
|
const layerCells = layer.split(',');
|
||||||
|
const layerName = layerCells[layerCells.length - 1].trim();
|
||||||
|
styleStr = `@layer ${layerName} {${styleStr}}`;
|
||||||
|
|
||||||
|
// Order of layer if needed
|
||||||
|
if (layerCells.length > 1) {
|
||||||
|
// zombieJ: stylis do not support layer order, so we need to handle it manually.
|
||||||
|
styleStr = `@layer ${layer}{%%%:%}${styleStr}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [styleStr, effectStyle];
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// == Register ==
|
||||||
|
// ============================================================================
|
||||||
|
function uniqueHash(path: (string | number)[], styleStr: string) {
|
||||||
|
return hash(`${path.join('%')}${styleStr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// function Empty() {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a style to the global style sheet.
|
||||||
|
*/
|
||||||
|
export default function useStyleRegister(
|
||||||
|
info: Ref<{
|
||||||
|
theme: Theme<any, any>;
|
||||||
|
token: any;
|
||||||
|
path: string[];
|
||||||
|
hashId?: string;
|
||||||
|
layer?: string;
|
||||||
|
nonce?: string | (() => string);
|
||||||
|
clientOnly?: boolean;
|
||||||
|
/**
|
||||||
|
* Tell cssinjs the insert order of style.
|
||||||
|
* It's useful when you need to insert style
|
||||||
|
* before other style to overwrite for the same selector priority.
|
||||||
|
*/
|
||||||
|
order?: number;
|
||||||
|
}>,
|
||||||
|
styleFn: () => CSSInterpolation,
|
||||||
|
) {
|
||||||
|
const styleContext = useStyleInject();
|
||||||
|
|
||||||
|
const tokenKey = computed(() => info.value.token._tokenKey as string);
|
||||||
|
|
||||||
|
const fullPath = computed(() => [tokenKey.value, ...info.value.path]);
|
||||||
|
|
||||||
|
// Check if need insert style
|
||||||
|
let isMergedClientSide = isClientSide;
|
||||||
|
if (process.env.NODE_ENV !== 'production' && styleContext.value.mock !== undefined) {
|
||||||
|
isMergedClientSide = styleContext.value.mock === 'client';
|
||||||
|
}
|
||||||
|
|
||||||
|
// const [cacheStyle[0], cacheStyle[1], cacheStyle[2]]
|
||||||
|
useGlobalCache<
|
||||||
|
[
|
||||||
|
styleStr: string,
|
||||||
|
tokenKey: string,
|
||||||
|
styleId: string,
|
||||||
|
effectStyle: Record<string, string>,
|
||||||
|
clientOnly: boolean | undefined,
|
||||||
|
order: number,
|
||||||
|
]
|
||||||
|
>(
|
||||||
|
'style',
|
||||||
|
fullPath,
|
||||||
|
// Create cache if needed
|
||||||
|
() => {
|
||||||
|
const { path, hashId, layer, nonce, clientOnly, order = 0 } = info.value;
|
||||||
|
const cachePath = fullPath.value.join('|');
|
||||||
|
// Get style from SSR inline style directly
|
||||||
|
if (existPath(cachePath)) {
|
||||||
|
const [inlineCacheStyleStr, styleHash] = getStyleAndHash(cachePath);
|
||||||
|
if (inlineCacheStyleStr) {
|
||||||
|
return [inlineCacheStyleStr, tokenKey.value, styleHash, {}, clientOnly, order];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const styleObj = styleFn();
|
||||||
|
const { hashPriority, container, transformers, linters, cache } = styleContext.value;
|
||||||
|
|
||||||
|
const [parsedStyle, effectStyle] = parseStyle(styleObj, {
|
||||||
|
hashId,
|
||||||
|
hashPriority,
|
||||||
|
layer,
|
||||||
|
path: path.join('-'),
|
||||||
|
transformers,
|
||||||
|
linters,
|
||||||
|
});
|
||||||
|
const styleStr = normalizeStyle(parsedStyle);
|
||||||
|
const styleId = uniqueHash(fullPath.value, styleStr);
|
||||||
|
|
||||||
|
if (isMergedClientSide) {
|
||||||
|
const mergedCSSConfig: Parameters<typeof updateCSS>[2] = {
|
||||||
|
mark: ATTR_MARK,
|
||||||
|
prepend: 'queue',
|
||||||
|
attachTo: container,
|
||||||
|
priority: order,
|
||||||
|
};
|
||||||
|
|
||||||
|
const nonceStr = typeof nonce === 'function' ? nonce() : nonce;
|
||||||
|
|
||||||
|
if (nonceStr) {
|
||||||
|
mergedCSSConfig.csp = { nonce: nonceStr };
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = updateCSS(styleStr, styleId, mergedCSSConfig);
|
||||||
|
|
||||||
|
(style as any)[CSS_IN_JS_INSTANCE] = cache.instanceId;
|
||||||
|
|
||||||
|
// Used for `useCacheToken` to remove on batch when token removed
|
||||||
|
style.setAttribute(ATTR_TOKEN, tokenKey.value);
|
||||||
|
|
||||||
|
// Dev usage to find which cache path made this easily
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
style.setAttribute(ATTR_CACHE_PATH, fullPath.value.join('|'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject client side effect style
|
||||||
|
Object.keys(effectStyle).forEach(effectKey => {
|
||||||
|
if (!globalEffectStyleKeys.has(effectKey)) {
|
||||||
|
globalEffectStyleKeys.add(effectKey);
|
||||||
|
|
||||||
|
// Inject
|
||||||
|
updateCSS(normalizeStyle(effectStyle[effectKey]), `_effect-${effectKey}`, {
|
||||||
|
mark: ATTR_MARK,
|
||||||
|
prepend: 'queue',
|
||||||
|
attachTo: container,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return [styleStr, tokenKey.value, styleId, effectStyle, clientOnly, order];
|
||||||
|
},
|
||||||
|
// Remove cache if no need
|
||||||
|
([, , styleId], fromHMR) => {
|
||||||
|
if ((fromHMR || styleContext.value.autoClear) && isClientSide) {
|
||||||
|
removeCSS(styleId, { mark: ATTR_MARK });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return (node: VueNode) => {
|
||||||
|
return node;
|
||||||
|
// let styleNode: VueNode;
|
||||||
|
// if (!styleContext.ssrInline || isMergedClientSide || !styleContext.defaultCache) {
|
||||||
|
// styleNode = <Empty />;
|
||||||
|
// } else {
|
||||||
|
// styleNode = (
|
||||||
|
// <style
|
||||||
|
// {...{
|
||||||
|
// [ATTR_TOKEN]: cacheStyle.value[1],
|
||||||
|
// [ATTR_MARK]: cacheStyle.value[2],
|
||||||
|
// }}
|
||||||
|
// innerHTML={cacheStyle.value[0]}
|
||||||
|
// />
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// {styleNode}
|
||||||
|
// {node}
|
||||||
|
// </>
|
||||||
|
// );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// == SSR ==
|
||||||
|
// ============================================================================
|
||||||
|
export function extractStyle(cache: Cache, plain = false) {
|
||||||
|
const matchPrefix = `style%`;
|
||||||
|
|
||||||
|
// prefix with `style` is used for `useStyleRegister` to cache style context
|
||||||
|
const styleKeys = Array.from(cache.cache.keys()).filter(key => key.startsWith(matchPrefix));
|
||||||
|
|
||||||
|
// Common effect styles like animation
|
||||||
|
const effectStyles: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
// Mapping of cachePath to style hash
|
||||||
|
const cachePathMap: Record<string, string> = {};
|
||||||
|
|
||||||
|
let styleText = '';
|
||||||
|
|
||||||
|
function toStyleStr(
|
||||||
|
style: string,
|
||||||
|
tokenKey?: string,
|
||||||
|
styleId?: string,
|
||||||
|
customizeAttrs: Record<string, string> = {},
|
||||||
|
) {
|
||||||
|
const attrs: Record<string, string | undefined> = {
|
||||||
|
...customizeAttrs,
|
||||||
|
[ATTR_TOKEN]: tokenKey,
|
||||||
|
[ATTR_MARK]: styleId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const attrStr = Object.keys(attrs)
|
||||||
|
.map(attr => {
|
||||||
|
const val = attrs[attr];
|
||||||
|
return val ? `${attr}="${val}"` : null;
|
||||||
|
})
|
||||||
|
.filter(v => v)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
return plain ? style : `<style ${attrStr}>${style}</style>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================== Fill Style ======================
|
||||||
|
type OrderStyle = [order: number, style: string];
|
||||||
|
|
||||||
|
const orderStyles: OrderStyle[] = styleKeys
|
||||||
|
.map(key => {
|
||||||
|
const cachePath = key.slice(matchPrefix.length).replace(/%/g, '|');
|
||||||
|
|
||||||
|
const [styleStr, tokenKey, styleId, effectStyle, clientOnly, order]: [
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
Record<string, string>,
|
||||||
|
boolean,
|
||||||
|
number,
|
||||||
|
] = cache.cache.get(key)![1];
|
||||||
|
|
||||||
|
// Skip client only style
|
||||||
|
if (clientOnly) {
|
||||||
|
return null! as OrderStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================== Style ======================
|
||||||
|
// Used for vc-util
|
||||||
|
const sharedAttrs = {
|
||||||
|
'data-vc-order': 'prependQueue',
|
||||||
|
'data-vc-priority': `${order}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
let keyStyleText = toStyleStr(styleStr, tokenKey, styleId, sharedAttrs);
|
||||||
|
|
||||||
|
// Save cache path with hash mapping
|
||||||
|
cachePathMap[cachePath] = styleId;
|
||||||
|
|
||||||
|
// =============== Create effect style ===============
|
||||||
|
if (effectStyle) {
|
||||||
|
Object.keys(effectStyle).forEach(effectKey => {
|
||||||
|
// Effect style can be reused
|
||||||
|
if (!effectStyles[effectKey]) {
|
||||||
|
effectStyles[effectKey] = true;
|
||||||
|
keyStyleText += toStyleStr(
|
||||||
|
normalizeStyle(effectStyle[effectKey]),
|
||||||
|
tokenKey,
|
||||||
|
`_effect-${effectKey}`,
|
||||||
|
sharedAttrs,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const ret: OrderStyle = [order, keyStyleText];
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
})
|
||||||
|
.filter(o => o);
|
||||||
|
|
||||||
|
orderStyles
|
||||||
|
.sort((o1, o2) => o1[0] - o2[0])
|
||||||
|
.forEach(([, style]) => {
|
||||||
|
styleText += style;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== Fill Cache Path ====================
|
||||||
|
styleText += toStyleStr(
|
||||||
|
`.${ATTR_CACHE_MAP}{content:"${serializeCacheMap(cachePathMap)}";}`,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
[ATTR_CACHE_MAP]: ATTR_CACHE_MAP,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return styleText;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
import useCacheToken from './hooks/useCacheToken';
|
||||||
|
import type { CSSInterpolation, CSSObject } from './hooks/useStyleRegister';
|
||||||
|
import useStyleRegister, { extractStyle } from './hooks/useStyleRegister';
|
||||||
|
import Keyframes from './Keyframes';
|
||||||
|
import type { Linter } from './linters';
|
||||||
|
import { legacyNotSelectorLinter, logicalPropertiesLinter, parentSelectorLinter } from './linters';
|
||||||
|
import type { StyleContextProps, StyleProviderProps } from './StyleContext';
|
||||||
|
import { createCache, useStyleInject, useStyleProvider, StyleProvider } from './StyleContext';
|
||||||
|
import type { DerivativeFunc, TokenType } from './theme';
|
||||||
|
import { createTheme, Theme } from './theme';
|
||||||
|
import type { Transformer } from './transformers/interface';
|
||||||
|
import legacyLogicalPropertiesTransformer from './transformers/legacyLogicalProperties';
|
||||||
|
import px2remTransformer from './transformers/px2rem';
|
||||||
|
import { supportLogicProps, supportWhere } from './util';
|
||||||
|
|
||||||
|
const cssinjs = {
|
||||||
|
Theme,
|
||||||
|
createTheme,
|
||||||
|
useStyleRegister,
|
||||||
|
useCacheToken,
|
||||||
|
createCache,
|
||||||
|
useStyleInject,
|
||||||
|
useStyleProvider,
|
||||||
|
Keyframes,
|
||||||
|
extractStyle,
|
||||||
|
|
||||||
|
// Transformer
|
||||||
|
legacyLogicalPropertiesTransformer,
|
||||||
|
px2remTransformer,
|
||||||
|
|
||||||
|
// Linters
|
||||||
|
logicalPropertiesLinter,
|
||||||
|
legacyNotSelectorLinter,
|
||||||
|
parentSelectorLinter,
|
||||||
|
|
||||||
|
// cssinjs
|
||||||
|
StyleProvider,
|
||||||
|
};
|
||||||
|
export {
|
||||||
|
Theme,
|
||||||
|
createTheme,
|
||||||
|
useStyleRegister,
|
||||||
|
useCacheToken,
|
||||||
|
createCache,
|
||||||
|
useStyleInject,
|
||||||
|
useStyleProvider,
|
||||||
|
Keyframes,
|
||||||
|
extractStyle,
|
||||||
|
|
||||||
|
// Transformer
|
||||||
|
legacyLogicalPropertiesTransformer,
|
||||||
|
px2remTransformer,
|
||||||
|
|
||||||
|
// Linters
|
||||||
|
logicalPropertiesLinter,
|
||||||
|
legacyNotSelectorLinter,
|
||||||
|
parentSelectorLinter,
|
||||||
|
|
||||||
|
// cssinjs
|
||||||
|
StyleProvider,
|
||||||
|
};
|
||||||
|
export type {
|
||||||
|
TokenType,
|
||||||
|
CSSObject,
|
||||||
|
CSSInterpolation,
|
||||||
|
DerivativeFunc,
|
||||||
|
Transformer,
|
||||||
|
Linter,
|
||||||
|
StyleContextProps,
|
||||||
|
StyleProviderProps,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const _experimental = {
|
||||||
|
supportModernCSS: () => supportWhere() && supportLogicProps(),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default cssinjs;
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import type { Linter } from './interface';
|
||||||
|
import { lintWarning } from './utils';
|
||||||
|
|
||||||
|
const linter: Linter = (key, value, info) => {
|
||||||
|
if (key === 'content') {
|
||||||
|
// From emotion: https://github.com/emotion-js/emotion/blob/main/packages/serialize/src/index.js#L63
|
||||||
|
const contentValuePattern =
|
||||||
|
/(attr|counters?|url|(((repeating-)?(linear|radial))|conic)-gradient)\(|(no-)?(open|close)-quote/;
|
||||||
|
const contentValues = ['normal', 'none', 'initial', 'inherit', 'unset'];
|
||||||
|
if (
|
||||||
|
typeof value !== 'string' ||
|
||||||
|
(contentValues.indexOf(value) === -1 &&
|
||||||
|
!contentValuePattern.test(value) &&
|
||||||
|
(value.charAt(0) !== value.charAt(value.length - 1) ||
|
||||||
|
(value.charAt(0) !== '"' && value.charAt(0) !== "'")))
|
||||||
|
) {
|
||||||
|
lintWarning(
|
||||||
|
`You seem to be using a value for 'content' without quotes, try replacing it with \`content: '"${value}"'\`.`,
|
||||||
|
info,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default linter;
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import type { Linter } from './interface';
|
||||||
|
import { lintWarning } from './utils';
|
||||||
|
|
||||||
|
const linter: Linter = (key, value, info) => {
|
||||||
|
if (key === 'animation') {
|
||||||
|
if (info.hashId && value !== 'none') {
|
||||||
|
lintWarning(
|
||||||
|
`You seem to be using hashed animation '${value}', in which case 'animationName' with Keyframe as value is recommended.`,
|
||||||
|
info,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default linter;
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
export { default as contentQuotesLinter } from './contentQuotesLinter';
|
||||||
|
export { default as hashedAnimationLinter } from './hashedAnimationLinter';
|
||||||
|
export type { Linter } from './interface';
|
||||||
|
export { default as legacyNotSelectorLinter } from './legacyNotSelectorLinter';
|
||||||
|
export { default as logicalPropertiesLinter } from './logicalPropertiesLinter';
|
||||||
|
export { default as parentSelectorLinter } from './parentSelectorLinter';
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
export interface LinterInfo {
|
||||||
|
path?: string;
|
||||||
|
hashId?: string;
|
||||||
|
parentSelectors: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Linter {
|
||||||
|
(key: string, value: string | number, info: LinterInfo): void;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import type { Linter, LinterInfo } from './interface';
|
||||||
|
import { lintWarning } from './utils';
|
||||||
|
|
||||||
|
function isConcatSelector(selector: string) {
|
||||||
|
const notContent = selector.match(/:not\(([^)]*)\)/)?.[1] || '';
|
||||||
|
|
||||||
|
// split selector. e.g.
|
||||||
|
// `h1#a.b` => ['h1', #a', '.b']
|
||||||
|
const splitCells = notContent.split(/(\[[^[]*])|(?=[.#])/).filter(str => str);
|
||||||
|
|
||||||
|
return splitCells.length > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePath(info: LinterInfo) {
|
||||||
|
return info.parentSelectors.reduce((prev, cur) => {
|
||||||
|
if (!prev) {
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur.includes('&') ? cur.replace(/&/g, prev) : `${prev} ${cur}`;
|
||||||
|
}, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
const linter: Linter = (_key, _value, info) => {
|
||||||
|
const parentSelectorPath = parsePath(info);
|
||||||
|
const notList = parentSelectorPath.match(/:not\([^)]*\)/g) || [];
|
||||||
|
|
||||||
|
if (notList.length > 0 && notList.some(isConcatSelector)) {
|
||||||
|
lintWarning(`Concat ':not' selector not support in legacy browsers.`, info);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default linter;
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
import type { Linter } from './interface';
|
||||||
|
import { lintWarning } from './utils';
|
||||||
|
|
||||||
|
const linter: Linter = (key, value, info) => {
|
||||||
|
switch (key) {
|
||||||
|
case 'marginLeft':
|
||||||
|
case 'marginRight':
|
||||||
|
case 'paddingLeft':
|
||||||
|
case 'paddingRight':
|
||||||
|
case 'left':
|
||||||
|
case 'right':
|
||||||
|
case 'borderLeft':
|
||||||
|
case 'borderLeftWidth':
|
||||||
|
case 'borderLeftStyle':
|
||||||
|
case 'borderLeftColor':
|
||||||
|
case 'borderRight':
|
||||||
|
case 'borderRightWidth':
|
||||||
|
case 'borderRightStyle':
|
||||||
|
case 'borderRightColor':
|
||||||
|
case 'borderTopLeftRadius':
|
||||||
|
case 'borderTopRightRadius':
|
||||||
|
case 'borderBottomLeftRadius':
|
||||||
|
case 'borderBottomRightRadius':
|
||||||
|
lintWarning(
|
||||||
|
`You seem to be using non-logical property '${key}' which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`,
|
||||||
|
info,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
case 'margin':
|
||||||
|
case 'padding':
|
||||||
|
case 'borderWidth':
|
||||||
|
case 'borderStyle':
|
||||||
|
// case 'borderColor':
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const valueArr = value.split(' ').map(item => item.trim());
|
||||||
|
if (valueArr.length === 4 && valueArr[1] !== valueArr[3]) {
|
||||||
|
lintWarning(
|
||||||
|
`You seem to be using '${key}' property with different left ${key} and right ${key}, which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`,
|
||||||
|
info,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 'clear':
|
||||||
|
case 'textAlign':
|
||||||
|
if (value === 'left' || value === 'right') {
|
||||||
|
lintWarning(
|
||||||
|
`You seem to be using non-logical value '${value}' of ${key}, which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`,
|
||||||
|
info,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 'borderRadius':
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const radiusGroups = value.split('/').map(item => item.trim());
|
||||||
|
const invalid = radiusGroups.reduce((result, group) => {
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const radiusArr = group.split(' ').map(item => item.trim());
|
||||||
|
// borderRadius: '2px 4px'
|
||||||
|
if (radiusArr.length >= 2 && radiusArr[0] !== radiusArr[1]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// borderRadius: '4px 4px 2px'
|
||||||
|
if (radiusArr.length === 3 && radiusArr[1] !== radiusArr[2]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// borderRadius: '4px 4px 2px 4px'
|
||||||
|
if (radiusArr.length === 4 && radiusArr[2] !== radiusArr[3]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
if (invalid) {
|
||||||
|
lintWarning(
|
||||||
|
`You seem to be using non-logical value '${value}' of ${key}, which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`,
|
||||||
|
info,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default linter;
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import type { Linter } from '..';
|
||||||
|
import { lintWarning } from './utils';
|
||||||
|
|
||||||
|
const linter: Linter = (_key, _value, info) => {
|
||||||
|
if (
|
||||||
|
info.parentSelectors.some(selector => {
|
||||||
|
const selectors = selector.split(',');
|
||||||
|
return selectors.some(item => item.split('&').length > 2);
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
lintWarning('Should not use more than one `&` in a selector.', info);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default linter;
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import devWarning from '../../../vc-util/warning';
|
||||||
|
import type { LinterInfo } from './interface';
|
||||||
|
|
||||||
|
export function lintWarning(message: string, info: LinterInfo) {
|
||||||
|
const { path, parentSelectors } = info;
|
||||||
|
|
||||||
|
devWarning(
|
||||||
|
false,
|
||||||
|
`[Ant Design Vue CSS-in-JS] ${path ? `Error in '${path}': ` : ''}${message}${
|
||||||
|
parentSelectors.length ? ` Selector info: ${parentSelectors.join(' -> ')}` : ''
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import warning from '../../warning';
|
||||||
|
import type { DerivativeFunc, TokenType } from './interface';
|
||||||
|
|
||||||
|
let uuid = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Theme with algorithms to derive tokens from design tokens.
|
||||||
|
* Use `createTheme` first which will help to manage the theme instance cache.
|
||||||
|
*/
|
||||||
|
export default class Theme<DesignToken extends TokenType, DerivativeToken extends TokenType> {
|
||||||
|
private derivatives: DerivativeFunc<DesignToken, DerivativeToken>[];
|
||||||
|
public readonly id: number;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
derivatives:
|
||||||
|
| DerivativeFunc<DesignToken, DerivativeToken>
|
||||||
|
| DerivativeFunc<DesignToken, DerivativeToken>[],
|
||||||
|
) {
|
||||||
|
this.derivatives = Array.isArray(derivatives) ? derivatives : [derivatives];
|
||||||
|
this.id = uuid;
|
||||||
|
|
||||||
|
if (derivatives.length === 0) {
|
||||||
|
warning(
|
||||||
|
derivatives.length > 0,
|
||||||
|
'[Ant Design Vue CSS-in-JS] Theme should have at least one derivative function.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDerivativeToken(token: DesignToken): DerivativeToken {
|
||||||
|
return this.derivatives.reduce<DerivativeToken>(
|
||||||
|
(result, derivative) => derivative(token, result),
|
||||||
|
undefined as any,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
import type Theme from './Theme';
|
||||||
|
import type { DerivativeFunc } from './interface';
|
||||||
|
|
||||||
|
// ================================== Cache ==================================
|
||||||
|
type ThemeCacheMap = Map<
|
||||||
|
DerivativeFunc<any, any>,
|
||||||
|
{
|
||||||
|
map?: ThemeCacheMap;
|
||||||
|
value?: [Theme<any, any>, number];
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
type DerivativeOptions = DerivativeFunc<any, any>[];
|
||||||
|
|
||||||
|
export function sameDerivativeOption(left: DerivativeOptions, right: DerivativeOptions) {
|
||||||
|
if (left.length !== right.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < left.length; i++) {
|
||||||
|
if (left[i] !== right[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ThemeCache {
|
||||||
|
public static MAX_CACHE_SIZE = 20;
|
||||||
|
public static MAX_CACHE_OFFSET = 5;
|
||||||
|
|
||||||
|
private readonly cache: ThemeCacheMap;
|
||||||
|
private keys: DerivativeOptions[];
|
||||||
|
private cacheCallTimes: number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.cache = new Map();
|
||||||
|
this.keys = [];
|
||||||
|
this.cacheCallTimes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public size(): number {
|
||||||
|
return this.keys.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private internalGet(
|
||||||
|
derivativeOption: DerivativeOptions,
|
||||||
|
updateCallTimes = false,
|
||||||
|
): [Theme<any, any>, number] | undefined {
|
||||||
|
let cache: ReturnType<ThemeCacheMap['get']> = { map: this.cache };
|
||||||
|
derivativeOption.forEach(derivative => {
|
||||||
|
if (!cache) {
|
||||||
|
cache = undefined;
|
||||||
|
} else {
|
||||||
|
cache = cache?.map?.get(derivative);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (cache?.value && updateCallTimes) {
|
||||||
|
cache.value[1] = this.cacheCallTimes++;
|
||||||
|
}
|
||||||
|
return cache?.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(derivativeOption: DerivativeOptions): Theme<any, any> | undefined {
|
||||||
|
return this.internalGet(derivativeOption, true)?.[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public has(derivativeOption: DerivativeOptions): boolean {
|
||||||
|
return !!this.internalGet(derivativeOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set(derivativeOption: DerivativeOptions, value: Theme<any, any>): void {
|
||||||
|
// New cache
|
||||||
|
if (!this.has(derivativeOption)) {
|
||||||
|
if (this.size() + 1 > ThemeCache.MAX_CACHE_SIZE + ThemeCache.MAX_CACHE_OFFSET) {
|
||||||
|
const [targetKey] = this.keys.reduce<[DerivativeOptions, number]>(
|
||||||
|
(result, key) => {
|
||||||
|
const [, callTimes] = result;
|
||||||
|
if (this.internalGet(key)![1] < callTimes) {
|
||||||
|
return [key, this.internalGet(key)![1]];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
[this.keys[0], this.cacheCallTimes],
|
||||||
|
);
|
||||||
|
this.delete(targetKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.keys.push(derivativeOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cache = this.cache;
|
||||||
|
derivativeOption.forEach((derivative, index) => {
|
||||||
|
if (index === derivativeOption.length - 1) {
|
||||||
|
cache.set(derivative, { value: [value, this.cacheCallTimes++] });
|
||||||
|
} else {
|
||||||
|
const cacheValue = cache.get(derivative);
|
||||||
|
if (!cacheValue) {
|
||||||
|
cache.set(derivative, { map: new Map() });
|
||||||
|
} else if (!cacheValue.map) {
|
||||||
|
cacheValue.map = new Map();
|
||||||
|
}
|
||||||
|
cache = cache.get(derivative)!.map!;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private deleteByPath(
|
||||||
|
currentCache: ThemeCacheMap,
|
||||||
|
derivatives: DerivativeFunc<any, any>[],
|
||||||
|
): Theme<any, any> | undefined {
|
||||||
|
const cache = currentCache.get(derivatives[0])!;
|
||||||
|
if (derivatives.length === 1) {
|
||||||
|
if (!cache.map) {
|
||||||
|
currentCache.delete(derivatives[0]);
|
||||||
|
} else {
|
||||||
|
currentCache.set(derivatives[0], { map: cache.map });
|
||||||
|
}
|
||||||
|
return cache.value?.[0];
|
||||||
|
}
|
||||||
|
const result = this.deleteByPath(cache.map!, derivatives.slice(1));
|
||||||
|
if ((!cache.map || cache.map.size === 0) && !cache.value) {
|
||||||
|
currentCache.delete(derivatives[0]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(derivativeOption: DerivativeOptions): Theme<any, any> | undefined {
|
||||||
|
// If cache exists
|
||||||
|
if (this.has(derivativeOption)) {
|
||||||
|
this.keys = this.keys.filter(item => !sameDerivativeOption(item, derivativeOption));
|
||||||
|
return this.deleteByPath(this.cache, derivativeOption);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import ThemeCache from './ThemeCache';
|
||||||
|
import Theme from './Theme';
|
||||||
|
import type { DerivativeFunc, TokenType } from './interface';
|
||||||
|
|
||||||
|
const cacheThemes = new ThemeCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as new Theme, but will always return same one if `derivative` not changed.
|
||||||
|
*/
|
||||||
|
export default function createTheme<
|
||||||
|
DesignToken extends TokenType,
|
||||||
|
DerivativeToken extends TokenType,
|
||||||
|
>(
|
||||||
|
derivatives:
|
||||||
|
| DerivativeFunc<DesignToken, DerivativeToken>[]
|
||||||
|
| DerivativeFunc<DesignToken, DerivativeToken>,
|
||||||
|
) {
|
||||||
|
const derivativeArr = Array.isArray(derivatives) ? derivatives : [derivatives];
|
||||||
|
// Create new theme if not exist
|
||||||
|
if (!cacheThemes.has(derivativeArr)) {
|
||||||
|
cacheThemes.set(derivativeArr, new Theme(derivativeArr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get theme from cache and return
|
||||||
|
return cacheThemes.get(derivativeArr)!;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export { default as createTheme } from './createTheme';
|
||||||
|
export { default as Theme } from './Theme';
|
||||||
|
export { default as ThemeCache } from './ThemeCache';
|
||||||
|
export type { TokenType, DerivativeFunc } from './interface';
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
export type TokenType = object;
|
||||||
|
export type DerivativeFunc<DesignToken extends TokenType, DerivativeToken extends TokenType> = (
|
||||||
|
designToken: DesignToken,
|
||||||
|
derivativeToken?: DerivativeToken,
|
||||||
|
) => DerivativeToken;
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import type { CSSObject } from '..';
|
||||||
|
|
||||||
|
export interface Transformer {
|
||||||
|
visit?: (cssObj: CSSObject) => CSSObject;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
import type { CSSObject } from '..';
|
||||||
|
import type { Transformer } from './interface';
|
||||||
|
|
||||||
|
function splitValues(value: string | number) {
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
return [value];
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitStyle = String(value).split(/\s+/);
|
||||||
|
|
||||||
|
// Combine styles split in brackets, like `calc(1px + 2px)`
|
||||||
|
let temp = '';
|
||||||
|
let brackets = 0;
|
||||||
|
return splitStyle.reduce<string[]>((list, item) => {
|
||||||
|
if (item.includes('(')) {
|
||||||
|
temp += item;
|
||||||
|
brackets += item.split('(').length - 1;
|
||||||
|
} else if (item.includes(')')) {
|
||||||
|
temp += ` ${item}`;
|
||||||
|
brackets -= item.split(')').length - 1;
|
||||||
|
if (brackets === 0) {
|
||||||
|
list.push(temp);
|
||||||
|
temp = '';
|
||||||
|
}
|
||||||
|
} else if (brackets > 0) {
|
||||||
|
temp += ` ${item}`;
|
||||||
|
} else {
|
||||||
|
list.push(item);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
type MatchValue = string[] & {
|
||||||
|
notSplit?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function noSplit(list: MatchValue): MatchValue {
|
||||||
|
list.notSplit = true;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyMap: Record<string, MatchValue> = {
|
||||||
|
// Inset
|
||||||
|
inset: ['top', 'right', 'bottom', 'left'],
|
||||||
|
insetBlock: ['top', 'bottom'],
|
||||||
|
insetBlockStart: ['top'],
|
||||||
|
insetBlockEnd: ['bottom'],
|
||||||
|
insetInline: ['left', 'right'],
|
||||||
|
insetInlineStart: ['left'],
|
||||||
|
insetInlineEnd: ['right'],
|
||||||
|
|
||||||
|
// Margin
|
||||||
|
marginBlock: ['marginTop', 'marginBottom'],
|
||||||
|
marginBlockStart: ['marginTop'],
|
||||||
|
marginBlockEnd: ['marginBottom'],
|
||||||
|
marginInline: ['marginLeft', 'marginRight'],
|
||||||
|
marginInlineStart: ['marginLeft'],
|
||||||
|
marginInlineEnd: ['marginRight'],
|
||||||
|
|
||||||
|
// Padding
|
||||||
|
paddingBlock: ['paddingTop', 'paddingBottom'],
|
||||||
|
paddingBlockStart: ['paddingTop'],
|
||||||
|
paddingBlockEnd: ['paddingBottom'],
|
||||||
|
paddingInline: ['paddingLeft', 'paddingRight'],
|
||||||
|
paddingInlineStart: ['paddingLeft'],
|
||||||
|
paddingInlineEnd: ['paddingRight'],
|
||||||
|
|
||||||
|
// Border
|
||||||
|
borderBlock: noSplit(['borderTop', 'borderBottom']),
|
||||||
|
borderBlockStart: noSplit(['borderTop']),
|
||||||
|
borderBlockEnd: noSplit(['borderBottom']),
|
||||||
|
borderInline: noSplit(['borderLeft', 'borderRight']),
|
||||||
|
borderInlineStart: noSplit(['borderLeft']),
|
||||||
|
borderInlineEnd: noSplit(['borderRight']),
|
||||||
|
|
||||||
|
// Border width
|
||||||
|
borderBlockWidth: ['borderTopWidth', 'borderBottomWidth'],
|
||||||
|
borderBlockStartWidth: ['borderTopWidth'],
|
||||||
|
borderBlockEndWidth: ['borderBottomWidth'],
|
||||||
|
borderInlineWidth: ['borderLeftWidth', 'borderRightWidth'],
|
||||||
|
borderInlineStartWidth: ['borderLeftWidth'],
|
||||||
|
borderInlineEndWidth: ['borderRightWidth'],
|
||||||
|
|
||||||
|
// Border style
|
||||||
|
borderBlockStyle: ['borderTopStyle', 'borderBottomStyle'],
|
||||||
|
borderBlockStartStyle: ['borderTopStyle'],
|
||||||
|
borderBlockEndStyle: ['borderBottomStyle'],
|
||||||
|
borderInlineStyle: ['borderLeftStyle', 'borderRightStyle'],
|
||||||
|
borderInlineStartStyle: ['borderLeftStyle'],
|
||||||
|
borderInlineEndStyle: ['borderRightStyle'],
|
||||||
|
|
||||||
|
// Border color
|
||||||
|
borderBlockColor: ['borderTopColor', 'borderBottomColor'],
|
||||||
|
borderBlockStartColor: ['borderTopColor'],
|
||||||
|
borderBlockEndColor: ['borderBottomColor'],
|
||||||
|
borderInlineColor: ['borderLeftColor', 'borderRightColor'],
|
||||||
|
borderInlineStartColor: ['borderLeftColor'],
|
||||||
|
borderInlineEndColor: ['borderRightColor'],
|
||||||
|
|
||||||
|
// Border radius
|
||||||
|
borderStartStartRadius: ['borderTopLeftRadius'],
|
||||||
|
borderStartEndRadius: ['borderTopRightRadius'],
|
||||||
|
borderEndStartRadius: ['borderBottomLeftRadius'],
|
||||||
|
borderEndEndRadius: ['borderBottomRightRadius'],
|
||||||
|
};
|
||||||
|
|
||||||
|
function skipCheck(value: string | number) {
|
||||||
|
return { _skip_check_: true, value };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert css logical properties to legacy properties.
|
||||||
|
* Such as: `margin-block-start` to `margin-top`.
|
||||||
|
* Transform list:
|
||||||
|
* - inset
|
||||||
|
* - margin
|
||||||
|
* - padding
|
||||||
|
* - border
|
||||||
|
*/
|
||||||
|
const transform: Transformer = {
|
||||||
|
visit: cssObj => {
|
||||||
|
const clone: CSSObject = {};
|
||||||
|
|
||||||
|
Object.keys(cssObj).forEach(key => {
|
||||||
|
const value = cssObj[key];
|
||||||
|
const matchValue = keyMap[key];
|
||||||
|
|
||||||
|
if (matchValue && (typeof value === 'number' || typeof value === 'string')) {
|
||||||
|
const values = splitValues(value);
|
||||||
|
|
||||||
|
if (matchValue.length && matchValue.notSplit) {
|
||||||
|
// not split means always give same value like border
|
||||||
|
matchValue.forEach(matchKey => {
|
||||||
|
clone[matchKey] = skipCheck(value);
|
||||||
|
});
|
||||||
|
} else if (matchValue.length === 1) {
|
||||||
|
// Handle like `marginBlockStart` => `marginTop`
|
||||||
|
clone[matchValue[0]] = skipCheck(value);
|
||||||
|
} else if (matchValue.length === 2) {
|
||||||
|
// Handle like `marginBlock` => `marginTop` & `marginBottom`
|
||||||
|
matchValue.forEach((matchKey, index) => {
|
||||||
|
clone[matchKey] = skipCheck(values[index] ?? values[0]);
|
||||||
|
});
|
||||||
|
} else if (matchValue.length === 4) {
|
||||||
|
// Handle like `inset` => `top` & `right` & `bottom` & `left`
|
||||||
|
matchValue.forEach((matchKey, index) => {
|
||||||
|
clone[matchKey] = skipCheck(values[index] ?? values[index - 2] ?? values[0]);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
clone[key] = value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clone[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default transform;
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* respect https://github.com/cuth/postcss-pxtorem
|
||||||
|
*/
|
||||||
|
import unitless from '@emotion/unitless';
|
||||||
|
import type { CSSObject } from '..';
|
||||||
|
import type { Transformer } from './interface';
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
/**
|
||||||
|
* The root font size.
|
||||||
|
* @default 16
|
||||||
|
*/
|
||||||
|
rootValue?: number;
|
||||||
|
/**
|
||||||
|
* The decimal numbers to allow the REM units to grow to.
|
||||||
|
* @default 5
|
||||||
|
*/
|
||||||
|
precision?: number;
|
||||||
|
/**
|
||||||
|
* Whether to allow px to be converted in media queries.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
mediaQuery?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pxRegex = /url\([^)]+\)|var\([^)]+\)|(\d*\.?\d+)px/g;
|
||||||
|
|
||||||
|
function toFixed(number: number, precision: number) {
|
||||||
|
const multiplier = Math.pow(10, precision + 1),
|
||||||
|
wholeNumber = Math.floor(number * multiplier);
|
||||||
|
return (Math.round(wholeNumber / 10) * 10) / multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transform = (options: Options = {}): Transformer => {
|
||||||
|
const { rootValue = 16, precision = 5, mediaQuery = false } = options;
|
||||||
|
|
||||||
|
const pxReplace = (m: string, $1: any) => {
|
||||||
|
if (!$1) return m;
|
||||||
|
const pixels = parseFloat($1);
|
||||||
|
// covenant: pixels <= 1, not transform to rem @zombieJ
|
||||||
|
if (pixels <= 1) return m;
|
||||||
|
const fixedVal = toFixed(pixels / rootValue, precision);
|
||||||
|
return `${fixedVal}rem`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const visit = (cssObj: CSSObject): CSSObject => {
|
||||||
|
const clone: CSSObject = { ...cssObj };
|
||||||
|
|
||||||
|
Object.entries(cssObj).forEach(([key, value]) => {
|
||||||
|
if (typeof value === 'string' && value.includes('px')) {
|
||||||
|
const newValue = value.replace(pxRegex, pxReplace);
|
||||||
|
clone[key] = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no unit
|
||||||
|
if (!unitless[key] && typeof value === 'number' && value !== 0) {
|
||||||
|
clone[key] = `${value}px`.replace(pxRegex, pxReplace);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Media queries
|
||||||
|
const mergedKey = key.trim();
|
||||||
|
if (mergedKey.startsWith('@') && mergedKey.includes('px') && mediaQuery) {
|
||||||
|
const newKey = key.replace(pxRegex, pxReplace);
|
||||||
|
|
||||||
|
clone[newKey] = clone[key];
|
||||||
|
delete clone[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { visit };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default transform;
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
import hash from '@emotion/hash';
|
||||||
|
import { removeCSS, updateCSS } from '../../vc-util/Dom/dynamicCSS';
|
||||||
|
import canUseDom from '../canUseDom';
|
||||||
|
|
||||||
|
import { Theme } from './theme';
|
||||||
|
|
||||||
|
// Create a cache here to avoid always loop generate
|
||||||
|
const flattenTokenCache = new WeakMap<any, string>();
|
||||||
|
|
||||||
|
export function flattenToken(token: any) {
|
||||||
|
let str = flattenTokenCache.get(token) || '';
|
||||||
|
|
||||||
|
if (!str) {
|
||||||
|
Object.keys(token).forEach(key => {
|
||||||
|
const value = token[key];
|
||||||
|
str += key;
|
||||||
|
if (value instanceof Theme) {
|
||||||
|
str += value.id;
|
||||||
|
} else if (value && typeof value === 'object') {
|
||||||
|
str += flattenToken(value);
|
||||||
|
} else {
|
||||||
|
str += value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Put in cache
|
||||||
|
flattenTokenCache.set(token, str);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert derivative token to key string
|
||||||
|
*/
|
||||||
|
export function token2key(token: any, salt: string): string {
|
||||||
|
return hash(`${salt}_${flattenToken(token)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomSelectorKey = `random-${Date.now()}-${Math.random()}`.replace(/\./g, '');
|
||||||
|
|
||||||
|
// Magic `content` for detect selector support
|
||||||
|
const checkContent = '_bAmBoO_';
|
||||||
|
|
||||||
|
function supportSelector(
|
||||||
|
styleStr: string,
|
||||||
|
handleElement: (ele: HTMLElement) => void,
|
||||||
|
supportCheck?: (ele: HTMLElement) => boolean,
|
||||||
|
): boolean {
|
||||||
|
if (canUseDom()) {
|
||||||
|
updateCSS(styleStr, randomSelectorKey);
|
||||||
|
|
||||||
|
const ele = document.createElement('div');
|
||||||
|
ele.style.position = 'fixed';
|
||||||
|
ele.style.left = '0';
|
||||||
|
ele.style.top = '0';
|
||||||
|
handleElement?.(ele);
|
||||||
|
document.body.appendChild(ele);
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
ele.innerHTML = 'Test';
|
||||||
|
ele.style.zIndex = '9999999';
|
||||||
|
}
|
||||||
|
|
||||||
|
const support = supportCheck
|
||||||
|
? supportCheck(ele)
|
||||||
|
: getComputedStyle(ele).content?.includes(checkContent);
|
||||||
|
|
||||||
|
ele.parentNode?.removeChild(ele);
|
||||||
|
removeCSS(randomSelectorKey);
|
||||||
|
|
||||||
|
return support;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let canLayer: boolean | undefined = undefined;
|
||||||
|
export function supportLayer(): boolean {
|
||||||
|
if (canLayer === undefined) {
|
||||||
|
canLayer = supportSelector(
|
||||||
|
`@layer ${randomSelectorKey} { .${randomSelectorKey} { content: "${checkContent}"!important; } }`,
|
||||||
|
ele => {
|
||||||
|
ele.className = randomSelectorKey;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return canLayer!;
|
||||||
|
}
|
||||||
|
|
||||||
|
let canWhere: boolean | undefined = undefined;
|
||||||
|
export function supportWhere(): boolean {
|
||||||
|
if (canWhere === undefined) {
|
||||||
|
canWhere = supportSelector(
|
||||||
|
`:where(.${randomSelectorKey}) { content: "${checkContent}"!important; }`,
|
||||||
|
ele => {
|
||||||
|
ele.className = randomSelectorKey;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return canWhere!;
|
||||||
|
}
|
||||||
|
|
||||||
|
let canLogic: boolean | undefined = undefined;
|
||||||
|
export function supportLogicProps(): boolean {
|
||||||
|
if (canLogic === undefined) {
|
||||||
|
canLogic = supportSelector(
|
||||||
|
`.${randomSelectorKey} { inset-block: 93px !important; }`,
|
||||||
|
ele => {
|
||||||
|
ele.className = randomSelectorKey;
|
||||||
|
},
|
||||||
|
ele => getComputedStyle(ele).bottom === '93px',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return canLogic!;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
type RecordType = Record<string, any>;
|
||||||
|
|
||||||
|
function extendsObject<T extends RecordType>(...list: T[]) {
|
||||||
|
const result: RecordType = { ...list[0] };
|
||||||
|
|
||||||
|
for (let i = 1; i < list.length; i++) {
|
||||||
|
const obj = list[i];
|
||||||
|
if (obj) {
|
||||||
|
Object.keys(obj).forEach(key => {
|
||||||
|
const val = obj[key];
|
||||||
|
if (val !== undefined) {
|
||||||
|
result[key] = val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default extendsObject;
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import type { SizeType } from '../config-provider/SizeContext';
|
||||||
|
|
||||||
|
export function isPresetSize(size?: SizeType | string | number): size is SizeType {
|
||||||
|
return ['small', 'middle', 'large'].includes(size as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isValidGapNumber(size?: SizeType | string | number): size is number {
|
||||||
|
if (!size) {
|
||||||
|
// The case of size = 0 is deliberately excluded here, because the default value of the gap attribute in CSS is 0, so if the user passes 0 in, we can directly ignore it.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return typeof size === 'number' && !Number.isNaN(size);
|
||||||
|
}
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
export function getComponentLocale(props, context, componentName, getDefaultLocale) {
|
|
||||||
let locale = {};
|
|
||||||
if (context && context.antLocale && context.antLocale[componentName]) {
|
|
||||||
locale = context.antLocale[componentName];
|
|
||||||
} else {
|
|
||||||
const defaultLocale = getDefaultLocale();
|
|
||||||
// TODO: make default lang of antd be English
|
|
||||||
// https://github.com/ant-design/ant-design/issues/6334
|
|
||||||
locale = defaultLocale.default || defaultLocale;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = {
|
|
||||||
...locale,
|
|
||||||
...props.locale,
|
|
||||||
};
|
|
||||||
result.lang = {
|
|
||||||
...locale.lang,
|
|
||||||
...props.locale.lang,
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLocaleCode(context) {
|
|
||||||
const localeCode = context.antLocale && context.antLocale.locale;
|
|
||||||
// Had use LocaleProvide but didn't set locale
|
|
||||||
if (context.antLocale && context.antLocale.exist && !localeCode) {
|
|
||||||
return 'zh-cn';
|
|
||||||
}
|
|
||||||
return localeCode;
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export function isWindow(obj: any) {
|
export function isWindow(obj: any): obj is Window {
|
||||||
return obj !== null && obj !== undefined && obj === obj.window;
|
return obj !== null && obj !== undefined && obj === obj.window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -12,16 +12,22 @@ export default function getScroll(
|
||||||
const method = top ? 'scrollTop' : 'scrollLeft';
|
const method = top ? 'scrollTop' : 'scrollLeft';
|
||||||
let result = 0;
|
let result = 0;
|
||||||
if (isWindow(target)) {
|
if (isWindow(target)) {
|
||||||
result = (target as Window)[top ? 'pageYOffset' : 'pageXOffset'];
|
result = target[top ? 'scrollY' : 'scrollX'];
|
||||||
} else if (target instanceof Document) {
|
} else if (target instanceof Document) {
|
||||||
result = target.documentElement[method];
|
result = target.documentElement[method];
|
||||||
|
} else if (target instanceof HTMLElement) {
|
||||||
|
result = target[method];
|
||||||
} else if (target) {
|
} else if (target) {
|
||||||
result = (target as HTMLElement)[method];
|
// According to the type inference, the `target` is `never` type.
|
||||||
|
// Since we configured the loose mode type checking, and supports mocking the target with such shape below::
|
||||||
|
// `{ documentElement: { scrollLeft: 200, scrollTop: 400 } }`,
|
||||||
|
// the program may falls into this branch.
|
||||||
|
// Check the corresponding tests for details. Don't sure what is the real scenario this happens.
|
||||||
|
result = target[method];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target && !isWindow(target) && typeof result !== 'number') {
|
if (target && !isWindow(target) && typeof result !== 'number') {
|
||||||
result = ((target as HTMLElement).ownerDocument || (target as Document)).documentElement?.[
|
result = ((target.ownerDocument ?? target) as any).documentElement?.[method];
|
||||||
method
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export interface ConfigurableLocation {
|
||||||
location?: Location;
|
location?: Location;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultWindow = /* #__PURE__ */ isClient ? window : undefined;
|
export const defaultWindow = isClient ? window : undefined;
|
||||||
export const defaultDocument = /* #__PURE__ */ isClient ? window.document : undefined;
|
export const defaultDocument = isClient ? window.document : undefined;
|
||||||
export const defaultNavigator = /* #__PURE__ */ isClient ? window.navigator : undefined;
|
export const defaultNavigator = isClient ? window.navigator : undefined;
|
||||||
export const defaultLocation = /* #__PURE__ */ isClient ? window.location : undefined;
|
export const defaultLocation = isClient ? window.location : undefined;
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,6 @@ export const rand = (min: number, max: number) => {
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
};
|
};
|
||||||
export const isIOS =
|
export const isIOS =
|
||||||
/* #__PURE__ */ isClient &&
|
isClient && window?.navigator?.userAgent && /iP(ad|hone|od)/.test(window.navigator.userAgent);
|
||||||
window?.navigator?.userAgent &&
|
|
||||||
/iP(ad|hone|od)/.test(window.navigator.userAgent);
|
|
||||||
export const hasOwn = <T extends object, K extends keyof T>(val: T, key: K): key is K =>
|
export const hasOwn = <T extends object, K extends keyof T>(val: T, key: K): key is K =>
|
||||||
Object.prototype.hasOwnProperty.call(val, key);
|
Object.prototype.hasOwnProperty.call(val, key);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ref, watch } from 'vue';
|
import { shallowRef, watch } from 'vue';
|
||||||
import type { MaybeComputedElementRef } from './unrefElement';
|
import type { MaybeComputedElementRef } from './unrefElement';
|
||||||
import type { UseResizeObserverOptions } from './useResizeObserver';
|
import type { UseResizeObserverOptions } from './useResizeObserver';
|
||||||
import { useResizeObserver } from './useResizeObserver';
|
import { useResizeObserver } from './useResizeObserver';
|
||||||
|
|
@ -23,8 +23,8 @@ export function useElementSize(
|
||||||
options: UseResizeObserverOptions = {},
|
options: UseResizeObserverOptions = {},
|
||||||
) {
|
) {
|
||||||
const { box = 'content-box' } = options;
|
const { box = 'content-box' } = options;
|
||||||
const width = ref(initialSize.width);
|
const width = shallowRef(initialSize.width);
|
||||||
const height = ref(initialSize.height);
|
const height = shallowRef(initialSize.height);
|
||||||
|
|
||||||
useResizeObserver(
|
useResizeObserver(
|
||||||
target,
|
target,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { tryOnScopeDispose } from './tryOnScopeDispose';
|
||||||
|
import { watch } from 'vue';
|
||||||
|
import type { MaybeElementRef } from './unrefElement';
|
||||||
|
import { unrefElement } from './unrefElement';
|
||||||
|
import { useSupported } from './useSupported';
|
||||||
|
import type { ConfigurableWindow } from './_configurable';
|
||||||
|
import { defaultWindow } from './_configurable';
|
||||||
|
|
||||||
|
export interface UseMutationObserverOptions extends MutationObserverInit, ConfigurableWindow {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watch for changes being made to the DOM tree.
|
||||||
|
*
|
||||||
|
* @see https://vueuse.org/useMutationObserver
|
||||||
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver MutationObserver MDN
|
||||||
|
* @param target
|
||||||
|
* @param callback
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
export function useMutationObserver(
|
||||||
|
target: MaybeElementRef,
|
||||||
|
callback: MutationCallback,
|
||||||
|
options: UseMutationObserverOptions = {},
|
||||||
|
) {
|
||||||
|
const { window = defaultWindow, ...mutationOptions } = options;
|
||||||
|
let observer: MutationObserver | undefined;
|
||||||
|
const isSupported = useSupported(() => window && 'MutationObserver' in window);
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
if (observer) {
|
||||||
|
observer.disconnect();
|
||||||
|
observer = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopWatch = watch(
|
||||||
|
() => unrefElement(target),
|
||||||
|
el => {
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
if (isSupported.value && window && el) {
|
||||||
|
observer = new MutationObserver(callback);
|
||||||
|
observer!.observe(el, mutationOptions);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
const stop = () => {
|
||||||
|
cleanup();
|
||||||
|
stopWatch();
|
||||||
|
};
|
||||||
|
|
||||||
|
tryOnScopeDispose(stop);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isSupported,
|
||||||
|
stop,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UseMutationObserverReturn = ReturnType<typeof useMutationObserver>;
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { tryOnMounted } from './tryOnMounted';
|
import { tryOnMounted } from './tryOnMounted';
|
||||||
import type { Ref } from 'vue';
|
import { shallowRef } from 'vue';
|
||||||
import { ref } from 'vue';
|
|
||||||
|
|
||||||
export function useSupported(callback: () => unknown, sync = false) {
|
export function useSupported(callback: () => unknown, sync = false) {
|
||||||
const isSupported = ref() as Ref<boolean>;
|
const isSupported = shallowRef<boolean>();
|
||||||
|
|
||||||
const update = () => (isSupported.value = Boolean(callback()));
|
const update = () => (isSupported.value = Boolean(callback()));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,21 @@
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
import { onMounted, onUnmounted, ref } from 'vue';
|
import { onMounted, onUnmounted, shallowRef } from 'vue';
|
||||||
import type { ScreenMap } from '../../_util/responsiveObserve';
|
import type { ScreenMap } from '../../_util/responsiveObserve';
|
||||||
import ResponsiveObserve from '../../_util/responsiveObserve';
|
import useResponsiveObserve from '../../_util/responsiveObserve';
|
||||||
|
|
||||||
function useBreakpoint(): Ref<ScreenMap> {
|
function useBreakpoint(): Ref<ScreenMap> {
|
||||||
const screens = ref<ScreenMap>({});
|
const screens = shallowRef<ScreenMap>({});
|
||||||
let token = null;
|
let token = null;
|
||||||
|
const responsiveObserve = useResponsiveObserve();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
token = ResponsiveObserve.subscribe(supportScreens => {
|
token = responsiveObserve.value.subscribe(supportScreens => {
|
||||||
screens.value = supportScreens;
|
screens.value = supportScreens;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
ResponsiveObserve.unsubscribe(token);
|
responsiveObserve.value.unsubscribe(token);
|
||||||
});
|
});
|
||||||
|
|
||||||
return screens;
|
return screens;
|
||||||
|
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
import type { RequiredMark } from '../../form/Form';
|
|
||||||
import type { ComputedRef, UnwrapRef } from 'vue';
|
|
||||||
import { computed, inject } from 'vue';
|
|
||||||
import type { ConfigProviderProps, CSPConfig, Direction, SizeType } from '../../config-provider';
|
|
||||||
import { defaultConfigProvider } from '../../config-provider';
|
|
||||||
import type { VueNode } from '../type';
|
|
||||||
import type { ValidateMessages } from '../../form/interface';
|
|
||||||
|
|
||||||
export default (
|
|
||||||
name: string,
|
|
||||||
props: Record<any, any>,
|
|
||||||
): {
|
|
||||||
configProvider: UnwrapRef<ConfigProviderProps>;
|
|
||||||
prefixCls: ComputedRef<string>;
|
|
||||||
rootPrefixCls: ComputedRef<string>;
|
|
||||||
direction: ComputedRef<Direction>;
|
|
||||||
size: ComputedRef<SizeType>;
|
|
||||||
getTargetContainer: ComputedRef<() => HTMLElement>;
|
|
||||||
space: ComputedRef<{ size: SizeType | number }>;
|
|
||||||
pageHeader: ComputedRef<{ ghost: boolean }>;
|
|
||||||
form?: ComputedRef<{
|
|
||||||
requiredMark?: RequiredMark;
|
|
||||||
colon?: boolean;
|
|
||||||
validateMessages?: ValidateMessages;
|
|
||||||
}>;
|
|
||||||
autoInsertSpaceInButton: ComputedRef<boolean>;
|
|
||||||
renderEmpty?: ComputedRef<(componentName?: string) => VueNode>;
|
|
||||||
virtual: ComputedRef<boolean>;
|
|
||||||
dropdownMatchSelectWidth: ComputedRef<boolean | number>;
|
|
||||||
getPopupContainer: ComputedRef<ConfigProviderProps['getPopupContainer']>;
|
|
||||||
getPrefixCls: ConfigProviderProps['getPrefixCls'];
|
|
||||||
autocomplete: ComputedRef<string>;
|
|
||||||
csp: ComputedRef<CSPConfig>;
|
|
||||||
} => {
|
|
||||||
const configProvider = inject<UnwrapRef<ConfigProviderProps>>(
|
|
||||||
'configProvider',
|
|
||||||
defaultConfigProvider,
|
|
||||||
);
|
|
||||||
const prefixCls = computed(() => configProvider.getPrefixCls(name, props.prefixCls));
|
|
||||||
const direction = computed(() => props.direction ?? configProvider.direction);
|
|
||||||
const rootPrefixCls = computed(() => configProvider.getPrefixCls());
|
|
||||||
const autoInsertSpaceInButton = computed(() => configProvider.autoInsertSpaceInButton);
|
|
||||||
const renderEmpty = computed(() => configProvider.renderEmpty);
|
|
||||||
const space = computed(() => configProvider.space);
|
|
||||||
const pageHeader = computed(() => configProvider.pageHeader);
|
|
||||||
const form = computed(() => configProvider.form);
|
|
||||||
const getTargetContainer = computed(
|
|
||||||
() => props.getTargetContainer || configProvider.getTargetContainer,
|
|
||||||
);
|
|
||||||
const getPopupContainer = computed(
|
|
||||||
() => props.getPopupContainer || configProvider.getPopupContainer,
|
|
||||||
);
|
|
||||||
|
|
||||||
const dropdownMatchSelectWidth = computed<boolean | number>(
|
|
||||||
() => props.dropdownMatchSelectWidth ?? configProvider.dropdownMatchSelectWidth,
|
|
||||||
);
|
|
||||||
const virtual = computed(
|
|
||||||
() =>
|
|
||||||
(props.virtual === undefined ? configProvider.virtual !== false : props.virtual !== false) &&
|
|
||||||
dropdownMatchSelectWidth.value !== false,
|
|
||||||
);
|
|
||||||
const size = computed(() => props.size || configProvider.componentSize);
|
|
||||||
const autocomplete = computed(() => props.autocomplete || configProvider.input?.autocomplete);
|
|
||||||
const csp = computed(() => configProvider.csp);
|
|
||||||
return {
|
|
||||||
configProvider,
|
|
||||||
prefixCls,
|
|
||||||
direction,
|
|
||||||
size,
|
|
||||||
getTargetContainer,
|
|
||||||
getPopupContainer,
|
|
||||||
space,
|
|
||||||
pageHeader,
|
|
||||||
form,
|
|
||||||
autoInsertSpaceInButton,
|
|
||||||
renderEmpty,
|
|
||||||
virtual,
|
|
||||||
dropdownMatchSelectWidth,
|
|
||||||
rootPrefixCls,
|
|
||||||
getPrefixCls: configProvider.getPrefixCls,
|
|
||||||
autocomplete,
|
|
||||||
csp,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { onBeforeUnmount, ref } from 'vue';
|
import { onBeforeUnmount, shallowRef } from 'vue';
|
||||||
|
|
||||||
const useDestroyed = () => {
|
const useDestroyed = () => {
|
||||||
const destroyed = ref(false);
|
const destroyed = shallowRef(false);
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
destroyed.value = true;
|
destroyed.value = true;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, shallowRef } from 'vue';
|
||||||
import { detectFlexGapSupported } from '../styleChecker';
|
import { detectFlexGapSupported } from '../styleChecker';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const flexible = ref(false);
|
const flexible = shallowRef(false);
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
flexible.value = detectFlexGapSupported();
|
flexible.value = detectFlexGapSupported();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import canUseDom from '../../_util/canUseDom';
|
||||||
|
|
||||||
|
let uuid = 0;
|
||||||
|
|
||||||
|
/** Is client side and not jsdom */
|
||||||
|
export const isBrowserClient = process.env.NODE_ENV !== 'test' && canUseDom();
|
||||||
|
|
||||||
|
/** Get unique id for accessibility usage */
|
||||||
|
export function getUUID(): number | string {
|
||||||
|
let retId: string | number;
|
||||||
|
|
||||||
|
// Test never reach
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (isBrowserClient) {
|
||||||
|
retId = uuid;
|
||||||
|
uuid += 1;
|
||||||
|
} else {
|
||||||
|
retId = 'TEST_OR_SSR';
|
||||||
|
}
|
||||||
|
|
||||||
|
return retId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useId(id = ref('')) {
|
||||||
|
// Inner id for accessibility usage. Only work in client side
|
||||||
|
const innerId = `vc_unique_${getUUID()}`;
|
||||||
|
|
||||||
|
return id.value || innerId;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
import { onBeforeUnmount, ref } from 'vue';
|
import { onBeforeUnmount, shallowRef } from 'vue';
|
||||||
import raf from '../raf';
|
import raf from '../raf';
|
||||||
|
|
||||||
export type Updater<State> = (prev: State) => State;
|
export type Updater<State> = (prev: State) => State;
|
||||||
|
|
@ -9,11 +9,11 @@ export type Updater<State> = (prev: State) => State;
|
||||||
export function useLayoutState<State>(
|
export function useLayoutState<State>(
|
||||||
defaultState: State,
|
defaultState: State,
|
||||||
): [Ref<State>, (updater: Updater<State>) => void] {
|
): [Ref<State>, (updater: Updater<State>) => void] {
|
||||||
const stateRef = ref(defaultState);
|
const stateRef = shallowRef(defaultState);
|
||||||
let tempState = stateRef.value;
|
let tempState = stateRef.value;
|
||||||
|
|
||||||
let updateBatchRef = [];
|
let updateBatchRef = [];
|
||||||
const rafRef = ref();
|
const rafRef = shallowRef();
|
||||||
function setFrameState(updater: Updater<State>) {
|
function setFrameState(updater: Updater<State>) {
|
||||||
raf.cancel(rafRef.value);
|
raf.cancel(rafRef.value);
|
||||||
updateBatchRef.push(updater);
|
updateBatchRef.push(updater);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
import { computed, watchEffect } from 'vue';
|
||||||
|
import { updateCSS, removeCSS } from '../../vc-util/Dom/dynamicCSS';
|
||||||
|
import getScrollBarSize from '../../_util/getScrollBarSize';
|
||||||
|
import canUseDom from '../../_util/canUseDom';
|
||||||
|
|
||||||
|
const UNIQUE_ID = `vc-util-locker-${Date.now()}`;
|
||||||
|
|
||||||
|
let uuid = 0;
|
||||||
|
|
||||||
|
/**../vc-util/Dom/dynam
|
||||||
|
* Test usage export. Do not use in your production
|
||||||
|
*/
|
||||||
|
export function isBodyOverflowing() {
|
||||||
|
return (
|
||||||
|
document.body.scrollHeight > (window.innerHeight || document.documentElement.clientHeight) &&
|
||||||
|
window.innerWidth > document.body.offsetWidth
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useScrollLocker(lock?: Ref<boolean>) {
|
||||||
|
const mergedLock = computed(() => !!lock && !!lock.value);
|
||||||
|
uuid += 1;
|
||||||
|
const id = `${UNIQUE_ID}_${uuid}`;
|
||||||
|
|
||||||
|
watchEffect(
|
||||||
|
onClear => {
|
||||||
|
if (!canUseDom()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mergedLock.value) {
|
||||||
|
const scrollbarSize = getScrollBarSize();
|
||||||
|
const isOverflow = isBodyOverflowing();
|
||||||
|
|
||||||
|
updateCSS(
|
||||||
|
`
|
||||||
|
html body {
|
||||||
|
overflow-y: hidden;
|
||||||
|
${isOverflow ? `width: calc(100% - ${scrollbarSize}px);` : ''}
|
||||||
|
}`,
|
||||||
|
id,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
removeCSS(id);
|
||||||
|
}
|
||||||
|
onClear(() => {
|
||||||
|
removeCSS(id);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ flush: 'post' },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
import type { ComputedRef, UnwrapRef } from 'vue';
|
|
||||||
import { computed, inject, provide } from 'vue';
|
|
||||||
import type { ConfigProviderProps, SizeType } from '../../config-provider';
|
|
||||||
import { defaultConfigProvider } from '../../config-provider';
|
|
||||||
|
|
||||||
const sizeProvider = Symbol('SizeProvider');
|
|
||||||
|
|
||||||
const useProvideSize = <T = SizeType>(props: Record<any, any>): ComputedRef<T> => {
|
|
||||||
const configProvider = inject<UnwrapRef<ConfigProviderProps>>(
|
|
||||||
'configProvider',
|
|
||||||
defaultConfigProvider,
|
|
||||||
);
|
|
||||||
const size = computed<T>(() => props.size || configProvider.componentSize);
|
|
||||||
provide(sizeProvider, size);
|
|
||||||
return size;
|
|
||||||
};
|
|
||||||
|
|
||||||
const useInjectSize = <T = SizeType>(props?: Record<any, any>): ComputedRef<T> => {
|
|
||||||
const size: ComputedRef<T> = props
|
|
||||||
? computed(() => props.size)
|
|
||||||
: inject(
|
|
||||||
sizeProvider,
|
|
||||||
computed(() => 'default' as unknown as T),
|
|
||||||
);
|
|
||||||
return size;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { useInjectSize, sizeProvider, useProvideSize };
|
|
||||||
|
|
||||||
export default useProvideSize;
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
let animation;
|
|
||||||
|
|
||||||
function isCssAnimationSupported() {
|
|
||||||
if (animation !== undefined) {
|
|
||||||
return animation;
|
|
||||||
}
|
|
||||||
const domPrefixes = 'Webkit Moz O ms Khtml'.split(' ');
|
|
||||||
const elm = document.createElement('div');
|
|
||||||
if (elm.style.animationName !== undefined) {
|
|
||||||
animation = true;
|
|
||||||
}
|
|
||||||
if (animation !== undefined) {
|
|
||||||
for (let i = 0; i < domPrefixes.length; i++) {
|
|
||||||
if (elm.style[`${domPrefixes[i]}AnimationName`] !== undefined) {
|
|
||||||
animation = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
animation = animation || false;
|
|
||||||
return animation;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default isCssAnimationSupported;
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
// MIT License from https://github.com/kaimallea/isMobile
|
|
||||||
|
|
||||||
const applePhone = /iPhone/i;
|
|
||||||
const appleIpod = /iPod/i;
|
|
||||||
const appleTablet = /iPad/i;
|
|
||||||
const androidPhone = /\bAndroid(?:.+)Mobile\b/i; // Match 'Android' AND 'Mobile'
|
|
||||||
const androidTablet = /Android/i;
|
|
||||||
const amazonPhone = /\bAndroid(?:.+)SD4930UR\b/i;
|
|
||||||
const amazonTablet = /\bAndroid(?:.+)(?:KF[A-Z]{2,4})\b/i;
|
|
||||||
const windowsPhone = /Windows Phone/i;
|
|
||||||
const windowsTablet = /\bWindows(?:.+)ARM\b/i; // Match 'Windows' AND 'ARM'
|
|
||||||
const otherBlackberry = /BlackBerry/i;
|
|
||||||
const otherBlackberry10 = /BB10/i;
|
|
||||||
const otherOpera = /Opera Mini/i;
|
|
||||||
const otherChrome = /\b(CriOS|Chrome)(?:.+)Mobile/i;
|
|
||||||
const otherFirefox = /Mobile(?:.+)Firefox\b/i; // Match 'Mobile' AND 'Firefox'
|
|
||||||
|
|
||||||
function match(regex, userAgent) {
|
|
||||||
return regex.test(userAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isMobile(userAgent) {
|
|
||||||
let ua = userAgent || (typeof navigator !== 'undefined' ? navigator.userAgent : '');
|
|
||||||
|
|
||||||
// Facebook mobile app's integrated browser adds a bunch of strings that
|
|
||||||
// match everything. Strip it out if it exists.
|
|
||||||
let tmp = ua.split('[FBAN');
|
|
||||||
if (typeof tmp[1] !== 'undefined') {
|
|
||||||
[ua] = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Twitter mobile app's integrated browser on iPad adds a "Twitter for
|
|
||||||
// iPhone" string. Same probably happens on other tablet platforms.
|
|
||||||
// This will confuse detection so strip it out if it exists.
|
|
||||||
tmp = ua.split('Twitter');
|
|
||||||
if (typeof tmp[1] !== 'undefined') {
|
|
||||||
[ua] = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = {
|
|
||||||
apple: {
|
|
||||||
phone: match(applePhone, ua) && !match(windowsPhone, ua),
|
|
||||||
ipod: match(appleIpod, ua),
|
|
||||||
tablet: !match(applePhone, ua) && match(appleTablet, ua) && !match(windowsPhone, ua),
|
|
||||||
device:
|
|
||||||
(match(applePhone, ua) || match(appleIpod, ua) || match(appleTablet, ua)) &&
|
|
||||||
!match(windowsPhone, ua),
|
|
||||||
},
|
|
||||||
amazon: {
|
|
||||||
phone: match(amazonPhone, ua),
|
|
||||||
tablet: !match(amazonPhone, ua) && match(amazonTablet, ua),
|
|
||||||
device: match(amazonPhone, ua) || match(amazonTablet, ua),
|
|
||||||
},
|
|
||||||
android: {
|
|
||||||
phone:
|
|
||||||
(!match(windowsPhone, ua) && match(amazonPhone, ua)) ||
|
|
||||||
(!match(windowsPhone, ua) && match(androidPhone, ua)),
|
|
||||||
tablet:
|
|
||||||
!match(windowsPhone, ua) &&
|
|
||||||
!match(amazonPhone, ua) &&
|
|
||||||
!match(androidPhone, ua) &&
|
|
||||||
(match(amazonTablet, ua) || match(androidTablet, ua)),
|
|
||||||
device:
|
|
||||||
(!match(windowsPhone, ua) &&
|
|
||||||
(match(amazonPhone, ua) ||
|
|
||||||
match(amazonTablet, ua) ||
|
|
||||||
match(androidPhone, ua) ||
|
|
||||||
match(androidTablet, ua))) ||
|
|
||||||
match(/\bokhttp\b/i, ua),
|
|
||||||
},
|
|
||||||
windows: {
|
|
||||||
phone: match(windowsPhone, ua),
|
|
||||||
tablet: match(windowsTablet, ua),
|
|
||||||
device: match(windowsPhone, ua) || match(windowsTablet, ua),
|
|
||||||
},
|
|
||||||
other: {
|
|
||||||
blackberry: match(otherBlackberry, ua),
|
|
||||||
blackberry10: match(otherBlackberry10, ua),
|
|
||||||
opera: match(otherOpera, ua),
|
|
||||||
firefox: match(otherFirefox, ua),
|
|
||||||
chrome: match(otherChrome, ua),
|
|
||||||
device:
|
|
||||||
match(otherBlackberry, ua) ||
|
|
||||||
match(otherBlackberry10, ua) ||
|
|
||||||
match(otherOpera, ua) ||
|
|
||||||
match(otherFirefox, ua) ||
|
|
||||||
match(otherChrome, ua),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Additional
|
|
||||||
any: null,
|
|
||||||
phone: null,
|
|
||||||
tablet: null,
|
|
||||||
};
|
|
||||||
result.any =
|
|
||||||
result.apple.device || result.android.device || result.windows.device || result.other.device;
|
|
||||||
|
|
||||||
// excludes 'other' devices and ipods, targeting touchscreen phones
|
|
||||||
result.phone = result.apple.phone || result.android.phone || result.windows.phone;
|
|
||||||
result.tablet = result.apple.tablet || result.android.tablet || result.windows.tablet;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultResult = {
|
|
||||||
...isMobile(),
|
|
||||||
isMobile,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default defaultResult;
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* https://github.com/akiran/json2mq.git
|
* https://github.com/akiran/json2mq.git
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const camel2hyphen = function (str) {
|
const camel2hyphen = function (str: string) {
|
||||||
return str
|
return str
|
||||||
.replace(/[A-Z]/g, function (match) {
|
.replace(/[A-Z]/g, function (match) {
|
||||||
return '-' + match.toLowerCase();
|
return '-' + match.toLowerCase();
|
||||||
|
|
@ -11,12 +11,12 @@ const camel2hyphen = function (str) {
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
};
|
};
|
||||||
|
|
||||||
const isDimension = function (feature) {
|
const isDimension = function (feature: string) {
|
||||||
const re = /[height|width]$/;
|
const re = /[height|width]$/;
|
||||||
return re.test(feature);
|
return re.test(feature);
|
||||||
};
|
};
|
||||||
|
|
||||||
const obj2mq = function (obj) {
|
const obj2mq = function (obj: { [x: string]: any }) {
|
||||||
let mq = '';
|
let mq = '';
|
||||||
const features = Object.keys(obj);
|
const features = Object.keys(obj);
|
||||||
features.forEach(function (feature, index) {
|
features.forEach(function (feature, index) {
|
||||||
|
|
@ -40,7 +40,7 @@ const obj2mq = function (obj) {
|
||||||
return mq;
|
return mq;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function (query) {
|
export default function (query: any[]) {
|
||||||
let mq = '';
|
let mq = '';
|
||||||
if (typeof query === 'string') {
|
if (typeof query === 'string') {
|
||||||
return query;
|
return query;
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
import isPlainObject from 'lodash-es/isPlainObject';
|
|
||||||
import classNames from '../classNames';
|
import classNames from '../classNames';
|
||||||
import { isVNode, Fragment, Comment, Text, h } from 'vue';
|
import { isVNode, Fragment, Comment, Text } from 'vue';
|
||||||
import { camelize, hyphenate, isOn, resolvePropValue } from '../util';
|
import { camelize, hyphenate, isOn, resolvePropValue } from '../util';
|
||||||
import isValid from '../isValid';
|
import isValid from '../isValid';
|
||||||
import initDefaultProps from './initDefaultProps';
|
import initDefaultProps from './initDefaultProps';
|
||||||
|
import type { VueInstance } from '../hooks/_vueuse/unrefElement';
|
||||||
// function getType(fn) {
|
// function getType(fn) {
|
||||||
// const match = fn && fn.toString().match(/^\s*function (\w+)/);
|
// const match = fn && fn.toString().match(/^\s*function (\w+)/);
|
||||||
// return match ? match[1] : '';
|
// return match ? match[1] : '';
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const splitAttrs = attrs => {
|
const splitAttrs = (attrs: any) => {
|
||||||
const allAttrs = Object.keys(attrs);
|
const allAttrs = Object.keys(attrs);
|
||||||
const eventAttrs = {};
|
const eventAttrs: Record<string, any> = {};
|
||||||
const onEvents = {};
|
const onEvents: Record<string, any> = {};
|
||||||
const extraAttrs = {};
|
const extraAttrs: Record<string, any> = {};
|
||||||
for (let i = 0, l = allAttrs.length; i < l; i++) {
|
for (let i = 0, l = allAttrs.length; i < l; i++) {
|
||||||
const key = allAttrs[i];
|
const key = allAttrs[i];
|
||||||
if (isOn(key)) {
|
if (isOn(key)) {
|
||||||
|
|
@ -25,7 +25,7 @@ const splitAttrs = attrs => {
|
||||||
}
|
}
|
||||||
return { onEvents, events: eventAttrs, extraAttrs };
|
return { onEvents, events: eventAttrs, extraAttrs };
|
||||||
};
|
};
|
||||||
const parseStyleText = (cssText = '', camel) => {
|
const parseStyleText = (cssText = '', camel = false) => {
|
||||||
const res = {};
|
const res = {};
|
||||||
const listDelimiter = /;(?![^(]*\))/g;
|
const listDelimiter = /;(?![^(]*\))/g;
|
||||||
const propertyDelimiter = /:(.+)/;
|
const propertyDelimiter = /:(.+)/;
|
||||||
|
|
@ -42,35 +42,11 @@ const parseStyleText = (cssText = '', camel) => {
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasProp = (instance, prop) => {
|
const hasProp = (instance: any, prop: string) => {
|
||||||
return instance[prop] !== undefined;
|
return instance[prop] !== undefined;
|
||||||
};
|
};
|
||||||
// 重构后直接使用 hasProp 替换
|
|
||||||
const slotHasProp = (slot, prop) => {
|
|
||||||
return hasProp(slot, prop);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getScopedSlots = ele => {
|
|
||||||
return (ele.data && ele.data.scopedSlots) || {};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSlots = ele => {
|
|
||||||
let componentOptions = ele.componentOptions || {};
|
|
||||||
if (ele.$vnode) {
|
|
||||||
componentOptions = ele.$vnode.componentOptions || {};
|
|
||||||
}
|
|
||||||
const children = ele.children || componentOptions.children || [];
|
|
||||||
const slots = {};
|
|
||||||
children.forEach(child => {
|
|
||||||
if (!isEmptyElement(child)) {
|
|
||||||
const name = (child.data && child.data.slot) || 'default';
|
|
||||||
slots[name] = slots[name] || [];
|
|
||||||
slots[name].push(child);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { ...slots, ...getScopedSlots(ele) };
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export const skipFlattenKey = Symbol('skipFlatten');
|
||||||
const flattenChildren = (children = [], filterEmpty = true) => {
|
const flattenChildren = (children = [], filterEmpty = true) => {
|
||||||
const temp = Array.isArray(children) ? children : [children];
|
const temp = Array.isArray(children) ? children : [children];
|
||||||
const res = [];
|
const res = [];
|
||||||
|
|
@ -78,7 +54,11 @@ const flattenChildren = (children = [], filterEmpty = true) => {
|
||||||
if (Array.isArray(child)) {
|
if (Array.isArray(child)) {
|
||||||
res.push(...flattenChildren(child, filterEmpty));
|
res.push(...flattenChildren(child, filterEmpty));
|
||||||
} else if (child && child.type === Fragment) {
|
} else if (child && child.type === Fragment) {
|
||||||
|
if (child.key === skipFlattenKey) {
|
||||||
|
res.push(child);
|
||||||
|
} else {
|
||||||
res.push(...flattenChildren(child.children, filterEmpty));
|
res.push(...flattenChildren(child.children, filterEmpty));
|
||||||
|
}
|
||||||
} else if (child && isVNode(child)) {
|
} else if (child && isVNode(child)) {
|
||||||
if (filterEmpty && !isEmptyElement(child)) {
|
if (filterEmpty && !isEmptyElement(child)) {
|
||||||
res.push(child);
|
res.push(child);
|
||||||
|
|
@ -92,39 +72,29 @@ const flattenChildren = (children = [], filterEmpty = true) => {
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSlot = (self, name = 'default', options = {}) => {
|
const getSlot = (self: any, name = 'default', options = {}) => {
|
||||||
if (isVNode(self)) {
|
if (isVNode(self)) {
|
||||||
if (self.type === Fragment) {
|
if (self.type === Fragment) {
|
||||||
return name === 'default' ? flattenChildren(self.children) : [];
|
return name === 'default' ? flattenChildren(self.children as any[]) : [];
|
||||||
} else if (self.children && self.children[name]) {
|
} else if (self.children && self.children[name]) {
|
||||||
return flattenChildren(self.children[name](options));
|
return flattenChildren(self.children[name](options));
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let res = self.$slots[name] && self.$slots[name](options);
|
const res = self.$slots[name] && self.$slots[name](options);
|
||||||
return flattenChildren(res);
|
return flattenChildren(res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAllChildren = ele => {
|
const findDOMNode = (instance: any) => {
|
||||||
let componentOptions = ele.componentOptions || {};
|
|
||||||
if (ele.$vnode) {
|
|
||||||
componentOptions = ele.$vnode.componentOptions || {};
|
|
||||||
}
|
|
||||||
return ele.children || componentOptions.children || [];
|
|
||||||
};
|
|
||||||
const getSlotOptions = () => {
|
|
||||||
throw Error('使用 .type 直接取值');
|
|
||||||
};
|
|
||||||
const findDOMNode = instance => {
|
|
||||||
let node = instance?.vnode?.el || (instance && (instance.$el || instance));
|
let node = instance?.vnode?.el || (instance && (instance.$el || instance));
|
||||||
while (node && !node.tagName) {
|
while (node && !node.tagName) {
|
||||||
node = node.nextSibling;
|
node = node.nextSibling;
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
const getOptionProps = instance => {
|
const getOptionProps = (instance: VueInstance) => {
|
||||||
const res = {};
|
const res = {};
|
||||||
if (instance.$ && instance.$.vnode) {
|
if (instance.$ && instance.$.vnode) {
|
||||||
const props = instance.$.vnode.props || {};
|
const props = instance.$.vnode.props || {};
|
||||||
|
|
@ -141,7 +111,7 @@ const getOptionProps = instance => {
|
||||||
Object.keys(originProps).forEach(key => {
|
Object.keys(originProps).forEach(key => {
|
||||||
props[camelize(key)] = originProps[key];
|
props[camelize(key)] = originProps[key];
|
||||||
});
|
});
|
||||||
const options = instance.type.props || {};
|
const options = (instance.type as any).props || {};
|
||||||
Object.keys(options).forEach(k => {
|
Object.keys(options).forEach(k => {
|
||||||
const v = resolvePropValue(options, props, k, props[k]);
|
const v = resolvePropValue(options, props, k, props[k]);
|
||||||
if (v !== undefined || k in props) {
|
if (v !== undefined || k in props) {
|
||||||
|
|
@ -151,7 +121,7 @@ const getOptionProps = instance => {
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
const getComponent = (instance, prop = 'default', options = instance, execute = true) => {
|
const getComponent = (instance: any, prop = 'default', options = instance, execute = true) => {
|
||||||
let com = undefined;
|
let com = undefined;
|
||||||
if (instance.$) {
|
if (instance.$) {
|
||||||
const temp = instance[prop];
|
const temp = instance[prop];
|
||||||
|
|
@ -179,94 +149,13 @@ const getComponent = (instance, prop = 'default', options = instance, execute =
|
||||||
}
|
}
|
||||||
return com;
|
return com;
|
||||||
};
|
};
|
||||||
const getComponentFromProp = (instance, prop, options = instance, execute = true) => {
|
|
||||||
if (instance.$createElement) {
|
|
||||||
// const h = instance.$createElement;
|
|
||||||
const temp = instance[prop];
|
|
||||||
if (temp !== undefined) {
|
|
||||||
return typeof temp === 'function' && execute ? temp(h, options) : temp;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
(instance.$scopedSlots[prop] && execute && instance.$scopedSlots[prop](options)) ||
|
|
||||||
instance.$scopedSlots[prop] ||
|
|
||||||
instance.$slots[prop] ||
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// const h = instance.context.$createElement;
|
|
||||||
const temp = getPropsData(instance)[prop];
|
|
||||||
if (temp !== undefined) {
|
|
||||||
return typeof temp === 'function' && execute ? temp(h, options) : temp;
|
|
||||||
}
|
|
||||||
const slotScope = getScopedSlots(instance)[prop];
|
|
||||||
if (slotScope !== undefined) {
|
|
||||||
return typeof slotScope === 'function' && execute ? slotScope(h, options) : slotScope;
|
|
||||||
}
|
|
||||||
const slotsProp = [];
|
|
||||||
const componentOptions = instance.componentOptions || {};
|
|
||||||
(componentOptions.children || []).forEach(child => {
|
|
||||||
if (child.data && child.data.slot === prop) {
|
|
||||||
if (child.data.attrs) {
|
|
||||||
delete child.data.attrs.slot;
|
|
||||||
}
|
|
||||||
if (child.tag === 'template') {
|
|
||||||
slotsProp.push(child.children);
|
|
||||||
} else {
|
|
||||||
slotsProp.push(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return slotsProp.length ? slotsProp : undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAllProps = ele => {
|
const getKey = (ele: any) => {
|
||||||
let props = getOptionProps(ele);
|
const key = ele.key;
|
||||||
if (ele.$) {
|
|
||||||
props = { ...props, ...this.$attrs };
|
|
||||||
} else {
|
|
||||||
props = { ...ele.props, ...props };
|
|
||||||
}
|
|
||||||
return props;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPropsData = ins => {
|
|
||||||
const vnode = ins.$ ? ins.$ : ins;
|
|
||||||
const res = {};
|
|
||||||
const originProps = vnode.props || {};
|
|
||||||
const props = {};
|
|
||||||
Object.keys(originProps).forEach(key => {
|
|
||||||
props[camelize(key)] = originProps[key];
|
|
||||||
});
|
|
||||||
const options = isPlainObject(vnode.type) ? vnode.type.props : {};
|
|
||||||
options &&
|
|
||||||
Object.keys(options).forEach(k => {
|
|
||||||
const v = resolvePropValue(options, props, k, props[k]);
|
|
||||||
if (k in props) {
|
|
||||||
// 仅包含 props,不包含默认值
|
|
||||||
res[k] = v;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { ...props, ...res }; // 合并事件、未声明属性等
|
|
||||||
};
|
|
||||||
const getValueByProp = (ele, prop) => {
|
|
||||||
return getPropsData(ele)[prop];
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAttrs = ele => {
|
|
||||||
let data = ele.data;
|
|
||||||
if (ele.$vnode) {
|
|
||||||
data = ele.$vnode.data;
|
|
||||||
}
|
|
||||||
return data ? data.attrs || {} : {};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getKey = ele => {
|
|
||||||
let key = ele.key;
|
|
||||||
return key;
|
return key;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getEvents(ele = {}, on = true) {
|
export function getEvents(ele: any = {}, on = true) {
|
||||||
let props = {};
|
let props = {};
|
||||||
if (ele.$) {
|
if (ele.$) {
|
||||||
props = { ...props, ...ele.$attrs };
|
props = { ...props, ...ele.$attrs };
|
||||||
|
|
@ -276,27 +165,9 @@ export function getEvents(ele = {}, on = true) {
|
||||||
return splitAttrs(props)[on ? 'onEvents' : 'events'];
|
return splitAttrs(props)[on ? 'onEvents' : 'events'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEvent(child, event) {
|
export function getClass(ele: any) {
|
||||||
return child.props && child.props[event];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取 xxx.native 或者 原生标签 事件
|
|
||||||
export function getDataEvents(child) {
|
|
||||||
let events = {};
|
|
||||||
if (child.data && child.data.on) {
|
|
||||||
events = child.data.on;
|
|
||||||
}
|
|
||||||
return { ...events };
|
|
||||||
}
|
|
||||||
|
|
||||||
// use getListeners instead this.$listeners
|
|
||||||
// https://github.com/vueComponent/ant-design-vue/issues/1705
|
|
||||||
export function getListeners(context) {
|
|
||||||
return (context.$vnode ? context.$vnode.componentOptions.listeners : context.$listeners) || {};
|
|
||||||
}
|
|
||||||
export function getClass(ele) {
|
|
||||||
const props = (isVNode(ele) ? ele.props : ele.$attrs) || {};
|
const props = (isVNode(ele) ? ele.props : ele.$attrs) || {};
|
||||||
let tempCls = props.class || {};
|
const tempCls = props.class || {};
|
||||||
let cls = {};
|
let cls = {};
|
||||||
if (typeof tempCls === 'string') {
|
if (typeof tempCls === 'string') {
|
||||||
tempCls.split(' ').forEach(c => {
|
tempCls.split(' ').forEach(c => {
|
||||||
|
|
@ -313,7 +184,7 @@ export function getClass(ele) {
|
||||||
}
|
}
|
||||||
return cls;
|
return cls;
|
||||||
}
|
}
|
||||||
export function getStyle(ele, camel) {
|
export function getStyle(ele: any, camel?: boolean) {
|
||||||
const props = (isVNode(ele) ? ele.props : ele.$attrs) || {};
|
const props = (isVNode(ele) ? ele.props : ele.$attrs) || {};
|
||||||
let style = props.style || {};
|
let style = props.style || {};
|
||||||
if (typeof style === 'string') {
|
if (typeof style === 'string') {
|
||||||
|
|
@ -327,19 +198,19 @@ export function getStyle(ele, camel) {
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getComponentName(opts) {
|
export function getComponentName(opts: any) {
|
||||||
return opts && (opts.Ctor.options.name || opts.tag);
|
return opts && (opts.Ctor.options.name || opts.tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isFragment(c) {
|
export function isFragment(c: any) {
|
||||||
return c.length === 1 && c[0].type === Fragment;
|
return c.length === 1 && c[0].type === Fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEmptyContent(c) {
|
export function isEmptyContent(c: any) {
|
||||||
return c === undefined || c === null || c === '' || (Array.isArray(c) && c.length === 0);
|
return c === undefined || c === null || c === '' || (Array.isArray(c) && c.length === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEmptyElement(c) {
|
export function isEmptyElement(c: any) {
|
||||||
return (
|
return (
|
||||||
c &&
|
c &&
|
||||||
(c.type === Comment ||
|
(c.type === Comment ||
|
||||||
|
|
@ -348,11 +219,11 @@ export function isEmptyElement(c) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEmptySlot(c) {
|
export function isEmptySlot(c: any) {
|
||||||
return !c || c().every(isEmptyElement);
|
return !c || c().every(isEmptyElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isStringElement(c) {
|
export function isStringElement(c: any) {
|
||||||
return c && c.type === Text;
|
return c && c.type === Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -370,7 +241,7 @@ export function filterEmpty(children = []) {
|
||||||
return res.filter(c => !isEmptyElement(c));
|
return res.filter(c => !isEmptyElement(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterEmptyWithUndefined(children) {
|
export function filterEmptyWithUndefined(children: any[]) {
|
||||||
if (children) {
|
if (children) {
|
||||||
const coms = filterEmpty(children);
|
const coms = filterEmpty(children);
|
||||||
return coms.length ? coms : undefined;
|
return coms.length ? coms : undefined;
|
||||||
|
|
@ -379,34 +250,18 @@ export function filterEmptyWithUndefined(children) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mergeProps() {
|
function isValidElement(element: any) {
|
||||||
const args = [].slice.call(arguments, 0);
|
|
||||||
const props = {};
|
|
||||||
args.forEach((p = {}) => {
|
|
||||||
for (const [k, v] of Object.entries(p)) {
|
|
||||||
props[k] = props[k] || {};
|
|
||||||
if (isPlainObject(v)) {
|
|
||||||
Object.assign(props[k], v);
|
|
||||||
} else {
|
|
||||||
props[k] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValidElement(element) {
|
|
||||||
if (Array.isArray(element) && element.length === 1) {
|
if (Array.isArray(element) && element.length === 1) {
|
||||||
element = element[0];
|
element = element[0];
|
||||||
}
|
}
|
||||||
return element && element.__v_isVNode && typeof element.type !== 'symbol'; // remove text node
|
return element && element.__v_isVNode && typeof element.type !== 'symbol'; // remove text node
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPropsSlot(slots, props, prop = 'default') {
|
function getPropsSlot(slots: any, props: any, prop = 'default') {
|
||||||
return props[prop] ?? slots[prop]?.();
|
return props[prop] ?? slots[prop]?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTextFromElement = ele => {
|
export const getTextFromElement = (ele: any) => {
|
||||||
if (isValidElement(ele) && isStringElement(ele[0])) {
|
if (isValidElement(ele) && isStringElement(ele[0])) {
|
||||||
return ele[0].children;
|
return ele[0].children;
|
||||||
}
|
}
|
||||||
|
|
@ -417,21 +272,12 @@ export {
|
||||||
hasProp,
|
hasProp,
|
||||||
getOptionProps,
|
getOptionProps,
|
||||||
getComponent,
|
getComponent,
|
||||||
getComponentFromProp,
|
|
||||||
getSlotOptions,
|
|
||||||
slotHasProp,
|
|
||||||
getPropsData,
|
|
||||||
getKey,
|
getKey,
|
||||||
getAttrs,
|
|
||||||
getValueByProp,
|
|
||||||
parseStyleText,
|
parseStyleText,
|
||||||
initDefaultProps,
|
initDefaultProps,
|
||||||
isValidElement,
|
isValidElement,
|
||||||
camelize,
|
camelize,
|
||||||
getSlots,
|
|
||||||
getSlot,
|
getSlot,
|
||||||
getAllProps,
|
|
||||||
getAllChildren,
|
|
||||||
findDOMNode,
|
findDOMNode,
|
||||||
flattenChildren,
|
flattenChildren,
|
||||||
getPropsSlot,
|
getPropsSlot,
|
||||||
|
|
@ -1,26 +1,35 @@
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import type { GlobalToken } from '../theme/interface';
|
||||||
|
import { useToken } from '../theme/internal';
|
||||||
|
|
||||||
export type Breakpoint = 'xxxl' | 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
|
export type Breakpoint = 'xxxl' | 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
|
||||||
export type BreakpointMap = Record<Breakpoint, string>;
|
export type BreakpointMap = Record<Breakpoint, string>;
|
||||||
export type ScreenMap = Partial<Record<Breakpoint, boolean>>;
|
export type ScreenMap = Partial<Record<Breakpoint, boolean>>;
|
||||||
export type ScreenSizeMap = Partial<Record<Breakpoint, number>>;
|
export type ScreenSizeMap = Partial<Record<Breakpoint, number>>;
|
||||||
|
|
||||||
export const responsiveArray: Breakpoint[] = ['xxxl', 'xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
|
export const responsiveArray: Breakpoint[] = ['xxxl', 'xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
|
||||||
|
|
||||||
export const responsiveMap: BreakpointMap = {
|
|
||||||
xs: '(max-width: 575px)',
|
|
||||||
sm: '(min-width: 576px)',
|
|
||||||
md: '(min-width: 768px)',
|
|
||||||
lg: '(min-width: 992px)',
|
|
||||||
xl: '(min-width: 1200px)',
|
|
||||||
xxl: '(min-width: 1600px)',
|
|
||||||
xxxl: '(min-width: 2000px)',
|
|
||||||
};
|
|
||||||
|
|
||||||
type SubscribeFunc = (screens: ScreenMap) => void;
|
type SubscribeFunc = (screens: ScreenMap) => void;
|
||||||
const subscribers = new Map<Number, SubscribeFunc>();
|
|
||||||
let subUid = -1;
|
|
||||||
let screens = {};
|
|
||||||
|
|
||||||
const responsiveObserve = {
|
const getResponsiveMap = (token: GlobalToken): BreakpointMap => ({
|
||||||
|
xs: `(max-width: ${token.screenXSMax}px)`,
|
||||||
|
sm: `(min-width: ${token.screenSM}px)`,
|
||||||
|
md: `(min-width: ${token.screenMD}px)`,
|
||||||
|
lg: `(min-width: ${token.screenLG}px)`,
|
||||||
|
xl: `(min-width: ${token.screenXL}px)`,
|
||||||
|
xxl: `(min-width: ${token.screenXXL}px)`,
|
||||||
|
xxxl: `{min-width: ${token.screenXXXL}px}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function useResponsiveObserver() {
|
||||||
|
const [, token] = useToken();
|
||||||
|
|
||||||
|
return computed(() => {
|
||||||
|
const responsiveMap: BreakpointMap = getResponsiveMap(token.value);
|
||||||
|
const subscribers = new Map<Number, SubscribeFunc>();
|
||||||
|
let subUid = -1;
|
||||||
|
let screens = {};
|
||||||
|
|
||||||
|
return {
|
||||||
matchHandlers: {} as {
|
matchHandlers: {} as {
|
||||||
[prop: string]: {
|
[prop: string]: {
|
||||||
mql: MediaQueryList;
|
mql: MediaQueryList;
|
||||||
|
|
@ -39,8 +48,8 @@ const responsiveObserve = {
|
||||||
func(screens);
|
func(screens);
|
||||||
return subUid;
|
return subUid;
|
||||||
},
|
},
|
||||||
unsubscribe(token: number) {
|
unsubscribe(paramToken: number) {
|
||||||
subscribers.delete(token);
|
subscribers.delete(paramToken);
|
||||||
if (!subscribers.size) this.unregister();
|
if (!subscribers.size) this.unregister();
|
||||||
},
|
},
|
||||||
unregister() {
|
unregister() {
|
||||||
|
|
@ -70,6 +79,7 @@ const responsiveObserve = {
|
||||||
listener(mql);
|
listener(mql);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
responsiveMap,
|
||||||
|
};
|
||||||
export default responsiveObserve;
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import raf from './raf';
|
import raf from './raf';
|
||||||
import getScroll, { isWindow } from './getScroll';
|
|
||||||
import { easeInOutCubic } from './easings';
|
import { easeInOutCubic } from './easings';
|
||||||
|
import getScroll, { isWindow } from './getScroll';
|
||||||
|
|
||||||
interface ScrollToOptions {
|
interface ScrollToOptions {
|
||||||
/** Scroll container, default as window */
|
/** Scroll container, default as window */
|
||||||
|
|
@ -22,9 +22,9 @@ export default function scrollTo(y: number, options: ScrollToOptions = {}) {
|
||||||
const time = timestamp - startTime;
|
const time = timestamp - startTime;
|
||||||
const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration);
|
const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration);
|
||||||
if (isWindow(container)) {
|
if (isWindow(container)) {
|
||||||
(container as Window).scrollTo(window.pageXOffset, nextScrollTop);
|
(container as Window).scrollTo(window.scrollX, nextScrollTop);
|
||||||
} else if (container instanceof HTMLDocument || container.constructor.name === 'HTMLDocument') {
|
} else if (container instanceof Document) {
|
||||||
(container as HTMLDocument).documentElement.scrollTop = nextScrollTop;
|
(container as Document).documentElement.scrollTop = nextScrollTop;
|
||||||
} else {
|
} else {
|
||||||
(container as HTMLElement).scrollTop = nextScrollTop;
|
(container as HTMLElement).scrollTop = nextScrollTop;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { toRaw } from 'vue';
|
import { toRaw } from 'vue';
|
||||||
|
|
||||||
function shallowEqual(objA, objB, compare, compareContext) {
|
function shallowEqual(objA: any, objB: any, compare?: any, compareContext?: any) {
|
||||||
let ret = compare ? compare.call(compareContext, objA, objB) : void 0;
|
let ret = compare ? compare.call(compareContext, objA, objB) : void 0;
|
||||||
|
|
||||||
if (ret !== void 0) {
|
if (ret !== void 0) {
|
||||||
|
|
@ -45,6 +45,6 @@ function shallowEqual(objA, objB, compare, compareContext) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function (value, other, customizer, thisArg) {
|
export default function (value: any, other: any) {
|
||||||
return shallowEqual(toRaw(value), toRaw(other), customizer, thisArg);
|
return shallowEqual(toRaw(value), toRaw(other));
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,82 @@
|
||||||
|
// import { StyleProvider } from '../../cssinjs';
|
||||||
|
import { extractStyle } from '../index';
|
||||||
|
import { ConfigProvider } from '../../../components';
|
||||||
|
import { theme } from '../../../index';
|
||||||
|
|
||||||
|
const testGreenColor = '#008000';
|
||||||
|
describe('Static-Style-Extract', () => {
|
||||||
|
it('should extract static styles', () => {
|
||||||
|
const cssText = extractStyle();
|
||||||
|
expect(cssText).not.toContain(testGreenColor);
|
||||||
|
expect(cssText).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
it('should extract static styles with customTheme', () => {
|
||||||
|
const cssText = extractStyle(node => {
|
||||||
|
return (
|
||||||
|
<ConfigProvider
|
||||||
|
theme={{
|
||||||
|
token: {
|
||||||
|
colorPrimary: testGreenColor,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{node}
|
||||||
|
</ConfigProvider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(cssText).toContain(testGreenColor);
|
||||||
|
expect(cssText).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extract static styles with customTheme and customStyle', () => {
|
||||||
|
const cssText = extractStyle(node => {
|
||||||
|
return (
|
||||||
|
<ConfigProvider
|
||||||
|
theme={{
|
||||||
|
algorithm: theme.darkAlgorithm,
|
||||||
|
token: {
|
||||||
|
colorPrimary: testGreenColor,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{node}
|
||||||
|
</ConfigProvider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(cssText).toContain('#037003');
|
||||||
|
expect(cssText).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
// it('with custom hashPriority', () => {
|
||||||
|
// const cssText = extractStyle(
|
||||||
|
// (node) => (
|
||||||
|
// <StyleProvider hashPriority='high'>
|
||||||
|
// <ConfigProvider
|
||||||
|
// theme={{
|
||||||
|
// token: {
|
||||||
|
// colorPrimary: testGreenColor,
|
||||||
|
// },
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// {node}
|
||||||
|
// </ConfigProvider>
|
||||||
|
// </StyleProvider>
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// expect(cssText).toContain(testGreenColor);
|
||||||
|
// expect(cssText).not.toContain(':where');
|
||||||
|
// expect(cssText).toMatchSnapshot();
|
||||||
|
//
|
||||||
|
// const cssText2 = extractStyle((node) => (
|
||||||
|
// <ConfigProvider
|
||||||
|
// theme={{
|
||||||
|
// token: {
|
||||||
|
// colorPrimary: testGreenColor,
|
||||||
|
// },
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// {node}
|
||||||
|
// </ConfigProvider>
|
||||||
|
// ));
|
||||||
|
// expect(cssText2).toContain(':where');
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
import { createCache, extractStyle as extStyle, StyleProvider } from '../cssinjs';
|
||||||
|
import * as antd from '../../components';
|
||||||
|
import { renderToString } from 'vue/server-renderer';
|
||||||
|
import type { CustomRender } from './interface';
|
||||||
|
const blackList: string[] = [
|
||||||
|
'ConfigProvider',
|
||||||
|
'Grid',
|
||||||
|
'Tour',
|
||||||
|
'SelectOptGroup',
|
||||||
|
'SelectOption',
|
||||||
|
'MentionsOption',
|
||||||
|
'TreeNode',
|
||||||
|
'TreeSelectNode',
|
||||||
|
'LocaleProvider',
|
||||||
|
];
|
||||||
|
|
||||||
|
const pickerMap = {
|
||||||
|
MonthPicker: 'month',
|
||||||
|
WeekPicker: 'week',
|
||||||
|
QuarterPicker: 'quarter',
|
||||||
|
};
|
||||||
|
|
||||||
|
const compChildNameMap = {
|
||||||
|
MenuDivider: 'Menu',
|
||||||
|
MenuItem: 'Menu',
|
||||||
|
MenuItemGroup: 'Menu',
|
||||||
|
SubMenu: 'Menu',
|
||||||
|
TableColumn: 'Table',
|
||||||
|
TableColumnGroup: 'Table',
|
||||||
|
TableSummary: 'Table',
|
||||||
|
TableSummaryRow: 'Table',
|
||||||
|
TableSummaryCell: 'Table',
|
||||||
|
TabPane: 'Tabs',
|
||||||
|
TimelineItem: 'Timeline',
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultNode = () => (
|
||||||
|
<>
|
||||||
|
{Object.keys(antd)
|
||||||
|
.filter(name => !blackList.includes(name) && name[0] === name[0].toUpperCase())
|
||||||
|
.map(compName => {
|
||||||
|
const Comp = antd[compName];
|
||||||
|
if (compName === 'Dropdown') {
|
||||||
|
return (
|
||||||
|
<Comp key={compName} menu={{ items: [] }}>
|
||||||
|
<div />
|
||||||
|
</Comp>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (compName === 'Anchor') {
|
||||||
|
return <Comp key={compName} items={[]} />;
|
||||||
|
}
|
||||||
|
if (compName in pickerMap) {
|
||||||
|
const Comp = antd['DatePicker'];
|
||||||
|
const type = pickerMap[compName];
|
||||||
|
return <Comp key={compName} picker={type} />;
|
||||||
|
}
|
||||||
|
if (compName in compChildNameMap) {
|
||||||
|
const ParentComp = antd[compChildNameMap[compName]];
|
||||||
|
return (
|
||||||
|
<ParentComp>
|
||||||
|
<Comp />
|
||||||
|
</ParentComp>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (compName === 'QRCode' || compName === 'Segmented') {
|
||||||
|
return (
|
||||||
|
<Comp key={compName} value={''}>
|
||||||
|
<div />
|
||||||
|
</Comp>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <Comp key={compName} />;
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export function extractStyle(customTheme?: CustomRender): string {
|
||||||
|
const cache = createCache();
|
||||||
|
renderToString(
|
||||||
|
<StyleProvider cache={cache}>
|
||||||
|
{customTheme ? customTheme(defaultNode()) : defaultNode()}
|
||||||
|
</StyleProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Grab style from cache
|
||||||
|
const styleText = extStyle(cache, true);
|
||||||
|
|
||||||
|
return styleText;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue