Skip to content

Commit

Permalink
feat: implement scrollable form components
Browse files Browse the repository at this point in the history
Signed-off-by: Mason Hu <[email protected]>
  • Loading branch information
mas-who committed Jan 16, 2024
1 parent 64ff69e commit 7e16b02
Show file tree
Hide file tree
Showing 26 changed files with 226 additions and 134 deletions.
41 changes: 41 additions & 0 deletions src/components/ScrollableContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { DependencyList, FC, ReactNode, useEffect, useRef } from "react";
import useEventListener from "@use-it/event-listener";
import { getAbsoluteHeightBelow, getParentsBottomSpacing } from "util/helpers";

interface Props {
children: ReactNode;
dependencies: DependencyList;
belowId?: string;
}

const ScrollableContainer: FC<Props> = ({
dependencies,
children,
belowId = "",
}) => {
const ref = useRef<HTMLDivElement>(null);

const updateChildContainerHeight = () => {
const childContainer = ref.current?.children[0];
if (!childContainer) {
return;
}
const above = childContainer.getBoundingClientRect().top + 1;
const below = getAbsoluteHeightBelow(belowId);
const parentsBottomSpacing = getParentsBottomSpacing(childContainer);
const offset = Math.ceil(above + below + parentsBottomSpacing);
const style = `height: calc(100vh - ${offset}px); min-height: calc(100vh - ${offset}px)`;
childContainer.setAttribute("style", style);
};

useEventListener("resize", updateChildContainerHeight);
useEffect(updateChildContainerHeight, [...dependencies, ref]);

return (
<div ref={ref} className="scrollable-container">
<div className="content-details">{children}</div>
</div>
);
};

export default ScrollableContainer;
21 changes: 21 additions & 0 deletions src/components/ScrollableForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { FC, ReactNode } from "react";
import ScrollableContainer from "./ScrollableContainer";
import { useNotify } from "@canonical/react-components";

interface Props {
children: ReactNode;
}

const ScrollableForm: FC<Props> = ({ children }) => {
const notify = useNotify();
return (
<ScrollableContainer
dependencies={[notify.notification]}
belowId="form-footer"
>
{children}
</ScrollableContainer>
);
};

export default ScrollableForm;
24 changes: 8 additions & 16 deletions src/components/ScrollableTable.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,29 @@
import React, { DependencyList, FC, ReactNode, useEffect, useRef } from "react";
import useEventListener from "@use-it/event-listener";
import { getParentsBottomSpacing } from "util/helpers";
import { getAbsoluteHeightBelow, getParentsBottomSpacing } from "util/helpers";

interface Props {
children: ReactNode;
dependencies: DependencyList;
belowId?: string;
}

