Compare commits

..

60 Commits

Author SHA1 Message Date
konnorrogers
9e43e65b6c try with manual writing 2023-10-20 15:42:55 -04:00
konnorrogers
26d83f37d6 with different base settings 2023-10-20 15:29:33 -04:00
konnorrogers
a751053803 try with all sandbox settings on 2023-10-20 15:26:35 -04:00
konnorrogers
44a7dd5089 try 1.0.1 2023-10-20 15:03:01 -04:00
konnorrogers
902ab85f32 fix playgrounds 2023-10-20 14:24:27 -04:00
konnorrogers
21647001a4 update light-preview 2023-10-20 14:04:27 -04:00
konnorrogers
a0ee2256c4 update light-pen 2023-10-20 12:09:02 -04:00
konnorrogers
51f5c30526 update sport awesome, rename to .html and .css 2023-10-20 11:58:18 -04:00
konnorrogers
9e0aa9c2ec move flow to sport-awesome 2023-10-19 14:36:52 -04:00
konnorrogers
15a9c63040 Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers/layouts 2023-10-19 12:54:04 -04:00
konnorrogers
a1a09a2b2b finish up sport awesome 2023-10-18 16:10:18 -04:00
konnorrogers
1df6afa541 finish up sport awesome 2023-10-18 13:49:09 -04:00
konnorrogers
d4e2abe218 Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers/layouts 2023-10-18 11:24:46 -04:00
konnorrogers
ac94d838f0 continued work on sportawesome 2023-10-16 16:31:50 -04:00
konnorrogers
23a0f4afdc working on sportawesome 2023-10-16 12:26:46 -04:00
konnorrogers
00f23c485d update layout stuff 2023-10-13 10:59:19 -04:00
konnorrogers
7a31446162 add disable-sticky 2023-10-12 12:55:33 -04:00
konnorrogers
11893fe80c Merge branch 'next' into konnorrogers/layouts 2023-10-12 12:18:10 -04:00
konnorrogers
e23adf4d11 use toggle-navigation 2023-10-12 12:15:24 -04:00
konnorrogers
739a6033af more notes to layout 2023-10-04 16:21:19 -04:00
konnorrogers
ed43baa459 update to use <current> on the nav-item 2023-10-04 15:58:58 -04:00
konnorrogers
9cbc27a6ef refactor nav-item to use a 'string' for 'current' 2023-10-04 15:52:38 -04:00
konnorrogers
b017c4df1e add support for data-wa-layout-navigation-toggle 2023-10-02 16:14:23 -04:00
konnorrogers
27aaa82a9c Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers/layouts 2023-10-02 14:45:02 -04:00
konnorrogers
57194ed12d readd nav button 2023-10-02 12:20:29 -04:00
konnorrogers
74d5b4c3f4 remove navigation button 2023-10-02 11:58:56 -04:00
konnorrogers
e95bfab77b update 2023-09-29 10:20:27 -04:00
konnorrogers
78785b872d Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers/layouts 2023-09-29 10:19:38 -04:00
konnorrogers
6f0c41cddf apply lindsay's tweaks 2023-09-28 17:29:40 -04:00
konnorrogers
52bda73657 apply lindsay's tweaks 2023-09-28 16:53:29 -04:00
konnorrogers
aaf845c72a prettier 2023-09-25 16:25:02 -04:00
konnorrogers
fc66179dc0 fix react examples 2023-09-25 16:07:16 -04:00
konnorrogers
67702b8d89 more nav-group / nav-item fixes 2023-09-25 12:25:06 -04:00
konnorrogers
81ff1422e8 working on nav group / nav-items 2023-09-22 14:26:25 -04:00
konnorrogers
27984299e0 remove align-items: stretch 2023-09-21 12:58:55 -04:00
konnorrogers
0cac319988 working on nav group / nav-items 2023-09-21 12:52:41 -04:00
konnorrogers
0509fb041f Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers/layouts 2023-09-21 10:05:16 -04:00
konnorrogers
32bc7f2207 more work on nav items 2023-09-20 18:14:25 -04:00
konnorrogers
7c73b6a458 Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers/layouts 2023-09-20 14:23:28 -04:00
konnorrogers
6b579a6946 move to nav-item + nav-group 2023-09-19 17:45:11 -04:00
konnorrogers
896fe76a8d continued layout work 2023-09-18 16:35:05 -04:00
konnorrogers
ed1621410b update to example.njk 2023-09-15 14:08:49 -04:00
konnorrogers
29e591e69b Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers/layouts 2023-09-15 12:56:59 -04:00
konnorrogers
396e632679 working on navigation 2023-09-15 12:36:16 -04:00
konnorrogers
316f8eb16d use brand button 2023-09-14 14:14:55 -04:00
konnorrogers
131b1ee57e adding layout examples 2023-09-14 14:10:43 -04:00
konnorrogers
75004768bb push up docs 2023-09-13 15:51:39 -04:00
konnorrogers
c8067674f6 fix doc layouts 2023-09-12 17:16:57 -04:00
konnorrogers
caf4dc5526 working on semantic tokens 2023-09-12 16:24:40 -04:00
konnorrogers
93841348e1 Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers/layouts 2023-09-12 14:46:53 -04:00
konnorrogers
8ad392a5ac update to WA3 2023-09-12 14:35:03 -04:00
konnorrogers
6fc8a5166e Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers/layouts 2023-09-11 15:46:09 -04:00
konnorrogers
0e27e1dd3d update to latest light-pen 2023-09-07 13:05:22 -04:00
Konnor Rogers
1b33f38280 use <script text/plain 2023-09-02 09:59:59 -04:00
Konnor Rogers
340869fa91 try with new setup 2023-09-02 01:20:54 -04:00
konnorrogers
06ff11114a fix templates 2023-08-30 16:35:01 -04:00
konnorrogers
ebe30a5ce8 fix duplicate binding 2023-08-30 15:56:47 -04:00
konnorrogers
4c84dec601 rename to .njk 2023-08-30 15:53:13 -04:00
konnorrogers
ece156de0b fix templates 2023-08-30 15:51:02 -04:00
konnorrogers
191f7d708c create layouts 2023-08-30 15:28:33 -04:00
4069 changed files with 138018 additions and 75433 deletions

10
.eslintignore Normal file
View File

@@ -0,0 +1,10 @@
.cache
docs/dist
docs/search.json
docs/**/*.min.js
dist
examples
node_modules
src/react
scripts

