Merge branch 'next' into prepare

This commit is contained in:
Cory LaViska
2024-03-06 07:54:52 -05:00
13 changed files with 124 additions and 19 deletions

View File

@@ -23,11 +23,21 @@ New versions of Web Awesome are released as-needed and generally occur when a cr
## Next
- Added support for `contextElement` to `VirtualElements` in `<sl-popup>` [#1874]
- Fixed a bug in `<sl-rating>` that caused the rating to not reset in some circumstances [#1877]
- Fixed a bug in `<sl-select>` that caused the menu to not close when rendered in a shadow root [#1878]
- Fixed a bug in `<sl-tree>` that caused a new stacking context resulting in tooltips being clipped [#1709]
- Fixed a bug in `<sl-tab-group>` that caused the scroll controls to toggle indefinitely when zoomed in Safari [#1839]
- Fixed a bug in the submenu controller that allowed two submenus to be open at the same time [#1880]
## 2.14.0
- Added the Arabic translation [#1852]
- Added help text to `<sl-checkbox>` [#1860]
- Added help text to `<sl-switch>` [#1800]
- Fixed a bug in `<sl-option>` that caused HTML tags to be included in `getTextLabel()`
- Fixed a bug in `<sl-carousel>` that caused slides to not switch correctly [#1862]
- Refactored component styles to be consumed more efficiently [#1692]
## 2.13.1

View File

@@ -74,6 +74,7 @@ export default class WaDialog extends WebAwesomeElement {
private readonly localize = new LocalizeController(this);
private originalTrigger: HTMLElement | null;
public modal = new Modal(this);
private closeWatcher: CloseWatcher | null;
@query('.dialog') dialog: HTMLElement;
@query('.dialog__panel') panel: HTMLElement;
@@ -111,6 +112,7 @@ export default class WaDialog extends WebAwesomeElement {
super.disconnectedCallback();
this.modal.deactivate();
unlockBodyScrolling(this);
this.closeWatcher?.destroy();
}
private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
@@ -129,7 +131,14 @@ export default class WaDialog extends WebAwesomeElement {
}
private addOpenListeners() {
document.addEventListener('keydown', this.handleDocumentKeyDown);
if ('CloseWatcher' in window) {
this.closeWatcher?.destroy();
this.closeWatcher = new CloseWatcher();
this.closeWatcher.onclose = () => this.requestClose('keyboard');
} else {
this.closeWatcher?.destroy();
document.addEventListener('keydown', this.handleDocumentKeyDown);
}
}
private removeOpenListeners() {

View File

@@ -81,6 +81,7 @@ export default class WaDrawer extends WebAwesomeElement {
private readonly localize = new LocalizeController(this);
private originalTrigger: HTMLElement | null;
public modal = new Modal(this);
private closeWatcher: CloseWatcher | null;
@query('.drawer') drawer: HTMLElement;
@query('.drawer__panel') panel: HTMLElement;
@@ -129,6 +130,7 @@ export default class WaDrawer extends WebAwesomeElement {
disconnectedCallback() {
super.disconnectedCallback();
unlockBodyScrolling(this);
this.closeWatcher?.destroy();
}
private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
@@ -147,7 +149,16 @@ export default class WaDrawer extends WebAwesomeElement {
}
private addOpenListeners() {
document.addEventListener('keydown', this.handleDocumentKeyDown);
if ('CloseWatcher' in window) {
this.closeWatcher?.destroy();
if (!this.contained) {
this.closeWatcher = new CloseWatcher();
this.closeWatcher.onclose = () => this.requestClose('keyboard');
}
} else {
document.addEventListener('keydown', this.handleDocumentKeyDown);
this.closeWatcher?.destroy();
}
}
private removeOpenListeners() {

View File

@@ -48,6 +48,7 @@ export default class WaDropdown extends WebAwesomeElement {
@query('.dropdown__panel') panel: HTMLSlotElement;
private readonly localize = new LocalizeController(this);
private closeWatcher: CloseWatcher | null;
/**
* Indicates whether or not the dropdown is open. You can toggle this attribute to show and hide the dropdown, or you
@@ -140,7 +141,7 @@ export default class WaDropdown extends WebAwesomeElement {
private handleKeyDown = (event: KeyboardEvent) => {
// Close when escape is pressed inside an open dropdown. We need to listen on the panel itself and stop propagation
// in case any ancestors are also listening for this key.
if (this.open && event.key === 'Escape') {
if (this.open && event.key === 'Escape' && !this.closeWatcher) {
event.stopPropagation();
this.hide();
this.focusOnTrigger();
@@ -334,7 +335,16 @@ export default class WaDropdown extends WebAwesomeElement {
addOpenListeners() {
this.panel.addEventListener('wa-select', this.handlePanelSelect);
this.panel.addEventListener('keydown', this.handleKeyDown);
if ('CloseWatcher' in window) {
this.closeWatcher?.destroy();
this.closeWatcher = new CloseWatcher();
this.closeWatcher.onclose = () => {
this.hide();
this.focusOnTrigger();
};
} else {
this.panel.addEventListener('keydown', this.handleKeyDown);
}
document.addEventListener('keydown', this.handleDocumentKeyDown);
document.addEventListener('mousedown', this.handleDocumentMouseDown);
}
@@ -346,6 +356,7 @@ export default class WaDropdown extends WebAwesomeElement {
}
document.removeEventListener('keydown', this.handleDocumentKeyDown);
document.removeEventListener('mousedown', this.handleDocumentMouseDown);
this.closeWatcher?.destroy();
}
@watch('open', { waitUntilFirstUpdate: true })

View File

@@ -229,6 +229,7 @@ export class SubmenuController implements ReactiveController {
// newly opened menu.
private enableSubmenu(delay = true) {
if (delay) {
window.clearTimeout(this.enableSubmenuTimer);
this.enableSubmenuTimer = window.setTimeout(() => {
this.setSubmenuState(true);
}, this.submenuOpenDelay);
@@ -238,7 +239,7 @@ export class SubmenuController implements ReactiveController {
}
private disableSubmenu() {
clearTimeout(this.enableSubmenuTimer);
window.clearTimeout(this.enableSubmenuTimer);
this.setSubmenuState(false);
}

View File

@@ -9,10 +9,16 @@ import type { CSSResultGroup } from 'lit';
export interface VirtualElement {
getBoundingClientRect: () => DOMRect;
contextElement?: Element;
}
function isVirtualElement(e: unknown): e is VirtualElement {
return e !== null && typeof e === 'object' && 'getBoundingClientRect' in e;
return (
e !== null &&
typeof e === 'object' &&
'getBoundingClientRect' in e &&
('contextElement' in e ? e instanceof Element : true)
);
}
/**

View File

@@ -264,7 +264,6 @@ export default class WaRating extends WebAwesomeElement {
'rating__symbol--hover': this.isHovering && Math.ceil(displayValue) === index + 1
})}
role="presentation"
@mouseenter=${this.handleMouseEnter}
>
<div
style=${styleMap({
@@ -297,7 +296,6 @@ export default class WaRating extends WebAwesomeElement {
'rating__symbol--active': displayValue >= index + 1
})}
role="presentation"
@mouseenter=${this.handleMouseEnter}
>
${unsafeHTML(this.getSymbol(index + 1))}
</span>

View File

@@ -60,6 +60,7 @@ export default css`
.rating__symbol {
transition: var(--wa-transition-fast) scale;
pointer-events: none;
}
.rating__symbol--hover {

View File

@@ -88,6 +88,7 @@ export default class WaSelect extends WebAwesomeElement implements WebAwesomeFor
private readonly localize = new LocalizeController(this);
private typeToSelectString = '';
private typeToSelectTimeout: number;
private closeWatcher: CloseWatcher | null;
@query('.select') popup: WaPopup;
@query('.select__combobox') combobox: HTMLSlotElement;
@@ -228,17 +229,37 @@ export default class WaSelect extends WebAwesomeElement implements WebAwesomeFor
//
// https://github.com/shoelace-style/shoelace/issues/1763
//
const root = this.getRootNode();
root.addEventListener('focusin', this.handleDocumentFocusIn);
root.addEventListener('keydown', this.handleDocumentKeyDown);
root.addEventListener('mousedown', this.handleDocumentMouseDown);
document.addEventListener('focusin', this.handleDocumentFocusIn);
document.addEventListener('keydown', this.handleDocumentKeyDown);
document.addEventListener('mousedown', this.handleDocumentMouseDown);
// If the component is rendered in a shadow root, we need to attach the focusin listener there too
if (this.getRootNode() !== document) {
this.getRootNode().addEventListener('focusin', this.handleDocumentFocusIn);
}
if ('CloseWatcher' in window) {
this.closeWatcher?.destroy();
this.closeWatcher = new CloseWatcher();
this.closeWatcher.onclose = () => {
if (this.open) {
this.hide();
this.displayInput.focus({ preventScroll: true });
}
};
}
}
private removeOpenListeners() {
const root = this.getRootNode();
root.removeEventListener('focusin', this.handleDocumentFocusIn);
root.removeEventListener('keydown', this.handleDocumentKeyDown);
root.removeEventListener('mousedown', this.handleDocumentMouseDown);
document.removeEventListener('focusin', this.handleDocumentFocusIn);
document.removeEventListener('keydown', this.handleDocumentKeyDown);
document.removeEventListener('mousedown', this.handleDocumentMouseDown);
if (this.getRootNode() !== document) {
this.getRootNode().removeEventListener('focusin', this.handleDocumentFocusIn);
}
this.closeWatcher?.destroy();
}
private handleFocus() {

View File

@@ -337,8 +337,13 @@ export default class WaTabGroup extends WebAwesomeElement {
if (this.noScrollControls) {
this.hasScrollControls = false;
} else {
// In most cases, we can compare scrollWidth to clientWidth to determine if scroll controls should show. However,
// Safari appears to calculate this incorrectly when zoomed at 110%, causing the controls to toggle indefinitely.
// Adding a single pixel to the comparison seems to resolve it.
//
// See https://github.com/shoelace-style/shoelace/issues/1839
this.hasScrollControls =
['top', 'bottom'].includes(this.placement) && this.nav.scrollWidth > this.nav.clientWidth;
['top', 'bottom'].includes(this.placement) && this.nav.scrollWidth > this.nav.clientWidth + 1;
}
}

View File

@@ -45,6 +45,7 @@ export default class WaTooltip extends WebAwesomeElement {
private hoverTimeout: number;
private readonly localize = new LocalizeController(this);
private closeWatcher: CloseWatcher | null;
@query('slot:not([name])') defaultSlot: HTMLSlotElement;
@query('.tooltip__body') body: HTMLElement;
@@ -108,6 +109,7 @@ export default class WaTooltip extends WebAwesomeElement {
disconnectedCallback() {
// Cleanup this event in case the tooltip is removed while open
this.closeWatcher?.destroy();
document.removeEventListener('keydown', this.handleDocumentKeyDown);
}
@@ -181,7 +183,15 @@ export default class WaTooltip extends WebAwesomeElement {
// Show
this.emit('wa-show');
document.addEventListener('keydown', this.handleDocumentKeyDown);
if ('CloseWatcher' in window) {
this.closeWatcher?.destroy();
this.closeWatcher = new CloseWatcher();
this.closeWatcher.onclose = () => {
this.hide();
};
} else {
document.addEventListener('keydown', this.handleDocumentKeyDown);
}
await stopAnimations(this.body);
this.body.hidden = false;
@@ -194,6 +204,7 @@ export default class WaTooltip extends WebAwesomeElement {
} else {
// Hide
this.emit('wa-hide');
this.closeWatcher?.destroy();
document.removeEventListener('keydown', this.handleDocumentKeyDown);
await stopAnimations(this.body);

View File

@@ -16,7 +16,6 @@ export default css`
--indent-size: var(--wa-space-l);
display: block;
isolation: isolate;
/*
* Tree item indentation uses the "em" unit to increment its width on each level, so setting the font size to zero

22
src/declaration.d.ts vendored
View File

@@ -14,3 +14,25 @@ declare namespace Chai {
interface HTMLInputElement {
showPicker: () => void;
}
/* eslint-disable */
interface CloseWatcher extends EventTarget {
new (options?: CloseWatcherOptions): CloseWatcher;
requestClose(): void;
close(): void;
destroy(): void;
oncancel: (event: Event) => void | null;
onclose: (event: Event) => void | null;
}
declare const CloseWatcher: CloseWatcher;
interface CloseWatcherOptions {
signal: AbortSignal;
}
declare interface Window {
CloseWatcher?: CloseWatcher;
}
/* eslint-enable */