kopia lustrzana https://github.com/shoelace-style/shoelace
Add sl-include
rodzic
e22763ea37
commit
3514e88dcd
|
@ -49,6 +49,7 @@
|
|||
- Utility Components
|
||||
- [Animation](/components/animation.md)
|
||||
- [Format Bytes](/components/format-bytes.md)
|
||||
- [Include](/components/include.md)
|
||||
- [Responsive Embed](/components/responsive-embed.md)
|
||||
|
||||
- Design Tokens
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<p style="margin-top: 0;">
|
||||
The content in this example was included from <a href="/assets/examples/include.html" target="_blank">a separate file</a>. 🤯
|
||||
</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lectus vestibulum mattis ullamcorper velit sed ullamcorper morbi. Fringilla urna porttitor rhoncus dolor purus non enim. Nullam vehicula ipsum a arcu cursus vitae congue mauris. Gravida in fermentum et sollicitudin.</p>
|
||||
<p>Cursus sit amet dictum sit amet justo donec enim. Sed id semper risus in hendrerit gravida. Viverra accumsan in nisl nisi scelerisque eu ultrices vitae. Et molestie ac feugiat sed lectus vestibulum mattis ullamcorper velit. Nec ullamcorper sit amet risus nullam. Et egestas quis ipsum suspendisse ultrices gravida dictum. Lorem donec massa sapien faucibus et molestie. A cras semper auctor neque vitae.</p>
|
|
@ -0,0 +1,39 @@
|
|||
# Include
|
||||
|
||||
[component-header:sl-include]
|
||||
|
||||
Includes give you the power to embed external HTML files into the page.
|
||||
|
||||
Included files are asynchronously requested using `window.fetch()`. Requests are cached, so the same file can be included multiple times, but only one request will be made.
|
||||
|
||||
The included content will be inserted into the `<sl-include>` element's default slot so it can be easily accessed and styled through the light DOM.
|
||||
|
||||
```html preview
|
||||
<sl-include src="/assets/examples/include.html"></sl-include>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Listening for Events
|
||||
|
||||
When an include file loads successfully, the `sl-load` event will be emitted. You can listen for this event to add custom loading logic to your includes.
|
||||
|
||||
If the request fails, the `sl-error` event will be emitted. In this case, `event.detail.status` will contain the resulting HTTP status code of the request, e.g. 404 (not found).
|
||||
|
||||
```html
|
||||
<sl-include src="/assets/examples/include.html"></sl-include>
|
||||
|
||||
<script>
|
||||
const include = document.querySelector('sl-include');
|
||||
|
||||
include.addEventListener('sl-load', () => {
|
||||
console.log('Success');
|
||||
});
|
||||
|
||||
include.addEventListener('sl-error', event => {
|
||||
console.log('Error', event.detail.status);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
[component-metadata:sl-include]
|
|
@ -10,6 +10,7 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
|
|||
|
||||
- Added `label` slot to `sl-input`, `sl-select`, and `sl-textarea` [#248](https://github.com/shoelace-style/shoelace/issues/248)
|
||||
- Added `label` slot to `sl-dialog` and `sl-drawer`
|
||||
- Added experimental `sl-include` component
|
||||
- Fixed a bug where initial transitions didn't show in `sl-dialog` and `sl-drawer` [#247](https://github.com/shoelace-style/shoelace/issues/247)
|
||||
- Fixed a bug where indeterminate checkboxes would maintain the indeterminate state when toggled
|
||||
- Fixed a bug where concurrent active modals (i.e. dialog, drawer) would try to steal focus from each other
|
||||
|
|
|
@ -529,6 +529,16 @@ export namespace Components {
|
|||
*/
|
||||
"position": number;
|
||||
}
|
||||
interface SlInclude {
|
||||
/**
|
||||
* The fetch mode to use.
|
||||
*/
|
||||
"mode": 'cors' | 'no-cors' | 'same-origin';
|
||||
/**
|
||||
* The location of the HTML file to include.
|
||||
*/
|
||||
"src": string;
|
||||
}
|
||||
interface SlInput {
|
||||
/**
|
||||
* The input's autocaptialize attribute.
|
||||
|
@ -1286,6 +1296,12 @@ declare global {
|
|||
prototype: HTMLSlImageComparerElement;
|
||||
new (): HTMLSlImageComparerElement;
|
||||
};
|
||||
interface HTMLSlIncludeElement extends Components.SlInclude, HTMLStencilElement {
|
||||
}
|
||||
var HTMLSlIncludeElement: {
|
||||
prototype: HTMLSlIncludeElement;
|
||||
new (): HTMLSlIncludeElement;
|
||||
};
|
||||
interface HTMLSlInputElement extends Components.SlInput, HTMLStencilElement {
|
||||
}
|
||||
var HTMLSlInputElement: {
|
||||
|
@ -1432,6 +1448,7 @@ declare global {
|
|||
"sl-icon-button": HTMLSlIconButtonElement;
|
||||
"sl-icon-library": HTMLSlIconLibraryElement;
|
||||
"sl-image-comparer": HTMLSlImageComparerElement;
|
||||
"sl-include": HTMLSlIncludeElement;
|
||||
"sl-input": HTMLSlInputElement;
|
||||
"sl-menu": HTMLSlMenuElement;
|
||||
"sl-menu-divider": HTMLSlMenuDividerElement;
|
||||
|
@ -2021,6 +2038,24 @@ declare namespace LocalJSX {
|
|||
*/
|
||||
"position"?: number;
|
||||
}
|
||||
interface SlInclude {
|
||||
/**
|
||||
* The fetch mode to use.
|
||||
*/
|
||||
"mode"?: 'cors' | 'no-cors' | 'same-origin';
|
||||
/**
|
||||
* Emitted when the included file fails to load due to an error.
|
||||
*/
|
||||
"onSl-error"?: (event: CustomEvent<{ status: number }>) => void;
|
||||
/**
|
||||
* Emitted when the included file is loaded.
|
||||
*/
|
||||
"onSl-load"?: (event: CustomEvent<any>) => void;
|
||||
/**
|
||||
* The location of the HTML file to include.
|
||||
*/
|
||||
"src"?: string;
|
||||
}
|
||||
interface SlInput {
|
||||
/**
|
||||
* The input's autocaptialize attribute.
|
||||
|
@ -2670,6 +2705,7 @@ declare namespace LocalJSX {
|
|||
"sl-icon-button": SlIconButton;
|
||||
"sl-icon-library": SlIconLibrary;
|
||||
"sl-image-comparer": SlImageComparer;
|
||||
"sl-include": SlInclude;
|
||||
"sl-input": SlInput;
|
||||
"sl-menu": SlMenu;
|
||||
"sl-menu-divider": SlMenuDivider;
|
||||
|
@ -2716,6 +2752,7 @@ declare module "@stencil/core" {
|
|||
"sl-icon-button": LocalJSX.SlIconButton & JSXBase.HTMLAttributes<HTMLSlIconButtonElement>;
|
||||
"sl-icon-library": LocalJSX.SlIconLibrary & JSXBase.HTMLAttributes<HTMLSlIconLibraryElement>;
|
||||
"sl-image-comparer": LocalJSX.SlImageComparer & JSXBase.HTMLAttributes<HTMLSlImageComparerElement>;
|
||||
"sl-include": LocalJSX.SlInclude & JSXBase.HTMLAttributes<HTMLSlIncludeElement>;
|
||||
"sl-input": LocalJSX.SlInput & JSXBase.HTMLAttributes<HTMLSlInputElement>;
|
||||
"sl-menu": LocalJSX.SlMenu & JSXBase.HTMLAttributes<HTMLSlMenuElement>;
|
||||
"sl-menu-divider": LocalJSX.SlMenuDivider & JSXBase.HTMLAttributes<HTMLSlMenuDividerElement>;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
:host {
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import { Component, Element, Event, EventEmitter, Prop, State, Watch, h } from '@stencil/core';
|
||||
|
||||
export interface IncludeFile {
|
||||
ok: boolean;
|
||||
status: number;
|
||||
html: string;
|
||||
}
|
||||
|
||||
const includeFiles = new Map<string, Promise<IncludeFile>>();
|
||||
|
||||
/**
|
||||
* @since 2.0
|
||||
* @status experimental
|
||||
*/
|
||||
|
||||
@Component({
|
||||
tag: 'sl-include',
|
||||
styleUrl: 'include.scss',
|
||||
shadow: true
|
||||
})
|
||||
export class Include {
|
||||
@Element() host: HTMLSlIncludeElement;
|
||||
|
||||
@State() html = '';
|
||||
|
||||
/** The location of the HTML file to include. */
|
||||
@Prop() src: string;
|
||||
|
||||
/** The fetch mode to use. */
|
||||
@Prop() mode: 'cors' | 'no-cors' | 'same-origin' = 'cors';
|
||||
|
||||
/** Emitted when the included file is loaded. */
|
||||
@Event({ eventName: 'sl-load' }) slLoad: EventEmitter;
|
||||
|
||||
/** Emitted when the included file fails to load due to an error. */
|
||||
@Event({ eventName: 'sl-error' }) slError: EventEmitter<{ status: number }>;
|
||||
|
||||
@Watch('src')
|
||||
handleSrcChange() {
|
||||
this.loadSource();
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
this.loadSource();
|
||||
}
|
||||
|
||||
async requestFile(src: string) {
|
||||
if (includeFiles.has(src)) {
|
||||
return includeFiles.get(src);
|
||||
} else {
|
||||
const request = fetch(src, { mode: this.mode }).then(async response => {
|
||||
return {
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
html: await response.text()
|
||||
};
|
||||
});
|
||||
includeFiles.set(src, request);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
async loadSource() {
|
||||
const src = this.src;
|
||||
const file = await this.requestFile(src);
|
||||
|
||||
// If the src changed since the request started do nothing, otherwise we risk overwriting a subsequent response
|
||||
if (src !== this.src) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.ok) {
|
||||
this.slError.emit({ status: file.status });
|
||||
return;
|
||||
}
|
||||
|
||||
this.host.innerHTML = file.html;
|
||||
this.slLoad.emit();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <slot />;
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue