Skip to content

Commit b381d8e

Browse files
Merge pull request #132 from gridaco/staging
Add native html iframe support (Youtube, Google maps, Webcam), html progress support
2 parents 0c6b81e + 7a6cc5a commit b381d8e

File tree

42 files changed

+1432
-74
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1432
-74
lines changed

editor/components/app-runner/vanilla-app-runner.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export function VanillaRunner({
8282
<iframe
8383
ref={ref}
8484
style={style}
85-
sandbox="allow-same-origin"
85+
sandbox="allow-same-origin allow-scripts"
8686
srcDoc={inlinesource}
8787
width={width}
8888
height={height}

externals/reflect-core

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: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
sandbox = "allow-scripts",
16+
q,
17+
...rest
18+
}: { key: WidgetKey } & GoogleMapsProps & IWHStyleWidget) {
19+
super({
20+
key,
21+
...rest,
22+
loading,
23+
sandbox,
24+
referrerpolicy,
25+
src: gmapurl(q),
26+
});
27+
}
28+
}
29+
30+
function gmapurl(q: string, apikey?: string): string {
31+
// build query param
32+
const query = {};
33+
query["q"] = q;
34+
if (apikey) {
35+
query["key"] = apikey;
36+
return `https://www.google.com/maps/embed/v1/place?${buildq(query)}`;
37+
} else {
38+
query["output"] = "embed";
39+
return `https://maps.google.com/maps?${buildq(query)}`;
40+
}
41+
}
42+
43+
const buildq = (q: object): string =>
44+
Object.keys(q)
45+
.map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(q[k])}`)
46+
.join("&");
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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
const webcamurl = "https://frames-appbox.vercel.app/webcam";
7+
8+
type WebcamProps = Omit<IIframeProps, "src" | "srcDoc"> & {};
9+
10+
export class HtmlIframeWebcam extends HtmlIframe {
11+
constructor({
12+
key,
13+
allow = "camera",
14+
loading = "lazy",
15+
referrerpolicy = "no-referrer-when-downgrade",
16+
sandbox = ["allow-scripts", "allow-same-origin"],
17+
...rest
18+
}: { key: WidgetKey } & WebcamProps & IWHStyleWidget) {
19+
super({
20+
key,
21+
...rest,
22+
allow,
23+
loading,
24+
sandbox,
25+
referrerpolicy,
26+
src: webcamurl,
27+
});
28+
}
29+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
sandbox = ["allow-scripts", "allow-same-origin"],
16+
video,
17+
...rest
18+
}: { key: WidgetKey } & YoutubeProps & IWHStyleWidget) {
19+
super({
20+
key,
21+
...rest,
22+
loading,
23+
sandbox,
24+
referrerpolicy,
25+
src: yturl(video),
26+
});
27+
}
28+
}
29+
30+
function yturl(video: string): string {
31+
return `https://www.youtube.com/embed/${video}`;
32+
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import type { ElementCssStyleData } from "@coli.codes/css";
2+
import type { 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 fwidth?: DimensionLength;
42+
readonly fheight?: 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 fwidth?: DimensionLength;
58+
readonly fheight?: 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+
fwidth,
73+
fheight,
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.fwidth = fwidth;
88+
this.fheight = fheight;
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+
overflow: containerstyle.overflow ?? "hidden",
108+
/* --------------------------- */
109+
110+
// ----------------------
111+
};
112+
}
113+
114+
jsxConfig(): StylableJSXElementConfig {
115+
const attrs = [
116+
this.id && new JSXAttribute("id", new StringLiteral(this.id)),
117+
this.title && new JSXAttribute("title", new StringLiteral(this.title)),
118+
this.src && new JSXAttribute("src", new StringLiteral(this.src)),
119+
this.srcdoc && new JSXAttribute("srcdoc", new StringLiteral(this.srcdoc)),
120+
121+
this.fwidth &&
122+
new JSXAttribute("width", new StringLiteral(css.length(this.fwidth))),
123+
this.fheight &&
124+
new JSXAttribute("height", new StringLiteral(css.length(this.fheight))),
125+
126+
this.sandbox?.length > 0 &&
127+
new JSXAttribute(
128+
"sandbox",
129+
new StringLiteral(
130+
Array.isArray(this.sandbox)
131+
? this.sandbox.join(" ")
132+
: (this.sandbox as string)
133+
)
134+
),
135+
136+
this.allow && new JSXAttribute("allow", new StringLiteral(this.allow)),
137+
].filter(Boolean);
138+
139+
return <StylableJSXElementConfig>{
140+
type: "tag-and-attr",
141+
tag: JSX.identifier("iframe"),
142+
attributes: attrs,
143+
};
144+
}
145+
146+
get finalStyle() {
147+
const superstyl = super.finalStyle;
148+
149+
// width override. ------------------------------------------------------------------------------------------
150+
// iframe element's width needs to be specified if the position is absolute and the left & right is specified.
151+
let width = superstyl.width;
152+
if (
153+
width === undefined &&
154+
superstyl.position === "absolute" &&
155+
superstyl.left !== undefined &&
156+
superstyl.right !== undefined
157+
) {
158+
width = "calc(100% - " + superstyl.left + " - " + superstyl.right + ")";
159+
}
160+
// ----------------------------------------------------------------------------------------------------------
161+
162+
return {
163+
...superstyl,
164+
width,
165+
};
166+
}
167+
}

packages/builder-web-core/widgets-native/html-input/html-input-range.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type {
88
import type { StylableJSXElementConfig } from "../../widget-core";
99
import { WidgetKey } from "../../widget-key";
1010
import { Container } from "../container";
11-
import { JSX, JSXAttribute, NumericLiteral, StringLiteral } from "coli";
11+
import { JSX, JSXAttribute, StringLiteral } from "coli";
1212
import * as css from "@web-builder/styles";
1313
import { RoundSliderThumbShape } from "@reflect-ui/core/lib/slider.thumb";
1414

0 commit comments

Comments
 (0)