mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-19 07:29:14 +00:00
Compare commits
46 Commits
konnorroge
...
tree-focus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
886049d714 | ||
|
|
848c059d51 | ||
|
|
8ffbd02db7 | ||
|
|
e88d57d17d | ||
|
|
5f4de6d9f5 | ||
|
|
2cce87deeb | ||
|
|
630b5b19a0 | ||
|
|
2ce1451a9f | ||
|
|
2d1badba96 | ||
|
|
1b5db078a7 | ||
|
|
91095bd63a | ||
|
|
d9703a64fd | ||
|
|
4c22e72390 | ||
|
|
d05b8fca20 | ||
|
|
afca2ad2e0 | ||
|
|
b2aa854d98 | ||
|
|
287fff7cf1 | ||
|
|
136ecae4a6 | ||
|
|
cac772d5e6 | ||
|
|
e1dedcb1b5 | ||
|
|
c4901eca68 | ||
|
|
a001c2d12b | ||
|
|
c4c622eabd | ||
|
|
1ae018bedd | ||
|
|
24929e27c1 | ||
|
|
33a8d92aec | ||
|
|
32d21fa560 | ||
|
|
347d8b7f79 | ||
|
|
8f9c15913b | ||
|
|
60d7f688eb | ||
|
|
15f914914c | ||
|
|
2914475821 | ||
|
|
8e831aa3e7 | ||
|
|
985d4585c4 | ||
|
|
854db13bd7 | ||
|
|
4ddf80459a | ||
|
|
89fc2ff643 | ||
|
|
d7145f1f84 | ||
|
|
441a957432 | ||
|
|
67cbb85682 | ||
|
|
0005d16a06 | ||
|
|
ca5ab03cd4 | ||
|
|
c9e30022df | ||
|
|
c167bdd80f | ||
|
|
b9f62bb1bc | ||
|
|
a01b2cf8a2 |
@@ -185,6 +185,17 @@ module.exports = {
|
||||
]
|
||||
}
|
||||
],
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'always',
|
||||
{
|
||||
ignorePackages: true,
|
||||
pattern: {
|
||||
js: 'always',
|
||||
ts: 'never'
|
||||
}
|
||||
}
|
||||
],
|
||||
'import/no-duplicates': 'warn',
|
||||
'sort-imports-es6-autofix/sort-imports-es6': [
|
||||
2,
|
||||
|
||||
@@ -9,3 +9,5 @@ src/react/index.ts
|
||||
node_modules
|
||||
package-lock.json
|
||||
tsconfig.json
|
||||
cdn
|
||||
_site
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
<td class="nowrap"><code>updateComplete</code></td>
|
||||
<td>
|
||||
A read-only promise that resolves when the component has
|
||||
<a href="/getting-started/usage?id=component-rendering-and-updating">finished updating</a>.
|
||||
<a href="/getting-started/usage?#component-rendering-and-updating">finished updating</a>.
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
|
||||
@@ -34,16 +34,16 @@
|
||||
<meta property="og:image" content="{{ assetUrl(meta.image, true) }}" />
|
||||
|
||||
{# Shoelace #}
|
||||
<link rel="stylesheet" href="/cdn/themes/light.css" />
|
||||
<link rel="stylesheet" href="/cdn/themes/dark.css" />
|
||||
<script type="module" src="/cdn/shoelace-autoloader.js"></script>
|
||||
<link rel="stylesheet" href="/dist/themes/light.css" />
|
||||
<link rel="stylesheet" href="/dist/themes/dark.css" />
|
||||
<script type="module" src="/dist/shoelace-autoloader.js"></script>
|
||||
|
||||
{# Set the initial theme and menu states here to prevent flashing #}
|
||||
<script>
|
||||
(() => {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const theme = localStorage.getItem('theme');
|
||||
document.documentElement.classList.toggle('sl-theme-dark', theme === 'dark' || (!theme && prefersDark));
|
||||
const theme = localStorage.getItem('theme') || 'auto';
|
||||
document.documentElement.classList.toggle('sl-theme-dark', theme === 'dark' || (theme === 'auto' && prefersDark));
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -80,16 +80,19 @@
|
||||
<sl-icon name="twitter"></sl-icon>
|
||||
</a>
|
||||
|
||||
{# Theme toggle #}
|
||||
<button
|
||||
id="theme-toggle"
|
||||
type="button"
|
||||
aria-label="Toggle light and dark theme"
|
||||
title="Toggle theme (press backslash)"
|
||||
>
|
||||
<sl-icon class="only-light" name="sun-fill"></sl-icon>
|
||||
<sl-icon class="only-dark" name="moon-fill"></sl-icon>
|
||||
</button>
|
||||
{# Theme selector #}
|
||||
<sl-dropdown id="theme-selector" placement="bottom-end" distance="3">
|
||||
<sl-button slot="trigger" size="small" variant="text" caret title="Press \ to toggle">
|
||||
<sl-icon class="only-light" name="sun-fill"></sl-icon>
|
||||
<sl-icon class="only-dark" name="moon-fill"></sl-icon>
|
||||
</sl-button>
|
||||
<sl-menu>
|
||||
<sl-menu-item type="checkbox" value="light">Light</sl-menu-item>
|
||||
<sl-menu-item type="checkbox" value="dark">Dark</sl-menu-item>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item type="checkbox" value="auto">System</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
</div>
|
||||
|
||||
<aside id="sidebar" data-preserve-scroll>
|
||||
@@ -103,12 +106,12 @@
|
||||
</header>
|
||||
|
||||
<div class="sidebar-buttons">
|
||||
<sl-button size="small" class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
|
||||
<sl-icon slot="prefix" name="heart"></sl-icon> Sponsor
|
||||
</sl-button>
|
||||
<sl-button size="small" class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace" target="_blank">
|
||||
<sl-icon slot="prefix" name="github"></sl-icon> Code
|
||||
</sl-button>
|
||||
<sl-button size="small" class="repo-button repo-button--star" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
|
||||
<sl-icon slot="prefix" name="star-fill"></sl-icon> Star
|
||||
</sl-button>
|
||||
<sl-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
|
||||
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
|
||||
</sl-button>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
</li>
|
||||
<li>
|
||||
<h2>Design Tokens</h2>
|
||||
<ul>
|
||||
@@ -51,7 +51,7 @@
|
||||
<li><a href="/tokens/border-radius">Border Radius</a></li>
|
||||
<li><a href="/tokens/transition">Transition</a></li>
|
||||
<li><a href="/tokens/z-index">Z-index</a></li>
|
||||
<li><a href="/tokens/more">More</a></li>
|
||||
<li><a href="/tokens/more">More Tokens</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
11
docs/assets/images/awesome.svg
Normal file
11
docs/assets/images/awesome.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="733" xmlns="http://www.w3.org/2000/svg" height="733">
|
||||
<circle cy="366.5" cx="366.5" r="366.5"/>
|
||||
<circle cy="366.5" cx="366.5" r="336.5" fill="#fede58"/>
|
||||
<path d="M325 665c-121-21-194-115-212-233v-8l-25-1-1-18h481c6 13 10 27 13 41 13 94-38 146-114 193-45 23-93 29-142 26z"/>
|
||||
<path d="M372 647c52-6 98-28 138-62 28-25 46-56 51-87 4-20 1-57-5-70l-423-1c-2 56 39 118 74 157 31 34 72 54 116 63 11 2 38 2 49 0z" fill="#871945"/>
|
||||
<path d="M76 342c-13-26-13-57-9-85 6-27 18-52 35-68 21-20 50-23 77-18 15 4 28 12 39 23 18 17 30 40 36 67 4 20 4 41 0 60l-6 21z"/>
|
||||
<path d="M234 323c5-6 6-40 2-58-3-16-4-16-10-10-14 14-38 14-52 0-15-18-12-41 6-55 3-3 5-5 5-6-1-4-22-8-34-7-42 4-57.6 40-66.2 77-3 17-1 53 4 59H234z" fill="#fff"/>
|
||||
<path d="M378 343c-2-3-6-20-7-29-5-28-1-57 11-83 15-30 41-52 72-60 29-7 57 0 82 15 26 17 45 49 50 82 2 12 2 33 0 45-1 10-5 26-8 30z"/>
|
||||
<path d="M565 324c4-5 5-34 4-50-2-14-6-24-8-24-1 0-3 2-6 5-17 17-47 13-58-9-7-16-4-31 8-43 4-4 7-8 7-9 0 0-4-2-8-3-51-17-105 20-115 80-3 15 0 43 3 53z" fill="#fff"/>
|
||||
<path d="M504 590s-46 40-105 53c-66 15-114-7-114-7s14-76 93-95c76-18 126 49 126 49z" fill="#f9bedd"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -48,8 +48,26 @@
|
||||
document.documentElement.classList.toggle('flavor-react', flavor === 'react');
|
||||
}
|
||||
|
||||
function syncFlavor() {
|
||||
setFlavor(getFlavor());
|
||||
|
||||
document.querySelectorAll('.code-preview__button--html').forEach(preview => {
|
||||
if (flavor === 'html') {
|
||||
preview.classList.add('code-preview__button--selected');
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.code-preview__button--react').forEach(preview => {
|
||||
if (flavor === 'react') {
|
||||
preview.classList.add('code-preview__button--selected');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const shoelaceVersion = document.documentElement.getAttribute('data-shoelace-version');
|
||||
const reactVersion = '18.2.0';
|
||||
const cdndir = 'cdn';
|
||||
const npmdir = 'dist';
|
||||
let flavor = getFlavor();
|
||||
let count = 1;
|
||||
|
||||
@@ -59,19 +77,7 @@
|
||||
}
|
||||
|
||||
// Sync flavor UI on page load
|
||||
setFlavor(getFlavor());
|
||||
|
||||
document.querySelectorAll('.code-preview__button--html').forEach(preview => {
|
||||
if (flavor === 'html') {
|
||||
preview.classList.add('code-preview__button--selected');
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.code-preview__button--react').forEach(preview => {
|
||||
if (flavor === 'react') {
|
||||
preview.classList.add('code-preview__button--selected');
|
||||
}
|
||||
});
|
||||
syncFlavor();
|
||||
|
||||
//
|
||||
// Resizing previews
|
||||
@@ -145,12 +151,8 @@
|
||||
});
|
||||
|
||||
function toggleSource(codeBlock, force) {
|
||||
const toggle = codeBlock.querySelector('.code-preview__toggle');
|
||||
|
||||
if (toggle) {
|
||||
codeBlock.classList.toggle('code-preview--expanded', force === undefined ? undefined : force);
|
||||
event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-preview--expanded'));
|
||||
}
|
||||
codeBlock.classList.toggle('code-preview--expanded', force);
|
||||
event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-preview--expanded'));
|
||||
}
|
||||
|
||||
//
|
||||
@@ -180,7 +182,7 @@
|
||||
// HTML templates
|
||||
if (!isReact) {
|
||||
htmlTemplate =
|
||||
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${shoelaceVersion}/%CDNDIR%/shoelace.js"></script>\n` +
|
||||
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${shoelaceVersion}/${cdndir}/shoelace.js"></script>\n` +
|
||||
`\n${htmlExample}`;
|
||||
jsTemplate = '';
|
||||
}
|
||||
@@ -191,10 +193,10 @@
|
||||
jsTemplate =
|
||||
`import React from 'https://cdn.skypack.dev/react@${reactVersion}';\n` +
|
||||
`import ReactDOM from 'https://cdn.skypack.dev/react-dom@${reactVersion}';\n` +
|
||||
`import { setBasePath } from 'https://cdn.skypack.dev/@shoelace-style/shoelace@${shoelaceVersion}/%CDNDIR%/utilities/base-path';\n` +
|
||||
`import { setBasePath } from 'https://cdn.skypack.dev/@shoelace-style/shoelace@${shoelaceVersion}/${cdndir}/utilities/base-path';\n` +
|
||||
`\n` +
|
||||
`// Set the base path for Shoelace assets\n` +
|
||||
`setBasePath('https://cdn.skypack.dev/@shoelace-style/shoelace@${shoelaceVersion}/%NPMDIR%/')\n` +
|
||||
`setBasePath('https://cdn.skypack.dev/@shoelace-style/shoelace@${shoelaceVersion}/${npmdir}/')\n` +
|
||||
`\n${convertModuleLinks(reactExample)}\n` +
|
||||
`\n` +
|
||||
`ReactDOM.render(<App />, document.getElementById('root'));`;
|
||||
@@ -202,7 +204,7 @@
|
||||
|
||||
// CSS templates
|
||||
cssTemplate =
|
||||
`@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${shoelaceVersion}/%CDNDIR%/themes/${
|
||||
`@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${shoelaceVersion}/${cdndir}/themes/${
|
||||
isDark ? 'dark' : 'light'
|
||||
}.css';\n` +
|
||||
'\n' +
|
||||
@@ -241,4 +243,7 @@
|
||||
form.remove();
|
||||
}
|
||||
});
|
||||
|
||||
// Set the initial flavor
|
||||
window.addEventListener('turbo:load', syncFlavor);
|
||||
})();
|
||||
|
||||
@@ -74,22 +74,56 @@
|
||||
})();
|
||||
|
||||
//
|
||||
// Theme switcher
|
||||
// Theme selector
|
||||
//
|
||||
(() => {
|
||||
function toggleTheme() {
|
||||
const isDark = !document.documentElement.classList.contains('sl-theme-dark');
|
||||
document.documentElement.classList.toggle('sl-theme-dark', isDark);
|
||||
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
||||
function getTheme() {
|
||||
return localStorage.getItem('theme') || 'auto';
|
||||
}
|
||||
|
||||
// Toggle the theme
|
||||
document.addEventListener('click', event => {
|
||||
const themeToggle = event.target.closest('#theme-toggle');
|
||||
if (!themeToggle) return;
|
||||
toggleTheme();
|
||||
function isDark() {
|
||||
if (theme === 'auto') {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
return theme === 'dark';
|
||||
}
|
||||
|
||||
function setTheme(newTheme) {
|
||||
theme = newTheme;
|
||||
localStorage.setItem('theme', theme);
|
||||
|
||||
// Update the UI
|
||||
updateSelection();
|
||||
|
||||
// Toggle the dark mode class
|
||||
document.documentElement.classList.toggle('sl-theme-dark', isDark());
|
||||
}
|
||||
|
||||
function updateSelection() {
|
||||
const menu = document.querySelector('#theme-selector sl-menu');
|
||||
if (!menu) return;
|
||||
[...menu.querySelectorAll('sl-menu-item')].map(item => (item.checked = item.getAttribute('value') === theme));
|
||||
}
|
||||
|
||||
let theme = getTheme();
|
||||
|
||||
// Selection is not preserved when changing page, so update when opening dropdown
|
||||
document.addEventListener('sl-show', event => {
|
||||
const themeSelector = event.target.closest('#theme-selector');
|
||||
if (!themeSelector) return;
|
||||
updateSelection();
|
||||
});
|
||||
|
||||
// Listen for selections
|
||||
document.addEventListener('sl-select', event => {
|
||||
const menu = event.target.closest('#theme-selector sl-menu');
|
||||
if (!menu) return;
|
||||
setTheme(event.detail.item.value);
|
||||
});
|
||||
|
||||
// Update the theme when the preference changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => setTheme(theme));
|
||||
|
||||
// Toggle with backslash
|
||||
document.addEventListener('keydown', event => {
|
||||
if (
|
||||
@@ -97,9 +131,12 @@
|
||||
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
|
||||
) {
|
||||
event.preventDefault();
|
||||
toggleTheme();
|
||||
setTheme(isDark() ? 'light' : 'dark');
|
||||
}
|
||||
});
|
||||
|
||||
// Set the initial theme and sync the UI
|
||||
setTheme(theme);
|
||||
})();
|
||||
|
||||
//
|
||||
|
||||
@@ -1054,6 +1054,31 @@ html.sidebar-open #menu-toggle {
|
||||
transition: 250ms scale ease;
|
||||
}
|
||||
|
||||
#theme-selector:not(:defined) {
|
||||
/* Hide when not defined to prevent extra wide icon toolbar while loading */
|
||||
display: none;
|
||||
}
|
||||
#theme-selector sl-menu {
|
||||
/* Set an initial size to prevent width being initally too small when first opening on small screen width */
|
||||
width: 140px;
|
||||
}
|
||||
#theme-selector sl-button {
|
||||
transition: 250ms scale ease;
|
||||
}
|
||||
#theme-selector sl-button::part(base) {
|
||||
color: var(--sl-color-neutral-0);
|
||||
}
|
||||
#theme-selector sl-button::part(label) {
|
||||
display: flex;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
#theme-selector sl-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.sl-theme-dark #theme-selector sl-button::part(base) {
|
||||
color: var(--sl-color-neutral-1000);
|
||||
}
|
||||
|
||||
.sl-theme-dark #icon-toolbar {
|
||||
background: var(--sl-color-neutral-200);
|
||||
}
|
||||
@@ -1064,7 +1089,8 @@ html.sidebar-open #menu-toggle {
|
||||
}
|
||||
|
||||
#icon-toolbar button:hover,
|
||||
#icon-toolbar a:hover {
|
||||
#icon-toolbar a:hover,
|
||||
#theme-selector sl-button:hover {
|
||||
scale: 1.1;
|
||||
}
|
||||
|
||||
@@ -1084,6 +1110,10 @@ html.sidebar-open #menu-toggle {
|
||||
font-size: 1rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
#theme-selector sl-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar addons */
|
||||
@@ -1173,6 +1203,12 @@ html.sidebar-open #menu-toggle {
|
||||
font-weight: var(--sl-font-weight-normal);
|
||||
}
|
||||
|
||||
.splash li img {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -2px;
|
||||
}
|
||||
|
||||
.splash-end {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
@@ -1219,19 +1255,6 @@ html.sidebar-open #menu-toggle {
|
||||
}
|
||||
}
|
||||
|
||||
/* Repo buttons */
|
||||
.repo-button--sponsor sl-icon {
|
||||
color: var(--sl-color-pink-600);
|
||||
}
|
||||
|
||||
.repo-button--github sl-icon {
|
||||
color: var(--sl-color-neutral-700);
|
||||
}
|
||||
|
||||
.repo-button--twitter sl-icon {
|
||||
color: var(--sl-color-sky-500);
|
||||
}
|
||||
|
||||
/* Component headers */
|
||||
.component-header h1 {
|
||||
margin-bottom: 0;
|
||||
@@ -1261,14 +1284,24 @@ html.sidebar-open #menu-toggle {
|
||||
}
|
||||
|
||||
/* Repo buttons */
|
||||
.repo-button--sponsor sl-icon {
|
||||
color: var(--sl-color-pink-600);
|
||||
.sidebar-buttons {
|
||||
display: flex;
|
||||
gap: 0.125rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sidebar-buttons .repo-button {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.repo-button--github sl-icon {
|
||||
color: var(--sl-color-neutral-700);
|
||||
}
|
||||
|
||||
.repo-button--star sl-icon {
|
||||
color: var(--sl-color-yellow-500);
|
||||
}
|
||||
|
||||
.repo-button--twitter sl-icon {
|
||||
color: var(--sl-color-sky-500);
|
||||
}
|
||||
|
||||
@@ -506,7 +506,7 @@ const App = () => {
|
||||
|
||||
### Multiple Slides Per View
|
||||
|
||||
The `slides-per-view` attribute makes it possible to display multiple slides at a time. You can also use the `slides-per-move` attribute to advance more than once slide at a time, if desired.
|
||||
The `slides-per-page` attribute makes it possible to display multiple slides at a time. You can also use the `slides-per-move` attribute to advance more than once slide at a time, if desired.
|
||||
|
||||
```html:preview
|
||||
<sl-carousel navigation pagination slides-per-page="2" slides-per-move="2">
|
||||
|
||||
@@ -289,7 +289,7 @@ const App = () => (
|
||||
|
||||
### Hoisting
|
||||
|
||||
Dropdown panels will be clipped if they're inside a container that has `overflow: auto|hidden`. The `hoist` attribute forces the panel to use a fixed positioning strategy, allowing it to break out of the container. In this case, the panel will be positioned relative to its containing block, which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
|
||||
Dropdown panels will be clipped if they're inside a container that has `overflow: auto|hidden`. The `hoist` attribute forces the panel to use a fixed positioning strategy, allowing it to break out of the container. In this case, the panel will be positioned relative to its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
|
||||
|
||||
```html:preview
|
||||
<div class="dropdown-hoist">
|
||||
|
||||
@@ -634,6 +634,39 @@ This example will load the same set of icons from the jsDelivr CDN instead of yo
|
||||
</script>
|
||||
```
|
||||
|
||||
#### Customize the default library to use SVG sprites
|
||||
|
||||
To improve performance you can use a SVG sprites to avoid multiple trips for each SVG. The browser will load the sprite sheet once and then you reference the particular SVG within the sprite sheet using hash selector.
|
||||
|
||||
As always, make sure to benchmark these changes. When using HTTP/2, it may in fact be more bandwidth-friendly to use multiple small requests instead of 1 large sprite sheet.
|
||||
|
||||
:::danger
|
||||
When using sprite sheets, the `sl-load` and `sl-error` events will not fire.
|
||||
:::
|
||||
|
||||
:::danger
|
||||
For security reasons, browsers may apply the same-origin policy on `<use>` elements located in the `<sl-icon>` shadow dom and
|
||||
may refuse to load a cross-origin URL. There is currently no defined way to set a cross-origin policy for `<use>` elements.
|
||||
For this reason, sprite sheets should only be used if you're self-hosting them.
|
||||
:::
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('sprite', {
|
||||
resolver: name => `/assets/images/sprite.svg#${name}`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor'),
|
||||
spriteSheet: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<sl-icon library="sprite" name="clock"></sl-icon>
|
||||
<sl-icon library="sprite" name="speedometer"></sl-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Customizing the System Library
|
||||
|
||||
The system library contains only the icons used internally by Shoelace components. Unlike the default icon library, the system library does not rely on physical assets. Instead, its icons are hard-coded as data URIs into the resolver to ensure their availability.
|
||||
@@ -666,7 +699,7 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
}
|
||||
|
||||
fetch('/dist/assets/icons/icons.json')
|
||||
.then(res => res.json())
|
||||
.then(res => res.json())
|
||||
.then(icons => {
|
||||
const container = document.querySelector('.icon-search');
|
||||
const input = container.querySelector('sl-input');
|
||||
@@ -685,12 +718,12 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
item.setAttribute('data-terms', [i.name, i.title, ...(i.tags || []), ...(i.categories || [])].join(' '));
|
||||
item.innerHTML = `
|
||||
<svg width="1em" height="1em" fill="currentColor">
|
||||
<use xlink:href="/assets/images/sprite.svg#${i.name}"></use>
|
||||
</svg>
|
||||
<use href="/assets/images/sprite.svg#${i.name}"></use>
|
||||
</svg>
|
||||
`;
|
||||
list.appendChild(item);
|
||||
|
||||
// Wrap it with a tooltip the first time the mouse lands on it. We do this instead of baking them into the DOM
|
||||
// Wrap it with a tooltip the first time the mouse lands on it. We do this instead of baking them into the DOM
|
||||
// to improve this page's performance. See: https://github.com/shoelace-style/shoelace/issues/1122
|
||||
item.addEventListener('mouseover', () => wrapWithTooltip(item), { once: true });
|
||||
|
||||
@@ -833,6 +866,6 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
@media screen and (max-width: 500px) {
|
||||
.icon-list {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -984,7 +984,7 @@ const App = () => {
|
||||
|
||||
By default, the popup is positioned using an absolute positioning strategy. However, if your anchor is fixed or exists within a container that has `overflow: auto|hidden`, the popup risks being clipped. To work around this, you can use a fixed positioning strategy by setting the `strategy` attribute to `fixed`.
|
||||
|
||||
The fixed positioning strategy reduces jumpiness when the anchor is fixed and allows the popup to break out containers that clip. When using this strategy, it's important to note that the content will be positioned _relative to its containing block_, which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
|
||||
The fixed positioning strategy reduces jumpiness when the anchor is fixed and allows the popup to break out containers that clip. When using this strategy, it's important to note that the content will be positioned relative to its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
|
||||
|
||||
In this example, you can see how the popup breaks out of the overflow container when it's fixed. The fixed positioning strategy tends to be less performant than absolute, so avoid using it unnecessarily.
|
||||
|
||||
|
||||
@@ -378,7 +378,7 @@ const App = () => (
|
||||
|
||||
### Hoisting
|
||||
|
||||
Tooltips will be clipped if they're inside a container that has `overflow: auto|hidden|scroll`. The `hoist` attribute forces the tooltip to use a fixed positioning strategy, allowing it to break out of the container. In this case, the tooltip will be positioned relative to its containing block, which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
|
||||
Tooltips will be clipped if they're inside a container that has `overflow: auto|hidden|scroll`. The `hoist` attribute forces the tooltip to use a fixed positioning strategy, allowing it to break out of the container. In this case, the tooltip will be positioned relative to its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
|
||||
|
||||
```html:preview
|
||||
<div class="tooltip-hoist">
|
||||
|
||||
@@ -108,9 +108,9 @@ Custom elements cannot have self-closing tags. Similar to `<script>` and `<texta
|
||||
|
||||
## Differences from Native Elements
|
||||
|
||||
You might expect similarly named elements to share the same API as native HTML elements. This is not always the case. Shoelace components **are not** designed to be one-to-one replacements for their HTML counterparts.
|
||||
You might expect similarly named elements to share the same API as native HTML elements, but this is not always the case. Shoelace components **are not** designed to be one-to-one replacements for their HTML counterparts. While they usually share the same API, there may be subtle differences.
|
||||
|
||||
For example, `<button>` and `<sl-button>` both have a `type` attribute, but it does different things. The former controls whether the button submits a form and the latter controls the button's appearance.
|
||||
For example, `<button>` and `<sl-button>` both have a `type` attribute, but the native one defaults to `submit` while the Shoelace one defaults to `button` since this is a better default for most users.
|
||||
|
||||
:::tip
|
||||
**Don't make assumptions about a component's API!** To prevent unexpected behaviors, please take the time to review the documentation and make sure you understand what each attribute, property, method, and event is intended to do.
|
||||
|
||||
@@ -19,8 +19,7 @@ toc: false
|
||||
- First-class [React support](/frameworks/react) ⚛️
|
||||
- Built-in localization 💬
|
||||
- Open source 😸
|
||||
|
||||
Designed in New Hampshire by [Cory LaViska](https://twitter.com/claviska).
|
||||
- [More awesome than ever](https://blog.fontawesome.com/shoelace-joins-font-awesome/) 
|
||||
|
||||
</div>
|
||||
<div class="splash-end">
|
||||
@@ -105,23 +104,7 @@ If you need to support IE11 or pre-Chromium Edge, this library isn't for you. Al
|
||||
|
||||
## License
|
||||
|
||||
Shoelace is designed in New Hampshire by [Cory LaViska](https://twitter.com/claviska). It's available under the terms of the MIT license.
|
||||
|
||||
Designing, developing, and supporting this library requires a lot of time, effort, and skill. If you're using this software to make a profit, I respectfully ask that you help [fund its development](https://github.com/sponsors/claviska) by becoming a sponsor.
|
||||
|
||||
👇 Your support is very much appreciated! 👇
|
||||
|
||||
<sl-button class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
|
||||
<sl-icon slot="prefix" name="heart"></sl-icon> Become a sponsor
|
||||
</sl-button>
|
||||
|
||||
<sl-button class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
|
||||
<sl-icon slot="prefix" name="github"></sl-icon> Star
|
||||
</sl-button>
|
||||
|
||||
<sl-button class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
|
||||
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
|
||||
</sl-button>
|
||||
Shoelace was created in New Hampshire by [Cory LaViska](https://twitter.com/claviska). It's available under the terms of the [MIT license](https://github.com/shoelace-style/shoelace/blob/next/LICENSE.md).
|
||||
|
||||
## Attribution
|
||||
|
||||
|
||||
@@ -14,6 +14,25 @@ New versions of Shoelace are released as-needed and generally occur when a criti
|
||||
|
||||
## Next
|
||||
|
||||
- Added tests for `<sl-qr-code>` [#1416]
|
||||
- Fixed a bug in `<sl-qr-code>` where the `background` attribute was never passed to the QR code [#1416]
|
||||
- Fixed a bug in `<sl-dropdown>` where aria attributes were incorrectly applied to the default `<slot>` causing Lighthouse errors [#1417]
|
||||
- Fixed a bug in `<sl-carousel>` that caused navigation to work incorrectly in some case [#1420]
|
||||
- Fixed a bug in `<sl-tree>` that caused focus to be stolen when removing focused tree items [#1430]
|
||||
|
||||
## 2.5.2
|
||||
|
||||
- Fixed broken source buttons in the docs [#1401]
|
||||
- Fixed broken links in the docs [#1407]
|
||||
|
||||
## 2.5.1
|
||||
|
||||
- Fixed missing extensions from imports that broke with TypeScript 5 [#1391]
|
||||
- Fixed a regression that caused slotted styles to not work in `<sl-select>` [#1387]
|
||||
- Reimplemented the theme switcher so it supports light, dark, and system (auto) in the docs [#1395]
|
||||
|
||||
## 2.5.0
|
||||
|
||||
This release [unbundles Lit](https://github.com/shoelace-style/shoelace/issues/559) (and other dependencies) from Shoelace. There are now two distributions for the project:
|
||||
|
||||
1. `cdn/` – a bundled, CDN-ready distribution
|
||||
@@ -23,21 +42,24 @@ This release [unbundles Lit](https://github.com/shoelace-style/shoelace/issues/5
|
||||
If you're a CDN user, you must update your path to point to `cdn/` instead of `dist/`. You can copy and paste the latest paths from the [installation page](/getting-started/installation).
|
||||
:::
|
||||
|
||||
- Added a `cdn/` distribution for bundled dependencies (imports for npm users remain the same) [#1369](https://github.com/shoelace-style/shoelace/pull/1369)
|
||||
- Added the `checkbox` part and related exported parts to `<sl-tree-item>` so you can target it with CSS [#1318](https://github.com/shoelace-style/shoelace/discussions/1318)
|
||||
- Added a `cdn/` distribution for bundled dependencies (imports for npm users remain the same) [#1369]
|
||||
- Added the `checkbox` part and related exported parts to `<sl-tree-item>` so you can target it with CSS [#1318]
|
||||
- Added the `submenu-icon` part to `<sl-menu-item>` (submenus have not been implemented yet, but this part is required to allow customizations)
|
||||
- Added tests for `<sl-split-panel>` [#1343](https://github.com/shoelace-style/shoelace/pull/1343)
|
||||
- Added the ability to use Sprite Sheets when using `<sl-icon>` via a custom resolver.
|
||||
- Added tests for `<sl-split-panel>` [#1343]
|
||||
- Fixed a bug where changing the size of `<sl-radio-group>` wouldn't update the size of child elements
|
||||
- Fixed a bug in `<sl-select>` and `<sl-color-picker>` where the `size` attribute wasn't being reflected [#1318](https://github.com/shoelace-style/shoelace/issues/1348)
|
||||
- Fixed a bug in `<sl-radio-group>` where `<sl-radio>` would not get checked if `<sl-radio-group>` was defined first. [#1364](https://github.com/shoelace-style/shoelace/pull/1364/)
|
||||
- Fixed a bug in `<sl-input>` that caused date pickers to look filled in even when empty in Safari [#1341](https://github.com/shoelace-style/shoelace/pull/1341)
|
||||
- Improved `<sl-button>` so it can accept children of variable heights [#1317](https://github.com/shoelace-style/shoelace/pull/1317)
|
||||
- Fixed a bug in `<sl-select>` and `<sl-color-picker>` where the `size` attribute wasn't being reflected [#1318]
|
||||
- Fixed a bug in `<sl-radio-group>` where `<sl-radio>` would not get checked if `<sl-radio-group>` was defined first. [#1364]
|
||||
- Fixed a bug in `<sl-input>` that caused date pickers to look filled in even when empty in Safari [#1341]
|
||||
- Fixed a bug in `<sl-radio-group>` that sometimes caused dual scrollbars in containers that overflowed [#1380]
|
||||
- Fixed a bug in `<sl-carousel>` not loading the English language pack automatically. [#1384]
|
||||
- Improved `<sl-button>` so it can accept children of variable heights [#1317]
|
||||
- Improved the docs to more clearly explain sizing radios and radio buttons
|
||||
- Improved the performance of `<sl-rating>` by partially rendering unseen icons [#1310](https://github.com/shoelace-style/shoelace/pull/1310)
|
||||
- Improved the Portuguese translation [#1336](https://github.com/shoelace-style/shoelace/pull/1336)
|
||||
- Improved the German translation [#1339](https://github.com/shoelace-style/shoelace/pull/1339)
|
||||
- Improved the autoloader so it watches `<html>` instead of `<body>` since the latter gets replaced by some frameworks [#1338](https://github.com/shoelace-style/shoelace/pull/1338)
|
||||
- Improved the Rails documentation [#1258](https://github.com/shoelace-style/shoelace/pull/1258)
|
||||
- Improved the performance of `<sl-rating>` by partially rendering unseen icons [#1310]
|
||||
- Improved the Portuguese translation [#1336]
|
||||
- Improved the German translation [#1339]
|
||||
- Improved the autoloader so it watches `<html>` instead of `<body>` since the latter gets replaced by some frameworks [#1338]
|
||||
- Improved the Rails documentation [#1258]
|
||||
- Replaced Docsify with Eleventy to generate a static HTML version of the docs
|
||||
- Updated esbuild to 0.18.2
|
||||
- Updated Lit to 2.7.5
|
||||
@@ -1127,12 +1149,12 @@ The most elegant solution I found was to use the [Web Animations API](https://de
|
||||
|
||||
## 2.0.0-beta.34
|
||||
|
||||
This release changes the way components are registered if you're [cherry picking](/getting-started/installation?id=cherry-picking) or [using a bundler](/getting-started/installation?id=bundling). This recommendation came from the LitElement team and simplifies Shoelace's dependency graph. It also eliminates the need to call a `register()` function before using each component.
|
||||
This release changes the way components are registered if you're [cherry picking](/getting-started/installation#cherry-picking) or [using a bundler](/getting-started/installation#bundling). This recommendation came from the LitElement team and simplifies Shoelace's dependency graph. It also eliminates the need to call a `register()` function before using each component.
|
||||
|
||||
From now on, importing a component will register it automatically. The caveat is that bundlers may not tree shake the library properly if you import from `@shoelace-style/shoelace`, so the recommendation is to import components and utilities from their corresponding files instead.
|
||||
|
||||
- 🚨 BREAKING: removed `all.shoelace.js` (use `shoelace.js` instead)
|
||||
- 🚨 BREAKING: component modules now have a side effect, so bundlers may not tree shake properly when importing from `@shoelace-style/shoelace` (see the [installation page](/getting-started/installation?id=bundling) for more details and how to update)
|
||||
- 🚨 BREAKING: component modules now have a side effect, so bundlers may not tree shake properly when importing from `@shoelace-style/shoelace` (see the [installation page](/getting-started/installation#bundling) for more details and how to update)
|
||||
- Added `sl-clear` event to `<sl-select>`
|
||||
- Fixed a bug where dynamically changing menu items in `<sl-select>` would cause the display label to be blank [#374]
|
||||
- Fixed a bug where setting the `value` attribute or property on `<sl-input>` and `<sl-textarea>` would trigger validation too soon
|
||||
|
||||
@@ -40,7 +40,7 @@ Import the Shoelace default theme (stylesheet) in `/resources/css/app.css`:
|
||||
|
||||
### Import Your Shoelace Components
|
||||
|
||||
Import each Shoelace component you plan to use in `/resources/js/bootstrap.js`. Use the full path to each component (as outlined in the [Cherry Picking instructions](https://shoelace.style/getting-started/installation?id=cherry-picking)). You can find the full import statement for a component in the _Importing_ section of the component's documentation (use the _Bundler_ import). Your imports should look similar to:
|
||||
Import each Shoelace component you plan to use in `/resources/js/bootstrap.js`. Use the full path to each component (as outlined in the [Cherry Picking instructions](https://shoelace.style/getting-started/installation#cherry-picking)). You can find the full import statement for a component in the _Importing_ section of the component's documentation (use the _Bundler_ import). Your imports should look similar to:
|
||||
|
||||
```js
|
||||
import '@shoelace-style/shoelace/dist/components/button/button.js';
|
||||
|
||||
@@ -84,7 +84,7 @@ If we use `useEffect` instead of `useLayoutEffect`, the initial render will occu
|
||||
:::
|
||||
|
||||
:::tip
|
||||
This will import all Shoelace components for convenience. To selectively import components, refer to the [Using webpack](/getting-started/installation?id=using-webpack) section of the docs.
|
||||
This will import all Shoelace components for convenience. To selectively import components, refer to the [Using webpack](/getting-started/installation#using-webpack) section of the docs.
|
||||
:::
|
||||
|
||||
You may be wondering where the `URL` property is coming from. We'll address that in the next few sections.
|
||||
|
||||
@@ -37,7 +37,7 @@ The next step is to import Shoelace's default theme (stylesheet) in `app/javascr
|
||||
@import '@shoelace-style/shoelace/dist/themes/dark'; // Optional dark theme
|
||||
```
|
||||
|
||||
Fore more details about themes, please refer to [Theme Basics](/getting-started/themes?id=theme-basics).
|
||||
Fore more details about themes, please refer to [Theme Basics](/getting-started/themes#theme-basics).
|
||||
|
||||
### Importing Required Scripts
|
||||
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@shoelace-style/shoelace",
|
||||
"version": "2.4.0",
|
||||
"version": "2.5.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@shoelace-style/shoelace",
|
||||
"version": "2.4.0",
|
||||
"version": "2.5.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^3.5.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@shoelace-style/shoelace",
|
||||
"description": "A forward-thinking library of web components.",
|
||||
"version": "2.4.0",
|
||||
"version": "2.5.2",
|
||||
"homepage": "https://github.com/shoelace-style/shoelace",
|
||||
"author": "Cory LaViska",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -70,11 +70,6 @@ async function buildTheDocs(watch = false) {
|
||||
//
|
||||
async function buildTheSource() {
|
||||
const alwaysExternal = ['@lit-labs/react', 'react'];
|
||||
const packageJSON = await fs.readFile('./package.json');
|
||||
const dependencies = [
|
||||
...Object.keys(packageJSON.dependencies || {}),
|
||||
...Object.keys(packageJSON.peerDependencies || {})
|
||||
];
|
||||
|
||||
const cdnConfig = {
|
||||
format: 'esm',
|
||||
@@ -215,8 +210,9 @@ if (!serve) {
|
||||
await nextTask(`Copying the build to "${sitedir}"`, async () => {
|
||||
await deleteAsync(sitedir);
|
||||
|
||||
// We copy the CDN build because that has everything bundled.
|
||||
await copy(cdndir, path.join(sitedir, 'cdn'));
|
||||
// We copy the CDN build because that has everything bundled. Yes this looks weird.
|
||||
// But if we do "/cdn" it requires changes all the docs to do /cdn instead of /dist.
|
||||
await copy(cdndir, path.join(sitedir, 'dist'));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -245,7 +241,7 @@ if (serve) {
|
||||
server: {
|
||||
baseDir: sitedir,
|
||||
routes: {
|
||||
'/cdn': './cdn'
|
||||
'/dist': './cdn'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -49,7 +49,7 @@ components.map(component => {
|
||||
})
|
||||
);
|
||||
|
||||
index.push(`export { default as ${component.name} } from './${tagWithoutPrefix}';`);
|
||||
index.push(`export { default as ${component.name} } from './${tagWithoutPrefix}/index.js';`);
|
||||
|
||||
fs.writeFileSync(componentFile, source, 'utf8');
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function (plop) {
|
||||
type: 'modify',
|
||||
path: '../../src/shoelace.ts',
|
||||
pattern: /\/\* plop:component \*\//,
|
||||
template: `export { default as {{ properCase tag }} } from './components/{{ tagWithoutPrefix tag }}/{{ tagWithoutPrefix tag }}';\n/* plop:component */`
|
||||
template: `export { default as {{ properCase tag }} } from './components/{{ tagWithoutPrefix tag }}/{{ tagWithoutPrefix tag }}.js';\n/* plop:component */`
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './{{ tagWithoutPrefix tag }}.styles';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './{{ tagWithoutPrefix tag }}.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
|
||||
describe('<{{ tag }}>', () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { aTimeout, expect, fixture, html, oneEvent } from '@open-wc/testing';
|
||||
import { clickOnElement, moveMouseOnElement } from '../../internal/test';
|
||||
import { queryByTestId } from '../../internal/test/data-testid-helpers';
|
||||
import { clickOnElement, moveMouseOnElement } from '../../internal/test.js';
|
||||
import { queryByTestId } from '../../internal/test/data-testid-helpers.js';
|
||||
import { resetMouse } from '@web/test-runner-commands';
|
||||
import sinon from 'sinon';
|
||||
import type SlAlert from './alert';
|
||||
import type SlIconButton from '../icon-button/icon-button';
|
||||
import type SlAlert from './alert.js';
|
||||
import type SlIconButton from '../icon-button/icon-button.js';
|
||||
|
||||
const getAlertContainer = (alert: SlAlert): HTMLElement => {
|
||||
return alert.shadowRoot!.querySelector<HTMLElement>('[part="base"]')!;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import '../icon-button/icon-button';
|
||||
import { animateTo, stopAnimations } from '../../internal/animate';
|
||||
import '../icon-button/icon-button.js';
|
||||
import { animateTo, stopAnimations } from '../../internal/animate.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query } from 'lit/decorators.js';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry';
|
||||
import { HasSlotController } from '../../internal/slot';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import { waitForEvent } from '../../internal/event';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './alert.styles';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './alert.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
const toastStack = Object.assign(document.createElement('div'), { className: 'sl-toast-stack' });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { clickOnElement } from '../../internal/test';
|
||||
import { clickOnElement } from '../../internal/test.js';
|
||||
import { expect, fixture, html, oneEvent } from '@open-wc/testing';
|
||||
import type SlAnimatedImage from './animated-image';
|
||||
import type SlAnimatedImage from './animated-image.js';
|
||||
|
||||
describe('<sl-animated-image>', () => {
|
||||
it('should render a component', async () => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import '../icon/icon';
|
||||
import '../icon/icon.js';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './animated-image.styles';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './animated-image.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { aTimeout, expect, fixture, html, oneEvent } from '@open-wc/testing';
|
||||
import type SlAnimation from './animation';
|
||||
import type SlAnimation from './animation.js';
|
||||
|
||||
describe('<sl-animation>', () => {
|
||||
const boxToAnimate = html`<div style="width: 10px; height: 10px;" data-testid="animated-box"></div>`;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { animations } from './animations';
|
||||
import { animations } from './animations.js';
|
||||
import { customElement, property, queryAsync } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './animation.styles';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './animation.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
@@ -91,8 +91,6 @@ export default class SlAnimation extends ShoelaceElement {
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.createAnimation();
|
||||
this.handleAnimationCancel = this.handleAnimationCancel.bind(this);
|
||||
this.handleAnimationFinish = this.handleAnimationFinish.bind(this);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
@@ -100,17 +98,17 @@ export default class SlAnimation extends ShoelaceElement {
|
||||
this.destroyAnimation();
|
||||
}
|
||||
|
||||
private handleAnimationFinish() {
|
||||
private handleAnimationFinish = () => {
|
||||
this.play = false;
|
||||
this.hasStarted = false;
|
||||
this.emit('sl-finish');
|
||||
}
|
||||
};
|
||||
|
||||
private handleAnimationCancel() {
|
||||
private handleAnimationCancel = () => {
|
||||
this.play = false;
|
||||
this.hasStarted = false;
|
||||
this.emit('sl-cancel');
|
||||
}
|
||||
};
|
||||
|
||||
private handleSlotChange() {
|
||||
this.destroyAnimation();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { aTimeout, expect, fixture, html, waitUntil } from '@open-wc/testing';
|
||||
import type SlAvatar from './avatar';
|
||||
import type SlAvatar from './avatar.js';
|
||||
|
||||
// The default avatar background just misses AA contrast, but the next step up is way too dark. Since avatars aren't
|
||||
// used to display text, we're going to relax this rule.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import '../icon/icon';
|
||||
import '../icon/icon.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './avatar.styles';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './avatar.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
import type SlBadge from './badge';
|
||||
import type SlBadge from './badge.js';
|
||||
|
||||
describe('<sl-badge>', () => {
|
||||
let el: SlBadge;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './badge.styles';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './badge.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
import type SlBreadcrumbItem from './breadcrumb-item';
|
||||
import type SlBreadcrumbItem from './breadcrumb-item.js';
|
||||
|
||||
describe('<sl-breadcrumb-item>', () => {
|
||||
let el: SlBreadcrumbItem;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { HasSlotController } from '../../internal/slot';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './breadcrumb-item.styles';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './breadcrumb-item.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
import type SlBreadcrumb from './breadcrumb';
|
||||
import type SlBreadcrumb from './breadcrumb.js';
|
||||
|
||||
// The default link color just misses AA contrast, but the next step up is way too dark. Maybe we can solve this in the
|
||||
// future with a prefers-contrast media query.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import '../icon/icon';
|
||||
import '../icon/icon.js';
|
||||
import { customElement, property, query } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './breadcrumb.styles';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './breadcrumb.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
import type SlBreadcrumbItem from '../breadcrumb-item/breadcrumb-item';
|
||||
import type SlBreadcrumbItem from '../breadcrumb-item/breadcrumb-item.js';
|
||||
|
||||
/**
|
||||
* @summary Breadcrumbs provide a group of links so users can easily navigate a website's hierarchy.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
|
||||
import type SlButtonGroup from './button-group';
|
||||
import type SlButtonGroup from './button-group.js';
|
||||
|
||||
describe('<sl-button-group>', () => {
|
||||
describe('defaults ', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './button-group.styles';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './button-group.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { expect, fixture, html, waitUntil } from '@open-wc/testing';
|
||||
import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests';
|
||||
import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests.js';
|
||||
import sinon from 'sinon';
|
||||
import type SlButton from './button';
|
||||
import type SlButton from './button.js';
|
||||
|
||||
const variants = ['default', 'primary', 'success', 'neutral', 'warning', 'danger'];
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import '../icon/icon';
|
||||
import '../spinner/spinner';
|
||||
import '../icon/icon.js';
|
||||
import '../spinner/spinner.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { FormControlController, validValidityState } from '../../internal/form';
|
||||
import { HasSlotController } from '../../internal/slot';
|
||||
import { FormControlController, validValidityState } from '../../internal/form.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html, literal } from 'lit/static-html.js';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './button.styles';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './button.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
import type { ShoelaceFormControl } from '../../internal/shoelace-element';
|
||||
import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
|
||||
|
||||
/**
|
||||
* @summary Buttons represent actions that are available to the user.
|
||||
@@ -167,17 +167,11 @@ export default class SlButton extends ShoelaceElement implements ShoelaceFormCon
|
||||
return '';
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.handleHostClick = this.handleHostClick.bind(this);
|
||||
constructor() {
|
||||
super();
|
||||
this.addEventListener('click', this.handleHostClick);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.removeEventListener('click', this.handleHostClick);
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
if (this.isButton()) {
|
||||
this.formControlController.updateValidity();
|
||||
@@ -204,13 +198,13 @@ export default class SlButton extends ShoelaceElement implements ShoelaceFormCon
|
||||
}
|
||||
}
|
||||
|
||||
private handleHostClick(event: MouseEvent) {
|
||||
private handleHostClick = (event: MouseEvent) => {
|
||||
// Prevent the click event from being emitted when the button is disabled or loading
|
||||
if (this.disabled || this.loading) {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private handleInvalid(event: Event) {
|
||||
this.formControlController.setValidity(false);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
import type SlCard from './card';
|
||||
import type SlCard from './card.js';
|
||||
|
||||
describe('<sl-card>', () => {
|
||||
let el: SlCard;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { HasSlotController } from '../../internal/slot';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './card.styles';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './card.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './carousel-item.styles';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './carousel-item.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { clickOnElement } from '../../internal/test';
|
||||
import { clickOnElement } from '../../internal/test.js';
|
||||
import { expect, fixture, html, oneEvent } from '@open-wc/testing';
|
||||
import sinon from 'sinon';
|
||||
import type SlCarousel from './carousel';
|
||||
import type SlCarousel from './carousel.js';
|
||||
|
||||
describe('<sl-carousel>', () => {
|
||||
it('should render a carousel with default configuration', async () => {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import '../icon/icon';
|
||||
import { AutoplayController } from './autoplay-controller';
|
||||
import { clamp } from '../../internal/math';
|
||||
import '../icon/icon.js';
|
||||
import { AutoplayController } from './autoplay-controller.js';
|
||||
import { clamp } from '../../internal/math.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '@shoelace-style/localize';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { map } from 'lit/directives/map.js';
|
||||
import { prefersReducedMotion } from '../../internal/animate';
|
||||
import { prefersReducedMotion } from '../../internal/animate.js';
|
||||
import { range } from 'lit/directives/range.js';
|
||||
import { ScrollController } from './scroll-controller';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import SlCarouselItem from '../carousel-item/carousel-item';
|
||||
import styles from './carousel.styles';
|
||||
import { ScrollController } from './scroll-controller.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlCarouselItem from '../carousel-item/carousel-item.js';
|
||||
import styles from './carousel.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
@@ -132,7 +132,7 @@ export default class SlCarousel extends ShoelaceElement {
|
||||
|
||||
protected firstUpdated(): void {
|
||||
this.initializeSlides();
|
||||
this.mutationObserver = new MutationObserver(this.handleSlotChange.bind(this));
|
||||
this.mutationObserver = new MutationObserver(this.handleSlotChange);
|
||||
this.mutationObserver.observe(this, { childList: true, subtree: false });
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ export default class SlCarousel extends ShoelaceElement {
|
||||
}
|
||||
|
||||
private getCurrentPage() {
|
||||
return Math.floor(this.activeSlide / this.slidesPerPage);
|
||||
return Math.ceil(this.activeSlide / this.slidesPerPage);
|
||||
}
|
||||
|
||||
private getSlides({ excludeClones = true }: { excludeClones?: boolean } = {}) {
|
||||
@@ -211,7 +211,7 @@ export default class SlCarousel extends ShoelaceElement {
|
||||
}
|
||||
}
|
||||
|
||||
private handleSlotChange(mutations: MutationRecord[]) {
|
||||
private handleSlotChange = (mutations: MutationRecord[]) => {
|
||||
const needsInitialization = mutations.some(mutation =>
|
||||
[...mutation.addedNodes, ...mutation.removedNodes].some(
|
||||
node => SlCarouselItem.isCarouselItem(node) && !(node as HTMLElement).hasAttribute('data-clone')
|
||||
@@ -223,7 +223,7 @@ export default class SlCarousel extends ShoelaceElement {
|
||||
this.initializeSlides();
|
||||
}
|
||||
this.requestUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
@watch('loop', { waitUntilFirstUpdate: true })
|
||||
@watch('slidesPerPage', { waitUntilFirstUpdate: true })
|
||||
@@ -325,7 +325,15 @@ export default class SlCarousel extends ShoelaceElement {
|
||||
* @param behavior - The behavior used for scrolling.
|
||||
*/
|
||||
previous(behavior: ScrollBehavior = 'smooth') {
|
||||
this.goToSlide(this.activeSlide - this.slidesPerMove, behavior);
|
||||
let previousIndex = this.activeSlide || this.activeSlide - this.slidesPerMove;
|
||||
let canSnap = false;
|
||||
|
||||
while (!canSnap && previousIndex > 0) {
|
||||
previousIndex -= 1;
|
||||
canSnap = Math.abs(previousIndex - this.slidesPerMove) % this.slidesPerMove === 0;
|
||||
}
|
||||
|
||||
this.goToSlide(previousIndex, behavior);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { debounce } from '../../internal/debounce';
|
||||
import { prefersReducedMotion } from '../../internal/animate';
|
||||
import { waitForEvent } from '../../internal/event';
|
||||
import { debounce } from '../../internal/debounce.js';
|
||||
import { prefersReducedMotion } from '../../internal/animate.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import type { ReactiveController, ReactiveElement } from 'lit';
|
||||
|
||||
interface ScrollHost extends ReactiveElement {
|
||||
@@ -20,16 +20,7 @@ export class ScrollController<T extends ScrollHost> implements ReactiveControlle
|
||||
|
||||
constructor(host: T) {
|
||||
this.host = host;
|
||||
|
||||
host.addController(this);
|
||||
|
||||
this.handleScroll = this.handleScroll.bind(this);
|
||||
this.handlePointerDown = this.handlePointerDown.bind(this);
|
||||
this.handlePointerMove = this.handlePointerMove.bind(this);
|
||||
this.handlePointerUp = this.handlePointerUp.bind(this);
|
||||
this.handlePointerUp = this.handlePointerUp.bind(this);
|
||||
this.handleTouchStart = this.handleTouchStart.bind(this);
|
||||
this.handleTouchEnd = this.handleTouchEnd.bind(this);
|
||||
}
|
||||
|
||||
async hostConnected() {
|
||||
@@ -58,13 +49,13 @@ export class ScrollController<T extends ScrollHost> implements ReactiveControlle
|
||||
scrollContainer.removeEventListener('touchend', this.handleTouchEnd);
|
||||
}
|
||||
|
||||
handleScroll() {
|
||||
handleScroll = () => {
|
||||
if (!this.scrolling) {
|
||||
this.scrolling = true;
|
||||
this.host.requestUpdate();
|
||||
}
|
||||
this.handleScrollEnd();
|
||||
}
|
||||
};
|
||||
|
||||
@debounce(100)
|
||||
handleScrollEnd() {
|
||||
@@ -84,7 +75,7 @@ export class ScrollController<T extends ScrollHost> implements ReactiveControlle
|
||||
}
|
||||
}
|
||||
|
||||
handlePointerDown(event: PointerEvent) {
|
||||
handlePointerDown = (event: PointerEvent) => {
|
||||
if (event.pointerType === 'touch') {
|
||||
return;
|
||||
}
|
||||
@@ -97,9 +88,9 @@ export class ScrollController<T extends ScrollHost> implements ReactiveControlle
|
||||
|
||||
this.host.scrollContainer.addEventListener('pointermove', this.handlePointerMove);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handlePointerMove(event: PointerEvent) {
|
||||
handlePointerMove = (event: PointerEvent) => {
|
||||
const scrollContainer = this.host.scrollContainer;
|
||||
|
||||
const hasMoved = !!event.movementX || !!event.movementY;
|
||||
@@ -111,28 +102,28 @@ export class ScrollController<T extends ScrollHost> implements ReactiveControlle
|
||||
// Ignore pointers that we are not tracking
|
||||
this.handleDrag(event);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handlePointerUp(event: PointerEvent) {
|
||||
handlePointerUp = (event: PointerEvent) => {
|
||||
this.pointers.delete(event.pointerId);
|
||||
this.host.scrollContainer.releasePointerCapture(event.pointerId);
|
||||
|
||||
if (this.pointers.size === 0) {
|
||||
this.handleDragEnd();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleTouchEnd(event: TouchEvent) {
|
||||
handleTouchEnd = (event: TouchEvent) => {
|
||||
for (const touch of event.changedTouches) {
|
||||
this.pointers.delete(touch.identifier);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleTouchStart(event: TouchEvent) {
|
||||
handleTouchStart = (event: TouchEvent) => {
|
||||
for (const touch of event.touches) {
|
||||
this.pointers.add(touch.identifier);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDragStart() {
|
||||
const host = this.host;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { aTimeout, expect, fixture, html, oneEvent, waitUntil } from '@open-wc/testing';
|
||||
import { clickOnElement } from '../../internal/test';
|
||||
import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests';
|
||||
import { clickOnElement } from '../../internal/test.js';
|
||||
import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests.js';
|
||||
import { sendKeys } from '@web/test-runner-commands';
|
||||
import sinon from 'sinon';
|
||||
import type SlCheckbox from './checkbox';
|
||||
import type SlCheckbox from './checkbox.js';
|
||||
|
||||
describe('<sl-checkbox>', () => {
|
||||
it('should pass accessibility tests', async () => {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import '../icon/icon';
|
||||
import '../icon/icon.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { defaultValue } from '../../internal/default-value';
|
||||
import { FormControlController } from '../../internal/form';
|
||||
import { defaultValue } from '../../internal/default-value.js';
|
||||
import { FormControlController } from '../../internal/form.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { live } from 'lit/directives/live.js';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './checkbox.styles';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './checkbox.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
import type { ShoelaceFormControl } from '../../internal/shoelace-element';
|
||||
import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
|
||||
|
||||
/**
|
||||
* @summary Checkboxes allow the user to toggle an option on or off.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { aTimeout, expect, fixture, html, oneEvent } from '@open-wc/testing';
|
||||
import { clickOnElement } from '../../internal/test';
|
||||
import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests';
|
||||
import { clickOnElement } from '../../internal/test.js';
|
||||
import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests.js';
|
||||
import { sendKeys } from '@web/test-runner-commands';
|
||||
import { serialize } from '../../utilities/form';
|
||||
import { serialize } from '../../utilities/form.js';
|
||||
import sinon from 'sinon';
|
||||
import type SlColorPicker from './color-picker';
|
||||
import type SlColorPicker from './color-picker.js';
|
||||
|
||||
describe('<sl-color-picker>', () => {
|
||||
describe('when the value changes', () => {
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import '../button-group/button-group';
|
||||
import '../button/button';
|
||||
import '../dropdown/dropdown';
|
||||
import '../icon/icon';
|
||||
import '../input/input';
|
||||
import '../visually-hidden/visually-hidden';
|
||||
import { clamp } from '../../internal/math';
|
||||
import '../button-group/button-group.js';
|
||||
import '../button/button.js';
|
||||
import '../dropdown/dropdown.js';
|
||||
import '../icon/icon.js';
|
||||
import '../input/input.js';
|
||||
import '../visually-hidden/visually-hidden.js';
|
||||
import { clamp } from '../../internal/math.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { defaultValue } from '../../internal/default-value';
|
||||
import { drag } from '../../internal/drag';
|
||||
import { FormControlController } from '../../internal/form';
|
||||
import { defaultValue } from '../../internal/default-value.js';
|
||||
import { drag } from '../../internal/drag.js';
|
||||
import { FormControlController } from '../../internal/form.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './color-picker.styles';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './color-picker.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
import type { ShoelaceFormControl } from '../../internal/shoelace-element';
|
||||
import type SlChangeEvent from '../../events/sl-change';
|
||||
import type SlDropdown from '../dropdown/dropdown';
|
||||
import type SlInput from '../input/input';
|
||||
import type SlInputEvent from '../../events/sl-input';
|
||||
import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
|
||||
import type SlChangeEvent from '../../events/sl-change.js';
|
||||
import type SlDropdown from '../dropdown/dropdown.js';
|
||||
import type SlInput from '../input/input.js';
|
||||
import type SlInputEvent from '../../events/sl-input.js';
|
||||
|
||||
const hasEyeDropper = 'EyeDropper' in window;
|
||||
|
||||
@@ -190,20 +190,12 @@ export default class SlColorPicker extends ShoelaceElement implements ShoelaceFo
|
||||
return this.input.validationMessage;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.handleFocusIn = this.handleFocusIn.bind(this);
|
||||
this.handleFocusOut = this.handleFocusOut.bind(this);
|
||||
constructor() {
|
||||
super();
|
||||
this.addEventListener('focusin', this.handleFocusIn);
|
||||
this.addEventListener('focusout', this.handleFocusOut);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.removeEventListener('focusin', this.handleFocusIn);
|
||||
this.removeEventListener('focusout', this.handleFocusOut);
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.input.updateComplete.then(() => {
|
||||
this.formControlController.updateValidity();
|
||||
@@ -222,15 +214,15 @@ export default class SlColorPicker extends ShoelaceElement implements ShoelaceFo
|
||||
});
|
||||
}
|
||||
|
||||
private handleFocusIn() {
|
||||
private handleFocusIn = () => {
|
||||
this.hasFocus = true;
|
||||
this.emit('sl-focus');
|
||||
}
|
||||
};
|
||||
|
||||
private handleFocusOut() {
|
||||
private handleFocusOut = () => {
|
||||
this.hasFocus = false;
|
||||
this.emit('sl-blur');
|
||||
}
|
||||
};
|
||||
|
||||
private handleFormatToggle() {
|
||||
const formats = ['hex', 'rgb', 'hsl', 'hsv'];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import '../icon/icon';
|
||||
import { animateTo, shimKeyframesHeightAuto, stopAnimations } from '../../internal/animate';
|
||||
import '../icon/icon.js';
|
||||
import { animateTo, shimKeyframesHeightAuto, stopAnimations } from '../../internal/animate.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query } from 'lit/decorators.js';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import { waitForEvent } from '../../internal/event';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './details.styles';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './details.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import '../icon-button/icon-button';
|
||||
import { animateTo, stopAnimations } from '../../internal/animate';
|
||||
import '../icon-button/icon-button.js';
|
||||
import { animateTo, stopAnimations } from '../../internal/animate.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query } from 'lit/decorators.js';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry';
|
||||
import { HasSlotController } from '../../internal/slot';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll';
|
||||
import { waitForEvent } from '../../internal/event';
|
||||
import { watch } from '../../internal/watch';
|
||||
import Modal from '../../internal/modal';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './dialog.styles';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import Modal from '../../internal/modal.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './dialog.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
@@ -67,7 +67,7 @@ export default class SlDialog extends ShoelaceElement {
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer');
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private modal: Modal;
|
||||
private modal = new Modal(this);
|
||||
private originalTrigger: HTMLElement | null;
|
||||
|
||||
@query('.dialog') dialog: HTMLElement;
|
||||
@@ -92,12 +92,6 @@ export default class SlDialog extends ShoelaceElement {
|
||||
*/
|
||||
@property({ attribute: 'no-header', type: Boolean, reflect: true }) noHeader = false;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.handleDocumentKeyDown = this.handleDocumentKeyDown.bind(this);
|
||||
this.modal = new Modal(this);
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.dialog.hidden = !this.open;
|
||||
|
||||
@@ -136,12 +130,12 @@ export default class SlDialog extends ShoelaceElement {
|
||||
document.removeEventListener('keydown', this.handleDocumentKeyDown);
|
||||
}
|
||||
|
||||
private handleDocumentKeyDown(event: KeyboardEvent) {
|
||||
private handleDocumentKeyDown = (event: KeyboardEvent) => {
|
||||
if (this.open && event.key === 'Escape') {
|
||||
event.stopPropagation();
|
||||
this.requestClose('keyboard');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@watch('open', { waitUntilFirstUpdate: true })
|
||||
async handleOpenChange() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
|
||||
import type SlDivider from './divider';
|
||||
import type SlDivider from './divider.js';
|
||||
|
||||
describe('<sl-divider>', () => {
|
||||
describe('defaults ', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './divider.styles';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './divider.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import '../icon-button/icon-button';
|
||||
import { animateTo, stopAnimations } from '../../internal/animate';
|
||||
import '../icon-button/icon-button.js';
|
||||
import { animateTo, stopAnimations } from '../../internal/animate.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query } from 'lit/decorators.js';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry';
|
||||
import { HasSlotController } from '../../internal/slot';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll';
|
||||
import { uppercaseFirstLetter } from '../../internal/string';
|
||||
import { waitForEvent } from '../../internal/event';
|
||||
import { watch } from '../../internal/watch';
|
||||
import Modal from '../../internal/modal';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './drawer.styles';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll.js';
|
||||
import { uppercaseFirstLetter } from '../../internal/string.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import Modal from '../../internal/modal.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './drawer.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
@@ -75,7 +75,7 @@ export default class SlDrawer extends ShoelaceElement {
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer');
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private modal: Modal;
|
||||
private modal = new Modal(this);
|
||||
private originalTrigger: HTMLElement | null;
|
||||
|
||||
@query('.drawer') drawer: HTMLElement;
|
||||
@@ -109,12 +109,6 @@ export default class SlDrawer extends ShoelaceElement {
|
||||
*/
|
||||
@property({ attribute: 'no-header', type: Boolean, reflect: true }) noHeader = false;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.handleDocumentKeyDown = this.handleDocumentKeyDown.bind(this);
|
||||
this.modal = new Modal(this);
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.drawer.hidden = !this.open;
|
||||
|
||||
@@ -156,12 +150,12 @@ export default class SlDrawer extends ShoelaceElement {
|
||||
document.removeEventListener('keydown', this.handleDocumentKeyDown);
|
||||
}
|
||||
|
||||
private handleDocumentKeyDown(event: KeyboardEvent) {
|
||||
private handleDocumentKeyDown = (event: KeyboardEvent) => {
|
||||
if (this.open && !this.contained && event.key === 'Escape') {
|
||||
event.stopPropagation();
|
||||
this.requestClose('keyboard');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@watch('open', { waitUntilFirstUpdate: true })
|
||||
async handleOpenChange() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { clickOnElement } from '../../internal/test';
|
||||
import { clickOnElement } from '../../internal/test.js';
|
||||
import { expect, fixture, html, waitUntil } from '@open-wc/testing';
|
||||
import { sendKeys, sendMouse } from '@web/test-runner-commands';
|
||||
import sinon from 'sinon';
|
||||
import type SlDropdown from './dropdown';
|
||||
import type SlDropdown from './dropdown.js';
|
||||
|
||||
describe('<sl-dropdown>', () => {
|
||||
it('should be visible with the open attribute', async () => {
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import '../popup/popup';
|
||||
import { animateTo, stopAnimations } from '../../internal/animate';
|
||||
import '../popup/popup.js';
|
||||
import { animateTo, stopAnimations } from '../../internal/animate.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query } from 'lit/decorators.js';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry';
|
||||
import { getTabbableBoundary } from '../../internal/tabbable';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
|
||||
import { getTabbableBoundary } from '../../internal/tabbable.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import { waitForEvent } from '../../internal/event';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './dropdown.styles';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './dropdown.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
import type SlButton from '../button/button';
|
||||
import type SlIconButton from '../icon-button/icon-button';
|
||||
import type SlMenu from '../menu/menu';
|
||||
import type SlPopup from '../popup/popup';
|
||||
import type SlSelectEvent from '../../events/sl-select';
|
||||
import type SlButton from '../button/button.js';
|
||||
import type SlIconButton from '../icon-button/icon-button.js';
|
||||
import type SlMenu from '../menu/menu.js';
|
||||
import type SlPopup from '../popup/popup.js';
|
||||
import type SlSelectEvent from '../../events/sl-select.js';
|
||||
|
||||
/**
|
||||
* @summary Dropdowns expose additional content that "drops down" in a panel.
|
||||
@@ -103,10 +103,6 @@ export default class SlDropdown extends ShoelaceElement {
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.handlePanelSelect = this.handlePanelSelect.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
this.handleDocumentKeyDown = this.handleDocumentKeyDown.bind(this);
|
||||
this.handleDocumentMouseDown = this.handleDocumentMouseDown.bind(this);
|
||||
|
||||
if (!this.containingElement) {
|
||||
this.containingElement = this;
|
||||
@@ -142,7 +138,7 @@ export default class SlDropdown extends ShoelaceElement {
|
||||
| undefined;
|
||||
}
|
||||
|
||||
handleKeyDown(event: KeyboardEvent) {
|
||||
private handleKeyDown = (event: KeyboardEvent) => {
|
||||
// Close when escape is pressed inside an open dropdown. We need to listen on the panel itself and stop propagation
|
||||
// in case any ancestors are also listening for this key.
|
||||
if (this.open && event.key === 'Escape') {
|
||||
@@ -150,9 +146,9 @@ export default class SlDropdown extends ShoelaceElement {
|
||||
this.hide();
|
||||
this.focusOnTrigger();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDocumentKeyDown(event: KeyboardEvent) {
|
||||
private handleDocumentKeyDown = (event: KeyboardEvent) => {
|
||||
// Close when escape or tab is pressed
|
||||
if (event.key === 'Escape' && this.open) {
|
||||
event.stopPropagation();
|
||||
@@ -189,17 +185,17 @@ export default class SlDropdown extends ShoelaceElement {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDocumentMouseDown(event: MouseEvent) {
|
||||
private handleDocumentMouseDown = (event: MouseEvent) => {
|
||||
// Close when clicking outside of the containing element
|
||||
const path = event.composedPath();
|
||||
if (this.containingElement && !path.includes(this.containingElement)) {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handlePanelSelect(event: SlSelectEvent) {
|
||||
private handlePanelSelect = (event: SlSelectEvent) => {
|
||||
const target = event.target as HTMLElement;
|
||||
|
||||
// Hide the dropdown when a menu item is selected
|
||||
@@ -207,7 +203,7 @@ export default class SlDropdown extends ShoelaceElement {
|
||||
this.hide();
|
||||
this.focusOnTrigger();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleTriggerClick() {
|
||||
if (this.open) {
|
||||
@@ -415,12 +411,9 @@ export default class SlDropdown extends ShoelaceElement {
|
||||
@slotchange=${this.handleTriggerSlotChange}
|
||||
></slot>
|
||||
|
||||
<slot
|
||||
part="panel"
|
||||
class="dropdown__panel"
|
||||
aria-hidden=${this.open ? 'false' : 'true'}
|
||||
aria-labelledby="dropdown"
|
||||
></slot>
|
||||
<div aria-hidden=${this.open ? 'false' : 'true'} aria-labelledby="dropdown">
|
||||
<slot part="panel" class="dropdown__panel"></slot>
|
||||
</div>
|
||||
</sl-popup>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
|
||||
/**
|
||||
* @summary Formats a number as a human readable bytes value.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
|
||||
/**
|
||||
* @summary Formats a date/time using the specified locale and options.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
|
||||
/**
|
||||
* @summary Formats a number using the specified locale and options.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import '../icon/icon';
|
||||
import '../icon/icon.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { html, literal } from 'lit/static-html.js';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './icon-button.styles';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './icon-button.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { elementUpdated, expect, fixture, html, oneEvent } from '@open-wc/testing';
|
||||
import { aTimeout, elementUpdated, expect, fixture, html, oneEvent } from '@open-wc/testing';
|
||||
import { registerIconLibrary } from '../../../dist/shoelace.js';
|
||||
import type SlErrorEvent from '../../events/sl-error';
|
||||
import type SlIcon from './icon';
|
||||
@@ -154,4 +154,79 @@ describe('<sl-icon>', () => {
|
||||
expect(ev).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('svg spritesheets', () => {
|
||||
// For some reason ESLint wants to fail in CI here, but works locally.
|
||||
/* eslint-disable */
|
||||
it('Should properly grab an SVG and render it from bootstrap icons', async () => {
|
||||
registerIconLibrary('sprite', {
|
||||
resolver: name => `/docs/assets/images/sprite.svg#${name}`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor'),
|
||||
spriteSheet: true
|
||||
});
|
||||
|
||||
const el = await fixture<SlIcon>(html`<sl-icon name="arrow-left" library="sprite"></sl-icon>`);
|
||||
|
||||
await elementUpdated(el);
|
||||
|
||||
const svg = el.shadowRoot?.querySelector("svg[part='svg']");
|
||||
const use = svg?.querySelector(`use[href='/docs/assets/images/sprite.svg#arrow-left']`);
|
||||
|
||||
expect(svg).to.be.instanceof(SVGElement);
|
||||
expect(use).to.be.instanceof(SVGUseElement);
|
||||
|
||||
// This is kind of hacky...but with no way to check "load", we just do a timeout :shrug:
|
||||
await aTimeout(200);
|
||||
|
||||
// Theres no way to really test that the icon rendered properly. We just gotta trust the browser to do it's thing :)
|
||||
// However, we can check the <use> size. It should be greater than 0x0 if loaded properly.
|
||||
const rect = use?.getBoundingClientRect();
|
||||
expect(rect?.width).to.be.greaterThan(0);
|
||||
expect(rect?.width).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('Should render nothing if the sprite hash is wrong', async () => {
|
||||
registerIconLibrary('sprite', {
|
||||
resolver: name => `/docs/assets/images/sprite.svg#${name}`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor'),
|
||||
spriteSheet: true
|
||||
});
|
||||
|
||||
const el = await fixture<SlIcon>(html`<sl-icon name="non-existant" library="sprite"></sl-icon>`);
|
||||
|
||||
await elementUpdated(el);
|
||||
|
||||
const svg = el.shadowRoot?.querySelector("svg[part='svg']");
|
||||
const use = svg?.querySelector('use');
|
||||
|
||||
// TODO: Theres no way to really test that the icon rendered properly. We just gotta trust the browser to do it's thing :)
|
||||
// However, we can check the <use> size. If it never loaded, it should be 0x0. Ideally, we could have error tracking...
|
||||
const rect = use?.getBoundingClientRect();
|
||||
expect(rect?.width).to.equal(0);
|
||||
expect(rect?.width).to.equal(0);
|
||||
});
|
||||
|
||||
// TODO: <use> svg icons don't emit a "load" or "error" event...if we can figure out how to get the event to emit errors.
|
||||
// Once we figure out how to emit errors / loading perhaps we can actually test this?
|
||||
it.skip("Should produce an error if the icon doesn't exist.", async () => {
|
||||
registerIconLibrary('sprite', {
|
||||
resolver(name) {
|
||||
return `/docs/assets/images/sprite.svg#${name}`;
|
||||
},
|
||||
mutator(svg) {
|
||||
return svg.setAttribute('fill', 'currentColor');
|
||||
},
|
||||
spriteSheet: true
|
||||
});
|
||||
|
||||
const el = await fixture<SlIcon>(html`<sl-icon name="bad-icon" library="sprite"></sl-icon>`);
|
||||
const listener = oneEvent(el, 'sl-error') as Promise<SlErrorEvent>;
|
||||
|
||||
el.name = 'bad-icon';
|
||||
const ev = await listener;
|
||||
await elementUpdated(el);
|
||||
expect(ev).to.exist;
|
||||
});
|
||||
});
|
||||
/* eslint-enable */
|
||||
});
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { getIconLibrary, unwatchIcon, watchIcon } from './library';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './icon.styles';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
import { getIconLibrary, type IconLibrary, unwatchIcon, watchIcon } from './library.js';
|
||||
import { html } from 'lit';
|
||||
import { isTemplateResult } from 'lit/directive-helpers.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './icon.styles.js';
|
||||
|
||||
import type { CSSResultGroup, HTMLTemplateResult } from 'lit';
|
||||
|
||||
const CACHEABLE_ERROR = Symbol();
|
||||
const RETRYABLE_ERROR = Symbol();
|
||||
type SVGResult = SVGSVGElement | typeof RETRYABLE_ERROR | typeof CACHEABLE_ERROR;
|
||||
type SVGResult = HTMLTemplateResult | SVGSVGElement | typeof RETRYABLE_ERROR | typeof CACHEABLE_ERROR;
|
||||
|
||||
let parser: DOMParser;
|
||||
const iconCache = new Map<string, Promise<SVGResult>>();
|
||||
@@ -18,18 +21,28 @@ const iconCache = new Map<string, Promise<SVGResult>>();
|
||||
* @status stable
|
||||
* @since 2.0
|
||||
*
|
||||
* @event sl-load - Emitted when the icon has loaded.
|
||||
* @event sl-error - Emitted when the icon fails to load due to an error.
|
||||
* @event sl-load - Emitted when the icon has loaded. When using `spriteSheet: true` this will not emit.
|
||||
* @event sl-error - Emitted when the icon fails to load due to an error. When using `spriteSheet: true` this will not emit.
|
||||
*
|
||||
* @csspart svg - The internal SVG element.
|
||||
* @csspart use - The <use> element generated when using `spriteSheet: true`
|
||||
*/
|
||||
@customElement('sl-icon')
|
||||
export default class SlIcon extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
private initialRender = false;
|
||||
|
||||
/** Given a URL, this function returns the resulting SVG element or an appropriate error symbol. */
|
||||
private static async resolveIcon(url: string): Promise<SVGResult> {
|
||||
private async resolveIcon(url: string, library?: IconLibrary): Promise<SVGResult> {
|
||||
let fileData: Response;
|
||||
|
||||
if (library?.spriteSheet) {
|
||||
return html`<svg part="svg">
|
||||
<use part="use" href="${url}"></use>
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
try {
|
||||
fileData = await fetch(url, { mode: 'cors' });
|
||||
if (!fileData.ok) return fileData.status === 410 ? CACHEABLE_ERROR : RETRYABLE_ERROR;
|
||||
@@ -57,7 +70,7 @@ export default class SlIcon extends ShoelaceElement {
|
||||
}
|
||||
}
|
||||
|
||||
@state() private svg: SVGElement | null = null;
|
||||
@state() private svg: SVGElement | HTMLTemplateResult | null = null;
|
||||
|
||||
/** The name of the icon to draw. Available names depend on the icon library being used. */
|
||||
@property({ reflect: true }) name?: string;
|
||||
@@ -83,6 +96,7 @@ export default class SlIcon extends ShoelaceElement {
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.initialRender = true;
|
||||
this.setIcon();
|
||||
}
|
||||
|
||||
@@ -126,11 +140,17 @@ export default class SlIcon extends ShoelaceElement {
|
||||
|
||||
let iconResolver = iconCache.get(url);
|
||||
if (!iconResolver) {
|
||||
iconResolver = SlIcon.resolveIcon(url);
|
||||
iconResolver = this.resolveIcon(url, library);
|
||||
iconCache.set(url, iconResolver);
|
||||
}
|
||||
|
||||
// If we haven't rendered yet, exit early. This avoids unnecessary work due to watching multiple props.
|
||||
if (!this.initialRender) {
|
||||
return;
|
||||
}
|
||||
|
||||
const svg = await iconResolver;
|
||||
|
||||
if (svg === RETRYABLE_ERROR) {
|
||||
iconCache.delete(url);
|
||||
}
|
||||
@@ -140,6 +160,11 @@ export default class SlIcon extends ShoelaceElement {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTemplateResult(svg)) {
|
||||
this.svg = svg;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (svg) {
|
||||
case RETRYABLE_ERROR:
|
||||
case CACHEABLE_ERROR:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getBasePath } from '../../utilities/base-path';
|
||||
import type { IconLibrary } from './library';
|
||||
import { getBasePath } from '../../utilities/base-path.js';
|
||||
import type { IconLibrary } from './library.js';
|
||||
|
||||
const library: IconLibrary = {
|
||||
name: 'default',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { IconLibrary } from './library';
|
||||
import type { IconLibrary } from './library.js';
|
||||
|
||||
//
|
||||
// System icons are a separate library to ensure they're always available, regardless of how the default icon library is
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import defaultLibrary from './library.default';
|
||||
import systemLibrary from './library.system';
|
||||
import type SlIcon from '../icon/icon';
|
||||
import defaultLibrary from './library.default.js';
|
||||
import systemLibrary from './library.system.js';
|
||||
import type SlIcon from '../icon/icon.js';
|
||||
|
||||
export type IconLibraryResolver = (name: string) => string;
|
||||
export type IconLibraryMutator = (svg: SVGElement) => void;
|
||||
@@ -8,6 +8,7 @@ export interface IconLibrary {
|
||||
name: string;
|
||||
resolver: IconLibraryResolver;
|
||||
mutator?: IconLibraryMutator;
|
||||
spriteSheet?: boolean;
|
||||
}
|
||||
|
||||
let registry: IconLibrary[] = [defaultLibrary, systemLibrary];
|
||||
@@ -29,15 +30,13 @@ export function getIconLibrary(name?: string) {
|
||||
}
|
||||
|
||||
/** Adds an icon library to the registry, or overrides an existing one. */
|
||||
export function registerIconLibrary(
|
||||
name: string,
|
||||
options: { resolver: IconLibraryResolver; mutator?: IconLibraryMutator }
|
||||
) {
|
||||
export function registerIconLibrary(name: string, options: Omit<IconLibrary, 'name'>) {
|
||||
unregisterIconLibrary(name);
|
||||
registry.push({
|
||||
name,
|
||||
resolver: options.resolver,
|
||||
mutator: options.mutator
|
||||
mutator: options.mutator,
|
||||
spriteSheet: options.spriteSheet
|
||||
});
|
||||
|
||||
// Redraw watched icons
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import '../icon/icon';
|
||||
import { clamp } from '../../internal/math';
|
||||
import '../icon/icon.js';
|
||||
import { clamp } from '../../internal/math.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query } from 'lit/decorators.js';
|
||||
import { drag } from '../../internal/drag';
|
||||
import { drag } from '../../internal/drag.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './image-comparer.styles';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './image-comparer.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import { requestInclude } from './request';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './include.styles';
|
||||
import { requestInclude } from './request.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './include.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles';
|
||||
import formControlStyles from '../../styles/form-control.styles';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import formControlStyles from '../../styles/form-control.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
|
||||
import { expect, fixture, html, oneEvent, waitUntil } from '@open-wc/testing';
|
||||
import { elementUpdated, expect, fixture, html, oneEvent, waitUntil } from '@open-wc/testing';
|
||||
import { getFormControls, serialize } from '../../../dist/shoelace.js';
|
||||
import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests';
|
||||
import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests.js';
|
||||
import { sendKeys } from '@web/test-runner-commands'; // must come from the same module
|
||||
import sinon from 'sinon';
|
||||
import type SlInput from './input';
|
||||
@@ -66,21 +66,69 @@ describe('<sl-input>', () => {
|
||||
|
||||
describe('value methods', () => {
|
||||
it('should set the value as a date when using valueAsDate', async () => {
|
||||
const el = await fixture<SlInput>(html` <sl-input type="date"></sl-input> `);
|
||||
const el = document.createElement(`sl-input`);
|
||||
el.type = 'date';
|
||||
const today = new Date();
|
||||
|
||||
el.valueAsDate = today;
|
||||
|
||||
// Test before we render in the dom
|
||||
expect(el.value).to.equal(today.toISOString().split('T')[0]);
|
||||
expect(el.valueAsDate.toISOString().split('T')[0]).to.equal(today.toISOString().split('T')[0]);
|
||||
|
||||
document.body.appendChild(el);
|
||||
|
||||
await elementUpdated(el);
|
||||
|
||||
// Update valueAsDate after we render to make sure it reflects properly
|
||||
el.valueAsDate = null;
|
||||
|
||||
await elementUpdated(el);
|
||||
|
||||
expect(el.value).to.equal('');
|
||||
expect(el.valueAsDate).to.equal(null);
|
||||
|
||||
// Update again with a real date to make sure it works
|
||||
el.valueAsDate = today;
|
||||
|
||||
await elementUpdated(el);
|
||||
|
||||
expect(el.value).to.equal(today.toISOString().split('T')[0]);
|
||||
expect(el.valueAsDate.toISOString().split('T')[0]).to.equal(today.toISOString().split('T')[0]);
|
||||
|
||||
el.remove();
|
||||
});
|
||||
|
||||
it('should set the value as a number when using valueAsNumber', async () => {
|
||||
const el = await fixture<SlInput>(html` <sl-input type="number"></sl-input> `);
|
||||
const el = document.createElement(`sl-input`);
|
||||
el.type = 'number';
|
||||
const num = 12345;
|
||||
|
||||
el.valueAsNumber = num;
|
||||
|
||||
expect(el.value).to.equal(num.toString());
|
||||
expect(el.valueAsNumber).to.equal(num);
|
||||
|
||||
document.body.appendChild(el);
|
||||
|
||||
await elementUpdated(el);
|
||||
|
||||
// Wait for render, then update the value
|
||||
const otherNum = 4567;
|
||||
el.valueAsNumber = otherNum;
|
||||
|
||||
await elementUpdated(el);
|
||||
|
||||
expect(el.value).to.equal(otherNum.toString());
|
||||
expect(el.valueAsNumber).to.equal(otherNum);
|
||||
|
||||
// Re-set valueAsNumber and make sure it updates.
|
||||
el.valueAsNumber = num;
|
||||
await elementUpdated(el);
|
||||
expect(el.value).to.equal(num.toString());
|
||||
expect(el.valueAsNumber).to.equal(num);
|
||||
|
||||
el.remove();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import '../icon/icon';
|
||||
import '../icon/icon.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { defaultValue } from '../../internal/default-value';
|
||||
import { FormControlController } from '../../internal/form';
|
||||
import { HasSlotController } from '../../internal/slot';
|
||||
import { defaultValue } from '../../internal/default-value.js';
|
||||
import { FormControlController } from '../../internal/form.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { live } from 'lit/directives/live.js';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './input.styles';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './input.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
import type { ShoelaceFormControl } from '../../internal/shoelace-element';
|
||||
import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
|
||||
|
||||
/**
|
||||
* @summary Inputs collect data from the user.
|
||||
@@ -63,6 +63,9 @@ export default class SlInput extends ShoelaceElement implements ShoelaceFormCont
|
||||
@state() private hasFocus = false;
|
||||
@property() title = ''; // make reactive to pass through
|
||||
|
||||
private __numberInput = Object.assign(document.createElement('input'), { type: 'number' });
|
||||
private __dateInput = Object.assign(document.createElement('input'), { type: 'date' });
|
||||
|
||||
/**
|
||||
* The type of input. Works the same as a native `<input>` element, but only a subset of types are supported. Defaults
|
||||
* to `text`.
|
||||
@@ -197,32 +200,24 @@ export default class SlInput extends ShoelaceElement implements ShoelaceFormCont
|
||||
|
||||
/** Gets or sets the current value as a `Date` object. Returns `null` if the value can't be converted. */
|
||||
get valueAsDate() {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'date';
|
||||
input.value = this.value;
|
||||
return input.valueAsDate;
|
||||
this.__dateInput.value = this.value;
|
||||
return this.input?.valueAsDate || this.__dateInput.valueAsDate;
|
||||
}
|
||||
|
||||
set valueAsDate(newValue: Date | null) {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'date';
|
||||
input.valueAsDate = newValue;
|
||||
this.value = input.value;
|
||||
this.__dateInput.valueAsDate = newValue;
|
||||
this.value = this.__dateInput.value;
|
||||
}
|
||||
|
||||
/** Gets or sets the current value as a number. Returns `NaN` if the value can't be converted. */
|
||||
get valueAsNumber() {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'number';
|
||||
input.value = this.value;
|
||||
return input.valueAsNumber;
|
||||
this.__numberInput.value = this.value;
|
||||
return this.input?.valueAsNumber || this.__numberInput.valueAsNumber;
|
||||
}
|
||||
|
||||
set valueAsNumber(newValue: number) {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'number';
|
||||
input.valueAsNumber = newValue;
|
||||
this.value = input.value;
|
||||
this.__numberInput.valueAsNumber = newValue;
|
||||
this.value = this.__numberInput.value;
|
||||
}
|
||||
|
||||
/** Gets the validity state object */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user