const ScrollableTable: FC<Props> = ({ dependencies, children, belowId }) => {
const ScrollableTable: FC<Props> = ({
dependencies,
children,
belowId = "",
}) => {
const ref = useRef<HTMLDivElement>(null);

const getAbsoluteHeightBelow = () => {
const element = belowId ? document.getElementById(belowId) : undefined;
if (!element) {
return 0;
}
const style = window.getComputedStyle(element);
const margin = parseFloat(style.marginTop) + parseFloat(style.marginBottom);
const padding =
parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
return element.offsetHeight + margin + padding + 1;
};

const updateTBodyHeight = () => {
const table = ref.current?.children[0];
if (!table || table.children.length !== 2) {
return;
}
const tBody = table.children[1];
const above = tBody.getBoundingClientRect().top + 1;
const below = getAbsoluteHeightBelow();
const parentsBottomSpacing = getParentsBottomSpacing(table as HTMLElement);
const below = getAbsoluteHeightBelow(belowId);
const parentsBottomSpacing = getParentsBottomSpacing(table);
const offset = Math.ceil(above + below + parentsBottomSpacing);
const style = `height: calc(100vh - ${offset}px); min-height: calc(100vh - ${offset}px)`;
tBody.setAttribute("style", style);
Expand Down
37 changes: 20 additions & 17 deletions src/components/forms/DiskDeviceForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import DiskDeviceFormRoot from "./DiskDeviceFormRoot";
import DiskDeviceFormInherited from "./DiskDeviceFormInherited";
import DiskDeviceFormCustom from "./DiskDeviceFormCustom";
import classnames from "classnames";
import ScrollableForm from "components/ScrollableForm";

interface Props {
formik: InstanceAndProfileFormikProps;
Expand Down Expand Up @@ -58,23 +59,25 @@ const DiskDeviceForm: FC<Props> = ({ formik, project }) => {
"disk-device-form--edit": !formik.values.readOnly,
})}
>
{/* hidden submit to enable enter key in inputs */}
<Input type="submit" hidden />
<DiskDeviceFormRoot
formik={formik}
project={project}
pools={pools}
profiles={profiles}
/>
<DiskDeviceFormInherited
formik={formik}
inheritedVolumes={inheritedVolumes}
/>
<DiskDeviceFormCustom
formik={formik}
project={project}
inheritedVolumes={inheritedVolumes}
/>
<ScrollableForm>
{/* hidden submit to enable enter key in inputs */}
<Input type="submit" hidden />
<DiskDeviceFormRoot
formik={formik}
project={project}
pools={pools}
profiles={profiles}
/>
<DiskDeviceFormInherited
formik={formik}
inheritedVolumes={inheritedVolumes}
/>
<DiskDeviceFormCustom
formik={formik}
project={project}
inheritedVolumes={inheritedVolumes}
/>
</ScrollableForm>
</div>
);
};
Expand Down
5 changes: 3 additions & 2 deletions src/pages/instances/forms/EditInstanceDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useSettings } from "context/useSettings";
import MigrateInstanceBtn from "pages/instances/actions/MigrateInstanceBtn";
import { isClusteredServer } from "util/settings";
import AutoExpandingTextArea from "components/AutoExpandingTextArea";
import ScrollableForm from "components/ScrollableForm";

