Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers/remove-value-as-from-input

This commit is contained in:
konnorrogers
2024-06-04 11:32:30 -04:00
132 changed files with 956 additions and 676 deletions

View File

@@ -9,10 +9,6 @@ const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
const { name, description, version, author, homepage, license } = packageData;
const outdir = 'dist';
function noDash(string) {
return string.replace(/^\s?-/, '').trim();
}
function replace(string, terms) {
terms.forEach(({ from, to }) => {
string = string?.replace(from, to);
@@ -106,6 +102,7 @@ export default {
if (classDoc?.events) {
classDoc.events.forEach(event => {
if (!event.name) return;
event.reactName = `on${pascalCase(event.name)}`;
event.eventName = `${pascalCase(event.name)}Event`;
});
@@ -174,12 +171,15 @@ export default {
url: `https://shoelace.style/components/${tag.replace('wa-', '')}`
};
}
}),
customElementVuejsPlugin({
outdir: './dist/types/vue',
fileName: 'index.d.ts',
componentTypePath: (_, tag) => `../../components/${tag.replace('wa-', '')}/${tag.replace('wa-', '')}.js`
})
//
// TODO - figure out why this broke when events were updated
//
// customElementVuejsPlugin({
// outdir: './dist/types/vue',
// fileName: 'index.d.ts',
// componentTypePath: (_, tag) => `../../components/${tag.replace('wa-', '')}/${tag.replace('wa-', '')}.js`
// })
]
};

View File

@@ -184,10 +184,12 @@
</thead>
<tbody>
{% for event in component.events %}
<tr>
<td class="table-name"><code>{{ event.name }}</code></td>
<td class="table-description">{{ event.description | inlineMarkdown | safe }}</td>
</tr>
{% if event.name %}
<tr>
<td class="table-name"><code>{{ event.name }}</code></td>
<td class="table-description">{{ event.description | inlineMarkdown | safe }}</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>

View File

