mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-20 07:54:14 +00:00
Compare commits
11 Commits
combobox-f
...
v3.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b69fa74a5 | ||
|
|
d1b6628d59 | ||
|
|
4dca4185e7 | ||
|
|
1e6d468959 | ||
|
|
00b8150f3e | ||
|
|
3d395653fc | ||
|
|
b3844ef77f | ||
|
|
a50648c6e8 | ||
|
|
b6b82fb0ac | ||
|
|
fa073e8d21 | ||
|
|
e0f6ff11ec |
@@ -105,6 +105,7 @@
|
||||
"keydown",
|
||||
"keyframes",
|
||||
"keymaker",
|
||||
"Kickstarter",
|
||||
"Konnor",
|
||||
"Kool",
|
||||
"labelledby",
|
||||
@@ -117,6 +118,7 @@
|
||||
"lowercasing",
|
||||
"Lucide",
|
||||
"maxlength",
|
||||
"mdash",
|
||||
"Menlo",
|
||||
"menuitemcheckbox",
|
||||
"menuitemradio",
|
||||
@@ -130,6 +132,7 @@
|
||||
"mouseout",
|
||||
"mouseup",
|
||||
"multiselectable",
|
||||
"nbsp",
|
||||
"nextjs",
|
||||
"nocheck",
|
||||
"noindex",
|
||||
@@ -179,6 +182,7 @@
|
||||
"shadowrootmode",
|
||||
"Shortcode",
|
||||
"Shortcodes",
|
||||
"signup",
|
||||
"sitedir",
|
||||
"slotchange",
|
||||
"smartquotes",
|
||||
|
||||
195
package-lock.json
generated
195
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@webawesome/monorepo",
|
||||
"version": "3.0.0-alpha.13",
|
||||
"version": "3.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@webawesome/monorepo",
|
||||
"version": "3.0.0-alpha.13",
|
||||
"version": "3.0.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",
|
||||
@@ -14037,7 +14047,7 @@
|
||||
}
|
||||
},
|
||||
"packages/webawesome-pro": {
|
||||
"name": "@shoelace-style/webawesome-pro",
|
||||
"name": "@awesome.me/webawesome-pro",
|
||||
"version": "3.0.0",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "4.1.0",
|
||||
@@ -14052,8 +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"
|
||||
"esbuild": "^0.25.11",
|
||||
"npm-check-updates": "^19.1.2",
|
||||
"rollup-plugin-typescript-paths": "^1.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17.0"
|
||||
@@ -14467,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",
|
||||
@@ -14509,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",
|
||||
@@ -14526,6 +14627,84 @@
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"packages/webawesome-pro/node_modules/npm-check-updates": {
|
||||
"version": "19.1.2",
|
||||
"resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-19.1.2.tgz",
|
||||
"integrity": "sha512-FNeFCVgPOj0fz89hOpGtxP2rnnRHR7hD2E8qNU8SMWfkyDZXA/xpgjsL3UMLSo3F/K13QvJDnbxPngulNDDo/g==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"ncu": "build/cli.js",
|
||||
"npm-check-updates": "build/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0",
|
||||
"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",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@webawesome/monorepo",
|
||||
"private": true,
|
||||
"description": "A forward-thinking library of web components.",
|
||||
"version": "3.0.0-alpha.13",
|
||||
"version": "3.0.0",
|
||||
"homepage": "https://webawesome.com/",
|
||||
"author": "Web Awesome",
|
||||
"license": "MIT",
|
||||
@@ -85,4 +85,4 @@
|
||||
"prettier --write"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,15 @@
|
||||
<li><span class="is-planned wa-split">Charts <span><a href="https://github.com/shoelace-style/webawesome/issues/1073" 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/checkbox/">Checkbox</a></li>
|
||||
<li><a href="/docs/components/color-picker/">Color Picker</a></li>
|
||||
<li><span class="is-planned wa-split">Combobox <span><a href="https://github.com/shoelace-style/webawesome/issues/1074" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
||||
<li>
|
||||
<span class="wa-split">
|
||||
<span>
|
||||
<a href="/docs/components/combobox">Combobox</a>
|
||||
<wa-icon name="flask" aria-hidden="true" class="icon-shrink"></wa-icon>
|
||||
</span>
|
||||
{{ proBadge() }}
|
||||
</span>
|
||||
</li>
|
||||
<li><a href="/docs/components/comparison/">Comparison</a></li>
|
||||
<li>
|
||||
<a class="wa-cluster wa-gap-xs" href="/docs/components/copy-button/">
|
||||
@@ -90,7 +98,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li><span class="is-planned wa-split">Data Grid <span><a href="https://github.com/shoelace-style/webawesome/issues/1072" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
||||
<li><span class="is-planned wa-split">Datepicker <span><a href="https://github.com/shoelace-style/webawesome/issues/1075" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
||||
<li><span class="is-planned wa-split">Date Picker <span><a href="https://github.com/shoelace-style/webawesome/issues/1075" 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/details/">Details</a></li>
|
||||
<li><a href="/docs/components/dialog/">Dialog</a></li>
|
||||
<li><a href="/docs/components/divider/">Divider</a></li>
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
<wa-badge variant="neutral">Since {{ component.since }}</wa-badge>
|
||||
<wa-badge
|
||||
{% if component.status == 'stable' %}variant="brand"{% endif %}
|
||||
{% if component.status == 'experimental' %}variant="warning"{% endif %}
|
||||
{% if component.status == 'experimental' %}variant="warning" appearance="filled"{% endif %}
|
||||
>
|
||||
{{ component.status }}
|
||||
</wa-badge>
|
||||
{% if isProComponent %}
|
||||
<wa-badge class="pro">Pro</wa-badge>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="component-summary">
|
||||
{{ component.summary | inlineMarkdown | safe }}
|
||||
@@ -20,6 +23,37 @@
|
||||
|
||||
{# Component API #}
|
||||
{% block afterContent %}
|
||||
{# Importing #}
|
||||
<h2>Importing</h2>
|
||||
<p>
|
||||
Autoloading components via <a href="/docs/#using-a-project">projects</a> is the recommended way to import components. If you prefer to do it manually, use one of the following code snippets.
|
||||
</p>
|
||||
|
||||
{% set componentName = component.tagName | stripPrefix %}
|
||||
{% set componentPath = ["components/", componentName, "/", componentName, ".js"] | join("") %}
|
||||
<wa-tab-group label="How would you like to import this component?">
|
||||
<wa-tab panel="cdn">CDN</wa-tab>
|
||||
<wa-tab panel="npm">npm</wa-tab>
|
||||
<wa-tab panel="react">React</wa-tab>
|
||||
<wa-tab-panel name="cdn">
|
||||
<p>
|
||||
Let your project code do the work! <a href="/signup">Sign up for free</a> to use a project with your very own CDN — it's the fastest and easiest way to use Web Awesome.
|
||||
</p>
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="npm">
|
||||
<p>
|
||||
To manually import this component from NPM, use the following code.
|
||||
</p>
|
||||
<pre><code class="language-js">import '@awesome.me/webawesome/dist/{{ componentPath }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="react">
|
||||
<p>
|
||||
To manually import this component from React, use the following code.
|
||||
</p>
|
||||
<pre><code class="language-js">import {{ component.name }} from '@awesome.me/webawesome/dist/react/{{ componentName }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
{# Slots #}
|
||||
{% if component.slots.length %}
|
||||
<h2>Slots</h2>
|
||||
@@ -270,38 +304,6 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{# Importing #}
|
||||
<h2>Importing</h2>
|
||||
<p>
|
||||
Autoloading components via <a href="/docs/#using-a-project">projects</a> is the recommended way to import components. If you prefer to do it manually, use one of the following code snippets.
|
||||
</p>
|
||||
|
||||
|
||||
{% set componentName = component.tagName | stripPrefix %}
|
||||
{% set componentPath = ["components/", componentName, "/", componentName, ".js"] | join("") %}
|
||||
<wa-tab-group label="How would you like to import this component?">
|
||||
<wa-tab panel="cdn">CDN</wa-tab>
|
||||
<wa-tab panel="npm">npm</wa-tab>
|
||||
<wa-tab panel="react">React</wa-tab>
|
||||
<wa-tab-panel name="cdn">
|
||||
<p>
|
||||
Let your project code do the work! <a href="/signup">Sign up for free</a> to use a project with your very own CDN — it's the fastest and easiest way to use Web Awesome.
|
||||
</p>
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="npm">
|
||||
<p>
|
||||
To manually import this component from NPM, use the following code.
|
||||
</p>
|
||||
<pre><code class="language-js">import '@awesome.me/webawesome/dist/{{ componentPath }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="react">
|
||||
<p>
|
||||
To manually import this component from React, use the following code.
|
||||
</p>
|
||||
<pre><code class="language-js">import {{ component.name }} from '@awesome.me/webawesome/dist/react/{{ componentName }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<div class="component-help">
|
||||
|
||||
@@ -285,9 +285,10 @@ Remember that custom tags are rendered in a shadow root. To style them, you can
|
||||
const name = option.querySelector('wa-icon[slot="start"]').name;
|
||||
|
||||
// You can return a string, a Lit Template, or an HTMLElement here
|
||||
// Important: include data-value so the tag can be removed properly!
|
||||
return `
|
||||
<wa-tag with-remove>
|
||||
<wa-icon name="${name}" style="padding-inline-end: .5rem;"></wa-icon>
|
||||
<wa-tag with-remove data-value="${option.value}">
|
||||
<wa-icon name="${name}"></wa-icon>
|
||||
${option.label}
|
||||
</wa-tag>
|
||||
`;
|
||||
@@ -299,6 +300,10 @@ Remember that custom tags are rendered in a shadow root. To style them, you can
|
||||
Be sure you trust the content you are outputting! Passing unsanitized user input to `getTag()` can result in XSS vulnerabilities.
|
||||
:::
|
||||
|
||||
:::info
|
||||
When using custom tags with `with-remove`, you must include the `data-value` attribute set to the option's value. This allows the select to identify which option to deselect when the tag's remove button is clicked.
|
||||
:::
|
||||
|
||||
### Lazy loading options
|
||||
|
||||
Lazy loading options works similarly to native `<select>` elements. The select component handles various scenarios intelligently:
|
||||
|
||||
@@ -11,9 +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
|
||||
## 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]
|
||||
@@ -21,7 +25,12 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
|
||||
- Fixed a bug in `<wa-tree-item>` that caused the spinner to not show when lazy loading [issue:1678]
|
||||
- Fixed a bug in `<wa-dropdown>` that caused the browser to hang when cancelling the `wa-hide` event [issue:1483]
|
||||
- Fixed a bug in `<wa-dropdown-item>` that prevented the icon dependency from being imported [issue:1825]
|
||||
- Fixed a bug in `<wa-select>` that prevented clicks on the tag's remove button from removing options in multiple mode
|
||||
- Fixed a bug in `<wa-select>` that caused tags to appear in alphabetical order instead of selection order when using `multiple`
|
||||
- Improved performance of `<wa-icon>` so initial rendering occurs faster, especially with multiple icons on the page [issue:1729]
|
||||
- Improved `<wa-slider>` to not throw an error when string values are passed to the `min`, `max`, and `step` properties [issue:1823]
|
||||
- Fixed a bug in Web Awesome form controls that caused `<wa-input form="foo">` to set the form property to equal `"foo"` instead of returning an `HTMLFormElement` breaking platform expectations. [pr:1815]
|
||||
- Fixed a bug in `<wa-button>` causing it to not copy over attributes for form submissions. [pr:1815]
|
||||
- Improved performance of all components by fixing how CSS is imported and reused [issue:1812]
|
||||
- Modified the default `transition` styles of `<wa-dropdown-item>` to use design tokens [pr:1693]
|
||||
|
||||
@@ -507,4 +516,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)
|
||||
@@ -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"
|
||||
"postversion": "node ./scripts/update-root-version.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17.0"
|
||||
|
||||
27
packages/webawesome/scripts/update-root-version.js
Executable file
27
packages/webawesome/scripts/update-root-version.js
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import * as url from 'url';
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
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 currentVersion = webawesomePackageJSON.version;
|
||||
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/);
|
||||
|
||||
// TODO: Make this smart and understand semver and "insert" in the correct spot instead of appending.
|
||||
if (!versions.includes(currentVersion)) {
|
||||
fs.appendFileSync(versionsFile, webawesomePackageJSON.version);
|
||||
}
|
||||
@@ -115,7 +115,6 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
|
||||
* The "form owner" to associate the button with. If omitted, the closest containing form will be used instead. The
|
||||
* value of this attribute must be an id of a form in the same document or shadow root as the button.
|
||||
*/
|
||||
@property({ reflect: true }) form: string | null = null;
|
||||
|
||||
/** Used to override the form owner's `action` attribute. */
|
||||
@property({ attribute: 'formaction' }) formAction: string;
|
||||
@@ -135,24 +134,27 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
|
||||
|
||||
private constructLightDOMButton() {
|
||||
const button = document.createElement('button');
|
||||
|
||||
for (const attribute of this.attributes) {
|
||||
if (attribute.name === 'style') {
|
||||
// Skip style attributes as they *shouldn't* be necessary
|
||||
continue;
|
||||
}
|
||||
button.setAttribute(attribute.name, attribute.value);
|
||||
}
|
||||
|
||||
button.type = this.type;
|
||||
button.style.position = 'absolute';
|
||||
button.style.width = '0';
|
||||
button.style.height = '0';
|
||||
button.style.clipPath = 'inset(50%)';
|
||||
button.style.overflow = 'hidden';
|
||||
button.style.whiteSpace = 'nowrap';
|
||||
button.style.position = 'absolute !important';
|
||||
button.style.width = '0 !important';
|
||||
button.style.height = '0 !important';
|
||||
button.style.clipPath = 'inset(50%) !important';
|
||||
button.style.overflow = 'hidden !important';
|
||||
button.style.whiteSpace = 'nowrap !important';
|
||||
if (this.name) {
|
||||
button.name = this.name;
|
||||
}
|
||||
button.value = this.value || '';
|
||||
|
||||
['form', 'formaction', 'formenctype', 'formmethod', 'formnovalidate', 'formtarget'].forEach(attr => {
|
||||
if (this.hasAttribute(attr)) {
|
||||
button.setAttribute(attr, this.getAttribute(attr)!);
|
||||
}
|
||||
});
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,13 +108,6 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
|
||||
@property({ type: Boolean, reflect: true, attribute: 'checked' }) defaultChecked: boolean =
|
||||
this.hasAttribute('checked');
|
||||
|
||||
/**
|
||||
* By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
|
||||
* to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
|
||||
* the same document or shadow root for this to work.
|
||||
*/
|
||||
@property({ reflect: true }) form = null;
|
||||
|
||||
/** Makes the checkbox a required field. */
|
||||
@property({ type: Boolean, reflect: true }) required = false;
|
||||
|
||||
|
||||
@@ -220,13 +220,6 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
|
||||
*/
|
||||
@property() swatches: string | string[] = '';
|
||||
|
||||
/**
|
||||
* By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
|
||||
* to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
|
||||
* the same document or shadow root for this to work.
|
||||
*/
|
||||
@property({ reflect: true }) form = null;
|
||||
|
||||
/** Makes the color picker a required field. */
|
||||
@property({ type: Boolean, reflect: true }) required = false;
|
||||
|
||||
|
||||
@@ -140,13 +140,6 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
|
||||
/** Hides the browser's built-in increment/decrement spin buttons for number inputs. */
|
||||
@property({ attribute: 'without-spin-buttons', type: Boolean }) withoutSpinButtons = false;
|
||||
|
||||
/**
|
||||
* By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
|
||||
* to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
|
||||
* the same document or shadow root for this to work.
|
||||
*/
|
||||
@property({ reflect: true }) form = null;
|
||||
|
||||
/** Makes the input a required field. */
|
||||
@property({ type: Boolean, reflect: true }) required = false;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import getText from '../../internal/get-text.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import '../icon/icon.js';
|
||||
import type WaSelect from '../select/select.js';
|
||||
import styles from './option.styles.js';
|
||||
|
||||
/**
|
||||
@@ -109,7 +110,7 @@ export default class WaOption extends WebAwesomeElement {
|
||||
this.updateDefaultLabel();
|
||||
|
||||
if (this.isInitialized) {
|
||||
// When the label changes, tell the controller to update
|
||||
// When the label changes, tell the parent <wa-select> to update
|
||||
customElements.whenDefined('wa-select').then(() => {
|
||||
const controller = this.closest('wa-select');
|
||||
if (controller) {
|
||||
@@ -117,6 +118,16 @@ export default class WaOption extends WebAwesomeElement {
|
||||
controller.selectionChanged?.();
|
||||
}
|
||||
});
|
||||
|
||||
// When the label changes, tell the parent <wa-combobox> to update
|
||||
customElements.whenDefined('wa-combobox').then(() => {
|
||||
// We cast to <wa-select> because it shares the same API as combobox
|
||||
const controller = this.closest<WaSelect>('wa-combobox');
|
||||
if (controller) {
|
||||
controller.handleDefaultSlotChange();
|
||||
controller.selectionChanged?.();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.isInitialized = true;
|
||||
}
|
||||
@@ -134,7 +145,8 @@ export default class WaOption extends WebAwesomeElement {
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
||||
if (changedProperties.has('defaultSelected')) {
|
||||
if (!this.closest('wa-select')?.hasInteracted) {
|
||||
// We cast to <wa-select> because it shares the same API as combobox
|
||||
if (!this.closest<WaSelect>('wa-combobox, wa-select')?.hasInteracted) {
|
||||
const oldVal = this.selected;
|
||||
this.selected = this.defaultSelected;
|
||||
this.requestUpdate('selected', oldVal);
|
||||
|
||||
@@ -39,11 +39,6 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
/** @internal Used by radio group to force disable radios while preserving their original disabled state. */
|
||||
@state() forceDisabled = false;
|
||||
|
||||
/**
|
||||
* The string pointing to a form's id.
|
||||
*/
|
||||
@property({ reflect: true }) form: string | null = null;
|
||||
|
||||
/** The radio's value. When selected, the radio group will receive this value. */
|
||||
@property({ reflect: true }) value: string;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { WaAfterHideEvent } from '../../events/after-hide.js';
|
||||
import { WaAfterShowEvent } from '../../events/after-show.js';
|
||||
import { WaClearEvent } from '../../events/clear.js';
|
||||
import { WaHideEvent } from '../../events/hide.js';
|
||||
import type { WaRemoveEvent } from '../../events/remove.js';
|
||||
import { WaRemoveEvent } from '../../events/remove.js';
|
||||
import { WaShowEvent } from '../../events/show.js';
|
||||
import { animateWithClass } from '../../internal/animate.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
@@ -99,6 +99,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'hint', 'label');
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private selectionOrder: Map<string, number> = new Map();
|
||||
private typeToSelectString = '';
|
||||
private typeToSelectTimeout: number;
|
||||
|
||||
@@ -256,13 +257,6 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
*/
|
||||
@property({ attribute: 'with-hint', type: Boolean }) withHint = false;
|
||||
|
||||
/**
|
||||
* By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
|
||||
* to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
|
||||
* the same document or shadow root for this to work.
|
||||
*/
|
||||
@property({ reflect: true }) form = null;
|
||||
|
||||
/** The select's required attribute. */
|
||||
@property({ type: Boolean, reflect: true }) required = false;
|
||||
|
||||
@@ -285,6 +279,8 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
?pill=${this.pill}
|
||||
size=${this.size}
|
||||
with-remove
|
||||
data-value=${option.value}
|
||||
@wa-remove=${(event: WaRemoveEvent) => this.handleTagRemove(event, option)}
|
||||
>
|
||||
${option.label}
|
||||
</wa-tag>
|
||||
@@ -520,6 +516,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
event.stopPropagation();
|
||||
|
||||
if (this.value !== null) {
|
||||
this.selectionOrder.clear();
|
||||
this.setSelectedOptions([]);
|
||||
this.displayInput.focus({ preventScroll: true });
|
||||
|
||||
@@ -603,24 +600,20 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
|
||||
if (this.disabled) return;
|
||||
|
||||
// Mark as interacted so selectionChanged() uses the correct filter logic
|
||||
this.hasInteracted = true;
|
||||
this.valueHasChanged = true;
|
||||
|
||||
// Use the directly provided option if available (from getTag method)
|
||||
let option = directOption;
|
||||
|
||||
// If no direct option was provided, find the option from the event path
|
||||
// If no direct option was provided, find the option from the data-value attribute
|
||||
if (!option) {
|
||||
const tagElement = (event.target as Element).closest('wa-tag[part~=tag]');
|
||||
const tagElement = (event.target as Element).closest('wa-tag[data-value]') as HTMLElement | null;
|
||||
|
||||
if (tagElement) {
|
||||
// Find the index of this tag among all tags
|
||||
const tagsContainer = this.shadowRoot?.querySelector('[part="tags"]');
|
||||
if (tagsContainer) {
|
||||
const allTags = Array.from(tagsContainer.children);
|
||||
const index = allTags.indexOf(tagElement as HTMLElement);
|
||||
|
||||
if (index >= 0 && index < this.selectedOptions.length) {
|
||||
option = this.selectedOptions[index];
|
||||
}
|
||||
}
|
||||
const value = tagElement.dataset.value;
|
||||
option = this.selectedOptions.find(opt => opt.value === value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -707,7 +700,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
const options = this.getAllOptions();
|
||||
|
||||
// Update selected options cache
|
||||
this.selectedOptions = options.filter(el => {
|
||||
const newSelectedOptions = options.filter(el => {
|
||||
if (!this.hasInteracted && !this.valueHasChanged) {
|
||||
const defaultValue = this.defaultValue;
|
||||
const defaultValues = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
|
||||
@@ -717,6 +710,32 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
return el.selected;
|
||||
});
|
||||
|
||||
// Update the selection order map
|
||||
const newSelectedValues = new Set(newSelectedOptions.map(el => el.value));
|
||||
|
||||
// Remove deselected options from the order map
|
||||
for (const value of this.selectionOrder.keys()) {
|
||||
if (!newSelectedValues.has(value)) {
|
||||
this.selectionOrder.delete(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Add newly selected options
|
||||
const maxOrder = this.selectionOrder.size > 0 ? Math.max(...this.selectionOrder.values()) : -1;
|
||||
let nextOrder = maxOrder + 1;
|
||||
for (const option of newSelectedOptions) {
|
||||
if (!this.selectionOrder.has(option.value)) {
|
||||
this.selectionOrder.set(option.value, nextOrder++);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort options by selection order
|
||||
this.selectedOptions = newSelectedOptions.sort((a, b) => {
|
||||
const orderA = this.selectionOrder.get(a.value) ?? 0;
|
||||
const orderB = this.selectionOrder.get(b.value) ?? 0;
|
||||
return orderA - orderB;
|
||||
});
|
||||
|
||||
let selectedValues = new Set(this.selectedOptions.map(el => el.value));
|
||||
|
||||
// Toggle values present in the DOM from this.value, while preserving options NOT present in the DOM (for lazy loading)
|
||||
@@ -888,6 +907,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
}
|
||||
|
||||
formResetCallback() {
|
||||
this.selectionOrder.clear();
|
||||
this.value = this.defaultValue;
|
||||
super.formResetCallback();
|
||||
this.handleValueChange();
|
||||
|
||||
@@ -167,12 +167,6 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
|
||||
/** The starting value from which to draw the slider's fill, which is based on its current value. */
|
||||
@property({ attribute: 'indicator-offset', type: Number }) indicatorOffset: number;
|
||||
|
||||
/**
|
||||
* The form to associate this control with. If omitted, the closest containing `<form>` will be used. The value of
|
||||
* this attribute must be an ID of a form in the same document or shadow root.
|
||||
*/
|
||||
@property({ reflect: true }) form = null;
|
||||
|
||||
/** The minimum value allowed. */
|
||||
@property({ type: Number }) min: number = 0;
|
||||
|
||||
@@ -437,8 +431,14 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
|
||||
/** Clamps a number to min/max while ensuring it's a valid step interval. */
|
||||
private clampAndRoundToStep(value: number) {
|
||||
const stepPrecision = (String(this.step).split('.')[1] || '').replace(/0+$/g, '').length;
|
||||
value = Math.round(value / this.step) * this.step;
|
||||
value = clamp(value, this.min, this.max);
|
||||
|
||||
// Ensure we're working with numbers (in case the user passes strings to the respective properties)
|
||||
const step = Number(this.step);
|
||||
const min = Number(this.min);
|
||||
const max = Number(this.max);
|
||||
|
||||
value = Math.round(value / step) * step;
|
||||
value = clamp(value, min, max);
|
||||
|
||||
return parseFloat(value.toFixed(stepPrecision));
|
||||
}
|
||||
|
||||
@@ -80,13 +80,6 @@ export default class WaSwitch extends WebAwesomeFormAssociatedElement {
|
||||
@property({ type: Boolean, attribute: 'checked', reflect: true }) defaultChecked: boolean =
|
||||
this.hasAttribute('checked');
|
||||
|
||||
/**
|
||||
* By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
|
||||
* to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
|
||||
* the same document or shadow root for this to work.
|
||||
*/
|
||||
@property({ reflect: true }) form = null;
|
||||
|
||||
/** Makes the switch a required field. */
|
||||
@property({ type: Boolean, reflect: true }) required = false;
|
||||
|
||||
|
||||
@@ -107,13 +107,6 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
|
||||
/** Makes the textarea readonly. */
|
||||
@property({ type: Boolean, reflect: true }) readonly = false;
|
||||
|
||||
/**
|
||||
* By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
|
||||
* to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
|
||||
* the same document or shadow root for this to work.
|
||||
*/
|
||||
@property({ reflect: true }) form = null;
|
||||
|
||||
/** Makes the textarea a required field. */
|
||||
@property({ type: Boolean, reflect: true }) required = false;
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ export interface WebAwesomeFormControl extends WebAwesomeElement {
|
||||
checked?: boolean;
|
||||
defaultSelected?: boolean;
|
||||
selected?: boolean;
|
||||
form?: string | null;
|
||||
get form(): HTMLFormElement | null;
|
||||
set form(val: string);
|
||||
|
||||
value?: unknown;
|
||||
|
||||
@@ -203,6 +204,23 @@ export class WebAwesomeFormAssociatedElement
|
||||
return this.internals.form;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
|
||||
* to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
|
||||
* the same document or shadow root for this to work.
|
||||
*/
|
||||
set form(val: string) {
|
||||
if (val) {
|
||||
this.setAttribute('form', val);
|
||||
} else {
|
||||
this.removeAttribute('form');
|
||||
}
|
||||
}
|
||||
|
||||
get form(): HTMLFormElement | null {
|
||||
return this.internals.form;
|
||||
}
|
||||
|
||||
@property({ attribute: false, state: true, type: Object })
|
||||
get validity() {
|
||||
return this.internals.validity;
|
||||
|
||||
Reference in New Issue
Block a user