kopia lustrzana https://github.com/shoelace-style/shoelace
Porównaj commity
84 Commity
Autor | SHA1 | Data |
---|---|---|
HappyXiaoAnAn | 6092796850 | |
Cory LaViska | 4120988079 | |
Enrico Gruner | 77c482ed16 | |
Cory LaViska | 9399df6e19 | |
Ahmad Alfy | b98deb877e | |
Konnor Rogers | f256d7aa8a | |
Cory LaViska | f757d514e4 | |
Cory LaViska | 07d2144395 | |
Cory LaViska | c042c8fe34 | |
Cory LaViska | 975115a923 | |
Cory LaViska | c31d4f5855 | |
Cory LaViska | 75e20e0672 | |
Uaena_Alex_John | 7ece400a30 | |
cptKNJO | 06a27dd899 | |
Konnor Rogers | 9767e84d26 | |
Cory LaViska | 8726910160 | |
Cory LaViska | d478ccb2da | |
Cory LaViska | d94acc6e06 | |
Fiqri Syah Redha | eb42671ef3 | |
Christian Schilling | 3ad6364678 | |
Konnor Rogers | 64996b2d35 | |
Susanne Kirchner | 0daa5d8dee | |
Konnor Rogers | 16d5575307 | |
Konnor Rogers | a427433701 | |
Danny Andrews | c6da4f5b14 | |
Cory LaViska | d0b71adb81 | |
Cory LaViska | ae66483671 | |
Cory LaViska | 537fd87497 | |
Cory LaViska | 1534f47d34 | |
Cory LaViska | eb08be0fce | |
Cory LaViska | dfc4cb6248 | |
Cory LaViska | c1eda83e5b | |
Cory LaViska | 0e5048989d | |
Konnor Rogers | ff2e0486b4 | |
Sebi | 4aa5e9c1f2 | |
Cory LaViska | 0b7e70bccf | |
Alessandro | 31f2600816 | |
Cory LaViska | f6d5344c44 | |
Nic Newdigate | 5a89439e14 | |
Cory LaViska | 2878957ef5 | |
Konnor Rogers | cd5a6486da | |
Cory LaViska | 77d6f27248 | |
Konnor Rogers | 7a62a87b9b | |
Cory LaViska | 0ac61a6a22 | |
Matt Walkland | acf76cf359 | |
Cory LaViska | 3451ec753c | |
cyantree | 2a4b3ee2e9 | |
Konnor Rogers | 3bc8495874 | |
Cory LaViska | 7f87887477 | |
Amadej Glasenčnik | c88b38f194 | |
Cory LaViska | e2bce65c02 | |
Susanne Kirchner | 9cbb0b8a95 | |
Cory LaViska | 2128e62109 | |
Cory LaViska | 12ce0217e5 | |
RoyDust | 1a2969a74b | |
Cory LaViska | 033fec9471 | |
Cory LaViska | 8272619663 | |
cyantree | 298892b10a | |
Cory LaViska | e1102ba9cf | |
Cory LaViska | b589938443 | |
Cory LaViska | 07b13d489a | |
Cory LaViska | e9405d33a8 | |
Cory LaViska | f2a42565e2 | |
Cory LaViska | 23f09dfa79 | |
stefanholzapfel | 6e288a80a3 | |
Cory LaViska | 9b19c4c782 | |
cyantree | 6440387432 | |
Cory LaViska | f3be76840f | |
Cory LaViska | 1056a10f8e | |
Cory LaViska | f9a73567f7 | |
Burton Smith | 6bc06d5d95 | |
Cory LaViska | 7571f8c534 | |
Cory LaViska | 1bf3e5a2b7 | |
Cory LaViska | 02ce4dbf4e | |
Cory LaViska | 775f30107f | |
Cory LaViska | 9ee1617696 | |
Alessandro | 7e38e93ab2 | |
Cory LaViska | dafb35c6e2 | |
Cory LaViska | a36bbe2fc4 | |
Ahmad Alfy | 4185430989 | |
Cory LaViska | e6d3d8317a | |
clintcs | 9451c3b8de | |
Konnor Rogers | a5e9b942e3 | |
Cory LaViska | 380d56fa40 |
|
@ -95,6 +95,23 @@
|
|||
</sl-dropdown>
|
||||
</div>
|
||||
|
||||
<a
|
||||
class="ks-banner{% if toc %} with-toc{% endif %}"
|
||||
href="https://www.kickstarter.com/projects/fontawesome/web-awesome?ref=71ihfk"
|
||||
target="_blank"
|
||||
>
|
||||
<span>
|
||||
<svg viewBox="0 0 20 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#f36944" d="M11.63 1.625C11.63 2.27911 11.2435 2.84296 10.6865 3.10064L14 6L17.2622 5.34755C17.0968 5.10642 17 4.81452 17 4.5C17 3.67157 17.6716 3 18.5 3C19.3284 3 20 3.67157 20 4.5C20 5.31157 19.3555 5.9726 18.5504 5.99917L15.0307 13.8207C14.7077 14.5384 13.9939 15 13.2068 15H6.79317C6.00615 15 5.29229 14.5384 4.96933 13.8207L1.44963 5.99917C0.64452 5.9726 0 5.31157 0 4.5C0 3.67157 0.671573 3 1.5 3C2.32843 3 3 3.67157 3 4.5C3 4.81452 2.9032 5.10642 2.73777 5.34755L6 6L9.31702 3.09761C8.76346 2.83855 8.38 2.27656 8.38 1.625C8.38 0.727537 9.10754 0 10.005 0C10.9025 0 11.63 0.727537 11.63 1.625Z"/>
|
||||
</svg>
|
||||
<span>
|
||||
<strong style="white-space: nowrap;">Get ready for more awesome!</strong>
|
||||
Web Awesome, the next iteration of Shoelace, is on Kickstarter.
|
||||
</span>
|
||||
</span>
|
||||
<span class="faux-button">Read Our Story</span>
|
||||
</a>
|
||||
|
||||
<aside id="sidebar" data-preserve-scroll>
|
||||
<header>
|
||||
<a href="/">
|
||||
|
|
|
@ -9,11 +9,14 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* @param {Document} content
|
||||
* @param {String} rawContent
|
||||
* @param {Replacements} replacements
|
||||
*/
|
||||
module.exports = function (content, replacements) {
|
||||
module.exports = function (rawContent, replacements) {
|
||||
let content = rawContent;
|
||||
replacements.forEach(replacement => {
|
||||
content.body.innerHTML = content.body.innerHTML.replaceAll(replacement.pattern, replacement.replacement);
|
||||
content = content.replaceAll(replacement.pattern, replacement.replacement);
|
||||
});
|
||||
|
||||
return content;
|
||||
};
|
||||
|
|
|
@ -1059,7 +1059,6 @@ html.sidebar-open #menu-toggle {
|
|||
padding: 0.5rem;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
transition: 250ms scale ease;
|
||||
}
|
||||
|
||||
#theme-selector:not(:defined) {
|
||||
|
@ -1102,12 +1101,6 @@ html.sidebar-open #menu-toggle {
|
|||
color: var(--sl-color-neutral-1000);
|
||||
}
|
||||
|
||||
#icon-toolbar button:hover,
|
||||
#icon-toolbar a:hover,
|
||||
#theme-selector sl-button:hover {
|
||||
scale: 1.1;
|
||||
}
|
||||
|
||||
#icon-toolbar a:not(:last-child),
|
||||
#icon-toolbar button:not(:last-child) {
|
||||
margin-right: 0.25rem;
|
||||
|
@ -1420,3 +1413,95 @@ body[data-page^='/tokens/'] .table-wrapper td:first-child code {
|
|||
grid-column-start: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
.ks-banner {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
width: 950px;
|
||||
left: calc(50% - 475px);
|
||||
font-size: 0.9375rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #1a3256;
|
||||
border-radius: var(--sl-border-radius-large);
|
||||
padding: 1rem 1.25rem;
|
||||
color: #fdfdfd;
|
||||
text-decoration: none;
|
||||
line-height: 1.4;
|
||||
z-index: 2;
|
||||
margin-left: 160px;
|
||||
}
|
||||
|
||||
.ks-banner:hover {
|
||||
color: #fdfdfd;
|
||||
}
|
||||
|
||||
.ks-banner > span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.ks-banner svg {
|
||||
flex: 0 0 1.5rem;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.ks-banner .faux-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
background: white;
|
||||
border: solid 1px #d4d4d4;
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
font-size: 0.8375rem;
|
||||
color: #353439;
|
||||
padding: 0.5rem 1rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ks-banner.with-toc {
|
||||
width: 1100px;
|
||||
left: calc(50% - 550px);
|
||||
margin-left: 140px;
|
||||
}
|
||||
|
||||
main {
|
||||
margin-top: 70px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1650px) {
|
||||
.ks-banner,
|
||||
.ks-banner.with-toc {
|
||||
width: 540px !important;
|
||||
top: 50px;
|
||||
left: calc(50% - 270px);
|
||||
}
|
||||
|
||||
main {
|
||||
margin-top: 140px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.ks-banner,
|
||||
.ks-banner.with-toc {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 680px) {
|
||||
.ks-banner,
|
||||
.ks-banner.with-toc {
|
||||
width: calc(100% - 2rem) !important;
|
||||
left: 1rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
margin-top: 150px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,7 +115,13 @@ module.exports = function (eleventyConfig) {
|
|||
//
|
||||
// Transforms
|
||||
//
|
||||
eleventyConfig.addTransform('html-transform', function (content) {
|
||||
eleventyConfig.addTransform('html-transform', function (rawContent) {
|
||||
let content = replacer(rawContent, [
|
||||
{ pattern: '%VERSION%', replacement: customElementsManifest.package.version },
|
||||
{ pattern: '%CDNDIR%', replacement: cdndir },
|
||||
{ pattern: '%NPMDIR%', replacement: npmdir }
|
||||
]);
|
||||
|
||||
// Parse the template and get a Document object
|
||||
const doc = new JSDOM(content, {
|
||||
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
|
||||
|
@ -140,11 +146,6 @@ module.exports = function (eleventyConfig) {
|
|||
scrollingTables(doc);
|
||||
copyCodeButtons(doc); // must be after codePreviews + highlightCodeBlocks
|
||||
typography(doc, '#content');
|
||||
replacer(doc, [
|
||||
{ pattern: '%VERSION%', replacement: customElementsManifest.package.version },
|
||||
{ pattern: '%CDNDIR%', replacement: cdndir },
|
||||
{ pattern: '%NPMDIR%', replacement: npmdir }
|
||||
]);
|
||||
|
||||
// Serialize the Document object to an HTML string and prepend the doctype
|
||||
content = `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
|
||||
|
|
|
@ -236,7 +236,7 @@ When a `target` is set, the link will receive `rel="noreferrer noopener"` for [s
|
|||
|
||||
### Setting a Custom Width
|
||||
|
||||
As expected, buttons can be given a custom width by setting the `width` attribute. This is useful for making buttons span the full width of their container on smaller screens.
|
||||
As expected, buttons can be given a custom width by passing inline styles to the component (or using a class). This is useful for making buttons span the full width of their container on smaller screens.
|
||||
|
||||
```html:preview
|
||||
<sl-button variant="default" size="small" style="width: 100%; margin-bottom: 1rem;">Small</sl-button>
|
||||
|
|
|
@ -89,6 +89,20 @@ const App = () => (
|
|||
);
|
||||
```
|
||||
|
||||
### Help Text
|
||||
|
||||
Add descriptive help text to a switch with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
|
||||
|
||||
```html:preview
|
||||
<sl-checkbox help-text="What should the user know about the checkbox?">Label</sl-checkbox>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import SlCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
|
||||
|
||||
const App = () => <SlCheckbox help-text="What should the user know about the switch?">Label</SlCheckbox>;
|
||||
```
|
||||
|
||||
### Custom Validity
|
||||
|
||||
Use the `setCustomValidity()` method to set a custom validation message. This will prevent the form from submitting and make the browser display the error message you provide. To clear the error, call this function with an empty string.
|
||||
|
|
|
@ -1839,3 +1839,15 @@ const App = () => {
|
|||
);
|
||||
};
|
||||
```
|
||||
|
||||
Sometimes the `getBoundingClientRects` might be derived from a real element. In this case provide the anchor element as context to ensure clipping and position updates for the popup work well.
|
||||
|
||||
```ts
|
||||
const virtualElement = {
|
||||
getBoundingClientRect() {
|
||||
// ...
|
||||
return { width, height, x, y, top, left, right, bottom };
|
||||
},
|
||||
contextElement: anchorElement
|
||||
};
|
||||
```
|
||||
|
|
|
@ -75,6 +75,20 @@ const App = () => (
|
|||
);
|
||||
```
|
||||
|
||||
### Help Text
|
||||
|
||||
Add descriptive help text to a switch with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
|
||||
|
||||
```html:preview
|
||||
<sl-switch help-text="What should the user know about the switch?">Label</sl-switch>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import SlSwitch from '@shoelace-style/shoelace/dist/react/checkbox';
|
||||
|
||||
const App = () => <SlSwitch help-text="What should the user know about the switch?">Label</SlSwitch>;
|
||||
```
|
||||
|
||||
### Custom Styles
|
||||
|
||||
Use the available custom properties to change how the switch is styled.
|
||||
|
|
|
@ -5,26 +5,6 @@ meta:
|
|||
layout: component
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<sl-tab>Tab</sl-tab>
|
||||
<sl-tab active>Active</sl-tab>
|
||||
<sl-tab closable>Closable</sl-tab>
|
||||
<sl-tab disabled>Disabled</sl-tab>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import SlTab from '@shoelace-style/shoelace/dist/react/tab';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<SlTab>Tab</SlTab>
|
||||
<SlTab active>Active</SlTab>
|
||||
<SlTab closable>Closable</SlTab>
|
||||
<SlTab disabled>Disabled</SlTab>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
:::tip
|
||||
Additional demonstrations can be found in the [tab group examples](/components/tab-group).
|
||||
:::
|
||||
|
|
|
@ -23,6 +23,7 @@ npm install @shoelace-style/shoelace
|
|||
Next, [include a theme](/getting-started/themes) and set the [base path](/getting-started/installation#setting-the-base-path) for icons and other assets. In this example, we'll import the light theme and use the CDN as a base path.
|
||||
|
||||
```jsx
|
||||
// main.js or main.ts
|
||||
import '@shoelace-style/shoelace/dist/themes/light.css';
|
||||
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path';
|
||||
|
||||
|
|
|
@ -12,6 +12,53 @@ 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
|
||||
|
||||
- `<sl-tab>` `closable` property now reflects. [#2041]
|
||||
- `<sl-tab-group>` now implements a proper "roving tabindex" and `<sl-tab>` is no longer tabbable by default. This aligns closer to the APG pattern for tabs. [#2041]
|
||||
- Fixed a bug in the submenu controller that prevented submenus from rendering in RTL without explicitly setting `dir` on the parent menu item [#1992]
|
||||
|
||||
## 2.15.1
|
||||
|
||||
- Fixed a bug in `<sl-radio-group>` where if a click did not contain a `<sl-radio>` it would show a console error. [#2009]
|
||||
- Fixed a bug in `<sl-split-panel>` that caused it not to recalculate it's position when going from being `display: none;` to its original display value. [#1942]
|
||||
- Fixed a bug in `<dialog>` where when it showed it would cause a layout shift. [#1967]
|
||||
- Fixed a bug in `<sl-tooltip>` that allowed unwanted text properties to leak in [#1947]
|
||||
- Fixed a bug in `<sl-button-group>` classes [#1974]
|
||||
- Fixed a bug in `<sl-textarea>` that may throw errors on `disconnectedCallback` in test environments [#1985]
|
||||
- Fixed a bug in `<sl-color-picker>` that would log a non-passive event listener warning [#2005]
|
||||
- Fixed a bug in the submenu controller that allowed submenus to go offscreen and not be scrollable [#2001]
|
||||
- Fixed a bug in `<sl-range>` that caused the tooltip position to be incorrect in some cases [#1979]
|
||||
|
||||
## 2.15.0
|
||||
|
||||
- Added the Slovenian translation [#1893]
|
||||
- Added support for `contextElement` to `VirtualElements` in `<sl-popup>` [#1874]
|
||||
- Added the `spinner` and `spinner__base` parts to `<sl-tree-item>` [#1937]
|
||||
- Added the `sync` property to `<sl-dropdown>` so the menu can easily sync sizes with the trigger element [#1935]
|
||||
- Fixed a bug in `<sl-icon>` that did not properly apply mutators to spritesheets [#1927]
|
||||
- Fixed a bug in `.sl-scroll-lock` causing layout shifts [#1895]
|
||||
- Fixed a bug in `<sl-rating>` that caused the rating to not reset in some circumstances [#1877]
|
||||
- Fixed a bug in `<sl-select>` that caused the menu to not close when rendered in a shadow root [#1878]
|
||||
- Fixed a bug in `<sl-tree>` that caused a new stacking context resulting in tooltips being clipped [#1709]
|
||||
- Fixed a bug in `<sl-tab-group>` that caused the scroll controls to toggle indefinitely when zoomed in Safari [#1839]
|
||||
- Fixed a bug in the submenu controller that allowed two submenus to be open at the same time [#1880]
|
||||
- Fixed a bug in `<sl-select>` where the tag size wouldn't update with the control's size [#1886]
|
||||
- Fixed a bug in `<sl-checkbox>` and `<sl-switch>` where the color of the required content wasn't applying correctly
|
||||
- Fixed a bug in `<sl-checkbox>` where help text was incorrectly styled [#1897]
|
||||
- Fixed a bug in `<sl-input>` that prevented the control from receiving focus when clicking over the clear button
|
||||
- Fixed a bug in `<sl-carousel>` that caused the carousel to be out of sync when used with reduced motion settings [#1887]
|
||||
- Fixed a bug in `<sl-button-group>` that caused styles to stop working when using `className` on buttons in React [#1926]
|
||||
|
||||
## 2.14.0
|
||||
|
||||
- Added the Arabic translation [#1852]
|
||||
- Added help text to `<sl-checkbox>` [#1860]
|
||||
- Added help text to `<sl-switch>` [#1800]
|
||||
- Fixed a bug in `<sl-option>` that caused HTML tags to be included in `getTextLabel()`
|
||||
- Fixed a bug in `<sl-carousel>` that caused slides to not switch correctly [#1862]
|
||||
- Refactored component styles to be consumed more efficiently [#1692]
|
||||
|
||||
## 2.13.1
|
||||
|
||||
- Fixed a bug where the safe triangle was always visible when selecting nested `<sl-menu>` elements [#1835]
|
||||
|
@ -21,6 +68,7 @@ New versions of Shoelace are released as-needed and generally occur when a criti
|
|||
- Added the `hover-bridge` feature to `<sl-popup>` to support better tooltip accessibility [#1734]
|
||||
- Added the `loading` attribute and the `spinner` and `spinner__base` part to `<sl-menu-item>` [#1700]
|
||||
- Fixed files that did not have `.js` extensions. [#1770]
|
||||
- Fixed a bug in `<sl-tree>` when providing custom expand / collapse icons [#1922]
|
||||
- Fixed `<sl-dialog>` not accounting for elements with hidden dialog controls like `<video>` [#1755]
|
||||
- Fixed focus trapping not scrolling elements into view. [#1750]
|
||||
- Fixed more performance issues with focus trapping performance. [#1750]
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
{
|
||||
"name": "@shoelace-style/shoelace",
|
||||
"version": "2.13.1",
|
||||
"version": "2.15.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@shoelace-style/shoelace",
|
||||
"version": "2.13.1",
|
||||
"hasInstallScript": true,
|
||||
"version": "2.15.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^4.0.2",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@shoelace-style/shoelace",
|
||||
"description": "A forward-thinking library of web components.",
|
||||
"version": "2.13.1",
|
||||
"version": "2.15.1",
|
||||
"homepage": "https://github.com/shoelace-style/shoelace",
|
||||
"author": "Cory LaViska",
|
||||
"license": "MIT",
|
||||
|
@ -49,7 +49,7 @@
|
|||
"start": "node scripts/build.js --serve",
|
||||
"build": "node scripts/build.js",
|
||||
"verify": "npm run prettier:check && npm run lint && npm run build && npm run test",
|
||||
"postinstall": "npx playwright install",
|
||||
"prepare": "npx playwright install",
|
||||
"prepublishOnly": "npm run verify",
|
||||
"prettier": "prettier --write --log-level=warn .",
|
||||
"prettier:check": "prettier --check --log-level=warn .",
|
||||
|
|
|
@ -2,6 +2,7 @@ import { property } from 'lit/decorators.js';
|
|||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './{{ tagWithoutPrefix tag }}.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -24,7 +25,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @cssproperty --example - An example CSS custom property.
|
||||
*/
|
||||
export default class {{ properCase tag }} extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { LocalizeController } from '../../utilities/localize.js';
|
|||
import { property, query } from 'lit/decorators.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIconButton from '../icon-button/icon-button.component.js';
|
||||
import styles from './alert.styles.js';
|
||||
|
@ -40,7 +41,7 @@ const toastStack = Object.assign(document.createElement('div'), { className: 'sl
|
|||
* @animation alert.hide - The animation to use when hiding the alert.
|
||||
*/
|
||||
export default class SlAlert extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = { 'sl-icon-button': SlIconButton };
|
||||
|
||||
private autoHideTimeout: number;
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: contents;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { html } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './animated-image.styles.js';
|
||||
|
@ -20,13 +21,13 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @slot play-icon - Optional play icon to use instead of the default. Works best with `<sl-icon>`.
|
||||
* @slot pause-icon - Optional pause icon to use instead of the default. Works best with `<sl-icon>`.
|
||||
*
|
||||
* @part - control-box - The container that surrounds the pause/play icons and provides their background.
|
||||
* @part control-box - The container that surrounds the pause/play icons and provides their background.
|
||||
*
|
||||
* @cssproperty --control-box-size - The size of the icon box.
|
||||
* @cssproperty --icon-size - The size of the play/pause icons.
|
||||
*/
|
||||
export default class SlAnimatedImage extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = { 'sl-icon': SlIcon };
|
||||
|
||||
@query('.animated-image__animated') animatedImage: HTMLImageElement;
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--control-box-size: 3rem;
|
||||
--icon-size: calc(var(--control-box-size) * 0.625);
|
||||
|
|
|
@ -2,6 +2,7 @@ import { animations } from './animations.js';
|
|||
import { html } from 'lit';
|
||||
import { property, queryAsync } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './animation.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -20,7 +21,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* animate multiple elements, either wrap them in a single container or use multiple `<sl-animation>` elements.
|
||||
*/
|
||||
export default class SlAnimation extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
private animation?: Animation;
|
||||
private hasStarted = false;
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: contents;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { classMap } from 'lit/directives/class-map.js';
|
|||
import { html } from 'lit';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './avatar.styles.js';
|
||||
|
@ -25,7 +26,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @cssproperty --size - The size of the avatar.
|
||||
*/
|
||||
export default class SlAvatar extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = {
|
||||
'sl-icon': SlIcon
|
||||
};
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './badge.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -16,7 +17,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
export default class SlBadge extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
/** The badge's theme variant. */
|
||||
@property({ reflect: true }) variant: 'primary' | 'success' | 'neutral' | 'warning' | 'danger' = 'primary';
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { HasSlotController } from '../../internal/slot.js';
|
|||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './breadcrumb-item.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -26,7 +27,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @csspart separator - The container that wraps the separator.
|
||||
*/
|
||||
export default class SlBreadcrumbItem extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'prefix', 'suffix');
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './breadcrumb.styles.js';
|
||||
|
@ -21,7 +22,7 @@ import type SlBreadcrumbItem from '../breadcrumb-item/breadcrumb-item.js';
|
|||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
export default class SlBreadcrumb extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = { 'sl-icon': SlIcon };
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { html } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './button-group.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -15,7 +16,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
export default class SlButtonGroup extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
@query('slot') defaultSlot: HTMLSlotElement;
|
||||
|
||||
|
@ -29,22 +30,22 @@ export default class SlButtonGroup extends ShoelaceElement {
|
|||
|
||||
private handleFocus(event: Event) {
|
||||
const button = findButton(event.target as HTMLElement);
|
||||
button?.classList.add('sl-button-group__button--focus');
|
||||
button?.toggleAttribute('data-sl-button-group__button--focus', true);
|
||||
}
|
||||
|
||||
private handleBlur(event: Event) {
|
||||
const button = findButton(event.target as HTMLElement);
|
||||
button?.classList.remove('sl-button-group__button--focus');
|
||||
button?.toggleAttribute('data-sl-button-group__button--focus', false);
|
||||
}
|
||||
|
||||
private handleMouseOver(event: Event) {
|
||||
const button = findButton(event.target as HTMLElement);
|
||||
button?.classList.add('sl-button-group__button--hover');
|
||||
button?.toggleAttribute('data-sl-button-group__button--hover', true);
|
||||
}
|
||||
|
||||
private handleMouseOut(event: Event) {
|
||||
const button = findButton(event.target as HTMLElement);
|
||||
button?.classList.remove('sl-button-group__button--hover');
|
||||
button?.toggleAttribute('data-sl-button-group__button--hover', false);
|
||||
}
|
||||
|
||||
private handleSlotChange() {
|
||||
|
@ -55,11 +56,14 @@ export default class SlButtonGroup extends ShoelaceElement {
|
|||
const button = findButton(el);
|
||||
|
||||
if (button) {
|
||||
button.classList.add('sl-button-group__button');
|
||||
button.classList.toggle('sl-button-group__button--first', index === 0);
|
||||
button.classList.toggle('sl-button-group__button--inner', index > 0 && index < slottedElements.length - 1);
|
||||
button.classList.toggle('sl-button-group__button--last', index === slottedElements.length - 1);
|
||||
button.classList.toggle('sl-button-group__button--radio', button.tagName.toLowerCase() === 'sl-radio-button');
|
||||
button.toggleAttribute('data-sl-button-group__button', true);
|
||||
button.toggleAttribute('data-sl-button-group__button--first', index === 0);
|
||||
button.toggleAttribute('data-sl-button-group__button--inner', index > 0 && index < slottedElements.length - 1);
|
||||
button.toggleAttribute('data-sl-button-group__button--last', index === slottedElements.length - 1);
|
||||
button.toggleAttribute(
|
||||
'data-sl-button-group__button--radio',
|
||||
button.tagName.toLowerCase() === 'sl-radio-button'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ describe('<sl-button-group>', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('slotted button classes', () => {
|
||||
it('slotted buttons have the right classes applied based on their order', async () => {
|
||||
describe('slotted button data attributes', () => {
|
||||
it('slotted buttons have the right data attributes applied based on their order', async () => {
|
||||
const group = await fixture<SlButtonGroup>(html`
|
||||
<sl-button-group>
|
||||
<sl-button>Button 1 Label</sl-button>
|
||||
|
@ -38,19 +38,19 @@ describe('<sl-button-group>', () => {
|
|||
`);
|
||||
|
||||
const allButtons = group.querySelectorAll('sl-button');
|
||||
const hasGroupClass = Array.from(allButtons).every(button =>
|
||||
button.classList.contains('sl-button-group__button')
|
||||
const hasGroupAttrib = Array.from(allButtons).every(button =>
|
||||
button.hasAttribute('data-sl-button-group__button')
|
||||
);
|
||||
expect(hasGroupClass).to.be.true;
|
||||
expect(hasGroupAttrib).to.be.true;
|
||||
|
||||
expect(allButtons[0]).to.have.class('sl-button-group__button--first');
|
||||
expect(allButtons[1]).to.have.class('sl-button-group__button--inner');
|
||||
expect(allButtons[2]).to.have.class('sl-button-group__button--last');
|
||||
expect(allButtons[0]).to.have.attribute('data-sl-button-group__button--first');
|
||||
expect(allButtons[1]).to.have.attribute('data-sl-button-group__button--inner');
|
||||
expect(allButtons[2]).to.have.attribute('data-sl-button-group__button--last');
|
||||
});
|
||||
});
|
||||
|
||||
describe('focus and blur events', () => {
|
||||
it('toggles focus class to slotted buttons on focus/blur', async () => {
|
||||
it('toggles focus data attribute to slotted buttons on focus/blur', async () => {
|
||||
const group = await fixture<SlButtonGroup>(html`
|
||||
<sl-button-group>
|
||||
<sl-button>Button 1 Label</sl-button>
|
||||
|
@ -63,16 +63,16 @@ describe('<sl-button-group>', () => {
|
|||
allButtons[0].dispatchEvent(new FocusEvent('focusin', { bubbles: true }));
|
||||
|
||||
await elementUpdated(allButtons[0]);
|
||||
expect(allButtons[0].classList.contains('sl-button-group__button--focus')).to.be.true;
|
||||
expect(allButtons[0]).to.have.attribute('data-sl-button-group__button--focus');
|
||||
|
||||
allButtons[0].dispatchEvent(new FocusEvent('focusout', { bubbles: true }));
|
||||
await elementUpdated(allButtons[0]);
|
||||
expect(allButtons[0].classList.contains('sl-button-group__button--focus')).not.to.be.true;
|
||||
expect(allButtons[0]).to.not.have.attribute('data-sl-button-group__button--focus');
|
||||
});
|
||||
});
|
||||
|
||||
describe('mouseover and mouseout events', () => {
|
||||
it('toggles hover class to slotted buttons on mouseover/mouseout', async () => {
|
||||
it('toggles hover data attribute to slotted buttons on mouseover/mouseout', async () => {
|
||||
const group = await fixture<SlButtonGroup>(html`
|
||||
<sl-button-group>
|
||||
<sl-button>Button 1 Label</sl-button>
|
||||
|
@ -85,11 +85,12 @@ describe('<sl-button-group>', () => {
|
|||
|
||||
allButtons[0].dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
|
||||
await elementUpdated(allButtons[0]);
|
||||
expect(allButtons[0].classList.contains('sl-button-group__button--hover')).to.be.true;
|
||||
expect(allButtons[0]).to.have.attribute('data-sl-button-group__button--hover');
|
||||
|
||||
allButtons[0].dispatchEvent(new MouseEvent('mouseout', { bubbles: true }));
|
||||
await elementUpdated(allButtons[0]);
|
||||
expect(allButtons[0].classList.contains('sl-button-group__button--hover')).not.to.be.true;
|
||||
console.log(allButtons[0]);
|
||||
expect(allButtons[0]).to.not.have.attribute('data-sl-button-group__button--hover');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@ import { ifDefined } from 'lit/directives/if-defined.js';
|
|||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import SlSpinner from '../spinner/spinner.component.js';
|
||||
|
@ -38,7 +39,7 @@ import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
|
|||
* @csspart spinner - The spinner that shows when the button is in the loading state.
|
||||
*/
|
||||
export default class SlButton extends ShoelaceElement implements ShoelaceFormControl {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = {
|
||||
'sl-icon': SlIcon,
|
||||
'sl-spinner': SlSpinner
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
@ -549,30 +546,30 @@ export default css`
|
|||
* buttons and we style them here instead.
|
||||
*/
|
||||
|
||||
:host(.sl-button-group__button--first:not(.sl-button-group__button--last)) .button {
|
||||
:host([data-sl-button-group__button--first]:not([data-sl-button-group__button--last])) .button {
|
||||
border-start-end-radius: 0;
|
||||
border-end-end-radius: 0;
|
||||
}
|
||||
|
||||
:host(.sl-button-group__button--inner) .button {
|
||||
:host([data-sl-button-group__button--inner]) .button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
:host(.sl-button-group__button--last:not(.sl-button-group__button--first)) .button {
|
||||
:host([data-sl-button-group__button--last]:not([data-sl-button-group__button--first])) .button {
|
||||
border-start-start-radius: 0;
|
||||
border-end-start-radius: 0;
|
||||
}
|
||||
|
||||
/* All except the first */
|
||||
:host(.sl-button-group__button:not(.sl-button-group__button--first)) {
|
||||
:host([data-sl-button-group__button]:not([data-sl-button-group__button--first])) {
|
||||
margin-inline-start: calc(-1 * var(--sl-input-border-width));
|
||||
}
|
||||
|
||||
/* Add a visual separator between solid buttons */
|
||||
:host(
|
||||
.sl-button-group__button:not(
|
||||
.sl-button-group__button--first,
|
||||
.sl-button-group__button--radio,
|
||||
[data-sl-button-group__button]:not(
|
||||
[data-sl-button-group__button--first],
|
||||
[data-sl-button-group__button--radio],
|
||||
[variant='default']
|
||||
):not(:hover)
|
||||
)
|
||||
|
@ -587,13 +584,13 @@ export default css`
|
|||
}
|
||||
|
||||
/* Bump hovered, focused, and checked buttons up so their focus ring isn't clipped */
|
||||
:host(.sl-button-group__button--hover) {
|
||||
:host([data-sl-button-group__button--hover]) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Focus and checked are always on top */
|
||||
:host(.sl-button-group__button--focus),
|
||||
:host(.sl-button-group__button[checked]) {
|
||||
:host([data-sl-button-group__button--focus]),
|
||||
:host([data-sl-button-group__button][checked]) {
|
||||
z-index: 2;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './card.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -28,7 +29,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @cssproperty --padding - The padding to use for the card's sections.
|
||||
*/
|
||||
export default class SlCard extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer', 'header', 'image');
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--border-color: var(--sl-color-neutral-200);
|
||||
--border-radius: var(--sl-border-radius-medium);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { html } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './carousel-item.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -15,7 +16,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
*
|
||||
*/
|
||||
export default class SlCarouselItem extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--aspect-ratio: inherit;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import { prefersReducedMotion } from '../../internal/animate.js';
|
|||
import { range } from 'lit/directives/range.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './carousel.styles.js';
|
||||
|
@ -47,7 +48,7 @@ import type SlCarouselItem from '../carousel-item/carousel-item.component.js';
|
|||
* partially visible as a scroll hint.
|
||||
*/
|
||||
export default class SlCarousel extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = { 'sl-icon': SlIcon };
|
||||
|
||||
/** When set, allows the user to navigate the carousel in the same direction indefinitely. */
|
||||
|
@ -91,9 +92,6 @@ export default class SlCarousel extends ShoelaceElement {
|
|||
@state() dragging = false;
|
||||
|
||||
private autoplayController = new AutoplayController(this, () => this.next());
|
||||
private intersectionObserver: IntersectionObserver; // determines which slide is displayed
|
||||
// A map containing the state of all the slides
|
||||
private readonly intersectionObserverEntries = new Map<Element, IntersectionObserverEntry>();
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private mutationObserver: MutationObserver;
|
||||
|
||||
|
@ -101,35 +99,10 @@ export default class SlCarousel extends ShoelaceElement {
|
|||
super.connectedCallback();
|
||||
this.setAttribute('role', 'region');
|
||||
this.setAttribute('aria-label', this.localize.term('carousel'));
|
||||
|
||||
const intersectionObserver = new IntersectionObserver(
|
||||
(entries: IntersectionObserverEntry[]) => {
|
||||
entries.forEach(entry => {
|
||||
// Store all the entries in a map to be processed when scrolling ends
|
||||
this.intersectionObserverEntries.set(entry.target, entry);
|
||||
|
||||
const slide = entry.target;
|
||||
slide.toggleAttribute('inert', !entry.isIntersecting);
|
||||
slide.classList.toggle('--in-view', entry.isIntersecting);
|
||||
slide.setAttribute('aria-hidden', entry.isIntersecting ? 'false' : 'true');
|
||||
});
|
||||
},
|
||||
{
|
||||
root: this,
|
||||
threshold: 0.6
|
||||
}
|
||||
);
|
||||
this.intersectionObserver = intersectionObserver;
|
||||
|
||||
// Store the initial state of each slide
|
||||
intersectionObserver.takeRecords().forEach(entry => {
|
||||
this.intersectionObserverEntries.set(entry.target, entry);
|
||||
});
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this.intersectionObserver.disconnect();
|
||||
this.mutationObserver.disconnect();
|
||||
}
|
||||
|
||||
|
@ -180,7 +153,7 @@ export default class SlCarousel extends ShoelaceElement {
|
|||
private handleKeyDown(event: KeyboardEvent) {
|
||||
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) {
|
||||
const target = event.target as HTMLElement;
|
||||
const isRtl = this.localize.dir() === 'rtl';
|
||||
const isRtl = this.matches(':dir(rtl)');
|
||||
const isFocusInPagination = target.closest('[part~="pagination-item"]') !== null;
|
||||
const isNext =
|
||||
event.key === 'ArrowDown' || (!isRtl && event.key === 'ArrowRight') || (isRtl && event.key === 'ArrowLeft');
|
||||
|
@ -290,19 +263,28 @@ export default class SlCarousel extends ShoelaceElement {
|
|||
this.scrolling = true;
|
||||
}
|
||||
|
||||
private handleScrollEnd() {
|
||||
if (!this.scrolling || this.dragging) return;
|
||||
/** @internal Synchronizes the slides with the IntersectionObserver API. */
|
||||
private synchronizeSlides() {
|
||||
const io = new IntersectionObserver(
|
||||
entries => {
|
||||
io.disconnect();
|
||||
|
||||
const entries = [...this.intersectionObserverEntries.values()];
|
||||
for (const entry of entries) {
|
||||
const slide = entry.target;
|
||||
slide.toggleAttribute('inert', !entry.isIntersecting);
|
||||
slide.classList.toggle('--in-view', entry.isIntersecting);
|
||||
slide.setAttribute('aria-hidden', entry.isIntersecting ? 'false' : 'true');
|
||||
}
|
||||
|
||||
const firstIntersecting: IntersectionObserverEntry | undefined = entries.find(entry => entry.isIntersecting);
|
||||
const firstIntersecting = entries.find(entry => entry.isIntersecting);
|
||||
|
||||
if (this.loop && firstIntersecting?.target.hasAttribute('data-clone')) {
|
||||
if (firstIntersecting) {
|
||||
if (this.loop && firstIntersecting.target.hasAttribute('data-clone')) {
|
||||
const clonePosition = Number(firstIntersecting.target.getAttribute('data-clone'));
|
||||
|
||||
// Scrolls to the original slide without animating, so the user won't notice that the position has changed
|
||||
this.goToSlide(clonePosition, 'instant');
|
||||
} else if (firstIntersecting) {
|
||||
} else {
|
||||
const slides = this.getSlides();
|
||||
|
||||
// Update the current index based on the first visible slide
|
||||
|
@ -310,6 +292,23 @@ export default class SlCarousel extends ShoelaceElement {
|
|||
// Set the index to the first "snappable" slide
|
||||
this.activeSlide = Math.ceil(slideIndex / this.slidesPerMove) * this.slidesPerMove;
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
root: this.scrollContainer,
|
||||
threshold: 0.6
|
||||
}
|
||||
);
|
||||
|
||||
this.getSlides({ excludeClones: false }).forEach(slide => {
|
||||
io.observe(slide);
|
||||
});
|
||||
}
|
||||
|
||||
private handleScrollEnd() {
|
||||
if (!this.scrolling || this.dragging) return;
|
||||
|
||||
this.synchronizeSlides();
|
||||
|
||||
this.scrolling = false;
|
||||
}
|
||||
|
@ -336,14 +335,8 @@ export default class SlCarousel extends ShoelaceElement {
|
|||
@watch('loop', { waitUntilFirstUpdate: true })
|
||||
@watch('slidesPerPage', { waitUntilFirstUpdate: true })
|
||||
initializeSlides() {
|
||||
const intersectionObserver = this.intersectionObserver;
|
||||
|
||||
this.intersectionObserverEntries.clear();
|
||||
|
||||
// Removes all the cloned elements from the carousel
|
||||
this.getSlides({ excludeClones: false }).forEach((slide, index) => {
|
||||
intersectionObserver.unobserve(slide);
|
||||
|
||||
slide.classList.remove('--in-view');
|
||||
slide.classList.remove('--is-active');
|
||||
slide.setAttribute('aria-label', this.localize.term('slideNum', index + 1));
|
||||
|
@ -360,9 +353,7 @@ export default class SlCarousel extends ShoelaceElement {
|
|||
this.createClones();
|
||||
}
|
||||
|
||||
this.getSlides({ excludeClones: false }).forEach(slide => {
|
||||
intersectionObserver.observe(slide);
|
||||
});
|
||||
this.synchronizeSlides();
|
||||
|
||||
// Because the DOM may be changed, restore the scroll position to the active slide
|
||||
this.goToSlide(this.activeSlide, 'auto');
|
||||
|
@ -476,7 +467,7 @@ export default class SlCarousel extends ShoelaceElement {
|
|||
this.scrollToSlide(nextSlide, prefersReducedMotion() ? 'auto' : behavior);
|
||||
}
|
||||
|
||||
private async scrollToSlide(slide: HTMLElement, behavior: ScrollBehavior = 'smooth') {
|
||||
private scrollToSlide(slide: HTMLElement, behavior: ScrollBehavior = 'smooth') {
|
||||
const scrollContainer = this.scrollContainer;
|
||||
const scrollContainerRect = scrollContainer.getBoundingClientRect();
|
||||
const nextSlideRect = slide.getBoundingClientRect();
|
||||
|
@ -484,16 +475,11 @@ export default class SlCarousel extends ShoelaceElement {
|
|||
const nextLeft = nextSlideRect.left - scrollContainerRect.left;
|
||||
const nextTop = nextSlideRect.top - scrollContainerRect.top;
|
||||
|
||||
// If the slide is already in view, don't need to scroll
|
||||
if (nextLeft !== scrollContainer.scrollLeft || nextTop !== scrollContainer.scrollTop) {
|
||||
scrollContainer.scrollTo({
|
||||
left: nextLeft + scrollContainer.scrollLeft,
|
||||
top: nextTop + scrollContainer.scrollTop,
|
||||
behavior
|
||||
});
|
||||
|
||||
await waitForEvent(scrollContainer, 'scrollend');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -502,7 +488,7 @@ export default class SlCarousel extends ShoelaceElement {
|
|||
const currentPage = this.getCurrentPage();
|
||||
const prevEnabled = this.canScrollPrev();
|
||||
const nextEnabled = this.canScrollNext();
|
||||
const isLtr = this.localize.dir() === 'ltr';
|
||||
const isLtr = this.matches(':dir(ltr)');
|
||||
|
||||
return html`
|
||||
<div part="base" class="carousel">
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--slide-gap: var(--sl-spacing-medium, 1rem);
|
||||
--aspect-ratio: 16 / 9;
|
||||
|
|
|
@ -1,15 +1,39 @@
|
|||
import '../../../dist/shoelace.js';
|
||||
import { aTimeout, expect, fixture, html, nextFrame, oneEvent, waitUntil } from '@open-wc/testing';
|
||||
import { clickOnElement, dragElement, moveMouseOnElement } from '../../internal/test.js';
|
||||
import { expect, fixture, html, oneEvent } from '@open-wc/testing';
|
||||
import { map } from 'lit/directives/map.js';
|
||||
import { range } from 'lit/directives/range.js';
|
||||
import { resetMouse } from '@web/test-runner-commands';
|
||||
import sinon from 'sinon';
|
||||
import type { SinonStub } from 'sinon';
|
||||
import type SlCarousel from './carousel.js';
|
||||
|
||||
describe('<sl-carousel>', () => {
|
||||
const sandbox = sinon.createSandbox();
|
||||
const ioCallbacks = new Map<IntersectionObserver, SinonStub>();
|
||||
const intersectionObserverCallbacks = () => {
|
||||
const callbacks = [...ioCallbacks.values()];
|
||||
return waitUntil(() => callbacks.every(callback => callback.called));
|
||||
};
|
||||
const OriginalIntersectionObserver = globalThis.IntersectionObserver;
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IntersectionObserver = class IntersectionObserverMock extends OriginalIntersectionObserver {
|
||||
constructor(callback: IntersectionObserverCallback, options?: IntersectionObserverInit) {
|
||||
const stubCallback = sandbox.stub().callsFake(callback);
|
||||
|
||||
super(stubCallback, options);
|
||||
|
||||
ioCallbacks.set(this, stubCallback);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await resetMouse();
|
||||
sandbox.restore();
|
||||
globalThis.IntersectionObserver = OriginalIntersectionObserver;
|
||||
ioCallbacks.clear();
|
||||
});
|
||||
|
||||
it('should render a carousel with default configuration', async () => {
|
||||
|
@ -34,15 +58,11 @@ describe('<sl-carousel>', () => {
|
|||
let clock: sinon.SinonFakeTimers;
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers({
|
||||
clock = sandbox.useFakeTimers({
|
||||
now: new Date()
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('should scroll forwards every `autoplay-interval` milliseconds', async () => {
|
||||
// Arrange
|
||||
const el = await fixture<SlCarousel>(html`
|
||||
|
@ -52,7 +72,7 @@ describe('<sl-carousel>', () => {
|
|||
<sl-carousel-item>Node 3</sl-carousel-item>
|
||||
</sl-carousel>
|
||||
`);
|
||||
sinon.stub(el, 'next');
|
||||
sandbox.stub(el, 'next');
|
||||
|
||||
await el.updateComplete;
|
||||
|
||||
|
@ -73,7 +93,7 @@ describe('<sl-carousel>', () => {
|
|||
<sl-carousel-item>Node 3</sl-carousel-item>
|
||||
</sl-carousel>
|
||||
`);
|
||||
sinon.stub(el, 'next');
|
||||
sandbox.stub(el, 'next');
|
||||
|
||||
await el.updateComplete;
|
||||
|
||||
|
@ -96,7 +116,7 @@ describe('<sl-carousel>', () => {
|
|||
<sl-carousel-item>Node 3</sl-carousel-item>
|
||||
</sl-carousel>
|
||||
`);
|
||||
sinon.stub(el, 'next');
|
||||
sandbox.stub(el, 'next');
|
||||
|
||||
await el.updateComplete;
|
||||
|
||||
|
@ -183,7 +203,7 @@ describe('<sl-carousel>', () => {
|
|||
<sl-carousel-item>Node 3</sl-carousel-item>
|
||||
</sl-carousel>
|
||||
`);
|
||||
sinon.stub(el, 'goToSlide');
|
||||
sandbox.stub(el, 'goToSlide');
|
||||
await el.updateComplete;
|
||||
|
||||
// Act
|
||||
|
@ -309,6 +329,7 @@ describe('<sl-carousel>', () => {
|
|||
await clickOnElement(nextButton);
|
||||
|
||||
await oneEvent(el.scrollContainer, 'scrollend');
|
||||
await intersectionObserverCallbacks();
|
||||
await el.updateComplete;
|
||||
|
||||
// Assert
|
||||
|
@ -335,13 +356,19 @@ describe('<sl-carousel>', () => {
|
|||
|
||||
// Act
|
||||
await clickOnElement(nextButton);
|
||||
await aTimeout(50);
|
||||
await clickOnElement(nextButton);
|
||||
await aTimeout(50);
|
||||
await clickOnElement(nextButton);
|
||||
await aTimeout(50);
|
||||
await clickOnElement(nextButton);
|
||||
await aTimeout(50);
|
||||
await clickOnElement(nextButton);
|
||||
await aTimeout(50);
|
||||
await clickOnElement(nextButton);
|
||||
|
||||
await oneEvent(el.scrollContainer, 'scrollend');
|
||||
await intersectionObserverCallbacks();
|
||||
await el.updateComplete;
|
||||
|
||||
// Assert
|
||||
|
@ -473,7 +500,7 @@ describe('<sl-carousel>', () => {
|
|||
</sl-carousel>
|
||||
`);
|
||||
const nextButton: HTMLElement = el.shadowRoot!.querySelector('.carousel__navigation-button--next')!;
|
||||
sinon.stub(el, 'next');
|
||||
sandbox.stub(el, 'next');
|
||||
|
||||
await el.updateComplete;
|
||||
|
||||
|
@ -496,10 +523,11 @@ describe('<sl-carousel>', () => {
|
|||
</sl-carousel>
|
||||
`);
|
||||
const nextButton: HTMLElement = el.shadowRoot!.querySelector('.carousel__navigation-button--next')!;
|
||||
sinon.stub(el, 'next');
|
||||
sandbox.stub(el, 'next');
|
||||
|
||||
el.goToSlide(2, 'auto');
|
||||
await oneEvent(el.scrollContainer, 'scrollend');
|
||||
await intersectionObserverCallbacks();
|
||||
await el.updateComplete;
|
||||
|
||||
// Act
|
||||
|
@ -535,6 +563,9 @@ describe('<sl-carousel>', () => {
|
|||
// wait scroll to actual item
|
||||
await oneEvent(el.scrollContainer, 'scrollend');
|
||||
|
||||
await intersectionObserverCallbacks();
|
||||
await el.updateComplete;
|
||||
|
||||
// Assert
|
||||
expect(nextButton).to.have.attribute('aria-disabled', 'false');
|
||||
expect(el.activeSlide).to.be.equal(0);
|
||||
|
@ -560,7 +591,7 @@ describe('<sl-carousel>', () => {
|
|||
await el.updateComplete;
|
||||
|
||||
const previousButton: HTMLElement = el.shadowRoot!.querySelector('.carousel__navigation-button--previous')!;
|
||||
sinon.stub(el, 'previous');
|
||||
sandbox.stub(el, 'previous');
|
||||
|
||||
await el.updateComplete;
|
||||
|
||||
|
@ -584,7 +615,7 @@ describe('<sl-carousel>', () => {
|
|||
`);
|
||||
|
||||
const previousButton: HTMLElement = el.shadowRoot!.querySelector('.carousel__navigation-button--previous')!;
|
||||
sinon.stub(el, 'previous');
|
||||
sandbox.stub(el, 'previous');
|
||||
await el.updateComplete;
|
||||
|
||||
// Act
|
||||
|
@ -618,6 +649,8 @@ describe('<sl-carousel>', () => {
|
|||
// wait scroll to actual item
|
||||
await oneEvent(el.scrollContainer, 'scrollend');
|
||||
|
||||
await intersectionObserverCallbacks();
|
||||
|
||||
// Assert
|
||||
expect(previousButton).to.have.attribute('aria-disabled', 'false');
|
||||
expect(el.activeSlide).to.be.equal(2);
|
||||
|
@ -632,19 +665,27 @@ describe('<sl-carousel>', () => {
|
|||
it('should scroll the carousel to the next slide', async () => {
|
||||
// Arrange
|
||||
const el = await fixture<SlCarousel>(html`
|
||||
<sl-carousel slides-per-page="2" slides-per-move="2">
|
||||
<sl-carousel>
|
||||
<sl-carousel-item>Node 1</sl-carousel-item>
|
||||
<sl-carousel-item>Node 2</sl-carousel-item>
|
||||
<sl-carousel-item>Node 3</sl-carousel-item>
|
||||
</sl-carousel>
|
||||
`);
|
||||
sinon.stub(el, 'goToSlide');
|
||||
await el.updateComplete;
|
||||
sandbox.spy(el, 'goToSlide');
|
||||
const expectedCarouselItem: HTMLElement = el.querySelector('sl-carousel-item:nth-child(2)')!;
|
||||
|
||||
// Act
|
||||
el.next();
|
||||
await oneEvent(el.scrollContainer, 'scrollend');
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.goToSlide).to.have.been.calledWith(2);
|
||||
const containerRect = el.scrollContainer.getBoundingClientRect();
|
||||
const itemRect = expectedCarouselItem.getBoundingClientRect();
|
||||
|
||||
// Assert
|
||||
expect(el.goToSlide).to.have.been.calledWith(1);
|
||||
expect(itemRect.top).to.be.equal(containerRect.top);
|
||||
expect(itemRect.left).to.be.equal(containerRect.left);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -652,19 +693,34 @@ describe('<sl-carousel>', () => {
|
|||
it('should scroll the carousel to the previous slide', async () => {
|
||||
// Arrange
|
||||
const el = await fixture<SlCarousel>(html`
|
||||
<sl-carousel slides-per-page="2" slides-per-move="2">
|
||||
<sl-carousel>
|
||||
<sl-carousel-item>Node 1</sl-carousel-item>
|
||||
<sl-carousel-item>Node 2</sl-carousel-item>
|
||||
<sl-carousel-item>Node 3</sl-carousel-item>
|
||||
</sl-carousel>
|
||||
`);
|
||||
sinon.stub(el, 'goToSlide');
|
||||
await el.updateComplete;
|
||||
const expectedCarouselItem: HTMLElement = el.querySelector('sl-carousel-item:nth-child(1)')!;
|
||||
|
||||
el.goToSlide(1);
|
||||
|
||||
await oneEvent(el.scrollContainer, 'scrollend');
|
||||
await intersectionObserverCallbacks();
|
||||
await nextFrame();
|
||||
|
||||
sandbox.spy(el, 'goToSlide');
|
||||
|
||||
// Act
|
||||
el.previous();
|
||||
await oneEvent(el.scrollContainer, 'scrollend');
|
||||
await intersectionObserverCallbacks();
|
||||
|
||||
expect(el.goToSlide).to.have.been.calledWith(-2);
|
||||
const containerRect = el.scrollContainer.getBoundingClientRect();
|
||||
const itemRect = expectedCarouselItem.getBoundingClientRect();
|
||||
|
||||
// Assert
|
||||
expect(el.goToSlide).to.have.been.calledWith(0);
|
||||
expect(itemRect.top).to.be.equal(containerRect.top);
|
||||
expect(itemRect.left).to.be.equal(containerRect.left);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -683,6 +739,7 @@ describe('<sl-carousel>', () => {
|
|||
// Act
|
||||
el.goToSlide(2);
|
||||
await oneEvent(el.scrollContainer, 'scrollend');
|
||||
await intersectionObserverCallbacks();
|
||||
await el.updateComplete;
|
||||
|
||||
// Assert
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { defaultValue } from '../../internal/default-value.js';
|
||||
import { FormControlController } from '../../internal/form.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { live } from 'lit/directives/live.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import formControlStyles from '../../styles/form-control.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './checkbox.styles.js';
|
||||
|
@ -21,6 +24,7 @@ import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
|
|||
* @dependency sl-icon
|
||||
*
|
||||
* @slot - The checkbox's label.
|
||||
* @slot help-text - Text that describes how to use the checkbox. Alternatively, you can use the `help-text` attribute.
|
||||
*
|
||||
* @event sl-blur - Emitted when the checkbox loses focus.
|
||||
* @event sl-change - Emitted when the checked state changes.
|
||||
|
@ -35,9 +39,10 @@ import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
|
|||
* @csspart checked-icon - The checked icon, an `<sl-icon>` element.
|
||||
* @csspart indeterminate-icon - The indeterminate icon, an `<sl-icon>` element.
|
||||
* @csspart label - The container that wraps the checkbox's label.
|
||||
* @csspart form-control-help-text - The help text's wrapper.
|
||||
*/
|
||||
export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormControl {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, formControlStyles, styles];
|
||||
static dependencies = { 'sl-icon': SlIcon };
|
||||
|
||||
private readonly formControlController = new FormControlController(this, {
|
||||
|
@ -45,6 +50,7 @@ export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormC
|
|||
defaultValue: (control: SlCheckbox) => control.defaultChecked,
|
||||
setValue: (control: SlCheckbox, checked: boolean) => (control.checked = checked)
|
||||
});
|
||||
private readonly hasSlotController = new HasSlotController(this, 'help-text');
|
||||
|
||||
@query('input[type="checkbox"]') input: HTMLInputElement;
|
||||
|
||||
|
@ -86,6 +92,9 @@ export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormC
|
|||
/** Makes the checkbox a required field. */
|
||||
@property({ type: Boolean, reflect: true }) required = false;
|
||||
|
||||
/** The checkbox's help text. If you need to display HTML, use the `help-text` slot instead. */
|
||||
@property({ attribute: 'help-text' }) helpText = '';
|
||||
|
||||
/** Gets the validity state object */
|
||||
get validity() {
|
||||
return this.input.validity;
|
||||
|
@ -178,12 +187,24 @@ export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormC
|
|||
}
|
||||
|
||||
render() {
|
||||
const hasHelpTextSlot = this.hasSlotController.test('help-text');
|
||||
const hasHelpText = this.helpText ? true : !!hasHelpTextSlot;
|
||||
|
||||
//
|
||||
// NOTE: we use a <div> around the label slot because of this Chrome bug.
|
||||
//
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1413733
|
||||
//
|
||||
return html`
|
||||
<div
|
||||
class=${classMap({
|
||||
'form-control': true,
|
||||
'form-control--small': this.size === 'small',
|
||||
'form-control--medium': this.size === 'medium',
|
||||
'form-control--large': this.size === 'large',
|
||||
'form-control--has-help-text': hasHelpText
|
||||
})}
|
||||
>
|
||||
<label
|
||||
part="base"
|
||||
class=${classMap({
|
||||
|
@ -208,6 +229,7 @@ export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormC
|
|||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
aria-checked=${this.checked ? 'true' : 'false'}
|
||||
aria-describedby="help-text"
|
||||
@click=${this.handleClick}
|
||||
@input=${this.handleInput}
|
||||
@invalid=${this.handleInvalid}
|
||||
|
@ -216,7 +238,9 @@ export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormC
|
|||
/>
|
||||
|
||||
<span
|
||||
part="control${this.checked ? ' control--checked' : ''}${this.indeterminate ? ' control--indeterminate' : ''}"
|
||||
part="control${this.checked ? ' control--checked' : ''}${this.indeterminate
|
||||
? ' control--indeterminate'
|
||||
: ''}"
|
||||
class="checkbox__control"
|
||||
>
|
||||
${this.checked
|
||||
|
@ -240,6 +264,16 @@ export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormC
|
|||
<slot></slot>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div
|
||||
aria-hidden=${hasHelpText ? 'false' : 'true'}
|
||||
class="form-control__help-text"
|
||||
id="help-text"
|
||||
part="form-control-help-text"
|
||||
>
|
||||
<slot name="help-text">${this.helpText}</slot>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
|
@ -118,6 +115,7 @@ export default css`
|
|||
|
||||
:host([required]) .checkbox__label::after {
|
||||
content: var(--sl-input-required-content);
|
||||
color: var(--sl-input-required-content-color);
|
||||
margin-inline-start: var(--sl-input-required-content-offset);
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -23,6 +23,7 @@ describe('<sl-checkbox>', () => {
|
|||
expect(el.checked).to.be.false;
|
||||
expect(el.indeterminate).to.be.false;
|
||||
expect(el.defaultChecked).to.be.false;
|
||||
expect(el.helpText).to.equal('');
|
||||
});
|
||||
|
||||
it('should have title if title attribute is set', async () => {
|
||||
|
|
|
@ -2,14 +2,15 @@ import { clamp } from '../../internal/math.js';
|
|||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { defaultValue } from '../../internal/default-value.js';
|
||||
import { drag } from '../../internal/drag.js';
|
||||
import { eventOptions, property, query, state } from 'lit/decorators.js';
|
||||
import { FormControlController } from '../../internal/form.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlButton from '../button/button.component.js';
|
||||
import SlButtonGroup from '../button-group/button-group.component.js';
|
||||
|
@ -90,7 +91,7 @@ declare const EyeDropper: EyeDropperConstructor;
|
|||
* @cssproperty --swatch-size - The size of each predefined color swatch.
|
||||
*/
|
||||
export default class SlColorPicker extends ShoelaceElement implements ShoelaceFormControl {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
static dependencies = {
|
||||
'sl-button-group': SlButtonGroup,
|
||||
|
@ -487,6 +488,7 @@ export default class SlColorPicker extends ShoelaceElement implements ShoelaceFo
|
|||
this.formControlController.emitInvalidEvent(event);
|
||||
}
|
||||
|
||||
@eventOptions({ passive: false })
|
||||
private handleTouchMove(event: TouchEvent) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--grid-width: 280px;
|
||||
--grid-height: 200px;
|
||||
|
|
|
@ -3,6 +3,7 @@ import { getAnimation, setDefaultAnimation } from '../../utilities/animation-reg
|
|||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import SlTooltip from '../tooltip/tooltip.component.js';
|
||||
|
@ -41,7 +42,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @animation copy.out - The animation to use when feedback icons animate out.
|
||||
*/
|
||||
export default class SlCopyButton extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = {
|
||||
'sl-icon': SlIcon,
|
||||
'sl-tooltip': SlTooltip
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--error-color: var(--sl-color-danger-600);
|
||||
--success-color: var(--sl-color-success-600);
|
||||
|
|
|
@ -6,6 +6,7 @@ import { LocalizeController } from '../../utilities/localize.js';
|
|||
import { property, query } from 'lit/decorators.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './details.styles.js';
|
||||
|
@ -39,7 +40,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @animation details.hide - The animation to use when hiding details. You can use `height: auto` with this animation.
|
||||
*/
|
||||
export default class SlDetails extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
static dependencies = {
|
||||
'sl-icon': SlIcon
|
||||
|
@ -186,7 +187,7 @@ export default class SlDetails extends ShoelaceElement {
|
|||
}
|
||||
|
||||
render() {
|
||||
const isRtl = this.localize.dir() === 'rtl';
|
||||
const isRtl = this.matches(':dir(rtl)');
|
||||
|
||||
return html`
|
||||
<details
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll.js
|
|||
import { property, query } from 'lit/decorators.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import Modal from '../../internal/modal.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIconButton from '../icon-button/icon-button.component.js';
|
||||
|
@ -66,7 +67,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* the third-party modal opens. Upon closing, call `modal.deactivateExternal()` to restore Shoelace's focus trapping.
|
||||
*/
|
||||
export default class SlDialog extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = {
|
||||
'sl-icon-button': SlIconButton
|
||||
};
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--width: 31rem;
|
||||
--header-spacing: var(--sl-spacing-large);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { property } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './divider.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -15,7 +16,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @cssproperty --spacing - The spacing of the divider.
|
||||
*/
|
||||
export default class SlDivider extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
/** Draws the divider in a vertical orientation. */
|
||||
@property({ type: Boolean, reflect: true }) vertical = false;
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--color: var(--sl-panel-border-color);
|
||||
--width: var(--sl-panel-border-width);
|
||||
|
|
|
@ -10,6 +10,7 @@ import { property, query } from 'lit/decorators.js';
|
|||
import { uppercaseFirstLetter } from '../../internal/string.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import Modal from '../../internal/modal.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIconButton from '../icon-button/icon-button.component.js';
|
||||
|
@ -74,7 +75,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* the third-party modal opens. Upon closing, call `modal.deactivateExternal()` to restore Shoelace's focus trapping.
|
||||
*/
|
||||
export default class SlDrawer extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = { 'sl-icon-button': SlIconButton };
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer');
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--size: 25rem;
|
||||
--header-spacing: var(--sl-spacing-large);
|
||||
|
|
|
@ -3,10 +3,12 @@ import { classMap } from 'lit/directives/class-map.js';
|
|||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
|
||||
import { getTabbableBoundary } from '../../internal/tabbable.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlPopup from '../popup/popup.component.js';
|
||||
import styles from './dropdown.styles.js';
|
||||
|
@ -40,7 +42,7 @@ import type SlMenu from '../menu/menu.js';
|
|||
* @animation dropdown.hide - The animation to use when hiding the dropdown.
|
||||
*/
|
||||
export default class SlDropdown extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = { 'sl-popup': SlPopup };
|
||||
|
||||
@query('.dropdown') popup: SlPopup;
|
||||
|
@ -101,6 +103,11 @@ export default class SlDropdown extends ShoelaceElement {
|
|||
*/
|
||||
@property({ type: Boolean }) hoist = false;
|
||||
|
||||
/**
|
||||
* Syncs the popup width or height to that of the trigger element.
|
||||
*/
|
||||
@property({ reflect: true }) sync: 'width' | 'height' | 'both' | undefined = undefined;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
|
@ -408,6 +415,7 @@ export default class SlDropdown extends ShoelaceElement {
|
|||
shift
|
||||
auto-size="vertical"
|
||||
auto-size-padding="10"
|
||||
sync=${ifDefined(this.sync ? this.sync : undefined)}
|
||||
class=${classMap({
|
||||
dropdown: true,
|
||||
'dropdown--open': this.open
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { classMap } from 'lit/directives/class-map.js';
|
|||
import { html, literal } from 'lit/static-html.js';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './icon-button.styles.js';
|
||||
|
@ -21,7 +22,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
export default class SlIconButton extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = { 'sl-icon': SlIcon };
|
||||
|
||||
@query('.icon-button') button: HTMLButtonElement | HTMLLinkElement;
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
color: var(--sl-color-neutral-600);
|
||||
|
|
|
@ -3,9 +3,9 @@ import { html } from 'lit';
|
|||
import { isTemplateResult } from 'lit/directive-helpers.js';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './icon.styles.js';
|
||||
|
||||
import type { CSSResultGroup, HTMLTemplateResult } from 'lit';
|
||||
|
||||
const CACHEABLE_ERROR = Symbol();
|
||||
|
@ -33,7 +33,7 @@ interface IconSource {
|
|||
* @csspart use - The <use> element generated when using `spriteSheet: true`
|
||||
*/
|
||||
export default class SlIcon extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
private initialRender = false;
|
||||
|
||||
|
@ -42,9 +42,21 @@ export default class SlIcon extends ShoelaceElement {
|
|||
let fileData: Response;
|
||||
|
||||
if (library?.spriteSheet) {
|
||||
return html`<svg part="svg">
|
||||
this.svg = html`<svg part="svg">
|
||||
<use part="use" href="${url}"></use>
|
||||
</svg>`;
|
||||
|
||||
// Using a templateResult requires the SVG to be written to the DOM first before we can grab the SVGElement
|
||||
// to be passed to the library's mutator function.
|
||||
await this.updateComplete;
|
||||
|
||||
const svg = this.shadowRoot!.querySelector("[part='svg']")!;
|
||||
|
||||
if (typeof library.mutator === 'function') {
|
||||
library.mutator(svg as SVGElement);
|
||||
}
|
||||
|
||||
return this.svg;
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
|
|
|
@ -204,6 +204,10 @@ describe('<sl-icon>', () => {
|
|||
const rect = use?.getBoundingClientRect();
|
||||
expect(rect?.width).to.equal(0);
|
||||
expect(rect?.width).to.equal(0);
|
||||
|
||||
// Make sure the mutator is applied.
|
||||
// https://github.com/shoelace-style/shoelace/issues/1925
|
||||
expect(svg?.getAttribute('fill')).to.equal('currentColor');
|
||||
});
|
||||
|
||||
// TODO: <use> svg icons don't emit a "load" or "error" event...if we can figure out how to get the event to emit errors.
|
||||
|
|
|
@ -2,10 +2,10 @@ import { clamp } from '../../internal/math.js';
|
|||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { drag } from '../../internal/drag.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './image-comparer.styles.js';
|
||||
|
@ -35,11 +35,9 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @cssproperty --handle-size - The size of the compare handle.
|
||||
*/
|
||||
export default class SlImageComparer extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static scopedElement = { 'sl-icon': SlIcon };
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@query('.image-comparer') base: HTMLElement;
|
||||
@query('.image-comparer__handle') handle: HTMLElement;
|
||||
|
||||
|
@ -48,7 +46,7 @@ export default class SlImageComparer extends ShoelaceElement {
|
|||
|
||||
private handleDrag(event: PointerEvent) {
|
||||
const { width } = this.base.getBoundingClientRect();
|
||||
const isRtl = this.localize.dir() === 'rtl';
|
||||
const isRtl = this.matches(':dir(rtl)');
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
|
@ -62,8 +60,8 @@ export default class SlImageComparer extends ShoelaceElement {
|
|||
}
|
||||
|
||||
private handleKeyDown(event: KeyboardEvent) {
|
||||
const isLtr = this.localize.dir() === 'ltr';
|
||||
const isRtl = this.localize.dir() === 'rtl';
|
||||
const isLtr = this.matches(':dir(ltr)');
|
||||
const isRtl = this.matches(':dir(rtl)');
|
||||
|
||||
if (['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) {
|
||||
const incr = event.shiftKey ? 10 : 1;
|
||||
|
@ -95,7 +93,7 @@ export default class SlImageComparer extends ShoelaceElement {
|
|||
}
|
||||
|
||||
render() {
|
||||
const isRtl = this.localize.dir() === 'rtl';
|
||||
const isRtl = this.matches(':dir(rtl)');
|
||||
|
||||
return html`
|
||||
<div
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--divider-width: 2px;
|
||||
--handle-size: 2.5rem;
|
||||
|
|
|
@ -2,6 +2,7 @@ import { html } from 'lit';
|
|||
import { property } from 'lit/decorators.js';
|
||||
import { requestInclude } from './request.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './include.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -16,7 +17,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @event {{ status: number }} sl-error - Emitted when the included file fails to load due to an error.
|
||||
*/
|
||||
export default class SlInclude extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
/**
|
||||
* The location of the HTML file to include. Be sure you trust the content you are including as it will be executed as
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import { live } from 'lit/directives/live.js';
|
|||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import formControlStyles from '../../styles/form-control.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './input.styles.js';
|
||||
|
@ -49,7 +51,7 @@ import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
|
|||
* @csspart suffix - The container that wraps the suffix.
|
||||
*/
|
||||
export default class SlInput extends ShoelaceElement implements ShoelaceFormControl {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, formControlStyles, styles];
|
||||
static dependencies = { 'sl-icon': SlIcon };
|
||||
|
||||
private readonly formControlController = new FormControlController(this, {
|
||||
|
@ -249,13 +251,16 @@ export default class SlInput extends ShoelaceElement implements ShoelaceFormCont
|
|||
}
|
||||
|
||||
private handleClearClick(event: MouseEvent) {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.value !== '') {
|
||||
this.value = '';
|
||||
this.emit('sl-clear');
|
||||
this.emit('sl-input');
|
||||
this.emit('sl-change');
|
||||
this.input.focus();
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
this.input.focus();
|
||||
}
|
||||
|
||||
private handleFocus() {
|
||||
|
@ -491,14 +496,11 @@ export default class SlInput extends ShoelaceElement implements ShoelaceFormCont
|
|||
@blur=${this.handleBlur}
|
||||
/>
|
||||
|
||||
${hasClearIcon
|
||||
${isClearIconVisible
|
||||
? html`
|
||||
<button
|
||||
part="clear-button"
|
||||
class=${classMap({
|
||||
input__clear: true,
|
||||
'input__clear--visible': isClearIconVisible
|
||||
})}
|
||||
class="input__clear"
|
||||
type="button"
|
||||
aria-label=${this.localize.term('clearEntry')}
|
||||
@click=${this.handleClearClick}
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import formControlStyles from '../../styles/form-control.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
${formControlStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
@ -252,10 +247,6 @@ export default css`
|
|||
* Clearable + Password Toggle
|
||||
*/
|
||||
|
||||
.input__clear:not(.input__clear--visible) {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.input__clear,
|
||||
.input__password-toggle {
|
||||
display: inline-flex;
|
||||
|
@ -280,10 +271,6 @@ export default css`
|
|||
outline: none;
|
||||
}
|
||||
|
||||
.input--empty .input__clear {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* Don't show the browser's password toggle in Edge */
|
||||
::-ms-reveal {
|
||||
display: none;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { getTextContent, HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { SubmenuController } from './submenu-controller.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import SlPopup from '../popup/popup.component.js';
|
||||
|
@ -39,7 +39,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @cssproperty [--submenu-offset=-2px] - The distance submenus shift to overlap the parent menu.
|
||||
*/
|
||||
export default class SlMenuItem extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = {
|
||||
'sl-icon': SlIcon,
|
||||
'sl-popup': SlPopup,
|
||||
|
@ -66,9 +66,8 @@ export default class SlMenuItem extends ShoelaceElement {
|
|||
/** Draws the menu item in a disabled state, preventing selection. */
|
||||
@property({ type: Boolean, reflect: true }) disabled = false;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private readonly hasSlotController = new HasSlotController(this, 'submenu');
|
||||
private submenuController: SubmenuController = new SubmenuController(this, this.hasSlotController, this.localize);
|
||||
private submenuController: SubmenuController = new SubmenuController(this, this.hasSlotController);
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
@ -154,7 +153,7 @@ export default class SlMenuItem extends ShoelaceElement {
|
|||
}
|
||||
|
||||
render() {
|
||||
const isRtl = this.localize.dir() === 'rtl';
|
||||
const isRtl = this.matches(':dir(rtl)');
|
||||
const isSubmenuExpanded = this.submenuController.isExpanded();
|
||||
|
||||
return html`
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--submenu-offset: -2px;
|
||||
|
||||
|
@ -150,4 +147,9 @@ export default css`
|
|||
outline-offset: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
::slotted(sl-menu) {
|
||||
max-width: var(--auto-size-available-width) !important;
|
||||
max-height: var(--auto-size-available-height) !important;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { createRef, ref, type Ref } from 'lit/directives/ref.js';
|
||||
import { type HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { type LocalizeController } from '../../utilities/localize.js';
|
||||
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
||||
import type SlMenuItem from './menu-item.js';
|
||||
import type SlPopup from '../popup/popup.js';
|
||||
|
@ -15,17 +14,11 @@ export class SubmenuController implements ReactiveController {
|
|||
private isPopupConnected = false;
|
||||
private skidding = 0;
|
||||
private readonly hasSlotController: HasSlotController;
|
||||
private readonly localize: LocalizeController;
|
||||
private readonly submenuOpenDelay = 100;
|
||||
|
||||
constructor(
|
||||
host: ReactiveControllerHost & SlMenuItem,
|
||||
hasSlotController: HasSlotController,
|
||||
localize: LocalizeController
|
||||
) {
|
||||
constructor(host: ReactiveControllerHost & SlMenuItem, hasSlotController: HasSlotController) {
|
||||
(this.host = host).addController(this);
|
||||
this.hasSlotController = hasSlotController;
|
||||
this.localize = localize;
|
||||
}
|
||||
|
||||
hostConnected() {
|
||||
|
@ -202,8 +195,7 @@ export class SubmenuController implements ReactiveController {
|
|||
private handlePopupReposition = () => {
|
||||
const submenuSlot: HTMLSlotElement | null = this.host.renderRoot.querySelector("slot[name='submenu']");
|
||||
const menu = submenuSlot?.assignedElements({ flatten: true }).filter(el => el.localName === 'sl-menu')[0];
|
||||
const isRtl = this.localize.dir() === 'rtl';
|
||||
|
||||
const isRtl = this.host.matches(':dir(rtl)');
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
|
@ -229,6 +221,7 @@ export class SubmenuController implements ReactiveController {
|
|||
// newly opened menu.
|
||||
private enableSubmenu(delay = true) {
|
||||
if (delay) {
|
||||
window.clearTimeout(this.enableSubmenuTimer);
|
||||
this.enableSubmenuTimer = window.setTimeout(() => {
|
||||
this.setSubmenuState(true);
|
||||
}, this.submenuOpenDelay);
|
||||
|
@ -238,7 +231,7 @@ export class SubmenuController implements ReactiveController {
|
|||
}
|
||||
|
||||
private disableSubmenu() {
|
||||
clearTimeout(this.enableSubmenuTimer);
|
||||
window.clearTimeout(this.enableSubmenuTimer);
|
||||
this.setSubmenuState(false);
|
||||
}
|
||||
|
||||
|
@ -266,7 +259,7 @@ export class SubmenuController implements ReactiveController {
|
|||
}
|
||||
|
||||
renderSubmenu() {
|
||||
const isLtr = this.localize.dir() === 'ltr';
|
||||
const isRtl = this.host.matches(':dir(rtl)');
|
||||
|
||||
// Always render the slot, but conditionally render the outer <sl-popup>
|
||||
if (!this.isConnected) {
|
||||
|
@ -276,12 +269,14 @@ export class SubmenuController implements ReactiveController {
|
|||
return html`
|
||||
<sl-popup
|
||||
${ref(this.popupRef)}
|
||||
placement=${isLtr ? 'right-start' : 'left-start'}
|
||||
placement=${isRtl ? 'left-start' : 'right-start'}
|
||||
anchor="anchor"
|
||||
flip
|
||||
flip-fallback-strategy="best-fit"
|
||||
skidding="${this.skidding}"
|
||||
strategy="fixed"
|
||||
auto-size="vertical"
|
||||
auto-size-padding="10"
|
||||
>
|
||||
<slot name="submenu"></slot>
|
||||
</sl-popup>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { html } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './menu-label.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -14,7 +15,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
export default class SlMenuLabel extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
render() {
|
||||
return html` <slot part="base" class="menu-label"></slot> `;
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { html } from 'lit';
|
||||
import { query } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './menu.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
import type SlMenuItem from '../menu-item/menu-item.component.js';
|
||||
|
||||
export interface MenuSelectEventDetail {
|
||||
item: SlMenuItem;
|
||||
}
|
||||
|
@ -19,7 +21,7 @@ export interface MenuSelectEventDetail {
|
|||
* @event {{ item: SlMenuItem }} sl-select - Emitted when a menu item is selected.
|
||||
*/
|
||||
export default class SlMenu extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
@query('slot') defaultSlot: HTMLSlotElement;
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
position: relative;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './mutation-observer.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -16,7 +17,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @slot - The content to watch for mutations.
|
||||
*/
|
||||
export default class SlMutationObserver extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
private mutationObserver: MutationObserver;
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: contents;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { html } from 'lit';
|
|||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './option.styles.js';
|
||||
|
@ -27,7 +28,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @csspart suffix - The container that wraps the suffix.
|
||||
*/
|
||||
export default class SlOption extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = { 'sl-icon': SlIcon };
|
||||
|
||||
private cachedTextLabel: string;
|
||||
|
@ -112,7 +113,7 @@ export default class SlOption extends ShoelaceElement {
|
|||
[...nodes].forEach(node => {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
if (!(node as HTMLElement).hasAttribute('slot')) {
|
||||
label += (node as HTMLElement).outerHTML;
|
||||
label += (node as HTMLElement).textContent;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
user-select: none;
|
||||
|
|
|
@ -52,4 +52,9 @@ describe('<sl-option>', () => {
|
|||
|
||||
expect(el.value).to.equal('10');
|
||||
});
|
||||
|
||||
it('should escape HTML when calling getTextLabel()', async () => {
|
||||
const el = await fixture<SlOption>(html` <sl-option><strong>Option</strong></sl-option> `);
|
||||
expect(el.getTextLabel()).to.equal('Option');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,16 +3,23 @@ import { classMap } from 'lit/directives/class-map.js';
|
|||
import { html } from 'lit';
|
||||
import { offsetParent } from 'composed-offset-position';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './popup.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
export interface VirtualElement {
|
||||
getBoundingClientRect: () => DOMRect;
|
||||
contextElement?: Element;
|
||||
}
|
||||
|
||||
function isVirtualElement(e: unknown): e is VirtualElement {
|
||||
return e !== null && typeof e === 'object' && 'getBoundingClientRect' in e;
|
||||
return (
|
||||
e !== null &&
|
||||
typeof e === 'object' &&
|
||||
'getBoundingClientRect' in e &&
|
||||
('contextElement' in e ? e instanceof Element : true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,7 +52,7 @@ function isVirtualElement(e: unknown): e is VirtualElement {
|
|||
* available when using `auto-size`.
|
||||
*/
|
||||
export default class SlPopup extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
private anchorEl: Element | VirtualElement | null;
|
||||
private cleanup: ReturnType<typeof autoUpdate> | undefined;
|
||||
|
@ -406,7 +413,7 @@ export default class SlPopup extends ShoelaceElement {
|
|||
//
|
||||
// Source: https://github.com/floating-ui/floating-ui/blob/cb3b6ab07f95275730d3e6e46c702f8d4908b55c/packages/dom/src/utils/getDocumentRect.ts#L31
|
||||
//
|
||||
const isRtl = getComputedStyle(this).direction === 'rtl';
|
||||
const isRtl = this.matches(':dir(rtl)');
|
||||
const staticSide = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' }[placement.split('-')[0]]!;
|
||||
|
||||
this.setAttribute('data-current-placement', placement);
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--arrow-color: var(--sl-color-neutral-1000);
|
||||
--arrow-size: 6px;
|
||||
|
|
|
@ -4,6 +4,7 @@ import { ifDefined } from 'lit/directives/if-defined.js';
|
|||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './progress-bar.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -26,7 +27,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @cssproperty --label-color - The color of the label.
|
||||
*/
|
||||
export default class SlProgressBar extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
/** The current progress as a percentage, 0 to 100. */
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--height: 1rem;
|
||||
--track-color: var(--sl-color-neutral-200);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './progress-ring.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -24,7 +25,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @cssproperty --indicator-transition-duration - The duration of the indicator's transition when the value changes.
|
||||
*/
|
||||
export default class SlProgressRing extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--size: 128px;
|
||||
--track-width: 4px;
|
||||
|
|
|
@ -2,6 +2,7 @@ import { html } from 'lit';
|
|||
import { property, query } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import QrCreator from 'qr-creator';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './qr-code.styles.js';
|
||||
|
@ -16,7 +17,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
export default class SlQrCode extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
@query('canvas') canvas: HTMLElement;
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { html } from 'lit/static-html.js';
|
|||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './radio-button.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -29,7 +30,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @csspart suffix - The container that wraps the suffix.
|
||||
*/
|
||||
export default class SlRadioButton extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, '[default]', 'prefix', 'suffix');
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ describe('<sl-radio-button>', () => {
|
|||
expect(radio2.checked).to.be.false;
|
||||
});
|
||||
|
||||
it('should receive positional classes from <sl-button-group>', async () => {
|
||||
it('should receive positional data attributes from <sl-button-group>', async () => {
|
||||
const radioGroup = await fixture<SlRadioGroup>(html`
|
||||
<sl-radio-group value="1">
|
||||
<sl-radio-button id="radio-1" value="1"></sl-radio-button>
|
||||
|
@ -35,11 +35,11 @@ describe('<sl-radio-button>', () => {
|
|||
|
||||
await Promise.all([radioGroup.updateComplete, radio1.updateComplete, radio2.updateComplete, radio3.updateComplete]);
|
||||
|
||||
expect(radio1.classList.contains('sl-button-group__button')).to.be.true;
|
||||
expect(radio1.classList.contains('sl-button-group__button--first')).to.be.true;
|
||||
expect(radio2.classList.contains('sl-button-group__button')).to.be.true;
|
||||
expect(radio2.classList.contains('sl-button-group__button--inner')).to.be.true;
|
||||
expect(radio3.classList.contains('sl-button-group__button')).to.be.true;
|
||||
expect(radio3.classList.contains('sl-button-group__button--last')).to.be.true;
|
||||
expect(radio1).to.have.attribute('data-sl-button-group__button');
|
||||
expect(radio1).to.have.attribute('data-sl-button-group__button--first');
|
||||
expect(radio2).to.have.attribute('data-sl-button-group__button');
|
||||
expect(radio2).to.have.attribute('data-sl-button-group__button--inner');
|
||||
expect(radio3).to.have.attribute('data-sl-button-group__button');
|
||||
expect(radio3).to.have.attribute('data-sl-button-group__button--last');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,6 +9,8 @@ import { HasSlotController } from '../../internal/slot.js';
|
|||
import { html } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import formControlStyles from '../../styles/form-control.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlButtonGroup from '../button-group/button-group.component.js';
|
||||
import styles from './radio-group.styles.js';
|
||||
|
@ -42,7 +44,7 @@ import type SlRadioButton from '../radio-button/radio-button.js';
|
|||
* @csspart button-group__base - The button group's `base` part.
|
||||
*/
|
||||
export default class SlRadioGroup extends ShoelaceElement implements ShoelaceFormControl {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, formControlStyles, styles];
|
||||
static dependencies = { 'sl-button-group': SlButtonGroup };
|
||||
|
||||
protected readonly formControlController = new FormControlController(this);
|
||||
|
@ -131,7 +133,7 @@ export default class SlRadioGroup extends ShoelaceElement implements ShoelaceFor
|
|||
const radios = this.getAllRadios();
|
||||
const oldValue = this.value;
|
||||
|
||||
if (target.disabled) {
|
||||
if (!target || target.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import formControlStyles from '../../styles/form-control.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
${formControlStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { classMap } from 'lit/directives/class-map.js';
|
|||
import { html } from 'lit';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './radio.styles.js';
|
||||
|
@ -27,7 +28,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @csspart label - The container that wraps the radio's label.
|
||||
*/
|
||||
export default class SlRadio extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = { 'sl-icon': SlIcon };
|
||||
|
||||
@state() checked = false;
|
||||
|
@ -115,9 +116,3 @@ export default class SlRadio extends ShoelaceElement {
|
|||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'sl-radio': SlRadio;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import { ifDefined } from 'lit/directives/if-defined.js';
|
|||
import { live } from 'lit/directives/live.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import formControlStyles from '../../styles/form-control.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './range.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -44,7 +46,7 @@ import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
|
|||
* @cssproperty --track-active-offset - The point of origin of the active track.
|
||||
*/
|
||||
export default class SlRange extends ShoelaceElement implements ShoelaceFormControl {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, formControlStyles, styles];
|
||||
|
||||
private readonly formControlController = new FormControlController(this);
|
||||
private readonly hasSlotController = new HasSlotController(this, 'help-text', 'label');
|
||||
|
@ -173,7 +175,7 @@ export default class SlRange extends ShoelaceElement implements ShoelaceFormCont
|
|||
const inputWidth = this.input.offsetWidth;
|
||||
const tooltipWidth = this.output.offsetWidth;
|
||||
const thumbSize = getComputedStyle(this.input).getPropertyValue('--thumb-size');
|
||||
const isRtl = this.localize.dir() === 'rtl';
|
||||
const isRtl = this.matches(':dir(rtl)');
|
||||
const percentAsWidth = inputWidth * percent;
|
||||
|
||||
// The calculations are used to "guess" where the thumb is located. Since we're using the native range control
|
||||
|
@ -214,7 +216,8 @@ export default class SlRange extends ShoelaceElement implements ShoelaceFormCont
|
|||
this.syncProgress(percent);
|
||||
|
||||
if (this.tooltip !== 'none') {
|
||||
this.syncTooltip(percent);
|
||||
// Ensure updates are drawn before we sync the tooltip
|
||||
this.updateComplete.then(() => this.syncTooltip(percent));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import formControlStyles from '../../styles/form-control.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
${formControlStyles}
|
||||
|
||||
:host {
|
||||
--thumb-size: 20px;
|
||||
--tooltip-offset: 10px;
|
||||
|
|
|
@ -2,10 +2,10 @@ import { clamp } from '../../internal/math.js';
|
|||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { eventOptions, property, query, state } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import SlIcon from '../icon/icon.component.js';
|
||||
import styles from './rating.styles.js';
|
||||
|
@ -32,11 +32,9 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @cssproperty --symbol-spacing - The spacing to use around symbols.
|
||||
*/
|
||||
export default class SlRating extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static dependencies = { 'sl-icon': SlIcon };
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@query('.rating') rating: HTMLElement;
|
||||
|
||||
@state() private hoverValue = 0;
|
||||
|
@ -79,7 +77,7 @@ export default class SlRating extends ShoelaceElement {
|
|||
}
|
||||
|
||||
private getValueFromXCoordinate(coordinate: number) {
|
||||
const isRtl = this.localize.dir() === 'rtl';
|
||||
const isRtl = this.matches(':dir(rtl)');
|
||||
const { left, right, width } = this.rating.getBoundingClientRect();
|
||||
const value = isRtl
|
||||
? this.roundToPrecision(((right - coordinate) / width) * this.max, this.precision)
|
||||
|
@ -107,8 +105,8 @@ export default class SlRating extends ShoelaceElement {
|
|||
}
|
||||
|
||||
private handleKeyDown(event: KeyboardEvent) {
|
||||
const isLtr = this.localize.dir() === 'ltr';
|
||||
const isRtl = this.localize.dir() === 'rtl';
|
||||
const isLtr = this.matches(':dir(ltr)');
|
||||
const isRtl = this.matches(':dir(rtl)');
|
||||
const oldValue = this.value;
|
||||
|
||||
if (this.disabled || this.readonly) {
|
||||
|
@ -213,7 +211,7 @@ export default class SlRating extends ShoelaceElement {
|
|||
}
|
||||
|
||||
render() {
|
||||
const isRtl = this.localize.dir() === 'rtl';
|
||||
const isRtl = this.matches(':dir(rtl)');
|
||||
const counter = Array.from(Array(this.max).keys());
|
||||
let displayValue = 0;
|
||||
|
||||
|
@ -263,7 +261,6 @@ export default class SlRating extends ShoelaceElement {
|
|||
'rating__symbol--hover': this.isHovering && Math.ceil(displayValue) === index + 1
|
||||
})}
|
||||
role="presentation"
|
||||
@mouseenter=${this.handleMouseEnter}
|
||||
>
|
||||
<div
|
||||
style=${styleMap({
|
||||
|
@ -296,7 +293,6 @@ export default class SlRating extends ShoelaceElement {
|
|||
'rating__symbol--active': displayValue >= index + 1
|
||||
})}
|
||||
role="presentation"
|
||||
@mouseenter=${this.handleMouseEnter}
|
||||
>
|
||||
${unsafeHTML(this.getSymbol(index + 1))}
|
||||
</span>
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--symbol-color: var(--sl-color-neutral-300);
|
||||
--symbol-color-active: var(--sl-color-amber-500);
|
||||
|
@ -60,6 +57,7 @@ export default css`
|
|||
|
||||
.rating__symbol {
|
||||
transition: var(--sl-transition-fast) scale;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rating__symbol--hover {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
import styles from './resize-observer.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
@ -16,7 +17,7 @@ import type { CSSResultGroup } from 'lit';
|
|||
* @event {{ entries: ResizeObserverEntry[] }} sl-resize - Emitted when the element is resized.
|
||||
*/
|
||||
export default class SlResizeObserver extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
|
||||
private resizeObserver: ResizeObserver;
|
||||
private observedElements: HTMLElement[] = [];
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Ładowanie…
Reference in New Issue