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

Feature/20/create-layouts #33

Merged
merged 28 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4ce3860
Add basic page layout
AmirAgassi Nov 19, 2024
31c7303
Header layout component
AmirAgassi Nov 19, 2024
a1c1dd2
Grid layout component
AmirAgassi Nov 19, 2024
a2fdd24
Section layout component
AmirAgassi Nov 19, 2024
77f1a49
Footer layout component
AmirAgassi Nov 19, 2024
0d31ae6
Remove stale checklistItems
AmirAgassi Nov 19, 2024
f39e5dd
Update DashboardLayout.tsx
AmirAgassi Nov 19, 2024
98618ab
Create Container.tsx
AmirAgassi Nov 21, 2024
6a6d72b
Create Footer.tsx
AmirAgassi Nov 21, 2024
beedf6b
Create Grid.tsx
AmirAgassi Nov 21, 2024
26011ba
Create FormContainer.tsx
AmirAgassi Nov 21, 2024
332dc1b
Create PageLayout.tsx
AmirAgassi Nov 21, 2024
672963c
Create Header.tsx
AmirAgassi Nov 21, 2024
499f9cf
Create Section.tsx
AmirAgassi Nov 21, 2024
474db98
Create Stack.tsx
AmirAgassi Nov 21, 2024
d724794
Create types.ts
AmirAgassi Nov 21, 2024
8ad6dc2
Create utils.ts
AmirAgassi Nov 21, 2024
9541e27
Remove stale layouts and components
AmirAgassi Nov 21, 2024
964a611
Create AdminDashboard.tsx
AmirAgassi Nov 21, 2024
2720e4f
Create UserDashboard.tsx
AmirAgassi Nov 21, 2024
2559117
Edit in new components into index.ts
AmirAgassi Nov 21, 2024
012a090
Add components to layout index.ts
AmirAgassi Nov 21, 2024
edcc818
Create DashboardTemplate.tsx
AmirAgassi Nov 21, 2024
d6d19a3
Create AdminDashboard.tsx
AmirAgassi Nov 21, 2024
2c07c6a
Use new format
AmirAgassi Nov 21, 2024
68cbf0b
Edit in routes
AmirAgassi Nov 21, 2024
791624f
Move to frontend
AmirAgassi Nov 27, 2024
0a45ac7
Merge branch 'main' into 20-fe-create-layouts
AmirAgassi Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions frontend/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
export { TextInput } from './TextInput';
export { TextArea } from './TextArea';
export { Dropdown } from './Dropdown';
export { FileUpload } from './FileUpload';
export { InfoCard } from './InfoCard';
export { Button } from './Button';
export { FormContainer } from './FormContainer';
export { ScrollLink } from './ScrollLink';
export { AnchorLinks } from './AnchorLinks';
export { TextArea } from './TextArea';
export {
PageLayout,
Section,
Grid,
Footer,
Header,
FormContainer,
DashboardTemplate,
AdminDashboard,
UserDashboard
} from './layout';
27 changes: 27 additions & 0 deletions frontend/src/components/layout/components/Container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { LayoutContainerProps } from './types';
import { widths } from './utils';

export const Container: React.FC<LayoutContainerProps> = ({
children,
width = 'normal',
background = 'bg-white',
fullHeight = false,
className = '',
id,
testId,
}) => {
return (
<div
id={id}
data-testid={testId}
className={`
${widths[width]}
${background}
${fullHeight ? 'min-h-screen' : ''}
${className}
`.trim()}
>
{children}
</div>
);
};
22 changes: 22 additions & 0 deletions frontend/src/components/layout/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ReactNode } from 'react';
import { Container } from './Container';
import { Stack } from './Stack';

interface FooterProps {
children: ReactNode;
className?: string;
}

const Footer: React.FC<FooterProps> = ({ children, className = '' }) => {
return (
<Container width="full" className={`flex-shrink-0 border-t border-gray-200 ${className}`}>
<div className="max-w-[1440px] mx-auto px-6 py-4">
<Stack gap="md">
{children}
</Stack>
</div>
</Container>
);
};

