Compare commits

..

973 Commits

Author SHA1 Message Date
Cory LaViska
b268d7dd8e done! 2023-06-08 15:12:23 -04:00
Cory LaViska
90e56e2f07 don't swallow errors 2023-06-08 15:05:39 -04:00
Cory LaViska
45ddaa4d38 cleaner watching 2023-06-08 14:46:12 -04:00
Cory LaViska
0de54b163a smarter exit 2023-06-08 13:47:01 -04:00
Cory LaViska
620b86cddb exit after 2023-06-08 12:51:15 -04:00
Cory LaViska
32d0ac4147 remove extra heading 2023-06-08 12:39:18 -04:00
Cory LaViska
a16733eb93 don't allow numbers at the start of an id 2023-06-08 12:39:12 -04:00
Cory LaViska
b67eef484c prettier 2023-06-08 12:23:31 -04:00
Konnor Rogers
5166964659 fix scroll spy 2023-06-08 12:15:02 -04:00
Cory LaViska
e72c2df6d2 fixes for turbo 2023-06-08 12:02:24 -04:00
Cory LaViska
cdafb3870c use delegation so resizing works with turbo 2023-06-08 11:26:25 -04:00
Cory LaViska
20abf21791 move scripts to <head> and defer for turbo 2023-06-08 11:12:31 -04:00
Konnor Rogers
482d155af4 add turbo 2023-06-08 00:15:07 -04:00
Konnor Rogers
ece23c727e add turbo 2023-06-07 17:23:57 -04:00
Cory LaViska
b7726cd514 cleanup 2023-06-07 16:56:46 -04:00
Cory LaViska
c31fe1ed35 update template 2023-06-07 16:53:48 -04:00
Cory LaViska
9e034810d9 reorder 2023-06-07 16:50:30 -04:00
Cory LaViska
217aabe55a update plop 2023-06-07 16:49:27 -04:00
Cory LaViska
1dea2f384e reorder 2023-06-07 16:47:28 -04:00
Cory LaViska
c24edec6b9 sync changelog 2023-06-07 16:43:03 -04:00
Cory LaViska
add09ca5b8 cleanup 2023-06-07 16:38:13 -04:00
Cory LaViska
57bd3632e8 fix spinner in prod build 2023-06-07 16:16:00 -04:00
Cory LaViska
6f44b6ffa6 remove await 2023-06-07 16:14:32 -04:00
Cory LaViska
ca8ba2d16b don't swallow errors 2023-06-07 16:14:25 -04:00
Cory LaViska
c37e4ba6b5 don't hide cursor 2023-06-07 15:19:10 -04:00
Cory LaViska
82cc778a0f fix urls when build on win 2023-06-07 14:03:04 -04:00
Cory LaViska
5085ef831b add shell for win 2023-06-07 13:49:10 -04:00
Cory LaViska
4721bd117b kill child processes without errors 2023-06-07 14:12:34 -04:00
Cory LaViska
57b13d848a nicer build (abort still erroring) 2023-06-07 13:28:22 -04:00
Cory LaViska
e01a43f01e rename vars 2023-06-07 07:54:05 -04:00
Cory LaViska
ec92351acf serve pages like page/index.html 2023-06-07 07:46:41 -04:00
Cory LaViska
8c44eb75d5 improvements 2023-06-06 17:02:15 -04:00
Konnor Rogers
aa48566aef make live reload actually work. 2023-06-06 15:46:50 -04:00
Cory LaViska
b8b6175a64 works with a 24 second docs refresh 😕 2023-06-06 09:21:07 -04:00
Cory LaViska
de5ad1b1b9 new docs 2023-06-06 08:22:18 -04:00
Cory LaViska
0866785495 remove old docs 2023-06-06 08:20:35 -04:00
Cory LaViska
947b3a9ec4 support cjs 2023-06-06 08:18:44 -04:00
Cory LaViska
6db27ca51f update changelog 2023-06-01 08:57:03 -05:00
Yuki Nishijima
f966ba97d7 Update the tutorial for Rails (#1258)
* Update the tutorial for Rails

* Update docs/tutorials/integrating-with-rails.md

Co-authored-by: Konnor Rogers <konnor5456@gmail.com>

* Update docs/tutorials/integrating-with-rails.md

Co-authored-by: Konnor Rogers <konnor5456@gmail.com>

* Update docs/tutorials/integrating-with-rails.md

Co-authored-by: Konnor Rogers <konnor5456@gmail.com>

* Update docs/tutorials/integrating-with-rails.md

Co-authored-by: Konnor Rogers <konnor5456@gmail.com>

* Update docs/tutorials/integrating-with-rails.md

Co-authored-by: Konnor Rogers <konnor5456@gmail.com>

* Update docs/tutorials/integrating-with-rails.md

* Add a bit more explanation to set up icons

---------

Co-authored-by: Konnor Rogers <konnor5456@gmail.com>
2023-06-01 08:53:33 -05:00
Cory LaViska
1af711bc89 update changelog 2023-05-25 12:26:56 -04:00
dhellgartner
c71da4a075 Split panel tests (#1343)
* Added tests for sl-split-panel

test for horizontal arrangement

* Added tests for sl-split-panel

tests for vertical arrangement

---------

Co-authored-by: stefanie.hellgartner <stefanie.hellgartner@in-tech.com>
2023-05-25 12:25:48 -04:00
Cory LaViska
d609fa87b4 reflect size; fixes #1348 2023-05-25 10:47:44 -04:00
Cory LaViska
dd16b0f65f update form control guidelines 2023-05-25 10:29:15 -04:00
ErikOnBike
6144e5eff4 Replace .map with .forEach when return value not used. See issue #740 (#1349) 2023-05-24 10:34:42 -04:00
Bernhard
c4db99f5a3 update angular getting started (#1352) 2023-05-24 10:34:05 -04:00
Cory LaViska
21431b0e56 Remove outdated line from docs 2023-05-22 09:41:12 -04:00
Cory LaViska
136e6bc915 update changelog 2023-05-17 16:54:16 -04:00
Stefan Bauer
05aeb78b01 Update de.ts (#1339)
"Gleiten" is a verb and means glide, "Slide" must be translated with "Folie" (like in previousSlide, nextSlide)
2023-05-17 16:51:34 -04:00
Yuki Nishijima
22010a1f9b Add better support for Turbo Drive (#1338) 2023-05-17 16:50:34 -04:00
Erick Almeida
e0fd6b210e Fix Portuguese translation strings (#1336) 2023-05-16 07:55:17 -04:00
Konnor Rogers
be1c38f0e5 tests: add regression tests for checkbox and toggle focus behavior (#1330)
* add regression test for checkbox focusing

* change number of checkboxes / switches

* change max-height to 400px so it fails

* re-add positon: relative;
2023-05-10 16:54:44 -04:00
Cory LaViska
b1bdedd3a3 add submenu-icon part 2023-05-04 10:17:41 -04:00
Cory LaViska
429b47963b update changelog; #1310 2023-05-02 10:23:20 -04:00
Cory LaViska
466c8a0883 Merge branch 'mpharoah-mpharoah/sl-rating-perf' into next 2023-05-02 10:22:30 -04:00
Cory LaViska
3a212030c0 Merge branch 'mpharoah/sl-rating-perf' of github.com:mpharoah/shoelace into mpharoah-mpharoah/sl-rating-perf 2023-05-02 10:02:13 -04:00
Cory LaViska
7f1ac48f5f update changelog 2023-05-02 10:01:28 -04:00
Cory LaViska
9c3e344ae0 reorder properties 2023-05-02 10:01:25 -04:00
Cory LaViska
db147e77b0 Merge branch 'KonnorRogers-patch-1' into next 2023-05-02 10:00:14 -04:00
Cory LaViska
0384b03528 add checkbox + exported parts; #1318 2023-05-01 13:43:42 -04:00
Konnor Rogers
fc06de7b11 feat: support variable height elements inside <sl-button> 2023-04-28 22:31:42 -04:00
Cory LaViska
5ada0a7093 update radio size when group size changes 2023-04-27 13:18:21 -04:00
Cory LaViska
f136b8eb12 update docs; fixes #1315 2023-04-27 13:17:54 -04:00
Matt Pharoah
56a160464f Fixed clipPath 2023-04-21 08:56:43 -04:00
Matt Pharoah
3dc9430932 Improve performance of sl-rating by not render all icons twice 2023-04-20 17:01:12 -04:00
Cory LaViska
afa69b4f9c 2.4.0 2023-04-14 12:56:26 -04:00
Cory LaViska
102f46d185 bump version 2023-04-14 12:54:55 -04:00
Cory LaViska
9c5e184d82 fixes #1302 2023-04-14 12:48:57 -04:00
Cory LaViska
385b5451c8 add size to radio group; fixes #1301 2023-04-14 12:20:17 -04:00
Cory LaViska
0e7487257b spell check and reorder static function 2023-04-13 14:16:03 -04:00
Matt Pharoah
eab0e3219f Improve performance of sl-icon by caching later (#1286)
* Improve performance of sl-icon by caching later

* Fixed error handling

* Don't use requestInclude in sl-icon

* Separate sl-icon errors into cacheable and retryable errors
2023-04-13 14:12:59 -04:00
Cory LaViska
cf89c901a2 no roles on slots; fixes #1287 2023-04-13 12:52:03 -04:00
Cory LaViska
902b08cc0f revert role and don't use <header> for buttons 2023-04-13 12:47:48 -04:00
Cory LaViska
caf9a09efa fix typos 2023-04-13 12:47:18 -04:00
dhellgartner
65734dc993 Slot aria attributes (#1296)
* Fix acessability issue

* Additionally adapted the test

* Added more accessability tests

* Updated the testing documentation

to take the fact that accessability checks cover only
rendered content into account

---------

Co-authored-by: Dominikus Hellgartner <dominikus.hellgartner@gmail.com>
2023-04-13 12:45:52 -04:00
Cory LaViska
0f02fffc3a less pipeline flakes 🤞🏻 2023-04-13 11:58:46 -04:00
Cory LaViska
931ecad8c5 update changelog 2023-04-13 11:56:33 -04:00
Alessandro
c137f83df6 fix(carousel): clickable elements don't work on chrome (#1266)
* fix(carousel): clickable elements don't work on chrome

* fix: update implementation
2023-04-13 11:55:40 -04:00
Cory LaViska
d3a0a38dce don't show hover when focused; fixes #1282 2023-04-13 10:31:24 -04:00
Cory LaViska
b76af1aa21 update changelog 2023-04-13 09:51:11 -04:00
Cory LaViska
5cf6a37ee2 wait until registered to set initial state; fixes #1292 2023-04-13 09:51:07 -04:00
Cory LaViska
63194abf93 prettier + changelog 2023-04-05 09:21:25 -05:00
Konnor Rogers
e196b0915a fix: split-panel divider now focusable in Edge / Chrome (#1289) 2023-04-05 09:18:57 -05:00
Cory LaViska
d2369d1de8 fix @since 2023-04-04 16:22:24 -05:00
dhellgartner
a9bbcc5556 first draft of testing guidelines (#1223)
Co-authored-by: Dominikus Hellgartner <dominikus.hellgartner@gmail.com>
2023-04-04 09:00:03 -05:00
Cory LaViska
8d9430e7a2 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2023-04-03 16:25:21 -05:00
Cory LaViska
0411754949 update changelog 2023-04-03 16:25:18 -05:00
Christophe Eymard
91ffaa1a2d stop holding a reference to a Promise when it is resolved (#1284) 2023-04-03 16:23:59 -05:00
Cory LaViska
ae9972a91a clarify events; #1283 2023-04-03 16:19:35 -05:00
Cory LaViska
478fa6f2bb update changelog 2023-03-31 15:03:31 -04:00
Cory LaViska
6a52a04591 Merge branch 'sloth30799-sloth30799' into next 2023-03-31 14:39:50 -04:00
Cory LaViska
a8f87e0d5e Merge branch 'sloth30799' of github.com:sloth30799/shoelace into sloth30799-sloth30799 2023-03-31 14:37:54 -04:00
Cory LaViska
cbc96fdf5c update changelog 2023-03-31 11:59:49 -04:00
dhellgartner
b4d24dd9af Added a basic test for animation (#1274)
Did not manage to check
that the properties are correctly passed
to the animation api at this point so this
stays a blackbox test

Co-authored-by: Dominikus Hellgartner <dominikus.hellgartner@gmail.com>
2023-03-31 11:59:07 -04:00
Cory LaViska
4b66cc2acb update changelog 2023-03-31 11:53:43 -04:00
Thomas Blum
3766d5ce27 Fixed wrong property value (#1272) 2023-03-31 11:52:03 -04:00
Cory LaViska
4b7d686754 prettier + highlighter 2023-03-29 16:49:59 -04:00
gennitdev
b948a07a4d Include slot example for Vue (#1271) 2023-03-29 16:48:39 -04:00
Cory LaViska
6d3505aefa fix property name 2023-03-28 11:31:42 -04:00
Cory LaViska
b22650ff51 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2023-03-27 09:31:46 -04:00
Cory LaViska
23a7f65b49 fix variable name case 2023-03-27 09:31:43 -04:00
Han Ye Htun
f4fba8eab4 added tests 2023-03-24 00:05:10 +06:30
Christophe Eymard
1734bf54a7 replaced Set with WeakSet (#1249) 2023-03-23 12:13:13 -04:00
Cory LaViska
88efec7815 prettier 2023-03-23 08:39:20 -04:00
Marko Hrovatic
e335189bb8 Update angular.md (#1264)
Added an example how to access Shoelace components from component code
2023-03-22 16:39:20 -04:00
Han Ye Htun
d03ca4ab95 Avatar Initials visible when image has a transparent background(#1256) 2023-03-22 21:46:23 +06:30
Cory LaViska
257407758f remove unnecessary dep 2023-03-21 16:25:52 -04:00
Han Ye Htun
2443c046aa Avatar Initials visible when image has a transparent background(#1256) 2023-03-22 00:50:01 +06:30
Cory LaViska
d710eb3947 update changelog 2023-03-20 14:00:42 -04:00
dhellgartner
7b2f6f230d Added tests to the sl-animated-images component (#1246)
Co-authored-by: Dominikus Hellgartner <dominikus.hellgartner@gmail.com>
2023-03-20 13:59:54 -04:00
Cory LaViska
07cb6070cc Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2023-03-20 11:25:56 -04:00
Cory LaViska
bd7dc2a7be #1244 2023-03-20 11:25:55 -04:00
Alessandro
db931c12be fix: prevent expand button to shrink (#1245) 2023-03-20 11:24:58 -04:00
Cory LaViska
765b311a08 update changelog 2023-03-14 12:00:53 -04:00
Cory LaViska
ce198d9c0b Merge branch 'alenaksu-fix/tree-item/clickable-label-elements' into next 2023-03-14 11:59:06 -04:00
Cory LaViska
8f5893931b Merge branch 'fix/tree-item/clickable-label-elements' of github.com:alenaksu/shoelace into alenaksu-fix/tree-item/clickable-label-elements 2023-03-14 11:57:36 -04:00
Stefan Bauer
221be48589 fixed big bug typo ;-) (#1242) 2023-03-14 11:56:55 -04:00
Cory LaViska
234ff2619d fixes #1243 2023-03-14 11:16:39 -04:00
Alessandro
b37be46ba3 fix(tree-item): move label outside of checkbox 2023-03-13 19:52:12 +00:00
Cory LaViska
6e2ea508db update changelog 2023-03-13 11:48:35 -04:00
Jared White
0e6e2abd28 Export autoload discover function and support shadow roots (#1236)
* Export autoload discover function and support shadow roots

* run prettier
2023-03-13 11:47:37 -04:00
Cory LaViska
db1bdfbf65 update changelog 2023-03-13 10:58:01 -04:00
Alessandro
7bf0f647b3 fix(carousel): change the way slide position is computed (#1235) 2023-03-13 10:56:00 -04:00
Cory LaViska
df25f8617b 2.3.0 2023-03-09 16:19:23 -05:00
Cory LaViska
ad2099a27f update version 2023-03-09 16:19:13 -05:00
Martin Alix
708127f96d Update French for Slide # (#1231) 2023-03-09 16:10:06 -05:00
Cory LaViska
9deb51e95a update docs 2023-03-09 16:09:33 -05:00
Cory LaViska
67852ea657 update installation docs 2023-03-07 16:52:02 -05:00
Cory LaViska
7240f4f8f4 Merge branch 'next' into autoload 2023-03-07 14:05:26 -05:00
Cory LaViska
17ee89a5e8 rename variable for clarity 2023-03-07 13:23:02 -05:00
Cory LaViska
f2177dccaf closes #1226 2023-03-07 11:03:03 -05:00
Cory LaViska
6aaf17b81a fixes #1224 2023-03-06 17:11:39 -05:00
dhellgartner
d113d13792 Fixed the avatar tests to produce less logs (#1222)
The reason for the problems is that the error event does
not escape from the shadow dom.
Thus it cannot be awaited for in the test

Co-authored-by: Dominikus Hellgartner <dominikus.hellgartner@gmail.com>
2023-03-06 08:30:01 -05:00
Cory LaViska
ab9cb5f185 update changelog 2023-03-03 10:56:55 -05:00
Cory LaViska
76fd7aa28d trigger update immediately 2023-03-03 10:55:53 -05:00
Cory LaViska
8f17bf4e9d Improve Carousel Accessibility (#1218)
* fix demo

* improve accessibility, reorg, and polish up

* add support for up/down

* fix docs

* update docs
2023-03-03 10:53:17 -05:00
Cory LaViska
0f0f71af9b add custom-elements.json to exports 2023-03-03 10:36:30 -05:00
Cory LaViska
e624701022 fixes #1220 2023-03-03 10:16:15 -05:00
Cory LaViska
4cedfc3201 fix check 2023-03-02 11:43:09 -05:00
Cory LaViska
d88d9fc81a update example 2023-03-02 11:43:00 -05:00
Cory LaViska
051baa4ff5 remove warning 2023-03-02 11:27:47 -05:00
Cory LaViska
57c3d7009b fix example 2023-03-02 11:27:42 -05:00
Cory LaViska
a27fd4d2e9 Merge branch 'next' into autoload 2023-03-02 10:49:30 -05:00
Cory LaViska
79ac425e2b fix demo 2023-03-01 12:59:14 -05:00
Cory LaViska
857f318f9c fix overscroll (#1217) 2023-03-01 11:48:16 -05:00
Cory LaViska
c0966bf767 remove unused property 2023-03-01 11:35:25 -05:00
Cory LaViska
86cecc9e30 fix carousel pagination in iOS 2023-03-01 11:34:42 -05:00
Alessandro
ec036d8e61 fix(carousel): various fixes and improvements (#1216)
* fix(carousel): don't resume autoplay if interacting

* fix(carousel): wrap pagination items

* chore(carousel): add unit tests

* feat(carousel): more reactive pagination dots

* fix(carousel): trigger scrollend when user scroll exactly over a snap point
2023-03-01 11:05:29 -05:00
Cory LaViska
77b25f4581 add tag parts to <sl-select> 2023-03-01 10:58:24 -05:00
Cory LaViska
a8d59b3329 update changelog 2023-02-28 17:11:21 -05:00
Cory LaViska
5990fbd000 2.2.0 2023-02-28 17:05:11 -05:00
Cory LaViska
954d78dcd1 update version 2023-02-28 17:04:59 -05:00
Cory LaViska
3ea31389dd fixes #1082 2023-02-28 13:33:34 -05:00
Cory LaViska
d79799043a remove unused import 2023-02-28 13:07:18 -05:00
Cory LaViska
9f8ce58288 use clickOnElement 2023-02-28 13:07:07 -05:00
Cory LaViska
2371c5490f Merge branch 'next' into autoload 2023-02-28 12:45:23 -05:00
Cory LaViska
77abd42d66 fix popup positioning edge case; closes #1135 2023-02-28 12:30:05 -05:00
Cory LaViska
218e78e947 add getForm() method; closes #1180 2023-02-28 12:10:14 -05:00
Cory LaViska
8a1efac9b8 fixes #1201 2023-02-28 12:03:20 -05:00
Cory LaViska
f9ae8327f6 fix menu focus color 2023-02-28 09:46:49 -05:00
Cory LaViska
7f3076d195 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2023-02-27 16:40:55 -05:00
Cory LaViska
1fa79e64ae fix track color in dark mode 2023-02-27 16:40:53 -05:00
Bünyamin Eskiocak
dde1010465 Fixed clipped calendar toggle in Firefox (#1213)
* fixed clipped calendar toggle in firefox

* changelog

---------

Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2023-02-27 12:04:32 -05:00
Cory LaViska
3a3a7347bc link to event 2023-02-27 11:15:57 -05:00
Cory LaViska
77c9750206 fix sl-tab-show event when closing 2023-02-27 11:13:59 -05:00
Cory LaViska
3d2e618be8 fix term 2023-02-24 16:08:06 -05:00
Cory LaViska
79feaae7fc adjust attribution 2023-02-24 15:56:31 -05:00
Cory LaViska
b0f7dfb86b update comment 2023-02-24 15:55:02 -05:00
Cory LaViska
e1979b8f38 reorder custom properties 2023-02-24 15:54:57 -05:00
Cory LaViska
7e9ae32b9b add carousel terms 2023-02-24 15:48:39 -05:00
Cory LaViska
480a1df246 reorder 2023-02-24 15:24:39 -05:00
Cory LaViska
ff798adb49 update examples 2023-02-24 15:10:54 -05:00
Cory LaViska
70a64262e9 add svg part 2023-02-24 12:51:46 -05:00
Cory LaViska
5f65896150 update localize 2023-02-24 12:35:25 -05:00
Cory LaViska
c69db4919b wip; improve carousel docs 2023-02-23 16:50:18 -05:00
Cory LaViska
a526e8a956 cleanup 2023-02-23 15:00:27 -05:00
Cory LaViska
4970ba065e set a default aspect ratio 2023-02-23 15:00:02 -05:00
Cory LaViska
0292ed30c5 update example 2023-02-23 14:59:48 -05:00
Cory LaViska
b64b1c2536 fix test 2023-02-23 14:49:49 -05:00
Cory LaViska
8f9eb012ba sort imports 2023-02-23 14:42:58 -05:00
Cory LaViska
c8fd9f19d2 ignore lorem ipsum 2023-02-23 14:40:05 -05:00
Cory LaViska
603aa93322 update and fix typos 2023-02-23 14:39:05 -05:00
Cory LaViska
74203de094 sort imports 2023-02-23 14:23:38 -05:00
Cory LaViska
4fa4682a45 Merge branch 'alenaksu-feat/carousel' into next 2023-02-23 14:20:01 -05:00
Cory LaViska
34e0fb2fc1 Merge branch 'feat/carousel' of github.com:alenaksu/shoelace into alenaksu-feat/carousel 2023-02-23 14:13:19 -05:00
Cory LaViska
50972f2b38 update changelog and comment 2023-02-23 11:33:31 -05:00
Cory LaViska
652ce6c9f1 Merge branch 'mpharoah-mpharoah/typescript-events' into next 2023-02-23 11:26:47 -05:00
Cory LaViska
8412b150b2 Merge branch 'mpharoah/typescript-events' of github.com:mpharoah/shoelace into mpharoah-mpharoah/typescript-events 2023-02-23 11:24:19 -05:00
Justin Fagnani
22b8ef4edf Fix a few spelling issues (#1192) 2023-02-23 11:15:20 -05:00
Cory LaViska
0865dede6f fix heading 2023-02-23 11:11:19 -05:00
Cory LaViska
d638d811ad remove unused type 2023-02-23 11:04:51 -05:00
Cory LaViska
bc58472b7b fix skipped tests 2023-02-23 11:02:26 -05:00
Cory LaViska
226c856b1e update scroll controls when adding tabs; fixes #1208 2023-02-23 10:12:36 -05:00
Cory LaViska
a127b8722e fix autoload timing issues 2023-02-22 14:18:43 -05:00
Cory LaViska
9c573fb454 add autoloader to docs 2023-02-22 14:18:36 -05:00
Cory LaViska
a346d18930 add autoloader docs 2023-02-22 14:18:19 -05:00
Cory LaViska
a32488baeb add autoloader prototype 2023-02-22 14:18:04 -05:00
Cory LaViska
a4131caeda add subpath 2023-02-22 14:16:11 -05:00
Cory LaViska
6c62a4f4c0 use passive listeners 2023-02-22 12:54:33 -05:00
Cory LaViska
5b12de1edf fixes #1205 2023-02-21 11:54:39 -05:00
Cory LaViska
f79a670ca3 fix padding in Chrome; closes #1197 2023-02-17 09:56:52 -05:00
Cory LaViska
e1ec60af62 2.1.0 2023-02-16 16:40:34 -05:00
Cory LaViska
dcbcc4c050 bump version 2023-02-16 16:39:11 -05:00
Cory LaViska
0eb3375bb9 welcome back, null 2023-02-16 16:30:13 -05:00
Cory LaViska
c26a8810c8 add crude inline error example; closes #1191 2023-02-16 16:22:46 -05:00
Cory LaViska
872227e345 reformat and add comment 2023-02-16 15:19:24 -05:00
dhellgartner
f22c529eab Avoid null logs on resize observer errors (#1196)
* Resize observer sometimes throws errors which
are nothing to worry about, see also the corresponding
comment on tab-group.test.ts
* Unfortunately, the web testing library installs an
error event handler which takes precedence before the
event handlers installed in the tests
(see node_modules/@web/browser-logs/dist/logUncaughtErrors.js)
* the only possibility to avoid these null logs is to install
an error event handler at an even earlier place

Co-authored-by: Dominikus Hellgartner <dominikus.hellgartner@gmail.com>
2023-02-16 15:13:57 -05:00
dhellgartner
3430b33c3e Alert test (#1189)
* Improved tests for SlAlert

* added more test for coverage
* Grouped tests in multiple subgroups

* remove executing only one tests

* Fix the now executing tests

---------

Co-authored-by: Dominikus Hellgartner <dominikus.hellgartner@gmail.com>
2023-02-16 15:04:11 -05:00
Cory LaViska
1bc2a6ef76 expose rel; fixes #1200 2023-02-16 12:15:59 -05:00
Cory LaViska
5a94f5bf5b update changelog 2023-02-16 10:43:28 -05:00
Thomas Blum
4277377189 Underscore was missing (#1195) 2023-02-16 10:39:01 -05:00
Matt Pharoah
d818980dea Fixed copy-paste type in comment 2023-02-15 09:41:05 -05:00
Matt Pharoah
636f61006f Adjusted comment to be more clear 2023-02-15 09:39:43 -05:00
Matt Pharoah
d93e698baf Added comments to explain the Typescript metaprogramming 2023-02-15 09:37:48 -05:00
Matt Pharoah
f8d8291caa Added sl-invalid event type 2023-02-14 23:06:47 -05:00
Matt Pharoah
21bef1c2ea Merge branch 'next' into mpharoah/typescript-events 2023-02-14 22:59:20 -05:00
Cory LaViska
f0efb9253c remove inline validation examples 2023-02-14 16:38:10 -05:00
Cory LaViska
cfd28f2608 update changelog 2023-02-14 15:24:19 -05:00
Cory LaViska
a3844fe074 comments 2023-02-14 15:21:52 -05:00
Cory LaViska
65e90f12f4 rename event 2023-02-14 15:12:21 -05:00
Cory LaViska
4335289d6a make doc comments consistent for check/report validity 2023-02-14 15:02:52 -05:00
Cory LaViska
86cc721e03 update description and document sl-invalid event 2023-02-14 14:59:34 -05:00
xdev1
4a28825ea7 Added some missing form validation standard features (implemented for #1181) (#1167)
* #1163 - added read-only properties 'validity' and 'validationMessage' to all nine form controls

* #1163 - added base support for showing form validation messages below the form controls

* #1163 - animated validation errors in demo

* #1181 - Removed all previous changes that have been validation error specific

* Started with 'Inline validation' demo / fixed merge issues / etc.

* #1181 - continued work on missing form validation features

* #1181 - enhanced validation support for SlColorPicker / some cleanup

* #1181 - fixed CSS issues

* #1181 - fixed again CSS issues

* '1181 - added form validation features finally working

* #1181 - bug fixes

* #1181 - fixed open issues / added API doc comments

* #1181 - updated inline validation demos / removed some legacy code

* #1181 - finished invalid form validation example

* #1181 - added tests / several bugfixes

* #1181 - fixed typos etc.

* #1181 - tests

* #1181 - tests

* #1181 - tests
2023-02-14 14:50:06 -05:00
Cory LaViska
19cf823da5 Merge branch 'ceymard-popup-element' into next 2023-02-14 09:30:47 -05:00
Christophe Eymard
737b55d78d allow Element as the anchor - now with correct typings 2023-02-10 21:56:57 +01:00
Cory LaViska
8493131db5 Revert "let popup be anchored to Element and not HTMLElement (#1186)"
This reverts commit 0d86c2af37.
2023-02-10 12:42:19 -05:00
Christophe Eymard
0d86c2af37 let popup be anchored to Element and not HTMLElement (#1186)
It works with SVG as well, is there a need to be restrictive here ?
2023-02-10 12:38:47 -05:00
Matt Pharoah
d6a7820a52 Make emit return the actual event type instead of CustomEvent<any> 2023-02-09 15:56:54 -05:00
Matt Pharoah
39ca1208f5 Removed unused event handler from sl-dropdown 2023-02-09 13:09:31 -05:00
Matt Pharoah
610a06bcb9 Use PropertyKey instead of string where appropriate 2023-02-09 13:06:17 -05:00
Matt Pharoah
b8584c0581 Require detail to always be provided when calling emit with an event that requires it 2023-02-09 13:00:04 -05:00
Matt Pharoah
ab19afeb66 Resolved merge conflict 2023-02-08 18:24:17 -05:00
Matt Pharoah
41b5cb367f Use typed events in components and tests 2023-02-08 18:19:27 -05:00
Matt Pharoah
e65b09fdec Fixed Typescript error when CustomEvents with non-object details are registered 2023-02-08 17:55:28 -05:00
Matt Pharoah
15a4049a01 Require the options parameter to have a detail property for events with details 2023-02-08 17:42:14 -05:00
Matt Pharoah
ce708fbba8 Perform type checking of the event detail in the emit function if the event type matches a Shoelace event 2023-02-08 17:01:00 -05:00
Matt Pharoah
75bd7784fb Basic events have an empty details object, not a null 2023-02-08 14:12:15 -05:00
Matt Pharoah
6e092ccf7a Added event types for all events 2023-02-08 13:55:19 -05:00
Matt Pharoah
b7b73ea3a9 Added sl-request-close event 2023-02-08 13:30:56 -05:00
Matt Pharoah
9dab91e0d1 Added event for sl-error 2023-02-08 13:17:12 -05:00
Matt Pharoah
a3a802a37b Register events with global event map 2023-02-08 13:14:43 -05:00
Matt Pharoah
358ad7bb30 Fixed type in documentation (TreeItem[] -> SlTreeItem[]) 2023-02-07 17:44:56 -05:00
Matt Pharoah
0a555c53c7 Export typescript types for events with details 2023-02-07 17:20:01 -05:00
Cory LaViska
b260a4dc29 add focus/blur to color picker 2023-02-07 17:18:03 -05:00
Cory LaViska
1f1024f4ca change default; #1175 2023-02-07 15:57:50 -05:00
Cory LaViska
9e92d92684 whitespace 2023-02-07 15:56:43 -05:00
Cory LaViska
527bf79973 improve user interaction heuristics; closes #1175 2023-02-07 15:29:26 -05:00
Cory LaViska
b281c5bbc1 use Set instead of WeakMap 2023-02-07 13:56:02 -05:00
Cory LaViska
f03de8925b fix checkbox required label in Chrome
See https://bugs.chromium.org/p/chromium/issues/detail?id=1413733
2023-02-07 10:52:54 -05:00
Cory LaViska
776ab2c715 add getForm() method 2023-02-07 09:01:32 -05:00
Cory LaViska
df967b7e84 improve checked state in forced-colors mode; fixes #1114 2023-02-06 18:00:39 -05:00
Cory LaViska
a539058253 validate even with novalidate; fixes #1164 2023-02-06 17:18:01 -05:00
Cory LaViska
af70d88153 improve icon page; fixes #1122 2023-02-06 15:19:08 -05:00
Cory LaViska
8dcffe270f remove 16.x from actions 2023-02-06 12:30:22 -05:00
Cory LaViska
c958f2e50a move focus logic; #1177 2023-02-06 12:20:32 -05:00
Cory LaViska
cedcd65c72 fix dropdown keyboard controls; closes #1177 2023-02-06 12:18:33 -05:00
Cory LaViska
12f62075ad move escape close logic to document listener; #1177 2023-02-06 11:32:06 -05:00
Cory LaViska
b8695b70a9 don't emit click when disabled; fixes #1113 2023-02-06 10:48:38 -05:00
Cory LaViska
a4e371618a fix comment 2023-02-06 10:47:08 -05:00
Cory LaViska
039ab175c3 add comment 2023-02-06 10:46:37 -05:00
Cory LaViska
7549e50fe4 fix positioning of native inputs; closes #1169 2023-02-05 12:06:06 -05:00
Cory LaViska
3c2cda699e fixes #1179 2023-02-05 11:08:57 -05:00
Cory LaViska
8685ddd049 fix select template; fixes #1178 2023-02-05 10:53:51 -05:00
Cory LaViska
c47ad40802 fixes #1172 2023-02-05 10:49:10 -05:00
Cory LaViska
ef1f129b22 fix test 2023-02-03 14:44:56 -05:00
Cory LaViska
6bb508ef14 revert; #1166 2023-02-03 14:36:17 -05:00
Cory LaViska
3596c8144d add dist to custom-elements.json; fixes #1166 2023-02-03 14:27:17 -05:00
Cory LaViska
20903bb638 update test 2023-02-02 12:23:32 -05:00
Alan Chambers
f45fb6848f Adjust sl-dropdown up/down keypress menuItems array to match menu (#1170)
* dropdown filtered menu items

* updated filter, added test

- updated filter to match menu getAllItems private method
- added test

---------

Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2023-02-02 11:53:38 -05:00
Cory LaViska
400f9b76d5 ignore non-menu items; fixes #1165 2023-02-02 11:47:00 -05:00
dhellgartner
38a9e98d9b Tab group tests (#1128)
* remove duplicate test

* Add tests for sl-tab-group -- initial round of tests

* use individual fixtures for each test

* extract mocks + utility functions in external files

* remove unnecessary internals of intersection observer from the mock

* added first test on scroll buttons

* add scrolling tests

* remove resize observer mock

Resize observer is triggered but waiting for element
to be updated is not enough. You need to free the main thread
with the test for some time

* Also removed intersection observer mock

By waiting long enough for the things to happen automatically

* Fix problems with resize observer

These problems appeared after npm ci but (according
to the sources linked in the comments) unproblematic

* Handle merge request comments

* replace custom wait function with corresponding function
 from openwc/testing
* Extracted waitForScrollingToEnd and isElementVisibleFromScrolling into
dedicated files to be reused
* Improve queryByTestId --> make it usable for more complex values
* Add js docs

* run lint fix

* Added tests for selecting a tab by click

* added further tests for tab group selection

* use Promise<void> instead of Promise<any>

to avoid eslint errors

---------

Co-authored-by: Dominikus Hellgartner <dominikus.hellgartner@gmail.com>
2023-01-27 09:55:28 -05:00
Cory LaViska
e8fe783fb4 fixes #1157 2023-01-26 14:22:04 -05:00
Cory LaViska
223ef32b70 fix web-types.json 2023-01-26 09:47:33 -05:00
Cory LaViska
0793a219a2 fix play/pause buttons; fixes #1147 2023-01-25 16:52:30 -05:00
Cory LaViska
3bb92c095f add state 2023-01-24 11:46:37 -05:00
Cory LaViska
deec097267 2.0.0 2023-01-24 11:43:38 -05:00
Cory LaViska
873e280700 update changelog 2023-01-24 11:38:17 -05:00
Cory LaViska
5d047f7a93 remove beta disclaimer 2023-01-23 12:55:45 -05:00
Cory LaViska
f24ab23752 add more design tokens to docs 2023-01-23 12:55:36 -05:00
Cory LaViska
44ecc8ce56 fixes #1141 2023-01-23 11:36:45 -05:00
Cory LaViska
e758b1d9bb whitespace 2023-01-23 11:29:50 -05:00
Cory LaViska
5cdbaa873d fixes #1141 2023-01-23 11:29:12 -05:00
Cory LaViska
e9aca6cedb fix part names; closes #1142 2023-01-23 10:51:24 -05:00
Cory LaViska
7c3896ed42 fix select input color; closes #1143 2023-01-23 10:44:19 -05:00
Cory LaViska
93158e8e90 fixes #1141 2023-01-22 13:34:32 -05:00
Cory LaViska
5f9bbdfa06 fixes #1139 2023-01-20 11:38:04 -05:00
Cory LaViska
3a0f486e98 fix label colors in checkbox, radio, and switch 2023-01-19 14:34:43 -05:00
Cory LaViska
29c671c0f4 fixes #1138 2023-01-19 14:22:29 -05:00
Cory LaViska
88c4bef5e7 Revert "Add JSON format for design tokens (#1129)"
This reverts commit 4a3f2caf59.
2023-01-19 11:37:52 -05:00
Cory LaViska
6066bc468b update config 2023-01-18 09:29:39 -05:00
Cory LaViska
efd944d822 remove contain:strict 2023-01-18 09:07:37 -05:00
Jared White
4a3f2caf59 Add JSON format for design tokens (#1129)
* Initial example of a JSON schema and converter for design tokens

* Clean up script and relocate file

* Update token JSON format and finish build process
2023-01-17 13:22:02 -05:00
Cory LaViska
511182b41b add padding to offset scrollbar; fixes #1132 2023-01-17 11:56:16 -05:00
Cory LaViska
1088a51ed5 fixes 1134 2023-01-17 10:45:19 -05:00
Cory LaViska
e3e0842bdd reorder importss 2023-01-17 10:44:07 -05:00
Cory LaViska
e4c908b08b add missing docs 2023-01-17 10:33:21 -05:00
Cory LaViska
f86578a213 fix tab panel display 2023-01-13 15:57:02 -05:00
Cory LaViska
3c2f5ec48e sort this, eslint 2023-01-13 15:43:55 -05:00
Cory LaViska
fec7ef17aa prettier 2023-01-13 14:42:21 -05:00
Cory LaViska
29ff99dd76 sigh 2023-01-13 14:37:45 -05:00
Cory LaViska
6b9b410bdc *put table back up* 2023-01-13 14:37:34 -05:00
Cory LaViska
b45a9d55ca update deps 2023-01-13 14:35:28 -05:00
Cory LaViska
7ce079b7a1 *flip table* 2023-01-13 14:29:25 -05:00
Cory LaViska
b0ba9ff14f remove expect 2023-01-13 14:22:45 -05:00
Cory LaViska
f665bf984b fix types 2023-01-13 14:22:06 -05:00
Cory LaViska
ac429a62c0 disable lint 2023-01-13 14:16:16 -05:00
Cory LaViska
dc909d10b6 fix words 2023-01-13 14:08:44 -05:00
Cory LaViska
aa65077b12 add getFormControls() method 2023-01-13 14:06:58 -05:00
Cory LaViska
7e37c51856 revert example 2023-01-13 12:52:02 -05:00
Cory LaViska
6e26daf804 add form attribute; fixes #1130 2023-01-13 12:34:33 -05:00
Cory LaViska
25c2d2d5bf upgrade CEM plugin 2023-01-13 10:39:59 -05:00
Cory LaViska
edc9e69f30 add @documentation tag 2023-01-12 10:26:25 -05:00
Cory LaViska
2e7ac38678 fixes #1127 2023-01-12 09:34:41 -05:00
Cory LaViska
1a68c825c0 update deps section 2023-01-11 15:29:51 -05:00
Cory LaViska
2cbdeeade0 remove cspell comment 2023-01-10 17:34:00 -05:00
Cory LaViska
79624f63ed add en-GB 2023-01-10 17:33:55 -05:00
xdev1
01a8ec36ec #1108 - simplification of translation files (#1120)
Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2023-01-10 17:33:23 -05:00
Jens Gabe
60324885ed Updated Danish translations (#1123)
Fixed typos and copy/paste translations from other languages
2023-01-10 17:30:01 -05:00
Cory LaViska
0c2f43b837 fixes #1125 2023-01-10 17:10:18 -05:00
Cory LaViska
0df27cf730 never return -0 2023-01-10 16:14:30 -05:00
Cory LaViska
68ed69292c add validation tests; fixes #1065 2023-01-10 15:05:44 -05:00
Cory LaViska
62c58b3a8c simplify validation logic 2023-01-10 15:05:07 -05:00
Cory LaViska
1fbb809057 update docs comment 2023-01-10 15:04:53 -05:00
Cory LaViska
e2d2f5d670 rename FormSubmitController; remove this.invalid 2023-01-10 13:24:06 -05:00
Cory LaViska
acef0da2c1 remove unused classes 2023-01-10 12:28:44 -05:00
Cory LaViska
3c66d2ab99 fixes #1121 2023-01-10 10:48:55 -05:00
Cory LaViska
31f16c4680 fix changelog 2023-01-09 16:31:05 -05:00
Cory LaViska
8056379fdd support multiple properties in watch decorator 2023-01-09 16:07:59 -05:00
Cory LaViska
9a6b9a7841 fixes #1119 2023-01-09 14:54:17 -05:00
Cory LaViska
02fc39ebe0 add parts 2023-01-09 14:25:26 -05:00
Cory LaViska
a90b22c05d add test 2023-01-09 14:25:21 -05:00
Cory LaViska
f5dd4f2aca fixes #1117 2023-01-09 14:20:17 -05:00
Cory LaViska
d5b3489b22 fixes #1066 2023-01-09 13:06:34 -05:00
Cory LaViska
8ee5f19184 pin source-map to beta; fixes #1066 2023-01-09 12:55:35 -05:00
Cory LaViska
d0a32d48b1 upgrade CEM and fix comment 2023-01-09 10:47:10 -05:00
Cory LaViska
bf527437a0 add stylesheet to test runner; fix tests 2023-01-09 10:35:37 -05:00
Cory LaViska
ae3070ac45 fixes #1107 2023-01-09 09:40:51 -05:00
Cory LaViska
6af68343a7 stop removing eslint comments 2023-01-09 09:39:47 -05:00
alenaksu
48ccc95dd9 chore: add react examples 2023-01-08 13:36:24 +01:00
Alessandro
c6a6a77bbd feat: add carousel component
feat: add nav indicators

wip

wip

wip

fix: minor fixes

fix: minor fixes

fix: some refactor

chore: update docs

chore: update docs

fix: remove slide component

feat: create sl-carousel-item

feat: code refactoring and improvements

chore: update docs with more examples

chore: fix docs

feat: add autoplay

feat: implement accessibility

fix: change icons for rtl

chore: minor change

feat: improve accessibility

fix: minor regression

fix: minor regression

chore: fix docs

fix: improve accessibility and minor fixes

fix: remove heading and refactor component

chore: add custom style exmaple

fix: address review commnets

* Removed header from carousel
* Added `ArrowUp` and `ArrowDown` in keyboard navigation
* Added `--scroll-hint-margin` css property
* Added an example with customized carousel layout
* Fixed thumbnails navigation in demo
* Renamed show-controls to show-arrows and updated the corresponding parts/css accordingly
* Changed `activeSlideElement` getter to a private method
* Changed pagination colors
* Added `--slide-width` and `--slide-height` css properties

chore: update docs

fix: integrate latest repo changes

fix: add aspect ratio and rebase

chore: remove ignore path

feat: multiple slides per page

feat: multiple slide per page

fix: various improvements

chore: minor changes

chore: minor changes

chore: add bit of documentation

chore: improve documentation

fix: add unit tests and fix minor issues

chore: update documentation and unit tests

chore: update tests
2023-01-08 12:24:24 +01:00
Cory LaViska
e632b51eb8 fixes #1110 2023-01-06 16:43:31 -05:00
Cory LaViska
c8e633c4a1 fix color picker bug 2023-01-06 15:24:57 -05:00
Cory LaViska
724f4a59db remove log 2023-01-06 13:28:48 -05:00
Cory LaViska
041364fb7d ignore swatch whitespace 2023-01-06 13:28:30 -05:00
Cory LaViska
fbcb4d8dbd fixes #1109 2023-01-06 12:25:03 -05:00
Cory LaViska
27a6b4a8c9 fixes #1108 2023-01-06 11:50:10 -05:00
Cory LaViska
c814e9e94e 2.0.0-beta.88 2023-01-05 15:43:08 -05:00
Cory LaViska
0e957c0cd4 update version 2023-01-05 15:41:32 -05:00
Cory LaViska
b330657e0a focus on disabled menu items and fix aria-checked 2023-01-05 15:13:21 -05:00
Cory LaViska
a0fce64fd9 closes #1070 2023-01-05 14:50:19 -05:00
Cory LaViska
b183a04fba add auto 2023-01-05 13:33:10 -05:00
Cory LaViska
7645b997b2 fixes #1105 2023-01-05 13:30:39 -05:00
Cory LaViska
d81e2f1470 fixes #1063 2023-01-05 12:43:17 -05:00
Cory LaViska
f50fe72df2 don't show scrollbar 2023-01-05 12:40:22 -05:00
Cory LaViska
dcca64a986 use public method for validity 2023-01-05 11:54:10 -05:00
Cory LaViska
c216cfe0fd fix min/max types 2023-01-05 11:29:51 -05:00
Cory LaViska
192f15e3b7 update cem plugin 2023-01-05 11:29:34 -05:00
Cory LaViska
01be3daf6d update docs 2023-01-04 14:51:30 -05:00
Cory LaViska
d36eec5637 add info about updateComplete 2023-01-04 14:04:24 -05:00
Cory LaViska
121464fa2d make swatches an attribute 2023-01-04 12:37:16 -05:00
Cory LaViska
ee0254e822 fixes #1097 2023-01-04 11:29:11 -05:00
Cory LaViska
27f634402c fix switch default value; #1103 2023-01-04 11:04:25 -05:00
Cory LaViska
67fbe3b34e fixes #1101 2023-01-04 09:58:56 -05:00
Cory LaViska
164ebce990 remove tts test 2023-01-03 15:17:39 -05:00
Cory LaViska
571ae704e0 update changelog 2023-01-03 15:10:51 -05:00
Cory LaViska
ad305fb653 Remove orphaned code 2023-01-03 15:09:57 -05:00
Cory LaViska
fc0541ce53 make internal 2023-01-03 15:08:49 -05:00
Cory LaViska
c8555f448c reorg and add private keyword 2023-01-03 15:04:07 -05:00
Cory LaViska
96e41198ec remove comment 2023-01-03 14:24:27 -05:00
Cory LaViska
57064aef4d remove void 2023-01-03 13:36:12 -05:00
Cory LaViska
cf200aa58a update tests 2023-01-03 10:37:39 -05:00
Cory LaViska
388a4f85a4 Revert "Remove the need to bind member methods in the connectedCallback (#1081)"
This reverts commit 5f8556b1b2.
2023-01-03 10:19:25 -05:00
Jeremiah Hoyet
5f8556b1b2 Remove the need to bind member methods in the connectedCallback (#1081)
* Remove the need to bind member methods in the connectedCallback

* Remove console.log
2023-01-03 10:15:12 -05:00
Cory LaViska
e411b57124 fixes #1096 2023-01-03 10:10:14 -05:00
Cory LaViska
0120e7429d update changelog 2023-01-03 09:49:56 -05:00
sowiner
377dbe28eb add zh-tw translations (#1086)
* add zh-tw translations

* add numOptionsSelected translate
2023-01-03 09:48:56 -05:00
Cory LaViska
b25b1d5750 Update changelog 2023-01-03 09:05:16 -05:00
Alan Chambers
0e1b792bf7 Update make-react.js script to use new @lit-labs/react createComponent options object (#1090)
* Update react wrapper signature

Updated react createComponent to use new options object

* removed unused pascalCase import
2023-01-03 08:57:27 -05:00
Cory LaViska
417f0d17c9 don't scroll when refocusing 2022-12-28 17:21:11 -05:00
Cory LaViska
d9252fe755 ignore disabled controls 2022-12-28 17:18:48 -05:00
Cory LaViska
c5555ab5fe Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2022-12-28 16:50:37 -05:00
Cory LaViska
eb61dc7d91 update bootstrap-icons 2022-12-28 16:50:35 -05:00
Bünyamin Eskiocak
a4c522f090 Added control--checked and control--indeterminate parts to <sl-checkbox> (#1085)
* removed checked and indeterminate parts from doc

* Revert "removed checked and indeterminate parts from doc"

This reverts commit 5e702387e1ace4e5d6b424f861df8e6a3a1a4fdd.

* control--checked & control--indeterminate parts
2022-12-28 16:40:59 -05:00
Cory LaViska
a8fe8c3e71 update hover styles 2022-12-28 16:36:07 -05:00
Cory LaViska
87000306c0 remove unused import 2022-12-28 16:25:33 -05:00
Cory LaViska
ae07b7d0a8 update changelog 2022-12-28 16:22:08 -05:00
Cory LaViska
626b76610f fix example 2022-12-28 16:22:05 -05:00
Cory LaViska
b4a1e1b0c9 fix docs 2022-12-28 16:12:41 -05:00
Cory LaViska
913243f8c1 more finishing touches + tests 2022-12-28 16:07:37 -05:00
Cory LaViska
563ed81984 remove example 2022-12-28 16:07:22 -05:00
Cory LaViska
fcbf339a86 remove unused selectors 2022-12-28 16:07:17 -05:00
Cory LaViska
70585e1d2a finishing touches 2022-12-28 15:31:42 -05:00
Cory LaViska
479e568296 update docs 2022-12-28 15:31:05 -05:00
Cory LaViska
a473e41ab3 fix docs 2022-12-28 14:43:01 -05:00
Cory LaViska
92f6a2d8e9 update changelog 2022-12-28 11:42:34 -05:00
Cory LaViska
06dc5740bf updates 2022-12-28 11:42:08 -05:00
Cory LaViska
fe524e0fac ignore clear button keys 2022-12-20 20:49:10 -05:00
Cory LaViska
ea7de2eb70 document slots 2022-12-20 18:44:57 -05:00
Cory LaViska
b8d02537a6 add grouping example 2022-12-20 18:44:33 -05:00
Cory LaViska
24744ef8c5 docs + cleanup wrapper 2022-12-20 18:24:25 -05:00
Cory LaViska
69997466be update example 2022-12-20 18:23:51 -05:00
Cory LaViska
f7d7fdf5b1 rerorder props 2022-12-20 18:23:19 -05:00
Cory LaViska
c0013c5639 refactor into set function 2022-12-20 17:53:55 -05:00
Cory LaViska
935040204f fix alignment to match other controls 2022-12-20 17:53:45 -05:00
Cory LaViska
2ffbf9b017 update examples 2022-12-20 17:43:50 -05:00
Cory LaViska
0f67b9a9d1 remove unused var 2022-12-20 13:51:53 -05:00
Cory LaViska
c5dee51233 restore example 2022-12-20 13:50:48 -05:00
Cory LaViska
b07238d536 temporarily disable a11y bug 2022-12-20 13:37:30 -05:00
Cory LaViska
e2a65c28f4 update select examples 2022-12-20 13:37:05 -05:00
Cory LaViska
1f457cdde0 keep on keepin on 2022-12-20 13:36:53 -05:00
Cory LaViska
46fda5f0a6 upgrade 2022-12-20 13:36:06 -05:00
Cory LaViska
e22c2f839b don't select disabled options 2022-12-20 13:00:27 -05:00
Cory LaViska
5bff912162 loosen up that type 2022-12-20 13:00:13 -05:00
Cory LaViska
f3010cecbe fix tests 2022-12-20 12:13:39 -05:00
Cory LaViska
2dc275defd fix validity and events 2022-12-20 11:40:49 -05:00
Cory LaViska
9f79445292 improve scroll on open 2022-12-20 10:31:55 -05:00
Cory LaViska
10cb26b81e focus after update 2022-12-20 10:21:41 -05:00
Cory LaViska
3722e0ad91 focus after open 2022-12-19 18:17:41 -05:00
Cory LaViska
f28a0ec743 fix in screen readers 2022-12-19 17:46:31 -05:00
Cory LaViska
a42b393bf1 Merge branch 'next' into select 2022-12-17 11:29:58 -05:00
Cory LaViska
41f50777bd update changelog 2022-12-17 11:29:25 -05:00
Cory LaViska
6afc3ba12e select rewrite (incomplete) 2022-12-17 11:27:30 -05:00
Alessandro
d8b7040a9e fix(tree): add initial sync (#1080) 2022-12-17 11:26:42 -05:00
Cory LaViska
59cd70ae6f 2.0.0-beta.87 2022-12-14 09:29:18 -05:00
Cory LaViska
6d9c8561fb update version 2022-12-14 09:22:57 -05:00
Cory LaViska
edb8a92838 update changelog 2022-12-14 09:22:30 -05:00
Cory LaViska
da3ffe9a60 update changelog and spelling list 2022-12-13 12:15:40 -05:00
Cory LaViska
e2b791dee8 i didn't attend Oxford, but I do like their commas 2022-12-13 12:15:28 -05:00
Alan Chambers
9178be576b color-picker library change and hsv format support (#1072)
* sl-color-picker use lib '@ctrl/tinycolor' instead of 'color'

typescript, esm and smaller

* parseColor adjustments

removed normalizeColorString and other tweaks

* added hsv format

* fixed const
2022-12-13 11:01:07 -05:00
Cory LaViska
752f5cff55 fixes #1071 2022-12-13 09:28:12 -05:00
Cory LaViska
3675787ddd remove polyfill 2022-12-13 07:24:06 -05:00
Cory LaViska
6284ed0347 Update tsconfig 2022-12-09 11:54:40 -05:00
Cory LaViska
1ff05c4b3d fixes #1061 2022-12-08 14:42:04 -05:00
Cory LaViska
066abe4e52 wait longer 2022-12-08 11:16:08 -05:00
Cory LaViska
e9134ddcf6 fixes #1051 2022-12-08 10:54:05 -05:00
Cory LaViska
0237851aa1 fix failing test 2022-12-08 09:01:33 -05:00
Cory LaViska
1841251fdf Current (#1059)
* Update fa.ts (#1053)

Improved translations by using original Farsi words rather than English ones

* Revert "Update fa.ts (#1053)" (#1058)

This reverts commit c951661b58.

Co-authored-by: Hadi F <62212402+hadi-f90@users.noreply.github.com>
2022-12-08 08:52:55 -05:00
Cory LaViska
b1e48406f3 #1053 2022-12-08 08:51:03 -05:00
Cory LaViska
4ca51cf11e cleanup 2022-12-07 17:43:48 -05:00
Cory LaViska
41bd61c3a5 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2022-12-07 17:40:49 -05:00
Cory LaViska
c6df057e15 fixes #917 2022-12-07 17:40:46 -05:00
Alan Chambers
1428481a5c Add direct react export to package.json (#1050) 2022-12-07 09:23:26 -05:00
Cory LaViska
1c359fbea9 2.0.0-beta.86 2022-12-06 15:38:56 -05:00
Cory LaViska
0329694760 update version 2022-12-06 15:38:52 -05:00
Cory LaViska
936ec5a23a fix focus visible 2022-12-06 15:32:06 -05:00
Cory LaViska
d291506284 update docs 2022-12-06 14:12:36 -05:00
Cory LaViska
3877351fdc fix wrong values for buttons in form submission 2022-12-06 14:04:34 -05:00
Cory LaViska
7885572ebd jsdoc exported functions 2022-12-06 11:48:57 -05:00
Cory LaViska
2ee926023c fix translate arg 2022-12-06 11:41:51 -05:00
Cory LaViska
69e557cd8c convert to static method 2022-12-06 11:37:44 -05:00
Cory LaViska
f2efa73e20 improve documentation 2022-12-06 11:18:14 -05:00
Cory LaViska
2dd57956d5 move slots up and add parens to methods 2022-12-06 11:17:48 -05:00
Cory LaViska
ce86a1c9f2 update CEM plugin 2022-12-06 11:17:28 -05:00
Cory LaViska
a0f83c3b2b fix border radius bug 2022-12-05 09:38:42 -05:00
Cory LaViska
80a16ee42a remove slot wrappers 2022-12-02 17:03:59 -05:00
Cory LaViska
31b05fedd3 fix mislabeled slot 2022-12-01 16:39:39 -05:00
Cory LaViska
d3a7950483 add fallback content 2022-12-01 16:36:53 -05:00
Cory LaViska
f299621490 add note about icons + slots 2022-12-01 16:29:02 -05:00
Cory LaViska
c62ee3e207 refactor icon animation 2022-12-01 16:02:36 -05:00
Cory LaViska
a313087937 fix example 2022-12-01 16:01:45 -05:00
Cory LaViska
3945571b20 update changelog 2022-12-01 15:39:40 -05:00
Cory LaViska
c6a044416a remove transform from animations 2022-12-01 15:38:59 -05:00
Cory LaViska
d2f1b50ad0 fix tree item bug 2022-12-01 15:29:46 -05:00
Cory LaViska
1f1bae77dd don't use transform 2022-12-01 15:28:02 -05:00
Cory LaViska
829e22dc1e stop using transform 2022-12-01 15:26:46 -05:00
Cory LaViska
8b28ee818f improve custom icons 2022-12-01 15:25:09 -05:00
Cory LaViska
afb2e3d5b4 fix docs 2022-12-01 14:08:16 -05:00
Cory LaViska
fb0adb9ccb fix description 2022-12-01 13:28:04 -05:00
Cory LaViska
efea514f5a add summary-icon slot + example; #1046 2022-12-01 13:23:09 -05:00
Cory LaViska
fda9bd52a3 move up 2022-11-30 15:45:21 -05:00
Cory LaViska
01f8ce6b03 fix typo 2022-11-30 15:43:43 -05:00
Cory LaViska
e10651565f fix typos 2022-11-30 15:43:36 -05:00
Bünyamin Eskiocak
35aa56d334 Added title attribute (#1043)
* added title attribute

Fixes #1042

* added tests for title

* also some other tests

* clarify why title was added
2022-11-30 15:42:56 -05:00
Tao Cumplido
31e1f2fc59 fix sl-selection-change event not emitted when all items are deselected (#1038)
also added more tests
2022-11-30 08:18:56 -05:00
Cory LaViska
ee30f7a10b fix indicator animation bug 2022-11-29 16:06:52 -05:00
Cory LaViska
a50909d474 fixes #1037 2022-11-29 15:52:13 -05:00
Cory LaViska
63115d51e5 fixes #1036 2022-11-29 14:47:23 -05:00
Cory LaViska
026036a14b fix border when hovering over icon; #1035 2022-11-29 14:22:50 -05:00
Cory LaViska
22e09a778b update test to account for removed attrs 2022-11-29 11:30:11 -05:00
Cory LaViska
488088d5f0 add header-actions slot 2022-11-29 11:17:15 -05:00
Cory LaViska
0c18880e5c replace icon x => x-lg 2022-11-29 11:03:58 -05:00
Cory LaViska
0b0a571d17 update changelog 2022-11-29 10:01:53 -05:00
Cory LaViska
ea9e596279 remove unused attribs 2022-11-29 10:01:25 -05:00
Cory LaViska
fe17c8406b add link to BEM post 2022-11-29 09:47:48 -05:00
Cory LaViska
e7a4a5135d improve screen reader experience 2022-11-29 09:25:45 -05:00
Cory LaViska
ebf12860be update changelog 2022-11-28 16:17:43 -05:00
Bünyamin Eskiocak
4b81778e46 moved changelog entry to correct version (#1033) 2022-11-28 16:14:16 -05:00
Cory LaViska
28a8a90bb1 update docs 2022-11-28 16:13:36 -05:00
Cory LaViska
8dab5a8f04 Merge branch 'next' into cem-better-data 2022-11-28 16:07:29 -05:00
Cory LaViska
4ac9483213 remove unused config 2022-11-28 16:06:35 -05:00
Cory LaViska
ae3c0d72c0 update plugin 2022-11-28 16:05:11 -05:00
Cory LaViska
83b73e823d update changelog; #1030 2022-11-28 11:56:13 -05:00
Tao Cumplido
da6ae608f1 only emit a tree's selection change event when the selection has actually changed (#1030) 2022-11-28 11:55:12 -05:00
Cory LaViska
1e39647d7f Merge branch 'next' into cem-better-data 2022-11-28 11:51:20 -05:00
Cory LaViska
4dc92247d5 add usage disclaimer to popup 2022-11-28 09:56:41 -05:00
Cory LaViska
1b6f982d68 fix missing dist 2022-11-28 09:24:27 -05:00
Cory LaViska
81c775ea87 2.0.0-beta.85 2022-11-24 13:39:15 -05:00
Cory LaViska
09f741e930 update changelog 2022-11-24 13:36:21 -05:00
Tao Cumplido
602a863d89 fix isTreeItem method when called with non-element nodes (#1026) 2022-11-24 13:33:33 -05:00
Cory LaViska
3dd19a7f62 fixes #1024 2022-11-24 13:29:34 -05:00
Cory LaViska
bdf890ab0d Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2022-11-23 10:48:04 -05:00
Cory LaViska
0b7eae0c20 update changelog 2022-11-23 10:48:02 -05:00
Tao Cumplido
f3e273744a don't treat lazy tree items as leaf nodes (#1023) 2022-11-23 10:46:25 -05:00
Cory LaViska
7f9b0bd5a2 fixes #1024 2022-11-23 10:38:04 -05:00
Cory LaViska
6579a95999 fixes #1024 2022-11-23 10:32:27 -05:00
Cory LaViska
aaa8e4a01b 2.0.0-beta.84 2022-11-22 15:26:00 -05:00
Cory LaViska
200d4385ec update version 2022-11-22 15:23:57 -05:00
Cory LaViska
50d983a1ab move controls and show code when clicking 2022-11-22 15:06:11 -05:00
Cory LaViska
1e6d295746 update form validation docs 2022-11-22 14:18:54 -05:00
Cory LaViska
5d128c154c remove tab reuse logic
This broke recently so might as well remove the extra logic and make launching a bi faster.
2022-11-22 13:22:51 -05:00
Cory LaViska
6c7d7f4b7e revert dist 2022-11-22 13:01:15 -05:00
Cory LaViska
0cc8fdb8f8 add comment 2022-11-22 11:00:36 -05:00
Uilton Oliveira
4f7d915853 Fix mapping in exports (#1020)
* Fix mapping in exports

Fixes #1019
- Add correct mapping for public entrypoints.

* Remove shoelace.js from from exports list

Should still works fine while importing this way: import { thing } from '@shoelace-style/shoelace';
2022-11-22 10:58:07 -05:00
Cory LaViska
61738a3f6a update cem plugin 2022-11-22 09:23:10 -05:00
Cory LaViska
9061c1987a Merge branch 'next' into cem-better-data 2022-11-22 09:01:05 -05:00
Cory LaViska
c40b3a86a9 add support for extended language codes 2022-11-22 08:55:34 -05:00
Cory LaViska
8b2c090bac fixes #1018 2022-11-21 16:18:44 -05:00
Bünyamin Eskiocak
8f2a3bd8bd fixed type validation by changing variant to type (#1017) 2022-11-21 16:06:41 -05:00
Cory LaViska
ade05a6262 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2022-11-21 12:41:15 -05:00
Bünyamin Eskiocak
a706e69be6 Added stepUp, stepDown and showPicker functions (#1013)
* added stepUp and stepDown to sl-range

* step function & default props tests for sl-range

* stepUp, stepDown & showPicker functions for input

* step functions & default props tests for sl-input

* made name and placeholder default to empty string

* updated changelog

Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2022-11-21 12:41:09 -05:00
Bünyamin Eskiocak
bc6a813c46 Added formenctype to <sl-button> (#1009)
* added formenctype to sl-button

* updated changelog

Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2022-11-21 12:37:54 -05:00
Cory LaViska
599373c24a use attr not prop 2022-11-18 10:45:47 -05:00
Cory LaViska
daebd08475 add validation states to all form controls; closes #1011 2022-11-18 09:56:05 -05:00
Cory LaViska
a3f658938d bulletproof part selectors 2022-11-17 09:35:44 -05:00
Cory LaViska
23414fe411 fix callout 2022-11-16 14:59:35 -05:00
Cory LaViska
c8061fdee9 update deps 2022-11-16 14:57:21 -05:00
Cory LaViska
857b21a6a3 don't error out on missing summaries 2022-11-16 12:58:32 -05:00
Cory LaViska
48be3f46b8 add cem-custom-data-generator 2022-11-16 12:47:34 -05:00
Cory LaViska
66012a57c8 remove unused properties; #1007 2022-11-16 10:21:38 -05:00
Samuel Stroschein
0e77d8a459 add "exports" field to fix node16 module resolution (#1007) 2022-11-16 10:19:43 -05:00
Cory LaViska
b559c2345a remove whitespace 2022-11-15 13:52:03 -05:00
Cory LaViska
d79d7da299 various improvements in forced-colors mode 2022-11-14 16:12:24 -05:00
Cory LaViska
b296ac08cf fix menu selection in WHC 2022-11-14 14:49:48 -05:00
Cory LaViska
36e677dd6b fix dialog/drawer borders in WHC 2022-11-14 14:49:24 -05:00
Cory LaViska
6167993941 improve search in WHC 2022-11-14 14:49:13 -05:00
Cory LaViska
f9d3f0be27 use outline 2022-11-14 14:43:12 -05:00
Cory LaViska
7f76cacc10 make docs search usable 2022-11-14 14:41:35 -05:00
Cory LaViska
96cac8da85 improve color picker for WHC 2022-11-14 13:00:30 -05:00
Cory LaViska
a788e976c9 update docs/changelog 2022-11-14 09:50:35 -05:00
Emil
68c1319ed5 Add loading attribute to sl-avatar (#1006) 2022-11-14 09:48:26 -05:00
Cory LaViska
9db6f256e5 update icons 2022-11-14 09:11:12 -05:00
Cory LaViska
33e19c003c fixes #968 2022-11-10 17:12:29 -05:00
Cory LaViska
337d688bd3 add 18.x 2022-11-10 16:34:22 -05:00
Cory LaViska
cc305f8957 replace all paths in CEM 2022-11-10 16:27:23 -05:00
Cory LaViska
40cb38e0a0 point module paths to dist; fixes #725 2022-11-10 15:28:08 -05:00
Cory LaViska
a0d3ac047d loosen rule 2022-11-10 15:26:10 -05:00
Bünyamin Eskiocak
e399bd19a2 updated changelog (#1005) 2022-11-10 12:34:36 -05:00
Cory LaViska
f64f144b4b fixes #985 2022-11-10 12:27:03 -05:00
Cory LaViska
197a0c3048 update icon tests 2022-11-10 12:02:26 -05:00
Cory LaViska
e5e6b0d74f watch grouped tests by default 2022-11-10 11:43:40 -05:00
Cory LaViska
db1c2ee5cc update changelog 2022-11-10 11:30:51 -05:00
Cory LaViska
c6a43ba4c2 remove base from icons 2022-11-10 11:30:42 -05:00
Cory LaViska
ed45f52433 update comment 2022-11-10 08:06:35 -05:00
Cory LaViska
2d478c36fa fixes #963 2022-11-09 16:53:53 -05:00
Cory LaViska
f140007758 fixes #992 2022-11-09 16:07:34 -05:00
Cory LaViska
f03b09a410 fixes #965 2022-11-09 15:27:51 -05:00
Cory LaViska
0d9767596a disable rule 2022-11-09 13:18:05 -05:00
Cory LaViska
c626706e27 update changelog 2022-11-09 11:54:51 -05:00
VitaliyMaznyi
58b653f1ae improve input submit (#988)
add check to input component if japanese character is chosen by pressing enter key and prevent form submitting

Co-authored-by: VitaliyMaznyi <vitalii.maznyi@fasterthanlight.me>
2022-11-09 11:51:17 -05:00
Cory LaViska
5eeb98d39d fixes #925 2022-11-09 11:34:50 -05:00
Cory LaViska
e90b64b463 revert example 2022-11-09 09:58:04 -05:00
Cory LaViska
5fd682d83a fixes #990 2022-11-09 09:50:58 -05:00
Cory LaViska
49193c972f fixes #999 2022-11-08 12:15:14 -05:00
Cory LaViska
c027c0a527 use margin instead of padding 2022-11-07 12:31:13 -05:00
Cory LaViska
8160c33aa4 update deps 2022-11-07 12:06:34 -05:00
Cory LaViska
7d0226e3c4 make sl-show|hide cancelable 2022-11-03 10:33:10 -05:00
Cory LaViska
084c1dc5b5 fix tab a11y test 2022-11-02 08:48:39 -05:00
Cory LaViska
bfa320c5b5 fix a11y test 2022-11-02 08:43:52 -05:00
Buni48
4e1cf11461 Added turkish, british and austrian translations (#989)
* turkish translation

* austrian translation

* british translations

* updated changelog

* Update src/translations/en-gb.ts

Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2022-11-02 08:37:22 -05:00
dhellgartner
5b4197da2c Relative time tests (#991)
* Added tests for sl-relative-time

* Solves a blinker which appears only when I run
the full test suite: Chromium and webkit are fine
but sometimes there is this one error on firefox
for the disabled test of the menu item (saying there
was a timeout waiting). Did not happen again after
doing the change.

Co-authored-by: Dominikus Hellgartner <dominikus.hellgartner@gmail.com>
2022-11-02 08:37:00 -05:00
Cory LaViska
e29b2f12fb fix typo 2022-10-28 11:16:12 -04:00
Cory LaViska
33a41bb2e4 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2022-10-28 09:57:44 -04:00
Cory LaViska
ab754e9409 add transition duration custom prop 2022-10-28 09:57:41 -04:00
dhellgartner
433955acf7 Added tests for sl-menu (#984)
Co-authored-by: Dominikus Hellgartner <dominikus.hellgartner@gmail.com>
2022-10-28 08:31:20 -04:00
Buni48
55445a80a3 removed left border from button group (#983) 2022-10-28 08:28:18 -04:00
Cory LaViska
9a64ea0670 update changelog 2022-10-27 13:00:17 -04:00
Buni48
0ff144a787 Fixed button group inner border disappears (#980)
* fixed button group inner border disappears

* updated changelog

* show border on active
2022-10-27 12:55:20 -04:00
bolte-17
8893045bf1 Avoid null dereference when removing event listeners in Dropdown (#958) 2022-10-27 09:28:35 -04:00
kzsa
a8de02bd53 Add hungarian translation (#982) 2022-10-27 09:21:20 -04:00
Michael Daross
4db0aec1c2 Fix: Issue 978 - Missing @summary annotation (#979)
Fixes https://github.com/shoelace-style/shoelace/issues/978
2022-10-27 09:20:19 -04:00
Cory LaViska
d885087c93 this is why we can't have nice thing 2022-10-24 10:45:42 -04:00
Cory LaViska
065aad2e4c bump node versions 2022-10-24 10:15:56 -04:00
Cory LaViska
9027ce055a prettier 2022-10-24 10:06:39 -04:00
Felix Becker
119b923522 testing: components/tag (#949)
* add test for SlTag

* add all classes (even not test relevant ones) to fix failing tests
2022-10-24 10:04:42 -04:00
Felix Becker
3b4d0652c4 testing: components/skeleton (#951)
* add tests for SlSkeleton

* check base aria-properties too
2022-10-24 10:04:30 -04:00
Felix Becker
1301934796 add test for SlRating (#953) 2022-10-24 10:04:17 -04:00
Emil
a8539bae02 Update vue-2.md (#959) 2022-10-24 10:03:24 -04:00
Manuel Schmidt
8121faa1d4 Enrich components @summary with description from docs (#962)
* keep header styles with repositioned description text

* `animated-image` move description to component

* code style

* `avatar` add summary from docs

* `badge` add summary from docs

* `breadcrumb` add summary from docs

* `button` add summary from docs

* lead sentence is now part of the header

* `button-group` add summary from docs

* `card` add summary from docs

* `checkbox` add summary from docs

* `color-picker` add summary from docs

* `details` add summary from docs

* `dialog` add summary from docs

* `divider` add summary from docs

* `drawer` add summary from docs

* `dropdown` add summary from docs

* `format-bytes` add summary from docs

* `format-date` add summary from docs

* `format-number` add summary from docs

* `icon` add summary from docs

* `icon-button` add summary from docs

* `image-comparer` add summary from docs

* `include` add summary from docs

* `input` add summary from docs

* `menu` add summary from docs

* `menu-item` add summary from docs

* `menu-label` add summary from docs

* `popup` add summary from docs

* `progressbar` add summary from docs

* `progress-ring` add summary from docs

* `radio` add summary from docs

* `radio-button` add summary from docs

* `range` add summary from docs

* `rating` add summary from docs

* `relative-time` add summary from docs

* `select` add summary from docs

* `skeleton` add summary from docs

* `spinner` add summary from docs

* `split-panel` add summary from docs

* `switch` add summary from docs

* `tab-group` add summary from docs

* `tag` add summary from docs

* `textarea` add summary from docs

* `tooltip` add summary from docs

* `visually-hidden` add summary from docs

* `animation` add summary from docs

* `breadcrumb-item` add summary from docs

* `mutation-observer` add summary from docs

* `radio-group` add summary from docs

* `resize-observer` add summary from docs

* `tab` add summary from docs

* `tab-panel` add summary from docs

* `tree` add summary from docs

* `tree-item` add summary from docs

* remove `title` for further usage of `Sl` classnames in docs

* revert: use markdown parser for component summary
2022-10-21 09:56:35 -04:00
Felix Becker
70766b4e68 add new theme token --sl-input-required-content-color to both themes (#948)
* add new theme token --sl-input-required-content-color to both themes

* use new token

* use label color as default value
2022-10-21 08:59:20 -04:00
Nick Cipriani
361efd43a2 Remove extra footer slot from Card with Footer example (#966)
The button and div both had a slot equal to footer when only the footer div actually needs it.
2022-10-21 08:08:49 -04:00
Manuel Schmidt
a78b9fe5bd sl-alert: move description from docs to component (#947)
* move component description from docs to component

* use component description in docs

* add style for lead sentence, keep bc for not updated components

* run `npm prettier`

* render summary as part of the header

* add `title` as another custom tag for JSDoc

* render components title in header
2022-10-19 09:35:53 -04:00
Manuel Schmidt
0cba4695ba Enrich docs with @summary and @title from custom-elements.json (#952)
* provide possibility to render components "description" into docs with `[component-description:COMPONENT_NAME]`

* the meta summary should be rendered into header too
2022-10-18 12:22:48 -04:00
Buni48
22fa81433e tests for sl-tab-panel (#956) 2022-10-18 10:49:50 -04:00
Buni48
5458a0e8f5 Added tests to menu-item & menu-label (#935)
* added tests to menu-item & menu-label

* updated changelog

* aTimeout instead of setTimeout
2022-10-14 08:55:09 -04:00
Buni48
0e67f85995 Fixed applying border radius to card header (#934)
* fixed applying border radius to card header

* updated changelog

Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2022-10-14 08:36:11 -04:00
Buni48
808003f3df Added checked state part for radio & radio button (#933)
* added checked state part for radio & radio button

* updated changelog
2022-10-14 08:35:00 -04:00
Buni48
46c225d50f removed intl safari support notice (#954) 2022-10-14 08:34:11 -04:00
Cory LaViska
0a3656efc5 prettier 2022-10-03 16:19:22 -04:00
Greg Leaver
0aca600063 Fix <sl-range> docs to have correct closing tag in examples. (#928)
A couple of the examples had `</sl-input>` instead of `</sl-range>`
2022-10-03 16:15:04 -04:00
Cory LaViska
38f05ff010 ts 2022-09-21 10:30:23 -04:00
Cory LaViska
3d4cbbf6ba fix path 2022-09-21 10:27:37 -04:00
Cory LaViska
c877e16c14 improve badge font size 2022-09-20 10:25:01 -04:00
Cory LaViska
acce8eb146 increase badge padding 2022-09-20 10:19:35 -04:00
Cory LaViska
03fe71353a 2.0.0-beta.83 2022-09-19 08:50:51 -04:00
Cory LaViska
8915282082 update version 2022-09-19 08:46:31 -04:00
Cory LaViska
28003ac430 downgrade Floating UI for perf reasons 2022-09-19 08:39:11 -04:00
Cory LaViska
f2dcad82a9 fixes #916 2022-09-19 08:19:04 -04:00
Cory LaViska
69ff4f0bbc refactor emit into ShoelaceElement 2022-09-16 16:21:40 -04:00
Cory LaViska
a1c93fd30f fixes #913 2022-09-16 15:27:10 -04:00
ErikOnBike
bdf8c4e669 Fix workaround for Safari which still has old property name in sl-progress-ring (#914) 2022-09-16 15:14:38 -04:00
Cory LaViska
854e576c2c fixes #912 2022-09-16 15:11:02 -04:00
Cory LaViska
cf32064e8c add issue number 2022-09-12 08:53:32 -04:00
Cory LaViska
1f91d4cb79 add missing part 2022-09-12 08:50:16 -04:00
Cory LaViska
a7fdb23577 fix tooltip alignmen 2022-09-12 07:51:03 -04:00
Cory LaViska
39d1bbed2c fixes #905 2022-09-09 09:38:54 -04:00
Cory LaViska
e2e152c373 update changelog 2022-09-05 12:56:05 -04:00
Cory LaViska
aae74dbaf7 reorder and update changelog 2022-09-05 12:56:00 -04:00
Buni48
6c716db037 added expand button part for tree item (#893); fixes #890 2022-09-05 12:52:41 -04:00
Cory LaViska
7c0c0fa244 use event phase constants 2022-09-05 12:27:47 -04:00
Jared White
ffd32e52ef Avoid bubbling events for sl-include (#897)
* Avoid bubbling events for sl-include

I ran into an issue where icons inside of an HTML include were dispatching `sl-load` events, which was causing my `sl-include` event handler to run multiple times. By adding these guards, we ensure only events immediately dispatched by the element itself will be handled.

* Use the correct event variable
2022-09-05 12:23:47 -04:00
Cory LaViska
27868b56e8 fixes #891 2022-09-02 15:19:35 -04:00
Cory LaViska
346f13e5a5 fix arrow placement 2022-09-02 11:28:49 -04:00
Cory LaViska
69547d4800 remove unused styles 2022-08-31 16:19:42 -05:00
Patrick McElhaney
9fc4185f87 Better descriptions for checkbox attributes (#894)
The description for the checkbox's value attribute is currently "the checkbox's value attribute." I think we can do better than that, especially considering anyone who hasn't worked with old-school HTML forms that POST to the URL in the `action` attribute is unlikely to know why a checkbox has a value and that value is not boolean. 

While I was here I improved a couple of other descriptions. If there's an interest, I can expand this PR to update all of the properties in all controls where the description is tautological.
2022-08-31 16:14:34 -05:00
Justin Fagnani
08b5da7c56 Change LitElement and site to Lit and lit.dev (#889) 2022-08-30 23:28:45 -05:00
Cory LaViska
05e026876a fix transition in Firefox 2022-08-30 17:51:51 -05:00
Cory LaViska
8d0d0ac4e0 forEach 2022-08-30 16:59:40 -05:00
Cory LaViska
972d629883 remove 404 from search 2022-08-30 16:59:24 -05:00
Cory LaViska
ce12020fca remove clearable 2022-08-30 16:09:49 -05:00
Cory LaViska
29b149cd26 fix sidebar buttons 2022-08-30 16:08:51 -05:00
Cory LaViska
7de37b9315 remove date 2022-08-30 16:08:40 -05:00
Cory LaViska
237cda3b63 fixes #884 2022-08-30 11:56:34 -05:00
Cory LaViska
51c4abb72b remove log 2022-08-30 11:49:17 -05:00
Cory LaViska
3a6890af81 fixes #886 2022-08-30 09:17:46 -05:00
Cory LaViska
7cc2c89f92 remove responsive media 2022-08-26 10:43:12 -04:00
Cory LaViska
bb55c93b1a upgrade exp components 2022-08-26 10:33:45 -04:00
Cory LaViska
a3d00a92a0 fixes #882 2022-08-26 10:28:18 -04:00
Christian Oliff
5eccce625a Update node.js.yml (#883) 2022-08-26 09:46:35 -04:00
Cory LaViska
0c3a25d2ad prettier 2022-08-26 09:21:02 -04:00
Rich Klein
d225aaa2ff Update Laravel integration guide to reflect v9.1 (#865)
Laravel 9.1 now uses Vite as the default bundler instead of webpack.
2022-08-26 09:18:28 -04:00
Christian Oliff
c6b7c24ed7 Update .editorconfig (#880) 2022-08-26 09:16:57 -04:00
Cory LaViska
827c36bb1d update tree; fixes #879 2022-08-26 09:12:51 -04:00
Cory LaViska
d9f48a5f2a update examples 2022-08-26 09:11:09 -04:00
Cory LaViska
ccbdc6c014 fix docs 2022-08-25 17:36:06 -04:00
Cory LaViska
b6edba912b fixes #873 2022-08-25 17:22:18 -04:00
Cory LaViska
4cc5baaa0b fixes #876 2022-08-25 16:24:13 -04:00
Cory LaViska
c2bbb0e8a4 fix arrow 2022-08-25 16:09:27 -04:00
Cory LaViska
352cade421 fix example 2022-08-25 12:24:39 -04:00
Cory LaViska
708977acf7 2.0.0-beta.82 2022-08-25 10:03:51 -04:00
Cory LaViska
bab5749788 update version 2022-08-25 10:00:03 -04:00
Cory LaViska
c6d0b27f0d fix test 2022-08-25 09:46:34 -04:00
Cory LaViska
ee31f3f682 fix disabled tab tabbing 2022-08-25 09:42:55 -04:00
Cory LaViska
105bb08ee1 fixes #872 2022-08-25 09:37:20 -04:00
Cory LaViska
ca64d26d77 fixes #871 2022-08-25 09:18:36 -04:00
Cory LaViska
23b2f9335b add test for #869 2022-08-25 09:00:52 -04:00
Cory LaViska
dfdd7c75c2 fixes #869 2022-08-25 08:48:23 -04:00
Cory LaViska
156a2812ae fix docs 2022-08-25 08:33:32 -04:00
Cory LaViska
e42beab336 remove extra border and redundant styels 2022-08-24 15:08:32 -04:00
Cory LaViska
098db9c3fa add arrow-placement 2022-08-24 15:06:16 -04:00
Cory LaViska
81d393fbc1 update heroicons example to 2.0 2022-08-24 08:23:55 -04:00
Cory LaViska
2696b1a9ec fixes #867 (for real this time) 2022-08-23 10:55:49 -04:00
Cory LaViska
322f23ba56 revert fix 2022-08-23 10:42:16 -04:00
Cory LaViska
09224041b8 revert fix 2022-08-23 10:42:01 -04:00
Cory LaViska
326816e7b7 fixes #867 2022-08-23 09:00:03 -04:00
Cory LaViska
4117b6d219 fix fallback 2022-08-22 17:15:27 -04:00
Cory LaViska
af9905acff fix flip fallbacks and add example 2022-08-22 17:11:21 -04:00
Cory LaViska
e0727cc72c cleanup when auto-size changes 2022-08-19 14:32:53 -04:00
Cory LaViska
624297f624 update docs 2022-08-19 14:26:41 -04:00
Cory LaViska
1996037acc update auto-size; fixes #860 2022-08-19 14:21:30 -04:00
Cory LaViska
8fa665d3e7 fix react example 2022-08-19 10:26:16 -04:00
Cory LaViska
f0a3972ef6 add sync 2022-08-19 09:17:44 -04:00
Cory LaViska
c8f42c5bde fixes #862 2022-08-18 08:43:37 -04:00
Cory LaViska
7a6144d8c4 fix react example 2022-08-17 17:23:38 -04:00
Cory LaViska
ea71155b8f fix style types 2022-08-17 16:31:23 -04:00
Cory LaViska
20e9e3c320 tree updates 2022-08-17 16:22:03 -04:00
Cory LaViska
bcf2139fe7 fix types 2022-08-17 12:33:37 -04:00
Cory LaViska
48f864475e fix tree single select behavior 2022-08-17 12:33:33 -04:00
Cory LaViska
32f24a881e make dir/lang reactive everywhere 2022-08-17 11:37:37 -04:00
Cory LaViska
0163edd8a3 improve RTL 2022-08-17 10:48:40 -04:00
Cory LaViska
81620f0199 2.0.0-beta.81 2022-08-17 09:31:21 -04:00
Cory LaViska
d850b0f507 update changelog 2022-08-17 09:29:15 -04:00
Cory LaViska
19b2cde0d9 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2022-08-17 08:50:09 -04:00
Cory LaViska
87e252940d fixes #858 2022-08-17 08:50:07 -04:00
Cory LaViska
c6d30ae1b5 fix example 2022-08-16 15:32:14 -04:00
Cory LaViska
23bb45b1b6 add react example 2022-08-15 16:41:55 -04:00
Cory LaViska
70f59aa4ba add support for external anchors 2022-08-15 16:26:10 -04:00
Cory LaViska
237230961a fixes #854 2022-08-15 12:16:21 -04:00
Cory LaViska
b669ab7b74 fixes #855 2022-08-15 11:02:36 -04:00
Cory LaViska
040e8ce5e1 update deps 2022-08-11 09:59:02 -04:00
Denis Korablev
a74de59687 chore(i18n): fix ru translations (#853) 2022-08-11 09:27:10 -04:00
Cory LaViska
186f57cdde update docs 2022-08-11 09:22:35 -04:00
Cory LaViska
c94db27f07 fix rating a11y 2022-08-11 09:17:34 -04:00
Cory LaViska
578a58042b fix test 2022-08-10 16:53:27 -04:00
Cory LaViska
b3b3956ff5 improve spinner a11y 2022-08-10 16:52:39 -04:00
Cory LaViska
ce2abb97e0 accept cmd+k for search 2022-08-10 16:28:39 -04:00
Cory LaViska
6fc71601dd fix attribute 2022-08-10 16:25:27 -04:00
Cory LaViska
fd3da7e773 fix examples 2022-08-10 16:24:45 -04:00
Cory LaViska
70700b1ab5 fix example 2022-08-10 12:54:36 -04:00
Cory LaViska
eb9107bf2b add cursor 2022-08-10 11:31:30 -04:00
Cory LaViska
d0820178c9 remove background on hover 2022-08-10 11:12:07 -04:00
Cory LaViska
a8e8325ea7 fix popup 2022-08-10 11:01:13 -04:00
Cory LaViska
4b9e35313d remove dup 2022-08-10 09:13:50 -04:00
Cory LaViska
dc1394483a fix link 2022-08-10 08:29:49 -04:00
Cory LaViska
4b32b9461c 2.0.0-beta.80 2022-08-09 16:33:55 -04:00
Cory LaViska
308c8228aa fix tooltip 2022-08-09 16:29:55 -04:00
Cory LaViska
d74d2dec2e move var 2022-08-09 16:22:44 -04:00
Cory LaViska
c9f3497a8a update comment 2022-08-09 16:22:40 -04:00
Cory LaViska
8e57d0075c update version 2022-08-09 16:14:25 -04:00
Cory LaViska
73c8eba269 remove padding from example 2022-08-09 16:10:09 -04:00
Cory LaViska
1c0ffa7a24 fix changelog 2022-08-09 16:06:28 -04:00
Cory LaViska
014354efde fix typo 2022-08-09 16:01:33 -04:00
Cory LaViska
13ec55ba07 simplify initial open behavior 2022-08-09 16:01:13 -04:00
Cory LaViska
d6508a1262 fix initial open state and add test 2022-08-09 16:00:56 -04:00
Cory LaViska
9ebb5c8ec7 remove error 2022-08-09 16:00:43 -04:00
Cory LaViska
2093568981 use popup in dropdown 2022-08-09 15:28:01 -04:00
Cory LaViska
bd5ca9034c fix example 2022-08-09 15:27:41 -04:00
Cory LaViska
7ae454f8ca add data-current-placement 2022-08-09 15:27:34 -04:00
Cory LaViska
b9fcd09209 fix placement styles 2022-08-09 15:27:08 -04:00
Cory LaViska
cf2915a591 add react examples 2022-08-09 12:49:01 -04:00
Cory LaViska
9e625752be fix tag 2022-08-09 11:19:12 -04:00
Cory LaViska
147d1f048b update examples 2022-08-09 11:00:04 -04:00
Cory LaViska
801c4f70ec add examples 2022-08-09 10:52:36 -04:00
Cory LaViska
bcc9081247 update changelog 2022-08-09 10:52:31 -04:00
Cory LaViska
3919ea97e9 move it 2022-08-09 10:52:25 -04:00
Cory LaViska
876078b725 remove redundant style 2022-08-09 10:52:11 -04:00
Cory LaViska
c765a1df9b fix attrib names and middleware order 2022-08-09 10:52:04 -04:00
Cory LaViska
a1bc784bc7 bigger checks 2022-08-08 15:44:57 -04:00
Cory LaViska
d14b9e12ed use popup in tooltip 2022-08-08 15:41:19 -04:00
Cory LaViska
ab0fdd66b4 add sl-reposition event; support <slot> anchors 2022-08-08 15:40:21 -04:00
Cory LaViska
bf730f5bd2 use display:contents 2022-08-08 11:10:53 -04:00
Cory LaViska
8c667791fa fix menu item alignment 2022-08-08 11:08:13 -04:00
Cory LaViska
d07a8cfaea fix import paths 2022-08-08 10:15:41 -04:00
Cory LaViska
23deda2253 refactor watch logic 2022-08-08 10:08:57 -04:00
Cory LaViska
1c6cf769d4 update example 2022-08-08 10:08:53 -04:00
Cory LaViska
2add23d5d2 add example template 2022-08-05 16:13:24 -04:00
Cory LaViska
26693b2256 looking good; needs docs 2022-08-05 16:11:39 -04:00
Cory LaViska
f31d13c424 add popup 2022-08-05 09:17:58 -04:00
Cory LaViska
59182db564 reorder method 2022-08-04 09:22:13 -04:00
Cory LaViska
1bae224f80 remove constant 2022-08-04 09:17:08 -04:00
Cory LaViska
9b3240a14f update examples 2022-08-04 08:23:50 -04:00
Cory LaViska
860224c894 use token 2022-08-03 15:57:40 -04:00
Cory LaViska
a8b2eb2bb0 fix styles 2022-08-03 15:26:54 -04:00
Cory LaViska
cea69beca9 update radio group, radio, radio buton 2022-08-03 11:55:24 -04:00
Cory LaViska
4b7da8f510 update docs 2022-08-03 11:06:52 -04:00
Cory LaViska
6d31b1d63d remove unused styles; fix focus 2022-08-03 10:56:46 -04:00
Cory LaViska
7b55b38aa4 remove unused property 2022-08-03 10:56:37 -04:00
Cory LaViska
bf1121d126 Merge branch 'break-stuff-radio-group-a11y-fixes' into next 2022-08-03 10:00:49 -04:00
Burton Smith
fb2d419ab0 fix react example 2022-08-02 13:20:02 -04:00
Burton Smith
7179b60499 update docs 2022-08-02 13:16:19 -04:00
Burton Smith
ad00d8840e run prettier 2022-08-02 13:10:26 -04:00
Burton Smith
3d9fd3b889 disable button group role when nested 2022-08-02 13:03:47 -04:00
Burton Smith
90f4d77ed6 fix screen reader issues for radios and group 2022-08-02 12:59:00 -04:00
Cory LaViska
36d1e7c52e fixes #845 2022-08-02 08:26:03 -04:00
Andreas
2a1895a125 🔨 fix missing kbd closing tag (#846) 2022-08-02 07:51:00 -04:00
Cory LaViska
d5a352465e 2.0.0-beta.79 2022-08-01 12:18:57 -04:00
Cory LaViska
088ab99d02 revert version 2022-08-01 12:18:54 -04:00
Cory LaViska
42ed7f5cbb 2.0.0-beta.80 2022-08-01 12:17:16 -04:00
Cory LaViska
66e8259421 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2022-08-01 12:08:33 -04:00
Cory LaViska
1f34b63a2e revert radio fixes 2022-08-01 12:08:31 -04:00
Cory LaViska
cf3b4fa501 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2022-08-01 12:01:46 -04:00
Cory LaViska
73c190217f Revert "fix event docs"
This reverts commit f5bcd03c3f.
2022-08-01 12:01:34 -04:00
Cory LaViska
b2986e89b0 Revert "update changelog"
This reverts commit 8b2ddc984b.
2022-08-01 12:01:30 -04:00
Cory LaViska
2bf68e8a32 revert 2022-08-01 12:00:30 -04:00
Cory LaViska
f392ab4270 2.0.0-beta.79 2022-08-01 11:43:04 -04:00
Cory LaViska
eefb8f6a6b update version 2022-08-01 11:42:52 -04:00
Cory LaViska
8b2ddc984b update changelog 2022-08-01 11:41:56 -04:00
Cory LaViska
f5bcd03c3f fix event docs 2022-08-01 11:41:51 -04:00
Cory LaViska
92a73f21eb Merge branch 'fix-radio-a11y' of github.com:break-stuff/shoelace into next 2022-08-01 11:28:38 -04:00
Cory LaViska
4dbc3d2c8f Revert "fix checked handling"
This reverts commit 98746c86e4.
2022-08-01 11:25:09 -04:00
Burton Smith
90aac9abe9 fix type-o 2022-08-01 10:38:58 -04:00
Burton Smith
589021c40f remove logs 2022-08-01 10:38:07 -04:00
Burton Smith
1cc65b145a fix form validation 2022-08-01 10:35:00 -04:00
Cory LaViska
f6fbde3c96 update order 2022-08-01 10:34:50 -04:00
Cory LaViska
caf25773f0 Merge branch 'feat/tree-view' of github.com:alenaksu/shoelace into next 2022-08-01 10:21:53 -04:00
Alessandro
b904bb0736 chore: update styles 2022-08-01 13:58:24 +00:00
Cory LaViska
28ecc90e7f prettier 2022-08-01 09:28:47 -04:00
Cory LaViska
72fb9c7293 update changelog 2022-08-01 09:25:50 -04:00
Denis Korablev
d69712530c fix: fix dropdown hoist position (#844) 2022-08-01 09:24:11 -04:00
Alessandro
71a39600f5 chore: update docs 2022-08-01 12:51:14 +00:00
Alessandro
0291d7c28d chore: minor refactor 2022-08-01 12:48:02 +00:00
Alessandro
ed2417d974 feat: add customizable icons
* fix a bug focusing collapsed nodes
2022-08-01 12:41:36 +00:00
Burton Smith
85fd8a5204 update checked radio when value changes 2022-07-29 13:13:54 -04:00
Burton Smith
6446bb1013 prevent form submission 2022-07-29 12:31:29 -04:00
Burton Smith
2f8852245e update docs to reflect new API 2022-07-29 12:20:16 -04:00
Burton Smith
401b46bad8 update label 2022-07-29 12:20:00 -04:00
Burton Smith
ae3efaf8ae fix focus styles 2022-07-29 11:03:49 -04:00
Cory LaViska
98746c86e4 fix checked handling 2022-07-29 08:25:36 -04:00
Alessandro
268aef1711 fix: collapsed nodes are still focusable 2022-07-28 18:01:43 +00:00
Burton Smith
d765cef376 move validation logic and value to group level 2022-07-28 13:00:43 -04:00
Cory LaViska
23ed3d5647 add RTL info 2022-07-28 09:16:37 -04:00
Cory LaViska
1a7fbbfab4 fixes #839 2022-07-27 16:45:39 -04:00
Cory LaViska
47388d4a3f update deps 2022-07-27 16:17:23 -04:00
Cory LaViska
ce09869ea2 various updates 2022-07-26 15:53:24 -04:00
Cory LaViska
33587f51d3 Merge branch 'feat/tree-view' of github.com:alenaksu/shoelace into alenaksu-feat/tree-view 2022-07-26 08:17:41 -04:00
Cory LaViska
ed76d8aecc update changelog 2022-07-26 08:04:02 -04:00
Daniel Dennerkrans
33d2ec0597 added swedish translation (#838) 2022-07-26 08:03:02 -04:00
Alessandro
b4ae6ed1aa chore: update docs 2022-07-24 15:12:57 +00:00
Alessandro
b6839254d4 feat: add new tree-view component
feat: add indeterminate state

fix: rename item role

fix: aria attributes

fix: restore hover effect

fix: indeterminate state

chore: fix lint issue

fix: address (partially) review comment

fix: minor fix

feat: add keyboard navigation

fix: dependency name

fix: keyboard selection does not update all items

chore: minor changes

chore: remove leftover

chore: update documentation

feat: add lazy loading + several fixes

fix: add aria-busy attribute

fix: improve keyboard navigation and lazy loading

chore: fix linting issue

chore: minor fixes

fix: update component styling and slot

chore: remove exportparts attribute

fix: remove button margin for safari

fix: set correct part name

feat: implement selection modes and fix accessibility issues

chore: fix linting issues

chore: update docs

fix: minor fix

fix: treeitem height style

chore: update documentation

chore: refactor implementation

chore: implement unit tests

chore: update Enter key behavior

chore: update doc

chore: update doc
2022-07-23 15:16:17 +00:00
Cory LaViska
8ee811af58 fixes #837 2022-07-21 15:29:58 -04:00
Cory LaViska
f34ffdac55 fix color selection 2022-07-21 10:12:29 -04:00
Cory LaViska
d7e7ff6101 fix tooltip hoist position 2022-07-21 10:08:04 -04:00
Cory LaViska
1c36a7bcd4 remove void 2022-07-21 10:01:30 -04:00
Cory LaViska
7a77be017e improve menu a11y 2022-07-20 16:53:59 -04:00
Cory LaViska
5b5c6710fe add roles 2022-07-20 16:46:14 -04:00
Cory LaViska
04f7d2e182 use connectedCallback 2022-07-20 16:46:06 -04:00
Cory LaViska
36ee3c8a70 don't use getTarget() 2022-07-20 15:41:46 -04:00
Cory LaViska
98eec84422 fix tests 2022-07-20 15:37:47 -04:00
Burton Smith
bc08a4c005 run prettier 2022-07-20 15:34:10 -04:00
Cory LaViska
41580992f6 fix focus styles 2022-07-20 15:33:50 -04:00
Cory LaViska
9b7ce98ec0 fix disabled + focus styles 2022-07-20 15:32:26 -04:00
Cory LaViska
864d567572 improve tab accessibility 2022-07-20 15:29:19 -04:00
Burton Smith
cf360b3b3f fix radio a11y 2022-07-20 13:55:12 -04:00
Cory LaViska
673dc29dfe update changelog 2022-07-20 10:46:29 -04:00
Steven Traversi
849b643cfc Tooltip: Recalculate target after slotchange (#831)
* Tooltip: Recalculate target after slotchange

The tooltip's positioning breaks after a slotchange, unless the tooltips target is recalculated.

* Update src/components/tooltip/tooltip.test.ts

Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2022-07-20 10:43:47 -04:00
Cory LaViska
c39c4a9e9f update floating ui to 1.0.0 2022-07-20 08:28:16 -04:00
Cory LaViska
92c36c65cb update text 2022-07-19 09:39:51 -04:00
Cory LaViska
b6f25e09d2 fixes #815 2022-07-19 09:34:42 -04:00
Cory LaViska
71119c963d use classMap instead of inline style 2022-07-19 09:06:24 -04:00
Cory LaViska
ecf5ab5aad use classMap instead of inline style 2022-07-19 09:06:12 -04:00
Cory LaViska
7e581e6ad7 fix tab divider 2022-07-19 09:05:22 -04:00
Cory LaViska
36fa84e4b0 update changelog 2022-07-19 08:33:39 -04:00
Philipp Sonntag
3fb4cba856 Added type declaration to styles property for all components (#829)
* Updated the plop template with type for styles property
2022-07-19 08:27:39 -04:00
Cory LaViska
00f98cc505 update icon sprite 2022-07-18 08:41:48 -04:00
Cory LaViska
d9f719196a update bootstrap-icons 2022-07-18 08:18:01 -04:00
Cory LaViska
4b0c7245bd update sl localize 2022-07-15 12:16:43 -04:00
Cory LaViska
ced3a2a45a 2.0.0-beta.78 2022-07-15 10:00:20 -04:00
Cory LaViska
e6838e0a1f update version 2022-07-15 09:59:49 -04:00
Cory LaViska
9e637ce0db add shameless plug 2022-07-15 09:35:59 -04:00
Cory LaViska
acbee743a0 update deps 2022-07-14 09:11:16 -04:00
Cory LaViska
fa179fa30b fixes #824 2022-07-11 09:18:29 -04:00
Cory LaViska
2376f75f1d remove code blocks from search results 2022-07-11 09:13:57 -04:00
Cory LaViska
ca95822bba than than 2022-07-08 08:37:23 -04:00
Cory LaViska
6fbcd2158a add note about ai-generated code 2022-07-08 08:36:33 -04:00
Cory LaViska
389b78f748 remove :focus-visible shim 2022-07-07 09:43:27 -04:00
Cory LaViska
99368b9fdd fixes #821 2022-07-06 09:48:30 -04:00
Cory LaViska
a5a4621e25 update changelog 2022-07-06 08:39:11 -04:00
Cory LaViska
fe95ebaa95 Merge branch 'alenaksu-feat/range/active-track-offset' into next 2022-07-06 08:37:07 -04:00
Alessandro
01c7ca27fe feat(range): add active track offset 2022-07-06 09:27:37 +00:00
Cory LaViska
96ab3146be fixes #814 2022-07-05 08:58:11 -04:00
Cory LaViska
3c920cfed2 update text 2022-07-01 20:33:26 -04:00
Oliver Salzburg
db7caf1997 docs: Note that releases are not on a schedule (#810) 2022-07-01 20:25:24 -04:00
Oliver Salzburg
808815bdab docs: Fix duplicate package in list (#809) 2022-07-01 20:24:28 -04:00
Cory LaViska
8a8fd7f5a9 break it 2022-06-29 08:10:48 -04:00
Cory LaViska
3b3cb6d61d fixes #786 2022-06-29 08:09:25 -04:00
Cory LaViska
30a45f1d14 2.0.0-beta.77 2022-06-28 18:15:42 -04:00
Cory LaViska
d10f628f1d update version 2022-06-28 18:15:18 -04:00
Cory LaViska
615da18dc9 update changelog 2022-06-28 18:08:35 -04:00
Cory LaViska
be11c13f67 fix sort order 2022-06-28 18:08:28 -04:00
Cory LaViska
cb7f0aa41e webkit doesn't focus ranges like inputs 2022-06-28 18:01:55 -04:00
Alessandro
b2cf3a5505 feat(form): add reset functionality (#799)
* feat(form): add reset functionality

* feat(interal): add defaultValue decorator

* feat: add defaultValue and defaultChecked

* chore: implement unit tests

* chore: remove leftover
2022-06-28 17:59:52 -04:00
Cory LaViska
0d19c46d18 update changelog 2022-06-24 09:09:20 -04:00
Cory LaViska
c05832db67 Merge branch 'PavelDymkov-tabbable-fix' into next 2022-06-24 09:03:55 -04:00
Cory LaViska
e76dbef5f5 Merge branch 'tabbable-fix' of github.com:PavelDymkov/shoelace into PavelDymkov-tabbable-fix 2022-06-24 09:01:39 -04:00
Cory LaViska
25c00c80b7 fix label color 2022-06-24 08:46:44 -04:00
Pavel Dymkov
012206e4d8 Algorithm fix: fill "allElements" with only unique elements to improve performance. 2022-06-24 15:37:38 +03:00
Cory LaViska
de9da437f1 fix password autocomplete/correct/capitalize 2022-06-24 08:36:51 -04:00
Cory LaViska
153fe15ed3 fix label alignment 2022-06-24 08:22:13 -04:00
Cory LaViska
b58374aff1 fixes #798 2022-06-23 16:57:30 -04:00
Cory LaViska
31ae084538 fixes #797 2022-06-23 16:34:59 -04:00
Cory LaViska
f980126e81 fixes #796 2022-06-22 09:18:34 -04:00
Cory LaViska
cb1ada1bd7 Merge branch 'Buni48-next' into next 2022-06-22 09:03:10 -04:00
Cory LaViska
200d340123 Merge branch 'next' of github.com:Buni48/shoelace into Buni48-next 2022-06-22 08:57:36 -04:00
Buni48
5c2f4dd84e Revert "Fixes #791"
This reverts commit 1c23daa3b1.
2022-06-21 17:26:13 +02:00
Buni48
953d175b44 Revert "fixed typescript error"
This reverts commit 9549539f5d.
2022-06-21 17:25:13 +02:00
Buni48
f52a463728 merge next 2022-06-21 17:21:54 +02:00
Cory LaViska
5f25049abc fixes #791 2022-06-21 09:53:48 -04:00
Cory LaViska
8edaf67197 add checked-icon part 2022-06-21 09:37:16 -04:00
Cory LaViska
41c1979283 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2022-06-21 09:21:41 -04:00
Cory LaViska
9840891cdc update changelog 2022-06-21 09:21:36 -04:00
Cory LaViska
7d22e18bfb reflect fieldset and add required 2022-06-21 09:21:33 -04:00
Cory LaViska
07af6f2741 whitespace 2022-06-21 09:21:11 -04:00
Cory LaViska
60d9819088 add required symbol 2022-06-21 09:20:58 -04:00
Buni48
9549539f5d fixed typescript error 2022-06-19 19:40:50 +02:00
Buni48
1c23daa3b1 Fixes #791 2022-06-19 18:37:38 +02:00
Buni48
4a6c37ae0c Fixes #793 2022-06-19 18:22:52 +02:00
Oliver Salzburg
f1bfd58dd2 chore: Add missing colons (#792) 2022-06-18 17:13:03 -04:00
Cory LaViska
4446814114 2.0.0-beta.76 2022-06-15 09:27:32 -04:00
Cory LaViska
1ae6236e90 update version 2022-06-15 09:27:17 -04:00
Cory LaViska
624a8bbe71 fixes #777 2022-06-15 09:12:01 -04:00
Cory LaViska
ca876b291a revert formdata event detection 2022-06-10 17:22:34 -04:00
Cory LaViska
c850a7eae1 improve formdata event detection 2022-06-10 16:52:31 -04:00
Cory LaViska
5613a3cef3 upgrade localization lib 2022-06-10 16:37:10 -04:00
Cory LaViska
d3b161fc25 fix border bug 2022-06-10 09:44:27 -04:00
Cory LaViska
7659e45cc1 fix border bug 2022-06-10 09:44:22 -04:00
Cory LaViska
8a28d66393 fixes #784 2022-06-09 18:14:38 -04:00
Cory LaViska
c8d92e41b2 add link 2022-06-09 17:22:25 -04:00
Cory LaViska
9b39c90849 improve toast stack RTL styles 2022-06-09 17:21:44 -04:00
Cory LaViska
5c8a34696e revert dir 2022-06-09 09:24:11 -04:00
Cory LaViska
3e5da7c25a fixes #783 2022-06-09 09:23:33 -04:00
Cory LaViska
b1e6770712 2.0.0-beta.75 2022-06-08 17:24:52 -04:00
Cory LaViska
fd49fd6456 update version 2022-06-08 17:24:18 -04:00
Cory LaViska
d0ff2fef35 fix RTL in breadcrumb, tab group, and split panel 2022-06-08 17:15:40 -04:00
Cory LaViska
4c3313e275 improve badge RTL styles 2022-06-07 13:27:42 -04:00
Cory LaViska
4e0bc36b02 improve RTL in rating 2022-06-07 13:09:14 -04:00
Cory LaViska
01eb84e3a6 fix indeterminate state in RTL 2022-06-07 10:37:33 -04:00
Cory LaViska
70c97e2ae4 add --track-width; fix rtl 2022-06-07 10:26:04 -04:00
Cory LaViska
c165c8e71f update localize 2022-06-07 10:04:06 -04:00
Cory LaViska
6dd9773f2c update Floating UI 2022-06-07 09:27:15 -04:00
Cory LaViska
15dbb0a634 defer dom parser instantiation 2022-06-07 09:08:54 -04:00
Cory LaViska
ce09ac2a92 update base path 2022-06-07 08:59:29 -04:00
Cory LaViska
425f936254 fixes #781 2022-06-06 17:14:50 -04:00
Cory LaViska
7a9e4b0e8f fixes #776 2022-06-06 17:01:27 -04:00
xdev1
48f10011e1 Fixed translations for 'de' and 'de-CH' (#779) 2022-06-06 11:07:53 -04:00
Cory LaViska
f6d3f799dd update localize and fix range RTL 2022-06-06 10:57:59 -04:00
Cory LaViska
2157f4a385 update changelog 2022-06-02 08:17:12 -04:00
S. MohammadMahdi Zamanian
0f76d05546 add Persian translation
* feat: fa translate

* fix: fa localize name
2022-06-02 08:16:11 -04:00
Cory LaViska
293f49e178 fixes #775 2022-06-02 08:11:51 -04:00
Cory LaViska
4d2de2dd57 fixes #775 2022-06-02 08:10:46 -04:00
Cory LaViska
46dc965cd0 update changelog 2022-06-01 10:18:25 -04:00
Cory LaViska
707aeb6d65 revert button group RTL styles due to breakage 2022-06-01 10:17:36 -04:00
Godgiven
96c63c60a2 refactor: changed style to minimum support of RTL language (#768) 2022-06-01 10:00:46 -04:00
Cory LaViska
9539123fc3 improve image comparer icon 2022-06-01 09:36:16 -04:00
Cory LaViska
ea07346ae6 change weakmap name 2022-06-01 09:27:16 -04:00
Cory LaViska
af1e440103 fixes #772 2022-06-01 09:23:06 -04:00
Cory LaViska
0cea7d23f0 run prettier 2022-06-01 08:15:36 -04:00
Corbin Crutchley
ea8c88a31a docs: Update NextJS guide with ESM instructions (#771) 2022-06-01 07:43:39 -04:00
Cory LaViska
b4e5544ff8 fixes #767 2022-05-31 08:41:06 -04:00
Cory LaViska
04d534cd30 fixes #766 2022-05-31 08:31:41 -04:00
Cory LaViska
7cb247976f 2.0.0-beta.74 2022-05-27 15:06:45 -04:00
Cory LaViska
3cbf9e14e6 update version 2022-05-27 15:04:00 -04:00
Majid Valipour
3bd6516440 Fix broken link design tokens in docs (#733)
`light.styles.ts` no longer exists and has been replaced with `light.css`
2022-05-27 14:44:55 -04:00
Cory LaViska
e3f691fbda remove variant 2022-05-27 14:38:42 -04:00
Cory LaViska
ae76bea220 improve code block hover styles 2022-05-27 14:38:30 -04:00
Cory LaViska
ca81a507b6 rework focus rings 2022-05-27 10:13:51 -04:00
Cory LaViska
3f2382cfdc update vue instructions 2022-05-27 09:45:14 -04:00
Oleg Voronkovich
c1ccae315f Add instructions for Vue 3 (#756) 2022-05-27 09:41:47 -04:00
Cory LaViska
92cb4e3d29 move warning 2022-05-27 08:29:03 -04:00
Cory LaViska
9dd8c45c57 update deps 2022-05-27 08:23:02 -04:00
Cory LaViska
fa84a84a40 fixes #764 2022-05-27 08:15:31 -04:00
Cory LaViska
9e747e7c2e fixes #765 2022-05-27 07:43:42 -04:00
Cory LaViska
79306e0618 use HSB grid for color picker; fixes #762 2022-05-26 17:01:29 -04:00
Cory LaViska
139073dc3e fix word 2022-05-26 07:42:27 -04:00
Cory LaViska
18d441ef2e cache menu items 2022-05-25 09:53:19 -04:00
Cory LaViska
e07058ef5a add tablericons 2022-05-25 09:27:20 -04:00
Cory LaViska
f02941445b fixes #760 2022-05-25 08:56:22 -04:00
Cory LaViska
37b172dbfd fixes #758 2022-05-24 10:34:47 -04:00
Cory LaViska
4b2fc37015 update changelog 2022-05-24 09:00:34 -04:00
Cory LaViska
ae219c83fb fixes #743 2022-05-24 09:00:28 -04:00
Cory LaViska
e60c5c4546 update eslint 2022-05-24 08:12:13 -04:00
Cory LaViska
f57adb33eb update deps 2022-05-24 07:54:33 -04:00
Alan Chambers
25821c1294 Added datetime-local as valid sl-input type (#753) 2022-05-11 15:54:37 -04:00
Cory LaViska
8904e9d709 #744 2022-05-11 10:48:06 -04:00
Cory LaViska
35885ef59f fixes #744 2022-05-11 10:34:40 -04:00
Cory LaViska
c19eb5847a #751 2022-05-11 10:16:37 -04:00
Pavel Dymkov
c591f1d23e Fix error when set null or undefined to SlInput.value (#751) 2022-05-11 10:14:54 -04:00
Cory LaViska
fd1f76169a update changelog 2022-05-11 08:34:19 -04:00
Buni48
8814746738 Prevent toggling password & clearing on disabled form controls (#746)
* prevent toggling password & clearing on disabled form controls

* hide clear icon also on readonly
2022-05-11 08:33:27 -04:00
Cory LaViska
7bb6c4f0c1 update message 2022-05-11 08:27:00 -04:00
Cory LaViska
6c14282223 add base path note 2022-05-11 08:25:27 -04:00
Cory LaViska
7333760ada ignore polyfill types 2022-05-11 08:17:27 -04:00
Cory LaViska
cb0b5feef8 prettier 2022-05-11 08:12:02 -04:00
Cory LaViska
01369464fa Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2022-05-11 08:10:57 -04:00
Cory LaViska
c2ceaa9a3f update changelog 2022-05-11 08:10:56 -04:00
Cory LaViska
36716508c3 fix test type 2022-05-11 08:10:47 -04:00
Jared White
d683a76a49 Ensure FormDataPolyfill arg is optional (#749)
Fixes #747
2022-05-11 08:08:53 -04:00
Cory LaViska
4a0f6ef8af reflect disabled in dropdown 2022-05-02 16:33:30 -04:00
Cory LaViska
92b735b7fa update changelog 2022-05-02 16:28:21 -04:00
Buni48
240db01e75 reflecting name and library in sl-icon (#742) 2022-05-02 16:26:37 -04:00
Cory LaViska
04bacccfea use forEach instead of map; fixes #740 2022-05-02 07:48:43 -04:00
Cory LaViska
25b6e8c2d7 fix typo 2022-05-02 07:48:25 -04:00
Cory LaViska
1e2ceb8252 prettier 2022-04-26 08:13:13 -04:00
Buni48
329a5aec32 added time as valid sl-input type (#736) 2022-04-26 08:09:54 -04:00
Cory LaViska
c86ae623cb fixes #732 2022-04-25 08:00:16 -04:00
Cory LaViska
ce8dbc4302 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2022-04-19 09:30:20 -04:00
Cory LaViska
72972c76af fixes #730 2022-04-19 09:30:17 -04:00
modmuss50
d55813fffb Fix placeholder capitalisation (#731) 2022-04-19 08:55:00 -04:00
Cory LaViska
1f92e96079 add comma dangle rule 2022-04-15 09:28:09 -04:00
Cory LaViska
2cb3c1fc9f add env 2022-04-15 09:26:36 -04:00
Cory LaViska
caaf2b0d1e fixes #729 2022-04-14 18:01:25 -04:00
Cory LaViska
a635e4cd30 gtfo 2022-04-14 17:33:32 -04:00
Cory LaViska
aa336fb525 make touchstart passive 2022-04-14 16:58:08 -04:00
Cory LaViska
c0dc08116c remove unnecessary bind 2022-04-14 15:28:43 -04:00
Cory LaViska
a5b3334222 remove rules 2022-04-13 10:59:24 -04:00
Cory LaViska
8fd2491b9c use visually hidden tag 2022-04-13 10:29:27 -04:00
410 changed files with 41488 additions and 18413 deletions

View File

@@ -1,4 +1,4 @@
# http://editorconfig.org
# https://editorconfig.org
root = true

View File

@@ -1,7 +1,16 @@
/* eslint-env node */
module.exports = {
plugins: ['@typescript-eslint', 'wc', 'lit', 'lit-a11y', 'chai-expect', 'chai-friendly', 'import'],
plugins: [
'@typescript-eslint',
'wc',
'lit',
'lit-a11y',
'chai-expect',
'chai-friendly',
'import',
'sort-imports-es6-autofix'
],
extends: [
'eslint:recommended',
'plugin:wc/recommended',
@@ -13,7 +22,6 @@ module.exports = {
es2021: true,
browser: true
},
reportUnusedDisableDirectives: true,
parserOptions: {
sourceType: 'module'
},
@@ -74,7 +82,6 @@ module.exports = {
'@typescript-eslint/non-nullable-type-assertion-style': 'warn',
'@typescript-eslint/prefer-for-of': 'warn',
'@typescript-eslint/prefer-optional-chain': 'warn',
'@typescript-eslint/prefer-readonly': 'warn',
'@typescript-eslint/prefer-ts-expect-error': 'warn',
'@typescript-eslint/prefer-return-this-type': 'error',
'@typescript-eslint/prefer-string-starts-ends-with': 'warn',
@@ -84,39 +91,17 @@ module.exports = {
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
'@typescript-eslint/member-delimiter-style': 'warn',
'@typescript-eslint/method-signature-style': 'warn',
'@typescript-eslint/naming-convention': [
'warn',
{
selector: 'default',
format: ['camelCase']
},
{
selector: ['function', 'enumMember', 'property'],
format: ['camelCase', 'PascalCase']
},
{
selector: 'variable',
modifiers: ['const'],
format: ['camelCase', 'PascalCase', 'UPPER_CASE']
},
{
selector: 'typeLike',
format: ['PascalCase']
},
{
selector: 'typeProperty',
format: ['camelCase', 'PascalCase', 'UPPER_CASE']
},
{
selector: 'objectLiteralProperty',
format: null
}
],
'@typescript-eslint/no-extraneous-class': 'error',
'@typescript-eslint/no-parameter-properties': 'error',
'@typescript-eslint/strict-boolean-expressions': 'off'
}
},
{
files: ['**/*.cjs'],
env: {
node: true
}
},
{
extends: ['plugin:chai-expect/recommended', 'plugin:chai-friendly/recommended'],
files: ['*.test.ts'],
@@ -129,10 +114,12 @@ module.exports = {
rules: {
'no-template-curly-in-string': 'error',
'array-callback-return': 'error',
'comma-dangle': 'off',
'consistent-return': 'error',
curly: 'off',
'default-param-last': 'error',
eqeqeq: 'error',
'lit-a11y/click-events-have-key-events': 'off',
'no-constructor-return': 'error',
'no-empty-function': 'warn',
'no-eval': 'error',
@@ -177,28 +164,12 @@ module.exports = {
'prefer-object-spread': 'warn',
'prefer-rest-params': 'warn',
'prefer-spread': 'warn',
'prefer-template': 'warn',
'no-else-return': 'warn',
'prefer-template': 'off',
'no-else-return': 'off',
'func-names': ['warn', 'never'],
'func-style': ['warn', 'declaration'],
'one-var': ['warn', 'never'],
'operator-assignment': 'warn',
'prefer-arrow-callback': 'warn',
'no-restricted-syntax': [
'warn',
{
selector: "CallExpression[callee.name='String']",
message: "Don't use the String function. Use .toString() instead."
},
{
selector: "CallExpression[callee.name='Number']",
message: "Don't use the Number function. Use parseInt or parseFloat instead."
},
{
selector: "CallExpression[callee.name='Boolean']",
message: "Don't use the Boolean function. Use a strict comparison instead."
}
],
'no-restricted-imports': [
'warn',
{
@@ -215,22 +186,12 @@ module.exports = {
}
],
'import/no-duplicates': 'warn',
'import/order': [
'warn',
'sort-imports-es6-autofix/sort-imports-es6': [
2,
{
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
ignoreCase: true,
ignoreMemberSort: false,
memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single']
}
],
'wc/guard-super-call': 'off'

View File

@@ -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
View File

@@ -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.

View File

@@ -15,13 +15,13 @@ jobs:
strategy:
matrix:
node-version: [14.x, 16.x]
node-version: [18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

7
.gitignore vendored
View File

@@ -1,8 +1,7 @@
.DS_Store
_site
.cache
docs/dist
docs/search.json
.DS_Store
dist
examples
docs/assets/images/sprite.svg
node_modules
src/react

View File

@@ -51,9 +51,7 @@ Once you've cloned the repo, run the following command.
npm start
```
This will spin up the Shoelace dev server. After the initial build, a browser will open automatically. There is currently no hot module reloading (HMR), as browser's don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically.
The documentation is powered by Docsify, which uses raw markdown files to generate pages. As such, no static files are built for the docs.
This will spin up the dev server. After the initial build, a browser will open automatically. There is currently no hot module reloading (HMR), as browser's don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically.
### Building

View File

@@ -8,19 +8,30 @@
"apos",
"atrule",
"autocorrect",
"autofix",
"autoload",
"autoloader",
"autoloading",
"autoplay",
"bezier",
"boxicons",
"CACHEABLE",
"callout",
"callouts",
"chatbubble",
"checkmark",
"claviska",
"Clippy",
"codebases",
"codepen",
"colocated",
"colour",
"combobox",
"Composability",
"Consolas",
"contenteditable",
"copydir",
"Cotte",
"coverpage",
"crossorigin",
"crutchcorn",
@@ -29,22 +40,26 @@
"datetime",
"describedby",
"Docsify",
"dogfood",
"dropdowns",
"easings",
"enterkeyhint",
"eqeqeq",
"erroneou",
"errormessage",
"esbuild",
"exportparts",
"fieldsets",
"formaction",
"formdata",
"formenctype",
"formmethod",
"formnovalidate",
"formtarget",
"FOUC",
"FOUCE",
"fullscreen",
"gestern",
"giga",
"globby",
"Grayscale",
@@ -62,20 +77,28 @@
"jsonata",
"keydown",
"keyframes",
"Kool",
"labelledby",
"Laravel",
"LaViska",
"linkify",
"listbox",
"listitem",
"litelement",
"lowercasing",
"Lucide",
"maxlength",
"Menlo",
"menuitemcheckbox",
"menuitemradio",
"middlewares",
"minlength",
"monospace",
"mousedown",
"mouseup",
"multiselectable",
"nextjs",
"nocheck",
"noopener",
"noreferrer",
"novalidate",
@@ -83,6 +106,7 @@
"ParamagicDev",
"peta",
"petabit",
"prismjs",
"progressbar",
"radiogroup",
"Railsbyte",
@@ -90,26 +114,41 @@
"reregister",
"resizer",
"resizers",
"retargeted",
"RETRYABLE",
"rgba",
"roadmap",
"Roboto",
"roledescription",
"Sapan",
"saturationl",
"Schilp",
"scrollbars",
"scrollend",
"scroller",
"Segoe",
"semibold",
"sitedir",
"slotchange",
"smartquotes",
"spacebar",
"stylesheet",
"Tabbable",
"tabindex",
"tabler",
"tablist",
"tabpanel",
"templating",
"tera",
"testid",
"textareas",
"textfield",
"tinycolor",
"transitionend",
"treeitem",
"Triaging",
"turbolinks",
"typeof",
"unbundles",
"unbundling",
"unicons",
@@ -124,6 +163,7 @@
"ignorePaths": [
"package.json",
"package-lock.json",
"docs/assets/examples/include.html",
".vscode/**",
"src/translations/!(en).ts",
"**/*.min.js"

View File

@@ -1,11 +1,29 @@
import fs from 'fs';
import { generateCustomData } from 'cem-plugin-vs-code-custom-data-generator';
import { parse } from 'comment-parser';
import { pascalCase } from 'pascal-case';
import commandLineArgs from 'command-line-args';
import fs from 'fs';
const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
const { name, description, version, author, homepage, license } = packageData;
// eslint-disable-next-line func-style
const noDash = string => string.replace(/^\s?-/, '').trim();
const { outdir } = commandLineArgs([
{ name: 'litelement', type: String },
{ name: 'analyze', defaultOption: true },
{ name: 'outdir', type: String }
]);
function noDash(string) {
return string.replace(/^\s?-/, '').trim();
}
function replace(string, terms) {
terms.forEach(({ from, to }) => {
string = string?.replace(from, to);
});
return string;
}
export default {
globs: ['src/components/**/*.ts'],
@@ -27,7 +45,7 @@ export default {
case ts.SyntaxKind.ClassDeclaration: {
const className = node.name.getText();
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
const customTags = ['animation', 'dependency', 'since', 'status'];
const customTags = ['animation', 'dependency', 'documentation', 'since', 'status', 'title'];
let customComments = '/**';
node.jsDoc?.forEach(jsDoc => {
@@ -63,8 +81,10 @@ export default {
break;
// Value-only metadata tags
case 'documentation':
case 'since':
case 'status':
case 'title':
classDoc[t.tag] = t.name;
break;
@@ -85,7 +105,6 @@ export default {
}
}
},
{
name: 'shoelace-react-event-names',
analyzePhase({ ts, node, moduleDoc }) {
@@ -102,6 +121,47 @@ export default {
}
}
}
}
},
{
name: 'shoelace-translate-module-paths',
packageLinkPhase({ customElementsManifest }) {
customElementsManifest?.modules?.forEach(mod => {
//
// CEM paths look like this:
//
// src/components/button/button.ts
//
// But we want them to look like this:
//
// components/button/button.js
//
const terms = [
{ from: /^src\//, to: '' }, // Strip the src/ prefix
{ from: /\.(t|j)sx?$/, to: '.js' } // Convert .ts to .js
];
mod.path = replace(mod.path, terms);
for (const ex of mod.exports ?? []) {
ex.declaration.module = replace(ex.declaration.module, terms);
}
for (const dec of mod.declarations ?? []) {
if (dec.kind === 'class') {
for (const member of dec.members ?? []) {
if (member.inheritedFrom) {
member.inheritedFrom.module = replace(member.inheritedFrom.module, terms);
}
}
}
}
});
}
},
// Generate custom VS Code data
generateCustomData({
outdir,
cssFileName: null
})
]
};

View File

@@ -1,5 +0,0 @@
# Not Found
<img class="not-found-image" src="/assets/images/undraw-not-found.svg" alt="Cute monsters hiding behind a tree">
Sorry, I couldn't find that page. Have you tried pressing <kbd>/</kbd> to search?

View File

@@ -0,0 +1,347 @@
{% extends "default.njk" %}
{# Find the component based on the `tag` front matter #}
{% set component = getComponent('sl-' + page.fileSlug) %}
{% block content %}
{# Determine the badge variant #}
{% if component.status == 'stable' %}
{% set badgeVariant = 'primary' %}
{% elseif component.status == 'experimental' %}
{% set badgeVariant = 'warning' %}
{% elseif component.status == 'planned' %}
{% set badgeVariant = 'neutral' %}
{% elseif component.status == 'deprecated' %}
{% set badgeVariant = 'danger' %}
{% else %}
{% set badgeVariant = 'neutral' %}
{% endif %}
{# Header #}
<header class="component-header">
<h1>{{ component.name | classNameToComponentName }}</h1>
<div class="component-header__tag">
<code>&lt;{{ component.tagName }}&gt; | {{ component.name }}</code>
</div>
<div class="component-header__info">
<sl-badge variant="neutral" pill>
Since {{component.since or '?' }}
</sl-badge>
<sl-badge variant="{{ badgeVariant }}" pill style="text-transform: capitalize;">
{{ component.status }}
</sl-badge>
</div>
</header>
<p class="component-summary">
{% if component.summary %}
{{ component.summary | markdownInline | safe }}
{% endif %}
</p>
{# Markdown content #}
{{ content | safe }}
{# Importing #}
<h2>Importing</h2>
<p>
If you're using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use
any of the following snippets to <a href="/getting-started/installation#cherry-picking">cherry pick</a> this component.
</p>
<sl-tab-group>
<sl-tab slot="nav" panel="script">Script</sl-tab>
<sl-tab slot="nav" panel="import">Import</sl-tab>
<sl-tab slot="nav" panel="bundler">Bundler</sl-tab>
<sl-tab slot="nav" panel="react">React</sl-tab>
<sl-tab-panel name="script">
<p>
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
using a script tag:
</p>
<pre><code class="language-html">&lt;script type=&quot;module&quot; src=&quot;https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/dist/{{ component.path }}&quot;&gt;&lt;/script&gt;</code></pre>
</sl-tab-panel>
<sl-tab-panel name="import">
<p>
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
using a JavaScript import:
</p>
<pre><code class="language-js">import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/dist/{{ component.path }}';</code></pre>
</sl-tab-panel>
<sl-tab-panel name="bundler">
<p>
To import this component using <a href="{{ rootUrl('/getting-started/installation#bundling') }}">a bundler</a>:
</p>
<pre><code class="language-js">import '@shoelace-style/shoelace/dist/{{ component.path }}';</code></pre>
</sl-tab-panel>
<sl-tab-panel name="react">
<p>
To import this component as a <a href="/frameworks/react">React component</a>:
</p>
<pre><code class="language-js">import { {{ component.name }} } from '@shoelace-style/shoelace/dist/react';</code></pre>
</sl-tab-panel>
</sl-tab-group>
{# Slots #}
{% if component.slots.length %}
<h2>Slots</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for slot in component.slots %}
<tr>
<td class="nowrap">
{% if slot.name %}
<code>{{ slot.name }}</code>
{% else %}
(default)
{% endif %}
</td>
<td>{{ slot.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#slots') }}">using slots</a>.</em></p>
{% endif %}
{# Properties #}
{% if component.properties.length %}
<h2>Properties</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-reflects">Reflects</th>
<th class="table-type">Type</th>
<th class="table-default">Default</th>
</tr>
</thead>
<tbody>
{% for prop in component.properties %}
<tr>
<td>
<code class="nowrap">{{ prop.name }}</code>
{% if prop.attribute != prop.name %}
<br>
<sl-tooltip content="This attribute is different from its property">
<small>
<code class="nowrap">
{{ prop.attribute }}
</code>
</small>
</sl-tooltip>
{% endif %}
</td>
<td>
{{ prop.description | markdownInline | safe }}
</td>
<td style="text-align: center;">
{% if prop.reflects %}
<sl-icon label="yes" name="check-lg"></sl-icon>
{% endif %}
</td>
<td>
{% if prop.type.text %}
<code>{{ prop.type.text | markdownInline | safe }}</code>
{% else %}
-
{% endif %}
</td>
<td>
{% if prop.default %}
<code>{{ prop.default | markdownInline | safe }}</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
<tr>
<td class="nowrap"><code>updateComplete</code></td>
<td>
A read-only promise that resolves when the component has
<a href="/getting-started/usage?id=component-rendering-and-updating">finished updating</a>.
</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#properties') }}">attributes and properties</a>.</em></p>
{% endif %}
{# Events #}
{% if component.events.length %}
<h2>Events</h2>
<table>
<thead>
<tr>
<th class="table-name" data-flavor="html">Name</th>
<th class="table-name" data-flavor="react">React Event</th>
<th class="table-description">Description</th>
<th class="table-event-detail">Event Detail</th>
</tr>
</thead>
<tbody>
{% for event in component.events %}
<tr>
<td data-flavor="html"><code class="nowrap">{{ event.name }}</code></td>
<td data-flavor="react"><code class="nowrap">{{ event.reactName }}</code></td>
<td>{{ event.description | markdownInline | safe }}</td>
<td>
{% if event.type.text %}
<code>{{ event.type.text }}</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#events') }}">events</a>.</em></p>
{% endif %}
{# Methods #}
{% if component.methods.length %}
<h2>Methods</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-arguments">Arguments</th>
</tr>
</thead>
<tbody>
{% for method in component.methods %}
<tr>
<td class="nowrap"><code>{{ method.name }}()</code></td>
<td>{{ method.description | markdownInline | safe }}</td>
<td>
{% if method.parameters.length %}
<code>
{% for param in method.parameters %}
{{ param.name }}: {{ param.type.text }}{% if not loop.last %},{% endif %}
{% endfor %}
</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#methods') }}">methods</a>.</em></p>
{% endif %}
{# Custom Properties #}
{% if component.cssProperties.length %}
<h2>Custom Properties</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-default">Default</th>
</tr>
</thead>
<tbody>
{% for cssProperty in component.cssProperties %}
<tr>
<td class="nowrap"><code>{{ cssProperty.name }}</code></td>
<td>{{ cssProperty.description | markdownInline | safe }}</td>
<td>{{ cssProperty.default }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#custom-properties') }}">customizing CSS custom properties</a>.</em></p>
{% endif %}
{# CSS Parts #}
{% if component.cssParts.length %}
<h2>Parts</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for cssPart in component.cssParts %}
<tr>
<td class="nowrap"><code>{{ cssPart.name }}</code></td>
<td>{{ cssPart.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#component-parts') }}">customizing CSS parts</a>.</em></p>
{% endif %}
{# Animations #}
{% if component.animations.length %}
<h2>Animations</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for animation in component.animations %}
<tr>
<td class="nowrap"><code>{{ animation.name }}</code></td>
<td>{{ animation.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#animations') }}">customizing animations</a>.</em></p>
{% endif %}
{# Dependencies #}
{% if component.dependencies.length %}
<h2>Dependencies</h2>
<p>This component automatically imports the following dependencies.</p>
<ul>
{% for dependency in component.dependencies %}
<li><code>&lt;{{ dependency }}&gt;</code></li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

147
docs/_includes/default.njk Normal file
View File

@@ -0,0 +1,147 @@
<!DOCTYPE html>
<html
lang="en"
data-layout="{{ layout }}"
data-shoelace-version="{{ meta.version }}"
>
<head>
{# Metadata #}
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="{{ meta.description }}" />
<title>{{ meta.title }}</title>
{# Opt out of Turbo caching #}
<meta name="turbo-cache-control">
{# Stylesheets #}
<link rel="stylesheet" href="{{ assetUrl('styles/docs.css') }}" />
<link rel="stylesheet" href="{{ assetUrl('styles/code-previews.css') }}" />
<link rel="stylesheet" href="{{ assetUrl('styles/search.css') }}" />
{# Favicons #}
<link rel="icon" href="{{ assetUrl('images/logo.svg') }}" type="image/x-icon" />
{# Twitter Cards #}
<meta name="twitter:card" content="summary" />
<meta name="twitter:creator" content="shoelace_style" />
<meta name="twitter:image" content="{{ assetUrl(meta.image, true) }}" />
{# OpenGraph #}
<meta property="og:url" content="{{ rootUrl(page.url, true) }}" />
<meta property="og:title" content="{{ meta.title }}" />
<meta property="og:description" content="{{ meta.description }}" />
<meta property="og:image" content="{{ assetUrl(meta.image, true) }}" />
{# Shoelace #}
<link rel="stylesheet" href="/dist/themes/light.css" />
<link rel="stylesheet" href="/dist/themes/dark.css" />
<script type="module" src="/dist/shoelace-autoloader.js"></script>
{# Set the initial theme and menu states here to prevent flashing #}
<script>
(() => {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const theme = localStorage.getItem('theme');
document.documentElement.classList.toggle('sl-theme-dark', theme === 'dark' || (!theme && prefersDark));
})();
</script>
{# Turbo + Scroll positioning #}
<script src="{{ assetUrl('scripts/turbo.js') }}" type="module"></script>
<script src="{{ assetUrl('scripts/docs.js') }}" defer></script>
<script src="{{ assetUrl('scripts/code-previews.js') }}" defer></script>
<script src="{{ assetUrl('scripts/lunr.js') }}" defer></script>
<script src="{{ assetUrl('scripts/search.js') }}" defer></script>
</head>
<body>
<a id="skip-to-main" class="visually-hidden" href="#main-content" data-smooth-link="false">
Skip to main content
</a>
{# Menu toggle #}
<button id="menu-toggle" type="button" aria-label="Menu">
<svg width="148" height="148" viewBox="0 0 148 148" xmlns="http://www.w3.org/2000/svg">
<g stroke="currentColor" stroke-width="18" fill="none" fill-rule="evenodd" stroke-linecap="round">
<path d="M9.5 125.5h129M9.5 74.5h129M9.5 23.5h129"></path>
</g>
</svg>
</button>
{# Icon toolbar #}
<div id="icon-toolbar">
{# GitHub #}
<a href="https://github.com/shoelace-style/shoelace" title="View Shoelace on GitHub">
<sl-icon name="github"></sl-icon>
</a>
{# Twitter #}
<a href="https://twitter.com/shoelace_style" title="Follow Shoelace on Twitter">
<sl-icon name="twitter"></sl-icon>
</a>
{# Theme toggle #}
<button
id="theme-toggle"
type="button"
aria-label="Toggle light and dark theme"
title="Toggle theme (press backslash)"
>
<sl-icon class="only-light" name="sun-fill"></sl-icon>
<sl-icon class="only-dark" name="moon-fill"></sl-icon>
</button>
</div>
<aside id="sidebar" data-preserve-scroll>
<header>
<a href="/">
<img src="{{ assetUrl('images/wordmark.svg') }}" alt="Shoelace" />
</a>
<div class="sidebar-version">
{{ meta.version }}
</div>
</header>
<div class="sidebar-buttons">
<sl-button size="small" class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
<sl-icon slot="prefix" name="heart"></sl-icon> Sponsor
</sl-button>
<sl-button size="small" class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace" target="_blank">
<sl-icon slot="prefix" name="github"></sl-icon> Code
</sl-button>
<sl-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
</sl-button>
</div>
<button class="search-box" type="button" title="Press / to search" aria-label="Search" data-plugin="search">
<sl-icon name="search"></sl-icon>
<span>Search</span>
</button>
<nav>
{% include 'sidebar.njk' %}
</nav>
</aside>
{# Content #}
<main>
<a id="main-content"></a>
<article id="content" class="content{% if toc %} content--with-toc{% endif %}">
{% if toc %}
<div class="content__toc">
<ul>
<li class="top"><a href="#">{{ meta.title }}</a></li>
</ul>
</div>
{% endif %}
<div class="content__body">
{% block content %}
{{ content | safe }}
{% endblock %}
</div>
</article>
</main>
</body>
</html>

View File

@@ -0,0 +1,65 @@
<ul>
<li>
<h2>Getting Started</h2>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/getting-started/installation">Installation</a></li>
<li><a href="/getting-started/usage">Usage</a></li>
<li><a href="/getting-started/themes">Themes</a></li>
<li><a href="/getting-started/customizing">Customizing</a></li>
<li><a href="/getting-started/form-controls">Form Controls</a></li>
<li><a href="/getting-started/localization">Localization</a></li>
</ul>
</li>
<li>
<h2>Frameworks</h2>
<ul>
<li><a href="/frameworks/react">React</a></li>
<li><a href="/frameworks/vue">Vue</a></li>
<li><a href="/frameworks/angular">Angular</a></li>
</ul>
</li>
<li>
<h2>Resources</h2>
<ul>
<li><a href="/resources/community">Community</a></li>
<li><a href="https://github.com/shoelace-style/shoelace/discussions">Help &amp; Support</a></li>
<li><a href="/resources/accessibility">Accessibility</a></li>
<li><a href="/resources/contributing">Contributing</a></li>
<li><a href="/resources/changelog">Changelog</a></li>
</ul>
</li>
<li>
<h2>Components</h2>
<ul>
{% for component in meta.components %}
<li>
<a href="/components/{{ component.tagName | removeSlPrefix }}">
{{ component.name | classNameToComponentName }}
</a>
</li>
{% endfor %}
</ul>
</li>
<li>
<h2>Design Tokens</h2>
<ul>
<li><a href="/tokens/typography">Typography</a></li>
<li><a href="/tokens/color">Color</a></li>
<li><a href="/tokens/spacing">Spacing</a></li>
<li><a href="/tokens/elevation">Elevation</a></li>
<li><a href="/tokens/border-radius">Border Radius</a></li>
<li><a href="/tokens/transition">Transition</a></li>
<li><a href="/tokens/z-index">Z-index</a></li>
<li><a href="/tokens/more">More</a></li>
</ul>
</li>
<li>
<h2>Tutorials</h2>
<ul>
<li><a href="/tutorials/integrating-with-laravel">Integrating with Laravel</a></li>
<li><a href="/tutorials/integrating-with-nextjs">Integrating with NextJS</a></li>
<li><a href="/tutorials/integrating-with-rails">Integrating with Rails</a></li>
</ul>
</li>
</ul>

View File

@@ -1,97 +0,0 @@
- 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)
- [Breadcrumb](/components/breadcrumb)
- [Breadcrumb Item](/components/breadcrumb-item)
- [Button](/components/button)
- [Button Group](/components/button-group)
- [Card](/components/card)
- [Checkbox](/components/checkbox)
- [Color Picker](/components/color-picker)
- [Details](/components/details)
- [Dialog](/components/dialog)
- [Divider](/components/divider)
- [Drawer](/components/drawer)
- [Dropdown](/components/dropdown)
- [Icon](/components/icon)
- [Icon Button](/components/icon-button)
- [Image Comparer](/components/image-comparer)
- [Input](/components/input)
- [Menu](/components/menu)
- [Menu Item](/components/menu-item)
- [Menu Label](/components/menu-label)
- [Progress Bar](/components/progress-bar)
- [Progress Ring](/components/progress-ring)
- [QR Code](/components/qr-code)
- [Radio](/components/radio)
- [Radio Button](/components/radio-button)
- [Radio Group](/components/radio-group)
- [Range](/components/range)
- [Rating](/components/rating)
- [Select](/components/select)
- [Skeleton](/components/skeleton)
- [Spinner](/components/spinner)
- [Split Panel](/components/split-panel)
- [Switch](/components/switch)
- [Tab Group](/components/tab-group)
- [Tab](/components/tab)
- [Tab Panel](/components/tab-panel)
- [Tag](/components/tag)
- [Textarea](/components/textarea)
- [Tooltip](/components/tooltip)
<!--plop:component-->
- Utilities
- [Animated Image](/components/animated-image)
- [Animation](/components/animation)
- [Format Bytes](/components/format-bytes)
- [Format Date](/components/format-date)
- [Format Number](/components/format-number)
- [Include](/components/include)
- [Mutation Observer](/components/mutation-observer)
- [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)
- [Elevation](/tokens/elevation)
- [Border Radius](/tokens/border-radius)
- [Transition](/tokens/transition)
- [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)

View File

@@ -0,0 +1,35 @@
function normalizePathname(pathname) {
// Remove /index.html
if (pathname.endsWith('/index.html')) {
pathname = pathname.replace(/\/index\.html/, '');
}
// Remove trailing slashes
return pathname.replace(/\/$/, '');
}
/**
* Adds a class name to links that are currently active.
*/
module.exports = function (doc, options) {
options = {
className: 'active-link', // the class to add to active links
pathname: undefined, // the current pathname to compare
within: 'body', // element containing the target links
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('a').forEach(link => {
if (normalizePathname(options.pathname) === normalizePathname(link.pathname)) {
link.classList.add(options.className);
}
});
return doc;
};

View File

@@ -0,0 +1,64 @@
const { createSlug } = require('./strings.cjs');
/**
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
* The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
options = {
levels: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], // the headings to convert
className: 'anchor-heading', // the class name to add
within: 'body', // the element containing the target headings
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => {
const hasAnchor = heading.querySelector('a');
const anchor = doc.createElement('a');
let id = heading.textContent ?? '';
let suffix = 0;
// Skip heading levels we don't care about
if (!options.levels?.includes(heading.tagName.toLowerCase())) {
return;
}
// Convert dots to underscores
id = id.replace(/\./g, '_');
// Turn it into a slug
id = createSlug(id);
// Make sure it starts with a letter
if (!/^[a-z]/i.test(id)) {
id = `id_${id}`;
}
// Make sure the id is unique
const originalId = id;
while (doc.getElementById(id) !== null) {
id = `${originalId}-${++suffix}`;
}
if (hasAnchor || !id) return;
heading.setAttribute('id', id);
anchor.setAttribute('href', `#${encodeURIComponent(id)}`);
anchor.setAttribute('aria-label', `Direct link to "${heading.textContent}"`);
if (options.className) {
heading.classList.add(options.className);
}
// Append the anchor
heading.append(anchor);
});
return doc;
};

71
docs/_utilities/cem.cjs Normal file
View File

@@ -0,0 +1,71 @@
const customElementsManifest = require('../../dist/custom-elements.json');
//
// Export it here so we can import it elsewhere and use the same version
//
module.exports.customElementsManifest = customElementsManifest;
//
// Gets all components from custom-elements.json and returns them in a more documentation-friendly format.
//
module.exports.getAllComponents = function () {
const allComponents = [];
customElementsManifest.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');
// Remove members that are private or don't have a description
const members = declaration.members?.filter(member => member.description && member.privacy !== 'private');
const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private');
const properties = members?.filter(prop => {
// Look for a corresponding attribute
const attribute = declaration.attributes?.find(attr => attr.fieldName === prop.name);
if (attribute) {
prop.attribute = attribute.name || attribute.fieldName;
}
return prop.kind === 'field' && prop.privacy !== 'private';
});
allComponents.push({
...declaration,
methods,
properties
});
}
});
});
// Build dependency graphs
allComponents.forEach(component => {
const dependencies = [];
// Recursively fetch sub-dependencies
function getDependencies(tag) {
const cmp = allComponents.find(c => c.tagName === tag);
if (!cmp || !Array.isArray(component.dependencies)) {
return;
}
cmp.dependencies?.forEach(dependentTag => {
if (!dependencies.includes(dependentTag)) {
dependencies.push(dependentTag);
}
getDependencies(dependentTag);
});
}
getDependencies(component.tagName);
component.dependencies = dependencies.sort();
});
// Sort by name
return allComponents.sort((a, b) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
});
};

View File

@@ -0,0 +1,138 @@
let count = 1;
function escapeHtml(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
/**
* Turns code fields with the :preview suffix into interactive code previews.
*/
module.exports = function (doc, options) {
options = {
within: 'body', // the element containing the code fields to convert
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('[class*=":preview"]').forEach(code => {
const pre = code.closest('pre');
if (!pre) {
return;
}
const adjacentPre = pre.nextElementSibling?.tagName.toLowerCase() === 'pre' ? pre.nextElementSibling : null;
const reactCode = adjacentPre?.querySelector('code[class$="react"]');
const sourceGroupId = `code-preview-source-group-${count}`;
const isExpanded = code.getAttribute('class').includes(':expanded');
const noCodePen = code.getAttribute('class').includes(':no-codepen');
count++;
const htmlButton = `
<button type="button"
title="Show HTML code"
class="code-preview__button code-preview__button--html"
>
HTML
</button>
`;
const reactButton = `
<button type="button" title="Show React code" class="code-preview__button code-preview__button--react">
React
</button>
`;
const codePenButton = `
<button type="button" class="code-preview__button code-preview__button--codepen" title="Edit on CodePen">
<svg
width="138"
height="26"
viewBox="0 0 138 26"
fill="none"
stroke="currentColor"
stroke-width="2.3"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M80 6h-9v14h9 M114 6h-9 v14h9 M111 13h-6 M77 13h-6 M122 20V6l11 14V6 M22 16.7L33 24l11-7.3V9.3L33 2L22 9.3V16.7z M44 16.7L33 9.3l-11 7.4 M22 9.3l11 7.3 l11-7.3 M33 2v7.3 M33 16.7V24 M88 14h6c2.2 0 4-1.8 4-4s-1.8-4-4-4h-6v14 M15 8c-1.3-1.3-3-2-5-2c-4 0-7 3-7 7s3 7 7 7 c2 0 3.7-0.8 5-2 M64 13c0 4-3 7-7 7h-5V6h5C61 6 64 9 64 13z" />
</svg>
</button>
`;
const codePreview = `
<div class="code-preview ${isExpanded ? 'code-preview--expanded' : ''}">
<div class="code-preview__preview">
${code.textContent}
<div class="code-preview__resizer">
<sl-icon name="grip-vertical"></sl-icon>
</div>
</div>
<div class="code-preview__source-group" id="${sourceGroupId}">
<div class="code-preview__source code-preview__source--html" ${reactCode ? 'data-flavor="html"' : ''}>
<pre><code class="language-html">${escapeHtml(code.textContent)}</code></pre>
</div>
${
reactCode
? `
<div class="code-preview__source code-preview__source--react" data-flavor="react">
<pre><code class="language-jsx">${escapeHtml(reactCode.textContent)}</code></pre>
</div>
`
: ''
}
</div>
<div class="code-preview__buttons">
<button
type="button"
class="code-preview__button code-preview__toggle"
aria-expanded="${isExpanded ? 'true' : 'false'}"
aria-controls="${sourceGroupId}"
>
Source
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
${reactCode ? ` ${htmlButton} ${reactButton} ` : ''}
${noCodePen ? '' : codePenButton}
</div>
</div>
`;
pre.insertAdjacentHTML('afterend', codePreview);
pre.remove();
if (adjacentPre) {
adjacentPre.remove();
}
});
// Wrap code preview scripts in anonymous functions so they don't run in the global scope
doc.querySelectorAll('.code-preview__preview script').forEach(script => {
if (script.type === 'module') {
// Modules are already scoped
script.textContent = script.innerHTML;
} else {
// Wrap non-modules in an anonymous function so they don't run in the global scope
script.textContent = `(() => { ${script.innerHTML} })();`;
}
});
return doc;
};

View File

@@ -0,0 +1,26 @@
/**
* Adds copy code buttons to code fields. The provided doc should be a document object provided by JSDOM. The same
* document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc) {
doc.querySelectorAll('pre > code').forEach(code => {
const pre = code.closest('pre');
const button = doc.createElement('button');
button.setAttribute('type', 'button');
button.classList.add('copy-code-button');
button.setAttribute('aria-label', 'Copy');
button.innerHTML = `
<svg class="copy-code-button__copy-icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-files" viewBox="0 0 16 16" part="svg">
<path d="M13 0H6a2 2 0 0 0-2 2 2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2 2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 13V4a2 2 0 0 0-2-2H5a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zM3 4a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4z"></path>
</svg>
<svg class="copy-code-button__copied-icon" style="display: none;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-lg" viewBox="0 0 16 16" part="svg">
<path d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425a.247.247 0 0 1 .02-.022Z"></path>
</svg>
`;
pre.append(button);
});
return doc;
};

View File

@@ -0,0 +1,41 @@
const { isExternalLink } = require('./strings.cjs');
/**
* Transforms external links to make them safer and optionally add a target. The provided doc should be a document
* object provided by JSDOM. The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
options = {
className: 'external-link', // the class name to add to links
noopener: true, // sets rel="noopener"
noreferrer: true, // sets rel="noreferrer"
ignore: () => false, // callback function to filter links that should be ignored
within: 'body', // element that contains the target links
target: '', // sets the target attribute
...options
};
const within = doc.querySelector(options.within);
if (within) {
within.querySelectorAll('a').forEach(link => {
if (isExternalLink(link) && !options.ignore(link)) {
link.classList.add(options.className);
const rel = [];
if (options.noopener) rel.push('noopener');
if (options.noreferrer) rel.push('noreferrer');
if (rel.length) {
link.setAttribute('rel', rel.join(' '));
}
if (options.target) {
link.setAttribute('target', options.target);
}
}
});
}
return doc;
};

View File

@@ -0,0 +1,63 @@
const Prism = require('prismjs');
const PrismLoader = require('prismjs/components/index.js');
PrismLoader('diff');
PrismLoader.silent = true;
/** Highlights a code string. */
function highlight(code, language) {
const alias = language.replace(/^diff-/, '');
const isDiff = /^diff-/i.test(language);
// Auto-load the target language
if (!Prism.languages[alias]) {
PrismLoader(alias);
if (!Prism.languages[alias]) {
throw new Error(`Unsupported language for code highlighting: "${language}"`);
}
}
// Register diff-* languages to use the diff grammar
if (isDiff) {
Prism.languages[language] = Prism.languages.diff;
}
return Prism.highlight(code, Prism.languages[language], language);
}
/**
* Highlights all code fields that have a language parameter. If the language has a colon in its name, the first chunk
* will be the language used and additional chunks will be applied as classes to the `<pre>`. For example, a code field
* tagged with "html:preview" will be rendered as `<pre class="language-html preview">`.
*
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
* appropriate DOM manipulations.
*/
module.exports = function (doc) {
doc.querySelectorAll('pre > code[class]').forEach(code => {
// Look for class="language-*" and split colons into separate classes
code.classList.forEach(className => {
if (className.startsWith('language-')) {
//
// We use certain suffixes to indicate code previews, expanded states, etc. The class might look something like
// this:
//
// class="language-html:preview:expanded"
//
// The language will always come first, so we need to drop the "language-" prefix and everything after the first
// color to get the highlighter language.
//
const language = className.replace(/^language-/, '').split(':')[0];
try {
code.innerHTML = highlight(code.textContent ?? '', language);
} catch (err) {
// Language not found, skip it
}
}
});
});
return doc;
};

View File

@@ -0,0 +1,67 @@
const MarkdownIt = require('markdown-it');
const markdownItContainer = require('markdown-it-container');
const markdownItIns = require('markdown-it-ins');
const markdownItKbd = require('markdown-it-kbd');
const markdownItMark = require('markdown-it-mark');
const markdownItReplaceIt = require('markdown-it-replace-it');
const markdown = MarkdownIt({
html: true,
xhtmlOut: false,
breaks: false,
langPrefix: 'language-',
linkify: false,
typographer: false
});
// Third-party plugins
markdown.use(markdownItContainer);
markdown.use(markdownItIns);
markdown.use(markdownItKbd);
markdown.use(markdownItMark);
markdown.use(markdownItReplaceIt);
// Callouts
['tip', 'warning', 'danger'].forEach(type => {
markdown.use(markdownItContainer, type, {
render: function (tokens, idx) {
if (tokens[idx].nesting === 1) {
return `<div role="alert" class="callout callout--${type}">`;
}
return '</div>\n';
}
});
});
// Asides
markdown.use(markdownItContainer, 'aside', {
render: function (tokens, idx) {
if (tokens[idx].nesting === 1) {
return `<aside>`;
}
return '</aside>\n';
}
});
// Details
markdown.use(markdownItContainer, 'details', {
validate: params => params.trim().match(/^details\s+(.*)$/),
render: (tokens, idx) => {
const m = tokens[idx].info.trim().match(/^details\s+(.*)$/);
if (tokens[idx].nesting === 1) {
return `<details>\n<summary><span>${markdown.utils.escapeHtml(m[1])}</span></summary>\n`;
}
return '</details>\n';
}
});
// Replace [#1234] with a link to GitHub issues
markdownItReplaceIt.replacements.push({
name: 'github-issues',
re: /\[#([0-9]+)\]/gs,
sub: '<a href="https://github.com/shoelace-style/shoelace/issues/$1">#$1</a>',
html: true,
default: true
});
module.exports = markdown;

View File

@@ -0,0 +1,26 @@
const { format } = require('prettier');
/** Formats markup using prettier. */
module.exports = function (content, options) {
options = {
arrowParens: 'avoid',
bracketSpacing: true,
htmlWhitespaceSensitivity: 'css',
insertPragma: false,
bracketSameLine: false,
jsxSingleQuote: false,
parser: 'html',
printWidth: 120,
proseWrap: 'preserve',
quoteProps: 'as-needed',
requirePragma: false,
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'none',
useTabs: false,
...options
};
return format(content, options);
}

View File

@@ -0,0 +1,21 @@
/**
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
* The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
const tables = [...doc.querySelectorAll('table')];
options = {
className: 'table-scroll', // the class name to add to the table's container
...options
};
tables.forEach(table => {
const div = doc.createElement('div');
div.classList.add(options.className);
table.insertAdjacentElement('beforebegin', div);
div.append(table);
});
return doc;
};

View File

@@ -0,0 +1,16 @@
const slugify = require('slugify');
/** Creates a slug from an arbitrary string of text. */
module.exports.createSlug = function (text) {
return slugify(String(text), {
remove: /[^\w|\s]/g,
lower: true
});
};
/** Determines whether or not a link is external. */
module.exports.isExternalLink = function (link) {
// We use the "internal" hostname when initializing JSDOM so we know that those are local links
if (!link.hostname || link.hostname === 'internal') return false;
return true;
};

View File

@@ -0,0 +1,42 @@
/**
* Generates an in-page table of contents based on headings.
*/
module.exports = function (doc, options) {
options = {
levels: ['h2'], // headings to include (they must have an id)
container: 'nav', // the container to append links to
listItem: true, // if true, links will be wrapped in <li>
within: 'body', // the element containing the headings to summarize
...options
};
const container = doc.querySelector(options.container);
const within = doc.querySelector(options.within);
const headingSelector = options.levels.map(h => `${h}[id]`).join(', ');
if (!container || !within) {
return doc;
}
within.querySelectorAll(headingSelector).forEach(heading => {
const listItem = doc.createElement('li');
const link = doc.createElement('a');
const level = heading.tagName.slice(1);
link.href = `#${heading.id}`;
link.textContent = heading.textContent;
if (options.listItem) {
// List item + link
listItem.setAttribute('data-level', level);
listItem.append(link);
container.append(listItem);
} else {
// Link only
link.setAttribute('data-level', level);
container.append(link);
}
});
return doc;
};

View File

@@ -0,0 +1,23 @@
const smartquotes = require('smartquotes');
smartquotes.replacements.push([/---/g, '\u2014']); // em dash
smartquotes.replacements.push([/--/g, '\u2013']); // en dash
smartquotes.replacements.push([/\.\.\./g, '\u2026']); // ellipsis
smartquotes.replacements.push([/\(c\)/gi, '\u00A9']); // copyright
smartquotes.replacements.push([/\(r\)/gi, '\u00AE']); // registered trademark
smartquotes.replacements.push([/\?!/g, '\u2048']); // ?!
smartquotes.replacements.push([/!!/g, '\u203C']); // !!
smartquotes.replacements.push([/\?\?/g, '\u2047']); // ??
smartquotes.replacements.push([/([0-9]\s?)-(\s?[0-9])/g, '$1\u2013$2']); // number ranges use en dash
/**
* Improves typography by adding smart quotes and similar corrections within the specified element(s).
*
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
* appropriate DOM manipulations.
*/
module.exports = function (doc, selector = 'body') {
const elements = [...doc.querySelectorAll(selector)];
elements.forEach(el => smartquotes.element(el));
return doc;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 861 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -0,0 +1,32 @@
<svg width="673" height="739" viewBox="0 0 673 739" xmlns="http://www.w3.org/2000/svg">
<g fill-rule="nonzero" fill="none">
<path d="M467 149.805c-46.62-7.44-99.71-11.41-155-11.41-50.6 0-99.35 3.32-142.98 9.58.01-.67.02-1.34.05-2.01C171.475 65.082 237.996.903 318.914 1.398c80.917.494 146.649 65.48 148.066 146.387.01.68.02 1.35.02 2.02Z" fill="#0BA5E9"/>
<path d="M337.55 1.342a149.047 149.047 0 0 0-168.93 143.229c-.031.67-.04 1.34-.05 2.01 12.961-1.86 26.384-3.454 40.164-4.784 3.478-71.745 57.64-130.8 128.816-140.455Z" opacity=".1" fill="#FFF"/>
<path d="M532.18 161.625a600.121 600.121 0 0 0-65.2-13.84 943.364 943.364 0 0 0-108.74-10.45 1133.608 1133.608 0 0 0-83.01-.34 973.29 973.29 0 0 0-106.16 8.97 624.292 624.292 0 0 0-77.25 15.66C32.61 177.995 0 199.935 0 223.395s32.61 45.4 91.82 61.77c41.64 11.52 92.98 19.37 148.92 22.97 23.09 1.5 46.96 2.26 71.26 2.26 24.38 0 48.33-.77 71.49-2.27 50.91-3.29 98.01-10.1 137.43-20 .21-.06.41-.11.62-.16 2.66-.66 5.28-1.35 7.87-2.04.93-.26 1.85-.51 2.77-.76a.978.978 0 0 1 .16-.05c.88-.24 1.75-.49 2.62-.73 1.74-.5 3.46-.99 5.15-1.5.08-.02.15-.04.22-.06 1.47-.44 2.91-.88 4.34-1.32 1.17-.37 2.33-.73 3.48-1.1.84-.27 1.67-.54 2.49-.81.6-.2 1.19-.39 1.77-.59.79-.26 1.58-.53 2.36-.8.33-.11.66-.22.98-.34.75-.25 1.48-.51 2.21-.77.79-.28 1.58-.57 2.36-.85.65-.23 1.3-.47 1.94-.71.54-.21 1.07-.41 1.61-.61 1.47-.55 2.91-1.12 4.33-1.68.71-.29 1.42-.57 2.12-.86.69-.28 1.39-.57 2.07-.86 1.12-.47 2.22-.94 3.3-1.41.52-.24 1.05-.47 1.56-.69.39-.18.77-.35 1.16-.53.28-.12.56-.25.83-.38 1.01-.46 2.01-.93 2.99-1.4 3.76-1.8 7.27-3.64 10.53-5.52 20.45-11.71 31.24-24.7 31.24-38.2 0-23.46-32.61-45.4-91.82-61.77Zm-.54 121.62c-41.69 11.53-93.17 19.38-149.26 22.95-22.81 1.45-46.39 2.2-70.38 2.2-23.91 0-47.41-.74-70.15-2.19-56.18-3.56-107.74-11.41-149.49-22.96C34.09 267.125 2 245.875 2 223.395c0-1.986.252-3.965.74-5.89 5.1-20.28 36.47-39.26 89.62-53.96a623.806 623.806 0 0 1 76.66-15.57 976.027 976.027 0 0 1 106.8-9c11.92-.39 23.98-.583 36.18-.58 15.41 0 30.65.31 45.63.91a941.367 941.367 0 0 1 109.37 10.5 598.858 598.858 0 0 1 64.64 13.74c53.14 14.7 84.5 33.67 89.61 53.94.496 1.93.75 3.916.75 5.91 0 22.48-32.09 43.73-90.36 59.85Z" fill="#3F3D56"/>
<path d="M623.43 224.305c0 13.36-11.01 26-30.67 37.29-3.27 1.88-6.79 3.72-10.53 5.52-.98.47-1.98.94-2.99 1.4-.27.13-.55.26-.83.38-.39.18-.77.35-1.16.53-.51.22-1.04.45-1.56.69-1.08.47-2.18.94-3.3 1.41-.68.29-1.38.58-2.07.86-.7.29-1.41.57-2.12.86-1.42.56-2.86 1.13-4.33 1.68-.54.2-1.07.4-1.61.61-.64.24-1.29.48-1.94.71-.78.28-1.57.57-2.36.85-.73.26-1.46.52-2.21.77-.32.12-.65.23-.98.34-.78.27-1.57.54-2.36.8-.58.2-1.17.39-1.77.59-.82.27-1.65.54-2.49.81-1.15.37-2.31.73-3.48 1.1-1.43.44-2.87.88-4.34 1.32-.07.02-.14.04-.22.06-1.69.51-3.41 1-5.15 1.5-.87.24-1.74.49-2.62.73a.978.978 0 0 0-.16.05c-.92.25-1.84.5-2.77.76-2.58.68-5.21 1.37-7.87 2.04-.21.05-.41.1-.62.16-38.35 9.58-85.4 16.56-137.47 19.93-22.81 1.47-46.59 2.25-71.02 2.25-24.65 0-48.63-.79-71.62-2.29-137.24-8.95-239.38-43.03-239.38-83.71.01-2.475.388-4.936 1.12-7.3.06.17.12.33.19.5 14.27 37.48 115.54 67.77 246.94 75.16 20.13 1.14 40.98 1.73 62.32 1.73 21.43 0 42.36-.6 62.57-1.74 131.29-7.42 232.46-37.72 246.68-75.17.24-.6.45-1.2.63-1.8a25.304 25.304 0 0 1 1.55 8.62ZM91.67 213.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217Z" fill="#3F3D56"/>
<path d="M162.67 260.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217ZM531.67 213.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217ZM460.67 260.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217ZM311.67 282.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217Z" fill="#3F3D56"/>
<circle fill="#2F2E41" cx="336.978" cy="450.705" r="42.012"/>
<path fill="#2F2E41" d="m300.555 488.547 20.447-10.24 5.715 11.413-20.447 10.24z"/>
<ellipse fill="#2F2E41" transform="rotate(-56.601 300.086 492.946)" cx="300.086" cy="492.946" rx="3.989" ry="10.636"/>
<path fill="#2F2E41" d="m347.239 489.72 5.715-11.412 20.447 10.24-5.715 11.412z"/>
<ellipse fill="#2F2E41" transform="rotate(-33.399 373.87 492.946)" cx="373.87" cy="492.946" rx="10.636" ry="3.989"/>
<circle fill="#FFF" cx="334.037" cy="440.429" r="14.359"/>
<ellipse fill="#3F3D56" transform="rotate(-45 334.135 434.282)" cx="334.135" cy="434.282" rx="4.766" ry="4.8"/>
<path d="M370.12 405c.632-15.553-12.773-28.727-29.941-29.425-17.168-.697-31.597 11.346-32.229 26.9-.632 15.553 11.302 19.087 28.47 19.785 17.167.697 33.068-1.706 33.7-17.26Z" fill="#0BA5E9"/>
<ellipse fill="#2F2E41" transform="rotate(-40.645 380.654 456.766)" cx="380.654" cy="456.766" rx="6.594" ry="21.006"/>
<ellipse fill="#2F2E41" transform="rotate(-49.355 293.42 456.766)" cx="293.419" cy="456.766" rx="21.006" ry="6.594"/>
<path d="M348.517 467.262a9.572 9.572 0 1 1-18.836 3.428l-.003-.018c-.942-5.202 3.08-7.043 8.282-7.985 5.203-.942 9.615-.628 10.557 4.575Z" fill="#FFF"/>
<path d="M266 495.395a2 2 0 0 1-2-2v-118a2 2 0 0 1 4 0v118a2 2 0 0 1-2 2ZM236 601.395a2 2 0 0 1-2-2v-86a2 2 0 0 1 4 0v86a2 2 0 0 1-2 2ZM313 530.395a2 2 0 0 1-2-2v-118a2 2 0 0 1 4 0v118a2 2 0 0 1-2 2ZM284 615.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM325 369.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM225 390.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM399 395.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM395 545.395a2 2 0 0 1-2-2v-58a2 2 0 0 1 4 0v58a2 2 0 0 1-2 2ZM355 596.395a2 2 0 0 1-2-2v-86a2 2 0 0 1 4 0v86a2 2 0 0 1-2 2ZM363 449.395a2 2 0 0 1-2-2v-118a2 2 0 0 1 4 0v118a2 2 0 0 1-2 2Z" fill="#CCC"/>
<ellipse fill="#2F2E41" transform="rotate(-39.938 594.37 683.981)" cx="594.369" cy="683.981" rx="6.76" ry="21.534"/>
<circle fill="#2F2E41" transform="rotate(-71.565 548.562 676.503)" cx="548.562" cy="676.503" r="43.067"/>
<path fill="#2F2E41" d="M553.707 710.303h13.084v23.442h-13.084zM527.54 710.303h13.084v23.442H527.54z"/>
<ellipse fill="#2F2E41" cx="555.888" cy="734.017" rx="10.903" ry="4.089"/>
<ellipse fill="#2F2E41" cx="529.72" cy="733.472" rx="10.903" ry="4.089"/>
<path d="M535.04 622.366c3.845-15.487 20.82-24.6 37.914-20.356 17.094 4.245 27.834 20.24 23.989 35.727-3.846 15.487-16.604 15.537-33.698 11.293-17.094-4.245-32.051-11.177-28.206-26.664Z" fill="#0BA5E9"/>
<ellipse fill="#2F2E41" transform="rotate(-64.626 500.054 656.52)" cx="500.054" cy="656.52" rx="6.76" ry="21.534"/>
<circle fill="#FFF" cx="542.124" cy="667.416" r="14.359"/>
<circle fill="#3F3D56" cx="536.222" cy="662.269" r="4.786"/>
<circle fill="#FFF" cx="542" cy="697.395" r="6"/>
<path d="M671.531 738.395h-236a1 1 0 1 1 0-2h236a1 1 0 0 1 0 2Z" fill="#3F3D56"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -1,365 +0,0 @@
/* 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');
if (script.type === 'module') {
newScript.type = 'module';
newScript.textContent = script.innerHTML;
} else {
newScript.appendChild(document.createTextNode(`(() => { ${script.innerHTML} })();`));
}
script.parentNode.replaceChild(newScript, script);
}
function getFlavor() {
return localStorage.getItem('flavor') || 'html';
}
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((html, next) => {
const domParser = new DOMParser();
const doc = domParser.parseFromString(html, 'text/html');
const htmlButton = `
<button
type="button"
title="Show HTML code"
class="code-block__button code-block__button--html ${flavor === 'html' ? 'code-block__button--selected' : ''}"
>
HTML
</button>
`;
const reactButton = `
<button
type="button"
title="Show React code"
class="code-block__button code-block__button--react ${
flavor === 'react' ? 'code-block__button--selected' : ''
}"
>
React
</button>
`;
const codePenButton = `
<button type="button" class="code-block__button code-block__button--codepen" title="Edit on CodePen">
<svg
width="138"
height="26"
viewBox="0 0 138 26"
fill="none"
stroke="currentColor"
stroke-width="2.3"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M80 6h-9v14h9 M114 6h-9 v14h9 M111 13h-6 M77 13h-6 M122 20V6l11 14V6 M22 16.7L33 24l11-7.3V9.3L33 2L22 9.3V16.7z M44 16.7L33 9.3l-11 7.4 M22 9.3l11 7.3 l11-7.3 M33 2v7.3 M33 16.7V24 M88 14h6c2.2 0 4-1.8 4-4s-1.8-4-4-4h-6v14 M15 8c-1.3-1.3-3-2-5-2c-4 0-7 3-7 7s3 7 7 7 c2 0 3.7-0.8 5-2 M64 13c0 4-3 7-7 7h-5V6h5C61 6 64 9 64 13z" />
</svg>
</button>
`;
[...doc.querySelectorAll('code[class^="lang-"]')].forEach(code => {
if (code.classList.contains('preview')) {
const isExpanded = code.classList.contains('expanded');
const pre = code.closest('pre');
const sourceGroupId = `code-block-source-group-${count}`;
const toggleId = `code-block-toggle-${count}`;
const reactPre = getAdjacentExample('react', pre);
const hasReact = reactPre !== null;
pre.setAttribute('data-lang', pre.getAttribute('data-lang').replace(/ preview$/, ''));
pre.setAttribute('aria-labelledby', toggleId);
const codeBlock = `
<div class="code-block ${isExpanded ? 'code-block--expanded' : ''}">
<div class="code-block__preview">
${code.textContent}
<div class="code-block__resizer">
<sl-icon name="grip-vertical"></sl-icon>
</div>
</div>
<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">
${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"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
${!code.classList.contains('no-codepen') ? codePenButton : ''}
</div>
</div>
`;
pre.replaceWith(domParser.parseFromString(codeBlock, 'text/html').body);
reactPre?.remove();
count++;
}
});
// Force the highlighter to run again so JSX fields get highlighted properly
requestAnimationFrame(() => Prism.highlightAll());
next(doc.body.innerHTML);
});
// After the page is done loading, force scripts in previews to execute
hook.doneEach(() => {
[...document.querySelectorAll('.code-block__preview script')].map(script => runScript(script));
});
// Horizontal resizing
hook.doneEach(() => {
[...document.querySelectorAll('.code-block__preview')].forEach(preview => {
const resizer = preview.querySelector('.code-block__resizer');
let startX;
let startWidth;
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);
}
function dragMove(event) {
setWidth(startWidth + (event.changedTouches ? event.changedTouches[0].pageX : event.pageX) - startX);
}
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);
}
function setWidth(width) {
preview.style.width = `${width}px`;
}
resizer.addEventListener('mousedown', dragStart);
resizer.addEventListener('touchstart', dragStart);
}, false);
});
});
// Toggle source mode
document.addEventListener('click', event => {
const button = event.target.closest('button');
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
document.addEventListener('click', event => {
const toggle = event.target.closest('.code-block__toggle');
if (toggle) {
const codeBlock = event.target.closest('.code-block');
codeBlock.classList.toggle('code-block--expanded');
event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-block--expanded'));
}
});
// Show pulse when copying
document.addEventListener('click', event => {
const button = event.target.closest('.docsify-copy-code-button');
if (button) {
button.classList.remove('copied');
requestAnimationFrame(() => {
button.addEventListener('animationend', () => button.classList.remove('copied'), { once: true });
button.classList.add('copied');
});
}
});
// Open in CodePen
document.addEventListener('click', event => {
const button = event.target.closest('button');
const version = sessionStorage.getItem('sl-version');
if (button?.classList.contains('code-block__button--codepen')) {
const codeBlock = button.closest('.code-block');
const htmlExample = codeBlock.querySelector('.code-block__source--html > pre > code')?.textContent;
const reactExample = codeBlock.querySelector('.code-block__source--react > pre > code')?.textContent;
const isReact = flavor === 'react' && typeof reactExample === 'string';
const theme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const isDark = theme === 'dark' || (theme === 'auto' && prefersDark);
const editors = isReact ? '0010' : '1000';
let htmlTemplate = '';
let jsTemplate = '';
let cssTemplate = '';
const form = document.createElement('form');
form.action = 'https://codepen.io/pen/define';
form.method = 'POST';
form.target = '_blank';
// HTML templates
if (!isReact) {
htmlTemplate =
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/shoelace.js"></script>\n` +
`\n${htmlExample}`;
jsTemplate = '';
}
// React templates
if (isReact) {
htmlTemplate = '<div id="root"></div>';
jsTemplate =
`import React from 'https://cdn.skypack.dev/react@${reactVersion}';\n` +
`import ReactDOM from 'https://cdn.skypack.dev/react-dom@${reactVersion}';\n` +
`import { setBasePath } from 'https://cdn.skypack.dev/@shoelace-style/shoelace@${version}/dist/utilities/base-path';\n` +
`\n` +
`// Set the base path for Shoelace assets\n` +
`setBasePath('https://cdn.skypack.dev/@shoelace-style/shoelace@${version}/dist/')\n` +
`\n${convertModuleLinks(reactExample)}\n` +
`\n` +
`ReactDOM.render(<App />, document.getElementById('root'));`;
}
// CSS templates
cssTemplate =
`@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/themes/${
isDark ? 'dark' : 'light'
}.css';\n` +
'\n' +
'body {\n' +
' font: 16px sans-serif;\n' +
' background-color: var(--sl-color-neutral-0);\n' +
' color: var(--sl-color-neutral-900);\n' +
' padding: 1rem;\n' +
'}';
// Docs: https://blog.codepen.io/documentation/prefill/
const data = {
title: '',
description: '',
tags: ['shoelace', 'web components'],
editors,
head: `<meta name="viewport" content="width=device-width">`,
html_classes: `sl-theme-${isDark ? 'dark' : 'light'}`,
css_external: ``,
js_external: ``,
js_module: true,
js_pre_processor: isReact ? 'babel' : 'none',
html: htmlTemplate,
css: cssTemplate,
js: jsTemplate
};
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'data';
input.value = JSON.stringify(data);
form.append(input);
document.body.append(form);
form.submit();
form.remove();
}
});
})();

View File

@@ -1,557 +0,0 @@
(() => {
const isDev = location.hostname === 'localhost';
const isNext = location.hostname === 'next.shoelace.style';
const customElements = fetch('/dist/custom-elements.json')
.then(res => res.json())
.catch(err => console.error(err));
function createPropsTable(props) {
const table = document.createElement('table');
table.classList.add('metadata-table');
table.innerHTML = `
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Reflects</th>
<th>Type</th>
<th>Default</th>
</tr>
</thead>
<tbody>
${props
.map(prop => {
const hasAttribute = !!prop.attribute;
const isAttributeDifferent = prop.attribute !== prop.name;
let attributeInfo = '';
if (!hasAttribute) {
attributeInfo = `<br><small>(property only)</small>`;
} else if (isAttributeDifferent) {
attributeInfo = `
<br>
<sl-tooltip content="This attribute is different from its property">
<small>
<code class="nowrap">
${escapeHtml(prop.attribute)}
</code>
</small>
</sl-tooltip>`;
}
return `
<tr>
<td>
<code class="nowrap">${escapeHtml(prop.name)}</code>
${attributeInfo}
</td>
<td>
${escapeHtml(prop.description)}
</td>
<td style="text-align: center;">${
prop.reflects ? '<sl-icon label="yes" name="check"></sl-icon>' : ''
}</td>
<td>${prop.type?.text ? `<code>${escapeHtml(prop.type?.text || '')}</code>` : '-'}</td>
<td>${prop.default ? `<code>${escapeHtml(prop.default)}</code>` : '-'}</td>
</tr>
`;
})
.join('')}
</tbody>
`;
return table.outerHTML;
}
function createEventsTable(events) {
const table = document.createElement('table');
table.classList.add('metadata-table');
table.innerHTML = `
<thead>
<tr>
<th data-flavor="html">Name</th>
<th data-flavor="react">React Event</th>
<th>Description</th>
<th>Event Detail</th>
</tr>
</thead>
<tbody>
${events
.map(
event => `
<tr>
<td data-flavor="html"><code class="nowrap">${escapeHtml(event.name)}</code></td>
<td data-flavor="react"><code class="nowrap">${escapeHtml(event.reactName)}</code></td>
<td>${escapeHtml(event.description)}</td>
<td>${event.type?.text ? `<code>${escapeHtml(event.type?.text)}` : '-'}</td>
</tr>
`
)
.join('')}
</tbody>
`;
return table.outerHTML;
}
function createMethodsTable(methods) {
const table = document.createElement('table');
table.classList.add('metadata-table');
table.innerHTML = `
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Arguments</th>
</tr>
</thead>
<tbody>
${methods
.map(
method => `
<tr>
<td class="nowrap"><code>${escapeHtml(method.name)}</code></td>
<td>${escapeHtml(method.description)}</td>
<td>
${
method.parameters?.length
? `
<code>${escapeHtml(
method.parameters.map(param => `${param.name}: ${param.type.text}`).join(', ')
)}</code>
`
: '-'
}
</td>
</tr>
`
)
.join('')}
</tbody>
`;
return table.outerHTML;
}
function createSlotsTable(slots) {
const table = document.createElement('table');
table.classList.add('metadata-table');
table.innerHTML = `
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
${slots
.map(
slot => `
<tr>
<td class="nowrap">${slot.name ? `<code>${escapeHtml(slot.name)}</code>` : '(default)'}</td>
<td>${escapeHtml(slot.description)}</td>
</tr>
`
)
.join('')}
</tbody>
`;
return table.outerHTML;
}
function createCustomPropertiesTable(styles) {
const table = document.createElement('table');
table.classList.add('metadata-table');
table.innerHTML = `
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Default</th>
</tr>
</thead>
<tbody>
${styles
.map(
style => `
<tr>
<td class="nowrap"><code>${escapeHtml(style.name)}</code></td>
<td>${escapeHtml(style.description)}</td>
<td>${style.default ? `<code>${escapeHtml(style.default)}</code>` : ''}</td>
</tr>
`
)
.join('')}
</tbody>
`;
return table.outerHTML;
}
function createPartsTable(parts) {
const table = document.createElement('table');
table.classList.add('metadata-table');
table.innerHTML = `
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
${parts
.map(
part => `
<tr>
<td class="nowrap"><code>${escapeHtml(part.name)}</code></td>
<td>${escapeHtml(part.description)}</td>
</tr>
`
)
.join('')}
</tbody>
`;
return table.outerHTML;
}
function createAnimationsTable(animations) {
const table = document.createElement('table');
table.classList.add('metadata-table');
table.innerHTML = `
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
${animations
.map(
animation => `
<tr>
<td class="nowrap"><code>${escapeHtml(animation.name)}</code></td>
<td>${escapeHtml(animation.description)}</td>
</tr>
`
)
.join('')}
</tbody>
`;
return table.outerHTML;
}
function createDependenciesList(targetComponent, allComponents) {
const ul = document.createElement('ul');
const dependencies = [];
// Recursively fetch sub-dependencies
function getDependencies(tag) {
const component = allComponents.find(c => c.tagName === tag);
if (!component || !Array.isArray(component.dependencies)) {
return;
}
component.dependencies?.forEach(dependentTag => {
if (!dependencies.includes(dependentTag)) {
dependencies.push(dependentTag);
}
getDependencies(dependentTag);
});
}
getDependencies(targetComponent);
dependencies.sort().forEach(tag => {
const li = document.createElement('li');
li.innerHTML = `<code>&lt;${tag}&gt;</code>`;
ul.appendChild(li);
});
return ul.outerHTML;
}
function escapeHtml(html) {
if (!html) {
return '';
}
return html
.toString()
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;')
.replace(/`(.*?)`/g, '<code>$1</code>');
}
function getAllComponents(metadata) {
const allComponents = [];
metadata.modules?.forEach(module => {
module.declarations?.forEach(declaration => {
if (declaration.customElement) {
// Generate the dist path based on the src path and attach it to the component
declaration.path = module.path.replace(/^src\//, 'dist/').replace(/\.ts$/, '.js');
allComponents.push(declaration);
}
});
});
return allComponents;
}
function getComponent(metadata, tagName) {
return getAllComponents(metadata).find(component => component.tagName === tagName);
}
if (!window.$docsify) {
throw new Error('Docsify must be loaded before installing this plugin.');
}
window.$docsify.plugins.push(hook => {
hook.mounted(async () => {
const metadata = await customElements;
const target = document.querySelector('.app-name');
// Add version
const version = document.createElement('div');
version.classList.add('sidebar-version');
version.textContent = isDev ? 'Development' : isNext ? 'Next' : metadata.package.version;
target.appendChild(version);
// Store version for reuse
sessionStorage.setItem('sl-version', metadata.package.version);
// Add repo buttons
const buttons = document.createElement('div');
buttons.classList.add('sidebar-buttons');
buttons.innerHTML = `
<sl-button size="small" class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
<sl-icon slot="prefix" name="heart"></sl-icon> Sponsor
</sl-button>
<sl-button size="small" class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
<sl-icon slot="prefix" name="github"></sl-icon> <span class="github-star-count">Star</span>
</sl-button>
<sl-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
</sl-button>
`;
target.appendChild(buttons);
});
hook.beforeEach(async (content, next) => {
const metadata = await customElements;
// Replace %VERSION% placeholders
content = content.replace(/%VERSION%/g, metadata.package.version);
// Handle [component-header] tags
content = content.replace(/\[component-header:([a-z-]+)\]/g, (match, tag) => {
const component = getComponent(metadata, tag);
let result = '';
if (!component) {
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';
}
result += `
<div class="component-header">
<div class="component-header__tag">
<code>&lt;${component.tagName}&gt; | ${component.name}</code>
</div>
<div class="component-header__info">
<sl-badge variant="neutral" pill>
Since ${component.since || '?'}
</sl-badge>
<sl-badge variant="${badgeType}" pill style="text-transform: capitalize;">
${component.status}
</sl-badge>
</div>
</div>
`;
return result.replace(/^ +| +$/gm, '');
});
// Handle [component-metadata] tags
content = content.replace(/\[component-metadata:([a-z-]+)\]/g, (match, tag) => {
const component = getComponent(metadata, tag);
let result = '';
if (!component) {
console.error(`Component not found in metadata: ${tag}`);
return next(content);
}
// Remove members that are private or don't have a description
const members = component.members?.filter(member => member.description && member.privacy !== 'private');
const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private');
const props = members?.filter(prop => {
// Look for a corresponding attribute
const attribute = component.attributes?.find(attr => attr.fieldName === prop.name);
if (attribute) {
prop.attribute = attribute.name || attribute.fieldName;
}
return prop.kind === 'field' && prop.privacy !== 'private';
});
if (component.path) {
/* prettier-ignore */
result += `
## Importing
<sl-tab-group>
<sl-tab slot="nav" panel="script">Script</sl-tab>
<sl-tab slot="nav" panel="import">Import</sl-tab>
<sl-tab slot="nav" panel="bundler">Bundler</sl-tab>
<sl-tab slot="nav" panel="react">React</sl-tab>
<sl-tab-panel name="script">\n
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) using a script tag:
\`\`\`html
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/${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}';
\`\`\`
</sl-tab-panel>
<sl-tab-panel name="bundler">\n
To import this component using [a bundler](/getting-started/installation#bundling):
\`\`\`js
import '@shoelace-style/shoelace/${component.path}';
\`\`\`
</sl-tab-panel>
<sl-tab-panel name="react">\n
To import this component as a [React component](/frameworks/react):
\`\`\`js
import { ${component.name} } from '@shoelace-style/shoelace/dist/react';
\`\`\`
</sl-tab-panel>
</sl-tab-group>
`;
}
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))}
`;
}
// Strip whitespace so markdown doesn't process things as code blocks
return result.replace(/^ +| +$/gm, '');
});
next(content);
});
// Wrap tables so we can scroll them horizontally when needed
hook.doneEach(() => {
const content = document.querySelector('.content');
const tables = [...content.querySelectorAll('table')];
tables.forEach(table => {
table.outerHTML = `
<div class="table-wrapper">
${table.outerHTML}
</div>
`;
});
});
});
})();

View File

@@ -1,24 +0,0 @@
(() => {
if (!window.$docsify) {
throw new Error('Docsify must be loaded before installing this plugin.');
}
//
// 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 => {
hook.ready(() => {
// Restore
const scrollTop = sessionStorage.getItem('bs-scroll');
if (scrollTop) {
document.documentElement.scrollTop = scrollTop;
}
// Remember
document.addEventListener('scroll', () => {
sessionStorage.setItem('bs-scroll', document.documentElement.scrollTop);
});
});
});
})();

File diff suppressed because it is too large Load Diff

View File

@@ -1,180 +0,0 @@
body.site-search-visible {
overflow: hidden;
}
.sidebar .search-box {
margin: 1.25rem 26px;
}
.sidebar .search-box kbd {
margin-top: 2px;
margin-right: 1rem;
}
/* Site search */
.site-search {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
}
.site-search[hidden] {
display: none;
}
.site-search__overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--sl-overlay-background-color);
z-index: -1;
}
.site-search__panel {
display: flex;
flex-direction: column;
max-width: 460px;
max-height: calc(100vh - 20rem);
background-color: var(--sl-panel-background-color);
border-radius: var(--sl-border-radius-large);
box-shadow: var(--sl-shadow-x-large);
margin: 10rem auto;
}
@media screen and (max-width: 900px) {
.site-search__panel {
max-width: 100%;
max-height: calc(92vh - 120px); /* allow iOS browser chrome */
margin: 4vh var(--sl-spacing-medium);
}
}
.site-search__input::part(base) {
border: none;
background: transparent;
border-radius: var(--sl-border-radius-large);
}
.site-search__input:focus-within::part(base) {
outline: none;
box-shadow: none;
}
.site-search__input {
--sl-input-height-large: 4rem;
}
.site-search__body {
flex: 1 1 auto;
overflow: auto;
}
.site-search--has-results .site-search__body {
border-top: solid 1px var(--sl-color-neutral-200);
}
.site-search__results {
display: none;
line-height: var(--sl-line-height-dense);
list-style: none;
padding: var(--sl-spacing-x-small) 0;
margin: 0;
}
.site-search--has-results .site-search__results {
display: block;
}
.site-search__results a {
display: block;
text-decoration: none;
padding: var(--sl-spacing-x-small) var(--sl-spacing-large);
}
.site-search__results li a:hover,
.site-search__results li a:hover small {
background-color: var(--sl-color-neutral-100);
}
.site-search__results li[aria-selected='true'] a,
.site-search__results li[aria-selected='true'] a small,
.site-search__results li[aria-selected='true'] a sl-icon {
outline: none;
color: var(--sl-color-neutral-0);
background-color: var(--sl-color-primary-600);
}
.sl-theme-dark .site-search__results li[aria-selected='true'] a,
.sl-theme-dark .site-search__results li[aria-selected='true'] a small,
.sl-theme-dark .site-search__results li[aria-selected='true'] a sl-icon {
background-color: var(--sl-color-primary-400);
color: var(--sl-color-neutral-1000);
}
.site-search__results h3 {
font-weight: var(--sl-font-weight-semibold);
margin: 0;
}
.site-search__results small {
display: block;
color: var(--sl-color-neutral-600);
}
.site-search__result {
padding: 0;
margin: 0;
}
.site-search__result a {
display: flex;
align-items: center;
gap: var(--sl-spacing-medium);
}
.site-search__result-icon {
flex: 0 0 auto;
display: flex;
color: var(--sl-color-neutral-400);
font-size: var(--sl-font-size-x-large);
}
.site-search__result-description {
flex: 1 1 auto;
}
.site-search__empty {
display: none;
border-top: solid 1px var(--sl-color-neutral-200);
text-align: center;
padding: var(--sl-spacing-x-large);
}
.site-search--no-results .site-search__empty {
display: block;
}
.site-search__footer {
display: flex;
justify-content: center;
gap: var(--sl-spacing-large);
border-top: solid 1px var(--sl-color-neutral-200);
border-bottom-left-radius: inherit;
border-bottom-right-radius: inherit;
padding: var(--sl-spacing-medium);
}
.site-search__footer small {
color: var(--sl-color-neutral-700);
}
@media screen and (max-width: 900px) {
.site-search__footer {
display: none;
}
}

View File

@@ -1,322 +0,0 @@
/* global lunr */
(() => {
if (!window.$docsify) {
throw new Error('Docsify must be loaded before installing this plugin.');
}
window.$docsify.plugins.push(hook => {
// Append the search box to the sidebar
hook.mounted(() => {
const appName = document.querySelector('.sidebar .app-name');
const searchBox = document.createElement('div');
searchBox.classList.add('search-box');
searchBox.innerHTML = `
<sl-input
type="search"
placeholder="Search"
clearable
pill
>
<sl-icon slot="prefix" name="search"></sl-icon>
<kbd slot="suffix" title="Press / to search">/</kbd>
</sl-input>
`;
const searchBoxInput = searchBox.querySelector('sl-input');
appName.insertAdjacentElement('afterend', searchBox);
// Show the search panel when the search is clicked
searchBoxInput.addEventListener('mousedown', event => {
event.preventDefault();
show();
});
// Show the search panel when a key is pressed
searchBoxInput.addEventListener('keydown', event => {
if (event.key === 'Tab') {
return;
}
// Pass the character that was typed through to the search input
if (event.key.length === 1) {
event.preventDefault();
input.value = event.key;
show();
}
});
});
// Append the search panel to the body
const siteSearch = document.createElement('div');
siteSearch.classList.add('site-search');
siteSearch.hidden = true;
siteSearch.innerHTML = `
<div class="site-search__overlay"></div>
<div
id="site-search-panel"
class="site-search__panel"
role="combobox"
aria-expanded="false"
aria-owns="site-search-results"
aria-activedescendant=""
>
<header class="site-search__header">
<sl-input
class="site-search__input"
type="search"
placeholder="Search this site"
aria-autocomplete="list"
aria-controls="site-search-results"
size="large"
clearable
>
<sl-icon slot="prefix" name="search"></sl-icon>
</sl-input>
</header>
<div class="site-search__body">
<ul
id="site-search-results"
class="site-search__results"
role="listbox"
aria-labelledby="site-search-panel"
>
</ul>
<div class="site-search__empty">No results found.</div>
</div>
<footer class="site-search__footer">
<small><kbd>↑</kbd> <kbd>↓</kbd> navigate</small>
<small><kbd>↲</kbd> select</small>
<small><kbd>esc</kbd> close</small>
</footer>
</div>
`;
document.body.append(siteSearch);
const overlay = siteSearch.querySelector('.site-search__overlay');
const panel = siteSearch.querySelector('.site-search__panel');
const input = siteSearch.querySelector('.site-search__input');
const results = siteSearch.querySelector('.site-search__results');
const animationDuration = 150;
const searchDebounce = 200;
let isShowing = false;
let searchTimeout;
let searchIndex;
let map;
// Load search data
fetch('../../../search.json')
.then(res => res.json())
.then(data => {
searchIndex = lunr.Index.load(data.searchIndex);
map = data.map;
});
async function show() {
isShowing = true;
document.body.classList.add('site-search-visible');
siteSearch.hidden = false;
requestAnimationFrame(() => input.focus());
updateResults();
await Promise.all([
panel.animate(
[
{ opacity: 0, transform: 'scale(.9)' },
{ opacity: 1, transform: 'scale(1)' }
],
{ duration: animationDuration }
).finished,
overlay.animate([{ opacity: 0 }, { opacity: 1 }], { duration: animationDuration }).finished
]);
document.addEventListener('mousedown', handleDocumentMouseDown);
document.addEventListener('keydown', handleDocumentKeyDown);
document.addEventListener('focusin', handleDocumentFocusIn);
}
async function hide() {
isShowing = false;
document.body.classList.remove('site-search-visible');
await Promise.all([
panel.animate(
[
{ opacity: 1, transform: 'scale(1)' },
{ opacity: 0, transform: 'scale(.9)' }
],
{ duration: animationDuration }
).finished,
overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: animationDuration }).finished
]);
siteSearch.hidden = true;
input.value = '';
updateResults();
document.removeEventListener('mousedown', handleDocumentMouseDown);
document.removeEventListener('keydown', handleDocumentKeyDown);
document.removeEventListener('focusin', handleDocumentFocusIn);
}
function handleInput() {
// Debounce search queries
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
}
function handleDocumentFocusIn(event) {
// Close when focus leaves the panel
if (event.target.closest('.site-search__panel') !== panel) {
hide();
}
}
function handleDocumentMouseDown(event) {
// Close when clicking outside of the panel
if (event.target.closest('.site-search__overlay') === overlay) {
hide();
}
}
function handleDocumentKeyDown(event) {
// Close when pressing escape
if (event.key === 'Escape') {
event.preventDefault();
hide();
return;
}
// Handle keyboard selections
if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter'].includes(event.key)) {
event.preventDefault();
const currentEl = results.querySelector('[aria-selected="true"]');
const items = [...results.querySelectorAll('li')];
const index = items.indexOf(currentEl);
let nextEl;
if (items.length === 0) {
return;
}
switch (event.key) {
case 'ArrowUp':
nextEl = items[Math.max(0, index - 1)];
break;
case 'ArrowDown':
nextEl = items[Math.min(items.length - 1, index + 1)];
break;
case 'Home':
nextEl = items[0];
break;
case 'End':
nextEl = items[items.length - 1];
break;
case 'Enter':
currentEl?.querySelector('a')?.click();
break;
}
// Update the selected item
items.forEach(item => {
if (item === nextEl) {
const a = nextEl.querySelector('a');
panel.setAttribute('aria-activedescendant', a.id);
item.setAttribute('aria-selected', 'true');
nextEl.scrollIntoView({ block: 'nearest' });
} else {
item.setAttribute('aria-selected', 'false');
}
});
}
}
async function updateResults(query = '') {
try {
await searchIndex;
const hasQuery = query.length > 0;
const searchTokens = query
.split(' ')
.map((term, index, arr) => `${term}${index === arr.length - 1 ? `* ${term}~1` : '~1'}`)
.join(' ');
const matches = hasQuery ? searchIndex.search(`${query} ${searchTokens}`) : [];
const hasResults = hasQuery && matches.length > 0;
siteSearch.classList.toggle('site-search--has-results', hasQuery && hasResults);
siteSearch.classList.toggle('site-search--no-results', hasQuery && !hasResults);
panel.setAttribute('aria-expanded', hasQuery && hasResults ? 'true' : 'false');
results.innerHTML = '';
matches.forEach((match, index) => {
const page = map[match.ref];
const li = document.createElement('li');
const a = document.createElement('a');
let icon = 'file-text';
a.setAttribute('role', 'option');
a.setAttribute('id', `search-result-item-${match.ref}`);
if (page.url.includes('getting-started/')) {
icon = 'lightbulb';
}
if (page.url.includes('resources/')) {
icon = 'book';
}
if (page.url.includes('components/')) {
icon = 'puzzle';
}
if (page.url.includes('tokens/')) {
icon = 'palette2';
}
if (page.url.includes('utilities/')) {
icon = 'wrench';
}
if (page.url.includes('tutorials/')) {
icon = 'joystick';
}
a.href = window.$docsify.routerMode === 'hash' ? `/#/${page.url}` : `/${page.url}`;
a.innerHTML = `
<div class="site-search__result-icon">
<sl-icon name="${icon}" aria-hidden="true"></sl-icon>
</div>
<div class="site-search__result__details">
<h3>${page.title}</h3>
<small>${page.url}</small>
</div>
`;
li.classList.add('site-search__result');
li.setAttribute('aria-selected', index === 0 ? 'true' : 'false');
li.appendChild(a);
results.appendChild(li);
});
} catch {
// Ignore query errors as the user types
}
}
// Show the search panel slash is pressed outside of a form element
document.addEventListener('keydown', event => {
if (
!isShowing &&
event.key === '/' &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
show();
}
});
input.addEventListener('sl-input', handleInput);
// Close when a result is selected
results.addEventListener('click', event => {
if (event.target.closest('a')) {
hide();
}
});
});
})();

View File

@@ -1,29 +0,0 @@
.theme-picker {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 30;
}
.theme-picker:not(:defined) {
display: none;
}
.theme-picker sl-menu-label {
white-space: nowrap;
}
.theme-picker sl-menu-label kbd {
margin-left: 0.5rem;
}
@media screen and (max-width: 768px) {
.theme-picker {
top: 0.5rem;
right: 0.5rem;
}
.theme-picker sl-menu-label {
display: none;
}
}

View File

@@ -1,82 +0,0 @@
(() => {
if (!window.$docsify) {
throw new Error('Docsify must be loaded before installing this plugin.');
}
window.$docsify.plugins.push(hook => {
hook.mounted(() => {
function getTheme() {
return localStorage.getItem('theme') || 'auto';
}
function isDark() {
if (theme === 'auto') {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
return theme === 'dark';
}
function setTheme(newTheme) {
const noTransitions = Object.assign(document.createElement('style'), {
textContent: '* { transition: none !important; }'
});
theme = newTheme;
localStorage.setItem('theme', theme);
// Update the UI
[...menu.querySelectorAll('sl-menu-item')].map(item => (item.checked = item.getAttribute('value') === theme));
menuIcon.name = isDark() ? 'moon' : 'sun';
// Toggle the dark mode class without transitions
document.body.appendChild(noTransitions);
requestAnimationFrame(() => {
document.documentElement.classList.toggle('sl-theme-dark', isDark());
requestAnimationFrame(() => document.body.removeChild(noTransitions));
});
}
let theme = getTheme();
// Generate the theme picker dropdown
const dropdown = document.createElement('sl-dropdown');
dropdown.classList.add('theme-picker');
dropdown.innerHTML = `
<sl-button size="small" pill slot="trigger" caret>
<sl-icon name="sun" label="Select Theme"></sl-icon>
</sl-button>
<sl-menu>
<sl-menu-label>Toggle <kbd>\\</kbd></sl-menu-label>
<sl-menu-item value="light">Light</sl-menu-item>
<sl-menu-item value="dark">Dark</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item value="auto">Auto</sl-menu-item>
</sl-menu>
`;
document.querySelector('.sidebar-toggle').insertAdjacentElement('afterend', dropdown);
// Listen for selections
const menu = dropdown.querySelector('sl-menu');
const menuIcon = dropdown.querySelector('sl-icon');
menu.addEventListener('sl-select', event => setTheme(event.detail.item.value));
// Update the theme when the preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => setTheme(theme));
// Toggle themes when pressing backslash
document.addEventListener('keydown', event => {
if (
event.key === '\\' &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
setTheme(isDark() ? 'light' : 'dark');
}
});
// Set the initial theme and sync the UI
setTheme(theme);
});
});
})();

View File

@@ -0,0 +1,244 @@
(() => {
function convertModuleLinks(html) {
html = html
.replace(/@shoelace-style\/shoelace/g, `https://cdn.skypack.dev/@shoelace-style/shoelace@${shoelaceVersion}`)
.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');
if (script.type === 'module') {
newScript.type = 'module';
newScript.textContent = script.innerHTML;
} else {
newScript.appendChild(document.createTextNode(`(() => { ${script.innerHTML} })();`));
}
script.parentNode.replaceChild(newScript, script);
}
function getFlavor() {
return sessionStorage.getItem('flavor') || 'html';
}
function setFlavor(newFlavor) {
flavor = ['html', 'react'].includes(newFlavor) ? newFlavor : 'html';
sessionStorage.setItem('flavor', flavor);
// Set the flavor class on the body
document.documentElement.classList.toggle('flavor-html', flavor === 'html');
document.documentElement.classList.toggle('flavor-react', flavor === 'react');
}
const shoelaceVersion = document.documentElement.getAttribute('data-shoelace-version');
const reactVersion = '18.2.0';
let flavor = getFlavor();
let count = 1;
// We need the version to open
if (!shoelaceVersion) {
throw new Error('The data-shoelace-version attribute is missing from <html>.');
}
// Sync flavor UI on page load
setFlavor(getFlavor());
document.querySelectorAll('.code-preview__button--html').forEach(preview => {
if (flavor === 'html') {
preview.classList.add('code-preview__button--selected');
}
});
document.querySelectorAll('.code-preview__button--react').forEach(preview => {
if (flavor === 'react') {
preview.classList.add('code-preview__button--selected');
}
});
//
// Resizing previews
//
document.addEventListener('mousedown', handleResizerDrag);
document.addEventListener('touchstart', handleResizerDrag, { passive: true });
function handleResizerDrag(event) {
const resizer = event.target.closest('.code-preview__resizer');
const preview = event.target.closest('.code-preview__preview');
if (!resizer || !preview) return;
let startX = event.changedTouches ? event.changedTouches[0].pageX : event.clientX;
let startWidth = parseInt(document.defaultView.getComputedStyle(preview).width, 10);
event.preventDefault();
preview.classList.add('code-preview__preview--dragging');
document.documentElement.addEventListener('mousemove', dragMove);
document.documentElement.addEventListener('touchmove', dragMove);
document.documentElement.addEventListener('mouseup', dragStop);
document.documentElement.addEventListener('touchend', dragStop);
function dragMove(event) {
const width = startWidth + (event.changedTouches ? event.changedTouches[0].pageX : event.pageX) - startX;
preview.style.width = `${width}px`;
}
function dragStop() {
preview.classList.remove('code-preview__preview--dragging');
document.documentElement.removeEventListener('mousemove', dragMove);
document.documentElement.removeEventListener('touchmove', dragMove);
document.documentElement.removeEventListener('mouseup', dragStop);
document.documentElement.removeEventListener('touchend', dragStop);
}
}
//
// Toggle source mode
//
document.addEventListener('click', event => {
const button = event.target.closest('.code-preview__button');
const codeBlock = button?.closest('.code-preview');
if (button?.classList.contains('code-preview__button--html')) {
// Show HTML
setFlavor('html');
toggleSource(codeBlock, true);
} else if (button?.classList.contains('code-preview__button--react')) {
// Show React
setFlavor('react');
toggleSource(codeBlock, true);
} else if (button?.classList.contains('code-preview__toggle')) {
// Toggle source
toggleSource(codeBlock);
} else {
return;
}
// Update flavor buttons
[...document.querySelectorAll('.code-preview')].forEach(cb => {
cb.querySelector('.code-preview__button--html')?.classList.toggle(
'code-preview__button--selected',
flavor === 'html'
);
cb.querySelector('.code-preview__button--react')?.classList.toggle(
'code-preview__button--selected',
flavor === 'react'
);
});
});
function toggleSource(codeBlock, force) {
const toggle = codeBlock.querySelector('.code-preview__toggle');
if (toggle) {
codeBlock.classList.toggle('code-preview--expanded', force === undefined ? undefined : force);
event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-preview--expanded'));
}
}
//
// Open in CodePen
//
document.addEventListener('click', event => {
const button = event.target.closest('button');
if (button?.classList.contains('code-preview__button--codepen')) {
const codeBlock = button.closest('.code-preview');
const htmlExample = codeBlock.querySelector('.code-preview__source--html > pre > code')?.textContent;
const reactExample = codeBlock.querySelector('.code-preview__source--react > pre > code')?.textContent;
const isReact = flavor === 'react' && typeof reactExample === 'string';
const theme = document.documentElement.classList.contains('sl-theme-dark') ? 'dark' : 'light';
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@${shoelaceVersion}/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@${shoelaceVersion}/dist/utilities/base-path';\n` +
`\n` +
`// Set the base path for Shoelace assets\n` +
`setBasePath('https://cdn.skypack.dev/@shoelace-style/shoelace@${shoelaceVersion}/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@${shoelaceVersion}/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.documentElement.append(form);
form.submit();
form.remove();
}
});
})();

245
docs/assets/scripts/docs.js Normal file
View File

@@ -0,0 +1,245 @@
//
// Sidebar
//
// When the sidebar is hidden, we apply the inert attribute to prevent focus from reaching it. Due to the many states
// the sidebar can have (e.g. static, hidden, expanded), we test for visibility by checking to see if it's placed
// offscreen or not. Then, on resize/transition we make sure to update the attribute accordingly.
//
(() => {
function getSidebar() {
return document.getElementById('sidebar');
}
function isSidebarOpen() {
return document.documentElement.classList.contains('sidebar-open');
}
function isSidebarVisible() {
return getSidebar().getBoundingClientRect().x >= 0;
}
function toggleSidebar(force) {
const isOpen = typeof force === 'boolean' ? force : !isSidebarOpen();
return document.documentElement.classList.toggle('sidebar-open', isOpen);
}
function updateInert() {
getSidebar().inert = !isSidebarVisible();
}
// Toggle the menu
document.addEventListener('click', event => {
const menuToggle = event.target.closest('#menu-toggle');
console.log(event.target, menuToggle);
if (!menuToggle) return;
toggleSidebar();
});
// Update the sidebar's inert state when the window resizes and when the sidebar transitions
window.addEventListener('resize', () => toggleSidebar(false));
document.addEventListener('transitionend', event => {
const sidebar = event.target.closest('#sidebar');
if (!sidebar) return;
updateInert();
});
// Close when a menu item is selected on mobile
document.addEventListener('click', event => {
const sidebar = event.target.closest('#sidebar');
const link = event.target.closest('a');
if (!sidebar || !link) return;
if (isSidebarOpen()) {
toggleSidebar();
}
});
// Close when open and escape is pressed
document.addEventListener('keydown', event => {
if (event.key === 'Escape' && isSidebarOpen()) {
event.stopImmediatePropagation();
toggleSidebar();
}
});
// Close when clicking outside of the sidebar
document.addEventListener('mousedown', event => {
if (isSidebarOpen() & !event.target?.closest('#sidebar, #menu-toggle')) {
event.stopImmediatePropagation();
toggleSidebar();
}
});
updateInert();
})();
//
// Theme switcher
//
(() => {
function toggleTheme() {
const isDark = !document.documentElement.classList.contains('sl-theme-dark');
document.documentElement.classList.toggle('sl-theme-dark', isDark);
localStorage.setItem('theme', isDark ? 'dark' : 'light');
}
// Toggle the theme
document.addEventListener('click', event => {
const themeToggle = event.target.closest('#theme-toggle');
if (!themeToggle) return;
toggleTheme();
});
// Toggle with backslash
document.addEventListener('keydown', event => {
if (
event.key === '\\' &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
toggleTheme();
}
});
})();
//
// Open details when printing
//
(() => {
const detailsOpenOnPrint = new Set();
window.addEventListener('beforeprint', () => {
detailsOpenOnPrint.clear();
document.querySelectorAll('details').forEach(details => {
if (details.open) {
detailsOpenOnPrint.add(details);
}
details.open = true;
});
});
window.addEventListener('afterprint', () => {
document.querySelectorAll('details').forEach(details => {
details.open = detailsOpenOnPrint.has(details);
});
detailsOpenOnPrint.clear();
});
})();
//
// Copy code buttons
//
(() => {
document.addEventListener('click', event => {
const button = event.target.closest('.copy-code-button');
const pre = button?.closest('pre');
const code = pre?.querySelector('code');
const copyIcon = button?.querySelector('.copy-code-button__copy-icon');
const copiedIcon = button?.querySelector('.copy-code-button__copied-icon');
if (button && code) {
navigator.clipboard.writeText(code.innerText);
copyIcon.style.display = 'none';
copiedIcon.style.display = 'inline';
button.classList.add('copy-code-button--copied');
setTimeout(() => {
copyIcon.style.display = 'inline';
copiedIcon.style.display = 'none';
button.classList.remove('copy-code-button--copied');
}, 1000);
}
});
})();
//
// Smooth links
//
(() => {
document.addEventListener('click', event => {
const link = event.target.closest('a');
const id = (link?.hash ?? '').substr(1);
const isFragment = link?.hasAttribute('href') && link?.getAttribute('href').startsWith('#');
if (!link || !isFragment || link.getAttribute('data-smooth-link') === 'false') {
return;
}
// Scroll to the top
if (link.hash === '') {
event.preventDefault();
window.scroll({ top: 0, behavior: 'smooth' });
history.pushState(undefined, undefined, location.pathname);
}
// Scroll to an id
if (id) {
const target = document.getElementById(id);
if (target) {
event.preventDefault();
window.scroll({ top: target.offsetTop, behavior: 'smooth' });
history.pushState(undefined, undefined, `#${id}`);
}
}
});
})();
//
// Table of Contents scrollspy
//
(() => {
// This will be stale if its not a function.
const getLinks = () => [...document.querySelectorAll('.content__toc a')];
const linkTargets = new WeakMap();
const visibleTargets = new WeakSet();
const observer = new IntersectionObserver(handleIntersect, { rootMargin: '0px 0px' });
let debounce;
function handleIntersect(entries) {
entries.forEach(entry => {
// Remember which targets are visible
if (entry.isIntersecting) {
visibleTargets.add(entry.target);
} else {
visibleTargets.delete(entry.target);
}
});
updateActiveLinks();
}
function updateActiveLinks() {
const links = getLinks()
// Find the first visible target and activate the respective link
links.find(link => {
const target = linkTargets.get(link);
if (target && visibleTargets.has(target)) {
links.forEach(el => el.classList.toggle('active', el === link));
return true;
}
return false;
});
}
// Observe link targets
function observeLinks () {
getLinks().forEach(link => {
const hash = link.hash.slice(1);
const target = hash ? document.querySelector(`.content__body #${hash}`) : null;
if (target) {
linkTargets.set(link, target);
observer.observe(target);
}
});
}
observeLinks()
document.addEventListener("turbo:load", updateActiveLinks)
document.addEventListener("turbo:load", observeLinks)
})();

View File

@@ -0,0 +1,369 @@
(() => {
// Append the search dialog to the body
const siteSearch = document.createElement('div');
const scrollbarWidth = Math.abs(window.innerWidth - document.documentElement.clientWidth);
siteSearch.classList.add('search');
siteSearch.innerHTML = `
<div class="search__overlay"></div>
<dialog id="search-dialog" class="search__dialog">
<div class="search__content">
<div class="search__header">
<div id="search-combobox" class="search__input-wrapper">
<sl-icon name="search"></sl-icon>
<input
id="search-input"
class="search__input"
type="search"
placeholder="Search"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
enterkeyhint="go"
spellcheck="false"
maxlength="100"
role="combobox"
aria-autocomplete="list"
aria-expanded="true"
aria-controls="search-listbox"
aria-haspopup="listbox"
aria-activedescendant
>
<button type="button" class="search__clear-button" aria-label="Clear entry" tabindex="-1" hidden>
<sl-icon name="x-circle-fill"></sl-icon>
</button>
</div>
</div>
<div class="search__body">
<ul
id="search-listbox"
class="search__results"
role="listbox"
aria-label="Search results"
></ul>
<div class="search__empty">No matching pages</div>
</div>
<footer class="search__footer">
<small><kbd>↑</kbd> <kbd>↓</kbd> Navigate</small>
<small><kbd>↲</kbd> Select</small>
<small><kbd>Esc</kbd> Close</small>
</footer>
</div>
</dialog>
`;
const overlay = siteSearch.querySelector('.search__overlay');
const dialog = siteSearch.querySelector('.search__dialog');
const input = siteSearch.querySelector('.search__input');
const clearButton = siteSearch.querySelector('.search__clear-button');
const results = siteSearch.querySelector('.search__results');
const version = document.documentElement.getAttribute('data-shoelace-version');
const animationDuration = 150;
const searchDebounce = 50;
let isShowing = false;
let searchTimeout;
let searchIndex;
let map;
const loadSearchIndex = new Promise(resolve => {
const key = `search_${version}`;
const cache = localStorage.getItem(key);
const wait = 'requestIdleCallback' in window ? requestIdleCallback : requestAnimationFrame;
// Cleanup older search indices (everything before this version)
try {
const items = { ...localStorage };
Object.keys(items).forEach(k => {
if (key > k) {
localStorage.removeItem(k);
}
});
} catch {
/* do nothing */
}
// Look for a cached index
try {
if (cache) {
const data = JSON.parse(cache);
searchIndex = window.lunr.Index.load(data.searchIndex);
map = data.map;
return resolve();
}
} catch {
/* do nothing */
}
// Wait until idle to fetch the index
wait(() => {
fetch('/assets/search.json')
.then(res => res.json())
.then(data => {
if (!window.lunr) {
console.error('The Lunr search client has not yet been loaded.');
}
searchIndex = window.lunr.Index.load(data.searchIndex);
map = data.map;
// Cache the search index for this version
if (version) {
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (err) {
console.warn(`Unable to cache the search index: ${err}`);
}
}
resolve();
});
});
});
async function show() {
isShowing = true;
document.body.append(siteSearch);
document.body.classList.add('search-visible');
document.body.style.setProperty('--docs-search-scroll-lock-size', `${scrollbarWidth}px`);
clearButton.hidden = true;
requestAnimationFrame(() => input.focus());
updateResults();
dialog.showModal();
await Promise.all([
dialog.animate(
[
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' },
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' }
],
{ duration: animationDuration }
).finished,
overlay.animate([{ opacity: 0 }, { opacity: 1 }], { duration: animationDuration }).finished
]);
dialog.addEventListener('mousedown', handleMouseDown);
dialog.addEventListener('keydown', handleKeyDown);
}
async function hide() {
isShowing = false;
await Promise.all([
dialog.animate(
[
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' },
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' }
],
{ duration: animationDuration }
).finished,
overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: animationDuration }).finished
]);
dialog.close();
input.blur(); // otherwise Safari will scroll to the bottom of the page on close
input.value = '';
document.body.classList.remove('search-visible');
document.body.style.removeProperty('--docs-search-scroll-lock-size');
siteSearch.remove();
updateResults();
dialog.removeEventListener('mousedown', handleMouseDown);
dialog.removeEventListener('keydown', handleKeyDown);
}
function handleInput() {
clearButton.hidden = input.value === '';
// Debounce search queries
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
}
function handleClear() {
clearButton.hidden = true;
input.value = '';
input.focus();
updateResults();
}
function handleMouseDown(event) {
if (!event.target.closest('.search__content')) {
hide();
}
}
function handleKeyDown(event) {
// Close when pressing escape
if (event.key === 'Escape') {
event.preventDefault(); // prevent <dialog> from closing immediately so it can animate
event.stopImmediatePropagation();
hide();
return;
}
// Handle keyboard selections
if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter'].includes(event.key)) {
event.preventDefault();
const currentEl = results.querySelector('[data-selected="true"]');
const items = [...results.querySelectorAll('li')];
const index = items.indexOf(currentEl);
let nextEl;
if (items.length === 0) {
return;
}
switch (event.key) {
case 'ArrowUp':
nextEl = items[Math.max(0, index - 1)];
break;
case 'ArrowDown':
nextEl = items[Math.min(items.length - 1, index + 1)];
break;
case 'Home':
nextEl = items[0];
break;
case 'End':
nextEl = items[items.length - 1];
break;
case 'Enter':
currentEl?.querySelector('a')?.click();
break;
}
// Update the selected item
items.forEach(item => {
if (item === nextEl) {
input.setAttribute('aria-activedescendant', item.id);
item.setAttribute('data-selected', 'true');
nextEl.scrollIntoView({ block: 'nearest' });
} else {
item.setAttribute('data-selected', 'false');
}
});
}
}
async function updateResults(query = '') {
try {
await loadSearchIndex;
const hasQuery = query.length > 0;
const searchTerms = query
.split(' ')
.map((term, index, arr) => {
// Search API: https://lunrjs.com/guides/searching.html
if (index === arr.length - 1) {
// The last term is not mandatory and 1x fuzzy. We also duplicate it with a wildcard to match partial words
// as the user types.
return `${term}~1 ${term}*`;
} else {
// All other terms are mandatory and 1x fuzzy
return `+${term}~1`;
}
})
.join(' ');
const matches = hasQuery ? searchIndex.search(searchTerms) : [];
const hasResults = hasQuery && matches.length > 0;
siteSearch.classList.toggle('search--has-results', hasQuery && hasResults);
siteSearch.classList.toggle('search--no-results', hasQuery && !hasResults);
input.setAttribute('aria-activedescendant', '');
results.innerHTML = '';
matches.forEach((match, index) => {
const page = map[match.ref];
const li = document.createElement('li');
const a = document.createElement('a');
const displayTitle = page.title ?? '';
const displayDescription = page.description ?? '';
const displayUrl = page.url.replace(/^\//, '');
let icon = 'file-text';
a.setAttribute('role', 'option');
a.setAttribute('id', `search-result-item-${match.ref}`);
if (page.url.includes('getting-started/')) {
icon = 'lightbulb';
}
if (page.url.includes('resources/')) {
icon = 'book';
}
if (page.url.includes('components/')) {
icon = 'puzzle';
}
if (page.url.includes('tokens/')) {
icon = 'palette2';
}
if (page.url.includes('utilities/')) {
icon = 'wrench';
}
if (page.url.includes('tutorials/')) {
icon = 'joystick';
}
li.classList.add('search__result');
li.setAttribute('role', 'option');
li.setAttribute('id', `search-result-item-${match.ref}`);
li.setAttribute('data-selected', index === 0 ? 'true' : 'false');
a.href = page.url;
a.innerHTML = `
<div class="search__result-icon" aria-hidden="true">
<sl-icon name="${icon}"></sl-icon>
</div>
<div class="search__result__details">
<div class="search__result-title"></div>
<div class="search__result-description"></div>
<div class="search__result-url"></div>
</div>
`;
a.querySelector('.search__result-title').textContent = displayTitle;
a.querySelector('.search__result-description').textContent = displayDescription;
a.querySelector('.search__result-url').textContent = displayUrl;
li.appendChild(a);
results.appendChild(li);
});
} catch {
// Ignore query errors as the user types
}
}
// Show the search dialog when clicking on data-plugin="search"
document.addEventListener('click', event => {
const searchButton = event.target.closest('[data-plugin="search"]');
if (searchButton) {
show();
}
});
// Show the search dialog when slash (or CMD+K) is pressed and focus is not inside a form element
document.addEventListener('keydown', event => {
if (
!isShowing &&
(event.key === '/' || (event.key === 'k' && (event.metaKey || event.ctrlKey))) &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
show();
}
});
input.addEventListener('input', handleInput);
clearButton.addEventListener('click', handleClear);
// Close when a result is selected
results.addEventListener('click', event => {
if (event.target.closest('a')) {
hide();
}
});
})();

View File

@@ -0,0 +1,29 @@
import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@7.3.0/+esm';
(() => {
if (!window.scrollPositions) {
window.scrollPositions = {};
}
function preserveScroll() {
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
scrollPositions[element.id] = element.scrollTop;
});
}
function restoreScroll(event) {
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
element.scrollTop = scrollPositions[element.id];
});
if (event.detail && event.detail.newBody) {
event.detail.newBody.querySelectorAll('[data-preserve-scroll').forEach(element => {
element.scrollTop = scrollPositions[element.id];
});
}
}
window.addEventListener('turbo:before-cache', preserveScroll);
window.addEventListener('turbo:before-render', restoreScroll);
window.addEventListener('turbo:render', restoreScroll);
})();

View File

@@ -1,11 +1,12 @@
.code-block {
/* Interactive code blocks */
.code-preview {
position: relative;
border-radius: 3px;
background-color: var(--sl-color-neutral-50);
margin-bottom: 1.5rem;
}
.code-block__preview {
.code-preview__preview {
position: relative;
border: solid 1px var(--sl-color-neutral-200);
border-bottom: none;
@@ -18,7 +19,7 @@
}
/* Block the preview while dragging to prevent iframes from intercepting drag events */
.code-block__preview--dragging:after {
.code-preview__preview--dragging:after {
content: '';
position: absolute;
top: 0;
@@ -29,7 +30,7 @@
cursor: ew-resize;
}
.code-block__resizer {
.code-preview__resizer {
display: flex;
align-items: center;
justify-content: center;
@@ -44,35 +45,34 @@
border-left: solid 1px var(--sl-color-neutral-200);
border-top-right-radius: 3px;
cursor: ew-resize;
transition: 250ms background-color;
}
@media screen and (max-width: 600px) {
.code-block__preview {
.code-preview__preview {
padding-right: 1.5rem;
}
.code-block__resizer {
.code-preview__resizer {
display: none;
}
}
.code-block__source {
.code-preview__source {
border: solid 1px var(--sl-color-neutral-200);
border-bottom: none;
border-radius: 0 !important;
display: none;
}
.code-block--expanded .code-block__source {
.code-preview--expanded .code-preview__source {
display: block;
}
.code-block__source pre {
.code-preview__source pre {
margin: 0;
}
.code-block__buttons {
.code-preview__buttons {
position: relative;
border: solid 1px var(--sl-color-neutral-200);
border-bottom-left-radius: 3px;
@@ -80,7 +80,7 @@
display: flex;
}
.code-block__button {
.code-preview__button {
flex: 0 0 auto;
height: 2.5rem;
min-width: 2.5rem;
@@ -96,39 +96,39 @@
cursor: pointer;
}
.code-block__button:not(:last-of-type) {
.code-preview__button:not(:last-of-type) {
border-right: solid 1px var(--sl-color-neutral-200);
}
.code-block__button--html,
.code-block__button--react {
.code-preview__button--html,
.code-preview__button--react {
width: 70px;
display: flex;
place-items: center;
justify-content: center;
}
.code-block__button--selected {
.code-preview__button--selected {
font-weight: 700;
color: var(--sl-color-primary-600);
}
.code-block__button--codepen {
.code-preview__button--codepen {
display: flex;
place-items: center;
width: 6rem;
}
.code-block__button:first-of-type {
.code-preview__button:first-of-type {
border-bottom-left-radius: 3px;
}
.code-block__button:last-of-type {
.code-preview__button:last-of-type {
border-bottom-right-radius: 3px;
}
.code-block__button:hover,
.code-block__button:active {
.code-preview__button:hover,
.code-preview__button:active {
box-shadow: 0 0 0 1px var(--sl-color-primary-400);
border-right-color: transparent;
background-color: var(--sl-color-primary-50);
@@ -136,17 +136,13 @@
z-index: 1;
}
.code-block__button:focus-visible {
.code-preview__button:focus-visible {
outline: none;
color: var(--sl-color-primary-600);
border-color: var(--sl-color-primary-400);
border-right-color: transparent;
background-color: var(--sl-color-primary-50);
box-shadow: 0 0 0 1px var(--sl-color-primary-400), var(--sl-focus-ring);
outline: var(--sl-focus-ring);
z-index: 2;
}
.code-block__toggle {
.code-preview__toggle {
position: relative;
display: flex;
flex: 1 1 auto;
@@ -155,74 +151,23 @@
width: 100%;
color: var(--sl-color-neutral-600);
cursor: pointer;
-webkit-appearance: none;
}
.code-block__toggle svg {
.code-preview__toggle svg {
width: 1em;
height: 1em;
margin-left: 0.25rem;
}
.code-block--expanded .code-block__toggle svg {
.code-preview--expanded .code-preview__toggle svg {
transform: rotate(180deg);
}
/* Copy button styles */
.markdown-section .docsify-copy-code-button {
top: 4px;
right: 4px;
background-color: var(--sl-color-neutral-500);
border-radius: var(--sl-border-radius-medium);
font-family: var(--sl-font-sans);
font-size: var(--sl-font-size-x-small);
font-weight: var(--sl-font-weight-semibold);
text-transform: uppercase;
padding: 8px;
user-select: none;
transition: 0.1s all;
}
.markdown-section .docsify-copy-code-button.copied {
animation: pulse 0.75s;
--pulse-color: var(--sl-color-neutral-600);
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 var(--pulse-color);
}
70% {
box-shadow: 0 0 0 0.5rem transparent;
}
100% {
box-shadow: 0 0 0 0 transparent;
}
}
.markdown-section .docsify-copy-code-button .label {
transition: none;
}
.markdown-section .docsify-copy-code-button .success,
.markdown-section .docsify-copy-code-button .error {
/* We can apply data-flavor="html|react" to any element on the page to toggle it when the flavor changes */
.flavor-html [data-flavor]:not([data-flavor='html']) {
display: none;
}
.markdown-section .docsify-copy-code-button:focus-visible {
box-shadow: var(--sl-focus-ring);
}
.markdown-section .docsify-copy-code-button:active {
background-color: var(--sl-color-neutral-600);
transform: scale(0.92);
}
/* We can apply data-flavor="html|react" to any element on the page to toggle it when the user's flavor changes */
body.flavor-html [data-flavor]:not([data-flavor='html']) {
display: none;
}
body.flavor-react [data-flavor]:not([data-flavor='react']) {
.flavor-react [data-flavor]:not([data-flavor='react']) {
display: none;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,347 @@
/* Search plugin */
:root,
:root.sl-theme-dark {
--docs-search-box-background: var(--sl-color-neutral-0);
--docs-search-box-border-width: 1px;
--docs-search-box-border-color: var(--sl-color-neutral-300);
--docs-search-box-color: var(--sl-color-neutral-600);
--docs-search-dialog-background: var(--sl-color-neutral-0);
--docs-search-border-width: var(--docs-border-width);
--docs-search-border-color: var(--docs-border-color);
--docs-search-text-color: var(--sl-color-neutral-900);
--docs-search-text-color-muted: var(--sl-color-neutral-500);
--docs-search-font-weight-normal: var(--sl-font-weight-normal);
--docs-search-font-weight-semibold: var(--sl-font-weight-semibold);
--docs-search-border-radius: calc(2 * var(--docs-border-radius));
--docs-search-accent-color: var(--sl-color-primary-600);
--docs-search-icon-color: var(--sl-color-neutral-500);
--docs-search-icon-color-active: var(--sl-color-neutral-600);
--docs-search-shadow: var(--docs-shadow-x-large);
--docs-search-result-background-hover: var(--sl-color-neutral-100);
--docs-search-result-color-hover: var(--sl-color-neutral-900);
--docs-search-result-background-active: var(--sl-color-primary-600);
--docs-search-result-color-active: var(--sl-color-neutral-0);
--docs-search-focus-ring: var(--sl-focus-ring);
--docs-search-overlay-background: rgb(0 0 0 / 0.33);
}
:root.sl-theme-dark {
--docs-search-overlay-background: rgb(71 71 71 / 0.33);
}
body.search-visible {
padding-right: var(--docs-search-scroll-lock-size) !important;
overflow: hidden !important;
}
/* Search box */
.search-box {
flex: 1 1 auto;
display: flex;
align-items: center;
width: 100%;
border: none;
border-radius: 9999px;
background: var(--docs-search-box-background);
border: solid var(--docs-search-box-border-width) var(--docs-search-box-border-color);
font: inherit;
color: var(--docs-search-box-color);
padding: 0.75rem 1rem;
margin: var(--sl-spacing-large) 0;
cursor: pointer;
}
.search-box span {
flex: 1 1 auto;
width: 1rem;
height: 1rem;
text-align: left;
line-height: 1;
margin: 0 0.75rem;
}
.search-box:focus {
outline: none;
}
.search-box:focus-visible {
outline: var(--docs-search-focus-ring);
}
/* Site search */
.search {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
}
.search[hidden] {
display: none;
}
.search__overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--docs-search-overlay-background);
z-index: -1;
}
.search__dialog {
width: 100%;
height: 100%;
max-width: none;
max-height: none;
background: transparent;
border: none;
padding: 0;
margin: 0;
}
.search__dialog:focus {
outline: none;
}
.search__dialog::backdrop {
display: none;
}
/* Fixes an iOS Safari 16.4 bug that draws the parent element's border radius incorrectly when showing/hiding results */
.search__header {
background-color: var(--docs-search-dialog-background);
border-radius: var(--docs-search-border-radius);
}
.search--has-results .search__header {
border-top-left-radius: var(--docs-search-border-radius);
border-top-right-radius: var(--docs-search-border-radius);
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.search__content {
display: flex;
flex-direction: column;
width: 100%;
max-width: 500px;
max-height: calc(100vh - 20rem);
background-color: var(--docs-search-dialog-background);
border-radius: var(--docs-search-border-radius);
box-shadow: var(--docs-search-shadow);
padding: 0;
margin: 10rem auto;
}
@media screen and (max-width: 900px) {
.search__content {
max-width: calc(100% - 2rem);
max-height: calc(90svh);
margin: 4vh 1rem;
}
}
.search__input-wrapper {
display: flex;
align-items: center;
}
.search__input-wrapper sl-icon {
width: 1.5rem;
height: 1.5rem;
flex: 0 0 auto;
color: var(--docs-search-icon-color);
margin: 0 1.5rem;
}
.search__clear-button {
display: flex;
background: none;
border: none;
font: inherit;
padding: 0;
margin: 0;
cursor: pointer;
}
.search__clear-button[hidden] {
display: none;
}
.search__clear-button:active sl-icon {
color: var(--docs-search-icon-color-active);
}
.search__input {
flex: 1 1 auto;
min-width: 0;
border: none;
font: inherit;
font-size: 1.5rem;
font-weight: var(--docs-search-font-weight-normal);
color: var(--docs-search-text-color);
background: transparent;
padding: 1rem 0;
margin: 0;
}
.search__input::placeholder {
color: var(--docs-search-text-color-muted);
}
.search__input::-webkit-search-decoration,
.search__input::-webkit-search-cancel-button,
.search__input::-webkit-search-results-button,
.search__input::-webkit-search-results-decoration {
display: none;
}
.search__input:focus,
.search__input:focus-visible {
outline: none;
}
.search__body {
flex: 1 1 auto;
overflow: auto;
}
.search--has-results .search__body {
border-top: solid var(--docs-search-border-width) var(--docs-search-border-color);
}
.search__results {
display: none;
line-height: 1.2;
list-style: none;
padding: 0.5rem 0;
margin: 0;
}
.search--has-results .search__results {
display: block;
}
.search__results a {
display: block;
text-decoration: none;
padding: 0.5rem 1.5rem;
}
.search__results a:focus-visible {
outline: var(--docs-search-focus-ring);
}
.search__results li a:hover,
.search__results li a:hover small {
background-color: var(--docs-search-result-background-hover);
color: var(--docs-search-result-color-hover);
}
.search__results li[data-selected='true'] a,
.search__results li[data-selected='true'] a * {
outline: none;
background-color: var(--docs-search-result-background-active);
color: var(--docs-search-result-color-active);
}
.search__results h3 {
font-weight: var(--docs-search-font-weight-semibold);
margin: 0;
}
.search__results small {
display: block;
color: var(--docs-search-text-color-muted);
}
.search__result {
padding: 0;
margin: 0;
}
.search__result a {
display: flex;
align-items: center;
gap: 1rem;
}
.search__result-icon {
flex: 0 0 auto;
display: flex;
color: var(--docs-search-text-color-muted);
}
.search__result-icon sl-icon {
font-size: 1.5rem;
}
.search__result__details {
width: calc(100% - 3rem);
}
.search__result-title,
.search__result-description,
.search__result-url {
max-width: 400px;
line-height: 1.3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.search__result-title {
font-size: 1.2rem;
font-weight: var(--docs-search-font-weight-semibold);
color: var(--docs-search-accent-color);
}
.search__result-description {
font-size: 0.875rem;
color: var(--docs-search-text-color);
}
.search__result-url {
font-size: 0.875rem;
color: var(--docs-search-text-color-muted);
}
.search__empty {
display: none;
border-top: solid var(--docs-search-border-width) var(--docs-search-border-color);
text-align: center;
color: var(--docs-search-text-color-muted);
padding: 2rem;
}
.search--no-results .search__empty {
display: block;
}
.search__footer {
display: flex;
justify-content: center;
gap: 2rem;
border-top: solid var(--docs-search-border-width) var(--docs-search-border-color);
border-bottom-left-radius: inherit;
border-bottom-right-radius: inherit;
padding: 1rem;
}
.search__footer small {
color: var(--docs-search-text-color-muted);
}
.search__footer small kbd:last-of-type {
margin-right: 0.25rem;
}
@media screen and (max-width: 900px) {
.search__footer {
display: none;
}
}

View File

@@ -1,490 +0,0 @@
# 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>
);
```
### Variants
Use the `variant` attribute to set the button's variant.
```html preview
<sl-radio-group label="Select an option">
<sl-radio-button variant="default" name="option" value="1" checked>Option 1</sl-radio-button>
<sl-radio-button variant="default" name="option" value="2">Option 2</sl-radio-button>
<sl-radio-button variant="default" name="option" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option">
<sl-radio-button variant="primary" name="option" value="1" checked>Option 1</sl-radio-button>
<sl-radio-button variant="primary" name="option" value="2">Option 2</sl-radio-button>
<sl-radio-button variant="primary" name="option" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option">
<sl-radio-button variant="success" name="option" value="1" checked>Option 1</sl-radio-button>
<sl-radio-button variant="success" name="option" value="2">Option 2</sl-radio-button>
<sl-radio-button variant="success" name="option" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option">
<sl-radio-button variant="neutral" name="option" value="1" checked>Option 1</sl-radio-button>
<sl-radio-button variant="neutral" name="option" value="2">Option 2</sl-radio-button>
<sl-radio-button variant="neutral" name="option" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option">
<sl-radio-button variant="warning" name="option" value="1" checked>Option 1</sl-radio-button>
<sl-radio-button variant="warning" name="option" value="2">Option 2</sl-radio-button>
<sl-radio-button variant="warning" name="option" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option">
<sl-radio-button variant="success" name="option" value="1" checked>Option 1</sl-radio-button>
<sl-radio-button variant="success" name="option" value="2">Option 2</sl-radio-button>
<sl-radio-button variant="success" 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 variant="default" name="option" value="1" checked>Option 1</SlRadioButton>
<SlRadioButton variant="default" name="option" value="2">Option 2</SlRadioButton>
<SlRadioButton variant="default" name="option" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option">
<SlRadioButton variant="primary" name="option" value="1" checked>Option 1</SlRadioButton>
<SlRadioButton variant="primary" name="option" value="2">Option 2</SlRadioButton>
<SlRadioButton variant="primary" name="option" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option">
<SlRadioButton variant="success" name="option" value="1" checked>Option 1</SlRadioButton>
<SlRadioButton variant="success" name="option" value="2">Option 2</SlRadioButton>
<SlRadioButton variant="success" name="option" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option">
<SlRadioButton variant="neutral" name="option" value="1" checked>Option 1</SlRadioButton>
<SlRadioButton variant="neutral" name="option" value="2">Option 2</SlRadioButton>
<SlRadioButton variant="neutral" name="option" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option">
<SlRadioButton variant="warning" name="option" value="1" checked>Option 1</SlRadioButton>
<SlRadioButton variant="warning" name="option" value="2">Option 2</SlRadioButton>
<SlRadioButton variant="warning" name="option" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option">
<SlRadioButton variant="success" name="option" value="1" checked>Option 1</SlRadioButton>
<SlRadioButton variant="success" name="option" value="2">Option 2</SlRadioButton>
<SlRadioButton variant="success" name="option" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Sizes
Use the `size` attribute to change a radio button's size.
```html preview
<sl-radio-group label="Select an option">
<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]

View File

@@ -1,95 +0,0 @@
# Radio Group
[component-header:sl-radio-group]
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 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 Label
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 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]

View File

@@ -1,172 +0,0 @@
# Radio
[component-header:sl-radio]
Radios allow the user to select a single option from a group.
Radios are designed to be used with [radio groups](/components/radio-group).
```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>
);
```
?> 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">
<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]

View File

@@ -1,140 +0,0 @@
# Rating
[component-header:sl-rating]
Ratings give users a way to quickly view and provide feedback.
```html preview
<sl-rating></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlRating />;
```
## Examples
### Maximum Value
Ratings are 0-5 by default. To change the maximum possible value, use the `max` attribute.
```html preview
<sl-rating max="3"></sl-rating>
```
```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="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
Set the `--symbol-size` custom property to adjust the size.
```html preview
<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.
```html preview
<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.
```html preview
<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>';
</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 => {
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]

View File

@@ -1,91 +0,0 @@
# Responsive Media
[component-header:sl-responsive-media]
Displays media in the desired aspect ratio.
You can slot in any [replaced element](https://developer.mozilla.org/en-US/docs/Web/CSS/Replaced_element), including `<iframe>`, `<img>`, and `<video>`. As the element's width changes, its height will resize proportionally. Only one element should be slotted into the container. The default aspect ratio is `16:9`.
```html preview
<sl-responsive-media>
<img
src="https://images.unsplash.com/photo-1541427468627-a89a96e5ca1d?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1800&q=80"
alt="A train riding through autumn foliage with mountains in the distance."
/>
</sl-responsive-media>
```
```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
The following image maintains a `4:3` aspect ratio as its container is resized.
```html preview
<sl-responsive-media aspect-ratio="4:3">
<img
src="https://images.unsplash.com/photo-1473186578172-c141e6798cf4?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1800&q=80"
alt="Two blue chairs on a sandy beach."
/>
</sl-responsive-media>
```
```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
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]

View File

@@ -1,504 +0,0 @@
# Select
[component-header:sl-select]
Selects allow you to choose one or more items from a dropdown menu.
```html preview
<sl-select>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item value="option-4">Option 4</sl-menu-item>
<sl-menu-item value="option-5">Option 5</sl-menu-item>
<sl-menu-item value="option-6">Option 6</sl-menu-item>
</sl-select>
```
```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.
```html preview
<sl-select placeholder="Select one">
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
</sl-select>
```
```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.
```html preview
<sl-select placeholder="Clearable" clearable>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
</sl-select>
```
```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.
```html preview
<sl-select filled>
<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 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.
```html preview
<sl-select pill>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
</sl-select>
```
```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.
```html preview
<sl-select placeholder="Disabled" disabled>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
</sl-select>
```
```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. 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>
<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-divider></sl-divider>
<sl-menu-item value="option-4">Option 4</sl-menu-item>
<sl-menu-item value="option-5">Option 5</sl-menu-item>
<sl-menu-item value="option-6">Option 6</sl-menu-item>
</sl-select>
```
```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.
```html preview
<sl-select placeholder="Select one">
<sl-menu-label>Group 1</sl-menu-label>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-label>Group 2</sl-menu-label>
<sl-menu-item value="option-4">Option 4</sl-menu-item>
<sl-menu-item value="option-5">Option 5</sl-menu-item>
<sl-menu-item value="option-6">Option 6</sl-menu-item>
</sl-select>
```
```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.
```html preview
<sl-select placeholder="Small" size="small" multiple>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
</sl-select>
<br />
<sl-select placeholder="Medium" size="medium" multiple>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
</sl-select>
<br />
<sl-select placeholder="Large" size="large" multiple>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
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>
<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>
<br />
<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>
</>
);
```
### Placement
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 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>
```
```jsx react
import {
SlMenuItem,
SlSelect
} from '@shoelace-style/shoelace/dist/react';
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
Use the `prefix` and `suffix` slots to add icons.
```html preview
<sl-select placeholder="Small" size="small">
<sl-icon name="house" slot="prefix"></sl-icon>
<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-icon name="chat" slot="suffix"></sl-icon>
</sl-select>
<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>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-icon name="chat" slot="suffix"></sl-icon>
</sl-select>
<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>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-icon name="chat" slot="suffix"></sl-icon>
</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]

View File

@@ -1,71 +0,0 @@
# Switch
[component-header:sl-switch]
Switches allow the user to toggle an option on or off.
```html preview
<sl-switch>Switch</sl-switch>
```
```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
### Checked
Use the `checked` attribute to activate the switch.
```html preview
<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.
```html preview
<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;">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]

226
docs/eleventy.config.cjs Normal file
View File

@@ -0,0 +1,226 @@
/* eslint-disable no-invalid-this */
const fs = require('fs');
const path = require('path');
const lunr = require('lunr');
const { capitalCase } = require('change-case');
const { JSDOM } = require('jsdom');
const { customElementsManifest, getAllComponents } = require('./_utilities/cem.cjs');
const shoelaceFlavoredMarkdown = require('./_utilities/markdown.cjs');
const activeLinks = require('./_utilities/active-links.cjs');
const anchorHeadings = require('./_utilities/anchor-headings.cjs');
const codePreviews = require('./_utilities/code-previews.cjs');
const copyCodeButtons = require('./_utilities/copy-code-buttons.cjs');
const externalLinks = require('./_utilities/external-links.cjs');
const highlightCodeBlocks = require('./_utilities/highlight-code.cjs');
const tableOfContents = require('./_utilities/table-of-contents.cjs');
const prettier = require('./_utilities/prettier.cjs');
const scrollingTables = require('./_utilities/scrolling-tables.cjs');
const typography = require('./_utilities/typography.cjs');
const assetsDir = 'assets';
const allComponents = getAllComponents();
let hasBuiltSearchIndex = false;
module.exports = function (eleventyConfig) {
//
// Global data
//
eleventyConfig.addGlobalData('baseUrl', 'https://shoelace.style/'); // the production URL
eleventyConfig.addGlobalData('layout', 'default'); // make 'default' the default layout
eleventyConfig.addGlobalData('toc', true); // enable the table of contents
eleventyConfig.addGlobalData('meta', {
title: 'Shoelace',
description: 'A forward-thinking library of web components.',
image: 'images/og-image.png',
version: customElementsManifest.package.version,
components: allComponents
});
//
// Layout aliases
//
eleventyConfig.addLayoutAlias('default', 'default.njk');
//
// Copy assets
//
eleventyConfig.addPassthroughCopy(assetsDir);
eleventyConfig.setServerPassthroughCopyBehavior('passthrough'); // emulates passthrough copy during --serve
//
// Functions
//
// Generates a URL relative to the site's root
eleventyConfig.addNunjucksGlobal('rootUrl', (value = '', absolute = false) => {
value = path.join('/', value);
return absolute ? new URL(value, eleventyConfig.globalData.baseUrl).toString() : value;
});
// Generates a URL relative to the site's asset directory
eleventyConfig.addNunjucksGlobal('assetUrl', (value = '', absolute = false) => {
value = path.join(`/${assetsDir}`, value);
return absolute ? new URL(value, eleventyConfig.globalData.baseUrl).toString() : value;
});
// Fetches a specific component's metadata
eleventyConfig.addNunjucksGlobal('getComponent', tagName => {
const component = allComponents.find(c => c.tagName === tagName);
if (!component) {
throw new Error(
`Unable to find a component called "${tagName}". Make sure the file name is the same as the component's tag ` +
`name (minus the sl- prefix).`
);
}
return component;
});
//
// Custom markdown syntaxes
//
eleventyConfig.setLibrary('md', shoelaceFlavoredMarkdown);
//
// Filters
//
eleventyConfig.addFilter('markdown', content => {
return shoelaceFlavoredMarkdown.render(content);
});
eleventyConfig.addFilter('markdownInline', content => {
return shoelaceFlavoredMarkdown.renderInline(content);
});
eleventyConfig.addFilter('classNameToComponentName', className => {
let name = capitalCase(className.replace(/^Sl/, ''));
if (name === 'Qr Code') name = 'QR Code'; // manual override
return name;
});
eleventyConfig.addFilter('removeSlPrefix', tagName => {
return tagName.replace(/^sl-/, '');
});
//
// Transforms
//
eleventyConfig.addTransform('html-transform', function (content) {
// Parse the template and get a Document object
const doc = new JSDOM(content, {
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
// identify which ones are internal and which ones are external.
url: `https://internal/`
}).window.document;
// DOM transforms
activeLinks(doc, { pathname: this.page.url });
anchorHeadings(doc, {
within: '#content .content__body',
levels: ['h2', 'h3', 'h4', 'h5']
});
tableOfContents(doc, {
levels: ['h2', 'h3'],
container: '#content .content__toc > ul',
within: '#content .content__body'
});
codePreviews(doc);
externalLinks(doc, { target: '_blank' });
highlightCodeBlocks(doc);
scrollingTables(doc);
copyCodeButtons(doc); // must be after codePreviews + highlightCodeBlocks
typography(doc, '#content');
// Serialize the Document object to an HTML string and prepend the doctype
content = `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
// String transforms
content = prettier(content);
return content;
});
//
// Build a search index
//
eleventyConfig.on('eleventy.after', ({ results }) => {
// We only want to build the search index on the first run so all pages get indexed.
if (hasBuiltSearchIndex) {
return;
}
const map = {};
const searchIndexFilename = path.join(eleventyConfig.dir.output, assetsDir, 'search.json');
const lunrInput = path.resolve('../node_modules/lunr/lunr.min.js');
const lunrOutput = path.join(eleventyConfig.dir.output, assetsDir, 'scripts/lunr.js');
const searchIndex = lunr(function () {
// The search index uses these field names extensively, so shortening them can save some serious bytes. The
// initial index file went from 468 KB => 401 KB by using single-character names!
this.ref('id'); // id
this.field('t', { boost: 50 }); // title
this.field('h', { boost: 25 }); // headings
this.field('c'); // content
results.forEach((result, index) => {
const url = path.join('/', path.relative(eleventyConfig.dir.output, result.outputPath)).replace(/\\/g, '/');
const doc = new JSDOM(result.content, {
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
// identify which ones are internal and which ones are external.
url: `https://internal/`
}).window.document;
const content = doc.querySelector('#content');
// Get title and headings
const title = (doc.querySelector('title')?.textContent || path.basename(result.outputPath)).trim();
const headings = [...content.querySelectorAll('h1, h2, h3, h4')]
.map(heading => heading.textContent)
.join(' ')
.replace(/\s+/g, ' ')
.trim();
// Remove code blocks and whitespace from content
[...content.querySelectorAll('code[class|=language]')].forEach(code => code.remove());
const textContent = content.textContent.replace(/\s+/g, ' ').trim();
// Update the index and map
this.add({ id: index, t: title, h: headings, c: textContent });
map[index] = { title, url };
});
});
// Copy the Lunr search client and write the index
fs.mkdirSync(path.dirname(lunrOutput), { recursive: true });
fs.copyFileSync(lunrInput, lunrOutput);
fs.writeFileSync(searchIndexFilename, JSON.stringify({ searchIndex, map }), 'utf-8');
hasBuiltSearchIndex = true;
});
//
// Send a signal to stdout that let's the build know we've reached this point
//
eleventyConfig.on('eleventy.after', () => {
console.log('[eleventy.after]');
});
//
// Dev server options (see https://www.11ty.dev/docs/dev-server/#options)
//
eleventyConfig.setServerOptions({
domDiff: false, // disable dom diffing so custom elements don't break on reload,
port: 4000, // if port 4000 is taken, 11ty will use the next one available
watch: ['dist/**/*'] // additional files to watch that will trigger server updates (array of paths or globs)
});
//
// 11ty config
//
return {
dir: {
input: 'pages',
output: '../_site',
includes: '../_includes' // resolved relative to the input dir
},
markdownTemplateEngine: 'njk', // use Nunjucks instead of Liquid for markdown files
templateEngineOverride: ['njk'] // just Nunjucks and then markdown
};
};

View File

@@ -1,338 +0,0 @@
# 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="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="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="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="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>
```

View File

@@ -1,96 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Shoelace: A forward-thinking library of web components.</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta
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, initial-scale=1" />
<meta name="custom-elements-manifest" content="dist/custom-elements.json" />
<meta property="og:title" content="Shoelace" />
<meta
property="og:description"
content="Shoelace provides a collection of professionally designed, every day UI components built on a framework-agnostic technology."
/>
<meta property="og:type" content="website" />
<meta property="og:url" content="https://shoelace.style/" />
<meta property="og:image" content="https://shoelace.style/assets/images/og-image.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@shoelace_style" />
<meta name="twitter:creator" content="@claviska" />
<meta name="twitter:title" content="Shoelace" />
<meta
name="twitter:description"
content="Shoelace provides a collection of professionally designed, every day UI components built on a framework-agnostic technology."
/>
<meta name="twitter:image" content="https://shoelace.style/assets/images/twitter-card.png" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify@4/themes/pure.css" />
<link rel="stylesheet" href="/assets/styles/docs.css" />
<link rel="stylesheet" href="/assets/plugins/code-block/code-block.css" />
<link rel="stylesheet" href="/assets/plugins/search/search.css" />
<link rel="stylesheet" href="/assets/plugins/theme-picker/theme-picker.css" />
<link rel="icon" href="/assets/images/logo.svg" type="image/x-icon" />
<link rel="apple-touch-icon" sizes="180x180" href="/assets/images/touch-icon.png" />
<!-- Import Shoelace -->
<link rel="stylesheet" href="/dist/themes/light.css" />
<link rel="stylesheet" href="/dist/themes/dark.css" />
<script type="module" src="/dist/shoelace.js"></script>
</head>
<body data-shoelace="/dist">
<div id="app"></div>
<script>
// Set the initial theme to prevent flashing
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const theme = localStorage.getItem('theme') || 'auto';
document.documentElement.classList.toggle('sl-theme-dark', theme === 'dark' || (theme === 'auto' && prefersDark));
</script>
<script>
window.$docsify = {
alias: {
'/.*/_sidebar.md': '/_sidebar.md'
},
auto2top: true,
copyCode: {
buttonText: 'Copy',
errorText: 'Failed to copy',
successText: 'Copied'
},
coverpage: false,
executeScript: true,
homepage: '/getting-started/overview.md',
loadSidebar: true,
logo: '/assets/images/wordmark.svg',
maxLevel: 3,
subMaxLevel: 2,
name: 'Shoelace',
nameLink: '/',
notFoundPage: '404.md',
pagination: {
previousText: 'Previous',
nextText: 'Next',
crossChapter: true,
crossChapterText: false
},
routerMode: 'history',
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-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.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>
<script src="/assets/plugins/search/lunr.min.js"></script>
<script src="/assets/plugins/search/search.js"></script>
<script src="/assets/plugins/theme-picker/theme-picker.js"></script>
</body>
</html>

19
docs/pages/404.md Normal file
View File

@@ -0,0 +1,19 @@
---
meta:
title: Page Not Found
description: "The page you were looking for couldn't be found."
permalink: 404.html
toc: false
---
<div style="text-align: center;">
# Page Not Found
![A UFO takes one of the little worker monsters](/assets/images/undraw-taken.svg)
The page you were looking for couldn't be found.
Press [[/]] to search, or [head back to the homepage](/).
</div>

View File

@@ -1,17 +1,18 @@
# Alert
---
meta:
title: Alert
description: Alerts are used to display important messages inline or as toast notifications.
layout: component
---
[component-header:sl-alert]
Alerts are used to display important messages inline or as toast notifications.
```html preview
```html:preview
<sl-alert open>
<sl-icon slot="icon" name="info-circle"></sl-icon>
This is a standard alert. You can customize its content and even the icon.
</sl-alert>
```
```jsx react
```jsx:react
import { SlAlert, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -22,7 +23,9 @@ const App = () => (
);
```
?> Alerts will not be visible if the `open` attribute is not present.
:::tip
Alerts will not be visible if the `open` attribute is not present.
:::
## Examples
@@ -30,7 +33,7 @@ const App = () => (
Set the `variant` attribute to change the alert's variant.
```html preview
```html:preview
<sl-alert variant="primary" open>
<sl-icon slot="icon" name="info-circle"></sl-icon>
<strong>This is super informative</strong><br />
@@ -70,7 +73,7 @@ Set the `variant` attribute to change the alert's variant.
</sl-alert>
```
```jsx react
```jsx:react
import { SlAlert, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -125,7 +128,7 @@ const App = () => (
Add the `closable` attribute to show a close button that will hide the alert.
```html preview
```html:preview
<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!
@@ -139,7 +142,7 @@ Add the `closable` attribute to show a close button that will hide the alert.
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlAlert, SlIcon } from '@shoelace-style/shoelace/dist/react';
@@ -164,11 +167,11 @@ const App = () => {
Icons are optional. Simply omit the `icon` slot if you don't want them.
```html preview
```html:preview
<sl-alert variant="primary" open> Nothing fancy here, just a simple alert. </sl-alert>
```
```jsx react
```jsx:react
import { SlAlert } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -182,7 +185,7 @@ const App = () => (
Set the `duration` attribute to automatically hide an alert after a period of time. This is useful for alerts that don't require acknowledgement.
```html preview
```html:preview
<div class="alert-duration">
<sl-button variant="primary">Show Alert</sl-button>
@@ -207,7 +210,7 @@ Set the `duration` attribute to automatically hide an alert after a period of ti
</style>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlAlert, SlButton, SlIcon } from '@shoelace-style/shoelace/dist/react';
@@ -245,7 +248,7 @@ To display an alert as a toast notification, or "toast", create the alert and ca
You should always use the `closable` attribute so users can dismiss the notification. It's also common to set a reasonable `duration` when the notification doesn't require acknowledgement.
```html preview
```html:preview
<div class="alert-toast">
<sl-button variant="primary">Primary</sl-button>
<sl-button variant="success">Success</sl-button>
@@ -296,7 +299,7 @@ You should always use the `closable` attribute so users can dismiss the notifica
</script>
```
```jsx react
```jsx:react
import { useRef } from 'react';
import { SlAlert, SlButton, SlIcon } from '@shoelace-style/shoelace/dist/react';
@@ -376,7 +379,7 @@ const App = () => {
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
```html:preview
<div class="alert-toast-wrapper">
<sl-button variant="primary">Create Toast</sl-button>
</div>
@@ -428,6 +431,6 @@ By default, the toast stack is positioned at the top-right of the viewport. You
}
```
?> By design, it is not possible to show toasts in more than one stack simultaneously. Such behavior is confusing and makes for a poor user experience.
[component-metadata:sl-alert]
:::tip
By design, it is not possible to show toasts in more than one stack simultaneously. Such behavior is confusing and makes for a poor user experience.
:::

View File

@@ -1,17 +1,18 @@
# Animated Image
---
meta:
title: Animated Image
description: A component for displaying animated GIFs and WEBPs that play and pause on interaction.
layout: component
---
[component-header:sl-animated-image]
A component for displaying animated GIFs and WEBPs that play and pause on interaction.
```html preview
```html:preview
<sl-animated-image
src="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
></sl-animated-image>
```
```jsx react
```jsx:react
import { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -22,7 +23,9 @@ const App = () => (
);
```
?> 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).
:::tip
This component uses `<canvas>` to draw freeze frames, so images are subject to [cross-origin restrictions](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image).
:::
## Examples
@@ -30,14 +33,14 @@ const App = () => (
Both GIF and WEBP images are supported.
```html preview
```html:preview
<sl-animated-image
src="https://shoelace.style/assets/images/tie.webp"
alt="Animation of a shoe being tied"
></sl-animated-image>
```
```jsx react
```jsx:react
import { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -49,7 +52,7 @@ const App = () => (
To set a custom size, apply a width and/or height to the host element.
```html preview
```html:preview
<sl-animated-image
src="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
@@ -58,7 +61,9 @@ To set a custom size, apply a width and/or height to the host element.
</sl-animated-image>
```
```jsx react
{% raw %}
```jsx:react
import { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -70,11 +75,13 @@ const App = () => (
);
```
{% endraw %}
### Customizing the Control Box
You can change the appearance and location of the control box by targeting the `control-box` part in your styles.
```html preview
```html:preview
<sl-animated-image
src="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
@@ -94,7 +101,7 @@ You can change the appearance and location of the control box by targeting the `
</style>
```
```jsx react
```jsx:react
import { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -121,5 +128,3 @@ const App = () => (
</>
);
```
[component-metadata:sl-animated-image]

View File

@@ -1,12 +1,13 @@
# Animation
[component-header:sl-animation]
Animate elements declaratively with nearly 100 baked-in presets, or roll your own with custom keyframes. Powered by the [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API).
---
meta:
title: Animation
description: Animate elements declaratively with nearly 100 baked-in presets, or roll your own with custom keyframes.
layout: component
---
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
```html:preview
<div class="animation-overview">
<sl-animation name="bounce" duration="2000" play><div class="box"></div></sl-animation>
<sl-animation name="jello" duration="2000" play><div class="box"></div></sl-animation>
@@ -25,7 +26,7 @@ To animate an element, wrap it in `<sl-animation>` and set an animation `name`.
</style>
```
```jsx react
```jsx:react
import { SlAnimation } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -60,7 +61,9 @@ const App = () => (
);
```
?> The animation will only be applied to the first child element found in `<sl-animation>`.
:::tip
The animation will only be applied to the first child element found in `<sl-animation>`.
:::
## Examples
@@ -68,7 +71,7 @@ const App = () => (
This example demonstrates all of the baked-in animations and easings. Animations are based on those found in the popular [Animate.css](https://animate.style/) library.
```html preview
```html:preview
<div class="animation-sandbox">
<sl-animation name="bounce" easing="ease-in-out" duration="2000" play>
<div class="box"></div>
@@ -93,19 +96,19 @@ This example demonstrates all of the baked-in animations and easings. Animations
const easings = getEasingNames();
animations.map(name => {
const menuItem = Object.assign(document.createElement('sl-menu-item'), {
const option = Object.assign(document.createElement('sl-option'), {
textContent: name,
value: name
});
animationName.appendChild(menuItem);
animationName.appendChild(option);
});
easings.map(name => {
const menuItem = Object.assign(document.createElement('sl-menu-item'), {
const option = Object.assign(document.createElement('sl-option'), {
textContent: name,
value: name
});
easingName.appendChild(menuItem);
easingName.appendChild(option);
});
animationName.addEventListener('sl-change', () => (animation.name = animationName.value));
@@ -135,7 +138,7 @@ This example demonstrates all of the baked-in animations and easings. Animations
Use an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) to control the animation when an element enters or exits the viewport. For example, scroll the box below in and out of your screen. The animation stops when the box exits the viewport and restarts each time it enters the viewport.
```html preview
```html:preview
<div class="animation-scroll">
<sl-animation name="jackInTheBox" duration="2000" iterations="1"><div class="box"></div></sl-animation>
</div>
@@ -168,7 +171,7 @@ Use an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/
</style>
```
```jsx react
```jsx:react
import { useEffect, useRef, useState } from 'react';
import { SlAnimation } from '@shoelace-style/shoelace/dist/react';
@@ -222,7 +225,7 @@ const App = () => {
Supply your own [keyframe formats](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Keyframe_Formats) to build custom animations.
```html preview
```html:preview
<div class="animation-keyframes">
<sl-animation easing="ease-in-out" duration="2000" play>
<div class="box"></div>
@@ -258,7 +261,7 @@ Supply your own [keyframe formats](https://developer.mozilla.org/en-US/docs/Web/
</style>
```
```jsx react
```jsx:react
import { SlAnimation } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -306,7 +309,7 @@ const App = () => (
Animations won't play until you apply the `play` attribute. You can omit it initially, then apply it on demand such as after a user interaction. In this example, the button will animate once every time the button is clicked.
```html preview
```html:preview
<div class="animation-form">
<sl-animation name="rubberBand" duration="1000" iterations="1">
<sl-button variant="primary">Click me</sl-button>
@@ -324,7 +327,7 @@ Animations won't play until you apply the `play` attribute. You can omit it init
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlAnimation, SlButton } from '@shoelace-style/shoelace/dist/react';
@@ -342,5 +345,3 @@ const App = () => {
);
};
```
[component-metadata:sl-animation]

View File

@@ -1,16 +1,17 @@
# Avatar
[component-header:sl-avatar]
Avatars are used to represent a person or object.
---
meta:
title: Avatar
description: Avatars are used to represent a person or object.
layout: component
---
By default, a generic icon will be shown. You can personalize avatars by adding custom icons, initials, and images. You should always provide a `label` for assistive devices.
```html preview
```html:preview
<sl-avatar label="User avatar"></sl-avatar>
```
```jsx react
```jsx:react
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlAvatar label="User avatar" />;
@@ -21,15 +22,21 @@ const App = () => <SlAvatar label="User avatar" />;
### Images
To use an image for the avatar, set the `image` and `label` attributes. This will take priority and be shown over initials and icons.
Avatar images can be lazily loaded by setting the `loading` attribute to `lazy`.
```html preview
```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"
label="Avatar of a gray tabby kitten looking down"
></sl-avatar>
<sl-avatar
image="https://images.unsplash.com/photo-1591871937573-74dbba515c4c?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
label="Avatar of a white and grey kitten on grey textile"
loading="lazy"
></sl-avatar>
```
```jsx react
```jsx:react
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -37,6 +44,11 @@ const App = () => (
image="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
label="Avatar of a gray tabby kitten looking down"
/>
<SlAvatar
image="https://images.unsplash.com/photo-1591871937573-74dbba515c4c?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
label="Avatar of a white and grey kitten on grey textile"
loading="lazy"
/>
);
```
@@ -44,11 +56,11 @@ const App = () => (
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
```html:preview
<sl-avatar initials="SL" label="Avatar with initials: SL"></sl-avatar>
```
```jsx react
```jsx:react
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlAvatar initials="SL" label="Avatar with initials: SL" />;
@@ -58,7 +70,7 @@ const App = () => <SlAvatar initials="SL" label="Avatar with initials: SL" />;
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
```html:preview
<sl-avatar label="Avatar with an image icon">
<sl-icon slot="icon" name="image"></sl-icon>
</sl-avatar>
@@ -72,7 +84,7 @@ When no image or initials are set, an icon will be shown. The default avatar sho
</sl-avatar>
```
```jsx react
```jsx:react
import { SlAvatar, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -96,13 +108,13 @@ const App = () => (
Avatars can be shaped using the `shape` attribute.
```html preview
```html:preview
<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
```jsx:react
import { SlAvatar, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -118,7 +130,7 @@ const App = () => (
You can group avatars with a few lines of CSS.
```html preview
```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"
@@ -152,7 +164,7 @@ You can group avatars with a few lines of CSS.
</style>
```
```jsx react
```jsx:react
import { SlAvatar, SlIcon } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -193,5 +205,3 @@ const App = () => (
</>
);
```
[component-metadata:sl-avatar]

View File

@@ -1,14 +1,15 @@
# Badge
---
meta:
title: Badge
description: Badges are used to draw attention and display statuses or counts.
layout: component
---
[component-header:sl-badge]
Badges are used to draw attention and display statuses or counts.
```html preview
```html:preview
<sl-badge>Badge</sl-badge>
```
```jsx react
```jsx:react
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlBadge>Badge</SlBadge>;
@@ -20,7 +21,7 @@ const App = () => <SlBadge>Badge</SlBadge>;
Set the `variant` attribute to change the badge's variant.
```html preview
```html:preview
<sl-badge variant="primary">Primary</sl-badge>
<sl-badge variant="success">Success</sl-badge>
<sl-badge variant="neutral">Neutral</sl-badge>
@@ -28,7 +29,7 @@ Set the `variant` attribute to change the badge's variant.
<sl-badge variant="danger">Danger</sl-badge>
```
```jsx react
```jsx:react
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -46,7 +47,7 @@ const App = () => (
Use the `pill` attribute to give badges rounded edges.
```html preview
```html:preview
<sl-badge variant="primary" pill>Primary</sl-badge>
<sl-badge variant="success" pill>Success</sl-badge>
<sl-badge variant="neutral" pill>Neutral</sl-badge>
@@ -54,7 +55,7 @@ Use the `pill` attribute to give badges rounded edges.
<sl-badge variant="danger" pill>Danger</sl-badge>
```
```jsx react
```jsx:react
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -82,7 +83,7 @@ const App = () => (
Use the `pulse` attribute to draw attention to the badge with a subtle animation.
```html preview
```html:preview
<div class="badge-pulse">
<sl-badge variant="primary" pill pulse>1</sl-badge>
<sl-badge variant="success" pill pulse>1</sl-badge>
@@ -98,7 +99,7 @@ Use the `pulse` attribute to draw attention to the badge with a subtle animation
</style>
```
```jsx react
```jsx:react
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -136,24 +137,26 @@ const App = () => (
One of the most common use cases for badges is attaching them to buttons. To make this easier, badges will be automatically positioned at the top-right when they're a child of a button.
```html preview
```html:preview
<sl-button>
Requests
<sl-badge pill>30</sl-badge>
</sl-button>
<sl-button style="margin-left: 1rem;">
<sl-button style="margin-inline-start: 1rem;">
Warnings
<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 variant="danger" pill>6</sl-badge>
</sl-button>
```
```jsx react
{% raw %}
```jsx:react
import { SlBadge, SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -163,14 +166,14 @@ const App = () => (
<SlBadge pill>30</SlBadge>
</SlButton>
<SlButton style={{ marginLeft: '1rem' }}>
<SlButton style={{ marginInlineStart: '1rem' }}>
Warnings
<SlBadge variant="warning" pill>
8
</SlBadge>
</SlButton>
<SlButton style={{ marginLeft: '1rem' }}>
<SlButton style={{ marginInlineStart: '1rem' }}>
Errors
<SlBadge variant="danger" pill>
6
@@ -180,11 +183,13 @@ const App = () => (
);
```
{% endraw %}
### With Menu Items
When including badges in menu items, use the `suffix` slot to make sure they're aligned correctly.
```html preview
```html:preview
<sl-menu style="max-width: 240px;">
<sl-menu-label>Messages</sl-menu-label>
<sl-menu-item>Comments <sl-badge slot="suffix" variant="neutral" pill>4</sl-badge></sl-menu-item>
@@ -192,7 +197,9 @@ When including badges in menu items, use the `suffix` slot to make sure they're
</sl-menu>
```
```jsx react
{% raw %}
```jsx:react
import { SlBadge, SlButton, SlMenu, SlMenuItem, SlMenuLabel } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -220,4 +227,4 @@ const App = () => (
);
```
[component-metadata:sl-badge]
{% endraw %}

View File

@@ -1,10 +1,11 @@
# Breadcrumb Item
---
meta:
title: Breadcrumb Item
description: Breadcrumb Items are used inside breadcrumbs to represent different links.
layout: component
---
[component-header:sl-breadcrumb-item]
Breadcrumb Items are used inside [breadcrumbs](/components/breadcrumb) to represent different links.
```html preview
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item>
<sl-icon slot="prefix" name="house"></sl-icon>
@@ -15,7 +16,7 @@ Breadcrumb Items are used inside [breadcrumbs](/components/breadcrumb) to repres
</sl-breadcrumb>
```
```jsx react
```jsx:react
import { SlBreadcrumb, SlBreadcrumbItem, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -30,6 +31,6 @@ const App = () => (
);
```
?> Additional demonstrations can be found in the [breadcrumb examples](/components/breadcrumb).
[component-metadata:sl-breadcrumb-item]
:::tip
Additional demonstrations can be found in the [breadcrumb examples](/components/breadcrumb).
:::

View File

@@ -1,12 +1,13 @@
# Breadcrumb
[component-header:sl-breadcrumb]
Breadcrumbs provide a group of links so users can easily navigate a website's hierarchy.
---
meta:
title: Breadcrumb
description: Breadcrumbs provide a group of links so users can easily navigate a website's hierarchy.
layout: component
---
Breadcrumbs are usually placed before a page's main content with the current page shown last to indicate the user's position in the navigation.
```html preview
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item>Catalog</sl-breadcrumb-item>
<sl-breadcrumb-item>Clothing</sl-breadcrumb-item>
@@ -15,7 +16,7 @@ Breadcrumbs are usually placed before a page's main content with the current pag
</sl-breadcrumb>
```
```jsx react
```jsx:react
import { SlBreadcrumb, SlBreadcrumbItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -36,7 +37,7 @@ By default, breadcrumb items are rendered as buttons so you can use them to navi
For websites, you'll probably want to use links instead. You can make any breadcrumb item a link by applying an `href` attribute to it. Now, when the user activates it, they'll be taken to the corresponding page — no event listeners required.
```html preview
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item href="https://example.com/home">Homepage</sl-breadcrumb-item>
@@ -48,7 +49,7 @@ For websites, you'll probably want to use links instead. You can make any breadc
</sl-breadcrumb>
```
```jsx react
```jsx:react
import { SlBreadcrumb, SlBreadcrumbItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -68,7 +69,7 @@ const App = () => (
Use the `separator` slot to change the separator that goes between breadcrumb items. Icons work well, but you can also use text or an image.
```html preview
```html:preview
<sl-breadcrumb>
<sl-icon name="dot" slot="separator"></sl-icon>
<sl-breadcrumb-item>First</sl-breadcrumb-item>
@@ -95,7 +96,7 @@ Use the `separator` slot to change the separator that goes between breadcrumb it
</sl-breadcrumb>
```
```jsx react
```jsx:react
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
import { SlBreadcrumb, SlBreadcrumbItem } from '@shoelace-style/shoelace/dist/react';
@@ -133,7 +134,7 @@ const App = () => (
Use the `prefix` slot to add content before any breadcrumb item.
```html preview
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item>
<sl-icon slot="prefix" name="house"></sl-icon>
@@ -144,7 +145,7 @@ Use the `prefix` slot to add content before any breadcrumb item.
</sl-breadcrumb>
```
```jsx react
```jsx:react
import { SlBreadcrumb, SlBreadcrumbItem, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -163,7 +164,7 @@ const App = () => (
Use the `suffix` slot to add content after any breadcrumb item.
```html preview
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item>Documents</sl-breadcrumb-item>
<sl-breadcrumb-item>Policies</sl-breadcrumb-item>
@@ -174,7 +175,7 @@ Use the `suffix` slot to add content after any breadcrumb item.
</sl-breadcrumb>
```
```jsx react
```jsx:react
import { SlBreadcrumb, SlBreadcrumbItem, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -193,7 +194,7 @@ const App = () => (
Dropdown menus can be placed in a prefix or suffix slot to provide additional options.
```html preview
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item>Homepage</sl-breadcrumb-item>
<sl-breadcrumb-item>Our Services</sl-breadcrumb-item>
@@ -205,16 +206,16 @@ Dropdown menus can be placed in a prefix or suffix slot to provide additional op
<sl-icon label="More options" name="three-dots"></sl-icon>
</sl-button>
<sl-menu>
<sl-menu-item checked>Web Design</sl-menu-item>
<sl-menu-item>Web Development</sl-menu-item>
<sl-menu-item>Marketing</sl-menu-item>
<sl-menu-item type="checkbox" checked>Web Design</sl-menu-item>
<sl-menu-item type="checkbox">Web Development</sl-menu-item>
<sl-menu-item type="checkbox">Marketing</sl-menu-item>
</sl-menu>
</sl-dropdown>
</sl-breadcrumb-item>
</sl-breadcrumb>
```
```jsx react
```jsx:react
import {
SlBreadcrumb,
SlBreadcrumbItem,
@@ -237,14 +238,14 @@ const App = () => (
<SlIcon label="More options" name="three-dots"></SlIcon>
</SlButton>
<SlMenu>
<SlMenuItem checked>Web Design</SlMenuItem>
<SlMenuItem>Web Development</SlMenuItem>
<SlMenuItem>Marketing</SlMenuItem>
<SlMenuItem type="checkbox" checked>
Web Design
</SlMenuItem>
<SlMenuItem type="checkbox">Web Development</SlMenuItem>
<SlMenuItem type="checkbox">Marketing</SlMenuItem>
</SlMenu>
</SlDropdown>
</SlBreadcrumbItem>
</SlBreadcrumb>
);
```
[component-metadata:sl-breadcrumb]

View File

@@ -1,22 +1,23 @@
# Button Group
---
meta:
title: Button Group
description: Button groups can be used to group related buttons into sections.
layout: component
---
[component-header:sl-button-group]
Button groups can be used to group related buttons into sections.
```html preview
<sl-button-group>
```html:preview
<sl-button-group label="Alignment">
<sl-button>Left</sl-button>
<sl-button>Center</sl-button>
<sl-button>Right</sl-button>
</sl-button-group>
```
```jsx react
```jsx:react
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton>Left</SlButton>
<SlButton>Center</SlButton>
<SlButton>Right</SlButton>
@@ -30,8 +31,8 @@ const App = () => (
All button sizes are supported, but avoid mixing sizes within the same button group.
```html preview
<sl-button-group>
```html:preview
<sl-button-group label="Alignment">
<sl-button size="small">Left</sl-button>
<sl-button size="small">Center</sl-button>
<sl-button size="small">Right</sl-button>
@@ -39,7 +40,7 @@ All button sizes are supported, but avoid mixing sizes within the same button gr
<br /><br />
<sl-button-group>
<sl-button-group label="Alignment">
<sl-button size="medium">Left</sl-button>
<sl-button size="medium">Center</sl-button>
<sl-button size="medium">Right</sl-button>
@@ -47,19 +48,19 @@ All button sizes are supported, but avoid mixing sizes within the same button gr
<br /><br />
<sl-button-group>
<sl-button-group label="Alignment">
<sl-button size="large">Left</sl-button>
<sl-button size="large">Center</sl-button>
<sl-button size="large">Right</sl-button>
</sl-button-group>
```
```jsx react
```jsx:react
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton size="small">Left</SlButton>
<SlButton size="small">Center</SlButton>
<SlButton size="small">Right</SlButton>
@@ -68,7 +69,7 @@ const App = () => (
<br />
<br />
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton size="medium">Left</SlButton>
<SlButton size="medium">Center</SlButton>
<SlButton size="medium">Right</SlButton>
@@ -77,7 +78,7 @@ const App = () => (
<br />
<br />
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton size="large">Left</SlButton>
<SlButton size="large">Center</SlButton>
<SlButton size="large">Right</SlButton>
@@ -88,10 +89,10 @@ const App = () => (
### Theme Buttons
Theme buttons are supported through the button's `type` attribute.
Theme buttons are supported through the button's `variant` attribute.
```html preview
<sl-button-group>
```html:preview
<sl-button-group label="Alignment">
<sl-button variant="primary">Left</sl-button>
<sl-button variant="primary">Center</sl-button>
<sl-button variant="primary">Right</sl-button>
@@ -99,7 +100,7 @@ Theme buttons are supported through the button's `type` attribute.
<br /><br />
<sl-button-group>
<sl-button-group label="Alignment">
<sl-button variant="success">Left</sl-button>
<sl-button variant="success">Center</sl-button>
<sl-button variant="success">Right</sl-button>
@@ -107,7 +108,7 @@ Theme buttons are supported through the button's `type` attribute.
<br /><br />
<sl-button-group>
<sl-button-group label="Alignment">
<sl-button variant="neutral">Left</sl-button>
<sl-button variant="neutral">Center</sl-button>
<sl-button variant="neutral">Right</sl-button>
@@ -115,7 +116,7 @@ Theme buttons are supported through the button's `type` attribute.
<br /><br />
<sl-button-group>
<sl-button-group label="Alignment">
<sl-button variant="warning">Left</sl-button>
<sl-button variant="warning">Center</sl-button>
<sl-button variant="warning">Right</sl-button>
@@ -123,19 +124,19 @@ Theme buttons are supported through the button's `type` attribute.
<br /><br />
<sl-button-group>
<sl-button-group label="Alignment">
<sl-button variant="danger">Left</sl-button>
<sl-button variant="danger">Center</sl-button>
<sl-button variant="danger">Right</sl-button>
</sl-button-group>
```
```jsx react
```jsx:react
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton variant="primary">Left</SlButton>
<SlButton variant="primary">Center</SlButton>
<SlButton variant="primary">Right</SlButton>
@@ -144,7 +145,7 @@ const App = () => (
<br />
<br />
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton variant="success">Left</SlButton>
<SlButton variant="success">Center</SlButton>
<SlButton variant="success">Right</SlButton>
@@ -153,7 +154,7 @@ const App = () => (
<br />
<br />
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton variant="neutral">Left</SlButton>
<SlButton variant="neutral">Center</SlButton>
<SlButton variant="neutral">Right</SlButton>
@@ -162,7 +163,7 @@ const App = () => (
<br />
<br />
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton variant="warning">Left</SlButton>
<SlButton variant="warning">Center</SlButton>
<SlButton variant="warning">Right</SlButton>
@@ -171,7 +172,7 @@ const App = () => (
<br />
<br />
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton variant="danger">Left</SlButton>
<SlButton variant="danger">Center</SlButton>
<SlButton variant="danger">Right</SlButton>
@@ -184,8 +185,8 @@ const App = () => (
Pill buttons are supported through the button's `pill` attribute.
```html preview
<sl-button-group>
```html:preview
<sl-button-group label="Alignment">
<sl-button size="small" pill>Left</sl-button>
<sl-button size="small" pill>Center</sl-button>
<sl-button size="small" pill>Right</sl-button>
@@ -193,7 +194,7 @@ Pill buttons are supported through the button's `pill` attribute.
<br /><br />
<sl-button-group>
<sl-button-group label="Alignment">
<sl-button size="medium" pill>Left</sl-button>
<sl-button size="medium" pill>Center</sl-button>
<sl-button size="medium" pill>Right</sl-button>
@@ -201,19 +202,19 @@ Pill buttons are supported through the button's `pill` attribute.
<br /><br />
<sl-button-group>
<sl-button-group label="Alignment">
<sl-button size="large" pill>Left</sl-button>
<sl-button size="large" pill>Center</sl-button>
<sl-button size="large" pill>Right</sl-button>
</sl-button-group>
```
```jsx react
```jsx:react
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton size="small" pill>
Left
</SlButton>
@@ -228,7 +229,7 @@ const App = () => (
<br />
<br />
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton size="medium" pill>
Left
</SlButton>
@@ -243,7 +244,7 @@ const App = () => (
<br />
<br />
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton size="large" pill>
Left
</SlButton>
@@ -262,8 +263,8 @@ const App = () => (
Dropdowns can be placed inside button groups as long as the trigger is an `<sl-button>` element.
```html preview
<sl-button-group>
```html:preview
<sl-button-group label="Example Button Group">
<sl-button>Button</sl-button>
<sl-button>Button</sl-button>
<sl-dropdown>
@@ -277,11 +278,11 @@ Dropdowns can be placed inside button groups as long as the trigger is an `<sl-b
</sl-button-group>
```
```jsx react
```jsx:react
import { SlButton, SlButtonGroup, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlButtonGroup>
<SlButtonGroup label="Example Button Group">
<SlButton>Button</SlButton>
<SlButton>Button</SlButton>
<SlDropdown>
@@ -302,8 +303,8 @@ const App = () => (
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>
```html:preview
<sl-button-group label="Example Button Group">
<sl-button variant="primary">Save</sl-button>
<sl-dropdown placement="bottom-end">
<sl-button slot="trigger" variant="primary" caret>
@@ -318,11 +319,11 @@ Create a split button using a button and a dropdown. Use a [visually hidden](/co
</sl-button-group>
```
```jsx react
```jsx:react
import { SlButton, SlButtonGroup, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlButtonGroup>
<SlButtonGroup label="Example Button Group">
<SlButton variant="primary">Save</SlButton>
<SlDropdown placement="bottom-end">
<SlButton slot="trigger" variant="primary" caret></SlButton>
@@ -340,8 +341,8 @@ const App = () => (
Buttons can be wrapped in tooltips to provide more detail when the user interacts with them.
```html preview
<sl-button-group>
```html:preview
<sl-button-group label="Alignment">
<sl-tooltip content="I'm on the left">
<sl-button>Left</sl-button>
</sl-tooltip>
@@ -356,12 +357,12 @@ Buttons can be wrapped in tooltips to provide more detail when the user interact
</sl-button-group>
```
```jsx react
```jsx:react
import { SlButton, SlButtonGroup, SlTooltip } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlTooltip content="I'm on the left">
<SlButton>Left</SlButton>
</SlTooltip>
@@ -382,7 +383,7 @@ const App = () => (
Create interactive toolbars with button groups.
```html preview
```html:preview
<div class="button-group-toolbar">
<sl-button-group label="History">
<sl-tooltip content="Undo">
@@ -425,7 +426,7 @@ Create interactive toolbars with button groups.
</style>
```
```jsx react
```jsx:react
import { SlButton, SlButtonGroup, SlIcon, SlTooltip } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -491,5 +492,3 @@ const App = () => (
</>
);
```
[component-metadata:sl-button-group]

View File

@@ -1,14 +1,15 @@
# Button
---
meta:
title: Button
description: Buttons represent actions that are available to the user.
layout: component
---
[component-header:sl-button]
Buttons represent actions that are available to the user.
```html preview
```html:preview
<sl-button>Button</sl-button>
```
```jsx react
```jsx:react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlButton>Button</SlButton>;
@@ -20,7 +21,7 @@ const App = () => <SlButton>Button</SlButton>;
Use the `variant` attribute to set the button's variant.
```html preview
```html:preview
<sl-button variant="default">Default</sl-button>
<sl-button variant="primary">Primary</sl-button>
<sl-button variant="success">Success</sl-button>
@@ -29,7 +30,7 @@ Use the `variant` attribute to set the button's variant.
<sl-button variant="danger">Danger</sl-button>
```
```jsx react
```jsx:react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -48,13 +49,13 @@ const App = () => (
Use the `size` attribute to change a button's size.
```html preview
```html:preview
<sl-button size="small">Small</sl-button>
<sl-button size="medium">Medium</sl-button>
<sl-button size="large">Large</sl-button>
```
```jsx react
```jsx:react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -70,7 +71,7 @@ const App = () => (
Use the `outline` attribute to draw outlined buttons with transparent backgrounds.
```html preview
```html:preview
<sl-button variant="default" outline>Default</sl-button>
<sl-button variant="primary" outline>Primary</sl-button>
<sl-button variant="success" outline>Success</sl-button>
@@ -79,7 +80,7 @@ Use the `outline` attribute to draw outlined buttons with transparent background
<sl-button variant="danger" outline>Danger</sl-button>
```
```jsx react
```jsx:react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -110,13 +111,13 @@ const App = () => (
Use the `pill` attribute to give buttons rounded edges.
```html preview
```html:preview
<sl-button size="small" pill>Small</sl-button>
<sl-button size="medium" pill>Medium</sl-button>
<sl-button size="large" pill>Large</sl-button>
```
```jsx react
```jsx:react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -136,9 +137,9 @@ const App = () => (
### Circle Buttons
Use the `circle` attribute to create circular icon buttons.
Use the `circle` attribute to create circular icon buttons. When this attribute is set, the button expects a single `<sl-icon>` in the default slot.
```html preview
```html:preview
<sl-button variant="default" size="small" circle>
<sl-icon name="gear" label="Settings"></sl-icon>
</sl-button>
@@ -152,7 +153,7 @@ Use the `circle` attribute to create circular icon buttons.
</sl-button>
```
```jsx react
```jsx:react
import { SlButton, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -174,13 +175,13 @@ const App = () => (
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
```html:preview
<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
```jsx:react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -200,16 +201,16 @@ const App = () => (
### Link Buttons
It's often helpful to have a button that works like a link. This is possible by setting the `href` attribute, which will make the component render an `<a>` under the hood. This gives you all the default link behavior the browser provides (e.g. <kbd>CMD/CTRL/SHIFT + CLICK</kbd>) and exposes the `target` and `download` attributes.
It's often helpful to have a button that works like a link. This is possible by setting the `href` attribute, which will make the component render an `<a>` under the hood. This gives you all the default link behavior the browser provides (e.g. [[CMD/CTRL/SHIFT]] + [[CLICK]]) and exposes the `target` and `download` attributes.
```html preview
```html:preview
<sl-button href="https://example.com/">Link</sl-button>
<sl-button href="https://example.com/" target="_blank">New Window</sl-button>
<sl-button href="/assets/images/wordmark.svg" download="shoelace.svg">Download</sl-button>
<sl-button href="https://example.com/" disabled>Disabled</sl-button>
```
```jsx react
```jsx:react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -228,19 +229,23 @@ const App = () => (
);
```
?> When a `target` is set, the link will receive `rel="noreferrer noopener"` for [security reasons](https://mathiasbynens.github.io/rel-noopener/).
:::tip
When a `target` is set, the link will receive `rel="noreferrer noopener"` for [security reasons](https://mathiasbynens.github.io/rel-noopener/).
:::
### Setting a Custom Width
As expected, buttons can be given a custom width by setting the `width` attribute. This is useful for making buttons span the full width of their container on smaller screens.
```html preview
```html:preview
<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
{% raw %}
```jsx:react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -258,11 +263,13 @@ const App = () => (
);
```
{% endraw %}
### Prefix and Suffix Icons
Use the `prefix` and `suffix` slots to add icons.
```html preview
```html:preview
<sl-button variant="default" size="small">
<sl-icon slot="prefix" name="gear"></sl-icon>
Settings
@@ -316,7 +323,7 @@ Use the `prefix` and `suffix` slots to add icons.
</sl-button>
```
```jsx react
```jsx:react
import { SlButton, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -382,13 +389,13 @@ const App = () => (
Use the `caret` attribute to add a dropdown indicator when a button will trigger a dropdown, menu, or popover.
```html preview
```html:preview
<sl-button size="small" caret>Small</sl-button>
<sl-button size="medium" caret>Medium</sl-button>
<sl-button size="large" caret>Large</sl-button>
```
```jsx react
```jsx:react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -410,7 +417,7 @@ const App = () => (
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
```html:preview
<sl-button variant="default" loading>Default</sl-button>
<sl-button variant="primary" loading>Primary</sl-button>
<sl-button variant="success" loading>Success</sl-button>
@@ -419,7 +426,7 @@ Use the `loading` attribute to make a button busy. The width will remain the sam
<sl-button variant="danger" loading>Danger</sl-button>
```
```jsx react
```jsx:react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -450,7 +457,7 @@ const App = () => (
Use the `disabled` attribute to disable a button. Clicks will be suppressed until the disabled state is removed.
```html preview
```html:preview
<sl-button variant="default" disabled>Default</sl-button>
<sl-button variant="primary" disabled>Primary</sl-button>
<sl-button variant="success" disabled>Success</sl-button>
@@ -459,7 +466,7 @@ Use the `disabled` attribute to disable a button. Clicks will be suppressed unti
<sl-button variant="danger" disabled>Danger</sl-button>
```
```jsx react
```jsx:react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -495,7 +502,7 @@ const App = () => (
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
```html:preview
<sl-button class="pink">Pink Button</sl-button>
<style>
@@ -534,5 +541,3 @@ This example demonstrates how to style buttons using a custom class. This is the
}
</style>
```
[component-metadata:sl-button]

View File

@@ -1,10 +1,11 @@
# Card
---
meta:
title: Card
description: Cards can be used to group related subjects in a container.
layout: component
---
[component-header:sl-card]
Cards can be used to group related subjects in a container.
```html preview
```html:preview
<sl-card class="card-overview">
<img
slot="image"
@@ -39,7 +40,7 @@ Cards can be used to group related subjects in a container.
</style>
```
```jsx react
```jsx:react
import { SlButton, SlCard, SlRating } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -52,8 +53,8 @@ const css = `
}
.card-overview [slot="footer"] {
display: flex;
justify-content: space-between;
display: flex;
justify-content: space-between;
align-items: center;
}
`;
@@ -86,11 +87,11 @@ const App = () => (
## Examples
## Basic Card
### Basic Card
Basic cards aren't very exciting, but they can display any content you want them to.
```html preview
```html:preview
<sl-card class="card-basic">
This is just a basic card. No image, no header, and no footer. Just your content.
</sl-card>
@@ -102,7 +103,7 @@ Basic cards aren't very exciting, but they can display any content you want them
</style>
```
```jsx react
```jsx:react
import { SlCard } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -122,15 +123,14 @@ const App = () => (
);
```
## Card with Header
### Card with Header
Headers can be used to display titles and more.
```html preview
```html:preview
<sl-card class="card-header">
<div slot="header">
Header Title
<sl-icon-button name="gear" label="Settings"></sl-icon-button>
</div>
@@ -158,7 +158,7 @@ Headers can be used to display titles and more.
</style>
```
```jsx react
```jsx:react
import { SlCard, SlIconButton } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -167,8 +167,8 @@ const css = `
}
.card-header [slot="header"] {
display: flex;
align-items: center;
display: flex;
align-items: center;
justify-content: space-between;
}
@@ -196,17 +196,17 @@ const App = () => (
);
```
## Card with Footer
### Card with Footer
Footers can be used to display actions, summaries, or other relevant content.
```html preview
```html:preview
<sl-card class="card-footer">
This card has a footer. You can put all sorts of things in it!
<div slot="footer">
<sl-rating></sl-rating>
<sl-button slot="footer" variant="primary">Preview</sl-button>
<sl-button variant="primary">Preview</sl-button>
</div>
</sl-card>
@@ -223,7 +223,7 @@ Footers can be used to display actions, summaries, or other relevant content.
</style>
```
```jsx react
```jsx:react
import { SlButton, SlCard, SlRating } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -232,8 +232,8 @@ const css = `
}
.card-footer [slot="footer"] {
display: flex;
justify-content: space-between;
display: flex;
justify-content: space-between;
align-items: center;
}
`;
@@ -255,11 +255,11 @@ const App = () => (
);
```
## Images
### Images
Cards accept an `image` slot. The image is displayed atop the card and stretches to fit.
```html preview
```html:preview
<sl-card class="card-image">
<img
slot="image"
@@ -276,7 +276,7 @@ Cards accept an `image` slot. The image is displayed atop the card and stretches
</style>
```
```jsx react
```jsx:react
import { SlCard } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -300,5 +300,3 @@ const App = () => (
</>
);
```
[component-metadata:sl-card]

View File

@@ -0,0 +1,84 @@
---
meta:
title: Carousel Item
description: A carousel item represent a slide within a carousel.
layout: component
---
```html:preview
<sl-carousel pagination>
<sl-carousel-item>
<img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
src="/assets/examples/carousel/mountains.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
src="/assets/examples/carousel/waterfall.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
src="/assets/examples/carousel/sunset.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
src="/assets/examples/carousel/field.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
src="/assets/examples/carousel/valley.jpg"
/>
</sl-carousel-item>
</sl-carousel>
```
```jsx:react
import { SlCarousel, SlCarouselItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlCarousel pagination>
<SlCarouselItem>
<img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
src="/assets/examples/carousel/mountains.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
src="/assets/examples/carousel/waterfall.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
src="/assets/examples/carousel/sunset.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
src="/assets/examples/carousel/field.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
src="/assets/examples/carousel/valley.jpg"
/>
</SlCarouselItem>
</SlCarousel>
);
```
:::tip
Additional demonstrations can be found in the [carousel examples](/components/carousel).
:::

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,23 @@
# Checkbox
---
meta:
title: Checkbox
description: Checkboxes allow the user to toggle an option on or off.
layout: component
---
[component-header:sl-checkbox]
Checkboxes allow the user to toggle an option on or off.
```html preview
```html:preview
<sl-checkbox>Checkbox</sl-checkbox>
```
```jsx react
```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.
:::tip
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
:::
## Examples
@@ -22,11 +25,11 @@ const App = () => <SlCheckbox>Checkbox</SlCheckbox>;
Use the `checked` attribute to activate the checkbox.
```html preview
```html:preview
<sl-checkbox checked>Checked</sl-checkbox>
```
```jsx react
```jsx:react
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlCheckbox checked>Checked</SlCheckbox>;
@@ -36,11 +39,11 @@ const App = () => <SlCheckbox checked>Checked</SlCheckbox>;
Use the `indeterminate` attribute to make the checkbox indeterminate.
```html preview
```html:preview
<sl-checkbox indeterminate>Indeterminate</sl-checkbox>
```
```jsx react
```jsx:react
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlCheckbox indeterminate>Indeterminate</SlCheckbox>;
@@ -50,21 +53,47 @@ const App = () => <SlCheckbox indeterminate>Indeterminate</SlCheckbox>;
Use the `disabled` attribute to disable the checkbox.
```html preview
```html:preview
<sl-checkbox disabled>Disabled</sl-checkbox>
```
```jsx react
```jsx:react
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlCheckbox disabled>Disabled</SlCheckbox>;
```
### Sizes
Use the `size` attribute to change a checkbox's size.
```html:preview
<sl-checkbox size="small">Small</sl-checkbox>
<br />
<sl-checkbox size="medium">Medium</sl-checkbox>
<br />
<sl-checkbox size="large">Large</sl-checkbox>
```
```jsx:react
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlCheckbox size="small">Small</SlCheckbox>
<br />
<SlCheckbox size="medium">Medium</SlCheckbox>
<br />
<SlCheckbox size="large">Large</SlCheckbox>
</>
);
```
### Custom Validity
Use the `setCustomValidity()` method to set a custom validation message. This will prevent the form from submitting and make the browser display the error message you provide. To clear the error, call this function with an empty string.
```html preview
```html:preview
<form class="custom-validity">
<sl-checkbox>Check me</sl-checkbox>
<br />
@@ -74,14 +103,18 @@ Use the `setCustomValidity()` method to set a custom validation message. This wi
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(() => {
customElements.whenDefined('sl-checkbox').then(async () => {
await checkbox.updateComplete;
checkbox.setCustomValidity(errorMessage);
});
// Update validity on change
checkbox.addEventListener('sl-change', () => {
checkbox.setCustomValidity(checkbox.checked ? '' : errorMessage);
});
// Handle submit
form.addEventListener('submit', event => {
event.preventDefault();
@@ -90,22 +123,29 @@ Use the `setCustomValidity()` method to set a custom validation message. This wi
</script>
```
```jsx react
{% raw %}
```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}>
@@ -120,4 +160,4 @@ const App = () => {
};
```
[component-metadata:sl-checkbox]
{% endraw %}

View File

@@ -1,20 +1,23 @@
# Color Picker
---
meta:
title: Color Picker
description: Color pickers allow the user to select a color.
layout: component
---
[component-header:sl-color-picker]
Color pickers allow the user to select a color.
```html preview
```html:preview
<sl-color-picker label="Select a color"></sl-color-picker>
```
```jsx react
```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.
:::tip
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
:::
## Examples
@@ -22,11 +25,11 @@ const App = () => <SlColorPicker label="Select a color" />;
Use the `value` attribute to set an initial value for the color picker.
```html preview
```html:preview
<sl-color-picker value="#4a90e2" label="Select a color"></sl-color-picker>
```
```jsx react
```jsx:react
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlColorPicker value="#4a90e2" label="Select a color" />;
@@ -34,13 +37,13 @@ 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`.
Use the `opacity` attribute to enable the opacity slider. When this is enabled, the value will be displayed as HEXA, RGBA, HSLA, or HSVA based on `format`.
```html preview
<sl-color-picker opacity label="Select a color"></sl-color-picker>
```html:preview
<sl-color-picker value="#f5a623ff" opacity label="Select a color"></sl-color-picker>
```
```jsx react
```jsx:react
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlColorPicker opacity label="Select a color" />;
@@ -48,17 +51,18 @@ const App = () => <SlColorPicker opacity label="Select a color" />;
### Formats
Set the color picker's format with the `format` attribute. Valid options include `hex`, `rgb`, and `hsl`. Note that the color picker's input will accept any parsable format (including CSS color names) regardless of this option.
Set the color picker's format with the `format` attribute. Valid options include `hex`, `rgb`, `hsl`, and `hsv`. Note that the color picker's input will accept any parsable format (including CSS color names) regardless of this option.
To prevent users from toggling the format themselves, add the `no-format-toggle` attribute.
```html preview
```html:preview
<sl-color-picker format="hex" value="#4a90e2" label="Select a color"></sl-color-picker>
<sl-color-picker format="rgb" value="rgb(80, 227, 194)" label="Select a color"></sl-color-picker>
<sl-color-picker format="hsl" value="hsl(290, 87%, 47%)" label="Select a color"></sl-color-picker>
<sl-color-picker format="hsv" value="hsv(55, 89%, 97%)" label="Select a color"></sl-color-picker>
```
```jsx react
```jsx:react
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -66,21 +70,50 @@ const App = () => (
<SlColorPicker format="hex" value="#4a90e2" />
<SlColorPicker format="rgb" value="rgb(80, 227, 194)" />
<SlColorPicker format="hsl" value="hsl(290, 87%, 47%)" />
<SlColorPicker format="hsv" value="hsv(55, 89%, 97%)" />
</>
);
```
### Swatches
Use the `swatches` attribute to add convenient presets to the color picker. Any format the color picker can parse is acceptable (including CSS color names), but each value must be separated by a semicolon (`;`). Alternatively, you can pass an array of color values to this property using JavaScript.
```html:preview
<sl-color-picker
label="Select a color"
swatches="
#d0021b; #f5a623; #f8e71c; #8b572a; #7ed321; #417505; #bd10e0; #9013fe;
#4a90e2; #50e3c2; #b8e986; #000; #444; #888; #ccc; #fff;
"
></sl-color-picker>
```
```jsx:react
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlColorPicker
label="Select a color"
swatches="
#d0021b; #f5a623; #f8e71c; #8b572a; #7ed321; #417505; #bd10e0; #9013fe;
#4a90e2; #50e3c2; #b8e986; #000; #444; #888; #ccc; #fff;
"
/>
);
```
### Sizes
Use the `size` attribute to change the color picker's trigger size.
```html preview
```html:preview
<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
```jsx:react
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -96,14 +129,12 @@ const App = () => (
The color picker can be rendered inline instead of in a dropdown using the `inline` attribute.
```html preview
```html:preview
<sl-color-picker inline label="Select a color"></sl-color-picker>
```
```jsx react
```jsx:react
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlColorPicker inline label="Select a color" />;
```
[component-metadata:sl-color-picker]

View File

@@ -1,19 +1,20 @@
---
meta:
title: Details
description: Details show a brief summary and expand to show additional content.
layout: component
---
<!-- cspell:dictionaries lorem-ipsum -->
# Details
[component-header:sl-details]
Details show a brief summary and expand to show additional content.
```html preview
```html:preview
<sl-details summary="Toggle Me">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</sl-details>
```
```jsx react
```jsx:react
import { SlDetails } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -30,14 +31,14 @@ const App = () => (
Use the `disable` attribute to prevent the details from expanding.
```html preview
```html:preview
<sl-details summary="Disabled" disabled>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</sl-details>
```
```jsx react
```jsx:react
import { SlDetails } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -48,11 +49,57 @@ const App = () => (
);
```
### Customizing the Summary Icon
Use the `expand-icon` and `collapse-icon` slots to change the expand and collapse icons, respectively. To disable the animation, override the `rotate` property on the `summary-icon` part as shown below.
```html:preview
<sl-details summary="Toggle Me" class="custom-icons">
<sl-icon name="plus-square" slot="expand-icon"></sl-icon>
<sl-icon name="dash-square" slot="collapse-icon"></sl-icon>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</sl-details>
<style>
sl-details.custom-icons::part(summary-icon) {
/* Disable the expand/collapse animation */
rotate: none;
}
</style>
```
```jsx:react
import { SlDetails, SlIcon } from '@shoelace-style/shoelace/dist/react';
const css = `
sl-details.custom-icon::part(summary-icon) {
/* Disable the expand/collapse animation */
rotate: none;
}
`;
const App = () => (
<>
<SlDetails summary="Toggle Me" class="custom-icon">
<SlIcon name="plus-square" slot="expand-icon" />
<SlIcon name="dash-square" slot="collapse-icon" />
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.
</SlDetails>
<style>{css}</style>
</>
);
```
### Grouping Details
Details are designed to function independently, but you can simulate a group or "accordion" where only one is shown at a time by listening for the `sl-show` event.
```html preview
```html:preview
<div class="details-group-example">
<sl-details summary="First" open>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
@@ -85,5 +132,3 @@ Details are designed to function independently, but you can simulate a group or
}
</style>
```
[component-metadata:sl-details]

View File

@@ -1,12 +1,13 @@
---
meta:
title: Dialog
description: 'Dialogs, sometimes called "modals", appear above the page and require the user''s immediate attention.'
layout: component
---
<!-- cspell:dictionaries lorem-ipsum -->
# Dialog
[component-header:sl-dialog]
Dialogs, sometimes called "modals", appear above the page and require the user's immediate attention.
```html preview
```html:preview
<sl-dialog label="Dialog" class="dialog-overview">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<sl-button slot="footer" variant="primary">Close</sl-button>
@@ -24,7 +25,7 @@ Dialogs, sometimes called "modals", appear above the page and require the user's
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
@@ -46,19 +47,13 @@ const App = () => {
};
```
## UX Tips
- Use a dialog when you immediately require the user's attention, e.g. confirming a destructive action.
- Always provide an obvious way for the user to dismiss the dialog.
- Don't nest dialogs. It almost always leads to a poor experience for the user.
## Examples
### Custom Width
Use the `--width` custom property to set the dialog's width.
```html preview
```html:preview
<sl-dialog label="Dialog" class="dialog-width" style="--width: 50vw;">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<sl-button slot="footer" variant="primary">Close</sl-button>
@@ -76,7 +71,9 @@ Use the `--width` custom property to set the dialog's width.
</script>
```
```jsx react
{% raw %}
```jsx:react
import { useState } from 'react';
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
@@ -98,11 +95,13 @@ const App = () => {
};
```
{% endraw %}
### Scrolling
By design, a dialog's height will never exceed that of the viewport. As such, dialogs will not scroll with the page ensuring the header and footer are always accessible to the user.
```html preview
```html:preview
<sl-dialog label="Dialog" class="dialog-scrolling">
<div style="height: 150vh; border: dashed 2px var(--sl-color-neutral-200); padding: 0 1rem;">
<p>Scroll down and give it a try! 👇</p>
@@ -122,7 +121,9 @@ By design, a dialog's height will never exceed that of the viewport. As such, di
</script>
```
```jsx react
{% raw %}
```jsx:react
import { useState } from 'react';
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
@@ -153,15 +154,70 @@ const App = () => {
};
```
{% endraw %}
### Header Actions
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [icon buttons](/components/icon-button) if needed.
```html:preview
<sl-dialog label="Dialog" class="dialog-header-actions">
<sl-icon-button class="new-window" slot="header-actions" name="box-arrow-up-right"></sl-icon-button>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<sl-button slot="footer" variant="primary">Close</sl-button>
</sl-dialog>
<sl-button>Open Dialog</sl-button>
<script>
const dialog = document.querySelector('.dialog-header-actions');
const openButton = dialog.nextElementSibling;
const closeButton = dialog.querySelector('sl-button[slot="footer"]');
const newWindowButton = dialog.querySelector('.new-window');
openButton.addEventListener('click', () => dialog.show());
closeButton.addEventListener('click', () => dialog.hide());
newWindowButton.addEventListener('click', () => window.open(location.href));
</script>
```
```jsx:react
import { useState } from 'react';
import { SlButton, SlDialog, SlIconButton } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDialog label="Dialog" open={open} onSlAfterHide={() => setOpen(false)}>
<SlIconButton
class="new-window"
slot="header-actions"
name="box-arrow-up-right"
onClick={() => window.open(location.href)}
/>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDialog>
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
</>
);
};
```
### Preventing the Dialog from Closing
By default, dialogs will close when the user clicks the close button, clicks the overlay, or presses the <kbd>Escape</kbd> key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.
By default, dialogs will close when the user clicks the close button, clicks the overlay, or presses the [[Escape]] key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.
To keep the dialog open in such cases, you can cancel the `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.
You can use `event.detail.source` to determine what triggered the request to close. This example prevents the dialog from closing when the overlay is clicked, but allows the close button or [[Escape]] to dismiss it.
```html preview
```html:preview
<sl-dialog label="Dialog" class="dialog-deny-close">
This dialog will not close when you click on the overlay.
<sl-button slot="footer" variant="primary">Close</sl-button>
@@ -186,7 +242,7 @@ You can use `event.detail.source` to determine what triggered the request to clo
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
@@ -219,7 +275,7 @@ const App = () => {
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
```html:preview
<sl-dialog label="Dialog" class="dialog-focus">
<sl-input autofocus placeholder="I will have focus when the dialog is opened"></sl-input>
<sl-button slot="footer" variant="primary">Close</sl-button>
@@ -238,7 +294,7 @@ By default, the dialog's panel will gain focus when opened. This allows a subseq
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlButton, SlDialog, SlInput } from '@shoelace-style/shoelace/dist/react';
@@ -260,6 +316,6 @@ const App = () => {
};
```
?> 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]
:::tip
You can further customize initial focus behavior by canceling the `sl-initial-focus` event and setting focus yourself inside the event handler.
:::

View File

@@ -1,14 +1,15 @@
# Divider
---
meta:
title: Divider
description: Dividers are used to visually separate or group elements.
layout: component
---
[component-header:sl-divider]
Dividers are used to visually separate or group elements.
```html preview
```html:preview
<sl-divider></sl-divider>
```
```jsx react
```jsx:react
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlDivider />;
@@ -20,35 +21,43 @@ const App = () => <SlDivider />;
Use the `--width` custom property to change the width of the divider.
```html preview
```html:preview
<sl-divider style="--width: 4px;"></sl-divider>
```
```jsx react
{% raw %}
```jsx:react
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlDivider style={{ '--width': '4px' }} />;
```
{% endraw %}
### Color
Use the `--color` custom property to change the color of the divider.
```html preview
```html:preview
<sl-divider style="--color: tomato;"></sl-divider>
```
```jsx react
{% raw %}
```jsx:react
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlDivider style={{ '--color': 'tomato' }} />;
```
{% endraw %}
### Spacing
Use the `--spacing` custom property to change the amount of space between the divider and it's neighboring elements.
```html preview
```html:preview
<div style="text-align: center;">
Above
<sl-divider style="--spacing: 2rem;"></sl-divider>
@@ -56,7 +65,9 @@ Use the `--spacing` custom property to change the amount of space between the di
</div>
```
```jsx react
{% raw %}
```jsx:react
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -68,11 +79,13 @@ const App = () => (
);
```
{% endraw %}
### Vertical
Add the `vertical` attribute to draw the divider in a vertical orientation. The divider will span the full height of its container. Vertical dividers work especially well inside of a flex container.
```html preview
```html:preview
<div style="display: flex; align-items: center; height: 2rem;">
First
<sl-divider vertical></sl-divider>
@@ -82,7 +95,9 @@ Add the `vertical` attribute to draw the divider in a vertical orientation. The
</div>
```
```jsx react
{% raw %}
```jsx:react
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -102,11 +117,13 @@ const App = () => (
);
```
{% endraw %}
### Menu Dividers
Use dividers in [menus](/components/menu) to visually group menu items.
```html preview
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item value="1">Option 1</sl-menu-item>
<sl-menu-item value="2">Option 2</sl-menu-item>
@@ -118,7 +135,9 @@ Use dividers in [menus](/components/menu) to visually group menu items.
</sl-menu>
```
```jsx react
{% raw %}
```jsx:react
import { SlDivider, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -134,4 +153,4 @@ const App = () => (
);
```
[component-metadata:sl-divider]
{% endraw %}

View File

@@ -1,12 +1,13 @@
---
meta:
title: Drawer
description: Drawers slide in from a container to expose additional options and information.
layout: component
---
<!-- cspell:dictionaries lorem-ipsum -->
# Drawer
[component-header:sl-drawer]
Drawers slide in from a container to expose additional options and information.
```html preview
```html:preview
<sl-drawer label="Drawer" class="drawer-overview">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<sl-button slot="footer" variant="primary">Close</sl-button>
@@ -24,7 +25,7 @@ Drawers slide in from a container to expose additional options and information.
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
@@ -52,7 +53,7 @@ const App = () => {
By default, drawers slide in from the end. To make the drawer slide in from the start, set the `placement` attribute to `start`.
```html preview
```html:preview
<sl-drawer label="Drawer" placement="start" class="drawer-placement-start">
This drawer slides in from the start.
<sl-button slot="footer" variant="primary">Close</sl-button>
@@ -70,7 +71,7 @@ By default, drawers slide in from the end. To make the drawer slide in from the
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
@@ -96,7 +97,7 @@ const App = () => {
To make the drawer slide in from the top, set the `placement` attribute to `top`.
```html preview
```html:preview
<sl-drawer label="Drawer" placement="top" class="drawer-placement-top">
This drawer slides in from the top.
<sl-button slot="footer" variant="primary">Close</sl-button>
@@ -114,7 +115,7 @@ To make the drawer slide in from the top, set the `placement` attribute to `top`
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
@@ -140,7 +141,7 @@ const App = () => {
To make the drawer slide in from the bottom, set the `placement` attribute to `bottom`.
```html preview
```html:preview
<sl-drawer label="Drawer" placement="bottom" class="drawer-placement-bottom">
This drawer slides in from the bottom.
<sl-button slot="footer" variant="primary">Close</sl-button>
@@ -158,7 +159,7 @@ To make the drawer slide in from the bottom, set the `placement` attribute to `b
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
@@ -182,9 +183,11 @@ const App = () => {
### 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.
By default, drawers slide out of their [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport. To make a drawer slide out of a parent element, add the `contained` attribute to the drawer and apply `position: relative` to its parent.
```html preview
Unlike normal drawers, contained drawers are not modal. This means they do not show an overlay, they do not trap focus, and they are not dismissible with [[Escape]]. This is intentional to allow users to interact with elements outside of the drawer.
```html:preview
<div
style="position: relative; border: solid 2px var(--sl-panel-border-color); height: 300px; padding: 1rem; margin-bottom: 1rem;"
>
@@ -196,19 +199,21 @@ By default, the drawer slides out of its [containing block](https://developer.mo
</sl-drawer>
</div>
<sl-button>Open Drawer</sl-button>
<sl-button>Toggle Drawer</sl-button>
<script>
const drawer = document.querySelector('.drawer-contained');
const openButton = drawer.parentElement.nextElementSibling;
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
openButton.addEventListener('click', () => drawer.show());
openButton.addEventListener('click', () => (drawer.open = !drawer.open));
closeButton.addEventListener('click', () => drawer.hide());
</script>
```
```jsx react
{% raw %}
```jsx:react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
@@ -228,7 +233,14 @@ const App = () => {
>
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%' }}>
<SlDrawer
label="Drawer"
contained
no-modal
open={open}
onSlAfterHide={() => setOpen(false)}
style={{ '--size': '50%' }}
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
Close
@@ -242,11 +254,13 @@ const App = () => {
};
```
{% endraw %}
### Custom Size
Use the `--size` custom property to set the drawer's size. This will be applied to the drawer's width or height depending on its `placement`.
```html preview
```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" variant="primary">Close</sl-button>
@@ -264,7 +278,9 @@ Use the `--size` custom property to set the drawer's size. This will be applied
</script>
```
```jsx react
{% raw %}
```jsx:react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
@@ -286,11 +302,13 @@ const App = () => {
};
```
{% endraw %}
### Scrolling
By design, a drawer's height will never exceed 100% of its container. As such, drawers will not scroll with the page to ensure the header and footer are always accessible to the user.
```html preview
```html:preview
<sl-drawer label="Drawer" class="drawer-scrolling">
<div style="height: 150vh; border: dashed 2px var(--sl-color-neutral-200); padding: 0 1rem;">
<p>Scroll down and give it a try! 👇</p>
@@ -310,7 +328,9 @@ By design, a drawer's height will never exceed 100% of its container. As such, d
</script>
```
```jsx react
{% raw %}
```jsx:react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
@@ -340,15 +360,65 @@ const App = () => {
};
```
{% endraw %}
### Header Actions
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [icon buttons](/components/icon-button) if needed.
```html:preview
<sl-drawer label="Drawer" class="drawer-header-actions">
<sl-icon-button class="new-window" slot="header-actions" name="box-arrow-up-right"></sl-icon-button>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<sl-button slot="footer" variant="primary">Close</sl-button>
</sl-drawer>
<sl-button>Open Drawer</sl-button>
<script>
const drawer = document.querySelector('.drawer-header-actions');
const openButton = drawer.nextElementSibling;
const closeButton = drawer.querySelector('sl-button[variant="primary"]');
const newWindowButton = drawer.querySelector('.new-window');
openButton.addEventListener('click', () => drawer.show());
closeButton.addEventListener('click', () => drawer.hide());
newWindowButton.addEventListener('click', () => window.open(location.href));
</script>
```
```jsx:react
import { useState } from 'react';
import { SlButton, SlDrawer, SlIconButton } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)}>
<SlIconButton slot="header-actions" name="box-arrow-up-right" onClick={() => window.open(location.href)} />
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDrawer>
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
</>
);
};
```
### Preventing the Drawer from Closing
By default, drawers will close when the user clicks the close button, clicks the overlay, or presses the <kbd>Escape</kbd> key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.
By default, drawers will close when the user clicks the close button, clicks the overlay, or presses the [[Escape]] key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.
To keep the drawer open in such cases, you can cancel the `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.
You can use `event.detail.source` to determine what triggered the request to close. This example prevents the drawer from closing when the overlay is clicked, but allows the close button or [[Escape]] to dismiss it.
```html preview
```html:preview
<sl-drawer label="Drawer" class="drawer-deny-close">
This drawer will not close when you click on the overlay.
<sl-button slot="footer" variant="primary">Close</sl-button>
@@ -373,7 +443,7 @@ You can use `event.detail.source` to determine what triggered the request to clo
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
@@ -406,7 +476,7 @@ const App = () => {
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
```html:preview
<sl-drawer label="Drawer" class="drawer-focus">
<sl-input autofocus placeholder="I will have focus when the drawer is opened"></sl-input>
<sl-button slot="footer" variant="primary">Close</sl-button>
@@ -425,7 +495,7 @@ By default, the drawer's panel will gain focus when opened. This allows a subseq
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlButton, SlDrawer, SlInput } from '@shoelace-style/shoelace/dist/react';
@@ -447,5 +517,6 @@ const App = () => {
};
```
?> 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]
:::tip
You can further customize initial focus behavior by canceling the `sl-initial-focus` event and setting focus yourself inside the event handler.
:::

View File

@@ -1,14 +1,15 @@
# Dropdown
[component-header:sl-dropdown]
Dropdowns expose additional content that "drops down" in a panel.
---
meta:
title: Dropdown
description: 'Dropdowns expose additional content that "drops down" in a panel.'
layout: component
---
Dropdowns consist of a trigger and a panel. By default, activating the trigger will expose the panel and interacting outside of the panel will close it.
Dropdowns are designed to work well with [menus](/components/menu) to provide a list of options the user can select from. However, dropdowns can also be used in lower-level applications (e.g. [color picker](/components/color-picker) and [select](/components/select)). The API gives you complete control over showing, hiding, and positioning the panel.
```html preview
```html:preview
<sl-dropdown>
<sl-button slot="trigger" caret>Dropdown</sl-button>
<sl-menu>
@@ -16,7 +17,7 @@ Dropdowns are designed to work well with [menus](/components/menu) to provide a
<sl-menu-item>Dropdown Item 2</sl-menu-item>
<sl-menu-item>Dropdown Item 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item checked>Checked</sl-menu-item>
<sl-menu-item type="checkbox" checked>Checkbox</sl-menu-item>
<sl-menu-item disabled>Disabled</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>
@@ -31,7 +32,7 @@ Dropdowns are designed to work well with [menus](/components/menu) to provide a
</sl-dropdown>
```
```jsx react
```jsx:react
import { SlButton, SlDivider, SlDropdown, SlIcon, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -44,7 +45,9 @@ const App = () => (
<SlMenuItem>Dropdown Item 2</SlMenuItem>
<SlMenuItem>Dropdown Item 3</SlMenuItem>
<SlDivider />
<SlMenuItem checked>Checked</SlMenuItem>
<SlMenuItem type="checkbox" checked>
Checkbox
</SlMenuItem>
<SlMenuItem disabled>Disabled</SlMenuItem>
<SlDivider />
<SlMenuItem>
@@ -64,9 +67,9 @@ const App = () => (
### Getting the Selected Item
When dropdowns are used with [menus](/components/menu), you can listen for the `sl-select` event to determine which menu item was selected. The menu item element will be exposed in `event.detail.item`. You can set `value` props to make it easier to identify commands.
When dropdowns are used with [menus](/components/menu), you can listen for the [`sl-select`](/components/menu#events) event to determine which menu item was selected. The menu item element will be exposed in `event.detail.item`. You can set `value` props to make it easier to identify commands.
```html preview
```html:preview
<div class="dropdown-selection">
<sl-dropdown>
<sl-button slot="trigger" caret>Edit</sl-button>
@@ -89,7 +92,7 @@ When dropdowns are used with [menus](/components/menu), you can listen for the `
</script>
```
```jsx react
```jsx:react
import { SlButton, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => {
@@ -115,7 +118,7 @@ const App = () => {
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
```html:preview
<div class="dropdown-selection-alt">
<sl-dropdown>
<sl-button slot="trigger" caret>Edit</sl-button>
@@ -139,7 +142,7 @@ Alternatively, you can listen for the `click` event on individual menu items. No
</script>
```
```jsx react
```jsx:react
import { SlButton, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => {
@@ -174,7 +177,7 @@ const App = () => {
The preferred placement of the dropdown can be set with the `placement` attribute. Note that the actual position may vary to ensure the panel remains in the viewport.
```html preview
```html:preview
<sl-dropdown placement="top-start">
<sl-button slot="trigger" caret>Edit</sl-button>
<sl-menu>
@@ -188,7 +191,7 @@ The preferred placement of the dropdown can be set with the `placement` attribut
</sl-dropdown>
```
```jsx react
```jsx:react
import { SlButton, SlDivider, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -212,7 +215,7 @@ const App = () => (
The distance from the panel to the trigger can be customized using the `distance` attribute. This value is specified in pixels.
```html preview
```html:preview
<sl-dropdown distance="30">
<sl-button slot="trigger" caret>Edit</sl-button>
<sl-menu>
@@ -226,7 +229,7 @@ The distance from the panel to the trigger can be customized using the `distance
</sl-dropdown>
```
```jsx react
```jsx:react
import { SlButton, SlDivider, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -250,7 +253,7 @@ const App = () => (
The offset of the panel along the trigger can be customized using the `skidding` attribute. This value is specified in pixels.
```html preview
```html:preview
<sl-dropdown skidding="30">
<sl-button slot="trigger" caret>Edit</sl-button>
<sl-menu>
@@ -264,7 +267,7 @@ The offset of the panel along the trigger can be customized using the `skidding`
</sl-dropdown>
```
```jsx react
```jsx:react
import { SlButton, SlDivider, SlDropdown, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -288,7 +291,7 @@ const App = () => (
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.
```html preview
```html:preview
<div class="dropdown-hoist">
<sl-dropdown>
<sl-button slot="trigger" caret>No Hoist</sl-button>
@@ -311,6 +314,7 @@ Dropdown panels will be clipped if they're inside a container that has `overflow
<style>
.dropdown-hoist {
position: relative;
border: solid 2px var(--sl-panel-border-color);
padding: var(--sl-spacing-medium);
overflow: hidden;
@@ -318,7 +322,7 @@ Dropdown panels will be clipped if they're inside a container that has `overflow
</style>
```
```jsx react
```jsx:react
import { SlButton, SlDivider, SlDropdown, SlIcon, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -359,5 +363,3 @@ const App = () => (
</>
);
```
[component-metadata:sl-dropdown]

View File

@@ -1,10 +1,11 @@
# Format Bytes
---
meta:
title: Format Bytes
description: Formats a number as a human readable bytes value.
layout: component
---
[component-header:sl-format-bytes]
Formats a number as a human readable bytes value.
```html preview
```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>
@@ -19,7 +20,9 @@ Formats a number as a human readable bytes value.
</script>
```
```jsx react
{% raw %}
```jsx:react
import { useState } from 'react';
import { SlButton, SlFormatBytes, SlInput } from '@shoelace-style/shoelace/dist/react';
@@ -43,20 +46,22 @@ const App = () => {
};
```
{% endraw %}
## Examples
### Formatting Bytes
Set the `value` attribute to a number to get the value in bytes.
```html preview
```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="1200000000"></sl-format-bytes>
```
```jsx react
```jsx:react
import { SlFormatBytes } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -76,14 +81,14 @@ const App = () => (
To get the value in bits, set the `unit` attribute to `bit`.
```html preview
```html:preview
<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
```jsx:react
import { SlFormatBytes } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -103,14 +108,14 @@ const App = () => (
Use the `lang` attribute to set the number formatting locale.
```html preview
```html:preview
<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
```jsx:react
import { SlFormatBytes } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -125,5 +130,3 @@ const App = () => (
</>
);
```
[component-metadata:sl-format-bytes]

View File

@@ -1,17 +1,18 @@
# Format Date
[component-header:sl-format-date]
Formats a date/time using the specified locale and options.
---
meta:
title: Format Date
description: Formats a date/time using the specified locale and options.
layout: component
---
Localization is handled by the browser's [`Intl.DateTimeFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat). No language packs are required.
```html preview
```html:preview
<!-- Shoelace 2 release date 🎉 -->
<sl-format-date date="2020-07-15T09:17:00-04:00"></sl-format-date>
```
```jsx react
```jsx:react
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlFormatDate date="2020-07-15T09:17:00-04:00" />;
@@ -19,7 +20,9 @@ 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.
:::tip
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
@@ -27,7 +30,7 @@ The `date` attribute determines the date/time to use when formatting. It must be
Formatting options are based on those found in the [`Intl.DateTimeFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat). When formatting options are provided, the date/time will be formatted according to those values. When no formatting options are provided, a localized, numeric date will be displayed instead.
```html preview
```html:preview
<!-- Human-readable date -->
<sl-format-date month="long" day="numeric" year="numeric"></sl-format-date><br />
@@ -47,7 +50,7 @@ Formatting options are based on those found in the [`Intl.DateTimeFormat` API](h
<sl-format-date></sl-format-date>
```
```jsx react
```jsx:react
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -82,12 +85,12 @@ const App = () => (
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
```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="24"></sl-format-date>
```
```jsx react
```jsx:react
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -103,13 +106,13 @@ const App = () => (
Use the `lang` attribute to set the date/time formatting locale.
```html preview
```html:preview
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
```jsx:react
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -122,5 +125,3 @@ const App = () => (
</>
);
```
[component-metadata:sl-format-date]

View File

@@ -1,12 +1,13 @@
# Format Number
[component-header:sl-format-number]
Formats a number using the specified locale and options.
---
meta:
title: Format Number
description: Formats a number using the specified locale and options.
layout: component
---
Localization is handled by the browser's [`Intl.NumberFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat). No language packs are required.
```html preview
```html:preview
<div class="format-number-overview">
<sl-format-number value="1000"></sl-format-number>
<br /><br />
@@ -22,7 +23,9 @@ Localization is handled by the browser's [`Intl.NumberFormat` API](https://devel
</script>
```
```jsx react
{% raw %}
```jsx:react
import { useState } from 'react';
import { SlFormatNumber, SlInput } from '@shoelace-style/shoelace/dist/react';
@@ -46,13 +49,15 @@ const App = () => {
};
```
{% endraw %}
## Examples
### Percentages
To get the value as a percent, set the `type` attribute to `percent`.
```html preview
```html:preview
<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 />
@@ -60,7 +65,7 @@ To get the value as a percent, set the `type` attribute to `percent`.
<sl-format-number type="percent" value="1"></sl-format-number>
```
```jsx react
```jsx:react
import { SlFormatNumber } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -82,13 +87,13 @@ const App = () => (
Use the `lang` attribute to set the number formatting locale.
```html preview
```html:preview
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
```jsx:react
import { SlFormatNumber } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -106,7 +111,7 @@ const App = () => (
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
```html:preview
<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 />
@@ -114,7 +119,7 @@ To format a number as a monetary value, set the `type` attribute to `currency` a
<sl-format-number type="currency" currency="CNY" value="2000" lang="zh-cn"></sl-format-number>
```
```jsx react
```jsx:react
import { SlFormatNumber } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -131,5 +136,3 @@ const App = () => (
</>
);
```
[component-metadata:sl-format-number]

View File

@@ -1,16 +1,17 @@
# Icon Button
[component-header:sl-icon-button]
Icons buttons are simple, icon-only buttons that can be used for actions and in toolbars.
---
meta:
title: Icon Button
description: Icons buttons are simple, icon-only buttons that can be used for actions and in toolbars.
layout: component
---
For a full list of icons that come bundled with Shoelace, refer to the [icon component](/components/icon).
```html preview
```html:preview
<sl-icon-button name="gear" label="Settings"></sl-icon-button>
```
```jsx react
```jsx:react
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlIconButton name="gear" label="Settings" />;
@@ -22,13 +23,15 @@ const App = () => <SlIconButton name="gear" label="Settings" />;
Icon buttons inherit their parent element's `font-size`.
```html preview
```html:preview
<sl-icon-button name="pencil" label="Edit" style="font-size: 1.5rem;"></sl-icon-button>
<sl-icon-button name="pencil" label="Edit" style="font-size: 2rem;"></sl-icon-button>
<sl-icon-button name="pencil" label="Edit" style="font-size: 2.5rem;"></sl-icon-button>
```
```jsx react
{% raw %}
```jsx:react
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -40,11 +43,13 @@ const App = () => (
);
```
{% endraw %}
### Colors
Icon buttons are designed to have a uniform appearance, so their color is not inherited. However, you can still customize them by styling the `base` part.
```html preview
```html:preview
<div class="icon-button-color">
<sl-icon-button name="type-bold" label="Bold"></sl-icon-button>
<sl-icon-button name="type-italic" label="Italic"></sl-icon-button>
@@ -67,7 +72,7 @@ Icon buttons are designed to have a uniform appearance, so their color is not in
</style>
```
```jsx react
```jsx:react
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -102,11 +107,11 @@ const App = () => (
Use the `href` attribute to convert the button to a link.
```html preview
```html:preview
<sl-icon-button name="gear" label="Settings" href="https://example.com" target="_blank"></sl-icon-button>
```
```jsx react
```jsx:react
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlIconButton name="gear" label="Settings" href="https://example.com" target="_blank" />;
@@ -116,13 +121,13 @@ const App = () => <SlIconButton name="gear" label="Settings" href="https://examp
Wrap a tooltip around an icon button to provide contextual information to the user.
```html preview
```html:preview
<sl-tooltip content="Settings">
<sl-icon-button name="gear" label="Settings"></sl-icon-button>
</sl-tooltip>
```
```jsx react
```jsx:react
import { SlIconButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -136,14 +141,12 @@ const App = () => (
Use the `disabled` attribute to disable the icon button.
```html preview
```html:preview
<sl-icon-button name="gear" label="Settings" disabled></sl-icon-button>
```
```jsx react
```jsx:react
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlIconButton name="gear" label="Settings" disabled />;
```
[component-metadata:sl-icon-button]

View File

@@ -1,12 +1,19 @@
# Icon
[component-header:sl-icon]
Icons are symbols that can be used to represent various options within an application.
---
meta:
title: Icon
description: Icons are symbols that can be used to represent various options within an application.
layout: component
---
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.
:::tip
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>
@@ -18,9 +25,9 @@ Click or tap on an icon below to copy its name and use it like this.
<sl-icon slot="prefix" name="search"></sl-icon>
</sl-input>
<sl-select value="outline">
<sl-menu-item value="outline">Outlined</sl-menu-item>
<sl-menu-item value="fill">Filled</sl-menu-item>
<sl-menu-item value="all">All icons</sl-menu-item>
<sl-option value="outline">Outlined</sl-option>
<sl-option value="fill">Filled</sl-option>
<sl-option value="all">All icons</sl-option>
</sl-select>
</div>
<div class="icon-list"></div>
@@ -33,7 +40,7 @@ Click or tap on an icon below to copy its name and use it like this.
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
```html:preview
<div style="color: #4a90e2;">
<sl-icon name="exclamation-triangle"></sl-icon>
<sl-icon name="archive"></sl-icon>
@@ -60,7 +67,9 @@ Icons inherit their color from the current text color. Thus, you can set the `co
</div>
```
```jsx react
{% raw %}
```jsx:react
import { SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -93,11 +102,13 @@ const App = () => (
);
```
{% endraw %}
### Sizing
Icons are sized relative to the current font size. To change their size, set the `font-size` property on the icon itself or on a parent element as shown below.
```html preview
```html:preview
<div style="font-size: 32px;">
<sl-icon name="exclamation-triangle"></sl-icon>
<sl-icon name="archive"></sl-icon>
@@ -118,7 +129,9 @@ Icons are sized relative to the current font size. To change their size, set the
</div>
```
```jsx react
{% raw %}
```jsx:react
import { SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -143,15 +156,17 @@ const App = () => (
);
```
{% endraw %}
### Labels
For non-decorative icons, use the `label` attribute to announce it to assistive devices.
```html preview
```html:preview
<sl-icon name="star-fill" label="Add to favorites"></sl-icon>
```
```jsx react
```jsx:react
import { SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlIcon name="star-fill" label="Add to favorites" />;
@@ -161,16 +176,20 @@ const App = () => <SlIcon name="star-fill" label="Add to favorites" />;
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
```html:preview
<sl-icon src="https://shoelace.style/assets/images/shoe.svg" style="font-size: 8rem;"></sl-icon>
```
```jsx react
{% raw %}
```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>;
```
{% endraw %}
## Icon Libraries
You can register additional icons to use with the `<sl-icon>` component through icon libraries. Icon files can exist locally or on a CORS-enabled endpoint (e.g. a CDN). There is no limit to how many icon libraries you can register and there is no cost associated with registering them, as individual icons are only requested when they're used.
@@ -211,7 +230,7 @@ This will register the [Boxicons](https://boxicons.com/) library using the jsDel
Icons in this library are licensed under the [Creative Commons 4.0 License](https://github.com/atisawd/boxicons#license).
```html preview
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
@@ -256,7 +275,7 @@ This will register the [Lucide](https://lucide.dev/) icon library using the jsDe
Icons in this library are licensed under the [MIT License](https://github.com/lucide-icons/lucide/blob/master/LICENSE).
```html preview
```html:preview
<div style="font-size: 24px;">
<sl-icon library="lucide" name="feather"></sl-icon>
<sl-icon library="lucide" name="pie-chart"></sl-icon>
@@ -281,7 +300,7 @@ This will register the [Font Awesome Free](https://fontawesome.com/) library usi
Icons in this library are licensed under the [Font Awesome Free License](https://github.com/FortAwesome/Font-Awesome/blob/master/LICENSE.txt). Some of the icons that appear on the Font Awesome website require a license and are therefore not available in the CDN.
```html preview
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
@@ -327,22 +346,22 @@ This will register the [Heroicons](https://heroicons.com/) library using the jsD
Icons in this library are licensed under the [MIT License](https://github.com/tailwindlabs/heroicons/blob/master/LICENSE).
```html preview
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('heroicons', {
resolver: name => `https://cdn.jsdelivr.net/npm/heroicons@0.4.2/outline/${name}.svg`
resolver: name => `https://cdn.jsdelivr.net/npm/heroicons@2.0.1/24/outline/${name}.svg`
});
</script>
<div style="font-size: 24px;">
<sl-icon library="heroicons" name="chat"></sl-icon>
<sl-icon library="heroicons" name="chat-bubble-left"></sl-icon>
<sl-icon library="heroicons" name="cloud"></sl-icon>
<sl-icon library="heroicons" name="cog"></sl-icon>
<sl-icon library="heroicons" name="document-text"></sl-icon>
<sl-icon library="heroicons" name="gift"></sl-icon>
<sl-icon library="heroicons" name="volume-up"></sl-icon>
<sl-icon library="heroicons" name="speaker-wave"></sl-icon>
</div>
```
@@ -352,7 +371,7 @@ This will register the [Iconoir](https://iconoir.com/) library using the jsDeliv
Icons in this library are licensed under the [MIT License](https://github.com/lucaburgio/iconoir/blob/master/LICENSE).
```html preview
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
@@ -377,7 +396,7 @@ This will register the [Ionicons](https://ionicons.com/) library using the jsDel
Icons in this library are licensed under the [MIT License](https://github.com/ionic-team/ionicons/blob/master/LICENSE).
```html preview
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
@@ -422,7 +441,7 @@ This will register the [Jam Icons](https://jam-icons.com/) library using the jsD
Icons in this library are licensed under the [MIT License](https://github.com/michaelampr/jam/blob/master/LICENSE).
```html preview
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
@@ -455,7 +474,7 @@ This will register the [Material Icons](https://material.io/resources/icons/?sty
Icons in this library are licensed under the [Apache 2.0 License](https://github.com/google/material-design-icons/blob/master/LICENSE).
```html preview
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
@@ -498,7 +517,7 @@ This will register the [Remix Icon](https://remixicon.com/) library using the js
Icons in this library are licensed under the [Apache 2.0 License](https://github.com/Remix-Design/RemixIcon/blob/master/License).
```html preview
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
@@ -529,13 +548,45 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
</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>
```
### Unicons
This will register the [Unicons](https://iconscout.com/unicons) library using the jsDelivr CDN. This library has two variations: line (default) and solid (`*-s`). A mutator function is required to set the SVG's `fill` to `currentColor`.
Icons in this library are licensed under the [Apache 2.0 License](https://github.com/Iconscout/unicons/blob/master/LICENSE). Some of the icons that appear on the Unicons website, particularly many of the solid variations, require a license and are therefore not available in the CDN.
```html preview
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
@@ -601,6 +652,19 @@ If you want to change the icons Shoelace uses internally, you can register an ic
<!-- Supporting scripts and styles for the search utility -->
<script>
function wrapWithTooltip(item) {
const tooltip = document.createElement('sl-tooltip');
tooltip.content = item.getAttribute('data-name');
// Close open tooltips
document.querySelectorAll('.icon-list sl-tooltip[open]').forEach(tooltip => tooltip.hide());
// Wrap it with a tooltip and trick it into showing up
item.parentNode.insertBefore(tooltip, item);
tooltip.appendChild(item);
requestAnimationFrame(() => tooltip.dispatchEvent(new MouseEvent('mouseover')));
}
fetch('/dist/assets/icons/icons.json')
.then(res => res.json())
.then(icons => {
@@ -621,22 +685,26 @@ If you want to change the icons Shoelace uses internally, you can register an ic
item.setAttribute('data-terms', [i.name, i.title, ...(i.tags || []), ...(i.categories || [])].join(' '));
item.innerHTML = `
<svg width="1em" height="1em" fill="currentColor">
<use xlink:href="/assets/icons/sprite.svg#${i.name}"></use>
<use xlink:href="/assets/images/sprite.svg#${i.name}"></use>
</svg>
`;
list.appendChild(item);
const tooltip = document.createElement('sl-tooltip');
tooltip.content = i.name;
tooltip.appendChild(item);
list.appendChild(tooltip);
// Wrap it with a tooltip the first time the mouse lands on it. We do this instead of baking them into the DOM
// to improve this page's performance. See: https://github.com/shoelace-style/shoelace/issues/1122
item.addEventListener('mouseover', () => wrapWithTooltip(item), { once: true });
// Copy on click
item.addEventListener('click', () => {
const tooltip = item.closest('sl-tooltip');
copyInput.value = i.name;
copyInput.select();
document.execCommand('copy');
tooltip.content = 'Copied!';
setTimeout(() => tooltip.content = i.name, 1000);
if (tooltip) {
tooltip.content = 'Copied!';
setTimeout(() => tooltip.content = i.name, 1000);
}
});
});
@@ -657,12 +725,12 @@ If you want to change the icons Shoelace uses internally, you can register an ic
});
// Sort by type and remember preference
const iconType = localStorage.getItem('sl-icon:type') || 'outline';
const iconType = sessionStorage.getItem('sl-icon:type') || 'outline';
select.value = iconType;
list.setAttribute('data-type', select.value);
select.addEventListener('sl-change', () => {
list.setAttribute('data-type', select.value);
localStorage.setItem('sl-icon:type', select.value);
sessionStorage.setItem('sl-icon:type', select.value);
});
});
</script>
@@ -768,5 +836,3 @@ If you want to change the icons Shoelace uses internally, you can register an ic
}
}
</style>
[component-metadata:sl-icon]

View File

@@ -1,12 +1,13 @@
# Image Comparer
[component-header:sl-image-comparer]
Compare visual differences between similar photos with a sliding panel.
---
meta:
title: Image Comparer
description: Compare visual differences between similar photos with a sliding panel.
layout: component
---
For best results, use images that share the same dimensions. The slider can be controlled by dragging or pressing the left and right arrow keys. (Tip: press shift + arrows to move the slider in larger intervals, or home + end to jump to the beginning or end.)
```html preview
```html:preview
<sl-image-comparer>
<img
slot="before"
@@ -21,7 +22,7 @@ For best results, use images that share the same dimensions. The slider can be c
</sl-image-comparer>
```
```jsx react
```jsx:react
import { SlImageComparer } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -46,7 +47,7 @@ const App = () => (
Use the `position` attribute to set the initial position of the slider. This is a percentage from `0` to `100`.
```html preview
```html:preview
<sl-image-comparer position="25">
<img
slot="before"
@@ -61,7 +62,7 @@ Use the `position` attribute to set the initial position of the slider. This is
</sl-image-comparer>
```
```jsx react
```jsx:react
import { SlImageComparer } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -79,5 +80,3 @@ const App = () => (
</SlImageComparer>
);
```
[component-metadata:sl-image-comparer]

View File

@@ -1,18 +1,19 @@
# Include
[component-header:sl-include]
Includes give you the power to embed external HTML files into the page.
---
meta:
title: Include
description: Includes give you the power to embed external HTML files into the page.
layout: component
---
Included files are asynchronously requested using `window.fetch()`. Requests are cached, so the same file can be included multiple times, but only one request will be made.
The included content will be inserted into the `<sl-include>` element's default slot so it can be easily accessed and styled through the light DOM.
```html preview
```html:preview
<sl-include src="https://shoelace.style/assets/examples/include.html"></sl-include>
```
```jsx react
```jsx:react
import { SlInclude } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlInclude src="https://shoelace.style/assets/examples/include.html" />;
@@ -32,14 +33,16 @@ If the request fails, the `sl-error` event will be emitted. In this case, `event
<script>
const include = document.querySelector('sl-include');
include.addEventListener('sl-load', () => {
console.log('Success');
include.addEventListener('sl-load', event => {
if (event.eventPhase === Event.AT_TARGET) {
console.log('Success');
}
});
include.addEventListener('sl-error', event => {
console.log('Error', event.detail.status);
if (event.eventPhase === Event.AT_TARGET) {
console.log('Error', event.detail.status);
}
});
</script>
```
[component-metadata:sl-include]

View File

@@ -1,20 +1,23 @@
# Input
---
meta:
title: Input
description: Inputs collect data from the user.
layout: component
---
[component-header:sl-input]
Inputs collect data from the user.
```html preview
```html:preview
<sl-input></sl-input>
```
```jsx react
```jsx:react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
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.
:::tip
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
:::
## Examples
@@ -22,11 +25,11 @@ const App = () => <SlInput />;
Use the `label` attribute to give the input an accessible label. For labels that contain HTML, use the `label` slot instead.
```html preview
```html:preview
<sl-input label="What is your name?"></sl-input>
```
```jsx react
```jsx:react
import { SlIcon, SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlInput label="What is your name?" />;
@@ -36,11 +39,11 @@ const App = () => <SlInput label="What is your name?" />;
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
```html:preview
<sl-input label="Nickname" help-text="What would you like people to call you?"></sl-input>
```
```jsx react
```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?" />;
@@ -50,11 +53,11 @@ const App = () => <SlInput label="Nickname" help-text="What would you like peopl
Use the `placeholder` attribute to add a placeholder.
```html preview
```html:preview
<sl-input placeholder="Type something"></sl-input>
```
```jsx react
```jsx:react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlInput placeholder="Type something" />;
@@ -64,11 +67,11 @@ const App = () => <SlInput placeholder="Type something" />;
Add the `clearable` attribute to add a clear button when the input has content.
```html preview
```html:preview
<sl-input placeholder="Clearable" clearable></sl-input>
```
```jsx react
```jsx:react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlInput placeholder="Clearable" clearable />;
@@ -76,49 +79,77 @@ 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.
Add the `password-toggle` attribute to add a toggle button that will show the password when activated.
```html preview
<sl-input type="password" placeholder="Password Toggle" size="small" toggle-password></sl-input>
<br />
<sl-input type="password" placeholder="Password Toggle" size="medium" toggle-password></sl-input>
<br />
<sl-input type="password" placeholder="Password Toggle" size="large" toggle-password></sl-input>
```html:preview
<sl-input type="password" placeholder="Password Toggle" password-toggle></sl-input>
```
```jsx react
```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 />
</>
);
const App = () => <SlInput type="password" placeholder="Password Toggle" size="medium" password-toggle />;
```
### Filled Inputs
Add the `filled` attribute to draw a filled input.
```html preview
```html:preview
<sl-input placeholder="Type something" filled></sl-input>
```
```jsx react
```jsx:react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlInput placeholder="Type something" filled />;
```
### Disabled
Use the `disabled` attribute to disable an input.
```html:preview
<sl-input placeholder="Disabled" disabled></sl-input>
```
```jsx:react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlInput placeholder="Disabled" disabled />;
```
### Sizes
Use the `size` attribute to change an input's size.
```html:preview
<sl-input placeholder="Small" size="small"></sl-input>
<br />
<sl-input placeholder="Medium" size="medium"></sl-input>
<br />
<sl-input placeholder="Large" size="large"></sl-input>
```
```jsx:react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlInput placeholder="Small" size="small" />
<br />
<SlInput placeholder="Medium" size="medium" />
<br />
<SlInput placeholder="Large" size="large" />
</>
);
```
### Pill
Use the `pill` attribute to give inputs rounded edges.
```html preview
```html:preview
<sl-input placeholder="Small" size="small" pill></sl-input>
<br />
<sl-input placeholder="Medium" size="medium" pill></sl-input>
@@ -126,7 +157,7 @@ Use the `pill` attribute to give inputs rounded edges.
<sl-input placeholder="Large" size="large" pill></sl-input>
```
```jsx react
```jsx:react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -144,76 +175,24 @@ const App = () => (
The `type` attribute controls the type of input the browser renders.
```html preview
<sl-input type="email" Placeholder="Email"></sl-input>
```html:preview
<sl-input type="email" placeholder="Email"></sl-input>
<br />
<sl-input type="number" Placeholder="Number"></sl-input>
<sl-input type="number" placeholder="Number"></sl-input>
<br />
<sl-input type="date" Placeholder="Date"></sl-input>
<sl-input type="date" placeholder="Date"></sl-input>
```
```jsx react
```jsx:react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlInput type="email" Placeholder="Email" />
<SlInput type="email" placeholder="Email" />
<br />
<SlInput type="number" Placeholder="Number" />
<SlInput type="number" placeholder="Number" />
<br />
<SlInput type="date" Placeholder="Date" />
</>
);
```
### Disabled
Use the `disabled` attribute to disable an input.
```html preview
<sl-input placeholder="Disabled" size="small" disabled></sl-input>
<br />
<sl-input placeholder="Disabled" size="medium" disabled></sl-input>
<br />
<sl-input placeholder="Disabled" size="large" disabled></sl-input>
```
```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 />
<sl-input placeholder="Medium" size="medium"></sl-input>
<br />
<sl-input placeholder="Large" size="large"></sl-input>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlInput placeholder="Small" size="small" />
<br />
<SlInput placeholder="Medium" size="medium" />
<br />
<SlInput placeholder="Large" size="large" />
<SlInput type="date" placeholder="Date" />
</>
);
```
@@ -222,7 +201,7 @@ const App = () => (
Use the `prefix` and `suffix` slots to add icons.
```html preview
```html:preview
<sl-input placeholder="Small" size="small">
<sl-icon name="house" slot="prefix"></sl-icon>
<sl-icon name="chat" slot="suffix"></sl-icon>
@@ -239,7 +218,7 @@ Use the `prefix` and `suffix` slots to add icons.
</sl-input>
```
```jsx react
```jsx:react
import { SlIcon, SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -264,29 +243,36 @@ const App = () => (
### Customizing Label Position
Use parts to customize the label's position.
Use [CSS parts](#css-parts) to customize the way form controls are drawn. This example uses CSS grid to position the label to the left of the control, but the possible orientations are nearly endless. The same technique works for inputs, textareas, radio groups, and similar form controls.
```html preview
<sl-input class="label-on-left" label="Name"></sl-input><br />
<sl-input class="label-on-left" label="Email" type="email"></sl-input>
```html:preview
<sl-input class="label-on-left" label="Name" help-text="Enter your name"></sl-input>
<sl-input class="label-on-left" label="Email" type="email" help-text="Enter your email"></sl-input>
<sl-textarea class="label-on-left" label="Bio" help-text="Tell us something about yourself"></sl-textarea>
<style>
.label-on-left {
--label-width: 3.75rem;
--gap-width: 1rem;
}
.label-on-left + .label-on-left {
margin-top: var(--sl-spacing-medium);
}
.label-on-left::part(form-control) {
display: flex;
display: grid;
grid: auto / var(--label-width) 1fr;
gap: var(--sl-spacing-3x-small) var(--gap-width);
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;
.label-on-left::part(form-control-help-text) {
grid-column-start: 2;
}
</style>
```
[component-metadata:sl-input]

View File

@@ -1,16 +1,17 @@
# Menu Item
---
meta:
title: Menu Item
description: Menu items provide options for the user to pick from in a menu.
layout: component
---
[component-header:sl-menu-item]
Menu items provide options for the user to pick from in a menu.
```html preview
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item>Option 1</sl-menu-item>
<sl-menu-item>Option 2</sl-menu-item>
<sl-menu-item>Option 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item checked>Checked</sl-menu-item>
<sl-menu-item type="checkbox" checked>Checkbox</sl-menu-item>
<sl-menu-item disabled>Disabled</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>
@@ -24,7 +25,9 @@ Menu items provide options for the user to pick from in a menu.
</sl-menu>
```
```jsx react
{% raw %}
```jsx:react
import { SlDivider, SlIcon, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -33,7 +36,9 @@ const App = () => (
<SlMenuItem>Option 2</SlMenuItem>
<SlMenuItem>Option 3</SlMenuItem>
<SlDivider />
<SlMenuItem checked>Checked</SlMenuItem>
<SlMenuItem type="checkbox" checked>
Checkbox
</SlMenuItem>
<SlMenuItem disabled>Disabled</SlMenuItem>
<SlDivider />
<SlMenuItem>
@@ -48,37 +53,15 @@ const App = () => (
);
```
{% endraw %}
## Examples
### Checked
Use the `checked` attribute to draw menu items in a checked state.
```html preview
<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
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item>Option 1</sl-menu-item>
<sl-menu-item disabled>Option 2</sl-menu-item>
@@ -86,7 +69,9 @@ Add the `disabled` attribute to disable the menu item so it cannot be selected.
</sl-menu>
```
```jsx react
{% raw %}
```jsx:react
import { SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -98,11 +83,13 @@ const App = () => (
);
```
{% endraw %}
### Prefix & Suffix
Add content to the start and end of menu items using the `prefix` and `suffix` slots.
```html preview
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item>
<sl-icon slot="prefix" name="house"></sl-icon>
@@ -124,7 +111,9 @@ Add content to the start and end of menu items using the `prefix` and `suffix` s
</sl-menu>
```
```jsx react
{% raw %}
```jsx:react
import { SlBadge, SlDivider, SlIcon, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -152,15 +141,53 @@ const App = () => (
);
```
{% endraw %}
### Checkbox Menu Items
Set the `type` attribute to `checkbox` to create a menu item that will toggle on and off when selected. You can use the `checked` attribute to set the initial state.
Checkbox menu items are visually indistinguishable from regular menu items. Their ability to be toggled is primarily inferred from context, much like you'd find in the menu of a native app.
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item type="checkbox">Autosave</sl-menu-item>
<sl-menu-item type="checkbox" checked>Check Spelling</sl-menu-item>
<sl-menu-item type="checkbox">Word Wrap</sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import { SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem type="checkbox">Autosave</SlMenuItem>
<SlMenuItem type="checkbox" checked>
Check Spelling
</SlMenuItem>
<SlMenuItem type="checkbox">Word Wrap</SlMenuItem>
</SlMenu>
);
```
{% endraw %}
### Value & Selection
The `value` attribute can be used to assign a hidden value, such as a unique identifier, to a menu item. When an item is selected, the `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
```html:preview
<sl-menu class="menu-value" style="max-width: 200px;">
<sl-menu-item value="opt-1">Option 1</sl-menu-item>
<sl-menu-item value="opt-2">Option 2</sl-menu-item>
<sl-menu-item value="opt-3">Option 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item type="checkbox" value="opt-4">Checkbox 4</sl-menu-item>
<sl-menu-item type="checkbox" value="opt-5">Checkbox 5</sl-menu-item>
<sl-menu-item type="checkbox" value="opt-6">Checkbox 6</sl-menu-item>
</sl-menu>
<script>
@@ -169,16 +196,19 @@ The `value` attribute can be used to assign a hidden value, such as a unique ide
menu.addEventListener('sl-select', event => {
const item = event.detail.item;
// Toggle checked state
item.checked = !item.checked;
// Log value
console.log(`Selected value: ${item.value}`);
if (item.type === 'checkbox') {
console.log(`Selected value: ${item.value} (${item.checked ? 'checked' : 'unchecked'})`);
} else {
console.log(`Selected value: ${item.value}`);
}
});
</script>
```
```jsx react
{% raw %}
```jsx:react
import { SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => {
@@ -202,4 +232,4 @@ const App = () => {
};
```
[component-metadata:sl-menu-item]
{% endraw %}

View File

@@ -1,10 +1,11 @@
# Menu Label
---
meta:
title: Menu Label
description: Menu labels are used to describe a group of menu items.
layout: component
---
[component-header:sl-menu-label]
Menu labels are used to describe a group of menu items.
```html preview
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-label>Fruits</sl-menu-label>
<sl-menu-item value="apple">Apple</sl-menu-item>
@@ -18,7 +19,9 @@ Menu labels are used to describe a group of menu items.
</sl-menu>
```
```jsx react
{% raw %}
```jsx:react
import { SlDivider, SlMenu, SlMenuLabel, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -36,4 +39,4 @@ const App = () => (
);
```
[component-metadata:sl-menu-label]
{% endraw %}

View File

@@ -1,12 +1,13 @@
# Menu
[component-header:sl-menu]
Menus provide a list of options for the user to choose from.
---
meta:
title: Menu
description: Menus provide a list of options for the user to choose from.
layout: component
---
You can use [menu items](/components/menu-item), [menu labels](/components/menu-label), and [dividers](/components/divider) to compose a menu. Menus support keyboard interactions, including type-to-select an option.
```html preview
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item value="undo">Undo</sl-menu-item>
<sl-menu-item value="redo">Redo</sl-menu-item>
@@ -18,7 +19,9 @@ You can use [menu items](/components/menu-item), [menu labels](/components/menu-
</sl-menu>
```
```jsx react
{% raw %}
```jsx:react
import { SlDivider, SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -34,6 +37,8 @@ const App = () => (
);
```
?> 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.
{% endraw %}
[component-metadata:sl-menu]
:::tip
Menus are intended for system menus (dropdown menus, select menus, context menus, etc.). They should not be mistaken for navigation menus which serve a different purpose and have a different semantic meaning. If you're building navigation, use `<nav>` and `<a>` elements instead.
:::

View File

@@ -1,12 +1,13 @@
# Mutation Observer
[component-header:sl-mutation-observer]
The Mutation Observer component offers a thin, declarative interface to the [`MutationObserver API`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver).
---
meta:
title: Mutation Observer
description: The Mutation Observer component offers a thin, declarative interface to the MutationObserver API.
layout: component
---
The mutation observer will report changes to the content it wraps through the `sl-mutation` event. When emitted, a collection of [MutationRecord](https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord) objects will be attached to `event.detail` that contains information about how it changed.
```html preview
```html:preview
<div class="mutation-overview">
<sl-mutation-observer attr="variant">
<sl-button variant="primary">Click to mutate</sl-button>
@@ -42,15 +43,15 @@ The mutation observer will report changes to the content it wraps through the `s
</div>
```
```jsx react
```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;
display: flex;
border: solid 2px var(--sl-input-border-color);
align-items: center;
justify-content: center;
text-align: center;
padding: 4rem 2rem;
@@ -82,7 +83,9 @@ const App = () => {
};
```
?> 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.
:::tip
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
@@ -90,7 +93,7 @@ const App = () => {
Use the `child-list` attribute to watch for new child elements that are added or removed.
```html preview
```html:preview
<div class="mutation-child-list">
<sl-mutation-observer child-list>
<div class="buttons">
@@ -141,7 +144,7 @@ Use the `child-list` attribute to watch for new child elements that are added or
</div>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlButton, SlMutationObserver } from '@shoelace-style/shoelace/dist/react';
@@ -189,5 +192,3 @@ const App = () => {
);
};
```
[component-metadata:sl-mutation-observer]

View File

@@ -0,0 +1,80 @@
---
meta:
title: Option
description: Options define the selectable items within various form controls such as select.
layout: component
---
```html:preview
<sl-select label="Select one">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
## Examples
### Disabled
Use the `disabled` attribute to disable an option and prevent it from being selected.
```html:preview
<sl-select label="Select one">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2" disabled>Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2" disabled>
Option 2
</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Prefix & Suffix
Add icons to the start and end of menu items using the `prefix` and `suffix` slots.
```html:preview
<sl-select label="Select one">
<sl-option value="option-1">
<sl-icon slot="prefix" name="envelope"></sl-icon>
Email
<sl-icon slot="suffix" name="patch-check"></sl-icon>
</sl-option>
<sl-option value="option-2">
<sl-icon slot="prefix" name="telephone"></sl-icon>
Phone
<sl-icon slot="suffix" name="patch-check"></sl-icon>
</sl-option>
<sl-option value="option-3">
<sl-icon slot="prefix" name="chat-dots"></sl-icon>
Chat
<sl-icon slot="suffix" name="patch-check"></sl-icon>
</sl-option>
</sl-select>
```

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,15 @@
# Progress Bar
---
meta:
title: Progress Bar
description: Progress bars are used to show the status of an ongoing operation.
layout: component
---
[component-header:sl-progress-bar]
Progress bars are used to show the status of an ongoing operation.
```html preview
```html:preview
<sl-progress-bar value="50"></sl-progress-bar>
```
```jsx react
```jsx:react
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlProgressBar value={50} />;
@@ -20,11 +21,11 @@ const App = () => <SlProgressBar value={50} />;
Use the `label` attribute to label the progress bar and tell assistive devices how to announce it.
```html preview
```html:preview
<sl-progress-bar value="50" label="Upload progress"></sl-progress-bar>
```
```jsx react
```jsx:react
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlProgressBar value="50" label="Upload progress" />;
@@ -34,21 +35,25 @@ const App = () => <SlProgressBar value="50" label="Upload progress" />;
Use the `--height` custom property to set the progress bar's height.
```html preview
```html:preview
<sl-progress-bar value="50" style="--height: 6px;"></sl-progress-bar>
```
```jsx react
{% raw %}
```jsx:react
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlProgressBar value={50} style={{ '--height': '6px' }} />;
```
{% endraw %}
### Showing Values
Use the default slot to show a value.
```html preview
```html:preview
<sl-progress-bar value="50" class="progress-bar-values">50%</sl-progress-bar>
<br />
@@ -75,7 +80,7 @@ Use the default slot to show a value.
</script>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlButton, SlIcon, SlProgressBar } from '@shoelace-style/shoelace/dist/react';
@@ -111,14 +116,12 @@ const App = () => {
The `indeterminate` attribute can be used to inform the user that the operation is pending, but its status cannot currently be determined. In this state, `value` is ignored and the label, if present, will not be shown.
```html preview
```html:preview
<sl-progress-bar indeterminate></sl-progress-bar>
```
```jsx react
```jsx:react
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlProgressBar indeterminate />;
```
[component-metadata:sl-progress-bar]

View File

@@ -1,14 +1,15 @@
# Progress Ring
---
meta:
title: Progress Ring
description: Progress rings are used to show the progress of a determinate operation in a circular fashion.
layout: component
---
[component-header:sl-progress-ring]
Progress rings are used to show the progress of a determinate operation in a circular fashion.
```html preview
```html:preview
<sl-progress-ring value="25"></sl-progress-ring>
```
```jsx react
```jsx:react
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlProgressRing value="25" />;
@@ -20,45 +21,55 @@ const App = () => <SlProgressRing value="25" />;
Use the `--size` custom property to set the diameter of the progress ring.
```html preview
```html:preview
<sl-progress-ring value="50" style="--size: 200px;"></sl-progress-ring>
```
```jsx react
{% raw %}
```jsx:react
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlProgressRing value="50" style={{ '--size': '200px' }} />;
```
### Track Width
{% endraw %}
Use the `--track-width` custom property to set the width of the progress ring's track.
### Track and Indicator Width
```html preview
<sl-progress-ring value="50" style="--track-width: 10px;"></sl-progress-ring>
Use the `--track-width` and `--indicator-width` custom properties to set the width of the progress ring's track and indicator.
```html:preview
<sl-progress-ring value="50" style="--track-width: 6px; --indicator-width: 12px;"></sl-progress-ring>
```
```jsx react
{% raw %}
```jsx:react
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlProgressRing value="50" style={{ '--track-width': '10px' }} />;
const App = () => <SlProgressRing value="50" style={{ '--track-width': '6px', '--indicator-width': '12px' }} />;
```
{% endraw %}
### Colors
To change the color, use the `--track-color` and `--indicator-color` custom properties.
```html preview
```html:preview
<sl-progress-ring
value="50"
style="
--track-color: pink;
--track-color: pink;
--indicator-color: deeppink;
"
></sl-progress-ring>
```
```jsx react
{% raw %}
```jsx:react
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => (
@@ -72,15 +83,17 @@ const App = () => (
);
```
{% endraw %}
### Labels
Use the `label` attribute to label the progress ring and tell assistive devices how to announce it.
```html preview
```html:preview
<sl-progress-ring value="50" label="Upload progress"></sl-progress-ring>
```
```jsx react
```jsx:react
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlProgressRing value="50" label="Upload progress" />;
@@ -90,7 +103,7 @@ const App = () => <SlProgressRing value="50" label="Upload progress" />;
Use the default slot to show a label inside the progress ring.
```html preview
```html:preview
<sl-progress-ring value="50" class="progress-ring-values" style="margin-bottom: .5rem;">50%</sl-progress-ring>
<br />
@@ -117,7 +130,9 @@ Use the default slot to show a label inside the progress ring.
</script>
```
```jsx react
{% raw %}
```jsx:react
import { useState } from 'react';
import { SlButton, SlIcon, SlProgressRing } from '@shoelace-style/shoelace/dist/react';
@@ -151,4 +166,4 @@ const App = () => {
};
```
[component-metadata:sl-progress-ring]
{% endraw %}

View File

@@ -1,12 +1,13 @@
# QR Code
[component-header:sl-qr-code]
Generates a [QR code](https://www.qrcode.com/) and renders it using the [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API).
---
meta:
title: QR Code
description: Generates a QR code and renders it using the Canvas API.
layout: component
---
QR codes are useful for providing small pieces of information to users who can quickly scan them with a smartphone. Most smartphones have built-in QR code scanners, so simply pointing the camera at a QR code will decode it and allow the user to visit a website, dial a phone number, read a message, etc.
```html preview
```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 />
@@ -19,8 +20,10 @@ QR codes are useful for providing small pieces of information to users who can q
const qrCode = container.querySelector('sl-qr-code');
const input = container.querySelector('sl-input');
input.value = qrCode.value;
input.addEventListener('sl-input', () => (qrCode.value = input.value));
customElements.whenDefined('sl-qr-code').then(() => {
input.value = qrCode.value;
input.addEventListener('sl-input', () => (qrCode.value = input.value));
});
</script>
<style>
@@ -34,7 +37,7 @@ QR codes are useful for providing small pieces of information to users who can q
</style>
```
```jsx react
```jsx:react
import { useState } from 'react';
import { SlQrCode, SlInput } from '@shoelace-style/shoelace/dist/react';
@@ -72,11 +75,11 @@ const App = () => {
Use the `fill` and `background` attributes to modify the QR code's colors. You should always ensure good contrast for optimal compatibility with QR code scanners.
```html preview
```html:preview
<sl-qr-code value="https://shoelace.style/" fill="deeppink" background="white"></sl-qr-code>
```
```jsx react
```jsx:react
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlQrCode value="https://shoelace.style/" fill="deeppink" background="white" />;
@@ -86,11 +89,11 @@ const App = () => <SlQrCode value="https://shoelace.style/" fill="deeppink" back
Use the `size` attribute to change the size of the QR code.
```html preview
```html:preview
<sl-qr-code value="https://shoelace.style/" size="64"></sl-qr-code>
```
```jsx react
```jsx:react
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlQrCode value="https://shoelace.style/" size="64" />;
@@ -100,11 +103,11 @@ const App = () => <SlQrCode value="https://shoelace.style/" size="64" />;
Create a rounded effect with the `radius` attribute.
```html preview
```html:preview
<sl-qr-code value="https://shoelace.style/" radius="0.5"></sl-qr-code>
```
```jsx react
```jsx:react
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlQrCode value="https://shoelace.style/" radius="0.5" />;
@@ -114,7 +117,7 @@ const App = () => <SlQrCode value="https://shoelace.style/" radius="0.5" />;
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.
```html preview
```html:preview
<div class="qr-error-correction">
<sl-qr-code value="https://shoelace.style/" error-correction="L"></sl-qr-code>
<sl-qr-code value="https://shoelace.style/" error-correction="M"></sl-qr-code>
@@ -131,7 +134,7 @@ QR codes can be rendered with various levels of [error correction](https://www.q
</style>
```
```jsx react
```jsx:react
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
const css = `
@@ -157,5 +160,3 @@ const App = () => {
);
};
```
[component-metadata:sl-qr-code]

View File

@@ -0,0 +1,296 @@
---
meta:
title: Radio Button
description: Radios buttons allow the user to select a single option from a group using a button-like control.
layout: component
---
Radio buttons are designed to be used with [radio groups](/components/radio-group). When a radio button has focus, the arrow keys can be used to change the selected option just like standard radio controls.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button value="1">Option 1</sl-radio-button>
<sl-radio-button value="2">Option 2</sl-radio-button>
<sl-radio-button value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2">Option 2</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
## Examples
### Checked States
To set the initial value and checked state, use the `value` attribute on the containing radio group.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button value="1">Option 1</sl-radio-button>
<sl-radio-button value="2">Option 2</sl-radio-button>
<sl-radio-button value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2">Option 2</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Disabled
Use the `disabled` attribute to disable a radio button.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button value="1">Option 1</sl-radio-button>
<sl-radio-button value="2" disabled>Option 2</sl-radio-button>
<sl-radio-button value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2" disabled>
Option 2
</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Sizes
Use the `size` attribute to change a radio button's size.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button size="small" value="1">Option 1</sl-radio-button>
<sl-radio-button size="small" value="2">Option 2</sl-radio-button>
<sl-radio-button size="small" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button size="medium" value="1">Option 1</sl-radio-button>
<sl-radio-button size="medium" value="2">Option 2</sl-radio-button>
<sl-radio-button size="medium" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button size="large" value="1">Option 1</sl-radio-button>
<sl-radio-button size="large" value="2">Option 2</sl-radio-button>
<sl-radio-button size="large" value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton size="small" value="1">Option 1</SlRadioButton>
<SlRadioButton size="small" value="2">Option 2</SlRadioButton>
<SlRadioButton size="small" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton size="medium" value="1">Option 1</SlRadioButton>
<SlRadioButton size="medium" value="2">Option 2</SlRadioButton>
<SlRadioButton size="medium" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton size="large" value="1">Option 1</SlRadioButton>
<SlRadioButton size="large" value="2">Option 2</SlRadioButton>
<SlRadioButton size="large" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Pill Buttons
Use the `pill` attribute to give radio buttons rounded edges.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button pill size="small" value="1">Option 1</sl-radio-button>
<sl-radio-button pill size="small" value="2">Option 2</sl-radio-button>
<sl-radio-button pill size="small" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button pill size="medium" value="1">Option 1</sl-radio-button>
<sl-radio-button pill size="medium" value="2">Option 2</sl-radio-button>
<sl-radio-button pill size="medium" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button pill size="large" value="1">Option 1</sl-radio-button>
<sl-radio-button pill size="large" value="2">Option 2</sl-radio-button>
<sl-radio-button pill size="large" value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton pill size="small" value="1">Option 1</SlRadioButton>
<SlRadioButton pill size="small" value="2">Option 2</SlRadioButton>
<SlRadioButton pill size="small" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton pill size="medium" value="1">Option 1</SlRadioButton>
<SlRadioButton pill size="medium" value="2">Option 2</SlRadioButton>
<SlRadioButton pill size="medium" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton pill size="large" value="1">Option 1</SlRadioButton>
<SlRadioButton pill size="large" value="2">Option 2</SlRadioButton>
<SlRadioButton pill size="large" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Prefix and Suffix Icons
Use the `prefix` and `suffix` slots to add icons.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button value="1">
<sl-icon slot="prefix" name="archive"></sl-icon>
Option 1
</sl-radio-button>
<sl-radio-button value="2">
<sl-icon slot="suffix" name="bag"></sl-icon>
Option 2
</sl-radio-button>
<sl-radio-button value="3">
<sl-icon slot="prefix" name="gift"></sl-icon>
<sl-icon slot="suffix" name="cart"></sl-icon>
Option 3
</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import { SlIcon, SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">
<SlIcon slot="prefix" name="archive" />
Option 1
</SlRadioButton>
<SlRadioButton value="2">
<SlIcon slot="suffix" name="bag" />
Option 2
</SlRadioButton>
<SlRadioButton value="3">
<SlIcon slot="prefix" name="gift" />
<SlIcon slot="suffix" name="cart" />
Option 3
</SlRadioButton>
</SlRadioGroup>
);
```
### Buttons with Icons
You can omit button labels and use icons instead. Make sure to set a `label` attribute on each icon so screen readers will announce each option correctly.
```html:preview
<sl-radio-group label="Select an option" name="a" value="neutral">
<sl-radio-button value="angry">
<sl-icon name="emoji-angry" label="Angry"></sl-icon>
</sl-radio-button>
<sl-radio-button value="sad">
<sl-icon name="emoji-frown" label="Sad"></sl-icon>
</sl-radio-button>
<sl-radio-button value="neutral">
<sl-icon name="emoji-neutral" label="Neutral"></sl-icon>
</sl-radio-button>
<sl-radio-button value="happy">
<sl-icon name="emoji-smile" label="Happy"></sl-icon>
</sl-radio-button>
<sl-radio-button value="laughing">
<sl-icon name="emoji-laughing" label="Laughing"></sl-icon>
</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import { SlIcon, SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="neutral">
<SlRadioButton value="angry">
<SlIcon name="emoji-angry" label="Angry" />
</SlRadioButton>
<SlRadioButton value="sad">
<SlIcon name="emoji-frown" label="Sad" />
</SlRadioButton>
<SlRadioButton value="neutral">
<SlIcon name="emoji-neutral" label="Neutral" />
</SlRadioButton>
<SlRadioButton value="happy">
<SlIcon name="emoji-smile" label="Happy" />
</SlRadioButton>
<SlRadioButton value="laughing">
<SlIcon name="emoji-laughing" label="Laughing" />
</SlRadioButton>
</SlRadioGroup>
);
```

View File

@@ -0,0 +1,282 @@
---
meta:
title: Radio Group
description: Radio groups are used to group multiple radios or radio buttons so they function as a single form control.
layout: component
---
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
## Examples
### Help Text
Add descriptive help text to a radio group with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
```html:preview
<sl-radio-group label="Select an option" help-text="Choose the most appropriate option." name="a" value="1">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" help-text="Choose the most appropriate option." name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
### Radio Buttons
[Radio buttons](/components/radio-button) offer an alternate way to display radio controls. In this case, an internal [button group](/components/button-group) is used to group the buttons into a single, cohesive control.
```html:preview
<sl-radio-group label="Select an option" help-text="Select an option that makes you proud." name="a" value="1">
<sl-radio-button value="1">Option 1</sl-radio-button>
<sl-radio-button value="2">Option 2</sl-radio-button>
<sl-radio-button value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import { SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2">Option 2</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Disabling Options
Radios and radio buttons can be disabled by adding the `disabled` attribute to the respective options inside the radio group.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2" disabled>Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2" disabled>
Option 2
</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
### Sizing Options
The size of [Radios](/components/radio) and [Radio Buttons](/components/radio-buttons) will be determined by the Radio Group's `size` attribute.
```html preview
<sl-radio-group label="Select an option" size="medium" value="medium" class="radio-group-size">
<sl-radio value="small">Small</sl-radio>
<sl-radio value="medium">Medium</sl-radio>
<sl-radio value="large">Large</sl-radio>
</sl-radio-group>
<script>
const radioGroup = document.querySelector('.radio-group-size');
radioGroup.addEventListener('sl-change', () => {
radioGroup.size = radioGroup.value;
});
</script>
```
```jsx react
import { useState } from 'react';
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [size, setSize] = useState('medium');
return (
<>
<SlRadioGroup
label="Select an option"
size={size}
value={size}
class="radio-group-size"
onSlChange={event => setSize(event.target.value)}
>
<SlRadio value="small">Small</SlRadio>
<SlRadio value="medium">Medium</SlRadio>
<SlRadio value="large">Large</SlRadio>
</SlRadioGroup>
</>
);
};
```
:::tip
[Radios](/components/radio) and [Radio Buttons](/components/radio-button) also have a `size` attribute. This can be useful in certain compositions, but it will be ignored when used inside of a Radio Group.
:::
### Validation
Setting the `required` attribute to make selecting an option mandatory. If a value has not been selected, it will prevent the form from submitting and display an error message.
```html:preview
<form class="validation">
<sl-radio-group label="Select an option" name="a" required>
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
<br />
<sl-button type="submit" variant="primary">Submit</sl-button>
</form>
<script>
const form = document.querySelector('.validation');
// Handle form submit
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
</script>
```
```jsx:react
import { SlButton, SlIcon, SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => {
function handleSubmit(event) {
event.preventDefault();
alert('All fields are valid!');
}
return (
<form class="custom-validity" onSubmit={handleSubmit}>
<SlRadioGroup label="Select an option" name="a" required onSlChange={handleChange}>
<SlRadio value="1">
Option 1
</SlRadio>
<SlRadiovalue="2">
Option 2
</SlRadio>
<SlRadio value="3">
Option 3
</SlRadio>
</SlRadioGroup>
<br />
<SlButton type="submit" variant="primary">
Submit
</SlButton>
</form>
);
};
```
### Custom Validity
Use the `setCustomValidity()` method to set a custom validation message. This will prevent the form from submitting and make the browser display the error message you provide. To clear the error, call this function with an empty string.
```html:preview
<form class="custom-validity">
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Not me</sl-radio>
<sl-radio value="2">Me neither</sl-radio>
<sl-radio value="3">Choose me</sl-radio>
</sl-radio-group>
<br />
<sl-button type="submit" variant="primary">Submit</sl-button>
</form>
<script>
const form = document.querySelector('.custom-validity');
const radioGroup = form.querySelector('sl-radio-group');
const errorMessage = 'You must choose the last option';
// Set initial validity as soon as the element is defined
customElements.whenDefined('sl-radio').then(() => {
radioGroup.setCustomValidity(errorMessage);
});
// Update validity when a selection is made
form.addEventListener('sl-change', () => {
const isValid = radioGroup.value === '3';
radioGroup.setCustomValidity(isValid ? '' : errorMessage);
});
// Handle form submit
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
</script>
```
```jsx:react
import { useEffect, useRef } from 'react';
import { SlButton, SlIcon, SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const radioGroup = useRef(null);
const errorMessage = 'You must choose this option';
function handleChange() {
radioGroup.current.setCustomValidity(radioGroup.current.value === '3' ? '' : errorMessage);
}
function handleSubmit(event) {
event.preventDefault();
alert('All fields are valid!');
}
useEffect(() => {
radio.current.setCustomValidity(errorMessage);
}, []);
return (
<form class="custom-validity" onSubmit={handleSubmit}>
<SlRadioGroup ref={radioGroup} label="Select an option" name="a" value="1" onSlChange={handleChange}>
<SlRadio value="1">Not me</SlRadio>
<SlRadio value="2">Me neither</SlRadio>
<SlRadio value="3">Choose me</SlRadio>
</SlRadioGroup>
<br />
<SlButton type="submit" variant="primary">
Submit
</SlButton>
</form>
);
};
```

View File

@@ -0,0 +1,142 @@
---
meta:
title: Radio
description: Radios allow the user to select a single option from a group.
layout: component
---
Radios are designed to be used with [radio groups](/components/radio-group).
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
:::tip
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
:::
## Examples
### Initial Value
To set the initial value and checked state, use the `value` attribute on the containing radio group.
```html:preview
<sl-radio-group label="Select an option" name="a" value="3">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="3">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
### Disabled
Use the `disabled` attribute to disable a radio.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2" disabled>Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2" disabled>
Option 2
</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
## Sizes
Add the `size` attribute to the [Radio Group](/components/radio-group) to change the radios' size.
```html preview
<sl-radio-group size="small" value="1">
<sl-radio value="1">Small 1</sl-radio>
<sl-radio value="2">Small 2</sl-radio>
<sl-radio value="3">Small 3</sl-radio>
</sl-radio-group>
<br />
<sl-radio-group size="medium" value="1">
<sl-radio value="1">Medium 1</sl-radio>
<sl-radio value="2">Medium 2</sl-radio>
<sl-radio value="3">Medium 3</sl-radio>
</sl-radio-group>
<br />
<sl-radio-group size="large" value="1">
<sl-radio value="1">Large 1</sl-radio>
<sl-radio value="2">Large 2</sl-radio>
<sl-radio value="3">Large 3</sl-radio>
</sl-radio-group>
```
```jsx react
import { SlRadio } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlRadioGroup size="small" value="1">
<SlRadio value="1">Small 1</SlRadio>
<SlRadio value="2">Small 2</SlRadio>
<SlRadio value="3">Small 3</SlRadio>
</SlRadioGroup>
<br />
<SlRadioGroup size="medium" value="1">
<SlRadio value="1">Medium 1</SlRadio>
<SlRadio value="2">Medium 2</SlRadio>
<SlRadio value="3">Medium 3</SlRadio>
</SlRadioGroup>
<br />
<SlRadioGroup size="large" value="1">
<SlRadio value="1">Large 1</SlRadio>
<SlRadio value="2">Large 2</SlRadio>
<SlRadio value="3">Large 3</SlRadio>
</SlRadioGroup>
</>
);
```

Some files were not shown because too many files have changed in this diff Show More