kopia lustrzana https://github.com/shoelace-style/shoelace
				
				
				
			Merge branch 'next' into autoload
						commit
						a27fd4d2e9
					
				| 
						 | 
				
			
			@ -582,12 +582,19 @@ The content of the carousel can be changed by appending or removing carousel ite
 | 
			
		|||
      slide.innerText = `Slide ${dynamicCarousel.children.length + 1}`;
 | 
			
		||||
      slide.style.setProperty('background', `var(--sl-color-${color}-200)`);
 | 
			
		||||
      dynamicCarousel.appendChild(slide);
 | 
			
		||||
      dynamicRemove.disabled = false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const removeSlide = () => {
 | 
			
		||||
      const slide = dynamicCarousel.children[dynamicCarousel.children.length - 1];
 | 
			
		||||
      slide.remove();
 | 
			
		||||
      colorIndex--;
 | 
			
		||||
      const numSlides = dynamicCarousel.querySelectorAll('sl-carousel-item').length;
 | 
			
		||||
 | 
			
		||||
      if (numSlides > 1) {
 | 
			
		||||
        slide.remove();
 | 
			
		||||
        colorIndex--;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      dynamicRemove.disabled = numSlides - 1 <= 1;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    dynamicAdd.addEventListener('click', addSlide);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,9 +10,14 @@ New versions of Shoelace are released as-needed and generally occur when a criti
 | 
			
		|||
 | 
			
		||||
- Added an experimental autoloader
 | 
			
		||||
- Added the `subpath` argument to `getBasePath()` to make it easier to generate full paths to any file
 | 
			
		||||
- Added `tag__base`, `tag__content`, `tag__remove-button`, `tag__remove-button__base` parts to `<sl-select>`
 | 
			
		||||
 | 
			
		||||
## 2.2.0
 | 
			
		||||
 | 
			
		||||
- Added TypeScript types to all custom events [#1183](https://github.com/shoelace-style/shoelace/pull/1183)
 | 
			
		||||
- Added the `svg` part to `<sl-icon>`
 | 
			
		||||
- Added the `getForm()` method to all form controls [#1180](https://github.com/shoelace-style/shoelace/issues/1180)
 | 
			
		||||
- Added the experimental carousel component [#851](https://github.com/shoelace-style/shoelace/pull/851)
 | 
			
		||||
- Fixed a bug in `<sl-select>` that caused the display label to render incorrectly in Chrome after form validation [#1197](https://github.com/shoelace-style/shoelace/discussions/1197)
 | 
			
		||||
- Fixed a bug in `<sl-input>` that prevented users from applying their own value for `autocapitalize`, `autocomplete`, and `autocorrect` when using `type="password` [#1205](https://github.com/shoelace-style/shoelace/issues/1205)
 | 
			
		||||
- Fixed a bug in `<sl-tab-group>` that prevented scroll controls from showing when dynamically adding tabs [#1208](https://github.com/shoelace-style/shoelace/issues/1208)
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +27,7 @@ New versions of Shoelace are released as-needed and generally occur when a criti
 | 
			
		|||
- Fixed a bug in `<sl-menu-item>` that caused the focus color to show when selecting menu items with a mouse or touch device
 | 
			
		||||
- Fixed a bug in `<sl-select>` that caused `sl-change` and `sl-input` to be emitted too early [#1201](https://github.com/shoelace-style/shoelace/issues/1201)
 | 
			
		||||
- Fixed a positioning edge case that caused `<sl-popup>` to positioned nested popups incorrectly [#1135](https://github.com/shoelace-style/shoelace/issues/1135)
 | 
			
		||||
- Fixed a bug in `<sl-tree>` that caused the tree item to collapse when clicking a child item, dragging the mouse, and releasing it on the parent node [#1082](https://github.com/shoelace-style/shoelace/issues/1082)
 | 
			
		||||
- Updated `@shoelace-style/localize` to 3.1.0
 | 
			
		||||
- Updated `@floating-ui/dom` to 1.2.1
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,12 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "@shoelace-style/shoelace",
 | 
			
		||||
  "version": "2.1.0",
 | 
			
		||||
  "version": "2.2.0",
 | 
			
		||||
  "lockfileVersion": 2,
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "packages": {
 | 
			
		||||
    "": {
 | 
			
		||||
      "name": "@shoelace-style/shoelace",
 | 
			
		||||
      "version": "2.1.0",
 | 
			
		||||
      "version": "2.2.0",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@ctrl/tinycolor": "^3.5.0",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "@shoelace-style/shoelace",
 | 
			
		||||
  "description": "A forward-thinking library of web components.",
 | 
			
		||||
  "version": "2.1.0",
 | 
			
		||||
  "version": "2.2.0",
 | 
			
		||||
  "homepage": "https://github.com/shoelace-style/shoelace",
 | 
			
		||||
  "author": "Cory LaViska",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@ export class AutoplayController implements ReactiveController {
 | 
			
		|||
  private host: ReactiveElement;
 | 
			
		||||
  private timerId = 0;
 | 
			
		||||
  private tickCallback: () => void;
 | 
			
		||||
  private activeInteractions = 0;
 | 
			
		||||
 | 
			
		||||
  paused = false;
 | 
			
		||||
  stopped = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -57,12 +58,16 @@ export class AutoplayController implements ReactiveController {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  pause = () => {
 | 
			
		||||
    this.paused = true;
 | 
			
		||||
    this.host.requestUpdate();
 | 
			
		||||
    if (!this.activeInteractions++) {
 | 
			
		||||
      this.paused = true;
 | 
			
		||||
      this.host.requestUpdate();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  resume = () => {
 | 
			
		||||
    this.paused = false;
 | 
			
		||||
    this.host.requestUpdate();
 | 
			
		||||
    if (!--this.activeInteractions) {
 | 
			
		||||
      this.paused = false;
 | 
			
		||||
      this.host.requestUpdate();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,8 +28,8 @@ export default css`
 | 
			
		|||
 | 
			
		||||
  .carousel__pagination {
 | 
			
		||||
    grid-area: pagination;
 | 
			
		||||
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-wrap: wrap;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    gap: var(--sl-spacing-small);
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -64,6 +64,7 @@ export default css`
 | 
			
		|||
    scroll-snap-type: x mandatory;
 | 
			
		||||
    scroll-padding-inline: var(--scroll-hint);
 | 
			
		||||
    padding-inline: var(--scroll-hint);
 | 
			
		||||
    overflow-y: hidden;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .carousel__slides--vertical {
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +75,7 @@ export default css`
 | 
			
		|||
    scroll-snap-type: y mandatory;
 | 
			
		||||
    scroll-padding-block: var(--scroll-hint);
 | 
			
		||||
    padding-block: var(--scroll-hint);
 | 
			
		||||
    overflow-x: hidden;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .carousel__slides--dragging,
 | 
			
		||||
| 
						 | 
				
			
			@ -140,6 +142,8 @@ export default css`
 | 
			
		|||
    background-color: var(--sl-color-neutral-300);
 | 
			
		||||
    will-change: transform;
 | 
			
		||||
    transition: var(--sl-transition-fast) ease-in;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .carousel__pagination-item--active {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,6 +78,34 @@ describe('<sl-carousel>', () => {
 | 
			
		|||
      // Assert
 | 
			
		||||
      expect(el.next).not.to.have.been.called;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should not resume if the user is still interacting', async () => {
 | 
			
		||||
      // Arrange
 | 
			
		||||
      const el = await fixture<SlCarousel>(html`
 | 
			
		||||
        <sl-carousel autoplay autoplay-interval="10">
 | 
			
		||||
          <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, 'next');
 | 
			
		||||
 | 
			
		||||
      await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
      // Act
 | 
			
		||||
      el.dispatchEvent(new Event('mouseenter'));
 | 
			
		||||
      el.dispatchEvent(new Event('focusin'));
 | 
			
		||||
      await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
      el.dispatchEvent(new Event('mouseleave'));
 | 
			
		||||
      await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
      clock.next();
 | 
			
		||||
      clock.next();
 | 
			
		||||
 | 
			
		||||
      // Assert
 | 
			
		||||
      expect(el.next).not.to.have.been.called;
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('when `loop` attribute is provided', () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -168,13 +168,21 @@ export default class SlCarousel extends ShoelaceElement {
 | 
			
		|||
  goToSlide(index: number, behavior: ScrollBehavior = 'smooth') {
 | 
			
		||||
    const { slidesPerPage, loop } = this;
 | 
			
		||||
 | 
			
		||||
    const slides = this.getSlides();
 | 
			
		||||
    const slidesWithClones = this.getSlides({ excludeClones: false });
 | 
			
		||||
    const normalizedIndex = clamp(index + (loop ? slidesPerPage : 0), 0, slidesWithClones.length - 1);
 | 
			
		||||
    const slide = slidesWithClones[normalizedIndex];
 | 
			
		||||
 | 
			
		||||
    // Sets the next index without taking into account clones, if any.
 | 
			
		||||
    const newActiveSlide = (index + slides.length) % slides.length;
 | 
			
		||||
    this.activeSlide = newActiveSlide;
 | 
			
		||||
 | 
			
		||||
    // Get the index of the next slide. For looping carousel it adds `slidesPerPage`
 | 
			
		||||
    // to normalize the starting index in order to ignore the first nth clones.
 | 
			
		||||
    const nextSlideIndex = clamp(index + (loop ? slidesPerPage : 0), 0, slidesWithClones.length - 1);
 | 
			
		||||
    const nextSlide = slidesWithClones[nextSlideIndex];
 | 
			
		||||
 | 
			
		||||
    this.scrollContainer.scrollTo({
 | 
			
		||||
      left: slide.offsetLeft,
 | 
			
		||||
      top: slide.offsetTop,
 | 
			
		||||
      left: nextSlide.offsetLeft,
 | 
			
		||||
      top: nextSlide.offsetTop,
 | 
			
		||||
      behavior: prefersReducedMotion() ? 'auto' : behavior
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -307,13 +315,18 @@ export default class SlCarousel extends ShoelaceElement {
 | 
			
		|||
    this.scrollController.mouseDragging = this.mouseDragging;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private renderPagination = () => {
 | 
			
		||||
    const slides = this.getSlides();
 | 
			
		||||
    const slidesCount = slides.length;
 | 
			
		||||
  private getPageCount() {
 | 
			
		||||
    return Math.ceil(this.getSlides().length / this.slidesPerPage);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    const { activeSlide, slidesPerPage } = this;
 | 
			
		||||
    const pagesCount = Math.ceil(slidesCount / slidesPerPage);
 | 
			
		||||
    const currentPage = Math.floor(activeSlide / slidesPerPage);
 | 
			
		||||
  private getCurrentPage() {
 | 
			
		||||
    return Math.floor(this.activeSlide / this.slidesPerPage);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private renderPagination = () => {
 | 
			
		||||
    const { slidesPerPage } = this;
 | 
			
		||||
    const pagesCount = this.getPageCount();
 | 
			
		||||
    const currentPage = this.getCurrentPage();
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <nav part="pagination" role="tablist" class="carousel__pagination" aria-controls="scroll-container">
 | 
			
		||||
| 
						 | 
				
			
			@ -338,11 +351,11 @@ export default class SlCarousel extends ShoelaceElement {
 | 
			
		|||
  };
 | 
			
		||||
 | 
			
		||||
  private renderNavigation = () => {
 | 
			
		||||
    const { loop, activeSlide } = this;
 | 
			
		||||
    const slides = this.getSlides();
 | 
			
		||||
    const slidesCount = slides.length;
 | 
			
		||||
    const prevEnabled = loop || activeSlide > 0;
 | 
			
		||||
    const nextEnabled = loop || activeSlide < slidesCount - 1;
 | 
			
		||||
    const { loop } = this;
 | 
			
		||||
    const pagesCount = this.getPageCount();
 | 
			
		||||
    const currentPage = this.getCurrentPage();
 | 
			
		||||
    const prevEnabled = loop || currentPage > 0;
 | 
			
		||||
    const nextEnabled = loop || currentPage < pagesCount - 1;
 | 
			
		||||
    const isLtr = this.localize.dir() === 'ltr';
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,6 +77,8 @@ export class ScrollController<T extends ScrollHost> implements ReactiveControlle
 | 
			
		|||
        })
 | 
			
		||||
      );
 | 
			
		||||
      this.host.requestUpdate();
 | 
			
		||||
    } else {
 | 
			
		||||
      this.handleScrollEnd();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,6 +59,10 @@ import type SlRemoveEvent from '../../events/sl-remove';
 | 
			
		|||
 * @csspart listbox - The listbox container where options are slotted.
 | 
			
		||||
 * @csspart tags - The container that houses option tags when `multiselect` is used.
 | 
			
		||||
 * @csspart tag - The individual tags that represent each multiselect option.
 | 
			
		||||
 * @csspart tag__base - The tag's base part.
 | 
			
		||||
 * @csspart tag__content - The tag's content part.
 | 
			
		||||
 * @csspart tag__remove-button - The tag's remove button.
 | 
			
		||||
 * @csspart tag__remove-button__base - The tag's remove button base part.
 | 
			
		||||
 * @csspart clear-button - The clear button.
 | 
			
		||||
 * @csspart expand-icon - The container that wraps the expand icon.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -766,6 +770,12 @@ export default class SlSelect extends ShoelaceElement implements ShoelaceFormCon
 | 
			
		|||
                          return html`
 | 
			
		||||
                            <sl-tag
 | 
			
		||||
                              part="tag"
 | 
			
		||||
                              exportparts="
 | 
			
		||||
                                base:tag__base,
 | 
			
		||||
                                content:tag__content,
 | 
			
		||||
                                remove-button:tag__remove-button,
 | 
			
		||||
                                remove-button__base:tag__remove-button__base
 | 
			
		||||
                              "
 | 
			
		||||
                              ?pill=${this.pill}
 | 
			
		||||
                              size=${this.size}
 | 
			
		||||
                              removable
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
import { expect, fixture, html, triggerBlurFor, triggerFocusFor } from '@open-wc/testing';
 | 
			
		||||
import { aTimeout, expect, fixture, html, triggerBlurFor, triggerFocusFor } from '@open-wc/testing';
 | 
			
		||||
import { clickOnElement } from '../../internal/test';
 | 
			
		||||
import { sendKeys } from '@web/test-runner-commands';
 | 
			
		||||
import sinon from 'sinon';
 | 
			
		||||
import type SlTree from './tree';
 | 
			
		||||
| 
						 | 
				
			
			@ -433,7 +434,7 @@ describe('<sl-tree>', () => {
 | 
			
		|||
        const expandButton: HTMLElement = node.shadowRoot!.querySelector('.tree-item__expand-button')!;
 | 
			
		||||
 | 
			
		||||
        // Act
 | 
			
		||||
        expandButton.click();
 | 
			
		||||
        await clickOnElement(expandButton);
 | 
			
		||||
        await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
        // Assert
 | 
			
		||||
| 
						 | 
				
			
			@ -453,10 +454,10 @@ describe('<sl-tree>', () => {
 | 
			
		|||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          // Act
 | 
			
		||||
          node0.click();
 | 
			
		||||
          await clickOnElement(node0);
 | 
			
		||||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          node1.click();
 | 
			
		||||
          await clickOnElement(node1);
 | 
			
		||||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          // Assert
 | 
			
		||||
| 
						 | 
				
			
			@ -474,10 +475,10 @@ describe('<sl-tree>', () => {
 | 
			
		|||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          // Act
 | 
			
		||||
          node0.click();
 | 
			
		||||
          await clickOnElement(node0);
 | 
			
		||||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          node1.click();
 | 
			
		||||
          await clickOnElement(node1);
 | 
			
		||||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          // Assert
 | 
			
		||||
| 
						 | 
				
			
			@ -492,7 +493,7 @@ describe('<sl-tree>', () => {
 | 
			
		|||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          // Act
 | 
			
		||||
          parentNode.click();
 | 
			
		||||
          await clickOnElement(parentNode);
 | 
			
		||||
          await parentNode.updateComplete;
 | 
			
		||||
 | 
			
		||||
          // Assert
 | 
			
		||||
| 
						 | 
				
			
			@ -511,10 +512,10 @@ describe('<sl-tree>', () => {
 | 
			
		|||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          // Act
 | 
			
		||||
          node0.click();
 | 
			
		||||
          await clickOnElement(node0);
 | 
			
		||||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          node1.click();
 | 
			
		||||
          await clickOnElement(node1);
 | 
			
		||||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          // Assert
 | 
			
		||||
| 
						 | 
				
			
			@ -529,7 +530,7 @@ describe('<sl-tree>', () => {
 | 
			
		|||
          const parentNode = el.children[2] as SlTreeItem;
 | 
			
		||||
 | 
			
		||||
          // Act
 | 
			
		||||
          parentNode.click();
 | 
			
		||||
          await clickOnElement(parentNode);
 | 
			
		||||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          // Assert
 | 
			
		||||
| 
						 | 
				
			
			@ -549,7 +550,10 @@ describe('<sl-tree>', () => {
 | 
			
		|||
          const childNode = parentNode.children[0] as SlTreeItem;
 | 
			
		||||
 | 
			
		||||
          // Act
 | 
			
		||||
          childNode.click();
 | 
			
		||||
          parentNode.expanded = true;
 | 
			
		||||
          await parentNode.updateComplete;
 | 
			
		||||
          await aTimeout(300);
 | 
			
		||||
          await clickOnElement(childNode);
 | 
			
		||||
          await el.updateComplete;
 | 
			
		||||
 | 
			
		||||
          // Assert
 | 
			
		||||
| 
						 | 
				
			
			@ -572,9 +576,9 @@ describe('<sl-tree>', () => {
 | 
			
		|||
          const node = el.children[0] as SlTreeItem;
 | 
			
		||||
 | 
			
		||||
          // Act
 | 
			
		||||
          node.click();
 | 
			
		||||
          await clickOnElement(node);
 | 
			
		||||
          await el.updateComplete;
 | 
			
		||||
          node.click();
 | 
			
		||||
          await clickOnElement(node);
 | 
			
		||||
          await Promise.all([node.updateComplete, el.updateComplete]);
 | 
			
		||||
 | 
			
		||||
          // Assert
 | 
			
		||||
| 
						 | 
				
			
			@ -598,9 +602,9 @@ describe('<sl-tree>', () => {
 | 
			
		|||
        const node = el.children[0] as SlTreeItem;
 | 
			
		||||
 | 
			
		||||
        // Act
 | 
			
		||||
        node.click();
 | 
			
		||||
        await clickOnElement(node);
 | 
			
		||||
        await el.updateComplete;
 | 
			
		||||
        node.click();
 | 
			
		||||
        await clickOnElement(node);
 | 
			
		||||
        await Promise.all([node.updateComplete, el.updateComplete]);
 | 
			
		||||
 | 
			
		||||
        // Assert
 | 
			
		||||
| 
						 | 
				
			
			@ -621,7 +625,7 @@ describe('<sl-tree>', () => {
 | 
			
		|||
        const node = el.querySelector<SlTreeItem>('#expandable')!;
 | 
			
		||||
 | 
			
		||||
        // Act
 | 
			
		||||
        node.click();
 | 
			
		||||
        await clickOnElement(node);
 | 
			
		||||
        await Promise.all([node.updateComplete, el.updateComplete]);
 | 
			
		||||
 | 
			
		||||
        // Assert
 | 
			
		||||
| 
						 | 
				
			
			@ -643,9 +647,9 @@ describe('<sl-tree>', () => {
 | 
			
		|||
        const node = el.children[0] as SlTreeItem;
 | 
			
		||||
 | 
			
		||||
        // Act
 | 
			
		||||
        node.click();
 | 
			
		||||
        await clickOnElement(node);
 | 
			
		||||
        await Promise.all([node.updateComplete, el.updateComplete]);
 | 
			
		||||
        node.click();
 | 
			
		||||
        await clickOnElement(node);
 | 
			
		||||
        await Promise.all([node.updateComplete, el.updateComplete]);
 | 
			
		||||
 | 
			
		||||
        // Assert
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -90,6 +90,7 @@ export default class SlTree extends ShoelaceElement {
 | 
			
		|||
  private lastFocusedItem: SlTreeItem;
 | 
			
		||||
  private readonly localize = new LocalizeController(this);
 | 
			
		||||
  private mutationObserver: MutationObserver;
 | 
			
		||||
  private clickTarget: SlTreeItem | null = null;
 | 
			
		||||
 | 
			
		||||
  async connectedCallback() {
 | 
			
		||||
    super.connectedCallback();
 | 
			
		||||
| 
						 | 
				
			
			@ -292,13 +293,20 @@ export default class SlTree extends ShoelaceElement {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  private handleClick(event: Event) {
 | 
			
		||||
    const target = event.target as HTMLElement;
 | 
			
		||||
    const target = event.target as SlTreeItem;
 | 
			
		||||
    const treeItem = target.closest('sl-tree-item')!;
 | 
			
		||||
    const isExpandButton = event
 | 
			
		||||
      .composedPath()
 | 
			
		||||
      .some((el: HTMLElement) => el?.classList?.contains('tree-item__expand-button'));
 | 
			
		||||
 | 
			
		||||
    if (!treeItem || treeItem.disabled) {
 | 
			
		||||
    //
 | 
			
		||||
    // Don't Do anything if there's no tree item, if it's disabled, or if the click doesn't match the initial target
 | 
			
		||||
    // from mousedown. The latter case prevents the user from starting a click on one item and ending it on another,
 | 
			
		||||
    // causing the parent node to collapse.
 | 
			
		||||
    //
 | 
			
		||||
    // See https://github.com/shoelace-style/shoelace/issues/1082
 | 
			
		||||
    //
 | 
			
		||||
    if (!treeItem || treeItem.disabled || target !== this.clickTarget) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -309,6 +317,11 @@ export default class SlTree extends ShoelaceElement {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleMouseDown(event: MouseEvent) {
 | 
			
		||||
    // Record the click target so we know which item the click initially targeted
 | 
			
		||||
    this.clickTarget = event.target as SlTreeItem;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private handleFocusOut(event: FocusEvent) {
 | 
			
		||||
    const relatedTarget = event.relatedTarget as HTMLElement;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -392,7 +405,13 @@ export default class SlTree extends ShoelaceElement {
 | 
			
		|||
 | 
			
		||||
  render() {
 | 
			
		||||
    return html`
 | 
			
		||||
      <div part="base" class="tree" @click=${this.handleClick} @keydown=${this.handleKeyDown}>
 | 
			
		||||
      <div
 | 
			
		||||
        part="base"
 | 
			
		||||
        class="tree"
 | 
			
		||||
        @click=${this.handleClick}
 | 
			
		||||
        @keydown=${this.handleKeyDown}
 | 
			
		||||
        @mousedown=${this.handleMouseDown}
 | 
			
		||||
      >
 | 
			
		||||
        <slot @slotchange=${this.handleSlotChange}></slot>
 | 
			
		||||
        <slot name="expand-icon" hidden aria-hidden="true"> </slot>
 | 
			
		||||
        <slot name="collapse-icon" hidden aria-hidden="true"> </slot>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,6 +50,7 @@ export async function clickOnElement(
 | 
			
		|||
  await sendMouse({ type: 'click', position: [clickX, clickY] });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** A testing utility that moves the mouse onto an element. */
 | 
			
		||||
export async function moveMouseOnElement(
 | 
			
		||||
  /** The element to click */
 | 
			
		||||
  el: Element,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue