Compare commits
475 Commits
autoload
...
konnorroge
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e43e65b6c | ||
|
|
26d83f37d6 | ||
|
|
a751053803 | ||
|
|
44a7dd5089 | ||
|
|
902ab85f32 | ||
|
|
21647001a4 | ||
|
|
a0ee2256c4 | ||
|
|
51f5c30526 | ||
|
|
9e0aa9c2ec | ||
|
|
15a9c63040 | ||
|
|
30a3164a96 | ||
|
|
2a22fb683c | ||
|
|
325ddafb13 | ||
|
|
a1a09a2b2b | ||
|
|
1df6afa541 | ||
|
|
fcb2c7868c | ||
|
|
46b198866d | ||
|
|
d4e2abe218 | ||
|
|
ac94d838f0 | ||
|
|
9a4da9b763 | ||
|
|
7869144f5e | ||
|
|
2d7d400040 | ||
|
|
6f5e5a2433 | ||
|
|
e59a4659d8 | ||
|
|
23a0f4afdc | ||
|
|
2f9732fc3d | ||
|
|
fdede79155 | ||
|
|
00f23c485d | ||
|
|
3277284473 | ||
|
|
b2b8d0d941 | ||
|
|
91bfd38a9a | ||
|
|
7a31446162 | ||
|
|
11893fe80c | ||
|
|
f4971456d0 | ||
|
|
e23adf4d11 | ||
|
|
cc18a90a86 | ||
|
|
60b6803437 | ||
|
|
2b57157502 | ||
|
|
739a6033af | ||
|
|
ed43baa459 | ||
|
|
9cbc27a6ef | ||
|
|
b017c4df1e | ||
|
|
27aaa82a9c | ||
|
|
57194ed12d | ||
|
|
74d5b4c3f4 | ||
|
|
967208d69b | ||
|
|
3bd13cd7cb | ||
|
|
e95bfab77b | ||
|
|
78785b872d | ||
|
|
ebe1904479 | ||
|
|
6f0c41cddf | ||
|
|
52bda73657 | ||
|
|
23356f6e39 | ||
|
|
4958ee41ae | ||
|
|
9784faa32a | ||
|
|
a913c22200 | ||
|
|
95dce95183 | ||
|
|
53f9230354 | ||
|
|
946f08db4b | ||
|
|
4b0ee8907f | ||
|
|
62bb58dc09 | ||
|
|
1d903fab38 | ||
|
|
a458f2a6f0 | ||
|
|
f66e8cec69 | ||
|
|
9fd070639c | ||
|
|
528748155a | ||
|
|
474ffb98d6 | ||
|
|
cb2d5e4eb4 | ||
|
|
3c51262a37 | ||
|
|
7e4dba7af1 | ||
|
|
319705106b | ||
|
|
2416f93a79 | ||
|
|
aaf845c72a | ||
|
|
fc66179dc0 | ||
|
|
67702b8d89 | ||
|
|
e398091a36 | ||
|
|
d836bcebbc | ||
|
|
81ff1422e8 | ||
|
|
d08f928818 | ||
|
|
27984299e0 | ||
|
|
0cac319988 | ||
|
|
c3e74ada39 | ||
|
|
0509fb041f | ||
|
|
a2e9a3de96 | ||
|
|
32bc7f2207 | ||
|
|
b2a99c83e3 | ||
|
|
a4185bc926 | ||
|
|
7c73b6a458 | ||
|
|
6b579a6946 | ||
|
|
896fe76a8d | ||
|
|
8e09db9d40 | ||
|
|
07ca5a45ae | ||
|
|
ed1621410b | ||
|
|
29e591e69b | ||
|
|
396e632679 | ||
|
|
88a8173178 | ||
|
|
316f8eb16d | ||
|
|
131b1ee57e | ||
|
|
5a8c6912dc | ||
|
|
a7c786987d | ||
|
|
1179e48955 | ||
|
|
07f0884462 | ||
|
|
ef3575358e | ||
|
|
5e2762cbc6 | ||
|
|
7e165fa8bd | ||
|
|
33706e0f27 | ||
|
|
e262ed14b0 | ||
|
|
545eb467fc | ||
|
|
75004768bb | ||
|
|
2848ab68ef | ||
|
|
c8067674f6 | ||
|
|
caf4dc5526 | ||
|
|
fc1aa42c26 | ||
|
|
8baa32d8c9 | ||
|
|
a519077112 | ||
|
|
93841348e1 | ||
|
|
5219188690 | ||
|
|
8ad392a5ac | ||
|
|
267b9eba20 | ||
|
|
9c343ef3fd | ||
|
|
e82b076981 | ||
|
|
97bd88f904 | ||
|
|
21d8cdbb5c | ||
|
|
a5f8c51904 | ||
|
|
94ad43e130 | ||
|
|
6fc8a5166e | ||
|
|
1f04cd2a50 | ||
|
|
5a55c240ee | ||
|
|
e02b36873e | ||
|
|
f89ef95d65 | ||
|
|
a65db66005 | ||
|
|
a6e19d0710 | ||
|
|
df02aeef89 | ||
|
|
2d03f60c70 | ||
|
|
e45b44ad03 | ||
|
|
015429e05d | ||
|
|
5628381449 | ||
|
|
0bf3cf2535 | ||
|
|
d4aa9ff99e | ||
|
|
0e27e1dd3d | ||
|
|
0229c315bb | ||
|
|
67d4458e69 | ||
|
|
67bfbed308 | ||
|
|
2c053b6fd3 | ||
|
|
6156e38a34 | ||
|
|
631df0293c | ||
|
|
b86a6a54ab | ||
|
|
201b32f3fb | ||
|
|
ebed8daee6 | ||
|
|
1b33f38280 | ||
|
|
340869fa91 | ||
|
|
55be0a557f | ||
|
|
b4c45b480b | ||
|
|
06ff11114a | ||
|
|
ebe30a5ce8 | ||
|
|
4c84dec601 | ||
|
|
ece156de0b | ||
|
|
191f7d708c | ||
|
|
8b9df9871a | ||
|
|
af7682aaca | ||
|
|
883cb161ec | ||
|
|
a2fbe121c3 | ||
|
|
ab770c566e | ||
|
|
1867603225 | ||
|
|
cf195da424 | ||
|
|
0cb6aa5d12 | ||
|
|
7e4d4c3c98 | ||
|
|
b5ef3191b7 | ||
|
|
f30481e229 | ||
|
|
ae010c333b | ||
|
|
43d1f9ee7a | ||
|
|
ec17e8736d | ||
|
|
44b27e791e | ||
|
|
02385027db | ||
|
|
b311072d9b | ||
|
|
87ac077b0a | ||
|
|
87837df35c | ||
|
|
5d72bbd162 | ||
|
|
a4fc1c5b44 | ||
|
|
539eaded73 | ||
|
|
93b2e78092 | ||
|
|
402a00dcd3 | ||
|
|
b63368d5f6 | ||
|
|
74c6d3ee36 | ||
|
|
621aa4362b | ||
|
|
c8919ad11f | ||
|
|
fad76dd1a2 | ||
|
|
b2f6499b87 | ||
|
|
9520e850dd | ||
|
|
4ee5271a83 | ||
|
|
d8de7bcc51 | ||
|
|
7ee31be6d6 | ||
|
|
9cb5ba7ac1 | ||
|
|
c380368b61 | ||
|
|
e298f7e5f4 | ||
|
|
c743561c25 | ||
|
|
e73e32fb71 | ||
|
|
b09a48bec4 | ||
|
|
aeef986cf5 | ||
|
|
6f08f50639 | ||
|
|
8fc5f598d0 | ||
|
|
1383ea3fe8 | ||
|
|
f8c37e0d14 | ||
|
|
cf543ef335 | ||
|
|
a3450a7d83 | ||
|
|
e80b2c9fb9 | ||
|
|
8d617fb98c | ||
|
|
a6e225e47c | ||
|
|
e21943f4fb | ||
|
|
c36df5ecc1 | ||
|
|
458def7830 | ||
|
|
b5d800f07a | ||
|
|
6551a6330b | ||
|
|
cb5f670909 | ||
|
|
5b6c1632bd | ||
|
|
bf15f2fb8a | ||
|
|
31ef2f7929 | ||
|
|
8aab94f184 | ||
|
|
b7acb27c98 | ||
|
|
dcbbc55f28 | ||
|
|
81dfcc2eae | ||
|
|
16f3e256b0 | ||
|
|
75b2da9eab | ||
|
|
9736f053d9 | ||
|
|
d0b710c26d | ||
|
|
5b83d4d1b0 | ||
|
|
89f0f4a02c | ||
|
|
a067ccb9e0 | ||
|
|
1ccea42cca | ||
|
|
0f90dd0f54 | ||
|
|
262cbc9a22 | ||
|
|
3a61d20d93 | ||
|
|
95f4f87eb8 | ||
|
|
5b3cc0d492 | ||
|
|
0de39a8163 | ||
|
|
879fd7a224 | ||
|
|
50af138424 | ||
|
|
9d592f4e08 | ||
|
|
5016d27af7 | ||
|
|
7218a19357 | ||
|
|
33d2d4368f | ||
|
|
cca40ca710 | ||
|
|
c6281859fd | ||
|
|
956271880d | ||
|
|
201ff4efc5 | ||
|
|
f954233bda | ||
|
|
8267968b76 | ||
|
|
1e243e4257 | ||
|
|
0b6c3a46cf | ||
|
|
a2e58b7696 | ||
|
|
119d299657 | ||
|
|
e8634e4178 | ||
|
|
2e2a683d11 | ||
|
|
414197acc9 | ||
|
|
8fd01e1eda | ||
|
|
e1ca7d1f59 | ||
|
|
f84d6939bd | ||
|
|
82446e2114 | ||
|
|
a4f0ae9088 | ||
|
|
fe3906f766 | ||
|
|
c9e644f3fc | ||
|
|
8ffbd02db7 | ||
|
|
e88d57d17d | ||
|
|
5f4de6d9f5 | ||
|
|
2cce87deeb | ||
|
|
630b5b19a0 | ||
|
|
2ce1451a9f | ||
|
|
2d1badba96 | ||
|
|
1b5db078a7 | ||
|
|
91095bd63a | ||
|
|
d9703a64fd | ||
|
|
4c22e72390 | ||
|
|
d05b8fca20 | ||
|
|
afca2ad2e0 | ||
|
|
b2aa854d98 | ||
|
|
287fff7cf1 | ||
|
|
136ecae4a6 | ||
|
|
cac772d5e6 | ||
|
|
e1dedcb1b5 | ||
|
|
c4901eca68 | ||
|
|
a001c2d12b | ||
|
|
c4c622eabd | ||
|
|
1ae018bedd | ||
|
|
24929e27c1 | ||
|
|
33a8d92aec | ||
|
|
32d21fa560 | ||
|
|
347d8b7f79 | ||
|
|
8f9c15913b | ||
|
|
60d7f688eb | ||
|
|
15f914914c | ||
|
|
2914475821 | ||
|
|
8e831aa3e7 | ||
|
|
985d4585c4 | ||
|
|
854db13bd7 | ||
|
|
4ddf80459a | ||
|
|
89fc2ff643 | ||
|
|
d7145f1f84 | ||
|
|
441a957432 | ||
|
|
67cbb85682 | ||
|
|
0005d16a06 | ||
|
|
ca5ab03cd4 | ||
|
|
c9e30022df | ||
|
|
c167bdd80f | ||
|
|
b9f62bb1bc | ||
|
|
a01b2cf8a2 | ||
|
|
f4b2623c8f | ||
|
|
af8426579e | ||
|
|
6b9ba9becf | ||
|
|
c6cc7b6983 | ||
|
|
0e869ec18d | ||
|
|
1b347874ef | ||
|
|
ff5b1e8573 | ||
|
|
73ad76a2fa | ||
|
|
aadcb486a9 | ||
|
|
4c854d64a7 | ||
|
|
c2e02d34ad | ||
|
|
8c8977549c | ||
|
|
24ef154d42 | ||
|
|
b5a3045bae | ||
|
|
7404e496cb | ||
|
|
5ba2c7eeec | ||
|
|
514a7f3d51 | ||
|
|
15474b83b1 | ||
|
|
a5f1bc6c82 | ||
|
|
834d44e0e4 | ||
|
|
c070149ae6 | ||
|
|
b0b6ea943e | ||
|
|
65b72217ea | ||
|
|
c4c2e8e3a9 | ||
|
|
47018d61cd | ||
|
|
d18db9adfa | ||
|
|
41913c8c58 | ||
|
|
68b982a744 | ||
|
|
a582302a79 | ||
|
|
bd3b2c93ee | ||
|
|
4704d63791 | ||
|
|
415a1477bb | ||
|
|
f363d5e187 | ||
|
|
efb0ee9c48 | ||
|
|
96daee5e1a | ||
|
|
d236206cce | ||
|
|
1ef8e1cf73 | ||
|
|
dc63f858b0 | ||
|
|
b8a3952153 | ||
|
|
4b2a62f660 | ||
|
|
08c074e44b | ||
|
|
d1953b0215 | ||
|
|
b268d7dd8e | ||
|
|
90e56e2f07 | ||
|
|
45ddaa4d38 | ||
|
|
0de54b163a | ||
|
|
620b86cddb | ||
|
|
32d0ac4147 | ||
|
|
a16733eb93 | ||
|
|
b67eef484c | ||
|
|
5166964659 | ||
|
|
e72c2df6d2 | ||
|
|
cdafb3870c | ||
|
|
20abf21791 | ||
|
|
482d155af4 | ||
|
|
ece23c727e | ||
|
|
b7726cd514 | ||
|
|
c31fe1ed35 | ||
|
|
9e034810d9 | ||
|
|
217aabe55a | ||
|
|
1dea2f384e | ||
|
|
c24edec6b9 | ||
|
|
add09ca5b8 | ||
|
|
57bd3632e8 | ||
|
|
6f44b6ffa6 | ||
|
|
ca8ba2d16b | ||
|
|
c37e4ba6b5 | ||
|
|
82cc778a0f | ||
|
|
5085ef831b | ||
|
|
4721bd117b | ||
|
|
57b13d848a | ||
|
|
e01a43f01e | ||
|
|
ec92351acf | ||
|
|
8c44eb75d5 | ||
|
|
aa48566aef | ||
|
|
7cbb26cbdb | ||
|
|
b8b6175a64 | ||
|
|
de5ad1b1b9 | ||
|
|
0866785495 | ||
|
|
947b3a9ec4 | ||
|
|
6db27ca51f | ||
|
|
f966ba97d7 | ||
|
|
1af711bc89 | ||
|
|
c71da4a075 | ||
|
|
d609fa87b4 | ||
|
|
dd16b0f65f | ||
|
|
6144e5eff4 | ||
|
|
c4db99f5a3 | ||
|
|
21431b0e56 | ||
|
|
136e6bc915 | ||
|
|
05aeb78b01 | ||
|
|
22010a1f9b | ||
|
|
e0fd6b210e | ||
|
|
be1c38f0e5 | ||
|
|
b1bdedd3a3 | ||
|
|
429b47963b | ||
|
|
466c8a0883 | ||
|
|
3a212030c0 | ||
|
|
7f1ac48f5f | ||
|
|
9c3e344ae0 | ||
|
|
db147e77b0 | ||
|
|
0384b03528 | ||
|
|
fc06de7b11 | ||
|
|
5ada0a7093 | ||
|
|
f136b8eb12 | ||
|
|
56a160464f | ||
|
|
3dc9430932 | ||
|
|
afa69b4f9c | ||
|
|
102f46d185 | ||
|
|
9c5e184d82 | ||
|
|
385b5451c8 | ||
|
|
0e7487257b | ||
|
|
eab0e3219f | ||
|
|
cf89c901a2 | ||
|
|
902b08cc0f | ||
|
|
caf9a09efa | ||
|
|
65734dc993 | ||
|
|
0f02fffc3a | ||
|
|
931ecad8c5 | ||
|
|
c137f83df6 | ||
|
|
d3a0a38dce | ||
|
|
b76af1aa21 | ||
|
|
5cf6a37ee2 | ||
|
|
63194abf93 | ||
|
|
e196b0915a | ||
|
|
d2369d1de8 | ||
|
|
a9bbcc5556 | ||
|
|
8d9430e7a2 | ||
|
|
0411754949 | ||
|
|
91ffaa1a2d | ||
|
|
ae9972a91a | ||
|
|
478fa6f2bb | ||
|
|
6a52a04591 | ||
|
|
a8f87e0d5e | ||
|
|
cbc96fdf5c | ||
|
|
b4d24dd9af | ||
|
|
4b66cc2acb | ||
|
|
3766d5ce27 | ||
|
|
4b7d686754 | ||
|
|
b948a07a4d | ||
|
|
6d3505aefa | ||
|
|
b22650ff51 | ||
|
|
23a7f65b49 | ||
|
|
f4fba8eab4 | ||
|
|
1734bf54a7 | ||
|
|
88efec7815 | ||
|
|
e335189bb8 | ||
|
|
d03ca4ab95 | ||
|
|
257407758f | ||
|
|
2443c046aa | ||
|
|
d710eb3947 | ||
|
|
7b2f6f230d | ||
|
|
07cb6070cc | ||
|
|
bd7dc2a7be | ||
|
|
db931c12be | ||
|
|
765b311a08 | ||
|
|
ce198d9c0b | ||
|
|
8f5893931b | ||
|
|
221be48589 | ||
|
|
234ff2619d | ||
|
|
b37be46ba3 | ||
|
|
6e2ea508db | ||
|
|
0e6e2abd28 | ||
|
|
db1bdfbf65 | ||
|
|
7bf0f647b3 | ||
|
|
df25f8617b | ||
|
|
ad2099a27f | ||
|
|
708127f96d | ||
|
|
9deb51e95a | ||
|
|
67852ea657 |
@@ -92,10 +92,17 @@ module.exports = {
|
||||
'@typescript-eslint/member-delimiter-style': 'warn',
|
||||
'@typescript-eslint/method-signature-style': 'warn',
|
||||
'@typescript-eslint/no-extraneous-class': 'error',
|
||||
'@typescript-eslint/no-parameter-properties': 'error',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/parameter-properties': 'error',
|
||||
'@typescript-eslint/strict-boolean-expressions': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.cjs'],
|
||||
env: {
|
||||
node: true
|
||||
}
|
||||
},
|
||||
{
|
||||
extends: ['plugin:chai-expect/recommended', 'plugin:chai-friendly/recommended'],
|
||||
files: ['*.test.ts'],
|
||||
@@ -179,6 +186,17 @@ module.exports = {
|
||||
]
|
||||
}
|
||||
],
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'always',
|
||||
{
|
||||
ignorePackages: true,
|
||||
pattern: {
|
||||
js: 'always',
|
||||
ts: 'never'
|
||||
}
|
||||
}
|
||||
],
|
||||
'import/no-duplicates': 'warn',
|
||||
'sort-imports-es6-autofix/sort-imports-es6': [
|
||||
2,
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -3,8 +3,7 @@ name: Bug Report
|
||||
about: Create a bug report to help us fix a demonstrable problem with code in the library.
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: claviska
|
||||
|
||||
assignees:
|
||||
---
|
||||
|
||||
### Describe the bug
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,4 +1,7 @@
|
||||
contact_links:
|
||||
- name: Feature Requests
|
||||
url: https://github.com/shoelace-style/shoelace/discussions/categories/ideas
|
||||
about: All requests for new features should go here.
|
||||
- name: Help & Support
|
||||
url: https://github.com/shoelace-style/shoelace/discussions/categories/help
|
||||
about: Please don't create issues for personal help requests. Instead, ask your question on the discussion forum.
|
||||
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,17 +0,0 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project.
|
||||
title: ''
|
||||
labels: feature
|
||||
assignees: claviska
|
||||
|
||||
---
|
||||
|
||||
### What issue are you having?
|
||||
Provide a clear and concise description of the problem you're facing.
|
||||
|
||||
### Describe the solution you'd like
|
||||
How would you like to see the library solve it?
|
||||
|
||||
### Describe alternatives you've considered
|
||||
In what ways have you tried to solve this with the current version?
|
||||
4
.github/SECURITY.md
vendored
@@ -1,7 +1,7 @@
|
||||
# Reporting Security Issues
|
||||
|
||||
We take security issues in Shoelace very seriously and appreciate your efforts to disclose your findings responsibly.
|
||||
We take security issues in Web Awesome very seriously and appreciate your efforts to disclose your findings responsibly.
|
||||
|
||||
To report a security issue, email [cory@abeautifulsite.net](mailto:cory@abeautifulsite.net) and include "SHOELACE SECURITY" in the subject line.
|
||||
To report a security issue, email [cory@fontawesome.com](mailto:cory@abeautifulsite.net) and include "WEB AWESOME SECURITY" in the subject line.
|
||||
|
||||
We'll respond as soon as possible and keep you updated throughout the process.
|
||||
|
||||
9
.gitignore
vendored
@@ -1,7 +1,10 @@
|
||||
.DS_Store
|
||||
_site
|
||||
.cache
|
||||
docs/dist
|
||||
docs/search.json
|
||||
.DS_Store
|
||||
package.json
|
||||
package-lock.json
|
||||
dist
|
||||
docs/assets/images/sprite.svg
|
||||
node_modules
|
||||
src/react
|
||||
cdn
|
||||
|
||||
@@ -7,5 +7,8 @@ docs/search.json
|
||||
src/components/icon/icons
|
||||
src/react/index.ts
|
||||
node_modules
|
||||
package.json
|
||||
package-lock.json
|
||||
tsconfig.json
|
||||
cdn
|
||||
_site
|
||||
|
||||
3
.vscode/settings.json
vendored
@@ -3,5 +3,6 @@
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
},
|
||||
"debug.enableStatusBarColor": false
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Contributing to Shoelace
|
||||
# Contributing to Web Awesome
|
||||
|
||||
Before contributing, please review the contributions guidelines at:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2020 A Beautiful Site, LLC
|
||||
Copyright (c) 2023 Fonticons, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
40
README.md
@@ -1,4 +1,4 @@
|
||||
# Shoelace
|
||||
# Web Awesome
|
||||
|
||||
A forward-thinking library of web components.
|
||||
|
||||
@@ -9,7 +9,7 @@ A forward-thinking library of web components.
|
||||
- Built with accessibility in mind ♿️
|
||||
- Open source 😸
|
||||
|
||||
Designed in New Hampshire by [Cory LaViska](https://twitter.com/claviska).
|
||||
Built by the folks behind [Font Awesome](https://fontawesome.com/).
|
||||
|
||||
---
|
||||
|
||||
@@ -21,15 +21,15 @@ Twitter: [@shoelace_style](https://twitter.com/shoelace_style)
|
||||
|
||||
---
|
||||
|
||||
## Shoemakers 🥾
|
||||
## Developers ✨
|
||||
|
||||
Shoemakers, or "Shoelace developers," can use this documentation to learn how to build Shoelace from source. You will need Node >= 14.17 to build and run the project locally.
|
||||
Developers can use this documentation to learn how to build Web Awesome from source. You will need Node >= 14.17 to build and run the project locally.
|
||||
|
||||
**You don't need to do any of this to use Shoelace!** This page is for people who want to contribute to the project, tinker with the source, or create a custom build of Shoelace.
|
||||
**You don't need to do any of this to use Web Awesome!** This page is for people who want to contribute to the project, tinker with the source, or create a custom build of Web Awesome.
|
||||
|
||||
If that's not what you're trying to do, the [documentation website](https://shoelace.style) is where you want to be.
|
||||
|
||||
### What are you using to build Shoelace?
|
||||
### What are you using to build Web Awesome?
|
||||
|
||||
Components are built with [LitElement](https://lit-element.polymer-project.org/), a custom elements base class that provides an intuitive API and reactive data binding. The build is a custom script with bundling powered by [esbuild](https://esbuild.github.io/).
|
||||
|
||||
@@ -38,8 +38,8 @@ Components are built with [LitElement](https://lit-element.polymer-project.org/)
|
||||
Start by [forking the repo](https://github.com/shoelace-style/shoelace/fork) on GitHub, then clone it locally and install dependencies.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_GITHUB_USERNAME/shoelace
|
||||
cd shoelace
|
||||
git clone https://github.com/YOUR_GITHUB_USERNAME/webawesome
|
||||
cd webawesome
|
||||
npm install
|
||||
```
|
||||
|
||||
@@ -51,9 +51,7 @@ Once you've cloned the repo, run the following command.
|
||||
npm start
|
||||
```
|
||||
|
||||
This will spin up the Shoelace dev server. After the initial build, a browser will open automatically. There is currently no hot module reloading (HMR), as browser's don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically.
|
||||
|
||||
The documentation is powered by Docsify, which uses raw markdown files to generate pages. As such, no static files are built for the docs.
|
||||
This will spin up the dev server. After the initial build, a browser will open automatically. There is currently no hot module reloading (HMR), as browser's don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically.
|
||||
|
||||
### Building
|
||||
|
||||
@@ -65,30 +63,18 @@ npm run build
|
||||
|
||||
### Creating New Components
|
||||
|
||||
To scaffold a new component, run the following command, replacing `sl-tag-name` with the desired tag name.
|
||||
To scaffold a new component, run the following command, replacing `wa-tag-name` with the desired tag name.
|
||||
|
||||
```bash
|
||||
npm run create sl-tag-name
|
||||
npm run create wa-tag-name
|
||||
```
|
||||
|
||||
This will generate a source file, a stylesheet, and a docs page for you. When you start the dev server, you'll find the new component in the "Components" section of the sidebar.
|
||||
|
||||
### Contributing
|
||||
|
||||
Shoelace is an open source project and contributions are encouraged! If you're interesting in contributing, please review the [contribution guidelines](CONTRIBUTING.md) first.
|
||||
Web Awesome is an open source project and contributions are encouraged! If you're interesting in contributing, please review the [contribution guidelines](CONTRIBUTING.md) first.
|
||||
|
||||
## License
|
||||
|
||||
Shoelace is designed in New Hampshire by [Cory LaViska](https://twitter.com/claviska). It’s available under the terms of the MIT license.
|
||||
|
||||
Designing, developing, and supporting this library requires a lot of time, effort, and skill. I’d like to keep it open source so everyone can use it, but that doesn’t provide me with any income.
|
||||
|
||||
**Therefore, if you’re using my software to make a profit,** I respectfully ask that you help [fund its development](https://github.com/sponsors/claviska) by becoming a sponsor. There are multiple tiers to choose from with benefits at every level, including prioritized support, bug fixes, feature requests, and advertising.
|
||||
|
||||
👇 Your support is very much appreciated! 👇
|
||||
|
||||
- [Become a sponsor](https://github.com/sponsors/claviska)
|
||||
- [Star on GitHub](https://github.com/shoelace-style/shoelace/stargazers)
|
||||
- [Follow on Twitter](https://twitter.com/shoelace_style)
|
||||
|
||||
Whether you're building Shoelace or building something _with_ Shoelace — have fun creating! 🥾
|
||||
Web Awesome is available under the terms of the MIT license.
|
||||
|
||||
27
cspell.json
@@ -15,8 +15,10 @@
|
||||
"autoplay",
|
||||
"bezier",
|
||||
"boxicons",
|
||||
"CACHEABLE",
|
||||
"callout",
|
||||
"callouts",
|
||||
"cdndir",
|
||||
"chatbubble",
|
||||
"checkmark",
|
||||
"claviska",
|
||||
@@ -26,6 +28,8 @@
|
||||
"colocated",
|
||||
"colour",
|
||||
"combobox",
|
||||
"Commonmark",
|
||||
"compat",
|
||||
"Composability",
|
||||
"Consolas",
|
||||
"contenteditable",
|
||||
@@ -39,13 +43,16 @@
|
||||
"datetime",
|
||||
"describedby",
|
||||
"Docsify",
|
||||
"dogfood",
|
||||
"dropdowns",
|
||||
"easings",
|
||||
"endraw",
|
||||
"enterkeyhint",
|
||||
"eqeqeq",
|
||||
"erroneou",
|
||||
"errormessage",
|
||||
"esbuild",
|
||||
"exportmaps",
|
||||
"exportparts",
|
||||
"fieldsets",
|
||||
"formaction",
|
||||
@@ -79,9 +86,11 @@
|
||||
"labelledby",
|
||||
"Laravel",
|
||||
"LaViska",
|
||||
"linkify",
|
||||
"listbox",
|
||||
"listitem",
|
||||
"litelement",
|
||||
"longform",
|
||||
"lowercasing",
|
||||
"Lucide",
|
||||
"maxlength",
|
||||
@@ -92,6 +101,7 @@
|
||||
"minlength",
|
||||
"monospace",
|
||||
"mousedown",
|
||||
"mousemove",
|
||||
"mouseup",
|
||||
"multiselectable",
|
||||
"nextjs",
|
||||
@@ -99,10 +109,16 @@
|
||||
"noopener",
|
||||
"noreferrer",
|
||||
"novalidate",
|
||||
"npmdir",
|
||||
"Numberish",
|
||||
"oklab",
|
||||
"oklch",
|
||||
"outdir",
|
||||
"ParamagicDev",
|
||||
"peta",
|
||||
"petabit",
|
||||
"Preact",
|
||||
"prismjs",
|
||||
"progressbar",
|
||||
"radiogroup",
|
||||
"Railsbyte",
|
||||
@@ -110,6 +126,8 @@
|
||||
"reregister",
|
||||
"resizer",
|
||||
"resizers",
|
||||
"retargeted",
|
||||
"RETRYABLE",
|
||||
"rgba",
|
||||
"roadmap",
|
||||
"Roboto",
|
||||
@@ -122,7 +140,9 @@
|
||||
"scroller",
|
||||
"Segoe",
|
||||
"semibold",
|
||||
"sitedir",
|
||||
"slotchange",
|
||||
"smartquotes",
|
||||
"spacebar",
|
||||
"stylesheet",
|
||||
"Tabbable",
|
||||
@@ -138,19 +158,22 @@
|
||||
"tinycolor",
|
||||
"transitionend",
|
||||
"treeitem",
|
||||
"treeshaking",
|
||||
"Triaging",
|
||||
"turbolinks",
|
||||
"typeof",
|
||||
"unbundles",
|
||||
"unbundling",
|
||||
"unicons",
|
||||
"unsanitized",
|
||||
"unsupportive",
|
||||
"valpha",
|
||||
"valuenow",
|
||||
"valuetext",
|
||||
"WCAG",
|
||||
"webawesome",
|
||||
"WEBP",
|
||||
"Webpacker",
|
||||
"wordmark"
|
||||
"Webpacker"
|
||||
],
|
||||
"ignorePaths": [
|
||||
"package.json",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { generateCustomData } from 'cem-plugin-vs-code-custom-data-generator';
|
||||
import * as path from 'path';
|
||||
import { customElementJetBrainsPlugin } from 'custom-element-jet-brains-integration';
|
||||
import { customElementVsCodePlugin } from 'custom-element-vs-code-integration';
|
||||
import { parse } from 'comment-parser';
|
||||
import { pascalCase } from 'pascal-case';
|
||||
import commandLineArgs from 'command-line-args';
|
||||
@@ -26,20 +28,47 @@ function replace(string, terms) {
|
||||
}
|
||||
|
||||
export default {
|
||||
globs: ['src/components/**/*.ts'],
|
||||
globs: ['src/components/**/*.component.ts'],
|
||||
exclude: ['**/*.styles.ts', '**/*.test.ts'],
|
||||
plugins: [
|
||||
// Append package data
|
||||
{
|
||||
name: 'shoelace-package-data',
|
||||
name: 'wa-package-data',
|
||||
packageLinkPhase({ customElementsManifest }) {
|
||||
customElementsManifest.package = { name, description, version, author, homepage, license };
|
||||
}
|
||||
},
|
||||
// Infer tag names because we no longer use @customElement decorators.
|
||||
{
|
||||
name: 'wa-infer-tag-names',
|
||||
analyzePhase({ ts, node, moduleDoc }) {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.ClassDeclaration: {
|
||||
const className = node.name.getText();
|
||||
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
|
||||
|
||||
const importPath = moduleDoc.path;
|
||||
|
||||
// This is kind of a best guess at components. "thing.component.ts"
|
||||
if (!importPath.endsWith('.component.ts')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tagNameWithoutPrefix = path.basename(importPath, '.component.ts');
|
||||
const tagName = 'wa-' + tagNameWithoutPrefix;
|
||||
|
||||
classDoc.tagNameWithoutPrefix = tagNameWithoutPrefix;
|
||||
classDoc.tagName = tagName;
|
||||
|
||||
// This used to be set to true by @customElement
|
||||
classDoc.customElement = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Parse custom jsDoc tags
|
||||
{
|
||||
name: 'shoelace-custom-tags',
|
||||
name: 'wa-custom-tags',
|
||||
analyzePhase({ ts, node, moduleDoc }) {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.ClassDeclaration: {
|
||||
@@ -58,6 +87,9 @@ export default {
|
||||
});
|
||||
});
|
||||
|
||||
// This is what allows us to map JSDOC comments to ReactWrappers.
|
||||
classDoc['jsDoc'] = node.jsDoc?.map(jsDoc => jsDoc.getFullText()).join('\n');
|
||||
|
||||
const parsed = parse(`${customComments}\n */`);
|
||||
parsed[0].tags?.forEach(t => {
|
||||
switch (t.tag) {
|
||||
@@ -106,7 +138,7 @@ export default {
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'shoelace-react-event-names',
|
||||
name: 'wa-react-event-names',
|
||||
analyzePhase({ ts, node, moduleDoc }) {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.ClassDeclaration: {
|
||||
@@ -116,6 +148,7 @@ export default {
|
||||
if (classDoc?.events) {
|
||||
classDoc.events.forEach(event => {
|
||||
event.reactName = `on${pascalCase(event.name)}`;
|
||||
event.eventName = `${pascalCase(event.name)}Event`;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -123,7 +156,7 @@ export default {
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'shoelace-translate-module-paths',
|
||||
name: 'wa-translate-module-paths',
|
||||
packageLinkPhase({ customElementsManifest }) {
|
||||
customElementsManifest?.modules?.forEach(mod => {
|
||||
//
|
||||
@@ -137,7 +170,7 @@ export default {
|
||||
//
|
||||
const terms = [
|
||||
{ from: /^src\//, to: '' }, // Strip the src/ prefix
|
||||
{ from: /\.(t|j)sx?$/, to: '.js' } // Convert .ts to .js
|
||||
{ from: /\.component.(t|j)sx?$/, to: '.js' } // Convert .ts to .js
|
||||
];
|
||||
|
||||
mod.path = replace(mod.path, terms);
|
||||
@@ -159,9 +192,25 @@ export default {
|
||||
}
|
||||
},
|
||||
// Generate custom VS Code data
|
||||
generateCustomData({
|
||||
customElementVsCodePlugin({
|
||||
outdir,
|
||||
cssFileName: null
|
||||
cssFileName: null,
|
||||
referencesTemplate: (_, tag) => [
|
||||
{
|
||||
name: 'Documentation',
|
||||
url: `https://shoelace.style/components/${tag.replace('wa-', '')}`
|
||||
}
|
||||
]
|
||||
}),
|
||||
customElementJetBrainsPlugin({
|
||||
outdir: './dist',
|
||||
excludeCss: true,
|
||||
referencesTemplate: (_, tag) => {
|
||||
return {
|
||||
name: 'Documentation',
|
||||
url: `https://shoelace.style/components/${tag.replace('wa-', '')}`
|
||||
};
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Not Found
|
||||
|
||||
<img class="not-found-image" src="/assets/images/undraw-not-found.svg" alt="Cute monsters hiding behind a tree">
|
||||
|
||||
Sorry, I couldn't find that page. Have you tried pressing <kbd>/</kbd> to search?
|
||||
349
docs/_includes/component.njk
Normal file
@@ -0,0 +1,349 @@
|
||||
{% extends "default.njk" %}
|
||||
|
||||
{# Find the component based on the `tag` front matter #}
|
||||
{% set component = getComponent('wa-' + page.fileSlug) %}
|
||||
|
||||
{% block content %}
|
||||
{# Determine the badge variant #}
|
||||
{% if component.status == 'stable' %}
|
||||
{% set badgeVariant = 'brand' %}
|
||||
{% elseif component.status == 'experimental' %}
|
||||
{% set badgeVariant = 'warning' %}
|
||||
{% elseif component.status == 'planned' %}
|
||||
{% set badgeVariant = 'neutral' %}
|
||||
{% elseif component.status == 'deprecated' %}
|
||||
{% set badgeVariant = 'danger' %}
|
||||
{% else %}
|
||||
{% set badgeVariant = 'neutral' %}
|
||||
{% endif %}
|
||||
|
||||
{# Header #}
|
||||
<header class="component-header">
|
||||
<h1>{{ component.name | classNameToComponentName }}</h1>
|
||||
|
||||
<div class="component-header__tag">
|
||||
<code><{{ component.tagName }}> | {{ component.name }}</code>
|
||||
</div>
|
||||
|
||||
<div class="component-header__info">
|
||||
<wa-badge variant="neutral" pill>
|
||||
Since {{component.since or '?' }}
|
||||
</wa-badge>
|
||||
<wa-badge variant="{{ badgeVariant }}" pill style="text-transform: capitalize;">
|
||||
{{ component.status }}
|
||||
</wa-badge>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<p class="component-summary">
|
||||
{% if component.summary %}
|
||||
{{ component.summary | markdownInline | safe }}
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{# Markdown content #}
|
||||
{{ content | safe }}
|
||||
|
||||
{# Importing #}
|
||||
<h2>Importing</h2>
|
||||
<p>
|
||||
If you're using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use
|
||||
any of the following snippets to <a href="/getting-started/installation#cherry-picking">cherry pick</a> this component.
|
||||
</p>
|
||||
|
||||
<wa-tab-group>
|
||||
<wa-tab slot="nav" panel="script">Script</wa-tab>
|
||||
<wa-tab slot="nav" panel="import">Import</wa-tab>
|
||||
<wa-tab slot="nav" panel="bundler">Bundler</wa-tab>
|
||||
<wa-tab slot="nav" panel="react">React</wa-tab>
|
||||
|
||||
<wa-tab-panel name="script">
|
||||
<p>
|
||||
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
|
||||
using a script tag:
|
||||
</p>
|
||||
<pre><code class="language-html"><script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/{{ meta.cdndir }}/{{ component.path }}"></script></code></pre>
|
||||
</wa-tab-panel>
|
||||
|
||||
<wa-tab-panel name="import">
|
||||
<p>
|
||||
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
|
||||
using a JavaScript import:
|
||||
</p>
|
||||
<pre><code class="language-js">import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/{{ meta.cdndir }}/{{ component.path }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
|
||||
<wa-tab-panel name="bundler">
|
||||
<p>
|
||||
To import this component using <a href="{{ rootUrl('/getting-started/installation#bundling') }}">a bundler</a>:
|
||||
</p>
|
||||
<pre><code class="language-js">import '@shoelace-style/shoelace/{{ meta.npmdir }}/{{ component.path }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
|
||||
<wa-tab-panel name="react">
|
||||
<p>
|
||||
To import this component as a <a href="/frameworks/react">React component</a>:
|
||||
</p>
|
||||
<pre><code class="language-js">import {{ component.name }} from '@shoelace-style/shoelace/{{ meta.npmdir }}/react/{{ component.tagNameWithoutPrefix }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
{# Slots #}
|
||||
{% if component.slots.length %}
|
||||
<h2>Slots</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for slot in component.slots %}
|
||||
<tr>
|
||||
<td class="nowrap">
|
||||
{% if slot.name %}
|
||||
<code>{{ slot.name }}</code>
|
||||
{% else %}
|
||||
(default)
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ slot.description | markdownInline | safe }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#slots') }}">using slots</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Properties #}
|
||||
{% if component.properties.length %}
|
||||
<h2>Properties</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-reflects">Reflects</th>
|
||||
<th class="table-type">Type</th>
|
||||
<th class="table-default">Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for prop in component.properties %}
|
||||
<tr>
|
||||
<td>
|
||||
<code class="nowrap">{{ prop.name }}</code>
|
||||
{% if prop.attribute | length > 0 %}
|
||||
{% if prop.attribute != prop.name %}
|
||||
<br>
|
||||
<wa-tooltip content="This attribute is different from its property">
|
||||
<small>
|
||||
<code class="nowrap">
|
||||
{{ prop.attribute }}
|
||||
</code>
|
||||
</small>
|
||||
</wa-tooltip>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ prop.description | markdownInline | safe }}
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
{% if prop.reflects %}
|
||||
<wa-icon label="yes" name="check-lg"></wa-icon>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if prop.type.text %}
|
||||
<code>{{ prop.type.text | markdownInline | safe }}</code>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if prop.default %}
|
||||
<code>{{ prop.default | markdownInline | safe }}</code>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td class="nowrap"><code>updateComplete</code></td>
|
||||
<td>
|
||||
A read-only promise that resolves when the component has
|
||||
<a href="/getting-started/usage?#component-rendering-and-updating">finished updating</a>.
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#attributes-and-properties') }}">attributes and properties</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Events #}
|
||||
{% if component.events.length %}
|
||||
<h2>Events</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name" data-flavor="html">Name</th>
|
||||
<th class="table-name" data-flavor="react">React Event</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-event-detail">Event Detail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for event in component.events %}
|
||||
<tr>
|
||||
<td data-flavor="html"><code class="nowrap">{{ event.name }}</code></td>
|
||||
<td data-flavor="react"><code class="nowrap">{{ event.reactName }}</code></td>
|
||||
<td>{{ event.description | markdownInline | safe }}</td>
|
||||
<td>
|
||||
{% if event.type.text %}
|
||||
<code>{{ event.type.text }}</code>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#events') }}">events</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Methods #}
|
||||
{% if component.methods.length %}
|
||||
<h2>Methods</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-arguments">Arguments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for method in component.methods %}
|
||||
<tr>
|
||||
<td class="nowrap"><code>{{ method.name }}()</code></td>
|
||||
<td>{{ method.description | markdownInline | safe }}</td>
|
||||
<td>
|
||||
{% if method.parameters.length %}
|
||||
<code>
|
||||
{% for param in method.parameters %}
|
||||
{{ param.name }}: {{ param.type.text }}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
</code>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#methods') }}">methods</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Custom Properties #}
|
||||
{% if component.cssProperties.length %}
|
||||
<h2>Custom Properties</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-default">Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for cssProperty in component.cssProperties %}
|
||||
<tr>
|
||||
<td class="nowrap"><code>{{ cssProperty.name }}</code></td>
|
||||
<td>{{ cssProperty.description | markdownInline | safe }}</td>
|
||||
<td>{{ cssProperty.default }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#custom-properties') }}">customizing CSS custom properties</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# CSS Parts #}
|
||||
{% if component.cssParts.length %}
|
||||
<h2>Parts</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for cssPart in component.cssParts %}
|
||||
<tr>
|
||||
<td class="nowrap"><code>{{ cssPart.name }}</code></td>
|
||||
<td>{{ cssPart.description | markdownInline | safe }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing/#css-parts') }}">customizing CSS parts</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Animations #}
|
||||
{% if component.animations.length %}
|
||||
<h2>Animations</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for animation in component.animations %}
|
||||
<tr>
|
||||
<td class="nowrap"><code>{{ animation.name }}</code></td>
|
||||
<td>{{ animation.description | markdownInline | safe }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing#animations') }}">customizing animations</a>.</em></p>
|
||||
{% endif %}
|
||||
|
||||
{# Dependencies #}
|
||||
{% if component.dependencies.length %}
|
||||
<h2>Dependencies</h2>
|
||||
|
||||
<p>This component automatically imports the following dependencies.</p>
|
||||
|
||||
<ul>
|
||||
{% for dependency in component.dependencies %}
|
||||
<li><code><{{ dependency }}></code></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
129
docs/_includes/default.njk
Normal file
@@ -0,0 +1,129 @@
|
||||
<!DOCTYPE html>
|
||||
<html
|
||||
lang="en"
|
||||
data-layout="{{ layout }}"
|
||||
data-wa-version="{{ meta.version }}"
|
||||
>
|
||||
<head>
|
||||
{# Metadata #}
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="{{ meta.description }}" />
|
||||
<title>{{ meta.title }}</title>
|
||||
|
||||
{# Opt out of Turbo caching #}
|
||||
<meta name="turbo-cache-control">
|
||||
|
||||
{# Stylesheets #}
|
||||
<link rel="stylesheet" href="{{ assetUrl('styles/docs.css') }}" />
|
||||
<link rel="stylesheet" href="{{ assetUrl('styles/code-previews.css') }}" />
|
||||
<link rel="stylesheet" href="{{ assetUrl('styles/search.css') }}" />
|
||||
|
||||
{# Favicons #}
|
||||
<link rel="icon" href="{{ assetUrl('images/favicon.svg') }}" type="image/x-icon" />
|
||||
|
||||
{# Twitter Cards #}
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:creator" content="shoelace_style" />
|
||||
<meta name="twitter:image" content="{{ assetUrl(meta.image, true) }}" />
|
||||
|
||||
{# OpenGraph #}
|
||||
<meta property="og:url" content="{{ rootUrl(page.url, true) }}" />
|
||||
<meta property="og:title" content="{{ meta.title }}" />
|
||||
<meta property="og:description" content="{{ meta.description }}" />
|
||||
<meta property="og:image" content="{{ assetUrl(meta.image, true) }}" />
|
||||
|
||||
{# Web Awesome #}
|
||||
<link rel="stylesheet" href="/dist/themes/applied.css" />
|
||||
<link rel="stylesheet" href="/dist/themes/forms.css" />
|
||||
<link id="theme-stylesheet" rel="stylesheet" href="/dist/themes/default.css" />
|
||||
<script type="module" src="/dist/autoloader.js"></script>
|
||||
|
||||
{# Set the initial theme and menu states here to prevent flashing #}
|
||||
<script>
|
||||
(() => {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const theme = localStorage.getItem('theme') || 'auto';
|
||||
document.documentElement.classList.toggle('wa-theme-default-dark', theme === 'dark' || (theme === 'auto' && prefersDark));
|
||||
})();
|
||||
</script>
|
||||
|
||||
{# Web Fonts #}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,500;0,600;1,400;1,500;1,600&family=Noto+Sans+Mono&display=swap" rel="stylesheet">
|
||||
|
||||
{# Turbo + Scroll positioning #}
|
||||
<script src="{{ assetUrl('scripts/turbo.js') }}" type="module"></script>
|
||||
<script src="{{ assetUrl('scripts/docs.js') }}" defer></script>
|
||||
<script src="{{ assetUrl('scripts/code-previews.js') }}" defer></script>
|
||||
<script src="{{ assetUrl('scripts/lunr.js') }}" defer></script>
|
||||
<script src="{{ assetUrl('scripts/search.js') }}" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a id="skip-to-main" class="wa-visually-hidden" href="#main-content" data-smooth-link="false">
|
||||
Skip to main content
|
||||
</a>
|
||||
|
||||
{# Menu toggle #}
|
||||
<button id="menu-toggle" type="button" aria-label="Menu">
|
||||
<svg width="148" height="148" viewBox="0 0 148 148" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="currentColor" stroke-width="18" fill="none" fill-rule="evenodd" stroke-linecap="round">
|
||||
<path d="M9.5 125.5h129M9.5 74.5h129M9.5 23.5h129"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<aside id="sidebar" data-preserve-scroll>
|
||||
<header>
|
||||
<a href="/">
|
||||
{% include 'logo.njk' %}
|
||||
</a>
|
||||
<div class="sidebar-version">
|
||||
{{ meta.version }}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="sidebar-buttons">
|
||||
<wa-button size="small" class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace" target="_blank">
|
||||
<wa-icon slot="prefix" name="github"></wa-icon> Code
|
||||
</wa-button>
|
||||
<wa-button size="small" class="repo-button repo-button--star" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
|
||||
<wa-icon slot="prefix" name="star-fill"></wa-icon> Star
|
||||
</wa-button>
|
||||
<wa-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
|
||||
<wa-icon slot="prefix" name="twitter"></wa-icon> Follow
|
||||
</wa-button>
|
||||
</div>
|
||||
|
||||
<button class="search-box" type="button" title="Press / to search" aria-label="Search" data-plugin="search">
|
||||
<wa-icon name="search"></wa-icon>
|
||||
<span>Search</span>
|
||||
</button>
|
||||
|
||||
<nav>
|
||||
{% include 'sidebar.njk' %}
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
{# Content #}
|
||||
<main>
|
||||
<a id="main-content"></a>
|
||||
<article id="content" class="content{% if toc %} content--with-toc{% endif %}">
|
||||
{% if toc %}
|
||||
<div class="content__toc">
|
||||
<ul>
|
||||
<li class="top"><a href="#">{{ meta.title }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="content__body">
|
||||
{% block content %}
|
||||
{{ content | safe }}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
105
docs/_includes/layout-example.njk
Normal file
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html
|
||||
lang="en"
|
||||
data-layout="{{ layout }}"
|
||||
data-shoelace-version="{{ meta.version }}"
|
||||
>
|
||||
<head>
|
||||
<!--
|
||||
JSPM Generator Import Map
|
||||
Edit URL: https://generator.jspm.io/#jcs9DoMwDIbhDF16kW4kpFO7cYkeIASTGBmDEvNz+zaduhSJwdInvY9vF6Wub/ViQSHoVMQQqZzoITfW6qeuDWFr/JTgMJLjsLgA2ficT6jBrS77hLOcwPtIilCau37o+rdMhwm84FpIgr68/28LZ9dDFWWkQ7NF4G/coK38NM4TA0vVQY8MRdfa2g8tjhfiKAE
|
||||
-->
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"highlight.js/lib/core": "https://ga.jspm.io/npm:highlight.js@11.9.0/es/core.js",
|
||||
"highlight.js/lib/languages/css": "https://ga.jspm.io/npm:highlight.js@11.9.0/es/languages/css.js",
|
||||
"highlight.js/lib/languages/javascript": "https://ga.jspm.io/npm:highlight.js@11.9.0/es/languages/javascript.js",
|
||||
"highlight.js/lib/languages/xml": "https://ga.jspm.io/npm:highlight.js@11.9.0/es/languages/xml.js",
|
||||
"lit": "https://ga.jspm.io/npm:lit@2.8.0/index.js",
|
||||
"lit/directives/ref.js": "https://ga.jspm.io/npm:lit@2.8.0/directives/ref.js",
|
||||
"lit/directives/unsafe-html.js": "https://ga.jspm.io/npm:lit@2.8.0/directives/unsafe-html.js",
|
||||
"lit/directives/when.js": "https://ga.jspm.io/npm:lit@2.8.0/directives/when.js",
|
||||
"web-component-define": "https://ga.jspm.io/npm:web-component-define@2.0.11/src/index.js"
|
||||
},
|
||||
"scopes": {
|
||||
"https://ga.jspm.io/": {
|
||||
"@lit/reactive-element": "https://ga.jspm.io/npm:@lit/reactive-element@1.6.3/reactive-element.js",
|
||||
"@open-wc/dedupe-mixin": "https://ga.jspm.io/npm:@open-wc/dedupe-mixin@1.4.0/index.js",
|
||||
"lit-element/lit-element.js": "https://ga.jspm.io/npm:lit-element@3.3.3/lit-element.js",
|
||||
"lit-html": "https://ga.jspm.io/npm:lit-html@2.8.0/lit-html.js",
|
||||
"lit-html/": "https://ga.jspm.io/npm:lit-html@2.8.0/"
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script async src="https://ga.jspm.io/npm:es-module-shims@1.8.0/dist/es-module-shims.js" crossorigin="anonymous"></script>
|
||||
{# Metadata #}
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="{{ meta.description }}" />
|
||||
<title>{{ meta.title }}</title>
|
||||
|
||||
{# Opt out of Turbo caching #}
|
||||
<meta name="turbo-cache-control" content="no-cache">
|
||||
<meta name="turbo-cache-control" content="no-preview">
|
||||
|
||||
{# Favicons #}
|
||||
<link rel="icon" href="{{ assetUrl('images/favicon.svg') }}" type="image/x-icon" />
|
||||
|
||||
{# Twitter Cards #}
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:creator" content="shoelace_style" />
|
||||
<meta name="twitter:image" content="{{ assetUrl(meta.image, true) }}" />
|
||||
|
||||
{# OpenGraph #}
|
||||
<meta property="og:url" content="{{ rootUrl(page.url, true) }}" />
|
||||
<meta property="og:title" content="{{ meta.title }}" />
|
||||
<meta property="og:description" content="{{ meta.description }}" />
|
||||
<meta property="og:image" content="{{ assetUrl(meta.image, true) }}" />
|
||||
|
||||
{# WebAwesome #}
|
||||
<link rel="stylesheet" href="/dist/themes/default.css" />
|
||||
<link rel="stylesheet" href="/dist/themes/applied.css" />
|
||||
<script type="module" src="/dist/webawesome.js"></script>
|
||||
|
||||
{# Set the initial theme and menu states here to prevent flashing #}
|
||||
<script>
|
||||
(() => {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const theme = localStorage.getItem('theme') || 'auto';
|
||||
document.documentElement.classList.toggle('wa-theme-dark', theme === 'dark' || (theme === 'auto' && prefersDark));
|
||||
|
||||
if (window.Turbo) {
|
||||
window.Turbo.session.drive = false
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
*, *:before, *:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
{% block content %}
|
||||
{{ content | safe }}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
72
docs/_includes/layout-templates/advanced-example.css
Normal file
@@ -0,0 +1,72 @@
|
||||
html {
|
||||
min-height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.grid {
|
||||
font-size: 1.35rem;
|
||||
text-align: center;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--wa-color-blue-90);
|
||||
}
|
||||
|
||||
aside {
|
||||
min-width: 250px;
|
||||
max-width: 250px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
main {
|
||||
background-color: var(--wa-color-green-90);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--wa-color-blue-80);
|
||||
}
|
||||
|
||||
.banner {
|
||||
background-color: var(--wa-color-yellow-90);
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: var(--wa-color-blue-90);
|
||||
}
|
||||
|
||||
.banner, .header {
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[slot=header] {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
[slot=aside] {
|
||||
background-color: var(--wa-color-yellow-90);
|
||||
}
|
||||
|
||||
[slot=menu] {
|
||||
background-color: var(--wa-color-red-80);
|
||||
}
|
||||
|
||||
[slot=main-header] {
|
||||
background-color: var(--wa-color-red-90);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
[slot=main-footer] {
|
||||
background-color: var(--wa-color-red-70);
|
||||
}
|
||||
22
docs/_includes/layout-templates/advanced-example.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<wa-layout main-id="main-content" class="wa-theme-light">
|
||||
<header slot="banner" class="grid banner">
|
||||
Banner
|
||||
</header>
|
||||
|
||||
<header slot="header" class="grid header">Header</header>
|
||||
|
||||
<aside class="grid" slot="menu">Menu</aside>
|
||||
|
||||
<header class="grid" slot="main-header">Inline header</header>
|
||||
|
||||
<main class="grid" id="main-content">
|
||||
<div style="width: 20ch; margin: 0 auto; background-color: white;">Main</div>
|
||||
</main>
|
||||
|
||||
<footer class="grid" slot="main-footer">Inline footer</footer>
|
||||
|
||||
<aside class="grid" slot="aside">Aside</aside>
|
||||
<footer class="grid" slot="footer">Footer</footer>
|
||||
</wa-layout>
|
||||
|
||||
{% include "layout-widget.njk" %}
|
||||
217
docs/_includes/layout-templates/app.css
Normal file
@@ -0,0 +1,217 @@
|
||||
html {
|
||||
min-height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
main {
|
||||
min-height: 100%;
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
wa-layout {
|
||||
background-color: var(--wa-color-neutral-95);
|
||||
color: var(--wa-color-neutral-20);
|
||||
}
|
||||
|
||||
wa-card :is(p, h3) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
wa-layout::part(header) {
|
||||
/** Because headers are sticky, this keeps text from leaking through. */
|
||||
background-color: var(--wa-color-white);
|
||||
}
|
||||
|
||||
wa-layout::part(drawer__panel) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
wa-layout[view="mobile"] {
|
||||
background-color: var(--wa-color-white);
|
||||
--menu-width: 0px;
|
||||
}
|
||||
|
||||
wa-layout[view="mobile"]::part(header) {
|
||||
padding: 0.25rem;
|
||||
border-bottom: 1px solid var(--wa-color-neutral-70);
|
||||
}
|
||||
|
||||
wa-layout[view="mobile"]::part(navigation) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
wa-layout[view="desktop"]::part(main) {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
wa-layout[view="desktop"] {
|
||||
--menu-width: 250px;
|
||||
}
|
||||
|
||||
|
||||
wa-layout[view="desktop"]::part(navigation) {
|
||||
padding-top: 1.9rem;
|
||||
}
|
||||
|
||||
wa-layout[view="desktop"] > [slot="header"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
wa-layout[view="desktop"]::part(header) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
wa-layout[view="desktop"] main {
|
||||
background-color: var(--wa-color-white);
|
||||
box-shadow: 0px 0px 3px 1px rgba(0,0,0,0.05);
|
||||
border: 1px solid var(--wa-color-neutral-80);
|
||||
border-top-left-radius: 8px;
|
||||
}
|
||||
|
||||
/* Navigation / Lists */
|
||||
|
||||
/* Highlights */
|
||||
.highlight {
|
||||
font-size: 0.85em;
|
||||
padding: 0.4em 0.6em;
|
||||
border-radius: 6px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.highlight--success {
|
||||
background-color: var(--wa-color-success-fill-muted);
|
||||
color: var(--wa-color-success-text-on-muted);
|
||||
}
|
||||
|
||||
.highlight--danger {
|
||||
background-color: var(--wa-color-red-90);
|
||||
color: var(--wa-color-red-30);
|
||||
}
|
||||
|
||||
/* Text */
|
||||
.text--light {
|
||||
color: var(--wa-color-neutral-40);
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.wa-card--muted::part(base) {
|
||||
--border-color: transparent;
|
||||
background-color: transparent;
|
||||
display: grid;
|
||||
height: 100%;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.wa-card--muted::part(body) {
|
||||
display: grid;
|
||||
align-content: flex-end;
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.wa-button--card {
|
||||
--border-radius: 8px;
|
||||
--padding: 1rem 0px;
|
||||
}
|
||||
|
||||
.wa-button--card.wa-button--muted {
|
||||
--background-color: var(--wa-color-neutral-95);
|
||||
}
|
||||
|
||||
.wa-button--card::part(base) {
|
||||
height: 100%;
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--padding);
|
||||
}
|
||||
|
||||
.wa-button--card::part(label) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wa-button--muted {
|
||||
--text-color: var(--wa-color-neutral-30);
|
||||
--text-color-active: var(--wa-color-neutral-30);
|
||||
--background-color: transparent;
|
||||
--background-color-active: var(--wa-color-neutral-90);
|
||||
--border-color: transparent;
|
||||
--border-color-active: var(--wa-color-neutral-80);
|
||||
}
|
||||
|
||||
.wa-button--muted::part(base) {
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
.wa-button--muted:is(:focus-within)::part(base) {
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color-active);
|
||||
border-color: var(--border-color-active);
|
||||
}
|
||||
|
||||
.wa-button--muted:is(:hover)::part(base) {
|
||||
background-color: var(--background-color-active);
|
||||
color: var(--text-color-active);
|
||||
border-color: var(--border-color-active);
|
||||
}
|
||||
|
||||
.wa-button--logo::part(base) {
|
||||
font-size: 1.5rem;
|
||||
color: var(--wa-color-neutral-30);
|
||||
}
|
||||
|
||||
.wa-button--square::part(base) {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.wa-button--stretch {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wa-button--stretch::part(label) {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.wa-button--nav-footer {
|
||||
--border-color: var(--wa-color-neutral-70);
|
||||
--wa-spacing-large: 8px;
|
||||
}
|
||||
|
||||
wa-layout[view="desktop"] .wa-button--nav-footer::part(base) {
|
||||
--border-color: transparent;
|
||||
border-top-color: var(--wa-color-neutral-70);
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
max-width: 100%;
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
table tr {
|
||||
border-bottom: 1px solid var(--wa-color-neutral-70);
|
||||
}
|
||||
|
||||
table th {
|
||||
font-weight: var(--wa-font-weight-semibold);
|
||||
text-align: left;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
table td {
|
||||
line-height: var(--wa-line-height-normal);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
* > table {
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
438
docs/_includes/layout-templates/app.html
Normal file
@@ -0,0 +1,438 @@
|
||||
<wa-layout main-id="main-content" class="wa-theme-light">
|
||||
<header slot="header">
|
||||
<wa-icon-button name="list" style="font-size: 1.5rem" data-toggle-nav></wa-icon>
|
||||
</header>
|
||||
<wa-button href="#" variant="text" style="padding: 0 0.4rem" class="wa-button--logo wa-button--stretch wa-button--muted" size="large" slot="navigation-header">
|
||||
<wa-icon name="music-note" slot="prefix" style="font-size: 2rem;"></wa-icon>
|
||||
Musicify
|
||||
</wa-button>
|
||||
|
||||
<nav style="padding: 1rem;" slot="navigation">
|
||||
<wa-nav-group style="height: 100%;">
|
||||
<wa-nav-item href="#">
|
||||
<wa-icon name="search" slot="prefix"></wa-icon>
|
||||
Search
|
||||
</wa-nav-item>
|
||||
|
||||
<wa-nav-item href="#">
|
||||
<wa-icon name="bell" slot="prefix"></wa-icon>
|
||||
Notifications
|
||||
</wa-nav-item>
|
||||
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<wa-nav-item href="#" current="page">
|
||||
<wa-icon name="house-door" slot="prefix"></wa-icon>
|
||||
Home
|
||||
</wa-nav-item>
|
||||
|
||||
<wa-nav-item href="#">
|
||||
<wa-icon name="music-note-list" slot="prefix"></wa-icon>
|
||||
Playlists
|
||||
</wa-nav-item>
|
||||
|
||||
<wa-nav-item href="#">
|
||||
<wa-icon name="file-earmark-music" slot="prefix"></wa-icon>
|
||||
Tracks
|
||||
</wa-nav-item>
|
||||
|
||||
<wa-nav-item href="#">
|
||||
<wa-icon name="gear" slot="prefix"></wa-icon>
|
||||
Settings
|
||||
</wa-nav-item>
|
||||
|
||||
<wa-nav-item href="#" style="margin-top: auto;">
|
||||
<wa-icon name="question-circle" slot="prefix"></wa-icon>
|
||||
Help
|
||||
</wa-nav-item>
|
||||
</wa-nav-group>
|
||||
</nav>
|
||||
|
||||
<!-- Hacky override to make padding 8px -->
|
||||
<wa-button slot="navigation-footer" outline class="wa-button--square wa-button--stretch wa-button--muted wa-button--nav-footer" size="large" href="#">
|
||||
<div style="display: grid; align-items: center; max-width: 100%; gap: 8px; grid-template-columns: minmax(0, auto) minmax(0, 1fr) minmax(0, auto);">
|
||||
<wa-avatar shape="rounded" style="--size: 36px;"></wa-avatar>
|
||||
<div style="text-overflow: ellipsis; max-width: 100%; overflow: hidden; text-align: start; font-size: 1rem;">Really really really long name</div>
|
||||
<wa-icon name="chevron-right"></wa-icon>
|
||||
</div>
|
||||
</wa-button>
|
||||
|
||||
|
||||
<main id="main-content" class="main">
|
||||
<h1 style="margin: 0.5rem 0 2rem 0;">Good Evening, Konnor Rogers</h1>
|
||||
|
||||
<section>
|
||||
<div style="display: flex; justify-content: space-between; flex-wrap: wrap; align-items: flex-end; gap: 8px;">
|
||||
<h2 style="">Overview</h2>
|
||||
|
||||
<wa-select value="monthly">
|
||||
<wa-option value="daily">Daily</wa-option>
|
||||
<wa-option value="weekly">Weekly</wa-option>
|
||||
<wa-option value="monthly">Monthly</wa-option>
|
||||
<wa-option value="yearly">Yearly</wa-option>
|
||||
</wa-select>
|
||||
</div>
|
||||
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
|
||||
<div style="margin-top: 1rem; display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-auto-rows: 1fr; gap: var(--wa-spacing-large); text-align: center;">
|
||||
<wa-card class="wa-card--muted" style="--padding: 8px;">
|
||||
<h3 slot="header">Total listening time</h3>
|
||||
|
||||
<p>
|
||||
<strong><wa-format-number value="35000"></wa-format-number></strong> minutes
|
||||
</p>
|
||||
<p>
|
||||
<mark class="highlight highlight--success">
|
||||
+16%
|
||||
</mark>
|
||||
|
||||
<small class="text--light">from last month</small>
|
||||
</p>
|
||||
</wa-card>
|
||||
|
||||
<wa-card class="wa-card--muted" style="--padding: 8px;">
|
||||
<h3 slot="header">Total songs played</h3>
|
||||
|
||||
<p>
|
||||
<strong><wa-format-number value="302"></wa-format-number></strong> songs
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<mark class="highlight highlight--danger">
|
||||
-0.3%
|
||||
</mark>
|
||||
|
||||
<small class="text--light">from last month</small>
|
||||
</p>
|
||||
</wa-card>
|
||||
|
||||
<wa-card class="wa-card--muted" style="--padding: 8px;">
|
||||
<h3 slot="header">Average listening session</h3>
|
||||
|
||||
<p>
|
||||
<strong><wa-format-number value="36"></wa-format-number></strong> minutes
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<mark class="highlight highlight--success">
|
||||
+11.4%
|
||||
</mark>
|
||||
|
||||
<small class="text--light">from last month</small>
|
||||
</p>
|
||||
</wa-card>
|
||||
|
||||
<wa-card class="wa-card--muted" style="--padding: 8px;">
|
||||
<h3 slot="header">Average track listening time</h3>
|
||||
|
||||
<p>
|
||||
<strong><wa-format-number value="2"></wa-format-number></strong> minutes,
|
||||
<strong><wa-format-number value="42"></wa-format-number></strong> seconds
|
||||
</p>
|
||||
<p>
|
||||
<mark class="highlight highlight--success">
|
||||
-6.2%
|
||||
</mark>
|
||||
|
||||
<small class="text--light">from last month</small>
|
||||
</p>
|
||||
</wa-card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="margin-top: 3rem;">
|
||||
<h2>Recent playlists</h2>
|
||||
|
||||
<div style="margin-top: 1rem; --card-width: clamp(200px, 100%, 350px); display: grid; grid-template-columns: repeat(auto-fit, minmax(var(--card-width), 1fr)); gap: 16px;">
|
||||
|
||||
<wa-button variant="neutral" class="wa-button--card wa-button--muted" href="#">
|
||||
<div style="display: flex; gap: 1rem;">
|
||||
<img src="https://via.placeholder.com/100x100" height="100" width="100" style="align-self: center; border-radius: 8px; display: inline-block; max-width: 100%; flex: 0 1 auto;">
|
||||
|
||||
<article style="display: grid; align-content: center; color: var(--wa-color-neutral-700); grid-template-columns: minmax(0, 1fr); max-width: 100%; overflow: hidden; width: 100%;">
|
||||
<h2 style="max-width: 100%; text-overflow: ellipsis; overflow: hidden;">
|
||||
Punk Rock Anthems
|
||||
</h2>
|
||||
|
||||
<p style="max-width: 100%; text-overflow: ellipsis; overflow: hidden;">
|
||||
For when you just wanna rock out, have a good time, and feel angsty.
|
||||
</p>
|
||||
|
||||
<wa-icon name="chevron-right" style="justify-self: flex-end;"></wa-icon>
|
||||
</article>
|
||||
</div>
|
||||
</wa-button>
|
||||
|
||||
|
||||
<wa-button variant="neutral" class="wa-button--card wa-button--muted" href="#">
|
||||
<div style="display: flex; gap: 1rem;">
|
||||
<img src="https://via.placeholder.com/100x100" height="100" width="100" style="align-self: center; border-radius: 8px; display: inline-block; max-width: 100%;">
|
||||
|
||||
<article style="display: grid; align-content: center; color: var(--wa-color-neutral-700); grid-template-columns: minmax(0, 1fr); max-width: 100%; overflow: hidden; width: 100%;">
|
||||
<h2 style="max-width: 100%; text-overflow: ellipsis; overflow: hidden;">
|
||||
Random
|
||||
</h2>
|
||||
|
||||
<p style="max-width: 100%; text-overflow: ellipsis; overflow: hidden;">
|
||||
Throw it on shuffle, and embrace the chaos.
|
||||
</p>
|
||||
|
||||
<wa-icon name="chevron-right" style="justify-self: flex-end;"></wa-icon>
|
||||
</article>
|
||||
</div>
|
||||
</wa-button>
|
||||
|
||||
<wa-button variant="neutral" class="wa-button--card wa-button--muted" href="#">
|
||||
<div style="display: flex; gap: 1rem;">
|
||||
<img src="https://via.placeholder.com/100x100" height="100" width="100" style="align-self: center; border-radius: 8px; display: inline-block;">
|
||||
|
||||
<article style="display: grid; align-content: center; color: var(--wa-color-neutral-700); grid-template-columns: minmax(0, 1fr); max-width: 100%; overflow: hidden; width: 100%;">
|
||||
<h2 style="max-width: 100%; text-overflow: ellipsis; overflow: hidden;">
|
||||
Classics
|
||||
</h2>
|
||||
|
||||
<p style="max-width: 100%; text-overflow: ellipsis; overflow: hidden;">
|
||||
Timeless songs that you love to relive.
|
||||
</p>
|
||||
|
||||
<wa-icon name="chevron-right" style="justify-self: flex-end;"></wa-icon>
|
||||
</article>
|
||||
</div>
|
||||
</wa-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="margin-top: 3rem;">
|
||||
<h2>Recent tracks</h2>
|
||||
|
||||
<div style="margin-top: 1rem; max-width: 100%; overflow: auto;">
|
||||
<table style="width: 100%; min-width: 500px; border-spacing: 2px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Release Date
|
||||
</th>
|
||||
|
||||
<th>
|
||||
Name
|
||||
</th>
|
||||
|
||||
<th>
|
||||
Album
|
||||
</th>
|
||||
|
||||
<th>
|
||||
Artist
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<wa-format-date date="2020-07-15T09:17:00-04:00"></wa-format-date>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
No Strangers to Love
|
||||
</td>
|
||||
|
||||
<td>
|
||||
You Know the Rules
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Rick Barry
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<wa-format-date date="2020-07-15T09:17:00-04:00"></wa-format-date>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
No Strangers to Love
|
||||
</td>
|
||||
|
||||
<td>
|
||||
You Know the Rules
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Rick Barry
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<wa-format-date date="2020-07-15T09:17:00-04:00"></wa-format-date>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
No Strangers to Love
|
||||
</td>
|
||||
|
||||
<td>
|
||||
You Know the Rules
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Rick Barry
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<wa-format-date date="2020-07-15T09:17:00-04:00"></wa-format-date>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
No Strangers to Love
|
||||
</td>
|
||||
|
||||
<td>
|
||||
You Know the Rules
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Rick Barry
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<wa-format-date date="2020-07-15T09:17:00-04:00"></wa-format-date>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
No Strangers to Love
|
||||
</td>
|
||||
|
||||
<td>
|
||||
You Know the Rules
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Rick Barry
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<wa-format-date date="2020-07-15T09:17:00-04:00"></wa-format-date>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
No Strangers to Love
|
||||
</td>
|
||||
|
||||
<td>
|
||||
You Know the Rules
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Rick Barry
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<wa-format-date date="2020-07-15T09:17:00-04:00"></wa-format-date>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
No Strangers to Love
|
||||
</td>
|
||||
|
||||
<td>
|
||||
You Know the Rules
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Rick Barry
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<wa-format-date date="2020-07-15T09:17:00-04:00"></wa-format-date>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
No Strangers to Love
|
||||
</td>
|
||||
|
||||
<td>
|
||||
You Know the Rules
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Rick Barry
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<wa-format-date date="2020-07-15T09:17:00-04:00"></wa-format-date>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
No Strangers to Love
|
||||
</td>
|
||||
|
||||
<td>
|
||||
You Know the Rules
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Rick Barry
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<wa-format-date date="2020-07-15T09:17:00-04:00"></wa-format-date>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
No Strangers to Love
|
||||
</td>
|
||||
|
||||
<td>
|
||||
You Know the Rules
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Rick Barry
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<wa-format-date date="2020-07-15T09:17:00-04:00"></wa-format-date>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
No Strangers to Love
|
||||
</td>
|
||||
|
||||
<td>
|
||||
You Know the Rules
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Rick Barry
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
</wa-layout>
|
||||
74
docs/_includes/layout-templates/example.css
Normal file
@@ -0,0 +1,74 @@
|
||||
html {
|
||||
min-height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.grid {
|
||||
font-size: 1.35rem;
|
||||
text-align: center;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--wa-color-blue-90);
|
||||
}
|
||||
|
||||
aside {
|
||||
min-width: 250px;
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
main {
|
||||
background-color: var(--wa-color-green-90);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--wa-color-blue-80);
|
||||
}
|
||||
|
||||
.banner {
|
||||
background-color: var(--wa-color-yellow-90);
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: var(--wa-color-blue-90);
|
||||
}
|
||||
|
||||
.banner, .header {
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[slot=header] {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
[slot=aside] {
|
||||
height: 100%;
|
||||
background-color: var(--wa-color-yellow-90);
|
||||
}
|
||||
|
||||
[slot=menu] {
|
||||
height: 100%;
|
||||
background-color: var(--wa-color-red-90);
|
||||
}
|
||||
|
||||
[slot=main-header] {
|
||||
background-color: var(--wa-color-red-80);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
[slot=main-footer] {
|
||||
background-color: var(--wa-color-green-80);
|
||||
}
|
||||
|
||||
11
docs/_includes/layout-templates/example.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<wa-layout main-id="main-content" class="wa-theme-light">
|
||||
<header class="grid" slot="header">Header</header>
|
||||
<aside class="grid" slot="menu">Menu</aside>
|
||||
<main class="grid" id="main-content">
|
||||
Main
|
||||
</main>
|
||||
<aside class="grid" slot="aside">Aside</aside>
|
||||
<footer class="grid" slot="footer">Footer</footer>
|
||||
</wa-layout>
|
||||
|
||||
{% include "layout-widget.njk" %}
|
||||
10
docs/_includes/layout-templates/hero.css
Normal file
@@ -0,0 +1,10 @@
|
||||
html {
|
||||
min-height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
2
docs/_includes/layout-templates/hero.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<wa-layout main-id="main-content" class="wa-theme-light">
|
||||
</wa-layout>
|
||||
181
docs/_includes/layout-templates/sport-awesome.css
Normal file
@@ -0,0 +1,181 @@
|
||||
html {
|
||||
min-height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
--wa-color-brand-fill-vivid: var(--wa-color-blue-20);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/** https://andy-bell.co.uk/my-favourite-3-lines-of-css/ */
|
||||
.flow > * + * {
|
||||
margin-block-start: var(--wa-flow-spacing);
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.navigation--desktop::part(nav-items) {
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.navigation--top::part(nav-items) {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.navigation--top wa-nav-item {
|
||||
font-size: 1.4rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.navigation--top wa-nav-item::part(content) {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.navigation--top wa-nav-item {
|
||||
--text-color: var(--wa-color-brand-text-on-vivid);
|
||||
--text-color-hover: var(--wa-color-text-normal);
|
||||
--background-color: transparent;
|
||||
--background-color-hover: var(--wa-color-neutral-fill-muted-alt);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--wa-color-brand-fill-vivid);
|
||||
background-color: var(--wa-color-white);
|
||||
}
|
||||
|
||||
.header > * {
|
||||
padding-top: var(--wa-space-base);
|
||||
padding-bottom: var(--wa-space-base);
|
||||
}
|
||||
|
||||
.header__navigation {
|
||||
display: flex;
|
||||
clip-path: polygon(2rem 0,100% 0,100% 100%,0 100%);
|
||||
padding-inline-start: 2.5rem;
|
||||
padding-inline-end: var(--wa-space-base);
|
||||
background-color: var(--wa-color-brand-fill-vivid);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header > .logo {
|
||||
padding-inline-start: var(--wa-space-base);
|
||||
/** Responsive font size for the top header to make it flow nicer */
|
||||
font-size: clamp(1rem, 4vw, 1.4rem);
|
||||
}
|
||||
|
||||
a.logo {
|
||||
flex-shrink: 0;
|
||||
font-size: 1.4rem;
|
||||
font-weight: bold;
|
||||
color: var(--wa-color-text-normal);
|
||||
text-decoration: none;
|
||||
margin: auto;
|
||||
|
||||
}
|
||||
|
||||
a.logo:is(:hover, :focus) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.logo__accent {
|
||||
color: var(--wa-color-yellow-70);
|
||||
}
|
||||
|
||||
.navigation--desktop {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.navigation--desktop wa-nav-item[current="page"] {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 8px;
|
||||
text-decoration-thickness: 4px;
|
||||
text-decoration-color: var(--wa-color-red-50);
|
||||
}
|
||||
|
||||
.navigation--extra {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
wa-layout[view="desktop"] [data-toggle-nav],
|
||||
wa-layout[view="desktop"]::part(navigation) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
wa-layout[view="mobile"] .navigation--desktop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-banner {
|
||||
padding: var(--wa-space-base);
|
||||
text-align: center;
|
||||
background-color: var(--wa-color-yellow-80);
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(clamp(175px, 100%, 400px), 1fr));
|
||||
gap: var(--wa-space-base);
|
||||
grid-template-rows: 1fr;
|
||||
align-items: start;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.stats-grid table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--wa-color-surface-outline);
|
||||
}
|
||||
|
||||
|
||||
.table-scroll {
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.stats-grid table * {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.stats-grid table th {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stats-grid table td:nth-child(2) {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.navigation--top.navigation--social::part(nav-items) {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.navigation--social {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.navigation--top .social-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 415px) {
|
||||
.navigation--top .social-link {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
292
docs/_includes/layout-templates/sport-awesome.html
Normal file
@@ -0,0 +1,292 @@
|
||||
<wa-layout main-id="main-content" class="wa-theme-light" mobile-breakpoint="925" disable-sticky="banner">
|
||||
<header class="layout-banner" slot="banner">
|
||||
Reminder! Get your insurance paperwork in by Oct 12!
|
||||
</header>
|
||||
|
||||
<header class="header" slot="header">
|
||||
<a href="#" class="logo">
|
||||
<span>Sport</span> <span class="logo__accent">Awesome</span>
|
||||
</a>
|
||||
|
||||
<div class="header__navigation">
|
||||
<wa-nav-group class="navigation navigation--top navigation--desktop">
|
||||
<wa-nav-item href="#" current="page">Home</wa-nav-item>
|
||||
<wa-nav-item href="#">Schedule</wa-nav-item>
|
||||
<wa-nav-item href="#">Roster</wa-nav-item>
|
||||
<wa-nav-item href="#">Stats</wa-nav-item>
|
||||
<wa-nav-item href="#">Videos</wa-nav-item>
|
||||
|
||||
</wa-nav-group>
|
||||
|
||||
<wa-nav-group class="navigation navigation--top navigation--social">
|
||||
<wa-nav-item class="social-link" href="#"><wa-icon name="instagram"></wa-icon></wa-nav-item>
|
||||
<wa-nav-item class="social-link" href="#"><wa-icon name="facebook"></wa-icon></wa-nav-item>
|
||||
<wa-nav-item data-toggle-nav href="#"><wa-icon name="list"></wa-icon></wa-nav-item>
|
||||
</wa-nav-group>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<a href="#" class="logo" slot="navigation-header">
|
||||
Sport <span class="logo__accent">Awesome</span>
|
||||
</a>
|
||||
|
||||
<wa-nav-group slot="navigation">
|
||||
<wa-nav-item href="#" current="page">
|
||||
<wa-icon name="house-door" slot="prefix"></wa-icon>
|
||||
Home
|
||||
</wa-nav-item>
|
||||
<wa-nav-item href="#">
|
||||
<wa-icon name="calendar" slot="prefix"></wa-icon>
|
||||
Schedule
|
||||
</wa-nav-item>
|
||||
<wa-nav-item href="#">
|
||||
<wa-icon name="people" slot="prefix"></wa-icon>
|
||||
Roster
|
||||
</wa-nav-item>
|
||||
<wa-nav-item href="#">
|
||||
<wa-icon name="graph-up-arrow" slot="prefix"></wa-icon>
|
||||
Stats
|
||||
</wa-nav-item>
|
||||
<wa-nav-item href="#">
|
||||
<wa-icon name="camera-video" slot="prefix"></wa-icon>
|
||||
Videos
|
||||
</wa-nav-item>
|
||||
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<wa-nav-group>
|
||||
<wa-nav-item href="#">
|
||||
<wa-icon name="instagram" slot="prefix"></wa-icon>
|
||||
Instagram
|
||||
</wa-nav-item>
|
||||
<wa-nav-item href="#">
|
||||
<wa-icon name="facebook" slot="prefix"></wa-icon>
|
||||
Facebook
|
||||
</wa-nav-item>
|
||||
</wa-nav-group>
|
||||
</wa-nav-group>
|
||||
|
||||
<main id="main-content" class="flow" style="padding: var(--wa-space-base);">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px;">
|
||||
<img src="https://via.placeholder.com/1600x900" height="512" width="300" style="aspect-ratio: 16/9; min-width: 75%; flex-grow: 1;">
|
||||
<aside style="display: flex; flex-direction: column; border-radius: var(--wa-corners-1x); flex-grow: 1; flex-shrink: 1;">
|
||||
<header style="font-size: 1.4rem; font-weight: bold; color: var(--wa-color-brand-text-on-vivid); background-color: var(--wa-color-brand-fill-vivid); padding: var(--wa-space-base); text-align: center; border-top-left-radius: inherit; border-top-right-radius: inherit;">
|
||||
Upcoming
|
||||
</header>
|
||||
|
||||
<div style="color: var(--wa-color-brand-text-on-vivid); background-color: var(--wa-color-red-40); padding: 0.5rem; text-align: center; font-weight: bold;">
|
||||
Tryouts!
|
||||
</div>
|
||||
|
||||
<div style="background-color: var(--wa-color-neutral-90); padding: var(--wa-space-base); border-bottom-left-radius: inherit; border-bottom-right-radius: inherit; text-align: center;">
|
||||
<span style="font-weight: bold; font-size: 1.2rem;">Barclay's Center</span>
|
||||
|
||||
<br>
|
||||
|
||||
<time>Sat, Jul 3rd • 11:30am</time>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h1>Welcome to Sport <span class="logo__accent">Awesome</span></h1>
|
||||
|
||||
<p>
|
||||
Dolor quam voluptate nostrum neque eius. Quo nemo corporis repellat quia sunt molestiae! Dolorem labore laudantium nobis numquam reprehenderit? Voluptatibus odio animi nemo maiores accusamus eaque Assumenda perferendis omnis quae.
|
||||
Adipisicing beatae lorem nisi aliquid similique Voluptas doloremque pariatur tempore omnis maiores explicabo. Provident iste vel explicabo corporis quaerat! Necessitatibus minus quas iusto ducimus consequatur illo Cum eos adipisci ut!
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 style="text-align: center; font-weight: bold; font-size: 1.5em;">Stats</h2>
|
||||
<div class="stats-grid">
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
Serve
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Attempts</td>
|
||||
<td>2936</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Serve %</td>
|
||||
<td>93.6%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Aces</td>
|
||||
<td>268</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Errors</td>
|
||||
<td>189</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
Serve Receive
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Attempts</td>
|
||||
<td>2428</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pass Rating</td>
|
||||
<td>1.72</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pass Error %</td>
|
||||
<td>13.3%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3-pass %</td>
|
||||
<td>28.5%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
Attack
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Attempts</td>
|
||||
<td>3624</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kills</td>
|
||||
<td>1431</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Errors</td>
|
||||
<td>268</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hitting Efficiency</td>
|
||||
<td>0.254</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Kill %</td>
|
||||
<td>39.5%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
Dig
|
||||
</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Attempts</td>
|
||||
<td>3124</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Digs</td>
|
||||
<td>2235</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Errors</td>
|
||||
<td>889</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Dig %</td>
|
||||
<td>71.5%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
Block
|
||||
</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Blocks</td>
|
||||
<td>348</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Errors</td>
|
||||
<td>414</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Block %</td>
|
||||
<td>31.6%</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Error %</td>
|
||||
<td>24.6%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
Set
|
||||
</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Assists</td>
|
||||
<td>1364</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Errors</td>
|
||||
<td>81</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer slot="main-footer" style="background-color: var(--wa-color-brand-fill-vivid); color: var(--wa-color-text-inverse); padding: var(--wa-space-base); text-align: center;">
|
||||
© 2023 - Sport Awesome
|
||||
</footer>
|
||||
</wa-layout>
|
||||
92
docs/_includes/layout-widget.njk
Normal file
@@ -0,0 +1,92 @@
|
||||
<!-- playground-hide -->
|
||||
<style>
|
||||
.layout-widget {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
background-color: white;
|
||||
bottom: 4rem;
|
||||
left: 4rem;
|
||||
}
|
||||
|
||||
.layout-widget:not(:defined) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<wa-dropdown id="js-layout-widget" class="layout-widget" stay-open-on-select>
|
||||
<wa-button slot="trigger" caret>Dropdown</wa-button>
|
||||
<wa-menu></wa-menu>
|
||||
</wa-dropdown>
|
||||
|
||||
<script type="module">
|
||||
const layoutWidget = document.querySelector("#js-layout-widget")
|
||||
const docFrag = new DocumentFragment()
|
||||
|
||||
function makeMenuItem (type, slot) {
|
||||
const menuItem = Object.assign(document.createElement("wa-menu-item"), {
|
||||
type: "checkbox",
|
||||
textContent: `${type} ${slot}`
|
||||
})
|
||||
|
||||
menuItem.setAttribute("value", `${type}-${slot}`)
|
||||
return menuItem
|
||||
}
|
||||
|
||||
document.querySelectorAll("wa-layout > [slot]").forEach((el) => {
|
||||
const slot = el.getAttribute("slot");
|
||||
docFrag.append(makeMenuItem("toggle", slot), makeMenuItem("overflow", slot), document.createElement("wa-divider"))
|
||||
})
|
||||
|
||||
docFrag.append(makeMenuItem("toggle", "main"), makeMenuItem("overflow", "main"))
|
||||
layoutWidget.querySelector("wa-menu").append(docFrag)
|
||||
|
||||
function capitalize(string) {
|
||||
return string.split(/\s+/).map((str) => str[0].toUppercase() + str.slice(1)).join(" ")
|
||||
}
|
||||
|
||||
function handleSelect (e) {
|
||||
const item = e.detail.item
|
||||
const val = item.getAttribute("value")
|
||||
|
||||
if (val === "footer-0") {
|
||||
|
||||
}
|
||||
|
||||
const slot = val.split("-").slice(1).join("-")
|
||||
|
||||
let el
|
||||
|
||||
if (slot === "main") {
|
||||
el = document.querySelector(`main`)
|
||||
} else {
|
||||
el = document.querySelector(`wa-layout > [slot='${slot}']`)
|
||||
}
|
||||
|
||||
if (val.startsWith("overflow")) {
|
||||
if (item.checked) {
|
||||
el.textContent = "lorem ".repeat(1_000)
|
||||
return
|
||||
}
|
||||
|
||||
el.textContent = slot
|
||||
return
|
||||
}
|
||||
|
||||
if (val.startsWith("toggle")) {
|
||||
if (item.checked) {
|
||||
el.setAttribute("hidden", "")
|
||||
return
|
||||
}
|
||||
|
||||
el.removeAttribute("hidden")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
layoutWidget.addEventListener("wa-select", handleSelect);
|
||||
{% if in_playground %}
|
||||
</script>
|
||||
{% else %}
|
||||
</script>
|
||||
{% endif %}
|
||||
<!-- playground-hide-end -->
|
||||
1
docs/_includes/logo.njk
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
31
docs/_includes/playground.njk
Normal file
@@ -0,0 +1,31 @@
|
||||
<script type="module" src="/assets/scripts/light-pen/exports/index.js">
|
||||
</script>
|
||||
|
||||
<light-pen style="height: 100%; padding: 8px;" resize-position="30" sandbox-settings="
|
||||
allow-downloads
|
||||
allow-forms
|
||||
allow-modals
|
||||
allow-orientation-lock
|
||||
allow-pointer-lock
|
||||
allow-popups
|
||||
allow-presentation
|
||||
allow-same-origin
|
||||
allow-scripts
|
||||
">
|
||||
<script type="text/plain" slot="html">
|
||||
{% include html_file %}
|
||||
</script>
|
||||
|
||||
<script type="text/plain" slot="css">
|
||||
@import "/dist/themes/default.css";
|
||||
@import "/dist/themes/applied.css";
|
||||
|
||||
{% include css_file %}
|
||||
</script>
|
||||
|
||||
<script type="text/plain" slot="js">
|
||||
import { setBasePath } from "/dist/utilities/base-path.js";
|
||||
setBasePath("/dist");
|
||||
import("/dist/autoloader.js");
|
||||
</script>
|
||||
</light-pen>
|
||||
84
docs/_includes/sidebar.njk
Normal file
@@ -0,0 +1,84 @@
|
||||
<ul>
|
||||
<li>
|
||||
<h2>Experimental</h2>
|
||||
<ul>
|
||||
<li><a href="/experimental/themer">Themer</a></li>
|
||||
<li><a href="/experimental/style-guide">Style Guide</a></li>
|
||||
<li><a href="/experimental/form-validation">Form Validation Styles</a></li>
|
||||
<li style="margin-top: .5rem;"><wa-switch id="theme-toggle">Dark mode</wa-switch></li>
|
||||
<script type="module">
|
||||
// Temporary dark toggle
|
||||
const toggle = document.getElementById('theme-toggle');
|
||||
toggle.checked = document.documentElement.classList.contains('wa-theme-default-dark');
|
||||
|
||||
toggle.addEventListener('wa-change', () => {
|
||||
document.documentElement.classList.toggle('wa-theme-default-dark');
|
||||
localStorage.setItem('theme', toggle.checked ? 'dark' : 'light');
|
||||
});
|
||||
</script>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Getting Started</h2>
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/getting-started/installation">Installation</a></li>
|
||||
<li><a href="/getting-started/usage">Usage</a></li>
|
||||
<li><a href="/getting-started/themes">Themes</a></li>
|
||||
<li><a href="/getting-started/customizing">Customizing</a></li>
|
||||
<li><a href="/getting-started/form-controls">Form Controls</a></li>
|
||||
<li><a href="/getting-started/localization">Localization</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Frameworks</h2>
|
||||
<ul>
|
||||
<li><a href="/frameworks/react">React</a></li>
|
||||
<li><a href="/frameworks/vue">Vue</a></li>
|
||||
<li><a href="/frameworks/angular">Angular</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Resources</h2>
|
||||
<ul>
|
||||
<li><a href="/resources/community">Community</a></li>
|
||||
<li><a href="https://github.com/shoelace-style/shoelace/discussions">Help & Support</a></li>
|
||||
<li><a href="/resources/accessibility">Accessibility</a></li>
|
||||
<li><a href="/resources/contributing">Contributing</a></li>
|
||||
<li><a href="/resources/changelog">Changelog</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Components</h2>
|
||||
<ul>
|
||||
{% for component in meta.components %}
|
||||
<li>
|
||||
<a href="/components/{{ component.tagName | removeWaPrefix }}">
|
||||
{{ component.name | classNameToComponentName }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Design Tokens</h2>
|
||||
<ul>
|
||||
<li><a href="/tokens/typography">Typography</a></li>
|
||||
<li><a href="/tokens/color">Color</a></li>
|
||||
<li><a href="/tokens/spacing">Spacing</a></li>
|
||||
<li><a href="/tokens/borders">Borders</a></li>
|
||||
<li><a href="/tokens/shadows">Shadows</a></li>
|
||||
<li><a href="/tokens/transition">Transition</a></li>
|
||||
<li><a href="/tokens/z-index">Z-index</a></li>
|
||||
<li><a href="/tokens/more">More Tokens</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Tutorials</h2>
|
||||
<ul>
|
||||
<li><a href="/tutorials/integrating-with-laravel">Integrating with Laravel</a></li>
|
||||
<li><a href="/tutorials/integrating-with-nextjs">Integrating with NextJS</a></li>
|
||||
<li><a href="/tutorials/integrating-with-rails">Integrating with Rails</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
103
docs/_sidebar.md
@@ -1,103 +0,0 @@
|
||||
- Getting Started
|
||||
|
||||
- [Overview](/)
|
||||
- [Installation](/getting-started/installation)
|
||||
- [Usage](/getting-started/usage)
|
||||
- [Themes](/getting-started/themes)
|
||||
- [Customizing](/getting-started/customizing)
|
||||
- [Form Controls](/getting-started/form-controls)
|
||||
- [Localization](/getting-started/localization)
|
||||
|
||||
- Frameworks
|
||||
|
||||
- [React](/frameworks/react)
|
||||
- [Vue](/frameworks/vue)
|
||||
- [Angular](/frameworks/angular)
|
||||
|
||||
- Resources
|
||||
|
||||
- [Community](/resources/community)
|
||||
- [Accessibility](/resources/accessibility)
|
||||
- [Contributing](/resources/contributing)
|
||||
- [Changelog](/resources/changelog)
|
||||
|
||||
- Components
|
||||
|
||||
- [Alert](/components/alert)
|
||||
- [Avatar](/components/avatar)
|
||||
- [Badge](/components/badge)
|
||||
- [Breadcrumb](/components/breadcrumb)
|
||||
- [Breadcrumb Item](/components/breadcrumb-item)
|
||||
- [Button](/components/button)
|
||||
- [Button Group](/components/button-group)
|
||||
- [Card](/components/card)
|
||||
- [Carousel](/components/carousel)
|
||||
- [Carousel Item](/components/carousel-item)
|
||||
- [Checkbox](/components/checkbox)
|
||||
- [Color Picker](/components/color-picker)
|
||||
- [Details](/components/details)
|
||||
- [Dialog](/components/dialog)
|
||||
- [Divider](/components/divider)
|
||||
- [Drawer](/components/drawer)
|
||||
- [Dropdown](/components/dropdown)
|
||||
- [Icon](/components/icon)
|
||||
- [Icon Button](/components/icon-button)
|
||||
- [Image Comparer](/components/image-comparer)
|
||||
- [Input](/components/input)
|
||||
- [Menu](/components/menu)
|
||||
- [Menu Item](/components/menu-item)
|
||||
- [Menu Label](/components/menu-label)
|
||||
- [Option](/components/option)
|
||||
- [Progress Bar](/components/progress-bar)
|
||||
- [Progress Ring](/components/progress-ring)
|
||||
- [QR Code](/components/qr-code)
|
||||
- [Radio](/components/radio)
|
||||
- [Radio Button](/components/radio-button)
|
||||
- [Radio Group](/components/radio-group)
|
||||
- [Range](/components/range)
|
||||
- [Rating](/components/rating)
|
||||
- [Select](/components/select)
|
||||
- [Skeleton](/components/skeleton)
|
||||
- [Spinner](/components/spinner)
|
||||
- [Split Panel](/components/split-panel)
|
||||
- [Switch](/components/switch)
|
||||
- [Tab Group](/components/tab-group)
|
||||
- [Tab](/components/tab)
|
||||
- [Tab Panel](/components/tab-panel)
|
||||
- [Tag](/components/tag)
|
||||
- [Textarea](/components/textarea)
|
||||
- [Tooltip](/components/tooltip)
|
||||
- [Tree](/components/tree)
|
||||
- [Tree Item](/components/tree-item)
|
||||
<!--plop:component-->
|
||||
|
||||
- Utilities
|
||||
|
||||
- [Animated Image](/components/animated-image)
|
||||
- [Animation](/components/animation)
|
||||
- [Format Bytes](/components/format-bytes)
|
||||
- [Format Date](/components/format-date)
|
||||
- [Format Number](/components/format-number)
|
||||
- [Include](/components/include)
|
||||
- [Mutation Observer](/components/mutation-observer)
|
||||
- [Popup](/components/popup)
|
||||
- [Relative Time](/components/relative-time)
|
||||
- [Resize Observer](/components/resize-observer)
|
||||
- [Visually Hidden](/components/visually-hidden)
|
||||
|
||||
- Design Tokens
|
||||
|
||||
- [Typography](/tokens/typography)
|
||||
- [Color](/tokens/color)
|
||||
- [Spacing](/tokens/spacing)
|
||||
- [Elevation](/tokens/elevation)
|
||||
- [Border Radius](/tokens/border-radius)
|
||||
- [Transition](/tokens/transition)
|
||||
- [Z-index](/tokens/z-index)
|
||||
- [More](/tokens/more)
|
||||
|
||||
- Tutorials
|
||||
|
||||
- [Integrating with Laravel](/tutorials/integrating-with-laravel)
|
||||
- [Integrating with NextJS](/tutorials/integrating-with-nextjs)
|
||||
- [Integrating with Rails](/tutorials/integrating-with-rails)
|
||||
35
docs/_utilities/active-links.cjs
Normal file
@@ -0,0 +1,35 @@
|
||||
function normalizePathname(pathname) {
|
||||
// Remove /index.html
|
||||
if (pathname.endsWith('/index.html')) {
|
||||
pathname = pathname.replace(/\/index\.html/, '');
|
||||
}
|
||||
|
||||
// Remove trailing slashes
|
||||
return pathname.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a class name to links that are currently active.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
options = {
|
||||
className: 'active-link', // the class to add to active links
|
||||
pathname: undefined, // the current pathname to compare
|
||||
within: 'body', // element containing the target links
|
||||
...options
|
||||
};
|
||||
|
||||
const within = doc.querySelector(options.within);
|
||||
|
||||
if (!within) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
within.querySelectorAll('a').forEach(link => {
|
||||
if (normalizePathname(options.pathname) === normalizePathname(link.pathname)) {
|
||||
link.classList.add(options.className);
|
||||
}
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
64
docs/_utilities/anchor-headings.cjs
Normal file
@@ -0,0 +1,64 @@
|
||||
const { createSlug } = require('./strings.cjs');
|
||||
|
||||
/**
|
||||
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
|
||||
* The same document will be returned with the appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
options = {
|
||||
levels: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], // the headings to convert
|
||||
className: 'anchor-heading', // the class name to add
|
||||
within: 'body', // the element containing the target headings
|
||||
...options
|
||||
};
|
||||
|
||||
const within = doc.querySelector(options.within);
|
||||
|
||||
if (!within) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
within.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => {
|
||||
const hasAnchor = heading.querySelector('a');
|
||||
const anchor = doc.createElement('a');
|
||||
let id = heading.textContent ?? '';
|
||||
let suffix = 0;
|
||||
|
||||
// Skip heading levels we don't care about
|
||||
if (!options.levels?.includes(heading.tagName.toLowerCase())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert dots to underscores
|
||||
id = id.replace(/\./g, '_');
|
||||
|
||||
// Turn it into a slug
|
||||
id = createSlug(id);
|
||||
|
||||
// Make sure it starts with a letter
|
||||
if (!/^[a-z]/i.test(id)) {
|
||||
id = `id_${id}`;
|
||||
}
|
||||
|
||||
// Make sure the id is unique
|
||||
const originalId = id;
|
||||
while (doc.getElementById(id) !== null) {
|
||||
id = `${originalId}-${++suffix}`;
|
||||
}
|
||||
|
||||
if (hasAnchor || !id) return;
|
||||
|
||||
heading.setAttribute('id', id);
|
||||
anchor.setAttribute('href', `#${encodeURIComponent(id)}`);
|
||||
anchor.setAttribute('aria-label', `Direct link to "${heading.textContent}"`);
|
||||
|
||||
if (options.className) {
|
||||
heading.classList.add(options.className);
|
||||
}
|
||||
|
||||
// Append the anchor
|
||||
heading.append(anchor);
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
71
docs/_utilities/cem.cjs
Normal file
@@ -0,0 +1,71 @@
|
||||
const customElementsManifest = require('../../dist/custom-elements.json');
|
||||
|
||||
//
|
||||
// Export it here so we can import it elsewhere and use the same version
|
||||
//
|
||||
module.exports.customElementsManifest = customElementsManifest;
|
||||
|
||||
//
|
||||
// Gets all components from custom-elements.json and returns them in a more documentation-friendly format.
|
||||
//
|
||||
module.exports.getAllComponents = function () {
|
||||
const allComponents = [];
|
||||
|
||||
customElementsManifest.modules?.forEach(module => {
|
||||
module.declarations?.forEach(declaration => {
|
||||
if (declaration.customElement) {
|
||||
// Generate the dist path based on the src path and attach it to the component
|
||||
declaration.path = module.path.replace(/^src\//, 'dist/').replace(/\.ts$/, '.js');
|
||||
|
||||
// Remove members that are private or don't have a description
|
||||
const members = declaration.members?.filter(member => member.description && member.privacy !== 'private');
|
||||
const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private');
|
||||
const properties = members?.filter(prop => {
|
||||
// Look for a corresponding attribute
|
||||
const attribute = declaration.attributes?.find(attr => attr.fieldName === prop.name);
|
||||
if (attribute) {
|
||||
prop.attribute = attribute.name || attribute.fieldName;
|
||||
}
|
||||
|
||||
return prop.kind === 'field' && prop.privacy !== 'private';
|
||||
});
|
||||
allComponents.push({
|
||||
...declaration,
|
||||
methods,
|
||||
properties
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Build dependency graphs
|
||||
allComponents.forEach(component => {
|
||||
const dependencies = [];
|
||||
|
||||
// Recursively fetch sub-dependencies
|
||||
function getDependencies(tag) {
|
||||
const cmp = allComponents.find(c => c.tagName === tag);
|
||||
if (!cmp || !Array.isArray(component.dependencies)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmp.dependencies?.forEach(dependentTag => {
|
||||
if (!dependencies.includes(dependentTag)) {
|
||||
dependencies.push(dependentTag);
|
||||
}
|
||||
getDependencies(dependentTag);
|
||||
});
|
||||
}
|
||||
|
||||
getDependencies(component.tagName);
|
||||
|
||||
component.dependencies = dependencies.sort();
|
||||
});
|
||||
|
||||
// Sort by name
|
||||
return allComponents.sort((a, b) => {
|
||||
if (a.name < b.name) return -1;
|
||||
if (a.name > b.name) return 1;
|
||||
return 0;
|
||||
});
|
||||
};
|
||||
138
docs/_utilities/code-previews.cjs
Normal file
@@ -0,0 +1,138 @@
|
||||
let count = 1;
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns code fields with the :preview suffix into interactive code previews.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
options = {
|
||||
within: 'body', // the element containing the code fields to convert
|
||||
...options
|
||||
};
|
||||
|
||||
const within = doc.querySelector(options.within);
|
||||
if (!within) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
within.querySelectorAll('[class*=":preview"]').forEach(code => {
|
||||
const pre = code.closest('pre');
|
||||
if (!pre) {
|
||||
return;
|
||||
}
|
||||
const adjacentPre = pre.nextElementSibling?.tagName.toLowerCase() === 'pre' ? pre.nextElementSibling : null;
|
||||
const reactCode = adjacentPre?.querySelector('code[class$="react"]');
|
||||
const sourceGroupId = `code-preview-source-group-${count}`;
|
||||
const isExpanded = code.getAttribute('class').includes(':expanded');
|
||||
const noCodePen = code.getAttribute('class').includes(':no-codepen');
|
||||
|
||||
count++;
|
||||
|
||||
const htmlButton = `
|
||||
<button type="button"
|
||||
title="Show HTML code"
|
||||
class="code-preview__button code-preview__button--html"
|
||||
>
|
||||
HTML
|
||||
</button>
|
||||
`;
|
||||
|
||||
const reactButton = `
|
||||
<button type="button" title="Show React code" class="code-preview__button code-preview__button--react">
|
||||
React
|
||||
</button>
|
||||
`;
|
||||
|
||||
const codePenButton = `
|
||||
<button type="button" class="code-preview__button code-preview__button--codepen" title="Edit on CodePen">
|
||||
<svg
|
||||
width="138"
|
||||
height="26"
|
||||
viewBox="0 0 138 26"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M80 6h-9v14h9 M114 6h-9 v14h9 M111 13h-6 M77 13h-6 M122 20V6l11 14V6 M22 16.7L33 24l11-7.3V9.3L33 2L22 9.3V16.7z M44 16.7L33 9.3l-11 7.4 M22 9.3l11 7.3 l11-7.3 M33 2v7.3 M33 16.7V24 M88 14h6c2.2 0 4-1.8 4-4s-1.8-4-4-4h-6v14 M15 8c-1.3-1.3-3-2-5-2c-4 0-7 3-7 7s3 7 7 7 c2 0 3.7-0.8 5-2 M64 13c0 4-3 7-7 7h-5V6h5C61 6 64 9 64 13z" />
|
||||
</svg>
|
||||
</button>
|
||||
`;
|
||||
|
||||
const codePreview = `
|
||||
<div class="code-preview ${isExpanded ? 'code-preview--expanded' : ''}">
|
||||
<div class="code-preview__preview">
|
||||
${code.textContent}
|
||||
<div class="code-preview__resizer">
|
||||
<wa-icon name="grip-vertical"></wa-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="code-preview__source-group" id="${sourceGroupId}">
|
||||
<div class="code-preview__source code-preview__source--html" ${reactCode ? 'data-flavor="html"' : ''}>
|
||||
<pre><code class="language-html">${escapeHtml(code.textContent)}</code></pre>
|
||||
</div>
|
||||
|
||||
${
|
||||
reactCode
|
||||
? `
|
||||
<div class="code-preview__source code-preview__source--react" data-flavor="react">
|
||||
<pre><code class="language-jsx">${escapeHtml(reactCode.textContent)}</code></pre>
|
||||
</div>
|
||||
`
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="code-preview__buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="code-preview__button code-preview__toggle"
|
||||
aria-expanded="${isExpanded ? 'true' : 'false'}"
|
||||
aria-controls="${sourceGroupId}"
|
||||
>
|
||||
Source
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
${reactCode ? ` ${htmlButton} ${reactButton} ` : ''}
|
||||
|
||||
${noCodePen ? '' : codePenButton}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
pre.insertAdjacentHTML('afterend', codePreview);
|
||||
pre.remove();
|
||||
|
||||
if (adjacentPre) {
|
||||
adjacentPre.remove();
|
||||
}
|
||||
});
|
||||
|
||||
// Wrap code preview scripts in anonymous functions so they don't run in the global scope
|
||||
doc.querySelectorAll('.code-preview__preview script').forEach(script => {
|
||||
if (script.type === 'module') {
|
||||
// Modules are already scoped
|
||||
script.textContent = script.innerHTML;
|
||||
} else {
|
||||
// Wrap non-modules in an anonymous function so they don't run in the global scope
|
||||
script.textContent = `(() => { ${script.innerHTML} })();`;
|
||||
}
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
23
docs/_utilities/copy-code-buttons.cjs
Normal file
@@ -0,0 +1,23 @@
|
||||
let codeBlockId = 0;
|
||||
|
||||
/**
|
||||
* Adds copy code buttons to code fields. The provided doc should be a document object provided by JSDOM. The same
|
||||
* document will be returned with the appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc) {
|
||||
doc.querySelectorAll('pre > code').forEach(code => {
|
||||
const pre = code.closest('pre');
|
||||
const button = doc.createElement('wa-copy-button');
|
||||
|
||||
if (!code.id) {
|
||||
code.id = `code-block-${++codeBlockId}`;
|
||||
}
|
||||
|
||||
button.classList.add('copy-code-button');
|
||||
button.setAttribute('from', code.id);
|
||||
|
||||
pre.append(button);
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
41
docs/_utilities/external-links.cjs
Normal file
@@ -0,0 +1,41 @@
|
||||
const { isExternalLink } = require('./strings.cjs');
|
||||
|
||||
/**
|
||||
* Transforms external links to make them safer and optionally add a target. The provided doc should be a document
|
||||
* object provided by JSDOM. The same document will be returned with the appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
options = {
|
||||
className: 'external-link', // the class name to add to links
|
||||
noopener: true, // sets rel="noopener"
|
||||
noreferrer: true, // sets rel="noreferrer"
|
||||
ignore: () => false, // callback function to filter links that should be ignored
|
||||
within: 'body', // element that contains the target links
|
||||
target: '', // sets the target attribute
|
||||
...options
|
||||
};
|
||||
|
||||
const within = doc.querySelector(options.within);
|
||||
|
||||
if (within) {
|
||||
within.querySelectorAll('a').forEach(link => {
|
||||
if (isExternalLink(link) && !options.ignore(link)) {
|
||||
link.classList.add(options.className);
|
||||
|
||||
const rel = [];
|
||||
if (options.noopener) rel.push('noopener');
|
||||
if (options.noreferrer) rel.push('noreferrer');
|
||||
|
||||
if (rel.length) {
|
||||
link.setAttribute('rel', rel.join(' '));
|
||||
}
|
||||
|
||||
if (options.target) {
|
||||
link.setAttribute('target', options.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return doc;
|
||||
};
|
||||
63
docs/_utilities/highlight-code.cjs
Normal file
@@ -0,0 +1,63 @@
|
||||
const Prism = require('prismjs');
|
||||
const PrismLoader = require('prismjs/components/index.js');
|
||||
|
||||
PrismLoader('diff');
|
||||
PrismLoader.silent = true;
|
||||
|
||||
/** Highlights a code string. */
|
||||
function highlight(code, language) {
|
||||
const alias = language.replace(/^diff-/, '');
|
||||
const isDiff = /^diff-/i.test(language);
|
||||
|
||||
// Auto-load the target language
|
||||
if (!Prism.languages[alias]) {
|
||||
PrismLoader(alias);
|
||||
|
||||
if (!Prism.languages[alias]) {
|
||||
throw new Error(`Unsupported language for code highlighting: "${language}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// Register diff-* languages to use the diff grammar
|
||||
if (isDiff) {
|
||||
Prism.languages[language] = Prism.languages.diff;
|
||||
}
|
||||
|
||||
return Prism.highlight(code, Prism.languages[language], language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights all code fields that have a language parameter. If the language has a colon in its name, the first chunk
|
||||
* will be the language used and additional chunks will be applied as classes to the `<pre>`. For example, a code field
|
||||
* tagged with "html:preview" will be rendered as `<pre class="language-html preview">`.
|
||||
*
|
||||
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
|
||||
* appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc) {
|
||||
doc.querySelectorAll('pre > code[class]').forEach(code => {
|
||||
// Look for class="language-*" and split colons into separate classes
|
||||
code.classList.forEach(className => {
|
||||
if (className.startsWith('language-')) {
|
||||
//
|
||||
// We use certain suffixes to indicate code previews, expanded states, etc. The class might look something like
|
||||
// this:
|
||||
//
|
||||
// class="language-html:preview:expanded"
|
||||
//
|
||||
// The language will always come first, so we need to drop the "language-" prefix and everything after the first
|
||||
// color to get the highlighter language.
|
||||
//
|
||||
const language = className.replace(/^language-/, '').split(':')[0];
|
||||
|
||||
try {
|
||||
code.innerHTML = highlight(code.textContent ?? '', language);
|
||||
} catch (err) {
|
||||
// Language not found, skip it
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
75
docs/_utilities/markdown.cjs
Normal file
@@ -0,0 +1,75 @@
|
||||
const MarkdownIt = require('markdown-it');
|
||||
const markdownItContainer = require('markdown-it-container');
|
||||
const markdownItIns = require('markdown-it-ins');
|
||||
const markdownItKbd = require('markdown-it-kbd');
|
||||
const markdownItMark = require('markdown-it-mark');
|
||||
const markdownItReplaceIt = require('markdown-it-replace-it');
|
||||
|
||||
const markdown = MarkdownIt({
|
||||
html: true,
|
||||
xhtmlOut: false,
|
||||
breaks: false,
|
||||
langPrefix: 'language-',
|
||||
linkify: false,
|
||||
typographer: false
|
||||
});
|
||||
|
||||
// Third-party plugins
|
||||
markdown.use(markdownItContainer);
|
||||
markdown.use(markdownItIns);
|
||||
markdown.use(markdownItKbd);
|
||||
markdown.use(markdownItMark);
|
||||
markdown.use(markdownItReplaceIt);
|
||||
|
||||
// Callouts
|
||||
['tip', 'warning', 'danger'].forEach(type => {
|
||||
const variant = type === 'tip' ? 'brand' : type;
|
||||
let icon = 'info-circle';
|
||||
if (type === 'warning') icon = 'exclamation-circle';
|
||||
if (type === 'danger') icon = 'exclamation-triangle';
|
||||
|
||||
markdown.use(markdownItContainer, type, {
|
||||
render: function (tokens, idx) {
|
||||
if (tokens[idx].nesting === 1) {
|
||||
return `
|
||||
<wa-alert class="callout" variant="${variant}" open>
|
||||
<wa-icon slot="icon" name="${icon}"></wa-icon>
|
||||
`;
|
||||
}
|
||||
return '</wa-alert>\n';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Asides
|
||||
markdown.use(markdownItContainer, 'aside', {
|
||||
render: function (tokens, idx) {
|
||||
if (tokens[idx].nesting === 1) {
|
||||
return `<aside>`;
|
||||
}
|
||||
return '</aside>\n';
|
||||
}
|
||||
});
|
||||
|
||||
// Details
|
||||
markdown.use(markdownItContainer, 'details', {
|
||||
validate: params => params.trim().match(/^details\s+(.*)$/),
|
||||
render: (tokens, idx) => {
|
||||
const m = tokens[idx].info.trim().match(/^details\s+(.*)$/);
|
||||
if (tokens[idx].nesting === 1) {
|
||||
return `<details>\n<summary><span>${markdown.utils.escapeHtml(m[1])}</span></summary>\n`;
|
||||
}
|
||||
return '</details>\n';
|
||||
}
|
||||
});
|
||||
|
||||
// Replace [#1234] with a link to GitHub issues
|
||||
markdownItReplaceIt.replacements.push({
|
||||
name: 'github-issues',
|
||||
re: /\[#([0-9]+)\]/gs,
|
||||
sub: '<a href="https://github.com/shoelace-style/shoelace/issues/$1">#$1</a>',
|
||||
html: true,
|
||||
default: true
|
||||
});
|
||||
|
||||
module.exports = markdown;
|
||||
26
docs/_utilities/prettier.cjs
Normal file
@@ -0,0 +1,26 @@
|
||||
const { format } = require('prettier');
|
||||
|
||||
/** Formats markup using prettier. */
|
||||
module.exports = function (content, options) {
|
||||
options = {
|
||||
arrowParens: 'avoid',
|
||||
bracketSpacing: true,
|
||||
htmlWhitespaceSensitivity: 'css',
|
||||
insertPragma: false,
|
||||
bracketSameLine: false,
|
||||
jsxSingleQuote: false,
|
||||
parser: 'html',
|
||||
printWidth: 120,
|
||||
proseWrap: 'preserve',
|
||||
quoteProps: 'as-needed',
|
||||
requirePragma: false,
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
trailingComma: 'none',
|
||||
useTabs: false,
|
||||
...options
|
||||
};
|
||||
|
||||
return format(content, options);
|
||||
};
|
||||
24
docs/_utilities/replacer.cjs
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @typedef {object} Replacement
|
||||
* @property {string | RegExp} pattern
|
||||
* @property {string} replacement
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Array<Replacement>} Replacements
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Document} content
|
||||
* @param {Replacements} replacements
|
||||
*/
|
||||
module.exports = function (content, replacements) {
|
||||
/** This seems trivial, but by assigning to a string first, THEN using innerHTML after iterating over every replacement, we reduce the calculations of JSDOM. At the time of writing benchmarks show a reduction from 9seconds to 3 seconds by doing so. */
|
||||
let html = content.body.innerHTML;
|
||||
|
||||
replacements.forEach(replacement => {
|
||||
html = html.replaceAll(replacement.pattern, replacement.replacement);
|
||||
});
|
||||
|
||||
content.body.innerHTML = html;
|
||||
};
|
||||
26
docs/_utilities/scrolling-tables.cjs
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Turns tables into scrollable tables
|
||||
* The same document will be returned with the appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
// We don't want to run this on layouts.
|
||||
if (doc.querySelector("[data-layout='layout-example.njk']")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tables = [...doc.querySelectorAll('table')];
|
||||
|
||||
options = {
|
||||
className: 'table-scroll', // the class name to add to the table's container
|
||||
...options
|
||||
};
|
||||
|
||||
tables.forEach(table => {
|
||||
const div = doc.createElement('div');
|
||||
div.classList.add(options.className);
|
||||
table.insertAdjacentElement('beforebegin', div);
|
||||
div.append(table);
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
16
docs/_utilities/strings.cjs
Normal file
@@ -0,0 +1,16 @@
|
||||
const slugify = require('slugify');
|
||||
|
||||
/** Creates a slug from an arbitrary string of text. */
|
||||
module.exports.createSlug = function (text) {
|
||||
return slugify(String(text), {
|
||||
remove: /[^\w|\s]/g,
|
||||
lower: true
|
||||
});
|
||||
};
|
||||
|
||||
/** Determines whether or not a link is external. */
|
||||
module.exports.isExternalLink = function (link) {
|
||||
// We use the "internal" hostname when initializing JSDOM so we know that those are local links
|
||||
if (!link.hostname || link.hostname === 'internal') return false;
|
||||
return true;
|
||||
};
|
||||
42
docs/_utilities/table-of-contents.cjs
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Generates an in-page table of contents based on headings.
|
||||
*/
|
||||
module.exports = function (doc, options) {
|
||||
options = {
|
||||
levels: ['h2'], // headings to include (they must have an id)
|
||||
container: 'nav', // the container to append links to
|
||||
listItem: true, // if true, links will be wrapped in <li>
|
||||
within: 'body', // the element containing the headings to summarize
|
||||
...options
|
||||
};
|
||||
|
||||
const container = doc.querySelector(options.container);
|
||||
const within = doc.querySelector(options.within);
|
||||
const headingSelector = options.levels.map(h => `${h}[id]`).join(', ');
|
||||
|
||||
if (!container || !within) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
within.querySelectorAll(headingSelector).forEach(heading => {
|
||||
const listItem = doc.createElement('li');
|
||||
const link = doc.createElement('a');
|
||||
const level = heading.tagName.slice(1);
|
||||
|
||||
link.href = `#${heading.id}`;
|
||||
link.textContent = heading.textContent;
|
||||
|
||||
if (options.listItem) {
|
||||
// List item + link
|
||||
listItem.setAttribute('data-level', level);
|
||||
listItem.append(link);
|
||||
container.append(listItem);
|
||||
} else {
|
||||
// Link only
|
||||
link.setAttribute('data-level', level);
|
||||
container.append(link);
|
||||
}
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
23
docs/_utilities/typography.cjs
Normal file
@@ -0,0 +1,23 @@
|
||||
const smartquotes = require('smartquotes');
|
||||
|
||||
smartquotes.replacements.push([/---/g, '\u2014']); // em dash
|
||||
smartquotes.replacements.push([/--/g, '\u2013']); // en dash
|
||||
smartquotes.replacements.push([/\.\.\./g, '\u2026']); // ellipsis
|
||||
smartquotes.replacements.push([/\(c\)/gi, '\u00A9']); // copyright
|
||||
smartquotes.replacements.push([/\(r\)/gi, '\u00AE']); // registered trademark
|
||||
smartquotes.replacements.push([/\?!/g, '\u2048']); // ?!
|
||||
smartquotes.replacements.push([/!!/g, '\u203C']); // !!
|
||||
smartquotes.replacements.push([/\?\?/g, '\u2047']); // ??
|
||||
smartquotes.replacements.push([/([0-9]\s?)-(\s?[0-9])/g, '$1\u2013$2']); // number ranges use en dash
|
||||
|
||||
/**
|
||||
* Improves typography by adding smart quotes and similar corrections within the specified element(s).
|
||||
*
|
||||
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
|
||||
* appropriate DOM manipulations.
|
||||
*/
|
||||
module.exports = function (doc, selector = 'body') {
|
||||
const elements = [...doc.querySelectorAll(selector)];
|
||||
elements.forEach(el => smartquotes.element(el));
|
||||
return doc;
|
||||
};
|
||||
|
Before Width: | Height: | Size: 1.0 MiB |
11
docs/assets/images/awesome.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="733" xmlns="http://www.w3.org/2000/svg" height="733">
|
||||
<circle cy="366.5" cx="366.5" r="366.5"/>
|
||||
<circle cy="366.5" cx="366.5" r="336.5" fill="#fede58"/>
|
||||
<path d="M325 665c-121-21-194-115-212-233v-8l-25-1-1-18h481c6 13 10 27 13 41 13 94-38 146-114 193-45 23-93 29-142 26z"/>
|
||||
<path d="M372 647c52-6 98-28 138-62 28-25 46-56 51-87 4-20 1-57-5-70l-423-1c-2 56 39 118 74 157 31 34 72 54 116 63 11 2 38 2 49 0z" fill="#871945"/>
|
||||
<path d="M76 342c-13-26-13-57-9-85 6-27 18-52 35-68 21-20 50-23 77-18 15 4 28 12 39 23 18 17 30 40 36 67 4 20 4 41 0 60l-6 21z"/>
|
||||
<path d="M234 323c5-6 6-40 2-58-3-16-4-16-10-10-14 14-38 14-52 0-15-18-12-41 6-55 3-3 5-5 5-6-1-4-22-8-34-7-42 4-57.6 40-66.2 77-3 17-1 53 4 59H234z" fill="#fff"/>
|
||||
<path d="M378 343c-2-3-6-20-7-29-5-28-1-57 11-83 15-30 41-52 72-60 29-7 57 0 82 15 26 17 45 49 50 82 2 12 2 33 0 45-1 10-5 26-8 30z"/>
|
||||
<path d="M565 324c4-5 5-34 4-50-2-14-6-24-8-24-1 0-3 2-6 5-17 17-47 13-58-9-7-16-4-31 8-43 4-4 7-8 7-9 0 0-4-2-8-3-51-17-105 20-115 80-3 15 0 43 3 53z" fill="#fff"/>
|
||||
<path d="M504 590s-46 40-105 53c-66 15-114-7-114-7s14-76 93-95c76-18 126 49 126 49z" fill="#f9bedd"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
docs/assets/images/favicon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="186" height="186" viewBox="0 0 186 186"><g fill="none" fill-rule="evenodd"><rect width="186" height="186" fill="#103257" opacity="0"/><path fill="#F6894C" d="M106.95,13.9672306 C106.95,19.1752428 104.10296,23.7175103 99.8823543,26.1190227 L130.2,48.8851296 L159.91784,39.4892184 C158.760743,37.4541707 158.1,35.0993755 158.1,32.5902046 C158.1,24.8763205 164.345703,18.6229741 172.05,18.6229741 C179.754297,18.6229741 186,24.8763205 186,32.5902046 C186,40.3040179 179.754297,46.5574352 172.05,46.5574352 C171.315566,46.5574352 170.594594,46.5006795 169.890983,46.39107 L137.151086,130.163238 C134.361086,137.302399 127.486526,142 119.830057,142 L66.1699429,142 C58.5134743,142 51.6389143,137.302399 48.8489143,130.163238 L16.1089463,46.39107 C15.4052994,46.5006795 14.6842926,46.5574352 13.95,46.5574352 C6.245632,46.5574352 0,40.3040179 0,32.5902046 C0,24.8763205 6.245632,18.6229741 13.95,18.6229741 C21.654368,18.6229741 27.9,24.8763205 27.9,32.5902046 C27.9,35.0993755 27.2391509,37.4541707 26.0822663,39.4892184 L55.8,48.8851296 L86.1176457,26.1190227 C81.89704,23.7175103 79.05,19.1752428 79.05,13.9672306 C79.05,6.25334639 85.2957029,0 93,0 C100.704297,0 106.95,6.25334639 106.95,13.9672306 Z" transform="translate(0 22)"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 8.3 KiB |
32
docs/assets/images/undraw-taken.svg
Normal file
@@ -0,0 +1,32 @@
|
||||
<svg width="673" height="739" viewBox="0 0 673 739" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill-rule="nonzero" fill="none">
|
||||
<path d="M467 149.805c-46.62-7.44-99.71-11.41-155-11.41-50.6 0-99.35 3.32-142.98 9.58.01-.67.02-1.34.05-2.01C171.475 65.082 237.996.903 318.914 1.398c80.917.494 146.649 65.48 148.066 146.387.01.68.02 1.35.02 2.02Z" fill="#0BA5E9"/>
|
||||
<path d="M337.55 1.342a149.047 149.047 0 0 0-168.93 143.229c-.031.67-.04 1.34-.05 2.01 12.961-1.86 26.384-3.454 40.164-4.784 3.478-71.745 57.64-130.8 128.816-140.455Z" opacity=".1" fill="#FFF"/>
|
||||
<path d="M532.18 161.625a600.121 600.121 0 0 0-65.2-13.84 943.364 943.364 0 0 0-108.74-10.45 1133.608 1133.608 0 0 0-83.01-.34 973.29 973.29 0 0 0-106.16 8.97 624.292 624.292 0 0 0-77.25 15.66C32.61 177.995 0 199.935 0 223.395s32.61 45.4 91.82 61.77c41.64 11.52 92.98 19.37 148.92 22.97 23.09 1.5 46.96 2.26 71.26 2.26 24.38 0 48.33-.77 71.49-2.27 50.91-3.29 98.01-10.1 137.43-20 .21-.06.41-.11.62-.16 2.66-.66 5.28-1.35 7.87-2.04.93-.26 1.85-.51 2.77-.76a.978.978 0 0 1 .16-.05c.88-.24 1.75-.49 2.62-.73 1.74-.5 3.46-.99 5.15-1.5.08-.02.15-.04.22-.06 1.47-.44 2.91-.88 4.34-1.32 1.17-.37 2.33-.73 3.48-1.1.84-.27 1.67-.54 2.49-.81.6-.2 1.19-.39 1.77-.59.79-.26 1.58-.53 2.36-.8.33-.11.66-.22.98-.34.75-.25 1.48-.51 2.21-.77.79-.28 1.58-.57 2.36-.85.65-.23 1.3-.47 1.94-.71.54-.21 1.07-.41 1.61-.61 1.47-.55 2.91-1.12 4.33-1.68.71-.29 1.42-.57 2.12-.86.69-.28 1.39-.57 2.07-.86 1.12-.47 2.22-.94 3.3-1.41.52-.24 1.05-.47 1.56-.69.39-.18.77-.35 1.16-.53.28-.12.56-.25.83-.38 1.01-.46 2.01-.93 2.99-1.4 3.76-1.8 7.27-3.64 10.53-5.52 20.45-11.71 31.24-24.7 31.24-38.2 0-23.46-32.61-45.4-91.82-61.77Zm-.54 121.62c-41.69 11.53-93.17 19.38-149.26 22.95-22.81 1.45-46.39 2.2-70.38 2.2-23.91 0-47.41-.74-70.15-2.19-56.18-3.56-107.74-11.41-149.49-22.96C34.09 267.125 2 245.875 2 223.395c0-1.986.252-3.965.74-5.89 5.1-20.28 36.47-39.26 89.62-53.96a623.806 623.806 0 0 1 76.66-15.57 976.027 976.027 0 0 1 106.8-9c11.92-.39 23.98-.583 36.18-.58 15.41 0 30.65.31 45.63.91a941.367 941.367 0 0 1 109.37 10.5 598.858 598.858 0 0 1 64.64 13.74c53.14 14.7 84.5 33.67 89.61 53.94.496 1.93.75 3.916.75 5.91 0 22.48-32.09 43.73-90.36 59.85Z" fill="#3F3D56"/>
|
||||
<path d="M623.43 224.305c0 13.36-11.01 26-30.67 37.29-3.27 1.88-6.79 3.72-10.53 5.52-.98.47-1.98.94-2.99 1.4-.27.13-.55.26-.83.38-.39.18-.77.35-1.16.53-.51.22-1.04.45-1.56.69-1.08.47-2.18.94-3.3 1.41-.68.29-1.38.58-2.07.86-.7.29-1.41.57-2.12.86-1.42.56-2.86 1.13-4.33 1.68-.54.2-1.07.4-1.61.61-.64.24-1.29.48-1.94.71-.78.28-1.57.57-2.36.85-.73.26-1.46.52-2.21.77-.32.12-.65.23-.98.34-.78.27-1.57.54-2.36.8-.58.2-1.17.39-1.77.59-.82.27-1.65.54-2.49.81-1.15.37-2.31.73-3.48 1.1-1.43.44-2.87.88-4.34 1.32-.07.02-.14.04-.22.06-1.69.51-3.41 1-5.15 1.5-.87.24-1.74.49-2.62.73a.978.978 0 0 0-.16.05c-.92.25-1.84.5-2.77.76-2.58.68-5.21 1.37-7.87 2.04-.21.05-.41.1-.62.16-38.35 9.58-85.4 16.56-137.47 19.93-22.81 1.47-46.59 2.25-71.02 2.25-24.65 0-48.63-.79-71.62-2.29-137.24-8.95-239.38-43.03-239.38-83.71.01-2.475.388-4.936 1.12-7.3.06.17.12.33.19.5 14.27 37.48 115.54 67.77 246.94 75.16 20.13 1.14 40.98 1.73 62.32 1.73 21.43 0 42.36-.6 62.57-1.74 131.29-7.42 232.46-37.72 246.68-75.17.24-.6.45-1.2.63-1.8a25.304 25.304 0 0 1 1.55 8.62ZM91.67 213.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217Z" fill="#3F3D56"/>
|
||||
<path d="M162.67 260.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217ZM531.67 213.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217ZM460.67 260.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217ZM311.67 282.54c-16.643 0-34.331-3.58-34.331-10.216 0-6.636 17.688-10.217 34.33-10.217 16.643 0 34.331 3.58 34.331 10.217 0 6.636-17.688 10.217-34.33 10.217Zm0-18.433c-19.054 0-32.331 4.33-32.331 8.217 0 3.886 13.277 8.217 32.33 8.217 19.053 0 32.331-4.33 32.331-8.217 0-3.886-13.278-8.217-32.33-8.217Z" fill="#3F3D56"/>
|
||||
<circle fill="#2F2E41" cx="336.978" cy="450.705" r="42.012"/>
|
||||
<path fill="#2F2E41" d="m300.555 488.547 20.447-10.24 5.715 11.413-20.447 10.24z"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-56.601 300.086 492.946)" cx="300.086" cy="492.946" rx="3.989" ry="10.636"/>
|
||||
<path fill="#2F2E41" d="m347.239 489.72 5.715-11.412 20.447 10.24-5.715 11.412z"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-33.399 373.87 492.946)" cx="373.87" cy="492.946" rx="10.636" ry="3.989"/>
|
||||
<circle fill="#FFF" cx="334.037" cy="440.429" r="14.359"/>
|
||||
<ellipse fill="#3F3D56" transform="rotate(-45 334.135 434.282)" cx="334.135" cy="434.282" rx="4.766" ry="4.8"/>
|
||||
<path d="M370.12 405c.632-15.553-12.773-28.727-29.941-29.425-17.168-.697-31.597 11.346-32.229 26.9-.632 15.553 11.302 19.087 28.47 19.785 17.167.697 33.068-1.706 33.7-17.26Z" fill="#0BA5E9"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-40.645 380.654 456.766)" cx="380.654" cy="456.766" rx="6.594" ry="21.006"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-49.355 293.42 456.766)" cx="293.419" cy="456.766" rx="21.006" ry="6.594"/>
|
||||
<path d="M348.517 467.262a9.572 9.572 0 1 1-18.836 3.428l-.003-.018c-.942-5.202 3.08-7.043 8.282-7.985 5.203-.942 9.615-.628 10.557 4.575Z" fill="#FFF"/>
|
||||
<path d="M266 495.395a2 2 0 0 1-2-2v-118a2 2 0 0 1 4 0v118a2 2 0 0 1-2 2ZM236 601.395a2 2 0 0 1-2-2v-86a2 2 0 0 1 4 0v86a2 2 0 0 1-2 2ZM313 530.395a2 2 0 0 1-2-2v-118a2 2 0 0 1 4 0v118a2 2 0 0 1-2 2ZM284 615.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM325 369.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM225 390.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM399 395.395a2 2 0 0 1-2-2v-48a2 2 0 0 1 4 0v48a2 2 0 0 1-2 2ZM395 545.395a2 2 0 0 1-2-2v-58a2 2 0 0 1 4 0v58a2 2 0 0 1-2 2ZM355 596.395a2 2 0 0 1-2-2v-86a2 2 0 0 1 4 0v86a2 2 0 0 1-2 2ZM363 449.395a2 2 0 0 1-2-2v-118a2 2 0 0 1 4 0v118a2 2 0 0 1-2 2Z" fill="#CCC"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-39.938 594.37 683.981)" cx="594.369" cy="683.981" rx="6.76" ry="21.534"/>
|
||||
<circle fill="#2F2E41" transform="rotate(-71.565 548.562 676.503)" cx="548.562" cy="676.503" r="43.067"/>
|
||||
<path fill="#2F2E41" d="M553.707 710.303h13.084v23.442h-13.084zM527.54 710.303h13.084v23.442H527.54z"/>
|
||||
<ellipse fill="#2F2E41" cx="555.888" cy="734.017" rx="10.903" ry="4.089"/>
|
||||
<ellipse fill="#2F2E41" cx="529.72" cy="733.472" rx="10.903" ry="4.089"/>
|
||||
<path d="M535.04 622.366c3.845-15.487 20.82-24.6 37.914-20.356 17.094 4.245 27.834 20.24 23.989 35.727-3.846 15.487-16.604 15.537-33.698 11.293-17.094-4.245-32.051-11.177-28.206-26.664Z" fill="#0BA5E9"/>
|
||||
<ellipse fill="#2F2E41" transform="rotate(-64.626 500.054 656.52)" cx="500.054" cy="656.52" rx="6.76" ry="21.534"/>
|
||||
<circle fill="#FFF" cx="542.124" cy="667.416" r="14.359"/>
|
||||
<circle fill="#3F3D56" cx="536.222" cy="662.269" r="4.786"/>
|
||||
<circle fill="#FFF" cx="542" cy="697.395" r="6"/>
|
||||
<path d="M671.531 738.395h-236a1 1 0 1 1 0-2h236a1 1 0 0 1 0 2Z" fill="#3F3D56"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
@@ -1,225 +0,0 @@
|
||||
.code-block {
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.code-block__preview {
|
||||
position: relative;
|
||||
border: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom: none;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
min-width: 20rem;
|
||||
max-width: 100%;
|
||||
padding: 1.5rem 3.25rem 1.5rem 1.5rem;
|
||||
}
|
||||
|
||||
/* Block the preview while dragging to prevent iframes from intercepting drag events */
|
||||
.code-block__preview--dragging:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.code-block__resizer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 1.75rem;
|
||||
font-size: 20px;
|
||||
color: var(--sl-color-neutral-600);
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
border-left: solid 1px var(--sl-color-neutral-200);
|
||||
border-top-right-radius: 3px;
|
||||
cursor: ew-resize;
|
||||
transition: 250ms background-color;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.code-block__preview {
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
.code-block__resizer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.code-block__source {
|
||||
border: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom: none;
|
||||
border-radius: 0 !important;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.code-block--expanded .code-block__source {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.code-block__source pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.code-block__buttons {
|
||||
position: relative;
|
||||
border: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.code-block__button {
|
||||
flex: 0 0 auto;
|
||||
height: 2.5rem;
|
||||
min-width: 2.5rem;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: var(--sl-color-neutral-0);
|
||||
font: inherit;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
color: var(--sl-color-neutral-600);
|
||||
padding: 0 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.code-block__button:not(:last-of-type) {
|
||||
border-right: solid 1px var(--sl-color-neutral-200);
|
||||
}
|
||||
|
||||
.code-block__button--html,
|
||||
.code-block__button--react {
|
||||
width: 70px;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.code-block__button--selected {
|
||||
font-weight: 700;
|
||||
color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.code-block__button--codepen {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
width: 6rem;
|
||||
}
|
||||
|
||||
.code-block__button:first-of-type {
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
|
||||
.code-block__button:last-of-type {
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
.code-block__button:hover,
|
||||
.code-block__button:active {
|
||||
box-shadow: 0 0 0 1px var(--sl-color-primary-400);
|
||||
border-right-color: transparent;
|
||||
background-color: var(--sl-color-primary-50);
|
||||
color: var(--sl-color-primary-600);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.code-block__button:focus-visible {
|
||||
outline: none;
|
||||
outline: var(--sl-focus-ring);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.code-block__toggle {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
color: var(--sl-color-neutral-600);
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.code-block__toggle svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.code-block--expanded .code-block__toggle svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* Copy button styles */
|
||||
.markdown-section .docsify-copy-code-button {
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
background-color: var(--sl-color-neutral-500);
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
font-family: var(--sl-font-sans);
|
||||
font-size: var(--sl-font-size-x-small);
|
||||
font-weight: var(--sl-font-weight-semibold);
|
||||
text-transform: uppercase;
|
||||
padding: 8px;
|
||||
user-select: none;
|
||||
transition: 0.1s all;
|
||||
}
|
||||
|
||||
.markdown-section .docsify-copy-code-button.copied {
|
||||
animation: pulse 0.75s;
|
||||
--pulse-color: var(--sl-color-neutral-600);
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 var(--pulse-color);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 0.5rem transparent;
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-section .docsify-copy-code-button .label {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.markdown-section .docsify-copy-code-button .success,
|
||||
.markdown-section .docsify-copy-code-button .error {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.markdown-section .docsify-copy-code-button:focus-visible {
|
||||
outline: var(--sl-focus-ring);
|
||||
outline-offset: var(--sl-focus-ring-offset);
|
||||
}
|
||||
|
||||
.markdown-section .docsify-copy-code-button:active {
|
||||
background-color: var(--sl-color-neutral-600);
|
||||
transform: scale(0.92);
|
||||
}
|
||||
|
||||
/* We can apply data-flavor="html|react" to any element on the page to toggle it when the user's flavor changes */
|
||||
body.flavor-html [data-flavor]:not([data-flavor='html']) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.flavor-react [data-flavor]:not([data-flavor='react']) {
|
||||
display: none;
|
||||
}
|
||||
@@ -1,374 +0,0 @@
|
||||
/* global Prism */
|
||||
|
||||
(() => {
|
||||
const reactVersion = '17.0.2';
|
||||
let flavor = getFlavor();
|
||||
let count = 1;
|
||||
|
||||
// Sync flavor UI on page load
|
||||
setFlavor(getFlavor());
|
||||
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
function convertModuleLinks(html) {
|
||||
const version = sessionStorage.getItem('sl-version');
|
||||
|
||||
html = html
|
||||
.replace(/@shoelace-style\/shoelace/g, `https://cdn.skypack.dev/@shoelace-style/shoelace@${version}`)
|
||||
.replace(/from 'react'/g, `from 'https://cdn.skypack.dev/react@${reactVersion}'`)
|
||||
.replace(/from "react"/g, `from "https://cdn.skypack.dev/react@${reactVersion}"`);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function getAdjacentExample(name, pre) {
|
||||
let currentPre = pre.nextElementSibling;
|
||||
|
||||
while (currentPre?.tagName.toLowerCase() === 'pre') {
|
||||
if (currentPre?.getAttribute('data-lang').split(' ').includes(name)) {
|
||||
return currentPre;
|
||||
}
|
||||
|
||||
currentPre = currentPre.nextElementSibling;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function runScript(script) {
|
||||
const newScript = document.createElement('script');
|
||||
|
||||
if (script.type === 'module') {
|
||||
newScript.type = 'module';
|
||||
newScript.textContent = script.innerHTML;
|
||||
} else {
|
||||
newScript.appendChild(document.createTextNode(`(() => { ${script.innerHTML} })();`));
|
||||
}
|
||||
|
||||
script.parentNode.replaceChild(newScript, script);
|
||||
}
|
||||
|
||||
function getFlavor() {
|
||||
return localStorage.getItem('flavor') || 'html';
|
||||
}
|
||||
|
||||
function setFlavor(newFlavor) {
|
||||
flavor = ['html', 'react'].includes(newFlavor) ? newFlavor : 'html';
|
||||
localStorage.setItem('flavor', flavor);
|
||||
|
||||
// Set the flavor class on the body
|
||||
document.body.classList.toggle('flavor-html', flavor === 'html');
|
||||
document.body.classList.toggle('flavor-react', flavor === 'react');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push(hook => {
|
||||
// Convert code blocks to previews
|
||||
hook.afterEach((html, next) => {
|
||||
const domParser = new DOMParser();
|
||||
const doc = domParser.parseFromString(html, 'text/html');
|
||||
|
||||
const htmlButton = `
|
||||
<button
|
||||
type="button"
|
||||
title="Show HTML code"
|
||||
class="code-block__button code-block__button--html ${flavor === 'html' ? 'code-block__button--selected' : ''}"
|
||||
>
|
||||
HTML
|
||||
</button>
|
||||
`;
|
||||
|
||||
const reactButton = `
|
||||
<button
|
||||
type="button"
|
||||
title="Show React code"
|
||||
class="code-block__button code-block__button--react ${
|
||||
flavor === 'react' ? 'code-block__button--selected' : ''
|
||||
}"
|
||||
>
|
||||
React
|
||||
</button>
|
||||
`;
|
||||
|
||||
const codePenButton = `
|
||||
<button type="button" class="code-block__button code-block__button--codepen" title="Edit on CodePen">
|
||||
<svg
|
||||
width="138"
|
||||
height="26"
|
||||
viewBox="0 0 138 26"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M80 6h-9v14h9 M114 6h-9 v14h9 M111 13h-6 M77 13h-6 M122 20V6l11 14V6 M22 16.7L33 24l11-7.3V9.3L33 2L22 9.3V16.7z M44 16.7L33 9.3l-11 7.4 M22 9.3l11 7.3 l11-7.3 M33 2v7.3 M33 16.7V24 M88 14h6c2.2 0 4-1.8 4-4s-1.8-4-4-4h-6v14 M15 8c-1.3-1.3-3-2-5-2c-4 0-7 3-7 7s3 7 7 7 c2 0 3.7-0.8 5-2 M64 13c0 4-3 7-7 7h-5V6h5C61 6 64 9 64 13z" />
|
||||
</svg>
|
||||
</button>
|
||||
`;
|
||||
|
||||
[...doc.querySelectorAll('code[class^="lang-"]')].forEach(code => {
|
||||
if (code.classList.contains('preview')) {
|
||||
const isExpanded = code.classList.contains('expanded');
|
||||
const pre = code.closest('pre');
|
||||
const sourceGroupId = `code-block-source-group-${count}`;
|
||||
const toggleId = `code-block-toggle-${count}`;
|
||||
const reactPre = getAdjacentExample('react', pre);
|
||||
const hasReact = reactPre !== null;
|
||||
|
||||
pre.setAttribute('data-lang', pre.getAttribute('data-lang').replace(/ preview$/, ''));
|
||||
pre.setAttribute('aria-labelledby', toggleId);
|
||||
|
||||
const codeBlock = `
|
||||
<div class="code-block ${isExpanded ? 'code-block--expanded' : ''}">
|
||||
<div class="code-block__preview">
|
||||
${code.textContent}
|
||||
<div class="code-block__resizer">
|
||||
<sl-icon name="grip-vertical"></sl-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="code-block__source-group" id="${sourceGroupId}">
|
||||
<div class="code-block__source code-block__source--html" ${hasReact ? 'data-flavor="html"' : ''}>
|
||||
${pre.outerHTML}
|
||||
</div>
|
||||
|
||||
${
|
||||
hasReact
|
||||
? `
|
||||
<div class="code-block__source code-block__source--react" data-flavor="react">
|
||||
${reactPre.outerHTML}
|
||||
</div>
|
||||
`
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="code-block__buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="code-block__button code-block__toggle"
|
||||
aria-expanded="${isExpanded ? 'true' : 'false'}"
|
||||
aria-controls="${sourceGroupId}"
|
||||
>
|
||||
Source
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
${hasReact ? ` ${htmlButton} ${reactButton} ` : ''}
|
||||
|
||||
${!code.classList.contains('no-codepen') ? codePenButton : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
pre.replaceWith(domParser.parseFromString(codeBlock, 'text/html').body);
|
||||
reactPre?.remove();
|
||||
|
||||
count++;
|
||||
}
|
||||
});
|
||||
|
||||
// Force the highlighter to run again so JSX fields get highlighted properly
|
||||
requestAnimationFrame(() => Prism.highlightAll());
|
||||
|
||||
next(doc.body.innerHTML);
|
||||
});
|
||||
|
||||
// After the page is done loading, force scripts in previews to execute
|
||||
hook.doneEach(() => {
|
||||
[...document.querySelectorAll('.code-block__preview script')].map(script => runScript(script));
|
||||
});
|
||||
|
||||
// Horizontal resizing
|
||||
hook.doneEach(() => {
|
||||
[...document.querySelectorAll('.code-block__preview')].forEach(preview => {
|
||||
const resizer = preview.querySelector('.code-block__resizer');
|
||||
let startX;
|
||||
let startWidth;
|
||||
|
||||
function dragStart(event) {
|
||||
startX = event.changedTouches ? event.changedTouches[0].pageX : event.clientX;
|
||||
startWidth = parseInt(document.defaultView.getComputedStyle(preview).width, 10);
|
||||
preview.classList.add('code-block__preview--dragging');
|
||||
event.preventDefault();
|
||||
document.documentElement.addEventListener('mousemove', dragMove);
|
||||
document.documentElement.addEventListener('touchmove', dragMove);
|
||||
document.documentElement.addEventListener('mouseup', dragStop);
|
||||
document.documentElement.addEventListener('touchend', dragStop);
|
||||
}
|
||||
|
||||
function dragMove(event) {
|
||||
setWidth(startWidth + (event.changedTouches ? event.changedTouches[0].pageX : event.pageX) - startX);
|
||||
}
|
||||
|
||||
function dragStop() {
|
||||
preview.classList.remove('code-block__preview--dragging');
|
||||
document.documentElement.removeEventListener('mousemove', dragMove);
|
||||
document.documentElement.removeEventListener('touchmove', dragMove);
|
||||
document.documentElement.removeEventListener('mouseup', dragStop);
|
||||
document.documentElement.removeEventListener('touchend', dragStop);
|
||||
}
|
||||
|
||||
function setWidth(width) {
|
||||
preview.style.width = `${width}px`;
|
||||
}
|
||||
|
||||
resizer.addEventListener('mousedown', dragStart);
|
||||
resizer.addEventListener('touchstart', dragStart, { passive: true });
|
||||
}, false);
|
||||
});
|
||||
});
|
||||
|
||||
// Toggle source mode
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('.code-block__button');
|
||||
const codeBlock = button?.closest('.code-block');
|
||||
|
||||
if (button?.classList.contains('code-block__button--html')) {
|
||||
// Show HTML
|
||||
setFlavor('html');
|
||||
toggleSource(codeBlock, true);
|
||||
} else if (button?.classList.contains('code-block__button--react')) {
|
||||
// Show React
|
||||
setFlavor('react');
|
||||
toggleSource(codeBlock, true);
|
||||
} else if (button?.classList.contains('code-block__toggle')) {
|
||||
// Toggle source
|
||||
toggleSource(codeBlock);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update flavor buttons
|
||||
[...document.querySelectorAll('.code-block')].forEach(cb => {
|
||||
cb.querySelector('.code-block__button--html')?.classList.toggle(
|
||||
'code-block__button--selected',
|
||||
flavor === 'html'
|
||||
);
|
||||
cb.querySelector('.code-block__button--react')?.classList.toggle(
|
||||
'code-block__button--selected',
|
||||
flavor === 'react'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function toggleSource(codeBlock, force) {
|
||||
const toggle = codeBlock.querySelector('.code-block__toggle');
|
||||
|
||||
if (toggle) {
|
||||
codeBlock.classList.toggle('code-block--expanded', force === undefined ? undefined : force);
|
||||
event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-block--expanded'));
|
||||
}
|
||||
}
|
||||
|
||||
// Show pulse when copying
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('.docsify-copy-code-button');
|
||||
if (button) {
|
||||
button.classList.remove('copied');
|
||||
requestAnimationFrame(() => {
|
||||
button.addEventListener('animationend', () => button.classList.remove('copied'), { once: true });
|
||||
button.classList.add('copied');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Open in CodePen
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('button');
|
||||
const version = sessionStorage.getItem('sl-version');
|
||||
|
||||
if (button?.classList.contains('code-block__button--codepen')) {
|
||||
const codeBlock = button.closest('.code-block');
|
||||
const htmlExample = codeBlock.querySelector('.code-block__source--html > pre > code')?.textContent;
|
||||
const reactExample = codeBlock.querySelector('.code-block__source--react > pre > code')?.textContent;
|
||||
const isReact = flavor === 'react' && typeof reactExample === 'string';
|
||||
const theme = localStorage.getItem('theme');
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const isDark = theme === 'dark' || (theme === 'auto' && prefersDark);
|
||||
const editors = isReact ? '0010' : '1000';
|
||||
let htmlTemplate = '';
|
||||
let jsTemplate = '';
|
||||
let cssTemplate = '';
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.action = 'https://codepen.io/pen/define';
|
||||
form.method = 'POST';
|
||||
form.target = '_blank';
|
||||
|
||||
// HTML templates
|
||||
if (!isReact) {
|
||||
htmlTemplate =
|
||||
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/shoelace.js"></script>\n` +
|
||||
`\n${htmlExample}`;
|
||||
jsTemplate = '';
|
||||
}
|
||||
|
||||
// React templates
|
||||
if (isReact) {
|
||||
htmlTemplate = '<div id="root"></div>';
|
||||
jsTemplate =
|
||||
`import React from 'https://cdn.skypack.dev/react@${reactVersion}';\n` +
|
||||
`import ReactDOM from 'https://cdn.skypack.dev/react-dom@${reactVersion}';\n` +
|
||||
`import { setBasePath } from 'https://cdn.skypack.dev/@shoelace-style/shoelace@${version}/dist/utilities/base-path';\n` +
|
||||
`\n` +
|
||||
`// Set the base path for Shoelace assets\n` +
|
||||
`setBasePath('https://cdn.skypack.dev/@shoelace-style/shoelace@${version}/dist/')\n` +
|
||||
`\n${convertModuleLinks(reactExample)}\n` +
|
||||
`\n` +
|
||||
`ReactDOM.render(<App />, document.getElementById('root'));`;
|
||||
}
|
||||
|
||||
// CSS templates
|
||||
cssTemplate =
|
||||
`@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/themes/${
|
||||
isDark ? 'dark' : 'light'
|
||||
}.css';\n` +
|
||||
'\n' +
|
||||
'body {\n' +
|
||||
' font: 16px sans-serif;\n' +
|
||||
' background-color: var(--sl-color-neutral-0);\n' +
|
||||
' color: var(--sl-color-neutral-900);\n' +
|
||||
' padding: 1rem;\n' +
|
||||
'}';
|
||||
|
||||
// Docs: https://blog.codepen.io/documentation/prefill/
|
||||
const data = {
|
||||
title: '',
|
||||
description: '',
|
||||
tags: ['shoelace', 'web components'],
|
||||
editors,
|
||||
head: `<meta name="viewport" content="width=device-width">`,
|
||||
html_classes: `sl-theme-${isDark ? 'dark' : 'light'}`,
|
||||
css_external: ``,
|
||||
js_external: ``,
|
||||
js_module: true,
|
||||
js_pre_processor: isReact ? 'babel' : 'none',
|
||||
html: htmlTemplate,
|
||||
css: cssTemplate,
|
||||
js: jsTemplate
|
||||
};
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'data';
|
||||
input.value = JSON.stringify(data);
|
||||
form.append(input);
|
||||
|
||||
document.body.append(form);
|
||||
form.submit();
|
||||
form.remove();
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -1,594 +0,0 @@
|
||||
(() => {
|
||||
const isDev = location.hostname === 'localhost';
|
||||
const isNext = location.hostname === 'next.shoelace.style';
|
||||
const customElements = fetch('/dist/custom-elements.json')
|
||||
.then(res => res.json())
|
||||
.catch(err => console.error(err));
|
||||
|
||||
function createPropsTable(props) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Reflects</th>
|
||||
<th>Type</th>
|
||||
<th>Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${props
|
||||
.map(prop => {
|
||||
const hasAttribute = !!prop.attribute;
|
||||
const isAttributeDifferent = prop.attribute !== prop.name;
|
||||
let attributeInfo = '';
|
||||
|
||||
if (!hasAttribute) {
|
||||
attributeInfo = `<br><small>(property only)</small>`;
|
||||
} else if (isAttributeDifferent) {
|
||||
attributeInfo = `
|
||||
<br>
|
||||
<sl-tooltip content="This attribute is different from its property">
|
||||
<small>
|
||||
<code class="nowrap">
|
||||
${escapeHtml(prop.attribute)}
|
||||
</code>
|
||||
</small>
|
||||
</sl-tooltip>`;
|
||||
}
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td>
|
||||
<code class="nowrap">${escapeHtml(prop.name)}</code>
|
||||
${attributeInfo}
|
||||
</td>
|
||||
<td>
|
||||
${escapeHtml(prop.description)}
|
||||
</td>
|
||||
<td style="text-align: center;">${
|
||||
prop.reflects ? '<sl-icon label="yes" name="check-lg"></sl-icon>' : ''
|
||||
}</td>
|
||||
<td>${prop.type?.text ? `<code>${escapeHtml(prop.type?.text || '')}</code>` : '-'}</td>
|
||||
<td>${prop.default ? `<code>${escapeHtml(prop.default)}</code>` : '-'}</td>
|
||||
</tr>
|
||||
`;
|
||||
})
|
||||
.join('')}
|
||||
|
||||
<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;
|
||||
}
|
||||
|
||||
function createEventsTable(events) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-flavor="html">Name</th>
|
||||
<th data-flavor="react">React Event</th>
|
||||
<th>Description</th>
|
||||
<th>Event Detail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${events
|
||||
.map(
|
||||
event => `
|
||||
<tr>
|
||||
<td data-flavor="html"><code class="nowrap">${escapeHtml(event.name)}</code></td>
|
||||
<td data-flavor="react"><code class="nowrap">${escapeHtml(event.reactName)}</code></td>
|
||||
<td>${escapeHtml(event.description)}</td>
|
||||
<td>${event.type?.text ? `<code>${escapeHtml(event.type?.text)}` : '-'}</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</tbody>
|
||||
`;
|
||||
|
||||
return table.outerHTML;
|
||||
}
|
||||
|
||||
function createMethodsTable(methods) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Arguments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${methods
|
||||
.map(
|
||||
method => `
|
||||
<tr>
|
||||
<td class="nowrap"><code>${escapeHtml(method.name)}()</code></td>
|
||||
<td>${escapeHtml(method.description)}</td>
|
||||
<td>
|
||||
${
|
||||
method.parameters?.length
|
||||
? `
|
||||
<code>${escapeHtml(
|
||||
method.parameters.map(param => `${param.name}: ${param.type?.text || ''}`).join(', ')
|
||||
)}</code>
|
||||
`
|
||||
: '-'
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</tbody>
|
||||
`;
|
||||
|
||||
return table.outerHTML;
|
||||
}
|
||||
|
||||
function createSlotsTable(slots) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${slots
|
||||
.map(
|
||||
slot => `
|
||||
<tr>
|
||||
<td class="nowrap">${slot.name ? `<code>${escapeHtml(slot.name)}</code>` : '(default)'}</td>
|
||||
<td>${escapeHtml(slot.description)}</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</tbody>
|
||||
`;
|
||||
|
||||
return table.outerHTML;
|
||||
}
|
||||
|
||||
function createCustomPropertiesTable(styles) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${styles
|
||||
.map(
|
||||
style => `
|
||||
<tr>
|
||||
<td class="nowrap"><code>${escapeHtml(style.name)}</code></td>
|
||||
<td>${escapeHtml(style.description)}</td>
|
||||
<td>${style.default ? `<code>${escapeHtml(style.default)}</code>` : ''}</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</tbody>
|
||||
`;
|
||||
|
||||
return table.outerHTML;
|
||||
}
|
||||
|
||||
function createPartsTable(parts) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${parts
|
||||
.map(
|
||||
part => `
|
||||
<tr>
|
||||
<td class="nowrap"><code>${escapeHtml(part.name)}</code></td>
|
||||
<td>${escapeHtml(part.description)}</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</tbody>
|
||||
`;
|
||||
|
||||
return table.outerHTML;
|
||||
}
|
||||
|
||||
function createAnimationsTable(animations) {
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('metadata-table');
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${animations
|
||||
.map(
|
||||
animation => `
|
||||
<tr>
|
||||
<td class="nowrap"><code>${escapeHtml(animation.name)}</code></td>
|
||||
<td>${escapeHtml(animation.description)}</td>
|
||||
</tr>
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</tbody>
|
||||
`;
|
||||
|
||||
return table.outerHTML;
|
||||
}
|
||||
|
||||
function createDependenciesList(targetComponent, allComponents) {
|
||||
const ul = document.createElement('ul');
|
||||
const dependencies = [];
|
||||
|
||||
// Recursively fetch sub-dependencies
|
||||
function getDependencies(tag) {
|
||||
const component = allComponents.find(c => c.tagName === tag);
|
||||
if (!component || !Array.isArray(component.dependencies)) {
|
||||
return;
|
||||
}
|
||||
|
||||
component.dependencies?.forEach(dependentTag => {
|
||||
if (!dependencies.includes(dependentTag)) {
|
||||
dependencies.push(dependentTag);
|
||||
}
|
||||
getDependencies(dependentTag);
|
||||
});
|
||||
}
|
||||
|
||||
getDependencies(targetComponent);
|
||||
dependencies.sort().forEach(tag => {
|
||||
const li = document.createElement('li');
|
||||
li.innerHTML = `<code><${tag}></code>`;
|
||||
ul.appendChild(li);
|
||||
});
|
||||
|
||||
return ul.outerHTML;
|
||||
}
|
||||
|
||||
function escapeHtml(html) {
|
||||
if (!html) {
|
||||
return '';
|
||||
}
|
||||
return html
|
||||
.toString()
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" rel="noopener noreferrer" target="_blank">$1</a>')
|
||||
.replace(/`(.*?)`/g, '<code>$1</code>');
|
||||
}
|
||||
|
||||
function getAllComponents(metadata) {
|
||||
const allComponents = [];
|
||||
metadata.modules?.forEach(module => {
|
||||
module.declarations?.forEach(declaration => {
|
||||
if (declaration.customElement) {
|
||||
// Generate the dist path based on the src path and attach it to the component
|
||||
declaration.path = module.path.replace(/^src\//, 'dist/').replace(/\.ts$/, '.js');
|
||||
|
||||
allComponents.push(declaration);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return allComponents;
|
||||
}
|
||||
|
||||
function getComponent(metadata, tagName) {
|
||||
return getAllComponents(metadata).find(component => component.tagName === tagName);
|
||||
}
|
||||
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push(hook => {
|
||||
hook.mounted(async () => {
|
||||
const metadata = await customElements;
|
||||
const target = document.querySelector('.app-name');
|
||||
|
||||
// Add version
|
||||
const version = document.createElement('div');
|
||||
version.classList.add('sidebar-version');
|
||||
version.textContent = isDev ? 'Development' : isNext ? 'Next' : metadata.package.version;
|
||||
target.appendChild(version);
|
||||
|
||||
// Store version for reuse
|
||||
sessionStorage.setItem('sl-version', metadata.package.version);
|
||||
|
||||
// Add repo buttons
|
||||
const buttons = document.createElement('div');
|
||||
buttons.classList.add('sidebar-buttons');
|
||||
buttons.innerHTML = `
|
||||
<sl-button size="small" class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
|
||||
<sl-icon slot="prefix" name="heart"></sl-icon> Sponsor
|
||||
</sl-button>
|
||||
<sl-button size="small" class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
|
||||
<sl-icon slot="prefix" name="github"></sl-icon> Star
|
||||
</sl-button>
|
||||
<sl-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
|
||||
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
|
||||
</sl-button>
|
||||
`;
|
||||
target.appendChild(buttons);
|
||||
});
|
||||
|
||||
hook.beforeEach(async (content, next) => {
|
||||
const metadata = await customElements;
|
||||
|
||||
// Replace %VERSION% placeholders
|
||||
content = content.replace(/%VERSION%/g, metadata.package.version);
|
||||
|
||||
// Handle [component-header] tags
|
||||
content = content.replace(/\[component-header:([a-z-]+)\]/g, (match, tag) => {
|
||||
const component = getComponent(metadata, tag);
|
||||
let result = '';
|
||||
|
||||
if (!component) {
|
||||
console.error(`Component not found in metadata: ${tag}`);
|
||||
return next(content);
|
||||
}
|
||||
|
||||
let badgeType = 'neutral';
|
||||
if (component.status === 'stable') {
|
||||
badgeType = 'primary';
|
||||
}
|
||||
if (component.status === 'experimental') {
|
||||
badgeType = 'warning';
|
||||
}
|
||||
if (component.status === 'planned') {
|
||||
badgeType = 'neutral';
|
||||
}
|
||||
if (component.status === 'deprecated') {
|
||||
badgeType = 'danger';
|
||||
}
|
||||
|
||||
result += `
|
||||
<div class="component-header">
|
||||
<div class="component-header__tag">
|
||||
<code><${component.tagName}> | ${component.title ?? component.name}</code>
|
||||
</div>
|
||||
|
||||
<div class="component-header__info">
|
||||
<sl-badge variant="neutral" pill>
|
||||
Since ${component.since || '?'}
|
||||
</sl-badge>
|
||||
|
||||
<sl-badge variant="${badgeType}" pill style="text-transform: capitalize;">
|
||||
${component.status}
|
||||
</sl-badge>
|
||||
</div>
|
||||
|
||||
<div class="component-header__summary">
|
||||
${component.summary ? `<p>${marked(component.summary)}</p>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return result.replace(/^ +| +$/gm, '');
|
||||
});
|
||||
|
||||
// Handle [component-metadata] tags
|
||||
content = content.replace(/\[component-metadata:([a-z-]+)\]/g, (match, tag) => {
|
||||
const component = getComponent(metadata, tag);
|
||||
let result = '';
|
||||
|
||||
if (!component) {
|
||||
console.error(`Component not found in metadata: ${tag}`);
|
||||
return next(content);
|
||||
}
|
||||
|
||||
// Remove members that are private or don't have a description
|
||||
const members = component.members?.filter(member => member.description && member.privacy !== 'private');
|
||||
const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private');
|
||||
const props = members?.filter(prop => {
|
||||
// Look for a corresponding attribute
|
||||
const attribute = component.attributes?.find(attr => attr.fieldName === prop.name);
|
||||
if (attribute) {
|
||||
prop.attribute = attribute.name || attribute.fieldName;
|
||||
}
|
||||
|
||||
return prop.kind === 'field' && prop.privacy !== 'private';
|
||||
});
|
||||
|
||||
if (component.path) {
|
||||
/* prettier-ignore */
|
||||
result += `
|
||||
## Importing
|
||||
|
||||
<sl-tab-group>
|
||||
<sl-tab slot="nav" panel="script">Script</sl-tab>
|
||||
<sl-tab slot="nav" panel="import">Import</sl-tab>
|
||||
<sl-tab slot="nav" panel="bundler">Bundler</sl-tab>
|
||||
<sl-tab slot="nav" panel="react">React</sl-tab>
|
||||
|
||||
<sl-tab-panel name="script">\n
|
||||
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) using a script tag:
|
||||
|
||||
\`\`\`html
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/dist/${component.path}"></script>
|
||||
\`\`\`
|
||||
</sl-tab-panel>
|
||||
|
||||
<sl-tab-panel name="import">\n
|
||||
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) using a JavaScript import:
|
||||
|
||||
\`\`\`js
|
||||
import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/dist/${component.path}';
|
||||
\`\`\`
|
||||
</sl-tab-panel>
|
||||
|
||||
<sl-tab-panel name="bundler">\n
|
||||
To import this component using [a bundler](/getting-started/installation#bundling):
|
||||
\`\`\`js
|
||||
import '@shoelace-style/shoelace/dist/${component.path}';
|
||||
\`\`\`
|
||||
</sl-tab-panel>
|
||||
|
||||
<sl-tab-panel name="react">\n
|
||||
To import this component as a [React component](/frameworks/react):
|
||||
\`\`\`js
|
||||
import { ${component.name} } from '@shoelace-style/shoelace/dist/react';
|
||||
\`\`\`
|
||||
</sl-tab-panel>
|
||||
</sl-tab-group>
|
||||
|
||||
<div class="sponsor-callout">
|
||||
<p>
|
||||
Shoelace is designed, developed, and maintained by <a href="https://twitter.com/claviska" target="_blank">Cory LaViska</a>.
|
||||
Please sponsor my open source work on GitHub. Your support will keep this project alive and growing!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<sl-button class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
|
||||
<sl-icon slot="prefix" name="heart"></sl-icon> Sponsor <span class="sponsor-callout__secondary-label">Development</span>
|
||||
</sl-button>
|
||||
|
||||
<sl-button class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
|
||||
<sl-icon slot="prefix" name="github"></sl-icon> Star <span class="sponsor-callout__secondary-label">on GitHub</span>
|
||||
</sl-button>
|
||||
|
||||
<sl-button class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
|
||||
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow <span class="sponsor-callout__secondary-label">on Twitter</span>
|
||||
</sl-button>
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.slots?.length) {
|
||||
result += `
|
||||
## Slots
|
||||
${createSlotsTable(component.slots)}
|
||||
|
||||
_Learn more about [using slots](/getting-started/usage#slots)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (props?.length) {
|
||||
result += `
|
||||
## Attributes & Properties
|
||||
${createPropsTable(props)}
|
||||
|
||||
_Learn more about [attributes and properties](/getting-started/usage#properties)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.events?.length) {
|
||||
result += `
|
||||
## Events
|
||||
${createEventsTable(component.events)}
|
||||
|
||||
_Learn more about [listening to events](/getting-started/usage#events)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (methods?.length) {
|
||||
result += `
|
||||
## Methods
|
||||
|
||||
${createMethodsTable(methods)}
|
||||
|
||||
_Learn more about [calling methods](/getting-started/usage#methods)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.cssProperties?.length) {
|
||||
result += `
|
||||
## CSS Custom Properties
|
||||
${createCustomPropertiesTable(component.cssProperties)}
|
||||
|
||||
_Learn more about [customizing CSS Custom Properties](/getting-started/customizing#custom-properties)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.cssParts?.length) {
|
||||
result += `
|
||||
## CSS Parts
|
||||
${createPartsTable(component.cssParts)}
|
||||
|
||||
_Learn more about [customizing CSS Parts](/getting-started/customizing#component-parts)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.animations?.length) {
|
||||
result += `
|
||||
## Animations
|
||||
${createAnimationsTable(component.animations)}
|
||||
|
||||
_Learn more about [customizing animations](/getting-started/customizing#animations)._
|
||||
`;
|
||||
}
|
||||
|
||||
if (component.dependencies?.length) {
|
||||
result += `
|
||||
## Dependencies
|
||||
|
||||
This component automatically imports the following dependencies.
|
||||
|
||||
${createDependenciesList(component.tagName, getAllComponents(metadata))}
|
||||
`;
|
||||
}
|
||||
|
||||
// Strip whitespace so markdown doesn't process things as code blocks
|
||||
return result.replace(/^ +| +$/gm, '');
|
||||
});
|
||||
|
||||
next(content);
|
||||
});
|
||||
|
||||
// Wrap tables so we can scroll them horizontally when needed
|
||||
hook.doneEach(() => {
|
||||
const content = document.querySelector('.content');
|
||||
const tables = [...content.querySelectorAll('table')];
|
||||
|
||||
tables.forEach(table => {
|
||||
table.outerHTML = `
|
||||
<div class="table-wrapper">
|
||||
${table.outerHTML}
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1,24 +0,0 @@
|
||||
(() => {
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
//
|
||||
// Docsify generates pages dynamically and asynchronously, so when a reload happens, the scroll position can't be
|
||||
// be restored immediately. This plugin waits until Docsify loads the page and then restores it.
|
||||
//
|
||||
window.$docsify.plugins.push(hook => {
|
||||
hook.ready(() => {
|
||||
// Restore
|
||||
const scrollTop = sessionStorage.getItem('bs-scroll');
|
||||
if (scrollTop) {
|
||||
document.documentElement.scrollTop = scrollTop;
|
||||
}
|
||||
|
||||
// Remember
|
||||
document.addEventListener('scroll', () => {
|
||||
sessionStorage.setItem('bs-scroll', document.documentElement.scrollTop);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
1311
docs/assets/plugins/search/lunr.min.js
vendored
@@ -1,192 +0,0 @@
|
||||
body.site-search-visible {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar .search-box {
|
||||
margin: 1.25rem 26px;
|
||||
}
|
||||
|
||||
.sidebar .search-box kbd {
|
||||
margin-top: 2px;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
/* Site search */
|
||||
.site-search {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.site-search[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.site-search__overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--sl-overlay-background-color);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.site-search__panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 460px;
|
||||
max-height: calc(100vh - 20rem);
|
||||
background-color: var(--sl-panel-background-color);
|
||||
border-radius: var(--sl-border-radius-large);
|
||||
box-shadow: var(--sl-shadow-x-large);
|
||||
margin: 10rem auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.site-search__panel {
|
||||
max-width: 100%;
|
||||
max-height: calc(92vh - 120px); /* allow iOS browser chrome */
|
||||
margin: 4vh var(--sl-spacing-medium);
|
||||
}
|
||||
}
|
||||
|
||||
.site-search__input::part(base) {
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: var(--sl-border-radius-large);
|
||||
}
|
||||
|
||||
.site-search__input:focus-within::part(base) {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.site-search__input {
|
||||
--sl-input-height-large: 4rem;
|
||||
}
|
||||
|
||||
.site-search__body {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.site-search--has-results .site-search__body {
|
||||
border-top: solid 1px var(--sl-color-neutral-200);
|
||||
}
|
||||
|
||||
.site-search__results {
|
||||
display: none;
|
||||
line-height: var(--sl-line-height-dense);
|
||||
list-style: none;
|
||||
padding: var(--sl-spacing-x-small) 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.site-search--has-results .site-search__results {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.site-search__results a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
padding: var(--sl-spacing-x-small) var(--sl-spacing-large);
|
||||
}
|
||||
|
||||
.site-search__results li a:hover,
|
||||
.site-search__results li a:hover small {
|
||||
background-color: var(--sl-color-neutral-100);
|
||||
}
|
||||
|
||||
.site-search__results li[aria-selected='true'] a,
|
||||
.site-search__results li[aria-selected='true'] a small,
|
||||
.site-search__results li[aria-selected='true'] a sl-icon {
|
||||
outline: none;
|
||||
color: var(--sl-color-neutral-0);
|
||||
background-color: var(--sl-color-primary-600);
|
||||
}
|
||||
|
||||
.sl-theme-dark .site-search__results li[aria-selected='true'] a,
|
||||
.sl-theme-dark .site-search__results li[aria-selected='true'] a small,
|
||||
.sl-theme-dark .site-search__results li[aria-selected='true'] a sl-icon {
|
||||
background-color: var(--sl-color-primary-400);
|
||||
color: var(--sl-color-neutral-1000);
|
||||
}
|
||||
|
||||
.site-search__results h3 {
|
||||
font-weight: var(--sl-font-weight-semibold);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.site-search__results small {
|
||||
display: block;
|
||||
color: var(--sl-color-neutral-600);
|
||||
}
|
||||
|
||||
.site-search__result {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.site-search__result a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sl-spacing-medium);
|
||||
}
|
||||
|
||||
.site-search__result-icon {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
color: var(--sl-color-neutral-400);
|
||||
font-size: var(--sl-font-size-x-large);
|
||||
}
|
||||
|
||||
.site-search__result-description {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.site-search__empty {
|
||||
display: none;
|
||||
border-top: solid 1px var(--sl-color-neutral-200);
|
||||
text-align: center;
|
||||
padding: var(--sl-spacing-x-large);
|
||||
}
|
||||
|
||||
.site-search--no-results .site-search__empty {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.site-search__footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: var(--sl-spacing-large);
|
||||
border-top: solid 1px var(--sl-color-neutral-200);
|
||||
border-bottom-left-radius: inherit;
|
||||
border-bottom-right-radius: inherit;
|
||||
padding: var(--sl-spacing-medium);
|
||||
}
|
||||
|
||||
.site-search__footer small {
|
||||
color: var(--sl-color-neutral-700);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.site-search__footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
@@ -1,320 +0,0 @@
|
||||
/* global lunr */
|
||||
(() => {
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push(hook => {
|
||||
// Append the search box to the sidebar
|
||||
hook.mounted(() => {
|
||||
const appName = document.querySelector('.sidebar .app-name');
|
||||
const searchBox = document.createElement('div');
|
||||
searchBox.classList.add('search-box');
|
||||
searchBox.innerHTML = `
|
||||
<sl-input type="search" placeholder="Search" pill>
|
||||
<sl-icon slot="prefix" name="search"></sl-icon>
|
||||
<kbd slot="suffix" title="Press / to search">/</kbd>
|
||||
</sl-input>
|
||||
`;
|
||||
const searchBoxInput = searchBox.querySelector('sl-input');
|
||||
|
||||
appName.insertAdjacentElement('afterend', searchBox);
|
||||
|
||||
// Show the search panel when the search is clicked
|
||||
searchBoxInput.addEventListener('mousedown', event => {
|
||||
event.preventDefault();
|
||||
show();
|
||||
});
|
||||
|
||||
// Show the search panel when a key is pressed
|
||||
searchBoxInput.addEventListener('keydown', event => {
|
||||
if (event.key === 'Tab') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass the character that was typed through to the search input
|
||||
if (event.key.length === 1) {
|
||||
event.preventDefault();
|
||||
input.value = event.key;
|
||||
show();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Append the search panel to the body
|
||||
const siteSearch = document.createElement('div');
|
||||
siteSearch.classList.add('site-search');
|
||||
siteSearch.hidden = true;
|
||||
siteSearch.innerHTML = `
|
||||
<div class="site-search__overlay"></div>
|
||||
<div
|
||||
id="site-search-panel"
|
||||
class="site-search__panel"
|
||||
role="combobox"
|
||||
aria-expanded="false"
|
||||
aria-owns="site-search-results"
|
||||
aria-activedescendant=""
|
||||
>
|
||||
<header class="site-search__header">
|
||||
<sl-input
|
||||
class="site-search__input"
|
||||
type="search"
|
||||
placeholder="Search this site"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="site-search-results"
|
||||
size="large"
|
||||
clearable
|
||||
>
|
||||
<sl-icon slot="prefix" name="search"></sl-icon>
|
||||
</sl-input>
|
||||
</header>
|
||||
<div class="site-search__body">
|
||||
<ul
|
||||
id="site-search-results"
|
||||
class="site-search__results"
|
||||
role="listbox"
|
||||
aria-labelledby="site-search-panel"
|
||||
>
|
||||
</ul>
|
||||
<div class="site-search__empty">No results found.</div>
|
||||
</div>
|
||||
<footer class="site-search__footer">
|
||||
<small><kbd>↑</kbd> <kbd>↓</kbd> navigate</small>
|
||||
<small><kbd>↲</kbd> select</small>
|
||||
<small><kbd>esc</kbd> close</small>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
document.body.append(siteSearch);
|
||||
|
||||
const overlay = siteSearch.querySelector('.site-search__overlay');
|
||||
const panel = siteSearch.querySelector('.site-search__panel');
|
||||
const input = siteSearch.querySelector('.site-search__input');
|
||||
const results = siteSearch.querySelector('.site-search__results');
|
||||
const animationDuration = 150;
|
||||
const searchDebounce = 200;
|
||||
let isShowing = false;
|
||||
let searchTimeout;
|
||||
let searchIndex;
|
||||
let map;
|
||||
|
||||
// Load search data
|
||||
fetch('../../../search.json')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
searchIndex = lunr.Index.load(data.searchIndex);
|
||||
map = data.map;
|
||||
});
|
||||
|
||||
async function show() {
|
||||
isShowing = true;
|
||||
document.body.classList.add('site-search-visible');
|
||||
siteSearch.hidden = false;
|
||||
requestAnimationFrame(() => input.focus());
|
||||
updateResults();
|
||||
|
||||
await Promise.all([
|
||||
panel.animate(
|
||||
[
|
||||
{ opacity: 0, transform: 'scale(.9)' },
|
||||
{ opacity: 1, transform: 'scale(1)' }
|
||||
],
|
||||
{ duration: animationDuration }
|
||||
).finished,
|
||||
overlay.animate([{ opacity: 0 }, { opacity: 1 }], { duration: animationDuration }).finished
|
||||
]);
|
||||
|
||||
document.addEventListener('mousedown', handleDocumentMouseDown);
|
||||
document.addEventListener('keydown', handleDocumentKeyDown);
|
||||
document.addEventListener('focusin', handleDocumentFocusIn);
|
||||
}
|
||||
|
||||
async function hide() {
|
||||
isShowing = false;
|
||||
document.body.classList.remove('site-search-visible');
|
||||
|
||||
await Promise.all([
|
||||
panel.animate(
|
||||
[
|
||||
{ opacity: 1, transform: 'scale(1)' },
|
||||
{ opacity: 0, transform: 'scale(.9)' }
|
||||
],
|
||||
{ duration: animationDuration }
|
||||
).finished,
|
||||
overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: animationDuration }).finished
|
||||
]);
|
||||
|
||||
siteSearch.hidden = true;
|
||||
input.value = '';
|
||||
updateResults();
|
||||
|
||||
document.removeEventListener('mousedown', handleDocumentMouseDown);
|
||||
document.removeEventListener('keydown', handleDocumentKeyDown);
|
||||
document.removeEventListener('focusin', handleDocumentFocusIn);
|
||||
}
|
||||
|
||||
function handleInput() {
|
||||
// Debounce search queries
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
|
||||
}
|
||||
|
||||
function handleDocumentFocusIn(event) {
|
||||
// Close when focus leaves the panel
|
||||
if (event.target.closest('.site-search__panel') !== panel) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
function handleDocumentMouseDown(event) {
|
||||
// Close when clicking outside of the panel
|
||||
if (event.target.closest('.site-search__overlay') === overlay) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
function handleDocumentKeyDown(event) {
|
||||
// Close when pressing escape
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle keyboard selections
|
||||
if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter'].includes(event.key)) {
|
||||
event.preventDefault();
|
||||
|
||||
const currentEl = results.querySelector('[aria-selected="true"]');
|
||||
const items = [...results.querySelectorAll('li')];
|
||||
const index = items.indexOf(currentEl);
|
||||
let nextEl;
|
||||
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case 'ArrowUp':
|
||||
nextEl = items[Math.max(0, index - 1)];
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
nextEl = items[Math.min(items.length - 1, index + 1)];
|
||||
break;
|
||||
case 'Home':
|
||||
nextEl = items[0];
|
||||
break;
|
||||
case 'End':
|
||||
nextEl = items[items.length - 1];
|
||||
break;
|
||||
case 'Enter':
|
||||
currentEl?.querySelector('a')?.click();
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the selected item
|
||||
items.forEach(item => {
|
||||
if (item === nextEl) {
|
||||
const a = nextEl.querySelector('a');
|
||||
panel.setAttribute('aria-activedescendant', a.id);
|
||||
|
||||
item.setAttribute('aria-selected', 'true');
|
||||
nextEl.scrollIntoView({ block: 'nearest' });
|
||||
} else {
|
||||
item.setAttribute('aria-selected', 'false');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function updateResults(query = '') {
|
||||
try {
|
||||
await searchIndex;
|
||||
|
||||
const hasQuery = query.length > 0;
|
||||
const searchTokens = query
|
||||
.split(' ')
|
||||
.map((term, index, arr) => `${term}${index === arr.length - 1 ? `* ${term}~1` : '~1'}`)
|
||||
.join(' ');
|
||||
const matches = hasQuery ? searchIndex.search(`${query} ${searchTokens}`) : [];
|
||||
const hasResults = hasQuery && matches.length > 0;
|
||||
siteSearch.classList.toggle('site-search--has-results', hasQuery && hasResults);
|
||||
siteSearch.classList.toggle('site-search--no-results', hasQuery && !hasResults);
|
||||
panel.setAttribute('aria-expanded', hasQuery && hasResults ? 'true' : 'false');
|
||||
|
||||
results.innerHTML = '';
|
||||
|
||||
matches.forEach((match, index) => {
|
||||
const page = map[match.ref];
|
||||
const li = document.createElement('li');
|
||||
const a = document.createElement('a');
|
||||
let icon = 'file-text';
|
||||
|
||||
a.setAttribute('role', 'option');
|
||||
a.setAttribute('id', `search-result-item-${match.ref}`);
|
||||
|
||||
if (page.url.includes('getting-started/')) {
|
||||
icon = 'lightbulb';
|
||||
}
|
||||
if (page.url.includes('resources/')) {
|
||||
icon = 'book';
|
||||
}
|
||||
if (page.url.includes('components/')) {
|
||||
icon = 'puzzle';
|
||||
}
|
||||
if (page.url.includes('tokens/')) {
|
||||
icon = 'palette2';
|
||||
}
|
||||
if (page.url.includes('utilities/')) {
|
||||
icon = 'wrench';
|
||||
}
|
||||
if (page.url.includes('tutorials/')) {
|
||||
icon = 'joystick';
|
||||
}
|
||||
|
||||
a.href = window.$docsify.routerMode === 'hash' ? `/#/${page.url}` : `/${page.url}`;
|
||||
a.innerHTML = `
|
||||
<div class="site-search__result-icon">
|
||||
<sl-icon name="${icon}" aria-hidden="true"></sl-icon>
|
||||
</div>
|
||||
<div class="site-search__result__details">
|
||||
<h3>${page.title}</h3>
|
||||
<small>${page.url}</small>
|
||||
</div>
|
||||
`;
|
||||
|
||||
li.classList.add('site-search__result');
|
||||
li.setAttribute('aria-selected', index === 0 ? 'true' : 'false');
|
||||
li.appendChild(a);
|
||||
results.appendChild(li);
|
||||
});
|
||||
} catch {
|
||||
// Ignore query errors as the user types
|
||||
}
|
||||
}
|
||||
|
||||
// Show the search panel slash is pressed outside of a form element
|
||||
document.addEventListener('keydown', event => {
|
||||
const isSlash = event.key === '/';
|
||||
const isCtrlK = (event.metaKey || event.ctrlKey) && event.key === 'k';
|
||||
|
||||
if (
|
||||
!isShowing &&
|
||||
(isSlash || isCtrlK) &&
|
||||
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
|
||||
) {
|
||||
event.preventDefault();
|
||||
show();
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('sl-input', handleInput);
|
||||
|
||||
// Close when a result is selected
|
||||
results.addEventListener('click', event => {
|
||||
if (event.target.closest('a')) {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1,29 +0,0 @@
|
||||
.theme-picker {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
.theme-picker:not(:defined) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.theme-picker sl-menu-label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.theme-picker sl-menu-label kbd {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.theme-picker {
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
}
|
||||
|
||||
.theme-picker sl-menu-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
(() => {
|
||||
if (!window.$docsify) {
|
||||
throw new Error('Docsify must be loaded before installing this plugin.');
|
||||
}
|
||||
|
||||
window.$docsify.plugins.push(hook => {
|
||||
hook.mounted(() => {
|
||||
function getTheme() {
|
||||
return localStorage.getItem('theme') || 'auto';
|
||||
}
|
||||
|
||||
function isDark() {
|
||||
if (theme === 'auto') {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
return theme === 'dark';
|
||||
}
|
||||
|
||||
function setTheme(newTheme) {
|
||||
const noTransitions = Object.assign(document.createElement('style'), {
|
||||
textContent: '* { transition: none !important; }'
|
||||
});
|
||||
|
||||
theme = newTheme;
|
||||
localStorage.setItem('theme', theme);
|
||||
|
||||
// Update the UI
|
||||
[...menu.querySelectorAll('sl-menu-item')].map(item => (item.checked = item.getAttribute('value') === theme));
|
||||
menuIcon.name = isDark() ? 'moon' : 'sun';
|
||||
|
||||
// Toggle the dark mode class without transitions
|
||||
document.body.appendChild(noTransitions);
|
||||
requestAnimationFrame(() => {
|
||||
document.documentElement.classList.toggle('sl-theme-dark', isDark());
|
||||
requestAnimationFrame(() => document.body.removeChild(noTransitions));
|
||||
});
|
||||
}
|
||||
|
||||
let theme = getTheme();
|
||||
|
||||
// Generate the theme picker dropdown
|
||||
const dropdown = document.createElement('sl-dropdown');
|
||||
dropdown.classList.add('theme-picker');
|
||||
dropdown.innerHTML = `
|
||||
<sl-button size="small" pill slot="trigger" caret>
|
||||
<sl-icon name="sun" label="Select Theme"></sl-icon>
|
||||
</sl-button>
|
||||
<sl-menu>
|
||||
<sl-menu-label>Toggle <kbd>\\</kbd></sl-menu-label>
|
||||
<sl-menu-item type="checkbox" value="light">Light</sl-menu-item>
|
||||
<sl-menu-item type="checkbox" value="dark">Dark</sl-menu-item>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item type="checkbox" value="auto">Auto</sl-menu-item>
|
||||
</sl-menu>
|
||||
`;
|
||||
document.querySelector('.sidebar-toggle').insertAdjacentElement('afterend', dropdown);
|
||||
|
||||
// Listen for selections
|
||||
const menu = dropdown.querySelector('sl-menu');
|
||||
const menuIcon = dropdown.querySelector('sl-icon');
|
||||
menu.addEventListener('sl-select', event => setTheme(event.detail.item.value));
|
||||
|
||||
// Update the theme when the preference changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => setTheme(theme));
|
||||
|
||||
// Toggle themes when pressing backslash
|
||||
document.addEventListener('keydown', event => {
|
||||
if (
|
||||
event.key === '\\' &&
|
||||
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
|
||||
) {
|
||||
event.preventDefault();
|
||||
|
||||
setTheme(isDark() ? 'light' : 'dark');
|
||||
}
|
||||
});
|
||||
|
||||
// Set the initial theme and sync the UI
|
||||
setTheme(theme);
|
||||
});
|
||||
});
|
||||
})();
|
||||
243
docs/assets/scripts/code-previews.js
Normal file
@@ -0,0 +1,243 @@
|
||||
(() => {
|
||||
function convertModuleLinks(html) {
|
||||
html = html
|
||||
.replace(/@shoelace-style\/shoelace/g, `https://esm.sh/@shoelace-style/shoelace@${waVersion}`)
|
||||
.replace(/from 'react'/g, `from 'https://esm.sh/react@${reactVersion}'`)
|
||||
.replace(/from "react"/g, `from "https://esm.sh/react@${reactVersion}"`);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function getAdjacentExample(name, pre) {
|
||||
let currentPre = pre.nextElementSibling;
|
||||
|
||||
while (currentPre?.tagName.toLowerCase() === 'pre') {
|
||||
if (currentPre?.getAttribute('data-lang').split(' ').includes(name)) {
|
||||
return currentPre;
|
||||
}
|
||||
|
||||
currentPre = currentPre.nextElementSibling;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function runScript(script) {
|
||||
const newScript = document.createElement('script');
|
||||
|
||||
if (script.type === 'module') {
|
||||
newScript.type = 'module';
|
||||
newScript.textContent = script.innerHTML;
|
||||
} else {
|
||||
newScript.appendChild(document.createTextNode(`(() => { ${script.innerHTML} })();`));
|
||||
}
|
||||
|
||||
script.parentNode.replaceChild(newScript, script);
|
||||
}
|
||||
|
||||
function getFlavor() {
|
||||
return sessionStorage.getItem('flavor') || 'html';
|
||||
}
|
||||
|
||||
function setFlavor(newFlavor) {
|
||||
flavor = ['html', 'react'].includes(newFlavor) ? newFlavor : 'html';
|
||||
sessionStorage.setItem('flavor', flavor);
|
||||
|
||||
// Set the flavor class on the body
|
||||
document.documentElement.classList.toggle('flavor-html', flavor === 'html');
|
||||
document.documentElement.classList.toggle('flavor-react', flavor === 'react');
|
||||
}
|
||||
|
||||
function syncFlavor() {
|
||||
setFlavor(getFlavor());
|
||||
|
||||
document.querySelectorAll('.code-preview__button--html').forEach(preview => {
|
||||
if (flavor === 'html') {
|
||||
preview.classList.add('code-preview__button--selected');
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.code-preview__button--react').forEach(preview => {
|
||||
if (flavor === 'react') {
|
||||
preview.classList.add('code-preview__button--selected');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const waVersion = document.documentElement.getAttribute('data-wa-version');
|
||||
const reactVersion = '18.2.0';
|
||||
const cdndir = 'cdn';
|
||||
const npmdir = 'dist';
|
||||
let flavor = getFlavor();
|
||||
let count = 1;
|
||||
|
||||
// We need the version to open
|
||||
if (!waVersion) {
|
||||
throw new Error('The data-wa-version attribute is missing from <html>.');
|
||||
}
|
||||
|
||||
// Sync flavor UI on page load
|
||||
syncFlavor();
|
||||
|
||||
//
|
||||
// Resizing previews
|
||||
//
|
||||
document.addEventListener('mousedown', handleResizerDrag);
|
||||
document.addEventListener('touchstart', handleResizerDrag, { passive: true });
|
||||
|
||||
function handleResizerDrag(event) {
|
||||
const resizer = event.target.closest('.code-preview__resizer');
|
||||
const preview = event.target.closest('.code-preview__preview');
|
||||
|
||||
if (!resizer || !preview) return;
|
||||
|
||||
let startX = event.changedTouches ? event.changedTouches[0].pageX : event.clientX;
|
||||
let startWidth = parseInt(document.defaultView.getComputedStyle(preview).width, 10);
|
||||
|
||||
event.preventDefault();
|
||||
preview.classList.add('code-preview__preview--dragging');
|
||||
document.documentElement.addEventListener('mousemove', dragMove);
|
||||
document.documentElement.addEventListener('touchmove', dragMove);
|
||||
document.documentElement.addEventListener('mouseup', dragStop);
|
||||
document.documentElement.addEventListener('touchend', dragStop);
|
||||
|
||||
function dragMove(event) {
|
||||
const width = startWidth + (event.changedTouches ? event.changedTouches[0].pageX : event.pageX) - startX;
|
||||
preview.style.width = `${width}px`;
|
||||
}
|
||||
|
||||
function dragStop() {
|
||||
preview.classList.remove('code-preview__preview--dragging');
|
||||
document.documentElement.removeEventListener('mousemove', dragMove);
|
||||
document.documentElement.removeEventListener('touchmove', dragMove);
|
||||
document.documentElement.removeEventListener('mouseup', dragStop);
|
||||
document.documentElement.removeEventListener('touchend', dragStop);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Toggle source mode
|
||||
//
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('.code-preview__button');
|
||||
const codeBlock = button?.closest('.code-preview');
|
||||
|
||||
if (button?.classList.contains('code-preview__button--html')) {
|
||||
// Show HTML
|
||||
setFlavor('html');
|
||||
toggleSource(codeBlock, true);
|
||||
} else if (button?.classList.contains('code-preview__button--react')) {
|
||||
// Show React
|
||||
setFlavor('react');
|
||||
toggleSource(codeBlock, true);
|
||||
} else if (button?.classList.contains('code-preview__toggle')) {
|
||||
// Toggle source
|
||||
toggleSource(codeBlock);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update flavor buttons
|
||||
[...document.querySelectorAll('.code-preview')].forEach(cb => {
|
||||
cb.querySelector('.code-preview__button--html')?.classList.toggle(
|
||||
'code-preview__button--selected',
|
||||
flavor === 'html'
|
||||
);
|
||||
cb.querySelector('.code-preview__button--react')?.classList.toggle(
|
||||
'code-preview__button--selected',
|
||||
flavor === 'react'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function toggleSource(codeBlock, force) {
|
||||
codeBlock.classList.toggle('code-preview--expanded', force);
|
||||
event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-preview--expanded'));
|
||||
}
|
||||
|
||||
//
|
||||
// Open in CodePen
|
||||
//
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('button');
|
||||
|
||||
if (button?.classList.contains('code-preview__button--codepen')) {
|
||||
const codeBlock = button.closest('.code-preview');
|
||||
const htmlExample = codeBlock.querySelector('.code-preview__source--html > pre > code')?.textContent;
|
||||
const reactExample = codeBlock.querySelector('.code-preview__source--react > pre > code')?.textContent;
|
||||
const isReact = flavor === 'react' && typeof reactExample === 'string';
|
||||
const editors = isReact ? '0010' : '1000';
|
||||
let htmlTemplate = '';
|
||||
let jsTemplate = '';
|
||||
let cssTemplate = '';
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.action = 'https://codepen.io/pen/define';
|
||||
form.method = 'POST';
|
||||
form.target = '_blank';
|
||||
|
||||
// HTML templates
|
||||
if (!isReact) {
|
||||
htmlTemplate =
|
||||
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${waVersion}/${cdndir}/autoloader.js"></script>\n` +
|
||||
`\n${htmlExample}`;
|
||||
jsTemplate = '';
|
||||
}
|
||||
|
||||
// React templates
|
||||
if (isReact) {
|
||||
htmlTemplate = '<div id="root"></div>';
|
||||
jsTemplate =
|
||||
`import React from 'https://esm.sh/react@${reactVersion}';\n` +
|
||||
`import ReactDOM from 'https://esm.sh/react-dom@${reactVersion}';\n` +
|
||||
`import { setBasePath } from 'https://esm.sh/@shoelace-style/shoelace@${waVersion}/${cdndir}/utilities/base-path';\n` +
|
||||
`\n` +
|
||||
`// Set the base path for Web Awesome assets\n` +
|
||||
`setBasePath('https://esm.sh/@shoelace-style/shoelace@${waVersion}/${npmdir}/')\n` +
|
||||
`\n${convertModuleLinks(reactExample)}\n` +
|
||||
`\n` +
|
||||
`ReactDOM.render(<App />, document.getElementById('root'));`;
|
||||
}
|
||||
|
||||
// CSS templates
|
||||
cssTemplate =
|
||||
`@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${waVersion}/${cdndir}/themes/default.css';\n` +
|
||||
'\n' +
|
||||
'body {\n' +
|
||||
' font: var(--wa-font-size-root) sans-serif;\n' +
|
||||
' background-color: var(--wa-color-surface-default);\n' +
|
||||
' color: var(--wa-color-text-normal);\n' +
|
||||
' padding: var(--wa-space-m);\n' +
|
||||
'}';
|
||||
|
||||
// Docs: https://blog.codepen.io/documentation/prefill/
|
||||
const data = {
|
||||
title: '',
|
||||
description: '',
|
||||
tags: ['web awesome', 'web components'],
|
||||
editors,
|
||||
head: `<meta name="viewport" content="width=device-width">`,
|
||||
css_external: ``,
|
||||
js_external: ``,
|
||||
js_module: true,
|
||||
js_pre_processor: isReact ? 'babel' : 'none',
|
||||
html: htmlTemplate,
|
||||
css: cssTemplate,
|
||||
js: jsTemplate
|
||||
};
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'data';
|
||||
input.value = JSON.stringify(data);
|
||||
form.append(input);
|
||||
|
||||
document.documentElement.append(form);
|
||||
form.submit();
|
||||
form.remove();
|
||||
}
|
||||
});
|
||||
|
||||
// Set the initial flavor
|
||||
window.addEventListener('turbo:load', syncFlavor);
|
||||
})();
|
||||
210
docs/assets/scripts/docs.js
Normal file
@@ -0,0 +1,210 @@
|
||||
//
|
||||
// Sidebar
|
||||
//
|
||||
// When the sidebar is hidden, we apply the inert attribute to prevent focus from reaching it. Due to the many states
|
||||
// the sidebar can have (e.g. static, hidden, expanded), we test for visibility by checking to see if it's placed
|
||||
// offscreen or not. Then, on resize/transition we make sure to update the attribute accordingly.
|
||||
//
|
||||
(() => {
|
||||
function getSidebar() {
|
||||
return document.getElementById('sidebar');
|
||||
}
|
||||
|
||||
function isSidebarOpen() {
|
||||
return document.documentElement.classList.contains('sidebar-open');
|
||||
}
|
||||
|
||||
function isSidebarVisible() {
|
||||
return getSidebar()?.getBoundingClientRect().x >= 0;
|
||||
}
|
||||
|
||||
function toggleSidebar(force) {
|
||||
const isOpen = typeof force === 'boolean' ? force : !isSidebarOpen();
|
||||
return document.documentElement.classList.toggle('sidebar-open', isOpen);
|
||||
}
|
||||
|
||||
function updateInert() {
|
||||
const sidebar = getSidebar();
|
||||
|
||||
if (sidebar) {
|
||||
sidebar.inert = !isSidebarVisible();
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle the menu
|
||||
document.addEventListener('click', event => {
|
||||
const menuToggle = event.target.closest('#menu-toggle');
|
||||
if (!menuToggle) return;
|
||||
toggleSidebar();
|
||||
});
|
||||
|
||||
// Update the sidebar's inert state when the window resizes and when the sidebar transitions
|
||||
window.addEventListener('resize', () => toggleSidebar(false));
|
||||
|
||||
document.addEventListener('transitionend', event => {
|
||||
const sidebar = event.target.closest('#sidebar');
|
||||
if (!sidebar) return;
|
||||
updateInert();
|
||||
});
|
||||
|
||||
// Close when a menu item is selected on mobile
|
||||
document.addEventListener('click', event => {
|
||||
const sidebar = event.target.closest('#sidebar');
|
||||
const link = event.target.closest('a');
|
||||
if (!sidebar || !link) return;
|
||||
|
||||
if (isSidebarOpen()) {
|
||||
toggleSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
// Close when open and escape is pressed
|
||||
document.addEventListener('keydown', event => {
|
||||
if (event.key === 'Escape' && isSidebarOpen()) {
|
||||
event.stopImmediatePropagation();
|
||||
toggleSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
// Close when clicking outside of the sidebar
|
||||
document.addEventListener('mousedown', event => {
|
||||
if (isSidebarOpen() & !event.target?.closest('#sidebar, #menu-toggle')) {
|
||||
event.stopImmediatePropagation();
|
||||
toggleSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
updateInert();
|
||||
})();
|
||||
|
||||
//
|
||||
// Open details when printing
|
||||
//
|
||||
(() => {
|
||||
const detailsOpenOnPrint = new Set();
|
||||
|
||||
window.addEventListener('beforeprint', () => {
|
||||
detailsOpenOnPrint.clear();
|
||||
document.querySelectorAll('details').forEach(details => {
|
||||
if (details.open) {
|
||||
detailsOpenOnPrint.add(details);
|
||||
}
|
||||
details.open = true;
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('afterprint', () => {
|
||||
document.querySelectorAll('details').forEach(details => {
|
||||
details.open = detailsOpenOnPrint.has(details);
|
||||
});
|
||||
detailsOpenOnPrint.clear();
|
||||
});
|
||||
})();
|
||||
|
||||
//
|
||||
// Smooth links
|
||||
//
|
||||
(() => {
|
||||
document.addEventListener('click', event => {
|
||||
const link = event.target.closest('a');
|
||||
const id = (link?.hash ?? '').substr(1);
|
||||
const isFragment = link?.hasAttribute('href') && link?.getAttribute('href').startsWith('#');
|
||||
|
||||
if (!link || !isFragment || link.getAttribute('data-smooth-link') === 'false') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Scroll to the top
|
||||
if (link.hash === '') {
|
||||
event.preventDefault();
|
||||
window.scroll({ top: 0, behavior: 'smooth' });
|
||||
history.pushState(undefined, undefined, location.pathname);
|
||||
}
|
||||
|
||||
// Scroll to an id
|
||||
if (id) {
|
||||
const target = document.getElementById(id);
|
||||
|
||||
if (target) {
|
||||
event.preventDefault();
|
||||
window.scroll({ top: target.offsetTop, behavior: 'smooth' });
|
||||
history.pushState(undefined, undefined, `#${id}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
//
|
||||
// Table of Contents scrollspy
|
||||
//
|
||||
(() => {
|
||||
// This will be stale if its not a function.
|
||||
const getLinks = () => [...document.querySelectorAll('.content__toc a')];
|
||||
const linkTargets = new WeakMap();
|
||||
const visibleTargets = new WeakSet();
|
||||
const observer = new IntersectionObserver(handleIntersect, { rootMargin: '0px 0px' });
|
||||
let debounce;
|
||||
|
||||
function handleIntersect(entries) {
|
||||
entries.forEach(entry => {
|
||||
// Remember which targets are visible
|
||||
if (entry.isIntersecting) {
|
||||
visibleTargets.add(entry.target);
|
||||
} else {
|
||||
visibleTargets.delete(entry.target);
|
||||
}
|
||||
});
|
||||
|
||||
updateActiveLinks();
|
||||
}
|
||||
|
||||
function updateActiveLinks() {
|
||||
const links = getLinks();
|
||||
// Find the first visible target and activate the respective link
|
||||
links.find(link => {
|
||||
const target = linkTargets.get(link);
|
||||
|
||||
if (target && visibleTargets.has(target)) {
|
||||
links.forEach(el => el.classList.toggle('active', el === link));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Observe link targets
|
||||
function observeLinks() {
|
||||
getLinks().forEach(link => {
|
||||
const hash = link.hash.slice(1);
|
||||
const target = hash ? document.querySelector(`.content__body #${hash}`) : null;
|
||||
|
||||
if (target) {
|
||||
linkTargets.set(link, target);
|
||||
observer.observe(target);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
observeLinks();
|
||||
|
||||
document.addEventListener('turbo:load', updateActiveLinks);
|
||||
document.addEventListener('turbo:load', observeLinks);
|
||||
})();
|
||||
|
||||
//
|
||||
// Show custom versions in the sidebar
|
||||
//
|
||||
(() => {
|
||||
function updateVersion() {
|
||||
const el = document.querySelector('.sidebar-version');
|
||||
if (!el) return;
|
||||
|
||||
if (location.hostname === 'next.shoelace.style') el.textContent = 'Next';
|
||||
if (location.hostname === 'localhost') el.textContent = 'Development';
|
||||
}
|
||||
|
||||
updateVersion();
|
||||
|
||||
document.addEventListener('turbo:load', updateVersion);
|
||||
})();
|
||||
11
docs/assets/scripts/light-pen/CHANGELOG.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## 1.1.0
|
||||
|
||||
- A number of fixes including allowing `<template>` elements to be slotted into `<light-pen>` and `<light-preview>`
|
||||
|
||||
## 1.0.2
|
||||
|
||||
- fix: issues with `>` and `<`
|
||||
|
||||
## 1.0.1
|
||||
|
||||
- fix: allow any HTML Element for templating
|
||||
21
docs/assets/scripts/light-pen/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Konnor Rogers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
96
docs/assets/scripts/light-pen/README.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Light(weight) Code(pen)
|
||||
|
||||
## Demo
|
||||
|
||||
<https://konnorrogers.github.io/light-pen>
|
||||
|
||||
## Purpose
|
||||
|
||||
A small lightweight editor using `<pre><code></code></pre>` and a `<textarea>`
|
||||
|
||||
Inspired by Chris Ferdinandi
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
npm install light-pen
|
||||
```
|
||||
|
||||
## Getting started
|
||||
|
||||
```js
|
||||
// Auto-register <light-pen>
|
||||
import "light-pen"
|
||||
|
||||
// Registry yourself
|
||||
import LightPen "light-pen/exports/light-pen.js"
|
||||
|
||||
LightPen.define() // Registers `<light-pen>`
|
||||
|
||||
// Register under another name
|
||||
import LightPen "light-pen/exports/light-pen.js"
|
||||
LightPen.define("other-name")
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```html
|
||||
<light-pen></light-pen>
|
||||
```
|
||||
|
||||
That's it!
|
||||
|
||||
|
||||
### Slotting in HTML / CSS / JS
|
||||
|
||||
```html
|
||||
<light-pen>
|
||||
<script type="text/plain" slot="html">
|
||||
<div style="color: red;">
|
||||
<div>Hi there friends</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Sup dude
|
||||
</p>
|
||||
</script>
|
||||
|
||||
<script type="text/plain" slot="css">
|
||||
p {
|
||||
color: green;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/plain" slot="js">
|
||||
console.log("Hi")
|
||||
</script>
|
||||
</light-pen>
|
||||
```
|
||||
|
||||
### Changing the title
|
||||
|
||||
```html
|
||||
<light-pen>
|
||||
<div slot="title">My Awesome Editor</div>
|
||||
</light-pen>
|
||||
```
|
||||
|
||||
### Opening languages by default
|
||||
|
||||
`<light-pen>` Takes a string of comma separated languages to open on initial render.
|
||||
|
||||
```
|
||||
<light-pen open-languages="html,css,js">
|
||||
</light-pen>
|
||||
```
|
||||
|
||||
More to come for more docs coming on how to change things!
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [ ] - Implement an extendable `LightPenBase` which includes a pluggable syntax highlighter and theme.
|
||||
- [ ] - Implement `<textarea>` rendering hooks to add CodeMirror for more robuste setups.
|
||||
- [ ] - Add vertical resizing of `<textarea>`
|
||||
- [ ] - More documentation around customization.
|
||||
- [ ] - Add a console logger
|
||||
>>>>>>> 7b592c8f5f05ed82d439d7950450eec06dc3ab66
|
||||
@@ -0,0 +1,44 @@
|
||||
// @ts-check
|
||||
// import { expandTypesPlugin } from './expand-types.js'
|
||||
|
||||
const globs = ['exports/**/*.{d.ts,js}', 'internal/**/*.{d.ts,js}', 'types/**/*.d.ts']
|
||||
|
||||
export default {
|
||||
/** Globs to analyze */
|
||||
globs,
|
||||
/** Globs to exclude */
|
||||
exclude: ['node_modules', 'docs'],
|
||||
/** Directory to output CEM to */
|
||||
outdir: '.',
|
||||
/** Run in dev mode, provides extra logging */
|
||||
dev: process.argv.includes("--verbose"),
|
||||
/** Run in watch mode, runs on file changes */
|
||||
watch: process.argv.includes("--watch"),
|
||||
/** Include third party custom elements manifests */
|
||||
dependencies: true,
|
||||
/** Output CEM path to `package.json`, defaults to true */
|
||||
packagejson: true,
|
||||
/** Enable special handling for litelement */
|
||||
litelement: true,
|
||||
/** Enable special handling for catalyst */
|
||||
catalyst: false,
|
||||
/** Enable special handling for fast */
|
||||
fast: false,
|
||||
/** Enable special handling for stencil */
|
||||
stencil: false,
|
||||
// overrideModuleCreation: ({ts, globs}) => {
|
||||
// const program = ts.createProgram(globs, {target: ts.ScriptTarget.ESNext, module: ts.ModuleKind.ESNext, allowJs: true, checkJs: true});
|
||||
//
|
||||
// // If we dont do this, everything blows up.
|
||||
// program.getTypeChecker()
|
||||
//
|
||||
// return program.getSourceFiles().filter(sf => globs.find(glob => {
|
||||
// return sf.fileName.includes(glob)
|
||||
// }))
|
||||
// },
|
||||
// /** Provide custom plugins */
|
||||
// plugins: [
|
||||
// /** You can now pass the typeChecker to your plugins */
|
||||
// expandTypesPlugin({ globs })
|
||||
// ],
|
||||
}
|
||||
1984
docs/assets/scripts/light-pen/custom-elements.json
Normal file
38
docs/assets/scripts/light-pen/docs/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# Bridgetown
|
||||
output
|
||||
.bridgetown-cache
|
||||
.bridgetown-metadata
|
||||
.bridgetown-webpack
|
||||
|
||||
# Dependency folders
|
||||
node_modules
|
||||
bower_components
|
||||
vendor
|
||||
|
||||
# Caches
|
||||
.sass-cache
|
||||
.npm
|
||||
.node_repl_history
|
||||
|
||||
# Ignore bundler config.
|
||||
/.bundle
|
||||
|
||||
# Ignore Byebug command history file.
|
||||
.byebug_history
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# Mac files
|
||||
.DS_Store
|
||||
|
||||
# Yarn
|
||||
yarn-error.log
|
||||
yarn-debug.log*
|
||||
.pnp/
|
||||
.pnp.js
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
src/bridgetown
|
||||
src/meta.json
|
||||
1
docs/assets/scripts/light-pen/docs/.ruby-version
Normal file
@@ -0,0 +1 @@
|
||||
ruby-3.0.4
|
||||
49
docs/assets/scripts/light-pen/docs/Gemfile
Normal file
@@ -0,0 +1,49 @@
|
||||
source "https://rubygems.org"
|
||||
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
||||
|
||||
####
|
||||
# Welcome to your project's Gemfile, used by Rubygems & Bundler.
|
||||
#
|
||||
# To install a plugin, run:
|
||||
#
|
||||
# bundle add new-plugin-name -g bridgetown_plugins
|
||||
#
|
||||
# This will ensure the plugin is added to the correct Bundler group.
|
||||
#
|
||||
# When you run Bridgetown commands, we recommend using a binstub like so:
|
||||
#
|
||||
# bin/bridgetown start (or console, etc.)
|
||||
#
|
||||
# This will help ensure the proper Bridgetown version is running.
|
||||
####
|
||||
|
||||
# If you need to upgrade/switch Bridgetown versions, change the line below
|
||||
# and then run `bundle update bridgetown`
|
||||
gem "bridgetown", "~> 1.3"
|
||||
|
||||
# Uncomment to add file-based dynamic routing to your project:
|
||||
# gem "bridgetown-routes", "~> 1.3"
|
||||
|
||||
# Uncomment to use the Inspectors API to manipulate the output
|
||||
# of your HTML or XML resources:
|
||||
gem "nokogiri", "~> 1.13"
|
||||
|
||||
# Puma is a Rack-compatible server used by Bridgetown
|
||||
# (you can optionally limit this to the "development" group)
|
||||
gem "puma", "~> 5.6"
|
||||
|
||||
gem "bridgetown-quick-search", "~> 2.0"
|
||||
|
||||
# gem "asset_mapper", "~> 1.0"
|
||||
gem "asset_mapper", "~> 1.0"
|
||||
|
||||
# gem "rack-cors", "~> 2.0"
|
||||
|
||||
gem "rack-cors", "~> 2.0"
|
||||
|
||||
gem "custom_elements_manifest_parser", "~> 0.2.4"
|
||||
|
||||
gem "ruby-lsp", "~> 0.11.1", :group => :development, require: false
|
||||
gem "yard", "~> 0.9.34", :group => :development
|
||||
gem "solargraph", "~> 0.49.0", :group => :development
|
||||
gem "debug", "~> 1.8", :group => :development
|
||||
241
docs/assets/scripts/light-pen/docs/Gemfile.lock
Normal file
@@ -0,0 +1,241 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activemodel (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activesupport (7.0.8)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
addressable (2.8.5)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
amazing_print (1.5.0)
|
||||
asset_mapper (1.0.1)
|
||||
dry-configurable (~> 1.0)
|
||||
dry-files (~> 1.0)
|
||||
dry-initializer (~> 3.0)
|
||||
dry-types (~> 1.5)
|
||||
rake (>= 10)
|
||||
zeitwerk (~> 2.5)
|
||||
ast (2.4.2)
|
||||
backport (1.2.0)
|
||||
base64 (0.1.1)
|
||||
benchmark (0.2.1)
|
||||
bridgetown (1.3.1)
|
||||
bridgetown-builder (= 1.3.1)
|
||||
bridgetown-core (= 1.3.1)
|
||||
bridgetown-paginate (= 1.3.1)
|
||||
bridgetown-builder (1.3.1)
|
||||
bridgetown-core (= 1.3.1)
|
||||
bridgetown-core (1.3.1)
|
||||
activemodel (>= 6.0, < 8.0)
|
||||
activesupport (>= 6.0, < 8.0)
|
||||
addressable (~> 2.4)
|
||||
amazing_print (~> 1.2)
|
||||
colorator (~> 1.0)
|
||||
erubi (~> 1.9)
|
||||
faraday (~> 2.0)
|
||||
faraday-follow_redirects (~> 0.3)
|
||||
hash_with_dot_access (~> 1.2)
|
||||
i18n (~> 1.0)
|
||||
kramdown (~> 2.1)
|
||||
kramdown-parser-gfm (~> 1.0)
|
||||
liquid (~> 5.0)
|
||||
listen (~> 3.0)
|
||||
rake (>= 13.0)
|
||||
roda (~> 3.46)
|
||||
rouge (~> 3.0)
|
||||
serbea (~> 1.0)
|
||||
thor (~> 1.1)
|
||||
tilt (~> 2.0)
|
||||
zeitwerk (~> 2.5)
|
||||
bridgetown-paginate (1.3.1)
|
||||
bridgetown-core (= 1.3.1)
|
||||
bridgetown-quick-search (2.0.0)
|
||||
bridgetown (>= 1.2.0.beta2, < 2.0)
|
||||
colorator (1.1.0)
|
||||
concurrent-ruby (1.2.2)
|
||||
custom_elements_manifest_parser (0.2.4)
|
||||
dry-struct (~> 1.0)
|
||||
dry-types (~> 1.0)
|
||||
dry-validation (~> 1.0)
|
||||
debug (1.8.0)
|
||||
irb (>= 1.5.0)
|
||||
reline (>= 0.3.1)
|
||||
diff-lcs (1.5.0)
|
||||
dry-configurable (1.1.0)
|
||||
dry-core (~> 1.0, < 2)
|
||||
zeitwerk (~> 2.6)
|
||||
dry-core (1.0.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
zeitwerk (~> 2.6)
|
||||
dry-files (1.0.1)
|
||||
dry-inflector (1.0.0)
|
||||
dry-initializer (3.1.1)
|
||||
dry-logic (1.5.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
dry-core (~> 1.0, < 2)
|
||||
zeitwerk (~> 2.6)
|
||||
dry-schema (1.13.3)
|
||||
concurrent-ruby (~> 1.0)
|
||||
dry-configurable (~> 1.0, >= 1.0.1)
|
||||
dry-core (~> 1.0, < 2)
|
||||
dry-initializer (~> 3.0)
|
||||
dry-logic (>= 1.4, < 2)
|
||||
dry-types (>= 1.7, < 2)
|
||||
zeitwerk (~> 2.6)
|
||||
dry-struct (1.6.0)
|
||||
dry-core (~> 1.0, < 2)
|
||||
dry-types (>= 1.7, < 2)
|
||||
ice_nine (~> 0.11)
|
||||
zeitwerk (~> 2.6)
|
||||
dry-types (1.7.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
dry-core (~> 1.0)
|
||||
dry-inflector (~> 1.0)
|
||||
dry-logic (~> 1.4)
|
||||
zeitwerk (~> 2.6)
|
||||
dry-validation (1.10.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
dry-core (~> 1.0, < 2)
|
||||
dry-initializer (~> 3.0)
|
||||
dry-schema (>= 1.12, < 2)
|
||||
zeitwerk (~> 2.6)
|
||||
e2mmap (0.1.0)
|
||||
erubi (1.12.0)
|
||||
faraday (2.7.11)
|
||||
base64
|
||||
faraday-net_http (>= 2.0, < 3.1)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-follow_redirects (0.3.0)
|
||||
faraday (>= 1, < 3)
|
||||
faraday-net_http (3.0.2)
|
||||
ffi (1.15.5)
|
||||
hash_with_dot_access (1.2.0)
|
||||
activesupport (>= 5.0.0, < 8.0)
|
||||
i18n (1.14.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
ice_nine (0.11.2)
|
||||
io-console (0.6.0)
|
||||
irb (1.8.1)
|
||||
rdoc
|
||||
reline (>= 0.3.8)
|
||||
jaro_winkler (1.5.6)
|
||||
json (2.6.3)
|
||||
kramdown (2.4.0)
|
||||
rexml
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
language_server-protocol (3.17.0.3)
|
||||
liquid (5.4.0)
|
||||
listen (3.8.0)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
minitest (5.20.0)
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.13.9-arm64-darwin)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.13.9-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
parallel (1.23.0)
|
||||
parser (3.2.2.4)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
psych (5.1.0)
|
||||
stringio
|
||||
public_suffix (5.0.3)
|
||||
puma (5.6.5)
|
||||
nio4r (~> 2.0)
|
||||
racc (1.6.0)
|
||||
rack (3.0.8)
|
||||
rack-cors (2.0.1)
|
||||
rack (>= 2.0.0)
|
||||
rainbow (3.1.1)
|
||||
rake (13.0.6)
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rbs (2.8.4)
|
||||
rdoc (6.5.0)
|
||||
psych (>= 4.0.0)
|
||||
regexp_parser (2.8.1)
|
||||
reline (0.3.9)
|
||||
io-console (~> 0.5)
|
||||
reverse_markdown (2.1.1)
|
||||
nokogiri
|
||||
rexml (3.2.6)
|
||||
roda (3.72.0)
|
||||
rack
|
||||
rouge (3.30.0)
|
||||
rubocop (1.56.4)
|
||||
base64 (~> 0.1.1)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.2.2.3)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.28.1, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.29.0)
|
||||
parser (>= 3.2.1.0)
|
||||
ruby-lsp (0.11.1)
|
||||
language_server-protocol (~> 3.17.0)
|
||||
sorbet-runtime (>= 0.5.5685)
|
||||
yarp (>= 0.12, < 0.13)
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
serbea (1.0.1)
|
||||
activesupport (>= 6.0)
|
||||
erubi (>= 1.10)
|
||||
tilt (~> 2.0)
|
||||
solargraph (0.49.0)
|
||||
backport (~> 1.2)
|
||||
benchmark
|
||||
bundler (~> 2.0)
|
||||
diff-lcs (~> 1.4)
|
||||
e2mmap
|
||||
jaro_winkler (~> 1.5)
|
||||
kramdown (~> 2.3)
|
||||
kramdown-parser-gfm (~> 1.1)
|
||||
parser (~> 3.0)
|
||||
rbs (~> 2.0)
|
||||
reverse_markdown (~> 2.0)
|
||||
rubocop (~> 1.38)
|
||||
thor (~> 1.0)
|
||||
tilt (~> 2.0)
|
||||
yard (~> 0.9, >= 0.9.24)
|
||||
sorbet-runtime (0.5.11064)
|
||||
stringio (3.0.8)
|
||||
thor (1.2.2)
|
||||
tilt (2.3.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
unicode-display_width (2.5.0)
|
||||
yard (0.9.34)
|
||||
yarp (0.12.0)
|
||||
zeitwerk (2.6.12)
|
||||
|
||||
PLATFORMS
|
||||
arm64-darwin-21
|
||||
arm64-darwin-22
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
asset_mapper (~> 1.0)
|
||||
bridgetown (~> 1.3)
|
||||
bridgetown-quick-search (~> 2.0)
|
||||
custom_elements_manifest_parser (~> 0.2.4)
|
||||
debug (~> 1.8)
|
||||
nokogiri (~> 1.13)
|
||||
puma (~> 5.6)
|
||||
rack-cors (~> 2.0)
|
||||
ruby-lsp (~> 0.11.1)
|
||||
solargraph (~> 0.49.0)
|
||||
yard (~> 0.9.34)
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.5
|
||||
2
docs/assets/scripts/light-pen/docs/Procfile.dev
Normal file
@@ -0,0 +1,2 @@
|
||||
bridgetown: bin/bridgetown start --skip-frontend
|
||||
esbuild: pnpm run esbuild-dev
|
||||
70
docs/assets/scripts/light-pen/docs/README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Bridgetown Website README
|
||||
|
||||
Welcome to your new Bridgetown website! You can update this README file to provide additional context and setup information for yourself or other contributors.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Install](#install)
|
||||
- [Development](#development)
|
||||
- [Commands](#commands)
|
||||
- [Deployment](#deployment)
|
||||
- [Contributing](#contributing)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [GCC](https://gcc.gnu.org/install/)
|
||||
- [Make](https://www.gnu.org/software/make/)
|
||||
- [Ruby](https://www.ruby-lang.org/en/downloads/)
|
||||
- `>= 2.7`
|
||||
- [Bridgetown Gem](https://rubygems.org/gems/bridgetown)
|
||||
- `gem install bridgetown -N`
|
||||
- [Node](https://nodejs.org)
|
||||
- `>= 12`
|
||||
- [Yarn](https://yarnpkg.com)
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
cd bridgetown-site-folder
|
||||
bundle install && yarn install
|
||||
```
|
||||
> Learn more: [Bridgetown Getting Started Documentation](https://www.bridgetownrb.com/docs/).
|
||||
|
||||
## Development
|
||||
|
||||
To start your site in development mode, run `bin/bridgetown start` and navigate to [localhost:4000](https://localhost:4000/)!
|
||||
|
||||
Use a [theme](https://github.com/topics/bridgetown-theme) or add some [plugins](https://www.bridgetownrb.com/plugins/) to get started quickly.
|
||||
|
||||
### Commands
|
||||
|
||||
```sh
|
||||
# running locally
|
||||
bin/bridgetown start
|
||||
|
||||
# build & deploy to production
|
||||
bin/bridgetown deploy
|
||||
|
||||
# load the site up within a Ruby console (IRB)
|
||||
bin/bridgetown console
|
||||
```
|
||||
|
||||
> Learn more: [Bridgetown CLI Documentation](https://www.bridgetownrb.com/docs/command-line-usage)
|
||||
|
||||
## Deployment
|
||||
|
||||
You can deploy Bridgetown sites on hosts like Render or Vercel as well as traditional web servers by simply building and copying the output folder to your HTML root.
|
||||
|
||||
> Read the [Bridgetown Deployment Documentation](https://www.bridgetownrb.com/docs/deployment) for more information.
|
||||
|
||||
## Contributing
|
||||
|
||||
If repo is on GitHub:
|
||||
|
||||
1. Fork it
|
||||
2. Clone the fork using `git clone` to your local development machine.
|
||||
3. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
4. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
5. Push to the branch (`git push origin my-new-feature`)
|
||||
6. Create a new Pull Request
|
||||
49
docs/assets/scripts/light-pen/docs/Rakefile
Normal file
@@ -0,0 +1,49 @@
|
||||
require "bridgetown"
|
||||
|
||||
Bridgetown.load_tasks
|
||||
|
||||
# Run rake without specifying any command to execute a deploy build by default.
|
||||
task default: :deploy
|
||||
|
||||
#
|
||||
# Standard set of tasks, which you can customize if you wish:
|
||||
#
|
||||
desc "Build the Bridgetown site for deployment"
|
||||
task :deploy => [:clean, "frontend:build"] do
|
||||
Bridgetown::Commands::Build.start
|
||||
end
|
||||
|
||||
desc "Build the site in a test environment"
|
||||
task :test do
|
||||
ENV["BRIDGETOWN_ENV"] = "test"
|
||||
Bridgetown::Commands::Build.start
|
||||
end
|
||||
|
||||
desc "Runs the clean command"
|
||||
task :clean do
|
||||
Bridgetown::Commands::Clean.start
|
||||
end
|
||||
|
||||
namespace :frontend do
|
||||
desc "Build the frontend with esbuild for deployment"
|
||||
task :build do
|
||||
sh "yarn run esbuild"
|
||||
end
|
||||
|
||||
desc "Watch the frontend with esbuild during development"
|
||||
task :dev do
|
||||
sh "yarn run esbuild-dev"
|
||||
rescue Interrupt
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Add your own Rake tasks here! You can use `environment` as a prerequisite
|
||||
# in order to write automations or other commands requiring a loaded site.
|
||||
#
|
||||
# task :my_task => :environment do
|
||||
# puts site.root_dir
|
||||
# automation do
|
||||
# say_status :rake, "I'm a Rake tast =) #{site.config.url}"
|
||||
# end
|
||||
# end
|
||||
27
docs/assets/scripts/light-pen/docs/bin/bridgetown
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'bridgetown' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("bridgetown-core", "bridgetown")
|
||||
27
docs/assets/scripts/light-pen/docs/bin/bt
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'bridgetown' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("bridgetown-core", "bridgetown")
|
||||
48
docs/assets/scripts/light-pen/docs/bridgetown.config.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
baseurl: "" # OPTIONAL: the subpath of your site, e.g. /blog
|
||||
url: "" # the base hostname & protocol for your site, e.g. https://example.com
|
||||
destination: output
|
||||
|
||||
development:
|
||||
url: "localhost:4000"
|
||||
# production:
|
||||
# url: "https://konnorrogers.github.io"
|
||||
# base_path: "/light-pen"
|
||||
|
||||
template_engine: erb
|
||||
permalink: pretty
|
||||
|
||||
collections:
|
||||
documentation:
|
||||
output: true
|
||||
|
||||
defaults:
|
||||
- scope:
|
||||
path: "images"
|
||||
values:
|
||||
image: true
|
||||
|
||||
- scope:
|
||||
path: "_documentation"
|
||||
values:
|
||||
layout: doc
|
||||
permalink: /:categories/:slug
|
||||
category_order: 0
|
||||
doc_order: 0
|
||||
|
||||
- scope:
|
||||
path: "_documentation/guides"
|
||||
values:
|
||||
category: guides
|
||||
category_order: 10
|
||||
|
||||
- scope:
|
||||
path: "_documentation/components"
|
||||
values:
|
||||
category: components
|
||||
category_order: 20
|
||||
|
||||
- scope:
|
||||
path: "_documentation/references"
|
||||
values:
|
||||
category: references
|
||||
category_order: 30
|
||||
16
docs/assets/scripts/light-pen/docs/config.ru
Normal file
@@ -0,0 +1,16 @@
|
||||
# This file is used by Rack-based servers during the Bridgetown boot process.
|
||||
|
||||
require "bridgetown-core/rack/boot"
|
||||
|
||||
Bridgetown::Rack.boot
|
||||
|
||||
require "rack/cors"
|
||||
|
||||
use Rack::Cors do
|
||||
allow do
|
||||
origins '*'
|
||||
resource '/*', headers: :any, methods: :get
|
||||
end
|
||||
end
|
||||
|
||||
run RodaApp.freeze.app # see server/roda_app.rb
|
||||
310
docs/assets/scripts/light-pen/docs/config/esbuild.defaults.js
Normal file
@@ -0,0 +1,310 @@
|
||||
// This file is created and managed by Bridgetown.
|
||||
// Instead of editing this file, add your overrides to `esbuild.config.js`
|
||||
//
|
||||
// To update this file to the latest version provided by Bridgetown,
|
||||
// run `bridgetown esbuild update`. Any changes to this file will be overwritten
|
||||
// when an update is applied hence we strongly recommend adding overrides to
|
||||
// `esbuild.config.js` instead of editing this file.
|
||||
//
|
||||
// Shipped with Bridgetown v1.1.0
|
||||
|
||||
const path = require("path")
|
||||
const fsLib = require("fs")
|
||||
const fs = fsLib.promises
|
||||
const { pathToFileURL, fileURLToPath } = require("url")
|
||||
const glob = require("glob")
|
||||
const postcss = require("postcss")
|
||||
const postCssImport = require("postcss-import")
|
||||
const readCache = require("read-cache")
|
||||
|
||||
// Detect if an NPM package is available
|
||||
const moduleAvailable = name => {
|
||||
try {
|
||||
require.resolve(name)
|
||||
return true
|
||||
} catch (e) { }
|
||||
return false
|
||||
}
|
||||
|
||||
// Generate a Source Map URL (used by the Sass plugin)
|
||||
const generateSourceMappingURL = sourceMap => {
|
||||
const data = Buffer.from(JSON.stringify(sourceMap), "utf-8").toString("base64")
|
||||
return `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${data} */`
|
||||
}
|
||||
|
||||
// Import Sass if available
|
||||
let sass
|
||||
if (moduleAvailable("sass")) {
|
||||
sass = require("sass")
|
||||
}
|
||||
|
||||
// Glob plugin derived from:
|
||||
// https://github.com/thomaschaaf/esbuild-plugin-import-glob
|
||||
// https://github.com/xiaohui-zhangxh/jsbundling-rails/commit/b15025dcc20f664b2b0eb238915991afdbc7cb58
|
||||
const importGlobPlugin = () => ({
|
||||
name: "import-glob",
|
||||
setup: (build) => {
|
||||
build.onResolve({ filter: /\*/ }, async (args) => {
|
||||
if (args.resolveDir === "") {
|
||||
return; // Ignore unresolvable paths
|
||||
}
|
||||
|
||||
const adjustedPath = args.path.replace(/^bridgetownComponents\//, "../../src/_components/")
|
||||
|
||||
return {
|
||||
path: adjustedPath,
|
||||
namespace: "import-glob",
|
||||
pluginData: {
|
||||
path: adjustedPath,
|
||||
resolveDir: args.resolveDir,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
build.onLoad({ filter: /.*/, namespace: "import-glob" }, async (args) => {
|
||||
const files = glob.sync(args.pluginData.path, {
|
||||
cwd: args.pluginData.resolveDir,
|
||||
}).sort()
|
||||
|
||||
const importerCode = `
|
||||
${files
|
||||
.map((module, index) => `import * as module${index} from '${module}'`)
|
||||
.join(';')}
|
||||
const modules = {${files
|
||||
.map((module, index) => `
|
||||
"${module.replace("../../src/_components/", "")}": module${index},`)
|
||||
.join("")}
|
||||
};
|
||||
export default modules;
|
||||
`
|
||||
|
||||
return { contents: importerCode, resolveDir: args.pluginData.resolveDir }
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// Plugin for PostCSS
|
||||
const postCssPlugin = (options, configuration) => ({
|
||||
name: "postcss",
|
||||
async setup(build) {
|
||||
// Process .css files with PostCSS
|
||||
build.onLoad({ filter: (configuration.filter || /\.css$/) }, async (args) => {
|
||||
const additionalFilePaths = []
|
||||
const css = await fs.readFile(args.path, "utf8")
|
||||
|
||||
// Configure import plugin so PostCSS can properly resolve `@import`ed CSS files
|
||||
const importPlugin = postCssImport({
|
||||
filter: itemPath => !itemPath.startsWith("/"), // ensure it doesn't try to import source-relative paths
|
||||
load: async filename => {
|
||||
let contents = await readCache(filename, "utf-8")
|
||||
const filedir = path.dirname(filename)
|
||||
// We'll want to track any imports later when in watch mode:
|
||||
additionalFilePaths.push(filename)
|
||||
|
||||
// We need to transform `url(...)` in imported CSS so the filepaths are properly
|
||||
// relative to the entrypoint. Seems icky to have to hack this! C'est la vie...
|
||||
contents = contents.replace(/url\(['"]?\.\/(.*?)['"]?\)/g, (_match, p1) => {
|
||||
const relpath = path.relative(args.path, path.resolve(filedir, p1)).replace(/^\.\.\//, "")
|
||||
return `url("${relpath}")`
|
||||
})
|
||||
return contents
|
||||
}
|
||||
})
|
||||
|
||||
// Process the file through PostCSS
|
||||
const result = await postcss([importPlugin, ...options.plugins]).process(css, {
|
||||
map: true,
|
||||
...options.options,
|
||||
from: args.path,
|
||||
});
|
||||
|
||||
return {
|
||||
contents: result.css,
|
||||
loader: "css",
|
||||
watchFiles: [args.path, ...additionalFilePaths],
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// Plugin for Sass
|
||||
const sassPlugin = (options) => ({
|
||||
name: "sass",
|
||||
async setup(build) {
|
||||
// Process .scss and .sass files with Sass
|
||||
build.onLoad({ filter: /\.(sass|scss)$/ }, async (args) => {
|
||||
if (!sass) {
|
||||
console.error("error: Sass is not installed. Try running `yarn add sass` and then building again.")
|
||||
return
|
||||
}
|
||||
|
||||
const modulesFolder = pathToFileURL("node_modules/")
|
||||
|
||||
const localOptions = {
|
||||
importers: [{
|
||||
// An importer that redirects relative URLs starting with "~" to
|
||||
// `node_modules`.
|
||||
findFileUrl(url) {
|
||||
if (!url.startsWith('~')) return null
|
||||
return new URL(url.substring(1), modulesFolder)
|
||||
}
|
||||
}],
|
||||
sourceMap: true,
|
||||
...options
|
||||
}
|
||||
const result = sass.compile(args.path, localOptions)
|
||||
|
||||
const watchPaths = result.loadedUrls
|
||||
.filter((x) => x.protocol === "file:" && !x.pathname.startsWith(modulesFolder.pathname))
|
||||
.map((x) => x.pathname)
|
||||
|
||||
let cssOutput = result.css.toString()
|
||||
|
||||
if (result.sourceMap) {
|
||||
const basedir = process.cwd()
|
||||
const sourceMap = result.sourceMap
|
||||
|
||||
const promises = sourceMap.sources.map(async source => {
|
||||
const sourceFile = await fs.readFile(fileURLToPath(source), "utf8")
|
||||
return sourceFile
|
||||
})
|
||||
sourceMap.sourcesContent = await Promise.all(promises)
|
||||
|
||||
sourceMap.sources = sourceMap.sources.map(source => {
|
||||
return path.relative(basedir, fileURLToPath(source))
|
||||
})
|
||||
|
||||
cssOutput += '\n' + generateSourceMappingURL(sourceMap)
|
||||
}
|
||||
|
||||
return {
|
||||
contents: cssOutput,
|
||||
loader: "css",
|
||||
watchFiles: [args.path, ...watchPaths],
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// Set up defaults and generate frontend bundling manifest file
|
||||
const bridgetownPreset = (outputFolder) => ({
|
||||
name: "bridgetownPreset",
|
||||
async setup(build) {
|
||||
// Ensure any imports anywhere starting with `/` are left verbatim
|
||||
// so they can be used in-browser for actual `src` repo files
|
||||
build.onResolve({ filter: /^\// }, args => {
|
||||
return { path: args.path, external: true }
|
||||
})
|
||||
|
||||
build.onStart(() => {
|
||||
console.log("esbuild: frontend bundling started...")
|
||||
})
|
||||
|
||||
// Generate the final output manifest
|
||||
build.onEnd(async (result) => {
|
||||
if (!result.metafile) {
|
||||
console.warn("esbuild: build process error, cannot write manifest")
|
||||
return
|
||||
}
|
||||
await fs.writeFile(path.join(outputFolder, 'meta.json'), JSON.stringify(result.metafile), { encoding: "utf8" })
|
||||
|
||||
const manifest = {}
|
||||
const entrypoints = []
|
||||
|
||||
// We don't need `frontend/` cluttering up everything
|
||||
// const stripPrefix = (str) => str.replace(/^frontend\//, "")
|
||||
|
||||
// For calculating the file size of bundle output
|
||||
const fileSize = (path) => {
|
||||
const { size } = fsLib.statSync(path)
|
||||
const i = Math.floor(Math.log(size) / Math.log(1024))
|
||||
return (size / Math.pow(1024, i)).toFixed(2) * 1 + ['B', 'KB', 'MB', 'GB', 'TB'][i]
|
||||
}
|
||||
|
||||
// Let's loop through all the various outputs
|
||||
// for (const key in result.metafile.outputs) {
|
||||
// const value = result.metafile.outputs[key]
|
||||
// const inputs = Object.keys(value.inputs)
|
||||
// const pathShortener = new RegExp(`^${outputFolder}\\/_bridgetown\\/static\\/`, "g")
|
||||
// const outputPath = key.replace(pathShortener, "")
|
||||
|
||||
// if (value.entryPoint) {
|
||||
// // We have an entrypoint!
|
||||
// manifest[stripPrefix(value.entryPoint)] = outputPath
|
||||
// entrypoints.push([outputPath, fileSize(key)])
|
||||
// } else if (key.match(/index(\.js)?\.[^-.]*\.css/) && inputs.find(item => item.match(/\.(s?css|sass)$/))) {
|
||||
// // Special treatment for index.css
|
||||
// manifest[stripPrefix(inputs.find(item => item.match(/\.(s?css|sass)$/)))] = outputPath
|
||||
// entrypoints.push([outputPath, fileSize(key)])
|
||||
// } else if (inputs.length > 0) {
|
||||
// // Naive implementation, we'll just grab the first input and hope it's accurate
|
||||
// manifest[stripPrefix(inputs[0])] = outputPath
|
||||
// }
|
||||
// }
|
||||
|
||||
// const manifestFolder = path.join(process.cwd(), ".bridgetown-cache", "frontend-bundling")
|
||||
// await fs.mkdir(manifestFolder, { recursive: true })
|
||||
// await fs.writeFile(path.join(manifestFolder, "manifest.json"), JSON.stringify(manifest))
|
||||
|
||||
console.log("esbuild: frontend bundling complete!")
|
||||
console.log("esbuild: entrypoints processed:")
|
||||
entrypoints.forEach(entrypoint => {
|
||||
const [entrypointName, entrypointSize] = entrypoint
|
||||
console.log(` - ${entrypointName}: ${entrypointSize}`)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Load the PostCSS config from postcss.config.js or whatever else is a supported location/format
|
||||
const postcssrc = require("postcss-load-config")
|
||||
const postCssConfig = postcssrc.sync()
|
||||
|
||||
module.exports = async (outputFolder, esbuildOptions) => {
|
||||
esbuildOptions.plugins = esbuildOptions.plugins || []
|
||||
// Add the PostCSS & glob plugins to the top of the plugin stack
|
||||
esbuildOptions.plugins.unshift(postCssPlugin(postCssConfig, esbuildOptions.postCssPluginConfig || {}))
|
||||
if (esbuildOptions.postCssPluginConfig) delete esbuildOptions.postCssPluginConfig
|
||||
esbuildOptions.plugins.unshift(importGlobPlugin())
|
||||
// Add the Sass plugin
|
||||
esbuildOptions.plugins.push(sassPlugin(esbuildOptions.sassOptions || {}))
|
||||
// Add the Bridgetown preset
|
||||
esbuildOptions.plugins.push(bridgetownPreset(outputFolder))
|
||||
|
||||
// esbuild, take it away!
|
||||
const esbuild = require("esbuild")
|
||||
|
||||
const watch = process.argv.includes("--watch")
|
||||
|
||||
const config = {
|
||||
bundle: true,
|
||||
loader: {
|
||||
".jpg": "file",
|
||||
".png": "file",
|
||||
".gif": "file",
|
||||
".svg": "file",
|
||||
".woff": "file",
|
||||
".woff2": "file",
|
||||
".ttf": "file",
|
||||
".eot": "file",
|
||||
},
|
||||
resolveExtensions: [".tsx", ".ts", ".jsx", ".js", ".css", ".scss", ".sass", ".json", ".js.rb"],
|
||||
nodePaths: ["frontend/javascript", "frontend/styles"],
|
||||
minify: process.argv.includes("--minify"),
|
||||
sourcemap: true,
|
||||
target: "es2016",
|
||||
entryPoints: ["frontend/javascript/index.js"],
|
||||
entryNames: "[dir]/[name].[hash]",
|
||||
outdir: path.join(process.cwd(), `${outputFolder}/bridgetown/static`),
|
||||
publicPath: "/bridgetown/static",
|
||||
metafile: true,
|
||||
...esbuildOptions,
|
||||
}
|
||||
|
||||
if (watch) {
|
||||
const context = await esbuild.context(config).catch(() => process.exit(1))
|
||||
await context.watch().catch(() => process.exit(1))
|
||||
} else {
|
||||
await esbuild.build(config).catch(() => process.exit(1))
|
||||
}
|
||||
}
|
||||
20
docs/assets/scripts/light-pen/docs/config/initializers.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
Bridgetown.configure do |config|
|
||||
config.url = ENV.fetch("URL", "https://konnorrogers.github.io")
|
||||
config.base_path = ENV.fetch("BASE_PATH", "/light-pen")
|
||||
config.base_url = config.url + config.base_path
|
||||
init :"bridgetown-quick-search"
|
||||
|
||||
# init :ssr
|
||||
# init :"bridgetown-routes"
|
||||
# only :server do
|
||||
# roda do |app|
|
||||
# app.plugin :default_headers,
|
||||
# 'Content-Type'=>'text/html',
|
||||
# 'Strict-Transport-Security'=>'max-age=16070400;',
|
||||
# 'X-Content-Type-Options'=>'nosniff',
|
||||
# 'X-Frame-Options'=>'deny',
|
||||
# 'X-XSS-Protection'=>'1; mode=block',
|
||||
# 'Access-Control-Allow-Origin'=>'*'
|
||||
# end
|
||||
# end
|
||||
end
|
||||
27
docs/assets/scripts/light-pen/docs/config/puma.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
# Puma is a fast, concurrent web server for Ruby & Rack
|
||||
#
|
||||
# Learn more at: https://puma.io
|
||||
#
|
||||
port ENV.fetch("BRIDGETOWN_PORT") { 4000 }
|
||||
|
||||
# You can adjust the number of workers (separate processes) and threads
|
||||
# (per process) based on your production system
|
||||
#
|
||||
if ENV["BRIDGETOWN_ENV"] == "production"
|
||||
workers ENV.fetch("BRIDGETOWN_CONCURRENCY") { 4 }
|
||||
end
|
||||
|
||||
max_threads_count = ENV.fetch("BRIDGETOWN_MAX_THREADS") { 5 }
|
||||
min_threads_count = ENV.fetch("BRIDGETOWN_MIN_THREADS") { max_threads_count }
|
||||
threads min_threads_count, max_threads_count
|
||||
|
||||
# Preload the application for maximum performance
|
||||
#
|
||||
preload_app!
|
||||
|
||||
# Use the Bridgetown logger format
|
||||
#
|
||||
require "bridgetown-core/rack/logger"
|
||||
log_formatter do |msg|
|
||||
Bridgetown::Rack::Logger.message_with_prefix msg
|
||||
end
|
||||
57
docs/assets/scripts/light-pen/docs/esbuild.config.js
Normal file
@@ -0,0 +1,57 @@
|
||||
// @ts-check
|
||||
const { spawn } = require("child_process");
|
||||
|
||||
// const glob = require("glob")
|
||||
const build = require("./config/esbuild.defaults.js")
|
||||
|
||||
const AssetMapper = require("asset-mapper-esbuild").default
|
||||
// Update this if you need to configure a destination folder other than `output`
|
||||
const outputFolder = "src"
|
||||
|
||||
// You can customize this as you wish, perhaps to add new esbuild plugins.
|
||||
//
|
||||
// ```
|
||||
const path = require("path")
|
||||
const esbuildCopy = require('esbuild-plugin-copy').default
|
||||
// const esbuildOptions = {
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// You can also support custom base_path deployments via changing `publicPath`.
|
||||
//
|
||||
// ```
|
||||
// const esbuildOptions = { publicPath: "/my_subfolder/_bridgetown/static" }
|
||||
// ```
|
||||
const watch = process.argv.includes("--watch")
|
||||
|
||||
const esbuildOptions = {
|
||||
target: "es2020",
|
||||
entryPoints: {
|
||||
"javascript/index": "frontend/javascript/index.js",
|
||||
"javascript/defer": "frontend/javascript/defer.js",
|
||||
"light-pen/exports/light-pen": "../exports/light-pen.js",
|
||||
"light-pen/exports/light-preview": "../exports/light-preview.js",
|
||||
},
|
||||
define: {
|
||||
"process.env.BASE_PATH": `"${process.env.BASE_PATH}"`
|
||||
},
|
||||
publicPath: path.join(process.env.BASE_PATH, "bridgetown", "static"),
|
||||
outdir: path.join(process.cwd(), outputFolder, "bridgetown", "static"),
|
||||
splitting: true,
|
||||
format: "esm",
|
||||
plugins: [
|
||||
esbuildCopy({
|
||||
assets: {
|
||||
from: [path.resolve(__dirname, 'node_modules/@shoelace-style/shoelace/dist/assets/icons/**/*.svg')],
|
||||
to: [path.resolve(__dirname, 'src/shoelace-assets/assets/icons')],
|
||||
},
|
||||
verbose: false
|
||||
}),
|
||||
AssetMapper({
|
||||
manifestFile: path.join(process.cwd(), ".bridgetown-cache", "asset-mapper-manifest.json"),
|
||||
// outputRoot: path.join(process.cwd(), process.env.BASE_PATH)
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
build(outputFolder, esbuildOptions)
|
||||
@@ -0,0 +1,21 @@
|
||||
// @ts-check
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
export default class ClipboardController extends Controller {
|
||||
connect () {
|
||||
this.element.addEventListener("clipboard-copy", this.showSuccess)
|
||||
}
|
||||
|
||||
showSuccess = () => {
|
||||
this.element.classList.add("clipboard--success")
|
||||
this.element.classList.remove("clipboard--idle")
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout)
|
||||
}
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
this.element.classList.remove("clipboard--success")
|
||||
this.element.classList.add("clipboard--idle")
|
||||
}, 2_000)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
export default class extends Controller {
|
||||
connect() {
|
||||
// console.log("Hello, Stimulus!", this.element)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
export default class ScrollSpyController extends Controller {
|
||||
connect () {
|
||||
this.observer = new IntersectionObserver(this.handleIntersect, { rootMargin: '0px 0px' });
|
||||
|
||||
this.linkMap = new WeakMap();
|
||||
this.visibleSet = new WeakSet();
|
||||
|
||||
this.observeLinks()
|
||||
this.updateActiveLinks()
|
||||
|
||||
this.selector = ["1","2","3","4","5","6"].map((str) => "h" + str + "[id]").join(",")
|
||||
document.querySelectorAll(this.selector).forEach((header) => {
|
||||
this.observer.observe(header);
|
||||
});
|
||||
|
||||
document.addEventListener("turbo:load", this.observeLinks)
|
||||
document.addEventListener("turbo:load", this.updateActiveLinks)
|
||||
|
||||
this.observeLinks()
|
||||
this.updateActiveLinks()
|
||||
}
|
||||
|
||||
disconnect () {
|
||||
this.observer.disconnect()
|
||||
}
|
||||
|
||||
get links () {
|
||||
return [...document.querySelectorAll('#table-of-contents li a')];
|
||||
}
|
||||
|
||||
handleIntersect = (entries) => {
|
||||
entries.forEach(entry => {
|
||||
// Remember which targets are visible
|
||||
if (entry.isIntersecting) {
|
||||
this.visibleSet.add(entry.target);
|
||||
} else {
|
||||
this.visibleSet.delete(entry.target);
|
||||
}
|
||||
});
|
||||
|
||||
this.updateActiveLinks();
|
||||
}
|
||||
|
||||
updateActiveLinks = () => {
|
||||
const links = this.links;
|
||||
// Find the first visible target and activate the respective link
|
||||
links.find(link => {
|
||||
const target = this.linkMap.get(link);
|
||||
|
||||
if (target && this.visibleSet.has(target)) {
|
||||
links.forEach(el => el.parentElement.classList.toggle('is-active', el === link));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
observeLinks = () => {
|
||||
this.links.forEach(link => {
|
||||
const hash = link.hash.slice(1);
|
||||
const target = hash ? document.querySelector(`main #${hash}`) : null;
|
||||
|
||||
if (target) {
|
||||
this.linkMap.set(link, target);
|
||||
this.observer.observe(target);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
export default class SearchController extends Controller {
|
||||
show () {
|
||||
document.querySelector("bridgetown-ninja-keys").open()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
export default class SideNavController extends Controller {
|
||||
async open() {
|
||||
document.addEventListener("sl-hide", this.reset)
|
||||
this.drawer.removeAttribute("hidden")
|
||||
|
||||
this.scrollTop = window.scrollY;
|
||||
document.body.classList.add('fixed-body');
|
||||
// Scroll the wrapper, rather than setting an offset
|
||||
// via `top` or `transform`.
|
||||
document.body.scroll(0, this.scrollTop);
|
||||
await this.drawer.show()
|
||||
}
|
||||
|
||||
reset = () => {
|
||||
window.scrollTo(0, this.scrollTop);
|
||||
document.body.classList.remove('fixed-body');
|
||||
}
|
||||
|
||||
async close () {
|
||||
document.removeEventListener("sl-hide", this.reset)
|
||||
this.reset()
|
||||
await this.drawer.hide()
|
||||
}
|
||||
|
||||
async toggle() {
|
||||
if (this.drawer.open) {
|
||||
await this.open()
|
||||
} else {
|
||||
await this.close()
|
||||
}
|
||||
}
|
||||
|
||||
get drawer() {
|
||||
return document.querySelector("#side-nav-drawer")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
|
||||
export default class ThemeSwitcher extends Controller {
|
||||
constructor (...args) {
|
||||
super(...args)
|
||||
|
||||
this.handleSelect = (e) => {
|
||||
window.applyTheme(e.detail.item.value)
|
||||
}
|
||||
|
||||
this.handleShortcut = (event) => {
|
||||
if (
|
||||
event.key === '\\' &&
|
||||
!event.composedPath().some(el => {
|
||||
return (
|
||||
['input', 'textarea'].includes(el?.tagName?.toLowerCase()) ||
|
||||
el.hasAttribute?.("contenteditable") ||
|
||||
el.getAttribute?.("role") === "textbox"
|
||||
)
|
||||
})
|
||||
) {
|
||||
event.preventDefault();
|
||||
|
||||
window.applyTheme(window.themeIsDark() ? 'light' : 'dark');
|
||||
}
|
||||
}
|
||||
|
||||
this.setLight = () => {
|
||||
window.applyTheme("light")
|
||||
}
|
||||
|
||||
this.setDark = () => {
|
||||
window.applyTheme("dark")
|
||||
}
|
||||
|
||||
// Set the initial theme and sync the UI
|
||||
window.applyTheme(window.getTheme());
|
||||
}
|
||||
|
||||
connect () {
|
||||
this.element.addEventListener("sl-select", this.handleSelect)
|
||||
document.addEventListener("keydown", this.handleShortcut)
|
||||
|
||||
// Update the theme when the preference changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', this.setDark);
|
||||
window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', this.setLight);
|
||||
|
||||
// Set the initial theme and sync the UI
|
||||
window.applyTheme(window.getTheme());
|
||||
}
|
||||
|
||||
disconnect () {
|
||||
this.element.removeEventListener("sl-select", this.handleSelect)
|
||||
document.removeEventListener("keydown", this.handleShortcut)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import "../styles/defer.css"
|
||||
import { BridgetownNinjaKeys } from "@konnorr/bridgetown-quick-search/ninja-keys.js"
|
||||
|
||||
;(window.requestIdleCallback || window.setTimeout)(async () => {
|
||||
// const { BridgetownNinjaKeys } = await import("@konnorr/bridgetown-quick-search/ninja-keys.js")
|
||||
|
||||
/** @type {import("konnors-ninja-keys").INinjaAction[]} */
|
||||
const staticData = [
|
||||
{
|
||||
id: "theme-light",
|
||||
icon: "<sl-icon name='sun'></sl-icon>",
|
||||
title: "Light Mode",
|
||||
section: "Theme",
|
||||
keywords: "theme",
|
||||
handler () {
|
||||
window.applyTheme("light");
|
||||
return {keepOpen: true}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "theme-dark",
|
||||
icon: "<sl-icon name='moon'></sl-icon>",
|
||||
title: "Dark Mode",
|
||||
section: "Theme",
|
||||
keywords: "theme",
|
||||
handler () {
|
||||
window.applyTheme("dark");
|
||||
return {keepOpen: true}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "theme-system",
|
||||
icon: "<sl-icon name='display'></sl-icon>",
|
||||
title: "System",
|
||||
section: "Theme",
|
||||
keywords: "theme",
|
||||
handler () {
|
||||
window.applyTheme("system");
|
||||
return {keepOpen: true}
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
;(class extends BridgetownNinjaKeys {
|
||||
constructor (...args) {
|
||||
super(...args)
|
||||
this.staticData = staticData
|
||||
}
|
||||
|
||||
createData() {
|
||||
this.results = this.showResultsForQuery(this._search || "*").reverse()
|
||||
|
||||
this.results.forEach((result) => {
|
||||
result.icon = `<sl-icon name="link-45deg"></sl-icon>`
|
||||
})
|
||||
|
||||
return [
|
||||
...this.staticData,
|
||||
...this.results,
|
||||
]
|
||||
}
|
||||
|
||||
open () {
|
||||
this.scrollTop = window.scrollY;
|
||||
document.body.classList.add('fixed-body');
|
||||
// Scroll the wrapper, rather than setting an offset
|
||||
// via `top` or `transform`.
|
||||
document.body.scroll(0, this.scrollTop);
|
||||
|
||||
this.nonModals.forEach((el) => {
|
||||
el.setAttribute("inert", "")
|
||||
})
|
||||
super.open()
|
||||
}
|
||||
|
||||
close () {
|
||||
document.body.classList.remove('fixed-body');
|
||||
window.scrollTo(0, this.scrollTop);
|
||||
super.close()
|
||||
this.nonModals.forEach((el) => el.removeAttribute("inert"))
|
||||
}
|
||||
|
||||
get nonModals () {
|
||||
return [...document.body.children].filter((el) => el.localName !== "bridgetown-ninja-keys")
|
||||
}
|
||||
}).define("bridgetown-ninja-keys")
|
||||
})
|
||||
@@ -0,0 +1,61 @@
|
||||
import "../styles/index.css"
|
||||
import { Application } from "@hotwired/stimulus"
|
||||
|
||||
// Shoelace
|
||||
import { setBasePath } from "@shoelace-style/shoelace/dist/utilities/base-path.js";
|
||||
|
||||
import LazyLoader from "./src/lazy-loader.js"
|
||||
|
||||
import * as Turbo from "@hotwired/turbo"
|
||||
window.Turbo = Turbo
|
||||
import "./src/layout.js"
|
||||
//
|
||||
LazyLoader()
|
||||
//
|
||||
setBasePath(process.env.BASE_PATH + "/shoelace-assets")
|
||||
|
||||
// Import all JavaScript & CSS files from src/_components
|
||||
import components from "bridgetownComponents/**/*.{js,jsx,js.rb,css}"
|
||||
|
||||
window.Stimulus = Application.start()
|
||||
|
||||
import controllers from "./controllers/**/*.{js,js.rb}"
|
||||
Object.entries(controllers).forEach(([filename, controller]) => {
|
||||
if (filename.includes("_controller.") || filename.includes("-controller.")) {
|
||||
const identifier = filename.replace("./controllers/", "")
|
||||
.replace(/[_-]controller..*$/, "")
|
||||
.replace("_", "-")
|
||||
.replace("/", "--")
|
||||
|
||||
Stimulus.register(identifier, controller.default)
|
||||
}
|
||||
})
|
||||
|
||||
;(() => {
|
||||
if (!window.scrollPositions) {
|
||||
window.scrollPositions = {};
|
||||
}
|
||||
|
||||
function preserveScroll() {
|
||||
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
|
||||
scrollPositions[element.id] = element.scrollTop;
|
||||
});
|
||||
}
|
||||
|
||||
function restoreScroll(event) {
|
||||
if (event.detail && event.detail.newBody) {
|
||||
event.detail.newBody.querySelectorAll('[data-preserve-scroll]').forEach(element => {
|
||||
element.scrollTop = scrollPositions[element.id];
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
|
||||
element.scrollTop = scrollPositions[element.id];
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
window.addEventListener('turbo:before-cache', preserveScroll);
|
||||
window.addEventListener('turbo:before-render', restoreScroll);
|
||||
window.addEventListener('turbo:render', restoreScroll);
|
||||
})();
|
||||
@@ -0,0 +1,10 @@
|
||||
class ExternalIcon extends HTMLElement {
|
||||
connectedCallback () {
|
||||
this.attachShadow({ mode: "open" })
|
||||
this.shadowRoot.innerHTML = `<sl-icon name="box-arrow-up-right"></sl-icon><sl-visually-hidden>Opens in new window</sl-visually-hidden>`
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.customElements.get("external-icon")) {
|
||||
window.customElements.define("external-icon", ExternalIcon)
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
import {LitElement, html, css} from "lit"
|
||||
|
||||
class KrLayout extends LitElement {
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
min-height: var(--height);
|
||||
--height: 100vh;
|
||||
--height: 100dvh;
|
||||
|
||||
--menu-width: auto;
|
||||
--main-width: 1fr;
|
||||
--aside-width: auto;
|
||||
|
||||
/** This is a best guess. We'll attempt to calculate this with a resize observer. **/
|
||||
--header-height: 68.33px;
|
||||
}
|
||||
|
||||
:host([variant="documentation"]) {
|
||||
--menu-width: 250px;
|
||||
--main-width: 105ch;
|
||||
--aside-width: auto;
|
||||
}
|
||||
|
||||
:host([variant="documentation"])::part(body) {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
*, *:after, *:before {
|
||||
box-sizing: border-box;;
|
||||
}
|
||||
|
||||
[part~="base"] {
|
||||
display: grid;
|
||||
/** Header, Main, Footer **/
|
||||
grid-template-rows: minmax(0, auto) minmax(0, 1fr) minmax(0, auto);
|
||||
min-height: var(--height);
|
||||
}
|
||||
|
||||
[part~="header"] {
|
||||
max-width: 100%;
|
||||
position: sticky;
|
||||
background: white;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
:is(.header, .aside, .menu, .footer) ::slotted(*) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:is(.aside, .menu) ::slotted(*) {
|
||||
min-width: 100%;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
[part~="header"] {
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
[part~="body"] {
|
||||
display: grid;
|
||||
/** Menu, Main, Aside **/
|
||||
grid-template-columns: minmax(0, var(--menu-width)) minmax(0, var(--main-width)) minmax(0, var(--aside-width));
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
[part~="aside"],
|
||||
[part~="menu"] {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
max-height: calc(var(--height) - var(--header-height));
|
||||
overflow: auto;
|
||||
position: sticky;
|
||||
top: var(--header-height);
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
[part~="main"] {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
/* main-header, main, main-footer */
|
||||
grid-template-rows: minmax(0, auto) minmax(0, 1fr) minmax(0, auto);
|
||||
}
|
||||
|
||||
[part~="footer"] {}
|
||||
|
||||
sl-visually-hidden:not(:focus-within) {
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
clip: rect(0 0 0 0) !important;
|
||||
clip-path: inset(50%) !important;
|
||||
border: none !important;
|
||||
overflow: hidden !important;
|
||||
white-space: nowrap !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.skip-links {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: calc(var(--header-height, 48px) - 2px);
|
||||
width: 100vw;
|
||||
z-index: 4;
|
||||
background-color: inherit;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
place-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
`
|
||||
|
||||
static properties = {
|
||||
main_id: { attribute: "main-id", reflect: true }
|
||||
}
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
this.main_id = "main"
|
||||
}
|
||||
|
||||
createResizeObserver (slot) {
|
||||
return new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.contentBoxSize) {
|
||||
const contentBoxSize = entry.borderBoxSize[0];
|
||||
this.style.setProperty(`--${slot}-height`, `${contentBoxSize.blockSize}px`)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
connectedCallback () {
|
||||
super.connectedCallback?.()
|
||||
|
||||
this.headerResizeObserver = this.createResizeObserver("header");
|
||||
this.footerResizeObserver = this.createResizeObserver("footer");
|
||||
|
||||
setTimeout(() => {
|
||||
this.header = this.shadowRoot.querySelector("[part~='header']")
|
||||
this.headerResizeObserver.observe(this.header)
|
||||
|
||||
// this.footer = this.shadowRoot.querySelector("[part~='main-footer']")
|
||||
// this.footerResizeObserver.observe(this.footer)
|
||||
})
|
||||
}
|
||||
|
||||
disconnectedCallback () {
|
||||
super.disconnectedCallback?.()
|
||||
this.headerResizeObserver.unobserve(this.header)
|
||||
// this.footerResizeObserver.unobserve(this.footer)
|
||||
}
|
||||
|
||||
render () {
|
||||
return html`
|
||||
<sl-visually-hidden class="skip-links" part="skip-links">
|
||||
<slot name="skip-links">
|
||||
<a href=${`#${this.main_id}`} part="skip-link">
|
||||
${this.skipToMain || "Skip to main"}
|
||||
</a>
|
||||
</slot>
|
||||
</sl-visually-hidden>
|
||||
|
||||
<div class="base" part="base">
|
||||
<div class="header" part="header">
|
||||
<slot name="header"></slot>
|
||||
</div>
|
||||
|
||||
<div class="body" part="body">
|
||||
<div class="menu" part="menu">
|
||||
<slot name="menu"></slot>
|
||||
</div>
|
||||
|
||||
<div class="main" part="main">
|
||||
<div class="main-header" part="main-header">
|
||||
<slot name="main-header"></slot>
|
||||
</div>
|
||||
|
||||
<div class="main-content" part="main-content"><slot></slot></div>
|
||||
|
||||
<div class="main-footer" part="main-footer">
|
||||
<slot name="main-footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aside" part="aside">
|
||||
<slot name="aside"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer" part="footer">
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div part="dialog" class="dialog">
|
||||
<slot name="dialog"></slot>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.customElements.get("kr-layout")) {
|
||||
window.customElements.define("kr-layout", KrLayout)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import LazyLoader from "web-component-lazy-loader";
|
||||
|
||||
export default function lazyLoader() {
|
||||
return new LazyLoader({
|
||||
components: {
|
||||
"light-pen": {
|
||||
register () {
|
||||
import("light-pen/exports/light-pen-register.js")
|
||||
}
|
||||
},
|
||||
"light-preview": {
|
||||
register () {
|
||||
import("light-pen/exports/light-preview-register.js")
|
||||
}
|
||||
},
|
||||
"clipboard-copy": {
|
||||
register() {
|
||||
import("@github/clipboard-copy-element");
|
||||
},
|
||||
},
|
||||
"external-icon": {
|
||||
register() {
|
||||
import("./external-icon.js");
|
||||
},
|
||||
},
|
||||
|
||||
// Shoelace
|
||||
"sl-alert": {
|
||||
register() {
|
||||
import("@shoelace-style/shoelace/dist/components/alert/alert.js");
|
||||
},
|
||||
},
|
||||
// "sl-breadcrumb": {
|
||||
// register () { import("@shoelace-style/shoelace/dist/components/breadcrumb/breadcrumb.js"); }
|
||||
// },
|
||||
// "sl-breadcrumb-item": {
|
||||
// register () { import("@shoelace-style/shoelace/dist/components/breadcrumb-item/breadcrumb-item.js"); }
|
||||
// },
|
||||
"sl-button": {
|
||||
register() {
|
||||
import("@shoelace-style/shoelace/dist/components/button/button.js");
|
||||
},
|
||||
},
|
||||
"sl-divider": {
|
||||
register() {
|
||||
import("@shoelace-style/shoelace/dist/components/divider/divider.js");
|
||||
},
|
||||
},
|
||||
"sl-drawer": {
|
||||
register() {
|
||||
import("@shoelace-style/shoelace/dist/components/drawer/drawer.js");
|
||||
},
|
||||
},
|
||||
"sl-dropdown": {
|
||||
register() {
|
||||
import(
|
||||
"@shoelace-style/shoelace/dist/components/dropdown/dropdown.js"
|
||||
);
|
||||
},
|
||||
},
|
||||
"sl-icon": {
|
||||
register() {
|
||||
import("@shoelace-style/shoelace/dist/components/icon/icon.js");
|
||||
},
|
||||
},
|
||||
"sl-icon-button": {
|
||||
register() {
|
||||
import(
|
||||
"@shoelace-style/shoelace/dist/components/icon-button/icon-button.js"
|
||||
);
|
||||
},
|
||||
},
|
||||
"sl-menu": {
|
||||
register() {
|
||||
import("@shoelace-style/shoelace/dist/components/menu/menu.js");
|
||||
},
|
||||
},
|
||||
"sl-menu-item": {
|
||||
register() {
|
||||
import(
|
||||
"@shoelace-style/shoelace/dist/components/menu-item/menu-item.js"
|
||||
);
|
||||
},
|
||||
},
|
||||
"sl-menu-label": {
|
||||
register() {
|
||||
import(
|
||||
"@shoelace-style/shoelace/dist/components/menu-label/menu-label.js"
|
||||
);
|
||||
},
|
||||
},
|
||||
"sl-visually-hidden": {
|
||||
register() {
|
||||
import(
|
||||
"@shoelace-style/shoelace/dist/components/visually-hidden/visually-hidden.js"
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
}).start();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
@import "./components/_alert.css";
|
||||
@import "./components/_aspect_ratio.css";
|
||||
@import "./components/_blog.css";
|
||||
@import "./components/_button.css";
|
||||
@import "./components/_call_to_action.css";
|
||||
@import "./components/_clipboard.css";
|
||||
@import "./components/_contact.css";
|
||||
@import "./components/_footer.css";
|
||||
@import "./components/_header.css";
|
||||
@import "./components/_hero.css";
|
||||
@import "./components/_input.css";
|
||||
@import "./components/_layout.css";
|
||||
@import "./components/_list.css";
|
||||
@import "./components/_link.css";
|
||||
@import "./components/_logo.css";
|
||||
@import "./components/_main_list.css";
|
||||
@import "./components/_pagination.css";
|
||||
@import "./components/_side_nav.css";
|
||||
@import "./components/_syntax_block.css";
|
||||
@import "./components/_table_of_contents.css";
|
||||
@import "./components/_text.css";
|
||||
@import "./components/_theme_switcher.css";
|
||||
@import "./components/_top_nav.css";
|
||||
@import "./components/_tables.css";
|
||||
@@ -0,0 +1,42 @@
|
||||
/** For for prose sections, blogging concerns, Markdown, etc. */
|
||||
|
||||
/** Pretty syntax blocks */
|
||||
pre.highlight {
|
||||
padding: 0.85rem 1rem;
|
||||
margin: 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
code
|
||||
.highlighter-rouge,
|
||||
.highlight {
|
||||
border-radius: 8px;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.4;
|
||||
font-family: var(--sl-font-mono);
|
||||
}
|
||||
|
||||
code,
|
||||
.highlight,
|
||||
.highlighter-rouge {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Inline code blocks */
|
||||
code,
|
||||
code.highlight,
|
||||
code.highlighter-rouge {
|
||||
/** Layout */
|
||||
display: inline-block;
|
||||
font-size: 0.85em;
|
||||
padding: 0.1em 0.4em;
|
||||
margin: 4px 0;
|
||||
border-radius: 6px;
|
||||
white-space: break-spaces;
|
||||
vertical-align: middle;
|
||||
|
||||
/* Decoration */
|
||||
background-color: var(--code-background-color);
|
||||
color: var(--code-color);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
*, *:before, *:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 100%;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeSpeed;
|
||||
scrollbar-gutter: stable;
|
||||
font-family: var(--sl-font-sans);
|
||||
background-color: var(--body-color);
|
||||
color: var(--text-color);
|
||||
display: grid;
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--sl-font-sans);
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
appearance: none;
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h1,h2,h3,h4,h5,h6 {
|
||||
scroll-margin-top: calc(80px + 2em);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-top: var(--sl-spacing-large);
|
||||
margin-bottom: var(--sl-spacing-small);
|
||||
}
|
||||
|
||||
main p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.25rem;
|
||||
margin: 0;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
h3, h4 {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
h5, h6 {
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p, li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
line-height: 1.8;
|
||||
list-style: disc outside;
|
||||
}
|
||||
|
||||
ul ul {
|
||||
list-style: circle outside;
|
||||
}
|
||||
|
||||
ul ul ul {
|
||||
list-style: square outside;
|
||||
}
|
||||
|
||||
ul ul ul ul {
|
||||
list-style: disc outside;
|
||||
}
|
||||
|
||||
[tabindex="-1"] {
|
||||
outline: transparent;
|
||||
}
|
||||
|
||||
a,
|
||||
kr-layout::part(skip-link) {
|
||||
word-wrap: anywhere;
|
||||
/* outline: transparent; */
|
||||
/* Unable to distinguish identifier, this makes links obvious */
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: solid;
|
||||
text-decoration-color: var(--link-color);
|
||||
text-underline-offset: 0.25em;
|
||||
color: var(--link-color);
|
||||
border-radius: 2px;
|
||||
display: inline;
|
||||
padding: 0.25em;
|
||||
margin: -0.25em;
|
||||
line-height: 1.2;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
kr-layout::part(skip-link):is(:hover),
|
||||
a:is(:hover) {
|
||||
color: var(--link-color-focus);
|
||||
}
|
||||
|
||||
|
||||
kr-layout::part(skip-link):is(:focus-visible),
|
||||
a:is(:focus-visible) {
|
||||
box-shadow: 0px 0px 3px 3px var(--sl-color-primary-600);
|
||||
outline: transparent;
|
||||
/* for when Safari supports border-radius */
|
||||
/* outline: 3px solid var(--sl-color-primary-600); */
|
||||
/* outline-offset: 4px; */
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
kr-layout::part(skip-link):is(:focus),
|
||||
a:is(:focus) {
|
||||
box-shadow: 0px 0px 3px 3px var(--sl-color-primary-600);
|
||||
outline: transparent;
|
||||
/* for when Safari supports border-radius */
|
||||
/* outline: 3px solid var(--sl-color-primary-600); */
|
||||
/* outline-offset: 4px; */
|
||||
}
|
||||
}
|
||||
|
||||
code > span {
|
||||
line-height: var(--sl-line-height-dense);
|
||||
}
|
||||
|
||||
kbd {
|
||||
font-family: var(--sl-font-mono);
|
||||
font-size: 87.5%;
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
border-radius: 4px;
|
||||
border: solid 1px var(--sl-color-neutral-200);
|
||||
box-shadow: inset 0 1px 0 var(--sl-color-neutral-0);
|
||||
padding: 2px 5px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
a external-icon {
|
||||
padding-top: 0.25em;
|
||||
font-size: 0.75em;
|
||||
margin-inline-start: -0.15em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-inline-start: 4px solid var(--divider-color);
|
||||
margin-inline-start: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
background-color: var(--sl-color-neutral-50);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
@import "./overrides/bridgetown-ninja-keys.css";
|
||||
@import "./overrides/shoelace.css";
|
||||