211
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,211 @@
/* eslint-env node */
module.exports = {
plugins: [
'@typescript-eslint',
'wc',
'lit',
'lit-a11y',
'chai-expect',
'chai-friendly',
'import',
'sort-imports-es6-autofix'
],
extends: [
'eslint:recommended',
'plugin:wc/recommended',
'plugin:wc/best-practice',
'plugin:lit/recommended',
'plugin:lit-a11y/recommended'
],
env: {
es2021: true,
browser: true
},
parserOptions: {
sourceType: 'module'
},
overrides: [
{
extends: [
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking'
],
parser: '@typescript-eslint/parser',
parserOptions: {
sourceType: 'module',
project: './tsconfig.json',
tsconfigRootDir: __dirname
},
files: ['*.ts'],
rules: {
'default-param-last': 'off',
'@typescript-eslint/default-param-last': 'error',
'no-empty-function': 'off',
'@typescript-eslint/no-empty-function': 'warn',
'no-implied-eval': 'off',
'@typescript-eslint/no-implied-eval': 'error',
'no-invalid-this': 'off',
'@typescript-eslint/no-invalid-this': 'error',
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'error',
'no-throw-literal': 'off',
'@typescript-eslint/no-throw-literal': 'error',
'no-unused-expressions': 'off',
'@typescript-eslint/prefer-regexp-exec': 'off',
'@typescript-eslint/no-unused-expressions': 'error',
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/no-misused-promises': [
'error',
{
checksVoidReturn: false
}
],
'@typescript-eslint/consistent-type-assertions': [
'warn',
{
assertionStyle: 'as',
objectLiteralTypeAssertions: 'never'
}
],
'@typescript-eslint/consistent-type-imports': 'warn',
'@typescript-eslint/no-base-to-string': 'error',
'@typescript-eslint/no-confusing-non-null-assertion': 'error',
'@typescript-eslint/no-invalid-void-type': 'error',
'@typescript-eslint/no-require-imports': 'error',
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'warn',
'@typescript-eslint/no-unnecessary-condition': 'off',
'@typescript-eslint/no-unnecessary-qualifier': 'warn',
'@typescript-eslint/non-nullable-type-assertion-style': 'warn',
'@typescript-eslint/prefer-for-of': 'warn',
'@typescript-eslint/prefer-optional-chain': 'warn',
'@typescript-eslint/prefer-ts-expect-error': 'warn',
'@typescript-eslint/prefer-return-this-type': 'error',
'@typescript-eslint/prefer-string-starts-ends-with': 'warn',
'@typescript-eslint/require-array-sort-compare': 'error',
'@typescript-eslint/unified-signatures': 'warn',
'@typescript-eslint/array-type': 'warn',
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
'@typescript-eslint/member-delimiter-style': 'warn',
'@typescript-eslint/method-signature-style': 'warn',
'@typescript-eslint/no-extraneous-class': 'error',
'@typescript-eslint/no-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'],
rules: {
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unused-expressions': 'off'
}
}
],
rules: {
'no-template-curly-in-string': 'error',
'array-callback-return': 'error',
'comma-dangle': 'off',
'consistent-return': 'error',
curly: 'off',
'default-param-last': 'error',
eqeqeq: 'error',
'lit-a11y/click-events-have-key-events': 'off',
'no-constructor-return': 'error',
'no-empty-function': 'warn',
'no-eval': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'error',
'no-floating-decimal': 'error',
'no-implicit-coercion': 'off',
'no-implicit-globals': 'error',
'no-implied-eval': 'error',
'no-invalid-this': 'error',
'no-labels': 'error',
'no-lone-blocks': 'error',
'no-new': 'error',
'no-new-func': 'error',
'no-new-wrappers': 'error',
'no-octal-escape': 'error',
'no-proto': 'error',
'no-return-assign': 'warn',
'no-script-url': 'error',
'no-self-compare': 'warn',
'no-sequences': 'warn',
'no-throw-literal': 'error',
'no-unmodified-loop-condition': 'error',
'no-unused-expressions': 'warn',
'no-useless-call': 'error',
'no-useless-concat': 'error',
'no-useless-return': 'warn',
'prefer-promise-reject-errors': 'error',
radix: 'off',
'require-await': 'error',
'wrap-iife': ['warn', 'inside'],
'no-shadow': 'error',
'no-array-constructor': 'error',
'no-bitwise': 'error',
'no-multi-assign': 'warn',
'no-new-object': 'error',
'no-useless-computed-key': 'warn',
'no-useless-rename': 'warn',
'no-var': 'error',
'prefer-const': 'warn',
'prefer-numeric-literals': 'warn',
'prefer-object-spread': 'warn',
'prefer-rest-params': 'warn',
'prefer-spread': 'warn',
'prefer-template': 'off',
'no-else-return': 'off',
'func-names': ['warn', 'never'],
'one-var': ['warn', 'never'],
'operator-assignment': 'warn',
'prefer-arrow-callback': 'warn',
'no-restricted-imports': [
'warn',
{
paths: [
{
name: '.',
message: 'Usage of local index imports is not allowed.'
},
{
name: './index',
message: 'Import from the source file instead.'
}
]
}
],
'import/extensions': [
'error',
'always',
{
ignorePackages: true,
pattern: {
js: 'always',
ts: 'never'
}
}
],
'import/no-duplicates': 'warn',
'sort-imports-es6-autofix/sort-imports-es6': [
2,
{
ignoreCase: true,
ignoreMemberSort: false,
memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single']
}
],
'wc/guard-super-call': 'off'
}
};

View File

@@ -35,7 +35,7 @@ This Code of Conduct applies within all project spaces, and it also applies when
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at support@fontawesome.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at cory@abeautifulsite.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
github: [claviska]

2
.github/SECURITY.md vendored
View File

@@ -2,6 +2,6 @@
We take security issues in Web Awesome very seriously and appreciate your efforts to disclose your findings responsibly.
To report a security issue, email [support@fontawesome.com](mailto:support@fontawesome.com) and include "WEB AWESOME 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.

View File

@@ -1,48 +0,0 @@
# # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Client Tests
on:
workflow_dispatch:
push:
branches: [next]
pull_request:
branches: [next]
jobs:
client_test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run prettier
working-directory: ./packages/webawesome
- name: Build
run: npm run build
working-directory: ./packages/webawesome
- name: Install Playwright
run: npx playwright install --with-deps
working-directory: ./packages/webawesome
- name: Run CSR tests
# FAIL_FAST to fail on first failing test.
run: FAIL_FAST="true" CSR_ONLY="true" npm run test
working-directory: ./packages/webawesome

30
.github/workflows/node.js.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node.js CI
on:
push:
branches: [next]
pull_request:
branches: [next]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npx playwright install-deps
- run: npm ci
- run: npm run verify

View File

@@ -1,42 +0,0 @@
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: SSR Tests
on:
# push:
# branches: [next]
workflow_dispatch:
jobs:
ssr_test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
working-directory: ./packages/webawesome
- name: Install Playwright
run: npx playwright install --with-deps
working-directory: ./packages/webawesome
- name: Run SSR tests
# FAIL_FAST to fail on first failing test.
run: FAIL_FAST="true" SSR_ONLY="true" npm run test
working-directory: ./packages/webawesome

14
.gitignore vendored
View File

@@ -1,12 +1,10 @@
_site
.cache
.DS_Store
dist/
dist-cdn/
package.json
package-lock.json
dist
docs/assets/images/sprite.svg
node_modules
packages/**/*/src/react
cdn/
yarn.lock
_bundle_
/packages/webawesome-pro
/packages/webawesome-app
src/react
cdn

28
.gitpod.yml Normal file
View File

@@ -0,0 +1,28 @@
tasks:
- init: npm install && npm run build
command: npm run start
ports:
- port: 3001
onOpen: ignore
- port: 4000-4999
onOpen: open-preview
github:
prebuilds:
# enable for the master/default branch (defaults to true)
master: true
# enable for all branches in this repo (defaults to false)
branches: true
# enable for pull requests coming from this repo (defaults to true)
pullRequests: true
# enable for pull requests coming from forks (defaults to false)
pullRequestsFromForks: true
# add a check to pull requests (defaults to true)
addCheck: true
# add a "Review in Gitpod" button as a comment to pull requests (defaults to false)
addComment: false
# add a "Review in Gitpod" button to the pull request's description (defaults to false)
addBadge: true
# add a label once the prebuild is ready to pull requests (defaults to false)
addLabel: true

