Compare commits

..

567 Commits

Author SHA1 Message Date
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
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
282 changed files with 22032 additions and 10459 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'
},
@@ -105,6 +113,7 @@ module.exports = {
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',
@@ -149,8 +158,8 @@ 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'],
'one-var': ['warn', 'never'],
'operator-assignment': 'warn',
@@ -171,22 +180,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

@@ -15,13 +15,13 @@ jobs:
strategy:
matrix:
node-version: [14.x, 16.x]
node-version: [16.x, 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'

View File

@@ -8,15 +8,20 @@
"apos",
"atrule",
"autocorrect",
"autofix",
"autoplay",
"bezier",
"boxicons",
"callout",
"callouts",
"chatbubble",
"checkmark",
"claviska",
"Clippy",
"codebases",
"codepen",
"colocated",
"colour",
"combobox",
"Composability",
"Consolas",
@@ -35,11 +40,13 @@
"enterkeyhint",
"eqeqeq",
"erroneou",
"errormessage",
"esbuild",
"exportparts",
"fieldsets",
"formaction",
"formdata",
"formenctype",
"formmethod",
"formnovalidate",
"formtarget",
@@ -72,10 +79,14 @@
"Lucide",
"maxlength",
"Menlo",
"menuitemcheckbox",
"menuitemradio",
"middlewares",
"minlength",
"monospace",
"mousedown",
"mouseup",
"multiselectable",
"nextjs",
"nocheck",
"noopener",
@@ -97,6 +108,7 @@
"Roboto",
"saturationl",
"Schilp",
"scrollbars",
"Segoe",
"semibold",
"slotchange",
@@ -111,9 +123,12 @@
"tera",
"textareas",
"textfield",
"tinycolor",
"transitionend",
"treeitem",
"Triaging",
"turbolinks",
"typeof",
"unbundles",
"unbundling",
"unicons",

View File

@@ -1,11 +1,29 @@
import fs from 'fs';
import { generateCustomData } from 'cem-plugin-vs-code-custom-data-generator';
import commandLineArgs from 'command-line-args';
import { parse } from 'comment-parser';
import { pascalCase } from 'pascal-case';
const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
const { name, description, version, author, homepage, license } = packageData;
// eslint-disable-next-line func-style
const noDash = string => string.replace(/^\s?-/, '').trim();
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

@@ -45,6 +45,7 @@
- [Menu](/components/menu)
- [Menu Item](/components/menu-item)
- [Menu Label](/components/menu-label)
- [Option](/components/option)
- [Progress Bar](/components/progress-bar)
- [Progress Ring](/components/progress-ring)
- [QR Code](/components/qr-code)
@@ -64,6 +65,8 @@
- [Tag](/components/tag)
- [Textarea](/components/textarea)
- [Tooltip](/components/tooltip)
- [Tree](/components/tree)
- [Tree Item](/components/tree-item)
<!--plop:component-->
- Utilities
@@ -75,9 +78,9 @@
- [Format Number](/components/format-number)
- [Include](/components/include)
- [Mutation Observer](/components/mutation-observer)
- [Popup](/components/popup)
- [Relative Time](/components/relative-time)
- [Resize Observer](/components/resize-observer)
- [Responsive Media](/components/responsive-media)
- [Visually Hidden](/components/visually-hidden)
- Design Tokens
@@ -89,6 +92,7 @@
- [Border Radius](/tokens/border-radius)
- [Transition](/tokens/transition)
- [Z-index](/tokens/z-index)
- [More](/tokens/more)
- Tutorials

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 861 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -146,8 +146,6 @@
</div>
<div class="code-block__buttons">
${hasReact ? ` ${htmlButton} ${reactButton} ` : ''}
<button
type="button"
class="code-block__button code-block__toggle"
@@ -167,6 +165,8 @@
</svg>
</button>
${hasReact ? ` ${htmlButton} ${reactButton} ` : ''}
${!code.classList.contains('no-codepen') ? codePenButton : ''}
</div>
</div>
@@ -232,36 +232,45 @@
// Toggle source mode
document.addEventListener('click', event => {
const button = event.target.closest('button');
const button = event.target.closest('.code-block__button');
const codeBlock = button?.closest('.code-block');
if (button?.classList.contains('code-block__button--html')) {
// Show HTML
setFlavor('html');
toggleSource(codeBlock, true);
} else if (button?.classList.contains('code-block__button--react')) {
// Show React
setFlavor('react');
toggleSource(codeBlock, true);
} else if (button?.classList.contains('code-block__toggle')) {
// Toggle source
toggleSource(codeBlock);
} else {
return;
}
// Update flavor buttons
[...document.querySelectorAll('.code-block')].forEach(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');
[...document.querySelectorAll('.code-block')].forEach(cb => {
cb.querySelector('.code-block__button--html')?.classList.toggle(
'code-block__button--selected',
flavor === 'html'
);
cb.querySelector('.code-block__button--react')?.classList.toggle(
'code-block__button--selected',
flavor === 'react'
);
});
});
// Expand and collapse code blocks
document.addEventListener('click', event => {
const toggle = event.target.closest('.code-block__toggle');
function toggleSource(codeBlock, force) {
const toggle = codeBlock.querySelector('.code-block__toggle');
if (toggle) {
const codeBlock = event.target.closest('.code-block');
codeBlock.classList.toggle('code-block--expanded');
codeBlock.classList.toggle('code-block--expanded', force === undefined ? undefined : force);
event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-block--expanded'));
}
});
}
// Show pulse when copying
document.addEventListener('click', event => {

View File

@@ -49,7 +49,7 @@
${escapeHtml(prop.description)}
</td>
<td style="text-align: center;">${
prop.reflects ? '<sl-icon label="yes" name="check"></sl-icon>' : ''
prop.reflects ? '<sl-icon label="yes" name="check-lg"></sl-icon>' : ''
}</td>
<td>${prop.type?.text ? `<code>${escapeHtml(prop.type?.text || '')}</code>` : '-'}</td>
<td>${prop.default ? `<code>${escapeHtml(prop.default)}</code>` : '-'}</td>
@@ -57,7 +57,18 @@
`;
})
.join('')}
</tbody>
<tr>
<td class="nowrap"><code>updateComplete</code></td>
<td>
A read-only promise that resolves when the component has
<a href="/getting-started/usage?id=component-rendering-and-updating">finished updating</a>.
</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
`;
return table.outerHTML;
@@ -110,14 +121,14 @@
.map(
method => `
<tr>
<td class="nowrap"><code>${escapeHtml(method.name)}</code></td>
<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(', ')
method.parameters.map(param => `${param.name}: ${param.type?.text || ''}`).join(', ')
)}</code>
`
: '-'
@@ -283,6 +294,7 @@
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;')
.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" rel="noopener noreferrer" target="_blank">$1</a>')
.replace(/`(.*?)`/g, '<code>$1</code>');
}
@@ -332,7 +344,7 @@
<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-icon slot="prefix" name="github"></sl-icon> Star
</sl-button>
<sl-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
@@ -374,7 +386,7 @@
result += `
<div class="component-header">
<div class="component-header__tag">
<code>&lt;${component.tagName}&gt; | ${component.name}</code>
<code>&lt;${component.tagName}&gt; | ${component.title ?? component.name}</code>
</div>
<div class="component-header__info">
@@ -386,6 +398,10 @@
${component.status}
</sl-badge>
</div>
<div class="component-header__summary">
${component.summary ? `<p>${marked(component.summary)}</p>` : ''}
</div>
</div>
`;
@@ -430,7 +446,7 @@
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>
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/dist/${component.path}"></script>
\`\`\`
</sl-tab-panel>
@@ -438,14 +454,14 @@
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}';
import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/dist/${component.path}';
\`\`\`
</sl-tab-panel>
<sl-tab-panel name="bundler">\n
To import this component using [a bundler](/getting-started/installation#bundling):
\`\`\`js
import '@shoelace-style/shoelace/${component.path}';
import '@shoelace-style/shoelace/dist/${component.path}';
\`\`\`
</sl-tab-panel>
@@ -456,15 +472,45 @@
\`\`\`
</sl-tab-panel>
</sl-tab-group>
<div class="sponsor-callout">
<p>
Shoelace is designed, developed, and maintained by <a href="https://twitter.com/claviska" target="_blank">Cory LaViska</a>.
Please sponsor my open source work on GitHub. Your support will keep this project alive and growing!
</p>
<p>
<sl-button class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
<sl-icon slot="prefix" name="heart"></sl-icon> Sponsor <span class="sponsor-callout__secondary-label">Development</span>
</sl-button>
<sl-button class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
<sl-icon slot="prefix" name="github"></sl-icon> Star <span class="sponsor-callout__secondary-label">on GitHub</span>
</sl-button>
<sl-button class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow <span class="sponsor-callout__secondary-label">on Twitter</span>
</sl-button>
</p>
</div>
`;
}
if (component.slots?.length) {
result += `
## Slots
${createSlotsTable(component.slots)}
_Learn more about [using slots](/getting-started/usage#slots)._
`;
}
if (props?.length) {
result += `
## Properties
## Attributes & Properties
${createPropsTable(props)}
_Learn more about [properties and attributes](/getting-started/usage#properties)._
_Learn more about [attributes and properties](/getting-started/usage#properties)._
`;
}
@@ -487,15 +533,6 @@
`;
}
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
@@ -527,7 +564,7 @@
result += `
## Dependencies
This component imports the following dependencies.
This component automatically imports the following dependencies.
${createDependenciesList(component.tagName, getAllComponents(metadata))}
`;

View File

@@ -178,3 +178,15 @@ body.site-search-visible {
display: none;
}
}
/* Forced colors mode */
@media (forced-colors: active) {
.site-search__panel {
border: solid 1px var(--sl-color-neutral-0);
}
.site-search__results li[aria-selected='true'] a {
outline: dashed 1px SelectedItem;
outline-offset: -1px;
}
}

View File

@@ -11,12 +11,7 @@
const searchBox = document.createElement('div');
searchBox.classList.add('search-box');
searchBox.innerHTML = `
<sl-input
type="search"
placeholder="Search"
clearable
pill
>
<sl-input type="search" placeholder="Search" pill>
<sl-icon slot="prefix" name="search"></sl-icon>
<kbd slot="suffix" title="Press / to search">/</kbd>
</sl-input>
@@ -300,9 +295,12 @@
// Show the search panel slash is pressed outside of a form element
document.addEventListener('keydown', event => {
const isSlash = event.key === '/';
const isCtrlK = (event.metaKey || event.ctrlKey) && event.key === 'k';
if (
!isShowing &&
event.key === '/' &&
(isSlash || isCtrlK) &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();

View File

@@ -47,10 +47,10 @@
</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-menu-item type="checkbox" value="light">Light</sl-menu-item>
<sl-menu-item type="checkbox" value="dark">Dark</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item value="auto">Auto</sl-menu-item>
<sl-menu-item type="checkbox" value="auto">Auto</sl-menu-item>
</sl-menu>
`;
document.querySelector('.sidebar-toggle').insertAdjacentElement('afterend', dropdown);

View File

@@ -50,6 +50,8 @@ strong {
}
.sidebar-buttons {
display: flex;
justify-content: space-between;
text-align: center;
margin-top: 0;
}
@@ -488,14 +490,26 @@ kbd,
.markdown-section p.tip code,
.markdown-section p.warn code {
background-color: var(--sl-color-neutral-100);
white-space: nowrap;
}
/* Sponsorship callouts */
.sponsor-callout {
background: var(--sl-color-warning-100);
border-left: solid 3px var(--sl-color-warning-300);
border-radius: var(--sl-border-radius-medium);
padding: 0.5rem 1.5rem;
}
@media screen and (max-width: 950px) {
.sponsor-callout__secondary-label {
display: none;
}
}
/* Component headers */
.component-header {
border-bottom: solid 1px var(--sl-color-neutral-200);
padding-bottom: 2rem;
margin-top: -1rem;
margin-bottom: 2rem;
}
.component-header__tag {
@@ -504,6 +518,13 @@ kbd,
margin: 0.75rem 0 0.25rem 0;
}
.component-header__summary {
font-size: var(--sl-font-size-large);
line-height: 1.6;
border-top: solid 1px var(--sl-color-neutral-200);
margin-top: 2rem;
}
.markdown-section .component-header__tag code {
background: none;
color: var(--sl-color-neutral-600);
@@ -516,12 +537,6 @@ kbd,
margin-bottom: 0.5rem;
}
/* Lead sentences that occur immediately after the header */
.component-header + p {
font-size: var(--sl-font-size-large);
line-height: 1.6;
}
/* Repo buttons */
.repo-button--sponsor sl-icon {
color: var(--sl-color-pink-600);

View File

@@ -2,8 +2,6 @@
[component-header:sl-alert]
Alerts are used to display important messages inline or as toast notifications.
```html preview
<sl-alert open>
<sl-icon slot="icon" name="info-circle"></sl-icon>

View File

@@ -2,8 +2,6 @@
[component-header:sl-animated-image]
A component for displaying animated GIFs and WEBPs that play and pause on interaction.
```html preview
<sl-animated-image
src="https://shoelace.style/assets/images/walk.gif"

View File

@@ -2,8 +2,6 @@
[component-header:sl-animation]
Animate elements declaratively with nearly 100 baked-in presets, or roll your own with custom keyframes. Powered by the [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API).
To animate an element, wrap it in `<sl-animation>` and set 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
@@ -93,19 +91,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));

View File

@@ -2,8 +2,6 @@
[component-header:sl-avatar]
Avatars are used to represent a person or object.
By default, a generic icon will be shown. You can personalize avatars by adding custom icons, initials, and images. You should always provide a `label` for assistive devices.
```html preview
@@ -21,12 +19,18 @@ 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
<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
@@ -37,6 +41,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"
/>
);
```

View File

@@ -2,8 +2,6 @@
[component-header:sl-badge]
Badges are used to draw attention and display statuses or counts.
```html preview
<sl-badge>Badge</sl-badge>
```

View File

@@ -2,8 +2,6 @@
[component-header:sl-breadcrumb-item]
Breadcrumb Items are used inside [breadcrumbs](/components/breadcrumb) to represent different links.
```html preview
<sl-breadcrumb>
<sl-breadcrumb-item>

View File

@@ -2,8 +2,6 @@
[component-header:sl-breadcrumb]
Breadcrumbs provide a group of links so users can easily navigate a website's hierarchy.
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
@@ -205,9 +203,9 @@ 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>
@@ -237,9 +235,11 @@ 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>

View File

@@ -2,10 +2,8 @@
[component-header:sl-button-group]
Button groups can be used to group related buttons into sections.
```html preview
<sl-button-group>
<sl-button-group label="Alignment">
<sl-button>Left</sl-button>
<sl-button>Center</sl-button>
<sl-button>Right</sl-button>
@@ -16,7 +14,7 @@ Button groups can be used to group related buttons into sections.
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton>Left</SlButton>
<SlButton>Center</SlButton>
<SlButton>Right</SlButton>
@@ -31,7 +29,7 @@ const App = () => (
All button sizes are supported, but avoid mixing sizes within the same button group.
```html preview
<sl-button-group>
<sl-button-group label="Alignment">
<sl-button size="small">Left</sl-button>
<sl-button size="small">Center</sl-button>
<sl-button size="small">Right</sl-button>
@@ -39,7 +37,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,7 +45,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="large">Left</sl-button>
<sl-button size="large">Center</sl-button>
<sl-button size="large">Right</sl-button>
@@ -59,7 +57,7 @@ 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 +66,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 +75,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 +86,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>
<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 +97,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 +105,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 +113,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,7 +121,7 @@ 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>
@@ -135,7 +133,7 @@ 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 +142,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 +151,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 +160,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 +169,7 @@ const App = () => (
<br />
<br />
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton variant="danger">Left</SlButton>
<SlButton variant="danger">Center</SlButton>
<SlButton variant="danger">Right</SlButton>
@@ -185,7 +183,7 @@ const App = () => (
Pill buttons are supported through the button's `pill` attribute.
```html preview
<sl-button-group>
<sl-button-group label="Alignment">
<sl-button size="small" pill>Left</sl-button>
<sl-button size="small" pill>Center</sl-button>
<sl-button size="small" pill>Right</sl-button>
@@ -193,7 +191,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,7 +199,7 @@ 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>
@@ -213,7 +211,7 @@ import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton size="small" pill>
Left
</SlButton>
@@ -228,7 +226,7 @@ const App = () => (
<br />
<br />
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton size="medium" pill>
Left
</SlButton>
@@ -243,7 +241,7 @@ const App = () => (
<br />
<br />
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlButton size="large" pill>
Left
</SlButton>
@@ -263,7 +261,7 @@ const App = () => (
Dropdowns can be placed inside button groups as long as the trigger is an `<sl-button>` element.
```html preview
<sl-button-group>
<sl-button-group label="Example Button Group">
<sl-button>Button</sl-button>
<sl-button>Button</sl-button>
<sl-dropdown>
@@ -281,7 +279,7 @@ Dropdowns can be placed inside button groups as long as the trigger is an `<sl-b
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>
@@ -303,7 +301,7 @@ 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>
<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>
@@ -322,7 +320,7 @@ Create a split button using a button and a dropdown. Use a [visually hidden](/co
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>
@@ -341,7 +339,7 @@ const App = () => (
Buttons can be wrapped in tooltips to provide more detail when the user interacts with them.
```html preview
<sl-button-group>
<sl-button-group label="Alignment">
<sl-tooltip content="I'm on the left">
<sl-button>Left</sl-button>
</sl-tooltip>
@@ -361,7 +359,7 @@ import { SlButton, SlButtonGroup, SlTooltip } from '@shoelace-style/shoelace/dis
const App = () => (
<>
<SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlTooltip content="I'm on the left">
<SlButton>Left</SlButton>
</SlTooltip>

View File

@@ -2,8 +2,6 @@
[component-header:sl-button]
Buttons represent actions that are available to the user.
```html preview
<sl-button>Button</sl-button>
```
@@ -136,7 +134,7 @@ 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
<sl-button variant="default" size="small" circle>

View File

@@ -2,8 +2,6 @@
[component-header:sl-card]
Cards can be used to group related subjects in a container.
```html preview
<sl-card class="card-overview">
<img
@@ -130,7 +128,6 @@ Headers can be used to display titles and more.
<sl-card class="card-header">
<div slot="header">
Header Title
<sl-icon-button name="gear" label="Settings"></sl-icon-button>
</div>
@@ -206,7 +203,7 @@ Footers can be used to display actions, summaries, or other relevant content.
<div slot="footer">
<sl-rating></sl-rating>
<sl-button slot="footer" variant="primary">Preview</sl-button>
<sl-button variant="primary">Preview</sl-button>
</div>
</sl-card>

View File

@@ -2,8 +2,6 @@
[component-header:sl-checkbox]
Checkboxes allow the user to toggle an option on or off.
```html preview
<sl-checkbox>Checkbox</sl-checkbox>
```
@@ -60,6 +58,32 @@ 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.
@@ -74,14 +98,17 @@ 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(() => {
checkbox.setCustomValidity(errorMessage);
});
// Update validity on change
checkbox.addEventListener('sl-change', () => {
checkbox.setCustomValidity(checkbox.checked ? '' : errorMessage);
});
// Handle submit
form.addEventListener('submit', event => {
event.preventDefault();
@@ -93,19 +120,24 @@ Use the `setCustomValidity()` method to set a custom validation message. This wi
```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}>

View File

@@ -2,8 +2,6 @@
[component-header:sl-color-picker]
Color pickers allow the user to select a color.
```html preview
<sl-color-picker label="Select a color"></sl-color-picker>
```
@@ -34,7 +32,7 @@ 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 value="#f5a623ff" opacity label="Select a color"></sl-color-picker>
@@ -48,7 +46,7 @@ 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.
@@ -56,6 +54,7 @@ To prevent users from toggling the format themselves, add the `no-format-toggle`
<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
@@ -66,10 +65,39 @@ 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.

View File

@@ -4,8 +4,6 @@
[component-header:sl-details]
Details show a brief summary and expand to show additional content.
```html preview
<sl-details summary="Toggle Me">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
@@ -48,6 +46,52 @@ 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.

View File

@@ -4,8 +4,6 @@
[component-header:sl-dialog]
Dialogs, sometimes called "modals", appear above the page and require the user's immediate attention.
```html preview
<sl-dialog label="Dialog" class="dialog-overview">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
@@ -153,6 +151,59 @@ const App = () => {
};
```
### 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.

View File

@@ -2,8 +2,6 @@
[component-header:sl-divider]
Dividers are used to visually separate or group elements.
```html preview
<sl-divider></sl-divider>
```

View File

@@ -4,8 +4,6 @@
[component-header:sl-drawer]
Drawers slide in from a container to expose additional options and information.
```html preview
<sl-drawer label="Drawer" class="drawer-overview">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
@@ -182,7 +180,9 @@ 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.
Unlike normal drawers, contained drawers are not modal. This means they do not show an overlay, they do not trap focus, and they are not dismissible with <kbd>Escape</kbd>. This is intentional to allow users to interact with elements outside of the drawer.
```html preview
<div
@@ -196,14 +196,14 @@ 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>
```
@@ -228,7 +228,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
@@ -340,6 +347,54 @@ const App = () => {
};
```
### 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.

View File

@@ -2,8 +2,6 @@
[component-header:sl-dropdown]
Dropdowns expose additional content that "drops down" in a panel.
Dropdowns consist of a trigger and a panel. By default, activating the trigger will expose the panel and interacting outside of the panel will close it.
Dropdowns are designed to work well with [menus](/components/menu) 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.
@@ -16,7 +14,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>
@@ -44,7 +42,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>
@@ -311,6 +311,7 @@ Dropdown panels will be clipped if they're inside a container that has `overflow
<style>
.dropdown-hoist {
position: relative;
border: solid 2px var(--sl-panel-border-color);
padding: var(--sl-spacing-medium);
overflow: hidden;

View File

@@ -2,8 +2,6 @@
[component-header:sl-format-bytes]
Formats a number as a human readable bytes value.
```html preview
<div class="format-bytes-overview">
The file is <sl-format-bytes value="1000"></sl-format-bytes> in size. <br /><br />

View File

@@ -2,8 +2,6 @@
[component-header:sl-format-date]
Formats a date/time using the specified locale and options.
Localization is handled by the browser's [`Intl.DateTimeFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat). No language packs are required.
```html preview

View File

@@ -2,8 +2,6 @@
[component-header:sl-format-number]
Formats a number using the specified locale and options.
Localization is handled by the browser's [`Intl.NumberFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat). No language packs are required.
```html preview

View File

@@ -2,8 +2,6 @@
[component-header:sl-icon-button]
Icons buttons are simple, icon-only buttons that can be used for actions and in toolbars.
For a full list of icons that come bundled with Shoelace, refer to the [icon component](/components/icon).
```html preview

View File

@@ -2,8 +2,6 @@
[component-header:sl-icon]
Icons are symbols that can be used to represent various options within an application.
Shoelace comes bundled with over 1,500 icons courtesy of the [Bootstrap Icons](https://icons.getbootstrap.com/) project. These icons are part of the `default` icon library. If you prefer, you can register [custom icon libraries](#icon-libraries) as well.
?> 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.
@@ -22,9 +20,9 @@ All available icons in the `default` icon library are shown below. Click or tap
<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>
@@ -336,17 +334,17 @@ Icons in this library are licensed under the [MIT License](https://github.com/ta
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>
```

View File

@@ -2,8 +2,6 @@
[component-header:sl-image-comparer]
Compare visual differences between similar photos with a sliding panel.
For best results, use images that share the same dimensions. The slider can be controlled by dragging or pressing the left and right arrow keys. (Tip: press shift + arrows to move the slider in larger intervals, or home + end to jump to the beginning or end.)
```html preview

View File

@@ -2,8 +2,6 @@
[component-header:sl-include]
Includes give you the power to embed external HTML files into the page.
Included files are asynchronously requested using `window.fetch()`. Requests are cached, so the same file can be included multiple times, but only one request will be made.
The included content will be inserted into the `<sl-include>` element's default slot so it can be easily accessed and styled through the light DOM.
@@ -32,12 +30,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>
```

View File

@@ -2,8 +2,6 @@
[component-header:sl-input]
Inputs collect data from the user.
```html preview
<sl-input></sl-input>
```
@@ -76,28 +74,16 @@ 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>
<sl-input type="password" placeholder="Password Toggle" password-toggle></sl-input>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlInput type="password" placeholder="Password Toggle" size="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
@@ -114,6 +100,46 @@ 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.
@@ -166,58 +192,6 @@ const App = () => (
);
```
### 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" />
</>
);
```
### Prefix & Suffix Icons
Use the `prefix` and `suffix` slots to add icons.
@@ -264,27 +238,37 @@ 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>
<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: 60px;
--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: span 2;
padding-left: calc(var(--label-width) + var(--gap-width));
}
</style>
```

View File

@@ -2,15 +2,13 @@
[component-header:sl-menu-item]
Menu items provide options for the user to pick from in a menu.
```html preview
<sl-menu style="max-width: 200px;">
<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>
@@ -33,7 +31,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>
@@ -50,30 +50,6 @@ const App = () => (
## 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.
@@ -152,6 +128,34 @@ const App = () => (
);
```
### Checkbox Menu Items
Set the `type` attribute to `checkbox` to create a menu item that will toggle on and off when selected. You can use the `checked` attribute to set the initial state.
Checkbox menu items are visually indistinguishable from regular menu items. Their ability to be toggled is primarily inferred from context, much like you'd find in the menu of a native app.
```html preview
<sl-menu style="max-width: 200px;">
<sl-menu-item type="checkbox">Autosave</sl-menu-item>
<sl-menu-item type="checkbox" checked>Check Spelling</sl-menu-item>
<sl-menu-item type="checkbox">Word Wrap</sl-menu-item>
</sl-menu>
```
```jsx react
import { SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem type="checkbox">Autosave</SlMenuItem>
<SlMenuItem type="checkbox" checked>
Check Spelling
</SlMenuItem>
<SlMenuItem type="checkbox">Word Wrap</SlMenuItem>
</SlMenu>
);
```
### Value & Selection
The `value` attribute can be used to assign a hidden value, such as a unique identifier, to a menu item. When an item is selected, the `sl-select` event will be emitted and a reference to the item will be available at `event.detail.item`. You can use this reference to access the selected item's value, its checked state, and more.
@@ -161,6 +165,10 @@ The `value` attribute can be used to assign a hidden value, such as a unique ide
<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,11 +177,12 @@ 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>
```

View File

@@ -2,8 +2,6 @@
[component-header:sl-menu-label]
Menu labels are used to describe a group of menu items.
```html preview
<sl-menu style="max-width: 200px;">
<sl-menu-label>Fruits</sl-menu-label>

View File

@@ -2,8 +2,6 @@
[component-header:sl-menu]
Menus provide a list of options for the user to choose from.
You can use [menu items](/components/menu-item), [menu labels](/components/menu-label), and [dividers](/components/divider) to compose a menu. Menus support keyboard interactions, including type-to-select an option.
```html preview

View File

@@ -2,8 +2,6 @@
[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).
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

79
docs/components/option.md Normal file
View File

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

1508
docs/components/popup.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,8 +2,6 @@
[component-header:sl-progress-bar]
Progress bars are used to show the status of an ongoing operation.
```html preview
<sl-progress-bar value="50"></sl-progress-bar>
```

View File

@@ -2,8 +2,6 @@
[component-header:sl-progress-ring]
Progress rings are used to show the progress of a determinate operation in a circular fashion.
```html preview
<sl-progress-ring value="25"></sl-progress-ring>
```
@@ -30,18 +28,18 @@ import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlProgressRing value="50" style={{ '--size': '200px' }} />;
```
### Track Width
### Track and Indicator Width
Use the `--track-width` custom property to set the width of the progress ring's track.
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: 10px;"></sl-progress-ring>
<sl-progress-ring value="50" style="--track-width: 6px; --indicator-width: 12px;"></sl-progress-ring>
```
```jsx react
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlProgressRing value="50" style={{ '--track-width': '10px' }} />;
const App = () => <SlProgressRing value="50" style={{ '--track-width': '6px', '--indicator-width': '12px' }} />;
```
### Colors

View File

@@ -2,8 +2,6 @@
[component-header:sl-qr-code]
Generates a [QR code](https://www.qrcode.com/) and renders it using the [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API).
QR codes are useful for providing small pieces of information to users who can quickly scan them with a smartphone. Most smartphones have built-in QR code scanners, so simply pointing the camera at a QR code will decode it and allow the user to visit a website, dial a phone number, read a message, etc.
```html preview

View File

@@ -2,15 +2,13 @@
[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 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>
```
@@ -18,31 +16,25 @@ Radio buttons are designed to be used with [radio groups](/components/radio-grou
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 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
### Checked States
To set the initial checked state, use the `checked` attribute.
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">
<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 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>
```
@@ -50,16 +42,10 @@ To set the initial checked state, use the `checked` attribute.
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 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>
);
```
@@ -69,10 +55,10 @@ const App = () => (
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 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>
```
@@ -80,16 +66,12 @@ Use the `disabled` attribute to disable a radio button.
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">
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2" disabled>
Option 2
</SlRadioButton>
<SlRadioButton name="option" value="3" disabled>
Option 3
</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
@@ -99,26 +81,26 @@ const App = () => (
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 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">
<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 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">
<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 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>
```
@@ -126,26 +108,26 @@ Use the `size` attribute to change a radio button's size.
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 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">
<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 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">
<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 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>
);
```
@@ -155,26 +137,26 @@ const App = () => (
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 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">
<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 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">
<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 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>
```
@@ -182,26 +164,26 @@ Use the `pill` attribute to give radio buttons rounded edges.
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 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">
<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 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">
<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 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>
);
```
@@ -211,18 +193,18 @@ const App = () => (
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-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 name="a" value="2">
<sl-radio-button value="2">
<sl-icon slot="suffix" name="bag"></sl-icon>
Option 2
</sl-radio-button>
<sl-radio-button name="a" value="3">
<sl-radio-button value="3">
<sl-icon slot="prefix" name="gift"></sl-icon>
<sl-icon slot="suffix" name="cart"></sl-icon>
Option 3
@@ -234,18 +216,18 @@ Use the `prefix` and `suffix` slots to add icons.
import { SlIcon, SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option">
<SlRadioButton name="a" value="1" checked>
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">
<SlIcon slot="prefix" name="archive" />
Option 1
</SlRadioButton>
<SlRadioButton name="a" value="2">
<SlRadioButton value="2">
<SlIcon slot="suffix" name="bag" />
Option 2
</SlRadioButton>
<SlRadioButton name="a" value="3">
<SlRadioButton value="3">
<SlIcon slot="prefix" name="gift" />
<SlIcon slot="suffix" name="cart" />
Option 3
@@ -259,24 +241,24 @@ const App = () => (
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-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 name="a" value="sad">
<sl-radio-button value="sad">
<sl-icon name="emoji-frown" label="Sad"></sl-icon>
</sl-radio-button>
<sl-radio-button name="a" value="neutral" checked>
<sl-radio-button value="neutral">
<sl-icon name="emoji-neutral" label="Neutral"></sl-icon>
</sl-radio-button>
<sl-radio-button name="a" value="happy">
<sl-radio-button value="happy">
<sl-icon name="emoji-smile" label="Happy"></sl-icon>
</sl-radio-button>
<sl-radio-button name="a" value="laughing">
<sl-radio-button value="laughing">
<sl-icon name="emoji-laughing" label="Laughing"></sl-icon>
</sl-radio-button>
</sl-radio-group>
@@ -286,101 +268,28 @@ You can omit button labels and use icons instead. Make sure to set a `label` att
import { SlIcon, SlRadioButton, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option">
<SlRadioButton name="a" value="angry">
<SlRadioGroup label="Select an option" name="a" value="neutral">
<SlRadioButton value="angry">
<SlIcon name="emoji-angry" label="Angry" />
</SlRadioButton>
<SlRadioButton name="a" value="sad">
<SlRadioButton value="sad">
<SlIcon name="emoji-frown" label="Sad" />
</SlRadioButton>
<SlRadioButton name="a" value="neutral" checked>
<SlRadioButton value="neutral">
<SlIcon name="emoji-neutral" label="Neutral" />
</SlRadioButton>
<SlRadioButton name="a" value="happy">
<SlRadioButton value="happy">
<SlIcon name="emoji-smile" label="Happy" />
</SlRadioButton>
<SlRadioButton name="a" value="laughing">
<SlRadioButton 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

@@ -2,13 +2,11 @@
[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 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>
```
@@ -16,31 +14,25 @@ Radio groups are used to group multiple [radios](/components/radio) or [radio bu
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 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
### Showing the Label
### Help Text
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.
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" 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 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>
```
@@ -48,16 +40,10 @@ You can show the fieldset and legend that wraps the radio group using the `field
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 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>
);
```
@@ -67,10 +53,10 @@ const App = () => (
[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 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>
```
@@ -78,18 +64,169 @@ const App = () => (
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 label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2">Option 2</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Disabling Options
Radios and radio buttons can be disabled by adding the `disabled` attribute to the respective options inside the radio group.
```html preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2" disabled>Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx react
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2" disabled>
Option 2
</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
### Validation
Setting the `required` attribute to make selecting an option mandatory. If a value has not been selected, it will prevent the form from submitting and display an error message.
```html preview
<form class="validation">
<sl-radio-group label="Select an option" name="a" required>
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
<br />
<sl-button type="submit" variant="primary">Submit</sl-button>
</form>
<script>
const form = document.querySelector('.validation');
// Handle form submit
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
</script>
```
```jsx react
import { SlButton, SlIcon, SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => {
function handleSubmit(event) {
event.preventDefault();
alert('All fields are valid!');
}
return (
<form class="custom-validity" onSubmit={handleSubmit}>
<SlRadioGroup label="Select an option" name="a" required onSlChange={handleChange}>
<SlRadio value="1">
Option 1
</SlRadio>
<SlRadiovalue="2">
Option 2
</SlRadio>
<SlRadio value="3">
Option 3
</SlRadio>
</SlRadioGroup>
<br />
<SlButton type="submit" variant="primary">
Submit
</SlButton>
</form>
);
};
```
### Custom Validity
Use the `setCustomValidity()` method to set a custom validation message. This will prevent the form from submitting and make the browser display the error message you provide. To clear the error, call this function with an empty string.
```html preview
<form class="custom-validity">
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Not me</sl-radio>
<sl-radio value="2">Me neither</sl-radio>
<sl-radio value="3">Choose me</sl-radio>
</sl-radio-group>
<br />
<sl-button type="submit" variant="primary">Submit</sl-button>
</form>
<script>
const form = document.querySelector('.custom-validity');
const radioGroup = form.querySelector('sl-radio-group');
const errorMessage = 'You must choose the last option';
// Set initial validity as soon as the element is defined
customElements.whenDefined('sl-radio-group').then(() => {
radioGroup.setCustomValidity(errorMessage);
});
// Update validity when a selection is made
form.addEventListener('sl-change', () => {
const isValid = radioGroup.value === '3';
radioGroup.setCustomValidity(isValid ? '' : errorMessage);
});
// Handle form submit
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
</script>
```
```jsx react
import { useEffect, useRef } from 'react';
import { SlButton, SlIcon, SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const radioGroup = useRef(null);
const errorMessage = 'You must choose this option';
function handleChange() {
radioGroup.current.setCustomValidity(radioGroup.current.value === '3' ? '' : errorMessage);
}
function handleSubmit(event) {
event.preventDefault();
alert('All fields are valid!');
}
useEffect(() => {
radio.current.setCustomValidity(errorMessage);
}, []);
return (
<form class="custom-validity" onSubmit={handleSubmit}>
<SlRadioGroup ref={radioGroup} label="Select an option" name="a" value="1" onSlChange={handleChange}>
<SlRadio value="1">Not me</SlRadio>
<SlRadio value="2">Me neither</SlRadio>
<SlRadio value="3">Choose me</SlRadio>
</SlRadioGroup>
<br />
<SlButton type="submit" variant="primary">
Submit
</SlButton>
</form>
);
};
```
[component-metadata:sl-radio-group]

View File

@@ -2,15 +2,13 @@
[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 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>
```
@@ -18,16 +16,10 @@ Radios are designed to be used with [radio groups](/components/radio-group).
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 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>
);
```
@@ -36,15 +28,15 @@ const App = () => (
## Examples
### Checked
### Initial Value
To set the initial checked state, use the `checked` attribute.
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">
<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 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>
```
@@ -52,16 +44,10 @@ To set the initial checked state, use the `checked` attribute.
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 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>
);
```
@@ -71,10 +57,10 @@ const App = () => (
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 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>
```
@@ -82,91 +68,38 @@ Use the `disabled` attribute to disable a radio.
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">
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2" disabled>
Option 2
</SlRadio>
<SlRadio name="option" value="3" disabled>
Option 3
</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
### Custom Validity
## Sizes
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.
Use the `size` attribute to change a radio's size.
```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>
<sl-radio size="small">Small</sl-radio>
<sl-radio size="medium">Medium</sl-radio>
<sl-radio size="large">Large</sl-radio>
```
```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>
);
};
import { SlRadio } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlRadio size="small">Small</SlRadio>
<br />
<SlRadio size="medium">Medium</SlRadio>
<br />
<SlRadio size="large">Large</SlRadio>
</>
);
```
[component-metadata:sl-radio]

View File

@@ -2,8 +2,6 @@
[component-header:sl-range]
Ranges allow the user to select a single value within a given range using a slider.
```html preview
<sl-range></sl-range>
```
@@ -23,7 +21,7 @@ const App = () => <SlRange />;
Use the `label` attribute to give the range an accessible label. For labels that contain HTML, use the `label` slot instead.
```html preview
<sl-range label="Volume" min="0" max="100"></sl-input>
<sl-range label="Volume" min="0" max="100"></sl-range>
```
```jsx react
@@ -37,12 +35,7 @@ const App = () => <SlRange label="Volume" min={0} max={100} />;
Add descriptive help text to a range with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
```html preview
<sl-range
label="Volume"
help-text="Controls the volume of the current song."
min="0"
max="100"
></sl-input>
<sl-range label="Volume" help-text="Controls the volume of the current song." min="0" max="100"></sl-range>
```
```jsx react
@@ -133,6 +126,38 @@ const App = () => (
);
```
### Custom Track Offset
You can customize the initial offset of the active track using the `--track-active-offset` custom property.
```html preview
<sl-range
min="-100"
max="100"
style="
--track-color-active: var(--sl-color-primary-600);
--track-color-inactive: var(--sl-color-primary-100);
--track-active-offset: 50%;
"
></sl-range>
```
```jsx react
import { SlRange } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRange
min={-100}
max={100}
style={{
'--track-color-active': 'var(--sl-color-primary-600)',
'--track-color-inactive': 'var(--sl-color-primary-200)',
'--track-active-offset': '50%'
}}
/>
);
```
### Custom Tooltip Formatter
You can change the tooltip's content by setting the `tooltipFormatter` property to a function that accepts the range's value as an argument.

View File

@@ -2,32 +2,44 @@
[component-header:sl-rating]
Ratings give users a way to quickly view and provide feedback.
```html preview
<sl-rating></sl-rating>
<sl-rating label="Rating"></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlRating />;
const App = () => <SlRating label="Rating" />;
```
## Examples
### Labels
Ratings are commonly identified contextually, so labels aren't displayed. However, you should always provide one for assistive devices using the `label` attribute.
```html preview
<sl-rating label="Rate this component"></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlRating label="Rate this component" />;
```
### Maximum Value
Ratings are 0-5 by default. To change the maximum possible value, use the `max` attribute.
```html preview
<sl-rating max="3"></sl-rating>
<sl-rating label="Rating" max="3"></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlRating max={3} />;
const App = () => <SlRating label="Rating" max={3} />;
```
### Precision
@@ -35,27 +47,27 @@ const App = () => <SlRating max={3} />;
Use the `precision` attribute to let users select fractional ratings.
```html preview
<sl-rating precision="0.5" value="2.5"></sl-rating>
<sl-rating label="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} />;
const App = () => <SlRating label="Rating" precision={0.5} value={2.5} />;
```
## Symbol Sizes
### Symbol Sizes
Set the `--symbol-size` custom property to adjust the size.
```html preview
<sl-rating style="--symbol-size: 2rem;"></sl-rating>
<sl-rating label="Rating" style="--symbol-size: 2rem;"></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlRating style={{ '--symbol-size': '2rem' }} />;
const App = () => <SlRating label="Rating" style={{ '--symbol-size': '2rem' }} />;
```
### Readonly
@@ -63,13 +75,13 @@ const App = () => <SlRating style={{ '--symbol-size': '2rem' }} />;
Use the `readonly` attribute to display a rating that users can't change.
```html preview
<sl-rating readonly value="3"></sl-rating>
<sl-rating label="Rating" readonly value="3"></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlRating readonly value={3} />;
const App = () => <SlRating label="Rating" readonly value={3} />;
```
### Disabled
@@ -77,13 +89,106 @@ const App = () => <SlRating readonly value={3} />;
Use the `disable` attribute to disable the rating.
```html preview
<sl-rating disabled value="3"></sl-rating>
<sl-rating label="Rating" disabled value="3"></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlRating disabled value={3} />;
const App = () => <SlRating label="Rating" disabled value={3} />;
```
### Detecting Hover
Use the `sl-hover` event to detect when the user hovers over (or touch and drag) the rating. This lets you hook into values as the user interacts with the rating, but before they select a value.
The event has a payload with `phase` and `value` properties. The `phase` property tells when hovering starts, moves to a new value, and ends. The `value` property tells what the rating's value would be if the user were to commit to the hovered value.
```html preview
<div class="detect-hover">
<sl-rating label="Rating"></sl-rating>
<span></span>
</div>
<script>
const rating = document.querySelector('.detect-hover > sl-rating');
const span = rating.nextElementSibling;
const terms = ['No rating', 'Terrible', 'Bad', 'OK', 'Good', 'Excellent'];
rating.addEventListener('sl-hover', event => {
span.textContent = terms[event.detail.value];
// Clear feedback when hovering stops
if (event.detail.phase === 'end') {
span.textContent = '';
}
});
</script>
<style>
.detect-hover span {
position: relative;
top: -4px;
left: 8px;
border-radius: var(--sl-border-radius-small);
background: var(--sl-color-neutral-900);
color: var(--sl-color-neutral-0);
text-align: center;
padding: 4px 6px;
}
.detect-hover span:empty {
display: none;
}
</style>
```
```jsx react
import { useState } from 'react';
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const terms = ['No rating', 'Terrible', 'Bad', 'OK', 'Good', 'Excellent'];
const css = `
.detect-hover span {
position: relative;
top: -4px;
left: 8px;
border-radius: var(--sl-border-radius-small);
background: var(--sl-color-neutral-900);
color: var(--sl-color-neutral-0);
text-align: center;
padding: 4px 6px;
}
.detect-hover span:empty {
display: none;
}
`;
function handleHover(event) {
rating.addEventListener('sl-hover', event => {
setFeedback(terms[event.detail.value]);
// Clear feedback when hovering stops
if (event.detail.phase === 'end') {
setFeedback('');
}
});
}
const App = () => {
const [feedback, setFeedback] = useState(true);
return (
<>
<div class="detect-hover">
<SlRating label="Rating" onSlHover={handleHover} />
<span>{feedback}</span>
</div>
<style>{css}</style>
</>
);
};
```
### Custom Icons
@@ -91,7 +196,7 @@ const App = () => <SlRating disabled value={3} />;
You can provide custom icons by passing a function to the `getSymbol` property.
```html preview
<sl-rating class="rating-hearts" style="--symbol-color-active: #ff4136;"></sl-rating>
<sl-rating label="Rating" class="rating-hearts" style="--symbol-color-active: #ff4136;"></sl-rating>
<script>
const rating = document.querySelector('.rating-hearts');
@@ -100,11 +205,14 @@ You can provide custom icons by passing a function to the `getSymbol` property.
```
```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' }} />
<SlRating
label="Rating"
getSymbol={() => '<sl-icon name="heart-fill"></sl-icon>'}
style={{ '--symbol-color-active': '#ff4136' }}
/>
);
```
@@ -113,7 +221,7 @@ const App = () => (
You can also use the `getSymbol` property to render different icons based on value.
```html preview
<sl-rating class="rating-emojis"></sl-rating>
<sl-rating label="Rating" class="rating-emojis"></sl-rating>
<script>
const rating = document.querySelector('.rating-emojis');
@@ -126,7 +234,6 @@ You can also use the `getSymbol` property to render different icons based on val
```
```jsx react
import '@shoelace-style/shoelace/dist/components/icon/icon';
import { SlRating } from '@shoelace-style/shoelace/dist/react';
function getSymbol(value) {
@@ -134,7 +241,7 @@ function getSymbol(value) {
return `<sl-icon name="${icons[value - 1]}"></sl-icon>`;
}
const App = () => <SlRating getSymbol={getSymbol} />;
const App = () => <SlRating label="Rating" getSymbol={getSymbol} />;
```
[component-metadata:sl-rating]

View File

@@ -2,8 +2,6 @@
[component-header:sl-relative-time]
Outputs a localized time phrase relative to the current date and time.
Localization is handled by the browser's [`Intl.RelativeTimeFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat). No language packs are required.
```html preview
@@ -21,8 +19,6 @@ The `date` attribute determines when the date/time is calculated from. It must b
?> When using strings, avoid ambiguous dates such as `03/04/2020` which can be interpreted as March 4 or April 3 depending on the user's browser and locale. Instead, always use a valid [ISO 8601 date time string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse#Date_Time_String_Format) to ensure the date will be parsed properly by all clients.
!> The `Intl.RelativeTimeFormat` API is available [in all major browsers](https://caniuse.com/mdn-javascript_builtins_intl_relativetimeformat), but it only became available to Safari in version 14. If you need to support Safari 13, you'll need to [use a polyfill](https://github.com/catamphetamine/relative-time-format).
## Examples
### Keeping Time in Sync

View File

@@ -2,8 +2,6 @@
[component-header:sl-resize-observer]
The Resize Observer component offers a thin, declarative interface to the [`ResizeObserver API`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).
The resize observer will report changes to the dimensions of the elements it wraps through the `sl-resize` event. When emitted, a collection of [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry) objects will be attached to `event.detail` that contains the target element and information about its dimensions.
```html preview

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

@@ -2,32 +2,28 @@
[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-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
<sl-option value="option-4">Option 4</sl-option>
<sl-option value="option-5">Option 5</sl-option>
<sl-option value="option-6">Option 6</sl-option>
</sl-select>
```
```jsx react
import { SlDivider, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, 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>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
<SlOption value="option-4">Option 4</SlOption>
<SlOption value="option-5">Option 5</SlOption>
<SlOption value="option-6">Option 6</SlOption>
</SlSelect>
);
```
@@ -42,20 +38,20 @@ Use the `label` attribute to give the select an accessible label. For labels tha
```html preview
<sl-select label="Select one">
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, 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>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
@@ -66,20 +62,20 @@ Add descriptive help text to a select with the `help-text` attribute. For help t
```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-option value="1">Novice</sl-option>
<sl-option value="2">Intermediate</sl-option>
<sl-option value="3">Advanced</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, 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>
<SlOption value="1">Novice</SlOption>
<SlOption value="2">Intermediate</SlOption>
<SlOption value="3">Advanced</SlOption>
</SlSelect>
);
```
@@ -90,44 +86,44 @@ 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-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 { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, 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>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Clearable
Use the `clearable` attribute to make the control clearable.
Use the `clearable` attribute to make the control clearable. The clear button only appears when an option is selected.
```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 clearable value="option-1">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, 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>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
@@ -138,20 +134,20 @@ 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-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 { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, 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>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
@@ -162,20 +158,20 @@ 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-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 { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, 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>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
@@ -186,227 +182,167 @@ 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-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 { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, 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>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</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.
To allow multiple options to be selected, use the `multiple` attribute. It's a good practice to use `clearable` when this option is enabled. To set multiple values at once, set `value` to a space-delimited list of values.
```html preview
<sl-select 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 label="Select a Few" value="option-1 option-2 option-3" multiple clearable>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
<sl-option value="option-4">Option 4</sl-option>
<sl-option value="option-5">Option 5</sl-option>
<sl-option value="option-6">Option 6</sl-option>
</sl-select>
```
```jsx react
import { SlDivider, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, 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 label="Select a Few" value="option-1 option-2 option-3" multiple clearable>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
<SlOption value="option-4">Option 4</SlOption>
<SlOption value="option-5">Option 5</SlOption>
<SlOption value="option-6">Option 6</SlOption>
</SlSelect>
);
```
?> 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.
?> Note that multi-select options may wrap, causing the control to expand vertically. You can use the `max-options-visible` attribute to control the maximum number of selected options to show at once.
### Grouping Options
### Setting Initial Values
Options can be grouped visually using menu labels and dividers.
Use the `value` attribute to set the initial selection. When using `multiple`, use space-delimited values to select more than one option.
```html preview
<sl-select 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 value="option-1 option-2" multiple clearable>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
<sl-option value="option-4">Option 4</sl-option>
</sl-select>
```
```jsx react
import { SlDivider, SlMenuItem, SlMenuLabel, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlDivider, SlOption, 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 value="option-1 option-2" multiple clearable>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Grouping Options
Use `<sl-divider>` to group listbox items visually. You can also use `<small>` to provide labels, but they won't be announced by most assistive devices.
```html preview
<sl-select>
<small>Section 1</small>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
<sl-divider></sl-divider>
<small>Section 2</small>
<sl-option value="option-4">Option 4</sl-option>
<sl-option value="option-5">Option 5</sl-option>
<sl-option value="option-6">Option 6</sl-option>
</sl-select>
```
```jsx react
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
<SlOption value="option-4">Option 4</SlOption>
<SlOption value="option-5">Option 5</SlOption>
<SlOption value="option-6">Option 6</SlOption>
</SlSelect>
);
```
### Sizes
Use the `size` attribute to change a select's size.
Use the `size` attribute to change a select's size. Note that size does not apply to listbox options.
```html preview
<sl-select placeholder="Small" size="small" 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 placeholder="Small" size="small">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Medium" size="medium" 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 placeholder="Medium" size="medium">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Large" size="large" 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 placeholder="Large" size="large">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, 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 placeholder="Small" size="small">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Medium" size="medium" multiple>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlSelect placeholder="Medium" size="medium">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Large" size="large" multiple>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlSelect placeholder="Large" size="large">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
</>
);
@@ -414,88 +350,82 @@ const App = () => (
### 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`.
The preferred placement of the select's listbox can be set with the `placement` attribute. Note that the actual position may vary to ensure the panel remains in the viewport. Valid placements are `top` and `bottom`.
```html preview
<sl-select placement="top">
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import {
SlMenuItem,
SlOption,
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>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlDropdown>
);
```
### Prefix & Suffix Icons
### Prefix Icons
Use the `prefix` and `suffix` slots to add icons.
Use the `prefix` slot to prepend an icon to the control.
```html preview
<sl-select placeholder="Small" size="small">
<sl-select placeholder="Small" size="small" clearable>
<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-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Medium" size="medium">
<sl-select placeholder="Medium" size="medium" clearable>
<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-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Large" size="large">
<sl-select placeholder="Large" size="large" clearable>
<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-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlIcon, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlIcon, SlOption, 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>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Medium" size="medium">
<SlIcon name="house" slot="prefix"></SlIcon>
<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>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Large" size="large">
<SlIcon name="house" slot="prefix"></SlIcon>
<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>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
</>
);

View File

@@ -2,8 +2,6 @@
[component-header:sl-skeleton]
Skeletons are used to show where content will eventually be drawn.
These are simple containers for scaffolding layouts that mimic what users will see when content has finished loading. This prevents large areas of empty space during asynchronous operations.
Skeletons try not to be opinionated, as there are endless possibilities for designing layouts. Therefore, you'll likely use more than one skeleton to create the effect you want. If you find yourself using them frequently, consider creating a template that renders them with the desired arrangement and styles.

View File

@@ -2,8 +2,6 @@
[component-header:sl-spinner]
Spinners are used to show the progress of an indeterminate operation.
```html preview
<sl-spinner></sl-spinner>
```

View File

@@ -2,8 +2,6 @@
[component-header:sl-split-panel]
Split panels display two adjacent panels, allowing the user to reposition them.
```html preview
<sl-split-panel>
<div
@@ -381,9 +379,9 @@ Try resizing the example below with each option and notice how the panels respon
</sl-split-panel>
<sl-select label="Primary Panel" value="" style="max-width: 200px; margin-top: 1rem;">
<sl-menu-item value="">None</sl-menu-item>
<sl-menu-item value="start">Start</sl-menu-item>
<sl-menu-item value="end">End</sl-menu-item>
<sl-option value="">None</sl-option>
<sl-option value="start">Start</sl-option>
<sl-option value="end">End</sl-option>
</sl-select>
</div>
@@ -585,11 +583,11 @@ const App = () => (
### Customizing the Divider
You can target the `divider` part to apply CSS properties to the divider. To add a handle, slot an icon or another element into the `handle` slot. When customizing the divider, make sure to think about focus styles for keyboard users.
You can target the `divider` part to apply CSS properties to the divider. To add a custom handle, slot an icon into the `divider` slot. When customizing the divider, make sure to think about focus styles for keyboard users.
```html preview
<sl-split-panel style="--divider-width: 20px;">
<sl-icon slot="handle" name="grip-vertical"></sl-icon>
<sl-icon slot="divider" name="grip-vertical"></sl-icon>
<div
slot="start"
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
@@ -610,7 +608,7 @@ import { SlSplitPanel, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSplitPanel style={{ '--divider-width': '20px' }}>
<SlIcon slot="handle" name="grip-vertical" />
<SlIcon slot="divider" name="grip-vertical" />
<div
slot="start"
style={{
@@ -642,9 +640,9 @@ const App = () => (
Here's a more elaborate example that changes the divider's color and width and adds a styled handle.
```html preview
<div class="split-panel-handle">
<div class="split-panel-divider">
<sl-split-panel>
<sl-icon slot="handle" name="grip-vertical"></sl-icon>
<sl-icon slot="divider" name="grip-vertical"></sl-icon>
<div
slot="start"
style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center;"
@@ -661,15 +659,15 @@ Here's a more elaborate example that changes the divider's color and width and a
</div>
<style>
.split-panel-handle sl-split-panel {
.split-panel-divider sl-split-panel {
--divider-width: 2px;
}
.split-panel-handle sl-split-panel::part(divider) {
.split-panel-divider sl-split-panel::part(divider) {
background-color: var(--sl-color-pink-600);
}
.split-panel-handle sl-icon {
.split-panel-divider sl-icon {
position: absolute;
border-radius: var(--sl-border-radius-small);
background: var(--sl-color-pink-600);
@@ -677,11 +675,11 @@ Here's a more elaborate example that changes the divider's color and width and a
padding: 0.5rem 0.125rem;
}
.split-panel-handle sl-split-panel::part(divider):focus-visible {
.split-panel-divider sl-split-panel::part(divider):focus-visible {
background-color: var(--sl-color-primary-600);
}
.split-panel-handle sl-split-panel:focus-within sl-icon {
.split-panel-divider sl-split-panel:focus-within sl-icon {
background-color: var(--sl-color-primary-600);
color: var(--sl-color-neutral-0);
}
@@ -692,15 +690,15 @@ Here's a more elaborate example that changes the divider's color and width and a
import { SlSplitPanel, SlIcon } from '@shoelace-style/shoelace/dist/react';
const css = `
.split-panel-handle sl-split-panel {
.split-panel-divider sl-split-panel {
--divider-width: 2px;
}
.split-panel-handle sl-split-panel::part(divider) {
.split-panel-divider sl-split-panel::part(divider) {
background-color: var(--sl-color-pink-600);
}
.split-panel-handle sl-icon {
.split-panel-divider sl-icon {
position: absolute;
border-radius: var(--sl-border-radius-small);
background: var(--sl-color-pink-600);
@@ -708,11 +706,11 @@ const css = `
padding: .5rem .125rem;
}
.split-panel-handle sl-split-panel::part(divider):focus-visible {
.split-panel-divider sl-split-panel::part(divider):focus-visible {
background-color: var(--sl-color-primary-600);
}
.split-panel-handle sl-split-panel:focus-within sl-icon {
.split-panel-divider sl-split-panel:focus-within sl-icon {
background-color: var(--sl-color-primary-600);
color: var(--sl-color-neutral-0);
}
@@ -720,9 +718,9 @@ const css = `
const App = () => (
<>
<div className="split-panel-handle">
<div className="split-panel-divider">
<SlSplitPanel>
<SlIcon slot="handle" name="grip-vertical" />
<SlIcon slot="divider" name="grip-vertical" />
<div
slot="start"
style={{

View File

@@ -2,8 +2,6 @@
[component-header:sl-switch]
Switches allow the user to toggle an option on or off.
```html preview
<sl-switch>Switch</sl-switch>
```
@@ -46,12 +44,38 @@ import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
const App = () => <SlSwitch disabled>Disabled</SlSwitch>;
```
### Custom Size
## Sizes
Use the available custom properties to make the switch a different size.
Use the `size` attribute to change a switch's size.
```html preview
<sl-switch style="--width: 80px; --height: 32px; --thumb-size: 26px;">Really big</sl-switch>
<sl-switch size="small">Small</sl-switch>
<br />
<sl-switch size="medium">Medium</sl-switch>
<br />
<sl-switch size="large">Large</sl-switch>
```
```jsx react
import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlSwitch size="small">Small</SlSwitch>
<br />
<SlSwitch size="medium">Medium</SlSwitch>
<br />
<SlSwitch size="large">Large</SlSwitch>
</>
);
```
### Custom Styles
Use the available custom properties to change how the switch is styled.
```html preview
<sl-switch style="--width: 80px; --height: 40px; --thumb-size: 36px;">Really big</sl-switch>
```
```jsx react

View File

@@ -2,8 +2,6 @@
[component-header:sl-tab-group]
Tab groups organize content into a container that shows one section at a time.
Tab groups make use of [tabs](/components/tab) and [tab panels](/components/tab-panel). Each tab must be slotted into the `nav` slot and its `panel` must refer to a tab panel of the same name.
```html preview

View File

@@ -2,8 +2,6 @@
[component-header:sl-tab-panel]
Tab panels are used inside [tab groups](/components/tab-group) to display tabbed content.
```html preview
<sl-tab-group>
<sl-tab slot="nav" panel="general">General</sl-tab>

View File

@@ -2,8 +2,6 @@
[component-header:sl-tab]
Tabs are used inside [tab groups](/components/tab-group) to represent and activate [tab panels](/components/tab-panel).
```html preview
<sl-tab>Tab</sl-tab>
<sl-tab active>Active</sl-tab>

View File

@@ -2,8 +2,6 @@
[component-header:sl-tag]
Tags are used as labels to organize things or to indicate a selection.
```html preview
<sl-tag variant="primary">Primary</sl-tag>
<sl-tag variant="success">Success</sl-tag>

View File

@@ -2,8 +2,6 @@
[component-header:sl-textarea]
Textareas collect data from the user and allow multiple lines of text.
```html preview
<sl-textarea></sl-textarea>
```

View File

@@ -2,8 +2,6 @@
[component-header:sl-tooltip]
Tooltips display additional information based on a specific action.
A tooltip's target is its _first child element_, so you should only wrap one element inside of the tooltip. If you need the tooltip to show up for multiple elements, nest them inside a container first.
Tooltips use `display: contents` so they won't interfere with how elements are positioned in a flex or grid layout.
@@ -94,6 +92,7 @@ Use the `placement` attribute to set the preferred placement of the tooltip.
<style>
.tooltip-placement-example {
width: 250px;
margin: 1rem;
}
.tooltip-placement-example-row:after {
@@ -282,20 +281,14 @@ const App = () => {
};
```
### Remove Arrows
### Removing Arrows
You can control the size of tooltip arrows by overriding the `--sl-tooltip-arrow-size` design token.
You can control the size of tooltip arrows by overriding the `--sl-tooltip-arrow-size` design token. To remove them, set the value to `0` as shown below.
```html preview
<div style="--sl-tooltip-arrow-size: 0;">
<sl-tooltip content="This is a tooltip">
<sl-button>Above</sl-button>
</sl-tooltip>
<sl-tooltip content="This is a tooltip" placement="bottom">
<sl-button>Below</sl-button>
</sl-tooltip>
</div>
<sl-tooltip content="This is a tooltip" style="--sl-tooltip-arrow-size: 0;">
<sl-button>No Arrow</sl-button>
</sl-tooltip>
```
```jsx react
@@ -338,7 +331,7 @@ Use the `content` slot to create tooltips with HTML content. Tooltips are design
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTooltip content="This is a tooltip">
<SlTooltip>
<div slot="content">
I'm not <strong>just</strong> a tooltip, I'm a <em>tooltip</em> with HTML!
</div>
@@ -348,6 +341,26 @@ const App = () => (
);
```
### Setting a Maximum Width
Use the `--max-width` custom property to change the width the tooltip can grow to before wrapping occurs.
```html preview
<sl-tooltip style="--max-width: 80px;" content="This tooltip will wrap after only 80 pixels.">
<sl-button>Hover me</sl-button>
</sl-tooltip>
```
```jsx react
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTooltip style={{ '--max-width': '80px' }} content="This tooltip will wrap after only 80 pixels.">
<SlButton>Hover Me</SlButton>
</SlTooltip>
);
```
### Hoisting
Tooltips will be clipped if they're inside a container that has `overflow: auto|hidden|scroll`. The `hoist` attribute forces the tooltip to use a fixed positioning strategy, allowing it to break out of the container. In this case, the tooltip will be positioned relative to its containing block, which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
@@ -365,10 +378,10 @@ Tooltips will be clipped if they're inside a container that has `overflow: auto|
<style>
.tooltip-hoist {
position: relative;
border: solid 2px var(--sl-panel-border-color);
overflow: hidden;
padding: var(--sl-spacing-medium);
position: relative;
}
</style>
```

View File

@@ -0,0 +1,163 @@
# Tree Item
[component-header:sl-tree-item]
```html preview
<sl-tree>
<sl-tree-item>
Item 1
<sl-tree-item>Item A</sl-tree-item>
<sl-tree-item>Item B</sl-tree-item>
<sl-tree-item>Item C</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item 2</sl-tree-item>
<sl-tree-item>Item 3</sl-tree-item>
</sl-tree>
```
<!-- prettier-ignore -->
```jsx react
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTree>
<SlTreeItem>
Item 1
<SlTreeItem>Item A</SlTreeItem>
<SlTreeItem>Item B</SlTreeItem>
<SlTreeItem>Item C</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item 2</SlTreeItem>
<SlTreeItem>Item 3</SlTreeItem>
</SlTree>
);
```
## Examples
### Nested tree items
A tree item can contain other tree items. This allows the node to be expanded or collapsed by the user.
```html preview
<sl-tree>
<sl-tree-item>
Item 1
<sl-tree-item>
Item A
<sl-tree-item>Item Z</sl-tree-item>
<sl-tree-item>Item Y</sl-tree-item>
<sl-tree-item>Item X</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item B</sl-tree-item>
<sl-tree-item>Item C</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item 2</sl-tree-item>
<sl-tree-item>Item 3</sl-tree-item>
</sl-tree>
```
<!-- prettier-ignore -->
```jsx react
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTree>
<SlTreeItem>
Item 1
<SlTreeItem>
Item A
<SlTreeItem>Item Z</SlTreeItem>
<SlTreeItem>Item Y</SlTreeItem>
<SlTreeItem>Item X</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item B</SlTreeItem>
<SlTreeItem>Item C</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item 2</SlTreeItem>
<SlTreeItem>Item 3</SlTreeItem>
</SlTree>
);
```
### Selected
Use the `selected` attribute to select a tree item initially.
```html preview
<sl-tree>
<sl-tree-item selected>
Item 1
<sl-tree-item>Item A</sl-tree-item>
<sl-tree-item>Item B</sl-tree-item>
<sl-tree-item>Item C</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item 2</sl-tree-item>
<sl-tree-item>Item 3</sl-tree-item>
</sl-tree>
```
<!-- prettier-ignore -->
```jsx react
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTree>
<SlTreeItem selected>
Item 1
<SlTreeItem>Item A</SlTreeItem>
<SlTreeItem>Item B</SlTreeItem>
<SlTreeItem>Item C</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item 2</SlTreeItem>
<SlTreeItem>Item 3</SlTreeItem>
</SlTree>
);
```
### Expanded
Use the `expanded` attribute to expand a tree item initially.
```html preview
<sl-tree>
<sl-tree-item expanded>
Item 1
<sl-tree-item expanded>
Item A
<sl-tree-item>Item Z</sl-tree-item>
<sl-tree-item>Item Y</sl-tree-item>
<sl-tree-item>Item X</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item B</sl-tree-item>
<sl-tree-item>Item C</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item 2</sl-tree-item>
<sl-tree-item>Item 3</sl-tree-item>
</sl-tree>
```
<!-- prettier-ignore -->
```jsx react
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTree>
<SlTreeItem expanded>
Item 1
<SlTreeItem expanded>
Item A
<SlTreeItem>Item Z</SlTreeItem>
<SlTreeItem>Item Y</SlTreeItem>
<SlTreeItem>Item X</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item B</SlTreeItem>
<SlTreeItem>Item C</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item 2</SlTreeItem>
<SlTreeItem>Item 3</SlTreeItem>
</SlTree>
);
```
[component-metadata:sl-tree-item]

464
docs/components/tree.md Normal file
View File

@@ -0,0 +1,464 @@
# Tree
[component-header:sl-tree]
```html preview
<sl-tree>
<sl-tree-item>
Deciduous
<sl-tree-item>Birch</sl-tree-item>
<sl-tree-item>
Maple
<sl-tree-item>Field maple</sl-tree-item>
<sl-tree-item>Red maple</sl-tree-item>
<sl-tree-item>Sugar maple</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Oak</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Coniferous
<sl-tree-item>Cedar</sl-tree-item>
<sl-tree-item>Pine</sl-tree-item>
<sl-tree-item>Spruce</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Non-trees
<sl-tree-item>Bamboo</sl-tree-item>
<sl-tree-item>Cactus</sl-tree-item>
<sl-tree-item>Fern</sl-tree-item>
</sl-tree-item>
</sl-tree>
```
<!-- prettier-ignore -->
```jsx react
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTree>
<SlTreeItem>
Deciduous
<SlTreeItem>Birch</SlTreeItem>
<SlTreeItem>
Maple
<SlTreeItem>Field maple</SlTreeItem>
<SlTreeItem>Red maple</SlTreeItem>
<SlTreeItem>Sugar maple</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Oak</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Coniferous
<SlTreeItem>Cedar</SlTreeItem>
<SlTreeItem>Pine</SlTreeItem>
<SlTreeItem>Spruce</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Non-trees
<SlTreeItem>Bamboo</SlTreeItem>
<SlTreeItem>Cactus</SlTreeItem>
<SlTreeItem>Fern</SlTreeItem>
</SlTreeItem>
</SlTree>
);
```
## Examples
### Selection Modes
The `selection` attribute lets you change the selection behavior of the tree.
- Use `single` to allow the selection of a single item (default).
- Use `multiple` to allow the selection of multiple items.
- Use `leaf` to only allow leaf nodes to be selected.
```html preview
<sl-select id="selection-mode" value="single" label="Selection">
<sl-option value="single">Single</sl-option>
<sl-option value="multiple">Multiple</sl-option>
<sl-option value="leaf">Leaf</sl-option>
</sl-select>
<br />
<sl-tree class="tree-selectable">
<sl-tree-item>
Item 1
<sl-tree-item>
Item A
<sl-tree-item>Item Z</sl-tree-item>
<sl-tree-item>Item Y</sl-tree-item>
<sl-tree-item>Item X</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item B</sl-tree-item>
<sl-tree-item>Item C</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item 2</sl-tree-item>
<sl-tree-item>Item 3</sl-tree-item>
</sl-tree>
<script>
const selectionMode = document.querySelector('#selection-mode');
const tree = document.querySelector('.tree-selectable');
selectionMode.addEventListener('sl-change', () => {
tree.querySelectorAll('sl-tree-item').forEach(item => (item.selected = false));
tree.selection = selectionMode.value;
});
</script>
```
<!-- prettier-ignore -->
```jsx react
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [selection, setSelection] = useState('single');
return (
<>
<SlSelect label="Selection" value={selection} onSlChange={event => setSelection(event.target.value)}>
<SlMenuItem value="single">single</SlMenuItem>
<SlMenuItem value="multiple">multiple</SlMenuItem>
<SlMenuItem value="leaf">leaf</SlMenuItem>
</SlSelect>
<br />
<SlTree selection={selection}>
<SlTreeItem>
Item 1
<SlTreeItem>
Item A
<SlTreeItem>Item Z</SlTreeItem>
<SlTreeItem>Item Y</SlTreeItem>
<SlTreeItem>Item X</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item B</SlTreeItem>
<SlTreeItem>Item C</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item 2</SlTreeItem>
<SlTreeItem>Item 3</SlTreeItem>
</SlTree>
</>
);
};
```
### Showing Indent Guides
Indent guides can be drawn by setting `--indent-guide-width`. You can also change the color, offset, and style, using `--indent-guide-color`, `--indent-guide-style`, and `--indent-guide-offset`, respectively.
```html preview
<sl-tree class="tree-with-lines">
<sl-tree-item expanded>
Deciduous
<sl-tree-item>Birch</sl-tree-item>
<sl-tree-item expanded>
Maple
<sl-tree-item>Field maple</sl-tree-item>
<sl-tree-item>Red maple</sl-tree-item>
<sl-tree-item>Sugar maple</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Oak</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Coniferous
<sl-tree-item>Cedar</sl-tree-item>
<sl-tree-item>Pine</sl-tree-item>
<sl-tree-item>Spruce</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Non-trees
<sl-tree-item>Bamboo</sl-tree-item>
<sl-tree-item>Cactus</sl-tree-item>
<sl-tree-item>Fern</sl-tree-item>
</sl-tree-item>
</sl-tree>
<style>
.tree-with-lines {
--indent-guide-width: 1px;
}
</style>
```
<!-- prettier-ignore -->
```jsx react
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTree class="tree-with-lines" style={{ '--indent-guide-width': '1px' }}>
<SlTreeItem expanded>
Deciduous
<SlTreeItem>Birch</SlTreeItem>
<SlTreeItem expanded>
Maple
<SlTreeItem>Field maple</SlTreeItem>
<SlTreeItem>Red maple</SlTreeItem>
<SlTreeItem>Sugar maple</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Oak</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Coniferous
<SlTreeItem>Cedar</SlTreeItem>
<SlTreeItem>Pine</SlTreeItem>
<SlTreeItem>Spruce</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Non-trees
<SlTreeItem>Bamboo</SlTreeItem>
<SlTreeItem>Cactus</SlTreeItem>
<SlTreeItem>Fern</SlTreeItem>
</SlTreeItem>
</SlTree>
);
```
### Lazy Loading
Use the `lazy` attribute on a tree item to indicate that the content is not yet present and will be loaded later. When the user tries to expand the node, the `loading` state is set to `true` and the `sl-lazy-load` event will be emitted to allow you to load data asynchronously. The item will remain in a loading state until its content is changed.
If you want to disable this behavior after the first load, simply remove the `lazy` attribute and, on the next expand, the existing content will be shown instead.
```html preview
<sl-tree>
<sl-tree-item lazy>Available Trees</sl-tree-item>
</sl-tree>
<script type="module">
const lazyItem = document.querySelector('sl-tree-item[lazy]');
lazyItem.addEventListener('sl-lazy-load', () => {
// Simulate asynchronous loading
setTimeout(() => {
const subItems = ['Birch', 'Cedar', 'Maple', 'Pine'];
for (const item of subItems) {
const treeItem = document.createElement('sl-tree-item');
treeItem.innerText = item;
lazyItem.append(treeItem);
}
// Disable lazy mode once the content has been loaded
lazyItem.lazy = false;
}, 1000);
});
</script>
```
```jsx react
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [childItems, setChildItems] = useState([]);
const [lazy, setLazy] = useState(true);
const handleLazyLoad = () => {
// Simulate asynchronous loading
setTimeout(() => {
setChildItems(['Birch', 'Cedar', 'Maple', 'Pine']);
// Disable lazy mode once the content has been loaded
setLazy(false);
}, 1000);
};
return (
<SlTree>
<SlTreeItem lazy={lazy} onSlLazyLoad={handleLazyLoad}>
Available Trees
{childItems.map(item => (
<SlTreeItem>{item}</SlTreeItem>
))}
</SlTreeItem>
</SlTree>
);
};
```
### Customizing the Expand and Collapse Icons
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 `expand-button` part as shown below.
```html preview
<sl-tree class="custom-icons">
<sl-icon name="plus-square" slot="expand-icon"></sl-icon>
<sl-icon name="dash-square" slot="collapse-icon"></sl-icon>
<sl-tree-item>
Deciduous
<sl-tree-item>Birch</sl-tree-item>
<sl-tree-item>
Maple
<sl-tree-item>Field maple</sl-tree-item>
<sl-tree-item>Red maple</sl-tree-item>
<sl-tree-item>Sugar maple</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Oak</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Coniferous
<sl-tree-item>Cedar</sl-tree-item>
<sl-tree-item>Pine</sl-tree-item>
<sl-tree-item>Spruce</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Non-trees
<sl-tree-item>Bamboo</sl-tree-item>
<sl-tree-item>Cactus</sl-tree-item>
<sl-tree-item>Fern</sl-tree-item>
</sl-tree-item>
</sl-tree>
<style>
.custom-icons sl-tree-item::part(expand-button) {
/* Disable the expand/collapse animation */
rotate: none;
}
</style>
```
<!-- prettier-ignore -->
```jsx react
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTree>
<SlIcon name="plus-square" slot="expand-icon"></SlIcon>
<SlIcon name="dash-square" slot="collapse-icon"></SlIcon>
<SlTreeItem>
Deciduous
<SlTreeItem>Birch</SlTreeItem>
<SlTreeItem>
Maple
<SlTreeItem>Field maple</SlTreeItem>
<SlTreeItem>Red maple</SlTreeItem>
<SlTreeItem>Sugar maple</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Oak</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Coniferous
<SlTreeItem>Cedar</SlTreeItem>
<SlTreeItem>Pine</SlTreeItem>
<SlTreeItem>Spruce</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Non-trees
<SlTreeItem>Bamboo</SlTreeItem>
<SlTreeItem>Cactus</SlTreeItem>
<SlTreeItem>Fern</SlTreeItem>
</SlTreeItem>
</SlTree>
);
```
### With Icons
Decorative icons can be used before labels to provide hints for each node.
```html preview
<sl-tree class="tree-with-icons">
<sl-tree-item expanded>
<sl-icon name="folder"></sl-icon>
Documents
<sl-tree-item>
<sl-icon name="folder"> </sl-icon>
Photos
<sl-tree-item>
<sl-icon name="image"></sl-icon>
birds.jpg
</sl-tree-item>
<sl-tree-item>
<sl-icon name="image"></sl-icon>
kitten.jpg
</sl-tree-item>
<sl-tree-item>
<sl-icon name="image"></sl-icon>
puppy.jpg
</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
<sl-icon name="folder"></sl-icon>
Writing
<sl-tree-item>
<sl-icon name="file"></sl-icon>
draft.txt
</sl-tree-item>
<sl-tree-item>
<sl-icon name="file-pdf"></sl-icon>
final.pdf
</sl-tree-item>
<sl-tree-item>
<sl-icon name="file-bar-graph"></sl-icon>
sales.xls
</sl-tree-item>
</sl-tree-item>
</sl-tree-item>
</sl-tree>
```
```jsx react
import { SlIcon, SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => {
return (
<SlTree class="tree-with-icons">
<SlTreeItem expanded>
<SlIcon name="folder" />
Root
<SlTreeItem>
<SlIcon name="folder" />
Folder 1<SlTreeItem>
<SlIcon name="files" />
File 1 - 1
</SlTreeItem>
<SlTreeItem disabled>
<SlIcon name="files" />
File 1 - 2
</SlTreeItem>
<SlTreeItem>
<SlIcon name="files" />
File 1 - 3
</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
<SlIcon name="files" />
Folder 2<SlTreeItem>
<SlIcon name="files" />
File 2 - 1
</SlTreeItem>
<SlTreeItem>
<SlIcon name="files" />
File 2 - 2
</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
<SlIcon name="files" />
File 1
</SlTreeItem>
</SlTreeItem>
</SlTree>
);
};
```
[component-metadata:sl-tree]

View File

@@ -2,14 +2,12 @@
[component-header:sl-visually-hidden]
The visually hidden utility makes content accessible to assistive devices without displaying it on the screen.
According to [The A11Y Project](https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/), "there are real world situations where visually hiding content may be appropriate, while the content should remain available to assistive technologies, such as screen readers. For instance, hiding a search field's label as a common magnifying glass icon is used in its stead."
Since visually hidden content can receive focus when tabbing, the element will become visible when something inside receives focus. This behavior is intentional, as sighted keyboards user won't be able to determine where the focus indicator is without it.
```html preview
<div style="min-height: 100px;">
<div style="min-height: 1.875rem;">
<sl-visually-hidden>
<a href="#">Skip to main content</a>
</sl-visually-hidden>
@@ -32,7 +30,7 @@ In this example, the link will open a new window. Screen readers will announce "
### Content Conveyed By Context
Adding a title or label may seem redundant at times, but they're very helpful for unsighted users. Rather than omit them, you can provide context to unsighted users with visually hidden content.
Adding a label may seem redundant at times, but they're very helpful for unsighted users. Rather than omit them, you can provide context to unsighted users with visually hidden content that will be announced by assistive devices such as screen readers.
```html preview
<sl-card style="width: 100%; max-width: 360px;">

View File

@@ -28,14 +28,16 @@ setBasePath('https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dis
You'll need to tell Vue to ignore Shoelace components. This is pretty easy because they all start with `sl-`.
```js
import { createApp } from 'vue';
import Vue from 'vue';
import App from './App.vue';
const app = createApp(App);
Vue.config.ignoredElements = [/sl-/];
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('sl-');
const app = new Vue({
render: h => h(App)
});
app.mount('#app');
app.$mount('#app');
```
Now you can start using Shoelace components in your app!
@@ -56,31 +58,32 @@ One caveat is there's currently [no support for v-model on custom elements](http
```html
<!-- This doesn't work -->
<sl-input v-model="name">
<!-- This works, but it's a bit longer -->
<sl-input :value="name" @input="name = $event.target.value"></sl-input
></sl-input>
<sl-input v-model="name"></sl-input>
<!-- This works, but it's a bit longer -->
<sl-input :value="name" @input="name = $event.target.value"></sl-input>
```
If that's too verbose for your liking, you can use a custom directive instead. [This utility](https://www.npmjs.com/package/@shoelace-style/vue-sl-model) adds a custom directive that will work just like `v-model` but for Shoelace components. To install it, use this command.
```bash
npm install @shoelace-style/vue-sl-model
npm install @shoelace-style/vue-sl-model@1
```
Next, import the directive and enable it like this.
```js
import Vue from 'vue';
import ShoelaceModelDirective from '@shoelace-style/vue-sl-model';
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.use(ShoelaceModelDirective);
Vue.use(ShoelaceModelDirective);
Vue.config.ignoredElements = [/sl-/];
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('sl-');
const app = new Vue({
render: h => h(App)
});
app.mount('#app');
app.$mount('#app');
```
Now you can use the `v-sl-model` directive to keep your data in sync!
@@ -89,4 +92,4 @@ Now you can use the `v-sl-model` directive to keep your data in sync!
<sl-input v-sl-model="name"></sl-input>
```
?> Are you using Shoelace with Vue? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/vue.md)
?> Are you using Shoelace with Vue? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/vue-2.md)

View File

@@ -4,13 +4,9 @@ Every Shoelace component makes use of a [shadow DOM](https://developer.mozilla.o
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.
?> 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.
## 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
## Data 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.
@@ -36,36 +32,37 @@ const data = serialize(form);
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
## Constraint 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.
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`, `maxlength`, etc. Shoelace implements many of the same attributes as native form controls, but check the documentation for a list of supported properties for each component.
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.
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.
?> If this syntax looks unfamiliar, don't worry! Most of what you're learning on this page is platform knowledge that applies to regular form controls, too.
!> 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.
To make a field required, use the `required` attribute. Required fields will automatically receive a `*` after their labels. This is configurable through the `--sl-input-required-content` custom property.
The form will not be submitted if a required field is incomplete.
```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-option value="birds">Birds</sl-option>
<sl-option value="cats">Cats</sl-option>
<sl-option value="dogs">Dogs</sl-option>
<sl-option value="other">Other</sl-option>
</sl-select>
<br />
<sl-textarea name="comment" label="Comment" required></sl-textarea>
<br />
<sl-checkbox required>Check me before submitting</sl-checkbox>
<br /><br />
<sl-button type="reset" variant="default">Reset</sl-button>
<sl-button type="submit" variant="primary">Submit</sl-button>
</form>
@@ -119,8 +116,8 @@ To restrict a value to a specific [pattern](https://developer.mozilla.org/en-US/
<form class="input-validation-pattern">
<sl-input name="letters" required label="Letters" pattern="[A-Za-z]+"></sl-input>
<br />
<sl-button type="reset" variant="default">Reset</sl-button>
<sl-button type="submit" variant="primary">Submit</sl-button>
<sl-button type="reset" variant="default">Reset</sl-button>
</form>
<script type="module">
@@ -159,12 +156,12 @@ Some input types will automatically trigger constraints, such as `email` and `ur
```html preview
<form class="input-validation-type">
<sl-input variant="email" label="Email" placeholder="you@example.com" required></sl-input>
<sl-input type="email" label="Email" placeholder="you@example.com" required></sl-input>
<br />
<sl-input variant="url" label="URL" placeholder="https://example.com/" required></sl-input>
<sl-input type="url" label="URL" placeholder="https://example.com/" required></sl-input>
<br />
<sl-button type="reset" variant="default">Reset</sl-button>
<sl-button type="submit" variant="primary">Submit</sl-button>
<sl-button type="reset" variant="default">Reset</sl-button>
</form>
<script type="module">
@@ -187,9 +184,9 @@ const App = () => {
return (
<form onSubmit={handleSubmit}>
<SlInput variant="email" label="Email" placeholder="you@example.com" required />
<SlInput type="email" label="Email" placeholder="you@example.com" required />
<br />
<SlInput variant="url" label="URL" placeholder="https://example.com/" required />
<SlInput type="url" label="URL" placeholder="https://example.com/" required />
<br />
<SlButton type="submit" variant="primary">
Submit
@@ -199,16 +196,16 @@ const App = () => {
};
```
### Custom Validation
### Custom Error Messages
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>
<sl-input label="Type shoelace" required></sl-input>
<br />
<sl-button type="reset" variant="default">Reset</sl-button>
<sl-button type="submit" variant="primary">Submit</sl-button>
<sl-button type="reset" variant="default">Reset</sl-button>
</form>
<script type="module">
@@ -267,76 +264,109 @@ const App = () => {
?> 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
## 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.
Due to the many ways form controls are used, Shoelace doesn't provide out of the box validation styles for form controls as part of its default theme. Instead, the following attributes will be applied to reflect a control's validity as users interact with it. You can use them to create custom styles for any of the validation states you're interested in.
- `data-required` - the form control is required
- `data-optional` - the form control is optional
- `data-invalid` - the form control is currently invalid
- `data-valid` - the form control is currently valid
- `data-user-invalid` - the form control is currently invalid and the user has interacted with it
- `data-user-valid` - the form control is currently valid and the user has interacted with it
These attributes map to the browser's built-in pseudo classes for validation: [`:required`](https://developer.mozilla.org/en-US/docs/Web/CSS/:required), [`:optional`](https://developer.mozilla.org/en-US/docs/Web/CSS/:optional), [`:invalid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:invalid), [`:valid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:valid), and the proposed [`:user-invalid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:user-invalid) and [`:user-valid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:user-valid).
?> In the future, data attributes will be replaced with custom pseudo classes such as `:--valid` and `:--invalid`. Shoelace is using data attributes as a workaround until browsers support custom states through [`ElementInternals.states`](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/states).
### Styling Invalid Form Controls
You can target validity using any of the aforementioned data attributes, but it's usually preferable to target `data-user-invalid` and `data-user-valid` since they get applied only after a user interaction such as typing or submitting. This prevents empty form controls from appearing invalid immediately, which often results in a poor user experience.
This example demonstrates custom validation styles using `data-user-invalid` and `data-user-valid`. Try Typing in the fields to see how validity changes with user input.
```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>
<form class="validity-styles">
<sl-input
name="name"
label="Name"
help-text="What would you like people to call you?"
autocomplete="off"
required
></sl-input>
<style>
.custom-input[invalid]:not([disabled])::part(label),
.custom-input[invalid]:not([disabled])::part(help-text) {
color: var(--sl-color-danger-600);
}
<sl-select label="Favorite Animal" help-text="Select the best option." clearable required>
<sl-option value="birds">Birds</sl-option>
<sl-option value="cats">Cats</sl-option>
<sl-option value="dogs">Dogs</sl-option>
<sl-option value="other">Other</sl-option>
</sl-select>
.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>
<sl-button type="submit" variant="primary">Submit</sl-button>
<sl-button type="reset" variant="default">Reset</sl-button>
</form>
<script type="module">
const form = document.querySelector('.validity-styles');
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
</script>
<style>
sl-input.invalid {
...;
.validity-styles sl-input,
.validity-styles sl-select {
margin-bottom: var(--sl-spacing-medium);
}
/* user invalid styles */
.validity-styles sl-input[data-user-invalid]::part(base),
.validity-styles sl-select[data-user-invalid]::part(combobox) {
border-color: var(--sl-color-danger-600);
}
.validity-styles [data-user-invalid]::part(form-control-label),
.validity-styles [data-user-invalid]::part(form-control-help-text) {
color: var(--sl-color-danger-700);
}
.validity-styles sl-input:focus-within[data-user-invalid]::part(base),
.validity-styles sl-select:focus-within[data-user-invalid]::part(combobox) {
border-color: var(--sl-color-danger-600);
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-300);
}
/* User valid styles */
.validity-styles sl-input[data-user-valid]::part(base),
.validity-styles sl-select[data-user-valid]::part(combobox) {
border-color: var(--sl-color-success-600);
}
.validity-styles [data-user-valid]::part(form-control-label),
.validity-styles [data-user-valid]::part(form-control-help-text) {
color: var(--sl-color-success-700);
}
.validity-styles sl-input:focus-within[data-user-valid]::part(base),
.validity-styles sl-select:focus-within[data-user-valid]::part(combobox) {
border-color: var(--sl-color-success-600);
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-success-300);
}
</style>
```
## Getting Associated Form Controls
At this time, using [`HTMLFormElement.elements`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements) will not return Shoelace form controls because the browser is unaware of their status as custom element form controls. Fortunately, Shoelace provides an `elements()` function that does something very similar. However, instead of returning an [`HTMLFormControlsCollection`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormControlsCollection), it returns an array of HTML and Shoelace form controls in the order they appear in the DOM.
```js
import { getFormControls } from '@shoelace-style/shoelace/dist/utilities/form.js';
const form = document.querySelector('#my-form');
const formControls = getFormControls(form);
console.log(formControls); // e.g. [input, sl-input, ...]
```
?> You probably don't need this function! If you're gathering form data for submission, you probably want to use [Data Serialization](#data-serializing) instead.

View File

@@ -1,6 +1,6 @@
# Localization
Components can be localized by importing the appropriate translation file and setting the desired [`lang` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang) on the `<html>` element. Here's an example that renders Shoelace components in Spanish.
Components can be localized by importing the appropriate translation file and setting the desired [`lang` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang) and/or [`dir` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) on the `<html>` element. Here's an example that renders Shoelace components in Spanish.
```html
<html lang="es">
@@ -57,7 +57,7 @@ If you have any questions, please start a [discussion](https://github.com/shoela
## Multiple Locales Per Page
You can use a different locale for an individual component by setting its `lang` attribute. Here's a contrived example to demonstrate.
You can use a different locale for an individual component by setting its `lang` and/or `dir` attributes. Here's a contrived example to demonstrate.
```html
<html lang="es">
@@ -70,7 +70,7 @@ You can use a different locale for an individual component by setting its `lang`
</html>
```
For performance reasons, the `lang` attribute must be on the component itself, not on an ancestor element.
For performance reasons, the `lang` and `dir` attributes must be on the component itself, not on an ancestor element.
```html
<html lang="es">

View File

@@ -106,7 +106,7 @@ Designing, developing, and supporting this library requires a lot of time, effor
</sl-button>
<sl-button class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
<sl-icon slot="prefix" name="github"></sl-icon> <span class="github-star-count">Star</span>
<sl-icon slot="prefix" name="github"></sl-icon> Star
</sl-button>
<sl-button class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
@@ -117,7 +117,7 @@ Designing, developing, and supporting this library requires a lot of time, effor
Special thanks to the following projects and individuals that help make Shoelace possible.
- Components are built with [LitElement](https://lit-element.polymer-project.org/)
- Components are built with [Lit](https://lit.dev/)
- Component metadata is generated by the [Custom Elements Manifest Analyzer](https://github.com/open-wc/custom-elements-manifest)
- Documentation is powered by [Docsify](https://docsify.js.org/)
- CDN services are provided by [jsDelivr](https://www.jsdelivr.com/)

View File

@@ -4,7 +4,7 @@ Shoelace components are just regular HTML elements, or [custom elements](https:/
If you're new to custom elements, often referred to as "web components," this section will familiarize you with how to use them.
## Properties
## Attributes & Properties
Many components have properties that can be set using attributes. For example, buttons accept a `size` attribute that maps to the `size` property which dictates the button's size.
@@ -18,7 +18,7 @@ Some properties are boolean, so they only have true/false values. To activate a
<sl-button disabled>Click me</sl-button>
```
In rare cases, a property may require an array, an object, or a function. For example, to customize the color picker's list of preset swatches, you set the `swatches` property to an array of colors. This can be done with JavaScript.
In rare cases, a property may require an array, an object, or a function. For example, to customize the color picker's list of preset swatches, you set the `swatches` property to an array of colors. This must be done with JavaScript.
```html
<sl-color-picker></sl-color-picker>
@@ -149,15 +149,45 @@ A clever way to use this method is to hide the `<body>` with `opacity: 0` and ad
</script>
```
## Component Rendering and Updating
Shoelace components are built with [Lit](https://lit.dev/), a tiny library that makes authoring custom elements easier, more maintainable, and a lot of fun! As a Shoelace user, here is some helpful information about rendering and updating you should probably be aware of.
To optimize performance and reduce re-renders, Lit batches component updates. This means changing multiple attributes or properties at the same time will result in just a single re-render. In most cases, this isn't an issue, but there may be times you'll need to wait for the component to update before continuing.
Consider this example. We're going to change the `checked` property of the checkbox and observe its corresponding `checked` attribute, which happens to reflect.
```js
const checkbox = document.querySelector('sl-checkbox');
checkbox.checked = true;
console.log(checkbox.hasAttribute('checked')); // false
```
Most devs will expect this to be `true` instead of `false`, but the component hasn't had a chance to re-render yet so the attribute doesn't exist when `hasAttribute()` is called. Since changes are batched, we need to wait for the update before proceeding. This can be done using the `updateComplete` property, which is available on all Lit-based components.
```js
const checkbox = document.querySelector('sl-checkbox');
checkbox.checked = true;
checkbox.updateComplete.then(() => {
console.log(checkbox.hasAttribute('checked')); // true
});
```
This time we see an empty string, which means the boolean attribute is now present!
?> Avoid using `setTimeout()` or `requestAnimationFrame()` in situations like this. They might work, but it's far more reliable to use `updateComplete` instead.
## Code Completion
### VS Code
Shoelace ships with a file called `vscode.html-custom-data.json` that can be used to describe its components to Visual Studio Code. This enables code completion for Shoelace components (also known as "code hinting" or "IntelliSense"). To enable it, you need to tell VS Code where the file is.
Shoelace ships with a file called `vscode.html-custom-data.json` that can be used to describe it's custom elements to Visual Studio Code. This enables code completion for Shoelace components (also known as "code hinting" or "IntelliSense"). To enable it, you need to tell VS Code where the file is.
1. [Install Shoelace locally](/getting-started/installation#local-installation)
2. Create a folder called `.vscode` at the root of your project
3. Create a file inside the folder called `settings.json`
2. If it doesn't already exist, create a folder called `.vscode` at the root of your project
3. If it doesn't already exist, create a file inside that folder called `settings.json`
4. Add the following to the file
```js

View File

@@ -9,7 +9,7 @@
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 name="custom-elements-manifest" content="/dist/custom-elements.json" />
<meta property="og:title" content="Shoelace" />
<meta
property="og:description"

View File

@@ -4,7 +4,334 @@ Shoelace follows [Semantic Versioning](https://semver.org/). Breaking changes in
Components with the <sl-badge variant="warning" pill>Experimental</sl-badge> badge should not be used in production. They are made available as release candidates for development and testing purposes. As such, changes to experimental components will not be subject to semantic versioning.
_During the beta period, these restrictions may be relaxed in the event of a mission-critical bug._ 🐛
New versions of Shoelace are released as-needed and generally occur when a critical mass of changes have accumulated. At any time, you can see what's coming in the next release by visiting [next.shoelace.style](https://next.shoelace.style).
## 2.0.0
This is the first stable release of Shoelace 2, meaning breaking changes to the API will no longer be accepted for this version. Development of Shoelace 2.0 started in January 2020. The first beta was released on [July 15, 2020](https://github.com/shoelace-style/shoelace/releases/tag/v2.0.0-beta.1). Since then, Shoelace has grown quite a bit! Here are some stats from the project as of January 24, 2023:
- 55 components have been built
- [Over 2,500 commits](https://github.com/shoelace-style/shoelace/commits/next) have been made to the project
- [85 people](https://github.com/shoelace-style/shoelace/graphs/contributors) have contributed to the project
- [669 issues](https://github.com/shoelace-style/shoelace/issues?q=is%3Aissue+is%3Aclosed) have been filed on GitHub
- [274 pull requests](https://github.com/shoelace-style/shoelace/pulls) have been opened
- [More than 150 discussions](https://github.com/shoelace-style/shoelace/discussions) have been started on GitHub
- [Over 500 people](https://discord.com/invite/mg8f26C) have joined the Shoelace community on Discord
- [Over 300 million CDN hits](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) per month
- [Over 13,000 npm downloads](https://www.npmjs.com/package/@shoelace-style/shoelace) per week
- [73rd most popular project](https://www.jsdelivr.com/statistics) on jsDelivr
- [#2 product of the day](https://www.producthunt.com/products/shoelace-css) on Product Hunt (July 25, 2020)
I'd like to extend a very special thank you to every single contributor who worked to make this possible. Everyone who's filed a bug, submitted a PR, requested a feature, started a discussion, helped with testing, and advocated for the project. You are just as responsible for Shoelace's success as I am. I'd also like to thank the folks at [Font&nbsp;Awesome](https://fontawesome.com/) for recognizing Shoelace's potential and [believing in me](https://blog.fontawesome.com/shoelace-joins-font-awesome/) to make it happen.
Thank you! And keep building _awesome_ stuff!
Without further ado, here are the notes for this release.
- Added support for the `inert` attribute on `<sl-menu-item>` to allow hidden menu items to not accept focus [#1107](https://github.com/shoelace-style/shoelace/issues/1107)
- Added the `tag` part to `<sl-select>`
- Added `sl-hover` event to `<sl-rating>` [#1125](https://github.com/shoelace-style/shoelace/issues/1125)
- Added the `@documentation` tag with a link to the docs for each component
- Added the `form` attribute to all form controls to allow placing them outside of a `<form>` element [#1130](https://github.com/shoelace-style/shoelace/issues/1130)
- Added the `getFormControls()` function as an alternative to `HTMLFormElement.elements`
- Added missing docs for the `header-actions` slot in `<sl-dialog>` and `<sl-drawer>`
- Added `hue-slider-handle` and `opacity-slider-handle` parts to `<sl-color-picker>` and correct other part names in the docs [#1142](https://github.com/shoelace-style/shoelace/issues/1142)
- Fixed a bug in `<sl-select>` that prevented placeholders from showing when `multiple` was used [#1109](https://github.com/shoelace-style/shoelace/issues/1109)
- Fixed a bug in `<sl-select>` that caused tags to not be rounded when using the `pill` attribute [#1117](https://github.com/shoelace-style/shoelace/issues/1117)
- Fixed a bug in `<sl-select>` where the `sl-change` and `sl-input` events didn't weren't emitted when removing tags [#1119](https://github.com/shoelace-style/shoelace/issues/1119)
- Fixed a bug in `<sl-select>` that caused the listbox to scroll to the first selected item when selecting multiple items [#1138](https://github.com/shoelace-style/shoelace/issues/1138)
- Fixed a bug in `<sl-select>` where the input color and input hover color wasn't using the correct design tokens [#1143](https://github.com/shoelace-style/shoelace/issues/1143)
- Fixed a bug in `<sl-color-picker>` that logged a console error when parsing swatches with whitespace
- Fixed a bug in `<sl-color-picker>` that caused selected colors to be wrong due to incorrect HSV calculations
- Fixed a bug in `<sl-color-picker>` that prevented the initial value from being set correct when assigned as a property [#1141](https://github.com/shoelace-style/shoelace/issues/1141)
- Fixed a bug in `<sl-radio-button>` that caused the checked button's right border to be incorrect [#1110](https://github.com/shoelace-style/shoelace/issues/1110)
- Fixed a bug in `<sl-spinner>` that caused the animation to stop working correctly in Safari [#1121](https://github.com/shoelace-style/shoelace/issues/1121)
- Fixed a bug that prevented the entire `<sl-tab-panel>` to be hidden when inactive
- Fixed a bug that caused the value of `<sl-radio-group>` to be `undefined` depending on where the radio was activated [#1134](https://github.com/shoelace-style/shoelace/issues/1134)
- Fixed a bug that caused body content to shift when scroll locking was enabled [#1132](https://github.com/shoelace-style/shoelace/issues/1132)
- Fixed a bug in `<sl-icon>` that caused icons to sometimes be clipped in Safari
- Fixed a bug that prevented label colors from inheriting by default in `<sl-checkbox>`, `<sl-radio>`, and `<sl-switch>`
- Fixed a bug in `<sl-radio-group>` that caused an extra margin between the host element and the internal fieldset [#1139](https://github.com/shoelace-style/shoelace/issues/1139)
- Refactored the `ShoelaceFormControl` interface to remove the `invalid` property, allowing a more intuitive API for controlling validation internally
- Renamed the internal `FormSubmitController` to `FormControlController` to better reflect what it's used for
- Updated Lit to 2.6.1
- Updated Floating UI to 1.1.0
- Updated all other dependencies to latest versions
## 2.0.0-beta.88
This release includes a complete rewrite of `<sl-select>` to improve accessibility and simplify its internals.
- 🚨 BREAKING: rewrote `<sl-select>`
- Accessibility has been significantly improved, especially in screen readers
- You must use `<sl-option>` instead of `<sl-menu-item>` for options now
- The `suffix` slot was removed because it was confusing to users and its position made the clear button inaccessible
- The `max-tags-visible` attribute has been renamed to `max-options-visible`
- Many parts have been removed or renamed (please see the docs for more details)
- 🚨 BREAKING: removed the `sl-label-change` event from `<sl-menu-item>` (listen for `slotchange` instead)
- 🚨 BREAKING: removed type to select logic from `<sl-menu>` (this was added specifically for `<sl-select>` which no longer uses `<sl-menu>`)
- 🚨 BREAKING: swatches in `<sl-color-picker>` are no longer present by default (but you can set them using the `swatches` attribute now)
- 🚨 BREAKING: improved the accessibility of `<sl-menu-item>` so checked items are announced as such
- Checkbox menu items must now have `type="checkbox"` before applying the `checked` attribute
- Checkbox menu items will now toggle their `checked` state on their own when selected
- Disabled menu items will now receive focus, but are still not selectable
- Added the `<sl-option>` component
- Added Traditional Chinese translation [#1086](https://github.com/shoelace-style/shoelace/pull/1086)
- Added support for `swatches` to be an attribute of `<sl-color-picker>` so swatches can be defined declaratively (it was previously a property; use a `;` to separate color values)
- Fixed a bug in `<sl-tree-item>` where the checked/indeterminate states could get out of sync when using the `multiple` option [#1076](https://github.com/shoelace-style/shoelace/issues/1076)
- Fixed a bug in `<sl-tree>` that caused `sl-selection-change` to emit before the DOM updated [#1096](https://github.com/shoelace-style/shoelace/issues/1096)
- Fixed a bug that prevented `<sl-switch>` from submitting a default value of `on` when no value was provided [#1103](https://github.com/shoelace-style/shoelace/discussions/1103)
- Fixed a bug in `<sl-textarea>` that caused the scrollbar to show sometimes when using `resize="auto"`
- Fixed a bug in `<sl-input>` and `<sl-textarea>` that caused its validation states to be out of sync in some cases [#1063](https://github.com/shoelace-style/shoelace/issues/1063)
- Reorganized all components to make class structures more consistent
- Updated some incorrect default values for design tokens in the docs [#1097](https://github.com/shoelace-style/shoelace/issues/1097)
- Updated non-public fields to use the `private` keyword (these were previously private only by convention, but now TypeScript will warn you)
- Updated the hover style of `<sl-menu-item>` to be consistent with `<sl-option>`
- Updated the status of `<sl-tree>` and `<sl-tree-item>` from experimental to stable
- Updated React wrappers to use the latest API from `@lit-labs/react` [#1090](https://github.com/shoelace-style/shoelace/pull/1090)
- Updated Bootstrap Icons to 1.10.3
## 2.0.0-beta.87
- 🚨 BREAKING: changed the default size of medium checkboxes, radios, and switches to 18px instead of 16px
- 🚨 BREAKING: renamed the `--sl-toggle-size` design token to `--sl-toggle-size-medium`
- Added the `--sl-toggle-size-small` and `--sl-toggle-size-large` design tokens
- Added the `size` attribute to `<sl-checkbox>`, `<sl-radio>`, and `<sl-switch>` [#1071](https://github.com/shoelace-style/shoelace/issues/1071)
- Added the `sl-input` event to `<sl-checkbox>`, `<sl-color-picker>`, `<sl-radio>`, `<sl-range>`, and `<sl-switch>`
- Added HSV format to `<sl-color-picker>` [#1072](https://github.com/shoelace-style/shoelace/pull/1072)
- Fixed a bug in `<sl-color-picker>` that sometimes prevented the color from updating when clicking or tapping on the controls
- Fixed a bug in `<sl-color-picker>` that prevented text from being entered in the color input
- Fixed a bug in `<sl-input>` that caused the `sl-change` event to be incorrectly emitted when the value was set programmatically [#917](https://github.com/shoelace-style/shoelace/issues/917)
- Fixed a bug in `<sl-input>` and `<sl-textarea>` that made it impossible to disable spell checking [#1061](https://github.com/shoelace-style/shoelace/issues/1061)
- Fixed non-modal behaviors in `<sl-drawer>` when using the `contained` attribute [#1051](https://github.com/shoelace-style/shoelace/issues/1051)
- Fixed a bug in `<sl-checkbox>` and `<sl-radio>` that caused the checked icons to not scale property when resized
- Fixed a bug that broke React imports [#1050](https://github.com/shoelace-style/shoelace/pull/1050)
- Refactored `<sl-color-picker>` to use `@ctrl/tinycolor` instead of `color` saving ~67KB [#1072](https://github.com/shoelace-style/shoelace/pull/1072)
- Removed the `formdata` event polyfill since it's now available in the last two versions of all major browsers
## 2.0.0-beta.86
- 🚨 BREAKING: changed the default value of `date` in `<sl-relative-time>` to the current date instead of the Unix epoch
- 🚨 BREAKING: removed the `handle-icon` part and slot from `<sl-image-comparer>` (use `handle` instead)
- 🚨 BREAKING: removed the `handle` slot from `<sl-split-panel>` (use the `divider` slot instead)
- 🚨 BREAKING: removed the `--box-shadow` custom property from `<sl-alert>` (apply a box shadow to `::part(base)` instead)
- 🚨 BREAKING: removed the `play-icon` and `pause-icon` parts (use the `play-icon` and `pause-icon` slots instead)
- Added `header-actions` slot to `<sl-dialog>` and `<sl-drawer>`
- Added the `expand-icon` and `collapse-icon` slots to `<sl-details>` and refactored the icon animation [#1046](https://github.com/shoelace-style/shoelace/discussions/1046)
- Added the `play-icon` and `pause-icon` slots to `<sl-animated-image>` so you can customize the default icons
- Converted `isTreeItem()` export to a static method of `<sl-tree-item>`
- Fixed a bug in `<sl-tree-item>` where `sl-selection-change` was emitted when the selection didn't change [#1030](https://github.com/shoelace-style/shoelace/pull/1030)
- Fixed a bug in `<sl-button-group>` that caused the border to render incorrectly when hovering over icons inside buttons [#1035](https://github.com/shoelace-style/shoelace/issues/1035)
- Fixed an incorrect default for `flip-fallback-strategy` in `<sl-popup>` that caused the fallback strategy to be `initial` instead of `best-fit`, which is inconsistent with Floating UI's default [#1036](https://github.com/shoelace-style/shoelace/issues/1036)
- Fixed a bug where browser validation tooltips would show up when hovering over form controls [#1037](https://github.com/shoelace-style/shoelace/issues/1037)
- Fixed a bug in `<sl-tab-group>` that sometimes caused the active tab indicator to not animate
- Fixed a bug in `<sl-tree-item>` that caused the expand/collapse icon slot to be out of sync when the node is open initially
- Fixed the mislabeled `handle-icon` slot in `<sl-image-comparer>` (it now points to the `<slot>`, not the slot's fallback content)
- Fixed the border radius in `<sl-dropdown>` so it matches with nested `<sl-menu>` elements
- Fixed a bug that caused all button values to appear in submitted form data even if they weren't the submitter
- Improved IntelliSense in VS Code, courtesy of [Burton's amazing CEM Analyzer plugin](https://github.com/break-stuff/cem-plugin-vs-code-custom-data-generator)
- Improved accessibility of `<sl-alert>` so the alert is announced and the close button has a label
- Improved accessibility of `<sl-progress-ring>` so slotted labels are announced along with visually hidden labels
- Refactored all styles and animations to use `translate`, `rotate`, and `scale` instead of `transform`
- Removed slot wrappers from many components, allowing better control over user-applied styles
- Removed unused aria attributes from `<sl-skeleton>`
- Replaced the `x` icon in the system icon library with `x-lg` to improve icon consistency
## 2.0.0-beta.85
- Fixed a bug in `<sl-dropdown>` that caused containing dialogs, drawers, etc. to close when pressing <kbd>Escape</kbd> while focused [#1024](https://github.com/shoelace-style/shoelace/issues/1024)
- Fixed a bug in `<sl-tree-item>` that allowed lazy nodes to be incorrectly selected [#1023](https://github.com/shoelace-style/shoelace/pull/1023)
- Fixed a typing bug in `<sl-tree-item>` [#1026](https://github.com/shoelace-style/shoelace/pull/1026)
- Updated Floating UI to 1.0.7 to fix a bug that prevented `hoist` from working correctly in `<sl-dropdown>` after a recent update [#1024](https://github.com/shoelace-style/shoelace/issues/1024)
## 2.0.0-beta.84
- 🚨 BREAKING: Removed the `fieldset` property from `<sl-radio-group>` (use CSS parts if you want to keep the border) [#965](https://github.com/shoelace-style/shoelace/issues/965)
- 🚨 BREAKING: Removed `base` and `label` parts from `<sl-radio-group>` (use `form-control` and `form-control__label` instead) [#965](https://github.com/shoelace-style/shoelace/issues/965)
- 🚨 BREAKING: Removed the `base` part from `<sl-icon>` (style the host element directly instead)
- 🚨 BREAKING: Removed the `invalid` attribute from form controls (use `[data-invalid]` to target it with CSS)
- Added validation states to all form controls to allow styling based on various validation states [#1011](https://github.com/shoelace-style/shoelace/issues/1011)
- `data-required` - indicates that a value is required
- `data-optional` - indicates that a value is NOT required
- `data-invalid` - indicates that the form control is invalid
- `data-valid` - indicates that the form control is valid
- `data-user-invalid` - indicates the form control is invalid and the user has interacted with it
- `data-user-valid` - indicates the form control is valid and the user has interacted with it
- Added npm exports [#1020](https://github.com/shoelace-style/shoelace/pull/1020)
- Added `checkValidity()` method to all form controls
- Added `reportValidity()` method to `<sl-range>`
- Added `button--checked` to `<sl-radio-button>` and `control--checked` to `<sl-radio>` to style just the checked state [#933](https://github.com/shoelace-style/shoelace/pull/933)
- Added tests for `<sl-menu>`, `<sl-menu-item>`, `<sl-menu-label>`, `<sl-rating>`, `<sl-relative-time>`, `<sl-skeleton>`, `<sl-tab-panel>` and `<sl-tag>` [#935](https://github.com/shoelace-style/shoelace/pull/935)
[#949](https://github.com/shoelace-style/shoelace/pull/949) [#951](https://github.com/shoelace-style/shoelace/pull/951) [#953](https://github.com/shoelace-style/shoelace/pull/953)
[#956](https://github.com/shoelace-style/shoelace/pull/956) [#984](https://github.com/shoelace-style/shoelace/pull/984) [#991](https://github.com/shoelace-style/shoelace/pull/991)
- Added translations for Hungarian, Turkish, English (United Kingdom) and German (Austria) [#982](https://github.com/shoelace-style/shoelace/pull/982) [#989](https://github.com/shoelace-style/shoelace/pull/989)
- Added `--indicator-transition-duration` custom property to `<sl-progress-ring>` [#986](https://github.com/shoelace-style/shoelace/issues/986)
- Added `--sl-input-required-content-color` custom property to all form controls [#948](https://github.com/shoelace-style/shoelace/pull/948)
- Added the ability to cancel `sl-show` and `sl-hide` events in `<sl-details>` [#993](https://github.com/shoelace-style/shoelace/issues/993)
- Added `focus()` and `blur()` methods to `<sl-radio-button>`
- Added `stepUp()` and `stepDown()` methods to `<sl-input>` and `<sl-range>` [#1013](https://github.com/shoelace-style/shoelace/pull/1013)
- Added `showPicker()` method to `<sl-input>` [#1013](https://github.com/shoelace-style/shoelace/pull/1013)
- Added the `handle-icon` part to `<sl-image-comparer>`
- Added `caret`, `check`, `grip-vertical`, `indeterminate`, and `radio` icons to the system library and removed `check-lg` [#985](https://github.com/shoelace-style/shoelace/issues/985)
- Added the `loading` attribute to `<sl-avatar>` to allow lazy loading of image avatars [#1006](https://github.com/shoelace-style/shoelace/pull/1006)
- Added `formenctype` attribute to `<sl-button>` [#1009](https://github.com/shoelace-style/shoelace/pull/1009)
- Added `exports` to `package.json` and removed the `main` and `module` properties [#1007](https://github.com/shoelace-style/shoelace/pull/1007)
- Fixed a bug in `<sl-card>` that prevented the border radius to apply correctly to the header [#934](https://github.com/shoelace-style/shoelace/pull/934)
- Fixed a bug in `<sl-button-group>` where the inner border disappeared on focus [#980](https://github.com/shoelace-style/shoelace/pull/980)
- Fixed a bug that caused prefix/suffix animations in `<sl-input>` to wobble [#996](https://github.com/shoelace-style/shoelace/issues/996)
- Fixed a bug in `<sl-icon>` that prevented color from being set on the host element [#999](https://github.com/shoelace-style/shoelace/issues/999)
- Fixed a bug in `<sl-dropdown>` where the `keydown` event erroneously propagated to ancestors when pressing <kbd>Escape</kbd> [#990](https://github.com/shoelace-style/shoelace/issues/990)
- Fixed a bug that prevented arrow keys from scrolling content within `<sl-dialog>` and `<sl-drawer>` [#925](https://github.com/shoelace-style/shoelace/issues/925)
- Fixed a bug that prevented <kbd>Escape</kbd> from closing `<sl-dialog>` and `<sl-drawer>` in some cases
- Fixed a bug that caused forms to submit unexpectedly when selecting certain characters [#988](https://github.com/shoelace-style/shoelace/pull/988)
- Fixed a bug in `<sl-radio-group>` that prevented the `invalid` property from correctly reflecting validity sometimes [#992](https://github.com/shoelace-style/shoelace/issues/992)
- Fixed a bug in `<sl-tree>` that prevented selections from working correctly on dynamically added tree items [#963](https://github.com/shoelace-style/shoelace/issues/963)
- Fixed module paths in `custom-elements.json` so they point to the dist file instead of the source file [#725](https://github.com/shoelace-style/shoelace/issues/725)
- Fixed an incorrect return value for `reportValidity()` in `<sl-color-picker>`
- Improved `<sl-badge>` to improve padding and render relative to the current font size
- Improved how many components display in forced-colors mode / Windows High Contrast mode
- Improved `<sl-color-picker>` so it's usable in forced-colors mode
- Improved `<sl-dialog>` and `<sl-drawer>` so the panel is more visible in forced-colors mode
- Improved `<sl-menu-item>` so selections are visible in forced-colors mode
- Improved `<sl-progress-bar>` so it's visible in forced-colors mode
- Improved `<sl-radio-button>` so checked states are visible in forced-colors mode
- Improved `<sl-range>` so the thumb, track, and tooltips are visible in forced-colors mode
- Improved `<sl-rating>` so icons are visible in forced-colors mode
- Improved `<sl-split-panel>` so the divider is visible in forced-colors mode
- Improved `<sl-tree-item>` so selected items are visible in forced-colors mode
- Improved `<sl-tab-group>` so tabs are cleaner and easier to understand in forced-colors mode
- Improved positioning of the menu in `<sl-select>` so you can customize the menu width [#1018](https://github.com/shoelace-style/shoelace/issues/1018)
- Moved all component descriptions to `@summary` to get them within documentation tools [#962](https://github.com/shoelace-style/shoelace/pull/962)
- Refactored form controls to use the `ShoelaceFormControl` interface to improve type safety and consistency
- Updated Lit to 2.4.1
- Updated `@shoelace-style/localize` t0 3.0.3 to support for extended language codes
- Updated Bootstrap Icons to 1.10.2
- Updated TypeScript to 4.8.4
- Updated esbuild to 0.15.14
- Updated all other dependencies to latest versions
## 2.0.0-beta.83
This release removes the `<sl-responsive-media>` component. When this component was introduced, support for [`aspect-ratio`](https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio)) wasn't great. These days, [the property is supported](https://caniuse.com/mdn-css_properties_aspect-ratio) by all of Shoelace's target browsers, making a dedicated component redundant.
- 🚨 BREAKING: Removed `<sl-responsive-media>` (use the well-supported `aspect-ratio` CSS property instead)
- 🚨 BREAKING: Changed the `toggle-password` attribute of `<sl-input>` to `password-toggle` for consistency
- Added an expand/collapse animation to `<sl-tree-item>`
- Added `sl-lazy-change` event to `<sl-tree-item>`
- Added `expand-button` part to `<sl-tree-item>` [#893](https://github.com/shoelace-style/shoelace/pull/893)
- Added `password-visible` attribute to `<sl-input>` [#913](https://github.com/shoelace-style/shoelace/issues/913)
- Fixed a bug in `<sl-popup>` that didn't account for the arrow's diagonal size
- Fixed a bug in `<sl-popup>` that caused arrow placement to be incorrect with RTL
- Fixed a bug in `<sl-progress-ring>` that caused the indeterminate animation to stop working in Safari [#891](https://github.com/shoelace-style/shoelace/issues/891)
- Fixed a bug in `<sl-range>` that caused it to overflow a container at 100% width [#905](https://github.com/shoelace-style/shoelace/issues/905)
- Fixed a bug in `<sl-tree-item>` that prevented custom expand/collapse icons from rendering
- Fixed a bug in `<sl-tree-item>` where the `expand-icon` and `collapse-icon` slots were reversed
- Fixed a bug in `<sl-tree-item>` that prevented the keyboard from working after lazy loading [#882](https://github.com/shoelace-style/shoelace/issues/882)
- Fixed a bug in `<sl-textarea>` that prevented the textarea from resizing automatically when setting the value programmatically [#912](https://github.com/shoelace-style/shoelace/discussions/912)
- Fixed a handful of paths to prevent TypeScript from getting upset [#886](https://github.com/shoelace-style/shoelace/issues/886)
- Fixed a bug in `<sl-radio-group>` where the `button-group__base` part was documented but not exposed [#909](https://github.com/shoelace-style/shoelace/discussions/909)
- Fixed a bug in `<sl-range>` that caused the active track color to render on the wrong side in RTL [#916](https://github.com/shoelace-style/shoelace/issues/916)
- Refactored the internal event emitter to be part of `ShoelaceElement` to reduce imports and improve DX
- Downgraded Floating UI from 1.0.1 to 1.0.0 due to new logic that makes positioning much slower for certain components [#915](https://github.com/shoelace-style/shoelace/issues/915)
- Upgraded the status of `<sl-animated-image>`, `<sl-popup>`, and `<sl-split-panel>` from experimental to stable
## 2.0.0-beta.82
- Added the `sync` and `arrow-placement` attributes to `<sl-popup>`
- Changed the `auto-size` attribute of the experimental `<sl-popup>` component so it accepts `horizontal`, `vertical`, and `both` instead of a boolean value
- Changed the `flip-fallback-placement` attribute of the experimental `<sl-popup>` component to `flip-fallback-placements`
- Changed the `flip-fallback-strategy` in the experimental `<sl-popup>` component to accept `best-fit` and `initial` instead of `bestFit` and `initialPlacement`
- Fixed a bug in `<sl-dropdown>` that caused the panel to resize horizontally when the trigger is clipped by the viewport [#860](https://github.com/shoelace-style/shoelace/issues/860)
- Fixed a bug in `<sl-tree>` where dynamically changing slotted items wouldn't update the tree properly
- Fixed a bug in `<sl-split-panel>` that caused the panel to stack when clicking on the divider in mobile versions of Chrome [#862](https://github.com/shoelace-style/shoelace/issues/862)
- Fixed a bug in `<sl-popup>` that prevented flip fallbacks from working as intended
- Fixed a bug that caused concurrent animations to work incorrectly when the durations were different [#867](https://github.com/shoelace-style/shoelace/issues/867)
- Fixed a bug in `<sl-color-picker>` that caused the trigger and color preview to ignore opacity on first render [#869](https://github.com/shoelace-style/shoelace/issues/869)
- Fixed a bug in `<sl-tree>` that prevented the keyboard from working when the component was nested in a shadow root [#871](https://github.com/shoelace-style/shoelace/issues/871)
- Fixed a bug in `<sl-tab-group>` that prevented the keyboard from working when the component was nested in a shadow root [#872](https://github.com/shoelace-style/shoelace/issues/872)
- Fixed a bug in `<sl-tab>` that allowed disabled tabs to erroneously receive focus
- Improved single selection in `<sl-tree>` so nodes expand and collapse and rece
ive selection when clicking on the label
- Renamed `expanded-icon` and `collapsed-icon` slots to `expand-icon` and `collapse-icon` in the experimental `<sl-tree>` and `<sl-tree-item>` components
- Improved RTL support for `<sl-image-comparer>`
- Refactored components to extend from `ShoelaceElement` to make `dir` and `lang` reactive properties in all components
## 2.0.0-beta.81
- 🚨 BREAKING: removed the `base` part from `<sl-menu>` and removed an unnecessary `<div>` that made styling more difficult
- Added the `anchor` property to `<sl-popup>` to support external anchors
- Added read-only custom properties `--auto-size-available-width` and `--auto-size-available-height` to `<sl-popup>` to improve support for overflowing popup content
- Added `label` to `<sl-rating>` to improve accessibility for screen readers
- Added the `base__popup` and `base__arrow` parts to `<sl-tooltip>` [#858](https://github.com/shoelace-style/shoelace/issues/858)
- Fixed a bug where auto-size wasn't being applied to `<sl-dropdown>` and `<sl-select>`
- Fixed a bug in `<sl-popup>` that caused auto-size to kick in before flip
- Fixed a bug in `<sl-popup>` that prevented the `arrow-padding` attribute from working as expected
- Fixed a bug in `<sl-tooltip>` that prevented the popup from appearing with the correct z-index [#854](https://github.com/shoelace-style/shoelace/issues/854)
- Improved accessibility of `<sl-rating>` so keyboard nav works better and screen readers announce it properly
- Improved accessibility of `<sl-spinner>` so screen readers no longer skip over it
- Removed a user agent sniffing notice that appeared in Chrome [#855](https://github.com/shoelace-style/shoelace/issues/855)
- Removed the default hover effect in `<sl-tree-items>` to make selections more obvious
- Updated Floating UI to 1.0.1
- Updated esbuild to 0.15.1
- Updated all other dependencies to latest versions
## 2.0.0-beta.80
This release breaks radio buttons, which is something that needed to happen to solve a longstanding accessibility issue where screen readers announced an incorrect number of radios, e.g. "1 of 1" instead of "1 of 3." Many attempts to solve this without breaking the existing API were made, but none worked across the board. The new implementation upgrades `<sl-radio-group>` to serve as the "form control" while `<sl-radio>` and `<sl-radio-button>` serve as options within the form control.
To upgrade to this version, you will need to rework your radio controls by moving `name` up to the radio group. And instead of setting `checked` to select a specific radio, you can set `value` on the radio group and the checked item will update automatically.
- 🚨 BREAKING: improved accessibility of `<sl-radio-group>`, `<sl-radio>`, and `<sl-radio-button>` so they announce properly in screen readers
- Added the `name` attribute to `<sl-radio-group>` and removed it from `<sl-radio>` and `<sl-radio-button>`
- Added the `value` attribute to `<sl-radio-group>` (use this to control which radio is checked)
- Added the `sl-change` event to `sl-radio-group`
- Added `setCustomValidity()` and `reportValidity()` to `<sl-radio-group>`
- Removed the `checked` attribute from `<sl-radio>` and `<sl-radio-button>` (use the radio group's `value` attribute instead)
- Removed the `sl-change` event from `<sl-radio>` and `<sl-radio-button>` (listen for it on the radio group instead)
- Removed the `invalid` attribute from `<sl-radio>` and `<sl-radio-button>`
- Removed `setCustomValidity()` and `reportValidity()` from `<sl-radio>` and `<sl-radio-button>` (now available on the radio group)
- Added the experimental `<sl-popup>` component
- Fixed a bug in `<sl-menu-item>` where labels weren't always aligned correctly
- Fixed a bug in `<sl-range>` that caused the tooltip to be positioned incorrectly when switching between LTR/RTL
- Refactored `<sl-dropdown>` to use `<sl-popup>`
- Refactored `<sl-tooltip>` to use `<sl-popup>` and added the `body` part
- Revert disabled focus behavior in `<sl-tab-group>`, `<sl-menu>`, and `<sl-tree>` to be consistent with native form controls and menus [#845](https://github.com/shoelace-style/shoelace/issues/845)
## 2.0.0-beta.79
- Added experimental `<sl-tree>` and `<sl-tree-item>` components [#823](https://github.com/shoelace-style/shoelace/pull/823)
- Added `--indicator-width` custom property to `<sl-progress-ring>` [#837](https://github.com/shoelace-style/shoelace/issues/837)
- Added Swedish translation [#838](https://github.com/shoelace-style/shoelace/pull/838)
- Added support for `step="any"` for `<sl-input type="number">` [#839](https://github.com/shoelace-style/shoelace/issues/839)
- Changed the type of component styles from `CSSResult` to `CSSResultGroup` [#828](https://github.com/shoelace-style/shoelace/issues/828)
- Fixed a bug in `<sl-color-picker>` where using <kbd>Left</kbd> and <kbd>Right</kbd> would select the wrong color
- Fixed a bug in `<sl-dropdown>` that caused the position to be incorrect on the first show when using `hoist` [#843](https://github.com/shoelace-style/shoelace/issues/843)
- Fixed a bug in `<sl-tab-group>` where the divider was on the wrong side when using `placement="end"`
- Fixed a bug in `<sl-tab-group>` that caused nested tab groups to scroll when using `placement="start|end"` [#815](https://github.com/shoelace-style/shoelace/issues/815)
- Fixed a bug in `<sl-tooltip>` that caused the target to be lost after a slot change [#831](https://github.com/shoelace-style/shoelace/pull/831)
- Fixed a bug in `<sl-tooltip>` that caused the position to be incorrect on the first show when using `hoist`
- Improved accessibility of `<sl-tab-group>`, `<sl-tab>`, and `<sl-tab-panel>` to announce better in screen readers and by allowing focus on disabled items
- Improved accessibility of `<sl-menu>` and `<sl-menu-item>` by allowing focus on disabled items
- Updated Lit to 2.2.8
- Update esbuild to 0.14.50
- Updated Bootstrap Icons to 1.9.1
- Updated Floating UI to 1.0.0
- Updated all other dependencies to latest versions
## 2.0.0-beta.78
- 🚨 BREAKING: Moved the `checked-icon` and `indeterminate-icon` parts from a wrapper `<span>` to the `<svg>` in `<sl-checkbox>` [#786](https://github.com/shoelace-style/shoelace/issues/786)
- 🚨 BREAKING: Moved the `checked-icon` part from a wrapper `<span>` to the `<svg>` in `<sl-radio>` [#786](https://github.com/shoelace-style/shoelace/issues/786)
- Added the `--track-active-offset` custom property to `<sl-range>` [#806](https://github.com/shoelace-style/shoelace/issues/806)
- Fixed a bug that caused `<sl-select>` to sometimes have two vertical scrollbars [#814](https://github.com/shoelace-style/shoelace/issues/814)
- Fixed a bug that caused a gray line to appear between radio buttons [#821](https://github.com/shoelace-style/shoelace/discussions/821)
- Fixed a bug that caused `<sl-animated-image>` to not render anything when using the `play` attribute initially [#824](https://github.com/shoelace-style/shoelace/issues/824)
- Removed `:focus-visible` shim now that the last two major versions of Safari support it
- Updated Bootstrap Icons to 1.9.0
- Updated esbuild to 0.14.49
- Updated Floating UI to 0.5.4
- Updated Lit to 2.2.7
- Updated all other dependencies to latest versions
## 2.0.0-beta.77
@@ -193,7 +520,7 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
- 🚨 BREAKING: the `unit` property of `<sl-format-bytes>` has changed to `byte | bit` instead of `bytes | bits`
- Added `display-label` part to `<sl-select>` [#650](https://github.com/shoelace-style/shoelace/issues/650)
- Added `--spacing` CSS custom property to `<sl-divider>` [#664](https://github.com/shoelace-style/shoelace/pull/664)
- Added `--spacing` custom property to `<sl-divider>` [#664](https://github.com/shoelace-style/shoelace/pull/664)
- Added `event.detail.source` to the `sl-request-close` event in `<sl-dialog>` and `<sl-drawer>`
- Fixed a bug that caused `<sl-progress-ring>` to render the wrong size when `--track-width` was increased [#656](https://github.com/shoelace-style/shoelace/issues/656)
- Fixed a bug that allowed `<sl-details>` to open and close when disabled using a screen reader [#658](https://github.com/shoelace-style/shoelace/issues/658)
@@ -293,7 +620,7 @@ Thank you for your help and patience with testing Shoelace. I promise, we're not
- Improved a11y of the scroll buttons in `<sl-tab-group>`
- Improved a11y of the close button in `<sl-tab>`
- Improved a11y of `<sl-tab-panel>` by removing an invalid `aria-selected` attribute [#579](https://github.com/shoelace-style/shoelace/issues/579)
- Improved a11y of `<sl-icon>` by not using a variation of the `name` attribute for labels (use the `label` prop instead)
- Improved a11y of `<sl-icon>` by not using a variation of the `name` attribute for labels (use the `label` attribute instead)
- Moved `role` from the shadow root to the host element in `<sl-menu>`
- Removed redundant `role="menu"` in `<sl-dropdown>`
- Slightly faster animations for showing and hiding `<sl-dropdown>`
@@ -355,7 +682,7 @@ This release is the second attempt at unbundling dependencies. This will be a br
Shoelace doesn't have a lot of dependencies, but this release unbundles most of them so you can potentially save some extra kilobytes. This will be a breaking change only if your configuration _does not_ support bare module specifiers. CDN users and bundler users will be unaffected.
- 🚨 BREAKING: renamed the `sl-clear` event to `sl-remove`, the `clear-button` part to `remove-button`, and the `clearable` property to `removable` in `<sl-tag>`
- Added the `disabled` prop to `<sl-resize-observer>`
- Added the `disabled` attribute to `<sl-resize-observer>`
- Fixed a bug in `<sl-mutation-observer>` where setting `disabled` initially didn't work
- Unbundled dependencies and configured external imports to be packaged with bare module specifiers

View File

@@ -19,6 +19,12 @@ A common misconception about contributing to an open source project is that you
Please take a moment to review these guidelines to make the contribution process as easy as possible for both yourself and the project's maintainers.
## AI-generated Code
As an open source maintainer, I respectfully ask that you refrain from using AI-generated code when contributing to this project. This includes code generated by tools such as GitHub Copilot, even if you make alterations to it afterwards. While some of Copilot's features are indeed convenient, the ethics surrounding which codebases the AI has been trained on and their corresponding software licenses remain very questionable and have yet to be tested in a legal context.
I realize that one cannot reasonably enforce this any more than one can enforce not copying licensed code from other codebases, nor do I wish to expend energy policing contributors. I would, however, like to avoid all ethical and legal challenges that result from using AI-generated code. As such, I respectfully ask that you refrain from using such tools when contributing to this project. At this time, I will not knowingly accept any code that has been generated in such a manner.
## Using the Issue Tracker
The [issue tracker](https://github.com/shoelace-style/shoelace/issues) is for bug reports, feature requests, and pull requests.
@@ -182,8 +188,6 @@ Please do not make any changes to `prettier.config.cjs` without consulting the m
Components should be composable, meaning you can easily reuse them with and within other components. This reduces the overall size of the library, expedites feature development, and maintains a consistent user experience.
The `<sl-select>` component, for example, makes use of the dropdown, input, menu, and menu item components. Because it's offloading most of its functionality and styles to lower-level components, the select component remains lightweight and its appearance is consistent with other form controls and menus.
### Component Structure
All components have a host element, which is a reference to the `<sl-*>` element itself. Make sure to always set the host element's `display` property to the appropriate value depending on your needs, as the default is `inline` per the custom element spec.
@@ -196,6 +200,23 @@ All components have a host element, which is a reference to the `<sl-*>` element
Aside from `display`, avoid setting styles on the host element when possible. The reason for this is that styles applied to the host element are not encapsulated. Instead, create a base element that wraps the component's internals and style that instead. This convention also makes it easier to use BEM in components, as the base element can serve as the "block" entity.
When authoring components, please try to follow the same structure and conventions found in other components. Classes, for example, generally follow this structure:
- Static properties/methods
- Private/public properties (that are _not_ reactive)
- `@query` decorators
- `@state` decorators
- `@property` decorators
- Lifecycle methods (`connectedCallback()`, `disconnectedCallback()`, `firstUpdated()`, etc.)
- Private methods
- `@watch` decorators
- Public methods
- The `render()` method
Please avoid using the `public` keyword for class fields. It's simply too verbose when combined with decorators, property names, and arguments. However, _please do_ add `private` in front of any property or method that is intended to be private.
?> This might seem like a lot, but it's fairly intuitive once you start working with the library. However, don't let this structure prevent you from submitting a PR. [Code can change](https://www.abeautifulsite.net/posts/code-can-change/) and nobody will chastise you for "getting it wrong." At the same time, encouraging consistency helps keep the library maintainable and easy for others to understand. (A lint rule that helps with things like this would be a very welcome PR!)
### Class Names
All components use a [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM), so styles are completely encapsulated from the rest of the document. As a result, class names used _inside_ a component won't conflict with class names _outside_ the component, so we're free to name them anything we want.
@@ -218,12 +239,34 @@ When a component relies on the presence of slotted content to do something, don'
See the source of card, dialog, or drawer for examples.
### Dynamic Slot Names and Expand/Collapse Icons
A pattern has been established in `<sl-details>` and `<sl-tree-item>` for expand/collapse icons that animate on open/close. In short, create two slots called `expand-icon` and `collapse-icon` and render them both in the DOM, using CSS to show/hide only one based on the current open state. Avoid conditionally rendering them. Also avoid using dynamic slot names, such as `<slot name=${open ? 'open' : 'closed'}>`, because Firefox will not animate them.
There should be a container element immediately surrounding both slots. The container should be animated with CSS by default and it should have a part so the user can override the animation or disable it. Please refer to the source and documentation for `<sl-details>` and/or `<sl-tree-item>` for details.
### Fallback Content in Slots
When providing fallback content inside of `<slot>` elements, avoid adding parts, e.g.:
```html
<slot name="icon">
<sl-icon part="close-icon"></sl-icon>
</slot>
```
This creates confusion because the part will be documented, but it won't work when the user slots in their own content. The recommended way to customize this example is for the user to slot in their own content and target its styles with CSS as needed.
### Custom Events
Components must only emit custom events, and all custom events must start with `sl-` as a namespace. For compatibility with frameworks that utilize DOM templates, custom events must have lowercase, kebab-style names. For example, use `sl-change` instead of `slChange`.
This convention avoids the problem of browsers lowercasing attributes, causing some frameworks to be unable to listen to them. This problem isn't specific to one framework, but [Vue's documentation](https://vuejs.org/v2/guide/components-custom-events.html#Event-Names) provides a good explanation of the problem.
### Change Events
When change events are emitted by Shoelace components, they should be named `sl-change` and they should only be emitted as a result of user input. Programmatic changes, such as setting `el.value = '…'` _should not_ result in a change event being emitted. This is consistent with how native form controls work.
### CSS Custom Properties
To expose custom properties as part of a component's API, scope them to the `:host` block.
@@ -248,6 +291,10 @@ export default class SlExample {
}
```
### Focusing on Disabled Items
When an item within a keyboard navigable set is disabled (e.g. tabs, trees, menu items, etc.), the disabled item _should not_ receive focus via keyboard, click, or tap. It should be skipped just like in operating system menus and in native HTML form controls. There is no exception to this. If a particular item requires focus for assistive devices to provide a good user experience, the item should not be disabled and, upon activation, it should inform the user why the respective action cannot be completed.
### When to use a property vs. a CSS custom property
When designing a component's API, standard properties are generally used to change the _behavior_ of a component, whereas CSS custom properties ("CSS variables") are used to change the _appearance_ of a component. Remember that properties can't respond to media queries, but CSS variables can.
@@ -266,7 +313,7 @@ This convention can be relaxed when the developer experience is greatly improved
### Naming CSS Parts
While CSS parts can be named [virtually anything](https://www.abeautifulsite.net/posts/valid-names-for-css-parts/), within Shoelace they must use the kebab-case convention and lowercase letters. Modifiers must be delimited by `--` like in BEM. This is useful for allowing users to target parts with various states, such as `my-part--focus`.
While CSS parts can be named [virtually anything](https://www.abeautifulsite.net/posts/valid-names-for-css-parts/), within Shoelace they must use the kebab-case convention and lowercase letters. Additionally, [a BEM-inspired naming convention](https://www.abeautifulsite.net/posts/css-parts-inspired-by-bem/) is used to distinguish parts, subparts, and states.
When composing elements, use `part` to export the host element and `exportparts` to export its parts.
@@ -282,6 +329,20 @@ render() {
This results in a consistent, easy to understand structure for parts. In this example, the `icon` part will target the host element and the `icon__base` part will target the icon's `base` part.
### Dependencies
TL;DR a component is a dependency if and only if it's rendered inside another component's shadow root.
Many Shoelace components use other Shoelace components internally. For example, `<sl-button>` uses both `<sl-icon>` and `<sl-spinner>` for its caret icon and loading state, respectively. Since these components appear in the button's shadow root, they are considered dependencies of Button. Since dependencies are automatically loaded, users only need to import the button and everything will work as expected.
Contrast this to `<sl-select>` and `<sl-option>`. At first, one might assume that Option is a dependency of Select. After all, you can't really use Select without slotting in at least one Option. However, Option _is not_ a dependency of Select! The reason is because no Option is rendered in the Select's shadow root. Since the options are provided by the user, it's up to them to import both components independently.
People often suggest that Shoelace should auto-load Select + Option, Menu + Menu Item, Breadcrumb + Breadcrumb Item, etc. Although some components are designed to work together, they're technically not dependencies so eagerly loading them may not be desirable. What if someone wants to roll their own component with a superset of features? They wouldn't be able to if Shoelace automatically imported it!
Similarly, in the case of `<sl-radio-group>` there was originally only `<sl-radio>`, but now you can use either `<sl-radio>` or `<sl-radio-button>` as child elements. Which component(s) should be auto-loaded dependencies in this case? Had Radio been a dependency of Radio Group, users that only wanted Radio Buttons would be forced to register both with no way to opt out and no way to provide their own customized version.
For non-dependencies, _the user_ should decide what gets registered, even if it comes with a minor inconvenience.
### Form Controls
Form controls should support submission and validation through the following conventions:
@@ -292,3 +353,13 @@ Form controls should support submission and validation through the following con
- All form controls must have an `invalid` property that reflects their validity
- All form controls should mirror their native validation attributes such as `required`, `pattern`, `minlength`, `maxlength`, etc. when possible
- All form controls must be tested to work with the standard `<form>` element
### System Icons
Avoid inlining SVG icons inside of templates. If a component requires an icon, make sure `<sl-icon>` is a dependency of the component and use the [system library](/components/icon#customizing-the-system-library):
```html
<sl-icon library="system" name="..."></sl-icon>
```
This will render the icons instantly whereas the default library will fetch them from a remote source. If an icon isn't available in the system library, you will need to add it to `library.system.ts`. Using the system library ensures that all icons load instantly and are customizable by users who wish to provide a custom resolver for the system library.

View File

@@ -2,11 +2,11 @@
Border radius tokens are used to give sharp edges a more subtle, rounded effect. They use rem units so they scale with the base font size. The pixel values displayed are based on a 16px font size.
| Token | Value | Example |
| ---------------------------- | -------------- | -------------------------------------------------------------------------------------------------------- |
| `--sl-border-radius-small` | 0.125rem (2px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-small);"></div> |
| `--sl-border-radius-medium` | 0.25rem (4px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-medium);"></div> |
| `--sl-border-radius-large` | 0.5rem (8px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-large);"></div> |
| `--sl-border-radius-x-large` | 1rem (16px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-x-large);"></div> |
| `--sl-border-radius-circle` | 50% | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-circle);"></div> |
| `--sl-border-radius-pill` | 9999px | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-pill); width: 6rem;"></div> |
| Token | Value | Example |
| ---------------------------- | --------------- | -------------------------------------------------------------------------------------------------------- |
| `--sl-border-radius-small` | 0.1875rem (3px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-small);"></div> |
| `--sl-border-radius-medium` | 0.25rem (4px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-medium);"></div> |
| `--sl-border-radius-large` | 0.5rem (8px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-large);"></div> |
| `--sl-border-radius-x-large` | 1rem (16px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-x-large);"></div> |
| `--sl-border-radius-circle` | 50% | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-circle);"></div> |
| `--sl-border-radius-pill` | 9999px | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-pill); width: 6rem;"></div> |

155
docs/tokens/more.md Normal file
View File

@@ -0,0 +1,155 @@
# More Design Tokens
All of the design tokens described herein are considered relatively stable. However, some changes might occur in future versions to address mission critical bugs or improvements. If such changes occur, they _will not_ be considered breaking changes and will be clearly documented in the [changelog](/resources/changelog).
Most design tokens are consistent across the light and dark theme. Those that vary will show both values.
?> Currently, the source of design tokens is considered to be [`light.css`](https://github.com/shoelace-style/shoelace/blob/next/src/themes/light.css). The dark theme, [dark.css](https://github.com/shoelace-style/shoelace/blob/next/src/themes/dark.css), mirrors all of the same tokens with dark mode-specific values where appropriate. Work is planned to move all design tokens to a single file, perhaps JSON or YAML, in the near future.
## Focus Rings
Focus ring tokens control the appearance of focus rings. Note that form inputs use `--sl-input-focus-ring-*` tokens instead.
| Token | Value |
| ------------------------ | ------------------------------------------------------------------------------------- |
| `--sl-focus-ring-color` | var(--sl-color-primary-600) (light theme)<br>var(--sl-color-primary-700) (dark theme) |
| `--sl-focus-ring-style` | solid |
| `--sl-focus-ring-width` | 3px |
| `--sl-focus-ring` | var(--sl-focus-ring-style) var(--sl-focus-ring-width) var(--sl-focus-ring-color) |
| `--sl-focus-ring-offset` | 1px |
## Buttons
Button tokens control the appearance of buttons. In addition, buttons also currently use some form input tokens such as `--sl-input-height-*` and `--sl-input-border-*`. More button tokens may be added in the future to make it easier to style them more independently.
| Token | Value |
| ------------------------------ | --------------------------- |
| `--sl-button-font-size-small` | var(--sl-font-size-x-small) |
| `--sl-button-font-size-medium` | var(--sl-font-size-small) |
| `--sl-button-font-size-large` | var(--sl-font-size-medium) |
## Form Inputs
Form input tokens control the appearance of form controls such as [input](/components/input), [select](/components/select), [textarea](/components/textarea), etc.
| Token | Value |
| --------------------------------------- | -------------------------------- |
| `--sl-input-height-small` | 1.875rem; (30px @ 16px base) |
| `--sl-input-height-medium` | 2.5rem; (40px @ 16px base) |
| `--sl-input-height-large` | 3.125rem; (50px @ 16px base) |
| `--sl-input-background-color` | var(--sl-color-neutral-0) |
| `--sl-input-background-color-hover` | var(--sl-input-background-color) |
| `--sl-input-background-color-focus` | var(--sl-input-background-color) |
| `--sl-input-background-color-disabled` | var(--sl-color-neutral-100) |
| `--sl-input-border-color` | var(--sl-color-neutral-300) |
| `--sl-input-border-color-hover` | var(--sl-color-neutral-400) |
| `--sl-input-border-color-focus` | var(--sl-color-primary-500) |
| `--sl-input-border-color-disabled` | var(--sl-color-neutral-300) |
| `--sl-input-border-width` | 1px |
| `--sl-input-required-content` | "\*" |
| `--sl-input-required-content-offset` | -2px |
| `--sl-input-required-content-color` | var(--sl-input-label-color) |
| `--sl-input-border-radius-small` | var(--sl-border-radius-medium) |
| `--sl-input-border-radius-medium` | var(--sl-border-radius-medium) |
| `--sl-input-border-radius-large` | var(--sl-border-radius-medium) |
| `--sl-input-font-family` | var(--sl-font-sans) |
| `--sl-input-font-weight` | var(--sl-font-weight-normal) |
| `--sl-input-font-size-small` | var(--sl-font-size-small) |
| `--sl-input-font-size-medium` | var(--sl-font-size-medium) |
| `--sl-input-font-size-large` | var(--sl-font-size-large) |
| `--sl-input-letter-spacing` | var(--sl-letter-spacing-normal) |
| `--sl-input-color` | var(--sl-color-neutral-700) |
| `--sl-input-color-hover` | var(--sl-color-neutral-700) |
| `--sl-input-color-focus` | var(--sl-color-neutral-700) |
| `--sl-input-color-disabled` | var(--sl-color-neutral-900) |
| `--sl-input-icon-color` | var(--sl-color-neutral-500) |
| `--sl-input-icon-color-hover` | var(--sl-color-neutral-600) |
| `--sl-input-icon-color-focus` | var(--sl-color-neutral-600) |
| `--sl-input-placeholder-color` | var(--sl-color-neutral-500) |
| `--sl-input-placeholder-color-disabled` | var(--sl-color-neutral-600) |
| `--sl-input-spacing-small` | var(--sl-spacing-small) |
| `--sl-input-spacing-medium` | var(--sl-spacing-medium) |
| `--sl-input-spacing-large` | var(--sl-spacing-large) |
| `--sl-input-focus-ring-color` | hsl(198.6 88.7% 48.4% / 40%) |
| `--sl-input-focus-ring-offset` | 0 |
## Filled Form Inputs
Filled form input tokens control the appearance of form controls using the `filled` variant.
| Token | Value |
| --------------------------------------------- | --------------------------- |
| `--sl-input-filled-background-color` | var(--sl-color-neutral-100) |
| `--sl-input-filled-background-color-hover` | var(--sl-color-neutral-100) |
| `--sl-input-filled-background-color-focus` | var(--sl-color-neutral-100) |
| `--sl-input-filled-background-color-disabled` | var(--sl-color-neutral-100) |
| `--sl-input-filled-color` | var(--sl-color-neutral-800) |
| `--sl-input-filled-color-hover` | var(--sl-color-neutral-800) |
| `--sl-input-filled-color-focus` | var(--sl-color-neutral-700) |
| `--sl-input-filled-color-disabled` | var(--sl-color-neutral-800) |
## Form Labels
Form label tokens control the appearance of labels in form controls.
| Token | Value |
| ----------------------------------- | -------------------------- |
| `--sl-input-label-font-size-small` | var(--sl-font-size-small) |
| `--sl-input-label-font-size-medium` | var(--sl-font-size-medium) |
| `--sl-input-label-font-size-large` | var(--sl-font-size-large) |
| `--sl-input-label-color` | inherit |
## Help Text
Help text tokens control the appearance of help text in form controls.
| Token | Value |
| --------------------------------------- | --------------------------- |
| `--sl-input-help-text-font-size-small` | var(--sl-font-size-x-small) |
| `--sl-input-help-text-font-size-medium` | var(--sl-font-size-small) |
| `--sl-input-help-text-font-size-large` | var(--sl-font-size-medium) |
| `--sl-input-help-text-color` | var(--sl-color-neutral-500) |
## Toggles
Toggle tokens control the appearance of toggles such as [checkbox](/components/checkbox), [radio](/components/radio), [switch](/components/switch), etc.
| Token | Value |
| ------------------------- | --------------------------- |
| `--sl-toggle-size-small` | 0.875rem (14px @ 16px base) |
| `--sl-toggle-size-medium` | 1.125rem (18px @ 16px base) |
| `--sl-toggle-size-large` | 1.375rem (22px @ 16px base) |
## Overlays
Overlay tokens control the appearance of overlays as used in [dialog](/components/dialog), [drawer](/components/drawer), etc.
| Token | Value |
| ------------------------------- | ------------------------- |
| `--sl-overlay-background-color` | hsl(240 3.8% 46.1% / 33%) |
## Panels
Panel tokens control the appearance of panels such as those used in [dialog](/components/dialog), [drawer](/components/drawer), [menu](/components/menu), etc.
| Token | Value |
| ----------------------------- | --------------------------- |
| `--sl-panel-background-color` | var(--sl-color-neutral-0) |
| `--sl-panel-border-color` | var(--sl-color-neutral-200) |
| `--sl-panel-border-width` | 1px |
## Tooltips
Tooltip tokens control the appearance of tooltips. This includes the [tooltip](/components/tooltip) component as well as other implementations, such [range tooltips](/components/range).
| Token | Value |
| ------------------------------- | ---------------------------------------------------- |
| `--sl-tooltip-border-radius` | var(--sl-border-radius-medium) |
| `--sl-tooltip-background-color` | var(--sl-color-neutral-800) |
| `--sl-tooltip-color` | var(--sl-color-neutral-0) |
| `--sl-tooltip-font-family` | var(--sl-font-sans) |
| `--sl-tooltip-font-weight` | var(--sl-font-weight-normal) |
| `--sl-tooltip-font-size` | var(--sl-font-size-small) |
| `--sl-tooltip-line-height` | var(--sl-line-height-dense) |
| `--sl-tooltip-padding` | var(--sl-spacing-2x-small) var(--sl-spacing-x-small) |
| `--sl-tooltip-arrow-size` | 6px |

View File

@@ -10,7 +10,7 @@ The default font stack is designed to be simple and highly available on as many
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `--sl-font-sans` | -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol' | <span style="font-family: var(--sl-font-sans)">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-font-serif` | Georgia, 'Times New Roman', serif | <span style="font-family: var(--sl-font-serif)">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-font-mono` | Menlo, Monaco, 'Courier New', monospace | <span style="font-family: var(--sl-font-mono)">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-font-mono` | SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace; | <span style="font-family: var(--sl-font-mono)">The quick brown fox jumped over the lazy dog.</span> |
## Font Size
@@ -41,18 +41,18 @@ Font sizes use `rem` units so they scale with the base font size. The pixel valu
| Token | Value | Example |
| ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| `--sl-letter-spacing-denser` | ? | <span style="letter-spacing: var(--sl-letter-spacing-denser);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-letter-spacing-denser` | -0.03em | <span style="letter-spacing: var(--sl-letter-spacing-denser);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-letter-spacing-dense` | -0.015em | <span style="letter-spacing: var(--sl-letter-spacing-dense);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-letter-spacing-normal` | normal | <span style="letter-spacing: var(--sl-letter-spacing-normal);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-letter-spacing-loose` | 0.075em | <span style="letter-spacing: var(--sl-letter-spacing-loose);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-letter-spacing-looser` | ? | <span style="letter-spacing: var(--sl-letter-spacing-looser);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-letter-spacing-looser` | 0.15em | <span style="letter-spacing: var(--sl-letter-spacing-looser);">The quick brown fox jumped over the lazy dog.</span> |
## Line Height
| Token | Value | Example |
| ------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--sl-line-height-denser` | ? | <div style="line-height: var(--sl-line-height-denser);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |
| `--sl-line-height-denser` | 1 | <div style="line-height: var(--sl-line-height-denser);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |
| `--sl-line-height-dense` | 1.4 | <div style="line-height: var(--sl-line-height-dense);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |
| `--sl-line-height-normal` | 1.8 | <div style="line-height: var(--sl-line-height-normal);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |
| `--sl-line-height-loose` | 2.2 | <div style="line-height: var(--sl-line-height-loose);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |
| `--sl-line-height-looser` | ? | <div style="line-height: var(--sl-line-height-looser);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |
| `--sl-line-height-looser` | 2.6 | <div style="line-height: var(--sl-line-height-looser);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |

View File

@@ -1,6 +1,6 @@
# Integrating with Laravel
This page explains how to integrate Shoelace with a [Laravel](https://laravel.com) app using a local Webpack bundle.
This page explains how to integrate Shoelace with a [Laravel 9](https://laravel.com) app using Vite. For additional details refer to the [Bundling Assets (Vite)](https://laravel.com/docs/9.x/vite) section in the official Laravel docs.
?> This is a community-maintained document. Please [ask the community](/resources/community) if you have questions about this integration. You can also [suggest improvements](https://github.com/shoelace-style/shoelace/blob/next/docs/tutorials/integrating-with-laravel.md) to make it better.
@@ -8,13 +8,12 @@ This page explains how to integrate Shoelace with a [Laravel](https://laravel.co
This integration has been tested with the following:
- Laravel >= 8
- Node >= 14.17
- Laravel Mix >= 6
- Laravel 9.1
- Vite 3.0
## Instructions
These instructions assume an out of the box [Laravel 8+ install](https://laravel.com/docs/8.x/installation) that uses [Laravel Mix](https://laravel.com/docs/8.x/mix) to compile assets.
These instructions assume a default [Laravel 9 install](https://laravel.com/docs/9.x/installation) that uses [Vite](https://vitejs.dev/) to bundle assets.
Be sure to run `npm install` to install the default Laravel front-end dependencies before installing Shoelace.
### Install the Shoelace package
@@ -25,7 +24,7 @@ npm install @shoelace-style/shoelace
### Import the Default Theme
Import Shoelace's default theme (stylesheet) in `/resources/css/app.css`:
Import the Shoelace default theme (stylesheet) in `/resources/css/app.css`:
```css
@import '/node_modules/@shoelace-style/shoelace/dist/themes/light.css';
@@ -33,14 +32,20 @@ Import Shoelace's default theme (stylesheet) in `/resources/css/app.css`:
### Import Your Shoelace Components
Import each Shoelace component you plan to use in `/resources/js/bootstrap.js`. Since [Laravel Mix](https://laravel.com/docs/8.x/mix) uses Webpack, use the full path to each component -- as outlined in the [Cherry Picking instructions](https://shoelace.style/getting-started/installation?id=cherry-picking). You can find the full import statement for a component in the _Importing_ section of the component's documentation (use the _Bundler_ import). Your imports should look similar to:
Import each Shoelace component you plan to use in `/resources/js/bootstrap.js`. Use the full path to each component (as outlined in the [Cherry Picking instructions](https://shoelace.style/getting-started/installation?id=cherry-picking)). You can find the full import statement for a component in the _Importing_ section of the component's documentation (use the _Bundler_ import). Your imports should look similar to:
```js
import '@shoelace-style/shoelace/dist/components/button/button.js';
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
import '@shoelace-style/shoelace/dist/components/dialog/dialog.js';
import '@shoelace-style/shoelace/dist/components/drawer/drawer.js';
import '@shoelace-style/shoelace/dist/components/menu/menu.js';
import '@shoelace-style/shoelace/dist/components/menu-item/menu-item.js';
```
### Copy the Shoelace Static Assets (icons, images, etc.) to a Public Folder
Since Vite has no way to copy arbitrary assets into your build (like webpack), you need to manually copy the Shoelace static assets to your project's public folder. Run this command from your project's root directory to copy the Shoelace static assets to the `./public/assets` folder:
```sh
cp -aR node_modules/@shoelace-style/shoelace/dist/assets/ ./public/assets
```
### Set the Base Path
@@ -52,66 +57,50 @@ import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path.j
setBasePath('/');
```
Here's an example `/resources/js/bootstrap.js` file, after importing and setting the base path and components.
Example `/resources/js/bootstrap.js` file:
```js
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path.js';
setBasePath('/assets');
setBasePath('/');
import '@shoelace-style/shoelace/dist/components/button/button.js';
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
import '@shoelace-style/shoelace/dist/components/dialog/dialog.js';
import '@shoelace-style/shoelace/dist/components/drawer/drawer.js';
import '@shoelace-style/shoelace/dist/components/menu/menu.js';
import '@shoelace-style/shoelace/dist/components/menu-item/menu-item.js';
```
### Configure Laravel Mix
### Verify Vite Entry Points
[Laravel Mix](https://laravel.com/docs/8.x/mix) is a wrapper around Webpack that simplifies configuration. Mix is used by default for compiling front-end assets in Laravel.
Modify `webpack.mix.js` to add Shoelace's assets to Webpack's build process:
Laravel pre-configures the Vite entry points in `vite.config.js` as `resources/css/app.css` and `resources/js/app.js`. If you use a different location for your CSS and/or Javascript entry point, update this configuration to accordingly.
```js
mix
.js('resources/js/app.js', 'public/js')
.postCss('resources/css/app.css', 'public/css', [])
.copy('node_modules/@shoelace-style/shoelace/dist/assets', 'public/assets');
```
Consider [extracting vendor libraries](https://laravel.com/docs/8.x/mix#vendor-extraction) to a separate file. This splits frequently updated vendor libraries (like Shoelace) from your front-end application code -- for better long-term caching.
Here's an example `webpack.mix.js` file that compiles and splits your JS into `app.js` and `vendor.js` files, and builds an optimized CSS bundle using PostCSS.
```js
mix
.js('resources/js/app.js', 'public/js')
.postCss('resources/css/app.css', 'public/css', [])
.copy('node_modules/@shoelace-style/shoelace/dist/assets', 'public/assets')
.extract(); // extracts libraries in node_modules to vendor.js
plugins: [
laravel({
input: ["resources/css/app.css", "resources/js/app.js"],
refresh: true,
}),
],
```
### Compile Front-End Assets
Run the [Laravel Mix](https://laravel.com/docs/8.x/mix) npm scripts to build your application's CSS and JavaScript code.
Run the Vite development server or production build.
```bash
## build a development bundle
## run the Vite development bundle
npm run dev
## build a production bundle
npm run prod
## build a production bundle (with versioning)
npm run build
```
### Include Front-End Assets in Your Layout File
Most full-stack Laravel applications use [layouts](https://laravel.com/docs/8.x/blade#building-layouts) to define the basic structure of a page.
After compiling your front-end assets (above), include them in your top-level layouts/templates. The following example uses the [Laravel asset helper](https://laravel.com/docs/8.x/helpers#method-asset) to generate a full URL.
Add the `@vite()` Blade directive to the `<head>` of your application's root template.
```html
<script defer src="{{ asset('js/manifest.js') }}"></script>
<script defer src="{{ asset('js/vendor.js') }}"></script>
<script defer src="{{ asset('/js/app.js') }}"></script>
<link href="{{ asset('css/app.css') }}" rel="stylesheet" />
<head>
... @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
```
Have fun using Shoelace components in your Laravel app!

View File

@@ -17,7 +17,7 @@ This integration has been tested with the following:
To get started using Shoelace with NextJS, the following packages must be installed.
```bash
yarn add @shoelace-style/shoelace @shoelace-style/shoelace copy-webpack-plugin next-compose-plugins next-transpile-modules
yarn add @shoelace-style/shoelace copy-webpack-plugin next-compose-plugins next-transpile-modules
```
### Enabling ESM

10671
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,26 @@
{
"name": "@shoelace-style/shoelace",
"description": "A forward-thinking library of web components.",
"version": "2.0.0-beta.77",
"version": "2.0.0",
"homepage": "https://github.com/shoelace-style/shoelace",
"author": "Cory LaViska",
"license": "MIT",
"main": "dist/shoelace.js",
"module": "dist/shoelace.js",
"customElements": "dist/custom-elements.json",
"web-types": "dist/web-types.json",
"type": "module",
"types": "dist/shoelace.d.ts",
"exports": {
".": {
"types": "./dist/shoelace.d.ts",
"import": "./dist/shoelace.js"
},
"./dist/themes/*": "./dist/themes/*",
"./dist/components/*": "./dist/components/*",
"./dist/utilities/*": "./dist/utilities/*",
"./dist/react": "./dist/react/index.js",
"./dist/react/*": "./dist/react/*",
"./dist/translations/*": "./dist/translations/*"
},
"files": [
"dist"
],
@@ -41,72 +51,75 @@
"lint:fix": "eslint src --max-warnings 0 --fix",
"ts-check": "tsc --noEmit --project ./tsconfig.json",
"create": "plop --plopfile scripts/plop/plopfile.js",
"test": "web-test-runner",
"test:component": "npm run test -- --group",
"test:watch": "web-test-runner --watch",
"test": "web-test-runner --group default",
"test:component": "web-test-runner -- --watch --group",
"test:watch": "web-test-runner --watch --group default",
"spellcheck": "cspell \"**/*.{js,ts,json,html,css,md}\" --no-progress",
"list-outdated-dependencies": "npm-check-updates --format repo --peer",
"update-dependencies": "npm-check-updates --peer -u && npm install && npm run lint:fix && npm run prettier && npm run verify"
"update-dependencies": "npm-check-updates --peer -u && npm install"
},
"engines": {
"node": ">=14.17.0"
},
"dependencies": {
"@floating-ui/dom": "^0.5.2",
"@lit-labs/react": "^1.0.4",
"@ctrl/tinycolor": "^3.5.0",
"@floating-ui/dom": "^1.1.0",
"@lit-labs/react": "^1.1.1",
"@shoelace-style/animations": "^1.1.0",
"@shoelace-style/localize": "^3.0.0",
"color": "4.2",
"lit": "^2.2.5",
"@shoelace-style/localize": "^3.0.4",
"lit": "^2.6.1",
"qr-creator": "^1.0.0"
},
"devDependencies": {
"@custom-elements-manifest/analyzer": "^0.6.1",
"@open-wc/testing": "^3.1.5",
"@types/color": "^3.0.3",
"@types/mocha": "^9.1.1",
"@types/react": "^18.0.9",
"@typescript-eslint/eslint-plugin": "^5.26.0",
"@typescript-eslint/parser": "^5.26.0",
"@web/dev-server-esbuild": "^0.3.0",
"@web/test-runner": "^0.13.28",
"@web/test-runner-commands": "^0.6.1",
"@web/test-runner-playwright": "^0.8.8",
"bootstrap-icons": "^1.8.3",
"browser-sync": "^2.27.10",
"chalk": "^5.0.1",
"@custom-elements-manifest/analyzer": "^0.6.8",
"@open-wc/testing": "^3.1.7",
"@types/mocha": "^10.0.1",
"@types/react": "^18.0.26",
"@typescript-eslint/eslint-plugin": "^5.48.1",
"@typescript-eslint/parser": "^5.48.1",
"@web/dev-server-esbuild": "^0.3.3",
"@web/test-runner": "^0.15.0",
"@web/test-runner-commands": "^0.6.5",
"@web/test-runner-playwright": "^0.9.0",
"bootstrap-icons": "^1.10.3",
"browser-sync": "^2.27.11",
"cem-plugin-vs-code-custom-data-generator": "^1.3.2",
"chalk": "^5.2.0",
"command-line-args": "^5.2.1",
"comment-parser": "^1.3.1",
"cspell": "^6.0.0",
"del": "^6.1.1",
"cspell": "^6.18.1",
"del": "^7.0.0",
"download": "^8.0.0",
"esbuild": "^0.14.40",
"eslint": "^8.16.0",
"esbuild": "^0.16.17",
"eslint": "^8.31.0",
"eslint-plugin-chai-expect": "^3.0.0",
"eslint-plugin-chai-friendly": "^0.7.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-lit": "^1.6.1",
"eslint-plugin-lit-a11y": "^2.2.0",
"eslint-plugin-markdown": "^2.2.1",
"eslint-plugin-wc": "^1.3.2",
"eslint-plugin-import": "^2.27.4",
"eslint-plugin-lit": "^1.8.2",
"eslint-plugin-lit-a11y": "^2.3.0",
"eslint-plugin-markdown": "^3.0.0",
"eslint-plugin-sort-imports-es6-autofix": "^0.6.0",
"eslint-plugin-wc": "^1.4.0",
"front-matter": "^4.0.2",
"get-port": "^6.1.2",
"globby": "^13.1.1",
"husky": "^8.0.1",
"jsonata": "^1.8.6",
"lint-staged": "^12.4.2",
"globby": "^13.1.3",
"husky": "^8.0.3",
"jsonata": "^2.0.1",
"lint-staged": "^13.1.0",
"lunr": "^2.3.9",
"npm-check-updates": "^13.0.3",
"npm-check-updates": "^16.6.2",
"open": "^8.4.0",
"pascal-case": "^3.1.2",
"plop": "^3.1.0",
"prettier": "^2.6.2",
"react": "^18.1.0",
"plop": "^3.1.1",
"prettier": "^2.8.2",
"react": "^18.2.0",
"recursive-copy": "^2.0.14",
"sinon": "^14.0.0",
"sinon": "^15.0.1",
"source-map": "^0.7.4",
"strip-css-comments": "^5.0.0",
"tslib": "^2.4.0",
"typescript": "4.7.2"
"tslib": "^2.4.1",
"typescript": "4.9.4",
"user-agent-data-types": "^0.3.0"
},
"lint-staged": {
"*.{ts,js}": [

View File

@@ -2,7 +2,7 @@ import browserSync from 'browser-sync';
import chalk from 'chalk';
import { execSync } from 'child_process';
import commandLineArgs from 'command-line-args';
import del from 'del';
import { deleteSync } from 'del';
import esbuild from 'esbuild';
import fs from 'fs';
import getPort, { portNumbers } from 'get-port';
@@ -20,7 +20,7 @@ const { bundle, copydir, dir, serve, types } = commandLineArgs([
const outdir = dir;
del.sync(outdir);
deleteSync(outdir);
fs.mkdirSync(outdir, { recursive: true });
(async () => {
@@ -28,7 +28,6 @@ fs.mkdirSync(outdir, { recursive: true });
execSync(`node scripts/make-metadata.js --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-search.js --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-react.js --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-vscode-data.js --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-web-types.js --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-themes.js --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-icons.js --outdir "${outdir}"`, { stdio: 'inherit' });
@@ -47,6 +46,9 @@ fs.mkdirSync(outdir, { recursive: true });
format: 'esm',
target: 'es2017',
entryPoints: [
//
// NOTE: Entry points must be mapped in package.json > exports, otherwise users won't be able to import them!
//
// The whole shebang
'./src/shoelace.ts',
// Components
@@ -87,7 +89,7 @@ fs.mkdirSync(outdir, { recursive: true });
// Copy the build output to an additional directory
if (copydir) {
del.sync(copydir);
deleteSync(copydir);
copy(outdir, copydir);
}
@@ -101,7 +103,7 @@ fs.mkdirSync(outdir, { recursive: true });
});
// Make sure docs/dist is empty since we're serving it virtually
del.sync('docs/dist');
deleteSync('docs/dist');
const browserSyncConfig = {
open: false,
@@ -118,36 +120,14 @@ fs.mkdirSync(outdir, { recursive: true });
routes: {
'/dist': './dist'
}
},
socket: {
socketIoClientConfig: {
// Configure socketIO to retry forever when disconnected to enable the auto-reattach timeout below to work
reconnectionAttempts: Infinity,
reconnectionDelay: 500,
reconnectionDelayMax: 500,
timeout: 1000
}
}
};
// Launch browser sync
bs.init(browserSyncConfig, () => {
// This init callback gets executed after the server has started
const socketIoConfig = browserSyncConfig.socket.socketIoClientConfig;
// Wait enough time for any open, detached clients to have a chance to reconnect. This will be used to determine
// if we reload an existing tab or open a new one.
const tabReattachDelay = socketIoConfig.reconnectionDelayMax * 2 + socketIoConfig.timeout;
setTimeout(() => {
const url = `http://localhost:${port}`;
console.log(chalk.cyan(`Launched the Shoelace dev server at ${url} 🥾\n`));
if (Object.keys(bs.sockets.sockets).length === 0) {
open(url);
} else {
bs.reload();
}
}, tabReattachDelay);
const url = `http://localhost:${port}`;
console.log(chalk.cyan(`Launched the Shoelace dev server at ${url} 🥾\n`));
open(url);
});
// Rebuild and reload when source files change

View File

@@ -4,7 +4,7 @@
import chalk from 'chalk';
import commandLineArgs from 'command-line-args';
import copy from 'recursive-copy';
import del from 'del';
import { deleteAsync } from 'del';
import download from 'download';
import fm from 'front-matter';
import { readFileSync, mkdirSync } from 'fs';
@@ -35,7 +35,7 @@ let numIcons = 0;
// Copy icons
console.log(`Copying icons and license`);
await del([iconDir]);
await deleteAsync([iconDir]);
mkdirSync(iconDir, { recursive: true });
await Promise.all([
copy(`${srcPath}/icons`, iconDir),

View File

@@ -2,8 +2,7 @@ import commandLineArgs from 'command-line-args';
import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import del from 'del';
import { pascalCase } from 'pascal-case';
import { deleteSync } from 'del';
import prettier from 'prettier';
import prettierConfig from '../prettier.config.cjs';
import { getAllComponents } from './shared.js';
@@ -13,7 +12,7 @@ const { outdir } = commandLineArgs({ name: 'outdir', type: String });
const reactDir = path.join('./src/react');
// Clear build directory
del.sync(reactDir);
deleteSync(reactDir);
fs.mkdirSync(reactDir, { recursive: true });
// Fetch component metadata
@@ -29,7 +28,7 @@ components.map(component => {
const tagWithoutPrefix = component.tagName.replace(/^sl-/, '');
const componentDir = path.join(reactDir, tagWithoutPrefix);
const componentFile = path.join(componentDir, 'index.ts');
const importPath = component.modulePath.replace(/^src\//, '').replace(/\.ts$/, '');
const importPath = component.path;
const events = (component.events || []).map(event => `${event.reactName}: '${event.name}'`).join(',\n');
fs.mkdirSync(componentDir, { recursive: true });
@@ -40,14 +39,14 @@ components.map(component => {
import { createComponent } from '@lit-labs/react';
import Component from '../../${importPath}';
export default createComponent(
React,
'${component.tagName}',
Component,
{
export default createComponent({
tagName: '${component.tagName}',
elementClass: Component,
react: React,
events: {
${events}
}
);
});
`,
Object.assign(prettierConfig, {
parser: 'babel-ts'

View File

@@ -15,7 +15,7 @@ console.log('Generating search index for documentation');
const headings = [];
const lines = markdown.split('\n');
lines.map(line => {
lines.forEach(line => {
if (line.startsWith('#')) {
const level = line.match(/^(#+)/)[0].length;
const content = line.replace(/^#+/, '');
@@ -37,16 +37,16 @@ console.log('Generating search index for documentation');
return '';
}
headers.map(header => {
headers.forEach(header => {
const tagName = header.match(/\[component-header:([a-z-]+)\]/)[1];
const component = getAllComponents(metadata).find(component => component.tagName === tagName);
if (component) {
const fields = ['members', 'cssProperties', 'cssParts', 'slots', 'events'];
fields.map(field => {
fields.forEach(field => {
if (component[field]) {
component[field].map(entry => {
component[field].forEach(entry => {
if (entry.name) members.push(entry.name);
if (entry.description) members.push(entry.description);
if (entry.attribute) members.push(entry.attribute);
@@ -70,13 +70,18 @@ console.log('Generating search index for documentation');
this.field('m', { boost: 2 }); // members (props, methods, events, etc.)
this.field('c'); // content
files.map((file, index) => {
files.forEach((file, index) => {
const relativePath = path.relative('./docs', file).replace(/\\/g, '/');
const relativePathNoExtension = relativePath.split('.').slice(0, -1).join('.');
const url = relativePath.replace(/\.md$/, '');
const filename = path.basename(file);
// Ignore certain directories and files
if (relativePath.startsWith('assets/') || relativePath.startsWith('dist/') || filename === '_sidebar.md') {
if (
relativePath.startsWith('assets/') ||
relativePath.startsWith('dist/') ||
filename === '_sidebar.md' ||
filename === '404.md'
) {
return false;
}
@@ -90,7 +95,11 @@ console.log('Generating search index for documentation');
.join(' ');
const members = getMembers(content);
this.add({ id: index, t: title, h: headings, m: members, c: content });
// Remove markdown code fields from search results. This seems to make search results a bit more accurate and
// reduces search.json from ~679 KB to ~455 KB.
const prunedContent = content.replace(/```(.*?)```/gs, '');
this.add({ id: index, t: title, h: headings, m: members, c: prunedContent });
map[index] = { title, url };
});

View File

@@ -1,58 +0,0 @@
//
// This script generates vscode.html-custom-data.json (for IntelliSense).
//
// You must generate dist/custom-elements.json before running this script.
//
import commandLineArgs from 'command-line-args';
import fs from 'fs';
import path from 'path';
import { getAllComponents } from './shared.js';
const { outdir } = commandLineArgs({ name: 'outdir', type: String });
const metadata = JSON.parse(fs.readFileSync(path.join(outdir, 'custom-elements.json'), 'utf8'));
console.log('Generating custom data for VS Code');
const components = getAllComponents(metadata);
const vscode = { tags: [] };
components.map(component => {
const name = component.tagName;
const attributes = component.attributes?.map(attr => {
const type = attr.type?.text;
let values = [];
if (type) {
type.split('|').map(val => {
val = val.trim();
// Only accept values that are strings and numbers
const isString = val.startsWith(`'`);
const isNumber = Number(val).toString() === val;
if (isString) {
// Remove quotes
val = val.replace(/^'/, '').replace(/'$/, '');
}
if (isNumber || isString) {
values.push({ name: val });
}
});
}
if (values.length === 0) {
values = undefined;
}
return {
name: attr.name,
description: attr.description,
values
};
});
vscode.tags.push({ name, attributes });
});
fs.writeFileSync(path.join(outdir, 'vscode.html-custom-data.json'), JSON.stringify(vscode, null, 2), 'utf8');

View File

@@ -1,12 +1,16 @@
import { LitElement, html } from 'lit';
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { emit } from '../../internal/event';
import ShoelaceElement from '../../internal/shoelace-element';
import { watch } from '../../internal/watch';
import { LocalizeController } from '../../utilities/localize';
import styles from './{{ tagWithoutPrefix tag }}.styles';
import type { CSSResultGroup } from 'lit';
/**
* @since 2.0
* @summary Short summary of the component's intended use.
* @documentation https://shoelace.style/components/{{ tagWithoutPrefix tag }}
* @status experimental
* @since 2.0
*
* @dependency sl-example
*
@@ -15,21 +19,23 @@ import styles from './{{ tagWithoutPrefix tag }}.styles';
* @slot - The default slot.
* @slot example - An example slot.
*
* @csspart base - The component's internal wrapper.
* @csspart base - The component's base wrapper.
*
* @cssproperty --example - An example CSS custom property.
*/
@customElement('{{ tag }}')
export default class {{ properCase tag }} extends LitElement {
static styles = styles;
export default class {{ properCase tag }} extends ShoelaceElement {
static styles: CSSResultGroup = styles;
/** An example property. */
@property() prop = 'example';
private readonly localize = new LocalizeController(this);
@watch('someProp')
/** An example attribute. */
@property() attr = 'example';
@watch('someProperty')
doSomething() {
// Example event
emit(this, 'sl-event-name');
this.emit('sl-event-name');
}
render() {

View File

@@ -1,3 +1,4 @@
/** Gets an array of components from a CEM object. */
export function getAllComponents(metadata) {
const allComponents = [];
@@ -5,10 +6,10 @@ export function getAllComponents(metadata) {
module.declarations?.map(declaration => {
if (declaration.customElement) {
const component = declaration;
const modulePath = module.path;
const path = module.path;
if (component) {
allComponents.push(Object.assign(component, { modulePath }));
allComponents.push(Object.assign(component, { path }));
}
}
});

View File

@@ -19,7 +19,6 @@ export default css`
border: solid var(--sl-panel-border-width) var(--sl-panel-border-color);
border-top-width: calc(var(--sl-panel-border-width) * 3);
border-radius: var(--sl-border-radius-medium);
box-shadow: var(--box-shadow);
font-family: var(--sl-font-sans);
font-size: var(--sl-font-size-small);
font-weight: var(--sl-font-weight-normal);
@@ -83,6 +82,7 @@ export default css`
.alert__message {
flex: 1 1 auto;
display: block;
padding: var(--sl-spacing-large);
overflow: hidden;
}
@@ -91,7 +91,7 @@ export default css`
flex: 0 0 auto;
display: flex;
align-items: center;
font-size: var(--sl-font-size-large);
font-size: var(--sl-font-size-medium);
padding-inline-end: var(--sl-spacing-medium);
}
`;

View File

@@ -5,21 +5,21 @@ import type SlAlert from './alert';
describe('<sl-alert>', () => {
it('should be visible with the open attribute', async () => {
const el = await fixture<SlAlert>(html` <sl-alert open>I am an alert</sl-alert> `);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part="base"]')!;
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
expect(base.hidden).to.be.false;
});
it('should not be visible without the open attribute', async () => {
const el = await fixture<SlAlert>(html` <sl-alert>I am an alert</sl-alert> `);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part="base"]')!;
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
expect(base.hidden).to.be.true;
});
it('should emit sl-show and sl-after-show when calling show()', async () => {
const el = await fixture<SlAlert>(html` <sl-alert>I am an alert</sl-alert> `);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part="base"]')!;
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const showHandler = sinon.spy();
const afterShowHandler = sinon.spy();
@@ -37,7 +37,7 @@ describe('<sl-alert>', () => {
it('should emit sl-hide and sl-after-hide when calling hide()', async () => {
const el = await fixture<SlAlert>(html` <sl-alert open>I am an alert</sl-alert> `);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part="base"]')!;
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const hideHandler = sinon.spy();
const afterHideHandler = sinon.spy();
@@ -55,7 +55,7 @@ describe('<sl-alert>', () => {
it('should emit sl-show and sl-after-show when setting open = true', async () => {
const el = await fixture<SlAlert>(html` <sl-alert>I am an alert</sl-alert> `);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part="base"]')!;
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const showHandler = sinon.spy();
const afterShowHandler = sinon.spy();
@@ -73,7 +73,7 @@ describe('<sl-alert>', () => {
it('should emit sl-hide and sl-after-hide when setting open = false', async () => {
const el = await fixture<SlAlert>(html` <sl-alert open>I am an alert</sl-alert> `);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part="base"]')!;
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const hideHandler = sinon.spy();
const afterHideHandler = sinon.spy();

View File

@@ -1,65 +1,71 @@
import { html, LitElement } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import '../../components/icon-button/icon-button';
import '../icon-button/icon-button';
import { animateTo, stopAnimations } from '../../internal/animate';
import { emit, waitForEvent } from '../../internal/event';
import { HasSlotController } from '../../internal/slot';
import { watch } from '../../internal/watch';
import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query } from 'lit/decorators.js';
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry';
import { HasSlotController } from '../../internal/slot';
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize';
import { waitForEvent } from '../../internal/event';
import { watch } from '../../internal/watch';
import ShoelaceElement from '../../internal/shoelace-element';
import styles from './alert.styles';
import type { CSSResultGroup } from 'lit';
const toastStack = Object.assign(document.createElement('div'), { className: 'sl-toast-stack' });
/**
* @since 2.0
* @summary Alerts are used to display important messages inline or as toast notifications.
* @documentation https://shoelace.style/components/alert
* @status stable
* @since 2.0
*
* @dependency sl-icon-button
*
* @slot - The alert's content.
* @slot icon - An icon to show in the alert.
* @slot - The alert's main content.
* @slot icon - An icon to show in the alert. Works best with `<sl-icon>`.
*
* @event sl-show - Emitted when the alert opens.
* @event sl-after-show - Emitted after the alert opens and all animations are complete.
* @event sl-hide - Emitted when the alert closes.
* @event sl-after-hide - Emitted after the alert closes and all animations are complete.
*
* @csspart base - The component's internal wrapper.
* @csspart icon - The container that wraps the alert icon.
* @csspart message - The alert message.
* @csspart close-button - The close button.
* @csspart close-button__base - The close button's `base` part.
*
* @cssproperty --box-shadow - The alert's box shadow.
* @csspart base - The component's base wrapper.
* @csspart icon - The container that wraps the optional icon.
* @csspart message - The container that wraps the alert's main content.
* @csspart close-button - The close button, an `<sl-icon-button>`.
* @csspart close-button__base - The close button's exported `base` part.
*
* @animation alert.show - The animation to use when showing the alert.
* @animation alert.hide - The animation to use when hiding the alert.
*/
@customElement('sl-alert')
export default class SlAlert extends LitElement {
static styles = styles;
export default class SlAlert extends ShoelaceElement {
static styles: CSSResultGroup = styles;
private autoHideTimeout: number;
private readonly hasSlotController = new HasSlotController(this, 'icon', 'suffix');
private readonly localize = new LocalizeController(this);
@query('[part="base"]') base: HTMLElement;
@query('[part~="base"]') base: HTMLElement;
/** Indicates whether or not the alert is open. You can use this in lieu of the show/hide methods. */
/**
* Indicates whether or not the alert is open. You can toggle this attribute to show and hide the alert, or you can
* use the `show()` and `hide()` methods and this attribute will reflect the alert's open state.
*/
@property({ type: Boolean, reflect: true }) open = false;
/** Makes the alert closable. */
/** Enables a close button that allows the user to dismiss the alert. */
@property({ type: Boolean, reflect: true }) closable = false;
/** The alert's variant. */
/** The alert's theme variant. */
@property({ reflect: true }) variant: 'primary' | 'success' | 'neutral' | 'warning' | 'danger' = 'primary';
/**
* The length of time, in milliseconds, the alert will show before closing itself. If the user interacts with
* the alert before it closes (e.g. moves the mouse over it), the timer will restart. Defaults to `Infinity`.
* the alert before it closes (e.g. moves the mouse over it), the timer will restart. Defaults to `Infinity`, meaning
* the alert will not close on its own.
*/
@property({ type: Number }) duration = Infinity;
@@ -67,6 +73,57 @@ export default class SlAlert extends LitElement {
this.base.hidden = !this.open;
}
private restartAutoHide() {
clearTimeout(this.autoHideTimeout);
if (this.open && this.duration < Infinity) {
this.autoHideTimeout = window.setTimeout(() => this.hide(), this.duration);
}
}
private handleCloseClick() {
this.hide();
}
private handleMouseMove() {
this.restartAutoHide();
}
@watch('open', { waitUntilFirstUpdate: true })
async handleOpenChange() {
if (this.open) {
// Show
this.emit('sl-show');
if (this.duration < Infinity) {
this.restartAutoHide();
}
await stopAnimations(this.base);
this.base.hidden = false;
const { keyframes, options } = getAnimation(this, 'alert.show', { dir: this.localize.dir() });
await animateTo(this.base, keyframes, options);
this.emit('sl-after-show');
} else {
// Hide
this.emit('sl-hide');
clearTimeout(this.autoHideTimeout);
await stopAnimations(this.base);
const { keyframes, options } = getAnimation(this, 'alert.hide', { dir: this.localize.dir() });
await animateTo(this.base, keyframes, options);
this.base.hidden = true;
this.emit('sl-after-hide');
}
}
@watch('duration')
handleDurationChange() {
this.restartAutoHide();
}
/** Shows the alert. */
async show() {
if (this.open) {
@@ -123,57 +180,6 @@ export default class SlAlert extends LitElement {
});
}
restartAutoHide() {
clearTimeout(this.autoHideTimeout);
if (this.open && this.duration < Infinity) {
this.autoHideTimeout = window.setTimeout(() => this.hide(), this.duration);
}
}
handleCloseClick() {
this.hide();
}
handleMouseMove() {
this.restartAutoHide();
}
@watch('open', { waitUntilFirstUpdate: true })
async handleOpenChange() {
if (this.open) {
// Show
emit(this, 'sl-show');
if (this.duration < Infinity) {
this.restartAutoHide();
}
await stopAnimations(this.base);
this.base.hidden = false;
const { keyframes, options } = getAnimation(this, 'alert.show', { dir: this.localize.dir() });
await animateTo(this.base, keyframes, options);
emit(this, 'sl-after-show');
} else {
// Hide
emit(this, 'sl-hide');
clearTimeout(this.autoHideTimeout);
await stopAnimations(this.base);
const { keyframes, options } = getAnimation(this, 'alert.hide', { dir: this.localize.dir() });
await animateTo(this.base, keyframes, options);
this.base.hidden = true;
emit(this, 'sl-after-hide');
}
}
@watch('duration')
handleDurationChange() {
this.restartAutoHide();
}
render() {
return html`
<div
@@ -190,18 +196,12 @@ export default class SlAlert extends LitElement {
'alert--danger': this.variant === 'danger'
})}
role="alert"
aria-live="assertive"
aria-atomic="true"
aria-hidden=${this.open ? 'false' : 'true'}
@mousemove=${this.handleMouseMove}
>
<span part="icon" class="alert__icon">
<slot name="icon"></slot>
</span>
<slot name="icon" part="icon" class="alert__icon"></slot>
<span part="message" class="alert__message">
<slot></slot>
</span>
<slot part="message" class="alert__message" aria-live="polite"></slot>
${this.closable
? html`
@@ -209,8 +209,9 @@ export default class SlAlert extends LitElement {
part="close-button"
exportparts="base:close-button__base"
class="alert__close-button"
name="x"
name="x-lg"
library="system"
label=${this.localize.term('close')}
@click=${this.handleCloseClick}
></sl-icon-button>
`
@@ -222,16 +223,16 @@ export default class SlAlert extends LitElement {
setDefaultAnimation('alert.show', {
keyframes: [
{ opacity: 0, transform: 'scale(0.8)' },
{ opacity: 1, transform: 'scale(1)' }
{ opacity: 0, scale: 0.8 },
{ opacity: 1, scale: 1 }
],
options: { duration: 250, easing: 'ease' }
});
setDefaultAnimation('alert.hide', {
keyframes: [
{ opacity: 1, transform: 'scale(1)' },
{ opacity: 0, transform: 'scale(0.8)' }
{ opacity: 1, scale: 1 },
{ opacity: 0, scale: 0.8 }
],
options: { duration: 250, easing: 'ease' }
});

View File

@@ -7,6 +7,7 @@ export default css`
:host {
--control-box-size: 3rem;
--icon-size: calc(var(--control-box-size) * 0.625);
display: inline-flex;
position: relative;
cursor: pointer;
@@ -43,10 +44,14 @@ export default css`
:host([play]:hover) .animated-image__control-box {
opacity: 1;
transform: scale(1);
}
:host([play]:not(:hover)) .animated-image__control-box {
opacity: 0;
}
:host([play]) slot[name='pause-icon'],
:host(:not([play])) slot[name='play-icon'] {
display: none;
}
`;

View File

@@ -1,49 +1,53 @@
import { html, LitElement } from 'lit';
import '../icon/icon';
import { customElement, property, query, state } from 'lit/decorators.js';
import '../../components/icon/icon';
import { emit } from '../../internal/event';
import { html } from 'lit';
import { watch } from '../../internal/watch';
import ShoelaceElement from '../../internal/shoelace-element';
import styles from './animated-image.styles';
import type { CSSResultGroup } from 'lit';
/**
* @summary A component for displaying animated GIFs and WEBPs that play and pause on interaction.
* @documentation https://shoelace.style/components/animated-image
* @status stable
* @since 2.0
* @status experimental
*
* @dependency sl-icon
*
* @event sl-load - Emitted when the image loads successfully.
* @event sl-error - Emitted when the image fails to load.
*
* @slot play-icon - Optional play icon to use instead of the default. Works best with `<sl-icon>`.
* @slot pause-icon - Optional pause icon to use instead of the default. Works best with `<sl-icon>`.
*
* @part - control-box - The container that surrounds the pause/play icons and provides their background.
* @part - play-icon - The icon to use for the play button.
* @part - pause-icon - The icon to use for the pause button.
*
* @cssproperty --control-box-size - The size of the icon box.
* @cssproperty --icon-size - The size of the play/pause icons.
*/
@customElement('sl-animated-image')
export default class SlAnimatedImage extends LitElement {
static styles = styles;
export default class SlAnimatedImage extends ShoelaceElement {
static styles: CSSResultGroup = styles;
@query('.animated-image__animated') animatedImage: HTMLImageElement;
@state() frozenFrame: string;
@state() isLoaded = false;
@query('.animated-image__animated') animatedImage: HTMLImageElement;
/** The image's src attribute. */
/** The path to the image to load. */
@property() src: string;
/** The image's alt attribute. */
/** A description of the image used by assistive devices. */
@property() alt: string;
/** When set, the image will animate. Otherwise, it will be paused. */
/** Plays the animation. When this attribute is remove, the animation will pause. */
@property({ type: Boolean, reflect: true }) play: boolean;
handleClick() {
private handleClick() {
this.play = !this.play;
}
handleLoad() {
private handleLoad() {
const canvas = document.createElement('canvas');
const { width, height } = this.animatedImage;
canvas.width = width;
@@ -52,16 +56,16 @@ export default class SlAnimatedImage extends LitElement {
this.frozenFrame = canvas.toDataURL('image/gif');
if (!this.isLoaded) {
emit(this, 'sl-load');
this.emit('sl-load');
this.isLoaded = true;
}
}
handleError() {
emit(this, 'sl-error');
private handleError() {
this.emit('sl-error');
}
@watch('play')
@watch('play', { waitUntilFirstUpdate: true })
handlePlayChange() {
// When the animation starts playing, reset the src so it plays from the beginning. Since the src is cached, this
// won't trigger another request.
@@ -101,9 +105,8 @@ export default class SlAnimatedImage extends LitElement {
/>
<div part="control-box" class="animated-image__control-box">
${this.play
? html`<sl-icon part="pause-icon" name="pause-fill" library="system"></sl-icon>`
: html`<sl-icon part="play-icon" name="play-fill" library="system"></sl-icon>`}
<slot name="play-icon"><sl-icon name="play-fill" library="system"></sl-icon></slot>
<slot name="pause-icon"><sl-icon name="pause-fill" library="system"></sl-icon></slot>
</div>
`
: ''}

View File

@@ -1,24 +1,27 @@
import { html, LitElement } from 'lit';
import { customElement, property, queryAsync } from 'lit/decorators.js';
import { emit } from '../../internal/event';
import { watch } from '../../internal/watch';
import styles from './animation.styles';
import { animations } from './animations';
import { customElement, property, queryAsync } from 'lit/decorators.js';
import { html } from 'lit';
import { watch } from '../../internal/watch';
import ShoelaceElement from '../../internal/shoelace-element';
import styles from './animation.styles';
import type { CSSResultGroup } from 'lit';
/**
* @since 2.0
* @summary 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).
* @documentation https://shoelace.style/components/animation
* @status stable
* @since 2.0
*
* @event sl-cancel - Emitted when the animation is canceled.
* @event sl-finish - Emitted when the animation finishes.
* @event sl-start - Emitted when the animation starts or restarts.
*
* @slot - The element to animate. If multiple elements are to be animated, wrap them in a single container or use
* multiple animation elements.
* @slot - The element to animate. Avoid slotting in more than one element, as subsequent ones will be ignored. To
* animate multiple elements, either wrap them in a single container or use multiple `<sl-animation>` elements.
*/
@customElement('sl-animation')
export default class SlAnimation extends LitElement {
static styles = styles;
export default class SlAnimation extends ShoelaceElement {
static styles: CSSResultGroup = styles;
private animation?: Animation;
private hasStarted = false;
@@ -29,15 +32,18 @@ export default class SlAnimation extends LitElement {
@property() name = 'none';
/**
* Plays the animation. When omitted, the animation will be paused. This prop will be automatically removed when the
* animation finishes or gets canceled.
* Plays the animation. When omitted, the animation will be paused. This attribute will be automatically removed when
* the animation finishes or gets canceled.
*/
@property({ type: Boolean, reflect: true }) play = false;
/** The number of milliseconds to delay the start of the animation. */
@property({ type: Number }) delay = 0;
/** Determines the direction of playback as well as the behavior when reaching the end of an iteration. */
/**
* Determines the direction of playback as well as the behavior when reaching the end of an iteration.
* [Learn more](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-direction)
*/
@property() direction: PlaybackDirection = 'normal';
/** The number of milliseconds each iteration of the animation takes to complete. */
@@ -94,68 +100,24 @@ export default class SlAnimation extends LitElement {
this.destroyAnimation();
}
@watch('name')
@watch('delay')
@watch('direction')
@watch('duration')
@watch('easing')
@watch('endDelay')
@watch('fill')
@watch('iterations')
@watch('iterationsStart')
@watch('keyframes')
handleAnimationChange() {
if (!this.hasUpdated) {
return;
}
this.createAnimation();
}
handleAnimationFinish() {
private handleAnimationFinish() {
this.play = false;
this.hasStarted = false;
emit(this, 'sl-finish');
this.emit('sl-finish');
}
handleAnimationCancel() {
private handleAnimationCancel() {
this.play = false;
this.hasStarted = false;
emit(this, 'sl-cancel');
this.emit('sl-cancel');
}
@watch('play')
handlePlayChange() {
if (this.animation) {
if (this.play && !this.hasStarted) {
this.hasStarted = true;
emit(this, 'sl-start');
}
if (this.play) {
this.animation.play();
} else {
this.animation.pause();
}
return true;
}
return false;
}
@watch('playbackRate')
handlePlaybackRateChange() {
if (this.animation) {
this.animation.playbackRate = this.playbackRate;
}
}
handleSlotChange() {
private handleSlotChange() {
this.destroyAnimation();
this.createAnimation();
}
async createAnimation() {
private async createAnimation() {
const easing = animations.easings[this.easing] ?? this.easing;
const keyframes = this.keyframes ?? (animations as unknown as Partial<Record<string, Keyframe[]>>)[this.name];
const slot = await this.defaultSlot;
@@ -182,7 +144,7 @@ export default class SlAnimation extends LitElement {
if (this.play) {
this.hasStarted = true;
emit(this, 'sl-start');
this.emit('sl-start');
} else {
this.animation.pause();
}
@@ -190,7 +152,7 @@ export default class SlAnimation extends LitElement {
return true;
}
destroyAnimation() {
private destroyAnimation() {
if (this.animation) {
this.animation.cancel();
this.animation.removeEventListener('cancel', this.handleAnimationCancel);
@@ -199,7 +161,53 @@ export default class SlAnimation extends LitElement {
}
}
/** Clears all KeyframeEffects caused by this animation and aborts its playback. */
@watch([
'name',
'delay',
'direction',
'duration',
'easing',
'endDelay',
'fill',
'iterations',
'iterationsStart',
'keyframes'
])
handleAnimationChange() {
if (!this.hasUpdated) {
return;
}
this.createAnimation();
}
@watch('play')
handlePlayChange() {
if (this.animation) {
if (this.play && !this.hasStarted) {
this.hasStarted = true;
this.emit('sl-start');
}
if (this.play) {
this.animation.play();
} else {
this.animation.pause();
}
return true;
}
return false;
}
@watch('playbackRate')
handlePlaybackRateChange() {
if (this.animation) {
this.animation.playbackRate = this.playbackRate;
}
}
/** Clears all keyframe effects caused by this animation and aborts its playback. */
cancel() {
this.animation?.cancel();
}

View File

@@ -2,6 +2,10 @@ import { expect, fixture, html, waitUntil } from '@open-wc/testing';
import sinon from 'sinon';
import type SlAvatar from './avatar';
// The default avatar background just misses AA contrast, but the next step up is way too dark. Since avatars aren't
// used to display text, we're going to relax this rule.
const ignoredRules = ['color-contrast'];
describe('<sl-avatar>', () => {
let el: SlAvatar;
@@ -11,11 +15,11 @@ describe('<sl-avatar>', () => {
});
it('should pass accessibility tests', async () => {
await expect(el).to.be.accessible();
await expect(el).to.be.accessible({ ignoredRules });
});
it('should default to circle styling', () => {
const part = el.shadowRoot!.querySelector('[part="base"]')!;
const part = el.shadowRoot!.querySelector('[part~="base"]')!;
expect(el.getAttribute('shape')).to.eq('circle');
expect(part.classList.value.trim()).to.eq('avatar avatar--circle');
});
@@ -36,17 +40,17 @@ describe('<sl-avatar>', () => {
* the image element to pass accessibility.
* https://html.spec.whatwg.org/multipage/images.html#ancillary-images
*/
await expect(el).to.be.accessible();
await expect(el).to.be.accessible({ ignoredRules });
});
it('renders "image" part, with src and a role of presentation', () => {
const part = el.shadowRoot!.querySelector('[part="image"]')!;
const part = el.shadowRoot!.querySelector('[part~="image"]')!;
expect(part.getAttribute('src')).to.eq(image);
});
it('renders the label attribute in the "base" part', () => {
const part = el.shadowRoot!.querySelector('[part="base"]')!;
const part = el.shadowRoot!.querySelector('[part~="base"]')!;
expect(part.getAttribute('aria-label')).to.eq(label);
});
@@ -59,11 +63,11 @@ describe('<sl-avatar>', () => {
});
it('should pass accessibility tests', async () => {
await expect(el).to.be.accessible();
await expect(el).to.be.accessible({ ignoredRules });
});
it('renders "initials" part, with initials as the text node', () => {
const part = el.shadowRoot!.querySelector<HTMLElement>('[part="initials"]')!;
const part = el.shadowRoot!.querySelector<HTMLElement>('[part~="initials"]')!;
expect(part.innerText).to.eq(initials);
});
@@ -76,11 +80,11 @@ describe('<sl-avatar>', () => {
});
it('should pass accessibility tests', async () => {
await expect(el).to.be.accessible();
await expect(el).to.be.accessible({ ignoredRules });
});
it('appends the appropriate class on the "base" part', () => {
const part = el.shadowRoot!.querySelector('[part="base"]')!;
const part = el.shadowRoot!.querySelector('[part~="base"]')!;
expect(el.getAttribute('shape')).to.eq(shape);
expect(part.classList.value.trim()).to.eq(`avatar avatar--${shape}`);
@@ -94,7 +98,7 @@ describe('<sl-avatar>', () => {
});
it('should pass accessibility tests', async () => {
await expect(el).to.be.accessible();
await expect(el).to.be.accessible({ ignoredRules });
});
it('should accept as an assigned child in the shadow root', () => {

View File

@@ -1,28 +1,32 @@
import { html, LitElement } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import '../icon/icon';
import { classMap } from 'lit/directives/class-map.js';
import '../../components/icon/icon';
import { customElement, property, state } from 'lit/decorators.js';
import { html } from 'lit';
import { watch } from '../../internal/watch';
import ShoelaceElement from '../../internal/shoelace-element';
import styles from './avatar.styles';
import type { CSSResultGroup } from 'lit';
/**
* @since 2.0
* @summary Avatars are used to represent a person or object.
* @documentation https://shoelace.style/components/avatar
* @status stable
* @since 2.0
*
* @dependency sl-icon
*
* @slot icon - The default icon to use when no image or initials are present.
* @slot icon - The default icon to use when no image or initials are present. Works best with `<sl-icon>`.
*
* @csspart base - The component's internal wrapper.
* @csspart icon - The container that wraps the avatar icon.
* @csspart initials - The container that wraps the avatar initials.
* @csspart image - The avatar image.
* @csspart base - The component's base wrapper.
* @csspart icon - The container that wraps the avatar's icon.
* @csspart initials - The container that wraps the avatar's initials.
* @csspart image - The avatar image. Only shown when the `image` attribute is set.
*
* @cssproperty --size - The size of the avatar.
*/
@customElement('sl-avatar')
export default class SlAvatar extends LitElement {
static styles = styles;
export default class SlAvatar extends ShoelaceElement {
static styles: CSSResultGroup = styles;
@state() private hasError = false;
@@ -35,6 +39,9 @@ export default class SlAvatar extends LitElement {
/** Initials to use as a fallback when no image is available (1-2 characters max recommended). */
@property() initials = '';
/** Indicates how the browser should load the image. */
@property() loading: 'eager' | 'lazy' = 'eager';
/** The shape of the avatar. */
@property({ reflect: true }) shape: 'circle' | 'square' | 'rounded' = 'circle';
@@ -60,11 +67,9 @@ export default class SlAvatar extends LitElement {
${this.initials
? html` <div part="initials" class="avatar__initials">${this.initials}</div> `
: html`
<div part="icon" class="avatar__icon" aria-hidden="true">
<slot name="icon">
<sl-icon name="person-fill" library="system"></sl-icon>
</slot>
</div>
<slot name="icon" part="icon" class="avatar__icon" aria-hidden="true">
<sl-icon name="person-fill" library="system"></sl-icon>
</slot>
`}
${this.image && !this.hasError
? html`
@@ -72,6 +77,7 @@ export default class SlAvatar extends LitElement {
part="image"
class="avatar__image"
src="${this.image}"
loading="${this.loading}"
alt=""
@error="${() => (this.hasError = true)}"
/>

View File

@@ -12,14 +12,14 @@ export default css`
display: inline-flex;
align-items: center;
justify-content: center;
font-size: var(--sl-font-size-x-small);
font-size: max(12px, 0.75em);
font-weight: var(--sl-font-weight-semibold);
letter-spacing: var(--sl-letter-spacing-normal);
line-height: 1;
border-radius: var(--sl-border-radius-small);
border: solid 1px var(--sl-color-neutral-0);
white-space: nowrap;
padding: 3px 6px;
padding: 0.35em 0.6em;
user-select: none;
cursor: inherit;
}

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