Compare commits

..

19 Commits

Author SHA1 Message Date
lindsaym-fa
edc60d34bf add PR to changelog 2026-01-09 18:37:40 -05:00
lindsaym-fa
a891f9f471 update changelog 2026-01-09 17:13:27 -05:00
lindsaym-fa
705c7d7505 update the docs 2026-01-09 17:12:02 -05:00
lindsaym-fa
d288bfdc3d add missing .wa-gap-4xl 2026-01-09 17:11:43 -05:00
Brian Talbot
09ace44c17 A Glow Up for Dialogs That Pop Up (#1927)
* adding visual divider + padding to wa-dialog footers

* adding `wa-button`-focused `.shush` to `utils.css`
2026-01-09 14:52:23 -05:00
Lindsay M
f3de45803f Ensure consistent input heights in flex/grid containers (#1915)
* apply flex rules to `form-control` parts

* prevent input heights from growing in flex containers

* change approach: ignore `form-control` wrapper for styling
2025-12-31 11:35:37 -05:00
Steve Matney
ede1f0725d Ensuring styles apply for input labels when using 'slot="label"' (#1757)
* Ensuring styles apply for input labels when using 'slot="label"'

* Using 'has-slotted' class for codebase consistency

* Fleshing out 'has-slotted' label class to relevant form elements

* Ensuring label line height uses the correct token; removing unrelated PR changes

* Using 'has-label' class instead of 'has-slotted' for labels; applying it with all label content
2025-12-30 12:13:28 -05:00
Kelsey Jackson
56b1196265 got it working, but may revert to use on repo (#1888) 2025-12-19 10:54:45 -06:00
Cory LaViska
7e5f18ea97 update changelog (#1889) 2025-12-18 14:25:22 -05:00
Brian Talbot
c748721f12 adding event tracking to site search (#1872) 2025-12-17 15:22:18 -05:00
konnorrogers
6e5a720f51 update lockfiles and prettierignore 2025-12-16 13:42:13 -05:00
konnorrogers
f739121aaf updae lockfile 2025-12-16 13:30:16 -05:00
konnorrogers
5fb3625ee5 fix script 2025-12-16 13:22:32 -05:00
konnorrogers
da206a8787 update versions 2025-12-16 13:21:46 -05:00
konnorrogers
2b69fa74a5 Bump package.json version 2025-12-16 13:21:23 -05:00
konnorrogers
d1b6628d59 update package lock 2025-12-16 11:48:56 -05:00
konnorrogers
4dca4185e7 Bump changelog 2025-12-16 11:48:29 -05:00
Konnor Rogers
1e6d468959 add changelog entry (#1877) 2025-12-16 11:46:01 -05:00
Cory LaViska
00b8150f3e ensure min/max/step are numbers; fixes #1823 (#1832)
Co-authored-by: konnorrogers <konnor5456@gmail.com>
2025-12-16 11:06:27 -05:00
22 changed files with 453 additions and 57 deletions

View File

@@ -20,4 +20,5 @@ packages/**/*/src/react/index.ts
node_modules
packages/**/*/_site
packages/**/*/_bundle_
packages/webawesome/docs/assets/scripts/prism-downloaded.js

View File

@@ -1 +1,2 @@
3.0.0
3.1.0

183
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@webawesome/monorepo",
"version": "3.0.0-alpha.13",
"version": "3.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@webawesome/monorepo",
"version": "3.0.0-alpha.13",
"version": "3.1.0",
"license": "MIT",
"workspaces": [
"packages/*"
@@ -593,6 +593,10 @@
"resolved": "packages/webawesome",
"link": true
},
"node_modules/@awesome.me/webawesome-pro": {
"resolved": "packages/webawesome-pro",
"link": true
},
"node_modules/@babel/code-frame": {
"version": "7.23.5",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
@@ -2519,10 +2523,6 @@
"resolved": "https://registry.npmjs.org/@shoelace-style/localize/-/localize-3.2.1.tgz",
"integrity": "sha512-r4C9C/5kSfMBIr0D9imvpRdCNXtUNgyYThc4YlS6K5Hchv1UyxNQ9mxwj+BTRH2i1Neits260sR3OjKMnplsFA=="
},
"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",
@@ -12139,6 +12139,16 @@
"fsevents": "~2.3.2"
}
},
"node_modules/rollup-plugin-typescript-paths": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-typescript-paths/-/rollup-plugin-typescript-paths-1.5.0.tgz",
"integrity": "sha512-zly2aiGNjYJNq5YUi6eyGrQnCYUQ8b5czOtHZIGriwG9U3Ba2F9hlSklafXCdsNulK/IlNmE0Kzj0h+fVV32pA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"typescript": ">=3.4"
}
},
"node_modules/run-async": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz",
@@ -14013,7 +14023,7 @@
},
"packages/webawesome": {
"name": "@awesome.me/webawesome",
"version": "3.0.0",
"version": "3.1.0",
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "4.1.0",
@@ -14037,8 +14047,8 @@
}
},
"packages/webawesome-pro": {
"name": "@shoelace-style/webawesome-pro",
"version": "3.0.0",
"name": "@awesome.me/webawesome-pro",
"version": "3.1.0",
"dependencies": {
"@ctrl/tinycolor": "4.1.0",
"@floating-ui/dom": "^1.6.13",
@@ -14052,9 +14062,11 @@
},
"devDependencies": {
"@wc-toolkit/jsx-types": "^1.3.0",
"@web/dev-server-rollup": "^0.6.4",
"eleventy-plugin-git-commit-date": "^0.1.3",
"esbuild": "^0.25.11",
"npm-check-updates": "^19.1.2"
"npm-check-updates": "^19.1.2",
"rollup-plugin-typescript-paths": "^1.5.0"
},
"engines": {
"node": ">=14.17.0"
@@ -14468,6 +14480,84 @@
"node": ">=18"
}
},
"packages/webawesome-pro/node_modules/@web/dev-server-core": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@web/dev-server-core/-/dev-server-core-0.7.5.tgz",
"integrity": "sha512-Da65zsiN6iZPMRuj4Oa6YPwvsmZmo5gtPWhW2lx3GTUf5CAEapjVpZVlUXnKPL7M7zRuk72jSsIl8lo+XpTCtw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/koa": "^2.11.6",
"@types/ws": "^7.4.0",
"@web/parse5-utils": "^2.1.0",
"chokidar": "^4.0.1",
"clone": "^2.1.2",
"es-module-lexer": "^1.0.0",
"get-stream": "^6.0.0",
"is-stream": "^2.0.0",
"isbinaryfile": "^5.0.0",
"koa": "^2.13.0",
"koa-etag": "^4.0.0",
"koa-send": "^5.0.1",
"koa-static": "^5.0.0",
"lru-cache": "^8.0.4",
"mime-types": "^2.1.27",
"parse5": "^6.0.1",
"picomatch": "^2.2.2",
"ws": "^7.5.10"
},
"engines": {
"node": ">=18.0.0"
}
},
"packages/webawesome-pro/node_modules/@web/dev-server-rollup": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/@web/dev-server-rollup/-/dev-server-rollup-0.6.4.tgz",
"integrity": "sha512-sJZfTGCCrdku5xYnQQG51odGI092hKY9YFM0X3Z0tRY3iXKXcYRaLZrErw5KfCxr6g0JRuhe4BBhqXTA5Q2I3Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/plugin-node-resolve": "^15.0.1",
"@web/dev-server-core": "^0.7.2",
"nanocolors": "^0.2.1",
"parse5": "^6.0.1",
"rollup": "^4.4.0",
"whatwg-url": "^14.0.0"
},
"engines": {
"node": ">=18.0.0"
}
},
"packages/webawesome-pro/node_modules/@web/parse5-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@web/parse5-utils/-/parse5-utils-2.1.0.tgz",
"integrity": "sha512-GzfK5disEJ6wEjoPwx8AVNwUe9gYIiwc+x//QYxYDAFKUp4Xb1OJAGLc2l2gVrSQmtPGLKrTRcW90Hv4pEq1qA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/parse5": "^6.0.1",
"parse5": "^6.0.1"
},
"engines": {
"node": ">=18.0.0"
}
},
"packages/webawesome-pro/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"packages/webawesome-pro/node_modules/esbuild": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz",
@@ -14510,6 +14600,16 @@
"@esbuild/win32-x64": "0.25.11"
}
},
"packages/webawesome-pro/node_modules/lru-cache": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
"integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=16.14"
}
},
"packages/webawesome-pro/node_modules/nanoid": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
@@ -14542,6 +14642,69 @@
"npm": ">=8.12.1"
}
},
"packages/webawesome-pro/node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"packages/webawesome-pro/node_modules/tr46": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
"dev": true,
"license": "MIT",
"dependencies": {
"punycode": "^2.3.1"
},
"engines": {
"node": ">=18"
}
},
"packages/webawesome-pro/node_modules/whatwg-url": {
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"tr46": "^5.1.0",
"webidl-conversions": "^7.0.0"
},
"engines": {
"node": ">=18"
}
},
"packages/webawesome-pro/node_modules/ws": {
"version": "7.5.10",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"packages/webawesome/node_modules/@esbuild/aix-ppc64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz",

View File

@@ -2,7 +2,7 @@
"name": "@webawesome/monorepo",
"private": true,
"description": "A forward-thinking library of web components.",
"version": "3.0.0",
"version": "3.1.0",
"homepage": "https://webawesome.com/",
"author": "Web Awesome",
"license": "MIT",

View File

@@ -1,12 +1,24 @@
// Search data
const version = document.documentElement.getAttribute('data-version') || '';
const res = await Promise.all([import('https://cdn.jsdelivr.net/npm/lunr/+esm'), fetch(`/search.json?v=${version}`)]);
const res = await Promise.all([
import('https://cdn.jsdelivr.net/npm/lunr/+esm'),
fetch(`/search.json?v=${version}`),
import('/assets/scripts/track.js').catch(() => null),
]);
const lunr = res[0].default;
const searchData = await res[1].json();
const searchIndex = lunr.Index.load(searchData.searchIndex);
const map = searchData.map;
const searchDebounce = 200;
const queryTrackDelay = 1000;
let searchTimeout;
let queryTrackTimeout;
let lastTrackedQuery = '';
let resultSelected = false;
// Optional event tracking - works standalone if track.js isn't available
const trackModule = res[2];
const trackEvent = trackModule?.trackEvent || window.trackEvent || (() => {});
// We're using Turbo, so references to these elements aren't guaranteed to remain intact
function getElements() {
@@ -17,6 +29,24 @@ function getElements() {
};
}
function trackQuerySubmit(query, resultSelectedValue) {
if (!query || query.length === 0) return;
const { results } = getElements();
if (!results) return;
const matches = results.querySelectorAll('li').length;
const truncatedQuery = query.length > 500 ? query.substring(0, 500) : query;
trackEvent('navigation:search_query_submit', {
query: truncatedQuery,
query_length: query.length,
result_count: matches,
has_results: matches > 0,
result_selected: resultSelectedValue,
});
}
// Show the search dialog when slash (or CMD+K) is pressed and focus is not inside a form element
document.addEventListener('keydown', event => {
if (
@@ -42,40 +72,98 @@ document.addEventListener('click', event => {
function show() {
const { dialog, input, results } = getElements();
if (!dialog || !input || !results) return;
const wasAlreadyOpen = dialog.open;
// Remove existing listeners before adding to prevent duplicates
input.removeEventListener('input', handleInput);
results.removeEventListener('click', handleSelection);
dialog.removeEventListener('keydown', handleKeyDown);
dialog.removeEventListener('wa-hide', handleClose);
resultSelected = false;
lastTrackedQuery = '';
input.addEventListener('input', handleInput);
results.addEventListener('click', handleSelection);
dialog.addEventListener('keydown', handleKeyDown);
dialog.addEventListener('wa-hide', handleClose);
dialog.open = true;
if (!wasAlreadyOpen) {
trackEvent('navigation:search_dialog_open');
}
}
function hide() {
function cleanup() {
const { dialog, input, results } = getElements();
if (!dialog || !input || !results) return;
clearTimeout(searchTimeout);
clearTimeout(queryTrackTimeout);
input.removeEventListener('input', handleInput);
results.removeEventListener('click', handleSelection);
dialog.removeEventListener('keydown', handleKeyDown);
dialog.removeEventListener('wa-hide', handleClose);
dialog.open = false;
// Reset state to prevent leakage between dialog sessions
resultSelected = false;
lastTrackedQuery = '';
}
function handleClose() {
const { input } = getElements();
async function handleClose() {
const { dialog, input } = getElements();
if (!dialog || !input) return;
clearTimeout(queryTrackTimeout);
queryTrackTimeout = null;
dialog.removeEventListener('wa-hide', handleClose);
if (!resultSelected) {
const query = input.value.trim();
if (query.length > 0 && query !== lastTrackedQuery) {
trackQuerySubmit(query, false);
lastTrackedQuery = query;
}
}
input.value = '';
updateResults();
try {
await updateResults();
} catch (error) {
// Silently handle errors - UI cleanup should continue
}
cleanup();
trackEvent('navigation:search_dialog_close');
}
function handleInput() {
const { input } = getElements();
if (!input) return;
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
clearTimeout(queryTrackTimeout);
const query = input.value.trim();
if (query.length === 0) {
lastTrackedQuery = '';
}
searchTimeout = setTimeout(async () => {
await updateResults(query);
if (query.length > 0 && query !== lastTrackedQuery) {
queryTrackTimeout = setTimeout(() => {
const { input: currentInput, results } = getElements();
if (!currentInput || resultSelected) return;
const currentQuery = currentInput.value.trim();
if (currentQuery === query && currentQuery !== lastTrackedQuery) {
trackQuerySubmit(currentQuery, false);
lastTrackedQuery = currentQuery;
}
}, queryTrackDelay);
}
}, searchDebounce);
}
function handleKeyDown(event) {
const { input, results } = getElements();
if (!input || !results) return;
// Handle keyboard selections
if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter'].includes(event.key)) {
@@ -104,7 +192,12 @@ function handleKeyDown(event) {
nextEl = items[items.length - 1];
break;
case 'Enter':
currentEl?.querySelector('a')?.click();
if (currentEl) {
const link = currentEl.querySelector('a');
if (link) {
selectResult(link, 'keyboard_enter');
}
}
break;
}
@@ -121,27 +214,62 @@ function handleKeyDown(event) {
}
}
function selectResult(link, selectionMethod) {
const { input, results } = getElements();
if (!input || !link) return;
// Clear pending query tracking timeout to prevent duplicate events
clearTimeout(queryTrackTimeout);
queryTrackTimeout = null;
resultSelected = true; // Set immediately so timeout callback (if executing) sees it
const query = input.value.trim();
if (!link.dataset.searchResultIndex) return;
const resultIndex = parseInt(link.dataset.searchResultIndex, 10);
if (isNaN(resultIndex) || resultIndex < 1) return;
const resultUrl = link.dataset.searchResultUrl || link.getAttribute('href');
if (!resultUrl) return;
lastTrackedQuery = query;
trackQuerySubmit(query, true);
trackEvent('navigation:search_result_click', {
query,
result_index: resultIndex,
result_url: resultUrl,
selection_method: selectionMethod,
});
const { dialog } = getElements();
if (dialog) {
dialog.removeEventListener('wa-hide', handleClose);
cleanup();
trackEvent('navigation:search_dialog_close');
dialog.open = false;
}
if (window.Turbo) {
Turbo.visit(resultUrl);
} else {
location.href = resultUrl;
}
}
function handleSelection(event) {
const link = event.target.closest('a');
if (link) {
event.preventDefault();
hide();
if (window.Turbo) {
Turbo.visit(link.href);
} else {
location.href = link.href;
}
selectResult(link, 'mouse_click');
}
}
// Queries the search index and updates the results
async function updateResults(query = '') {
const { dialog, input, results } = getElements();
if (!dialog || !input || !results) return;
try {
const hasQuery = query.length > 0;
const trimmedQuery = query.trim();
const hasQuery = trimmedQuery.length > 0;
let matches = [];
if (hasQuery) {
@@ -149,13 +277,13 @@ async function updateResults(query = '') {
const seenRefs = new Set();
// Start with a standard search to get the best "exact match" result
searchIndex.search(`${query}`).forEach(match => {
searchIndex.search(`${trimmedQuery}`).forEach(match => {
matches.push(match);
seenRefs.add(match.ref);
});
// Add wildcard matches if not already included
searchIndex.search(`${query}*`).forEach(match => {
searchIndex.search(`${trimmedQuery}*`).forEach(match => {
if (!seenRefs.has(match.ref)) {
matches.push(match);
seenRefs.add(match.ref);
@@ -163,11 +291,10 @@ async function updateResults(query = '') {
});
// Add fuzzy search matches last
const fuzzyTokens = query
const fuzzyTokens = trimmedQuery
.split(' ')
.map(term => `${term}~1`)
.join(' ');
searchIndex.search(fuzzyTokens).forEach(match => {
if (!seenRefs.has(match.ref)) {
matches.push(match);
@@ -180,12 +307,12 @@ async function updateResults(query = '') {
dialog.classList.toggle('has-results', hasQuery && hasResults);
dialog.classList.toggle('no-results', hasQuery && !hasResults);
input.setAttribute('aria-activedescendant', '');
results.innerHTML = '';
matches.forEach((match, index) => {
const page = map[match.ref];
if (!page || !page.url) return;
const li = document.createElement('li');
const a = document.createElement('a');
const displayTitle = page.title ?? '';
@@ -197,12 +324,10 @@ async function updateResults(query = '') {
li.setAttribute('role', 'option');
li.setAttribute('id', `search-result-item-${match.ref}`);
li.setAttribute('data-selected', index === 0 ? 'true' : 'false');
if (page.url === '/') icon = 'home';
if (page.url.startsWith('/docs/utilities/native')) icon = 'code';
if (page.url.startsWith('/docs/components')) icon = 'puzzle-piece';
if (page.url.startsWith('/docs/theme') || page.url.startsWith('/docs/restyle')) icon = 'palette';
a.href = page.url;
a.innerHTML = `
<div class="site-search-result-icon" aria-hidden="true">
@@ -218,6 +343,9 @@ async function updateResults(query = '') {
a.querySelector('.site-search-result-description').textContent = displayDescription;
a.querySelector('.site-search-result-url').textContent = displayUrl;
// Use 1-based indexing for analytics
a.dataset.searchResultIndex = (index + 1).toString();
a.dataset.searchResultUrl = page.url;
li.appendChild(a);
results.appendChild(li);
});

View File

@@ -130,6 +130,13 @@
}
}
/* dialogs */
wa-dialog:has([slot='footer']) [slot='footer'] {
border-block-start: var(--wa-border-width-s) solid var(--wa-color-surface-border);
flex-grow: 1; /* make footer contents span entire width of dialog */
padding-block-start: var(--wa-space-l);
}
/* anchor headings */
.anchor-heading a {
opacity: 0;
@@ -250,7 +257,6 @@
z-index: 0;
}
}
/* #endregion */
/* buttons with icon toggle on hover */
wa-button .icon-hover {
@@ -262,6 +268,13 @@
wa-button:hover .icon-hover {
display: inline-flex;
}
/* buttons that are "shushed" (visually muted) by default, but have their full presentation otherwise */
wa-button.shush {
&:not(:hover):not(active)::part(base) {
color: var(--wa-color-text-quiet);
}
}
/* #endregion */
/* #region resets */

View File

@@ -13,10 +13,16 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
## Next
- Fixed a bug in `<wa-combobox>` that prevented the listbox from opening when options were preselected [issue:1883]
- Added missing `.wa-gap-4xl` utility class [pr:1931]
## 3.1.0
- Added `<wa-combobox>` as an experimental pro component [issue:1074]
- Added version 2.0.0 of the [official Web Awesome Figma Design Kit](/docs/resources/figma)
- Added npm support for Web Awesome Pro
- Added `layers.css` to define cascade layer order and updated palettes, themes, native styles, and utilities to import the new rule for more fail-safe modularity [pr:1793]
- [PRO]: Fixed a few sizing bugs in `<wa-page>` and `slot="footer"` no longer will always "overflow" the container.
- Fixed a bug in `<wa-slider>` that caused some touch devices to end up with the incorrect value [issue:1703]
- Fixed a bug in `<wa-card>` that prevented some slots from being detected correctly [discuss:1450]
- Fixed a z-index bug in `<wa-scroller>` styles [issue:1724]
@@ -515,4 +521,4 @@ Many of these changes and improvements were the direct result of feedback from u
</details>
Did we miss something? [Let us know!](https://github.com/shoelace-style/webawesome/discussions)
Did we miss something? [Let us know!](https://github.com/shoelace-style/webawesome/discussions)

View File

@@ -14,8 +14,7 @@ tags: layoutUtilities
}
</style>
Web Awesome includes classes to set the `gap` property of flex and grid containers. They can be used alongside other Web Awesome layout utilities, like [cluster](/docs/utilities/cluster) and [stack](/docs/utilities/stack), to change the space between items.
Or even by themselves — all gap properties also set `display: flex` with a specificity of 0 so that it can be trivially overridden.
Web Awesome includes classes to set the `gap` property of flex and grid containers. Use them alone to create a flex container with a gap, or use them alongside other Web Awesome layout utilities, like [cluster](/docs/utilities/cluster) and [stack](/docs/utilities/stack), to change the space between items.
Besides `wa-gap-0`, which sets `gap` to zero, each class corresponds to one of the [`--wa-space-*`](/docs/tokens/space) tokens in your theme.
@@ -31,3 +30,4 @@ Besides `wa-gap-0`, which sets `gap` to zero, each class corresponds to one of t
| `wa-gap-xl` | `--wa-space-xl` | <div class="wa-cluster wa-gap-xl"><div class="preview-block"></div><div class="preview-block"></div></div> |
| `wa-gap-2xl` | `--wa-space-2xl` | <div class="wa-cluster wa-gap-2xl"><div class="preview-block"></div><div class="preview-block"></div></div> |
| `wa-gap-3xl` | `--wa-space-3xl` | <div class="wa-cluster wa-gap-3xl"><div class="preview-block"></div><div class="preview-block"></div></div> |
| `wa-gap-4xl` | `--wa-space-4xl` | <div class="wa-cluster wa-gap-4xl"><div class="preview-block"></div><div class="preview-block"></div></div> |

View File

@@ -4,7 +4,7 @@
"access": "public"
},
"description": "A forward-thinking library of web components.",
"version": "3.0.0",
"version": "3.1.0",
"homepage": "https://webawesome.com/",
"author": "Web Awesome",
"license": "MIT",
@@ -67,7 +67,7 @@
"check-updates": "npm-check-updates --cooldown 7 --interactive --format group",
"print-version": "echo $npm_package_version",
"tag-version": "git tag -a \"v$(npm run print-version | tail -n1)\" -m \"tag v$(npm run print-version | tail -n1)\"",
"postversion": "npm run tag-version && node ./scripts/update-root-version.js"
"postversion": "node ./scripts/update-root-version.js"
},
"engines": {
"node": ">=14.17.0"

View File

@@ -10,8 +10,8 @@ const monorepoRoot = path.resolve(__dirname, '..', '..', '..');
const rootPackageJSONFile = path.join(monorepoRoot, 'package.json');
const webawesomePackageJSONFile = path.join(path.resolve(__dirname, '..'), 'package.json');
const rootPackageJSON = JSON.parse(fs.readFileSync(rootPackageJSONFile));
const webawesomePackageJSON = JSON.parse(fs.readFileSync(webawesomePackageJSONFile));
const rootPackageJSON = JSON.parse(fs.readFileSync(rootPackageJSONFile, { encoding: 'utf8' }));
const webawesomePackageJSON = JSON.parse(fs.readFileSync(webawesomePackageJSONFile, { encoding: 'utf8' }));
const currentVersion = webawesomePackageJSON.version;
rootPackageJSON.version = currentVersion;
@@ -19,9 +19,9 @@ rootPackageJSON.version = currentVersion;
fs.writeFileSync(rootPackageJSONFile, JSON.stringify(rootPackageJSON, null, 2));
const versionsFile = path.join(monorepoRoot, 'VERSIONS.txt');
const versions = fs.readFileSync(versions).split(/\r?\n/);
const versions = fs.readFileSync(versionsFile, { encoding: 'utf8' }).split(/\r?\n/);
// TODO: Make this smart and understand semver and "insert" in the correct spot instead of appending.
if (!versions.includes(currentVersion)) {
fs.appendFileSync(webawesomePackageJSON.version);
fs.appendFileSync(versionsFile, webawesomePackageJSON.version);
}

View File

@@ -1277,7 +1277,14 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
})}
part="trigger-container form-control"
>
<div part="form-control-label" class="label" id="form-control-label">
<div
part="form-control-label"
class=${classMap({
label: true,
'has-label': hasLabel,
})}
id="form-control-label"
>
<slot name="label">${this.label}</slot>
</div>

View File

@@ -6,7 +6,6 @@ export default css`
}
.text-field {
flex: auto;
display: flex;
align-items: stretch;
justify-content: start;

View File

@@ -48,6 +48,27 @@ describe('<wa-input>', () => {
expect(input.title).to.equal('Test');
});
it('should have label with "has-label" class if label has a slotted element', async () => {
const el = await fixture<WaInput>(html` <wa-input><span slot="label">Name</span></wa-input> `);
await el.updateComplete;
const label = el.shadowRoot!.querySelector('[part~="form-control-label"]')!;
expect(label.classList.contains('has-label')).to.equal(true);
});
it('should have label with "has-label" class if label is provided as an attribute', async () => {
const el = await fixture<WaInput>(html` <wa-input label="Name"></wa-input> `);
await el.updateComplete;
const label = el.shadowRoot!.querySelector('[part~="form-control-label"]')!;
expect(label.classList.contains('has-label')).to.equal(true);
});
it('should not have "has-label" class on label if no label content is provided', async () => {
const el = await fixture<WaInput>(html` <wa-input></wa-input> `);
await el.updateComplete;
const label = el.shadowRoot!.querySelector('[part~="form-control-label"]')!;
expect(label.classList.contains('has-label')).to.equal(false);
});
it('should be disabled with the disabled attribute', async () => {
const el = await fixture<WaInput>(html` <wa-input disabled></wa-input> `);
await el.updateComplete;

View File

@@ -342,7 +342,15 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
(typeof this.value === 'number' || (this.value && this.value.length > 0));
return html`
<label part="form-control-label label" class="label" for="input" aria-hidden=${hasLabel ? 'false' : 'true'}>
<label
part="form-control-label label"
class=${classMap({
label: true,
'has-label': hasLabel,
})}
for="input"
aria-hidden=${hasLabel ? 'false' : 'true'}
>
<slot name="label">${this.label}</slot>
</label>

View File

@@ -352,7 +352,10 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
<label
part="form-control-label"
id="label"
class="label"
class=${classMap({
label: true,
'has-label': hasLabel,
})}
aria-hidden=${hasLabel ? 'false' : 'true'}
@click=${this.handleLabelClick}
>

View File

@@ -938,7 +938,10 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
<label
id="label"
part="form-control-label label"
class="label"
class=${classMap({
label: true,
'has-label': hasLabel,
})}
aria-hidden=${hasLabel ? 'false' : 'true'}
@click=${this.handleLabelClick}
>

View File

@@ -807,7 +807,7 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
id="label"
part="label"
for=${this.isRange ? 'thumb-min' : 'text-box'}
class=${classMap({ vh: !hasLabel })}
class=${classMap({ vh: !hasLabel, 'has-label': hasLabel })}
@pointerdown=${this.handleLabelPointerDown}
>
<slot name="label">${this.label}</slot>

View File

@@ -331,7 +331,15 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
const hasHint = this.hint ? true : !!hasHintSlot;
return html`
<label part="form-control-label label" class="label" for="input" aria-hidden=${hasLabel ? 'false' : 'true'}>
<label
part="form-control-label label"
class=${classMap({
label: true,
'has-label': hasLabel,
})}
for="input"
aria-hidden=${hasLabel ? 'false' : 'true'}
>
<slot name="label">${this.label}</slot>
</label>

View File

@@ -28,3 +28,4 @@ export type { WaSlideChangeEvent } from './slide-change.js';
export type { WaStartEvent } from './start.js';
export type { WaTabHideEvent } from './tab-hide.js';
export type { WaTabShowEvent } from './tab-show.js';
export type { WaVideoChangeEvent } from './video-change.js';

View File

@@ -0,0 +1,26 @@
export class WaVideoChangeEvent extends Event {
readonly detail: WaVideoChangeEventDetail;
constructor(detail: WaVideoChangeEventDetail) {
super('wa-video-change', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
export interface WaVideoChangeEventDetail {
previousIndex: number;
currentIndex: number;
video: {
title?: string;
poster?: string;
duration?: string;
sources: { src: string; type: string }[];
tracks: { src: string; kind: string; srclang: string; label: string }[];
};
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-video-change': WaVideoChangeEvent;
}
}

View File

@@ -6,8 +6,14 @@ export default css`
flex-direction: column;
}
/* Treat wrapped labels, inputs, and hints as direct children of the host element */
[part~='form-control'] {
display: contents;
}
/* Label */
:is([part~='form-control-label'], [part~='label']):has(*:not(:empty)) {
:is([part~='form-control-label'], [part~='label']):has(*:not(:empty)),
:is([part~='form-control-label'], [part~='label']).has-label {
display: inline-flex;
color: var(--wa-form-control-label-color);
font-weight: var(--wa-form-control-label-font-weight);
@@ -29,7 +35,6 @@ export default css`
line-height: var(--wa-form-control-hint-line-height);
margin-block-start: 0.5em;
font-size: var(--wa-font-size-smaller);
line-height: var(--wa-form-control-label-line-height);
&:not(.has-slotted) {
display: none;

View File

@@ -45,4 +45,7 @@
.wa-gap-3xl {
gap: var(--wa-space-3xl);
}
.wa-gap-4xl {
gap: var(--wa-space-4xl);
}
}