From 4e81b4a8ee76ecacad90ff14c990dcf62a452e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?= Date: Mon, 21 Mar 2022 11:45:05 +0000 Subject: [PATCH] WIP: Experimenting how to make the layout more scalable Inspired by * https://gregberge.com/blog/react-scalable-layout > In a React application, composition must always be preferred. > The layout is no exception. * https://www.sabinthedev.com/blog/using-a-react-components-function-from-its-parent --- web/src/DBusError.jsx | 31 +++++++-------- web/src/InstallationFinished.jsx | 58 +++++++++++++-------------- web/src/InstallationProgress.jsx | 11 +++--- web/src/LanguageSelector.jsx | 3 ++ web/src/LoadingEnvironment.jsx | 20 +++++----- web/src/LoginForm.jsx | 28 ++++++------- web/src/Overview.jsx | 11 ++---- web/src/ProbingProgress.jsx | 11 +++--- web/src/{ => layout}/Layout.jsx | 4 +- web/src/layout/SectionTitle.jsx | 18 +++++++++ web/src/layout/slot.jsx | 68 ++++++++++++++++++++++++++++++++ web/src/main.jsx | 6 ++- 12 files changed, 176 insertions(+), 93 deletions(-) rename web/src/{ => layout}/Layout.jsx (96%) create mode 100644 web/src/layout/SectionTitle.jsx create mode 100644 web/src/layout/slot.jsx diff --git a/web/src/DBusError.jsx b/web/src/DBusError.jsx index 6a01cf1cd9..867ee34855 100644 --- a/web/src/DBusError.jsx +++ b/web/src/DBusError.jsx @@ -22,14 +22,14 @@ import React from "react"; import { Button, Title, EmptyState, EmptyStateIcon, EmptyStateBody } from "@patternfly/react-core"; -import Layout from "./Layout"; -import Center from "./Center"; - import { EOS_ANNOUNCEMENT as SectionIcon, EOS_ENDPOINTS_DISCONNECTED as DisconnectionIcon } from "eos-icons-react"; +import Center from "./Center"; +import { SectionTitle } from "./layout/SectionTitle"; + // TODO: an example const ReloadAction = () => ( - - - - - +
+ Installation Finished + + + + Congratulations! + + +
+ The installation on your machine is complete. + + At this point you can 'Restart installation' to continue playing with D-Installer or + manually reboot the machine to log in to the new system. + + Have a lot of fun! Your openSUSE Development Team. +
+ + + +
+
+
); } diff --git a/web/src/InstallationProgress.jsx b/web/src/InstallationProgress.jsx index 966df7063c..b4d6eec948 100644 --- a/web/src/InstallationProgress.jsx +++ b/web/src/InstallationProgress.jsx @@ -22,18 +22,17 @@ import React from "react"; import Center from "./Center"; -import Layout from "./Layout"; import ProgressReport from "./ProgressReport"; +import { SectionTitle } from "./layout/SectionTitle"; import { EOS_DOWNLOADING as SectionIcon } from "eos-icons-react"; function InstallationProgress() { return ( - -
- -
-
+
+ Installing + +
); } diff --git a/web/src/LanguageSelector.jsx b/web/src/LanguageSelector.jsx index 58f8512cbc..3ec3a4adac 100644 --- a/web/src/LanguageSelector.jsx +++ b/web/src/LanguageSelector.jsx @@ -32,6 +32,8 @@ import { ModalVariant } from "@patternfly/react-core"; +import { SectionTitle } from "./layout/SectionTitle"; + const reducer = (state, action) => { switch (action.type) { case "LOAD": { @@ -132,6 +134,7 @@ export default function LanguageSelector() { ]} > + Select the language
-
- - - - Loading installation environment, please wait. - - -
- +
+ + + + Loading installation environment, please wait. + + +
); } diff --git a/web/src/LoginForm.jsx b/web/src/LoginForm.jsx index 6b22a0bb30..6392680b20 100644 --- a/web/src/LoginForm.jsx +++ b/web/src/LoginForm.jsx @@ -24,7 +24,7 @@ import { useAuthContext } from "./context/auth"; import { Alert, Button, Form, FormAlert, FormGroup, TextInput } from "@patternfly/react-core"; import Center from "./Center"; -import Layout from "./Layout"; +import { SectionTitle } from "./layout/SectionTitle"; const formError = error => ( @@ -64,19 +64,19 @@ function LoginForm() { }; return ( - -
- - {error && formError(error)} - - - - - - - -
-
+
+ Please, log in +
+ {error && formError(error)} + + + + + + + + +
); } diff --git a/web/src/Overview.jsx b/web/src/Overview.jsx index 0c5232cb7d..465ad4fe14 100644 --- a/web/src/Overview.jsx +++ b/web/src/Overview.jsx @@ -24,11 +24,11 @@ import { useInstallerClient } from "./context/installer"; import { Button, Flex, FlexItem } from "@patternfly/react-core"; -import Layout from "./Layout"; import Category from "./Category"; import LanguageSelector from "./LanguageSelector"; import ProductSelector from "./ProductSelector"; import Storage from "./Storage"; +import { SectionTitle } from "./layout/SectionTitle"; import { EOS_FACT_CHECK as OverviewIcon, @@ -71,13 +71,10 @@ function Overview() { }; return ( - + <> + Installation Summary {renderCategories()} - + ); } diff --git a/web/src/ProbingProgress.jsx b/web/src/ProbingProgress.jsx index 626e926e5e..7ccea16cd0 100644 --- a/web/src/ProbingProgress.jsx +++ b/web/src/ProbingProgress.jsx @@ -22,17 +22,16 @@ import React from "react"; import Center from "./Center"; -import Layout from "./Layout"; import ProgressReport from "./ProgressReport"; +import { SectionTitle } from "./layout/SectionTitle"; import { EOS_MULTISTATE as SectionIcon } from "eos-icons-react"; const ProbingProgress = () => ( - -
- -
-
+
+ Probing + +
); export default ProbingProgress; diff --git a/web/src/Layout.jsx b/web/src/layout/Layout.jsx similarity index 96% rename from web/src/Layout.jsx rename to web/src/layout/Layout.jsx index 24a4b141a3..0af9104e26 100644 --- a/web/src/Layout.jsx +++ b/web/src/layout/Layout.jsx @@ -21,6 +21,8 @@ import React from "react"; +import { SectionTitlePlaceholder } from "./SectionTitle"; + /** * D-Installer main layout component. * @@ -76,7 +78,7 @@ function Layout({

{SectionIcon && } - {sectionTitle} + D-Installer

diff --git a/web/src/layout/SectionTitle.jsx b/web/src/layout/SectionTitle.jsx new file mode 100644 index 0000000000..2f4e6bd1c4 --- /dev/null +++ b/web/src/layout/SectionTitle.jsx @@ -0,0 +1,18 @@ +import React, { useRef } from "react"; + +import createLayoutSlot from "./slot"; + +const Slot = createLayoutSlot(); + +export function SectionTitlePlaceholder({ children, ...props }) { + const slotRef = useRef(); + return ( + + {children} + + ); +} + +export function SectionTitle({ children }) { + return {children}; +} diff --git a/web/src/layout/slot.jsx b/web/src/layout/slot.jsx new file mode 100644 index 0000000000..19313fae71 --- /dev/null +++ b/web/src/layout/slot.jsx @@ -0,0 +1,68 @@ +import React, { forwardRef, useState, useImperativeHandle, useEffect } from "react"; + +/** + * Function for creating a layout slot. + * + */ +export default function createLayoutSlot() { + const slot = { ref: null, defaultContent: null, previousContent: null }; + + const Placeholder = forwardRef(({ as: SlotContainer = "div", children, ...props }, ref) => { + console.log("props", props, "children", children); + const [content, setContent] = useState(children); + console.log("Rendering slot with default", children, "and content", content); + slot.defaultContent = children; + + useImperativeHandle(ref, () => ({ + changeContent: newContent => { + console.info("Saving previous slot content ", content); + slot.previousContent = content; + console.info("Changing the slot content to", newContent); + setContent(newContent); + } + })); + + slot.ref = ref; + + return ( + + {content} + + ); + }); + + const Content = ({ children }) => { + const container = slot.ref; + console.log("rendering Slot Content", children, container); + + useEffect(() => { + // Set the new content, only if the container is available + if (container.current) { + console.log("calling #ChangeContent of ", container.current); + + container.current.changeContent(children); + } + + return () => { + if (!container) return; + // FIXME: this is tricky because usually defaultContent became previousContent + // Maybe defaultContent does not make sense + const contentToRestore = slot.previousContent ? slot.previousContent: slot.defaultContent; + + // Restore previous or initial content + if (contentToRestore) { + container.current.changeContent(contentToRestore); + slot.previousContent = null; + } + }; + }, [children]); + + // Always return null, as it is changing the content of the slot; + return null; + }; + + Placeholder.displayName = "Layout Slot Placeholder"; + Content.displayName = "Layout Slot Content"; + + return { Placeholder, Content }; +} diff --git a/web/src/main.jsx b/web/src/main.jsx index 1f1ead3d14..94c189df3f 100644 --- a/web/src/main.jsx +++ b/web/src/main.jsx @@ -27,6 +27,8 @@ import { InstallerClientProvider } from "./context/installer"; import { AuthProvider } from "./context/auth"; import { createClient } from "./lib/client"; +import Layout from "./layout/Layout"; + import "./patternfly.scss"; import "./app.scss"; import "./layout.scss"; @@ -37,7 +39,9 @@ ReactDOM.render( - + + + ,