@@ -392,13 +392,13 @@ const App = () => {
By default, dialogs will close when the user clicks the close button, clicks the overlay, or presses the [[Escape]] key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.
To keep the dialog open in such cases, you can cancel the `wa-request-close` event. When canceled, the dialog will remain open and pulse briefly to draw the user's attention to it.
To keep the dialog open in such cases, you can cancel the `wa-hide` event. When canceled, the dialog will remain open and pulse briefly to draw the user's attention to it.
You can use `event.detail.source` to determine which element triggered the request to close. This example prevents the dialog from closing when the overlay is clicked, but allows the close button or [[Escape]] to dismiss it.
```html {.example}
<wa-dialog label="Dialog" with-header with-footer class="dialog-deny-close">
This dialog will only close when you click the right button.
This dialog will only close when you click the button below.
<wa-button slot="footer" variant="brand" data-dialog="dismiss">Only this button will close it</wa-button>
</wa-dialog>
@@ -412,7 +412,7 @@ You can use `event.detail.source` to determine which element triggered the reque
openButton.addEventListener('click', () => dialog.open = true);
// Prevent the dialog from closing unless the close button was clicked
dialog.addEventListener('wa-request-close', event => {
dialog.addEventListener('wa-hide', event => {
if (event.detail.source !== closeButton) {
event.preventDefault();
}

View File

@@ -137,7 +137,7 @@ const App = () => {
### Dismissing Drawers
You can add the special `data-dialog="dismiss"` attribute to a button inside the drawer to tell it to close without additional JavaScript. Alternatively, you can set the `open` property to `false` to close the drawer programmatically.
You can add the special `data-drawer="dismiss"` attribute to a button inside the drawer to tell it to close without additional JavaScript. Alternatively, you can set the `open` property to `false` to close the drawer programmatically.
```html {.example}
<wa-drawer label="Drawer" with-header with-footer class="drawer-dismiss">
@@ -521,13 +521,13 @@ const App = () => {
By default, drawers will close when the user clicks the close button, clicks the overlay, or presses the [[Escape]] key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.
To keep the drawer open in such cases, you can cancel the `wa-request-close` event. When canceled, the drawer will remain open and pulse briefly to draw the user's attention to it.
To keep the drawer open in such cases, you can cancel the `wa-hide` event. When canceled, the drawer will remain open and pulse briefly to draw the user's attention to it.
You can use `event.detail.source` to determine what triggered the request to close. This example prevents the drawer from closing when the overlay is clicked, but allows the close button or [[Escape]] to dismiss it.
```html {.example}
<wa-drawer label="Drawer" with-header with-footer class="drawer-deny-close">
This drawer will not close when you click on the overlay.
This drawer will only close when you click the button below.
<wa-button slot="footer" variant="brand" data-drawer="dismiss">Close</wa-button>
</wa-drawer>
@@ -536,12 +536,13 @@ You can use `event.detail.source` to determine what triggered the request to clo
<script>
const drawer = document.querySelector('.drawer-deny-close');
const openButton = drawer.nextElementSibling;
const closeButton = drawer.querySelector('wa-button[slot="footer"]');
openButton.addEventListener('click', () => drawer.open = true);
// Prevent the drawer from closing when the user clicks on the overlay
drawer.addEventListener('wa-request-close', event => {
if (event.detail.source === 'overlay') {
// Prevent the drawer from closing unless the close button is clicked
drawer.addEventListener('wa-hide', event => {
if (event.detail.source !== closeButton) {
event.preventDefault();
}
});

View File

@@ -12,24 +12,24 @@ Web Awesome makes use of several design tokens to provide a consistent appearanc
Design tokens offer a high-level way to customize the library with minimal effort. There are no component-specific variables, however, as design tokens are intended to be generic and highly reusable. To customize an individual component, refer to the section entitled [CSS Parts](#css-parts).
Design tokens are accessed through CSS custom properties that are defined in your theme. Because design tokens live at the page level, they're prefixed with `--sl-` to avoid collisions with other libraries.
Design tokens are accessed through CSS custom properties that are defined in your theme. Because design tokens live at the page level, they're prefixed with `--wa-` to avoid collisions with other libraries.
To customize a design token, simply override it in your stylesheet using a `:root` block. Here's an example that changes the primary theme to purple based on existing [color primitives](/tokens/color#primitives).
```css
:root {
/* Changes the primary theme color to purple using primitives */
--sl-color-primary-50: var(--sl-color-purple-50);
--sl-color-primary-100: var(--sl-color-purple-100);
--sl-color-primary-200: var(--sl-color-purple-200);
--sl-color-primary-300: var(--sl-color-purple-300);
--sl-color-primary-400: var(--sl-color-purple-400);
--sl-color-primary-500: var(--sl-color-purple-500);
--sl-color-primary-600: var(--sl-color-purple-600);
--sl-color-primary-700: var(--sl-color-purple-700);
--sl-color-primary-800: var(--sl-color-purple-800);
--sl-color-primary-900: var(--sl-color-purple-900);
--sl-color-primary-950: var(--sl-color-purple-950);
--wa-color-primary-50: var(--wa-color-purple-50);
--wa-color-primary-100: var(--wa-color-purple-100);
--wa-color-primary-200: var(--wa-color-purple-200);
--wa-color-primary-300: var(--wa-color-purple-300);
--wa-color-primary-400: var(--wa-color-purple-400);
--wa-color-primary-500: var(--wa-color-purple-500);
--wa-color-primary-600: var(--wa-color-purple-600);
--wa-color-primary-700: var(--wa-color-purple-700);
--wa-color-primary-800: var(--wa-color-purple-800);
--wa-color-primary-900: var(--wa-color-purple-900);
--wa-color-primary-950: var(--wa-color-purple-950);
}
```
@@ -48,7 +48,7 @@ Here's an example that modifies buttons with the `tomato-button` class.
<style>
.tomato-button::part(base) {
background: var(--sl-color-neutral-0);
background: var(--wa-color-neutral-0);
border: solid 1px tomato;
}
@@ -82,7 +82,7 @@ Most (but not all) components expose parts. You can find them in each component'
## Custom Properties
For convenience, some components expose CSS custom properties you can override. These are not design tokens, nor do they have the same `--sl-` prefix since they're scoped to a component.
For convenience, some components expose CSS custom properties you can override. These are not design tokens, nor do they have the same `--wa-` prefix since they're scoped to a component.
You can set custom properties on a component in your stylesheet.

View File

@@ -39,6 +39,7 @@ New versions of Web Awesome are released as-needed and generally occur when a cr
- Removed the `show()` method from `<wa-tab-group>` (use the `active` attribute instead)
- Removed the `show()` and `hide()` methods from `<wa-dialog>` and `<wa-drawer`> (toggle the `open` attribute instead)
- Removed JavaScript-based animation customizations due to high confusion and low usage
- Removed the `wa-request-close` event from `<wa-dialog>` and `<wa-drawer>` (use the `wa-hide` event instead)
- Removed `valueAsDate` from `<wa-input>`. Instead use the following to mimic browser behavior:
setter: `waInput.value = new Date().toLocaleDateString()`
getter: `new Date(waInput.value)`

View File

@@ -314,9 +314,9 @@ When providing fallback content inside of `<slot>` elements, avoid adding parts,
This creates confusion because the part will be documented, but it won't work when the user slots in their own content. The recommended way to customize this example is for the user to slot in their own content and target its styles with CSS as needed.
### Custom Events
### Emitting Events
Components must only emit custom events, and all custom events must start with `sl-` as a namespace. For compatibility with frameworks that utilize DOM templates, custom events must have lowercase, kebab-style names. For example, use `wa-change` instead of `slChange`.
Components must only emit events that start with `wa-` as a namespace. For compatibility with frameworks that utilize DOM templates, events must have lowercase, kebab-style names. For example, use `wa-change` instead of `waChange`.
This convention avoids the problem of browsers lowercasing attributes, causing some frameworks to be unable to listen to them. This problem isn't specific to one framework, but [Vue's documentation](https://vuejs.org/v2/guide/components-custom-events.html#Event-Names) provides a good explanation of the problem.

View File

@@ -14,7 +14,7 @@ For component developers, built-in themes are also available as JavaScript modul
## Theme Basics
All themes are scoped to classes using the `sl-theme-{name}` convention, where `{name}` is a lowercase, hyphen-delimited value representing the name of the theme. The included light and dark themes use `wa-theme-default-light` and `wa-theme-default-dark`, respectively. A custom theme called "Purple Power", for example, would use a class called `sl-theme-purple-power`
All themes are scoped to classes using the `wa-theme-{name}` convention, where `{name}` is a lowercase, hyphen-delimited value representing the name of the theme. The included light and dark themes use `wa-theme-default-light` and `wa-theme-default-dark`, respectively. A custom theme called "Purple Power", for example, would use a class called `wa-theme-purple-power`
All selectors must be scoped to the theme's class to ensure interoperability with other themes. You should also scope them to `:host` so they can be imported and applied to custom element shadow roots.

View File

@@ -39,7 +39,7 @@ Refer to a component's documentation for a complete list of its properties.
You can listen for standard events such as `click`, `mouseover`, etc. as you normally would. However, it's important to note that many events emitted within a component's shadow root will be [retargeted](https://dom.spec.whatwg.org/#retarget) to the host element. This may result in, for example, multiple `click` handlers executing even if the user clicks just once. Furthermore, `event.target` will point to the host element, making things even more confusing.
As a result, you should almost always listen for custom events instead. For example, instead of listening to `click` to determine when an `<wa-checkbox>` gets toggled, listen to `wa-change`.
As a result, you should almost always listen for Web Awesome events instead. For example, instead of listening to `click` to determine when an `<wa-checkbox>` gets toggled, listen to `wa-change`.
```html
<wa-checkbox>Check me</wa-checkbox>
@@ -52,7 +52,7 @@ As a result, you should almost always listen for custom events instead. For exam
</script>
```
All custom events are prefixed with `sl-` to prevent collisions with standard events and other libraries. Refer to a component's documentation for a complete list of its custom events.
All Web Awesome events are prefixed with `wa-` to prevent collisions with standard events and other libraries. Refer to a component's documentation for a complete list of its events.
## Methods

View File

@@ -9,6 +9,14 @@ export function getAllComponents(metadata) {
const path = module.path;
if (component) {
// Calling `new Event()` adds a blank entry into the CEM, so we'll filter them out here
if (component.events) {
component.events = component.events.filter(event => {
return event.name ? true : false;
});
}
// component.events = component.events.filter(event => !!event.name);
allComponents.push(Object.assign(component, { path }));
}
}

View File

@@ -1,6 +1,8 @@
import '../icon/icon.js';
import { customElement, property, query, state } from 'lit/decorators.js';
import { html } from 'lit';
import { WaErrorEvent } from '../../events/error.js';
import { WaLoadEvent } from '../../events/load.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './animated-image.styles.js';
@@ -57,13 +59,13 @@ export default class WaAnimatedImage extends WebAwesomeElement {
this.frozenFrame = canvas.toDataURL('image/gif');
if (!this.isLoaded) {
this.emit('wa-load');
this.dispatchEvent(new WaLoadEvent());
this.isLoaded = true;
}
}
private handleError() {
this.emit('wa-error');
this.dispatchEvent(new WaErrorEvent());
}
@watch('play', { waitUntilFirstUpdate: true })

View File

@@ -1,6 +1,9 @@
import { animations } from './animations.js';
import { customElement, property, queryAsync } from 'lit/decorators.js';
import { html } from 'lit';
import { WaCancelEvent } from '../../events/cancel.js';
import { WaFinishEvent } from '../../events/finish.js';
import { WaStartEvent } from '../../events/start.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './animation.styles.js';
@@ -102,13 +105,13 @@ export default class WaAnimation extends WebAwesomeElement {
private handleAnimationFinish = () => {
this.play = false;
this.hasStarted = false;
this.emit('wa-finish');
this.dispatchEvent(new WaFinishEvent());
};
private handleAnimationCancel = () => {
this.play = false;
this.hasStarted = false;
this.emit('wa-cancel');
this.dispatchEvent(new WaCancelEvent());
};
private handleSlotChange() {
@@ -143,7 +146,7 @@ export default class WaAnimation extends WebAwesomeElement {
if (this.play) {
this.hasStarted = true;
this.emit('wa-start');
this.dispatchEvent(new WaStartEvent());
} else {
this.animation.pause();
}
@@ -185,7 +188,7 @@ export default class WaAnimation extends WebAwesomeElement {
if (this.animation) {
if (this.play && !this.hasStarted) {
this.hasStarted = true;
this.emit('wa-start');
this.dispatchEvent(new WaStartEvent());
}
if (this.play) {

View File

@@ -6,6 +6,9 @@ import { html, literal } from 'lit/static-html.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { LocalizeController } from '../../utilities/localize.js';
import { MirrorValidator } from '../../internal/validators/mirror-validator.js';
import { WaBlurEvent } from '../../events/blur.js';
import { WaFocusEvent } from '../../events/focus.js';
import { WaInvalidEvent } from '../../events/invalid.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
@@ -148,12 +151,12 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
private handleBlur() {
this.hasFocus = false;
this.emit('wa-blur');
this.dispatchEvent(new WaBlurEvent());
}
private handleFocus() {
this.hasFocus = true;
this.emit('wa-focus');
this.dispatchEvent(new WaFocusEvent());
}
private handleClick() {
@@ -193,7 +196,7 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
}
private handleInvalid() {
this.emit('wa-invalid');
this.dispatchEvent(new WaInvalidEvent());
}
private isButton() {

View File

@@ -11,6 +11,7 @@ import { map } from 'lit/directives/map.js';
import { prefersReducedMotion } from '../../internal/animate.js';
import { range } from 'lit/directives/range.js';
import { waitForEvent } from '../../internal/event.js';
import { WaSlideChangeEvent } from '../../events/slide-change.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './carousel.styles.js';
@@ -387,12 +388,12 @@ export default class WaCarousel extends WebAwesomeElement {
// Do not emit an event on first render
if (this.hasUpdated) {
this.emit('wa-slide-change', {
detail: {
this.dispatchEvent(
new WaSlideChangeEvent({
index: this.activeSlide,
slide: slides[this.activeSlide]
}
});
})
);
}
}

View File

@@ -6,6 +6,10 @@ import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import { RequiredValidator } from '../../internal/validators/required-validator.js';
import { WaBlurEvent } from '../../events/blur.js';
import { WaChangeEvent } from '../../events/change.js';
import { WaFocusEvent } from '../../events/focus.js';
import { WaInputEvent } from '../../events/input.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
@@ -113,21 +117,21 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
private handleClick() {
this.checked = !this.checked;
this.indeterminate = false;
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
}
private handleBlur() {
this.hasFocus = false;
this.emit('wa-blur');
this.dispatchEvent(new WaBlurEvent());
}
private handleInput() {
this.emit('wa-input');
this.dispatchEvent(new WaInputEvent());
}
private handleFocus() {
this.hasFocus = true;
this.emit('wa-focus');
this.dispatchEvent(new WaFocusEvent());
}
@watch('defaultChecked')

View File

@@ -32,7 +32,7 @@ describe('<wa-color-picker>', async () => {
await aTimeout(200); // wait for the dropdown to open
await el.updateComplete;
// Simulate a drag event. "sl-change" should not fire until we stop dragging.
// Simulate a drag event. "wa-change" should not fire until we stop dragging.
await dragElement(grid, 2, 0, {
afterMouseDown: () => {
expect(changeHandler).to.have.not.been.called;

View File

@@ -14,13 +14,16 @@ import { LocalizeController } from '../../utilities/localize.js';
import { RequiredValidator } from '../../internal/validators/required-validator.js';
import { styleMap } from 'lit/directives/style-map.js';
import { TinyColor } from '@ctrl/tinycolor';
import { WaBlurEvent } from '../../events/blur.js';
import { WaChangeEvent } from '../../events/change.js';
import { WaFocusEvent } from '../../events/focus.js';
import { WaInputEvent } from '../../events/input.js';
import { WaInvalidEvent } from '../../events/invalid.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './color-picker.styles.js';
import type { CSSResultGroup } from 'lit';
import type { WaChangeEvent } from '../../events/wa-change.js';
import type { WaInputEvent } from '../../events/wa-input.js';
import type WaDropdown from '../dropdown/dropdown.js';
import type WaInput from '../input/input.js';
@@ -218,12 +221,12 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
private handleFocusIn = () => {
this.hasFocus = true;
this.emit('wa-focus');
this.dispatchEvent(new WaFocusEvent());
};
private handleFocusOut = () => {
this.hasFocus = false;
this.emit('wa-blur');
this.dispatchEvent(new WaBlurEvent());
};
private handleFormatToggle() {
@@ -231,8 +234,8 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
const nextIndex = (formats.indexOf(this.format) + 1) % formats.length;
this.format = formats[nextIndex] as 'hex' | 'rgb' | 'hsl' | 'hsv';
this.setColor(this.value);
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
private handleAlphaDrag(event: PointerEvent) {
@@ -252,13 +255,13 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
if (this.value !== currentValue) {
currentValue = this.value;
this.emit('wa-input');
this.dispatchEvent(new WaInputEvent());
}
},
onStop: () => {
if (this.value !== initialValue) {
initialValue = this.value;
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
}
},
initialEvent: event
@@ -282,13 +285,13 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
if (this.value !== currentValue) {
currentValue = this.value;
this.emit('wa-input');
this.dispatchEvent(new WaInputEvent());
}
},
onStop: () => {
if (this.value !== initialValue) {
initialValue = this.value;
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
}
},
initialEvent: event
@@ -315,14 +318,14 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
if (this.value !== currentValue) {
currentValue = this.value;
this.emit('wa-input');
this.dispatchEvent(new WaInputEvent());
}
},
onStop: () => {
this.isDraggingGridHandle = false;
if (this.value !== initialValue) {
initialValue = this.value;
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
}
},
initialEvent: event
@@ -358,8 +361,8 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
}
@@ -392,8 +395,8 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
}
@@ -426,8 +429,8 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
}
@@ -446,8 +449,8 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
}
@@ -467,8 +470,8 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
this.input.value = this.value;
if (this.value !== oldValue) {
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
setTimeout(() => this.input.select());
@@ -643,8 +646,8 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
this.setColor(colorSelectionResult.sRGBHex);
if (this.value !== oldValue) {
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
})
.catch(() => {
@@ -659,8 +662,8 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
this.setColor(color);
if (this.value !== oldValue) {
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
}
}
@@ -785,7 +788,7 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
if (!this.disabled) {
// By standards we have to emit a `wa-invalid` event here synchronously.
this.emit('wa-invalid');
this.dispatchEvent(new WaInvalidEvent());
}
return false;

View File

@@ -5,6 +5,8 @@ import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query, state } from 'lit/decorators.js';
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize.js';
import { WaCopyEvent } from '../../events/copy.js';
import { WaErrorEvent } from '../../events/error.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './copy-button.styles.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
@@ -132,27 +134,23 @@ export default class WaCopyButton extends WebAwesomeElement {
} else {
// No target
this.showStatus('error');
this.emit('wa-error');
this.dispatchEvent(new WaErrorEvent());
}
}
// No value
if (!valueToCopy) {
this.showStatus('error');
this.emit('wa-error');
this.dispatchEvent(new WaErrorEvent());
} else {
try {
await navigator.clipboard.writeText(valueToCopy);
this.showStatus('success');
this.emit('wa-copy', {
detail: {
value: valueToCopy
}
});
this.dispatchEvent(new WaCopyEvent({ value: valueToCopy }));
} catch (error) {
// Rejected by browser
this.showStatus('error');
this.emit('wa-error');
this.dispatchEvent(new WaErrorEvent());
}
}
}

View File

@@ -1,8 +1,8 @@
// cspell:dictionaries lorem-ipsum
import { expect, fixture, html, waitUntil } from '@open-wc/testing';
import sinon from 'sinon';
import type { WaHideEvent } from '../../events/wa-hide.js';
import type { WaShowEvent } from '../../events/wa-show.js';
import type { WaHideEvent } from '../../events/hide.js';
import type { WaShowEvent } from '../../events/show.js';
import type WaDetails from './details.js';
describe('<wa-details>', () => {

View File

@@ -1,9 +1,13 @@
import '../icon/icon.js';
import { animate, parseDuration, stopAnimations } from '../../internal/animate.js';
import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query } from 'lit/decorators.js';
import { html } from 'lit';
import { parseDuration, stopAnimations } from '../../internal/animate.js';
import { WaAfterHideEvent } from '../../events/after-hide.js';
import { WaAfterShowEvent } from '../../events/after-show.js';
import { WaHideEvent } from '../../events/hide.js';
import { waitForEvent } from '../../internal/event.js';
import { WaShowEvent } from '../../events/show.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './details.styles.js';
@@ -132,8 +136,9 @@ export default class WaDetails extends WebAwesomeElement {
if (this.open) {
this.details.open = true;
// Show
const slShow = this.emit('wa-show', { cancelable: true });
if (slShow.defaultPrevented) {
const waShow = new WaShowEvent();
this.dispatchEvent(waShow);
if (waShow.defaultPrevented) {
this.open = false;
this.details.open = false;
return;
@@ -142,7 +147,8 @@ export default class WaDetails extends WebAwesomeElement {
await stopAnimations(this.body);
const duration = parseDuration(getComputedStyle(this.body).getPropertyValue('--show-duration'));
// We can't animate to 'auto', so use the scroll height for now
await this.body.animate(
await animate(
this.body,
[
{ height: '0', opacity: '0' },
{ height: `${this.body.scrollHeight}px`, opacity: '1' }
@@ -151,14 +157,15 @@ export default class WaDetails extends WebAwesomeElement {
duration,
easing: 'linear'
}
).finished;
);
this.body.style.height = 'auto';
this.emit('wa-after-show');
this.dispatchEvent(new WaAfterShowEvent());
} else {
// Hide
const slHide = this.emit('wa-hide', { cancelable: true });
if (slHide.defaultPrevented) {
const waHide = new WaHideEvent();
this.dispatchEvent(waHide);
if (waHide.defaultPrevented) {
this.details.open = true;
this.open = true;
return;
@@ -167,17 +174,18 @@ export default class WaDetails extends WebAwesomeElement {
await stopAnimations(this.body);
const duration = parseDuration(getComputedStyle(this.body).getPropertyValue('--hide-duration'));
// We can't animate from 'auto', so use the scroll height for now
await this.body.animate(
await animate(
this.body,
[
{ height: `${this.body.scrollHeight}px`, opacity: '1' },
{ height: '0', opacity: '0' }
],
{ duration, easing: 'linear' }
).finished;
);
this.body.style.height = 'auto';
this.details.open = false;
this.emit('wa-after-hide');
this.dispatchEvent(new WaAfterHideEvent());
}
}

View File

@@ -98,12 +98,12 @@ describe('<wa-dialog>', () => {
expect(getComputedStyle(el).display).to.equal('none');
});
it('should not close when wa-request-close is prevented', async () => {
it('should not close when wa-hide is prevented', async () => {
const el = await fixture<WaDialog>(html`
<wa-dialog with-header open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
`);
el.addEventListener('wa-request-close', event => {
el.addEventListener('wa-hide', event => {
event.preventDefault();
});
await sendKeys({ press: 'Escape' });

View File

@@ -5,6 +5,10 @@ import { customElement, property, query } from 'lit/decorators.js';
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize.js';
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll.js';
import { WaAfterHideEvent } from '../../events/after-hide.js';
import { WaAfterShowEvent } from '../../events/after-show.js';
import { WaHideEvent } from '../../events/hide.js';
import { WaShowEvent } from '../../events/show.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './dialog.styles.js';
@@ -26,13 +30,12 @@ import type { CSSResultGroup } from 'lit';
*
* @event wa-show - Emitted when the dialog opens.
* @event wa-after-show - Emitted after the dialog opens and all animations are complete.
* @event wa-hide - Emitted when the dialog closes.
* @event wa-after-hide - Emitted after the dialog closes and all animations are complete.
* @event {{ source: Element }} wa-request-close - Emitted when the user attempts to close the dialog. Calling
* `event.preventDefault()` will keep the dialog open. You can inspect `event.detail.source` to see which element
* caused the dialog to close. If the source is the dialog element itself, the user has pressed [[Escape]] or the
* dialog has been closed programmatically. Avoid using this unless closing the dialog will result in destructive
* @event {{ source: Element }} wa-hide - Emitted when the dialog is requested to close. Calling
* `event.preventDefault()` will prevent the dialog from closing. You can inspect `event.detail.source` to see which
* element caused the dialog to close. If the source is the dialog element itself, the user has pressed [[Escape]] or
* the dialog has been closed programmatically. Avoid using this unless closing the dialog will result in destructive
* behavior such as data loss.
* @event wa-after-hide - Emitted after the dialog closes and all animations are complete.
*
* @csspart header - The dialog's header. This element wraps the title and header actions.
* @csspart header-actions - Optional actions to add to the header. Works best with `<wa-icon-button>`.
@@ -95,33 +98,31 @@ export default class WaDialog extends WebAwesomeElement {
}
private async requestClose(source: Element) {
const waRequestClose = this.emit('wa-request-close', {
cancelable: true,
detail: { source }
});
// Hide
const waHideEvent = new WaHideEvent({ source });
this.dispatchEvent(waHideEvent);
if (waRequestClose.defaultPrevented) {
if (waHideEvent.defaultPrevented) {
this.open = true;
animateWithClass(this.dialog, 'pulse');
} else {
// Hide
this.emit('wa-hide');
this.removeOpenListeners();
await animateWithClass(this.dialog, 'hide');
this.open = false;
this.dialog.close();
unlockBodyScrolling(this);
// Restore focus to the original trigger
const trigger = this.originalTrigger;
if (typeof trigger?.focus === 'function') {
setTimeout(() => trigger.focus());
}
this.emit('wa-after-hide');
return;
}
this.removeOpenListeners();
await animateWithClass(this.dialog, 'hide');
this.open = false;
this.dialog.close();
unlockBodyScrolling(this);
// Restore focus to the original trigger
const trigger = this.originalTrigger;
if (typeof trigger?.focus === 'function') {
setTimeout(() => trigger.focus());
}
this.dispatchEvent(new WaAfterHideEvent());
}
private addOpenListeners() {
@@ -193,7 +194,13 @@ export default class WaDialog extends WebAwesomeElement {
/** Shows the dialog. */
private async show() {
// Show
this.emit('wa-show');
const waShowEvent = new WaShowEvent();
this.dispatchEvent(waShowEvent);
if (waShowEvent.defaultPrevented) {
this.open = false;
return;
}
this.addOpenListeners();
this.originalTrigger = document.activeElement as HTMLElement;
this.open = true;
@@ -211,7 +218,7 @@ export default class WaDialog extends WebAwesomeElement {
await animateWithClass(this.dialog, 'show');
this.emit('wa-after-show');
this.dispatchEvent(new WaAfterShowEvent());
}
render() {

View File

@@ -98,12 +98,12 @@ describe('<wa-drawer>', () => {
expect(getComputedStyle(el).display).to.equal('none');
});
it('should not close when wa-request-close is prevented', async () => {
it('should not close when wa-hide is prevented', async () => {
const el = await fixture<WaDrawer>(html`
<wa-drawer open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-drawer>
`);
el.addEventListener('wa-request-close', event => {
el.addEventListener('wa-hide', event => {
event.preventDefault();
});
await sendKeys({ press: 'Escape' });

View File

@@ -5,6 +5,10 @@ import { customElement, property, query } from 'lit/decorators.js';
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize.js';
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll.js';
import { WaAfterHideEvent } from '../../events/after-hide.js';
import { WaAfterShowEvent } from '../../events/after-show.js';
import { WaHideEvent } from '../../events/hide.js';
import { WaShowEvent } from '../../events/show.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './drawer.styles.js';
@@ -28,10 +32,10 @@ import type { CSSResultGroup } from 'lit';
* @event wa-after-show - Emitted after the drawer opens and all animations are complete.
* @event wa-hide - Emitted when the drawer closes.
* @event wa-after-hide - Emitted after the drawer closes and all animations are complete.
* @event {{ source: Element }} wa-request-close - Emitted when the user attempts to close the drawer. Calling
* `event.preventDefault()` will keep the drawer open. You can inspect `event.detail.source` to see which element
* caused the drawer to close. If the source is the drawer element itself, the user has pressed [[Escape]] or the
* drawer has been closed programmatically. Avoid using this unless closing the drawer will result in destructive
* @event {{ source: Element }} wa-hide - Emitted when the drawer is requesting to close. Calling
* `event.preventDefault()` will prevent the dialog from closing. You can inspect `event.detail.source` to see which
* element caused the dialog to close. If the source is the dialog element itself, the user has pressed [[Escape]] or
* the dialog has been closed programmatically. Avoid using this unless closing the dialog will result in destructive
* behavior such as data loss.
*
* @csspart header - The drawer's header. This element wraps the title and header actions.
@@ -103,33 +107,31 @@ export default class WaDrawer extends WebAwesomeElement {
}
private async requestClose(source: Element) {
const waRequestClose = this.emit('wa-request-close', {
cancelable: true,
detail: { source }
});
// Hide
const waHideEvent = new WaHideEvent({ source });
this.dispatchEvent(waHideEvent);
if (waRequestClose.defaultPrevented) {
if (waHideEvent.defaultPrevented) {
this.open = true;
animateWithClass(this.drawer, 'pulse');
} else {
// Hide
this.emit('wa-hide');
this.removeOpenListeners();
await animateWithClass(this.drawer, 'hide');
this.open = false;
this.drawer.close();
unlockBodyScrolling(this);
// Restore focus to the original trigger
const trigger = this.originalTrigger;
if (typeof trigger?.focus === 'function') {
setTimeout(() => trigger.focus());
}
this.emit('wa-after-hide');
return;
}
this.removeOpenListeners();
await animateWithClass(this.drawer, 'hide');
this.open = false;
this.drawer.close();
unlockBodyScrolling(this);
// Restore focus to the original trigger
const trigger = this.originalTrigger;
if (typeof trigger?.focus === 'function') {
setTimeout(() => trigger.focus());
}
this.dispatchEvent(new WaAfterHideEvent());
}
private addOpenListeners() {
@@ -201,7 +203,14 @@ export default class WaDrawer extends WebAwesomeElement {
/** Shows the drawer. */
private async show() {
// Show
this.emit('wa-show');
const waShowEvent = new WaShowEvent();
this.dispatchEvent(waShowEvent);
if (waShowEvent.defaultPrevented) {
this.open = false;
return;
}
// Show
this.addOpenListeners();
this.originalTrigger = document.activeElement as HTMLElement;
this.open = true;
@@ -219,7 +228,7 @@ export default class WaDrawer extends WebAwesomeElement {
await animateWithClass(this.drawer, 'show');
this.emit('wa-after-show');
this.dispatchEvent(new WaAfterShowEvent());
}
render() {

View File

@@ -4,13 +4,17 @@ import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query } from 'lit/decorators.js';
import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { WaAfterHideEvent } from '../../events/after-hide.js';
import { WaAfterShowEvent } from '../../events/after-show.js';
import { WaHideEvent } from '../../events/hide.js';
import { waitForEvent } from '../../internal/event.js';
import { WaShowEvent } from '../../events/show.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './dropdown.styles.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import type { CSSResultGroup } from 'lit';
import type { WaSelectEvent } from '../../events/wa-select.js';
import type { WaSelectEvent } from '../../events/select.js';
import type WaButton from '../button/button.js';
import type WaIconButton from '../icon-button/icon-button.js';
import type WaMenu from '../menu/menu.js';
@@ -361,24 +365,32 @@ export default class WaDropdown extends WebAwesomeElement {
if (this.open) {
// Show
this.emit('wa-show');
this.addOpenListeners();
const waShowEvent = new WaShowEvent();
this.dispatchEvent(waShowEvent);
if (waShowEvent.defaultPrevented) {
this.open = false;
return;
}
this.addOpenListeners();
this.panel.hidden = false;
this.popup.active = true;
await animateWithClass(this.popup.popup, 'show-with-scale');
this.emit('wa-after-show');
this.dispatchEvent(new WaAfterShowEvent());
} else {
// Hide
this.emit('wa-hide');
this.removeOpenListeners();
const waHideEvent = new WaHideEvent();
this.dispatchEvent(waHideEvent);
if (waHideEvent.defaultPrevented) {
this.open = true;
return;
}
this.removeOpenListeners();
await animateWithClass(this.popup.popup, 'hide-with-scale');
this.panel.hidden = true;
this.popup.active = false;
this.emit('wa-after-hide');
this.dispatchEvent(new WaAfterHideEvent());
}
}

View File

@@ -3,6 +3,8 @@ import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query, state } from 'lit/decorators.js';
import { html, literal } from 'lit/static-html.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { WaBlurEvent } from '../../events/blur.js';
import { WaFocusEvent } from '../../events/focus.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './icon-button.styles.js';
@@ -73,12 +75,12 @@ export default class WaIconButton extends WebAwesomeFormAssociatedElement {
private handleBlur() {
this.hasFocus = false;
this.emit('wa-blur');
this.dispatchEvent(new WaBlurEvent());
}
private handleFocus() {
this.hasFocus = true;
this.emit('wa-focus');
this.dispatchEvent(new WaFocusEvent());
}
private handleClick(event: MouseEvent) {

View File

@@ -1,7 +1,7 @@
import { aTimeout, elementUpdated, expect, fixture, html, oneEvent } from '@open-wc/testing';
import { registerIconLibrary } from '../../../dist/webawesome.js';
import type { WaErrorEvent } from '../../events/wa-error.js';
import type { WaLoadEvent } from '../../events/wa-load.js';
import type { WaErrorEvent } from '../../events/error.js';
import type { WaLoadEvent } from '../../events/load.js';
import type WaIcon from './icon.js';
const testLibraryIcons = {

View File

@@ -2,6 +2,8 @@ import { customElement, property, state } from 'lit/decorators.js';
import { getIconLibrary, type IconLibrary, unwatchIcon, watchIcon } from './library.js';
import { html } from 'lit';
import { isTemplateResult } from 'lit/directive-helpers.js';
import { WaErrorEvent } from '../../events/error.js';
import { WaLoadEvent } from '../../events/load.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './icon.styles.js';
@@ -206,12 +208,12 @@ export default class WaIcon extends WebAwesomeElement {
case RETRYABLE_ERROR:
case CACHEABLE_ERROR:
this.svg = null;
this.emit('wa-error');
this.dispatchEvent(new WaErrorEvent());
break;
default:
this.svg = svg.cloneNode(true) as SVGElement;
library?.mutator?.(this.svg);
this.emit('wa-load');
this.dispatchEvent(new WaLoadEvent());
}
}

View File

@@ -5,6 +5,7 @@ import { customElement, property, query } from 'lit/decorators.js';
import { drag } from '../../internal/drag.js';
import { html } from 'lit';
import { styleMap } from 'lit/directives/style-map.js';
import { WaChangeEvent } from '../../events/change.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './image-comparer.styles.js';
@@ -89,7 +90,7 @@ export default class WaImageComparer extends WebAwesomeElement {
@watch('position', { waitUntilFirstUpdate: true })
handlePositionChange() {
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
}
render() {

View File

@@ -48,7 +48,7 @@ describe('<wa-include>', () => {
expect(loadHandler).to.have.been.calledOnce;
});
it('should emit wa-error when content cannot be loaded', async () => {
it('should emit wa-include-error when content cannot be loaded', async () => {
sinon.stub(window, 'fetch').resolves({
...stubbedFetchResponse,
ok: false,
@@ -58,7 +58,7 @@ describe('<wa-include>', () => {
const el = await fixture<WaInclude>(html` <wa-include src="/not-found"></wa-include> `);
const loadHandler = sinon.spy();
el.addEventListener('wa-error', loadHandler);
el.addEventListener('wa-include-error', loadHandler);
await waitUntil(() => loadHandler.calledOnce);
expect(loadHandler).to.have.been.calledOnce;

View File

@@ -1,6 +1,8 @@
import { customElement, property } from 'lit/decorators.js';
import { html } from 'lit';
import { requestInclude } from './request.js';
import { WaIncludeErrorEvent } from '../../events/include-error.js';
import { WaLoadEvent } from '../../events/load.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './include.styles.js';
@@ -55,7 +57,7 @@ export default class WaInclude extends WebAwesomeElement {
}
if (!file.ok) {
this.emit('wa-error', { detail: { status: file.status } });
this.dispatchEvent(new WaIncludeErrorEvent({ status: file.status }));
return;
}
@@ -65,9 +67,9 @@ export default class WaInclude extends WebAwesomeElement {
[...this.querySelectorAll('script')].forEach(script => this.executeScript(script));
}
this.emit('wa-load');
this.dispatchEvent(new WaLoadEvent());
} catch {
this.emit('wa-error', { detail: { status: -1 } });
this.dispatchEvent(new WaIncludeErrorEvent({ status: -1 }));
}
}

View File

@@ -7,6 +7,11 @@ import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import { LocalizeController } from '../../utilities/localize.js';
import { MirrorValidator } from '../../internal/validators/mirror-validator.js';
import { WaBlurEvent } from '../../events/blur.js';
import { WaChangeEvent } from '../../events/change.js';
import { WaClearEvent } from '../../events/clear.js';
import { WaFocusEvent } from '../../events/focus.js';
import { WaInputEvent } from '../../events/input.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
@@ -204,12 +209,12 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
private handleBlur() {
this.hasFocus = false;
this.emit('wa-blur');
this.dispatchEvent(new WaBlurEvent());
}
private handleChange() {
this.value = this.input.value;
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
}
private handleClearClick(event: MouseEvent) {
@@ -217,9 +222,9 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
if (this.value !== '') {
this.value = '';
this.emit('wa-clear');
this.emit('wa-input');
this.emit('wa-change');
this.dispatchEvent(new WaClearEvent());
this.dispatchEvent(new WaInputEvent());
this.dispatchEvent(new WaChangeEvent());
}
this.input.focus();
@@ -227,12 +232,12 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
private handleFocus() {
this.hasFocus = true;
this.emit('wa-focus');
this.dispatchEvent(new WaFocusEvent());
}
private handleInput() {
this.value = this.input.value;
this.emit('wa-input');
this.dispatchEvent(new WaInputEvent());
}
private handleKeyDown(event: KeyboardEvent) {

View File

@@ -104,9 +104,10 @@ export default css`
}
:host(:focus-visible) .menu-item {
outline: none;
background-color: var(--wa-color-brand-spot);
color: var(--wa-color-brand-text-on-spot);
outline: var(--wa-focus-ring);
outline-offset: calc(-1 * var(--wa-focus-ring-width));
background-color: var(--wa-color-neutral-fill-subtle);
color: var(--wa-color-neutral-text-on-fill);
opacity: 1;
}
@@ -145,7 +146,7 @@ export default css`
}
}
::slotted(sl-menu) {
::slotted(wa-menu) {
max-width: var(--auto-size-available-width) !important;
max-height: var(--auto-size-available-height) !important;
}

View File

@@ -1,7 +1,7 @@
import { expect, fixture, html, waitUntil } from '@open-wc/testing';
import { sendKeys } from '@web/test-runner-commands';
import sinon from 'sinon';
import type { WaSelectEvent } from '../../events/wa-select.js';
import type { WaSelectEvent } from '../../events/select.js';
import type WaMenuItem from './menu-item.js';
describe('<wa-menu-item>', () => {

View File

@@ -87,7 +87,8 @@ export default class WaMenuItem extends WebAwesomeElement {
// When the label changes, emit a slotchange event so parent controls see it
if (textLabel !== this.cachedTextLabel) {
this.cachedTextLabel = textLabel;
this.emit('slotchange', { bubbles: true, composed: false, cancelable: false });
/** @internal - prevent the CEM from recording this event */
this.dispatchEvent(new Event('slotchange', { bubbles: true, composed: false, cancelable: false }));
}
}

View File

@@ -3,7 +3,7 @@ import { expect, fixture } from '@open-wc/testing';
import { html } from 'lit';
import { sendKeys } from '@web/test-runner-commands';
import sinon from 'sinon';
import type { WaSelectEvent } from '../../events/wa-select.js';
import type { WaSelectEvent } from '../../events/select.js';
import type WaMenu from './menu.js';
describe('<wa-menu>', () => {

View File

@@ -1,6 +1,7 @@
import '../menu-item/menu-item.js';
import { customElement, query } from 'lit/decorators.js';
import { html } from 'lit';
import { WaSelectEvent } from '../../events/select.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './menu.styles.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
@@ -48,7 +49,7 @@ export default class WaMenu extends WebAwesomeElement {
item.checked = !item.checked;
}
this.emit('wa-select', { detail: { item } });
this.dispatchEvent(new WaSelectEvent({ item }));
}
private handleKeyDown(event: KeyboardEvent) {

View File

@@ -1,5 +1,6 @@
import { customElement, property } from 'lit/decorators.js';
import { html } from 'lit';
import { WaMutationEvent } from '../../events/mutation.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './mutation-observer.styles.js';
@@ -59,9 +60,7 @@ export default class WaMutationObserver extends WebAwesomeElement {
}
private handleMutation = (mutationList: MutationRecord[]) => {
this.emit('wa-mutation', {
detail: { mutationList }
});
this.dispatchEvent(new WaMutationEvent({ mutationList }));
};
private startObserver() {

View File

@@ -69,7 +69,8 @@ export default class WaOption extends WebAwesomeElement {
// When the label changes, emit a slotchange event so parent controls see it
if (textLabel !== this.cachedTextLabel) {
this.cachedTextLabel = textLabel;
this.emit('slotchange', { bubbles: true, composed: false, cancelable: false });
/** @internal - prevent the CEM from recording this event */
this.dispatchEvent(new Event('slotchange', { bubbles: true, composed: false, cancelable: false }));
}
}

View File

@@ -48,7 +48,7 @@ export default css`
.popup-hover-bridge {
position: fixed;
z-index: calc(var(--sl-z-index-dropdown) - 1);
z-index: calc(var(--wa-z-index-dropdown) - 1);
top: 0;
right: 0;
bottom: 0;

View File

@@ -3,6 +3,7 @@ import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query } from 'lit/decorators.js';
import { html } from 'lit';
import { offsetParent } from 'composed-offset-position';
import { WaRepositionEvent } from '../../events/reposition.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './popup.styles.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
@@ -470,7 +471,7 @@ export default class WaPopup extends WebAwesomeElement {
// Wait until the new position is drawn before updating the hover bridge, otherwise it can get out of sync
requestAnimationFrame(() => this.updateHoverBridge());
this.emit('wa-reposition');
this.dispatchEvent(new WaRepositionEvent());
}
private updateHoverBridge = () => {

View File

@@ -3,6 +3,8 @@ import { customElement, property, query, state } from 'lit/decorators.js';
import { HasSlotController } from '../../internal/slot.js';
import { html } from 'lit/static-html.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { WaBlurEvent } from '../../events/blur.js';
import { WaFocusEvent } from '../../events/focus.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
@@ -76,7 +78,7 @@ export default class WaRadioButton extends WebAwesomeFormAssociatedElement {
private handleBlur() {
this.hasFocus = false;
this.emit('wa-blur');
this.dispatchEvent(new WaBlurEvent());
}
private handleClick(e: MouseEvent) {
@@ -91,7 +93,7 @@ export default class WaRadioButton extends WebAwesomeFormAssociatedElement {
private handleFocus() {
this.hasFocus = true;
this.emit('wa-focus');
this.dispatchEvent(new WaFocusEvent());
}
@watch('disabled', { waitUntilFirstUpdate: true })

View File

@@ -3,7 +3,7 @@ import { clickOnElement } from '../../internal/test.js';
import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests.js';
import { sendKeys } from '@web/test-runner-commands';
import sinon from 'sinon';
import type { WaChangeEvent } from '../../events/wa-change.js';
import type { WaChangeEvent } from '../../events/change.js';
import type WaRadio from '../radio/radio.js';
import type WaRadioGroup from './radio-group.js';

View File

@@ -6,6 +6,8 @@ import { HasSlotController } from '../../internal/slot.js';
import { html } from 'lit';
import { RequiredValidator } from '../../internal/validators/required-validator.js';
import { uniqueId } from '../../internal/math.js';
import { WaChangeEvent } from '../../events/change.js';
import { WaInputEvent } from '../../events/input.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
@@ -123,8 +125,8 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
};
@@ -259,8 +261,8 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
event.preventDefault();

View File

@@ -2,6 +2,8 @@ import '../icon/icon.js';
import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, state } from 'lit/decorators.js';
import { html } from 'lit';
import { WaBlurEvent } from '../../events/blur.js';
import { WaFocusEvent } from '../../events/focus.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
@@ -76,12 +78,12 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
private handleBlur = () => {
this.hasFocus = false;
this.emit('wa-blur');
this.dispatchEvent(new WaBlurEvent());
};
private handleFocus = () => {
this.hasFocus = true;
this.emit('wa-focus');
this.dispatchEvent(new WaFocusEvent());
};
private setInitialAttributes() {

View File

@@ -6,6 +6,10 @@ import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import { LocalizeController } from '../../utilities/localize.js';
import { MirrorValidator } from '../../internal/validators/mirror-validator.js';
import { WaBlurEvent } from '../../events/blur.js';
import { WaChangeEvent } from '../../events/change.js';
import { WaFocusEvent } from '../../events/focus.js';
import { WaInputEvent } from '../../events/input.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
@@ -128,25 +132,25 @@ export default class WaRange extends WebAwesomeFormAssociatedElement {
}
private handleChange() {
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
}
private handleInput() {
this.value = parseFloat(this.input.value);
this.emit('wa-input');
this.dispatchEvent(new WaInputEvent());
this.syncRange();
}
private handleBlur() {
this.hasFocus = false;
this.hasTooltip = false;
this.emit('wa-blur');
this.dispatchEvent(new WaBlurEvent());
}
private handleFocus() {
this.hasFocus = true;
this.hasTooltip = true;
this.emit('wa-focus');
this.dispatchEvent(new WaFocusEvent());
}
@eventOptions({ passive: true })

View File

@@ -5,6 +5,8 @@ import { customElement, eventOptions, property, query, state } from 'lit/decorat
import { html } from 'lit';
import { styleMap } from 'lit/directives/style-map.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { WaChangeEvent } from '../../events/change.js';
import { WaHoverEvent } from '../../events/hover.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './rating.styles.js';
@@ -93,7 +95,7 @@ export default class WaRating extends WebAwesomeElement {
}
this.setValue(this.getValueFromMousePosition(event));
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
}
private setValue(newValue: number) {
@@ -137,7 +139,7 @@ export default class WaRating extends WebAwesomeElement {
}
if (this.value !== oldValue) {
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
}
}
@@ -170,7 +172,7 @@ export default class WaRating extends WebAwesomeElement {
private handleTouchEnd(event: TouchEvent) {
this.isHovering = false;
this.setValue(this.hoverValue);
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
// Prevent click on mobile devices
event.preventDefault();
@@ -183,22 +185,22 @@ export default class WaRating extends WebAwesomeElement {
@watch('hoverValue')
handleHoverValueChange() {
this.emit('wa-hover', {
detail: {
this.dispatchEvent(
new WaHoverEvent({
phase: 'move',
value: this.hoverValue
}
});
})
);
}
@watch('isHovering')
handleIsHoveringChange() {
this.emit('wa-hover', {
detail: {
this.dispatchEvent(
new WaHoverEvent({
phase: this.isHovering ? 'start' : 'end',
value: this.hoverValue
}
});
})
);
}
/** Sets focus on the rating. */

View File

@@ -1,5 +1,6 @@
import { customElement, property } from 'lit/decorators.js';
import { html } from 'lit';
import { WaResizeEvent } from '../../events/resize.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './resize-observer.styles.js';
@@ -29,7 +30,7 @@ export default class WaResizeObserver extends WebAwesomeElement {
connectedCallback() {
super.connectedCallback();
this.resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {
this.emit('wa-resize', { detail: { entries } });
this.dispatchEvent(new WaResizeEvent({ entries }));
});
if (!this.disabled) {

View File

@@ -85,7 +85,7 @@ export default css`
}
.select__display-input::placeholder {
color: var(--sl-input-placeholder-color);
color: var(--wa-form-controls-placeholder-color);
}
/* Visually hide the display input when multiple is enabled */
@@ -306,6 +306,7 @@ export default css`
}
.select__listbox ::slotted(small) {
display: block;
font-size: var(--wa-font-size-s);
font-weight: var(--wa-font-weight-medium);
color: var(--wa-color-text-quiet);

View File

@@ -10,14 +10,23 @@ import { LocalizeController } from '../../utilities/localize.js';
import { RequiredValidator } from '../../internal/validators/required-validator.js';
import { scrollIntoView } from '../../internal/scroll.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { WaAfterHideEvent } from '../../events/after-hide.js';
import { WaAfterShowEvent } from '../../events/after-show.js';
import { WaBlurEvent } from '../../events/blur.js';
import { WaChangeEvent } from '../../events/change.js';
import { WaClearEvent } from '../../events/clear.js';
import { WaFocusEvent } from '../../events/focus.js';
import { WaHideEvent } from '../../events/hide.js';
import { WaInputEvent } from '../../events/input.js';
import { waitForEvent } from '../../internal/event.js';
import { WaShowEvent } from '../../events/show.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
import formControlStyles from '../../styles/form-control.styles.js';
import styles from './select.styles.js';
import type { CSSResultGroup, TemplateResult } from 'lit';
import type { WaRemoveEvent } from '../../events/wa-remove.js';
import type { WaRemoveEvent } from '../../events/remove.js';
import type WaOption from '../option/option.js';
import type WaPopup from '../popup/popup.js';
@@ -290,12 +299,12 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
private handleFocus() {
this.hasFocus = true;
this.displayInput.setSelectionRange(0, 0);
this.emit('wa-focus');
this.dispatchEvent(new WaFocusEvent());
}
private handleBlur() {
this.hasFocus = false;
this.emit('wa-blur');
this.dispatchEvent(new WaBlurEvent());
}
private handleDocumentFocusIn = (event: KeyboardEvent) => {
@@ -346,8 +355,8 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
// Emit after updating
this.updateComplete.then(() => {
this.emit('wa-input');
this.emit('wa-change');
this.dispatchEvent(new WaInputEvent());
this.dispatchEvent(new WaChangeEvent());
});
if (!this.multiple) {
@@ -475,9 +484,9 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
// Emit after update
this.updateComplete.then(() => {
this.emit('wa-clear');
this.emit('wa-input');
this.emit('wa-change');
this.dispatchEvent(new WaClearEvent());
this.dispatchEvent(new WaInputEvent());
this.dispatchEvent(new WaChangeEvent());
});
}
}
@@ -506,8 +515,8 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
if (this.value !== oldValue) {
// Emit after updating
this.updateComplete.then(() => {
this.emit('wa-input');
this.emit('wa-change');
this.dispatchEvent(new WaInputEvent());
this.dispatchEvent(new WaChangeEvent());
});
}
@@ -543,8 +552,8 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
// Emit after updating
this.updateComplete.then(() => {
this.emit('wa-input');
this.emit('wa-change');
this.dispatchEvent(new WaInputEvent());
this.dispatchEvent(new WaChangeEvent());
});
}
}
@@ -675,9 +684,14 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
this.setCurrentOption(this.selectedOptions[0] || this.getFirstOption());
// Show
this.emit('wa-show');
this.addOpenListeners();
const waShowEvent = new WaShowEvent();
this.dispatchEvent(waShowEvent);
if (waShowEvent.defaultPrevented) {
this.open = false;
return;
}
this.addOpenListeners();
this.listbox.hidden = false;
this.popup.active = true;
@@ -693,17 +707,22 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
scrollIntoView(this.currentOption, this.listbox, 'vertical', 'auto');
}
this.emit('wa-after-show');
this.dispatchEvent(new WaAfterShowEvent());
} else {
// Hide
this.emit('wa-hide');
this.removeOpenListeners();
const waHideEvent = new WaHideEvent();
this.dispatchEvent(waHideEvent);
if (waHideEvent.defaultPrevented) {
this.open = false;
return;
}
this.removeOpenListeners();
await animateWithClass(this.popup.popup, 'hide');
this.listbox.hidden = true;
this.popup.active = false;
this.emit('wa-after-hide');
this.dispatchEvent(new WaAfterHideEvent());
}
}

View File

@@ -4,6 +4,7 @@ import { drag } from '../../internal/drag.js';
import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { LocalizeController } from '../../utilities/localize.js';
import { WaRepositionEvent } from '../../events/reposition.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './split-panel.styles.js';
@@ -208,7 +209,7 @@ export default class WaSplitPanel extends WebAwesomeElement {
handlePositionChange() {
this.cachedPositionInPixels = this.percentageToPixels(this.position);
this.positionInPixels = this.percentageToPixels(this.position);
this.emit('wa-reposition');
this.dispatchEvent(new WaRepositionEvent());
}
@watch('positionInPixels')

View File

@@ -183,11 +183,11 @@ describe('<wa-switch>', async () => {
</form>
`);
const button = form.querySelector('wa-button')!;
const slSwitch = form.querySelector('wa-switch')!;
const waSwitch = form.querySelector('wa-switch')!;
const submitHandler = sinon.spy((event: SubmitEvent) => event.preventDefault());
// Submitting the form after setting custom validity should not trigger the handler
slSwitch.setCustomValidity('Invalid selection');
waSwitch.setCustomValidity('Invalid selection');
form.addEventListener('submit', submitHandler);
button.click();
await aTimeout(100);
@@ -196,13 +196,13 @@ describe('<wa-switch>', async () => {
});
it('should be invalid when required and unchecked', async () => {
const slSwitch = await fixture<HTMLFormElement>(html` <wa-switch required></wa-switch> `);
expect(slSwitch.checkValidity()).to.be.false;
const waSwitch = await fixture<HTMLFormElement>(html` <wa-switch required></wa-switch> `);
expect(waSwitch.checkValidity()).to.be.false;
});
it('should be valid when required and checked', async () => {
const slSwitch = await fixture<HTMLFormElement>(html` <wa-switch required checked></wa-switch> `);
expect(slSwitch.checkValidity()).to.be.true;
const waSwitch = await fixture<HTMLFormElement>(html` <wa-switch required checked></wa-switch> `);
expect(waSwitch.checkValidity()).to.be.true;
});
it('should be present in form data when using the form attribute and located outside of a <form>', async () => {
@@ -222,14 +222,14 @@ describe('<wa-switch>', async () => {
it('should receive validation attributes ("states") even when novalidate is used on the parent form', async () => {
const el = await fixture<HTMLFormElement>(html` <form novalidate><wa-switch required></wa-switch></form> `);
const slSwitch = el.querySelector<WaSwitch>('wa-switch')!;
const waSwitch = el.querySelector<WaSwitch>('wa-switch')!;
expect(slSwitch.hasAttribute('data-wa-required')).to.be.true;
expect(slSwitch.hasAttribute('data-wa-optional')).to.be.false;
expect(slSwitch.hasAttribute('data-wa-invalid')).to.be.true;
expect(slSwitch.hasAttribute('data-wa-valid')).to.be.false;
expect(slSwitch.hasAttribute('data-wa-user-invalid')).to.be.false;
expect(slSwitch.hasAttribute('data-wa-user-valid')).to.be.false;
expect(waSwitch.hasAttribute('data-wa-required')).to.be.true;
expect(waSwitch.hasAttribute('data-wa-optional')).to.be.false;
expect(waSwitch.hasAttribute('data-wa-invalid')).to.be.true;
expect(waSwitch.hasAttribute('data-wa-valid')).to.be.false;
expect(waSwitch.hasAttribute('data-wa-user-invalid')).to.be.false;
expect(waSwitch.hasAttribute('data-wa-user-valid')).to.be.false;
});
});

View File

@@ -5,6 +5,10 @@ import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import { MirrorValidator } from '../../internal/validators/mirror-validator.js';
import { WaBlurEvent } from '../../events/blur.js';
import { WaChangeEvent } from '../../events/change.js';
import { WaFocusEvent } from '../../events/focus.js';
import { WaInputEvent } from '../../events/input.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
@@ -101,36 +105,36 @@ export default class WaSwitch extends WebAwesomeFormAssociatedElement {
private handleBlur() {
this.hasFocus = false;
this.emit('wa-blur');
this.dispatchEvent(new WaBlurEvent());
}
private handleInput() {
this.emit('wa-input');
this.dispatchEvent(new WaInputEvent());
}
private handleClick() {
this.checked = !this.checked;
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
}
private handleFocus() {
this.hasFocus = true;
this.emit('wa-focus');
this.dispatchEvent(new WaFocusEvent());
}
private handleKeyDown(event: KeyboardEvent) {
if (event.key === 'ArrowLeft') {
event.preventDefault();
this.checked = false;
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
if (event.key === 'ArrowRight') {
event.preventDefault();
this.checked = true;
this.emit('wa-change');
this.emit('wa-input');
this.dispatchEvent(new WaChangeEvent());
this.dispatchEvent(new WaInputEvent());
}
}

View File

@@ -6,7 +6,7 @@ import { queryByTestId } from '../../internal/test/data-testid-helpers.js';
import { sendKeys } from '@web/test-runner-commands';
import { waitForScrollingToEnd } from '../../internal/test/wait-for-scrolling.js';
import type { HTMLTemplateResult } from 'lit';
import type { WaTabShowEvent } from '../../events/wa-tab-show.js';
import type { WaTabShowEvent } from '../../events/tab-show.js';
import type WaTab from '../tab/tab.js';
import type WaTabGroup from './tab-group.js';
import type WaTabPanel from '../tab-panel/tab-panel.js';

View File

@@ -6,6 +6,8 @@ import { customElement, property, query, state } from 'lit/decorators.js';
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize.js';
import { scrollIntoView } from '../../internal/scroll.js';
import { WaTabHideEvent } from '../../events/tab-hide.js';
import { WaTabShowEvent } from '../../events/tab-show.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './tab-group.styles.js';
@@ -270,10 +272,10 @@ export default class WaTabGroup extends WebAwesomeElement {
// Emit events
if (options.emitEvents) {
if (previousTab) {
this.emit('wa-tab-hide', { detail: { name: previousTab.panel } });
this.dispatchEvent(new WaTabHideEvent({ name: previousTab.panel }));
}
this.emit('wa-tab-show', { detail: { name: this.activeTab.panel } });
this.dispatchEvent(new WaTabShowEvent({ name: this.activeTab.panel }));
}
}
}

View File

@@ -3,6 +3,7 @@ import { classMap } from 'lit/directives/class-map.js';
import { customElement, property } from 'lit/decorators.js';
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize.js';
import { WaRemoveEvent } from '../../events/remove.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './tag.styles.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
@@ -51,7 +52,7 @@ export default class WaTag extends WebAwesomeElement {
@property({ type: Boolean }) removable = false;
private handleRemoveClick() {
this.emit('wa-remove');
this.dispatchEvent(new WaRemoveEvent());
}
render() {

View File

@@ -5,6 +5,10 @@ import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import { MirrorValidator } from '../../internal/validators/mirror-validator.js';
import { WaBlurEvent } from '../../events/blur.js';
import { WaChangeEvent } from '../../events/change.js';
import { WaFocusEvent } from '../../events/focus.js';
import { WaInputEvent } from '../../events/input.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import componentStyles from '../../styles/component.styles.js';
@@ -165,25 +169,25 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
private handleBlur() {
this.hasFocus = false;
this.emit('wa-blur');
this.dispatchEvent(new WaBlurEvent());
this.checkValidity();
}
private handleChange() {
this.value = this.input.value;
this.setTextareaHeight();
this.emit('wa-change');
this.dispatchEvent(new WaChangeEvent());
this.checkValidity();
}
private handleFocus() {
this.hasFocus = true;
this.emit('wa-focus');
this.dispatchEvent(new WaFocusEvent());
}
private handleInput() {
this.value = this.input.value;
this.emit('wa-input');
this.dispatchEvent(new WaInputEvent());
}
private setTextareaHeight() {

View File

@@ -2,7 +2,11 @@ import { animateWithClass, stopAnimations } from '../../internal/animate.js';
import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query } from 'lit/decorators.js';
import { html } from 'lit';
import { WaAfterHideEvent } from '../../events/after-hide.js';
import { WaAfterShowEvent } from '../../events/after-show.js';
import { WaHideEvent } from '../../events/hide.js';
import { waitForEvent } from '../../internal/event.js';
import { WaShowEvent } from '../../events/show.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './tooltip.styles.js';
@@ -182,7 +186,13 @@ export default class WaTooltip extends WebAwesomeElement {
}
// Show
this.emit('wa-show');
const waShowEvent = new WaShowEvent();
this.dispatchEvent(waShowEvent);
if (waShowEvent.defaultPrevented) {
this.open = false;
return;
}
if ('CloseWatcher' in window) {
this.closeWatcher?.destroy();
this.closeWatcher = new CloseWatcher();
@@ -199,10 +209,16 @@ export default class WaTooltip extends WebAwesomeElement {
await animateWithClass(this.popup.popup, 'show-with-scale');
this.popup.reposition();
this.emit('wa-after-show');
this.dispatchEvent(new WaAfterShowEvent());
} else {
// Hide
this.emit('wa-hide');
const waHideEvent = new WaHideEvent();
this.dispatchEvent(waHideEvent);
if (waHideEvent.defaultPrevented) {
this.open = false;
return;
}
this.closeWatcher?.destroy();
document.removeEventListener('keydown', this.handleDocumentKeyDown);
@@ -211,7 +227,7 @@ export default class WaTooltip extends WebAwesomeElement {
this.popup.active = false;
this.body.hidden = true;
this.emit('wa-after-hide');
this.dispatchEvent(new WaAfterHideEvent());
}
}

View File

@@ -1,12 +1,18 @@
import '../checkbox/checkbox.js';
import '../icon/icon.js';
import '../spinner/spinner.js';
import { animate, parseDuration, stopAnimations } from '../../internal/animate.js';
import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query, state } from 'lit/decorators.js';
import { html } from 'lit';
import { live } from 'lit/directives/live.js';
import { LocalizeController } from '../../utilities/localize.js';
import { parseDuration, stopAnimations } from '../../internal/animate.js';
import { WaAfterCollapseEvent } from '../../events/after-collapse.js';
import { WaAfterExpandEvent } from '../../events/after-expand.js';
import { WaCollapseEvent } from '../../events/collapse.js';
import { WaExpandEvent } from '../../events/expand.js';
import { WaLazyChangeEvent } from '../../events/lazy-change.js';
import { WaLazyLoadEvent } from '../../events/lazy-load.js';
import { watch } from '../../internal/watch.js';
import { when } from 'lit/directives/when.js';
import componentStyles from '../../styles/component.styles.js';
@@ -114,21 +120,23 @@ export default class WaTreeItem extends WebAwesomeElement {
}
private async animateCollapse() {
this.emit('wa-collapse');
this.dispatchEvent(new WaCollapseEvent());
await stopAnimations(this.childrenContainer);
// We can't animate from 'auto', so use the scroll height for now
const duration = parseDuration(getComputedStyle(this.childrenContainer).getPropertyValue('--hide-duration'));
await this.childrenContainer.animate(
await animate(
this.childrenContainer,
[
// We can't animate from 'auto', so use the scroll height for now
{ height: `${this.childrenContainer.scrollHeight}px`, opacity: '1', overflow: 'hidden' },
{ height: '0', opacity: '0', overflow: 'hidden' }
],
{ duration, easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)' }
).finished;
);
this.childrenContainer.hidden = true;
this.emit('wa-after-collapse');
this.dispatchEvent(new WaAfterCollapseEvent());
}
// Checks whether the item is nested into an item
@@ -149,13 +157,14 @@ export default class WaTreeItem extends WebAwesomeElement {
}
private async animateExpand() {
this.emit('wa-expand');
this.dispatchEvent(new WaExpandEvent());
await stopAnimations(this.childrenContainer);
this.childrenContainer.hidden = false;
// We can't animate to 'auto', so use the scroll height for now
const duration = parseDuration(getComputedStyle(this.childrenContainer).getPropertyValue('--show-duration'));
await this.childrenContainer.animate(
await animate(
this.childrenContainer,
[
{ height: '0', opacity: '0', overflow: 'hidden' },
{ height: `${this.childrenContainer.scrollHeight}px`, opacity: '1', overflow: 'hidden' }
@@ -164,10 +173,10 @@ export default class WaTreeItem extends WebAwesomeElement {
duration,
easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)'
}
).finished;
);
this.childrenContainer.style.height = 'auto';
this.emit('wa-after-expand');
this.dispatchEvent(new WaAfterExpandEvent());
}
@watch('loading', { waitUntilFirstUpdate: true })
@@ -203,8 +212,7 @@ export default class WaTreeItem extends WebAwesomeElement {
if (this.expanded) {
if (this.lazy) {
this.loading = true;
this.emit('wa-lazy-load');
this.dispatchEvent(new WaLazyLoadEvent());
} else {
this.animateExpand();
}
@@ -215,7 +223,7 @@ export default class WaTreeItem extends WebAwesomeElement {
@watch('lazy', { waitUntilFirstUpdate: true })
handleLazyChange() {
this.emit('wa-lazy-change');
this.dispatchEvent(new WaLazyChangeEvent());
}
/** Gets all the nested tree items in this node. */

View File

@@ -758,7 +758,7 @@ describe('<wa-tree>', () => {
<wa-tree>
<wa-tree-item>
Item 1
<sl-icon name="1-circle" slot="expand-icon"></sl-icon>
<wa-icon name="1-circle" slot="expand-icon"></wa-icon>
<wa-tree-item> Item A </wa-tree-item>
</wa-tree-item>
<wa-tree-item>

View File

@@ -1,6 +1,7 @@
import { clamp } from '../../internal/math.js';
import { customElement, property, query } from 'lit/decorators.js';
import { html } from 'lit';
import { WaSelectionChangeEvent } from '../../events/selection-change.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './tree.styles.js';
@@ -199,7 +200,7 @@ export default class WaTree extends WebAwesomeElement {
) {
// Wait for the tree items' DOM to update before emitting
Promise.all(nextSelection.map(el => el.updateComplete)).then(() => {
this.emit('wa-selection-change', { detail: { selection: nextSelection } });
this.dispatchEvent(new WaSelectionChangeEvent({ selection: nextSelection }));
});
}
}

View File

@@ -0,0 +1,11 @@
export class WaAfterCollapseEvent extends Event {
constructor() {
super('wa-after-collapse', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-after-collapse': WaAfterCollapseEvent;
}
}

View File

@@ -0,0 +1,11 @@
export class WaAfterExpandEvent extends Event {
constructor() {
super('wa-after-expand', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-after-expand': WaAfterExpandEvent;
}
}

11
src/events/after-hide.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaAfterHideEvent extends Event {
constructor() {
super('wa-after-hide', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-after-hide': WaAfterHideEvent;
}
}

11
src/events/after-show.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaAfterShowEvent extends Event {
constructor() {
super('wa-after-show', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-after-show': WaAfterShowEvent;
}
}

11
src/events/blur.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaBlurEvent extends Event {
constructor() {
super('wa-blur', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-blur': WaBlurEvent;
}
}

11
src/events/cancel.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaCancelEvent extends Event {
constructor() {
super('wa-cancel', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-cancel': WaCancelEvent;
}
}

11
src/events/change.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaChangeEvent extends Event {
constructor() {
super('wa-change', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-change': WaChangeEvent;
}
}

11
src/events/clear.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaClearEvent extends Event {
constructor() {
super('wa-clear', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-clear': WaClearEvent;
}
}

11
src/events/close.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaCloseEvent extends Event {
constructor() {
super('wa-close', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-close': WaCloseEvent;
}
}

11
src/events/collapse.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaCollapseEvent extends Event {
constructor() {
super('wa-collapse', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-collapse': WaCollapseEvent;
}
}

18
src/events/copy.ts Normal file
View File

@@ -0,0 +1,18 @@
export class WaCopyEvent extends Event {
readonly detail: WaCopyErrorEventDetail;
constructor(detail: WaCopyErrorEventDetail) {
super('wa-copy', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
interface WaCopyErrorEventDetail {
/** The value that occurred while copying. */
value: string;
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-copy': WaCopyEvent;
}
}

11
src/events/error.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaErrorEvent extends Event {
constructor() {
super('wa-error', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-error': WaErrorEvent;
}
}

View File

@@ -1,34 +1,33 @@
export type { WaAfterCollapseEvent } from './wa-after-collapse.js';
export type { WaAfterExpandEvent } from './wa-after-expand.js';
export type { WaAfterHideEvent } from './wa-after-hide.js';
export type { WaAfterShowEvent } from './wa-after-show.js';
export type { WaBlurEvent } from './wa-blur.js';
export type { WaCancelEvent } from './wa-cancel.js';
export type { WaChangeEvent } from './wa-change.js';
export type { WaClearEvent } from './wa-clear.js';
export type { WaCloseEvent } from './wa-close.js';
export type { WaCollapseEvent } from './wa-collapse.js';
export type { WaCopyEvent } from './wa-copy.js';
export type { WaErrorEvent } from './wa-error.js';
export type { WaExpandEvent } from './wa-expand.js';
export type { WaFinishEvent } from './wa-finish.js';
export type { WaFocusEvent } from './wa-focus.js';
export type { WaHideEvent } from './wa-hide.js';
export type { WaHoverEvent } from './wa-hover.js';
export type { WaInputEvent } from './wa-input.js';
export type { WaInvalidEvent } from './wa-invalid.js';
export type { WaLazyChangeEvent } from './wa-lazy-change.js';
export type { WaLazyLoadEvent } from './wa-lazy-load.js';
export type { WaLoadEvent } from './wa-load.js';
export type { WaMutationEvent } from './wa-mutation.js';
export type { WaRemoveEvent } from './wa-remove.js';
export type { WaRepositionEvent } from './wa-reposition.js';
export type { WaRequestCloseEvent } from './wa-request-close.js';
export type { WaResizeEvent } from './wa-resize.js';
export type { WaSelectEvent } from './wa-select.js';
export type { WaSelectionChangeEvent } from './wa-selection-change.js';
export type { WaShowEvent } from './wa-show.js';
export type { WaSlideChangeEvent } from './wa-slide-change.js';
export type { WaStartEvent } from './wa-start.js';
export type { WaTabHideEvent } from './wa-tab-hide.js';
export type { WaTabShowEvent } from './wa-tab-show.js';
export type { WaAfterCollapseEvent } from './after-collapse.js';
export type { WaAfterExpandEvent } from './after-expand.js';
export type { WaAfterHideEvent } from './after-hide.js';
export type { WaAfterShowEvent } from './after-show.js';
export type { WaBlurEvent } from './blur.js';
export type { WaCancelEvent } from './cancel.js';
export type { WaChangeEvent } from './change.js';
export type { WaClearEvent } from './clear.js';
export type { WaCloseEvent } from './close.js';
export type { WaCollapseEvent } from './collapse.js';
export type { WaCopyEvent } from './copy.js';
export type { WaErrorEvent } from './error.js';
export type { WaExpandEvent } from './expand.js';
export type { WaFinishEvent } from './finish.js';
export type { WaFocusEvent } from './focus.js';
export type { WaHideEvent } from './hide.js';
export type { WaHoverEvent } from './hover.js';
export type { WaInputEvent } from './input.js';
export type { WaInvalidEvent } from './invalid.js';
export type { WaLazyChangeEvent } from './lazy-change.js';
export type { WaLazyLoadEvent } from './lazy-load.js';
export type { WaLoadEvent } from './load.js';
export type { WaMutationEvent } from './mutation.js';
export type { WaRemoveEvent } from './remove.js';
export type { WaRepositionEvent } from './reposition.js';
export type { WaResizeEvent } from './resize.js';
export type { WaSelectEvent } from './select.js';
export type { WaSelectionChangeEvent } from './selection-change.js';
export type { WaShowEvent } from './show.js';
export type { WaSlideChangeEvent } from './slide-change.js';
export type { WaStartEvent } from './start.js';
export type { WaTabHideEvent } from './tab-hide.js';
export type { WaTabShowEvent } from './tab-show.js';

11
src/events/expand.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaExpandEvent extends Event {
constructor() {
super('wa-expand', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-expand': WaExpandEvent;
}
}

11
src/events/finish.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaFinishEvent extends Event {
constructor() {
super('wa-finish', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-finish': WaFinishEvent;
}
}

11
src/events/focus.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaFocusEvent extends Event {
constructor() {
super('wa-focus', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-focus': WaFocusEvent;
}
}

18
src/events/hide.ts Normal file
View File

@@ -0,0 +1,18 @@
export class WaHideEvent extends Event {
readonly detail: WaHideEventDetails | undefined;
constructor(detail?: WaHideEventDetails) {
super('wa-hide', { bubbles: true, cancelable: true, composed: true });
this.detail = detail;
}
}
interface WaHideEventDetails {
source: Element;
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-hide': WaHideEvent;
}
}

19
src/events/hover.ts Normal file
View File

@@ -0,0 +1,19 @@
interface WaHoverEventDetail {
phase: 'start' | 'move' | 'end';
value: number;
}
export class WaHoverEvent extends Event {
readonly detail: WaHoverEventDetail;
constructor(detail: WaHoverEventDetail) {
super('wa-hover', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-hover': WaHoverEvent;
}
}

View File

@@ -0,0 +1,18 @@
export class WaIncludeErrorEvent extends Event {
readonly detail: WaIncludeErrorDetail;
constructor(detail: WaIncludeErrorDetail) {
super('wa-include-error', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
interface WaIncludeErrorDetail {
status: number;
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-include-error': WaIncludeErrorEvent;
}
}

11
src/events/input.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaInputEvent extends Event {
constructor() {
super('wa-input', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-input': WaInputEvent;
}
}

11
src/events/invalid.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaInvalidEvent extends Event {
constructor() {
super('wa-invalid', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-invalid': WaInvalidEvent;
}
}

11
src/events/lazy-change.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaLazyChangeEvent extends Event {
constructor() {
super('wa-lazy-change', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-lazy-change': WaLazyChangeEvent;
}
}

11
src/events/lazy-load.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaLazyLoadEvent extends Event {
constructor() {
super('wa-lazy-load', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-lazy-load': WaLazyLoadEvent;
}
}

11
src/events/load.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaLoadEvent extends Event {
constructor() {
super('wa-load', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-load': WaLoadEvent;
}
}

18
src/events/mutation.ts Normal file
View File

@@ -0,0 +1,18 @@
export class WaMutationEvent extends Event {
readonly detail: WaMutationEventDetail;
constructor(detail: WaMutationEventDetail) {
super('wa-mutation', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
interface WaMutationEventDetail {
mutationList: MutationRecord[];
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-mutation': WaMutationEvent;
}
}

11
src/events/remove.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaRemoveEvent extends Event {
constructor() {
super('wa-remove', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-remove': WaRemoveEvent;
}
}

11
src/events/reposition.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaRepositionEvent extends Event {
constructor() {
super('wa-reposition', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-reposition': WaRepositionEvent;
}
}

18
src/events/resize.ts Normal file
View File

@@ -0,0 +1,18 @@
export class WaResizeEvent extends Event {
readonly detail: WaResizeEventDetail;
constructor(detail: WaResizeEventDetail) {
super('wa-resize', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
interface WaResizeEventDetail {
entries: ResizeObserverEntry[];
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-resize': WaResizeEvent;
}
}

20
src/events/select.ts Normal file
View File

@@ -0,0 +1,20 @@
import type WaMenuItem from '../components/menu-item/menu-item.js';
export class WaSelectEvent extends Event {
readonly detail;
constructor(detail: WaSelectEventDetail) {
super('wa-select', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
interface WaSelectEventDetail {
item: WaMenuItem;
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-select': WaSelectEvent;
}
}

View File

@@ -0,0 +1,20 @@
import type WaTreeItem from '../components/tree-item/tree-item.js';
export class WaSelectionChangeEvent extends Event {
readonly detail: WaSelectionChangeEventDetail;
constructor(detail: WaSelectionChangeEventDetail) {
super('wa-selection-change', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
interface WaSelectionChangeEventDetail {
selection: WaTreeItem[];
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-selection-change': WaSelectionChangeEvent;
}
}

11
src/events/show.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaShowEvent extends Event {
constructor() {
super('wa-show', { bubbles: true, cancelable: true, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-show': WaShowEvent;
}
}

View File

@@ -0,0 +1,21 @@
import type WaCarouselItem from '../components/carousel-item/carousel-item.js';
export class WaSlideChangeEvent extends Event {
readonly detail: WaSlideChangeEventDetails;
constructor(detail: WaSlideChangeEventDetails) {
super('wa-slide-change', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
interface WaSlideChangeEventDetails {
index: number;
slide: WaCarouselItem;
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-slide-change': WaSlideChangeEvent;
}
}

11
src/events/start.ts Normal file
View File

@@ -0,0 +1,11 @@
export class WaStartEvent extends Event {
constructor() {
super('wa-start', { bubbles: true, cancelable: false, composed: true });
}
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-start': WaStartEvent;
}
}

18
src/events/tab-hide.ts Normal file
View File

@@ -0,0 +1,18 @@
export class WaTabHideEvent extends Event {
readonly detail: WaTabHideEventDetail;
constructor(detail: WaTabHideEventDetail) {
super('wa-tab-hide', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
interface WaTabHideEventDetail {
name: string;
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-tab-hide': WaTabHideEvent;
}
}

18
src/events/tab-show.ts Normal file
View File

@@ -0,0 +1,18 @@
export class WaTabShowEvent extends Event {
readonly detail: WaTabShowEventDetail;
constructor(detail: WaTabShowEventDetail) {
super('wa-tab-show', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
interface WaTabShowEventDetail {
name: string;
}
declare global {
interface GlobalEventHandlersEventMap {
'wa-tab-show': WaTabShowEvent;
}
}

View File

@@ -1,7 +0,0 @@
export type WaAfterCollapseEvent = CustomEvent<Record<PropertyKey, never>>;
declare global {
interface GlobalEventHandlersEventMap {
'wa-after-collapse': WaAfterCollapseEvent;
}
}

View File

@@ -1,7 +0,0 @@
export type WaAfterExpandEvent = CustomEvent<Record<PropertyKey, never>>;
declare global {
interface GlobalEventHandlersEventMap {
'wa-after-expand': WaAfterExpandEvent;
}
}

View File

@@ -1,7 +0,0 @@
export type WaAfterHideEvent = CustomEvent<Record<PropertyKey, never>>;
declare global {
interface GlobalEventHandlersEventMap {
'wa-after-hide': WaAfterHideEvent;
}
}

View File

@@ -1,7 +0,0 @@
export type WaAfterShowEvent = CustomEvent<Record<PropertyKey, never>>;
declare global {
interface GlobalEventHandlersEventMap {
'wa-after-show': WaAfterShowEvent;
}
}

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