Merge branch 'next' into cem-better-data

This commit is contained in:
Cory LaViska
2022-11-28 11:51:20 -05:00
11 changed files with 164 additions and 131 deletions

View File

@@ -146,8 +146,6 @@
</div>
<div class="code-block__buttons">
${hasReact ? ` ${htmlButton} ${reactButton} ` : ''}
<button
type="button"
class="code-block__button code-block__toggle"
@@ -167,6 +165,8 @@
</svg>
</button>
${hasReact ? ` ${htmlButton} ${reactButton} ` : ''}
${!code.classList.contains('no-codepen') ? codePenButton : ''}
</div>
</div>
@@ -232,36 +232,45 @@
// Toggle source mode
document.addEventListener('click', event => {
const button = event.target.closest('button');
const button = event.target.closest('.code-block__button');
const codeBlock = button?.closest('.code-block');
if (button?.classList.contains('code-block__button--html')) {
// Show HTML
setFlavor('html');
toggleSource(codeBlock, true);
} else if (button?.classList.contains('code-block__button--react')) {
// Show React
setFlavor('react');
toggleSource(codeBlock, true);
} else if (button?.classList.contains('code-block__toggle')) {
// Toggle source
toggleSource(codeBlock);
} else {
return;
}
// Update flavor buttons
[...document.querySelectorAll('.code-block')].forEach(codeBlock => {
codeBlock
.querySelector('.code-block__button--html')
?.classList.toggle('code-block__button--selected', flavor === 'html');
codeBlock
.querySelector('.code-block__button--react')
?.classList.toggle('code-block__button--selected', flavor === 'react');
[...document.querySelectorAll('.code-block')].forEach(cb => {
cb.querySelector('.code-block__button--html')?.classList.toggle(
'code-block__button--selected',
flavor === 'html'
);
cb.querySelector('.code-block__button--react')?.classList.toggle(
'code-block__button--selected',
flavor === 'react'
);
});
});
// Expand and collapse code blocks
document.addEventListener('click', event => {
const toggle = event.target.closest('.code-block__toggle');
function toggleSource(codeBlock, force) {
const toggle = codeBlock.querySelector('.code-block__toggle');
if (toggle) {
const codeBlock = event.target.closest('.code-block');
codeBlock.classList.toggle('code-block--expanded');
codeBlock.classList.toggle('code-block--expanded', force === undefined ? undefined : force);
event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-block--expanded'));
}
});
}
// Show pulse when copying
document.addEventListener('click', event => {

View File

@@ -434,7 +434,7 @@
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) using a script tag:
\`\`\`html
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/${component.path}"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/dist/${component.path}"></script>
\`\`\`
</sl-tab-panel>
@@ -442,14 +442,14 @@
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) using a JavaScript import:
\`\`\`js
import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/${component.path}';
import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/dist/${component.path}';
\`\`\`
</sl-tab-panel>
<sl-tab-panel name="bundler">\n
To import this component using [a bundler](/getting-started/installation#bundling):
\`\`\`js
import '@shoelace-style/shoelace/${component.path}';
import '@shoelace-style/shoelace/dist/${component.path}';
\`\`\`
</sl-tab-panel>

View File

@@ -490,6 +490,7 @@ kbd,
.markdown-section p.tip code,
.markdown-section p.warn code {
background-color: var(--sl-color-neutral-100);
white-space: nowrap;
}
/* Sponsorship callouts */

View File

@@ -6,6 +6,8 @@ This component's name is inspired by [`<popup>`](https://github.com/MicrosoftEdg
Popup doesn't provide any styles — just positioning! The popup's preferred placement, distance, and skidding (offset) can be configured using attributes. An arrow that points to the anchor can be shown and customized to your liking. Additional positioning options are available and described in more detail below.
!> Popup is a low-level utility built specifically for positioning elements. Do not mistake it for a [tooltip](/components/tooltip) or similar because _it does not facilitate an accessible experience!_ Almost every correct usage of `<sl-popup>` will involve building other components. It should rarely, if ever, occur in your own HTML.
```html preview
<div class="popup-overview">
<sl-popup placement="top" active>

View File

@@ -4,13 +4,11 @@ Every Shoelace component makes use of a [shadow DOM](https://developer.mozilla.o
Shoelace solves this problem by using the [`formdata`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/formdata_event) event, which is [available in all modern browsers](https://caniuse.com/mdn-api_htmlformelement_formdata_event). This means, when a form is submitted, Shoelace form controls will automatically append their values to the `FormData` object that's used to submit the form. In most cases, things will "just work." However, if you're using a form serialization library, it might need to be adapted to recognize Shoelace form controls.
?> Shoelace uses event listeners to intercept the form's `formdata` and `submit` events. This allows it to inject data and trigger validation as necessary. If you're also attaching an event listener to the form, _you must attach it after Shoelace form controls are connected to the DOM_, otherwise your logic will run before Shoelace has a chance to inject form data and validate form controls.
?> If you're using an older browser that doesn't support the `formdata` event, a lightweight polyfill will be automatically applied to ensure forms submit as expected.
## A Note About Event Handling
Shoelace uses event listeners to intercept the form's `formdata` and `submit` events. This allows it to inject data and trigger validation as necessary. If you're also attaching an event listener to the form, _you must attach it after Shoelace form controls are connected to the DOM_, otherwise your logic will run before Shoelace has a chance to inject form data and validate form controls.
## Form Serialization
## Data Serialization
Serialization is just a fancy word for collecting form data. If you're relying on standard form submissions, e.g. `<form action="...">`, you can probably skip this section. However, most modern apps use the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) or a library such as [axios](https://github.com/axios/axios) to submit forms using JavaScript.
@@ -36,19 +34,21 @@ const data = serialize(form);
This results in an object with name/value pairs that map to each form control. If more than one form control shares the same name, the values will be passed as an array, e.g. `{ name: ['value1', 'value2'] }`.
## Form Control Validation
## Constraint Validation
Client-side validation can be enabled through the browser's [Constraint Validation API](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation) for Shoelace form controls. You can activate it using attributes such as `required`, `pattern`, `minlength`, and `maxlength`. Shoelace implements many of the same attributes as native form controls, but check each form control's documentation for a list of all supported properties.
Client-side validation can be enabled through the browser's [Constraint Validation API](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation) for Shoelace form controls. You can activate it using attributes such as `required`, `pattern`, `minlength`, `maxlength`, etc. Shoelace implements many of the same attributes as native form controls, but check the documentation for a list of supported properties for each component.
As the user interacts with a form control, its `invalid` attribute will reflect its validity based on its current value and the constraints that have been defined. When a form control is invalid, the containing form will not be submitted. Instead, the browser will show the user a relevant error message. If you don't want to use client-side validation, you can suppress this behavior by adding `novalidate` to the surrounding `<form>` element.
If you don't want to use client-side validation, you can suppress this behavior by adding `novalidate` to the surrounding `<form>` element.
All form controls support validation, but not all validation props are available for every component. Refer to a component's documentation to see which validation props it supports.
?> If this syntax looks unfamiliar, don't worry! Most of what you're learning on this page is platform knowledge that applies to regular form controls, too.
!> Client-side validation can be used to improve the UX of forms, but it is not a replacement for server-side validation. **You should always validate and sanitize user input on the server!**
### Required Fields
To make a field required, use the `required` prop. The form will not be submitted if a required form control is empty.
To make a field required, use the `required` attribute. Required fields will automatically receive a `*` after their labels. This is configurable through the `--sl-input-required-content` custom property.
The form will not be submitted if a required field is incomplete.
```html preview
<form class="input-validation-required">
@@ -65,7 +65,6 @@ To make a field required, use the `required` prop. The form will not be submitte
<br />
<sl-checkbox required>Check me before submitting</sl-checkbox>
<br /><br />
<sl-button type="reset" variant="default">Reset</sl-button>
<sl-button type="submit" variant="primary">Submit</sl-button>
</form>
@@ -119,8 +118,8 @@ To restrict a value to a specific [pattern](https://developer.mozilla.org/en-US/
<form class="input-validation-pattern">
<sl-input name="letters" required label="Letters" pattern="[A-Za-z]+"></sl-input>
<br />
<sl-button type="reset" variant="default">Reset</sl-button>
<sl-button type="submit" variant="primary">Submit</sl-button>
<sl-button type="reset" variant="default">Reset</sl-button>
</form>
<script type="module">
@@ -163,8 +162,8 @@ Some input types will automatically trigger constraints, such as `email` and `ur
<br />
<sl-input type="url" label="URL" placeholder="https://example.com/" required></sl-input>
<br />
<sl-button type="reset" variant="default">Reset</sl-button>
<sl-button type="submit" variant="primary">Submit</sl-button>
<sl-button type="reset" variant="default">Reset</sl-button>
</form>
<script type="module">
@@ -199,16 +198,16 @@ const App = () => {
};
```
### Custom Validation
### Custom Error Messages
To create a custom validation error, pass a non-empty string to the `setCustomValidity()` method. This will override any existing validation constraints. The form will not be submitted when a custom validity is set and the browser will show a validation error when the containing form is submitted. To make the input valid again, call `setCustomValidity()` again with an empty string.
```html preview
<form class="input-validation-custom">
<sl-input label="Type 'shoelace'" required></sl-input>
<sl-input label="Type shoelace" required></sl-input>
<br />
<sl-button type="reset" variant="default">Reset</sl-button>
<sl-button type="submit" variant="primary">Submit</sl-button>
<sl-button type="reset" variant="default">Reset</sl-button>
</form>
<script type="module">
@@ -267,76 +266,94 @@ const App = () => {
?> Custom validation can be applied to any form control that supports the `setCustomValidity()` method. It is not limited to inputs and textareas.
### Custom Validation Styles
## Custom Validation Styles
The `invalid` attribute reflects the form control's validity, so you can style invalid fields using the `[invalid]` selector. The example below demonstrates how you can give erroneous fields a different appearance. Type something other than "shoelace" to demonstrate this.
Due to the many ways form controls are used, Shoelace doesn't provide out of the box validation styles for form controls as part of its default theme. Instead, the following attributes will be applied to reflect a control's validity as users interact with it. You can use them to create custom styles for any of the validation states you're interested in.
- `data-required` - the form control is required
- `data-optional` - the form control is optional
- `data-invalid` - the form control is currently invalid
- `data-valid` - the form control is currently valid
- `data-user-invalid` - the form control is currently invalid and the user has interacted with it
- `data-user-valid` - the form control is currently valid and the user has interacted with it
These attributes map to the browser's built-in pseudo classes for validation: [`:required`](https://developer.mozilla.org/en-US/docs/Web/CSS/:required), [`:optional`](https://developer.mozilla.org/en-US/docs/Web/CSS/:optional), [`:invalid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:invalid), [`:valid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:valid), and the proposed [`:user-invalid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:user-invalid) and [`:user-valid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:user-valid).
?> In the future, data attributes will be replaced with custom pseudo classes such as `:--valid` and `:--invalid`. Shoelace is using data attributes as a workaround until browsers support custom states through [`ElementInternals.states`](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/states).
### Styling Invalid Form Controls
You can target validity using any of the aforementioned data attributes, but it's usually preferable to target `data-user-invalid` and `data-user-valid` since they get applied only after a user interaction such as typing or submitting. This prevents empty form controls from appearing invalid immediately, which often results in a poor user experience.
This example demonstrates custom validation styles using `data-user-invalid` and `data-user-valid`. Try Typing in the fields to see how validity changes with user input.
```html preview
<sl-input class="custom-input" label="Type Something" required pattern="shoelace">
<small slot="help-text">Please enter "shoelace" to continue</small>
</sl-input>
<form class="validity-styles">
<sl-input
name="name"
label="Name"
help-text="What would you like people to call you?"
autocomplete="off"
required
></sl-input>
<style>
.custom-input[invalid]:not([disabled])::part(label),
.custom-input[invalid]:not([disabled])::part(help-text) {
color: var(--sl-color-danger-600);
}
<sl-select label="Favorite Animal" help-text="Select the best option." clearable required>
<sl-menu-item value="birds">Birds</sl-menu-item>
<sl-menu-item value="cats">Cats</sl-menu-item>
<sl-menu-item value="dogs">Dogs</sl-menu-item>
<sl-menu-item value="other">Other</sl-menu-item>
</sl-select>
.custom-input[invalid]:not([disabled])::part(base) {
border-color: var(--sl-color-danger-500);
}
.custom-input[invalid]:focus-within::part(base) {
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-500);
}
</style>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const css = `
.custom-input[invalid]:not([disabled])::part(label),
.custom-input[invalid]:not([disabled])::part(help-text) {
color: var(--sl-color-danger-600);
}
.custom-input[invalid]:not([disabled])::part(base) {
border-color: var(--sl-color-danger-500);
}
.custom-input[invalid]:focus-within::part(base) {
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-500);
}
`;
const App = () => (
<>
<SlInput className="custom-input" required pattern="shoelace">
<small slot="help-text">Please enter "shoelace" to continue</small>
</SlInput>
<style>{css}</style>
</>
);
```
### Third-party Validation
To opt out of the browser's built-in validation and use your own, add the `novalidate` attribute to the form. This will ignore all constraints and prevent the browser from showing its own warnings when form controls are invalid.
Remember that the `invalid` attribute on form controls reflects validity as defined by the [Constraint Validation API](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation). You can set it initially, but the `invalid` attribute will update as the user interacts with the form control. As such, you should not rely on it to set invalid styles using a custom validation library.
Instead, toggle a class and target it in your stylesheet as shown below.
```html
<form novalidate>
<sl-input class="invalid"></sl-input>
<sl-button type="submit" variant="primary">Submit</sl-button>
<sl-button type="reset" variant="default">Reset</sl-button>
</form>
<script type="module">
const form = document.querySelector('.validity-styles');
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
</script>
<style>
sl-input.invalid {
...;
.validity-styles sl-input,
.validity-styles sl-select {
margin-bottom: var(--sl-spacing-medium);
}
/* user invalid styles */
.validity-styles sl-input[data-user-invalid]::part(base),
.validity-styles sl-select[data-user-invalid]::part(control) {
border-color: var(--sl-color-danger-600);
}
.validity-styles [data-user-invalid]::part(form-control-label),
.validity-styles [data-user-invalid]::part(form-control-help-text) {
color: var(--sl-color-danger-700);
}
.validity-styles sl-input:focus-within[data-user-invalid]::part(base),
.validity-styles sl-select:focus-within[data-user-invalid]::part(control) {
border-color: var(--sl-color-danger-600);
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-300);
}
/* User valid styles */
.validity-styles sl-input[data-user-valid]::part(base),
.validity-styles sl-select[data-user-valid]::part(control) {
border-color: var(--sl-color-success-600);
}
.validity-styles [data-user-valid]::part(form-control-label),
.validity-styles [data-user-valid]::part(form-control-help-text) {
color: var(--sl-color-success-700);
}
.validity-styles sl-input:focus-within[data-user-valid]::part(base),
.validity-styles sl-select:focus-within[data-user-valid]::part(control) {
border-color: var(--sl-color-success-600);
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-success-300);
}
</style>
```

View File

@@ -8,7 +8,14 @@ New versions of Shoelace are released as-needed and generally occur when a criti
?> During the beta period, these restrictions may be relaxed in the event of a mission-critical bug. 🐛
## Next
## 2.0.0-beta.85
- Fixed a bug in `<sl-dropdown>` that caused containing dialogs, drawers, etc. to close when pressing <kbd>Escape</kbd> while focused [#1024](https://github.com/shoelace-style/shoelace/issues/1024)
- Fixed a bug in `<sl-tree-item>` that allowed lazy nodes to be incorrectly selected [#1023](https://github.com/shoelace-style/shoelace/pull/1023)
- Fixed a typing bug in `<sl-tree-item>` [#1026](https://github.com/shoelace-style/shoelace/pull/1026)
- Updated Floating UI to 1.0.7 to fix a bug that prevented `hoist` from working correctly in `<sl-dropdown>` after a recent update [#1024](https://github.com/shoelace-style/shoelace/issues/1024)
## 2.0.0-beta.84
- 🚨 BREAKING: Removed the `fieldset` property from `<sl-radio-group>` (use CSS parts if you want to keep the border) [#965](https://github.com/shoelace-style/shoelace/issues/965)
- 🚨 BREAKING: Removed `base` and `label` parts from `<sl-radio-group>` (use `form-control` and `form-control__label` instead) [#965](https://github.com/shoelace-style/shoelace/issues/965)
@@ -21,6 +28,7 @@ New versions of Shoelace are released as-needed and generally occur when a criti
- `data-valid` - indicates that the form control is valid
- `data-user-invalid` - indicates the form control is invalid and the user has interacted with it
- `data-user-valid` - indicates the form control is valid and the user has interacted with it
- Added npm exports [#1020](https://github.com/shoelace-style/shoelace/pull/1020)
- Added `checkValidity()` method to all form controls
- Added `reportValidity()` method to `<sl-range>`
- Added `button--checked` to `<sl-radio-button>` and `control--checked` to `<sl-radio>` to style just the checked state [#933](https://github.com/shoelace-style/shoelace/pull/933)

18
package-lock.json generated
View File

@@ -1,15 +1,15 @@
{
"name": "@shoelace-style/shoelace",
"version": "2.0.0-beta.83",
"version": "2.0.0-beta.85",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@shoelace-style/shoelace",
"version": "2.0.0-beta.83",
"version": "2.0.0-beta.85",
"license": "MIT",
"dependencies": {
"@floating-ui/dom": "^1.0.6",
"@floating-ui/dom": "^1.0.7",
"@lit-labs/react": "^1.1.0",
"@shoelace-style/animations": "^1.1.0",
"@shoelace-style/localize": "^3.0.3",
@@ -723,9 +723,9 @@
"integrity": "sha512-Skfy0YS3NJ5nV9us0uuPN0HDk1Q4edljaOhRBJGDWs9EBa7ZVMYBHRFlhLvvmwEoaIM9BlH6QJFn9/uZg0bACg=="
},
"node_modules/@floating-ui/dom": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.0.6.tgz",
"integrity": "sha512-kt/tg1oip9OAH1xjCTcx1OpcUpu9rjDw3GKJ/rEhUqhO7QyJWfrHU0DpLTNsH67+JyFL5Kv9X1utsXwKFVtyEQ==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.0.7.tgz",
"integrity": "sha512-6RsqvCYe0AYWtsGvuWqCm7mZytnXAZCjWtsWu1Kg8dI3INvj/DbKlDsZO+mKSaQdPT12uxIW9W2dAWJkPx4Y5g==",
"dependencies": {
"@floating-ui/core": "^1.0.2"
}
@@ -15731,9 +15731,9 @@
"integrity": "sha512-Skfy0YS3NJ5nV9us0uuPN0HDk1Q4edljaOhRBJGDWs9EBa7ZVMYBHRFlhLvvmwEoaIM9BlH6QJFn9/uZg0bACg=="
},
"@floating-ui/dom": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.0.6.tgz",
"integrity": "sha512-kt/tg1oip9OAH1xjCTcx1OpcUpu9rjDw3GKJ/rEhUqhO7QyJWfrHU0DpLTNsH67+JyFL5Kv9X1utsXwKFVtyEQ==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.0.7.tgz",
"integrity": "sha512-6RsqvCYe0AYWtsGvuWqCm7mZytnXAZCjWtsWu1Kg8dI3INvj/DbKlDsZO+mKSaQdPT12uxIW9W2dAWJkPx4Y5g==",
"requires": {
"@floating-ui/core": "^1.0.2"
}

View File

@@ -1,7 +1,7 @@
{
"name": "@shoelace-style/shoelace",
"description": "A forward-thinking library of web components.",
"version": "2.0.0-beta.83",
"version": "2.0.0-beta.85",
"homepage": "https://github.com/shoelace-style/shoelace",
"author": "Cory LaViska",
"license": "MIT",
@@ -13,7 +13,12 @@
".": {
"types": "./dist/shoelace.d.ts",
"import": "./dist/shoelace.js"
}
},
"./dist/themes/*": "./dist/themes/*",
"./dist/components/*": "./dist/components/*",
"./dist/utilities/*": "./dist/utilities/*",
"./dist/react/*": "./dist/react/*",
"./dist/translations/*": "./dist/translations/*"
},
"files": [
"dist"
@@ -56,7 +61,7 @@
"node": ">=14.17.0"
},
"dependencies": {
"@floating-ui/dom": "^1.0.6",
"@floating-ui/dom": "^1.0.7",
"@lit-labs/react": "^1.1.0",
"@shoelace-style/animations": "^1.1.0",
"@shoelace-style/localize": "^3.0.3",

View File

@@ -46,6 +46,9 @@ fs.mkdirSync(outdir, { recursive: true });
format: 'esm',
target: 'es2017',
entryPoints: [
//
// NOTE: Entry points must be mapped in package.json > exports, otherwise users won't be able to import them!
//
// The whole shebang
'./src/shoelace.ts',
// Components
@@ -131,22 +134,9 @@ fs.mkdirSync(outdir, { recursive: true });
// Launch browser sync
bs.init(browserSyncConfig, () => {
// This init callback gets executed after the server has started
const socketIoConfig = browserSyncConfig.socket.socketIoClientConfig;
// Wait enough time for any open, detached clients to have a chance to reconnect. This will be used to determine
// if we reload an existing tab or open a new one.
const tabReattachDelay = socketIoConfig.reconnectionDelayMax * 2 + socketIoConfig.timeout;
setTimeout(() => {
const url = `http://localhost:${port}`;
console.log(chalk.cyan(`Launched the Shoelace dev server at ${url} 🥾\n`));
if (Object.keys(bs.sockets.sockets).length === 0) {
open(url);
} else {
bs.reload();
}
}, tabReattachDelay);
const url = `http://localhost:${port}`;
console.log(chalk.cyan(`Launched the Shoelace dev server at ${url} 🥾\n`));
open(url);
});
// Rebuild and reload when source files change

View File

@@ -214,7 +214,8 @@ export default class SlDropdown extends ShoelaceElement {
handleTriggerKeyDown(event: KeyboardEvent) {
// Close when escape or tab is pressed
if (event.key === 'Escape') {
if (event.key === 'Escape' && this.open) {
event.stopPropagation();
this.focusOnTrigger();
this.hide();
return;

View File

@@ -14,8 +14,8 @@ import '../spinner/spinner';
import styles from './tree-item.styles';
import type { CSSResultGroup, PropertyValueMap } from 'lit';
export function isTreeItem(element: Element) {
return element && element?.getAttribute('role') === 'treeitem';
export function isTreeItem(node: Node) {
return node instanceof Element && node.getAttribute('role') === 'treeitem';
}
/**
@@ -96,7 +96,7 @@ export default class SlTreeItem extends ShoelaceElement {
this.childrenContainer.hidden = !this.expanded;
this.childrenContainer.style.height = this.expanded ? 'auto' : '0';
this.isLeaf = this.getChildrenItems().length === 0;
this.isLeaf = !this.lazy && this.getChildrenItems().length === 0;
this.handleExpandedChange();
}
@@ -202,7 +202,7 @@ export default class SlTreeItem extends ShoelaceElement {
handleChildrenSlotChange() {
this.loading = false;
this.isLeaf = this.getChildrenItems().length === 0;
this.isLeaf = !this.lazy && this.getChildrenItems().length === 0;
}
protected willUpdate(changedProperties: PropertyValueMap<SlTreeItem> | Map<PropertyKey, unknown>): void {