mirror of
https://github.com/shoelace-style/shoelace.git
synced 2026-01-12 02:59:13 +00:00
Merge branch 'next' into konnorrogers/fix-select-regression
This commit is contained in:
@@ -14,6 +14,7 @@
|
|||||||
"autoloading",
|
"autoloading",
|
||||||
"autoplay",
|
"autoplay",
|
||||||
"bezier",
|
"bezier",
|
||||||
|
"Bokmål",
|
||||||
"boxicons",
|
"boxicons",
|
||||||
"CACHEABLE",
|
"CACHEABLE",
|
||||||
"callout",
|
"callout",
|
||||||
|
|||||||
@@ -283,7 +283,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</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 %}
|
{% endif %}
|
||||||
|
|
||||||
{# CSS Parts #}
|
{# CSS Parts #}
|
||||||
|
|||||||
@@ -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) {
|
if (!window.scrollPositions) {
|
||||||
@@ -6,13 +6,13 @@ import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@7.3.0/+esm'
|
|||||||
}
|
}
|
||||||
|
|
||||||
function preserveScroll() {
|
function preserveScroll() {
|
||||||
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
|
document.querySelectorAll('[data-preserve-scroll]').forEach(element => {
|
||||||
scrollPositions[element.id] = element.scrollTop;
|
scrollPositions[element.id] = element.scrollTop;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function restoreScroll(event) {
|
function restoreScroll(event) {
|
||||||
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
|
document.querySelectorAll('[data-preserve-scroll]').forEach(element => {
|
||||||
element.scrollTop = scrollPositions[element.id];
|
element.scrollTop = scrollPositions[element.id];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
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:
|
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
|
```html:preview
|
||||||
<form id="lazy-options-example">
|
<form id="lazy-options-example">
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const App = () => (
|
|||||||
|
|
||||||
### Sizes
|
### Sizes
|
||||||
|
|
||||||
Use the `size` attribute to change a tab's size.
|
Use the `size` attribute to change a tag's size.
|
||||||
|
|
||||||
```html:preview
|
```html:preview
|
||||||
<sl-tag size="small">Small</sl-tag>
|
<sl-tag size="small">Small</sl-tag>
|
||||||
@@ -53,7 +53,7 @@ const App = () => (
|
|||||||
|
|
||||||
### Pill
|
### Pill
|
||||||
|
|
||||||
Use the `pill` attribute to give tabs rounded edges.
|
Use the `pill` attribute to give tags rounded edges.
|
||||||
|
|
||||||
```html:preview
|
```html:preview
|
||||||
<sl-tag size="small" pill>Small</sl-tag>
|
<sl-tag size="small" pill>Small</sl-tag>
|
||||||
|
|||||||
@@ -14,11 +14,17 @@ New versions of Shoelace are released as-needed and generally occur when a criti
|
|||||||
|
|
||||||
## Next
|
## 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]
|
- 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>` 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-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-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-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]
|
- Improved performance of `<sl-range>` by skipping positioning logic when tooltip isn't shown [#2064]
|
||||||
|
|
||||||
## 2.18.0
|
## 2.18.0
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ export default class SlCarousel extends ShoelaceElement {
|
|||||||
@state() dragging = false;
|
@state() dragging = false;
|
||||||
|
|
||||||
private autoplayController = new AutoplayController(this, () => this.next());
|
private autoplayController = new AutoplayController(this, () => this.next());
|
||||||
|
private dragStartPosition: [number, number] = [-1, -1];
|
||||||
private readonly localize = new LocalizeController(this);
|
private readonly localize = new LocalizeController(this);
|
||||||
private mutationObserver: MutationObserver;
|
private mutationObserver: MutationObserver;
|
||||||
private pendingSlideChange = false;
|
private pendingSlideChange = false;
|
||||||
@@ -151,6 +152,20 @@ export default class SlCarousel extends ShoelaceElement {
|
|||||||
) as SlCarouselItem[];
|
) 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) {
|
private handleKeyDown(event: KeyboardEvent) {
|
||||||
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) {
|
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
@@ -208,6 +223,7 @@ export default class SlCarousel extends ShoelaceElement {
|
|||||||
// Start dragging if it hasn't yet
|
// Start dragging if it hasn't yet
|
||||||
this.scrollContainer.style.setProperty('scroll-snap-type', 'none');
|
this.scrollContainer.style.setProperty('scroll-snap-type', 'none');
|
||||||
this.dragging = true;
|
this.dragging = true;
|
||||||
|
this.dragStartPosition = [event.clientX, event.clientY];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scrollContainer.scrollBy({
|
this.scrollContainer.scrollBy({
|
||||||
@@ -255,6 +271,7 @@ export default class SlCarousel extends ShoelaceElement {
|
|||||||
scrollContainer.style.removeProperty('scroll-snap-type');
|
scrollContainer.style.removeProperty('scroll-snap-type');
|
||||||
|
|
||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
|
this.dragStartPosition = [-1, -1];
|
||||||
this.handleScrollEnd();
|
this.handleScrollEnd();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -364,10 +381,10 @@ export default class SlCarousel extends ShoelaceElement {
|
|||||||
this.createClones();
|
this.createClones();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.synchronizeSlides();
|
|
||||||
|
|
||||||
// Because the DOM may be changed, restore the scroll position to the active slide
|
// Because the DOM may be changed, restore the scroll position to the active slide
|
||||||
this.goToSlide(this.activeSlide, 'auto');
|
this.goToSlide(this.activeSlide, 'auto');
|
||||||
|
|
||||||
|
this.synchronizeSlides();
|
||||||
}
|
}
|
||||||
|
|
||||||
private createClones() {
|
private createClones() {
|
||||||
@@ -533,6 +550,7 @@ export default class SlCarousel extends ShoelaceElement {
|
|||||||
@mousedown="${this.handleMouseDragStart}"
|
@mousedown="${this.handleMouseDragStart}"
|
||||||
@scroll="${this.handleScroll}"
|
@scroll="${this.handleScroll}"
|
||||||
@scrollend=${this.handleScrollEnd}
|
@scrollend=${this.handleScrollEnd}
|
||||||
|
@click=${this.handleClick}
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ export default class SlDialog extends ShoelaceElement {
|
|||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this.modal.deactivate();
|
this.modal.deactivate();
|
||||||
unlockBodyScrolling(this);
|
unlockBodyScrolling(this);
|
||||||
this.closeWatcher?.destroy();
|
this.removeOpenListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
|
private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export default class SlDrawer extends ShoelaceElement {
|
|||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
unlockBodyScrolling(this);
|
unlockBodyScrolling(this);
|
||||||
this.closeWatcher?.destroy();
|
this.removeOpenListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
|
private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ export default class SlRating extends ShoelaceElement {
|
|||||||
aria-valuenow=${this.value}
|
aria-valuenow=${this.value}
|
||||||
aria-valuemin=${0}
|
aria-valuemin=${0}
|
||||||
aria-valuemax=${this.max}
|
aria-valuemax=${this.max}
|
||||||
tabindex=${this.disabled ? '-1' : '0'}
|
tabindex=${this.disabled || this.readonly ? '-1' : '0'}
|
||||||
@click=${this.handleClick}
|
@click=${this.handleClick}
|
||||||
@keydown=${this.handleKeyDown}
|
@keydown=${this.handleKeyDown}
|
||||||
@mouseenter=${this.handleMouseEnter}
|
@mouseenter=${this.handleMouseEnter}
|
||||||
|
|||||||
@@ -518,8 +518,8 @@ export default class SlSelect extends ShoelaceElement implements ShoelaceFormCon
|
|||||||
|
|
||||||
/* @internal - used by options to update labels */
|
/* @internal - used by options to update labels */
|
||||||
public handleDefaultSlotChange() {
|
public handleDefaultSlotChange() {
|
||||||
if (!customElements.get('wa-option')) {
|
if (!customElements.get('sl-option')) {
|
||||||
customElements.whenDefined('wa-option').then(() => this.handleDefaultSlotChange());
|
customElements.whenDefined('sl-option').then(() => this.handleDefaultSlotChange());
|
||||||
}
|
}
|
||||||
|
|
||||||
const allOptions = this.getAllOptions();
|
const allOptions = this.getAllOptions();
|
||||||
|
|||||||
39
src/translations/nb.ts
Normal file
39
src/translations/nb.ts
Normal 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
39
src/translations/nn.ts
Normal 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
41
src/translations/uk.ts
Normal 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;
|
||||||
Reference in New Issue
Block a user