Merge branch 'next' into konnorrogers/fix-select-regression

This commit is contained in:
Cory LaViska
2024-12-02 13:22:23 -05:00
committed by GitHub
14 changed files with 162 additions and 18 deletions

View File

@@ -14,6 +14,7 @@
"autoloading",
"autoplay",
"bezier",
"Bokmål",
"boxicons",
"CACHEABLE",
"callout",

View File

@@ -283,7 +283,7 @@
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#custom-properties') }}">customizing CSS custom properties</a>.</em></p>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing#custom-properties') }}">customizing CSS custom properties</a>.</em></p>
{% endif %}
{# CSS Parts #}

View File

@@ -1,4 +1,4 @@
import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@7.3.0/+esm';
import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.10/+esm';
(() => {
if (!window.scrollPositions) {
@@ -6,13 +6,13 @@ import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@7.3.0/+esm'
}
function preserveScroll() {
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
document.querySelectorAll('[data-preserve-scroll]').forEach(element => {
scrollPositions[element.id] = element.scrollTop;
});
}
function restoreScroll(event) {
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
document.querySelectorAll('[data-preserve-scroll]').forEach(element => {
element.scrollTop = scrollPositions[element.id];
});

View File

@@ -504,17 +504,17 @@ Remember that custom tags are rendered in a shadow root. To style them, you can
### Lazy loading options
Lazy loading options is very hard to get right. `<wa-select>` largely follows how a native `<select>` works.
Lazy loading options is very hard to get right. `<sl-select>` largely follows how a native `<select>` works.
Here are the following conditions:
- If a `<wa-select>` is created without any options, but is given a `value` attribute, its `value` will be `""`, and then when options are added, if any of the options have a value equal to the `<wa-select>` value, the value of the `<wa-select>` will equal that of the option.
- If a `<sl-select>` is created without any options, but is given a `value` attribute, its `value` will be `""`, and then when options are added, if any of the options have a value equal to the `<sl-select>` value, the value of the `<sl-select>` will equal that of the option.
EX: `<wa-select value="foo">` will have a value of `""` until `<wa-option value="foo">Foo</wa-option>` connects, at which point its value will become `"foo"` when submitting.
EX: `<sl-select value="foo">` will have a value of `""` until `<sl-option value="foo">Foo</sl-option>` connects, at which point its value will become `"foo"` when submitting.
- If a `<wa-select multiple>` with an initial value has multiple values, but only some of the options are present, it will only respect the options that are present, and if a selected option is loaded in later, _AND_ the value of the select has not changed via user interaction or direct property assignment, it will add the selected option to the form value and to the `.value` of the select.
- If a `<sl-select multiple>` with an initial value has multiple values, but only some of the options are present, it will only respect the options that are present, and if a selected option is loaded in later, _AND_ the value of the select has not changed via user interaction or direct property assignment, it will add the selected option to the form value and to the `.value` of the select.
This can be hard to conceptualize, so heres a fairly large example showing how lazy loaded options work with `<wa-select>` and `<wa-select multiple>` when given initial value attributes. Feel free to play around with it in a codepen.
This can be hard to conceptualize, so heres a fairly large example showing how lazy loaded options work with `<sl-select>` and `<sl-select multiple>` when given initial value attributes. Feel free to play around with it in a codepen.
```html:preview
<form id="lazy-options-example">

View File

@@ -31,7 +31,7 @@ const App = () => (
### Sizes
Use the `size` attribute to change a tab's size.
Use the `size` attribute to change a tag's size.
```html:preview
<sl-tag size="small">Small</sl-tag>
@@ -53,7 +53,7 @@ const App = () => (
### Pill
Use the `pill` attribute to give tabs rounded edges.
Use the `pill` attribute to give tags rounded edges.
```html:preview
<sl-tag size="small" pill>Small</sl-tag>

View File

@@ -14,11 +14,17 @@ New versions of Shoelace are released as-needed and generally occur when a criti
## Next
- Added Norwegian translations for Bokmål and Nynorsk [#2268]
- Added Ukrainian translation [#2270]
- Added support for <kbd>Enter</kbd> to `<sl-split-panel>` to align with ARIA APG's [window splitter pattern](https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/) [#2234]
- Fixed a bug in `<sl-select>` when setting the value property before the element connected. [#2255]
- Fixed a bug in `<sl-select>` where it was using the wrong tag name. [#2287]
- Fixed a bug in `<sl-carousel>` that caused the navigation icons to be reversed
- Fixed a bug in `<sl-select>` that prevented label changes in `<sl-option>` from updating the controller [#1971]
- Fixed a bug in `<sl-textarea>` that caused a console warning in Firefox when typing [#2107]
- Fixed a bug in `<sl-carousel>` that caused interactive elements to be activated when dragging [#2196]
- Fixed a bug in `<sl-carousel>` that caused out of order slides when used inside a resize observer [#2260]
- Fixed a bug in `<sl-rating>` that allowed tabbing into the rating when readonly [#2271]
- Improved performance of `<sl-range>` by skipping positioning logic when tooltip isn't shown [#2064]
## 2.18.0

View File

@@ -92,6 +92,7 @@ export default class SlCarousel extends ShoelaceElement {
@state() dragging = false;
private autoplayController = new AutoplayController(this, () => this.next());
private dragStartPosition: [number, number] = [-1, -1];
private readonly localize = new LocalizeController(this);
private mutationObserver: MutationObserver;
private pendingSlideChange = false;
@@ -151,6 +152,20 @@ export default class SlCarousel extends ShoelaceElement {
) as SlCarouselItem[];
}
private handleClick(event: MouseEvent) {
if (this.dragging && this.dragStartPosition[0] > 0 && this.dragStartPosition[1] > 0) {
const deltaX = Math.abs(this.dragStartPosition[0] - event.clientX);
const deltaY = Math.abs(this.dragStartPosition[1] - event.clientY);
const delta = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// Prevents clicks on interactive elements while dragging if the click is within a small range. This prevents
// accidental drags from interfering with intentional clicks.
if (delta >= 10) {
event.preventDefault();
}
}
}
private handleKeyDown(event: KeyboardEvent) {
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) {
const target = event.target as HTMLElement;
@@ -208,6 +223,7 @@ export default class SlCarousel extends ShoelaceElement {
// Start dragging if it hasn't yet
this.scrollContainer.style.setProperty('scroll-snap-type', 'none');
this.dragging = true;
this.dragStartPosition = [event.clientX, event.clientY];
}
this.scrollContainer.scrollBy({
@@ -255,6 +271,7 @@ export default class SlCarousel extends ShoelaceElement {
scrollContainer.style.removeProperty('scroll-snap-type');
this.dragging = false;
this.dragStartPosition = [-1, -1];
this.handleScrollEnd();
});
};
@@ -364,10 +381,10 @@ export default class SlCarousel extends ShoelaceElement {
this.createClones();
}
this.synchronizeSlides();
// Because the DOM may be changed, restore the scroll position to the active slide
this.goToSlide(this.activeSlide, 'auto');
this.synchronizeSlides();
}
private createClones() {
@@ -533,6 +550,7 @@ export default class SlCarousel extends ShoelaceElement {
@mousedown="${this.handleMouseDragStart}"
@scroll="${this.handleScroll}"
@scrollend=${this.handleScrollEnd}
@click=${this.handleClick}
>
<slot></slot>
</div>

View File

@@ -114,7 +114,7 @@ export default class SlDialog extends ShoelaceElement {
super.disconnectedCallback();
this.modal.deactivate();
unlockBodyScrolling(this);
this.closeWatcher?.destroy();
this.removeOpenListeners();
}
private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {

View File

@@ -131,7 +131,7 @@ export default class SlDrawer extends ShoelaceElement {
disconnectedCallback() {
super.disconnectedCallback();
unlockBodyScrolling(this);
this.closeWatcher?.destroy();
this.removeOpenListeners();
}
private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {

View File

@@ -240,7 +240,7 @@ export default class SlRating extends ShoelaceElement {
aria-valuenow=${this.value}
aria-valuemin=${0}
aria-valuemax=${this.max}
tabindex=${this.disabled ? '-1' : '0'}
tabindex=${this.disabled || this.readonly ? '-1' : '0'}
@click=${this.handleClick}
@keydown=${this.handleKeyDown}
@mouseenter=${this.handleMouseEnter}

View File

@@ -518,8 +518,8 @@ export default class SlSelect extends ShoelaceElement implements ShoelaceFormCon
/* @internal - used by options to update labels */
public handleDefaultSlotChange() {
if (!customElements.get('wa-option')) {
customElements.whenDefined('wa-option').then(() => this.handleDefaultSlotChange());
if (!customElements.get('sl-option')) {
customElements.whenDefined('sl-option').then(() => this.handleDefaultSlotChange());
}
const allOptions = this.getAllOptions();

39
src/translations/nb.ts Normal file
View File

@@ -0,0 +1,39 @@
import { registerTranslation } from '../utilities/localize.js';
import type { Translation } from '../utilities/localize.js';
const translation: Translation = {
$code: 'nb',
$name: 'Norwegian Bokmål',
$dir: 'ltr',
carousel: 'Karusell',
clearEntry: 'Tøm felt',
close: 'Lukk',
copied: 'Kopiert',
copy: 'Kopier',
currentValue: 'Nåværende verdi',
error: 'Feil',
goToSlide: (slide, count) => `Gå til visning ${slide} av ${count}`,
hidePassword: 'Skjul passord',
loading: 'Laster',
nextSlide: 'Neste visning',
numOptionsSelected: num => {
if (num === 0) return 'Ingen alternativer valgt';
if (num === 1) return 'Ett alternativ valgt';
return `${num} alternativer valgt`;
},
previousSlide: 'Forrige visning',
progress: 'Fremdrift',
remove: 'Fjern',
resize: 'Endre størrelse',
scrollToEnd: 'Rull til slutten',
scrollToStart: 'Rull til starten',
selectAColorFromTheScreen: 'Velg en farge fra skjermen',
showPassword: 'Vis passord',
slideNum: slide => `Visning ${slide}`,
toggleColorFormat: 'Bytt fargeformat'
};
registerTranslation(translation);
export default translation;

39
src/translations/nn.ts Normal file
View File

@@ -0,0 +1,39 @@
import { registerTranslation } from '../utilities/localize.js';
import type { Translation } from '../utilities/localize.js';
const translation: Translation = {
$code: 'nn',
$name: 'Norwegian Nynorsk',
$dir: 'ltr',
carousel: 'Karusell',
clearEntry: 'Tøm felt',
close: 'Lukk',
copied: 'Kopiert',
copy: 'Kopier',
currentValue: 'Nåverande verdi',
error: 'Feil',
goToSlide: (slide, count) => `Gå til visning ${slide} av ${count}`,
hidePassword: 'Gøym passord',
loading: 'Lastar',
nextSlide: 'Neste visning',
numOptionsSelected: num => {
if (num === 0) return 'Ingen alternativ valt';
if (num === 1) return 'Eitt alternativ valt';
return `${num} alternativ valt`;
},
previousSlide: 'Førre visning',
progress: 'Framdrift',
remove: 'Fjern',
resize: 'Endre storleik',
scrollToEnd: 'Rull til slutten',
scrollToStart: 'Rull til starten',
selectAColorFromTheScreen: 'Vel ein farge frå skjermen',
showPassword: 'Vis passord',
slideNum: slide => `Visning ${slide}`,
toggleColorFormat: 'Byt fargeformat'
};
registerTranslation(translation);
export default translation;

41
src/translations/uk.ts Normal file
View File

@@ -0,0 +1,41 @@
import { registerTranslation } from '../utilities/localize.js';
import type { Translation } from '../utilities/localize.js';
const translation: Translation = {
$code: 'uk',
$name: 'Українська',
$dir: 'ltr',
carousel: 'Карусель',
clearEntry: 'Очистити поле',
close: 'Закрити',
copied: 'Скопійовано',
copy: 'Скопіювати',
currentValue: 'Поточне значення',
error: 'Збій',
goToSlide: (slide, count) => `Перейти до слайда №${slide} з ${count}`,
hidePassword: 'Приховати пароль',
loading: 'Завантаження',
nextSlide: 'Наступний слайд',
numOptionsSelected: num => {
const n = num % 10;
if (n === 0) return 'не вибрано варіантів';
if (n === 1) return 'вибрано 1 варіант';
if (n === 2 || n === 3 || n === 4) return `вибрано ${num} варіанти`;
return `вибрано ${num} варіантів`;
},
previousSlide: 'Попередній слайд',
progress: 'Поступ',
remove: 'Видалити',
resize: 'Змінити розмір',
scrollToEnd: 'Прокрутити в кінець',
scrollToStart: 'Прокрутити на початок',
selectAColorFromTheScreen: 'Виберіть колір на екрані',
showPassword: 'Показати пароль',
slideNum: slide => `Слайд ${slide}`,
toggleColorFormat: 'Переключити кольорову модель'
};
registerTranslation(translation);
export default translation;