Compare commits
1693 Commits
v2.0.0-bet
...
v2.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1ec60af62 | ||
|
|
dcbcc4c050 | ||
|
|
0eb3375bb9 | ||
|
|
c26a8810c8 | ||
|
|
872227e345 | ||
|
|
f22c529eab | ||
|
|
3430b33c3e | ||
|
|
1bc2a6ef76 | ||
|
|
5a94f5bf5b | ||
|
|
4277377189 | ||
|
|
f0efb9253c | ||
|
|
cfd28f2608 | ||
|
|
a3844fe074 | ||
|
|
65e90f12f4 | ||
|
|
4335289d6a | ||
|
|
86cc721e03 | ||
|
|
4a28825ea7 | ||
|
|
19cf823da5 | ||
|
|
737b55d78d | ||
|
|
8493131db5 | ||
|
|
0d86c2af37 | ||
|
|
b260a4dc29 | ||
|
|
1f1024f4ca | ||
|
|
9e92d92684 | ||
|
|
527bf79973 | ||
|
|
b281c5bbc1 | ||
|
|
f03de8925b | ||
|
|
776ab2c715 | ||
|
|
df967b7e84 | ||
|
|
a539058253 | ||
|
|
af70d88153 | ||
|
|
8dcffe270f | ||
|
|
c958f2e50a | ||
|
|
cedcd65c72 | ||
|
|
12f62075ad | ||
|
|
b8695b70a9 | ||
|
|
a4e371618a | ||
|
|
039ab175c3 | ||
|
|
7549e50fe4 | ||
|
|
3c2cda699e | ||
|
|
8685ddd049 | ||
|
|
c47ad40802 | ||
|
|
ef1f129b22 | ||
|
|
6bb508ef14 | ||
|
|
3596c8144d | ||
|
|
20903bb638 | ||
|
|
f45fb6848f | ||
|
|
400f9b76d5 | ||
|
|
38a9e98d9b | ||
|
|
e8fe783fb4 | ||
|
|
223ef32b70 | ||
|
|
0793a219a2 | ||
|
|
3bb92c095f | ||
|
|
deec097267 | ||
|
|
873e280700 | ||
|
|
5d047f7a93 | ||
|
|
f24ab23752 | ||
|
|
44ecc8ce56 | ||
|
|
e758b1d9bb | ||
|
|
5cdbaa873d | ||
|
|
e9aca6cedb | ||
|
|
7c3896ed42 | ||
|
|
93158e8e90 | ||
|
|
5f9bbdfa06 | ||
|
|
3a0f486e98 | ||
|
|
29c671c0f4 | ||
|
|
88c4bef5e7 | ||
|
|
6066bc468b | ||
|
|
efd944d822 | ||
|
|
4a3f2caf59 | ||
|
|
511182b41b | ||
|
|
1088a51ed5 | ||
|
|
e3e0842bdd | ||
|
|
e4c908b08b | ||
|
|
f86578a213 | ||
|
|
3c2f5ec48e | ||
|
|
fec7ef17aa | ||
|
|
29ff99dd76 | ||
|
|
6b9b410bdc | ||
|
|
b45a9d55ca | ||
|
|
7ce079b7a1 | ||
|
|
b0ba9ff14f | ||
|
|
f665bf984b | ||
|
|
ac429a62c0 | ||
|
|
dc909d10b6 | ||
|
|
aa65077b12 | ||
|
|
7e37c51856 | ||
|
|
6e26daf804 | ||
|
|
25c2d2d5bf | ||
|
|
edc9e69f30 | ||
|
|
2e7ac38678 | ||
|
|
1a68c825c0 | ||
|
|
2cbdeeade0 | ||
|
|
79624f63ed | ||
|
|
01a8ec36ec | ||
|
|
60324885ed | ||
|
|
0c2f43b837 | ||
|
|
0df27cf730 | ||
|
|
68ed69292c | ||
|
|
62c58b3a8c | ||
|
|
1fbb809057 | ||
|
|
e2d2f5d670 | ||
|
|
acef0da2c1 | ||
|
|
3c66d2ab99 | ||
|
|
31f16c4680 | ||
|
|
8056379fdd | ||
|
|
9a6b9a7841 | ||
|
|
02fc39ebe0 | ||
|
|
a90b22c05d | ||
|
|
f5dd4f2aca | ||
|
|
d5b3489b22 | ||
|
|
8ee5f19184 | ||
|
|
d0a32d48b1 | ||
|
|
bf527437a0 | ||
|
|
ae3070ac45 | ||
|
|
6af68343a7 | ||
|
|
e632b51eb8 | ||
|
|
c8e633c4a1 | ||
|
|
724f4a59db | ||
|
|
041364fb7d | ||
|
|
fbcb4d8dbd | ||
|
|
27a6b4a8c9 | ||
|
|
c814e9e94e | ||
|
|
0e957c0cd4 | ||
|
|
b330657e0a | ||
|
|
a0fce64fd9 | ||
|
|
b183a04fba | ||
|
|
7645b997b2 | ||
|
|
d81e2f1470 | ||
|
|
f50fe72df2 | ||
|
|
dcca64a986 | ||
|
|
c216cfe0fd | ||
|
|
192f15e3b7 | ||
|
|
01be3daf6d | ||
|
|
d36eec5637 | ||
|
|
121464fa2d | ||
|
|
ee0254e822 | ||
|
|
27f634402c | ||
|
|
67fbe3b34e | ||
|
|
164ebce990 | ||
|
|
571ae704e0 | ||
|
|
ad305fb653 | ||
|
|
fc0541ce53 | ||
|
|
c8555f448c | ||
|
|
96e41198ec | ||
|
|
57064aef4d | ||
|
|
cf200aa58a | ||
|
|
388a4f85a4 | ||
|
|
5f8556b1b2 | ||
|
|
e411b57124 | ||
|
|
0120e7429d | ||
|
|
377dbe28eb | ||
|
|
b25b1d5750 | ||
|
|
0e1b792bf7 | ||
|
|
417f0d17c9 | ||
|
|
d9252fe755 | ||
|
|
c5555ab5fe | ||
|
|
eb61dc7d91 | ||
|
|
a4c522f090 | ||
|
|
a8fe8c3e71 | ||
|
|
87000306c0 | ||
|
|
ae07b7d0a8 | ||
|
|
626b76610f | ||
|
|
b4a1e1b0c9 | ||
|
|
913243f8c1 | ||
|
|
563ed81984 | ||
|
|
fcbf339a86 | ||
|
|
70585e1d2a | ||
|
|
479e568296 | ||
|
|
a473e41ab3 | ||
|
|
92f6a2d8e9 | ||
|
|
06dc5740bf | ||
|
|
fe524e0fac | ||
|
|
ea7de2eb70 | ||
|
|
b8d02537a6 | ||
|
|
24744ef8c5 | ||
|
|
69997466be | ||
|
|
f7d7fdf5b1 | ||
|
|
c0013c5639 | ||
|
|
935040204f | ||
|
|
2ffbf9b017 | ||
|
|
0f67b9a9d1 | ||
|
|
c5dee51233 | ||
|
|
b07238d536 | ||
|
|
e2a65c28f4 | ||
|
|
1f457cdde0 | ||
|
|
46fda5f0a6 | ||
|
|
e22c2f839b | ||
|
|
5bff912162 | ||
|
|
f3010cecbe | ||
|
|
2dc275defd | ||
|
|
9f79445292 | ||
|
|
10cb26b81e | ||
|
|
3722e0ad91 | ||
|
|
f28a0ec743 | ||
|
|
a42b393bf1 | ||
|
|
41f50777bd | ||
|
|
6afc3ba12e | ||
|
|
d8b7040a9e | ||
|
|
59cd70ae6f | ||
|
|
6d9c8561fb | ||
|
|
edb8a92838 | ||
|
|
da3ffe9a60 | ||
|
|
e2b791dee8 | ||
|
|
9178be576b | ||
|
|
752f5cff55 | ||
|
|
3675787ddd | ||
|
|
6284ed0347 | ||
|
|
1ff05c4b3d | ||
|
|
066abe4e52 | ||
|
|
e9134ddcf6 | ||
|
|
0237851aa1 | ||
|
|
1841251fdf | ||
|
|
b1e48406f3 | ||
|
|
4ca51cf11e | ||
|
|
41bd61c3a5 | ||
|
|
c6df057e15 | ||
|
|
1428481a5c | ||
|
|
1c359fbea9 | ||
|
|
0329694760 | ||
|
|
936ec5a23a | ||
|
|
d291506284 | ||
|
|
3877351fdc | ||
|
|
7885572ebd | ||
|
|
2ee926023c | ||
|
|
69e557cd8c | ||
|
|
f2efa73e20 | ||
|
|
2dd57956d5 | ||
|
|
ce86a1c9f2 | ||
|
|
a0f83c3b2b | ||
|
|
80a16ee42a | ||
|
|
31b05fedd3 | ||
|
|
d3a7950483 | ||
|
|
f299621490 | ||
|
|
c62ee3e207 | ||
|
|
a313087937 | ||
|
|
3945571b20 | ||
|
|
c6a044416a | ||
|
|
d2f1b50ad0 | ||
|
|
1f1bae77dd | ||
|
|
829e22dc1e | ||
|
|
8b28ee818f | ||
|
|
afb2e3d5b4 | ||
|
|
fb0adb9ccb | ||
|
|
efea514f5a | ||
|
|
fda9bd52a3 | ||
|
|
01f8ce6b03 | ||
|
|
e10651565f | ||
|
|
35aa56d334 | ||
|
|
31e1f2fc59 | ||
|
|
ee30f7a10b | ||
|
|
a50909d474 | ||
|
|
63115d51e5 | ||
|
|
026036a14b | ||
|
|
22e09a778b | ||
|
|
488088d5f0 | ||
|
|
0c18880e5c | ||
|
|
0b0a571d17 | ||
|
|
ea9e596279 | ||
|
|
fe17c8406b | ||
|
|
e7a4a5135d | ||
|
|
ebf12860be | ||
|
|
4b81778e46 | ||
|
|
28a8a90bb1 | ||
|
|
8dab5a8f04 | ||
|
|
4ac9483213 | ||
|
|
ae3c0d72c0 | ||
|
|
83b73e823d | ||
|
|
da6ae608f1 | ||
|
|
1e39647d7f | ||
|
|
4dc92247d5 | ||
|
|
1b6f982d68 | ||
|
|
81c775ea87 | ||
|
|
09f741e930 | ||
|
|
602a863d89 | ||
|
|
3dd19a7f62 | ||
|
|
bdf890ab0d | ||
|
|
0b7eae0c20 | ||
|
|
f3e273744a | ||
|
|
7f9b0bd5a2 | ||
|
|
6579a95999 | ||
|
|
aaa8e4a01b | ||
|
|
200d4385ec | ||
|
|
50d983a1ab | ||
|
|
1e6d295746 | ||
|
|
5d128c154c | ||
|
|
6c7d7f4b7e | ||
|
|
0cc8fdb8f8 | ||
|
|
4f7d915853 | ||
|
|
61738a3f6a | ||
|
|
9061c1987a | ||
|
|
c40b3a86a9 | ||
|
|
8b2c090bac | ||
|
|
8f2a3bd8bd | ||
|
|
ade05a6262 | ||
|
|
a706e69be6 | ||
|
|
bc6a813c46 | ||
|
|
599373c24a | ||
|
|
daebd08475 | ||
|
|
a3f658938d | ||
|
|
23414fe411 | ||
|
|
c8061fdee9 | ||
|
|
857b21a6a3 | ||
|
|
48be3f46b8 | ||
|
|
66012a57c8 | ||
|
|
0e77d8a459 | ||
|
|
b559c2345a | ||
|
|
d79d7da299 | ||
|
|
b296ac08cf | ||
|
|
36e677dd6b | ||
|
|
6167993941 | ||
|
|
f9d3f0be27 | ||
|
|
7f76cacc10 | ||
|
|
96cac8da85 | ||
|
|
a788e976c9 | ||
|
|
68c1319ed5 | ||
|
|
9db6f256e5 | ||
|
|
33e19c003c | ||
|
|
337d688bd3 | ||
|
|
cc305f8957 | ||
|
|
40cb38e0a0 | ||
|
|
a0d3ac047d | ||
|
|
e399bd19a2 | ||
|
|
f64f144b4b | ||
|
|
197a0c3048 | ||
|
|
e5e6b0d74f | ||
|
|
db1c2ee5cc | ||
|
|
c6a43ba4c2 | ||
|
|
ed45f52433 | ||
|
|
2d478c36fa | ||
|
|
f140007758 | ||
|
|
f03b09a410 | ||
|
|
0d9767596a | ||
|
|
c626706e27 | ||
|
|
58b653f1ae | ||
|
|
5eeb98d39d | ||
|
|
e90b64b463 | ||
|
|
5fd682d83a | ||
|
|
49193c972f | ||
|
|
c027c0a527 | ||
|
|
8160c33aa4 | ||
|
|
7d0226e3c4 | ||
|
|
084c1dc5b5 | ||
|
|
bfa320c5b5 | ||
|
|
4e1cf11461 | ||
|
|
5b4197da2c | ||
|
|
e29b2f12fb | ||
|
|
33a41bb2e4 | ||
|
|
ab754e9409 | ||
|
|
433955acf7 | ||
|
|
55445a80a3 | ||
|
|
9a64ea0670 | ||
|
|
0ff144a787 | ||
|
|
8893045bf1 | ||
|
|
a8de02bd53 | ||
|
|
4db0aec1c2 | ||
|
|
d885087c93 | ||
|
|
065aad2e4c | ||
|
|
9027ce055a | ||
|
|
119b923522 | ||
|
|
3b4d0652c4 | ||
|
|
1301934796 | ||
|
|
a8539bae02 | ||
|
|
8121faa1d4 | ||
|
|
70766b4e68 | ||
|
|
361efd43a2 | ||
|
|
a78b9fe5bd | ||
|
|
0cba4695ba | ||
|
|
22fa81433e | ||
|
|
5458a0e8f5 | ||
|
|
0e67f85995 | ||
|
|
808003f3df | ||
|
|
46c225d50f | ||
|
|
0a3656efc5 | ||
|
|
0aca600063 | ||
|
|
38f05ff010 | ||
|
|
3d4cbbf6ba | ||
|
|
c877e16c14 | ||
|
|
acce8eb146 | ||
|
|
03fe71353a | ||
|
|
8915282082 | ||
|
|
28003ac430 | ||
|
|
f2dcad82a9 | ||
|
|
69ff4f0bbc | ||
|
|
a1c93fd30f | ||
|
|
bdf8c4e669 | ||
|
|
854e576c2c | ||
|
|
cf32064e8c | ||
|
|
1f91d4cb79 | ||
|
|
a7fdb23577 | ||
|
|
39d1bbed2c | ||
|
|
e2e152c373 | ||
|
|
aae74dbaf7 | ||
|
|
6c716db037 | ||
|
|
7c0c0fa244 | ||
|
|
ffd32e52ef | ||
|
|
27868b56e8 | ||
|
|
346f13e5a5 | ||
|
|
69547d4800 | ||
|
|
9fc4185f87 | ||
|
|
08b5da7c56 | ||
|
|
05e026876a | ||
|
|
8d0d0ac4e0 | ||
|
|
972d629883 | ||
|
|
ce12020fca | ||
|
|
29b149cd26 | ||
|
|
7de37b9315 | ||
|
|
237cda3b63 | ||
|
|
51c4abb72b | ||
|
|
3a6890af81 | ||
|
|
7cc2c89f92 | ||
|
|
bb55c93b1a | ||
|
|
a3d00a92a0 | ||
|
|
5eccce625a | ||
|
|
0c3a25d2ad | ||
|
|
d225aaa2ff | ||
|
|
c6b7c24ed7 | ||
|
|
827c36bb1d | ||
|
|
d9f48a5f2a | ||
|
|
ccbdc6c014 | ||
|
|
b6edba912b | ||
|
|
4cc5baaa0b | ||
|
|
c2bbb0e8a4 | ||
|
|
352cade421 | ||
|
|
708977acf7 | ||
|
|
bab5749788 | ||
|
|
c6d0b27f0d | ||
|
|
ee31f3f682 | ||
|
|
105bb08ee1 | ||
|
|
ca64d26d77 | ||
|
|
23b2f9335b | ||
|
|
dfdd7c75c2 | ||
|
|
156a2812ae | ||
|
|
e42beab336 | ||
|
|
098db9c3fa | ||
|
|
81d393fbc1 | ||
|
|
2696b1a9ec | ||
|
|
322f23ba56 | ||
|
|
09224041b8 | ||
|
|
326816e7b7 | ||
|
|
4117b6d219 | ||
|
|
af9905acff | ||
|
|
e0727cc72c | ||
|
|
624297f624 | ||
|
|
1996037acc | ||
|
|
8fa665d3e7 | ||
|
|
f0a3972ef6 | ||
|
|
c8f42c5bde | ||
|
|
7a6144d8c4 | ||
|
|
ea71155b8f | ||
|
|
20e9e3c320 | ||
|
|
bcf2139fe7 | ||
|
|
48f864475e | ||
|
|
32f24a881e | ||
|
|
0163edd8a3 | ||
|
|
81620f0199 | ||
|
|
d850b0f507 | ||
|
|
19b2cde0d9 | ||
|
|
87e252940d | ||
|
|
c6d30ae1b5 | ||
|
|
23bb45b1b6 | ||
|
|
70f59aa4ba | ||
|
|
237230961a | ||
|
|
b669ab7b74 | ||
|
|
040e8ce5e1 | ||
|
|
a74de59687 | ||
|
|
186f57cdde | ||
|
|
c94db27f07 | ||
|
|
578a58042b | ||
|
|
b3b3956ff5 | ||
|
|
ce2abb97e0 | ||
|
|
6fc71601dd | ||
|
|
fd3da7e773 | ||
|
|
70700b1ab5 | ||
|
|
eb9107bf2b | ||
|
|
d0820178c9 | ||
|
|
a8e8325ea7 | ||
|
|
4b9e35313d | ||
|
|
dc1394483a | ||
|
|
4b32b9461c | ||
|
|
308c8228aa | ||
|
|
d74d2dec2e | ||
|
|
c9f3497a8a | ||
|
|
8e57d0075c | ||
|
|
73c8eba269 | ||
|
|
1c0ffa7a24 | ||
|
|
014354efde | ||
|
|
13ec55ba07 | ||
|
|
d6508a1262 | ||
|
|
9ebb5c8ec7 | ||
|
|
2093568981 | ||
|
|
bd5ca9034c | ||
|
|
7ae454f8ca | ||
|
|
b9fcd09209 | ||
|
|
cf2915a591 | ||
|
|
9e625752be | ||
|
|
147d1f048b | ||
|
|
801c4f70ec | ||
|
|
bcc9081247 | ||
|
|
3919ea97e9 | ||
|
|
876078b725 | ||
|
|
c765a1df9b | ||
|
|
a1bc784bc7 | ||
|
|
d14b9e12ed | ||
|
|
ab0fdd66b4 | ||
|
|
bf730f5bd2 | ||
|
|
8c667791fa | ||
|
|
d07a8cfaea | ||
|
|
23deda2253 | ||
|
|
1c6cf769d4 | ||
|
|
2add23d5d2 | ||
|
|
26693b2256 | ||
|
|
f31d13c424 | ||
|
|
59182db564 | ||
|
|
1bae224f80 | ||
|
|
9b3240a14f | ||
|
|
860224c894 | ||
|
|
a8b2eb2bb0 | ||
|
|
cea69beca9 | ||
|
|
4b7da8f510 | ||
|
|
6d31b1d63d | ||
|
|
7b55b38aa4 | ||
|
|
bf1121d126 | ||
|
|
fb2d419ab0 | ||
|
|
7179b60499 | ||
|
|
ad00d8840e | ||
|
|
3d9fd3b889 | ||
|
|
90f4d77ed6 | ||
|
|
36d1e7c52e | ||
|
|
2a1895a125 | ||
|
|
d5a352465e | ||
|
|
088ab99d02 | ||
|
|
42ed7f5cbb | ||
|
|
66e8259421 | ||
|
|
1f34b63a2e | ||
|
|
cf3b4fa501 | ||
|
|
73c190217f | ||
|
|
b2986e89b0 | ||
|
|
2bf68e8a32 | ||
|
|
f392ab4270 | ||
|
|
eefb8f6a6b | ||
|
|
8b2ddc984b | ||
|
|
f5bcd03c3f | ||
|
|
92a73f21eb | ||
|
|
4dbc3d2c8f | ||
|
|
90aac9abe9 | ||
|
|
589021c40f | ||
|
|
1cc65b145a | ||
|
|
f6fbde3c96 | ||
|
|
caf25773f0 | ||
|
|
b904bb0736 | ||
|
|
28ecc90e7f | ||
|
|
72fb9c7293 | ||
|
|
d69712530c | ||
|
|
71a39600f5 | ||
|
|
0291d7c28d | ||
|
|
ed2417d974 | ||
|
|
85fd8a5204 | ||
|
|
6446bb1013 | ||
|
|
2f8852245e | ||
|
|
401b46bad8 | ||
|
|
ae3efaf8ae | ||
|
|
98746c86e4 | ||
|
|
268aef1711 | ||
|
|
d765cef376 | ||
|
|
23ed3d5647 | ||
|
|
1a7fbbfab4 | ||
|
|
47388d4a3f | ||
|
|
ce09869ea2 | ||
|
|
33587f51d3 | ||
|
|
ed76d8aecc | ||
|
|
33d2ec0597 | ||
|
|
b4ae6ed1aa | ||
|
|
b6839254d4 | ||
|
|
8ee811af58 | ||
|
|
f34ffdac55 | ||
|
|
d7e7ff6101 | ||
|
|
1c36a7bcd4 | ||
|
|
7a77be017e | ||
|
|
5b5c6710fe | ||
|
|
04f7d2e182 | ||
|
|
36ee3c8a70 | ||
|
|
98eec84422 | ||
|
|
bc08a4c005 | ||
|
|
41580992f6 | ||
|
|
9b7ce98ec0 | ||
|
|
864d567572 | ||
|
|
cf360b3b3f | ||
|
|
673dc29dfe | ||
|
|
849b643cfc | ||
|
|
c39c4a9e9f | ||
|
|
92c36c65cb | ||
|
|
b6f25e09d2 | ||
|
|
71119c963d | ||
|
|
ecf5ab5aad | ||
|
|
7e581e6ad7 | ||
|
|
36fa84e4b0 | ||
|
|
3fb4cba856 | ||
|
|
00f98cc505 | ||
|
|
d9f719196a | ||
|
|
4b0c7245bd | ||
|
|
ced3a2a45a | ||
|
|
e6838e0a1f | ||
|
|
9e637ce0db | ||
|
|
acbee743a0 | ||
|
|
fa179fa30b | ||
|
|
2376f75f1d | ||
|
|
ca95822bba | ||
|
|
6fbcd2158a | ||
|
|
389b78f748 | ||
|
|
99368b9fdd | ||
|
|
a5a4621e25 | ||
|
|
fe95ebaa95 | ||
|
|
01c7ca27fe | ||
|
|
96ab3146be | ||
|
|
3c920cfed2 | ||
|
|
db7caf1997 | ||
|
|
808815bdab | ||
|
|
8a8fd7f5a9 | ||
|
|
3b3cb6d61d | ||
|
|
30a45f1d14 | ||
|
|
d10f628f1d | ||
|
|
615da18dc9 | ||
|
|
be11c13f67 | ||
|
|
cb7f0aa41e | ||
|
|
b2cf3a5505 | ||
|
|
0d19c46d18 | ||
|
|
c05832db67 | ||
|
|
e76dbef5f5 | ||
|
|
25c00c80b7 | ||
|
|
012206e4d8 | ||
|
|
de9da437f1 | ||
|
|
153fe15ed3 | ||
|
|
b58374aff1 | ||
|
|
31ae084538 | ||
|
|
f980126e81 | ||
|
|
cb1ada1bd7 | ||
|
|
200d340123 | ||
|
|
5c2f4dd84e | ||
|
|
953d175b44 | ||
|
|
f52a463728 | ||
|
|
5f25049abc | ||
|
|
8edaf67197 | ||
|
|
41c1979283 | ||
|
|
9840891cdc | ||
|
|
7d22e18bfb | ||
|
|
07af6f2741 | ||
|
|
60d9819088 | ||
|
|
9549539f5d | ||
|
|
1c23daa3b1 | ||
|
|
4a6c37ae0c | ||
|
|
f1bfd58dd2 | ||
|
|
4446814114 | ||
|
|
1ae6236e90 | ||
|
|
624a8bbe71 | ||
|
|
ca876b291a | ||
|
|
c850a7eae1 | ||
|
|
5613a3cef3 | ||
|
|
d3b161fc25 | ||
|
|
7659e45cc1 | ||
|
|
8a28d66393 | ||
|
|
c8d92e41b2 | ||
|
|
9b39c90849 | ||
|
|
5c8a34696e | ||
|
|
3e5da7c25a | ||
|
|
b1e6770712 | ||
|
|
fd49fd6456 | ||
|
|
d0ff2fef35 | ||
|
|
4c3313e275 | ||
|
|
4e0bc36b02 | ||
|
|
01eb84e3a6 | ||
|
|
70c97e2ae4 | ||
|
|
c165c8e71f | ||
|
|
6dd9773f2c | ||
|
|
15dbb0a634 | ||
|
|
ce09ac2a92 | ||
|
|
425f936254 | ||
|
|
7a9e4b0e8f | ||
|
|
48f10011e1 | ||
|
|
f6d3f799dd | ||
|
|
2157f4a385 | ||
|
|
0f76d05546 | ||
|
|
293f49e178 | ||
|
|
4d2de2dd57 | ||
|
|
46dc965cd0 | ||
|
|
707aeb6d65 | ||
|
|
96c63c60a2 | ||
|
|
9539123fc3 | ||
|
|
ea07346ae6 | ||
|
|
af1e440103 | ||
|
|
0cea7d23f0 | ||
|
|
ea8c88a31a | ||
|
|
b4e5544ff8 | ||
|
|
04d534cd30 | ||
|
|
7cb247976f | ||
|
|
3cbf9e14e6 | ||
|
|
3bd6516440 | ||
|
|
e3f691fbda | ||
|
|
ae76bea220 | ||
|
|
ca81a507b6 | ||
|
|
3f2382cfdc | ||
|
|
c1ccae315f | ||
|
|
92cb4e3d29 | ||
|
|
9dd8c45c57 | ||
|
|
fa84a84a40 | ||
|
|
9e747e7c2e | ||
|
|
79306e0618 | ||
|
|
139073dc3e | ||
|
|
18d441ef2e | ||
|
|
e07058ef5a | ||
|
|
f02941445b | ||
|
|
37b172dbfd | ||
|
|
4b2fc37015 | ||
|
|
ae219c83fb | ||
|
|
e60c5c4546 | ||
|
|
f57adb33eb | ||
|
|
25821c1294 | ||
|
|
8904e9d709 | ||
|
|
35885ef59f | ||
|
|
c19eb5847a | ||
|
|
c591f1d23e | ||
|
|
fd1f76169a | ||
|
|
8814746738 | ||
|
|
7bb6c4f0c1 | ||
|
|
6c14282223 | ||
|
|
7333760ada | ||
|
|
cb0b5feef8 | ||
|
|
01369464fa | ||
|
|
c2ceaa9a3f | ||
|
|
36716508c3 | ||
|
|
d683a76a49 | ||
|
|
4a0f6ef8af | ||
|
|
92b735b7fa | ||
|
|
240db01e75 | ||
|
|
04bacccfea | ||
|
|
25b6e8c2d7 | ||
|
|
1e2ceb8252 | ||
|
|
329a5aec32 | ||
|
|
c86ae623cb | ||
|
|
ce8dbc4302 | ||
|
|
72972c76af | ||
|
|
d55813fffb | ||
|
|
1f92e96079 | ||
|
|
2cb3c1fc9f | ||
|
|
caaf2b0d1e | ||
|
|
a635e4cd30 | ||
|
|
aa336fb525 | ||
|
|
c0dc08116c | ||
|
|
a5b3334222 | ||
|
|
8fd2491b9c | ||
|
|
deea93acad | ||
|
|
ba92feee4a | ||
|
|
e6458c26d6 | ||
|
|
4befbfafc5 | ||
|
|
fc938ea3eb | ||
|
|
5e444a4f7c | ||
|
|
1250627862 | ||
|
|
a588bdc7b3 | ||
|
|
44a4d13bad | ||
|
|
7467806969 | ||
|
|
9f844a8b91 | ||
|
|
83435a47de | ||
|
|
33612590ed | ||
|
|
7475e9576c | ||
|
|
56d114f13d | ||
|
|
50f702e9f1 | ||
|
|
43a361a52d | ||
|
|
85976a1f35 | ||
|
|
167cc6862a | ||
|
|
5ca57e4110 | ||
|
|
988d09ed2a | ||
|
|
bafa5fad54 | ||
|
|
a2e816253f | ||
|
|
68603f9aed | ||
|
|
46ac480713 | ||
|
|
35cad794e9 | ||
|
|
ee1a0c2c59 | ||
|
|
6f2ded4ce8 | ||
|
|
756b86a416 | ||
|
|
4b22fd2095 | ||
|
|
368854ba41 | ||
|
|
af4d25ee37 | ||
|
|
cb460ee7ba | ||
|
|
300cbd090f | ||
|
|
e32c15204c | ||
|
|
b8b68af316 | ||
|
|
0a5fb5e9e7 | ||
|
|
99f475b56f | ||
|
|
0b1ff75f1b | ||
|
|
ca653cd245 | ||
|
|
84563bdcd4 | ||
|
|
49215503a6 | ||
|
|
9a8aafc189 | ||
|
|
347808e86c | ||
|
|
f34960d82a | ||
|
|
c6165ee502 | ||
|
|
f676949460 | ||
|
|
73cfaee5ec | ||
|
|
a6983d2d99 | ||
|
|
43e4a5b250 | ||
|
|
7b2c027c26 | ||
|
|
e2069889b4 | ||
|
|
499b3f1ff4 | ||
|
|
b4713f9bc6 | ||
|
|
5132ee3559 | ||
|
|
2a702d1cb5 | ||
|
|
23a4859e0e | ||
|
|
ae94aecdd7 | ||
|
|
a6edf34a92 | ||
|
|
c26b1335f5 | ||
|
|
fdeb7689d7 | ||
|
|
2d5e765193 | ||
|
|
29d82736a7 | ||
|
|
e493c65b12 | ||
|
|
ada6f533b7 | ||
|
|
5ea578b8c8 | ||
|
|
67cb6abe56 | ||
|
|
3ff6a02391 | ||
|
|
dde83e7f67 | ||
|
|
fe527ff5dd | ||
|
|
4f99bbace9 | ||
|
|
ad0ac34f9d | ||
|
|
ba3306b497 | ||
|
|
7ff8b34e80 | ||
|
|
fc4b1464b9 | ||
|
|
485347e20b | ||
|
|
34676105a1 | ||
|
|
339eacb01f | ||
|
|
27a047976b | ||
|
|
3289129782 | ||
|
|
afa715c860 | ||
|
|
ae0eddfb25 | ||
|
|
13b2f8018d | ||
|
|
eb66ce2d4b | ||
|
|
ad16b0b5a6 | ||
|
|
c81f519b7c | ||
|
|
6450c0bee6 | ||
|
|
87d1db760f | ||
|
|
1c903f4d26 | ||
|
|
b84a8bc76a | ||
|
|
e77f059685 | ||
|
|
4e108d434a | ||
|
|
0f626bebbf | ||
|
|
cff57b6562 | ||
|
|
5cb9212fa4 | ||
|
|
c2910d742a | ||
|
|
e8174f7462 | ||
|
|
f245d97fc0 | ||
|
|
469c03f5e7 | ||
|
|
3144c45688 | ||
|
|
99e746ba81 | ||
|
|
7527b9f9b1 | ||
|
|
5e6add724d | ||
|
|
a5cd9a4968 | ||
|
|
c5fe481c33 | ||
|
|
38b0ace0ca | ||
|
|
1819f38ccb | ||
|
|
4834ecbddb | ||
|
|
3aa5fdba55 | ||
|
|
8ae987ea69 | ||
|
|
d77f543c8f | ||
|
|
517415f743 | ||
|
|
b9770e7e73 | ||
|
|
93cb8a2411 | ||
|
|
4866d2d190 | ||
|
|
80a9d05ff3 | ||
|
|
1db7aa3f26 | ||
|
|
ff1e11022d | ||
|
|
b55bf31fdc | ||
|
|
75c557ec63 | ||
|
|
2116ba19f6 | ||
|
|
8b9375ea68 | ||
|
|
9a024c6146 | ||
|
|
642de684e8 | ||
|
|
eb18d759f1 | ||
|
|
3fa41ea8d9 | ||
|
|
1147d6ba4a | ||
|
|
b0bebcd162 | ||
|
|
12dedf2047 | ||
|
|
9af8184221 | ||
|
|
62eeb06abe | ||
|
|
e60b5f670a | ||
|
|
6b2e64ef96 | ||
|
|
d6bfc773de | ||
|
|
a1e4b50b29 | ||
|
|
38c8762f64 | ||
|
|
ddd5b581fa | ||
|
|
e2afe4b787 | ||
|
|
b24ff33fe0 | ||
|
|
0922ee202a | ||
|
|
946fcf2b25 | ||
|
|
a517a8038f | ||
|
|
faca17ff78 | ||
|
|
d4e6126ec3 | ||
|
|
5c0da06d66 | ||
|
|
0d3bcb0b2c | ||
|
|
e0ab8fc8e2 | ||
|
|
011f12a7e4 | ||
|
|
f7f146df04 | ||
|
|
803a6d8a25 | ||
|
|
fa907a7f2b | ||
|
|
033a3a9db9 | ||
|
|
4a0dbde6b7 | ||
|
|
e41e08f2af | ||
|
|
c738f715a3 | ||
|
|
0d2572d37d | ||
|
|
a0df846493 | ||
|
|
01a6d60890 | ||
|
|
8721999a22 | ||
|
|
2b505466ce | ||
|
|
17627d0775 | ||
|
|
0f87fb86a9 | ||
|
|
f438f5252e | ||
|
|
a7291046a6 | ||
|
|
9e15209161 | ||
|
|
aed26b526a | ||
|
|
da5b7afa58 | ||
|
|
c271b07476 | ||
|
|
6b428c1ea9 | ||
|
|
5641ba4a14 | ||
|
|
461c7099ed | ||
|
|
81871868c4 | ||
|
|
191a835d9f | ||
|
|
0d7aabe920 | ||
|
|
1b8b7823c2 | ||
|
|
75ca306959 | ||
|
|
37994de120 | ||
|
|
3e35b0f7c6 | ||
|
|
f555a3323e | ||
|
|
a57526a0ff | ||
|
|
68001b00f3 | ||
|
|
7fbc248aa7 | ||
|
|
ec63d4c528 | ||
|
|
c368af633c | ||
|
|
486fe1dfc4 | ||
|
|
271ae9a36a | ||
|
|
18513724a0 | ||
|
|
9cfb652b29 | ||
|
|
fbec7bd360 | ||
|
|
401aba407c | ||
|
|
c9f14d7f58 | ||
|
|
aa7e32f81d | ||
|
|
6ab6ac81aa | ||
|
|
f13672776a | ||
|
|
13b299a3aa | ||
|
|
7a764f51ec | ||
|
|
71a93409fe | ||
|
|
f70961e67d | ||
|
|
8194d627ee | ||
|
|
4e9573334a | ||
|
|
74cc1296c8 | ||
|
|
4adcb8c938 | ||
|
|
4fdc5aa55f | ||
|
|
fd43cb4fd7 | ||
|
|
e08236eaff | ||
|
|
27b5e3daa7 | ||
|
|
955d3f9dd5 | ||
|
|
557d973ba4 | ||
|
|
b9bf8887dc | ||
|
|
5995258c5e | ||
|
|
2f46b6f507 | ||
|
|
dbb4be7cfa | ||
|
|
a6a8da5aa4 | ||
|
|
86706f31c6 | ||
|
|
59ba63f875 | ||
|
|
52933a528b | ||
|
|
0330498bbb | ||
|
|
837dac17ea | ||
|
|
9fb3b5cfed | ||
|
|
2ad00deb38 | ||
|
|
8fb8a5002d | ||
|
|
af9ee948ef | ||
|
|
9eb76fe470 | ||
|
|
6d766a8ce9 | ||
|
|
33afecf7da | ||
|
|
938c7d2437 | ||
|
|
c3bee5e725 | ||
|
|
bb46282b91 | ||
|
|
ca346ccbb2 | ||
|
|
46f05224ab | ||
|
|
1e3bac6031 | ||
|
|
3f90a3f49d | ||
|
|
099dea886f | ||
|
|
d4d253284d | ||
|
|
d6e15f985c | ||
|
|
c1b0497624 | ||
|
|
f25e4827a2 | ||
|
|
5d57a51618 | ||
|
|
d803ffcf95 | ||
|
|
4de5f180b6 | ||
|
|
2bad321397 | ||
|
|
ab37cc9661 | ||
|
|
a9a5166da7 | ||
|
|
5723ea3f8b | ||
|
|
2daea0836a | ||
|
|
e05c66a973 | ||
|
|
0295d9c573 | ||
|
|
d60e9f3bc2 | ||
|
|
d71d35b258 | ||
|
|
9988d76c3f | ||
|
|
f55d0a67d9 | ||
|
|
171adf7310 | ||
|
|
03a9890781 | ||
|
|
bf2d7b9d92 | ||
|
|
05da17e9cb | ||
|
|
f653f643f7 | ||
|
|
d742ea6efc | ||
|
|
1fb1103e2d | ||
|
|
baea8b62be | ||
|
|
fe3dd76f68 | ||
|
|
6af6e9c2c0 | ||
|
|
707bfb9c78 | ||
|
|
316087d18c | ||
|
|
46c538dffb | ||
|
|
218f6cb8d1 | ||
|
|
05f6c7656c | ||
|
|
3534ae910e | ||
|
|
dd65778017 | ||
|
|
67f3a4b164 | ||
|
|
fcf1fd9bec | ||
|
|
2ceccd201a | ||
|
|
45edfeee2d | ||
|
|
2a6cf2aea2 | ||
|
|
8bb3e5d9c9 | ||
|
|
d3ad2ec4f8 | ||
|
|
a3ef96a799 | ||
|
|
a5b7f8fd6b | ||
|
|
33accf65ef | ||
|
|
e2012433cb | ||
|
|
d6d05121e4 | ||
|
|
125392ce57 | ||
|
|
fb20155485 | ||
|
|
1d44ee2f45 | ||
|
|
2a6e91477a | ||
|
|
302f3b57c5 | ||
|
|
7d818c0590 | ||
|
|
ab77fca2f5 | ||
|
|
4d33e1388b | ||
|
|
9bac03d2f3 | ||
|
|
68bb155793 | ||
|
|
2be6ec1711 | ||
|
|
27e254ac4d | ||
|
|
a7a1fb45bb | ||
|
|
0732799ecb | ||
|
|
ea22ac6b37 | ||
|
|
aeacf40654 | ||
|
|
f87cb8d940 | ||
|
|
f37907a55a | ||
|
|
0538d5c0db | ||
|
|
9a84e126a1 | ||
|
|
5ec633192f | ||
|
|
7b87dc9d21 | ||
|
|
35df355cc6 | ||
|
|
59830a4793 | ||
|
|
d685ce1e6e | ||
|
|
0eba0d4b4a | ||
|
|
b32d3e5c17 | ||
|
|
d696f19764 | ||
|
|
1645c2103c | ||
|
|
9dfac1dd6f | ||
|
|
2937dbfc3a | ||
|
|
f3a1cf82ac | ||
|
|
84ae7d187b | ||
|
|
49ba670b36 | ||
|
|
78f9dcc68a | ||
|
|
8258d84c23 | ||
|
|
a2a48415f7 | ||
|
|
5539705bf3 | ||
|
|
1061873788 | ||
|
|
a587c9523d | ||
|
|
eed18fba57 | ||
|
|
df2158df0b | ||
|
|
40ec49aeb9 | ||
|
|
4900bbf989 | ||
|
|
3dc92ae8e8 | ||
|
|
7255b0b30f | ||
|
|
b9b01d3816 | ||
|
|
256103b02e | ||
|
|
194c190519 | ||
|
|
8e5c258896 | ||
|
|
fd375788f8 | ||
|
|
d80fe902b3 | ||
|
|
4304d8badd | ||
|
|
ecdf507645 | ||
|
|
c4d271d767 | ||
|
|
2dcd60efc4 | ||
|
|
4251f2a7f7 | ||
|
|
4a58ad861c | ||
|
|
b87bbd569e | ||
|
|
11bad31b5f | ||
|
|
d428ffe937 | ||
|
|
4ff60abb93 | ||
|
|
7a5a476ed1 | ||
|
|
b47f3563de | ||
|
|
a828addd28 | ||
|
|
93ce71acdd | ||
|
|
a15ccfee1b | ||
|
|
e5cbee2770 | ||
|
|
e9f7c5ed5a | ||
|
|
3141590d28 | ||
|
|
cfa800c1cd | ||
|
|
6eb79aacdb | ||
|
|
2ace7d4161 | ||
|
|
b36d2edfde | ||
|
|
3897446cb7 | ||
|
|
d4c41a2b27 | ||
|
|
dfaabeead2 | ||
|
|
989e368fb4 | ||
|
|
0c6060eae7 | ||
|
|
8a766fe100 | ||
|
|
9afb32f526 | ||
|
|
8ea8a20cc9 | ||
|
|
70f0c7d01e | ||
|
|
f214f0f033 | ||
|
|
37425db04d | ||
|
|
357909401d | ||
|
|
87286593cb | ||
|
|
2e1a74f79a | ||
|
|
341ea70dbc | ||
|
|
54e4c57ae5 | ||
|
|
9432ffebad | ||
|
|
c748792f16 | ||
|
|
f1b304304c | ||
|
|
8532f7df26 | ||
|
|
9cb3314494 | ||
|
|
cfc7b2ac93 | ||
|
|
2f2709abc6 | ||
|
|
8fd8087370 | ||
|
|
d140dc2c71 | ||
|
|
1a4f330bd5 | ||
|
|
65a0125035 | ||
|
|
bfe506dbf3 | ||
|
|
da7a177599 | ||
|
|
fe677f133b | ||
|
|
d6e4d2f24b | ||
|
|
a14642b62a | ||
|
|
9d223067ae | ||
|
|
39009a6ee6 | ||
|
|
0ea16b1d97 | ||
|
|
977e9e0019 | ||
|
|
e186db3b8e | ||
|
|
0135a37af8 | ||
|
|
a923d1effc | ||
|
|
11f7bf2bb1 | ||
|
|
b336cdffe5 | ||
|
|
f85b9e1b2b | ||
|
|
1422e3ffb7 | ||
|
|
b1a080cb91 | ||
|
|
59ad01c560 | ||
|
|
a24eaa6693 | ||
|
|
b98b10c580 | ||
|
|
2fca01401b | ||
|
|
f20296cf7c | ||
|
|
fe041c5e10 | ||
|
|
ca44d23031 | ||
|
|
18452f692c | ||
|
|
6aca68824a | ||
|
|
7177b9bf82 | ||
|
|
5b6e24bd13 | ||
|
|
cc93108df0 | ||
|
|
95041b75b0 | ||
|
|
0929799daf | ||
|
|
c771534c43 | ||
|
|
3ffbc09630 | ||
|
|
de4207940c | ||
|
|
3eb7d6337a | ||
|
|
1dd556d6c8 | ||
|
|
a250d9b184 | ||
|
|
bb6cedfce4 | ||
|
|
a4c9b9c8cf | ||
|
|
c88ea6666b | ||
|
|
5ecd73c599 | ||
|
|
84739ba695 | ||
|
|
3bb1e9ff91 | ||
|
|
fbd6691711 | ||
|
|
aec17da6b0 | ||
|
|
639533662d | ||
|
|
a340ce4a68 | ||
|
|
6e5fe64e8b | ||
|
|
84bdbb84b8 | ||
|
|
f91ffb6cb4 | ||
|
|
13815199a3 | ||
|
|
98c20ff551 | ||
|
|
479b6b9081 | ||
|
|
c640d2ea77 | ||
|
|
715547d2fd | ||
|
|
8a914a536b | ||
|
|
f56b6c0648 | ||
|
|
25aa8318d9 | ||
|
|
72f2cbe9e8 | ||
|
|
fc7836084a | ||
|
|
60d9d9202b | ||
|
|
a9df468282 | ||
|
|
0bba773c3e | ||
|
|
7be03ae623 | ||
|
|
d4741532f5 | ||
|
|
10f31efefa | ||
|
|
be662ddf32 | ||
|
|
ff84beaade | ||
|
|
8dba8fa5fb | ||
|
|
3a3f5552a7 | ||
|
|
88cba353c0 | ||
|
|
a2851370bb | ||
|
|
7c0ef7dcf0 | ||
|
|
fb6d5d89b8 | ||
|
|
45ceff4c08 | ||
|
|
6169abc700 | ||
|
|
c09e12d13e | ||
|
|
6152e15e10 | ||
|
|
79910b2ae8 | ||
|
|
c347df7c17 | ||
|
|
e9e2b35c59 | ||
|
|
8ae753c396 | ||
|
|
d2c94321f2 | ||
|
|
4c10f8a537 | ||
|
|
9a19cc2173 | ||
|
|
c4cbc894f5 | ||
|
|
449f5e6c7f | ||
|
|
34447a3f2f | ||
|
|
eee97d7dba | ||
|
|
f16392947a | ||
|
|
c3adf92b49 | ||
|
|
a6580b018d | ||
|
|
00c843c7ce | ||
|
|
5fe55a4db9 | ||
|
|
4ca998c346 | ||
|
|
42b3e2cc11 | ||
|
|
59fb8db6be | ||
|
|
664beafefa | ||
|
|
04443a64e2 | ||
|
|
92dedf3386 | ||
|
|
2ba5fb9820 | ||
|
|
222235159b | ||
|
|
1061bd5e0d | ||
|
|
ccec9a8348 | ||
|
|
bf9e06e67d | ||
|
|
1e03d222c5 | ||
|
|
3722f46b8e | ||
|
|
8d7bf97127 | ||
|
|
d26c1a6407 | ||
|
|
f296ff8476 | ||
|
|
a75a71994a | ||
|
|
7d1373a1d1 | ||
|
|
23abc50015 | ||
|
|
5e28d1131a | ||
|
|
a141e64a69 | ||
|
|
e12ee97bd9 | ||
|
|
ebd84642e1 | ||
|
|
888ac2ea0d | ||
|
|
37068c922c | ||
|
|
eec24d2ed1 | ||
|
|
8fa9d629a3 | ||
|
|
ef22dd7dc4 | ||
|
|
3f12624b78 | ||
|
|
d6fa67374c | ||
|
|
d4183cf718 | ||
|
|
4ad0480039 | ||
|
|
7188425ac0 | ||
|
|
c83581cf47 | ||
|
|
f8fa29f157 | ||
|
|
f2a4db6291 | ||
|
|
a4aff0b1e9 | ||
|
|
f5c2e0b425 | ||
|
|
2f88c55ec0 | ||
|
|
db7075a91a | ||
|
|
8ac007ba9a | ||
|
|
111fa8397c | ||
|
|
0ef4e92a96 | ||
|
|
9123afac18 | ||
|
|
1b5dca52e3 | ||
|
|
bc3e9c43da | ||
|
|
ded01cfd8a | ||
|
|
c737b7494f | ||
|
|
47aff56e71 | ||
|
|
15ce341d81 | ||
|
|
7476204258 | ||
|
|
e083b1a02e | ||
|
|
fa74fc54e3 | ||
|
|
fbbeec6d2f | ||
|
|
b4192364f6 | ||
|
|
c7dc82947f | ||
|
|
c38fd3986c | ||
|
|
35d7926e18 | ||
|
|
86e06ce1e6 | ||
|
|
1a08f063a6 | ||
|
|
740208ed76 | ||
|
|
521f6fe3f1 | ||
|
|
f43d490763 | ||
|
|
898179b645 | ||
|
|
8c3888da02 | ||
|
|
e1471ec9a1 | ||
|
|
eac07a51ba | ||
|
|
753000b56a | ||
|
|
8ab2907c3f | ||
|
|
723ee80c8f | ||
|
|
e1c003c8df | ||
|
|
96c82dc69f | ||
|
|
0dd9483797 | ||
|
|
4da110087d | ||
|
|
7f75a64647 | ||
|
|
71d7f6fd98 | ||
|
|
42595f241f | ||
|
|
c67c84dfda | ||
|
|
2ba6a3b097 | ||
|
|
0456ad96fb | ||
|
|
0ab87b03fa | ||
|
|
54fc41e175 | ||
|
|
261e824290 | ||
|
|
802363e1da | ||
|
|
1054d5bb74 | ||
|
|
f5d143710e | ||
|
|
c025208bf5 | ||
|
|
7306e39f3e | ||
|
|
0d30d183df | ||
|
|
cd504a4127 | ||
|
|
bba41402aa | ||
|
|
e1f129abc5 | ||
|
|
7e6c6924f2 | ||
|
|
185fc4c942 | ||
|
|
5f7c6b307f | ||
|
|
a9f80467c3 | ||
|
|
89fb1a8804 | ||
|
|
54bfce0d5d | ||
|
|
dca1d1b413 | ||
|
|
4b07ee40a7 | ||
|
|
d8644c940b | ||
|
|
7df70abb48 | ||
|
|
6e3c5c0388 | ||
|
|
6cbe2e288b | ||
|
|
1f95c6ca6e | ||
|
|
ac6ae43449 | ||
|
|
ba3117f435 | ||
|
|
fd449f68d9 | ||
|
|
574947656c | ||
|
|
5b1b704d1d | ||
|
|
abbdb207e0 | ||
|
|
2d89fc945f | ||
|
|
25b130ce2c | ||
|
|
ca098eb171 | ||
|
|
f6ccd119e8 | ||
|
|
7b1f99b41a | ||
|
|
4193ee1980 | ||
|
|
c718012c3e | ||
|
|
aca5c8af73 | ||
|
|
2d4a699790 | ||
|
|
fc0475b2c5 | ||
|
|
0f6e13b8c9 | ||
|
|
c60cfae677 | ||
|
|
ded05cf079 | ||
|
|
c360d471cd | ||
|
|
382a39e6ed | ||
|
|
bfa1499889 | ||
|
|
053a5e9bd7 | ||
|
|
3677f6cf0f | ||
|
|
03fb75f030 | ||
|
|
8e209d2767 | ||
|
|
0cf77a4115 | ||
|
|
14368c4ce2 | ||
|
|
e74838fdc1 | ||
|
|
3f64b13d70 | ||
|
|
613f4b6440 | ||
|
|
1bfa4c6ba8 | ||
|
|
9089473cf0 | ||
|
|
687eb7d0dc | ||
|
|
52cb8adf65 | ||
|
|
06f241adb1 | ||
|
|
f5ba173156 | ||
|
|
5bd7bfb4ce | ||
|
|
e4413dceee | ||
|
|
f644454080 | ||
|
|
16f4b485b5 | ||
|
|
679dd4578c | ||
|
|
00bf318ee9 | ||
|
|
da3415d029 | ||
|
|
147a8d2f57 | ||
|
|
a1d354ac15 | ||
|
|
1fb0e41603 | ||
|
|
f121c119f0 | ||
|
|
f4c472e7de | ||
|
|
719d62b670 | ||
|
|
165306f56a | ||
|
|
248bb32977 | ||
|
|
dfb9127052 | ||
|
|
5933e948e1 | ||
|
|
624f6ef5a1 | ||
|
|
2865b546fa | ||
|
|
9d7c3b2af9 | ||
|
|
033896dfc2 | ||
|
|
10d95ba8e6 | ||
|
|
43f74583ea | ||
|
|
36d499f253 | ||
|
|
4c6e61e6dd | ||
|
|
0829af77c0 | ||
|
|
f3281d3985 | ||
|
|
5cd5567292 | ||
|
|
c0102a1658 | ||
|
|
c173d0d76e | ||
|
|
d7dd4b82da | ||
|
|
f9010cf3ca | ||
|
|
a0d2dbf685 | ||
|
|
d679d27043 | ||
|
|
9c0641dcad | ||
|
|
af852a088c | ||
|
|
76f04c0b0e | ||
|
|
27376df5f2 | ||
|
|
fd285dbd6f | ||
|
|
76e1d4b403 | ||
|
|
51dbc1a1ee | ||
|
|
7095236116 | ||
|
|
6014c888c2 | ||
|
|
c41714c6f3 | ||
|
|
a3626e121b | ||
|
|
b7caba8b11 | ||
|
|
0a45f446e2 | ||
|
|
917dbb12da | ||
|
|
767c0b3675 | ||
|
|
30477bb0ad | ||
|
|
1ce4d31503 | ||
|
|
066f7cfa08 | ||
|
|
be8f2bf280 | ||
|
|
e339571ce0 | ||
|
|
3ea89b3956 | ||
|
|
1e65d45844 | ||
|
|
99750b8edb | ||
|
|
2af3831366 | ||
|
|
5d9c65041e | ||
|
|
e789f92db2 | ||
|
|
9a1f7dd3eb | ||
|
|
fb48c4cf0e | ||
|
|
8c5dee529b | ||
|
|
c885b76bbc | ||
|
|
6d03f12e54 | ||
|
|
be5bbe95b6 | ||
|
|
e571c884c1 | ||
|
|
96008539d5 | ||
|
|
8c221f33a8 | ||
|
|
9612af06a8 | ||
|
|
17c1be6dd7 | ||
|
|
6607cdb0bd | ||
|
|
5f33bd312e | ||
|
|
185bc30893 | ||
|
|
b35d5e9914 | ||
|
|
a89eab43c4 | ||
|
|
5eac4ebee8 | ||
|
|
65a3c3a33c | ||
|
|
b0cf35d264 | ||
|
|
f8aff7dcd9 | ||
|
|
b754912990 | ||
|
|
83a01cf0e2 | ||
|
|
d81db3fd4b | ||
|
|
1b383888b8 | ||
|
|
cd76672831 | ||
|
|
94f4d652b4 | ||
|
|
5aab7c34e6 | ||
|
|
72eff3754f | ||
|
|
0d3d9c8233 | ||
|
|
3bb8614143 | ||
|
|
2b12a47915 | ||
|
|
9216e72a3e | ||
|
|
9b99a92dc1 | ||
|
|
ab1a68d0f8 | ||
|
|
4fedf9548f | ||
|
|
a396c86e84 | ||
|
|
75f2da3547 | ||
|
|
6c9a25a0c7 | ||
|
|
34db96822b | ||
|
|
37e60c9dfe | ||
|
|
54b2cbc608 | ||
|
|
7f4169e4e3 | ||
|
|
c356ec40d6 | ||
|
|
23a7268e84 | ||
|
|
35d960c09f | ||
|
|
a7d81993d5 | ||
|
|
e5af3257c9 | ||
|
|
da83581f45 | ||
|
|
5900594562 | ||
|
|
a050832da0 | ||
|
|
fe766dc438 | ||
|
|
c1a5ce4b27 | ||
|
|
64b0e63945 | ||
|
|
95f6841a48 | ||
|
|
3189c6fe98 | ||
|
|
9dbee4c686 | ||
|
|
04a2d45334 | ||
|
|
6766af0f9d | ||
|
|
c7058e7613 | ||
|
|
3d1f858301 | ||
|
|
21de0db790 | ||
|
|
c5d4c532b3 | ||
|
|
3877e89277 | ||
|
|
688ac96faa | ||
|
|
e53f36e5ef | ||
|
|
ad35df2a17 | ||
|
|
fd104ad74f | ||
|
|
bffa9a2ed8 | ||
|
|
dfd18776c6 | ||
|
|
f746ef7954 | ||
|
|
a71b83b4c3 | ||
|
|
cebd206bcc | ||
|
|
d07311cf64 | ||
|
|
7d6390d9bf | ||
|
|
68f40ed779 | ||
|
|
4d56124274 | ||
|
|
6aa31e612a | ||
|
|
6d9018627e | ||
|
|
28b0e74e04 | ||
|
|
246f1155e6 | ||
|
|
a59d29e10c | ||
|
|
b13e2c32ff | ||
|
|
807cba18d1 | ||
|
|
e938f94ffc | ||
|
|
d6e6f2cb25 | ||
|
|
43328b94b7 | ||
|
|
dde15b0881 | ||
|
|
d84fd95f3c | ||
|
|
efed99b890 | ||
|
|
1a6c1ed1c3 | ||
|
|
fa82d31000 | ||
|
|
d11e7fe562 | ||
|
|
e36a49b635 | ||
|
|
17df0e3cd3 | ||
|
|
76df2fd204 | ||
|
|
f9491bb944 | ||
|
|
3541540d84 | ||
|
|
1ae5b9cfcd | ||
|
|
e493bda6df | ||
|
|
7de15e94b2 | ||
|
|
002f847165 | ||
|
|
a654f50c33 | ||
|
|
a2594d4a6b | ||
|
|
b98e1f6b20 | ||
|
|
a48eef2575 | ||
|
|
4aeb804441 | ||
|
|
a30adac959 | ||
|
|
d605de4e07 | ||
|
|
fe49ee0aad | ||
|
|
e47fd3b879 | ||
|
|
383e039297 | ||
|
|
46a206de64 | ||
|
|
f39cb976aa | ||
|
|
ad7c02f745 | ||
|
|
1c98a831d8 | ||
|
|
ffcdf4462b | ||
|
|
22cfc82d35 | ||
|
|
83fbe9eccc | ||
|
|
9a89c14e20 | ||
|
|
6b1d762245 | ||
|
|
0b9da0b049 | ||
|
|
5bcce80b50 | ||
|
|
9f3bf24d06 | ||
|
|
647ab0ab78 | ||
|
|
188a7b661e | ||
|
|
fe4393593a | ||
|
|
6fd5a97fde | ||
|
|
10cd4b8999 | ||
|
|
cb3d76889d | ||
|
|
07504dd149 | ||
|
|
2ea5b65cb8 | ||
|
|
f124c8d81b | ||
|
|
1e7567fb8f | ||
|
|
5f6874edbf | ||
|
|
3558bd5d9b | ||
|
|
8e45f7d785 | ||
|
|
ceb234f0d5 | ||
|
|
1c8a3ea7d2 | ||
|
|
522d6a5fc4 | ||
|
|
21a7df9a84 | ||
|
|
d6eeacbdc1 | ||
|
|
d4d581e4fb | ||
|
|
9c31d148fe | ||
|
|
47c932bd46 | ||
|
|
cc7ab8f7c4 | ||
|
|
6883851866 | ||
|
|
94a5c6244e | ||
|
|
42fb6c7100 | ||
|
|
5e7fcdd105 | ||
|
|
12b0365ce0 | ||
|
|
114dfa3904 | ||
|
|
e31a05beb0 | ||
|
|
a28942f264 | ||
|
|
af61f45ecb | ||
|
|
0da9955403 | ||
|
|
e4f699641a | ||
|
|
23b56b4053 | ||
|
|
0971153a74 | ||
|
|
0b3abfae28 | ||
|
|
d0b9488c1c | ||
|
|
698962e0ea | ||
|
|
1c010ffe5a | ||
|
|
95ba1b5849 | ||
|
|
19dba93cca | ||
|
|
94fe4f1f46 | ||
|
|
de9d43f67a | ||
|
|
301c58d6fb | ||
|
|
227ecc0193 | ||
|
|
ee260e671f | ||
|
|
1a954c5b25 | ||
|
|
a14dd95c21 | ||
|
|
19f7918435 | ||
|
|
1e4de5f821 | ||
|
|
45f4b33eb1 | ||
|
|
16e7287c24 | ||
|
|
3b2b5eed5a | ||
|
|
0521740824 | ||
|
|
1d2033953b | ||
|
|
e20edefc61 | ||
|
|
a9287d9d80 | ||
|
|
ba029db24e | ||
|
|
529c187bc4 | ||
|
|
714914ffe5 | ||
|
|
cd23b9ebfe | ||
|
|
35ce68f4f6 | ||
|
|
f7bcd89b97 | ||
|
|
0b44fba68c | ||
|
|
501869c7aa | ||
|
|
15cb1cb746 | ||
|
|
7454cc12a1 | ||
|
|
52d52810b9 | ||
|
|
936039f7a7 | ||
|
|
ba0e8f7973 | ||
|
|
8449a99418 | ||
|
|
28c9dbab1f | ||
|
|
81753cd44b | ||
|
|
9970bc84ff | ||
|
|
499bc4c4cd | ||
|
|
b0921b5be0 | ||
|
|
afc4dfaf50 | ||
|
|
9dda3a9323 | ||
|
|
115e80dce0 | ||
|
|
9f405686ec | ||
|
|
e7d7469c4e | ||
|
|
0dbb72efe9 | ||
|
|
9b21d5a619 | ||
|
|
8ffcdebffc | ||
|
|
e089184a14 | ||
|
|
a2a059962c | ||
|
|
3eb42321d5 | ||
|
|
474484b059 | ||
|
|
998e255636 | ||
|
|
3938644442 | ||
|
|
b13e637593 | ||
|
|
95e3f5e0e8 | ||
|
|
d5ee79fe1e | ||
|
|
51f003d5fd | ||
|
|
dd89657f1e | ||
|
|
1e280608d3 | ||
|
|
c6e5bedd3c | ||
|
|
0ff5b46799 | ||
|
|
e34090a87b | ||
|
|
f690b24c68 | ||
|
|
10f045fe6e | ||
|
|
8d8b77ca07 | ||
|
|
234d2380ef | ||
|
|
4de659d5bb | ||
|
|
2aabe4e11c | ||
|
|
171e55ce6d | ||
|
|
297e6c8872 | ||
|
|
2b39d613b7 | ||
|
|
2432fd1d85 | ||
|
|
3cc3d4997b | ||
|
|
9c0189f8be | ||
|
|
fc5a21f57d | ||
|
|
ee9ce8a87b | ||
|
|
d720121044 | ||
|
|
3fce846a8d | ||
|
|
8776c3f4a8 | ||
|
|
189ad7889d | ||
|
|
79a15e1470 | ||
|
|
dfd0d0ed30 | ||
|
|
d7bf0bd653 | ||
|
|
6044190019 | ||
|
|
89b8d0ef67 | ||
|
|
489d713fa2 | ||
|
|
8d984d8dac | ||
|
|
cadbae85a5 | ||
|
|
01bb476023 | ||
|
|
d5c37f7b29 | ||
|
|
99181cf5c6 | ||
|
|
7836b8229a | ||
|
|
3cff627b22 | ||
|
|
4263899bc0 | ||
|
|
0f4bb2b24b | ||
|
|
7bd7b421b8 | ||
|
|
49eb9bbcf8 | ||
|
|
a87596b3a1 | ||
|
|
1ca890b1e9 | ||
|
|
52aba14ae9 | ||
|
|
916ee07265 | ||
|
|
1e67c7411c | ||
|
|
62ef8e17c7 | ||
|
|
327ef6b06c | ||
|
|
5dff7b2855 | ||
|
|
22b359a612 |
@@ -1,4 +1,4 @@
|
||||
# http://editorconfig.org
|
||||
# https://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
10
.eslintignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.cache
|
||||
docs/dist
|
||||
docs/search.json
|
||||
docs/**/*.min.js
|
||||
dist
|
||||
examples
|
||||
node_modules
|
||||
src/react
|
||||
scripts
|
||||
|
||||
193
.eslintrc.cjs
Normal file
@@ -0,0 +1,193 @@
|
||||
/* 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-parameter-properties': 'error',
|
||||
'@typescript-eslint/strict-boolean-expressions': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
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/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'
|
||||
}
|
||||
};
|
||||
40
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,38 +1,36 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us improve.
|
||||
about: Create a bug report to help us fix a demonstrable problem with code in the library.
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: claviska
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
### Describe the bug
|
||||
A bug is _a demonstrable problem_ caused by code in the library. Please provide a clear and concise description of what the bug is here.
|
||||
|
||||
**To Reproduce**
|
||||
### To Reproduce
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
2. Click on '...'
|
||||
3. Scroll down to '...'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
### Demo
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
If the bug isn't obvious, please provide a link to a CodePen or Fiddle with a minimal reproduction. Bugs that have repros get attention faster than those that don't.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
Tip: use the CodePen button on any example in the docs!
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
### Screenshots
|
||||
If applicable, add screenshots to help explain the bug.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
### Browser / OS
|
||||
- OS: [e.g. Mac, Windows]
|
||||
- Browser: [e.g. Chrome, Firefox, Safari]
|
||||
- Browser version: [e.g. 22]
|
||||
|
||||
### Additional information
|
||||
Provide any additional information about the bug here.
|
||||
|
||||
15
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -7,14 +7,11 @@ assignees: claviska
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
### What issue are you having?
|
||||
Provide a clear and concise description of the problem you're facing.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
### Describe the solution you'd like
|
||||
How would you like to see the library solve it?
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
### Describe alternatives you've considered
|
||||
In what ways have you tried to solve this with the current version?
|
||||
|
||||
2
.github/SECURITY.md
vendored
@@ -4,4 +4,4 @@ We take security issues in Shoelace very seriously and appreciate your efforts t
|
||||
|
||||
To report a security issue, email [cory@abeautifulsite.net](mailto:cory@abeautifulsite.net) and include "SHOELACE SECURITY" in the subject line.
|
||||
|
||||
Well respond as soon as possible and keep you updated throughout the process.
|
||||
We'll respond as soon as possible and keep you updated throughout the process.
|
||||
|
||||
30
.github/workflows/node.js.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# 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
|
||||
17
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# This workflow will create a GitHub release every time a tag is pushed
|
||||
name: Create GitHub Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v2.*"
|
||||
- "v3.*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: "marvinpinto/action-automatic-releases@v1.2.1"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
prerelease: false
|
||||
2
.gitignore
vendored
@@ -1,6 +1,8 @@
|
||||
.DS_Store
|
||||
.cache
|
||||
docs/dist
|
||||
docs/search.json
|
||||
dist
|
||||
examples
|
||||
node_modules
|
||||
src/react
|
||||
|
||||
28
.gitpod.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
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
|
||||
4
.husky/pre-commit
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install lint-staged
|
||||
@@ -1,9 +1,11 @@
|
||||
*.md
|
||||
*.hbs
|
||||
.cache
|
||||
.github
|
||||
cspell.json
|
||||
dist
|
||||
docs/*.md
|
||||
docs/search.json
|
||||
src/components/icon/icons
|
||||
src/react/index.ts
|
||||
node_modules
|
||||
package-lock.json
|
||||
tsconfig.json
|
||||
|
||||
9
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"bierner.lit-html",
|
||||
"bashmish.es6-string-css",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
]
|
||||
}
|
||||
7
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ Twitter: [@shoelace_style](https://twitter.com/shoelace_style)
|
||||
|
||||
## Shoemakers 🥾
|
||||
|
||||
Shoemakers, or "Shoelace developers," can use this documentation to learn how to build Shoelace from source. You will need Node >= 12.10.0 to build and run the project locally.
|
||||
Shoemakers, or "Shoelace developers," can use this documentation to learn how to build Shoelace from source. You will need Node >= 14.17 to build and run the project locally.
|
||||
|
||||
**You don't need to do any of this to use Shoelace!** This page is for people who want to contribute to the project, tinker with the source, or create a custom build of Shoelace.
|
||||
|
||||
@@ -51,9 +51,7 @@ Once you've cloned the repo, run the following command.
|
||||
npm start
|
||||
```
|
||||
|
||||
This will spin up the Shoelace 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. The exception is component metadata used by the docs, which is generated by TypeDoc. This tool takes a few seconds to run so, to prevent long reload delays, it only runs once at startup.
|
||||
This will spin up the Shoelace 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.
|
||||
|
||||
The documentation is powered by Docsify, which uses raw markdown files to generate pages. As such, no static files are built for the docs.
|
||||
|
||||
|
||||
154
cspell.json
Normal file
@@ -0,0 +1,154 @@
|
||||
{
|
||||
"version": "0.2",
|
||||
"words": [
|
||||
"activedescendant",
|
||||
"allowfullscreen",
|
||||
"animationend",
|
||||
"Animista",
|
||||
"apos",
|
||||
"atrule",
|
||||
"autocorrect",
|
||||
"autofix",
|
||||
"autoplay",
|
||||
"bezier",
|
||||
"boxicons",
|
||||
"callout",
|
||||
"callouts",
|
||||
"chatbubble",
|
||||
"checkmark",
|
||||
"claviska",
|
||||
"Clippy",
|
||||
"codebases",
|
||||
"codepen",
|
||||
"colocated",
|
||||
"colour",
|
||||
"combobox",
|
||||
"Composability",
|
||||
"Consolas",
|
||||
"contenteditable",
|
||||
"copydir",
|
||||
"coverpage",
|
||||
"crossorigin",
|
||||
"crutchcorn",
|
||||
"csspart",
|
||||
"cssproperty",
|
||||
"datetime",
|
||||
"describedby",
|
||||
"Docsify",
|
||||
"dropdowns",
|
||||
"easings",
|
||||
"enterkeyhint",
|
||||
"eqeqeq",
|
||||
"erroneou",
|
||||
"errormessage",
|
||||
"esbuild",
|
||||
"exportparts",
|
||||
"fieldsets",
|
||||
"formaction",
|
||||
"formdata",
|
||||
"formenctype",
|
||||
"formmethod",
|
||||
"formnovalidate",
|
||||
"formtarget",
|
||||
"FOUC",
|
||||
"FOUCE",
|
||||
"fullscreen",
|
||||
"giga",
|
||||
"globby",
|
||||
"Grayscale",
|
||||
"haspopup",
|
||||
"heroicons",
|
||||
"hexa",
|
||||
"Iconoir",
|
||||
"Iframes",
|
||||
"iife",
|
||||
"inputmode",
|
||||
"ionicon",
|
||||
"ionicons",
|
||||
"jsDelivr",
|
||||
"jsfiddle",
|
||||
"jsonata",
|
||||
"keydown",
|
||||
"keyframes",
|
||||
"labelledby",
|
||||
"Laravel",
|
||||
"LaViska",
|
||||
"listbox",
|
||||
"litelement",
|
||||
"lowercasing",
|
||||
"Lucide",
|
||||
"maxlength",
|
||||
"Menlo",
|
||||
"menuitemcheckbox",
|
||||
"menuitemradio",
|
||||
"middlewares",
|
||||
"minlength",
|
||||
"monospace",
|
||||
"mousedown",
|
||||
"mouseup",
|
||||
"multiselectable",
|
||||
"nextjs",
|
||||
"nocheck",
|
||||
"noopener",
|
||||
"noreferrer",
|
||||
"novalidate",
|
||||
"outdir",
|
||||
"ParamagicDev",
|
||||
"peta",
|
||||
"petabit",
|
||||
"progressbar",
|
||||
"radiogroup",
|
||||
"Railsbyte",
|
||||
"remixicon",
|
||||
"reregister",
|
||||
"resizer",
|
||||
"resizers",
|
||||
"rgba",
|
||||
"roadmap",
|
||||
"Roboto",
|
||||
"saturationl",
|
||||
"Schilp",
|
||||
"scrollbars",
|
||||
"Segoe",
|
||||
"semibold",
|
||||
"slotchange",
|
||||
"spacebar",
|
||||
"stylesheet",
|
||||
"Tabbable",
|
||||
"tabindex",
|
||||
"tabler",
|
||||
"tablist",
|
||||
"tabpanel",
|
||||
"templating",
|
||||
"tera",
|
||||
"textareas",
|
||||
"textfield",
|
||||
"tinycolor",
|
||||
"transitionend",
|
||||
"treeitem",
|
||||
"Triaging",
|
||||
"turbolinks",
|
||||
"typeof",
|
||||
"unbundles",
|
||||
"unbundling",
|
||||
"unicons",
|
||||
"unsupportive",
|
||||
"valpha",
|
||||
"valuenow",
|
||||
"valuetext",
|
||||
"WEBP",
|
||||
"Webpacker",
|
||||
"wordmark"
|
||||
],
|
||||
"ignorePaths": [
|
||||
"package.json",
|
||||
"package-lock.json",
|
||||
".vscode/**",
|
||||
"src/translations/!(en).ts",
|
||||
"**/*.min.js"
|
||||
],
|
||||
"ignoreRegExpList": [
|
||||
"(^|[^a-z])sl[a-z]*(^|[^a-z])"
|
||||
],
|
||||
"useGitignore": true
|
||||
}
|
||||
167
custom-elements-manifest.config.js
Normal file
@@ -0,0 +1,167 @@
|
||||
import { generateCustomData } from 'cem-plugin-vs-code-custom-data-generator';
|
||||
import { parse } from 'comment-parser';
|
||||
import { pascalCase } from 'pascal-case';
|
||||
import commandLineArgs from 'command-line-args';
|
||||
import fs from 'fs';
|
||||
|
||||
const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
||||
const { name, description, version, author, homepage, license } = packageData;
|
||||
|
||||
const { outdir } = commandLineArgs([
|
||||
{ name: 'litelement', type: String },
|
||||
{ name: 'analyze', defaultOption: true },
|
||||
{ name: 'outdir', type: String }
|
||||
]);
|
||||
|
||||
function noDash(string) {
|
||||
return string.replace(/^\s?-/, '').trim();
|
||||
}
|
||||
|
||||
function replace(string, terms) {
|
||||
terms.forEach(({ from, to }) => {
|
||||
string = string?.replace(from, to);
|
||||
});
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
export default {
|
||||
globs: ['src/components/**/*.ts'],
|
||||
exclude: ['**/*.styles.ts', '**/*.test.ts'],
|
||||
plugins: [
|
||||
// Append package data
|
||||
{
|
||||
name: 'shoelace-package-data',
|
||||
packageLinkPhase({ customElementsManifest }) {
|
||||
customElementsManifest.package = { name, description, version, author, homepage, license };
|
||||
}
|
||||
},
|
||||
|
||||
// Parse custom jsDoc tags
|
||||
{
|
||||
name: 'shoelace-custom-tags',
|
||||
analyzePhase({ ts, node, moduleDoc }) {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.ClassDeclaration: {
|
||||
const className = node.name.getText();
|
||||
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
|
||||
const customTags = ['animation', 'dependency', 'documentation', 'since', 'status', 'title'];
|
||||
let customComments = '/**';
|
||||
|
||||
node.jsDoc?.forEach(jsDoc => {
|
||||
jsDoc?.tags?.forEach(tag => {
|
||||
const tagName = tag.tagName.getText();
|
||||
|
||||
if (customTags.includes(tagName)) {
|
||||
customComments += `\n * @${tagName} ${tag.comment}`;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const parsed = parse(`${customComments}\n */`);
|
||||
parsed[0].tags?.forEach(t => {
|
||||
switch (t.tag) {
|
||||
// Animations
|
||||
case 'animation':
|
||||
if (!Array.isArray(classDoc['animations'])) {
|
||||
classDoc['animations'] = [];
|
||||
}
|
||||
classDoc['animations'].push({
|
||||
name: t.name,
|
||||
description: noDash(t.description)
|
||||
});
|
||||
break;
|
||||
|
||||
// Dependencies
|
||||
case 'dependency':
|
||||
if (!Array.isArray(classDoc['dependencies'])) {
|
||||
classDoc['dependencies'] = [];
|
||||
}
|
||||
classDoc['dependencies'].push(t.name);
|
||||
break;
|
||||
|
||||
// Value-only metadata tags
|
||||
case 'documentation':
|
||||
case 'since':
|
||||
case 'status':
|
||||
case 'title':
|
||||
classDoc[t.tag] = t.name;
|
||||
break;
|
||||
|
||||
// All other tags
|
||||
default:
|
||||
if (!Array.isArray(classDoc[t.tag])) {
|
||||
classDoc[t.tag] = [];
|
||||
}
|
||||
|
||||
classDoc[t.tag].push({
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
type: t.type || undefined
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'shoelace-react-event-names',
|
||||
analyzePhase({ ts, node, moduleDoc }) {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.ClassDeclaration: {
|
||||
const className = node.name.getText();
|
||||
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
|
||||
|
||||
if (classDoc?.events) {
|
||||
classDoc.events.forEach(event => {
|
||||
event.reactName = `on${pascalCase(event.name)}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'shoelace-translate-module-paths',
|
||||
packageLinkPhase({ customElementsManifest }) {
|
||||
customElementsManifest?.modules?.forEach(mod => {
|
||||
//
|
||||
// CEM paths look like this:
|
||||
//
|
||||
// src/components/button/button.ts
|
||||
//
|
||||
// But we want them to look like this:
|
||||
//
|
||||
// components/button/button.js
|
||||
//
|
||||
const terms = [
|
||||
{ from: /^src\//, to: '' }, // Strip the src/ prefix
|
||||
{ from: /\.(t|j)sx?$/, to: '.js' } // Convert .ts to .js
|
||||
];
|
||||
|
||||
mod.path = replace(mod.path, terms);
|
||||
|
||||
for (const ex of mod.exports ?? []) {
|
||||
ex.declaration.module = replace(ex.declaration.module, terms);
|
||||
}
|
||||
|
||||
for (const dec of mod.declarations ?? []) {
|
||||
if (dec.kind === 'class') {
|
||||
for (const member of dec.members ?? []) {
|
||||
if (member.inheritedFrom) {
|
||||
member.inheritedFrom.module = replace(member.inheritedFrom.module, terms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
// Generate custom VS Code data
|
||||
generateCustomData({
|
||||
outdir,
|
||||
cssFileName: null
|
||||
})
|
||||
]
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
# Not Found
|
||||
|
||||
Sorry, I couldn't find that page.
|
||||
<img class="not-found-image" src="/assets/images/undraw-not-found.svg" alt="Cute monsters hiding behind a tree">
|
||||
|
||||
Sorry, I couldn't find that page. Have you tried pressing <kbd>/</kbd> to search?
|
||||
|
||||
151
docs/_sidebar.md
@@ -1,74 +1,101 @@
|
||||
- Getting Started
|
||||
|
||||
- [Overview](/)
|
||||
- [Installation](/getting-started/installation.md)
|
||||
- [Usage](/getting-started/usage.md)
|
||||
- [Customizing](/getting-started/customizing.md)
|
||||
- [Themes](/getting-started/themes.md)
|
||||
- [Installation](/getting-started/installation)
|
||||
- [Usage](/getting-started/usage)
|
||||
- [Themes](/getting-started/themes)
|
||||
- [Customizing](/getting-started/customizing)
|
||||
- [Form Controls](/getting-started/form-controls)
|
||||
- [Localization](/getting-started/localization)
|
||||
|
||||
- Frameworks
|
||||
|
||||
- [React](/frameworks/react)
|
||||
- [Vue](/frameworks/vue)
|
||||
- [Angular](/frameworks/angular)
|
||||
|
||||
- Resources
|
||||
- [Community](/resources/community.md)
|
||||
- [Contributing](/resources/contributing.md)
|
||||
- [Changelog](/resources/changelog.md)
|
||||
|
||||
- [Community](/resources/community)
|
||||
- [Accessibility](/resources/accessibility)
|
||||
- [Contributing](/resources/contributing)
|
||||
- [Changelog](/resources/changelog)
|
||||
|
||||
- Components
|
||||
- [Alert](/components/alert.md)
|
||||
- [Avatar](/components/avatar.md)
|
||||
- [Badge](/components/badge.md)
|
||||
- [Button](/components/button.md)
|
||||
- [Button Group](/components/button-group.md)
|
||||
- [Card](/components/card.md)
|
||||
- [Checkbox](/components/checkbox.md)
|
||||
- [Color Picker](/components/color-picker.md)
|
||||
- [Details](/components/details.md)
|
||||
- [Dialog](/components/dialog.md)
|
||||
- [Drawer](/components/drawer.md)
|
||||
- [Dropdown](/components/dropdown.md)
|
||||
- [Form](/components/form.md)
|
||||
- [Icon](/components/icon.md)
|
||||
- [Icon Button](/components/icon-button.md)
|
||||
- [Image Comparer](/components/image-comparer.md)
|
||||
- [Input](/components/input.md)
|
||||
- [Menu](/components/menu.md)
|
||||
- [Menu Divider](/components/menu-divider.md)
|
||||
- [Menu Item](/components/menu-item.md)
|
||||
- [Menu Label](/components/menu-label.md)
|
||||
- [Progress Bar](/components/progress-bar.md)
|
||||
- [Progress Ring](/components/progress-ring.md)
|
||||
- [Radio](/components/radio.md)
|
||||
- [Radio Group](/components/radio-group.md)
|
||||
- [Range](/components/range.md)
|
||||
- [Rating](/components/rating.md)
|
||||
- [Select](/components/select.md)
|
||||
- [Skeleton](/components/skeleton.md)
|
||||
- [Spinner](/components/spinner.md)
|
||||
- [Switch](/components/switch.md)
|
||||
- [Tab Group](/components/tab-group.md)
|
||||
- [Tab](/components/tab.md)
|
||||
- [Tab Panel](/components/tab-panel.md)
|
||||
- [Tag](/components/tag.md)
|
||||
- [Textarea](/components/textarea.md)
|
||||
- [Tooltip](/components/tooltip.md)
|
||||
|
||||
- [Alert](/components/alert)
|
||||
- [Avatar](/components/avatar)
|
||||
- [Badge](/components/badge)
|
||||
- [Breadcrumb](/components/breadcrumb)
|
||||
- [Breadcrumb Item](/components/breadcrumb-item)
|
||||
- [Button](/components/button)
|
||||
- [Button Group](/components/button-group)
|
||||
- [Card](/components/card)
|
||||
- [Checkbox](/components/checkbox)
|
||||
- [Color Picker](/components/color-picker)
|
||||
- [Details](/components/details)
|
||||
- [Dialog](/components/dialog)
|
||||
- [Divider](/components/divider)
|
||||
- [Drawer](/components/drawer)
|
||||
- [Dropdown](/components/dropdown)
|
||||
- [Icon](/components/icon)
|
||||
- [Icon Button](/components/icon-button)
|
||||
- [Image Comparer](/components/image-comparer)
|
||||
- [Input](/components/input)
|
||||
- [Menu](/components/menu)
|
||||
- [Menu Item](/components/menu-item)
|
||||
- [Menu Label](/components/menu-label)
|
||||
- [Option](/components/option)
|
||||
- [Progress Bar](/components/progress-bar)
|
||||
- [Progress Ring](/components/progress-ring)
|
||||
- [QR Code](/components/qr-code)
|
||||
- [Radio](/components/radio)
|
||||
- [Radio Button](/components/radio-button)
|
||||
- [Radio Group](/components/radio-group)
|
||||
- [Range](/components/range)
|
||||
- [Rating](/components/rating)
|
||||
- [Select](/components/select)
|
||||
- [Skeleton](/components/skeleton)
|
||||
- [Spinner](/components/spinner)
|
||||
- [Split Panel](/components/split-panel)
|
||||
- [Switch](/components/switch)
|
||||
- [Tab Group](/components/tab-group)
|
||||
- [Tab](/components/tab)
|
||||
- [Tab Panel](/components/tab-panel)
|
||||
- [Tag](/components/tag)
|
||||
- [Textarea](/components/textarea)
|
||||
- [Tooltip](/components/tooltip)
|
||||
- [Tree](/components/tree)
|
||||
- [Tree Item](/components/tree-item)
|
||||
<!--plop:component-->
|
||||
|
||||
- Utilities
|
||||
- [Animation](/components/animation.md)
|
||||
- [Format Bytes](/components/format-bytes.md)
|
||||
- [Format Date](/components/format-date.md)
|
||||
- [Format Number](/components/format-number.md)
|
||||
- [Include](/components/include.md)
|
||||
- [QR Code](/components/qr-code.md)
|
||||
- [Relative Time](/components/relative-time.md)
|
||||
- [Resize Observer](/components/resize-observer.md)
|
||||
- [Responsive Media](/components/responsive-media.md)
|
||||
|
||||
- [Animated Image](/components/animated-image)
|
||||
- [Animation](/components/animation)
|
||||
- [Format Bytes](/components/format-bytes)
|
||||
- [Format Date](/components/format-date)
|
||||
- [Format Number](/components/format-number)
|
||||
- [Include](/components/include)
|
||||
- [Mutation Observer](/components/mutation-observer)
|
||||
- [Popup](/components/popup)
|
||||
- [Relative Time](/components/relative-time)
|
||||
- [Resize Observer](/components/resize-observer)
|
||||
- [Visually Hidden](/components/visually-hidden)
|
||||
|
||||
- Design Tokens
|
||||
- [Typography](/tokens/typography.md)
|
||||
- [Color](/tokens/color.md)
|
||||
- [Spacing](/tokens/spacing.md)
|
||||
- [Elevation](/tokens/elevation.md)
|
||||
- [Border Radius](/tokens/border-radius.md)
|
||||
- [Transition](/tokens/transition.md)
|
||||
- [Z-index](/tokens/z-index.md)
|
||||
|
||||
- [Typography](/tokens/typography)
|
||||
- [Color](/tokens/color)
|
||||
- [Spacing](/tokens/spacing)
|
||||
- [Elevation](/tokens/elevation)
|
||||
- [Border Radius](/tokens/border-radius)
|
||||
- [Transition](/tokens/transition)
|
||||
- [Z-index](/tokens/z-index)
|
||||
- [More](/tokens/more)
|
||||
|
||||
- Tutorials
|
||||
- [Integrating with NextJS](/tutorials/integrating-with-nextjs.md)
|
||||
- [Integrating with Rails](/tutorials/integrating-with-rails.md)
|
||||
|
||||
- [Integrating with Laravel](/tutorials/integrating-with-laravel)
|
||||
- [Integrating with NextJS](/tutorials/integrating-with-nextjs)
|
||||
- [Integrating with Rails](/tutorials/integrating-with-rails)
|
||||
|
||||
|
Before Width: | Height: | Size: 709 KiB After Width: | Height: | Size: 1.0 MiB |
@@ -1,6 +1,6 @@
|
||||
<svg viewBox="0 0 127 141" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g fill-rule="nonzero" fill="#409eff">
|
||||
<g fill-rule="nonzero" fill="#0ea5e9">
|
||||
<path d="M102.375,90.85 C102.979,90.557 103.57,90.215 104.15,89.826 L106.425,88.501 C106.848,88.19 107.64,87.573 108.8,86.65 L108.95,86.501 C109.883,85.567 110.916,85.117 112.05,85.15 C112.55,85.15 113.133,85.284 113.8,85.55 L122.3,78 C122.533,77.8 122.767,77.633 123,77.5 L123.05,77.5 C123.95,76.967 124.7,77 125.3,77.6 C126.367,78.166 126.45,79.133 125.55,80.5 L116.65,88.399 C116.717,88.533 116.75,88.666 116.75,88.799 C116.883,89.399 116.833,89.999 116.6,90.599 C116.4,90.999 116.117,91.382 115.75,91.749 C115.379,92.145 114.996,92.528 114.6,92.898 L109.45,96.648 C108.99,96.97 108.523,97.27 108.05,97.548 C107.46,97.906 106.86,98.232 106.25,98.523 C105.985,98.644 105.718,98.76 105.45,98.874 C103.841,99.559 102.174,100.017 100.45,100.249 C99.65,106.982 97.35,113.183 93.55,118.849 C88.75,125.915 82.183,131.299 73.85,134.999 C65.55,138.699 56.567,140.549 46.9,140.549 C33.567,140.415 22.75,137.415 14.45,131.549 C4.817,124.75 0,115.7 0,104.399 L0,102.849 C0.333,95.149 2.6,87.849 6.8,80.95 C10.933,74.116 16.983,69.5 24.95,67.1 C29.05,65.9 33.166,65.3 37.3,65.3 C41.567,65.3 45.967,66.083 50.5,67.65 C55.033,69.216 60.15,71.916 65.85,75.75 L80.7,85.7 C84.833,88.399 88.6,90.233 92,91.2 C91.3,84.3 88.8,78.399 84.5,73.5 C80.2,68.533 74.717,64.9 68.05,62.6 L61.65,60.4 C55.783,58.333 51.417,56.3 48.55,54.3 C40.817,49.067 36.517,41.883 35.65,32.75 L35.5,30.05 C35.5,21.25 39.067,13.883 46.2,7.95 C52.567,2.65 60.133,0 68.9,0 C75.5,0 81.417,1.9 86.65,5.7 C91.917,9.533 94.9,14.967 95.6,22 L95.75,24.75 C95.75,29.85 94.433,34.216 91.8,37.85 C89.1,41.483 86.717,43.3 84.65,43.3 C84.21,43.269 83.802,43.21 83.425,43.125 L74.1,51.9 C72.6,52.733 71.583,52.583 71.05,51.45 C70.517,50.85 70.567,50.1 71.2,49.2 L71.25,49.15 C71.383,48.95 71.567,48.733 71.8,48.5 L80.475,40.275 C80.376,39.872 80.318,39.431 80.3,38.95 C80.3,37.817 80.75,36.867 81.65,36.1 C85.45,32.7 87.35,28.784 87.35,24.35 C87.35,19.95 85.683,16.25 82.35,13.25 C79.017,10.25 74.467,8.716 68.7,8.65 C61.5,8.65 55.583,10.817 50.95,15.15 C46.317,19.483 44,24.683 44,30.75 C44,35.65 45.883,40.066 49.65,44 C53.383,47.9 59.15,50.966 66.95,53.2 C77.883,56.367 86.233,61.7 92,69.2 C97.133,75.833 100,83.283 100.6,91.55 C101.199,91.365 101.791,91.132 102.375,90.85 Z M71.95,49.05 C71.95,49.35 72.117,49.5 72.45,49.5 C72.483,49.5 75.117,47.066 80.35,42.2 C80.35,41.533 78.95,42.45 76.15,44.95 C73.35,47.483 71.95,48.85 71.95,49.05 Z M74.15,50.8 C74.15,50.533 74.017,50.4 73.75,50.4 C73.45,50.4 73.183,50.55 72.95,50.85 C72.416,50.817 72.033,50.884 71.8,51.05 C71.7,51.117 71.65,51.183 71.65,51.25 C71.65,51.45 71.783,51.6 72.05,51.7 L72.95,51.7 C73.75,51.4 74.15,51.1 74.15,50.8 Z M80.35,45.35 C80.35,44.583 79.9,44.583 79,45.35 C78.567,45.75 78.017,46.317 77.35,47.05 C77.117,47.217 76.633,47.667 75.9,48.4 C75.133,49.2 74.75,49.683 74.75,49.85 L74.8,50.2 C75,50.267 75.133,50.3 75.2,50.3 C75.233,50.3 76.1,49.5 77.8,47.9 C79.5,46.3 80.35,45.45 80.35,45.35 Z M124.2,78.3 L115.8,85.7 C116.3,85.967 116.667,86.349 116.9,86.849 L125.2,79.45 C125.266,79.116 125.217,78.849 125.05,78.649 C124.883,78.517 124.6,78.399 124.2,78.3 Z M123.75,78.05 L123.55,77.85 L116.15,83.85 L116.6,84.4 L123.75,78.05 Z M91.85,99.899 C89.65,99.333 87.617,98.649 85.75,97.849 C81.55,96.149 76.333,93.183 70.1,88.95 L59.85,82 C55.517,79.233 51.567,77.166 48,75.8 C44.4,74.467 40.867,73.8 37.4,73.8 L35.9,73.8 C27.067,74.267 20.2,77.583 15.3,83.75 C10.734,89.45 8.45,96.184 8.45,103.95 C8.45,112.683 11.95,119.516 18.95,124.45 C25.916,129.416 35.467,131.899 47.6,131.899 C57.133,131.899 65.166,130.2 71.7,126.799 C78.2,123.399 83.25,118.899 86.85,113.299 C89.55,109.033 91.217,104.566 91.85,99.899 Z"></path>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
23
docs/assets/images/open-in-gitpod.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
BIN
docs/assets/images/tie.webp
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 55 KiB |
@@ -5,7 +5,7 @@
|
||||
<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="#FFFFFF" x="180" y="16" width="579" height="343" rx="20"></rect>
|
||||
<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>
|
||||
@@ -22,7 +22,7 @@
|
||||
<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="#009DFF" fill-rule="nonzero"></path>
|
||||
<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>
|
||||
@@ -32,7 +32,7 @@
|
||||
<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="#009DFF" fill-rule="nonzero"></path>
|
||||
<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>
|
||||
@@ -41,7 +41,7 @@
|
||||
<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="#E6E6E6" fill-rule="nonzero"></path>
|
||||
<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>
|
||||
@@ -50,10 +50,10 @@
|
||||
<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="#009DFF" fill-rule="nonzero"></path>
|
||||
<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="#009DFF" 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>
|
||||
@@ -67,14 +67,14 @@
|
||||
<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="#009DFF" fill-rule="nonzero"></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="#3F3D56" 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="#F2F2F2" fill-rule="nonzero"></path>
|
||||
<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>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
1
docs/assets/images/undraw-not-found.svg
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
docs/assets/images/walk.gif
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
@@ -1,17 +1,17 @@
|
||||
.code-block {
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
background: var(--sl-color-gray-50);
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.code-block__preview {
|
||||
position: relative;
|
||||
border: solid 1px var(--sl-color-gray-200);
|
||||
border: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom: none;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
background-color: white;
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
min-width: 20rem;
|
||||
max-width: 100%;
|
||||
padding: 1.5rem 3.25rem 1.5rem 1.5rem;
|
||||
@@ -39,22 +39,14 @@
|
||||
bottom: 0;
|
||||
width: 1.75rem;
|
||||
font-size: 20px;
|
||||
color: var(--sl-color-gray-500);
|
||||
background-color: white;
|
||||
border-left: solid 1px var(--sl-color-gray-200);
|
||||
color: var(--sl-color-neutral-600);
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
border-left: solid 1px var(--sl-color-neutral-200);
|
||||
border-top-right-radius: 3px;
|
||||
cursor: ew-resize;
|
||||
transition: 250ms background-color;
|
||||
}
|
||||
|
||||
.code-block__resizer:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px var(--sl-color-primary-400), 0 0 0 var(--sl-focus-ring-width) var(--sl-focus-ring-color-primary);
|
||||
background-color: var(--sl-color-primary-50);
|
||||
color: var(--sl-color-primary-500);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.code-block__preview {
|
||||
padding-right: 1.5rem;
|
||||
@@ -66,102 +58,168 @@
|
||||
}
|
||||
|
||||
.code-block__source {
|
||||
border: solid 1px var(--sl-color-gray-200);
|
||||
border: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom: none;
|
||||
border-radius: 0 !important;
|
||||
margin: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.code-block__source .docsify-copy-code-button {
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.code-block--expanded .code-block__source {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.code-block__source pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.code-block__buttons {
|
||||
position: relative;
|
||||
border: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.code-block__button {
|
||||
flex: 0 0 auto;
|
||||
height: 2.5rem;
|
||||
min-width: 2.5rem;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: var(--sl-color-neutral-0);
|
||||
font: inherit;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
color: var(--sl-color-neutral-600);
|
||||
padding: 0 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.code-block__button:not(:last-of-type) {
|
||||
border-right: solid 1px var(--sl-color-neutral-200);
|
||||
}
|
||||
|
||||
.code-block__button--html,
|
||||
.code-block__button--react {
|
||||
width: 70px;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.code-block__button--selected {
|
||||
font-weight: 700;
|
||||
color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.code-block__button--codepen {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
width: 6rem;
|
||||
}
|
||||
|
||||
.code-block__button:first-of-type {
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
|
||||
.code-block__button:last-of-type {
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
.code-block__button:hover,
|
||||
.code-block__button:active {
|
||||
box-shadow: 0 0 0 1px var(--sl-color-primary-400);
|
||||
border-right-color: transparent;
|
||||
background-color: var(--sl-color-primary-50);
|
||||
color: var(--sl-color-primary-600);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.code-block__button:focus-visible {
|
||||
outline: none;
|
||||
outline: var(--sl-focus-ring);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.code-block__toggle {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 2.5rem;
|
||||
border: solid 1px var(--sl-color-gray-200);
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
background: var(--sl-color-white);
|
||||
font: inherit;
|
||||
font-size: 0.875rem;
|
||||
color: var(--sl-color-gray-600);
|
||||
color: var(--sl-color-neutral-600);
|
||||
cursor: pointer;
|
||||
transition: 250ms background-color;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.code-block__toggle:hover,
|
||||
.code-block__toggle:active {
|
||||
border-color: var(--sl-color-primary-400);
|
||||
background-color: var(--sl-color-primary-50);
|
||||
color: var(--sl-color-primary-500);
|
||||
}
|
||||
|
||||
.code-block__toggle:focus {
|
||||
outline: none;
|
||||
border-color: var(--sl-color-primary-400);
|
||||
background-color: var(--sl-color-primary-50);
|
||||
color: var(--sl-color-primary-500);
|
||||
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-focus-ring-color-primary);
|
||||
}
|
||||
|
||||
.code-block__toggle svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-left: 0.25rem;
|
||||
transition: 250ms transform;
|
||||
}
|
||||
|
||||
.code-block--expanded .code-block__toggle svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* Dark theme */
|
||||
.sl-theme-dark .code-block {
|
||||
background-color: var(--sl-color-gray-800);
|
||||
/* Copy button styles */
|
||||
.markdown-section .docsify-copy-code-button {
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
background-color: var(--sl-color-neutral-500);
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
font-family: var(--sl-font-sans);
|
||||
font-size: var(--sl-font-size-x-small);
|
||||
font-weight: var(--sl-font-weight-semibold);
|
||||
text-transform: uppercase;
|
||||
padding: 8px;
|
||||
user-select: none;
|
||||
transition: 0.1s all;
|
||||
}
|
||||
|
||||
.sl-theme-dark .code-block__preview {
|
||||
background-color: var(--sl-color-gray-900);
|
||||
border-color: var(--sl-color-gray-800);
|
||||
.markdown-section .docsify-copy-code-button.copied {
|
||||
animation: pulse 0.75s;
|
||||
--pulse-color: var(--sl-color-neutral-600);
|
||||
}
|
||||
|
||||
.sl-theme-dark .code-block__source {
|
||||
border-color: var(--sl-color-gray-800);
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 var(--pulse-color);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 0.5rem transparent;
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.sl-theme-dark .code-block__resizer {
|
||||
border-left-color: var(--sl-color-gray-800);
|
||||
background-color: var(--sl-color-gray-900);
|
||||
color: var(--sl-color-gray-400);
|
||||
.markdown-section .docsify-copy-code-button .label {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.sl-theme-dark .code-block__toggle {
|
||||
background-color: var(--sl-color-gray-900);
|
||||
border-color: var(--sl-color-gray-800);
|
||||
color: var(--sl-color-gray-400);
|
||||
.markdown-section .docsify-copy-code-button .success,
|
||||
.markdown-section .docsify-copy-code-button .error {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sl-theme-dark .code-block__toggle:hover,
|
||||
.sl-theme-dark .code-block__toggle:active {
|
||||
background-color: var(--sl-color-gray-900);
|
||||
border-color: var(--sl-color-gray-800);
|
||||
color: var(--sl-color-gray-400);
|
||||
.markdown-section .docsify-copy-code-button:focus-visible {
|
||||
outline: var(--sl-focus-ring);
|
||||
outline-offset: var(--sl-focus-ring-offset);
|
||||
}
|
||||
|
||||
.sl-theme-dark .code-block__toggle:focus {
|
||||
border-color: var(--sl-color-primary-500);
|
||||
background-color: var(--sl-color-primary-900);
|
||||
color: var(--sl-color-primary-500);
|
||||
.markdown-section .docsify-copy-code-button:active {
|
||||
background-color: var(--sl-color-neutral-600);
|
||||
transform: scale(0.92);
|
||||
}
|
||||
|
||||
/* We can apply data-flavor="html|react" to any element on the page to toggle it when the user's flavor changes */
|
||||
body.flavor-html [data-flavor]:not([data-flavor='html']) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.flavor-react [data-flavor]:not([data-flavor='react']) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,42 @@
|
||||
/* global Prism */
|
||||
|
||||
(() => {
|
||||
const reactVersion = '17.0.2';
|
||||
let flavor = getFlavor();
|
||||
let count = 1;
|
||||
|
||||
// Sync flavor UI on page load
|
||||
setFlavor(getFlavor());
|
||||
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
function convertModuleLinks(html) {
|
||||
const version = sessionStorage.getItem('sl-version');
|
||||
|
||||
html = html
|
||||
.replace(/@shoelace-style\/shoelace/g, `https://cdn.skypack.dev/@shoelace-style/shoelace@${version}`)
|
||||
.replace(/from 'react'/g, `from 'https://cdn.skypack.dev/react@${reactVersion}'`)
|
||||
.replace(/from "react"/g, `from "https://cdn.skypack.dev/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');
|
||||
|
||||
@@ -18,61 +50,138 @@
|
||||
script.parentNode.replaceChild(newScript, script);
|
||||
}
|
||||
|
||||
function wrap(el, wrapper) {
|
||||
el.parentNode.insertBefore(wrapper, el);
|
||||
wrapper.appendChild(el);
|
||||
function getFlavor() {
|
||||
return localStorage.getItem('flavor') || 'html';
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push((hook, vm) => {
|
||||
function setFlavor(newFlavor) {
|
||||
flavor = ['html', 'react'].includes(newFlavor) ? newFlavor : 'html';
|
||||
localStorage.setItem('flavor', flavor);
|
||||
|
||||
// Set the flavor class on the body
|
||||
document.body.classList.toggle('flavor-html', flavor === 'html');
|
||||
document.body.classList.toggle('flavor-react', flavor === 'react');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push(hook => {
|
||||
// Convert code blocks to previews
|
||||
hook.afterEach(function (html, next) {
|
||||
hook.afterEach((html, next) => {
|
||||
const domParser = new DOMParser();
|
||||
const doc = domParser.parseFromString(html, 'text/html');
|
||||
|
||||
[...doc.querySelectorAll('code[class^="lang-"]')].map(code => {
|
||||
if (code.classList.contains('preview')) {
|
||||
const pre = code.closest('pre');
|
||||
const preId = `code-block-preview-${count}`;
|
||||
const toggleId = `code-block-toggle-${count}`;
|
||||
const htmlButton = `
|
||||
<button
|
||||
type="button"
|
||||
title="Show HTML code"
|
||||
class="code-block__button code-block__button--html ${flavor === 'html' ? 'code-block__button--selected' : ''}"
|
||||
>
|
||||
HTML
|
||||
</button>
|
||||
`;
|
||||
|
||||
const reactButton = `
|
||||
<button
|
||||
type="button"
|
||||
title="Show React code"
|
||||
class="code-block__button code-block__button--react ${
|
||||
flavor === 'react' ? 'code-block__button--selected' : ''
|
||||
}"
|
||||
>
|
||||
React
|
||||
</button>
|
||||
`;
|
||||
|
||||
const codePenButton = `
|
||||
<button type="button" class="code-block__button code-block__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>
|
||||
`;
|
||||
|
||||
[...doc.querySelectorAll('code[class^="lang-"]')].forEach(code => {
|
||||
if (code.classList.contains('preview')) {
|
||||
const isExpanded = code.classList.contains('expanded');
|
||||
const pre = code.closest('pre');
|
||||
const sourceGroupId = `code-block-source-group-${count}`;
|
||||
const toggleId = `code-block-toggle-${count}`;
|
||||
const reactPre = getAdjacentExample('react', pre);
|
||||
const hasReact = reactPre !== null;
|
||||
|
||||
pre.id = preId;
|
||||
pre.classList.add('code-block__source');
|
||||
pre.setAttribute('data-lang', pre.getAttribute('data-lang').replace(/ preview$/, ''));
|
||||
pre.setAttribute('aria-labelledby', toggleId);
|
||||
|
||||
const codeBlock = `
|
||||
<div class="code-block">
|
||||
<div class="code-block ${isExpanded ? 'code-block--expanded' : ''}">
|
||||
<div class="code-block__preview">
|
||||
${code.textContent}
|
||||
<div class="code-block__resizer" role="slider" tabindex="0">
|
||||
<div class="code-block__resizer">
|
||||
<sl-icon name="grip-vertical"></sl-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${pre.outerHTML}
|
||||
<div class="code-block__source-group" id="${sourceGroupId}">
|
||||
<div class="code-block__source code-block__source--html" ${hasReact ? 'data-flavor="html"' : ''}>
|
||||
${pre.outerHTML}
|
||||
</div>
|
||||
|
||||
<div class="code-block__toggle" aria-expanded="false" aria-controls="${preId}">
|
||||
Source
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
${
|
||||
hasReact
|
||||
? `
|
||||
<div class="code-block__source code-block__source--react" data-flavor="react">
|
||||
${reactPre.outerHTML}
|
||||
</div>
|
||||
`
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="code-block__buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="code-block__button code-block__toggle"
|
||||
aria-expanded="${isExpanded ? 'true' : 'false'}"
|
||||
aria-controls="${sourceGroupId}"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
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>
|
||||
|
||||
${hasReact ? ` ${htmlButton} ${reactButton} ` : ''}
|
||||
|
||||
${!code.classList.contains('no-codepen') ? codePenButton : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
pre.replaceWith(domParser.parseFromString(codeBlock, 'text/html').body);
|
||||
reactPre?.remove();
|
||||
|
||||
count++;
|
||||
}
|
||||
});
|
||||
|
||||
// Force the highlighter to run again so JSX fields get highlighted properly
|
||||
requestAnimationFrame(() => Prism.highlightAll());
|
||||
|
||||
next(doc.body.innerHTML);
|
||||
});
|
||||
|
||||
@@ -83,66 +192,183 @@
|
||||
|
||||
// Horizontal resizing
|
||||
hook.doneEach(() => {
|
||||
[...document.querySelectorAll('.code-block__preview')].map(preview => {
|
||||
[...document.querySelectorAll('.code-block__preview')].forEach(preview => {
|
||||
const resizer = preview.querySelector('.code-block__resizer');
|
||||
let startX;
|
||||
let startWidth;
|
||||
|
||||
const dragStart = event => {
|
||||
function dragStart(event) {
|
||||
startX = event.changedTouches ? event.changedTouches[0].pageX : event.clientX;
|
||||
startWidth = parseInt(document.defaultView.getComputedStyle(preview).width, 10);
|
||||
preview.classList.add('code-block__preview--dragging');
|
||||
event.preventDefault();
|
||||
resizer.focus();
|
||||
document.documentElement.addEventListener('mousemove', dragMove, false);
|
||||
document.documentElement.addEventListener('touchmove', dragMove, false);
|
||||
document.documentElement.addEventListener('mouseup', dragStop, false);
|
||||
document.documentElement.addEventListener('touchend', dragStop, false);
|
||||
};
|
||||
document.documentElement.addEventListener('mousemove', dragMove);
|
||||
document.documentElement.addEventListener('touchmove', dragMove);
|
||||
document.documentElement.addEventListener('mouseup', dragStop);
|
||||
document.documentElement.addEventListener('touchend', dragStop);
|
||||
}
|
||||
|
||||
const dragMove = event => {
|
||||
function dragMove(event) {
|
||||
setWidth(startWidth + (event.changedTouches ? event.changedTouches[0].pageX : event.pageX) - startX);
|
||||
};
|
||||
}
|
||||
|
||||
const dragStop = event => {
|
||||
function dragStop() {
|
||||
preview.classList.remove('code-block__preview--dragging');
|
||||
document.documentElement.removeEventListener('mousemove', dragMove, false);
|
||||
document.documentElement.removeEventListener('touchmove', dragMove, false);
|
||||
document.documentElement.removeEventListener('mouseup', dragStop, false);
|
||||
document.documentElement.removeEventListener('touchend', dragStop, false);
|
||||
};
|
||||
document.documentElement.removeEventListener('mousemove', dragMove);
|
||||
document.documentElement.removeEventListener('touchmove', dragMove);
|
||||
document.documentElement.removeEventListener('mouseup', dragStop);
|
||||
document.documentElement.removeEventListener('touchend', dragStop);
|
||||
}
|
||||
|
||||
const handleKeyDown = event => {
|
||||
if (['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) {
|
||||
const currentWidth = preview.clientWidth;
|
||||
const maxWidth = preview.parentElement.clientWidth;
|
||||
const incr = event.shiftKey ? 100 : 10;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if (event.key === 'ArrowLeft') setWidth(currentWidth - incr);
|
||||
if (event.key === 'ArrowRight') setWidth(currentWidth + incr);
|
||||
if (event.key === 'Home') setWidth(0);
|
||||
if (event.key === 'End') setWidth(maxWidth);
|
||||
}
|
||||
};
|
||||
|
||||
const setWidth = width => (preview.style.width = width + 'px');
|
||||
function setWidth(width) {
|
||||
preview.style.width = `${width}px`;
|
||||
}
|
||||
|
||||
resizer.addEventListener('mousedown', dragStart);
|
||||
resizer.addEventListener('touchstart', dragStart);
|
||||
resizer.addEventListener('keydown', handleKeyDown);
|
||||
resizer.addEventListener('touchstart', dragStart, { passive: true });
|
||||
}, false);
|
||||
});
|
||||
});
|
||||
|
||||
// Expand and collapse code blocks
|
||||
// Toggle source mode
|
||||
document.addEventListener('click', event => {
|
||||
const toggle = event.target.closest('.code-block__toggle');
|
||||
const button = event.target.closest('.code-block__button');
|
||||
const codeBlock = button?.closest('.code-block');
|
||||
|
||||
if (button?.classList.contains('code-block__button--html')) {
|
||||
// Show HTML
|
||||
setFlavor('html');
|
||||
toggleSource(codeBlock, true);
|
||||
} else if (button?.classList.contains('code-block__button--react')) {
|
||||
// Show React
|
||||
setFlavor('react');
|
||||
toggleSource(codeBlock, true);
|
||||
} else if (button?.classList.contains('code-block__toggle')) {
|
||||
// Toggle source
|
||||
toggleSource(codeBlock);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update flavor buttons
|
||||
[...document.querySelectorAll('.code-block')].forEach(cb => {
|
||||
cb.querySelector('.code-block__button--html')?.classList.toggle(
|
||||
'code-block__button--selected',
|
||||
flavor === 'html'
|
||||
);
|
||||
cb.querySelector('.code-block__button--react')?.classList.toggle(
|
||||
'code-block__button--selected',
|
||||
flavor === 'react'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function toggleSource(codeBlock, force) {
|
||||
const toggle = codeBlock.querySelector('.code-block__toggle');
|
||||
|
||||
if (toggle) {
|
||||
const codeBlock = event.target.closest('.code-block');
|
||||
codeBlock.classList.toggle('code-block--expanded');
|
||||
codeBlock.classList.toggle('code-block--expanded', force === undefined ? undefined : force);
|
||||
event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-block--expanded'));
|
||||
}
|
||||
}
|
||||
|
||||
// Show pulse when copying
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('.docsify-copy-code-button');
|
||||
if (button) {
|
||||
button.classList.remove('copied');
|
||||
requestAnimationFrame(() => {
|
||||
button.addEventListener('animationend', () => button.classList.remove('copied'), { once: true });
|
||||
button.classList.add('copied');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Open in CodePen
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('button');
|
||||
const version = sessionStorage.getItem('sl-version');
|
||||
|
||||
if (button?.classList.contains('code-block__button--codepen')) {
|
||||
const codeBlock = button.closest('.code-block');
|
||||
const htmlExample = codeBlock.querySelector('.code-block__source--html > pre > code')?.textContent;
|
||||
const reactExample = codeBlock.querySelector('.code-block__source--react > pre > code')?.textContent;
|
||||
const isReact = flavor === 'react' && typeof reactExample === 'string';
|
||||
const theme = localStorage.getItem('theme');
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const isDark = theme === 'dark' || (theme === 'auto' && prefersDark);
|
||||
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@${version}/dist/shoelace.js"></script>\n` +
|
||||
`\n${htmlExample}`;
|
||||
jsTemplate = '';
|
||||
}
|
||||
|
||||
// React templates
|
||||
if (isReact) {
|
||||
htmlTemplate = '<div id="root"></div>';
|
||||
jsTemplate =
|
||||
`import React from 'https://cdn.skypack.dev/react@${reactVersion}';\n` +
|
||||
`import ReactDOM from 'https://cdn.skypack.dev/react-dom@${reactVersion}';\n` +
|
||||
`import { setBasePath } from 'https://cdn.skypack.dev/@shoelace-style/shoelace@${version}/dist/utilities/base-path';\n` +
|
||||
`\n` +
|
||||
`// Set the base path for Shoelace assets\n` +
|
||||
`setBasePath('https://cdn.skypack.dev/@shoelace-style/shoelace@${version}/dist/')\n` +
|
||||
`\n${convertModuleLinks(reactExample)}\n` +
|
||||
`\n` +
|
||||
`ReactDOM.render(<App />, document.getElementById('root'));`;
|
||||
}
|
||||
|
||||
// CSS templates
|
||||
cssTemplate =
|
||||
`@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/themes/${
|
||||
isDark ? 'dark' : 'light'
|
||||
}.css';\n` +
|
||||
'\n' +
|
||||
'body {\n' +
|
||||
' font: 16px sans-serif;\n' +
|
||||
' background-color: var(--sl-color-neutral-0);\n' +
|
||||
' color: var(--sl-color-neutral-900);\n' +
|
||||
' padding: 1rem;\n' +
|
||||
'}';
|
||||
|
||||
// Docs: https://blog.codepen.io/documentation/prefill/
|
||||
const data = {
|
||||
title: '',
|
||||
description: '',
|
||||
tags: ['shoelace', 'web components'],
|
||||
editors,
|
||||
head: `<meta name="viewport" content="width=device-width">`,
|
||||
html_classes: `sl-theme-${isDark ? 'dark' : 'light'}`,
|
||||
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.body.append(form);
|
||||
form.submit();
|
||||
form.remove();
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
(() => {
|
||||
let metadataStore;
|
||||
|
||||
function getAttrName(propName) {
|
||||
return propName.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`).replace(/^-/, '');
|
||||
}
|
||||
const isDev = location.hostname === 'localhost';
|
||||
const isNext = location.hostname === 'next.shoelace.style';
|
||||
const customElements = fetch('/dist/custom-elements.json')
|
||||
.then(res => res.json())
|
||||
.catch(err => console.error(err));
|
||||
|
||||
function createPropsTable(props) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Reflects</th>
|
||||
<th>Type</th>
|
||||
<th>Default</th>
|
||||
</tr>
|
||||
@@ -19,31 +21,54 @@
|
||||
<tbody>
|
||||
${props
|
||||
.map(prop => {
|
||||
const attr = getAttrName(prop.name);
|
||||
const hasAttribute = !!prop.attribute;
|
||||
const isAttributeDifferent = prop.attribute !== prop.name;
|
||||
let attributeInfo = '';
|
||||
|
||||
if (!hasAttribute) {
|
||||
attributeInfo = `<br><small>(property only)</small>`;
|
||||
} else if (isAttributeDifferent) {
|
||||
attributeInfo = `
|
||||
<br>
|
||||
<sl-tooltip content="This attribute is different from its property">
|
||||
<small>
|
||||
<code class="nowrap">
|
||||
${escapeHtml(prop.attribute)}
|
||||
</code>
|
||||
</small>
|
||||
</sl-tooltip>`;
|
||||
}
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td>
|
||||
<code>${escapeHtml(prop.name)}</code>
|
||||
${
|
||||
prop.name !== attr
|
||||
? `
|
||||
<br>
|
||||
<small>
|
||||
<sl-tooltip content="Use this attribute in your HTML">
|
||||
<code class="attribute-tooltip">${escapeHtml(attr)}</code>
|
||||
</sl-tooltip>
|
||||
</small>`
|
||||
: ''
|
||||
}
|
||||
<code class="nowrap">${escapeHtml(prop.name)}</code>
|
||||
${attributeInfo}
|
||||
</td>
|
||||
<td>${escapeHtml(prop.description)}</td>
|
||||
<td><code style="white-space: normal;">${escapeHtml(prop.type)}</code></td>
|
||||
<td><code style="white-space: normal;">${escapeHtml(prop.defaultValue)}</code></td>
|
||||
<td>
|
||||
${escapeHtml(prop.description)}
|
||||
</td>
|
||||
<td style="text-align: center;">${
|
||||
prop.reflects ? '<sl-icon label="yes" name="check-lg"></sl-icon>' : ''
|
||||
}</td>
|
||||
<td>${prop.type?.text ? `<code>${escapeHtml(prop.type?.text || '')}</code>` : '-'}</td>
|
||||
<td>${prop.default ? `<code>${escapeHtml(prop.default)}</code>` : '-'}</td>
|
||||
</tr>
|
||||
`;
|
||||
`;
|
||||
})
|
||||
.join('')}
|
||||
</tbody>
|
||||
|
||||
<tr>
|
||||
<td class="nowrap"><code>updateComplete</code></td>
|
||||
<td>
|
||||
A read-only promise that resolves when the component has
|
||||
<a href="/getting-started/usage?id=component-rendering-and-updating">finished updating</a>.
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
`;
|
||||
|
||||
return table.outerHTML;
|
||||
@@ -51,24 +76,27 @@
|
||||
|
||||
function createEventsTable(events) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Event</th>
|
||||
<th data-flavor="html">Name</th>
|
||||
<th data-flavor="react">React Event</th>
|
||||
<th>Description</th>
|
||||
<th>Details</th>
|
||||
<th>Event Detail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${events
|
||||
.map(
|
||||
event => `
|
||||
<tr>
|
||||
<td><code>${escapeHtml(event.name)}</code></td>
|
||||
<td>${escapeHtml(event.description)}</td>
|
||||
<td><code style="white-space: normal;">${escapeHtml(event.details)}</code></td>
|
||||
</tr>
|
||||
`
|
||||
<tr>
|
||||
<td data-flavor="html"><code class="nowrap">${escapeHtml(event.name)}</code></td>
|
||||
<td data-flavor="react"><code class="nowrap">${escapeHtml(event.reactName)}</code></td>
|
||||
<td>${escapeHtml(event.description)}</td>
|
||||
<td>${event.type?.text ? `<code>${escapeHtml(event.type?.text)}` : '-'}</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</tbody>
|
||||
@@ -79,10 +107,11 @@
|
||||
|
||||
function createMethodsTable(methods) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Method</th>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Arguments</th>
|
||||
</tr>
|
||||
@@ -92,17 +121,17 @@
|
||||
.map(
|
||||
method => `
|
||||
<tr>
|
||||
<td><code>${escapeHtml(method.name)}</code></td>
|
||||
<td class="nowrap"><code>${escapeHtml(method.name)}()</code></td>
|
||||
<td>${escapeHtml(method.description)}</td>
|
||||
<td>
|
||||
${
|
||||
method.params.length
|
||||
method.parameters?.length
|
||||
? `
|
||||
<code style="white-space: normal;">${escapeHtml(
|
||||
method.params.map(param => `${param.name}: ${param.type}`).join(', ')
|
||||
<code>${escapeHtml(
|
||||
method.parameters.map(param => `${param.name}: ${param.type?.text || ''}`).join(', ')
|
||||
)}</code>
|
||||
`
|
||||
: ''
|
||||
: '-'
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -117,10 +146,11 @@
|
||||
|
||||
function createSlotsTable(slots) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Slot</th>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -128,11 +158,11 @@
|
||||
${slots
|
||||
.map(
|
||||
slot => `
|
||||
<tr>
|
||||
<td><code>${slot.name ? escapeHtml(slot.name) : '(default)'}</code></td>
|
||||
<td>${escapeHtml(slot.description)}</td>
|
||||
</tr>
|
||||
`
|
||||
<tr>
|
||||
<td class="nowrap">${slot.name ? `<code>${escapeHtml(slot.name)}</code>` : '(default)'}</td>
|
||||
<td>${escapeHtml(slot.description)}</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</tbody>
|
||||
@@ -143,22 +173,25 @@
|
||||
|
||||
function createCustomPropertiesTable(styles) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${styles
|
||||
.map(
|
||||
style => `
|
||||
<tr>
|
||||
<td><code>${escapeHtml(style.name)}</code></td>
|
||||
<td>${escapeHtml(style.description)}</td>
|
||||
</tr>
|
||||
`
|
||||
<tr>
|
||||
<td class="nowrap"><code>${escapeHtml(style.name)}</code></td>
|
||||
<td>${escapeHtml(style.description)}</td>
|
||||
<td>${style.default ? `<code>${escapeHtml(style.default)}</code>` : ''}</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</tbody>
|
||||
@@ -169,6 +202,7 @@
|
||||
|
||||
function createPartsTable(parts) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -180,11 +214,38 @@
|
||||
${parts
|
||||
.map(
|
||||
part => `
|
||||
<tr>
|
||||
<td class="nowrap"><code>${escapeHtml(part.name)}</code></td>
|
||||
<td>${escapeHtml(part.description)}</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</tbody>
|
||||
`;
|
||||
|
||||
return table.outerHTML;
|
||||
}
|
||||
|
||||
function createAnimationsTable(animations) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<td><code>${escapeHtml(part.name)}</code></td>
|
||||
<td>${escapeHtml(part.description)}</td>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
`
|
||||
</thead>
|
||||
<tbody>
|
||||
${animations
|
||||
.map(
|
||||
animation => `
|
||||
<tr>
|
||||
<td class="nowrap"><code>${escapeHtml(animation.name)}</code></td>
|
||||
<td>${escapeHtml(animation.description)}</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</tbody>
|
||||
@@ -197,23 +258,23 @@
|
||||
const ul = document.createElement('ul');
|
||||
const dependencies = [];
|
||||
|
||||
// Recursively fetch subdependencies
|
||||
// Recursively fetch sub-dependencies
|
||||
function getDependencies(tag) {
|
||||
const component = allComponents.find(c => c.tag === tag);
|
||||
const component = allComponents.find(c => c.tagName === tag);
|
||||
if (!component || !Array.isArray(component.dependencies)) {
|
||||
return [];
|
||||
return;
|
||||
}
|
||||
|
||||
component.dependencies.map(tag => {
|
||||
if (!dependencies.includes(tag)) {
|
||||
dependencies.push(tag);
|
||||
component.dependencies?.forEach(dependentTag => {
|
||||
if (!dependencies.includes(dependentTag)) {
|
||||
dependencies.push(dependentTag);
|
||||
}
|
||||
getDependencies(tag);
|
||||
getDependencies(dependentTag);
|
||||
});
|
||||
}
|
||||
|
||||
getDependencies(targetComponent);
|
||||
dependencies.sort().map(tag => {
|
||||
dependencies.sort().forEach(tag => {
|
||||
const li = document.createElement('li');
|
||||
li.innerHTML = `<code><${tag}></code>`;
|
||||
ul.appendChild(li);
|
||||
@@ -223,102 +284,124 @@
|
||||
}
|
||||
|
||||
function escapeHtml(html) {
|
||||
return (html + '')
|
||||
if (!html) {
|
||||
return '';
|
||||
}
|
||||
return html
|
||||
.toString()
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" rel="noopener noreferrer" target="_blank">$1</a>')
|
||||
.replace(/`(.*?)`/g, '<code>$1</code>');
|
||||
}
|
||||
|
||||
function getMetadata() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Simple caching to prevent multiple XHR requests
|
||||
if (metadataStore) {
|
||||
return resolve(metadataStore);
|
||||
}
|
||||
function getAllComponents(metadata) {
|
||||
const allComponents = [];
|
||||
metadata.modules?.forEach(module => {
|
||||
module.declarations?.forEach(declaration => {
|
||||
if (declaration.customElement) {
|
||||
// Generate the dist path based on the src path and attach it to the component
|
||||
declaration.path = module.path.replace(/^src\//, 'dist/').replace(/\.ts$/, '.js');
|
||||
|
||||
fetch('/dist/metadata.json')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
metadataStore = data;
|
||||
resolve(metadataStore);
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
allComponents.push(declaration);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return allComponents;
|
||||
}
|
||||
|
||||
function getComponent(metadata, tagName) {
|
||||
return getAllComponents(metadata).find(component => component.tagName === tagName);
|
||||
}
|
||||
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push((hook, vm) => {
|
||||
hook.mounted(function () {
|
||||
getMetadata().then(metadata => {
|
||||
const target = document.querySelector('.app-name');
|
||||
window.$docsify.plugins.push(hook => {
|
||||
hook.mounted(async () => {
|
||||
const metadata = await customElements;
|
||||
const target = document.querySelector('.app-name');
|
||||
|
||||
// Add version
|
||||
const version = document.createElement('div');
|
||||
version.classList.add('sidebar-version');
|
||||
version.textContent = metadata.version;
|
||||
target.appendChild(version);
|
||||
// Add version
|
||||
const version = document.createElement('div');
|
||||
version.classList.add('sidebar-version');
|
||||
version.textContent = isDev ? 'Development' : isNext ? 'Next' : metadata.package.version;
|
||||
target.appendChild(version);
|
||||
|
||||
// Add repo buttons
|
||||
const buttons = document.createElement('div');
|
||||
buttons.classList.add('sidebar-buttons');
|
||||
buttons.innerHTML = `
|
||||
<a class="repo-button repo-button--small repo-button--sponsor" href="https://github.com/sponsors/claviska" rel="noopener" target="_blank">
|
||||
<sl-icon name="heart"></sl-icon> Sponsor
|
||||
</a>
|
||||
<a class="repo-button repo-button--small repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" rel="noopener" target="_blank">
|
||||
<sl-icon name="github"></sl-icon> <span class="github-star-count">Star</span>
|
||||
</a>
|
||||
<a class="repo-button repo-button--small repo-button--twitter" href="https://twitter.com/shoelace_style" rel="noopener" target="_blank">
|
||||
<sl-icon name="twitter"></sl-icon> Follow
|
||||
</a>
|
||||
`;
|
||||
target.appendChild(buttons);
|
||||
});
|
||||
// Store version for reuse
|
||||
sessionStorage.setItem('sl-version', metadata.package.version);
|
||||
|
||||
// Add repo buttons
|
||||
const buttons = document.createElement('div');
|
||||
buttons.classList.add('sidebar-buttons');
|
||||
buttons.innerHTML = `
|
||||
<sl-button size="small" class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
|
||||
<sl-icon slot="prefix" name="heart"></sl-icon> Sponsor
|
||||
</sl-button>
|
||||
<sl-button size="small" class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
|
||||
<sl-icon slot="prefix" name="github"></sl-icon> Star
|
||||
</sl-button>
|
||||
<sl-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
|
||||
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
|
||||
</sl-button>
|
||||
`;
|
||||
target.appendChild(buttons);
|
||||
});
|
||||
|
||||
hook.beforeEach(async function (content, next) {
|
||||
const metadata = await getMetadata();
|
||||
hook.beforeEach(async (content, next) => {
|
||||
const metadata = await customElements;
|
||||
|
||||
// Replace %VERSION% placeholders
|
||||
content = content.replace(/%VERSION%/g, metadata.version);
|
||||
content = content.replace(/%VERSION%/g, metadata.package.version);
|
||||
|
||||
// Handle [component-header] tags
|
||||
content = content.replace(/\[component-header:([a-z-]+)\]/g, (match, tag) => {
|
||||
const component = metadata.components.filter(data => data.tag === tag)[0];
|
||||
const component = getComponent(metadata, tag);
|
||||
let result = '';
|
||||
|
||||
if (!component) {
|
||||
console.error('Component not found in metadata: ' + tag);
|
||||
next(content);
|
||||
console.error(`Component not found in metadata: ${tag}`);
|
||||
return next(content);
|
||||
}
|
||||
|
||||
let badgeType = 'info';
|
||||
if (component.status === 'stable') badgeType = 'primary';
|
||||
if (component.status === 'experimental') badgeType = 'warning';
|
||||
if (component.status === 'planned') badgeType = 'info';
|
||||
if (component.status === 'deprecated') badgeType = 'danger';
|
||||
let badgeType = 'neutral';
|
||||
if (component.status === 'stable') {
|
||||
badgeType = 'primary';
|
||||
}
|
||||
if (component.status === 'experimental') {
|
||||
badgeType = 'warning';
|
||||
}
|
||||
if (component.status === 'planned') {
|
||||
badgeType = 'neutral';
|
||||
}
|
||||
if (component.status === 'deprecated') {
|
||||
badgeType = 'danger';
|
||||
}
|
||||
|
||||
result += `
|
||||
<div class="component-header">
|
||||
<div class="component-header__tag">
|
||||
<code>${component.className} | <${component.tag}></code>
|
||||
<code><${component.tagName}> | ${component.title ?? component.name}</code>
|
||||
</div>
|
||||
|
||||
<div class="component-header__info">
|
||||
<sl-badge type="info" pill>
|
||||
<sl-badge variant="neutral" pill>
|
||||
Since ${component.since || '?'}
|
||||
</sl-badge>
|
||||
|
||||
<sl-badge type="${badgeType}" pill style="text-transform: capitalize;">
|
||||
<sl-badge variant="${badgeType}" pill style="text-transform: capitalize;">
|
||||
${component.status}
|
||||
</sl-badge>
|
||||
</div>
|
||||
|
||||
<div class="component-header__summary">
|
||||
${component.summary ? `<p>${marked(component.summary)}</p>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -327,64 +410,163 @@
|
||||
|
||||
// Handle [component-metadata] tags
|
||||
content = content.replace(/\[component-metadata:([a-z-]+)\]/g, (match, tag) => {
|
||||
const component = metadata.components.filter(data => data.tag === tag)[0];
|
||||
const component = getComponent(metadata, tag);
|
||||
let result = '';
|
||||
|
||||
if (!component) {
|
||||
console.error('Component not found in metadata: ' + tag);
|
||||
next(content);
|
||||
console.error(`Component not found in metadata: ${tag}`);
|
||||
return next(content);
|
||||
}
|
||||
|
||||
if (component.props.length) {
|
||||
// Remove members that are private or don't have a description
|
||||
const members = component.members?.filter(member => member.description && member.privacy !== 'private');
|
||||
const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private');
|
||||
const props = members?.filter(prop => {
|
||||
// Look for a corresponding attribute
|
||||
const attribute = component.attributes?.find(attr => attr.fieldName === prop.name);
|
||||
if (attribute) {
|
||||
prop.attribute = attribute.name || attribute.fieldName;
|
||||
}
|
||||
|
||||
return prop.kind === 'field' && prop.privacy !== 'private';
|
||||
});
|
||||
|
||||
if (component.path) {
|
||||
/* prettier-ignore */
|
||||
result += `
|
||||
## Properties
|
||||
${createPropsTable(component.props)}
|
||||
## Importing
|
||||
|
||||
<sl-tab-group>
|
||||
<sl-tab slot="nav" panel="script">Script</sl-tab>
|
||||
<sl-tab slot="nav" panel="import">Import</sl-tab>
|
||||
<sl-tab slot="nav" panel="bundler">Bundler</sl-tab>
|
||||
<sl-tab slot="nav" panel="react">React</sl-tab>
|
||||
|
||||
<sl-tab-panel name="script">\n
|
||||
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) using a script tag:
|
||||
|
||||
\`\`\`html
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/dist/${component.path}"></script>
|
||||
\`\`\`
|
||||
</sl-tab-panel>
|
||||
|
||||
<sl-tab-panel name="import">\n
|
||||
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) using a JavaScript import:
|
||||
|
||||
\`\`\`js
|
||||
import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/dist/${component.path}';
|
||||
\`\`\`
|
||||
</sl-tab-panel>
|
||||
|
||||
<sl-tab-panel name="bundler">\n
|
||||
To import this component using [a bundler](/getting-started/installation#bundling):
|
||||
\`\`\`js
|
||||
import '@shoelace-style/shoelace/dist/${component.path}';
|
||||
\`\`\`
|
||||
</sl-tab-panel>
|
||||
|
||||
<sl-tab-panel name="react">\n
|
||||
To import this component as a [React component](/frameworks/react):
|
||||
\`\`\`js
|
||||
import { ${component.name} } from '@shoelace-style/shoelace/dist/react';
|
||||
\`\`\`
|
||||
</sl-tab-panel>
|
||||
</sl-tab-group>
|
||||
|
||||
<div class="sponsor-callout">
|
||||
<p>
|
||||
Shoelace is designed, developed, and maintained by <a href="https://twitter.com/claviska" target="_blank">Cory LaViska</a>.
|
||||
Please sponsor my open source work on GitHub. Your support will keep this project alive and growing!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<sl-button class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
|
||||
<sl-icon slot="prefix" name="heart"></sl-icon> Sponsor <span class="sponsor-callout__secondary-label">Development</span>
|
||||
</sl-button>
|
||||
|
||||
<sl-button class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
|
||||
<sl-icon slot="prefix" name="github"></sl-icon> Star <span class="sponsor-callout__secondary-label">on GitHub</span>
|
||||
</sl-button>
|
||||
|
||||
<sl-button class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
|
||||
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow <span class="sponsor-callout__secondary-label">on Twitter</span>
|
||||
</sl-button>
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.events.length) {
|
||||
result += `
|
||||
## Events
|
||||
${createEventsTable(component.events)}
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.methods.length) {
|
||||
result += `
|
||||
## Methods
|
||||
${createMethodsTable(component.methods)}
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.slots.length) {
|
||||
if (component.slots?.length) {
|
||||
result += `
|
||||
## Slots
|
||||
${createSlotsTable(component.slots)}
|
||||
|
||||
_Learn more about [using slots](/getting-started/usage#slots)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.cssCustomProperties.length) {
|
||||
if (props?.length) {
|
||||
result += `
|
||||
## Attributes & Properties
|
||||
${createPropsTable(props)}
|
||||
|
||||
_Learn more about [attributes and properties](/getting-started/usage#properties)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.events?.length) {
|
||||
result += `
|
||||
## Events
|
||||
${createEventsTable(component.events)}
|
||||
|
||||
_Learn more about [listening to events](/getting-started/usage#events)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (methods?.length) {
|
||||
result += `
|
||||
## Methods
|
||||
|
||||
${createMethodsTable(methods)}
|
||||
|
||||
_Learn more about [calling methods](/getting-started/usage#methods)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.cssProperties?.length) {
|
||||
result += `
|
||||
## CSS Custom Properties
|
||||
${createCustomPropertiesTable(component.cssCustomProperties)}
|
||||
${createCustomPropertiesTable(component.cssProperties)}
|
||||
|
||||
_Learn more about [customizing CSS Custom Properties](/getting-started/customizing#custom-properties)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.parts.length) {
|
||||
if (component.cssParts?.length) {
|
||||
result += `
|
||||
## CSS Parts
|
||||
${createPartsTable(component.parts)}
|
||||
${createPartsTable(component.cssParts)}
|
||||
|
||||
_Learn more about [customizing CSS Parts](/getting-started/customizing#component-parts)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.dependencies.length) {
|
||||
if (component.animations?.length) {
|
||||
result += `
|
||||
## Animations
|
||||
${createAnimationsTable(component.animations)}
|
||||
|
||||
_Learn more about [customizing animations](/getting-started/customizing#animations)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.dependencies?.length) {
|
||||
result += `
|
||||
## Dependencies
|
||||
|
||||
This component has the following dependencies so, if you're [cherry picking](/getting-started/installation#cherry-picking),
|
||||
be sure to import these components in addition to <code><${tag}></code>.
|
||||
This component automatically imports the following dependencies.
|
||||
|
||||
${createDependenciesList(component.tag, metadata.components)}
|
||||
${createDependenciesList(component.tagName, getAllComponents(metadata))}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -394,5 +576,19 @@
|
||||
|
||||
next(content);
|
||||
});
|
||||
|
||||
// Wrap tables so we can scroll them horizontally when needed
|
||||
hook.doneEach(() => {
|
||||
const content = document.querySelector('.content');
|
||||
const tables = [...content.querySelectorAll('table')];
|
||||
|
||||
tables.forEach(table => {
|
||||
table.outerHTML = `
|
||||
<div class="table-wrapper">
|
||||
${table.outerHTML}
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// Docsify generates pages dynamically and asynchronously, so when a reload happens, the scroll position can't be
|
||||
// be restored immediately. This plugin waits until Docsify loads the page and then restores it.
|
||||
//
|
||||
window.$docsify.plugins.push((hook, vm) => {
|
||||
window.$docsify.plugins.push(hook => {
|
||||
hook.ready(() => {
|
||||
// Restore
|
||||
const scrollTop = sessionStorage.getItem('bs-scroll');
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
|
||||
// Remember
|
||||
document.addEventListener('scroll', event => {
|
||||
document.addEventListener('scroll', () => {
|
||||
sessionStorage.setItem('bs-scroll', document.documentElement.scrollTop);
|
||||
});
|
||||
});
|
||||
|
||||
1311
docs/assets/plugins/search/lunr.min.js
vendored
Normal file
192
docs/assets/plugins/search/search.css
Normal file
@@ -0,0 +1,192 @@
|
||||
body.site-search-visible {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar .search-box {
|
||||
margin: 1.25rem 26px;
|
||||
}
|
||||
|
||||
.sidebar .search-box kbd {
|
||||
margin-top: 2px;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
/* Site search */
|
||||
.site-search {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.site-search[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.site-search__overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--sl-overlay-background-color);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.site-search__panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 460px;
|
||||
max-height: calc(100vh - 20rem);
|
||||
background-color: var(--sl-panel-background-color);
|
||||
border-radius: var(--sl-border-radius-large);
|
||||
box-shadow: var(--sl-shadow-x-large);
|
||||
margin: 10rem auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.site-search__panel {
|
||||
max-width: 100%;
|
||||
max-height: calc(92vh - 120px); /* allow iOS browser chrome */
|
||||
margin: 4vh var(--sl-spacing-medium);
|
||||
}
|
||||
}
|
||||
|
||||
.site-search__input::part(base) {
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: var(--sl-border-radius-large);
|
||||
}
|
||||
|
||||
.site-search__input:focus-within::part(base) {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.site-search__input {
|
||||
--sl-input-height-large: 4rem;
|
||||
}
|
||||
|
||||
.site-search__body {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.site-search--has-results .site-search__body {
|
||||
border-top: solid 1px var(--sl-color-neutral-200);
|
||||
}
|
||||
|
||||
.site-search__results {
|
||||
display: none;
|
||||
line-height: var(--sl-line-height-dense);
|
||||
list-style: none;
|
||||
padding: var(--sl-spacing-x-small) 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.site-search--has-results .site-search__results {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.site-search__results a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
padding: var(--sl-spacing-x-small) var(--sl-spacing-large);
|
||||
}
|
||||
|
||||
.site-search__results li a:hover,
|
||||
.site-search__results li a:hover small {
|
||||
background-color: var(--sl-color-neutral-100);
|
||||
}
|
||||
|
||||
.site-search__results li[aria-selected='true'] a,
|
||||
.site-search__results li[aria-selected='true'] a small,
|
||||
.site-search__results li[aria-selected='true'] a sl-icon {
|
||||
outline: none;
|
||||
color: var(--sl-color-neutral-0);
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.sl-theme-dark .site-search__results li[aria-selected='true'] a,
|
||||
.sl-theme-dark .site-search__results li[aria-selected='true'] a small,
|
||||
.sl-theme-dark .site-search__results li[aria-selected='true'] a sl-icon {
|
||||
background-color: var(--sl-color-primary-400);
|
||||
color: var(--sl-color-neutral-1000);
|
||||
}
|
||||
|
||||
.site-search__results h3 {
|
||||
font-weight: var(--sl-font-weight-semibold);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.site-search__results small {
|
||||
display: block;
|
||||
color: var(--sl-color-neutral-600);
|
||||
}
|
||||
|
||||
.site-search__result {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.site-search__result a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sl-spacing-medium);
|
||||
}
|
||||
|
||||
.site-search__result-icon {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
color: var(--sl-color-neutral-400);
|
||||
font-size: var(--sl-font-size-x-large);
|
||||
}
|
||||
|
||||
.site-search__result-description {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.site-search__empty {
|
||||
display: none;
|
||||
border-top: solid 1px var(--sl-color-neutral-200);
|
||||
text-align: center;
|
||||
padding: var(--sl-spacing-x-large);
|
||||
}
|
||||
|
||||
.site-search--no-results .site-search__empty {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.site-search__footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: var(--sl-spacing-large);
|
||||
border-top: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom-left-radius: inherit;
|
||||
border-bottom-right-radius: inherit;
|
||||
padding: var(--sl-spacing-medium);
|
||||
}
|
||||
|
||||
.site-search__footer small {
|
||||
color: var(--sl-color-neutral-700);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.site-search__footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Forced colors mode */
|
||||
@media (forced-colors: active) {
|
||||
.site-search__panel {
|
||||
border: solid 1px var(--sl-color-neutral-0);
|
||||
}
|
||||
|
||||
.site-search__results li[aria-selected='true'] a {
|
||||
outline: dashed 1px SelectedItem;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
}
|
||||
320
docs/assets/plugins/search/search.js
Normal file
@@ -0,0 +1,320 @@
|
||||
/* global lunr */
|
||||
(() => {
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push(hook => {
|
||||
// Append the search box to the sidebar
|
||||
hook.mounted(() => {
|
||||
const appName = document.querySelector('.sidebar .app-name');
|
||||
const searchBox = document.createElement('div');
|
||||
searchBox.classList.add('search-box');
|
||||
searchBox.innerHTML = `
|
||||
<sl-input type="search" placeholder="Search" pill>
|
||||
<sl-icon slot="prefix" name="search"></sl-icon>
|
||||
<kbd slot="suffix" title="Press / to search">/</kbd>
|
||||
</sl-input>
|
||||
`;
|
||||
const searchBoxInput = searchBox.querySelector('sl-input');
|
||||
|
||||
appName.insertAdjacentElement('afterend', searchBox);
|
||||
|
||||
// Show the search panel when the search is clicked
|
||||
searchBoxInput.addEventListener('mousedown', event => {
|
||||
event.preventDefault();
|
||||
show();
|
||||
});
|
||||
|
||||
// Show the search panel when a key is pressed
|
||||
searchBoxInput.addEventListener('keydown', event => {
|
||||
if (event.key === 'Tab') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass the character that was typed through to the search input
|
||||
if (event.key.length === 1) {
|
||||
event.preventDefault();
|
||||
input.value = event.key;
|
||||
show();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Append the search panel to the body
|
||||
const siteSearch = document.createElement('div');
|
||||
siteSearch.classList.add('site-search');
|
||||
siteSearch.hidden = true;
|
||||
siteSearch.innerHTML = `
|
||||
<div class="site-search__overlay"></div>
|
||||
<div
|
||||
id="site-search-panel"
|
||||
class="site-search__panel"
|
||||
role="combobox"
|
||||
aria-expanded="false"
|
||||
aria-owns="site-search-results"
|
||||
aria-activedescendant=""
|
||||
>
|
||||
<header class="site-search__header">
|
||||
<sl-input
|
||||
class="site-search__input"
|
||||
type="search"
|
||||
placeholder="Search this site"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="site-search-results"
|
||||
size="large"
|
||||
clearable
|
||||
>
|
||||
<sl-icon slot="prefix" name="search"></sl-icon>
|
||||
</sl-input>
|
||||
</header>
|
||||
<div class="site-search__body">
|
||||
<ul
|
||||
id="site-search-results"
|
||||
class="site-search__results"
|
||||
role="listbox"
|
||||
aria-labelledby="site-search-panel"
|
||||
>
|
||||
</ul>
|
||||
<div class="site-search__empty">No results found.</div>
|
||||
</div>
|
||||
<footer class="site-search__footer">
|
||||
<small><kbd>↑</kbd> <kbd>↓</kbd> navigate</small>
|
||||
<small><kbd>↲</kbd> select</small>
|
||||
<small><kbd>esc</kbd> close</small>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
document.body.append(siteSearch);
|
||||
|
||||
const overlay = siteSearch.querySelector('.site-search__overlay');
|
||||
const panel = siteSearch.querySelector('.site-search__panel');
|
||||
const input = siteSearch.querySelector('.site-search__input');
|
||||
const results = siteSearch.querySelector('.site-search__results');
|
||||
const animationDuration = 150;
|
||||
const searchDebounce = 200;
|
||||
let isShowing = false;
|
||||
let searchTimeout;
|
||||
let searchIndex;
|
||||
let map;
|
||||
|
||||
// Load search data
|
||||
fetch('../../../search.json')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
searchIndex = lunr.Index.load(data.searchIndex);
|
||||
map = data.map;
|
||||
});
|
||||
|
||||
async function show() {
|
||||
isShowing = true;
|
||||
document.body.classList.add('site-search-visible');
|
||||
siteSearch.hidden = false;
|
||||
requestAnimationFrame(() => input.focus());
|
||||
updateResults();
|
||||
|
||||
await Promise.all([
|
||||
panel.animate(
|
||||
[
|
||||
{ opacity: 0, transform: 'scale(.9)' },
|
||||
{ opacity: 1, transform: 'scale(1)' }
|
||||
],
|
||||
{ duration: animationDuration }
|
||||
).finished,
|
||||
overlay.animate([{ opacity: 0 }, { opacity: 1 }], { duration: animationDuration }).finished
|
||||
]);
|
||||
|
||||
document.addEventListener('mousedown', handleDocumentMouseDown);
|
||||
document.addEventListener('keydown', handleDocumentKeyDown);
|
||||
document.addEventListener('focusin', handleDocumentFocusIn);
|
||||
}
|
||||
|
||||
async function hide() {
|
||||
isShowing = false;
|
||||
document.body.classList.remove('site-search-visible');
|
||||
|
||||
await Promise.all([
|
||||
panel.animate(
|
||||
[
|
||||
{ opacity: 1, transform: 'scale(1)' },
|
||||
{ opacity: 0, transform: 'scale(.9)' }
|
||||
],
|
||||
{ duration: animationDuration }
|
||||
).finished,
|
||||
overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: animationDuration }).finished
|
||||
]);
|
||||
|
||||
siteSearch.hidden = true;
|
||||
input.value = '';
|
||||
updateResults();
|
||||
|
||||
document.removeEventListener('mousedown', handleDocumentMouseDown);
|
||||
document.removeEventListener('keydown', handleDocumentKeyDown);
|
||||
document.removeEventListener('focusin', handleDocumentFocusIn);
|
||||
}
|
||||
|
||||
function handleInput() {
|
||||
// Debounce search queries
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
|
||||
}
|
||||
|
||||
function handleDocumentFocusIn(event) {
|
||||
// Close when focus leaves the panel
|
||||
if (event.target.closest('.site-search__panel') !== panel) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
function handleDocumentMouseDown(event) {
|
||||
// Close when clicking outside of the panel
|
||||
if (event.target.closest('.site-search__overlay') === overlay) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
function handleDocumentKeyDown(event) {
|
||||
// Close when pressing escape
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle keyboard selections
|
||||
if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter'].includes(event.key)) {
|
||||
event.preventDefault();
|
||||
|
||||
const currentEl = results.querySelector('[aria-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) {
|
||||
const a = nextEl.querySelector('a');
|
||||
panel.setAttribute('aria-activedescendant', a.id);
|
||||
|
||||
item.setAttribute('aria-selected', 'true');
|
||||
nextEl.scrollIntoView({ block: 'nearest' });
|
||||
} else {
|
||||
item.setAttribute('aria-selected', 'false');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function updateResults(query = '') {
|
||||
try {
|
||||
await searchIndex;
|
||||
|
||||
const hasQuery = query.length > 0;
|
||||
const searchTokens = query
|
||||
.split(' ')
|
||||
.map((term, index, arr) => `${term}${index === arr.length - 1 ? `* ${term}~1` : '~1'}`)
|
||||
.join(' ');
|
||||
const matches = hasQuery ? searchIndex.search(`${query} ${searchTokens}`) : [];
|
||||
const hasResults = hasQuery && matches.length > 0;
|
||||
siteSearch.classList.toggle('site-search--has-results', hasQuery && hasResults);
|
||||
siteSearch.classList.toggle('site-search--no-results', hasQuery && !hasResults);
|
||||
panel.setAttribute('aria-expanded', hasQuery && hasResults ? 'true' : 'false');
|
||||
|
||||
results.innerHTML = '';
|
||||
|
||||
matches.forEach((match, index) => {
|
||||
const page = map[match.ref];
|
||||
const li = document.createElement('li');
|
||||
const a = document.createElement('a');
|
||||
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';
|
||||
}
|
||||
if (page.url.includes('tokens/')) {
|
||||
icon = 'palette2';
|
||||
}
|
||||
if (page.url.includes('utilities/')) {
|
||||
icon = 'wrench';
|
||||
}
|
||||
if (page.url.includes('tutorials/')) {
|
||||
icon = 'joystick';
|
||||
}
|
||||
|
||||
a.href = window.$docsify.routerMode === 'hash' ? `/#/${page.url}` : `/${page.url}`;
|
||||
a.innerHTML = `
|
||||
<div class="site-search__result-icon">
|
||||
<sl-icon name="${icon}" aria-hidden="true"></sl-icon>
|
||||
</div>
|
||||
<div class="site-search__result__details">
|
||||
<h3>${page.title}</h3>
|
||||
<small>${page.url}</small>
|
||||
</div>
|
||||
`;
|
||||
|
||||
li.classList.add('site-search__result');
|
||||
li.setAttribute('aria-selected', index === 0 ? 'true' : 'false');
|
||||
li.appendChild(a);
|
||||
results.appendChild(li);
|
||||
});
|
||||
} catch {
|
||||
// Ignore query errors as the user types
|
||||
}
|
||||
}
|
||||
|
||||
// Show the search panel slash is pressed outside of a form element
|
||||
document.addEventListener('keydown', event => {
|
||||
const isSlash = event.key === '/';
|
||||
const isCtrlK = (event.metaKey || event.ctrlKey) && event.key === 'k';
|
||||
|
||||
if (
|
||||
!isShowing &&
|
||||
(isSlash || isCtrlK) &&
|
||||
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
|
||||
) {
|
||||
event.preventDefault();
|
||||
show();
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('sl-input', handleInput);
|
||||
|
||||
// Close when a result is selected
|
||||
results.addEventListener('click', event => {
|
||||
if (event.target.closest('a')) {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1,14 +0,0 @@
|
||||
(() => {
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push((hook, vm) => {
|
||||
hook.mounted(function () {
|
||||
// Move search below the app name
|
||||
const appName = document.querySelector('.sidebar .app-name');
|
||||
const search = document.querySelector('.sidebar .search');
|
||||
appName.insertAdjacentElement('afterend', search);
|
||||
});
|
||||
});
|
||||
})();
|
||||
29
docs/assets/plugins/theme-picker/theme-picker.css
Normal file
@@ -0,0 +1,29 @@
|
||||
.theme-picker {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
.theme-picker:not(:defined) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.theme-picker sl-menu-label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.theme-picker sl-menu-label kbd {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.theme-picker {
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
}
|
||||
|
||||
.theme-picker sl-menu-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
82
docs/assets/plugins/theme-picker/theme-picker.js
Normal file
@@ -0,0 +1,82 @@
|
||||
(() => {
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push(hook => {
|
||||
hook.mounted(() => {
|
||||
function getTheme() {
|
||||
return localStorage.getItem('theme') || 'auto';
|
||||
}
|
||||
|
||||
function isDark() {
|
||||
if (theme === 'auto') {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
return theme === 'dark';
|
||||
}
|
||||
|
||||
function setTheme(newTheme) {
|
||||
const noTransitions = Object.assign(document.createElement('style'), {
|
||||
textContent: '* { transition: none !important; }'
|
||||
});
|
||||
|
||||
theme = newTheme;
|
||||
localStorage.setItem('theme', theme);
|
||||
|
||||
// Update the UI
|
||||
[...menu.querySelectorAll('sl-menu-item')].map(item => (item.checked = item.getAttribute('value') === theme));
|
||||
menuIcon.name = isDark() ? 'moon' : 'sun';
|
||||
|
||||
// Toggle the dark mode class without transitions
|
||||
document.body.appendChild(noTransitions);
|
||||
requestAnimationFrame(() => {
|
||||
document.documentElement.classList.toggle('sl-theme-dark', isDark());
|
||||
requestAnimationFrame(() => document.body.removeChild(noTransitions));
|
||||
});
|
||||
}
|
||||
|
||||
let theme = getTheme();
|
||||
|
||||
// Generate the theme picker dropdown
|
||||
const dropdown = document.createElement('sl-dropdown');
|
||||
dropdown.classList.add('theme-picker');
|
||||
dropdown.innerHTML = `
|
||||
<sl-button size="small" pill slot="trigger" caret>
|
||||
<sl-icon name="sun" label="Select Theme"></sl-icon>
|
||||
</sl-button>
|
||||
<sl-menu>
|
||||
<sl-menu-label>Toggle <kbd>\\</kbd></sl-menu-label>
|
||||
<sl-menu-item type="checkbox" value="light">Light</sl-menu-item>
|
||||
<sl-menu-item type="checkbox" value="dark">Dark</sl-menu-item>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item type="checkbox" value="auto">Auto</sl-menu-item>
|
||||
</sl-menu>
|
||||
`;
|
||||
document.querySelector('.sidebar-toggle').insertAdjacentElement('afterend', dropdown);
|
||||
|
||||
// Listen for selections
|
||||
const menu = dropdown.querySelector('sl-menu');
|
||||
const menuIcon = dropdown.querySelector('sl-icon');
|
||||
menu.addEventListener('sl-select', event => setTheme(event.detail.item.value));
|
||||
|
||||
// Update the theme when the preference changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => setTheme(theme));
|
||||
|
||||
// Toggle themes when pressing backslash
|
||||
document.addEventListener('keydown', event => {
|
||||
if (
|
||||
event.key === '\\' &&
|
||||
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
|
||||
) {
|
||||
event.preventDefault();
|
||||
|
||||
setTheme(isDark() ? 'light' : 'dark');
|
||||
}
|
||||
});
|
||||
|
||||
// Set the initial theme and sync the UI
|
||||
setTheme(theme);
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1,13 +0,0 @@
|
||||
.theme-toggle {
|
||||
position: fixed;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.theme-toggle {
|
||||
top: 0.25rem;
|
||||
right: 0.25rem;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
(() => {
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push((hook, vm) => {
|
||||
hook.mounted(function () {
|
||||
let isDark = localStorage.getItem('theme') === 'sl-theme-dark';
|
||||
const sidebarToggle = document.querySelector('.sidebar-toggle');
|
||||
const noTransitions = Object.assign(document.createElement('style'), {
|
||||
textContent: '* { transition: none !important; }'
|
||||
});
|
||||
const toggle = Object.assign(document.createElement('sl-icon-button'), {
|
||||
name: isDark ? 'sun' : 'moon',
|
||||
label: 'Toggle dark mode'
|
||||
});
|
||||
toggle.classList.add('theme-toggle');
|
||||
|
||||
// Set initial theme
|
||||
if (isDark) {
|
||||
document.body.classList.add('sl-theme-dark');
|
||||
}
|
||||
|
||||
// Toggle theme
|
||||
toggle.addEventListener('click', () => {
|
||||
isDark = !isDark;
|
||||
isDark ? localStorage.setItem('theme', 'sl-theme-dark') : localStorage.removeItem('theme');
|
||||
toggle.name = isDark ? 'sun' : 'moon';
|
||||
|
||||
// Disable transitions as the theme changes
|
||||
document.body.appendChild(noTransitions);
|
||||
requestAnimationFrame(() => {
|
||||
document.body.classList.toggle('sl-theme-dark', isDark);
|
||||
requestAnimationFrame(() => document.body.removeChild(noTransitions));
|
||||
});
|
||||
});
|
||||
|
||||
sidebarToggle.insertAdjacentElement('afterend', toggle);
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1,60 +0,0 @@
|
||||
/* Color demo */
|
||||
.color-demo {
|
||||
width: 4rem;
|
||||
height: 2rem;
|
||||
border-radius: var(--sl-border-radius-small);
|
||||
box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.33);
|
||||
}
|
||||
|
||||
/* Border radius demo */
|
||||
.border-radius-demo {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
background: var(--sl-color-primary-500);
|
||||
}
|
||||
|
||||
/* Transition demo */
|
||||
.transition-demo {
|
||||
position: relative;
|
||||
background: var(--sl-color-gray-200);
|
||||
width: 8rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.transition-demo:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: var(--sl-color-primary-500);
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
transition-duration: inherit;
|
||||
transition-property: width;
|
||||
}
|
||||
|
||||
.transition-demo:hover:after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sl-theme-dark .transition-demo {
|
||||
background: var(--sl-color-gray-800);
|
||||
}
|
||||
|
||||
/* Spacing demo */
|
||||
.spacing-demo {
|
||||
width: 100px;
|
||||
background: var(--sl-color-primary-500);
|
||||
}
|
||||
|
||||
/* Elevation dmeo */
|
||||
.elevation-demo {
|
||||
background: var(--sl-color-white);
|
||||
border-radius: 3px;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.sl-theme-dark .elevation-demo {
|
||||
background-color: var(--sl-color-gray-800);
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
/* Elevation tokens */
|
||||
.sl-theme-dark {
|
||||
--sl-shadow-x-small: 0 1px 0 #6b72800d;
|
||||
--sl-shadow-small: 0 1px 2px #6b72801a;
|
||||
--sl-shadow-medium: 0 2px 4px #6b72801a;
|
||||
--sl-shadow-large: 0 2px 8px #6b72801a;
|
||||
--sl-shadow-x-large: 0 4px 16px #6b72801a;
|
||||
}
|
||||
|
||||
.sl-theme-dark {
|
||||
background: var(--sl-color-gray-900);
|
||||
color: var(--sl-color-gray-200);
|
||||
}
|
||||
|
||||
/* Sidebar **/
|
||||
.sl-theme-dark .sidebar {
|
||||
background: var(--sl-color-gray-900);
|
||||
border-right-color: var(--sl-color-gray-900);
|
||||
}
|
||||
|
||||
.sl-theme-dark .sidebar li > p {
|
||||
color: var(--sl-color-white);
|
||||
border-bottom-color: var(--sl-color-gray-800);
|
||||
}
|
||||
|
||||
.sl-theme-dark .sidebar-toggle {
|
||||
background-color: var(--sl-color-gray-900);
|
||||
}
|
||||
|
||||
.sl-theme-dark .docsify-pagination-container {
|
||||
border-top-color: var(--sl-color-gray-800) !important;
|
||||
}
|
||||
|
||||
/* Search */
|
||||
.sl-theme-dark .sidebar .search input[type='search'] {
|
||||
background: var(--sl-input-background-color);
|
||||
border-color: var(--sl-input-border-color);
|
||||
color: var(--sl-input-color);
|
||||
}
|
||||
|
||||
.sl-theme-dark .sidebar .search input[type='search']:hover {
|
||||
border-color: var(--sl-input-border-color-hover);
|
||||
}
|
||||
|
||||
.sl-theme-dark .sidebar .search input[type='search']:focus {
|
||||
border-color: var(--sl-input-border-color-focus);
|
||||
}
|
||||
|
||||
.sl-theme-dark .sidebar .clear-button {
|
||||
color: var(--sl-color-gray-700);
|
||||
}
|
||||
|
||||
.sl-theme-dark .sidebar .clear-button svg circle {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
.sl-theme-dark .component-header {
|
||||
border-bottom-color: var(--sl-color-gray-800);
|
||||
}
|
||||
|
||||
.sl-theme-dark .anchor span {
|
||||
color: var(--sl-color-white);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section h2 {
|
||||
border-bottom-color: var(--sl-color-gray-800);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section blockquote {
|
||||
border-left-color: var(--sl-color-gray-800);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section kbd {
|
||||
border-color: var(--sl-color-gray-800);
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.sl-theme-dark .markdown-section tr:nth-child(2n) {
|
||||
background: var(--sl-color-gray-950);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section td {
|
||||
border-top-color: var(--sl-color-gray-800);
|
||||
border-bottom-color: var(--sl-color-gray-800);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section table .attribute-tooltip {
|
||||
border-bottom-color: var(--sl-color-gray-700);
|
||||
}
|
||||
|
||||
/* Tips & warnings */
|
||||
.sl-theme-dark .markdown-section p.tip,
|
||||
.sl-theme-dark .markdown-section p.warn {
|
||||
background-color: var(--sl-color-gray-950);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section p.tip:before,
|
||||
.sl-theme-dark .markdown-section p.warn:before {
|
||||
color: var(--sl-color-gray-900);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section p.tip code,
|
||||
.sl-theme-dark .markdown-section p.warn code {
|
||||
background-color: var(--sl-color-gray-800);
|
||||
}
|
||||
|
||||
/* Code */
|
||||
.sl-theme-dark .markdown-section code {
|
||||
background: var(--sl-color-gray-950);
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
.sl-theme-dark .markdown-section pre,
|
||||
.sl-theme-dark .code-block__source {
|
||||
background-color: var(--sl-color-gray-800);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section pre > code {
|
||||
color: var(--sl-color-gray-200);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section pre .token.comment {
|
||||
color: var(--sl-color-gray-600);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section pre .token.prolog,
|
||||
.sl-theme-dark .markdown-section pre .token.doctype,
|
||||
.sl-theme-dark .markdown-section pre .token.cdata,
|
||||
.sl-theme-dark .markdown-section pre .token.operator {
|
||||
color: var(--sl-color-gray-300);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section pre .token.property,
|
||||
.sl-theme-dark .markdown-section pre .token.keyword,
|
||||
.sl-theme-dark .markdown-section pre .token.tag,
|
||||
.sl-theme-dark .markdown-section pre .token.url {
|
||||
color: var(--sl-color-primary-400);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section pre .token.symbol,
|
||||
.sl-theme-dark .markdown-section pre .token.deleted {
|
||||
color: #f92672;
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section pre .token.boolean,
|
||||
.sl-theme-dark .markdown-section pre .token.constant,
|
||||
.sl-theme-dark .markdown-section pre .token.selector,
|
||||
.sl-theme-dark .markdown-section pre .token.attr-name,
|
||||
.sl-theme-dark .markdown-section pre .token.string,
|
||||
.sl-theme-dark .markdown-section pre .token.char,
|
||||
.sl-theme-dark .markdown-section pre .token.builtin,
|
||||
.sl-theme-dark .markdown-section pre .token.inserted {
|
||||
color: var(--sl-color-success-400);
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section pre .token.atrule,
|
||||
.sl-theme-dark .markdown-section pre .token.attr-value,
|
||||
.sl-theme-dark .markdown-section pre .token.number,
|
||||
.sl-theme-dark .markdown-section pre .token.variable {
|
||||
color: #c380ff;
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section pre .token.function,
|
||||
.sl-theme-dark .markdown-section pre .token.class-name {
|
||||
color: #ffbb4d;
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section pre .token.regex {
|
||||
color: #f8bd5b;
|
||||
}
|
||||
|
||||
.sl-theme-dark .markdown-section pre .token.important {
|
||||
color: #fd495f;
|
||||
}
|
||||
|
||||
/* Repo buttons */
|
||||
.sl-theme-dark .repo-button {
|
||||
background-color: var(--sl-color-gray-900);
|
||||
border-color: var(--sl-color-gray-800);
|
||||
color: var(--sl-color-gray-200);
|
||||
}
|
||||
|
||||
.sl-theme-dark .repo-button--github sl-icon {
|
||||
color: var(--sl-color-white);
|
||||
}
|
||||
|
||||
.sl-theme-dark .repo-button:hover {
|
||||
background-color: var(--sl-color-gray-900);
|
||||
border: solid 1px var(--sl-color-gray-700);
|
||||
}
|
||||
|
||||
.sl-theme-dark .repo-button:focus {
|
||||
border-color: var(--sl-color-primary-500);
|
||||
}
|
||||
@@ -13,12 +13,17 @@ body {
|
||||
font-size: var(--sl-font-size-medium);
|
||||
font-weight: var(--sl-font-weight-normal);
|
||||
letter-spacing: var(--sl-letter-spacing-normal);
|
||||
color: var(--sl-color-gray-800);
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
color: var(--sl-color-neutral-900);
|
||||
line-height: var(--sl-line-height-normal);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--sl-color-primary-500);
|
||||
color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--sl-color-primary-700);
|
||||
}
|
||||
|
||||
strong {
|
||||
@@ -27,99 +32,30 @@ strong {
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
background: var(--sl-color-white);
|
||||
border-right: solid 1px var(--sl-color-gray-200);
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
border-right: solid 1px var(--sl-color-neutral-200);
|
||||
}
|
||||
|
||||
.sidebar .app-name {
|
||||
padding: 0 1.5rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.sidebar-version {
|
||||
font-size: var(--sl-font-size-x-small);
|
||||
font-weight: var(--sl-font-weight-normal);
|
||||
color: var(--sl-color-gray-400);
|
||||
color: var(--sl-color-neutral-500);
|
||||
text-align: right;
|
||||
padding: 0 var(--sl-spacing-small);
|
||||
margin: -1.25rem 0 0.6rem 0;
|
||||
}
|
||||
|
||||
.sidebar-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
text-align: center;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Search */
|
||||
.sidebar .search {
|
||||
position: relative;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.sidebar .search input[type='search'] {
|
||||
border: solid 1px var(--sl-input-border-color);
|
||||
border-radius: var(--sl-border-radius-pill);
|
||||
padding-left: 1rem;
|
||||
padding-right: 2rem;
|
||||
margin: 0 1.25rem;
|
||||
transition: var(--sl-transition-fast) color, var(--sl-transition-fast) border, var(--sl-transition-fast) box-shadow;
|
||||
}
|
||||
|
||||
.sidebar .search input[type='search']:hover {
|
||||
border-color: var(--sl-input-border-color-hover);
|
||||
}
|
||||
|
||||
.sidebar .search input[type='search']:focus {
|
||||
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-focus-ring-color-primary);
|
||||
border-color: var(--sl-input-border-color-focus);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.sidebar .input-wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 0 0.25rem;
|
||||
}
|
||||
|
||||
.sidebar .clear-button {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
top: 7px;
|
||||
width: 22px !important;
|
||||
height: 22px !important;
|
||||
}
|
||||
|
||||
.sidebar .clear-button svg {
|
||||
transform: scale(0.75) !important;
|
||||
}
|
||||
|
||||
.sidebar .clear-button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search .results-panel {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.search .matching-post {
|
||||
border-bottom: solid 1px var(--sl-color-gray-500) !important;
|
||||
padding: 0.25rem 1.5rem;
|
||||
}
|
||||
|
||||
.search .matching-post a {
|
||||
display: block;
|
||||
border-radius: inherit;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.search .matching-post h2 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.search .matching-post p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Sidebar toggle */
|
||||
.sidebar-toggle {
|
||||
top: 0.25rem;
|
||||
@@ -127,13 +63,21 @@ strong {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
background-color: var(--sl-color-white);
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.sidebar-toggle:hover .sidebar-toggle-button {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.sidebar-toggle:active .sidebar-toggle-button span {
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.sidebar-toggle:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-focus-ring-color-primary);
|
||||
outline: var(--sl-focus-ring);
|
||||
outline-offset: var(--sl-focus-ring-offset);
|
||||
}
|
||||
|
||||
.sidebar-toggle span:last-child {
|
||||
@@ -168,12 +112,12 @@ strong {
|
||||
|
||||
.sidebar-nav li.collapse > a,
|
||||
.sidebar-nav li.active > a {
|
||||
color: var(--sl-color-primary-500);
|
||||
color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.sidebar li > p {
|
||||
font-weight: var(--sl-font-weight-bold);
|
||||
border-bottom: solid 1px var(--sl-color-gray-200);
|
||||
border-bottom: solid 1px var(--sl-color-neutral-200);
|
||||
margin: 0 0.75rem 0.5rem 0;
|
||||
}
|
||||
|
||||
@@ -218,6 +162,11 @@ strong {
|
||||
max-width: 22rem;
|
||||
}
|
||||
|
||||
.markdown-section .splash-start h1:first-of-type {
|
||||
font-size: var(--sl-font-size-large);
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1040px) {
|
||||
.splash {
|
||||
display: block;
|
||||
@@ -250,12 +199,15 @@ strong {
|
||||
|
||||
.markdown-section {
|
||||
max-width: 860px;
|
||||
overflow-anchor: none;
|
||||
}
|
||||
|
||||
.anchor span {
|
||||
color: var(--sl-color-neutral-1000);
|
||||
}
|
||||
|
||||
.markdown-section blockquote {
|
||||
position: relative;
|
||||
border-left: solid 4px var(--sl-color-gray-200);
|
||||
border-left: solid 4px var(--sl-color-neutral-200);
|
||||
font-style: italic;
|
||||
padding: 1rem 1.5rem;
|
||||
margin: 0 0 1rem 0;
|
||||
@@ -279,7 +231,13 @@ strong {
|
||||
}
|
||||
|
||||
.docsify-pagination-container {
|
||||
border-top-color: var(--sl-color-gray-200) !important;
|
||||
border-top-color: var(--sl-color-neutral-200) !important;
|
||||
}
|
||||
|
||||
.pagination-item-label,
|
||||
.pagination-item-subtitle,
|
||||
.pagination-item-title {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.markdown-section h1,
|
||||
@@ -293,12 +251,12 @@ strong {
|
||||
}
|
||||
|
||||
.markdown-section h1 {
|
||||
font-size: var(--sl-font-size-xx-large);
|
||||
font-size: var(--sl-font-size-2x-large);
|
||||
}
|
||||
|
||||
.markdown-section h2 {
|
||||
font-size: var(--sl-font-size-x-large);
|
||||
border-bottom: solid 1px var(--sl-color-gray-200);
|
||||
border-bottom: solid 1px var(--sl-color-neutral-200);
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
@@ -329,23 +287,26 @@ strong {
|
||||
.markdown-section code {
|
||||
font-family: var(--sl-font-mono);
|
||||
font-size: 87.5%;
|
||||
background: var(--sl-color-gray-50);
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
border-radius: var(--sl-border-radius-small);
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
kbd,
|
||||
.markdown-section kbd {
|
||||
font-family: var(--sl-font-mono);
|
||||
font-size: 87.5%;
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
border-radius: var(--sl-border-radius-small);
|
||||
border: solid 1px var(--sl-color-gray-200);
|
||||
padding: 2px 4px;
|
||||
border: solid 1px var(--sl-color-neutral-200);
|
||||
box-shadow: inset 0 1px 0 var(--sl-color-neutral-0);
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
.markdown-section pre {
|
||||
position: relative;
|
||||
background: var(--sl-color-gray-50);
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
}
|
||||
|
||||
@@ -353,7 +314,7 @@ strong {
|
||||
display: block;
|
||||
background: none;
|
||||
border-radius: 0;
|
||||
color: var(--sl-color-gray-700);
|
||||
color: var(--sl-color-neutral-900);
|
||||
padding: var(--sl-spacing-medium);
|
||||
overflow: auto;
|
||||
hyphens: none;
|
||||
@@ -361,18 +322,18 @@ strong {
|
||||
}
|
||||
|
||||
.markdown-section pre .token.comment {
|
||||
color: var(--sl-color-gray-400);
|
||||
color: var(--sl-color-neutral-500);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.prolog,
|
||||
.markdown-section pre .token.doctype,
|
||||
.markdown-section pre .token.cdata,
|
||||
.markdown-section pre .token.operator {
|
||||
color: var(--sl-color-gray-600);
|
||||
color: var(--sl-color-neutral-700);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.punctuation {
|
||||
color: var(--sl-color-gray-500);
|
||||
color: var(--sl-color-neutral-700);
|
||||
}
|
||||
|
||||
.namespace {
|
||||
@@ -383,12 +344,12 @@ strong {
|
||||
.markdown-section pre .token.keyword,
|
||||
.markdown-section pre .token.tag,
|
||||
.markdown-section pre .token.url {
|
||||
color: var(--sl-color-primary-500);
|
||||
color: var(--sl-color-sky-800);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.symbol,
|
||||
.markdown-section pre .token.deleted {
|
||||
color: #f92672;
|
||||
color: var(--sl-color-pink-700);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.boolean,
|
||||
@@ -399,27 +360,24 @@ strong {
|
||||
.markdown-section pre .token.char,
|
||||
.markdown-section pre .token.builtin,
|
||||
.markdown-section pre .token.inserted {
|
||||
color: var(--sl-color-success-600);
|
||||
color: var(--sl-color-emerald-700);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.atrule,
|
||||
.markdown-section pre .token.attr-value,
|
||||
.markdown-section pre .token.number,
|
||||
.markdown-section pre .token.variable {
|
||||
color: #9013fe;
|
||||
color: var(--sl-color-violet-700);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.function,
|
||||
.markdown-section pre .token.class-name {
|
||||
color: #eb9200;
|
||||
}
|
||||
|
||||
.markdown-section pre .token.class-name,
|
||||
.markdown-section pre .token.regex {
|
||||
color: #f5a623;
|
||||
color: var(--sl-color-orange-700);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.important {
|
||||
color: #d0021b;
|
||||
color: var(--sl-color-red-700);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.important,
|
||||
@@ -432,7 +390,18 @@ strong {
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
.table-wrapper table {
|
||||
min-width: 800px;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-section table {
|
||||
display: table;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
@@ -441,32 +410,36 @@ strong {
|
||||
}
|
||||
|
||||
.markdown-section tr:nth-child(2n) {
|
||||
background: var(--sl-color-gray-50);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.markdown-section th {
|
||||
border: none;
|
||||
font-weight: inherit;
|
||||
font-weight: var(--sl-font-weight-semibold);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown-section td {
|
||||
border-top: solid 1px var(--sl-color-gray-200);
|
||||
border-bottom: solid 1px var(--sl-color-gray-200);
|
||||
border-top: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom: solid 1px var(--sl-color-neutral-200);
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
padding: 0.75rem 0.5rem;
|
||||
}
|
||||
|
||||
.markdown-section td code {
|
||||
.markdown-section table .nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.markdown-section table .attribute-tooltip {
|
||||
background: none;
|
||||
border-bottom: dashed 1px var(--sl-color-gray-200);
|
||||
.markdown-section table sl-tooltip code {
|
||||
border-bottom: dashed 1px var(--sl-color-neutral-300);
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.markdown-section .metadata-table {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
/* Iframes */
|
||||
.markdown-section iframe {
|
||||
border: none;
|
||||
@@ -476,7 +449,7 @@ strong {
|
||||
.markdown-section p.tip,
|
||||
.markdown-section p.warn {
|
||||
position: relative;
|
||||
background: var(--sl-color-gray-50);
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
border-left: solid 4px transparent;
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
padding-left: 1.5rem;
|
||||
@@ -486,7 +459,7 @@ strong {
|
||||
.markdown-section p.warn:before {
|
||||
content: '!';
|
||||
border-radius: 100%;
|
||||
color: var(--sl-color-white);
|
||||
color: var(--sl-color-neutral-0);
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
left: -12px;
|
||||
@@ -499,32 +472,44 @@ strong {
|
||||
}
|
||||
|
||||
.markdown-section p.warn {
|
||||
border-left-color: var(--sl-color-primary-500);
|
||||
border-left-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.markdown-section p.warn:before {
|
||||
background-color: var(--sl-color-primary-500);
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.markdown-section p.tip {
|
||||
border-left-color: var(--sl-color-danger-500);
|
||||
border-left-color: var(--sl-color-danger-600);
|
||||
}
|
||||
|
||||
.markdown-section p.tip:before {
|
||||
background-color: var(--sl-color-danger-500);
|
||||
background-color: var(--sl-color-danger-600);
|
||||
}
|
||||
|
||||
.markdown-section p.tip code,
|
||||
.markdown-section p.warn code {
|
||||
background-color: var(--sl-color-gray-100);
|
||||
background-color: var(--sl-color-neutral-100);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Sponsorship callouts */
|
||||
.sponsor-callout {
|
||||
background: var(--sl-color-warning-100);
|
||||
border-left: solid 3px var(--sl-color-warning-300);
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
padding: 0.5rem 1.5rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 950px) {
|
||||
.sponsor-callout__secondary-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Component headers */
|
||||
.component-header {
|
||||
border-bottom: solid 1px var(--sl-color-gray-200);
|
||||
padding-bottom: 2rem;
|
||||
margin-top: -1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.component-header__tag {
|
||||
@@ -533,9 +518,16 @@ strong {
|
||||
margin: 0.75rem 0 0.25rem 0;
|
||||
}
|
||||
|
||||
.component-header__tag code {
|
||||
.component-header__summary {
|
||||
font-size: var(--sl-font-size-large);
|
||||
line-height: 1.6;
|
||||
border-top: solid 1px var(--sl-color-neutral-200);
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.markdown-section .component-header__tag code {
|
||||
background: none;
|
||||
color: var(--sl-color-gray-500);
|
||||
color: var(--sl-color-neutral-600);
|
||||
font-size: var(--sl-font-size-large);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -545,77 +537,122 @@ strong {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* Lead sentences that occur immediately after the header */
|
||||
.component-header + p {
|
||||
font-size: var(--sl-font-size-large);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Copy button */
|
||||
.docsify-copy-code-button {
|
||||
font-size: var(--sl-font-size-small) !important;
|
||||
border-top-right-radius: var(--sl-border-radius-medium) !important;
|
||||
border-bottom-left-radius: var(--sl-border-radius-medium) !important;
|
||||
}
|
||||
|
||||
/* Repo buttons */
|
||||
html .repo-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background-color: var(--sl-color-white);
|
||||
border: solid 1px var(--sl-color-gray-200);
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
box-shadow: var(--sl-shadow-x-small);
|
||||
font-size: var(--sl-font-size-small);
|
||||
font-weight: var(--sl-font-weight-semibold);
|
||||
line-height: 2;
|
||||
text-decoration: none;
|
||||
color: var(--sl-color-gray-700);
|
||||
padding: var(--sl-spacing-xx-small) var(--sl-spacing-small);
|
||||
margin-bottom: var(--sl-spacing-xx-small);
|
||||
transition: 0.25s all;
|
||||
.repo-button--sponsor sl-icon {
|
||||
color: var(--sl-color-pink-600);
|
||||
}
|
||||
|
||||
html .repo-button:hover {
|
||||
text-decoration: none;
|
||||
background-color: var(--sl-color-gray-50);
|
||||
border: solid 1px var(--sl-color-gray-200);
|
||||
.repo-button--github sl-icon {
|
||||
color: var(--sl-color-neutral-700);
|
||||
}
|
||||
|
||||
html .repo-button:focus {
|
||||
outline: none;
|
||||
border-color: var(--sl-color-primary-500);
|
||||
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-focus-ring-color-primary);
|
||||
.repo-button--twitter sl-icon {
|
||||
color: var(--sl-color-sky-500);
|
||||
}
|
||||
|
||||
html .repo-button:not(:last-of-type) {
|
||||
margin-right: 0.125rem;
|
||||
@media screen and (max-width: 400px) {
|
||||
:not(.sidebar-buttons) > .repo-button {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
html .repo-button sl-icon {
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
margin-right: 0.35rem;
|
||||
}
|
||||
|
||||
html .repo-button--small {
|
||||
font-size: var(--sl-font-size-x-small);
|
||||
padding: var(--sl-spacing-xxx-small) var(--sl-spacing-x-small);
|
||||
}
|
||||
|
||||
html .repo-button--sponsor sl-icon {
|
||||
color: #ea4aaa;
|
||||
}
|
||||
|
||||
html .repo-button--github sl-icon {
|
||||
color: #242a2e;
|
||||
}
|
||||
|
||||
html .repo-button--twitter sl-icon {
|
||||
color: #1ea0f2;
|
||||
}
|
||||
|
||||
body[data-page^='tokens/'] .table-wrapper td:first-child,
|
||||
body[data-page^='tokens/'] .table-wrapper td:first-child code {
|
||||
body[data-page^='/tokens/'] .table-wrapper td:first-child,
|
||||
body[data-page^='/tokens/'] .table-wrapper td:first-child code {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Border radius demo */
|
||||
.border-radius-demo {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
background: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
/* Transition demo */
|
||||
.transition-demo {
|
||||
position: relative;
|
||||
background: var(--sl-color-neutral-200);
|
||||
width: 8rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.transition-demo:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: var(--sl-color-primary-600);
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
transition-duration: inherit;
|
||||
transition-property: width;
|
||||
}
|
||||
|
||||
.transition-demo:hover:after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Spacing demo */
|
||||
.spacing-demo {
|
||||
width: 100px;
|
||||
background: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
/* Elevation demo */
|
||||
.elevation-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: 1rem var(--sl-spacing-2x-small);
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.color-palette__name {
|
||||
font-size: var(--sl-font-size-medium);
|
||||
font-weight: var(--sl-font-weight-semibold);
|
||||
grid-template-columns: repeat(11, 1fr);
|
||||
}
|
||||
|
||||
.color-palette__name code {
|
||||
background: none;
|
||||
font-size: var(--sl-font-size-x-small);
|
||||
}
|
||||
|
||||
.color-palette__example {
|
||||
font-size: var(--sl-font-size-x-small);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.color-palette__swatch {
|
||||
height: 3rem;
|
||||
border-radius: var(--sl-border-radius-small);
|
||||
}
|
||||
|
||||
.color-palette__swatch--border {
|
||||
box-shadow: inset 0 0 0 1px var(--sl-color-neutral-300);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
.color-palette {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
}
|
||||
|
||||
.color-palette__name {
|
||||
grid-column-start: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
.not-found-image {
|
||||
display: block;
|
||||
max-width: 460px;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
[component-header:sl-alert]
|
||||
|
||||
Alerts are used to display important messages either inline or as toast notifications.
|
||||
|
||||
```html preview
|
||||
<sl-alert open>
|
||||
<sl-icon slot="icon" name="info-circle"></sl-icon>
|
||||
@@ -11,60 +9,122 @@ Alerts are used to display important messages either inline or as toast notifica
|
||||
</sl-alert>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAlert, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlAlert open>
|
||||
<SlIcon slot="icon" name="info-circle" />
|
||||
This is a standard alert. You can customize its content and even the icon.
|
||||
</SlAlert>
|
||||
);
|
||||
```
|
||||
|
||||
?> Alerts will not be visible if the `open` attribute is not present.
|
||||
|
||||
## Examples
|
||||
|
||||
### Types
|
||||
### Variants
|
||||
|
||||
Set the `type` attribute to change the alert's type.
|
||||
Set the `variant` attribute to change the alert's variant.
|
||||
|
||||
```html preview
|
||||
<sl-alert type="primary" open>
|
||||
<sl-alert variant="primary" open>
|
||||
<sl-icon slot="icon" name="info-circle"></sl-icon>
|
||||
<strong>This is super informative</strong><br>
|
||||
<strong>This is super informative</strong><br />
|
||||
You can tell by how pretty the alert is.
|
||||
</sl-alert>
|
||||
|
||||
<br>
|
||||
<br />
|
||||
|
||||
<sl-alert type="success" open>
|
||||
<sl-alert variant="success" open>
|
||||
<sl-icon slot="icon" name="check2-circle"></sl-icon>
|
||||
<strong>Your changes have been saved</strong><br>
|
||||
<strong>Your changes have been saved</strong><br />
|
||||
You can safely exit the app now.
|
||||
</sl-alert>
|
||||
|
||||
<br>
|
||||
<br />
|
||||
|
||||
<sl-alert type="info" open>
|
||||
<sl-alert variant="neutral" open>
|
||||
<sl-icon slot="icon" name="gear"></sl-icon>
|
||||
<strong>Your settings have been updated</strong><br>
|
||||
<strong>Your settings have been updated</strong><br />
|
||||
Settings will take affect on next login.
|
||||
</sl-alert>
|
||||
|
||||
<br>
|
||||
<br />
|
||||
|
||||
<sl-alert type="warning" open>
|
||||
<sl-alert variant="warning" open>
|
||||
<sl-icon slot="icon" name="exclamation-triangle"></sl-icon>
|
||||
<strong>Your session has ended</strong><br>
|
||||
<strong>Your session has ended</strong><br />
|
||||
Please login again to continue.
|
||||
</sl-alert>
|
||||
|
||||
<br>
|
||||
<br />
|
||||
|
||||
<sl-alert type="danger" open>
|
||||
<sl-alert variant="danger" open>
|
||||
<sl-icon slot="icon" name="exclamation-octagon"></sl-icon>
|
||||
<strong>Your account has been deleted</strong><br>
|
||||
We're very sorry to see you go!
|
||||
<strong>Your account has been deleted</strong><br />
|
||||
We're very sorry to see you go!
|
||||
</sl-alert>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAlert, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlAlert variant="primary" open>
|
||||
<SlIcon slot="icon" name="info-circle" />
|
||||
<strong>This is super informative</strong>
|
||||
<br />
|
||||
You can tell by how pretty the alert is.
|
||||
</SlAlert>
|
||||
|
||||
<br />
|
||||
|
||||
<SlAlert variant="success" open>
|
||||
<SlIcon slot="icon" name="check2-circle" />
|
||||
<strong>Your changes have been saved</strong>
|
||||
<br />
|
||||
You can safely exit the app now.
|
||||
</SlAlert>
|
||||
|
||||
<br />
|
||||
|
||||
<SlAlert variant="neutral" open>
|
||||
<SlIcon slot="icon" name="gear" />
|
||||
<strong>Your settings have been updated</strong>
|
||||
<br />
|
||||
Settings will take affect on next login.
|
||||
</SlAlert>
|
||||
|
||||
<br />
|
||||
|
||||
<SlAlert variant="warning" open>
|
||||
<SlIcon slot="icon" name="exclamation-triangle" />
|
||||
<strong>Your session has ended</strong>
|
||||
<br />
|
||||
Please login again to continue.
|
||||
</SlAlert>
|
||||
|
||||
<br />
|
||||
|
||||
<SlAlert variant="danger" open>
|
||||
<SlIcon slot="icon" name="exclamation-octagon" />
|
||||
<strong>Your account has been deleted</strong>
|
||||
<br />
|
||||
We're very sorry to see you go!
|
||||
</SlAlert>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Closable
|
||||
|
||||
Add the `closable` attribute to show a close button that will hide the alert.
|
||||
|
||||
```html preview
|
||||
<sl-alert type="primary" open closable class="alert-closable">
|
||||
<sl-alert variant="primary" open closable class="alert-closable">
|
||||
<sl-icon slot="icon" name="info-circle"></sl-icon>
|
||||
You can close this alert any time!
|
||||
</sl-alert>
|
||||
@@ -72,30 +132,59 @@ Add the `closable` attribute to show a close button that will hide the alert.
|
||||
<script>
|
||||
const alert = document.querySelector('.alert-closable');
|
||||
alert.addEventListener('sl-after-hide', () => {
|
||||
setTimeout(() => alert.open = true, 2000);
|
||||
setTimeout(() => (alert.open = true), 2000);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlAlert, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
||||
function handleHide() {
|
||||
setOpen(false);
|
||||
setTimeout(() => setOpen(true), 2000);
|
||||
}
|
||||
|
||||
return (
|
||||
<SlAlert open={open} closable onSlAfterHide={handleHide}>
|
||||
<SlIcon slot="icon" name="info-circle" />
|
||||
You can close this alert any time!
|
||||
</SlAlert>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Without Icons
|
||||
|
||||
Icons are optional. Simply omit the `icon` slot if you don't want them.
|
||||
|
||||
```html preview
|
||||
<sl-alert type="primary" open>
|
||||
Nothing fancy here, just a simple alert.
|
||||
</sl-alert>
|
||||
<sl-alert variant="primary" open> Nothing fancy here, just a simple alert. </sl-alert>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAlert } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlAlert variant="primary" open>
|
||||
Nothing fancy here, just a simple alert.
|
||||
</SlAlert>
|
||||
);
|
||||
```
|
||||
|
||||
### Duration
|
||||
|
||||
Set the `duration` prop to automatically hide an alert after a period of time. This is useful for alerts that don't require acknowledgement.
|
||||
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">
|
||||
<sl-button type="primary">Show Alert</sl-button>
|
||||
<sl-button variant="primary">Show Alert</sl-button>
|
||||
|
||||
<sl-alert type="primary" duration="3000" closable>
|
||||
<sl-alert variant="primary" duration="3000" closable>
|
||||
<sl-icon slot="icon" name="info-circle"></sl-icon>
|
||||
This alert will automatically hide itself after three seconds, unless you interact with it.
|
||||
</sl-alert>
|
||||
@@ -116,47 +205,79 @@ Set the `duration` prop to automatically hide an alert after a period of time. T
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlAlert, SlButton, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.alert-duration sl-alert {
|
||||
margin-top: var(--sl-spacing-medium);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="alert-duration">
|
||||
<SlButton variant="primary" onClick={() => setOpen(true)}>
|
||||
Show Alert
|
||||
</SlButton>
|
||||
|
||||
<SlAlert variant="primary" duration="3000" open={open} closable onSlAfterHide={() => setOpen(false)}>
|
||||
<SlIcon slot="icon" name="info-circle" />
|
||||
This alert will automatically hide itself after three seconds, unless you interact with it.
|
||||
</SlAlert>
|
||||
</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` prop so users can dismiss the notification. It's also common to set a reasonable `duration` when the notification doesn't require acknowledgement.
|
||||
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">
|
||||
<sl-button type="primary">Primary</sl-button>
|
||||
<sl-button type="success">Success</sl-button>
|
||||
<sl-button type="info">Info</sl-button>
|
||||
<sl-button type="warning">Warning</sl-button>
|
||||
<sl-button type="danger">Danger</sl-button>
|
||||
|
||||
<sl-alert type="primary" duration="3000" closable>
|
||||
<sl-button variant="primary">Primary</sl-button>
|
||||
<sl-button variant="success">Success</sl-button>
|
||||
<sl-button variant="neutral">Neutral</sl-button>
|
||||
<sl-button variant="warning">Warning</sl-button>
|
||||
<sl-button variant="danger">Danger</sl-button>
|
||||
|
||||
<sl-alert variant="primary" duration="3000" closable>
|
||||
<sl-icon slot="icon" name="info-circle"></sl-icon>
|
||||
<strong>This is super informative</strong><br>
|
||||
<strong>This is super informative</strong><br />
|
||||
You can tell by how pretty the alert is.
|
||||
</sl-alert>
|
||||
|
||||
<sl-alert type="success" duration="3000" closable>
|
||||
<sl-alert variant="success" duration="3000" closable>
|
||||
<sl-icon slot="icon" name="check2-circle"></sl-icon>
|
||||
<strong>Your changes have been saved</strong><br>
|
||||
<strong>Your changes have been saved</strong><br />
|
||||
You can safely exit the app now.
|
||||
</sl-alert>
|
||||
|
||||
<sl-alert type="info" duration="3000" closable>
|
||||
<sl-alert variant="neutral" duration="3000" closable>
|
||||
<sl-icon slot="icon" name="gear"></sl-icon>
|
||||
<strong>Your settings have been updated</strong><br>
|
||||
<strong>Your settings have been updated</strong><br />
|
||||
Settings will take affect on next login.
|
||||
</sl-alert>
|
||||
|
||||
<sl-alert type="warning" duration="3000" closable>
|
||||
<sl-alert variant="warning" duration="3000" closable>
|
||||
<sl-icon slot="icon" name="exclamation-triangle"></sl-icon>
|
||||
<strong>Your session has ended</strong><br>
|
||||
<strong>Your session has ended</strong><br />
|
||||
Please login again to continue.
|
||||
</sl-alert>
|
||||
|
||||
<sl-alert type="danger" duration="3000" closable>
|
||||
<sl-alert variant="danger" duration="3000" closable>
|
||||
<sl-icon slot="icon" name="exclamation-octagon"></sl-icon>
|
||||
<strong>Your account has been deleted</strong><br>
|
||||
<strong>Your account has been deleted</strong><br />
|
||||
We're very sorry to see you go!
|
||||
</sl-alert>
|
||||
</div>
|
||||
@@ -164,22 +285,98 @@ You should always use the `closable` prop so users can dismiss the notification.
|
||||
<script>
|
||||
const container = document.querySelector('.alert-toast');
|
||||
|
||||
['primary', 'success', 'info', 'warning', 'danger'].map(type => {
|
||||
const button = container.querySelector(`sl-button[type="${type}"]`);
|
||||
const alert = container.querySelector(`sl-alert[type="${type}"]`);
|
||||
['primary', 'success', 'neutral', 'warning', 'danger'].map(variant => {
|
||||
const button = container.querySelector(`sl-button[variant="${variant}"]`);
|
||||
const alert = container.querySelector(`sl-alert[variant="${variant}"]`);
|
||||
|
||||
button.addEventListener('click', () => alert.toast());
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useRef } from 'react';
|
||||
import { SlAlert, SlButton, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
function showToast(alert) {
|
||||
alert.toast();
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
const primary = useRef(null);
|
||||
const success = useRef(null);
|
||||
const neutral = useRef(null);
|
||||
const warning = useRef(null);
|
||||
const danger = useRef(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlButton variant="primary" onClick={() => primary.current.toast()}>
|
||||
Primary
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="success" onClick={() => success.current.toast()}>
|
||||
Success
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="neutral" onClick={() => neutral.current.toast()}>
|
||||
Neutral
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="warning" onClick={() => warning.current.toast()}>
|
||||
Warning
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="danger" onClick={() => danger.current.toast()}>
|
||||
Danger
|
||||
</SlButton>
|
||||
|
||||
<SlAlert ref={primary} variant="primary" duration="3000" closable>
|
||||
<SlIcon slot="icon" name="info-circle" />
|
||||
<strong>This is super informative</strong>
|
||||
<br />
|
||||
You can tell by how pretty the alert is.
|
||||
</SlAlert>
|
||||
|
||||
<SlAlert ref={success} variant="success" duration="3000" closable>
|
||||
<SlIcon slot="icon" name="check2-circle" />
|
||||
<strong>Your changes have been saved</strong>
|
||||
<br />
|
||||
You can safely exit the app now.
|
||||
</SlAlert>
|
||||
|
||||
<SlAlert ref={neutral} variant="neutral" duration="3000" closable>
|
||||
<SlIcon slot="icon" name="gear" />
|
||||
<strong>Your settings have been updated</strong>
|
||||
<br />
|
||||
Settings will take affect on next login.
|
||||
</SlAlert>
|
||||
|
||||
<SlAlert ref={warning} variant="warning" duration="3000" closable>
|
||||
<SlIcon slot="icon" name="exclamation-triangle" />
|
||||
<strong>Your session has ended</strong>
|
||||
<br />
|
||||
Please login again to continue.
|
||||
</SlAlert>
|
||||
|
||||
<SlAlert ref={danger} variant="danger" duration="3000" closable>
|
||||
<SlIcon slot="icon" name="exclamation-octagon" />
|
||||
<strong>Your account has been deleted</strong>
|
||||
<br />
|
||||
We're very sorry to see you go!
|
||||
</SlAlert>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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">
|
||||
<sl-button type="primary">Create Toast</sl-button>
|
||||
<sl-button variant="primary">Create Toast</sl-button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -192,12 +389,12 @@ For convenience, you can create a utility that emits toast notifications with a
|
||||
const div = document.createElement('div');
|
||||
div.textContent = html;
|
||||
return div.innerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom function to emit toast notifications
|
||||
function notify(message, type = 'primary', icon = 'info-circle', duration = 3000) {
|
||||
function notify(message, variant = 'primary', icon = 'info-circle', duration = 3000) {
|
||||
const alert = Object.assign(document.createElement('sl-alert'), {
|
||||
type: type,
|
||||
variant,
|
||||
closable: true,
|
||||
duration: duration,
|
||||
innerHTML: `
|
||||
|
||||
123
docs/components/animated-image.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Animated Image
|
||||
|
||||
[component-header:sl-animated-image]
|
||||
|
||||
```html preview
|
||||
<sl-animated-image
|
||||
src="https://shoelace.style/assets/images/walk.gif"
|
||||
alt="Animation of untied shoes walking on pavement"
|
||||
></sl-animated-image>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlAnimatedImage
|
||||
src="https://shoelace.style/assets/images/walk.gif"
|
||||
alt="Animation of untied shoes walking on pavement"
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
?> 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
|
||||
<sl-animated-image
|
||||
src="https://shoelace.style/assets/images/tie.webp"
|
||||
alt="Animation of a shoe being tied"
|
||||
></sl-animated-image>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlAnimatedImage 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
|
||||
<sl-animated-image
|
||||
src="https://shoelace.style/assets/images/walk.gif"
|
||||
alt="Animation of untied shoes walking on pavement"
|
||||
style="width: 150px; height: 200px;"
|
||||
>
|
||||
</sl-animated-image>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlAnimatedImage
|
||||
src="https://shoelace.style/assets/images/walk.gif"
|
||||
alt="Animation of untied shoes walking on pavement"
|
||||
style={{ width: '150px', height: '200px' }}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-animated-image
|
||||
src="https://shoelace.style/assets/images/walk.gif"
|
||||
alt="Animation of untied shoes walking on pavement"
|
||||
class="animated-image-custom-control-box"
|
||||
></sl-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 { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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 = () => (
|
||||
<>
|
||||
<SlAnimatedImage
|
||||
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>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-animated-image]
|
||||
@@ -2,16 +2,14 @@
|
||||
|
||||
[component-header:sl-animation]
|
||||
|
||||
Animate elements declaratively with nearly 100 baked-in presets, or roll your own with custom keyframes. Powered by the [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API).
|
||||
|
||||
To animate an element, wrap it in `<sl-animation>` and set a `name` and `duration`. Refer to the [properties table](#properties) for additional options.
|
||||
To animate an element, wrap it in `<sl-animation>` and set an animation `name`. The animation will not start until you add the `play` attribute. Refer to the [properties table](#properties) for a list of all animation options.
|
||||
|
||||
```html preview
|
||||
<div class="animation-overview">
|
||||
<sl-animation name="bounce" duration="2000"><div class="box"></div></sl-animation>
|
||||
<sl-animation name="jello" duration="2000"><div class="box"></div></sl-animation>
|
||||
<sl-animation name="heartBeat" duration="2000"><div class="box"></div></sl-animation>
|
||||
<sl-animation name="flip" duration="2000"><div class="box"></div></sl-animation>
|
||||
<sl-animation name="bounce" duration="2000" play><div class="box"></div></sl-animation>
|
||||
<sl-animation name="jello" duration="2000" play><div class="box"></div></sl-animation>
|
||||
<sl-animation name="heartBeat" duration="2000" play><div class="box"></div></sl-animation>
|
||||
<sl-animation name="flip" duration="2000" play><div class="box"></div></sl-animation>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -19,13 +17,48 @@ To animate an element, wrap it in `<sl-animation>` and set a `name` and `duratio
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: var(--sl-color-primary-500);
|
||||
background-color: var(--sl-color-primary-600);
|
||||
margin: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
?> The animation will be applied only to the first child element found in `<sl-animation>`.
|
||||
```jsx react
|
||||
import { SlAnimation } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.animation-overview .box {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: var(--sl-color-primary-600);
|
||||
margin: 1.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div class="animation-overview">
|
||||
<SlAnimation name="bounce" duration={2000} play>
|
||||
<div class="box" />
|
||||
</SlAnimation>
|
||||
<SlAnimation name="jello" duration={2000} play>
|
||||
<div class="box" />
|
||||
</SlAnimation>
|
||||
<SlAnimation name="heartBeat" duration={2000} play>
|
||||
<div class="box" />
|
||||
</SlAnimation>
|
||||
<SlAnimation name="flip" duration={2000} play>
|
||||
<div class="box" />
|
||||
</SlAnimation>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
?> The animation will only be applied to the first child element found in `<sl-animation>`.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -35,55 +68,54 @@ This example demonstrates all of the baked-in animations and easings. Animations
|
||||
|
||||
```html preview
|
||||
<div class="animation-sandbox">
|
||||
<sl-animation name="bounce" easing="ease-in-out" duration="2000">
|
||||
<sl-animation name="bounce" easing="ease-in-out" duration="2000" play>
|
||||
<div class="box"></div>
|
||||
</sl-animation>
|
||||
|
||||
<div class="controls">
|
||||
<sl-select label="Animation" value="bounce"></sl-select>
|
||||
<sl-select label="Easing" value="linear"></sl-select>
|
||||
<sl-range min="0" max="2" step=".5" value="1"></sl-range>
|
||||
<sl-input label="Playback Rate" type="number" min="0" max="2" step=".25" value="1"></sl-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import { getAnimationNames, getEasingNames } from '/dist/shoelace.js';
|
||||
import { getAnimationNames, getEasingNames } from '/dist/utilities/animation.js';
|
||||
|
||||
const container = document.querySelector('.animation-sandbox');
|
||||
const animation = container.querySelector('sl-animation');
|
||||
const animationName = container.querySelector('.controls sl-select:nth-child(1)');
|
||||
const easingName = container.querySelector('.controls sl-select:nth-child(2)');
|
||||
const playbackRate = container.querySelector('sl-range');
|
||||
const playbackRate = container.querySelector('sl-input[type="number"]');
|
||||
const animations = getAnimationNames();
|
||||
const easings = getEasingNames();
|
||||
|
||||
animations.map(name => {
|
||||
const menuItem = Object.assign(document.createElement('sl-menu-item'), {
|
||||
const option = Object.assign(document.createElement('sl-option'), {
|
||||
textContent: name,
|
||||
value: name
|
||||
});
|
||||
animationName.appendChild(menuItem);
|
||||
});
|
||||
|
||||
easings.map(name => {
|
||||
const menuItem = Object.assign(document.createElement('sl-menu-item'), {
|
||||
textContent: name,
|
||||
value: name
|
||||
});
|
||||
easingName.appendChild(menuItem);
|
||||
animationName.appendChild(option);
|
||||
});
|
||||
|
||||
animationName.addEventListener('sl-change', () => animation.name = animationName.value);
|
||||
easingName.addEventListener('sl-change', () => animation.easing = easingName.value);
|
||||
playbackRate.addEventListener('sl-change', () => animation.playbackRate = playbackRate.value);
|
||||
playbackRate.tooltipFormatter = val => `Playback Rate = ${val}`;
|
||||
easings.map(name => {
|
||||
const option = Object.assign(document.createElement('sl-option'), {
|
||||
textContent: name,
|
||||
value: name
|
||||
});
|
||||
easingName.appendChild(option);
|
||||
});
|
||||
|
||||
animationName.addEventListener('sl-change', () => (animation.name = animationName.value));
|
||||
easingName.addEventListener('sl-change', () => (animation.easing = easingName.value));
|
||||
playbackRate.addEventListener('sl-input', () => (animation.playbackRate = playbackRate.value));
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.animation-sandbox .box {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: var(--sl-color-primary-500);
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.animation-sandbox .controls {
|
||||
@@ -115,11 +147,10 @@ Use an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
if (entries[0].isIntersecting) {
|
||||
// Start the animation when the box enters the viewport
|
||||
animation.pause = null;
|
||||
animation.play = true;
|
||||
} else {
|
||||
// Reset the animation when the box leaves the viewport
|
||||
animation.pause = true;
|
||||
animation.setCurrentTime(0);
|
||||
animation.play = false;
|
||||
animation.currentTime = 0;
|
||||
}
|
||||
});
|
||||
observer.observe(box);
|
||||
@@ -130,18 +161,68 @@ Use an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: var(--sl-color-primary-500);
|
||||
}
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { SlAnimation } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.animation-scroll {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
|
||||
.animation-scroll .box {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => {
|
||||
const animation = useRef(null);
|
||||
const box = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
if (entries[0].isIntersecting) {
|
||||
animation.current.play = true;
|
||||
} else {
|
||||
animation.current.play = false;
|
||||
animation.current.currentTime = 0;
|
||||
}
|
||||
});
|
||||
|
||||
if (box.current) {
|
||||
observer.observe(box.current);
|
||||
}
|
||||
}, [box]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="animation-scroll">
|
||||
<SlAnimation ref={animation} name="jackInTheBox" duration={2000} iterations={1}>
|
||||
<div ref={box} class="box" />
|
||||
</SlAnimation>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Custom Keyframe Formats
|
||||
|
||||
Supply your own [keyframe formats](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Keyframe_Formats) to build custom animations.
|
||||
|
||||
```html preview
|
||||
<div class="animation-keyframes">
|
||||
<sl-animation easing="ease-in-out" duration="2000">
|
||||
<sl-animation easing="ease-in-out" duration="2000" play>
|
||||
<div class="box"></div>
|
||||
</sl-animation>
|
||||
</div>
|
||||
@@ -170,9 +251,94 @@ Supply your own [keyframe formats](https://developer.mozilla.org/en-US/docs/Web/
|
||||
.animation-keyframes .box {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: var(--sl-color-primary-500);
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAnimation } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.animation-keyframes .box {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div class="animation-keyframes">
|
||||
<SlAnimation
|
||||
easing="ease-in-out"
|
||||
duration={2000}
|
||||
play
|
||||
keyframes={[
|
||||
{
|
||||
offset: 0,
|
||||
easing: 'cubic-bezier(0.250, 0.460, 0.450, 0.940)',
|
||||
fillMode: 'both',
|
||||
transformOrigin: 'center center',
|
||||
transform: 'rotate(0)'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
easing: 'cubic-bezier(0.250, 0.460, 0.450, 0.940)',
|
||||
fillMode: 'both',
|
||||
transformOrigin: 'center center',
|
||||
transform: 'rotate(90deg)'
|
||||
}
|
||||
]}
|
||||
>
|
||||
<div class="box" />
|
||||
</SlAnimation>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Playing Animations on Demand
|
||||
|
||||
Animations won't play until you apply the `play` attribute. You can omit it initially, then apply it on demand such as after a user interaction. In this example, the button will animate once every time the button is clicked.
|
||||
|
||||
```html preview
|
||||
<div class="animation-form">
|
||||
<sl-animation name="rubberBand" duration="1000" iterations="1">
|
||||
<sl-button variant="primary">Click me</sl-button>
|
||||
</sl-animation>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.animation-form');
|
||||
const animation = container.querySelector('sl-animation');
|
||||
const button = container.querySelector('sl-button');
|
||||
|
||||
button.addEventListener('click', () => {
|
||||
animation.play = true;
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlAnimation, SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [play, setPlay] = useState(false);
|
||||
|
||||
return (
|
||||
<div class="animation-form">
|
||||
<SlAnimation name="rubberBand" duration={1000} iterations={1} play={play} onSlFinish={() => setPlay(false)}>
|
||||
<SlButton variant="primary" onClick={() => setPlay(true)}>
|
||||
Click me
|
||||
</SlButton>
|
||||
</SlAnimation>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
[component-metadata:sl-animation]
|
||||
|
||||
@@ -2,23 +2,51 @@
|
||||
|
||||
[component-header:sl-avatar]
|
||||
|
||||
Avatars are used to represent a person or object.
|
||||
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
|
||||
<sl-avatar></sl-avatar>
|
||||
<sl-avatar label="User avatar"></sl-avatar>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlAvatar label="User avatar" />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Images
|
||||
|
||||
To use an image for the avatar, set the `image` and `alt` attributes. This will take priority and be shown over initials and icons.
|
||||
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
|
||||
<sl-avatar
|
||||
image="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
|
||||
alt="Gray tabby kitten looking down"
|
||||
label="Avatar of a gray tabby kitten looking down"
|
||||
></sl-avatar>
|
||||
<sl-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"
|
||||
></sl-avatar>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlAvatar
|
||||
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"
|
||||
/>
|
||||
<SlAvatar
|
||||
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
|
||||
@@ -26,7 +54,13 @@ To use an image for the avatar, set the `image` and `alt` attributes. This will
|
||||
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
|
||||
<sl-avatar initials="SL"></sl-avatar>
|
||||
<sl-avatar initials="SL" label="Avatar with initials: SL"></sl-avatar>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlAvatar initials="SL" label="Avatar with initials: SL" />;
|
||||
```
|
||||
|
||||
### Custom Icons
|
||||
@@ -34,31 +68,60 @@ When you don't have an image to use, you can set the `initials` attribute to sho
|
||||
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
|
||||
<sl-avatar>
|
||||
<sl-avatar label="Avatar with an image icon">
|
||||
<sl-icon slot="icon" name="image"></sl-icon>
|
||||
</sl-avatar>
|
||||
|
||||
<sl-avatar>
|
||||
<sl-avatar label="Avatar with an archive icon">
|
||||
<sl-icon slot="icon" name="archive"></sl-icon>
|
||||
</sl-avatar>
|
||||
|
||||
<sl-avatar>
|
||||
<sl-avatar label="Avatar with a briefcase icon">
|
||||
<sl-icon slot="icon" name="briefcase"></sl-icon>
|
||||
</sl-avatar>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAvatar, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlAvatar label="Avatar with an image icon">
|
||||
<SlIcon slot="icon" name="image" />
|
||||
</SlAvatar>
|
||||
|
||||
<SlAvatar label="Avatar with an archive icon">
|
||||
<SlIcon slot="icon" name="archive" />
|
||||
</SlAvatar>
|
||||
|
||||
<SlAvatar label="Avatar with a briefcase icon">
|
||||
<SlIcon slot="icon" name="briefcase" />
|
||||
</SlAvatar>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Shapes
|
||||
|
||||
Avatars can be shaped using the `shape` attribute.
|
||||
|
||||
```html preview
|
||||
<sl-avatar shape="square"></sl-avatar>
|
||||
|
||||
<sl-avatar shape="rounded"></sl-avatar>
|
||||
|
||||
<sl-avatar shape="circle"></sl-avatar>
|
||||
<sl-avatar shape="square" label="Square avatar"></sl-avatar>
|
||||
<sl-avatar shape="rounded" label="Rounded avatar"></sl-avatar>
|
||||
<sl-avatar shape="circle" label="Circle avatar"></sl-avatar>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAvatar, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlAvatar shape="square" label="Square avatar" />
|
||||
<SlAvatar shape="rounded" label="Rounded avatar" />
|
||||
<SlAvatar shape="circle" label="Circle avatar" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Avatar Groups
|
||||
|
||||
@@ -66,10 +129,25 @@ You can group avatars with a few lines of CSS.
|
||||
|
||||
```html preview
|
||||
<div class="avatar-group">
|
||||
<sl-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"></sl-avatar>
|
||||
<sl-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"></sl-avatar>
|
||||
<sl-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"></sl-avatar>
|
||||
<sl-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"></sl-avatar>
|
||||
<sl-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"
|
||||
></sl-avatar>
|
||||
|
||||
<sl-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"
|
||||
></sl-avatar>
|
||||
|
||||
<sl-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"
|
||||
></sl-avatar>
|
||||
|
||||
<sl-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"
|
||||
></sl-avatar>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -78,9 +156,51 @@ You can group avatars with a few lines of CSS.
|
||||
}
|
||||
|
||||
.avatar-group sl-avatar::part(base) {
|
||||
border: solid 2px white;
|
||||
border: solid 2px var(--sl-color-neutral-0);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlAvatar, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.avatar-group sl-avatar:not(:first-of-type) {
|
||||
margin-left: -1rem;
|
||||
}
|
||||
|
||||
.avatar-group sl-avatar::part(base) {
|
||||
border: solid 2px var(--sl-color-neutral-0);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="avatar-group">
|
||||
<SlAvatar
|
||||
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"
|
||||
/>
|
||||
|
||||
<SlAvatar
|
||||
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"
|
||||
/>
|
||||
|
||||
<SlAvatar
|
||||
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"
|
||||
/>
|
||||
|
||||
<SlAvatar
|
||||
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>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-avatar]
|
||||
|
||||
@@ -2,24 +2,42 @@
|
||||
|
||||
[component-header:sl-badge]
|
||||
|
||||
Badges are used to draw attention and display statuses or counts.
|
||||
|
||||
```html preview
|
||||
<sl-badge>Badge</sl-badge>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlBadge>Badge</SlBadge>;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Types
|
||||
### Variants
|
||||
|
||||
Set the `type` attribute to change the badge's type.
|
||||
Set the `variant` attribute to change the badge's variant.
|
||||
|
||||
```html preview
|
||||
<sl-badge type="primary">Primary</sl-badge>
|
||||
<sl-badge type="success">Success</sl-badge>
|
||||
<sl-badge type="info">Info</sl-badge>
|
||||
<sl-badge type="warning">Warning</sl-badge>
|
||||
<sl-badge type="danger">Danger</sl-badge>
|
||||
<sl-badge variant="primary">Primary</sl-badge>
|
||||
<sl-badge variant="success">Success</sl-badge>
|
||||
<sl-badge variant="neutral">Neutral</sl-badge>
|
||||
<sl-badge variant="warning">Warning</sl-badge>
|
||||
<sl-badge variant="danger">Danger</sl-badge>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlBadge variant="primary">Primary</SlBadge>
|
||||
<SlBadge variant="success">Success</SlBadge>
|
||||
<SlBadge variant="neutral">Neutral</SlBadge>
|
||||
<SlBadge variant="warning">Warning</SlBadge>
|
||||
<SlBadge variant="danger">Danger</SlBadge>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill Badges
|
||||
@@ -27,11 +45,35 @@ Set the `type` attribute to change the badge's type.
|
||||
Use the `pill` attribute to give badges rounded edges.
|
||||
|
||||
```html preview
|
||||
<sl-badge type="primary" pill>Primary</sl-badge>
|
||||
<sl-badge type="success" pill>Success</sl-badge>
|
||||
<sl-badge type="info" pill>Info</sl-badge>
|
||||
<sl-badge type="warning" pill>Warning</sl-badge>
|
||||
<sl-badge type="danger" pill>Danger</sl-badge>
|
||||
<sl-badge variant="primary" pill>Primary</sl-badge>
|
||||
<sl-badge variant="success" pill>Success</sl-badge>
|
||||
<sl-badge variant="neutral" pill>Neutral</sl-badge>
|
||||
<sl-badge variant="warning" pill>Warning</sl-badge>
|
||||
<sl-badge variant="danger" pill>Danger</sl-badge>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlBadge variant="primary" pill>
|
||||
Primary
|
||||
</SlBadge>
|
||||
<SlBadge variant="success" pill>
|
||||
Success
|
||||
</SlBadge>
|
||||
<SlBadge variant="neutral" pill>
|
||||
Neutral
|
||||
</SlBadge>
|
||||
<SlBadge variant="warning" pill>
|
||||
Warning
|
||||
</SlBadge>
|
||||
<SlBadge variant="danger" pill>
|
||||
Danger
|
||||
</SlBadge>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pulsating Badges
|
||||
@@ -40,11 +82,11 @@ Use the `pulse` attribute to draw attention to the badge with a subtle animation
|
||||
|
||||
```html preview
|
||||
<div class="badge-pulse">
|
||||
<sl-badge type="primary" pill pulse>1</sl-badge>
|
||||
<sl-badge type="success" pill pulse>1</sl-badge>
|
||||
<sl-badge type="info" pill pulse>1</sl-badge>
|
||||
<sl-badge type="warning" pill pulse>1</sl-badge>
|
||||
<sl-badge type="danger" pill pulse>1</sl-badge>
|
||||
<sl-badge variant="primary" pill pulse>1</sl-badge>
|
||||
<sl-badge variant="success" pill pulse>1</sl-badge>
|
||||
<sl-badge variant="neutral" pill pulse>1</sl-badge>
|
||||
<sl-badge variant="warning" pill pulse>1</sl-badge>
|
||||
<sl-badge variant="danger" pill pulse>1</sl-badge>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -54,6 +96,40 @@ Use the `pulse` attribute to draw attention to the badge with a subtle animation
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.badge-pulse sl-badge:not(:last-of-type) {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="badge-pulse">
|
||||
<SlBadge variant="primary" pill pulse>
|
||||
1
|
||||
</SlBadge>
|
||||
<SlBadge variant="success" pill pulse>
|
||||
1
|
||||
</SlBadge>
|
||||
<SlBadge variant="neutral" pill pulse>
|
||||
1
|
||||
</SlBadge>
|
||||
<SlBadge variant="warning" pill pulse>
|
||||
1
|
||||
</SlBadge>
|
||||
<SlBadge variant="danger" pill pulse>
|
||||
1
|
||||
</SlBadge>
|
||||
</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.
|
||||
@@ -64,27 +140,82 @@ One of the most common use cases for badges is attaching them to buttons. To mak
|
||||
<sl-badge pill>30</sl-badge>
|
||||
</sl-button>
|
||||
|
||||
<sl-button style="margin-left: 1rem;">
|
||||
<sl-button style="margin-inline-start: 1rem;">
|
||||
Warnings
|
||||
<sl-badge type="warning" pill>8</sl-badge>
|
||||
<sl-badge variant="warning" pill>8</sl-badge>
|
||||
</sl-button>
|
||||
|
||||
<sl-button style="margin-left: 1rem;">
|
||||
<sl-button style="margin-inline-start: 1rem;">
|
||||
Errors
|
||||
<sl-badge type="danger" pill>6</sl-badge>
|
||||
<sl-badge variant="danger" pill>6</sl-badge>
|
||||
</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBadge, SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton>
|
||||
Requests
|
||||
<SlBadge pill>30</SlBadge>
|
||||
</SlButton>
|
||||
|
||||
<SlButton style={{ marginInlineStart: '1rem' }}>
|
||||
Warnings
|
||||
<SlBadge variant="warning" pill>
|
||||
8
|
||||
</SlBadge>
|
||||
</SlButton>
|
||||
|
||||
<SlButton style={{ marginInlineStart: '1rem' }}>
|
||||
Errors
|
||||
<SlBadge variant="danger" pill>
|
||||
6
|
||||
</SlBadge>
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### With Menu Items
|
||||
|
||||
When including badges in menu items, use the `suffix` slot to make sure they're aligned correctly.
|
||||
|
||||
```html preview
|
||||
<sl-menu style="max-width: 240px; border: solid 1px var(--sl-panel-border-color); border-radius: var(--sl-border-radius-medium);">
|
||||
<sl-menu style="max-width: 240px;">
|
||||
<sl-menu-label>Messages</sl-menu-label>
|
||||
<sl-menu-item>Comments <sl-badge slot="suffix" type="info" pill>4</sl-badge></sl-menu-item>
|
||||
<sl-menu-item>Replies <sl-badge slot="suffix" type="info" pill>12</sl-badge></sl-menu-item>
|
||||
<sl-menu-item>Comments <sl-badge slot="suffix" variant="neutral" pill>4</sl-badge></sl-menu-item>
|
||||
<sl-menu-item>Replies <sl-badge slot="suffix" variant="neutral" pill>12</sl-badge></sl-menu-item>
|
||||
</sl-menu>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBadge, SlButton, SlMenu, SlMenuItem, SlMenuLabel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlMenu
|
||||
style={{
|
||||
maxWidth: '240px',
|
||||
border: 'solid 1px var(--sl-panel-border-color)',
|
||||
borderRadius: 'var(--sl-border-radius-medium)'
|
||||
}}
|
||||
>
|
||||
<SlMenuLabel>Messages</SlMenuLabel>
|
||||
<SlMenuItem>
|
||||
Comments
|
||||
<SlBadge slot="suffix" variant="neutral" pill>
|
||||
4
|
||||
</SlBadge>
|
||||
</SlMenuItem>
|
||||
<SlMenuItem>
|
||||
Replies
|
||||
<SlBadge slot="suffix" variant="neutral" pill>
|
||||
12
|
||||
</SlBadge>
|
||||
</SlMenuItem>
|
||||
</SlMenu>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-badge]
|
||||
|
||||
33
docs/components/breadcrumb-item.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Breadcrumb Item
|
||||
|
||||
[component-header:sl-breadcrumb-item]
|
||||
|
||||
```html preview
|
||||
<sl-breadcrumb>
|
||||
<sl-breadcrumb-item>
|
||||
<sl-icon slot="prefix" name="house"></sl-icon>
|
||||
Home
|
||||
</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Clothing</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Shirts</sl-breadcrumb-item>
|
||||
</sl-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBreadcrumb, SlBreadcrumbItem, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlBreadcrumb>
|
||||
<SlBreadcrumbItem>
|
||||
<SlIcon slot="prefix" name="house"></SlIcon>
|
||||
Home
|
||||
</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Clothing</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Shirts</SlBreadcrumbItem>
|
||||
</SlBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
?> Additional demonstrations can be found in the [breadcrumb examples](/components/breadcrumb).
|
||||
|
||||
[component-metadata:sl-breadcrumb-item]
|
||||
250
docs/components/breadcrumb.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# Breadcrumb
|
||||
|
||||
[component-header:sl-breadcrumb]
|
||||
|
||||
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
|
||||
<sl-breadcrumb>
|
||||
<sl-breadcrumb-item>Catalog</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Clothing</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Women's</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Shirts & Tops</sl-breadcrumb-item>
|
||||
</sl-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBreadcrumb, SlBreadcrumbItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlBreadcrumb>
|
||||
<SlBreadcrumbItem>Catalog</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Clothing</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Women's</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Shirts & Tops</SlBreadcrumbItem>
|
||||
</SlBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
## 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
|
||||
<sl-breadcrumb>
|
||||
<sl-breadcrumb-item href="https://example.com/home">Homepage</sl-breadcrumb-item>
|
||||
|
||||
<sl-breadcrumb-item href="https://example.com/home/services">Our Services</sl-breadcrumb-item>
|
||||
|
||||
<sl-breadcrumb-item href="https://example.com/home/services/digital">Digital Media</sl-breadcrumb-item>
|
||||
|
||||
<sl-breadcrumb-item href="https://example.com/home/services/digital/web-design">Web Design</sl-breadcrumb-item>
|
||||
</sl-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBreadcrumb, SlBreadcrumbItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlBreadcrumb>
|
||||
<SlBreadcrumbItem href="https://example.com/home">Homepage</SlBreadcrumbItem>
|
||||
|
||||
<SlBreadcrumbItem href="https://example.com/home/services">Our Services</SlBreadcrumbItem>
|
||||
|
||||
<SlBreadcrumbItem href="https://example.com/home/services/digital">Digital Media</SlBreadcrumbItem>
|
||||
|
||||
<SlBreadcrumbItem href="https://example.com/home/services/digital/web-design">Web Design</SlBreadcrumbItem>
|
||||
</SlBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-breadcrumb>
|
||||
<sl-icon name="dot" slot="separator"></sl-icon>
|
||||
<sl-breadcrumb-item>First</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Second</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Third</sl-breadcrumb-item>
|
||||
</sl-breadcrumb>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-breadcrumb>
|
||||
<sl-icon name="arrow-right" slot="separator"></sl-icon>
|
||||
<sl-breadcrumb-item>First</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Second</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Third</sl-breadcrumb-item>
|
||||
</sl-breadcrumb>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-breadcrumb>
|
||||
<span slot="separator">/</span>
|
||||
<sl-breadcrumb-item>First</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Second</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Third</sl-breadcrumb-item>
|
||||
</sl-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
|
||||
import { SlBreadcrumb, SlBreadcrumbItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlBreadcrumb>
|
||||
<sl-icon name="dot" slot="separator" />
|
||||
<SlBreadcrumbItem>First</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Second</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Third</SlBreadcrumbItem>
|
||||
</SlBreadcrumb>
|
||||
|
||||
<br />
|
||||
|
||||
<SlBreadcrumb>
|
||||
<sl-icon name="arrow-right" slot="separator" />
|
||||
<SlBreadcrumbItem>First</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Second</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Third</SlBreadcrumbItem>
|
||||
</SlBreadcrumb>
|
||||
|
||||
<br />
|
||||
|
||||
<SlBreadcrumb>
|
||||
<span slot="separator">/</span>
|
||||
<SlBreadcrumbItem>First</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Second</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Third</SlBreadcrumbItem>
|
||||
</SlBreadcrumb>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefixes
|
||||
|
||||
Use the `prefix` slot to add content before any breadcrumb item.
|
||||
|
||||
```html preview
|
||||
<sl-breadcrumb>
|
||||
<sl-breadcrumb-item>
|
||||
<sl-icon slot="prefix" name="house"></sl-icon>
|
||||
Home
|
||||
</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Articles</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Traveling</sl-breadcrumb-item>
|
||||
</sl-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBreadcrumb, SlBreadcrumbItem, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlBreadcrumb>
|
||||
<SlBreadcrumbItem>
|
||||
<SlIcon slot="prefix" name="house" />
|
||||
Home
|
||||
</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Articles</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Traveling</SlBreadcrumbItem>
|
||||
</SlBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
### Suffixes
|
||||
|
||||
Use the `suffix` slot to add content after any breadcrumb item.
|
||||
|
||||
```html preview
|
||||
<sl-breadcrumb>
|
||||
<sl-breadcrumb-item>Documents</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Policies</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>
|
||||
Security
|
||||
<sl-icon slot="suffix" name="shield-lock"></sl-icon>
|
||||
</sl-breadcrumb-item>
|
||||
</sl-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBreadcrumb, SlBreadcrumbItem, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlBreadcrumb>
|
||||
<SlBreadcrumbItem>Documents</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Policies</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>
|
||||
Security
|
||||
<SlIcon slot="suffix" name="shield-lock"></SlIcon>
|
||||
</SlBreadcrumbItem>
|
||||
</SlBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
### With Dropdowns
|
||||
|
||||
Dropdown menus can be placed in a prefix or suffix slot to provide additional options.
|
||||
|
||||
```html preview
|
||||
<sl-breadcrumb>
|
||||
<sl-breadcrumb-item>Homepage</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Our Services</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>Digital Media</sl-breadcrumb-item>
|
||||
<sl-breadcrumb-item>
|
||||
Web Design
|
||||
<sl-dropdown slot="suffix">
|
||||
<sl-button slot="trigger" size="small" circle>
|
||||
<sl-icon label="More options" name="three-dots"></sl-icon>
|
||||
</sl-button>
|
||||
<sl-menu>
|
||||
<sl-menu-item type="checkbox" checked>Web Design</sl-menu-item>
|
||||
<sl-menu-item type="checkbox">Web Development</sl-menu-item>
|
||||
<sl-menu-item type="checkbox">Marketing</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
</sl-breadcrumb-item>
|
||||
</sl-breadcrumb>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import {
|
||||
SlBreadcrumb,
|
||||
SlBreadcrumbItem,
|
||||
SlButton,
|
||||
SlDropdown,
|
||||
SlIcon,
|
||||
SlMenu,
|
||||
SlMenuItem
|
||||
} from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlBreadcrumb>
|
||||
<SlBreadcrumbItem>Homepage</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Our Services</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>Digital Media</SlBreadcrumbItem>
|
||||
<SlBreadcrumbItem>
|
||||
Web Design
|
||||
<SlDropdown slot="suffix">
|
||||
<SlButton slot="trigger" size="small" circle>
|
||||
<SlIcon label="More options" name="three-dots"></SlIcon>
|
||||
</SlButton>
|
||||
<SlMenu>
|
||||
<SlMenuItem type="checkbox" checked>
|
||||
Web Design
|
||||
</SlMenuItem>
|
||||
<SlMenuItem type="checkbox">Web Development</SlMenuItem>
|
||||
<SlMenuItem type="checkbox">Marketing</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
</SlBreadcrumbItem>
|
||||
</SlBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-breadcrumb]
|
||||
@@ -2,16 +2,26 @@
|
||||
|
||||
[component-header:sl-button-group]
|
||||
|
||||
Button groups can be used to group related buttons into sections.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button>Left</sl-button>
|
||||
<sl-button>Center</sl-button>
|
||||
<sl-button>Right</sl-button>
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton>Left</SlButton>
|
||||
<SlButton>Center</SlButton>
|
||||
<SlButton>Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Button Sizes
|
||||
@@ -19,107 +29,239 @@ Button groups can be used to group related buttons into sections.
|
||||
All button sizes are supported, but avoid mixing sizes within the same button group.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button size="small">Left</sl-button>
|
||||
<sl-button size="small">Center</sl-button>
|
||||
<sl-button size="small">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button size="medium">Left</sl-button>
|
||||
<sl-button size="medium">Center</sl-button>
|
||||
<sl-button size="medium">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button size="large">Left</sl-button>
|
||||
<sl-button size="large">Center</sl-button>
|
||||
<sl-button size="large">Right</sl-button>
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton size="small">Left</SlButton>
|
||||
<SlButton size="small">Center</SlButton>
|
||||
<SlButton size="small">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton size="medium">Left</SlButton>
|
||||
<SlButton size="medium">Center</SlButton>
|
||||
<SlButton size="medium">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton size="large">Left</SlButton>
|
||||
<SlButton size="large">Center</SlButton>
|
||||
<SlButton size="large">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Theme Buttons
|
||||
|
||||
Theme buttons are supported through the button's `type` attribute.
|
||||
Theme buttons are supported through the button's `variant` attribute.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button type="primary">Left</sl-button>
|
||||
<sl-button type="primary">Center</sl-button>
|
||||
<sl-button type="primary">Right</sl-button>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button variant="primary">Left</sl-button>
|
||||
<sl-button variant="primary">Center</sl-button>
|
||||
<sl-button variant="primary">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button type="success">Left</sl-button>
|
||||
<sl-button type="success">Center</sl-button>
|
||||
<sl-button type="success">Right</sl-button>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button variant="success">Left</sl-button>
|
||||
<sl-button variant="success">Center</sl-button>
|
||||
<sl-button variant="success">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button type="info">Left</sl-button>
|
||||
<sl-button type="info">Center</sl-button>
|
||||
<sl-button type="info">Right</sl-button>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button variant="neutral">Left</sl-button>
|
||||
<sl-button variant="neutral">Center</sl-button>
|
||||
<sl-button variant="neutral">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button type="warning">Left</sl-button>
|
||||
<sl-button type="warning">Center</sl-button>
|
||||
<sl-button type="warning">Right</sl-button>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button variant="warning">Left</sl-button>
|
||||
<sl-button variant="warning">Center</sl-button>
|
||||
<sl-button variant="warning">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button type="danger">Left</sl-button>
|
||||
<sl-button type="danger">Center</sl-button>
|
||||
<sl-button type="danger">Right</sl-button>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button variant="danger">Left</sl-button>
|
||||
<sl-button variant="danger">Center</sl-button>
|
||||
<sl-button variant="danger">Right</sl-button>
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton variant="primary">Left</SlButton>
|
||||
<SlButton variant="primary">Center</SlButton>
|
||||
<SlButton variant="primary">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton variant="success">Left</SlButton>
|
||||
<SlButton variant="success">Center</SlButton>
|
||||
<SlButton variant="success">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton variant="neutral">Left</SlButton>
|
||||
<SlButton variant="neutral">Center</SlButton>
|
||||
<SlButton variant="neutral">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton variant="warning">Left</SlButton>
|
||||
<SlButton variant="warning">Center</SlButton>
|
||||
<SlButton variant="warning">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton variant="danger">Left</SlButton>
|
||||
<SlButton variant="danger">Center</SlButton>
|
||||
<SlButton variant="danger">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill Buttons
|
||||
|
||||
Pill buttons are supported through the button's `pill` attribute.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button size="small" pill>Left</sl-button>
|
||||
<sl-button size="small" pill>Center</sl-button>
|
||||
<sl-button size="small" pill>Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button size="medium" pill>Left</sl-button>
|
||||
<sl-button size="medium" pill>Center</sl-button>
|
||||
<sl-button size="medium" pill>Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-button size="large" pill>Left</sl-button>
|
||||
<sl-button size="large" pill>Center</sl-button>
|
||||
<sl-button size="large" pill>Right</sl-button>
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton size="small" pill>
|
||||
Left
|
||||
</SlButton>
|
||||
<SlButton size="small" pill>
|
||||
Center
|
||||
</SlButton>
|
||||
<SlButton size="small" pill>
|
||||
Right
|
||||
</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton size="medium" pill>
|
||||
Left
|
||||
</SlButton>
|
||||
<SlButton size="medium" pill>
|
||||
Center
|
||||
</SlButton>
|
||||
<SlButton size="medium" pill>
|
||||
Right
|
||||
</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlButton size="large" pill>
|
||||
Left
|
||||
</SlButton>
|
||||
<SlButton size="large" pill>
|
||||
Center
|
||||
</SlButton>
|
||||
<SlButton size="large" pill>
|
||||
Right
|
||||
</SlButton>
|
||||
</SlButtonGroup>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Dropdowns in Button Groups
|
||||
|
||||
Dropdowns can be placed inside button groups as long as the trigger is a `<sl-button>` element.
|
||||
Dropdowns can be placed inside button groups as long as the trigger is an `<sl-button>` element.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button-group label="Example Button Group">
|
||||
<sl-button>Button</sl-button>
|
||||
<sl-button>Button</sl-button>
|
||||
<sl-dropdown>
|
||||
@@ -133,15 +275,38 @@ Dropdowns can be placed inside button groups as long as the trigger is a `<sl-bu
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlButtonGroup label="Example Button Group">
|
||||
<SlButton>Button</SlButton>
|
||||
<SlButton>Button</SlButton>
|
||||
<SlDropdown>
|
||||
<SlButton slot="trigger" caret>
|
||||
Dropdown
|
||||
</SlButton>
|
||||
<SlMenu>
|
||||
<SlMenuItem>Item 1</SlMenuItem>
|
||||
<SlMenuItem>Item 2</SlMenuItem>
|
||||
<SlMenuItem>Item 3</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
</SlButtonGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Split Buttons
|
||||
|
||||
Create a split button using a button and a dropdown.
|
||||
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
|
||||
<sl-button-group>
|
||||
<sl-button type="primary">Save</sl-button>
|
||||
<sl-button-group label="Example Button Group">
|
||||
<sl-button variant="primary">Save</sl-button>
|
||||
<sl-dropdown placement="bottom-end">
|
||||
<sl-button slot="trigger" type="primary" caret></sl-button>
|
||||
<sl-button slot="trigger" variant="primary" caret>
|
||||
<sl-visually-hidden>More options</sl-visually-hidden>
|
||||
</sl-button>
|
||||
<sl-menu>
|
||||
<sl-menu-item>Save</sl-menu-item>
|
||||
<sl-menu-item>Save as…</sl-menu-item>
|
||||
@@ -151,12 +316,30 @@ Create a split button using a button and a dropdown.
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlButtonGroup label="Example Button Group">
|
||||
<SlButton variant="primary">Save</SlButton>
|
||||
<SlDropdown placement="bottom-end">
|
||||
<SlButton slot="trigger" variant="primary" caret></SlButton>
|
||||
<SlMenu>
|
||||
<SlMenuItem>Save</SlMenuItem>
|
||||
<SlMenuItem>Save as…</SlMenuItem>
|
||||
<SlMenuItem>Save all</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
</SlButtonGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Tooltips in Button Groups
|
||||
|
||||
Buttons can be wrapped in tooltips to provide more detail when the user interacts with them.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-tooltip content="I'm on the left">
|
||||
<sl-button>Left</sl-button>
|
||||
</sl-tooltip>
|
||||
@@ -171,6 +354,28 @@ Buttons can be wrapped in tooltips to provide more detail when the user interact
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlTooltip content="I'm on the left">
|
||||
<SlButton>Left</SlButton>
|
||||
</SlTooltip>
|
||||
|
||||
<SlTooltip content="I'm in the middle">
|
||||
<SlButton>Center</SlButton>
|
||||
</SlTooltip>
|
||||
|
||||
<SlTooltip content="I'm on the right">
|
||||
<SlButton>Right</SlButton>
|
||||
</SlTooltip>
|
||||
</SlButtonGroup>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Toolbar Example
|
||||
|
||||
Create interactive toolbars with button groups.
|
||||
@@ -179,34 +384,34 @@ Create interactive toolbars with button groups.
|
||||
<div class="button-group-toolbar">
|
||||
<sl-button-group label="History">
|
||||
<sl-tooltip content="Undo">
|
||||
<sl-button><sl-icon name="arrow-counterclockwise"></sl-icon></sl-button>
|
||||
<sl-button><sl-icon name="arrow-counterclockwise" label="Undo"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
<sl-tooltip content="Redo">
|
||||
<sl-button><sl-icon name="arrow-clockwise"></sl-icon></sl-button>
|
||||
<sl-button><sl-icon name="arrow-clockwise" label="Redo"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
</sl-button-group>
|
||||
|
||||
<sl-button-group label="Formatting">
|
||||
<sl-tooltip content="Bold">
|
||||
<sl-button><sl-icon name="type-bold"></sl-icon></sl-button>
|
||||
<sl-button><sl-icon name="type-bold" label="Bold"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
<sl-tooltip content="Italic">
|
||||
<sl-button><sl-icon name="type-italic"></sl-icon></sl-button>
|
||||
<sl-button><sl-icon name="type-italic" label="Italic"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
<sl-tooltip content="Underline">
|
||||
<sl-button><sl-icon name="type-underline"></sl-icon></sl-button>
|
||||
<sl-button><sl-icon name="type-underline" label="Underline"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
</sl-button-group>
|
||||
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-tooltip content="Align Left">
|
||||
<sl-button><sl-icon name="justify-left"></sl-icon></sl-button>
|
||||
<sl-button><sl-icon name="justify-left" label="Align Left"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
<sl-tooltip content="Align Center">
|
||||
<sl-button><sl-icon name="justify"></sl-icon></sl-button>
|
||||
<sl-button><sl-icon name="justify" label="Align Center"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
<sl-tooltip content="Align Right">
|
||||
<sl-button><sl-icon name="justify-right"></sl-icon></sl-button>
|
||||
<sl-button><sl-icon name="justify-right" label="Align Right"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
</sl-button-group>
|
||||
</div>
|
||||
@@ -218,4 +423,71 @@ Create interactive toolbars with button groups.
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup, SlIcon, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.button-group-toolbar sl-button-group:not(:last-of-type) {
|
||||
margin-right: var(--sl-spacing-x-small);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="button-group-toolbar">
|
||||
<SlButtonGroup label="History">
|
||||
<SlTooltip content="Undo">
|
||||
<SlButton>
|
||||
<SlIcon name="arrow-counterclockwise"></SlIcon>
|
||||
</SlButton>
|
||||
</SlTooltip>
|
||||
<SlTooltip content="Redo">
|
||||
<SlButton>
|
||||
<SlIcon name="arrow-clockwise"></SlIcon>
|
||||
</SlButton>
|
||||
</SlTooltip>
|
||||
</SlButtonGroup>
|
||||
|
||||
<SlButtonGroup label="Formatting">
|
||||
<SlTooltip content="Bold">
|
||||
<SlButton>
|
||||
<SlIcon name="type-bold"></SlIcon>
|
||||
</SlButton>
|
||||
</SlTooltip>
|
||||
<SlTooltip content="Italic">
|
||||
<SlButton>
|
||||
<SlIcon name="type-italic"></SlIcon>
|
||||
</SlButton>
|
||||
</SlTooltip>
|
||||
<SlTooltip content="Underline">
|
||||
<SlButton>
|
||||
<SlIcon name="type-underline"></SlIcon>
|
||||
</SlButton>
|
||||
</SlTooltip>
|
||||
</SlButtonGroup>
|
||||
|
||||
<SlButtonGroup label="Alignment">
|
||||
<SlTooltip content="Align Left">
|
||||
<SlButton>
|
||||
<SlIcon name="justify-left"></SlIcon>
|
||||
</SlButton>
|
||||
</SlTooltip>
|
||||
<SlTooltip content="Align Center">
|
||||
<SlButton>
|
||||
<SlIcon name="justify"></SlIcon>
|
||||
</SlButton>
|
||||
</SlTooltip>
|
||||
<SlTooltip content="Align Right">
|
||||
<SlButton>
|
||||
<SlIcon name="justify-right"></SlIcon>
|
||||
</SlButton>
|
||||
</SlTooltip>
|
||||
</SlButtonGroup>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-button-group]
|
||||
|
||||
@@ -2,30 +2,49 @@
|
||||
|
||||
[component-header:sl-button]
|
||||
|
||||
Buttons represent actions that are available to the user.
|
||||
|
||||
```html preview
|
||||
<sl-button>Button</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlButton>Button</SlButton>;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Types
|
||||
### Variants
|
||||
|
||||
Use the `type` attribute to set the button's type.
|
||||
Use the `variant` attribute to set the button's variant.
|
||||
|
||||
```html preview
|
||||
<sl-button type="default">Default</sl-button>
|
||||
<sl-button type="primary">Primary</sl-button>
|
||||
<sl-button type="success">Success</sl-button>
|
||||
<sl-button type="info">Info</sl-button>
|
||||
<sl-button type="warning">Warning</sl-button>
|
||||
<sl-button type="danger">Danger</sl-button>
|
||||
<sl-button variant="default">Default</sl-button>
|
||||
<sl-button variant="primary">Primary</sl-button>
|
||||
<sl-button variant="success">Success</sl-button>
|
||||
<sl-button variant="neutral">Neutral</sl-button>
|
||||
<sl-button variant="warning">Warning</sl-button>
|
||||
<sl-button variant="danger">Danger</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton variant="default">Default</SlButton>
|
||||
<SlButton variant="primary">Primary</SlButton>
|
||||
<SlButton variant="success">Success</SlButton>
|
||||
<SlButton variant="neutral">Neutral</SlButton>
|
||||
<SlButton variant="warning">Warning</SlButton>
|
||||
<SlButton variant="danger">Danger</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` prop to change a button's size.
|
||||
Use the `size` attribute to change a button's size.
|
||||
|
||||
```html preview
|
||||
<sl-button size="small">Small</sl-button>
|
||||
@@ -33,9 +52,61 @@ Use the `size` prop to change a button's size.
|
||||
<sl-button size="large">Large</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton size="small">Small</SlButton>
|
||||
<SlButton size="medium">Medium</SlButton>
|
||||
<SlButton size="large">Large</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Outline Buttons
|
||||
|
||||
Use the `outline` attribute to draw outlined buttons with transparent backgrounds.
|
||||
|
||||
```html preview
|
||||
<sl-button variant="default" outline>Default</sl-button>
|
||||
<sl-button variant="primary" outline>Primary</sl-button>
|
||||
<sl-button variant="success" outline>Success</sl-button>
|
||||
<sl-button variant="neutral" outline>Neutral</sl-button>
|
||||
<sl-button variant="warning" outline>Warning</sl-button>
|
||||
<sl-button variant="danger" outline>Danger</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton variant="default" outline>
|
||||
Default
|
||||
</SlButton>
|
||||
<SlButton variant="primary" outline>
|
||||
Primary
|
||||
</SlButton>
|
||||
<SlButton variant="success" outline>
|
||||
Success
|
||||
</SlButton>
|
||||
<SlButton variant="neutral" outline>
|
||||
Neutral
|
||||
</SlButton>
|
||||
<SlButton variant="warning" outline>
|
||||
Warning
|
||||
</SlButton>
|
||||
<SlButton variant="danger" outline>
|
||||
Danger
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill Buttons
|
||||
|
||||
Use the `pill` prop to give buttons rounded edges.
|
||||
Use the `pill` attribute to give buttons rounded edges.
|
||||
|
||||
```html preview
|
||||
<sl-button size="small" pill>Small</sl-button>
|
||||
@@ -43,24 +114,86 @@ Use the `pill` prop to give buttons rounded edges.
|
||||
<sl-button size="large" pill>Large</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton size="small" pill>
|
||||
Small
|
||||
</SlButton>
|
||||
<SlButton size="medium" pill>
|
||||
Medium
|
||||
</SlButton>
|
||||
<SlButton size="large" pill>
|
||||
Large
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Circle Buttons
|
||||
|
||||
Use the `circle` prop to create circular icon buttons.
|
||||
Use the `circle` attribute to create circular icon buttons. When this attribute is set, the button expects a single `<sl-icon>` in the default slot.
|
||||
|
||||
```html preview
|
||||
<sl-button type="default" size="small" circle><sl-icon name="gear"></sl-icon></sl-button>
|
||||
<sl-button type="default" size="medium" circle><sl-icon name="gear"></sl-icon></sl-button>
|
||||
<sl-button type="default" size="large" circle><sl-icon name="gear"></sl-icon></sl-button>
|
||||
<sl-button variant="default" size="small" circle>
|
||||
<sl-icon name="gear" label="Settings"></sl-icon>
|
||||
</sl-button>
|
||||
|
||||
<sl-button variant="default" size="medium" circle>
|
||||
<sl-icon name="gear" label="Settings"></sl-icon>
|
||||
</sl-button>
|
||||
|
||||
<sl-button variant="default" size="large" circle>
|
||||
<sl-icon name="gear" label="Settings"></sl-icon>
|
||||
</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton variant="default" size="small" circle>
|
||||
<SlIcon name="gear" />
|
||||
</SlButton>
|
||||
<SlButton variant="default" size="medium" circle>
|
||||
<SlIcon name="gear" />
|
||||
</SlButton>
|
||||
<SlButton variant="default" size="large" circle>
|
||||
<SlIcon name="gear" />
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Text Buttons
|
||||
|
||||
Use the `text` type to create text buttons that share the same size as regular buttons but don't have backgrounds or borders.
|
||||
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
|
||||
<sl-button type="text" size="small">Text</sl-button>
|
||||
<sl-button type="text" size="medium">Text</sl-button>
|
||||
<sl-button type="text" size="large">Text</sl-button>
|
||||
<sl-button variant="text" size="small">Text</sl-button>
|
||||
<sl-button variant="text" size="medium">Text</sl-button>
|
||||
<sl-button variant="text" size="large">Text</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton variant="text" size="small">
|
||||
Text
|
||||
</SlButton>
|
||||
<SlButton variant="text" size="medium">
|
||||
Text
|
||||
</SlButton>
|
||||
<SlButton variant="text" size="large">
|
||||
Text
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Link Buttons
|
||||
@@ -74,16 +207,53 @@ It's often helpful to have a button that works like a link. This is possible by
|
||||
<sl-button href="https://example.com/" disabled>Disabled</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton href="https://example.com/">Link</SlButton>
|
||||
<SlButton href="https://example.com/" target="_blank">
|
||||
New Window
|
||||
</SlButton>
|
||||
<SlButton href="/assets/images/wordmark.svg" download="shoelace.svg">
|
||||
Download
|
||||
</SlButton>
|
||||
<SlButton href="https://example.com/" disabled>
|
||||
Disabled
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
?> 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 its `width`. This is useful for making buttons span the full width of their container on smaller screens.
|
||||
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
|
||||
<sl-button type="default" size="small" style="width: 100%; margin-bottom: 1rem;">Small</sl-button>
|
||||
<sl-button type="default" size="medium" style="width: 100%; margin-bottom: 1rem;">Medium</sl-button>
|
||||
<sl-button type="default" size="large" style="width: 100%;">Large</sl-button>
|
||||
<sl-button variant="default" size="small" style="width: 100%; margin-bottom: 1rem;">Small</sl-button>
|
||||
<sl-button variant="default" size="medium" style="width: 100%; margin-bottom: 1rem;">Medium</sl-button>
|
||||
<sl-button variant="default" size="large" style="width: 100%;">Large</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton variant="default" size="small" style={{ width: '100%', marginBottom: '1rem' }}>
|
||||
Small
|
||||
</SlButton>
|
||||
<SlButton variant="default" size="medium" style={{ width: '100%', marginBottom: '1rem' }}>
|
||||
Medium
|
||||
</SlButton>
|
||||
<SlButton variant="default" size="large" style={{ width: '100%' }}>
|
||||
Large
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix and Suffix Icons
|
||||
@@ -91,62 +261,124 @@ As expected, buttons can be given a custom width by setting its `width`. This is
|
||||
Use the `prefix` and `suffix` slots to add icons.
|
||||
|
||||
```html preview
|
||||
<sl-button type="default" size="small">
|
||||
<sl-button variant="default" size="small">
|
||||
<sl-icon slot="prefix" name="gear"></sl-icon>
|
||||
Settings
|
||||
</sl-button>
|
||||
|
||||
<sl-button type="default" size="small">
|
||||
<sl-button variant="default" size="small">
|
||||
<sl-icon slot="suffix" name="arrow-counterclockwise"></sl-icon>
|
||||
Refresh
|
||||
</sl-button>
|
||||
|
||||
<sl-button type="default" size="small">
|
||||
<sl-button variant="default" size="small">
|
||||
<sl-icon slot="prefix" name="link-45deg"></sl-icon>
|
||||
<sl-icon slot="suffix" name="box-arrow-up-right"></sl-icon>
|
||||
Open
|
||||
</sl-button>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button type="default">
|
||||
<sl-button variant="default">
|
||||
<sl-icon slot="prefix" name="gear"></sl-icon>
|
||||
Settings
|
||||
</sl-button>
|
||||
|
||||
<sl-button type="default">
|
||||
<sl-button variant="default">
|
||||
<sl-icon slot="suffix" name="arrow-counterclockwise"></sl-icon>
|
||||
Refresh
|
||||
</sl-button>
|
||||
|
||||
<sl-button type="default">
|
||||
<sl-button variant="default">
|
||||
<sl-icon slot="prefix" name="link-45deg"></sl-icon>
|
||||
<sl-icon slot="suffix" name="box-arrow-up-right"></sl-icon>
|
||||
Open
|
||||
</sl-button>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button type="default" size="large">
|
||||
<sl-button variant="default" size="large">
|
||||
<sl-icon slot="prefix" name="gear"></sl-icon>
|
||||
Settings
|
||||
</sl-button>
|
||||
|
||||
<sl-button type="default" size="large">
|
||||
<sl-button variant="default" size="large">
|
||||
<sl-icon slot="suffix" name="arrow-counterclockwise"></sl-icon>
|
||||
Refresh
|
||||
</sl-button>
|
||||
|
||||
<sl-button type="default" size="large">
|
||||
<sl-button variant="default" size="large">
|
||||
<sl-icon slot="prefix" name="link-45deg"></sl-icon>
|
||||
<sl-icon slot="suffix" name="box-arrow-up-right"></sl-icon>
|
||||
Open
|
||||
</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton variant="default" size="small">
|
||||
<SlIcon slot="prefix" name="gear"></SlIcon>
|
||||
Settings
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="default" size="small">
|
||||
<SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon>
|
||||
Refresh
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="default" size="small">
|
||||
<SlIcon slot="prefix" name="link-45deg"></SlIcon>
|
||||
<SlIcon slot="suffix" name="box-arrow-up-right"></SlIcon>
|
||||
Open
|
||||
</SlButton>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButton variant="default">
|
||||
<SlIcon slot="prefix" name="gear"></SlIcon>
|
||||
Settings
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="default">
|
||||
<SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon>
|
||||
Refresh
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="default">
|
||||
<SlIcon slot="prefix" name="link-45deg"></SlIcon>
|
||||
<SlIcon slot="suffix" name="box-arrow-up-right"></SlIcon>
|
||||
Open
|
||||
</SlButton>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButton variant="default" size="large">
|
||||
<SlIcon slot="prefix" name="gear"></SlIcon>
|
||||
Settings
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="default" size="large">
|
||||
<SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon>
|
||||
Refresh
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="default" size="large">
|
||||
<SlIcon slot="prefix" name="link-45deg"></SlIcon>
|
||||
<SlIcon slot="suffix" name="box-arrow-up-right"></SlIcon>
|
||||
Open
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Caret
|
||||
|
||||
Use the `caret` prop to add a dropdown indicator when a button will trigger a dropdown, menu, or popover.
|
||||
Use the `caret` attribute to add a dropdown indicator when a button will trigger a dropdown, menu, or popover.
|
||||
|
||||
```html preview
|
||||
<sl-button size="small" caret>Small</sl-button>
|
||||
@@ -154,35 +386,112 @@ Use the `caret` prop to add a dropdown indicator when a button will trigger a dr
|
||||
<sl-button size="large" caret>Large</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton size="small" caret>
|
||||
Small
|
||||
</SlButton>
|
||||
<SlButton size="medium" caret>
|
||||
Medium
|
||||
</SlButton>
|
||||
<SlButton size="large" caret>
|
||||
Large
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Loading
|
||||
|
||||
Use the `loading` prop to make a button busy. The width will remain the same as before, preventing adjacent elements from moving around. Clicks will be suppressed until the loading state is removed.
|
||||
Use the `loading` attribute to make a button busy. The width will remain the same as before, preventing adjacent elements from moving around. Clicks will be suppressed until the loading state is removed.
|
||||
|
||||
```html preview
|
||||
<sl-button type="default" loading>Default</sl-button>
|
||||
<sl-button type="primary" loading>Primary</sl-button>
|
||||
<sl-button type="success" loading>Success</sl-button>
|
||||
<sl-button type="info" loading>Info</sl-button>
|
||||
<sl-button type="warning" loading>Warning</sl-button>
|
||||
<sl-button type="danger" loading>Danger</sl-button>
|
||||
<sl-button variant="default" loading>Default</sl-button>
|
||||
<sl-button variant="primary" loading>Primary</sl-button>
|
||||
<sl-button variant="success" loading>Success</sl-button>
|
||||
<sl-button variant="neutral" loading>Neutral</sl-button>
|
||||
<sl-button variant="warning" loading>Warning</sl-button>
|
||||
<sl-button variant="danger" loading>Danger</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton variant="default" loading>
|
||||
Default
|
||||
</SlButton>
|
||||
<SlButton variant="primary" loading>
|
||||
Primary
|
||||
</SlButton>
|
||||
<SlButton variant="success" loading>
|
||||
Success
|
||||
</SlButton>
|
||||
<SlButton variant="neutral" loading>
|
||||
Neutral
|
||||
</SlButton>
|
||||
<SlButton variant="warning" loading>
|
||||
Warning
|
||||
</SlButton>
|
||||
<SlButton variant="danger" loading>
|
||||
Danger
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` prop to disable a button. Clicks will be suppressed until the disabled state is removed.
|
||||
Use the `disabled` attribute to disable a button. Clicks will be suppressed until the disabled state is removed.
|
||||
|
||||
```html preview
|
||||
<sl-button type="default" disabled>Default</sl-button>
|
||||
<sl-button type="primary" disabled>Primary</sl-button>
|
||||
<sl-button type="success" disabled>Success</sl-button>
|
||||
<sl-button type="info" disabled>Info</sl-button>
|
||||
<sl-button type="warning" disabled>Warning</sl-button>
|
||||
<sl-button type="danger" disabled>Danger</sl-button>
|
||||
<sl-button variant="default" disabled>Default</sl-button>
|
||||
<sl-button variant="primary" disabled>Primary</sl-button>
|
||||
<sl-button variant="success" disabled>Success</sl-button>
|
||||
<sl-button variant="neutral" disabled>Neutral</sl-button>
|
||||
<sl-button variant="warning" disabled>Warning</sl-button>
|
||||
<sl-button variant="danger" disabled>Danger</sl-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButton variant="default" disabled>
|
||||
Default
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="primary" disabled>
|
||||
Primary
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="success" disabled>
|
||||
Success
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="neutral" disabled>
|
||||
Neutral
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="warning" disabled>
|
||||
Warning
|
||||
</SlButton>
|
||||
|
||||
<SlButton variant="danger" disabled>
|
||||
Danger
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### 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 type attribute instead of a class (e.g. `sl-button[type="primary"]`).
|
||||
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. `sl-button[variant="primary"]`).
|
||||
|
||||
```html preview
|
||||
<sl-button class="pink">Pink Button</sl-button>
|
||||
@@ -192,7 +501,7 @@ This example demonstrates how to style buttons using a custom class. This is the
|
||||
/* Set design tokens for height and border width */
|
||||
--sl-input-height-medium: 48px;
|
||||
--sl-input-border-width: 4px;
|
||||
|
||||
|
||||
border-radius: 0;
|
||||
background-color: #ff1493;
|
||||
border-top-color: #ff7ac1;
|
||||
|
||||
@@ -2,22 +2,20 @@
|
||||
|
||||
[component-header:sl-card]
|
||||
|
||||
Cards can be used to group related subjects in a container.
|
||||
|
||||
```html preview
|
||||
<sl-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"
|
||||
<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>
|
||||
<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">
|
||||
<sl-button type="primary" pill>More Info</sl-button>
|
||||
<sl-button variant="primary" pill>More Info</sl-button>
|
||||
<sl-rating></sl-rating>
|
||||
</div>
|
||||
</sl-card>
|
||||
@@ -28,7 +26,27 @@ Cards can be used to group related subjects in a container.
|
||||
}
|
||||
|
||||
.card-overview small {
|
||||
color: var(--sl-color-gray-500);
|
||||
color: var(--sl-color-neutral-500);
|
||||
}
|
||||
|
||||
.card-overview [slot='footer'] {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlCard, SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.card-overview {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card-overview small {
|
||||
color: var(--sl-color-neutral-500);
|
||||
}
|
||||
|
||||
.card-overview [slot="footer"] {
|
||||
@@ -36,7 +54,32 @@ Cards can be used to group related subjects in a container.
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlCard 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">
|
||||
<SlButton variant="primary" pill>
|
||||
More Info
|
||||
</SlButton>
|
||||
<SlRating></SlRating>
|
||||
</div>
|
||||
</SlCard>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
@@ -57,6 +100,26 @@ Basic cards aren't very exciting, but they can display any content you want them
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlCard } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.card-basic {
|
||||
max-width: 300px;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlCard className="card-basic">
|
||||
This is just a basic card. No image, no header, and no footer. Just your content.
|
||||
</SlCard>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
## Card with Header
|
||||
|
||||
Headers can be used to display titles and more.
|
||||
@@ -65,34 +128,71 @@ Headers can be used to display titles and more.
|
||||
<sl-card class="card-header">
|
||||
<div slot="header">
|
||||
Header Title
|
||||
|
||||
<sl-icon-button name="gear"></sl-icon-button>
|
||||
<sl-icon-button name="gear" label="Settings"></sl-icon-button>
|
||||
</div>
|
||||
|
||||
This card has a header. You can put all sorts of things in it!
|
||||
</sl-card>
|
||||
|
||||
<style>
|
||||
.card-header {
|
||||
max-width: 300px;
|
||||
}
|
||||
.card-header {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card-header [slot="header"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.card-header [slot='header'] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-header h3 {
|
||||
margin: 0;
|
||||
}
|
||||
.card-header h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card-header sl-icon-button {
|
||||
font-size: var(--sl-font-size-medium);
|
||||
}
|
||||
.card-header sl-icon-button {
|
||||
font-size: var(--sl-font-size-medium);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlCard, SlIconButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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 sl-icon-button {
|
||||
font-size: var(--sl-font-size-medium);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlCard className="card-header">
|
||||
<div slot="header">
|
||||
Header Title
|
||||
<SlIconButton name="gear"></SlIconButton>
|
||||
</div>
|
||||
This card has a header. You can put all sorts of things in it!
|
||||
</SlCard>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
## Card with Footer
|
||||
|
||||
Footers can be used to display actions, summaries, or other relevant content.
|
||||
@@ -103,7 +203,7 @@ Footers can be used to display actions, summaries, or other relevant content.
|
||||
|
||||
<div slot="footer">
|
||||
<sl-rating></sl-rating>
|
||||
<sl-button slot="footer" type="primary">Preview</sl-button>
|
||||
<sl-button variant="primary">Preview</sl-button>
|
||||
</div>
|
||||
</sl-card>
|
||||
|
||||
@@ -112,12 +212,44 @@ Footers can be used to display actions, summaries, or other relevant content.
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card-footer [slot='footer'] {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlCard, SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.card-footer {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card-footer [slot="footer"] {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlCard className="card-footer">
|
||||
This card has a footer. You can put all sorts of things in it!
|
||||
<div slot="footer">
|
||||
<SlRating></SlRating>
|
||||
<SlButton slot="footer" variant="primary">
|
||||
Preview
|
||||
</SlButton>
|
||||
</div>
|
||||
</SlCard>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
## Images
|
||||
@@ -126,11 +258,11 @@ Cards accept an `image` slot. The image is displayed atop the card and stretches
|
||||
|
||||
```html preview
|
||||
<sl-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"
|
||||
<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.
|
||||
</sl-card>
|
||||
|
||||
@@ -141,4 +273,29 @@ Cards accept an `image` slot. The image is displayed atop the card and stretches
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlCard } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.card-image {
|
||||
max-width: 300px;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlCard 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.
|
||||
</SlCard>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-card]
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
|
||||
[component-header:sl-checkbox]
|
||||
|
||||
Checkboxes allow the user to toggle an option on or off.
|
||||
|
||||
```html preview
|
||||
<sl-checkbox>Checkbox</sl-checkbox>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form.md) instead.
|
||||
```jsx react
|
||||
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlCheckbox>Checkbox</SlCheckbox>;
|
||||
```
|
||||
|
||||
?> 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
|
||||
|
||||
@@ -20,6 +24,12 @@ Use the `checked` attribute to activate the checkbox.
|
||||
<sl-checkbox checked>Checked</sl-checkbox>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlCheckbox checked>Checked</SlCheckbox>;
|
||||
```
|
||||
|
||||
### Indeterminate
|
||||
|
||||
Use the `indeterminate` attribute to make the checkbox indeterminate.
|
||||
@@ -28,6 +38,12 @@ Use the `indeterminate` attribute to make the checkbox indeterminate.
|
||||
<sl-checkbox indeterminate>Indeterminate</sl-checkbox>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlCheckbox indeterminate>Indeterminate</SlCheckbox>;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable the checkbox.
|
||||
@@ -36,4 +52,104 @@ Use the `disabled` attribute to disable the checkbox.
|
||||
<sl-checkbox disabled>Disabled</sl-checkbox>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlCheckbox disabled>Disabled</SlCheckbox>;
|
||||
```
|
||||
|
||||
## Sizes
|
||||
|
||||
Use the `size` attribute to change a checkbox's size.
|
||||
|
||||
```html preview
|
||||
<sl-checkbox size="small">Small</sl-checkbox>
|
||||
<br />
|
||||
<sl-checkbox size="medium">Medium</sl-checkbox>
|
||||
<br />
|
||||
<sl-checkbox size="large">Large</sl-checkbox>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlCheckbox size="small">Small</SlCheckbox>
|
||||
<br />
|
||||
<SlCheckbox size="medium">Medium</SlCheckbox>
|
||||
<br />
|
||||
<SlCheckbox size="large">Large</SlCheckbox>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### 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">
|
||||
<sl-checkbox>Check me</sl-checkbox>
|
||||
<br />
|
||||
<sl-button type="submit" variant="primary" style="margin-top: 1rem;">Submit</sl-button>
|
||||
</form>
|
||||
<script>
|
||||
const form = document.querySelector('.custom-validity');
|
||||
const checkbox = form.querySelector('sl-checkbox');
|
||||
const errorMessage = `Don't forget to check me!`;
|
||||
|
||||
// Set initial validity as soon as the element is defined
|
||||
customElements.whenDefined('sl-checkbox').then(() => {
|
||||
checkbox.setCustomValidity(errorMessage);
|
||||
});
|
||||
|
||||
// Update validity on change
|
||||
checkbox.addEventListener('sl-change', () => {
|
||||
checkbox.setCustomValidity(checkbox.checked ? '' : errorMessage);
|
||||
});
|
||||
|
||||
// Handle submit
|
||||
form.addEventListener('submit', event => {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { SlButton, SlCheckbox } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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}>
|
||||
<SlCheckbox ref={checkbox} onSlChange={handleChange}>
|
||||
Check me
|
||||
</SlCheckbox>
|
||||
<br />
|
||||
<SlButton type="submit" variant="primary" style={{ marginTop: '1rem' }}>
|
||||
Submit
|
||||
</SlButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
[component-metadata:sl-checkbox]
|
||||
|
||||
@@ -2,32 +2,100 @@
|
||||
|
||||
[component-header:sl-color-picker]
|
||||
|
||||
Color pickers allow the user to select a color.
|
||||
|
||||
```html preview
|
||||
<sl-color-picker></sl-color-picker>
|
||||
<sl-color-picker label="Select a color"></sl-color-picker>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlColorPicker label="Select a color" />;
|
||||
```
|
||||
|
||||
?> 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
|
||||
|
||||
### Opacity
|
||||
### Initial Value
|
||||
|
||||
Use the `opacity` attribute to enable the opacity slider. When this is enabled, the value will be displayed as HEXA, RGBA, or HSLA based on `format`.
|
||||
Use the `value` attribute to set an initial value for the color picker.
|
||||
|
||||
```html preview
|
||||
<sl-color-picker opacity></sl-color-picker>
|
||||
<sl-color-picker value="#4a90e2" label="Select a color"></sl-color-picker>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlColorPicker 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
|
||||
<sl-color-picker value="#f5a623ff" opacity label="Select a color"></sl-color-picker>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlColorPicker opacity label="Select a color" />;
|
||||
```
|
||||
|
||||
### Formats
|
||||
|
||||
Set the color picker's format with the `format` attribute. Valid options include `hex`, `rgb`, and `hsl`. Note that the color picker's input will accept any parsable format (including CSS color names) regardless of this option.
|
||||
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
|
||||
<sl-color-picker format="hex" value="#4a90e2"></sl-color-picker>
|
||||
<sl-color-picker format="rgb" value="rgb(80, 227, 194)"></sl-color-picker>
|
||||
<sl-color-picker format="hsl" value="hsl(290, 87%, 47%)"></sl-color-picker>
|
||||
<sl-color-picker format="hex" value="#4a90e2" label="Select a color"></sl-color-picker>
|
||||
<sl-color-picker format="rgb" value="rgb(80, 227, 194)" label="Select a color"></sl-color-picker>
|
||||
<sl-color-picker format="hsl" value="hsl(290, 87%, 47%)" label="Select a color"></sl-color-picker>
|
||||
<sl-color-picker format="hsv" value="hsv(55, 89%, 97%)" label="Select a color"></sl-color-picker>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlColorPicker format="hex" value="#4a90e2" />
|
||||
<SlColorPicker format="rgb" value="rgb(80, 227, 194)" />
|
||||
<SlColorPicker format="hsl" value="hsl(290, 87%, 47%)" />
|
||||
<SlColorPicker 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
|
||||
<sl-color-picker
|
||||
label="Select a color"
|
||||
swatches="
|
||||
#d0021b; #f5a623; #f8e71c; #8b572a; #7ed321; #417505; #bd10e0; #9013fe;
|
||||
#4a90e2; #50e3c2; #b8e986; #000; #444; #888; #ccc; #fff;
|
||||
"
|
||||
></sl-color-picker>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlColorPicker
|
||||
label="Select a color"
|
||||
swatches="
|
||||
#d0021b; #f5a623; #f8e71c; #8b572a; #7ed321; #417505; #bd10e0; #9013fe;
|
||||
#4a90e2; #50e3c2; #b8e986; #000; #444; #888; #ccc; #fff;
|
||||
"
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizes
|
||||
@@ -35,18 +103,35 @@ To prevent users from toggling the format themselves, add the `no-format-toggle`
|
||||
Use the `size` attribute to change the color picker's trigger size.
|
||||
|
||||
```html preview
|
||||
<sl-color-picker size="small"></sl-color-picker>
|
||||
<sl-color-picker size="medium"></sl-color-picker>
|
||||
<sl-color-picker size="large"></sl-color-picker>
|
||||
<sl-color-picker size="small" label="Select a color"></sl-color-picker>
|
||||
<sl-color-picker size="medium" label="Select a color"></sl-color-picker>
|
||||
<sl-color-picker size="large" label="Select a color"></sl-color-picker>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlColorPicker size="small" label="Select a color" />
|
||||
<SlColorPicker size="medium" label="Select a color" />
|
||||
<SlColorPicker 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
|
||||
<sl-color-picker inline></sl-color-picker>
|
||||
<sl-color-picker inline label="Select a color"></sl-color-picker>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlColorPicker inline label="Select a color" />;
|
||||
```
|
||||
|
||||
[component-metadata:sl-color-picker]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!-- cspell:dictionaries lorem-ipsum -->
|
||||
|
||||
# Details
|
||||
|
||||
[component-header:sl-details]
|
||||
|
||||
Details show a brief summary and expand to show additional content.
|
||||
|
||||
```html preview
|
||||
<sl-details summary="Toggle Me">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
@@ -11,6 +11,17 @@ Details show a brief summary and expand to show additional content.
|
||||
</sl-details>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDetails } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlDetails 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.
|
||||
</SlDetails>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Disabled
|
||||
@@ -24,6 +35,63 @@ Use the `disable` attribute to prevent the details from expanding.
|
||||
</sl-details>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDetails } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlDetails 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.
|
||||
</SlDetails>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-details summary="Toggle Me" class="custom-icons">
|
||||
<sl-icon name="plus-square" slot="expand-icon"></sl-icon>
|
||||
<sl-icon name="dash-square" slot="collapse-icon"></sl-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.
|
||||
</sl-details>
|
||||
|
||||
<style>
|
||||
sl-details.custom-icons::part(summary-icon) {
|
||||
/* Disable the expand/collapse animation */
|
||||
rotate: none;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDetails, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
sl-details.custom-icon::part(summary-icon) {
|
||||
/* Disable the expand/collapse animation */
|
||||
rotate: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlDetails summary="Toggle Me" class="custom-icon">
|
||||
<SlIcon name="plus-square" slot="expand-icon" />
|
||||
<SlIcon name="dash-square" 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.
|
||||
</SlDetails>
|
||||
|
||||
<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 `sl-show` event.
|
||||
@@ -57,7 +125,7 @@ Details are designed to function independently, but you can simulate a group or
|
||||
|
||||
<style>
|
||||
.details-group-example sl-details:not(:last-of-type) {
|
||||
margin-bottom: var(--sl-spacing-xx-small);
|
||||
margin-bottom: var(--sl-spacing-2x-small);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<!-- cspell:dictionaries lorem-ipsum -->
|
||||
|
||||
# Dialog
|
||||
|
||||
[component-header:sl-dialog]
|
||||
|
||||
Dialogs, sometimes called "modals", appear above the page and require the user's immediate attention.
|
||||
|
||||
```html preview
|
||||
<sl-dialog label="Dialog" class="dialog-overview">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-dialog>
|
||||
|
||||
<sl-button>Open Dialog</sl-button>
|
||||
@@ -22,6 +22,28 @@ Dialogs, sometimes called "modals", appear above the page and require the user's
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDialog label="Dialog" open={open} onSlAfterHide={() => setOpen(false)}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDialog>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## UX Tips
|
||||
|
||||
- Use a dialog when you immediately require the user's attention, e.g. confirming a destructive action.
|
||||
@@ -37,7 +59,7 @@ Use the `--width` custom property to set the dialog's width.
|
||||
```html preview
|
||||
<sl-dialog label="Dialog" class="dialog-width" style="--width: 50vw;">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-dialog>
|
||||
|
||||
<sl-button>Open Dialog</sl-button>
|
||||
@@ -52,16 +74,38 @@ Use the `--width` custom property to set the dialog's width.
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDialog label="Dialog" open={open} style={{ '--width': '50vw' }} onSlAfterHide={() => setOpen(false)}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDialog>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-dialog label="Dialog" class="dialog-scrolling">
|
||||
<div style="height: 150vh; border: dashed 2px var(--sl-color-gray-200); padding: 0 1rem;">
|
||||
<div style="height: 150vh; border: dashed 2px var(--sl-color-neutral-200); padding: 0 1rem;">
|
||||
<p>Scroll down and give it a try! 👇</p>
|
||||
</div>
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-dialog>
|
||||
|
||||
<sl-button>Open Dialog</sl-button>
|
||||
@@ -76,38 +120,160 @@ By design, a dialog's height will never exceed that of the viewport. As such, di
|
||||
</script>
|
||||
```
|
||||
|
||||
### Ignoring Clicks on the Overlay
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
By default, dialogs are closed when the user clicks or taps on the overlay. To prevent this behavior, cancel the `sl-overlay-dismiss` event.
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDialog label="Dialog" open={open} onSlAfterHide={() => setOpen(false)}>
|
||||
<div
|
||||
style={{
|
||||
height: '150vh',
|
||||
border: 'dashed 2px var(--sl-color-neutral-200)',
|
||||
padding: '0 1rem'
|
||||
}}
|
||||
>
|
||||
<p>Scroll down and give it a try! 👇</p>
|
||||
</div>
|
||||
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDialog>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-dialog label="Dialog" class="dialog-no-overlay-dismiss">
|
||||
This dialog will not be closed when you click outside of it.
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-dialog label="Dialog" class="dialog-header-actions">
|
||||
<sl-icon-button class="new-window" slot="header-actions" name="box-arrow-up-right"></sl-icon-button>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-dialog>
|
||||
|
||||
<sl-button>Open Dialog</sl-button>
|
||||
|
||||
<script>
|
||||
const dialog = document.querySelector('.dialog-no-overlay-dismiss');
|
||||
const dialog = document.querySelector('.dialog-header-actions');
|
||||
const openButton = dialog.nextElementSibling;
|
||||
const closeButton = dialog.querySelector('sl-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 { SlButton, SlDialog, SlIconButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDialog label="Dialog" open={open} onSlAfterHide={() => setOpen(false)}>
|
||||
<SlIconButton
|
||||
class="new-window"
|
||||
slot="header-actions"
|
||||
name="box-arrow-up-right"
|
||||
onClick={() => window.open(location.href)}
|
||||
/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDialog>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Preventing the Dialog from Closing
|
||||
|
||||
By default, dialogs will close when the user clicks the close button, clicks the overlay, or presses the <kbd>Escape</kbd> 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 `sl-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 <kbd>Escape</kbd> to dismiss it.
|
||||
|
||||
```html preview
|
||||
<sl-dialog label="Dialog" class="dialog-deny-close">
|
||||
This dialog will not close when you click on the overlay.
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-dialog>
|
||||
|
||||
<sl-button>Open Dialog</sl-button>
|
||||
|
||||
<script>
|
||||
const dialog = document.querySelector('.dialog-deny-close');
|
||||
const openButton = dialog.nextElementSibling;
|
||||
const closeButton = dialog.querySelector('sl-button[slot="footer"]');
|
||||
|
||||
openButton.addEventListener('click', () => dialog.show());
|
||||
closeButton.addEventListener('click', () => dialog.hide());
|
||||
|
||||
dialog.addEventListener('sl-overlay-dismiss', event => event.preventDefault());
|
||||
// Prevent the dialog from closing when the user clicks on the overlay
|
||||
dialog.addEventListener('sl-request-close', event => {
|
||||
if (event.detail.source === 'overlay') {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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 (
|
||||
<>
|
||||
<SlDialog label="Dialog" open={open} onSlRequestClose={handleRequestClose} onSlAfterHide={() => setOpen(false)}>
|
||||
This dialog will not close when you click on the overlay.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDialog>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Customizing Initial Focus
|
||||
|
||||
By default, the dialog's panel will gain focus when opened. To set focus on a different element, listen for the `sl-initial-focus` event.
|
||||
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
|
||||
<sl-dialog label="Dialog" class="dialog-focus">
|
||||
<sl-input placeholder="I will have focus when the dialog is opened"></sl-input>
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-input autofocus placeholder="I will have focus when the dialog is opened"></sl-input>
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-dialog>
|
||||
|
||||
<sl-button>Open Dialog</sl-button>
|
||||
@@ -120,12 +286,31 @@ By default, the dialog's panel will gain focus when opened. To set focus on a di
|
||||
|
||||
openButton.addEventListener('click', () => dialog.show());
|
||||
closeButton.addEventListener('click', () => dialog.hide());
|
||||
|
||||
dialog.addEventListener('sl-initial-focus', event => {
|
||||
event.preventDefault();
|
||||
input.focus({ preventScroll: true });
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDialog, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDialog label="Dialog" open={open} onSlAfterHide={() => setOpen(false)}>
|
||||
<SlInput autofocus placeholder="I will have focus when the dialog is opened" />
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDialog>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
?> You can further customize initial focus behavior by canceling the `sl-initial-focus` event and setting focus yourself inside the event handler.
|
||||
|
||||
[component-metadata:sl-dialog]
|
||||
|
||||
135
docs/components/divider.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Divider
|
||||
|
||||
[component-header:sl-divider]
|
||||
|
||||
```html preview
|
||||
<sl-divider></sl-divider>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlDivider />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Width
|
||||
|
||||
Use the `--width` custom property to change the width of the divider.
|
||||
|
||||
```html preview
|
||||
<sl-divider style="--width: 4px;"></sl-divider>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlDivider style={{ '--width': '4px' }} />;
|
||||
```
|
||||
|
||||
### Color
|
||||
|
||||
Use the `--color` custom property to change the color of the divider.
|
||||
|
||||
```html preview
|
||||
<sl-divider style="--color: tomato;"></sl-divider>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlDivider style={{ '--color': 'tomato' }} />;
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-divider style="--spacing: 2rem;"></sl-divider>
|
||||
Below
|
||||
</div>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
Above
|
||||
<SlDivider style={{ '--spacing': '2rem' }} />
|
||||
Below
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-divider vertical></sl-divider>
|
||||
Middle
|
||||
<sl-divider vertical></sl-divider>
|
||||
Last
|
||||
</div>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
height: '2rem'
|
||||
}}
|
||||
>
|
||||
First
|
||||
<SlDivider vertical />
|
||||
Middle
|
||||
<SlDivider vertical />
|
||||
Last
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
### Menu Dividers
|
||||
|
||||
Use dividers in [menus](/components/menu) to visually group menu items.
|
||||
|
||||
```html preview
|
||||
<sl-menu style="max-width: 200px;">
|
||||
<sl-menu-item value="1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="3">Option 3</sl-menu-item>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item value="4">Option 4</sl-menu-item>
|
||||
<sl-menu-item value="5">Option 5</sl-menu-item>
|
||||
<sl-menu-item value="6">Option 6</sl-menu-item>
|
||||
</sl-menu>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlMenu style={{ maxWidth: '200px' }}>
|
||||
<SlMenuItem value="1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="3">Option 3</SlMenuItem>
|
||||
<sl-divider />
|
||||
<SlMenuItem value="4">Option 4</SlMenuItem>
|
||||
<SlMenuItem value="5">Option 5</SlMenuItem>
|
||||
<SlMenuItem value="6">Option 6</SlMenuItem>
|
||||
</SlMenu>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-divider]
|
||||
@@ -1,13 +1,13 @@
|
||||
<!-- cspell:dictionaries lorem-ipsum -->
|
||||
|
||||
# Drawer
|
||||
|
||||
[component-header:sl-drawer]
|
||||
|
||||
Drawers slide in from a container to expose additional options and information.
|
||||
|
||||
```html preview
|
||||
<sl-drawer label="Drawer" class="drawer-overview">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-drawer>
|
||||
|
||||
<sl-button>Open Drawer</sl-button>
|
||||
@@ -15,37 +15,81 @@ Drawers slide in from a container to expose additional options and information.
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-overview');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('sl-button[type="primary"]');
|
||||
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDrawer>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Slide in From Left
|
||||
### Slide in From Start
|
||||
|
||||
To make the drawer slide in from the left, set the `placement` attribute to `left`.
|
||||
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
|
||||
<sl-drawer label="Drawer" placement="left" class="drawer-placement-left">
|
||||
This drawer slides in from the left.
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-drawer label="Drawer" placement="start" class="drawer-placement-start">
|
||||
This drawer slides in from the start.
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-drawer>
|
||||
|
||||
<sl-button>Open Drawer</sl-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-placement-left');
|
||||
const drawer = document.querySelector('.drawer-placement-start');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('sl-button[type="primary"]');
|
||||
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDrawer label="Drawer" placement="start" open={open} onSlAfterHide={() => setOpen(false)}>
|
||||
This drawer slides in from the start.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDrawer>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Slide in From Top
|
||||
|
||||
To make the drawer slide in from the top, set the `placement` attribute to `top`.
|
||||
@@ -53,7 +97,7 @@ To make the drawer slide in from the top, set the `placement` attribute to `top`
|
||||
```html preview
|
||||
<sl-drawer label="Drawer" placement="top" class="drawer-placement-top">
|
||||
This drawer slides in from the top.
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-drawer>
|
||||
|
||||
<sl-button>Open Drawer</sl-button>
|
||||
@@ -61,13 +105,35 @@ To make the drawer slide in from the top, set the `placement` attribute to `top`
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-placement-top');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('sl-button[type="primary"]');
|
||||
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDrawer label="Drawer" placement="top" open={open} onSlAfterHide={() => setOpen(false)}>
|
||||
This drawer slides in from the top.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDrawer>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Slide in From Bottom
|
||||
|
||||
To make the drawer slide in from the bottom, set the `placement` attribute to `bottom`.
|
||||
@@ -75,7 +141,7 @@ To make the drawer slide in from the bottom, set the `placement` attribute to `b
|
||||
```html preview
|
||||
<sl-drawer label="Drawer" placement="bottom" class="drawer-placement-bottom">
|
||||
This drawer slides in from the bottom.
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-drawer>
|
||||
|
||||
<sl-button>Open Drawer</sl-button>
|
||||
@@ -83,16 +149,40 @@ To make the drawer slide in from the bottom, set the `placement` attribute to `b
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-placement-bottom');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('sl-button[type="primary"]');
|
||||
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDrawer label="Drawer" placement="bottom" open={open} onSlAfterHide={() => setOpen(false)}>
|
||||
This drawer slides in from the bottom.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDrawer>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Contained to an Element
|
||||
|
||||
By default, the drawer slides out of its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport. To make the drawer slide out of its parent element, add the `contained` prop and `position: relative` to the parent.
|
||||
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 <kbd>Escape</kbd>. This is intentional to allow users to interact with elements outside of the drawer.
|
||||
|
||||
```html preview
|
||||
<div
|
||||
@@ -102,22 +192,63 @@ By default, the drawer slides out of its [containing block](https://developer.mo
|
||||
|
||||
<sl-drawer label="Drawer" contained class="drawer-contained" style="--size: 50%;">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-drawer>
|
||||
</div>
|
||||
|
||||
<sl-button>Open Drawer</sl-button>
|
||||
<sl-button>Toggle Drawer</sl-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-contained');
|
||||
const openButton = drawer.parentElement.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('sl-button[type="primary"]');
|
||||
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
openButton.addEventListener('click', () => (drawer.open = !drawer.open));
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
border: 'solid 2px var(--sl-panel-border-color)',
|
||||
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.
|
||||
<SlDrawer
|
||||
label="Drawer"
|
||||
contained
|
||||
no-modal
|
||||
open={open}
|
||||
onSlAfterHide={() => setOpen(false)}
|
||||
style={{ '--size': '50%' }}
|
||||
>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDrawer>
|
||||
</div>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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`.
|
||||
@@ -125,7 +256,7 @@ Use the `--size` custom property to set the drawer's size. This will be applied
|
||||
```html preview
|
||||
<sl-drawer label="Drawer" class="drawer-custom-size" style="--size: 50vw;">
|
||||
This drawer is always 50% of the viewport.
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-drawer>
|
||||
|
||||
<sl-button>Open Drawer</sl-button>
|
||||
@@ -133,23 +264,45 @@ Use the `--size` custom property to set the drawer's size. This will be applied
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-custom-size');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('sl-button[type="primary"]');
|
||||
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)} style={{ '--size': '50vw' }}>
|
||||
This drawer is always 50% of the viewport.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDrawer>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-drawer label="Drawer" class="drawer-scrolling">
|
||||
<div style="height: 150vh; border: dashed 2px var(--sl-color-gray-200); padding: 0 1rem;">
|
||||
<div style="height: 150vh; border: dashed 2px var(--sl-color-neutral-200); padding: 0 1rem;">
|
||||
<p>Scroll down and give it a try! 👇</p>
|
||||
</div>
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-drawer>
|
||||
|
||||
<sl-button>Open Drawer</sl-button>
|
||||
@@ -157,45 +310,161 @@ By design, a drawer's height will never exceed 100% of its container. As such, d
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-scrolling');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('sl-button[type="primary"]');
|
||||
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
### Ignoring Clicks on the Overlay
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
By default, drawers are closed when the user clicks or taps on the overlay. To prevent this behavior, cancel the `sl-overlay-dismiss` event.
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)}>
|
||||
<div
|
||||
style={{
|
||||
height: '150vh',
|
||||
border: 'dashed 2px var(--sl-color-neutral-200)',
|
||||
padding: '0 1rem'
|
||||
}}
|
||||
>
|
||||
<p>Scroll down and give it a try! 👇</p>
|
||||
</div>
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDrawer>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-drawer label="Drawer" class="drawer-no-overlay-dismiss">
|
||||
This drawer will not be closed when you click outside of it.
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-drawer label="Drawer" class="drawer-header-actions">
|
||||
<sl-icon-button class="new-window" slot="header-actions" name="box-arrow-up-right"></sl-icon-button>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-drawer>
|
||||
|
||||
<sl-button>Open Drawer</sl-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-no-overlay-dismiss');
|
||||
const drawer = document.querySelector('.drawer-header-actions');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('sl-button[type="primary"]');
|
||||
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
|
||||
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 { SlButton, SlDrawer, SlIconButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)}>
|
||||
<SlIconButton slot="header-actions" name="box-arrow-up-right" onClick={() => window.open(location.href)} />
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDrawer>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Preventing the Drawer from Closing
|
||||
|
||||
By default, drawers will close when the user clicks the close button, clicks the overlay, or presses the <kbd>Escape</kbd> 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 `sl-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 <kbd>Escape</kbd> to dismiss it.
|
||||
|
||||
```html preview
|
||||
<sl-drawer label="Drawer" class="drawer-deny-close">
|
||||
This drawer will not close when you click on the overlay.
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-drawer>
|
||||
|
||||
<sl-button>Open Drawer</sl-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-deny-close');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
|
||||
drawer.addEventListener('sl-overlay-dismiss', event => event.preventDefault());
|
||||
// Prevent the drawer from closing when the user clicks on the overlay
|
||||
drawer.addEventListener('sl-request-close', event => {
|
||||
if (event.detail.source === 'overlay') {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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 (
|
||||
<>
|
||||
<SlDrawer label="Drawer" open={open} onSlRequestClose={handleRequestClose} onSlAfterHide={() => setOpen(false)}>
|
||||
This drawer will not close when you click on the overlay.
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Save & Close
|
||||
</SlButton>
|
||||
</SlDrawer>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Customizing Initial Focus
|
||||
|
||||
By default, the drawer's panel will gain focus when opened. To set focus on a different element, listen for the `sl-initial-focus` event.
|
||||
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
|
||||
<sl-drawer label="Drawer" class="drawer-focus">
|
||||
<sl-input placeholder="I will have focus when the drawer is opened"></sl-input>
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
<sl-input autofocus placeholder="I will have focus when the drawer is opened"></sl-input>
|
||||
<sl-button slot="footer" variant="primary">Close</sl-button>
|
||||
</sl-drawer>
|
||||
|
||||
<sl-button>Open Drawer</sl-button>
|
||||
@@ -204,16 +473,34 @@ By default, the drawer's panel will gain focus when opened. To set focus on a di
|
||||
const drawer = document.querySelector('.drawer-focus');
|
||||
const input = drawer.querySelector('sl-input');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('sl-button[type="primary"]');
|
||||
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
|
||||
|
||||
openButton.addEventListener('click', () => drawer.show());
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
|
||||
drawer.addEventListener('sl-initial-focus', event => {
|
||||
event.preventDefault();
|
||||
input.focus({ preventScroll: true });
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlDrawer, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)}>
|
||||
<SlInput autofocus placeholder="I will have focus when the drawer is opened" />
|
||||
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</SlButton>
|
||||
</SlDrawer>
|
||||
|
||||
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
?> You can further customize initial focus behavior by canceling the `sl-initial-focus` event and setting focus yourself inside the event handler.
|
||||
[component-metadata:sl-drawer]
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
[component-header:sl-dropdown]
|
||||
|
||||
Dropdowns expose additional content that "drops down" in a panel.
|
||||
|
||||
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.md) 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.md) and [select](/components/select.md)). The API gives you complete control over showing, hiding, and positioning the panel.
|
||||
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) and [select](/components/select)). The API gives you complete control over showing, hiding, and positioning the panel.
|
||||
|
||||
```html preview
|
||||
<sl-dropdown>
|
||||
@@ -15,10 +13,10 @@ Dropdowns are designed to work well with [menus](/components/menu.md) to provide
|
||||
<sl-menu-item>Dropdown Item 1</sl-menu-item>
|
||||
<sl-menu-item>Dropdown Item 2</sl-menu-item>
|
||||
<sl-menu-item>Dropdown Item 3</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-menu-item checked>Checked</sl-menu-item>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item type="checkbox" checked>Checkbox</sl-menu-item>
|
||||
<sl-menu-item disabled>Disabled</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item>
|
||||
Prefix
|
||||
<sl-icon slot="prefix" name="gift"></sl-icon>
|
||||
@@ -31,8 +29,147 @@ Dropdowns are designed to work well with [menus](/components/menu.md) to provide
|
||||
</sl-dropdown>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlDivider, SlDropdown, SlIcon, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlDropdown>
|
||||
<SlButton slot="trigger" caret>
|
||||
Dropdown
|
||||
</SlButton>
|
||||
<SlMenu>
|
||||
<SlMenuItem>Dropdown Item 1</SlMenuItem>
|
||||
<SlMenuItem>Dropdown Item 2</SlMenuItem>
|
||||
<SlMenuItem>Dropdown Item 3</SlMenuItem>
|
||||
<SlDivider />
|
||||
<SlMenuItem type="checkbox" checked>
|
||||
Checkbox
|
||||
</SlMenuItem>
|
||||
<SlMenuItem disabled>Disabled</SlMenuItem>
|
||||
<SlDivider />
|
||||
<SlMenuItem>
|
||||
Prefix
|
||||
<SlIcon slot="prefix" name="gift" />
|
||||
</SlMenuItem>
|
||||
<SlMenuItem>
|
||||
Suffix Icon
|
||||
<SlIcon slot="suffix" name="heart" />
|
||||
</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Getting the Selected Item
|
||||
|
||||
When dropdowns are used with [menus](/components/menu), you can listen for the `sl-select` 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">
|
||||
<sl-dropdown>
|
||||
<sl-button slot="trigger" caret>Edit</sl-button>
|
||||
<sl-menu>
|
||||
<sl-menu-item value="cut">Cut</sl-menu-item>
|
||||
<sl-menu-item value="copy">Copy</sl-menu-item>
|
||||
<sl-menu-item value="paste">Paste</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.dropdown-selection');
|
||||
const dropdown = container.querySelector('sl-dropdown');
|
||||
|
||||
dropdown.addEventListener('sl-select', event => {
|
||||
const selectedItem = event.detail.item;
|
||||
console.log(selectedItem.value);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
function handleSelect(event) {
|
||||
const selectedItem = event.detail.item;
|
||||
console.log(selectedItem.value);
|
||||
}
|
||||
|
||||
return (
|
||||
<SlDropdown>
|
||||
<SlButton slot="trigger" caret>
|
||||
Edit
|
||||
</SlButton>
|
||||
<SlMenu onSlSelect={handleSelect}>
|
||||
<SlMenuItem value="cut">Cut</SlMenuItem>
|
||||
<SlMenuItem value="copy">Copy</SlMenuItem>
|
||||
<SlMenuItem value="paste">Paste</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
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">
|
||||
<sl-dropdown>
|
||||
<sl-button slot="trigger" caret>Edit</sl-button>
|
||||
<sl-menu>
|
||||
<sl-menu-item value="cut">Cut</sl-menu-item>
|
||||
<sl-menu-item value="copy">Copy</sl-menu-item>
|
||||
<sl-menu-item value="paste">Paste</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.dropdown-selection-alt');
|
||||
const cut = container.querySelector('sl-menu-item[value="cut"]');
|
||||
const copy = container.querySelector('sl-menu-item[value="copy"]');
|
||||
const paste = container.querySelector('sl-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 { SlButton, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
function handleCut() {
|
||||
console.log('cut');
|
||||
}
|
||||
|
||||
function handleCopy() {
|
||||
console.log('copy');
|
||||
}
|
||||
|
||||
function handlePaste() {
|
||||
console.log('paste');
|
||||
}
|
||||
|
||||
return (
|
||||
<SlDropdown>
|
||||
<SlButton slot="trigger" caret>
|
||||
Edit
|
||||
</SlButton>
|
||||
<SlMenu>
|
||||
<SlMenuItem onClick={handleCut}>Cut</SlMenuItem>
|
||||
<SlMenuItem onClick={handleCopy}>Copy</SlMenuItem>
|
||||
<SlMenuItem onClick={handlePaste}>Paste</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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.
|
||||
@@ -44,13 +181,33 @@ The preferred placement of the dropdown can be set with the `placement` attribut
|
||||
<sl-menu-item>Cut</sl-menu-item>
|
||||
<sl-menu-item>Copy</sl-menu-item>
|
||||
<sl-menu-item>Paste</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item>Find</sl-menu-item>
|
||||
<sl-menu-item>Replace</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlDivider, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlDropdown placement="top-start">
|
||||
<SlButton slot="trigger" caret>
|
||||
Edit
|
||||
</SlButton>
|
||||
<SlMenu>
|
||||
<SlMenuItem>Cut</SlMenuItem>
|
||||
<SlMenuItem>Copy</SlMenuItem>
|
||||
<SlMenuItem>Paste</SlMenuItem>
|
||||
<SlDivider />
|
||||
<SlMenuItem>Find</SlMenuItem>
|
||||
<SlMenuItem>Replace</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
### Distance
|
||||
|
||||
The distance from the panel to the trigger can be customized using the `distance` attribute. This value is specified in pixels.
|
||||
@@ -62,13 +219,33 @@ The distance from the panel to the trigger can be customized using the `distance
|
||||
<sl-menu-item>Cut</sl-menu-item>
|
||||
<sl-menu-item>Copy</sl-menu-item>
|
||||
<sl-menu-item>Paste</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item>Find</sl-menu-item>
|
||||
<sl-menu-item>Replace</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlDivider, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlDropdown distance={30}>
|
||||
<SlButton slot="trigger" caret>
|
||||
Edit
|
||||
</SlButton>
|
||||
<SlMenu>
|
||||
<SlMenuItem>Cut</SlMenuItem>
|
||||
<SlMenuItem>Copy</SlMenuItem>
|
||||
<SlMenuItem>Paste</SlMenuItem>
|
||||
<SlDivider />
|
||||
<SlMenuItem>Find</SlMenuItem>
|
||||
<SlMenuItem>Replace</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
### Skidding
|
||||
|
||||
The offset of the panel along the trigger can be customized using the `skidding` attribute. This value is specified in pixels.
|
||||
@@ -80,13 +257,33 @@ The offset of the panel along the trigger can be customized using the `skidding`
|
||||
<sl-menu-item>Cut</sl-menu-item>
|
||||
<sl-menu-item>Copy</sl-menu-item>
|
||||
<sl-menu-item>Paste</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item>Find</sl-menu-item>
|
||||
<sl-menu-item>Replace</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlDivider, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlDropdown skidding={30}>
|
||||
<SlButton slot="trigger" caret>
|
||||
Edit
|
||||
</SlButton>
|
||||
<SlMenu>
|
||||
<SlMenuItem>Cut</SlMenuItem>
|
||||
<SlMenuItem>Copy</SlMenuItem>
|
||||
<SlMenuItem>Paste</SlMenuItem>
|
||||
<SlDivider />
|
||||
<SlMenuItem>Find</SlMenuItem>
|
||||
<SlMenuItem>Replace</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
### 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, 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.
|
||||
@@ -114,6 +311,7 @@ Dropdown panels will be clipped if they're inside a container that has `overflow
|
||||
|
||||
<style>
|
||||
.dropdown-hoist {
|
||||
position: relative;
|
||||
border: solid 2px var(--sl-panel-border-color);
|
||||
padding: var(--sl-spacing-medium);
|
||||
overflow: hidden;
|
||||
@@ -121,57 +319,46 @@ Dropdown panels will be clipped if they're inside a container that has `overflow
|
||||
</style>
|
||||
```
|
||||
|
||||
### Getting the Selected Item
|
||||
```jsx react
|
||||
import { SlButton, SlDivider, SlDropdown, SlIcon, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
When dropdowns are used with [menus](/components/menu.md), you can listen for the `sl-select` 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.
|
||||
const css = `
|
||||
.dropdown-hoist {
|
||||
border: solid 2px var(--sl-panel-border-color);
|
||||
padding: var(--sl-spacing-medium);
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
|
||||
```html preview
|
||||
<div class="dropdown-selection">
|
||||
<sl-dropdown>
|
||||
<sl-button slot="trigger" caret>Edit</sl-button>
|
||||
<sl-menu>
|
||||
<sl-menu-item value="cut">Cut</sl-menu-item>
|
||||
<sl-menu-item value="copy">Copy</sl-menu-item>
|
||||
<sl-menu-item value="paste">Paste</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
</div>
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="dropdown-hoist">
|
||||
<SlDropdown>
|
||||
<SlButton slot="trigger" caret>
|
||||
No Hoist
|
||||
</SlButton>
|
||||
<SlMenu>
|
||||
<SlMenuItem>Item 1</SlMenuItem>
|
||||
<SlMenuItem>Item 2</SlMenuItem>
|
||||
<SlMenuItem>Item 3</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.dropdown-selection');
|
||||
const dropdown = container.querySelector('sl-dropdown');
|
||||
<SlDropdown hoist>
|
||||
<SlButton slot="trigger" caret>
|
||||
Hoist
|
||||
</SlButton>
|
||||
<SlMenu>
|
||||
<SlMenuItem>Item 1</SlMenuItem>
|
||||
<SlMenuItem>Item 2</SlMenuItem>
|
||||
<SlMenuItem>Item 3</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
</div>
|
||||
|
||||
dropdown.addEventListener('sl-select', event => {
|
||||
const selectedItem = event.detail.item;
|
||||
console.log(selectedItem.value);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
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">
|
||||
<sl-dropdown>
|
||||
<sl-button slot="trigger" caret>Edit</sl-button>
|
||||
<sl-menu>
|
||||
<sl-menu-item value="cut">Cut</sl-menu-item>
|
||||
<sl-menu-item value="copy">Copy</sl-menu-item>
|
||||
<sl-menu-item value="paste">Paste</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.dropdown-selection-alt');
|
||||
const cut = container.querySelector('sl-menu-item[value="cut"]');
|
||||
const copy = container.querySelector('sl-menu-item[value="copy"]');
|
||||
const paste = container.querySelector('sl-menu-item[value="paste"]');
|
||||
|
||||
cut.addEventListener('click', () => console.log('cut'));
|
||||
copy.addEventListener('click', () => console.log('copy'));
|
||||
paste.addEventListener('click', () => console.log('paste'));
|
||||
</script>
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-dropdown]
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
# Form
|
||||
|
||||
[component-header:sl-form]
|
||||
|
||||
Forms collect data that can easily be processed and sent to a server.
|
||||
|
||||
All Shoelace components make use of a [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM) to encapsulate markup, styles, and behavior. One caveat of this approach is that native `<form>` elements will not recognize Shoelace form controls.
|
||||
|
||||
This component solves that problem by serializing _both_ Shoelace form controls and native form controls when the form is submitted. The resulting form data is exposed in the `sl-submit` event as a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object in `event.detail.formData`. You can also find an array of form controls in `event.detail.formControls`.
|
||||
|
||||
Shoelace forms don't make use of `action` and `method` attributes and they don't submit the same was as native forms. To handle submission, you need to listen for the `sl-submit` event as shown in the example below and make an XHR request with the resulting form data.
|
||||
|
||||
```html preview
|
||||
<sl-form class="form-overview">
|
||||
<sl-input name="name" type="text" label="Name"></sl-input>
|
||||
<br>
|
||||
<sl-select name="favorite" label="Select your favorite">
|
||||
<sl-menu-item value="birds">Birds</sl-menu-item>
|
||||
<sl-menu-item value="cats">Cats</sl-menu-item>
|
||||
<sl-menu-item value="dogs">Dogs</sl-menu-item>
|
||||
</sl-select>
|
||||
<br>
|
||||
<sl-checkbox name="agree" value="yes">
|
||||
I totally agree
|
||||
</sl-checkbox>
|
||||
<br><br>
|
||||
<sl-button submit>Submit</sl-button>
|
||||
</sl-form>
|
||||
|
||||
<script>
|
||||
const form = document.querySelector('.form-overview');
|
||||
|
||||
// Watch for the slSubmit event
|
||||
form.addEventListener('sl-submit', event => {
|
||||
const formData = event.detail.formData;
|
||||
let output = '';
|
||||
|
||||
//
|
||||
// Example 1: Post data to a server and wait for a JSON response
|
||||
//
|
||||
fetch('https://jsonplaceholder.typicode.com/posts', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(result => {
|
||||
console.log('Success:', result);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
|
||||
//
|
||||
// Example 2: Output all form control names + values
|
||||
//
|
||||
for (const entry of formData.entries()) {
|
||||
output += `${entry[0]}: ${entry[1]}\n`;
|
||||
}
|
||||
alert(output);
|
||||
|
||||
//
|
||||
// Example 3: Get all form controls that were serialized as
|
||||
// an array of HTML elements
|
||||
//
|
||||
console.log(event.detail.formControls);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
## Form Control Validation
|
||||
|
||||
Client-side validation can be enabled through the browser's [Constraint Validation API](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation) for many form controls. You can enable it using props such as `required`, `pattern`, `minlength`, and `maxlength`. As the user interacts with the form control, the `invalid` attribute will reflect its validity based on its current value and the constraints that have been defined.
|
||||
|
||||
When a form control is invalid, the containing form will not be submitted. Instead, the browser will show the user a relevant error message. If you don't want to use client-side validation, you can suppress this behavior by adding `novalidate` to the `<sl-form>` element.
|
||||
|
||||
All form controls support validation, but not all validation props are available for every component. Refer to a component's documentation to see which validation props it supports.
|
||||
|
||||
Note that validity is not checked until the user interacts with the control or its containing form is submitted. This prevents required controls from being rendered as invalid right away, which can result in a poor user experience. If you need this behavior, set the `invalid` attribute initially.
|
||||
|
||||
!> Client-side validation can be used to improve the UX of forms, but it is not a replacement for server-side validation. **You should always validate and sanitize user input on the server!**
|
||||
|
||||
### Required Fields
|
||||
|
||||
To make a field required, use the `required` prop. The form will not be submitted if a required form control is empty.
|
||||
|
||||
```html preview
|
||||
<sl-form class="input-validation-required">
|
||||
<sl-input name="name" label="Name" required></sl-input>
|
||||
<br>
|
||||
<sl-select label="Favorite Animal" clearable required>
|
||||
<sl-menu-item value="birds">Birds</sl-menu-item>
|
||||
<sl-menu-item value="cats">Cats</sl-menu-item>
|
||||
<sl-menu-item value="dogs">Dogs</sl-menu-item>
|
||||
<sl-menu-item value="other">Other</sl-menu-item>
|
||||
</sl-select>
|
||||
<br>
|
||||
<sl-textarea name="comment" label="Comment" required></sl-textarea>
|
||||
<br>
|
||||
<sl-checkbox required>Check me before submitting</sl-checkbox>
|
||||
<br><br>
|
||||
<sl-button type="primary" submit>Submit</sl-button>
|
||||
</sl-form>
|
||||
|
||||
<script>
|
||||
const form = document.querySelector('.input-validation-required');
|
||||
form.addEventListener('sl-submit', () => alert('All fields are valid!'));
|
||||
</script>
|
||||
```
|
||||
|
||||
### Input Patterns
|
||||
|
||||
To restrict a value to a specific [pattern](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern), use the `pattern` attribute. This example only allows the letters A-Z, so the form will not submit if a number or symbol is entered. This only works with `<sl-input>` elements.
|
||||
|
||||
```html preview
|
||||
<sl-form class="input-validation-pattern">
|
||||
<sl-input name="letters" required label="Letters" pattern="[A-Za-z]+"></sl-input>
|
||||
<br>
|
||||
<sl-button type="primary" submit>Submit</sl-button>
|
||||
</sl-form>
|
||||
|
||||
<script>
|
||||
const form = document.querySelector('.input-validation-pattern');
|
||||
form.addEventListener('sl-submit', () => alert('All fields are valid!'));
|
||||
</script>
|
||||
```
|
||||
|
||||
### Input Types
|
||||
|
||||
Some input types will automatically trigger constraints, such as `email` and `url`.
|
||||
|
||||
```html preview
|
||||
<sl-form class="input-validation-type">
|
||||
<sl-input type="email" label="Email" placeholder="you@example.com" required></sl-input>
|
||||
<br>
|
||||
<sl-input type="url" label="URL" placeholder="https://example.com/" required></sl-input>
|
||||
<br>
|
||||
<sl-button type="primary" submit>Submit</sl-button>
|
||||
</sl-form>
|
||||
|
||||
<script>
|
||||
const form = document.querySelector('.input-validation-type');
|
||||
form.addEventListener('sl-submit', () => alert('All fields are valid!'));
|
||||
</script>
|
||||
```
|
||||
|
||||
### Custom Validation
|
||||
|
||||
To create a custom validation error, use the `setCustomValidity` method. The form will not be submitted when this method is called with anything other than an empty string, and its message will be shown by the browser as the validation error. To make the input valid again, call the method a second time with an empty string as the argument.
|
||||
|
||||
```html preview
|
||||
<sl-form class="input-validation-custom">
|
||||
<sl-input label="Type 'shoelace'" required></sl-input>
|
||||
<br>
|
||||
<sl-button type="primary" submit>Submit</sl-button>
|
||||
</sl-form>
|
||||
|
||||
<script>
|
||||
const form = document.querySelector('.input-validation-custom');
|
||||
const input = form.querySelector('sl-input');
|
||||
|
||||
form.addEventListener('sl-submit', () => alert('All fields are valid!'));
|
||||
input.addEventListener('sl-input', () => {
|
||||
if (input.value === 'shoelace') {
|
||||
input.setCustomValidity('');
|
||||
} else {
|
||||
input.setCustomValidity('Hey, you\'re supposed to type \'shoelace\' before submitting this!');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### Custom Validation Styles
|
||||
|
||||
The `invalid` attribute reflects the form control's validity, so you can style invalid fields using the `[invalid]` selector. The example below demonstrates how you can give erroneous fields a different appearance. Type something other than "shoelace" to demonstrate this.
|
||||
|
||||
```html preview
|
||||
<sl-input class="custom-input" required pattern="shoelace">
|
||||
<small slot="help-text">Please enter "shoelace" to continue</small>
|
||||
</sl-input>
|
||||
|
||||
<style>
|
||||
.custom-input[invalid]:not([disabled])::part(label),
|
||||
.custom-input[invalid]:not([disabled])::part(help-text) {
|
||||
color: var(--sl-color-danger-600);
|
||||
}
|
||||
|
||||
.custom-input[invalid]:not([disabled])::part(base) {
|
||||
border-color: var(--sl-color-danger-500);
|
||||
}
|
||||
|
||||
.custom-input[invalid] {
|
||||
--focus-ring: 0 0 0 var(--sl-focus-ring-width) var(--sl-focus-ring-color-danger);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### Third-party Validation
|
||||
|
||||
To opt out of the browser's built-in validation and use your own, add the `novalidate` attribute to the form. This will ignore all constraints and prevent the browser from showing its own warnings when form controls are invalid.
|
||||
|
||||
Remember that the `invalid` prop on form controls reflects validity as defined by the [Constraint Validation API](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation). You can set it initially, but the `invalid` prop will update as the user interacts with the form control. As such, you should not rely on it to set invalid styles using a custom validation library.
|
||||
|
||||
Instead, toggle a class and target it in your stylesheet as shown below.
|
||||
|
||||
```html
|
||||
<sl-form novalidate>
|
||||
<sl-input class="invalid"></sl-input>
|
||||
</sl-form>
|
||||
|
||||
<style>
|
||||
sl-input.invalid {
|
||||
...
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
[component-metadata:sl-form]
|
||||
@@ -2,13 +2,10 @@
|
||||
|
||||
[component-header:sl-format-bytes]
|
||||
|
||||
Formats a number as a human readable bytes value.
|
||||
|
||||
```html preview
|
||||
<div class="format-bytes-overview">
|
||||
The file is <sl-format-bytes value="1000"></sl-format-bytes> in size.
|
||||
<br><br>
|
||||
<sl-input type="number" value="1000" label="Number to Format" style="max-width: 180px;"></sl-input>
|
||||
The file is <sl-format-bytes value="1000"></sl-format-bytes> in size. <br /><br />
|
||||
<sl-input type="number" value="1000" label="Number to Format" style="max-width: 180px;"></sl-input>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -16,10 +13,34 @@ Formats a number as a human readable bytes value.
|
||||
const formatter = container.querySelector('sl-format-bytes');
|
||||
const input = container.querySelector('sl-input');
|
||||
|
||||
input.addEventListener('sl-input', () => formatter.value = input.value || 0);
|
||||
input.addEventListener('sl-input', () => (formatter.value = input.value || 0));
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlFormatBytes, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [value, setValue] = useState(1000);
|
||||
|
||||
return (
|
||||
<>
|
||||
The file is <SlFormatBytes value={value} /> in size.
|
||||
<br />
|
||||
<br />
|
||||
<SlInput
|
||||
type="number"
|
||||
value={value}
|
||||
label="Number to Format"
|
||||
style={{ maxWidth: '180px' }}
|
||||
onSlInput={event => setValue(event.target.value)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Formatting Bytes
|
||||
@@ -27,32 +48,80 @@ Formats a number as a human readable bytes value.
|
||||
Set the `value` attribute to a number to get the value in bytes.
|
||||
|
||||
```html preview
|
||||
<sl-format-bytes value="12"></sl-format-bytes><br>
|
||||
<sl-format-bytes value="1200"></sl-format-bytes><br>
|
||||
<sl-format-bytes value="1200000"></sl-format-bytes><br>
|
||||
<sl-format-bytes value="12"></sl-format-bytes><br />
|
||||
<sl-format-bytes value="1200"></sl-format-bytes><br />
|
||||
<sl-format-bytes value="1200000"></sl-format-bytes><br />
|
||||
<sl-format-bytes value="1200000000"></sl-format-bytes>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlFormatBytes } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlFormatBytes value="12" />
|
||||
<br />
|
||||
<SlFormatBytes value="1200" />
|
||||
<br />
|
||||
<SlFormatBytes value="1200000" />
|
||||
<br />
|
||||
<SlFormatBytes value="1200000000" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Formatting Bits
|
||||
|
||||
To get the value in bits, set the `unit` attribute to `bits`.
|
||||
To get the value in bits, set the `unit` attribute to `bit`.
|
||||
|
||||
```html preview
|
||||
<sl-format-bytes value="12" unit="bits"></sl-format-bytes><br>
|
||||
<sl-format-bytes value="1200" unit="bits"></sl-format-bytes><br>
|
||||
<sl-format-bytes value="1200000" unit="bits"></sl-format-bytes><br>
|
||||
<sl-format-bytes value="1200000000" unit="bits"></sl-format-bytes>
|
||||
<sl-format-bytes value="12" unit="bit"></sl-format-bytes><br />
|
||||
<sl-format-bytes value="1200" unit="bit"></sl-format-bytes><br />
|
||||
<sl-format-bytes value="1200000" unit="bit"></sl-format-bytes><br />
|
||||
<sl-format-bytes value="1200000000" unit="bit"></sl-format-bytes>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlFormatBytes } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlFormatBytes value="12" unit="bit" />
|
||||
<br />
|
||||
<SlFormatBytes value="1200" unit="bit" />
|
||||
<br />
|
||||
<SlFormatBytes value="1200000" unit="bit" />
|
||||
<br />
|
||||
<SlFormatBytes value="1200000000" unit="bit" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Localization
|
||||
|
||||
Use the `locale` attribute to set the number formatting locale.
|
||||
Use the `lang` attribute to set the number formatting locale.
|
||||
|
||||
```html preview
|
||||
<sl-format-bytes value="12" locale="de"></sl-format-bytes><br>
|
||||
<sl-format-bytes value="1200" locale="de"></sl-format-bytes><br>
|
||||
<sl-format-bytes value="1200000" locale="de"></sl-format-bytes><br>
|
||||
<sl-format-bytes value="1200000000" locale="de"></sl-format-bytes>
|
||||
<sl-format-bytes value="12" lang="de"></sl-format-bytes><br />
|
||||
<sl-format-bytes value="1200" lang="de"></sl-format-bytes><br />
|
||||
<sl-format-bytes value="1200000" lang="de"></sl-format-bytes><br />
|
||||
<sl-format-bytes value="1200000000" lang="de"></sl-format-bytes>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlFormatBytes } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlFormatBytes value="12" lang="de" />
|
||||
<br />
|
||||
<SlFormatBytes value="1200" lang="de" />
|
||||
<br />
|
||||
<SlFormatBytes value="1200000" lang="de" />
|
||||
<br />
|
||||
<SlFormatBytes value="1200000000" lang="de" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-format-bytes]
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
[component-header:sl-format-date]
|
||||
|
||||
Formats a date/time using the specified locale and options.
|
||||
|
||||
Localization is handled by the browser's [`Intl.DateTimeFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat). No language packs are required.
|
||||
|
||||
```html preview
|
||||
@@ -11,11 +9,16 @@ Localization is handled by the browser's [`Intl.DateTimeFormat` API](https://dev
|
||||
<sl-format-date date="2020-07-15T09:17:00-04:00"></sl-format-date>
|
||||
```
|
||||
|
||||
The `date` prop determines the date/time to use when formatting. It must be a string that [`Date.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) can interpret or a [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object set via JavaScript. If omitted, the current date/time will be assumed.
|
||||
```jsx react
|
||||
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlFormatDate date="2020-07-15T09:17:00-04:00" />;
|
||||
```
|
||||
|
||||
The `date` attribute determines the date/time to use when formatting. It must be a string that [`Date.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) can interpret or a [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object set via JavaScript. If omitted, the current date/time will be assumed.
|
||||
|
||||
?> When using strings, avoid ambiguous dates such as `03/04/2020` which can be interpreted as March 4 or April 3 depending on the user's browser and locale. Instead, always use a valid [ISO 8601 date time string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse#Date_Time_String_Format) to ensure the date will be parsed properly by all clients.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Date & Time Formatting
|
||||
@@ -24,41 +27,98 @@ Formatting options are based on those found in the [`Intl.DateTimeFormat` API](h
|
||||
|
||||
```html preview
|
||||
<!-- Human-readable date -->
|
||||
<sl-format-date month="long" day="numeric" year="numeric"></sl-format-date><br>
|
||||
<sl-format-date month="long" day="numeric" year="numeric"></sl-format-date><br />
|
||||
|
||||
<!-- Time -->
|
||||
<sl-format-date hour="numeric" minute="numeric"></sl-format-date><br>
|
||||
<sl-format-date hour="numeric" minute="numeric"></sl-format-date><br />
|
||||
|
||||
<!-- Weekday -->
|
||||
<sl-format-date weekday="long"></sl-format-date><br>
|
||||
<sl-format-date weekday="long"></sl-format-date><br />
|
||||
|
||||
<!-- Month -->
|
||||
<sl-format-date month="long"></sl-format-date><br>
|
||||
<sl-format-date month="long"></sl-format-date><br />
|
||||
|
||||
<!-- Year -->
|
||||
<sl-format-date year="numeric"></sl-format-date><br>
|
||||
<sl-format-date year="numeric"></sl-format-date><br />
|
||||
|
||||
<!-- No formatting options -->
|
||||
<sl-format-date></sl-format-date>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
{/* Human-readable date */}
|
||||
<SlFormatDate month="long" day="numeric" year="numeric" />
|
||||
<br />
|
||||
|
||||
{/* Time */}
|
||||
<SlFormatDate hour="numeric" minute="numeric" />
|
||||
<br />
|
||||
|
||||
{/* Weekday */}
|
||||
<SlFormatDate weekday="long" />
|
||||
<br />
|
||||
|
||||
{/* Month */}
|
||||
<SlFormatDate month="long" />
|
||||
<br />
|
||||
|
||||
{/* Year */}
|
||||
<SlFormatDate year="numeric" />
|
||||
<br />
|
||||
|
||||
{/* No formatting options */}
|
||||
<SlFormatDate />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Hour Formatting
|
||||
|
||||
By default, the browser will determine whether to use 12-hour or 24-hour time. To force one or the other, set the `hourFormat` prop to `12` or `24`.
|
||||
By default, the browser will determine whether to use 12-hour or 24-hour time. To force one or the other, set the `hour-format` attribute to `12` or `24`.
|
||||
|
||||
```html preview
|
||||
<sl-format-date hour="numeric" minute="numeric" hour-format="12"></sl-format-date><br>
|
||||
<sl-format-date hour="numeric" minute="numeric" hour-format="12"></sl-format-date><br />
|
||||
<sl-format-date hour="numeric" minute="numeric" hour-format="24"></sl-format-date>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlFormatDate hour="numeric" minute="numeric" hour-format="12" />
|
||||
<br />
|
||||
<SlFormatDate hour="numeric" minute="numeric" hour-format="24" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Localization
|
||||
|
||||
Use the `locale` attribute to set the date/time formatting locale.
|
||||
Use the `lang` attribute to set the date/time formatting locale.
|
||||
|
||||
```html preview
|
||||
English: <sl-format-date locale="en"></sl-format-date><br>
|
||||
French: <sl-format-date locale="fr"></sl-format-date><br>
|
||||
Russian: <sl-format-date locale="ru"></sl-format-date><br>
|
||||
English: <sl-format-date lang="en"></sl-format-date><br />
|
||||
French: <sl-format-date lang="fr"></sl-format-date><br />
|
||||
Russian: <sl-format-date lang="ru"></sl-format-date>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
English: <SlFormatDate lang="en" />
|
||||
<br />
|
||||
French: <SlFormatDate lang="fr" />
|
||||
<br />
|
||||
Russian: <SlFormatDate lang="ru" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-format-date]
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
|
||||
[component-header:sl-format-number]
|
||||
|
||||
Formats a number using the specified locale and options.
|
||||
|
||||
Localization is handled by the browser's [`Intl.NumberFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat). No language packs are required.
|
||||
|
||||
```html preview
|
||||
<div class="format-number-overview">
|
||||
<sl-format-number value="1000"></sl-format-number>
|
||||
<br><br>
|
||||
<sl-input type="number" value="1000" label="Number to Format" style="max-width: 180px;"></sl-input>
|
||||
<sl-format-number value="1000"></sl-format-number>
|
||||
<br /><br />
|
||||
<sl-input type="number" value="1000" label="Number to Format" style="max-width: 180px;"></sl-input>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -18,10 +16,34 @@ Localization is handled by the browser's [`Intl.NumberFormat` API](https://devel
|
||||
const formatter = container.querySelector('sl-format-number');
|
||||
const input = container.querySelector('sl-input');
|
||||
|
||||
input.addEventListener('sl-input', () => formatter.value = input.value || 0);
|
||||
input.addEventListener('sl-input', () => (formatter.value = input.value || 0));
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlFormatNumber, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [value, setValue] = useState(1000);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlFormatNumber value={value} />
|
||||
<br />
|
||||
<br />
|
||||
<SlInput
|
||||
type="number"
|
||||
value={value}
|
||||
label="Number to Format"
|
||||
style={{ maxWidth: '180px' }}
|
||||
onSlInput={event => setValue(event.target.value)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Percentages
|
||||
@@ -29,33 +51,83 @@ Localization is handled by the browser's [`Intl.NumberFormat` API](https://devel
|
||||
To get the value as a percent, set the `type` attribute to `percent`.
|
||||
|
||||
```html preview
|
||||
<sl-format-number type="percent" value="0"></sl-format-number><br>
|
||||
<sl-format-number type="percent" value=".25"></sl-format-number><br>
|
||||
<sl-format-number type="percent" value=".50"></sl-format-number><br>
|
||||
<sl-format-number type="percent" value=".75"></sl-format-number><br>
|
||||
<sl-format-number type="percent" value="0"></sl-format-number><br />
|
||||
<sl-format-number type="percent" value="0.25"></sl-format-number><br />
|
||||
<sl-format-number type="percent" value="0.50"></sl-format-number><br />
|
||||
<sl-format-number type="percent" value="0.75"></sl-format-number><br />
|
||||
<sl-format-number type="percent" value="1"></sl-format-number>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlFormatNumber } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlFormatNumber type="percent" value={0} />
|
||||
<br />
|
||||
<SlFormatNumber type="percent" value={0.25} />
|
||||
<br />
|
||||
<SlFormatNumber type="percent" value={0.5} />
|
||||
<br />
|
||||
<SlFormatNumber type="percent" value={0.75} />
|
||||
<br />
|
||||
<SlFormatNumber type="percent" value={1} />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Localization
|
||||
|
||||
Use the `locale` attribute to set the number formatting locale.
|
||||
Use the `lang` attribute to set the number formatting locale.
|
||||
|
||||
```html preview
|
||||
English: <sl-format-number value="2000" locale="en" minimum-fraction-digits="2"></sl-format-number><br>
|
||||
German: <sl-format-number value="2000" locale="de" minimum-fraction-digits="2"></sl-format-number><br>
|
||||
Russian: <sl-format-number value="2000" locale="ru" minimum-fraction-digits="2"></sl-format-number>
|
||||
English: <sl-format-number value="2000" lang="en" minimum-fraction-digits="2"></sl-format-number><br />
|
||||
German: <sl-format-number value="2000" lang="de" minimum-fraction-digits="2"></sl-format-number><br />
|
||||
Russian: <sl-format-number value="2000" lang="ru" minimum-fraction-digits="2"></sl-format-number>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlFormatNumber } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
English: <SlFormatNumber value="2000" lang="en" minimum-fraction-digits="2" />
|
||||
<br />
|
||||
German: <SlFormatNumber value="2000" lang="de" minimum-fraction-digits="2" />
|
||||
<br />
|
||||
Russian: <SlFormatNumber value="2000" lang="ru" minimum-fraction-digits="2" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Currency
|
||||
|
||||
To format a number as a monetary value, set the `type` attribute to `currency` and set the `currency` attribute to the desired ISO 4217 currency code. You should also specify `locale` to ensure the the number is formatted correctly for the target locale.
|
||||
To format a number as a monetary value, set the `type` attribute to `currency` and set the `currency` attribute to the desired ISO 4217 currency code. You should also specify `lang` to ensure the the number is formatted correctly for the target locale.
|
||||
|
||||
```html preview
|
||||
<sl-format-number type="currency" currency="USD" value="2000" locale="en-US"></sl-format-number><br>
|
||||
<sl-format-number type="currency" currency="GBP" value="2000" locale="en-GB"></sl-format-number><br>
|
||||
<sl-format-number type="currency" currency="EUR" value="2000" locale="de"></sl-format-number><br>
|
||||
<sl-format-number type="currency" currency="RUB" value="2000" locale="ru"></sl-format-number><br>
|
||||
<sl-format-number type="currency" currency="CNY" value="2000" locale="zh-cn"></sl-format-number>
|
||||
<sl-format-number type="currency" currency="USD" value="2000" lang="en-US"></sl-format-number><br />
|
||||
<sl-format-number type="currency" currency="GBP" value="2000" lang="en-GB"></sl-format-number><br />
|
||||
<sl-format-number type="currency" currency="EUR" value="2000" lang="de"></sl-format-number><br />
|
||||
<sl-format-number type="currency" currency="RUB" value="2000" lang="ru"></sl-format-number><br />
|
||||
<sl-format-number type="currency" currency="CNY" value="2000" lang="zh-cn"></sl-format-number>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlFormatNumber } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlFormatNumber type="currency" currency="USD" value="2000" lang="en-US" />
|
||||
<br />
|
||||
<SlFormatNumber type="currency" currency="GBP" value="2000" lang="en-GB" />
|
||||
<br />
|
||||
<SlFormatNumber type="currency" currency="EUR" value="2000" lang="de" />
|
||||
<br />
|
||||
<SlFormatNumber type="currency" currency="RUB" value="2000" lang="ru" />
|
||||
<br />
|
||||
<SlFormatNumber type="currency" currency="CNY" value="2000" lang="zh-cn" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-format-number]
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
|
||||
[component-header:sl-icon-button]
|
||||
|
||||
Icons buttons are simple, icon-only buttons that can be used for actions and in toolbars.
|
||||
|
||||
For a full list of icons that come bundled with Shoelace, refer to the [icon component](/components/icon).
|
||||
|
||||
```html preview
|
||||
<sl-icon-button name="gear" label="Settings"></sl-icon-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlIconButton name="gear" label="Settings" />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Sizes
|
||||
@@ -22,6 +26,18 @@ Icon buttons inherit their parent element's `font-size`.
|
||||
<sl-icon-button name="pencil" label="Edit" style="font-size: 2.5rem;"></sl-icon-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlIconButton name="pencil" label="Edit" style={{ fontSize: '1.5rem' }} />
|
||||
<SlIconButton name="pencil" label="Edit" style={{ fontSize: '2rem' }} />
|
||||
<SlIconButton name="pencil" label="Edit" style={{ fontSize: '2.5rem' }} />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### 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.
|
||||
@@ -37,7 +53,7 @@ Icon buttons are designed to have a uniform appearance, so their color is not in
|
||||
.icon-button-color sl-icon-button::part(base) {
|
||||
color: #b00091;
|
||||
}
|
||||
|
||||
|
||||
.icon-button-color sl-icon-button::part(base):hover,
|
||||
.icon-button-color sl-icon-button::part(base):focus {
|
||||
color: #c913aa;
|
||||
@@ -49,6 +65,51 @@ Icon buttons are designed to have a uniform appearance, so their color is not in
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.icon-button-color sl-icon-button::part(base) {
|
||||
color: #b00091;
|
||||
}
|
||||
|
||||
.icon-button-color sl-icon-button::part(base):hover,
|
||||
.icon-button-color sl-icon-button::part(base):focus {
|
||||
color: #c913aa;
|
||||
}
|
||||
|
||||
.icon-button-color sl-icon-button::part(base):active {
|
||||
color: #960077;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="icon-button-color">
|
||||
<SlIconButton name="type-bold" label="Bold" />
|
||||
<SlIconButton name="type-italic" label="Italic" />
|
||||
<SlIconButton name="type-underline" label="Underline" />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Link Buttons
|
||||
|
||||
Use the `href` attribute to convert the button to a link.
|
||||
|
||||
```html preview
|
||||
<sl-icon-button name="gear" label="Settings" href="https://example.com" target="_blank"></sl-icon-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlIconButton name="gear" 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.
|
||||
@@ -59,9 +120,28 @@ Wrap a tooltip around an icon button to provide contextual information to the us
|
||||
</sl-tooltip>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIconButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlTooltip content="Settings">
|
||||
<SlIconButton name="gear" label="Settings" />
|
||||
</SlTooltip>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable the icon button.
|
||||
|
||||
```html preview
|
||||
<sl-icon-button name="gear" label="Settings" disabled></sl-icon-button>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlIconButton name="gear" label="Settings" disabled />;
|
||||
```
|
||||
|
||||
[component-metadata:sl-icon-button]
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
[component-header:sl-icon]
|
||||
|
||||
Icons are symbols that can be used to represent various options within an application.
|
||||
Shoelace 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.
|
||||
|
||||
Shoelace comes bundled with over 1,300 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.
|
||||
?> Depending on how you're loading Shoelace, you may need to copy icon assets and/or [set the base path](getting-started/installation#setting-the-base-path) so Shoelace knows where to load them from. Otherwise, icons may not appear and you'll see 404 Not Found errors in the dev console.
|
||||
|
||||
Click or tap on an icon below to copy its name and use it like this.
|
||||
## 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
|
||||
<sl-icon name="icon-name-here"></sl-icon>
|
||||
@@ -18,17 +20,81 @@ Click or tap on an icon below to copy its name and use it like this.
|
||||
<sl-icon slot="prefix" name="search"></sl-icon>
|
||||
</sl-input>
|
||||
<sl-select value="outline">
|
||||
<sl-menu-item value="outline">Outlined</sl-menu-item>
|
||||
<sl-menu-item value="fill">Filled</sl-menu-item>
|
||||
<sl-menu-item value="all">All icons</sl-menu-item>
|
||||
<sl-option value="outline">Outlined</sl-option>
|
||||
<sl-option value="fill">Filled</sl-option>
|
||||
<sl-option value="all">All icons</sl-option>
|
||||
</sl-select>
|
||||
</div>
|
||||
<div class="icon-list"></div>
|
||||
<input type="text" class="icon-copy-input">
|
||||
<input type="text" class="icon-copy-input" aria-hidden="true" tabindex="-1">
|
||||
</div>
|
||||
|
||||
## Examples
|
||||
|
||||
### Colors
|
||||
|
||||
Icons inherit their color from the current text color. Thus, you can set the `color` property on the `<sl-icon>` element or an ancestor to change the color.
|
||||
|
||||
```html preview
|
||||
<div style="color: #4a90e2;">
|
||||
<sl-icon name="exclamation-triangle"></sl-icon>
|
||||
<sl-icon name="archive"></sl-icon>
|
||||
<sl-icon name="battery-charging"></sl-icon>
|
||||
<sl-icon name="bell"></sl-icon>
|
||||
</div>
|
||||
<div style="color: #9013fe;">
|
||||
<sl-icon name="clock"></sl-icon>
|
||||
<sl-icon name="cloud"></sl-icon>
|
||||
<sl-icon name="download"></sl-icon>
|
||||
<sl-icon name="file-earmark"></sl-icon>
|
||||
</div>
|
||||
<div style="color: #417505;">
|
||||
<sl-icon name="flag"></sl-icon>
|
||||
<sl-icon name="heart"></sl-icon>
|
||||
<sl-icon name="image"></sl-icon>
|
||||
<sl-icon name="lightning"></sl-icon>
|
||||
</div>
|
||||
<div style="color: #f5a623;">
|
||||
<sl-icon name="mic"></sl-icon>
|
||||
<sl-icon name="search"></sl-icon>
|
||||
<sl-icon name="star"></sl-icon>
|
||||
<sl-icon name="trash"></sl-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div style={{ color: '#4a90e2' }}>
|
||||
<SlIcon name="exclamation-triangle"></SlIcon>
|
||||
<SlIcon name="archive"></SlIcon>
|
||||
<SlIcon name="battery-charging"></SlIcon>
|
||||
<SlIcon name="bell"></SlIcon>
|
||||
</div>
|
||||
<div style={{ color: '#9013fe' }}>
|
||||
<SlIcon name="clock"></SlIcon>
|
||||
<SlIcon name="cloud"></SlIcon>
|
||||
<SlIcon name="download"></SlIcon>
|
||||
<SlIcon name="file-earmark"></SlIcon>
|
||||
</div>
|
||||
<div style={{ color: '#417505' }}>
|
||||
<SlIcon name="flag"></SlIcon>
|
||||
<SlIcon name="heart"></SlIcon>
|
||||
<SlIcon name="image"></SlIcon>
|
||||
<SlIcon name="lightning"></SlIcon>
|
||||
</div>
|
||||
<div style={{ color: '#f5a623' }}>
|
||||
<SlIcon name="mic"></SlIcon>
|
||||
<SlIcon name="search"></SlIcon>
|
||||
<SlIcon name="star"></SlIcon>
|
||||
<SlIcon name="trash"></SlIcon>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### 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.
|
||||
@@ -40,6 +106,7 @@ Icons are sized relative to the current font size. To change their size, set the
|
||||
<sl-icon name="battery-charging"></sl-icon>
|
||||
<sl-icon name="bell"></sl-icon>
|
||||
<sl-icon name="clock"></sl-icon>
|
||||
<sl-icon name="cloud"></sl-icon>
|
||||
<sl-icon name="download"></sl-icon>
|
||||
<sl-icon name="file-earmark"></sl-icon>
|
||||
<sl-icon name="flag"></sl-icon>
|
||||
@@ -50,16 +117,60 @@ Icons are sized relative to the current font size. To change their size, set the
|
||||
<sl-icon name="search"></sl-icon>
|
||||
<sl-icon name="star"></sl-icon>
|
||||
<sl-icon name="trash"></sl-icon>
|
||||
<sl-icon name="x-circle"></sl-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<div style={{ fontSize: '32px' }}>
|
||||
<SlIcon name="exclamation-triangle" />
|
||||
<SlIcon name="archive" />
|
||||
<SlIcon name="battery-charging" />
|
||||
<SlIcon name="bell" />
|
||||
<SlIcon name="clock" />
|
||||
<SlIcon name="cloud" />
|
||||
<SlIcon name="download" />
|
||||
<SlIcon name="file-earmark" />
|
||||
<SlIcon name="flag" />
|
||||
<SlIcon name="heart" />
|
||||
<SlIcon name="image" />
|
||||
<SlIcon name="lightning" />
|
||||
<SlIcon name="mic" />
|
||||
<SlIcon name="search" />
|
||||
<SlIcon name="star" />
|
||||
<SlIcon name="trash" />
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
### Labels
|
||||
|
||||
For non-decorative icons, use the `label` attribute to announce it to assistive devices.
|
||||
|
||||
```html preview
|
||||
<sl-icon name="star-fill" label="Add to favorites"></sl-icon>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlIcon name="star-fill" 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
|
||||
<sl-icon src="/assets/images/shoe.svg" style="font-size: 8rem;"></sl-icon>
|
||||
<sl-icon src="https://shoelace.style/assets/images/shoe.svg" style="font-size: 8rem;"></sl-icon>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlIcon src="https://shoelace.style/assets/images/shoe.svg" style={{ fontSize: '8rem' }}></SlIcon>;
|
||||
```
|
||||
|
||||
## Icon Libraries
|
||||
@@ -76,7 +187,7 @@ Here's an example that registers an icon library located in the `/assets/icons`
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/shoelace/dist/utilities/icon-library.js';
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('my-icons', {
|
||||
resolver: name => `/assets/icons/${name}.svg`,
|
||||
@@ -104,7 +215,7 @@ Icons in this library are licensed under the [Creative Commons 4.0 License](http
|
||||
|
||||
```html preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/shoelace.js';
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('boxicons', {
|
||||
resolver: name => {
|
||||
@@ -113,7 +224,7 @@ Icons in this library are licensed under the [Creative Commons 4.0 License](http
|
||||
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')
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor')
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -124,14 +235,14 @@ Icons in this library are licensed under the [Creative Commons 4.0 License](http
|
||||
<sl-icon library="boxicons" name="bx-save"></sl-icon>
|
||||
<sl-icon library="boxicons" name="bx-server"></sl-icon>
|
||||
<sl-icon library="boxicons" name="bx-wine"></sl-icon>
|
||||
<br>
|
||||
<br />
|
||||
<sl-icon library="boxicons" name="bxs-bot"></sl-icon>
|
||||
<sl-icon library="boxicons" name="bxs-cookie"></sl-icon>
|
||||
<sl-icon library="boxicons" name="bxs-joystick"></sl-icon>
|
||||
<sl-icon library="boxicons" name="bxs-save"></sl-icon>
|
||||
<sl-icon library="boxicons" name="bxs-server"></sl-icon>
|
||||
<sl-icon library="boxicons" name="bxs-wine"></sl-icon>
|
||||
<br>
|
||||
<br />
|
||||
<sl-icon library="boxicons" name="bxl-apple"></sl-icon>
|
||||
<sl-icon library="boxicons" name="bxl-chrome"></sl-icon>
|
||||
<sl-icon library="boxicons" name="bxl-edge"></sl-icon>
|
||||
@@ -141,27 +252,27 @@ Icons in this library are licensed under the [Creative Commons 4.0 License](http
|
||||
</div>
|
||||
```
|
||||
|
||||
### Feather Icons
|
||||
### Lucide
|
||||
|
||||
This will register the [Feather Icons](https://feathericons.com/) library using the jsDelivr CDN.
|
||||
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/feathericons/feather/blob/master/LICENSE).
|
||||
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;">
|
||||
<sl-icon library="feather" name="feather"></sl-icon>
|
||||
<sl-icon library="feather" name="pie-chart"></sl-icon>
|
||||
<sl-icon library="feather" name="settings"></sl-icon>
|
||||
<sl-icon library="feather" name="map-pin"></sl-icon>
|
||||
<sl-icon library="feather" name="printer"></sl-icon>
|
||||
<sl-icon library="feather" name="shopping-cart"></sl-icon>
|
||||
<sl-icon library="lucide" name="feather"></sl-icon>
|
||||
<sl-icon library="lucide" name="pie-chart"></sl-icon>
|
||||
<sl-icon library="lucide" name="settings"></sl-icon>
|
||||
<sl-icon library="lucide" name="map-pin"></sl-icon>
|
||||
<sl-icon library="lucide" name="printer"></sl-icon>
|
||||
<sl-icon library="lucide" name="shopping-cart"></sl-icon>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/shoelace.js';
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('feather', {
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/icons/${name}.svg`
|
||||
registerIconLibrary('lucide', {
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/lucide-static@0.16.29/icons/${name}.svg`
|
||||
});
|
||||
</script>
|
||||
```
|
||||
@@ -174,7 +285,7 @@ Icons in this library are licensed under the [Font Awesome Free License](https:/
|
||||
|
||||
```html preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/shoelace.js';
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('fa', {
|
||||
resolver: name => {
|
||||
@@ -195,20 +306,20 @@ Icons in this library are licensed under the [Font Awesome Free License](https:/
|
||||
<sl-icon library="fa" name="far-hdd"></sl-icon>
|
||||
<sl-icon library="fa" name="far-heart"></sl-icon>
|
||||
<sl-icon library="fa" name="far-star"></sl-icon>
|
||||
<br>
|
||||
<br />
|
||||
<sl-icon library="fa" name="fas-archive"></sl-icon>
|
||||
<sl-icon library="fa" name="fas-book"></sl-icon>
|
||||
<sl-icon library="fa" name="fas-chess-knight"></sl-icon>
|
||||
<sl-icon library="fa" name="fas-dice"></sl-icon>
|
||||
<sl-icon library="fa" name="fas-pizza-slice"></sl-icon>
|
||||
<sl-icon library="fa" name="fas-scroll"></sl-icon>
|
||||
<br>
|
||||
<br />
|
||||
<sl-icon library="fa" name="fab-apple"></sl-icon>
|
||||
<sl-icon library="fa" name="fab-chrome"></sl-icon>
|
||||
<sl-icon library="fa" name="fab-edge"></sl-icon>
|
||||
<sl-icon library="fa" name="fab-firefox"></sl-icon>
|
||||
<sl-icon library="fa" name="fab-opera"></sl-icon>
|
||||
<sl-icon library="fa" name="fab-microsoft"></sl-icon>
|
||||
<sl-icon library="fa" name="fab-microsoft"></sl-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
@@ -220,20 +331,45 @@ Icons in this library are licensed under the [MIT License](https://github.com/ta
|
||||
|
||||
```html preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/shoelace.js';
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('heroicons', {
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/heroicons@0.4.2/outline/${name}.svg`
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/heroicons@2.0.1/24/outline/${name}.svg`
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<sl-icon library="heroicons" name="chat"></sl-icon>
|
||||
<sl-icon library="heroicons" name="chat-bubble-left"></sl-icon>
|
||||
<sl-icon library="heroicons" name="cloud"></sl-icon>
|
||||
<sl-icon library="heroicons" name="cog"></sl-icon>
|
||||
<sl-icon library="heroicons" name="document-text"></sl-icon>
|
||||
<sl-icon library="heroicons" name="gift"></sl-icon>
|
||||
<sl-icon library="heroicons" name="volume-up"></sl-icon>
|
||||
<sl-icon library="heroicons" name="speaker-wave"></sl-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;">
|
||||
<sl-icon library="iconoir" name="check-circled-outline"></sl-icon>
|
||||
<sl-icon library="iconoir" name="drawer"></sl-icon>
|
||||
<sl-icon library="iconoir" name="keyframes"></sl-icon>
|
||||
<sl-icon library="iconoir" name="headset-help"></sl-icon>
|
||||
<sl-icon library="iconoir" name="color-picker"></sl-icon>
|
||||
<sl-icon library="iconoir" name="wifi"></sl-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
@@ -245,11 +381,11 @@ Icons in this library are licensed under the [MIT License](https://github.com/io
|
||||
|
||||
```html preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/shoelace.js';
|
||||
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 => {
|
||||
mutator: svg => {
|
||||
svg.setAttribute('fill', 'currentColor');
|
||||
svg.setAttribute('stroke', 'currentColor');
|
||||
[...svg.querySelectorAll('.ionicon-fill-none')].map(el => el.setAttribute('fill', 'none'));
|
||||
@@ -265,14 +401,14 @@ Icons in this library are licensed under the [MIT License](https://github.com/io
|
||||
<sl-icon library="ionicons" name="chatbubble"></sl-icon>
|
||||
<sl-icon library="ionicons" name="settings"></sl-icon>
|
||||
<sl-icon library="ionicons" name="warning"></sl-icon>
|
||||
<br>
|
||||
<br />
|
||||
<sl-icon library="ionicons" name="alarm-outline"></sl-icon>
|
||||
<sl-icon library="ionicons" name="american-football-outline"></sl-icon>
|
||||
<sl-icon library="ionicons" name="bug-outline"></sl-icon>
|
||||
<sl-icon library="ionicons" name="chatbubble-outline"></sl-icon>
|
||||
<sl-icon library="ionicons" name="settings-outline"></sl-icon>
|
||||
<sl-icon library="ionicons" name="warning-outline"></sl-icon>
|
||||
<br>
|
||||
<br />
|
||||
<sl-icon library="ionicons" name="alarm-sharp"></sl-icon>
|
||||
<sl-icon library="ionicons" name="american-football-sharp"></sl-icon>
|
||||
<sl-icon library="ionicons" name="bug-sharp"></sl-icon>
|
||||
@@ -290,7 +426,7 @@ Icons in this library are licensed under the [MIT License](https://github.com/mi
|
||||
|
||||
```html preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/shoelace.js';
|
||||
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`,
|
||||
@@ -305,7 +441,7 @@ Icons in this library are licensed under the [MIT License](https://github.com/mi
|
||||
<sl-icon library="jam" name="leaf"></sl-icon>
|
||||
<sl-icon library="jam" name="picture"></sl-icon>
|
||||
<sl-icon library="jam" name="set-square"></sl-icon>
|
||||
<br>
|
||||
<br />
|
||||
<sl-icon library="jam" name="calendar-f"></sl-icon>
|
||||
<sl-icon library="jam" name="camera-f"></sl-icon>
|
||||
<sl-icon library="jam" name="filter-f"></sl-icon>
|
||||
@@ -323,7 +459,7 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
|
||||
|
||||
```html preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/shoelace.js';
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('material', {
|
||||
resolver: name => {
|
||||
@@ -341,14 +477,14 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
|
||||
<sl-icon library="material" name="volume_up"></sl-icon>
|
||||
<sl-icon library="material" name="settings"></sl-icon>
|
||||
<sl-icon library="material" name="shopping_basket"></sl-icon>
|
||||
<br>
|
||||
<br />
|
||||
<sl-icon library="material" name="notifications_round"></sl-icon>
|
||||
<sl-icon library="material" name="email_round"></sl-icon>
|
||||
<sl-icon library="material" name="delete_round"></sl-icon>
|
||||
<sl-icon library="material" name="volume_up_round"></sl-icon>
|
||||
<sl-icon library="material" name="settings_round"></sl-icon>
|
||||
<sl-icon library="material" name="shopping_basket_round"></sl-icon>
|
||||
<br>
|
||||
<br />
|
||||
<sl-icon library="material" name="notifications_sharp"></sl-icon>
|
||||
<sl-icon library="material" name="email_sharp"></sl-icon>
|
||||
<sl-icon library="material" name="delete_sharp"></sl-icon>
|
||||
@@ -360,38 +496,70 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
|
||||
|
||||
### Remix Icon
|
||||
|
||||
This will register the [Remix Icon](https://remixicon.com/) library using the jsDelivr CDN. This library has two variations: line (default) and fill (`*-fill`). It also groups icons by categories, so the name must include the category and icon separated by a slash. A mutator function is required to set the SVG's `fill` to `currentColor`.
|
||||
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/shoelace.js';
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('remixicon', {
|
||||
resolver: name => {
|
||||
const match = name.match(/^(.*?)\/(.*?)(-(fill))?$/);
|
||||
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]}${match[3] || '-line'}.svg`;
|
||||
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;">
|
||||
<sl-icon library="remixicon" name="business/cloud"></sl-icon>
|
||||
<sl-icon library="remixicon" name="design/brush"></sl-icon>
|
||||
<sl-icon library="remixicon" name="business/pie-chart"></sl-icon>
|
||||
<sl-icon library="remixicon" name="development/bug"></sl-icon>
|
||||
<sl-icon library="remixicon" name="media/image"></sl-icon>
|
||||
<sl-icon library="remixicon" name="system/alert"></sl-icon>
|
||||
<br>
|
||||
<sl-icon library="remixicon" name="business/cloud-line"></sl-icon>
|
||||
<sl-icon library="remixicon" name="design/brush-line"></sl-icon>
|
||||
<sl-icon library="remixicon" name="business/pie-chart-line"></sl-icon>
|
||||
<sl-icon library="remixicon" name="development/bug-line"></sl-icon>
|
||||
<sl-icon library="remixicon" name="media/image-line"></sl-icon>
|
||||
<sl-icon library="remixicon" name="system/alert-line"></sl-icon>
|
||||
<br />
|
||||
<sl-icon library="remixicon" name="business/cloud-fill"></sl-icon>
|
||||
<sl-icon library="remixicon" name="design/brush-fill"></sl-icon>
|
||||
<sl-icon library="remixicon" name="business/pie-chart-fill"></sl-icon>
|
||||
<sl-icon library="remixicon" name="development/bug-fill"></sl-icon>
|
||||
<sl-icon library="remixicon" name="media/image-fill"></sl-icon>
|
||||
<sl-icon library="remixicon" name="system/alert-fill"></sl-icon>
|
||||
<sl-icon library="remixicon" name="system/alert-fill"></sl-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;">
|
||||
<sl-icon library="tabler" name="alert-triangle"></sl-icon>
|
||||
<sl-icon library="tabler" name="arrow-back"></sl-icon>
|
||||
<sl-icon library="tabler" name="at"></sl-icon>
|
||||
<sl-icon library="tabler" name="ball-baseball"></sl-icon>
|
||||
<sl-icon library="tabler" name="cake"></sl-icon>
|
||||
<sl-icon library="tabler" name="files"></sl-icon>
|
||||
<br />
|
||||
<sl-icon library="tabler" name="keyboard"></sl-icon>
|
||||
<sl-icon library="tabler" name="moon"></sl-icon>
|
||||
<sl-icon library="tabler" name="pig"></sl-icon>
|
||||
<sl-icon library="tabler" name="printer"></sl-icon>
|
||||
<sl-icon library="tabler" name="ship"></sl-icon>
|
||||
<sl-icon library="tabler" name="toilet-paper"></sl-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
@@ -403,12 +571,14 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
|
||||
|
||||
```html preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/shoelace.js';
|
||||
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`;
|
||||
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')
|
||||
});
|
||||
@@ -421,12 +591,12 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
|
||||
<sl-icon library="unicons" name="polygon"></sl-icon>
|
||||
<sl-icon library="unicons" name="rocket"></sl-icon>
|
||||
<sl-icon library="unicons" name="star"></sl-icon>
|
||||
<br>
|
||||
<br />
|
||||
<sl-icon library="unicons" name="clock-s"></sl-icon>
|
||||
<sl-icon library="unicons" name="graph-bar-s"></sl-icon>
|
||||
<sl-icon library="unicons" name="padlock-s"></sl-icon>
|
||||
<sl-icon library="unicons" name="polygon-s"></sl-icon>
|
||||
<sl-icon library="unicons" name="rocket-s"></sl-icon>
|
||||
<sl-icon library="unicons" name="rocket-s"></sl-icon>
|
||||
<sl-icon library="unicons" name="star-s"></sl-icon>
|
||||
</div>
|
||||
```
|
||||
@@ -439,7 +609,7 @@ This example will load the same set of icons from the jsDelivr CDN instead of yo
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/shoelace/dist/utilities/icon-library.js';
|
||||
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`
|
||||
@@ -455,7 +625,7 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/shoelace/dist/utilities/icon-library.js';
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('system', {
|
||||
resolver: name => `/path/to/custom/icons/${name}.svg`
|
||||
@@ -465,6 +635,19 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
|
||||
<!-- Supporting scripts and styles for the search utility -->
|
||||
<script>
|
||||
function wrapWithTooltip(item) {
|
||||
const tooltip = document.createElement('sl-tooltip');
|
||||
tooltip.content = item.getAttribute('data-name');
|
||||
|
||||
// Close open tooltips
|
||||
document.querySelectorAll('.icon-list sl-tooltip[open]').forEach(tooltip => tooltip.hide());
|
||||
|
||||
// Wrap it with a tooltip and trick it into showing up
|
||||
item.parentNode.insertBefore(tooltip, item);
|
||||
tooltip.appendChild(item);
|
||||
requestAnimationFrame(() => tooltip.dispatchEvent(new MouseEvent('mouseover')));
|
||||
}
|
||||
|
||||
fetch('/dist/assets/icons/icons.json')
|
||||
.then(res => res.json())
|
||||
.then(icons => {
|
||||
@@ -475,6 +658,7 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
const loader = container.querySelector('.icon-loader');
|
||||
const list = container.querySelector('.icon-list');
|
||||
const queue = [];
|
||||
let inputTimeout;
|
||||
|
||||
// Generate icons
|
||||
icons.map(i => {
|
||||
@@ -483,37 +667,44 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
item.setAttribute('data-name', i.name);
|
||||
item.setAttribute('data-terms', [i.name, i.title, ...(i.tags || []), ...(i.categories || [])].join(' '));
|
||||
item.innerHTML = `
|
||||
<svg width="1em" height="1em">
|
||||
<svg width="1em" height="1em" fill="currentColor">
|
||||
<use xlink:href="/assets/icons/sprite.svg#${i.name}"></use>
|
||||
</svg>
|
||||
`;
|
||||
list.appendChild(item);
|
||||
|
||||
const tooltip = document.createElement('sl-tooltip');
|
||||
tooltip.content = i.name;
|
||||
|
||||
tooltip.appendChild(item);
|
||||
list.appendChild(tooltip);
|
||||
// Wrap it with a tooltip the first time the mouse lands on it. We do this instead of baking them into the DOM
|
||||
// to improve this page's performance. See: https://github.com/shoelace-style/shoelace/issues/1122
|
||||
item.addEventListener('mouseover', () => wrapWithTooltip(item), { once: true });
|
||||
|
||||
// Copy on click
|
||||
item.addEventListener('click', () => {
|
||||
const tooltip = item.closest('sl-tooltip');
|
||||
copyInput.value = i.name;
|
||||
copyInput.select();
|
||||
document.execCommand('copy');
|
||||
tooltip.content = 'Copied!';
|
||||
setTimeout(() => tooltip.content = i.name, 1000);
|
||||
|
||||
if (tooltip) {
|
||||
tooltip.content = 'Copied!';
|
||||
setTimeout(() => tooltip.content = i.name, 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Filter as the user types
|
||||
input.addEventListener('sl-input', () => {
|
||||
[...list.querySelectorAll('.icon-list-item')].map(item => {
|
||||
const filter = input.value.toLowerCase();
|
||||
if (filter === '') {
|
||||
item.hidden = false;
|
||||
} else {
|
||||
const terms = item.getAttribute('data-terms').toLowerCase();
|
||||
item.hidden = terms.indexOf(filter) < 0;
|
||||
}
|
||||
});
|
||||
clearTimeout(inputTimeout);
|
||||
inputTimeout = setTimeout(() => {
|
||||
[...list.querySelectorAll('.icon-list-item')].map(item => {
|
||||
const filter = input.value.toLowerCase();
|
||||
if (filter === '') {
|
||||
item.hidden = false;
|
||||
} else {
|
||||
const terms = item.getAttribute('data-terms').toLowerCase();
|
||||
item.hidden = terms.indexOf(filter) < 0;
|
||||
}
|
||||
});
|
||||
}, 250);
|
||||
});
|
||||
|
||||
// Sort by type and remember preference
|
||||
@@ -534,6 +725,10 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
padding: var(--sl-spacing-medium);
|
||||
}
|
||||
|
||||
.icon-search [hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon-search-controls {
|
||||
display: flex;
|
||||
}
|
||||
@@ -571,7 +766,7 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--sl-border-radius-circle);
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
font-size: 24px;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
@@ -582,11 +777,7 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
|
||||
.icon-list-item:hover {
|
||||
background-color: var(--sl-color-primary-50);
|
||||
color: var(--sl-color-primary-500);
|
||||
}
|
||||
|
||||
.sl-theme-dark .icon-list-item:hover {
|
||||
background-color: var(--sl-color-primary-900);
|
||||
color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.icon-list[data-type="outline"] .icon-list-item[data-name$="-fill"] {
|
||||
|
||||
@@ -2,17 +2,42 @@
|
||||
|
||||
[component-header:sl-image-comparer]
|
||||
|
||||
Compare visual differences between similar photos with a sliding panel.
|
||||
|
||||
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
|
||||
<sl-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.">
|
||||
<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."
|
||||
/>
|
||||
</sl-image-comparer>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlImageComparer } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlImageComparer>
|
||||
<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."
|
||||
/>
|
||||
</SlImageComparer>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Initial Position
|
||||
@@ -20,10 +45,37 @@ For best results, use images that share the same dimensions. The slider can be c
|
||||
Use the `position` attribute to set the initial position of the slider. This is a percentage from `0` to `100`.
|
||||
|
||||
```html preview
|
||||
<sl-image-comparer position="0">
|
||||
<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.">
|
||||
<sl-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."
|
||||
/>
|
||||
</sl-image-comparer>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlImageComparer } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlImageComparer 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."
|
||||
/>
|
||||
</SlImageComparer>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-image-comparer]
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
|
||||
[component-header:sl-include]
|
||||
|
||||
Includes give you the power to embed external HTML files into the page.
|
||||
|
||||
Included files are asynchronously requested using `window.fetch()`. Requests are cached, so the same file can be included multiple times, but only one request will be made.
|
||||
|
||||
The included content will be inserted into the `<sl-include>` element's default slot so it can be easily accessed and styled through the light DOM.
|
||||
|
||||
```html preview
|
||||
<sl-include src="/assets/examples/include.html"></sl-include>
|
||||
<sl-include src="https://shoelace.style/assets/examples/include.html"></sl-include>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInclude } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlInclude src="https://shoelace.style/assets/examples/include.html" />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
@@ -21,17 +25,21 @@ When an include file loads successfully, the `sl-load` event will be emitted. Yo
|
||||
If the request fails, the `sl-error` event will be emitted. In this case, `event.detail.status` will contain the resulting HTTP status code of the request, e.g. 404 (not found).
|
||||
|
||||
```html
|
||||
<sl-include src="/assets/examples/include.html"></sl-include>
|
||||
<sl-include src="https://shoelace.style/assets/examples/include.html"></sl-include>
|
||||
|
||||
<script>
|
||||
const include = document.querySelector('sl-include');
|
||||
|
||||
include.addEventListener('sl-load', () => {
|
||||
console.log('Success');
|
||||
|
||||
include.addEventListener('sl-load', event => {
|
||||
if (event.eventPhase === Event.AT_TARGET) {
|
||||
console.log('Success');
|
||||
}
|
||||
});
|
||||
|
||||
include.addEventListener('sl-error', event => {
|
||||
console.log('Error', event.detail.status);
|
||||
if (event.eventPhase === Event.AT_TARGET) {
|
||||
console.log('Error', event.detail.status);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -2,103 +2,20 @@
|
||||
|
||||
[component-header:sl-input]
|
||||
|
||||
Inputs collect data from the user.
|
||||
|
||||
```html preview
|
||||
<sl-input></sl-input>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form.md) instead.
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
?> Please refer to the section on [form control validation](/components/form?id=form-control-validation) to learn how to do client-side validation.
|
||||
const App = () => <SlInput />;
|
||||
```
|
||||
|
||||
?> 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
|
||||
|
||||
### Placeholders
|
||||
|
||||
Use the `placeholder` attribute to add a placeholder.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Type something"></sl-input>
|
||||
```
|
||||
|
||||
### Clearable
|
||||
|
||||
Add the `clearable` prop to add a clear button when the input has content.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Clearable" clearable></sl-input>
|
||||
```
|
||||
|
||||
### Toggle Password
|
||||
|
||||
Add the `toggle-password` prop to add a toggle button that will show the password when activated.
|
||||
|
||||
```html preview
|
||||
<sl-input type="password" placeholder="Password Toggle" size="small" toggle-password></sl-input>
|
||||
<br>
|
||||
<sl-input type="password" placeholder="Password Toggle" size="medium" toggle-password></sl-input>
|
||||
<br>
|
||||
<sl-input type="password" placeholder="Password Toggle" size="large" toggle-password></sl-input>
|
||||
```
|
||||
|
||||
### Pill
|
||||
|
||||
Use the `pill` prop to give inputs rounded edges.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Small" size="small" pill></sl-input>
|
||||
<br>
|
||||
<sl-input placeholder="Medium" size="medium" pill></sl-input>
|
||||
<br>
|
||||
<sl-input placeholder="Large" size="large" pill></sl-input>
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable an input.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Disabled" size="small" disabled></sl-input>
|
||||
<br>
|
||||
<sl-input placeholder="Disabled" size="medium" disabled></sl-input>
|
||||
<br>
|
||||
<sl-input placeholder="Disabled" size="large" disabled></sl-input>
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change an input's size.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Small" size="small"></sl-input>
|
||||
<br>
|
||||
<sl-input placeholder="Medium" size="medium"></sl-input>
|
||||
<br>
|
||||
<sl-input placeholder="Large" size="large"></sl-input>
|
||||
```
|
||||
|
||||
### Prefix & Suffix Icons
|
||||
|
||||
Use the `prefix` and `suffix` slots to add icons.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Small" size="small">
|
||||
<sl-icon name="tag" slot="prefix"></sl-icon>
|
||||
<sl-icon name="gear" slot="suffix"></sl-icon>
|
||||
</sl-input>
|
||||
<br>
|
||||
<sl-input placeholder="Medium" size="medium">
|
||||
<sl-icon name="tag" slot="prefix"></sl-icon>
|
||||
<sl-icon name="gear" slot="suffix"></sl-icon>
|
||||
</sl-input>
|
||||
<br>
|
||||
<sl-input placeholder="Large" size="large">
|
||||
<sl-icon name="tag" slot="prefix"></sl-icon>
|
||||
<sl-icon name="gear" slot="suffix"></sl-icon>
|
||||
</sl-input>
|
||||
```
|
||||
|
||||
### Labels
|
||||
|
||||
Use the `label` attribute to give the input an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
@@ -107,15 +24,252 @@ Use the `label` attribute to give the input an accessible label. For labels that
|
||||
<sl-input label="What is your name?"></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIcon, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlInput 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
|
||||
<sl-input
|
||||
label="Nickname"
|
||||
help-text="What would you like people to call you?"
|
||||
></sl-input>
|
||||
<sl-input label="Nickname" help-text="What would you like people to call you?"></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIcon, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlInput label="Nickname" help-text="What would you like people to call you?" />;
|
||||
```
|
||||
|
||||
### Placeholders
|
||||
|
||||
Use the `placeholder` attribute to add a placeholder.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Type something"></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlInput placeholder="Type something" />;
|
||||
```
|
||||
|
||||
### Clearable
|
||||
|
||||
Add the `clearable` attribute to add a clear button when the input has content.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Clearable" clearable></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlInput placeholder="Clearable" clearable />;
|
||||
```
|
||||
|
||||
### Toggle Password
|
||||
|
||||
Add the `password-toggle` attribute to add a toggle button that will show the password when activated.
|
||||
|
||||
```html preview
|
||||
<sl-input type="password" placeholder="Password Toggle" password-toggle></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlInput type="password" placeholder="Password Toggle" size="medium" password-toggle />;
|
||||
```
|
||||
|
||||
### Filled Inputs
|
||||
|
||||
Add the `filled` attribute to draw a filled input.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Type something" filled></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlInput placeholder="Type something" filled />;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable an input.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Disabled" disabled></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlInput placeholder="Disabled" disabled />;
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change an input's size.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Small" size="small"></sl-input>
|
||||
<br />
|
||||
<sl-input placeholder="Medium" size="medium"></sl-input>
|
||||
<br />
|
||||
<sl-input placeholder="Large" size="large"></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlInput placeholder="Small" size="small" />
|
||||
<br />
|
||||
<SlInput placeholder="Medium" size="medium" />
|
||||
<br />
|
||||
<SlInput placeholder="Large" size="large" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill
|
||||
|
||||
Use the `pill` attribute to give inputs rounded edges.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Small" size="small" pill></sl-input>
|
||||
<br />
|
||||
<sl-input placeholder="Medium" size="medium" pill></sl-input>
|
||||
<br />
|
||||
<sl-input placeholder="Large" size="large" pill></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlInput placeholder="Small" size="small" pill />
|
||||
<br />
|
||||
<SlInput placeholder="Medium" size="medium" pill />
|
||||
<br />
|
||||
<SlInput placeholder="Large" size="large" pill />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Input Types
|
||||
|
||||
The `type` attribute controls the type of input the browser renders.
|
||||
|
||||
```html preview
|
||||
<sl-input type="email" placeholder="Email"></sl-input>
|
||||
<br />
|
||||
<sl-input type="number" placeholder="Number"></sl-input>
|
||||
<br />
|
||||
<sl-input type="date" placeholder="Date"></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlInput type="email" placeholder="Email" />
|
||||
<br />
|
||||
<SlInput type="number" placeholder="Number" />
|
||||
<br />
|
||||
<SlInput type="date" placeholder="Date" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix & Suffix Icons
|
||||
|
||||
Use the `prefix` and `suffix` slots to add icons.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Small" size="small">
|
||||
<sl-icon name="house" slot="prefix"></sl-icon>
|
||||
<sl-icon name="chat" slot="suffix"></sl-icon>
|
||||
</sl-input>
|
||||
<br />
|
||||
<sl-input placeholder="Medium" size="medium">
|
||||
<sl-icon name="house" slot="prefix"></sl-icon>
|
||||
<sl-icon name="chat" slot="suffix"></sl-icon>
|
||||
</sl-input>
|
||||
<br />
|
||||
<sl-input placeholder="Large" size="large">
|
||||
<sl-icon name="house" slot="prefix"></sl-icon>
|
||||
<sl-icon name="chat" slot="suffix"></sl-icon>
|
||||
</sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIcon, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlInput placeholder="Small" size="small">
|
||||
<SlIcon name="house" slot="prefix"></SlIcon>
|
||||
<SlIcon name="chat" slot="suffix"></SlIcon>
|
||||
</SlInput>
|
||||
<br />
|
||||
<SlInput placeholder="Medium" size="medium">
|
||||
<SlIcon name="house" slot="prefix"></SlIcon>
|
||||
<SlIcon name="chat" slot="suffix"></SlIcon>
|
||||
</SlInput>
|
||||
<br />
|
||||
<SlInput placeholder="Large" size="large">
|
||||
<SlIcon name="house" slot="prefix"></SlIcon>
|
||||
<SlIcon name="chat" slot="suffix"></SlIcon>
|
||||
</SlInput>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-input class="label-on-left" label="Name" help-text="Enter your name""></sl-input>
|
||||
<sl-input class="label-on-left" label="Email" type="email" help-text="Enter your email"></sl-input>
|
||||
<sl-textarea class="label-on-left" label="Bio" help-text="Tell us something about yourself"></sl-textarea>
|
||||
|
||||
<style>
|
||||
.label-on-left {
|
||||
--label-width: 3.75rem;
|
||||
--gap-width: 1rem;
|
||||
}
|
||||
|
||||
.label-on-left + .label-on-left {
|
||||
margin-top: var(--sl-spacing-medium);
|
||||
}
|
||||
|
||||
.label-on-left::part(form-control) {
|
||||
display: grid;
|
||||
grid: auto / var(--label-width) 1fr;
|
||||
gap: var(--sl-spacing-3x-small) 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>
|
||||
```
|
||||
|
||||
[component-metadata:sl-input]
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# Menu Divider
|
||||
|
||||
[component-header:sl-menu-divider]
|
||||
|
||||
Menu dividers are used to visually group menu items.
|
||||
|
||||
```html preview
|
||||
<sl-menu
|
||||
style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); border-radius: var(--sl-border-radius-medium);"
|
||||
>
|
||||
<sl-menu-item value="1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="2">Option 2</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-menu-item value="3">Option 3</sl-menu-item>
|
||||
<sl-menu-item value="4">Option 4</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-menu-item value="5">Option 5</sl-menu-item>
|
||||
<sl-menu-item value="6">Option 6</sl-menu-item>
|
||||
</sl-menu>
|
||||
```
|
||||
|
||||
[component-metadata:sl-menu-divider]
|
||||
@@ -2,19 +2,15 @@
|
||||
|
||||
[component-header:sl-menu-item]
|
||||
|
||||
Menu items provide options for the user to pick from in a menu.
|
||||
|
||||
```html preview
|
||||
<sl-menu
|
||||
style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); border-radius: var(--sl-border-radius-medium);"
|
||||
>
|
||||
<sl-menu style="max-width: 200px;">
|
||||
<sl-menu-item>Option 1</sl-menu-item>
|
||||
<sl-menu-item>Option 2</sl-menu-item>
|
||||
<sl-menu-item>Option 3</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-menu-item checked>Checked</sl-menu-item>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item type="checkbox" checked>Checkbox</sl-menu-item>
|
||||
<sl-menu-item disabled>Disabled</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item>
|
||||
Prefix Icon
|
||||
<sl-icon slot="prefix" name="gift"></sl-icon>
|
||||
@@ -26,4 +22,193 @@ Menu items provide options for the user to pick from in a menu.
|
||||
</sl-menu>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider, SlIcon, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlMenu style={{ maxWidth: '200px' }}>
|
||||
<SlMenuItem>Option 1</SlMenuItem>
|
||||
<SlMenuItem>Option 2</SlMenuItem>
|
||||
<SlMenuItem>Option 3</SlMenuItem>
|
||||
<SlDivider />
|
||||
<SlMenuItem type="checkbox" checked>
|
||||
Checkbox
|
||||
</SlMenuItem>
|
||||
<SlMenuItem disabled>Disabled</SlMenuItem>
|
||||
<SlDivider />
|
||||
<SlMenuItem>
|
||||
Prefix Icon
|
||||
<SlIcon slot="prefix" name="gift" />
|
||||
</SlMenuItem>
|
||||
<SlMenuItem>
|
||||
Suffix Icon
|
||||
<SlIcon slot="suffix" name="heart" />
|
||||
</SlMenuItem>
|
||||
</SlMenu>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Disabled
|
||||
|
||||
Add the `disabled` attribute to disable the menu item so it cannot be selected.
|
||||
|
||||
```html preview
|
||||
<sl-menu style="max-width: 200px;">
|
||||
<sl-menu-item>Option 1</sl-menu-item>
|
||||
<sl-menu-item disabled>Option 2</sl-menu-item>
|
||||
<sl-menu-item>Option 3</sl-menu-item>
|
||||
</sl-menu>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlMenu style={{ maxWidth: '200px' }}>
|
||||
<SlMenuItem>Option 1</SlMenuItem>
|
||||
<SlMenuItem disabled>Option 2</SlMenuItem>
|
||||
<SlMenuItem>Option 3</SlMenuItem>
|
||||
</SlMenu>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix & Suffix
|
||||
|
||||
Add content to the start and end of menu items using the `prefix` and `suffix` slots.
|
||||
|
||||
```html preview
|
||||
<sl-menu style="max-width: 200px;">
|
||||
<sl-menu-item>
|
||||
<sl-icon slot="prefix" name="house"></sl-icon>
|
||||
Home
|
||||
</sl-menu-item>
|
||||
|
||||
<sl-menu-item>
|
||||
<sl-icon slot="prefix" name="envelope"></sl-icon>
|
||||
Messages
|
||||
<sl-badge slot="suffix" variant="primary" pill>12</sl-badge>
|
||||
</sl-menu-item>
|
||||
|
||||
<sl-divider></sl-divider>
|
||||
|
||||
<sl-menu-item>
|
||||
<sl-icon slot="prefix" name="gear"></sl-icon>
|
||||
Settings
|
||||
</sl-menu-item>
|
||||
</sl-menu>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlBadge, SlDivider, SlIcon, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlMenu style={{ maxWidth: '200px' }}>
|
||||
<SlMenuItem>
|
||||
<SlIcon slot="prefix" name="house" />
|
||||
Home
|
||||
</SlMenuItem>
|
||||
|
||||
<SlMenuItem>
|
||||
<SlIcon slot="prefix" name="envelope" />
|
||||
Messages
|
||||
<SlBadge slot="suffix" variant="primary" pill>
|
||||
12
|
||||
</SlBadge>
|
||||
</SlMenuItem>
|
||||
|
||||
<SlDivider />
|
||||
|
||||
<SlMenuItem>
|
||||
<SlIcon slot="prefix" name="gear" />
|
||||
Settings
|
||||
</SlMenuItem>
|
||||
</SlMenu>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-menu style="max-width: 200px;">
|
||||
<sl-menu-item type="checkbox">Autosave</sl-menu-item>
|
||||
<sl-menu-item type="checkbox" checked>Check Spelling</sl-menu-item>
|
||||
<sl-menu-item type="checkbox">Word Wrap</sl-menu-item>
|
||||
</sl-menu>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlMenu style={{ maxWidth: '200px' }}>
|
||||
<SlMenuItem type="checkbox">Autosave</SlMenuItem>
|
||||
<SlMenuItem type="checkbox" checked>
|
||||
Check Spelling
|
||||
</SlMenuItem>
|
||||
<SlMenuItem type="checkbox">Word Wrap</SlMenuItem>
|
||||
</SlMenu>
|
||||
);
|
||||
```
|
||||
|
||||
### 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 `sl-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
|
||||
<sl-menu class="menu-value" style="max-width: 200px;">
|
||||
<sl-menu-item value="opt-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="opt-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="opt-3">Option 3</sl-menu-item>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item type="checkbox" value="opt-4">Checkbox 4</sl-menu-item>
|
||||
<sl-menu-item type="checkbox" value="opt-5">Checkbox 5</sl-menu-item>
|
||||
<sl-menu-item type="checkbox" value="opt-6">Checkbox 6</sl-menu-item>
|
||||
</sl-menu>
|
||||
|
||||
<script>
|
||||
const menu = document.querySelector('.menu-value');
|
||||
|
||||
menu.addEventListener('sl-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>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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 (
|
||||
<SlMenu style={{ maxWidth: '200px' }} onSlSelect={handleSelect}>
|
||||
<SlMenuItem value="opt-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="opt-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="opt-3">Option 3</SlMenuItem>
|
||||
</SlMenu>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
[component-metadata:sl-menu-item]
|
||||
|
||||
@@ -2,17 +2,13 @@
|
||||
|
||||
[component-header:sl-menu-label]
|
||||
|
||||
Menu labels are used to describe a group of menu items.
|
||||
|
||||
```html preview
|
||||
<sl-menu
|
||||
style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); border-radius: var(--sl-border-radius-medium);"
|
||||
>
|
||||
<sl-menu style="max-width: 200px;">
|
||||
<sl-menu-label>Fruits</sl-menu-label>
|
||||
<sl-menu-item value="apple">Apple</sl-menu-item>
|
||||
<sl-menu-item value="banana">Banana</sl-menu-item>
|
||||
<sl-menu-item value="orange">Orange</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-label>Vegetables</sl-menu-label>
|
||||
<sl-menu-item value="broccoli">Broccoli</sl-menu-item>
|
||||
<sl-menu-item value="carrot">Carrot</sl-menu-item>
|
||||
@@ -20,4 +16,22 @@ Menu labels are used to describe a group of menu items.
|
||||
</sl-menu>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider, SlMenu, SlMenuLabel, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlMenu style={{ maxWidth: '200px' }}>
|
||||
<SlMenuLabel>Fruits</SlMenuLabel>
|
||||
<SlMenuItem value="apple">Apple</SlMenuItem>
|
||||
<SlMenuItem value="banana">Banana</SlMenuItem>
|
||||
<SlMenuItem value="orange">Orange</SlMenuItem>
|
||||
<SlDivider />
|
||||
<SlMenuLabel>Vegetables</SlMenuLabel>
|
||||
<SlMenuItem value="broccoli">Broccoli</SlMenuItem>
|
||||
<SlMenuItem value="carrot">Carrot</SlMenuItem>
|
||||
<SlMenuItem value="zucchini">Zucchini</SlMenuItem>
|
||||
</SlMenu>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-menu-label]
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
|
||||
[component-header:sl-menu]
|
||||
|
||||
Menus provide a list of options for the user to choose from.
|
||||
|
||||
You can use [menu items](/components/menu-item.md), [menu dividers](/components/menu-divider.md), and [menu labels](/components/menu-label.md) to compose a menu.
|
||||
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
|
||||
<sl-menu style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); border-radius: var(--sl-border-radius-medium);">
|
||||
<sl-menu style="max-width: 200px;">
|
||||
<sl-menu-item value="undo">Undo</sl-menu-item>
|
||||
<sl-menu-item value="redo">Redo</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item value="cut">Cut</sl-menu-item>
|
||||
<sl-menu-item value="copy">Copy</sl-menu-item>
|
||||
<sl-menu-item value="paste">Paste</sl-menu-item>
|
||||
@@ -18,6 +16,22 @@ You can use [menu items](/components/menu-item.md), [menu dividers](/components/
|
||||
</sl-menu>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlMenu style={{ maxWidth: '200px' }}>
|
||||
<SlMenuItem value="undo">Undo</SlMenuItem>
|
||||
<SlMenuItem value="redo">Redo</SlMenuItem>
|
||||
<SlDivider />
|
||||
<SlMenuItem value="cut">Cut</SlMenuItem>
|
||||
<SlMenuItem value="copy">Copy</SlMenuItem>
|
||||
<SlMenuItem value="paste">Paste</SlMenuItem>
|
||||
<SlMenuItem value="delete">Delete</SlMenuItem>
|
||||
</SlMenu>
|
||||
);
|
||||
```
|
||||
|
||||
?> 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.
|
||||
|
||||
[component-metadata:sl-menu]
|
||||
|
||||
191
docs/components/mutation-observer.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Mutation Observer
|
||||
|
||||
[component-header:sl-mutation-observer]
|
||||
|
||||
The mutation observer will report changes to the content it wraps through the `sl-mutation` event. When emitted, a collection of [MutationRecord](https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord) objects will be attached to `event.detail` that contains information about how it changed.
|
||||
|
||||
```html preview
|
||||
<div class="mutation-overview">
|
||||
<sl-mutation-observer attr="variant">
|
||||
<sl-button variant="primary">Click to mutate</sl-button>
|
||||
</sl-mutation-observer>
|
||||
|
||||
<br />
|
||||
👆 Click the button and watch the console
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.mutation-overview');
|
||||
const mutationObserver = container.querySelector('sl-mutation-observer');
|
||||
const button = container.querySelector('sl-button');
|
||||
const variants = ['primary', 'success', 'neutral', 'warning', 'danger'];
|
||||
let clicks = 0;
|
||||
|
||||
// Change the button's variant attribute
|
||||
button.addEventListener('click', () => {
|
||||
clicks++;
|
||||
button.setAttribute('variant', variants[clicks % variants.length]);
|
||||
});
|
||||
|
||||
// Log mutations
|
||||
mutationObserver.addEventListener('sl-mutation', event => {
|
||||
console.log(event.detail);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.mutation-overview sl-button {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlMutationObserver } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.resize-observer-overview div {
|
||||
display: flex;
|
||||
border: solid 2px var(--sl-input-border-color);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const variants = ['primary', 'success', 'neutral', 'warning', 'danger'];
|
||||
let clicks = 0;
|
||||
|
||||
const App = () => {
|
||||
const [variant, setVariant] = useState('primary');
|
||||
|
||||
function handleClick() {
|
||||
clicks++;
|
||||
setVariant(variants[clicks % variants.length]);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlMutationObserver attr="*" onSlMutation={event => console.log(event.detail)}>
|
||||
<SlButton variant={variant} onClick={handleClick}>
|
||||
Click to mutate
|
||||
</SlButton>
|
||||
</SlMutationObserver>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
?> When you create a mutation observer, you must indicate what changes it should respond to by including at least one of `attr`, `child-list`, or `char-data`. If you don't specify at least one of these attributes, no mutation events will be emitted.
|
||||
|
||||
## Examples
|
||||
|
||||
### Child List
|
||||
|
||||
Use the `child-list` attribute to watch for new child elements that are added or removed.
|
||||
|
||||
```html preview
|
||||
<div class="mutation-child-list">
|
||||
<sl-mutation-observer child-list>
|
||||
<div class="buttons">
|
||||
<sl-button variant="primary">Add button</sl-button>
|
||||
</div>
|
||||
</sl-mutation-observer>
|
||||
|
||||
👆 Add and remove buttons and watch the console
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.mutation-child-list');
|
||||
const mutationObserver = container.querySelector('sl-mutation-observer');
|
||||
const buttons = container.querySelector('.buttons');
|
||||
const button = container.querySelector('sl-button[variant="primary"]');
|
||||
let i = 0;
|
||||
|
||||
// Add a button
|
||||
button.addEventListener('click', () => {
|
||||
const button = document.createElement('sl-button');
|
||||
button.textContent = ++i;
|
||||
buttons.append(button);
|
||||
});
|
||||
|
||||
// Remove a button
|
||||
buttons.addEventListener('click', event => {
|
||||
const target = event.target.closest('sl-button:not([variant="primary"])');
|
||||
event.stopPropagation();
|
||||
|
||||
if (target) {
|
||||
target.remove();
|
||||
}
|
||||
});
|
||||
|
||||
// Log mutations
|
||||
mutationObserver.addEventListener('sl-mutation', event => {
|
||||
console.log(event.detail);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.mutation-child-list .buttons {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlMutationObserver } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.mutation-child-list .buttons {
|
||||
display: flex;
|
||||
gap: .25rem;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
let buttonCount = 0;
|
||||
|
||||
const App = () => {
|
||||
const [buttonIds, setButtonIds] = useState([]);
|
||||
|
||||
function addButton() {
|
||||
setButtonIds([...buttonIds, ++buttonCount]);
|
||||
}
|
||||
|
||||
function removeButton(id) {
|
||||
setButtonIds(buttonIds.filter(i => i !== id));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mutation-child-list">
|
||||
<SlMutationObserver child-list onSlMutation={event => console.log(event.detail)}>
|
||||
<div className="buttons">
|
||||
<SlButton variant="primary" onClick={addButton}>
|
||||
Add button
|
||||
</SlButton>
|
||||
{buttonIds.map(id => (
|
||||
<SlButton key={id} variant="default" onClick={() => removeButton(id)}>
|
||||
{id}
|
||||
</SlButton>
|
||||
))}
|
||||
</div>
|
||||
</SlMutationObserver>
|
||||
</div>
|
||||
👆 Add and remove buttons and watch the console
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
[component-metadata:sl-mutation-observer]
|
||||
79
docs/components/option.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Option
|
||||
|
||||
[component-header:sl-option]
|
||||
|
||||
```html preview
|
||||
<sl-select label="Select one">
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable an option and prevent it from being selected.
|
||||
|
||||
```html preview
|
||||
<sl-select label="Select one">
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2" disabled>Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2" disabled>
|
||||
Option 2
|
||||
</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix & Suffix
|
||||
|
||||
Add icons to the start and end of menu items using the `prefix` and `suffix` slots.
|
||||
|
||||
```html preview
|
||||
<sl-select label="Select one">
|
||||
<sl-option value="option-1">
|
||||
<sl-icon slot="prefix" name="envelope"></sl-icon>
|
||||
Email
|
||||
<sl-icon slot="suffix" name="patch-check"></sl-icon>
|
||||
</sl-option>
|
||||
|
||||
<sl-option value="option-2">
|
||||
<sl-icon slot="prefix" name="telephone"></sl-icon>
|
||||
Phone
|
||||
<sl-icon slot="suffix" name="patch-check"></sl-icon>
|
||||
</sl-option>
|
||||
|
||||
<sl-option value="option-3">
|
||||
<sl-icon slot="prefix" name="chat-dots"></sl-icon>
|
||||
Chat
|
||||
<sl-icon slot="suffix" name="patch-check"></sl-icon>
|
||||
</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
[component-metadata:sl-option]
|
||||
1508
docs/components/popup.md
Normal file
@@ -2,59 +2,121 @@
|
||||
|
||||
[component-header:sl-progress-bar]
|
||||
|
||||
Progress bars are used to show the status of an ongoing operation.
|
||||
|
||||
```html preview
|
||||
<sl-progress-bar percentage="50"></sl-progress-bar>
|
||||
<sl-progress-bar value="50"></sl-progress-bar>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlProgressBar value={50} />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Labels
|
||||
|
||||
Use the `label` attribute to label the progress bar and tell assistive devices how to announce it.
|
||||
|
||||
```html preview
|
||||
<sl-progress-bar value="50" label="Upload progress"></sl-progress-bar>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlProgressBar value="50" label="Upload progress" />;
|
||||
```
|
||||
|
||||
### Custom Height
|
||||
|
||||
Use the `--height` custom property to set the progress bar's height.
|
||||
|
||||
```html preview
|
||||
<sl-progress-bar percentage="50" style="--height: 6px;"></sl-progress-bar>
|
||||
<sl-progress-bar value="50" style="--height: 6px;"></sl-progress-bar>
|
||||
```
|
||||
|
||||
### Labels
|
||||
```jsx react
|
||||
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
Use the default slot to show a label.
|
||||
const App = () => <SlProgressBar value={50} style={{ '--height': '6px' }} />;
|
||||
```
|
||||
|
||||
### Showing Values
|
||||
|
||||
Use the default slot to show a value.
|
||||
|
||||
```html preview
|
||||
<sl-progress-bar percentage="50" class="progress-bar-labels">50%</sl-progress-bar>
|
||||
<sl-progress-bar value="50" class="progress-bar-values">50%</sl-progress-bar>
|
||||
|
||||
<br>
|
||||
<br />
|
||||
|
||||
<sl-button circle><sl-icon name="dash"></sl-icon></sl-button>
|
||||
<sl-button circle><sl-icon name="plus"></sl-icon></sl-button>
|
||||
<sl-button circle><sl-icon name="dash" label="Decrease"></sl-icon></sl-button>
|
||||
<sl-button circle><sl-icon name="plus" label="Increase"></sl-icon></sl-button>
|
||||
|
||||
<script>
|
||||
const progressBar = document.querySelector('.progress-bar-labels');
|
||||
const progressBar = document.querySelector('.progress-bar-values');
|
||||
const subtractButton = progressBar.nextElementSibling.nextElementSibling;
|
||||
const addButton = subtractButton.nextElementSibling;
|
||||
|
||||
addButton.addEventListener('click', () => {
|
||||
const percentage = Math.min(100, progressBar.percentage + 10);
|
||||
progressBar.percentage = percentage;
|
||||
progressBar.textContent = `${percentage}%`;
|
||||
const value = Math.min(100, progressBar.value + 10);
|
||||
progressBar.value = value;
|
||||
progressBar.textContent = `${value}%`;
|
||||
});
|
||||
|
||||
subtractButton.addEventListener('click', () => {
|
||||
const percentage = Math.max(0, progressBar.percentage - 10)
|
||||
progressBar.percentage = percentage;
|
||||
progressBar.textContent = `${percentage}%`;
|
||||
const value = Math.max(0, progressBar.value - 10);
|
||||
progressBar.value = value;
|
||||
progressBar.textContent = `${value}%`;
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlIcon, SlProgressBar } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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 (
|
||||
<>
|
||||
<SlProgressBar value={value}>{value}%</SlProgressBar>
|
||||
|
||||
<br />
|
||||
|
||||
<SlButton circle onClick={() => adjustValue(-10)}>
|
||||
<SlIcon name="dash" label="Decrease" />
|
||||
</SlButton>
|
||||
|
||||
<SlButton circle onClick={() => adjustValue(10)}>
|
||||
<SlIcon name="plus" label="Increase" />
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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, `percentage` is ignored and the label, if present, will not be shown.
|
||||
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
|
||||
<sl-progress-bar indeterminate></sl-progress-bar>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlProgressBar indeterminate />;
|
||||
```
|
||||
|
||||
[component-metadata:sl-progress-bar]
|
||||
|
||||
@@ -2,28 +2,44 @@
|
||||
|
||||
[component-header:sl-progress-ring]
|
||||
|
||||
Progress rings are used to show the progress of a determinate operation in a circular fashion.
|
||||
|
||||
```html preview
|
||||
<sl-progress-ring percentage="50"></sl-progress-ring>
|
||||
<sl-progress-ring value="25"></sl-progress-ring>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlProgressRing value="25" />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Size
|
||||
|
||||
Use the `size` attribute to set the diameter of the progress ring.
|
||||
Use the `--size` custom property to set the diameter of the progress ring.
|
||||
|
||||
```html preview
|
||||
<sl-progress-ring percentage="50" size="200"></sl-progress-ring>
|
||||
<sl-progress-ring value="50" style="--size: 200px;"></sl-progress-ring>
|
||||
```
|
||||
|
||||
### Stroke Width
|
||||
```jsx react
|
||||
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
Use the `stroke-width` attribute to set the width of the progress ring's indicator.
|
||||
const App = () => <SlProgressRing value="50" style={{ '--size': '200px' }} />;
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-progress-ring percentage="50" stroke-width="10"></sl-progress-ring>
|
||||
<sl-progress-ring value="50" style="--track-width: 6px; --indicator-width: 12px;"></sl-progress-ring>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlProgressRing value="50" style={{ '--track-width': '6px', '--indicator-width': '12px' }} />;
|
||||
```
|
||||
|
||||
### Colors
|
||||
@@ -31,41 +47,106 @@ Use the `stroke-width` attribute to set the width of the progress ring's indicat
|
||||
To change the color, use the `--track-color` and `--indicator-color` custom properties.
|
||||
|
||||
```html preview
|
||||
<sl-progress-ring
|
||||
percentage="50"
|
||||
style="--track-color: #ffe2c6; --indicator-color: tomato;"
|
||||
<sl-progress-ring
|
||||
value="50"
|
||||
style="
|
||||
--track-color: pink;
|
||||
--indicator-color: deeppink;
|
||||
"
|
||||
></sl-progress-ring>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlProgressRing
|
||||
value="50"
|
||||
style={{
|
||||
'--track-color': 'pink',
|
||||
'--indicator-color': 'deeppink'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### Labels
|
||||
|
||||
Use the default slot to show a label.
|
||||
Use the `label` attribute to label the progress ring and tell assistive devices how to announce it.
|
||||
|
||||
```html preview
|
||||
<sl-progress-ring percentage="50" size="200" class="progress-ring-labels" style="margin-bottom: .5rem;">50%</sl-progress-ring>
|
||||
<sl-progress-ring value="50" label="Upload progress"></sl-progress-ring>
|
||||
```
|
||||
|
||||
<br>
|
||||
```jsx react
|
||||
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
<sl-button circle><sl-icon name="dash"></sl-icon></sl-button>
|
||||
<sl-button circle><sl-icon name="plus"></sl-icon></sl-button>
|
||||
const App = () => <SlProgressRing value="50" label="Upload progress" />;
|
||||
```
|
||||
|
||||
### Showing Values
|
||||
|
||||
Use the default slot to show a label inside the progress ring.
|
||||
|
||||
```html preview
|
||||
<sl-progress-ring value="50" class="progress-ring-values" style="margin-bottom: .5rem;">50%</sl-progress-ring>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-button circle><sl-icon name="dash" label="Decrease"></sl-icon></sl-button>
|
||||
<sl-button circle><sl-icon name="plus" label="Increase"></sl-icon></sl-button>
|
||||
|
||||
<script>
|
||||
const progressRing = document.querySelector('.progress-ring-labels');
|
||||
const progressRing = document.querySelector('.progress-ring-values');
|
||||
const subtractButton = progressRing.nextElementSibling.nextElementSibling;
|
||||
const addButton = subtractButton.nextElementSibling;
|
||||
|
||||
addButton.addEventListener('click', () => {
|
||||
const percentage = Math.min(100, progressRing.percentage + 10);
|
||||
progressRing.percentage = percentage;
|
||||
progressRing.textContent = `${percentage}%`;
|
||||
const value = Math.min(100, progressRing.value + 10);
|
||||
progressRing.value = value;
|
||||
progressRing.textContent = `${value}%`;
|
||||
});
|
||||
|
||||
subtractButton.addEventListener('click', () => {
|
||||
const percentage = Math.max(0, progressRing.percentage - 10)
|
||||
progressRing.percentage = percentage;
|
||||
progressRing.textContent = `${percentage}%`;
|
||||
const value = Math.max(0, progressRing.value - 10);
|
||||
progressRing.value = value;
|
||||
progressRing.textContent = `${value}%`;
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlIcon, SlProgressRing } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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 (
|
||||
<>
|
||||
<SlProgressRing value={value} style={{ marginBottom: '.5rem' }}>
|
||||
{value}%
|
||||
</SlProgressRing>
|
||||
|
||||
<br />
|
||||
|
||||
<SlButton circle onClick={() => adjustValue(-10)}>
|
||||
<SlIcon name="dash" label="Decrease" />
|
||||
</SlButton>
|
||||
|
||||
<SlButton circle onClick={() => adjustValue(10)}>
|
||||
<SlIcon name="plus" label="Increase" />
|
||||
</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
[component-metadata:sl-progress-ring]
|
||||
|
||||
@@ -2,40 +2,112 @@
|
||||
|
||||
[component-header:sl-qr-code]
|
||||
|
||||
Generates a [QR code](https://www.qrcode.com/) and renders it using the [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API).
|
||||
|
||||
QR codes are useful for providing small pieces of information to users who can quickly scan them with a smartphone. Most smartphones have built-in QR code scanners, so simply pointing the camera at a QR code will decode it and allow the user to visit a website, dial a phone number, read a message, etc.
|
||||
|
||||
```html preview
|
||||
<sl-qr-code value="https://shoelace.style/" label="Scan this code to visit Shoelace on the web!"></sl-qr-code>
|
||||
<div class="qr-overview">
|
||||
<sl-qr-code value="https://shoelace.style/" label="Scan this code to visit Shoelace on the web!"></sl-qr-code>
|
||||
<br />
|
||||
|
||||
<sl-input maxlength="255" clearable label="Value"></sl-input>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.qr-overview');
|
||||
const qrCode = container.querySelector('sl-qr-code');
|
||||
const input = container.querySelector('sl-input');
|
||||
|
||||
input.value = qrCode.value;
|
||||
input.addEventListener('sl-input', () => (qrCode.value = input.value));
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.qr-overview {
|
||||
max-width: 256px;
|
||||
}
|
||||
|
||||
.qr-overview sl-input {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlQrCode, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.qr-overview {
|
||||
max-width: 256px;
|
||||
}
|
||||
|
||||
.qr-overview sl-input {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => {
|
||||
const [value, setValue] = useState('https://shoelace.style/');
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="qr-overview">
|
||||
<SlQrCode value={value} label="Scan this code to visit Shoelace on the web!" />
|
||||
<br />
|
||||
|
||||
<SlInput maxlength="255" clearable onInput={event => setValue(event.target.value)} />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Colors
|
||||
|
||||
Use the `fill` and `background` props to modify the QR code's colors. You should always ensure good contrast for optimal compatibility with QR code scanners.
|
||||
Use the `fill` and `background` attributes to modify the QR code's colors. You should always ensure good contrast for optimal compatibility with QR code scanners.
|
||||
|
||||
```html preview
|
||||
<sl-qr-code value="https://shoelace.style/" fill="deeppink" background="white"></sl-qr-code>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlQrCode value="https://shoelace.style/" fill="deeppink" background="white" />;
|
||||
```
|
||||
|
||||
### Size
|
||||
|
||||
Use the `size` prop to change the size of the QR code.
|
||||
Use the `size` attribute to change the size of the QR code.
|
||||
|
||||
```html preview
|
||||
<sl-qr-code value="https://shoelace.style/" size="64"></sl-qr-code>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlQrCode value="https://shoelace.style/" size="64" />;
|
||||
```
|
||||
|
||||
### Radius
|
||||
|
||||
Create a rounded effect with the `radius` prop.
|
||||
Create a rounded effect with the `radius` attribute.
|
||||
|
||||
```html preview
|
||||
<sl-qr-code value="https://shoelace.style/" radius="0.5"></sl-qr-code>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlQrCode value="https://shoelace.style/" radius="0.5" />;
|
||||
```
|
||||
|
||||
### Error Correction
|
||||
|
||||
QR codes can be rendered with various levels of [error correction](https://www.qrcode.com/en/about/error_correction.html) that can be set using the `error-correction` attribute. This example generates four codes with the same value using different error correction levels.
|
||||
@@ -57,4 +129,31 @@ QR codes can be rendered with various levels of [error correction](https://www.q
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.qr-error-correction {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="qr-error-correction">
|
||||
<SlQrCode value="https://shoelace.style/" error-correction="L" />
|
||||
<SlQrCode value="https://shoelace.style/" error-correction="M" />
|
||||
<SlQrCode value="https://shoelace.style/" error-correction="Q" />
|
||||
<SlQrCode value="https://shoelace.style/" error-correction="H" />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
[component-metadata:sl-qr-code]
|
||||
|
||||
295
docs/components/radio-button.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# Radio Button
|
||||
|
||||
[component-header:sl-radio-button]
|
||||
|
||||
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
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio-button value="1">Option 1</sl-radio-button>
|
||||
<sl-radio-button value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadioButton value="1">Option 1</SlRadioButton>
|
||||
<SlRadioButton value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Checked States
|
||||
|
||||
To set the initial value and checked state, use the `value` attribute on the containing radio group.
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio-button value="1">Option 1</sl-radio-button>
|
||||
<sl-radio-button value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadioButton value="1">Option 1</SlRadioButton>
|
||||
<SlRadioButton value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable a radio button.
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio-button value="1">Option 1</sl-radio-button>
|
||||
<sl-radio-button value="2" disabled>Option 2</sl-radio-button>
|
||||
<sl-radio-button value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadioButton value="1">Option 1</SlRadioButton>
|
||||
<SlRadioButton value="2" disabled>
|
||||
Option 2
|
||||
</SlRadioButton>
|
||||
<SlRadioButton value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a radio button's size.
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio-button size="small" value="1">Option 1</sl-radio-button>
|
||||
<sl-radio-button size="small" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button size="small" value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio-button size="medium" value="1">Option 1</sl-radio-button>
|
||||
<sl-radio-button size="medium" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button size="medium" value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio-button size="large" value="1">Option 1</sl-radio-button>
|
||||
<sl-radio-button size="large" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button size="large" value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadioButton size="small" value="1">Option 1</SlRadioButton>
|
||||
<SlRadioButton size="small" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton size="small" value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadioButton size="medium" value="1">Option 1</SlRadioButton>
|
||||
<SlRadioButton size="medium" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton size="medium" value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadioButton size="large" value="1">Option 1</SlRadioButton>
|
||||
<SlRadioButton size="large" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton size="large" value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill Buttons
|
||||
|
||||
Use the `pill` attribute to give radio buttons rounded edges.
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio-button pill size="small" value="1">Option 1</sl-radio-button>
|
||||
<sl-radio-button pill size="small" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button pill size="small" value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio-button pill size="medium" value="1">Option 1</sl-radio-button>
|
||||
<sl-radio-button pill size="medium" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button pill size="medium" value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio-button pill size="large" value="1">Option 1</sl-radio-button>
|
||||
<sl-radio-button pill size="large" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button pill size="large" value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadioButton pill size="small" value="1">Option 1</SlRadioButton>
|
||||
<SlRadioButton pill size="small" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton pill size="small" value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadioButton pill size="medium" value="1">Option 1</SlRadioButton>
|
||||
<SlRadioButton pill size="medium" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton pill size="medium" value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadioButton pill size="large" value="1">Option 1</SlRadioButton>
|
||||
<SlRadioButton pill size="large" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton pill size="large" value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix and Suffix Icons
|
||||
|
||||
Use the `prefix` and `suffix` slots to add icons.
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio-button value="1">
|
||||
<sl-icon slot="prefix" name="archive"></sl-icon>
|
||||
Option 1
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button value="2">
|
||||
<sl-icon slot="suffix" name="bag"></sl-icon>
|
||||
Option 2
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button value="3">
|
||||
<sl-icon slot="prefix" name="gift"></sl-icon>
|
||||
<sl-icon slot="suffix" name="cart"></sl-icon>
|
||||
Option 3
|
||||
</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIcon, SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadioButton value="1">
|
||||
<SlIcon slot="prefix" name="archive" />
|
||||
Option 1
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton value="2">
|
||||
<SlIcon slot="suffix" name="bag" />
|
||||
Option 2
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton value="3">
|
||||
<SlIcon slot="prefix" name="gift" />
|
||||
<SlIcon slot="suffix" name="cart" />
|
||||
Option 3
|
||||
</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-radio-group label="Select an option" name="a" value="neutral">
|
||||
<sl-radio-button value="angry">
|
||||
<sl-icon name="emoji-angry" label="Angry"></sl-icon>
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button value="sad">
|
||||
<sl-icon name="emoji-frown" label="Sad"></sl-icon>
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button value="neutral">
|
||||
<sl-icon name="emoji-neutral" label="Neutral"></sl-icon>
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button value="happy">
|
||||
<sl-icon name="emoji-smile" label="Happy"></sl-icon>
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button value="laughing">
|
||||
<sl-icon name="emoji-laughing" label="Laughing"></sl-icon>
|
||||
</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIcon, SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="neutral">
|
||||
<SlRadioButton value="angry">
|
||||
<SlIcon name="emoji-angry" label="Angry" />
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton value="sad">
|
||||
<SlIcon name="emoji-frown" label="Sad" />
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton value="neutral">
|
||||
<SlIcon name="emoji-neutral" label="Neutral" />
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton value="happy">
|
||||
<SlIcon name="emoji-smile" label="Happy" />
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton value="laughing">
|
||||
<SlIcon name="emoji-laughing" label="Laughing" />
|
||||
</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-radio-button]
|
||||
@@ -2,28 +2,231 @@
|
||||
|
||||
[component-header:sl-radio-group]
|
||||
|
||||
Radio Groups are used to group multiple radios so they function as a single control.
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an item">
|
||||
<sl-radio value="1" checked>Item 1</sl-radio>
|
||||
<sl-radio value="2">Item 2</sl-radio>
|
||||
<sl-radio value="3">Item 3</sl-radio>
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio value="1">Option 1</sl-radio>
|
||||
<sl-radio value="2">Option 2</sl-radio>
|
||||
<sl-radio value="3">Option 3</sl-radio>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadio value="1">Option 1</SlRadio>
|
||||
<SlRadio value="2">Option 2</SlRadio>
|
||||
<SlRadio value="3">Option 3</SlRadio>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Hiding the Fieldset
|
||||
### Help Text
|
||||
|
||||
You can hide the fieldset and legend that wraps the radio group using the `no-fieldset` attribute. In this case, a label is still required for assistive devices to properly identify the control.
|
||||
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
|
||||
<sl-radio-group label="Select an item" no-fieldset>
|
||||
<sl-radio value="1" checked>Item 1</sl-radio>
|
||||
<sl-radio value="2">Item 2</sl-radio>
|
||||
<sl-radio value="3">Item 3</sl-radio>
|
||||
<sl-radio-group label="Select an option" help-text="Choose the most appropriate option." name="a" value="1">
|
||||
<sl-radio value="1">Option 1</sl-radio>
|
||||
<sl-radio value="2">Option 2</sl-radio>
|
||||
<sl-radio value="3">Option 3</sl-radio>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" help-text="Choose the most appropriate option." name="a" value="1">
|
||||
<SlRadio value="1">Option 1</SlRadio>
|
||||
<SlRadio value="2">Option 2</SlRadio>
|
||||
<SlRadio value="3">Option 3</SlRadio>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-radio-group label="Select an option" help-text="Select an option that makes you proud." name="a" value="1">
|
||||
<sl-radio-button value="1">Option 1</sl-radio-button>
|
||||
<sl-radio-button value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadioButton value="1">Option 1</SlRadioButton>
|
||||
<SlRadioButton value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabling Options
|
||||
|
||||
Radios and radio buttons can be disabled by adding the `disabled` attribute to the respective options inside the radio group.
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio value="1">Option 1</sl-radio>
|
||||
<sl-radio value="2" disabled>Option 2</sl-radio>
|
||||
<sl-radio value="3">Option 3</sl-radio>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadio value="1">Option 1</SlRadio>
|
||||
<SlRadio value="2" disabled>
|
||||
Option 2
|
||||
</SlRadio>
|
||||
<SlRadio value="3">Option 3</SlRadio>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### 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">
|
||||
<sl-radio-group label="Select an option" name="a" required>
|
||||
<sl-radio value="1">Option 1</sl-radio>
|
||||
<sl-radio value="2">Option 2</sl-radio>
|
||||
<sl-radio value="3">Option 3</sl-radio>
|
||||
</sl-radio-group>
|
||||
<br />
|
||||
<sl-button type="submit" variant="primary">Submit</sl-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 { SlButton, SlIcon, SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
const App = () => {
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
}
|
||||
|
||||
return (
|
||||
<form class="custom-validity" onSubmit={handleSubmit}>
|
||||
<SlRadioGroup label="Select an option" name="a" required onSlChange={handleChange}>
|
||||
<SlRadio value="1">
|
||||
Option 1
|
||||
</SlRadio>
|
||||
<SlRadiovalue="2">
|
||||
Option 2
|
||||
</SlRadio>
|
||||
<SlRadio value="3">
|
||||
Option 3
|
||||
</SlRadio>
|
||||
</SlRadioGroup>
|
||||
<br />
|
||||
<SlButton type="submit" variant="primary">
|
||||
Submit
|
||||
</SlButton>
|
||||
</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">
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio value="1">Not me</sl-radio>
|
||||
<sl-radio value="2">Me neither</sl-radio>
|
||||
<sl-radio value="3">Choose me</sl-radio>
|
||||
</sl-radio-group>
|
||||
<br />
|
||||
<sl-button type="submit" variant="primary">Submit</sl-button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const form = document.querySelector('.custom-validity');
|
||||
const radioGroup = form.querySelector('sl-radio-group');
|
||||
const errorMessage = 'You must choose the last option';
|
||||
|
||||
// Set initial validity as soon as the element is defined
|
||||
customElements.whenDefined('sl-radio-group').then(() => {
|
||||
radioGroup.setCustomValidity(errorMessage);
|
||||
});
|
||||
|
||||
// Update validity when a selection is made
|
||||
form.addEventListener('sl-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 { SlButton, SlIcon, SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
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}>
|
||||
<SlRadioGroup ref={radioGroup} label="Select an option" name="a" value="1" onSlChange={handleChange}>
|
||||
<SlRadio value="1">Not me</SlRadio>
|
||||
<SlRadio value="2">Me neither</SlRadio>
|
||||
<SlRadio value="3">Choose me</SlRadio>
|
||||
</SlRadioGroup>
|
||||
<br />
|
||||
<SlButton type="submit" variant="primary">
|
||||
Submit
|
||||
</SlButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
[component-metadata:sl-radio-group]
|
||||
|
||||
@@ -2,33 +2,104 @@
|
||||
|
||||
[component-header:sl-radio]
|
||||
|
||||
Radios allow the user to select one option from a group of many.
|
||||
|
||||
Radios are designed to be used with [radio groups](/components/radio-group). As such, all of the examples on this page utilize them to demonstrate their correct usage.
|
||||
Radios are designed to be used with [radio groups](/components/radio-group).
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an option" no-fieldset>
|
||||
<sl-radio value="1" checked>Option 1</sl-radio>
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio value="1">Option 1</sl-radio>
|
||||
<sl-radio value="2">Option 2</sl-radio>
|
||||
<sl-radio value="3">Option 3</sl-radio>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form.md) instead.
|
||||
```jsx react
|
||||
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadio value="1">Option 1</SlRadio>
|
||||
<SlRadio value="2">Option 2</SlRadio>
|
||||
<SlRadio value="3">Option 3</SlRadio>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
?> 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
|
||||
<sl-radio-group label="Select an option" name="a" value="3">
|
||||
<sl-radio value="1">Option 1</sl-radio>
|
||||
<sl-radio value="2">Option 2</sl-radio>
|
||||
<sl-radio value="3">Option 3</sl-radio>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="3">
|
||||
<SlRadio value="1">Option 1</SlRadio>
|
||||
<SlRadio value="2">Option 2</SlRadio>
|
||||
<SlRadio value="3">Option 3</SlRadio>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable a radio.
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an option" no-fieldset>
|
||||
<sl-radio value="1" checked>Option 1</sl-radio>
|
||||
<sl-radio value="2">Option 2</sl-radio>
|
||||
<sl-radio-group label="Select an option" name="a" value="1">
|
||||
<sl-radio value="1">Option 1</sl-radio>
|
||||
<sl-radio value="2" disabled>Option 2</sl-radio>
|
||||
<sl-radio value="3">Option 3</sl-radio>
|
||||
<sl-radio value="4" disabled>Disabled</sl-radio>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option" name="a" value="1">
|
||||
<SlRadio value="1">Option 1</SlRadio>
|
||||
<SlRadio value="2" disabled>
|
||||
Option 2
|
||||
</SlRadio>
|
||||
<SlRadio value="3">Option 3</SlRadio>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Sizes
|
||||
|
||||
Use the `size` attribute to change a radio's size.
|
||||
|
||||
```html preview
|
||||
<sl-radio size="small">Small</sl-radio>
|
||||
<sl-radio size="medium">Medium</sl-radio>
|
||||
<sl-radio size="large">Large</sl-radio>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRadio } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlRadio size="small">Small</SlRadio>
|
||||
<br />
|
||||
<SlRadio size="medium">Medium</SlRadio>
|
||||
<br />
|
||||
<SlRadio size="large">Large</SlRadio>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-radio]
|
||||
|
||||
@@ -2,22 +2,74 @@
|
||||
|
||||
[component-header:sl-range]
|
||||
|
||||
Ranges allow the user to select a single value within a given range using a slider.
|
||||
|
||||
```html preview
|
||||
<sl-range min="0" max="100" step="1"></sl-range>
|
||||
<sl-range></sl-range>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form.md) instead.
|
||||
```jsx react
|
||||
import { SlRange } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRange />;
|
||||
```
|
||||
|
||||
?> 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
|
||||
|
||||
### Disabled
|
||||
### Labels
|
||||
|
||||
Use the `disabled` prop to disable a slider.
|
||||
Use the `label` attribute to give the range an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
|
||||
```html preview
|
||||
<sl-range min="0" max="100" step="1" disabled></sl-range>
|
||||
<sl-range label="Volume" min="0" max="100"></sl-range>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRange } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRange 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
|
||||
<sl-range label="Volume" help-text="Controls the volume of the current song." min="0" max="100"></sl-range>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRange } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRange 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
|
||||
<sl-range min="0" max="10" step="1"></sl-range>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRange } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRange min={0} max={10} step={1} />;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable a slider.
|
||||
|
||||
```html preview
|
||||
<sl-range disabled></sl-range>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRange } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRange disabled />;
|
||||
```
|
||||
|
||||
### Tooltip Placement
|
||||
@@ -25,7 +77,13 @@ Use the `disabled` prop to disable a slider.
|
||||
By default, the tooltip is shown on top. Set `tooltip` to `bottom` to show it below the slider.
|
||||
|
||||
```html preview
|
||||
<sl-range min="0" max="100" step="1" tooltip="bottom"></sl-range>
|
||||
<sl-range tooltip="bottom"></sl-range>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRange } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRange tooltip="bottom" />;
|
||||
```
|
||||
|
||||
### Disable the Tooltip
|
||||
@@ -33,12 +91,76 @@ By default, the tooltip is shown on top. Set `tooltip` to `bottom` to show it be
|
||||
To disable the tooltip, set `tooltip` to `none`.
|
||||
|
||||
```html preview
|
||||
<sl-range min="0" max="100" step="1" tooltip="none"></sl-range>
|
||||
<sl-range tooltip="none"></sl-range>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRange } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRange 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
|
||||
<sl-range
|
||||
style="
|
||||
--track-color-active: var(--sl-color-primary-600);
|
||||
--track-color-inactive: var(--sl-color-primary-100);
|
||||
"
|
||||
></sl-range>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRange } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRange
|
||||
style={{
|
||||
'--track-color-active': 'var(--sl-color-primary-600)',
|
||||
'--track-color-inactive': 'var(--sl-color-primary-200)'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Track Offset
|
||||
|
||||
You can customize the initial offset of the active track using the `--track-active-offset` custom property.
|
||||
|
||||
```html preview
|
||||
<sl-range
|
||||
min="-100"
|
||||
max="100"
|
||||
style="
|
||||
--track-color-active: var(--sl-color-primary-600);
|
||||
--track-color-inactive: var(--sl-color-primary-100);
|
||||
--track-active-offset: 50%;
|
||||
"
|
||||
></sl-range>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRange } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRange
|
||||
min={-100}
|
||||
max={100}
|
||||
style={{
|
||||
'--track-color-active': 'var(--sl-color-primary-600)',
|
||||
'--track-color-inactive': 'var(--sl-color-primary-200)',
|
||||
'--track-active-offset': '50%'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Tooltip Formatter
|
||||
|
||||
You can change the tooltip's content by setting the `tooltipFormatter` prop to a function that accepts the range's value as an argument.
|
||||
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
|
||||
<sl-range min="0" max="100" step="1" class="range-with-custom-formatter"></sl-range>
|
||||
@@ -49,25 +171,10 @@ You can change the tooltip's content by setting the `tooltipFormatter` prop to a
|
||||
</script>
|
||||
```
|
||||
|
||||
### Labels
|
||||
```jsx react
|
||||
import { SlRange } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
Use the `label` attribute to give the range an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
|
||||
```html preview
|
||||
<sl-range label="Volume" min="0" max="100"></sl-input>
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-range
|
||||
label="Volume"
|
||||
help-text="Controls the volume of the current song."
|
||||
min="0"
|
||||
max="100"
|
||||
></sl-input>
|
||||
const App = () => <SlRange min={0} max={100} step={1} tooltipFormatter={value => `Total - ${value}%`} />;
|
||||
```
|
||||
|
||||
[component-metadata:sl-range]
|
||||
|
||||
@@ -2,20 +2,44 @@
|
||||
|
||||
[component-header:sl-rating]
|
||||
|
||||
Ratings give users a way to quickly view and provide feedback.
|
||||
|
||||
```html preview
|
||||
<sl-rating></sl-rating>
|
||||
<sl-rating label="Rating"></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating 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
|
||||
<sl-rating label="Rate this component"></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating label="Rate this component" />;
|
||||
```
|
||||
|
||||
### Maximum Value
|
||||
|
||||
Ratings are 0-5 by default. To change the maximum possible value, use the `max` attribute.
|
||||
|
||||
```html preview
|
||||
<sl-rating max="3"></sl-rating>
|
||||
<sl-rating label="Rating" max="3"></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating label="Rating" max={3} />;
|
||||
```
|
||||
|
||||
### Precision
|
||||
@@ -23,15 +47,27 @@ Ratings are 0-5 by default. To change the maximum possible value, use the `max`
|
||||
Use the `precision` attribute to let users select fractional ratings.
|
||||
|
||||
```html preview
|
||||
<sl-rating precision=".5" value="2.5"></sl-rating>
|
||||
<sl-rating label="Rating" precision="0.5" value="2.5"></sl-rating>
|
||||
```
|
||||
|
||||
## Symbol Sizes
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating label="Rating" precision={0.5} value={2.5} />;
|
||||
```
|
||||
|
||||
### Symbol Sizes
|
||||
|
||||
Set the `--symbol-size` custom property to adjust the size.
|
||||
|
||||
```html preview
|
||||
<sl-rating style="--symbol-size: 2rem;"></sl-rating>
|
||||
<sl-rating label="Rating" style="--symbol-size: 2rem;"></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating label="Rating" style={{ '--symbol-size': '2rem' }} />;
|
||||
```
|
||||
|
||||
### Readonly
|
||||
@@ -39,7 +75,13 @@ Set the `--symbol-size` custom property to adjust the size.
|
||||
Use the `readonly` attribute to display a rating that users can't change.
|
||||
|
||||
```html preview
|
||||
<sl-rating readonly value="3"></sl-rating>
|
||||
<sl-rating label="Rating" readonly value="3"></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating label="Rating" readonly value={3} />;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
@@ -47,33 +89,159 @@ Use the `readonly` attribute to display a rating that users can't change.
|
||||
Use the `disable` attribute to disable the rating.
|
||||
|
||||
```html preview
|
||||
<sl-rating disabled value="3"></sl-rating>
|
||||
<sl-rating label="Rating" disabled value="3"></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating label="Rating" disabled value={3} />;
|
||||
```
|
||||
|
||||
### Detecting Hover
|
||||
|
||||
Use the `sl-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">
|
||||
<sl-rating label="Rating"></sl-rating>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const rating = document.querySelector('.detect-hover > sl-rating');
|
||||
const span = rating.nextElementSibling;
|
||||
const terms = ['No rating', 'Terrible', 'Bad', 'OK', 'Good', 'Excellent'];
|
||||
|
||||
rating.addEventListener('sl-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(--sl-border-radius-small);
|
||||
background: var(--sl-color-neutral-900);
|
||||
color: var(--sl-color-neutral-0);
|
||||
text-align: center;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.detect-hover span:empty {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const terms = ['No rating', 'Terrible', 'Bad', 'OK', 'Good', 'Excellent'];
|
||||
const css = `
|
||||
.detect-hover span {
|
||||
position: relative;
|
||||
top: -4px;
|
||||
left: 8px;
|
||||
border-radius: var(--sl-border-radius-small);
|
||||
background: var(--sl-color-neutral-900);
|
||||
color: var(--sl-color-neutral-0);
|
||||
text-align: center;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.detect-hover span:empty {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
function handleHover(event) {
|
||||
rating.addEventListener('sl-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">
|
||||
<SlRating label="Rating" onSlHover={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
|
||||
<sl-rating class="rating-hearts" style="--symbol-color-active: #ff4136;"></sl-rating>
|
||||
<sl-rating label="Rating" class="rating-hearts" style="--symbol-color-active: #ff4136;"></sl-rating>
|
||||
|
||||
<script>
|
||||
const rating = document.querySelector('.rating-hearts');
|
||||
rating.getSymbol = () => '<sl-icon name="heart-fill"></sl-icon>';
|
||||
rating.getSymbol = () => '<sl-icon name="heart-fill"></sl-icon>';
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRating
|
||||
label="Rating"
|
||||
getSymbol={() => '<sl-icon name="heart-fill"></sl-icon>'}
|
||||
style={{ '--symbol-color-active': '#ff4136' }}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### Value-based Icons
|
||||
|
||||
You can also use the `getSymbol` property to render different icons based on value.
|
||||
|
||||
```html preview
|
||||
<sl-rating class="rating-emojis"></sl-rating>
|
||||
<sl-rating label="Rating" class="rating-emojis"></sl-rating>
|
||||
|
||||
<script>
|
||||
const rating = document.querySelector('.rating-emojis');
|
||||
|
||||
rating.symbol = (value) => {
|
||||
rating.getSymbol = value => {
|
||||
const icons = ['emoji-angry', 'emoji-frown', 'emoji-expressionless', 'emoji-smile', 'emoji-laughing'];
|
||||
return icons[value - 1];
|
||||
return `<sl-icon name="${icons[value - 1]}"></sl-icon>`;
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
function getSymbol(value) {
|
||||
const icons = ['emoji-angry', 'emoji-frown', 'emoji-expressionless', 'emoji-smile', 'emoji-laughing'];
|
||||
return `<sl-icon name="${icons[value - 1]}"></sl-icon>`;
|
||||
}
|
||||
|
||||
const App = () => <SlRating label="Rating" getSymbol={getSymbol} />;
|
||||
```
|
||||
|
||||
[component-metadata:sl-rating]
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
[component-header:sl-relative-time]
|
||||
|
||||
Outputs a localized time phrase relative to the current date and time.
|
||||
|
||||
Localization is handled by the browser's [`Intl.RelativeTimeFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat). No language packs are required.
|
||||
|
||||
```html preview
|
||||
@@ -11,12 +9,16 @@ Localization is handled by the browser's [`Intl.RelativeTimeFormat` API](https:/
|
||||
<sl-relative-time date="2020-07-15T09:17:00-04:00"></sl-relative-time>
|
||||
```
|
||||
|
||||
The `date` prop determines when the date/time is calculated from. It must be a string that [`Date.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) can interpret or a [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object set via JavaScript.
|
||||
```jsx react
|
||||
import { SlRelativeTime } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRelativeTime date="2020-07-15T09:17:00-04:00" />;
|
||||
```
|
||||
|
||||
The `date` attribute determines when the date/time is calculated from. It must be a string that [`Date.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) can interpret or a [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object set via JavaScript.
|
||||
|
||||
?> When using strings, avoid ambiguous dates such as `03/04/2020` which can be interpreted as March 4 or April 3 depending on the user's browser and locale. Instead, always use a valid [ISO 8601 date time string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse#Date_Time_String_Format) to ensure the date will be parsed properly by all clients.
|
||||
|
||||
!> The `Intl.RelativeTimeFormat` API is available [in all major browsers](https://caniuse.com/mdn-javascript_builtins_intl_relativetimeformat), but it only became available to Safari in version 14. If you need to support Safari 13, you'll need to [use a polyfill](https://github.com/catamphetamine/relative-time-format).
|
||||
|
||||
## Examples
|
||||
|
||||
### Keeping Time in Sync
|
||||
@@ -36,26 +38,66 @@ Use the `sync` attribute to update the displayed value automatically as time pas
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRelativeTime } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const date = new Date(new Date().getTime() - 60000);
|
||||
|
||||
const App = () => <SlRelativeTime date={date} sync />;
|
||||
```
|
||||
|
||||
### Formatting Styles
|
||||
|
||||
You can change how the time is displayed using the `format` attribute. Note that some locales may display the same values for `narrow` and `short` formats.
|
||||
|
||||
```html preview
|
||||
<sl-relative-time date="2020-07-15T09:17:00-04:00" format="narrow"></sl-relative-time><br>
|
||||
<sl-relative-time date="2020-07-15T09:17:00-04:00" format="short"></sl-relative-time><br>
|
||||
<sl-relative-time date="2020-07-15T09:17:00-04:00" format="narrow"></sl-relative-time><br />
|
||||
<sl-relative-time date="2020-07-15T09:17:00-04:00" format="short"></sl-relative-time><br />
|
||||
<sl-relative-time date="2020-07-15T09:17:00-04:00" format="long"></sl-relative-time>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRelativeTime } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlRelativeTime date="2020-07-15T09:17:00-04:00" format="narrow" />
|
||||
<br />
|
||||
<SlRelativeTime date="2020-07-15T09:17:00-04:00" format="short" />
|
||||
<br />
|
||||
<SlRelativeTime date="2020-07-15T09:17:00-04:00" format="long" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Localization
|
||||
|
||||
Use the `locale` attribute to set the desired locale.
|
||||
Use the `lang` attribute to set the desired locale.
|
||||
|
||||
```html preview
|
||||
English: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="en-US"></sl-relative-time><br>
|
||||
Chinese: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="zh-CN"></sl-relative-time><br>
|
||||
German: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="de"></sl-relative-time><br>
|
||||
Greek: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="el"></sl-relative-time><br>
|
||||
Russian: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="ru"></sl-relative-time>
|
||||
English: <sl-relative-time date="2020-07-15T09:17:00-04:00" lang="en-US"></sl-relative-time><br />
|
||||
Chinese: <sl-relative-time date="2020-07-15T09:17:00-04:00" lang="zh-CN"></sl-relative-time><br />
|
||||
German: <sl-relative-time date="2020-07-15T09:17:00-04:00" lang="de"></sl-relative-time><br />
|
||||
Greek: <sl-relative-time date="2020-07-15T09:17:00-04:00" lang="el"></sl-relative-time><br />
|
||||
Russian: <sl-relative-time date="2020-07-15T09:17:00-04:00" lang="ru"></sl-relative-time>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRelativeTime } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
English: <SlRelativeTime date="2020-07-15T09:17:00-04:00" lang="en-US" />
|
||||
<br />
|
||||
Chinese: <SlRelativeTime date="2020-07-15T09:17:00-04:00" lang="zh-CN" />
|
||||
<br />
|
||||
German: <SlRelativeTime date="2020-07-15T09:17:00-04:00" lang="de" />
|
||||
<br />
|
||||
Greek: <SlRelativeTime date="2020-07-15T09:17:00-04:00" lang="el" />
|
||||
<br />
|
||||
Russian: <SlRelativeTime date="2020-07-15T09:17:00-04:00" lang="ru" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-relative-time]
|
||||
|
||||
@@ -2,16 +2,12 @@
|
||||
|
||||
[component-header:sl-resize-observer]
|
||||
|
||||
Resize observers offer a thin, declarative interface to the [`ResizeObserver API`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).
|
||||
|
||||
The resize observer will report changes to the dimensions of the elements it wraps through the `sl-resize` event. When emitted, a collection of [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry) objects will be attached to `event.detail`, containing the target element and information about its dimensions.
|
||||
The resize observer will report changes to the dimensions of the elements it wraps through the `sl-resize` event. When emitted, a collection of [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry) objects will be attached to `event.detail` that contains the target element and information about its dimensions.
|
||||
|
||||
```html preview
|
||||
<div class="resize-observer-overview">
|
||||
<sl-resize-observer>
|
||||
<div>
|
||||
Resize this box and watch the console 👉
|
||||
</div>
|
||||
<div>Resize this box and watch the console 👉</div>
|
||||
</sl-resize-observer>
|
||||
</div>
|
||||
|
||||
@@ -20,11 +16,26 @@ The resize observer will report changes to the dimensions of the elements it wra
|
||||
const resizeObserver = container.querySelector('sl-resize-observer');
|
||||
|
||||
resizeObserver.addEventListener('sl-resize', event => {
|
||||
console.log(event);
|
||||
console.log(event.detail);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.resize-observer-overview div {
|
||||
display: flex;
|
||||
border: solid 2px var(--sl-input-border-color);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlResizeObserver } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.resize-observer-overview div {
|
||||
display: flex;
|
||||
border: solid 2px var(--sl-input-border-color);
|
||||
@@ -33,7 +44,19 @@ The resize observer will report changes to the dimensions of the elements it wra
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="resize-observer-overview">
|
||||
<SlResizeObserver onSlResize={event => console.log(event.detail)}>
|
||||
<div>Resize this box and watch the console 👉</div>
|
||||
</SlResizeObserver>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-resize-observer]
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
# Responsive Media
|
||||
|
||||
[component-header:sl-responsive-media]
|
||||
|
||||
Displays media in the desired aspect ratio.
|
||||
|
||||
You can slot in any [replaced element](https://developer.mozilla.org/en-US/docs/Web/CSS/Replaced_element), including `<iframe>`, `<img>`, and `<video>`. As the element's width changes, its height will resize proportionally. Only one element should be slotted into the container. The default aspect ratio is `16:9`.
|
||||
|
||||
```html preview
|
||||
<sl-responsive-media>
|
||||
<img src="https://images.unsplash.com/photo-1541427468627-a89a96e5ca1d?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1800&q=80" alt="A train riding through autumn foliage with mountains in the distance.">
|
||||
</sl-responsive-media>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Responsive Images
|
||||
|
||||
The following image maintains a `4:3` aspect ratio as its container is resized.
|
||||
|
||||
```html preview
|
||||
<sl-responsive-media aspect-ratio="4:3">
|
||||
<img src="https://images.unsplash.com/photo-1473186578172-c141e6798cf4?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1800&q=80" alt="Two blue chairs on a sandy beach.">
|
||||
</sl-responsive-media>
|
||||
```
|
||||
|
||||
### Responsive Videos
|
||||
|
||||
The following video is embedded using an `iframe` and maintains a `16:9` aspect ratio as its container is resized.
|
||||
|
||||
```html preview
|
||||
<sl-responsive-media aspect-ratio="16:9">
|
||||
<iframe src="https://player.vimeo.com/video/1053647?title=0&byline=0&portrait=0" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>
|
||||
</sl-responsive-media>
|
||||
```
|
||||
|
||||
[component-metadata:sl-responsive-media]
|
||||
@@ -2,190 +2,433 @@
|
||||
|
||||
[component-header:sl-select]
|
||||
|
||||
Selects allow you to choose one or more items from a dropdown menu.
|
||||
|
||||
```html preview
|
||||
<sl-select>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-menu-item value="option-4">Option 4</sl-menu-item>
|
||||
<sl-menu-item value="option-5">Option 5</sl-menu-item>
|
||||
<sl-menu-item value="option-6">Option 6</sl-menu-item>
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
<sl-option value="option-4">Option 4</sl-option>
|
||||
<sl-option value="option-5">Option 5</sl-option>
|
||||
<sl-option value="option-6">Option 6</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form.md) instead.
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
<SlOption value="option-4">Option 4</SlOption>
|
||||
<SlOption value="option-5">Option 5</SlOption>
|
||||
<SlOption value="option-6">Option 6</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
?> 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
|
||||
|
||||
### Placeholders
|
||||
|
||||
Use the `placeholder` attribute to add a placeholder.
|
||||
|
||||
```html preview
|
||||
<sl-select placeholder="Select one">
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
### Clearable
|
||||
|
||||
Use the `clearable` attribute to make the control clearable.
|
||||
|
||||
```html preview
|
||||
<sl-select placeholder="Clearable" clearable>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
### Pill
|
||||
|
||||
Use the `pill` prop to give selects rounded edges.
|
||||
|
||||
```html preview
|
||||
<sl-select pill>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` prop to disable a select.
|
||||
|
||||
```html preview
|
||||
<sl-select placeholder="Disabled" disabled>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
### 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. When using this option, `value` will be an array instead of a string.
|
||||
|
||||
```html preview
|
||||
<sl-select placeholder="Select a few" multiple clearable>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-menu-item value="option-4">Option 4</sl-menu-item>
|
||||
<sl-menu-item value="option-5">Option 5</sl-menu-item>
|
||||
<sl-menu-item value="option-6">Option 6</sl-menu-item>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
### Grouping Options
|
||||
|
||||
Options can be grouped visually using menu labels and menu dividers.
|
||||
|
||||
```html preview
|
||||
<sl-select placeholder="Select one">
|
||||
<sl-menu-label>Group 1</sl-menu-label>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
<sl-menu-divider></sl-menu-divider>
|
||||
<sl-menu-label>Group 2</sl-menu-label>
|
||||
<sl-menu-item value="option-4">Option 4</sl-menu-item>
|
||||
<sl-menu-item value="option-5">Option 5</sl-menu-item>
|
||||
<sl-menu-item value="option-6">Option 6</sl-menu-item>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a select's size.
|
||||
|
||||
```html preview
|
||||
<sl-select placeholder="Small" size="small" multiple>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
</sl-select>
|
||||
|
||||
<br>
|
||||
|
||||
<sl-select placeholder="Medium" size="medium" multiple>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
</sl-select>
|
||||
|
||||
<br>
|
||||
|
||||
<sl-select placeholder="Large" size="large" multiple>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
### Selecting Options Programmatically
|
||||
|
||||
The `value` prop is bound to the current selection. As the selection changes, so will the value. To programmatically manage the selection, update the `value` property.
|
||||
|
||||
```html preview
|
||||
<div class="selecting-example">
|
||||
<sl-select placeholder="">
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
</sl-select>
|
||||
|
||||
<br>
|
||||
|
||||
<sl-button data-option="option-1">Set 1</sl-button>
|
||||
<sl-button data-option="option-2">Set 2</sl-button>
|
||||
<sl-button data-option="option-3">Set 3</sl-button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.selecting-example');
|
||||
const select = container.querySelector('sl-select');
|
||||
|
||||
[...container.querySelectorAll('sl-button')].map(button => {
|
||||
button.addEventListener('click', () => {
|
||||
select.value = button.dataset.option;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### Labels
|
||||
|
||||
Use the `label` attribute to give the select an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
|
||||
```html preview
|
||||
<sl-select label="Select one">
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Option 2</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect label="Select one">
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-select
|
||||
label="Experience"
|
||||
help-text="Please tell us your skill level."
|
||||
>
|
||||
<sl-menu-item value="option-1">Novice</sl-menu-item>
|
||||
<sl-menu-item value="option-2">Intermediate</sl-menu-item>
|
||||
<sl-menu-item value="option-3">Advanced</sl-menu-item>
|
||||
<sl-select label="Experience" help-text="Please tell us your skill level.">
|
||||
<sl-option value="1">Novice</sl-option>
|
||||
<sl-option value="2">Intermediate</sl-option>
|
||||
<sl-option value="3">Advanced</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect label="Experience" help-text="Please tell us your skill level.">
|
||||
<SlOption value="1">Novice</SlOption>
|
||||
<SlOption value="2">Intermediate</SlOption>
|
||||
<SlOption value="3">Advanced</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Placeholders
|
||||
|
||||
Use the `placeholder` attribute to add a placeholder.
|
||||
|
||||
```html preview
|
||||
<sl-select placeholder="Select one">
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect placeholder="Select one">
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Clearable
|
||||
|
||||
Use the `clearable` attribute to make the control clearable. The clear button only appears when an option is selected.
|
||||
|
||||
```html preview
|
||||
<sl-select clearable value="option-1">
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect placeholder="Clearable" clearable>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Filled Selects
|
||||
|
||||
Add the `filled` attribute to draw a filled select.
|
||||
|
||||
```html preview
|
||||
<sl-select filled>
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect filled>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill
|
||||
|
||||
Use the `pill` attribute to give selects rounded edges.
|
||||
|
||||
```html preview
|
||||
<sl-select pill>
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect pill>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable a select.
|
||||
|
||||
```html preview
|
||||
<sl-select placeholder="Disabled" disabled>
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect placeholder="Disabled" disabled>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-select label="Select a Few" value="option-1 option-2 option-3" multiple clearable>
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
<sl-option value="option-4">Option 4</sl-option>
|
||||
<sl-option value="option-5">Option 5</sl-option>
|
||||
<sl-option value="option-6">Option 6</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect label="Select a Few" value="option-1 option-2 option-3" multiple clearable>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
<SlOption value="option-4">Option 4</SlOption>
|
||||
<SlOption value="option-5">Option 5</SlOption>
|
||||
<SlOption value="option-6">Option 6</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
?> 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`, use space-delimited values to select more than one option.
|
||||
|
||||
```html preview
|
||||
<sl-select value="option-1 option-2" multiple clearable>
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
<sl-option value="option-4">Option 4</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider, SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect value="option-1 option-2" multiple clearable>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Grouping Options
|
||||
|
||||
Use `<sl-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
|
||||
<sl-select>
|
||||
<small>Section 1</small>
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
<sl-divider></sl-divider>
|
||||
<small>Section 2</small>
|
||||
<sl-option value="option-4">Option 4</sl-option>
|
||||
<sl-option value="option-5">Option 5</sl-option>
|
||||
<sl-option value="option-6">Option 6</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
<SlOption value="option-4">Option 4</SlOption>
|
||||
<SlOption value="option-5">Option 5</SlOption>
|
||||
<SlOption value="option-6">Option 6</SlOption>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a select's size. Note that size does not apply to listbox options.
|
||||
|
||||
```html preview
|
||||
<sl-select placeholder="Small" size="small">
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-select placeholder="Medium" size="medium">
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-select placeholder="Large" size="large">
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlSelect placeholder="Small" size="small">
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
|
||||
<br />
|
||||
|
||||
<SlSelect placeholder="Medium" size="medium">
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
|
||||
<br />
|
||||
|
||||
<SlSelect placeholder="Large" size="large">
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-select placement="top">
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import {
|
||||
SlOption,
|
||||
SlSelect
|
||||
} from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect placement="top">
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix Icons
|
||||
|
||||
Use the `prefix` slot to prepend an icon to the control.
|
||||
|
||||
```html preview
|
||||
<sl-select placeholder="Small" size="small" clearable>
|
||||
<sl-icon name="house" slot="prefix"></sl-icon>
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
<br />
|
||||
<sl-select placeholder="Medium" size="medium" clearable>
|
||||
<sl-icon name="house" slot="prefix"></sl-icon>
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
<br />
|
||||
<sl-select placeholder="Large" size="large" clearable>
|
||||
<sl-icon name="house" slot="prefix"></sl-icon>
|
||||
<sl-option value="option-1">Option 1</sl-option>
|
||||
<sl-option value="option-2">Option 2</sl-option>
|
||||
<sl-option value="option-3">Option 3</sl-option>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIcon, SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlSelect placeholder="Small" size="small">
|
||||
<SlIcon name="house" slot="prefix"></SlIcon>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
<br />
|
||||
<SlSelect placeholder="Medium" size="medium">
|
||||
<SlIcon name="house" slot="prefix"></SlIcon>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
<br />
|
||||
<SlSelect placeholder="Large" size="large">
|
||||
<SlIcon name="house" slot="prefix"></SlIcon>
|
||||
<SlOption value="option-1">Option 1</SlOption>
|
||||
<SlOption value="option-2">Option 2</SlOption>
|
||||
<SlOption value="option-3">Option 3</SlOption>
|
||||
</SlSelect>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-select]
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
[component-header:sl-skeleton]
|
||||
|
||||
Skeletons are used to show where content will eventually be drawn.
|
||||
|
||||
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.
|
||||
@@ -54,22 +52,76 @@ Skeletons try not to be opinionated, as there are endless possibilities for desi
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.skeleton-overview header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skeleton-overview header sl-skeleton:last-child {
|
||||
flex: 0 0 auto;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.skeleton-overview sl-skeleton {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skeleton-overview sl-skeleton:nth-child(1) {
|
||||
float: left;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-right: 1rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.skeleton-overview sl-skeleton:nth-child(3) {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.skeleton-overview sl-skeleton:nth-child(4) {
|
||||
width: 80%;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="skeleton-overview">
|
||||
<header>
|
||||
<SlSkeleton />
|
||||
<SlSkeleton />
|
||||
</header>
|
||||
|
||||
<SlSkeleton />
|
||||
<SlSkeleton />
|
||||
<SlSkeleton />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Effects
|
||||
|
||||
The default effect is `sheen`, which animates a sheen horizontally across the skeleton. There's also `pulse`, which fades the skeleton in and out. To disable animations, use `none`. Effects are intentionally subtle, as they can be distracting otherwise.
|
||||
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">
|
||||
<sl-skeleton effect="none"></sl-skeleton>
|
||||
None
|
||||
|
||||
<sl-skeleton effect="sheen"></sl-skeleton>
|
||||
Sheen
|
||||
|
||||
<sl-skeleton effect="pulse"></sl-skeleton>
|
||||
Pulse
|
||||
|
||||
<sl-skeleton effect="none"></sl-skeleton>
|
||||
None
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -83,6 +135,35 @@ The default effect is `sheen`, which animates a sheen horizontally across the sk
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.skeleton-effects {
|
||||
font-size: var(--sl-font-size-small);
|
||||
}
|
||||
|
||||
.skeleton-effects sl-skeleton:not(:first-child) {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="skeleton-effects">
|
||||
<SlSkeleton effect="none" />
|
||||
None
|
||||
<SlSkeleton effect="sheen" />
|
||||
Sheen
|
||||
<SlSkeleton effect="pulse" />
|
||||
Pulse
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Paragraphs
|
||||
|
||||
Use multiple skeletons and some clever styles to simulate paragraphs.
|
||||
@@ -115,6 +196,42 @@ Use multiple skeletons and some clever styles to simulate paragraphs.
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.skeleton-paragraphs sl-skeleton {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skeleton-paragraphs sl-skeleton:nth-child(2) {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.skeleton-paragraphs sl-skeleton:nth-child(4) {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.skeleton-paragraphs sl-skeleton:last-child {
|
||||
width: 50%;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="skeleton-paragraphs">
|
||||
<SlSkeleton />
|
||||
<SlSkeleton />
|
||||
<SlSkeleton />
|
||||
<SlSkeleton />
|
||||
<SlSkeleton />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Avatars
|
||||
|
||||
Set a matching width and height to make a circle, square, or rounded avatar skeleton.
|
||||
@@ -127,6 +244,27 @@ Set a matching width and height to make a circle, square, or rounded avatar skel
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.skeleton-avatars sl-skeleton {
|
||||
display: inline-block;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.skeleton-avatars sl-skeleton:nth-child(1) {
|
||||
--border-radius: 0;
|
||||
}
|
||||
|
||||
.skeleton-avatars sl-skeleton:nth-child(2) {
|
||||
--border-radius: var(--sl-border-radius-medium);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.skeleton-avatars sl-skeleton {
|
||||
display: inline-block;
|
||||
width: 3rem;
|
||||
@@ -141,7 +279,19 @@ Set a matching width and height to make a circle, square, or rounded avatar skel
|
||||
.skeleton-avatars sl-skeleton:nth-child(2) {
|
||||
--border-radius: var(--sl-border-radius-medium);
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="skeleton-avatars">
|
||||
<SlSkeleton />
|
||||
<SlSkeleton />
|
||||
<SlSkeleton />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Shapes
|
||||
@@ -177,6 +327,58 @@ Use the `--border-radius` custom property to make circles, squares, and rectangl
|
||||
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 sl-skeleton:not(:last-child) {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.skeleton-shapes sl-skeleton {
|
||||
display: inline-flex;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.skeleton-shapes .square::part(indicator) {
|
||||
--border-radius: var(--sl-border-radius-medium);
|
||||
}
|
||||
|
||||
.skeleton-shapes .circle::part(indicator) {
|
||||
--border-radius: var(--sl-border-radius-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%);
|
||||
@@ -190,7 +392,21 @@ Use the `--border-radius` custom property to make circles, squares, and rectangl
|
||||
.skeleton-shapes sl-skeleton:not(:last-child) {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="skeleton-shapes">
|
||||
<SlSkeleton className="square" />
|
||||
<SlSkeleton className="circle" />
|
||||
<SlSkeleton className="triangle" />
|
||||
<SlSkeleton className="cross" />
|
||||
<SlSkeleton className="comment" />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Colors
|
||||
@@ -198,7 +414,30 @@ Use the `--border-radius` custom property to make circles, squares, and rectangl
|
||||
Set the `--color` and `--sheen-color` custom properties to adjust the skeleton's color.
|
||||
|
||||
```html preview
|
||||
<sl-skeleton style="--color: tomato; --sheen-color: #ffb094;"></sl-skeleton>
|
||||
<sl-skeleton effect="sheen" style="--color: tomato; --sheen-color: #ffb094;"></sl-skeleton>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.skeleton-avatars sl-skeleton {
|
||||
display: inline-block;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.skeleton-avatars sl-skeleton:nth-child(1) {
|
||||
--border-radius: 0;
|
||||
}
|
||||
|
||||
.skeleton-avatars sl-skeleton:nth-child(2) {
|
||||
--border-radius: var(--sl-border-radius-medium);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => <SlSkeleton effect="sheen" style={{ '--color': 'tomato', '--sheen-color': '#ffb094' }} />;
|
||||
```
|
||||
|
||||
[component-metadata:sl-skeleton]
|
||||
|
||||
@@ -2,17 +2,21 @@
|
||||
|
||||
[component-header:sl-spinner]
|
||||
|
||||
Spinners are used to show the progress of an indeterminate operation.
|
||||
|
||||
```html preview
|
||||
<sl-spinner></sl-spinner>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSpinner } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlSpinner />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Size
|
||||
|
||||
Spinners are sized relative to 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.
|
||||
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
|
||||
<sl-spinner></sl-spinner>
|
||||
@@ -20,12 +24,37 @@ Spinners are sized relative to the current font size. To change their size, set
|
||||
<sl-spinner style="font-size: 3rem;"></sl-spinner>
|
||||
```
|
||||
|
||||
### Stroke Width
|
||||
```jsx react
|
||||
import { SlSpinner } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
The width of the spinner can be changed by setting the `--stroke-width` custom property.
|
||||
const App = () => (
|
||||
<>
|
||||
<SlSpinner />
|
||||
<SlSpinner style={{ fontSize: '2rem' }} />
|
||||
<SlSpinner style={{ fontSize: '3rem' }} />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Track Width
|
||||
|
||||
The width of the spinner's track can be changed by setting the `--track-width` custom property.
|
||||
|
||||
```html preview
|
||||
<sl-spinner style="font-size: 2rem; --stroke-width: 6px;"></sl-spinner>
|
||||
<sl-spinner style="font-size: 50px; --track-width: 10px;"></sl-spinner>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSpinner } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSpinner
|
||||
style={{
|
||||
fontSize: '3rem',
|
||||
'--track-width': '6px'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### Color
|
||||
@@ -33,7 +62,21 @@ The width of the spinner can be changed by setting the `--stroke-width` custom p
|
||||
The spinner's colors can be changed by setting the `--indicator-color` and `--track-color` custom properties.
|
||||
|
||||
```html preview
|
||||
<sl-spinner style="font-size: 2rem; --indicator-color: tomato;"></sl-spinner>
|
||||
<sl-spinner style="font-size: 3rem; --indicator-color: deeppink; --track-color: pink;"></sl-spinner>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSpinner } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSpinner
|
||||
style={{
|
||||
fontSize: '3rem',
|
||||
'--indicator-color': 'deeppink',
|
||||
'--track-color': 'pink'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-spinner]
|
||||
|
||||
756
docs/components/split-panel.md
Normal file
@@ -0,0 +1,756 @@
|
||||
# Split Panel
|
||||
|
||||
[component-header:sl-split-panel]
|
||||
|
||||
```html preview
|
||||
<sl-split-panel>
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSplitPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSplitPanel>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</SlSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
## 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
|
||||
<sl-split-panel position="75">
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</sl-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
|
||||
<sl-split-panel position-in-pixels="150">
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSplitPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSplitPanel position="200">
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</SlSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-split-panel vertical style="height: 400px;">
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 100%; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 100%; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSplitPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSplitPanel vertical style={{ height: '400px' }}>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '100%',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '100%',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</SlSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
### 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">
|
||||
<sl-split-panel snap="100px 50%">
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</sl-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(--sl-color-neutral-400);
|
||||
transform: translateX(-3px);
|
||||
}
|
||||
|
||||
.split-panel-snapping-dots::before {
|
||||
left: 100px;
|
||||
}
|
||||
|
||||
.split-panel-snapping-dots::after {
|
||||
left: 50%;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSplitPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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(--sl-color-neutral-400);
|
||||
transform: translateX(-3px);
|
||||
}
|
||||
|
||||
.split-panel-snapping-dots::before {
|
||||
left: 100px;
|
||||
}
|
||||
|
||||
.split-panel-snapping-dots::after {
|
||||
left: 50%;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="split-panel-snapping">
|
||||
<SlSplitPanel snap="100px 50%">
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</SlSplitPanel>
|
||||
|
||||
<div className="split-panel-snapping-dots" />
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Add the `disabled` attribute to prevent the divider from being repositioned.
|
||||
|
||||
```html preview
|
||||
<sl-split-panel disabled>
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSplitPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSplitPanel disabled>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</SlSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
### 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">
|
||||
<sl-split-panel>
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
|
||||
<sl-select label="Primary Panel" value="" style="max-width: 200px; margin-top: 1rem;">
|
||||
<sl-option value="">None</sl-option>
|
||||
<sl-option value="start">Start</sl-option>
|
||||
<sl-option value="end">End</sl-option>
|
||||
</sl-select>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.split-panel-primary');
|
||||
const splitPanel = container.querySelector('sl-split-panel');
|
||||
const select = container.querySelector('sl-select');
|
||||
|
||||
select.addEventListener('sl-change', () => (splitPanel.primary = select.value));
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlSplitPanel, SlSelect, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [primary, setPrimary] = useState('');
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlSplitPanel primary={primary}>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</SlSplitPanel>
|
||||
|
||||
<SlSelect
|
||||
label="Primary Panel"
|
||||
value={primary}
|
||||
style={{ maxWidth: '200px', marginTop: '1rem' }}
|
||||
onSlChange={event => setPrimary(event.target.value)}
|
||||
>
|
||||
<SlMenuItem value="">None</SlMenuItem>
|
||||
<SlMenuItem value="start">Start</SlMenuItem>
|
||||
<SlMenuItem value="end">End</SlMenuItem>
|
||||
</SlSelect>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-split-panel style="--min: 150px; --max: calc(100% - 150px);">
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSplitPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSplitPanel style={{ '--min': '150px', '--max': 'calc(100% - 150px)' }}>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</SlSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
### Nested Split Panels
|
||||
|
||||
Create complex layouts that can be repositioned independently by nesting split panels.
|
||||
|
||||
```html preview
|
||||
<sl-split-panel>
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 400px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div slot="end">
|
||||
<sl-split-panel vertical style="height: 400px;">
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 100%; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Top
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 100%; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Bottom
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSplitPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSplitPanel>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '400px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div slot="end">
|
||||
<SlSplitPanel vertical style={{ height: '400px' }}>
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '100%',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '100%',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</SlSplitPanel>
|
||||
</div>
|
||||
</SlSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-split-panel style="--divider-width: 20px;">
|
||||
<sl-icon slot="divider" name="grip-vertical"></sl-icon>
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSplitPanel, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSplitPanel style={{ '--divider-width': '20px' }}>
|
||||
<SlIcon slot="divider" name="grip-vertical" />
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</SlSplitPanel>
|
||||
);
|
||||
```
|
||||
|
||||
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">
|
||||
<sl-split-panel>
|
||||
<sl-icon slot="divider" name="grip-vertical"></sl-icon>
|
||||
<div
|
||||
slot="start"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.split-panel-divider sl-split-panel {
|
||||
--divider-width: 2px;
|
||||
}
|
||||
|
||||
.split-panel-divider sl-split-panel::part(divider) {
|
||||
background-color: var(--sl-color-pink-600);
|
||||
}
|
||||
|
||||
.split-panel-divider sl-icon {
|
||||
position: absolute;
|
||||
border-radius: var(--sl-border-radius-small);
|
||||
background: var(--sl-color-pink-600);
|
||||
color: var(--sl-color-neutral-0);
|
||||
padding: 0.5rem 0.125rem;
|
||||
}
|
||||
|
||||
.split-panel-divider sl-split-panel::part(divider):focus-visible {
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.split-panel-divider sl-split-panel:focus-within sl-icon {
|
||||
background-color: var(--sl-color-primary-600);
|
||||
color: var(--sl-color-neutral-0);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSplitPanel, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.split-panel-divider sl-split-panel {
|
||||
--divider-width: 2px;
|
||||
}
|
||||
|
||||
.split-panel-divider sl-split-panel::part(divider) {
|
||||
background-color: var(--sl-color-pink-600);
|
||||
}
|
||||
|
||||
.split-panel-divider sl-icon {
|
||||
position: absolute;
|
||||
border-radius: var(--sl-border-radius-small);
|
||||
background: var(--sl-color-pink-600);
|
||||
color: var(--sl-color-neutral-0);
|
||||
padding: .5rem .125rem;
|
||||
}
|
||||
|
||||
.split-panel-divider sl-split-panel::part(divider):focus-visible {
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.split-panel-divider sl-split-panel:focus-within sl-icon {
|
||||
background-color: var(--sl-color-primary-600);
|
||||
color: var(--sl-color-neutral-0);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="split-panel-divider">
|
||||
<SlSplitPanel>
|
||||
<SlIcon slot="divider" name="grip-vertical" />
|
||||
<div
|
||||
slot="start"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
Start
|
||||
</div>
|
||||
<div
|
||||
slot="end"
|
||||
style={{
|
||||
height: '200px',
|
||||
background: 'var(--sl-color-neutral-50)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
End
|
||||
</div>
|
||||
</SlSplitPanel>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-split-panel]
|
||||
@@ -2,13 +2,17 @@
|
||||
|
||||
[component-header:sl-switch]
|
||||
|
||||
Switches allow the user to toggle an option on or off.
|
||||
|
||||
```html preview
|
||||
<sl-switch>Switch</sl-switch>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form.md) instead.
|
||||
```jsx react
|
||||
import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlSwitch>Switch</SlSwitch>;
|
||||
```
|
||||
|
||||
?> 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
|
||||
|
||||
@@ -20,6 +24,12 @@ Use the `checked` attribute to activate the switch.
|
||||
<sl-switch checked>Checked</sl-switch>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlSwitch checked>Checked</SlSwitch>;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable the switch.
|
||||
@@ -28,12 +38,58 @@ Use the `disabled` attribute to disable the switch.
|
||||
<sl-switch disabled>Disabled</sl-switch>
|
||||
```
|
||||
|
||||
### Custom Size
|
||||
```jsx react
|
||||
import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
Use the available custom properties to make the switch a different size.
|
||||
const App = () => <SlSwitch disabled>Disabled</SlSwitch>;
|
||||
```
|
||||
|
||||
## Sizes
|
||||
|
||||
Use the `size` attribute to change a switch's size.
|
||||
|
||||
```html preview
|
||||
<sl-switch style="--width: 80px; --height: 32px; --thumb-size: 26px;"></sl-switch>
|
||||
<sl-switch size="small">Small</sl-switch>
|
||||
<br />
|
||||
<sl-switch size="medium">Medium</sl-switch>
|
||||
<br />
|
||||
<sl-switch size="large">Large</sl-switch>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlSwitch size="small">Small</SlSwitch>
|
||||
<br />
|
||||
<SlSwitch size="medium">Medium</SlSwitch>
|
||||
<br />
|
||||
<SlSwitch size="large">Large</SlSwitch>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Styles
|
||||
|
||||
Use the available custom properties to change how the switch is styled.
|
||||
|
||||
```html preview
|
||||
<sl-switch style="--width: 80px; --height: 40px; --thumb-size: 36px;">Really big</sl-switch>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSwitch
|
||||
style={{
|
||||
'--width': '80px',
|
||||
'--height': '32px',
|
||||
'--thumb-size': '26px'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-switch]
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
[component-header:sl-tab-group]
|
||||
|
||||
Tab groups organize content into a container that shows one section at a time.
|
||||
|
||||
Tab groups make use of [tabs](/components/tab.md) and [tab panels](/components/tab-panel.md). Each tab must be slotted into the `nav` slot and its `panel` must refer to a tab panel of the same name.
|
||||
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
|
||||
<sl-tab-group>
|
||||
@@ -20,6 +18,32 @@ Tab groups make use of [tabs](/components/tab.md) and [tab panels](/components/t
|
||||
</sl-tab-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlTabGroup>
|
||||
<SlTab slot="nav" panel="general">
|
||||
General
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</SlTab>
|
||||
|
||||
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
|
||||
</SlTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Tabs on Bottom
|
||||
@@ -40,12 +64,38 @@ Tabs can be shown on the bottom by setting `placement` to `bottom`.
|
||||
</sl-tab-group>
|
||||
```
|
||||
|
||||
### Tabs on Left
|
||||
```jsx react
|
||||
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
Tabs can be shown on the left by setting `placement` to `left`.
|
||||
const App = () => (
|
||||
<SlTabGroup placement="bottom">
|
||||
<SlTab slot="nav" panel="general">
|
||||
General
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</SlTab>
|
||||
|
||||
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
|
||||
</SlTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Tabs on Start
|
||||
|
||||
Tabs can be shown on the starting side by setting `placement` to `start`.
|
||||
|
||||
```html preview
|
||||
<sl-tab-group placement="left">
|
||||
<sl-tab-group placement="start">
|
||||
<sl-tab slot="nav" panel="general">General</sl-tab>
|
||||
<sl-tab slot="nav" panel="custom">Custom</sl-tab>
|
||||
<sl-tab slot="nav" panel="advanced">Advanced</sl-tab>
|
||||
@@ -58,12 +108,38 @@ Tabs can be shown on the left by setting `placement` to `left`.
|
||||
</sl-tab-group>
|
||||
```
|
||||
|
||||
### Tabs on Right
|
||||
```jsx react
|
||||
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
Tabs can be shown on the right by setting `placement` to `right`.
|
||||
const App = () => (
|
||||
<SlTabGroup placement="start">
|
||||
<SlTab slot="nav" panel="general">
|
||||
General
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</SlTab>
|
||||
|
||||
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
|
||||
</SlTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Tabs on End
|
||||
|
||||
Tabs can be shown on the ending side by setting `placement` to `end`.
|
||||
|
||||
```html preview
|
||||
<sl-tab-group placement="right">
|
||||
<sl-tab-group placement="end">
|
||||
<sl-tab slot="nav" panel="general">General</sl-tab>
|
||||
<sl-tab slot="nav" panel="custom">Custom</sl-tab>
|
||||
<sl-tab slot="nav" panel="advanced">Advanced</sl-tab>
|
||||
@@ -76,9 +152,35 @@ Tabs can be shown on the right by setting `placement` to `right`.
|
||||
</sl-tab-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlTabGroup placement="end">
|
||||
<SlTab slot="nav" panel="general">
|
||||
General
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</SlTab>
|
||||
|
||||
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
|
||||
</SlTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Closable Tabs
|
||||
|
||||
Add the `closable` prop 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.
|
||||
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
|
||||
<sl-tab-group class="tabs-closable">
|
||||
@@ -112,6 +214,48 @@ Add the `closable` prop to a tab to show a close button. This example shows how
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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('sl-tab-group');
|
||||
const tabPanel = tabGroup.querySelector(`[aria-labelledby="${tab.id}"]`);
|
||||
|
||||
tab.remove();
|
||||
tabPanel.remove();
|
||||
}
|
||||
|
||||
return (
|
||||
<SlTabGroup className="tabs-closable" onSlClose={handleClose}>
|
||||
<SlTab slot="nav" panel="general">
|
||||
General
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="closable-1" closable onSlClose={handleClose}>
|
||||
Closable 1
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="closable-2" closable onSlClose={handleClose}>
|
||||
Closable 2
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="closable-3" closable onSlClose={handleClose}>
|
||||
Closable 3
|
||||
</SlTab>
|
||||
|
||||
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="closable-1">This is the first closable tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="closable-2">This is the second closable tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="closable-3">This is the third closable tab panel.</SlTabPanel>
|
||||
</SlTabGroup>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Scrolling Tabs
|
||||
|
||||
When there are more tabs than horizontal space allows, the nav will be scrollable.
|
||||
@@ -162,6 +306,96 @@ When there are more tabs than horizontal space allows, the nav will be scrollabl
|
||||
</sl-tab-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlTabGroup>
|
||||
<SlTab slot="nav" panel="tab-1">
|
||||
Tab 1
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-2">
|
||||
Tab 2
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-3">
|
||||
Tab 3
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-4">
|
||||
Tab 4
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-5">
|
||||
Tab 5
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-6">
|
||||
Tab 6
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-7">
|
||||
Tab 7
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-8">
|
||||
Tab 8
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-9">
|
||||
Tab 9
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-10">
|
||||
Tab 10
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-11">
|
||||
Tab 11
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-12">
|
||||
Tab 12
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-13">
|
||||
Tab 13
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-14">
|
||||
Tab 14
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-15">
|
||||
Tab 15
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-16">
|
||||
Tab 16
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-17">
|
||||
Tab 17
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-18">
|
||||
Tab 18
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-19">
|
||||
Tab 19
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-20">
|
||||
Tab 20
|
||||
</SlTab>
|
||||
|
||||
<SlTabPanel name="tab-1">Tab panel 1</SlTabPanel>
|
||||
<SlTabPanel name="tab-2">Tab panel 2</SlTabPanel>
|
||||
<SlTabPanel name="tab-3">Tab panel 3</SlTabPanel>
|
||||
<SlTabPanel name="tab-4">Tab panel 4</SlTabPanel>
|
||||
<SlTabPanel name="tab-5">Tab panel 5</SlTabPanel>
|
||||
<SlTabPanel name="tab-6">Tab panel 6</SlTabPanel>
|
||||
<SlTabPanel name="tab-7">Tab panel 7</SlTabPanel>
|
||||
<SlTabPanel name="tab-8">Tab panel 8</SlTabPanel>
|
||||
<SlTabPanel name="tab-9">Tab panel 9</SlTabPanel>
|
||||
<SlTabPanel name="tab-10">Tab panel 10</SlTabPanel>
|
||||
<SlTabPanel name="tab-11">Tab panel 11</SlTabPanel>
|
||||
<SlTabPanel name="tab-12">Tab panel 12</SlTabPanel>
|
||||
<SlTabPanel name="tab-13">Tab panel 13</SlTabPanel>
|
||||
<SlTabPanel name="tab-14">Tab panel 14</SlTabPanel>
|
||||
<SlTabPanel name="tab-15">Tab panel 15</SlTabPanel>
|
||||
<SlTabPanel name="tab-16">Tab panel 16</SlTabPanel>
|
||||
<SlTabPanel name="tab-17">Tab panel 17</SlTabPanel>
|
||||
<SlTabPanel name="tab-18">Tab panel 18</SlTabPanel>
|
||||
<SlTabPanel name="tab-19">Tab panel 19</SlTabPanel>
|
||||
<SlTabPanel name="tab-20">Tab panel 20</SlTabPanel>
|
||||
</SlTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Manual Activation
|
||||
|
||||
When focused, keyboard users can press <kbd>Left</kbd> or <kbd>Right</kbd> 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 <kbd>Space</kbd> or <kbd>Enter</kbd> before showing the tab panel (manual activation).
|
||||
@@ -180,4 +414,30 @@ When focused, keyboard users can press <kbd>Left</kbd> or <kbd>Right</kbd> to se
|
||||
</sl-tab-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlTabGroup activation="manual">
|
||||
<SlTab slot="nav" panel="general">
|
||||
General
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</SlTab>
|
||||
|
||||
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
|
||||
</SlTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-tab-group]
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
[component-header:sl-tab-panel]
|
||||
|
||||
Tab panels are used inside tab groups to display content.
|
||||
|
||||
```html preview
|
||||
<sl-tab-group>
|
||||
<sl-tab slot="nav" panel="general">General</sl-tab>
|
||||
@@ -18,6 +16,32 @@ Tab panels are used inside tab groups to display content.
|
||||
</sl-tab-group>
|
||||
```
|
||||
|
||||
?> Additional demonstrations can be found in the [tab group examples](/components/tab-group.md).
|
||||
```jsx react
|
||||
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlTabGroup>
|
||||
<SlTab slot="nav" panel="general">
|
||||
General
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="custom">
|
||||
Custom
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="advanced">
|
||||
Advanced
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="disabled" disabled>
|
||||
Disabled
|
||||
</SlTab>
|
||||
|
||||
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
|
||||
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
|
||||
</SlTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
?> Additional demonstrations can be found in the [tab group examples](/components/tab-group).
|
||||
|
||||
[component-metadata:sl-tab-panel]
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
[component-header:sl-tab]
|
||||
|
||||
Tabs are used inside tab groups to represent tab panels.
|
||||
|
||||
```html preview
|
||||
<sl-tab>Tab</sl-tab>
|
||||
<sl-tab active>Active</sl-tab>
|
||||
@@ -11,6 +9,19 @@ Tabs are used inside tab groups to represent tab panels.
|
||||
<sl-tab disabled>Disabled</sl-tab>
|
||||
```
|
||||
|
||||
?> Additional demonstrations can be found in the [tab group examples](/components/tab-group.md).
|
||||
```jsx react
|
||||
import { SlTab } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlTab>Tab</SlTab>
|
||||
<SlTab active>Active</SlTab>
|
||||
<SlTab closable>Closable</SlTab>
|
||||
<SlTab disabled>Disabled</SlTab>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
?> Additional demonstrations can be found in the [tab group examples](/components/tab-group).
|
||||
|
||||
[component-metadata:sl-tab]
|
||||
|
||||
@@ -2,21 +2,33 @@
|
||||
|
||||
[component-header:sl-tag]
|
||||
|
||||
Tags are used as labels to organize things or to indicate a selection.
|
||||
|
||||
```html preview
|
||||
<sl-tag type="primary">Primary</sl-tag>
|
||||
<sl-tag type="success">Success</sl-tag>
|
||||
<sl-tag type="info">Info</sl-tag>
|
||||
<sl-tag type="warning">Warning</sl-tag>
|
||||
<sl-tag type="danger">Danger</sl-tag>
|
||||
<sl-tag variant="primary">Primary</sl-tag>
|
||||
<sl-tag variant="success">Success</sl-tag>
|
||||
<sl-tag variant="neutral">Neutral</sl-tag>
|
||||
<sl-tag variant="warning">Warning</sl-tag>
|
||||
<sl-tag variant="danger">Danger</sl-tag>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTag } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlTag variant="primary">Primary</SlTag>
|
||||
<SlTag variant="success">Success</SlTag>
|
||||
<SlTag variant="neutral">Neutral</SlTag>
|
||||
<SlTag variant="warning">Warning</SlTag>
|
||||
<SlTag variant="danger">Danger</SlTag>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` prop to change a tab's size.
|
||||
Use the `size` attribute to change a tab's size.
|
||||
|
||||
```html preview
|
||||
<sl-tag size="small">Small</sl-tag>
|
||||
@@ -24,9 +36,21 @@ Use the `size` prop to change a tab's size.
|
||||
<sl-tag size="large">Large</sl-tag>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTag } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlTag size="small">Small</SlTag>
|
||||
<SlTag size="medium">Medium</SlTag>
|
||||
<SlTag size="large">Large</SlTag>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill
|
||||
|
||||
Use the `pill` prop to give tabs rounded edges.
|
||||
Use the `pill` attribute to give tabs rounded edges.
|
||||
|
||||
```html preview
|
||||
<sl-tag size="small" pill>Small</sl-tag>
|
||||
@@ -34,32 +58,88 @@ Use the `pill` prop to give tabs rounded edges.
|
||||
<sl-tag size="large" pill>Large</sl-tag>
|
||||
```
|
||||
|
||||
### Clearable
|
||||
```jsx react
|
||||
import { SlTag } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
Use the `clearable` attribute to add a clear button to the tag.
|
||||
const App = () => (
|
||||
<>
|
||||
<SlTag size="small" pill>
|
||||
Small
|
||||
</SlTag>
|
||||
<SlTag size="medium" pill>
|
||||
Medium
|
||||
</SlTag>
|
||||
<SlTag size="large" pill>
|
||||
Large
|
||||
</SlTag>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Removable
|
||||
|
||||
Use the `removable` attribute to add a remove button to the tag.
|
||||
|
||||
```html preview
|
||||
<div class="tags-clearable">
|
||||
<sl-tag size="small" clearable>Small</sl-tag>
|
||||
<sl-tag size="medium" clearable>Medium</sl-tag>
|
||||
<sl-tag size="large" clearable>Large</sl-tag>
|
||||
<div class="tags-removable">
|
||||
<sl-tag size="small" removable>Small</sl-tag>
|
||||
<sl-tag size="medium" removable>Medium</sl-tag>
|
||||
<sl-tag size="large" removable>Large</sl-tag>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const div = document.querySelector('.tags-clearable');
|
||||
const div = document.querySelector('.tags-removable');
|
||||
|
||||
div.addEventListener('sl-clear', event => {
|
||||
div.addEventListener('sl-remove', event => {
|
||||
const tag = event.target;
|
||||
tag.style.opacity = '0';
|
||||
setTimeout(() => tag.style.opacity = '1', 2000);
|
||||
setTimeout(() => (tag.style.opacity = '1'), 2000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.tags-clearable sl-tag {
|
||||
.tags-removable sl-tag {
|
||||
transition: var(--sl-transition-medium) opacity;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTag } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.tags-removable sl-tag {
|
||||
transition: var(--sl-transition-medium) 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">
|
||||
<SlTag size="small" removable onSlRemove={handleRemove}>
|
||||
Small
|
||||
</SlTag>
|
||||
|
||||
<SlTag size="medium" removable onSlRemove={handleRemove}>
|
||||
Medium
|
||||
</SlTag>
|
||||
|
||||
<SlTag size="large" removable onSlRemove={handleRemove}>
|
||||
Large
|
||||
</SlTag>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
[component-metadata:sl-tag]
|
||||
|
||||
@@ -2,18 +2,48 @@
|
||||
|
||||
[component-header:sl-textarea]
|
||||
|
||||
Textareas collect data from the user and allow multiple lines of text.
|
||||
|
||||
```html preview
|
||||
<sl-textarea></sl-textarea>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form.md) instead.
|
||||
```jsx react
|
||||
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
?> Please refer to the section on [form control validation](/components/form?id=form-control-validation) to learn how to do client-side validation.
|
||||
const App = () => <SlTextarea />;
|
||||
```
|
||||
|
||||
?> 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
|
||||
<sl-textarea label="Comments"></sl-textarea>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlTextarea 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
|
||||
<sl-textarea label="Feedback" help-text="Please tell us what you think."> </sl-textarea>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlTextarea 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.
|
||||
@@ -22,6 +52,12 @@ Use the `rows` attribute to change the number of text rows that get shown.
|
||||
<sl-textarea rows="2"></sl-textarea>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlTextarea rows={2} />;
|
||||
```
|
||||
|
||||
### Placeholders
|
||||
|
||||
Use the `placeholder` attribute to add a placeholder.
|
||||
@@ -30,44 +66,64 @@ Use the `placeholder` attribute to add a placeholder.
|
||||
<sl-textarea placeholder="Type something"></sl-textarea>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlTextarea placeholder="Type something" />;
|
||||
```
|
||||
|
||||
### Filled Textareas
|
||||
|
||||
Add the `filled` attribute to draw a filled textarea.
|
||||
|
||||
```html preview
|
||||
<sl-textarea placeholder="Type something" filled></sl-textarea>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlTextarea placeholder="Type something" filled />;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable an input.
|
||||
Use the `disabled` attribute to disable a textarea.
|
||||
|
||||
```html preview
|
||||
<sl-textarea placeholder="Textarea" disabled></sl-textarea>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlTextarea placeholder="Textarea" disabled />;
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a textarea's size.
|
||||
|
||||
```html preview
|
||||
<sl-textarea placeholder="Small" size="small"></sl-textarea>
|
||||
<br>
|
||||
<br />
|
||||
<sl-textarea placeholder="Medium" size="medium"></sl-textarea>
|
||||
<br>
|
||||
<br />
|
||||
<sl-textarea placeholder="Large" size="large"></sl-textarea>
|
||||
```
|
||||
|
||||
### Labels
|
||||
```jsx react
|
||||
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
Use the `label` attribute to give the textarea an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
|
||||
```html preview
|
||||
<sl-textarea label="Comments"></sl-textarea>
|
||||
```
|
||||
|
||||
### 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
|
||||
<sl-textarea
|
||||
label="Feedback"
|
||||
help-text="Please tell us what you think."
|
||||
>
|
||||
</sl-textarea>
|
||||
const App = () => (
|
||||
<>
|
||||
<SlTextarea placeholder="Small" size="small"></SlTextarea>
|
||||
<br />
|
||||
<SlTextarea placeholder="Medium" size="medium"></SlTextarea>
|
||||
<br />
|
||||
<SlTextarea placeholder="Large" size="large"></SlTextarea>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Prevent Resizing
|
||||
@@ -78,6 +134,12 @@ By default, textareas can be resized vertically by the user. To prevent resizing
|
||||
<sl-textarea resize="none"></sl-textarea>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlTextarea resize="none" />;
|
||||
```
|
||||
|
||||
### Expand with Content
|
||||
|
||||
Textareas will automatically resize to expand to fit their content when `resize` is set to `auto`.
|
||||
@@ -86,4 +148,10 @@ Textareas will automatically resize to expand to fit their content when `resize`
|
||||
<sl-textarea resize="auto"></sl-textarea>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlTextarea resize="auto" />;
|
||||
```
|
||||
|
||||
[component-metadata:sl-textarea]
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
[component-header:sl-tooltip]
|
||||
|
||||
Tooltips display additional information based on a specific action.
|
||||
|
||||
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.
|
||||
@@ -14,6 +12,16 @@ Tooltips use `display: contents` so they won't interfere with how elements are p
|
||||
</sl-tooltip>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlTooltip content="This is a tooltip">
|
||||
<SlButton>Hover Me</SlButton>
|
||||
</SlTooltip>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Placement
|
||||
@@ -41,7 +49,7 @@ Use the `placement` attribute to set the preferred placement of the tooltip.
|
||||
<sl-button></sl-button>
|
||||
</sl-tooltip>
|
||||
|
||||
<sl-tooltip content="right-start" placement="right-start" style="margin-left: 400px;">
|
||||
<sl-tooltip content="right-start" placement="right-start">
|
||||
<sl-button></sl-button>
|
||||
</sl-tooltip>
|
||||
</div>
|
||||
@@ -84,6 +92,7 @@ Use the `placement` attribute to set the preferred placement of the tooltip.
|
||||
<style>
|
||||
.tooltip-placement-example {
|
||||
width: 250px;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.tooltip-placement-example-row:after {
|
||||
@@ -99,17 +108,119 @@ Use the `placement` attribute to set the preferred placement of the tooltip.
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.tooltip-placement-example [placement='top-start'] sl-button,
|
||||
.tooltip-placement-example [placement='bottom-start'] sl-button {
|
||||
.tooltip-placement-example-row:nth-child(1) sl-tooltip:first-child sl-button,
|
||||
.tooltip-placement-example-row:nth-child(5) sl-tooltip:first-child sl-button {
|
||||
margin-left: calc(40px + 0.25rem);
|
||||
}
|
||||
|
||||
.tooltip-placement-example [placement^='right'] sl-button {
|
||||
.tooltip-placement-example-row:nth-child(2) sl-tooltip:nth-child(2) sl-button,
|
||||
.tooltip-placement-example-row:nth-child(3) sl-tooltip:nth-child(2) sl-button,
|
||||
.tooltip-placement-example-row:nth-child(4) sl-tooltip:nth-child(2) sl-button {
|
||||
margin-left: calc((40px * 3) + (0.25rem * 3));
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.tooltip-placement-example {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.tooltip-placement-example-row:after {
|
||||
content: '';
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.tooltip-placement-example sl-button {
|
||||
float: left;
|
||||
width: 2.5rem;
|
||||
margin-right: 0.25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.tooltip-placement-example-row:nth-child(1) sl-tooltip:first-child sl-button,
|
||||
.tooltip-placement-example-row:nth-child(5) sl-tooltip:first-child sl-button {
|
||||
margin-left: calc(40px + 0.25rem);
|
||||
}
|
||||
|
||||
.tooltip-placement-example-row:nth-child(2) sl-tooltip:nth-child(2) sl-button,
|
||||
.tooltip-placement-example-row:nth-child(3) sl-tooltip:nth-child(2) sl-button,
|
||||
.tooltip-placement-example-row:nth-child(4) sl-tooltip:nth-child(2) sl-button {
|
||||
margin-left: calc((40px * 3) + (0.25rem * 3));
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div className="tooltip-placement-example">
|
||||
<div className="tooltip-placement-example-row">
|
||||
<SlTooltip content="top-start" placement="top-start">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
|
||||
<SlTooltip content="top" placement="top">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
|
||||
<SlTooltip content="top-end" placement="top-end">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
</div>
|
||||
|
||||
<div className="tooltip-placement-example-row">
|
||||
<SlTooltip content="left-start" placement="left-start">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
|
||||
<SlTooltip content="right-start" placement="right-start">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
</div>
|
||||
|
||||
<div className="tooltip-placement-example-row">
|
||||
<SlTooltip content="left" placement="left">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
|
||||
<SlTooltip content="right" placement="right">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
</div>
|
||||
|
||||
<div className="tooltip-placement-example-row">
|
||||
<SlTooltip content="left-end" placement="left-end">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
|
||||
<SlTooltip content="right-end" placement="right-end">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
</div>
|
||||
|
||||
<div className="tooltip-placement-example-row">
|
||||
<SlTooltip content="bottom-start" placement="bottom-start">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
|
||||
<SlTooltip content="bottom" placement="bottom">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
|
||||
<SlTooltip content="bottom-end" placement="bottom-end">
|
||||
<SlButton />
|
||||
</SlTooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Click Trigger
|
||||
|
||||
Set the `trigger` attribute to `click` to toggle the tooltip on click instead of hover.
|
||||
@@ -120,15 +231,25 @@ Set the `trigger` attribute to `click` to toggle the tooltip on click instead of
|
||||
</sl-tooltip>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlTooltip content="Click again to dismiss" trigger="click">
|
||||
<SlButton>Click to Toggle</SlButton>
|
||||
</SlTooltip>
|
||||
);
|
||||
```
|
||||
|
||||
### Manual Trigger
|
||||
|
||||
Tooltips can be controller programmatically by setting the `trigger` attribute to `manual`. Use the `open` prop to control when the tooltip is shown.
|
||||
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
|
||||
<sl-button style="margin-right: 4rem;">Toggle Manually</sl-button>
|
||||
|
||||
<sl-tooltip content="This is an avatar" trigger="manual" class="manual-tooltip">
|
||||
<sl-avatar></sl-avatar>
|
||||
<sl-avatar label="User"></sl-avatar>
|
||||
</sl-tooltip>
|
||||
|
||||
<script>
|
||||
@@ -139,20 +260,51 @@ Tooltips can be controller programmatically by setting the `trigger` attribute t
|
||||
</script>
|
||||
```
|
||||
|
||||
### Remove Arrows
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlAvatar, SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
You can control the size of tooltip arrows by overriding the `--sl-tooltip-arrow-size` design token.
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlButton style={{ marginRight: '4rem' }} onClick={() => setOpen(!open)}>
|
||||
Toggle Manually
|
||||
</SlButton>
|
||||
|
||||
<SlTooltip open={open} content="This is an avatar" trigger="manual">
|
||||
<SlAvatar />
|
||||
</SlTooltip>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Removing Arrows
|
||||
|
||||
You can control the size of tooltip arrows by overriding the `--sl-tooltip-arrow-size` design token. To remove them, set the value to `0` as shown below.
|
||||
|
||||
```html preview
|
||||
<div style="--sl-tooltip-arrow-size: 0;">
|
||||
<sl-tooltip content="This is a tooltip">
|
||||
<sl-button>Above</sl-button>
|
||||
</sl-tooltip>
|
||||
<sl-tooltip content="This is a tooltip" style="--sl-tooltip-arrow-size: 0;">
|
||||
<sl-button>No Arrow</sl-button>
|
||||
</sl-tooltip>
|
||||
```
|
||||
|
||||
<sl-tooltip content="This is a tooltip" placement="bottom">
|
||||
<sl-button>Below</sl-button>
|
||||
</sl-tooltip>
|
||||
</div>
|
||||
```jsx react
|
||||
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<div style={{ '--sl-tooltip-arrow-size': '0' }}>
|
||||
<SlTooltip content="This is a tooltip">
|
||||
<SlButton>Above</SlButton>
|
||||
</SlTooltip>
|
||||
|
||||
<SlTooltip content="This is a tooltip" placement="bottom">
|
||||
<SlButton>Below</SlButton>
|
||||
</SlTooltip>
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
To override it globally, set it in a root block in your stylesheet after the Shoelace stylesheet is loaded.
|
||||
@@ -165,13 +317,102 @@ To override it globally, set it in a root block in your stylesheet after the Sho
|
||||
|
||||
### HTML in Tooltips
|
||||
|
||||
Use the `content` slot to create tooltips with HTML content.
|
||||
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
|
||||
<sl-tooltip>
|
||||
<div slot="content">I'm not <strong>just</strong> a tooltip, I'm a <em>tooltip</em> with HTML!</div>
|
||||
|
||||
<sl-button>Hover me</sl-button>
|
||||
</sl-tooltip>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlTooltip>
|
||||
<div slot="content">
|
||||
I'm not <strong>just</strong> a tooltip, I'm a <em>tooltip</em> with HTML!
|
||||
</div>
|
||||
|
||||
<SlButton>Hover Me</SlButton>
|
||||
</SlTooltip>
|
||||
);
|
||||
```
|
||||
|
||||
### Setting a Maximum Width
|
||||
|
||||
Use the `--max-width` custom property to change the width the tooltip can grow to before wrapping occurs.
|
||||
|
||||
```html preview
|
||||
<sl-tooltip style="--max-width: 80px;" content="This tooltip will wrap after only 80 pixels.">
|
||||
<sl-button>Hover me</sl-button>
|
||||
</sl-tooltip>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlTooltip style={{ '--max-width': '80px' }} content="This tooltip will wrap after only 80 pixels.">
|
||||
<SlButton>Hover Me</SlButton>
|
||||
</SlTooltip>
|
||||
);
|
||||
```
|
||||
|
||||
### 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, 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">
|
||||
<sl-tooltip content="This is a tooltip">
|
||||
<sl-button>No Hoist</sl-button>
|
||||
</sl-tooltip>
|
||||
|
||||
<sl-tooltip content="This is a tooltip" hoist>
|
||||
<sl-button>Hoist</sl-button>
|
||||
</sl-tooltip>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.tooltip-hoist {
|
||||
position: relative;
|
||||
border: solid 2px var(--sl-panel-border-color);
|
||||
overflow: hidden;
|
||||
padding: var(--sl-spacing-medium);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.tooltip-hoist {
|
||||
border: solid 2px var(--sl-panel-border-color);
|
||||
overflow: hidden;
|
||||
padding: var(--sl-spacing-medium);
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<div class="tooltip-hoist">
|
||||
<SlTooltip content="This is a tooltip">
|
||||
<SlButton>No Hoist</SlButton>
|
||||
</SlTooltip>
|
||||
|
||||
<SlTooltip content="This is a tooltip" hoist>
|
||||
<SlButton>Hoist</SlButton>
|
||||
</SlTooltip>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-tooltip]
|
||||
|
||||