@valbuild/ui 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -3
- package/server/.tmp/assets/index-082e6676.css +1 -0
- package/server/.tmp/assets/index-3108ab2a.js +197 -0
- package/{index.html → server/.tmp/index.html} +3 -1
- package/.babelrc.json +0 -10
- package/.storybook/main.js +0 -25
- package/.storybook/preview-head.html +0 -6
- package/.storybook/preview.js +0 -33
- package/.storybook/theme.css +0 -34
- package/CHANGELOG.md +0 -0
- package/components.json +0 -16
- package/fix-server-hack.js +0 -54
- package/fullscreen.vite.config.ts +0 -9
- package/jest.config.js +0 -4
- package/postcss.config.js +0 -6
- package/rollup.config.js +0 -23
- package/server.vite.config.ts +0 -31
- package/src/App.tsx +0 -73
- package/src/assets/icons/Bold.tsx +0 -23
- package/src/assets/icons/Chevron.tsx +0 -28
- package/src/assets/icons/FontColor.tsx +0 -30
- package/src/assets/icons/ImageIcon.tsx +0 -29
- package/src/assets/icons/Italic.tsx +0 -24
- package/src/assets/icons/Logo.tsx +0 -103
- package/src/assets/icons/Section.tsx +0 -41
- package/src/assets/icons/Strikethrough.tsx +0 -22
- package/src/assets/icons/TextIcon.tsx +0 -20
- package/src/assets/icons/Underline.tsx +0 -22
- package/src/assets/icons/Undo.tsx +0 -20
- package/src/components/Button.tsx +0 -68
- package/src/components/Checkbox.tsx +0 -51
- package/src/components/DraggableList.stories.tsx +0 -20
- package/src/components/DraggableList.tsx +0 -95
- package/src/components/Dropdown.tsx +0 -101
- package/src/components/EditButton.tsx +0 -10
- package/src/components/ErrorText.tsx +0 -3
- package/src/components/ExpandLogo.tsx +0 -72
- package/src/components/Grid.stories.tsx +0 -43
- package/src/components/Grid.tsx +0 -139
- package/src/components/RichTextEditor/ContentEditable.tsx +0 -117
- package/src/components/RichTextEditor/Nodes/ImageNode.tsx +0 -100
- package/src/components/RichTextEditor/Plugins/AutoFocus.tsx +0 -12
- package/src/components/RichTextEditor/Plugins/ImagePlugin.tsx +0 -45
- package/src/components/RichTextEditor/Plugins/LinkEditorPlugin.tsx +0 -58
- package/src/components/RichTextEditor/Plugins/Toolbar.tsx +0 -412
- package/src/components/RichTextEditor/RichTextEditor.tsx +0 -105
- package/src/components/UploadModal.tsx +0 -109
- package/src/components/User.tsx +0 -17
- package/src/components/ValFormField.tsx +0 -574
- package/src/components/ValFullscreen.tsx +0 -1278
- package/src/components/ValMenu.tsx +0 -92
- package/src/components/ValOverlay.tsx +0 -488
- package/src/components/ValOverlayContext.tsx +0 -80
- package/src/components/ValWindow.stories.tsx +0 -146
- package/src/components/ValWindow.tsx +0 -220
- package/src/components/dashboard/DashboardButton.tsx +0 -25
- package/src/components/dashboard/DashboardDropdown.tsx +0 -59
- package/src/components/dashboard/Dropdown.stories.tsx +0 -11
- package/src/components/dashboard/Dropdown.tsx +0 -70
- package/src/components/dashboard/FormGroup.stories.tsx +0 -37
- package/src/components/dashboard/FormGroup.tsx +0 -42
- package/src/components/dashboard/Grid2.stories.tsx +0 -56
- package/src/components/dashboard/Grid2.tsx +0 -72
- package/src/components/dashboard/Tree.stories.tsx +0 -91
- package/src/components/dashboard/Tree.tsx +0 -72
- package/src/components/dashboard/ValDashboardEditor.tsx +0 -269
- package/src/components/dashboard/ValDashboardGrid.tsx +0 -142
- package/src/components/dashboard/ValTreeNavigator.tsx +0 -253
- package/src/components/forms/Form.tsx +0 -126
- package/src/components/forms/FormContainer.tsx +0 -24
- package/src/components/forms/ImageForm.tsx +0 -195
- package/src/components/forms/TextArea.tsx +0 -24
- package/src/components/ui/accordion.tsx +0 -58
- package/src/components/ui/alert-dialog.tsx +0 -139
- package/src/components/ui/avatar.tsx +0 -48
- package/src/components/ui/button.tsx +0 -56
- package/src/components/ui/calendar.tsx +0 -62
- package/src/components/ui/card.tsx +0 -86
- package/src/components/ui/checkbox.tsx +0 -28
- package/src/components/ui/command.tsx +0 -153
- package/src/components/ui/dialog.tsx +0 -120
- package/src/components/ui/dropdown-menu.tsx +0 -198
- package/src/components/ui/form.tsx +0 -177
- package/src/components/ui/input.tsx +0 -24
- package/src/components/ui/label.tsx +0 -24
- package/src/components/ui/popover.tsx +0 -29
- package/src/components/ui/progress.tsx +0 -26
- package/src/components/ui/radio-group.tsx +0 -42
- package/src/components/ui/scroll-area.tsx +0 -51
- package/src/components/ui/select.tsx +0 -119
- package/src/components/ui/switch.tsx +0 -27
- package/src/components/ui/tabs.tsx +0 -53
- package/src/components/ui/toggle.tsx +0 -43
- package/src/components/ui/tooltip.tsx +0 -28
- package/src/components/usePatch.ts +0 -86
- package/src/components/useTheme.ts +0 -45
- package/src/dto/SerializedSchema.ts +0 -69
- package/src/dto/Session.ts +0 -12
- package/src/dto/SessionMode.ts +0 -5
- package/src/dto/Tree.ts +0 -18
- package/src/exports.ts +0 -6
- package/src/index.css +0 -115
- package/src/index.tsx +0 -14
- package/src/lib/IValStore.ts +0 -6
- package/src/lib/utils.ts +0 -6
- package/src/main.jsx +0 -10
- package/src/richtext/conversion/conversion.test.ts +0 -146
- package/src/richtext/conversion/lexicalToRichTextSource.test.ts +0 -89
- package/src/richtext/conversion/lexicalToRichTextSource.ts +0 -285
- package/src/richtext/conversion/parseRichTextSource.test.ts +0 -469
- package/src/richtext/conversion/parseRichTextSource.ts +0 -233
- package/src/richtext/conversion/richTextSourceToLexical.test.ts +0 -381
- package/src/richtext/conversion/richTextSourceToLexical.ts +0 -293
- package/src/richtext/shadowRootPolyFill.js +0 -115
- package/src/server.ts +0 -70
- package/src/stories/Button.stories.tsx +0 -20
- package/src/stories/Checkbox.stories.tsx +0 -14
- package/src/stories/Dropdown.stories.tsx +0 -23
- package/src/stories/Introduction.mdx +0 -221
- package/src/stories/RichTextEditor.stories.tsx +0 -24
- package/src/stories/assets/code-brackets.svg +0 -1
- package/src/stories/assets/colors.svg +0 -1
- package/src/stories/assets/comments.svg +0 -1
- package/src/stories/assets/direction.svg +0 -1
- package/src/stories/assets/flow.svg +0 -1
- package/src/stories/assets/plugin.svg +0 -1
- package/src/stories/assets/repo.svg +0 -1
- package/src/stories/assets/stackalt.svg +0 -1
- package/src/utils/Remote.ts +0 -15
- package/src/utils/imageMimeType.ts +0 -23
- package/src/utils/readImage.ts +0 -54
- package/src/utils/resolvePath.ts +0 -32
- package/src/vite-env.d.ts +0 -1
- package/src/vite-index.tsx +0 -7
- package/src/vite-server.ts +0 -42
- package/tailwind.config.js +0 -83
- package/tsconfig.json +0 -19
- package/vite.config.ts +0 -43
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import classNames from "classnames";
|
|
2
|
-
import { Children, cloneElement, useRef } from "react";
|
|
3
|
-
|
|
4
|
-
interface GridProps {
|
|
5
|
-
children: React.ReactNode | React.ReactNode[];
|
|
6
|
-
}
|
|
7
|
-
export function Grid({ children }: GridProps): React.ReactElement {
|
|
8
|
-
const leftColHeaderRef = useRef<HTMLDivElement>(null);
|
|
9
|
-
const leftColBodyRef = useRef<HTMLDivElement>(null);
|
|
10
|
-
const rightColHeaderRef = useRef<HTMLDivElement>(null);
|
|
11
|
-
const rightColBodyRef = useRef<HTMLDivElement>(null);
|
|
12
|
-
|
|
13
|
-
const [headerRow, bodyRow] = Children.toArray(children);
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<div className="flex flex-col w-full h-screen bg-warm-black overflow-clip">
|
|
17
|
-
{cloneElement(headerRow as React.ReactElement, {
|
|
18
|
-
leftColRef: leftColHeaderRef,
|
|
19
|
-
rightColRef: rightColHeaderRef,
|
|
20
|
-
})}
|
|
21
|
-
{cloneElement(bodyRow as React.ReactElement, {
|
|
22
|
-
leftColRef: leftColBodyRef,
|
|
23
|
-
rightColRef: rightColBodyRef,
|
|
24
|
-
})}
|
|
25
|
-
</div>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
type GridRowProps = {
|
|
30
|
-
children: React.ReactNode | React.ReactNode[];
|
|
31
|
-
leftColRef?: React.RefObject<HTMLDivElement>;
|
|
32
|
-
rightColRef?: React.RefObject<HTMLDivElement>;
|
|
33
|
-
fill?: boolean;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
Grid.Row = ({
|
|
37
|
-
children,
|
|
38
|
-
leftColRef,
|
|
39
|
-
rightColRef,
|
|
40
|
-
fill: bottomRow = false,
|
|
41
|
-
}: GridRowProps): React.ReactElement => {
|
|
42
|
-
const [leftCol, middleCol, rightCol] = Children.toArray(children);
|
|
43
|
-
//implement mouseover event to share hover-event between rows
|
|
44
|
-
console;
|
|
45
|
-
return (
|
|
46
|
-
<div
|
|
47
|
-
className={classNames("flex w-full py-2 border-b border-dark-gray", {
|
|
48
|
-
"h-full": bottomRow,
|
|
49
|
-
})}
|
|
50
|
-
>
|
|
51
|
-
<div className="relative" style={{ width: "300px" }} ref={leftColRef}>
|
|
52
|
-
{leftCol}
|
|
53
|
-
{bottomRow && (
|
|
54
|
-
<div
|
|
55
|
-
className="absolute inset-y-0 right-0 cursor-col-resize w-[1px] bg-dark-gray hover:w-[2px] hover:bg-light-gray -my-[100px]"
|
|
56
|
-
// onMouseDown={handleMouseDown("left")}
|
|
57
|
-
></div>
|
|
58
|
-
)}
|
|
59
|
-
</div>
|
|
60
|
-
<div className="flex-auto">{middleCol}</div>
|
|
61
|
-
<div className="relative" style={{ width: "300px" }} ref={rightColRef}>
|
|
62
|
-
{rightCol}
|
|
63
|
-
{bottomRow && (
|
|
64
|
-
<div
|
|
65
|
-
// onMouseDown={handleMouseDown("right")}
|
|
66
|
-
className="absolute inset-y-0 left-0 cursor-col-resize w-[1px] bg-dark-gray hover:w-[2px] hover:bg-light-gray -my-2"
|
|
67
|
-
></div>
|
|
68
|
-
)}
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
72
|
-
};
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { Tree } from "./Tree";
|
|
3
|
-
|
|
4
|
-
const meta: Meta<typeof Tree> = { component: Tree };
|
|
5
|
-
|
|
6
|
-
export default meta;
|
|
7
|
-
type Story = StoryObj<typeof Tree>;
|
|
8
|
-
|
|
9
|
-
type Node = {
|
|
10
|
-
path: string;
|
|
11
|
-
type: "string" | "image" | "section";
|
|
12
|
-
children: Node[];
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const nodes: Node[] = [
|
|
16
|
-
{
|
|
17
|
-
path: "Main nav",
|
|
18
|
-
type: "section",
|
|
19
|
-
children: [],
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
path: "H1",
|
|
23
|
-
type: "string",
|
|
24
|
-
children: [
|
|
25
|
-
{
|
|
26
|
-
path: "Section 3",
|
|
27
|
-
type: "string",
|
|
28
|
-
children: [],
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
path: "Section 4",
|
|
32
|
-
type: "section",
|
|
33
|
-
children: [
|
|
34
|
-
{
|
|
35
|
-
path: "Section 5",
|
|
36
|
-
type: "string",
|
|
37
|
-
children: [],
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
path: "Section 6",
|
|
41
|
-
type: "section",
|
|
42
|
-
children: [
|
|
43
|
-
{
|
|
44
|
-
path: "Section 7",
|
|
45
|
-
type: "string",
|
|
46
|
-
children: [],
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
path: "Image 1",
|
|
50
|
-
type: "image",
|
|
51
|
-
children: [],
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
path: "Image 2",
|
|
55
|
-
type: "image",
|
|
56
|
-
children: [],
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
},
|
|
62
|
-
],
|
|
63
|
-
},
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
const renderNodes = (node: Node) => (
|
|
67
|
-
<Tree.Node path={node.path} type={node.type}>
|
|
68
|
-
{node.children.map(renderNodes)}
|
|
69
|
-
</Tree.Node>
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
export const Default: Story = {
|
|
73
|
-
render: () => (
|
|
74
|
-
<Tree rootPath="root">
|
|
75
|
-
<Tree.Node path="Main nav" type="section" />
|
|
76
|
-
<Tree.Node path="H1" type="string">
|
|
77
|
-
<Tree.Node path="Section 3" type="string" />
|
|
78
|
-
<Tree.Node path="Section 4" type="section">
|
|
79
|
-
<Tree.Node path="Section 5" type="string" />
|
|
80
|
-
<Tree.Node path="Section 6" type="section">
|
|
81
|
-
<Tree.Node path="Section 7" type="string" />
|
|
82
|
-
</Tree.Node>
|
|
83
|
-
</Tree.Node>
|
|
84
|
-
</Tree.Node>
|
|
85
|
-
</Tree>
|
|
86
|
-
),
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
export const TestData: Story = {
|
|
90
|
-
render: () => <Tree rootPath="root">{nodes.map(renderNodes)}</Tree>,
|
|
91
|
-
};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { Children, cloneElement } from "react";
|
|
2
|
-
import ImageIcon from "../../assets/icons/ImageIcon";
|
|
3
|
-
import Section from "../../assets/icons/Section";
|
|
4
|
-
import TextIcon from "../../assets/icons/TextIcon";
|
|
5
|
-
|
|
6
|
-
type TreeProps = {
|
|
7
|
-
children: React.ReactNode | React.ReactNode[];
|
|
8
|
-
rootPath?: string;
|
|
9
|
-
};
|
|
10
|
-
export function Tree({ children, rootPath }: TreeProps): React.ReactElement {
|
|
11
|
-
return (
|
|
12
|
-
<div className="flex flex-col w-full py-2 text-xs">
|
|
13
|
-
{Children.map(children, (child) => {
|
|
14
|
-
return cloneElement(child as React.ReactElement, {
|
|
15
|
-
paths: [rootPath],
|
|
16
|
-
});
|
|
17
|
-
})}
|
|
18
|
-
</div>
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
type TreeNodeProps = {
|
|
22
|
-
children?: React.ReactNode | React.ReactNode[];
|
|
23
|
-
path: string;
|
|
24
|
-
paths?: string[];
|
|
25
|
-
level?: number;
|
|
26
|
-
type: "string" | "image" | "section";
|
|
27
|
-
setActivePath?: (path: string) => void;
|
|
28
|
-
};
|
|
29
|
-
Tree.Node = ({
|
|
30
|
-
children,
|
|
31
|
-
paths = [],
|
|
32
|
-
path,
|
|
33
|
-
level = 1,
|
|
34
|
-
type,
|
|
35
|
-
setActivePath,
|
|
36
|
-
}: TreeNodeProps): React.ReactElement => {
|
|
37
|
-
const paddingLeft = level * 30;
|
|
38
|
-
const logo =
|
|
39
|
-
type === "string" ? (
|
|
40
|
-
<TextIcon />
|
|
41
|
-
) : type === "image" ? (
|
|
42
|
-
<ImageIcon className="h-[9px] w-[9px]" />
|
|
43
|
-
) : (
|
|
44
|
-
<Section />
|
|
45
|
-
);
|
|
46
|
-
return (
|
|
47
|
-
<div className="w-full">
|
|
48
|
-
<button
|
|
49
|
-
className="flex justify-between w-full group py-2 text-xs font-[400] shrink-0"
|
|
50
|
-
onClick={() => {
|
|
51
|
-
setActivePath && setActivePath(path);
|
|
52
|
-
}}
|
|
53
|
-
style={{ paddingLeft: paddingLeft }}
|
|
54
|
-
>
|
|
55
|
-
<div className="flex items-center justify-center gap-2">
|
|
56
|
-
{logo}
|
|
57
|
-
<p>{path}</p>
|
|
58
|
-
</div>
|
|
59
|
-
</button>
|
|
60
|
-
{children && (
|
|
61
|
-
<>
|
|
62
|
-
{Children.map(children, (child) => {
|
|
63
|
-
return cloneElement(child as React.ReactElement, {
|
|
64
|
-
level: level + 1,
|
|
65
|
-
paths: [...paths, path],
|
|
66
|
-
});
|
|
67
|
-
})}
|
|
68
|
-
</>
|
|
69
|
-
)}
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
72
|
-
};
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import { SerializedModule } from "@valbuild/core";
|
|
2
|
-
import { ValApi } from "@valbuild/core";
|
|
3
|
-
import { LexicalEditor } from "lexical";
|
|
4
|
-
import { FC, useEffect, useState } from "react";
|
|
5
|
-
import { Inputs, RichTextEditor } from "../../exports";
|
|
6
|
-
import Button from "../Button";
|
|
7
|
-
import { ImageForm } from "../forms/ImageForm";
|
|
8
|
-
import { TextArea } from "../forms/TextArea";
|
|
9
|
-
|
|
10
|
-
interface ValDashboardEditorProps {
|
|
11
|
-
selectedPath: string;
|
|
12
|
-
valApi: ValApi;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const ValDashboardEditor: FC<ValDashboardEditorProps> = ({
|
|
16
|
-
selectedPath,
|
|
17
|
-
}) => {
|
|
18
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
19
|
-
const [selectedModule, setSelectedModule] = useState<SerializedModule>();
|
|
20
|
-
const [inputs, setInputs] = useState<Inputs>({});
|
|
21
|
-
const [inputIsDirty, setInputIsDirty] = useState<{ [path: string]: boolean }>(
|
|
22
|
-
{}
|
|
23
|
-
);
|
|
24
|
-
const [richTextEditor, setRichTextEditor] = useState<{
|
|
25
|
-
[path: string]: LexicalEditor;
|
|
26
|
-
}>();
|
|
27
|
-
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
// if (selectedPath) {
|
|
30
|
-
// valApi.getModule(selectedPath).then((module) => {
|
|
31
|
-
// setSelectedModule(module);
|
|
32
|
-
// });
|
|
33
|
-
// }
|
|
34
|
-
}, [selectedPath]);
|
|
35
|
-
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
// if (selectedModule && selectedModule?.source) {
|
|
38
|
-
// setInputs({});
|
|
39
|
-
// for (const key of Object.keys(selectedModule?.source)) {
|
|
40
|
-
// if (key !== "rank") {
|
|
41
|
-
// valApi
|
|
42
|
-
// .getModule(`${selectedModule.path}.${key}`)
|
|
43
|
-
// .then((serializedModule) => {
|
|
44
|
-
// let input: Inputs[string] | undefined;
|
|
45
|
-
// if (
|
|
46
|
-
// serializedModule.schema.type === "string" &&
|
|
47
|
-
// typeof serializedModule.source === "string"
|
|
48
|
-
// ) {
|
|
49
|
-
// input = {
|
|
50
|
-
// status: "completed",
|
|
51
|
-
// type: "text",
|
|
52
|
-
// data: serializedModule.source,
|
|
53
|
-
// };
|
|
54
|
-
// } else if (
|
|
55
|
-
// serializedModule.schema.type === "richtext" &&
|
|
56
|
-
// typeof serializedModule.source === "object"
|
|
57
|
-
// ) {
|
|
58
|
-
// input = {
|
|
59
|
-
// status: "completed",
|
|
60
|
-
// type: "richtext",
|
|
61
|
-
// data: serializedModule.source as RichText, // TODO: validate
|
|
62
|
-
// };
|
|
63
|
-
// } else if (
|
|
64
|
-
// serializedModule.schema.type === "image" &&
|
|
65
|
-
// serializedModule.source &&
|
|
66
|
-
// typeof serializedModule.source === "object" &&
|
|
67
|
-
// FILE_REF_PROP in serializedModule.source &&
|
|
68
|
-
// typeof serializedModule.source[FILE_REF_PROP] === "string" &&
|
|
69
|
-
// VAL_EXTENSION in serializedModule.source &&
|
|
70
|
-
// typeof serializedModule.source[VAL_EXTENSION] === "string"
|
|
71
|
-
// ) {
|
|
72
|
-
// input = {
|
|
73
|
-
// status: "completed",
|
|
74
|
-
// type: "image",
|
|
75
|
-
// data: Internal.convertImageSource(
|
|
76
|
-
// serializedModule.source as FileSource<ImageMetadata>
|
|
77
|
-
// ),
|
|
78
|
-
// };
|
|
79
|
-
// }
|
|
80
|
-
// if (!input) {
|
|
81
|
-
// throw new Error(
|
|
82
|
-
// `Unsupported module type: ${serializedModule.schema.type}`
|
|
83
|
-
// );
|
|
84
|
-
// }
|
|
85
|
-
// setInputs((inputs) => {
|
|
86
|
-
// return {
|
|
87
|
-
// ...inputs,
|
|
88
|
-
// [serializedModule.path]: input,
|
|
89
|
-
// } as Inputs;
|
|
90
|
-
// });
|
|
91
|
-
// });
|
|
92
|
-
// }
|
|
93
|
-
// }
|
|
94
|
-
// }
|
|
95
|
-
}, [selectedModule]);
|
|
96
|
-
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
for (const key of Object.keys(inputs)) {
|
|
99
|
-
if (!Object.keys(inputIsDirty).includes(key)) {
|
|
100
|
-
setInputIsDirty({
|
|
101
|
-
...inputIsDirty,
|
|
102
|
-
[key]: false,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}, [inputs]);
|
|
107
|
-
|
|
108
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
109
|
-
const patchElement = async (path: string) => {
|
|
110
|
-
// Promise.all(
|
|
111
|
-
// Object.entries(inputs)
|
|
112
|
-
// .filter(([k]) => k === key)
|
|
113
|
-
// .map(([path, input]) => {
|
|
114
|
-
// if (input.status === "completed") {
|
|
115
|
-
// const [moduleId, modulePath] = Internal.splitModuleIdAndModulePath(
|
|
116
|
-
// path as SourcePath
|
|
117
|
-
// );
|
|
118
|
-
// if (input.type === "text") {
|
|
119
|
-
// const patch: PatchJSON = [
|
|
120
|
-
// {
|
|
121
|
-
// value: input.data,
|
|
122
|
-
// op: "replace",
|
|
123
|
-
// path: `/${modulePath
|
|
124
|
-
// .split(".")
|
|
125
|
-
// .map((p) => {
|
|
126
|
-
// return JSON.parse(p);
|
|
127
|
-
// })
|
|
128
|
-
// .join("/")}`,
|
|
129
|
-
// },
|
|
130
|
-
// ];
|
|
131
|
-
// console.log("patch", patch);
|
|
132
|
-
// return valApi.patchModuleContent(moduleId, patch);
|
|
133
|
-
// } else if (input.type === "image") {
|
|
134
|
-
// const pathParts = modulePath.split(".").map((p) => JSON.parse(p));
|
|
135
|
-
// if (!input?.data || !("src" in input.data)) {
|
|
136
|
-
// // TODO: We probably need to have an Output type that is different from the Input: we have a union of both cases in Input right now, and we believe we do not want that
|
|
137
|
-
// console.warn(
|
|
138
|
-
// "No .src on input provided - this might mean no changes was made"
|
|
139
|
-
// );
|
|
140
|
-
// return;
|
|
141
|
-
// }
|
|
142
|
-
// const patch: PatchJSON = [
|
|
143
|
-
// {
|
|
144
|
-
// value: input.data.src,
|
|
145
|
-
// op: "replace",
|
|
146
|
-
// path: `/${pathParts.slice(0, -1).join("/")}/$${
|
|
147
|
-
// pathParts[pathParts.length - 1]
|
|
148
|
-
// }`,
|
|
149
|
-
// },
|
|
150
|
-
// ];
|
|
151
|
-
// if (input.data.metadata) {
|
|
152
|
-
// if (input.data.addMetadata) {
|
|
153
|
-
// patch.push({
|
|
154
|
-
// value: input.data.metadata,
|
|
155
|
-
// op: "add",
|
|
156
|
-
// path: `/${pathParts.join("/")}/metadata`,
|
|
157
|
-
// });
|
|
158
|
-
// } else {
|
|
159
|
-
// patch.push({
|
|
160
|
-
// value: input.data.metadata,
|
|
161
|
-
// op: "replace",
|
|
162
|
-
// path: `/${pathParts.join("/")}/metadata`,
|
|
163
|
-
// });
|
|
164
|
-
// }
|
|
165
|
-
// }
|
|
166
|
-
// console.log("patch", patch);
|
|
167
|
-
// return valApi.patchModuleContent(moduleId, patch);
|
|
168
|
-
// } else if (input.type === "richtext") {
|
|
169
|
-
// const patch: PatchJSON = [
|
|
170
|
-
// {
|
|
171
|
-
// value: input.data,
|
|
172
|
-
// op: "replace",
|
|
173
|
-
// path: `/${modulePath
|
|
174
|
-
// .split(".")
|
|
175
|
-
// .map((p) => JSON.parse(p))
|
|
176
|
-
// .join("/")}`,
|
|
177
|
-
// },
|
|
178
|
-
// ];
|
|
179
|
-
// return valApi.patchModuleContent(moduleId, patch);
|
|
180
|
-
// }
|
|
181
|
-
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
182
|
-
// throw new Error(`Unsupported input type: ${(input as any).type}`);
|
|
183
|
-
// } else {
|
|
184
|
-
// console.error("Submitted incomplete input, ignoring...");
|
|
185
|
-
// return Promise.resolve();
|
|
186
|
-
// }
|
|
187
|
-
// })
|
|
188
|
-
// ).then((res) => {
|
|
189
|
-
// console.log("patched", res);
|
|
190
|
-
// });
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
return (
|
|
194
|
-
<div className="flex flex-col items-start px-4">
|
|
195
|
-
{selectedModule ? (
|
|
196
|
-
<div className="flex flex-col items-start w-full py-3 gap-[36px] font-normal">
|
|
197
|
-
{Object.entries(inputs).map(([path, input]) => {
|
|
198
|
-
return (
|
|
199
|
-
<div key={path} className={"flex flex-col justify-start "}>
|
|
200
|
-
{input.status === "requested" && (
|
|
201
|
-
<div className="p-2 text-center text-primary">Loading...</div>
|
|
202
|
-
)}
|
|
203
|
-
<div className="flex flex-col gap-1 font-[550]">
|
|
204
|
-
<p>{path.split(".").slice(-1)[0].split('"').join("")}</p>
|
|
205
|
-
{input.status === "completed" && input.type === "image" && (
|
|
206
|
-
<ImageForm
|
|
207
|
-
name={path}
|
|
208
|
-
data={input.data}
|
|
209
|
-
onChange={(data) => {
|
|
210
|
-
if (data.value) {
|
|
211
|
-
setInputs({
|
|
212
|
-
...inputs,
|
|
213
|
-
[path]: {
|
|
214
|
-
status: "completed",
|
|
215
|
-
type: "image",
|
|
216
|
-
data: data.value,
|
|
217
|
-
},
|
|
218
|
-
});
|
|
219
|
-
setInputIsDirty({ ...inputIsDirty, [path]: true });
|
|
220
|
-
}
|
|
221
|
-
}}
|
|
222
|
-
error={null}
|
|
223
|
-
/>
|
|
224
|
-
)}
|
|
225
|
-
{input.status === "completed" && input.type === "text" && (
|
|
226
|
-
<TextArea
|
|
227
|
-
name={path}
|
|
228
|
-
text={input.data}
|
|
229
|
-
onChange={(data) => {
|
|
230
|
-
setInputs({
|
|
231
|
-
...inputs,
|
|
232
|
-
[path]: {
|
|
233
|
-
status: "completed",
|
|
234
|
-
type: "text",
|
|
235
|
-
data: data,
|
|
236
|
-
},
|
|
237
|
-
});
|
|
238
|
-
setInputIsDirty({ ...inputIsDirty, [path]: true });
|
|
239
|
-
}}
|
|
240
|
-
/>
|
|
241
|
-
)}
|
|
242
|
-
{input.status === "completed" &&
|
|
243
|
-
input.type === "richtext" && (
|
|
244
|
-
<RichTextEditor
|
|
245
|
-
richtext={input.data}
|
|
246
|
-
onEditor={(editor) => {
|
|
247
|
-
setRichTextEditor({
|
|
248
|
-
...richTextEditor,
|
|
249
|
-
[path]: editor,
|
|
250
|
-
});
|
|
251
|
-
}}
|
|
252
|
-
/>
|
|
253
|
-
)}
|
|
254
|
-
{inputIsDirty[path] && (
|
|
255
|
-
<Button onClick={() => patchElement(path)}>
|
|
256
|
-
Save changes
|
|
257
|
-
</Button>
|
|
258
|
-
)}
|
|
259
|
-
</div>
|
|
260
|
-
</div>
|
|
261
|
-
);
|
|
262
|
-
})}
|
|
263
|
-
</div>
|
|
264
|
-
) : (
|
|
265
|
-
<h1 className="px-4 py-3">No module selected</h1>
|
|
266
|
-
)}
|
|
267
|
-
</div>
|
|
268
|
-
);
|
|
269
|
-
};
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { SerializedModule } from "@valbuild/core";
|
|
2
|
-
import { ValApi } from "@valbuild/core";
|
|
3
|
-
import classNames from "classnames";
|
|
4
|
-
import React, { useState, FC, ReactNode, useEffect } from "react";
|
|
5
|
-
import { ValDashboardEditor } from "./ValDashboardEditor";
|
|
6
|
-
import { ValTreeNavigator } from "./ValTreeNavigator";
|
|
7
|
-
|
|
8
|
-
interface PanelProps {
|
|
9
|
-
header?: ReactNode;
|
|
10
|
-
width?: number;
|
|
11
|
-
onResize?: (width: number) => void;
|
|
12
|
-
collapsible?: boolean;
|
|
13
|
-
collapsed?: boolean;
|
|
14
|
-
onCollapse?: () => void;
|
|
15
|
-
children: ReactNode;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const Panel: FC<PanelProps> = ({
|
|
19
|
-
header,
|
|
20
|
-
width,
|
|
21
|
-
onResize,
|
|
22
|
-
collapsible,
|
|
23
|
-
collapsed,
|
|
24
|
-
onCollapse,
|
|
25
|
-
children,
|
|
26
|
-
}) => {
|
|
27
|
-
const handleMouseDown = (e: React.MouseEvent) => {
|
|
28
|
-
if (!onResize) return;
|
|
29
|
-
|
|
30
|
-
e.preventDefault();
|
|
31
|
-
const initialX = e.clientX;
|
|
32
|
-
const initialWidth = width || 0;
|
|
33
|
-
|
|
34
|
-
const handleMouseMove = (moveEvent: MouseEvent) => {
|
|
35
|
-
const newWidth = initialWidth + moveEvent.clientX - initialX;
|
|
36
|
-
onResize(newWidth);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const handleMouseUp = () => {
|
|
40
|
-
window.removeEventListener("mousemove", handleMouseMove);
|
|
41
|
-
window.removeEventListener("mouseup", handleMouseUp);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
window.addEventListener("mousemove", handleMouseMove);
|
|
45
|
-
window.addEventListener("mouseup", handleMouseUp);
|
|
46
|
-
};
|
|
47
|
-
return (
|
|
48
|
-
<>
|
|
49
|
-
{!collapsed ? (
|
|
50
|
-
<div
|
|
51
|
-
className={classNames(
|
|
52
|
-
"relative border border-dark-gray min-w-0 h-full overflow-auto",
|
|
53
|
-
{
|
|
54
|
-
"flex-grow": !width,
|
|
55
|
-
}
|
|
56
|
-
)}
|
|
57
|
-
style={width ? { width: `${width}px` } : {}}
|
|
58
|
-
>
|
|
59
|
-
{onResize && (
|
|
60
|
-
<div
|
|
61
|
-
className="absolute inset-y-0 right-0 cursor-col-resize w-[1px] bg-dark-gray hover:w-[2px] hover:bg-light-gray"
|
|
62
|
-
onMouseDown={handleMouseDown}
|
|
63
|
-
/>
|
|
64
|
-
)}
|
|
65
|
-
<div className="bg-gray-300 border border-dark-gray flex justify-between items-center h-[75px] w-full font-serif px-4">
|
|
66
|
-
{header}
|
|
67
|
-
{collapsible && (
|
|
68
|
-
<button
|
|
69
|
-
onClick={onCollapse}
|
|
70
|
-
className="px-2 py-1 font-bold text-white bg-red-500 rounded hover:bg-red-700"
|
|
71
|
-
>
|
|
72
|
-
{collapsed ? "Expand" : "Collapse"}
|
|
73
|
-
</button>
|
|
74
|
-
)}
|
|
75
|
-
</div>
|
|
76
|
-
<div>{children}</div>
|
|
77
|
-
</div>
|
|
78
|
-
) : (
|
|
79
|
-
<button
|
|
80
|
-
onClick={onCollapse}
|
|
81
|
-
className="absolute inset-y-0 right-[16px] w-fit flex items-center justify-end"
|
|
82
|
-
>
|
|
83
|
-
open panel again
|
|
84
|
-
</button>
|
|
85
|
-
)}
|
|
86
|
-
</>
|
|
87
|
-
);
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
interface ValDashboardGridProps {
|
|
91
|
-
valApi: ValApi;
|
|
92
|
-
editMode: boolean;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export const ValDashboardGrid: FC<ValDashboardGridProps> = ({
|
|
96
|
-
valApi,
|
|
97
|
-
editMode,
|
|
98
|
-
}) => {
|
|
99
|
-
const [widths, setWidths] = useState([300, (2 * window.innerWidth) / 3]);
|
|
100
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
101
|
-
const [modules, setModules] = useState<SerializedModule[]>([]);
|
|
102
|
-
const [selectedPath, setSelectedPath] = useState<string>("");
|
|
103
|
-
useEffect(() => {
|
|
104
|
-
// valApi.getTree({}).then((modules) => {
|
|
105
|
-
// setModules(modules);
|
|
106
|
-
// });
|
|
107
|
-
}, [editMode]);
|
|
108
|
-
|
|
109
|
-
const handleResize = (index: number) => (width: number) => {
|
|
110
|
-
setWidths((prevWidths) => {
|
|
111
|
-
const newWidths = [...prevWidths];
|
|
112
|
-
newWidths[index] = Math.max(width, 300);
|
|
113
|
-
return newWidths;
|
|
114
|
-
});
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
return (
|
|
118
|
-
<div className="flex justify-start h-screen">
|
|
119
|
-
<Panel width={widths[0]} onResize={handleResize(0)}>
|
|
120
|
-
<ValTreeNavigator
|
|
121
|
-
modules={modules}
|
|
122
|
-
selectedModule={selectedPath}
|
|
123
|
-
setSelectedModule={setSelectedPath}
|
|
124
|
-
valApi={valApi}
|
|
125
|
-
/>
|
|
126
|
-
</Panel>
|
|
127
|
-
<Panel
|
|
128
|
-
header={
|
|
129
|
-
selectedPath && (
|
|
130
|
-
<div className="w-full max-w-[1000px] bg-dark-gray px-4 py-2 rounded-lg">
|
|
131
|
-
{selectedPath}
|
|
132
|
-
</div>
|
|
133
|
-
)
|
|
134
|
-
}
|
|
135
|
-
width={widths[1]}
|
|
136
|
-
onResize={handleResize(1)}
|
|
137
|
-
>
|
|
138
|
-
<ValDashboardEditor selectedPath={selectedPath} valApi={valApi} />
|
|
139
|
-
</Panel>
|
|
140
|
-
</div>
|
|
141
|
-
);
|
|
142
|
-
};
|