View File

@@ -1,23 +1,14 @@
# Files are relative to .prettierignore at the root of this monorepo.
# <https://github.com/prettier/prettier-vscode/issues/1252>
*.hbs
*.md
!packages/webawesome/docs/docs/patterns/**/*.md
docs/docs/patterns/blog-news/post-list.md
**/*/.cache
.cache
.github
cspell.json
packages/**/*/dist
packages/**/*/dist-cdn
packages/**/*/docs/search.json
packages/**/*/src/components/icon/icons
packages/**/*/src/react/index.ts
**/*/package.json
**/*/package-lock.json
**/*/tsconfig.json
**/*/tsconfig.prod.json
dist
docs/search.json
src/components/icon/icons
src/react/index.ts
node_modules
packages/**/*/_site
packages/webawesome/docs/assets/scripts/prism-downloaded.js
package.json
package-lock.json
tsconfig.json
cdn
_site

View File

@@ -3,7 +3,7 @@
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bierner.lit-html",
"streetsidesoftware.code-spell-checker",
"ronnidc.nunjucks"
"bashmish.es6-string-css",
"streetsidesoftware.code-spell-checker"
]
}

View File

@@ -1,9 +1,8 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"prettier.enable": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
"source.fixAll.eslint": true
},
"debug.enableStatusBarColor": false
}

View File

