fix(ui): respect iframe width and height in org landing embed section (#706)
Embed iframes were placed inside flex column layouts without shrink protection, so authored width/height attributes could be ignored when the preview panel was narrower than the iframe. Parse dimensions from pasted embed code and size a dedicated host wrapper so the iframe renders at the intended dimensions while still scaling down with max-width: 100%. Co-authored-by: Cursor Agent <[email protected]> Co-authored-by: Rotimi Best <[email protected]>
This commit is contained in:
co-authored by
Cursor Agent
Rotimi Best
parent
1ec0c34ba7
commit
a337363965
@@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { sanitizeEmbedHtml } from '../../tools/sanitize';
|
||||||
|
import { parseEmbedIframeDimensions } from './landing-page-utils';
|
||||||
|
|
||||||
|
let { code }: { code: string } = $props();
|
||||||
|
|
||||||
|
const dimensions = $derived(parseEmbedIframeDimensions(code));
|
||||||
|
const sanitizedCode = $derived(sanitizeEmbedHtml(code));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="landing-embed-host ui:inline-flex ui:max-w-full ui:shrink-0 ui:justify-center"
|
||||||
|
style:width={dimensions.width}
|
||||||
|
style:max-width="100%"
|
||||||
|
>
|
||||||
|
<div class="landing-embed-frame ui:w-full" style:height={dimensions.height}>
|
||||||
|
{@html sanitizedCode}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:global(.landing-embed-frame iframe) {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { OrgLandingPageLabels, OrgLandingPageTheme, LandingPageEmbed } from './types';
|
import type { OrgLandingPageLabels, OrgLandingPageTheme, LandingPageEmbed } from './types';
|
||||||
import SecondaryActionButton from './secondary-action-button.svelte';
|
import SecondaryActionButton from './secondary-action-button.svelte';
|
||||||
import EditableLandingSection from './editable-section.svelte';
|
import EditableLandingSection from './editable-section.svelte';
|
||||||
import { sanitizeEmbedHtml } from '../../tools/sanitize';
|
import EmbedFrame from './embed-frame.svelte';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
embed,
|
embed,
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
style="border: 1px solid #262a35; background: #0f1218; box-shadow: 0 30px 90px -30px rgba(0,0,0,0.7);"
|
style="border: 1px solid #262a35; background: #0f1218; box-shadow: 0 30px 90px -30px rgba(0,0,0,0.7);"
|
||||||
>
|
>
|
||||||
<div class="ui:p-5 ui:overflow-x-auto" style="color: #e9eaed;">
|
<div class="ui:p-5 ui:overflow-x-auto" style="color: #e9eaed;">
|
||||||
{@html sanitizeEmbedHtml(embed.code)}
|
<EmbedFrame code={embed.code} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -76,9 +76,7 @@
|
|||||||
<SecondaryActionButton href={embed.secondaryAction.href} label={embed.secondaryAction.label} {variant} />
|
<SecondaryActionButton href={embed.secondaryAction.href} label={embed.secondaryAction.label} {variant} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if embed.code}
|
{#if embed.code}
|
||||||
<div class="ui:flex ui:w-full ui:justify-center">
|
<EmbedFrame code={embed.code} />
|
||||||
{@html sanitizeEmbedHtml(embed.code)}
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -95,9 +93,7 @@
|
|||||||
<SecondaryActionButton href={embed.secondaryAction.href} label={embed.secondaryAction.label} {variant} />
|
<SecondaryActionButton href={embed.secondaryAction.href} label={embed.secondaryAction.label} {variant} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if embed.code}
|
{#if embed.code}
|
||||||
<div class="ui:flex ui:w-full ui:justify-center">
|
<EmbedFrame code={embed.code} />
|
||||||
{@html sanitizeEmbedHtml(embed.code)}
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -84,3 +84,42 @@ export function defaultExercisesLabel(count: number): string {
|
|||||||
export function defaultEnrolledLabel(count: number): string {
|
export function defaultEnrolledLabel(count: number): string {
|
||||||
return `${count.toLocaleString()} enrolled`;
|
return `${count.toLocaleString()} enrolled`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EmbedIframeDimensions = {
|
||||||
|
width?: string;
|
||||||
|
height?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function toCssSize(value: string | undefined): string | undefined {
|
||||||
|
if (!value) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmed = value.trim();
|
||||||
|
if (!trimmed) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^\d+(\.\d+)?$/.test(trimmed)) {
|
||||||
|
return `${trimmed}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reads width/height from the first iframe in a pasted embed snippet. */
|
||||||
|
export function parseEmbedIframeDimensions(html: string): EmbedIframeDimensions {
|
||||||
|
const iframeMatch = html.match(/<iframe\b[^>]*>/i);
|
||||||
|
if (!iframeMatch) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const tag = iframeMatch[0];
|
||||||
|
const widthRaw = tag.match(/\bwidth\s*=\s*["']?([^"'\s>]+)["']?/i)?.[1];
|
||||||
|
const heightRaw = tag.match(/\bheight\s*=\s*["']?([^"'\s>]+)["']?/i)?.[1];
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: toCssSize(widthRaw),
|
||||||
|
height: toCssSize(heightRaw)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user