mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 04:09:12 +00:00
Merge branch 'next' into efficient-style-imports
This commit is contained in:
@@ -23,6 +23,15 @@ 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]
|
||||
|
||||
@@ -75,6 +75,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;
|
||||
@@ -112,6 +113,7 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
super.disconnectedCallback();
|
||||
this.modal.deactivate();
|
||||
unlockBodyScrolling(this);
|
||||
this.closeWatcher?.destroy();
|
||||
}
|
||||
|
||||
private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
|
||||
@@ -130,7 +132,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() {
|
||||
|
||||
@@ -82,6 +82,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;
|
||||
@@ -130,6 +131,7 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
unlockBodyScrolling(this);
|
||||
this.closeWatcher?.destroy();
|
||||
}
|
||||
|
||||
private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
|
||||
@@ -148,7 +150,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() {
|
||||
|
||||
@@ -49,6 +49,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
|
||||
@@ -141,7 +142,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();
|
||||
@@ -335,7 +336,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);
|
||||
}
|
||||
@@ -347,6 +357,7 @@ export default class WaDropdown extends WebAwesomeElement {
|
||||
}
|
||||
document.removeEventListener('keydown', this.handleDocumentKeyDown);
|
||||
document.removeEventListener('mousedown', this.handleDocumentMouseDown);
|
||||
this.closeWatcher?.destroy();
|
||||
}
|
||||
|
||||
@watch('open', { waitUntilFirstUpdate: true })
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,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)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -265,7 +265,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({
|
||||
@@ -298,7 +297,6 @@ export default class WaRating extends WebAwesomeElement {
|
||||
'rating__symbol--active': displayValue >= index + 1
|
||||
})}
|
||||
role="presentation"
|
||||
@mouseenter=${this.handleMouseEnter}
|
||||
>
|
||||
${unsafeHTML(this.getSymbol(index + 1))}
|
||||
</span>
|
||||
|
||||
@@ -57,6 +57,7 @@ export default css`
|
||||
|
||||
.rating__symbol {
|
||||
transition: var(--wa-transition-fast) scale;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rating__symbol--hover {
|
||||
|
||||
@@ -90,6 +90,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;
|
||||
@@ -230,17 +231,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() {
|
||||
|
||||
@@ -338,8 +338,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,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;
|
||||
@@ -109,6 +110,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);
|
||||
}
|
||||
|
||||
@@ -182,7 +184,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;
|
||||
@@ -195,6 +205,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);
|
||||
|
||||
@@ -13,7 +13,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
22
src/declaration.d.ts
vendored
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user