export { Footer };
31 changes: 31 additions & 0 deletions frontend/src/components/layout/components/FormContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ReactNode } from 'react';

interface FormContainerProps {
children: ReactNode;
title?: string;
description?: string;
className?: string;
}

const FormContainer: React.FC<FormContainerProps> = ({
children,
title,
description,
className = ''
}) => {
return (
<div className={`bg-white rounded-lg border border-gray-200 ${className}`}>
{(title || description) && (
<div className="border-b border-gray-200 p-6">
{title && <h3 className="text-lg font-semibold">{title}</h3>}
{description && <p className="mt-1 text-sm text-gray-600">{description}</p>}
</div>
)}
<div className="p-6">
{children}
</div>
</div>
);
};

export { FormContainer };
40 changes: 40 additions & 0 deletions frontend/src/components/layout/components/Grid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ReactNode } from 'react';

type GridColumns = 1 | 2 | 3 | 4 | 6 | 12;
type GridGap = 'none' | 'small' | 'normal' | 'large';

interface GridProps {
children: ReactNode;
columns?: GridColumns;
gap?: GridGap;
className?: string;
}

const gapClasses: Record<GridGap, string> = {
none: 'gap-0',
small: 'gap-2',
normal: 'gap-4',
large: 'gap-8'
};

const Grid: React.FC<GridProps> = ({
children,
columns = 3,
gap = 'normal',
className = ''
}) => {
return (
<div className={`
grid
grid-cols-1
sm:grid-cols-2
${columns > 2 ? `lg:grid-cols-${columns}` : ''}
${gapClasses[gap]}
${className}
`}>
{children}
</div>
);
};

export { Grid };
16 changes: 16 additions & 0 deletions frontend/src/components/layout/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ReactNode } from 'react';

interface HeaderProps {
children: ReactNode;
className?: string;
}

const Header: React.FC<HeaderProps> = ({ children, className = '' }) => {
return (
<header className={`flex-shrink-0 w-full border-b border-gray-200 bg-white ${className}`}>
{children}
</header>
);
};

export { Header };
18 changes: 18 additions & 0 deletions frontend/src/components/layout/components/PageLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ReactNode } from 'react';
import { Container } from './Container';
import { Stack } from './Stack';

interface PageLayoutProps {
children: ReactNode;
className?: string;
}

export const PageLayout: React.FC<PageLayoutProps> = ({ children, className = '' }) => {
return (
<Container width="screen" fullHeight className={className}>
<Stack gap="none">
{children}
</Stack>
</Container>
);
};
63 changes: 63 additions & 0 deletions frontend/src/components/layout/components/Section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { ReactNode } from 'react';

type SectionWidth = 'full' | 'wide' | 'normal' | 'narrow';
type SectionPadding = 'none' | 'small' | 'normal' | 'large';
type SectionAlignment = 'left' | 'center' | 'right';

interface SectionProps {
children: ReactNode;
width?: SectionWidth;
padding?: SectionPadding;
align?: SectionAlignment;
className?: string;
background?: string;
container?: boolean; // new prop to control container width
}

// predefined width classes
const widthClasses: Record<SectionWidth, string> = {
full: 'w-full',
wide: 'max-w-7xl',
normal: 'max-w-5xl',
narrow: 'max-w-3xl'
};

// predefined padding classes
const paddingClasses: Record<SectionPadding, string> = {
none: 'p-0',
small: 'py-4 px-4',
normal: 'py-8 px-6',
large: 'py-16 px-8'
};

const Section: React.FC<SectionProps> = ({
children,
width = 'normal',
padding = 'normal',
align = 'center',
className = '',
background = 'bg-white',
container = true // default to using container
}) => {
const content = container ? (
<div className={`
${widthClasses[width]}
${align === 'center' ? 'mx-auto' : ''}
${className}
`}>
{children}
</div>
) : children;

return (
<section className={`
${background}
${paddingClasses[padding]}
w-full
`}>
{content}
</section>
);
};

