Compare commits

...

41 Commits

Author SHA1 Message Date
Cory LaViska
8cf1171a65 cleanup /index.html from search results 2023-07-13 15:21:37 -04:00
Cory LaViska
414197acc9 unset last focused item; #1436 (#1446) 2023-07-12 14:52:13 -04:00
Ben Anderson
8fd01e1eda Add event types to react wrapper components (#1419)
* Rename SlSlideChange for consistency with other events

* Setup React event types for events used by Shoelace components

Means that consumers of Shoelace via the React wrapper will be able to
use callback methods with the correct event type, instead of having to
rely on casting and friends when using Typescript.

* Add docs demonstrating importing event types for React callbacks
2023-07-12 11:31:27 -04:00
Cory LaViska
e1ca7d1f59 Lit a11y update (#1444)
* update eslint-plugin-lit-a11y to latest

* update eslint deps

* remove aria- and role attribs from slots; closes #1422
2023-07-12 11:12:15 -04:00
Cory LaViska
f84d6939bd Doc updates (#1445)
* rename to CSS parts

* fix double dashes from merging
2023-07-11 15:23:51 -04:00
Konnor Rogers
82446e2114 Add modal tab tracking (#1403)
* add modal tab tracking

* prettier

* sort by tabindex

* sort by tabindex

* add a dialog test case for shadow roots

* add a changelog note

* add a changelog note

* prettier + test fixes

* prettier + test fixes
2023-07-07 15:32:23 -04:00
Konnor Rogers
a4f0ae9088 fix: valueAsDate now falls back to native implementation (#1399)
* fix: valueAsDate now falls back to native implementation

* changelog

* prettier

* prettier
2023-07-07 13:51:22 -04:00
Cory LaViska
fe3906f766 Don't steal focus when removing focused tree items (#1430)
* don't steal focus when removing focused tree items; #1428

* update PR link
2023-07-06 10:36:41 -04:00
Cory LaViska
c9e644f3fc Allow selecting menu items with space (#1429)
* allow selecting menu items with space; #1423

* update PR
2023-07-06 10:36:29 -04:00
Cory LaViska
8ffbd02db7 update changelog 2023-07-05 16:32:59 -04:00
Evan Harrison
e88d57d17d change .floor to .ceil in getCurrentPage; modify prev function to return to closest previous snappable index (#1420) 2023-07-05 16:26:00 -04:00
Cory LaViska
5f4de6d9f5 skip flaky tests 2023-07-03 15:59:24 -04:00
Cory LaViska
2cce87deeb add links 2023-07-03 15:50:58 -04:00
Cory LaViska
630b5b19a0 fix regression; #1417 2023-07-03 15:49:25 -04:00
Cory LaViska
2ce1451a9f fix typo 2023-07-03 15:17:43 -04:00
Cory LaViska
2d1badba96 this is why we can't have nice things 2023-07-03 13:31:28 -04:00
Cory LaViska
1b5db078a7 move aria attribs off <slot>; fixes #1417 2023-07-03 12:39:42 -04:00
Cory LaViska
91095bd63a better description for lighthouse 2023-07-03 12:38:09 -04:00
Cory LaViska
d9703a64fd restore concurrency 2023-07-03 11:25:43 -04:00
Cory LaViska
4c22e72390 update changelog 2023-07-03 11:21:39 -04:00
dhellgartner
d05b8fca20 Qr code tests (#1416)
* Add tests for qr-code

* Fix a small bug in qr-code

The background color was not passed to the
qr code

---------

Co-authored-by: Dominikus Hellgartner <dominikus.hellgartner@gmail.com>
2023-07-03 11:19:50 -04:00
Evan Harrison
afca2ad2e0 change carousel docs to use correct attr name, slides-per-page (#1415) 2023-07-03 11:14:15 -04:00
Cory LaViska
b2aa854d98 update docs 2023-06-28 15:27:21 -04:00
Cory LaViska
287fff7cf1 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2023-06-26 12:25:30 -04:00
Cory LaViska
136ecae4a6 2.5.2 2023-06-26 12:21:04 -04:00
Cory LaViska
cac772d5e6 update version 2023-06-26 12:20:55 -04:00
Cory LaViska
e1dedcb1b5 Fix broken links (#1407)
* add spacing

* update old links
2023-06-26 12:20:13 -04:00
Cory LaViska
c4901eca68 update old links 2023-06-26 12:17:48 -04:00
Cory LaViska
a001c2d12b add spacing 2023-06-26 12:11:15 -04:00
Eddie Cheng R4
c4c622eabd redirect the link to the right page (#1404) 2023-06-26 12:08:54 -04:00
Cory LaViska
1ae018bedd fix broken source buttons in docs (#1401) 2023-06-23 12:03:51 -04:00
Cory LaViska
24929e27c1 skip 2023-06-22 11:23:45 -04:00
Cory LaViska
33a8d92aec update version 2023-06-22 11:11:11 -04:00
Cory LaViska
32d21fa560 2.5.1 2023-06-22 11:10:15 -04:00
Cory LaViska
347d8b7f79 Merge branch 'next' of https://github.com/shoelace-style/shoelace into next 2023-06-22 11:04:34 -04:00
Cory LaViska
8f9c15913b update changelog 2023-06-22 11:04:33 -04:00
Konnor Rogers
60d7f688eb fix extensionless imports (#1394) 2023-06-22 10:56:24 -04:00
Cory LaViska
15f914914c simplify theme toggle 2023-06-22 10:47:41 -04:00
Alan Chambers
2914475821 docs changed theme toggle to theme selector (#1395) 2023-06-22 10:44:08 -04:00
Konnor Rogers
8e831aa3e7 fix source flavors (#1388)
* fix load flavor

* Update docs/assets/scripts/code-previews.js

* fix previews

---------

Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2023-06-21 11:12:09 -04:00
Cory LaViska
985d4585c4 fixes #1387 (#1392) 2023-06-21 11:07:02 -04:00
204 changed files with 1875 additions and 1372 deletions

View File

@@ -92,7 +92,8 @@ module.exports = {
'@typescript-eslint/member-delimiter-style': 'warn',
'@typescript-eslint/method-signature-style': 'warn',
'@typescript-eslint/no-extraneous-class': 'error',
'@typescript-eslint/no-parameter-properties': 'error',
'@typescript-eslint/no-redundant-type-constituents': 'off',
'@typescript-eslint/parameter-properties': 'error',
'@typescript-eslint/strict-boolean-expressions': 'off'
}
},
@@ -185,6 +186,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,

View File

@@ -116,6 +116,7 @@ export default {
if (classDoc?.events) {
classDoc.events.forEach(event => {
event.reactName = `on${pascalCase(event.name)}`;
event.eventName = `${pascalCase(event.name)}Event`;
});
}
}

View File

@@ -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>

View File

@@ -42,8 +42,8 @@
<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>

View File

@@ -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>

View 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

View File

@@ -48,6 +48,22 @@
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';
@@ -61,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
@@ -147,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'));
}
//
@@ -243,4 +243,7 @@
form.remove();
}
});
// Set the initial flavor
window.addEventListener('turbo:load', syncFlavor);
})();

View File

@@ -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);
})();
//

View File

@@ -284,7 +284,7 @@
const a = document.createElement('a');
const displayTitle = page.title ?? '';
const displayDescription = page.description ?? '';
const displayUrl = page.url.replace(/^\//, '');
const displayUrl = page.url.replace(/^\//, '').replace(/\/$/, '');
let icon = 'file-text';
a.setAttribute('role', 'option');

View File

@@ -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);
}

View File

@@ -171,7 +171,10 @@ module.exports = function (eleventyConfig) {
this.field('c'); // content
results.forEach((result, index) => {
const url = path.join('/', path.relative(eleventyConfig.dir.output, result.outputPath)).replace(/\\/g, '/');
const url = path
.join('/', path.relative(eleventyConfig.dir.output, result.outputPath))
.replace(/\\/g, '/') // convert backslashes to forward slashes
.replace(/\/index.html$/, '/'); // convert trailing /index.html to /
const doc = new JSDOM(result.content, {
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
// identify which ones are internal and which ones are external.

View File

@@ -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">

View File

@@ -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">

View File

@@ -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.

View File

@@ -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">

View File

@@ -83,6 +83,25 @@ function MyComponent() {
export default MyComponent;
```
You can also import the event type for use in your callbacks, shown below.
```tsx
import { useCallback, useState } from 'react';
import { SlInput, SlInputEvent } from '@shoelace-style/shoelace/%NPMDIR%/react';
import type SlInputElement from '@shoelace-style/shoelace/%NPMDIR%/components/input/input';
function MyComponent() {
const [value, setValue] = useState('');
const onInput = useCallback((event: SlInputEvent) => {
setValue(event.detail);
}, []);
return <SlInput value={value} onSlInput={event => setValue((event.target as SlInputElement).value)} />;
}
export default MyComponent;
```
## Testing with Jest
Testing with web components can be challenging if your test environment runs in a Node environment (i.e. it doesn't run in a real browser). Fortunately, [Jest](https://jestjs.io/) has made a number of strides to support web components and provide additional browser APIs. However, it's still not a complete replication of a browser environment.

View File

@@ -6,13 +6,13 @@ meta:
# Customizing
Shoelace components can be customized at a high level through design tokens. This gives you control over theme colors and general styling. For more advanced customizations, you can make use of component parts and custom properties to target individual components.
Shoelace components can be customized at a high level through design tokens. This gives you control over theme colors and general styling. For more advanced customizations, you can make use of CSS parts and custom properties to target individual components.
## Design Tokens
Shoelace makes use of several design tokens to provide a consistent appearance across components. You can customize them and use them in your own application with pure CSS — no preprocessor required.
Design tokens offer a high-level way to customize the library with minimal effort. There are no component-specific variables, however, as design tokens are intended to be generic and highly reusable. To customize an individual component, refer to the section entitled [Component Parts](#component-parts).
Design tokens offer a high-level way to customize the library with minimal effort. There are no component-specific variables, however, as design tokens are intended to be generic and highly reusable. To customize an individual component, refer to the section entitled [CSS Parts](#css-parts).
Design tokens are accessed through CSS custom properties that are defined in your theme. Because design tokens live at the page level, they're prefixed with `--sl-` to avoid collisions with other libraries.
@@ -37,9 +37,9 @@ To customize a design token, simply override it in your stylesheet using a `:roo
Many design tokens are described further along in this documentation. For a complete list, refer to `src/themes/light.css` in the project's [source code](https://github.com/shoelace-style/shoelace/blob/current/src/themes/light.css).
## Component Parts
## CSS Parts
Whereas design tokens offer a high-level way to customize the library, component parts offer a low-level way to customize individual components. Again, this is done with pure CSS — no preprocessor required.
Whereas design tokens offer a high-level way to customize the library, CSS parts offer a low-level way to customize individual components. Again, this is done with pure CSS — no preprocessor required.
Shoelace components use a [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM) to encapsulate their styles and behaviors. As a result, you can't simply target their internals with the usual CSS selectors. Instead, components expose "parts" that can be targeted with the [CSS part selector](https://developer.mozilla.org/en-US/docs/Web/CSS/::part), or `::part()`.
@@ -76,7 +76,7 @@ At first glance, this approach might seem a bit verbose or even limiting, but it
- Customizations can be made to components with explicit selectors, such as `::part(icon)`, rather than implicit selectors, such as `.button > div > span + .icon`, that are much more fragile.
- The internal structure of a component will likely change as it evolves. By exposing component parts through an API, the internals can be reworked without fear of breaking customizations as long as its parts remain intact.
- The internal structure of a component will likely change as it evolves. By exposing CSS parts through an API, the internals can be reworked without fear of breaking customizations as long as its parts remain intact.
- It encourages us to think more about how components are designed and how customizations should be allowed before users can take advantage of them. Once we opt a part into the component's API, it's guaranteed to be supported and can't be removed until a major version of the library is released.

View File

@@ -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/) ![Awesome emoji](/assets/images/awesome.svg)
</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

View File

@@ -12,6 +12,29 @@ Components with the <sl-badge variant="warning" pill>Experimental</sl-badge> bad
New versions of Shoelace are released as-needed and generally occur when a critical mass of changes have accumulated. At any time, you can see what's coming in the next release by visiting [next.shoelace.style](https://next.shoelace.style).
## Next
- Added tests for `<sl-qr-code>` [#1416]
- Added support for pressing [[Space]] to select/toggle selected `<sl-menu-item>` elements [#1429]
- Fixed a bug in focus trapping of modal elements like `<sl-dialog>`. We now manually handle focus ordering as well as added `offsetParent()` check for tabbable boundaries in Safari. Test cases added for `<sl-dialog>` inside a shadowRoot [#1403]
- Fixed a bug in `valueAsDate` on `<sl-input>` where it would always set `type="date"` for the underlying `<input>` element. It now falls back to the native browser implementation for the in-memory input. This may cause unexpected behavior if you're using `valueAsDate` on any input elements that aren't `type="date"`. [#1399]
- 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 number of slots that incorrectly had aria- and/or role attributes directly on them [#1422]
- Fixed a bug in `<sl-tree>` that caused focus to be stolen when removing focused tree items [#1430]
- Updated ESLint and related plugins to the latest versions
## 2.5.2
- Fixed broken source buttons in the docs [#1401]
## 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:
@@ -23,24 +46,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 the ability to use Sprite Sheets when using `<sl-icon>` via a custom resolver.
- Added tests for `<sl-split-panel>` [#1343](https://github.com/shoelace-style/shoelace/pull/1343)
- 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)
- Fixed a bug in `<sl-radio-group>` that sometimes caused dual scrollbars in containers that overflowed [#1380](https://github.com/shoelace-style/shoelace/issues/1380)
- Fixed a bug in `<sl-carousel>` not loading the English language pack automatically. [#1384](https://github.com/shoelace-style/shoelace/pull/1384)
- 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
@@ -1130,12 +1153,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

View File

@@ -18,146 +18,146 @@ Currently, the source of design tokens is considered to be [`light.css`](https:/
Focus ring tokens control the appearance of focus rings. Note that form inputs use `--sl-input-focus-ring-*` tokens instead.
| Token | Value |
| ------------------------ | ------------------------------------------------------------------------------------- |
| `--sl-focus-ring-color` | var(--sl-color-primary-600) (light theme)<br>var(--sl-color-primary-700) (dark theme) |
| `--sl-focus-ring-style` | solid |
| `--sl-focus-ring-width` | 3px |
| `--sl-focus-ring` | var(--sl-focus-ring-style) var(--sl-focus-ring-width) var(--sl-focus-ring-color) |
| `--sl-focus-ring-offset` | 1px |
| Token | Value |
| ------------------------ | ----------------------------------------------------------------------------------------- |
| `--sl-focus-ring-color` | `var(--sl-color-primary-600)` (light theme)<br>`var(--sl-color-primary-700)` (dark theme) |
| `--sl-focus-ring-style` | `solid` |
| `--sl-focus-ring-width` | `3px` |
| `--sl-focus-ring` | `var(--sl-focus-ring-style) var(--sl-focus-ring-width) var(--sl-focus-ring-color)` |
| `--sl-focus-ring-offset` | `1px` |
## Buttons
Button tokens control the appearance of buttons. In addition, buttons also currently use some form input tokens such as `--sl-input-height-*` and `--sl-input-border-*`. More button tokens may be added in the future to make it easier to style them more independently.
| Token | Value |
| ------------------------------ | --------------------------- |
| `--sl-button-font-size-small` | var(--sl-font-size-x-small) |
| `--sl-button-font-size-medium` | var(--sl-font-size-small) |
| `--sl-button-font-size-large` | var(--sl-font-size-medium) |
| Token | Value |
| ------------------------------ | ----------------------------- |
| `--sl-button-font-size-small` | `var(--sl-font-size-x-small)` |
| `--sl-button-font-size-medium` | `var(--sl-font-size-small)` |
| `--sl-button-font-size-large` | `var(--sl-font-size-medium)` |
## Form Inputs
Form input tokens control the appearance of form controls such as [input](/components/input), [select](/components/select), [textarea](/components/textarea), etc.
| Token | Value |
| --------------------------------------- | -------------------------------- |
| `--sl-input-height-small` | 1.875rem; (30px @ 16px base) |
| `--sl-input-height-medium` | 2.5rem; (40px @ 16px base) |
| `--sl-input-height-large` | 3.125rem; (50px @ 16px base) |
| `--sl-input-background-color` | var(--sl-color-neutral-0) |
| `--sl-input-background-color-hover` | var(--sl-input-background-color) |
| `--sl-input-background-color-focus` | var(--sl-input-background-color) |
| `--sl-input-background-color-disabled` | var(--sl-color-neutral-100) |
| `--sl-input-border-color` | var(--sl-color-neutral-300) |
| `--sl-input-border-color-hover` | var(--sl-color-neutral-400) |
| `--sl-input-border-color-focus` | var(--sl-color-primary-500) |
| `--sl-input-border-color-disabled` | var(--sl-color-neutral-300) |
| `--sl-input-border-width` | 1px |
| `--sl-input-required-content` | "\*" |
| `--sl-input-required-content-offset` | -2px |
| `--sl-input-required-content-color` | var(--sl-input-label-color) |
| `--sl-input-border-radius-small` | var(--sl-border-radius-medium) |
| `--sl-input-border-radius-medium` | var(--sl-border-radius-medium) |
| `--sl-input-border-radius-large` | var(--sl-border-radius-medium) |
| `--sl-input-font-family` | var(--sl-font-sans) |
| `--sl-input-font-weight` | var(--sl-font-weight-normal) |
| `--sl-input-font-size-small` | var(--sl-font-size-small) |
| `--sl-input-font-size-medium` | var(--sl-font-size-medium) |
| `--sl-input-font-size-large` | var(--sl-font-size-large) |
| `--sl-input-letter-spacing` | var(--sl-letter-spacing-normal) |
| `--sl-input-color` | var(--sl-color-neutral-700) |
| `--sl-input-color-hover` | var(--sl-color-neutral-700) |
| `--sl-input-color-focus` | var(--sl-color-neutral-700) |
| `--sl-input-color-disabled` | var(--sl-color-neutral-900) |
| `--sl-input-icon-color` | var(--sl-color-neutral-500) |
| `--sl-input-icon-color-hover` | var(--sl-color-neutral-600) |
| `--sl-input-icon-color-focus` | var(--sl-color-neutral-600) |
| `--sl-input-placeholder-color` | var(--sl-color-neutral-500) |
| `--sl-input-placeholder-color-disabled` | var(--sl-color-neutral-600) |
| `--sl-input-spacing-small` | var(--sl-spacing-small) |
| `--sl-input-spacing-medium` | var(--sl-spacing-medium) |
| `--sl-input-spacing-large` | var(--sl-spacing-large) |
| `--sl-input-focus-ring-color` | hsl(198.6 88.7% 48.4% / 40%) |
| `--sl-input-focus-ring-offset` | 0 |
| Token | Value |
| --------------------------------------- | ---------------------------------- |
| `--sl-input-height-small` | `1.875rem` (30px @ 16px base) |
| `--sl-input-height-medium` | `2.5rem` (40px @ 16px base) |
| `--sl-input-height-large` | `3.125rem` (50px @ 16px base) |
| `--sl-input-background-color` | `var(--sl-color-neutral-0)` |
| `--sl-input-background-color-hover` | `var(--sl-input-background-color)` |
| `--sl-input-background-color-focus` | `var(--sl-input-background-color)` |
| `--sl-input-background-color-disabled` | `var(--sl-color-neutral-100)` |
| `--sl-input-border-color` | `var(--sl-color-neutral-300)` |
| `--sl-input-border-color-hover` | `var(--sl-color-neutral-400)` |
| `--sl-input-border-color-focus` | `var(--sl-color-primary-500)` |
| `--sl-input-border-color-disabled` | `var(--sl-color-neutral-300)` |
| `--sl-input-border-width` | `1px` |
| `--sl-input-required-content` | `*` |
| `--sl-input-required-content-offset` | `-2px` |
| `--sl-input-required-content-color` | `var(--sl-input-label-color)` |
| `--sl-input-border-radius-small` | `var(--sl-border-radius-medium)` |
| `--sl-input-border-radius-medium` | `var(--sl-border-radius-medium)` |
| `--sl-input-border-radius-large` | `var(--sl-border-radius-medium)` |
| `--sl-input-font-family` | `var(--sl-font-sans)` |
| `--sl-input-font-weight` | `var(--sl-font-weight-normal)` |
| `--sl-input-font-size-small` | `var(--sl-font-size-small)` |
| `--sl-input-font-size-medium` | `var(--sl-font-size-medium)` |
| `--sl-input-font-size-large` | `var(--sl-font-size-large)` |
| `--sl-input-letter-spacing` | `var(--sl-letter-spacing-normal)` |
| `--sl-input-color` | `var(--sl-color-neutral-700)` |
| `--sl-input-color-hover` | `var(--sl-color-neutral-700)` |
| `--sl-input-color-focus` | `var(--sl-color-neutral-700)` |
| `--sl-input-color-disabled` | `var(--sl-color-neutral-900)` |
| `--sl-input-icon-color` | `var(--sl-color-neutral-500)` |
| `--sl-input-icon-color-hover` | `var(--sl-color-neutral-600)` |
| `--sl-input-icon-color-focus` | `var(--sl-color-neutral-600)` |
| `--sl-input-placeholder-color` | `var(--sl-color-neutral-500)` |
| `--sl-input-placeholder-color-disabled` | `var(--sl-color-neutral-600)` |
| `--sl-input-spacing-small` | `var(--sl-spacing-small)` |
| `--sl-input-spacing-medium` | `var(--sl-spacing-medium)` |
| `--sl-input-spacing-large` | `var(--sl-spacing-large)` |
| `--sl-input-focus-ring-color` | `hsl(198.6 88.7% 48.4% / 40%)` |
| `--sl-input-focus-ring-offset` | `0` |
## Filled Form Inputs
Filled form input tokens control the appearance of form controls using the `filled` variant.
| Token | Value |
| --------------------------------------------- | --------------------------- |
| `--sl-input-filled-background-color` | var(--sl-color-neutral-100) |
| `--sl-input-filled-background-color-hover` | var(--sl-color-neutral-100) |
| `--sl-input-filled-background-color-focus` | var(--sl-color-neutral-100) |
| `--sl-input-filled-background-color-disabled` | var(--sl-color-neutral-100) |
| `--sl-input-filled-color` | var(--sl-color-neutral-800) |
| `--sl-input-filled-color-hover` | var(--sl-color-neutral-800) |
| `--sl-input-filled-color-focus` | var(--sl-color-neutral-700) |
| `--sl-input-filled-color-disabled` | var(--sl-color-neutral-800) |
| Token | Value |
| --------------------------------------------- | ----------------------------- |
| `--sl-input-filled-background-color` | `var(--sl-color-neutral-100)` |
| `--sl-input-filled-background-color-hover` | `var(--sl-color-neutral-100)` |
| `--sl-input-filled-background-color-focus` | `var(--sl-color-neutral-100)` |
| `--sl-input-filled-background-color-disabled` | `var(--sl-color-neutral-100)` |
| `--sl-input-filled-color` | `var(--sl-color-neutral-800)` |
| `--sl-input-filled-color-hover` | `var(--sl-color-neutral-800)` |
| `--sl-input-filled-color-focus` | `var(--sl-color-neutral-700)` |
| `--sl-input-filled-color-disabled` | `var(--sl-color-neutral-800)` |
## Form Labels
Form label tokens control the appearance of labels in form controls.
| Token | Value |
| ----------------------------------- | -------------------------- |
| `--sl-input-label-font-size-small` | var(--sl-font-size-small) |
| `--sl-input-label-font-size-medium` | var(--sl-font-size-medium) |
| `--sl-input-label-font-size-large` | var(--sl-font-size-large) |
| `--sl-input-label-color` | inherit |
| Token | Value |
| ----------------------------------- | ---------------------------- |
| `--sl-input-label-font-size-small` | `var(--sl-font-size-small)` |
| `--sl-input-label-font-size-medium` | `var(--sl-font-size-medium`) |
| `--sl-input-label-font-size-large` | `var(--sl-font-size-large)` |
| `--sl-input-label-color` | `inherit` |
## Help Text
Help text tokens control the appearance of help text in form controls.
| Token | Value |
| --------------------------------------- | --------------------------- |
| `--sl-input-help-text-font-size-small` | var(--sl-font-size-x-small) |
| `--sl-input-help-text-font-size-medium` | var(--sl-font-size-small) |
| `--sl-input-help-text-font-size-large` | var(--sl-font-size-medium) |
| `--sl-input-help-text-color` | var(--sl-color-neutral-500) |
| Token | Value |
| --------------------------------------- | ----------------------------- |
| `--sl-input-help-text-font-size-small` | `var(--sl-font-size-x-small)` |
| `--sl-input-help-text-font-size-medium` | `var(--sl-font-size-small)` |
| `--sl-input-help-text-font-size-large` | `var(--sl-font-size-medium)` |
| `--sl-input-help-text-color` | `var(--sl-color-neutral-500)` |
## Toggles
Toggle tokens control the appearance of toggles such as [checkbox](/components/checkbox), [radio](/components/radio), [switch](/components/switch), etc.
| Token | Value |
| ------------------------- | --------------------------- |
| `--sl-toggle-size-small` | 0.875rem (14px @ 16px base) |
| `--sl-toggle-size-medium` | 1.125rem (18px @ 16px base) |
| `--sl-toggle-size-large` | 1.375rem (22px @ 16px base) |
| Token | Value |
| ------------------------- | ----------------------------- |
| `--sl-toggle-size-small` | `0.875rem` (14px @ 16px base) |
| `--sl-toggle-size-medium` | `1.125rem` (18px @ 16px base) |
| `--sl-toggle-size-large` | `1.375rem` (22px @ 16px base) |
## Overlays
Overlay tokens control the appearance of overlays as used in [dialog](/components/dialog), [drawer](/components/drawer), etc.
| Token | Value |
| ------------------------------- | ------------------------- |
| `--sl-overlay-background-color` | hsl(240 3.8% 46.1% / 33%) |
| Token | Value |
| ------------------------------- | --------------------------- |
| `--sl-overlay-background-color` | `hsl(240 3.8% 46.1% / 33%)` |
## Panels
Panel tokens control the appearance of panels such as those used in [dialog](/components/dialog), [drawer](/components/drawer), [menu](/components/menu), etc.
| Token | Value |
| ----------------------------- | --------------------------- |
| `--sl-panel-background-color` | var(--sl-color-neutral-0) |
| `--sl-panel-border-color` | var(--sl-color-neutral-200) |
| `--sl-panel-border-width` | 1px |
| Token | Value |
| ----------------------------- | ----------------------------- |
| `--sl-panel-background-color` | `var(--sl-color-neutral-0)` |
| `--sl-panel-border-color` | `var(--sl-color-neutral-200)` |
| `--sl-panel-border-width` | `1px` |
## Tooltips
Tooltip tokens control the appearance of tooltips. This includes the [tooltip](/components/tooltip) component as well as other implementations, such [range tooltips](/components/range).
| Token | Value |
| ------------------------------- | ---------------------------------------------------- |
| `--sl-tooltip-border-radius` | var(--sl-border-radius-medium) |
| `--sl-tooltip-background-color` | var(--sl-color-neutral-800) |
| `--sl-tooltip-color` | var(--sl-color-neutral-0) |
| `--sl-tooltip-font-family` | var(--sl-font-sans) |
| `--sl-tooltip-font-weight` | var(--sl-font-weight-normal) |
| `--sl-tooltip-font-size` | var(--sl-font-size-small) |
| `--sl-tooltip-line-height` | var(--sl-line-height-dense) |
| `--sl-tooltip-padding` | var(--sl-spacing-2x-small) var(--sl-spacing-x-small) |
| `--sl-tooltip-arrow-size` | 6px |
| Token | Value |
| ------------------------------- | ------------------------------------------------------ |
| `--sl-tooltip-border-radius` | `var(--sl-border-radius-medium)` |
| `--sl-tooltip-background-color` | `var(--sl-color-neutral-800)` |
| `--sl-tooltip-color` | `var(--sl-color-neutral-0)` |
| `--sl-tooltip-font-family` | `var(--sl-font-sans)` |
| `--sl-tooltip-font-weight` | `var(--sl-font-weight-normal)` |
| `--sl-tooltip-font-size` | `var(--sl-font-size-small)` |
| `--sl-tooltip-line-height` | `var(--sl-line-height-dense)` |
| `--sl-tooltip-padding` | `var(--sl-spacing-2x-small) var(--sl-spacing-x-small)` |
| `--sl-tooltip-arrow-size` | `6px` |

View File

@@ -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';

View File

@@ -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.

View File

@@ -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

842
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "@shoelace-style/shoelace",
"description": "A forward-thinking library of web components.",
"version": "2.5.0",
"version": "2.5.2",
"homepage": "https://github.com/shoelace-style/shoelace",
"author": "Cory LaViska",
"license": "MIT",
@@ -79,8 +79,8 @@
"@open-wc/testing": "^3.1.7",
"@types/mocha": "^10.0.1",
"@types/react": "^18.0.26",
"@typescript-eslint/eslint-plugin": "^5.48.1",
"@typescript-eslint/parser": "^5.48.1",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@web/dev-server-esbuild": "^0.3.3",
"@web/test-runner": "^0.15.0",
"@web/test-runner-commands": "^0.6.5",
@@ -96,15 +96,15 @@
"del": "^7.0.0",
"download": "^8.0.0",
"esbuild": "^0.18.2",
"eslint": "^8.31.0",
"eslint": "^8.44.0",
"eslint-plugin-chai-expect": "^3.0.0",
"eslint-plugin-chai-friendly": "^0.7.2",
"eslint-plugin-import": "^2.27.4",
"eslint-plugin-lit": "^1.8.2",
"eslint-plugin-lit-a11y": "^2.3.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-lit": "^1.8.3",
"eslint-plugin-lit-a11y": "^4.1.0",
"eslint-plugin-markdown": "^3.0.0",
"eslint-plugin-sort-imports-es6-autofix": "^0.6.0",
"eslint-plugin-wc": "^1.4.0",
"eslint-plugin-wc": "^1.5.0",
"front-matter": "^4.0.2",
"get-port": "^7.0.0",
"globby": "^13.1.3",

View File

@@ -25,7 +25,14 @@ components.map(component => {
const componentDir = path.join(reactDir, tagWithoutPrefix);
const componentFile = path.join(componentDir, 'index.ts');
const importPath = component.path;
const events = (component.events || []).map(event => `${event.reactName}: '${event.name}'`).join(',\n');
const eventImports = (component.events || [])
.map(event => `import { ${event.eventName} } from '../../../src/events/events';`)
.join('\n');
const eventNameImport =
(component.events || []).length > 0 ? `import { type EventName } from '@lit-labs/react';` : ``;
const events = (component.events || [])
.map(event => `${event.reactName}: '${event.name}' as EventName<${event.eventName}>`)
.join(',\n');
fs.mkdirSync(componentDir, { recursive: true });
@@ -35,6 +42,9 @@ components.map(component => {
import { createComponent } from '@lit-labs/react';
import Component from '../../${importPath}';
${eventNameImport}
${eventImports}
export default createComponent({
tagName: '${component.tagName}',
elementClass: Component,
@@ -49,7 +59,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');
});

View File

@@ -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 */`
}
]
});

View File

@@ -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';
/**

View File

@@ -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}

View File

@@ -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}

View File

@@ -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"]')!;

View File

@@ -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' });
@@ -199,9 +199,13 @@ export default class SlAlert extends ShoelaceElement {
aria-hidden=${this.open ? 'false' : 'true'}
@mousemove=${this.handleMouseMove}
>
<slot name="icon" part="icon" class="alert__icon"></slot>
<div part="icon" class="alert__icon">
<slot name="icon"></slot>
</div>
<slot part="message" class="alert__message" aria-live="polite"></slot>
<div part="message" class="alert__message" aria-live="polite">
<slot></slot>
</div>
${this.closable
? html`

View File

@@ -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}

View File

@@ -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 () => {

View File

@@ -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';
/**

View File

@@ -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}

View File

@@ -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>`;

View File

@@ -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';
/**

View File

@@ -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}

View File

@@ -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.

View File

@@ -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';
/**
@@ -69,9 +69,11 @@ export default class SlAvatar extends ShoelaceElement {
avatarWithoutImage = html`<div part="initials" class="avatar__initials">${this.initials}</div>`;
} else {
avatarWithoutImage = html`
<slot name="icon" part="icon" class="avatar__icon" aria-hidden="true">
<sl-icon name="person-fill" library="system"></sl-icon>
</slot>
<div part="icon" class="avatar__icon" aria-hidden="true">
<slot name="icon">
<sl-icon name="person-fill" library="system"></sl-icon>
</slot>
</div>
`;
}

View File

@@ -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}

View File

@@ -1,6 +1,10 @@
import '../../../dist/shoelace.js';
import { expect, fixture, html } from '@open-wc/testing';
import type SlBadge from './badge';
import type SlBadge from './badge.js';
// The default badge background just misses AA contrast, but the next step up is way too dark. We're going to relax this
// rule for now.
const ignoredRules = ['color-contrast'];
describe('<sl-badge>', () => {
let el: SlBadge;
@@ -11,7 +15,7 @@ describe('<sl-badge>', () => {
});
it('should pass accessibility tests with a role of status on the base part.', async () => {
await expect(el).to.be.accessible();
await expect(el).to.be.accessible({ ignoredRules });
const part = el.shadowRoot!.querySelector('[part~="base"]')!;
expect(part.getAttribute('role')).to.eq('status');
@@ -33,7 +37,7 @@ describe('<sl-badge>', () => {
});
it('should pass accessibility tests', async () => {
await expect(el).to.be.accessible();
await expect(el).to.be.accessible({ ignoredRules });
});
it('should append the pill class to the classlist to render a pill', () => {
@@ -48,7 +52,7 @@ describe('<sl-badge>', () => {
});
it('should pass accessibility tests', async () => {
await expect(el).to.be.accessible();
await expect(el).to.be.accessible({ ignoredRules });
});
it('should append the pulse class to the classlist to render a pulse', () => {
@@ -64,7 +68,7 @@ describe('<sl-badge>', () => {
});
it('should pass accessibility tests', async () => {
await expect(el).to.be.accessible();
await expect(el).to.be.accessible({ ignoredRules });
});
it('should default to square styling, with the primary color', () => {

View File

@@ -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';
/**
@@ -30,7 +30,7 @@ export default class SlBadge extends ShoelaceElement {
render() {
return html`
<slot
<span
part="base"
class=${classMap({
badge: true,
@@ -43,7 +43,9 @@ export default class SlBadge extends ShoelaceElement {
'badge--pulse': this.pulse
})}
role="status"
></slot>
>
<slot></slot>
</span>
`;
}
}

View File

@@ -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}

View File

@@ -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;

View File

@@ -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';
/**
@@ -55,7 +55,9 @@ export default class SlBreadcrumbItem extends ShoelaceElement {
'breadcrumb-item--has-suffix': this.hasSlotController.test('suffix')
})}
>
<slot name="prefix" part="prefix" class="breadcrumb-item__prefix"></slot>
<span part="prefix" class="breadcrumb-item__prefix">
<slot name="prefix"></slot>
</span>
${isLink
? html`
@@ -75,9 +77,13 @@ export default class SlBreadcrumbItem extends ShoelaceElement {
</button>
`}
<slot name="suffix" part="suffix" class="breadcrumb-item__suffix"></slot>
<span part="suffix" class="breadcrumb-item__suffix">
<slot name="suffix"></slot>
</span>
<slot name="separator" part="separator" class="breadcrumb-item__separator" aria-hidden="true"></slot>
<span part="separator" class="breadcrumb-item__separator" aria-hidden="true">
<slot name="separator"></slot>
</span>
</div>
`;
}

View File

@@ -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}

View File

@@ -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.

View File

@@ -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.
@@ -90,9 +90,11 @@ export default class SlBreadcrumb extends ShoelaceElement {
<slot @slotchange=${this.handleSlotChange}></slot>
</nav>
<slot name="separator" hidden aria-hidden="true">
<sl-icon name=${this.localize.dir() === 'rtl' ? 'chevron-left' : 'chevron-right'} library="system"></sl-icon>
</slot>
<span hidden aria-hidden="true">
<slot name="separator">
<sl-icon name=${this.localize.dir() === 'rtl' ? 'chevron-left' : 'chevron-right'} library="system"></sl-icon>
</slot>
</span>
`;
}
}

View File

@@ -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}

View File

@@ -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 ', () => {

View File

@@ -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';
/**
@@ -68,7 +68,7 @@ export default class SlButtonGroup extends ShoelaceElement {
render() {
// eslint-disable-next-line lit-a11y/mouse-events-have-key-events
return html`
<slot
<div
part="base"
class="button-group"
role="${this.disableRole ? 'presentation' : 'group'}"
@@ -77,8 +77,9 @@ export default class SlButtonGroup extends ShoelaceElement {
@focusin=${this.handleFocus}
@mouseover=${this.handleMouseOver}
@mouseout=${this.handleMouseOut}
@slotchange=${this.handleSlotChange}
></slot>
>
<slot @slotchange=${this.handleSlotChange}></slot>
</div>
`;
}
}

View File

@@ -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}

View File

@@ -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'];

View File

@@ -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.

View File

@@ -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}

View File

@@ -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;

View File

@@ -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';
/**

View File

@@ -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}

View File

@@ -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';
/**

View File

@@ -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}

View File

@@ -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 () => {

View File

@@ -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 '../../utilities/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';
/**
@@ -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 } = {}) {
@@ -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);
}
/**

View File

@@ -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 {

View File

@@ -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}

View File

@@ -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 () => {

View File

@@ -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.

View File

@@ -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}

View File

@@ -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', () => {

View File

@@ -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;

View File

@@ -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}

View File

@@ -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';
/**

View File

@@ -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}

View File

@@ -1,6 +1,7 @@
import '../../../dist/shoelace.js';
// cspell:dictionaries lorem-ipsum
import { expect, fixture, html, waitUntil } from '@open-wc/testing';
import { aTimeout, elementUpdated, expect, fixture, html, waitUntil } from '@open-wc/testing';
import { LitElement } from 'lit';
import { sendKeys } from '@web/test-runner-commands';
import sinon from 'sinon';
import type SlDialog from './dialog';
@@ -146,4 +147,124 @@ describe('<sl-dialog>', () => {
expect(el.open).to.be.false;
});
// https://github.com/shoelace-style/shoelace/issues/1382
it('should properly cycle through tabbable elements when sl-dialog is used in a shadowRoot', async () => {
class AContainer extends LitElement {
get dialog() {
return this.shadowRoot?.querySelector('sl-dialog');
}
openDialog() {
this.dialog?.show();
}
render() {
return html`
<h1>Dialog Example</h1>
<sl-dialog label="Dialog" class="dialog-overview">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<br />
<label><input type="checkbox" />A</label>
<label><input type="checkbox" />B</label>
<button>Button</button>
</sl-dialog>
<sl-button @click=${this.openDialog}>Open Dialog</sl-button>
`;
}
}
if (!window.customElements.get('a-container')) {
window.customElements.define('a-container', AContainer);
}
const testCase = await fixture(html`
<div>
<a-container></a-container>
<p>
Open the dialog, then use <kbd>Tab</kbd> to cycle through the inputs. Focus should be trapped, but it reaches
things outside the dialog.
</p>
</div>
`);
const container = testCase.querySelector('a-container');
if (!container) {
throw Error('Could not find <a-container> element.');
}
await elementUpdated(container);
const dialog = container.shadowRoot?.querySelector('sl-dialog');
if (!dialog) {
throw Error('Could not find <sl-dialog> element.');
}
const closeButton = dialog.shadowRoot?.querySelector('sl-icon-button');
const checkbox1 = dialog.querySelector("input[type='checkbox']");
const checkbox2 = dialog.querySelectorAll("input[type='checkbox']")[1];
const button = dialog.querySelector('button');
// Opens modal.
const openModalButton = container.shadowRoot?.querySelector('sl-button');
if (openModalButton) openModalButton.click();
// Test tab cycling
await pressTab();
expect(container.shadowRoot?.activeElement).to.equal(dialog);
expect(dialog.shadowRoot?.activeElement).to.equal(closeButton);
await pressTab();
expect(container.shadowRoot?.activeElement).to.equal(checkbox1);
await pressTab();
expect(container.shadowRoot?.activeElement).to.equal(checkbox2);
await pressTab();
expect(container.shadowRoot?.activeElement).to.equal(button);
await pressTab();
expect(dialog.shadowRoot?.activeElement).to.equal(closeButton);
await pressTab();
expect(container.shadowRoot?.activeElement).to.equal(checkbox1);
// Test Shift+Tab cycling
// I found these timeouts were needed for WebKit locally.
await aTimeout(10);
await sendKeys({ down: 'Shift' });
await aTimeout(10);
await pressTab();
expect(dialog.shadowRoot?.activeElement).to.equal(closeButton);
await pressTab();
expect(container.shadowRoot?.activeElement).to.equal(button);
await pressTab();
expect(container.shadowRoot?.activeElement).to.equal(checkbox2);
await pressTab();
expect(container.shadowRoot?.activeElement).to.equal(checkbox1);
await pressTab();
expect(dialog.shadowRoot?.activeElement).to.equal(closeButton);
// End shift+tab cycling
await sendKeys({ up: 'Shift' });
});
});
// We wait 50ms just to give the browser some time to figure out the current focus.
// 50 was the magic number I found locally :shrug:
async function pressTab() {
await aTimeout(50);
await sendKeys({ press: 'Tab' });
await aTimeout(50);
}

View File

@@ -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';
/**
@@ -104,6 +104,7 @@ export default class SlDialog extends ShoelaceElement {
disconnectedCallback() {
super.disconnectedCallback();
this.modal.deactivate();
unlockBodyScrolling(this);
}
@@ -269,7 +270,7 @@ export default class SlDialog extends ShoelaceElement {
aria-hidden=${this.open ? 'false' : 'true'}
aria-label=${ifDefined(this.noHeader ? this.label : undefined)}
aria-labelledby=${ifDefined(!this.noHeader ? 'title' : undefined)}
tabindex="0"
tabindex="-1"
>
${!this.noHeader
? html`
@@ -292,8 +293,10 @@ export default class SlDialog extends ShoelaceElement {
</header>
`
: ''}
<slot part="body" class="dialog__body"></slot>
${
'' /* The tabindex="-1" is here because the body is technically scrollable if overflowing. However, if there's no focusable elements inside, you won't actually be able to scroll it via keyboard. */
}
<slot part="body" class="dialog__body" tabindex="-1"></slot>
<footer part="footer" class="dialog__footer">
<slot name="footer"></slot>

View File

@@ -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}

View File

@@ -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 ', () => {

View File

@@ -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';
/**

View File

@@ -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}

View File

@@ -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';
/**

View File

@@ -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}

View File

@@ -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 () => {

View File

@@ -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.
@@ -411,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>
`;
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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}

View File

@@ -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';
/**

View File

@@ -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}

View File

@@ -1,10 +1,10 @@
import { customElement, property, state } from 'lit/decorators.js';
import { getIconLibrary, type IconLibrary, unwatchIcon, watchIcon } from './library';
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';
import ShoelaceElement from '../../internal/shoelace-element';
import styles from './icon.styles';
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';

View File

@@ -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',

View File

@@ -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

View File

@@ -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;

View File

@@ -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}

View File

@@ -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';
/**
@@ -108,16 +108,19 @@ export default class SlImageComparer extends ShoelaceElement {
@keydown=${this.handleKeyDown}
>
<div class="image-comparer__image">
<slot name="before" part="before" class="image-comparer__before"></slot>
<div part="before" class="image-comparer__before">
<slot name="before"></slot>
</div>
<slot
name="after"
<div
part="after"
class="image-comparer__after"
style=${styleMap({
clipPath: isRtl ? `inset(0 0 0 ${100 - this.position}%)` : `inset(0 ${100 - this.position}% 0 0)`
})}
></slot>
>
<slot name="after"></slot>
</div>
</div>
<div
@@ -129,8 +132,7 @@ export default class SlImageComparer extends ShoelaceElement {
@mousedown=${this.handleDrag}
@touchstart=${this.handleDrag}
>
<slot
name="handle"
<div
part="handle"
class="image-comparer__handle"
role="scrollbar"
@@ -140,8 +142,10 @@ export default class SlImageComparer extends ShoelaceElement {
aria-controls="image-comparer"
tabindex="0"
>
<sl-icon library="system" name="grip-vertical"></sl-icon>
</slot>
<slot name="handle">
<sl-icon library="system" name="grip-vertical"></sl-icon>
</slot>
</div>
</div>
</div>
`;

View File

@@ -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}

View File

@@ -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';
/**

View File

@@ -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}
@@ -147,8 +147,8 @@ export default css`
cursor: default;
}
.input__prefix::slotted(sl-icon),
.input__suffix::slotted(sl-icon) {
.input__prefix ::slotted(sl-icon),
.input__suffix ::slotted(sl-icon) {
color: var(--sl-input-icon-color);
}
@@ -172,11 +172,11 @@ export default css`
width: calc(1em + var(--sl-input-spacing-small) * 2);
}
.input--small .input__prefix::slotted(*) {
.input--small .input__prefix ::slotted(*) {
margin-inline-start: var(--sl-input-spacing-small);
}
.input--small .input__suffix::slotted(*) {
.input--small .input__suffix ::slotted(*) {
margin-inline-end: var(--sl-input-spacing-small);
}
@@ -196,11 +196,11 @@ export default css`
width: calc(1em + var(--sl-input-spacing-medium) * 2);
}
.input--medium .input__prefix::slotted(*) {
.input--medium .input__prefix ::slotted(*) {
margin-inline-start: var(--sl-input-spacing-medium);
}
.input--medium .input__suffix::slotted(*) {
.input--medium .input__suffix ::slotted(*) {
margin-inline-end: var(--sl-input-spacing-medium);
}
@@ -220,11 +220,11 @@ export default css`
width: calc(1em + var(--sl-input-spacing-large) * 2);
}
.input--large .input__prefix::slotted(*) {
.input--large .input__prefix ::slotted(*) {
margin-inline-start: var(--sl-input-spacing-large);
}
.input--large .input__suffix::slotted(*) {
.input--large .input__suffix ::slotted(*) {
margin-inline-end: var(--sl-input-spacing-large);
}

View File

@@ -1,7 +1,7 @@
// eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
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';

Some files were not shown because too many files have changed in this diff Show More