mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-13 12:39:14 +00:00
Compare commits
12 Commits
docs-fix
...
scroll-on-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
362ea5e319 | ||
|
|
28c442ba1c | ||
|
|
eeedca813a | ||
|
|
6c210310b6 | ||
|
|
106638b4af | ||
|
|
534966a7fc | ||
|
|
b10ec21780 | ||
|
|
1ff5adfa73 | ||
|
|
51677a38d9 | ||
|
|
698815a96b | ||
|
|
3f62e09d51 | ||
|
|
b5e7931dbc |
1
.github/workflows/client_tests.yml
vendored
1
.github/workflows/client_tests.yml
vendored
@@ -4,7 +4,6 @@
|
||||
name: Client Tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [next]
|
||||
pull_request:
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
# 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/**/*.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
|
||||
docs/assets/scripts/prism-downloaded.js
|
||||
|
||||
@@ -125,7 +125,6 @@
|
||||
"noreferrer",
|
||||
"novalidate",
|
||||
"Numberish",
|
||||
"nums",
|
||||
"oklab",
|
||||
"oklch",
|
||||
"onscrollend",
|
||||
@@ -140,7 +139,6 @@
|
||||
"progressbar",
|
||||
"radiogroup",
|
||||
"Railsbyte",
|
||||
"referrerpolicy",
|
||||
"remixicon",
|
||||
"reregister",
|
||||
"resizer",
|
||||
@@ -167,7 +165,6 @@
|
||||
"slotchange",
|
||||
"smartquotes",
|
||||
"spacebar",
|
||||
"srcdoc",
|
||||
"stylesheet",
|
||||
"svgs",
|
||||
"Tabbable",
|
||||
|
||||
23
package-lock.json
generated
23
package-lock.json
generated
@@ -2488,10 +2488,6 @@
|
||||
"resolved": "packages/webawesome",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@shoelace-style/webawesome-pro": {
|
||||
"resolved": "packages/webawesome-pro",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@sindresorhus/is": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz",
|
||||
@@ -13991,25 +13987,6 @@
|
||||
"engines": {
|
||||
"node": ">=14.17.0"
|
||||
}
|
||||
},
|
||||
"packages/webawesome-pro": {
|
||||
"name": "@shoelace-style/webawesome-pro",
|
||||
"version": "3.0.0-alpha.13",
|
||||
"license": "TODO",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^4.1.0",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@shoelace-style/animations": "^1.2.0",
|
||||
"@shoelace-style/localize": "^3.2.1",
|
||||
"composed-offset-position": "^0.0.6",
|
||||
"lit": "^3.2.1",
|
||||
"qr-creator": "^1.0.0",
|
||||
"style-observer": "^0.0.7"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"engines": {
|
||||
"node": ">=14.17.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
packages/webawesome/.gitignore
vendored
4
packages/webawesome/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
_site
|
||||
dist
|
||||
dist-cdn
|
||||
src/react
|
||||
@@ -3,9 +3,9 @@ import { customElementVsCodePlugin } from 'custom-element-vs-code-integration';
|
||||
// import { customElementVuejsPlugin } from 'custom-element-vuejs-integration';
|
||||
import { parse } from 'comment-parser';
|
||||
import fs from 'fs';
|
||||
import * as path from 'node:path';
|
||||
import { pascalCase } from 'pascal-case';
|
||||
import * as url from 'url';
|
||||
import * as path from "node:path"
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
const packageData = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
||||
@@ -186,3 +186,4 @@ export default {
|
||||
// })
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import * as fs from "node:fs"
|
||||
import { anchorHeadingsPlugin } from './_utils/anchor-headings.js';
|
||||
import { codeExamplesPlugin } from './_utils/code-examples.js';
|
||||
import { copyCodePlugin } from './_utils/copy-code.js';
|
||||
@@ -41,7 +41,7 @@ export default async function (eleventyConfig) {
|
||||
*/
|
||||
const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4'];
|
||||
|
||||
const docsDir = path.join(process.env.BASE_DIR || '.', 'docs');
|
||||
const docsDir = path.join(process.env.BASE_DIR || ".", 'docs');
|
||||
const passThrough = [...passThroughExtensions.map(ext => path.join(docsDir, '**/*.' + ext))];
|
||||
|
||||
/**
|
||||
@@ -176,8 +176,9 @@ export default async function (eleventyConfig) {
|
||||
// eleventyConfig.addPlugin(formatCodePlugin());
|
||||
// }
|
||||
|
||||
let assetsDir = path.join(process.env.BASE_DIR || 'docs', 'assets');
|
||||
fs.cpSync(assetsDir, path.join(eleventyConfig.directories.output, 'assets'), { recursive: true });
|
||||
|
||||
let assetsDir = path.join(process.env.BASE_DIR || "docs", "assets")
|
||||
fs.cpSync(assetsDir, path.join(eleventyConfig.directories.output, "assets"), { recursive: true })
|
||||
|
||||
for (let glob of passThrough) {
|
||||
eleventyConfig.addPassthroughCopy(glob);
|
||||
@@ -209,6 +210,7 @@ export default async function (eleventyConfig) {
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
export const config = {
|
||||
markdownTemplateEngine: 'njk',
|
||||
dir: {
|
||||
@@ -217,4 +219,5 @@ export const config = {
|
||||
layouts: '_layouts',
|
||||
},
|
||||
templateFormats: ['njk', 'md'],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
* @module components Fetches components from custom-elements.json and exposes them in a saner format.
|
||||
*/
|
||||
import { readFileSync } from 'fs';
|
||||
import { dirname, join, resolve } from 'path';
|
||||
import { join, dirname, resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const customElementsJSON = process.env.DIST_DIR
|
||||
? join(process.env.DIST_DIR, 'custom-elements.json')
|
||||
: resolve(__dirname, '../../dist/custom-elements.json');
|
||||
? join(process.env.DIST_DIR, "custom-elements.json")
|
||||
: resolve(__dirname, '../../dist/custom-elements.json')
|
||||
|
||||
const manifest = JSON.parse(readFileSync(customElementsJSON), 'utf-8');
|
||||
|
||||
@@ -76,3 +76,4 @@ components.sort((a, b) => {
|
||||
});
|
||||
|
||||
export default components;
|
||||
|
||||
|
||||
43
packages/webawesome/docs/_data/componentsBy.js
Normal file
43
packages/webawesome/docs/_data/componentsBy.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import components from './components.js';
|
||||
|
||||
const by = {
|
||||
attribute: {},
|
||||
slot: {},
|
||||
event: {},
|
||||
method: {},
|
||||
cssPart: {},
|
||||
cssProperty: {},
|
||||
};
|
||||
|
||||
function getAll(component, type) {
|
||||
let prop = type + 's';
|
||||
if (type === 'cssProperty') {
|
||||
prop = 'cssProperties';
|
||||
}
|
||||
|
||||
return component[prop] ?? [];
|
||||
}
|
||||
|
||||
for (const componentName in components) {
|
||||
const component = components[componentName];
|
||||
|
||||
for (const type of ['attribute', 'slot', 'event', 'method', 'cssPart', 'cssProperty']) {
|
||||
for (const item of getAll(component, type)) {
|
||||
by[type][item.name] ??= [];
|
||||
by[type][item.name].push(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by descending number of components
|
||||
const sortByLengthDesc = (a, b) => b[1].length - a[1].length;
|
||||
|
||||
for (const key in by) {
|
||||
by[key] = sortObject(by[key], sortByLengthDesc);
|
||||
}
|
||||
|
||||
export default by;
|
||||
|
||||
function sortObject(obj, sorter) {
|
||||
return Object.fromEntries(Object.entries(obj).sort(sorter));
|
||||
}
|
||||
@@ -15,8 +15,9 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<wa-zoomable-frame srcdoc="" zoom="0.5" id="page_slots_iframe"></wa-zoomable-frame>
|
||||
<wa-viewport-demo viewport="1000">
|
||||
<iframe srcdoc="" id="page_slots_iframe"></iframe>
|
||||
</wa-viewport-demo>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
<li><a href="/docs/components/format-date/">Format Date</a></li>
|
||||
<li><a href="/docs/components/format-number/">Format Number</a></li>
|
||||
<li><a href="/docs/components/icon/">Icon</a></li>
|
||||
<li><a href="/docs/components/icon-button/">Icon Button</a></li>
|
||||
<li><a href="/docs/components/include/">Include</a></li>
|
||||
<li><a href="/docs/components/input/">Input</a></li>
|
||||
<li>
|
||||
@@ -174,7 +175,6 @@
|
||||
<li><a href="/docs/components/tooltip/">Tooltip</a></li>
|
||||
<li><a href="/docs/components/tree/">Tree</a></li>
|
||||
<li><a href="/docs/components/tree-item/">Tree Item</a></li>
|
||||
<li><a href="/docs/components/zoomable-frame">Zoomable Frame</a></li>
|
||||
{# PLOP_NEW_COMPONENT_PLACEHOLDER #}
|
||||
</ul>
|
||||
</wa-details>
|
||||
|
||||
4
packages/webawesome/docs/_includes/svgs/icon-button.njk
Normal file
4
packages/webawesome/docs/_includes/svgs/icon-button.njk
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 6C0 2.68629 2.68629 0 6 0H26C29.3137 0 32 2.68629 32 6V26C32 29.3137 29.3137 32 26 32H6C2.68629 32 0 29.3137 0 26V6Z" fill="var(--wa-color-neutral-fill-normal)"/>
|
||||
<path d="M23.4688 13.2188C23.5938 13.5 23.5 13.7812 23.2812 14L21.9375 15.2188C21.9688 15.4688 21.9688 15.75 21.9688 16C21.9688 16.2812 21.9688 16.5625 21.9375 16.8125L23.2812 18.0312C23.5 18.2188 23.5938 18.5312 23.4688 18.8125C23.3438 19.1875 23.1875 19.5312 23 19.875L22.8438 20.125C22.625 20.4688 22.4062 20.8125 22.1562 21.0938C21.9688 21.3438 21.6562 21.4062 21.375 21.3125L19.6562 20.7812C19.2188 21.0938 18.75 21.3438 18.2812 21.5625L17.875 23.3438C17.8125 23.625 17.5938 23.8438 17.3125 23.9062C16.875 23.9688 16.4375 24 15.9688 24C15.5312 24 15.0938 23.9688 14.6562 23.9062C14.375 23.8438 14.1562 23.625 14.0938 23.3438L13.6875 21.5625C13.1875 21.3438 12.75 21.0938 12.3125 20.7812L10.5938 21.3125C10.3125 21.4062 10 21.3438 9.8125 21.125C9.5625 20.8125 9.34375 20.4688 9.125 20.125L8.96875 19.875C8.78125 19.5312 8.625 19.1875 8.5 18.8125C8.375 18.5312 8.46875 18.25 8.6875 18.0312L10.0312 16.8125C10 16.5625 10 16.2812 10 16C10 15.75 10 15.4688 10.0312 15.2188L8.6875 14C8.46875 13.7812 8.375 13.5 8.5 13.2188C8.625 12.8438 8.78125 12.5 8.96875 12.1562L9.125 11.9062C9.34375 11.5625 9.5625 11.2188 9.8125 10.9062C10 10.6875 10.3125 10.625 10.5938 10.7188L12.3125 11.25C12.75 10.9375 13.2188 10.6562 13.6875 10.4688L14.0938 8.6875C14.1562 8.40625 14.375 8.1875 14.6562 8.125C15.0938 8.0625 15.5312 8 16 8C16.4375 8 16.875 8.0625 17.3125 8.125C17.5938 8.15625 17.8125 8.40625 17.875 8.6875L18.2812 10.4688C18.7812 10.6562 19.2188 10.9375 19.6562 11.25L21.375 10.7188C21.6562 10.625 21.9688 10.6875 22.1562 10.9062C22.4062 11.2188 22.625 11.5625 22.8438 11.9062L23 12.1562C23.1875 12.5 23.3438 12.8438 23.5 13.2188H23.4688ZM16 18.5C16.875 18.5 17.6875 18.0312 18.1562 17.25C18.5938 16.5 18.5938 15.5312 18.1562 14.75C17.6875 14 16.875 13.5 16 13.5C15.0938 13.5 14.2812 14 13.8125 14.75C13.375 15.5312 13.375 16.5 13.8125 17.25C14.2812 18.0312 15.0938 18.5 16 18.5Z" fill="var(--wa-color-neutral-on-normal)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -3,9 +3,7 @@
|
||||
<wa-card>
|
||||
<div slot="header" class="wa-split">
|
||||
<h3 class="wa-heading-m">Your Cart</h3>
|
||||
<wa-button appearance="plain" size="small" tabindex="-1">
|
||||
<wa-icon name="xmark" label="Close"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="xmark" tabindex="-1"></wa-icon-button>
|
||||
</div>
|
||||
<div class="wa-stack wa-gap-xl">
|
||||
<div class="wa-flank">
|
||||
@@ -75,9 +73,7 @@
|
||||
<div class="wa-stack">
|
||||
<div class="wa-split">
|
||||
<h3 class="wa-heading-m">To-Do</h3>
|
||||
<wa-button appearance="plain" size="small" tabindex="-1">
|
||||
<wa-icon name="plus" label="Add task"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button tabindex="-1" name="plus" label="Add task"></wa-icon-button>
|
||||
</div>
|
||||
<wa-checkbox tabindex="-1" checked>Umbrella for Adelard</wa-checkbox>
|
||||
<wa-checkbox tabindex="-1" checked>Waste-paper basket for Dora</wa-checkbox>
|
||||
@@ -102,9 +98,7 @@
|
||||
</div>
|
||||
<span class="wa-caption-m">Samwise G</span>
|
||||
</div>
|
||||
<wa-button appearance="plain" size="small" tabindex="-1">
|
||||
<wa-icon name="ellipsis" label="Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button tabindex="-1" name="ellipsis" label="Options"></wa-icon-button>
|
||||
</div>
|
||||
<div class="wa-stack wa-gap-2xs">
|
||||
<wa-progress-bar value="34" style="height: 0.5em"></wa-progress-bar>
|
||||
@@ -114,15 +108,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="wa-grid wa-align-items-center" style="--min-column-size: 1em; justify-items: center;">
|
||||
<wa-button appearance="plain" tabindex="-1">
|
||||
<wa-icon name="backward" label="Skip backward"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button appearance="plain" size="large" tabindex="-1">
|
||||
<wa-icon name="pause" label="Pause"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button appearance="plain" tabindex="-1">
|
||||
<wa-icon name="forward" label="Skip forward"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button tabindex="-1" name="backward" label="Skip backward"></wa-icon-button>
|
||||
<wa-icon-button tabindex="-1" name="pause" style="font-size: var(--wa-font-size-2xl);" label="Pause"></wa-icon-button>
|
||||
<wa-icon-button tabindex="-1" name="forward" label="Skip forward"></wa-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
</wa-card>
|
||||
@@ -265,9 +253,7 @@
|
||||
</div>
|
||||
</a>
|
||||
<wa-dropdown>
|
||||
<wa-button id="more-actions-2" slot="trigger" appearance="plain" size="small" tabindex="-1">
|
||||
<wa-icon name="ellipsis-vertical" label="View menu"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="more-actions-2" slot="trigger" name="ellipsis-vertical" label="View menu" tabindex="-1"></wa-icon-button>
|
||||
<wa-menu>
|
||||
<wa-menu-item>Copy link</wa-menu-item>
|
||||
<wa-menu-item>Rename</wa-menu-item>
|
||||
|
||||
@@ -70,9 +70,7 @@
|
||||
<div class="alignment">
|
||||
<wa-avatar></wa-avatar>
|
||||
<wa-rating></wa-rating>
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="gear" label="Settings"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="gear" label="Settings"></wa-icon-button>
|
||||
<wa-spinner></wa-spinner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -33,14 +33,14 @@
|
||||
<h1 class="title">
|
||||
<span v-content="title">{{ title }}</span>
|
||||
<template v-if="saved || tweaked">
|
||||
<wa-button appearance="plain" size="small" @click="rename">
|
||||
<wa-icon name="pencil" label="Rename palette"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button appearance="plain" size="small" v-if="saved" class="delete" @click="deleteSaved">
|
||||
<wa-icon name="trash" label="Delete palette"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="pencil" label="Rename palette" @click="rename"></wa-icon-button>
|
||||
<wa-icon-button v-if="saved" class="delete" name="trash" label="Delete palette" @click="deleteSaved"></wa-icon-button>
|
||||
<wa-button @click="save()" :disabled="!unsavedChanges"
|
||||
:variant="unsavedChanges ? 'success' : 'neutral'" size="small" :appearance="unsavedChanges ? 'accent' : 'outlined'">
|
||||
<span slot="prefix" class="icon-modifier">
|
||||
<wa-icon name="sidebar" variant="regular"></wa-icon>
|
||||
<wa-icon name="circle-plus" class="modifier" style="color: light-dark(var(--wa-color-green-70), var(--wa-color-green-60));"></wa-icon>
|
||||
</span>
|
||||
<span v-content="unsavedChanges ? 'Save' : 'Saved'">Save</span>
|
||||
</wa-button>
|
||||
</template>
|
||||
@@ -71,8 +71,10 @@
|
||||
<wa-tag v-for="tweakHumanReadable, param in tweaksHumanReadable" with-remove @wa-remove="reset(param)" v-content="tweakHumanReadable"></wa-tag>
|
||||
</div>
|
||||
|
||||
<wa-button @click="reset()" appearance="outlined" variant="danger" size="small">
|
||||
<wa-icon slot="prefix" name="circle-xmark" variant="regular"></wa-icon>
|
||||
<wa-button @click="reset()" appearance="outlined" variant="danger">
|
||||
<span slot="prefix" class="icon-modifier">
|
||||
<wa-icon name="circle-xmark" variant="regular"></wa-icon>
|
||||
</span>
|
||||
Reset
|
||||
</wa-button>
|
||||
</wa-callout>
|
||||
@@ -128,9 +130,7 @@
|
||||
@input="tweaking.grayChroma = true" @change="tweaking.grayChroma = false">
|
||||
<div slot="label">
|
||||
Gray colorfulness
|
||||
<wa-button appearance="plain" @click="grayChroma = originalGrayChroma" class="clear-button">
|
||||
<wa-icon name="circle-xmark" label="Reset" variant="regular"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button @click="grayChroma = originalGrayChroma" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
|
||||
</div>
|
||||
</wa-slider>
|
||||
<div class="label-min">Neutral</div>
|
||||
@@ -149,9 +149,7 @@
|
||||
@change="tweaking.hue = tweaking.{{ hue }} = false">
|
||||
<div slot="label">
|
||||
Tweak {{ hue }} hue
|
||||
<wa-button appearance="plain" @click="hueShifts.{{ hue }} = 0" class="clear-button">
|
||||
<wa-icon name="circle-xmark" label="Reset" variant="regular"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button @click="hueShifts.{{ hue }} = 0" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
|
||||
</div>
|
||||
</wa-slider>
|
||||
<div class="label-min">More {{hueBefore}}</div>
|
||||
@@ -195,9 +193,7 @@ style="--min: {{ chromaScaleBounds[0] }}; --max: {{ chromaScaleBounds[1] }};">
|
||||
@change="tweaking.chroma = false">
|
||||
<div slot="label">
|
||||
Overall colorfulness
|
||||
<wa-button appearance="plain" @click="chromaScale = 1" class="clear-button">
|
||||
<wa-icon name="circle-xmark" label="Reset" variant="regular"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button @click="chromaScale = 1" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
|
||||
</div>
|
||||
</wa-slider>
|
||||
<div class="label-min">More muted</div>
|
||||
|
||||
@@ -25,6 +25,7 @@ export function codeExamplesPlugin(options = {}) {
|
||||
const pre = code.closest('pre');
|
||||
const hasButtons = !code.classList.contains('no-buttons');
|
||||
const isOpen = code.classList.contains('open') || !hasButtons;
|
||||
const isViewportDemo = code.classList.contains('viewport');
|
||||
const noEdit = code.classList.contains('no-edit');
|
||||
const id = `code-example-${uuid().slice(-12)}`;
|
||||
let preview = pre.textContent;
|
||||
@@ -34,10 +35,29 @@ export function codeExamplesPlugin(options = {}) {
|
||||
root.querySelectorAll('script').forEach(script => script.setAttribute('type', 'module'));
|
||||
preview = root.toString();
|
||||
|
||||
const escapedHtml = markdown.utils.escapeHtml(`
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Web Awesome Demo</title>
|
||||
<link rel="stylesheet" href="https://early.webawesome.com/webawesome@[version]/dist/styles/themes/default.css" />
|
||||
<link rel="stylesheet" href="https://early.webawesome.com/webawesome@[version]/dist/styles/webawesome.css" />
|
||||
<script type="module" src="https://early.webawesome.com/webawesome@[version]/dist/webawesome.loader.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
${preview}
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
||||
const codeExample = parse(`
|
||||
<div class="code-example ${isOpen ? 'open' : ''}">
|
||||
<div class="code-example ${isOpen ? 'open' : ''} ${isViewportDemo ? 'is-viewport-demo' : ''}">
|
||||
<div class="code-example-preview">
|
||||
${preview}
|
||||
|
||||
${isViewportDemo ? ` <wa-viewport-demo><iframe srcdoc="${escapedHtml}"></iframe></wa-viewport-demo>` : preview}
|
||||
|
||||
<div class="code-example-resizer" aria-hidden="true">
|
||||
<wa-icon name="grip-lines-vertical"></wa-icon>
|
||||
</div>
|
||||
|
||||
@@ -32,7 +32,7 @@ export function copyCodePlugin(eleventyConfig, options = {}) {
|
||||
}
|
||||
|
||||
// Add a copy button
|
||||
pre.innerHTML += `<wa-button href="#${preId}" class="block-link-icon" appearance="plain" size="small"><wa-icon name="link" label="Copy link"></wa-icon></wa-button>
|
||||
pre.innerHTML += `<wa-icon-button href="#${preId}" class="block-link-icon" name="link"></wa-icon-button>
|
||||
<wa-copy-button from="${codeId}" class="copy-button"></wa-copy-button>`;
|
||||
});
|
||||
|
||||
|
||||
@@ -32,9 +32,7 @@
|
||||
</header>
|
||||
<nav slot="subheader">
|
||||
<div class="wa-cluster" style="flex-wrap: nowrap">
|
||||
<wa-button data-toggle-nav appearance="plain" size="small">
|
||||
<wa-icon name="bars" label="Menu"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button data-toggle-nav name="bars" label="Menu"></wa-icon-button>
|
||||
<wa-breadcrumb style="font-size: var(--wa-font-size-s)">
|
||||
<wa-breadcrumb-item>Field Guides</wa-breadcrumb-item>
|
||||
<wa-breadcrumb-item>Owls</wa-breadcrumb-item>
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
<wa-page class="wa-dark">
|
||||
<header slot="header">
|
||||
<div class="wa-cluster">
|
||||
<wa-button data-toggle-nav appearance="plain" size="small">
|
||||
<wa-icon name="bars" label="Menu"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="bars" label="Menu" data-toggle-nav></wa-icon-button>
|
||||
<wa-icon name="record-vinyl"></wa-icon>
|
||||
<span class="wa-heading-m">radiogaga</span>
|
||||
</div>
|
||||
@@ -32,10 +30,7 @@
|
||||
</wa-input>
|
||||
<div class="wa-split">
|
||||
<h2 class="wa-heading-s">For You</h2>
|
||||
<wa-button id="settings" appearance="plain" size="small">
|
||||
<wa-icon name="gear" label="Settings"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-tooltip for="settings">Settings</wa-tooltip>
|
||||
<wa-icon-button id="settings" name="gear" label="Settings"></wa-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
<nav slot="navigation">
|
||||
@@ -125,18 +120,12 @@
|
||||
</ul>
|
||||
</nav>
|
||||
<div slot="main-header">
|
||||
<wa-button id="back" appearance="plain" size="small">
|
||||
<wa-icon name="chevron-left" label="Back"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="back" name="chevron-left" label="Back"></wa-icon-button>
|
||||
<wa-tooltip for="back" placement="bottom" distance="2">Back</wa-tooltip>
|
||||
<div class="wa-cluster">
|
||||
<wa-button id="favorite" appearance="plain" size="small">
|
||||
<wa-icon name="heart" label="Favorite" variant="regular"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="favorite" name="heart" variant="regular" label="Favorite"></wa-icon-button>
|
||||
<wa-tooltip for="favorite" placement="bottom" distance="2">Favorite</wa-tooltip>
|
||||
<wa-button id="options" appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="options" name="ellipsis" label="Options"></wa-icon-button>
|
||||
<wa-tooltip for="options" placement="bottom" distance="2">Options</wa-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -163,16 +152,10 @@
|
||||
</div>
|
||||
<div id="play-controls" class="wa-split wa-gap-xl">
|
||||
<div class="wa-cluster wa-gap-xl">
|
||||
<wa-button variant="brand" size="large">
|
||||
<wa-icon name="play" label="Play"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="shuffle" label="Shuffle"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="play" label="Play"></wa-icon-button>
|
||||
<wa-icon-button name="shuffle" label="Shuffle"></wa-icon-button>
|
||||
</div>
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="plus" label="Add to Library"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="plus" label="Add to Library"></wa-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -184,9 +167,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:27</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
@@ -196,9 +177,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">2:36</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
@@ -208,9 +187,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">2:51</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
@@ -220,9 +197,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:05</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
@@ -232,9 +207,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">1:56</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
@@ -244,9 +217,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:32</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
@@ -256,9 +227,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">2:46</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
@@ -268,9 +237,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:27</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
@@ -280,9 +247,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">2:13</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
@@ -295,9 +260,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">2:55</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
@@ -310,9 +273,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:10</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wa-split">
|
||||
@@ -325,9 +286,7 @@
|
||||
</span>
|
||||
<span class="wa-cluster">
|
||||
<span class="wa-caption-m">3:22</span>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="ellipsis" label="Song Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="ellipsis" label="Song Options"></wa-icon-button>
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
@@ -421,8 +380,8 @@
|
||||
aspect-ratio: 1;
|
||||
color: var(--wa-color-brand-fill-loud);
|
||||
display: flex;
|
||||
height: var(--flank-size);
|
||||
justify-content: center;
|
||||
padding-block: 0.5em;
|
||||
}
|
||||
#recent wa-icon {
|
||||
border-radius: var(--wa-border-radius-s);
|
||||
@@ -461,14 +420,16 @@
|
||||
[slot='main-header'] {
|
||||
background-color: var(--wa-color-surface-raised);
|
||||
}
|
||||
#play-controls wa-button::part(base) {
|
||||
#play-controls wa-icon-button::part(base) {
|
||||
border: var(--wa-border-width-l) var(--wa-border-style) currentColor;
|
||||
border-radius: var(--wa-border-radius-circle);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
#play-controls wa-button:has(wa-icon[name='play'])::part(base) {
|
||||
#play-controls wa-icon-button[name='play']::part(base) {
|
||||
background-color: var(--wa-color-brand-fill-loud);
|
||||
border: none;
|
||||
font-size: 2.5rem;
|
||||
color: var(--wa-color-brand-on-loud);
|
||||
font-size: 3rem;
|
||||
padding: 0.5em 0.45em 0.5em 0.55em;
|
||||
}
|
||||
[slot='main-footer'].wa-grid > * {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { domChange, ThemeAspect } from './theme-picker.js';
|
||||
import { domChange, nextFrame, ThemeAspect } from './theme-picker.js';
|
||||
|
||||
const presetTheme = new ThemeAspect({
|
||||
defaultValue: 'default',
|
||||
@@ -33,7 +33,7 @@ const presetTheme = new ThemeAspect({
|
||||
|
||||
if (instant) {
|
||||
// If no VT, delay by 1 frame to make it smoother
|
||||
await new Promise(requestAnimationFrame);
|
||||
await nextFrame();
|
||||
}
|
||||
|
||||
oldStylesheet.remove();
|
||||
|
||||
@@ -6,4 +6,3 @@ Prism.languages.markup={comment:{pattern:/<!--(?:(?!<!--)[\s\S])*?-->/,greedy:!0
|
||||
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/};
|
||||
Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp("(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])"),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript;
|
||||
!function(){if("undefined"!=typeof Prism){var n,s,a="";Prism.plugins.customClass={add:function(s){n=s},map:function(n){s="function"==typeof n?n:function(s){return n[s]||s}},prefix:function(n){a=n||""},apply:t},Prism.hooks.add("wrap",(function(e){if(n){var u=n({content:e.content,type:e.type,language:e.language});Array.isArray(u)?e.classes.push.apply(e.classes,u):u&&e.classes.push(u)}(s||a)&&(e.classes=e.classes.map((function(n){return t(n,e.language)})))}))}function t(n,t){return a+(s?s(n,t):n)}}();
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ window.addEventListener('scroll', updateScrollClass);
|
||||
window.addEventListener('turbo:render', updateScrollClass);
|
||||
updateScrollClass();
|
||||
|
||||
// Restore scroll position after components are defined
|
||||
// Restore scroll position after components are definedAdd commentMore actions
|
||||
allDefined().then(() => {
|
||||
const navigationType = getNavigationType();
|
||||
const key = `wa-scroll-y-[${location.pathname}]`;
|
||||
|
||||
@@ -79,12 +79,10 @@ const sidebar = {
|
||||
let append = [...badges];
|
||||
|
||||
if (entity.delete) {
|
||||
let deleteButton = Object.assign(document.createElement('wa-button'), {
|
||||
appearance: 'plain',
|
||||
variant: 'danger',
|
||||
size: 'small',
|
||||
let deleteButton = Object.assign(document.createElement('wa-icon-button'), {
|
||||
name: 'trash',
|
||||
label: 'Delete',
|
||||
className: 'delete',
|
||||
innerHTML: '<wa-icon name="trash" label="Delete"></wa-icon>',
|
||||
});
|
||||
deleteButton.addEventListener('click', () => entity.delete());
|
||||
append.push(deleteButton);
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { domChange } from './util/dom-change.js';
|
||||
export { domChange };
|
||||
|
||||
export function nextFrame() {
|
||||
return new Promise(resolve => requestAnimationFrame(resolve));
|
||||
}
|
||||
|
||||
export class ThemeAspect {
|
||||
constructor(options) {
|
||||
Object.assign(this, options);
|
||||
this.set();
|
||||
this.syncIframes();
|
||||
|
||||
// Update when local storage changes.
|
||||
// That way changes in one window will propagate to others (including iframes).
|
||||
@@ -64,30 +67,6 @@ export class ThemeAspect {
|
||||
this.syncUI();
|
||||
}
|
||||
|
||||
async syncIframes() {
|
||||
await customElements.whenDefined('wa-zoomable-frame');
|
||||
await new Promise(requestAnimationFrame);
|
||||
|
||||
// Sync to wa-zoomable-frame iframes
|
||||
let dark = this.computedValue === 'dark';
|
||||
for (let zoomableEl of document.querySelectorAll('wa-zoomable-frame')) {
|
||||
const iframe = zoomableEl.iframe;
|
||||
|
||||
const applyToIframe = () => {
|
||||
try {
|
||||
iframe.contentDocument.documentElement.classList.toggle('wa-dark', dark);
|
||||
} catch (e) {
|
||||
// Silently handle access issues
|
||||
}
|
||||
};
|
||||
|
||||
// Try immediately
|
||||
applyToIframe();
|
||||
// Also listen for load in case it wasn't ready
|
||||
iframe.addEventListener('load', applyToIframe, { once: true });
|
||||
}
|
||||
}
|
||||
|
||||
syncUI(container = document) {
|
||||
for (let picker of container.querySelectorAll(this.picker)) {
|
||||
picker.setAttribute('value', this.value);
|
||||
@@ -108,22 +87,27 @@ const colorScheme = new ThemeAspect({
|
||||
},
|
||||
|
||||
applyChange() {
|
||||
// Toggle the dark mode class with view transition
|
||||
const updateTheme = () => {
|
||||
// Toggle the dark mode class
|
||||
domChange(() => {
|
||||
let dark = this.computedValue === 'dark';
|
||||
document.documentElement.classList.toggle(`wa-dark`, dark);
|
||||
document.documentElement.dispatchEvent(new CustomEvent('wa-color-scheme-change', { detail: { dark } }));
|
||||
this.syncIframes();
|
||||
};
|
||||
|
||||
if (document.startViewTransition) {
|
||||
document.startViewTransition(() => domChange(updateTheme));
|
||||
} else {
|
||||
domChange(updateTheme);
|
||||
}
|
||||
syncViewportDemoColorSchemes();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function syncViewportDemoColorSchemes() {
|
||||
const isDark = document.documentElement.classList.contains('wa-dark');
|
||||
|
||||
// Update viewport demo color schemes in code examples
|
||||
document.querySelectorAll('.code-example.is-viewport-demo wa-viewport-demo').forEach(demo => {
|
||||
demo.querySelectorAll('iframe').forEach(iframe => {
|
||||
iframe.contentWindow.document.documentElement?.classList?.toggle('wa-dark', isDark);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Update the color scheme when the preference changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => colorScheme.set());
|
||||
|
||||
@@ -137,3 +121,12 @@ document.addEventListener('keydown', event => {
|
||||
colorScheme.set(colorScheme.get() === 'dark' ? 'light' : 'dark');
|
||||
}
|
||||
});
|
||||
|
||||
// When rendering a code example with a viewport demo, set the theme to match initially
|
||||
document.querySelectorAll('.code-example.is-viewport-demo wa-viewport-demo iframe').forEach(iframe => {
|
||||
const isDark = document.documentElement.classList.contains('wa-dark');
|
||||
|
||||
iframe.addEventListener('load', () => {
|
||||
iframe.contentWindow.document.documentElement?.classList?.toggle('wa-dark', isDark);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -116,12 +116,10 @@
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
border-left: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet) !important; /* TODO - remove after native styles refactor */
|
||||
background: var(--wa-color-surface-default) !important; /* TODO - remove after native styles refactor */
|
||||
color: var(--wa-color-text-quiet) !important; /* TODO - remove after native styles refactor */
|
||||
}
|
||||
&:hover {
|
||||
border-left: var(--wa-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet) !important; /* TODO - remove after native styles refactor */
|
||||
background: var(--wa-color-surface-default) !important; /* TODO - remove after native styles refactor */
|
||||
color: var(--wa-color-text-quiet) !important; /* TODO - remove after native styles refactor */
|
||||
}
|
||||
|
||||
&:first-of-type {
|
||||
|
||||
@@ -9,10 +9,8 @@ wa-copy-button.copy-button {
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
padding: 0.25rem;
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
color: white;
|
||||
}
|
||||
&:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
|
||||
@@ -193,13 +193,10 @@ wa-badge.pro {
|
||||
}
|
||||
}
|
||||
|
||||
wa-button.delete {
|
||||
wa-icon-button.delete {
|
||||
vertical-align: -0.2em;
|
||||
margin-inline-start: var(--wa-space-xs);
|
||||
|
||||
&:hover wa-icon {
|
||||
color: var(--wa-color-danger-on-quiet);
|
||||
}
|
||||
|
||||
&:not(li:hover > *, :focus) {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -211,9 +208,12 @@ wa-badge.pro {
|
||||
}
|
||||
}
|
||||
|
||||
wa-button.delete {
|
||||
&::part(base):hover {
|
||||
wa-icon-button.delete {
|
||||
&:hover {
|
||||
color: var(--wa-color-danger-on-quiet);
|
||||
}
|
||||
|
||||
&::part(base):hover {
|
||||
background: var(--wa-color-danger-fill-quiet);
|
||||
}
|
||||
|
||||
@@ -495,10 +495,8 @@ table.colors {
|
||||
tbody {
|
||||
tr {
|
||||
border: none;
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,6 +545,27 @@ table.colors {
|
||||
--icon-color: var(--wa-color-success-fill-quiet);
|
||||
}
|
||||
|
||||
.icon-modifier {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
|
||||
.modifier {
|
||||
position: absolute;
|
||||
bottom: -0.1em;
|
||||
right: -0.3em;
|
||||
font-size: 60%;
|
||||
|
||||
&::part(svg) {
|
||||
stroke: var(--background-color, var(--wa-color-surface-default));
|
||||
stroke-width: 100px;
|
||||
paint-order: stroke;
|
||||
overflow: visible;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Layout Examples */
|
||||
.layout-example-boundary {
|
||||
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
|
||||
|
||||
@@ -124,7 +124,7 @@ html.wa-theme-tailspin .preview-container {
|
||||
&::part(footer) {
|
||||
border: none;
|
||||
}
|
||||
& wa-button {
|
||||
& wa-icon-button {
|
||||
color: var(--wa-color-base-50);
|
||||
}
|
||||
}
|
||||
@@ -226,11 +226,11 @@ html.wa-theme-brutalist .preview-container {
|
||||
--wa-color-neutral-border-quiet: color-mix(in oklab, var(--wa-color-gray-30), white 40%);
|
||||
}
|
||||
|
||||
.message-composer [slot='header'] wa-button::part(base) {
|
||||
.message-composer [slot='header'] wa-icon-button::part(base) {
|
||||
color: var(--wa-color-neutral-on-loud);
|
||||
}
|
||||
|
||||
.message-composer .grouped-buttons wa-button::part(base):hover {
|
||||
.message-composer .grouped-buttons wa-icon-button::part(base):hover {
|
||||
background-color: var(--wa-color-neutral-fill-normal);
|
||||
color: var(--wa-color-text-normal);
|
||||
}
|
||||
@@ -421,7 +421,7 @@ html.wa-theme-playful .preview-container {
|
||||
--wa-color-neutral-fill-quiet: var(--wa-color-gray-90);
|
||||
}
|
||||
|
||||
.message-composer wa-button {
|
||||
.message-composer wa-icon-button {
|
||||
color: var(--wa-text-color-normal);
|
||||
font-size: var(--wa-font-size-l);
|
||||
}
|
||||
@@ -662,12 +662,12 @@ html.wa-theme-premium .preview-container {
|
||||
--padding: var(--wa-space-s) var(--wa-space-xl);
|
||||
}
|
||||
|
||||
.message-composer .grouped-buttons wa-button::part(base) {
|
||||
.message-composer .grouped-buttons wa-icon-button::part(base) {
|
||||
block-size: var(--wa-form-control-height-s);
|
||||
inline-size: var(--wa-form-control-height-s);
|
||||
justify-content: center;
|
||||
}
|
||||
.message-composer .grouped-buttons wa-button::part(base):hover {
|
||||
.message-composer .grouped-buttons wa-icon-button::part(base):hover {
|
||||
background-color: var(--wa-color-neutral-fill-normal);
|
||||
color: var(--wa-color-text-normal);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--wa-space-xs);
|
||||
wa-icon-button {
|
||||
font-size: var(--wa-font-size-l);
|
||||
color: var(--wa-color-text-quiet);
|
||||
|
||||
wa-button:has(wa-icon) {
|
||||
&:not(:hover, :focus) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -128,11 +127,11 @@
|
||||
|
||||
> input {
|
||||
font: inherit;
|
||||
margin-block: 0.75em;
|
||||
margin-block: calc(-1 * var(--wa-space-smaller));
|
||||
field-sizing: content;
|
||||
}
|
||||
|
||||
wa-button {
|
||||
wa-icon-button {
|
||||
font-size: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,11 @@ const template = `
|
||||
<span class="editable-text">
|
||||
<template v-if="isEditing">
|
||||
<input ref="input" class="wa-size-s" :aria-label="label" :value="value" @input="handleInput" @keydown.enter="done" @keydown.esc="cancel" @blur="handleBlur" />
|
||||
<wa-button appearance="plain" v-if="blur !== 'done'" @click="done">
|
||||
<wa-icon name="check" label="Done editing"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button v-if="blur !== 'done'" name="check" label="Done editing" @click="done"></wa-icon-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="text" ref="wrapper" @focus="edit" @click="edit" tabindex="0">{{ value }}</span>
|
||||
<wa-button appearance="plain" @click="edit">
|
||||
<wa-icon name="pencil" :label="'Edit ' + label"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="pencil" :label="'Edit ' + label" @click="edit"></wa-icon-button>
|
||||
</template>
|
||||
</span>
|
||||
`;
|
||||
|
||||
@@ -8,9 +8,7 @@ const template = `
|
||||
<div class="ui-slider-header">
|
||||
<label :for="sliderId">{{ label }}</label>
|
||||
<info-tip v-if="clearable && (value !== defaultValue ?? initialValue)" :text="'Reset to ' + valueFormatter(defaultValue ?? initialValue)">
|
||||
<wa-button @click="value = defaultValue ?? initialValue" class="clear-button">
|
||||
<wa-icon name="circle-xmark" library="system" variant="regular" :label="'Reset to ' + tooltipFormatter(defaultValue ?? initialValue)"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button @click="value = defaultValue ?? initialValue" class="clear-button" name="circle-xmark" library="system" variant="regular" :label="'Reset to ' + tooltipFormatter(defaultValue ?? initialValue)"></wa-icon-button>
|
||||
</info-tip>
|
||||
</div>
|
||||
<info-tip v-if="$slots.min" :text="'Set to min (' + valueFormatter(min) + ')'">
|
||||
|
||||
@@ -103,19 +103,6 @@ It's often helpful to have a button that works like a link. This is possible by
|
||||
<wa-button href="/assets/images/logo.svg" download="shoelace.svg">Download</wa-button>
|
||||
```
|
||||
|
||||
### Icon Buttons
|
||||
|
||||
When only an [icon](/docs/components/icon) is slotted into the `label` slot, the button becomes an icon button. In this case, it's important to give the icon a label for users with assistive devices. Icon buttons can use any appearance or variant.
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-cluster">
|
||||
<wa-button variant="neutral" appearance="accent"><wa-icon name="house" label="Home"></wa-icon></wa-button>
|
||||
<wa-button variant="neutral" appearance="outlined"><wa-icon name="house" label="Home"></wa-icon></wa-button>
|
||||
<wa-button variant="neutral" appearance="filled"><wa-icon name="house" label="Home"></wa-icon></wa-button>
|
||||
<wa-button variant="neutral" appearance="plain"><wa-icon name="house" label="Home"></wa-icon></wa-button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Setting a Custom Width
|
||||
|
||||
As expected, buttons can be given a custom width by setting the `width` CSS property. This is useful for making buttons span the full width of their container on smaller screens.
|
||||
|
||||
@@ -57,9 +57,7 @@ If using SSR, you need to also use the `with-header` attribute to add a header t
|
||||
<wa-card class="card-header">
|
||||
<div slot="header" class="wa-split">
|
||||
Header Title
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="gear" variant="solid" label="Settings"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="gear" variant="solid" label="Settings" class="wa-size-m"></wa-icon-button>
|
||||
</div>
|
||||
|
||||
This card has a header. You can put all sorts of things in it!
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
let url = new URL(location);
|
||||
const pushedURL = false;
|
||||
|
||||
const matchers = {
|
||||
default(textContent, query) {
|
||||
return textContent.includes(query);
|
||||
},
|
||||
|
||||
i(textContent, query) {
|
||||
return textContent.toLowerCase().includes(query.toLowerCase());
|
||||
},
|
||||
|
||||
regexp(textContent, query) {
|
||||
query.lastIndex = 0;
|
||||
return query.test(textContent);
|
||||
},
|
||||
};
|
||||
|
||||
matchers.iregexp = matchers.regexp; // i is baked into the query
|
||||
|
||||
function filterByName(value) {
|
||||
const previousFilter = url.searchParams.get('name') || '';
|
||||
url = new URL(location);
|
||||
|
||||
if (value) {
|
||||
const isRegexp = name_search_regexp.checked;
|
||||
const i = !name_search_i.checked;
|
||||
const query = isRegexp ? new RegExp(value, 'gmsv' + (i ? 'i' : '')) : value;
|
||||
const matcherId = (i ? 'i' : '') + (isRegexp ? 'regexp' : '');
|
||||
const matcher = matchers[matcherId] ?? matchers.default;
|
||||
|
||||
for (const th of document.querySelectorAll('table tbody th:first-child')) {
|
||||
const tr = th.parentNode;
|
||||
const matches = matcher(th.textContent, query);
|
||||
tr.toggleAttribute('hidden', !matches);
|
||||
}
|
||||
url.searchParams.set('name', value);
|
||||
|
||||
if (matcherId) {
|
||||
url.searchParams.set('match', matcherId);
|
||||
} else {
|
||||
url.searchParams.delete('match');
|
||||
}
|
||||
} else {
|
||||
for (const tr of document.querySelectorAll('table tbody tr[hidden]')) {
|
||||
tr.removeAttribute('hidden');
|
||||
}
|
||||
url.searchParams.delete('name');
|
||||
url.searchParams.delete('match');
|
||||
}
|
||||
|
||||
if (value !== previousFilter) {
|
||||
history[pushedURL ? 'replaceState' : 'pushState'](null, '', url);
|
||||
}
|
||||
|
||||
// Update heading counts
|
||||
for (const h2 of document.querySelectorAll('h2:has(+ table)')) {
|
||||
const count = h2.querySelector('.count');
|
||||
if (!count) continue;
|
||||
const table = h2.nextElementSibling;
|
||||
const visibleRows = table.querySelectorAll('tbody tr:not([hidden])').length;
|
||||
count.textContent = visibleRows;
|
||||
const outlineLink = document.querySelector(`#outline-standard a[href="#${h2.id}"]`);
|
||||
if (outlineLink) {
|
||||
// Why not just = h2.textContent? To skip the "Jump to heading" link
|
||||
outlineLink.textContent = '';
|
||||
outlineLink.append(...[...h2.childNodes].slice(0, 3).map(n => n.cloneNode(true)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name_search.value) {
|
||||
filterByName(name_search.value);
|
||||
}
|
||||
|
||||
name_search_group.addEventListener('input', e => filterByName(name_search.value));
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: Component Cheatsheet
|
||||
layout: docs
|
||||
unlisted: true
|
||||
---
|
||||
|
||||
<style>
|
||||
table code {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>
|
||||
This page lists every bit of syntax used by every Web Awesome component and which components share it.
|
||||
For these times when your memory is failing, or to simply explore the possibilities!
|
||||
</p>
|
||||
|
||||
<fieldset id="name_search_group">
|
||||
<legend>Filter by name</legend>
|
||||
<wa-input type="search" with-clear id="name_search"></wa-input>
|
||||
<wa-checkbox id="name_search_i" checked>Case sensitive</wa-checkbox>
|
||||
<wa-checkbox id="name_search_regexp">Regular expression</wa-checkbox>
|
||||
</fieldset>
|
||||
|
||||
<script>
|
||||
{
|
||||
let url = new URL(location);
|
||||
if (url.searchParams.get("name")) {
|
||||
name_search.value = url.searchParams.get("name");
|
||||
}
|
||||
|
||||
if (url.searchParams.get("match")) {
|
||||
let matcherId = url.searchParams.get("match");
|
||||
let caseSensitive = !matcherId.startsWith("i");
|
||||
let isRegexp = matcherId.endsWith("regexp");
|
||||
|
||||
customElements.whenDefined("wa-checkbox").then(async () => {
|
||||
await Promise.all([
|
||||
name_search_i.updateComplete,
|
||||
name_search_regexp.updateComplete,
|
||||
]);
|
||||
name_search_i.checked = caseSensitive;
|
||||
name_search_regexp.checked = isRegexp;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="module" src="/docs/components/cheatsheet.js"></script>
|
||||
|
||||
{% for type, all in componentsBy -%}
|
||||
{% set typeTitle = "CSS custom properties" if type == "cssProperty" else ("CSS parts" if type == "cssPart" else (type | title) + "s") %}
|
||||
<h2 id="{{ typeTitle | slugify }}">
|
||||
All <span class="count">{{ (all | keys).length }}</span>
|
||||
{{ typeTitle }}
|
||||
</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Components</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for name, thingComponents in all -%}
|
||||
<tr>
|
||||
<th><code>{{ name }}{{ "()" if type == "method" }}</code></th>
|
||||
<td>
|
||||
{% set componentLinks = [] %}
|
||||
{% for component in thingComponents %}
|
||||
{%- set link -%}
|
||||
<a href="../{{ component.slug }}"><code><{{ component.tagName }}></code></a>
|
||||
{%- endset -%}
|
||||
{# https://giuliachiola.dev/posts/add-items-to-an-array-in-nunjucks/ #}
|
||||
{% set componentLinks = (componentLinks.push(link), componentLinks) %}
|
||||
{%- endfor -%}
|
||||
{% if thingComponents.length > 1 %}
|
||||
<details open>
|
||||
<summary><strong>{{ thingComponents.length }}</strong> components</summary>
|
||||
{{ componentLinks | safe }}
|
||||
</details>
|
||||
{% else %}
|
||||
{{ componentLinks | safe }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
|
||||
{%- endfor %}
|
||||
@@ -49,7 +49,7 @@ Use the `expand-icon` and `collapse-icon` slots to change the expand and collaps
|
||||
</style>
|
||||
```
|
||||
|
||||
### HTML in Summary
|
||||
### HTML in summary
|
||||
|
||||
To use HTML in the summary, use the `summary` slot.
|
||||
Links and other interactive elements will still retain their behavior:
|
||||
@@ -67,7 +67,7 @@ Links and other interactive elements will still retain their behavior:
|
||||
</wa-details>
|
||||
```
|
||||
|
||||
### Right-to-Left Languages
|
||||
### Right-to-Left languages
|
||||
|
||||
The details component automatically adapts to right-to-left languages:
|
||||
|
||||
@@ -104,23 +104,40 @@ Use the `appearance` attribute to change the element’s visual appearance.
|
||||
|
||||
### Grouping Details
|
||||
|
||||
Use the `name` attribute to create accordion-like behavior where only one details element with the same name can be open at a time. This matches the behavior of native `<details>` elements.
|
||||
Details are designed to function independently, but you can simulate a group or "accordion" where only one is shown at a time by listening for the `wa-show` event.
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-stack">
|
||||
<wa-details name="group-1" summary="Section 1" open>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
<div class="details-group-example">
|
||||
<wa-details summary="First" open>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</wa-details>
|
||||
|
||||
<wa-details name="group-1" summary="Section 2">
|
||||
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam,
|
||||
eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
|
||||
<wa-details summary="Second">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</wa-details>
|
||||
|
||||
<wa-details name="group-1" summary="Section 3">
|
||||
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque
|
||||
corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident.
|
||||
<wa-details summary="Third">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</wa-details>
|
||||
</div>
|
||||
```
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.details-group-example');
|
||||
|
||||
// Close all other details when one is shown
|
||||
container.addEventListener('wa-show', event => {
|
||||
if (event.target.localName === 'wa-details') {
|
||||
[...container.querySelectorAll('wa-details')].map(details => (details.open = event.target === details));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.details-group-example wa-details:not(:last-of-type) {
|
||||
margin-bottom: var(--wa-space-2xs);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
@@ -67,28 +67,24 @@ Footers can be used to display titles and more. Use the `footer` slot to add a f
|
||||
</script>
|
||||
```
|
||||
|
||||
### Opening and Closing Dialogs Declaratively
|
||||
### Dismissing Dialogs
|
||||
|
||||
You can open and close dialogs with JavaScript by toggling the `open` attribute, but you can also do it declaratively. Add the `data-dialog="open id"` to any button on the page, where `id` is the ID of the dialog you want to open.
|
||||
You can add the special `data-dialog="close"` attribute to a button inside the dialog to tell it to close without additional JavaScript. Alternatively, you can set the `open` property to `false` to close the dialog programmatically.
|
||||
|
||||
```html {.example}
|
||||
<wa-dialog label="Dialog" id="dialog-opening">
|
||||
<wa-dialog label="Dialog" class="dialog-dismiss">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand" data-dialog="close">Close</wa-button>
|
||||
</wa-dialog>
|
||||
|
||||
<wa-button data-dialog="open dialog-opening">Open Dialog</wa-button>
|
||||
```
|
||||
<wa-button>Open Dialog</wa-button>
|
||||
|
||||
Similarly, you can add `data-dialog="close"` to a button _inside_ of a dialog to tell it to close.
|
||||
<script>
|
||||
const dialog = document.querySelector('.dialog-dismiss');
|
||||
const openButton = dialog.nextElementSibling;
|
||||
|
||||
```html {.example}
|
||||
<wa-dialog label="Dialog" id="dialog-dismiss">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand" data-dialog="close">Close</wa-button>
|
||||
</wa-dialog>
|
||||
|
||||
<wa-button data-dialog="open dialog-dismiss">Open Dialog</wa-button>
|
||||
openButton.addEventListener('click', () => dialog.open = true);
|
||||
</script>
|
||||
```
|
||||
|
||||
### Custom Width
|
||||
@@ -135,13 +131,11 @@ By design, a dialog's height will never exceed that of the viewport. As such, di
|
||||
|
||||
### Header Actions
|
||||
|
||||
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [buttons](/docs/components/button) if needed.
|
||||
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [icon buttons](/docs/components/icon-button) if needed.
|
||||
|
||||
```html {.example}
|
||||
<wa-dialog label="Dialog" class="dialog-header-actions">
|
||||
<wa-button class="new-window" slot="header-actions" appearance="plain">
|
||||
<wa-icon name="arrow-up-right-from-square" variant="solid" label="Open in new window"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button class="new-window" slot="header-actions" name="arrow-up-right-from-square" variant="solid"></wa-icon-button>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand" data-dialog="close">Close</wa-button>
|
||||
</wa-dialog>
|
||||
|
||||
@@ -65,28 +65,24 @@ Footers can be used to display titles and more. Use the `footer` slot to add a f
|
||||
</script>
|
||||
```
|
||||
|
||||
### Opening and Closing Drawers Declaratively
|
||||
### Dismissing Drawers
|
||||
|
||||
You can open and close drawers with JavaScript by toggling the `open` attribute, but you can also do it declaratively. Add the `data-drawer="open id"` to any button on the page, where `id` is the ID of the drawer you want to open.
|
||||
You can add the special `data-drawer="close"` attribute to a button inside the drawer to tell it to close without additional JavaScript. Alternatively, you can set the `open` property to `false` to close the drawer programmatically.
|
||||
|
||||
```html {.example}
|
||||
<wa-drawer label="Drawer" id="drawer-opening">
|
||||
<wa-drawer label="Drawer" class="drawer-dismiss">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand" data-drawer="close">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
<wa-button data-drawer="open drawer-opening">Open Drawer</wa-button>
|
||||
```
|
||||
<wa-button>Open Drawer</wa-button>
|
||||
|
||||
Similarly, you can add `data-drawer="close"` to a button _inside_ of a drawer to tell it to close.
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-dismiss');
|
||||
const openButton = drawer.nextElementSibling;
|
||||
|
||||
```html {.example}
|
||||
<wa-drawer label="Drawer" id="drawer-dismiss">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand" data-drawer="close">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
<wa-button data-drawer="open drawer-dismiss">Open Drawer</wa-button>
|
||||
openButton.addEventListener('click', () => drawer.open = true);
|
||||
</script>
|
||||
```
|
||||
|
||||
### Slide in From Start
|
||||
@@ -193,13 +189,11 @@ By design, a drawer's height will never exceed 100% of its container. As such, d
|
||||
|
||||
### Header Actions
|
||||
|
||||
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [buttons](/docs/components/button) if needed.
|
||||
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [icon buttons](/docs/components/icon-button) if needed.
|
||||
|
||||
```html {.example}
|
||||
<wa-drawer label="Drawer" class="drawer-header-actions">
|
||||
<wa-button class="new-window" slot="header-actions" appearance="plain">
|
||||
<wa-icon name="arrow-up-right-from-square" variant="solid" label="Open in new window"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button class="new-window" slot="header-actions" name="arrow-up-right-from-square" variant="solid"></wa-icon-button>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand" data-drawer="close">Close</wa-button>
|
||||
</wa-drawer>
|
||||
|
||||
76
packages/webawesome/docs/docs/components/icon-button.md
Normal file
76
packages/webawesome/docs/docs/components/icon-button.md
Normal file
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Icon Button
|
||||
description: Icons buttons are simple, icon-only buttons that can be used for actions and in toolbars.
|
||||
tags: [actions, apps]
|
||||
icon: icon-button
|
||||
---
|
||||
|
||||
For a full list of icons that come bundled with Web Awesome, refer to the [icon component](/docs/components/icon).
|
||||
|
||||
```html {.example}
|
||||
<wa-icon-button name="gear" label="Settings"></wa-icon-button>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Sizes
|
||||
|
||||
Icon buttons inherit their parent element's `font-size`.
|
||||
|
||||
```html {.example}
|
||||
<wa-icon-button name="pen-to-square" variant="solid" label="Edit" style="font-size: 1.5rem;"></wa-icon-button>
|
||||
<wa-icon-button name="pen-to-square" variant="solid" label="Edit" style="font-size: 2rem;"></wa-icon-button>
|
||||
<wa-icon-button name="pen-to-square" variant="solid" label="Edit" style="font-size: 2.5rem;"></wa-icon-button>
|
||||
```
|
||||
|
||||
### Colors
|
||||
|
||||
Icon buttons are designed to have a uniform appearance, so their color is not inherited. However, you can still customize them by styling the `base` part.
|
||||
|
||||
```html {.example}
|
||||
<div class="icon-button-color">
|
||||
<wa-icon-button name="bold" variant="solid" label="Bold"></wa-icon-button>
|
||||
<wa-icon-button name="italic" variant="solid" label="Italic"></wa-icon-button>
|
||||
<wa-icon-button name="underline" variant="solid" label="Underline"></wa-icon-button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.icon-button-color wa-icon-button::part(base) {
|
||||
color: #b00091;
|
||||
}
|
||||
|
||||
.icon-button-color wa-icon-button::part(base):hover,
|
||||
.icon-button-color wa-icon-button::part(base):focus {
|
||||
color: #c913aa;
|
||||
}
|
||||
|
||||
.icon-button-color wa-icon-button::part(base):active {
|
||||
color: #960077;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### Link Buttons
|
||||
|
||||
Use the `href` attribute to convert the button to a link.
|
||||
|
||||
```html {.example}
|
||||
<wa-icon-button name="gear" variant="solid" label="Settings" href="https://example.com" target="_blank"></wa-icon-button>
|
||||
```
|
||||
|
||||
### Icon Button with Tooltip
|
||||
|
||||
Add a tooltip that references the `id` of the icon button to provide contextual information.
|
||||
|
||||
```html {.example}
|
||||
<wa-icon-button id="icon-button" name="gear" variant="solid" label="Settings"></wa-icon-button>
|
||||
<wa-tooltip for="icon-button">Settings</wa-tooltip>
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` attribute to disable the icon button.
|
||||
|
||||
```html {.example}
|
||||
<wa-icon-button name="gear" variant="solid" label="Settings" disabled></wa-icon-button>
|
||||
```
|
||||
@@ -38,19 +38,15 @@ Use the default slot to show a value.
|
||||
<wa-progress-bar value="50" id="progress-bar-demo">50%</wa-progress-bar>
|
||||
|
||||
<div>
|
||||
<wa-button pill appearance="filled">
|
||||
<wa-icon name="minus" label="Decrease"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button pill appearance="filled">
|
||||
<wa-icon name="plus" label="Increase"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button pill name="minus" label="Decrease"></wa-icon-button>
|
||||
<wa-icon-button pill name="plus" label="Increase"></wa-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const progressBar = document.querySelector('#progress-bar-demo');
|
||||
const subtractButton = document.querySelector('wa-button:has(wa-icon[name="minus"])');
|
||||
const addButton = document.querySelector('wa-button:has(wa-icon[name="plus"])');
|
||||
const subtractButton = document.querySelector('wa-icon-button[name="minus"]');
|
||||
const addButton = document.querySelector('wa-icon-button[name="plus"]');
|
||||
|
||||
addButton.addEventListener('click', () => {
|
||||
const value = Math.min(100, progressBar.value + 10);
|
||||
|
||||
@@ -75,3 +75,14 @@ Add the `size` attribute to the [Radio Group](/docs/components/radio-group) to c
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
### Hint
|
||||
|
||||
Add descriptive hint to a switch with the `hint` attribute. For hints that contain HTML, use the `hint` slot instead.
|
||||
|
||||
```html {.example}
|
||||
<wa-radio-group label="Select an option" name="a" value="1">
|
||||
<wa-radio value="1" hint="What should the user know about radio 1?">Option 1</wa-radio>
|
||||
<wa-radio value="2" hint="What should the user know about radio 2?">Option 2</wa-radio>
|
||||
<wa-radio value="3" hint="What should the user know about radio 3?">Option 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
@@ -131,7 +131,7 @@ You can provide custom icons by passing a function to the `getSymbol` property.
|
||||
|
||||
### Value-based Icons
|
||||
|
||||
You can also use the `getSymbol` property to render different icons based on value and/or whether the icon is currently selected.
|
||||
You can also use the `getSymbol` property to render different icons based on value.
|
||||
|
||||
```html {.example}
|
||||
<wa-rating label="Rating" class="rating-emojis"></wa-rating>
|
||||
@@ -142,7 +142,7 @@ You can also use the `getSymbol` property to render different icons based on val
|
||||
await customElements.whenDefined("wa-rating")
|
||||
await rating.updateComplete
|
||||
|
||||
rating.getSymbol = (value, isSelected) => {
|
||||
rating.getSymbol = value => {
|
||||
const icons = ['face-angry', 'face-frown', 'face-meh', 'face-smile', 'face-laugh'];
|
||||
return `<wa-icon name="${icons[value - 1]}"></wa-icon>`;
|
||||
};
|
||||
|
||||
@@ -101,9 +101,7 @@ You can make a tab closable by adding a close button next to the tab and inside
|
||||
<wa-tab-group class="tabs-closable">
|
||||
<wa-tab panel="general">General</wa-tab>
|
||||
<wa-tab panel="closable">Closable</wa-tab>
|
||||
<wa-button slot="nav" tabindex="-1" appearance="plain" size="small">
|
||||
<wa-icon name="xmark" label="Close the closable tab"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button slot="nav" tabindex="-1" name="xmark" label="Close the closable tab"></wa-icon-button>
|
||||
<wa-tab panel="closable-2">Advanced</wa-tab>
|
||||
|
||||
<wa-tab-panel name="general">This is the general tab panel.</wa-tab-panel>
|
||||
@@ -116,17 +114,17 @@ You can make a tab closable by adding a close button next to the tab and inside
|
||||
<wa-button disabled>Restore tab</wa-button>
|
||||
|
||||
<style>
|
||||
.tabs-closable wa-button {
|
||||
.tabs-closable wa-icon-button {
|
||||
position: relative;
|
||||
left: -1.5em;
|
||||
top: 0.675em; }
|
||||
left: -1rem;
|
||||
top: .75rem; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const tabGroup = document.querySelector('.tabs-closable');
|
||||
const generalTab = tabGroup.querySelectorAll('wa-tab')[0];
|
||||
const closableTab = tabGroup.querySelectorAll('wa-tab')[1];
|
||||
const closeButton = tabGroup.querySelector('wa-button');
|
||||
const closeButton = tabGroup.querySelector('wa-icon-button');
|
||||
const restoreButton = tabGroup.nextElementSibling.nextElementSibling;
|
||||
|
||||
// Remove the tab when the close button is clicked
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
---
|
||||
title: Zoomable Frame
|
||||
layout: component
|
||||
---
|
||||
|
||||
```html {.example}
|
||||
<wa-zoomable-frame src="https://backers.webawesome.com/" zoom="0.5">
|
||||
</wa-zoomable-frame>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Loading external content
|
||||
|
||||
Use the `src` attribute to embed external websites or resources. The URL must be accessible, and cross-origin restrictions may apply due to the Same-Origin Policy, potentially limiting access to the iframe's content.
|
||||
|
||||
```html
|
||||
<wa-zoomable-frame src="https://example.com/">
|
||||
</wa-zoomable-frame>
|
||||
```
|
||||
|
||||
The zoomable frame fills 100% width by default with a 16:9 aspect ratio. Customize this using the `aspect-ratio` CSS property.
|
||||
|
||||
```html
|
||||
<wa-zoomable-frame src="https://example.com/" style="aspect-ratio: 4/3;">
|
||||
</wa-zoomable-frame>
|
||||
```
|
||||
|
||||
Use the `srcdoc` attribute or property to display custom HTML content directly within the iframe, perfect for rendering inline content without external resources.
|
||||
|
||||
```html
|
||||
<wa-zoomable-frame srcdoc="<html><body><h1>Hello, World!</h1><p>This is inline content.</p></body></html>">
|
||||
</wa-zoomable-frame>
|
||||
```
|
||||
|
||||
:::info
|
||||
When both `src` and `srcdoc` are specified, `srcdoc` takes precedence.
|
||||
:::
|
||||
|
||||
### Controlling zoom behavior
|
||||
|
||||
Set the `zoom` attribute to control the frame's zoom level. Use `1` for 100%, `2` for 200%, `0.5` for 50%, and so on.
|
||||
|
||||
Define specific zoom increments with the `zoom-levels` attribute using space-separated percentages and decimal values like `zoom-levels="0.25 0.5 75% 100%"`.
|
||||
|
||||
```html {.example}
|
||||
<wa-zoomable-frame
|
||||
src="https://backers.webawesome.com/"
|
||||
zoom="0.5"
|
||||
zoom-levels="50% 0.75 100%"
|
||||
>
|
||||
</wa-zoomable-frame>
|
||||
```
|
||||
|
||||
### Hiding zoom controls
|
||||
|
||||
Add the `without-controls` attribute to hide the zoom control interface from the frame.
|
||||
|
||||
```html {.example}
|
||||
<wa-zoomable-frame
|
||||
src="https://backers.webawesome.com/"
|
||||
without-controls
|
||||
zoom="0.5"
|
||||
>
|
||||
</wa-zoomable-frame>
|
||||
```
|
||||
|
||||
### Preventing user interaction
|
||||
|
||||
Apply the `without-interaction` attribute to make the frame non-interactive. Note that this prevents keyboard navigation into the frame, which may impact accessibility for some users.
|
||||
|
||||
```html {.example}
|
||||
<wa-zoomable-frame
|
||||
src="https://backers.webawesome.com/"
|
||||
zoom="0.5"
|
||||
without-interaction
|
||||
>
|
||||
</wa-zoomable-frame>
|
||||
```
|
||||
@@ -1731,7 +1731,7 @@ hasOutline: false
|
||||
grid-column-end: col-end;
|
||||
}
|
||||
|
||||
.project-header wa-button {
|
||||
.project-header wa-icon-button {
|
||||
color: inherit;
|
||||
font-size: var(--wa-font-size-l);
|
||||
|
||||
@@ -2002,15 +2002,9 @@ hasOutline: false
|
||||
<span id="project-name" style="margin-inline-start: var(--wa-space-l);">Project Name</span>
|
||||
</h1>
|
||||
<div>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="magnifying-glass" label="Search"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="user" label="Account"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button appearance="plain" size="small">
|
||||
<wa-icon name="bag-shopping" label="Your Basket"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="magnifying-glass" label="Search"></wa-icon-button>
|
||||
<wa-icon-button name="user" label="Account"></wa-icon-button>
|
||||
<wa-icon-button name="bag-shopping" label="Your Basket"></wa-icon-button>
|
||||
</div>
|
||||
</header>
|
||||
<section class="strata hero">
|
||||
@@ -2148,49 +2142,31 @@ hasOutline: false
|
||||
<wa-card class="card-header card-footer">
|
||||
<div slot="header">
|
||||
<div class="grouped-buttons">
|
||||
<wa-button appearance="plain" size="small" id="bold">
|
||||
<wa-icon name="bold" label="Bold"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="bold" name="bold" label="Bold"></wa-icon-button>
|
||||
<wa-tooltip for="bold">Bold</wa-tooltip>
|
||||
<wa-button appearance="plain" size="small" id="italic">
|
||||
<wa-icon name="italic" label="Italic"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="italic" name="italic" label="Italic"></wa-icon-button>
|
||||
<wa-tooltip for="italic">Italic</wa-tooltip>
|
||||
<wa-button appearance="plain" size="small" id="strikethrough">
|
||||
<wa-icon name="strikethrough" label="strikethrough"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="strikethrough" name="strikethrough" label="strikethrough"></wa-icon-button>
|
||||
<wa-tooltip for="strikethrough">Strikethrough</wa-tooltip>
|
||||
</div>
|
||||
<div class="grouped-buttons">
|
||||
<wa-button appearance="plain" size="small" id="link">
|
||||
<wa-icon name="link" label="Link"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="link" name="link" label="Link"></wa-icon-button>
|
||||
<wa-tooltip for="link">Link</wa-tooltip>
|
||||
</div>
|
||||
<div class="grouped-buttons">
|
||||
<wa-button appearance="plain" size="small" id="list">
|
||||
<wa-icon name="list" label="Unordered List"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="list" name="list" label="Unordered List"></wa-icon-button>
|
||||
<wa-tooltip for="list">Unordered List</wa-tooltip>
|
||||
<wa-button appearance="plain" size="small" id="list-ol">
|
||||
<wa-icon name="list-ol" label="Ordered List"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="list-ol" name="list-ol" label="Ordered List"></wa-icon-button>
|
||||
<wa-tooltip for="list-ol">Ordered List</wa-tooltip>
|
||||
</div>
|
||||
<div class="grouped-buttons">
|
||||
<wa-button appearance="plain" size="small" id="block-quote">
|
||||
<wa-icon name="block-quote" label="Block Quote"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="block-quote" name="block-quote" label="Block Quote"></wa-icon-button>
|
||||
<wa-tooltip for="block-quote">Block Quote</wa-tooltip>
|
||||
</div>
|
||||
<div class="grouped-buttons">
|
||||
<wa-button appearance="plain" size="small" id="code">
|
||||
<wa-icon name="code" label="Code"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="code" name="code" label="Code"></wa-icon-button>
|
||||
<wa-tooltip for="code">Code</wa-tooltip>
|
||||
<wa-button appearance="plain" size="small" id="inline-code">
|
||||
<wa-icon name="terminal" label="Inline Code"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="inline-code" name="terminal" label="Inline Code"></wa-icon-button>
|
||||
<wa-tooltip for="inline-code">Inline Code</wa-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2200,37 +2176,23 @@ hasOutline: false
|
||||
<div slot="footer">
|
||||
<div class="tools">
|
||||
<div class="grouped-buttons">
|
||||
<wa-button appearance="plain" size="small" id="add-file">
|
||||
<wa-icon name="circle-plus" label="Add File"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="add-file" name="circle-plus" label="Add File"></wa-icon-button>
|
||||
<wa-tooltip for="add-file">Add File</wa-tooltip>
|
||||
<wa-button appearance="plain" size="small" id="formatting">
|
||||
<wa-icon name="font-case" label="Open Formatting"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="formatting" name="font-case" label="Open Formatting"></wa-icon-button>
|
||||
<wa-tooltip for="formatting">Formatting</wa-tooltip>
|
||||
<wa-button appearance="plain" size="small" id="emojis">
|
||||
<wa-icon name="face-smile" label="Emoji"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="emojis" name="face-smile" label="Emoji"></wa-icon-button>
|
||||
<wa-tooltip for="emojis">Emojis</wa-tooltip>
|
||||
<wa-button appearance="plain" size="small" id="mention">
|
||||
<wa-icon name="at" label="Mention"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="mention" name="at" label="Mention"></wa-icon-button>
|
||||
<wa-tooltip for="mention">Mention</wa-tooltip>
|
||||
</div>
|
||||
<div class="grouped-buttons">
|
||||
<wa-button appearance="plain" size="small" id="record-video">
|
||||
<wa-icon name="video" label="Video"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="record-video" name="video" label="Video"></wa-icon-button>
|
||||
<wa-tooltip for="record-video">Record Video</wa-tooltip>
|
||||
<wa-button appearance="plain" size="small" id="record-audio">
|
||||
<wa-icon name="microphone" label="Microphone"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="record-audio" name="microphone" label="Microphone"></wa-icon-button>
|
||||
<wa-tooltip for="record-audio">Record Audio Clip</wa-tooltip>
|
||||
</div>
|
||||
<div class="grouped-buttons">
|
||||
<wa-button appearance="plain" size="small" id="add-magic">
|
||||
<wa-icon name="sparkles" label="Magic"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="add-magic" name="sparkles" label="Magic"></wa-icon-button>
|
||||
<wa-tooltip for="add-magic">Add Magic</wa-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,39 +31,21 @@ During the alpha period, things might break! We take breaking changes very serio
|
||||
- `<wa-tab-group no-scroll-controls>` => `<wa-tab-group without-scroll-controls>`
|
||||
- `<wa-tag removable>` => `<wa-tag with-remove>`
|
||||
- 🚨 BREAKING: removed the `size` attribute from `<wa-card>`; please set the size of child elements on the children directly
|
||||
- 🚨 BREAKING: Greatly simplified the sizing strategy across components and utilities
|
||||
- Removed `--wa-size`, `--wa-size-smaller`, `--wa-size-larger`, `--wa-space`, `--wa-space-smaller`, and `--wa-space-larger`
|
||||
- Added tokens for `--wa-form-control-padding-inline`, `--wa-form-control-padding-block`, and `--wa-form-control-toggle-size`
|
||||
- Refactored default `--wa-font-size-*` values to use an apparent 1.125 ratio and round rendered values to the nearest whole pixel
|
||||
- Added convenience tokens for `--wa-font-size-smaller` and `--wa-font-size-larger`
|
||||
- Updated components to use relative `em` values for internal padding and margin wherever appropriate
|
||||
- 🚨 BREAKING: removed the `hint` property and slot from `<wa-radio>`; please apply hints directly to `<wa-radio-group>` instead
|
||||
- 🚨 BREAKING: removed `<wa-icon-button>`; use `<wa-button><wa-icon name="..." label="..."></wa-icon></wa-button>` instead
|
||||
- Added a new free component: `<wa-popover>` (#2 of 14 per stretch goals)
|
||||
- Added a new free component: `<wa-zoomable-frame>` (#3 of 14 per stretch goals)
|
||||
- Added a `min-block-size` to `<wa-divider orientation="vertical">` to ensure the divider is visible regardless of container height [issue:675]
|
||||
- Added support for `name` in `<wa-details>` for exclusively opening one in a group
|
||||
- Added `--checked-icon-scale` to `<wa-checkbox>`
|
||||
- Added `--tag-max-size` to `<wa-select>` when using `multiple`
|
||||
- Added support for `data-dialog="open <id>"` to `<wa-dialog>`
|
||||
- Added support for `data-drawer="open <id>"` to `<wa-drawer>`
|
||||
- Added `@media (hover: hover)` to component hover styles to prevent sticky hover states
|
||||
- Fixed a bug in `<wa-radio-group>` that caused radios to uncheck when assigning a numeric value [issue:924]
|
||||
- Fixed `<wa-button-group>` so dividers properly show between buttons
|
||||
- Fixed the tooltip position in `<wa-slider>` when using RTL
|
||||
- Fixed a bug in `<wa-details>` and native `<details>` styles that made the summary hard to click [issue:684]
|
||||
- Fixed a handful of bugs unify form control height across components and native elements
|
||||
- Improved CSS utilities and Native Styles to use [CSS layers](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) for easier end user customization (no more specificity conflicts — your CSS wins!)
|
||||
- Improved native `<button>` styles to properly space icons
|
||||
- Improved button appearances in `<wa-color-picker>`
|
||||
- Improved `<wa-rating>` to have more accessible icons by default
|
||||
- Removed the experimental `<wa-code-demo>` component
|
||||
|
||||
## 3.0.0-alpha.13
|
||||
|
||||
- 🚨 BREAKING: Renamed `<image-comparer>` to `<wa-comparison>` and improved compatibility for non-image content
|
||||
- 🚨 BREAKING: Added slot detection to `<wa-dialog>` and `<wa-drawer>` so you don't need to specify `with-header` and `with-footer`; headers are on by default now, but you can use the `without-header` attribute to turn them off
|
||||
- 🚨 BREAKING: Renamed the `image` slot to `media` for a more appropriate naming convention
|
||||
- 🚨 BREAKING: Added slot detection to `<wa-dialog>` and `<wa-drawer>` so you don't need to specify `with-header` and `with-footer`; headers are on by default now, but you can use the `without-header` attribute to turn them off
|
||||
- 🚨 BREAKING: Renamed the `image` slot to `media` for a more appropriate naming convention
|
||||
- Added [a theme builder](/docs/themes/edit/) to create your own themes
|
||||
- Added a new Blog & News pattern category
|
||||
- Added a new free component: `<wa-scroller>` (#1 of 14 per stretch goals)
|
||||
@@ -125,7 +107,7 @@ During the alpha period, things might break! We take breaking changes very serio
|
||||
### Design Tokens
|
||||
|
||||
- Added `--wa-color-[hue]` tokens with the "core" color of each scale, regardless of which tint it lives on.
|
||||
You can find them in the first column of each color palette.
|
||||
You can find them in the first column of each color palette.
|
||||
|
||||
### Themes
|
||||
|
||||
@@ -150,21 +132,20 @@ During the alpha period, things might break! We take breaking changes very serio
|
||||
- Fixed an incorrect CSS value in the expand icon
|
||||
- Fixed a bug that prevented the description from being read by screen readers
|
||||
|
||||
#### `<wa-option>`
|
||||
#### `<wa-option>`
|
||||
|
||||
- `label` attribute to override the generated label (useful for rich content)
|
||||
- `defaultLabel` property
|
||||
- Dropped `getTextLabel()` method (if you need dynamic labels, just set the `label` attribute dynamically)
|
||||
- Dropped `base` part for easier styling. CSS can now be applied directly to the element itself.
|
||||
|
||||
#### `<wa-menu-item>`
|
||||
#### `<wa-menu-item>`
|
||||
|
||||
- `label` attribute to override the generated label (useful for rich content)
|
||||
- `defaultLabel` property
|
||||
- Dropped `getTextLabel()` method (if you need dynamic labels, just set the `label` attribute dynamically)
|
||||
|
||||
#### `<wa-card>`
|
||||
|
||||
- Fixed a bug where child elements did not have correct rounding when headers and footers were absent.
|
||||
- Re-introduced `--border-color` so that the card itself can have a different border color than its inner borders.
|
||||
- Fixed a bug that prevented slots from showing automatically without `with-` attributes
|
||||
@@ -350,12 +331,12 @@ Here's a list of some of the things that have changed since Shoelace v2. For que
|
||||
- Removed `inline` from `<wa-color-picker>`
|
||||
- Removed `getFormControls()` since we now use Form Associated Custom Elements and can reliably access Web Awesome Elements via `formElement.elements`.
|
||||
- Removed `valueAsDate` from `<wa-input>`; use the following to mimic native behaviors:
|
||||
setter: `waInput.value = new Date().toLocaleDateString()`
|
||||
getter: `new Date(waInput.value)`
|
||||
setter: `waInput.value = new Date().toLocaleDateString()`
|
||||
getter: `new Date(waInput.value)`
|
||||
- Removed `valueAsNumber` from `<wa-input>`; use the following to mimic native behaviors:
|
||||
setter: `waInput.value = 5.toString()`
|
||||
getter: `Number(waInput.value)`
|
||||
setter: `waInput.value = 5.toString()`
|
||||
getter: `Number(waInput.value)`
|
||||
|
||||
Did we miss something? [Let us know!](https://github.com/shoelace-style/webawesome-alpha/discussions)
|
||||
|
||||
Are you coming from Shoelace? [The 2.x changelog can be found here.](https://shoelace.style/resources/changelog/)
|
||||
Are you coming from Shoelace? [The 2.x changelog can be found here.](https://shoelace.style/resources/changelog/)
|
||||
@@ -23,13 +23,15 @@ unlisted: true
|
||||
|
||||
<div class="title">
|
||||
<h1><editable-text :model-value="title" label="theme name" @submit="newTitle => save({title: newTitle})" blur="cancel"></editable-text></h1>
|
||||
<wa-button v-if="saved" class="delete" @click="deleteSaved" appearance="plain" size="small">
|
||||
<wa-icon name="trash" label="Delete theme"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button v-if="saved" class="delete" name="trash" label="Delete theme" @click="deleteSaved"></wa-icon-button>
|
||||
</div>
|
||||
|
||||
<wa-button v-if="tweaked || uid" @click="save()" :disabled="!unsavedChanges"
|
||||
:variant="unsavedChanges ? 'success' : 'neutral'" size="small" :appearance="unsavedChanges ? 'accent' : 'outlined'">
|
||||
<span slot="prefix" class="icon-modifier">
|
||||
<wa-icon name="sidebar" variant="regular"></wa-icon>
|
||||
<wa-icon name="circle-plus" class="modifier" style="color: light-dark(var(--wa-color-green-70), var(--wa-color-green-60));"></wa-icon>
|
||||
</span>
|
||||
<span v-content="unsavedChanges ? 'Save' : 'Saved'">Save</span>
|
||||
</wa-button>
|
||||
<wa-button size="small" @click="ui.showCode = !showCode" appearance="outlined">
|
||||
|
||||
@@ -45,11 +45,11 @@ wa-page > header {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
wa-button {
|
||||
wa-icon-button {
|
||||
transition: var(--wa-transition-slow);
|
||||
}
|
||||
|
||||
&:not(:hover, :focus-within, :has(input)) wa-button {
|
||||
&:not(:hover, :focus-within, :has(input)) wa-icon-button {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ wa-input::part(input) {
|
||||
grid-column-end: col-end;
|
||||
}
|
||||
|
||||
.project-header wa-button {
|
||||
.project-header wa-icon-button {
|
||||
color: inherit;
|
||||
font-size: var(--wa-font-size-l);
|
||||
|
||||
|
||||
@@ -21,15 +21,9 @@ noTheme: true
|
||||
<span id="project-name" style="margin-inline-start: var(--wa-space-l);">Project Name</span>
|
||||
</h1>
|
||||
<div>
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="magnifying-glass" label="Search"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="user" label="Account"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="bag-shopping" label="Your Basket"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="magnifying-glass" label="Search"></wa-icon-button>
|
||||
<wa-icon-button name="user" label="Account"></wa-icon-button>
|
||||
<wa-icon-button name="bag-shopping" label="Your Basket"></wa-icon-button>
|
||||
</div>
|
||||
</header>
|
||||
<section class="strata hero">
|
||||
|
||||
@@ -8,38 +8,31 @@ For components that share similar qualities, Web Awesome includes custom propert
|
||||
|
||||
## Form Controls
|
||||
|
||||
Components such as [input](/docs/components/input), [select](/docs/components/select), [textarea](/docs/components/textarea), [checkbox](/docs/components/checkbox), and others share a number of styles to give your forms a cohesive appearance. Web Awesome defines custom properties for these styles using the format `--wa-form-control-{style}`.
|
||||
Components such as [input](/docs/components/input), [select](/docs/components/select), [textarea](/docs/components/textarea), [checkbox](/docs/components/checkbox), etc. share a number of styles to give your forms a cohesive appearance. Web Awesome defines custom properties for these styles using the format `--wa-form-control-{style}`.
|
||||
|
||||
Not every form control uses all of these custom properties. For example, `<wa-radio>` defines its own height and border radius to achieve its familiar shape but shares many other styles with other components for a cohesive look and feel. Similarly, `<wa-button>` defines many of its own styles but matches the height and border width of other form controls.
|
||||
|
||||
| Custom Property | Default Value |
|
||||
| ------------------------------------------- | ------------------------------------- |
|
||||
| `--wa-form-control-background-color` | `var(--wa-color-surface-default)` |
|
||||
| `--wa-form-control-border-color` | `var(--wa-color-neutral-border-loud)` |
|
||||
| `--wa-form-control-border-style` | `var(--wa-border-style)` |
|
||||
| `--wa-form-control-border-width` | `var(--wa-border-width-s)` |
|
||||
| `--wa-form-control-border-radius` | `var(--wa-border-radius-m)` |
|
||||
| `--wa-form-control-activated-color` | `var(--wa-color-brand-fill-loud)` |
|
||||
| `--wa-form-control-label-color` | `var(--wa-color-neutral-border-loud)` |
|
||||
| `--wa-form-control-label-font-weight` | `var(--wa-font-weight-normal)` |
|
||||
| `--wa-form-control-label-line-height` | `var(--wa-line-height-normal)` |
|
||||
| `--wa-form-control-value-color` | `var(--wa-color-text-normal)` |
|
||||
| `--wa-form-control-value-font-weight` | `var(--wa-font-weight-body)` |
|
||||
| `--wa-form-control-value-line-height` | `var(--wa-line-height-condensed)` |
|
||||
| `--wa-form-control-hint-color` | `var(--wa-color-text-quiet)` |
|
||||
| `--wa-form-control-hint-font-weight` | `var(--wa-font-weight-body)` |
|
||||
| `--wa-form-control-hint-line-height` | `var(--wa-line-height-normal)` |
|
||||
| `--wa-form-control-placeholder-color` | `var(--wa-color-gray-60)` |
|
||||
| `--wa-form-control-required-content` | `'*'` |
|
||||
| `--wa-form-control-required-content-color` | `inherit` |
|
||||
| `--wa-form-control-required-content-offset` | `-0.1em` |
|
||||
| `--wa-form-control-padding-block` | `0.75em` |
|
||||
| `--wa-form-control-padding-inline` | `1em` |
|
||||
| `--wa-form-control-height` | `round(calc(2 * var(--wa-form-control-padding-block) + 1em * var(--wa-form-control-value-line-height)), 1px)` |
|
||||
| `--wa-form-control-toggle-size` | `round(1.25em, 1px)` |
|
||||
| Custom Property | Default Value |
|
||||
| ------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| `--wa-form-control-background-color` | `var(--wa-color-surface-default)` |
|
||||
| `--wa-form-control-border-color` | `var(--wa-color-neutral-border-loud)` |
|
||||
| `--wa-form-control-border-style` | `var(--wa-border-style)` |
|
||||
| `--wa-form-control-border-width` | `var(--wa-border-width-s)` |
|
||||
| `--wa-form-control-border-radius` | `var(--wa-border-radius-m)` |
|
||||
| `--wa-form-control-activated-color` | `var(--wa-color-brand-fill-loud)` |
|
||||
| `--wa-form-control-label-color` | `var(--wa-color-neutral-border-loud)` |
|
||||
| `--wa-form-control-label-font-weight` | `var(--wa-font-weight-normal)` |
|
||||
| `--wa-form-control-label-line-height` | `var(--wa-line-height-normal)` |
|
||||
| `--wa-form-control-value-color` | `var(--wa-color-text-normal)` |
|
||||
| `--wa-form-control-value-font-weight` | `var(--wa-font-weight-body)` |
|
||||
| `--wa-form-control-value-line-height` | `var(--wa-line-height-condensed)` |
|
||||
| `--wa-form-control-placeholder-color` | `var(--wa-color-gray-60)` |
|
||||
| `--wa-form-control-required-content` | `'*'` |
|
||||
| `--wa-form-control-required-content-color` | `inherit` |
|
||||
| `--wa-form-control-required-content-offset` | `-0.1em` |
|
||||
|
||||
```html {.example}
|
||||
<form class="wa-stack">
|
||||
<form class="wa-block-spacing-l">
|
||||
<wa-input label="Input" placeholder="Placeholder"></wa-input>
|
||||
<wa-select label="Select" value="option-1">
|
||||
<wa-option value="option-1">Option 1</wa-option>
|
||||
@@ -57,6 +50,19 @@ Not every form control uses all of these custom properties. For example, `<wa-ra
|
||||
<wa-slider label="Range"></wa-slider>
|
||||
<wa-button>Button</wa-button>
|
||||
</form>
|
||||
|
||||
<style>
|
||||
.wa-block-spacing-l > * + *, wa-radio {
|
||||
display: block;
|
||||
margin-block-start: var(--wa-space-l);
|
||||
}
|
||||
wa-radio {
|
||||
margin-block-start: var(--wa-space-2xs);
|
||||
}
|
||||
wa-radio, wa-checkbox, wa-switch, wa-button {
|
||||
width: fit-content;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## Panels
|
||||
@@ -70,7 +76,7 @@ Panels consist of components with larger, contained surface areas like [callout]
|
||||
| `--wa-panel-border-radius` | `var(--wa-border-radius-l)` |
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-stack">
|
||||
<div class="wa-block-spacing-l">
|
||||
<wa-callout>
|
||||
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
|
||||
This is a simple callout with an icon.
|
||||
@@ -80,6 +86,13 @@ Panels consist of components with larger, contained surface areas like [callout]
|
||||
<code>wa-details</code>, at your service.
|
||||
</wa-details>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wa-block-spacing-l > * + * {
|
||||
display: block;
|
||||
margin-block-start: var(--wa-space-l);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## Tooltips
|
||||
@@ -96,8 +109,6 @@ Tooltip styles are shared between the [tooltip](/docs/components/tooltip) compon
|
||||
| `--wa-tooltip-line-height` | `var(--wa-line-height-normal)` |
|
||||
|
||||
```html {.example}
|
||||
<wa-button id="bullseye-example" appearance="plain">
|
||||
<wa-icon label="Target" name="bullseye"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="bullseye-example" label="Button" name="bullseye"></wa-icon-button>
|
||||
<wa-tooltip for="bullseye-example" open trigger="manual">This is a tooltip</wa-tooltip>
|
||||
```
|
||||
|
||||
@@ -35,19 +35,21 @@ description: Lock down consistent spacing Web Awesome's space properties.
|
||||
|
||||
Space properties are used intentionally throughout Web Awesome to create predictable rhythm and meaningful proximity. These properties use `rem` units in order to scale proportionately with the root font size.
|
||||
|
||||
You can use `--wa-space-scale` to increase or decrease all spacing at once. By default, this multiplier is `1`.
|
||||
Each space property uses a `calc()` function with `--wa-space-scale` to scale all spacing at once. By default, this multiplier is `1`. The table below lists the result of the calculation.
|
||||
|
||||
The calculations for each size and the resulting pixel value (assuming a 16px root font size) are listed below.
|
||||
| Custom Property | Default Value | Preview |
|
||||
| ---------------- | ------------------------------- | --------------------------------------------------------------------- |
|
||||
| `--wa-space-3xs` | `0.125rem` <small>(2px)</small> | <div class="spacing-example" style="width: var(--wa-space-3xs)"></div> |
|
||||
| `--wa-space-2xs` | `0.25rem` <small>(4px)</small> | <div class="spacing-example" style="width: var(--wa-space-2xs)"></div> |
|
||||
| `--wa-space-xs` | `0.5rem` <small>(8px)</small> | <div class="spacing-example" style="width: var(--wa-space-xs)"></div> |
|
||||
| `--wa-space-s` | `0.75rem` <small>(12px)</small> | <div class="spacing-example" style="width: var(--wa-space-s)"></div> |
|
||||
| `--wa-space-m` | `1rem` <small>(16px)</small> | <div class="spacing-example" style="width: var(--wa-space-m)"></div> |
|
||||
| `--wa-space-l` | `1.25rem` <small>(20px)</small> | <div class="spacing-example" style="width: var(--wa-space-l)"></div> |
|
||||
| `--wa-space-xl` | `1.5rem` <small>(24px)</small> | <div class="spacing-example" style="width: var(--wa-space-xl)"></div> |
|
||||
| `--wa-space-2xl` | `2rem` <small>(32px)</small> | <div class="spacing-example" style="width: var(--wa-space-2xl)"></div> |
|
||||
| `--wa-space-3xl` | `3rem` <small>(48px)</small> | <div class="spacing-example" style="width: var(--wa-space-3xl)"></div> |
|
||||
|
||||
| Custom Property | Default Value | Preview - |
|
||||
| ---------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------- |
|
||||
| `--wa-space-3xs` | `calc(var(--wa-space-scale) * 0.125rem)` <small>(2px)</small> | <div class="spacing-example" style="width: var(--wa-space-3xs)"></div> |
|
||||
| `--wa-space-2xs` | `calc(var(--wa-space-scale) * 0.25rem)` <small>(4px)</small> | <div class="spacing-example" style="width: var(--wa-space-2xs)"></div> |
|
||||
| `--wa-space-xs` | `calc(var(--wa-space-scale) * 0.5rem)` <small>(8px)</small> | <div class="spacing-example" style="width: var(--wa-space-xs)"></div> |
|
||||
| `--wa-space-s` | `calc(var(--wa-space-scale) * 0.75rem)` <small>(12px)</small> | <div class="spacing-example" style="width: var(--wa-space-s)"></div> |
|
||||
| `--wa-space-m` | `calc(var(--wa-space-scale) * 1rem)` <small>(16px)</small> | <div class="spacing-example" style="width: var(--wa-space-m)"></div> |
|
||||
| `--wa-space-l` | `calc(var(--wa-space-scale) * 1.5rem)` <small>(24px)</small> | <div class="spacing-example" style="width: var(--wa-space-l)"></div> |
|
||||
| `--wa-space-xl` | `calc(var(--wa-space-scale) * 2rem)` <small>(32px)</small> | <div class="spacing-example" style="width: var(--wa-space-xl)"></div> |
|
||||
| `--wa-space-2xl` | `calc(var(--wa-space-scale) * 2.5rem)` <small>(40px)</small> | <div class="spacing-example" style="width: var(--wa-space-2xl)"></div> |
|
||||
| `--wa-space-3xl` | `calc(var(--wa-space-scale) * 3rem)` <small>(48px)</small> | <div class="spacing-example" style="width: var(--wa-space-3xl)"></div> |
|
||||
| `--wa-space-4xl` | `calc(var(--wa-space-scale) * 4rem)` <small>(64px)</small> | <div class="spacing-example" style="width: var(--wa-space-4xl)"></div> |
|
||||
When using space properties, it may be helpful to consider three distinct groups:
|
||||
- Small-scale space (`3xs`, `2xs`, and `xs`) can be used for gaps between cooperating elements, such as a dropdown button and its menu, and padding within small components, such as badges and tooltips
|
||||
- Normal space (`s`, `m`, and `l`) can be used for gaps between related elements with distinct touch targets and padding within typical interface elements, such as buttons and inputs
|
||||
- Large-scale space (`xl`, `2xl`, and `3xl`) can be used for gaps between unrelated elements and padding within larger components, such as cards and dialogs
|
||||
|
||||
@@ -17,32 +17,21 @@ Font families are assigned specific roles — like heading or code — t
|
||||
|
||||
## Font Size
|
||||
|
||||
Font sizes use a ratio of 1.125 to scale sizes proportionally. Starting with the medium (`m`) font size, smaller sizes (`s` through `2xs`) are 1.125x smaller as the sizes decrease, and larger sizes (`l` through `4xl`) are _twice_ 1.125x larger as sizes increase — here, the ratio is doubled to maximize impact between sizes.
|
||||
Font sizes use the Major Second type scale, rounded to the nearest whole pixel assuming a 16px root font size. To maximize variation in larger font sizes, every other step on the scale is skipped.
|
||||
|
||||
Each value uses `rem` units and is rounded to the nearest whole pixel when rendered with [`round()`](https://developer.mozilla.org/en-US/docs/Web/CSS/round).
|
||||
|
||||
You can use `--wa-font-size-scale` to increase or decrease all font sizes at once. By default, this multiplier is `1`.
|
||||
|
||||
The calculations for each size and the resulting pixel value (assuming a 16px root font size) are listed below.
|
||||
Each font size uses a `calc()` function with `--wa-font-size-scale` to scale all font sizes at once. By default, this multiplier is `1`. The table below lists the result of the calculation.
|
||||
|
||||
| Custom Property | Default Value | Preview |
|
||||
| -------------------- | --------------------------------- | ---------------------------------------------------------- |
|
||||
| `--wa-font-size-2xs` | `round(calc(var(--wa-font-size-xs) / 1.125), 1px)` <small>(11px)</small> | <div style="font-size: var(--wa-font-size-2xs)">AaBb</div> |
|
||||
| `--wa-font-size-xs` | `round(calc(var(--wa-font-size-s) / 1.125), 1px)` <small>(12px)</small> | <div style="font-size: var(--wa-font-size-xs)">AaBb</div> |
|
||||
| `--wa-font-size-s` | `round(calc(var(--wa-font-size-m) / 1.125), 1px)` <small>(14px)</small> | <div style="font-size: var(--wa-font-size-s)">AaBb</div> |
|
||||
| `--wa-font-size-m` | `calc(1rem * var(--wa-font-size-scale))` <small>(16px)</small> | <div style="font-size: var(--wa-font-size-m)">AaBb</div> |
|
||||
| `--wa-font-size-l` | `round(calc(var(--wa-font-size-m) * 1.125 * 1.125), 1px)` <small>(20px)</small> | <div style="font-size: var(--wa-font-size-l)">AaBb</div> |
|
||||
| `--wa-font-size-xl` | `round(calc(var(--wa-font-size-l) * 1.125 * 1.125), 1px)` <small>(25px)</small> | <div style="font-size: var(--wa-font-size-xl)">AaBb</div> |
|
||||
| `--wa-font-size-2xl` | `round(calc(var(--wa-font-size-xl) * 1.125 * 1.125), 1px)` <small>(32px)</small> | <div style="font-size: var(--wa-font-size-2xl)">AaBb</div> |
|
||||
| `--wa-font-size-3xl` | `round(calc(var(--wa-font-size-2xl) * 1.125 * 1.125), 1px)` <small>(41px)</small> | <div style="font-size: var(--wa-font-size-3xl)">AaBb</div> |
|
||||
| `--wa-font-size-4xl` | `round(calc(var(--wa-font-size-3xl) * 1.125 * 1.125)` <small>(52px)</small> | <div style="font-size: var(--wa-font-size-4xl)">AaBb</div> |
|
||||
|
||||
You can also use these two custom properties make any font size proportionally smaller or larger to its parent.
|
||||
|
||||
| Custom Property | Default Value |
|
||||
| ------------------------ | --------------------------------------- |
|
||||
| `--wa-font-size-smaller` | `round(calc(1em / 1.125), 1px)` |
|
||||
| `--wa-font-size-larger` | `round(calc(1em * 1.125 * 1.125), 1px)` |
|
||||
| `--wa-font-size-2xs` | `0.6875rem` <small>(11px)</small> | <div style="font-size: var(--wa-font-size-2xs)">AaBb</div> |
|
||||
| `--wa-font-size-xs` | `0.75rem` <small>(12px)</small> | <div style="font-size: var(--wa-font-size-xs)">AaBb</div> |
|
||||
| `--wa-font-size-s` | `0.875rem` <small>(14px)</small> | <div style="font-size: var(--wa-font-size-s)">AaBb</div> |
|
||||
| `--wa-font-size-m` | `1rem` <small>(16px)</small> | <div style="font-size: var(--wa-font-size-m)">AaBb</div> |
|
||||
| `--wa-font-size-l` | `1.25rem` <small>(20px)</small> | <div style="font-size: var(--wa-font-size-l)">AaBb</div> |
|
||||
| `--wa-font-size-xl` | `1.625rem` <small>(26px)</small> | <div style="font-size: var(--wa-font-size-xl)">AaBb</div> |
|
||||
| `--wa-font-size-2xl` | `2rem` <small>(32px)</small> | <div style="font-size: var(--wa-font-size-2xl)">AaBb</div> |
|
||||
| `--wa-font-size-3xl` | `2.5625rem` <small>(41px)</small> | <div style="font-size: var(--wa-font-size-3xl)">AaBb</div> |
|
||||
| `--wa-font-size-4xl` | `3.25rem` <small>(52px)</small> | <div style="font-size: var(--wa-font-size-4xl)">AaBb</div> |
|
||||
|
||||
## Font Weight
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ file: styles/utilities/variants.css
|
||||
|
||||
Some Web Awesome components, like `<wa-button>`, allow you to change the color by using a `variant` attribute:
|
||||
|
||||
- [`<wa-badge>`](/docs/components/badge)
|
||||
- [`<wa-button>`](/docs/components/button)
|
||||
- [`<wa-button-group>`](/docs/components/button-group)
|
||||
- [`<wa-callout>`](/docs/components/callout)
|
||||
- [`<wa-tag>`](/docs/components/tag)
|
||||
{% for component in componentsBy.attribute.variant %}
|
||||
{% if component.fileSlug != "icon" or component.fileSlug != "icon-button" -%}
|
||||
- <a href="../{{ component.url }}"><code><{{ component.tagName }}></code></a>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
|
||||
You can create the same effect on any element by using the color variant utility classes:
|
||||
|
||||
|
||||
@@ -40,9 +40,7 @@ Frames are well-suited for images and image placeholders.
|
||||
<h3>The Lord of the Rings: The Fellowship of the Ring</h3>
|
||||
<span>J.R.R. Tolkien</span>
|
||||
</div>
|
||||
<wa-button id="options-menu" appearance="plain">
|
||||
<wa-icon name="ellipsis" label="Options"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="options-menu" name="ellipsis"></wa-icon-button>
|
||||
<wa-tooltip for="options-menu">Options</wa-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,9 +57,7 @@ Frames are well-suited for images and image placeholders.
|
||||
<span class="wa-body-s">Kitten • Male</span>
|
||||
<div class="wa-flank:end wa-gap-xs">
|
||||
<wa-button size="small" appearance="filled" variant="brand">Adopt this pet</wa-button>
|
||||
<wa-button id="fav-whitesocks" appearance="plain" size="small">
|
||||
<wa-icon name="heart" variant="regular" label="Favorite"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="fav-whitesocks" name="heart" variant="regular"></wa-icon-button>
|
||||
<wa-tooltip for="fav-whitesocks">Favorite</wa-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -78,9 +74,7 @@ Frames are well-suited for images and image placeholders.
|
||||
<span class="wa-body-s">Adult • Male</span>
|
||||
<div class="wa-flank:end wa-gap-xs">
|
||||
<wa-button size="small" appearance="filled" variant="brand">Adopt this pet</wa-button>
|
||||
<wa-button id="fav-bumpkin" appearance="plain" size="small">
|
||||
<wa-icon name="heart" variant="regular" label="Favorite"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="fav-bumpkin" name="heart" variant="regular"></wa-icon-button>
|
||||
<wa-tooltip for="fav-bumpkin">Favorite</wa-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -94,9 +88,7 @@ Frames are well-suited for images and image placeholders.
|
||||
<span class="wa-body-s">Kitten • Female</span>
|
||||
<div class="wa-flank:end wa-gap-xs">
|
||||
<wa-button size="small" appearance="filled" variant="brand">Adopt this pet</wa-button>
|
||||
<wa-button id="fav-swishtail" appearance="plain" size="small">
|
||||
<wa-icon name="heart" variant="regular" label="Favorite"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="fav-swishtail" name="heart" variant="regular"></wa-icon-button>
|
||||
<wa-tooltip for="fav-swishtail">Favorite</wa-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -110,9 +102,7 @@ Frames are well-suited for images and image placeholders.
|
||||
<span class="wa-body-s">Adult • Female</span>
|
||||
<div class="wa-flank:end wa-gap-xs">
|
||||
<wa-button size="small" appearance="filled" variant="brand">Adopt this pet</wa-button>
|
||||
<wa-button id="fav-sharpears" appearance="plain" size="small">
|
||||
<wa-icon name="heart" variant="regular" label="Favorite"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button id="fav-sharpears" name="heart" variant="regular"></wa-icon-button>
|
||||
<wa-tooltip for="fav-sharpears">Favorite</wa-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,21 +36,13 @@ Splits are especially helpful for navigation, header, and footer layouts.
|
||||
<div class="wa-flank">
|
||||
<div class="wa-split:column">
|
||||
<div class="wa-stack">
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="house" label="Home"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="calendar" label="Calendar"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="envelope" label="Mail"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="house" label="Home"></wa-icon-button>
|
||||
<wa-icon-button name="calendar" label="Calendar"></wa-icon-button>
|
||||
<wa-icon-button name="envelope" label="Mail"></wa-icon-button>
|
||||
</div>
|
||||
<div class="wa-stack">
|
||||
<wa-divider></wa-divider>
|
||||
<wa-button appearance="plain">
|
||||
<wa-icon name="right-from-bracket" label="Sign Out"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-icon-button name="right-from-bracket" label="Sign Out"></wa-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="placeholder">
|
||||
|
||||
@@ -216,6 +216,7 @@ layout: page
|
||||
border-color: var(--wa-color-surface-border);
|
||||
border-radius: 0.75rem;
|
||||
color: var(--wa-color-text-normal);
|
||||
display: block;
|
||||
height: 100%;
|
||||
line-height: var(--wa-line-height-normal);
|
||||
padding: 1.25rem;
|
||||
@@ -239,7 +240,7 @@ layout: page
|
||||
font-weight: var(--wa-font-weight-normal);
|
||||
}
|
||||
&::part(label) {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
wa-callout {
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"test:component": "CSR_ONLY=\"true\" web-test-runner -- --watch --group",
|
||||
"test:contrast": "cd src/styles/color && node contrast.test.js",
|
||||
"test:watch": "web-test-runner --watch --group default",
|
||||
"prettier": "prettier --check --log-level=warn --ignore-path=\"../../.prettierignore\" .",
|
||||
"prettier": "prettier --check --log-level=warn --ignore-path=\"../../.prettierignore\".",
|
||||
"prettier:fix": "prettier --write --log-level=warn --ignore-path=\"../../.prettierignore\" .",
|
||||
"spellcheck": "cspell \"**/*.{js,ts,json,html,css,md}\" --no-progress --config=\"../../cspell.json\"",
|
||||
"verify": "npm run prettier && npm run build && npm run test",
|
||||
|
||||
@@ -8,12 +8,19 @@ import { replace } from 'esbuild-plugin-replace';
|
||||
import { mkdir, readFile } from 'fs/promises';
|
||||
import getPort, { portNumbers } from 'get-port';
|
||||
import { globby } from 'globby';
|
||||
import ora from 'ora';
|
||||
import { dirname, join, relative } from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import ora from 'ora';
|
||||
import copy from 'recursive-copy';
|
||||
import { getCdnDir, getDistDir, getDocsDir, getRootDir, getSiteDir, runScript } from './utils.js';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import {
|
||||
getCdnDir,
|
||||
getDistDir,
|
||||
getDocsDir,
|
||||
getRootDir,
|
||||
getSiteDir,
|
||||
runScript,
|
||||
} from './utils.js';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const isDeveloping = process.argv.includes('--develop');
|
||||
@@ -35,18 +42,19 @@ let buildContexts = {
|
||||
/**
|
||||
* @param {BuildOptions} [options={}]
|
||||
*/
|
||||
export async function build(options = {}) {
|
||||
export async function build (options = {}) {
|
||||
if (!options.watchedSrcDirectories) {
|
||||
options.watchedSrcDirectories = ['src'];
|
||||
options.watchedSrcDirectories = ['src']
|
||||
}
|
||||
|
||||
if (!options.watchedDocsDirectories) {
|
||||
options.watchedDocsDirectories = [getDocsDir()];
|
||||
options.watchedDocsDirectories = [getDocsDir()]
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs the full build.
|
||||
*/
|
||||
* Runs the full build.
|
||||
*/
|
||||
async function buildAll() {
|
||||
const start = Date.now();
|
||||
|
||||
@@ -84,8 +92,8 @@ export async function build(options = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes components and generates the custom elements manifest file.
|
||||
*/
|
||||
* Analyzes components and generates the custom elements manifest file.
|
||||
*/
|
||||
function generateManifest() {
|
||||
spinner.start('Generating CEM');
|
||||
|
||||
@@ -105,14 +113,14 @@ export async function build(options = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates React wrappers for all components.
|
||||
*/
|
||||
* Generates React wrappers for all components.
|
||||
*/
|
||||
function generateReactWrappers() {
|
||||
spinner.start('Generating React wrappers');
|
||||
|
||||
try {
|
||||
// need to run make-react from this directories.
|
||||
execSync(`node ${join(__dirname, 'make-react.js')} --outdir "${getCdnDir()}"`, { stdio: 'inherit' });
|
||||
execSync(`node ${join(__dirname, "make-react.js")} --outdir "${getCdnDir()}"`, { stdio: 'inherit' });
|
||||
} catch (error) {
|
||||
console.error(`\n\n${error.message}`);
|
||||
|
||||
@@ -126,8 +134,8 @@ export async function build(options = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies theme stylesheets to the dist.
|
||||
*/
|
||||
* Copies theme stylesheets to the dist.
|
||||
*/
|
||||
async function generateStyles() {
|
||||
spinner.start('Copying stylesheets');
|
||||
|
||||
@@ -139,20 +147,20 @@ export async function build(options = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs TypeScript to generate types.
|
||||
*/
|
||||
* Runs TypeScript to generate types.
|
||||
*/
|
||||
async function generateTypes() {
|
||||
spinner.start('Running the TypeScript compiler');
|
||||
|
||||
const cwd = process.cwd();
|
||||
const cwd = process.cwd()
|
||||
try {
|
||||
if (process.env.ROOT_DIR) {
|
||||
process.chdir(process.env.ROOT_DIR);
|
||||
process.chdir(process.env.ROOT_DIR)
|
||||
}
|
||||
execSync(`tsc --project ./tsconfig.prod.json --outdir "${getCdnDir()}"`);
|
||||
process.chdir(cwd);
|
||||
process.chdir(cwd)
|
||||
} catch (error) {
|
||||
process.chdir(cwd);
|
||||
process.chdir(cwd)
|
||||
if (!isDeveloping) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -165,12 +173,12 @@ export async function build(options = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs esbuild to generate the final dist.
|
||||
*/
|
||||
* Runs esbuild to generate the final dist.
|
||||
*/
|
||||
async function generateBundle() {
|
||||
spinner.start('Bundling with esbuild');
|
||||
|
||||
const rootDir = process.env.ROOT_DIR || '.';
|
||||
const rootDir = process.env.ROOT_DIR || "."
|
||||
// Bundled config
|
||||
const config = {
|
||||
format: 'esm',
|
||||
@@ -238,8 +246,8 @@ export async function build(options = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Incrementally rebuilds the source files. Must be called only after `generateBundle()` has been called.
|
||||
*/
|
||||
* Incrementally rebuilds the source files. Must be called only after `generateBundle()` has been called.
|
||||
*/
|
||||
async function regenerateBundle() {
|
||||
try {
|
||||
spinner.start('Re-bundling with esbuild');
|
||||
@@ -258,12 +266,12 @@ export async function build(options = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the documentation site.
|
||||
*/
|
||||
* Generates the documentation site.
|
||||
*/
|
||||
async function generateDocs() {
|
||||
/**
|
||||
* Used by the webawesome-app to skip doc generation since it will do its own.
|
||||
*/
|
||||
* Used by the webawesome-app to skip doc generation since it will do its own.
|
||||
*/
|
||||
if (process.env.SKIP_ELEVENTY === 'true') {
|
||||
return;
|
||||
}
|
||||
@@ -367,19 +375,19 @@ export async function build(options = {}) {
|
||||
|
||||
// TODO: Should probably listen for all of these instead of just "change"
|
||||
const watchEvents = [
|
||||
'change',
|
||||
"change",
|
||||
// "unlink",
|
||||
// "add"
|
||||
];
|
||||
]
|
||||
// Rebuild and reload when source files change
|
||||
options.watchedSrcDirectories.forEach(dir => {
|
||||
const watcher = bs.watch(join(dir, '**', '!(*.test).*'));
|
||||
options.watchedSrcDirectories.forEach((dir) => {
|
||||
const watcher = bs.watch(join(dir, "**", "!(*.test).*"))
|
||||
|
||||
watchEvents.forEach(evt => {
|
||||
watcher.on(evt, handleWatchEvent(evt));
|
||||
});
|
||||
function handleWatchEvent(evt) {
|
||||
return async filename => {
|
||||
watchEvents.forEach((evt) => {
|
||||
watcher.on(evt, handleWatchEvent(evt))
|
||||
})
|
||||
function handleWatchEvent (evt) {
|
||||
return async (filename) => {
|
||||
spinner.info(`File modified ${chalk.gray(`(${relative(getRootDir(), filename)})`)}`);
|
||||
|
||||
try {
|
||||
@@ -393,8 +401,8 @@ export async function build(options = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof options.onWatchEvent === 'function') {
|
||||
await options.onWatchEvent(evt, filename);
|
||||
if (typeof options.onWatchEvent === "function") {
|
||||
await options.onWatchEvent(evt, filename)
|
||||
}
|
||||
await regenerateBundle();
|
||||
|
||||
@@ -419,29 +427,29 @@ export async function build(options = {}) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Rebuild the docs and reload when the docs change
|
||||
options.watchedDocsDirectories.forEach(dir => {
|
||||
const watcher = bs.watch(join(dir, '**', '*.*'));
|
||||
options.watchedDocsDirectories.forEach((dir) => {
|
||||
const watcher = bs.watch(join(dir, "**", "*.*"))
|
||||
|
||||
watchEvents.forEach(evt => {
|
||||
watcher.on(evt, handleWatchEvent(evt));
|
||||
});
|
||||
watchEvents.forEach((evt) => {
|
||||
watcher.on(evt, handleWatchEvent(evt))
|
||||
})
|
||||
|
||||
function handleWatchEvent(evt) {
|
||||
return async filename => {
|
||||
function handleWatchEvent (evt) {
|
||||
return async (filename) => {
|
||||
spinner.info(`File modified ${chalk.gray(`(${relative(getRootDir(), filename)})`)}`);
|
||||
if (typeof options.onWatchEvent === 'function') {
|
||||
await options.onWatchEvent(evt, filename);
|
||||
if (typeof options.onWatchEvent === "function") {
|
||||
await options.onWatchEvent(evt, filename)
|
||||
}
|
||||
await generateDocs();
|
||||
reload();
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
@@ -460,23 +468,22 @@ export async function build(options = {}) {
|
||||
|
||||
process.on('SIGINT', terminate);
|
||||
process.on('SIGTERM', terminate);
|
||||
}
|
||||
}
|
||||
|
||||
// https://exploringjs.com/nodejs-shell-scripting/ch_nodejs-path.html#detecting-if-module-is-main
|
||||
// Detects if this was called via node scripts/build.js
|
||||
function isRunAsMain() {
|
||||
if (import.meta.url.startsWith('file:')) {
|
||||
// (A)
|
||||
function isRunAsMain () {
|
||||
if (import.meta.url.startsWith('file:')) { // (A)
|
||||
const modulePath = fileURLToPath(import.meta.url);
|
||||
if (process.argv[1] === modulePath) {
|
||||
// (B)
|
||||
return true;
|
||||
if (process.argv[1] === modulePath) { // (B)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
if (isRunAsMain()) {
|
||||
await build();
|
||||
await build()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import Eleventy from '@11ty/eleventy';
|
||||
import { deleteAsync } from 'del';
|
||||
import { join } from 'path';
|
||||
import { getDocsDir, getEleventyConfigPath, getSiteDir } from './utils.js';
|
||||
import { getDocsDir, getSiteDir } from './utils.js';
|
||||
|
||||
const elev = new Eleventy(getDocsDir(), getSiteDir(), {
|
||||
quietMode: true,
|
||||
configPath: getEleventyConfigPath(),
|
||||
configPath: join(getDocsDir(), '.eleventy.js'),
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
@@ -13,3 +13,4 @@ await deleteAsync(getSiteDir());
|
||||
|
||||
// Write it
|
||||
await elev.write();
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import { getAllComponents } from './shared.js';
|
||||
|
||||
const { outdir } = commandLineArgs({ name: 'outdir', type: String });
|
||||
|
||||
const reactDir = path.join(process.env.ROOT_DIR || '.', 'src', 'react');
|
||||
const srcDir = process.env.ROOT_DIR ? path.join(process.env.ROOT_DIR, 'src') : '.';
|
||||
const reactDir = path.join(process.env.ROOT_DIR || ".", 'src', 'react');
|
||||
const srcDir = process.env.ROOT_DIR ? path.join(process.env.ROOT_DIR, "src") : "."
|
||||
|
||||
// Clear build directory
|
||||
deleteSync(reactDir);
|
||||
@@ -25,7 +25,7 @@ for await (const component of components) {
|
||||
const tagWithoutPrefix = component.tagName.replace(/^wa-/, '');
|
||||
const componentDir = path.join(reactDir, tagWithoutPrefix);
|
||||
const componentFile = path.join(componentDir, 'index.ts');
|
||||
const importPath = path.relative(srcDir, component.path);
|
||||
const importPath = path.relative(srcDir, component.path)
|
||||
|
||||
// We only want to wrap wa- prefixed events, because the others are native
|
||||
const eventsToWrap = component.events?.filter(event => event.name.startsWith('wa-')) || [];
|
||||
@@ -81,3 +81,4 @@ for await (const component of components) {
|
||||
|
||||
// Generate the index file
|
||||
fs.writeFileSync(path.join(reactDir, 'index.ts'), index.join('\n'), 'utf8');
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import styles from './{{ tagWithoutPrefix tag }}.css';
|
||||
*/
|
||||
@customElement("{{ tag }}")
|
||||
export default class {{ properCase tag }} extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
/** An example attribute. */
|
||||
@property() attr = 'example';
|
||||
|
||||
@@ -11,7 +11,6 @@ export const getDistDir = () => process.env.DIST_DIR || join(getRootDir(), 'dist
|
||||
export const getCdnDir = () => process.env.CDN_DIR || join(getRootDir(), 'dist-cdn');
|
||||
export const getDocsDir = () => process.env.DOCS_DIR || join(getRootDir(), 'docs');
|
||||
export const getSiteDir = () => process.env.SITE_DIR || join(getRootDir(), '_site');
|
||||
export const getEleventyConfigPath = () => process.env.ELEVENTY_CONFIG_PATH || join(getDocsDir(), '.eleventy.js');
|
||||
|
||||
/**
|
||||
* Runs a script and returns a promise that resolves with the content of stdout when the script exits or rejects with
|
||||
@@ -58,3 +57,4 @@ export function runScript(scriptPath, args = [], options = {}) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -36,10 +36,8 @@ img[aria-hidden='true'] {
|
||||
transition: opacity var(--wa-transition-normal) var(--wa-transition-easing);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
:host([play]:hover) .control-box {
|
||||
opacity: 1;
|
||||
}
|
||||
:host([play]:hover) .control-box {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:host([play]:not(:hover)) .control-box {
|
||||
|
||||
@@ -28,7 +28,7 @@ import styles from './animated-image.css';
|
||||
*/
|
||||
@customElement('wa-animated-image')
|
||||
export default class WaAnimatedImage extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
@query('.animated') animatedImage: HTMLImageElement;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { animations } from './animations.js';
|
||||
*/
|
||||
@customElement('wa-animation')
|
||||
export default class WaAnimation extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private animation?: Animation;
|
||||
private hasStarted = false;
|
||||
|
||||
@@ -29,7 +29,7 @@ import styles from './avatar.css';
|
||||
*/
|
||||
@customElement('wa-avatar')
|
||||
export default class WaAvatar extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
@state() private hasError = false;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import styles from './badge.css';
|
||||
*/
|
||||
@customElement('wa-badge')
|
||||
export default class WaBadge extends WebAwesomeElement {
|
||||
static css = [variantStyles, appearanceStyles, styles];
|
||||
static shadowStyle = [variantStyles, appearanceStyles, styles];
|
||||
|
||||
/** The badge's theme variant. Defaults to `brand` if not within another element with a variant. */
|
||||
@property({ reflect: true }) variant: 'brand' | 'neutral' | 'success' | 'warning' | 'danger' = 'brand';
|
||||
|
||||
@@ -26,10 +26,8 @@
|
||||
transition: color var(--wa-transition-normal) var(--wa-transition-easing);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
:host(:not(:last-of-type)) .label:hover {
|
||||
color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
|
||||
}
|
||||
:host(:not(:last-of-type)) .label:hover {
|
||||
color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
|
||||
}
|
||||
|
||||
:host(:not(:last-of-type)) .label:active {
|
||||
|
||||
@@ -12,8 +12,8 @@ import styles from './breadcrumb-item.css';
|
||||
* @since 2.0
|
||||
*
|
||||
* @slot - The breadcrumb item's label.
|
||||
* @slot prefix - An optional prefix, usually an icon.
|
||||
* @slot suffix - An optional suffix, usually an icon.
|
||||
* @slot prefix - An optional prefix, usually an icon or icon button.
|
||||
* @slot suffix - An optional suffix, usually an icon or icon button.
|
||||
* @slot separator - The separator to use for the breadcrumb item. This will only change the separator for this item. If
|
||||
* you want to change it for all items in the group, set the separator on `<wa-breadcrumb>` instead.
|
||||
*
|
||||
@@ -24,7 +24,7 @@ import styles from './breadcrumb-item.css';
|
||||
*/
|
||||
@customElement('wa-breadcrumb-item')
|
||||
export default class WaBreadcrumbItem extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
@query('slot:not([name])') defaultSlot: HTMLSlotElement;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import styles from './breadcrumb.css';
|
||||
*/
|
||||
@customElement('wa-breadcrumb')
|
||||
export default class WaBreadcrumb extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private separatorDir = this.localize.dir();
|
||||
|
||||
@@ -9,11 +9,9 @@
|
||||
flex-wrap: wrap;
|
||||
gap: 1px;
|
||||
|
||||
@media (hover: hover) {
|
||||
> :hover,
|
||||
&::slotted(:hover) {
|
||||
z-index: 1;
|
||||
}
|
||||
> :hover,
|
||||
&::slotted(:hover) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Focus and checked are always on top */
|
||||
|
||||
@@ -20,7 +20,7 @@ import styles from './button-group.css';
|
||||
*/
|
||||
@customElement('wa-button-group')
|
||||
export default class WaButtonGroup extends WebAwesomeElement {
|
||||
static css = [sizeStyles, variantStyles, styles];
|
||||
static shadowStyle = [sizeStyles, variantStyles, styles];
|
||||
|
||||
@query('slot') defaultSlot: HTMLSlotElement;
|
||||
|
||||
@@ -37,7 +37,7 @@ export default class WaButtonGroup extends WebAwesomeElement {
|
||||
@property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'horizontal';
|
||||
|
||||
/** The component's size. */
|
||||
@property({ reflect: true }) size: 'small' | 'medium' | 'large'; // unset by default to not override child elements
|
||||
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
|
||||
|
||||
/** The button group's theme variant. Defaults to `neutral` if not within another element with a variant. */
|
||||
@property({ reflect: true }) variant: 'neutral' | 'brand' | 'success' | 'warning' | 'danger' = 'neutral';
|
||||
@@ -85,7 +85,7 @@ export default class WaButtonGroup extends WebAwesomeElement {
|
||||
|
||||
if (button) {
|
||||
if ((button as WaButton).appearance === 'outlined') this.hasOutlined = true;
|
||||
if (this.size) button.setAttribute('size', this.size);
|
||||
button.setAttribute('size', this.size);
|
||||
button.classList.add('wa-button-group__button');
|
||||
button.classList.toggle('wa-button-group__horizontal', this.orientation === 'horizontal');
|
||||
button.classList.toggle('wa-button-group__vertical', this.orientation === 'vertical');
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
transition-duration: var(--wa-transition-fast);
|
||||
transition-timing-function: var(--wa-transition-easing);
|
||||
cursor: pointer;
|
||||
padding: 0 var(--wa-form-control-padding-inline);
|
||||
padding: 0 var(--wa-space, var(--wa-space-m));
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-size: var(--wa-size, var(--wa-font-size-m));
|
||||
font-weight: var(--wa-font-weight-action);
|
||||
line-height: calc(var(--wa-form-control-height) - var(--border-width) * 2);
|
||||
height: var(--wa-form-control-height);
|
||||
@@ -51,12 +51,10 @@
|
||||
}
|
||||
|
||||
/* Interactive states */
|
||||
@media (hover: hover) {
|
||||
.button:not(.disabled):not(.loading):hover {
|
||||
background-color: var(--background-color-hover, var(--background-color));
|
||||
border-color: var(--border-color-hover, var(--border-color, var(--background-color-hover)));
|
||||
color: var(--text-color-hover, var(--text-color));
|
||||
}
|
||||
.button:not(.disabled):not(.loading):hover {
|
||||
background-color: var(--background-color-hover, var(--background-color));
|
||||
border-color: var(--border-color-hover, var(--border-color, var(--background-color-hover)));
|
||||
color: var(--text-color-hover, var(--text-color));
|
||||
}
|
||||
|
||||
.button:not(.disabled):not(.loading):active {
|
||||
@@ -91,13 +89,6 @@
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* Icon buttons */
|
||||
.button.is-icon-button {
|
||||
outline-offset: 2px;
|
||||
width: var(--wa-form-control-height);
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
/* Pill modifier */
|
||||
:host([pill]) .button {
|
||||
border-radius: var(--wa-border-radius-pill);
|
||||
@@ -116,11 +107,11 @@
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.label::slotted(wa-icon) {
|
||||
align-self: center;
|
||||
vertical-align: -2px;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -173,7 +164,6 @@ wa-icon[part~='caret'] {
|
||||
/*
|
||||
* Badges
|
||||
*/
|
||||
|
||||
button ::slotted(wa-badge) {
|
||||
border-color: var(--wa-color-surface-default);
|
||||
position: absolute;
|
||||
@@ -192,12 +182,12 @@ button ::slotted(wa-badge) {
|
||||
*/
|
||||
|
||||
slot[name='prefix']::slotted(*) {
|
||||
margin-inline-end: var(--wa-form-control-padding-inline);
|
||||
margin-inline-end: var(--wa-space);
|
||||
}
|
||||
|
||||
slot[name='suffix']::slotted(*),
|
||||
.button:not(.visually-hidden-label) [part~='caret'] {
|
||||
margin-inline-start: var(--wa-form-control-padding-inline);
|
||||
margin-inline-start: var(--wa-space);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -53,7 +53,7 @@ import styles from './button.css';
|
||||
*/
|
||||
@customElement('wa-button')
|
||||
export default class WaButton extends WebAwesomeFormAssociatedElement {
|
||||
static css = [styles, variantStyles, sizeStyles, appearanceStyles];
|
||||
static shadowStyle = [styles, variantStyles, sizeStyles, appearanceStyles];
|
||||
|
||||
static get validators() {
|
||||
return [...super.validators, MirrorValidator()];
|
||||
@@ -64,15 +64,13 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@query('.button') button: HTMLButtonElement | HTMLLinkElement;
|
||||
@query('slot:not([name])') labelSlot: HTMLSlotElement;
|
||||
|
||||
@state() invalid = false;
|
||||
@state() isIconButton = false;
|
||||
|
||||
@property() title = ''; // make reactive to pass through
|
||||
|
||||
/** The button's theme variant. Defaults to `neutral` if not within another element with a variant. */
|
||||
@property({ reflect: true }) variant: 'neutral' | 'brand' | 'success' | 'warning' | 'danger' = 'neutral';
|
||||
@property({ reflect: true })
|
||||
variant: 'neutral' | 'brand' | 'success' | 'warning' | 'danger' = 'neutral';
|
||||
|
||||
/** The button's visual appearance. */
|
||||
@property({ reflect: true }) appearance: 'accent' | 'filled' | 'outlined' | 'plain' = 'accent';
|
||||
@@ -144,6 +142,19 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
|
||||
/** Used to override the form owner's `target` attribute. */
|
||||
@property({ attribute: 'formtarget' }) formTarget: '_self' | '_blank' | '_parent' | '_top' | string;
|
||||
|
||||
private handleClick() {
|
||||
const form = this.getForm();
|
||||
|
||||
if (!form) return;
|
||||
|
||||
const lightDOMButton = this.constructLightDOMButton();
|
||||
|
||||
// form.append(lightDOMButton);
|
||||
this.parentElement?.append(lightDOMButton);
|
||||
lightDOMButton.click();
|
||||
lightDOMButton.remove();
|
||||
}
|
||||
|
||||
private constructLightDOMButton() {
|
||||
const button = document.createElement('button');
|
||||
button.type = this.type;
|
||||
@@ -167,52 +178,10 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
|
||||
return button;
|
||||
}
|
||||
|
||||
private handleClick() {
|
||||
const form = this.getForm();
|
||||
|
||||
if (!form) return;
|
||||
|
||||
const lightDOMButton = this.constructLightDOMButton();
|
||||
|
||||
// form.append(lightDOMButton);
|
||||
this.parentElement?.append(lightDOMButton);
|
||||
lightDOMButton.click();
|
||||
lightDOMButton.remove();
|
||||
}
|
||||
|
||||
private handleInvalid() {
|
||||
this.dispatchEvent(new WaInvalidEvent());
|
||||
}
|
||||
|
||||
private handleLabelSlotChange() {
|
||||
const nodes = this.labelSlot.assignedNodes({ flatten: true });
|
||||
let hasIconLabel = false;
|
||||
let hasIcon = false;
|
||||
let text = '';
|
||||
|
||||
// If there's only an icon and no text, it's an icon button
|
||||
[...nodes].forEach(node => {
|
||||
if (node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).localName === 'wa-icon') {
|
||||
hasIcon = true;
|
||||
if (!hasIconLabel) hasIconLabel = (node as HTMLElement).hasAttribute('label');
|
||||
}
|
||||
|
||||
// Concatenate text nodes
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
text += node.textContent;
|
||||
}
|
||||
});
|
||||
|
||||
this.isIconButton = text.trim() === '' && hasIcon;
|
||||
|
||||
if (this.isIconButton && !hasIconLabel) {
|
||||
console.warn(
|
||||
'Icon buttons must have a label for screen readers. Add <wa-icon label="..."> to remove this warning.',
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private isButton() {
|
||||
return this.href ? false : true;
|
||||
}
|
||||
@@ -265,7 +234,6 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
|
||||
'has-label': this.hasSlotController.test('[default]'),
|
||||
'has-prefix': this.hasSlotController.test('prefix'),
|
||||
'has-suffix': this.hasSlotController.test('suffix'),
|
||||
'is-icon-button': this.isIconButton,
|
||||
})}
|
||||
?disabled=${ifDefined(isLink ? undefined : this.disabled)}
|
||||
type=${ifDefined(isLink ? undefined : this.type)}
|
||||
@@ -283,7 +251,7 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
|
||||
@click=${this.handleClick}
|
||||
>
|
||||
<slot name="prefix" part="prefix" class="prefix"></slot>
|
||||
<slot part="label" class="label" @slotchange=${this.handleLabelSlotChange}></slot>
|
||||
<slot part="label" class="label"></slot>
|
||||
<slot name="suffix" part="suffix" class="suffix"></slot>
|
||||
${
|
||||
this.caret
|
||||
|
||||
@@ -24,7 +24,7 @@ import styles from './callout.css';
|
||||
*/
|
||||
@customElement('wa-callout')
|
||||
export default class WaCallout extends WebAwesomeElement {
|
||||
static css = [variantStyles, appearanceStyles, sizeStyles, styles];
|
||||
static shadowStyle = [variantStyles, appearanceStyles, sizeStyles, styles];
|
||||
|
||||
/** The callout's theme variant. Defaults to `brand` if not within another element with a variant. */
|
||||
@property({ reflect: true }) variant: 'brand' | 'neutral' | 'success' | 'warning' | 'danger' | 'brand' = 'brand';
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
:host {
|
||||
--spacing: var(--wa-space-l);
|
||||
--space-s: var(--wa-space-l);
|
||||
--space-m: var(--wa-space-xl);
|
||||
--space-l: var(--wa-space-2xl);
|
||||
|
||||
--spacing: var(--wa-space);
|
||||
--border-width: var(--wa-panel-border-width);
|
||||
--outlined-background-color: var(--wa-color-surface-default);
|
||||
--outlined-border-color: var(--wa-color-surface-border);
|
||||
|
||||
@@ -26,11 +26,11 @@ import styles from './card.css';
|
||||
* @cssproperty [--border-color=var(--wa-color-surface-border)] - The color of the card's borders. Expects a single value.
|
||||
* @cssproperty [--inner-border-color=var(--wa-color-surface-border)] - The color of the card's inner borders, e.g. those separating headers and footers from the main content. Expects a single value.
|
||||
* @cssproperty [--border-width=var(--wa-panel-border-width)] - The width of the card's borders. Expects a single value.
|
||||
* @cssproperty [--spacing=var(--wa-space-l)] - The amount of space around and between sections of the card. Expects a single value.
|
||||
* @cssproperty [--spacing=var(--wa-space)] - The amount of space around and between sections of the card. Expects a single value.
|
||||
*/
|
||||
@customElement('wa-card')
|
||||
export default class WaCard extends WebAwesomeElement {
|
||||
static css = [sizeStyles, appearanceStyles, styles];
|
||||
static shadowStyle = [sizeStyles, appearanceStyles, styles];
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer', 'header', 'media');
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import styles from './carousel-item.css';
|
||||
*/
|
||||
@customElement('wa-carousel-item')
|
||||
export default class WaCarouselItem extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
@@ -52,7 +52,7 @@ import styles from './carousel.css';
|
||||
*/
|
||||
@customElement('wa-carousel')
|
||||
export default class WaCarousel extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
/** When set, allows the user to navigate the carousel in the same direction indefinitely. */
|
||||
@property({ type: Boolean, reflect: true }) loop = false;
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
--border-width: var(--wa-form-control-border-width);
|
||||
--box-shadow: none;
|
||||
--checked-icon-color: var(--wa-color-brand-on-loud);
|
||||
--checked-icon-scale: 0.8;
|
||||
--toggle-size: var(--wa-form-control-toggle-size);
|
||||
--toggle-size: 1lh;
|
||||
|
||||
color: var(--wa-form-control-value-color);
|
||||
display: inline-flex;
|
||||
@@ -44,7 +43,7 @@
|
||||
color var(--wa-transition-fast);
|
||||
transition-timing-function: var(--wa-transition-easing);
|
||||
|
||||
margin-inline-end: 0.5em;
|
||||
margin-inline-end: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
:host [part~='base'] {
|
||||
@@ -87,7 +86,6 @@ input {
|
||||
|
||||
[part~='icon'] {
|
||||
display: flex;
|
||||
scale: var(--checked-icon-scale);
|
||||
|
||||
/* Without this, Safari renders the icon slightly to the left */
|
||||
&::part(svg) {
|
||||
|
||||
@@ -199,17 +199,17 @@ describe('<wa-checkbox>', () => {
|
||||
|
||||
expect(checkbox.checkValidity()).to.be.false;
|
||||
expect(checkbox.checkValidity()).to.be.false;
|
||||
expect(checkbox.customStates.has('invalid')).to.be.true;
|
||||
expect(checkbox.customStates.has('valid')).to.be.false;
|
||||
expect(checkbox.customStates.has('user-invalid')).to.be.true;
|
||||
expect(checkbox.customStates.has('user-valid')).to.be.false;
|
||||
expect(checkbox.hasCustomState('invalid')).to.be.true;
|
||||
expect(checkbox.hasCustomState('valid')).to.be.false;
|
||||
expect(checkbox.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(checkbox.hasCustomState('user-valid')).to.be.false;
|
||||
|
||||
await clickOnElement(checkbox);
|
||||
await checkbox.updateComplete;
|
||||
await aTimeout(0);
|
||||
|
||||
expect(checkbox.customStates.has('user-invalid')).to.be.true;
|
||||
expect(checkbox.customStates.has('user-valid')).to.be.false;
|
||||
expect(checkbox.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(checkbox.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should be invalid when required and unchecked', async () => {
|
||||
@@ -244,12 +244,12 @@ describe('<wa-checkbox>', () => {
|
||||
`);
|
||||
const checkbox = el.querySelector<WaCheckbox>('wa-checkbox')!;
|
||||
|
||||
expect(checkbox.customStates.has('required')).to.be.true;
|
||||
expect(checkbox.customStates.has('optional')).to.be.false;
|
||||
expect(checkbox.customStates.has('invalid')).to.be.true;
|
||||
expect(checkbox.customStates.has('valid')).to.be.false;
|
||||
expect(checkbox.customStates.has('user-invalid')).to.be.false;
|
||||
expect(checkbox.customStates.has('user-valid')).to.be.false;
|
||||
expect(checkbox.hasCustomState('required')).to.be.true;
|
||||
expect(checkbox.hasCustomState('optional')).to.be.false;
|
||||
expect(checkbox.hasCustomState('invalid')).to.be.true;
|
||||
expect(checkbox.hasCustomState('valid')).to.be.false;
|
||||
expect(checkbox.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(checkbox.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ import styles from './checkbox.css';
|
||||
*/
|
||||
@customElement('wa-checkbox')
|
||||
export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
|
||||
static css = [formControlStyles, sizeStyles, styles];
|
||||
static shadowStyle = [formControlStyles, sizeStyles, styles];
|
||||
|
||||
static shadowRootOptions = { ...WebAwesomeFormAssociatedElement.shadowRootOptions, delegatesFocus: true };
|
||||
|
||||
@@ -156,14 +156,14 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
|
||||
this.input.indeterminate = this.indeterminate; // force a sync update
|
||||
}
|
||||
|
||||
this.customStates.set('checked', this.checked);
|
||||
this.customStates.set('indeterminate', this.indeterminate);
|
||||
this.toggleCustomState('checked', this.checked);
|
||||
this.toggleCustomState('indeterminate', this.indeterminate);
|
||||
this.updateValidity();
|
||||
}
|
||||
|
||||
@watch('disabled')
|
||||
handleDisabledChange() {
|
||||
this.customStates.set('disabled', this.disabled);
|
||||
this.toggleCustomState('disabled', this.disabled);
|
||||
}
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
--slider-handle-size: calc(var(--slider-height) + 0.25rem);
|
||||
--swatch-border-radius: var(--wa-border-radius-m);
|
||||
--swatch-size: 1.5rem;
|
||||
--trigger-border-radius: var(--wa-form-control-border-radius);
|
||||
--trigger-border-radius: var(--wa-border-radius-circle);
|
||||
}
|
||||
|
||||
.color-picker {
|
||||
@@ -301,7 +301,6 @@
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
forced-color-adjust: none;
|
||||
width: var(--wa-form-control-height);
|
||||
height: var(--wa-form-control-height);
|
||||
@@ -319,7 +318,7 @@
|
||||
background-color: currentColor;
|
||||
box-shadow:
|
||||
inset 0 0 0 var(--border-width) var(--wa-form-control-border-color),
|
||||
inset 0 0 0 calc(var(--border-width) * 3) var(--wa-color-surface-default);
|
||||
inset 0 0 0 calc(var(--border-width) * 2) var(--wa-color-surface-default);
|
||||
}
|
||||
|
||||
.trigger-empty:before {
|
||||
|
||||
@@ -501,12 +501,12 @@ describe('<wa-color-picker>', () => {
|
||||
const grid = el.shadowRoot!.querySelector('[part~="grid"]')!;
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.customStates.has('required')).to.be.true;
|
||||
expect(el.customStates.has('optional')).to.be.false;
|
||||
expect(el.customStates.has('invalid')).to.be.false;
|
||||
expect(el.customStates.has('valid')).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
expect(el.hasCustomState('required')).to.be.true;
|
||||
expect(el.hasCustomState('optional')).to.be.false;
|
||||
expect(el.hasCustomState('invalid')).to.be.false;
|
||||
expect(el.hasCustomState('valid')).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
|
||||
await clickOnElement(trigger);
|
||||
await aTimeout(500);
|
||||
@@ -514,8 +514,8 @@ describe('<wa-color-picker>', () => {
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.true;
|
||||
});
|
||||
|
||||
it('should receive the correct validation attributes ("states") when invalid', async () => {
|
||||
@@ -523,12 +523,12 @@ describe('<wa-color-picker>', () => {
|
||||
const trigger = el.shadowRoot!.querySelector('[part~="trigger"]')!;
|
||||
const grid = el.shadowRoot!.querySelector('[part~="grid"]')!;
|
||||
|
||||
expect(el.customStates.has('required')).to.be.true;
|
||||
expect(el.customStates.has('optional')).to.be.false;
|
||||
expect(el.customStates.has('invalid')).to.be.true;
|
||||
expect(el.customStates.has('valid')).to.be.false;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
expect(el.hasCustomState('required')).to.be.true;
|
||||
expect(el.hasCustomState('optional')).to.be.false;
|
||||
expect(el.hasCustomState('invalid')).to.be.true;
|
||||
expect(el.hasCustomState('valid')).to.be.false;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
|
||||
await clickOnElement(trigger);
|
||||
await aTimeout(500);
|
||||
@@ -536,8 +536,8 @@ describe('<wa-color-picker>', () => {
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,7 +102,7 @@ declare const EyeDropper: EyeDropperConstructor;
|
||||
*/
|
||||
@customElement('wa-color-picker')
|
||||
export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
|
||||
static css = [visuallyHidden, sizeStyles, formControlStyles, styles];
|
||||
static shadowStyle = [visuallyHidden, sizeStyles, formControlStyles, styles];
|
||||
|
||||
static shadowRootOptions = { ...WebAwesomeFormAssociatedElement.shadowRootOptions, delegatesFocus: true };
|
||||
|
||||
@@ -1017,7 +1017,6 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
|
||||
<wa-button
|
||||
part="format-button"
|
||||
size="small"
|
||||
appearance="outlined"
|
||||
aria-label=${this.localize.term('toggleColorFormat')}
|
||||
exportparts="
|
||||
base:format-button__base,
|
||||
@@ -1039,7 +1038,6 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
|
||||
<wa-button
|
||||
part="eye-dropper-button"
|
||||
size="small"
|
||||
appearance="outlined"
|
||||
exportparts="
|
||||
base:eye-dropper-button__base,
|
||||
prefix:eye-dropper-button__prefix,
|
||||
|
||||
@@ -38,7 +38,7 @@ import styles from './comparison.css';
|
||||
*/
|
||||
@customElement('wa-comparison')
|
||||
export default class WaComparison extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@@ -55,12 +55,12 @@ export default class WaComparison extends WebAwesomeElement {
|
||||
|
||||
drag(this, {
|
||||
onMove: x => {
|
||||
this.customStates.set('dragging', true);
|
||||
this.toggleCustomState('dragging', true);
|
||||
this.position = parseFloat(clamp((x / width) * 100, 0, 100).toFixed(2));
|
||||
if (isRtl) this.position = 100 - this.position;
|
||||
},
|
||||
onStop: () => {
|
||||
this.customStates.set('dragging', false);
|
||||
this.toggleCustomState('dragging', false);
|
||||
},
|
||||
initialEvent: event,
|
||||
});
|
||||
|
||||
@@ -17,18 +17,12 @@
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0.5em;
|
||||
padding: var(--wa-space-xs);
|
||||
cursor: pointer;
|
||||
transition: color var(--wa-transition-fast) var(--wa-transition-easing);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.button:hover:not([disabled]) {
|
||||
background-color: var(--background-color-hover);
|
||||
color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
|
||||
}
|
||||
}
|
||||
|
||||
.button:hover:not([disabled]),
|
||||
.button:focus-visible:not([disabled]) {
|
||||
background-color: var(--background-color-hover);
|
||||
color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
|
||||
|
||||
@@ -44,7 +44,7 @@ import styles from './copy-button.css';
|
||||
*/
|
||||
@customElement('wa-copy-button')
|
||||
export default class WaCopyButton extends WebAwesomeElement {
|
||||
static css = [visuallyHidden, styles];
|
||||
static shadowStyle = [visuallyHidden, styles];
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ import styles from './details.css';
|
||||
*/
|
||||
@customElement('wa-details')
|
||||
export default class WaDetails extends WebAwesomeElement {
|
||||
static css = [appearanceStyles, styles];
|
||||
static shadowStyle = [appearanceStyles, styles];
|
||||
|
||||
private detailsObserver: MutationObserver;
|
||||
private readonly localize = new LocalizeController(this);
|
||||
@@ -65,9 +65,6 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
/** The summary to show in the header. If you need to display HTML, use the `summary` slot instead. */
|
||||
@property() summary: string;
|
||||
|
||||
/** Groups related details elements. When one opens, others with the same name will close. */
|
||||
@property() name: string;
|
||||
|
||||
/** Disables the details so it can't be toggled. */
|
||||
@property({ type: Boolean, reflect: true }) disabled = false;
|
||||
|
||||
@@ -141,20 +138,6 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
}
|
||||
}
|
||||
|
||||
/** Closes other <wa-details> elements in the same document when they have the same name. */
|
||||
private closeOthersWithSameName() {
|
||||
if (!this.name) return;
|
||||
|
||||
const root = this.getRootNode() as Document | ShadowRoot;
|
||||
const otherDetails = root.querySelectorAll(`wa-details[name="${this.name}"]`) as NodeListOf<WaDetails>;
|
||||
|
||||
otherDetails.forEach(detail => {
|
||||
if (detail !== this && detail.open) {
|
||||
detail.open = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@watch('open', { waitUntilFirstUpdate: true })
|
||||
async handleOpenChange() {
|
||||
if (this.open) {
|
||||
@@ -168,9 +151,6 @@ export default class WaDetails extends WebAwesomeElement {
|
||||
return;
|
||||
}
|
||||
|
||||
// Close other details with the same name
|
||||
this.closeOthersWithSameName();
|
||||
|
||||
const duration = parseDuration(getComputedStyle(this.body).getPropertyValue('--show-duration'));
|
||||
// We can't animate to 'auto', so use the scroll height for now
|
||||
await animate(
|
||||
|
||||
@@ -72,13 +72,8 @@
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
padding-inline-start: var(--spacing);
|
||||
padding: var(--spacing);
|
||||
padding-block-end: 0;
|
||||
|
||||
/* Subtract the close button's padding so that the X is visually aligned with the edges of the dialog content */
|
||||
padding-inline-end: calc(var(--spacing) - var(--wa-form-control-padding-block));
|
||||
padding-block-start: calc(var(--spacing) - var(--wa-form-control-padding-block));
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -101,11 +96,12 @@
|
||||
padding-inline-start: var(--spacing);
|
||||
}
|
||||
|
||||
.header-actions wa-button,
|
||||
.header-actions ::slotted(wa-button) {
|
||||
.header-actions wa-icon-button,
|
||||
.header-actions ::slotted(wa-icon-button) {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--wa-font-size-m);
|
||||
}
|
||||
|
||||
.body {
|
||||
|
||||
@@ -6,13 +6,12 @@ import { WaAfterShowEvent } from '../../events/after-show.js';
|
||||
import { WaHideEvent } from '../../events/hide.js';
|
||||
import { WaShowEvent } from '../../events/show.js';
|
||||
import { animateWithClass } from '../../internal/animate.js';
|
||||
import { parseSpaceDelimitedTokens } from '../../internal/parse.js';
|
||||
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import '../button/button.js';
|
||||
import '../icon-button/icon-button.js';
|
||||
import styles from './dialog.css';
|
||||
|
||||
/**
|
||||
@@ -21,11 +20,11 @@ import styles from './dialog.css';
|
||||
* @status stable
|
||||
* @since 2.0
|
||||
*
|
||||
* @dependency wa-button
|
||||
* @dependency wa-icon-button
|
||||
*
|
||||
* @slot - The dialog's main content.
|
||||
* @slot label - The dialog's label. Alternatively, you can use the `label` attribute.
|
||||
* @slot header-actions - Optional actions to add to the header. Works best with `<wa-button>`.
|
||||
* @slot header-actions - Optional actions to add to the header. Works best with `<wa-icon-button>`.
|
||||
* @slot footer - The dialog's footer, usually one or more buttons representing various options.
|
||||
*
|
||||
* @event wa-show - Emitted when the dialog opens.
|
||||
@@ -38,9 +37,9 @@ import styles from './dialog.css';
|
||||
* @event wa-after-hide - Emitted after the dialog closes and all animations are complete.
|
||||
*
|
||||
* @csspart header - The dialog's header. This element wraps the title and header actions.
|
||||
* @csspart header-actions - Optional actions to add to the header. Works best with `<wa-button>`.
|
||||
* @csspart header-actions - Optional actions to add to the header. Works best with `<wa-icon-button>`.
|
||||
* @csspart title - The dialog's title.
|
||||
* @csspart close-button - The close button, a `<wa-button>`.
|
||||
* @csspart close-button - The close button, a `<wa-icon-button>`.
|
||||
* @csspart close-button__base - The close button's exported `base` part.
|
||||
* @csspart body - The dialog's body.
|
||||
* @csspart footer - The dialog's footer.
|
||||
@@ -55,7 +54,7 @@ import styles from './dialog.css';
|
||||
*/
|
||||
@customElement('wa-dialog')
|
||||
export default class WaDialog extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer', 'header-actions', 'label');
|
||||
@@ -63,7 +62,10 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
|
||||
@query('.dialog') dialog: HTMLDialogElement;
|
||||
|
||||
/** Indicates whether or not the dialog is open. Toggle this attribute to show and hide the dialog. */
|
||||
/**
|
||||
* Indicates whether or not the dialog is open. You can toggle this attribute to show and hide the dialog, or you can
|
||||
* use the `show()` and `hide()` methods and this attribute will reflect the dialog's open state.
|
||||
*/
|
||||
@property({ type: Boolean, reflect: true }) open = false;
|
||||
|
||||
/**
|
||||
@@ -233,20 +235,16 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
</h2>
|
||||
<div part="header-actions" class="header-actions">
|
||||
<slot name="header-actions"></slot>
|
||||
<wa-button
|
||||
<wa-icon-button
|
||||
part="close-button"
|
||||
exportparts="base:close-button__base"
|
||||
class="close"
|
||||
appearance="plain"
|
||||
name="xmark"
|
||||
label=${this.localize.term('close')}
|
||||
library="system"
|
||||
variant="solid"
|
||||
@click="${(event: PointerEvent) => this.requestClose(event.target as Element)}"
|
||||
>
|
||||
<wa-icon
|
||||
name="xmark"
|
||||
label=${this.localize.term('close')}
|
||||
library="system"
|
||||
variant="solid"
|
||||
></wa-icon>
|
||||
</wa-button>
|
||||
></wa-icon-button>
|
||||
</div>
|
||||
</header>
|
||||
`
|
||||
@@ -266,28 +264,6 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Watch for data-dialog="open *" clicks
|
||||
//
|
||||
document.addEventListener('click', (event: MouseEvent) => {
|
||||
const dialogAttrEl = (event.target as Element).closest('[data-dialog]');
|
||||
|
||||
if (dialogAttrEl instanceof Element) {
|
||||
const [command, id] = parseSpaceDelimitedTokens(dialogAttrEl.getAttribute('data-dialog') || '');
|
||||
|
||||
if (command === 'open' && id?.length) {
|
||||
const doc = dialogAttrEl.getRootNode() as Document | ShadowRoot;
|
||||
const dialog = doc.getElementById(id) as WaDialog;
|
||||
|
||||
if (dialog?.localName === 'wa-dialog') {
|
||||
dialog.open = true;
|
||||
} else {
|
||||
console.warn(`A dialog with an ID of "${id}" could not be found in this document.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Ugly, but it fixes light dismiss in Safari: https://bugs.webkit.org/show_bug.cgi?id=267688
|
||||
if (!isServer) {
|
||||
document.addEventListener('pointerdown', () => {
|
||||
|
||||
@@ -15,7 +15,7 @@ import styles from './divider.css';
|
||||
*/
|
||||
@customElement('wa-divider')
|
||||
export default class WaDivider extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
/** Sets the divider's orientation. */
|
||||
@property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'horizontal';
|
||||
|
||||
@@ -136,12 +136,8 @@
|
||||
.header {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
padding-inline-start: var(--spacing);
|
||||
padding: var(--spacing);
|
||||
padding-block-end: 0;
|
||||
|
||||
/* Subtract the close button's padding so that the X is visually aligned with the edges of the dialog content */
|
||||
padding-inline-end: calc(var(--spacing) - var(--wa-form-control-padding-block));
|
||||
padding-block-start: calc(var(--spacing) - var(--wa-form-control-padding-block));
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -164,11 +160,12 @@
|
||||
padding-inline-start: var(--spacing);
|
||||
}
|
||||
|
||||
.header-actions wa-button,
|
||||
.header-actions ::slotted(wa-button) {
|
||||
.header-actions wa-icon-button,
|
||||
.header-actions ::slotted(wa-icon-button) {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--wa-font-size-m);
|
||||
}
|
||||
|
||||
.body {
|
||||
|
||||
@@ -6,13 +6,12 @@ import { WaAfterShowEvent } from '../../events/after-show.js';
|
||||
import { WaHideEvent } from '../../events/hide.js';
|
||||
import { WaShowEvent } from '../../events/show.js';
|
||||
import { animateWithClass } from '../../internal/animate.js';
|
||||
import { parseSpaceDelimitedTokens } from '../../internal/parse.js';
|
||||
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import '../button/button.js';
|
||||
import '../icon-button/icon-button.js';
|
||||
import styles from './drawer.css';
|
||||
|
||||
/**
|
||||
@@ -21,11 +20,11 @@ import styles from './drawer.css';
|
||||
* @status stable
|
||||
* @since 2.0
|
||||
*
|
||||
* @dependency wa-button
|
||||
* @dependency wa-icon-button
|
||||
*
|
||||
* @slot - The drawer's main content.
|
||||
* @slot label - The drawer's label. Alternatively, you can use the `label` attribute.
|
||||
* @slot header-actions - Optional actions to add to the header. Works best with `<wa-button>`.
|
||||
* @slot header-actions - Optional actions to add to the header. Works best with `<wa-icon-button>`.
|
||||
* @slot footer - The drawer's footer, usually one or more buttons representing various options.
|
||||
*
|
||||
* @event wa-show - Emitted when the drawer opens.
|
||||
@@ -33,15 +32,15 @@ import styles from './drawer.css';
|
||||
* @event wa-hide - Emitted when the drawer closes.
|
||||
* @event wa-after-hide - Emitted after the drawer closes and all animations are complete.
|
||||
* @event {{ source: Element }} wa-hide - Emitted when the drawer is requesting to close. Calling
|
||||
* `event.preventDefault()` will prevent the drawer from closing. You can inspect `event.detail.source` to see which
|
||||
* element caused the drawer to close. If the source is the drawer element itself, the user has pressed [[Escape]] or
|
||||
* the drawer has been closed programmatically. Avoid using this unless closing the drawer will result in destructive
|
||||
* `event.preventDefault()` will prevent the dialog from closing. You can inspect `event.detail.source` to see which
|
||||
* element caused the dialog to close. If the source is the dialog element itself, the user has pressed [[Escape]] or
|
||||
* the dialog has been closed programmatically. Avoid using this unless closing the dialog will result in destructive
|
||||
* behavior such as data loss.
|
||||
*
|
||||
* @csspart header - The drawer's header. This element wraps the title and header actions.
|
||||
* @csspart header-actions - Optional actions to add to the header. Works best with `<wa-button>`.
|
||||
* @csspart header-actions - Optional actions to add to the header. Works best with `<wa-icon-button>`.
|
||||
* @csspart title - The drawer's title.
|
||||
* @csspart close-button - The close button, a `<wa-button>`.
|
||||
* @csspart close-button - The close button, a `<wa-icon-button>`.
|
||||
* @csspart close-button__base - The close button's exported `base` part.
|
||||
* @csspart body - The drawer's body.
|
||||
* @csspart footer - The drawer's footer.
|
||||
@@ -60,7 +59,7 @@ import styles from './drawer.css';
|
||||
*/
|
||||
@customElement('wa-drawer')
|
||||
export default class WaDrawer extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer', 'header-actions', 'label');
|
||||
@@ -68,7 +67,10 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
|
||||
@query('.drawer') drawer: HTMLDialogElement;
|
||||
|
||||
/** Indicates whether or not the drawer is open. Toggle this attribute to show and hide the drawer. */
|
||||
/**
|
||||
* Indicates whether or not the drawer is open. You can toggle this attribute to show and hide the drawer, or you can
|
||||
* use the `show()` and `hide()` methods and this attribute will reflect the drawer's open state.
|
||||
*/
|
||||
@property({ type: Boolean, reflect: true }) open = false;
|
||||
|
||||
/**
|
||||
@@ -249,20 +251,16 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
</h2>
|
||||
<div part="header-actions" class="header-actions">
|
||||
<slot name="header-actions"></slot>
|
||||
<wa-button
|
||||
<wa-icon-button
|
||||
part="close-button"
|
||||
exportparts="base:close-button__base"
|
||||
class="close"
|
||||
appearance="plain"
|
||||
name="xmark"
|
||||
label=${this.localize.term('close')}
|
||||
library="system"
|
||||
variant="solid"
|
||||
@click="${(event: PointerEvent) => this.requestClose(event.target as Element)}"
|
||||
>
|
||||
<wa-icon
|
||||
name="xmark"
|
||||
label=${this.localize.term('close')}
|
||||
library="system"
|
||||
variant="solid"
|
||||
></wa-icon>
|
||||
</wa-button>
|
||||
></wa-icon-button>
|
||||
</div>
|
||||
</header>
|
||||
`
|
||||
@@ -282,28 +280,6 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Watch for data-drawer="open *" clicks
|
||||
//
|
||||
document.addEventListener('click', (event: MouseEvent) => {
|
||||
const drawerAttrEl = (event.target as Element).closest('[data-drawer]');
|
||||
|
||||
if (drawerAttrEl instanceof Element) {
|
||||
const [command, id] = parseSpaceDelimitedTokens(drawerAttrEl.getAttribute('data-drawer') || '');
|
||||
|
||||
if (command === 'open' && id?.length) {
|
||||
const doc = drawerAttrEl.getRootNode() as Document | ShadowRoot;
|
||||
const drawer = doc.getElementById(id) as WaDrawer;
|
||||
|
||||
if (drawer?.localName === 'wa-drawer') {
|
||||
drawer.open = true;
|
||||
} else {
|
||||
console.warn(`A drawer with an ID of "${id}" could not be found in this document.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!isServer) {
|
||||
// Ugly, but it fixes light dismiss in Safari: https://bugs.webkit.org/show_bug.cgi?id=267688
|
||||
document.body.addEventListener('pointerdown', () => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { watch } from '../../internal/watch.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import sizeStyles from '../../styles/utilities/size.css';
|
||||
import type WaButton from '../button/button.js';
|
||||
import type WaIconButton from '../icon-button/icon-button.js';
|
||||
import type WaMenu from '../menu/menu.js';
|
||||
import '../popup/popup.js';
|
||||
import type WaPopup from '../popup/popup.js';
|
||||
@@ -43,7 +44,7 @@ import styles from './dropdown.css';
|
||||
*/
|
||||
@customElement('wa-dropdown')
|
||||
export default class WaDropdown extends WebAwesomeElement {
|
||||
static css = [sizeStyles, styles];
|
||||
static shadowStyle = [sizeStyles, styles];
|
||||
|
||||
@query('.dropdown') popup: WaPopup;
|
||||
@query('#trigger') trigger: HTMLSlotElement;
|
||||
@@ -281,13 +282,14 @@ export default class WaDropdown extends WebAwesomeElement {
|
||||
switch (tagName) {
|
||||
// Web Awesome buttons have to update the internal button so it's announced correctly by screen readers
|
||||
case 'wa-button':
|
||||
target = (accessibleTrigger as WaButton).button;
|
||||
case 'wa-icon-button':
|
||||
target = (accessibleTrigger as WaButton | WaIconButton).button;
|
||||
|
||||
// Either the tag hasn't registered, or it hasn't rendered.
|
||||
// So, wait for the tag to register, and then try again.
|
||||
if (target === undefined || target === null) {
|
||||
customElements.whenDefined(tagName).then(async () => {
|
||||
await (accessibleTrigger as WaButton).updateComplete;
|
||||
await (accessibleTrigger as WaButton | WaIconButton).updateComplete;
|
||||
this.updateAccessibleTrigger();
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
:host {
|
||||
--background-color-hover: var(--wa-color-neutral-fill-quiet);
|
||||
--text-color-hover: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
|
||||
--background-color-active: transparent;
|
||||
--text-color-active: color-mix(in oklab, currentColor, var(--wa-color-mix-active));
|
||||
|
||||
display: inline-block;
|
||||
color: var(--wa-color-text-quiet);
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
padding: var(--wa-space-xs);
|
||||
cursor: pointer;
|
||||
transition: color var(--wa-transition-fast) var(--wa-transition-easing);
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
:host(:not([disabled])) .icon-button:hover,
|
||||
:host(:not([disabled])) .icon-button:focus-visible {
|
||||
background-color: var(--background-color-hover);
|
||||
color: var(--text-color-hover);
|
||||
}
|
||||
|
||||
:host(:not([disabled])) .icon-button:active {
|
||||
background-color: var(--background-color-active);
|
||||
color: var(--text-color-active);
|
||||
}
|
||||
|
||||
.icon-button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:host([disabled]) .icon-button {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.icon-button:focus-visible {
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: var(--wa-focus-ring-offset);
|
||||
}
|
||||
|
||||
.icon {
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
import { expect, waitUntil } from '@open-wc/testing';
|
||||
import { html } from 'lit';
|
||||
import sinon from 'sinon';
|
||||
import { fixtures } from '../../internal/test/fixture.js';
|
||||
import type WaIconButton from './icon-button.js';
|
||||
|
||||
type LinkTarget = '_self' | '_blank' | '_parent' | '_top';
|
||||
|
||||
describe('<wa-icon-button>', () => {
|
||||
for (const fixture of fixtures) {
|
||||
describe(`with "${fixture.type}" rendering`, () => {
|
||||
describe('defaults ', () => {
|
||||
it('default properties', async () => {
|
||||
const el = await fixture<WaIconButton>(html` <wa-icon-button></wa-icon-button> `);
|
||||
|
||||
expect(el.name).to.be.null;
|
||||
expect(el.library).to.be.undefined;
|
||||
expect(el.src).to.be.undefined;
|
||||
expect(el.href).to.be.undefined;
|
||||
expect(el.target).to.be.undefined;
|
||||
expect(el.download).to.be.undefined;
|
||||
expect(el.label).to.equal('');
|
||||
expect(el.disabled).to.equal(false);
|
||||
});
|
||||
|
||||
it('renders as a button by default', async () => {
|
||||
const el = await fixture<WaIconButton>(html` <wa-icon-button></wa-icon-button> `);
|
||||
|
||||
expect(el.shadowRoot?.querySelector('button')).to.exist;
|
||||
expect(el.shadowRoot?.querySelector('a')).not.to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('when styling the host element', () => {
|
||||
it('renders the correct color and font size', async () => {
|
||||
const el = await fixture<WaIconButton>(html`
|
||||
<wa-icon-button
|
||||
library="system"
|
||||
name="check"
|
||||
style="color: rgb(0, 136, 221); font-size: 2rem;"
|
||||
></wa-icon-button>
|
||||
`);
|
||||
const icon = el.shadowRoot!.querySelector('wa-icon')!;
|
||||
const styles = getComputedStyle(icon);
|
||||
|
||||
expect(styles.color).to.equal('rgb(0, 136, 221)');
|
||||
expect(styles.fontSize).to.equal('32px');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when icon attributes are present', () => {
|
||||
it('renders an wa-icon from a library', async () => {
|
||||
const el = await fixture<WaIconButton>(html`
|
||||
<wa-icon-button library="system" name="check"></wa-icon-button>
|
||||
`);
|
||||
expect(el.shadowRoot?.querySelector('wa-icon')).to.exist;
|
||||
});
|
||||
|
||||
it('renders an wa-icon from a src', async () => {
|
||||
const fakeId = 'test-src';
|
||||
const el = await fixture<WaIconButton>(html` <wa-icon-button></wa-icon-button> `);
|
||||
|
||||
el.src = `data:image/svg+xml,${encodeURIComponent(`<svg id="${fakeId}"></svg>`)}`;
|
||||
|
||||
await el.updateComplete;
|
||||
|
||||
const internalWaIcon = el.shadowRoot?.querySelector('wa-icon');
|
||||
|
||||
await waitUntil(() => internalWaIcon?.shadowRoot?.querySelector('svg'), 'SVG not rendered');
|
||||
|
||||
expect(internalWaIcon).to.exist;
|
||||
expect(internalWaIcon?.shadowRoot?.querySelector('svg')).to.exist;
|
||||
expect(internalWaIcon?.shadowRoot?.querySelector('svg')?.getAttribute('id')).to.equal(fakeId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when href is present', () => {
|
||||
it('renders as an anchor', async () => {
|
||||
const el = await fixture<WaIconButton>(html` <wa-icon-button href="some/path"></wa-icon-button> `);
|
||||
|
||||
expect(el.shadowRoot?.querySelector('a')).to.exist;
|
||||
expect(el.shadowRoot?.querySelector('button')).not.to.exist;
|
||||
});
|
||||
|
||||
it(`the anchor rel is not present`, async () => {
|
||||
const el = await fixture<WaIconButton>(html` <wa-icon-button href="some/path"></wa-icon-button> `);
|
||||
expect(el.shadowRoot?.querySelector(`a[rel]`)).not.to.exist;
|
||||
});
|
||||
|
||||
describe('and target is present', () => {
|
||||
['_blank', '_parent', '_self', '_top'].forEach((target: LinkTarget) => {
|
||||
it(`the anchor target is the provided target: ${target}`, async () => {
|
||||
const el = await fixture<WaIconButton>(html`
|
||||
<wa-icon-button href="some/path" target="${target}"></wa-icon-button>
|
||||
`);
|
||||
expect(el.shadowRoot?.querySelector(`a[target="${target}"]`)).to.exist;
|
||||
});
|
||||
|
||||
it(`the anchor rel is set to 'noreferrer noopener'`, async () => {
|
||||
const el = await fixture<WaIconButton>(html`
|
||||
<wa-icon-button href="some/path" target="${target}"></wa-icon-button>
|
||||
`);
|
||||
expect(el.shadowRoot?.querySelector(`a[rel="noreferrer noopener"]`)).to.exist;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('and download is present', () => {
|
||||
it(`the anchor download attribute is the provided download`, async () => {
|
||||
const fakeDownload = 'some/path';
|
||||
const el = await fixture<WaIconButton>(html`
|
||||
<wa-icon-button href="some/path" download="${fakeDownload}"></wa-icon-button>
|
||||
`);
|
||||
|
||||
expect(el.shadowRoot?.querySelector(`a[download="${fakeDownload}"]`)).to.exist;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when label is present', () => {
|
||||
it('the internal aria-label attribute is set to the provided label when rendering a button', async () => {
|
||||
const fakeLabel = 'some label';
|
||||
const el = await fixture<WaIconButton>(html` <wa-icon-button label="${fakeLabel}"></wa-icon-button> `);
|
||||
expect(el.shadowRoot?.querySelector(`button[aria-label="${fakeLabel}"]`)).to.exist;
|
||||
});
|
||||
|
||||
it('the internal aria-label attribute is set to the provided label when rendering an anchor', async () => {
|
||||
const fakeLabel = 'some label';
|
||||
const el = await fixture<WaIconButton>(html`
|
||||
<wa-icon-button href="some/path" label="${fakeLabel}"></wa-icon-button>
|
||||
`);
|
||||
expect(el.shadowRoot?.querySelector(`a[aria-label="${fakeLabel}"]`)).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('when disabled is present', () => {
|
||||
it('the internal button has a disabled attribute when rendering a button', async () => {
|
||||
const el = await fixture<WaIconButton>(html` <wa-icon-button disabled></wa-icon-button> `);
|
||||
expect(el.shadowRoot?.querySelector(`button[disabled]`)).to.exist;
|
||||
});
|
||||
|
||||
it('the internal anchor has an aria-disabled attribute when rendering an anchor', async () => {
|
||||
const el = await fixture<WaIconButton>(html` <wa-icon-button href="some/path" disabled></wa-icon-button> `);
|
||||
expect(el.shadowRoot?.querySelector(`a[aria-disabled="true"]`)).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('when using methods', () => {
|
||||
it('should emit focus and blur when the button is focused and blurred', async () => {
|
||||
const el = await fixture<WaIconButton>(html` <wa-icon-button></wa-icon-button> `);
|
||||
const focusHandler = sinon.spy();
|
||||
const blurHandler = sinon.spy();
|
||||
|
||||
el.addEventListener('focus', focusHandler);
|
||||
el.addEventListener('blur', blurHandler);
|
||||
|
||||
el.focus();
|
||||
await waitUntil(() => focusHandler.calledOnce);
|
||||
|
||||
el.blur();
|
||||
await waitUntil(() => blurHandler.calledOnce);
|
||||
|
||||
expect(focusHandler).to.have.been.calledOnce;
|
||||
expect(blurHandler).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should emit a click event when calling click()', async () => {
|
||||
const el = await fixture<WaIconButton>(html` <wa-icon-button></wa-icon-button> `);
|
||||
const clickHandler = sinon.spy();
|
||||
|
||||
el.addEventListener('click', clickHandler);
|
||||
el.click();
|
||||
await waitUntil(() => clickHandler.calledOnce);
|
||||
|
||||
expect(clickHandler).to.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
139
packages/webawesome/src/components/icon-button/icon-button.ts
Normal file
139
packages/webawesome/src/components/icon-button/icon-button.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { customElement, property, query } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { html, literal } from 'lit/static-html.js';
|
||||
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
|
||||
import '../icon/icon.js';
|
||||
import styles from './icon-button.css';
|
||||
|
||||
/**
|
||||
* @summary Icons buttons are simple, icon-only buttons that can be used for actions and in toolbars.
|
||||
* @documentation https://backers.webawesome.com/docs/components/icon-button
|
||||
* @status stable
|
||||
* @since 2.0
|
||||
*
|
||||
* @dependency wa-icon
|
||||
*
|
||||
* @event blur - Emitted when the icon button loses focus.
|
||||
* @event focus - Emitted when the icon button gains focus.
|
||||
*
|
||||
* @cssproperty [--background-color-hover=var(--wa-color-neutral-fill-quiet)] - The color of the button's background on hover.
|
||||
* @cssproperty [--background-color-active=var(--wa-color-neutral-fill-quiet)] - The color of the button's background on `:active`.
|
||||
* @cssproperty --text-color-hover - The color of the button's background on hover.
|
||||
* @cssproperty --text-color-active - The color of the button's background on `:active`.
|
||||
*
|
||||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
@customElement('wa-icon-button')
|
||||
export default class WaIconButton extends WebAwesomeFormAssociatedElement {
|
||||
static shadowStyle = styles;
|
||||
|
||||
@query('.icon-button') button: HTMLButtonElement | HTMLLinkElement;
|
||||
|
||||
/** The name of the icon to draw. Available names depend on the icon library being used. */
|
||||
@property({ reflect: true }) name: string | null = null;
|
||||
|
||||
/**
|
||||
* The family of icons to choose from. For Font Awesome, valid options include `classic`, `sharp`, `duotone`, and
|
||||
* `brands`. Custom icon libraries may or may not use this property.
|
||||
*/
|
||||
@property({ reflect: true }) family: string;
|
||||
|
||||
/**
|
||||
* The name of the icon's variant. For Font Awesome, valid options include `thin`, `light`, `regular`, and `solid` for
|
||||
* the _classic_ and _sharp_ families. Custom icon libraries may or may not use this property.
|
||||
*/
|
||||
@property({ reflect: true }) variant: string;
|
||||
|
||||
/** The name of a registered custom icon library. */
|
||||
@property() library?: string;
|
||||
|
||||
/**
|
||||
* An external URL of an SVG file. Be sure you trust the content you are including, as it will be executed as code and
|
||||
* can result in XSS attacks.
|
||||
*/
|
||||
@property() src?: string;
|
||||
|
||||
/** When set, the underlying button will be rendered as an `<a>` with this `href` instead of a `<button>`. */
|
||||
@property() href?: string;
|
||||
|
||||
/** Tells the browser where to open the link. Only used when `href` is set. */
|
||||
@property() target?: '_blank' | '_parent' | '_self' | '_top';
|
||||
|
||||
/** Tells the browser to download the linked file as this filename. Only used when `href` is set. */
|
||||
@property() download?: string;
|
||||
|
||||
/**
|
||||
* A description that gets read by assistive devices. For optimal accessibility, you should always include a label
|
||||
* that describes what the icon button does.
|
||||
*/
|
||||
@property() label = '';
|
||||
|
||||
/** Disables the button. */
|
||||
@property({ type: Boolean }) disabled = false;
|
||||
|
||||
private handleClick(event: MouseEvent) {
|
||||
if (this.disabled) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
/** Simulates a click on the icon button. */
|
||||
click() {
|
||||
this.button.click();
|
||||
}
|
||||
|
||||
/** Sets focus on the icon button. */
|
||||
focus(options?: FocusOptions) {
|
||||
this.button.focus(options);
|
||||
}
|
||||
|
||||
/** Removes focus from the icon button. */
|
||||
blur() {
|
||||
this.button.blur();
|
||||
}
|
||||
|
||||
render() {
|
||||
const isLink = this.href ? true : false;
|
||||
const tag = isLink ? literal`a` : literal`button`;
|
||||
|
||||
/* eslint-disable lit/binding-positions, lit/no-invalid-html */
|
||||
return html`
|
||||
<${tag}
|
||||
part="base"
|
||||
class=${classMap({
|
||||
'icon-button': true,
|
||||
})}
|
||||
?disabled=${ifDefined(isLink ? undefined : this.disabled)}
|
||||
type=${ifDefined(isLink ? undefined : 'button')}
|
||||
href=${ifDefined(isLink ? this.href : undefined)}
|
||||
target=${ifDefined(isLink ? this.target : undefined)}
|
||||
download=${ifDefined(isLink ? this.download : undefined)}
|
||||
rel=${ifDefined(isLink && this.target ? 'noreferrer noopener' : undefined)}
|
||||
role=${ifDefined(isLink ? undefined : 'button')}
|
||||
aria-disabled=${this.disabled ? 'true' : 'false'}
|
||||
aria-label="${this.label}"
|
||||
tabindex=${this.disabled ? '-1' : '0'}
|
||||
@click=${this.handleClick}
|
||||
>
|
||||
<wa-icon
|
||||
class="icon"
|
||||
name=${ifDefined(this.name)}
|
||||
family=${ifDefined(this.family)}
|
||||
variant=${ifDefined(this.variant)}
|
||||
library=${ifDefined(this.library)}
|
||||
src=${ifDefined(this.src)}
|
||||
aria-hidden="true"
|
||||
fixed-width
|
||||
></wa-icon>
|
||||
</${tag}>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'wa-icon-button': WaIconButton;
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ interface IconSource {
|
||||
*/
|
||||
@customElement('wa-icon')
|
||||
export default class WaIcon extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
@state() private svg: SVGElement | HTMLTemplateResult | null = null;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user