Compare commits
1774 Commits
epic/pac-m
...
next
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09ace44c17 | ||
|
|
f3de45803f | ||
|
|
ede1f0725d | ||
|
|
56b1196265 | ||
|
|
7e5f18ea97 | ||
|
|
c748721f12 | ||
|
|
6e5a720f51 | ||
|
|
f739121aaf | ||
|
|
5fb3625ee5 | ||
|
|
da206a8787 | ||
|
|
2b69fa74a5 | ||
|
|
d1b6628d59 | ||
|
|
4dca4185e7 | ||
|
|
1e6d468959 | ||
|
|
00b8150f3e | ||
|
|
3d395653fc | ||
|
|
b3844ef77f | ||
|
|
a50648c6e8 | ||
|
|
b6b82fb0ac | ||
|
|
fa073e8d21 | ||
|
|
e0f6ff11ec | ||
|
|
895fb304e0 | ||
|
|
079c09b9a3 | ||
|
|
fa6ccc1ee2 | ||
|
|
579e86fc49 | ||
|
|
2baebca230 | ||
|
|
c0ced13f06 | ||
|
|
8ae90ef2b2 | ||
|
|
5fb6faf527 | ||
|
|
fa558eeda0 | ||
|
|
ed3cb6d782 | ||
|
|
09c8f02f79 | ||
|
|
d6fe9f7819 | ||
|
|
200b02f023 | ||
|
|
1429095eed | ||
|
|
9706964511 | ||
|
|
bb0a961784 | ||
|
|
e2f2a8a0d3 | ||
|
|
86293cc3e1 | ||
|
|
8fb521d9ef | ||
|
|
2491ca45ac | ||
|
|
411d385d65 | ||
|
|
634828d89a | ||
|
|
17fd3d238c | ||
|
|
ab0f2a1411 | ||
|
|
4069360242 | ||
|
|
8c51c5b933 | ||
|
|
5a6ce1c4d3 | ||
|
|
e695b54dcd | ||
|
|
9a3fc59e6a | ||
|
|
dfaa959951 | ||
|
|
9d6ded49b9 | ||
|
|
3451255454 | ||
|
|
20628a98eb | ||
|
|
3d4b3ada27 | ||
|
|
0bead172fe | ||
|
|
9ca3e6f39c | ||
|
|
e2c7286ccc | ||
|
|
46812e08c5 | ||
|
|
0f601df74a | ||
|
|
eb2410ff88 | ||
|
|
479fff4b61 | ||
|
|
9c7b341889 | ||
|
|
d09973b571 | ||
|
|
a03da137e3 | ||
|
|
d8b0447151 | ||
|
|
6f0bd4e872 | ||
|
|
55973a7eff | ||
|
|
e9aa72cc87 | ||
|
|
9716ecec41 | ||
|
|
776b980ce5 | ||
|
|
02f58d3c8b | ||
|
|
fba18efad6 | ||
|
|
4a7f9dfe00 | ||
|
|
1fbde40bab | ||
|
|
14bd0459df | ||
|
|
e4a4d2eb18 | ||
|
|
a465f0348f | ||
|
|
5e1eba1751 | ||
|
|
00f79f3749 | ||
|
|
788804774f | ||
|
|
986b8c4d99 | ||
|
|
d18d44eaa6 | ||
|
|
29422d196a | ||
|
|
d68351b8b0 | ||
|
|
0d2b5c7381 | ||
|
|
c221bdd6fc | ||
|
|
a4b2fc7c13 | ||
|
|
887797e3b8 | ||
|
|
246a64ffa5 | ||
|
|
ac5ae9e193 | ||
|
|
2a22696cc9 | ||
|
|
85949f5502 | ||
|
|
2e0d72ca92 | ||
|
|
4c9aed87a0 | ||
|
|
eb9bda5e28 | ||
|
|
8d590d2597 | ||
|
|
ef0b9ff742 | ||
|
|
f906b07d98 | ||
|
|
1bfebe36a3 | ||
|
|
2ae0138d00 | ||
|
|
3612f72a3f | ||
|
|
0f6cf33020 | ||
|
|
95fb68c7fa | ||
|
|
7d3c676ea8 | ||
|
|
9f02a55ebb | ||
|
|
0a70f1b7ab | ||
|
|
122ce96d26 | ||
|
|
6dd1d6263c | ||
|
|
23793333f0 | ||
|
|
bac343f900 | ||
|
|
205e3bf131 | ||
|
|
927562127f | ||
|
|
3a23e48473 | ||
|
|
925cc2816f | ||
|
|
03949b8673 | ||
|
|
96733925d6 | ||
|
|
d792ac00e9 | ||
|
|
3bf62371d0 | ||
|
|
22e7372a63 | ||
|
|
a4788cd01d | ||
|
|
7675aeb788 | ||
|
|
24f6508716 | ||
|
|
322491d84d | ||
|
|
f89638bc6d | ||
|
|
951dbabfc4 | ||
|
|
096aa1f22d | ||
|
|
ee5f7cddef | ||
|
|
0aefdc20cd | ||
|
|
0d5d7b5e73 | ||
|
|
ac97c7087a | ||
|
|
eeb41f4d99 | ||
|
|
b0680f19a9 | ||
|
|
1101a3cf07 | ||
|
|
3c0924fac2 | ||
|
|
d490d04ebc | ||
|
|
52b5f557e0 | ||
|
|
dd1c689e33 | ||
|
|
f398899991 | ||
|
|
8fc0ee89ff | ||
|
|
ed17964f10 | ||
|
|
64ce424c42 | ||
|
|
51253650e1 | ||
|
|
0b45173192 | ||
|
|
0eb8eaea00 | ||
|
|
334e3361b4 | ||
|
|
5e482739a9 | ||
|
|
c0b18f6580 | ||
|
|
c18df17429 | ||
|
|
2ec957ff76 | ||
|
|
1fd68dfb3c | ||
|
|
0b5689de62 | ||
|
|
8ffd9991db | ||
|
|
566f98359c | ||
|
|
6387dffe75 | ||
|
|
bb747d50dd | ||
|
|
2704ef7197 | ||
|
|
336a3acdd0 | ||
|
|
e68640b8ca | ||
|
|
487b629995 | ||
|
|
255647d2a9 | ||
|
|
53f1b3615d | ||
|
|
d00d8bac4e | ||
|
|
87864924e1 | ||
|
|
ce3496a621 | ||
|
|
cf80b57a79 | ||
|
|
694a9eccb9 | ||
|
|
502df6ee9c | ||
|
|
44142fb56d | ||
|
|
7e00d2b02e | ||
|
|
fcf37f83a1 | ||
|
|
0beceff73f | ||
|
|
afe60eae69 | ||
|
|
b4f45f4ff1 | ||
|
|
fd4cded708 | ||
|
|
4b2b72e822 | ||
|
|
56657ebcfc | ||
|
|
844015df7b | ||
|
|
d5da2e2db5 | ||
|
|
efbc404524 | ||
|
|
b6afa148ae | ||
|
|
7c3795897c | ||
|
|
986e52f977 | ||
|
|
6dce88429a | ||
|
|
04497cfd13 | ||
|
|
75116a5b0c | ||
|
|
5e192023b4 | ||
|
|
26b29189db | ||
|
|
ac20e495d9 | ||
|
|
4ac31e06f3 | ||
|
|
4993b1034f | ||
|
|
b3b93091f7 | ||
|
|
8cf20d9938 | ||
|
|
63296b7ed5 | ||
|
|
0e56ed0cbb | ||
|
|
e70cb5c66d | ||
|
|
913abd0db1 | ||
|
|
d2798a96da | ||
|
|
42729153db | ||
|
|
c2df5ca1ea | ||
|
|
c49813c7c1 | ||
|
|
f63dfbfff0 | ||
|
|
9a7947debd | ||
|
|
d2e1653519 | ||
|
|
5b7004e72d | ||
|
|
7ccfed9e93 | ||
|
|
4f1de3918b | ||
|
|
adaea2fa5f | ||
|
|
e016e4bd48 | ||
|
|
5e0b5f3fe6 | ||
|
|
493e1c06b9 | ||
|
|
8b763d0392 | ||
|
|
9d82749abb | ||
|
|
3e3e5276a6 | ||
|
|
a0a47b1bdf | ||
|
|
fa51cff947 | ||
|
|
fd08d4f227 | ||
|
|
7008c0cef7 | ||
|
|
e9ce8659f6 | ||
|
|
325d6f211b | ||
|
|
bd1570ec76 | ||
|
|
406f3a0e81 | ||
|
|
08babbce6d | ||
|
|
293b86705a | ||
|
|
fe5ab48c06 | ||
|
|
35e9dfe88d | ||
|
|
44567ec967 | ||
|
|
bfdd0bd6d0 | ||
|
|
f3f5d40b7b | ||
|
|
3d75d6b59c | ||
|
|
f30801ab66 | ||
|
|
f92ef1f74e | ||
|
|
da27a8dc74 | ||
|
|
437e0d9aec | ||
|
|
583d9c0616 | ||
|
|
cb29033eaf | ||
|
|
c02ac306af | ||
|
|
56d678b504 | ||
|
|
b1864eb93d | ||
|
|
d2b5613e85 | ||
|
|
f7ca634822 | ||
|
|
35d33c89b9 | ||
|
|
094bd478ff | ||
|
|
0b619a99f1 | ||
|
|
89cf48c865 | ||
|
|
b7a6ebd228 | ||
|
|
6c8bbd51d1 | ||
|
|
6085b9698c | ||
|
|
17ee36175b | ||
|
|
3c7bb71a59 | ||
|
|
d66b552962 | ||
|
|
6f6e23c78c | ||
|
|
310f7a8c5d | ||
|
|
9a7b258108 | ||
|
|
e10aba0ed1 | ||
|
|
1ea5dae9ad | ||
|
|
21310bd367 | ||
|
|
c1478e5865 | ||
|
|
9b8433c996 | ||
|
|
57dac67aab | ||
|
|
441bfd7b72 | ||
|
|
7150c59334 | ||
|
|
bd2a3c3b64 | ||
|
|
11519625ed | ||
|
|
f19848c11e | ||
|
|
36b21b0be7 | ||
|
|
fe2c2ab7af | ||
|
|
b98b9baba4 | ||
|
|
7b18be876b | ||
|
|
c9e6895ef7 | ||
|
|
64c8647dee | ||
|
|
9e9a00547a | ||
|
|
f747483e32 | ||
|
|
8719bbc88b | ||
|
|
0ff5e7fb7a | ||
|
|
2a52b2766f | ||
|
|
1e8bbc3b06 | ||
|
|
1ef9cb9601 | ||
|
|
5b54410212 | ||
|
|
f621fbb224 | ||
|
|
f5624fbf4a | ||
|
|
f65bc3918e | ||
|
|
f49c10b05b | ||
|
|
3b6c018fed | ||
|
|
c10e1e77c9 | ||
|
|
a1e879035c | ||
|
|
d2625fccab | ||
|
|
29c8ad08bb | ||
|
|
0f68e6f0dd | ||
|
|
4dca2b9541 | ||
|
|
36ccaa4aaa | ||
|
|
c86d7635aa | ||
|
|
d84e842a4e | ||
|
|
fb8c06235f | ||
|
|
f6a10e9dda | ||
|
|
5ce34677ed | ||
|
|
8ebc839dfd | ||
|
|
2779eb1753 | ||
|
|
341ca809e9 | ||
|
|
7232ddee5d | ||
|
|
f63001f18e | ||
|
|
8b831f6ece | ||
|
|
4dee3d0f2d | ||
|
|
10a78d99af | ||
|
|
99d9024339 | ||
|
|
313e31a3f2 | ||
|
|
4eeabc57e5 | ||
|
|
c8c0d0f848 | ||
|
|
e8687b895d | ||
|
|
7743b670ed | ||
|
|
88e99d7cd3 | ||
|
|
c0d18ab9f0 | ||
|
|
6576f67cfa | ||
|
|
057ff5fb31 | ||
|
|
1dac76a35e | ||
|
|
e7f2962984 | ||
|
|
956dc9bdd9 | ||
|
|
62020be8c1 | ||
|
|
429cf68301 | ||
|
|
dd77663a75 | ||
|
|
fa9f18f002 | ||
|
|
c2e2e1afcc | ||
|
|
2628f4ff7c | ||
|
|
16722a191d | ||
|
|
21243729c6 | ||
|
|
1581b99796 | ||
|
|
103cbc439e | ||
|
|
3a40ffe58a | ||
|
|
453e8ff501 | ||
|
|
dc556e5379 | ||
|
|
c11d6129a3 | ||
|
|
dd5c32680a | ||
|
|
81996cbc63 | ||
|
|
d20945d78b | ||
|
|
07327be95e | ||
|
|
b99d7771dc | ||
|
|
10cc9bdc68 | ||
|
|
bc598dad92 | ||
|
|
ff61ac002f | ||
|
|
63ec9d5bc1 | ||
|
|
555327b2fc | ||
|
|
15c6eaec90 | ||
|
|
6f856fbd89 | ||
|
|
df46e1db3b | ||
|
|
d21d829c29 | ||
|
|
2331e88dcf | ||
|
|
c162983ca2 | ||
|
|
0d09037916 | ||
|
|
d2e32a0a53 | ||
|
|
71d45eabbd | ||
|
|
eac60a9022 | ||
|
|
f8dca5d1a8 | ||
|
|
5980b5f843 | ||
|
|
9a3ffb04ba | ||
|
|
04d37224e0 | ||
|
|
f4a63f9e22 | ||
|
|
3ab342ebb6 | ||
|
|
48fe9389c8 | ||
|
|
6b2a081fa0 | ||
|
|
afb2082c79 | ||
|
|
7bdc9a2cc4 | ||
|
|
dead18d23c | ||
|
|
2b37c54d7c | ||
|
|
35b61e5cf3 | ||
|
|
cc33805d27 | ||
|
|
6e548dd85b | ||
|
|
212ca5b0a6 | ||
|
|
b96c3f318b | ||
|
|
b0aba7ce07 | ||
|
|
e39bb856c4 | ||
|
|
23dc884678 | ||
|
|
4e14f25831 | ||
|
|
b15502a01e | ||
|
|
73f6eeacbd | ||
|
|
5921f3eeaa | ||
|
|
35338ef643 | ||
|
|
5c950a7874 | ||
|
|
a7ea989170 | ||
|
|
7253ed8ab0 | ||
|
|
37e82b70fa | ||
|
|
39d1b82634 | ||
|
|
1259a9b78d | ||
|
|
59077e75d2 | ||
|
|
62ddf13a6a | ||
|
|
684bb25244 | ||
|
|
1f2ebf5e7a | ||
|
|
2182d3c692 | ||
|
|
074c7a16d6 | ||
|
|
4130246833 | ||
|
|
bbda731f56 | ||
|
|
6492ee7728 | ||
|
|
73e1c04b37 | ||
|
|
49bb6af154 | ||
|
|
9b8d73a2ea | ||
|
|
0b4c1a5934 | ||
|
|
8b17b3d3e0 | ||
|
|
f5d1b74e00 | ||
|
|
40ea444c48 | ||
|
|
403927687e | ||
|
|
6af4e849af | ||
|
|
2c839a4225 | ||
|
|
3703ef26da | ||
|
|
3640b4c6e1 | ||
|
|
27fc269a94 | ||
|
|
a5b2fffb7a | ||
|
|
7de6a676b8 | ||
|
|
3c77d400f8 | ||
|
|
6ee10f44f4 | ||
|
|
6afc6e928c | ||
|
|
f8fcbd60ec | ||
|
|
bdd25571a8 | ||
|
|
38c13640fc | ||
|
|
00a3b1aa8d | ||
|
|
22298781c5 | ||
|
|
fe27710f57 | ||
|
|
d94589d113 | ||
|
|
3508bf6339 | ||
|
|
61e65ffcb9 | ||
|
|
aed28adbe4 | ||
|
|
15b8bde81b | ||
|
|
9ee3fb5d28 | ||
|
|
47aa376c08 | ||
|
|
69ba974a50 | ||
|
|
8dfb411e5e | ||
|
|
a35a8fd2ad | ||
|
|
2503005bbd | ||
|
|
78027170ea | ||
|
|
a20aa48992 | ||
|
|
ac8accd664 | ||
|
|
c571573063 | ||
|
|
e813440315 | ||
|
|
cfc3f181a3 | ||
|
|
7545f04c46 | ||
|
|
38bd6528fe | ||
|
|
2202ea9642 | ||
|
|
58fbcede51 | ||
|
|
971200cc88 | ||
|
|
b75d3b615c | ||
|
|
9d1c47449e | ||
|
|
003fd28cb0 | ||
|
|
2f300d8930 | ||
|
|
f13deb87bb | ||
|
|
deb9fd70b3 | ||
|
|
ff3b3d6558 | ||
|
|
6b3edb8a56 | ||
|
|
6162b8b115 | ||
|
|
cff752b600 | ||
|
|
7892a94b9b | ||
|
|
40a58ff35f | ||
|
|
0f2950c4cc | ||
|
|
b334884f57 | ||
|
|
734417d66b | ||
|
|
2cfd651d2f | ||
|
|
3e2d1b98be | ||
|
|
40f332f37c | ||
|
|
bfda64f690 | ||
|
|
883d6df2ef | ||
|
|
b4240fd321 | ||
|
|
8755a834f6 | ||
|
|
8d905296b8 | ||
|
|
8eba1e5003 | ||
|
|
21aa85acc0 | ||
|
|
404c15b303 | ||
|
|
8a26afc334 | ||
|
|
513a1e35a9 | ||
|
|
09f668fc99 | ||
|
|
d451ba98e5 | ||
|
|
fd287edd56 | ||
|
|
8424b49646 | ||
|
|
fa24c0f70e | ||
|
|
1bba87c66d | ||
|
|
0db9ca12e3 | ||
|
|
041555fe99 | ||
|
|
b41dbd2de7 | ||
|
|
7c6f31e0c7 | ||
|
|
9e84274a93 | ||
|
|
2b3803f91e | ||
|
|
faed8da3cd | ||
|
|
17cf902f53 | ||
|
|
8214ff6b2d | ||
|
|
c9979e15f8 | ||
|
|
fcfe2bde7d | ||
|
|
59dcaaff83 | ||
|
|
5bad30ec30 | ||
|
|
87c1762146 | ||
|
|
899edd1d5e | ||
|
|
872a110b1e | ||
|
|
07fe6d598e | ||
|
|
79bafc513a | ||
|
|
1d03f7bee0 | ||
|
|
a9bf1bd838 | ||
|
|
c0ca739366 | ||
|
|
a6745602d6 | ||
|
|
da4f619d95 | ||
|
|
1283a696a5 | ||
|
|
d12b97b0b0 | ||
|
|
e5c2884880 | ||
|
|
1d600a77c4 | ||
|
|
db3c568ba2 | ||
|
|
4bb9805ba6 | ||
|
|
bd935fa8d5 | ||
|
|
c3e582b47b | ||
|
|
4d094a4e19 | ||
|
|
782c404bdf | ||
|
|
f1438981b2 | ||
|
|
18b88c2f5c | ||
|
|
a2d85f49a3 | ||
|
|
be00026cd3 | ||
|
|
58ed88bc5a | ||
|
|
1d14e186f3 | ||
|
|
5f672aabc2 | ||
|
|
db08e12a32 | ||
|
|
e0fc639226 | ||
|
|
e6c662b543 | ||
|
|
d1de9a9a73 | ||
|
|
4931de8eb4 | ||
|
|
71e7227763 | ||
|
|
dd671e15aa | ||
|
|
2daeea0349 | ||
|
|
3cb6625c1d | ||
|
|
c4b5446d01 | ||
|
|
41affca083 | ||
|
|
132dbfabcc | ||
|
|
4fc6224464 | ||
|
|
4921d1c32e | ||
|
|
54d71d2319 | ||
|
|
c1ecca0169 | ||
|
|
d6a91919e0 | ||
|
|
4621094ea1 | ||
|
|
726dc73e2a | ||
|
|
4bfebf3249 | ||
|
|
99ad0abdd3 | ||
|
|
902e2b6367 | ||
|
|
7f3210b12d | ||
|
|
eee63bdecd | ||
|
|
a91fd07ed7 | ||
|
|
34aa8917a6 | ||
|
|
715c2c0def | ||
|
|
13b5385633 | ||
|
|
d25f3748c4 | ||
|
|
b6620ddf7e | ||
|
|
d70d4a91b1 | ||
|
|
bb1f7b2b7a | ||
|
|
9921c17d63 | ||
|
|
7f964f9b56 | ||
|
|
31eeea1630 | ||
|
|
ffda52a7cf | ||
|
|
9be7919c60 | ||
|
|
c30f3c4b09 | ||
|
|
7e8f13b5cb | ||
|
|
97ab986425 | ||
|
|
f1fa7f713e | ||
|
|
3394a95057 | ||
|
|
eed9ddb9b3 | ||
|
|
daf5e7734d | ||
|
|
63133d0e33 | ||
|
|
b2d74145b8 | ||
|
|
17eeccbc5d | ||
|
|
68f645e776 | ||
|
|
b3e4c59197 | ||
|
|
6080a85035 | ||
|
|
f7e088c8df | ||
|
|
80e1c05b37 | ||
|
|
99f0273783 | ||
|
|
8e13683a30 | ||
|
|
fd73542d2c | ||
|
|
bde0ed7403 | ||
|
|
8dc49f7119 | ||
|
|
25cb96aa30 | ||
|
|
360273ac27 | ||
|
|
404d59d9d6 | ||
|
|
ce1ce6caca | ||
|
|
74ecc52a15 | ||
|
|
a64cc60ad5 | ||
|
|
c5f91ec2f7 | ||
|
|
4f9af1e871 | ||
|
|
aeb25f3a7d | ||
|
|
b89ee673e6 | ||
|
|
3c70c44b8a | ||
|
|
d8881c0b1e | ||
|
|
88b6fe2fbe | ||
|
|
6a0725cf00 | ||
|
|
3ff8745910 | ||
|
|
0cb72adb28 | ||
|
|
3d35f5d4e0 | ||
|
|
815cc4220b | ||
|
|
a7caf19b34 | ||
|
|
71c054d6e4 | ||
|
|
e1bf5471bf | ||
|
|
099edc5186 | ||
|
|
e3560dcf98 | ||
|
|
f2bb2c84a0 | ||
|
|
13b3342017 | ||
|
|
d1c1d689ce | ||
|
|
44e5e37a2b | ||
|
|
566aae927d | ||
|
|
7258c001a7 | ||
|
|
7a70940c6a | ||
|
|
45f4edc426 | ||
|
|
da32015f27 | ||
|
|
03d8238edb | ||
|
|
34f8744493 | ||
|
|
fa3fe5f753 | ||
|
|
fc6c7de1fd | ||
|
|
0037712549 | ||
|
|
5301945bfa | ||
|
|
1298651dd8 | ||
|
|
5f9695fde1 | ||
|
|
2eb2597efe | ||
|
|
431e82261b | ||
|
|
df51149d0a | ||
|
|
fba0b11343 | ||
|
|
3618e93490 | ||
|
|
cfa95307d1 | ||
|
|
15344c2a2a | ||
|
|
3974aa5130 | ||
|
|
a6702ad6d2 | ||
|
|
ecf21adddc | ||
|
|
52c24fc3b7 | ||
|
|
d464714d7b | ||
|
|
7d089bbe2f | ||
|
|
71914afc91 | ||
|
|
9d139e3fa0 | ||
|
|
db3039e9fe | ||
|
|
9494b9bb67 | ||
|
|
7e1f4f0351 | ||
|
|
5ebe4f4d3e | ||
|
|
dfb9d53a25 | ||
|
|
c2c1a2ff5b | ||
|
|
ac86c037a1 | ||
|
|
6b07c9a040 | ||
|
|
24a76f6a7c | ||
|
|
89c0667e9c | ||
|
|
434084ea4e | ||
|
|
1738c6345b | ||
|
|
0ac7916a1b | ||
|
|
e7979991e3 | ||
|
|
07f70098f8 | ||
|
|
17146698db | ||
|
|
bf852b1296 | ||
|
|
e367c0ef29 | ||
|
|
01210ef364 | ||
|
|
40648e15fb | ||
|
|
ab67ecfad3 | ||
|
|
a07f6280a3 | ||
|
|
6822b25772 | ||
|
|
200188b0c3 | ||
|
|
bc6fe95f13 | ||
|
|
3a33fa208a | ||
|
|
3ec4e6de07 | ||
|
|
eb07dc1410 | ||
|
|
5bfeb8044e | ||
|
|
aa915c3e28 | ||
|
|
c79457a607 | ||
|
|
419f0610e4 | ||
|
|
7ab5ca8640 | ||
|
|
c39faff936 | ||
|
|
6d31db57f6 | ||
|
|
21ed4f82b0 | ||
|
|
844e374a72 | ||
|
|
e5f4c14608 | ||
|
|
1ad963f5ad | ||
|
|
4476117932 | ||
|
|
e52a7a5ce5 | ||
|
|
fa66f4262d | ||
|
|
a87f3627bb | ||
|
|
06e432589f | ||
|
|
b4c4074ae1 | ||
|
|
19042fcca4 | ||
|
|
8541369ae1 | ||
|
|
31cfdf5704 | ||
|
|
3511a60b93 | ||
|
|
e55e091192 | ||
|
|
09df23dff8 | ||
|
|
f4a88c3b3a | ||
|
|
559efcd1d2 | ||
|
|
e046015ed5 | ||
|
|
44dbdd14cc | ||
|
|
5e3fed605e | ||
|
|
4b4f2247c5 | ||
|
|
eca15dc7fc | ||
|
|
eca444bbaa | ||
|
|
f39308dbc5 | ||
|
|
fc84e1a50d | ||
|
|
ef7d47e2b9 | ||
|
|
a9af3172ad | ||
|
|
cde8bea97a | ||
|
|
33b4045dad | ||
|
|
bc4ad39f2e | ||
|
|
b868b1e8fc | ||
|
|
e916d771b0 | ||
|
|
bd6fe74c7d | ||
|
|
ac7437894a | ||
|
|
55ebe6e20b | ||
|
|
e6388e7671 | ||
|
|
9a21ae6f52 | ||
|
|
cd3386ce78 | ||
|
|
26b9bbb515 | ||
|
|
bda0ec0313 | ||
|
|
2490fbeaca | ||
|
|
297149021e | ||
|
|
c700c3ec09 | ||
|
|
b383d8bf2d | ||
|
|
1dcf895be1 | ||
|
|
19fd55ca97 | ||
|
|
fa094d924d | ||
|
|
2a957e6316 | ||
|
|
90b6a9a8ac | ||
|
|
99fdd90601 | ||
|
|
ecaa461638 | ||
|
|
d3a65ee35d | ||
|
|
8ab1489cc4 | ||
|
|
093c42ce66 | ||
|
|
ed3e7014e3 | ||
|
|
7db62766dc | ||
|
|
2de515bce7 | ||
|
|
80949c2988 | ||
|
|
c7a35afefb | ||
|
|
89dd462720 | ||
|
|
ce40d5e997 | ||
|
|
752cbb2f16 | ||
|
|
2048df8a40 | ||
|
|
beace5a611 | ||
|
|
1a8908acf7 | ||
|
|
812badd721 | ||
|
|
8142da95d8 | ||
|
|
546c9c67b4 | ||
|
|
d5fe23ab39 | ||
|
|
3b10933151 | ||
|
|
51a416718d | ||
|
|
08f327c224 | ||
|
|
b196ccc0bc | ||
|
|
c62eed16aa | ||
|
|
e982c10987 | ||
|
|
23af07bcd5 | ||
|
|
6aca515024 | ||
|
|
7d3f8b175c | ||
|
|
e2acfd106f | ||
|
|
cfd282df4b | ||
|
|
d0567c7ae9 | ||
|
|
f076dd1afa | ||
|
|
83fb761d41 | ||
|
|
fd337dd1ee | ||
|
|
22883021e9 | ||
|
|
02b72f991b | ||
|
|
84734a56b6 | ||
|
|
c159964490 | ||
|
|
5c8e044f4d | ||
|
|
a238880dbe | ||
|
|
307c989e3b | ||
|
|
c33d17b4a2 | ||
|
|
9d3e2bd214 | ||
|
|
6e99787425 | ||
|
|
43a1179513 | ||
|
|
8887056651 | ||
|
|
ec3251d0c6 | ||
|
|
f5ac87c8a3 | ||
|
|
a3d8f712ce | ||
|
|
0fdf4e0f4d | ||
|
|
013cf46be8 | ||
|
|
cbd74eded2 | ||
|
|
960c6d8da3 | ||
|
|
ef81c77c60 | ||
|
|
01918ca784 | ||
|
|
b0bb014167 | ||
|
|
9f12c1d9ab | ||
|
|
4456a8df5c | ||
|
|
8e7816f308 | ||
|
|
3861041bc1 | ||
|
|
e29ae5d18e | ||
|
|
6ef2e92923 | ||
|
|
562fec9ac7 | ||
|
|
b38568f5c5 | ||
|
|
c0012f5b94 | ||
|
|
41ee75bbfe | ||
|
|
54f7916ddb | ||
|
|
e170b488ea | ||
|
|
cd172ede8c | ||
|
|
d1b38af039 | ||
|
|
506fb2588b | ||
|
|
123a8c331f | ||
|
|
1a1847eeb5 | ||
|
|
835326a2db | ||
|
|
1905ca9b77 | ||
|
|
fc3688df97 | ||
|
|
107f074f99 | ||
|
|
5d3e58bde0 | ||
|
|
f5b8f2257d | ||
|
|
52c0fdf6de | ||
|
|
c2c42f56ba | ||
|
|
328d6989d9 | ||
|
|
49465c9a1d | ||
|
|
394a028973 | ||
|
|
431d23d420 | ||
|
|
c230a42053 | ||
|
|
378f100729 | ||
|
|
87314f8864 | ||
|
|
70a8fc6425 | ||
|
|
486b0649e0 | ||
|
|
63a2367cc5 | ||
|
|
995d0f2af4 | ||
|
|
874bb5cbf6 | ||
|
|
15495bd9bd | ||
|
|
f4678e12c6 | ||
|
|
2bf7ebbb71 | ||
|
|
68e53a4ef1 | ||
|
|
0f0b9d50cd | ||
|
|
5d13583da7 | ||
|
|
f99f32d054 | ||
|
|
39acaeba70 | ||
|
|
e87c725e07 | ||
|
|
31a3421709 | ||
|
|
5d1fb7c477 | ||
|
|
13e606c1cb | ||
|
|
42dd64f397 | ||
|
|
53e87e4dcd | ||
|
|
dabaef3fab | ||
|
|
d5939efc30 | ||
|
|
d345878de6 | ||
|
|
609578ff30 | ||
|
|
648137a964 | ||
|
|
cd487bf8be | ||
|
|
239ddcf75d | ||
|
|
ad2e945e44 | ||
|
|
11f7adefc2 | ||
|
|
909986b520 | ||
|
|
8de5fc9580 | ||
|
|
197aedcc70 | ||
|
|
491533e09c | ||
|
|
474d8d7c7b | ||
|
|
8d83076ef2 | ||
|
|
e69632ff60 | ||
|
|
996fa6df57 | ||
|
|
239782a7d8 | ||
|
|
7d3a629a1e | ||
|
|
067ae799fd | ||
|
|
ea165cb477 | ||
|
|
d33b5c4870 | ||
|
|
96db264724 | ||
|
|
dda4575aa4 | ||
|
|
2322762cc6 | ||
|
|
4a14eb3282 | ||
|
|
931faf911c | ||
|
|
f4db1e37e0 | ||
|
|
833b7b1207 | ||
|
|
622ee91323 | ||
|
|
d206414825 | ||
|
|
73d26ece05 | ||
|
|
3b90a1cc31 | ||
|
|
289ce105dc | ||
|
|
deb2752d35 | ||
|
|
df4393e033 | ||
|
|
cf34747701 | ||
|
|
ad467f0691 | ||
|
|
142403fe78 | ||
|
|
60f2b9657b | ||
|
|
a41787d23a | ||
|
|
252adbd830 | ||
|
|
c8db30fa0f | ||
|
|
fa415005eb | ||
|
|
ba8046bff5 | ||
|
|
948254dd29 | ||
|
|
82b799c1a4 | ||
|
|
358444bc1d | ||
|
|
dbab4ae645 | ||
|
|
f5ed55513b | ||
|
|
d6fb269381 | ||
|
|
3734f9cea5 | ||
|
|
5a058a8808 | ||
|
|
e2d99e3c86 | ||
|
|
e74edd8f68 | ||
|
|
d961203d17 | ||
|
|
be752371a6 | ||
|
|
af3b83318b | ||
|
|
b0cd38e2c5 | ||
|
|
0d8d718b7a | ||
|
|
54687046ab | ||
|
|
22529870f4 | ||
|
|
8aafea456d | ||
|
|
cfb70f2135 | ||
|
|
1d02644996 | ||
|
|
312809a766 | ||
|
|
5e221f6c3a | ||
|
|
232734563f | ||
|
|
5cbc64ad4f | ||
|
|
35858e0549 | ||
|
|
752b8d462e | ||
|
|
1da80bee80 | ||
|
|
315dd40734 | ||
|
|
5a82b7ad99 | ||
|
|
1a29875940 | ||
|
|
be0631bd3e | ||
|
|
e882102aff | ||
|
|
97c0470f13 | ||
|
|
47badda35c | ||
|
|
b2353ca9bd | ||
|
|
65310be0b0 | ||
|
|
ac9960c1f0 | ||
|
|
b9393068a7 | ||
|
|
9c216f60ff | ||
|
|
ce65a49dbe | ||
|
|
e973b599aa | ||
|
|
9979028d7e | ||
|
|
d09ea4a73a | ||
|
|
1bf83499b0 | ||
|
|
9c4e4a2280 | ||
|
|
f8e61f72c4 | ||
|
|
d08b95378a | ||
|
|
c1153708f6 | ||
|
|
d676c1b569 | ||
|
|
77f50a4890 | ||
|
|
24e2b47ff1 | ||
|
|
651c38afbf | ||
|
|
2dec73b2c5 | ||
|
|
ad36ba5569 | ||
|
|
1855d1b809 | ||
|
|
543fa3c85c | ||
|
|
fe05300bdc | ||
|
|
0eebf44292 | ||
|
|
7c40243da3 | ||
|
|
4a046684c8 | ||
|
|
49eeca1617 | ||
|
|
88e2af266f | ||
|
|
0bcc788752 | ||
|
|
5d687f1aa0 | ||
|
|
8fbd0b54e4 | ||
|
|
37a1680a85 | ||
|
|
2b0acca219 | ||
|
|
5293f0fa63 | ||
|
|
7d52f00d8d | ||
|
|
f8e367a5e7 | ||
|
|
b9541ba2e0 | ||
|
|
25ee6ef1ad | ||
|
|
45cb5598fc | ||
|
|
a6bf9ea58c | ||
|
|
1e1877c954 | ||
|
|
47c9f43954 | ||
|
|
2136fed4cb | ||
|
|
4b6baa93a6 | ||
|
|
ef4cbd915d | ||
|
|
d9b8fc6806 | ||
|
|
6bb6bcc538 | ||
|
|
e60a5032da | ||
|
|
a6b71e119c | ||
|
|
8e01281c12 | ||
|
|
a31de87882 | ||
|
|
dbfaa4299f | ||
|
|
78432c41d8 | ||
|
|
0d003c0309 | ||
|
|
0a796d618f | ||
|
|
84909930fb | ||
|
|
cbab3b09e5 | ||
|
|
8a321951d3 | ||
|
|
4852f5614f | ||
|
|
87621ef109 | ||
|
|
6a9c7dd2bc | ||
|
|
1fb8088fc8 | ||
|
|
27c58637eb | ||
|
|
426a242d26 | ||
|
|
7a7a7abe78 | ||
|
|
aade89b66e | ||
|
|
771a1559ef | ||
|
|
74ae758819 | ||
|
|
5e2081451d | ||
|
|
1ffdc19305 | ||
|
|
6996d17531 | ||
|
|
1e3b93d579 | ||
|
|
70fab26e6b | ||
|
|
725fb1f013 | ||
|
|
6d4c8e5942 | ||
|
|
b6e0ca5d45 | ||
|
|
35571c1d4e | ||
|
|
82a5aff8db | ||
|
|
e74a30f62a | ||
|
|
11d3a73f05 | ||
|
|
9fefb6d65a | ||
|
|
4b18c74b83 | ||
|
|
f739c6c143 | ||
|
|
9739aceb9f | ||
|
|
afde497a1a | ||
|
|
666f1e5d52 | ||
|
|
1013e83032 | ||
|
|
b15d938c7e | ||
|
|
c066c5f30b | ||
|
|
abbfd9b43d | ||
|
|
ee0167a059 | ||
|
|
f86c07bdab | ||
|
|
699595496e | ||
|
|
fa42764a4b | ||
|
|
15bbafd631 | ||
|
|
9423178df5 | ||
|
|
9260d35b4c | ||
|
|
10156a8218 | ||
|
|
d0574677c1 | ||
|
|
7b0274a657 | ||
|
|
325eaeb225 | ||
|
|
f840d623d5 | ||
|
|
f9590195a5 | ||
|
|
53eb0d8f30 | ||
|
|
b0c4c7263b | ||
|
|
b5d1a71f46 | ||
|
|
dd7b673328 | ||
|
|
9f0276f55e | ||
|
|
15d250b103 | ||
|
|
0c40579be4 | ||
|
|
f0f080a428 | ||
|
|
54b511b709 | ||
|
|
c50da3429b | ||
|
|
ad3cd289dd | ||
|
|
b1fffc7df9 | ||
|
|
587db10022 | ||
|
|
47294ad0ed | ||
|
|
28ebab868b | ||
|
|
07d15fd863 | ||
|
|
2c9eb385fd | ||
|
|
6d9f513f68 | ||
|
|
4b56a03144 | ||
|
|
9c17d27af4 | ||
|
|
cc37a5c44b | ||
|
|
9e6ee89585 | ||
|
|
ea25de8040 | ||
|
|
7bee00d706 | ||
|
|
6b9f064aeb | ||
|
|
e9377cf746 | ||
|
|
35d2b7f12a | ||
|
|
922d7d9894 | ||
|
|
24f5f3c4ca | ||
|
|
8358127992 | ||
|
|
67563b2bba | ||
|
|
73e9e9ab3a | ||
|
|
7ce393feb8 | ||
|
|
b25e58b24b | ||
|
|
cb0240c91d | ||
|
|
16302c0130 | ||
|
|
88f2f99290 | ||
|
|
36a397ddaa | ||
|
|
8fd897e348 | ||
|
|
adc76b4eb1 | ||
|
|
cbb4aa8be1 | ||
|
|
968930c543 | ||
|
|
4be8a46cdd | ||
|
|
c02496ff02 | ||
|
|
4b8afb4bc3 | ||
|
|
01aa2afc15 | ||
|
|
4f3539cb43 | ||
|
|
260bd47030 | ||
|
|
f27c982962 | ||
|
|
19340adcfb | ||
|
|
f22e598ad6 | ||
|
|
68f693cbc0 | ||
|
|
2f6fe33e2c | ||
|
|
c307cfde1c | ||
|
|
883ef50186 | ||
|
|
c359556605 | ||
|
|
3e18309367 | ||
|
|
2fb0cc7d6d | ||
|
|
26449e12e5 | ||
|
|
7058de0568 | ||
|
|
0c3438e1a5 | ||
|
|
bae9b57c2a | ||
|
|
739c45d34c | ||
|
|
32df4598fc | ||
|
|
f52daeecdc | ||
|
|
930216e3f7 | ||
|
|
8e40a082f5 | ||
|
|
e3fdf61e4a | ||
|
|
414b29b046 | ||
|
|
ba4cdc9b28 | ||
|
|
99704faeb4 | ||
|
|
78c446f525 | ||
|
|
b50b4ca632 | ||
|
|
21d84332e4 | ||
|
|
c88a2c788d | ||
|
|
b1c459c226 | ||
|
|
a8ddefe31e | ||
|
|
80d23a4efe | ||
|
|
5393a86e2b | ||
|
|
1d822db30d | ||
|
|
654cd0b0bd | ||
|
|
6da942083c | ||
|
|
f15814c784 | ||
|
|
60c7c91e8a | ||
|
|
62cf254365 | ||
|
|
747f185dc5 | ||
|
|
1a61bd9583 | ||
|
|
7cff8fadde | ||
|
|
67abde6d97 | ||
|
|
6e0b3d820c | ||
|
|
998180b05d | ||
|
|
ee9cc49956 | ||
|
|
3f79b35878 | ||
|
|
fd3ec885f9 | ||
|
|
22ca715ddb | ||
|
|
5e6b8628d4 | ||
|
|
0a980addc3 | ||
|
|
9bbfa1aeb2 | ||
|
|
48fc9d9779 | ||
|
|
8ec8e2a9b4 | ||
|
|
5ec5979136 | ||
|
|
4926ad6ce5 | ||
|
|
f013756b7b | ||
|
|
feb092a7fa | ||
|
|
406e899b34 | ||
|
|
4d89c344d1 | ||
|
|
b9f484ceb6 | ||
|
|
1920ae8c1e | ||
|
|
08422c29e7 | ||
|
|
59a70dd5b7 | ||
|
|
86a80d3e78 | ||
|
|
1cebae7868 | ||
|
|
7b0ab1f7b7 | ||
|
|
1aebdf4a06 | ||
|
|
e94a424596 | ||
|
|
14db763613 | ||
|
|
65ced601e9 | ||
|
|
2f0b259d96 | ||
|
|
8267dd91d9 | ||
|
|
2149a1efde | ||
|
|
7990717a95 | ||
|
|
4526effbfa | ||
|
|
a8774b5c88 | ||
|
|
fbc5946e71 | ||
|
|
0fbdf110b1 | ||
|
|
d37d873b4c | ||
|
|
d5419ab0d0 | ||
|
|
4805cdb64c | ||
|
|
5d74d8163b | ||
|
|
76c74fc8a9 | ||
|
|
321f53f953 | ||
|
|
849def9ef2 | ||
|
|
911668617b | ||
|
|
d55e750ecd | ||
|
|
08d6af502c | ||
|
|
9a68409ccd | ||
|
|
b8f4a38c80 | ||
|
|
a86acc9717 | ||
|
|
a1fc75689e | ||
|
|
0a5e4ab99c | ||
|
|
fd8876b3c4 | ||
|
|
994cba2fcf | ||
|
|
fb565d202e | ||
|
|
adb655a53b | ||
|
|
2d19c5d915 | ||
|
|
ff0e00e4e4 | ||
|
|
1ea76cc08a | ||
|
|
786b3c3da2 | ||
|
|
c74049a140 | ||
|
|
e7a0444c67 | ||
|
|
32ed169ca8 | ||
|
|
9e2f1d15d8 | ||
|
|
8d93a44566 | ||
|
|
471cb0287d | ||
|
|
1428ec0708 | ||
|
|
4ed0a93ec6 | ||
|
|
ba125dc06a | ||
|
|
fe8b0624fe | ||
|
|
56dc07e30d | ||
|
|
6dd31748e9 | ||
|
|
2f4379716a | ||
|
|
58ac1c920d | ||
|
|
abedd14cac | ||
|
|
73ca47b608 | ||
|
|
5ddc832e9d | ||
|
|
c097b21443 | ||
|
|
17fb0ef94e | ||
|
|
2089065499 | ||
|
|
b168214952 | ||
|
|
678e3bd108 | ||
|
|
6cc030fa06 | ||
|
|
b84bd8e3cf | ||
|
|
8c1921da15 | ||
|
|
9a86311bc4 | ||
|
|
5950100615 | ||
|
|
4e3e0aad14 | ||
|
|
6cc3b05fb7 | ||
|
|
0e75ec6758 | ||
|
|
f157d4dfa4 | ||
|
|
3c81f9c5e9 | ||
|
|
d97bf9a9e9 | ||
|
|
bda66ab4b9 | ||
|
|
2719880636 | ||
|
|
ca20f4de22 | ||
|
|
9080810c20 | ||
|
|
0a6b36d3c8 | ||
|
|
0cf6765d56 | ||
|
|
7b3e572d25 | ||
|
|
be387992e6 | ||
|
|
1f8a282971 | ||
|
|
8c804957fa | ||
|
|
8b1fc9a18f | ||
|
|
bd35c23784 | ||
|
|
aef3ed8bcb | ||
|
|
01b23a3e88 | ||
|
|
4185f3816b | ||
|
|
4522b40879 | ||
|
|
c2d240b393 | ||
|
|
85c2a38cef | ||
|
|
d9c64baefb | ||
|
|
05b693ed12 | ||
|
|
6cef8ae54d | ||
|
|
0d3ec37abe | ||
|
|
b83d5b3ec1 | ||
|
|
5889053025 | ||
|
|
84f7a22dfe | ||
|
|
413c478f19 | ||
|
|
6c0c8843c8 | ||
|
|
0245429d78 | ||
|
|
9f445a2083 | ||
|
|
d9e1b0abb9 | ||
|
|
37dcafdc47 | ||
|
|
07a10ef465 | ||
|
|
1cb0dba351 | ||
|
|
17465c7d92 | ||
|
|
2f16308b1e | ||
|
|
fb0cb42d62 | ||
|
|
03a75169f6 | ||
|
|
c57cdb75b5 | ||
|
|
e5fced8cee | ||
|
|
866c3a79d7 | ||
|
|
8e4d7ce3af | ||
|
|
a6080560b9 | ||
|
|
50fc61bd06 | ||
|
|
96f017824e | ||
|
|
644e8a9d47 | ||
|
|
03ec323733 | ||
|
|
b104a29950 | ||
|
|
6e44c1af0b | ||
|
|
dfbaaac019 | ||
|
|
7ab01f3270 | ||
|
|
d3fafd6db2 | ||
|
|
e9af6d3ad4 | ||
|
|
387cbbc21c | ||
|
|
6a62e6a0a4 | ||
|
|
53d69774d3 | ||
|
|
961c63570a | ||
|
|
0e5350b9bb | ||
|
|
32a777e49e | ||
|
|
e48243b492 | ||
|
|
ce21410233 | ||
|
|
1456b37915 | ||
|
|
0636023797 | ||
|
|
f271dde950 | ||
|
|
735e713ef6 | ||
|
|
2cad68bd18 | ||
|
|
67c7d79f37 | ||
|
|
d8f9356e1c | ||
|
|
78dd896386 | ||
|
|
6fe026357e | ||
|
|
06e72f557f | ||
|
|
c6aa11c114 | ||
|
|
adf03087a9 | ||
|
|
63df4f9631 | ||
|
|
4158ca903a | ||
|
|
3e513d5eee | ||
|
|
0f00b9931b | ||
|
|
e142b7a504 | ||
|
|
4f29770bcb | ||
|
|
502853483a | ||
|
|
7c46ffed47 | ||
|
|
7b9f30ad90 | ||
|
|
a8273ab5fa | ||
|
|
0054ea02b7 | ||
|
|
b7a8c6226c | ||
|
|
d52ce2f9c3 | ||
|
|
6e1520c868 | ||
|
|
ff11d86a9d | ||
|
|
36fd982263 | ||
|
|
0341c1c73e | ||
|
|
2970b3edf2 | ||
|
|
f9b17a2e04 | ||
|
|
ec08532b88 | ||
|
|
43c5c009f0 | ||
|
|
fcc96e3b76 | ||
|
|
9339f87ead | ||
|
|
25944d8d7d | ||
|
|
14914abf65 | ||
|
|
cd9fa25a2e | ||
|
|
2ade7be332 | ||
|
|
6e99c20987 | ||
|
|
774d0de537 | ||
|
|
b200a4e40c | ||
|
|
42e3408993 | ||
|
|
4dc20ed989 | ||
|
|
24512bc34d | ||
|
|
1e0c095169 | ||
|
|
f66a6d6481 | ||
|
|
4b9176cce1 | ||
|
|
145c5b7c5a | ||
|
|
f58cb6b08a | ||
|
|
8535b94087 | ||
|
|
4bda5725b5 | ||
|
|
6a1572eaf6 | ||
|
|
7b897b5448 | ||
|
|
33eccfc5f8 | ||
|
|
3ecc14d399 | ||
|
|
d026d5c796 | ||
|
|
9fddb4eb01 | ||
|
|
7ae82d722d | ||
|
|
7474a80ff9 | ||
|
|
5ac40d728c | ||
|
|
f6dd3d383d | ||
|
|
5969d4cbdb | ||
|
|
3b6cea10d2 | ||
|
|
76e55f840a | ||
|
|
1569d10d4b | ||
|
|
c4d5e9d2f0 | ||
|
|
c8e534de0b | ||
|
|
236ecd0240 | ||
|
|
1175b02d29 | ||
|
|
0a5b845b97 | ||
|
|
6b58a7ac61 | ||
|
|
514e394a49 | ||
|
|
d89197eb78 | ||
|
|
3fd29bb252 | ||
|
|
17b5c0b79e | ||
|
|
7b1d5d480b | ||
|
|
18de476ebd | ||
|
|
0be8efdc25 | ||
|
|
508a1511f8 | ||
|
|
42c0f9a8d1 | ||
|
|
495a59e33e | ||
|
|
0cd821e954 | ||
|
|
2079767828 | ||
|
|
b88687e85d | ||
|
|
7af3cc3c4f | ||
|
|
ab532a1153 | ||
|
|
f454285aba | ||
|
|
7f53665755 | ||
|
|
99377f5f11 | ||
|
|
2aee4c6cc9 | ||
|
|
7cff365355 | ||
|
|
9a4cc2b151 | ||
|
|
37a458aee0 | ||
|
|
24389e5ab9 | ||
|
|
f534051053 | ||
|
|
6c1bd0e064 | ||
|
|
d8fa532caa | ||
|
|
18688ef231 | ||
|
|
6318d84a1d | ||
|
|
4a27c5efff | ||
|
|
b69ba66408 | ||
|
|
1cdbf8d36d | ||
|
|
c0285fc9dd | ||
|
|
7b47f74c26 | ||
|
|
c450efa222 | ||
|
|
9ccf1e988b | ||
|
|
2544f2e163 | ||
|
|
89c3651580 | ||
|
|
df7da2e5ef | ||
|
|
6566b54fe9 | ||
|
|
8c8f307bb1 | ||
|
|
f51338237e | ||
|
|
7c9881ed9e | ||
|
|
16cdfbb11b | ||
|
|
7e5fa4e620 | ||
|
|
8ab6ffb4e1 | ||
|
|
8a15dcb379 | ||
|
|
49d06bdbcd | ||
|
|
34c8967726 | ||
|
|
1dba8635ad | ||
|
|
6c86926ee5 | ||
|
|
d1cdc4c245 | ||
|
|
3f16d6fc3c | ||
|
|
fddbf37a3a | ||
|
|
9492fb7afc | ||
|
|
a02f8e8670 | ||
|
|
c1d940466c | ||
|
|
b72e09b4e3 | ||
|
|
6704db88b7 | ||
|
|
877874813e | ||
|
|
ae9ff38fa0 | ||
|
|
5181377820 | ||
|
|
997be64cf9 | ||
|
|
f33735d321 | ||
|
|
8ad5878c33 | ||
|
|
a57dd53c1b | ||
|
|
4fef023bea | ||
|
|
851d276b48 | ||
|
|
ab91ab1a89 | ||
|
|
ba893cadc9 | ||
|
|
50dda5d487 | ||
|
|
0b1b976ff5 | ||
|
|
007b74bcf0 | ||
|
|
12d0622f03 | ||
|
|
fcf58f9648 | ||
|
|
878f2454fe | ||
|
|
e06f6ffe52 | ||
|
|
7bc36712b1 | ||
|
|
f1627630ad | ||
|
|
efda5a32f9 | ||
|
|
8c6f86abe0 | ||
|
|
def14fba53 | ||
|
|
b3d10b3e45 | ||
|
|
31248dbd6f | ||
|
|
c031efe84f | ||
|
|
25d7e83e68 | ||
|
|
458b1b599f | ||
|
|
015e5fc333 | ||
|
|
8fa942375a | ||
|
|
231df0a036 | ||
|
|
a41c917d17 | ||
|
|
8168249c56 | ||
|
|
32e1dc40d0 | ||
|
|
dba74b58ba | ||
|
|
30825dc8e3 | ||
|
|
a9f6091b60 | ||
|
|
3db2fad222 | ||
|
|
03321c646f | ||
|
|
53917b9e25 | ||
|
|
f43e35c3a9 | ||
|
|
c385101e1f | ||
|
|
edc384890d | ||
|
|
00ab1d9021 | ||
|
|
2cd34ad71c | ||
|
|
93c8ec1a26 | ||
|
|
fb5cb77c1b | ||
|
|
1ec44cef06 | ||
|
|
301996ed57 | ||
|
|
0224a3ff98 | ||
|
|
47d0184ed8 | ||
|
|
a388cd30e0 | ||
|
|
4adfcecb80 | ||
|
|
a62f9b28f0 | ||
|
|
cd837cb291 | ||
|
|
dfa1f2adf7 | ||
|
|
d88f27f3e1 | ||
|
|
4357323bbb | ||
|
|
bd40f7b9c6 | ||
|
|
83715779dc | ||
|
|
715a4286f9 | ||
|
|
02f4b67b7f | ||
|
|
b189696ac5 | ||
|
|
5c0013e74f | ||
|
|
96b3b57d81 | ||
|
|
22e4d10031 | ||
|
|
c37f86bbd6 | ||
|
|
0e4066b476 | ||
|
|
232f12576c | ||
|
|
f4f56fc344 | ||
|
|
16209d02b6 | ||
|
|
fce5357354 | ||
|
|
7709a63384 | ||
|
|
21cbf76baf | ||
|
|
089265a022 | ||
|
|
f1ee5ee508 | ||
|
|
83bc4bf2fb | ||
|
|
f734a85665 | ||
|
|
33c859a8ca | ||
|
|
a2f63a1b63 | ||
|
|
a884a0c1f0 | ||
|
|
dd37a1c2bd | ||
|
|
8c23d78362 | ||
|
|
847ad26814 | ||
|
|
e943572d36 | ||
|
|
62f2b1c0fb | ||
|
|
0e6f83b9c3 | ||
|
|
2d39099061 | ||
|
|
65718a9728 | ||
|
|
0c297196ad | ||
|
|
670a581d12 | ||
|
|
a9e0a71fa1 | ||
|
|
17b34dc432 | ||
|
|
6144c849e3 | ||
|
|
28410618ec | ||
|
|
0c8ea73282 | ||
|
|
c98945a885 | ||
|
|
c350614e8e | ||
|
|
e42ac799af | ||
|
|
1cc8d9ad28 | ||
|
|
f208b5bb0f | ||
|
|
dbbe580ef1 | ||
|
|
fb33dd7036 | ||
|
|
dc12e16d83 | ||
|
|
2e7096c66f | ||
|
|
700ccff3cd | ||
|
|
9e93d178f9 | ||
|
|
530673dcd1 | ||
|
|
5d5ac5576e | ||
|
|
39b5737a7f | ||
|
|
2157b209a2 | ||
|
|
4942eaedb3 | ||
|
|
ca6ebecfdc | ||
|
|
77e1708657 | ||
|
|
42b711b45a | ||
|
|
ca524a03f3 | ||
|
|
4ee831f18a | ||
|
|
8430d6076b | ||
|
|
9c17b0e16b | ||
|
|
fbd7347d4c | ||
|
|
602ef0e8f3 | ||
|
|
1d1080f2e4 | ||
|
|
a715e0c1b6 | ||
|
|
a134b1a359 | ||
|
|
d0b673c99d | ||
|
|
55ba270b83 | ||
|
|
db94c609bd | ||
|
|
d7e4719895 | ||
|
|
01e75456e8 | ||
|
|
acb9c69d4d | ||
|
|
275652f301 | ||
|
|
1319ae748c | ||
|
|
095ee2bfd0 | ||
|
|
823bac0174 | ||
|
|
ed45d37b3d | ||
|
|
dd040d3b39 | ||
|
|
3b813a9421 | ||
|
|
0dccdaecdf | ||
|
|
2423af3da2 | ||
|
|
d9f0665766 | ||
|
|
282d79da4a | ||
|
|
889ded3b01 | ||
|
|
42103edc38 | ||
|
|
da4380dabd | ||
|
|
d1a9793fec | ||
|
|
db092bbf52 | ||
|
|
81a3f29650 | ||
|
|
f8e7fe7905 | ||
|
|
62888735f2 | ||
|
|
1b509dd44b | ||
|
|
a326671a21 | ||
|
|
3dcf3af5c5 | ||
|
|
7e72337ff3 | ||
|
|
71bd7c039d | ||
|
|
126c4f1789 | ||
|
|
efc640974a | ||
|
|
9385ebd893 | ||
|
|
0900b012af | ||
|
|
7a9872d5b7 | ||
|
|
6807dc8493 | ||
|
|
70e927db3b | ||
|
|
f94ef27a0b | ||
|
|
dccda93f6a | ||
|
|
abc4c4a130 | ||
|
|
a293e13540 | ||
|
|
86d1ff3cef | ||
|
|
2631a05e74 | ||
|
|
2f586f4437 | ||
|
|
5f01216858 | ||
|
|
3154652a00 | ||
|
|
0ea9fd3f6f | ||
|
|
081ef02b72 | ||
|
|
f32175bf1e | ||
|
|
a1e9192db7 | ||
|
|
c69de376df | ||
|
|
7e8b79545f | ||
|
|
3ce6260110 | ||
|
|
8d74d159ba | ||
|
|
3de1eb1c51 | ||
|
|
dc8e8b9904 | ||
|
|
67535f6af0 | ||
|
|
0b9ca99053 | ||
|
|
25787847fd | ||
|
|
db2e6df86d | ||
|
|
000bce99f8 | ||
|
|
3191b71235 | ||
|
|
6f62f96f11 | ||
|
|
b621f5c2d0 | ||
|
|
eee439ffd2 | ||
|
|
fc5e3a4cd9 | ||
|
|
3119a75c19 | ||
|
|
c3bd625d46 | ||
|
|
92e343dd8b | ||
|
|
ec41613c23 | ||
|
|
e255006d8c | ||
|
|
3303a31255 | ||
|
|
497ccebb73 | ||
|
|
702ff2fd31 | ||
|
|
1dbef2449a | ||
|
|
50b817093d | ||
|
|
0995da09ef | ||
|
|
53b5484f2b | ||
|
|
f803bee9a9 | ||
|
|
a9e95ee493 | ||
|
|
a30326e4de | ||
|
|
99fe58c96f | ||
|
|
2111b3c6fb | ||
|
|
22e8ae39a2 | ||
|
|
2941db66e4 | ||
|
|
cd8d5d0eea | ||
|
|
56dcd409a7 | ||
|
|
88a9093137 | ||
|
|
f77acb4716 | ||
|
|
2f470f129f | ||
|
|
2d666a0c40 | ||
|
|
51509ddff5 | ||
|
|
d1ae120833 | ||
|
|
6dc847f0a4 | ||
|
|
1a6a45c624 | ||
|
|
79a312acce | ||
|
|
41582042fb | ||
|
|
14556a847f | ||
|
|
463de572fe | ||
|
|
91d3a98fa4 | ||
|
|
a18470e5f7 | ||
|
|
0856f20e2f | ||
|
|
fb34961fdf | ||
|
|
d0b9c42d7c | ||
|
|
27ab7ee41b | ||
|
|
a1d614600d | ||
|
|
3cf3492e23 | ||
|
|
05193f55f8 | ||
|
|
00f01da4f4 | ||
|
|
84430f461a | ||
|
|
1c1cad0cbb | ||
|
|
b8a357c6cc | ||
|
|
07599df5f4 | ||
|
|
da46b4faf7 | ||
|
|
ec1305c671 | ||
|
|
bb47ca0ccd | ||
|
|
bf7117951a | ||
|
|
24851812c1 | ||
|
|
30463c3cd8 | ||
|
|
d86253a7b1 | ||
|
|
e76624a5fd | ||
|
|
4be761379d | ||
|
|
25f3c9b508 | ||
|
|
120067a69d | ||
|
|
8df82c359a | ||
|
|
f77b869283 | ||
|
|
cabdba9bd1 | ||
|
|
0cca54b731 | ||
|
|
08f39419a3 | ||
|
|
258d1713e8 | ||
|
|
f3cbec4e96 | ||
|
|
da8e8ad1f6 | ||
|
|
f0d5a115fc | ||
|
|
8222334835 | ||
|
|
f3953bd5c9 | ||
|
|
1d05c315ac | ||
|
|
cf7a2fa672 | ||
|
|
eda6d20fe8 | ||
|
|
445118b20d | ||
|
|
1d3826f19c | ||
|
|
3c5b25aa92 | ||
|
|
78d31430a5 | ||
|
|
b6f1833d97 | ||
|
|
0abd0dec5f | ||
|
|
6d92f03fb2 | ||
|
|
8deaaf04e3 | ||
|
|
c4a084ee41 | ||
|
|
0afef0f30e | ||
|
|
5d0578ec3c | ||
|
|
4f60188e3e | ||
|
|
cfa4e964a0 | ||
|
|
870df3f030 | ||
|
|
596697c5c5 | ||
|
|
28c8bf1815 | ||
|
|
296a2e6366 | ||
|
|
4b93aafccf | ||
|
|
c9f9a88637 | ||
|
|
9cb1073082 | ||
|
|
a13c02ab02 | ||
|
|
797d6d1eb1 | ||
|
|
4342242455 | ||
|
|
1d05474b51 | ||
|
|
65c781c086 | ||
|
|
0b9a0bbb72 | ||
|
|
d828437316 | ||
|
|
45064505ce | ||
|
|
0cdc54aba7 | ||
|
|
40c8488712 | ||
|
|
cb56535cd5 | ||
|
|
c3a2fbdfb0 | ||
|
|
13207d48df | ||
|
|
e8f7342cb6 | ||
|
|
23249dc4e9 | ||
|
|
1703d1452d | ||
|
|
d0dd3b59e5 | ||
|
|
e6a4eadf1c | ||
|
|
34bba3db19 | ||
|
|
1174200f20 | ||
|
|
1039d8e057 | ||
|
|
51518df076 | ||
|
|
398b4c47de | ||
|
|
598f598bd0 | ||
|
|
b28d0ad5c7 | ||
|
|
2f74995680 | ||
|
|
c7099f67fa | ||
|
|
39c4bcf3ea | ||
|
|
1e059c1fb9 | ||
|
|
bf5ed6c92a | ||
|
|
80ce2088a0 | ||
|
|
61c7329885 | ||
|
|
11137cf6e3 | ||
|
|
4444cf45fb | ||
|
|
659bfda3ba | ||
|
|
ab4813b38e | ||
|
|
456b37fa9b | ||
|
|
7c70242b10 | ||
|
|
757539f5d2 | ||
|
|
dd33acef98 | ||
|
|
46f73540ce | ||
|
|
99f6da8521 | ||
|
|
3c86c52e5c | ||
|
|
0c63db27ab | ||
|
|
ba002752ee | ||
|
|
8d9448acd9 | ||
|
|
125d72958f | ||
|
|
163bb16a1f | ||
|
|
e8d1b408ac | ||
|
|
5e8c924aa6 | ||
|
|
6f6c2dce3c | ||
|
|
608df60a9c | ||
|
|
3fb8910ba1 | ||
|
|
88a1a829f9 | ||
|
|
4901d87ffb | ||
|
|
ef8d22d3bd | ||
|
|
2a25d2aa6f | ||
|
|
edbf152f2c | ||
|
|
8c3b283893 | ||
|
|
ac576eab7b | ||
|
|
d30cf62f50 | ||
|
|
6aa641bb33 | ||
|
|
90fdb93ae1 | ||
|
|
f7b520b251 | ||
|
|
1f83a4619f | ||
|
|
07e0863ce9 | ||
|
|
84782b3cbe | ||
|
|
ffdcb04a84 | ||
|
|
f1a65e7dc8 | ||
|
|
5ebc1b0cf0 | ||
|
|
f2c09ed486 | ||
|
|
eee77a13cf | ||
|
|
6fb7e82b7a | ||
|
|
3dd2fc7669 | ||
|
|
d0a348111a | ||
|
|
6663afdd10 | ||
|
|
878fe3b3de | ||
|
|
67b2888489 | ||
|
|
af8be3216f | ||
|
|
fb8430d9e5 | ||
|
|
c028a7a468 | ||
|
|
6423c766e0 | ||
|
|
6856362174 | ||
|
|
b927019e0c | ||
|
|
1f8640b5f2 | ||
|
|
a49b8af638 | ||
|
|
8e49d99d99 | ||
|
|
bc9eb79e5e | ||
|
|
c80e35ef58 | ||
|
|
e2ca0e4d5e | ||
|
|
e0d79a271a | ||
|
|
a77b9a92cd | ||
|
|
967ffd6e39 | ||
|
|
23dbadab91 | ||
|
|
6b3d2fe052 | ||
|
|
d566a829e6 | ||
|
|
8e5e039af8 | ||
|
|
2e725a2d93 | ||
|
|
c139865635 | ||
|
|
f59c544fbe | ||
|
|
28bdcae2c6 | ||
|
|
b1530d0773 | ||
|
|
5feee64425 | ||
|
|
9647259b5f | ||
|
|
84e276ae10 | ||
|
|
4718c3d815 | ||
|
|
aa1bfb0885 | ||
|
|
acf2055768 | ||
|
|
ef4d2fac40 | ||
|
|
b53c1d940a | ||
|
|
edd62490f8 | ||
|
|
96a381d3a3 | ||
|
|
a2a72de2cf | ||
|
|
9a51e69320 | ||
|
|
07be57847d | ||
|
|
0095ca5fe7 | ||
|
|
a1b1d594aa | ||
|
|
531a2f1634 | ||
|
|
cb1c423aea | ||
|
|
93306c99ce | ||
|
|
5f8c69064c | ||
|
|
f51a09ddf0 | ||
|
|
92533c0297 |
@@ -1,10 +0,0 @@
|
||||
.cache
|
||||
docs/dist
|
||||
docs/search.json
|
||||
docs/**/*.min.js
|
||||
dist
|
||||
examples
|
||||
node_modules
|
||||
src/react
|
||||
scripts
|
||||
|
||||
211
.eslintrc.cjs
@@ -1,211 +0,0 @@
|
||||
/* eslint-env node */
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'wc',
|
||||
'lit',
|
||||
'lit-a11y',
|
||||
'chai-expect',
|
||||
'chai-friendly',
|
||||
'import',
|
||||
'sort-imports-es6-autofix'
|
||||
],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:wc/recommended',
|
||||
'plugin:wc/best-practice',
|
||||
'plugin:lit/recommended',
|
||||
'plugin:lit-a11y/recommended'
|
||||
],
|
||||
env: {
|
||||
es2021: true,
|
||||
browser: true
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:@typescript-eslint/recommended-requiring-type-checking'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
project: './tsconfig.json',
|
||||
tsconfigRootDir: __dirname
|
||||
},
|
||||
files: ['*.ts'],
|
||||
rules: {
|
||||
'default-param-last': 'off',
|
||||
'@typescript-eslint/default-param-last': 'error',
|
||||
'no-empty-function': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'warn',
|
||||
'no-implied-eval': 'off',
|
||||
'@typescript-eslint/no-implied-eval': 'error',
|
||||
'no-invalid-this': 'off',
|
||||
'@typescript-eslint/no-invalid-this': 'error',
|
||||
'no-shadow': 'off',
|
||||
'@typescript-eslint/no-shadow': 'error',
|
||||
'no-throw-literal': 'off',
|
||||
'@typescript-eslint/no-throw-literal': 'error',
|
||||
'no-unused-expressions': 'off',
|
||||
'@typescript-eslint/prefer-regexp-exec': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 'error',
|
||||
'@typescript-eslint/unbound-method': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
'@typescript-eslint/no-misused-promises': [
|
||||
'error',
|
||||
{
|
||||
checksVoidReturn: false
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/consistent-type-assertions': [
|
||||
'warn',
|
||||
{
|
||||
assertionStyle: 'as',
|
||||
objectLiteralTypeAssertions: 'never'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/consistent-type-imports': 'warn',
|
||||
'@typescript-eslint/no-base-to-string': 'error',
|
||||
'@typescript-eslint/no-confusing-non-null-assertion': 'error',
|
||||
'@typescript-eslint/no-invalid-void-type': 'error',
|
||||
'@typescript-eslint/no-require-imports': 'error',
|
||||
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'warn',
|
||||
'@typescript-eslint/no-unnecessary-condition': 'off',
|
||||
'@typescript-eslint/no-unnecessary-qualifier': 'warn',
|
||||
'@typescript-eslint/non-nullable-type-assertion-style': 'warn',
|
||||
'@typescript-eslint/prefer-for-of': 'warn',
|
||||
'@typescript-eslint/prefer-optional-chain': 'warn',
|
||||
'@typescript-eslint/prefer-ts-expect-error': 'warn',
|
||||
'@typescript-eslint/prefer-return-this-type': 'error',
|
||||
'@typescript-eslint/prefer-string-starts-ends-with': 'warn',
|
||||
'@typescript-eslint/require-array-sort-compare': 'error',
|
||||
'@typescript-eslint/unified-signatures': 'warn',
|
||||
'@typescript-eslint/array-type': 'warn',
|
||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
||||
'@typescript-eslint/member-delimiter-style': 'warn',
|
||||
'@typescript-eslint/method-signature-style': 'warn',
|
||||
'@typescript-eslint/no-extraneous-class': 'error',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/parameter-properties': 'error',
|
||||
'@typescript-eslint/strict-boolean-expressions': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.cjs'],
|
||||
env: {
|
||||
node: true
|
||||
}
|
||||
},
|
||||
{
|
||||
extends: ['plugin:chai-expect/recommended', 'plugin:chai-friendly/recommended'],
|
||||
files: ['*.test.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 'off'
|
||||
}
|
||||
}
|
||||
],
|
||||
rules: {
|
||||
'no-template-curly-in-string': 'error',
|
||||
'array-callback-return': 'error',
|
||||
'comma-dangle': 'off',
|
||||
'consistent-return': 'error',
|
||||
curly: 'off',
|
||||
'default-param-last': 'error',
|
||||
eqeqeq: 'error',
|
||||
'lit-a11y/click-events-have-key-events': 'off',
|
||||
'no-constructor-return': 'error',
|
||||
'no-empty-function': 'warn',
|
||||
'no-eval': 'error',
|
||||
'no-extend-native': 'error',
|
||||
'no-extra-bind': 'error',
|
||||
'no-floating-decimal': 'error',
|
||||
'no-implicit-coercion': 'off',
|
||||
'no-implicit-globals': 'error',
|
||||
'no-implied-eval': 'error',
|
||||
'no-invalid-this': 'error',
|
||||
'no-labels': 'error',
|
||||
'no-lone-blocks': 'error',
|
||||
'no-new': 'error',
|
||||
'no-new-func': 'error',
|
||||
'no-new-wrappers': 'error',
|
||||
'no-octal-escape': 'error',
|
||||
'no-proto': 'error',
|
||||
'no-return-assign': 'warn',
|
||||
'no-script-url': 'error',
|
||||
'no-self-compare': 'warn',
|
||||
'no-sequences': 'warn',
|
||||
'no-throw-literal': 'error',
|
||||
'no-unmodified-loop-condition': 'error',
|
||||
'no-unused-expressions': 'warn',
|
||||
'no-useless-call': 'error',
|
||||
'no-useless-concat': 'error',
|
||||
'no-useless-return': 'warn',
|
||||
'prefer-promise-reject-errors': 'error',
|
||||
radix: 'off',
|
||||
'require-await': 'error',
|
||||
'wrap-iife': ['warn', 'inside'],
|
||||
'no-shadow': 'error',
|
||||
'no-array-constructor': 'error',
|
||||
'no-bitwise': 'error',
|
||||
'no-multi-assign': 'warn',
|
||||
'no-new-object': 'error',
|
||||
'no-useless-computed-key': 'warn',
|
||||
'no-useless-rename': 'warn',
|
||||
'no-var': 'error',
|
||||
'prefer-const': 'warn',
|
||||
'prefer-numeric-literals': 'warn',
|
||||
'prefer-object-spread': 'warn',
|
||||
'prefer-rest-params': 'warn',
|
||||
'prefer-spread': 'warn',
|
||||
'prefer-template': 'off',
|
||||
'no-else-return': 'off',
|
||||
'func-names': ['warn', 'never'],
|
||||
'one-var': ['warn', 'never'],
|
||||
'operator-assignment': 'warn',
|
||||
'prefer-arrow-callback': 'warn',
|
||||
'no-restricted-imports': [
|
||||
'warn',
|
||||
{
|
||||
paths: [
|
||||
{
|
||||
name: '.',
|
||||
message: 'Usage of local index imports is not allowed.'
|
||||
},
|
||||
{
|
||||
name: './index',
|
||||
message: 'Import from the source file instead.'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'always',
|
||||
{
|
||||
ignorePackages: true,
|
||||
pattern: {
|
||||
js: 'always',
|
||||
ts: 'never'
|
||||
}
|
||||
}
|
||||
],
|
||||
'import/no-duplicates': 'warn',
|
||||
'sort-imports-es6-autofix/sort-imports-es6': [
|
||||
2,
|
||||
{
|
||||
ignoreCase: true,
|
||||
ignoreMemberSort: false,
|
||||
memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single']
|
||||
}
|
||||
],
|
||||
'wc/guard-super-call': 'off'
|
||||
}
|
||||
};
|
||||
2
.github/CODE_OF_CONDUCT.md
vendored
@@ -35,7 +35,7 @@ This Code of Conduct applies within all project spaces, and it also applies when
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at cory@abeautifulsite.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at support@fontawesome.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
|
||||
1
.github/FUNDING.yml
vendored
@@ -1 +0,0 @@
|
||||
github: [claviska]
|
||||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,7 +1,7 @@
|
||||
contact_links:
|
||||
- name: Feature Requests
|
||||
url: https://github.com/shoelace-style/shoelace/discussions/categories/ideas
|
||||
url: https://github.com/shoelace-style/webawesome/discussions/categories/ideas-suggestions
|
||||
about: All requests for new features should go here.
|
||||
- name: Help & Support
|
||||
url: https://github.com/shoelace-style/shoelace/discussions/categories/help
|
||||
url: https://github.com/shoelace-style/webawesome/discussions/categories/ask-for-help
|
||||
about: Please don't create issues for personal help requests. Instead, ask your question on the discussion forum.
|
||||
|
||||
2
.github/SECURITY.md
vendored
@@ -2,6 +2,6 @@
|
||||
|
||||
We take security issues in Web Awesome very seriously and appreciate your efforts to disclose your findings responsibly.
|
||||
|
||||
To report a security issue, email [cory@fontawesome.com](mailto:cory@abeautifulsite.net) and include "WEB AWESOME SECURITY" in the subject line.
|
||||
To report a security issue, email [support@fontawesome.com](mailto:support@fontawesome.com) and include "WEB AWESOME SECURITY" in the subject line.
|
||||
|
||||
We'll respond as soon as possible and keep you updated throughout the process.
|
||||
|
||||
48
.github/workflows/client_tests.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Client Tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [next]
|
||||
pull_request:
|
||||
branches: [next]
|
||||
|
||||
|
||||
jobs:
|
||||
client_test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Lint
|
||||
run: npm run prettier
|
||||
working-directory: ./packages/webawesome
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
working-directory: ./packages/webawesome
|
||||
|
||||
- name: Install Playwright
|
||||
run: npx playwright install --with-deps
|
||||
working-directory: ./packages/webawesome
|
||||
|
||||
- name: Run CSR tests
|
||||
# FAIL_FAST to fail on first failing test.
|
||||
run: FAIL_FAST="true" CSR_ONLY="true" npm run test
|
||||
working-directory: ./packages/webawesome
|
||||
30
.github/workflows/node.js.yml
vendored
@@ -1,30 +0,0 @@
|
||||
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [next]
|
||||
pull_request:
|
||||
branches: [next]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- run: npx playwright install-deps
|
||||
- run: npm ci
|
||||
- run: npm run verify
|
||||
42
.github/workflows/ssr_tests.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: SSR Tests
|
||||
|
||||
on:
|
||||
# push:
|
||||
# branches: [next]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
ssr_test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
working-directory: ./packages/webawesome
|
||||
|
||||
- name: Install Playwright
|
||||
run: npx playwright install --with-deps
|
||||
working-directory: ./packages/webawesome
|
||||
|
||||
- name: Run SSR tests
|
||||
# FAIL_FAST to fail on first failing test.
|
||||
run: FAIL_FAST="true" SSR_ONLY="true" npm run test
|
||||
working-directory: ./packages/webawesome
|
||||
|
||||
14
.gitignore
vendored
@@ -1,10 +1,12 @@
|
||||
_site
|
||||
.cache
|
||||
.DS_Store
|
||||
package.json
|
||||
package-lock.json
|
||||
dist
|
||||
docs/assets/images/sprite.svg
|
||||
dist/
|
||||
dist-cdn/
|
||||
node_modules
|
||||
src/react
|
||||
cdn
|
||||
packages/**/*/src/react
|
||||
cdn/
|
||||
yarn.lock
|
||||
_bundle_
|
||||
/packages/webawesome-pro
|
||||
/packages/webawesome-app
|
||||
|
||||
28
.gitpod.yml
@@ -1,28 +0,0 @@
|
||||
tasks:
|
||||
- init: npm install && npm run build
|
||||
command: npm run start
|
||||
|
||||
ports:
|
||||
- port: 3001
|
||||
onOpen: ignore
|
||||
- port: 4000-4999
|
||||
onOpen: open-preview
|
||||
|
||||
github:
|
||||
prebuilds:
|
||||
# enable for the master/default branch (defaults to true)
|
||||
master: true
|
||||
# enable for all branches in this repo (defaults to false)
|
||||
branches: true
|
||||
# enable for pull requests coming from this repo (defaults to true)
|
||||
pullRequests: true
|
||||
# enable for pull requests coming from forks (defaults to false)
|
||||
pullRequestsFromForks: true
|
||||
# add a check to pull requests (defaults to true)
|
||||
addCheck: true
|
||||
# add a "Review in Gitpod" button as a comment to pull requests (defaults to false)
|
||||
addComment: false
|
||||
# add a "Review in Gitpod" button to the pull request's description (defaults to false)
|
||||
addBadge: true
|
||||
# add a label once the prebuild is ready to pull requests (defaults to false)
|
||||
addLabel: true
|
||||
@@ -1,14 +1,24 @@
|
||||
# Files are relative to .prettierignore at the root of this monorepo.
|
||||
# <https://github.com/prettier/prettier-vscode/issues/1252>
|
||||
|
||||
*.hbs
|
||||
.cache
|
||||
*.md
|
||||
!packages/webawesome/docs/docs/patterns/**/*.md
|
||||
docs/docs/patterns/blog-news/post-list.md
|
||||
**/*/.cache
|
||||
.github
|
||||
cspell.json
|
||||
dist
|
||||
docs/search.json
|
||||
src/components/icon/icons
|
||||
src/react/index.ts
|
||||
packages/**/*/dist
|
||||
packages/**/*/dist-cdn
|
||||
packages/**/*/docs/search.json
|
||||
packages/**/*/src/components/icon/icons
|
||||
packages/**/*/src/react/index.ts
|
||||
**/*/package.json
|
||||
**/*/package-lock.json
|
||||
**/*/tsconfig.json
|
||||
**/*/tsconfig.prod.json
|
||||
node_modules
|
||||
package.json
|
||||
package-lock.json
|
||||
tsconfig.json
|
||||
cdn
|
||||
_site
|
||||
|
||||
packages/**/*/_site
|
||||
packages/**/*/_bundle_
|
||||
packages/webawesome/docs/assets/scripts/prism-downloaded.js
|
||||
|
||||
4
.vscode/extensions.json
vendored
@@ -3,7 +3,7 @@
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"bierner.lit-html",
|
||||
"bashmish.es6-string-css",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"ronnidc.nunjucks"
|
||||
]
|
||||
}
|
||||
|
||||
1
.vscode/settings.json
vendored
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"prettier.enable": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
Before contributing, please review the contributions guidelines at:
|
||||
|
||||
[shoelace.style/resources/contributing](https://shoelace.style/resources/contributing)
|
||||
[webawesome.com/docs/resources/contributing](https://webawesome.com/docs/resources/contributing)
|
||||
|
||||
57
README.md
@@ -1,7 +1,5 @@
|
||||
# Web Awesome
|
||||
|
||||
A forward-thinking library of web components.
|
||||
|
||||
- Works with all frameworks 🧩
|
||||
- Works with CDNs 🚛
|
||||
- Fully customizable with CSS 🎨
|
||||
@@ -13,29 +11,51 @@ Built by the folks behind [Font Awesome](https://fontawesome.com/).
|
||||
|
||||
---
|
||||
|
||||
Documentation: [shoelace.style](https://shoelace.style)
|
||||
Documentation: [webawesome.com](https://webawesome.com)
|
||||
|
||||
Source: [github.com/shoelace-style/shoelace](https://github.com/shoelace-style/shoelace)
|
||||
Source: [github.com/shoelace-style/webawesome](https://github.com/shoelace-style/webawesome)
|
||||
|
||||
Twitter: [@shoelace_style](https://twitter.com/shoelace_style)
|
||||
Twitter: [@webawesomer](https://twitter.com/webawesomer)
|
||||
|
||||
---
|
||||
|
||||
## Developers ✨
|
||||
|
||||
Developers can use this documentation to learn how to build Web Awesome from source. You will need Node >= 14.17 to build and run the project locally.
|
||||
Developers can use this documentation to learn how to build Web Awesome from source. You will need Node.js 14.17 or later to build and run the project locally.
|
||||
|
||||
**You don't need to do any of this to use Web Awesome!** This page is for people who want to contribute to the project, tinker with the source, or create a custom build of Web Awesome.
|
||||
|
||||
If that's not what you're trying to do, the [documentation website](https://shoelace.style) is where you want to be.
|
||||
If that's not what you're trying to do, the [documentation website](https://webawesome.com) is where you want to be.
|
||||
|
||||
### What are you using to build Web Awesome?
|
||||
|
||||
Components are built with [LitElement](https://lit-element.polymer-project.org/), a custom elements base class that provides an intuitive API and reactive data binding. The build is a custom script with bundling powered by [esbuild](https://esbuild.github.io/).
|
||||
Components are built with [Lit](https://lit.dev/), a custom elements base class that provides an intuitive API and reactive data binding. The build is a custom script with bundling powered by [esbuild](https://esbuild.github.io/).
|
||||
|
||||
### Understanding the Web Awesome monorepo
|
||||
|
||||
Web Awesome uses [npm workspaces](https://docs.npmjs.com/cli/v11/using-npm/workspaces) for its monorepo structure and is fairly minimal in what it provides.
|
||||
|
||||
By using npm workspaces and a monorepo structure, we can get consistent builds, shared configurations, and reduced duplication across repositories which reduces regressions and forces consistency across `webawesome`, `webawesome-pro`, and `webawesome-app`.
|
||||
|
||||
Generally, if you plan to only work with the free version of `webawesome` it is easiest to go to `packages/webawesome` and run all commands from there.
|
||||
|
||||
### Where do npm dependencies go?
|
||||
|
||||
Any dependencies intended to be used across all packages (i.e., `prettier`, `eslint`) that are **not** used at runtime should be in the root `devDependencies` of `package.json`.
|
||||
|
||||
```bash
|
||||
npm install -D -w prettier
|
||||
```
|
||||
|
||||
Any dependencies that will be used at runtime by a package should be part of the specific package's `dependencies` such as `lit`. This is required because if that dependency is not in the `packages/*/package.json`, it will not be installed when used via npm.
|
||||
|
||||
Individual packages are also free to install `devDependencies` as needed as long as they are specific to that package only.
|
||||
|
||||
To install a package specific to a Web Awesome package, change your working directory to that package's root (i.e., `cd packages/webawesome && npm install <package-name>`).
|
||||
|
||||
### Forking the Repo
|
||||
|
||||
Start by [forking the repo](https://github.com/shoelace-style/shoelace/fork) on GitHub, then clone it locally and install dependencies.
|
||||
Start by [forking the repo](https://github.com/shoelace-style/webawesome/fork) on GitHub, then clone it locally and install dependencies.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_GITHUB_USERNAME/webawesome
|
||||
@@ -45,36 +65,49 @@ npm install
|
||||
|
||||
### Developing
|
||||
|
||||
Once you've cloned the repo, run the following command.
|
||||
Once you've cloned the repo, run the following command from the respective directory within `packages/*`.
|
||||
|
||||
```bash
|
||||
cd packages/webawesome
|
||||
npm start
|
||||
```
|
||||
|
||||
This will spin up the dev server. After the initial build, a browser will open automatically. There is currently no hot module reloading (HMR), as browser's don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically.
|
||||
This will spin up the dev server. After the initial build, a browser will open automatically. There is currently no hot module reloading (HMR), as browsers don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically.
|
||||
|
||||
### Building
|
||||
|
||||
To generate a production build, run the following command.
|
||||
|
||||
```bash
|
||||
cd packages/webawesome
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can also run `npm run build:serve` to start an [`http-server`](https://www.npmjs.com/package/http-server) instance on `http://localhost:4000` after the build completes, so you can preview the production build.
|
||||
|
||||
### Creating New Components
|
||||
|
||||
To scaffold a new component, run the following command, replacing `wa-tag-name` with the desired tag name.
|
||||
|
||||
```bash
|
||||
cd packages/webawesome
|
||||
npm run create wa-tag-name
|
||||
```
|
||||
|
||||
This will generate a source file, a stylesheet, and a docs page for you. When you start the dev server, you'll find the new component in the "Components" section of the sidebar.
|
||||
|
||||
### Adding additional packages
|
||||
|
||||
Right now the only additional packages are in private repositories.
|
||||
|
||||
To add additional packages from other repositories, run `git clone <url> packages/<package-name>` to clone your repo into `packages/`.
|
||||
|
||||
Make sure to run `npm install` at the root of the monorepo after adding your package!
|
||||
|
||||
### Contributing
|
||||
|
||||
Web Awesome is an open source project and contributions are encouraged! If you're interesting in contributing, please review the [contribution guidelines](CONTRIBUTING.md) first.
|
||||
|
||||
## License
|
||||
|
||||
Web Awesome is available under the terms of the MIT license.
|
||||
Web Awesome is available under the terms of the [MIT License](LICENSE.md).
|
||||
|
||||
2
VERSIONS.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
3.0.0
|
||||
3.1.0
|
||||
61
cspell.json
@@ -5,27 +5,31 @@
|
||||
"allowfullscreen",
|
||||
"animationend",
|
||||
"Animista",
|
||||
"APG",
|
||||
"apos",
|
||||
"atrule",
|
||||
"autocapitalize",
|
||||
"autocorrect",
|
||||
"autofix",
|
||||
"autofocus",
|
||||
"autoload",
|
||||
"autoloader",
|
||||
"autoloading",
|
||||
"autoplay",
|
||||
"bezier",
|
||||
"Blockquotes",
|
||||
"boxicons",
|
||||
"CACHEABLE",
|
||||
"callout",
|
||||
"callouts",
|
||||
"cdndir",
|
||||
"canvastext",
|
||||
"chatbubble",
|
||||
"checkmark",
|
||||
"claviska",
|
||||
"Clippy",
|
||||
"codebases",
|
||||
"codepen",
|
||||
"colocated",
|
||||
"colorjs",
|
||||
"colour",
|
||||
"combobox",
|
||||
"Commonmark",
|
||||
@@ -40,14 +44,23 @@
|
||||
"crutchcorn",
|
||||
"csspart",
|
||||
"cssproperty",
|
||||
"cssstate",
|
||||
"datalist",
|
||||
"datetime",
|
||||
"describedby",
|
||||
"dictsort",
|
||||
"Docsify",
|
||||
"dogfood",
|
||||
"dropdowns",
|
||||
"easings",
|
||||
"ecommerce",
|
||||
"eleventy",
|
||||
"elif",
|
||||
"endfor",
|
||||
"endmarkdown",
|
||||
"endraw",
|
||||
"endregion",
|
||||
"endset",
|
||||
"enterkeyhint",
|
||||
"eqeqeq",
|
||||
"erroneou",
|
||||
@@ -55,7 +68,11 @@
|
||||
"esbuild",
|
||||
"exportmaps",
|
||||
"exportparts",
|
||||
"fetchpriority",
|
||||
"fieldsets",
|
||||
"focusin",
|
||||
"focusout",
|
||||
"fontawesome",
|
||||
"formaction",
|
||||
"formdata",
|
||||
"formenctype",
|
||||
@@ -64,29 +81,35 @@
|
||||
"formtarget",
|
||||
"FOUC",
|
||||
"FOUCE",
|
||||
"Frontmatter",
|
||||
"fullscreen",
|
||||
"gestern",
|
||||
"giga",
|
||||
"globby",
|
||||
"Grayscale",
|
||||
"groupby",
|
||||
"haspopup",
|
||||
"heroicons",
|
||||
"hexa",
|
||||
"Hotwire",
|
||||
"hrefs",
|
||||
"Iconoir",
|
||||
"Iframes",
|
||||
"iife",
|
||||
"inputmode",
|
||||
"ionicon",
|
||||
"ionicons",
|
||||
"jank",
|
||||
"jsDelivr",
|
||||
"jsfiddle",
|
||||
"jsonata",
|
||||
"keydown",
|
||||
"keyframes",
|
||||
"keymaker",
|
||||
"Kickstarter",
|
||||
"Konnor",
|
||||
"Kool",
|
||||
"labelledby",
|
||||
"Laravel",
|
||||
"LaViska",
|
||||
"linkify",
|
||||
"listbox",
|
||||
"listitem",
|
||||
@@ -95,24 +118,32 @@
|
||||
"lowercasing",
|
||||
"Lucide",
|
||||
"maxlength",
|
||||
"mdash",
|
||||
"Menlo",
|
||||
"menuitemcheckbox",
|
||||
"menuitemradio",
|
||||
"metaframeworks",
|
||||
"middlewares",
|
||||
"minlength",
|
||||
"minmax",
|
||||
"monospace",
|
||||
"mousedown",
|
||||
"mousemove",
|
||||
"mouseout",
|
||||
"mouseup",
|
||||
"multiselectable",
|
||||
"nbsp",
|
||||
"nextjs",
|
||||
"nocheck",
|
||||
"noindex",
|
||||
"noopener",
|
||||
"noreferrer",
|
||||
"noscript",
|
||||
"Notdog",
|
||||
"novalidate",
|
||||
"npmdir",
|
||||
"nowrap",
|
||||
"Numberish",
|
||||
"nums",
|
||||
"oklab",
|
||||
"oklch",
|
||||
"onscrollend",
|
||||
@@ -121,10 +152,13 @@
|
||||
"peta",
|
||||
"petabit",
|
||||
"Preact",
|
||||
"preconnect",
|
||||
"prerendered",
|
||||
"prismjs",
|
||||
"progressbar",
|
||||
"radiogroup",
|
||||
"Railsbyte",
|
||||
"referrerpolicy",
|
||||
"remixicon",
|
||||
"reregister",
|
||||
"resizer",
|
||||
@@ -141,23 +175,34 @@
|
||||
"scrollbars",
|
||||
"scrollend",
|
||||
"scroller",
|
||||
"Scrollers",
|
||||
"Segoe",
|
||||
"selectattr",
|
||||
"semibold",
|
||||
"shadowrootmode",
|
||||
"Shortcode",
|
||||
"Shortcodes",
|
||||
"signup",
|
||||
"sitedir",
|
||||
"slotchange",
|
||||
"smartquotes",
|
||||
"spacebar",
|
||||
"srcdoc",
|
||||
"stylesheet",
|
||||
"svgs",
|
||||
"Tabbable",
|
||||
"tabindex",
|
||||
"tabler",
|
||||
"tablist",
|
||||
"tabpanel",
|
||||
"tbody",
|
||||
"templating",
|
||||
"tera",
|
||||
"testid",
|
||||
"textareas",
|
||||
"textfield",
|
||||
"thead",
|
||||
"Themer",
|
||||
"tinycolor",
|
||||
"transitionend",
|
||||
"treeitem",
|
||||
@@ -167,18 +212,22 @@
|
||||
"typeof",
|
||||
"unbundles",
|
||||
"unbundling",
|
||||
"Uncategorized",
|
||||
"unicons",
|
||||
"unsanitized",
|
||||
"unsupportive",
|
||||
"valpha",
|
||||
"valuenow",
|
||||
"valuetext",
|
||||
"viewports",
|
||||
"Vuejs",
|
||||
"WCAG",
|
||||
"webawesome",
|
||||
"webawesomer",
|
||||
"WEBP",
|
||||
"Webpacker",
|
||||
"xmark"
|
||||
"xmark",
|
||||
"zoomable"
|
||||
],
|
||||
"ignorePaths": [
|
||||
"package.json",
|
||||
|
||||
@@ -1,349 +0,0 @@
|
||||
{% extends "default.njk" %}
|
||||
|
||||
{# Find the component based on the `tag` front matter #}
|
||||
{% set component = getComponent('wa-' + page.fileSlug) %}
|
||||
|
||||
{% block content %}
|
||||
{# Determine the badge variant #}
|
||||
{% if component.status == 'stable' %}
|
||||
{% set badgeVariant = 'brand' %}
|
||||
{% elseif component.status == 'experimental' %}
|
||||
{% set badgeVariant = 'warning' %}
|
||||
{% elseif component.status == 'planned' %}
|
||||
{% set badgeVariant = 'neutral' %}
|
||||
{% elseif component.status == 'deprecated' %}
|
||||
{% set badgeVariant = 'danger' %}
|
||||
{% else %}
|
||||
{% set badgeVariant = 'neutral' %}
|
||||
{% endif %}
|
||||
|
||||
{# Header #}
|
||||
<header class="component-header">
|
||||
<h1>{{ component.name | classNameToComponentName }}</h1>
|
||||
|
||||
<div class="component-header__tag">
|
||||
<code><{{ component.tagName }}> | {{ component.name }}</code>
|
||||
</div>
|
||||
|
||||
<div class="component-header__info">
|
||||
<wa-badge variant="neutral" pill>
|
||||
Since {{component.since or '?' }}
|
||||
</wa-badge>
|
||||
<wa-badge variant="{{ badgeVariant }}" pill style="text-transform: capitalize;">
|
||||
{{ component.status }}
|
||||
</wa-badge>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<p class="component-summary">
|
||||
{% if component.summary %}
|
||||
{{ component.summary | markdownInline | safe }}
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{# Markdown content #}
|
||||
{{ content | safe }}
|
||||
|
||||
{# Importing #}
|
||||
<h2>Importing</h2>
|
||||
<p>
|
||||
If you're using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use
|
||||
any of the following snippets to <a href="/getting-started/installation#cherry-picking">cherry pick</a> this component.
|
||||
</p>
|
||||
|
||||
<wa-tab-group>
|
||||
<wa-tab slot="nav" panel="script">Script</wa-tab>
|
||||
<wa-tab slot="nav" panel="import">Import</wa-tab>
|
||||
<wa-tab slot="nav" panel="bundler">Bundler</wa-tab>
|
||||
<wa-tab slot="nav" panel="react">React</wa-tab>
|
||||
|
||||
<wa-tab-panel name="script">
|
||||
<p>
|
||||
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
|
||||
using a script tag:
|
||||
</p>
|
||||
<pre><code class="language-html"><script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/{{ meta.cdndir }}/{{ component.path }}"></script></code></pre>
|
||||
</wa-tab-panel>
|
||||
|
||||
<wa-tab-panel name="import">
|
||||
<p>
|
||||
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
|
||||
using a JavaScript import:
|
||||
</p>
|
||||
<pre><code class="language-js">import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/{{ meta.cdndir }}/{{ component.path }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
|
||||
<wa-tab-panel name="bundler">
|
||||
<p>
|
||||
To import this component using <a href="{{ rootUrl('/getting-started/installation#bundling') }}">a bundler</a>:
|
||||
</p>
|
||||
<pre><code class="language-js">import '@shoelace-style/shoelace/{{ meta.npmdir }}/{{ component.path }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
|
||||
<wa-tab-panel name="react">
|
||||
<p>
|
||||
To import this component as a <a href="/frameworks/react">React component</a>:
|
||||
</p>
|
||||
<pre><code class="language-js">import {{ component.name }} from '@shoelace-style/shoelace/{{ meta.npmdir }}/react/{{ component.tagNameWithoutPrefix }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
{# Slots #}
|
||||
{% if component.slots.length %}
|
||||
<h2>Slots</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for slot in component.slots %}
|
||||
<tr>
|
||||
<td class="nowrap">
|
||||
{% if slot.name %}
|
||||
<code>{{ slot.name }}</code>
|
||||
{% else %}
|
||||
(default)
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ slot.description | markdownInline | safe }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#slots') }}">using slots</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Properties #}
|
||||
{% if component.properties.length %}
|
||||
<h2>Properties</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-reflects">Reflects</th>
|
||||
<th class="table-type">Type</th>
|
||||
<th class="table-default">Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for prop in component.properties %}
|
||||
<tr>
|
||||
<td>
|
||||
<code class="nowrap">{{ prop.name }}</code>
|
||||
{% if prop.attribute | length > 0 %}
|
||||
{% if prop.attribute != prop.name %}
|
||||
<br>
|
||||
<wa-tooltip content="This attribute is different from its property">
|
||||
<small>
|
||||
<code class="nowrap">
|
||||
{{ prop.attribute }}
|
||||
</code>
|
||||
</small>
|
||||
</wa-tooltip>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ prop.description | markdownInline | safe }}
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
{% if prop.reflects %}
|
||||
<wa-icon label="yes" name="check" variant="solid"></wa-icon>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if prop.type.text %}
|
||||
<code>{{ prop.type.text | trimPipes | markdownInline | safe }}</code>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if prop.default %}
|
||||
<code>{{ prop.default | markdownInline | safe }}</code>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td class="nowrap"><code>updateComplete</code></td>
|
||||
<td>
|
||||
A read-only promise that resolves when the component has
|
||||
<a href="/getting-started/usage?#component-rendering-and-updating">finished updating</a>.
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#attributes-and-properties') }}">attributes and properties</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Events #}
|
||||
{% if component.events.length %}
|
||||
<h2>Events</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name" data-flavor="html">Name</th>
|
||||
<th class="table-name" data-flavor="react">React Event</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-event-detail">Event Detail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for event in component.events %}
|
||||
<tr>
|
||||
<td data-flavor="html"><code class="nowrap">{{ event.name }}</code></td>
|
||||
<td data-flavor="react"><code class="nowrap">{{ event.reactName }}</code></td>
|
||||
<td>{{ event.description | markdownInline | safe }}</td>
|
||||
<td>
|
||||
{% if event.type.text %}
|
||||
<code>{{ event.type.text | trimPipes }}</code>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#events') }}">events</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Methods #}
|
||||
{% if component.methods.length %}
|
||||
<h2>Methods</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-arguments">Arguments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for method in component.methods %}
|
||||
<tr>
|
||||
<td class="nowrap"><code>{{ method.name }}()</code></td>
|
||||
<td>{{ method.description | markdownInline | safe }}</td>
|
||||
<td>
|
||||
{% if method.parameters.length %}
|
||||
<code>
|
||||
{% for param in method.parameters %}
|
||||
{{ param.name }}: {{ param.type.text | trimPipes }}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
</code>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#methods') }}">methods</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Custom Properties #}
|
||||
{% if component.cssProperties.length %}
|
||||
<h2>Custom Properties</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-default">Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for cssProperty in component.cssProperties %}
|
||||
<tr>
|
||||
<td class="nowrap"><code>{{ cssProperty.name }}</code></td>
|
||||
<td>{{ cssProperty.description | markdownInline | safe }}</td>
|
||||
<td>{{ cssProperty.default }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#custom-properties') }}">customizing CSS custom properties</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# CSS Parts #}
|
||||
{% if component.cssParts.length %}
|
||||
<h2>Parts</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for cssPart in component.cssParts %}
|
||||
<tr>
|
||||
<td class="nowrap"><code>{{ cssPart.name }}</code></td>
|
||||
<td>{{ cssPart.description | markdownInline | safe }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing/#css-parts') }}">customizing CSS parts</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Animations #}
|
||||
{% if component.animations.length %}
|
||||
<h2>Animations</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for animation in component.animations %}
|
||||
<tr>
|
||||
<td class="nowrap"><code>{{ animation.name }}</code></td>
|
||||
<td>{{ animation.description | markdownInline | safe }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing#animations') }}">customizing animations</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Dependencies #}
|
||||
{% if component.dependencies.length %}
|
||||
<h2>Dependencies</h2>
|
||||
|
||||
<p>This component automatically imports the following dependencies.</p>
|
||||
|
||||
<ul>
|
||||
{% for dependency in component.dependencies %}
|
||||
<li><code><{{ dependency }}></code></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,129 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html
|
||||
lang="en"
|
||||
data-layout="{{ layout }}"
|
||||
data-wa-version="{{ meta.version }}"
|
||||
>
|
||||
<head>
|
||||
{# Metadata #}
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="{{ meta.description }}" />
|
||||
<title>{{ meta.title }}</title>
|
||||
|
||||
{# Opt out of Turbo caching #}
|
||||
<meta name="turbo-cache-control">
|
||||
|
||||
{# Stylesheets #}
|
||||
<link rel="stylesheet" href="{{ assetUrl('styles/docs.css') }}" />
|
||||
<link rel="stylesheet" href="{{ assetUrl('styles/code-previews.css') }}" />
|
||||
<link rel="stylesheet" href="{{ assetUrl('styles/search.css') }}" />
|
||||
|
||||
{# Favicons #}
|
||||
<link rel="icon" href="{{ assetUrl('images/favicon.svg') }}" type="image/x-icon" />
|
||||
|
||||
{# Twitter Cards #}
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:creator" content="shoelace_style" />
|
||||
<meta name="twitter:image" content="{{ assetUrl(meta.image, true) }}" />
|
||||
|
||||
{# OpenGraph #}
|
||||
<meta property="og:url" content="{{ rootUrl(page.url, true) }}" />
|
||||
<meta property="og:title" content="{{ meta.title }}" />
|
||||
<meta property="og:description" content="{{ meta.description }}" />
|
||||
<meta property="og:image" content="{{ assetUrl(meta.image, true) }}" />
|
||||
|
||||
{# Web Awesome #}
|
||||
<link rel="stylesheet" href="/dist/themes/applied.css" />
|
||||
<link rel="stylesheet" href="/dist/themes/forms.css" />
|
||||
<link id="theme-stylesheet" rel="stylesheet" href="/dist/themes/default.css" />
|
||||
<script type="module" src="/dist/autoloader.js"></script>
|
||||
|
||||
{# Set the initial theme and menu states here to prevent flashing #}
|
||||
<script>
|
||||
(() => {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const theme = localStorage.getItem('theme') || 'auto';
|
||||
document.documentElement.classList.toggle('wa-theme-default-dark', theme === 'dark' || (theme === 'auto' && prefersDark));
|
||||
})();
|
||||
</script>
|
||||
|
||||
{# Web Fonts #}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Assistant:wght@200..800&family=Inter:wght@100..900&family=Lora:wght@400..700&family=Mulish:wght@200..1000&family=Noto+Sans+Display:wght@100..900&family=Noto+Sans+Mono:wght@100..900&family=Noto+Sans:wght@100..900&family=Noto+Serif:wght@100..900&family=Open+Sans:wght@300..800&family=Playfair+Display:wght@400..900&family=Playfair:opsz,wght@5..1200,300;5..1200,400;5..1200,500;5..1200,600&family=Quicksand:wght@300..700&family=Roboto+Flex:opsz,wght@8..144,300;8..144,400;8..144,500;8..144,600&family=Roboto+Mono:wght@300..700&family=Roboto+Serif:opsz,wght@8..144,300;8..144,400;8..144,500;8..144,600&family=Roboto+Slab:wght@300..700&display=swap" rel="stylesheet">
|
||||
|
||||
{# Turbo + Scroll positioning #}
|
||||
<script src="{{ assetUrl('scripts/turbo.js') }}" type="module"></script>
|
||||
<script src="{{ assetUrl('scripts/docs.js') }}" defer></script>
|
||||
<script src="{{ assetUrl('scripts/code-previews.js') }}" defer></script>
|
||||
<script src="{{ assetUrl('scripts/lunr.js') }}" defer></script>
|
||||
<script src="{{ assetUrl('scripts/search.js') }}" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a id="skip-to-main" class="wa-visually-hidden" href="#main-content" data-smooth-link="false">
|
||||
Skip to main content
|
||||
</a>
|
||||
|
||||
{# Menu toggle #}
|
||||
<button id="menu-toggle" type="button" aria-label="Menu">
|
||||
<svg width="148" height="148" viewBox="0 0 148 148" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="currentColor" stroke-width="18" fill="none" fill-rule="evenodd" stroke-linecap="round">
|
||||
<path d="M9.5 125.5h129M9.5 74.5h129M9.5 23.5h129"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<aside id="sidebar" data-preserve-scroll>
|
||||
<header>
|
||||
<a href="/">
|
||||
{% include 'logo.njk' %}
|
||||
</a>
|
||||
<div class="sidebar-version">
|
||||
{{ meta.version }}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="sidebar-buttons">
|
||||
<wa-button size="small" class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace" target="_blank">
|
||||
<wa-icon slot="prefix" name="github" family="brands"></wa-icon> Code
|
||||
</wa-button>
|
||||
<wa-button size="small" class="repo-button repo-button--star" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
|
||||
<wa-icon slot="prefix" name="star" variant="solid"></wa-icon> Star
|
||||
</wa-button>
|
||||
<wa-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
|
||||
<wa-icon slot="prefix" name="twitter" family="brands"></wa-icon> Follow
|
||||
</wa-button>
|
||||
</div>
|
||||
|
||||
<button class="search-box" type="button" title="Press / to search" aria-label="Search" data-plugin="search">
|
||||
<wa-icon name="search"></wa-icon>
|
||||
<span>Search</span>
|
||||
</button>
|
||||
|
||||
<nav>
|
||||
{% include 'sidebar.njk' %}
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
{# Content #}
|
||||
<main>
|
||||
<a id="main-content"></a>
|
||||
<article id="content" class="content{% if toc %} content--with-toc{% endif %}">
|
||||
{% if toc %}
|
||||
<div class="content__toc">
|
||||
<ul>
|
||||
<li class="top"><a href="#">{{ meta.title }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="content__body">
|
||||
{% block content %}
|
||||
{{ content | safe }}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 8.3 KiB |
@@ -1,85 +0,0 @@
|
||||
<ul>
|
||||
<li>
|
||||
<h2>Experimental</h2>
|
||||
<ul>
|
||||
<li><a href="/experimental/themer">Themer</a></li>
|
||||
<li><a href="/experimental/style-guide">Style Guide</a></li>
|
||||
<li><a href="/experimental/form-validation">Form Validation Styles</a></li>
|
||||
<li style="margin-top: .5rem;"><wa-switch id="theme-toggle">Dark mode</wa-switch></li>
|
||||
<script type="module">
|
||||
// Temporary dark toggle
|
||||
const toggle = document.getElementById('theme-toggle');
|
||||
toggle.checked = document.documentElement.classList.contains('wa-theme-default-dark');
|
||||
|
||||
toggle.addEventListener('wa-change', () => {
|
||||
document.documentElement.classList.toggle('wa-theme-default-dark');
|
||||
localStorage.setItem('theme', toggle.checked ? 'dark' : 'light');
|
||||
});
|
||||
</script>
|
||||
<li><a href="/experimental/sandbox">Sandbox</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Getting Started</h2>
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/getting-started/installation">Installation</a></li>
|
||||
<li><a href="/getting-started/usage">Usage</a></li>
|
||||
<li><a href="/getting-started/themes">Themes</a></li>
|
||||
<li><a href="/getting-started/customizing">Customizing</a></li>
|
||||
<li><a href="/getting-started/form-controls">Form Controls</a></li>
|
||||
<li><a href="/getting-started/localization">Localization</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Frameworks</h2>
|
||||
<ul>
|
||||
<li><a href="/frameworks/react">React</a></li>
|
||||
<li><a href="/frameworks/vue">Vue</a></li>
|
||||
<li><a href="/frameworks/angular">Angular</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Resources</h2>
|
||||
<ul>
|
||||
<li><a href="/resources/community">Community</a></li>
|
||||
<li><a href="https://github.com/shoelace-style/shoelace/discussions">Help & Support</a></li>
|
||||
<li><a href="/resources/accessibility">Accessibility</a></li>
|
||||
<li><a href="/resources/contributing">Contributing</a></li>
|
||||
<li><a href="/resources/changelog">Changelog</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Components</h2>
|
||||
<ul>
|
||||
{% for component in meta.components %}
|
||||
<li>
|
||||
<a href="/components/{{ component.tagName | removeWaPrefix }}">
|
||||
{{ component.name | classNameToComponentName }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Design Tokens</h2>
|
||||
<ul>
|
||||
<li><a href="/tokens/typography">Typography</a></li>
|
||||
<li><a href="/tokens/color">Color</a></li>
|
||||
<li><a href="/tokens/spacing">Spacing</a></li>
|
||||
<li><a href="/tokens/borders">Borders</a></li>
|
||||
<li><a href="/tokens/shadows">Shadows</a></li>
|
||||
<li><a href="/tokens/transition">Transition</a></li>
|
||||
<li><a href="/tokens/z-index">Z-index</a></li>
|
||||
<li><a href="/tokens/more">More Tokens</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Tutorials</h2>
|
||||
<ul>
|
||||
<li><a href="/tutorials/integrating-with-laravel">Integrating with Laravel</a></li>
|
||||
<li><a href="/tutorials/integrating-with-nextjs">Integrating with NextJS</a></li>
|
||||
<li><a href="/tutorials/integrating-with-rails">Integrating with Rails</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -1,35 +0,0 @@
|
||||
function normalizePathname(pathname) {
|
||||
// Remove /index.html
|
||||
if (pathname.endsWith('/index.html')) {
|
||||
pathname = pathname.replace(/\/index\.html/, '');
|
||||
}
|
||||
|
||||
// Remove trailing slashes
|
||||
return pathname.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a class name to links that are currently active.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
options = {
|
||||
className: 'active-link', // the class to add to active links
|
||||
pathname: undefined, // the current pathname to compare
|
||||
within: 'body', // element containing the target links
|
||||
...options
|
||||
};
|
||||
|
||||
const within = doc.querySelector(options.within);
|
||||
|
||||
if (!within) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
within.querySelectorAll('a').forEach(link => {
|
||||
if (normalizePathname(options.pathname) === normalizePathname(link.pathname)) {
|
||||
link.classList.add(options.className);
|
||||
}
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
const { createSlug } = require('./strings.cjs');
|
||||
|
||||
/**
|
||||
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
|
||||
* The same document will be returned with the appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
options = {
|
||||
levels: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], // the headings to convert
|
||||
className: 'anchor-heading', // the class name to add
|
||||
within: 'body', // the element containing the target headings
|
||||
...options
|
||||
};
|
||||
|
||||
const within = doc.querySelector(options.within);
|
||||
|
||||
if (!within) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
within.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => {
|
||||
const hasAnchor = heading.querySelector('a');
|
||||
const anchor = doc.createElement('a');
|
||||
let id = heading.textContent ?? '';
|
||||
let suffix = 0;
|
||||
|
||||
// Skip heading levels we don't care about
|
||||
if (!options.levels?.includes(heading.tagName.toLowerCase())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert dots to underscores
|
||||
id = id.replace(/\./g, '_');
|
||||
|
||||
// Turn it into a slug
|
||||
id = createSlug(id);
|
||||
|
||||
// Make sure it starts with a letter
|
||||
if (!/^[a-z]/i.test(id)) {
|
||||
id = `id_${id}`;
|
||||
}
|
||||
|
||||
// Make sure the id is unique
|
||||
const originalId = id;
|
||||
while (doc.getElementById(id) !== null) {
|
||||
id = `${originalId}-${++suffix}`;
|
||||
}
|
||||
|
||||
if (hasAnchor || !id) return;
|
||||
|
||||
heading.setAttribute('id', id);
|
||||
anchor.setAttribute('href', `#${encodeURIComponent(id)}`);
|
||||
anchor.setAttribute('aria-label', `Direct link to "${heading.textContent}"`);
|
||||
|
||||
if (options.className) {
|
||||
heading.classList.add(options.className);
|
||||
}
|
||||
|
||||
// Append the anchor
|
||||
heading.append(anchor);
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
@@ -1,138 +0,0 @@
|
||||
let count = 1;
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns code fields with the :preview suffix into interactive code previews.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
options = {
|
||||
within: 'body', // the element containing the code fields to convert
|
||||
...options
|
||||
};
|
||||
|
||||
const within = doc.querySelector(options.within);
|
||||
if (!within) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
within.querySelectorAll('[class*=":preview"]').forEach(code => {
|
||||
const pre = code.closest('pre');
|
||||
if (!pre) {
|
||||
return;
|
||||
}
|
||||
const adjacentPre = pre.nextElementSibling?.tagName.toLowerCase() === 'pre' ? pre.nextElementSibling : null;
|
||||
const reactCode = adjacentPre?.querySelector('code[class$="react"]');
|
||||
const sourceGroupId = `code-preview-source-group-${count}`;
|
||||
const isExpanded = code.getAttribute('class').includes(':expanded');
|
||||
const noCodePen = code.getAttribute('class').includes(':no-codepen');
|
||||
|
||||
count++;
|
||||
|
||||
const htmlButton = `
|
||||
<button type="button"
|
||||
title="Show HTML code"
|
||||
class="code-preview__button code-preview__button--html"
|
||||
>
|
||||
HTML
|
||||
</button>
|
||||
`;
|
||||
|
||||
const reactButton = `
|
||||
<button type="button" title="Show React code" class="code-preview__button code-preview__button--react">
|
||||
React
|
||||
</button>
|
||||
`;
|
||||
|
||||
const codePenButton = `
|
||||
<button type="button" class="code-preview__button code-preview__button--codepen" title="Edit on CodePen">
|
||||
<svg
|
||||
width="138"
|
||||
height="26"
|
||||
viewBox="0 0 138 26"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M80 6h-9v14h9 M114 6h-9 v14h9 M111 13h-6 M77 13h-6 M122 20V6l11 14V6 M22 16.7L33 24l11-7.3V9.3L33 2L22 9.3V16.7z M44 16.7L33 9.3l-11 7.4 M22 9.3l11 7.3 l11-7.3 M33 2v7.3 M33 16.7V24 M88 14h6c2.2 0 4-1.8 4-4s-1.8-4-4-4h-6v14 M15 8c-1.3-1.3-3-2-5-2c-4 0-7 3-7 7s3 7 7 7 c2 0 3.7-0.8 5-2 M64 13c0 4-3 7-7 7h-5V6h5C61 6 64 9 64 13z" />
|
||||
</svg>
|
||||
</button>
|
||||
`;
|
||||
|
||||
const codePreview = `
|
||||
<div class="code-preview ${isExpanded ? 'code-preview--expanded' : ''}">
|
||||
<div class="code-preview__preview">
|
||||
${code.textContent}
|
||||
<div class="code-preview__resizer">
|
||||
<wa-icon name="grip-vertical" variant="solid"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="code-preview__source-group" id="${sourceGroupId}">
|
||||
<div class="code-preview__source code-preview__source--html" ${reactCode ? 'data-flavor="html"' : ''}>
|
||||
<pre><code class="language-html">${escapeHtml(code.textContent)}</code></pre>
|
||||
</div>
|
||||
|
||||
${
|
||||
reactCode
|
||||
? `
|
||||
<div class="code-preview__source code-preview__source--react" data-flavor="react">
|
||||
<pre><code class="language-jsx">${escapeHtml(reactCode.textContent)}</code></pre>
|
||||
</div>
|
||||
`
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="code-preview__buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="code-preview__button code-preview__toggle"
|
||||
aria-expanded="${isExpanded ? 'true' : 'false'}"
|
||||
aria-controls="${sourceGroupId}"
|
||||
>
|
||||
Source
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
${reactCode ? ` ${htmlButton} ${reactButton} ` : ''}
|
||||
|
||||
${noCodePen ? '' : codePenButton}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
pre.insertAdjacentHTML('afterend', codePreview);
|
||||
pre.remove();
|
||||
|
||||
if (adjacentPre) {
|
||||
adjacentPre.remove();
|
||||
}
|
||||
});
|
||||
|
||||
// Wrap code preview scripts in anonymous functions so they don't run in the global scope
|
||||
doc.querySelectorAll('.code-preview__preview script').forEach(script => {
|
||||
if (script.type === 'module') {
|
||||
// Modules are already scoped
|
||||
script.textContent = script.innerHTML;
|
||||
} else {
|
||||
// Wrap non-modules in an anonymous function so they don't run in the global scope
|
||||
script.textContent = `(() => { ${script.innerHTML} })();`;
|
||||
}
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
let codeBlockId = 0;
|
||||
|
||||
/**
|
||||
* Adds copy code buttons to code fields. The provided doc should be a document object provided by JSDOM. The same
|
||||
* document will be returned with the appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc) {
|
||||
doc.querySelectorAll('pre > code').forEach(code => {
|
||||
const pre = code.closest('pre');
|
||||
const button = doc.createElement('wa-copy-button');
|
||||
|
||||
if (!code.id) {
|
||||
code.id = `code-block-${++codeBlockId}`;
|
||||
}
|
||||
|
||||
button.classList.add('copy-code-button');
|
||||
button.setAttribute('from', code.id);
|
||||
|
||||
pre.append(button);
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
@@ -1,41 +0,0 @@
|
||||
const { isExternalLink } = require('./strings.cjs');
|
||||
|
||||
/**
|
||||
* Transforms external links to make them safer and optionally add a target. The provided doc should be a document
|
||||
* object provided by JSDOM. The same document will be returned with the appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
options = {
|
||||
className: 'external-link', // the class name to add to links
|
||||
noopener: true, // sets rel="noopener"
|
||||
noreferrer: true, // sets rel="noreferrer"
|
||||
ignore: () => false, // callback function to filter links that should be ignored
|
||||
within: 'body', // element that contains the target links
|
||||
target: '', // sets the target attribute
|
||||
...options
|
||||
};
|
||||
|
||||
const within = doc.querySelector(options.within);
|
||||
|
||||
if (within) {
|
||||
within.querySelectorAll('a').forEach(link => {
|
||||
if (isExternalLink(link) && !options.ignore(link)) {
|
||||
link.classList.add(options.className);
|
||||
|
||||
const rel = [];
|
||||
if (options.noopener) rel.push('noopener');
|
||||
if (options.noreferrer) rel.push('noreferrer');
|
||||
|
||||
if (rel.length) {
|
||||
link.setAttribute('rel', rel.join(' '));
|
||||
}
|
||||
|
||||
if (options.target) {
|
||||
link.setAttribute('target', options.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return doc;
|
||||
};
|
||||
@@ -1,63 +0,0 @@
|
||||
const Prism = require('prismjs');
|
||||
const PrismLoader = require('prismjs/components/index.js');
|
||||
|
||||
PrismLoader('diff');
|
||||
PrismLoader.silent = true;
|
||||
|
||||
/** Highlights a code string. */
|
||||
function highlight(code, language) {
|
||||
const alias = language.replace(/^diff-/, '');
|
||||
const isDiff = /^diff-/i.test(language);
|
||||
|
||||
// Auto-load the target language
|
||||
if (!Prism.languages[alias]) {
|
||||
PrismLoader(alias);
|
||||
|
||||
if (!Prism.languages[alias]) {
|
||||
throw new Error(`Unsupported language for code highlighting: "${language}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// Register diff-* languages to use the diff grammar
|
||||
if (isDiff) {
|
||||
Prism.languages[language] = Prism.languages.diff;
|
||||
}
|
||||
|
||||
return Prism.highlight(code, Prism.languages[language], language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights all code fields that have a language parameter. If the language has a colon in its name, the first chunk
|
||||
* will be the language used and additional chunks will be applied as classes to the `<pre>`. For example, a code field
|
||||
* tagged with "html:preview" will be rendered as `<pre class="language-html preview">`.
|
||||
*
|
||||
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
|
||||
* appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc) {
|
||||
doc.querySelectorAll('pre > code[class]').forEach(code => {
|
||||
// Look for class="language-*" and split colons into separate classes
|
||||
code.classList.forEach(className => {
|
||||
if (className.startsWith('language-')) {
|
||||
//
|
||||
// We use certain suffixes to indicate code previews, expanded states, etc. The class might look something like
|
||||
// this:
|
||||
//
|
||||
// class="language-html:preview:expanded"
|
||||
//
|
||||
// The language will always come first, so we need to drop the "language-" prefix and everything after the first
|
||||
// color to get the highlighter language.
|
||||
//
|
||||
const language = className.replace(/^language-/, '').split(':')[0];
|
||||
|
||||
try {
|
||||
code.innerHTML = highlight(code.textContent ?? '', language);
|
||||
} catch (err) {
|
||||
// Language not found, skip it
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
@@ -1,75 +0,0 @@
|
||||
const MarkdownIt = require('markdown-it');
|
||||
const markdownItContainer = require('markdown-it-container');
|
||||
const markdownItIns = require('markdown-it-ins');
|
||||
const markdownItKbd = require('markdown-it-kbd');
|
||||
const markdownItMark = require('markdown-it-mark');
|
||||
const markdownItReplaceIt = require('markdown-it-replace-it');
|
||||
|
||||
const markdown = MarkdownIt({
|
||||
html: true,
|
||||
xhtmlOut: false,
|
||||
breaks: false,
|
||||
langPrefix: 'language-',
|
||||
linkify: false,
|
||||
typographer: false
|
||||
});
|
||||
|
||||
// Third-party plugins
|
||||
markdown.use(markdownItContainer);
|
||||
markdown.use(markdownItIns);
|
||||
markdown.use(markdownItKbd);
|
||||
markdown.use(markdownItMark);
|
||||
markdown.use(markdownItReplaceIt);
|
||||
|
||||
// Callouts
|
||||
['tip', 'warning', 'danger'].forEach(type => {
|
||||
const variant = type === 'tip' ? 'brand' : type;
|
||||
let icon = 'circle-info';
|
||||
if (type === 'warning') icon = 'triangle-exclamation';
|
||||
if (type === 'danger') icon = 'circle-exclamation';
|
||||
|
||||
markdown.use(markdownItContainer, type, {
|
||||
render: function (tokens, idx) {
|
||||
if (tokens[idx].nesting === 1) {
|
||||
return `
|
||||
<wa-alert class="callout" variant="${variant}" open>
|
||||
<wa-icon slot="icon" name="${icon}" variant="regular"></wa-icon>
|
||||
`;
|
||||
}
|
||||
return '</wa-alert>\n';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Asides
|
||||
markdown.use(markdownItContainer, 'aside', {
|
||||
render: function (tokens, idx) {
|
||||
if (tokens[idx].nesting === 1) {
|
||||
return `<aside>`;
|
||||
}
|
||||
return '</aside>\n';
|
||||
}
|
||||
});
|
||||
|
||||
// Details
|
||||
markdown.use(markdownItContainer, 'details', {
|
||||
validate: params => params.trim().match(/^details\s+(.*)$/),
|
||||
render: (tokens, idx) => {
|
||||
const m = tokens[idx].info.trim().match(/^details\s+(.*)$/);
|
||||
if (tokens[idx].nesting === 1) {
|
||||
return `<details>\n<summary><span>${markdown.utils.escapeHtml(m[1])}</span></summary>\n`;
|
||||
}
|
||||
return '</details>\n';
|
||||
}
|
||||
});
|
||||
|
||||
// Replace [#1234] with a link to GitHub issues
|
||||
markdownItReplaceIt.replacements.push({
|
||||
name: 'github-issues',
|
||||
re: /\[#([0-9]+)\]/gs,
|
||||
sub: '<a href="https://github.com/shoelace-style/shoelace/issues/$1">#$1</a>',
|
||||
html: true,
|
||||
default: true
|
||||
});
|
||||
|
||||
module.exports = markdown;
|
||||
@@ -1,26 +0,0 @@
|
||||
const { format } = require('prettier');
|
||||
|
||||
/** Formats markup using prettier. */
|
||||
module.exports = function (content, options) {
|
||||
options = {
|
||||
arrowParens: 'avoid',
|
||||
bracketSpacing: true,
|
||||
htmlWhitespaceSensitivity: 'css',
|
||||
insertPragma: false,
|
||||
bracketSameLine: false,
|
||||
jsxSingleQuote: false,
|
||||
parser: 'html',
|
||||
printWidth: 120,
|
||||
proseWrap: 'preserve',
|
||||
quoteProps: 'as-needed',
|
||||
requirePragma: false,
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
trailingComma: 'none',
|
||||
useTabs: false,
|
||||
...options
|
||||
};
|
||||
|
||||
return format(content, options);
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* @typedef {object} Replacement
|
||||
* @property {string | RegExp} pattern
|
||||
* @property {string} replacement
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Array<Replacement>} Replacements
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Document} content
|
||||
* @param {Replacements} replacements
|
||||
*/
|
||||
module.exports = function (content, replacements) {
|
||||
/** This seems trivial, but by assigning to a string first, THEN using innerHTML after iterating over every replacement, we reduce the calculations of JSDOM. At the time of writing benchmarks show a reduction from 9seconds to 3 seconds by doing so. */
|
||||
let html = content.body.innerHTML;
|
||||
|
||||
replacements.forEach(replacement => {
|
||||
html = html.replaceAll(replacement.pattern, replacement.replacement);
|
||||
});
|
||||
|
||||
content.body.innerHTML = html;
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
|
||||
* The same document will be returned with the appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
const tables = [...doc.querySelectorAll('table')];
|
||||
|
||||
options = {
|
||||
className: 'table-scroll', // the class name to add to the table's container
|
||||
...options
|
||||
};
|
||||
|
||||
tables.forEach(table => {
|
||||
const div = doc.createElement('div');
|
||||
div.classList.add(options.className);
|
||||
table.insertAdjacentElement('beforebegin', div);
|
||||
div.append(table);
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
const slugify = require('slugify');
|
||||
|
||||
/** Creates a slug from an arbitrary string of text. */
|
||||
module.exports.createSlug = function (text) {
|
||||
return slugify(String(text), {
|
||||
remove: /[^\w|\s]/g,
|
||||
lower: true
|
||||
});
|
||||
};
|
||||
|
||||
/** Determines whether or not a link is external. */
|
||||
module.exports.isExternalLink = function (link) {
|
||||
// We use the "internal" hostname when initializing JSDOM so we know that those are local links
|
||||
if (!link.hostname || link.hostname === 'internal') return false;
|
||||
return true;
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Generates an in-page table of contents based on headings.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
options = {
|
||||
levels: ['h2'], // headings to include (they must have an id)
|
||||
container: 'nav', // the container to append links to
|
||||
listItem: true, // if true, links will be wrapped in <li>
|
||||
within: 'body', // the element containing the headings to summarize
|
||||
...options
|
||||
};
|
||||
|
||||
const container = doc.querySelector(options.container);
|
||||
const within = doc.querySelector(options.within);
|
||||
const headingSelector = options.levels.map(h => `${h}[id]`).join(', ');
|
||||
|
||||
if (!container || !within) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
within.querySelectorAll(headingSelector).forEach(heading => {
|
||||
const listItem = doc.createElement('li');
|
||||
const link = doc.createElement('a');
|
||||
const level = heading.tagName.slice(1);
|
||||
|
||||
link.href = `#${heading.id}`;
|
||||
link.textContent = heading.textContent;
|
||||
|
||||
if (options.listItem) {
|
||||
// List item + link
|
||||
listItem.setAttribute('data-level', level);
|
||||
listItem.append(link);
|
||||
container.append(listItem);
|
||||
} else {
|
||||
// Link only
|
||||
link.setAttribute('data-level', level);
|
||||
container.append(link);
|
||||
}
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
const smartquotes = require('smartquotes');
|
||||
|
||||
smartquotes.replacements.push([/---/g, '\u2014']); // em dash
|
||||
smartquotes.replacements.push([/--/g, '\u2013']); // en dash
|
||||
smartquotes.replacements.push([/\.\.\./g, '\u2026']); // ellipsis
|
||||
smartquotes.replacements.push([/\(c\)/gi, '\u00A9']); // copyright
|
||||
smartquotes.replacements.push([/\(r\)/gi, '\u00AE']); // registered trademark
|
||||
smartquotes.replacements.push([/\?!/g, '\u2048']); // ?!
|
||||
smartquotes.replacements.push([/!!/g, '\u203C']); // !!
|
||||
smartquotes.replacements.push([/\?\?/g, '\u2047']); // ??
|
||||
smartquotes.replacements.push([/([0-9]\s?)-(\s?[0-9])/g, '$1\u2013$2']); // number ranges use en dash
|
||||
|
||||
/**
|
||||
* Improves typography by adding smart quotes and similar corrections within the specified element(s).
|
||||
*
|
||||
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
|
||||
* appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc, selector = 'body') {
|
||||
const elements = [...doc.querySelectorAll(selector)];
|
||||
elements.forEach(el => smartquotes.element(el));
|
||||
return doc;
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="186" height="186" viewBox="0 0 186 186"><g fill="none" fill-rule="evenodd"><rect width="186" height="186" fill="#103257" opacity="0"/><path fill="#F6894C" d="M106.95,13.9672306 C106.95,19.1752428 104.10296,23.7175103 99.8823543,26.1190227 L130.2,48.8851296 L159.91784,39.4892184 C158.760743,37.4541707 158.1,35.0993755 158.1,32.5902046 C158.1,24.8763205 164.345703,18.6229741 172.05,18.6229741 C179.754297,18.6229741 186,24.8763205 186,32.5902046 C186,40.3040179 179.754297,46.5574352 172.05,46.5574352 C171.315566,46.5574352 170.594594,46.5006795 169.890983,46.39107 L137.151086,130.163238 C134.361086,137.302399 127.486526,142 119.830057,142 L66.1699429,142 C58.5134743,142 51.6389143,137.302399 48.8489143,130.163238 L16.1089463,46.39107 C15.4052994,46.5006795 14.6842926,46.5574352 13.95,46.5574352 C6.245632,46.5574352 0,40.3040179 0,32.5902046 C0,24.8763205 6.245632,18.6229741 13.95,18.6229741 C21.654368,18.6229741 27.9,24.8763205 27.9,32.5902046 C27.9,35.0993755 27.2391509,37.4541707 26.0822663,39.4892184 L55.8,48.8851296 L86.1176457,26.1190227 C81.89704,23.7175103 79.05,19.1752428 79.05,13.9672306 C79.05,6.25334639 85.2957029,0 93,0 C100.704297,0 106.95,6.25334639 106.95,13.9672306 Z" transform="translate(0 22)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,23 +0,0 @@
|
||||
<svg width="160" height="45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d)">
|
||||
<rect x="2" y="2" width="156" height="40" rx="16" fill="#F9F9F9"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.425 11.174c.604 1.114.233 2.53-.83 3.164l-6.986 4.166a.378.378 0 00-.18.325v6.748c0 .134.069.258.18.325l5.714 3.407c.11.066.244.066.354 0l5.714-3.407a.378.378 0 00.18-.325V21.29l-4.986 2.936c-1.067.628-2.416.231-3.015-.886-.6-1.118-.22-2.532.846-3.16l7.008-4.127c2.048-1.206 4.576.345 4.576 2.806v6.718c0 1.803-.924 3.467-2.42 4.36l-5.713 3.407a4.596 4.596 0 01-4.734 0l-5.714-3.408C18.924 29.044 18 27.38 18 25.576V18.83c0-1.803.924-3.467 2.42-4.36l6.985-4.165c1.063-.634 2.415-.245 3.02.87z" fill="url(#paint0_linear)"/>
|
||||
<path fill="#F9F9F9" d="M47 12.5h95v-1H47z"/>
|
||||
<path d="M52.538 27.752c2.744 0 4.844-1.876 4.844-5.152 0-3.108-2.1-5.152-4.844-5.152s-4.802 2.002-4.802 5.152c0 3.29 2.058 5.152 4.802 5.152zm0-1.554c-1.736 0-2.912-1.316-2.912-3.598 0-2.226 1.162-3.598 2.912-3.598s2.954 1.4 2.954 3.598c0 2.31-1.218 3.598-2.954 3.598zm7.89 4.158V27.22c0-.196-.013-.378-.055-.658.434.7 1.022 1.19 2.17 1.19 1.736 0 3.066-1.414 3.066-3.626 0-2.17-1.19-3.682-2.996-3.682-1.078 0-1.806.476-2.24 1.204.042-.28.056-.462.056-.672v-.308H58.72v9.688h1.708zm1.695-3.948c-1.036 0-1.764-.938-1.764-2.31 0-1.414.742-2.296 1.764-2.296 1.092 0 1.75.952 1.75 2.296 0 1.372-.7 2.31-1.75 2.31zm7.866 1.344c1.848 0 3.052-1.078 3.192-2.478h-1.736c-.112.714-.714 1.134-1.456 1.134-1.036 0-1.722-.826-1.736-1.904h4.97v-.378c0-2.226-1.204-3.682-3.29-3.682-1.988 0-3.43 1.47-3.43 3.626 0 2.366 1.442 3.682 3.486 3.682zm-1.75-4.48c.098-.896.756-1.554 1.68-1.554.924 0 1.526.63 1.554 1.554H68.24zm8.006 4.228v-4.004c0-.952.616-1.694 1.456-1.694.798 0 1.288.63 1.288 1.638v4.06h1.708v-4.312c0-1.68-.896-2.744-2.408-2.744-1.078 0-1.722.518-2.1 1.204.042-.266.056-.476.056-.672v-.308h-1.708V27.5h1.708zm8.911-7.868h1.792V17.84h-1.792v1.792zm.042 1.036V27.5h1.708v-6.832h-1.708zm5.097 6.832v-4.004c0-.952.616-1.694 1.456-1.694.798 0 1.288.63 1.288 1.638v4.06h1.708v-4.312c0-1.68-.896-2.744-2.408-2.744-1.078 0-1.722.518-2.1 1.204.042-.266.056-.476.056-.672v-.308h-1.708V27.5h1.708zm13.238.252c1.526 0 2.52-.658 2.982-1.54-.07.322-.098.644-.098.98v.308h1.68v-5.222h-4.34v1.554h2.66v.07c0 1.4-1.134 2.296-2.59 2.296-1.792 0-3.024-1.372-3.024-3.598s1.26-3.598 3.066-3.598c1.302 0 2.17.756 2.296 1.736h1.89c-.182-1.904-1.764-3.29-4.214-3.29-2.954 0-4.928 2.128-4.928 5.152 0 3.136 1.848 5.152 4.62 5.152zm6.063-8.12h1.792V17.84h-1.792v1.792zm.042 1.036V27.5h1.708v-6.832h-1.708zm6.413 6.958c.434 0 .84-.07 1.008-.126v-1.288c-.168.028-.35.042-.518.042-.728 0-1.008-.42-1.008-1.134v-3.094h1.68v-1.358h-1.68v-2.464h-1.708v2.464h-1.554v1.358h1.554v3.346c0 1.526.77 2.254 2.226 2.254zm3.961 2.73V27.22c0-.196-.014-.378-.056-.658.434.7 1.022 1.19 2.17 1.19 1.736 0 3.066-1.414 3.066-3.626 0-2.17-1.19-3.682-2.996-3.682-1.078 0-1.806.476-2.24 1.204.042-.28.056-.462.056-.672v-.308h-1.708v9.688h1.708zm1.694-3.948c-1.036 0-1.764-.938-1.764-2.31 0-1.414.742-2.296 1.764-2.296 1.092 0 1.75.952 1.75 2.296 0 1.372-.7 2.31-1.75 2.31zm7.88 1.344c2.058 0 3.514-1.372 3.514-3.64 0-2.24-1.456-3.668-3.514-3.668-2.044 0-3.5 1.428-3.5 3.668 0 2.268 1.442 3.64 3.5 3.64zm0-1.344c-1.064 0-1.764-.84-1.764-2.296 0-1.484.728-2.31 1.764-2.31 1.05 0 1.778.826 1.778 2.31 0 1.456-.714 2.296-1.778 2.296zm7.551 1.344c1.26 0 1.876-.686 2.142-1.19-.056.238-.056.42-.056.658v.28h1.708v-9.8h-1.708v3.276c0 .21 0 .42.056.672-.392-.658-1.05-1.204-2.114-1.204-1.596 0-3.15 1.218-3.15 3.668 0 2.408 1.316 3.64 3.122 3.64zm.406-1.344c-1.022 0-1.792-.896-1.792-2.31 0-1.358.77-2.296 1.792-2.296s1.778.896 1.778 2.296c0 1.372-.756 2.31-1.778 2.31z" fill="#12100C"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="33.806" y1="13.629" x2="22.389" y2="30.86" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFB45B"/>
|
||||
<stop offset="1" stop-color="#FF8A00"/>
|
||||
</linearGradient>
|
||||
<filter id="filter0_d" x="0" y=".5" width="160" height="44" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dy=".5"/>
|
||||
<feGaussianBlur stdDeviation="1"/>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feBlend in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 55 KiB |
@@ -1,80 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="879px" height="553px" viewBox="0 0 879 553" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
|
||||
<title>undraw-content-team</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="undraw-content-team" transform="translate(-3.000000, 0.000000)">
|
||||
<rect id="Rectangle" fill-opacity="0.25" fill="#fff" x="180" y="16" width="579" height="343" rx="20"></rect>
|
||||
<path d="M840.86295,552.21468 L98.22937,552.21468 C84.4769827,552.21468 73.32847,541.066167 73.32847,527.31378 L73.32847,527.31378 C342.533003,496.02706 606.678133,496.02706 865.76386,527.31378 L865.76386,527.31378 C865.763854,541.066169 854.615339,552.21468 840.86295,552.21468 Z" id="Path" fill="#CCCCCC" fill-rule="nonzero"></path>
|
||||
<path d="M865.76386,528.77851 L73.32847,527.31375 L165.16896,372.78153 L165.60837,372.04915 L165.60837,31.47769 C165.607672,23.1290737 168.923838,15.1221947 174.827202,9.21883153 C180.730565,3.31546831 188.737444,-0.00069762467 197.08606,2.84217094e-14 L737.61198,2.84217094e-14 C745.960596,-0.00069762467 753.967475,3.31546831 759.870838,9.21883153 C765.774202,15.1221947 769.090368,23.1290737 769.08967,31.47769 L769.08967,374.39269 L865.76386,528.77851 Z M196.36834,21.97141 C190.70822,21.9777587 186.121369,26.5646095 186.11502,32.22473 L186.11502,342.75394 C186.121622,348.413956 190.708324,353.000658 196.36834,353.00726 L742.72398,353.00726 C748.383893,353.000411 752.970451,348.413853 752.9773,342.75394 L752.9773,32.22473 C752.970704,26.5647122 748.383998,21.9780061 742.72398,21.97141 L196.36834,21.97141 Z" id="Combined-Shape" fill="#E6E6E6" fill-rule="nonzero"></path>
|
||||
<path d="M199.95903,394.02055 C198.24391,394.023463 196.686317,395.021305 195.9667,396.57816 L167.66779,458.0981 C167.041726,459.458563 167.151668,461.044211 167.959498,462.305253 C168.767328,463.566295 170.161797,464.32907 171.6594,464.3291 L766.77054,464.3291 C768.293506,464.329083 769.707849,463.540407 770.508355,462.244793 C771.308862,460.949179 771.381351,459.331427 770.69993,457.96941 L739.93993,396.44947 C739.199697,394.957483 737.676056,394.015676 736.01054,394.0206 L199.95903,394.02055 Z" id="Path" fill="#CCCCCC" fill-rule="nonzero"></path>
|
||||
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="467.34902" cy="10.25332" r="6.59142"></circle>
|
||||
<path d="M408.85444,476.04714 C406.86865,476.049642 405.130517,477.381666 404.61179,479.29851 L397.51328,505.66419 C397.15753,506.984241 397.436508,508.39467 398.26801,509.479887 C399.099513,510.565104 400.388783,511.201438 401.75593,511.20139 L536.75349,511.20139 C538.174875,511.201399 539.508438,510.513737 540.332692,509.355747 C541.156947,508.197757 541.370032,506.712543 540.9046,505.36952 L531.77845,479.00384 C531.165175,477.234516 529.498505,476.04781 527.62591,476.04714 L408.85444,476.04714 Z" id="Path" fill="#CCCCCC" fill-rule="nonzero"></path>
|
||||
<polygon id="Path" fill="#CCCCCC" fill-rule="nonzero" points="769.09 366.922 769.09 372.782 165.169 372.782 165.623 372.049 165.623 366.922"></polygon>
|
||||
<path d="M694.54732,380.34397 C694.063752,380.344578 693.640491,380.668942 693.51417,381.13572 L691.78557,387.55617 C691.698952,387.877623 691.766894,388.221078 691.969377,388.485341 C692.17186,388.749605 692.485812,388.904563 692.81873,388.90456 L725.69267,388.90456 C726.038798,388.904561 726.36354,388.737104 726.564256,388.455115 C726.764973,388.173127 726.816861,387.811455 726.70352,387.48441 L724.48117,381.06397 C724.331826,380.633113 723.925966,380.344133 723.46996,380.34397 L694.54732,380.34397 Z" id="Path" fill="#CCCCCC" fill-rule="nonzero"></path>
|
||||
<path d="M646.54732,380.34397 C646.063752,380.344578 645.640491,380.668942 645.51417,381.13572 L643.78557,387.55617 C643.698952,387.877623 643.766894,388.221078 643.969377,388.485341 C644.17186,388.749605 644.485812,388.904563 644.81873,388.90456 L677.69267,388.90456 C678.038798,388.904561 678.36354,388.737104 678.564256,388.455115 C678.764973,388.173127 678.816861,387.811455 678.70352,387.48441 L676.48117,381.06397 C676.331826,380.633113 675.925966,380.344133 675.46996,380.34397 L646.54732,380.34397 Z" id="Path" fill="#CCCCCC" fill-rule="nonzero"></path>
|
||||
<path d="M507.48137,421.13959 L510.05784,421.13959 L504.90484,76.75135 L502.32836,76.75135 L502.43115,83.62195 L444.68449,83.62195 L444.78727,76.75135 L442.2108,76.75135 L437.0578,421.13959 L439.63428,421.13959 L439.96839,398.81019 L507.14722,398.81019 L507.48137,421.13959 Z M503.76762,172.93959 L443.34807,172.93959 L443.9777,130.85725 L503.13798,130.85725 L503.76762,172.93959 Z M503.8062,175.51607 L504.43583,217.5984 L442.67983,217.5984 L443.30947,175.51607 L503.8062,175.51607 Z M504.47441,220.17488 L505.10405,262.25722 L442.01164,262.25722 L442.64128,220.17488 L504.47441,220.17488 Z M505.14262,264.83369 L505.77226,306.91603 L441.34343,306.91603 L441.97307,264.83369 L505.14262,264.83369 Z M505.81083,309.49251 L506.44047,351.5749 L440.67517,351.5749 L441.30481,309.49251 L505.81083,309.49251 Z M502.46977,86.19843 L503.09941,128.28077 L444.01628,128.28077 L444.64592,86.19843 L502.46977,86.19843 Z M440.007,396.23371 L440.63664,354.15137 L506.47905,354.15137 L507.10869,396.23371 L440.007,396.23371 Z" id="Shape" fill="#3F3D56" fill-rule="nonzero"></path>
|
||||
<circle id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="460.7944" cy="153.35761" r="42.01233"></circle>
|
||||
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" transform="translate(481.263536, 194.779340) rotate(-10.261060) translate(-481.263536, -194.779340) " x="474.881916" y="183.345595" width="12.76324" height="22.86749"></rect>
|
||||
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" transform="translate(456.145380, 199.326392) rotate(-10.261060) translate(-456.145380, -199.326392) " x="449.76376" y="187.892647" width="12.76324" height="22.86749"></rect>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(455.991944, 213.405824) rotate(-40.261060) translate(-455.991944, -213.405824) " cx="455.991944" cy="213.405824" rx="10.63602" ry="3.98853"></ellipse>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(480.586874, 208.953401) rotate(-40.261060) translate(-480.586874, -208.953401) " cx="480.586874" cy="208.953401" rx="10.63605" ry="3.98853"></ellipse>
|
||||
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="457.85318" cy="143.08115" r="14.35864"></circle>
|
||||
<circle id="Oval" fill="#3F3D56" fill-rule="nonzero" cx="451.95181" cy="137.93436" r="4.78622"></circle>
|
||||
<path d="M493.93625,107.65269 C494.56802,92.0991 481.16312,78.92509 463.99545,78.22776 C446.82778,77.53043 432.39853,89.57376 431.76672,105.12736 C431.13491,120.68096 443.06863,124.21482 460.23627,124.91221 C477.40391,125.6096 493.30447,123.20627 493.93625,107.65269 Z" id="Path" fill="#0ea5e9" fill-rule="nonzero"></path>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(422.001785, 145.703659) rotate(-77.089900) translate(-422.001785, -145.703659) " cx="422.001785" cy="145.703659" rx="6.59448" ry="21.00616"></ellipse>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(504.470422, 159.418119) rotate(-77.089900) translate(-504.470422, -159.418119) " cx="504.470422" cy="159.418119" rx="6.59448" ry="21.00616"></ellipse>
|
||||
<path d="M472.3331,169.91416 C472.9488,173.280144 471.720947,176.718794 469.112518,178.933525 C466.504089,181.148255 462.911834,181.802199 459.690258,180.648779 C456.468682,179.495358 454.107795,176.710012 453.4978,173.34299 L453.49445,173.3245 C452.5527,168.12235 456.57483,166.2815 461.77701,165.33976 C466.97919,164.39802 471.39139,164.71201 472.3331,169.91416 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(312.185926, 293.633672) rotate(-69.082170) translate(-312.185926, -293.633672) " cx="312.185926" cy="293.633672" rx="21.53369" ry="6.76007"></ellipse>
|
||||
<circle id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(272.378778, 326.155721) rotate(-80.782520) translate(-272.378778, -326.155721) " cx="272.378778" cy="326.155721" r="43.06735"></circle>
|
||||
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="252.75315" y="359.95537" width="13.08374" height="23.44171"></rect>
|
||||
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="278.92063" y="359.95537" width="13.08374" height="23.44171"></rect>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="263.65629" cy="383.66967" rx="10.90314" ry="4.08868"></ellipse>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="289.82377" cy="383.12453" rx="10.90314" ry="4.08868"></ellipse>
|
||||
<path d="M230.60203,286.10434 C227.12455,270.53055 238.2407,254.79388 255.43064,250.9555 C272.62058,247.11712 289.37486,256.63058 292.85233,272.20438 C296.3298,287.77818 284.93742,293.52203 267.74748,297.36038 C250.55754,301.19873 234.07951,301.67812 230.60203,286.10434 Z" id="Path" fill="#0ea5e9" fill-rule="nonzero"></path>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(223.870603, 306.172293) rotate(-64.625740) translate(-223.870603, -306.172293) " cx="223.870603" cy="306.172293" rx="6.76007" ry="21.53368"></ellipse>
|
||||
<circle id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="85.9008" cy="369.76318" r="43.06733"></circle>
|
||||
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="66.27519" y="403.56287" width="13.08374" height="23.44171"></rect>
|
||||
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="92.44267" y="403.56287" width="13.08374" height="23.44171"></rect>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="77.17827" cy="427.27716" rx="10.90314" ry="4.08868"></ellipse>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="103.34581" cy="426.732" rx="10.90314" ry="4.08868"></ellipse>
|
||||
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="86.99113" cy="358.86008" r="14.71922"></circle>
|
||||
<circle id="Oval" fill="#3F3D56" fill-rule="nonzero" cx="86.99113" cy="358.86008" r="4.90642"></circle>
|
||||
<path d="M44.12401,329.71183 C40.64653,314.13804 51.76268,298.4014 68.95262,294.56302 C86.14256,290.72464 102.89683,300.23813 106.37431,315.81192 C109.85179,331.38571 98.45939,337.12961 81.26945,340.96792 C64.07951,344.80623 47.60154,345.28568 44.12401,329.71183 Z" id="Path" fill="#12a5e9" fill-rule="nonzero"></path>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(110.988725, 213.490755) rotate(-69.082170) translate(-110.988725, -213.490755) " cx="110.988725" cy="213.490755" rx="21.53369" ry="6.76007"></ellipse>
|
||||
<circle id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(71.181568, 246.012788) rotate(-80.782520) translate(-71.181568, -246.012788) " cx="71.1815681" cy="246.012788" r="43.06735"></circle>
|
||||
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="51.55595" y="279.81244" width="13.08374" height="23.44171"></rect>
|
||||
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="77.72343" y="279.81244" width="13.08374" height="23.44171"></rect>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="62.45909" cy="303.52674" rx="10.90314" ry="4.08868"></ellipse>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="88.62657" cy="302.9816" rx="10.90314" ry="4.08868"></ellipse>
|
||||
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="72.27189" cy="235.10965" r="14.71922"></circle>
|
||||
<circle id="Oval" fill="#3F3D56" fill-rule="nonzero" cx="72.27189" cy="235.10965" r="4.90642"></circle>
|
||||
<path d="M29.40483,205.96134 C25.92735,190.38755 37.0435,174.65088 54.23344,170.8125 C71.42338,166.97412 88.17766,176.48758 91.65513,192.06138 C95.1326,207.63518 83.74022,213.37903 66.55028,217.21738 C49.36034,221.05573 32.88231,221.53519 29.40483,205.96134 Z" id="Path" fill="#0ea5e9" fill-rule="nonzero"></path>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(22.673401, 226.029366) rotate(-64.625740) translate(-22.673401, -226.029366) " cx="22.6734006" cy="226.029366" rx="6.76007" ry="21.53368"></ellipse>
|
||||
<path d="M50.02702,261.54972 C50.02702,265.76487 60.88029,274.08829 72.92357,274.08829 C84.96685,274.08829 96.25871,262.22129 96.25871,258.0062 C96.25871,253.79111 84.96678,258.82395 72.92357,258.82395 C60.88036,258.82395 50.02702,257.33457 50.02702,261.54972 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M113.52376,81.56869 C111.867655,81.5704977 110.525568,82.9125851 110.52376,84.56869 L110.52376,208.56869 C110.525568,210.224795 111.867655,211.566882 113.52376,211.56869 L400.52376,211.56869 C402.179865,211.566882 403.521952,210.224795 403.52376,208.56869 L403.52376,84.56869 C403.521952,82.9125851 402.179865,81.5704977 400.52376,81.56869 L113.52376,81.56869 Z" id="Path" fill="#fbc024" fill-rule="nonzero"></path>
|
||||
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="191.01816" cy="146.56869" r="29.1211"></circle>
|
||||
<path d="M256.7436,142.69417 C254.884983,142.724723 253.39428,144.240132 253.39428,146.099 C253.39428,147.957868 254.884983,149.473277 256.7436,149.50383 L348.68926,149.50383 C349.906316,149.524778 351.042001,148.894505 351.668119,147.850647 C352.294237,146.806789 352.31556,145.508108 351.72405,144.444258 C351.132539,143.380407 350.018157,142.713189 348.80107,142.69417 C348.763797,142.693537 348.726527,142.693537 348.68926,142.69417 L256.7436,142.69417 Z" id="b71acdfd-6a55-428e-917a-53f192cb0203" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M256.7436,122.96697 C254.884983,122.997523 253.39428,124.512932 253.39428,126.3718 C253.39428,128.230668 254.884983,129.746077 256.7436,129.77663 L302.65917,129.77663 C303.876249,129.797613 305.011971,129.167347 305.638111,128.123473 C306.26425,127.0796 306.285572,125.78089 305.694037,124.717025 C305.102503,123.65316 303.988082,122.985951 302.77097,122.96697 C302.733703,122.966337 302.696437,122.966337 302.65917,122.96697 L256.7436,122.96697 Z" id="ad4fbcfa-41b0-45f9-a593-23b6dc3fe165" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M256.7436,163.36076 C254.884983,163.391313 253.39428,164.906722 253.39428,166.76559 C253.39428,168.624458 254.884983,170.139867 256.7436,170.17042 L348.68926,170.17042 C349.906316,170.191368 351.042001,169.561095 351.668119,168.517237 C352.294237,167.473379 352.31556,166.174698 351.72405,165.110848 C351.132539,164.046997 350.018157,163.379779 348.80107,163.36076 C348.763797,163.360093 348.726527,163.360093 348.68926,163.36076 L256.7436,163.36076 Z" id="bce2a7cd-a325-424d-9b3a-f63d1dad5aba" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(695.602650, 372.610085) rotate(-20.917830) translate(-695.602650, -372.610085) " cx="695.60265" cy="372.610085" rx="6.76007" ry="21.53369"></ellipse>
|
||||
<circle id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(726.409811, 414.132115) rotate(-9.217470) translate(-726.409811, -414.132115) " cx="726.409811" cy="414.132115" r="43.06733"></circle>
|
||||
<polygon id="Path" fill="#2F2E41" fill-rule="nonzero" points="746.035 447.932 732.952 447.932 735.512 476.005 746.058 473.14"></polygon>
|
||||
<polygon id="Path" fill="#2F2E41" fill-rule="nonzero" points="717.512 448.005 704.428 448.005 706.988 476.078 717.058 473.14"></polygon>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(706.318056, 473.319125) rotate(-4.181640) translate(-706.318056, -473.319125) " cx="706.318056" cy="473.319125" rx="10.90314" ry="4.08868"></ellipse>
|
||||
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="725.31949" cy="403.22896" r="14.71922"></circle>
|
||||
<circle id="Oval" fill="#3F3D56" fill-rule="nonzero" cx="725.31949" cy="403.22896" r="4.90642"></circle>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(771.918005, 384.148727) rotate(-53.549900) translate(-771.918005, -384.148727) " cx="771.918005" cy="384.148727" rx="21.53368" ry="6.76007"></ellipse>
|
||||
<path d="M705.39698,436.33866 C705.39698,432.86466 714.34198,426.00466 724.26778,426.00466 C734.19358,426.00466 743.50006,435.78516 743.50006,439.25914 C743.50006,442.73312 734.19351,438.58514 724.26778,438.58514 C714.34205,438.58514 705.39698,439.81268 705.39698,436.33866 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero" transform="translate(724.448520, 433.325266) rotate(-180.000000) translate(-724.448520, -433.325266) "></path>
|
||||
<path d="M847.1767,378.54172 L611.43117,345.86064 C607.604361,345.32541 604.932725,341.793834 605.45868,337.96574 L631.96057,146.79396 C632.495641,142.967058 636.027329,140.295337 639.85547,140.82147 L875.60098,173.50256 C879.427885,174.037626 882.099609,177.569318 881.57347,181.39746 L855.0716,372.56924 C854.536375,376.396051 851.004794,379.067687 847.1767,378.54172 L847.1767,378.54172 Z" id="Path" fill="#9358ff" fill-rule="nonzero"></path>
|
||||
<path d="M762.72231,318.87957 L642.36784,302.19498 C642.216871,302.176045 642.067969,302.14324 641.92302,302.09698 L712.51355,211.39072 C713.394132,210.238632 714.82651,209.649483 716.262854,209.8486 C717.699198,210.047717 718.917295,211.004295 719.45127,212.35248 L748.48059,283.8148 L749.87186,287.23459 L762.72231,318.87957 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<polygon id="Path" fill="#000000" fill-rule="nonzero" opacity="0.2" points="762.722 318.879 721.63 313.183 745.864 286.679 747.609 284.77 748.481 283.815 749.872 287.235"></polygon>
|
||||
<path d="M829.73481,328.16942 L725.63807,313.73863 L749.87186,287.23463 L751.61612,285.32515 L783.19533,250.78503 C784.29885,249.735613 785.796059,249.204111 787.314261,249.322828 C788.832463,249.441546 790.228856,250.199316 791.15584,251.40751 C791.271244,251.575507 791.375823,251.750689 791.46894,251.93199 L829.73481,328.16942 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="764.18602" cy="224.18353" r="18"></circle>
|
||||
<rect id="Rectangle" fill="#FFFFFF" fill-rule="nonzero" transform="translate(653.262165, 167.923576) rotate(7.892770) translate(-653.262165, -167.923576) " x="642.262129" y="156.923541" width="22.0000711" height="22.0000711"></rect>
|
||||
<path d="M768.18655,374.08068 C771.66403,358.50689 760.54788,342.77022 743.35794,338.93184 C726.168,335.09346 709.41373,344.60692 705.93625,360.18071 C702.45877,375.7545 713.85117,381.49837 731.04111,385.33671 C748.23105,389.17505 764.70908,389.6545 768.18655,374.08068 Z" id="Path" fill="#12a5e9" fill-rule="nonzero"></path>
|
||||
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(735.010556, 473.249892) rotate(-4.181640) translate(-735.010556, -473.249892) " cx="735.010556" cy="473.249892" rx="10.90314" ry="4.08868"></ellipse>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
@@ -1,32 +0,0 @@
|
||||
<svg width="673" height="739" viewBox="0 0 673 739" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill-rule="nonzero" fill="none">
|
||||
<path d="M467 149.805c-46.62-7.44-99.71-11.41-155-11.41-50.6 0-99.35 3.32-142.98 9.58.01-.67.02-1.34.05-2.01C171.475 65.082 237.996.903 318.914 1.398c80.917.494 146.649 65.48 148.066 146.387.01.68.02 1.35.02 2.02Z" fill="#0BA5E9"/>
|
||||
<path d="M337.55 1.342a149.047 149.047 0 0 0-168.93 143.229c-.031.67-.04 1.34-.05 2.01 12.961-1.86 26.384-3.454 40.164-4.784 3.478-71.745 57.64-130.8 128.816-140.455Z" opacity=".1" fill="#FFF"/>
|
||||
<path d="M532.18 161.625a600.121 600.121 0 0 0-65.2-13.84 943.364 943.364 0 0 0-108.74-10.45 1133.608 1133.608 0 0 0-83.01-.34 973.29 973.29 0 0 0-106.16 8.97 624.292 624.292 0 0 0-77.25 15.66C32.61 177.995 0 199.935 0 223.395s32.61 45.4 91.82 61.77c41.64 11.52 92.98 19.37 148.92 22.97 23.09 1.5 46.96 2.26 71.26 2.26 24.38 0 48.33-.77 71.49-2.27 50.91-3.29 98.01-10.1 137.43-20 .21-.06.41-.11.62-.16 2.66-.66 5.28-1.35 7.87-2.04.93-.26 1.85-.51 2.77-.76a.978.978 0 0 1 .16-.05c.88-.24 1.75-.49 2.62-.73 1.74-.5 3.46-.99 5.15-1.5.08-.02.15-.04.22-.06 1.47-.44 2.91-.88 4.34-1.32 1.17-.37 2.33-.73 3.48-1.1.84-.27 1.67-.54 2.49-.81.6-.2 1.19-.39 1.77-.59.79-.26 1.58-.53 2.36-.8.33-.11.66-.22.98-.34.75-.25 1.48-.51 2.21-.77.79-.28 1.58-.57 2.36-.85.65-.23 1.3-.47 1.94-.71.54-.21 1.07-.41 1.61-.61 1.47-.55 2.91-1.12 4.33-1.68.71-.29 1.42-.57 2.12-.86.69-.28 1.39-.57 2.07-.86 1.12-.47 2.22-.94 3.3-1.41.52-.24 1.05-.47 1.56-.69.39-.18.77-.35 1.16-.53.28-.12.56-.25.83-.38 1.01-.46 2.01-.93 2.99-1.4 3.76-1.8 7.27-3.64 10.53-5.52 20.45-11.71 31.24-24.7 31.24-38.2 0-23.46-32.61-45.4-91.82-61.77Zm-.54 121.62c-41.69 11.53-93.17 19.38-149.26 22.95-22.81 1.45-46.39 2.2-70.38 2.2-23.91 0-47.41-.74-70.15-2.19-56.18-3.56-107.74-11.41-149.49-22.96C34.09 267.125 2 245.875 2 223.395c0-1.986.252-3.965.74-5.89 5.1-20.28 36.47-39.26 89.62-53.96a623.806 623.806 0 0 1 76.66-15.57 976.027 976.027 0 0 1 106.8-9c11.92-.39 23.98-.583 36.18-.58 15.41 0 30.65.31 45.63.91a941.367 941.367 0 0 1 109.37 10.5 598.858 598.858 0 0 1 64.64 13.74c53.14 14.7 84.5 33.67 89.61 53.94.496 1.93.75 3.916.75 5.91 0 22.48-32.09 43.73-90.36 59.85Z" fill="#3F3D56"/>
|
||||
<path d="M623.43 224.305c0 13.36-11.01 26-30.67 37.29-3.27 1.88-6.79 3.72-10.53 5.52-.98.47-1.98.94-2.99 1.4-.27.13-.55.26-.83.38-.39.18-.77.35-1.16.53-.51.22-1.04.45-1.56.69-1.08.47-2.18.94-3.3 1.41-.68.29-1.38.58-2.07.86-.7.29-1.41.57-2.12.86-1.42.56-2.86 1.13-4.33 1.68-.54.2-1.07.4-1.61.61-.64.24-1.29.48-1.94.71-.78.28-1.57.57-2.36.85-.73.26-1.46.52-2.21.77-.32.12-.65.23-.98.34-.78.27-1.57.54-2.36.8-.58.2-1.17.39-1.77.59-.82.27-1.65.54-2.49.81-1.15.37-2.31.73-3.48 1.1-1.43.44-2.87.88-4.34 1.32-.07.02-.14.04-.22.06-1.69.51-3.41 1-5.15 1.5-.87.24-1.74.49-2.62.73a.978.978 0 0 0-.16.05c-.92.25-1.84.5-2.77.76-2.58.68-5.21 1.37-7.87 2.04-.21.05-.41.1-.62.16-38.35 9.58-85.4 16.56-137.47 19.93-22.81 1.47-46.59 2.25-71.02 2.25-24.65 0-48.63-.79-71.62-2.29-137.24-8.95-239.38-43.03-239.38-83.71.01-2.475.388-4.936 1.12-7.3.06.17.12.33.19.5 14.27 37.48 115.54 67.77 246.94 75.16 20.13 1.14 40.98 1.73 62.32 1.73 21.43 0 42.36-.6 62.57-1.74 131.29-7.42 232.46-37.72 246.68-75.17.24-.6.45-1.2.63-1.8a25.304 25.304 0 0 1 1.55 8.62ZM91.67 213.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217Z" fill="#3F3D56"/>
|
||||
<path d="M162.67 260.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217ZM531.67 213.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217ZM460.67 260.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217ZM311.67 282.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217Z" fill="#3F3D56"/>
|
||||
<circle fill="#2F2E41" cx="336.978" cy="450.705" r="42.012"/>
|
||||
<path fill="#2F2E41" d="m300.555 488.547 20.447-10.24 5.715 11.413-20.447 10.24z"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-56.601 300.086 492.946)" cx="300.086" cy="492.946" rx="3.989" ry="10.636"/>
|
||||
<path fill="#2F2E41" d="m347.239 489.72 5.715-11.412 20.447 10.24-5.715 11.412z"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-33.399 373.87 492.946)" cx="373.87" cy="492.946" rx="10.636" ry="3.989"/>
|
||||
<circle fill="#FFF" cx="334.037" cy="440.429" r="14.359"/>
|
||||
<ellipse fill="#3F3D56" transform="rotate(-45 334.135 434.282)" cx="334.135" cy="434.282" rx="4.766" ry="4.8"/>
|
||||
<path d="M370.12 405c.632-15.553-12.773-28.727-29.941-29.425-17.168-.697-31.597 11.346-32.229 26.9-.632 15.553 11.302 19.087 28.47 19.785 17.167.697 33.068-1.706 33.7-17.26Z" fill="#0BA5E9"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-40.645 380.654 456.766)" cx="380.654" cy="456.766" rx="6.594" ry="21.006"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-49.355 293.42 456.766)" cx="293.419" cy="456.766" rx="21.006" ry="6.594"/>
|
||||
<path d="M348.517 467.262a9.572 9.572 0 1 1-18.836 3.428l-.003-.018c-.942-5.202 3.08-7.043 8.282-7.985 5.203-.942 9.615-.628 10.557 4.575Z" fill="#FFF"/>
|
||||
<path d="M266 495.395a2 2 0 0 1-2-2v-118a2 2 0 0 1 4 0v118a2 2 0 0 1-2 2ZM236 601.395a2 2 0 0 1-2-2v-86a2 2 0 0 1 4 0v86a2 2 0 0 1-2 2ZM313 530.395a2 2 0 0 1-2-2v-118a2 2 0 0 1 4 0v118a2 2 0 0 1-2 2ZM284 615.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM325 369.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM225 390.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM399 395.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM395 545.395a2 2 0 0 1-2-2v-58a2 2 0 0 1 4 0v58a2 2 0 0 1-2 2ZM355 596.395a2 2 0 0 1-2-2v-86a2 2 0 0 1 4 0v86a2 2 0 0 1-2 2ZM363 449.395a2 2 0 0 1-2-2v-118a2 2 0 0 1 4 0v118a2 2 0 0 1-2 2Z" fill="#CCC"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-39.938 594.37 683.981)" cx="594.369" cy="683.981" rx="6.76" ry="21.534"/>
|
||||
<circle fill="#2F2E41" transform="rotate(-71.565 548.562 676.503)" cx="548.562" cy="676.503" r="43.067"/>
|
||||
<path fill="#2F2E41" d="M553.707 710.303h13.084v23.442h-13.084zM527.54 710.303h13.084v23.442H527.54z"/>
|
||||
<ellipse fill="#2F2E41" cx="555.888" cy="734.017" rx="10.903" ry="4.089"/>
|
||||
<ellipse fill="#2F2E41" cx="529.72" cy="733.472" rx="10.903" ry="4.089"/>
|
||||
<path d="M535.04 622.366c3.845-15.487 20.82-24.6 37.914-20.356 17.094 4.245 27.834 20.24 23.989 35.727-3.846 15.487-16.604 15.537-33.698 11.293-17.094-4.245-32.051-11.177-28.206-26.664Z" fill="#0BA5E9"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-64.626 500.054 656.52)" cx="500.054" cy="656.52" rx="6.76" ry="21.534"/>
|
||||
<circle fill="#FFF" cx="542.124" cy="667.416" r="14.359"/>
|
||||
<circle fill="#3F3D56" cx="536.222" cy="662.269" r="4.786"/>
|
||||
<circle fill="#FFF" cx="542" cy="697.395" r="6"/>
|
||||
<path d="M671.531 738.395h-236a1 1 0 1 1 0-2h236a1 1 0 0 1 0 2Z" fill="#3F3D56"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 21 KiB |
@@ -1,243 +0,0 @@
|
||||
(() => {
|
||||
function convertModuleLinks(html) {
|
||||
html = html
|
||||
.replace(/@shoelace-style\/shoelace/g, `https://esm.sh/@shoelace-style/shoelace@${waVersion}`)
|
||||
.replace(/from 'react'/g, `from 'https://esm.sh/react@${reactVersion}'`)
|
||||
.replace(/from "react"/g, `from "https://esm.sh/react@${reactVersion}"`);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function getAdjacentExample(name, pre) {
|
||||
let currentPre = pre.nextElementSibling;
|
||||
|
||||
while (currentPre?.tagName.toLowerCase() === 'pre') {
|
||||
if (currentPre?.getAttribute('data-lang').split(' ').includes(name)) {
|
||||
return currentPre;
|
||||
}
|
||||
|
||||
currentPre = currentPre.nextElementSibling;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function runScript(script) {
|
||||
const newScript = document.createElement('script');
|
||||
|
||||
if (script.type === 'module') {
|
||||
newScript.type = 'module';
|
||||
newScript.textContent = script.innerHTML;
|
||||
} else {
|
||||
newScript.appendChild(document.createTextNode(`(() => { ${script.innerHTML} })();`));
|
||||
}
|
||||
|
||||
script.parentNode.replaceChild(newScript, script);
|
||||
}
|
||||
|
||||
function getFlavor() {
|
||||
return sessionStorage.getItem('flavor') || 'html';
|
||||
}
|
||||
|
||||
function setFlavor(newFlavor) {
|
||||
flavor = ['html', 'react'].includes(newFlavor) ? newFlavor : 'html';
|
||||
sessionStorage.setItem('flavor', flavor);
|
||||
|
||||
// Set the flavor class on the body
|
||||
document.documentElement.classList.toggle('flavor-html', flavor === 'html');
|
||||
document.documentElement.classList.toggle('flavor-react', flavor === 'react');
|
||||
}
|
||||
|
||||
function syncFlavor() {
|
||||
setFlavor(getFlavor());
|
||||
|
||||
document.querySelectorAll('.code-preview__button--html').forEach(preview => {
|
||||
if (flavor === 'html') {
|
||||
preview.classList.add('code-preview__button--selected');
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.code-preview__button--react').forEach(preview => {
|
||||
if (flavor === 'react') {
|
||||
preview.classList.add('code-preview__button--selected');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const waVersion = document.documentElement.getAttribute('data-wa-version');
|
||||
const reactVersion = '18.2.0';
|
||||
const cdndir = 'cdn';
|
||||
const npmdir = 'dist';
|
||||
let flavor = getFlavor();
|
||||
let count = 1;
|
||||
|
||||
// We need the version to open
|
||||
if (!waVersion) {
|
||||
throw new Error('The data-wa-version attribute is missing from <html>.');
|
||||
}
|
||||
|
||||
// Sync flavor UI on page load
|
||||
syncFlavor();
|
||||
|
||||
//
|
||||
// Resizing previews
|
||||
//
|
||||
document.addEventListener('mousedown', handleResizerDrag);
|
||||
document.addEventListener('touchstart', handleResizerDrag, { passive: true });
|
||||
|
||||
function handleResizerDrag(event) {
|
||||
const resizer = event.target.closest('.code-preview__resizer');
|
||||
const preview = event.target.closest('.code-preview__preview');
|
||||
|
||||
if (!resizer || !preview) return;
|
||||
|
||||
let startX = event.changedTouches ? event.changedTouches[0].pageX : event.clientX;
|
||||
let startWidth = parseInt(document.defaultView.getComputedStyle(preview).width, 10);
|
||||
|
||||
event.preventDefault();
|
||||
preview.classList.add('code-preview__preview--dragging');
|
||||
document.documentElement.addEventListener('mousemove', dragMove);
|
||||
document.documentElement.addEventListener('touchmove', dragMove);
|
||||
document.documentElement.addEventListener('mouseup', dragStop);
|
||||
document.documentElement.addEventListener('touchend', dragStop);
|
||||
|
||||
function dragMove(event) {
|
||||
const width = startWidth + (event.changedTouches ? event.changedTouches[0].pageX : event.pageX) - startX;
|
||||
preview.style.width = `${width}px`;
|
||||
}
|
||||
|
||||
function dragStop() {
|
||||
preview.classList.remove('code-preview__preview--dragging');
|
||||
document.documentElement.removeEventListener('mousemove', dragMove);
|
||||
document.documentElement.removeEventListener('touchmove', dragMove);
|
||||
document.documentElement.removeEventListener('mouseup', dragStop);
|
||||
document.documentElement.removeEventListener('touchend', dragStop);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Toggle source mode
|
||||
//
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('.code-preview__button');
|
||||
const codeBlock = button?.closest('.code-preview');
|
||||
|
||||
if (button?.classList.contains('code-preview__button--html')) {
|
||||
// Show HTML
|
||||
setFlavor('html');
|
||||
toggleSource(codeBlock, true);
|
||||
} else if (button?.classList.contains('code-preview__button--react')) {
|
||||
// Show React
|
||||
setFlavor('react');
|
||||
toggleSource(codeBlock, true);
|
||||
} else if (button?.classList.contains('code-preview__toggle')) {
|
||||
// Toggle source
|
||||
toggleSource(codeBlock);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update flavor buttons
|
||||
[...document.querySelectorAll('.code-preview')].forEach(cb => {
|
||||
cb.querySelector('.code-preview__button--html')?.classList.toggle(
|
||||
'code-preview__button--selected',
|
||||
flavor === 'html'
|
||||
);
|
||||
cb.querySelector('.code-preview__button--react')?.classList.toggle(
|
||||
'code-preview__button--selected',
|
||||
flavor === 'react'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function toggleSource(codeBlock, force) {
|
||||
codeBlock.classList.toggle('code-preview--expanded', force);
|
||||
event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-preview--expanded'));
|
||||
}
|
||||
|
||||
//
|
||||
// Open in CodePen
|
||||
//
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('button');
|
||||
|
||||
if (button?.classList.contains('code-preview__button--codepen')) {
|
||||
const codeBlock = button.closest('.code-preview');
|
||||
const htmlExample = codeBlock.querySelector('.code-preview__source--html > pre > code')?.textContent;
|
||||
const reactExample = codeBlock.querySelector('.code-preview__source--react > pre > code')?.textContent;
|
||||
const isReact = flavor === 'react' && typeof reactExample === 'string';
|
||||
const editors = isReact ? '0010' : '1000';
|
||||
let htmlTemplate = '';
|
||||
let jsTemplate = '';
|
||||
let cssTemplate = '';
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.action = 'https://codepen.io/pen/define';
|
||||
form.method = 'POST';
|
||||
form.target = '_blank';
|
||||
|
||||
// HTML templates
|
||||
if (!isReact) {
|
||||
htmlTemplate =
|
||||
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${waVersion}/${cdndir}/autoloader.js"></script>\n` +
|
||||
`\n${htmlExample}`;
|
||||
jsTemplate = '';
|
||||
}
|
||||
|
||||
// React templates
|
||||
if (isReact) {
|
||||
htmlTemplate = '<div id="root"></div>';
|
||||
jsTemplate =
|
||||
`import React from 'https://esm.sh/react@${reactVersion}';\n` +
|
||||
`import ReactDOM from 'https://esm.sh/react-dom@${reactVersion}';\n` +
|
||||
`import { setBasePath } from 'https://esm.sh/@shoelace-style/shoelace@${waVersion}/${cdndir}/utilities/base-path';\n` +
|
||||
`\n` +
|
||||
`// Set the base path for Web Awesome assets\n` +
|
||||
`setBasePath('https://esm.sh/@shoelace-style/shoelace@${waVersion}/${npmdir}/')\n` +
|
||||
`\n${convertModuleLinks(reactExample)}\n` +
|
||||
`\n` +
|
||||
`ReactDOM.render(<App />, document.getElementById('root'));`;
|
||||
}
|
||||
|
||||
// CSS templates
|
||||
cssTemplate =
|
||||
`@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${waVersion}/${cdndir}/themes/default.css';\n` +
|
||||
'\n' +
|
||||
'body {\n' +
|
||||
' font: var(--wa-font-size-root) sans-serif;\n' +
|
||||
' background-color: var(--wa-color-surface-default);\n' +
|
||||
' color: var(--wa-color-text-normal);\n' +
|
||||
' padding: var(--wa-space-m);\n' +
|
||||
'}';
|
||||
|
||||
// Docs: https://blog.codepen.io/documentation/prefill/
|
||||
const data = {
|
||||
title: '',
|
||||
description: '',
|
||||
tags: ['web awesome', 'web components'],
|
||||
editors,
|
||||
head: `<meta name="viewport" content="width=device-width">`,
|
||||
css_external: ``,
|
||||
js_external: ``,
|
||||
js_module: true,
|
||||
js_pre_processor: isReact ? 'babel' : 'none',
|
||||
html: htmlTemplate,
|
||||
css: cssTemplate,
|
||||
js: jsTemplate
|
||||
};
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'data';
|
||||
input.value = JSON.stringify(data);
|
||||
form.append(input);
|
||||
|
||||
document.documentElement.append(form);
|
||||
form.submit();
|
||||
form.remove();
|
||||
}
|
||||
});
|
||||
|
||||
// Set the initial flavor
|
||||
window.addEventListener('turbo:load', syncFlavor);
|
||||
})();
|
||||
@@ -1,206 +0,0 @@
|
||||
//
|
||||
// Sidebar
|
||||
//
|
||||
// When the sidebar is hidden, we apply the inert attribute to prevent focus from reaching it. Due to the many states
|
||||
// the sidebar can have (e.g. static, hidden, expanded), we test for visibility by checking to see if it's placed
|
||||
// offscreen or not. Then, on resize/transition we make sure to update the attribute accordingly.
|
||||
//
|
||||
(() => {
|
||||
function getSidebar() {
|
||||
return document.getElementById('sidebar');
|
||||
}
|
||||
|
||||
function isSidebarOpen() {
|
||||
return document.documentElement.classList.contains('sidebar-open');
|
||||
}
|
||||
|
||||
function isSidebarVisible() {
|
||||
return getSidebar().getBoundingClientRect().x >= 0;
|
||||
}
|
||||
|
||||
function toggleSidebar(force) {
|
||||
const isOpen = typeof force === 'boolean' ? force : !isSidebarOpen();
|
||||
return document.documentElement.classList.toggle('sidebar-open', isOpen);
|
||||
}
|
||||
|
||||
function updateInert() {
|
||||
getSidebar().inert = !isSidebarVisible();
|
||||
}
|
||||
|
||||
// Toggle the menu
|
||||
document.addEventListener('click', event => {
|
||||
const menuToggle = event.target.closest('#menu-toggle');
|
||||
if (!menuToggle) return;
|
||||
toggleSidebar();
|
||||
});
|
||||
|
||||
// Update the sidebar's inert state when the window resizes and when the sidebar transitions
|
||||
window.addEventListener('resize', () => toggleSidebar(false));
|
||||
|
||||
document.addEventListener('transitionend', event => {
|
||||
const sidebar = event.target.closest('#sidebar');
|
||||
if (!sidebar) return;
|
||||
updateInert();
|
||||
});
|
||||
|
||||
// Close when a menu item is selected on mobile
|
||||
document.addEventListener('click', event => {
|
||||
const sidebar = event.target.closest('#sidebar');
|
||||
const link = event.target.closest('a');
|
||||
if (!sidebar || !link) return;
|
||||
|
||||
if (isSidebarOpen()) {
|
||||
toggleSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
// Close when open and escape is pressed
|
||||
document.addEventListener('keydown', event => {
|
||||
if (event.key === 'Escape' && isSidebarOpen()) {
|
||||
event.stopImmediatePropagation();
|
||||
toggleSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
// Close when clicking outside of the sidebar
|
||||
document.addEventListener('mousedown', event => {
|
||||
if (isSidebarOpen() & !event.target?.closest('#sidebar, #menu-toggle')) {
|
||||
event.stopImmediatePropagation();
|
||||
toggleSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
updateInert();
|
||||
})();
|
||||
|
||||
//
|
||||
// Open details when printing
|
||||
//
|
||||
(() => {
|
||||
const detailsOpenOnPrint = new Set();
|
||||
|
||||
window.addEventListener('beforeprint', () => {
|
||||
detailsOpenOnPrint.clear();
|
||||
document.querySelectorAll('details').forEach(details => {
|
||||
if (details.open) {
|
||||
detailsOpenOnPrint.add(details);
|
||||
}
|
||||
details.open = true;
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('afterprint', () => {
|
||||
document.querySelectorAll('details').forEach(details => {
|
||||
details.open = detailsOpenOnPrint.has(details);
|
||||
});
|
||||
detailsOpenOnPrint.clear();
|
||||
});
|
||||
})();
|
||||
|
||||
//
|
||||
// Smooth links
|
||||
//
|
||||
(() => {
|
||||
document.addEventListener('click', event => {
|
||||
const link = event.target.closest('a');
|
||||
const id = (link?.hash ?? '').substr(1);
|
||||
const isFragment = link?.hasAttribute('href') && link?.getAttribute('href').startsWith('#');
|
||||
|
||||
if (!link || !isFragment || link.getAttribute('data-smooth-link') === 'false') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Scroll to the top
|
||||
if (link.hash === '') {
|
||||
event.preventDefault();
|
||||
window.scroll({ top: 0, behavior: 'smooth' });
|
||||
history.pushState(undefined, undefined, location.pathname);
|
||||
}
|
||||
|
||||
// Scroll to an id
|
||||
if (id) {
|
||||
const target = document.getElementById(id);
|
||||
|
||||
if (target) {
|
||||
event.preventDefault();
|
||||
window.scroll({ top: target.offsetTop, behavior: 'smooth' });
|
||||
history.pushState(undefined, undefined, `#${id}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
//
|
||||
// Table of Contents scrollspy
|
||||
//
|
||||
(() => {
|
||||
// This will be stale if its not a function.
|
||||
const getLinks = () => [...document.querySelectorAll('.content__toc a')];
|
||||
const linkTargets = new WeakMap();
|
||||
const visibleTargets = new WeakSet();
|
||||
const observer = new IntersectionObserver(handleIntersect, { rootMargin: '0px 0px' });
|
||||
let debounce;
|
||||
|
||||
function handleIntersect(entries) {
|
||||
entries.forEach(entry => {
|
||||
// Remember which targets are visible
|
||||
if (entry.isIntersecting) {
|
||||
visibleTargets.add(entry.target);
|
||||
} else {
|
||||
visibleTargets.delete(entry.target);
|
||||
}
|
||||
});
|
||||
|
||||
updateActiveLinks();
|
||||
}
|
||||
|
||||
function updateActiveLinks() {
|
||||
const links = getLinks();
|
||||
// Find the first visible target and activate the respective link
|
||||
links.find(link => {
|
||||
const target = linkTargets.get(link);
|
||||
|
||||
if (target && visibleTargets.has(target)) {
|
||||
links.forEach(el => el.classList.toggle('active', el === link));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Observe link targets
|
||||
function observeLinks() {
|
||||
getLinks().forEach(link => {
|
||||
const hash = link.hash.slice(1);
|
||||
const target = hash ? document.querySelector(`.content__body #${hash}`) : null;
|
||||
|
||||
if (target) {
|
||||
linkTargets.set(link, target);
|
||||
observer.observe(target);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
observeLinks();
|
||||
|
||||
document.addEventListener('turbo:load', updateActiveLinks);
|
||||
document.addEventListener('turbo:load', observeLinks);
|
||||
})();
|
||||
|
||||
//
|
||||
// Show custom versions in the sidebar
|
||||
//
|
||||
(() => {
|
||||
function updateVersion() {
|
||||
const el = document.querySelector('.sidebar-version');
|
||||
if (!el) return;
|
||||
|
||||
if (location.hostname === 'next.shoelace.style') el.textContent = 'Next';
|
||||
if (location.hostname === 'localhost') el.textContent = 'Development';
|
||||
}
|
||||
|
||||
updateVersion();
|
||||
|
||||
document.addEventListener('turbo:load', updateVersion);
|
||||
})();
|
||||
@@ -1,384 +0,0 @@
|
||||
(() => {
|
||||
// Append the search dialog to the body
|
||||
const siteSearch = document.createElement('div');
|
||||
const scrollbarWidth = Math.abs(window.innerWidth - document.documentElement.clientWidth);
|
||||
|
||||
siteSearch.classList.add('search');
|
||||
siteSearch.innerHTML = `
|
||||
<div class="search__overlay"></div>
|
||||
<dialog id="search-dialog" class="search__dialog">
|
||||
<div class="search__content">
|
||||
<div class="search__header">
|
||||
<div id="search-combobox" class="search__input-wrapper">
|
||||
<wa-icon name="search"></wa-icon>
|
||||
<input
|
||||
id="search-input"
|
||||
class="search__input"
|
||||
type="search"
|
||||
placeholder="Search"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
enterkeyhint="go"
|
||||
spellcheck="false"
|
||||
maxlength="100"
|
||||
role="combobox"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="true"
|
||||
aria-controls="search-listbox"
|
||||
aria-haspopup="listbox"
|
||||
aria-activedescendant
|
||||
>
|
||||
<button type="button" class="search__clear-button" aria-label="Clear entry" tabindex="-1" hidden>
|
||||
<wa-icon name="circle-xmark" variant="regular"></wa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search__body">
|
||||
<ul
|
||||
id="search-listbox"
|
||||
class="search__results"
|
||||
role="listbox"
|
||||
aria-label="Search results"
|
||||
></ul>
|
||||
<div class="search__empty">No matching pages</div>
|
||||
</div>
|
||||
<footer class="search__footer">
|
||||
<small><kbd><wa-icon label="Up" name="arrow-up"></wa-icon></kbd> <kbd><wa-icon label="Down" name="arrow-down"></wa-icon></kbd> Navigate</small>
|
||||
<small><kbd><wa-icon label="Enter" name="arrow-turn-down-left"></wa-icon></kbd> Select</small>
|
||||
<small><kbd>Esc</kbd> Close</small>
|
||||
</footer>
|
||||
</div>
|
||||
</dialog>
|
||||
`;
|
||||
|
||||
const overlay = siteSearch.querySelector('.search__overlay');
|
||||
const dialog = siteSearch.querySelector('.search__dialog');
|
||||
const input = siteSearch.querySelector('.search__input');
|
||||
const clearButton = siteSearch.querySelector('.search__clear-button');
|
||||
const results = siteSearch.querySelector('.search__results');
|
||||
const version = document.documentElement.getAttribute('data-wa-version');
|
||||
const key = `search_${version}`;
|
||||
const searchDebounce = 50;
|
||||
const animationDuration = 150;
|
||||
let isShowing = false;
|
||||
let searchTimeout;
|
||||
let searchIndex;
|
||||
let map;
|
||||
|
||||
const loadSearchIndex = new Promise(resolve => {
|
||||
const cache = localStorage.getItem(key);
|
||||
const wait = 'requestIdleCallback' in window ? requestIdleCallback : requestAnimationFrame;
|
||||
|
||||
// Cleanup older search indices (everything before this version)
|
||||
try {
|
||||
const items = { ...localStorage };
|
||||
|
||||
Object.keys(items).forEach(k => {
|
||||
if (key > k) {
|
||||
localStorage.removeItem(k);
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
// Look for a cached index
|
||||
try {
|
||||
if (cache) {
|
||||
const data = JSON.parse(cache);
|
||||
|
||||
searchIndex = window.lunr.Index.load(data.searchIndex);
|
||||
map = data.map;
|
||||
|
||||
return resolve();
|
||||
}
|
||||
} catch {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
// Wait until idle to fetch the index
|
||||
wait(() => {
|
||||
fetch('/assets/search.json')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (!window.lunr) {
|
||||
console.error('The Lunr search client has not yet been loaded.');
|
||||
}
|
||||
|
||||
searchIndex = window.lunr.Index.load(data.searchIndex);
|
||||
map = data.map;
|
||||
|
||||
// Cache the search index for this version
|
||||
if (version) {
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(data));
|
||||
} catch (err) {
|
||||
console.warn(`Unable to cache the search index: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function show() {
|
||||
isShowing = true;
|
||||
document.body.append(siteSearch);
|
||||
document.body.classList.add('search-visible');
|
||||
document.body.style.setProperty('--docs-search-scroll-lock-size', `${scrollbarWidth}px`);
|
||||
clearButton.hidden = true;
|
||||
requestAnimationFrame(() => input.focus());
|
||||
updateResults();
|
||||
|
||||
dialog.showModal();
|
||||
|
||||
await Promise.all([
|
||||
dialog.animate(
|
||||
[
|
||||
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' },
|
||||
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' }
|
||||
],
|
||||
{ duration: animationDuration }
|
||||
).finished,
|
||||
overlay.animate([{ opacity: 0 }, { opacity: 1 }], { duration: animationDuration }).finished
|
||||
]);
|
||||
|
||||
dialog.addEventListener('mousedown', handleMouseDown);
|
||||
dialog.addEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
|
||||
async function hide() {
|
||||
isShowing = false;
|
||||
|
||||
await Promise.all([
|
||||
dialog.animate(
|
||||
[
|
||||
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' },
|
||||
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' }
|
||||
],
|
||||
{ duration: animationDuration }
|
||||
).finished,
|
||||
overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: animationDuration }).finished
|
||||
]);
|
||||
|
||||
dialog.close();
|
||||
|
||||
input.blur(); // otherwise Safari will scroll to the bottom of the page on close
|
||||
input.value = '';
|
||||
document.body.classList.remove('search-visible');
|
||||
document.body.style.removeProperty('--docs-search-scroll-lock-size');
|
||||
siteSearch.remove();
|
||||
updateResults();
|
||||
|
||||
dialog.removeEventListener('mousedown', handleMouseDown);
|
||||
dialog.removeEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
|
||||
function handleInput() {
|
||||
clearButton.hidden = input.value === '';
|
||||
|
||||
// Debounce search queries
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
|
||||
}
|
||||
|
||||
function handleClear() {
|
||||
clearButton.hidden = true;
|
||||
input.value = '';
|
||||
input.focus();
|
||||
updateResults();
|
||||
}
|
||||
|
||||
function handleMouseDown(event) {
|
||||
if (!event.target.closest('.search__content')) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyDown(event) {
|
||||
// Close when pressing escape
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault(); // prevent <dialog> from closing immediately so it can animate
|
||||
event.stopImmediatePropagation();
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle keyboard selections
|
||||
if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter'].includes(event.key)) {
|
||||
event.preventDefault();
|
||||
|
||||
const currentEl = results.querySelector('[data-selected="true"]');
|
||||
const items = [...results.querySelectorAll('li')];
|
||||
const index = items.indexOf(currentEl);
|
||||
let nextEl;
|
||||
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case 'ArrowUp':
|
||||
nextEl = items[Math.max(0, index - 1)];
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
nextEl = items[Math.min(items.length - 1, index + 1)];
|
||||
break;
|
||||
case 'Home':
|
||||
nextEl = items[0];
|
||||
break;
|
||||
case 'End':
|
||||
nextEl = items[items.length - 1];
|
||||
break;
|
||||
case 'Enter':
|
||||
currentEl?.querySelector('a')?.click();
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the selected item
|
||||
items.forEach(item => {
|
||||
if (item === nextEl) {
|
||||
input.setAttribute('aria-activedescendant', item.id);
|
||||
item.setAttribute('data-selected', 'true');
|
||||
nextEl.scrollIntoView({ block: 'nearest' });
|
||||
} else {
|
||||
item.setAttribute('data-selected', 'false');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function updateResults(query = '') {
|
||||
try {
|
||||
await loadSearchIndex;
|
||||
|
||||
const hasQuery = query.length > 0;
|
||||
const searchTerms = query
|
||||
.split(' ')
|
||||
.map((term, index, arr) => {
|
||||
// Search API: https://lunrjs.com/guides/searching.html
|
||||
if (index === arr.length - 1) {
|
||||
// The last term is not mandatory and 1x fuzzy. We also duplicate it with a wildcard to match partial words
|
||||
// as the user types.
|
||||
return `${term}~1 ${term}*`;
|
||||
} else {
|
||||
// All other terms are mandatory and 1x fuzzy
|
||||
return `+${term}~1`;
|
||||
}
|
||||
})
|
||||
.join(' ');
|
||||
const matches = hasQuery ? searchIndex.search(searchTerms) : [];
|
||||
const hasResults = hasQuery && matches.length > 0;
|
||||
|
||||
siteSearch.classList.toggle('search--has-results', hasQuery && hasResults);
|
||||
siteSearch.classList.toggle('search--no-results', hasQuery && !hasResults);
|
||||
|
||||
input.setAttribute('aria-activedescendant', '');
|
||||
results.innerHTML = '';
|
||||
|
||||
matches.forEach((match, index) => {
|
||||
const page = map[match.ref];
|
||||
const li = document.createElement('li');
|
||||
const a = document.createElement('a');
|
||||
const displayTitle = page.title ?? '';
|
||||
const displayDescription = page.description ?? '';
|
||||
const displayUrl = page.url.replace(/^\//, '').replace(/\/$/, '');
|
||||
let icon = 'file-text';
|
||||
|
||||
a.setAttribute('role', 'option');
|
||||
a.setAttribute('id', `search-result-item-${match.ref}`);
|
||||
|
||||
if (page.url.includes('getting-started/')) {
|
||||
icon = 'lightbulb';
|
||||
}
|
||||
if (page.url.includes('resources/')) {
|
||||
icon = 'book';
|
||||
}
|
||||
if (page.url.includes('components/')) {
|
||||
icon = 'puzzle-piece';
|
||||
}
|
||||
if (page.url.includes('tokens/')) {
|
||||
icon = 'swatchbook';
|
||||
}
|
||||
if (page.url.includes('utilities/')) {
|
||||
icon = 'wrench';
|
||||
}
|
||||
if (page.url.includes('tutorials/')) {
|
||||
icon = 'gamepad';
|
||||
}
|
||||
|
||||
li.classList.add('search__result');
|
||||
li.setAttribute('role', 'option');
|
||||
li.setAttribute('id', `search-result-item-${match.ref}`);
|
||||
li.setAttribute('data-selected', index === 0 ? 'true' : 'false');
|
||||
|
||||
a.href = page.url;
|
||||
a.innerHTML = `
|
||||
<div class="search__result-icon" aria-hidden="true">
|
||||
<wa-icon name="${icon}"></wa-icon>
|
||||
</div>
|
||||
<div class="search__result__details">
|
||||
<div class="search__result-title"></div>
|
||||
<div class="search__result-description"></div>
|
||||
<div class="search__result-url"></div>
|
||||
</div>
|
||||
`;
|
||||
a.querySelector('.search__result-title').textContent = displayTitle;
|
||||
a.querySelector('.search__result-description').textContent = displayDescription;
|
||||
a.querySelector('.search__result-url').textContent = displayUrl;
|
||||
|
||||
li.appendChild(a);
|
||||
results.appendChild(li);
|
||||
});
|
||||
} catch {
|
||||
// Ignore query errors as the user types
|
||||
}
|
||||
}
|
||||
|
||||
// Show the search dialog when clicking on data-plugin="search"
|
||||
document.addEventListener('click', event => {
|
||||
const searchButton = event.target.closest('[data-plugin="search"]');
|
||||
if (searchButton) {
|
||||
show();
|
||||
}
|
||||
});
|
||||
|
||||
// Show the search dialog when slash (or CMD+K) is pressed and focus is not inside a form element
|
||||
document.addEventListener('keydown', event => {
|
||||
if (
|
||||
!isShowing &&
|
||||
(event.key === '/' || (event.key === 'k' && (event.metaKey || event.ctrlKey))) &&
|
||||
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
|
||||
) {
|
||||
event.preventDefault();
|
||||
show();
|
||||
}
|
||||
});
|
||||
|
||||
// Purge cache when we press CMD+CTRL+R
|
||||
document.addEventListener('keydown', event => {
|
||||
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === 'r') {
|
||||
localStorage.clear();
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('input', handleInput);
|
||||
clearButton.addEventListener('click', handleClear);
|
||||
|
||||
// Close when a result is selected
|
||||
results.addEventListener('click', event => {
|
||||
if (event.target.closest('a')) {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
// We're using Turbo, so when a user searches for something, visits a result, and presses the back button, the search
|
||||
// UI will still be visible but not interactive. This removes the search UI when Turbo renders a page so they don't
|
||||
// get trapped.
|
||||
window.addEventListener('turbo:render', () => {
|
||||
document.body.classList.remove('search-visible');
|
||||
document.querySelectorAll('.search__overlay, .search__dialog').forEach(el => el.remove());
|
||||
});
|
||||
})();
|
||||
@@ -1,29 +0,0 @@
|
||||
import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@7.3.0/+esm';
|
||||
|
||||
(() => {
|
||||
if (!window.scrollPositions) {
|
||||
window.scrollPositions = {};
|
||||
}
|
||||
|
||||
function preserveScroll() {
|
||||
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
|
||||
scrollPositions[element.id] = element.scrollTop;
|
||||
});
|
||||
}
|
||||
|
||||
function restoreScroll(event) {
|
||||
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
|
||||
element.scrollTop = scrollPositions[element.id];
|
||||
});
|
||||
|
||||
if (event.detail && event.detail.newBody) {
|
||||
event.detail.newBody.querySelectorAll('[data-preserve-scroll').forEach(element => {
|
||||
element.scrollTop = scrollPositions[element.id];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('turbo:before-cache', preserveScroll);
|
||||
window.addEventListener('turbo:before-render', restoreScroll);
|
||||
window.addEventListener('turbo:render', restoreScroll);
|
||||
})();
|
||||
@@ -1,173 +0,0 @@
|
||||
/* Interactive code blocks */
|
||||
.code-preview {
|
||||
position: relative;
|
||||
border-radius: var(--wa-corners-s);
|
||||
background-color: var(--wa-color-neutral-fill-subtle);
|
||||
margin-bottom: var(--wa-space-xl);
|
||||
}
|
||||
|
||||
.code-preview__preview {
|
||||
position: relative;
|
||||
border: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
border-bottom: none;
|
||||
border-top-left-radius: var(--wa-corners-s);
|
||||
border-top-right-radius: var(--wa-corners-s);
|
||||
background-color: var(--wa-color-surface-default);
|
||||
min-width: 20rem;
|
||||
max-width: 100%;
|
||||
padding: var(--wa-space-xl) var(--wa-space-3xl) var(--wa-space-xl) var(--wa-space-xl);
|
||||
}
|
||||
|
||||
/* Block the preview while dragging to prevent iframes from intercepting drag events */
|
||||
.code-preview__preview--dragging:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.code-preview__resizer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 1.75rem;
|
||||
font-size: var(--wa-font-size-xs);
|
||||
color: var(--wa-color-text-quiet);
|
||||
background-color: var(--wa-color-surface-default);
|
||||
border-left: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
border-top-right-radius: var(--wa-corners-s);
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.code-preview__preview {
|
||||
padding-right: var(--wa-space-xl);
|
||||
}
|
||||
|
||||
.code-preview__resizer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.code-preview__source {
|
||||
border: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
border-bottom: none;
|
||||
border-radius: 0 !important;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.code-preview--expanded .code-preview__source {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.code-preview__source pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.code-preview__buttons {
|
||||
position: relative;
|
||||
border: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
border-bottom-left-radius: var(--wa-corners-s);
|
||||
border-bottom-right-radius: var(--wa-corners-s);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.code-preview__button {
|
||||
flex: 0 0 auto;
|
||||
height: 2.5rem;
|
||||
min-width: 2.5rem;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: var(--wa-color-surface-default);
|
||||
font: inherit;
|
||||
font-size: var(--wa-font-size-xs);
|
||||
font-weight: var(--wa-font-weight-normal);
|
||||
text-transform: uppercase;
|
||||
color: var(--wa-color-text-quiet);
|
||||
padding: 0 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.code-preview__button:not(:last-of-type) {
|
||||
border-right: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
}
|
||||
|
||||
.code-preview__button--html,
|
||||
.code-preview__button--react {
|
||||
width: 70px;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.code-preview__button--selected {
|
||||
font-weight: var(--wa-font-weight-heavy);
|
||||
color: var(--wa-color-brand-text-on-surface);
|
||||
}
|
||||
|
||||
.code-preview__button--codepen {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
width: 6rem;
|
||||
}
|
||||
|
||||
.code-preview__button:first-of-type {
|
||||
border-bottom-left-radius: var(--wa-corners-s);
|
||||
}
|
||||
|
||||
.code-preview__button:last-of-type {
|
||||
border-bottom-right-radius: var(--wa-corners-s);
|
||||
}
|
||||
|
||||
.code-preview__button:hover,
|
||||
.code-preview__button:active {
|
||||
box-shadow: 0 0 0 var(--wa-border-width-s) var(--wa-color-brand-border-subtle);
|
||||
border-right-color: transparent;
|
||||
background-color: var(--wa-color-brand-fill-subtle);
|
||||
color: var(--wa-color-brand-text-on-surface);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.code-preview__button:focus-visible {
|
||||
outline: none;
|
||||
outline: var(--wa-focus-ring);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.code-preview__toggle {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
color: var(--wa-color-text-quiet);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.code-preview__toggle svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.code-preview--expanded .code-preview__toggle svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* We can apply data-flavor="html|react" to any element on the page to toggle it when the flavor changes */
|
||||
.flavor-html [data-flavor]:not([data-flavor='html']) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flavor-react [data-flavor]:not([data-flavor='react']) {
|
||||
display: none;
|
||||
}
|
||||
@@ -1,943 +0,0 @@
|
||||
:root {
|
||||
--docs-background-color: var(--wa-color-surface-default);
|
||||
--docs-content-max-width: 860px;
|
||||
--docs-sidebar-width: 320px;
|
||||
--docs-sidebar-transition-speed: 250ms;
|
||||
--docs-content-toc-max-width: 260px;
|
||||
--docs-content-padding: 2rem;
|
||||
--docs-content-vertical-spacing: 2rem;
|
||||
--docs-search-overlay-background: rgb(0 0 0 / 0.2);
|
||||
--docs-skip-to-main-width: 200px;
|
||||
}
|
||||
|
||||
/* Light theme */
|
||||
:root {
|
||||
color-scheme: normal;
|
||||
|
||||
--docs-overlay-color: hsl(240 3.8% 46.1% / 33%);
|
||||
--docs-shadow-x-small: 0 1px 2px hsl(240 3.8% 46.1% / 12%);
|
||||
--docs-shadow-small: 0 1px 2px hsl(240 3.8% 46.1% / 24%);
|
||||
--docs-shadow-medium: 0 2px 4px hsl(240 3.8% 46.1% / 24%);
|
||||
--docs-shadow-large: 0 2px 8px hsl(240 3.8% 46.1% / 24%);
|
||||
--docs-shadow-x-large: 0 4px 16px hsl(240 3.8% 46.1% / 24%);
|
||||
}
|
||||
|
||||
/* Utils */
|
||||
html.wa-theme-dark .only-light,
|
||||
html:not(.wa-theme-dark) .only-dark {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.visually-hidden:not(:focus-within) {
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
clip: rect(0 0 0 0) !important;
|
||||
clip-path: inset(50%) !important;
|
||||
border: none !important;
|
||||
overflow: hidden !important;
|
||||
white-space: nowrap !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
:root {
|
||||
--docs-content-padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
line-height: var(--wa-font-line-height-regular);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* Common elements */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 3rem 0 1.5rem 0;
|
||||
}
|
||||
|
||||
h1:first-of-type {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.badges img {
|
||||
border-radius: var(--wa-corners-s);
|
||||
}
|
||||
|
||||
.callout img {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* Color matching logos */
|
||||
svg.logo {
|
||||
color: var(--wa-color-brand-text-on-surface);
|
||||
}
|
||||
|
||||
/* Anchor headings */
|
||||
.anchor-heading {
|
||||
position: relative;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.anchor-heading a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.anchor-heading a::after {
|
||||
content: '#';
|
||||
color: color-mix(in oklab, var(--wa-color-text-link) 100%, var(--wa-color-mix-hover));
|
||||
margin-inline: 0.5rem;
|
||||
opacity: 0;
|
||||
transition: 100ms opacity;
|
||||
}
|
||||
|
||||
.anchor-heading:hover a::after,
|
||||
.anchor-heading:focus-within a::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* External links */
|
||||
.external-link__icon {
|
||||
width: 0.75em;
|
||||
height: 0.75em;
|
||||
vertical-align: 0;
|
||||
margin-left: 0.25em;
|
||||
margin-right: 0.125em;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
table th p:first-child,
|
||||
table td p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
table th p:last-child,
|
||||
table td p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.table-scroll {
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.table-scroll code {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
th.table-name,
|
||||
th.table-event-detail {
|
||||
min-width: 15ch;
|
||||
}
|
||||
|
||||
th.table-description {
|
||||
min-width: 50ch !important;
|
||||
max-width: 70ch;
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
|
||||
pre:not(:last-child) {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
pre {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
display: block;
|
||||
background: none !important;
|
||||
border-radius: 0;
|
||||
hyphens: none;
|
||||
tab-size: 2;
|
||||
white-space: pre;
|
||||
padding: 1rem;
|
||||
margin: -1rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
pre .token.comment {
|
||||
color: var(--wa-color-base-40);
|
||||
}
|
||||
|
||||
pre .token.prolog,
|
||||
pre .token.doctype,
|
||||
pre .token.cdata,
|
||||
pre .token.operator,
|
||||
pre .token.punctuation {
|
||||
color: var(--wa-color-base-40);
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
pre .token.property,
|
||||
pre .token.keyword,
|
||||
pre .token.tag,
|
||||
pre .token.url {
|
||||
color: var(--wa-color-brand-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.symbol,
|
||||
pre .token.deleted {
|
||||
color: var(--wa-color-danger-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.boolean,
|
||||
pre .token.constant,
|
||||
pre .token.selector,
|
||||
pre .token.attr-name,
|
||||
pre .token.string,
|
||||
pre .token.char,
|
||||
pre .token.builtin,
|
||||
pre .token.inserted {
|
||||
color: var(--wa-color-success-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.atrule,
|
||||
pre .token.attr-value,
|
||||
pre .token.number,
|
||||
pre .token.variable {
|
||||
color: var(--wa-color-warning-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.function,
|
||||
pre .token.class-name,
|
||||
pre .token.regex {
|
||||
color: var(--wa-color-danger-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.important {
|
||||
color: var(--wa-color-danger-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.important,
|
||||
pre .token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
pre .token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Copy code button */
|
||||
.copy-code-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
white-space: normal;
|
||||
color: var(--wa-color-neutral-text-on-fill-alt);
|
||||
transition:
|
||||
150ms opacity,
|
||||
150ms scale;
|
||||
}
|
||||
|
||||
.copy-code-button::part(button) {
|
||||
background-color: var(--wa-color-neutral-fill-subtle);
|
||||
border-radius: 0 var(--wa-corners-s) 0 var(--wa-corners-s);
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.copy-code-button::part(button):hover {
|
||||
background-color: color-mix(in oklab, var(--wa-color-neutral-fill-subtle), var(--wa-color-mix-hover));
|
||||
}
|
||||
|
||||
.copy-code-button::part(button):active {
|
||||
background-color: color-mix(in oklab, var(--wa-color-neutral-fill-subtle), var(--wa-color-mix-active));
|
||||
}
|
||||
|
||||
pre .copy-code-button {
|
||||
opacity: 0;
|
||||
scale: 0.75;
|
||||
}
|
||||
|
||||
pre:hover .copy-code-button,
|
||||
.copy-code-button:focus-within {
|
||||
opacity: 1;
|
||||
scale: 1;
|
||||
}
|
||||
|
||||
/* Callouts */
|
||||
.callout {
|
||||
margin-bottom: var(--docs-content-vertical-spacing);
|
||||
}
|
||||
|
||||
.callout > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.callout > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.callout a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.callout p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Aside */
|
||||
.content aside {
|
||||
float: right;
|
||||
min-width: 300px;
|
||||
max-width: 50%;
|
||||
background: var(--wa-color-surface-lowered);
|
||||
border-radius: var(--wa-corners-s);
|
||||
padding: var(--wa-space-m);
|
||||
margin-left: var(--wa-space-m);
|
||||
}
|
||||
|
||||
.content aside > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.content aside > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.content aside {
|
||||
float: none;
|
||||
width: calc(100% + (var(--docs-content-padding) * 2));
|
||||
max-width: none;
|
||||
margin: var(--docs-content-vertical-spacing) calc(-1 * var(--docs-content-padding));
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
flex: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 20;
|
||||
width: var(--docs-sidebar-width);
|
||||
background-color: var(--docs-background-color);
|
||||
border-right: solid var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
border-radius: 0;
|
||||
padding: 2rem;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
scrollbar-width: thin;
|
||||
transition: var(--docs-sidebar-transition-speed) translate ease-in-out;
|
||||
}
|
||||
|
||||
#sidebar::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
#sidebar::-webkit-scrollbar-thumb {
|
||||
background: transparent;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
#sidebar:hover::-webkit-scrollbar-thumb {
|
||||
background: var(--wa-color-base-60);
|
||||
}
|
||||
|
||||
#sidebar:hover::-webkit-scrollbar-track {
|
||||
background: var(--wa-color-base-95);
|
||||
}
|
||||
|
||||
#sidebar > header {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
#sidebar > header h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#sidebar > header svg {
|
||||
margin-bottom: var(--wa-space-s);
|
||||
}
|
||||
|
||||
#sidebar > header a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#sidebar nav a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#sidebar nav h2 {
|
||||
font-size: var(--wa-font-size-m);
|
||||
font-weight: var(--wa-font-weight-medium);
|
||||
border-bottom: solid var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
margin: var(--wa-space-xl) 0 var(--wa-space-xs) 0;
|
||||
}
|
||||
|
||||
#sidebar ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#sidebar ul li {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0.125rem 0.5rem;
|
||||
}
|
||||
|
||||
#sidebar ul ul ul {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
#sidebar ul li a {
|
||||
line-height: 1.33;
|
||||
color: inherit;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#sidebar ul li a:not(.active-link):hover {
|
||||
color: var(--wa-color-text-link);
|
||||
}
|
||||
|
||||
#sidebar nav .active-link {
|
||||
color: var(--wa-color-text-link);
|
||||
border-bottom: dashed 1px var(--wa-color-text-link);
|
||||
}
|
||||
|
||||
#sidebar > header img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
#sidebar {
|
||||
translate: -100%;
|
||||
}
|
||||
|
||||
.sidebar-open #sidebar {
|
||||
translate: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-version {
|
||||
font-size: var(--wa-font-size-s);
|
||||
color: var(--wa-color-text-quiet);
|
||||
text-align: right;
|
||||
margin-top: calc(-1 * var(--wa-space-s));
|
||||
margin-bottom: calc(-1 * var(--wa-space-s));
|
||||
}
|
||||
|
||||
.sidebar-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* Main content */
|
||||
main {
|
||||
position: relative;
|
||||
padding: var(--docs-content-vertical-spacing) var(--docs-content-padding)
|
||||
calc(var(--docs-content-vertical-spacing) * 2) var(--docs-content-padding);
|
||||
margin-left: var(--docs-sidebar-width);
|
||||
}
|
||||
|
||||
.sidebar-open .content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.content__body > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
main {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Component layouts */
|
||||
.content {
|
||||
display: grid;
|
||||
grid-template-columns: 100%;
|
||||
gap: 2rem;
|
||||
position: relative;
|
||||
max-width: var(--docs-content-max-width);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.content--with-toc {
|
||||
/* There's a 2rem gap, so we need to remove it from the column */
|
||||
grid-template-columns: calc(75% - 2rem) min(25%, var(--docs-content-toc-max-width));
|
||||
max-width: calc(var(--docs-content-max-width) + var(--docs-content-toc-max-width));
|
||||
}
|
||||
|
||||
.content__body {
|
||||
order: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content:not(.content--with-toc) .content__toc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content__toc {
|
||||
order: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.content__toc ul {
|
||||
position: sticky;
|
||||
top: 5rem;
|
||||
max-height: calc(100vh - 6rem);
|
||||
font-size: var(--wa-font-size-s);
|
||||
line-height: 1.33;
|
||||
border-left: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
list-style: none;
|
||||
padding: 1rem 0;
|
||||
margin: 0;
|
||||
padding-left: 1rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.content__toc li {
|
||||
padding: 0 0 0 0.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.content__toc li[data-level='3'] {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
/* We don't use them, but just in case */
|
||||
.content__toc li[data-level='4'],
|
||||
.content__toc li[data-level='5'],
|
||||
.content__toc li[data-level='6'] {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.content__toc li:not(:last-of-type) {
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.content__toc a {
|
||||
color: var(--wa-color-text-normal);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.content__toc a:hover {
|
||||
color: var(--wa-color-text-link);
|
||||
}
|
||||
|
||||
.content__toc a.active {
|
||||
color: var(--wa-color-brand-text-on-surface);
|
||||
border-bottom: dashed 1px var(--wa-color-brand-text-on-surface);
|
||||
}
|
||||
|
||||
.content__toc .top a {
|
||||
font-weight: var(--wa-font-weight-medium);
|
||||
color: var(--wa-color-text-quiet);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.content {
|
||||
grid-template-columns: 100%;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.content__toc {
|
||||
position: relative;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.content__toc ul {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
gap: 1rem 1.5rem;
|
||||
position: static;
|
||||
border: none;
|
||||
border-bottom: solid 1px var(--wa-color-surface-border);
|
||||
border-radius: 0;
|
||||
padding: 1rem 1.5rem 1rem 0.5rem; /* extra right padding to hide the fade effect */
|
||||
margin-top: 1rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.content__toc ul::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 1rem; /* don't cover the scrollbar */
|
||||
right: 0;
|
||||
width: 2rem;
|
||||
background: linear-gradient(90deg, rgba(0 0 0 / 0) 0%, var(--wa-color-surface-default) 100%);
|
||||
}
|
||||
|
||||
.content__toc li {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.content__toc li:not(:last-of-type) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.content__toc [data-level]:not([data-level='2']) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content__body {
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Menu toggle */
|
||||
#menu-toggle {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 30;
|
||||
top: 0.25rem;
|
||||
left: 0.25rem;
|
||||
height: auto;
|
||||
width: auto;
|
||||
color: var(--wa-color-neutral-spot);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: var(--wa-color-surface-default);
|
||||
padding: 0.5rem;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
transition: 250ms rotate ease;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
#menu-toggle {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
#menu-toggle svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
html.sidebar-open #menu-toggle {
|
||||
rotate: 180deg;
|
||||
}
|
||||
|
||||
/* Skip to main content */
|
||||
#skip-to-main {
|
||||
position: fixed;
|
||||
top: 0.25rem;
|
||||
left: calc(50% - var(--docs-skip-to-main-width) / 2);
|
||||
z-index: 100;
|
||||
width: var(--docs-skip-to-main-width);
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
border-radius: 9999px;
|
||||
background: var(--wa-color-surface-default);
|
||||
color: var(--wa-color-text-normal);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
/* Print styles */
|
||||
@media print {
|
||||
a:not(.anchor-heading)[href]::after {
|
||||
content: ' (' attr(href) ')';
|
||||
}
|
||||
|
||||
details,
|
||||
pre {
|
||||
border: solid var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
}
|
||||
|
||||
details summary {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
details summary span {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
details summary::marker,
|
||||
details summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.component-page__navigation,
|
||||
.copy-code-button,
|
||||
.code-preview__buttons,
|
||||
.code-preview__resizer {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.flavor-html .code-preview__source--html,
|
||||
.flavor-react .code-preview__source--react {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.flavor-html .code-preview__source--html > pre,
|
||||
.flavor-react .code-preview__source--react > pre {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.code-preview__source-group {
|
||||
border-bottom: solid 1px var(--wa-border-width-s);
|
||||
border-bottom-left-radius: var(--wa-corners-s);
|
||||
border-bottom-right-radius: var(--wa-corners-s);
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
#menu-toggle,
|
||||
#icon-toolbar,
|
||||
.external-link__icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Splash */
|
||||
.splash {
|
||||
display: flex;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
.splash-start {
|
||||
min-width: 440px;
|
||||
}
|
||||
|
||||
.splash li img {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -2px;
|
||||
}
|
||||
|
||||
.splash svg {
|
||||
margin-block-end: var(--wa-space-m);
|
||||
}
|
||||
|
||||
.splash-end {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
width: auto;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.splash-image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.splash-start h1:first-of-type {
|
||||
font-size: var(--wa-font-size-l);
|
||||
font-weight: var(--wa-font-weight-normal);
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1280px) {
|
||||
.splash {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.splash-start {
|
||||
min-width: 0;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.splash-end {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.splash-image {
|
||||
display: block;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
/* Shields */
|
||||
.splash + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Component headers */
|
||||
.component-header h1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.component-header__tag {
|
||||
margin-top: -0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.component-header__tag code {
|
||||
background: none;
|
||||
color: var(--wa-color-text-quiet);
|
||||
font-size: var(--wa-font-size-l);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.component-header__info {
|
||||
margin-bottom: var(--wa-space-2xl);
|
||||
}
|
||||
|
||||
.component-summary {
|
||||
font-size: var(--wa-font-size-l);
|
||||
line-height: 1.6;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
/* Repo buttons */
|
||||
.sidebar-buttons {
|
||||
display: flex;
|
||||
gap: 0.125rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sidebar-buttons .repo-button {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.repo-button wa-icon {
|
||||
color: var(--wa-color-neutral-text-on-spot);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
:not(.sidebar-buttons) > .repo-button {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
body[data-page^='/tokens/'] .table-wrapper td:first-child,
|
||||
body[data-page^='/tokens/'] .table-wrapper td:first-child code {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Border demos */
|
||||
.border-demo {
|
||||
height: 60px;
|
||||
border-left: solid 1px var(--wa-color-brand-spot);
|
||||
}
|
||||
|
||||
.corner-demo {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
background: var(--wa-color-brand-spot);
|
||||
}
|
||||
|
||||
/* Transition demo */
|
||||
.transition-demo {
|
||||
position: relative;
|
||||
background: var(--wa-color-neutral-fill-subtle);
|
||||
width: 8rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.transition-demo:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: var(--wa-color-brand-spot);
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
transition-duration: inherit;
|
||||
transition-property: width;
|
||||
}
|
||||
|
||||
.transition-demo:hover:after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Spacing demo */
|
||||
.spacing-demo {
|
||||
background: var(--wa-color-brand-spot);
|
||||
}
|
||||
|
||||
/* Shadow demo */
|
||||
.shadow-demo {
|
||||
background: transparent;
|
||||
border-radius: 3px;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
/* Color palettes */
|
||||
.color-palette {
|
||||
display: grid;
|
||||
grid-template-columns: 200px repeat(11, 1fr);
|
||||
gap: var(--wa-space-m) 1px;
|
||||
margin: var(--wa-space-2xl) 0;
|
||||
}
|
||||
|
||||
.color-palette__name {
|
||||
font-weight: var(--wa-font-weight-medium);
|
||||
grid-template-columns: repeat(11, 1fr);
|
||||
}
|
||||
|
||||
.color-palette__name code {
|
||||
background: none;
|
||||
font-size: var(--wa-font-size-s);
|
||||
}
|
||||
|
||||
.color-palette__example {
|
||||
font-size: var(--wa-font-size-s);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.color-palette__swatch {
|
||||
height: 3rem;
|
||||
border-radius: var(--wa-corners-xs);
|
||||
}
|
||||
|
||||
.color-palette__swatch--border {
|
||||
box-shadow: inset 0 0 0 1px var(--wa-color-surface-border);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
.color-palette {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
}
|
||||
|
||||
.color-palette__name {
|
||||
grid-column-start: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
.docs-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
/* Search plugin */
|
||||
:root {
|
||||
--docs-search-box-background: var(--wa-form-controls-background);
|
||||
--docs-search-box-border-width: var(--wa-form-controls-border-width);
|
||||
--docs-search-box-border-color: var(--wa-form-controls-resting-color);
|
||||
--docs-search-box-color: var(--wa-form-controls-placeholder-color);
|
||||
|
||||
--docs-search-dialog-background: var(--wa-color-surface-raised);
|
||||
--docs-search-border-width: var(--wa-border-width-s);
|
||||
--docs-search-border-color: var(--wa-color-surface-border);
|
||||
--docs-search-text-color: var(--wa-color-text-normal);
|
||||
--docs-search-text-color-muted: var(--wa-color-text-quiet);
|
||||
--docs-search-font-weight-normal: var(--wa-font-weight-normal);
|
||||
--docs-search-font-weight-semibold: var(--wa-font-weight-medium);
|
||||
--docs-search-border-radius: calc(2 * var(--wa-corners-s));
|
||||
|
||||
--docs-search-accent-color: var(--wa-color-brand-text-on-surface);
|
||||
--docs-search-icon-color: var(--wa-color-neutral-spot);
|
||||
--docs-search-icon-color-active: color-mix(in lch, var(--wa-color-neutral-spot), 8% black);
|
||||
--docs-search-shadow: var(--wa-shadow-level-3);
|
||||
--docs-search-result-background-hover: var(--wa-color-neutral-fill-highlight);
|
||||
--docs-search-result-color-hover: var(--wa-color-neutral-text-on-fill);
|
||||
--docs-search-result-background-active: var(--wa-color-brand-spot);
|
||||
--docs-search-result-color-active: var(--wa-color-brand-text-on-spot);
|
||||
--docs-search-focus-ring: var(--wa-focus-ring);
|
||||
--docs-search-overlay-background: rgb(0 0 0 / 0.33);
|
||||
}
|
||||
|
||||
body.search-visible {
|
||||
padding-right: var(--docs-search-scroll-lock-size) !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/* Search box */
|
||||
.search-box {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 9999px;
|
||||
background: var(--docs-search-box-background);
|
||||
border: solid var(--docs-search-box-border-width) var(--docs-search-box-border-color);
|
||||
font: inherit;
|
||||
color: var(--docs-search-box-color);
|
||||
padding: 0.75rem 1rem;
|
||||
margin: var(--wa-space-l) 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search-box span {
|
||||
flex: 1 1 auto;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
text-align: left;
|
||||
line-height: 1;
|
||||
margin: 0 0.75rem;
|
||||
}
|
||||
|
||||
.search-box:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search-box:focus-visible {
|
||||
outline: var(--docs-search-focus-ring);
|
||||
}
|
||||
|
||||
/* Site search */
|
||||
.search {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.search[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search__overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--docs-search-overlay-background);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.search__dialog {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search__dialog:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search__dialog::backdrop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Fixes an iOS Safari 16.4 bug that draws the parent element's border radius incorrectly when showing/hiding results */
|
||||
.search__header {
|
||||
background-color: var(--docs-search-dialog-background);
|
||||
border-radius: var(--docs-search-border-radius);
|
||||
}
|
||||
|
||||
.search--has-results .search__header {
|
||||
border-top-left-radius: var(--docs-search-border-radius);
|
||||
border-top-right-radius: var(--docs-search-border-radius);
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.search__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
max-height: calc(100vh - 20rem);
|
||||
background-color: var(--docs-search-dialog-background);
|
||||
border-radius: var(--docs-search-border-radius);
|
||||
box-shadow: var(--docs-search-shadow);
|
||||
padding: 0;
|
||||
margin: 10rem auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.search__content {
|
||||
max-width: calc(100% - 2rem);
|
||||
max-height: calc(90svh);
|
||||
margin: 4vh 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.search__input-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search__input-wrapper wa-icon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
flex: 0 0 auto;
|
||||
color: var(--docs-search-icon-color);
|
||||
margin: 0 1.5rem;
|
||||
}
|
||||
|
||||
.search__clear-button {
|
||||
display: flex;
|
||||
background: none;
|
||||
border: none;
|
||||
font: inherit;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search__clear-button[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search__clear-button:active wa-icon {
|
||||
color: var(--docs-search-icon-color-active);
|
||||
}
|
||||
|
||||
.search__input {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
border: none;
|
||||
font: inherit;
|
||||
font-size: 1.5rem;
|
||||
font-weight: var(--docs-search-font-weight-normal);
|
||||
color: var(--docs-search-text-color);
|
||||
background: transparent;
|
||||
padding: 1rem 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search__input::placeholder {
|
||||
color: var(--docs-search-text-color-muted);
|
||||
}
|
||||
|
||||
.search__input::-webkit-search-decoration,
|
||||
.search__input::-webkit-search-cancel-button,
|
||||
.search__input::-webkit-search-results-button,
|
||||
.search__input::-webkit-search-results-decoration {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search__input:focus,
|
||||
.search__input:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search__body {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.search--has-results .search__body {
|
||||
border-top: solid var(--docs-search-border-width) var(--docs-search-border-color);
|
||||
}
|
||||
|
||||
.search__results {
|
||||
display: none;
|
||||
line-height: 1.2;
|
||||
list-style: none;
|
||||
padding: 0.5rem 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search--has-results .search__results {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search__results a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 1.5rem;
|
||||
}
|
||||
|
||||
.search__results a:focus-visible {
|
||||
outline: var(--docs-search-focus-ring);
|
||||
}
|
||||
|
||||
.search__results li a:hover,
|
||||
.search__results li a:hover small {
|
||||
background-color: var(--docs-search-result-background-hover);
|
||||
color: var(--docs-search-result-color-hover);
|
||||
}
|
||||
|
||||
.search__results li[data-selected='true'] a,
|
||||
.search__results li[data-selected='true'] a * {
|
||||
outline: none;
|
||||
background-color: var(--docs-search-result-background-active);
|
||||
color: var(--docs-search-result-color-active);
|
||||
}
|
||||
|
||||
.search__results h3 {
|
||||
font-weight: var(--docs-search-font-weight-semibold);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search__results small {
|
||||
display: block;
|
||||
color: var(--docs-search-text-color-muted);
|
||||
}
|
||||
|
||||
.search__result {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search__result a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.search__result-icon {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
color: var(--docs-search-text-color-muted);
|
||||
}
|
||||
|
||||
.search__result-icon wa-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.search__result__details {
|
||||
width: calc(100% - 3rem);
|
||||
}
|
||||
|
||||
.search__result-title,
|
||||
.search__result-description,
|
||||
.search__result-url {
|
||||
max-width: 400px;
|
||||
line-height: 1.3;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.search__result-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: var(--docs-search-font-weight-semibold);
|
||||
color: var(--docs-search-accent-color);
|
||||
}
|
||||
|
||||
.search__result-description {
|
||||
font-size: 0.875rem;
|
||||
color: var(--docs-search-text-color);
|
||||
}
|
||||
|
||||
.search__result-url {
|
||||
font-size: 0.875rem;
|
||||
color: var(--docs-search-text-color-muted);
|
||||
}
|
||||
|
||||
.search__empty {
|
||||
display: none;
|
||||
border-top: solid var(--docs-search-border-width) var(--docs-search-border-color);
|
||||
text-align: center;
|
||||
color: var(--docs-search-text-color-muted);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.search--no-results .search__empty {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search__footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
border-top: solid var(--docs-search-border-width) var(--docs-search-border-color);
|
||||
border-bottom-left-radius: inherit;
|
||||
border-bottom-right-radius: inherit;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.search__footer small {
|
||||
color: var(--docs-search-text-color-muted);
|
||||
}
|
||||
|
||||
.search__footer small kbd:last-of-type {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.search__footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
/* eslint-disable no-invalid-this */
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const lunr = require('lunr');
|
||||
const { capitalCase } = require('change-case');
|
||||
const { JSDOM } = require('jsdom');
|
||||
const { customElementsManifest, getAllComponents } = require('./_utilities/cem.cjs');
|
||||
const webAwesomeFlavoredMarkdown = require('./_utilities/markdown.cjs');
|
||||
const activeLinks = require('./_utilities/active-links.cjs');
|
||||
const anchorHeadings = require('./_utilities/anchor-headings.cjs');
|
||||
const codePreviews = require('./_utilities/code-previews.cjs');
|
||||
const copyCodeButtons = require('./_utilities/copy-code-buttons.cjs');
|
||||
const externalLinks = require('./_utilities/external-links.cjs');
|
||||
const highlightCodeBlocks = require('./_utilities/highlight-code.cjs');
|
||||
const tableOfContents = require('./_utilities/table-of-contents.cjs');
|
||||
const prettier = require('./_utilities/prettier.cjs');
|
||||
const scrollingTables = require('./_utilities/scrolling-tables.cjs');
|
||||
const typography = require('./_utilities/typography.cjs');
|
||||
const replacer = require('./_utilities/replacer.cjs');
|
||||
|
||||
const assetsDir = 'assets';
|
||||
const cdndir = 'cdn';
|
||||
const npmdir = 'dist';
|
||||
const allComponents = getAllComponents();
|
||||
let hasBuiltSearchIndex = false;
|
||||
|
||||
module.exports = function (eleventyConfig) {
|
||||
//
|
||||
// Global data
|
||||
//
|
||||
eleventyConfig.addGlobalData('baseUrl', 'https://shoelace.style/'); // the production URL
|
||||
eleventyConfig.addGlobalData('layout', 'default'); // make 'default' the default layout
|
||||
eleventyConfig.addGlobalData('toc', true); // enable the table of contents
|
||||
eleventyConfig.addGlobalData('meta', {
|
||||
title: 'Web Awesome',
|
||||
description: 'A forward-thinking library of web components.',
|
||||
image: 'images/og-image.png',
|
||||
version: customElementsManifest.package.version,
|
||||
components: allComponents,
|
||||
cdndir,
|
||||
npmdir
|
||||
});
|
||||
|
||||
//
|
||||
// Layout aliases
|
||||
//
|
||||
eleventyConfig.addLayoutAlias('default', 'default.njk');
|
||||
|
||||
//
|
||||
// Copy assets
|
||||
//
|
||||
eleventyConfig.addPassthroughCopy(assetsDir);
|
||||
eleventyConfig.setServerPassthroughCopyBehavior('passthrough'); // emulates passthrough copy during --serve
|
||||
|
||||
//
|
||||
// Functions
|
||||
//
|
||||
|
||||
// Generates a URL relative to the site's root
|
||||
eleventyConfig.addNunjucksGlobal('rootUrl', (value = '', absolute = false) => {
|
||||
value = path.join('/', value);
|
||||
return absolute ? new URL(value, eleventyConfig.globalData.baseUrl).toString() : value;
|
||||
});
|
||||
|
||||
// Generates a URL relative to the site's asset directory
|
||||
eleventyConfig.addNunjucksGlobal('assetUrl', (value = '', absolute = false) => {
|
||||
value = path.join(`/${assetsDir}`, value);
|
||||
return absolute ? new URL(value, eleventyConfig.globalData.baseUrl).toString() : value;
|
||||
});
|
||||
|
||||
// Fetches a specific component's metadata
|
||||
eleventyConfig.addNunjucksGlobal('getComponent', tagName => {
|
||||
const component = allComponents.find(c => c.tagName === tagName);
|
||||
if (!component) {
|
||||
throw new Error(
|
||||
`Unable to find a component called "${tagName}". Make sure the file name is the same as the component's tag ` +
|
||||
`name (minus the wa- prefix).`
|
||||
);
|
||||
}
|
||||
return component;
|
||||
});
|
||||
|
||||
//
|
||||
// Custom markdown syntaxes
|
||||
//
|
||||
eleventyConfig.setLibrary('md', webAwesomeFlavoredMarkdown);
|
||||
|
||||
//
|
||||
// Filters
|
||||
//
|
||||
eleventyConfig.addFilter('markdown', content => {
|
||||
return webAwesomeFlavoredMarkdown.render(content);
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter('markdownInline', content => {
|
||||
return webAwesomeFlavoredMarkdown.renderInline(content);
|
||||
});
|
||||
|
||||
// Trims whitespace and pipes from the start and end of a string. Useful for CEM types, which can be pipe-delimited.
|
||||
// With Prettier 3, this means a leading pipe will exist if the line wraps.
|
||||
eleventyConfig.addFilter('trimPipes', content => {
|
||||
return typeof content === 'string' ? content.replace(/^(\s|\|)/g, '').replace(/(\s|\|)$/g, '') : content;
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter('classNameToComponentName', className => {
|
||||
let name = capitalCase(className.replace(/^Wa/, ''));
|
||||
if (name === 'Qr Code') name = 'QR Code'; // manual override
|
||||
return name;
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter('removeWaPrefix', tagName => {
|
||||
return tagName.replace(/^wa-/, '');
|
||||
});
|
||||
|
||||
//
|
||||
// Transforms
|
||||
//
|
||||
eleventyConfig.addTransform('html-transform', function (content) {
|
||||
// Parse the template and get a Document object
|
||||
const doc = new JSDOM(content, {
|
||||
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
|
||||
// identify which ones are internal and which ones are external.
|
||||
url: `https://internal/`
|
||||
}).window.document;
|
||||
|
||||
// DOM transforms
|
||||
activeLinks(doc, { pathname: this.page.url });
|
||||
anchorHeadings(doc, {
|
||||
within: '#content .content__body',
|
||||
levels: ['h2', 'h3', 'h4', 'h5']
|
||||
});
|
||||
tableOfContents(doc, {
|
||||
levels: ['h2', 'h3'],
|
||||
container: '#content .content__toc > ul',
|
||||
within: '#content .content__body'
|
||||
});
|
||||
codePreviews(doc);
|
||||
externalLinks(doc, { target: '_blank' });
|
||||
highlightCodeBlocks(doc);
|
||||
scrollingTables(doc);
|
||||
copyCodeButtons(doc); // must be after codePreviews + highlightCodeBlocks
|
||||
typography(doc, '#content');
|
||||
|
||||
replacer(doc, [
|
||||
{ pattern: '%VERSION%', replacement: customElementsManifest.package.version },
|
||||
{ pattern: '%CDNDIR%', replacement: cdndir },
|
||||
{ pattern: '%NPMDIR%', replacement: npmdir }
|
||||
]);
|
||||
|
||||
// Serialize the Document object to an HTML string and prepend the doctype
|
||||
content = `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
|
||||
|
||||
// String transforms
|
||||
content = prettier(content);
|
||||
|
||||
return content;
|
||||
});
|
||||
|
||||
//
|
||||
// Build a search index
|
||||
//
|
||||
eleventyConfig.on('eleventy.after', ({ results }) => {
|
||||
// We only want to build the search index on the first run so all pages get indexed.
|
||||
if (hasBuiltSearchIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
const map = {};
|
||||
const searchIndexFilename = path.join(eleventyConfig.dir.output, assetsDir, 'search.json');
|
||||
const lunrInput = path.resolve('../node_modules/lunr/lunr.min.js');
|
||||
const lunrOutput = path.join(eleventyConfig.dir.output, assetsDir, 'scripts/lunr.js');
|
||||
const searchIndex = lunr(function () {
|
||||
// The search index uses these field names extensively, so shortening them can save some serious bytes. The
|
||||
// initial index file went from 468 KB => 401 KB by using single-character names!
|
||||
this.ref('id'); // id
|
||||
this.field('t', { boost: 50 }); // title
|
||||
this.field('h', { boost: 25 }); // headings
|
||||
this.field('c'); // content
|
||||
|
||||
results.forEach((result, index) => {
|
||||
const url = path
|
||||
.join('/', path.relative(eleventyConfig.dir.output, result.outputPath))
|
||||
.replace(/\\/g, '/') // convert backslashes to forward slashes
|
||||
.replace(/\/index.html$/, '/'); // convert trailing /index.html to /
|
||||
const doc = new JSDOM(result.content, {
|
||||
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
|
||||
// identify which ones are internal and which ones are external.
|
||||
url: `https://internal/`
|
||||
}).window.document;
|
||||
const content = doc.querySelector('#content');
|
||||
|
||||
// Get title and headings
|
||||
const title = (doc.querySelector('title')?.textContent || path.basename(result.outputPath)).trim();
|
||||
const headings = [...content.querySelectorAll('h1, h2, h3, h4')]
|
||||
.map(heading => heading.textContent)
|
||||
.join(' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
|
||||
// Remove code blocks and whitespace from content
|
||||
[...content.querySelectorAll('code[class|=language]')].forEach(code => code.remove());
|
||||
const textContent = content.textContent.replace(/\s+/g, ' ').trim();
|
||||
|
||||
// Update the index and map
|
||||
this.add({ id: index, t: title, h: headings, c: textContent });
|
||||
map[index] = { title, url };
|
||||
});
|
||||
});
|
||||
|
||||
// Copy the Lunr search client and write the index
|
||||
fs.mkdirSync(path.dirname(lunrOutput), { recursive: true });
|
||||
fs.copyFileSync(lunrInput, lunrOutput);
|
||||
fs.writeFileSync(searchIndexFilename, JSON.stringify({ searchIndex, map }), 'utf-8');
|
||||
|
||||
hasBuiltSearchIndex = true;
|
||||
});
|
||||
|
||||
//
|
||||
// Send a signal to stdout that let's the build know we've reached this point
|
||||
//
|
||||
eleventyConfig.on('eleventy.after', () => {
|
||||
console.log('[eleventy.after]');
|
||||
});
|
||||
|
||||
//
|
||||
// Dev server options (see https://www.11ty.dev/docs/dev-server/#options)
|
||||
//
|
||||
eleventyConfig.setServerOptions({
|
||||
domDiff: false, // disable dom diffing so custom elements don't break on reload,
|
||||
port: 4000, // if port 4000 is taken, 11ty will use the next one available
|
||||
watch: ['cdn/**/*'] // additional files to watch that will trigger server updates (array of paths or globs)
|
||||
});
|
||||
|
||||
//
|
||||
// 11ty config
|
||||
//
|
||||
return {
|
||||
dir: {
|
||||
input: 'pages',
|
||||
output: '../_site',
|
||||
includes: '../_includes' // resolved relative to the input dir
|
||||
},
|
||||
markdownTemplateEngine: 'njk', // use Nunjucks instead of Liquid for markdown files
|
||||
templateEngineOverride: ['njk'] // just Nunjucks and then markdown
|
||||
};
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Page Not Found
|
||||
description: "The page you were looking for couldn't be found."
|
||||
permalink: 404.html
|
||||
toc: false
|
||||
---
|
||||
|
||||
<div style="text-align: center;">
|
||||
|
||||
# Page Not Found
|
||||
|
||||

|
||||
|
||||
The page you were looking for couldn't be found.
|
||||
|
||||
Press [[/]] to search, or [head back to the homepage](/).
|
||||
|
||||
</div>
|
||||
@@ -1,443 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Alert
|
||||
description: Alerts are used to display important messages inline or as toast notifications.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-alert open>
|
||||
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
|
||||
This is a standard alert. You can customize its content and even the icon.
|
||||
</wa-alert>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAlert from '@shoelace-style/shoelace/dist/react/alert';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => (
|
||||
<WaAlert open>
|
||||
<WaIcon slot="icon" name="circle-info" variant="regular" />
|
||||
This is a standard alert. You can customize its content and even the icon.
|
||||
</WaAlert>
|
||||
);
|
||||
```
|
||||
|
||||
:::warning
|
||||
Alerts will not be visible if the `open` attribute is not present.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### Variants
|
||||
|
||||
Set the `variant` attribute to change the alert's variant.
|
||||
|
||||
```html:preview
|
||||
<wa-alert variant="brand" open>
|
||||
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
|
||||
<strong>This is super informative</strong><br />
|
||||
You can tell by how pretty the alert is.
|
||||
</wa-alert>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-alert variant="success" open>
|
||||
<wa-icon slot="icon" name="circle-check" variant="regular"></wa-icon>
|
||||
<strong>Your changes have been saved</strong><br />
|
||||
You can safely exit the app now.
|
||||
</wa-alert>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-alert variant="neutral" open>
|
||||
<wa-icon slot="icon" name="gear" variant="regular"></wa-icon>
|
||||
<strong>Your settings have been updated</strong><br />
|
||||
Settings will take effect on next login.
|
||||
</wa-alert>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-alert variant="warning" open>
|
||||
<wa-icon slot="icon" name="triangle-exclamation" variant="regular"></wa-icon>
|
||||
<strong>Your session has ended</strong><br />
|
||||
Please login again to continue.
|
||||
</wa-alert>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-alert variant="danger" open>
|
||||
<wa-icon slot="icon" name="circle-exclamation" variant="regular"></wa-icon>
|
||||
<strong>Your account has been deleted</strong><br />
|
||||
We're very sorry to see you go!
|
||||
</wa-alert>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAlert from '@shoelace-style/shoelace/dist/react/alert';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaAlert variant="brand" open>
|
||||
<WaIcon slot="icon" name="circle-info" variant="regular" />
|
||||
<strong>This is super informative</strong>
|
||||
<br />
|
||||
You can tell by how pretty the alert is.
|
||||
</WaAlert>
|
||||
|
||||
<br />
|
||||
|
||||
<WaAlert variant="success" open>
|
||||
<WaIcon slot="icon" name="circle-check" variant="regular" />
|
||||
<strong>Your changes have been saved</strong>
|
||||
<br />
|
||||
You can safely exit the app now.
|
||||
</WaAlert>
|
||||
|
||||
<br />
|
||||
|
||||
<WaAlert variant="neutral" open>
|
||||
<WaIcon slot="icon" name="gear" variant="regular" />
|
||||
<strong>Your settings have been updated</strong>
|
||||
<br />
|
||||
Settings will take effect on next login.
|
||||
</WaAlert>
|
||||
|
||||
<br />
|
||||
|
||||
<WaAlert variant="warning" open>
|
||||
<WaIcon slot="icon" name="triangle-exclamation" variant="regular" />
|
||||
<strong>Your session has ended</strong>
|
||||
<br />
|
||||
Please login again to continue.
|
||||
</WaAlert>
|
||||
|
||||
<br />
|
||||
|
||||
<WaAlert variant="danger" open>
|
||||
<WaIcon slot="icon" name="circle-exclamation" variant="regular" />
|
||||
<strong>Your account has been deleted</strong>
|
||||
<br />
|
||||
We're very sorry to see you go!
|
||||
</WaAlert>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Closable
|
||||
|
||||
Add the `closable` attribute to show a close button that will hide the alert.
|
||||
|
||||
```html:preview
|
||||
<wa-alert variant="brand" open closable class="alert-closable">
|
||||
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
|
||||
You can close this alert any time!
|
||||
</wa-alert>
|
||||
|
||||
<script>
|
||||
const alert = document.querySelector('.alert-closable');
|
||||
alert.addEventListener('wa-after-hide', () => {
|
||||
setTimeout(() => (alert.open = true), 2000);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaAlert from '@shoelace-style/shoelace/dist/react/alert';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
||||
function handleHide() {
|
||||
setOpen(false);
|
||||
setTimeout(() => setOpen(true), 2000);
|
||||
}
|
||||
|
||||
return (
|
||||
<WaAlert open={open} closable onWaAfterHide={handleHide}>
|
||||
<WaIcon slot="icon" name="circle-info" variant="regular" />
|
||||
You can close this alert any time!
|
||||
</WaAlert>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Without Icons
|
||||
|
||||
Icons are optional. Simply omit the `icon` slot if you don't want them.
|
||||
|
||||
```html:preview
|
||||
<wa-alert variant="brand" open> Nothing fancy here, just a simple alert. </wa-alert>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAlert from '@shoelace-style/shoelace/dist/react/alert';
|
||||
|
||||
const App = () => (
|
||||
<WaAlert variant="brand" open>
|
||||
Nothing fancy here, just a simple alert.
|
||||
</WaAlert>
|
||||
);
|
||||
```
|
||||
|
||||
### Duration
|
||||
|
||||
Set the `duration` attribute to automatically hide an alert after a period of time. This is useful for alerts that don't require acknowledgement.
|
||||
|
||||
```html:preview
|
||||
<div class="alert-duration">
|
||||
<wa-button variant="brand">Show Alert</wa-button>
|
||||
|
||||
<wa-alert variant="brand" duration="3000" closable>
|
||||
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
|
||||
This alert will automatically hide itself after three seconds, unless you interact with it.
|
||||
</wa-alert>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.alert-duration');
|
||||
const button = container.querySelector('wa-button');
|
||||
const alert = container.querySelector('wa-alert');
|
||||
|
||||
button.addEventListener('click', () => alert.show());
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.alert-duration wa-alert {
|
||||
margin-top: var(--wa-space-m);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaAlert from '@shoelace-style/shoelace/dist/react/alert';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const css = `
|
||||
.alert-duration wa-alert {
|
||||
margin-top: var(--wa-space-m);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="alert-duration">
|
||||
<WaButton variant="brand" onClick={() => setOpen(true)}>
|
||||
Show Alert
|
||||
</WaButton>
|
||||
|
||||
<WaAlert variant="brand" duration="3000" open={open} closable onWaAfterHide={() => setOpen(false)}>
|
||||
<WaIcon slot="icon" name="circle-info" variant="regular" />
|
||||
This alert will automatically hide itself after three seconds, unless you interact with it.
|
||||
</WaAlert>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Toast Notifications
|
||||
|
||||
To display an alert as a toast notification, or "toast", create the alert and call its `toast()` method. This will move the alert out of its position in the DOM and into [the toast stack](#the-toast-stack) where it will be shown. Once dismissed, it will be removed from the DOM completely. To reuse a toast, store a reference to it and call `toast()` again later on.
|
||||
|
||||
You should always use the `closable` attribute so users can dismiss the notification. It's also common to set a reasonable `duration` when the notification doesn't require acknowledgement.
|
||||
|
||||
```html:preview
|
||||
<div class="alert-toast">
|
||||
<wa-button variant="brand">Brand</wa-button>
|
||||
<wa-button variant="success">Success</wa-button>
|
||||
<wa-button variant="neutral">Neutral</wa-button>
|
||||
<wa-button variant="warning">Warning</wa-button>
|
||||
<wa-button variant="danger">Danger</wa-button>
|
||||
|
||||
<wa-alert variant="brand" duration="3000" closable>
|
||||
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
|
||||
<strong>This is super informative</strong><br />
|
||||
You can tell by how pretty the alert is.
|
||||
</wa-alert>
|
||||
|
||||
<wa-alert variant="success" duration="3000" closable>
|
||||
<wa-icon slot="icon" name="circle-check" variant="regular"></wa-icon>
|
||||
<strong>Your changes have been saved</strong><br />
|
||||
You can safely exit the app now.
|
||||
</wa-alert>
|
||||
|
||||
<wa-alert variant="neutral" duration="3000" closable>
|
||||
<wa-icon slot="icon" name="gear" variant="regular"></wa-icon>
|
||||
<strong>Your settings have been updated</strong><br />
|
||||
Settings will take effect on next login.
|
||||
</wa-alert>
|
||||
|
||||
<wa-alert variant="warning" duration="3000" closable>
|
||||
<wa-icon slot="icon" name="triangle-exclamation" variant="regular"></wa-icon>
|
||||
<strong>Your session has ended</strong><br />
|
||||
Please login again to continue.
|
||||
</wa-alert>
|
||||
|
||||
<wa-alert variant="danger" duration="3000" closable>
|
||||
<wa-icon slot="icon" name="circle-exclamation" variant="regular"></wa-icon>
|
||||
<strong>Your account has been deleted</strong><br />
|
||||
We're very sorry to see you go!
|
||||
</wa-alert>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.alert-toast');
|
||||
|
||||
['brand', 'success', 'neutral', 'warning', 'danger'].map(variant => {
|
||||
const button = container.querySelector(`wa-button[variant="${variant}"]`);
|
||||
const alert = container.querySelector(`wa-alert[variant="${variant}"]`);
|
||||
|
||||
button.addEventListener('click', () => alert.toast());
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useRef } from 'react';
|
||||
import WaAlert from '@shoelace-style/shoelace/dist/react/alert';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
function showToast(alert) {
|
||||
alert.toast();
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
const brand = useRef(null);
|
||||
const success = useRef(null);
|
||||
const neutral = useRef(null);
|
||||
const warning = useRef(null);
|
||||
const danger = useRef(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaButton variant="brand" onClick={() => brand.current.toast()}>
|
||||
Brand
|
||||
</WaButton>
|
||||
|
||||
<WaButton variant="success" onClick={() => success.current.toast()}>
|
||||
Success
|
||||
</WaButton>
|
||||
|
||||
<WaButton variant="neutral" onClick={() => neutral.current.toast()}>
|
||||
Neutral
|
||||
</WaButton>
|
||||
|
||||
<WaButton variant="warning" onClick={() => warning.current.toast()}>
|
||||
Warning
|
||||
</WaButton>
|
||||
|
||||
<WaButton variant="danger" onClick={() => danger.current.toast()}>
|
||||
Danger
|
||||
</WaButton>
|
||||
|
||||
<WaAlert ref={brand} variant="brand" duration="3000" closable>
|
||||
<WaIcon slot="icon" name="circle-info" variant="regular" />
|
||||
<strong>This is super informative</strong>
|
||||
<br />
|
||||
You can tell by how pretty the alert is.
|
||||
</WaAlert>
|
||||
|
||||
<WaAlert ref={success} variant="success" duration="3000" closable>
|
||||
<WaIcon slot="icon" name="circle-check" variant="regular" />
|
||||
<strong>Your changes have been saved</strong>
|
||||
<br />
|
||||
You can safely exit the app now.
|
||||
</WaAlert>
|
||||
|
||||
<WaAlert ref={neutral} variant="neutral" duration="3000" closable>
|
||||
<WaIcon slot="icon" name="gear" variant="regular" />
|
||||
<strong>Your settings have been updated</strong>
|
||||
<br />
|
||||
Settings will take effect on next login.
|
||||
</WaAlert>
|
||||
|
||||
<WaAlert ref={warning} variant="warning" duration="3000" closable>
|
||||
<WaIcon slot="icon" name="triangle-exclamation" variant="regular" />
|
||||
<strong>Your session has ended</strong>
|
||||
<br />
|
||||
Please login again to continue.
|
||||
</WaAlert>
|
||||
|
||||
<WaAlert ref={danger} variant="danger" duration="3000" closable>
|
||||
<WaIcon slot="icon" name="circle-exclamation" variant="regular" />
|
||||
<strong>Your account has been deleted</strong>
|
||||
<br />
|
||||
We're very sorry to see you go!
|
||||
</WaAlert>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Creating Toasts Imperatively
|
||||
|
||||
For convenience, you can create a utility that emits toast notifications with a function call rather than composing them in your HTML. To do this, generate the alert with JavaScript, append it to the body, and call the `toast()` method as shown in the example below.
|
||||
|
||||
```html:preview
|
||||
<div class="alert-toast-wrapper">
|
||||
<wa-button variant="brand">Create Toast</wa-button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.alert-toast-wrapper');
|
||||
const button = container.querySelector('wa-button');
|
||||
let count = 0;
|
||||
|
||||
// Always escape HTML for text arguments!
|
||||
function escapeHtml(html) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = html;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// Custom function to emit toast notifications
|
||||
function notify(message, variant = 'brand', icon = 'circle-info', duration = 3000) {
|
||||
const alert = Object.assign(document.createElement('wa-alert'), {
|
||||
variant,
|
||||
closable: true,
|
||||
duration: duration,
|
||||
innerHTML: `
|
||||
<wa-icon name="${icon}" variant="regular" slot="icon"></wa-icon>
|
||||
${escapeHtml(message)}
|
||||
`
|
||||
});
|
||||
|
||||
document.body.append(alert);
|
||||
return alert.toast();
|
||||
}
|
||||
|
||||
button.addEventListener('click', () => {
|
||||
notify(`This is custom toast #${++count}`);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### The Toast Stack
|
||||
|
||||
The toast stack is a fixed position singleton element created and managed internally by the alert component. It will be added and removed from the DOM as needed when toasts are shown. When more than one toast is visible, they will stack vertically in the toast stack.
|
||||
|
||||
By default, the toast stack is positioned at the top-right of the viewport. You can change its position by targeting `.wa-toast-stack` in your stylesheet. To make toasts appear at the top-left of the viewport, for example, use the following styles.
|
||||
|
||||
```css
|
||||
.wa-toast-stack {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
By design, it is not possible to show toasts in more than one stack simultaneously. Such behavior is confusing and makes for a poor user experience.
|
||||
:::
|
||||
@@ -1,130 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Animated Image
|
||||
description: A component for displaying animated GIFs and WEBPs that play and pause on interaction.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-animated-image
|
||||
src="https://shoelace.style/assets/images/walk.gif"
|
||||
alt="Animation of untied shoes walking on pavement"
|
||||
></wa-animated-image>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAnimatedImage from '@shoelace-style/shoelace/dist/react/animated-image';
|
||||
|
||||
const App = () => (
|
||||
<WaAnimatedImage
|
||||
src="https://shoelace.style/assets/images/walk.gif"
|
||||
alt="Animation of untied shoes walking on pavement"
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
:::tip
|
||||
This component uses `<canvas>` to draw freeze frames, so images are subject to [cross-origin restrictions](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image).
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### WEBP Images
|
||||
|
||||
Both GIF and WEBP images are supported.
|
||||
|
||||
```html:preview
|
||||
<wa-animated-image
|
||||
src="https://shoelace.style/assets/images/tie.webp"
|
||||
alt="Animation of a shoe being tied"
|
||||
></wa-animated-image>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAnimatedImage from '@shoelace-style/shoelace/dist/react/animated-image';
|
||||
|
||||
const App = () => (
|
||||
<WaAnimatedImage src="https://shoelace.style/assets/images/tie.webp" alt="Animation of a shoe being tied" />
|
||||
);
|
||||
```
|
||||
|
||||
### Setting a Width and Height
|
||||
|
||||
To set a custom size, apply a width and/or height to the host element.
|
||||
|
||||
```html:preview
|
||||
<wa-animated-image
|
||||
src="https://shoelace.style/assets/images/walk.gif"
|
||||
alt="Animation of untied shoes walking on pavement"
|
||||
style="width: 150px; height: 200px;"
|
||||
>
|
||||
</wa-animated-image>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaAnimatedImage from '@shoelace-style/shoelace/dist/react/animated-image';
|
||||
|
||||
const App = () => (
|
||||
<WaAnimatedImage
|
||||
src="https://shoelace.style/assets/images/walk.gif"
|
||||
alt="Animation of untied shoes walking on pavement"
|
||||
style={{ width: '150px', height: '200px' }}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Customizing the Control Box
|
||||
|
||||
You can change the appearance and location of the control box by targeting the `control-box` part in your styles.
|
||||
|
||||
```html:preview
|
||||
<wa-animated-image
|
||||
src="https://shoelace.style/assets/images/walk.gif"
|
||||
alt="Animation of untied shoes walking on pavement"
|
||||
class="animated-image-custom-control-box"
|
||||
></wa-animated-image>
|
||||
|
||||
<style>
|
||||
.animated-image-custom-control-box::part(control-box) {
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: 1rem;
|
||||
left: 1rem;
|
||||
background-color: deeppink;
|
||||
border: none;
|
||||
color: pink;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAnimatedImage from '@shoelace-style/shoelace/dist/react/animated-image';
|
||||
|
||||
const css = `
|
||||
.animated-image-custom-control-box::part(control-box) {
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: 1rem;
|
||||
left: 1rem;
|
||||
background-color: deeppink;
|
||||
border: none;
|
||||
color: pink;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaAnimatedImage
|
||||
className="animated-image-custom-control-box"
|
||||
src="https://shoelace.style/assets/images/walk.gif"
|
||||
alt="Animation of untied shoes walking on pavement"
|
||||
/>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
@@ -1,210 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Avatar
|
||||
description: Avatars are used to represent a person or object.
|
||||
layout: component
|
||||
---
|
||||
|
||||
By default, a generic icon will be shown. You can personalize avatars by adding custom icons, initials, and images. You should always provide a `label` for assistive devices.
|
||||
|
||||
```html:preview
|
||||
<wa-avatar label="User avatar"></wa-avatar>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAvatar from '@shoelace-style/shoelace/dist/react/avatar';
|
||||
|
||||
const App = () => <WaAvatar label="User avatar" />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Images
|
||||
|
||||
To use an image for the avatar, set the `image` and `label` attributes. This will take priority and be shown over initials and icons.
|
||||
Avatar images can be lazily loaded by setting the `loading` attribute to `lazy`.
|
||||
|
||||
```html:preview
|
||||
<wa-avatar
|
||||
image="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
|
||||
label="Avatar of a gray tabby kitten looking down"
|
||||
></wa-avatar>
|
||||
<wa-avatar
|
||||
image="https://images.unsplash.com/photo-1591871937573-74dbba515c4c?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
|
||||
label="Avatar of a white and grey kitten on grey textile"
|
||||
loading="lazy"
|
||||
></wa-avatar>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAvatar from '@shoelace-style/shoelace/dist/react/avatar';
|
||||
|
||||
const App = () => (
|
||||
<WaAvatar
|
||||
image="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
|
||||
label="Avatar of a gray tabby kitten looking down"
|
||||
/>
|
||||
<WaAvatar
|
||||
image="https://images.unsplash.com/photo-1591871937573-74dbba515c4c?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
|
||||
label="Avatar of a white and grey kitten on grey textile"
|
||||
loading="lazy"
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### Initials
|
||||
|
||||
When you don't have an image to use, you can set the `initials` attribute to show something more personalized than an icon.
|
||||
|
||||
```html:preview
|
||||
<wa-avatar initials="WA" label="Avatar with initials: SL"></wa-avatar>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAvatar from '@shoelace-style/shoelace/dist/react/avatar';
|
||||
|
||||
const App = () => <WaAvatar initials="WA" label="Avatar with initials: SL" />;
|
||||
```
|
||||
|
||||
### Custom Icons
|
||||
|
||||
When no image or initials are set, an icon will be shown. The default avatar shows a generic "user" icon, but you can customize this with the `icon` slot.
|
||||
|
||||
```html:preview
|
||||
<wa-avatar label="Avatar with an image icon">
|
||||
<wa-icon slot="icon" name="image" variant="solid"></wa-icon>
|
||||
</wa-avatar>
|
||||
|
||||
<wa-avatar label="Avatar with an archive icon">
|
||||
<wa-icon slot="icon" name="archive" variant="solid"></wa-icon>
|
||||
</wa-avatar>
|
||||
|
||||
<wa-avatar label="Avatar with a briefcase icon">
|
||||
<wa-icon slot="icon" name="briefcase" variant="solid"></wa-icon>
|
||||
</wa-avatar>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAvatar from '@shoelace-style/shoelace/dist/react/avatar';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaAvatar label="Avatar with an image icon">
|
||||
<WaIcon slot="icon" name="image" />
|
||||
</WaAvatar>
|
||||
|
||||
<WaAvatar label="Avatar with an archive icon">
|
||||
<WaIcon slot="icon" name="archive" />
|
||||
</WaAvatar>
|
||||
|
||||
<WaAvatar label="Avatar with a briefcase icon">
|
||||
<WaIcon slot="icon" name="briefcase" />
|
||||
</WaAvatar>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Shapes
|
||||
|
||||
Avatars can be shaped using the `shape` attribute.
|
||||
|
||||
```html:preview
|
||||
<wa-avatar shape="square" label="Square avatar"></wa-avatar>
|
||||
<wa-avatar shape="rounded" label="Rounded avatar"></wa-avatar>
|
||||
<wa-avatar shape="circle" label="Circle avatar"></wa-avatar>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAvatar from '@shoelace-style/shoelace/dist/react/avatar';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaAvatar shape="square" label="Square avatar" />
|
||||
<WaAvatar shape="rounded" label="Rounded avatar" />
|
||||
<WaAvatar shape="circle" label="Circle avatar" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Avatar Groups
|
||||
|
||||
You can group avatars with a few lines of CSS.
|
||||
|
||||
```html:preview
|
||||
<div class="avatar-group">
|
||||
<wa-avatar
|
||||
image="https://images.unsplash.com/photo-1490150028299-bf57d78394e0?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80&crop=right"
|
||||
label="Avatar 1 of 4"
|
||||
></wa-avatar>
|
||||
|
||||
<wa-avatar
|
||||
image="https://images.unsplash.com/photo-1503454537195-1dcabb73ffb9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
|
||||
label="Avatar 2 of 4"
|
||||
></wa-avatar>
|
||||
|
||||
<wa-avatar
|
||||
image="https://images.unsplash.com/photo-1456439663599-95b042d50252?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
|
||||
label="Avatar 3 of 4"
|
||||
></wa-avatar>
|
||||
|
||||
<wa-avatar
|
||||
image="https://images.unsplash.com/flagged/photo-1554078875-e37cb8b0e27d?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=top&q=80"
|
||||
label="Avatar 4 of 4"
|
||||
></wa-avatar>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.avatar-group wa-avatar:not(:first-of-type) {
|
||||
margin-left: calc(-1 * var(--wa-space-m));
|
||||
}
|
||||
|
||||
.avatar-group wa-avatar::part(base) {
|
||||
border: solid 2px var(--wa-color-surface-default);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaAvatar from '@shoelace-style/shoelace/dist/react/avatar';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const css = `
|
||||
.avatar-group wa-avatar:not(:first-of-type) {
|
||||
margin-left: calc(-1 * var(--wa-space-m));
|
||||
}
|
||||
|
||||
.avatar-group wa-avatar::part(base) {
|
||||
border: solid 2px var(--wa-color-surface-default);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="avatar-group">
|
||||
<WaAvatar
|
||||
image="https://images.unsplash.com/photo-1490150028299-bf57d78394e0?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80&crop=right"
|
||||
label="Avatar 1 of 4"
|
||||
/>
|
||||
|
||||
<WaAvatar
|
||||
image="https://images.unsplash.com/photo-1503454537195-1dcabb73ffb9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
|
||||
label="Avatar 2 of 4"
|
||||
/>
|
||||
|
||||
<WaAvatar
|
||||
image="https://images.unsplash.com/photo-1456439663599-95b042d50252?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
|
||||
label="Avatar 3 of 4"
|
||||
/>
|
||||
|
||||
<WaAvatar
|
||||
image="https://images.unsplash.com/flagged/photo-1554078875-e37cb8b0e27d?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=top&q=80"
|
||||
label="Avatar 4 of 4"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
@@ -1,231 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Badge
|
||||
description: Badges are used to draw attention and display statuses or counts.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-badge>Badge</wa-badge>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaBadge from '@shoelace-style/shoelace/dist/react/badge';
|
||||
|
||||
const App = () => <WaBadge>Badge</WaBadge>;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Variants
|
||||
|
||||
Set the `variant` attribute to change the badge's variant.
|
||||
|
||||
```html:preview
|
||||
<wa-badge variant="brand">Brand</wa-badge>
|
||||
<wa-badge variant="success">Success</wa-badge>
|
||||
<wa-badge variant="neutral">Neutral</wa-badge>
|
||||
<wa-badge variant="warning">Warning</wa-badge>
|
||||
<wa-badge variant="danger">Danger</wa-badge>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaBadge from '@shoelace-style/shoelace/dist/react/badge';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaBadge variant="brand">Brand</WaBadge>
|
||||
<WaBadge variant="success">Success</WaBadge>
|
||||
<WaBadge variant="neutral">Neutral</WaBadge>
|
||||
<WaBadge variant="warning">Warning</WaBadge>
|
||||
<WaBadge variant="danger">Danger</WaBadge>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill Badges
|
||||
|
||||
Use the `pill` attribute to give badges rounded edges.
|
||||
|
||||
```html:preview
|
||||
<wa-badge variant="brand" pill>Brand</wa-badge>
|
||||
<wa-badge variant="success" pill>Success</wa-badge>
|
||||
<wa-badge variant="neutral" pill>Neutral</wa-badge>
|
||||
<wa-badge variant="warning" pill>Warning</wa-badge>
|
||||
<wa-badge variant="danger" pill>Danger</wa-badge>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaBadge from '@shoelace-style/shoelace/dist/react/badge';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaBadge variant="brand" pill>
|
||||
Brand
|
||||
</WaBadge>
|
||||
<WaBadge variant="success" pill>
|
||||
Success
|
||||
</WaBadge>
|
||||
<WaBadge variant="neutral" pill>
|
||||
Neutral
|
||||
</WaBadge>
|
||||
<WaBadge variant="warning" pill>
|
||||
Warning
|
||||
</WaBadge>
|
||||
<WaBadge variant="danger" pill>
|
||||
Danger
|
||||
</WaBadge>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pulsating Badges
|
||||
|
||||
Use the `pulse` attribute to draw attention to the badge with a subtle animation.
|
||||
|
||||
```html:preview
|
||||
<div class="badge-pulse">
|
||||
<wa-badge variant="brand" pill pulse>1</wa-badge>
|
||||
<wa-badge variant="success" pill pulse>1</wa-badge>
|
||||
<wa-badge variant="neutral" pill pulse>1</wa-badge>
|
||||
<wa-badge variant="warning" pill pulse>1</wa-badge>
|
||||
<wa-badge variant="danger" pill pulse>1</wa-badge>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.badge-pulse wa-badge:not(:last-of-type) {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaBadge from '@shoelace-style/shoelace/dist/react/badge';
|
||||
|
||||
const css = `
|
||||
.badge-pulse wa-badge:not(:last-of-type) {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="badge-pulse">
|
||||
<WaBadge variant="brand" pill pulse>
|
||||
1
|
||||
</WaBadge>
|
||||
<WaBadge variant="success" pill pulse>
|
||||
1
|
||||
</WaBadge>
|
||||
<WaBadge variant="neutral" pill pulse>
|
||||
1
|
||||
</WaBadge>
|
||||
<WaBadge variant="warning" pill pulse>
|
||||
1
|
||||
</WaBadge>
|
||||
<WaBadge variant="danger" pill pulse>
|
||||
1
|
||||
</WaBadge>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### With Buttons
|
||||
|
||||
One of the most common use cases for badges is attaching them to buttons. To make this easier, badges will be automatically positioned at the top-right when they're a child of a button.
|
||||
|
||||
```html:preview
|
||||
<wa-button>
|
||||
Requests
|
||||
<wa-badge pill>30</wa-badge>
|
||||
</wa-button>
|
||||
|
||||
<wa-button style="margin-inline-start: 1rem;">
|
||||
Warnings
|
||||
<wa-badge variant="warning" pill>8</wa-badge>
|
||||
</wa-button>
|
||||
|
||||
<wa-button style="margin-inline-start: 1rem;">
|
||||
Errors
|
||||
<wa-badge variant="danger" pill>6</wa-badge>
|
||||
</wa-button>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaBadge from '@shoelace-style/shoelace/dist/react/badge';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton>
|
||||
Requests
|
||||
<WaBadge pill>30</WaBadge>
|
||||
</WaButton>
|
||||
|
||||
<WaButton style={{ marginInlineStart: '1rem' }}>
|
||||
Warnings
|
||||
<WaBadge variant="warning" pill>
|
||||
8
|
||||
</WaBadge>
|
||||
</WaButton>
|
||||
|
||||
<WaButton style={{ marginInlineStart: '1rem' }}>
|
||||
Errors
|
||||
<WaBadge variant="danger" pill>
|
||||
6
|
||||
</WaBadge>
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### With Menu Items
|
||||
|
||||
When including badges in menu items, use the `suffix` slot to make sure they're aligned correctly.
|
||||
|
||||
```html:preview
|
||||
<wa-menu style="max-width: 240px;">
|
||||
<wa-menu-label>Messages</wa-menu-label>
|
||||
<wa-menu-item>Comments <wa-badge slot="suffix" variant="neutral" pill>4</wa-badge></wa-menu-item>
|
||||
<wa-menu-item>Replies <wa-badge slot="suffix" variant="neutral" pill>12</wa-badge></wa-menu-item>
|
||||
</wa-menu>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaBadge from '@shoelace-style/shoelace/dist/react/badge';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
import WaMenuLabel from '@shoelace-style/shoelace/dist/react/menu-label';
|
||||
|
||||
const App = () => (
|
||||
<WaMenu
|
||||
style={{ maxWidth: '240px' }}
|
||||
>
|
||||
<WaMenuLabel>Messages</WaMenuLabel>
|
||||
<WaMenuItem>
|
||||
Comments
|
||||
<WaBadge slot="suffix" variant="neutral" pill>
|
||||
4
|
||||
</WaBadge>
|
||||
</WaMenuItem>
|
||||
<WaMenuItem>
|
||||
Replies
|
||||
<WaBadge slot="suffix" variant="neutral" pill>
|
||||
12
|
||||
</WaBadge>
|
||||
</WaMenuItem>
|
||||
</WaMenu>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Breadcrumb Item
|
||||
description: Breadcrumb Items are used inside breadcrumbs to represent different links.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-breadcrumb>
|
||||
<wa-breadcrumb-item>
|
||||
<wa-icon slot="prefix" name="house" variant="solid"></wa-icon>
|
||||
Home
|
||||
</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Clothing</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Shirts</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
|
||||
import WaBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => (
|
||||
<WaBreadcrumb>
|
||||
<WaBreadcrumbItem>
|
||||
<WaIcon slot="prefix" name="house"></WaIcon>
|
||||
Home
|
||||
</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Clothing</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Shirts</WaBreadcrumbItem>
|
||||
</WaBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
:::tip
|
||||
Additional demonstrations can be found in the [breadcrumb examples](/components/breadcrumb).
|
||||
:::
|
||||
@@ -1,258 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Breadcrumb
|
||||
description: Breadcrumbs provide a group of links so users can easily navigate a website's hierarchy.
|
||||
layout: component
|
||||
---
|
||||
|
||||
Breadcrumbs are usually placed before a page's main content with the current page shown last to indicate the user's position in the navigation.
|
||||
|
||||
```html:preview
|
||||
<wa-breadcrumb>
|
||||
<wa-breadcrumb-item>Catalog</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Clothing</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Women's</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Shirts & Tops</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
|
||||
import WaBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
|
||||
|
||||
const App = () => (
|
||||
<WaBreadcrumb>
|
||||
<WaBreadcrumbItem>Catalog</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Clothing</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Women's</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Shirts & Tops</WaBreadcrumbItem>
|
||||
</WaBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Breadcrumb Links
|
||||
|
||||
By default, breadcrumb items are rendered as buttons so you can use them to navigate single-page applications. In this case, you'll need to add event listeners to handle clicks.
|
||||
|
||||
For websites, you'll probably want to use links instead. You can make any breadcrumb item a link by applying an `href` attribute to it. Now, when the user activates it, they'll be taken to the corresponding page — no event listeners required.
|
||||
|
||||
```html:preview
|
||||
<wa-breadcrumb>
|
||||
<wa-breadcrumb-item href="https://example.com/home">Homepage</wa-breadcrumb-item>
|
||||
|
||||
<wa-breadcrumb-item href="https://example.com/home/services">Our Services</wa-breadcrumb-item>
|
||||
|
||||
<wa-breadcrumb-item href="https://example.com/home/services/digital">Digital Media</wa-breadcrumb-item>
|
||||
|
||||
<wa-breadcrumb-item href="https://example.com/home/services/digital/web-design">Web Design</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
|
||||
import WaBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
|
||||
|
||||
const App = () => (
|
||||
<WaBreadcrumb>
|
||||
<WaBreadcrumbItem href="https://example.com/home">Homepage</WaBreadcrumbItem>
|
||||
|
||||
<WaBreadcrumbItem href="https://example.com/home/services">Our Services</WaBreadcrumbItem>
|
||||
|
||||
<WaBreadcrumbItem href="https://example.com/home/services/digital">Digital Media</WaBreadcrumbItem>
|
||||
|
||||
<WaBreadcrumbItem href="https://example.com/home/services/digital/web-design">Web Design</WaBreadcrumbItem>
|
||||
</WaBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Separators
|
||||
|
||||
Use the `separator` slot to change the separator that goes between breadcrumb items. Icons work well, but you can also use text or an image.
|
||||
|
||||
```html:preview
|
||||
<wa-breadcrumb>
|
||||
<wa-icon slot="separator" name="angles-right" variant="solid"></wa-icon>
|
||||
<wa-breadcrumb-item>First</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Second</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Third</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-breadcrumb>
|
||||
<wa-icon slot="separator" name="arrow-right" variant="solid"></wa-icon>
|
||||
<wa-breadcrumb-item>First</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Second</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Third</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-breadcrumb>
|
||||
<span slot="separator">/</span>
|
||||
<wa-breadcrumb-item>First</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Second</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Third</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
|
||||
import WaBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
|
||||
import WaBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaBreadcrumb>
|
||||
<wa-icon slot="separator" name="angles-right" variant="solid" />
|
||||
<WaBreadcrumbItem>First</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Second</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Third</WaBreadcrumbItem>
|
||||
</WaBreadcrumb>
|
||||
|
||||
<br />
|
||||
|
||||
<WaBreadcrumb>
|
||||
<wa-icon slot="separator" name="arrow-right" variant="solid" />
|
||||
<WaBreadcrumbItem>First</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Second</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Third</WaBreadcrumbItem>
|
||||
</WaBreadcrumb>
|
||||
|
||||
<br />
|
||||
|
||||
<WaBreadcrumb>
|
||||
<span slot="separator">/</span>
|
||||
<WaBreadcrumbItem>First</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Second</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Third</WaBreadcrumbItem>
|
||||
</WaBreadcrumb>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefixes
|
||||
|
||||
Use the `prefix` slot to add content before any breadcrumb item.
|
||||
|
||||
```html:preview
|
||||
<wa-breadcrumb>
|
||||
<wa-breadcrumb-item>
|
||||
<wa-icon slot="prefix" name="house" variant="solid"></wa-icon>
|
||||
Home
|
||||
</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Articles</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Traveling</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
|
||||
import WaBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => (
|
||||
<WaBreadcrumb>
|
||||
<WaBreadcrumbItem>
|
||||
<WaIcon slot="prefix" name="house" variant="solid" />
|
||||
Home
|
||||
</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Articles</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Traveling</WaBreadcrumbItem>
|
||||
</WaBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
### Suffixes
|
||||
|
||||
Use the `suffix` slot to add content after any breadcrumb item.
|
||||
|
||||
```html:preview
|
||||
<wa-breadcrumb>
|
||||
<wa-breadcrumb-item>Documents</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Policies</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>
|
||||
Security
|
||||
<wa-icon slot="suffix" name="shield" variant="solid"></wa-icon>
|
||||
</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
|
||||
import WaBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => (
|
||||
<WaBreadcrumb>
|
||||
<WaBreadcrumbItem>Documents</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Policies</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>
|
||||
Security
|
||||
<WaIcon slot="suffix" name="shield" variant="solid"></WaIcon>
|
||||
</WaBreadcrumbItem>
|
||||
</WaBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
### With Dropdowns
|
||||
|
||||
Dropdown menus can be placed in a prefix or suffix slot to provide additional options.
|
||||
|
||||
```html:preview
|
||||
<wa-breadcrumb>
|
||||
<wa-breadcrumb-item>Homepage</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Our Services</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Digital Media</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>
|
||||
Web Design
|
||||
<wa-dropdown slot="suffix">
|
||||
<wa-button slot="trigger" size="small" pill>
|
||||
<wa-icon label="More options" name="ellipsis" variant="solid"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item type="checkbox" checked>Web Design</wa-menu-item>
|
||||
<wa-menu-item type="checkbox">Web Development</wa-menu-item>
|
||||
<wa-menu-item type="checkbox">Marketing</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import {
|
||||
WaBreadcrumb,
|
||||
WaBreadcrumbItem,
|
||||
WaButton,
|
||||
WaDropdown,
|
||||
WaIcon,
|
||||
WaMenu,
|
||||
WaMenuItem
|
||||
} from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<WaBreadcrumb>
|
||||
<WaBreadcrumbItem>Homepage</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Our Services</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>Digital Media</WaBreadcrumbItem>
|
||||
<WaBreadcrumbItem>
|
||||
Web Design
|
||||
<WaDropdown slot="suffix">
|
||||
<WaButton slot="trigger" size="small" pill>
|
||||
<WaIcon label="More options" name="ellipsis"></WaIcon>
|
||||
</WaButton>
|
||||
<WaMenu>
|
||||
<WaMenuItem type="checkbox" checked>
|
||||
Web Design
|
||||
</WaMenuItem>
|
||||
<WaMenuItem type="checkbox">Web Development</WaMenuItem>
|
||||
<WaMenuItem type="checkbox">Marketing</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
</WaBreadcrumbItem>
|
||||
</WaBreadcrumb>
|
||||
);
|
||||
```
|
||||
@@ -1,511 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Button Group
|
||||
description: Button groups can be used to group related buttons into sections.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button>Left</wa-button>
|
||||
<wa-button>Center</wa-button>
|
||||
<wa-button>Right</wa-button>
|
||||
</wa-button-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
|
||||
|
||||
const App = () => (
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton>Left</WaButton>
|
||||
<WaButton>Center</WaButton>
|
||||
<WaButton>Right</WaButton>
|
||||
</WaButtonGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Button Sizes
|
||||
|
||||
All button sizes are supported, but avoid mixing sizes within the same button group.
|
||||
|
||||
```html:preview
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button size="small">Left</wa-button>
|
||||
<wa-button size="small">Center</wa-button>
|
||||
<wa-button size="small">Right</wa-button>
|
||||
</wa-button-group>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button size="medium">Left</wa-button>
|
||||
<wa-button size="medium">Center</wa-button>
|
||||
<wa-button size="medium">Right</wa-button>
|
||||
</wa-button-group>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button size="large">Left</wa-button>
|
||||
<wa-button size="large">Center</wa-button>
|
||||
<wa-button size="large">Right</wa-button>
|
||||
</wa-button-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton size="small">Left</WaButton>
|
||||
<WaButton size="small">Center</WaButton>
|
||||
<WaButton size="small">Right</WaButton>
|
||||
</WaButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton size="medium">Left</WaButton>
|
||||
<WaButton size="medium">Center</WaButton>
|
||||
<WaButton size="medium">Right</WaButton>
|
||||
</WaButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton size="large">Left</WaButton>
|
||||
<WaButton size="large">Center</WaButton>
|
||||
<WaButton size="large">Right</WaButton>
|
||||
</WaButtonGroup>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Theme Buttons
|
||||
|
||||
Theme buttons are supported through the button's `variant` attribute.
|
||||
|
||||
```html:preview
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button variant="brand">Left</wa-button>
|
||||
<wa-button variant="brand">Center</wa-button>
|
||||
<wa-button variant="brand">Right</wa-button>
|
||||
</wa-button-group>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button variant="success">Left</wa-button>
|
||||
<wa-button variant="success">Center</wa-button>
|
||||
<wa-button variant="success">Right</wa-button>
|
||||
</wa-button-group>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button>Left</wa-button>
|
||||
<wa-button>Center</wa-button>
|
||||
<wa-button>Right</wa-button>
|
||||
</wa-button-group>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button variant="warning">Left</wa-button>
|
||||
<wa-button variant="warning">Center</wa-button>
|
||||
<wa-button variant="warning">Right</wa-button>
|
||||
</wa-button-group>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button variant="danger">Left</wa-button>
|
||||
<wa-button variant="danger">Center</wa-button>
|
||||
<wa-button variant="danger">Right</wa-button>
|
||||
</wa-button-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton variant="brand">Left</WaButton>
|
||||
<WaButton variant="brand">Center</WaButton>
|
||||
<WaButton variant="brand">Right</WaButton>
|
||||
</WaButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton variant="success">Left</WaButton>
|
||||
<WaButton variant="success">Center</WaButton>
|
||||
<WaButton variant="success">Right</WaButton>
|
||||
</WaButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton>Left</WaButton>
|
||||
<WaButton>Center</WaButton>
|
||||
<WaButton>Right</WaButton>
|
||||
</WaButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton variant="warning">Left</WaButton>
|
||||
<WaButton variant="warning">Center</WaButton>
|
||||
<WaButton variant="warning">Right</WaButton>
|
||||
</WaButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton variant="danger">Left</WaButton>
|
||||
<WaButton variant="danger">Center</WaButton>
|
||||
<WaButton variant="danger">Right</WaButton>
|
||||
</WaButtonGroup>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill Buttons
|
||||
|
||||
Pill buttons are supported through the button's `pill` attribute.
|
||||
|
||||
```html:preview
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button size="small" pill>Left</wa-button>
|
||||
<wa-button size="small" pill>Center</wa-button>
|
||||
<wa-button size="small" pill>Right</wa-button>
|
||||
</wa-button-group>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button size="medium" pill>Left</wa-button>
|
||||
<wa-button size="medium" pill>Center</wa-button>
|
||||
<wa-button size="medium" pill>Right</wa-button>
|
||||
</wa-button-group>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-button size="large" pill>Left</wa-button>
|
||||
<wa-button size="large" pill>Center</wa-button>
|
||||
<wa-button size="large" pill>Right</wa-button>
|
||||
</wa-button-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton size="small" pill>
|
||||
Left
|
||||
</WaButton>
|
||||
<WaButton size="small" pill>
|
||||
Center
|
||||
</WaButton>
|
||||
<WaButton size="small" pill>
|
||||
Right
|
||||
</WaButton>
|
||||
</WaButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton size="medium" pill>
|
||||
Left
|
||||
</WaButton>
|
||||
<WaButton size="medium" pill>
|
||||
Center
|
||||
</WaButton>
|
||||
<WaButton size="medium" pill>
|
||||
Right
|
||||
</WaButton>
|
||||
</WaButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaButton size="large" pill>
|
||||
Left
|
||||
</WaButton>
|
||||
<WaButton size="large" pill>
|
||||
Center
|
||||
</WaButton>
|
||||
<WaButton size="large" pill>
|
||||
Right
|
||||
</WaButton>
|
||||
</WaButtonGroup>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Dropdowns in Button Groups
|
||||
|
||||
Dropdowns can be placed inside button groups as long as the trigger is an `<wa-button>` element.
|
||||
|
||||
```html:preview
|
||||
<wa-button-group label="Example Button Group">
|
||||
<wa-button>Button</wa-button>
|
||||
<wa-button>Button</wa-button>
|
||||
<wa-dropdown>
|
||||
<wa-button slot="trigger" caret>Dropdown</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Item 1</wa-menu-item>
|
||||
<wa-menu-item>Item 2</wa-menu-item>
|
||||
<wa-menu-item>Item 3</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
</wa-button-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
|
||||
import WaDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaButtonGroup label="Example Button Group">
|
||||
<WaButton>Button</WaButton>
|
||||
<WaButton>Button</WaButton>
|
||||
<WaDropdown>
|
||||
<WaButton slot="trigger" caret>
|
||||
Dropdown
|
||||
</WaButton>
|
||||
<WaMenu>
|
||||
<WaMenuItem>Item 1</WaMenuItem>
|
||||
<WaMenuItem>Item 2</WaMenuItem>
|
||||
<WaMenuItem>Item 3</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
</WaButtonGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Split Buttons
|
||||
|
||||
Create a split button using a button and a dropdown. Use a [visually hidden](/components/visually-hidden) label to ensure the dropdown is accessible to users with assistive devices.
|
||||
|
||||
```html:preview
|
||||
<wa-button-group label="Example Button Group">
|
||||
<wa-button variant="brand">Save</wa-button>
|
||||
<wa-dropdown placement="bottom-end">
|
||||
<wa-button slot="trigger" variant="brand" caret>
|
||||
<wa-visually-hidden>More options</wa-visually-hidden>
|
||||
</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Save</wa-menu-item>
|
||||
<wa-menu-item>Save as…</wa-menu-item>
|
||||
<wa-menu-item>Save all</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
</wa-button-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
|
||||
import WaDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaButtonGroup label="Example Button Group">
|
||||
<WaButton variant="brand">Save</WaButton>
|
||||
<WaDropdown placement="bottom-end">
|
||||
<WaButton slot="trigger" variant="brand" caret></WaButton>
|
||||
<WaMenu>
|
||||
<WaMenuItem>Save</WaMenuItem>
|
||||
<WaMenuItem>Save as…</WaMenuItem>
|
||||
<WaMenuItem>Save all</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
</WaButtonGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Tooltips in Button Groups
|
||||
|
||||
Buttons can be wrapped in tooltips to provide more detail when the user interacts with them.
|
||||
|
||||
```html:preview
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-tooltip content="I'm on the left">
|
||||
<wa-button>Left</wa-button>
|
||||
</wa-tooltip>
|
||||
|
||||
<wa-tooltip content="I'm in the middle">
|
||||
<wa-button>Center</wa-button>
|
||||
</wa-tooltip>
|
||||
|
||||
<wa-tooltip content="I'm on the right">
|
||||
<wa-button>Right</wa-button>
|
||||
</wa-tooltip>
|
||||
</wa-button-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
|
||||
import WaTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaTooltip content="I'm on the left">
|
||||
<WaButton>Left</WaButton>
|
||||
</WaTooltip>
|
||||
|
||||
<WaTooltip content="I'm in the middle">
|
||||
<WaButton>Center</WaButton>
|
||||
</WaTooltip>
|
||||
|
||||
<WaTooltip content="I'm on the right">
|
||||
<WaButton>Right</WaButton>
|
||||
</WaTooltip>
|
||||
</WaButtonGroup>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Toolbar Example
|
||||
|
||||
Create interactive toolbars with button groups.
|
||||
|
||||
```html:preview
|
||||
<div class="button-group-toolbar">
|
||||
<wa-button-group label="History">
|
||||
<wa-tooltip content="Undo">
|
||||
<wa-button><wa-icon name="undo" variant="solid" label="Undo"></wa-icon></wa-button>
|
||||
</wa-tooltip>
|
||||
<wa-tooltip content="Redo">
|
||||
<wa-button><wa-icon name="redo" variant="solid" label="Redo"></wa-icon></wa-button>
|
||||
</wa-tooltip>
|
||||
</wa-button-group>
|
||||
|
||||
<wa-button-group label="Formatting">
|
||||
<wa-tooltip content="Bold">
|
||||
<wa-button><wa-icon name="bold" variant="solid" label="Bold"></wa-icon></wa-button>
|
||||
</wa-tooltip>
|
||||
<wa-tooltip content="Italic">
|
||||
<wa-button><wa-icon name="italic" variant="solid" label="Italic"></wa-icon></wa-button>
|
||||
</wa-tooltip>
|
||||
<wa-tooltip content="Underline">
|
||||
<wa-button><wa-icon name="underline" variant="solid" label="Underline"></wa-icon></wa-button>
|
||||
</wa-tooltip>
|
||||
</wa-button-group>
|
||||
|
||||
<wa-button-group label="Alignment">
|
||||
<wa-tooltip content="Align Left">
|
||||
<wa-button><wa-icon name="align-left" variant="solid" label="Align Left"></wa-icon></wa-button>
|
||||
</wa-tooltip>
|
||||
<wa-tooltip content="Align Center">
|
||||
<wa-button><wa-icon name="align-center" variant="solid" label="Align Center"></wa-icon></wa-button>
|
||||
</wa-tooltip>
|
||||
<wa-tooltip content="Align Right">
|
||||
<wa-button><wa-icon name="align-right" variant="solid" label="Align Right"></wa-icon></wa-button>
|
||||
</wa-tooltip>
|
||||
</wa-button-group>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.button-group-toolbar wa-button-group:not(:last-of-type) {
|
||||
margin-right: var(--wa-space-xs);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
|
||||
|
||||
const css = `
|
||||
.button-group-toolbar wa-button-group:not(:last-of-type) {
|
||||
margin-right: var(--wa-space-xs);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="button-group-toolbar">
|
||||
<WaButtonGroup label="History">
|
||||
<WaTooltip content="Undo">
|
||||
<WaButton>
|
||||
<WaIcon name="undo" variant="solid"></WaIcon>
|
||||
</WaButton>
|
||||
</WaTooltip>
|
||||
<WaTooltip content="Redo">
|
||||
<WaButton>
|
||||
<WaIcon name="redo" variant="solid"></WaIcon>
|
||||
</WaButton>
|
||||
</WaTooltip>
|
||||
</WaButtonGroup>
|
||||
|
||||
<WaButtonGroup label="Formatting">
|
||||
<WaTooltip content="Bold">
|
||||
<WaButton>
|
||||
<WaIcon name="bold" variant="solid"></WaIcon>
|
||||
</WaButton>
|
||||
</WaTooltip>
|
||||
<WaTooltip content="Italic">
|
||||
<WaButton>
|
||||
<WaIcon name="italic" variant="solid"></WaIcon>
|
||||
</WaButton>
|
||||
</WaTooltip>
|
||||
<WaTooltip content="Underline">
|
||||
<WaButton>
|
||||
<WaIcon name="underline" variant="solid"></WaIcon>
|
||||
</WaButton>
|
||||
</WaTooltip>
|
||||
</WaButtonGroup>
|
||||
|
||||
<WaButtonGroup label="Alignment">
|
||||
<WaTooltip content="Align Left">
|
||||
<WaButton>
|
||||
<WaIcon name="align-left" variant="solid"></WaIcon>
|
||||
</WaButton>
|
||||
</WaTooltip>
|
||||
<WaTooltip content="Align Center">
|
||||
<WaButton>
|
||||
<WaIcon name="align-center" variant="solid"></WaIcon>
|
||||
</WaButton>
|
||||
</WaTooltip>
|
||||
<WaTooltip content="Align Right">
|
||||
<WaButton>
|
||||
<WaIcon name="align-right" variant="solid"></WaIcon>
|
||||
</WaButton>
|
||||
</WaTooltip>
|
||||
</WaButtonGroup>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
@@ -1,490 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Button
|
||||
description: Buttons represent actions that are available to the user.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-button>Button</wa-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => <WaButton>Button</WaButton>;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Variants
|
||||
|
||||
Use the `variant` attribute to set the button's variant.
|
||||
|
||||
```html:preview
|
||||
<wa-button variant="brand">Brand</wa-button>
|
||||
<wa-button variant="success">Success</wa-button>
|
||||
<wa-button variant="neutral">Neutral</wa-button>
|
||||
<wa-button variant="warning">Warning</wa-button>
|
||||
<wa-button variant="danger">Danger</wa-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton variant="brand">Brand</WaButton>
|
||||
<WaButton variant="success">Success</WaButton>
|
||||
<WaButton variant="neutral">Neutral</WaButton>
|
||||
<WaButton variant="warning">Warning</WaButton>
|
||||
<WaButton variant="danger">Danger</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a button's size.
|
||||
|
||||
```html:preview
|
||||
<wa-button size="small">Small</wa-button>
|
||||
<wa-button size="medium">Medium</wa-button>
|
||||
<wa-button size="large">Large</wa-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton size="small">Small</WaButton>
|
||||
<WaButton size="medium">Medium</WaButton>
|
||||
<WaButton size="large">Large</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Outline Buttons
|
||||
|
||||
Use the `outline` attribute to draw outlined buttons with transparent backgrounds.
|
||||
|
||||
```html:preview
|
||||
<wa-button variant="brand" outline>Brand</wa-button>
|
||||
<wa-button variant="success" outline>Success</wa-button>
|
||||
<wa-button variant="neutral" outline>Neutral</wa-button>
|
||||
<wa-button variant="warning" outline>Warning</wa-button>
|
||||
<wa-button variant="danger" outline>Danger</wa-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton variant="brand" outline>
|
||||
Brand
|
||||
</WaButton>
|
||||
<WaButton variant="success" outline>
|
||||
Success
|
||||
</WaButton>
|
||||
<WaButton variant="neutral" outline>
|
||||
Neutral
|
||||
</WaButton>
|
||||
<WaButton variant="warning" outline>
|
||||
Warning
|
||||
</WaButton>
|
||||
<WaButton variant="danger" outline>
|
||||
Danger
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill Buttons
|
||||
|
||||
Use the `pill` attribute to give buttons rounded edges.
|
||||
|
||||
```html:preview
|
||||
<wa-button size="small" pill>Small</wa-button>
|
||||
<wa-button size="medium" pill>Medium</wa-button>
|
||||
<wa-button size="large" pill>Large</wa-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton size="small" pill>
|
||||
Small
|
||||
</WaButton>
|
||||
<WaButton size="medium" pill>
|
||||
Medium
|
||||
</WaButton>
|
||||
<WaButton size="large" pill>
|
||||
Large
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Text Buttons
|
||||
|
||||
Use the `text` variant to create text buttons that share the same size as regular buttons but don't have backgrounds or borders.
|
||||
|
||||
```html:preview
|
||||
<wa-button variant="text" size="small">Text</wa-button>
|
||||
<wa-button variant="text" size="medium">Text</wa-button>
|
||||
<wa-button variant="text" size="large">Text</wa-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton variant="text" size="small">
|
||||
Text
|
||||
</WaButton>
|
||||
<WaButton variant="text" size="medium">
|
||||
Text
|
||||
</WaButton>
|
||||
<WaButton variant="text" size="large">
|
||||
Text
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Link Buttons
|
||||
|
||||
It's often helpful to have a button that works like a link. This is possible by setting the `href` attribute, which will make the component render an `<a>` under the hood. This gives you all the default link behavior the browser provides (e.g. [[CMD/CTRL/SHIFT]] + [[CLICK]]) and exposes the `target` and `download` attributes.
|
||||
|
||||
```html:preview
|
||||
<wa-button href="https://example.com/">Link</wa-button>
|
||||
<wa-button href="https://example.com/" target="_blank">New Window</wa-button>
|
||||
<wa-button href="/assets/images/logo.svg" download="shoelace.svg">Download</wa-button>
|
||||
<wa-button href="https://example.com/" disabled>Disabled</wa-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton href="https://example.com/">Link</WaButton>
|
||||
<WaButton href="https://example.com/" target="_blank">
|
||||
New Window
|
||||
</WaButton>
|
||||
<WaButton href="/assets/images/logo.svg" download="shoelace.svg">
|
||||
Download
|
||||
</WaButton>
|
||||
<WaButton href="https://example.com/" disabled>
|
||||
Disabled
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
:::tip
|
||||
When a `target` is set, the link will receive `rel="noreferrer noopener"` for [security reasons](https://mathiasbynens.github.io/rel-noopener/).
|
||||
:::
|
||||
|
||||
### Setting a Custom Width
|
||||
|
||||
As expected, buttons can be given a custom width by setting the `width` attribute. This is useful for making buttons span the full width of their container on smaller screens.
|
||||
|
||||
```html:preview
|
||||
<wa-button size="small" style="width: 100%; margin-bottom: 1rem;">Small</wa-button>
|
||||
<wa-button size="medium" style="width: 100%; margin-bottom: 1rem;">Medium</wa-button>
|
||||
<wa-button size="large" style="width: 100%;">Large</wa-button>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton size="small" style={{ width: '100%', marginBottom: '1rem' }}>
|
||||
Small
|
||||
</WaButton>
|
||||
<WaButton size="medium" style={{ width: '100%', marginBottom: '1rem' }}>
|
||||
Medium
|
||||
</WaButton>
|
||||
<WaButton size="large" style={{ width: '100%' }}>
|
||||
Large
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Prefix and Suffix Icons
|
||||
|
||||
Use the `prefix` and `suffix` slots to add icons.
|
||||
|
||||
```html:preview
|
||||
<wa-button size="small">
|
||||
<wa-icon slot="prefix" name="gear" variant="solid"></wa-icon>
|
||||
Settings
|
||||
</wa-button>
|
||||
|
||||
<wa-button size="small">
|
||||
<wa-icon slot="suffix" name="undo" variant="solid"></wa-icon>
|
||||
Refresh
|
||||
</wa-button>
|
||||
|
||||
<wa-button size="small">
|
||||
<wa-icon slot="prefix" name="link" variant="solid"></wa-icon>
|
||||
<wa-icon slot="suffix" name="arrow-up-right-from-square" variant="solid"></wa-icon>
|
||||
Open
|
||||
</wa-button>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button>
|
||||
<wa-icon slot="prefix" name="gear" variant="solid"></wa-icon>
|
||||
Settings
|
||||
</wa-button>
|
||||
|
||||
<wa-button>
|
||||
<wa-icon slot="suffix" name="undo" variant="solid"></wa-icon>
|
||||
Refresh
|
||||
</wa-button>
|
||||
|
||||
<wa-button>
|
||||
<wa-icon slot="prefix" name="link" variant="solid"></wa-icon>
|
||||
<wa-icon slot="suffix" name="arrow-up-right-from-square" variant="solid"></wa-icon>
|
||||
Open
|
||||
</wa-button>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<wa-button size="large">
|
||||
<wa-icon slot="prefix" name="gear" variant="solid"></wa-icon>
|
||||
Settings
|
||||
</wa-button>
|
||||
|
||||
<wa-button size="large">
|
||||
<wa-icon slot="suffix" name="undo" variant="solid"></wa-icon>
|
||||
Refresh
|
||||
</wa-button>
|
||||
|
||||
<wa-button size="large">
|
||||
<wa-icon slot="prefix" name="link" variant="solid"></wa-icon>
|
||||
<wa-icon slot="suffix" name="arrow-up-right-from-square" variant="solid"></wa-icon>
|
||||
Open
|
||||
</wa-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton size="small">
|
||||
<WaIcon slot="prefix" name="gear" variant="solid"></WaIcon>
|
||||
Settings
|
||||
</WaButton>
|
||||
|
||||
<WaButton size="small">
|
||||
<WaIcon slot="suffix" name="undo" variant="solid"></WaIcon>
|
||||
Refresh
|
||||
</WaButton>
|
||||
|
||||
<WaButton size="small">
|
||||
<WaIcon slot="prefix" name="link" variant="solid"></WaIcon>
|
||||
<WaIcon slot="suffix" name="arrow-up-right-from-square" variant="solid"></WaIcon>
|
||||
Open
|
||||
</WaButton>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<WaButton>
|
||||
<WaIcon slot="prefix" name="gear" variant="solid"></WaIcon>
|
||||
Settings
|
||||
</WaButton>
|
||||
|
||||
<WaButton>
|
||||
<WaIcon slot="suffix" name="undo" variant="solid"></WaIcon>
|
||||
Refresh
|
||||
</WaButton>
|
||||
|
||||
<WaButton>
|
||||
<WaIcon slot="prefix" name="link" variant="solid"></WaIcon>
|
||||
<WaIcon slot="suffix" name="arrow-up-right-from-square" variant="solid"></WaIcon>
|
||||
Open
|
||||
</WaButton>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<WaButton size="large">
|
||||
<WaIcon slot="prefix" name="gear" variant="solid"></WaIcon>
|
||||
Settings
|
||||
</WaButton>
|
||||
|
||||
<WaButton size="large">
|
||||
<WaIcon slot="suffix" name="undo" variant="solid"></WaIcon>
|
||||
Refresh
|
||||
</WaButton>
|
||||
|
||||
<WaButton size="large">
|
||||
<WaIcon slot="prefix" name="link" variant="solid"></WaIcon>
|
||||
<WaIcon slot="suffix" name="arrow-up-right-from-square" variant="solid"></WaIcon>
|
||||
Open
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Caret
|
||||
|
||||
Use the `caret` attribute to add a dropdown indicator when a button will trigger a dropdown, menu, or popover.
|
||||
|
||||
```html:preview
|
||||
<wa-button size="small" caret>Small</wa-button>
|
||||
<wa-button size="medium" caret>Medium</wa-button>
|
||||
<wa-button size="large" caret>Large</wa-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton size="small" caret>
|
||||
Small
|
||||
</WaButton>
|
||||
<WaButton size="medium" caret>
|
||||
Medium
|
||||
</WaButton>
|
||||
<WaButton size="large" caret>
|
||||
Large
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Loading
|
||||
|
||||
Use the `loading` attribute to make a button busy. The width will remain the same as before, preventing adjacent elements from moving around.
|
||||
|
||||
```html:preview
|
||||
<wa-button variant="brand" loading>Brand</wa-button>
|
||||
<wa-button variant="success" loading>Success</wa-button>
|
||||
<wa-button variant="neutral" loading>Neutral</wa-button>
|
||||
<wa-button variant="warning" loading>Warning</wa-button>
|
||||
<wa-button variant="danger" loading>Danger</wa-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton variant="brand" loading>
|
||||
Brand
|
||||
</WaButton>
|
||||
<WaButton variant="success" loading>
|
||||
Success
|
||||
</WaButton>
|
||||
<WaButton variant="neutral" loading>
|
||||
Neutral
|
||||
</WaButton>
|
||||
<WaButton variant="warning" loading>
|
||||
Warning
|
||||
</WaButton>
|
||||
<WaButton variant="danger" loading>
|
||||
Danger
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable a button.
|
||||
|
||||
```html:preview
|
||||
<wa-button variant="brand" disabled>Brand</wa-button>
|
||||
<wa-button variant="success" disabled>Success</wa-button>
|
||||
<wa-button variant="neutral" disabled>Neutral</wa-button>
|
||||
<wa-button variant="warning" disabled>Warning</wa-button>
|
||||
<wa-button variant="danger" disabled>Danger</wa-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaButton variant="brand" disabled>
|
||||
Brand
|
||||
</WaButton>
|
||||
|
||||
<WaButton variant="success" disabled>
|
||||
Success
|
||||
</WaButton>
|
||||
|
||||
<WaButton variant="neutral" disabled>
|
||||
Neutral
|
||||
</WaButton>
|
||||
|
||||
<WaButton variant="warning" disabled>
|
||||
Warning
|
||||
</WaButton>
|
||||
|
||||
<WaButton variant="danger" disabled>
|
||||
Danger
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Styling Buttons
|
||||
|
||||
This example demonstrates how to style buttons using a custom class. This is the recommended approach if you need to add additional variations. To customize an existing variation, modify the selector to target the button's `variant` attribute instead of a class (e.g. `wa-button[variant="brand"]`).
|
||||
|
||||
```html:preview
|
||||
<wa-button class="pink">Pink Button</wa-button>
|
||||
|
||||
<style>
|
||||
wa-button.pink::part(base) {
|
||||
border-radius: 6px;
|
||||
border: solid 2px;
|
||||
background: #ff1493;
|
||||
border-top-color: #ff7ac1;
|
||||
border-left-color: #ff7ac1;
|
||||
border-bottom-color: #ad005c;
|
||||
border-right-color: #ad005c;
|
||||
color: white;
|
||||
font-size: 1.125rem;
|
||||
box-shadow: 0 2px 10px #0002;
|
||||
transition: var(--wa-transition-normal) all;
|
||||
}
|
||||
|
||||
wa-button.pink::part(base):hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
wa-button.pink::part(base):active {
|
||||
border-top-color: #ad005c;
|
||||
border-right-color: #ff7ac1;
|
||||
border-bottom-color: #ff7ac1;
|
||||
border-left-color: #ad005c;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
wa-button.pink::part(base):focus-visible {
|
||||
outline: dashed 2px deeppink;
|
||||
outline-offset: 4px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
@@ -1,307 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Card
|
||||
description: Cards can be used to group related subjects in a container.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-card class="card-overview">
|
||||
<img
|
||||
slot="image"
|
||||
src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
|
||||
alt="A kitten sits patiently between a terracotta pot and decorative grasses."
|
||||
/>
|
||||
|
||||
<strong>Mittens</strong><br />
|
||||
This kitten is as cute as he is playful. Bring him home today!<br />
|
||||
<small>6 weeks old</small>
|
||||
|
||||
<div slot="footer">
|
||||
<wa-button variant="brand" pill>More Info</wa-button>
|
||||
<wa-rating></wa-rating>
|
||||
</div>
|
||||
</wa-card>
|
||||
|
||||
<style>
|
||||
.card-overview {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card-overview small {
|
||||
color: var(--wa-color-text-quiet);
|
||||
}
|
||||
|
||||
.card-overview [slot='footer'] {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaCard from '@shoelace-style/shoelace/dist/react/card';
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
const css = `
|
||||
.card-overview {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card-overview small {
|
||||
color: var(--wa-color-text-quiet);
|
||||
}
|
||||
|
||||
.card-overview [slot="footer"] {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaCard className="card-overview">
|
||||
<img
|
||||
slot="image"
|
||||
src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
|
||||
alt="A kitten sits patiently between a terracotta pot and decorative grasses."
|
||||
/>
|
||||
<strong>Mittens</strong>
|
||||
<br />
|
||||
This kitten is as cute as he is playful. Bring him home today!
|
||||
<br />
|
||||
<small>6 weeks old</small>
|
||||
<div slot="footer">
|
||||
<WaButton variant="brand" pill>
|
||||
More Info
|
||||
</WaButton>
|
||||
<WaRating></WaRating>
|
||||
</div>
|
||||
</WaCard>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Card
|
||||
|
||||
Basic cards aren't very exciting, but they can display any content you want them to.
|
||||
|
||||
```html:preview
|
||||
<wa-card class="card-basic">
|
||||
This is just a basic card. No image, no header, and no footer. Just your content.
|
||||
</wa-card>
|
||||
|
||||
<style>
|
||||
.card-basic {
|
||||
max-width: 300px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaCard from '@shoelace-style/shoelace/dist/react/card';
|
||||
|
||||
const css = `
|
||||
.card-basic {
|
||||
max-width: 300px;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaCard className="card-basic">
|
||||
This is just a basic card. No image, no header, and no footer. Just your content.
|
||||
</WaCard>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Card with Header
|
||||
|
||||
Headers can be used to display titles and more.
|
||||
|
||||
```html:preview
|
||||
<wa-card class="card-header">
|
||||
<div slot="header">
|
||||
Header Title
|
||||
<wa-icon-button name="gear" variant="solid" label="Settings"></wa-icon-button>
|
||||
</div>
|
||||
|
||||
This card has a header. You can put all sorts of things in it!
|
||||
</wa-card>
|
||||
|
||||
<style>
|
||||
.card-header {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card-header [slot='header'] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-header h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card-header wa-icon-button {
|
||||
font-size: var(--wa-font-size-m);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaCard from '@shoelace-style/shoelace/dist/react/card';
|
||||
import WaIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
|
||||
|
||||
const css = `
|
||||
.card-header {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card-header [slot="header"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-header h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card-header wa-icon-button {
|
||||
font-size: var(--wa-font-size-m);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaCard className="card-header">
|
||||
<div slot="header">
|
||||
Header Title
|
||||
<WaIconButton name="gear" variant="solid"></WaIconButton>
|
||||
</div>
|
||||
This card has a header. You can put all sorts of things in it!
|
||||
</WaCard>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Card with Footer
|
||||
|
||||
Footers can be used to display actions, summaries, or other relevant content.
|
||||
|
||||
```html:preview
|
||||
<wa-card class="card-footer">
|
||||
This card has a footer. You can put all sorts of things in it!
|
||||
|
||||
<div slot="footer">
|
||||
<wa-rating></wa-rating>
|
||||
<wa-button variant="brand">Preview</wa-button>
|
||||
</div>
|
||||
</wa-card>
|
||||
|
||||
<style>
|
||||
.card-footer {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card-footer [slot='footer'] {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaCard from '@shoelace-style/shoelace/dist/react/card';
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
const css = `
|
||||
.card-footer {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card-footer [slot="footer"] {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaCard className="card-footer">
|
||||
This card has a footer. You can put all sorts of things in it!
|
||||
<div slot="footer">
|
||||
<WaRating></WaRating>
|
||||
<WaButton slot="footer" variant="brand">
|
||||
Preview
|
||||
</WaButton>
|
||||
</div>
|
||||
</WaCard>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Images
|
||||
|
||||
Cards accept an `image` slot. The image is displayed atop the card and stretches to fit.
|
||||
|
||||
```html:preview
|
||||
<wa-card class="card-image">
|
||||
<img
|
||||
slot="image"
|
||||
src="https://images.unsplash.com/photo-1547191783-94d5f8f6d8b1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&q=80"
|
||||
alt="A kitten walks towards camera on top of pallet."
|
||||
/>
|
||||
This is a kitten, but not just any kitten. This kitten likes walking along pallets.
|
||||
</wa-card>
|
||||
|
||||
<style>
|
||||
.card-image {
|
||||
max-width: 300px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaCard from '@shoelace-style/shoelace/dist/react/card';
|
||||
|
||||
const css = `
|
||||
.card-image {
|
||||
max-width: 300px;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaCard className="card-image">
|
||||
<img
|
||||
slot="image"
|
||||
src="https://images.unsplash.com/photo-1547191783-94d5f8f6d8b1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&q=80"
|
||||
alt="A kitten walks towards camera on top of pallet."
|
||||
/>
|
||||
This is a kitten, but not just any kitten. This kitten likes walking along pallets.
|
||||
</WaCard>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
@@ -1,85 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Carousel Item
|
||||
description: A carousel item represent a slide within a carousel.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-carousel pagination>
|
||||
<wa-carousel-item>
|
||||
<img
|
||||
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
|
||||
src="/assets/examples/carousel/mountains.jpg"
|
||||
/>
|
||||
</wa-carousel-item>
|
||||
<wa-carousel-item>
|
||||
<img
|
||||
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
|
||||
src="/assets/examples/carousel/waterfall.jpg"
|
||||
/>
|
||||
</wa-carousel-item>
|
||||
<wa-carousel-item>
|
||||
<img
|
||||
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
|
||||
src="/assets/examples/carousel/sunset.jpg"
|
||||
/>
|
||||
</wa-carousel-item>
|
||||
<wa-carousel-item>
|
||||
<img
|
||||
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
|
||||
src="/assets/examples/carousel/field.jpg"
|
||||
/>
|
||||
</wa-carousel-item>
|
||||
<wa-carousel-item>
|
||||
<img
|
||||
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
|
||||
src="/assets/examples/carousel/valley.jpg"
|
||||
/>
|
||||
</wa-carousel-item>
|
||||
</wa-carousel>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaCarousel from '@shoelace-style/shoelace/dist/react/carousel';
|
||||
import WaCarouselItem from '@shoelace-style/shoelace/dist/react/carousel-item';
|
||||
|
||||
const App = () => (
|
||||
<WaCarousel pagination>
|
||||
<WaCarouselItem>
|
||||
<img
|
||||
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
|
||||
src="/assets/examples/carousel/mountains.jpg"
|
||||
/>
|
||||
</WaCarouselItem>
|
||||
<WaCarouselItem>
|
||||
<img
|
||||
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
|
||||
src="/assets/examples/carousel/waterfall.jpg"
|
||||
/>
|
||||
</WaCarouselItem>
|
||||
<WaCarouselItem>
|
||||
<img
|
||||
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
|
||||
src="/assets/examples/carousel/sunset.jpg"
|
||||
/>
|
||||
</WaCarouselItem>
|
||||
<WaCarouselItem>
|
||||
<img
|
||||
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
|
||||
src="/assets/examples/carousel/field.jpg"
|
||||
/>
|
||||
</WaCarouselItem>
|
||||
<WaCarouselItem>
|
||||
<img
|
||||
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
|
||||
src="/assets/examples/carousel/valley.jpg"
|
||||
/>
|
||||
</WaCarouselItem>
|
||||
</WaCarousel>
|
||||
);
|
||||
```
|
||||
|
||||
:::tip
|
||||
Additional demonstrations can be found in the [carousel examples](/components/carousel).
|
||||
:::
|
||||
@@ -1,166 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Checkbox
|
||||
description: Checkboxes allow the user to toggle an option on or off.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-checkbox>Checkbox</wa-checkbox>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
|
||||
|
||||
const App = () => <WaCheckbox>Checkbox</WaCheckbox>;
|
||||
```
|
||||
|
||||
:::tip
|
||||
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### Checked
|
||||
|
||||
Use the `checked` attribute to activate the checkbox.
|
||||
|
||||
```html:preview
|
||||
<wa-checkbox checked>Checked</wa-checkbox>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
|
||||
|
||||
const App = () => <WaCheckbox checked>Checked</WaCheckbox>;
|
||||
```
|
||||
|
||||
### Indeterminate
|
||||
|
||||
Use the `indeterminate` attribute to make the checkbox indeterminate.
|
||||
|
||||
```html:preview
|
||||
<wa-checkbox indeterminate>Indeterminate</wa-checkbox>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
|
||||
|
||||
const App = () => <WaCheckbox indeterminate>Indeterminate</WaCheckbox>;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable the checkbox.
|
||||
|
||||
```html:preview
|
||||
<wa-checkbox disabled>Disabled</wa-checkbox>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
|
||||
|
||||
const App = () => <WaCheckbox disabled>Disabled</WaCheckbox>;
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a checkbox's size.
|
||||
|
||||
```html:preview
|
||||
<wa-checkbox size="small">Small</wa-checkbox>
|
||||
<br />
|
||||
<wa-checkbox size="medium">Medium</wa-checkbox>
|
||||
<br />
|
||||
<wa-checkbox size="large">Large</wa-checkbox>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaCheckbox size="small">Small</WaCheckbox>
|
||||
<br />
|
||||
<WaCheckbox size="medium">Medium</WaCheckbox>
|
||||
<br />
|
||||
<WaCheckbox size="large">Large</WaCheckbox>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Validity
|
||||
|
||||
Use the `setCustomValidity()` method to set a custom validation message. This will prevent the form from submitting and make the browser display the error message you provide. To clear the error, call this function with an empty string.
|
||||
|
||||
```html:preview
|
||||
<form class="custom-validity">
|
||||
<wa-checkbox>Check me</wa-checkbox>
|
||||
<br />
|
||||
<wa-button type="submit" variant="brand" style="margin-top: 1rem;">Submit</wa-button>
|
||||
</form>
|
||||
<script>
|
||||
const form = document.querySelector('.custom-validity');
|
||||
const checkbox = form.querySelector('wa-checkbox');
|
||||
const errorMessage = `Don't forget to check me!`;
|
||||
|
||||
// Set initial validity as soon as the element is defined
|
||||
customElements.whenDefined('wa-checkbox').then(async () => {
|
||||
await checkbox.updateComplete;
|
||||
checkbox.setCustomValidity(errorMessage);
|
||||
});
|
||||
|
||||
// Update validity on change
|
||||
checkbox.addEventListener('wa-change', () => {
|
||||
checkbox.setCustomValidity(checkbox.checked ? '' : errorMessage);
|
||||
});
|
||||
|
||||
// Handle submit
|
||||
customElements.whenDefined('wa-checkbox').then(() => {
|
||||
form.addEventListener('submit', event => {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import { useEffect, useRef } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
|
||||
|
||||
const App = () => {
|
||||
const checkbox = useRef(null);
|
||||
const errorMessage = `Don't forget to check me!`;
|
||||
|
||||
function handleChange() {
|
||||
checkbox.current.setCustomValidity(checkbox.current.checked ? '' : errorMessage);
|
||||
}
|
||||
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
checkbox.current.setCustomValidity(errorMessage);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<form class="custom-validity" onSubmit={handleSubmit}>
|
||||
<WaCheckbox ref={checkbox} onWaChange={handleChange}>
|
||||
Check me
|
||||
</WaCheckbox>
|
||||
<br />
|
||||
<WaButton type="submit" variant="brand" style={{ marginTop: '1rem' }}>
|
||||
Submit
|
||||
</WaButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
@@ -1,140 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Color Picker
|
||||
description: Color pickers allow the user to select a color.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-color-picker label="Select a color"></wa-color-picker>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaColorPicker from '@shoelace-style/shoelace/dist/react/color-picker';
|
||||
|
||||
const App = () => <WaColorPicker label="Select a color" />;
|
||||
```
|
||||
|
||||
:::tip
|
||||
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### Initial Value
|
||||
|
||||
Use the `value` attribute to set an initial value for the color picker.
|
||||
|
||||
```html:preview
|
||||
<wa-color-picker value="#4a90e2" label="Select a color"></wa-color-picker>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaColorPicker from '@shoelace-style/shoelace/dist/react/color-picker';
|
||||
|
||||
const App = () => <WaColorPicker value="#4a90e2" label="Select a color" />;
|
||||
```
|
||||
|
||||
### Opacity
|
||||
|
||||
Use the `opacity` attribute to enable the opacity slider. When this is enabled, the value will be displayed as HEXA, RGBA, HSLA, or HSVA based on `format`.
|
||||
|
||||
```html:preview
|
||||
<wa-color-picker value="#f5a623ff" opacity label="Select a color"></wa-color-picker>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaColorPicker from '@shoelace-style/shoelace/dist/react/color-picker';
|
||||
|
||||
const App = () => <WaColorPicker opacity label="Select a color" />;
|
||||
```
|
||||
|
||||
### Formats
|
||||
|
||||
Set the color picker's format with the `format` attribute. Valid options include `hex`, `rgb`, `hsl`, and `hsv`. Note that the color picker's input will accept any parsable format (including CSS color names) regardless of this option.
|
||||
|
||||
To prevent users from toggling the format themselves, add the `no-format-toggle` attribute.
|
||||
|
||||
```html:preview
|
||||
<wa-color-picker format="hex" value="#4a90e2" label="Select a color"></wa-color-picker>
|
||||
<wa-color-picker format="rgb" value="rgb(80, 227, 194)" label="Select a color"></wa-color-picker>
|
||||
<wa-color-picker format="hsl" value="hsl(290, 87%, 47%)" label="Select a color"></wa-color-picker>
|
||||
<wa-color-picker format="hsv" value="hsv(55, 89%, 97%)" label="Select a color"></wa-color-picker>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaColorPicker from '@shoelace-style/shoelace/dist/react/color-picker';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaColorPicker format="hex" value="#4a90e2" />
|
||||
<WaColorPicker format="rgb" value="rgb(80, 227, 194)" />
|
||||
<WaColorPicker format="hsl" value="hsl(290, 87%, 47%)" />
|
||||
<WaColorPicker format="hsv" value="hsv(55, 89%, 97%)" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Swatches
|
||||
|
||||
Use the `swatches` attribute to add convenient presets to the color picker. Any format the color picker can parse is acceptable (including CSS color names), but each value must be separated by a semicolon (`;`). Alternatively, you can pass an array of color values to this property using JavaScript.
|
||||
|
||||
```html:preview
|
||||
<wa-color-picker
|
||||
label="Select a color"
|
||||
swatches="
|
||||
#d0021b; #f5a623; #f8e71c; #8b572a; #7ed321; #417505; #bd10e0; #9013fe;
|
||||
#4a90e2; #50e3c2; #b8e986; #000; #444; #888; #ccc; #fff;
|
||||
"
|
||||
></wa-color-picker>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaColorPicker from '@shoelace-style/shoelace/dist/react/color-picker';
|
||||
|
||||
const App = () => (
|
||||
<WaColorPicker
|
||||
label="Select a color"
|
||||
swatches="
|
||||
#d0021b; #f5a623; #f8e71c; #8b572a; #7ed321; #417505; #bd10e0; #9013fe;
|
||||
#4a90e2; #50e3c2; #b8e986; #000; #444; #888; #ccc; #fff;
|
||||
"
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change the color picker's trigger size.
|
||||
|
||||
```html:preview
|
||||
<wa-color-picker size="small" label="Select a color"></wa-color-picker>
|
||||
<wa-color-picker size="medium" label="Select a color"></wa-color-picker>
|
||||
<wa-color-picker size="large" label="Select a color"></wa-color-picker>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaColorPicker from '@shoelace-style/shoelace/dist/react/color-picker';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaColorPicker size="small" label="Select a color" />
|
||||
<WaColorPicker size="medium" label="Select a color" />
|
||||
<WaColorPicker size="large" label="Select a color" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Inline
|
||||
|
||||
The color picker can be rendered inline instead of in a dropdown using the `inline` attribute.
|
||||
|
||||
```html:preview
|
||||
<wa-color-picker inline label="Select a color"></wa-color-picker>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaColorPicker from '@shoelace-style/shoelace/dist/react/color-picker';
|
||||
|
||||
const App = () => <WaColorPicker inline label="Select a color" />;
|
||||
```
|
||||
@@ -1,137 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Details
|
||||
description: Details show a brief summary and expand to show additional content.
|
||||
layout: component
|
||||
---
|
||||
|
||||
<!-- cspell:dictionaries lorem-ipsum -->
|
||||
|
||||
```html:preview
|
||||
<wa-details summary="Toggle Me">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</wa-details>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaDetails from '@shoelace-style/shoelace/dist/react/details';
|
||||
|
||||
const App = () => (
|
||||
<WaDetails summary="Toggle Me">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</WaDetails>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disable` attribute to prevent the details from expanding.
|
||||
|
||||
```html:preview
|
||||
<wa-details summary="Disabled" disabled>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</wa-details>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaDetails from '@shoelace-style/shoelace/dist/react/details';
|
||||
|
||||
const App = () => (
|
||||
<WaDetails summary="Disabled" disabled>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</WaDetails>
|
||||
);
|
||||
```
|
||||
|
||||
### Customizing the Summary Icon
|
||||
|
||||
Use the `expand-icon` and `collapse-icon` slots to change the expand and collapse icons, respectively. To disable the animation, override the `rotate` property on the `summary-icon` part as shown below.
|
||||
|
||||
```html:preview
|
||||
<wa-details summary="Toggle Me" class="custom-icons">
|
||||
<wa-icon name="square-plus" slot="expand-icon" variant="regular"></wa-icon>
|
||||
<wa-icon name="square-minus" slot="collapse-icon" variant="regular"></wa-icon>
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</wa-details>
|
||||
|
||||
<style>
|
||||
wa-details.custom-icons::part(summary-icon) {
|
||||
/* Disable the expand/collapse animation */
|
||||
rotate: none;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaDetails from '@shoelace-style/shoelace/dist/react/details';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const css = `
|
||||
wa-details.custom-icon::part(summary-icon) {
|
||||
/* Disable the expand/collapse animation */
|
||||
rotate: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaDetails summary="Toggle Me" class="custom-icon">
|
||||
<WaIcon name="square-plus" slot="expand-icon" />
|
||||
<WaIcon name="square-minus" slot="collapse-icon" />
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
|
||||
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
||||
consequat.
|
||||
</WaDetails>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Grouping Details
|
||||
|
||||
Details are designed to function independently, but you can simulate a group or "accordion" where only one is shown at a time by listening for the `wa-show` event.
|
||||
|
||||
```html:preview
|
||||
<div class="details-group-example">
|
||||
<wa-details summary="First" open>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</wa-details>
|
||||
|
||||
<wa-details summary="Second">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</wa-details>
|
||||
|
||||
<wa-details summary="Third">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</wa-details>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.details-group-example');
|
||||
|
||||
// Close all other details when one is shown
|
||||
container.addEventListener('wa-show', event => {
|
||||
if (event.target.localName === 'wa-details') {
|
||||
[...container.querySelectorAll('wa-details')].map(details => (details.open = event.target === details));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.details-group-example wa-details:not(:last-of-type) {
|
||||
margin-bottom: var(--wa-space-2xs);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
@@ -1,329 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Dialog
|
||||
description: 'Dialogs, sometimes called "modals", appear above the page and require the user''s immediate attention.'
|
||||
layout: component
|
||||
---
|
||||
|
||||
<!-- cspell:dictionaries lorem-ipsum -->
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" class="dialog-overview">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-dialog>
|
||||
|
||||
<wa-button>Open Dialog</wa-button>
|
||||
|
||||
<script>
|
||||
const dialog = document.querySelector('.dialog-overview');
|
||||
const openButton = dialog.nextElementSibling;
|
||||
const closeButton = dialog.querySelector('wa-button[slot="footer"]');
|
||||
|
||||
openButton.addEventListener('click', () => dialog.show());
|
||||
closeButton.addEventListener('click', () => dialog.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDialog from '@shoelace-style/shoelace/dist/react/dialog';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDialog>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Dialog</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Custom Width
|
||||
|
||||
Use the `--width` custom property to set the dialog's width.
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" class="dialog-width" style="--width: 50vw;">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-dialog>
|
||||
|
||||
<wa-button>Open Dialog</wa-button>
|
||||
|
||||
<script>
|
||||
const dialog = document.querySelector('.dialog-width');
|
||||
const openButton = dialog.nextElementSibling;
|
||||
const closeButton = dialog.querySelector('wa-button[slot="footer"]');
|
||||
|
||||
openButton.addEventListener('click', () => dialog.show());
|
||||
closeButton.addEventListener('click', () => dialog.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDialog from '@shoelace-style/shoelace/dist/react/dialog';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" open={open} style={{ '--width': '50vw' }} onWaAfterHide={() => setOpen(false)}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDialog>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Dialog</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Scrolling
|
||||
|
||||
By design, a dialog's height will never exceed that of the viewport. As such, dialogs will not scroll with the page ensuring the header and footer are always accessible to the user.
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" class="dialog-scrolling">
|
||||
<div style="height: 150vh; border: dashed 2px var(--wa-color-surface-border); padding: 0 1rem;">
|
||||
<p>Scroll down and give it a try! 👇</p>
|
||||
</div>
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-dialog>
|
||||
|
||||
<wa-button>Open Dialog</wa-button>
|
||||
|
||||
<script>
|
||||
const dialog = document.querySelector('.dialog-scrolling');
|
||||
const openButton = dialog.nextElementSibling;
|
||||
const closeButton = dialog.querySelector('wa-button[slot="footer"]');
|
||||
|
||||
openButton.addEventListener('click', () => dialog.show());
|
||||
closeButton.addEventListener('click', () => dialog.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDialog from '@shoelace-style/shoelace/dist/react/dialog';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<div
|
||||
style={{
|
||||
height: '150vh',
|
||||
border: 'dashed 2px var(--wa-color-surface-border)',
|
||||
padding: '0 1rem'
|
||||
}}
|
||||
>
|
||||
<p>Scroll down and give it a try! 👇</p>
|
||||
</div>
|
||||
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDialog>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Dialog</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Header Actions
|
||||
|
||||
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [icon buttons](/components/icon-button) if needed.
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" class="dialog-header-actions">
|
||||
<wa-icon-button class="new-window" slot="header-actions" name="arrow-up-right-from-square" variant="solid"></wa-icon-button>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-dialog>
|
||||
|
||||
<wa-button>Open Dialog</wa-button>
|
||||
|
||||
<script>
|
||||
const dialog = document.querySelector('.dialog-header-actions');
|
||||
const openButton = dialog.nextElementSibling;
|
||||
const closeButton = dialog.querySelector('wa-button[slot="footer"]');
|
||||
const newWindowButton = dialog.querySelector('.new-window');
|
||||
|
||||
openButton.addEventListener('click', () => dialog.show());
|
||||
closeButton.addEventListener('click', () => dialog.hide());
|
||||
newWindowButton.addEventListener('click', () => window.open(location.href));
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDialog from '@shoelace-style/shoelace/dist/react/dialog';
|
||||
import WaIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaIconButton
|
||||
class="new-window"
|
||||
slot="header-actions"
|
||||
name="arrow-up-right-from-square"
|
||||
onClick={() => window.open(location.href)}
|
||||
/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDialog>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Dialog</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Preventing the Dialog from Closing
|
||||
|
||||
By default, dialogs will close when the user clicks the close button, clicks the overlay, or presses the [[Escape]] key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.
|
||||
|
||||
To keep the dialog open in such cases, you can cancel the `wa-request-close` event. When canceled, the dialog will remain open and pulse briefly to draw the user's attention to it.
|
||||
|
||||
You can use `event.detail.source` to determine what triggered the request to close. This example prevents the dialog from closing when the overlay is clicked, but allows the close button or [[Escape]] to dismiss it.
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" class="dialog-deny-close">
|
||||
This dialog will not close when you click on the overlay.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-dialog>
|
||||
|
||||
<wa-button>Open Dialog</wa-button>
|
||||
|
||||
<script>
|
||||
const dialog = document.querySelector('.dialog-deny-close');
|
||||
const openButton = dialog.nextElementSibling;
|
||||
const closeButton = dialog.querySelector('wa-button[slot="footer"]');
|
||||
|
||||
openButton.addEventListener('click', () => dialog.show());
|
||||
closeButton.addEventListener('click', () => dialog.hide());
|
||||
|
||||
// Prevent the dialog from closing when the user clicks on the overlay
|
||||
dialog.addEventListener('wa-request-close', event => {
|
||||
if (event.detail.source === 'overlay') {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDialog from '@shoelace-style/shoelace/dist/react/dialog';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// Prevent the dialog from closing when the user clicks on the overlay
|
||||
function handleRequestClose(event) {
|
||||
if (event.detail.source === 'overlay') {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" open={open} onWaRequestClose={handleRequestClose} onWaAfterHide={() => setOpen(false)}>
|
||||
This dialog will not close when you click on the overlay.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDialog>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Dialog</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Customizing Initial Focus
|
||||
|
||||
By default, the dialog's panel will gain focus when opened. This allows a subsequent tab press to focus on the first tabbable element in the dialog. If you want a different element to have focus, add the `autofocus` attribute to it as shown below.
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" class="dialog-focus">
|
||||
<wa-input autofocus placeholder="I will have focus when the dialog is opened"></wa-input>
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-dialog>
|
||||
|
||||
<wa-button>Open Dialog</wa-button>
|
||||
|
||||
<script>
|
||||
const dialog = document.querySelector('.dialog-focus');
|
||||
const input = dialog.querySelector('wa-input');
|
||||
const openButton = dialog.nextElementSibling;
|
||||
const closeButton = dialog.querySelector('wa-button[slot="footer"]');
|
||||
|
||||
openButton.addEventListener('click', () => dialog.show());
|
||||
closeButton.addEventListener('click', () => dialog.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDialog from '@shoelace-style/shoelace/dist/react/dialog';
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaInput autofocus placeholder="I will have focus when the dialog is opened" />
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDialog>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Dialog</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
:::tip
|
||||
You can further customize initial focus behavior by canceling the `wa-initial-focus` event and setting focus yourself inside the event handler.
|
||||
:::
|
||||
@@ -1,158 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Divider
|
||||
description: Dividers are used to visually separate or group elements.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-divider></wa-divider>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
|
||||
const App = () => <WaDivider />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Width
|
||||
|
||||
Use the `--width` custom property to change the width of the divider.
|
||||
|
||||
```html:preview
|
||||
<wa-divider style="--width: 4px;"></wa-divider>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
|
||||
const App = () => <WaDivider style={{ '--width': '4px' }} />;
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Color
|
||||
|
||||
Use the `--color` custom property to change the color of the divider.
|
||||
|
||||
```html:preview
|
||||
<wa-divider style="--color: tomato;"></wa-divider>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
|
||||
const App = () => <WaDivider style={{ '--color': 'tomato' }} />;
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Spacing
|
||||
|
||||
Use the `--spacing` custom property to change the amount of space between the divider and it's neighboring elements.
|
||||
|
||||
```html:preview
|
||||
<div style="text-align: center;">
|
||||
Above
|
||||
<wa-divider style="--spacing: 2rem;"></wa-divider>
|
||||
Below
|
||||
</div>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
Above
|
||||
<WaDivider style={{ '--spacing': '2rem' }} />
|
||||
Below
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Vertical
|
||||
|
||||
Add the `vertical` attribute to draw the divider in a vertical orientation. The divider will span the full height of its container. Vertical dividers work especially well inside of a flex container.
|
||||
|
||||
```html:preview
|
||||
<div style="display: flex; align-items: center; height: 2rem;">
|
||||
First
|
||||
<wa-divider vertical></wa-divider>
|
||||
Middle
|
||||
<wa-divider vertical></wa-divider>
|
||||
Last
|
||||
</div>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
|
||||
const App = () => (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
height: '2rem'
|
||||
}}
|
||||
>
|
||||
First
|
||||
<WaDivider vertical />
|
||||
Middle
|
||||
<WaDivider vertical />
|
||||
Last
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Menu Dividers
|
||||
|
||||
Use dividers in [menus](/components/menu) to visually group menu items.
|
||||
|
||||
```html:preview
|
||||
<wa-menu style="max-width: 200px;">
|
||||
<wa-menu-item value="1">Option 1</wa-menu-item>
|
||||
<wa-menu-item value="2">Option 2</wa-menu-item>
|
||||
<wa-menu-item value="3">Option 3</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item value="4">Option 4</wa-menu-item>
|
||||
<wa-menu-item value="5">Option 5</wa-menu-item>
|
||||
<wa-menu-item value="6">Option 6</wa-menu-item>
|
||||
</wa-menu>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaMenu style={{ maxWidth: '200px' }}>
|
||||
<WaMenuItem value="1">Option 1</WaMenuItem>
|
||||
<WaMenuItem value="2">Option 2</WaMenuItem>
|
||||
<WaMenuItem value="3">Option 3</WaMenuItem>
|
||||
<wa-divider />
|
||||
<WaMenuItem value="4">Option 4</WaMenuItem>
|
||||
<WaMenuItem value="5">Option 5</WaMenuItem>
|
||||
<WaMenuItem value="6">Option 6</WaMenuItem>
|
||||
</WaMenu>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
@@ -1,534 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Drawer
|
||||
description: Drawers slide in from a container to expose additional options and information.
|
||||
layout: component
|
||||
---
|
||||
|
||||
<!-- cspell:dictionaries lorem-ipsum -->
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" class="drawer-overview">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
<wa-button>Open Drawer</wa-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-overview');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDrawer>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Slide in From Start
|
||||
|
||||
By default, drawers slide in from the end. To make the drawer slide in from the start, set the `placement` attribute to `start`.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" placement="start" class="drawer-placement-start">
|
||||
This drawer slides in from the start.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
<wa-button>Open Drawer</wa-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-placement-start');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" placement="start" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
This drawer slides in from the start.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDrawer>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Slide in From Top
|
||||
|
||||
To make the drawer slide in from the top, set the `placement` attribute to `top`.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" placement="top" class="drawer-placement-top">
|
||||
This drawer slides in from the top.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
<wa-button>Open Drawer</wa-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-placement-top');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" placement="top" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
This drawer slides in from the top.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDrawer>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Slide in From Bottom
|
||||
|
||||
To make the drawer slide in from the bottom, set the `placement` attribute to `bottom`.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" placement="bottom" class="drawer-placement-bottom">
|
||||
This drawer slides in from the bottom.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
<wa-button>Open Drawer</wa-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-placement-bottom');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" placement="bottom" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
This drawer slides in from the bottom.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDrawer>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Contained to an Element
|
||||
|
||||
By default, drawers slide out of their [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport. To make a drawer slide out of a parent element, add the `contained` attribute to the drawer and apply `position: relative` to its parent.
|
||||
|
||||
Unlike normal drawers, contained drawers are not modal. This means they do not show an overlay, they do not trap focus, and they are not dismissible with [[Escape]]. This is intentional to allow users to interact with elements outside of the drawer.
|
||||
|
||||
```html:preview
|
||||
<div
|
||||
style="position: relative; border: solid 2px var(--wa-color-surface-border); height: 300px; padding: 1rem; margin-bottom: 1rem;"
|
||||
>
|
||||
The drawer will be contained to this box. This content won't shift or be affected in any way when the drawer opens.
|
||||
|
||||
<wa-drawer label="Drawer" contained class="drawer-contained" style="--size: 50%;">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
</div>
|
||||
|
||||
<wa-button>Toggle Drawer</wa-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-contained');
|
||||
const openButton = drawer.parentElement.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
|
||||
|
||||
openButton.addEventListener('click', () => (drawer.open = !drawer.open));
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
border: 'solid 2px var(--wa-color-surface-border)',
|
||||
height: '300px',
|
||||
padding: '1rem',
|
||||
marginBottom: '1rem'
|
||||
}}
|
||||
>
|
||||
The drawer will be contained to this box. This content won't shift or be affected in any way when the drawer
|
||||
opens.
|
||||
<WaDrawer
|
||||
label="Drawer"
|
||||
contained
|
||||
no-modal
|
||||
open={open}
|
||||
onWaAfterHide={() => setOpen(false)}
|
||||
style={{ '--size': '50%' }}
|
||||
>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDrawer>
|
||||
</div>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Custom Size
|
||||
|
||||
Use the `--size` custom property to set the drawer's size. This will be applied to the drawer's width or height depending on its `placement`.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" class="drawer-custom-size" style="--size: 50vw;">
|
||||
This drawer is always 50% of the viewport.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
<wa-button>Open Drawer</wa-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-custom-size');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)} style={{ '--size': '50vw' }}>
|
||||
This drawer is always 50% of the viewport.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDrawer>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Scrolling
|
||||
|
||||
By design, a drawer's height will never exceed 100% of its container. As such, drawers will not scroll with the page to ensure the header and footer are always accessible to the user.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" class="drawer-scrolling">
|
||||
<div style="height: 150vh; border: dashed 2px var(--wa-color-surface-border); padding: 0 1rem;">
|
||||
<p>Scroll down and give it a try! 👇</p>
|
||||
</div>
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
<wa-button>Open Drawer</wa-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-scrolling');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<div
|
||||
style={{
|
||||
height: '150vh',
|
||||
border: 'dashed 2px var(--wa-color-surface-border)',
|
||||
padding: '0 1rem'
|
||||
}}
|
||||
>
|
||||
<p>Scroll down and give it a try! 👇</p>
|
||||
</div>
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDrawer>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Header Actions
|
||||
|
||||
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [icon buttons](/components/icon-button) if needed.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" class="drawer-header-actions">
|
||||
<wa-icon-button class="new-window" slot="header-actions" name="arrow-up-right-from-square" variant="solid"></wa-icon-button>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
<wa-button>Open Drawer</wa-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-header-actions');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
|
||||
const newWindowButton = drawer.querySelector('.new-window');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
newWindowButton.addEventListener('click', () => window.open(location.href));
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
|
||||
import WaIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaIconButton slot="header-actions" name="arrow-up-right-from-square" onClick={() => window.open(location.href)} />
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDrawer>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Preventing the Drawer from Closing
|
||||
|
||||
By default, drawers will close when the user clicks the close button, clicks the overlay, or presses the [[Escape]] key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.
|
||||
|
||||
To keep the drawer open in such cases, you can cancel the `wa-request-close` event. When canceled, the drawer will remain open and pulse briefly to draw the user's attention to it.
|
||||
|
||||
You can use `event.detail.source` to determine what triggered the request to close. This example prevents the drawer from closing when the overlay is clicked, but allows the close button or [[Escape]] to dismiss it.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" class="drawer-deny-close">
|
||||
This drawer will not close when you click on the overlay.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
<wa-button>Open Drawer</wa-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-deny-close');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
|
||||
// Prevent the drawer from closing when the user clicks on the overlay
|
||||
drawer.addEventListener('wa-request-close', event => {
|
||||
if (event.detail.source === 'overlay') {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// Prevent the drawer from closing when the user clicks on the overlay
|
||||
function handleRequestClose(event) {
|
||||
if (event.detail.source === 'overlay') {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" open={open} onWaRequestClose={handleRequestClose} onWaAfterHide={() => setOpen(false)}>
|
||||
This drawer will not close when you click on the overlay.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Save & Close
|
||||
</WaButton>
|
||||
</WaDrawer>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Customizing Initial Focus
|
||||
|
||||
By default, the drawer's panel will gain focus when opened. This allows a subsequent tab press to focus on the first tabbable element in the drawer. If you want a different element to have focus, add the `autofocus` attribute to it as shown below.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" class="drawer-focus">
|
||||
<wa-input autofocus placeholder="I will have focus when the drawer is opened"></wa-input>
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
<wa-button>Open Drawer</wa-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-focus');
|
||||
const input = drawer.querySelector('wa-input');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaInput autofocus placeholder="I will have focus when the drawer is opened" />
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDrawer>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
:::tip
|
||||
You can further customize initial focus behavior by canceling the `wa-initial-focus` event and setting focus yourself inside the event handler.
|
||||
:::
|
||||
@@ -1,482 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Dropdown
|
||||
description: 'Dropdowns expose additional content that "drops down" in a panel.'
|
||||
layout: component
|
||||
---
|
||||
|
||||
Dropdowns consist of a trigger and a panel. By default, activating the trigger will expose the panel and interacting outside of the panel will close it.
|
||||
|
||||
Dropdowns are designed to work well with [menus](/components/menu) to provide a list of options the user can select from. However, dropdowns can also be used in lower-level applications (e.g. [color picker](/components/color-picker)). The API gives you complete control over showing, hiding, and positioning the panel.
|
||||
|
||||
```html:preview
|
||||
<wa-dropdown>
|
||||
<wa-button slot="trigger" caret>Dropdown</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Dropdown Item 1</wa-menu-item>
|
||||
<wa-menu-item>Dropdown Item 2</wa-menu-item>
|
||||
<wa-menu-item>Dropdown Item 3</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item type="checkbox" checked>Checkbox</wa-menu-item>
|
||||
<wa-menu-item disabled>Disabled</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item>
|
||||
Prefix
|
||||
<wa-icon slot="prefix" name="gift" variant="solid"></wa-icon>
|
||||
</wa-menu-item>
|
||||
<wa-menu-item>
|
||||
Suffix Icon
|
||||
<wa-icon slot="suffix" name="heart" variant="solid"></wa-icon>
|
||||
</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaDropdown>
|
||||
<WaButton slot="trigger" caret>
|
||||
Dropdown
|
||||
</WaButton>
|
||||
<WaMenu>
|
||||
<WaMenuItem>Dropdown Item 1</WaMenuItem>
|
||||
<WaMenuItem>Dropdown Item 2</WaMenuItem>
|
||||
<WaMenuItem>Dropdown Item 3</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem type="checkbox" checked>
|
||||
Checkbox
|
||||
</WaMenuItem>
|
||||
<WaMenuItem disabled>Disabled</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem>
|
||||
Prefix
|
||||
<WaIcon slot="prefix" name="gift" />
|
||||
</WaMenuItem>
|
||||
<WaMenuItem>
|
||||
Suffix Icon
|
||||
<WaIcon slot="suffix" name="heart" />
|
||||
</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Getting the Selected Item
|
||||
|
||||
When dropdowns are used with [menus](/components/menu), you can listen for the [`wa-select`](/components/menu#events) event to determine which menu item was selected. The menu item element will be exposed in `event.detail.item`. You can set `value` props to make it easier to identify commands.
|
||||
|
||||
```html:preview
|
||||
<div class="dropdown-selection">
|
||||
<wa-dropdown>
|
||||
<wa-button slot="trigger" caret>Edit</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item value="cut">Cut</wa-menu-item>
|
||||
<wa-menu-item value="copy">Copy</wa-menu-item>
|
||||
<wa-menu-item value="paste">Paste</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.dropdown-selection');
|
||||
const dropdown = container.querySelector('wa-dropdown');
|
||||
|
||||
dropdown.addEventListener('wa-select', event => {
|
||||
const selectedItem = event.detail.item;
|
||||
console.log(selectedItem.value);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => {
|
||||
function handleSelect(event) {
|
||||
const selectedItem = event.detail.item;
|
||||
console.log(selectedItem.value);
|
||||
}
|
||||
|
||||
return (
|
||||
<WaDropdown>
|
||||
<WaButton slot="trigger" caret>
|
||||
Edit
|
||||
</WaButton>
|
||||
<WaMenu onWaSelect={handleSelect}>
|
||||
<WaMenuItem value="cut">Cut</WaMenuItem>
|
||||
<WaMenuItem value="copy">Copy</WaMenuItem>
|
||||
<WaMenuItem value="paste">Paste</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
Alternatively, you can listen for the `click` event on individual menu items. Note that, using this approach, disabled menu items will still emit a `click` event.
|
||||
|
||||
```html:preview
|
||||
<div class="dropdown-selection-alt">
|
||||
<wa-dropdown>
|
||||
<wa-button slot="trigger" caret>Edit</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item value="cut">Cut</wa-menu-item>
|
||||
<wa-menu-item value="copy">Copy</wa-menu-item>
|
||||
<wa-menu-item value="paste">Paste</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.dropdown-selection-alt');
|
||||
const cut = container.querySelector('wa-menu-item[value="cut"]');
|
||||
const copy = container.querySelector('wa-menu-item[value="copy"]');
|
||||
const paste = container.querySelector('wa-menu-item[value="paste"]');
|
||||
|
||||
cut.addEventListener('click', () => console.log('cut'));
|
||||
copy.addEventListener('click', () => console.log('copy'));
|
||||
paste.addEventListener('click', () => console.log('paste'));
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => {
|
||||
function handleCut() {
|
||||
console.log('cut');
|
||||
}
|
||||
|
||||
function handleCopy() {
|
||||
console.log('copy');
|
||||
}
|
||||
|
||||
function handlePaste() {
|
||||
console.log('paste');
|
||||
}
|
||||
|
||||
return (
|
||||
<WaDropdown>
|
||||
<WaButton slot="trigger" caret>
|
||||
Edit
|
||||
</WaButton>
|
||||
<WaMenu>
|
||||
<WaMenuItem onClick={handleCut}>Cut</WaMenuItem>
|
||||
<WaMenuItem onClick={handleCopy}>Copy</WaMenuItem>
|
||||
<WaMenuItem onClick={handlePaste}>Paste</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Placement
|
||||
|
||||
The preferred placement of the dropdown can be set with the `placement` attribute. Note that the actual position may vary to ensure the panel remains in the viewport.
|
||||
|
||||
```html:preview
|
||||
<wa-dropdown placement="top-start">
|
||||
<wa-button slot="trigger" caret>Edit</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Cut</wa-menu-item>
|
||||
<wa-menu-item>Copy</wa-menu-item>
|
||||
<wa-menu-item>Paste</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item>Find</wa-menu-item>
|
||||
<wa-menu-item>Replace</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaDropdown placement="top-start">
|
||||
<WaButton slot="trigger" caret>
|
||||
Edit
|
||||
</WaButton>
|
||||
<WaMenu>
|
||||
<WaMenuItem>Cut</WaMenuItem>
|
||||
<WaMenuItem>Copy</WaMenuItem>
|
||||
<WaMenuItem>Paste</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem>Find</WaMenuItem>
|
||||
<WaMenuItem>Replace</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
### Distance
|
||||
|
||||
The distance from the panel to the trigger can be customized using the `distance` attribute. This value is specified in pixels.
|
||||
|
||||
```html:preview
|
||||
<wa-dropdown distance="30">
|
||||
<wa-button slot="trigger" caret>Edit</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Cut</wa-menu-item>
|
||||
<wa-menu-item>Copy</wa-menu-item>
|
||||
<wa-menu-item>Paste</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item>Find</wa-menu-item>
|
||||
<wa-menu-item>Replace</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaDropdown distance={30}>
|
||||
<WaButton slot="trigger" caret>
|
||||
Edit
|
||||
</WaButton>
|
||||
<WaMenu>
|
||||
<WaMenuItem>Cut</WaMenuItem>
|
||||
<WaMenuItem>Copy</WaMenuItem>
|
||||
<WaMenuItem>Paste</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem>Find</WaMenuItem>
|
||||
<WaMenuItem>Replace</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
### Skidding
|
||||
|
||||
The offset of the panel along the trigger can be customized using the `skidding` attribute. This value is specified in pixels.
|
||||
|
||||
```html:preview
|
||||
<wa-dropdown skidding="30">
|
||||
<wa-button slot="trigger" caret>Edit</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Cut</wa-menu-item>
|
||||
<wa-menu-item>Copy</wa-menu-item>
|
||||
<wa-menu-item>Paste</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item>Find</wa-menu-item>
|
||||
<wa-menu-item>Replace</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaDropdown skidding={30}>
|
||||
<WaButton slot="trigger" caret>
|
||||
Edit
|
||||
</WaButton>
|
||||
<WaMenu>
|
||||
<WaMenuItem>Cut</WaMenuItem>
|
||||
<WaMenuItem>Copy</WaMenuItem>
|
||||
<WaMenuItem>Paste</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem>Find</WaMenuItem>
|
||||
<WaMenuItem>Replace</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
### Submenus
|
||||
|
||||
To create a submenu, nest an `<wa-menu slot="submenu">` element in a [menu item](/components/menu-item).
|
||||
|
||||
```html:preview
|
||||
<wa-dropdown>
|
||||
<wa-button slot="trigger" caret>Edit</wa-button>
|
||||
|
||||
<wa-menu style="max-width: 200px;">
|
||||
<wa-menu-item value="undo">Undo</wa-menu-item>
|
||||
<wa-menu-item value="redo">Redo</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item value="cut">Cut</wa-menu-item>
|
||||
<wa-menu-item value="copy">Copy</wa-menu-item>
|
||||
<wa-menu-item value="paste">Paste</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item>
|
||||
Find
|
||||
<wa-menu slot="submenu">
|
||||
<wa-menu-item value="find">Find…</wa-menu-item>
|
||||
<wa-menu-item value="find-previous">Find Next</wa-menu-item>
|
||||
<wa-menu-item value="find-next">Find Previous</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-menu-item>
|
||||
<wa-menu-item>
|
||||
Transformations
|
||||
<wa-menu slot="submenu">
|
||||
<wa-menu-item value="uppercase">Make uppercase</wa-menu-item>
|
||||
<wa-menu-item value="lowercase">Make lowercase</wa-menu-item>
|
||||
<wa-menu-item value="capitalize">Capitalize</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const css = `
|
||||
.dropdown-hoist {
|
||||
border: solid 2px var(--wa-color-surface-border);
|
||||
padding: var(--wa-space-m);
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaDropdown>
|
||||
<WaButton slot="trigger" caret>Edit</WaButton>
|
||||
|
||||
<WaMenu style="max-width: 200px;">
|
||||
<WaMenuItem value="undo">Undo</WaMenuItem>
|
||||
<WaMenuItem value="redo">Redo</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem value="cut">Cut</WaMenuItem>
|
||||
<WaMenuItem value="copy">Copy</WaMenuItem>
|
||||
<WaMenuItem value="paste">Paste</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem>
|
||||
Find
|
||||
<WaMenu slot="submenu">
|
||||
<WaMenuItem value="find">Find…</WaMenuItem>
|
||||
<WaMenuItem value="find-previous">Find Next</WaMenuItem>
|
||||
<WaMenuItem value="find-next">Find Previous</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaMenuItem>
|
||||
<WaMenuItem>
|
||||
Transformations
|
||||
<WaMenu slot="submenu">
|
||||
<WaMenuItem value="uppercase">Make uppercase</WaMenuItem>
|
||||
<WaMenuItem value="lowercase">Make lowercase</WaMenuItem>
|
||||
<WaMenuItem value="capitalize">Capitalize</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
:::warning
|
||||
As a UX best practice, avoid using more than one level of submenu when possible.
|
||||
:::
|
||||
|
||||
### Hoisting
|
||||
|
||||
Dropdown panels will be clipped if they're inside a container that has `overflow: auto|hidden`. The `hoist` attribute forces the panel to use a fixed positioning strategy, allowing it to break out of the container. In this case, the panel will be positioned relative to its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
|
||||
|
||||
```html:preview
|
||||
<div class="dropdown-hoist">
|
||||
<wa-dropdown>
|
||||
<wa-button slot="trigger" caret>No Hoist</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Item 1</wa-menu-item>
|
||||
<wa-menu-item>Item 2</wa-menu-item>
|
||||
<wa-menu-item>Item 3</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
|
||||
<wa-dropdown hoist>
|
||||
<wa-button slot="trigger" caret>Hoist</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Item 1</wa-menu-item>
|
||||
<wa-menu-item>Item 2</wa-menu-item>
|
||||
<wa-menu-item>Item 3</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.dropdown-hoist {
|
||||
position: relative;
|
||||
border: solid 2px var(--wa-color-surface-border);
|
||||
padding: var(--wa-space-m);
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const css = `
|
||||
.dropdown-hoist {
|
||||
border: solid 2px var(--wa-color-surface-border);
|
||||
padding: var(--wa-space-m);
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="dropdown-hoist">
|
||||
<WaDropdown>
|
||||
<WaButton slot="trigger" caret>
|
||||
No Hoist
|
||||
</WaButton>
|
||||
<WaMenu>
|
||||
<WaMenuItem>Item 1</WaMenuItem>
|
||||
<WaMenuItem>Item 2</WaMenuItem>
|
||||
<WaMenuItem>Item 3</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
|
||||
<WaDropdown hoist>
|
||||
<WaButton slot="trigger" caret>
|
||||
Hoist
|
||||
</WaButton>
|
||||
<WaMenu>
|
||||
<WaMenuItem>Item 1</WaMenuItem>
|
||||
<WaMenuItem>Item 2</WaMenuItem>
|
||||
<WaMenuItem>Item 3</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
@@ -1,134 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Format Bytes
|
||||
description: Formats a number as a human readable bytes value.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<div class="format-bytes-overview">
|
||||
The file is <wa-format-bytes value="1000"></wa-format-bytes> in size. <br /><br />
|
||||
<wa-input type="number" value="1000" label="Number to Format" style="max-width: 180px;"></wa-input>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.format-bytes-overview');
|
||||
const formatter = container.querySelector('wa-format-bytes');
|
||||
const input = container.querySelector('wa-input');
|
||||
|
||||
input.addEventListener('wa-input', () => (formatter.value = input.value || 0));
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaFormatBytes from '@shoelace-style/shoelace/dist/react/format-bytes';
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => {
|
||||
const [value, setValue] = useState(1000);
|
||||
|
||||
return (
|
||||
<>
|
||||
The file is <WaFormatBytes value={value} /> in size.
|
||||
<br />
|
||||
<br />
|
||||
<WaInput
|
||||
type="number"
|
||||
value={value}
|
||||
label="Number to Format"
|
||||
style={{ maxWidth: '180px' }}
|
||||
onWaInput={event => setValue(event.target.value)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
## Examples
|
||||
|
||||
### Formatting Bytes
|
||||
|
||||
Set the `value` attribute to a number to get the value in bytes.
|
||||
|
||||
```html:preview
|
||||
<wa-format-bytes value="12"></wa-format-bytes><br />
|
||||
<wa-format-bytes value="1200"></wa-format-bytes><br />
|
||||
<wa-format-bytes value="1200000"></wa-format-bytes><br />
|
||||
<wa-format-bytes value="1200000000"></wa-format-bytes>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaFormatBytes from '@shoelace-style/shoelace/dist/react/format-bytes';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaFormatBytes value="12" />
|
||||
<br />
|
||||
<WaFormatBytes value="1200" />
|
||||
<br />
|
||||
<WaFormatBytes value="1200000" />
|
||||
<br />
|
||||
<WaFormatBytes value="1200000000" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Formatting Bits
|
||||
|
||||
To get the value in bits, set the `unit` attribute to `bit`.
|
||||
|
||||
```html:preview
|
||||
<wa-format-bytes value="12" unit="bit"></wa-format-bytes><br />
|
||||
<wa-format-bytes value="1200" unit="bit"></wa-format-bytes><br />
|
||||
<wa-format-bytes value="1200000" unit="bit"></wa-format-bytes><br />
|
||||
<wa-format-bytes value="1200000000" unit="bit"></wa-format-bytes>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaFormatBytes from '@shoelace-style/shoelace/dist/react/format-bytes';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaFormatBytes value="12" unit="bit" />
|
||||
<br />
|
||||
<WaFormatBytes value="1200" unit="bit" />
|
||||
<br />
|
||||
<WaFormatBytes value="1200000" unit="bit" />
|
||||
<br />
|
||||
<WaFormatBytes value="1200000000" unit="bit" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Localization
|
||||
|
||||
Use the `lang` attribute to set the number formatting locale.
|
||||
|
||||
```html:preview
|
||||
<wa-format-bytes value="12" lang="de"></wa-format-bytes><br />
|
||||
<wa-format-bytes value="1200" lang="de"></wa-format-bytes><br />
|
||||
<wa-format-bytes value="1200000" lang="de"></wa-format-bytes><br />
|
||||
<wa-format-bytes value="1200000000" lang="de"></wa-format-bytes>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaFormatBytes from '@shoelace-style/shoelace/dist/react/format-bytes';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaFormatBytes value="12" lang="de" />
|
||||
<br />
|
||||
<WaFormatBytes value="1200" lang="de" />
|
||||
<br />
|
||||
<WaFormatBytes value="1200000" lang="de" />
|
||||
<br />
|
||||
<WaFormatBytes value="1200000000" lang="de" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
@@ -1,153 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Icon Button
|
||||
description: Icons buttons are simple, icon-only buttons that can be used for actions and in toolbars.
|
||||
layout: component
|
||||
---
|
||||
|
||||
For a full list of icons that come bundled with Web Awesome, refer to the [icon component](/components/icon).
|
||||
|
||||
```html:preview
|
||||
<wa-icon-button name="gear" label="Settings"></wa-icon-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
|
||||
|
||||
const App = () => <WaIconButton name="gear" label="Settings" />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Sizes
|
||||
|
||||
Icon buttons inherit their parent element's `font-size`.
|
||||
|
||||
```html:preview
|
||||
<wa-icon-button name="pen-to-square" variant="solid" label="Edit" style="font-size: 1.5rem;"></wa-icon-button>
|
||||
<wa-icon-button name="pen-to-square" variant="solid" label="Edit" style="font-size: 2rem;"></wa-icon-button>
|
||||
<wa-icon-button name="pen-to-square" variant="solid" label="Edit" style="font-size: 2.5rem;"></wa-icon-button>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaIconButton name="pen-to-square" variant="solid" label="Edit" style={{ fontSize: '1.5rem' }} />
|
||||
<WaIconButton name="pen-to-square" variant="solid" label="Edit" style={{ fontSize: '2rem' }} />
|
||||
<WaIconButton name="pen-to-square" variant="solid" label="Edit" style={{ fontSize: '2.5rem' }} />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Colors
|
||||
|
||||
Icon buttons are designed to have a uniform appearance, so their color is not inherited. However, you can still customize them by styling the `base` part.
|
||||
|
||||
```html:preview
|
||||
<div class="icon-button-color">
|
||||
<wa-icon-button name="bold" variant="solid" label="Bold"></wa-icon-button>
|
||||
<wa-icon-button name="italic" variant="solid" label="Italic"></wa-icon-button>
|
||||
<wa-icon-button name="underline" variant="solid" label="Underline"></wa-icon-button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.icon-button-color wa-icon-button::part(base) {
|
||||
color: #b00091;
|
||||
}
|
||||
|
||||
.icon-button-color wa-icon-button::part(base):hover,
|
||||
.icon-button-color wa-icon-button::part(base):focus {
|
||||
color: #c913aa;
|
||||
}
|
||||
|
||||
.icon-button-color wa-icon-button::part(base):active {
|
||||
color: #960077;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
|
||||
|
||||
const css = `
|
||||
.icon-button-color wa-icon-button::part(base) {
|
||||
color: #b00091;
|
||||
}
|
||||
|
||||
.icon-button-color wa-icon-button::part(base):hover,
|
||||
.icon-button-color wa-icon-button::part(base):focus {
|
||||
color: #c913aa;
|
||||
}
|
||||
|
||||
.icon-button-color wa-icon-button::part(base):active {
|
||||
color: #960077;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="icon-button-color">
|
||||
<WaIconButton name="type-bold" variant="solid" label="Bold" />
|
||||
<WaIconButton name="type-italic" variant="solid" label="Italic" />
|
||||
<WaIconButton name="type-underline" variant="solid" label="Underline" />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Link Buttons
|
||||
|
||||
Use the `href` attribute to convert the button to a link.
|
||||
|
||||
```html:preview
|
||||
<wa-icon-button name="gear" variant="solid" label="Settings" href="https://example.com" target="_blank"></wa-icon-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
|
||||
|
||||
const App = () => <WaIconButton name="gear" variant="solid" label="Settings" href="https://example.com" target="_blank" />;
|
||||
```
|
||||
|
||||
### Icon Button with Tooltip
|
||||
|
||||
Wrap a tooltip around an icon button to provide contextual information to the user.
|
||||
|
||||
```html:preview
|
||||
<wa-tooltip content="Settings">
|
||||
<wa-icon-button name="gear" variant="solid" label="Settings"></wa-icon-button>
|
||||
</wa-tooltip>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
|
||||
import WaTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
|
||||
|
||||
const App = () => (
|
||||
<WaTooltip content="Settings">
|
||||
<WaIconButton name="gear" variant="solid" label="Settings" />
|
||||
</WaTooltip>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable the icon button.
|
||||
|
||||
```html:preview
|
||||
<wa-icon-button name="gear" variant="solid" label="Settings" disabled></wa-icon-button>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
|
||||
|
||||
const App = () => <WaIconButton name="gear" variant="solid" label="Settings" disabled />;
|
||||
```
|
||||
@@ -1,670 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Icon
|
||||
description: Icons are symbols that can be used to represent various options within an application.
|
||||
layout: component
|
||||
---
|
||||
|
||||
Web Awesome comes bundled with over 1,500 icons courtesy of the [Bootstrap Icons](https://icons.getbootstrap.com/) project. These icons are part of the `default` icon library. If you prefer, you can register [custom icon libraries](#icon-libraries) as well.
|
||||
|
||||
:::tip
|
||||
Depending on how you're loading Web Awesome, you may need to copy icon assets and/or [set the base path](/getting-started/installation/#setting-the-base-path) so Web Awesome knows where to load them from. Otherwise, icons may not appear and you'll see 404 Not Found errors in the dev console.
|
||||
:::
|
||||
|
||||
## Default Icons
|
||||
|
||||
All available icons in the `default` icon library are shown below. Click or tap on any icon to copy its name, then you can use it in your HTML like this.
|
||||
|
||||
```html
|
||||
<wa-icon name="icon-name-here"></wa-icon>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
TODO - show how to use `family` and `variant` attributes.
|
||||
TODO - show how to use FA pro via `data-webawesome-kit="..."`
|
||||
|
||||
### Colors
|
||||
|
||||
Icons inherit their color from the current text color. Thus, you can set the `color` property on the `<wa-icon>` element or an ancestor to change the color.
|
||||
|
||||
```html:preview
|
||||
<div style="color: #4a90e2;">
|
||||
<wa-icon name="exclamation-triangle"></wa-icon>
|
||||
<wa-icon name="archive"></wa-icon>
|
||||
<wa-icon name="battery-three-quarters"></wa-icon>
|
||||
<wa-icon name="bell"></wa-icon>
|
||||
</div>
|
||||
<div style="color: #9013fe;">
|
||||
<wa-icon name="clock"></wa-icon>
|
||||
<wa-icon name="cloud"></wa-icon>
|
||||
<wa-icon name="download"></wa-icon>
|
||||
<wa-icon name="file"></wa-icon>
|
||||
</div>
|
||||
<div style="color: #417505;">
|
||||
<wa-icon name="flag"></wa-icon>
|
||||
<wa-icon name="heart"></wa-icon>
|
||||
<wa-icon name="image"></wa-icon>
|
||||
<wa-icon name="bolt-lightning"></wa-icon>
|
||||
</div>
|
||||
<div style="color: #f5a623;">
|
||||
<wa-icon name="microphone"></wa-icon>
|
||||
<wa-icon name="search"></wa-icon>
|
||||
<wa-icon name="star"></wa-icon>
|
||||
<wa-icon name="trash"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div style={{ color: '#4a90e2' }}>
|
||||
<WaIcon name="exclamation-triangle"></WaIcon>
|
||||
<WaIcon name="archive"></WaIcon>
|
||||
<WaIcon name="battery-three-quarters"></WaIcon>
|
||||
<WaIcon name="bell"></WaIcon>
|
||||
</div>
|
||||
<div style={{ color: '#9013fe' }}>
|
||||
<WaIcon name="clock"></WaIcon>
|
||||
<WaIcon name="cloud"></WaIcon>
|
||||
<WaIcon name="download"></WaIcon>
|
||||
<WaIcon name="file"></WaIcon>
|
||||
</div>
|
||||
<div style={{ color: '#417505' }}>
|
||||
<WaIcon name="flag"></WaIcon>
|
||||
<WaIcon name="heart"></WaIcon>
|
||||
<WaIcon name="image"></WaIcon>
|
||||
<WaIcon name="bolt-lightning"></WaIcon>
|
||||
</div>
|
||||
<div style={{ color: '#f5a623' }}>
|
||||
<WaIcon name="microphone"></WaIcon>
|
||||
<WaIcon name="search"></WaIcon>
|
||||
<WaIcon name="star"></WaIcon>
|
||||
<WaIcon name="trash"></WaIcon>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Sizing
|
||||
|
||||
Icons are sized relative to the current font size. To change their size, set the `font-size` property on the icon itself or on a parent element as shown below.
|
||||
|
||||
```html:preview
|
||||
<div style="font-size: 32px;">
|
||||
<wa-icon name="exclamation-triangle"></wa-icon>
|
||||
<wa-icon name="archive"></wa-icon>
|
||||
<wa-icon name="battery-three-quarters"></wa-icon>
|
||||
<wa-icon name="bell"></wa-icon>
|
||||
<wa-icon name="clock"></wa-icon>
|
||||
<wa-icon name="cloud"></wa-icon>
|
||||
<wa-icon name="download"></wa-icon>
|
||||
<wa-icon name="file"></wa-icon>
|
||||
<wa-icon name="flag"></wa-icon>
|
||||
<wa-icon name="heart"></wa-icon>
|
||||
<wa-icon name="image"></wa-icon>
|
||||
<wa-icon name="bolt-lightning"></wa-icon>
|
||||
<wa-icon name="microphone"></wa-icon>
|
||||
<wa-icon name="search"></wa-icon>
|
||||
<wa-icon name="star"></wa-icon>
|
||||
<wa-icon name="trash"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => (
|
||||
<div style={{ fontSize: '32px' }}>
|
||||
<WaIcon name="exclamation-triangle" />
|
||||
<WaIcon name="archive" />
|
||||
<WaIcon name="battery-three-quarters" />
|
||||
<WaIcon name="bell" />
|
||||
<WaIcon name="clock" />
|
||||
<WaIcon name="cloud" />
|
||||
<WaIcon name="download" />
|
||||
<WaIcon name="file" />
|
||||
<WaIcon name="flag" />
|
||||
<WaIcon name="heart" />
|
||||
<WaIcon name="image" />
|
||||
<WaIcon name="bolt-lightning" />
|
||||
<WaIcon name="microphone" />
|
||||
<WaIcon name="search" />
|
||||
<WaIcon name="star" />
|
||||
<WaIcon name="trash" />
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Labels
|
||||
|
||||
For non-decorative icons, use the `label` attribute to announce it to assistive devices.
|
||||
|
||||
```html:preview
|
||||
<wa-icon name="star" label="Add to favorites"></wa-icon>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => <WaIcon name="star" label="Add to favorites" />;
|
||||
```
|
||||
|
||||
### Custom Icons
|
||||
|
||||
Custom icons can be loaded individually with the `src` attribute. Only SVGs on a local or CORS-enabled endpoint are supported. If you're using more than one custom icon, it might make sense to register a [custom icon library](#icon-libraries).
|
||||
|
||||
```html:preview
|
||||
<wa-icon src="https://shoelace.style/assets/images/shoe.svg" style="font-size: 8rem;"></wa-icon>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => <WaIcon src="https://shoelace.style/assets/images/shoe.svg" style={{ fontSize: '8rem' }}></WaIcon>;
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
## Icon Libraries
|
||||
|
||||
You can register additional icons to use with the `<wa-icon>` component through icon libraries. Icon files can exist locally or on a CORS-enabled endpoint (e.g. a CDN). There is no limit to how many icon libraries you can register and there is no cost associated with registering them, as individual icons are only requested when they're used.
|
||||
|
||||
Web Awesome ships with two built-in icon libraries, `default` and `system`. The [default icon library](#customizing-the-default-library) contains all of the icons in the Bootstrap Icons project. The [system icon library](#customizing-the-system-library) contains only a small subset of icons that are used internally by Web Awesome components.
|
||||
|
||||
To register an additional icon library, use the `registerIconLibrary()` function that's exported from `utilities/icon-library.js`. At a minimum, you must provide a name and a resolver function. The resolver function translates an icon name to a URL where the corresponding SVG file exists. Refer to the examples below to better understand how it works.
|
||||
|
||||
If necessary, a mutator function can be used to mutate the SVG element before rendering. This is necessary for some libraries due to the many possible ways SVGs are crafted. For example, icons should ideally inherit the current text color via `currentColor`, so you may need to apply `fill="currentColor` or `stroke="currentColor"` to the SVG element using this function.
|
||||
|
||||
Here's an example that registers an icon library located in the `/assets/icons` directory.
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('my-icons', {
|
||||
resolver: name => `/assets/icons/${name}.svg`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor')
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
To display an icon, set the `library` and `name` attributes of an `<wa-icon>` element.
|
||||
|
||||
```html
|
||||
<!-- This will show the icon located at /assets/icons/smile.svg -->
|
||||
<wa-icon library="my-icons" name="smile"></wa-icon>
|
||||
```
|
||||
|
||||
If an icon is used before registration occurs, it will be empty initially but shown when registered.
|
||||
|
||||
The following examples demonstrate how to register a number of popular, open source icon libraries via CDN. Feel free to adapt the code as you see fit to use your own origin or naming conventions.
|
||||
|
||||
### Boxicons
|
||||
|
||||
This will register the [Boxicons](https://boxicons.com/) library using the jsDelivr CDN. This library has three variations: regular (`bx-*`), solid (`bxs-*`), and logos (`bxl-*`). A mutator function is required to set the SVG's `fill` to `currentColor`.
|
||||
|
||||
Icons in this library are licensed under the [Creative Commons 4.0 License](https://github.com/atisawd/boxicons#license).
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('boxicons', {
|
||||
resolver: name => {
|
||||
let folder = 'regular';
|
||||
if (name.substring(0, 4) === 'bxs-') folder = 'solid';
|
||||
if (name.substring(0, 4) === 'bxl-') folder = 'logos';
|
||||
return `https://cdn.jsdelivr.net/npm/boxicons@2.0.5/svg/${folder}/${name}.svg`;
|
||||
},
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor')
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="boxicons" name="bx-bot"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bx-cookie"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bx-joystick"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bx-save"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bx-server"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bx-wine"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="boxicons" name="bxs-bot"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bxs-cookie"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bxs-joystick"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bxs-save"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bxs-server"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bxs-wine"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="boxicons" name="bxl-apple"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bxl-chrome"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bxl-edge"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bxl-firefox"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bxl-opera"></wa-icon>
|
||||
<wa-icon library="boxicons" name="bxl-microsoft"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Lucide
|
||||
|
||||
This will register the [Lucide](https://lucide.dev/) icon library using the jsDelivr CDN. This project is a community-maintained fork of the popular [Feather](https://feathericons.com/) icon library.
|
||||
|
||||
Icons in this library are licensed under the [MIT License](https://github.com/lucide-icons/lucide/blob/master/LICENSE).
|
||||
|
||||
```html:preview
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="lucide" name="feather"></wa-icon>
|
||||
<wa-icon library="lucide" name="pie-chart"></wa-icon>
|
||||
<wa-icon library="lucide" name="settings"></wa-icon>
|
||||
<wa-icon library="lucide" name="map-pin"></wa-icon>
|
||||
<wa-icon library="lucide" name="printer"></wa-icon>
|
||||
<wa-icon library="lucide" name="shopping-cart"></wa-icon>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('lucide', {
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/lucide-static@0.16.29/icons/${name}.svg`
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### Font Awesome
|
||||
|
||||
This will register the [Font Awesome Free](https://fontawesome.com/) library using the jsDelivr CDN. This library has three variations: regular (`far-*`), solid (`fas-*`), and brands (`fab-*`). A mutator function is required to set the SVG's `fill` to `currentColor`.
|
||||
|
||||
Icons in this library are licensed under the [Font Awesome Free License](https://github.com/FortAwesome/Font-Awesome/blob/master/LICENSE.txt). Some of the icons that appear on the Font Awesome website require a license and are therefore not available in the CDN.
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('fa', {
|
||||
resolver: name => {
|
||||
const filename = name.replace(/^fa[rbs]-/, '');
|
||||
let folder = 'regular';
|
||||
if (name.substring(0, 4) === 'fas-') folder = 'solid';
|
||||
if (name.substring(0, 4) === 'fab-') folder = 'brands';
|
||||
return `https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.1/svgs/${folder}/${filename}.svg`;
|
||||
},
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor')
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="fa" name="far-bell"></wa-icon>
|
||||
<wa-icon library="fa" name="far-comment"></wa-icon>
|
||||
<wa-icon library="fa" name="far-hand-point-right"></wa-icon>
|
||||
<wa-icon library="fa" name="far-hdd"></wa-icon>
|
||||
<wa-icon library="fa" name="far-heart"></wa-icon>
|
||||
<wa-icon library="fa" name="far-star"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="fa" name="fas-archive"></wa-icon>
|
||||
<wa-icon library="fa" name="fas-book"></wa-icon>
|
||||
<wa-icon library="fa" name="fas-chess-knight"></wa-icon>
|
||||
<wa-icon library="fa" name="fas-dice"></wa-icon>
|
||||
<wa-icon library="fa" name="fas-pizza-slice"></wa-icon>
|
||||
<wa-icon library="fa" name="fas-scroll"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="fa" name="fab-apple"></wa-icon>
|
||||
<wa-icon library="fa" name="fab-chrome"></wa-icon>
|
||||
<wa-icon library="fa" name="fab-edge"></wa-icon>
|
||||
<wa-icon library="fa" name="fab-firefox"></wa-icon>
|
||||
<wa-icon library="fa" name="fab-opera"></wa-icon>
|
||||
<wa-icon library="fa" name="fab-microsoft"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Heroicons
|
||||
|
||||
This will register the [Heroicons](https://heroicons.com/) library using the jsDelivr CDN.
|
||||
|
||||
Icons in this library are licensed under the [MIT License](https://github.com/tailwindlabs/heroicons/blob/master/LICENSE).
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('heroicons', {
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/heroicons@2.0.1/24/outline/${name}.svg`
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="heroicons" name="chat-bubble-left"></wa-icon>
|
||||
<wa-icon library="heroicons" name="cloud"></wa-icon>
|
||||
<wa-icon library="heroicons" name="cog"></wa-icon>
|
||||
<wa-icon library="heroicons" name="document-text"></wa-icon>
|
||||
<wa-icon library="heroicons" name="gift"></wa-icon>
|
||||
<wa-icon library="heroicons" name="speaker-wave"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Iconoir
|
||||
|
||||
This will register the [Iconoir](https://iconoir.com/) library using the jsDelivr CDN.
|
||||
|
||||
Icons in this library are licensed under the [MIT License](https://github.com/lucaburgio/iconoir/blob/master/LICENSE).
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('iconoir', {
|
||||
resolver: name => `https://cdn.jsdelivr.net/gh/lucaburgio/iconoir@latest/icons/${name}.svg`
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="iconoir" name="check-circled-outline"></wa-icon>
|
||||
<wa-icon library="iconoir" name="drawer"></wa-icon>
|
||||
<wa-icon library="iconoir" name="keyframes"></wa-icon>
|
||||
<wa-icon library="iconoir" name="headset-help"></wa-icon>
|
||||
<wa-icon library="iconoir" name="color-picker"></wa-icon>
|
||||
<wa-icon library="iconoir" name="wifi"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Ionicons
|
||||
|
||||
This will register the [Ionicons](https://ionicons.com/) library using the jsDelivr CDN. This library has three variations: outline (default), filled (`*-filled`), and sharp (`*-sharp`). A mutator function is required to polyfill a handful of styles we're not including.
|
||||
|
||||
Icons in this library are licensed under the [MIT License](https://github.com/ionic-team/ionicons/blob/master/LICENSE).
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('ionicons', {
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/ionicons@5.1.2/dist/ionicons/svg/${name}.svg`,
|
||||
mutator: svg => {
|
||||
svg.setAttribute('fill', 'currentColor');
|
||||
svg.setAttribute('stroke', 'currentColor');
|
||||
[...svg.querySelectorAll('.ionicon-fill-none')].map(el => el.setAttribute('fill', 'none'));
|
||||
[...svg.querySelectorAll('.ionicon-stroke-width')].map(el => el.setAttribute('stroke-width', '32px'));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="ionicons" name="alarm"></wa-icon>
|
||||
<wa-icon library="ionicons" name="american-football"></wa-icon>
|
||||
<wa-icon library="ionicons" name="bug"></wa-icon>
|
||||
<wa-icon library="ionicons" name="chatbubble"></wa-icon>
|
||||
<wa-icon library="ionicons" name="settings"></wa-icon>
|
||||
<wa-icon library="ionicons" name="warning"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="ionicons" name="alarm-outline"></wa-icon>
|
||||
<wa-icon library="ionicons" name="american-football-outline"></wa-icon>
|
||||
<wa-icon library="ionicons" name="bug-outline"></wa-icon>
|
||||
<wa-icon library="ionicons" name="chatbubble-outline"></wa-icon>
|
||||
<wa-icon library="ionicons" name="settings-outline"></wa-icon>
|
||||
<wa-icon library="ionicons" name="warning-outline"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="ionicons" name="alarm-sharp"></wa-icon>
|
||||
<wa-icon library="ionicons" name="american-football-sharp"></wa-icon>
|
||||
<wa-icon library="ionicons" name="bug-sharp"></wa-icon>
|
||||
<wa-icon library="ionicons" name="chatbubble-sharp"></wa-icon>
|
||||
<wa-icon library="ionicons" name="settings-sharp"></wa-icon>
|
||||
<wa-icon library="ionicons" name="warning-sharp"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Jam Icons
|
||||
|
||||
This will register the [Jam Icons](https://jam-icons.com/) library using the jsDelivr CDN. This library has two variations: regular (default) and filled (`*-f`). A mutator function is required to set the SVG's `fill` to `currentColor`.
|
||||
|
||||
Icons in this library are licensed under the [MIT License](https://github.com/michaelampr/jam/blob/master/LICENSE).
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('jam', {
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/jam-icons@2.0.0/svg/${name}.svg`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor')
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="jam" name="calendar"></wa-icon>
|
||||
<wa-icon library="jam" name="camera"></wa-icon>
|
||||
<wa-icon library="jam" name="filter"></wa-icon>
|
||||
<wa-icon library="jam" name="leaf"></wa-icon>
|
||||
<wa-icon library="jam" name="picture"></wa-icon>
|
||||
<wa-icon library="jam" name="set-square"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="jam" name="calendar-f"></wa-icon>
|
||||
<wa-icon library="jam" name="camera-f"></wa-icon>
|
||||
<wa-icon library="jam" name="filter-f"></wa-icon>
|
||||
<wa-icon library="jam" name="leaf-f"></wa-icon>
|
||||
<wa-icon library="jam" name="picture-f"></wa-icon>
|
||||
<wa-icon library="jam" name="set-square-f"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Material Icons
|
||||
|
||||
This will register the [Material Icons](https://material.io/resources/icons/?style=baseline) library using the jsDelivr CDN. This library has three variations: outline (default), round (`*_round`), and sharp (`*_sharp`). A mutator function is required to set the SVG's `fill` to `currentColor`.
|
||||
|
||||
Icons in this library are licensed under the [Apache 2.0 License](https://github.com/google/material-design-icons/blob/master/LICENSE).
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('material', {
|
||||
resolver: name => {
|
||||
const match = name.match(/^(.*?)(_(round|sharp))?$/);
|
||||
return `https://cdn.jsdelivr.net/npm/@material-icons/svg@1.0.5/svg/${match[1]}/${match[3] || 'outline'}.svg`;
|
||||
},
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor')
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="material" name="notifications"></wa-icon>
|
||||
<wa-icon library="material" name="email"></wa-icon>
|
||||
<wa-icon library="material" name="delete"></wa-icon>
|
||||
<wa-icon library="material" name="volume_up"></wa-icon>
|
||||
<wa-icon library="material" name="settings"></wa-icon>
|
||||
<wa-icon library="material" name="shopping_basket"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="material" name="notifications_round"></wa-icon>
|
||||
<wa-icon library="material" name="email_round"></wa-icon>
|
||||
<wa-icon library="material" name="delete_round"></wa-icon>
|
||||
<wa-icon library="material" name="volume_up_round"></wa-icon>
|
||||
<wa-icon library="material" name="settings_round"></wa-icon>
|
||||
<wa-icon library="material" name="shopping_basket_round"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="material" name="notifications_sharp"></wa-icon>
|
||||
<wa-icon library="material" name="email_sharp"></wa-icon>
|
||||
<wa-icon library="material" name="delete_sharp"></wa-icon>
|
||||
<wa-icon library="material" name="volume_up_sharp"></wa-icon>
|
||||
<wa-icon library="material" name="settings_sharp"></wa-icon>
|
||||
<wa-icon library="material" name="shopping_basket_sharp"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Remix Icon
|
||||
|
||||
This will register the [Remix Icon](https://remixicon.com/) library using the jsDelivr CDN. This library groups icons by categories, so the name must include the category and icon separated by a slash, as well as the `-line` or `-fill` suffix as needed. A mutator function is required to set the SVG's `fill` to `currentColor`.
|
||||
|
||||
Icons in this library are licensed under the [Apache 2.0 License](https://github.com/Remix-Design/RemixIcon/blob/master/License).
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('remixicon', {
|
||||
resolver: name => {
|
||||
const match = name.match(/^(.*?)\/(.*?)?$/);
|
||||
match[1] = match[1].charAt(0).toUpperCase() + match[1].slice(1);
|
||||
return `https://cdn.jsdelivr.net/npm/remixicon@2.5.0/icons/${match[1]}/${match[2]}.svg`;
|
||||
},
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor')
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="remixicon" name="business/cloud-line"></wa-icon>
|
||||
<wa-icon library="remixicon" name="design/brush-line"></wa-icon>
|
||||
<wa-icon library="remixicon" name="business/pie-chart-line"></wa-icon>
|
||||
<wa-icon library="remixicon" name="development/bug-line"></wa-icon>
|
||||
<wa-icon library="remixicon" name="media/image-line"></wa-icon>
|
||||
<wa-icon library="remixicon" name="system/alert-line"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="remixicon" name="business/cloud-fill"></wa-icon>
|
||||
<wa-icon library="remixicon" name="design/brush-fill"></wa-icon>
|
||||
<wa-icon library="remixicon" name="business/pie-chart-fill"></wa-icon>
|
||||
<wa-icon library="remixicon" name="development/bug-fill"></wa-icon>
|
||||
<wa-icon library="remixicon" name="media/image-fill"></wa-icon>
|
||||
<wa-icon library="remixicon" name="system/alert-fill"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Tabler Icons
|
||||
|
||||
This will register the [Tabler Icons](https://tabler-icons.io/) library using the jsDelivr CDN. This library features over 1,950 open source icons.
|
||||
|
||||
Icons in this library are licensed under the [MIT License](https://github.com/tabler/tabler-icons/blob/master/LICENSE).
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('tabler', {
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/@tabler/icons@1.68.0/icons/${name}.svg`
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="tabler" name="alert-triangle"></wa-icon>
|
||||
<wa-icon library="tabler" name="arrow-back"></wa-icon>
|
||||
<wa-icon library="tabler" name="at"></wa-icon>
|
||||
<wa-icon library="tabler" name="ball-baseball"></wa-icon>
|
||||
<wa-icon library="tabler" name="cake"></wa-icon>
|
||||
<wa-icon library="tabler" name="files"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="tabler" name="keyboard"></wa-icon>
|
||||
<wa-icon library="tabler" name="moon"></wa-icon>
|
||||
<wa-icon library="tabler" name="pig"></wa-icon>
|
||||
<wa-icon library="tabler" name="printer"></wa-icon>
|
||||
<wa-icon library="tabler" name="ship"></wa-icon>
|
||||
<wa-icon library="tabler" name="toilet-paper"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Unicons
|
||||
|
||||
This will register the [Unicons](https://iconscout.com/unicons) library using the jsDelivr CDN. This library has two variations: line (default) and solid (`*-s`). A mutator function is required to set the SVG's `fill` to `currentColor`.
|
||||
|
||||
Icons in this library are licensed under the [Apache 2.0 License](https://github.com/Iconscout/unicons/blob/master/LICENSE). Some of the icons that appear on the Unicons website, particularly many of the solid variations, require a license and are therefore not available in the CDN.
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('unicons', {
|
||||
resolver: name => {
|
||||
const match = name.match(/^(.*?)(-s)?$/);
|
||||
return `https://cdn.jsdelivr.net/npm/@iconscout/unicons@3.0.3/svg/${match[2] === '-s' ? 'solid' : 'line'}/${
|
||||
match[1]
|
||||
}.svg`;
|
||||
},
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor')
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="unicons" name="clock"></wa-icon>
|
||||
<wa-icon library="unicons" name="graph-bar"></wa-icon>
|
||||
<wa-icon library="unicons" name="padlock"></wa-icon>
|
||||
<wa-icon library="unicons" name="polygon"></wa-icon>
|
||||
<wa-icon library="unicons" name="rocket"></wa-icon>
|
||||
<wa-icon library="unicons" name="star"></wa-icon>
|
||||
<br />
|
||||
<wa-icon library="unicons" name="clock-s"></wa-icon>
|
||||
<wa-icon library="unicons" name="graph-bar-s"></wa-icon>
|
||||
<wa-icon library="unicons" name="padlock-s"></wa-icon>
|
||||
<wa-icon library="unicons" name="polygon-s"></wa-icon>
|
||||
<wa-icon library="unicons" name="rocket-s"></wa-icon>
|
||||
<wa-icon library="unicons" name="star-s"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Customizing the Default Library
|
||||
|
||||
The default icon library contains over 1,300 icons courtesy of the [Bootstrap Icons](https://icons.getbootstrap.com/) project. These are the icons that display when you use `<wa-icon>` without the `library` attribute. If you prefer to have these icons resolve elsewhere or to a different icon library, register an icon library using the `default` name and a custom resolver.
|
||||
|
||||
This example will load the same set of icons from the jsDelivr CDN instead of your local assets folder.
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('default', {
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.0.0/icons/${name}.svg`
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
#### Customize the default library to use SVG sprites
|
||||
|
||||
To improve performance you can use a SVG sprites to avoid multiple trips for each SVG. The browser will load the sprite sheet once and then you reference the particular SVG within the sprite sheet using hash selector.
|
||||
|
||||
As always, make sure to benchmark these changes. When using HTTP/2, it may in fact be more bandwidth-friendly to use multiple small requests instead of 1 large sprite sheet.
|
||||
|
||||
:::danger
|
||||
When using sprite sheets, the `wa-load` and `wa-error` events will not fire.
|
||||
:::
|
||||
|
||||
:::danger
|
||||
For security reasons, browsers may apply the same-origin policy on `<use>` elements located in the `<wa-icon>` shadow DOM and may refuse to load a cross-origin URL. There is currently no defined way to set a cross-origin policy for `<use>` elements. For this reason, sprite sheets should only be used if you're self-hosting them.
|
||||
:::
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('sprite', {
|
||||
resolver: name => `/assets/images/sprite.svg#${name}`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor'),
|
||||
spriteSheet: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<wa-icon library="sprite" name="clock"></wa-icon>
|
||||
<wa-icon library="sprite" name="speedometer"></wa-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Customizing the System Library
|
||||
|
||||
The system library contains only the icons used internally by Web Awesome components. Unlike the default icon library, the system library does not rely on physical assets. Instead, its icons are hard-coded as data URIs into the resolver to ensure their availability.
|
||||
|
||||
If you want to change the icons Web Awesome uses internally, you can register an icon library using the `system` name and a custom resolver. If you choose to do this, it's your responsibility to provide all of the icons that are required by components. You can reference `src/components/library.system.ts` for a complete list of system icons used by Web Awesome.
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('system', {
|
||||
resolver: name => `/path/to/custom/icons/${name}.svg`
|
||||
});
|
||||
</script>
|
||||
```
|
||||
@@ -1,82 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Image Comparer
|
||||
description: Compare visual differences between similar photos with a sliding panel.
|
||||
layout: component
|
||||
---
|
||||
|
||||
For best results, use images that share the same dimensions. The slider can be controlled by dragging or pressing the left and right arrow keys. (Tip: press shift + arrows to move the slider in larger intervals, or home + end to jump to the beginning or end.)
|
||||
|
||||
```html:preview
|
||||
<wa-image-comparer>
|
||||
<img
|
||||
slot="before"
|
||||
src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80&sat=-100&bri=-5"
|
||||
alt="Grayscale version of kittens in a basket looking around."
|
||||
/>
|
||||
<img
|
||||
slot="after"
|
||||
src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80"
|
||||
alt="Color version of kittens in a basket looking around."
|
||||
/>
|
||||
</wa-image-comparer>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaImageComparer from '@shoelace-style/shoelace/dist/react/image-comparer';
|
||||
|
||||
const App = () => (
|
||||
<WaImageComparer>
|
||||
<img
|
||||
slot="before"
|
||||
src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80&sat=-100&bri=-5"
|
||||
alt="Grayscale version of kittens in a basket looking around."
|
||||
/>
|
||||
<img
|
||||
slot="after"
|
||||
src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80"
|
||||
alt="Color version of kittens in a basket looking around."
|
||||
/>
|
||||
</WaImageComparer>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Initial Position
|
||||
|
||||
Use the `position` attribute to set the initial position of the slider. This is a percentage from `0` to `100`.
|
||||
|
||||
```html:preview
|
||||
<wa-image-comparer position="25">
|
||||
<img
|
||||
slot="before"
|
||||
src="https://images.unsplash.com/photo-1520903074185-8eca362b3dce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1200&q=80"
|
||||
alt="A person sitting on bricks wearing untied boots."
|
||||
/>
|
||||
<img
|
||||
slot="after"
|
||||
src="https://images.unsplash.com/photo-1520640023173-50a135e35804?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2250&q=80"
|
||||
alt="A person sitting on a yellow curb tying shoelaces on a boot."
|
||||
/>
|
||||
</wa-image-comparer>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaImageComparer from '@shoelace-style/shoelace/dist/react/image-comparer';
|
||||
|
||||
const App = () => (
|
||||
<WaImageComparer position={25}>
|
||||
<img
|
||||
slot="before"
|
||||
src="https://images.unsplash.com/photo-1520903074185-8eca362b3dce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1200&q=80"
|
||||
alt="A person sitting on bricks wearing untied boots."
|
||||
/>
|
||||
<img
|
||||
slot="after"
|
||||
src="https://images.unsplash.com/photo-1520640023173-50a135e35804?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2250&q=80"
|
||||
alt="A person sitting on a yellow curb tying shoelaces on a boot."
|
||||
/>
|
||||
</WaImageComparer>
|
||||
);
|
||||
```
|
||||
@@ -1,281 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Input
|
||||
description: Inputs collect data from the user.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-input></wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => <WaInput />;
|
||||
```
|
||||
|
||||
:::tip
|
||||
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### Labels
|
||||
|
||||
Use the `label` attribute to give the input an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
|
||||
```html:preview
|
||||
<wa-input label="What is your name?"></wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => <WaInput label="What is your name?" />;
|
||||
```
|
||||
|
||||
### Help Text
|
||||
|
||||
Add descriptive help text to an input with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
|
||||
|
||||
```html:preview
|
||||
<wa-input label="Nickname" help-text="What would you like people to call you?"></wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => <WaInput label="Nickname" help-text="What would you like people to call you?" />;
|
||||
```
|
||||
|
||||
### Placeholders
|
||||
|
||||
Use the `placeholder` attribute to add a placeholder.
|
||||
|
||||
```html:preview
|
||||
<wa-input placeholder="Type something"></wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => <WaInput placeholder="Type something" />;
|
||||
```
|
||||
|
||||
### Clearable
|
||||
|
||||
Add the `clearable` attribute to add a clear button when the input has content.
|
||||
|
||||
```html:preview
|
||||
<wa-input placeholder="Clearable" clearable></wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => <WaInput placeholder="Clearable" clearable />;
|
||||
```
|
||||
|
||||
### Toggle Password
|
||||
|
||||
Add the `password-toggle` attribute to add a toggle button that will show the password when activated.
|
||||
|
||||
```html:preview
|
||||
<wa-input type="password" placeholder="Password Toggle" password-toggle></wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => <WaInput type="password" placeholder="Password Toggle" size="medium" password-toggle />;
|
||||
```
|
||||
|
||||
### Filled Inputs
|
||||
|
||||
Add the `filled` attribute to draw a filled input.
|
||||
|
||||
```html:preview
|
||||
<wa-input placeholder="Type something" filled></wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => <WaInput placeholder="Type something" filled />;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable an input.
|
||||
|
||||
```html:preview
|
||||
<wa-input placeholder="Disabled" disabled></wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => <WaInput placeholder="Disabled" disabled />;
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change an input's size.
|
||||
|
||||
```html:preview
|
||||
<wa-input placeholder="Small" size="small"></wa-input>
|
||||
<br />
|
||||
<wa-input placeholder="Medium" size="medium"></wa-input>
|
||||
<br />
|
||||
<wa-input placeholder="Large" size="large"></wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaInput placeholder="Small" size="small" />
|
||||
<br />
|
||||
<WaInput placeholder="Medium" size="medium" />
|
||||
<br />
|
||||
<WaInput placeholder="Large" size="large" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill
|
||||
|
||||
Use the `pill` attribute to give inputs rounded edges.
|
||||
|
||||
```html:preview
|
||||
<wa-input placeholder="Small" size="small" pill></wa-input>
|
||||
<br />
|
||||
<wa-input placeholder="Medium" size="medium" pill></wa-input>
|
||||
<br />
|
||||
<wa-input placeholder="Large" size="large" pill></wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaInput placeholder="Small" size="small" pill />
|
||||
<br />
|
||||
<WaInput placeholder="Medium" size="medium" pill />
|
||||
<br />
|
||||
<WaInput placeholder="Large" size="large" pill />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Input Types
|
||||
|
||||
The `type` attribute controls the type of input the browser renders.
|
||||
|
||||
```html:preview
|
||||
<wa-input type="email" placeholder="Email"></wa-input>
|
||||
<br />
|
||||
<wa-input type="number" placeholder="Number"></wa-input>
|
||||
<br />
|
||||
<wa-input type="date" placeholder="Date"></wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaInput type="email" placeholder="Email" />
|
||||
<br />
|
||||
<WaInput type="number" placeholder="Number" />
|
||||
<br />
|
||||
<WaInput type="date" placeholder="Date" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix & Suffix Icons
|
||||
|
||||
Use the `prefix` and `suffix` slots to add icons.
|
||||
|
||||
```html:preview
|
||||
<wa-input placeholder="Small" size="small">
|
||||
<wa-icon name="house" variant="solid" slot="prefix"></wa-icon>
|
||||
<wa-icon name="comment" variant="solid" slot="suffix"></wa-icon>
|
||||
</wa-input>
|
||||
<br />
|
||||
<wa-input placeholder="Medium" size="medium">
|
||||
<wa-icon name="house" variant="solid" slot="prefix"></wa-icon>
|
||||
<wa-icon name="comment" variant="solid" slot="suffix"></wa-icon>
|
||||
</wa-input>
|
||||
<br />
|
||||
<wa-input placeholder="Large" size="large">
|
||||
<wa-icon name="house" variant="solid" slot="prefix"></wa-icon>
|
||||
<wa-icon name="comment" variant="solid" slot="suffix"></wa-icon>
|
||||
</wa-input>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaInput from '@shoelace-style/shoelace/dist/react/input';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaInput placeholder="Small" size="small">
|
||||
<WaIcon name="house" variant="solid" slot="prefix"></WaIcon>
|
||||
<WaIcon name="comment" variant="solid" slot="suffix"></WaIcon>
|
||||
</WaInput>
|
||||
<br />
|
||||
<WaInput placeholder="Medium" size="medium">
|
||||
<WaIcon name="house" variant="solid" slot="prefix"></WaIcon>
|
||||
<WaIcon name="comment" variant="solid" slot="suffix"></WaIcon>
|
||||
</WaInput>
|
||||
<br />
|
||||
<WaInput placeholder="Large" size="large">
|
||||
<WaIcon name="house" variant="solid" slot="prefix"></WaIcon>
|
||||
<WaIcon name="comment" variant="solid" slot="suffix"></WaIcon>
|
||||
</WaInput>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Customizing Label Position
|
||||
|
||||
Use [CSS parts](#css-parts) to customize the way form controls are drawn. This example uses CSS grid to position the label to the left of the control, but the possible orientations are nearly endless. The same technique works for inputs, textareas, radio groups, and similar form controls.
|
||||
|
||||
```html:preview
|
||||
<wa-input class="label-on-left" label="Name" help-text="Enter your name"></wa-input>
|
||||
<wa-input class="label-on-left" label="Email" type="email" help-text="Enter your email"></wa-input>
|
||||
<wa-textarea class="label-on-left" label="Bio" help-text="Tell us something about yourself"></wa-textarea>
|
||||
|
||||
<style>
|
||||
.label-on-left {
|
||||
--label-width: 3.75rem;
|
||||
--gap-width: 1rem;
|
||||
}
|
||||
|
||||
.label-on-left + .label-on-left {
|
||||
margin-top: var(--wa-space-m);
|
||||
}
|
||||
|
||||
.label-on-left::part(form-control) {
|
||||
display: grid;
|
||||
grid: auto / var(--label-width) 1fr;
|
||||
gap: var(--wa-space-3xs) var(--gap-width);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label-on-left::part(form-control-label) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.label-on-left::part(form-control-help-text) {
|
||||
grid-column-start: 2;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
@@ -1,274 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Menu Item
|
||||
description: Menu items provide options for the user to pick from in a menu.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-menu style="max-width: 200px;">
|
||||
<wa-menu-item>Option 1</wa-menu-item>
|
||||
<wa-menu-item>Option 2</wa-menu-item>
|
||||
<wa-menu-item>Option 3</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item type="checkbox" checked>Checkbox</wa-menu-item>
|
||||
<wa-menu-item disabled>Disabled</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item>
|
||||
Prefix Icon
|
||||
<wa-icon slot="prefix" name="gift" variant="solid"></wa-icon>
|
||||
</wa-menu-item>
|
||||
<wa-menu-item>
|
||||
Suffix Icon
|
||||
<wa-icon slot="suffix" name="heart" variant="solid"></wa-icon>
|
||||
</wa-menu-item>
|
||||
</wa-menu>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaMenu style={{ maxWidth: '200px' }}>
|
||||
<WaMenuItem>Option 1</WaMenuItem>
|
||||
<WaMenuItem>Option 2</WaMenuItem>
|
||||
<WaMenuItem>Option 3</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem type="checkbox" checked>
|
||||
Checkbox
|
||||
</WaMenuItem>
|
||||
<WaMenuItem disabled>Disabled</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem>
|
||||
Prefix Icon
|
||||
<WaIcon slot="prefix" name="gift" variant="solid" />
|
||||
</WaMenuItem>
|
||||
<WaMenuItem>
|
||||
Suffix Icon
|
||||
<WaIcon slot="suffix" name="heart" variant="solid" />
|
||||
</WaMenuItem>
|
||||
</WaMenu>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
## Examples
|
||||
|
||||
### Prefix & Suffix
|
||||
|
||||
Add content to the start and end of menu items using the `prefix` and `suffix` slots.
|
||||
|
||||
```html:preview
|
||||
<wa-menu style="max-width: 200px;">
|
||||
<wa-menu-item>
|
||||
<wa-icon slot="prefix" name="house" variant="solid"></wa-icon>
|
||||
Home
|
||||
</wa-menu-item>
|
||||
|
||||
<wa-menu-item>
|
||||
<wa-icon slot="prefix" name="envelope" variant="solid"></wa-icon>
|
||||
Messages
|
||||
<wa-badge slot="suffix" variant="brand" pill>12</wa-badge>
|
||||
</wa-menu-item>
|
||||
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<wa-menu-item>
|
||||
<wa-icon slot="prefix" name="gear" variant="solid"></wa-icon>
|
||||
Settings
|
||||
</wa-menu-item>
|
||||
</wa-menu>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaBadge from '@shoelace-style/shoelace/dist/react/badge';
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaMenu style={{ maxWidth: '200px' }}>
|
||||
<WaMenuItem>
|
||||
<WaIcon slot="prefix" name="house" variant="solid" />
|
||||
Home
|
||||
</WaMenuItem>
|
||||
|
||||
<WaMenuItem>
|
||||
<WaIcon slot="prefix" name="envelope" variant="solid" />
|
||||
Messages
|
||||
<WaBadge slot="suffix" variant="brand" pill>
|
||||
12
|
||||
</WaBadge>
|
||||
</WaMenuItem>
|
||||
|
||||
<WaDivider />
|
||||
|
||||
<WaMenuItem>
|
||||
<WaIcon slot="prefix" name="gear" variant="solid" />
|
||||
Settings
|
||||
</WaMenuItem>
|
||||
</WaMenu>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Disabled
|
||||
|
||||
Add the `disabled` attribute to disable the menu item so it cannot be selected.
|
||||
|
||||
```html:preview
|
||||
<wa-menu style="max-width: 200px;">
|
||||
<wa-menu-item>Option 1</wa-menu-item>
|
||||
<wa-menu-item disabled>Option 2</wa-menu-item>
|
||||
<wa-menu-item>Option 3</wa-menu-item>
|
||||
</wa-menu>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaMenu style={{ maxWidth: '200px' }}>
|
||||
<WaMenuItem>Option 1</WaMenuItem>
|
||||
<WaMenuItem disabled>Option 2</WaMenuItem>
|
||||
<WaMenuItem>Option 3</WaMenuItem>
|
||||
</WaMenu>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Loading
|
||||
|
||||
Use the `loading` attribute to indicate that a menu item is busy. Like a disabled menu item, clicks will be suppressed until the loading state is removed.
|
||||
|
||||
```html:preview
|
||||
<wa-menu style="max-width: 200px;">
|
||||
<wa-menu-item>Option 1</wa-menu-item>
|
||||
<wa-menu-item loading>Option 2</wa-menu-item>
|
||||
<wa-menu-item>Option 3</wa-menu-item>
|
||||
</wa-menu>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaMenu style={{ maxWidth: '200px' }}>
|
||||
<WaMenuItem>Option 1</WaMenuItem>
|
||||
<WaMenuItem loading>Option 2</WaMenuItem>
|
||||
<WaMenuItem>Option 3</WaMenuItem>
|
||||
</WaMenu>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Checkbox Menu Items
|
||||
|
||||
Set the `type` attribute to `checkbox` to create a menu item that will toggle on and off when selected. You can use the `checked` attribute to set the initial state.
|
||||
|
||||
Checkbox menu items are visually indistinguishable from regular menu items. Their ability to be toggled is primarily inferred from context, much like you'd find in the menu of a native app.
|
||||
|
||||
```html:preview
|
||||
<wa-menu style="max-width: 200px;">
|
||||
<wa-menu-item type="checkbox">Autosave</wa-menu-item>
|
||||
<wa-menu-item type="checkbox" checked>Check Spelling</wa-menu-item>
|
||||
<wa-menu-item type="checkbox">Word Wrap</wa-menu-item>
|
||||
</wa-menu>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaMenu style={{ maxWidth: '200px' }}>
|
||||
<WaMenuItem type="checkbox">Autosave</WaMenuItem>
|
||||
<WaMenuItem type="checkbox" checked>
|
||||
Check Spelling
|
||||
</WaMenuItem>
|
||||
<WaMenuItem type="checkbox">Word Wrap</WaMenuItem>
|
||||
</WaMenu>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Value & Selection
|
||||
|
||||
The `value` attribute can be used to assign a hidden value, such as a unique identifier, to a menu item. When an item is selected, the `wa-select` event will be emitted and a reference to the item will be available at `event.detail.item`. You can use this reference to access the selected item's value, its checked state, and more.
|
||||
|
||||
```html:preview
|
||||
<wa-menu class="menu-value" style="max-width: 200px;">
|
||||
<wa-menu-item value="opt-1">Option 1</wa-menu-item>
|
||||
<wa-menu-item value="opt-2">Option 2</wa-menu-item>
|
||||
<wa-menu-item value="opt-3">Option 3</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item type="checkbox" value="opt-4">Checkbox 4</wa-menu-item>
|
||||
<wa-menu-item type="checkbox" value="opt-5">Checkbox 5</wa-menu-item>
|
||||
<wa-menu-item type="checkbox" value="opt-6">Checkbox 6</wa-menu-item>
|
||||
</wa-menu>
|
||||
|
||||
<script>
|
||||
const menu = document.querySelector('.menu-value');
|
||||
|
||||
menu.addEventListener('wa-select', event => {
|
||||
const item = event.detail.item;
|
||||
|
||||
// Log value
|
||||
if (item.type === 'checkbox') {
|
||||
console.log(`Selected value: ${item.value} (${item.checked ? 'checked' : 'unchecked'})`);
|
||||
} else {
|
||||
console.log(`Selected value: ${item.value}`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => {
|
||||
function handleSelect(event) {
|
||||
const item = event.detail.item;
|
||||
|
||||
// Toggle checked state
|
||||
item.checked = !item.checked;
|
||||
|
||||
// Log value
|
||||
console.log(`Selected value: ${item.value}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<WaMenu style={{ maxWidth: '200px' }} onWaSelect={handleSelect}>
|
||||
<WaMenuItem value="opt-1">Option 1</WaMenuItem>
|
||||
<WaMenuItem value="opt-2">Option 2</WaMenuItem>
|
||||
<WaMenuItem value="opt-3">Option 3</WaMenuItem>
|
||||
</WaMenu>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Menu Label
|
||||
description: Menu labels are used to describe a group of menu items.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-menu style="max-width: 200px;">
|
||||
<wa-menu-label>Fruits</wa-menu-label>
|
||||
<wa-menu-item value="apple">Apple</wa-menu-item>
|
||||
<wa-menu-item value="banana">Banana</wa-menu-item>
|
||||
<wa-menu-item value="orange">Orange</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-label>Vegetables</wa-menu-label>
|
||||
<wa-menu-item value="broccoli">Broccoli</wa-menu-item>
|
||||
<wa-menu-item value="carrot">Carrot</wa-menu-item>
|
||||
<wa-menu-item value="zucchini">Zucchini</wa-menu-item>
|
||||
</wa-menu>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuLabel from '@shoelace-style/shoelace/dist/react/menu-label';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaMenu style={{ maxWidth: '200px' }}>
|
||||
<WaMenuLabel>Fruits</WaMenuLabel>
|
||||
<WaMenuItem value="apple">Apple</WaMenuItem>
|
||||
<WaMenuItem value="banana">Banana</WaMenuItem>
|
||||
<WaMenuItem value="orange">Orange</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuLabel>Vegetables</WaMenuLabel>
|
||||
<WaMenuItem value="broccoli">Broccoli</WaMenuItem>
|
||||
<WaMenuItem value="carrot">Carrot</WaMenuItem>
|
||||
<WaMenuItem value="zucchini">Zucchini</WaMenuItem>
|
||||
</WaMenu>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
@@ -1,155 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Menu
|
||||
description: Menus provide a list of options for the user to choose from.
|
||||
layout: component
|
||||
---
|
||||
|
||||
You can use [menu items](/components/menu-item), [menu labels](/components/menu-label), and [dividers](/components/divider) to compose a menu. Menus support keyboard interactions, including type-to-select an option.
|
||||
|
||||
```html:preview
|
||||
<wa-menu style="max-width: 200px;">
|
||||
<wa-menu-item value="undo">Undo</wa-menu-item>
|
||||
<wa-menu-item value="redo">Redo</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item value="cut">Cut</wa-menu-item>
|
||||
<wa-menu-item value="copy">Copy</wa-menu-item>
|
||||
<wa-menu-item value="paste">Paste</wa-menu-item>
|
||||
<wa-menu-item value="delete">Delete</wa-menu-item>
|
||||
</wa-menu>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaMenu style={{ maxWidth: '200px' }}>
|
||||
<WaMenuItem value="undo">Undo</WaMenuItem>
|
||||
<WaMenuItem value="redo">Redo</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem value="cut">Cut</WaMenuItem>
|
||||
<WaMenuItem value="copy">Copy</WaMenuItem>
|
||||
<WaMenuItem value="paste">Paste</WaMenuItem>
|
||||
<WaMenuItem value="delete">Delete</WaMenuItem>
|
||||
</WaMenu>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
:::tip
|
||||
Menus are intended for system menus (dropdown menus, select menus, context menus, etc.). They should not be mistaken for navigation menus which serve a different purpose and have a different semantic meaning. If you're building navigation, use `<nav>` and `<a>` elements instead.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### In Dropdowns
|
||||
|
||||
Menus work really well when used inside [dropdowns](/components/dropdown).
|
||||
|
||||
```html:preview
|
||||
<wa-dropdown>
|
||||
<wa-button slot="trigger" caret>Edit</wa-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item value="cut">Cut</wa-menu-item>
|
||||
<wa-menu-item value="copy">Copy</wa-menu-item>
|
||||
<wa-menu-item value="paste">Paste</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-dropdown>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaDropdown>
|
||||
<WaButton slot="trigger" caret>Edit</WaButton>
|
||||
<WaMenu>
|
||||
<WaMenuItem value="cut">Cut</WaMenuItem>
|
||||
<WaMenuItem value="copy">Copy</WaMenuItem>
|
||||
<WaMenuItem value="paste">Paste</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
### Submenus
|
||||
|
||||
To create a submenu, nest an `<wa-menu slot="submenu">` in any [menu item](/components/menu-item).
|
||||
|
||||
```html:preview
|
||||
<wa-menu style="max-width: 200px;">
|
||||
<wa-menu-item value="undo">Undo</wa-menu-item>
|
||||
<wa-menu-item value="redo">Redo</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item value="cut">Cut</wa-menu-item>
|
||||
<wa-menu-item value="copy">Copy</wa-menu-item>
|
||||
<wa-menu-item value="paste">Paste</wa-menu-item>
|
||||
<wa-divider></wa-divider>
|
||||
<wa-menu-item>
|
||||
Find
|
||||
<wa-menu slot="submenu">
|
||||
<wa-menu-item value="find">Find…</wa-menu-item>
|
||||
<wa-menu-item value="find-previous">Find Next</wa-menu-item>
|
||||
<wa-menu-item value="find-next">Find Previous</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-menu-item>
|
||||
<wa-menu-item>
|
||||
Transformations
|
||||
<wa-menu slot="submenu">
|
||||
<wa-menu-item value="uppercase">Make uppercase</wa-menu-item>
|
||||
<wa-menu-item value="lowercase">Make lowercase</wa-menu-item>
|
||||
<wa-menu-item value="capitalize">Capitalize</wa-menu-item>
|
||||
</wa-menu>
|
||||
</wa-menu-item>
|
||||
</wa-menu>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => (
|
||||
<WaMenu style={{ maxWidth: '200px' }}>
|
||||
<WaMenuItem value="undo">Undo</WaMenuItem>
|
||||
<WaMenuItem value="redo">Redo</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem value="cut">Cut</WaMenuItem>
|
||||
<WaMenuItem value="copy">Copy</WaMenuItem>
|
||||
<WaMenuItem value="paste">Paste</WaMenuItem>
|
||||
<WaDivider />
|
||||
<WaMenuItem>
|
||||
Find
|
||||
<WaMenu slot="submenu">
|
||||
<WaMenuItem value="find">Find…</WaMenuItem>
|
||||
<WaMenuItem value="find-previous">Find Next</WaMenuItem>
|
||||
<WaMenuItem value="find-next">Find Previous</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaMenuItem>
|
||||
<WaMenuItem>
|
||||
Transformations
|
||||
<WaMenu slot="submenu">
|
||||
<WaMenuItem value="uppercase">Make uppercase</WaMenuItem>
|
||||
<WaMenuItem value="lowercase">Make lowercase</WaMenuItem>
|
||||
<WaMenuItem value="capitalize">Capitalize</WaMenuItem>
|
||||
</WaMenu>
|
||||
</WaMenuItem>
|
||||
</WaMenu>
|
||||
);
|
||||
```
|
||||
|
||||
:::warning
|
||||
As a UX best practice, avoid using more than one level of submenus when possible.
|
||||
:::
|
||||
|
||||
{% endraw %}
|
||||
@@ -1,82 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Option
|
||||
description: Options define the selectable items within various form controls such as select.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-select label="Select one">
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable an option and prevent it from being selected.
|
||||
|
||||
```html:preview
|
||||
<wa-select label="Select one">
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2" disabled>Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2" disabled>
|
||||
Option 2
|
||||
</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix & Suffix
|
||||
|
||||
Add icons to the start and end of menu items using the `prefix` and `suffix` slots.
|
||||
|
||||
```html:preview
|
||||
<wa-select label="Select one">
|
||||
<wa-option value="option-1">
|
||||
<wa-icon slot="prefix" name="envelope" variant="solid"></wa-icon>
|
||||
Email
|
||||
<wa-icon slot="suffix" name="circle-check" variant="solid"></wa-icon>
|
||||
</wa-option>
|
||||
|
||||
<wa-option value="option-2">
|
||||
<wa-icon slot="prefix" name="phone" variant="solid"></wa-icon>
|
||||
Phone
|
||||
<wa-icon slot="suffix" name="circle-check" variant="solid"></wa-icon>
|
||||
</wa-option>
|
||||
|
||||
<wa-option value="option-3">
|
||||
<wa-icon slot="prefix" name="comment" variant="solid"></wa-icon>
|
||||
Chat
|
||||
<wa-icon slot="suffix" name="circle-check" variant="solid"></wa-icon>
|
||||
</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
@@ -1,129 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Progress Bar
|
||||
description: Progress bars are used to show the status of an ongoing operation.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-progress-bar value="50"></wa-progress-bar>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaProgressBar from '@shoelace-style/shoelace/dist/react/progress-bar';
|
||||
|
||||
const App = () => <WaProgressBar value={50} />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Labels
|
||||
|
||||
Use the `label` attribute to label the progress bar and tell assistive devices how to announce it.
|
||||
|
||||
```html:preview
|
||||
<wa-progress-bar value="50" label="Upload progress"></wa-progress-bar>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaProgressBar from '@shoelace-style/shoelace/dist/react/progress-bar';
|
||||
|
||||
const App = () => <WaProgressBar value="50" label="Upload progress" />;
|
||||
```
|
||||
|
||||
### Custom Height
|
||||
|
||||
Use the `--height` custom property to set the progress bar's height.
|
||||
|
||||
```html:preview
|
||||
<wa-progress-bar value="50" style="--height: 6px;"></wa-progress-bar>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaProgressBar from '@shoelace-style/shoelace/dist/react/progress-bar';
|
||||
|
||||
const App = () => <WaProgressBar value={50} style={{ '--height': '6px' }} />;
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Showing Values
|
||||
|
||||
Use the default slot to show a value.
|
||||
|
||||
```html:preview
|
||||
<wa-progress-bar value="50" class="progress-bar-values">50%</wa-progress-bar>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-button circle><wa-icon name="minus" variant="solid" label="Decrease"></wa-icon></wa-button>
|
||||
<wa-button circle><wa-icon name="plus" variant="solid" label="Increase"></wa-icon></wa-button>
|
||||
|
||||
<script>
|
||||
const progressBar = document.querySelector('.progress-bar-values');
|
||||
const subtractButton = progressBar.nextElementSibling.nextElementSibling;
|
||||
const addButton = subtractButton.nextElementSibling;
|
||||
|
||||
addButton.addEventListener('click', () => {
|
||||
const value = Math.min(100, progressBar.value + 10);
|
||||
progressBar.value = value;
|
||||
progressBar.textContent = `${value}%`;
|
||||
});
|
||||
|
||||
subtractButton.addEventListener('click', () => {
|
||||
const value = Math.max(0, progressBar.value - 10);
|
||||
progressBar.value = value;
|
||||
progressBar.textContent = `${value}%`;
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaProgressBar from '@shoelace-style/shoelace/dist/react/progress-bar';
|
||||
|
||||
const App = () => {
|
||||
const [value, setValue] = useState(50);
|
||||
|
||||
function adjustValue(amount) {
|
||||
let newValue = value + amount;
|
||||
if (newValue < 0) newValue = 0;
|
||||
if (newValue > 100) newValue = 100;
|
||||
setValue(newValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaProgressBar value={value}>{value}%</WaProgressBar>
|
||||
|
||||
<br />
|
||||
|
||||
<WaButton circle onClick={() => adjustValue(-10)}>
|
||||
<WaIcon name="minus" variant="solid" label="Decrease" />
|
||||
</WaButton>
|
||||
|
||||
<WaButton circle onClick={() => adjustValue(10)}>
|
||||
<WaIcon name="plus" variant="solid" label="Increase" />
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Indeterminate
|
||||
|
||||
The `indeterminate` attribute can be used to inform the user that the operation is pending, but its status cannot currently be determined. In this state, `value` is ignored and the label, if present, will not be shown.
|
||||
|
||||
```html:preview
|
||||
<wa-progress-bar indeterminate></wa-progress-bar>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaProgressBar from '@shoelace-style/shoelace/dist/react/progress-bar';
|
||||
|
||||
const App = () => <WaProgressBar indeterminate />;
|
||||
```
|
||||
@@ -1,171 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Progress Ring
|
||||
description: Progress rings are used to show the progress of a determinate operation in a circular fashion.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-progress-ring value="25"></wa-progress-ring>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaProgressRing from '@shoelace-style/shoelace/dist/react/progress-ring';
|
||||
|
||||
const App = () => <WaProgressRing value="25" />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Size
|
||||
|
||||
Use the `--size` custom property to set the diameter of the progress ring.
|
||||
|
||||
```html:preview
|
||||
<wa-progress-ring value="50" style="--size: 200px;"></wa-progress-ring>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaProgressRing from '@shoelace-style/shoelace/dist/react/progress-ring';
|
||||
|
||||
const App = () => <WaProgressRing value="50" style={{ '--size': '200px' }} />;
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Track and Indicator Width
|
||||
|
||||
Use the `--track-width` and `--indicator-width` custom properties to set the width of the progress ring's track and indicator.
|
||||
|
||||
```html:preview
|
||||
<wa-progress-ring value="50" style="--track-width: 6px; --indicator-width: 12px;"></wa-progress-ring>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaProgressRing from '@shoelace-style/shoelace/dist/react/progress-ring';
|
||||
|
||||
const App = () => <WaProgressRing value="50" style={{ '--track-width': '6px', '--indicator-width': '12px' }} />;
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Colors
|
||||
|
||||
To change the color, use the `--track-color` and `--indicator-color` custom properties.
|
||||
|
||||
```html:preview
|
||||
<wa-progress-ring
|
||||
value="50"
|
||||
style="
|
||||
--track-color: pink;
|
||||
--indicator-color: deeppink;
|
||||
"
|
||||
></wa-progress-ring>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaProgressRing from '@shoelace-style/shoelace/dist/react/progress-ring';
|
||||
|
||||
const App = () => (
|
||||
<WaProgressRing
|
||||
value="50"
|
||||
style={{
|
||||
'--track-color': 'pink',
|
||||
'--indicator-color': 'deeppink'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Labels
|
||||
|
||||
Use the `label` attribute to label the progress ring and tell assistive devices how to announce it.
|
||||
|
||||
```html:preview
|
||||
<wa-progress-ring value="50" label="Upload progress"></wa-progress-ring>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaProgressRing from '@shoelace-style/shoelace/dist/react/progress-ring';
|
||||
|
||||
const App = () => <WaProgressRing value="50" label="Upload progress" />;
|
||||
```
|
||||
|
||||
### Showing Values
|
||||
|
||||
Use the default slot to show a label inside the progress ring.
|
||||
|
||||
```html:preview
|
||||
<wa-progress-ring value="50" class="progress-ring-values" style="margin-bottom: .5rem;">50%</wa-progress-ring>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-button circle><wa-icon name="minus" variant="solid" label="Decrease"></wa-icon></wa-button>
|
||||
<wa-button circle><wa-icon name="plus" variant="solid" label="Increase"></wa-icon></wa-button>
|
||||
|
||||
<script>
|
||||
const progressRing = document.querySelector('.progress-ring-values');
|
||||
const subtractButton = progressRing.nextElementSibling.nextElementSibling;
|
||||
const addButton = subtractButton.nextElementSibling;
|
||||
|
||||
addButton.addEventListener('click', () => {
|
||||
const value = Math.min(100, progressRing.value + 10);
|
||||
progressRing.value = value;
|
||||
progressRing.textContent = `${value}%`;
|
||||
});
|
||||
|
||||
subtractButton.addEventListener('click', () => {
|
||||
const value = Math.max(0, progressRing.value - 10);
|
||||
progressRing.value = value;
|
||||
progressRing.textContent = `${value}%`;
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaProgressRing from '@shoelace-style/shoelace/dist/react/progress-ring';
|
||||
|
||||
const App = () => {
|
||||
const [value, setValue] = useState(50);
|
||||
|
||||
function adjustValue(amount) {
|
||||
let newValue = value + amount;
|
||||
if (newValue < 0) newValue = 0;
|
||||
if (newValue > 100) newValue = 100;
|
||||
setValue(newValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaProgressRing value={value} style={{ marginBottom: '.5rem' }}>
|
||||
{value}%
|
||||
</WaProgressRing>
|
||||
|
||||
<br />
|
||||
|
||||
<WaButton circle onClick={() => adjustValue(-10)}>
|
||||
<WaIcon name="minus" variant="solid" label="Decrease" />
|
||||
</WaButton>
|
||||
|
||||
<WaButton circle onClick={() => adjustValue(10)}>
|
||||
<WaIcon name="plus" variant="solid" label="Increase" />
|
||||
</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
@@ -1,305 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Radio Button
|
||||
description: Radios buttons allow the user to select a single option from a group using a button-like control.
|
||||
layout: component
|
||||
---
|
||||
|
||||
Radio buttons are designed to be used with [radio groups](/components/radio-group). When a radio button has focus, the arrow keys can be used to change the selected option just like standard radio controls.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" name="a" value="1">
|
||||
<wa-radio-button value="1">Option 1</wa-radio-button>
|
||||
<wa-radio-button value="2">Option 2</wa-radio-button>
|
||||
<wa-radio-button value="3">Option 3</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" name="a" value="1">
|
||||
<WaRadioButton value="1">Option 1</WaRadioButton>
|
||||
<WaRadioButton value="2">Option 2</WaRadioButton>
|
||||
<WaRadioButton value="3">Option 3</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Checked States
|
||||
|
||||
To set the initial value and checked state, use the `value` attribute on the containing radio group.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" name="a" value="1">
|
||||
<wa-radio-button value="1">Option 1</wa-radio-button>
|
||||
<wa-radio-button value="2">Option 2</wa-radio-button>
|
||||
<wa-radio-button value="3">Option 3</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" name="a" value="1">
|
||||
<WaRadioButton value="1">Option 1</WaRadioButton>
|
||||
<WaRadioButton value="2">Option 2</WaRadioButton>
|
||||
<WaRadioButton value="3">Option 3</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable a radio button.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" name="a" value="1">
|
||||
<wa-radio-button value="1">Option 1</wa-radio-button>
|
||||
<wa-radio-button value="2" disabled>Option 2</wa-radio-button>
|
||||
<wa-radio-button value="3">Option 3</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" name="a" value="1">
|
||||
<WaRadioButton value="1">Option 1</WaRadioButton>
|
||||
<WaRadioButton value="2" disabled>
|
||||
Option 2
|
||||
</WaRadioButton>
|
||||
<WaRadioButton value="3">Option 3</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a radio button's size.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group size="small" label="Select an option" name="a" value="1">
|
||||
<wa-radio-button value="1">Option 1</wa-radio-button>
|
||||
<wa-radio-button value="2">Option 2</wa-radio-button>
|
||||
<wa-radio-button value="3">Option 3</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-radio-group size="medium" label="Select an option" name="a" value="1">
|
||||
<wa-radio-button value="1">Option 1</wa-radio-button>
|
||||
<wa-radio-button value="2">Option 2</wa-radio-button>
|
||||
<wa-radio-button value="3">Option 3</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-radio-group size="large" label="Select an option" name="a" value="1">
|
||||
<wa-radio-button value="1">Option 1</wa-radio-button>
|
||||
<wa-radio-button value="2">Option 2</wa-radio-button>
|
||||
<wa-radio-button value="3">Option 3</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup size="small" label="Select an option" name="a" value="1">
|
||||
<WaRadioButton value="1">Option 1</WaRadioButton>
|
||||
<WaRadioButton value="2">Option 2</WaRadioButton>
|
||||
<WaRadioButton value="3">Option 3</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<WaRadioGroup size="medium" label="Select an option" name="a" value="1">
|
||||
<WaRadioButton value="1">Option 1</WaRadioButton>
|
||||
<WaRadioButton value="2">Option 2</WaRadioButton>
|
||||
<WaRadioButton value="3">Option 3</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<WaRadioGroup size="large" label="Select an option" name="a" value="1">
|
||||
<WaRadioButton value="1">Option 1</WaRadioButton>
|
||||
<WaRadioButton value="2">Option 2</WaRadioButton>
|
||||
<WaRadioButton value="3">Option 3</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill Buttons
|
||||
|
||||
Use the `pill` attribute to give radio buttons rounded edges.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group size="small" label="Select an option" name="a" value="1">
|
||||
<wa-radio-button pill value="1">Option 1</wa-radio-button>
|
||||
<wa-radio-button pill value="2">Option 2</wa-radio-button>
|
||||
<wa-radio-button pill value="3">Option 3</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-radio-group size="medium" label="Select an option" name="a" value="1">
|
||||
<wa-radio-button pill value="1">Option 1</wa-radio-button>
|
||||
<wa-radio-button pill value="2">Option 2</wa-radio-button>
|
||||
<wa-radio-button pill value="3">Option 3</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-radio-group size="large" label="Select an option" name="a" value="1">
|
||||
<wa-radio-button pill value="1">Option 1</wa-radio-button>
|
||||
<wa-radio-button pill value="2">Option 2</wa-radio-button>
|
||||
<wa-radio-button pill value="3">Option 3</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup size="small" label="Select an option" name="a" value="1">
|
||||
<WaRadioButton pill value="1">Option 1</WaRadioButton>
|
||||
<WaRadioButton pill value="2">Option 2</WaRadioButton>
|
||||
<WaRadioButton pill value="3">Option 3</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<WaRadioGroup size="medium" label="Select an option" name="a" value="1">
|
||||
<WaRadioButton pill value="1">Option 1</WaRadioButton>
|
||||
<WaRadioButton pill value="2">Option 2</WaRadioButton>
|
||||
<WaRadioButton pill value="3">Option 3</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<WaRadioGroup size="large" label="Select an option" name="a" value="1">
|
||||
<WaRadioButton pill value="1">Option 1</WaRadioButton>
|
||||
<WaRadioButton pill value="2">Option 2</WaRadioButton>
|
||||
<WaRadioButton pill value="3">Option 3</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix and Suffix Icons
|
||||
|
||||
Use the `prefix` and `suffix` slots to add icons.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" name="a" value="1">
|
||||
<wa-radio-button value="1">
|
||||
<wa-icon slot="prefix" name="archive" variant="solid"></wa-icon>
|
||||
Option 1
|
||||
</wa-radio-button>
|
||||
|
||||
<wa-radio-button value="2">
|
||||
<wa-icon slot="suffix" name="bag-shopping" variant="solid"></wa-icon>
|
||||
Option 2
|
||||
</wa-radio-button>
|
||||
|
||||
<wa-radio-button value="3">
|
||||
<wa-icon slot="prefix" name="gift" variant="solid"></wa-icon>
|
||||
<wa-icon slot="suffix" name="shopping-cart" variant="solid"></wa-icon>
|
||||
Option 3
|
||||
</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" name="a" value="1">
|
||||
<WaRadioButton value="1">
|
||||
<WaIcon slot="prefix" name="archive" variant="solid" />
|
||||
Option 1
|
||||
</WaRadioButton>
|
||||
|
||||
<WaRadioButton value="2">
|
||||
<WaIcon slot="suffix" name="bag" variant="solid" />
|
||||
Option 2
|
||||
</WaRadioButton>
|
||||
|
||||
<WaRadioButton value="3">
|
||||
<WaIcon slot="prefix" name="gift" variant="solid" />
|
||||
<WaIcon slot="suffix" name="cart" variant="solid" />
|
||||
Option 3
|
||||
</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Buttons with Icons
|
||||
|
||||
You can omit button labels and use icons instead. Make sure to set a `label` attribute on each icon so screen readers will announce each option correctly.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" name="a" value="neutral">
|
||||
<wa-radio-button value="angry">
|
||||
<wa-icon name="face-angry" variant="solid" label="Angry"></wa-icon>
|
||||
</wa-radio-button>
|
||||
|
||||
<wa-radio-button value="sad">
|
||||
<wa-icon name="face-frown" variant="solid" label="Sad"></wa-icon>
|
||||
</wa-radio-button>
|
||||
|
||||
<wa-radio-button value="neutral">
|
||||
<wa-icon name="face-meh" variant="solid" label="Neutral"></wa-icon>
|
||||
</wa-radio-button>
|
||||
|
||||
<wa-radio-button value="happy">
|
||||
<wa-icon name="face-smile" variant="solid" label="Happy"></wa-icon>
|
||||
</wa-radio-button>
|
||||
|
||||
<wa-radio-button value="laughing">
|
||||
<wa-icon name="face-laugh" variant="solid" label="Laughing"></wa-icon>
|
||||
</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" name="a" value="neutral">
|
||||
<WaRadioButton value="angry">
|
||||
<WaIcon name="face-angry" label="Angry" />
|
||||
</WaRadioButton>
|
||||
|
||||
<WaRadioButton value="sad">
|
||||
<WaIcon name="face-frown" label="Sad" />
|
||||
</WaRadioButton>
|
||||
|
||||
<WaRadioButton value="neutral">
|
||||
<WaIcon name="face-neutral" label="Neutral" />
|
||||
</WaRadioButton>
|
||||
|
||||
<WaRadioButton value="happy">
|
||||
<WaIcon name="face-smile" label="Happy" />
|
||||
</WaRadioButton>
|
||||
|
||||
<WaRadioButton value="laughing">
|
||||
<WaIcon name="face-laughing" label="Laughing" />
|
||||
</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
@@ -1,293 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Radio Group
|
||||
description: Radio groups are used to group multiple radios or radio buttons so they function as a single form control.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" name="a" value="1">
|
||||
<wa-radio value="1">Option 1</wa-radio>
|
||||
<wa-radio value="2">Option 2</wa-radio>
|
||||
<wa-radio value="3">Option 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadio from '@shoelace-style/shoelace/dist/react/radio';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" name="a" value="1">
|
||||
<WaRadio value="1">Option 1</WaRadio>
|
||||
<WaRadio value="2">Option 2</WaRadio>
|
||||
<WaRadio value="3">Option 3</WaRadio>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Help Text
|
||||
|
||||
Add descriptive help text to a radio group with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" help-text="Choose the most appropriate option." name="a" value="1">
|
||||
<wa-radio value="1">Option 1</wa-radio>
|
||||
<wa-radio value="2">Option 2</wa-radio>
|
||||
<wa-radio value="3">Option 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadio from '@shoelace-style/shoelace/dist/react/radio';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" help-text="Choose the most appropriate option." name="a" value="1">
|
||||
<WaRadio value="1">Option 1</WaRadio>
|
||||
<WaRadio value="2">Option 2</WaRadio>
|
||||
<WaRadio value="3">Option 3</WaRadio>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Radio Buttons
|
||||
|
||||
[Radio buttons](/components/radio-button) offer an alternate way to display radio controls. In this case, an internal [button group](/components/button-group) is used to group the buttons into a single, cohesive control.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" help-text="Select an option that makes you proud." name="a" value="1">
|
||||
<wa-radio-button value="1">Option 1</wa-radio-button>
|
||||
<wa-radio-button value="2">Option 2</wa-radio-button>
|
||||
<wa-radio-button value="3">Option 3</wa-radio-button>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" name="a" value="1">
|
||||
<WaRadioButton value="1">Option 1</WaRadioButton>
|
||||
<WaRadioButton value="2">Option 2</WaRadioButton>
|
||||
<WaRadioButton value="3">Option 3</WaRadioButton>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabling Options
|
||||
|
||||
Radios and radio buttons can be disabled by adding the `disabled` attribute to the respective options inside the radio group.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" name="a" value="1">
|
||||
<wa-radio value="1">Option 1</wa-radio>
|
||||
<wa-radio value="2" disabled>Option 2</wa-radio>
|
||||
<wa-radio value="3">Option 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadio from '@shoelace-style/shoelace/dist/react/radio';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" name="a" value="1">
|
||||
<WaRadio value="1">Option 1</WaRadio>
|
||||
<WaRadio value="2" disabled>
|
||||
Option 2
|
||||
</WaRadio>
|
||||
<WaRadio value="3">Option 3</WaRadio>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizing Options
|
||||
|
||||
The size of [Radios](/components/radio) and [Radio Buttons](/components/radio-buttons) will be determined by the Radio Group's `size` attribute.
|
||||
|
||||
```html preview
|
||||
<wa-radio-group label="Select an option" size="medium" value="medium" class="radio-group-size">
|
||||
<wa-radio value="small">Small</wa-radio>
|
||||
<wa-radio value="medium">Medium</wa-radio>
|
||||
<wa-radio value="large">Large</wa-radio>
|
||||
</wa-radio-group>
|
||||
|
||||
<script>
|
||||
const radioGroup = document.querySelector('.radio-group-size');
|
||||
|
||||
radioGroup.addEventListener('wa-change', () => {
|
||||
radioGroup.size = radioGroup.value;
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import WaRadio from '@shoelace-style/shoelace/dist/react/radio';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => {
|
||||
const [size, setSize] = useState('medium');
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaRadioGroup
|
||||
label="Select an option"
|
||||
size={size}
|
||||
value={size}
|
||||
class="radio-group-size"
|
||||
onWaChange={event => setSize(event.target.value)}
|
||||
>
|
||||
<WaRadio value="small">Small</WaRadio>
|
||||
<WaRadio value="medium">Medium</WaRadio>
|
||||
<WaRadio value="large">Large</WaRadio>
|
||||
</WaRadioGroup>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
:::tip
|
||||
[Radios](/components/radio) and [Radio Buttons](/components/radio-button) also have a `size` attribute. This can be useful in certain compositions, but it will be ignored when used inside of a Radio Group.
|
||||
:::
|
||||
|
||||
### Validation
|
||||
|
||||
Setting the `required` attribute to make selecting an option mandatory. If a value has not been selected, it will prevent the form from submitting and display an error message.
|
||||
|
||||
```html:preview
|
||||
<form class="validation">
|
||||
<wa-radio-group label="Select an option" name="a" required>
|
||||
<wa-radio value="1">Option 1</wa-radio>
|
||||
<wa-radio value="2">Option 2</wa-radio>
|
||||
<wa-radio value="3">Option 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
<br />
|
||||
<wa-button type="submit" variant="brand">Submit</wa-button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const form = document.querySelector('.validation');
|
||||
|
||||
// Handle form submit
|
||||
form.addEventListener('submit', event => {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaRadio from '@shoelace-style/shoelace/dist/react/radio';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
const App = () => {
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
}
|
||||
|
||||
return (
|
||||
<form class="custom-validity" onSubmit={handleSubmit}>
|
||||
<WaRadioGroup label="Select an option" name="a" required onWaChange={handleChange}>
|
||||
<WaRadio value="1">
|
||||
Option 1
|
||||
</WaRadio>
|
||||
<WaRadiovalue="2">
|
||||
Option 2
|
||||
</WaRadio>
|
||||
<WaRadio value="3">
|
||||
Option 3
|
||||
</WaRadio>
|
||||
</WaRadioGroup>
|
||||
<br />
|
||||
<WaButton type="submit" variant="brand">
|
||||
Submit
|
||||
</WaButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Custom Validity
|
||||
|
||||
Use the `setCustomValidity()` method to set a custom validation message. This will prevent the form from submitting and make the browser display the error message you provide. To clear the error, call this function with an empty string.
|
||||
|
||||
```html:preview
|
||||
<form class="custom-validity">
|
||||
<wa-radio-group label="Select an option" name="a" value="1">
|
||||
<wa-radio value="1">Not me</wa-radio>
|
||||
<wa-radio value="2">Me neither</wa-radio>
|
||||
<wa-radio value="3">Choose me</wa-radio>
|
||||
</wa-radio-group>
|
||||
<br />
|
||||
<wa-button type="submit" variant="brand">Submit</wa-button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const form = document.querySelector('.custom-validity');
|
||||
const radioGroup = form.querySelector('wa-radio-group');
|
||||
const errorMessage = 'You must choose the last option';
|
||||
|
||||
// Set initial validity as soon as the element is defined
|
||||
customElements.whenDefined('wa-radio').then(() => {
|
||||
radioGroup.setCustomValidity(errorMessage);
|
||||
});
|
||||
|
||||
// Update validity when a selection is made
|
||||
form.addEventListener('wa-change', () => {
|
||||
const isValid = radioGroup.value === '3';
|
||||
radioGroup.setCustomValidity(isValid ? '' : errorMessage);
|
||||
});
|
||||
|
||||
// Handle form submit
|
||||
form.addEventListener('submit', event => {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useEffect, useRef } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaRadio from '@shoelace-style/shoelace/dist/react/radio';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
const App = () => {
|
||||
const radioGroup = useRef(null);
|
||||
const errorMessage = 'You must choose this option';
|
||||
|
||||
function handleChange() {
|
||||
radioGroup.current.setCustomValidity(radioGroup.current.value === '3' ? '' : errorMessage);
|
||||
}
|
||||
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
radio.current.setCustomValidity(errorMessage);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<form class="custom-validity" onSubmit={handleSubmit}>
|
||||
<WaRadioGroup ref={radioGroup} label="Select an option" name="a" value="1" onWaChange={handleChange}>
|
||||
<WaRadio value="1">Not me</WaRadio>
|
||||
<WaRadio value="2">Me neither</WaRadio>
|
||||
<WaRadio value="3">Choose me</WaRadio>
|
||||
</WaRadioGroup>
|
||||
<br />
|
||||
<WaButton type="submit" variant="brand">
|
||||
Submit
|
||||
</WaButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
@@ -1,145 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Radio
|
||||
description: Radios allow the user to select a single option from a group.
|
||||
layout: component
|
||||
---
|
||||
|
||||
Radios are designed to be used with [radio groups](/components/radio-group).
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" name="a" value="1">
|
||||
<wa-radio value="1">Option 1</wa-radio>
|
||||
<wa-radio value="2">Option 2</wa-radio>
|
||||
<wa-radio value="3">Option 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadio from '@shoelace-style/shoelace/dist/react/radio';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" name="a" value="1">
|
||||
<WaRadio value="1">Option 1</WaRadio>
|
||||
<WaRadio value="2">Option 2</WaRadio>
|
||||
<WaRadio value="3">Option 3</WaRadio>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
:::tip
|
||||
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### Initial Value
|
||||
|
||||
To set the initial value and checked state, use the `value` attribute on the containing radio group.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" name="a" value="3">
|
||||
<wa-radio value="1">Option 1</wa-radio>
|
||||
<wa-radio value="2">Option 2</wa-radio>
|
||||
<wa-radio value="3">Option 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadio from '@shoelace-style/shoelace/dist/react/radio';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" name="a" value="3">
|
||||
<WaRadio value="1">Option 1</WaRadio>
|
||||
<WaRadio value="2">Option 2</WaRadio>
|
||||
<WaRadio value="3">Option 3</WaRadio>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable a radio.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group label="Select an option" name="a" value="1">
|
||||
<wa-radio value="1">Option 1</wa-radio>
|
||||
<wa-radio value="2" disabled>Option 2</wa-radio>
|
||||
<wa-radio value="3">Option 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRadio from '@shoelace-style/shoelace/dist/react/radio';
|
||||
import WaRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
|
||||
|
||||
const App = () => (
|
||||
<WaRadioGroup label="Select an option" name="a" value="1">
|
||||
<WaRadio value="1">Option 1</WaRadio>
|
||||
<WaRadio value="2" disabled>
|
||||
Option 2
|
||||
</WaRadio>
|
||||
<WaRadio value="3">Option 3</WaRadio>
|
||||
</WaRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Sizes
|
||||
|
||||
Add the `size` attribute to the [Radio Group](/components/radio-group) to change the radios' size.
|
||||
|
||||
```html:preview
|
||||
<wa-radio-group size="small" value="1">
|
||||
<wa-radio value="1">Small 1</wa-radio>
|
||||
<wa-radio value="2">Small 2</wa-radio>
|
||||
<wa-radio value="3">Small 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-radio-group size="medium" value="1">
|
||||
<wa-radio value="1">Medium 1</wa-radio>
|
||||
<wa-radio value="2">Medium 2</wa-radio>
|
||||
<wa-radio value="3">Medium 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-radio-group size="large" value="1">
|
||||
<wa-radio value="1">Large 1</wa-radio>
|
||||
<wa-radio value="2">Large 2</wa-radio>
|
||||
<wa-radio value="3">Large 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import WaRadio from '@shoelace-style/shoelace/dist/react/radio';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaRadioGroup size="small" value="1">
|
||||
<WaRadio value="1">Small 1</WaRadio>
|
||||
<WaRadio value="2">Small 2</WaRadio>
|
||||
<WaRadio value="3">Small 3</WaRadio>
|
||||
</WaRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<WaRadioGroup size="medium" value="1">
|
||||
<WaRadio value="1">Medium 1</WaRadio>
|
||||
<WaRadio value="2">Medium 2</WaRadio>
|
||||
<WaRadio value="3">Medium 3</WaRadio>
|
||||
</WaRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<WaRadioGroup size="large" value="1">
|
||||
<WaRadio value="1">Large 1</WaRadio>
|
||||
<WaRadio value="2">Large 2</WaRadio>
|
||||
<WaRadio value="3">Large 3</WaRadio>
|
||||
</WaRadioGroup>
|
||||
</>
|
||||
);
|
||||
```
|
||||
@@ -1,191 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Range
|
||||
description: Ranges allow the user to select a single value within a given range using a slider.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-range></wa-range>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRange from '@shoelace-style/shoelace/dist/react/range';
|
||||
|
||||
const App = () => <WaRange />;
|
||||
```
|
||||
|
||||
:::tip
|
||||
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### Labels
|
||||
|
||||
Use the `label` attribute to give the range an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
|
||||
```html:preview
|
||||
<wa-range label="Volume" min="0" max="100"></wa-range>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRange from '@shoelace-style/shoelace/dist/react/range';
|
||||
|
||||
const App = () => <WaRange label="Volume" min={0} max={100} />;
|
||||
```
|
||||
|
||||
### Help Text
|
||||
|
||||
Add descriptive help text to a range with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
|
||||
|
||||
```html:preview
|
||||
<wa-range label="Volume" help-text="Controls the volume of the current song." min="0" max="100"></wa-range>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRange from '@shoelace-style/shoelace/dist/react/range';
|
||||
|
||||
const App = () => <WaRange label="Volume" help-text="Controls the volume of the current song." min={0} max={100} />;
|
||||
```
|
||||
|
||||
### Min, Max, and Step
|
||||
|
||||
Use the `min` and `max` attributes to set the range's minimum and maximum values, respectively. The `step` attribute determines the value's interval when increasing and decreasing.
|
||||
|
||||
```html:preview
|
||||
<wa-range min="0" max="10" step="1"></wa-range>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRange from '@shoelace-style/shoelace/dist/react/range';
|
||||
|
||||
const App = () => <WaRange min={0} max={10} step={1} />;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable a slider.
|
||||
|
||||
```html:preview
|
||||
<wa-range disabled></wa-range>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRange from '@shoelace-style/shoelace/dist/react/range';
|
||||
|
||||
const App = () => <WaRange disabled />;
|
||||
```
|
||||
|
||||
### Tooltip Placement
|
||||
|
||||
By default, the tooltip is shown on top. Set `tooltip` to `bottom` to show it below the slider.
|
||||
|
||||
```html:preview
|
||||
<wa-range tooltip="bottom"></wa-range>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRange from '@shoelace-style/shoelace/dist/react/range';
|
||||
|
||||
const App = () => <WaRange tooltip="bottom" />;
|
||||
```
|
||||
|
||||
### Disable the Tooltip
|
||||
|
||||
To disable the tooltip, set `tooltip` to `none`.
|
||||
|
||||
```html:preview
|
||||
<wa-range tooltip="none"></wa-range>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRange from '@shoelace-style/shoelace/dist/react/range';
|
||||
|
||||
const App = () => <WaRange tooltip="none" />;
|
||||
```
|
||||
|
||||
### Custom Track Colors
|
||||
|
||||
You can customize the active and inactive portions of the track using the `--track-color-active` and `--track-color-inactive` custom properties.
|
||||
|
||||
```html:preview
|
||||
<wa-range
|
||||
style="
|
||||
--track-color-active: var(--wa-color-brand-spot);
|
||||
--track-color-inactive: var(--wa-color-brand-fill-highlight);
|
||||
"
|
||||
></wa-range>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaRange from '@shoelace-style/shoelace/dist/react/range';
|
||||
|
||||
const App = () => (
|
||||
<WaRange
|
||||
style={{
|
||||
'--track-color-active': 'var(--wa-color-brand-spot)',
|
||||
'--track-color-inactive': 'var(--wa-color-brand-fill-highlight)'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Custom Track Offset
|
||||
|
||||
You can customize the initial offset of the active track using the `--track-active-offset` custom property.
|
||||
|
||||
```html:preview
|
||||
<wa-range
|
||||
min="-100"
|
||||
max="100"
|
||||
style="
|
||||
--track-color-active: var(--wa-color-brand-spot);
|
||||
--track-color-inactive: var(--wa-color-brand-fill-highlight);
|
||||
--track-active-offset: 50%;
|
||||
"
|
||||
></wa-range>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaRange from '@shoelace-style/shoelace/dist/react/range';
|
||||
|
||||
const App = () => (
|
||||
<WaRange
|
||||
min={-100}
|
||||
max={100}
|
||||
style={{
|
||||
'--track-color-active': 'var(--wa-color-brand-spot)',
|
||||
'--track-color-inactive': 'var(--wa-color-brand-fill-highlight)',
|
||||
'--track-active-offset': '50%'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Custom Tooltip Formatter
|
||||
|
||||
You can change the tooltip's content by setting the `tooltipFormatter` property to a function that accepts the range's value as an argument.
|
||||
|
||||
```html:preview
|
||||
<wa-range min="0" max="100" step="1" class="range-with-custom-formatter"></wa-range>
|
||||
|
||||
<script>
|
||||
const range = document.querySelector('.range-with-custom-formatter');
|
||||
range.tooltipFormatter = value => `Total - ${value}%`;
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRange from '@shoelace-style/shoelace/dist/react/range';
|
||||
|
||||
const App = () => <WaRange min={0} max={100} step={1} tooltipFormatter={value => `Total - ${value}%`} />;
|
||||
```
|
||||
@@ -1,256 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Rating
|
||||
description: Ratings give users a way to quickly view and provide feedback.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-rating label="Rating"></wa-rating>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
const App = () => <WaRating label="Rating" />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Labels
|
||||
|
||||
Ratings are commonly identified contextually, so labels aren't displayed. However, you should always provide one for assistive devices using the `label` attribute.
|
||||
|
||||
```html:preview
|
||||
<wa-rating label="Rate this component"></wa-rating>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
const App = () => <WaRating label="Rate this component" />;
|
||||
```
|
||||
|
||||
### Maximum Value
|
||||
|
||||
Ratings are 0-5 by default. To change the maximum possible value, use the `max` attribute.
|
||||
|
||||
```html:preview
|
||||
<wa-rating label="Rating" max="3"></wa-rating>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
const App = () => <WaRating label="Rating" max={3} />;
|
||||
```
|
||||
|
||||
### Precision
|
||||
|
||||
Use the `precision` attribute to let users select fractional ratings.
|
||||
|
||||
```html:preview
|
||||
<wa-rating label="Rating" precision="0.5" value="2.5"></wa-rating>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
const App = () => <WaRating label="Rating" precision={0.5} value={2.5} />;
|
||||
```
|
||||
|
||||
### Symbol Sizes
|
||||
|
||||
Set the `--symbol-size` custom property to adjust the size.
|
||||
|
||||
```html:preview
|
||||
<wa-rating label="Rating" style="--symbol-size: 2rem;"></wa-rating>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
const App = () => <WaRating label="Rating" style={{ '--symbol-size': '2rem' }} />;
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Readonly
|
||||
|
||||
Use the `readonly` attribute to display a rating that users can't change.
|
||||
|
||||
```html:preview
|
||||
<wa-rating label="Rating" readonly value="3"></wa-rating>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
const App = () => <WaRating label="Rating" readonly value={3} />;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disable` attribute to disable the rating.
|
||||
|
||||
```html:preview
|
||||
<wa-rating label="Rating" disabled value="3"></wa-rating>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
const App = () => <WaRating label="Rating" disabled value={3} />;
|
||||
```
|
||||
|
||||
### Detecting Hover
|
||||
|
||||
Use the `wa-hover` event to detect when the user hovers over (or touch and drag) the rating. This lets you hook into values as the user interacts with the rating, but before they select a value.
|
||||
|
||||
The event has a payload with `phase` and `value` properties. The `phase` property tells when hovering starts, moves to a new value, and ends. The `value` property tells what the rating's value would be if the user were to commit to the hovered value.
|
||||
|
||||
```html:preview
|
||||
<div class="detect-hover">
|
||||
<wa-rating label="Rating"></wa-rating>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const rating = document.querySelector('.detect-hover > wa-rating');
|
||||
const span = rating.nextElementSibling;
|
||||
const terms = ['No rating', 'Terrible', 'Bad', 'OK', 'Good', 'Excellent'];
|
||||
|
||||
rating.addEventListener('wa-hover', event => {
|
||||
span.textContent = terms[event.detail.value];
|
||||
|
||||
// Clear feedback when hovering stops
|
||||
if (event.detail.phase === 'end') {
|
||||
span.textContent = '';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.detect-hover span {
|
||||
position: relative;
|
||||
top: -4px;
|
||||
left: 8px;
|
||||
border-radius: var(--wa-corners-s);
|
||||
background: var(--wa-color-neutral-spot);
|
||||
color: var(--wa-color-neutral-text-on-spot);
|
||||
text-align: center;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.detect-hover span:empty {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
const terms = ['No rating', 'Terrible', 'Bad', 'OK', 'Good', 'Excellent'];
|
||||
const css = `
|
||||
.detect-hover span {
|
||||
position: relative;
|
||||
top: -4px;
|
||||
left: 8px;
|
||||
border-radius: var(--wa-corners-s);
|
||||
background: var(--wa-color-neutral-spot);
|
||||
color: var(--wa-color-neutral-text-on-spot);
|
||||
text-align: center;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.detect-hover span:empty {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
function handleHover(event) {
|
||||
rating.addEventListener('wa-hover', event => {
|
||||
setFeedback(terms[event.detail.value]);
|
||||
|
||||
// Clear feedback when hovering stops
|
||||
if (event.detail.phase === 'end') {
|
||||
setFeedback('');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
const [feedback, setFeedback] = useState(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="detect-hover">
|
||||
<WaRating label="Rating" onWaHover={handleHover} />
|
||||
<span>{feedback}</span>
|
||||
</div>
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Custom Icons
|
||||
|
||||
You can provide custom icons by passing a function to the `getSymbol` property.
|
||||
|
||||
```html:preview
|
||||
<wa-rating label="Rating" class="rating-hearts" style="--symbol-color-active: #ff4136;"></wa-rating>
|
||||
|
||||
<script>
|
||||
const rating = document.querySelector('.rating-hearts');
|
||||
rating.getSymbol = () => '<wa-icon name="heart" variant="solid"></wa-icon>';
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
const App = () => (
|
||||
<WaRating
|
||||
label="Rating"
|
||||
getSymbol={() => '<wa-icon name="heart" variant="solid"></wa-icon>'}
|
||||
style={{ '--symbol-color-active': '#ff4136' }}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Value-based Icons
|
||||
|
||||
You can also use the `getSymbol` property to render different icons based on value.
|
||||
|
||||
```html:preview
|
||||
<wa-rating label="Rating" class="rating-emojis"></wa-rating>
|
||||
|
||||
<script>
|
||||
const rating = document.querySelector('.rating-emojis');
|
||||
|
||||
rating.getSymbol = value => {
|
||||
const icons = ['face-angry', 'face-frown', 'face-meh', 'face-smile', 'face-laugh'];
|
||||
return `<wa-icon name="${icons[value - 1]}"></wa-icon>`;
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaRating from '@shoelace-style/shoelace/dist/react/rating';
|
||||
|
||||
function getSymbol(value) {
|
||||
const icons = ['face-angry', 'face-frown', 'face-meh', 'face-smile', 'face-laugh'];
|
||||
return `<wa-icon name="${icons[value - 1]}"></wa-icon>`;
|
||||
}
|
||||
|
||||
const App = () => <WaRating label="Rating" getSymbol={getSymbol} />;
|
||||
```
|
||||
@@ -1,504 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Select
|
||||
description: Selects allow you to choose items from a menu of predefined options.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-select>
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
<wa-option value="option-4">Option 4</wa-option>
|
||||
<wa-option value="option-5">Option 5</wa-option>
|
||||
<wa-option value="option-6">Option 6</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
<WaOption value="option-4">Option 4</WaOption>
|
||||
<WaOption value="option-5">Option 5</WaOption>
|
||||
<WaOption value="option-6">Option 6</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
:::tip
|
||||
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### Labels
|
||||
|
||||
Use the `label` attribute to give the select an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
|
||||
```html:preview
|
||||
<wa-select label="Select one">
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect label="Select one">
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Help Text
|
||||
|
||||
Add descriptive help text to a select with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
|
||||
|
||||
```html:preview
|
||||
<wa-select label="Experience" help-text="Please tell us your skill level.">
|
||||
<wa-option value="1">Novice</wa-option>
|
||||
<wa-option value="2">Intermediate</wa-option>
|
||||
<wa-option value="3">Advanced</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect label="Experience" help-text="Please tell us your skill level.">
|
||||
<WaOption value="1">Novice</WaOption>
|
||||
<WaOption value="2">Intermediate</WaOption>
|
||||
<WaOption value="3">Advanced</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Placeholders
|
||||
|
||||
Use the `placeholder` attribute to add a placeholder.
|
||||
|
||||
```html:preview
|
||||
<wa-select placeholder="Select one">
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect placeholder="Select one">
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Clearable
|
||||
|
||||
Use the `clearable` attribute to make the control clearable. The clear button only appears when an option is selected.
|
||||
|
||||
```html:preview
|
||||
<wa-select clearable value="option-1">
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect placeholder="Clearable" clearable>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Filled Selects
|
||||
|
||||
Add the `filled` attribute to draw a filled select.
|
||||
|
||||
```html:preview
|
||||
<wa-select filled>
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect filled>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill
|
||||
|
||||
Use the `pill` attribute to give selects rounded edges.
|
||||
|
||||
```html:preview
|
||||
<wa-select pill>
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect pill>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable a select.
|
||||
|
||||
```html:preview
|
||||
<wa-select placeholder="Disabled" disabled>
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect placeholder="Disabled" disabled>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Multiple
|
||||
|
||||
To allow multiple options to be selected, use the `multiple` attribute. It's a good practice to use `clearable` when this option is enabled. To set multiple values at once, set `value` to a space-delimited list of values.
|
||||
|
||||
```html:preview
|
||||
<wa-select label="Select a Few" value="option-1 option-2 option-3" multiple clearable>
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
<wa-option value="option-4">Option 4</wa-option>
|
||||
<wa-option value="option-5">Option 5</wa-option>
|
||||
<wa-option value="option-6">Option 6</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect label="Select a Few" value="option-1 option-2 option-3" multiple clearable>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
<WaOption value="option-4">Option 4</WaOption>
|
||||
<WaOption value="option-5">Option 5</WaOption>
|
||||
<WaOption value="option-6">Option 6</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
:::tip
|
||||
Note that multi-select options may wrap, causing the control to expand vertically. You can use the `max-options-visible` attribute to control the maximum number of selected options to show at once.
|
||||
:::
|
||||
|
||||
### Setting Initial Values
|
||||
|
||||
Use the `value` attribute to set the initial selection.
|
||||
|
||||
When using `multiple`, the `value` _attribute_ uses space-delimited values to select more than one option. Because of this, `<wa-option>` values cannot contain spaces. If you're accessing the `value` _property_ through Javascript, it will be an array.
|
||||
|
||||
```html:preview
|
||||
<wa-select value="option-1 option-2" multiple clearable>
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
<wa-option value="option-4">Option 4</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaDivider from '@shoelace-style/shoelace/dist/react/divider';
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect value="option-1 option-2" multiple clearable>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Grouping Options
|
||||
|
||||
Use `<wa-divider>` to group listbox items visually. You can also use `<small>` to provide labels, but they won't be announced by most assistive devices.
|
||||
|
||||
```html:preview
|
||||
<wa-select>
|
||||
<small>Section 1</small>
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
<wa-divider></wa-divider>
|
||||
<small>Section 2</small>
|
||||
<wa-option value="option-4">Option 4</wa-option>
|
||||
<wa-option value="option-5">Option 5</wa-option>
|
||||
<wa-option value="option-6">Option 6</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
<WaOption value="option-4">Option 4</WaOption>
|
||||
<WaOption value="option-5">Option 5</WaOption>
|
||||
<WaOption value="option-6">Option 6</WaOption>
|
||||
</WaSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a select's size. Note that size does not apply to listbox options.
|
||||
|
||||
```html:preview
|
||||
<wa-select placeholder="Small" size="small">
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-select placeholder="Medium" size="medium">
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
|
||||
<br />
|
||||
|
||||
<wa-select placeholder="Large" size="large">
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaSelect placeholder="Small" size="small">
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
|
||||
<br />
|
||||
|
||||
<WaSelect placeholder="Medium" size="medium">
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
|
||||
<br />
|
||||
|
||||
<WaSelect placeholder="Large" size="large">
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Placement
|
||||
|
||||
The preferred placement of the select's listbox can be set with the `placement` attribute. Note that the actual position may vary to ensure the panel remains in the viewport. Valid placements are `top` and `bottom`.
|
||||
|
||||
```html:preview
|
||||
<wa-select placement="top">
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<WaSelect placement="top">
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix Icons
|
||||
|
||||
Use the `prefix` slot to prepend an icon to the control.
|
||||
|
||||
```html:preview
|
||||
<wa-select placeholder="Small" size="small" clearable>
|
||||
<wa-icon slot="prefix" name="house" variant="solid"></wa-icon>
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
<br />
|
||||
<wa-select placeholder="Medium" size="medium" clearable>
|
||||
<wa-icon slot="prefix" name="house" variant="solid"></wa-icon>
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
<br />
|
||||
<wa-select placeholder="Large" size="large" clearable>
|
||||
<wa-icon slot="prefix" name="house" variant="solid"></wa-icon>
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
<wa-option value="option-2">Option 2</wa-option>
|
||||
<wa-option value="option-3">Option 3</wa-option>
|
||||
</wa-select>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
import WaOption from '@shoelace-style/shoelace/dist/react/option';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaSelect placeholder="Small" size="small">
|
||||
<WaIcon slot="prefix" name="house" variant="solid"></WaIcon>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
<br />
|
||||
<WaSelect placeholder="Medium" size="medium">
|
||||
<WaIcon slot="prefix" name="house" variant="solid"></WaIcon>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
<br />
|
||||
<WaSelect placeholder="Large" size="large">
|
||||
<WaIcon slot="prefix" name="house" variant="solid"></WaIcon>
|
||||
<WaOption value="option-1">Option 1</WaOption>
|
||||
<WaOption value="option-2">Option 2</WaOption>
|
||||
<WaOption value="option-3">Option 3</WaOption>
|
||||
</WaSelect>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Tags
|
||||
|
||||
When multiple options can be selected, you can provide custom tags by passing a function to the `getTag` property. Your function can return a string of HTML, a <a href="https://lit.dev/docs/templates/overview/">Lit Template</a>, or an [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement). The `getTag()` function will be called for each option. The first argument is an `<wa-option>` element and the second argument is the tag's index (its position in the tag list).
|
||||
|
||||
Remember that custom tags are rendered in a shadow root. To style them, you can use the `style` attribute in your template or you can add your own [parts](/getting-started/customizing/#css-parts) and target them with the [`::part()`](https://developer.mozilla.org/en-US/docs/Web/CSS/::part) selector.
|
||||
|
||||
```html:preview
|
||||
<wa-select
|
||||
placeholder="Select one"
|
||||
value="email phone"
|
||||
multiple
|
||||
clearable
|
||||
class="custom-tag"
|
||||
>
|
||||
<wa-option value="email">
|
||||
<wa-icon slot="prefix" name="envelope" variant="solid"></wa-icon>
|
||||
Email
|
||||
</wa-option>
|
||||
<wa-option value="phone">
|
||||
<wa-icon slot="prefix" name="phone" variant="solid"></wa-icon>
|
||||
Phone
|
||||
</wa-option>
|
||||
<wa-option value="chat">
|
||||
<wa-icon slot="prefix" name="comment" variant="solid"></wa-icon>
|
||||
Chat
|
||||
</wa-option>
|
||||
</wa-select>
|
||||
|
||||
<script type="module">
|
||||
const select = document.querySelector('.custom-tag');
|
||||
|
||||
select.getTag = (option, index) => {
|
||||
// Use the same icon used in the <wa-option>
|
||||
const name = option.querySelector('wa-icon[slot="prefix"]').name;
|
||||
|
||||
// You can return a string, a Lit Template, or an HTMLElement here
|
||||
return `
|
||||
<wa-tag removable>
|
||||
<wa-icon name="${name}" style="padding-inline-end: .5rem;"></wa-icon>
|
||||
${option.getTextLabel()}
|
||||
</wa-tag>
|
||||
`;
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
:::warning
|
||||
Be sure you trust the content you are outputting! Passing unsanitized user input to `getTag()` can result in XSS vulnerabilities.
|
||||
:::
|
||||
@@ -1,448 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Skeleton
|
||||
description: Skeletons are used to provide a visual representation of where content will eventually be drawn.
|
||||
layout: component
|
||||
---
|
||||
|
||||
These are simple containers for scaffolding layouts that mimic what users will see when content has finished loading. This prevents large areas of empty space during asynchronous operations.
|
||||
|
||||
Skeletons try not to be opinionated, as there are endless possibilities for designing layouts. Therefore, you'll likely use more than one skeleton to create the effect you want. If you find yourself using them frequently, consider creating a template that renders them with the desired arrangement and styles.
|
||||
|
||||
```html:preview
|
||||
<div class="skeleton-overview">
|
||||
<header>
|
||||
<wa-skeleton effect="sheen"></wa-skeleton>
|
||||
<wa-skeleton effect="sheen"></wa-skeleton>
|
||||
</header>
|
||||
|
||||
<wa-skeleton effect="sheen"></wa-skeleton>
|
||||
<wa-skeleton effect="sheen"></wa-skeleton>
|
||||
<wa-skeleton effect="sheen"></wa-skeleton>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.skeleton-overview header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skeleton-overview header wa-skeleton:last-child {
|
||||
flex: 0 0 auto;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.skeleton-overview wa-skeleton {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skeleton-overview wa-skeleton:nth-child(1) {
|
||||
float: left;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-right: 1rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.skeleton-overview wa-skeleton:nth-child(3) {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.skeleton-overview wa-skeleton:nth-child(4) {
|
||||
width: 80%;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaSkeleton from '@shoelace-style/shoelace/dist/react/skeleton';
|
||||
|
||||
const css = `
|
||||
.skeleton-overview header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skeleton-overview header wa-skeleton:last-child {
|
||||
flex: 0 0 auto;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.skeleton-overview wa-skeleton {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skeleton-overview wa-skeleton:nth-child(1) {
|
||||
float: left;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-right: 1rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.skeleton-overview wa-skeleton:nth-child(3) {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.skeleton-overview wa-skeleton:nth-child(4) {
|
||||
width: 80%;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="skeleton-overview">
|
||||
<header>
|
||||
<WaSkeleton />
|
||||
<WaSkeleton />
|
||||
</header>
|
||||
|
||||
<WaSkeleton />
|
||||
<WaSkeleton />
|
||||
<WaSkeleton />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Effects
|
||||
|
||||
There are two built-in effects, `sheen` and `pulse`. Effects are intentionally subtle, as they can be distracting when used extensively. The default is `none`, which displays a static, non-animated skeleton.
|
||||
|
||||
```html:preview
|
||||
<div class="skeleton-effects">
|
||||
<wa-skeleton effect="none"></wa-skeleton>
|
||||
None
|
||||
|
||||
<wa-skeleton effect="sheen"></wa-skeleton>
|
||||
Sheen
|
||||
|
||||
<wa-skeleton effect="pulse"></wa-skeleton>
|
||||
Pulse
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.skeleton-effects {
|
||||
font-size: var(--wa-font-size-s);
|
||||
}
|
||||
|
||||
.skeleton-effects wa-skeleton:not(:first-child) {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaSkeleton from '@shoelace-style/shoelace/dist/react/skeleton';
|
||||
|
||||
const css = `
|
||||
.skeleton-effects {
|
||||
font-size: var(--wa-font-size-s);
|
||||
}
|
||||
|
||||
.skeleton-effects wa-skeleton:not(:first-child) {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="skeleton-effects">
|
||||
<WaSkeleton effect="none" />
|
||||
None
|
||||
<WaSkeleton effect="sheen" />
|
||||
Sheen
|
||||
<WaSkeleton effect="pulse" />
|
||||
Pulse
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Paragraphs
|
||||
|
||||
Use multiple skeletons and some clever styles to simulate paragraphs.
|
||||
|
||||
```html:preview
|
||||
<div class="skeleton-paragraphs">
|
||||
<wa-skeleton></wa-skeleton>
|
||||
<wa-skeleton></wa-skeleton>
|
||||
<wa-skeleton></wa-skeleton>
|
||||
<wa-skeleton></wa-skeleton>
|
||||
<wa-skeleton></wa-skeleton>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.skeleton-paragraphs wa-skeleton {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skeleton-paragraphs wa-skeleton:nth-child(2) {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.skeleton-paragraphs wa-skeleton:nth-child(4) {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.skeleton-paragraphs wa-skeleton:last-child {
|
||||
width: 50%;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaSkeleton from '@shoelace-style/shoelace/dist/react/skeleton';
|
||||
|
||||
const css = `
|
||||
.skeleton-paragraphs wa-skeleton {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skeleton-paragraphs wa-skeleton:nth-child(2) {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.skeleton-paragraphs wa-skeleton:nth-child(4) {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.skeleton-paragraphs wa-skeleton:last-child {
|
||||
width: 50%;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="skeleton-paragraphs">
|
||||
<WaSkeleton />
|
||||
<WaSkeleton />
|
||||
<WaSkeleton />
|
||||
<WaSkeleton />
|
||||
<WaSkeleton />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Avatars
|
||||
|
||||
Set a matching width and height to make a circle, square, or rounded avatar skeleton.
|
||||
|
||||
```html:preview
|
||||
<div class="skeleton-avatars">
|
||||
<wa-skeleton></wa-skeleton>
|
||||
<wa-skeleton></wa-skeleton>
|
||||
<wa-skeleton></wa-skeleton>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.skeleton-avatars wa-skeleton {
|
||||
display: inline-block;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.skeleton-avatars wa-skeleton:nth-child(1) {
|
||||
--border-radius: 0;
|
||||
}
|
||||
|
||||
.skeleton-avatars wa-skeleton:nth-child(2) {
|
||||
--border-radius: var(--wa-corners-s);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaSkeleton from '@shoelace-style/shoelace/dist/react/skeleton';
|
||||
|
||||
const css = `
|
||||
.skeleton-avatars wa-skeleton {
|
||||
display: inline-block;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.skeleton-avatars wa-skeleton:nth-child(1) {
|
||||
--border-radius: 0;
|
||||
}
|
||||
|
||||
.skeleton-avatars wa-skeleton:nth-child(2) {
|
||||
--border-radius: var(--wa-corners-s);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="skeleton-avatars">
|
||||
<WaSkeleton />
|
||||
<WaSkeleton />
|
||||
<WaSkeleton />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Shapes
|
||||
|
||||
Use the `--border-radius` custom property to make circles, squares, and rectangles. For more complex shapes, you can apply `clip-path` to the `indicator` part. [Try Clippy](https://bennettfeely.com/clippy/) if you need help generating custom shapes.
|
||||
|
||||
```html:preview
|
||||
<div class="skeleton-shapes">
|
||||
<wa-skeleton class="square"></wa-skeleton>
|
||||
<wa-skeleton class="circle"></wa-skeleton>
|
||||
<wa-skeleton class="triangle"></wa-skeleton>
|
||||
<wa-skeleton class="cross"></wa-skeleton>
|
||||
<wa-skeleton class="comment"></wa-skeleton>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.skeleton-shapes wa-skeleton {
|
||||
display: inline-flex;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.skeleton-shapes .square::part(indicator) {
|
||||
--border-radius: var(--wa-corners-s);
|
||||
}
|
||||
|
||||
.skeleton-shapes .circle::part(indicator) {
|
||||
--border-radius: var(--wa-corners-circle);
|
||||
}
|
||||
|
||||
.skeleton-shapes .triangle::part(indicator) {
|
||||
--border-radius: 0;
|
||||
clip-path: polygon(50% 0, 0 100%, 100% 100%);
|
||||
}
|
||||
|
||||
.skeleton-shapes .cross::part(indicator) {
|
||||
--border-radius: 0;
|
||||
clip-path: polygon(
|
||||
20% 0%,
|
||||
0% 20%,
|
||||
30% 50%,
|
||||
0% 80%,
|
||||
20% 100%,
|
||||
50% 70%,
|
||||
80% 100%,
|
||||
100% 80%,
|
||||
70% 50%,
|
||||
100% 20%,
|
||||
80% 0%,
|
||||
50% 30%
|
||||
);
|
||||
}
|
||||
|
||||
.skeleton-shapes .comment::part(indicator) {
|
||||
--border-radius: 0;
|
||||
clip-path: polygon(0% 0%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%);
|
||||
}
|
||||
|
||||
.skeleton-shapes wa-skeleton:not(:last-child) {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaSkeleton from '@shoelace-style/shoelace/dist/react/skeleton';
|
||||
|
||||
const css = `
|
||||
.skeleton-shapes wa-skeleton {
|
||||
display: inline-flex;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.skeleton-shapes .square::part(indicator) {
|
||||
--border-radius: var(--wa-corners-s);
|
||||
}
|
||||
|
||||
.skeleton-shapes .circle::part(indicator) {
|
||||
--border-radius: var(--wa-corners-circle);
|
||||
}
|
||||
|
||||
.skeleton-shapes .triangle::part(indicator) {
|
||||
--border-radius: 0;
|
||||
clip-path: polygon(50% 0, 0 100%, 100% 100%);
|
||||
}
|
||||
|
||||
.skeleton-shapes .cross::part(indicator) {
|
||||
--border-radius: 0;
|
||||
clip-path: polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);
|
||||
}
|
||||
|
||||
.skeleton-shapes .comment::part(indicator) {
|
||||
--border-radius: 0;
|
||||
clip-path: polygon(0% 0%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%);
|
||||
}
|
||||
|
||||
.skeleton-shapes wa-skeleton:not(:last-child) {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="skeleton-shapes">
|
||||
<WaSkeleton className="square" />
|
||||
<WaSkeleton className="circle" />
|
||||
<WaSkeleton className="triangle" />
|
||||
<WaSkeleton className="cross" />
|
||||
<WaSkeleton className="comment" />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Colors
|
||||
|
||||
Set the `--color` and `--sheen-color` custom properties to adjust the skeleton's color.
|
||||
|
||||
```html:preview
|
||||
<wa-skeleton effect="sheen" style="--color: tomato; --sheen-color: #ffb094;"></wa-skeleton>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSkeleton from '@shoelace-style/shoelace/dist/react/skeleton';
|
||||
|
||||
const css = `
|
||||
.skeleton-avatars wa-skeleton {
|
||||
display: inline-block;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.skeleton-avatars wa-skeleton:nth-child(1) {
|
||||
--border-radius: 0;
|
||||
}
|
||||
|
||||
.skeleton-avatars wa-skeleton:nth-child(2) {
|
||||
--border-radius: var(--wa-corners-s);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => <WaSkeleton effect="sheen" style={{ '--color': 'tomato', '--sheen-color': '#ffb094' }} />;
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
@@ -1,95 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Spinner
|
||||
description: Spinners are used to show the progress of an indeterminate operation.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-spinner></wa-spinner>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaSpinner from '@shoelace-style/shoelace/dist/react/spinner';
|
||||
|
||||
const App = () => <WaSpinner />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Size
|
||||
|
||||
Spinners are sized based on the current font size. To change their size, set the `font-size` property on the spinner itself or on a parent element as shown below.
|
||||
|
||||
```html:preview
|
||||
<wa-spinner></wa-spinner>
|
||||
<wa-spinner style="font-size: 2rem;"></wa-spinner>
|
||||
<wa-spinner style="font-size: 3rem;"></wa-spinner>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSpinner from '@shoelace-style/shoelace/dist/react/spinner';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaSpinner />
|
||||
<WaSpinner style={{ fontSize: '2rem' }} />
|
||||
<WaSpinner style={{ fontSize: '3rem' }} />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Track Width
|
||||
|
||||
The width of the spinner's track can be changed by setting the `--track-width` custom property.
|
||||
|
||||
```html:preview
|
||||
<wa-spinner style="font-size: 50px; --track-width: 10px;"></wa-spinner>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSpinner from '@shoelace-style/shoelace/dist/react/spinner';
|
||||
|
||||
const App = () => (
|
||||
<WaSpinner
|
||||
style={{
|
||||
fontSize: '3rem',
|
||||
'--track-width': '6px'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Color
|
||||
|
||||
The spinner's colors can be changed by setting the `--indicator-color` and `--track-color` custom properties.
|
||||
|
||||
```html:preview
|
||||
<wa-spinner style="font-size: 3rem; --indicator-color: deeppink; --track-color: pink;"></wa-spinner>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSpinner from '@shoelace-style/shoelace/dist/react/spinner';
|
||||
|
||||
const App = () => (
|
||||
<WaSpinner
|
||||
style={{
|
||||
fontSize: '3rem',
|
||||
'--indicator-color': 'deeppink',
|
||||
'--track-color': 'pink'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
@@ -1,843 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Split Panel
|
||||
description: Split panels display two adjacent panels, allowing the user to reposition them.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-split-panel>
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaSplitPanel>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</WaSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
## Examples
|
||||
|
||||
### Initial Position
|
||||
|
||||
To set the initial position, use the `position` attribute. If no position is provided, it will default to 50% of the available space.
|
||||
|
||||
```html:preview
|
||||
<wa-split-panel position="75">
|
||||
<div
|
||||
slot="start"
|
||||
style="
|
||||
height: 200px;
|
||||
background: var(--wa-color-surface-lowered);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="
|
||||
height: 200px;
|
||||
background: var(--wa-color-surface-lowered);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
```
|
||||
|
||||
### Initial Position in Pixels
|
||||
|
||||
To set the initial position in pixels instead of a percentage, use the `position-in-pixels` attribute.
|
||||
|
||||
```html:preview
|
||||
<wa-split-panel position-in-pixels="150">
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaSplitPanel position="200">
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</WaSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Vertical
|
||||
|
||||
Add the `vertical` attribute to render the split panel in a vertical orientation where the start and end panels are stacked. You also need to set a height when using the vertical orientation.
|
||||
|
||||
```html:preview
|
||||
<wa-split-panel vertical style="height: 400px;">
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 100%; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 100%; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaSplitPanel vertical style={{ height: '400px' }}>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '100%',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '100%',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</WaSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Snapping
|
||||
|
||||
To snap panels at specific positions while dragging, add the `snap` attribute with one or more space-separated values. Values must be in pixels or percentages. For example, to snap the panel at `100px` and `50%`, use `snap="100px 50%"`. You can also customize how close the divider must be before snapping with the `snap-threshold` attribute.
|
||||
|
||||
```html:preview
|
||||
<div class="split-panel-snapping">
|
||||
<wa-split-panel snap="100px 50%">
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
|
||||
<div class="split-panel-snapping-dots"></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.split-panel-snapping {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.split-panel-snapping-dots::before,
|
||||
.split-panel-snapping-dots::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -12px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--wa-color-neutral-spot);
|
||||
transform: translateX(-3px);
|
||||
}
|
||||
|
||||
.split-panel-snapping-dots::before {
|
||||
left: 100px;
|
||||
}
|
||||
|
||||
.split-panel-snapping-dots::after {
|
||||
left: 50%;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel';
|
||||
|
||||
const css = `
|
||||
.split-panel-snapping {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.split-panel-snapping-dots::before,
|
||||
.split-panel-snapping-dots::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -12px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--wa-color-neutral-spot);
|
||||
transform: translateX(-3px);
|
||||
}
|
||||
|
||||
.split-panel-snapping-dots::before {
|
||||
left: 100px;
|
||||
}
|
||||
|
||||
.split-panel-snapping-dots::after {
|
||||
left: 50%;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="split-panel-snapping">
|
||||
<WaSplitPanel snap="100px 50%">
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</WaSplitPanel>
|
||||
|
||||
<div className="split-panel-snapping-dots" />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Disabled
|
||||
|
||||
Add the `disabled` attribute to prevent the divider from being repositioned.
|
||||
|
||||
```html:preview
|
||||
<wa-split-panel disabled>
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaSplitPanel disabled>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</WaSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Setting the Primary Panel
|
||||
|
||||
By default, both panels will grow or shrink proportionally when the host element is resized. If a primary panel is designated, it will maintain its size and the secondary panel will grow or shrink to fit the remaining space. You can set the primary panel to `start` or `end` using the `primary` attribute.
|
||||
|
||||
Try resizing the example below with each option and notice how the panels respond.
|
||||
|
||||
```html:preview
|
||||
<div class="split-panel-primary">
|
||||
<wa-split-panel>
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
|
||||
<wa-select label="Primary Panel" value="" style="max-width: 200px; margin-top: 1rem;">
|
||||
<wa-option value="">None</wa-option>
|
||||
<wa-option value="start">Start</wa-option>
|
||||
<wa-option value="end">End</wa-option>
|
||||
</wa-select>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.split-panel-primary');
|
||||
const splitPanel = container.querySelector('wa-split-panel');
|
||||
const select = container.querySelector('wa-select');
|
||||
|
||||
select.addEventListener('wa-change', () => (splitPanel.primary = select.value));
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel';
|
||||
import WaSelect from '@shoelace-style/shoelace/dist/react/select';
|
||||
import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
|
||||
|
||||
const App = () => {
|
||||
const [primary, setPrimary] = useState('');
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaSplitPanel primary={primary}>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</WaSplitPanel>
|
||||
|
||||
<WaSelect
|
||||
label="Primary Panel"
|
||||
value={primary}
|
||||
style={{ maxWidth: '200px', marginTop: '1rem' }}
|
||||
onWaChange={event => setPrimary(event.target.value)}
|
||||
>
|
||||
<WaMenuItem value="">None</WaMenuItem>
|
||||
<WaMenuItem value="start">Start</WaMenuItem>
|
||||
<WaMenuItem value="end">End</WaMenuItem>
|
||||
</WaSelect>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Min & Max
|
||||
|
||||
To set a minimum or maximum size of the primary panel, use the `--min` and `--max` custom properties. Since the secondary panel is flexible, size constraints can only be applied to the primary panel. If no primary panel is designated, these constraints will be applied to the `start` panel.
|
||||
|
||||
This examples demonstrates how you can ensure both panels are at least 150px using `--min`, `--max`, and the `calc()` function.
|
||||
|
||||
```html:preview
|
||||
<wa-split-panel style="--min: 150px; --max: calc(100% - 150px);">
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaSplitPanel style={{ '--min': '150px', '--max': 'calc(100% - 150px)' }}>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</WaSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Nested Split Panels
|
||||
|
||||
Create complex layouts that can be repositioned independently by nesting split panels.
|
||||
|
||||
```html:preview
|
||||
<wa-split-panel>
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 400px; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div slot="end">
|
||||
<wa-split-panel vertical style="height: 400px;">
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 100%; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden"
|
||||
>
|
||||
Top
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 100%; background: var(--wa-color-surface-lowered); display: flex; align-items: center; justify-content: center; overflow: hidden"
|
||||
>
|
||||
Bottom
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaSplitPanel>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '400px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div slot="end">
|
||||
<WaSplitPanel vertical style={{ height: '400px' }}>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '100%',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '100%',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</WaSplitPanel>
|
||||
</div>
|
||||
</WaSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Customizing the Divider
|
||||
|
||||
You can target the `divider` part to apply CSS properties to the divider. To add a custom handle, slot an icon into the `divider` slot. When customizing the divider, make sure to think about focus styles for keyboard users.
|
||||
|
||||
```html:preview
|
||||
<wa-split-panel style="--divider-width: 20px;">
|
||||
<wa-icon slot="divider" name="grip-vertical" variant="solid"></wa-icon>
|
||||
<div
|
||||
slot="start"
|
||||
style="
|
||||
height: 200px;
|
||||
background: var(--wa-color-surface-lowered);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="
|
||||
height: 200px;
|
||||
background: var(--wa-color-surface-lowered);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const App = () => (
|
||||
<WaSplitPanel style={{ '--divider-width': '20px' }}>
|
||||
<WaIcon slot="divider" name="grip-vertical" variant="solid" />
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</WaSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
Here's a more elaborate example that changes the divider's color and width and adds a styled handle.
|
||||
|
||||
```html:preview
|
||||
<div class="split-panel-divider">
|
||||
<wa-split-panel>
|
||||
<wa-icon slot="divider" name="grip-vertical" variant="solid"></wa-icon>
|
||||
<div
|
||||
slot="start"
|
||||
style="
|
||||
height: 200px;
|
||||
background: var(--wa-color-surface-lowered);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="
|
||||
height: 200px;
|
||||
background: var(--wa-color-surface-lowered);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</wa-split-panel>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.split-panel-divider wa-split-panel {
|
||||
--divider-width: 4px;
|
||||
}
|
||||
|
||||
.split-panel-divider wa-split-panel::part(divider) {
|
||||
background-color: var(--wa-color-red-50);
|
||||
}
|
||||
|
||||
.split-panel-divider wa-icon {
|
||||
position: absolute;
|
||||
border-radius: var(--wa-corners-l);
|
||||
background: var(--wa-color-red-50);
|
||||
color: white;
|
||||
padding: 0.5rem 0.25rem;
|
||||
}
|
||||
|
||||
.split-panel-divider wa-split-panel::part(divider):focus-visible {
|
||||
background-color: var(--wa-color-blue-50);
|
||||
}
|
||||
|
||||
.split-panel-divider wa-split-panel:focus-within wa-icon {
|
||||
background-color: var(--wa-color-blue-50);
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel';
|
||||
import WaIcon from '@shoelace-style/shoelace/dist/react/icon';
|
||||
|
||||
const css = `
|
||||
.split-panel-divider wa-split-panel {
|
||||
--divider-width: 4px;
|
||||
}
|
||||
|
||||
.split-panel-divider wa-split-panel::part(divider) {
|
||||
background-color: var(--wa-color-red-50);
|
||||
}
|
||||
|
||||
.split-panel-divider wa-icon {
|
||||
position: absolute;
|
||||
border-radius: var(--wa-corners-xs);
|
||||
background: var(--wa-color-red-50);
|
||||
color: white;
|
||||
padding: .5rem .25rem;
|
||||
}
|
||||
|
||||
.split-panel-divider wa-split-panel::part(divider):focus-visible {
|
||||
background-color: var(--wa-color-blue-50);
|
||||
}
|
||||
|
||||
.split-panel-divider wa-split-panel:focus-within wa-icon {
|
||||
background-color: var(--wa-color-blue-50);
|
||||
color: white;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="split-panel-divider">
|
||||
<WaSplitPanel>
|
||||
<WaIcon slot="divider" name="grip-vertical" variant="solid" />
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--wa-color-surface-lowered)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</WaSplitPanel>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
@@ -1,102 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Switch
|
||||
description: Switches allow the user to toggle an option on or off.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-switch>Switch</wa-switch>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaSwitch from '@shoelace-style/shoelace/dist/react/switch';
|
||||
|
||||
const App = () => <WaSwitch>Switch</WaSwitch>;
|
||||
```
|
||||
|
||||
:::tip
|
||||
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### Checked
|
||||
|
||||
Use the `checked` attribute to activate the switch.
|
||||
|
||||
```html:preview
|
||||
<wa-switch checked>Checked</wa-switch>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaSwitch from '@shoelace-style/shoelace/dist/react/switch';
|
||||
|
||||
const App = () => <WaSwitch checked>Checked</WaSwitch>;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable the switch.
|
||||
|
||||
```html:preview
|
||||
<wa-switch disabled>Disabled</wa-switch>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaSwitch from '@shoelace-style/shoelace/dist/react/switch';
|
||||
|
||||
const App = () => <WaSwitch disabled>Disabled</WaSwitch>;
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a switch's size.
|
||||
|
||||
```html:preview
|
||||
<wa-switch size="small">Small</wa-switch>
|
||||
<br />
|
||||
<wa-switch size="medium">Medium</wa-switch>
|
||||
<br />
|
||||
<wa-switch size="large">Large</wa-switch>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaSwitch from '@shoelace-style/shoelace/dist/react/switch';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaSwitch size="small">Small</WaSwitch>
|
||||
<br />
|
||||
<WaSwitch size="medium">Medium</WaSwitch>
|
||||
<br />
|
||||
<WaSwitch size="large">Large</WaSwitch>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Styles
|
||||
|
||||
Use the available custom properties to change how the switch is styled.
|
||||
|
||||
```html:preview
|
||||
<wa-switch style="--width: 80px; --height: 40px; --thumb-size: 36px;">Really big</wa-switch>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaSwitch from '@shoelace-style/shoelace/dist/react/switch';
|
||||
|
||||
const App = () => (
|
||||
<WaSwitch
|
||||
style={{
|
||||
'--width': '80px',
|
||||
'--height': '32px',
|
||||
'--thumb-size': '26px'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
@@ -1,458 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Tab Group
|
||||
description: Tab groups organize content into a container that shows one section at a time.
|
||||
layout: component
|
||||
---
|
||||
|
||||
Tab groups make use of [tabs](/components/tab) and [tab panels](/components/tab-panel). Each tab must be slotted into the `nav` slot and its `panel` must refer to a tab panel of the same name.
|
||||
|
||||
```html:preview
|
||||
<wa-tab-group>
|
||||
<wa-tab slot="nav" panel="general">General</wa-tab>
|
||||
<wa-tab slot="nav" panel="custom">Custom</wa-tab>
|
||||
<wa-tab slot="nav" panel="advanced">Advanced</wa-tab>
|
||||
<wa-tab slot="nav" panel="disabled" disabled>Disabled</wa-tab>
|
||||
|
||||
<wa-tab-panel name="general">This is the general tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="custom">This is the custom tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="advanced">This is the advanced tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="disabled">This is a disabled tab panel.</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTab from '@shoelace-style/shoelace/dist/react/tab';
|
||||
import WaTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
|
||||
import WaTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaTabGroup>
|
||||
<WaTab slot="nav" panel="general">
|
||||
General
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</WaTab>
|
||||
|
||||
<WaTabPanel name="general">This is the general tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="custom">This is the custom tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="advanced">This is the advanced tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="disabled">This is a disabled tab panel.</WaTabPanel>
|
||||
</WaTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Tabs on Bottom
|
||||
|
||||
Tabs can be shown on the bottom by setting `placement` to `bottom`.
|
||||
|
||||
```html:preview
|
||||
<wa-tab-group placement="bottom">
|
||||
<wa-tab slot="nav" panel="general">General</wa-tab>
|
||||
<wa-tab slot="nav" panel="custom">Custom</wa-tab>
|
||||
<wa-tab slot="nav" panel="advanced">Advanced</wa-tab>
|
||||
<wa-tab slot="nav" panel="disabled" disabled>Disabled</wa-tab>
|
||||
|
||||
<wa-tab-panel name="general">This is the general tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="custom">This is the custom tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="advanced">This is the advanced tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="disabled">This is a disabled tab panel.</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTab from '@shoelace-style/shoelace/dist/react/tab';
|
||||
import WaTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
|
||||
import WaTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaTabGroup placement="bottom">
|
||||
<WaTab slot="nav" panel="general">
|
||||
General
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</WaTab>
|
||||
|
||||
<WaTabPanel name="general">This is the general tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="custom">This is the custom tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="advanced">This is the advanced tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="disabled">This is a disabled tab panel.</WaTabPanel>
|
||||
</WaTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Tabs on Start
|
||||
|
||||
Tabs can be shown on the starting side by setting `placement` to `start`.
|
||||
|
||||
```html:preview
|
||||
<wa-tab-group placement="start">
|
||||
<wa-tab slot="nav" panel="general">General</wa-tab>
|
||||
<wa-tab slot="nav" panel="custom">Custom</wa-tab>
|
||||
<wa-tab slot="nav" panel="advanced">Advanced</wa-tab>
|
||||
<wa-tab slot="nav" panel="disabled" disabled>Disabled</wa-tab>
|
||||
|
||||
<wa-tab-panel name="general">This is the general tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="custom">This is the custom tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="advanced">This is the advanced tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="disabled">This is a disabled tab panel.</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTab from '@shoelace-style/shoelace/dist/react/tab';
|
||||
import WaTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
|
||||
import WaTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaTabGroup placement="start">
|
||||
<WaTab slot="nav" panel="general">
|
||||
General
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</WaTab>
|
||||
|
||||
<WaTabPanel name="general">This is the general tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="custom">This is the custom tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="advanced">This is the advanced tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="disabled">This is a disabled tab panel.</WaTabPanel>
|
||||
</WaTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Tabs on End
|
||||
|
||||
Tabs can be shown on the ending side by setting `placement` to `end`.
|
||||
|
||||
```html:preview
|
||||
<wa-tab-group placement="end">
|
||||
<wa-tab slot="nav" panel="general">General</wa-tab>
|
||||
<wa-tab slot="nav" panel="custom">Custom</wa-tab>
|
||||
<wa-tab slot="nav" panel="advanced">Advanced</wa-tab>
|
||||
<wa-tab slot="nav" panel="disabled" disabled>Disabled</wa-tab>
|
||||
|
||||
<wa-tab-panel name="general">This is the general tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="custom">This is the custom tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="advanced">This is the advanced tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="disabled">This is a disabled tab panel.</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTab from '@shoelace-style/shoelace/dist/react/tab';
|
||||
import WaTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
|
||||
import WaTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaTabGroup placement="end">
|
||||
<WaTab slot="nav" panel="general">
|
||||
General
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</WaTab>
|
||||
|
||||
<WaTabPanel name="general">This is the general tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="custom">This is the custom tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="advanced">This is the advanced tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="disabled">This is a disabled tab panel.</WaTabPanel>
|
||||
</WaTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Closable Tabs
|
||||
|
||||
Add the `closable` attribute to a tab to show a close button. This example shows how you can dynamically remove tabs from the DOM when the close button is activated.
|
||||
|
||||
```html:preview
|
||||
<wa-tab-group class="tabs-closable">
|
||||
<wa-tab slot="nav" panel="general">General</wa-tab>
|
||||
<wa-tab slot="nav" panel="closable-1" closable>Closable 1</wa-tab>
|
||||
<wa-tab slot="nav" panel="closable-2" closable>Closable 2</wa-tab>
|
||||
<wa-tab slot="nav" panel="closable-3" closable>Closable 3</wa-tab>
|
||||
|
||||
<wa-tab-panel name="general">This is the general tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="closable-1">This is the first closable tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="closable-2">This is the second closable tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="closable-3">This is the third closable tab panel.</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
<script>
|
||||
const tabGroup = document.querySelector('.tabs-closable');
|
||||
|
||||
tabGroup.addEventListener('wa-close', async event => {
|
||||
const tab = event.target;
|
||||
const panel = tabGroup.querySelector(`wa-tab-panel[name="${tab.panel}"]`);
|
||||
|
||||
// Show the previous tab if the tab is currently active
|
||||
if (tab.active) {
|
||||
tabGroup.show(tab.previousElementSibling.panel);
|
||||
}
|
||||
|
||||
// Remove the tab + panel
|
||||
tab.remove();
|
||||
panel.remove();
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTab from '@shoelace-style/shoelace/dist/react/tab';
|
||||
import WaTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
|
||||
import WaTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
|
||||
|
||||
const App = () => {
|
||||
function handleClose(event) {
|
||||
//
|
||||
// This is a crude example that removes the tab and its panel from the DOM.
|
||||
// There are better ways to manage tab creation/removal in React, but that
|
||||
// would significantly complicate the example.
|
||||
//
|
||||
const tab = event.target;
|
||||
const tabGroup = tab.closest('wa-tab-group');
|
||||
const tabPanel = tabGroup.querySelector(`[aria-labelledby="${tab.id}"]`);
|
||||
|
||||
tab.remove();
|
||||
tabPanel.remove();
|
||||
}
|
||||
|
||||
return (
|
||||
<WaTabGroup className="tabs-closable" onWaClose={handleClose}>
|
||||
<WaTab slot="nav" panel="general">
|
||||
General
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="closable-1" closable onWaClose={handleClose}>
|
||||
Closable 1
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="closable-2" closable onWaClose={handleClose}>
|
||||
Closable 2
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="closable-3" closable onWaClose={handleClose}>
|
||||
Closable 3
|
||||
</WaTab>
|
||||
|
||||
<WaTabPanel name="general">This is the general tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="closable-1">This is the first closable tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="closable-2">This is the second closable tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="closable-3">This is the third closable tab panel.</WaTabPanel>
|
||||
</WaTabGroup>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Scrolling Tabs
|
||||
|
||||
When there are more tabs than horizontal space allows, the nav will be scrollable.
|
||||
|
||||
```html:preview
|
||||
<wa-tab-group>
|
||||
<wa-tab slot="nav" panel="tab-1">Tab 1</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-2">Tab 2</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-3">Tab 3</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-4">Tab 4</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-5">Tab 5</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-6">Tab 6</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-7">Tab 7</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-8">Tab 8</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-9">Tab 9</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-10">Tab 10</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-11">Tab 11</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-12">Tab 12</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-13">Tab 13</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-14">Tab 14</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-15">Tab 15</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-16">Tab 16</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-17">Tab 17</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-18">Tab 18</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-19">Tab 19</wa-tab>
|
||||
<wa-tab slot="nav" panel="tab-20">Tab 20</wa-tab>
|
||||
|
||||
<wa-tab-panel name="tab-1">Tab panel 1</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-2">Tab panel 2</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-3">Tab panel 3</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-4">Tab panel 4</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-5">Tab panel 5</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-6">Tab panel 6</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-7">Tab panel 7</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-8">Tab panel 8</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-9">Tab panel 9</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-10">Tab panel 10</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-11">Tab panel 11</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-12">Tab panel 12</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-13">Tab panel 13</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-14">Tab panel 14</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-15">Tab panel 15</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-16">Tab panel 16</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-17">Tab panel 17</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-18">Tab panel 18</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-19">Tab panel 19</wa-tab-panel>
|
||||
<wa-tab-panel name="tab-20">Tab panel 20</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTab from '@shoelace-style/shoelace/dist/react/tab';
|
||||
import WaTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
|
||||
import WaTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaTabGroup>
|
||||
<WaTab slot="nav" panel="tab-1">
|
||||
Tab 1
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-2">
|
||||
Tab 2
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-3">
|
||||
Tab 3
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-4">
|
||||
Tab 4
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-5">
|
||||
Tab 5
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-6">
|
||||
Tab 6
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-7">
|
||||
Tab 7
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-8">
|
||||
Tab 8
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-9">
|
||||
Tab 9
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-10">
|
||||
Tab 10
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-11">
|
||||
Tab 11
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-12">
|
||||
Tab 12
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-13">
|
||||
Tab 13
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-14">
|
||||
Tab 14
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-15">
|
||||
Tab 15
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-16">
|
||||
Tab 16
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-17">
|
||||
Tab 17
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-18">
|
||||
Tab 18
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-19">
|
||||
Tab 19
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="tab-20">
|
||||
Tab 20
|
||||
</WaTab>
|
||||
|
||||
<WaTabPanel name="tab-1">Tab panel 1</WaTabPanel>
|
||||
<WaTabPanel name="tab-2">Tab panel 2</WaTabPanel>
|
||||
<WaTabPanel name="tab-3">Tab panel 3</WaTabPanel>
|
||||
<WaTabPanel name="tab-4">Tab panel 4</WaTabPanel>
|
||||
<WaTabPanel name="tab-5">Tab panel 5</WaTabPanel>
|
||||
<WaTabPanel name="tab-6">Tab panel 6</WaTabPanel>
|
||||
<WaTabPanel name="tab-7">Tab panel 7</WaTabPanel>
|
||||
<WaTabPanel name="tab-8">Tab panel 8</WaTabPanel>
|
||||
<WaTabPanel name="tab-9">Tab panel 9</WaTabPanel>
|
||||
<WaTabPanel name="tab-10">Tab panel 10</WaTabPanel>
|
||||
<WaTabPanel name="tab-11">Tab panel 11</WaTabPanel>
|
||||
<WaTabPanel name="tab-12">Tab panel 12</WaTabPanel>
|
||||
<WaTabPanel name="tab-13">Tab panel 13</WaTabPanel>
|
||||
<WaTabPanel name="tab-14">Tab panel 14</WaTabPanel>
|
||||
<WaTabPanel name="tab-15">Tab panel 15</WaTabPanel>
|
||||
<WaTabPanel name="tab-16">Tab panel 16</WaTabPanel>
|
||||
<WaTabPanel name="tab-17">Tab panel 17</WaTabPanel>
|
||||
<WaTabPanel name="tab-18">Tab panel 18</WaTabPanel>
|
||||
<WaTabPanel name="tab-19">Tab panel 19</WaTabPanel>
|
||||
<WaTabPanel name="tab-20">Tab panel 20</WaTabPanel>
|
||||
</WaTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Manual Activation
|
||||
|
||||
When focused, keyboard users can press [[Left]] or [[Right]] to select the desired tab. By default, the corresponding tab panel will be shown immediately (automatic activation). You can change this behavior by setting `activation="manual"` which will require the user to press [[Space]] or [[Enter]] before showing the tab panel (manual activation).
|
||||
|
||||
```html:preview
|
||||
<wa-tab-group activation="manual">
|
||||
<wa-tab slot="nav" panel="general">General</wa-tab>
|
||||
<wa-tab slot="nav" panel="custom">Custom</wa-tab>
|
||||
<wa-tab slot="nav" panel="advanced">Advanced</wa-tab>
|
||||
<wa-tab slot="nav" panel="disabled" disabled>Disabled</wa-tab>
|
||||
|
||||
<wa-tab-panel name="general">This is the general tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="custom">This is the custom tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="advanced">This is the advanced tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="disabled">This is a disabled tab panel.</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTab from '@shoelace-style/shoelace/dist/react/tab';
|
||||
import WaTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
|
||||
import WaTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaTabGroup activation="manual">
|
||||
<WaTab slot="nav" panel="general">
|
||||
General
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</WaTab>
|
||||
|
||||
<WaTabPanel name="general">This is the general tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="custom">This is the custom tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="advanced">This is the advanced tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="disabled">This is a disabled tab panel.</WaTabPanel>
|
||||
</WaTabGroup>
|
||||
);
|
||||
```
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Tab Panel
|
||||
description: Tab panels are used inside tab groups to display tabbed content.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-tab-group>
|
||||
<wa-tab slot="nav" panel="general">General</wa-tab>
|
||||
<wa-tab slot="nav" panel="custom">Custom</wa-tab>
|
||||
<wa-tab slot="nav" panel="advanced">Advanced</wa-tab>
|
||||
<wa-tab slot="nav" panel="disabled" disabled>Disabled</wa-tab>
|
||||
|
||||
<wa-tab-panel name="general">This is the general tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="custom">This is the custom tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="advanced">This is the advanced tab panel.</wa-tab-panel>
|
||||
<wa-tab-panel name="disabled">This is a disabled tab panel.</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTab from '@shoelace-style/shoelace/dist/react/tab';
|
||||
import WaTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
|
||||
import WaTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
|
||||
|
||||
const App = () => (
|
||||
<WaTabGroup>
|
||||
<WaTab slot="nav" panel="general">
|
||||
General
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</WaTab>
|
||||
<WaTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</WaTab>
|
||||
|
||||
<WaTabPanel name="general">This is the general tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="custom">This is the custom tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="advanced">This is the advanced tab panel.</WaTabPanel>
|
||||
<WaTabPanel name="disabled">This is a disabled tab panel.</WaTabPanel>
|
||||
</WaTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
:::tip
|
||||
Additional demonstrations can be found in the [tab group examples](/components/tab-group).
|
||||
:::
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Tab
|
||||
description: Tabs are used inside tab groups to represent and activate tab panels.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-tab>Tab</wa-tab>
|
||||
<wa-tab active>Active</wa-tab>
|
||||
<wa-tab closable>Closable</wa-tab>
|
||||
<wa-tab disabled>Disabled</wa-tab>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTab from '@shoelace-style/shoelace/dist/react/tab';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaTab>Tab</WaTab>
|
||||
<WaTab active>Active</WaTab>
|
||||
<WaTab closable>Closable</WaTab>
|
||||
<WaTab disabled>Disabled</WaTab>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
:::tip
|
||||
Additional demonstrations can be found in the [tab group examples](/components/tab-group).
|
||||
:::
|
||||
@@ -1,146 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Tag
|
||||
description: Tags are used as labels to organize things or to indicate a selection.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-tag variant="brand">Brand</wa-tag>
|
||||
<wa-tag variant="success">Success</wa-tag>
|
||||
<wa-tag variant="neutral">Neutral</wa-tag>
|
||||
<wa-tag variant="warning">Warning</wa-tag>
|
||||
<wa-tag variant="danger">Danger</wa-tag>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTag from '@shoelace-style/shoelace/dist/react/tag';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaTag variant="brand">Brand</WaTag>
|
||||
<WaTag variant="success">Success</WaTag>
|
||||
<WaTag variant="neutral">Neutral</WaTag>
|
||||
<WaTag variant="warning">Warning</WaTag>
|
||||
<WaTag variant="danger">Danger</WaTag>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a tab's size.
|
||||
|
||||
```html:preview
|
||||
<wa-tag size="small">Small</wa-tag>
|
||||
<wa-tag size="medium">Medium</wa-tag>
|
||||
<wa-tag size="large">Large</wa-tag>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTag from '@shoelace-style/shoelace/dist/react/tag';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaTag size="small">Small</WaTag>
|
||||
<WaTag size="medium">Medium</WaTag>
|
||||
<WaTag size="large">Large</WaTag>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill
|
||||
|
||||
Use the `pill` attribute to give tabs rounded edges.
|
||||
|
||||
```html:preview
|
||||
<wa-tag size="small" pill>Small</wa-tag>
|
||||
<wa-tag size="medium" pill>Medium</wa-tag>
|
||||
<wa-tag size="large" pill>Large</wa-tag>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTag from '@shoelace-style/shoelace/dist/react/tag';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaTag size="small" pill>
|
||||
Small
|
||||
</WaTag>
|
||||
<WaTag size="medium" pill>
|
||||
Medium
|
||||
</WaTag>
|
||||
<WaTag size="large" pill>
|
||||
Large
|
||||
</WaTag>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Removable
|
||||
|
||||
Use the `removable` attribute to add a remove button to the tag.
|
||||
|
||||
```html:preview
|
||||
<div class="tags-removable">
|
||||
<wa-tag size="small" removable>Small</wa-tag>
|
||||
<wa-tag size="medium" removable>Medium</wa-tag>
|
||||
<wa-tag size="large" removable>Large</wa-tag>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const div = document.querySelector('.tags-removable');
|
||||
|
||||
div.addEventListener('wa-remove', event => {
|
||||
const tag = event.target;
|
||||
tag.style.opacity = '0';
|
||||
setTimeout(() => (tag.style.opacity = '1'), 2000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.tags-removable wa-tag {
|
||||
transition: var(--wa-transition-fast) opacity;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTag from '@shoelace-style/shoelace/dist/react/tag';
|
||||
|
||||
const css = `
|
||||
.tags-removable wa-tag {
|
||||
transition: var(--wa-transition-fast) opacity;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => {
|
||||
function handleRemove(event) {
|
||||
const tag = event.target;
|
||||
tag.style.opacity = '0';
|
||||
setTimeout(() => (tag.style.opacity = '1'), 2000);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="tags-removable">
|
||||
<WaTag size="small" removable onWaRemove={handleRemove}>
|
||||
Small
|
||||
</WaTag>
|
||||
|
||||
<WaTag size="medium" removable onWaRemove={handleRemove}>
|
||||
Medium
|
||||
</WaTag>
|
||||
|
||||
<WaTag size="large" removable onWaRemove={handleRemove}>
|
||||
Large
|
||||
</WaTag>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
@@ -1,160 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Textarea
|
||||
description: Textareas collect data from the user and allow multiple lines of text.
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-textarea></wa-textarea>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTextarea from '@shoelace-style/shoelace/dist/react/textarea';
|
||||
|
||||
const App = () => <WaTextarea />;
|
||||
```
|
||||
|
||||
:::tip
|
||||
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### Labels
|
||||
|
||||
Use the `label` attribute to give the textarea an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
|
||||
```html:preview
|
||||
<wa-textarea label="Comments"></wa-textarea>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTextarea from '@shoelace-style/shoelace/dist/react/textarea';
|
||||
|
||||
const App = () => <WaTextarea label="Comments" />;
|
||||
```
|
||||
|
||||
### Help Text
|
||||
|
||||
Add descriptive help text to a textarea with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
|
||||
|
||||
```html:preview
|
||||
<wa-textarea label="Feedback" help-text="Please tell us what you think."> </wa-textarea>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTextarea from '@shoelace-style/shoelace/dist/react/textarea';
|
||||
|
||||
const App = () => <WaTextarea label="Feedback" help-text="Please tell us what you think." />;
|
||||
```
|
||||
|
||||
### Rows
|
||||
|
||||
Use the `rows` attribute to change the number of text rows that get shown.
|
||||
|
||||
```html:preview
|
||||
<wa-textarea rows="2"></wa-textarea>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTextarea from '@shoelace-style/shoelace/dist/react/textarea';
|
||||
|
||||
const App = () => <WaTextarea rows={2} />;
|
||||
```
|
||||
|
||||
### Placeholders
|
||||
|
||||
Use the `placeholder` attribute to add a placeholder.
|
||||
|
||||
```html:preview
|
||||
<wa-textarea placeholder="Type something"></wa-textarea>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTextarea from '@shoelace-style/shoelace/dist/react/textarea';
|
||||
|
||||
const App = () => <WaTextarea placeholder="Type something" />;
|
||||
```
|
||||
|
||||
### Filled Textareas
|
||||
|
||||
Add the `filled` attribute to draw a filled textarea.
|
||||
|
||||
```html:preview
|
||||
<wa-textarea placeholder="Type something" filled></wa-textarea>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTextarea from '@shoelace-style/shoelace/dist/react/textarea';
|
||||
|
||||
const App = () => <WaTextarea placeholder="Type something" filled />;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable a textarea.
|
||||
|
||||
```html:preview
|
||||
<wa-textarea placeholder="Textarea" disabled></wa-textarea>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTextarea from '@shoelace-style/shoelace/dist/react/textarea';
|
||||
|
||||
const App = () => <WaTextarea placeholder="Textarea" disabled />;
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a textarea's size.
|
||||
|
||||
```html:preview
|
||||
<wa-textarea placeholder="Small" size="small"></wa-textarea>
|
||||
<br />
|
||||
<wa-textarea placeholder="Medium" size="medium"></wa-textarea>
|
||||
<br />
|
||||
<wa-textarea placeholder="Large" size="large"></wa-textarea>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTextarea from '@shoelace-style/shoelace/dist/react/textarea';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaTextarea placeholder="Small" size="small"></WaTextarea>
|
||||
<br />
|
||||
<WaTextarea placeholder="Medium" size="medium"></WaTextarea>
|
||||
<br />
|
||||
<WaTextarea placeholder="Large" size="large"></WaTextarea>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Prevent Resizing
|
||||
|
||||
By default, textareas can be resized vertically by the user. To prevent resizing, set the `resize` attribute to `none`.
|
||||
|
||||
```html:preview
|
||||
<wa-textarea resize="none"></wa-textarea>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTextarea from '@shoelace-style/shoelace/dist/react/textarea';
|
||||
|
||||
const App = () => <WaTextarea resize="none" />;
|
||||
```
|
||||
|
||||
### Expand with Content
|
||||
|
||||
Textareas will automatically resize to expand to fit their content when `resize` is set to `auto`.
|
||||
|
||||
```html:preview
|
||||
<wa-textarea resize="auto"></wa-textarea>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaTextarea from '@shoelace-style/shoelace/dist/react/textarea';
|
||||
|
||||
const App = () => <WaTextarea resize="auto" />;
|
||||
```
|
||||
@@ -1,440 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
title: Tooltip
|
||||
description: Tooltips display additional information based on a specific action.
|
||||
layout: component
|
||||
---
|
||||
|
||||
A tooltip's target is its _first child element_, so you should only wrap one element inside of the tooltip. If you need the tooltip to show up for multiple elements, nest them inside a container first.
|
||||
|
||||
Tooltips use `display: contents` so they won't interfere with how elements are positioned in a flex or grid layout.
|
||||
|
||||
```html:preview
|
||||
<wa-tooltip content="This is a tooltip">
|
||||
<wa-button>Hover Me</wa-button>
|
||||
</wa-tooltip>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
|
||||
|
||||
const App = () => (
|
||||
<WaTooltip content="This is a tooltip">
|
||||
<WaButton>Hover Me</WaButton>
|
||||
</WaTooltip>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Placement
|
||||
|
||||
Use the `placement` attribute to set the preferred placement of the tooltip.
|
||||
|
||||
```html:preview
|
||||
<div class="tooltip-placement-example">
|
||||
<div class="tooltip-placement-example-row">
|
||||
<wa-tooltip content="top-start" placement="top-start">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
|
||||
<wa-tooltip content="top" placement="top">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
|
||||
<wa-tooltip content="top-end" placement="top-end">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="tooltip-placement-example-row">
|
||||
<wa-tooltip content="left-start" placement="left-start">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
|
||||
<wa-tooltip content="right-start" placement="right-start">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="tooltip-placement-example-row">
|
||||
<wa-tooltip content="left" placement="left">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
|
||||
<wa-tooltip content="right" placement="right">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="tooltip-placement-example-row">
|
||||
<wa-tooltip content="left-end" placement="left-end">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
|
||||
<wa-tooltip content="right-end" placement="right-end">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="tooltip-placement-example-row">
|
||||
<wa-tooltip content="bottom-start" placement="bottom-start">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
|
||||
<wa-tooltip content="bottom" placement="bottom">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
|
||||
<wa-tooltip content="bottom-end" placement="bottom-end">
|
||||
<wa-button></wa-button>
|
||||
</wa-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.tooltip-placement-example {
|
||||
width: 250px;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.tooltip-placement-example-row:after {
|
||||
content: '';
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.tooltip-placement-example wa-button {
|
||||
float: left;
|
||||
width: 2.5rem;
|
||||
margin-right: 0.25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.tooltip-placement-example-row:nth-child(1) wa-tooltip:first-child wa-button,
|
||||
.tooltip-placement-example-row:nth-child(5) wa-tooltip:first-child wa-button {
|
||||
margin-left: calc(40px + 0.25rem);
|
||||
}
|
||||
|
||||
.tooltip-placement-example-row:nth-child(2) wa-tooltip:nth-child(2) wa-button,
|
||||
.tooltip-placement-example-row:nth-child(3) wa-tooltip:nth-child(2) wa-button,
|
||||
.tooltip-placement-example-row:nth-child(4) wa-tooltip:nth-child(2) wa-button {
|
||||
margin-left: calc((40px * 3) + (0.25rem * 3));
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
|
||||
|
||||
const css = `
|
||||
.tooltip-placement-example {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.tooltip-placement-example-row:after {
|
||||
content: '';
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.tooltip-placement-example wa-button {
|
||||
float: left;
|
||||
width: 2.5rem;
|
||||
margin-right: 0.25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.tooltip-placement-example-row:nth-child(1) wa-tooltip:first-child wa-button,
|
||||
.tooltip-placement-example-row:nth-child(5) wa-tooltip:first-child wa-button {
|
||||
margin-left: calc(40px + 0.25rem);
|
||||
}
|
||||
|
||||
.tooltip-placement-example-row:nth-child(2) wa-tooltip:nth-child(2) wa-button,
|
||||
.tooltip-placement-example-row:nth-child(3) wa-tooltip:nth-child(2) wa-button,
|
||||
.tooltip-placement-example-row:nth-child(4) wa-tooltip:nth-child(2) wa-button {
|
||||
margin-left: calc((40px * 3) + (0.25rem * 3));
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="tooltip-placement-example">
|
||||
<div className="tooltip-placement-example-row">
|
||||
<WaTooltip content="top-start" placement="top-start">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
|
||||
<WaTooltip content="top" placement="top">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
|
||||
<WaTooltip content="top-end" placement="top-end">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
</div>
|
||||
|
||||
<div className="tooltip-placement-example-row">
|
||||
<WaTooltip content="left-start" placement="left-start">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
|
||||
<WaTooltip content="right-start" placement="right-start">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
</div>
|
||||
|
||||
<div className="tooltip-placement-example-row">
|
||||
<WaTooltip content="left" placement="left">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
|
||||
<WaTooltip content="right" placement="right">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
</div>
|
||||
|
||||
<div className="tooltip-placement-example-row">
|
||||
<WaTooltip content="left-end" placement="left-end">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
|
||||
<WaTooltip content="right-end" placement="right-end">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
</div>
|
||||
|
||||
<div className="tooltip-placement-example-row">
|
||||
<WaTooltip content="bottom-start" placement="bottom-start">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
|
||||
<WaTooltip content="bottom" placement="bottom">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
|
||||
<WaTooltip content="bottom-end" placement="bottom-end">
|
||||
<WaButton />
|
||||
</WaTooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Click Trigger
|
||||
|
||||
Set the `trigger` attribute to `click` to toggle the tooltip on click instead of hover.
|
||||
|
||||
```html:preview
|
||||
<wa-tooltip content="Click again to dismiss" trigger="click">
|
||||
<wa-button>Click to Toggle</wa-button>
|
||||
</wa-tooltip>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
|
||||
|
||||
const App = () => (
|
||||
<WaTooltip content="Click again to dismiss" trigger="click">
|
||||
<WaButton>Click to Toggle</WaButton>
|
||||
</WaTooltip>
|
||||
);
|
||||
```
|
||||
|
||||
### Manual Trigger
|
||||
|
||||
Tooltips can be controller programmatically by setting the `trigger` attribute to `manual`. Use the `open` attribute to control when the tooltip is shown.
|
||||
|
||||
```html:preview
|
||||
<wa-button style="margin-right: 4rem;">Toggle Manually</wa-button>
|
||||
|
||||
<wa-tooltip content="This is an avatar" trigger="manual" class="manual-tooltip">
|
||||
<wa-avatar label="User"></wa-avatar>
|
||||
</wa-tooltip>
|
||||
|
||||
<script>
|
||||
const tooltip = document.querySelector('.manual-tooltip');
|
||||
const toggle = tooltip.previousElementSibling;
|
||||
|
||||
toggle.addEventListener('click', () => (tooltip.open = !tooltip.open));
|
||||
</script>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaAvatar from '@shoelace-style/shoelace/dist/react/avatar';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaButton style={{ marginRight: '4rem' }} onClick={() => setOpen(!open)}>
|
||||
Toggle Manually
|
||||
</WaButton>
|
||||
|
||||
<WaTooltip open={open} content="This is an avatar" trigger="manual">
|
||||
<WaAvatar />
|
||||
</WaTooltip>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Removing Arrows
|
||||
|
||||
You can control the size of tooltip arrows by overriding the `--wa-tooltip-arrow-size` design token. To remove them, set the value to `0` as shown below.
|
||||
|
||||
```html:preview
|
||||
<wa-tooltip content="This is a tooltip" style="--wa-tooltip-arrow-size: 0;">
|
||||
<wa-button>No Arrow</wa-button>
|
||||
</wa-tooltip>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
|
||||
|
||||
const App = () => (
|
||||
<div style={{ '--wa-tooltip-arrow-size': '0' }}>
|
||||
<WaTooltip content="This is a tooltip">
|
||||
<WaButton>Above</WaButton>
|
||||
</WaTooltip>
|
||||
|
||||
<WaTooltip content="This is a tooltip" placement="bottom">
|
||||
<WaButton>Below</WaButton>
|
||||
</WaTooltip>
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
To override it globally, set it in a root block in your stylesheet after the Web Awesome stylesheet is loaded.
|
||||
|
||||
```css
|
||||
:root {
|
||||
--wa-tooltip-arrow-size: 0;
|
||||
}
|
||||
```
|
||||
|
||||
### HTML in Tooltips
|
||||
|
||||
Use the `content` slot to create tooltips with HTML content. Tooltips are designed only for text and presentational elements. Avoid placing interactive content, such as buttons, links, and form controls, in a tooltip.
|
||||
|
||||
```html:preview
|
||||
<wa-tooltip>
|
||||
<div slot="content">I'm not <strong>just</strong> a tooltip, I'm a <em>tooltip</em> with HTML!</div>
|
||||
|
||||
<wa-button>Hover me</wa-button>
|
||||
</wa-tooltip>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
|
||||
|
||||
const App = () => (
|
||||
<WaTooltip>
|
||||
<div slot="content">
|
||||
I'm not <strong>just</strong> a tooltip, I'm a <em>tooltip</em> with HTML!
|
||||
</div>
|
||||
|
||||
<WaButton>Hover Me</WaButton>
|
||||
</WaTooltip>
|
||||
);
|
||||
```
|
||||
|
||||
### Setting a Maximum Width
|
||||
|
||||
Use the `--max-width` custom property to change the width the tooltip can grow to before wrapping occurs.
|
||||
|
||||
```html:preview
|
||||
<wa-tooltip style="--max-width: 80px;" content="This tooltip will wrap after only 80 pixels.">
|
||||
<wa-button>Hover me</wa-button>
|
||||
</wa-tooltip>
|
||||
```
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
|
||||
|
||||
const App = () => (
|
||||
<WaTooltip style={{ '--max-width': '80px' }} content="This tooltip will wrap after only 80 pixels.">
|
||||
<WaButton>Hover Me</WaButton>
|
||||
</WaTooltip>
|
||||
);
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Hoisting
|
||||
|
||||
Tooltips will be clipped if they're inside a container that has `overflow: auto|hidden|scroll`. The `hoist` attribute forces the tooltip to use a fixed positioning strategy, allowing it to break out of the container. In this case, the tooltip will be positioned relative to its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
|
||||
|
||||
```html:preview
|
||||
<div class="tooltip-hoist">
|
||||
<wa-tooltip content="This is a tooltip">
|
||||
<wa-button>No Hoist</wa-button>
|
||||
</wa-tooltip>
|
||||
|
||||
<wa-tooltip content="This is a tooltip" hoist>
|
||||
<wa-button>Hoist</wa-button>
|
||||
</wa-tooltip>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.tooltip-hoist {
|
||||
position: relative;
|
||||
border: solid 2px var(--wa-color-surface-border);
|
||||
overflow: hidden;
|
||||
padding: var(--wa-space-m);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
|
||||
|
||||
const css = `
|
||||
.tooltip-hoist {
|
||||
border: solid 2px var(--wa-color-surface-border);
|
||||
overflow: hidden;
|
||||
padding: var(--wa-space-m);
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div class="tooltip-hoist">
|
||||
<WaTooltip content="This is a tooltip">
|
||||
<WaButton>No Hoist</WaButton>
|
||||
</WaTooltip>
|
||||
|
||||
<WaTooltip content="This is a tooltip" hoist>
|
||||
<WaButton>Hoist</WaButton>
|
||||
</WaTooltip>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||