refactor progress
This commit is contained in:
87
packages/admin/src/components/ui/Layout/Box.tsx
Normal file
87
packages/admin/src/components/ui/Layout/Box.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @jsxRuntime automatic */
|
||||
/** @jsxImportSource hono/jsx */
|
||||
|
||||
import {cn} from '@fluxer/admin/src/utils/ClassNames';
|
||||
import type {PropsWithChildren} from 'hono/jsx';
|
||||
|
||||
export type BoxSpacing = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||
export type BoxBackground = 'white' | 'gray-50' | 'gray-100' | 'transparent';
|
||||
export type BoxBorder = 'none' | 'gray-200' | 'gray-300';
|
||||
export type BoxRounded = 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full';
|
||||
|
||||
export interface BoxProps {
|
||||
p?: BoxSpacing;
|
||||
m?: BoxSpacing;
|
||||
bg?: BoxBackground;
|
||||
border?: BoxBorder;
|
||||
rounded?: BoxRounded;
|
||||
}
|
||||
|
||||
const backgroundClasses: Record<BoxBackground, string> = {
|
||||
white: 'bg-white',
|
||||
'gray-50': 'bg-gray-50',
|
||||
'gray-100': 'bg-gray-100',
|
||||
transparent: 'bg-transparent',
|
||||
};
|
||||
|
||||
const borderClasses: Record<BoxBorder, string> = {
|
||||
none: '',
|
||||
'gray-200': 'border border-gray-200',
|
||||
'gray-300': 'border border-gray-300',
|
||||
};
|
||||
|
||||
const roundedClasses: Record<BoxRounded, string> = {
|
||||
none: 'rounded-none',
|
||||
sm: 'rounded-sm',
|
||||
md: 'rounded-md',
|
||||
lg: 'rounded-lg',
|
||||
xl: 'rounded-xl',
|
||||
'2xl': 'rounded-2xl',
|
||||
full: 'rounded-full',
|
||||
};
|
||||
|
||||
function getPaddingClass(p: BoxSpacing): string {
|
||||
return `p-${p}`;
|
||||
}
|
||||
|
||||
function getMarginClass(m: BoxSpacing): string {
|
||||
return `m-${m}`;
|
||||
}
|
||||
|
||||
export function Box({
|
||||
p,
|
||||
m,
|
||||
bg = 'transparent',
|
||||
border = 'none',
|
||||
rounded = 'none',
|
||||
children,
|
||||
}: PropsWithChildren<BoxProps>) {
|
||||
const classes = cn(
|
||||
p !== undefined && getPaddingClass(p),
|
||||
m !== undefined && getMarginClass(m),
|
||||
backgroundClasses[bg],
|
||||
borderClasses[border],
|
||||
roundedClasses[rounded],
|
||||
);
|
||||
|
||||
return <div class={classes}>{children}</div>;
|
||||
}
|
||||
39
packages/admin/src/components/ui/Layout/DetailPageLayout.tsx
Normal file
39
packages/admin/src/components/ui/Layout/DetailPageLayout.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @jsxRuntime automatic */
|
||||
/** @jsxImportSource hono/jsx */
|
||||
|
||||
import {PageContainer} from '@fluxer/admin/src/components/ui/Layout/PageContainer';
|
||||
import type {Child, PropsWithChildren} from 'hono/jsx';
|
||||
|
||||
export interface DetailPageLayoutProps {
|
||||
header: Child;
|
||||
tabs?: Child;
|
||||
}
|
||||
|
||||
export function DetailPageLayout({header, tabs, children}: PropsWithChildren<DetailPageLayoutProps>) {
|
||||
return (
|
||||
<PageContainer>
|
||||
{header}
|
||||
{tabs}
|
||||
{children}
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
94
packages/admin/src/components/ui/Layout/Flex.tsx
Normal file
94
packages/admin/src/components/ui/Layout/Flex.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @jsxRuntime automatic */
|
||||
/** @jsxImportSource hono/jsx */
|
||||
|
||||
import {cn} from '@fluxer/admin/src/utils/ClassNames';
|
||||
import type {PropsWithChildren} from 'hono/jsx';
|
||||
|
||||
export type FlexDirection = 'row' | 'col' | 'row-reverse' | 'col-reverse';
|
||||
export type FlexAlign = 'start' | 'center' | 'end' | 'baseline' | 'stretch';
|
||||
export type FlexJustify = 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly';
|
||||
export type FlexWrap = 'wrap' | 'nowrap' | 'wrap-reverse';
|
||||
|
||||
export interface FlexProps {
|
||||
direction?: FlexDirection;
|
||||
align?: FlexAlign;
|
||||
justify?: FlexJustify;
|
||||
gap?: number | string;
|
||||
wrap?: FlexWrap;
|
||||
}
|
||||
|
||||
const directionClasses: Record<FlexDirection, string> = {
|
||||
row: 'flex-row',
|
||||
col: 'flex-col',
|
||||
'row-reverse': 'flex-row-reverse',
|
||||
'col-reverse': 'flex-col-reverse',
|
||||
};
|
||||
|
||||
const alignClasses: Record<FlexAlign, string> = {
|
||||
start: 'items-start',
|
||||
center: 'items-center',
|
||||
end: 'items-end',
|
||||
baseline: 'items-baseline',
|
||||
stretch: 'items-stretch',
|
||||
};
|
||||
|
||||
const justifyClasses: Record<FlexJustify, string> = {
|
||||
start: 'justify-start',
|
||||
center: 'justify-center',
|
||||
end: 'justify-end',
|
||||
between: 'justify-between',
|
||||
around: 'justify-around',
|
||||
evenly: 'justify-evenly',
|
||||
};
|
||||
|
||||
const wrapClasses: Record<FlexWrap, string> = {
|
||||
wrap: 'flex-wrap',
|
||||
nowrap: 'flex-nowrap',
|
||||
'wrap-reverse': 'flex-wrap-reverse',
|
||||
};
|
||||
|
||||
function getGapClass(gap: number | string): string {
|
||||
if (typeof gap === 'number') {
|
||||
return `gap-${gap}`;
|
||||
}
|
||||
return gap;
|
||||
}
|
||||
|
||||
export function Flex({
|
||||
direction = 'row',
|
||||
align = 'stretch',
|
||||
justify = 'start',
|
||||
gap,
|
||||
wrap = 'nowrap',
|
||||
children,
|
||||
}: PropsWithChildren<FlexProps>) {
|
||||
const classes = cn(
|
||||
'flex',
|
||||
directionClasses[direction],
|
||||
alignClasses[align],
|
||||
justifyClasses[justify],
|
||||
gap !== undefined && getGapClass(gap),
|
||||
wrapClasses[wrap],
|
||||
);
|
||||
|
||||
return <div class={classes}>{children}</div>;
|
||||
}
|
||||
44
packages/admin/src/components/ui/Layout/FormGrid.tsx
Normal file
44
packages/admin/src/components/ui/Layout/FormGrid.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @jsxRuntime automatic */
|
||||
/** @jsxImportSource hono/jsx */
|
||||
|
||||
import type {PropsWithChildren} from 'hono/jsx';
|
||||
|
||||
export interface FormGridProps {
|
||||
cols?: 2 | 3 | 4;
|
||||
gap?: 'sm' | 'md' | 'lg';
|
||||
}
|
||||
|
||||
const colsClasses = {
|
||||
2: 'md:grid-cols-2',
|
||||
3: 'md:grid-cols-3',
|
||||
4: 'md:grid-cols-4',
|
||||
};
|
||||
|
||||
const gapClasses = {
|
||||
sm: 'gap-2',
|
||||
md: 'gap-4',
|
||||
lg: 'gap-6',
|
||||
};
|
||||
|
||||
export function FormGrid({cols = 2, gap = 'md', children}: PropsWithChildren<FormGridProps>) {
|
||||
return <div class={`grid grid-cols-1 ${colsClasses[cols]} ${gapClasses[gap]}`}>{children}</div>;
|
||||
}
|
||||
70
packages/admin/src/components/ui/Layout/HStack.tsx
Normal file
70
packages/admin/src/components/ui/Layout/HStack.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @jsxRuntime automatic */
|
||||
/** @jsxImportSource hono/jsx */
|
||||
|
||||
import {cn} from '@fluxer/admin/src/utils/ClassNames';
|
||||
import type {PropsWithChildren} from 'hono/jsx';
|
||||
|
||||
export type HStackAlign = 'start' | 'center' | 'end' | 'baseline' | 'stretch';
|
||||
export type HStackJustify = 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly';
|
||||
|
||||
export interface HStackProps {
|
||||
gap?: number | string;
|
||||
align?: HStackAlign;
|
||||
justify?: HStackJustify;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const alignClasses: Record<HStackAlign, string> = {
|
||||
start: 'items-start',
|
||||
center: 'items-center',
|
||||
end: 'items-end',
|
||||
baseline: 'items-baseline',
|
||||
stretch: 'items-stretch',
|
||||
};
|
||||
|
||||
const justifyClasses: Record<HStackJustify, string> = {
|
||||
start: 'justify-start',
|
||||
center: 'justify-center',
|
||||
end: 'justify-end',
|
||||
between: 'justify-between',
|
||||
around: 'justify-around',
|
||||
evenly: 'justify-evenly',
|
||||
};
|
||||
|
||||
function getGapClass(gap: number | string): string {
|
||||
if (typeof gap === 'number') {
|
||||
return `gap-${gap}`;
|
||||
}
|
||||
return gap;
|
||||
}
|
||||
|
||||
export function HStack({
|
||||
gap = 4,
|
||||
align = 'center',
|
||||
justify = 'start',
|
||||
class: className,
|
||||
children,
|
||||
}: PropsWithChildren<HStackProps>) {
|
||||
const classes = cn('flex flex-row', getGapClass(gap), alignClasses[align], justifyClasses[justify], className);
|
||||
|
||||
return <div class={classes}>{children}</div>;
|
||||
}
|
||||
33
packages/admin/src/components/ui/Layout/PageContainer.tsx
Normal file
33
packages/admin/src/components/ui/Layout/PageContainer.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @jsxRuntime automatic */
|
||||
/** @jsxImportSource hono/jsx */
|
||||
|
||||
import type {PropsWithChildren} from 'hono/jsx';
|
||||
|
||||
export interface PageContainerProps {
|
||||
maxWidth?: 'full' | '7xl';
|
||||
}
|
||||
|
||||
export function PageContainer({maxWidth = '7xl', children}: PropsWithChildren<PageContainerProps>) {
|
||||
const widthClass = maxWidth === 'full' ? 'w-full' : 'max-w-7xl';
|
||||
|
||||
return <div class={`mx-auto ${widthClass} space-y-6`}>{children}</div>;
|
||||
}
|
||||
45
packages/admin/src/components/ui/Layout/PageHeader.tsx
Normal file
45
packages/admin/src/components/ui/Layout/PageHeader.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @jsxRuntime automatic */
|
||||
/** @jsxImportSource hono/jsx */
|
||||
|
||||
import {cn} from '@fluxer/admin/src/utils/ClassNames';
|
||||
import type {Child, PropsWithChildren} from 'hono/jsx';
|
||||
|
||||
export interface PageHeaderProps {
|
||||
title: string;
|
||||
description?: string;
|
||||
actions?: Child;
|
||||
}
|
||||
|
||||
export function PageHeader({title, description, actions, children}: PropsWithChildren<PageHeaderProps>) {
|
||||
return (
|
||||
<div>
|
||||
<div class={cn('flex items-start justify-between', description ? 'mb-2' : 'mb-0')}>
|
||||
<div class="flex min-w-0 flex-1 flex-col gap-2">
|
||||
<h1 class="font-bold text-3xl text-gray-900">{title}</h1>
|
||||
{description && <p class="text-base text-gray-600">{description}</p>}
|
||||
</div>
|
||||
{actions && <div class="ml-4 flex-shrink-0">{actions}</div>}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
50
packages/admin/src/components/ui/Layout/PageLayout.tsx
Normal file
50
packages/admin/src/components/ui/Layout/PageLayout.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @jsxRuntime automatic */
|
||||
/** @jsxImportSource hono/jsx */
|
||||
|
||||
import {cn} from '@fluxer/admin/src/utils/ClassNames';
|
||||
import type {PropsWithChildren} from 'hono/jsx';
|
||||
|
||||
export type PageLayoutMaxWidth = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | '6xl' | '7xl';
|
||||
|
||||
export interface PageLayoutProps {
|
||||
maxWidth?: PageLayoutMaxWidth;
|
||||
padding?: boolean;
|
||||
}
|
||||
|
||||
const maxWidthClasses: Record<PageLayoutMaxWidth, string> = {
|
||||
sm: 'max-w-sm',
|
||||
md: 'max-w-md',
|
||||
lg: 'max-w-lg',
|
||||
xl: 'max-w-xl',
|
||||
'2xl': 'max-w-2xl',
|
||||
'3xl': 'max-w-3xl',
|
||||
'4xl': 'max-w-4xl',
|
||||
'5xl': 'max-w-5xl',
|
||||
'6xl': 'max-w-6xl',
|
||||
'7xl': 'max-w-7xl',
|
||||
};
|
||||
|
||||
export function PageLayout({maxWidth = '7xl', padding = false, children}: PropsWithChildren<PageLayoutProps>) {
|
||||
const classes = cn('mx-auto w-full', maxWidthClasses[maxWidth], padding && 'px-4 sm:px-6 lg:px-8');
|
||||
|
||||
return <div class={classes}>{children}</div>;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @jsxRuntime automatic */
|
||||
/** @jsxImportSource hono/jsx */
|
||||
|
||||
import {PageContainer} from '@fluxer/admin/src/components/ui/Layout/PageContainer';
|
||||
import {PageHeader} from '@fluxer/admin/src/components/ui/Layout/PageHeader';
|
||||
import type {Child, PropsWithChildren} from 'hono/jsx';
|
||||
|
||||
export interface SearchListPageLayoutProps {
|
||||
title: string;
|
||||
description?: string;
|
||||
actions?: Child;
|
||||
searchForm: Child;
|
||||
}
|
||||
|
||||
export function SearchListPageLayout({
|
||||
title,
|
||||
description,
|
||||
actions,
|
||||
searchForm,
|
||||
children,
|
||||
}: PropsWithChildren<SearchListPageLayoutProps>) {
|
||||
return (
|
||||
<PageContainer>
|
||||
<PageHeader title={title} description={description} actions={actions} />
|
||||
{searchForm}
|
||||
{children}
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
37
packages/admin/src/components/ui/Layout/TwoColumnGrid.tsx
Normal file
37
packages/admin/src/components/ui/Layout/TwoColumnGrid.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @jsxRuntime automatic */
|
||||
/** @jsxImportSource hono/jsx */
|
||||
|
||||
import type {PropsWithChildren} from 'hono/jsx';
|
||||
|
||||
export interface TwoColumnGridProps {
|
||||
gap?: 'sm' | 'md' | 'lg';
|
||||
}
|
||||
|
||||
const gapClasses = {
|
||||
sm: 'gap-2',
|
||||
md: 'gap-4',
|
||||
lg: 'gap-6',
|
||||
};
|
||||
|
||||
export function TwoColumnGrid({gap = 'md', children}: PropsWithChildren<TwoColumnGridProps>) {
|
||||
return <div class={`grid grid-cols-1 md:grid-cols-2 ${gapClasses[gap]}`}>{children}</div>;
|
||||
}
|
||||
52
packages/admin/src/components/ui/Layout/VStack.tsx
Normal file
52
packages/admin/src/components/ui/Layout/VStack.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @jsxRuntime automatic */
|
||||
/** @jsxImportSource hono/jsx */
|
||||
|
||||
import {cn} from '@fluxer/admin/src/utils/ClassNames';
|
||||
import type {PropsWithChildren} from 'hono/jsx';
|
||||
|
||||
export type VStackAlign = 'start' | 'center' | 'end' | 'stretch';
|
||||
|
||||
export interface VStackProps {
|
||||
gap?: number | string;
|
||||
align?: VStackAlign;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const alignClasses: Record<VStackAlign, string> = {
|
||||
start: 'items-start',
|
||||
center: 'items-center',
|
||||
end: 'items-end',
|
||||
stretch: 'items-stretch',
|
||||
};
|
||||
|
||||
function getGapClass(gap: number | string): string {
|
||||
if (typeof gap === 'number') {
|
||||
return `gap-${gap}`;
|
||||
}
|
||||
return gap;
|
||||
}
|
||||
|
||||
export function VStack({gap = 4, align = 'stretch', class: className, children}: PropsWithChildren<VStackProps>) {
|
||||
const classes = cn('flex flex-col', getGapClass(gap), alignClasses[align], className);
|
||||
|
||||
return <div class={classes}>{children}</div>;
|
||||
}
|
||||
Reference in New Issue
Block a user