@@ -2,4 +2,4 @@
Before contributing, please review the contributions guidelines at:
[webawesome.com/docs/resources/contributing](https://webawesome.com/docs/resources/contributing)
[shoelace.style/resources/contributing](https://shoelace.style/resources/contributing)

View File

@@ -1,5 +1,7 @@
# Web Awesome
A forward-thinking library of web components.
- Works with all frameworks 🧩
- Works with CDNs 🚛
- Fully customizable with CSS 🎨
@@ -11,51 +13,29 @@ Built by the folks behind [Font Awesome](https://fontawesome.com/).
---
Documentation: [webawesome.com](https://webawesome.com)
Documentation: [shoelace.style](https://shoelace.style)
Source: [github.com/shoelace-style/webawesome](https://github.com/shoelace-style/webawesome)
Source: [github.com/shoelace-style/shoelace](https://github.com/shoelace-style/shoelace)
Twitter: [@webawesomer](https://twitter.com/webawesomer)
Twitter: [@shoelace_style](https://twitter.com/shoelace_style)
---
## Developers ✨
Developers can use this documentation to learn how to build Web Awesome from source. You will need Node.js 14.17 or later 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 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://webawesome.com) is where you want to be.
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 Web Awesome?
Components are built with [Lit](https://lit.dev/), 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/).
### Understanding the Web Awesome monorepo
Web Awesome uses [npm workspaces](https://docs.npmjs.com/cli/v11/using-npm/workspaces) for its monorepo structure and is fairly minimal in what it provides.
By using npm workspaces and a monorepo structure, we can get consistent builds, shared configurations, and reduced duplication across repositories which reduces regressions and forces consistency across `webawesome`, `webawesome-pro`, and `webawesome-app`.
Generally, if you plan to only work with the free version of `webawesome` it is easiest to go to `packages/webawesome` and run all commands from there.
### Where do npm dependencies go?
Any dependencies intended to be used across all packages (i.e., `prettier`, `eslint`) that are **not** used at runtime should be in the root `devDependencies` of `package.json`.
```bash
npm install -D -w prettier
```
Any dependencies that will be used at runtime by a package should be part of the specific package's `dependencies` such as `lit`. This is required because if that dependency is not in the `packages/*/package.json`, it will not be installed when used via npm.
Individual packages are also free to install `devDependencies` as needed as long as they are specific to that package only.
To install a package specific to a Web Awesome package, change your working directory to that package's root (i.e., `cd packages/webawesome && npm install <package-name>`).
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/).
### Forking the Repo
Start by [forking the repo](https://github.com/shoelace-style/webawesome/fork) on GitHub, then clone it locally and install dependencies.
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/webawesome
@@ -65,49 +45,36 @@ npm install
### Developing
Once you've cloned the repo, run the following command from the respective directory within `packages/*`.
Once you've cloned the repo, run the following command.
```bash
cd packages/webawesome
npm start
```
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 browsers don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically.
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
To generate a production build, run the following command.
```bash
cd packages/webawesome
npm run build
```
You can also run `npm run build:serve` to start an [`http-server`](https://www.npmjs.com/package/http-server) instance on `http://localhost:4000` after the build completes, so you can preview the production build.
### Creating New Components
To scaffold a new component, run the following command, replacing `wa-tag-name` with the desired tag name.
```bash
cd packages/webawesome
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.
### Adding additional packages
Right now the only additional packages are in private repositories.
To add additional packages from other repositories, run `git clone <url> packages/<package-name>` to clone your repo into `packages/`.
Make sure to run `npm install` at the root of the monorepo after adding your package!
### Contributing
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
Web Awesome is available under the terms of the [MIT License](LICENSE.md).
Web Awesome is available under the terms of the MIT license.

View File

@@ -1,6 +0,0 @@
3.0.0-beta.1
3.0.0-beta.2
3.0.0-beta.3
3.0.0-beta.4
3.0.0-beta.5
3.0.0-beta.6

View File

@@ -5,31 +5,27 @@
"allowfullscreen",
"animationend",
"Animista",
"APG",
"apos",
"atrule",
"autocapitalize",
"autocorrect",
"autofix",
"autofocus",
"autoload",
"autoloader",
"autoloading",
"autoplay",
"bezier",
"Blockquotes",
"boxicons",
"CACHEABLE",
"callout",
"callouts",
"canvastext",
"cdndir",
"chatbubble",
"checkmark",
"claviska",
"Clippy",
"codebases",
"codepen",
"colocated",
"colorjs",
"colour",
"combobox",
"Commonmark",
@@ -44,23 +40,13 @@
"crutchcorn",
"csspart",
"cssproperty",
"cssstate",
"datalist",
"datetime",
"describedby",
"dictsort",
"Docsify",
"dogfood",
"dropdowns",
"easings",
"ecommerce",
"eleventy",
"elif",
"endfor",
"endmarkdown",
"endraw",
"endregion",
"endset",
"enterkeyhint",
"eqeqeq",
"erroneou",
@@ -68,11 +54,7 @@
"esbuild",
"exportmaps",
"exportparts",
"fetchpriority",
"fieldsets",
"focusin",
"focusout",
"fontawesome",
"formaction",
"formdata",
"formenctype",
@@ -81,34 +63,29 @@
"formtarget",
"FOUC",
"FOUCE",
"Frontmatter",
"fullscreen",
"gestern",
"giga",
"globby",
"Grayscale",
"groupby",
"haspopup",
"heroicons",
"hexa",
"Hotwire",
"hrefs",
"Iconoir",
"Iframes",
"iife",
"inputmode",
"ionicon",
"ionicons",
"jank",
"jsDelivr",
"jsfiddle",
"jsonata",
"keydown",
"keyframes",
"keymaker",
"Konnor",
"Kool",
"labelledby",
"Laravel",
"LaViska",
"linkify",
"listbox",
"listitem",
@@ -120,42 +97,31 @@
"Menlo",
"menuitemcheckbox",
"menuitemradio",
"metaframeworks",
"middlewares",
"minlength",
"minmax",
"monospace",
"mousedown",
"mousemove",
"mouseout",
"mouseup",
"multiselectable",
"nextjs",
"nocheck",
"noindex",
"noopener",
"noreferrer",
"noscript",
"Notdog",
"novalidate",
"nowrap",
"npmdir",
"Numberish",
"nums",
"oklab",
"oklch",
"onscrollend",
"outdir",
"ParamagicDev",
"peta",
"petabit",
"Preact",
"preconnect",
"prerendered",
"prismjs",
"progressbar",
"radiogroup",
"Railsbyte",
"referrerpolicy",
"remixicon",
"reregister",
"resizer",
@@ -172,33 +138,23 @@
"scrollbars",
"scrollend",
"scroller",
"Scrollers",
"Segoe",
"selectattr",
"semibold",
"shadowrootmode",
"Shortcode",
"Shortcodes",
"sitedir",
"slotchange",
"smartquotes",
"spacebar",
"srcdoc",
"stylesheet",
"svgs",
"Tabbable",
"tabindex",
"tabler",
"tablist",
"tabpanel",
"tbody",
"templating",
"tera",
"testid",
"textareas",
"textfield",
"thead",
"Themer",
"tinycolor",
"transitionend",
"treeitem",
@@ -208,22 +164,16 @@
"typeof",
"unbundles",
"unbundling",
"Uncategorized",
"unicons",
"unsanitized",
"unsupportive",
"valpha",
"valuenow",
"valuetext",
"viewports",
"Vuejs",
"WCAG",
"webawesome",
"webawesomer",
"WEBP",
"Webpacker",
"xmark",
"zoomable"
"Webpacker"
],
"ignorePaths": [
"package.json",

View File

@@ -0,0 +1,216 @@
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';
import fs from 'fs';
const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
const { name, description, version, author, homepage, license } = packageData;
const { outdir } = commandLineArgs([
{ name: 'litelement', type: String },
{ name: 'analyze', defaultOption: true },
{ name: 'outdir', type: String }
]);
function noDash(string) {
return string.replace(/^\s?-/, '').trim();
}
function replace(string, terms) {
terms.forEach(({ from, to }) => {
string = string?.replace(from, to);
});
return string;
}
export default {
globs: ['src/components/**/*.component.ts'],
exclude: ['**/*.styles.ts', '**/*.test.ts'],
plugins: [
// Append 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: 'wa-custom-tags',
analyzePhase({ ts, node, moduleDoc }) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration: {
const className = node.name.getText();
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
const customTags = ['animation', 'dependency', 'documentation', 'since', 'status', 'title'];
let customComments = '/**';
node.jsDoc?.forEach(jsDoc => {
jsDoc?.tags?.forEach(tag => {
const tagName = tag.tagName.getText();
if (customTags.includes(tagName)) {
customComments += `\n * @${tagName} ${tag.comment}`;
}
});
});
// 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) {
// Animations
case 'animation':
if (!Array.isArray(classDoc['animations'])) {
classDoc['animations'] = [];
}
classDoc['animations'].push({
name: t.name,
description: noDash(t.description)
});
break;
// Dependencies
case 'dependency':
if (!Array.isArray(classDoc['dependencies'])) {
classDoc['dependencies'] = [];
}
classDoc['dependencies'].push(t.name);
break;
// Value-only metadata tags
case 'documentation':
case 'since':
case 'status':
case 'title':
classDoc[t.tag] = t.name;
break;
// All other tags
default:
if (!Array.isArray(classDoc[t.tag])) {
classDoc[t.tag] = [];
}
classDoc[t.tag].push({
name: t.name,
description: t.description,
type: t.type || undefined
});
}
});
}
}
}
},
{
name: 'wa-react-event-names',
analyzePhase({ ts, node, moduleDoc }) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration: {
const className = node.name.getText();
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
if (classDoc?.events) {
classDoc.events.forEach(event => {
event.reactName = `on${pascalCase(event.name)}`;
event.eventName = `${pascalCase(event.name)}Event`;
});
}
}
}
}
},
{
name: 'wa-translate-module-paths',
packageLinkPhase({ customElementsManifest }) {
customElementsManifest?.modules?.forEach(mod => {
//
// CEM paths look like this:
//
// src/components/button/button.ts
//
// But we want them to look like this:
//
// components/button/button.js
//
const terms = [
{ from: /^src\//, to: '' }, // Strip the src/ prefix
{ from: /\.component.(t|j)sx?$/, to: '.js' } // Convert .ts to .js
];
mod.path = replace(mod.path, terms);
for (const ex of mod.exports ?? []) {
ex.declaration.module = replace(ex.declaration.module, terms);
}
for (const dec of mod.declarations ?? []) {
if (dec.kind === 'class') {
for (const member of dec.members ?? []) {
if (member.inheritedFrom) {
member.inheritedFrom.module = replace(member.inheritedFrom.module, terms);
}
}
}
}
});
}
},
// Generate custom VS Code data
customElementVsCodePlugin({
outdir,
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-', '')}`
};
}
})
]
};

View 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>&lt;{{ component.tagName }}&gt; | {{ 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">&lt;script type=&quot;module&quot; src=&quot;https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/{{ meta.cdndir }}/{{ component.path }}&quot;&gt;&lt;/script&gt;</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>&lt;{{ dependency }}&gt;</code></li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

129
docs/_includes/default.njk Normal file
View 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>

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

View 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);
}

View 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" %}

View 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;
}

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

View 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);
}

View 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" %}

View File

@@ -0,0 +1,10 @@
html {
min-height: 100%;
height: auto;
}
body {
padding: 0;
height: auto;
}

View File

@@ -0,0 +1,2 @@
<wa-layout main-id="main-content" class="wa-theme-light">
</wa-layout>

View 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;
}
}

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

View 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 %}
&lt;/script>
{% else %}
</script>
{% endif %}
<!-- playground-hide-end -->

1
docs/_includes/logo.njk Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

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

View 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 &amp; Support</a></li>
<li><a href="/resources/accessibility">Accessibility</a></li>
<li><a href="/resources/contributing">Contributing</a></li>
<li><a href="/resources/changelog">Changelog</a></li>
</ul>
</li>
<li>
<h2>Components</h2>
<ul>
{% for component in meta.components %}
<li>
<a href="/components/{{ component.tagName | 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>

View File

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

View File

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

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

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

View File

