Porównaj commity

...

7 Commity

Autor SHA1 Wiadomość Data
RoCat 17b5386ddc
Merge c4856756b9 into 64996b2d35 2024-04-17 16:55:00 +08:00
Konnor Rogers 64996b2d35
add changelog entry for button classes (#1976)
* add changelog entry

* prettier
2024-04-12 12:17:37 -04:00
Susanne Kirchner 0daa5d8dee
Fix invalid css on button style (#1975) 2024-04-12 12:07:48 -04:00
Konnor Rogers 16d5575307
Fix: split panel properly recalculates when going from hidden to shown (#1942)
* fix: split-panel now properly calculates it size when it goes from hidden to being shown.

* chore: add changelog note

* prettier
2024-04-11 14:09:56 -04:00
Konnor Rogers a427433701
fix: scrollbar gutters and dialog scrolling on open (#1967)
* fix: scrollbar gutters and dialog scrolling on open

* prettier

* fix check for current scrollbarGutter property

* prettier
2024-04-11 13:52:41 -04:00
Danny Andrews c6da4f5b14
Update docs for customizing button widths (#1973)
Currently, the docs state that you can set a width attribute to
customize the width of buttons, but no such attribute exists. I've
updated the docs to direct people to set a custom width via CSS through
inline styles or a custom class.
2024-04-11 12:32:54 -05:00
rcatoio c4856756b9 feat: add a countdown on sl-alert 2024-03-01 11:51:24 +01:00
9 zmienionych plików z 204 dodań i 9 usunięć

Wyświetl plik

@ -247,6 +247,69 @@ const App = () => {
};
```
### Countdown
Set the `countdown` attribute to display a loading bar that indicates the alert remaining time. This is useful for alerts with relatively long duration.
```html:preview
<div class="alert-countdown">
<sl-button variant="primary">Show Alert</sl-button>
<sl-alert variant="primary" duration="10000" countdown="RtL" closable>
<sl-icon slot="icon" name="info-circle"></sl-icon>
You're not stuck, the alert will close after a pretty long duration.
</sl-alert>
</div>
<script>
const container = document.querySelector('.alert-countdown');
const button = container.querySelector('sl-button');
const alert = container.querySelector('sl-alert');
button.addEventListener('click', () => alert.show());
</script>
<style>
.alert-countdown sl-alert {
margin-top: var(--sl-spacing-medium);
}
</style>
```
```jsx:react
import { useState } from 'react';
import SlAlert from '@shoelace-style/shoelace/dist/react/alert';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const css = `
.alert-countdown sl-alert {
margin-top: var(--sl-spacing-medium);
}
`;
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<div className="alert-countdown">
<SlButton variant="primary" onClick={() => setOpen(true)}>
Show Alert
</SlButton>
<SlAlert variant="primary" duration="3000" countdown="RtL" open={open} closable onSlAfterHide={() => setOpen(false)}>
<SlIcon slot="icon" name="info-circle" />
You're not stuck, the alert will close after a pretty long duration.
</SlAlert>
</div>
<style>{css}</style>
</>
);
};
```
### Toast Notifications
To display an alert as a toast notification, or "toast", create the alert and call its `toast()` method. This will move the alert out of its position in the DOM and into [the toast stack](#the-toast-stack) where it will be shown. Once dismissed, it will be removed from the DOM completely. To reuse a toast, store a reference to it and call `toast()` again later on.

Wyświetl plik

@ -236,7 +236,7 @@ When a `target` is set, the link will receive `rel="noreferrer noopener"` for [s
### Setting a Custom Width
As expected, buttons can be given a custom width by setting the `width` attribute. This is useful for making buttons span the full width of their container on smaller screens.
As expected, buttons can be given a custom width by passing inline styles to the component (or using a class). This is useful for making buttons span the full width of their container on smaller screens.
```html:preview
<sl-button variant="default" size="small" style="width: 100%; margin-bottom: 1rem;">Small</sl-button>

Wyświetl plik

@ -14,7 +14,10 @@ New versions of Shoelace are released as-needed and generally occur when a criti
## Next
- Fixed a bug in `<sl-split-panel>` that caused it not to recalculate it's position when going from being `display: none;` to its original display value. [#1942]
- Fixed a bug in `<dialog>` where when it showed it would cause a layout shift. [#1967]
- Fixed a bug in `<sl-tooltip>` that allowed unwanted text properties to leak in [#1947]
- Fixed a bug in `<sl-button-group>` classes [#1974]
## 2.15.0

Wyświetl plik

@ -4,7 +4,7 @@ import { getAnimation, setDefaultAnimation } from '../../utilities/animation-reg
import { HasSlotController } from '../../internal/slot.js';
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize.js';
import { property, query } from 'lit/decorators.js';
import { property, query, state } from 'lit/decorators.js';
import { waitForEvent } from '../../internal/event.js';
import { watch } from '../../internal/watch.js';
import componentStyles from '../../styles/component.styles.js';
@ -45,11 +45,15 @@ export default class SlAlert extends ShoelaceElement {
static dependencies = { 'sl-icon-button': SlIconButton };
private autoHideTimeout: number;
private remainingTimeInterval: number;
private countdownAnimation?: Animation;
private readonly hasSlotController = new HasSlotController(this, 'icon', 'suffix');
private readonly localize = new LocalizeController(this);
@query('[part~="base"]') base: HTMLElement;
@query('.alert__countdown-elapsed') countdownElement: HTMLElement;
/**
* Indicates whether or not the alert is open. You can toggle this attribute to show and hide the alert, or you can
* use the `show()` and `hide()` methods and this attribute will reflect the alert's open state.
@ -69,14 +73,57 @@ export default class SlAlert extends ShoelaceElement {
*/
@property({ type: Number }) duration = Infinity;
/**
* Enables a countdown that indicates the remaining time the alert will be displayed.
* Typically used to indicate the remaining time before a whole app refresh.
*/
@property({ type: String, reflect: true }) countdown?: 'RtL' | 'LtR';
@state() private remainingTime = this.duration;
firstUpdated() {
this.base.hidden = !this.open;
}
private restartAutoHide() {
this.handleCountdownChange();
clearTimeout(this.autoHideTimeout);
clearInterval(this.remainingTimeInterval);
if (this.open && this.duration < Infinity) {
this.autoHideTimeout = window.setTimeout(() => this.hide(), this.duration);
this.remainingTime = this.duration;
this.remainingTimeInterval = window.setInterval(() => {
this.remainingTime -= 100;
}, 100);
}
}
private pauseAutoHide() {
this.countdownAnimation?.pause();
clearTimeout(this.autoHideTimeout);
clearInterval(this.remainingTimeInterval);
}
private resumeAutoHide() {
this.autoHideTimeout = window.setTimeout(() => this.hide(), this.remainingTime);
this.remainingTimeInterval = window.setInterval(() => {
this.remainingTime -= 100;
}, 100);
this.countdownAnimation?.play();
}
private handleCountdownChange() {
if(this.open && this.duration < Infinity && this.countdown) {
const { countdownElement } = this;
const start = this.countdown === 'LtR' ? '0' : '100%';
const end = this.countdown === 'LtR' ? '100%' : '0';
this.countdownAnimation = countdownElement.animate([
{ width: start },
{ width: end }
], {
duration: this.duration,
easing: 'linear'
});
}
}
@ -84,10 +131,6 @@ export default class SlAlert extends ShoelaceElement {
this.hide();
}
private handleMouseMove() {
this.restartAutoHide();
}
@watch('open', { waitUntilFirstUpdate: true })
async handleOpenChange() {
if (this.open) {
@ -109,6 +152,7 @@ export default class SlAlert extends ShoelaceElement {
this.emit('sl-hide');
clearTimeout(this.autoHideTimeout);
clearInterval(this.remainingTimeInterval);
await stopAnimations(this.base);
const { keyframes, options } = getAnimation(this, 'alert.hide', { dir: this.localize.dir() });
@ -151,6 +195,7 @@ export default class SlAlert extends ShoelaceElement {
*/
async toast() {
return new Promise<void>(resolve => {
this.handleCountdownChange();
if (toastStack.parentElement === null) {
document.body.append(toastStack);
}
@ -188,6 +233,7 @@ export default class SlAlert extends ShoelaceElement {
alert: true,
'alert--open': this.open,
'alert--closable': this.closable,
'alert--has-countdown': !!this.countdown,
'alert--has-icon': this.hasSlotController.test('icon'),
'alert--primary': this.variant === 'primary',
'alert--success': this.variant === 'success',
@ -197,7 +243,8 @@ export default class SlAlert extends ShoelaceElement {
})}
role="alert"
aria-hidden=${this.open ? 'false' : 'true'}
@mousemove=${this.handleMouseMove}
@mouseenter=${this.pauseAutoHide}
@mouseleave=${this.resumeAutoHide}
>
<div part="icon" class="alert__icon">
<slot name="icon"></slot>
@ -220,6 +267,18 @@ export default class SlAlert extends ShoelaceElement {
></sl-icon-button>
`
: ''}
<div role="timer" class="alert__timer">${this.remainingTime}</div>
${this.countdown ? html`
<div class="alert__countdown">
<div class=${classMap({
'alert__countdown-elapsed': true,
'alert__countdown-elapsed--rtl': this.countdown !== 'LtR'
})}>
</div>
</div>
`: ''}
</div>
`;
}

Wyświetl plik

@ -22,6 +22,7 @@ export default css`
line-height: 1.6;
color: var(--sl-color-neutral-700);
margin: inherit;
overflow: hidden;
}
.alert:not(.alert--has-icon) .alert__icon,
@ -37,6 +38,10 @@ export default css`
padding-inline-start: var(--sl-spacing-large);
}
.alert--has-countdown {
border-bottom: none;
}
.alert--primary {
border-top-color: var(--sl-color-primary-600);
}
@ -91,4 +96,45 @@ export default css`
font-size: var(--sl-font-size-medium);
padding-inline-end: var(--sl-spacing-medium);
}
.alert__countdown {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: calc(var(--sl-panel-border-width) * 3);
background-color: var(--sl-panel-border-color);
}
.alert__countdown .alert__countdown-elapsed {
height: 100%;
}
.alert__countdown .alert__countdown-elapsed--rtl {
width: 0;
}
.alert--primary .alert__countdown-elapsed {
background-color: var(--sl-color-primary-600);
}
.alert--success .alert__countdown-elapsed {
background-color: var(--sl-color-success-600);
}
.alert--neutral .alert__countdown-elapsed {
background-color: var(--sl-color-neutral-600);
}
.alert--warning .alert__countdown-elapsed {
background-color: var(--sl-color-warning-600);
}
.alert--danger .alert__countdown-elapsed {
background-color: var(--sl-color-danger-600);
}
.alert__timer {
display: none;
}
`;

Wyświetl plik

@ -590,7 +590,7 @@ export default css`
/* Focus and checked are always on top */
:host([data-sl-button-group__button--focus]),
:host([data-sl-button-group__button[checked]]) {
:host([data-sl-button-group__button][checked]) {
z-index: 2;
}
`;

Wyświetl plik

@ -189,6 +189,14 @@ export default class SlSplitPanel extends ShoelaceElement {
const { width, height } = entries[0].contentRect;
this.size = this.vertical ? height : width;
// There's some weird logic that gets `this.cachedPositionInPixels = NaN` or `this.position === Infinity` when
// a split-panel goes from `display: none;` to showing.
if (isNaN(this.cachedPositionInPixels) || this.position === Infinity) {
this.cachedPositionInPixels = Number(this.getAttribute('position-in-pixels'));
this.positionInPixels = Number(this.getAttribute('position-in-pixels'));
this.position = this.pixelsToPercentage(this.positionInPixels);
}
// Resize when a primary panel is set
if (this.primary) {
this.position = this.pixelsToPercentage(this.cachedPositionInPixels);

Wyświetl plik

@ -33,6 +33,19 @@ export function lockBodyScrolling(lockingEl: HTMLElement) {
if (!document.documentElement.classList.contains('sl-scroll-lock')) {
/** Scrollbar width + body padding calculation can go away once Safari has scrollbar-gutter support. */
const scrollbarWidth = getScrollbarWidth() + getExistingBodyPadding(); // must be measured before the `sl-scroll-lock` class is applied
let scrollbarGutterProperty = getComputedStyle(document.documentElement).scrollbarGutter;
// default is auto, unsupported browsers is "undefined"
if (!scrollbarGutterProperty || scrollbarGutterProperty === 'auto') {
scrollbarGutterProperty = 'stable';
}
if (scrollbarWidth <= 0) {
// if there's no scrollbar, just set it to "revert" so whatever the user has set gets used. This is useful is the page is not overflowing and showing a scrollbar, or if the user has overflow: hidden, or any other reason a scrollbar may not be showing.
scrollbarGutterProperty = 'revert';
}
document.documentElement.style.setProperty('--sl-scroll-lock-gutter', scrollbarGutterProperty);
document.documentElement.classList.add('sl-scroll-lock');
document.documentElement.style.setProperty('--sl-scroll-lock-size', `${scrollbarWidth}px`);
}

Wyświetl plik

@ -6,7 +6,10 @@
@supports (scrollbar-gutter: stable) {
.sl-scroll-lock {
scrollbar-gutter: stable !important;
scrollbar-gutter: var(--sl-scroll-lock-gutter) !important;
}
.sl-scroll-lock body {
overflow: hidden !important;
}
}