Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Datc 19 add flexbox abstraction #45

Merged
merged 17 commits into from
Nov 24, 2024
11 changes: 10 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,20 @@
"workbench.editor.customLabels.enabled": true,
"workbench.editor.customLabels.patterns": {
"**/config/ci/**/caller.mk": "${dirname}.caller",
"**/config/ci/**/runner.ts": "${dirname}.runner"
"**/config/ci/**/runner.ts": "${dirname}.runner",
// react-func pattern
"**/src/**/func.tsx": "${dirname(-3)}.${dirname}.func",
"**/src/**/styles.ts": "${dirname}.styles",
"**/src/**/stories.{ts,tsx}": "${dirname}.stories",
"**/src/**/index.ts": "${dirname}.module",
"**/src/**/constants.ts": "${dirname}.constants",
"**/src/**/tests.tsx": "${dirname}.tests",
"**/src/**/types.ts": "${dirname}.types"
},

"material-icon-theme.files.associations": {
"stories.ts": "Storybook",
"stories.tsx": "Storybook",
"tests.tsx": "Test-jsx",
"styles.ts": "Css",
"func.tsx": "SVGr",
Expand Down
7 changes: 1 addition & 6 deletions apps/frontend/admin/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { ThemeProvider } from "@lib-theme";
import { Button } from "@lib-components";

export default function App() {
return (
<ThemeProvider>
<Button appearance="primary"> Hello ADMIN </Button>
</ThemeProvider>
);
return <ThemeProvider>Hello ADMIN</ThemeProvider>;
}
7 changes: 1 addition & 6 deletions apps/frontend/ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { ThemeProvider } from "@lib-theme";
import { Button } from "@lib-components";

export default function App() {
return (
<ThemeProvider>
<Button appearance="primary"> Hello My UI APP </Button>
</ThemeProvider>
);
return <ThemeProvider>Hello My UI APP</ThemeProvider>;
}
1 change: 0 additions & 1 deletion libs/components/src/atoms/index.ts

This file was deleted.

6 changes: 6 additions & 0 deletions libs/components/src/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
propsValues: {
undefinedClassString: "$$!!NoClassString!!$$",
undefinedDataTestId: "$$!!NoDataTestId!!$$",
},
};
2 changes: 1 addition & 1 deletion libs/components/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { Button } from "@lib-components/atoms";
export { Flex } from "@lib-components/layout";
111 changes: 111 additions & 0 deletions libs/components/src/layout/Flex/func.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import type { ReactNode } from "react";

import constants from "@lib-components/defaults";

import { mergeClasses } from "@lib-theme";
import type { TThemeSpacing, TThemeShorthandSpacing } from "@lib-theme";

import {
useGap,
useFlexBox,
useMargin,
usePadding,
useShorthandDimension,
} from "@lib-components/layout/Flex/hooks";

import type {
TFlexDirection,
TFlexOption,
TFlexWrap,
TFlexShorthandDimensions,
} from "@lib-components/layout/Flex/types";

type TProps = {
children: ReactNode;
direction?: TFlexDirection;
justifyContent?: TFlexOption;
alignItems?: TFlexOption;
wrap?: TFlexWrap;
className?: string;
gap?: TThemeSpacing;
margin?: TThemeShorthandSpacing;
padding?: TThemeShorthandSpacing;
testId?: string;
shWidth?: TFlexShorthandDimensions;
shHeight?: TFlexShorthandDimensions;
};

