mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-13 12:39:14 +00:00
Compare commits
12 Commits
data-actio
...
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
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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}]`;
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
|
||||
> input {
|
||||
font: inherit;
|
||||
margin-block: 0.75em;
|
||||
margin-block: calc(-1 * var(--wa-space-smaller));
|
||||
field-sizing: content;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>`;
|
||||
};
|
||||
|
||||
@@ -31,29 +31,14 @@ 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
|
||||
- Added a new free component: `<wa-popover>` (#2 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>`
|
||||
- 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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 = {}) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
@@ -182,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()];
|
||||
|
||||
@@ -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,7 +17,7 @@
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -6,7 +6,6 @@ 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';
|
||||
@@ -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');
|
||||
@@ -265,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';
|
||||
|
||||
@@ -6,7 +6,6 @@ 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';
|
||||
@@ -33,9 +32,9 @@ 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.
|
||||
@@ -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');
|
||||
@@ -281,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', () => {
|
||||
|
||||
@@ -44,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;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
padding: 0.5em;
|
||||
padding: var(--wa-space-xs);
|
||||
cursor: pointer;
|
||||
transition: color var(--wa-transition-fast) var(--wa-transition-easing);
|
||||
-webkit-appearance: none;
|
||||
|
||||
@@ -26,7 +26,7 @@ import styles from './icon-button.css';
|
||||
*/
|
||||
@customElement('wa-icon-button')
|
||||
export default class WaIconButton extends WebAwesomeFormAssociatedElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
@query('.icon-button') button: HTMLButtonElement | HTMLLinkElement;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ export const icons: { [key: string]: { [key: string]: string } } = {
|
||||
copy: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><path d="M384 336H192c-8.8 0-16-7.2-16-16V64c0-8.8 7.2-16 16-16l140.1 0L400 115.9V320c0 8.8-7.2 16-16 16zM192 384H384c35.3 0 64-28.7 64-64V115.9c0-12.7-5.1-24.9-14.1-33.9L366.1 14.1c-9-9-21.2-14.1-33.9-14.1H192c-35.3 0-64 28.7-64 64V320c0 35.3 28.7 64 64 64zM64 128c-35.3 0-64 28.7-64 64V448c0 35.3 28.7 64 64 64H256c35.3 0 64-28.7 64-64V416H272v32c0 8.8-7.2 16-16 16H64c-8.8 0-16-7.2-16-16V192c0-8.8 7.2-16 16-16H96V128H64z"/></svg>`,
|
||||
eye: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="18" viewBox="0 0 576 512"><path d="M288 80c-65.2 0-118.8 29.6-159.9 67.7C89.6 183.5 63 226 49.4 256c13.6 30 40.2 72.5 78.6 108.3C169.2 402.4 222.8 432 288 432s118.8-29.6 159.9-67.7C486.4 328.5 513 286 526.6 256c-13.6-30-40.2-72.5-78.6-108.3C406.8 109.6 353.2 80 288 80zM95.4 112.6C142.5 68.8 207.2 32 288 32s145.5 36.8 192.6 80.6c46.8 43.5 78.1 95.4 93 131.1c3.3 7.9 3.3 16.7 0 24.6c-14.9 35.7-46.2 87.7-93 131.1C433.5 443.2 368.8 480 288 480s-145.5-36.8-192.6-80.6C48.6 356 17.3 304 2.5 268.3c-3.3-7.9-3.3-16.7 0-24.6C17.3 208 48.6 156 95.4 112.6zM288 336c44.2 0 80-35.8 80-80s-35.8-80-80-80c-.7 0-1.3 0-2 0c1.3 5.1 2 10.5 2 16c0 35.3-28.7 64-64 64c-5.5 0-10.9-.7-16-2c0 .7 0 1.3 0 2c0 44.2 35.8 80 80 80zm0-208a128 128 0 1 1 0 256 128 128 0 1 1 0-256z"/></svg>`,
|
||||
'eye-slash': `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="20" viewBox="0 0 640 512"><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zm151 118.3C226 97.7 269.5 80 320 80c65.2 0 118.8 29.6 159.9 67.7C518.4 183.5 545 226 558.6 256c-12.6 28-36.6 66.8-70.9 100.9l-53.8-42.2c9.1-17.6 14.2-37.5 14.2-58.7c0-70.7-57.3-128-128-128c-32.2 0-61.7 11.9-84.2 31.5l-46.1-36.1zM394.9 284.2l-81.5-63.9c4.2-8.5 6.6-18.2 6.6-28.3c0-5.5-.7-10.9-2-16c.7 0 1.3 0 2 0c44.2 0 80 35.8 80 80c0 9.9-1.8 19.4-5.1 28.2zm51.3 163.3l-41.9-33C378.8 425.4 350.7 432 320 432c-65.2 0-118.8-29.6-159.9-67.7C121.6 328.5 95 286 81.4 256c8.3-18.4 21.5-41.5 39.4-64.8L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5zm-88-69.3L302 334c-23.5-5.4-43.1-21.2-53.7-42.3l-56.1-44.2c-.2 2.8-.3 5.6-.3 8.5c0 70.7 57.3 128 128 128c13.3 0 26.1-2 38.2-5.8z"/></svg>`,
|
||||
star: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M287.9 0c9.2 0 17.6 5.2 21.6 13.5l68.6 141.3 153.2 22.6c9 1.3 16.5 7.6 19.3 16.3s.5 18.1-5.9 24.5L433.6 328.4l26.2 155.6c1.5 9-2.2 18.1-9.7 23.5s-17.3 6-25.3 1.7l-137-73.2L151 509.1c-8.1 4.3-17.9 3.7-25.3-1.7s-11.2-14.5-9.7-23.5l26.2-155.6L31.1 218.2c-6.5-6.4-8.7-15.9-5.9-24.5s10.3-14.9 19.3-16.3l153.2-22.6L266.3 13.5C270.4 5.2 278.7 0 287.9 0zm0 79L235.4 187.2c-3.5 7.1-10.2 12.1-18.1 13.3L99 217.9 184.9 303c5.5 5.5 8.1 13.3 6.8 21L171.4 443.7l105.2-56.2c7.1-3.8 15.6-3.8 22.6 0l105.2 56.2L384.2 324.1c-1.3-7.7 1.2-15.5 6.8-21l85.9-85.1L358.6 200.5c-7.8-1.2-14.6-6.1-18.1-13.3L287.9 79z"/></svg>',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { requestInclude } from './request.js';
|
||||
*/
|
||||
@customElement('wa-include')
|
||||
export default class WaInclude extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
/**
|
||||
* The location of the HTML file to include. Be sure you trust the content you are including as it will be executed as
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
border-width: var(--border-width);
|
||||
cursor: text;
|
||||
color: var(--wa-form-control-value-color);
|
||||
font-size: var(--wa-form-control-value-font-size);
|
||||
font-size: var(--wa-size);
|
||||
font-family: inherit;
|
||||
font-weight: var(--wa-form-control-value-font-weight);
|
||||
line-height: var(--wa-form-control-value-line-height);
|
||||
@@ -35,7 +35,7 @@
|
||||
transition-timing-function: var(--wa-transition-easing);
|
||||
background-color: var(--background-color, var(--wa-form-control-background-color));
|
||||
box-shadow: var(--box-shadow);
|
||||
padding: 0 var(--wa-form-control-padding-inline);
|
||||
padding: var(--wa-space-smaller) var(--wa-space);
|
||||
|
||||
&:focus-within {
|
||||
outline: var(--wa-focus-ring);
|
||||
@@ -142,11 +142,11 @@ textarea {
|
||||
}
|
||||
|
||||
.prefix::slotted(*) {
|
||||
margin-inline-end: var(--wa-form-control-padding-inline);
|
||||
margin-inline-end: var(--wa-space);
|
||||
}
|
||||
|
||||
.suffix::slotted(*) {
|
||||
margin-inline-start: var(--wa-form-control-padding-inline);
|
||||
margin-inline-start: var(--wa-space);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -108,12 +108,12 @@ describe('<wa-input>', () => {
|
||||
const el = await fixture<WaInput>(html` <wa-input required value="a"></wa-input> `);
|
||||
|
||||
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;
|
||||
|
||||
el.focus();
|
||||
await el.updateComplete;
|
||||
@@ -123,19 +123,19 @@ describe('<wa-input>', () => {
|
||||
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 () => {
|
||||
const el = await fixture<WaInput>(html` <wa-input required></wa-input> `);
|
||||
|
||||
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;
|
||||
|
||||
el.focus();
|
||||
await el.updateComplete;
|
||||
@@ -145,20 +145,20 @@ describe('<wa-input>', () => {
|
||||
el.blur();
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.customStates.has('user-invalid')).to.be.true;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should receive validation attributes ("states") even when novalidate is used on the parent form', async () => {
|
||||
const el = await fixture<HTMLFormElement>(html` <form novalidate><wa-input required></wa-input></form> `);
|
||||
const input = el.querySelector<WaInput>('wa-input')!;
|
||||
|
||||
expect(input.customStates.has('required')).to.be.true;
|
||||
expect(input.customStates.has('optional')).to.be.false;
|
||||
expect(input.customStates.has('invalid')).to.be.true;
|
||||
expect(input.customStates.has('valid')).to.be.false;
|
||||
expect(input.customStates.has('user-invalid')).to.be.false;
|
||||
expect(input.customStates.has('user-valid')).to.be.false;
|
||||
expect(input.hasCustomState('required')).to.be.true;
|
||||
expect(input.hasCustomState('optional')).to.be.false;
|
||||
expect(input.hasCustomState('invalid')).to.be.true;
|
||||
expect(input.hasCustomState('valid')).to.be.false;
|
||||
expect(input.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(input.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -215,10 +215,10 @@ describe('<wa-input>', () => {
|
||||
await input.updateComplete;
|
||||
|
||||
expect(input.checkValidity()).to.be.false;
|
||||
expect(input.customStates.has('invalid')).to.be.true;
|
||||
expect(input.customStates.has('valid')).to.be.false;
|
||||
expect(input.customStates.has('user-invalid')).to.be.false;
|
||||
expect(input.customStates.has('user-valid')).to.be.false;
|
||||
expect(input.hasCustomState('invalid')).to.be.true;
|
||||
expect(input.hasCustomState('valid')).to.be.false;
|
||||
expect(input.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(input.hasCustomState('user-valid')).to.be.false;
|
||||
|
||||
input.focus();
|
||||
await sendKeys({ type: 'test' });
|
||||
@@ -226,8 +226,8 @@ describe('<wa-input>', () => {
|
||||
input.blur();
|
||||
await input.updateComplete;
|
||||
|
||||
expect(input.customStates.has('user-invalid')).to.be.true;
|
||||
expect(input.customStates.has('user-valid')).to.be.false;
|
||||
expect(input.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(input.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should be present in form data when using the form attribute and located outside of a <form>', async () => {
|
||||
|
||||
@@ -57,7 +57,7 @@ import styles from './input.css';
|
||||
*/
|
||||
@customElement('wa-input')
|
||||
export default class WaInput extends WebAwesomeFormAssociatedElement {
|
||||
static css = [sizeStyles, appearanceStyles, formControlStyles, styles];
|
||||
static shadowStyle = [sizeStyles, appearanceStyles, formControlStyles, styles];
|
||||
|
||||
static shadowRootOptions = { ...WebAwesomeFormAssociatedElement.shadowRootOptions, delegatesFocus: true };
|
||||
|
||||
@@ -300,7 +300,7 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has('value')) {
|
||||
this.customStates.set('blank', !this.value);
|
||||
this.toggleCustomState('blank', !this.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
font: inherit;
|
||||
padding: 0.5em 0.25em;
|
||||
padding: var(--wa-space-xs) var(--wa-space-2xs);
|
||||
line-height: var(--wa-line-height-condensed);
|
||||
transition: fill var(--wa-transition-normal) var(--wa-transition-easing);
|
||||
user-select: none;
|
||||
@@ -39,11 +39,11 @@
|
||||
|
||||
:host([loading]) wa-spinner {
|
||||
--indicator-color: currentColor;
|
||||
--track-width: round(0.0625em, 1px);
|
||||
--track-width: 0.0625rem;
|
||||
position: absolute;
|
||||
font-size: var(--wa-font-size-smaller);
|
||||
font-size: 0.8em;
|
||||
top: calc(50% - 0.5em);
|
||||
left: 0.6em;
|
||||
left: 0.5rem;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
}
|
||||
|
||||
.prefix::slotted(*) {
|
||||
margin-inline-end: 0.5em;
|
||||
margin-inline-end: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
.suffix {
|
||||
@@ -71,7 +71,7 @@
|
||||
}
|
||||
|
||||
.suffix::slotted(*) {
|
||||
margin-inline-start: 0.5em;
|
||||
margin-inline-start: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
/* Safe triangle */
|
||||
@@ -114,8 +114,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: var(--wa-font-size-smaller);
|
||||
width: 2em;
|
||||
font-size: 0.875em;
|
||||
width: var(--wa-space-xl);
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ import { SubmenuController } from './submenu-controller.js';
|
||||
*/
|
||||
@customElement('wa-menu-item')
|
||||
export default class WaMenuItem extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@@ -133,7 +133,7 @@ export default class WaMenuItem extends WebAwesomeElement {
|
||||
this.dispatchEvent(new Event('slotchange', { bubbles: true, composed: false, cancelable: false }));
|
||||
}
|
||||
|
||||
this.customStates.set('has-submenu', this.isSubmenu());
|
||||
this.toggleCustomState('has-submenu', this.isSubmenu());
|
||||
}
|
||||
|
||||
private handleHostClick = (event: MouseEvent) => {
|
||||
@@ -201,7 +201,7 @@ export default class WaMenuItem extends WebAwesomeElement {
|
||||
render() {
|
||||
const isRtl = this.hasUpdated ? this.localize.dir() === 'rtl' : this.dir === 'rtl';
|
||||
const isSubmenuExpanded = this.submenuController.isExpanded();
|
||||
this.customStates.set('submenu-expanded', isSubmenuExpanded);
|
||||
this.toggleCustomState('submenu-expanded', isSubmenuExpanded);
|
||||
|
||||
this.internals.ariaHasPopup = this.isSubmenu() + '';
|
||||
this.internals.ariaExpanded = isSubmenuExpanded + '';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
:host {
|
||||
display: block;
|
||||
color: var(--wa-color-text-quiet);
|
||||
font-size: var(--wa-font-size-smaller);
|
||||
font-size: var(--wa-font-size-s);
|
||||
font-weight: var(--wa-font-weight-semibold);
|
||||
padding: 0.5em 2.25em;
|
||||
padding: var(--wa-space-2xs) calc(var(--wa-space-2xs) + var(--wa-space-xl));
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import styles from './menu-label.css';
|
||||
*/
|
||||
@customElement('wa-menu-label')
|
||||
export default class WaMenuLabel extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
render() {
|
||||
return html`<slot></slot>`;
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
background-color: var(--wa-color-surface-raised);
|
||||
border: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
padding: 0.5em 0;
|
||||
padding: var(--wa-space-xs) 0;
|
||||
overflow: auto;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
::slotted(wa-divider) {
|
||||
--spacing: 0.5em;
|
||||
--spacing: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface MenuSelectEventDetail {
|
||||
*/
|
||||
@customElement('wa-menu')
|
||||
export default class WaMenu extends WebAwesomeElement {
|
||||
static css = [sizeStyles, styles];
|
||||
static shadowStyle = [sizeStyles, styles];
|
||||
|
||||
/** The component's size. */
|
||||
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
|
||||
|
||||
@@ -17,7 +17,7 @@ import styles from './mutation-observer.css';
|
||||
*/
|
||||
@customElement('wa-mutation-observer')
|
||||
export default class WaMutationObserver extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private mutationObserver: MutationObserver;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font: inherit;
|
||||
padding: 0.5em 1em 0.5em 0.25em;
|
||||
padding: var(--wa-space-xs) var(--wa-space-m) var(--wa-space-xs) var(--wa-space-2xs);
|
||||
line-height: var(--wa-line-height-condensed);
|
||||
transition: fill var(--wa-transition-normal) var(--wa-transition-easing);
|
||||
cursor: pointer;
|
||||
@@ -51,9 +51,9 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: var(--wa-font-size-smaller);
|
||||
font-size: 0.875em;
|
||||
visibility: hidden;
|
||||
width: 2em;
|
||||
width: var(--wa-space-xl);
|
||||
}
|
||||
|
||||
:host(:state(selected)) .check {
|
||||
@@ -68,11 +68,11 @@
|
||||
}
|
||||
|
||||
.prefix::slotted(*) {
|
||||
margin-inline-end: 0.5em;
|
||||
margin-inline-end: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
.suffix::slotted(*) {
|
||||
margin-inline-start: 0.5em;
|
||||
margin-inline-start: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
|
||||
@@ -35,7 +35,7 @@ import styles from './option.css';
|
||||
*/
|
||||
@customElement('wa-option')
|
||||
export default class WaOption extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
// @ts-expect-error - Controller is currently unused
|
||||
private readonly localize = new LocalizeController(this);
|
||||
@@ -130,9 +130,9 @@ export default class WaOption extends WebAwesomeElement {
|
||||
// We need this because Safari doesn't honor :hover styles while dragging
|
||||
// Test case: https://codepen.io/leaverou/pen/VYZOOjy
|
||||
if (event.type === 'mouseenter') {
|
||||
this.customStates.set('hover', true);
|
||||
this.toggleCustomState('hover', true);
|
||||
} else if (event.type === 'mouseleave') {
|
||||
this.customStates.set('hover', false);
|
||||
this.toggleCustomState('hover', false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,7 +145,7 @@ export default class WaOption extends WebAwesomeElement {
|
||||
|
||||
if (changedProperties.has('selected')) {
|
||||
this.setAttribute('aria-selected', this.selected ? 'true' : 'false');
|
||||
this.customStates.set('selected', this.selected);
|
||||
this.toggleCustomState('selected', this.selected);
|
||||
}
|
||||
|
||||
if (changedProperties.has('value')) {
|
||||
@@ -165,7 +165,7 @@ export default class WaOption extends WebAwesomeElement {
|
||||
}
|
||||
|
||||
if (changedProperties.has('current')) {
|
||||
this.customStates.set('current', this.current);
|
||||
this.toggleCustomState('current', this.current);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ function toLength(px: number | string): string {
|
||||
*/
|
||||
@customElement('wa-page')
|
||||
export default class WaPage extends WebAwesomeElement {
|
||||
static css = [visuallyHidden, styles];
|
||||
static shadowStyle = [visuallyHidden, styles];
|
||||
|
||||
private headerResizeObserver = this.slotResizeObserver('header');
|
||||
private subheaderResizeObserver = this.slotResizeObserver('subheader');
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
flex-direction: column;
|
||||
width: max-content;
|
||||
max-width: var(--max-width);
|
||||
padding: var(--wa-space-l);
|
||||
padding: var(--wa-space);
|
||||
background-color: var(--wa-color-surface-default);
|
||||
border: var(--wa-panel-border-width) solid var(--wa-color-surface-border);
|
||||
border-radius: var(--wa-panel-border-radius);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { PropertyValues } from 'lit';
|
||||
import { html } from 'lit';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
@@ -41,12 +40,10 @@ const openPopovers = new Set<WaPopover>();
|
||||
* @cssproperty [--max-width=25rem] - The maximum width of the popover's body content.
|
||||
* @cssproperty [--show-duration=100ms] - The speed of the show animation.
|
||||
* @cssproperty [--hide-duration=100ms] - The speed of the hide animation.
|
||||
*
|
||||
* @cssstate open - Applied when the popover is open.
|
||||
*/
|
||||
@customElement('wa-popover')
|
||||
export default class WaPopover extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
static dependencies = { 'wa-popup': WaPopup };
|
||||
|
||||
@query('dialog') dialog: HTMLDialogElement;
|
||||
@@ -113,12 +110,6 @@ export default class WaPopover extends WebAwesomeElement {
|
||||
}
|
||||
}
|
||||
|
||||
updated(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has('open')) {
|
||||
this.customStates.set('open', this.open);
|
||||
}
|
||||
}
|
||||
|
||||
private handleAnchorClick = () => {
|
||||
// Clicks on the anchor should toggle the popover
|
||||
this.open = !this.open;
|
||||
|
||||
@@ -68,7 +68,7 @@ const SUPPORTS_POPOVER = globalThis?.HTMLElement?.prototype.hasOwnProperty('popo
|
||||
*/
|
||||
@customElement('wa-popup')
|
||||
export default class WaPopup extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private anchorEl: Element | VirtualElement | null;
|
||||
private cleanup: ReturnType<typeof autoUpdate> | undefined;
|
||||
|
||||
@@ -24,7 +24,7 @@ import styles from './progress-bar.css';
|
||||
*/
|
||||
@customElement('wa-progress-bar')
|
||||
export default class WaProgressBar extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
/** The current progress as a percentage, 0 to 100. */
|
||||
|
||||
@@ -25,7 +25,7 @@ import styles from './progress-ring.css';
|
||||
*/
|
||||
@customElement('wa-progress-ring')
|
||||
export default class WaProgressRing extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ let QrCreator: _QrCreator.default;
|
||||
*/
|
||||
@customElement('wa-qr-code')
|
||||
export default class WaQrCode extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
@query('canvas') canvas: HTMLElement;
|
||||
|
||||
|
||||
@@ -26,18 +26,18 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75em;
|
||||
gap: var(--wa-space-s);
|
||||
}
|
||||
|
||||
/* Horizontal */
|
||||
:host([orientation='horizontal']) [part~='form-control-input'] {
|
||||
flex-direction: row;
|
||||
gap: 1em;
|
||||
gap: var(--wa-space-m);
|
||||
}
|
||||
|
||||
/* Help text */
|
||||
[part~='hint'] {
|
||||
margin-block-start: 0.5em;
|
||||
margin-block-start: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
/* Radios have the "button" appearance */
|
||||
|
||||
@@ -2,7 +2,7 @@ import { aTimeout, expect, oneEvent } from '@open-wc/testing';
|
||||
import { sendKeys } from '@web/test-runner-commands';
|
||||
import { html } from 'lit';
|
||||
import sinon from 'sinon';
|
||||
// import { clickOnElement } from '../../internal/test.js';
|
||||
import { clickOnElement } from '../../internal/test.js';
|
||||
import { fixtures } from '../../internal/test/fixture.js';
|
||||
import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests.js';
|
||||
import type WaRadio from '../radio/radio.js';
|
||||
@@ -99,22 +99,19 @@ describe('<wa-radio-group>', () => {
|
||||
const secondRadio = radioGroup.querySelectorAll('wa-radio')[1];
|
||||
|
||||
expect(radioGroup.checkValidity()).to.be.true;
|
||||
expect(radioGroup.customStates.has('required')).to.be.true;
|
||||
expect(radioGroup.customStates.has('optional')).to.be.false;
|
||||
expect(radioGroup.customStates.has('invalid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('valid')).to.be.true;
|
||||
expect(radioGroup.customStates.has('user-invalid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-valid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('required')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('optional')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('invalid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('valid')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-valid')).to.be.false;
|
||||
|
||||
// TODO: Go back to clickOnElement when we can determine why CI is not cleaning up elements.
|
||||
// await clickOnElement(secondRadio);
|
||||
secondRadio.click();
|
||||
await clickOnElement(secondRadio);
|
||||
await secondRadio.updateComplete;
|
||||
await radioGroup.updateComplete;
|
||||
|
||||
expect(radioGroup.checkValidity()).to.be.true;
|
||||
expect(radioGroup.customStates.has('user-invalid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-valid')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-valid')).to.be.true;
|
||||
});
|
||||
|
||||
it('should receive the correct validation attributes ("states") when invalid', async () => {
|
||||
@@ -126,22 +123,19 @@ describe('<wa-radio-group>', () => {
|
||||
`);
|
||||
const secondRadio = radioGroup.querySelectorAll('wa-radio')[1];
|
||||
|
||||
expect(radioGroup.customStates.has('required')).to.be.true;
|
||||
expect(radioGroup.customStates.has('optional')).to.be.false;
|
||||
expect(radioGroup.customStates.has('invalid')).to.be.true;
|
||||
expect(radioGroup.customStates.has('valid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-invalid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-valid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('required')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('optional')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('invalid')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('valid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-valid')).to.be.false;
|
||||
|
||||
// TODO: Go back to clickOnElement when we can determine why CI is not cleaning up elements.
|
||||
// await clickOnElement(secondRadio);
|
||||
secondRadio.click();
|
||||
await radioGroup.updateComplete;
|
||||
await clickOnElement(secondRadio);
|
||||
radioGroup.value = '';
|
||||
await radioGroup.updateComplete;
|
||||
|
||||
expect(radioGroup.customStates.has('user-invalid')).to.be.true;
|
||||
expect(radioGroup.customStates.has('user-valid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should receive validation attributes ("states") even when novalidate is used on the parent form', async () => {
|
||||
@@ -155,12 +149,12 @@ describe('<wa-radio-group>', () => {
|
||||
`);
|
||||
const radioGroup = el.querySelector<WaRadioGroup>('wa-radio-group')!;
|
||||
|
||||
expect(radioGroup.customStates.has('required')).to.be.true;
|
||||
expect(radioGroup.customStates.has('optional')).to.be.false;
|
||||
expect(radioGroup.customStates.has('invalid')).to.be.true;
|
||||
expect(radioGroup.customStates.has('valid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-invalid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-valid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('required')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('optional')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('invalid')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('valid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should show a constraint validation error when setCustomValidity() is called', async () => {
|
||||
|
||||
@@ -37,7 +37,7 @@ import styles from './radio-group.css';
|
||||
*/
|
||||
@customElement('wa-radio-group')
|
||||
export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
|
||||
static css = [sizeStyles, formControlStyles, styles];
|
||||
static shadowStyle = [sizeStyles, formControlStyles, styles];
|
||||
|
||||
static get validators() {
|
||||
const validators = isServer
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
--border-width: var(--wa-form-control-border-width);
|
||||
--box-shadow: none;
|
||||
--checked-icon-color: var(--wa-form-control-activated-color);
|
||||
--checked-icon-scale: 0.7;
|
||||
--toggle-size: var(--wa-form-control-toggle-size);
|
||||
--checked-icon-scale: 0.75;
|
||||
--toggle-size: round(1lh, 1px);
|
||||
|
||||
color: var(--wa-form-control-value-color);
|
||||
display: inline-flex;
|
||||
@@ -36,7 +36,7 @@
|
||||
}
|
||||
|
||||
[part~='hint'] {
|
||||
margin-block-start: 0.5em;
|
||||
margin-block-start: var(--wa-space-3xs);
|
||||
}
|
||||
|
||||
/* Default appearance */
|
||||
@@ -63,7 +63,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);
|
||||
}
|
||||
|
||||
.checked-icon {
|
||||
@@ -101,7 +101,7 @@
|
||||
background-color: var(--wa-color-surface-default);
|
||||
border: var(--border-width) var(--border-style) var(--wa-form-control-border-color);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
padding: 0 var(--wa-form-control-padding-inline);
|
||||
padding: 0 var(--wa-space);
|
||||
transition:
|
||||
background-color var(--wa-transition-fast),
|
||||
border-color var(--wa-transition-fast);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { PropertyValues } from 'lit';
|
||||
import { html, isServer } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
|
||||
import formControlStyles from '../../styles/component/form-control.css';
|
||||
import sizeStyles from '../../styles/utilities/size.css';
|
||||
@@ -16,6 +18,7 @@ import styles from './radio.css';
|
||||
* @dependency wa-icon
|
||||
*
|
||||
* @slot - The radio's label.
|
||||
* @slot hint - Text that describes how to use the checkbox. Alternatively, you can use the `hint` attribute.
|
||||
*
|
||||
* @event blur - Emitted when the control loses focus.
|
||||
* @event focus - Emitted when the control gains focus.
|
||||
@@ -23,6 +26,7 @@ import styles from './radio.css';
|
||||
* @csspart control - The circular container that wraps the radio's checked state.
|
||||
* @csspart checked-icon - The checked icon.
|
||||
* @csspart label - The container that wraps the radio's label.
|
||||
* @csspart hint - The hint's wrapper.
|
||||
*
|
||||
* @cssproperty --background-color - The radio's background color.
|
||||
* @cssproperty --background-color-checked - The radio's background color when checked.
|
||||
@@ -40,7 +44,7 @@ import styles from './radio.css';
|
||||
*/
|
||||
@customElement('wa-radio')
|
||||
export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
static css = [formControlStyles, sizeStyles, styles];
|
||||
static shadowStyle = [formControlStyles, sizeStyles, styles];
|
||||
|
||||
@state() checked = false;
|
||||
|
||||
@@ -64,6 +68,11 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
/** Disables the radio. */
|
||||
@property({ type: Boolean }) disabled = false;
|
||||
|
||||
/** The radio's hint. If you need to display HTML, use the `hint` slot instead. */
|
||||
@property() hint = '';
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'hint');
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
if (!isServer) {
|
||||
@@ -86,13 +95,13 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has('checked')) {
|
||||
this.customStates.set('checked', this.checked);
|
||||
this.toggleCustomState('checked', this.checked);
|
||||
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
|
||||
this.tabIndex = this.checked ? 0 : -1;
|
||||
}
|
||||
|
||||
if (changedProperties.has('disabled')) {
|
||||
this.customStates.set('disabled', this.disabled);
|
||||
this.toggleCustomState('disabled', this.disabled);
|
||||
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
|
||||
}
|
||||
}
|
||||
@@ -111,6 +120,9 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
};
|
||||
|
||||
render() {
|
||||
const hasHintSlot = isServer ? true : this.hasSlotController.test('hint');
|
||||
const hasHint = this.hint ? true : !!hasHintSlot;
|
||||
|
||||
return html`
|
||||
<span part="control" class="control">
|
||||
${this.checked
|
||||
@@ -123,6 +135,15 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
</span>
|
||||
|
||||
<slot part="label" class="label"></slot>
|
||||
|
||||
<slot
|
||||
name="hint"
|
||||
aria-hidden=${hasHint ? 'false' : 'true'}
|
||||
class="${classMap({ 'has-slotted': hasHint })}"
|
||||
id="hint"
|
||||
part="hint"
|
||||
>${this.hint}</slot
|
||||
>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
:host {
|
||||
--symbol-color: var(--wa-color-neutral-on-quiet);
|
||||
--size-xs: var(--wa-space-s);
|
||||
--size-s: var(--wa-space-m);
|
||||
--size-m: var(--wa-space-l);
|
||||
--size-l: var(--wa-space-xl);
|
||||
|
||||
--symbol-color: var(--wa-color-neutral-fill-normal);
|
||||
--symbol-color-active: var(--wa-color-yellow-70);
|
||||
--symbol-spacing: 0.125em;
|
||||
--symbol-size: var(--wa-size);
|
||||
--symbol-spacing: var(--wa-space-3xs);
|
||||
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import styles from './rating.css';
|
||||
*/
|
||||
@customElement('wa-rating')
|
||||
export default class WaRating extends WebAwesomeElement {
|
||||
static css = [sizeStyles, styles];
|
||||
static shadowStyle = [sizeStyles, styles];
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@@ -68,11 +68,8 @@ export default class WaRating extends WebAwesomeElement {
|
||||
* The function should return a string containing trusted HTML of the symbol to render at the specified value. Works
|
||||
* well with `<wa-icon>` elements.
|
||||
*/
|
||||
@property() getSymbol: (value: number, isSelected: boolean) => string = (_value, isSelected) => {
|
||||
return isSelected
|
||||
? '<wa-icon name="star" library="system" variant="solid"></wa-icon>'
|
||||
: '<wa-icon name="star" library="system" variant="regular"></wa-icon>';
|
||||
};
|
||||
@property() getSymbol: (value: number) => string = () =>
|
||||
'<wa-icon name="star" library="system" variant="solid"></wa-icon>';
|
||||
|
||||
/** The component's size. */
|
||||
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
|
||||
@@ -257,8 +254,6 @@ export default class WaRating extends WebAwesomeElement {
|
||||
>
|
||||
<span class="symbols">
|
||||
${counter.map(index => {
|
||||
const isSelected = displayValue >= index + 1;
|
||||
|
||||
if (displayValue > index && displayValue < index + 1) {
|
||||
// Users can click the current value to clear the rating. When this happens, we set this.isHovering to
|
||||
// false to prevent the hover state from confusing them as they move the mouse out of the control. This
|
||||
@@ -279,7 +274,7 @@ export default class WaRating extends WebAwesomeElement {
|
||||
: `inset(0 0 0 ${(displayValue - index) * 100}%)`,
|
||||
})}
|
||||
>
|
||||
${unsafeHTML(this.getSymbol(index + 1, false))}
|
||||
${unsafeHTML(this.getSymbol(index + 1))}
|
||||
</div>
|
||||
<div
|
||||
class="partial-filled"
|
||||
@@ -289,7 +284,7 @@ export default class WaRating extends WebAwesomeElement {
|
||||
: `inset(0 ${100 - (displayValue - index) * 100}% 0 0)`,
|
||||
})}
|
||||
>
|
||||
${unsafeHTML(this.getSymbol(index + 1, true))}
|
||||
${unsafeHTML(this.getSymbol(index + 1))}
|
||||
</div>
|
||||
</span>
|
||||
`;
|
||||
@@ -304,7 +299,7 @@ export default class WaRating extends WebAwesomeElement {
|
||||
})}
|
||||
role="presentation"
|
||||
>
|
||||
${unsafeHTML(this.getSymbol(index + 1, isSelected))}
|
||||
${unsafeHTML(this.getSymbol(index + 1))}
|
||||
</span>
|
||||
`;
|
||||
})}
|
||||
|
||||
@@ -17,7 +17,7 @@ import styles from './resize-observer.css';
|
||||
*/
|
||||
@customElement('wa-resize-observer')
|
||||
export default class WaResizeObserver extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private resizeObserver: ResizeObserver;
|
||||
private observedElements: HTMLElement[] = [];
|
||||
|
||||
@@ -20,7 +20,7 @@ import styles from './scroller.css';
|
||||
*/
|
||||
@customElement('wa-scroller')
|
||||
export default class WaScroller extends WebAwesomeElement {
|
||||
static css = [styles];
|
||||
static shadowStyle = [styles];
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private resizeObserver = new ResizeObserver(() => this.updateScroll());
|
||||
|
||||
@@ -6,7 +6,32 @@ label:has(select),
|
||||
--outlined-text-color: var(--wa-form-control-value-color);
|
||||
--border-width: var(--wa-form-control-border-width);
|
||||
--box-shadow: initial;
|
||||
--tag-max-size: 10ch;
|
||||
}
|
||||
|
||||
:host [part~='combobox'] {
|
||||
background-color: var(--background-color, var(--wa-form-control-background-color));
|
||||
border-color: var(--border-color, var(--wa-form-control-border-color));
|
||||
border-radius: var(--wa-form-control-border-radius);
|
||||
border-style: var(--wa-form-control-border-style);
|
||||
border-width: var(--border-width);
|
||||
box-shadow: var(--box-shadow);
|
||||
color: var(--wa-form-control-value-color);
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-size: var(--wa-size);
|
||||
font-weight: var(--wa-form-control-value-font-weight);
|
||||
line-height: var(--wa-form-control-value-line-height);
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
padding: var(--wa-space-smaller) var(--wa-space);
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
transition:
|
||||
background-color var(--wa-transition-normal),
|
||||
border var(--wa-transition-normal),
|
||||
outline var(--wa-transition-fast);
|
||||
transition-timing-function: var(--wa-transition-easing);
|
||||
}
|
||||
|
||||
/* Add ellipses to multi select options */
|
||||
@@ -15,7 +40,7 @@ label:has(select),
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
max-width: var(--tag-max-size);
|
||||
max-width: 7ch;
|
||||
}
|
||||
|
||||
:host .disabled [part~='combobox'] {
|
||||
@@ -61,31 +86,21 @@ label:has(select),
|
||||
|
||||
min-height: var(--wa-form-control-height);
|
||||
|
||||
background-color: var(--background-color, var(--wa-form-control-background-color));
|
||||
border-color: var(--border-color, var(--wa-form-control-border-color));
|
||||
border-radius: var(--wa-form-control-border-radius);
|
||||
border-style: var(--wa-form-control-border-style);
|
||||
border-width: var(--border-width);
|
||||
box-shadow: var(--box-shadow);
|
||||
color: var(--wa-form-control-value-color);
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-weight: var(--wa-form-control-value-font-weight);
|
||||
line-height: var(--wa-form-control-value-line-height);
|
||||
overflow: hidden;
|
||||
padding: 0 var(--wa-form-control-padding-inline);
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
transition:
|
||||
background-color var(--wa-transition-normal),
|
||||
border var(--wa-transition-normal),
|
||||
outline var(--wa-transition-fast);
|
||||
transition-timing-function: var(--wa-transition-easing);
|
||||
:host([size='small']) & {
|
||||
&:not(.placeholder-visible *) {
|
||||
padding-block: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
:host([multiple]) .select:not(.placeholder-visible) & {
|
||||
:host([size='large']) & {
|
||||
&:not(.placeholder-visible *) {
|
||||
padding-block: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
:host([multiple]) .select:not(.placeholder-visible) {
|
||||
padding-inline-start: 0;
|
||||
padding-block: calc(var(--wa-form-control-height) * 0.1 - var(--wa-form-control-border-width));
|
||||
padding-block: 3px;
|
||||
}
|
||||
|
||||
/* Pills */
|
||||
@@ -145,8 +160,16 @@ label:has(select),
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-inline-start: 0.25em;
|
||||
gap: 0.25em;
|
||||
margin-inline-start: var(--wa-space-2xs);
|
||||
gap: 3px;
|
||||
|
||||
:host([size='small']) & {
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
:host([size='large']) & {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
&::slotted(wa-tag) {
|
||||
cursor: pointer !important;
|
||||
@@ -169,15 +192,15 @@ label:has(select),
|
||||
}
|
||||
|
||||
.suffix::slotted(*) {
|
||||
margin-inline-start: var(--wa-form-control-padding-inline);
|
||||
margin-inline-start: var(--wa-space-s);
|
||||
}
|
||||
|
||||
.prefix::slotted(*) {
|
||||
margin-inline-end: var(--wa-form-control-padding-inline);
|
||||
margin-inline-end: var(--wa-space);
|
||||
}
|
||||
|
||||
:host([multiple]) .prefix::slotted(*) {
|
||||
margin-inline: var(--wa-form-control-padding-inline);
|
||||
margin-inline: var(--wa-space);
|
||||
}
|
||||
|
||||
/* Clear button */
|
||||
@@ -192,7 +215,7 @@ label:has(select),
|
||||
padding: 0;
|
||||
transition: color var(--wa-transition-normal);
|
||||
cursor: pointer;
|
||||
margin-inline-start: var(--wa-form-control-padding-inline);
|
||||
margin-inline-start: var(--wa-space);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
@@ -215,7 +238,7 @@ label:has(select),
|
||||
color: var(--wa-color-neutral-on-quiet);
|
||||
transition: rotate var(--wa-transition-slow) ease;
|
||||
rotate: 0deg;
|
||||
margin-inline-start: var(--wa-form-control-padding-inline);
|
||||
margin-inline-start: var(--wa-space-s);
|
||||
|
||||
.open & {
|
||||
rotate: -180deg;
|
||||
@@ -233,7 +256,7 @@ label:has(select),
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
border-style: var(--wa-border-style);
|
||||
border-width: var(--border-width);
|
||||
padding-block: 0.5em;
|
||||
padding-block: var(--wa-space-xs);
|
||||
padding-inline: 0;
|
||||
overflow: auto;
|
||||
overscroll-behavior: none;
|
||||
@@ -243,15 +266,15 @@ label:has(select),
|
||||
max-height: var(--auto-size-available-height);
|
||||
|
||||
&::slotted(wa-divider) {
|
||||
--spacing: 0.5em;
|
||||
--spacing: var(--wa-space-xs);
|
||||
}
|
||||
}
|
||||
|
||||
slot:not([name])::slotted(small) {
|
||||
display: block;
|
||||
font-size: var(--wa-font-size-smaller);
|
||||
font-size: var(--wa-font-size-s);
|
||||
font-weight: var(--wa-font-weight-semibold);
|
||||
color: var(--wa-color-text-quiet);
|
||||
padding-block: 0.5em;
|
||||
padding-inline: 2.25em;
|
||||
padding-block: var(--wa-space-xs);
|
||||
padding-inline: var(--wa-space-xl);
|
||||
}
|
||||
|
||||
@@ -331,12 +331,12 @@ describe('<wa-select>', () => {
|
||||
const secondOption = el.querySelectorAll('wa-option')[1];
|
||||
|
||||
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 el.show();
|
||||
await clickOnElement(secondOption);
|
||||
@@ -345,8 +345,8 @@ describe('<wa-select>', () => {
|
||||
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 () => {
|
||||
@@ -359,12 +359,12 @@ describe('<wa-select>', () => {
|
||||
`);
|
||||
const secondOption = el.querySelectorAll('wa-option')[1];
|
||||
|
||||
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 el.show();
|
||||
await clickOnElement(secondOption);
|
||||
@@ -373,8 +373,8 @@ describe('<wa-select>', () => {
|
||||
el.blur();
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.customStates.has('user-invalid')).to.be.true;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should receive validation attributes ("states") even when novalidate is used on the parent form', async () => {
|
||||
@@ -389,12 +389,12 @@ describe('<wa-select>', () => {
|
||||
`);
|
||||
const select = el.querySelector<WaSelect>('wa-select')!;
|
||||
|
||||
expect(select.customStates.has('required')).to.be.true;
|
||||
expect(select.customStates.has('optional')).to.be.false;
|
||||
expect(select.customStates.has('invalid')).to.be.true;
|
||||
expect(select.customStates.has('valid')).to.be.false;
|
||||
expect(select.customStates.has('user-invalid')).to.be.false;
|
||||
expect(select.customStates.has('user-valid')).to.be.false;
|
||||
expect(select.hasCustomState('required')).to.be.true;
|
||||
expect(select.hasCustomState('optional')).to.be.false;
|
||||
expect(select.hasCustomState('invalid')).to.be.true;
|
||||
expect(select.hasCustomState('valid')).to.be.false;
|
||||
expect(select.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(select.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -79,13 +79,12 @@ import styles from './select.css';
|
||||
* @cssproperty --border-color - The border color of the select's combobox.
|
||||
* @cssproperty --border-width - The width of the select's borders, including the listbox.
|
||||
* @cssproperty --box-shadow - The shadow effects around the edges of the select's combobox.
|
||||
* @cssproperty [--tag-max-size=10ch] - When using `multiple`, the max size of tags before their content is truncated.
|
||||
*
|
||||
* @cssstate blank - The select is empty.
|
||||
*/
|
||||
@customElement('wa-select')
|
||||
export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
static css = [appearanceStyles, formControlStyles, sizeStyles, styles];
|
||||
static shadowStyle = [appearanceStyles, formControlStyles, sizeStyles, styles];
|
||||
|
||||
static get validators() {
|
||||
const validators = isServer
|
||||
@@ -740,7 +739,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has('value')) {
|
||||
this.customStates.set('blank', !this.value);
|
||||
this.toggleCustomState('blank', !this.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import styles from './skeleton.css';
|
||||
*/
|
||||
@customElement('wa-skeleton')
|
||||
export default class WaSkeleton extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
/** Determines which effect the skeleton will use. */
|
||||
@property({ reflect: true }) effect: 'pulse' | 'sheen' | 'none' = 'none';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
--thumb-color: var(--wa-form-control-activated-color);
|
||||
--thumb-gap: calc(var(--thumb-size) * 0.125);
|
||||
--thumb-shadow: initial;
|
||||
--thumb-size: calc(1em * var(--wa-form-control-value-line-height));
|
||||
--thumb-size: calc(1rem * var(--wa-form-control-value-line-height));
|
||||
|
||||
--track-color-active: var(--wa-color-neutral-fill-normal);
|
||||
--track-color-inactive: var(--wa-color-neutral-fill-normal);
|
||||
@@ -22,7 +22,6 @@ input[type='range'] {
|
||||
border-radius: calc(var(--track-height) / 2);
|
||||
width: 100%;
|
||||
height: var(--track-height);
|
||||
font-size: inherit;
|
||||
line-height: var(--wa-form-control-height);
|
||||
vertical-align: middle;
|
||||
margin: 0;
|
||||
@@ -153,7 +152,7 @@ input[type='range']:focus {
|
||||
line-height: var(--wa-tooltip-line-height);
|
||||
color: var(--wa-tooltip-content-color);
|
||||
opacity: 0;
|
||||
padding: 0.25em 0.5em;
|
||||
padding: var(--wa-space-2xs) var(--wa-space-xs);
|
||||
transition: var(--wa-transition-normal) opacity;
|
||||
pointer-events: none;
|
||||
|
||||
|
||||
@@ -133,18 +133,18 @@ describe('<wa-slider>', () => {
|
||||
await slider.updateComplete;
|
||||
|
||||
expect(slider.checkValidity()).to.be.false;
|
||||
expect(slider.customStates.has('invalid')).to.be.true;
|
||||
expect(slider.customStates.has('valid')).to.be.false;
|
||||
expect(slider.customStates.has('user-invalid')).to.be.false;
|
||||
expect(slider.customStates.has('user-valid')).to.be.false;
|
||||
expect(slider.hasCustomState('invalid')).to.be.true;
|
||||
expect(slider.hasCustomState('valid')).to.be.false;
|
||||
expect(slider.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(slider.hasCustomState('user-valid')).to.be.false;
|
||||
|
||||
await clickOnElement(slider);
|
||||
await slider.updateComplete;
|
||||
slider.blur();
|
||||
await slider.updateComplete;
|
||||
|
||||
expect(slider.customStates.has('user-invalid')).to.be.true;
|
||||
expect(slider.customStates.has('user-valid')).to.be.false;
|
||||
expect(slider.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(slider.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should receive validation attributes ("states") even when novalidate is used on the parent form', async () => {
|
||||
@@ -154,10 +154,10 @@ describe('<wa-slider>', () => {
|
||||
slider.setCustomValidity('Invalid value');
|
||||
await slider.updateComplete;
|
||||
|
||||
expect(slider.customStates.has('invalid')).to.be.true;
|
||||
expect(slider.customStates.has('valid')).to.be.false;
|
||||
expect(slider.customStates.has('user-invalid')).to.be.false;
|
||||
expect(slider.customStates.has('user-valid')).to.be.false;
|
||||
expect(slider.hasCustomState('invalid')).to.be.true;
|
||||
expect(slider.hasCustomState('valid')).to.be.false;
|
||||
expect(slider.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(slider.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should be present in form data when using the form attribute and located outside of a <form>', async () => {
|
||||
|
||||
@@ -45,7 +45,7 @@ import styles from './slider.css';
|
||||
*/
|
||||
@customElement('wa-slider')
|
||||
export default class WaSlider extends WebAwesomeFormAssociatedElement {
|
||||
static css = [formControlStyles, styles];
|
||||
static shadowStyle = [formControlStyles, styles];
|
||||
|
||||
static get validators() {
|
||||
return [...super.validators, MirrorValidator()];
|
||||
|
||||
@@ -19,7 +19,7 @@ import styles from './spinner.css';
|
||||
*/
|
||||
@customElement('wa-spinner')
|
||||
export default class WaSpinner extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import styles from './split-panel.css';
|
||||
*/
|
||||
@customElement('wa-split-panel')
|
||||
export default class WaSplitPanel extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static shadowStyle = styles;
|
||||
|
||||
private cachedPositionInPixels: number;
|
||||
private isCollapsed = false;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
--border-style: var(--wa-form-control-border-style);
|
||||
--border-width: var(--wa-form-control-border-width);
|
||||
--box-shadow: initial;
|
||||
--height: var(--wa-form-control-toggle-size);
|
||||
--height: calc(1em * var(--wa-form-control-value-line-height));
|
||||
--thumb-color: var(--wa-form-control-border-color);
|
||||
--thumb-color-checked: var(--wa-form-control-background-color);
|
||||
--thumb-shadow: initial;
|
||||
@@ -14,7 +14,6 @@
|
||||
--width: calc(var(--height) * 1.75);
|
||||
|
||||
display: inline-flex;
|
||||
line-height: var(--wa-form-control-value-line-height);
|
||||
}
|
||||
|
||||
label {
|
||||
|
||||
@@ -230,12 +230,12 @@ describe('<wa-switch>', () => {
|
||||
const el = await fixture<HTMLFormElement>(html` <form novalidate><wa-switch required></wa-switch></form> `);
|
||||
const waSwitch = el.querySelector<WaSwitch>('wa-switch')!;
|
||||
|
||||
expect(waSwitch.customStates.has('required')).to.be.true;
|
||||
expect(waSwitch.customStates.has('optional')).to.be.false;
|
||||
expect(waSwitch.customStates.has('invalid')).to.be.true;
|
||||
expect(waSwitch.customStates.has('valid')).to.be.false;
|
||||
expect(waSwitch.customStates.has('user-invalid')).to.be.false;
|
||||
expect(waSwitch.customStates.has('user-valid')).to.be.false;
|
||||
expect(waSwitch.hasCustomState('required')).to.be.true;
|
||||
expect(waSwitch.hasCustomState('optional')).to.be.false;
|
||||
expect(waSwitch.hasCustomState('invalid')).to.be.true;
|
||||
expect(waSwitch.hasCustomState('valid')).to.be.false;
|
||||
expect(waSwitch.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(waSwitch.hasCustomState('user-valid')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user