diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 8166ce168..1b12550c7 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,5 +1,4 @@ - Getting Started - - [Overview](/) - [Installation](/getting-started/installation.md) - [Usage](/getting-started/usage.md) @@ -9,9 +8,7 @@ - [Community](/getting-started/community.md) - Components - - [Alert](/components/alert.md) - - [Animation](/components/animation.md) - [Avatar](/components/avatar.md) - [Badge](/components/badge.md) - [Button](/components/button.md) @@ -49,7 +46,9 @@ - [Tooltip](/components/tooltip.md) - Utility Components + - [Animation](/components/animation.md) - [Format Bytes](/components/format-bytes.md) + - [Responsive Embed](/components/responsive-embed.md) - Design Tokens - [Typography](/tokens/typography.md) diff --git a/docs/components/responsive-embed.md b/docs/components/responsive-embed.md new file mode 100644 index 000000000..f3e333157 --- /dev/null +++ b/docs/components/responsive-embed.md @@ -0,0 +1,27 @@ +# Responsive Embed + +[component-header:sl-responsive-embed] + +Displays embedded media in a responsive manner based on its aspect ratio. + +You can embed any element of the ` + +``` + +## Examples + +### Aspect Ratio + +To set the aspect ratio, use the `aspect-ratio` attribute. + +```html preview + + + +``` + +[component-metadata:sl-responsive-embed] diff --git a/src/components.d.ts b/src/components.d.ts index 08b99ee98..cb5891b3e 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -785,6 +785,12 @@ export namespace Components { */ "value": number; } + interface SlResponsiveEmbed { + /** + * The aspect ratio of the embedded media in the format of `width:height`, e.g. `16:9`, `4:3`, or `1:1`. Ratios not in this format will be ignored. + */ + "aspectRatio": string; + } interface SlSelect { /** * Set to true to add a clear button when the select is populated. @@ -1282,6 +1288,12 @@ declare global { prototype: HTMLSlRatingElement; new (): HTMLSlRatingElement; }; + interface HTMLSlResponsiveEmbedElement extends Components.SlResponsiveEmbed, HTMLStencilElement { + } + var HTMLSlResponsiveEmbedElement: { + prototype: HTMLSlResponsiveEmbedElement; + new (): HTMLSlResponsiveEmbedElement; + }; interface HTMLSlSelectElement extends Components.SlSelect, HTMLStencilElement { } var HTMLSlSelectElement: { @@ -1371,6 +1383,7 @@ declare global { "sl-radio": HTMLSlRadioElement; "sl-range": HTMLSlRangeElement; "sl-rating": HTMLSlRatingElement; + "sl-responsive-embed": HTMLSlResponsiveEmbedElement; "sl-select": HTMLSlSelectElement; "sl-skeleton": HTMLSlSkeletonElement; "sl-spinner": HTMLSlSpinnerElement; @@ -2211,6 +2224,12 @@ declare namespace LocalJSX { */ "value"?: number; } + interface SlResponsiveEmbed { + /** + * The aspect ratio of the embedded media in the format of `width:height`, e.g. `16:9`, `4:3`, or `1:1`. Ratios not in this format will be ignored. + */ + "aspectRatio"?: string; + } interface SlSelect { /** * Set to true to add a clear button when the select is populated. @@ -2563,6 +2582,7 @@ declare namespace LocalJSX { "sl-radio": SlRadio; "sl-range": SlRange; "sl-rating": SlRating; + "sl-responsive-embed": SlResponsiveEmbed; "sl-select": SlSelect; "sl-skeleton": SlSkeleton; "sl-spinner": SlSpinner; @@ -2607,6 +2627,7 @@ declare module "@stencil/core" { "sl-radio": LocalJSX.SlRadio & JSXBase.HTMLAttributes; "sl-range": LocalJSX.SlRange & JSXBase.HTMLAttributes; "sl-rating": LocalJSX.SlRating & JSXBase.HTMLAttributes; + "sl-responsive-embed": LocalJSX.SlResponsiveEmbed & JSXBase.HTMLAttributes; "sl-select": LocalJSX.SlSelect & JSXBase.HTMLAttributes; "sl-skeleton": LocalJSX.SlSkeleton & JSXBase.HTMLAttributes; "sl-spinner": LocalJSX.SlSpinner & JSXBase.HTMLAttributes; diff --git a/src/components/responsive-embed/responsive-embed.scss b/src/components/responsive-embed/responsive-embed.scss new file mode 100644 index 000000000..6ba06182c --- /dev/null +++ b/src/components/responsive-embed/responsive-embed.scss @@ -0,0 +1,19 @@ +@import 'component'; + +:host { + display: block; +} + +.responsive-embed { + position: relative; + + ::slotted(embed), + ::slotted(iframe), + ::slotted(object) { + position: absolute !important; + top: 0 !important; + left: 0 !important; + width: 100% !important; + height: 100% !important; + } +} diff --git a/src/components/responsive-embed/responsive-embed.tsx b/src/components/responsive-embed/responsive-embed.tsx new file mode 100644 index 000000000..4d14cace2 --- /dev/null +++ b/src/components/responsive-embed/responsive-embed.tsx @@ -0,0 +1,52 @@ +import { Component, Prop, Watch, h } from '@stencil/core'; + +/** + * @since 2.0 + * @status stable + * + * @part base - The component's base wrapper. + */ + +@Component({ + tag: 'sl-responsive-embed', + styleUrl: 'responsive-embed.scss', + shadow: true +}) +export class ResponsiveEmbed { + base: HTMLElement; + + /** + * The aspect ratio of the embedded media in the format of `width:height`, e.g. `16:9`, `4:3`, or `1:1`. Ratios not in + * this format will be ignored. + */ + @Prop() aspectRatio = '16:9'; + + @Watch('aspectRatio') + handleAspectRatioChange() { + this.setAspectRatio(); + } + + connectedCallback() { + this.handleSlotChange = this.handleSlotChange.bind(this); + } + + handleSlotChange() { + this.setAspectRatio(); + } + + setAspectRatio() { + const split = this.aspectRatio.split(':'); + const x = parseInt(split[0]); + const y = parseInt(split[1]); + + this.base.style.paddingBottom = x && y ? `${(y / x) * 100}%` : null; + } + + render() { + return ( +
(this.base = el)} part="base" class="responsive-embed"> + +
+ ); + } +}