export { Section };
42 changes: 42 additions & 0 deletions frontend/src/components/layout/components/Stack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { BaseLayoutProps, LayoutSpacingProps, LayoutAlignmentProps } from './types';
import { getSpacingClasses, gaps } from './utils';

interface StackProps extends BaseLayoutProps, LayoutSpacingProps, LayoutAlignmentProps {}

export const Stack: React.FC<StackProps> = ({
children,
direction = 'column',
align = 'left',
justify = 'start',
gap = 'md',
padding,
margin,
className = '',
id,
testId,
}) => {
const paddingClasses = getSpacingClasses('p', padding);
const marginClasses = getSpacingClasses('m', margin);

return (
<div
id={id}
data-testid={testId}
className={`
flex
${direction === 'column' ? 'flex-col' : 'flex-row'}
${gaps[gap]}
${align === 'center' ? 'items-center' : align === 'right' ? 'items-end' : 'items-start'}
${justify === 'center' ? 'justify-center' :
justify === 'end' ? 'justify-end' :
justify === 'between' ? 'justify-between' :
justify === 'around' ? 'justify-around' : 'justify-start'}
${paddingClasses}
${marginClasses}
${className}
`.trim()}
>
{children}
</div>
);
};
39 changes: 39 additions & 0 deletions frontend/src/components/layout/components/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ReactNode } from 'react';

export type Spacing = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
export type Width = 'full' | 'screen' | 'wide' | 'normal' | 'narrow' | 'content';
export type Alignment = 'left' | 'center' | 'right';
export type Direction = 'row' | 'column';
export type Gap = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';

export interface BaseLayoutProps {
children: ReactNode;
className?: string;
id?: string;
testId?: string;
}

export interface LayoutSpacingProps {
padding?: Spacing | { x?: Spacing; y?: Spacing };
margin?: Spacing | { x?: Spacing; y?: Spacing };
gap?: Gap;
}

export interface LayoutAlignmentProps {
align?: Alignment;
justify?: 'start' | 'center' | 'end' | 'between' | 'around';
direction?: Direction;
}

export interface LayoutContainerProps extends BaseLayoutProps {
width?: Width;
background?: string;
fullHeight?: boolean;
}

export interface LayoutGridProps extends BaseLayoutProps, LayoutSpacingProps {
columns?: number | { sm?: number; md?: number; lg?: number };
rows?: number;
autoFit?: boolean;
minChildWidth?: string;
}
58 changes: 58 additions & 0 deletions frontend/src/components/layout/components/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Spacing, Width, Gap } from './types';

export const spacing: Record<Spacing, string> = {
none: '0',
xs: '0.25rem',
sm: '0.5rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem',
'2xl': '3rem',
};

export const widths: Record<Width, string> = {
full: 'w-full',
screen: 'w-screen max-w-[100vw]',
wide: 'max-w-7xl',
normal: 'max-w-5xl',
narrow: 'max-w-3xl',
content: 'max-w-prose',
};

export const gaps: Record<Gap, string> = {
none: 'gap-0',
xs: 'gap-1',
sm: 'gap-2',
md: 'gap-4',
lg: 'gap-6',
xl: 'gap-8',
};

export const getSpacingClasses = (
type: 'p' | 'm',
spacing?: Spacing | { x?: Spacing; y?: Spacing }
): string => {
if (!spacing) return '';

if (typeof spacing === 'string') {
return `${type}-${spacing}`;
}

const { x, y } = spacing;
return `${y ? `${type}y-${y}` : ''} ${x ? `${type}x-${x}` : ''}`.trim();
};

export const getResponsiveGridColumns = (
columns?: number | { sm?: number; md?: number; lg?: number }
): string => {
if (!columns) return '';

if (typeof columns === 'number') {
return `grid-cols-${columns}`;
}

const { sm, md, lg } = columns;
return `${sm ? `sm:grid-cols-${sm}` : ''} ${md ? `md:grid-cols-${md}` : ''} ${
lg ? `lg:grid-cols-${lg}` : ''
}`.trim();
};
Loading