diff --git a/src/canvas/components/WidgetContainer.tsx b/src/canvas/components/WidgetContainer.tsx index 1ce08d95..8fe8bc44 100644 --- a/src/canvas/components/WidgetContainer.tsx +++ b/src/canvas/components/WidgetContainer.tsx @@ -6,7 +6,6 @@ import { stringifyError } from "../utils"; import { LuGripVertical } from "react-icons/lu"; import { Box } from "@radix-ui/themes"; import { updateWidgetSettings, useWidgetsStore } from "../hooks"; -import { events } from "../../core"; interface WidgetContainerProps { id: string; @@ -20,9 +19,7 @@ const WidgetContainer = memo(({ id }: WidgetContainerProps) => { const onStop = useCallback( (_: DraggableEvent, data: DraggableData) => { - const pos = { x: x + data.x, y: y + data.y }; - updateWidgetSettings(id, pos); - events.updateSettings.toManager({ id, settings: pos }); + updateWidgetSettings(id, { x: x + data.x, y: y + data.y }, true); }, [id, x, y], ); diff --git a/src/canvas/hooks/useWidgetsStore.ts b/src/canvas/hooks/useWidgetsStore.ts index 65987c10..3f343c95 100644 --- a/src/canvas/hooks/useWidgetsStore.ts +++ b/src/canvas/hooks/useWidgetsStore.ts @@ -1,6 +1,7 @@ import { create } from "zustand"; import { WidgetSettings } from "../../types"; import { FC, createElement } from "react"; +import { events } from "../../core"; import ErrorDisplay from "../components/ErrorDisplay"; interface Widget { @@ -105,6 +106,7 @@ export function updateWidgetRenderError( export function updateWidgetSettings( id: string, settings: Partial, + emit: boolean = false, ) { useWidgetsStore.setState((state) => { if (id in state.widgets) { @@ -117,6 +119,10 @@ export function updateWidgetSettings( } return {}; }); + + if (emit) { + events.updateSettings.toManager({ id, settings }).catch(console.error); + } } export function removeWidgets(ids: string[]) { diff --git a/src/manager/components/Widgets/Config.tsx b/src/manager/components/Widgets/Config.tsx index e21d00cd..711b6ccd 100644 --- a/src/manager/components/Widgets/Config.tsx +++ b/src/manager/components/Widgets/Config.tsx @@ -13,7 +13,7 @@ const Config = memo(({ id }: ConfigProps) => { return ( - + {config.type === WidgetConfigType.VALID ? ( { }} > - + Name {config.content.name} - + Entry {config.content.entry} - + Dependencies diff --git a/src/manager/components/Widgets/Settings.tsx b/src/manager/components/Widgets/Settings.tsx index a0747423..6bca6070 100644 --- a/src/manager/components/Widgets/Settings.tsx +++ b/src/manager/components/Widgets/Settings.tsx @@ -1,35 +1,85 @@ -import { Box, Flex } from "@radix-ui/themes"; -import { LuRepeat } from "react-icons/lu"; -import { events } from "../../../core"; -import { toast } from "sonner"; -import WidgetContentHeading from "./WidgetContentHeading"; -import WidgetContentSettingsList from "./WidgetContentSettingsList"; -import { useWidgetsStore } from "../../hooks"; -import { memo } from "react"; +import { Flex, Table } from "@radix-ui/themes"; +import { FaTimes } from "react-icons/fa"; +import { updateWidgetSettings, useWidgetsStore } from "../../hooks"; +import { memo, useCallback } from "react"; +import NumberInput from "./NumberInput"; + +const X = ({ id }: SettingsProps) => { + const x = useWidgetsStore((state) => state.widgets[id].settings.x); + const onChange = useCallback( + (value: number) => updateWidgetSettings(id, { x: value }, true), + [id], + ); + + return ; +}; + +const Y = ({ id }: SettingsProps) => { + const y = useWidgetsStore((state) => state.widgets[id].settings.y); + const onChange = useCallback( + (value: number) => updateWidgetSettings(id, { y: value }, true), + [id], + ); + + return ; +}; + +const Opacity = ({ id }: SettingsProps) => { + const opacity = useWidgetsStore( + (state) => state.widgets[id].settings.opacity, + ); + const onChange = useCallback( + (value: number) => updateWidgetSettings(id, { opacity: value }, true), + [id], + ); + + return ( + + ); +}; + +X.displayName = "Settings.X"; +Y.displayName = "Settings.Y"; +Opacity.displayName = "Settings.Opacity"; interface SettingsProps { id: string; } const Settings = memo(({ id }: SettingsProps) => { - const settings = useWidgetsStore((state) => state.widgets[id].settings); - return ( - - } - actionText="Re-render" - action={() => - events.renderWidgets - .toCanvas([{ id }]) - .then(() => toast.success("Re-rendered widget.")) - } - /> - - - - + because we may have another column in the future, + // so header is not semantically correct + "& .label": { color: "var(--gray-11)", width: "100px" }, + }} + > + + + Position (px) + + + + + + + + + + Opacity (%) + + + + + + ); }); diff --git a/src/manager/components/Widgets/WidgetContentHeading.tsx b/src/manager/components/Widgets/WidgetContentHeading.tsx deleted file mode 100644 index c1e91f3a..00000000 --- a/src/manager/components/Widgets/WidgetContentHeading.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { Button, Flex, Heading } from "@radix-ui/themes"; -import { ReactNode } from "react"; - -interface WidgetContentHeadingProps { - /** The component to put in the heading. */ - heading: ReactNode; - /** The icon for the action button. */ - actionIcon: React.ReactNode; - /** The text for the action button. */ - actionText: string; - /** The action on button click. */ - action: () => void; -} - -/** - * The heading component for each section of a widget tab. - * - * This displays the heading aligned left and the action button aligned right. The - * action button will be composed of the icon then the text. - */ -const WidgetContentHeading = ({ - heading, - actionIcon, - actionText, - action, -}: WidgetContentHeadingProps) => { - return ( - - {heading} - - - ); -}; - -export default WidgetContentHeading; diff --git a/src/manager/components/Widgets/WidgetContentSettingsList.tsx b/src/manager/components/Widgets/WidgetContentSettingsList.tsx deleted file mode 100644 index a0b9fa1f..00000000 --- a/src/manager/components/Widgets/WidgetContentSettingsList.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { WidgetSettings } from "../../../types"; -import { events } from "../../../core"; -import { DataList, Flex } from "@radix-ui/themes"; -import NumberInput from "./NumberInput"; -import { FaTimes } from "react-icons/fa"; -import { updateWidgetSettings } from "../../hooks"; - -interface WidgetContentSettingListProps { - id: string; - settings: WidgetSettings; -} - -/** - * Component for displaying the widget-specific settings. - * - * This includes setter for the position and opacity of a widget. The states in the - * manager will be updated via the setter, and the updated settings will also be sent - * to the canvas via emitting corresponding events. - */ -const WidgetContentSettingList = ({ - id, - settings, -}: WidgetContentSettingListProps) => { - function updateSetting(settings: Partial) { - updateWidgetSettings(id, settings); - events.updateSettings.toCanvas({ id, settings }).catch(console.error); - } - - return ( - - - Position (px) - - - updateSetting({ x: value })} - /> - - updateSetting({ y: value })} - /> - - - - - Opacity (%) - - updateSetting({ opacity: value })} - /> - - - - ); -}; - -export default WidgetContentSettingList; diff --git a/src/manager/hooks/useWidgetsStore.ts b/src/manager/hooks/useWidgetsStore.ts index 651450ed..bf0ce430 100644 --- a/src/manager/hooks/useWidgetsStore.ts +++ b/src/manager/hooks/useWidgetsStore.ts @@ -66,6 +66,7 @@ export async function rescan(initial: boolean = false) { export function updateWidgetSettings( id: string, settings: Partial, + emit: boolean = false, ) { useWidgetsStore.setState((state) => { if (id in state.widgets) { @@ -81,6 +82,10 @@ export function updateWidgetSettings( } return {}; }); + + if (emit) { + events.updateSettings.toCanvas({ id, settings }).catch(console.error); + } } export function removeWidgets(ids: string[]) {