From 373a34c3d1974a60f234ea7305fd6cb58be62392 Mon Sep 17 00:00:00 2001 From: Germain Date: Tue, 15 Aug 2023 18:43:39 +0100 Subject: [PATCH] Create flexbox layout helper (#11411) --- res/css/_components.pcss | 2 + res/css/components/views/utils/_Box.pcss | 21 ++++++ res/css/components/views/utils/_Flex.pcss | 23 ++++++ res/css/views/rooms/_RoomHeader.pcss | 3 - src/components/utils/Box.tsx | 91 +++++++++++++++++++++++ src/components/utils/Flex.tsx | 86 +++++++++++++++++++++ src/components/views/rooms/RoomHeader.tsx | 13 +++- 7 files changed, 232 insertions(+), 7 deletions(-) create mode 100644 res/css/components/views/utils/_Box.pcss create mode 100644 res/css/components/views/utils/_Flex.pcss create mode 100644 src/components/utils/Box.tsx create mode 100644 src/components/utils/Flex.tsx diff --git a/res/css/_components.pcss b/res/css/_components.pcss index 7b3e833014..413a18a15e 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -52,6 +52,8 @@ @import "./components/views/settings/shared/_SettingsSubsectionHeading.pcss"; @import "./components/views/spaces/_QuickThemeSwitcher.pcss"; @import "./components/views/typography/_Caption.pcss"; +@import "./components/views/utils/_Box.pcss"; +@import "./components/views/utils/_Flex.pcss"; @import "./compound/_Icon.pcss"; @import "./compound/_SuccessDialog.pcss"; @import "./structures/_AutoHideScrollbar.pcss"; diff --git a/res/css/components/views/utils/_Box.pcss b/res/css/components/views/utils/_Box.pcss new file mode 100644 index 0000000000..97de604e62 --- /dev/null +++ b/res/css/components/views/utils/_Box.pcss @@ -0,0 +1,21 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_Box { + flex: var(--mx-box-flex, initial); + flex-shrink: var(--mx-box-shrink, initial); + flex-grow: var(--mx-box-grow, initial); +} diff --git a/res/css/components/views/utils/_Flex.pcss b/res/css/components/views/utils/_Flex.pcss new file mode 100644 index 0000000000..6094476d38 --- /dev/null +++ b/res/css/components/views/utils/_Flex.pcss @@ -0,0 +1,23 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_Flex { + display: var(--mx-flex-display, initial); + flex-direction: var(--mx-flex-direction, initial); + align-items: var(--mx-flex-align, initial); + justify-content: var(--mx-flex-justify, initial); + gap: var(--mx-flex-gap, initial); +} diff --git a/res/css/views/rooms/_RoomHeader.pcss b/res/css/views/rooms/_RoomHeader.pcss index 3937e10584..3a0b73b666 100644 --- a/res/css/views/rooms/_RoomHeader.pcss +++ b/res/css/views/rooms/_RoomHeader.pcss @@ -15,10 +15,7 @@ limitations under the License. */ .mx_RoomHeader { - display: flex; - align-items: center; height: 64px; - gap: var(--cpd-space-3x); padding: 0 var(--cpd-space-3x); border-bottom: 1px solid $separator; background-color: $background; diff --git a/src/components/utils/Box.tsx b/src/components/utils/Box.tsx new file mode 100644 index 0000000000..9e91858b50 --- /dev/null +++ b/src/components/utils/Box.tsx @@ -0,0 +1,91 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import classNames from "classnames"; +import React, { useEffect, useRef } from "react"; + +type FlexProps = { + /** + * The type of the HTML element + * @default div + */ + as?: string; + /** + * The CSS class name. + */ + className?: string; + /** + * the on click event callback + */ + onClick?: (e: React.MouseEvent) => void; + /** + * The flex space to use + * @default null + */ + flex?: string | null; + /** + * The flex shrink factor + * @default null + */ + shrink?: string | null; + /** + * The flex grow factor + * @default null + */ + grow?: string | null; +}; + +/** + * Set or remove a CSS property + * @param ref the reference + * @param name the CSS property name + * @param value the CSS property value + */ +function addOrRemoveProperty( + ref: React.MutableRefObject, + name: string, + value?: string | null, +): void { + const style = ref.current!.style; + if (value) { + style.setProperty(name, value); + } else { + style.removeProperty(name); + } +} + +/** + * A flex child helper + */ +export function Box({ + as = "div", + flex = null, + shrink = null, + grow = null, + className, + children, + ...props +}: React.PropsWithChildren): JSX.Element { + const ref = useRef(); + + useEffect(() => { + addOrRemoveProperty(ref, `--mx-box-flex`, flex); + addOrRemoveProperty(ref, `--mx-box-shrink`, shrink); + addOrRemoveProperty(ref, `--mx-box-grow`, grow); + }, [flex, grow, shrink]); + + return React.createElement(as, { ...props, className: classNames("mx_Box", className), ref }, children); +} diff --git a/src/components/utils/Flex.tsx b/src/components/utils/Flex.tsx new file mode 100644 index 0000000000..20802f4f02 --- /dev/null +++ b/src/components/utils/Flex.tsx @@ -0,0 +1,86 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import classNames from "classnames"; +import React, { useEffect, useRef } from "react"; + +type FlexProps = { + /** + * The type of the HTML element + * @default div + */ + as?: string; + /** + * The CSS class name. + */ + className?: string; + /** + * The type of flex container + * @default flex + */ + display?: "flex" | "inline-flex"; + /** + * The flow direction of the flex children + * @default row + */ + direction?: "row" | "column" | "row-reverse" | "column-reverse"; + /** + * The alingment of the flex children + * @default start + */ + align?: "start" | "center" | "end" | "baseline" | "stretch"; + /** + * The justification of the flex children + * @default start + */ + justify?: "start" | "center" | "end" | "between"; + /** + * The spacing between the flex children, expressed with the CSS unit + * @default 0 + */ + gap?: string; + /** + * the on click event callback + */ + onClick?: (e: React.MouseEvent) => void; +}; + +/** + * A flexbox container helper + */ +export function Flex({ + as = "div", + display = "flex", + direction = "row", + align = "start", + justify = "start", + gap = "0", + className, + children, + ...props +}: React.PropsWithChildren): JSX.Element { + const ref = useRef(); + + useEffect(() => { + ref.current!.style.setProperty(`--mx-flex-display`, display); + ref.current!.style.setProperty(`--mx-flex-direction`, direction); + ref.current!.style.setProperty(`--mx-flex-align`, align); + ref.current!.style.setProperty(`--mx-flex-justify`, justify); + ref.current!.style.setProperty(`--mx-flex-gap`, gap); + }, [align, direction, display, gap, justify]); + + return React.createElement(as, { ...props, className: classNames("mx_Flex", className), ref }, children); +} diff --git a/src/components/views/rooms/RoomHeader.tsx b/src/components/views/rooms/RoomHeader.tsx index 3c78525995..d6bf5cbf95 100644 --- a/src/components/views/rooms/RoomHeader.tsx +++ b/src/components/views/rooms/RoomHeader.tsx @@ -23,13 +23,18 @@ import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar"; import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases"; import RightPanelStore from "../../../stores/right-panel/RightPanelStore"; import { useTopic } from "../../../hooks/room/useTopic"; +import { Flex } from "../../utils/Flex"; +import { Box } from "../../utils/Box"; export default function RoomHeader({ room }: { room: Room }): JSX.Element { const roomName = useRoomName(room); const roomTopic = useTopic(room); return ( -
{ const rightPanel = RightPanelStore.instance; @@ -39,7 +44,7 @@ export default function RoomHeader({ room }: { room: Room }): JSX.Element { }} > -
+ )} -
-
+ + ); }