Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(web): add extension settings panel to plugin playground #1383

Merged
merged 26 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8a07f45
feat: add widget panel
mulengawilfred Jan 28, 2025
92ead8e
fix: refactor property item
mulengawilfred Jan 28, 2025
2f51ca5
fix: remove unused props
mulengawilfred Jan 28, 2025
147ab9b
fix: remove comment
mulengawilfred Jan 28, 2025
b78e9fe
Merge branch 'main' into feat/plugin-playground-widgets-panel
mulengawilfred Jan 29, 2025
48e26aa
fix: refactor code setState
mulengawilfred Jan 29, 2025
515dd72
Merge branch 'main' into feat/plugin-playground-widgets-panel
mulengawilfred Jan 29, 2025
f54c507
fix: remove setNotification and add error message
mulengawilfred Jan 29, 2025
fd276ea
fix: add field types to widget
mulengawilfred Feb 3, 2025
c12659f
Merge branch 'main' into feat/plugin-playground-widgets-panel
mulengawilfred Feb 4, 2025
3a82b32
fix: refactor widget list
mulengawilfred Feb 4, 2025
9481acf
Merge branch 'main' into feat/plugin-playground-widgets-panel
mulengawilfred Feb 4, 2025
d4a21df
Merge branch 'main' into feat/plugin-playground-widgets-panel
mulengawilfred Feb 4, 2025
4b364fa
fix: update each field on change
mulengawilfred Feb 5, 2025
05e9457
Merge branch 'main' into feat/plugin-playground-widgets-panel
mulengawilfred Feb 5, 2025
5df7e0e
feat: attempt to update input fields in extension settings
mulengawilfred Feb 6, 2025
23b64a6
fix: remove setTimeout
mulengawilfred Feb 6, 2025
8f7a8c3
fix: comment out extension settings
mulengawilfred Feb 6, 2025
02ff145
refactor: connect widget property
airslice Feb 6, 2025
5fc4d3c
refactor: rename panel
airslice Feb 6, 2025
80dfe1e
Merge branch 'main' into feat/plugin-playground-widgets-panel
airslice Feb 6, 2025
76ebf1d
Merge branch 'main' into feat/plugin-playground-widgets-panel
airslice Feb 7, 2025
d79f315
fix: added infoboxblock and storyblock extensions to test
mulengawilfred Feb 10, 2025
a2e87c6
refactor: support property for infoboxBlock and storyBlock
airslice Feb 10, 2025
d3179e6
Merge branch 'main' into feat/plugin-playground-widgets-panel
airslice Feb 12, 2025
e4073aa
fix: add fix to maintain state of story panel
mulengawilfred Feb 12, 2025
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
57 changes: 3 additions & 54 deletions web/src/beta/features/PluginPlayground/Code/hook.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,18 @@
import Visualizer from "@reearth/beta/features/Visualizer";
import { useNotification } from "@reearth/services/state";
import * as yaml from "js-yaml";
import { ComponentProps, useCallback, useState } from "react";
import { useCallback, useState } from "react";
import { v4 } from "uuid";

import { Story } from "../../Visualizer/Crust/StoryPanel/types";
import { WidgetLocation } from "../../Visualizer/Crust/Widgets/types";
import { DEFAULT_LAYERS_PLUGIN_PLAYGROUND } from "../LayerList/constants";
import { FileType } from "../Plugins/constants";

type Widgets = ComponentProps<typeof Visualizer>["widgets"];

type ReearthYML = {
id: string;
name: string;
version: string;
extensions?: {
id: string;
type: string;
name: string;
description: string;
widgetLayout?: {
extended: boolean;
defaultLocation: {
zone: WidgetLocation["zone"];
section: WidgetLocation["section"];
area: WidgetLocation["area"];
};
};
}[];
};

type CustomInfoboxBlock = {
id: string;
name: string;
description: string;
__REEARTH_SOURCECODE: string;
extensionId: string;
pluginId: string;
};

