mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-20 15:54:15 +00:00
Compare commits
17 Commits
v3.1.0
...
kj/update-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6c92ca2f7 | ||
|
|
5ed32c38de | ||
|
|
9d735da9af | ||
|
|
56b1196265 | ||
|
|
7e5f18ea97 | ||
|
|
4c1df98053 | ||
|
|
bd029b8a36 | ||
|
|
c60c62beb0 | ||
|
|
c748721f12 | ||
|
|
6e5a720f51 | ||
|
|
f739121aaf | ||
|
|
5fb3625ee5 | ||
|
|
da206a8787 | ||
|
|
fc62edc581 | ||
|
|
9f731325bb | ||
|
|
89b7a4c71b | ||
|
|
1ebfd31877 |
@@ -20,4 +20,5 @@ packages/**/*/src/react/index.ts
|
||||
node_modules
|
||||
|
||||
packages/**/*/_site
|
||||
packages/**/*/_bundle_
|
||||
packages/webawesome/docs/assets/scripts/prism-downloaded.js
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
3.0.0
|
||||
3.1.0
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@webawesome/monorepo",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@webawesome/monorepo",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.0",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
@@ -14023,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",
|
||||
@@ -14048,7 +14048,7 @@
|
||||
},
|
||||
"packages/webawesome-pro": {
|
||||
"name": "@awesome.me/webawesome-pro",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.0",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "4.1.0",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
<li><a href="/docs/resources/contributing">Contributing</a></li>
|
||||
<li><a href="/docs/resources/changelog">Changelog</a></li>
|
||||
<li><a href="/docs/resources/visual-tests">Visual Tests</a></li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
<!-- Components -->
|
||||
@@ -156,13 +154,26 @@
|
||||
<li><a href="/docs/components/textarea/">Textarea</a></li>
|
||||
<li><span class="is-planned wa-split">Toast <span><a href="https://github.com/shoelace-style/webawesome/issues/105" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge( { description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
||||
<li><a href="/docs/components/tooltip/">Tooltip</a></li>
|
||||
<li><a href="/docs/components/tree/">Tree</a></li>
|
||||
<li><a href="/docs/components/tree-item/">Tree Item</a></li>
|
||||
<li>
|
||||
<a href="/docs/components/tree/">Tree</a>
|
||||
<span class="wa-split">
|
||||
<a class="wa-cluster wa-gap-xs" href="/docs/components/video/">
|
||||
Video <wa-icon name="flask" aria-hidden="true" class="icon-shrink"></wa-icon>
|
||||
</a>
|
||||
{{ proBadge() }}
|
||||
</span>
|
||||
<ul>
|
||||
<li><a href="/docs/components/tree-item/">Tree Item</a></li>
|
||||
<li>
|
||||
<span class="wa-split">
|
||||
<a href="/docs/components/video-item/">
|
||||
Video Item <wa-icon name="flask" aria-hidden="true" class="icon-shrink"></wa-icon>
|
||||
</a>
|
||||
{{ proBadge() }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><span class="is-planned wa-split">Video <span><a href="https://github.com/shoelace-style/webawesome/issues/985" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge( { description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
||||
<li><a href="/docs/components/zoomable-frame">Zoomable Frame</a></li>
|
||||
{# PLOP_NEW_COMPONENT_PLACEHOLDER #}
|
||||
</ul>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -11,8 +11,13 @@ Web Awesome follows [Semantic Versioning](https://semver.org/). Breaking changes
|
||||
|
||||
Components with the <wa-badge variant="warning">Experimental</wa-badge> badge should not be used in production. They are made available as release candidates for development and testing purposes. As such, changes to experimental components will not be subject to semantic versioning.
|
||||
|
||||
## Next
|
||||
|
||||
- Fixed a bug in `<wa-combobox>` that prevented the listbox from opening when options were preselected [issue:1883]
|
||||
|
||||
## 3.1.0
|
||||
|
||||
- Added `<wa-video>` as an experimental pro component
|
||||
- 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
|
||||
@@ -516,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)
|
||||
|
||||
@@ -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, { encoding: "utf8" }));
|
||||
const webawesomePackageJSON = JSON.parse(fs.readFileSync(webawesomePackageJSONFile, { encoding: "utf8" }));
|
||||
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,7 +19,7 @@ rootPackageJSON.version = currentVersion;
|
||||
fs.writeFileSync(rootPackageJSONFile, JSON.stringify(rootPackageJSON, null, 2));
|
||||
|
||||
const versionsFile = path.join(monorepoRoot, 'VERSIONS.txt');
|
||||
const versions = fs.readFileSync(versionsFile, { encoding: "utf8" }).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)) {
|
||||
|
||||
@@ -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';
|
||||
|
||||
26
packages/webawesome/src/events/video-change.ts
Normal file
26
packages/webawesome/src/events/video-change.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user