Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion editor/components/app-runner/vanilla-app-runner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function VanillaRunner({
<iframe
ref={ref}
style={style}
sandbox="allow-same-origin"
sandbox="allow-same-origin allow-scripts"
srcDoc={inlinesource}
width={width}
height={height}
Expand Down
2 changes: 1 addition & 1 deletion externals/reflect-core
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { HtmlIframe } from "../html-iframe";
import type { IIframeProps } from "../html-iframe";
import type { IWHStyleWidget } from "@reflect-ui/core";
import type { WidgetKey } from "../../widget-key";

type FigmaProps = Omit<IIframeProps, "src" | "srcDoc"> & {
latlng: string;
};

export class HtmlIframeFigma extends HtmlIframe {
constructor({
key,
loading = "lazy",
allow = "fullscreen",
latlng,
...rest
}: { key: WidgetKey } & FigmaProps & IWHStyleWidget) {
super({
key,
...rest,
loading,
allow,
src: figmaurl(latlng),
});
}
}

function figmaurl(url: string): string {
const re =
/https:\/\/([\w\.-]+\.)?figma.com\/(file|proto)\/([0-9a-zA-Z]{22,128})(?:\/.*)?$/;
if (re.test(url)) {
return `https://www.figma.com/embed?embed_host=astra&url=${url}`;
} else {
return "https://figma.com/";
//
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { HtmlIframe } from "../html-iframe";
import type { IIframeProps } from "../html-iframe";
import type { IWHStyleWidget } from "@reflect-ui/core";
import type { WidgetKey } from "../../widget-key";

type GoogleMapsProps = Omit<IIframeProps, "src" | "srcDoc"> & {
q: string;
};

export class HtmlIframeGoogleMaps extends HtmlIframe {
constructor({
key,
loading = "lazy",
referrerpolicy = "no-referrer-when-downgrade",
sandbox = "allow-scripts",
q,
...rest
}: { key: WidgetKey } & GoogleMapsProps & IWHStyleWidget) {
super({
key,
...rest,
loading,
sandbox,
referrerpolicy,
src: gmapurl(q),
});
}
}

function gmapurl(q: string, apikey?: string): string {
// build query param
const query = {};
query["q"] = q;
if (apikey) {
query["key"] = apikey;
return `https://www.google.com/maps/embed/v1/place?${buildq(query)}`;
} else {
query["output"] = "embed";
return `https://maps.google.com/maps?${buildq(query)}`;
}
}

const buildq = (q: object): string =>
Object.keys(q)
.map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(q[k])}`)
.join("&");
31 changes: 31 additions & 0 deletions packages/builder-web-core/widgets-native/html-iframe-osm/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { HtmlIframe } from "../html-iframe";
import type { IIframeProps } from "../html-iframe";
import type { IWHStyleWidget } from "@reflect-ui/core";
import type { WidgetKey } from "../../widget-key";

type OsmProps = Omit<IIframeProps, "src" | "srcDoc"> & {
latlng: string;
};

export class HtmlIframeOpenStreetMap extends HtmlIframe {
constructor({
key,
loading = "lazy",
referrerpolicy = "no-referrer-when-downgrade",
latlng,
...rest
}: { key: WidgetKey } & OsmProps & IWHStyleWidget) {
super({
key,
...rest,
loading,
referrerpolicy,
src: osmurl(latlng),
});
}
}

function osmurl(latlng: string | { lat: number; lng: number }): string {
const p = typeof latlng === "string" ? latlng : `${latlng.lat},${latlng.lng}`;
return `https://www.openstreetmap.org/export/embed.html?bbox=${p}`;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { HtmlIframe } from "../html-iframe";
import type { IIframeProps } from "../html-iframe";
import type { IWHStyleWidget } from "@reflect-ui/core";
import type { WidgetKey } from "../../widget-key";

const webcamurl = "https://frames-appbox.vercel.app/webcam";

type WebcamProps = Omit<IIframeProps, "src" | "srcDoc"> & {};

export class HtmlIframeWebcam extends HtmlIframe {
constructor({
key,
allow = "camera",
loading = "lazy",
referrerpolicy = "no-referrer-when-downgrade",
sandbox = ["allow-scripts", "allow-same-origin"],
...rest
}: { key: WidgetKey } & WebcamProps & IWHStyleWidget) {
super({
key,
...rest,
allow,
loading,
sandbox,
referrerpolicy,
src: webcamurl,
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { HtmlIframe } from "../html-iframe";
import type { IIframeProps } from "../html-iframe";
import type { IWHStyleWidget } from "@reflect-ui/core";
import type { WidgetKey } from "../../widget-key";

type YoutubeProps = Omit<IIframeProps, "src" | "srcDoc"> & {
video: string;
};

export class HtmlIframeYoutube extends HtmlIframe {
constructor({
key,
loading = "lazy",
referrerpolicy = "no-referrer-when-downgrade",
sandbox = ["allow-scripts", "allow-same-origin"],
video,
...rest
}: { key: WidgetKey } & YoutubeProps & IWHStyleWidget) {
super({
key,
...rest,
loading,
sandbox,
referrerpolicy,
src: yturl(video),
});
}
}

function yturl(video: string): string {
return `https://www.youtube.com/embed/${video}`;
}
167 changes: 167 additions & 0 deletions packages/builder-web-core/widgets-native/html-iframe/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import type { ElementCssStyleData } from "@coli.codes/css";
import type { DimensionLength, IWHStyleWidget } from "@reflect-ui/core";
import { WidgetKey } from "../../widget-key";
import type { StylableJSXElementConfig } from "../../widget-core";
import { Container } from "../container";
import * as css from "@web-builder/styles";
import { JSX, JSXAttribute, StringLiteral } from "coli";

type IframeAttrSandbox =
| "allow-downloads-without-user-activation"
| "allow-downloads"
| "allow-forms"
| "allow-modals"
| "allow-orientation-lock"
| "allow-pointer-lock"
| "allow-popups"
| "allow-popups-to-escape-sandbox"
| "allow-presentation"
| "allow-same-origin"
| "allow-scripts"
| "allow-storage-access-by-user-activation"
| "allow-top-navigation"
| "allow-top-navigation-by-user-activation";

type IframeAttrReferrerPolicy =
| "no-referrer"
| "no-referrer-when-downgrade"
| "origin"
| "origin-when-cross-origin"
| "same-origin"
| "strict-origin"
| "strict-origin-when-cross-origin"
| "unsafe-url";

export interface IIframeProps {
readonly id?: string;
readonly title?: string;

readonly src?: string;
readonly srcdoc?: string;
readonly fwidth?: DimensionLength;
readonly fheight?: DimensionLength;

readonly allow?: string;
readonly loading?: "eager" | "lazy";
readonly name?: string;
readonly referrerpolicy?: IframeAttrReferrerPolicy;
readonly sandbox?: IframeAttrSandbox | ReadonlyArray<IframeAttrSandbox>;
}

export class HtmlIframe extends Container implements IIframeProps {
readonly id?: string;
readonly title?: string;

readonly src?: string;
readonly srcdoc?: string;
readonly fwidth?: DimensionLength;
readonly fheight?: DimensionLength;

readonly allow?: string;
readonly loading?: "eager" | "lazy";
readonly name?: string;
readonly referrerpolicy?: IframeAttrReferrerPolicy;
readonly sandbox?: IframeAttrSandbox | ReadonlyArray<IframeAttrSandbox>;

constructor({
key,
id,
title,
src,
srcdoc,
fwidth,
fheight,
allow,
loading,
name,
referrerpolicy,
sandbox,
...rest
}: { key: WidgetKey } & IIframeProps & IWHStyleWidget) {
super({ key, ...rest });

this.id = id;
this.title = title;
this.src = src;
this.srcdoc = srcdoc;
this.fwidth = fwidth;
this.fheight = fheight;
this.allow = allow;
this.loading = loading;
this.name = name;
this.referrerpolicy = referrerpolicy;
this.sandbox = sandbox;
}
//

styleData(): ElementCssStyleData {
const containerstyle = super.styleData();

return {
// general layouts, continer ---------------------
...containerstyle,
// -------------------------------------------------

/* Override default CSS styles */
border: containerstyle.border ?? "none",
overflow: containerstyle.overflow ?? "hidden",
/* --------------------------- */

// ----------------------
};
}

jsxConfig(): StylableJSXElementConfig {
const attrs = [
this.id && new JSXAttribute("id", new StringLiteral(this.id)),
this.title && new JSXAttribute("title", new StringLiteral(this.title)),
this.src && new JSXAttribute("src", new StringLiteral(this.src)),
this.srcdoc && new JSXAttribute("srcdoc", new StringLiteral(this.srcdoc)),

this.fwidth &&
new JSXAttribute("width", new StringLiteral(css.length(this.fwidth))),
this.fheight &&
new JSXAttribute("height", new StringLiteral(css.length(this.fheight))),

this.sandbox?.length > 0 &&
new JSXAttribute(
"sandbox",
new StringLiteral(
Array.isArray(this.sandbox)
? this.sandbox.join(" ")
: (this.sandbox as string)
)
),

this.allow && new JSXAttribute("allow", new StringLiteral(this.allow)),
].filter(Boolean);

return <StylableJSXElementConfig>{
type: "tag-and-attr",
tag: JSX.identifier("iframe"),
attributes: attrs,
};
}

get finalStyle() {
const superstyl = super.finalStyle;

// width override. ------------------------------------------------------------------------------------------
// iframe element's width needs to be specified if the position is absolute and the left & right is specified.
let width = superstyl.width;
if (
width === undefined &&
superstyl.position === "absolute" &&
superstyl.left !== undefined &&
superstyl.right !== undefined
) {
width = "calc(100% - " + superstyl.left + " - " + superstyl.right + ")";
}
// ----------------------------------------------------------------------------------------------------------

return {
...superstyl,
width,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
import type { StylableJSXElementConfig } from "../../widget-core";
import { WidgetKey } from "../../widget-key";
import { Container } from "../container";
import { JSX, JSXAttribute, NumericLiteral, StringLiteral } from "coli";
import { JSX, JSXAttribute, StringLiteral } from "coli";
import * as css from "@web-builder/styles";
import { RoundSliderThumbShape } from "@reflect-ui/core/lib/slider.thumb";

Expand Down
Loading