Merge branch 'next' into file-input

file-input
Cory LaViska 2023-09-26 09:30:26 -04:00
commit eac0a840f4
20 zmienionych plików z 158 dodań i 107 usunięć

3
.gitignore vendored
Wyświetl plik

@ -1,9 +1,8 @@
_site
.cache
.DS_Store
cdn
dist
docs/assets/images/sprite.svg
node_modules
src/react
cdn
web-types.json

Wyświetl plik

@ -203,6 +203,7 @@ export default {
]
}),
customElementJetBrainsPlugin({
outdir: './dist',
excludeCss: true,
referencesTemplate: (_, tag) => {
return {

Wyświetl plik

@ -54,7 +54,7 @@ Set the `variant` attribute to change the alert's variant.
<sl-alert variant="neutral" open>
<sl-icon slot="icon" name="gear"></sl-icon>
<strong>Your settings have been updated</strong><br />
Settings will take affect on next login.
Settings will take effect on next login.
</sl-alert>
<br />
@ -102,7 +102,7 @@ const App = () => (
<SlIcon slot="icon" name="gear" />
<strong>Your settings have been updated</strong>
<br />
Settings will take affect on next login.
Settings will take effect on next login.
</SlAlert>
<br />
@ -276,7 +276,7 @@ You should always use the `closable` attribute so users can dismiss the notifica
<sl-alert variant="neutral" duration="3000" closable>
<sl-icon slot="icon" name="gear"></sl-icon>
<strong>Your settings have been updated</strong><br />
Settings will take affect on next login.
Settings will take effect on next login.
</sl-alert>
<sl-alert variant="warning" duration="3000" closable>
@ -361,7 +361,7 @@ const App = () => {
<SlIcon slot="icon" name="gear" />
<strong>Your settings have been updated</strong>
<br />
Settings will take affect on next login.
Settings will take effect on next login.
</SlAlert>
<SlAlert ref={warning} variant="warning" duration="3000" closable>

Wyświetl plik

@ -87,26 +87,26 @@ const App = () => (
Use the `size` attribute to change a radio button's size.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button size="small" value="1">Option 1</sl-radio-button>
<sl-radio-button size="small" value="2">Option 2</sl-radio-button>
<sl-radio-button size="small" value="3">Option 3</sl-radio-button>
<sl-radio-group size="small" label="Select an option" name="a" value="1">
<sl-radio-button value="1">Option 1</sl-radio-button>
<sl-radio-button value="2">Option 2</sl-radio-button>
<sl-radio-button value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button size="medium" value="1">Option 1</sl-radio-button>
<sl-radio-button size="medium" value="2">Option 2</sl-radio-button>
<sl-radio-button size="medium" value="3">Option 3</sl-radio-button>
<sl-radio-group size="medium" label="Select an option" name="a" value="1">
<sl-radio-button value="1">Option 1</sl-radio-button>
<sl-radio-button value="2">Option 2</sl-radio-button>
<sl-radio-button value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button size="large" value="1">Option 1</sl-radio-button>
<sl-radio-button size="large" value="2">Option 2</sl-radio-button>
<sl-radio-button size="large" value="3">Option 3</sl-radio-button>
<sl-radio-group size="large" label="Select an option" name="a" value="1">
<sl-radio-button value="1">Option 1</sl-radio-button>
<sl-radio-button value="2">Option 2</sl-radio-button>
<sl-radio-button value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
@ -115,26 +115,26 @@ import SlRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton size="small" value="1">Option 1</SlRadioButton>
<SlRadioButton size="small" value="2">Option 2</SlRadioButton>
<SlRadioButton size="small" value="3">Option 3</SlRadioButton>
<SlRadioGroup size="small" label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2">Option 2</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton size="medium" value="1">Option 1</SlRadioButton>
<SlRadioButton size="medium" value="2">Option 2</SlRadioButton>
<SlRadioButton size="medium" value="3">Option 3</SlRadioButton>
<SlRadioGroup size="medium" label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2">Option 2</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton size="large" value="1">Option 1</SlRadioButton>
<SlRadioButton size="large" value="2">Option 2</SlRadioButton>
<SlRadioButton size="large" value="3">Option 3</SlRadioButton>
<SlRadioGroup size="large" label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2">Option 2</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
@ -144,26 +144,26 @@ const App = () => (
Use the `pill` attribute to give radio buttons rounded edges.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button pill size="small" value="1">Option 1</sl-radio-button>
<sl-radio-button pill size="small" value="2">Option 2</sl-radio-button>
<sl-radio-button pill size="small" value="3">Option 3</sl-radio-button>
<sl-radio-group size="small" label="Select an option" name="a" value="1">
<sl-radio-button pill value="1">Option 1</sl-radio-button>
<sl-radio-button pill value="2">Option 2</sl-radio-button>
<sl-radio-button pill value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button pill size="medium" value="1">Option 1</sl-radio-button>
<sl-radio-button pill size="medium" value="2">Option 2</sl-radio-button>
<sl-radio-button pill size="medium" value="3">Option 3</sl-radio-button>
<sl-radio-group size="medium" label="Select an option" name="a" value="1">
<sl-radio-button pill value="1">Option 1</sl-radio-button>
<sl-radio-button pill value="2">Option 2</sl-radio-button>
<sl-radio-button pill value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button pill size="large" value="1">Option 1</sl-radio-button>
<sl-radio-button pill size="large" value="2">Option 2</sl-radio-button>
<sl-radio-button pill size="large" value="3">Option 3</sl-radio-button>
<sl-radio-group size="large" label="Select an option" name="a" value="1">
<sl-radio-button pill value="1">Option 1</sl-radio-button>
<sl-radio-button pill value="2">Option 2</sl-radio-button>
<sl-radio-button pill value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
@ -172,26 +172,26 @@ import SlRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton pill size="small" value="1">Option 1</SlRadioButton>
<SlRadioButton pill size="small" value="2">Option 2</SlRadioButton>
<SlRadioButton pill size="small" value="3">Option 3</SlRadioButton>
<SlRadioGroup size="small" label="Select an option" name="a" value="1">
<SlRadioButton pill value="1">Option 1</SlRadioButton>
<SlRadioButton pill value="2">Option 2</SlRadioButton>
<SlRadioButton pill value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton pill size="medium" value="1">Option 1</SlRadioButton>
<SlRadioButton pill size="medium" value="2">Option 2</SlRadioButton>
<SlRadioButton pill size="medium" value="3">Option 3</SlRadioButton>
<SlRadioGroup size="medium" label="Select an option" name="a" value="1">
<SlRadioButton pill value="1">Option 1</SlRadioButton>
<SlRadioButton pill value="2">Option 2</SlRadioButton>
<SlRadioButton pill value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton pill size="large" value="1">Option 1</SlRadioButton>
<SlRadioButton pill size="large" value="2">Option 2</SlRadioButton>
<SlRadioButton pill size="large" value="3">Option 3</SlRadioButton>
<SlRadioGroup size="large" label="Select an option" name="a" value="1">
<SlRadioButton pill value="1">Option 1</SlRadioButton>
<SlRadioButton pill value="2">Option 2</SlRadioButton>
<SlRadioButton pill value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```

Wyświetl plik

@ -32,6 +32,10 @@ If you'd rather not use the CDN for assets, you can create a [build task](https:
Now you can start using components!
### Preact
Preact users facing type errors using components may benefit from setting "paths" in their tsconfig.json so that react types will instead resolve to preact/compat as described in [Preact's typescript documentation](https://preactjs.com/guide/v10/typescript/#typescript-preactcompat-configuration).
## Usage
### Importing Components

Wyświetl plik

@ -208,13 +208,13 @@ Shoelace ships with a file called `vscode.html-custom-data.json` that can be use
}
```
If `settings.json` already exists, simply add the above line to the root of the object. Note that you may need to restart VS Code for the changes to take affect.
If `settings.json` already exists, simply add the above line to the root of the object. Note that you may need to restart VS Code for the changes to take effect.
## JetBrains IDEs
### JetBrains IDEs
If you are using a [JetBrains IDE](https://www.jetbrains.com/) and you are installing Shoelace from NPM, the editor will automatically detect the `web-types.json` file from the package and you should immediately see component information in your editor.
If you are installing from the CDN, you can [download a local copy](https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace/cdn/web-types.json) and add it to the root of your project.
If you are installing from the CDN, you can [download a local copy](https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace/dist/web-types.json) and add it to the root of your project.
### Other Editors

Wyświetl plik

@ -14,8 +14,13 @@ New versions of Shoelace are released as-needed and generally occur when a criti
## Next
- Added the `modal` property to `<sl-dialog>` and `<sl-drawer>` to support third-party modals [#1571]
- Fixed a bug in the autoloader causing it to register non-Shoelace elements [#1563]
- Fixed a bug in `<sl-switch>` that resulted in improper spacing between the label and the required asterisk [#1540]
- Fixed a bug in `<sl-icon>` that caused icons to not load when the default library used a sprite [#1572]
- Removed error when a missing popup anchor is provided [#1548]
- Updated `@ctrl/tinycolor` to 4.0.1 [#1542]
- Updated Bootstrap Icons to 1.11.0
## 2.8.0

28
package-lock.json wygenerowano
Wyświetl plik

@ -11,7 +11,7 @@
"dependencies": {
"@ctrl/tinycolor": "^4.0.1",
"@floating-ui/dom": "^1.2.1",
"@lit-labs/react": "^2.0.1",
"@lit-labs/react": "^2.0.3",
"@shoelace-style/animations": "^1.1.0",
"@shoelace-style/localize": "^3.1.1",
"composed-offset-position": "^0.0.4",
@ -30,7 +30,7 @@
"@web/test-runner": "^0.15.0",
"@web/test-runner-commands": "^0.6.5",
"@web/test-runner-playwright": "^0.9.0",
"bootstrap-icons": "^1.10.5",
"bootstrap-icons": "^1.11.0",
"browser-sync": "^2.29.3",
"chalk": "^5.2.0",
"change-case": "^4.1.2",
@ -1474,9 +1474,9 @@
}
},
"node_modules/@lit-labs/react": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-2.0.1.tgz",
"integrity": "sha512-Nj+XB3HamqaWefN91lpFPJaqjJ78XzGkPWCedB4jyH22GBFEenpE9A/h8B/2dnIGXtNtd9D/RFpUdQ/dBtWFqA==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-2.0.3.tgz",
"integrity": "sha512-lSvWbTrbxWqYv/iiOwbAEJfFZrKjO/QjJ4IEXhg43sdD5fNFz4wRXpVsntfVn4DnxpQd+NVRnrsF2USgK0XCTw==",
"peerDependencies": {
"@types/react": "17 || 18"
}
@ -3988,9 +3988,9 @@
}
},
"node_modules/bootstrap-icons": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.10.5.tgz",
"integrity": "sha512-oSX26F37V7QV7NCE53PPEL45d7EGXmBgHG3pDpZvcRaKVzWMqIRL9wcqJUyEha1esFtM3NJzvmxFXDxjJYD0jQ==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.0.tgz",
"integrity": "sha512-bLTbtACfUqwZf6f/xUYUb7bTRZC68QaQwwy9h1b96NPKfnwqzSatHqDypW6R2CBW7zUE7lP+O93GdZuPY3RIHA==",
"dev": true,
"funding": [
{
@ -18290,9 +18290,9 @@
}
},
"@lit-labs/react": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-2.0.1.tgz",
"integrity": "sha512-Nj+XB3HamqaWefN91lpFPJaqjJ78XzGkPWCedB4jyH22GBFEenpE9A/h8B/2dnIGXtNtd9D/RFpUdQ/dBtWFqA==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-2.0.3.tgz",
"integrity": "sha512-lSvWbTrbxWqYv/iiOwbAEJfFZrKjO/QjJ4IEXhg43sdD5fNFz4wRXpVsntfVn4DnxpQd+NVRnrsF2USgK0XCTw==",
"requires": {}
},
"@lit-labs/ssr-dom-shim": {
@ -20213,9 +20213,9 @@
}
},
"bootstrap-icons": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.10.5.tgz",
"integrity": "sha512-oSX26F37V7QV7NCE53PPEL45d7EGXmBgHG3pDpZvcRaKVzWMqIRL9wcqJUyEha1esFtM3NJzvmxFXDxjJYD0jQ==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.0.tgz",
"integrity": "sha512-bLTbtACfUqwZf6f/xUYUb7bTRZC68QaQwwy9h1b96NPKfnwqzSatHqDypW6R2CBW7zUE7lP+O93GdZuPY3RIHA==",
"dev": true
},
"boxen": {

Wyświetl plik

@ -6,7 +6,7 @@
"author": "Cory LaViska",
"license": "MIT",
"customElements": "dist/custom-elements.json",
"web-types": "./web-types.json",
"web-types": "./dist/web-types.json",
"type": "module",
"types": "dist/shoelace.d.ts",
"jsdelivr": "./cdn/shoelace-autoloader.js",
@ -62,7 +62,7 @@
"dependencies": {
"@ctrl/tinycolor": "^4.0.1",
"@floating-ui/dom": "^1.2.1",
"@lit-labs/react": "^2.0.1",
"@lit-labs/react": "^2.0.3",
"@shoelace-style/animations": "^1.1.0",
"@shoelace-style/localize": "^3.1.1",
"composed-offset-position": "^0.0.4",
@ -81,7 +81,7 @@
"@web/test-runner": "^0.15.0",
"@web/test-runner-commands": "^0.6.5",
"@web/test-runner-playwright": "^0.9.0",
"bootstrap-icons": "^1.10.5",
"bootstrap-icons": "^1.11.0",
"browser-sync": "^2.29.3",
"chalk": "^5.2.0",
"change-case": "^4.1.2",

Wyświetl plik

@ -34,6 +34,9 @@ const shoelaceVersion = JSON.stringify(packageData.version.toString());
async function buildTheDocs(watch = false) {
return new Promise(async (resolve, reject) => {
const afterSignal = '[eleventy.after]';
// Totally non-scientific way to handle errors. Perhaps its just better to resolve on stderr? :shrug:
const errorSignal = 'Original error stack trace:';
const args = ['@11ty/eleventy', '--quiet'];
const output = [];
@ -65,6 +68,13 @@ async function buildTheDocs(watch = false) {
resolve({ child, output });
}
});
child.stderr.on('data', data => {
if (data.includes(errorSignal)) {
// This closes the dev server, not sure if thats what we want?
reject(output);
}
});
} else {
child.on('close', () => {
resolve({ child, output });
@ -205,9 +215,8 @@ await nextTask('Running the TypeScript compiler', () => {
});
// Copy the above steps to the CDN directory directly so we don't need to twice the work for nothing.
await nextTask(`Copying Web Types, Themes, Icons, and TS Types to "${cdndir}"`, async () => {
await nextTask(`Themes, Icons, and TS Types to "${cdndir}"`, async () => {
await deleteAsync(cdndir);
await copy('./web-types.json', `${outdir}/web-types.json`);
await copy(outdir, cdndir);
});

Wyświetl plik

@ -87,6 +87,7 @@ export default class SlDetails extends ShoelaceElement {
}
disconnectedCallback() {
super.disconnectedCallback();
this.detailsObserver.disconnect();
}

Wyświetl plik

@ -60,6 +60,10 @@ import type { CSSResultGroup } from 'lit';
* @animation dialog.denyClose - The animation to use when a request to close the dialog is denied.
* @animation dialog.overlay.show - The animation to use when showing the dialog's overlay.
* @animation dialog.overlay.hide - The animation to use when hiding the dialog's overlay.
*
* @property modal - Exposes the internal modal utility that controls focus trapping. To temporarily disable focus
* trapping and allow third-party modals spawned from an active Shoelace modal, call `modal.activateExternal()` when
* the third-party modal opens. Upon closing, call `modal.deactivateExternal()` to restore Shoelace's focus trapping.
*/
export default class SlDialog extends ShoelaceElement {
static styles: CSSResultGroup = styles;
@ -69,8 +73,8 @@ export default class SlDialog extends ShoelaceElement {
private readonly hasSlotController = new HasSlotController(this, 'footer');
private readonly localize = new LocalizeController(this);
private modal = new Modal(this);
private originalTrigger: HTMLElement | null;
public modal = new Modal(this);
@query('.dialog') dialog: HTMLElement;
@query('.dialog__panel') panel: HTMLElement;

Wyświetl plik

@ -68,6 +68,10 @@ import type { CSSResultGroup } from 'lit';
* @animation drawer.denyClose - The animation to use when a request to close the drawer is denied.
* @animation drawer.overlay.show - The animation to use when showing the drawer's overlay.
* @animation drawer.overlay.hide - The animation to use when hiding the drawer's overlay.
*
* @property modal - Exposes the internal modal utility that controls focus trapping. To temporarily disable focus
* trapping and allow third-party modals spawned from an active Shoelace modal, call `modal.activateExternal()` when
* the third-party modal opens. Upon closing, call `modal.deactivateExternal()` to restore Shoelace's focus trapping.
*/
export default class SlDrawer extends ShoelaceElement {
static styles: CSSResultGroup = styles;
@ -75,8 +79,8 @@ export default class SlDrawer extends ShoelaceElement {
private readonly hasSlotController = new HasSlotController(this, 'footer');
private readonly localize = new LocalizeController(this);
private modal = new Modal(this);
private originalTrigger: HTMLElement | null;
public modal = new Modal(this);
@query('.drawer') drawer: HTMLElement;
@query('.drawer__panel') panel: HTMLElement;

Wyświetl plik

@ -15,6 +15,11 @@ type SVGResult = HTMLTemplateResult | SVGSVGElement | typeof RETRYABLE_ERROR | t
let parser: DOMParser;
const iconCache = new Map<string, Promise<SVGResult>>();
interface IconSource {
url?: string;
fromLibrary: boolean;
}
/**
* @summary Icons are symbols that can be used to represent various options within an application.
* @documentation https://shoelace.style/components/icon
@ -104,12 +109,19 @@ export default class SlIcon extends ShoelaceElement {
unwatchIcon(this);
}
private getUrl() {
private getIconSource(): IconSource {
const library = getIconLibrary(this.library);
if (this.name && library) {
return library.resolver(this.name);
return {
url: library.resolver(this.name),
fromLibrary: true
};
}
return this.src;
return {
url: this.src,
fromLibrary: false
};
}
@watch('label')
@ -129,8 +141,8 @@ export default class SlIcon extends ShoelaceElement {
@watch(['name', 'src', 'library'])
async setIcon() {
const library = getIconLibrary(this.library);
const url = this.getUrl();
const { url, fromLibrary } = this.getIconSource();
const library = fromLibrary ? getIconLibrary(this.library) : undefined;
if (!url) {
this.svg = null;
@ -154,7 +166,7 @@ export default class SlIcon extends ShoelaceElement {
iconCache.delete(url);
}
if (url !== this.getUrl()) {
if (url !== this.getIconSource().url) {
// If the url has changed while fetching the icon, ignore this request
return;
}

Wyświetl plik

@ -52,6 +52,7 @@ export default class SlMutationObserver extends ShoelaceElement {
}
disconnectedCallback() {
super.disconnectedCallback();
this.stopObserver();
}

Wyświetl plik

@ -198,6 +198,7 @@ export default class SlPopup extends ShoelaceElement {
}
disconnectedCallback() {
super.disconnectedCallback();
this.stop();
}
@ -246,13 +247,10 @@ export default class SlPopup extends ShoelaceElement {
this.anchorEl = this.anchorEl.assignedElements({ flatten: true })[0] as HTMLElement;
}
if (!this.anchorEl) {
throw new Error(
'Invalid anchor element: no anchor could be found using the anchor slot or the anchor attribute.'
);
// If the anchor is valid, start it up
if (this.anchorEl) {
this.start();
}
this.start();
}
private start() {

Wyświetl plik

@ -117,6 +117,7 @@ export default class SlTabGroup extends ShoelaceElement {
}
disconnectedCallback() {
super.disconnectedCallback();
this.mutationObserver.disconnect();
this.resizeObserver.unobserve(this.nav);
}

Wyświetl plik

@ -5,6 +5,7 @@ let activeModals: HTMLElement[] = [];
export default class Modal {
element: HTMLElement;
isExternalActivated: boolean;
tabDirection: 'forward' | 'backward' = 'forward';
currentFocus: HTMLElement | null;
@ -12,6 +13,7 @@ export default class Modal {
this.element = element;
}
/** Activates focus trapping. */
activate() {
activeModals.push(this.element);
document.addEventListener('focusin', this.handleFocusIn);
@ -19,6 +21,7 @@ export default class Modal {
document.addEventListener('keyup', this.handleKeyUp);
}
/** Deactivates focus trapping. */
deactivate() {
activeModals = activeModals.filter(modal => modal !== this.element);
this.currentFocus = null;
@ -27,13 +30,24 @@ export default class Modal {
document.removeEventListener('keyup', this.handleKeyUp);
}
/** Determines if this modal element is currently active or not. */
isActive() {
// The "active" modal is always the most recent one shown
return activeModals[activeModals.length - 1] === this.element;
}
checkFocus() {
if (this.isActive()) {
/** Activates external modal behavior and temporarily disables focus trapping. */
activateExternal() {
this.isExternalActivated = true;
}
/** Deactivates external modal behavior and re-enables focus trapping. */
deactivateExternal() {
this.isExternalActivated = false;
}
private checkFocus() {
if (this.isActive() && !this.isExternalActivated) {
const tabbableElements = getTabbableElements(this.element);
if (!this.element.matches(':focus-within')) {
const start = tabbableElements[0];
@ -56,11 +70,9 @@ export default class Modal {
return getTabbableElements(this.element).findIndex(el => el === this.currentFocus);
}
/**
* Checks if the `startElement` is already focused. This is important if the modal already
* has an existing focus prior to the first tab key.
*/
startElementAlreadyFocused(startElement: HTMLElement) {
// Checks if the `startElement` is already focused. This is important if the modal already has an existing focus prior
// to the first tab key.
private startElementAlreadyFocused(startElement: HTMLElement) {
for (const activeElement of activeElements()) {
if (startElement === activeElement) {
return true;
@ -70,8 +82,8 @@ export default class Modal {
return false;
}
handleKeyDown = (event: KeyboardEvent) => {
if (event.key !== 'Tab') return;
private handleKeyDown = (event: KeyboardEvent) => {
if (event.key !== 'Tab' || this.isExternalActivated) return;
if (event.shiftKey) {
this.tabDirection = 'backward';

Wyświetl plik

@ -15,13 +15,13 @@ const observer = new MutationObserver(mutations => {
*/
export async function discover(root: Element | ShadowRoot) {
const rootTagName = root instanceof Element ? root.tagName.toLowerCase() : '';
const rootIsCustomElement = rootTagName?.includes('-');
const rootIsShoelaceElement = rootTagName?.startsWith('sl-');
const tags = [...root.querySelectorAll(':not(:defined)')]
.map(el => el.tagName.toLowerCase())
.filter(tag => tag.startsWith('sl-'));
// If the root element is an undefined custom element, add it to the list
if (rootIsCustomElement && !customElements.get(rootTagName)) {
// If the root element is an undefined Shoelace component, add it to the list
if (rootIsShoelaceElement && !customElements.get(rootTagName)) {
tags.push(rootTagName);
}
@ -35,14 +35,14 @@ export async function discover(root: Element | ShadowRoot) {
* Registers an element by tag name.
*/
function register(tagName: string): Promise<void> {
const tagWithoutPrefix = tagName.replace(/^sl-/i, '');
const path = getBasePath(`components/${tagWithoutPrefix}/${tagWithoutPrefix}.js`);
// If the element is already defined, there's nothing more to do
if (customElements.get(tagName)) {
return Promise.resolve();
}
const tagWithoutPrefix = tagName.replace(/^sl-/i, '');
const path = getBasePath(`components/${tagWithoutPrefix}/${tagWithoutPrefix}.js`);
// Register it
return new Promise((resolve, reject) => {
import(path).then(() => resolve()).catch(() => reject(new Error(`Unable to autoload <${tagName}> from ${path}`)));

Wyświetl plik

@ -13,7 +13,7 @@ const translation: Translation = {
copy: 'Kopieren',
currentValue: 'Aktueller Wert',
error: 'Fehler',
goToSlide: (slide, count) => `Gehen Sie zu Folie ${slide} von ${count}`,
goToSlide: (slide, count) => `Zu Folie ${slide} von ${count} gehen`,
hidePassword: 'Passwort verbergen',
loading: 'Wird geladen',
nextSlide: 'Nächste Folie',
@ -28,7 +28,7 @@ const translation: Translation = {
resize: 'Größe ändern',
scrollToEnd: 'Zum Ende scrollen',
scrollToStart: 'Zum Anfang scrollen',
selectAColorFromTheScreen: 'Wähle eine Farbe vom Bildschirm',
selectAColorFromTheScreen: 'Farbe vom Bildschirm auswählen',
showPassword: 'Passwort anzeigen',
slideNum: slide => `Folie ${slide}`,
toggleColorFormat: 'Farbformat umschalten'