type CustomStoryBlock = CustomInfoboxBlock;
import { CustomInfoboxBlock, CustomStoryBlock, Widgets } from "../types";
import { getYmlJson } from "../utils";

type Props = {
files: FileType[];
resetVisualizer: () => void;
};

const getYmlJson = (file: FileType) => {
if (file.sourceCode === "") {
return { success: false, message: "YAML file is empty" } as const;
}

try {
const data = yaml.load(file.sourceCode) as ReearthYML;
return { success: true, data } as const;
} catch (error) {
const message =
error instanceof yaml.YAMLException
? error.message
: "Failed to parse YAML";
return { success: false, message } as const;
}
};

export default ({ files, resetVisualizer }: Props) => {
const [infoboxBlocks, setInfoboxBlocks] = useState<CustomInfoboxBlock[]>();
const [story, setStory] = useState<Story>();
Expand Down
3 changes: 2 additions & 1 deletion web/src/beta/features/PluginPlayground/Code/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { Button, CodeInput } from "@reearth/beta/lib/reearth-ui";
import { styled } from "@reearth/services/theme";
import { FC, useCallback, useState } from "react";

import { getLanguageByFileExtension } from "../utils";

import HtmlEditModal from "./HtmlEditModal";
import { getLanguageByFileExtension } from "./utils";

type Props = {
fileTitle: string;
Expand Down
12 changes: 0 additions & 12 deletions web/src/beta/features/PluginPlayground/Code/utils.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@ extensions:
zone: outer
section: left
area: top
schema:
groups:
- id: default
title: Data
fields:
- id: 1
type: string
title: Text
name: Title
- id: 2
type: number
title: Number
name: Number
- id: 3
type: bool
title: Boolean
name: Boolean
- id: demo-infobox-block-1
type: infoboxBlock
name: Demo Infobox Block 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { FC, useState } from "react";

import { InputField, NumberField, SwitchField } from "../../../ui/fields";

type Props = {
field: {
id: string;
type: string;
title: string;
name?: string;
};
};

const PropertyItem: FC<Props> = ({ field }) => {
const [value, setValue] = useState<string | number | boolean>(() => {
switch (field.type) {
case "number":
return 0;
case "bool":
return false;
default:
return "";
}
});

const handleChange = (newValue?: string | number | boolean) => {
switch (typeof newValue) {
case "string":
setValue(newValue);
break;
case "number":
if (!isNaN(newValue)) {
setValue(newValue);
}
break;
case "boolean":
setValue(newValue);
break;
default:
console.warn(`Unsupported value type: ${typeof newValue}`);
}
};

return (
<>
{field.type === "number" ? (
<NumberField
key={field.id}
title={field.name}
value={value as number}
onChange={handleChange}
/>
) : field.type === "bool" ? (
<SwitchField
key={field.id}
title={field.name}
value={value as boolean}
onChange={handleChange}
/>
) : (
<InputField
key={field.id}
title={field.name}
value={value as string}
onChange={handleChange}
/>
)}
</>
);
};

export default PropertyItem;
88 changes: 88 additions & 0 deletions web/src/beta/features/PluginPlayground/WidgetsList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { useNotification } from "@reearth/services/state";
import { styled } from "@reearth/services/theme";
import { FC } from "react";

import { getYmlJson } from "../utils";

import PropertyItem from "./PropertyItem";

type Props = {
selectedPlugin: {
id: string;
title: string;
files: {
id: string;
title: string;
sourceCode: string;
}[];
};
};
const WidgetsList: FC<Props> = ({ selectedPlugin }): JSX.Element => {
const [, setNotification] = useNotification();
const ymlFile =
selectedPlugin.files &&
selectedPlugin.files.find((f) => f.title.endsWith("reearth.yml"));

if (!ymlFile) {
setNotification({
type: "error",
text: "No YAML configuration file found"
});
return <div />;
}

const getYmlResult = getYmlJson(ymlFile);

if (!getYmlResult.success) {
setNotification({ type: "error", text: getYmlResult.message });
return <div />;
}

const ymlJSON = getYmlResult.data;

if (!ymlJSON || !ymlJSON.extensions) {
setNotification({
type: "error",
text: "No extensions found in YAML file"
});
return <div />;
}
const widgetExtension = ymlJSON.extensions.find((e) => e.type === "widget");

if (
!widgetExtension ||
!widgetExtension.schema ||
!widgetExtension.schema.groups
) {
setNotification({
type: "error",
text: "No schema found in widget extension"
});
return <div />;
}

const widgetSchema = widgetExtension.schema.groups;

if (!widgetSchema || widgetSchema.length == 0) {
setNotification({
type: "error",
text: "No schema found in widget extension"
});
return <div />;
}

const { fields } = widgetSchema[0];
return (
<Wrapper>
{fields.map((field) => (
<PropertyItem key={field.id} field={field} />
))}
</Wrapper>
);
};

const Wrapper = styled.div`
padding: 10px;
`;

export default WidgetsList;
8 changes: 7 additions & 1 deletion web/src/beta/features/PluginPlayground/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Plugins from "./Plugins";
import usePlugins from "./Plugins/usePlugins";
import SettingsList from "./SettingsList";
import Viewer from "./Viewer";
import WidgetsList from "./WidgetsList";

export default () => {
const visualizerRef = useRef<MapRef | null>(null);
Expand Down Expand Up @@ -188,11 +189,16 @@ export default () => {
/>
);

const WidgetsPanel: FC = () => (
<WidgetsList selectedPlugin={selectedPlugin} />
);

return {
LayersPanel,
MainAreaTabs,
RightAreaTabs,
SettingsPanel,
SubRightAreaTabs
SubRightAreaTabs,
WidgetsPanel
};
};
6 changes: 5 additions & 1 deletion web/src/beta/features/PluginPlayground/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const PluginPlayground: FC = () => {
MainAreaTabs,
RightAreaTabs,
SettingsPanel,
SubRightAreaTabs
SubRightAreaTabs,
WidgetsPanel
} = useHooks();

return (
Expand All @@ -31,6 +32,9 @@ const PluginPlayground: FC = () => {
<Panel noPadding alwaysOpen extend title="Settings">
<SettingsPanel />
</Panel>
<Panel noPadding alwaysOpen extend title="Widgets">
<WidgetsPanel />
</Panel>
</Area>
</Area>
<Area
Expand Down
58 changes: 58 additions & 0 deletions web/src/beta/features/PluginPlayground/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Visualizer from "@reearth/beta/features/Visualizer";
import { ComponentProps } from "react";

import { Field } from "../Visualizer/Crust/StoryPanel/types";
import { WidgetLocation } from "../Visualizer/Crust/Widgets/types";

export type ReearthYML = {
id: string;
name: string;
version: string;
extensions?: {
id: string;
type: string;
name: string;
description: string;
widgetLayout?: {
extended: boolean;
defaultLocation: {
zone: WidgetLocation["zone"];
section: WidgetLocation["section"];
area: WidgetLocation["area"];
};
};
schema?: {
groups: Group;
};
}[];
};

export type Widgets = ComponentProps<typeof Visualizer>["widgets"];

export type CustomInfoboxBlock = {
id: string;
name: string;
description: string;
__REEARTH_SOURCECODE: string;
extensionId: string;
pluginId: string;
};

export type CustomStoryBlock = CustomInfoboxBlock;

export interface CustomField extends Field {
id?: string;
ui?: string;
defaultValue?: string | number | boolean | null;
}

export type Group = {
id: string;
title: string;
fields: {
id: string;
type: string;
title: string;
name?: string;
}[];
}[];
Loading
Loading