export const instanceEditDetailPayload = (values: EditInstanceFormValues) => {
return {
Expand All @@ -28,7 +29,7 @@ const EditInstanceDetails: FC<Props> = ({ formik, project }) => {
const isClustered = isClusteredServer(settings);

return (
<div className="details">
<ScrollableForm>
<Row>
<Col size={12}>
<Input
Expand Down Expand Up @@ -91,7 +92,7 @@ const EditInstanceDetails: FC<Props> = ({ formik, project }) => {
setSelected={(value) => void formik.setFieldValue("profiles", value)}
readOnly={readOnly}
/>
</div>
</ScrollableForm>
);
};

Expand Down
5 changes: 3 additions & 2 deletions src/pages/instances/forms/InstanceCreateDetailsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { LxdImageType, RemoteImage } from "types/image";
import InstanceLocationSelect from "pages/instances/forms/InstanceLocationSelect";
import UseCustomIsoBtn from "pages/images/actions/UseCustomIsoBtn";
import AutoExpandingTextArea from "components/AutoExpandingTextArea";
import ScrollableForm from "components/ScrollableForm";

export interface InstanceDetailsFormValues {
name?: string;
Expand Down Expand Up @@ -74,7 +75,7 @@ const InstanceCreateDetailsForm: FC<Props> = ({
}

return (
<div className="details">
<ScrollableForm>
<Row>
<Col size={12}>
<Input
Expand Down Expand Up @@ -162,7 +163,7 @@ const InstanceCreateDetailsForm: FC<Props> = ({
: ""
}
/>
</div>
</ScrollableForm>
);
};

Expand Down
4 changes: 2 additions & 2 deletions src/pages/networks/forms/NetworkFormBridge.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React, { FC } from "react";
import { Input, Select } from "@canonical/react-components";
import { FormikProps } from "formik/dist/types";
import ConfigurationTable from "components/ConfigurationTable";
import { getConfigurationRow } from "components/ConfigurationRow";
import { NetworkFormValues } from "pages/networks/forms/NetworkForm";
import ScrollableConfigurationTable from "components/forms/ScrollableConfigurationTable";

interface Props {
formik: FormikProps<NetworkFormValues>;
}

const NetworkFormBridge: FC<Props> = ({ formik }) => {
return (
<ConfigurationTable
<ScrollableConfigurationTable
rows={[
getConfigurationRow({
formik,
Expand Down
4 changes: 2 additions & 2 deletions src/pages/networks/forms/NetworkFormDns.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React, { FC } from "react";
import { Input, Select, Textarea } from "@canonical/react-components";
import { FormikProps } from "formik/dist/types";
import ConfigurationTable from "components/ConfigurationTable";
import { getConfigurationRow } from "components/ConfigurationRow";
import { NetworkFormValues } from "pages/networks/forms/NetworkForm";
import ScrollableConfigurationTable from "components/forms/ScrollableConfigurationTable";

interface Props {
formik: FormikProps<NetworkFormValues>;
}

const NetworkFormDns: FC<Props> = ({ formik }) => {
return (
<ConfigurationTable
<ScrollableConfigurationTable
rows={[
getConfigurationRow({
formik,
Expand Down
4 changes: 2 additions & 2 deletions src/pages/networks/forms/NetworkFormIpv4.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { FC } from "react";
import { Input, Select, Textarea } from "@canonical/react-components";
import { FormikProps } from "formik/dist/types";
import ConfigurationTable from "components/ConfigurationTable";
import { getConfigurationRow } from "components/ConfigurationRow";
import { NetworkFormValues } from "pages/networks/forms/NetworkForm";
import { optionTrueFalse } from "util/instanceOptions";
import ScrollableConfigurationTable from "components/forms/ScrollableConfigurationTable";

interface Props {
formik: FormikProps<NetworkFormValues>;
Expand All @@ -14,7 +14,7 @@ const NetworkFormIpv4: FC<Props> = ({ formik }) => {
const hasDhcp = formik.values.ipv4_dhcp !== "false";

return (
<ConfigurationTable
<ScrollableConfigurationTable
rows={[
getConfigurationRow({
formik,
Expand Down
4 changes: 2 additions & 2 deletions src/pages/networks/forms/NetworkFormIpv6.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { FC } from "react";
import { Input, Select, Textarea } from "@canonical/react-components";
import { FormikProps } from "formik/dist/types";
import ConfigurationTable from "components/ConfigurationTable";
import { getConfigurationRow } from "components/ConfigurationRow";
import { NetworkFormValues } from "pages/networks/forms/NetworkForm";
import { optionTrueFalse } from "util/instanceOptions";
import ScrollableConfigurationTable from "components/forms/ScrollableConfigurationTable";

interface Props {
formik: FormikProps<NetworkFormValues>;
Expand All @@ -14,7 +14,7 @@ const NetworkFormIpv6: FC<Props> = ({ formik }) => {
const hasDhcp = formik.values.ipv6_dhcp !== "false";

return (
<ConfigurationTable
<ScrollableConfigurationTable
rows={[
getConfigurationRow({
formik,
Expand Down
5 changes: 3 additions & 2 deletions src/pages/networks/forms/NetworkFormMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { NetworkFormValues } from "pages/networks/forms/NetworkForm";
import NetworkTypeSelector from "pages/networks/forms/NetworkTypeSelector";
import { optionTrueFalse } from "util/instanceOptions";
import AutoExpandingTextArea from "components/AutoExpandingTextArea";
import ScrollableForm from "components/ScrollableForm";

interface Props {
formik: FormikProps<NetworkFormValues>;
Expand All @@ -29,7 +30,7 @@ const NetworkFormMain: FC<Props> = ({ formik, project }) => {
};

return (
<>
<ScrollableForm>
<Row>
<Col size={12}>
<NetworkTypeSelector formik={formik} />
Expand Down Expand Up @@ -145,7 +146,7 @@ const NetworkFormMain: FC<Props> = ({ formik, project }) => {
: []),
]}
/>
</>
</ScrollableForm>
);
};

Expand Down
Loading

0 comments on commit 7e16b02

Please sign in to comment.