@@ -0,0 +1,138 @@
let count = 1;
function escapeHtml(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
/**
* Turns code fields with the :preview suffix into interactive code previews.
*/
module.exports = function (doc, options) {
options = {
within: 'body', // the element containing the code fields to convert
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('[class*=":preview"]').forEach(code => {
const pre = code.closest('pre');
if (!pre) {
return;
}
const adjacentPre = pre.nextElementSibling?.tagName.toLowerCase() === 'pre' ? pre.nextElementSibling : null;
const reactCode = adjacentPre?.querySelector('code[class$="react"]');
const sourceGroupId = `code-preview-source-group-${count}`;
const isExpanded = code.getAttribute('class').includes(':expanded');
const noCodePen = code.getAttribute('class').includes(':no-codepen');
count++;
const htmlButton = `
<button type="button"
title="Show HTML code"
class="code-preview__button code-preview__button--html"
>
HTML
</button>
`;
const reactButton = `
<button type="button" title="Show React code" class="code-preview__button code-preview__button--react">
React
</button>
`;
const codePenButton = `
<button type="button" class="code-preview__button code-preview__button--codepen" title="Edit on CodePen">
<svg
width="138"
height="26"
viewBox="0 0 138 26"
fill="none"
stroke="currentColor"
stroke-width="2.3"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M80 6h-9v14h9 M114 6h-9 v14h9 M111 13h-6 M77 13h-6 M122 20V6l11 14V6 M22 16.7L33 24l11-7.3V9.3L33 2L22 9.3V16.7z M44 16.7L33 9.3l-11 7.4 M22 9.3l11 7.3 l11-7.3 M33 2v7.3 M33 16.7V24 M88 14h6c2.2 0 4-1.8 4-4s-1.8-4-4-4h-6v14 M15 8c-1.3-1.3-3-2-5-2c-4 0-7 3-7 7s3 7 7 7 c2 0 3.7-0.8 5-2 M64 13c0 4-3 7-7 7h-5V6h5C61 6 64 9 64 13z" />
</svg>
</button>
`;
const codePreview = `
<div class="code-preview ${isExpanded ? 'code-preview--expanded' : ''}">
<div class="code-preview__preview">
${code.textContent}
<div class="code-preview__resizer">
<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;
};

View 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;
};

View File

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

View File

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

View File

@@ -0,0 +1,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;

View File

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

View File

@@ -0,0 +1,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;
};

View 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;
};

View File

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

View File

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

View File

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

View File

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

View File

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 175 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View 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

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,23 @@
<svg width="160" height="45" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d)">
<rect x="2" y="2" width="156" height="40" rx="16" fill="#F9F9F9"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.425 11.174c.604 1.114.233 2.53-.83 3.164l-6.986 4.166a.378.378 0 00-.18.325v6.748c0 .134.069.258.18.325l5.714 3.407c.11.066.244.066.354 0l5.714-3.407a.378.378 0 00.18-.325V21.29l-4.986 2.936c-1.067.628-2.416.231-3.015-.886-.6-1.118-.22-2.532.846-3.16l7.008-4.127c2.048-1.206 4.576.345 4.576 2.806v6.718c0 1.803-.924 3.467-2.42 4.36l-5.713 3.407a4.596 4.596 0 01-4.734 0l-5.714-3.408C18.924 29.044 18 27.38 18 25.576V18.83c0-1.803.924-3.467 2.42-4.36l6.985-4.165c1.063-.634 2.415-.245 3.02.87z" fill="url(#paint0_linear)"/>
<path fill="#F9F9F9" d="M47 12.5h95v-1H47z"/>
<path d="M52.538 27.752c2.744 0 4.844-1.876 4.844-5.152 0-3.108-2.1-5.152-4.844-5.152s-4.802 2.002-4.802 5.152c0 3.29 2.058 5.152 4.802 5.152zm0-1.554c-1.736 0-2.912-1.316-2.912-3.598 0-2.226 1.162-3.598 2.912-3.598s2.954 1.4 2.954 3.598c0 2.31-1.218 3.598-2.954 3.598zm7.89 4.158V27.22c0-.196-.013-.378-.055-.658.434.7 1.022 1.19 2.17 1.19 1.736 0 3.066-1.414 3.066-3.626 0-2.17-1.19-3.682-2.996-3.682-1.078 0-1.806.476-2.24 1.204.042-.28.056-.462.056-.672v-.308H58.72v9.688h1.708zm1.695-3.948c-1.036 0-1.764-.938-1.764-2.31 0-1.414.742-2.296 1.764-2.296 1.092 0 1.75.952 1.75 2.296 0 1.372-.7 2.31-1.75 2.31zm7.866 1.344c1.848 0 3.052-1.078 3.192-2.478h-1.736c-.112.714-.714 1.134-1.456 1.134-1.036 0-1.722-.826-1.736-1.904h4.97v-.378c0-2.226-1.204-3.682-3.29-3.682-1.988 0-3.43 1.47-3.43 3.626 0 2.366 1.442 3.682 3.486 3.682zm-1.75-4.48c.098-.896.756-1.554 1.68-1.554.924 0 1.526.63 1.554 1.554H68.24zm8.006 4.228v-4.004c0-.952.616-1.694 1.456-1.694.798 0 1.288.63 1.288 1.638v4.06h1.708v-4.312c0-1.68-.896-2.744-2.408-2.744-1.078 0-1.722.518-2.1 1.204.042-.266.056-.476.056-.672v-.308h-1.708V27.5h1.708zm8.911-7.868h1.792V17.84h-1.792v1.792zm.042 1.036V27.5h1.708v-6.832h-1.708zm5.097 6.832v-4.004c0-.952.616-1.694 1.456-1.694.798 0 1.288.63 1.288 1.638v4.06h1.708v-4.312c0-1.68-.896-2.744-2.408-2.744-1.078 0-1.722.518-2.1 1.204.042-.266.056-.476.056-.672v-.308h-1.708V27.5h1.708zm13.238.252c1.526 0 2.52-.658 2.982-1.54-.07.322-.098.644-.098.98v.308h1.68v-5.222h-4.34v1.554h2.66v.07c0 1.4-1.134 2.296-2.59 2.296-1.792 0-3.024-1.372-3.024-3.598s1.26-3.598 3.066-3.598c1.302 0 2.17.756 2.296 1.736h1.89c-.182-1.904-1.764-3.29-4.214-3.29-2.954 0-4.928 2.128-4.928 5.152 0 3.136 1.848 5.152 4.62 5.152zm6.063-8.12h1.792V17.84h-1.792v1.792zm.042 1.036V27.5h1.708v-6.832h-1.708zm6.413 6.958c.434 0 .84-.07 1.008-.126v-1.288c-.168.028-.35.042-.518.042-.728 0-1.008-.42-1.008-1.134v-3.094h1.68v-1.358h-1.68v-2.464h-1.708v2.464h-1.554v1.358h1.554v3.346c0 1.526.77 2.254 2.226 2.254zm3.961 2.73V27.22c0-.196-.014-.378-.056-.658.434.7 1.022 1.19 2.17 1.19 1.736 0 3.066-1.414 3.066-3.626 0-2.17-1.19-3.682-2.996-3.682-1.078 0-1.806.476-2.24 1.204.042-.28.056-.462.056-.672v-.308h-1.708v9.688h1.708zm1.694-3.948c-1.036 0-1.764-.938-1.764-2.31 0-1.414.742-2.296 1.764-2.296 1.092 0 1.75.952 1.75 2.296 0 1.372-.7 2.31-1.75 2.31zm7.88 1.344c2.058 0 3.514-1.372 3.514-3.64 0-2.24-1.456-3.668-3.514-3.668-2.044 0-3.5 1.428-3.5 3.668 0 2.268 1.442 3.64 3.5 3.64zm0-1.344c-1.064 0-1.764-.84-1.764-2.296 0-1.484.728-2.31 1.764-2.31 1.05 0 1.778.826 1.778 2.31 0 1.456-.714 2.296-1.778 2.296zm7.551 1.344c1.26 0 1.876-.686 2.142-1.19-.056.238-.056.42-.056.658v.28h1.708v-9.8h-1.708v3.276c0 .21 0 .42.056.672-.392-.658-1.05-1.204-2.114-1.204-1.596 0-3.15 1.218-3.15 3.668 0 2.408 1.316 3.64 3.122 3.64zm.406-1.344c-1.022 0-1.792-.896-1.792-2.31 0-1.358.77-2.296 1.792-2.296s1.778.896 1.778 2.296c0 1.372-.756 2.31-1.778 2.31z" fill="#12100C"/>
</g>
<defs>
<linearGradient id="paint0_linear" x1="33.806" y1="13.629" x2="22.389" y2="30.86" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFB45B"/>
<stop offset="1" stop-color="#FF8A00"/>
</linearGradient>
<filter id="filter0_d" x="0" y=".5" width="160" height="44" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dy=".5"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
<feBlend in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="879px" height="553px" viewBox="0 0 879 553" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
<title>undraw-content-team</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="undraw-content-team" transform="translate(-3.000000, 0.000000)">
<rect id="Rectangle" fill-opacity="0.25" fill="#fff" x="180" y="16" width="579" height="343" rx="20"></rect>
<path d="M840.86295,552.21468 L98.22937,552.21468 C84.4769827,552.21468 73.32847,541.066167 73.32847,527.31378 L73.32847,527.31378 C342.533003,496.02706 606.678133,496.02706 865.76386,527.31378 L865.76386,527.31378 C865.763854,541.066169 854.615339,552.21468 840.86295,552.21468 Z" id="Path" fill="#CCCCCC" fill-rule="nonzero"></path>
<path d="M865.76386,528.77851 L73.32847,527.31375 L165.16896,372.78153 L165.60837,372.04915 L165.60837,31.47769 C165.607672,23.1290737 168.923838,15.1221947 174.827202,9.21883153 C180.730565,3.31546831 188.737444,-0.00069762467 197.08606,2.84217094e-14 L737.61198,2.84217094e-14 C745.960596,-0.00069762467 753.967475,3.31546831 759.870838,9.21883153 C765.774202,15.1221947 769.090368,23.1290737 769.08967,31.47769 L769.08967,374.39269 L865.76386,528.77851 Z M196.36834,21.97141 C190.70822,21.9777587 186.121369,26.5646095 186.11502,32.22473 L186.11502,342.75394 C186.121622,348.413956 190.708324,353.000658 196.36834,353.00726 L742.72398,353.00726 C748.383893,353.000411 752.970451,348.413853 752.9773,342.75394 L752.9773,32.22473 C752.970704,26.5647122 748.383998,21.9780061 742.72398,21.97141 L196.36834,21.97141 Z" id="Combined-Shape" fill="#E6E6E6" fill-rule="nonzero"></path>
<path d="M199.95903,394.02055 C198.24391,394.023463 196.686317,395.021305 195.9667,396.57816 L167.66779,458.0981 C167.041726,459.458563 167.151668,461.044211 167.959498,462.305253 C168.767328,463.566295 170.161797,464.32907 171.6594,464.3291 L766.77054,464.3291 C768.293506,464.329083 769.707849,463.540407 770.508355,462.244793 C771.308862,460.949179 771.381351,459.331427 770.69993,457.96941 L739.93993,396.44947 C739.199697,394.957483 737.676056,394.015676 736.01054,394.0206 L199.95903,394.02055 Z" id="Path" fill="#CCCCCC" fill-rule="nonzero"></path>
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="467.34902" cy="10.25332" r="6.59142"></circle>
<path d="M408.85444,476.04714 C406.86865,476.049642 405.130517,477.381666 404.61179,479.29851 L397.51328,505.66419 C397.15753,506.984241 397.436508,508.39467 398.26801,509.479887 C399.099513,510.565104 400.388783,511.201438 401.75593,511.20139 L536.75349,511.20139 C538.174875,511.201399 539.508438,510.513737 540.332692,509.355747 C541.156947,508.197757 541.370032,506.712543 540.9046,505.36952 L531.77845,479.00384 C531.165175,477.234516 529.498505,476.04781 527.62591,476.04714 L408.85444,476.04714 Z" id="Path" fill="#CCCCCC" fill-rule="nonzero"></path>
<polygon id="Path" fill="#CCCCCC" fill-rule="nonzero" points="769.09 366.922 769.09 372.782 165.169 372.782 165.623 372.049 165.623 366.922"></polygon>
<path d="M694.54732,380.34397 C694.063752,380.344578 693.640491,380.668942 693.51417,381.13572 L691.78557,387.55617 C691.698952,387.877623 691.766894,388.221078 691.969377,388.485341 C692.17186,388.749605 692.485812,388.904563 692.81873,388.90456 L725.69267,388.90456 C726.038798,388.904561 726.36354,388.737104 726.564256,388.455115 C726.764973,388.173127 726.816861,387.811455 726.70352,387.48441 L724.48117,381.06397 C724.331826,380.633113 723.925966,380.344133 723.46996,380.34397 L694.54732,380.34397 Z" id="Path" fill="#CCCCCC" fill-rule="nonzero"></path>
<path d="M646.54732,380.34397 C646.063752,380.344578 645.640491,380.668942 645.51417,381.13572 L643.78557,387.55617 C643.698952,387.877623 643.766894,388.221078 643.969377,388.485341 C644.17186,388.749605 644.485812,388.904563 644.81873,388.90456 L677.69267,388.90456 C678.038798,388.904561 678.36354,388.737104 678.564256,388.455115 C678.764973,388.173127 678.816861,387.811455 678.70352,387.48441 L676.48117,381.06397 C676.331826,380.633113 675.925966,380.344133 675.46996,380.34397 L646.54732,380.34397 Z" id="Path" fill="#CCCCCC" fill-rule="nonzero"></path>
<path d="M507.48137,421.13959 L510.05784,421.13959 L504.90484,76.75135 L502.32836,76.75135 L502.43115,83.62195 L444.68449,83.62195 L444.78727,76.75135 L442.2108,76.75135 L437.0578,421.13959 L439.63428,421.13959 L439.96839,398.81019 L507.14722,398.81019 L507.48137,421.13959 Z M503.76762,172.93959 L443.34807,172.93959 L443.9777,130.85725 L503.13798,130.85725 L503.76762,172.93959 Z M503.8062,175.51607 L504.43583,217.5984 L442.67983,217.5984 L443.30947,175.51607 L503.8062,175.51607 Z M504.47441,220.17488 L505.10405,262.25722 L442.01164,262.25722 L442.64128,220.17488 L504.47441,220.17488 Z M505.14262,264.83369 L505.77226,306.91603 L441.34343,306.91603 L441.97307,264.83369 L505.14262,264.83369 Z M505.81083,309.49251 L506.44047,351.5749 L440.67517,351.5749 L441.30481,309.49251 L505.81083,309.49251 Z M502.46977,86.19843 L503.09941,128.28077 L444.01628,128.28077 L444.64592,86.19843 L502.46977,86.19843 Z M440.007,396.23371 L440.63664,354.15137 L506.47905,354.15137 L507.10869,396.23371 L440.007,396.23371 Z" id="Shape" fill="#3F3D56" fill-rule="nonzero"></path>
<circle id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="460.7944" cy="153.35761" r="42.01233"></circle>
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" transform="translate(481.263536, 194.779340) rotate(-10.261060) translate(-481.263536, -194.779340) " x="474.881916" y="183.345595" width="12.76324" height="22.86749"></rect>
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" transform="translate(456.145380, 199.326392) rotate(-10.261060) translate(-456.145380, -199.326392) " x="449.76376" y="187.892647" width="12.76324" height="22.86749"></rect>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(455.991944, 213.405824) rotate(-40.261060) translate(-455.991944, -213.405824) " cx="455.991944" cy="213.405824" rx="10.63602" ry="3.98853"></ellipse>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(480.586874, 208.953401) rotate(-40.261060) translate(-480.586874, -208.953401) " cx="480.586874" cy="208.953401" rx="10.63605" ry="3.98853"></ellipse>
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="457.85318" cy="143.08115" r="14.35864"></circle>
<circle id="Oval" fill="#3F3D56" fill-rule="nonzero" cx="451.95181" cy="137.93436" r="4.78622"></circle>
<path d="M493.93625,107.65269 C494.56802,92.0991 481.16312,78.92509 463.99545,78.22776 C446.82778,77.53043 432.39853,89.57376 431.76672,105.12736 C431.13491,120.68096 443.06863,124.21482 460.23627,124.91221 C477.40391,125.6096 493.30447,123.20627 493.93625,107.65269 Z" id="Path" fill="#0ea5e9" fill-rule="nonzero"></path>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(422.001785, 145.703659) rotate(-77.089900) translate(-422.001785, -145.703659) " cx="422.001785" cy="145.703659" rx="6.59448" ry="21.00616"></ellipse>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(504.470422, 159.418119) rotate(-77.089900) translate(-504.470422, -159.418119) " cx="504.470422" cy="159.418119" rx="6.59448" ry="21.00616"></ellipse>
<path d="M472.3331,169.91416 C472.9488,173.280144 471.720947,176.718794 469.112518,178.933525 C466.504089,181.148255 462.911834,181.802199 459.690258,180.648779 C456.468682,179.495358 454.107795,176.710012 453.4978,173.34299 L453.49445,173.3245 C452.5527,168.12235 456.57483,166.2815 461.77701,165.33976 C466.97919,164.39802 471.39139,164.71201 472.3331,169.91416 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(312.185926, 293.633672) rotate(-69.082170) translate(-312.185926, -293.633672) " cx="312.185926" cy="293.633672" rx="21.53369" ry="6.76007"></ellipse>
<circle id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(272.378778, 326.155721) rotate(-80.782520) translate(-272.378778, -326.155721) " cx="272.378778" cy="326.155721" r="43.06735"></circle>
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="252.75315" y="359.95537" width="13.08374" height="23.44171"></rect>
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="278.92063" y="359.95537" width="13.08374" height="23.44171"></rect>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="263.65629" cy="383.66967" rx="10.90314" ry="4.08868"></ellipse>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="289.82377" cy="383.12453" rx="10.90314" ry="4.08868"></ellipse>
<path d="M230.60203,286.10434 C227.12455,270.53055 238.2407,254.79388 255.43064,250.9555 C272.62058,247.11712 289.37486,256.63058 292.85233,272.20438 C296.3298,287.77818 284.93742,293.52203 267.74748,297.36038 C250.55754,301.19873 234.07951,301.67812 230.60203,286.10434 Z" id="Path" fill="#0ea5e9" fill-rule="nonzero"></path>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(223.870603, 306.172293) rotate(-64.625740) translate(-223.870603, -306.172293) " cx="223.870603" cy="306.172293" rx="6.76007" ry="21.53368"></ellipse>
<circle id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="85.9008" cy="369.76318" r="43.06733"></circle>
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="66.27519" y="403.56287" width="13.08374" height="23.44171"></rect>
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="92.44267" y="403.56287" width="13.08374" height="23.44171"></rect>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="77.17827" cy="427.27716" rx="10.90314" ry="4.08868"></ellipse>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="103.34581" cy="426.732" rx="10.90314" ry="4.08868"></ellipse>
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="86.99113" cy="358.86008" r="14.71922"></circle>
<circle id="Oval" fill="#3F3D56" fill-rule="nonzero" cx="86.99113" cy="358.86008" r="4.90642"></circle>
<path d="M44.12401,329.71183 C40.64653,314.13804 51.76268,298.4014 68.95262,294.56302 C86.14256,290.72464 102.89683,300.23813 106.37431,315.81192 C109.85179,331.38571 98.45939,337.12961 81.26945,340.96792 C64.07951,344.80623 47.60154,345.28568 44.12401,329.71183 Z" id="Path" fill="#12a5e9" fill-rule="nonzero"></path>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(110.988725, 213.490755) rotate(-69.082170) translate(-110.988725, -213.490755) " cx="110.988725" cy="213.490755" rx="21.53369" ry="6.76007"></ellipse>
<circle id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(71.181568, 246.012788) rotate(-80.782520) translate(-71.181568, -246.012788) " cx="71.1815681" cy="246.012788" r="43.06735"></circle>
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="51.55595" y="279.81244" width="13.08374" height="23.44171"></rect>
<rect id="Rectangle" fill="#2F2E41" fill-rule="nonzero" x="77.72343" y="279.81244" width="13.08374" height="23.44171"></rect>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="62.45909" cy="303.52674" rx="10.90314" ry="4.08868"></ellipse>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" cx="88.62657" cy="302.9816" rx="10.90314" ry="4.08868"></ellipse>
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="72.27189" cy="235.10965" r="14.71922"></circle>
<circle id="Oval" fill="#3F3D56" fill-rule="nonzero" cx="72.27189" cy="235.10965" r="4.90642"></circle>
<path d="M29.40483,205.96134 C25.92735,190.38755 37.0435,174.65088 54.23344,170.8125 C71.42338,166.97412 88.17766,176.48758 91.65513,192.06138 C95.1326,207.63518 83.74022,213.37903 66.55028,217.21738 C49.36034,221.05573 32.88231,221.53519 29.40483,205.96134 Z" id="Path" fill="#0ea5e9" fill-rule="nonzero"></path>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(22.673401, 226.029366) rotate(-64.625740) translate(-22.673401, -226.029366) " cx="22.6734006" cy="226.029366" rx="6.76007" ry="21.53368"></ellipse>
<path d="M50.02702,261.54972 C50.02702,265.76487 60.88029,274.08829 72.92357,274.08829 C84.96685,274.08829 96.25871,262.22129 96.25871,258.0062 C96.25871,253.79111 84.96678,258.82395 72.92357,258.82395 C60.88036,258.82395 50.02702,257.33457 50.02702,261.54972 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
<path d="M113.52376,81.56869 C111.867655,81.5704977 110.525568,82.9125851 110.52376,84.56869 L110.52376,208.56869 C110.525568,210.224795 111.867655,211.566882 113.52376,211.56869 L400.52376,211.56869 C402.179865,211.566882 403.521952,210.224795 403.52376,208.56869 L403.52376,84.56869 C403.521952,82.9125851 402.179865,81.5704977 400.52376,81.56869 L113.52376,81.56869 Z" id="Path" fill="#fbc024" fill-rule="nonzero"></path>
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="191.01816" cy="146.56869" r="29.1211"></circle>
<path d="M256.7436,142.69417 C254.884983,142.724723 253.39428,144.240132 253.39428,146.099 C253.39428,147.957868 254.884983,149.473277 256.7436,149.50383 L348.68926,149.50383 C349.906316,149.524778 351.042001,148.894505 351.668119,147.850647 C352.294237,146.806789 352.31556,145.508108 351.72405,144.444258 C351.132539,143.380407 350.018157,142.713189 348.80107,142.69417 C348.763797,142.693537 348.726527,142.693537 348.68926,142.69417 L256.7436,142.69417 Z" id="b71acdfd-6a55-428e-917a-53f192cb0203" fill="#FFFFFF" fill-rule="nonzero"></path>
<path d="M256.7436,122.96697 C254.884983,122.997523 253.39428,124.512932 253.39428,126.3718 C253.39428,128.230668 254.884983,129.746077 256.7436,129.77663 L302.65917,129.77663 C303.876249,129.797613 305.011971,129.167347 305.638111,128.123473 C306.26425,127.0796 306.285572,125.78089 305.694037,124.717025 C305.102503,123.65316 303.988082,122.985951 302.77097,122.96697 C302.733703,122.966337 302.696437,122.966337 302.65917,122.96697 L256.7436,122.96697 Z" id="ad4fbcfa-41b0-45f9-a593-23b6dc3fe165" fill="#FFFFFF" fill-rule="nonzero"></path>
<path d="M256.7436,163.36076 C254.884983,163.391313 253.39428,164.906722 253.39428,166.76559 C253.39428,168.624458 254.884983,170.139867 256.7436,170.17042 L348.68926,170.17042 C349.906316,170.191368 351.042001,169.561095 351.668119,168.517237 C352.294237,167.473379 352.31556,166.174698 351.72405,165.110848 C351.132539,164.046997 350.018157,163.379779 348.80107,163.36076 C348.763797,163.360093 348.726527,163.360093 348.68926,163.36076 L256.7436,163.36076 Z" id="bce2a7cd-a325-424d-9b3a-f63d1dad5aba" fill="#FFFFFF" fill-rule="nonzero"></path>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(695.602650, 372.610085) rotate(-20.917830) translate(-695.602650, -372.610085) " cx="695.60265" cy="372.610085" rx="6.76007" ry="21.53369"></ellipse>
<circle id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(726.409811, 414.132115) rotate(-9.217470) translate(-726.409811, -414.132115) " cx="726.409811" cy="414.132115" r="43.06733"></circle>
<polygon id="Path" fill="#2F2E41" fill-rule="nonzero" points="746.035 447.932 732.952 447.932 735.512 476.005 746.058 473.14"></polygon>
<polygon id="Path" fill="#2F2E41" fill-rule="nonzero" points="717.512 448.005 704.428 448.005 706.988 476.078 717.058 473.14"></polygon>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(706.318056, 473.319125) rotate(-4.181640) translate(-706.318056, -473.319125) " cx="706.318056" cy="473.319125" rx="10.90314" ry="4.08868"></ellipse>
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="725.31949" cy="403.22896" r="14.71922"></circle>
<circle id="Oval" fill="#3F3D56" fill-rule="nonzero" cx="725.31949" cy="403.22896" r="4.90642"></circle>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(771.918005, 384.148727) rotate(-53.549900) translate(-771.918005, -384.148727) " cx="771.918005" cy="384.148727" rx="21.53368" ry="6.76007"></ellipse>
<path d="M705.39698,436.33866 C705.39698,432.86466 714.34198,426.00466 724.26778,426.00466 C734.19358,426.00466 743.50006,435.78516 743.50006,439.25914 C743.50006,442.73312 734.19351,438.58514 724.26778,438.58514 C714.34205,438.58514 705.39698,439.81268 705.39698,436.33866 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero" transform="translate(724.448520, 433.325266) rotate(-180.000000) translate(-724.448520, -433.325266) "></path>
<path d="M847.1767,378.54172 L611.43117,345.86064 C607.604361,345.32541 604.932725,341.793834 605.45868,337.96574 L631.96057,146.79396 C632.495641,142.967058 636.027329,140.295337 639.85547,140.82147 L875.60098,173.50256 C879.427885,174.037626 882.099609,177.569318 881.57347,181.39746 L855.0716,372.56924 C854.536375,376.396051 851.004794,379.067687 847.1767,378.54172 L847.1767,378.54172 Z" id="Path" fill="#9358ff" fill-rule="nonzero"></path>
<path d="M762.72231,318.87957 L642.36784,302.19498 C642.216871,302.176045 642.067969,302.14324 641.92302,302.09698 L712.51355,211.39072 C713.394132,210.238632 714.82651,209.649483 716.262854,209.8486 C717.699198,210.047717 718.917295,211.004295 719.45127,212.35248 L748.48059,283.8148 L749.87186,287.23459 L762.72231,318.87957 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
<polygon id="Path" fill="#000000" fill-rule="nonzero" opacity="0.2" points="762.722 318.879 721.63 313.183 745.864 286.679 747.609 284.77 748.481 283.815 749.872 287.235"></polygon>
<path d="M829.73481,328.16942 L725.63807,313.73863 L749.87186,287.23463 L751.61612,285.32515 L783.19533,250.78503 C784.29885,249.735613 785.796059,249.204111 787.314261,249.322828 C788.832463,249.441546 790.228856,250.199316 791.15584,251.40751 C791.271244,251.575507 791.375823,251.750689 791.46894,251.93199 L829.73481,328.16942 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
<circle id="Oval" fill="#FFFFFF" fill-rule="nonzero" cx="764.18602" cy="224.18353" r="18"></circle>
<rect id="Rectangle" fill="#FFFFFF" fill-rule="nonzero" transform="translate(653.262165, 167.923576) rotate(7.892770) translate(-653.262165, -167.923576) " x="642.262129" y="156.923541" width="22.0000711" height="22.0000711"></rect>
<path d="M768.18655,374.08068 C771.66403,358.50689 760.54788,342.77022 743.35794,338.93184 C726.168,335.09346 709.41373,344.60692 705.93625,360.18071 C702.45877,375.7545 713.85117,381.49837 731.04111,385.33671 C748.23105,389.17505 764.70908,389.6545 768.18655,374.08068 Z" id="Path" fill="#12a5e9" fill-rule="nonzero"></path>
<ellipse id="Oval" fill="#2F2E41" fill-rule="nonzero" transform="translate(735.010556, 473.249892) rotate(-4.181640) translate(-735.010556, -473.249892) " cx="735.010556" cy="473.249892" rx="10.90314" ry="4.08868"></ellipse>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

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

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

View 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
View 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);
})();

View 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 `&gt;` and `&lt;`
## 1.0.1
- fix: allow any HTML Element for templating

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

View 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

View File

@@ -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 })
// ],
}

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -0,0 +1 @@
ruby-3.0.4

View 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

View 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

View File

@@ -0,0 +1,2 @@
bridgetown: bin/bridgetown start --skip-frontend
esbuild: pnpm run esbuild-dev

View 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

View 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

View 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")

View 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")

View 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

View 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

View 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))
}
}

View 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

View 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

View 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)

View File

@@ -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)
}
}

View File

@@ -0,0 +1,6 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
// console.log("Hello, Stimulus!", this.element)
}
}

View File

@@ -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);
}
});
}
}

View File

@@ -0,0 +1,6 @@
import { Controller } from "@hotwired/stimulus"
export default class SearchController extends Controller {
show () {
document.querySelector("bridgetown-ninja-keys").open()
}
}

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