impl basic grid layout

This commit is contained in:
Yanzhen Yu 2021-08-03 16:31:33 +08:00
parent e5a2895758
commit 011ccde187
9 changed files with 246 additions and 20 deletions

View File

@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>meta-ui runtime example: basic grid layout</title>
</head>
<body>
<div id="root"></div>
<script type="module">
import renderApp from "../../src/main.tsx";
renderApp({
version: "example/v1",
metadata: {
name: "basic_grid_layout",
description: "basic grid layout example",
},
spec: {
components: [
{
id: "grid_container",
type: "core/v1/grid_layout",
properties: {
layout: [
{
x: 4,
y: 0,
w: 4,
h: 9,
i: "box1",
},
{
x: 8,
y: 0,
w: 2,
h: 12,
i: "box2",
},
],
},
traits: [],
},
{
id: "box1",
type: "chakra_ui/v1/box",
properties: {
border: "2px solid blue",
w: "100%",
h: "100%",
},
traits: [
{
type: "core/v1/slot",
properties: {
container: {
id: "grid_container",
slot: "container",
},
},
},
],
},
{
id: "box2",
type: "chakra_ui/v1/box",
properties: {
bgColor: "pink",
w: "100%",
h: "100%",
},
traits: [
{
type: "core/v1/slot",
properties: {
container: {
id: "grid_container",
slot: "container",
},
},
},
],
},
],
},
});
</script>
</body>
</html>

View File

@ -17,6 +17,7 @@
"nanoid": "^3.1.23",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-grid-layout": "^1.2.5",
"react-markdown": "^6.0.2",
"zustand": "^3.5.5"
},
@ -24,6 +25,7 @@
"@types/lodash": "^4.14.170",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react-grid-layout": "^1.1.2",
"@vitejs/plugin-react-refresh": "^1.3.1",
"typescript": "^4.3.2",
"vite": "^2.3.8"

View File

