kopia lustrzana https://github.com/shoelace-style/shoelace
Merge branch 'next' into file-input
commit
eac0a840f4
|
@ -1,9 +1,8 @@
|
|||
_site
|
||||
.cache
|
||||
.DS_Store
|
||||
cdn
|
||||
dist
|
||||
docs/assets/images/sprite.svg
|
||||
node_modules
|
||||
src/react
|
||||
cdn
|
||||
web-types.json
|
|
@ -203,6 +203,7 @@ export default {
|
|||
]
|
||||
}),
|
||||
customElementJetBrainsPlugin({
|
||||
outdir: './dist',
|
||||
excludeCss: true,
|
||||
referencesTemplate: (_, tag) => {
|
||||
return {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -87,6 +87,7 @@ export default class SlDetails extends ShoelaceElement {
|
|||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.detailsObserver.disconnect();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ export default class SlMutationObserver extends ShoelaceElement {
|
|||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.stopObserver();
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -117,6 +117,7 @@ export default class SlTabGroup extends ShoelaceElement {
|
|||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.mutationObserver.disconnect();
|
||||
this.resizeObserver.unobserve(this.nav);
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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}`)));
|
||||
|
|
|
@ -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'
|
||||
|
|
Ładowanie…
Reference in New Issue