Skip to content

Commit 4c6722d

Browse files
add iframe & 3rd party iframe embeddings (yt, gmap, osm, figma)
1 parent 78ddbc0 commit 4c6722d

6 files changed

Lines changed: 285 additions & 0 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { HtmlIframe } from "../html-iframe";
2+
import type { IIframeProps } from "../html-iframe";
3+
import type { IWHStyleWidget } from "@reflect-ui/core";
4+
import type { WidgetKey } from "../../widget-key";
5+
6+
type FigmaProps = Omit<IIframeProps, "src" | "srcDoc"> & {
7+
latlng: string;
8+
};
9+
10+
export class HtmlIframeFigma extends HtmlIframe {
11+
constructor({
12+
key,
13+
loading = "lazy",
14+
allow = "fullscreen",
15+
latlng,
16+
...rest
17+
}: { key: WidgetKey } & FigmaProps & IWHStyleWidget) {
18+
super({
19+
key,
20+
...rest,
21+
loading,
22+
allow,
23+
src: figmaurl(latlng),
24+
});
25+
}
26+
}
27+
28+
function figmaurl(url: string): string {
29+
const re =
30+
/https:\/\/([\w\.-]+\.)?figma.com\/(file|proto)\/([0-9a-zA-Z]{22,128})(?:\/.*)?$/;
31+
if (re.test(url)) {
32+
return `https://www.figma.com/embed?embed_host=astra&url=${url}`;
33+
} else {
34+
return "https://figma.com/";
35+
//
36+
}
37+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { HtmlIframe } from "../html-iframe";
2+
import type { IIframeProps } from "../html-iframe";
3+
import type { IWHStyleWidget } from "@reflect-ui/core";
4+
import type { WidgetKey } from "../../widget-key";
5+
6+
type GoogleMapsProps = Omit<IIframeProps, "src" | "srcDoc"> & {
7+
q: string;
8+
};
9+
10+
export class HtmlIframeGoogleMaps extends HtmlIframe {
11+
constructor({
12+
key,
13+
loading = "lazy",
14+
referrerpolicy = "no-referrer-when-downgrade",
15+
q,
16+
...rest
17+
}: { key: WidgetKey } & GoogleMapsProps & IWHStyleWidget) {
18+
super({
19+
key,
20+
...rest,
21+
loading,
22+
referrerpolicy,
23+
src: gmapurl(q),
24+
});
25+
}
26+
}
27+
28+
function gmapurl(q: string, apikey?: string): string {
29+
// build query param
30+
const query = new URLSearchParams();
31+
query.set("q", q);
32+
if (apikey) {
33+
query.set("key", apikey);
34+
}
35+
36+
// build url
37+
const url = new URL("https://www.google.com/maps/embed/v1/place");
38+
url.search = query.toString();
39+
40+
return url.toString();
41+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { HtmlIframe } from "../html-iframe";
2+
import type { IIframeProps } from "../html-iframe";
3+
import type { IWHStyleWidget } from "@reflect-ui/core";
4+
import type { WidgetKey } from "../../widget-key";
5+
6+
type OsmProps = Omit<IIframeProps, "src" | "srcDoc"> & {
7+
latlng: string;
8+
};
9+
10+
export class HtmlIframeOpenStreetMap extends HtmlIframe {
11+
constructor({
12+
key,
13+
loading = "lazy",
14+
referrerpolicy = "no-referrer-when-downgrade",
15+
latlng,
16+
...rest
17+
}: { key: WidgetKey } & OsmProps & IWHStyleWidget) {
18+
super({
19+
key,
20+
...rest,
21+
loading,
22+
referrerpolicy,
23+
src: osmurl(latlng),
24+
});
25+
}
26+
}
27+
28+
function osmurl(latlng: string | { lat: number; lng: number }): string {
29+
const p = typeof latlng === "string" ? latlng : `${latlng.lat},${latlng.lng}`;
30+
return `https://www.openstreetmap.org/export/embed.html?bbox=${p}`;
31+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { HtmlIframe } from "../html-iframe";
2+
import type { IIframeProps } from "../html-iframe";
3+
import type { IWHStyleWidget } from "@reflect-ui/core";
4+
import type { WidgetKey } from "../../widget-key";
5+
6+
type YoutubeProps = Omit<IIframeProps, "src" | "srcDoc"> & {
7+
video: string;
8+
};
9+
10+
export class HtmlIframeYoutube extends HtmlIframe {
11+
constructor({
12+
key,
13+
loading = "lazy",
14+
referrerpolicy = "no-referrer-when-downgrade",
15+
video,
16+
...rest
17+
}: { key: WidgetKey } & YoutubeProps & IWHStyleWidget) {
18+
super({
19+
key,
20+
...rest,
21+
loading,
22+
referrerpolicy,
23+
src: yturl(video),
24+
});
25+
}
26+
}
27+
28+
function yturl(video: string): string {
29+
return `https://www.youtube.com/embed/${video}`;
30+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import type { ElementCssStyleData } from "@coli.codes/css";
2+
import type { Color, DimensionLength, IWHStyleWidget } from "@reflect-ui/core";
3+
import { WidgetKey } from "../../widget-key";
4+
import type { StylableJSXElementConfig } from "../../widget-core";
5+
import { Container } from "../container";
6+
import * as css from "@web-builder/styles";
7+
import { JSX, JSXAttribute, StringLiteral } from "coli";
8+
9+
type IframeAttrSandbox =
10+
| "allow-downloads-without-user-activation"
11+
| "allow-downloads"
12+
| "allow-forms"
13+
| "allow-modals"
14+
| "allow-orientation-lock"
15+
| "allow-pointer-lock"
16+
| "allow-popups"
17+
| "allow-popups-to-escape-sandbox"
18+
| "allow-presentation"
19+
| "allow-same-origin"
20+
| "allow-scripts"
21+
| "allow-storage-access-by-user-activation"
22+
| "allow-top-navigation"
23+
| "allow-top-navigation-by-user-activation";
24+
25+
type IframeAttrReferrerPolicy =
26+
| "no-referrer"
27+
| "no-referrer-when-downgrade"
28+
| "origin"
29+
| "origin-when-cross-origin"
30+
| "same-origin"
31+
| "strict-origin"
32+
| "strict-origin-when-cross-origin"
33+
| "unsafe-url";
34+
35+
export interface IIframeProps {
36+
readonly id?: string;
37+
readonly title?: string;
38+
39+
readonly src?: string;
40+
readonly srcdoc?: string;
41+
readonly width: DimensionLength;
42+
readonly height: DimensionLength;
43+
44+
readonly allow?: string;
45+
readonly loading?: "eager" | "lazy";
46+
readonly name?: string;
47+
readonly referrerpolicy?: IframeAttrReferrerPolicy;
48+
readonly sandbox?: IframeAttrSandbox | ReadonlyArray<IframeAttrSandbox>;
49+
}
50+
51+
export class HtmlIframe extends Container implements IIframeProps {
52+
readonly id?: string;
53+
readonly title?: string;
54+
55+
readonly src?: string;
56+
readonly srcdoc?: string;
57+
readonly width: DimensionLength;
58+
readonly height: DimensionLength;
59+
60+
readonly allow?: string;
61+
readonly loading?: "eager" | "lazy";
62+
readonly name?: string;
63+
readonly referrerpolicy?: IframeAttrReferrerPolicy;
64+
readonly sandbox?: IframeAttrSandbox | ReadonlyArray<IframeAttrSandbox>;
65+
66+
constructor({
67+
key,
68+
id,
69+
title,
70+
src,
71+
srcdoc,
72+
width,
73+
height,
74+
allow,
75+
loading,
76+
name,
77+
referrerpolicy,
78+
sandbox,
79+
...rest
80+
}: { key: WidgetKey } & IIframeProps & IWHStyleWidget) {
81+
super({ key, ...rest });
82+
83+
this.id = id;
84+
this.title = title;
85+
this.src = src;
86+
this.srcdoc = srcdoc;
87+
this.width = width;
88+
this.height = height;
89+
this.allow = allow;
90+
this.loading = loading;
91+
this.name = name;
92+
this.referrerpolicy = referrerpolicy;
93+
this.sandbox = sandbox;
94+
}
95+
//
96+
97+
styleData(): ElementCssStyleData {
98+
const containerstyle = super.styleData();
99+
100+
return {
101+
// general layouts, continer ---------------------
102+
...containerstyle,
103+
// -------------------------------------------------
104+
105+
/* Override default CSS styles */
106+
border: containerstyle.border ?? "none",
107+
/* --------------------------- */
108+
109+
// ----------------------
110+
};
111+
}
112+
113+
jsxConfig(): StylableJSXElementConfig {
114+
const attrs = [
115+
this.id && new JSXAttribute("id", new StringLiteral(this.id)),
116+
this.title && new JSXAttribute("title", new StringLiteral(this.title)),
117+
this.src && new JSXAttribute("src", new StringLiteral(this.src)),
118+
this.srcdoc && new JSXAttribute("srcdoc", new StringLiteral(this.srcdoc)),
119+
120+
this.width &&
121+
new JSXAttribute("width", new StringLiteral(css.length(this.width))),
122+
this.height &&
123+
new JSXAttribute("height", new StringLiteral(css.length(this.height))),
124+
125+
this.sandbox.length > 0 &&
126+
new JSXAttribute(
127+
"sandbox",
128+
new StringLiteral(
129+
Array.isArray(this.sandbox)
130+
? this.sandbox.join(" ")
131+
: (this.sandbox as string)
132+
)
133+
),
134+
].filter(Boolean);
135+
136+
return <StylableJSXElementConfig>{
137+
type: "tag-and-attr",
138+
tag: JSX.identifier("iframe"),
139+
attributes: attrs,
140+
};
141+
}
142+
}

packages/builder-web-core/widgets-native/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ export * from "./html-image";
99
export * from "./html-button";
1010
export * from "./html-progress";
1111
export * from "./html-input";
12+
export { HtmlIframe as Iframe } from "./html-iframe";
13+
export { HtmlIframeGoogleMaps as GoogleMaps } from "./html-iframe-googlemaps";
14+
export { HtmlIframeOpenStreetMap as OpenStreetMap } from "./html-iframe-osm";
15+
export { HtmlIframeFigma as EmbedFigma } from "./html-iframe-figma";
1216
export * from "./error-widget";
1317

1418
export * from "@web-builder/core/widget-tree/widget";

0 commit comments

Comments
 (0)