/**
* @description
* - fluent does not provide a `Flex` component for consistent layout (it was removed in the latest version)
* - to have a straight forward implementation of `Flex` component, we have created this layout component
* - having this allows to use fewer makeStyles call and repeting flex configurations in the code
* - its especially usefull when certain layout styles have to be applied conditionally
* - for this the entire conditional logic is abstracted inside this component, providing very much styled-component like ergonomics
*
*
* @props
* - `direction`: flex-direction property
* - `justifyContent`: justify-content property
* - `alignItems`: align-items property
* - `wrap`: flex-wrap property
* - `className`: to add additional classes to the component, will override all specified styles from props
* - `gap`: gap between children, with fixed predefined values from the design system, not discriminating between horizontal and vertical gap (because there are literally the same values)
* - `margin`: margin property, using the same values like gap, expects the shorthand notation
* - `padding`: same like margin, but for padding
*
* ```
* // the shorthand is not a simple string, but rather defined as an array that can be of size 1 up to 4
* // each element provides additional restraint from the design system tokens
* // following examples will only use padding, but the same applies to margin
* // the * between tokens.spacing and the further specifier can be interpreted as either horizontal or vertical (both the same values, reasoning is explained in gap comment above)
*
* <Flex padding={["S"]} /> // like saying padding: tokens.spacing*S;
* <Flex padding={["S", "M"]} /> // like saying padding: `${tokens.spacing*S} ${tokens.spacing*M}`;
* <Flex padding={["S", "M", "L"]} /> // like saying padding: `${tokens.spacing*S} ${tokens.spacing*M} ${tokens.spacing*L}`;
* <Flex padding={["S", "M", "L", "XL"]} /> // like saying padding: `${tokens.spacing*S} ${tokens.spacing*M} ${tokens.spacing*L} ${tokens.spacing*XL}`;
*
* ```
* - `testId`: passed down the data-testid attribute
* - `shWidth`: shorthand for width property
* - `shHeight`: shorthand for height property
*
* @default
* direction = "row", justifyContent = "start", alignItems = "start", wrap = "nowrap", gap = "None", margin = ["None"], padding = ["None"], className = "NoClass"
*/
export default function Flex({
direction = "row",
justifyContent = "start",
alignItems = "start",
wrap = "nowrap",
gap = "None",
margin = ["None"],
padding = ["None"],
shHeight = "auto",
shWidth = "auto",
className = constants.propsValues.undefinedClassString,
testId = constants.propsValues.undefinedDataTestId,
children,
}: TProps) {
const flexBoxClass = useFlexBox(justifyContent, alignItems, direction, wrap);
const gapClass = useGap(gap);
const marginClass = useMargin(margin);
const paddingClass = usePadding(padding);
const dimensionClass = useShorthandDimension(shWidth, shHeight);

return (
<div
data-testid={testId}
className={mergeClasses(
flexBoxClass,
gapClass,
marginClass,
paddingClass,
dimensionClass,
className,
)}
>
{children}
</div>
);
}
7 changes: 7 additions & 0 deletions libs/components/src/layout/Flex/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import useGap from "@lib-components/layout/Flex/hooks/useGap";
import useMargin from "@lib-components/layout/Flex/hooks/useMargin";
import usePadding from "@lib-components/layout/Flex/hooks/usePadding";
import useFlexBox from "@lib-components/layout/Flex/hooks/useFlexBox";
import useShorthandDimension from "@lib-components/layout/Flex/hooks/useShorthandDimension";

export { useGap, useMargin, usePadding, useFlexBox, useShorthandDimension };
36 changes: 36 additions & 0 deletions libs/components/src/layout/Flex/hooks/useFlexBox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { mergeClasses } from "@lib-theme";

import { useFlexBoxClasses } from "@lib-components/layout/Flex/styles";

import type {
TFlexDirection,
TFlexOption,
TFlexWrap,
} from "@lib-components/layout/Flex/types";

export default function useFlexBox(
justifyContent?: TFlexOption,
alignItems?: TFlexOption,
direction?: TFlexDirection,
wrap?: TFlexWrap,
) {
const classes = useFlexBoxClasses();
const directionClass = direction
? classes[`${direction}Direction`]
: undefined;
const justifyContentClass = justifyContent
? classes[`${justifyContent}Content`]
: undefined;
const alignItemsClass = alignItems
? classes[`${alignItems}Items`]
: undefined;
const wrapClass = wrap ? classes[wrap] : undefined;

return mergeClasses(
classes.base,
directionClass,
justifyContentClass,
alignItemsClass,
wrapClass,
);
}
9 changes: 9 additions & 0 deletions libs/components/src/layout/Flex/hooks/useGap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useGapClasses } from "@lib-components/layout/Flex/styles";
import type { TThemeSpacing } from "@lib-theme";