@ -16,10 +16,13 @@ import { setStore, useStore, emitter } from "./store";
import { ContainerPropertySchema } from "./traits/core/slot";
import { Static } from "@sinclair/typebox";
const ImplWrapper: React.FC<{
component: RuntimeApplication["spec"]["components"][0];
slotsMap: SlotsMap | undefined;
}> = ({ component: c, slotsMap }) => {
const ImplWrapper = React.forwardRef<
HTMLDivElement,
{
component: RuntimeApplication["spec"]["components"][0];
slotsMap: SlotsMap | undefined;
}
>(({ component: c, slotsMap, ...props }, ref) => {
const Impl = registry.getComponent(
c.parsedType.version,
c.parsedType.name
@ -90,8 +93,13 @@ const ImplWrapper: React.FC<{
C = <W>{C}</W>;
}
return C;
};
return (
<div key={c.id} data-meta-ui-id={c.id} ref={ref} {...props}>
{C}
{props.children}
</div>
);
});
const DebugStore: React.FC = () => {
const store = useStore();
@ -129,7 +137,13 @@ const DebugEvent: React.FC = () => {
};
export type ComponentsMap = Map<string, SlotsMap>;
export type SlotsMap = Map<string, Array<React.FC>>;
export type SlotsMap = Map<
string,
Array<{
component: React.FC;
id: string;
}>
>;
export function resolveNestedComponents(app: RuntimeApplication): {
topLevelComponents: RuntimeApplication["spec"]["components"];
componentsMap: ComponentsMap;
@ -154,9 +168,17 @@ export function resolveNestedComponents(app: RuntimeApplication): {
componentsMap
.get(id)
?.get(slot)
?.push(() => (
<ImplWrapper component={c} slotsMap={componentsMap.get(c.id)} />
));
?.push({
component: React.forwardRef<HTMLDivElement, any>((props, ref) => (
<ImplWrapper
component={c}
slotsMap={componentsMap.get(c.id)}
{...props}
ref={ref}
/>
)),
id: c.id,
});
} else {
topLevelComponents.push(c);
}

View File

@ -0,0 +1,29 @@
import React from "react";
import RGL, { WidthProvider } from "react-grid-layout";
import { Static, Type } from "@sinclair/typebox";
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";
const ReactGridLayout = WidthProvider(RGL);
export const LayoutPropertySchema = Type.Array(
Type.Object({
x: Type.Number(),
y: Type.Number(),
w: Type.Number(),
h: Type.Number(),
i: Type.String(),
})
);
const GridLayout: React.FC<{
layout: Static<typeof LayoutPropertySchema>;
}> = ({ children, layout }) => {
return (
<ReactGridLayout rowHeight={30} layout={layout}>
{children}
</ReactGridLayout>
);
};
export default GridLayout;

View File

@ -1,17 +1,17 @@
import React from "react";
import { SlotsMap } from "../../App";
export function getSlots(slotsMap: SlotsMap | undefined, slot: string) {
return (slotsMap?.get(slot) || []).map(({ component: ImplWrapper, id }) => (
<ImplWrapper key={id} />
));
}
const Slot: React.FC<{ slotsMap: SlotsMap | undefined; slot: string }> = ({
slotsMap,
slot,
}) => {
return (
<>
{(slotsMap?.get(slot) || []).map((ImplWrapper, idx) => (
<ImplWrapper key={idx} />
))}
</>
);
return <>{getSlots(slotsMap, slot)}</>;
};
export default Slot;

View File

@ -0,0 +1,39 @@
import React, { Suspense } from "react";
import { ComponentImplementation } from "../../registry";
import { createComponent } from "@meta-ui/core";
import { getSlots } from "../_internal/Slot";
import { LayoutPropertySchema } from "../../components/_internal/GridLayout";
import { Static } from "@sinclair/typebox";
const BaseGridLayout = React.lazy(
() => import("../../components/_internal/GridLayout")
);
const GridLayout: ComponentImplementation<{
layout: Static<typeof LayoutPropertySchema>;
}> = ({ slotsMap, layout = [] }) => {
return (
<Suspense fallback={null}>
<BaseGridLayout layout={layout}>
{getSlots(slotsMap, "container")}
</BaseGridLayout>
</Suspense>
);
};
export default {
...createComponent({
version: "core/v1",
metadata: {
name: "grid_layout",
description: "drag and drop to layout in a grid",
},
spec: {
properties: [{ name: "layout", ...LayoutPropertySchema }],
acceptTraits: [],
state: {},
methods: [],
},
}),
impl: GridLayout,
};

View File

@ -6,6 +6,7 @@ import { SlotsMap } from "./App";
/* --- plain --- */
import PlainButton from "./components/plain/Button";
import CoreText from "./components/core/Text";
import CoreGridLayout from "./components/core/GridLayout";
/* --- chakra-ui --- */
import ChakraUIRoot from "./components/chakra-ui/Root";
import ChakraUIButton from "./components/chakra-ui/Button";
@ -102,6 +103,7 @@ export const registry = new Registry();
registry.registerComponent(PlainButton);
registry.registerComponent(CoreText);
registry.registerComponent(CoreGridLayout);
registry.registerComponent(ChakraUIRoot);
registry.registerComponent(ChakraUIButton);
registry.registerComponent(ChakraUITabs);

View File

@ -16,7 +16,7 @@ const Hidden: React.FC<HiddenProps> = ({ hidden: _hidden, children }) => {
return <>{children}</>;
};
const useHiddenState: TraitImplementation<HiddenProps> = ({ hidden }) => {
const useHiddenTrait: TraitImplementation<HiddenProps> = ({ hidden }) => {
return {
props: null,
component: (props) => <Hidden {...props} hidden={hidden} />,
@ -43,5 +43,5 @@ export default {
methods: [],
},
}),
impl: useHiddenState,
impl: useHiddenTrait,
};

View File

@ -2267,6 +2267,13 @@
dependencies:
"@types/react" "*"
"@types/react-grid-layout@^1.1.2":
version "1.1.2"
resolved "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-1.1.2.tgz#be46ac453e5193f512b0c4c90f36342e9e82258c"
integrity sha512-jGpMO5VTXgrCsOoxGHSzfM/9sihlN6GDNyssaMdl73Q7Vtrbe0VVYxoavodommoRXS29hLW/2RLbQ/Oj5++slg==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^17.0.0":
version "17.0.14"
resolved "http://192.168.26.29:7001/@types/react/download/@types/react-17.0.14.tgz#f0629761ca02945c4e8fea99b8177f4c5c61fb0f"
@ -2937,6 +2944,11 @@ cjs-module-lexer@^1.0.0:
resolved "http://192.168.26.29:7001/cjs-module-lexer/download/cjs-module-lexer-1.2.1.tgz#2fd46d9906a126965aa541345c499aaa18e8cd73"
integrity sha1-L9RtmQahJpZapUE0XEmaqhjozXM=
classnames@2.3.1, classnames@^2.2.5:
version "2.3.1"
resolved "http://192.168.26.29:7001/classnames/download/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
integrity sha1-38+jiR4wbsHa0QXQ6I9EF7hTXo4=
clean-stack@^2.0.0:
version "2.2.0"
resolved "http://192.168.26.29:7001/clean-stack/download/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
@ -5405,6 +5417,11 @@ lodash.clonedeep@^4.5.0:
resolved "http://192.168.26.29:7001/lodash.clonedeep/download/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
lodash.isequal@^4.0.0:
version "4.5.0"
resolved "http://192.168.26.29:7001/lodash.isequal/download/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.ismatch@^4.4.0:
version "4.4.0"
resolved "http://192.168.26.29:7001/lodash.ismatch/download/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37"
@ -6523,7 +6540,7 @@ promzard@^0.3.0:
dependencies:
read "1"
prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@15.x, prop-types@^15.0.0, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "http://192.168.26.29:7001/prop-types/download/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha1-UsQedbjIfnK52TYOAga5ncv/psU=
@ -6612,6 +6629,14 @@ react-dom@^17.0.0:
object-assign "^4.1.1"
scheduler "^0.20.2"
react-draggable@^4.0.0, react-draggable@^4.0.3:
version "4.4.3"
resolved "http://192.168.26.29:7001/react-draggable/download/react-draggable-4.4.3.tgz#0727f2cae5813e36b0e4962bf11b2f9ef2b406f3"
integrity sha1-ByfyyuWBPjaw5JYr8RsvnvK0BvM=
dependencies:
classnames "^2.2.5"
prop-types "^15.6.0"
react-fast-compare@3.2.0:
version "3.2.0"
resolved "http://192.168.26.29:7001/react-fast-compare/download/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
@ -6629,6 +6654,17 @@ react-focus-lock@2.5.0:
use-callback-ref "^1.2.1"
use-sidecar "^1.0.1"
react-grid-layout@^1.2.5:
version "1.2.5"
resolved "http://192.168.26.29:7001/react-grid-layout/download/react-grid-layout-1.2.5.tgz#fa40288d5a1fa783484c44ce78b1e10eb5313d26"
integrity sha1-+kAojVofp4NITETOeLHhDrUxPSY=
dependencies:
classnames "2.3.1"
lodash.isequal "^4.0.0"
prop-types "^15.0.0"
react-draggable "^4.0.0"
react-resizable "^3.0.1"
react-is@^16.7.0, react-is@^16.8.1:
version "16.13.1"
resolved "http://192.168.26.29:7001/react-is/download/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@ -6682,6 +6718,14 @@ react-remove-scroll@2.4.1:
use-callback-ref "^1.2.3"
use-sidecar "^1.0.1"
react-resizable@^3.0.1:
version "3.0.4"
resolved "http://192.168.26.29:7001/react-resizable/download/react-resizable-3.0.4.tgz#aa20108eff28c52c6fddaa49abfbef8abf5e581b"
integrity sha1-qiAQjv8oxSxv3apJq/vvir9eWBs=
dependencies:
prop-types "15.x"
react-draggable "^4.0.3"
react-style-singleton@^2.1.0:
version "2.1.1"
resolved "http://192.168.26.29:7001/react-style-singleton/download/react-style-singleton-2.1.1.tgz#ce7f90b67618be2b6b94902a30aaea152ce52e66"