Compare commits
573 Commits
context-me
...
v2.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
10
.eslintignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.cache
|
||||
docs/dist
|
||||
docs/search.json
|
||||
docs/**/*.min.js
|
||||
dist
|
||||
examples
|
||||
node_modules
|
||||
src/react
|
||||
scripts
|
||||
|
||||
194
.eslintrc.cjs
Normal file
@@ -0,0 +1,194 @@
|
||||
/* eslint-env node */
|
||||
|
||||
module.exports = {
|
||||
plugins: ['@typescript-eslint', 'wc', 'lit', 'lit-a11y', 'chai-expect', 'chai-friendly', 'import'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:wc/recommended',
|
||||
'plugin:wc/best-practice',
|
||||
'plugin:lit/recommended',
|
||||
'plugin:lit-a11y/recommended'
|
||||
],
|
||||
env: {
|
||||
es2021: true,
|
||||
browser: true
|
||||
},
|
||||
reportUnusedDisableDirectives: 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',
|
||||
'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': 'warn',
|
||||
'no-else-return': 'warn',
|
||||
'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',
|
||||
'import/order': [
|
||||
'warn',
|
||||
{
|
||||
groups: ['builtin', 'external', 'internal', 'unknown', 'parent', 'sibling', 'index', 'object', 'type'],
|
||||
pathGroups: [
|
||||
{
|
||||
pattern: 'dist/**',
|
||||
group: 'external'
|
||||
}
|
||||
],
|
||||
alphabetize: {
|
||||
order: 'asc',
|
||||
caseInsensitive: true
|
||||
},
|
||||
'newlines-between': 'never',
|
||||
warnOnUnassignedImports: true
|
||||
}
|
||||
],
|
||||
'wc/guard-super-call': 'off'
|
||||
}
|
||||
};
|
||||
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -29,8 +29,8 @@ If applicable, add screenshots to help explain the bug.
|
||||
|
||||
### Browser / OS
|
||||
- OS: [e.g. Mac, Windows]
|
||||
- Browser [e.g. Chrome, Firefox, Safari]
|
||||
- Browser version [e.g. 22]
|
||||
- Browser: [e.g. Chrome, Firefox, Safari]
|
||||
- Browser version: [e.g. 22]
|
||||
|
||||
### Additional information
|
||||
Provide any additional information about the bug here.
|
||||
|
||||
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.
|
||||
|
||||
23
.github/workflows/node.js.yml
vendored
@@ -5,13 +5,12 @@ name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ next ]
|
||||
branches: [next]
|
||||
pull_request:
|
||||
branches: [ next ]
|
||||
branches: [next]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
@@ -20,12 +19,12 @@ jobs:
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- run: npm run build --if-present
|
||||
- run: npm test
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- run: npx playwright install-deps
|
||||
- run: npm ci
|
||||
- run: npm run verify
|
||||
|
||||
1
.gitignore
vendored
@@ -5,3 +5,4 @@ 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,10 +1,11 @@
|
||||
*.hbs
|
||||
*.md
|
||||
.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
|
||||
|
||||
5
.vscode/extensions.json
vendored
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"bierner.lit-html",
|
||||
"bashmish.es6-string-css"
|
||||
"bashmish.es6-string-css",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
]
|
||||
}
|
||||
|
||||
5
.vscode/settings.json
vendored
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
"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 >= 14 to build and run the project locally. It is preferred, but not required, to use npm 7.
|
||||
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.
|
||||
|
||||
|
||||
141
cspell.json
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"version": "0.2",
|
||||
"words": [
|
||||
"activedescendant",
|
||||
"allowfullscreen",
|
||||
"animationend",
|
||||
"Animista",
|
||||
"apos",
|
||||
"atrule",
|
||||
"autocorrect",
|
||||
"autoplay",
|
||||
"bezier",
|
||||
"boxicons",
|
||||
"chatbubble",
|
||||
"checkmark",
|
||||
"claviska",
|
||||
"Clippy",
|
||||
"codebases",
|
||||
"codepen",
|
||||
"colocated",
|
||||
"combobox",
|
||||
"Composability",
|
||||
"Consolas",
|
||||
"contenteditable",
|
||||
"copydir",
|
||||
"coverpage",
|
||||
"crossorigin",
|
||||
"crutchcorn",
|
||||
"csspart",
|
||||
"cssproperty",
|
||||
"datetime",
|
||||
"describedby",
|
||||
"Docsify",
|
||||
"dropdowns",
|
||||
"easings",
|
||||
"enterkeyhint",
|
||||
"eqeqeq",
|
||||
"erroneou",
|
||||
"esbuild",
|
||||
"exportparts",
|
||||
"fieldsets",
|
||||
"formaction",
|
||||
"formdata",
|
||||
"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",
|
||||
"minlength",
|
||||
"monospace",
|
||||
"mousedown",
|
||||
"mouseup",
|
||||
"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",
|
||||
"transitionend",
|
||||
"Triaging",
|
||||
"turbolinks",
|
||||
"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
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
import fs from 'fs';
|
||||
import commentParser from 'comment-parser';
|
||||
import { parse } from 'comment-parser';
|
||||
import { pascalCase } from 'pascal-case';
|
||||
|
||||
const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
||||
const { name, description, version, author, homepage, license } = packageData;
|
||||
// eslint-disable-next-line func-style
|
||||
const noDash = string => string.replace(/^\s?-/, '').trim();
|
||||
|
||||
export default {
|
||||
globs: ['src/components/**/*.ts'],
|
||||
exclude: ['**/*.test.ts'],
|
||||
exclude: ['**/*.styles.ts', '**/*.test.ts'],
|
||||
plugins: [
|
||||
// Append package data
|
||||
{
|
||||
name: 'shoelace-package-data',
|
||||
packageLinkPhase({ customElementsManifest, context }) {
|
||||
packageLinkPhase({ customElementsManifest }) {
|
||||
customElementsManifest.package = { name, description, version, author, homepage, license };
|
||||
}
|
||||
},
|
||||
@@ -20,9 +22,9 @@ export default {
|
||||
// Parse custom jsDoc tags
|
||||
{
|
||||
name: 'shoelace-custom-tags',
|
||||
analyzePhase({ ts, node, moduleDoc, context }) {
|
||||
analyzePhase({ ts, node, moduleDoc }) {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.ClassDeclaration:
|
||||
case ts.SyntaxKind.ClassDeclaration: {
|
||||
const className = node.name.getText();
|
||||
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
|
||||
const customTags = ['animation', 'dependency', 'since', 'status'];
|
||||
@@ -38,8 +40,8 @@ export default {
|
||||
});
|
||||
});
|
||||
|
||||
const parsed = commentParser.parse(customComments + '\n */');
|
||||
parsed[0].tags?.map(t => {
|
||||
const parsed = parse(`${customComments}\n */`);
|
||||
parsed[0].tags?.forEach(t => {
|
||||
switch (t.tag) {
|
||||
// Animations
|
||||
case 'animation':
|
||||
@@ -79,6 +81,25 @@ export default {
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
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)}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
- Getting Started
|
||||
|
||||
- [Overview](/)
|
||||
- [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)
|
||||
- [Accessibility](/resources/accessibility)
|
||||
- [Contributing](/resources/contributing)
|
||||
- [Changelog](/resources/changelog)
|
||||
|
||||
- Components
|
||||
|
||||
- [Alert](/components/alert)
|
||||
- [Avatar](/components/avatar)
|
||||
- [Badge](/components/badge)
|
||||
@@ -21,13 +33,11 @@
|
||||
- [Card](/components/card)
|
||||
- [Checkbox](/components/checkbox)
|
||||
- [Color Picker](/components/color-picker)
|
||||
- [Context Menu](/components/context-menu)
|
||||
- [Details](/components/details)
|
||||
- [Dialog](/components/dialog)
|
||||
- [Divider](/components/divider)
|
||||
- [Drawer](/components/drawer)
|
||||
- [Dropdown](/components/dropdown)
|
||||
- [Form](/components/form)
|
||||
- [Icon](/components/icon)
|
||||
- [Icon Button](/components/icon-button)
|
||||
- [Image Comparer](/components/image-comparer)
|
||||
@@ -39,12 +49,14 @@
|
||||
- [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)
|
||||
@@ -55,6 +67,7 @@
|
||||
<!--plop:component-->
|
||||
|
||||
- Utilities
|
||||
|
||||
- [Animated Image](/components/animated-image)
|
||||
- [Animation](/components/animation)
|
||||
- [Format Bytes](/components/format-bytes)
|
||||
@@ -65,8 +78,10 @@
|
||||
- [Relative Time](/components/relative-time)
|
||||
- [Resize Observer](/components/resize-observer)
|
||||
- [Responsive Media](/components/responsive-media)
|
||||
- [Visually Hidden](/components/visually-hidden)
|
||||
|
||||
- Design Tokens
|
||||
|
||||
- [Typography](/tokens/typography)
|
||||
- [Color](/tokens/color)
|
||||
- [Spacing](/tokens/spacing)
|
||||
@@ -76,6 +91,7 @@
|
||||
- [Z-index](/tokens/z-index)
|
||||
|
||||
- Tutorials
|
||||
|
||||
- [Integrating with Laravel](/tutorials/integrating-with-laravel)
|
||||
- [Integrating with NextJS](/tutorials/integrating-with-nextjs)
|
||||
- [Integrating with Rails](/tutorials/integrating-with-rails)
|
||||
- [Integrating with Rails](/tutorials/integrating-with-rails)
|
||||
|
||||
|
Before Width: | Height: | Size: 688 KiB After Width: | Height: | Size: 945 KiB |
|
Before Width: | Height: | Size: 29 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 |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
@@ -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>
|
||||
@@ -53,7 +53,7 @@
|
||||
<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="#0ea5e9" 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,13 +67,13 @@
|
||||
<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="#0ea5e9" 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>
|
||||
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@@ -1,17 +1,17 @@
|
||||
.code-block {
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
background-color: rgb(var(--sl-color-neutral-50));
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.code-block__preview {
|
||||
position: relative;
|
||||
border: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
border: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom: none;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
background-color: rgb(var(--sl-color-neutral-0));
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
min-width: 20rem;
|
||||
max-width: 100%;
|
||||
padding: 1.5rem 3.25rem 1.5rem 1.5rem;
|
||||
@@ -39,9 +39,9 @@
|
||||
bottom: 0;
|
||||
width: 1.75rem;
|
||||
font-size: 20px;
|
||||
color: rgb(var(--sl-color-neutral-600));
|
||||
background-color: rgb(var(--sl-color-neutral-0));
|
||||
border-left: solid 1px rgb(var(--sl-color-neutral-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;
|
||||
@@ -58,10 +58,9 @@
|
||||
}
|
||||
|
||||
.code-block__source {
|
||||
border: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
border: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom: none;
|
||||
border-radius: 0 !important;
|
||||
margin: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -69,9 +68,13 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.code-block__source pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.code-block__buttons {
|
||||
position: relative;
|
||||
border: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
border: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
display: flex;
|
||||
@@ -83,18 +86,31 @@
|
||||
min-width: 2.5rem;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: rgb(var(--sl-color-neutral-0));
|
||||
background: var(--sl-color-neutral-0);
|
||||
font: inherit;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
color: rgb(var(--sl-color-neutral-600));
|
||||
color: var(--sl-color-neutral-600);
|
||||
padding: 0 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.code-block__button:not(:last-of-type) {
|
||||
border-right: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
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 {
|
||||
@@ -113,20 +129,16 @@
|
||||
|
||||
.code-block__button:hover,
|
||||
.code-block__button:active {
|
||||
box-shadow: 0 0 0 1px rgb(var(--sl-color-primary-400));
|
||||
box-shadow: 0 0 0 1px var(--sl-color-primary-400);
|
||||
border-right-color: transparent;
|
||||
background-color: rgb(var(--sl-color-primary-50));
|
||||
color: rgb(var(--sl-color-primary-700));
|
||||
background-color: var(--sl-color-primary-50);
|
||||
color: var(--sl-color-primary-600);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.code-block__button:focus-visible {
|
||||
outline: none;
|
||||
color: rgb(var(--sl-color-primary-600));
|
||||
border-color: rgb(var(--sl-color-primary-400));
|
||||
border-right-color: transparent;
|
||||
background-color: rgb(var(--sl-color-primary-50));
|
||||
box-shadow: 0 0 0 1px rgb(var(--sl-color-primary-400)), var(--sl-focus-ring);
|
||||
outline: var(--sl-focus-ring);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
@@ -137,7 +149,7 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
color: rgb(var(--sl-color-neutral-600));
|
||||
color: var(--sl-color-neutral-600);
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
@@ -156,7 +168,7 @@
|
||||
.markdown-section .docsify-copy-code-button {
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
background-color: rgb(var(--sl-color-neutral-600));
|
||||
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);
|
||||
@@ -169,7 +181,7 @@
|
||||
|
||||
.markdown-section .docsify-copy-code-button.copied {
|
||||
animation: pulse 0.75s;
|
||||
--pulse-color: rgb(var(--sl-color-neutral-600));
|
||||
--pulse-color: var(--sl-color-neutral-600);
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
@@ -194,10 +206,20 @@
|
||||
}
|
||||
|
||||
.markdown-section .docsify-copy-code-button:focus-visible {
|
||||
box-shadow: 0 0 0 3px rgb(var(--sl-color-neutral-500) / 50%);
|
||||
outline: var(--sl-focus-ring);
|
||||
outline-offset: var(--sl-focus-ring-offset);
|
||||
}
|
||||
|
||||
.markdown-section .docsify-copy-code-button:active {
|
||||
background-color: rgb(var(--sl-color-neutral-600));
|
||||
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,46 +50,78 @@
|
||||
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');
|
||||
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"
|
||||
|
||||
const htmlButton = `
|
||||
<button
|
||||
type="button"
|
||||
title="Show HTML code"
|
||||
class="code-block__button code-block__button--html ${flavor === 'html' ? 'code-block__button--selected' : ''}"
|
||||
>
|
||||
<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>
|
||||
`;
|
||||
HTML
|
||||
</button>
|
||||
`;
|
||||
|
||||
[...doc.querySelectorAll('code[class^="lang-"]')].map(code => {
|
||||
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 preId = `code-block-preview-${count}`;
|
||||
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">
|
||||
@@ -65,11 +129,32 @@
|
||||
</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>
|
||||
|
||||
${
|
||||
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="false" aria-controls="${preId}">
|
||||
View Source
|
||||
${hasReact ? ` ${htmlButton} ${reactButton} ` : ''}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="code-block__button code-block__toggle"
|
||||
aria-expanded="${isExpanded ? 'true' : 'false'}"
|
||||
aria-controls="${sourceGroupId}"
|
||||
>
|
||||
Source
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
@@ -88,11 +173,15 @@
|
||||
`;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -103,85 +192,65 @@
|
||||
|
||||
// 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();
|
||||
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 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('touchstart', dragStart, { passive: true });
|
||||
}, false);
|
||||
});
|
||||
});
|
||||
|
||||
// Open in CodePen
|
||||
// Toggle source mode
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('button');
|
||||
|
||||
if (button?.classList.contains('code-block__button--codepen')) {
|
||||
const codeBlock = button.closest('.code-block');
|
||||
const html = codeBlock.querySelector('.code-block__source > code').textContent;
|
||||
const version = sessionStorage.getItem('sl-version');
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.action = 'https://codepen.io/pen/define';
|
||||
form.method = 'POST';
|
||||
form.target = '_blank';
|
||||
|
||||
// Docs: https://blog.codepen.io/documentation/prefill/
|
||||
const data = {
|
||||
title: '',
|
||||
description: '',
|
||||
tags: ['shoelace', 'web components'],
|
||||
editors: '100',
|
||||
head: `<meta name="viewport" content="width=device-width">`,
|
||||
css_external: ``,
|
||||
js_external: ``,
|
||||
js_module: true,
|
||||
html:
|
||||
`<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/themes/light.css">\n` +
|
||||
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/shoelace.js"></script>\n` +
|
||||
`\n` +
|
||||
html,
|
||||
css: `body {\n font: 16px sans-serif;\n}`,
|
||||
js: ``
|
||||
};
|
||||
|
||||
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();
|
||||
if (button?.classList.contains('code-block__button--html')) {
|
||||
setFlavor('html');
|
||||
} else if (button?.classList.contains('code-block__button--react')) {
|
||||
setFlavor('react');
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update flavor buttons
|
||||
[...document.querySelectorAll('.code-block')].forEach(codeBlock => {
|
||||
codeBlock
|
||||
.querySelector('.code-block__button--html')
|
||||
?.classList.toggle('code-block__button--selected', flavor === 'html');
|
||||
codeBlock
|
||||
.querySelector('.code-block__button--react')
|
||||
?.classList.toggle('code-block__button--selected', flavor === 'react');
|
||||
});
|
||||
});
|
||||
|
||||
// Expand and collapse code blocks
|
||||
@@ -205,4 +274,92 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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();
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
function createPropsTable(props) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -29,7 +30,7 @@
|
||||
} else if (isAttributeDifferent) {
|
||||
attributeInfo = `
|
||||
<br>
|
||||
<sl-tooltip content="This attribute is different than the property">
|
||||
<sl-tooltip content="This attribute is different from its property">
|
||||
<small>
|
||||
<code class="nowrap">
|
||||
${escapeHtml(prop.attribute)}
|
||||
@@ -64,10 +65,12 @@
|
||||
|
||||
function createEventsTable(events) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th data-flavor="html">Name</th>
|
||||
<th data-flavor="react">React Event</th>
|
||||
<th>Description</th>
|
||||
<th>Event Detail</th>
|
||||
</tr>
|
||||
@@ -77,7 +80,8 @@
|
||||
.map(
|
||||
event => `
|
||||
<tr>
|
||||
<td><code class="nowrap">${escapeHtml(event.name)}</code></td>
|
||||
<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>
|
||||
@@ -92,6 +96,7 @@
|
||||
|
||||
function createMethodsTable(methods) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -130,6 +135,7 @@
|
||||
|
||||
function createSlotsTable(slots) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -156,11 +162,13 @@
|
||||
|
||||
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>
|
||||
@@ -168,8 +176,9 @@
|
||||
.map(
|
||||
style => `
|
||||
<tr>
|
||||
<td><code>${escapeHtml(style.name)}</code></td>
|
||||
<td class="nowrap"><code>${escapeHtml(style.name)}</code></td>
|
||||
<td>${escapeHtml(style.description)}</td>
|
||||
<td>${style.default ? `<code>${escapeHtml(style.default)}</code>` : ''}</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
@@ -182,6 +191,7 @@
|
||||
|
||||
function createPartsTable(parts) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -208,6 +218,7 @@
|
||||
|
||||
function createAnimationsTable(animations) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -236,23 +247,23 @@
|
||||
const ul = document.createElement('ul');
|
||||
const dependencies = [];
|
||||
|
||||
// Recursively fetch subdependencies
|
||||
// Recursively fetch sub-dependencies
|
||||
function getDependencies(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);
|
||||
@@ -262,7 +273,11 @@
|
||||
}
|
||||
|
||||
function escapeHtml(html) {
|
||||
return (html + '')
|
||||
if (!html) {
|
||||
return '';
|
||||
}
|
||||
return html
|
||||
.toString()
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
@@ -273,8 +288,8 @@
|
||||
|
||||
function getAllComponents(metadata) {
|
||||
const allComponents = [];
|
||||
metadata.modules?.map(module => {
|
||||
module.declarations?.map(declaration => {
|
||||
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');
|
||||
@@ -295,8 +310,8 @@
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push((hook, vm) => {
|
||||
hook.mounted(async function () {
|
||||
window.$docsify.plugins.push(hook => {
|
||||
hook.mounted(async () => {
|
||||
const metadata = await customElements;
|
||||
const target = document.querySelector('.app-name');
|
||||
|
||||
@@ -314,19 +329,19 @@
|
||||
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 name="heart"></sl-icon> Sponsor
|
||||
<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 name="github"></sl-icon> <span class="github-star-count">Star</span>
|
||||
<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 name="twitter"></sl-icon> Follow
|
||||
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
|
||||
</sl-button>
|
||||
`;
|
||||
target.appendChild(buttons);
|
||||
});
|
||||
|
||||
hook.beforeEach(async function (content, next) {
|
||||
hook.beforeEach(async (content, next) => {
|
||||
const metadata = await customElements;
|
||||
|
||||
// Replace %VERSION% placeholders
|
||||
@@ -338,15 +353,23 @@
|
||||
let result = '';
|
||||
|
||||
if (!component) {
|
||||
console.error('Component not found in metadata: ' + tag);
|
||||
console.error(`Component not found in metadata: ${tag}`);
|
||||
return next(content);
|
||||
}
|
||||
|
||||
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';
|
||||
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">
|
||||
@@ -355,11 +378,11 @@
|
||||
</div>
|
||||
|
||||
<div class="component-header__info">
|
||||
<sl-badge type="neutral" 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>
|
||||
@@ -375,7 +398,7 @@
|
||||
let result = '';
|
||||
|
||||
if (!component) {
|
||||
console.error('Component not found in metadata: ' + tag);
|
||||
console.error(`Component not found in metadata: ${tag}`);
|
||||
return next(content);
|
||||
}
|
||||
|
||||
@@ -392,79 +415,27 @@
|
||||
return prop.kind === 'field' && prop.privacy !== 'private';
|
||||
});
|
||||
|
||||
if (props?.length) {
|
||||
result += `
|
||||
## Properties
|
||||
${createPropsTable(props)}
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.events?.length) {
|
||||
result += `
|
||||
## Events
|
||||
${createEventsTable(component.events)}
|
||||
`;
|
||||
}
|
||||
|
||||
if (methods?.length) {
|
||||
result += `
|
||||
## Methods
|
||||
${createMethodsTable(methods)}
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.slots?.length) {
|
||||
result += `
|
||||
## Slots
|
||||
${createSlotsTable(component.slots)}
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.cssProperties?.length) {
|
||||
result += `
|
||||
## CSS Custom Properties
|
||||
${createCustomPropertiesTable(component.cssProperties)}
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.cssParts?.length) {
|
||||
result += `
|
||||
## CSS Parts
|
||||
${createPartsTable(component.cssParts)}
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.animations?.length) {
|
||||
result += `
|
||||
## Animations
|
||||
${createAnimationsTable(component.animations)}
|
||||
|
||||
Learn how to [customize animations](/getting-started/customizing#animations).
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.dependencies?.length) {
|
||||
result += `
|
||||
## Dependencies
|
||||
|
||||
This component imports the following dependencies.
|
||||
|
||||
${createDependenciesList(component.tagName, getAllComponents(metadata))}
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.path) {
|
||||
/* prettier-ignore */
|
||||
result += `
|
||||
## Importing
|
||||
|
||||
<sl-tab-group>
|
||||
<sl-tab slot="nav" panel="cdn" active>CDN</sl-tab>
|
||||
<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="cdn">\n
|
||||
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace):
|
||||
<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}/${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}/${component.path}';
|
||||
@@ -479,12 +450,107 @@
|
||||
</sl-tab-panel>
|
||||
|
||||
<sl-tab-panel name="react">\n
|
||||
To import this component using [\`@shoelace-style/react\`](https://www.npmjs.com/package/@shoelace-style/react):
|
||||
To import this component as a [React component](/frameworks/react):
|
||||
\`\`\`js
|
||||
import '@shoelace-style/react/dist/${component.tagName.replace(/^sl-/, '')}';
|
||||
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 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 (props?.length) {
|
||||
result += `
|
||||
## Properties
|
||||
${createPropsTable(props)}
|
||||
|
||||
_Learn more about [properties and attributes](/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.slots?.length) {
|
||||
result += `
|
||||
## Slots
|
||||
${createSlotsTable(component.slots)}
|
||||
|
||||
_Learn more about [using slots](/getting-started/usage#slots)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.cssProperties?.length) {
|
||||
result += `
|
||||
## CSS Custom Properties
|
||||
${createCustomPropertiesTable(component.cssProperties)}
|
||||
|
||||
_Learn more about [customizing CSS Custom Properties](/getting-started/customizing#custom-properties)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.cssParts?.length) {
|
||||
result += `
|
||||
## CSS Parts
|
||||
${createPartsTable(component.cssParts)}
|
||||
|
||||
_Learn more about [customizing CSS Parts](/getting-started/customizing#component-parts)._
|
||||
`;
|
||||
}
|
||||
|
||||
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 imports the following dependencies.
|
||||
|
||||
${createDependenciesList(component.tagName, getAllComponents(metadata))}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -496,11 +562,11 @@
|
||||
});
|
||||
|
||||
// Wrap tables so we can scroll them horizontally when needed
|
||||
hook.doneEach(function () {
|
||||
hook.doneEach(() => {
|
||||
const content = document.querySelector('.content');
|
||||
const tables = [...content.querySelectorAll('table')];
|
||||
|
||||
tables.map(table => {
|
||||
tables.forEach(table => {
|
||||
table.outerHTML = `
|
||||
<div class="table-wrapper">
|
||||
${table.outerHTML}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
3
docs/assets/plugins/search/lunr.min.js
vendored
@@ -323,7 +323,8 @@
|
||||
w = new RegExp('([^aeiouylsz])\\1$'),
|
||||
Q = new RegExp('^' + n + i + '[^aeiouwxy]$'),
|
||||
k = /^(.+?[^aeiou])y$/,
|
||||
S = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,
|
||||
S =
|
||||
/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,
|
||||
E = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,
|
||||
L = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,
|
||||
b = /^(.+?)(s|t)(ion)$/,
|
||||
|
||||
@@ -8,6 +8,7 @@ body.site-search-visible {
|
||||
|
||||
.sidebar .search-box kbd {
|
||||
margin-top: 2px;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
/* Site search */
|
||||
@@ -30,7 +31,7 @@ body.site-search-visible {
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgb(var(--sl-overlay-background-color) / var(--sl-overlay-opacity));
|
||||
background: var(--sl-overlay-background-color);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@@ -39,7 +40,7 @@ body.site-search-visible {
|
||||
flex-direction: column;
|
||||
max-width: 460px;
|
||||
max-height: calc(100vh - 20rem);
|
||||
background-color: rgb(var(--sl-surface-base-alt));
|
||||
background-color: var(--sl-panel-background-color);
|
||||
border-radius: var(--sl-border-radius-large);
|
||||
box-shadow: var(--sl-shadow-x-large);
|
||||
margin: 10rem auto;
|
||||
@@ -74,7 +75,7 @@ body.site-search-visible {
|
||||
}
|
||||
|
||||
.site-search--has-results .site-search__body {
|
||||
border-top: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
border-top: solid 1px var(--sl-color-neutral-200);
|
||||
}
|
||||
|
||||
.site-search__results {
|
||||
@@ -97,15 +98,22 @@ body.site-search-visible {
|
||||
|
||||
.site-search__results li a:hover,
|
||||
.site-search__results li a:hover small {
|
||||
background-color: rgb(var(--sl-color-neutral-100));
|
||||
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: rgb(var(--sl-color-neutral-0));
|
||||
background-color: rgb(var(--sl-color-primary-600));
|
||||
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 {
|
||||
@@ -115,7 +123,7 @@ body.site-search-visible {
|
||||
|
||||
.site-search__results small {
|
||||
display: block;
|
||||
color: rgb(var(--sl-color-neutral-600));
|
||||
color: var(--sl-color-neutral-600);
|
||||
}
|
||||
|
||||
.site-search__result {
|
||||
@@ -132,7 +140,7 @@ body.site-search-visible {
|
||||
.site-search__result-icon {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
color: rgb(var(--sl-color-neutral-400));
|
||||
color: var(--sl-color-neutral-400);
|
||||
font-size: var(--sl-font-size-x-large);
|
||||
}
|
||||
|
||||
@@ -142,7 +150,7 @@ body.site-search-visible {
|
||||
|
||||
.site-search__empty {
|
||||
display: none;
|
||||
border-top: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
border-top: solid 1px var(--sl-color-neutral-200);
|
||||
text-align: center;
|
||||
padding: var(--sl-spacing-x-large);
|
||||
}
|
||||
@@ -155,14 +163,14 @@ body.site-search-visible {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: var(--sl-spacing-large);
|
||||
border-top: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
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: rgb(var(--sl-color-neutral-700));
|
||||
color: var(--sl-color-neutral-700);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* global lunr */
|
||||
(() => {
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push((hook, vm) => {
|
||||
window.$docsify.plugins.push(hook => {
|
||||
// Append the search box to the sidebar
|
||||
hook.mounted(function () {
|
||||
hook.mounted(() => {
|
||||
const appName = document.querySelector('.sidebar .app-name');
|
||||
const searchBox = document.createElement('div');
|
||||
searchBox.classList.add('search-box');
|
||||
@@ -51,12 +52,21 @@
|
||||
siteSearch.hidden = true;
|
||||
siteSearch.innerHTML = `
|
||||
<div class="site-search__overlay"></div>
|
||||
<div class="site-search__panel">
|
||||
<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
|
||||
>
|
||||
@@ -64,7 +74,13 @@
|
||||
</sl-input>
|
||||
</header>
|
||||
<div class="site-search__body">
|
||||
<ul class="site-search__results"></ul>
|
||||
<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">
|
||||
@@ -76,7 +92,6 @@
|
||||
`;
|
||||
document.body.append(siteSearch);
|
||||
|
||||
const searchButtons = [...document.querySelectorAll('[data-site-search]')];
|
||||
const overlay = siteSearch.querySelector('.site-search__overlay');
|
||||
const panel = siteSearch.querySelector('.site-search__panel');
|
||||
const input = siteSearch.querySelector('.site-search__input');
|
||||
@@ -89,7 +104,7 @@
|
||||
let map;
|
||||
|
||||
// Load search data
|
||||
const searchData = fetch('../../../search.json')
|
||||
fetch('../../../search.json')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
searchIndex = lunr.Index.load(data.searchIndex);
|
||||
@@ -100,7 +115,7 @@
|
||||
isShowing = true;
|
||||
document.body.classList.add('site-search-visible');
|
||||
siteSearch.hidden = false;
|
||||
input.focus();
|
||||
requestAnimationFrame(() => input.focus());
|
||||
updateResults();
|
||||
|
||||
await Promise.all([
|
||||
@@ -203,16 +218,17 @@
|
||||
}
|
||||
|
||||
// Update the selected item
|
||||
items.map(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');
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,34 +237,47 @@
|
||||
await searchIndex;
|
||||
|
||||
const hasQuery = query.length > 0;
|
||||
let matches = hasQuery ? searchIndex.search(`${query}`) : [];
|
||||
|
||||
// Fall back to a fuzzy search if no matches are found
|
||||
if (matches.length === 0 && hasQuery) {
|
||||
matches = searchIndex.search(`${query}~2`);
|
||||
}
|
||||
|
||||
let hasResults = hasQuery && matches.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.map((match, index) => {
|
||||
matches.forEach((match, index) => {
|
||||
const page = map[match.ref];
|
||||
const li = document.createElement('li');
|
||||
const a = document.createElement('a');
|
||||
let icon = 'file-text';
|
||||
|
||||
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.setAttribute('role', 'option');
|
||||
a.setAttribute('id', `search-result-item-${match.ref}`);
|
||||
|
||||
a.href = $docsify.routerMode === 'hash' ? `/#/${page.url}` : `/${page.url}`;
|
||||
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>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push((hook, vm) => {
|
||||
hook.mounted(function () {
|
||||
window.$docsify.plugins.push(hook => {
|
||||
hook.mounted(() => {
|
||||
function getTheme() {
|
||||
return localStorage.getItem('theme') || 'auto';
|
||||
}
|
||||
@@ -12,9 +12,8 @@
|
||||
function isDark() {
|
||||
if (theme === 'auto') {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
} else {
|
||||
return theme === 'dark';
|
||||
}
|
||||
return theme === 'dark';
|
||||
}
|
||||
|
||||
function setTheme(newTheme) {
|
||||
@@ -62,7 +61,7 @@
|
||||
menu.addEventListener('sl-select', event => setTheme(event.detail.item.value));
|
||||
|
||||
// Update the theme when the preference changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addListener(event => setTheme(theme));
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => setTheme(theme));
|
||||
|
||||
// Toggle themes when pressing backslash
|
||||
document.addEventListener('keydown', event => {
|
||||
@@ -73,11 +72,10 @@
|
||||
event.preventDefault();
|
||||
|
||||
setTheme(isDark() ? 'light' : 'dark');
|
||||
show();
|
||||
}
|
||||
});
|
||||
|
||||
// Set the intial theme and sync the UI
|
||||
// Set the initial theme and sync the UI
|
||||
setTheme(theme);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,17 +13,17 @@ body {
|
||||
font-size: var(--sl-font-size-medium);
|
||||
font-weight: var(--sl-font-weight-normal);
|
||||
letter-spacing: var(--sl-letter-spacing-normal);
|
||||
background-color: rgb(var(--sl-surface-base));
|
||||
color: rgb(var(--sl-color-neutral-800));
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
color: var(--sl-color-neutral-900);
|
||||
line-height: var(--sl-line-height-normal);
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgb(var(--sl-color-primary-600));
|
||||
color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: rgb(var(--sl-color-primary-700));
|
||||
color: var(--sl-color-primary-700);
|
||||
}
|
||||
|
||||
strong {
|
||||
@@ -32,8 +32,8 @@ strong {
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
background-color: rgb(var(--sl-surface-base));
|
||||
border-right: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
border-right: solid 1px var(--sl-color-neutral-200);
|
||||
}
|
||||
|
||||
.sidebar .app-name {
|
||||
@@ -43,7 +43,7 @@ strong {
|
||||
.sidebar-version {
|
||||
font-size: var(--sl-font-size-x-small);
|
||||
font-weight: var(--sl-font-weight-normal);
|
||||
color: rgb(var(--sl-color-neutral-500));
|
||||
color: var(--sl-color-neutral-500);
|
||||
text-align: right;
|
||||
padding: 0 var(--sl-spacing-small);
|
||||
margin: -1.25rem 0 0.6rem 0;
|
||||
@@ -61,7 +61,7 @@ strong {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
background-color: rgb(var(--sl-surface-base));
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -70,12 +70,12 @@ strong {
|
||||
}
|
||||
|
||||
.sidebar-toggle:active .sidebar-toggle-button span {
|
||||
background-color: rgb(var(--sl-color-primary-600));
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.sidebar-toggle:focus {
|
||||
outline: none;
|
||||
box-shadow: var(--sl-focus-ring);
|
||||
outline: var(--sl-focus-ring);
|
||||
outline-offset: var(--sl-focus-ring-offset);
|
||||
}
|
||||
|
||||
.sidebar-toggle span:last-child {
|
||||
@@ -110,12 +110,12 @@ strong {
|
||||
|
||||
.sidebar-nav li.collapse > a,
|
||||
.sidebar-nav li.active > a {
|
||||
color: rgb(var(--sl-color-primary-600));
|
||||
color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.sidebar li > p {
|
||||
font-weight: var(--sl-font-weight-bold);
|
||||
border-bottom: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
border-bottom: solid 1px var(--sl-color-neutral-200);
|
||||
margin: 0 0.75rem 0.5rem 0;
|
||||
}
|
||||
|
||||
@@ -197,16 +197,15 @@ strong {
|
||||
|
||||
.markdown-section {
|
||||
max-width: 860px;
|
||||
overflow-anchor: none;
|
||||
}
|
||||
|
||||
.anchor span {
|
||||
color: rgb(var(--sl-color-neutral-1000));
|
||||
color: var(--sl-color-neutral-1000);
|
||||
}
|
||||
|
||||
.markdown-section blockquote {
|
||||
position: relative;
|
||||
border-left: solid 4px rgb(var(--sl-color-neutral-200));
|
||||
border-left: solid 4px var(--sl-color-neutral-200);
|
||||
font-style: italic;
|
||||
padding: 1rem 1.5rem;
|
||||
margin: 0 0 1rem 0;
|
||||
@@ -230,7 +229,7 @@ strong {
|
||||
}
|
||||
|
||||
.docsify-pagination-container {
|
||||
border-top-color: rgb(var(--sl-color-neutral-200)) !important;
|
||||
border-top-color: var(--sl-color-neutral-200) !important;
|
||||
}
|
||||
|
||||
.pagination-item-label,
|
||||
@@ -255,7 +254,7 @@ strong {
|
||||
|
||||
.markdown-section h2 {
|
||||
font-size: var(--sl-font-size-x-large);
|
||||
border-bottom: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
border-bottom: solid 1px var(--sl-color-neutral-200);
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
@@ -286,30 +285,26 @@ strong {
|
||||
.markdown-section code {
|
||||
font-family: var(--sl-font-mono);
|
||||
font-size: 87.5%;
|
||||
background-color: rgb(var(--sl-color-neutral-50));
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
border-radius: var(--sl-border-radius-small);
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
.markdown-section tr:nth-child(2n) code {
|
||||
background-color: rgb(var(--sl-color-neutral-100));
|
||||
}
|
||||
|
||||
kbd,
|
||||
.markdown-section kbd {
|
||||
font-family: var(--sl-font-mono);
|
||||
font-size: 87.5%;
|
||||
background-color: rgb(var(--sl-color-neutral-50));
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
border-radius: var(--sl-border-radius-small);
|
||||
border: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
box-shadow: inset 0 1px 0 rgb(var(--sl-color-neutral-0));
|
||||
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-color: rgb(var(--sl-color-neutral-50));
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
}
|
||||
|
||||
@@ -317,7 +312,7 @@ kbd,
|
||||
display: block;
|
||||
background: none;
|
||||
border-radius: 0;
|
||||
color: rgb(var(--sl-color-neutral-800));
|
||||
color: var(--sl-color-neutral-900);
|
||||
padding: var(--sl-spacing-medium);
|
||||
overflow: auto;
|
||||
hyphens: none;
|
||||
@@ -325,18 +320,18 @@ kbd,
|
||||
}
|
||||
|
||||
.markdown-section pre .token.comment {
|
||||
color: rgb(var(--sl-color-neutral-500));
|
||||
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: rgb(var(--sl-color-neutral-600));
|
||||
color: var(--sl-color-neutral-700);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.punctuation {
|
||||
color: rgb(var(--sl-color-neutral-600));
|
||||
color: var(--sl-color-neutral-700);
|
||||
}
|
||||
|
||||
.namespace {
|
||||
@@ -347,12 +342,12 @@ kbd,
|
||||
.markdown-section pre .token.keyword,
|
||||
.markdown-section pre .token.tag,
|
||||
.markdown-section pre .token.url {
|
||||
color: rgb(var(--sl-color-sky-600));
|
||||
color: var(--sl-color-sky-800);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.symbol,
|
||||
.markdown-section pre .token.deleted {
|
||||
color: rgb(var(--sl-color-pink-600));
|
||||
color: var(--sl-color-pink-700);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.boolean,
|
||||
@@ -363,24 +358,24 @@ kbd,
|
||||
.markdown-section pre .token.char,
|
||||
.markdown-section pre .token.builtin,
|
||||
.markdown-section pre .token.inserted {
|
||||
color: rgb(var(--sl-color-emerald-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: rgb(var(--sl-color-violet-600));
|
||||
color: var(--sl-color-violet-700);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.function,
|
||||
.markdown-section pre .token.class-name,
|
||||
.markdown-section pre .token.regex {
|
||||
color: rgb(var(--sl-color-orange-600));
|
||||
color: var(--sl-color-orange-700);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.important {
|
||||
color: rgb(var(--sl-color-red-600));
|
||||
color: var(--sl-color-red-700);
|
||||
}
|
||||
|
||||
.markdown-section pre .token.important,
|
||||
@@ -413,7 +408,7 @@ kbd,
|
||||
}
|
||||
|
||||
.markdown-section tr:nth-child(2n) {
|
||||
background: rgb(var(--sl-color-neutral-50));
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.markdown-section th {
|
||||
@@ -423,10 +418,11 @@ kbd,
|
||||
}
|
||||
|
||||
.markdown-section td {
|
||||
border-top: solid 1px rgb(var(--sl-color-neutral-200));
|
||||
border-bottom: solid 1px rgb(var(--sl-color-neutral-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 table .nowrap {
|
||||
@@ -434,10 +430,14 @@ kbd,
|
||||
}
|
||||
|
||||
.markdown-section table sl-tooltip code {
|
||||
border-bottom: dashed 1px rgb(var(--sl-color-neutral-300));
|
||||
border-bottom: dashed 1px var(--sl-color-neutral-300);
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.markdown-section .metadata-table {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
/* Iframes */
|
||||
.markdown-section iframe {
|
||||
border: none;
|
||||
@@ -447,7 +447,7 @@ kbd,
|
||||
.markdown-section p.tip,
|
||||
.markdown-section p.warn {
|
||||
position: relative;
|
||||
background-color: rgb(var(--sl-color-neutral-50));
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
border-left: solid 4px transparent;
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
padding-left: 1.5rem;
|
||||
@@ -457,7 +457,7 @@ kbd,
|
||||
.markdown-section p.warn:before {
|
||||
content: '!';
|
||||
border-radius: 100%;
|
||||
color: rgb(var(--sl-color-neutral-0));
|
||||
color: var(--sl-color-neutral-0);
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
left: -12px;
|
||||
@@ -470,29 +470,43 @@ kbd,
|
||||
}
|
||||
|
||||
.markdown-section p.warn {
|
||||
border-left-color: rgb(var(--sl-color-primary-600));
|
||||
border-left-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.markdown-section p.warn:before {
|
||||
background-color: rgb(var(--sl-color-primary-600));
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.markdown-section p.tip {
|
||||
border-left-color: rgb(var(--sl-color-danger-600));
|
||||
border-left-color: var(--sl-color-danger-600);
|
||||
}
|
||||
|
||||
.markdown-section p.tip:before {
|
||||
background-color: rgb(var(--sl-color-danger-600));
|
||||
background-color: var(--sl-color-danger-600);
|
||||
}
|
||||
|
||||
.markdown-section p.tip code,
|
||||
.markdown-section p.warn code {
|
||||
background-color: rgb(var(--sl-color-neutral-100));
|
||||
background-color: var(--sl-color-neutral-100);
|
||||
}
|
||||
|
||||
/* 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 rgb(var(--sl-color-neutral-200));
|
||||
border-bottom: solid 1px var(--sl-color-neutral-200);
|
||||
padding-bottom: 2rem;
|
||||
margin-top: -1rem;
|
||||
margin-bottom: 2rem;
|
||||
@@ -506,7 +520,7 @@ kbd,
|
||||
|
||||
.markdown-section .component-header__tag code {
|
||||
background: none;
|
||||
color: rgb(var(--sl-color-neutral-600));
|
||||
color: var(--sl-color-neutral-600);
|
||||
font-size: var(--sl-font-size-large);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -524,15 +538,15 @@ kbd,
|
||||
|
||||
/* Repo buttons */
|
||||
.repo-button--sponsor sl-icon {
|
||||
color: rgb(var(--sl-color-pink-600));
|
||||
color: var(--sl-color-pink-600);
|
||||
}
|
||||
|
||||
.repo-button--github sl-icon {
|
||||
color: rgb(var(--sl-color-neutral-700));
|
||||
color: var(--sl-color-neutral-700);
|
||||
}
|
||||
|
||||
.repo-button--twitter sl-icon {
|
||||
color: rgb(var(--sl-color-sky-500));
|
||||
color: var(--sl-color-sky-500);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
@@ -551,13 +565,13 @@ body[data-page^='/tokens/'] .table-wrapper td:first-child code {
|
||||
.border-radius-demo {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
background: rgb(var(--sl-color-primary-600));
|
||||
background: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
/* Transition demo */
|
||||
.transition-demo {
|
||||
position: relative;
|
||||
background: rgb(var(--sl-color-neutral-200));
|
||||
background: var(--sl-color-neutral-200);
|
||||
width: 8rem;
|
||||
height: 2rem;
|
||||
}
|
||||
@@ -565,7 +579,7 @@ body[data-page^='/tokens/'] .table-wrapper td:first-child code {
|
||||
.transition-demo:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: rgb(var(--sl-color-primary-600));
|
||||
background-color: var(--sl-color-primary-600);
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
@@ -581,10 +595,10 @@ body[data-page^='/tokens/'] .table-wrapper td:first-child code {
|
||||
/* Spacing demo */
|
||||
.spacing-demo {
|
||||
width: 100px;
|
||||
background: rgb(var(--sl-color-primary-600));
|
||||
background: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
/* Elevation dmeo */
|
||||
/* Elevation demo */
|
||||
.elevation-demo {
|
||||
background: transparent;
|
||||
border-radius: 3px;
|
||||
@@ -623,7 +637,7 @@ body[data-page^='/tokens/'] .table-wrapper td:first-child code {
|
||||
}
|
||||
|
||||
.color-palette__swatch--border {
|
||||
box-shadow: inset 0 0 0 1px rgb(var(--sl-color-neutral-1000) / 10%);
|
||||
box-shadow: inset 0 0 0 1px var(--sl-color-neutral-300);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[component-header:sl-alert]
|
||||
|
||||
Alerts are used to display important messages either inline or as toast notifications.
|
||||
Alerts are used to display important messages inline or as toast notifications.
|
||||
|
||||
```html preview
|
||||
<sl-alert open>
|
||||
@@ -11,60 +11,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="neutral" 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,19 +134,48 @@ 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
|
||||
@@ -93,9 +184,9 @@ Set the `duration` attribute to automatically hide an alert after a period of ti
|
||||
|
||||
```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,6 +207,38 @@ Set the `duration` attribute to automatically hide an alert after a period of ti
|
||||
</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.
|
||||
@@ -124,39 +247,39 @@ You should always use the `closable` attribute so users can dismiss the notifica
|
||||
|
||||
```html preview
|
||||
<div class="alert-toast">
|
||||
<sl-button type="primary">Primary</sl-button>
|
||||
<sl-button type="success">Success</sl-button>
|
||||
<sl-button type="neutral">Neutral</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="neutral" 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 +287,98 @@ You should always use the `closable` attribute so users can dismiss the notifica
|
||||
<script>
|
||||
const container = document.querySelector('.alert-toast');
|
||||
|
||||
['primary', 'success', 'neutral', '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 +391,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: `
|
||||
|
||||
@@ -5,12 +5,25 @@
|
||||
A component for displaying animated GIFs and WEBPs that play and pause on interaction.
|
||||
|
||||
```html preview
|
||||
<sl-animated-image
|
||||
src="/assets/images/walk.gif"
|
||||
<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
|
||||
@@ -18,32 +31,52 @@ A component for displaying animated GIFs and WEBPs that play and pause on intera
|
||||
Both GIF and WEBP images are supported.
|
||||
|
||||
```html preview
|
||||
<sl-animated-image
|
||||
src="/assets/images/tie.webp"
|
||||
<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="/assets/images/walk.gif"
|
||||
<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="/assets/images/walk.gif"
|
||||
<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>
|
||||
@@ -55,9 +88,38 @@ You can change the appearance and location of the control box by targeting the `
|
||||
bottom: 1rem;
|
||||
left: 1rem;
|
||||
background-color: deeppink;
|
||||
color: white;
|
||||
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]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
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 an animation `name`. The animation not start until you add the `play` attribute. Refer to the [properties table](#properties) for a list of all animation 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">
|
||||
@@ -19,12 +19,47 @@ To animate an element, wrap it in `<sl-animation>` and set an animation `name`.
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: rgb(var(--sl-color-primary-600));
|
||||
background-color: var(--sl-color-primary-600);
|
||||
margin: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```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
|
||||
@@ -42,7 +77,7 @@ This example demonstrates all of the baked-in animations and easings. Animations
|
||||
<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>
|
||||
|
||||
@@ -53,7 +88,7 @@ This example demonstrates all of the baked-in animations and easings. Animations
|
||||
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();
|
||||
|
||||
@@ -64,7 +99,7 @@ This example demonstrates all of the baked-in animations and easings. Animations
|
||||
});
|
||||
animationName.appendChild(menuItem);
|
||||
});
|
||||
|
||||
|
||||
easings.map(name => {
|
||||
const menuItem = Object.assign(document.createElement('sl-menu-item'), {
|
||||
textContent: name,
|
||||
@@ -73,17 +108,16 @@ This example demonstrates all of the baked-in animations and easings. Animations
|
||||
easingName.appendChild(menuItem);
|
||||
});
|
||||
|
||||
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}`;
|
||||
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: rgb(var(--sl-color-primary-600));
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.animation-sandbox .controls {
|
||||
@@ -129,11 +163,61 @@ Use an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: rgb(var(--sl-color-primary-600));
|
||||
}
|
||||
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.
|
||||
@@ -169,11 +253,55 @@ Supply your own [keyframe formats](https://developer.mozilla.org/en-US/docs/Web/
|
||||
.animation-keyframes .box {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: rgb(var(--sl-color-primary-600));
|
||||
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.
|
||||
@@ -181,7 +309,7 @@ Animations won't play until you apply the `play` attribute. You can omit it init
|
||||
```html preview
|
||||
<div class="animation-form">
|
||||
<sl-animation name="rubberBand" duration="1000" iterations="1">
|
||||
<sl-button type="primary">Click me</sl-button>
|
||||
<sl-button variant="primary">Click me</sl-button>
|
||||
</sl-animation>
|
||||
</div>
|
||||
|
||||
@@ -196,4 +324,23 @@ Animations won't play until you apply the `play` attribute. You can omit it init
|
||||
</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]
|
||||
|
||||
@@ -4,29 +4,54 @@
|
||||
|
||||
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.
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
```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"
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### Initials
|
||||
|
||||
When you don't have an image to use, you can set the `initials` attribute to show something more personalized than an icon.
|
||||
|
||||
```html preview
|
||||
<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 +59,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 +120,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 +147,51 @@ You can group avatars with a few lines of CSS.
|
||||
}
|
||||
|
||||
.avatar-group sl-avatar::part(base) {
|
||||
border: solid 2px rgb(var(--sl-color-neutral-0));
|
||||
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]
|
||||
|
||||
@@ -8,18 +8,38 @@ Badges are used to draw attention and display statuses or counts.
|
||||
<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="neutral">Neutral</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 +47,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="neutral" pill>Neutral</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 +84,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="neutral" 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 +98,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 +142,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 rgb(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="neutral" pill>4</sl-badge></sl-menu-item>
|
||||
<sl-menu-item>Replies <sl-badge slot="suffix" type="neutral" 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]
|
||||
|
||||
@@ -15,6 +15,21 @@ Breadcrumb Items are used inside [breadcrumbs](/components/breadcrumb) to repres
|
||||
</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]
|
||||
|
||||
@@ -15,6 +15,19 @@ Breadcrumbs are usually placed before a page's main content with the current pag
|
||||
</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
|
||||
@@ -25,24 +38,32 @@ For websites, you'll probably want to use links instead. You can make any breadc
|
||||
|
||||
```html preview
|
||||
<sl-breadcrumb>
|
||||
<sl-breadcrumb-item href="https://example.com/home">
|
||||
Homepage
|
||||
</sl-breadcrumb-item>
|
||||
<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">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">Digital Media</sl-breadcrumb-item>
|
||||
|
||||
<sl-breadcrumb-item href="https://example.com/home/services/digital/web-design">
|
||||
Web Design
|
||||
</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.
|
||||
@@ -52,28 +73,62 @@ Use the `separator` slot to change the separator that goes between breadcrumb it
|
||||
<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-item>Third</sl-breadcrumb-item>
|
||||
</sl-breadcrumb>
|
||||
|
||||
<br>
|
||||
<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-item>Third</sl-breadcrumb-item>
|
||||
</sl-breadcrumb>
|
||||
|
||||
<br>
|
||||
<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-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.
|
||||
@@ -89,6 +144,21 @@ Use the `prefix` slot to add content before any 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.
|
||||
@@ -104,6 +174,21 @@ Use the `suffix` slot to add content after any 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.
|
||||
@@ -124,9 +209,42 @@ Dropdown menus can be placed in a prefix or suffix slot to provide additional op
|
||||
<sl-menu-item>Web Development</sl-menu-item>
|
||||
<sl-menu-item>Marketing</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
</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 checked>Web Design</SlMenuItem>
|
||||
<SlMenuItem>Web Development</SlMenuItem>
|
||||
<SlMenuItem>Marketing</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
</SlBreadcrumbItem>
|
||||
</SlBreadcrumb>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-breadcrumb]
|
||||
|
||||
@@ -12,6 +12,18 @@ Button groups can be used to group related buttons into sections.
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlButtonGroup>
|
||||
<SlButton>Left</SlButton>
|
||||
<SlButton>Center</SlButton>
|
||||
<SlButton>Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Button Sizes
|
||||
@@ -25,7 +37,7 @@ All button sizes are supported, but avoid mixing sizes within the same button gr
|
||||
<sl-button size="small">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button size="medium">Left</sl-button>
|
||||
@@ -33,7 +45,7 @@ All button sizes are supported, but avoid mixing sizes within the same button gr
|
||||
<sl-button size="medium">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button size="large">Left</sl-button>
|
||||
@@ -42,50 +54,132 @@ All button sizes are supported, but avoid mixing sizes within the same button gr
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButtonGroup>
|
||||
<SlButton size="small">Left</SlButton>
|
||||
<SlButton size="small">Center</SlButton>
|
||||
<SlButton size="small">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup>
|
||||
<SlButton size="medium">Left</SlButton>
|
||||
<SlButton size="medium">Center</SlButton>
|
||||
<SlButton size="medium">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup>
|
||||
<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.
|
||||
|
||||
```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 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 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="neutral">Left</sl-button>
|
||||
<sl-button type="neutral">Center</sl-button>
|
||||
<sl-button type="neutral">Right</sl-button>
|
||||
<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 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 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>
|
||||
<SlButton variant="primary">Left</SlButton>
|
||||
<SlButton variant="primary">Center</SlButton>
|
||||
<SlButton variant="primary">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup>
|
||||
<SlButton variant="success">Left</SlButton>
|
||||
<SlButton variant="success">Center</SlButton>
|
||||
<SlButton variant="success">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup>
|
||||
<SlButton variant="neutral">Left</SlButton>
|
||||
<SlButton variant="neutral">Center</SlButton>
|
||||
<SlButton variant="neutral">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup>
|
||||
<SlButton variant="warning">Left</SlButton>
|
||||
<SlButton variant="warning">Center</SlButton>
|
||||
<SlButton variant="warning">Right</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup>
|
||||
<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.
|
||||
@@ -97,7 +191,7 @@ Pill buttons are supported through the button's `pill` attribute.
|
||||
<sl-button size="small" pill>Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button size="medium" pill>Left</sl-button>
|
||||
@@ -105,7 +199,7 @@ Pill buttons are supported through the button's `pill` attribute.
|
||||
<sl-button size="medium" pill>Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
<br /><br />
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button size="large" pill>Left</sl-button>
|
||||
@@ -114,12 +208,62 @@ Pill buttons are supported through the button's `pill` attribute.
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlButtonGroup>
|
||||
<SlButton size="small" pill>
|
||||
Left
|
||||
</SlButton>
|
||||
<SlButton size="small" pill>
|
||||
Center
|
||||
</SlButton>
|
||||
<SlButton size="small" pill>
|
||||
Right
|
||||
</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup>
|
||||
<SlButton size="medium" pill>
|
||||
Left
|
||||
</SlButton>
|
||||
<SlButton size="medium" pill>
|
||||
Center
|
||||
</SlButton>
|
||||
<SlButton size="medium" pill>
|
||||
Right
|
||||
</SlButton>
|
||||
</SlButtonGroup>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<SlButtonGroup>
|
||||
<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 an `<sl-button>` element.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button-group>
|
||||
<sl-button>Button</sl-button>
|
||||
<sl-button>Button</sl-button>
|
||||
<sl-dropdown>
|
||||
@@ -133,15 +277,38 @@ Dropdowns can be placed inside button groups as long as the trigger is an `<sl-b
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlButtonGroup, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlButtonGroup>
|
||||
<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>
|
||||
<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,6 +318,24 @@ 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>
|
||||
<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.
|
||||
@@ -171,6 +356,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>
|
||||
<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 +386,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 +425,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]
|
||||
|
||||
@@ -8,19 +8,40 @@ Buttons represent actions that are available to the user.
|
||||
<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="neutral">Neutral</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
|
||||
@@ -33,17 +54,56 @@ Use the `size` attribute 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 type="default" outline>Default</sl-button>
|
||||
<sl-button type="primary" outline>Primary</sl-button>
|
||||
<sl-button type="success" outline>Success</sl-button>
|
||||
<sl-button type="neutral" outline>Neutral</sl-button>
|
||||
<sl-button type="warning" outline>Warning</sl-button>
|
||||
<sl-button type="danger" outline>Danger</sl-button>
|
||||
<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
|
||||
@@ -56,24 +116,86 @@ Use the `pill` attribute 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` attribute to create circular icon buttons.
|
||||
|
||||
```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
|
||||
@@ -87,16 +209,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
|
||||
@@ -104,59 +263,121 @@ 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` attribute to add a dropdown indicator when a button will trigger a dropdown, menu, or popover.
|
||||
@@ -167,17 +388,62 @@ Use the `caret` attribute to add a dropdown indicator when a button will trigger
|
||||
<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` 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="neutral" loading>Neutral</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
|
||||
@@ -185,17 +451,49 @@ Use the `loading` attribute to make a button busy. The width will remain the sam
|
||||
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="neutral" disabled>Neutral</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>
|
||||
@@ -205,7 +503,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;
|
||||
|
||||
@@ -6,18 +6,18 @@ 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 +28,27 @@ Cards can be used to group related subjects in a container.
|
||||
}
|
||||
|
||||
.card-overview small {
|
||||
color: rgb(var(--sl-color-neutral-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 +56,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 +102,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.
|
||||
@@ -66,33 +131,71 @@ Headers can be used to display titles and more.
|
||||
<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 +206,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 slot="footer" variant="primary">Preview</sl-button>
|
||||
</div>
|
||||
</sl-card>
|
||||
|
||||
@@ -112,12 +215,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 +261,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 +276,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]
|
||||
|
||||
@@ -8,7 +8,13 @@ Checkboxes allow the user to toggle an option on or off.
|
||||
<sl-checkbox>Checkbox</sl-checkbox>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) 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 +26,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 +40,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 +54,70 @@ 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>;
|
||||
```
|
||||
|
||||
### 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]
|
||||
|
||||
@@ -5,17 +5,45 @@
|
||||
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
|
||||
|
||||
### Initial Value
|
||||
|
||||
Use the `value` attribute to set an initial value for the color picker.
|
||||
|
||||
```html preview
|
||||
<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, or HSLA based on `format`.
|
||||
|
||||
```html preview
|
||||
<sl-color-picker opacity></sl-color-picker>
|
||||
<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
|
||||
@@ -25,9 +53,21 @@ Set the color picker's format with the `format` attribute. Valid options include
|
||||
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>
|
||||
```
|
||||
|
||||
```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%)" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizes
|
||||
@@ -35,18 +75,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,140 +0,0 @@
|
||||
# Context Menu
|
||||
|
||||
[component-header:sl-context-menu]
|
||||
|
||||
Context menus offer additional options through a menu that opens at the pointer's location, usually activated by a right-click.
|
||||
|
||||
Context menus are designed to work with [menus](/components/menu) and [menu items](/components/menu-item). The menu must include `slot="menu"`. Other content you provide will be part of the context menu's target area.
|
||||
|
||||
```html preview
|
||||
<sl-context-menu>
|
||||
<div style="height: 200px; background: rgb(var(--sl-color-neutral-100)); display: flex; align-items: center; justify-content: center; padding: 1rem;">
|
||||
Right-click to activate the context menu
|
||||
</div>
|
||||
|
||||
<sl-menu slot="menu">
|
||||
<sl-menu-item value="undo">Undo</sl-menu-item>
|
||||
<sl-menu-item value="redo">Redo</sl-menu-item>
|
||||
<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>
|
||||
<sl-menu-item value="delete">Delete</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-context-menu>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Handling Selections
|
||||
|
||||
The [menu component](/components/menu) emits an `sl-select` event when a menu item is selected. You can use this to handle selections. The selected item will be available in `event.detail.item`.
|
||||
|
||||
```html preview
|
||||
<div class="context-menu-selections">
|
||||
<sl-context-menu>
|
||||
<div style="height: 200px; background: rgb(var(--sl-color-neutral-100)); display: flex; align-items: center; justify-content: center; padding: 1rem;">
|
||||
Right-click to activate the context menu
|
||||
</div>
|
||||
|
||||
<sl-menu slot="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-context-menu>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.context-menu-selections');
|
||||
const menu = container.querySelector('sl-menu');
|
||||
const result = container.querySelector('.result');
|
||||
|
||||
menu.addEventListener('sl-select', event => {
|
||||
console.log(`You selected: ${event.detail.item.value}`);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### Inline
|
||||
|
||||
The context menu uses `display: contents`, so it will assume the shape of the content you slot in.
|
||||
|
||||
```html preview
|
||||
<sl-context-menu>
|
||||
<span style="background: rgb(var(--sl-color-neutral-100)); padding: .5rem 1rem;">
|
||||
Right-click here
|
||||
</span>
|
||||
|
||||
<sl-menu slot="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-context-menu>
|
||||
```
|
||||
|
||||
### Placement
|
||||
|
||||
The preferred placement of the context menu can be set with the `placement` attribute. Note that the actual position may vary to ensure the menu remains in the viewport.
|
||||
|
||||
```html preview
|
||||
<sl-context-menu placement="top-end">
|
||||
<div style="height: 200px; background: rgb(var(--sl-color-neutral-100)); display: flex; align-items: center; justify-content: center; padding: 1rem;">
|
||||
Right-click to activate the context menu
|
||||
</div>
|
||||
|
||||
<sl-menu slot="menu">
|
||||
<sl-menu-item value="undo">Undo</sl-menu-item>
|
||||
<sl-menu-item value="redo">Redo</sl-menu-item>
|
||||
<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>
|
||||
<sl-menu-item value="delete">Delete</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-context-menu>
|
||||
```
|
||||
|
||||
### Detecting the Target Item
|
||||
|
||||
A single context menu can wrap a number of items. To detect the item that activated the context menu...
|
||||
|
||||
TODO
|
||||
|
||||
```html preview
|
||||
<div class="context-menu-detecting">
|
||||
<sl-context-menu>
|
||||
<ul>
|
||||
<li>Item 1</li>
|
||||
<li>Item 2</li>
|
||||
<li>Item 3</li>
|
||||
<li>Item 4</li>
|
||||
<li>Item 5</li>
|
||||
</ul>
|
||||
|
||||
<sl-menu slot="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-context-menu>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.context-menu-detecting ul {
|
||||
max-width: 300px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.context-menu-detecting li {
|
||||
background: rgb(var(--sl-color-neutral-100));
|
||||
padding: .5rem 1rem;
|
||||
margin: 0 0 2px 0;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
[component-metadata:sl-context-menu]
|
||||
@@ -1,3 +1,5 @@
|
||||
<!-- cspell:dictionaries lorem-ipsum -->
|
||||
|
||||
# Details
|
||||
|
||||
[component-header:sl-details]
|
||||
@@ -11,6 +13,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 +37,17 @@ 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>
|
||||
);
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<!-- cspell:dictionaries lorem-ipsum -->
|
||||
|
||||
# Dialog
|
||||
|
||||
[component-header:sl-dialog]
|
||||
@@ -7,7 +9,7 @@ Dialogs, sometimes called "modals", appear above the page and require the user's
|
||||
```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 +24,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 +61,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 +76,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 rgb(var(--sl-color-neutral-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,16 +122,49 @@ By design, a dialog's height will never exceed that of the viewport. As such, di
|
||||
</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)}>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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 unless you use the button below.
|
||||
<sl-button slot="footer" type="primary">Save & Close</sl-button>
|
||||
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>
|
||||
@@ -93,23 +172,57 @@ To keep the dialog open in such cases, you can cancel the `sl-request-close` eve
|
||||
<script>
|
||||
const dialog = document.querySelector('.dialog-deny-close');
|
||||
const openButton = dialog.nextElementSibling;
|
||||
const saveButton = dialog.querySelector('sl-button[slot="footer"]');
|
||||
const closeButton = dialog.querySelector('sl-button[slot="footer"]');
|
||||
|
||||
openButton.addEventListener('click', () => dialog.show());
|
||||
saveButton.addEventListener('click', () => dialog.hide());
|
||||
closeButton.addEventListener('click', () => dialog.hide());
|
||||
|
||||
dialog.addEventListener('sl-request-close', 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. This allows the first tab press to focus on the first tabbable element within the dialog. To set focus on a different element, listen for and cancel 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>
|
||||
@@ -122,12 +235,31 @@ By default, the dialog's panel will gain focus when opened. This allows the firs
|
||||
|
||||
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]
|
||||
|
||||
@@ -8,6 +8,12 @@ Dividers are used to visually separate or group elements.
|
||||
<sl-divider></sl-divider>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlDivider />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Width
|
||||
@@ -18,6 +24,12 @@ Use the `--width` custom property to change the width of the divider.
|
||||
<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.
|
||||
@@ -26,6 +38,12 @@ Use the `--color` custom property to change the color of the divider.
|
||||
<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.
|
||||
@@ -38,6 +56,18 @@ Use the `--spacing` custom property to change the amount of space between the di
|
||||
</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.
|
||||
@@ -52,12 +82,32 @@ Add the `vertical` attribute to draw the divider in a vertical orientation. The
|
||||
</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; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);">
|
||||
<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>
|
||||
@@ -68,4 +118,20 @@ Use dividers in [menus](/components/menu) to visually group menu items.
|
||||
</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,3 +1,5 @@
|
||||
<!-- cspell:dictionaries lorem-ipsum -->
|
||||
|
||||
# Drawer
|
||||
|
||||
[component-header:sl-drawer]
|
||||
@@ -7,7 +9,7 @@ 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,13 +17,35 @@ 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 Start
|
||||
@@ -31,7 +55,7 @@ By default, drawers slide in from the end. To make the drawer slide in from the
|
||||
```html preview
|
||||
<sl-drawer label="Drawer" placement="start" class="drawer-placement-start">
|
||||
This drawer slides in from the start.
|
||||
<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>
|
||||
@@ -39,13 +63,35 @@ By default, drawers slide in from the end. To make the drawer slide in from the
|
||||
<script>
|
||||
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 +99,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 +107,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 +143,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,26 +151,48 @@ 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` attribute and `position: relative` to the parent.
|
||||
|
||||
```html preview
|
||||
<div
|
||||
style="position: relative; border: solid 2px rgb(var(--sl-panel-border-color)); height: 300px; padding: 1rem; margin-bottom: 1rem;"
|
||||
style="position: relative; border: solid 2px var(--sl-panel-border-color); height: 300px; padding: 1rem; margin-bottom: 1rem;"
|
||||
>
|
||||
The drawer will be contained to this box. This content won't shift or be affected in any way when the drawer opens.
|
||||
|
||||
<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>
|
||||
|
||||
@@ -111,13 +201,47 @@ By default, the drawer slides out of its [containing block](https://developer.mo
|
||||
<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());
|
||||
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 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 +249,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 +257,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 rgb(var(--sl-color-neutral-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,24 +303,55 @@ 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>
|
||||
```
|
||||
|
||||
```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)}>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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 dialog will not close unless you use the button below.
|
||||
<sl-button slot="footer" type="primary">Save & Close</sl-button>
|
||||
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>
|
||||
@@ -182,23 +359,57 @@ To keep the drawer open in such cases, you can cancel the `sl-request-close` eve
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-deny-close');
|
||||
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-request-close', 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. This allows the first tab press to focus on the first tabbable element within the drawer. To set focus on a different element, listen for and cancel 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>
|
||||
@@ -207,16 +418,34 @@ By default, the drawer's panel will gain focus when opened. This allows the firs
|
||||
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]
|
||||
|
||||
@@ -31,6 +31,35 @@ Dropdowns are designed to work well with [menus](/components/menu) to provide a
|
||||
</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 checked>Checked</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
|
||||
@@ -60,6 +89,87 @@ When dropdowns are used with [menus](/components/menu), you can listen for the `
|
||||
</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.
|
||||
@@ -78,6 +188,26 @@ The preferred placement of the dropdown can be set with the `placement` attribut
|
||||
</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.
|
||||
@@ -96,6 +226,26 @@ The distance from the panel to the trigger can be customized using the `distance
|
||||
</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.
|
||||
@@ -114,6 +264,26 @@ The offset of the panel along the trigger can be customized using the `skidding`
|
||||
</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.
|
||||
@@ -141,11 +311,53 @@ Dropdown panels will be clipped if they're inside a container that has `overflow
|
||||
|
||||
<style>
|
||||
.dropdown-hoist {
|
||||
border: solid 2px rgb(var(--sl-panel-border-color));
|
||||
border: solid 2px var(--sl-panel-border-color);
|
||||
padding: var(--sl-spacing-medium);
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlDivider, SlDropdown, SlIcon, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.dropdown-hoist {
|
||||
border: solid 2px var(--sl-panel-border-color);
|
||||
padding: var(--sl-spacing-medium);
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
|
||||
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>
|
||||
|
||||
<SlDropdown hoist>
|
||||
<SlButton slot="trigger" caret>
|
||||
Hoist
|
||||
</SlButton>
|
||||
<SlMenu>
|
||||
<SlMenuItem>Item 1</SlMenuItem>
|
||||
<SlMenuItem>Item 2</SlMenuItem>
|
||||
<SlMenuItem>Item 3</SlMenuItem>
|
||||
</SlMenu>
|
||||
</SlDropdown>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-dropdown]
|
||||
|
||||
@@ -1,215 +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 way 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.
|
||||
|
||||
!> 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: rgb(var(--sl-color-danger-600));
|
||||
}
|
||||
|
||||
.custom-input[invalid]:not([disabled])::part(base) {
|
||||
border-color: rgb(var(--sl-color-danger-500));
|
||||
}
|
||||
|
||||
.custom-input[invalid]:focus-within::part(base) {
|
||||
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-danger-500) / var(--sl-focus-ring-alpha));
|
||||
}
|
||||
</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` attribute 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` attribute 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]
|
||||
@@ -6,9 +6,8 @@ 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 +15,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 +50,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]
|
||||
|
||||
@@ -11,11 +11,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>
|
||||
```
|
||||
|
||||
```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 +29,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 `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]
|
||||
|
||||
@@ -8,9 +8,9 @@ Localization is handled by the browser's [`Intl.NumberFormat` API](https://devel
|
||||
|
||||
```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 +18,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 +53,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]
|
||||
|
||||
@@ -10,6 +10,12 @@ For a full list of icons that come bundled with Shoelace, refer to the [icon com
|
||||
<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 +28,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.
|
||||
@@ -49,6 +67,37 @@ 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.
|
||||
@@ -57,6 +106,12 @@ Use the `href` attribute to convert the button to a link.
|
||||
<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.
|
||||
@@ -67,6 +122,16 @@ 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.
|
||||
@@ -75,4 +140,10 @@ Use the `disabled` attribute to disable the icon button.
|
||||
<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]
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
|
||||
Icons are symbols that can be used to represent various options within an application.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Click or tap on an icon below to copy its name and use it like this.
|
||||
?> 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.
|
||||
|
||||
## 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>
|
||||
@@ -24,11 +28,75 @@ Click or tap on an icon below to copy its name and use it like this.
|
||||
</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 +108,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 +119,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
|
||||
@@ -113,7 +226,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 +237,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 +254,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/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>
|
||||
```
|
||||
@@ -195,20 +308,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>
|
||||
```
|
||||
|
||||
@@ -290,14 +403,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>
|
||||
@@ -330,7 +443,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>
|
||||
@@ -366,14 +479,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>
|
||||
@@ -410,13 +523,45 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
|
||||
<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>
|
||||
<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>
|
||||
```
|
||||
|
||||
@@ -433,7 +578,9 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
|
||||
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')
|
||||
});
|
||||
@@ -446,12 +593,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>
|
||||
```
|
||||
@@ -509,7 +656,7 @@ 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>
|
||||
`;
|
||||
@@ -558,7 +705,7 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
|
||||
<style>
|
||||
.icon-search {
|
||||
border: solid 1px rgb(var(--sl-panel-border-color));
|
||||
border: solid 1px var(--sl-panel-border-color);
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
padding: var(--sl-spacing-medium);
|
||||
}
|
||||
@@ -614,8 +761,8 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
}
|
||||
|
||||
.icon-list-item:hover {
|
||||
background-color: rgb(var(--sl-color-primary-50));
|
||||
color: rgb(var(--sl-color-primary-600));
|
||||
background-color: var(--sl-color-primary-50);
|
||||
color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.icon-list[data-type="outline"] .icon-list-item[data-name$="-fill"] {
|
||||
|
||||
@@ -8,11 +8,38 @@ For best results, use images that share the same dimensions. The slider can be c
|
||||
|
||||
```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
|
||||
@@ -21,9 +48,36 @@ Use the `position` attribute to set the initial position of the slider. This is
|
||||
|
||||
```html preview
|
||||
<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.">
|
||||
<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]
|
||||
|
||||
@@ -8,8 +8,14 @@ Included files are asynchronously requested using `window.fetch()`. Requests are
|
||||
|
||||
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 no-codepen
|
||||
<sl-include src="/assets/examples/include.html"></sl-include>
|
||||
```html preview
|
||||
<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,11 +27,11 @@ 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');
|
||||
});
|
||||
|
||||
@@ -8,12 +8,44 @@ Inputs collect data from the user.
|
||||
<sl-input></sl-input>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) 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
|
||||
|
||||
### Labels
|
||||
|
||||
Use the `label` attribute to give the input an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
|
||||
```html preview
|
||||
<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>
|
||||
```
|
||||
|
||||
```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.
|
||||
@@ -22,6 +54,12 @@ Use the `placeholder` attribute to add a placeholder.
|
||||
<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.
|
||||
@@ -30,18 +68,38 @@ Add the `clearable` attribute to add a clear button when the input has content.
|
||||
<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 `toggle-password` attribute 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>
|
||||
<br />
|
||||
<sl-input type="password" placeholder="Password Toggle" size="medium" toggle-password></sl-input>
|
||||
<br>
|
||||
<br />
|
||||
<sl-input type="password" placeholder="Password Toggle" size="large" toggle-password></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlInput type="password" placeholder="Password Toggle" size="small" toggle-password />
|
||||
<br />
|
||||
<SlInput type="password" placeholder="Password Toggle" size="medium" toggle-password />
|
||||
<br />
|
||||
<SlInput type="password" placeholder="Password Toggle" size="large" toggle-password />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Filled Inputs
|
||||
|
||||
Add the `filled` attribute to draw a filled input.
|
||||
@@ -50,28 +108,62 @@ Add the `filled` attribute to draw a filled input.
|
||||
<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 />;
|
||||
```
|
||||
|
||||
### Pill
|
||||
|
||||
Use the `pill` attribute to give inputs rounded edges.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Small" size="small" pill></sl-input>
|
||||
<br>
|
||||
<br />
|
||||
<sl-input placeholder="Medium" size="medium" pill></sl-input>
|
||||
<br>
|
||||
<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>
|
||||
<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" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
@@ -80,24 +172,52 @@ Use the `disabled` attribute to disable an input.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Disabled" size="small" disabled></sl-input>
|
||||
<br>
|
||||
<br />
|
||||
<sl-input placeholder="Disabled" size="medium" disabled></sl-input>
|
||||
<br>
|
||||
<br />
|
||||
<sl-input placeholder="Disabled" size="large" disabled></sl-input>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlInput placeholder="Disabled" size="small" disabled />
|
||||
<br />
|
||||
<SlInput placeholder="Disabled" size="medium" disabled />
|
||||
<br />
|
||||
<SlInput placeholder="Disabled" size="large" disabled />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change an input's size.
|
||||
|
||||
```html preview
|
||||
<sl-input placeholder="Small" size="small"></sl-input>
|
||||
<br>
|
||||
<br />
|
||||
<sl-input placeholder="Medium" size="medium"></sl-input>
|
||||
<br>
|
||||
<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" />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix & Suffix Icons
|
||||
|
||||
Use the `prefix` and `suffix` slots to add icons.
|
||||
@@ -107,35 +227,66 @@ Use the `prefix` and `suffix` slots to add icons.
|
||||
<sl-icon name="house" slot="prefix"></sl-icon>
|
||||
<sl-icon name="chat" slot="suffix"></sl-icon>
|
||||
</sl-input>
|
||||
<br>
|
||||
<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>
|
||||
<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>
|
||||
```
|
||||
|
||||
### Labels
|
||||
```jsx react
|
||||
import { SlIcon, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
Use the `label` attribute to give the input an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
|
||||
```html preview
|
||||
<sl-input label="What is your name?"></sl-input>
|
||||
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>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Help Text
|
||||
### Customizing Label Position
|
||||
|
||||
Add descriptive help text to an input with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
|
||||
Use parts to customize the label's position.
|
||||
|
||||
```html preview
|
||||
<sl-input
|
||||
label="Nickname"
|
||||
help-text="What would you like people to call you?"
|
||||
></sl-input>
|
||||
<sl-input class="label-on-left" label="Name"></sl-input><br />
|
||||
<sl-input class="label-on-left" label="Email" type="email"></sl-input>
|
||||
|
||||
<style>
|
||||
.label-on-left::part(form-control) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.label-on-left::part(form-control-label) {
|
||||
flex: 0 0 auto;
|
||||
width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.label-on-left::part(form-control-input) {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
[component-metadata:sl-input]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
Menu items provide options for the user to pick from in a menu.
|
||||
|
||||
```html preview
|
||||
<sl-menu style="max-width: 200px; border: solid 1px rgb(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>
|
||||
@@ -24,6 +24,30 @@ 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 checked>Checked</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
|
||||
|
||||
### Checked
|
||||
@@ -31,31 +55,55 @@ Menu items provide options for the user to pick from in a menu.
|
||||
Use the `checked` attribute to draw menu items in a checked state.
|
||||
|
||||
```html preview
|
||||
<sl-menu style="max-width: 200px; border: solid 1px rgb(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 checked>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 checked>Option 2</SlMenuItem>
|
||||
<SlMenuItem>Option 3</SlMenuItem>
|
||||
</SlMenu>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Add the `disabled` attribute to disable the menu item so it cannot be selected.
|
||||
|
||||
```html preview
|
||||
<sl-menu style="max-width: 200px; border: solid 1px rgb(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 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; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);">
|
||||
<sl-menu style="max-width: 200px;">
|
||||
<sl-menu-item>
|
||||
<sl-icon slot="prefix" name="house"></sl-icon>
|
||||
Home
|
||||
@@ -64,7 +112,7 @@ Add content to the start and end of menu items using the `prefix` and `suffix` s
|
||||
<sl-menu-item>
|
||||
<sl-icon slot="prefix" name="envelope"></sl-icon>
|
||||
Messages
|
||||
<sl-badge slot="suffix" type="primary" pill>12</sl-badge>
|
||||
<sl-badge slot="suffix" variant="primary" pill>12</sl-badge>
|
||||
</sl-menu-item>
|
||||
|
||||
<sl-divider></sl-divider>
|
||||
@@ -76,12 +124,40 @@ Add content to the start and end of menu items using the `prefix` and `suffix` s
|
||||
</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>
|
||||
);
|
||||
```
|
||||
|
||||
### 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; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);">
|
||||
<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>
|
||||
@@ -102,4 +178,28 @@ The `value` attribute can be used to assign a hidden value, such as a unique ide
|
||||
</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]
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
Menu labels are used to describe a group of menu items.
|
||||
|
||||
```html preview
|
||||
<sl-menu
|
||||
style="max-width: 200px; border: solid 1px rgb(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>
|
||||
@@ -20,4 +18,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]
|
||||
|
||||
@@ -7,7 +7,7 @@ Menus provide a list of options for the user to choose from.
|
||||
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 rgb(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-divider></sl-divider>
|
||||
@@ -18,6 +18,22 @@ You can use [menu items](/components/menu-item), [menu labels](/components/menu-
|
||||
</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]
|
||||
|
||||
@@ -8,24 +8,24 @@ The mutation observer will report changes to the content it wraps through the `s
|
||||
|
||||
```html preview
|
||||
<div class="mutation-overview">
|
||||
<sl-mutation-observer attr>
|
||||
<sl-button type="primary">Click to mutate</sl-button>
|
||||
<sl-mutation-observer attr="variant">
|
||||
<sl-button variant="primary">Click to mutate</sl-button>
|
||||
</sl-mutation-observer>
|
||||
|
||||
<br>
|
||||
<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 types = ['primary', 'success', 'neutral', 'warning', 'danger'];
|
||||
const variants = ['primary', 'success', 'neutral', 'warning', 'danger'];
|
||||
let clicks = 0;
|
||||
|
||||
// Change the button's type attribute
|
||||
// Change the button's variant attribute
|
||||
button.addEventListener('click', () => {
|
||||
clicks++;
|
||||
button.setAttribute('type', types[clicks % types.length]);
|
||||
button.setAttribute('variant', variants[clicks % variants.length]);
|
||||
});
|
||||
|
||||
// Log mutations
|
||||
@@ -42,6 +42,46 @@ The mutation observer will report changes to the content it wraps through the `s
|
||||
</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
|
||||
@@ -54,7 +94,7 @@ Use the `child-list` attribute to watch for new child elements that are added or
|
||||
<div class="mutation-child-list">
|
||||
<sl-mutation-observer child-list>
|
||||
<div class="buttons">
|
||||
<sl-button type="primary">Add button</sl-button>
|
||||
<sl-button variant="primary">Add button</sl-button>
|
||||
</div>
|
||||
</sl-mutation-observer>
|
||||
|
||||
@@ -64,7 +104,7 @@ Use the `child-list` attribute to watch for new child elements that are added or
|
||||
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[type="primary"]');
|
||||
const button = container.querySelector('sl-button[variant="primary"]');
|
||||
let i = 0;
|
||||
|
||||
// Add a button
|
||||
@@ -76,7 +116,7 @@ Use the `child-list` attribute to watch for new child elements that are added or
|
||||
|
||||
// Remove a button
|
||||
buttons.addEventListener('click', event => {
|
||||
const target = event.target.closest('sl-button:not([type="primary"])');
|
||||
const target = event.target.closest('sl-button:not([variant="primary"])');
|
||||
event.stopPropagation();
|
||||
|
||||
if (target) {
|
||||
@@ -93,7 +133,7 @@ Use the `child-list` attribute to watch for new child elements that are added or
|
||||
<style>
|
||||
.mutation-child-list .buttons {
|
||||
display: flex;
|
||||
gap: .25rem;
|
||||
gap: 0.25rem;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -101,4 +141,53 @@ Use the `child-list` attribute to watch for new child elements that are added or
|
||||
</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]
|
||||
|
||||
@@ -8,16 +8,14 @@ Progress bars are used to show the status of an ongoing operation.
|
||||
<sl-progress-bar value="50"></sl-progress-bar>
|
||||
```
|
||||
|
||||
## Examples
|
||||
```jsx react
|
||||
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
### Custom Height
|
||||
|
||||
Use the `--height` custom property to set the progress bar's height.
|
||||
|
||||
```html preview
|
||||
<sl-progress-bar value="50" style="--height: 6px;"></sl-progress-bar>
|
||||
const App = () => <SlProgressBar value={50} />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Labels
|
||||
|
||||
Use the `label` attribute to label the progress bar and tell assistive devices how to announce it.
|
||||
@@ -26,6 +24,26 @@ Use the `label` attribute to label the progress bar and tell assistive devices h
|
||||
<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 value="50" style="--height: 6px;"></sl-progress-bar>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlProgressBar value={50} style={{ '--height': '6px' }} />;
|
||||
```
|
||||
|
||||
### Showing Values
|
||||
|
||||
Use the default slot to show a value.
|
||||
@@ -33,10 +51,10 @@ Use the default slot to show a value.
|
||||
```html preview
|
||||
<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-values');
|
||||
@@ -50,13 +68,45 @@ Use the default slot to show a value.
|
||||
});
|
||||
|
||||
subtractButton.addEventListener('click', () => {
|
||||
const value = Math.max(0, progressBar.value - 10)
|
||||
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, `value` is ignored and the label, if present, will not be shown.
|
||||
@@ -65,4 +115,10 @@ The `indeterminate` attribute can be used to inform the user that the operation
|
||||
<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]
|
||||
|
||||
@@ -8,6 +8,12 @@ Progress rings are used to show the progress of a determinate operation in a cir
|
||||
<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
|
||||
@@ -18,6 +24,12 @@ Use the `--size` custom property to set the diameter of the progress ring.
|
||||
<sl-progress-ring value="50" style="--size: 200px;"></sl-progress-ring>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlProgressRing value="50" style={{ '--size': '200px' }} />;
|
||||
```
|
||||
|
||||
### Track Width
|
||||
|
||||
Use the `--track-width` custom property to set the width of the progress ring's track.
|
||||
@@ -26,13 +38,19 @@ Use the `--track-width` custom property to set the width of the progress ring's
|
||||
<sl-progress-ring value="50" style="--track-width: 10px;"></sl-progress-ring>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlProgressRing value="50" style={{ '--track-width': '10px' }} />;
|
||||
```
|
||||
|
||||
### Colors
|
||||
|
||||
To change the color, use the `--track-color` and `--indicator-color` custom properties.
|
||||
|
||||
```html preview
|
||||
<sl-progress-ring
|
||||
value="50"
|
||||
<sl-progress-ring
|
||||
value="50"
|
||||
style="
|
||||
--track-color: pink;
|
||||
--indicator-color: deeppink;
|
||||
@@ -40,6 +58,20 @@ To change the color, use the `--track-color` and `--indicator-color` custom prop
|
||||
></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 `label` attribute to label the progress ring and tell assistive devices how to announce it.
|
||||
@@ -48,17 +80,23 @@ Use the `label` attribute to label the progress ring and tell assistive devices
|
||||
<sl-progress-ring value="50" label="Upload progress"></sl-progress-ring>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlProgressRing value="50" label="Upload progress" />;
|
||||
```
|
||||
|
||||
### Showing Values
|
||||
|
||||
Use the default slot to show a label.
|
||||
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>
|
||||
<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 progressRing = document.querySelector('.progress-ring-values');
|
||||
@@ -72,11 +110,45 @@ Use the default slot to show a label.
|
||||
});
|
||||
|
||||
subtractButton.addEventListener('click', () => {
|
||||
const value = Math.max(0, progressRing.value - 10)
|
||||
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]
|
||||
|
||||
@@ -9,9 +9,9 @@ QR codes are useful for providing small pieces of information to users who can q
|
||||
```html preview
|
||||
<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>
|
||||
<br />
|
||||
|
||||
<sl-input maxlength="255" clearable></sl-input>
|
||||
<sl-input maxlength="255" clearable label="Value"></sl-input>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -20,7 +20,7 @@ QR codes are useful for providing small pieces of information to users who can q
|
||||
const input = container.querySelector('sl-input');
|
||||
|
||||
input.value = qrCode.value;
|
||||
input.addEventListener('sl-input', () => qrCode.value = input.value);
|
||||
input.addEventListener('sl-input', () => (qrCode.value = input.value));
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -34,6 +34,38 @@ QR codes are useful for providing small pieces of information to users who can q
|
||||
</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
|
||||
@@ -44,6 +76,12 @@ Use the `fill` and `background` attributes to modify the QR code's colors. You s
|
||||
<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` attribute to change the size of the QR code.
|
||||
@@ -52,6 +90,12 @@ Use the `size` attribute to change the size of the QR code.
|
||||
<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` attribute.
|
||||
@@ -60,6 +104,12 @@ Create a rounded effect with the `radius` attribute.
|
||||
<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.
|
||||
@@ -81,4 +131,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]
|
||||
|
||||
386
docs/components/radio-button.md
Normal file
@@ -0,0 +1,386 @@
|
||||
# Radio Button
|
||||
|
||||
[component-header:sl-radio-button]
|
||||
|
||||
Radios buttons allow the user to select a single option from a group using a button-like control.
|
||||
|
||||
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">
|
||||
<sl-radio-button name="a" value="1" checked>Option 1</sl-radio-button>
|
||||
<sl-radio-button name="a" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button name="a" 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">
|
||||
<SlRadioButton name="option" value="1" checked>
|
||||
Option 1
|
||||
</SlRadioButton>
|
||||
<SlRadioButton name="option" value="2">
|
||||
Option 2
|
||||
</SlRadioButton>
|
||||
<SlRadioButton name="option" value="3">
|
||||
Option 3
|
||||
</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Checked
|
||||
|
||||
To set the initial checked state, use the `checked` attribute.
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an option">
|
||||
<sl-radio-button name="option" value="1" checked>Option 1</sl-radio-button>
|
||||
<sl-radio-button name="option" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button name="option" 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">
|
||||
<SlRadioButton name="option" value="1" checked>
|
||||
Option 1
|
||||
</SlRadioButton>
|
||||
<SlRadioButton name="option" value="2">
|
||||
Option 2
|
||||
</SlRadioButton>
|
||||
<SlRadioButton name="option" 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">
|
||||
<sl-radio-button name="option" value="1" checked>Option 1</sl-radio-button>
|
||||
<sl-radio-button name="option" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button name="option" value="3" disabled>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">
|
||||
<SlRadioButton name="option" value="1" checked>
|
||||
Option 1
|
||||
</SlRadioButton>
|
||||
<SlRadioButton name="option" value="2">
|
||||
Option 2
|
||||
</SlRadioButton>
|
||||
<SlRadioButton name="option" value="3" disabled>
|
||||
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">
|
||||
<sl-radio-button size="small" name="option" value="1" checked>Option 1</sl-radio-button>
|
||||
<sl-radio-button size="small" name="option" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button size="small" name="option" value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-radio-group label="Select an option">
|
||||
<sl-radio-button size="medium" name="option" value="1" checked>Option 1</sl-radio-button>
|
||||
<sl-radio-button size="medium" name="option" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button size="medium" name="option" value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-radio-group label="Select an option">
|
||||
<sl-radio-button size="large" name="option" value="1" checked>Option 1</sl-radio-button>
|
||||
<sl-radio-button size="large" name="option" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button size="large" name="option" 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">
|
||||
<SlRadioButton size="small" name="option" value="1" checked>Option 1</SlRadioButton>
|
||||
<SlRadioButton size="small" name="option" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton size="small" name="option" value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<SlRadioGroup label="Select an option">
|
||||
<SlRadioButton size="medium" name="option" value="1" checked>Option 1</SlRadioButton>
|
||||
<SlRadioButton size="medium" name="option" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton size="medium" name="option" value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<SlRadioGroup label="Select an option">
|
||||
<SlRadioButton size="large" name="option" value="1" checked>Option 1</SlRadioButton>
|
||||
<SlRadioButton size="large" name="option" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton size="large" name="option" 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">
|
||||
<sl-radio-button pill size="small" name="option" value="1" checked>Option 1</sl-radio-button>
|
||||
<sl-radio-button pill size="small" name="option" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button pill size="small" name="option" value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-radio-group label="Select an option">
|
||||
<sl-radio-button pill size="medium" name="option" value="1" checked>Option 1</sl-radio-button>
|
||||
<sl-radio-button pill size="medium" name="option" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button pill size="medium" name="option" value="3">Option 3</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
|
||||
<br />
|
||||
|
||||
<sl-radio-group label="Select an option">
|
||||
<sl-radio-button pill size="large" name="option" value="1" checked>Option 1</sl-radio-button>
|
||||
<sl-radio-button pill size="large" name="option" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button pill size="large" name="option" 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">
|
||||
<SlRadioButton pill size="small" name="option" value="1" checked>Option 1</SlRadioButton>
|
||||
<SlRadioButton pill size="small" name="option" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton pill size="small" name="option" value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<SlRadioGroup label="Select an option">
|
||||
<SlRadioButton pill size="medium" name="option" value="1" checked>Option 1</SlRadioButton>
|
||||
<SlRadioButton pill size="medium" name="option" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton pill size="medium" name="option" value="3">Option 3</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<SlRadioGroup label="Select an option">
|
||||
<SlRadioButton pill size="large" name="option" value="1" checked>Option 1</SlRadioButton>
|
||||
<SlRadioButton pill size="large" name="option" value="2">Option 2</SlRadioButton>
|
||||
<SlRadioButton pill size="large" name="option" 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">
|
||||
<sl-radio-button name="a" value="1" checked>
|
||||
<sl-icon slot="prefix" name="archive"></sl-icon>
|
||||
Option 1
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button name="a" value="2">
|
||||
<sl-icon slot="suffix" name="bag"></sl-icon>
|
||||
Option 2
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button name="a" 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">
|
||||
<SlRadioButton name="a" value="1" checked>
|
||||
<SlIcon slot="prefix" name="archive" />
|
||||
Option 1
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton name="a" value="2">
|
||||
<SlIcon slot="suffix" name="bag" />
|
||||
Option 2
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton name="a" 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">
|
||||
<sl-radio-button name="a" value="angry">
|
||||
<sl-icon name="emoji-angry" label="Angry"></sl-icon>
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button name="a" value="sad">
|
||||
<sl-icon name="emoji-frown" label="Sad"></sl-icon>
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button name="a" value="neutral" checked>
|
||||
<sl-icon name="emoji-neutral" label="Neutral"></sl-icon>
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button name="a" value="happy">
|
||||
<sl-icon name="emoji-smile" label="Happy"></sl-icon>
|
||||
</sl-radio-button>
|
||||
|
||||
<sl-radio-button name="a" 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">
|
||||
<SlRadioButton name="a" value="angry">
|
||||
<SlIcon name="emoji-angry" label="Angry" />
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton name="a" value="sad">
|
||||
<SlIcon name="emoji-frown" label="Sad" />
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton name="a" value="neutral" checked>
|
||||
<SlIcon name="emoji-neutral" label="Neutral" />
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton name="a" value="happy">
|
||||
<SlIcon name="emoji-smile" label="Happy" />
|
||||
</SlRadioButton>
|
||||
|
||||
<SlRadioButton name="a" value="laughing">
|
||||
<SlIcon name="emoji-laughing" label="Laughing" />
|
||||
</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### 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">
|
||||
<sl-radio-button name="a" value="1" checked>Not me</sl-radio-button>
|
||||
<sl-radio-button name="a" value="2">Me neither</sl-radio-button>
|
||||
<sl-radio-button name="a" value="3">Choose me</sl-radio-button>
|
||||
</sl-radio-group>
|
||||
<br />
|
||||
<sl-button type="submit" variant="primary">Submit</sl-button>
|
||||
</form>
|
||||
<script>
|
||||
const form = document.querySelector('.custom-validity');
|
||||
const radioButton = form.querySelectorAll('sl-radio-button')[2];
|
||||
const errorMessage = 'You must choose this option';
|
||||
// Set initial validity as soon as the element is defined
|
||||
customElements.whenDefined('sl-radio-button').then(() => {
|
||||
radioButton.setCustomValidity(errorMessage);
|
||||
});
|
||||
// Update validity when a selection is made
|
||||
form.addEventListener('sl-change', () => {
|
||||
const isValid = radioButton.checked;
|
||||
radioButton.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, SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
const App = () => {
|
||||
const radio = useRef(null);
|
||||
const errorMessage = 'You must choose this option';
|
||||
function handleChange(event) {
|
||||
radio.current.setCustomValidity(radio.current.checked ? '' : errorMessage);
|
||||
}
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
}
|
||||
useEffect(() => {
|
||||
radio.current.setCustomValidity(errorMessage);
|
||||
}, []);
|
||||
return (
|
||||
<form class="custom-validity" onSubmit={handleSubmit}>
|
||||
<SlRadioGroup label="Select an option">
|
||||
<SlRadioButton name="a" value="1" checked onSlChange={handleChange}>
|
||||
Not me
|
||||
</SlRadioButton>
|
||||
<SlRadioButton name="a" value="2" onSlChange={handleChange}>
|
||||
Me neither
|
||||
</SlRadioButton>
|
||||
<SlRadioButton ref={radio} name="a" value="3" onSlChange={handleChange}>
|
||||
Choose me
|
||||
</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
<br />
|
||||
<SlButton type="submit" variant="primary">
|
||||
Submit
|
||||
</SlButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
[component-metadata:sl-radio-button]
|
||||
@@ -2,28 +2,94 @@
|
||||
|
||||
[component-header:sl-radio-group]
|
||||
|
||||
Radio Groups are used to group multiple radios so they function as a single control.
|
||||
Radio groups are used to group multiple [radios](/components/radio) or [radio buttons](/components/radio-button) so they function as a single form 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">
|
||||
<sl-radio name="option" value="1" checked>Option 1</sl-radio>
|
||||
<sl-radio name="option" value="2">Option 2</sl-radio>
|
||||
<sl-radio name="option" 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">
|
||||
<SlRadio name="option" value="1" checked>
|
||||
Option 1
|
||||
</SlRadio>
|
||||
<SlRadio name="option" value="2">
|
||||
Option 2
|
||||
</SlRadio>
|
||||
<SlRadio name="option" value="3">
|
||||
Option 3
|
||||
</SlRadio>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Showing the Fieldset
|
||||
### Showing the Label
|
||||
|
||||
You can show a fieldset and legend that wraps the radio group using the `fieldset` attribute.
|
||||
You can show the fieldset and legend that wraps the radio group using the `fieldset` attribute. If you don't use this option, you should still provide a label so screen readers announce the control correctly.
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an item" 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" fieldset>
|
||||
<sl-radio name="option" value="1" checked>Option 1</sl-radio>
|
||||
<sl-radio name="option" value="2">Option 2</sl-radio>
|
||||
<sl-radio name="option" 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" fieldset>
|
||||
<SlRadio name="option" value="1" checked>
|
||||
Option 1
|
||||
</SlRadio>
|
||||
<SlRadio name="option" value="2">
|
||||
Option 2
|
||||
</SlRadio>
|
||||
<SlRadio name="option" 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">
|
||||
<sl-radio-button name="option" value="1" checked>Option 1</sl-radio-button>
|
||||
<sl-radio-button name="option" value="2">Option 2</sl-radio-button>
|
||||
<sl-radio-button name="option" 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">
|
||||
<SlRadioButton name="option" value="1" checked>
|
||||
Option 1
|
||||
</SlRadioButton>
|
||||
<SlRadioButton name="option" value="2">
|
||||
Option 2
|
||||
</SlRadioButton>
|
||||
<SlRadioButton name="option" value="3">
|
||||
Option 3
|
||||
</SlRadioButton>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-radio-group]
|
||||
|
||||
@@ -2,33 +2,171 @@
|
||||
|
||||
[component-header:sl-radio]
|
||||
|
||||
Radios allow the user to select one option from a group of many.
|
||||
Radios allow the user to select a single option from a group.
|
||||
|
||||
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 value="2">Option 2</sl-radio>
|
||||
<sl-radio value="3">Option 3</sl-radio>
|
||||
<sl-radio-group label="Select an option">
|
||||
<sl-radio name="option" value="1" checked>Option 1</sl-radio>
|
||||
<sl-radio name="option" value="2">Option 2</sl-radio>
|
||||
<sl-radio name="option" value="3">Option 3</sl-radio>
|
||||
</sl-radio-group>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) instead.
|
||||
```jsx react
|
||||
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRadioGroup label="Select an option">
|
||||
<SlRadio name="option" value="1" checked>
|
||||
Option 1
|
||||
</SlRadio>
|
||||
<SlRadio name="option" value="2">
|
||||
Option 2
|
||||
</SlRadio>
|
||||
<SlRadio name="option" 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
|
||||
|
||||
### Checked
|
||||
|
||||
To set the initial checked state, use the `checked` attribute.
|
||||
|
||||
```html preview
|
||||
<sl-radio-group label="Select an option">
|
||||
<sl-radio name="option" value="1" checked>Option 1</sl-radio>
|
||||
<sl-radio name="option" value="2">Option 2</sl-radio>
|
||||
<sl-radio name="option" 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">
|
||||
<SlRadio name="option" value="1" checked>
|
||||
Option 1
|
||||
</SlRadio>
|
||||
<SlRadio name="option" value="2">
|
||||
Option 2
|
||||
</SlRadio>
|
||||
<SlRadio name="option" 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 value="3">Option 3</sl-radio>
|
||||
<sl-radio value="4" disabled>Disabled</sl-radio>
|
||||
<sl-radio-group label="Select an option">
|
||||
<sl-radio name="option" value="1" checked>Option 1</sl-radio>
|
||||
<sl-radio name="option" value="2">Option 2</sl-radio>
|
||||
<sl-radio name="option" value="3" disabled>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">
|
||||
<SlRadio name="option" value="1" checked>
|
||||
Option 1
|
||||
</SlRadio>
|
||||
<SlRadio name="option" value="2">
|
||||
Option 2
|
||||
</SlRadio>
|
||||
<SlRadio name="option" value="3" disabled>
|
||||
Option 3
|
||||
</SlRadio>
|
||||
</SlRadioGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### 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">
|
||||
<sl-radio name="a" value="1" checked>Not me</sl-radio>
|
||||
<sl-radio name="a" value="2">Me neither</sl-radio>
|
||||
<sl-radio name="a" 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 radio = form.querySelectorAll('sl-radio')[2];
|
||||
const errorMessage = 'You must choose this option';
|
||||
// Set initial validity as soon as the element is defined
|
||||
customElements.whenDefined('sl-radio').then(() => {
|
||||
radio.setCustomValidity(errorMessage);
|
||||
});
|
||||
// Update validity when a selection is made
|
||||
form.addEventListener('sl-change', () => {
|
||||
const isValid = radio.checked;
|
||||
radio.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 radio = useRef(null);
|
||||
const errorMessage = 'You must choose this option';
|
||||
function handleChange(event) {
|
||||
radio.current.setCustomValidity(radio.current.checked ? '' : errorMessage);
|
||||
}
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
}
|
||||
useEffect(() => {
|
||||
radio.current.setCustomValidity(errorMessage);
|
||||
}, []);
|
||||
return (
|
||||
<form class="custom-validity" onSubmit={handleSubmit}>
|
||||
<SlRadioGroup label="Select an option">
|
||||
<SlRadio name="a" value="1" checked onSlChange={handleChange}>
|
||||
Not me
|
||||
</SlRadio>
|
||||
<SlRadio name="a" value="2" onSlChange={handleChange}>
|
||||
Me neither
|
||||
</SlRadio>
|
||||
<SlRadio ref={radio} name="a" value="3" onSlChange={handleChange}>
|
||||
Choose me
|
||||
</SlRadio>
|
||||
</SlRadioGroup>
|
||||
<br />
|
||||
<SlButton type="submit" variant="primary">
|
||||
Submit
|
||||
</SlButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
[component-metadata:sl-radio]
|
||||
|
||||
@@ -8,16 +8,75 @@ Ranges allow the user to select a single value within a given range using a slid
|
||||
<sl-range></sl-range>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) 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
|
||||
|
||||
### Labels
|
||||
|
||||
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>
|
||||
```
|
||||
|
||||
```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-input>
|
||||
```
|
||||
|
||||
```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 min="0" max="100" step="1" disabled></sl-range>
|
||||
<sl-range disabled></sl-range>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRange } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRange disabled />;
|
||||
```
|
||||
|
||||
### Tooltip Placement
|
||||
@@ -25,7 +84,13 @@ Use the `disabled` attribute 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,7 +98,13 @@ 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
|
||||
@@ -41,10 +112,57 @@ To disable the tooltip, set `tooltip` to `none`.
|
||||
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: rgb(var(--sl-color-primary-600));
|
||||
--track-color-inactive: rgb(var(--sl-color-primary-200));
|
||||
"></sl-range>
|
||||
<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
|
||||
@@ -60,25 +178,10 @@ You can change the tooltip's content by setting the `tooltipFormatter` property
|
||||
</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]
|
||||
|
||||
@@ -8,6 +8,12 @@ Ratings give users a way to quickly view and provide feedback.
|
||||
<sl-rating></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Maximum Value
|
||||
@@ -18,12 +24,24 @@ Ratings are 0-5 by default. To change the maximum possible value, use the `max`
|
||||
<sl-rating max="3"></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating max={3} />;
|
||||
```
|
||||
|
||||
### Precision
|
||||
|
||||
Use the `precision` attribute to let users select fractional ratings.
|
||||
|
||||
```html preview
|
||||
<sl-rating precision=".5" value="2.5"></sl-rating>
|
||||
<sl-rating precision="0.5" value="2.5"></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating precision={0.5} value={2.5} />;
|
||||
```
|
||||
|
||||
## Symbol Sizes
|
||||
@@ -34,6 +52,12 @@ Set the `--symbol-size` custom property to adjust the size.
|
||||
<sl-rating style="--symbol-size: 2rem;"></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating style={{ '--symbol-size': '2rem' }} />;
|
||||
```
|
||||
|
||||
### Readonly
|
||||
|
||||
Use the `readonly` attribute to display a rating that users can't change.
|
||||
@@ -42,6 +66,12 @@ Use the `readonly` attribute to display a rating that users can't change.
|
||||
<sl-rating readonly value="3"></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating readonly value={3} />;
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disable` attribute to disable the rating.
|
||||
@@ -50,30 +80,61 @@ Use the `disable` attribute to disable the rating.
|
||||
<sl-rating disabled value="3"></sl-rating>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlRating disabled value={3} />;
|
||||
```
|
||||
|
||||
### 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>
|
||||
|
||||
<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 '@shoelace-style/shoelace/dist/components/icon/icon';
|
||||
import { SlRating } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlRating 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>
|
||||
|
||||
<script>
|
||||
const rating = document.querySelector('.rating-emojis');
|
||||
|
||||
rating.getSymbol = (value) => {
|
||||
rating.getSymbol = value => {
|
||||
const icons = ['emoji-angry', 'emoji-frown', 'emoji-expressionless', 'emoji-smile', 'emoji-laughing'];
|
||||
return `<sl-icon name="${icons[value - 1]}"></sl-icon>`;
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import '@shoelace-style/shoelace/dist/components/icon/icon';
|
||||
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 getSymbol={getSymbol} />;
|
||||
```
|
||||
|
||||
[component-metadata:sl-rating]
|
||||
|
||||
@@ -11,6 +11,12 @@ 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>
|
||||
```
|
||||
|
||||
```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.
|
||||
@@ -36,26 +42,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]
|
||||
|
||||
@@ -9,9 +9,7 @@ The resize observer will report changes to the dimensions of the elements it wra
|
||||
```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>
|
||||
|
||||
@@ -26,9 +24,9 @@ The resize observer will report changes to the dimensions of the elements it wra
|
||||
|
||||
<style>
|
||||
.resize-observer-overview div {
|
||||
display: flex;
|
||||
border: solid 2px rgb(var(--sl-input-border-color));
|
||||
align-items: center;
|
||||
display: flex;
|
||||
border: solid 2px var(--sl-input-border-color);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
@@ -36,4 +34,31 @@ The resize observer will report changes to the dimensions of the elements it wra
|
||||
</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);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
}
|
||||
`;
|
||||
|
||||
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]
|
||||
|
||||
@@ -8,10 +8,26 @@ You can slot in any [replaced element](https://developer.mozilla.org/en-US/docs/
|
||||
|
||||
```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.">
|
||||
<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>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlResponsiveMedia } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlResponsiveMedia>
|
||||
<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."
|
||||
/>
|
||||
</SlResponsiveMedia>
|
||||
);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Responsive Images
|
||||
@@ -20,18 +36,56 @@ 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.">
|
||||
<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>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlResponsiveMedia } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlResponsiveMedia 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."
|
||||
/>
|
||||
</SlResponsiveMedia>
|
||||
);
|
||||
```
|
||||
|
||||
### 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>
|
||||
<iframe
|
||||
title="Video of the kittens"
|
||||
src="https://player.vimeo.com/video/1053647?title=0&byline=0&portrait=0"
|
||||
frameborder="0"
|
||||
allow="autoplay; fullscreen"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</sl-responsive-media>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlResponsiveMedia } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlResponsiveMedia aspect-ratio="16:9">
|
||||
<iframe
|
||||
title="Video of the kittens"
|
||||
src="https://player.vimeo.com/video/1053647?title=0&byline=0&portrait=0"
|
||||
frameborder="0"
|
||||
allow="autoplay; fullscreen"
|
||||
allowfullscreen
|
||||
/>
|
||||
</SlResponsiveMedia>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-responsive-media]
|
||||
|
||||
@@ -16,10 +16,74 @@ Selects allow you to choose one or more items from a dropdown menu.
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) instead.
|
||||
```jsx react
|
||||
import { SlDivider, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
<SlDivider />
|
||||
<SlMenuItem value="option-4">Option 4</SlMenuItem>
|
||||
<SlMenuItem value="option-5">Option 5</SlMenuItem>
|
||||
<SlMenuItem value="option-6">Option 6</SlMenuItem>
|
||||
</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
|
||||
|
||||
### 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-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect label="Select one">
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</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="1">Novice</sl-menu-item>
|
||||
<sl-menu-item value="2">Intermediate</sl-menu-item>
|
||||
<sl-menu-item value="3">Advanced</sl-menu-item>
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect label="Experience" help-text="Please tell us your skill level.">
|
||||
<SlMenuItem value="1">Novice</SlMenuItem>
|
||||
<SlMenuItem value="2">Intermediate</SlMenuItem>
|
||||
<SlMenuItem value="3">Advanced</SlMenuItem>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Placeholders
|
||||
|
||||
Use the `placeholder` attribute to add a placeholder.
|
||||
@@ -32,6 +96,18 @@ Use the `placeholder` attribute to add a placeholder.
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect placeholder="Select one">
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Clearable
|
||||
|
||||
Use the `clearable` attribute to make the control clearable.
|
||||
@@ -44,6 +120,18 @@ Use the `clearable` attribute to make the control clearable.
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect placeholder="Clearable" clearable>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Filled Selects
|
||||
|
||||
Add the `filled` attribute to draw a filled select.
|
||||
@@ -56,6 +144,18 @@ Add the `filled` attribute to draw a filled select.
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect filled>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Pill
|
||||
|
||||
Use the `pill` attribute to give selects rounded edges.
|
||||
@@ -68,6 +168,18 @@ Use the `pill` attribute to give selects rounded edges.
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect pill>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable a select.
|
||||
@@ -80,9 +192,101 @@ Use the `disabled` attribute to disable a select.
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect placeholder="Disabled" disabled>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Setting the Selection
|
||||
|
||||
Use the `value` attribute to set the current selection. When users interact with the control, its `value` will update to reflect the newly selected menu item's value. Note that the value must be an array when using the [`multiple`](#multiple) option.
|
||||
|
||||
```html preview
|
||||
<sl-select value="option-2">
|
||||
<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>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect value="option-2">
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Setting the Selection Imperatively
|
||||
|
||||
To programmatically set the selection, update the `value` property as shown below. Note that the value must be an array when using the [`multiple`](#multiple) option.
|
||||
|
||||
```html preview
|
||||
<div class="selecting-example">
|
||||
<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-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>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlButton, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const [value, setValue] = useState('option-1');
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlSelect value={value} onSlChange={event => setValue(event.target.value)}>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</SlSelect>
|
||||
|
||||
<br />
|
||||
|
||||
<SlButton onClick={() => setValue('option-1')}>Set 1</SlButton>
|
||||
<SlButton onClick={() => setValue('option-2')}>Set 2</SlButton>
|
||||
<SlButton onClick={() => setValue('option-3')}>Set 3</SlButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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.
|
||||
To allow multiple options to be selected, use the `multiple` attribute. With this option, `value` will be an array of strings instead of a string. It's a good practice to use `clearable` when this option is enabled.
|
||||
|
||||
```html preview
|
||||
<sl-select placeholder="Select a few" multiple clearable>
|
||||
@@ -96,6 +300,24 @@ To allow multiple options to be selected, use the `multiple` attribute. It's a g
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect placeholder="Select a few" multiple clearable>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
<SlDivider />
|
||||
<SlMenuItem value="option-4">Option 4</SlMenuItem>
|
||||
<SlMenuItem value="option-5">Option 5</SlMenuItem>
|
||||
<SlMenuItem value="option-6">Option 6</SlMenuItem>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
?> When using the `multiple` option, the value will be an array instead of a string. You may need to [set the selection imperatively](#setting-the-selection-imperatively) unless you're using a framework that supports binding properties declaratively.
|
||||
|
||||
### Grouping Options
|
||||
|
||||
Options can be grouped visually using menu labels and dividers.
|
||||
@@ -114,6 +336,24 @@ Options can be grouped visually using menu labels and dividers.
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlDivider, SlMenuItem, SlMenuLabel, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<SlSelect placeholder="Select one">
|
||||
<SlMenuLabel>Group 1</SlMenuLabel>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
<SlDivider></SlDivider>
|
||||
<SlMenuLabel>Group 2</SlMenuLabel>
|
||||
<SlMenuItem value="option-4">Option 4</SlMenuItem>
|
||||
<SlMenuItem value="option-5">Option 5</SlMenuItem>
|
||||
<SlMenuItem value="option-6">Option 6</SlMenuItem>
|
||||
</SlSelect>
|
||||
);
|
||||
```
|
||||
|
||||
### Sizes
|
||||
|
||||
Use the `size` attribute to change a select's size.
|
||||
@@ -125,7 +365,7 @@ Use the `size` attribute to change a select's size.
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
</sl-select>
|
||||
|
||||
<br>
|
||||
<br />
|
||||
|
||||
<sl-select placeholder="Medium" size="medium" multiple>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
@@ -133,7 +373,7 @@ Use the `size` attribute to change a select's size.
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
</sl-select>
|
||||
|
||||
<br>
|
||||
<br />
|
||||
|
||||
<sl-select placeholder="Large" size="large" multiple>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
@@ -142,62 +382,61 @@ Use the `size` attribute to change a select's size.
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
### Selecting Options Programmatically
|
||||
```jsx react
|
||||
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
The `value` property is bound to the current selection. As the selection changes, so will the value. To programmatically manage the selection, update the `value` property.
|
||||
const App = () => (
|
||||
<>
|
||||
<SlSelect placeholder="Small" size="small" multiple>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</SlSelect>
|
||||
|
||||
```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 />
|
||||
|
||||
<br>
|
||||
<SlSelect placeholder="Medium" size="medium" multiple>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</SlSelect>
|
||||
|
||||
<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>
|
||||
<br />
|
||||
|
||||
<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>
|
||||
<SlSelect placeholder="Large" size="large" multiple>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</SlSelect>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Labels
|
||||
### Placement
|
||||
|
||||
Use the `label` attribute to give the select an accessible label. For labels that contain HTML, use the `label` slot instead.
|
||||
The preferred placement of the select's menu 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 label="Select one">
|
||||
<sl-select placement="top">
|
||||
<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>
|
||||
```
|
||||
|
||||
### Help Text
|
||||
```jsx react
|
||||
import {
|
||||
SlMenuItem,
|
||||
SlSelect
|
||||
} from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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>
|
||||
const App = () => (
|
||||
<SlSelect placement="top">
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
</SlDropdown>
|
||||
);
|
||||
```
|
||||
|
||||
### Prefix & Suffix Icons
|
||||
@@ -212,7 +451,7 @@ Use the `prefix` and `suffix` slots to add icons.
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
<sl-icon name="chat" slot="suffix"></sl-icon>
|
||||
</sl-select>
|
||||
<br>
|
||||
<br />
|
||||
<sl-select placeholder="Medium" size="medium">
|
||||
<sl-icon name="house" slot="prefix"></sl-icon>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
@@ -220,7 +459,7 @@ Use the `prefix` and `suffix` slots to add icons.
|
||||
<sl-menu-item value="option-3">Option 3</sl-menu-item>
|
||||
<sl-icon name="chat" slot="suffix"></sl-icon>
|
||||
</sl-select>
|
||||
<br>
|
||||
<br />
|
||||
<sl-select placeholder="Large" size="large">
|
||||
<sl-icon name="house" slot="prefix"></sl-icon>
|
||||
<sl-menu-item value="option-1">Option 1</sl-menu-item>
|
||||
@@ -230,4 +469,36 @@ Use the `prefix` and `suffix` slots to add icons.
|
||||
</sl-select>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlIcon, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlSelect placeholder="Small" size="small">
|
||||
<SlIcon name="house" slot="prefix"></SlIcon>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
<SlIcon name="chat" slot="suffix"></SlIcon>
|
||||
</SlSelect>
|
||||
<br />
|
||||
<SlSelect placeholder="Medium" size="medium">
|
||||
<SlIcon name="house" slot="prefix"></SlIcon>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
<SlIcon name="chat" slot="suffix"></SlIcon>
|
||||
</SlSelect>
|
||||
<br />
|
||||
<SlSelect placeholder="Large" size="large">
|
||||
<SlIcon name="house" slot="prefix"></SlIcon>
|
||||
<SlMenuItem value="option-1">Option 1</SlMenuItem>
|
||||
<SlMenuItem value="option-2">Option 2</SlMenuItem>
|
||||
<SlMenuItem value="option-3">Option 3</SlMenuItem>
|
||||
<SlIcon name="chat" slot="suffix"></SlIcon>
|
||||
</SlSelect>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
[component-metadata:sl-select]
|
||||
|
||||
@@ -54,11 +54,65 @@ 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
|
||||
|
||||
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.
|
||||
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">
|
||||
@@ -83,6 +137,35 @@ There are two built-in effects, `sheen` and `pulse`. Effects are intentionally s
|
||||
</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 +198,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 +246,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 +281,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 +329,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 +394,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
|
||||
@@ -201,4 +419,27 @@ Set the `--color` and `--sheen-color` custom properties to adjust the skeleton's
|
||||
<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]
|
||||
|
||||
@@ -8,6 +8,12 @@ Spinners are used to show the progress of an indeterminate operation.
|
||||
<sl-spinner></sl-spinner>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSpinner } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlSpinner />;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Size
|
||||
@@ -20,12 +26,37 @@ Spinners are sized based on the current font size. To change their size, set the
|
||||
<sl-spinner style="font-size: 3rem;"></sl-spinner>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSpinner } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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: 3rem; --track-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
|
||||
@@ -36,4 +67,18 @@ The spinner's colors can be changed by setting the `--indicator-color` and `--tr
|
||||
<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]
|
||||
|
||||
758
docs/components/split-panel.md
Normal file
@@ -0,0 +1,758 @@
|
||||
# Split Panel
|
||||
|
||||
[component-header:sl-split-panel]
|
||||
|
||||
Split panels display two adjacent panels, allowing the user to reposition them.
|
||||
|
||||
```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-menu-item value="">None</sl-menu-item>
|
||||
<sl-menu-item value="start">Start</sl-menu-item>
|
||||
<sl-menu-item value="end">End</sl-menu-item>
|
||||
</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 handle, slot an icon or another element into the `handle` 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="handle" 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="handle" 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-handle">
|
||||
<sl-split-panel>
|
||||
<sl-icon slot="handle" 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-handle sl-split-panel {
|
||||
--divider-width: 2px;
|
||||
}
|
||||
|
||||
.split-panel-handle sl-split-panel::part(divider) {
|
||||
background-color: var(--sl-color-pink-600);
|
||||
}
|
||||
|
||||
.split-panel-handle 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-handle sl-split-panel::part(divider):focus-visible {
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.split-panel-handle 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-handle sl-split-panel {
|
||||
--divider-width: 2px;
|
||||
}
|
||||
|
||||
.split-panel-handle sl-split-panel::part(divider) {
|
||||
background-color: var(--sl-color-pink-600);
|
||||
}
|
||||
|
||||
.split-panel-handle 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-handle sl-split-panel::part(divider):focus-visible {
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.split-panel-handle 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-handle">
|
||||
<SlSplitPanel>
|
||||
<SlIcon slot="handle" 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,19 @@
|
||||
|
||||
[component-header:sl-switch]
|
||||
|
||||
Switches allow the user to toggle an option on or off.
|
||||
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) 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 +26,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 +40,32 @@ Use the `disabled` attribute to disable the switch.
|
||||
<sl-switch disabled>Disabled</sl-switch>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => <SlSwitch disabled>Disabled</SlSwitch>;
|
||||
```
|
||||
|
||||
### Custom Size
|
||||
|
||||
Use the available custom properties to make the switch a different size.
|
||||
|
||||
```html preview
|
||||
<sl-switch style="--width: 80px; --height: 32px; --thumb-size: 26px;"></sl-switch>
|
||||
<sl-switch style="--width: 80px; --height: 32px; --thumb-size: 26px;">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]
|
||||
|
||||
@@ -20,6 +20,32 @@ Tab groups make use of [tabs](/components/tab) and [tab panels](/components/tab-
|
||||
</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,6 +66,32 @@ Tabs can be shown on the bottom by setting `placement` to `bottom`.
|
||||
</sl-tab-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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`.
|
||||
@@ -58,6 +110,32 @@ Tabs can be shown on the starting side by setting `placement` to `start`.
|
||||
</sl-tab-group>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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`.
|
||||
@@ -76,6 +154,32 @@ Tabs can be shown on the ending side by setting `placement` to `end`.
|
||||
</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` 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.
|
||||
@@ -112,6 +216,48 @@ Add the `closable` attribute to a tab to show a close button. This example shows
|
||||
</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 +308,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 +416,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]
|
||||
|
||||
@@ -18,6 +18,32 @@ Tab panels are used inside [tab groups](/components/tab-group) to display tabbed
|
||||
</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>
|
||||
);
|
||||
```
|
||||
|
||||
?> Additional demonstrations can be found in the [tab group examples](/components/tab-group).
|
||||
|
||||
[component-metadata:sl-tab-panel]
|
||||
|
||||
@@ -11,6 +11,19 @@ Tabs are used inside [tab groups](/components/tab-group) to represent and activa
|
||||
<sl-tab disabled>Disabled</sl-tab>
|
||||
```
|
||||
|
||||
```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]
|
||||
|
||||
@@ -5,11 +5,25 @@
|
||||
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="neutral">Neutral</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
|
||||
@@ -24,6 +38,18 @@ Use the `size` attribute 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` attribute to give tabs rounded edges.
|
||||
@@ -34,6 +60,24 @@ Use the `pill` attribute to give tabs rounded edges.
|
||||
<sl-tag size="large" pill>Large</sl-tag>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlTag } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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.
|
||||
@@ -51,7 +95,7 @@ Use the `removable` attribute to add a remove button to the tag.
|
||||
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>
|
||||
|
||||
@@ -62,4 +106,42 @@ Use the `removable` attribute to add a remove button to the tag.
|
||||
</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]
|
||||
|
||||
@@ -8,12 +8,44 @@ Textareas collect data from the user and allow multiple lines of text.
|
||||
<sl-textarea></sl-textarea>
|
||||
```
|
||||
|
||||
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) 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 +54,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,6 +68,12 @@ 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.
|
||||
@@ -38,6 +82,12 @@ Add the `filled` attribute to draw a filled textarea.
|
||||
<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 a textarea.
|
||||
@@ -46,36 +96,36 @@ Use the `disabled` attribute to disable a textarea.
|
||||
<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
|
||||
@@ -86,6 +136,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`.
|
||||
@@ -94,4 +150,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]
|
||||
|
||||
@@ -14,6 +14,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 +51,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>
|
||||
@@ -99,17 +109,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,6 +232,16 @@ 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` attribute to control when the tooltip is shown.
|
||||
@@ -128,7 +250,7 @@ Tooltips can be controller programmatically by setting the `trigger` attribute t
|
||||
<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,6 +261,27 @@ Tooltips can be controller programmatically by setting the `trigger` attribute t
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useState } from 'react';
|
||||
import { SlAvatar, SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Remove Arrows
|
||||
|
||||
You can control the size of tooltip arrows by overriding the `--sl-tooltip-arrow-size` design token.
|
||||
@@ -155,6 +298,22 @@ You can control the size of tooltip arrows by overriding the `--sl-tooltip-arrow
|
||||
</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.
|
||||
|
||||
```css
|
||||
@@ -170,10 +329,25 @@ Use the `content` slot to create tooltips with HTML content. Tooltips are design
|
||||
```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 content="This is a tooltip">
|
||||
<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>
|
||||
);
|
||||
```
|
||||
|
||||
### 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.
|
||||
@@ -191,7 +365,7 @@ Tooltips will be clipped if they're inside a container that has `overflow: auto|
|
||||
|
||||
<style>
|
||||
.tooltip-hoist {
|
||||
border: solid 2px rgb(var(--sl-panel-border-color));
|
||||
border: solid 2px var(--sl-panel-border-color);
|
||||
overflow: hidden;
|
||||
padding: var(--sl-spacing-medium);
|
||||
position: relative;
|
||||
@@ -199,4 +373,33 @@ Tooltips will be clipped if they're inside a container that has `overflow: auto|
|
||||
</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]
|
||||
|
||||
47
docs/components/visually-hidden.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Visually Hidden
|
||||
|
||||
[component-header:sl-visually-hidden]
|
||||
|
||||
The visually hidden utility makes content accessible to assistive devices without displaying it on the screen.
|
||||
|
||||
According to [The A11Y Project](https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/), "there are real world situations where visually hiding content may be appropriate, while the content should remain available to assistive technologies, such as screen readers. For instance, hiding a search field's label as a common magnifying glass icon is used in its stead."
|
||||
|
||||
Since visually hidden content can receive focus when tabbing, the element will become visible when something inside receives focus. This behavior is intentional, as sighted keyboards user won't be able to determine where the focus indicator is without it.
|
||||
|
||||
```html preview
|
||||
<div style="min-height: 100px;">
|
||||
<sl-visually-hidden>
|
||||
<a href="#">Skip to main content</a>
|
||||
</sl-visually-hidden>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Links That Open in New Windows
|
||||
|
||||
In this example, the link will open a new window. Screen readers will announce "opens in a new window" even though the text content isn't visible to sighted users.
|
||||
|
||||
```html preview
|
||||
<a href="https://example.com/" target="_blank">
|
||||
Visit External Page
|
||||
<sl-icon name="box-arrow-up-right"></sl-icon>
|
||||
<sl-visually-hidden>opens in a new window</sl-visually-hidden>
|
||||
</a>
|
||||
```
|
||||
|
||||
### Content Conveyed By Context
|
||||
|
||||
Adding a title or label may seem redundant at times, but they're very helpful for unsighted users. Rather than omit them, you can provide context to unsighted users with visually hidden content.
|
||||
|
||||
```html preview
|
||||
<sl-card style="width: 100%; max-width: 360px;">
|
||||
<header>
|
||||
<sl-visually-hidden>Personal Info</sl-visually-hidden>
|
||||
</header>
|
||||
<sl-input label="Name" style="margin-bottom: .5rem;"></sl-input>
|
||||
<sl-input label="Email" type="email"></sl-input>
|
||||
</sl-card>
|
||||
```
|
||||
|
||||
[component-metadata:sl-visually-hidden]
|
||||
46
docs/frameworks/angular.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Angular
|
||||
|
||||
Angular [plays nice](https://custom-elements-everywhere.com/#angular) with custom elements, so you can use Shoelace in your Angular apps with ease.
|
||||
|
||||
## Installation
|
||||
|
||||
To add Shoelace to your Angular app, install the package from npm.
|
||||
|
||||
```bash
|
||||
npm install @shoelace-style/shoelace
|
||||
```
|
||||
|
||||
Next, [include a theme](/getting-started/themes) and set the [base path](/getting-started/installation#setting-the-base-path) for icons and other assets. In this example, we'll import the light theme and use the CDN as a base path.
|
||||
|
||||
```jsx
|
||||
import '@shoelace-style/shoelace/dist/themes/light.css';
|
||||
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path';
|
||||
|
||||
setBasePath('https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/');
|
||||
```
|
||||
|
||||
?> If you'd rather not use the CDN for assets, you can create a build task that copies `node_modules/@shoelace-style/shoelace/dist/assets` into a public folder in your app. Then you can point the base path to that folder instead.
|
||||
|
||||
## Configuration
|
||||
|
||||
Then make sure to apply the custom elements schema as shown below.
|
||||
|
||||
```js
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [BrowserModule],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class AppModule {}
|
||||
```
|
||||
|
||||
Now you can start using Shoelace components in your app!
|
||||
|
||||
?> Are you using Shoelace with Angular? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/angular.md)
|
||||
142
docs/frameworks/react.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# React
|
||||
|
||||
Shoelace offers a React version of every component to provide an idiomatic experience for React users. You can easily toggle between HTML and React examples throughout the documentation.
|
||||
|
||||
## Installation
|
||||
|
||||
To add Shoelace to your React app, install the package from npm.
|
||||
|
||||
```bash
|
||||
npm install @shoelace-style/shoelace
|
||||
```
|
||||
|
||||
Next, [include a theme](/getting-started/themes) and set the [base path](/getting-started/installation#setting-the-base-path) for icons and other assets. In this example, we'll import the light theme and use the CDN as a base path.
|
||||
|
||||
```jsx
|
||||
// App.jsx
|
||||
import '@shoelace-style/shoelace/dist/themes/light.css';
|
||||
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path';
|
||||
|
||||
setBasePath('https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/');
|
||||
```
|
||||
|
||||
?> If you'd rather not use the CDN for assets, you can create a [build task](https://webpack.js.org/plugins/copy-webpack-plugin/) that copies `node_modules/@shoelace-style/shoelace/dist/assets` into your app's `public` directory. Then you can point the base path to that folder instead.
|
||||
|
||||
Now you can start using components!
|
||||
|
||||
## Usage
|
||||
|
||||
### Importing Components
|
||||
|
||||
Every Shoelace component is available to import as a React component. Note that we're importing the `<SlButton>` _React component_ instead of the `<sl-button>` _custom element_ in the example below.
|
||||
|
||||
```jsx
|
||||
import { SlButton } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const MyComponent = () => <SlButton variant="primary">Click me</SlButton>;
|
||||
|
||||
export default MyComponent;
|
||||
```
|
||||
|
||||
You can find a copy + paste import for each component in the "importing" section of its documentation.
|
||||
|
||||
### Event Handling
|
||||
|
||||
Many Shoelace components emit [custom events](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). For example, the [input component](/components/input) emits the `sl-input` event when it receives input. In React, you can listen for the event using `onSlInput`.
|
||||
|
||||
Here's how you can bind the input's value to a state variable.
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react';
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
function MyComponent() {
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
return <SlInput value={value} onSlInput={event => setValue(event.target.value)} />;
|
||||
}
|
||||
|
||||
export default MyComponent;
|
||||
```
|
||||
|
||||
If you're using TypeScript, it's important to note that `event.target` will be a reference to the underlying custom element. You can use `(event.target as any).value` as a quick fix, or you can strongly type the event target as shown below.
|
||||
|
||||
```tsx
|
||||
import { useState } from 'react';
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
import type SlInputElement from '@shoelace-style/shoelace/dist/components/input/input';
|
||||
|
||||
function MyComponent() {
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
return <SlInput value={value} onSlInput={event => setValue((event.target as SlInputElement).value)} />;
|
||||
}
|
||||
|
||||
export default MyComponent;
|
||||
```
|
||||
|
||||
## Testing with Jest
|
||||
|
||||
Testing with web components can be challenging if your test environment runs in a Node environment (i.e. it doesn't run in a real browser). Fortunately, [Jest](https://jestjs.io/) has made a number of strides to support web components and provide additional browser APIs. However, it's still not a complete replication of a browser environment.
|
||||
|
||||
Here are some tips that will help smooth things over if you're having trouble with Jest + Shoelace.
|
||||
|
||||
?> If you're looking for a fast, modern testing alternative, consider [Web Test Runner](https://modern-web.dev/docs/test-runner/overview/).
|
||||
|
||||
### Upgrade Jest
|
||||
|
||||
Jest underwent a major revamp and received support for web components in [version 26.5.0](https://github.com/facebook/jest/blob/main/CHANGELOG.md#2650) when it introduced [JSDOM 16.2.0](https://github.com/jsdom/jsdom/blob/master/Changelog.md#1620). This release also included a number of mocks for built-in browser functions such as `MutationObserver`, `document.createRange`, and others.
|
||||
|
||||
If you're using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html#create-react-app), you can update `react-scripts` which will also update Jest.
|
||||
|
||||
```
|
||||
npm install react-scripts@latest
|
||||
```
|
||||
|
||||
### Mock Missing APIs
|
||||
|
||||
Some components use `window.matchMedia`, but this function isn't supported by JSDOM so you'll need to mock it yourself.
|
||||
|
||||
In `src/setupTests.js`, add the following.
|
||||
|
||||
```js
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation(query => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // deprecated
|
||||
removeListener: jest.fn(), // deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn()
|
||||
}))
|
||||
});
|
||||
```
|
||||
|
||||
For more details, refer to Jest's [manual mocking](https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom) documentation.
|
||||
|
||||
### Transform ES Modules
|
||||
|
||||
ES Modules are a [well-supported browser standard](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/). This is how Shoelace is distributed, but most React apps expect CommonJS. As a result, you'll probably run into the following error.
|
||||
|
||||
```
|
||||
Error: Unable to import outside of a module
|
||||
```
|
||||
|
||||
To fix this, add the following to your `package.json` which tells the transpiler to process Shoelace modules.
|
||||
|
||||
```js
|
||||
{
|
||||
"jest": {
|
||||
"transformIgnorePatterns": ["node_modules/?!(@shoelace)"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
These instructions are for apps created via Create React App. If you're using Jest directly, you can add `transformIgnorePatterns` directly into `jest.config.js`.
|
||||
|
||||
For more details, refer to Jest's [`transformIgnorePatterns` customization](https://jestjs.io/docs/tutorial-react-native#transformignorepatterns-customization) documentation.
|
||||
|
||||
?> Are you using Shoelace with React? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/react.md)
|
||||
92
docs/frameworks/vue-2.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Vue (version 2)
|
||||
|
||||
Vue [plays nice](https://custom-elements-everywhere.com/#vue) with custom elements, so you can use Shoelace in your Vue apps with ease.
|
||||
|
||||
!> These instructions are for Vue 2. If you're using Vue 3 or above, please see the [Vue 3 instructions](/frameworks/vue).
|
||||
|
||||
## Installation
|
||||
|
||||
To add Shoelace to your Vue app, install the package from npm.
|
||||
|
||||
```bash
|
||||
npm install @shoelace-style/shoelace
|
||||
```
|
||||
|
||||
Next, [include a theme](/getting-started/themes) and set the [base path](/getting-started/installation#setting-the-base-path) for icons and other assets. In this example, we'll import the light theme and use the CDN as a base path.
|
||||
|
||||
```jsx
|
||||
import '@shoelace-style/shoelace/dist/themes/light.css';
|
||||
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path';
|
||||
|
||||
setBasePath('https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/');
|
||||
```
|
||||
|
||||
?> If you'd rather not use the CDN for assets, you can create a build task that copies `node_modules/@shoelace-style/shoelace/dist/assets` into a public folder in your app. Then you can point the base path to that folder instead.
|
||||
|
||||
## Configuration
|
||||
|
||||
You'll need to tell Vue to ignore Shoelace components. This is pretty easy because they all start with `sl-`.
|
||||
|
||||
```js
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('sl-');
|
||||
|
||||
app.mount('#app');
|
||||
```
|
||||
|
||||
Now you can start using Shoelace components in your app!
|
||||
|
||||
## Usage
|
||||
|
||||
### Binding Complex Data
|
||||
|
||||
When binding complex data such as objects and arrays, use the `.prop` modifier to make Vue bind them as a property instead of an attribute.
|
||||
|
||||
```html
|
||||
<sl-color-picker :swatches.prop="mySwatches" />
|
||||
```
|
||||
|
||||
### Two-way Binding
|
||||
|
||||
One caveat is there's currently [no support for v-model on custom elements](https://github.com/vuejs/vue/issues/7830), but you can still achieve two-way binding manually.
|
||||
|
||||
```html
|
||||
<!-- This doesn't work -->
|
||||
<sl-input v-model="name">
|
||||
<!-- This works, but it's a bit longer -->
|
||||
<sl-input :value="name" @input="name = $event.target.value"></sl-input
|
||||
></sl-input>
|
||||
```
|
||||
|
||||
If that's too verbose for your liking, you can use a custom directive instead. [This utility](https://www.npmjs.com/package/@shoelace-style/vue-sl-model) adds a custom directive that will work just like `v-model` but for Shoelace components. To install it, use this command.
|
||||
|
||||
```bash
|
||||
npm install @shoelace-style/vue-sl-model
|
||||
```
|
||||
|
||||
Next, import the directive and enable it like this.
|
||||
|
||||
```js
|
||||
import ShoelaceModelDirective from '@shoelace-style/vue-sl-model';
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(ShoelaceModelDirective);
|
||||
|
||||
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('sl-');
|
||||
|
||||
app.mount('#app');
|
||||
```
|
||||
|
||||
Now you can use the `v-sl-model` directive to keep your data in sync!
|
||||
|
||||
```html
|
||||
<sl-input v-sl-model="name"></sl-input>
|
||||
```
|
||||
|
||||
?> Are you using Shoelace with Vue? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/vue.md)
|
||||
100
docs/frameworks/vue.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Vue
|
||||
|
||||
Vue [plays nice](https://custom-elements-everywhere.com/#vue) with custom elements, so you can use Shoelace in your Vue apps with ease.
|
||||
|
||||
?> These instructions are for Vue 3 and above. If you're using Vue 2, please see the [Vue 2 instructions](/frameworks/vue-2).
|
||||
|
||||
## Installation
|
||||
|
||||
To add Shoelace to your Vue app, install the package from npm.
|
||||
|
||||
```bash
|
||||
npm install @shoelace-style/shoelace
|
||||
```
|
||||
|
||||
Next, [include a theme](/getting-started/themes) and set the [base path](/getting-started/installation#setting-the-base-path) for icons and other assets. In this example, we'll import the light theme and use the CDN as a base path.
|
||||
|
||||
```jsx
|
||||
import '@shoelace-style/shoelace/dist/themes/light.css';
|
||||
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path';
|
||||
|
||||
setBasePath('https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/');
|
||||
```
|
||||
|
||||
?> If you'd rather not use the CDN for assets, you can create a build task that copies `node_modules/@shoelace-style/shoelace/dist/assets` into a public folder in your app. Then you can point the base path to that folder instead.
|
||||
|
||||
## Configuration
|
||||
|
||||
You'll need to tell Vue to ignore Shoelace components. This is pretty easy because they all start with `sl-`.
|
||||
|
||||
```js
|
||||
import { fileURLToPath, URL } from 'url';
|
||||
|
||||
import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue({
|
||||
template: {
|
||||
compilerOptions: {
|
||||
isCustomElement: tag => tag.startsWith('sl-')
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Now you can start using Shoelace components in your app!
|
||||
|
||||
## Usage
|
||||
|
||||
### QR code generator example
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="container">
|
||||
<h1>QR code generator</h1>
|
||||
|
||||
<sl-input maxlength="255" clearable label="Value" v-model="qrCode"></sl-input>
|
||||
|
||||
<sl-qr-code :value="qrCode"></sl-qr-code>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import '@shoelace-style/shoelace/dist/components/qr-code/qr-code.js';
|
||||
import '@shoelace-style/shoelace/dist/components/input/input.js';
|
||||
|
||||
const qrCode = ref();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
sl-input {
|
||||
margin: var(--sl-spacing-large) 0;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### Binding Complex Data
|
||||
|
||||
When binding complex data such as objects and arrays, use the `.prop` modifier to make Vue bind them as a property instead of an attribute.
|
||||
|
||||
```html
|
||||
<sl-color-picker :swatches.prop="mySwatches" />
|
||||
```
|
||||
|
||||
?> Are you using Shoelace with Vue? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/vue.md)
|
||||
@@ -29,7 +29,7 @@ To customize a design token, simply override it in your stylesheet using a `:roo
|
||||
}
|
||||
```
|
||||
|
||||
Many design tokens are described further along in this documentation. For a complete list, refer to `src/themes/light.styles.ts` in the project's [source code](https://github.com/shoelace-style/shoelace/blob/current/src/themes/light.styles.ts).
|
||||
Many design tokens are described further along in this documentation. For a complete list, refer to `src/themes/light.css` in the project's [source code](https://github.com/shoelace-style/shoelace/blob/current/src/themes/light.css).
|
||||
|
||||
## Component Parts
|
||||
|
||||
@@ -40,26 +40,24 @@ Shoelace components use a [shadow DOM](https://developer.mozilla.org/en-US/docs/
|
||||
Here's an example that modifies buttons with the `tomato-button` class.
|
||||
|
||||
```html preview
|
||||
<sl-button class="tomato-button">
|
||||
Tomato Button
|
||||
</sl-button>
|
||||
<sl-button class="tomato-button"> Tomato Button </sl-button>
|
||||
|
||||
<style>
|
||||
.tomato-button::part(base) {
|
||||
background: rgb(var(--sl-color-neutral-0));
|
||||
background: var(--sl-color-neutral-0);
|
||||
border: solid 1px tomato;
|
||||
}
|
||||
|
||||
.tomato-button::part(base):hover {
|
||||
background: rgba(255, 99, 71, .1);
|
||||
}
|
||||
background: rgba(255, 99, 71, 0.1);
|
||||
}
|
||||
|
||||
.tomato-button::part(base):active {
|
||||
background: rgba(255, 99, 71, .2);
|
||||
}
|
||||
background: rgba(255, 99, 71, 0.2);
|
||||
}
|
||||
|
||||
.tomato-button::part(base):focus {
|
||||
box-shadow: 0 0 0 3px rgba(255, 99, 71, .33);
|
||||
.tomato-button::part(base):focus-visible {
|
||||
box-shadow: 0 0 0 3px rgba(255, 99, 71, 0.33);
|
||||
}
|
||||
|
||||
.tomato-button::part(label) {
|
||||
@@ -74,9 +72,9 @@ At first glance, this approach might seem a bit verbose or even limiting, but it
|
||||
|
||||
- The internal structure of a component will likely change as it evolves. By exposing component parts through an API, the internals can be reworked without fear of breaking customizations as long as its parts remain intact.
|
||||
|
||||
- It encourages us to think more about how components are designed and how customizations should be allowed before users can take advantage of them. Once we opt a part into the component's API, it's guaranteed to be supported and can't be removed until a major version of the library is released.
|
||||
- It encourages us to think more about how components are designed and how customizations should be allowed before users can take advantage of them. Once we opt a part into the component's API, it's guaranteed to be supported and can't be removed until a major version of the library is released.
|
||||
|
||||
Most (but not all) components expose parts. You can find them in each component's API documention under the "CSS Parts" section.
|
||||
Most (but not all) components expose parts. You can find them in each component's API documentation under the "CSS Parts" section.
|
||||
|
||||
## Custom Properties
|
||||
|
||||
@@ -110,7 +108,7 @@ Not all components expose CSS custom properties. For those that do, they can be
|
||||
|
||||
Some components use animation, such as when a dialog is shown or hidden. Animations are performed using the [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API) rather than CSS. However, you can still customize them through Shoelace's animation registry. If a component has customizable animations, they'll be listed in the "Animation" section of its documentation.
|
||||
|
||||
To customize a default animation, use the `setDefaultAnimation()` method. The function accepts an animation name (found in the component's docs) and an object with `keyframes` and `options` or `null` to disable the animation.
|
||||
To customize a default animation, use the `setDefaultAnimation()` method. The function accepts an animation name (found in the component's docs) and an object with `keyframes`, and `options` or `null` to disable the animation.
|
||||
|
||||
This example will make all dialogs use a custom show animation.
|
||||
|
||||
@@ -129,6 +127,8 @@ setDefaultAnimation('dialog.show', {
|
||||
});
|
||||
```
|
||||
|
||||
?> To support RTL languages in your animation, you can pass an additional property called `rtlKeyframes`. This property shares the same type as `keyframes` and will be automatically used when the component's directionality is RTL. If `rtlKeyframes` is not provided, `keyframes` will be used as a fallback.
|
||||
|
||||
If you only want to target a single component, use the `setAnimation()` method instead. This function accepts an element, an animation name, and an object comprised of animation `keyframes` and `options`.
|
||||
|
||||
In this example, only the target dialog will use a custom show animation.
|
||||
|
||||
342
docs/getting-started/form-controls.md
Normal file
@@ -0,0 +1,342 @@
|
||||
# Form Controls
|
||||
|
||||
Every Shoelace component makes 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 do not recognize form controls located inside a shadow root.
|
||||
|
||||
Shoelace solves this problem by using the [`formdata`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/formdata_event) event, which is [available in all modern browsers](https://caniuse.com/mdn-api_htmlformelement_formdata_event). This means, when a form is submitted, Shoelace form controls will automatically append their values to the `FormData` object that's used to submit the form. In most cases, things will "just work." However, if you're using a form serialization library, it might need to be adapted to recognize Shoelace form controls.
|
||||
|
||||
?> If you're using an older browser that doesn't support the `formdata` event, a lightweight polyfill will be automatically applied to ensure forms submit as expected.
|
||||
|
||||
## A Note About Event Handling
|
||||
|
||||
Shoelace uses event listeners to intercept the form's `formdata` and `submit` events. This allows it to inject data and trigger validation as necessary. If you're also attaching an event listener to the form, _you must attach it after Shoelace form controls are connected to the DOM_, otherwise your logic will run before Shoelace has a chance to inject form data and validate form controls.
|
||||
|
||||
## Form Serialization
|
||||
|
||||
Serialization is just a fancy word for collecting form data. If you're relying on standard form submissions, e.g. `<form action="...">`, you can probably skip this section. However, most modern apps use the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) or a library such as [axios](https://github.com/axios/axios) to submit forms using JavaScript.
|
||||
|
||||
The [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) interface offers a standard way to serialize forms in the browser. You can create a `FormData` object from any `<form>` element like this.
|
||||
|
||||
```js
|
||||
const form = document.querySelector('form');
|
||||
const data = new FormData(form);
|
||||
|
||||
// All form control data is available in a FormData object
|
||||
```
|
||||
|
||||
However, some folks find `FormData` tricky to work with or they need to pass a JSON payload to their server. To accommodate this, Shoelace offers a serialization utility that gathers form data and returns a simple JavaScript object instead.
|
||||
|
||||
```js
|
||||
import { serialize } from '@shoelace-style/shoelace/dist/utilities/form.js';
|
||||
|
||||
const form = document.querySelector('form');
|
||||
const data = serialize(form);
|
||||
|
||||
// All form control data is available in a plain object
|
||||
```
|
||||
|
||||
This results in an object with name/value pairs that map to each form control. If more than one form control shares the same name, the values will be passed as an array, e.g. `{ name: ['value1', 'value2'] }`.
|
||||
|
||||
## 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 Shoelace form controls. You can activate it using attributes such as `required`, `pattern`, `minlength`, and `maxlength`. Shoelace implements many of the same attributes as native form controls, but check each form control's documentation for a list of all supported properties.
|
||||
|
||||
As the user interacts with a form control, its `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 surrounding `<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.
|
||||
|
||||
!> 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
|
||||
<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="reset" variant="default">Reset</sl-button>
|
||||
<sl-button type="submit" variant="primary">Submit</sl-button>
|
||||
</form>
|
||||
|
||||
<script type="module">
|
||||
const form = document.querySelector('.input-validation-required');
|
||||
form.addEventListener('submit', event => {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlCheckbox, SlInput, SlMenuItem, SlSelect, SlTextarea } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<SlInput name="name" label="Name" required />
|
||||
<br />
|
||||
<SlSelect label="Favorite Animal" clearable required>
|
||||
<SlMenuItem value="birds">Birds</SlMenuItem>
|
||||
<SlMenuItem value="cats">Cats</SlMenuItem>
|
||||
<SlMenuItem value="dogs">Dogs</SlMenuItem>
|
||||
<SlMenuItem value="other">Other</SlMenuItem>
|
||||
</SlSelect>
|
||||
<br />
|
||||
<SlTextarea name="comment" label="Comment" required></SlTextarea>
|
||||
<br />
|
||||
<SlCheckbox required>Check me before submitting</SlCheckbox>
|
||||
<br />
|
||||
<br />
|
||||
<SlButton type="submit" variant="primary">
|
||||
Submit
|
||||
</SlButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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
|
||||
<form class="input-validation-pattern">
|
||||
<sl-input name="letters" required label="Letters" pattern="[A-Za-z]+"></sl-input>
|
||||
<br />
|
||||
<sl-button type="reset" variant="default">Reset</sl-button>
|
||||
<sl-button type="submit" variant="primary">Submit</sl-button>
|
||||
</form>
|
||||
|
||||
<script type="module">
|
||||
const form = document.querySelector('.input-validation-pattern');
|
||||
form.addEventListener('submit', event => {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<SlInput name="letters" required label="Letters" pattern="[A-Za-z]+" />
|
||||
<br />
|
||||
<SlButton type="submit" variant="primary">
|
||||
Submit
|
||||
</SlButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Input Types
|
||||
|
||||
Some input types will automatically trigger constraints, such as `email` and `url`.
|
||||
|
||||
```html preview
|
||||
<form class="input-validation-type">
|
||||
<sl-input variant="email" label="Email" placeholder="you@example.com" required></sl-input>
|
||||
<br />
|
||||
<sl-input variant="url" label="URL" placeholder="https://example.com/" required></sl-input>
|
||||
<br />
|
||||
<sl-button type="reset" variant="default">Reset</sl-button>
|
||||
<sl-button type="submit" variant="primary">Submit</sl-button>
|
||||
</form>
|
||||
|
||||
<script type="module">
|
||||
const form = document.querySelector('.input-validation-type');
|
||||
form.addEventListener('submit', event => {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlButton, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<SlInput variant="email" label="Email" placeholder="you@example.com" required />
|
||||
<br />
|
||||
<SlInput variant="url" label="URL" placeholder="https://example.com/" required />
|
||||
<br />
|
||||
<SlButton type="submit" variant="primary">
|
||||
Submit
|
||||
</SlButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Custom Validation
|
||||
|
||||
To create a custom validation error, pass a non-empty string to the `setCustomValidity()` method. This will override any existing validation constraints. The form will not be submitted when a custom validity is set and the browser will show a validation error when the containing form is submitted. To make the input valid again, call `setCustomValidity()` again with an empty string.
|
||||
|
||||
```html preview
|
||||
<form class="input-validation-custom">
|
||||
<sl-input label="Type 'shoelace'" required></sl-input>
|
||||
<br />
|
||||
<sl-button type="reset" variant="default">Reset</sl-button>
|
||||
<sl-button type="submit" variant="primary">Submit</sl-button>
|
||||
</form>
|
||||
|
||||
<script type="module">
|
||||
const form = document.querySelector('.input-validation-custom');
|
||||
const input = form.querySelector('sl-input');
|
||||
|
||||
form.addEventListener('submit', event => {
|
||||
event.preventDefault();
|
||||
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>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { useRef, useState } from 'react';
|
||||
import { SlButton, SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const App = () => {
|
||||
const input = useRef(null);
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
function handleInput(event) {
|
||||
setValue(event.target.value);
|
||||
|
||||
if (event.target.value === 'shoelace') {
|
||||
input.current.setCustomValidity('');
|
||||
} else {
|
||||
input.current.setCustomValidity("Hey, you're supposed to type 'shoelace' before submitting this!");
|
||||
}
|
||||
}
|
||||
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
alert('All fields are valid!');
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<SlInput ref={input} label="Type 'shoelace'" required value={value} onSlInput={handleInput} />
|
||||
<br />
|
||||
<SlButton type="submit" variant="primary">
|
||||
Submit
|
||||
</SlButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
?> Custom validation can be applied to any form control that supports the `setCustomValidity()` method. It is not limited to inputs and textareas.
|
||||
|
||||
### 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" label="Type Something" 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-within::part(base) {
|
||||
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-500);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```jsx react
|
||||
import { SlInput } from '@shoelace-style/shoelace/dist/react';
|
||||
|
||||
const css = `
|
||||
.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-within::part(base) {
|
||||
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-500);
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlInput className="custom-input" required pattern="shoelace">
|
||||
<small slot="help-text">Please enter "shoelace" to continue</small>
|
||||
</SlInput>
|
||||
|
||||
<style>{css}</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` attribute 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` attribute 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
|
||||
<form novalidate>
|
||||
<sl-input class="invalid"></sl-input>
|
||||
</form>
|
||||
|
||||
<style>
|
||||
sl-input.invalid {
|
||||
...;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
@@ -2,21 +2,25 @@
|
||||
|
||||
You can use Shoelace via CDN or by installing it locally. You can also [cherry pick](#cherry-picking) individual components for faster load times.
|
||||
|
||||
## CDN Installation (Recommended)
|
||||
If you're using a framework, make sure to check out the pages for [React](/frameworks/react), [Vue](/frameworks/vue), and [Angular](/frameworks/angular).
|
||||
|
||||
## CDN Installation (Easiest)
|
||||
|
||||
The easiest way to install Shoelace is with the CDN. Just add the following tags to your page to get all components and the default light theme.
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/light.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/light.css" />
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/shoelace.js"></script>
|
||||
```
|
||||
|
||||
?> If you're only using a handful of components, it will be more efficient to [cherry pick](#cherry-picking) the ones you need.
|
||||
|
||||
### Dark Theme
|
||||
|
||||
If you prefer to use the dark theme instead, use this. Note the `sl-theme-dark` class on the `<html>` element. [Learn more about the Dark Theme.](/getting-started/themes#dark-theme)
|
||||
If you prefer to use the [dark theme](/getting-started/themes#dark-theme) instead, use this code and add `<html class="sl-theme-dark">` to the page.
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/dark.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/dark.css" />
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/shoelace.js"></script>
|
||||
```
|
||||
|
||||
@@ -25,10 +29,17 @@ If you prefer to use the dark theme instead, use this. Note the `sl-theme-dark`
|
||||
If you want to load the light or dark theme based on the user's `prefers-color-scheme` setting, use this. The `media` attributes ensure that only the user's preferred theme stylesheet loads and the `onload` attribute sets the appropriate [theme class](/getting-started/themes) on the `<html>` element.
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" media="(prefers-color-scheme:light)" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/light.css">
|
||||
<link rel="stylesheet" media="(prefers-color-scheme:dark)"
|
||||
href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/dark.css"
|
||||
onload="document.documentElement.classList.add('sl-theme-dark');">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
media="(prefers-color-scheme:light)"
|
||||
href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/light.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
media="(prefers-color-scheme:dark)"
|
||||
href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/dark.css"
|
||||
onload="document.documentElement.classList.add('sl-theme-dark');"
|
||||
/>
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/shoelace.js"></script>
|
||||
```
|
||||
|
||||
@@ -47,7 +58,7 @@ It's up to you to make the source files available to your app. One way to do thi
|
||||
Once you've done that, add the following tags to your page. Make sure to update `href` and `src` so they point to the route you created.
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="/shoelace/dist/themes/light.css">
|
||||
<link rel="stylesheet" href="/shoelace/dist/themes/light.css" />
|
||||
<script type="module" src="/shoelace/dist/shoelace.js"></script>
|
||||
```
|
||||
|
||||
@@ -63,13 +74,13 @@ However, if you're [cherry picking](#cherry-picking) or [bundling](#bundling) Sh
|
||||
|
||||
```html
|
||||
<!-- Option 1: the data-shoelace attribute -->
|
||||
<script src="bundle.js" data-shoelace="/path/to/shoelace"></script>
|
||||
<script src="bundle.js" data-shoelace="/path/to/shoelace/dist"></script>
|
||||
|
||||
<!-- Option 2: the setBasePath() method -->
|
||||
<script src="bundle.js"></script>
|
||||
<script type="module">
|
||||
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path.js';
|
||||
setBasePath('/path/to/shoelace');
|
||||
setBasePath('/path/to/shoelace/dist');
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -77,16 +88,16 @@ However, if you're [cherry picking](#cherry-picking) or [bundling](#bundling) Sh
|
||||
|
||||
## Cherry Picking
|
||||
|
||||
The previous approach is the _easiest_ way to load Shoelace, but easy isn't always efficient. You'll incur the full size of the library even if you only use a handful of components. This is convenient for prototyping, but may result in longer load times in production. To improve this, you can cherry pick the components you need.
|
||||
The previous approach is the _easiest_ way to load Shoelace, but easy isn't always efficient. You'll incur the full size of the library even if you only use a handful of components. This is convenient for prototyping or if you're using most of the components, but it may result in longer load times in production. To improve this, you can cherry pick the components you need.
|
||||
|
||||
Cherry picking can be done from your local install or [directly from the CDN](https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/). This will limit the number of files the browser has to download and reduce the amount of bytes being transferred. The disadvantage is that you need to load component manually.
|
||||
Cherry picking can be done from your local install or [directly from the CDN](https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/). This will limit the number of files the browser has to download and reduce the amount of bytes being transferred. The disadvantage is that you need to load components manually.
|
||||
|
||||
Here's an example that loads only the button component. Again, if you're not using a module resolver, you'll need to adjust the path to point to the folder Shoelace is in.
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="@shoelace-style/shoelace/dist/themes/light.css">
|
||||
<link rel="stylesheet" href="/path/to/shoelace/dist/themes/light.css" />
|
||||
|
||||
<script type="module" data-shoelace="/path/to/shoelace">
|
||||
<script type="module" data-shoelace="/path/to/shoelace/dist">
|
||||
import '@shoelace-style/shoelace/dist/components/button/button.js';
|
||||
|
||||
// <sl-button> is ready to use!
|
||||
@@ -125,7 +136,7 @@ import '@shoelace-style/shoelace/dist/components/rating/rating.js';
|
||||
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path.js';
|
||||
|
||||
// Set the base path to the folder you copied Shoelace's assets to
|
||||
setBasePath('/dist/shoelace');
|
||||
setBasePath('/path/to/shoelace/dist');
|
||||
|
||||
// <sl-button>, <sl-icon>, <sl-input>, and <sl-rating> are ready to use!
|
||||
```
|
||||
|
||||
128
docs/getting-started/localization.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Localization
|
||||
|
||||
Components can be localized by importing the appropriate translation file and setting the desired [`lang` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang) on the `<html>` element. Here's an example that renders Shoelace components in Spanish.
|
||||
|
||||
```html
|
||||
<html lang="es">
|
||||
<head>
|
||||
<script type="module" src="/path/to/shoelace/dist/translations/es.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Through the magic of a mutation observer, changing the `lang` attribute will automatically update all localized components to use the new locale.
|
||||
|
||||
## Available Translations
|
||||
|
||||
Shoelace ships with a number of translations. The default is English (US), which also serves as the fallback locale. As such, you do not need to import the English translation. To see a list of all available translations in the latest version, [refer to this directory](https://github.com/shoelace-style/shoelace/tree/current/src/translations).
|
||||
|
||||
The location of translations depends on how you're consuming Shoelace.
|
||||
|
||||
- If you're using the CDN, [import them from the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace?path=dist%2Ftranslations)
|
||||
- If you're using a bundler, import them from `@shoelace-style/shoelace/dist/translations/[lang].js`
|
||||
|
||||
You do not need to load translations up front. You can import them dynamically even after updating the `lang` attribute. Once a translation is registered, localized components will update automatically.
|
||||
|
||||
```js
|
||||
// Same as setting <html lang="de">
|
||||
document.documentElement.lang = 'de';
|
||||
|
||||
// Import the translation
|
||||
import('/path/to/shoelace/dist/translations/de.js');
|
||||
```
|
||||
|
||||
### Translation Resolution
|
||||
|
||||
The locale set by `<html lang="...">` is the default locale for the document. If a country code is provided, e.g. `es-PE` for Peruvian Spanish, the localization library will resolve it like this:
|
||||
|
||||
1. Look for `es-PE`
|
||||
2. Look for `es`
|
||||
3. Fall back to `en`
|
||||
|
||||
Shoelace uses English as a fallback to provide a better experience than rendering nothing or throwing an error.
|
||||
|
||||
### Submitting New Translations or Improvements
|
||||
|
||||
To contribute new translations or improvements to existing translations, please submit a pull request on GitHub. Translations are located in [`src/translations`](https://github.com/shoelace-style/shoelace/blob/next/src/translations) and can be edited directly on GitHub if you don't want to clone the repo locally.
|
||||
|
||||
Regional translations are welcome! For example, if a German translation (`de`) exists it's perfectly acceptable to submit a German (Switzerland) (`de-CH`) translation.
|
||||
|
||||
If you have any questions, please start a [discussion](https://github.com/shoelace-style/shoelace/discussions) or ask in the [community chat](https://discord.gg/mg8f26C).
|
||||
|
||||
?> Shoelace provides a localization mechanism for component internals. This is not designed to be used as localization tool for your entire application. You should use a more appropriate tool such as [i18next](https://www.i18next.com/) if you need to localize content in your app.
|
||||
|
||||
## Multiple Locales Per Page
|
||||
|
||||
You can use a different locale for an individual component by setting its `lang` attribute. Here's a contrived example to demonstrate.
|
||||
|
||||
```html
|
||||
<html lang="es">
|
||||
...
|
||||
|
||||
<body>
|
||||
<sl-button><!-- Spanish --></sl-button>
|
||||
<sl-button lang="ru"><!-- Russian --></sl-button>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
For performance reasons, the `lang` attribute must be on the component itself, not on an ancestor element.
|
||||
|
||||
```html
|
||||
<html lang="es">
|
||||
...
|
||||
|
||||
<body>
|
||||
<div lang="ru">
|
||||
<sl-button><!-- still in Spanish --></sl-button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
This limitation exists because there's no efficient way to determine the current locale of a given element in a DOM tree. I consider this a gap in the platform and [I've proposed a couple properties](https://github.com/whatwg/html/issues/7039) to make this possible.
|
||||
|
||||
## Creating Your Own Translations
|
||||
|
||||
You can provide your own translations if you have specific needs or if you don't want to wait for a translation to land upstream. The easiest way to do this is to copy `src/translations/en.ts` into your own project and translate the terms inside. When your translation is done, you can import it and use it just like a built-in translation.
|
||||
|
||||
Let's create a Spanish translation as an example. The following assumes you're using TypeScript, but you can also create translations with regular JavaScript.
|
||||
|
||||
```js
|
||||
import { registerTranslation } from '@shoelace-style/shoelace/dist/utilities/localize';
|
||||
import type { Translation } from '@shoelace-style/shoelace/dist/utilities/localize';
|
||||
|
||||
const translation: Translation = {
|
||||
$code: 'es',
|
||||
$name: 'Español',
|
||||
$dir: 'ltr',
|
||||
|
||||
term1: '...',
|
||||
term2: '...',
|
||||
...
|
||||
};
|
||||
|
||||
registerTranslation(translation);
|
||||
|
||||
export default translation;
|
||||
```
|
||||
|
||||
Once your translation has been compiled to JavaScript, import it and activate it like this.
|
||||
|
||||
```html
|
||||
<html lang="es">
|
||||
<head>
|
||||
<script type="module" src="/path/to/es.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
?> If your translation isn't working, make sure you're using the same localize module when importing `registerTranslation`. If you're using a different module, your translation won't be recognized.
|
||||
@@ -2,17 +2,19 @@
|
||||
<div class="splash-start">
|
||||
<img class="splash-logo" src="/assets/images/wordmark.svg" alt="Shoelace">
|
||||
|
||||
# <span hidden>Shoelace:</span> A forward-thinking library of web components.
|
||||
# <sl-visually-hidden>Shoelace:</sl-visually-hidden> A forward-thinking library of web components.
|
||||
|
||||
- Works with all frameworks 🧩
|
||||
- Works with CDNs 🚛
|
||||
- Fully customizable with CSS 🎨
|
||||
- Includes a dark theme 🌛
|
||||
- Built with accessibility in mind ♿️
|
||||
- First-party [React wrappers](/getting-started/usage#react)
|
||||
- First-class [React support](/frameworks/react) ⚛️
|
||||
- Built-in localization 💬
|
||||
- Open source 😸
|
||||
|
||||
Designed in New Hampshire by [Cory LaViska](https://twitter.com/claviska).
|
||||
|
||||
</div>
|
||||
<div class="splash-end">
|
||||
<img class="splash-image" src="/assets/images/undraw-content-team.svg" alt="Cartoon of people assembling components while standing on a giant laptop.">
|
||||
@@ -31,17 +33,17 @@ Designed in New Hampshire by [Cory LaViska](https://twitter.com/claviska).
|
||||
Add the following code to your page.
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/light.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/light.css" />
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/shoelace.js"></script>
|
||||
```
|
||||
|
||||
Now you have access to all of Shoelace's components! Try adding a button:
|
||||
|
||||
```html
|
||||
```html preview expanded
|
||||
<sl-button>Click me</sl-button>
|
||||
```
|
||||
|
||||
See the [installation instructions](getting-started/installation) for more details and other ways to install Shoelace.
|
||||
?> This will load all of Shoelace's components, but you should probably only load the ones you're actually using. To learn how, or for other ways to install Shoelace, refer to the [installation instructions](getting-started/installation).
|
||||
|
||||
## New to Web Components?
|
||||
|
||||
@@ -61,19 +63,21 @@ This is the technology that Shoelace is built on.
|
||||
|
||||
## What Problem Does This Solve?
|
||||
|
||||
Shoelace provides a collection of professionally designed, every day UI components built on a framework-agnostic technology. Why spend hundreds of hours (or more) building a design system from scratch? Why make a component library that only works with one framework?
|
||||
Shoelace provides a collection of professionally designed, highly customizable UI components built on a framework agnostic technology. Why spend hundreds of hours (or more) building a design system from scratch? Why make a component library that only works with one framework?
|
||||
|
||||
With Shoelace, you can:
|
||||
|
||||
- Start building things faster (no need to roll your own buttons)
|
||||
- Build multiple apps with different frameworks that all share the same UI
|
||||
- Skip having to learn a new component library every time you switch frameworks
|
||||
- Build multiple apps with different frameworks that all share the same UI components
|
||||
- Fully customize components to match your existing designs
|
||||
- Incrementally adopt components as needed (no need to ditch your framework)
|
||||
- Upgrade or switch frameworks without rebuilding foundational components
|
||||
|
||||
If your organization is looking to build a design system, [Shoelace will save you thousands of dollars](https://medium.com/eightshapes-llc/and-you-thought-buttons-were-easy-26eb5b5c1871).* All the foundational components you need are right here, ready to be customized for your brand. And since it's built on web standards, browsers will continue to support it for many years to come.
|
||||
If your organization is looking to build a design system, [Shoelace will save you thousands of dollars](https://medium.com/eightshapes-llc/and-you-thought-buttons-were-easy-26eb5b5c1871).\* All the foundational components you need are right here, ready to be customized for your brand. And since it's built on web standards, browsers will continue to support it for many years to come.
|
||||
|
||||
Whether you use Shoelace as a starting point for your organization's design system or for a fun personal project, there's no limit to what you can do with it.
|
||||
|
||||
<small>*Please consider giving back some of what you save by [supporting this project with a sponsorship](https://github.com/sponsors/claviska).</small>
|
||||
<small>\*Please consider giving back some of what you save by [supporting this project with a sponsorship](https://github.com/sponsors/claviska).</small>
|
||||
|
||||
## Browser Support
|
||||
|
||||
@@ -98,15 +102,15 @@ Designing, developing, and supporting this library requires a lot of time, effor
|
||||
👇 Your support is very much appreciated! 👇
|
||||
|
||||
<sl-button class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
|
||||
<sl-icon name="heart"></sl-icon> Become a sponsor
|
||||
<sl-icon slot="prefix" name="heart"></sl-icon> Become a sponsor
|
||||
</sl-button>
|
||||
|
||||
<sl-button class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
|
||||
<sl-icon name="github"></sl-icon> <span class="github-star-count">Star</span>
|
||||
<sl-icon slot="prefix" name="github"></sl-icon> Star
|
||||
</sl-button>
|
||||
|
||||
<sl-button class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
|
||||
<sl-icon name="twitter"></sl-icon> Follow
|
||||
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
|
||||
</sl-button>
|
||||
|
||||
## Attribution
|
||||
@@ -117,10 +121,10 @@ Special thanks to the following projects and individuals that help make Shoelace
|
||||
- Component metadata is generated by the [Custom Elements Manifest Analyzer](https://github.com/open-wc/custom-elements-manifest)
|
||||
- Documentation is powered by [Docsify](https://docsify.js.org/)
|
||||
- CDN services are provided by [jsDelivr](https://www.jsdelivr.com/)
|
||||
- Color primitives are derived from [Tailwind](https://tailwindcss.com/)
|
||||
- Color primitives are inspired by [Tailwind](https://tailwindcss.com/)
|
||||
- Icons are courtesy of [Bootstrap Icons](https://icons.getbootstrap.com/)
|
||||
- The homepage illustration is courtesy of [unDraw](https://undraw.co/)
|
||||
- Positioning of menus, tooltips, et al is handled by [Popper.js](https://popper.js.org/)
|
||||
- Positioning of dropdowns, tooltips, et al is handled by [Floating UI](https://floating-ui.com/)
|
||||
- Animations are courtesy of [animate.css](https://animate.style/)
|
||||
- QR codes are generated with [qr-creator](https://github.com/nimiq/qr-creator)
|
||||
- Search is powered by [Lunr](https://lunrjs.com/)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Themes
|
||||
|
||||
Shoelace is designed to be highly customizable through pure CSS. Out of the box, you can choose from a light or dark theme. Alternatively, you can design your own theme from scratch.
|
||||
Shoelace is designed to be highly customizable through pure CSS. Out of the box, you can choose from a light or dark theme. Alternatively, you can design your own theme.
|
||||
|
||||
A theme is nothing more than a stylesheet that uses the Shoelace API to define design tokens and apply custom styles to components. To create a theme, you will need a decent understanding of CSS, including [CSS Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) and the [`::part` selector](https://developer.mozilla.org/en-US/docs/Web/CSS/::part).
|
||||
|
||||
For developers, built-in themes are also available as JavaScript modules that export [Lit CSSResult](https://lit.dev/docs/api/styles/#CSSResult) objects. You can find them in `dist/themes/*.styles.js`.
|
||||
?> For component developers, built-in themes are also available as JavaScript modules that export [Lit CSSResult](https://lit.dev/docs/api/styles/#CSSResult) objects. You can find them in `dist/themes/*.styles.js`.
|
||||
|
||||
## Theme Basics
|
||||
|
||||
All themes are scoped to classes using the `sl-theme-{name}` convention, where `{name}` is a lowercase, hyphen-delimited value representing the name of the theme. The included light and dark themes use `sl-theme-light` and `sl-theme-dark`, respectively. A custom theme called "Purple Power", for example, would use the `sl-theme-purple-power` class.
|
||||
All themes are scoped to classes using the `sl-theme-{name}` convention, where `{name}` is a lowercase, hyphen-delimited value representing the name of the theme. The included light and dark themes use `sl-theme-light` and `sl-theme-dark`, respectively. A custom theme called "Purple Power", for example, would use a class called `sl-theme-purple-power`
|
||||
|
||||
Every selector must be scoped to the theme's class to ensure interoperability with other themes. You should also scope them to `:host` so they can be imported and applied to custom element shadow roots.
|
||||
All selectors must be scoped to the theme's class to ensure interoperability with other themes. You should also scope them to `:host` so they can be imported and applied to custom element shadow roots.
|
||||
|
||||
```css
|
||||
:host,
|
||||
@@ -21,12 +21,12 @@ Every selector must be scoped to the theme's class to ensure interoperability wi
|
||||
|
||||
### Activating Themes
|
||||
|
||||
To activate a theme, import it and apply the theme's class to the `<html>` element. This example imports and activates the dark theme, or "dark mode."
|
||||
To activate a theme, import it and apply the theme's class to the `<html>` element. This example imports and activates the built-in dark theme.
|
||||
|
||||
```html
|
||||
<html class="sl-theme-dark">
|
||||
<head>
|
||||
<link rel="stylesheet" href="path/to/shoelace/dist/themes/dark.css">
|
||||
<link rel="stylesheet" href="path/to/shoelace/dist/themes/dark.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -44,33 +44,35 @@ You can activate themes on various containers throughout the page. This example
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="path/to/shoelace/dist/themes/light.css">
|
||||
<link rel="stylesheet" href="path/to/shoelace/dist/themes/dark.css">
|
||||
<link rel="stylesheet" href="path/to/shoelace/dist/themes/light.css" />
|
||||
<link rel="stylesheet" href="path/to/shoelace/dist/themes/dark.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="sl-theme-dark">
|
||||
<!-- dark-themed sidebar -->
|
||||
</nav>
|
||||
|
||||
|
||||
<!-- light-themed content -->
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
It's for this reason that themes must be scoped to specific classes.
|
||||
|
||||
## Creating Themes
|
||||
|
||||
There are two ways to create themes. The easiest way is to customize a built-in theme. The advanced way is to create a new theme from scratch. Which method you choose depends on your project's requirements and the amount of effort you're willing to commit to.
|
||||
|
||||
### Customizing a Built-in Theme
|
||||
|
||||
The easiest way to customize Shoelace is to override one of the built-in themes. You can do this by importing the light or dark theme as-is, then creating a separate stylesheet that overrides the [design tokens](/getting-started/customizing#design-tokens) and adds [component styles](/getting-started/customizing#component-parts) to your liking. You must import your theme _after_ the built-in theme.
|
||||
The easiest way to customize Shoelace is to override one of the built-in themes. You can do this by importing the light or dark theme as-is, then creating a separate stylesheet that overrides [design tokens](/getting-started/customizing#design-tokens) and adds [component styles](/getting-started/customizing#component-parts) to your liking. You must import your theme _after_ the built-in theme.
|
||||
|
||||
If you're customizing the light theme, you should scope your styles to the following selectors.
|
||||
|
||||
```css
|
||||
:root,
|
||||
:host,
|
||||
:root,
|
||||
:host,
|
||||
.sl-theme-light {
|
||||
/* your custom styles here */
|
||||
}
|
||||
@@ -87,7 +89,7 @@ If you're customizing the dark theme, you should scope your styles to the follow
|
||||
|
||||
By customizing a built-in theme, you'll maintain a smaller stylesheet containing only the changes you've made. Contrast this to [creating a new theme](#creating-a-new-theme), where you need to explicitly define every design token required by the library. This approach is more "future-proof," as new design tokens that emerge in subsequent versions of Shoelace will be accounted for by the built-in theme.
|
||||
|
||||
While this may be easier to maintain, the drawback is that your theme modifies a built-in theme and thus can't be activated independently.
|
||||
While this approach is easier to maintain, the drawback is that your theme can't be activated independently — it's tied to the built-in theme you're extending.
|
||||
|
||||
### Creating a New Theme
|
||||
|
||||
@@ -108,14 +110,14 @@ You will, however, need to maintain your theme more carefully, as new versions o
|
||||
|
||||
## Dark Theme
|
||||
|
||||
The built-in dark theme uses an inverted + shifted color scale so, if you're using design tokens as intended, you'll get a decent dark mode for free. While this isn't the same as a professionally curated dark theme, it provides an excellent baseline for one and you're encouraged to customize it depending on your needs.
|
||||
The built-in dark theme uses an inverted color scale so, if you're using design tokens as intended, you'll get dark mode for free. While this isn't the same as a professionally curated dark theme, it provides an excellent baseline for one and you're encouraged to customize it depending on your needs.
|
||||
|
||||
This was achieved by taking the light theme's [color tokens](/tokens/color) and "flipping" the scale so 100 becomes 900, 200 becomes 800, 300 becomes 700, etc. Next, the luminance of each primitive was increased slightly to avoid true black, a color that is typically undesirable in dark themes. The result is a custom palette that complements the light theme well and makes it easy to offer light and dark variations with minimal effort.
|
||||
The dark theme works by taking the light theme's [color tokens](/tokens/color) and "flipping" the scale so 100 becomes 900, 200 becomes 800, 300 becomes 700, etc. Next, the luminance of each primitive was fine-tuned to avoid true black, which is often undesirable in dark themes, and provide a richer experience. The result is a custom dark palette that complements the light theme and makes it easy to offer light and dark modes with minimal effort.
|
||||
|
||||
To install the dark theme, add the following to the `<head>` section of your page.
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/dark.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/themes/dark.css" />
|
||||
```
|
||||
|
||||
To activate the theme, apply the `sl-theme-dark` class to the `<html>` element.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Usage
|
||||
|
||||
Shoelace components are just regular HTML elements, or "custom elements" to be precise. You can use them like any other element. Each component has detailed documentation that describes its full API, including properties, events, methods, and more.
|
||||
Shoelace components are just regular HTML elements, or [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) to be precise. You can use them like any other element. Each component has detailed documentation that describes its full API, including properties, events, methods, and more.
|
||||
|
||||
## Web Component Basics
|
||||
If you're new to custom elements, often referred to as "web components," this section will familiarize you with how to use them.
|
||||
|
||||
### Properties
|
||||
## Properties
|
||||
|
||||
Many components have properties that can be set using attributes. For example, buttons accept a `size` attribute that maps to the `size` property which dictates the button's size.
|
||||
|
||||
@@ -31,7 +31,7 @@ In rare cases, a property may require an array, an object, or a function. For ex
|
||||
|
||||
Refer to a component's documentation for a complete list of its properties.
|
||||
|
||||
### Events
|
||||
## Events
|
||||
|
||||
You can listen for standard events such as `click`, `mouseover`, etc. as you normally would. In addition, some components emit custom events. These work the same way as standard events, but are prefixed with `sl-` to prevent collisions with standard events and other libraries.
|
||||
|
||||
@@ -48,7 +48,7 @@ You can listen for standard events such as `click`, `mouseover`, etc. as you nor
|
||||
|
||||
Refer to a component's documentation for a complete list of its custom events.
|
||||
|
||||
### Methods
|
||||
## Methods
|
||||
|
||||
Some components have methods you can call to trigger various behaviors. For example, you can set focus on a Shoelace input using the `focus()` method.
|
||||
|
||||
@@ -63,7 +63,7 @@ Some components have methods you can call to trigger various behaviors. For exam
|
||||
|
||||
Refer to a component's documentation for a complete list of its methods and their arguments.
|
||||
|
||||
### Slots
|
||||
## Slots
|
||||
|
||||
Many components use slots to accept content inside of them. The most common slot is the _default_ slot, which includes any content inside the component that doesn't have a `slot` attribute.
|
||||
|
||||
@@ -86,7 +86,7 @@ The location of a named slot doesn't matter. You can put it anywhere inside the
|
||||
|
||||
Refer to a component's documentation for a complete list of available slots.
|
||||
|
||||
### Don't Use Self-closing Tags
|
||||
## Don't Use Self-closing Tags
|
||||
|
||||
Custom elements cannot have self-closing tags. Similar to `<script>` and `<textarea>`, you must always include the full closing tag.
|
||||
|
||||
@@ -98,7 +98,7 @@ Custom elements cannot have self-closing tags. Similar to `<script>` and `<texta
|
||||
<sl-input></sl-input>
|
||||
```
|
||||
|
||||
### Differences from Native Elements
|
||||
## Differences from Native Elements
|
||||
|
||||
You might expect similarly named elements to share the same API as native HTML elements. This is not always the case. Shoelace components **are not** designed to be one-to-one replacements for their HTML counterparts.
|
||||
|
||||
@@ -106,6 +106,49 @@ For example, `<button>` and `<sl-button>` both have a `type` attribute, but it d
|
||||
|
||||
?> **Don't make assumptions about a component's API!** To prevent unexpected behaviors, please take the time to review the documentation and make sure you understand what each attribute, property, method, and event is intended to do.
|
||||
|
||||
## Waiting for Components to Load
|
||||
|
||||
Web components are registered with JavaScript, so depending on how and when you load Shoelace, you may notice a [Flash of Undefined Custom Elements (FOUCE)](https://www.abeautifulsite.net/posts/flash-of-undefined-custom-elements/) when the page loads. There are a couple ways to prevent this, both of which are described in the linked article.
|
||||
|
||||
One option is to use the [`:defined`](https://developer.mozilla.org/en-US/docs/Web/CSS/:defined) CSS pseudo-class to "hide" custom elements that haven't been registered yet. You can scope it to specific tags or you can hide all undefined custom elements as shown below.
|
||||
|
||||
```css
|
||||
:not(:defined) {
|
||||
visibility: hidden;
|
||||
}
|
||||
```
|
||||
|
||||
As soon as a custom element is registered, it will immediately appear with all of its styles, effectively eliminating FOUCE. Note the use of `visibility: hidden` instead of `display: none` to reduce shifting as elements are registered. The drawback to this approach is that custom elements can potentially appear one by one instead of all at the same time.
|
||||
|
||||
Another option is to use [`customElements.whenDefined()`](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/whenDefined), which returns a promise that resolves when the specified element gets registered. You'll probably want to use it with [`Promise.allSettled()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled) in case an element fails to load for some reason.
|
||||
|
||||
A clever way to use this method is to hide the `<body>` with `opacity: 0` and add a class that fades it in as soon as all your custom elements are defined.
|
||||
|
||||
```html
|
||||
<style>
|
||||
body {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
body.ready {
|
||||
opacity: 1;
|
||||
transition: 0.25s opacity;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="module">
|
||||
await Promise.allSettled([
|
||||
customElements.whenDefined('sl-button'),
|
||||
customElements.whenDefined('sl-card'),
|
||||
customElements.whenDefined('sl-rating')
|
||||
]);
|
||||
|
||||
// Button, card, and rating are registered now! Add
|
||||
// the `ready` class so the UI fades in.
|
||||
document.body.classList.add('ready');
|
||||
</script>
|
||||
```
|
||||
|
||||
## Code Completion
|
||||
|
||||
### VS Code
|
||||
@@ -128,123 +171,3 @@ If `settings.json` already exists, simply add the above line to the root of the
|
||||
### Other Editors
|
||||
|
||||
Most popular editors support custom code completion with a bit of configuration. Please [submit a feature request](https://github.com/shoelace-style/shoelace/issues/new/choose) for your editor of choice. PRs are also welcome!
|
||||
|
||||
## React
|
||||
|
||||
React [doesn't play nice](https://custom-elements-everywhere.com/#react) with custom elements — it's a bit finicky about props.
|
||||
|
||||
> React passes all data to Custom Elements in the form of HTML attributes. For primitive data this is fine, but the system breaks down when passing rich data, like objects or arrays. In these instances you end up with stringified values like `some-attr="[object Object]"` which can't actually be used.
|
||||
|
||||
Event handling can also be cumbersome.
|
||||
|
||||
> Because React implements its own synthetic event system, it cannot listen for DOM events coming from Custom Elements without the use of a workaround. Developers will need to reference their Custom Elements using a ref and manually attach event listeners with addEventListener. This makes working with Custom Elements cumbersome.
|
||||
|
||||
Fortunately, there's a package called [@shoelace-style/react](https://www.npmjs.com/package/@shoelace-style/react) that will let you use Shoelace components as if they were React components. You can install it using this command.
|
||||
|
||||
```bash
|
||||
npm install @shoelace-style/react
|
||||
```
|
||||
|
||||
Include the default theme and any components you want to use in your app.
|
||||
|
||||
```jsx
|
||||
import '@shoelace-style/shoelace/dist/themes/light.css';
|
||||
|
||||
import SlButton from '@shoelace-style/react/dist/button';
|
||||
|
||||
// ...
|
||||
|
||||
const MyComponent = (props) => {
|
||||
return (
|
||||
<SlButton type="primary">
|
||||
Click me
|
||||
</SlButton>
|
||||
)
|
||||
};
|
||||
```
|
||||
|
||||
## Vue
|
||||
|
||||
Vue [plays nice](https://custom-elements-everywhere.com/#vue) with custom elements. You just have to tell it to ignore Shoelace components. This is pretty easy because they all start with `sl-`.
|
||||
|
||||
```js
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('sl-');
|
||||
|
||||
app.mount('#app');
|
||||
```
|
||||
|
||||
### Binding Complex Data
|
||||
|
||||
When binding complex data such as objects and arrays, use the `.prop` modifier to make Vue bind them as a property instead of an attribute.
|
||||
|
||||
```html
|
||||
<sl-color-picker :swatches.prop="mySwatches" />
|
||||
```
|
||||
|
||||
### Two-way Binding
|
||||
|
||||
One caveat is there's currently [no support for v-model on custom elements](https://github.com/vuejs/vue/issues/7830), but you can still achieve two-way binding manually.
|
||||
|
||||
```html
|
||||
<!-- This doesn't work -->
|
||||
<sl-input v-model="name">
|
||||
|
||||
<!-- This works, but it's a bit longer -->
|
||||
<sl-input :value="name" @input="name = $event.target.value">
|
||||
```
|
||||
|
||||
If that's too verbose, you can use a custom directive instead.
|
||||
|
||||
### Using a Custom Directive
|
||||
|
||||
You can use [this utility](https://www.npmjs.com/package/@shoelace-style/vue-sl-model) to add a custom directive that will work just like `v-model` but for Shoelace components. To install it, use this command.
|
||||
|
||||
```bash
|
||||
npm install @shoelace-style/vue-sl-model
|
||||
```
|
||||
|
||||
Next, import the directive and enable it like this.
|
||||
|
||||
```js
|
||||
import ShoelaceModelDirective from '@shoelace-style/vue-sl-model';
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(ShoelaceModelDirective);
|
||||
|
||||
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('sl-');
|
||||
|
||||
app.mount('#app');
|
||||
```
|
||||
|
||||
Now you can use the `v-sl-model` directive to keep your data in sync!
|
||||
|
||||
```html
|
||||
<sl-input v-sl-model="name">
|
||||
```
|
||||
|
||||
## Angular
|
||||
|
||||
Angular [plays nice](https://custom-elements-everywhere.com/#angular) with custom elements. Just make sure to apply the custom elements schema as shown below.
|
||||
|
||||
```js
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [BrowserModule],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class AppModule {}
|
||||
```
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
name="description"
|
||||
content="Shoelace provides a collection of professionally designed, every day UI components built on a framework-agnostic technology."
|
||||
/>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="custom-elements-manifest" content="dist/custom-elements.json" />
|
||||
<meta property="og:title" content="Shoelace" />
|
||||
<meta
|
||||
@@ -44,7 +41,7 @@
|
||||
<link rel="stylesheet" href="/dist/themes/dark.css" />
|
||||
<script type="module" src="/dist/shoelace.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<body data-shoelace="/dist">
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
// Set the initial theme to prevent flashing
|
||||
@@ -65,7 +62,6 @@
|
||||
},
|
||||
coverpage: false,
|
||||
executeScript: true,
|
||||
ga: 'UA-6412891-16',
|
||||
homepage: '/getting-started/overview.md',
|
||||
loadSidebar: true,
|
||||
logo: '/assets/images/wordmark.svg',
|
||||
@@ -81,15 +77,15 @@
|
||||
crossChapterText: false
|
||||
},
|
||||
routerMode: 'history',
|
||||
themeColor: 'rgb(var(--sl-color-primary-500))'
|
||||
themeColor: 'var(--sl-color-primary-500)'
|
||||
};
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/docsify.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/ga.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-copy-code@2"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-pagination@2/dist/docsify-pagination.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.19.0/components/prism-bash.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.19.0/components/prism-jsx.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.25.0/components/prism-bash.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.25.0/components/prism-jsx.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.25.0/components/prism-tsx.min.js"></script>
|
||||
<script src="/assets/plugins/code-block/code-block.js"></script>
|
||||
<script src="/assets/plugins/metadata/metadata.js"></script>
|
||||
<script src="/assets/plugins/scroll-position/scroll-position.js"></script>
|
||||
|
||||
18
docs/resources/accessibility.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Accessibility Commitment
|
||||
|
||||
Shoelace recognizes the need for all users, regardless of ability and device, to have undeterred access to the websites and applications that are created with it. This is an important goal of the project.
|
||||
|
||||
Oftentimes, people will ask “is Shoelace accessible?” I’m reluctant to answer because accessibility isn’t binary — there’s no simple “yes” or “no” response to provide. What seems accessible to a sighted user might be completely inaccessible to a non-sighted user. And even if you optimize for various screen readers, you still have to account for low-level vision, color blindness, hearing impairments, mobility impairments, and more.
|
||||
|
||||
Accessibility is something you have to continuously strive for. No individual contributor — or perhaps even an entire team — can claim their software is 100% accessible because of the sheer diversity of abilities, devices, assistive technologies, and individual use cases.
|
||||
|
||||
Furthermore, accessibility doesn’t stop at the component level. Using accessible building blocks doesn’t magically make the rest of your webpage or application compliant. There is no library or overlay that will make your software “fully accessible” without putting in the effort. It’s also worth noting that web components are still somewhat bleeding edge, so browsers, assistive devices, and [even specifications](https://wicg.github.io/aom/spec/) are still evolving to help improve accessibility on the web platform.
|
||||
|
||||
My commitment to Shoelace users is this: Everything I develop will be built with accessibility in mind. I will test and improve every component to the best of my ability and knowledge. I will work around upstream issues, such as browser bugs and limitations, to the best of my ability and within reason.
|
||||
|
||||
I’m fully aware that I may not get it right every time for every user, so I invite the community to participate in this ongoing effort by submitting [issues](https://github.com/shoelace-style/shoelace/issues?q=is%3Aissue+is%3Aopen+label%3Aa11y), [pull requests](https://github.com/shoelace-style/shoelace/pulls?q=is%3Aopen+is%3Apr+label%3Aa11y), and [discussions](https://github.com/shoelace-style/shoelace/discussions). Many accessibility improvements have already been made thanks to contributors submitting code, feedback, and suggestions.
|
||||
|
||||
This is the path forward. Together, we will continue to make Shoelace accessible to as many users as possible.
|
||||
|
||||
— Cory LaViska<br>
|
||||
_Creator of Shoelace_
|
||||
@@ -1,18 +1,339 @@
|
||||
# Changelog
|
||||
|
||||
Shoelace follows [Semantic Versioning](https://semver.org/). Breaking changes in components with the <sl-badge type="primary" pill>Stable</sl-badge> badge will not be accepted until the next major version. As such, all contributions must consider the project's roadmap and take this into consideration. Features that are deemed no longer necessary will be deprecated but not removed.
|
||||
Shoelace follows [Semantic Versioning](https://semver.org/). Breaking changes in components with the <sl-badge variant="primary" pill>Stable</sl-badge> badge will not be accepted until the next major version. As such, all contributions must consider the project's roadmap and take this into consideration. Features that are deemed no longer necessary will be deprecated but not removed.
|
||||
|
||||
Components with the <sl-badge type="warning" pill>Experimental</sl-badge> badge should not be used in production. They are made available as release candidates for development and testing purposes. As such, changes to experimental components will not be subject to semantic versioning.
|
||||
Components with the <sl-badge variant="warning" pill>Experimental</sl-badge> badge should not be used in production. They are made available as release candidates for development and testing purposes. As such, changes to experimental components will not be subject to semantic versioning.
|
||||
|
||||
New versions of Shoelace are released as-needed and generally occur when a critical mass of changes have accumulated. At any time, you can see what's coming in the next release by visiting [next.shoelace.style](https://next.shoelace.style).
|
||||
|
||||
_During the beta period, these restrictions may be relaxed in the event of a mission-critical bug._ 🐛
|
||||
|
||||
## Next
|
||||
## 2.0.0-beta.78
|
||||
|
||||
- Added experimental `<sl-context-menu>` component
|
||||
- 🚨 BREAKING: Moved the `checked-icon` and `indeterminate-icon` parts from a wrapper `<span>` to the `<svg>` in `<sl-checkbox>` [#786](https://github.com/shoelace-style/shoelace/issues/786)
|
||||
- 🚨 BREAKING: Moved the `checked-icon` part from a wrapper `<span>` to the `<svg>` in `<sl-radio>` [#786](https://github.com/shoelace-style/shoelace/issues/786)
|
||||
- Added the `--track-active-offset` CSS custom property to `<sl-range>` [#806](https://github.com/shoelace-style/shoelace/issues/806)
|
||||
- Fixed a bug that caused `<sl-select>` to sometimes have two vertical scrollbars [#814](https://github.com/shoelace-style/shoelace/issues/814)
|
||||
- Fixed a bug that caused a gray line to appear between radio buttons [#821](https://github.com/shoelace-style/shoelace/discussions/821)
|
||||
- Fixed a bug that caused `<sl-animated-image>` to not render anything when using the `play` attribute initially [#824](https://github.com/shoelace-style/shoelace/issues/824)
|
||||
- Removed `:focus-visible` shim now that the last two major versions of Safari support it
|
||||
- Updated Bootstrap Icons to 1.9.0
|
||||
- Updated esbuild to 0.14.49
|
||||
- Updated Floating UI to 0.5.4
|
||||
- Updated Lit to 2.2.7
|
||||
- Updated all other dependencies to latest versions
|
||||
|
||||
## 2.0.0-beta.77
|
||||
|
||||
- Added styles to required form controls so they show an asterisk next to the label by default
|
||||
- Added the `--sl-input-required-content` design token
|
||||
- Added the `required` attribute to `<sl-radio-group>` and fixed constraint validation logic to support custom validation
|
||||
- Added the `checked-icon` part to `<sl-menu-item>`
|
||||
- Added the `no-spin-buttons` attribute to `<sl-input type="number">` [#798](https://github.com/shoelace-style/shoelace/issues/798)
|
||||
- Added support for resetting forms using `<sl-button type="reset">` [#799](https://github.com/shoelace-style/shoelace/pull/799)
|
||||
- Fixed a bug where a duplicate clear button showed in Firefox [#791](https://github.com/shoelace-style/shoelace/issues/791)
|
||||
- Fixed a bug where setting `valueAsDate` or `valueAsNumber` too early on `<sl-input>` would throw an error [#796](https://github.com/shoelace-style/shoelace/issues/796)
|
||||
- Fixed a bug in `<sl-color-picker>` where empty values weren't properly supported [#797](https://github.com/shoelace-style/shoelace/issues/797)
|
||||
- Fixed a bug in `<sl-color-picker>` where values were logged to the console when using the keyboard
|
||||
- Fixed a bug in `<sl-input>` where password controls would try to autocorrect/autocomplete/autocapitalize when the password is visible
|
||||
- Fixed label alignment in `<sl-checkbox>` and `<sl-radio>` so they align to the top of the control instead of the center when wrapping
|
||||
- Fixed labels in `<sl-checkbox>` and `<sl-radio>` so they use the `--sl-input-label-color` design token
|
||||
- Improved performance of the tabbable utility which can prevent the browser from temporarily locking up in focus traps [#800](https://github.com/shoelace-style/shoelace/pull/800)
|
||||
- Updated the `fieldset` attribute so it reflects in `<sl-radio-group>`
|
||||
|
||||
## 2.0.0-beta.76
|
||||
|
||||
- Added support for RTL animations in the Animation Registry
|
||||
- Fixed a bug where the bottom border of `<sl-select>` could be cut off when the dropdown scrolls
|
||||
- Fixed a bug in `<sl-select>` that could result in the browser locking up due to an infinite positioning loop [#777](https://github.com/shoelace-style/shoelace/issues/777)
|
||||
- Improved RTL animations for `<sl-drawer>` [#784](https://github.com/shoelace-style/shoelace/issues/784)
|
||||
- Improved RTL styles for `<sl-button-group>` [#783](https://github.com/shoelace-style/shoelace/issues/783)
|
||||
- Improved RTL styles for the toast stack [#785](https://github.com/shoelace-style/shoelace/issues/785)
|
||||
- Improved typings for translations and localized terms
|
||||
- Upgraded @shoelace-style/localize to 3.0
|
||||
|
||||
## 2.0.0-beta.75
|
||||
|
||||
- Added Persian translation [#774](https://github.com/shoelace-style/shoelace/pull/774)
|
||||
- Added `color-scheme` to light and dark themes to improve rendering of browser-provided UI [#776](https://github.com/shoelace-style/shoelace/issues/776)
|
||||
- Added `--track-width` custom property to `<sl-tab-group>`
|
||||
- Fixed focus rings for `<sl-input>`, `<sl-select>`, and `<sl-textarea>` in Safari since they don't use `:focus-visible` [#767](https://github.com/shoelace-style/shoelace/issues/767)
|
||||
- Fixed a bug where calling `HTMLFormElement.reportValidity()` would skip Shoelace form controls [#772](https://github.com/shoelace-style/shoelace/issues/772)
|
||||
- Fixed a bug that prevented `<sl-tooltip>` from closing when disabled [#775](https://github.com/shoelace-style/shoelace/issues/775)
|
||||
- Fixed a bug that allowed `<sl-icon-button>` to emit a `click` event when disabled [#781](https://github.com/shoelace-style/shoelace/issues/781)
|
||||
- Improved the default icon for `<sl-image-comparer>` so it's more intuitive and removed `grip-vertical` from system icon library
|
||||
- Improved RTL styles for many components [#768](https://github.com/shoelace-style/shoelace/pull/768)
|
||||
- Improved base path logic to execute only when `getBasePath()` is first called to better support SSR [#778](https://github.com/shoelace-style/shoelace/issues/778)
|
||||
- Improved `DOMParser` instantiation in `<sl-icon>` to better support SSR [#778](https://github.com/shoelace-style/shoelace/issues/778)
|
||||
- Reverted menu item caching due to regression [#766](https://github.com/shoelace-style/shoelace/issues/766)
|
||||
- Updated Floating UI to 0.5.2
|
||||
|
||||
## 2.0.0-beta.74
|
||||
|
||||
- 🚨 BREAKING: reworked focus rings to use outlines instead of box shadows
|
||||
- Removed the `--sl-focus-ring-alpha` design token
|
||||
- Refactored `--sl-focus-ring` to be an `outline` property instead of a `box-shadow` property
|
||||
- Added `--sl-focus-ring-color`, `--sl-focus-ring-style`, and `--sl-focus-ring-offset`
|
||||
- 🚨 BREAKING: removed `variant` from `<sl-radio-button>`
|
||||
- Added `sl-label-change` event to `<sl-menu-item>`
|
||||
- Added `blur()`, `click()`, and `focus()` methods as well as `sl-blur` and `sl-focus` events to `<sl-icon-button>` [#730](https://github.com/shoelace-style/shoelace/issues/730)
|
||||
- Added Tabler Icons example to icons page
|
||||
- Fixed a bug where updating a menu item's label wouldn't update the display label in `<sl-select>` [#729](https://github.com/shoelace-style/shoelace/issues/729)
|
||||
- Fixed a bug where the FormData event polyfill was causing issues with older browsers [#747](https://github.com/shoelace-style/shoelace/issues/747)
|
||||
- Fixed a bug that caused a console error when setting `value` to `null` or `undefined` in `<sl-input>`, `<sl-select>`, and `<sl-textarea>` [#751](https://github.com/shoelace-style/shoelace/pull/751)
|
||||
- Fixed a bug that caused `<sl-checkbox>` and `<sl-radio>` controls without a `value` to submit as `null` instead of `on` like native inputs [#744](https://github.com/shoelace-style/shoelace/issues/744)
|
||||
- Fixed a bug that caused `<sl-dropdown>` and dependent components to add unexpected padding around the panel [#743](https://github.com/shoelace-style/shoelace/issues/743)
|
||||
- Fixed a bug that prevented `valueAsDate` and `valueAsNumber` from updating synchronously [#760](https://github.com/shoelace-style/shoelace/issues/760)
|
||||
- Fixed a bug that caused `<sl-menu-item>` to load icons from the default library instead of the system library [#765](https://github.com/shoelace-style/shoelace/issues/765)
|
||||
- Fixed a bug in `<sl-input>` that prevented a canceled `keydown` event from submitting the containing form when pressing enter [#764](https://github.com/shoelace-style/shoelace/issues/764)
|
||||
- Improved behavior of clearable and password toggle buttons in `<sl-input>` and `<sl-select>` [#745](https://github.com/shoelace-style/shoelace/issues/745)
|
||||
- Improved performance of `<sl-select>` by caching menu items instead of traversing for them each time
|
||||
- Improved drag utility so initial click/touch events can be accepted [#758](https://github.com/shoelace-style/shoelace/issues/758)
|
||||
- Improved `<sl-color-picker>` to use an HSB grid instead of HSL to be more consistent with existing color picker implementations [#762](https://github.com/shoelace-style/shoelace/issues/762)
|
||||
- Improved `<sl-color-picker>` so the cursor is hidden and the preview is larger when dragging the grid
|
||||
- Refactored `<sl-menu>` to be more performant by caching menu items on slot change
|
||||
- Reverted form submit logic [#718](https://github.com/shoelace-style/shoelace/issues/718)
|
||||
- Updated the `disabled` attribute so it reflects in `<sl-dropdown>` [#741](https://github.com/shoelace-style/shoelace/discussions/741)
|
||||
- Updated the `name` and `icon` attribute so they reflect in `<sl-icon>` [#742](https://github.com/shoelace-style/shoelace/pull/742)
|
||||
- Updated Lit to 2.2.5
|
||||
- Updated Bootstrap Icons to 1.8.3
|
||||
- Updated TypeScript to 4.7.2
|
||||
- Updated esbuild to 0.14.40
|
||||
- Updated all other dependencies to latest versions
|
||||
|
||||
## 2.0.0-beta.73
|
||||
|
||||
- Added `button` part to `<sl-radio-button>`
|
||||
- Added custom validity examples and tests to `<sl-checkbox>`, `<sl-radio>`, and `<sl-radio-button>`
|
||||
- Added `enterkeyhint` attribute to `<sl-input>` and `<sl-textarea>`
|
||||
- Fixed a bug that prevented `setCustomValidity()` from working with `<sl-radio-button>`
|
||||
- Fixed a bug where the right border of a checked `<sl-radio-button>` was the wrong color
|
||||
- Fixed a bug that prevented a number of properties, methods, etc. from being documented in `<sl-radio>` and `<sl-radio-button>`
|
||||
- Fixed a bug in `<sl-avatar>` that prevented valid images from showing after an invalid or missing image was provided [#717](https://github.com/shoelace-style/shoelace/issues/717)
|
||||
- Fixed a bug that resulted in a console error being thrown on keydown in `<sl-dropdown>` [#719](https://github.com/shoelace-style/shoelace/issues/719)
|
||||
- Fixed a bug that prevented `<sl-dropdown>` from being closed when opened initially [#720](https://github.com/shoelace-style/shoelace/issues/720)
|
||||
- Fixed a bug that caused the test runner to fail when using a locale other than en-US [#726](https://github.com/shoelace-style/shoelace/issues/726)
|
||||
- Improved form submit logic so most user-added event listeners will run after form data is attached and validation occurs [#718](https://github.com/shoelace-style/shoelace/issues/718)
|
||||
- Improved accessibility of `<sl-tooltip>` so screen readers announce the content on hover/focus [#219](https://github.com/shoelace-style/shoelace/issues/219)
|
||||
- Improved accessibility of form controls by exposing clear buttons and password visibility buttons to screen readers while keeping them out of the tab order [#727](https://github.com/shoelace-style/shoelace/issues/727)
|
||||
- Updated `<sl-tab-group>` and `<sl-menu>` to cycle through tabs and menu items instead of stopping at the first/last when using the keyboard
|
||||
- Removed path aliasing (again) because it doesn't work with Web Test Runner's esbuild plugin
|
||||
|
||||
## 2.0.0-beta.72
|
||||
|
||||
- 🚨 BREAKING: refactored parts in `<sl-input>`, `<sl-range>`, `<sl-select>`, and `<sl-textarea>` to allow you to customize the label and help text position
|
||||
- Added `form-control-input` part
|
||||
- Renamed `label` to `form-control-label`
|
||||
- Renamed `help-text` to `form-control-help-text`
|
||||
- 🚨 BREAKING: removed status from the `sl-error` event payload in `<sl-icon>`
|
||||
- Added the experimental `<sl-radio-button>` component
|
||||
- Added `button-group` and `button-group__base` parts to `<sl-radio-group>`
|
||||
- Added the `label` attribute and slot to `<sl-color-picker>` to improve accessibility with screen readers
|
||||
- Fixed a bug that prevented form submission from working as expected in some cases
|
||||
- Fixed a bug that prevented `<sl-split-panel>` from toggling `vertical` properly [#703](https://github.com/shoelace-style/shoelace/issues/703)
|
||||
- Fixed a bug that prevented `<sl-color-picker>` from rendering a color initially [#704](https://github.com/shoelace-style/shoelace/issues/704)
|
||||
- Fixed a bug that caused focus trapping to fail when used inside a shadow root [#709](https://github.com/shoelace-style/shoelace/issues/709)
|
||||
- Improved accessibility throughout the docs
|
||||
- Improved accessibility of `<sl-dropdown>` so the trigger's expanded state is announced correctly
|
||||
- Improved accessibility of `<sl-format-date>` but rendering a `<time>` element instead of plain text
|
||||
- Improved accessibility of `<sl-select>` so disabled controls announce correct
|
||||
- Improved accessibility in `<sl-tag>` so remove buttons have labels
|
||||
- Refactored `<sl-radio>` to move selection logic into `<sl-radio-group>`
|
||||
- Updated slot detection logic so it ignores visually hidden elements
|
||||
- Upgraded the status of `<sl-visually-hidden>` from experimental to stable
|
||||
|
||||
## 2.0.0-beta.71
|
||||
|
||||
- 🚨 BREAKING: refactored exported parts to ensure composed components and their parts can be targeted via CSS
|
||||
- Refactored the `eye-dropper-button` part and added `eye-dropper-button__base`, `eye-dropper-button__prefix`, `eye-dropper-button__label`, `eye-dropper-button__suffix`, and `eye-dropper-button__caret` parts to `<sl-color-picker>`
|
||||
- Refactored the `format-button` part and added `format-button__base`, `format-button__prefix`, `format-button__label`, `format-button__suffix`, and `format-button__caret` parts to `<sl-color-picker>`
|
||||
- Moved the `close-button` part in `<sl-alert>` to the internal `<sl-icon-button>` and removed the `<span>` that wrapped it
|
||||
- Moved the `close-button` part in `<sl-dialog>` and `<sl-drawer>` to point to the host element and added the `close-button__base` part
|
||||
- Renamed parts in `<sl-select>` from `tag-base` to `tag__base`, `tag-content` to `tag__content`, and `tag-remove-button` to `tag__remove-button`
|
||||
- Moved the `close-button` part in `<sl-tab>` to the internal `<sl-icon-button>` and added the `close-button__base` part
|
||||
- Moved the `scroll-button` part in `<sl-tab-group>` to the internal `<sl-icon-button>` and added the `scroll-button__base`, `scroll-button--start`, and `scroll-button--end` parts
|
||||
- Moved the `remove-button` part in `<sl-tag>` to the internal `<sl-icon-button>` and added the `remove-button__base` part
|
||||
- 🚨 BREAKING: removed `checked-icon` part from `<sl-menu-item>` in preparation for parts refactor
|
||||
- 🚨 BREAKING: changed the `typeToSelect()` method's argument from `String` to `KeyboardEvent` in `<sl-menu>` to support more advanced key combinations
|
||||
- Added `form`, `formaction`, `formmethod`, `formnovalidate`, and `formtarget` attributes to `<sl-button>` [#699](https://github.com/shoelace-style/shoelace/issues/699)
|
||||
- Added Prettier and ESLint to markdown files
|
||||
- Added background color and border to `<sl-menu>`
|
||||
- Added more tests for `<sl-input>`, `<sl-select>`, and `<sl-textarea>`
|
||||
- Fixed a bug that prevented forms from submitting when pressing <kbd>Enter</kbd> inside of an `<sl-input>` [#700](https://github.com/shoelace-style/shoelace/issues/700)
|
||||
- Fixed a bug in `<sl-input>` that prevented the `valueAsDate` and `valueAsNumber` properties from working when set before the component was initialized
|
||||
- Fixed a bug in `<sl-dropdown>` where pressing <kbd>Home</kbd> or <kbd>End</kbd> wouldn't select the first or last menu items, respectively
|
||||
- Improved `autofocus` behavior in Safari for `<sl-dialog>` and `<sl-drawer>` [#693](https://github.com/shoelace-style/shoelace/issues/693)
|
||||
- Improved type to select logic in `<sl-menu>` so it supports <kbd>Backspace</kbd> and gives users more time before resetting
|
||||
- Improved checkmark size and positioning in `<sl-menu-item>`
|
||||
- Improved accessibility in form controls that have help text so they're announced correctly in various screen readers
|
||||
- Removed feature detection for `focus({ preventScroll })` since it no longer works in Safari
|
||||
- Removed the `--sl-tooltip-arrow-start-end-offset` design token
|
||||
- Removed the `pattern` attribute from `<sl-textarea>` as it was documented incorrectly and never supported
|
||||
- Replaced Popper positioning dependency with Floating UI in `<sl-dropdown>` and `<sl-tooltip>`
|
||||
|
||||
## 2.0.0-beta.70
|
||||
|
||||
- Added `tag-base`, `tag-content`, and `tag-remove-button` parts to `<sl-select>` [#682](https://github.com/shoelace-style/shoelace/discussions/682)
|
||||
- Added support for focusing elements with `autofocus` when `<sl-dialog>` and `<sl-drawer>` open [#688](https://github.com/shoelace-style/shoelace/issues/688)
|
||||
- Added the `placement` attribute to `<sl-select>` [#687](https://github.com/shoelace-style/shoelace/pull/687)
|
||||
- Added Danish translation [#690](https://github.com/shoelace-style/shoelace/pull/690)
|
||||
- Fixed a bug that allowed `<sl-dropdown>` to go into an incorrect state when activating the trigger while disabled [#684](https://github.com/shoelace-style/shoelace/pull/684)
|
||||
- Fixed a bug where Safari would sometimes not focus after preventing `sl-initial-focus` [#688](https://github.com/shoelace-style/shoelace/issues/688)
|
||||
- Fixed a bug where the active tab indicator in `<sl-tab-group>` would be misaligned when using disabled tabs [#695](https://github.com/shoelace-style/shoelace/pull/695)
|
||||
- Improved the size of the remove button in `<sl-tag>`
|
||||
- Removed Google Analytics from the docs
|
||||
|
||||
## 2.0.0-beta.69
|
||||
|
||||
- Added `web-types.json` to improve the dev experience for WebStorm/PHPStorm users [#328](https://github.com/shoelace-style/shoelace/issues/328)
|
||||
- Fixed a bug that caused an error when pressing up/down in `<sl-select>`
|
||||
- Fixed a bug that caused `<sl-details>` to not show when double clicking the summary while open [#662](https://github.com/shoelace-style/shoelace/issues/662)
|
||||
- Fixed a bug that prevented the first/last menu item from receiving focus when pressing up/down in `<sl-dropdown>`
|
||||
- Fixed a bug that caused the active tab indicator in `<sl-tab-group>` to render incorrectly when used inside an element that animates [#671](https://github.com/shoelace-style/shoelace/pull/671)
|
||||
- Fixed a bug that allowed values in `<sl-range>` to be invalid according to its `min|max|step` [#674](https://github.com/shoelace-style/shoelace/issues/674)
|
||||
- Updated Lit to 2.1.4
|
||||
- Updated all other dependencies to latest versions
|
||||
|
||||
## 2.0.0-beta.68
|
||||
|
||||
- Fixed path aliases in generated files so they're relative again [#669](https://github.com/shoelace-style/shoelace/pull/669)
|
||||
|
||||
## 2.0.0-beta.67
|
||||
|
||||
- Fixed a TypeScript config regression introduced in [#647](https://github.com/shoelace-style/shoelace/pull/647) that removed the `rootDir`, breaking the expected build output
|
||||
|
||||
## 2.0.0-beta.66
|
||||
|
||||
- Attempted to fix a bug that prevented types from being generated in the build
|
||||
|
||||
## 2.0.0-beta.65
|
||||
|
||||
- 🚨 BREAKING: the `unit` property of `<sl-format-bytes>` has changed to `byte | bit` instead of `bytes | bits`
|
||||
- Added `display-label` part to `<sl-select>` [#650](https://github.com/shoelace-style/shoelace/issues/650)
|
||||
- Added `--spacing` CSS custom property to `<sl-divider>` [#664](https://github.com/shoelace-style/shoelace/pull/664)
|
||||
- Added `event.detail.source` to the `sl-request-close` event in `<sl-dialog>` and `<sl-drawer>`
|
||||
- Fixed a bug that caused `<sl-progress-ring>` to render the wrong size when `--track-width` was increased [#656](https://github.com/shoelace-style/shoelace/issues/656)
|
||||
- Fixed a bug that allowed `<sl-details>` to open and close when disabled using a screen reader [#658](https://github.com/shoelace-style/shoelace/issues/658)
|
||||
- Fixed a bug in the FormData event polyfill that threw an error in some environments [#666](https://github.com/shoelace-style/shoelace/issues/666)
|
||||
- Implemented stricter linting to improve consistency and reduce errors, which resulted in many small refactors throughout the codebase [#647](https://github.com/shoelace-style/shoelace/pull/647)
|
||||
- Improved accessibility of `<sl-dialog>` and `<sl-drawer>` by making the title an `<h2>` and adding a label to the close button
|
||||
- Improved search results in the documentation
|
||||
- Refactored `<sl-format-byte>` to use `Intl.NumberFormat` so it supports localization
|
||||
- Refactored themes so utility styles are no longer injected as `<style>` elements to support stricter CSP rules [#571](https://github.com/shoelace-style/shoelace/issues/571)
|
||||
- Restored the nicer animation on `<sl-spinner>` and verified it works in Safari
|
||||
- Updated Feather icon example to use Lucide [#657](https://github.com/shoelace-style/shoelace/issues/657)
|
||||
- Updated minimum Node version to 14.17
|
||||
- Updated Lit to 2.1.2
|
||||
- Updated to Bootstrap Icons to 1.8.1
|
||||
- Updated all other dependencies to latest versions
|
||||
|
||||
## 2.0.0-beta.64
|
||||
|
||||
- 🚨 BREAKING: removed `<sl-form>` because all form components submit with `<form>` now ([learn more](/getting-started/form-controls))
|
||||
- 🚨 BREAKING: changed `submit` attribute to `type="submit"` on `<sl-button>`
|
||||
- 🚨 BREAKING: changed the `alt` attribute to `label` in `<sl-avatar>` for consistency with other components
|
||||
- Added `role="status"` to `<sl-spinner>`
|
||||
- Added `valueAsDate` and `valueAsNumber` properties to `<sl-input>` [#570](https://github.com/shoelace-style/shoelace/issues/570)
|
||||
- Added `start`, `end`, and `panel` parts to `<sl-split-panel>` [#639](https://github.com/shoelace-style/shoelace/issues/639)
|
||||
- Fixed broken spinner animation in Safari [#633](https://github.com/shoelace-style/shoelace/issues/633)
|
||||
- Fixed an a11y bug in `<sl-tooltip>` where `aria-describedby` referenced an id in the shadow root
|
||||
- Fixed a bug in `<sl-radio>` where tabbing didn't work properly in Firefox [#596](https://github.com/shoelace-style/shoelace/issues/596)
|
||||
- Fixed a bug in `<sl-input>` where clicking the left/right edge of the control wouldn't focus it
|
||||
- Fixed a bug in `<sl-input>` where autofill had strange styles [#644](https://github.com/shoelace-style/shoelace/pull/644)
|
||||
- Improved `<sl-spinner>` track color when used on various backgrounds
|
||||
- Improved a11y in `<sl-radio>` so VoiceOver announces radios properly in a radio group
|
||||
- Improved the API for the experimental `<sl-split-panel>` component by making `position` accept a percentage and adding the `position-in-pixels` attribute
|
||||
- Refactored `<sl-breadcrumb-item>`, `<sl-button>`, `<sl-card>`, `<sl-dialog>`, `<sl-drawer>`, `<sl-input>`, `<sl-range>`, `<sl-select>`, and `<sl-textarea>` to use a Reactive Controller for slot detection
|
||||
- Refactored internal id usage in `<sl-details>`, `<sl-dialog>`, `<sl-drawer>`, and `<sl-dropdown>`
|
||||
- Removed `position: relative` from the common component stylesheet
|
||||
- Updated Lit to 2.1.0
|
||||
- Updated all other dependencies to latest versions
|
||||
|
||||
## 2.0.0-beta.63
|
||||
|
||||
- 🚨 BREAKING: changed the `type` attribute to `variant` in `<sl-alert>`, `<sl-badge>`, `<sl-button>`, and `<sl-tag>` since it's more appropriate and to disambiguate from other `type` attributes
|
||||
- 🚨 BREAKING: removed `base` part from `<sl-divider>` to simplify the styling API
|
||||
- Added the experimental `<sl-split-panel>` component
|
||||
- Added `focus()` and `blur()` methods to `<sl-select>` [#625](https://github.com/shoelace-style/shoelace/pull/625)
|
||||
- Fixed a bug where setting `tooltipFormatter` on `<sl-range>` in JSX causes React@experimental to error out
|
||||
- Fixed a bug where clicking on a slotted icon in `<sl-button>` wouldn't submit forms [#626](https://github.com/shoelace-style/shoelace/issues/626)
|
||||
- Added the `sl-` prefix to generated ids for `<sl-tab>` and `<sl-tab-panel>`
|
||||
- Refactored `<sl-button>` to use Lit's static expressions to reduce code
|
||||
- Simplified `<sl-spinner>` animation
|
||||
|
||||
## 2.0.0-beta.62
|
||||
|
||||
- 🚨 BREAKING: changed the `locale` attribute to `lang` in `<sl-format-bytes>`, `<sl-format-date>`, `<sl-format-number>`, and `<sl-relative-time>` to be consistent with how localization is handled
|
||||
- Added localization support including translations for English, German, German (Switzerland), Spanish, French, Hebrew, Japanese, Dutch, Polish, Portuguese, and Russian translations [#419](https://github.com/shoelace-style/shoelace/issues/419)
|
||||
- CodePen examples will now open in light or dark depending on your current preference
|
||||
- Fixed a bug where tag names weren't being generated in `vscode.html-custom-data.json` [#593](https://github.com/shoelace-style/shoelace/pull/593)
|
||||
- Fixed a bug in `<sl-tooltip>` where the tooltip wouldn't reposition when content changed
|
||||
- Fixed a bug in `<sl-select>` where focusing on a filled control showed the wrong focus ring
|
||||
- Fixed a bug where setting `value` initially on `<sl-color-picker>` didn't work in React [#602](https://github.com/shoelace-style/shoelace/issues/602)
|
||||
- Updated filled inputs to have the same appearance when focused
|
||||
- Updated `color` dependency from 3.1.3 to 4.0.2
|
||||
- Updated `<sl-format-bytes>`, `<sl-format-date>`, `<sl-format-number>`, and `<sl-relative-time>` to work like other localized components
|
||||
- Upgraded the status of `<sl-qr-code>` from experimental to stable
|
||||
- Updated to Bootstrap Icons to 1.7.2
|
||||
- Upgraded color to 4.1.0
|
||||
|
||||
## 2.0.0-beta.61
|
||||
|
||||
This release improves the dark theme by shifting luminance in both directions, slightly condensing the spectrum. This results in richer colors in dark mode. It also reduces theme stylesheet sizes by eliminating superfluous gray palette variations.
|
||||
|
||||
In [beta.48](#_200-beta48), I introduced a change to color tokens that allowed you to access alpha values at the expense of a verbose, non-standard syntax. After considering feedback from the community, I've decided to revert this change so the `rgb()` function is no longer required. Many users reported never using it for alpha, and even more reported having trouble remembering to use `rgb()` and that it was causing more harm than good.
|
||||
|
||||
Furthermore, both Safari and Firefox have implemented [`color-mix()`](<https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix()>) behind a flag, so access to alpha channels and other capabilities are coming to the browser soon.
|
||||
|
||||
If you're using color tokens in your own stylesheet, simply remove the `rgb()` to update to this version.
|
||||
|
||||
```css
|
||||
.your-styles {
|
||||
/* change this */
|
||||
color: rgb(var(--sl-color-primary-500));
|
||||
|
||||
/* to this */
|
||||
color: var(--sl-color-primary-500);
|
||||
}
|
||||
```
|
||||
|
||||
Thank you for your help and patience with testing Shoelace. I promise, we're not far from a stable release!
|
||||
|
||||
- 🚨 BREAKING: removed blue gray, cool gray, true gray, and warm gray color palettes
|
||||
- 🚨 BREAKING: removed `--sl-focus-ring-color`, and `--sl-focus-ring-alpha` (use `--sl-focus-ring` instead)
|
||||
- 🚨 BREAKING: removed `--sl-surface-base` and `--sl-surface-base-alt` tokens (use the neutral palette instead)
|
||||
- Added experimental `<sl-visually-hidden>` component
|
||||
- Added `clear-icon` slot to `<sl-select>` [#591](https://github.com/shoelace-style/shoelace/issues/591)
|
||||
- Fixed a bug in `<sl-progress-bar>` where the label would show in the default slot
|
||||
- Improved the dark theme palette so colors are bolder and don't appear washed out
|
||||
- Improved a11y of `<sl-avatar>` by representing it as an image with an `alt` [#579](https://github.com/shoelace-style/shoelace/issues/579)
|
||||
- Improved a11y of the scroll buttons in `<sl-tab-group>`
|
||||
- Improved a11y of the close button in `<sl-tab>`
|
||||
- Improved a11y of `<sl-tab-panel>` by removing an invalid `aria-selected` attribute [#579](https://github.com/shoelace-style/shoelace/issues/579)
|
||||
- Improved a11y of `<sl-icon>` by not using a variation of the `name` attribute for labels (use the `label` prop instead)
|
||||
- Moved `role` from the shadow root to the host element in `<sl-menu>`
|
||||
- Removed redundant `role="menu"` in `<sl-dropdown>`
|
||||
- Slightly faster animations for showing and hiding `<sl-dropdown>`
|
||||
- Updated to Bootstrap Icons to 1.7.1
|
||||
- Upgraded the status of `<sl-mutation-observer>` from experimental to stable
|
||||
|
||||
## 2.0.0-beta.60
|
||||
|
||||
- Added React examples and CodePen links to all components
|
||||
- Changed the `attr` in experimental `<sl-mutation-observer>` to require `"*"` instead of `""` to target all attributes
|
||||
- Fixed a bug in `<sl-progress-bar>` where the `label` attribute didn't set the label
|
||||
- Fixed a bug in `<sl-rating>` that caused disabled and readonly controls to transition on hover
|
||||
- The `panel` property of `<sl-tab>` is now reflected
|
||||
- The `name` property of `<sl-tab-panel>` is now reflected
|
||||
|
||||
## 2.0.0-beta.59
|
||||
|
||||
- Added React wrappers as first-class citizens
|
||||
- Added eye dropper to `<sl-color-picker>` when the browser supports the [EyeDropper API](https://wicg.github.io/eyedropper-api/)
|
||||
- Fixed a bug in `<sl-button-group>` where buttons groups with only one button would have an incorrect border radius
|
||||
- Improved the `<sl-color-picker>` trigger's border in dark mode
|
||||
- Refactored positioning logic in `<sl-dropdown>` so Popper is only active when the menu is open
|
||||
- Switched clearable icon from `x-circle` to `x-circle-fill` to make it easier to recognize
|
||||
- Updated to Bootstrap Icons to 1.7.0
|
||||
- Updated to Lit 2.0.2
|
||||
|
||||
## 2.0.0-beta.58
|
||||
@@ -58,11 +379,11 @@ Shoelace doesn't have a lot of dependencies, but this release unbundles most of
|
||||
## 2.0.0-beta.53
|
||||
|
||||
- 🚨 BREAKING: removed `<sl-menu-divider>` (use `<sl-divider>` instead)
|
||||
- 🚨 BREAKING: removed `percentage` attribute from `<sl-progress-bar>` and `<sl-progress-ring>` (use `value`) instead
|
||||
- 🚨 BREAKING: removed `percentage` attribute from `<sl-progress-bar>` and `<sl-progress-ring>` (use `value` instead)
|
||||
- 🚨 BREAKING: switched the default `type` of `<sl-tag>` from `primary` to `neutral`
|
||||
- Added the experimental `<sl-mutation-observer>` component
|
||||
- Added the `<sl-divider>` component
|
||||
- Added `--sl-surface-base` and `--sl-surface-base-alt` as early surface tokens to improve the appearance of alert, card, and panels in dark mode
|
||||
- Added `--sl-color-neutral-0` and `--sl-color-neutral-50` as early surface tokens to improve the appearance of alert, card, and panels in dark mode
|
||||
- Added the `--sl-panel-border-width` design token
|
||||
- Added missing background color to `<sl-details>`
|
||||
- Added the `--padding` custom property to `<sl-tab-panel>`
|
||||
@@ -138,14 +459,14 @@ This release also fixes a critical bug in the color scale where `--sl-color-neut
|
||||
|
||||
## 2.0.0-beta.48
|
||||
|
||||
This release improves theming by offering both light and dark themes that can be used autonomously. It also improves contrast in most components, adds a variety of new color primitives, and changes the way color tokens are consumed.
|
||||
This release improves theming by offering both light and dark themes that can be used autonomously. It also improves contrast in most components, adds a variety of new color primitives, and changes the way color tokens are consumed.
|
||||
|
||||
Previously, color tokens were in hexidecimal format. Now, Shoelace now uses an `R G B` format that requires you to use the `rgb()` function in your CSS.
|
||||
Previously, color tokens were in hexadecimal format. Now, Shoelace now uses an `R G B` format that requires you to use the `rgb()` function in your CSS.
|
||||
|
||||
```css
|
||||
.example {
|
||||
/* rgb() is required now */
|
||||
color: rgb(var(--sl-color-neutral-500));
|
||||
color: var(--sl-color-neutral-500);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -195,7 +516,7 @@ This release improves how component dependencies are imported. If you've been ch
|
||||
- Fixed a bug where tabbing into `<sl-radio-group>` would not always focus the checked radio
|
||||
- Fixed a bug in component styles that prevented the box sizing reset from being applied
|
||||
- Fixed a regression in `<sl-color-picker>` where dragging the grid handle wasn't smooth
|
||||
- Fixed a bug where slot detection could incorrecly match against slots of child elements [#481](https://github.com/shoelace-style/shoelace/pull/481)
|
||||
- Fixed a bug where slot detection could incorrectly match against slots of child elements [#481](https://github.com/shoelace-style/shoelace/pull/481)
|
||||
- Fixed a bug in `<sl-input>` where focus would move to the end of the input when typing in Safari [#480](https://github.com/shoelace-style/shoelace/issues/480)
|
||||
- Improved base path utility logic
|
||||
|
||||
@@ -232,7 +553,7 @@ The docs have been updated to use the new `custom-elements.json` file. If you're
|
||||
- Added `sl-request-close` event to `<sl-dialog>` and `<sl-drawer>`
|
||||
- Added `dialog.denyClose` and `drawer.denyClose` animations
|
||||
- Fixed a bug in `<sl-color-picker>` where setting `value` immediately wouldn't trigger an update
|
||||
- Fixed a bug in `<sl-dialog>` and `<sl-drawer>` where setting `open` intially didn't set a focus trap
|
||||
- Fixed a bug in `<sl-dialog>` and `<sl-drawer>` where setting `open` initially didn't set a focus trap
|
||||
- Fixed a bug that resulted in form controls having incorrect validity when `disabled` was initially set [#473](https://github.com/shoelace-style/shoelace/issues/473)
|
||||
- Fixed a bug in the docs that caused the metadata file to be requested twice
|
||||
- Fixed a bug where tabbing out of a modal would cause the browser to lag [#466](https://github.com/shoelace-style/shoelace/issues/466)
|
||||
@@ -256,7 +577,7 @@ The docs have been updated to use the new `custom-elements.json` file. If you're
|
||||
|
||||
- Added `?` to optional arguments in methods tables in the docs
|
||||
- Added the `scrollPosition()` method to `<sl-textarea>` to get/set scroll position
|
||||
- Added intial tests for `<sl-dialog>`, `<sl-drawer>`, `<sl-dropdown>`, and `<sl-tooltip>`
|
||||
- Added initial tests for `<sl-dialog>`, `<sl-drawer>`, `<sl-dropdown>`, and `<sl-tooltip>`
|
||||
- Fixed a bug in `<sl-tab-group>` where scrollable tab icons were not displaying correctly
|
||||
- Fixed a bug in `<sl-dialog>` and `<sl-drawer>` where preventing clicks on the overlay no longer worked as described [#452](https://github.com/shoelace-style/shoelace/issues/452)
|
||||
- Fixed a bug in `<sl-dialog>` and `<sl-drawer>` where setting initial focus no longer worked as described [#453](https://github.com/shoelace-style/shoelace/issues/453)
|
||||
@@ -275,7 +596,7 @@ Technical reasons aside, canceling these events seldom led to a good user experi
|
||||
- 🚨 BREAKING: `sl-show` and `sl-hide` events are no longer cancelable
|
||||
- Added Iconoir example to the icon docs
|
||||
- Added Web Test Runner
|
||||
- Added intial tests for `<sl-alert>` and `<sl-details>`
|
||||
- Added initial tests for `<sl-alert>` and `<sl-details>`
|
||||
- Changed the `cancelable` default to `false` for the internal `@event` decorator
|
||||
- Fixed a bug where toggling `open` stopped working in `<sl-alert>`, `<sl-dialog>`, `<sl-drawer>`, `<sl-dropdown>`, and `<sl-tooltip>`
|
||||
- Fixed a bug in `<sl-range>` where setting a value outside the default `min` or `max` would clamp the value [#448](https://github.com/shoelace-style/shoelace/issues/448)
|
||||
@@ -486,7 +807,7 @@ The component API remains the same except for the changes noted below. Thanks fo
|
||||
- 🚨 BREAKING: Fixed animations bloat
|
||||
- Removed ~400 baked-in Animista animations because they were causing ~200KB of bloat (they can still be used with custom keyframes)
|
||||
- Reworked animations into a separate module ([`@shoelace-style/animations`](https://github.com/shoelace-style/animations)) so it's more maintainable and animations are sync with the latest version of animate.css
|
||||
- Animation and easing names are now camelcase (e.g. `easeInOut` instead of `ease-in-out`)
|
||||
- Animation and easing names are now camelCase (e.g. `easeInOut` instead of `ease-in-out`)
|
||||
- Added initial E2E tests [#169](https://github.com/shoelace-style/shoelace/pull/169)
|
||||
- Added the `FocusOptions` argument to all components that have a `setFocus()` method
|
||||
- Added `sl-initial-focus` event to `<sl-dialog>` and `<sl-drawer>` so focus can be customized to a specific element
|
||||
@@ -557,7 +878,7 @@ The component API remains the same except for the changes noted below. Thanks fo
|
||||
- Fixed a bug where `<sl-menu-item>` wouldn't render properly in the dark theme
|
||||
- Fixed a bug where `<sl-select>` would show an autocomplete menu
|
||||
- Improved placeholder contrast in dark theme
|
||||
- Updated to Boostrap Icons 1.1.0
|
||||
- Updated to Bootstrap Icons 1.1.0
|
||||
- Updated to Stencil 2.3.0
|
||||
|
||||
## 2.0.0-beta.22
|
||||
@@ -595,7 +916,7 @@ The component API remains the same except for the changes noted below. Thanks fo
|
||||
- Refactored `<sl-icon>` request logic and removed unused cache map
|
||||
- Reworked show/hide logic in `<sl-alert>`, `<sl-dialog>`, and `<sl-drawer>` to not use reflow hacks and the `hidden` attribute
|
||||
- Reworked slot logic in `<sl-card>`, `<sl-dialog>`, and `<sl-drawer>`
|
||||
- Updated to Popper 2.5.3 to address a fixed position bug in Firefox
|
||||
- Updated to Popper 2.5.3 to address a fixed position bug in Firefox
|
||||
|
||||
## 2.0.0-beta.20
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ The [discussion forum](https://github.com/shoelace-style/shoelace/discussions) i
|
||||
- Show the community what you're working on
|
||||
- Learn more about the project, its values, and its roadmap
|
||||
|
||||
<sl-button type="primary" href="https://github.com/shoelace-style/shoelace/discussions" target="_blank">
|
||||
<sl-button variant="primary" href="https://github.com/shoelace-style/shoelace/discussions" target="_blank">
|
||||
<sl-icon name="github" slot="prefix"></sl-icon>
|
||||
Join the Discussion
|
||||
</sl-button>
|
||||
@@ -27,16 +27,16 @@ The [community chat](https://discord.gg/mg8f26C) is open to the public and power
|
||||
- Show the community what you're working on
|
||||
- Chat live with other designers, developers, and Shoelace fans
|
||||
|
||||
<sl-button type="primary" href="https://discord.gg/mg8f26C" target="_blank">
|
||||
<sl-button variant="primary" href="https://discord.gg/mg8f26C" target="_blank">
|
||||
<sl-icon name="discord" slot="prefix"></sl-icon>
|
||||
Join the Chat
|
||||
</sl-button>
|
||||
|
||||
## Stack Overflow
|
||||
|
||||
You can post questions on Stack Overflow using [the "shoelace" tag](https://stackoverflow.com/questions/tagged/shoelace). This is a public forum where talented developers answer questions. It's a great way to get help, but it is not maintained or actively monitored by the Shoelace author.
|
||||
You can post questions on Stack Overflow using [the "shoelace" tag](https://stackoverflow.com/questions/tagged/shoelace). This is a public forum where talented developers answer questions. It's a great way to get help, but it is not maintained by the Shoelace author.
|
||||
|
||||
<sl-button type="primary" href="https://stackoverflow.com/questions/ask?tags=shoelace" target="_blank">
|
||||
<sl-button variant="primary" href="https://stackoverflow.com/questions/ask?tags=shoelace" target="_blank">
|
||||
<sl-icon name="stack-overflow" slot="prefix"></sl-icon>
|
||||
Ask for Help
|
||||
</sl-button>
|
||||
@@ -47,7 +47,7 @@ Follow [@shoelace_style](https://twitter.com/shoelace_style) on Twitter for gene
|
||||
|
||||
**Please avoid using Twitter for support questions.** The [discussion forum](https://github.com/shoelace-style/shoelace/discussions) is a much better place to share code snippets, screenshots, and other troubleshooting info. You'll have much better luck there, as more users will have a chance to help you.
|
||||
|
||||
<sl-button type="primary" href="https://twitter.com/shoelace_style" target="_blank">
|
||||
<sl-button variant="primary" href="https://twitter.com/shoelace_style" target="_blank">
|
||||
<sl-icon name="twitter" slot="prefix"></sl-icon>
|
||||
Follow on Twitter
|
||||
</sl-button>
|
||||
|
||||
@@ -19,6 +19,12 @@ A common misconception about contributing to an open source project is that you
|
||||
|
||||
Please take a moment to review these guidelines to make the contribution process as easy as possible for both yourself and the project's maintainers.
|
||||
|
||||
## AI-generated Code
|
||||
|
||||
As an open source maintainer, I respectfully ask that you refrain from using AI-generated code when contributing to this project. This includes code generated by tools such as GitHub Copilot, even if you make alterations to it afterwards. While some of Copilot's features are indeed convenient, the ethics surrounding which codebases the AI has been trained on and their corresponding software licenses remain very questionable and have yet to be tested in a legal context.
|
||||
|
||||
I realize that one cannot reasonably enforce this any more than one can enforce not copying licensed code from other codebases, nor do I wish to expend energy policing contributors. I would, however, like to avoid all ethical and legal challenges that result from using AI-generated code. As such, I respectfully ask that you refrain from using such tools when contributing to this project. At this time, I will not knowingly accept any code that has been generated in such a manner.
|
||||
|
||||
## Using the Issue Tracker
|
||||
|
||||
The [issue tracker](https://github.com/shoelace-style/shoelace/issues) is for bug reports, feature requests, and pull requests.
|
||||
@@ -85,11 +91,17 @@ npm install
|
||||
Once you've cloned the repo, run the following command to spin up the Shoelace dev server.
|
||||
|
||||
```bash
|
||||
npm start
|
||||
npm run start
|
||||
```
|
||||
|
||||
After the initial build, a browser will open automatically to a local version of the docs. The documentation is powered by Docsify, which uses raw markdown files to generate pages on the fly.
|
||||
|
||||
### Cloud-based Development
|
||||
|
||||
Alternatively, you can use [Gitpod](https://www.gitpod.io/) to setup a dev environment in the cloud using only your browser.
|
||||
|
||||
[](https://gitpod.io/#https://github.com/shoelace-style/shoelace)
|
||||
|
||||
### Creating New Components
|
||||
|
||||
To scaffold a new component, run the following command, replacing `sl-tag-name` with the desired tag name.
|
||||
@@ -104,7 +116,7 @@ This will generate a source file, a stylesheet, and a docs page for you. When yo
|
||||
|
||||
Component development occurs _within_ the local docs site. I've found that offering common variations _in the docs_ is more beneficial for users than segmenting demos and code examples into separate tools such as Storybook. This encourages more thorough documentation, streamlines development for maintainers, and simplifies how the project is built. It also reduces installation and startup times significantly.
|
||||
|
||||
There is currently no hot module reloading (HMR), as browsers don't provide a way to unregister custom elements. However, most changes to the source will reload the browser automatically.
|
||||
There is currently no hot module reloading (HMR), as browsers don't provide a way to unregister custom elements. However, most changes to the source will reload the browser automatically.
|
||||
|
||||
For more information about running and building the project locally, refer to `README.md` in the project's root.
|
||||
|
||||
@@ -113,7 +125,7 @@ For more information about running and building the project locally, refer to `R
|
||||
Shoelace uses [Web Test Runner](https://modern-web.dev/guides/test-runner/getting-started/) for testing. To launch the test runner during development, open a terminal and launch the dev server.
|
||||
|
||||
```bash
|
||||
npm start
|
||||
npm run start
|
||||
```
|
||||
|
||||
In a second terminal window, launch the test runner.
|
||||
@@ -124,13 +136,20 @@ npm run test:watch
|
||||
|
||||
Follow the on-screen instructions to work with the test runner. Tests will automatically re-run as you make changes.
|
||||
|
||||
To run tests only once, make sure to build the project first.
|
||||
To run all tests only once:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npm run test
|
||||
```
|
||||
|
||||
To run just one test file:
|
||||
|
||||
If the test file is `src/components/breadcrumb-item.test.ts`, then `<nameOfTestFile>` would be `breadcrumb-item`.
|
||||
|
||||
```bash
|
||||
npm run test -- --group <nameOfTestFile>
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
The following is a non-exhaustive list of conventions, patterns, and best practices we try to follow. As a contributor, we ask that you make a good faith effort to follow them as well. This ensures consistency and maintainability throughout the project.
|
||||
@@ -171,7 +190,7 @@ Components should be composable, meaning you can easily reuse them with and with
|
||||
|
||||
The `<sl-select>` component, for example, makes use of the dropdown, input, menu, and menu item components. Because it's offloading most of its functionality and styles to lower-level components, the select component remains lightweight and its appearance is consistent with other form controls and menus.
|
||||
|
||||
### Component Stucture
|
||||
### Component Structure
|
||||
|
||||
All components have a host element, which is a reference to the `<sl-*>` element itself. Make sure to always set the host element's `display` property to the appropriate value depending on your needs, as the default is `inline` per the custom element spec.
|
||||
|
||||
@@ -191,7 +210,9 @@ Internally, each component uses the [BEM methodology](http://getbem.com/) for cl
|
||||
|
||||
### Boolean Props
|
||||
|
||||
Boolean props should _always_ default to `false`, otherwise there's no way for the user to unset the, without JavaScript. To keep the API as friendly and consistent as possible, use a name like `noHeader` with a default value of `false` instead of `header` with a default value of `true`.
|
||||
Boolean props should _always_ default to `false`, otherwise there's no way for the user to unset them using only attributes. To keep the API as friendly and consistent as possible, use a property such as `noHeader` and a corresponding kebab-case attribute such as `no-header`.
|
||||
|
||||
When naming boolean props that hide or disable things, prefix them with `no-`, e.g. `no-spin-buttons` and avoid using other verbs such as `hide-` and `disable-` for consistency.
|
||||
|
||||
### Conditional Slots
|
||||
|
||||
@@ -215,8 +236,8 @@ To expose custom properties as part of a component's API, scope them to the `:ho
|
||||
|
||||
```css
|
||||
:host {
|
||||
--color: rgb(var(--sl-color-primary-500));
|
||||
--background-color: rgb(var(--sl-color-neutral-100));
|
||||
--color: var(--sl-color-primary-500);
|
||||
--background-color: var(--sl-color-neutral-100);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -225,7 +246,7 @@ Then use the following syntax for comments so they appear in the generated docs.
|
||||
```js
|
||||
/**
|
||||
* @cssproperty --color: The component's text color.
|
||||
* @cssproperty --background-color: The component's background color.
|
||||
* @cssproperty --background-color: The component's background color.
|
||||
*/
|
||||
@customElement('sl-example')
|
||||
export default class SlExample {
|
||||
@@ -249,31 +270,31 @@ Parts let you target a specific element inside the component's shadow DOM but, b
|
||||
|
||||
This convention can be relaxed when the developer experience is greatly improved by not following these suggestions.
|
||||
|
||||
### Design Token Color Values
|
||||
### Naming CSS Parts
|
||||
|
||||
All design tokens that implement a color value must do so in the following `R G B` format for consistency.
|
||||
While CSS parts can be named [virtually anything](https://www.abeautifulsite.net/posts/valid-names-for-css-parts/), within Shoelace they must use the kebab-case convention and lowercase letters. Modifiers must be delimited by `--` like in BEM. This is useful for allowing users to target parts with various states, such as `my-part--focus`.
|
||||
|
||||
```css
|
||||
:root {
|
||||
--sl-color-sky-500: 14 165 233;
|
||||
When composing elements, use `part` to export the host element and `exportparts` to export its parts.
|
||||
|
||||
```js
|
||||
render() {
|
||||
return html`
|
||||
<div part="base">
|
||||
<sl-icon part="icon" exportparts="base:icon__base" ...></sl-icon>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
This format requires the consumer to use the `rgb()` function in their stylesheets, but it also lets them control each token's alpha channel.
|
||||
|
||||
```css
|
||||
.example {
|
||||
color: rgb(var(--sl-color-sky-500));
|
||||
background-color: rgb(var(--sl-color-sky-500) / 5%); /* 5% opacity */
|
||||
}
|
||||
```
|
||||
This results in a consistent, easy to understand structure for parts. In this example, the `icon` part will target the host element and the `icon__base` part will target the icon's `base` part.
|
||||
|
||||
### Form Controls
|
||||
|
||||
Form controls should support validation through the following conventions:
|
||||
Form controls should support submission and validation through the following conventions:
|
||||
|
||||
- All form controls must have an `invalid` property that reflects their validity
|
||||
- All form controls must use `name`, `value`, and `disabled` properties in the same manner as `HTMLInputElement`
|
||||
- All form controls must have a `setCustomValidity()` method so the user can set a custom validation message
|
||||
- All form controls must have a `reportValidity()` method that report their validity during form submission
|
||||
- All form controls must have an `invalid` property that reflects their validity
|
||||
- All form controls should mirror their native validation attributes such as `required`, `pattern`, `minlength`, `maxlength`, etc. when possible
|
||||
- All form controls must be serialized by `<sl-form>`
|
||||
- All form controls must be tested to work with the standard `<form>` element
|
||||
|
||||