function useGap(gap?: TThemeSpacing) {
const classes = useGapClasses();
return gap ? classes[`gap${gap}`] : undefined;
}

export default useGap;
39 changes: 39 additions & 0 deletions libs/components/src/layout/Flex/hooks/useMargin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useMarginClasses } from "@lib-components/layout/Flex/styles";
import { mergeClasses } from "@lib-theme";

import type { TThemeShorthandSpacing } from "@lib-theme";

function useMargin(margin?: TThemeShorthandSpacing) {
const classes = useMarginClasses();

if (margin === undefined) {
return "noMarginValue";
}
if (margin.length === 1) {
return classes[`margin${margin[0]}`];
}
if (margin.length === 2) {
return mergeClasses(
classes[`marginTop${margin[0]}`],
classes[`marginRight${margin[1]}`],
classes[`marginBottom${margin[0]}`],
classes[`marginLeft${margin[1]}`],
);
}
if (margin.length === 3) {
return mergeClasses(
classes[`marginTop${margin[0]}`],
classes[`marginRight${margin[1]}`],
classes[`marginBottom${margin[2]}`],
classes[`marginLeft${margin[1]}`],
);
}
return mergeClasses(
classes[`marginTop${margin[0]}`],
classes[`marginRight${margin[1]}`],
classes[`marginBottom${margin[2]}`],
classes[`marginLeft${margin[3]}`],
);
}

export default useMargin;
39 changes: 39 additions & 0 deletions libs/components/src/layout/Flex/hooks/usePadding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { usePaddingClasses } from "@lib-components/layout/Flex/styles";
import { mergeClasses } from "@lib-theme";

import type { TThemeShorthandSpacing } from "@lib-theme";

function usePadding(padding?: TThemeShorthandSpacing) {
const classes = usePaddingClasses();

if (padding === undefined) {
return "noPaddingValue";
}
if (padding.length === 1) {
return classes[`padding${padding[0]}`];
}
if (padding.length === 2) {
return mergeClasses(
classes[`paddingTop${padding[0]}`],
classes[`paddingRight${padding[1]}`],
classes[`paddingBottom${padding[0]}`],
classes[`paddingLeft${padding[1]}`],
);
}
if (padding.length === 3) {
return mergeClasses(
classes[`paddingTop${padding[0]}`],
classes[`paddingRight${padding[1]}`],
classes[`paddingBottom${padding[2]}`],
classes[`paddingLeft${padding[1]}`],
);
}
return mergeClasses(
classes[`paddingTop${padding[0]}`],
classes[`paddingRight${padding[1]}`],
classes[`paddingBottom${padding[2]}`],
classes[`paddingLeft${padding[3]}`],
);
}

export default usePadding;
17 changes: 17 additions & 0 deletions libs/components/src/layout/Flex/hooks/useShorthandDimension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { mergeClasses } from "@lib-theme";

import { useDimensionClasses } from "@lib-components/layout/Flex/styles";

import type { TFlexShorthandDimensions } from "@lib-components/layout/Flex/types";

export default function useShorthandDimension(
shorthandWidth: TFlexShorthandDimensions,
shorthandHeight: TFlexShorthandDimensions,
) {
const classes = useDimensionClasses();

const widthClass = classes[`${shorthandWidth}Width`];
const heightClass = classes[`${shorthandHeight}Height`];

return mergeClasses(widthClass, heightClass);
}
3 changes: 3 additions & 0 deletions libs/components/src/layout/Flex/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Flex from "@lib-components/layout/Flex/func";

export default Flex;
Loading