feat: add iframe widget (#291)

* feat: add nestjs replacement, remove nestjs

* feat: add iframe widget

* fix: format issue

* fix: format issue

* fix: format issue

* fix: lockfile frozen
This commit is contained in:
Meier Lukas
2024-04-13 11:58:41 +02:00
committed by GitHub
parent 82e9887f36
commit 8f4897186c
8 changed files with 142 additions and 5 deletions

View File

@@ -67,7 +67,6 @@
"@types/react": "^18.2.76",
"@types/react-dom": "^18.2.25",
"@types/chroma-js": "2.4.4",
"dotenv-cli": "^7.4.1",
"concurrently": "^8.2.2",
"eslint": "^8.57.0",
"prettier": "^3.2.5",

View File

@@ -1,2 +1,8 @@
export const widgetKinds = ["clock", "weather", "app", "video"] as const;
export const widgetKinds = [
"clock",
"weather",
"app",
"iframe",
"video",
] as const;
export type WidgetKind = (typeof widgetKinds)[number];

View File

@@ -372,6 +372,45 @@ export default {
},
},
},
iframe: {
name: "iFrame",
description:
"Embed any content from the internet. Some websites may restrict access.",
option: {
embedUrl: {
label: "Embed URL",
},
allowFullScreen: {
label: "Allow full screen",
},
allowTransparency: {
label: "Allow transparency",
},
allowScrolling: {
label: "Allow scrolling",
},
allowPayment: {
label: "Allow payment",
},
allowAutoPlay: {
label: "Allow auto play",
},
allowMicrophone: {
label: "Allow microphone",
},
allowCamera: {
label: "Allow camera",
},
allowGeolocation: {
label: "Allow geolocation",
},
},
error: {
noUrl: "No iFrame URL provided",
noBrowerSupport:
"Your Browser does not support iframes. Please update your browser.",
},
},
weather: {
name: "Weather",
description:

View File

@@ -0,0 +1,8 @@
.iframe {
border-radius: var(--mantine-radius-sm);
width: 100%;
height: 100%;
border: none;
background: none;
background-color: transparent;
}

View File

@@ -0,0 +1,62 @@
import { objectEntries } from "@homarr/common";
import { useI18n } from "@homarr/translation/client";
import { Box, IconBrowserOff, Stack, Text, Title } from "@homarr/ui";
import type { WidgetComponentProps } from "../definition";
import classes from "./component.module.css";
export default function IFrameWidget({
options,
}: WidgetComponentProps<"iframe">) {
const t = useI18n();
const { embedUrl, ...permissions } = options;
const allowedPermissions = getAllowedPermissions(permissions);
if (embedUrl.trim() === "") return <NoUrl />;
return (
<Box h="100%" w="100%">
<iframe
className={classes.iframe}
src={embedUrl}
title="widget iframe"
allow={allowedPermissions.join(" ")}
>
<Text>{t("widget.iframe.error.noBrowerSupport")}</Text>
</iframe>
</Box>
);
}
const NoUrl = () => {
const t = useI18n();
return (
<Stack align="center" justify="center" h="100%">
<IconBrowserOff />
<Title order={4}>{t("widget.iframe.error.noUrl")}</Title>
</Stack>
);
};
const getAllowedPermissions = (
permissions: Omit<WidgetComponentProps<"iframe">["options"], "embedUrl">,
) => {
return objectEntries(permissions)
.filter(([_key, value]) => value)
.map(([key]) => permissionMapping[key]);
};
const permissionMapping = {
allowAutoPlay: "autoplay",
allowCamera: "camera",
allowFullScreen: "fullscreen",
allowGeolocation: "geolocation",
allowMicrophone: "microphone",
allowPayment: "payment",
allowScrolling: "scrolling",
allowTransparency: "transparency",
} satisfies Record<
keyof Omit<WidgetComponentProps<"iframe">["options"], "embedUrl">,
string
>;

View File

@@ -0,0 +1,24 @@
import { IconBrowser } from "@homarr/ui";
import { createWidgetDefinition } from "../definition";
import { optionsBuilder } from "../options";
export const { definition, componentLoader } = createWidgetDefinition(
"iframe",
{
icon: IconBrowser,
options: optionsBuilder.from((factory) => ({
embedUrl: factory.text(),
allowFullScreen: factory.switch(),
allowScrolling: factory.switch({
defaultValue: true,
}),
allowTransparency: factory.switch(),
allowPayment: factory.switch(),
allowAutoPlay: factory.switch(),
allowMicrophone: factory.switch(),
allowCamera: factory.switch(),
allowGeolocation: factory.switch(),
})),
},
).withDynamicImport(() => import("./component"));

View File

@@ -8,6 +8,7 @@ import { Loader as UiLoader } from "@homarr/ui";
import * as app from "./app";
import * as clock from "./clock";
import type { WidgetComponentProps } from "./definition";
import * as iframe from "./iframe";
import type { WidgetImportRecord } from "./import";
import * as video from "./video";
import * as weather from "./weather";
@@ -22,6 +23,7 @@ export const widgetImports = {
clock,
weather,
app,
iframe,
video,
} satisfies WidgetImportRecord;

3
pnpm-lock.yaml generated
View File

@@ -207,9 +207,6 @@ importers:
concurrently:
specifier: ^8.2.2
version: 8.2.2
dotenv-cli:
specifier: ^7.4.1
version: 7.4.1
eslint:
specifier: ^8.57.0
version: 8.57.0