feat: update theme provider and density styles, enhance typography and font mappings, and refactor showcase components
This commit is contained in:
Vendored
+1
-1
@@ -4,5 +4,5 @@
|
||||
"mode": "auto"
|
||||
}
|
||||
],
|
||||
"cSpell.words": ["mantine", "Millis", "Pandang", "Ujung", "WITA"]
|
||||
"cSpell.words": ["mantine", "Menlo", "Millis", "Pandang", "Segoe", "Ujung", "WITA"]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ const ShowcaseView = lazy(() => import('./showcase/showcase-view'));
|
||||
|
||||
export default function App() {
|
||||
const [colorScheme, setColorScheme] = useState<ColorSchemeType>('light');
|
||||
const [density, setDensity] = useState<DensityType>('standard');
|
||||
const [density, setDensity] = useState<DensityType>('compact');
|
||||
|
||||
return (
|
||||
<ThemeProvider colorScheme={colorScheme} density={density}>
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
import { ColorSchemeType, DensityType } from '@repo/ui/provider';
|
||||
import { Button, Card, Container, Select, Group, Stack, Text, Title, TextInput, Checkbox } from '@repo/ui/components';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Container,
|
||||
Select,
|
||||
Group,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
TextInput,
|
||||
Checkbox,
|
||||
PasswordInput,
|
||||
NumberInput,
|
||||
Textarea,
|
||||
Switch,
|
||||
Radio,
|
||||
Table,
|
||||
Badge,
|
||||
Divider,
|
||||
} from '@repo/ui/components';
|
||||
|
||||
interface ShowcaseViewProps {
|
||||
colorScheme: ColorSchemeType;
|
||||
@@ -9,15 +28,24 @@ interface ShowcaseViewProps {
|
||||
}
|
||||
|
||||
export default function ShowcaseView({ colorScheme, setColorScheme, density, setDensity }: ShowcaseViewProps) {
|
||||
return (
|
||||
<Container size="md" py="xl">
|
||||
<Stack gap="xl">
|
||||
<Title order={1}>UI Gateway Showcase</Title>
|
||||
// Mock data for the table
|
||||
const tableData = [
|
||||
{ id: 'ORD-001', customer: 'John Doe', status: 'Shipped', total: '$120.00' },
|
||||
{ id: 'ORD-002', customer: 'Jane Smith', status: 'Pending', total: '$85.50' },
|
||||
{ id: 'ORD-003', customer: 'Acme Corp', status: 'Delivered', total: '$1,250.00' },
|
||||
];
|
||||
|
||||
{/* Control Panel */}
|
||||
return (
|
||||
<Container size="lg" py="xl">
|
||||
<Stack gap="xl">
|
||||
<Title order={1}>Super App UI Showcase</Title>
|
||||
|
||||
{/* =========================================
|
||||
CONTROL PANEL
|
||||
========================================= */}
|
||||
<Card withBorder shadow="sm" radius="md" p="md">
|
||||
<Title order={3} mb="md">
|
||||
Control Panel
|
||||
Theme Controls
|
||||
</Title>
|
||||
<Group grow align="flex-end">
|
||||
<Select
|
||||
@@ -30,55 +58,152 @@ export default function ShowcaseView({ colorScheme, setColorScheme, density, set
|
||||
]}
|
||||
/>
|
||||
<Select
|
||||
label="Density"
|
||||
label="Density (Spacing & Sizing)"
|
||||
value={density}
|
||||
onChange={(val: string | null) => setDensity((val as DensityType) || 'standard')}
|
||||
data={[
|
||||
{ value: 'compact', label: 'Compact' },
|
||||
{ value: 'standard', label: 'Standard' },
|
||||
{ value: 'spacious', label: 'Spacious' },
|
||||
{ value: 'compact', label: 'Compact (ERP Mode)' },
|
||||
{ value: 'standard', label: 'Standard (UI Mode)' },
|
||||
]}
|
||||
/>
|
||||
</Group>
|
||||
</Card>
|
||||
|
||||
{/* Showcase Area */}
|
||||
{/* =========================================
|
||||
TAILWIND V4 BRIDGE TEST
|
||||
========================================= */}
|
||||
<Card withBorder shadow="sm" radius="md" p="md">
|
||||
<Title order={3} mb="md">
|
||||
Tailwind v4 Synchronization
|
||||
</Title>
|
||||
{/* This div purely uses Tailwind classes to prove it inherits Mantine's variables */}
|
||||
<div className="bg-brand-500 text-brand-50 p-md rounded-md shadow-md text-base">
|
||||
<span className="font-bold">Tailwind works!</span> The padding (p-md), border-radius (rounded-md), text size
|
||||
(text-base), and background color of this box are entirely controlled by the ThemeProvider's current state.
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* =========================================
|
||||
TYPOGRAPHY & BUTTONS
|
||||
========================================= */}
|
||||
<Card withBorder shadow="sm" radius="md" p="md">
|
||||
<Stack gap="lg">
|
||||
<div className="bg-brand-100">
|
||||
<Title order={3} mb="sm">
|
||||
Typography
|
||||
<div>
|
||||
<Title order={3} mb="xs">
|
||||
Typography & Badges
|
||||
</Title>
|
||||
<Text size="sm" c="dimmed">
|
||||
This is dimmed small text.
|
||||
This is dimmed small text indicating a subtitle.
|
||||
</Text>
|
||||
<Text mb="md">
|
||||
This is standard text describing the components below. Watch how the font changes when you switch
|
||||
density.
|
||||
</Text>
|
||||
<Text>This is standard text describing the components below.</Text>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Title order={3} mb="sm">
|
||||
Buttons
|
||||
</Title>
|
||||
<Group>
|
||||
<Button variant="filled">Filled Button</Button>
|
||||
<Button variant="outline">Outline Button</Button>
|
||||
<Button variant="light">Light Button</Button>
|
||||
<Button variant="subtle">Subtle Button</Button>
|
||||
<Badge color="brand">Brand Badge</Badge>
|
||||
<Badge color="success" variant="light">
|
||||
Success Status
|
||||
</Badge>
|
||||
<Badge color="error" variant="outline">
|
||||
Error State
|
||||
</Badge>
|
||||
</Group>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div>
|
||||
<Title order={3} mb="md">
|
||||
Form Elements
|
||||
Buttons
|
||||
</Title>
|
||||
<Group grow>
|
||||
<TextInput label="First Name" placeholder="John" />
|
||||
<TextInput label="Last Name" placeholder="Doe" />
|
||||
<Group>
|
||||
<Button variant="filled" color="brand">
|
||||
Filled Button
|
||||
</Button>
|
||||
<Button variant="outline" color="brand">
|
||||
Outline Button
|
||||
</Button>
|
||||
<Button variant="light" color="info">
|
||||
Light Info
|
||||
</Button>
|
||||
<Button variant="subtle" color="error">
|
||||
Cancel
|
||||
</Button>
|
||||
</Group>
|
||||
<Checkbox label="I agree to the terms and conditions" mt="md" defaultChecked />
|
||||
</div>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
{/* =========================================
|
||||
COMPLEX FORMS (ERP STYLE)
|
||||
========================================= */}
|
||||
<Card withBorder shadow="sm" radius="md" p="md">
|
||||
<Title order={3} mb="md">
|
||||
Form Elements
|
||||
</Title>
|
||||
<Stack gap="md">
|
||||
<Group grow align="flex-start">
|
||||
<TextInput label="First Name" placeholder="Enter your first name" withAsterisk />
|
||||
<TextInput label="Last Name" placeholder="Enter your last name" />
|
||||
</Group>
|
||||
|
||||
<Group grow align="flex-start">
|
||||
<NumberInput label="Age" placeholder="25" min={0} max={100} />
|
||||
<PasswordInput label="Password" placeholder="Your secret password" withAsterisk />
|
||||
</Group>
|
||||
|
||||
<Textarea label="Bio" placeholder="Tell us about yourself" minRows={3} />
|
||||
|
||||
<Group mt="sm">
|
||||
<Checkbox label="I agree to the terms and conditions" defaultChecked />
|
||||
<Switch label="Enable notifications" defaultChecked />
|
||||
</Group>
|
||||
|
||||
<Radio.Group name="favoriteFramework" label="Select your favorite framework" withAsterisk>
|
||||
<Group mt="xs">
|
||||
<Radio value="react" label="React" />
|
||||
<Radio value="svelte" label="Svelte" />
|
||||
<Radio value="vue" label="Vue" />
|
||||
</Group>
|
||||
</Radio.Group>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
{/* =========================================
|
||||
DATA GRID / TABLE
|
||||
========================================= */}
|
||||
<Card withBorder shadow="sm" radius="md" p="md">
|
||||
<Title order={3} mb="md">
|
||||
Data Grid
|
||||
</Title>
|
||||
<Table striped highlightOnHover withTableBorder withColumnBorders>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>Order ID</Table.Th>
|
||||
<Table.Th>Customer</Table.Th>
|
||||
<Table.Th>Status</Table.Th>
|
||||
<Table.Th>Total Amount</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{tableData.map((row) => (
|
||||
<Table.Tr key={row.id}>
|
||||
<Table.Td>{row.id}</Table.Td>
|
||||
<Table.Td>{row.customer}</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge
|
||||
size="sm"
|
||||
color={row.status === 'Delivered' ? 'success' : row.status === 'Shipped' ? 'info' : 'warning'}
|
||||
>
|
||||
{row.status}
|
||||
</Badge>
|
||||
</Table.Td>
|
||||
<Table.Td>{row.total}</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Card>
|
||||
</Stack>
|
||||
</Container>
|
||||
);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,12 +1,12 @@
|
||||
import { MantineProvider, createTheme, MantineThemeOverride } from '@mantine/core';
|
||||
import { MantineProvider, createTheme, mergeThemeOverrides } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { brandColors, errorColors, warningColors, successColors, infoColors } from '../theme/tokens/colors';
|
||||
import { typography } from '../theme/tokens/typography';
|
||||
import { radius } from '../theme/tokens/radius';
|
||||
import { densityStyles } from '../theme/tokens/density';
|
||||
import { compactDensity, standardDensity } from '../theme/tokens/density';
|
||||
|
||||
export type ColorSchemeType = 'light' | 'dark';
|
||||
export type DensityType = 'compact' | 'standard' | 'spacious';
|
||||
export type DensityType = 'compact' | 'standard';
|
||||
|
||||
interface ThemeProviderProps {
|
||||
children: React.ReactNode;
|
||||
@@ -14,6 +14,11 @@ interface ThemeProviderProps {
|
||||
density?: DensityType;
|
||||
}
|
||||
|
||||
const densityMap = {
|
||||
compact: compactDensity,
|
||||
standard: standardDensity,
|
||||
};
|
||||
|
||||
export function ThemeProvider({ children, colorScheme = 'light', density = 'standard' }: ThemeProviderProps) {
|
||||
const baseTheme = createTheme({
|
||||
colors: {
|
||||
@@ -25,19 +30,19 @@ export function ThemeProvider({ children, colorScheme = 'light', density = 'stan
|
||||
},
|
||||
primaryColor: 'brand',
|
||||
fontFamily: typography.fontFamily,
|
||||
fontFamilyMonospace: typography.fontFamilyMonospace,
|
||||
headings: typography.headings,
|
||||
spacing: densityStyles[density].spacing,
|
||||
radius: radius,
|
||||
});
|
||||
|
||||
const mantineColorScheme: 'light' | 'dark' = colorScheme;
|
||||
const themeOverride: MantineThemeOverride = baseTheme;
|
||||
const selectedDensity = densityMap[density];
|
||||
const mergedTheme = mergeThemeOverrides(baseTheme, selectedDensity);
|
||||
|
||||
return (
|
||||
<MantineProvider
|
||||
theme={themeOverride}
|
||||
forceColorScheme={mantineColorScheme}
|
||||
defaultColorScheme={mantineColorScheme}
|
||||
theme={mergedTheme}
|
||||
forceColorScheme={colorScheme}
|
||||
defaultColorScheme={colorScheme}
|
||||
>
|
||||
{children}
|
||||
</MantineProvider>
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
@source "../src";
|
||||
|
||||
@theme {
|
||||
/* =========================================
|
||||
FONT FAMILY MAPPING (Mantine -> Tailwind)
|
||||
========================================= */
|
||||
--base-font-size: 13px;
|
||||
--font-sans: var(--mantine-font-family);
|
||||
--font-mono: var(--mantine-font-family-monospace);
|
||||
|
||||
/* =========================================
|
||||
1. COLORS (Strictly mapped to Mantine 0-9)
|
||||
========================================= */
|
||||
@@ -244,3 +251,15 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================
|
||||
BASE RESETS
|
||||
========================================= */
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
font-size: var(--base-font-size);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,100 @@
|
||||
export const densityStyles = {
|
||||
compact: {
|
||||
spacing: { xs: '0.25rem', sm: '0.5rem', md: '0.75rem', lg: '1rem', xl: '1.25rem' },
|
||||
import { MantineThemeOverride } from '@mantine/core';
|
||||
|
||||
export const standardDensity: MantineThemeOverride = {
|
||||
spacing: { xs: '0.5rem', sm: '0.75rem', md: '1rem', lg: '1.5rem', xl: '2rem' },
|
||||
fontSizes: { xs: '0.75rem', sm: '0.875rem', md: '1rem', lg: '1.125rem', xl: '1.25rem' },
|
||||
components: {
|
||||
// Inputs & Buttons
|
||||
Button: { defaultProps: { size: 'md' } },
|
||||
TextInput: { defaultProps: { size: 'md' } },
|
||||
Select: { defaultProps: { size: 'md' } },
|
||||
NumberInput: { defaultProps: { size: 'md' } },
|
||||
PasswordInput: { defaultProps: { size: 'md' } },
|
||||
Textarea: { defaultProps: { size: 'md' } },
|
||||
MultiSelect: { defaultProps: { size: 'md' } },
|
||||
DatePickerInput: { defaultProps: { size: 'md' } },
|
||||
|
||||
// Toggles
|
||||
Checkbox: { defaultProps: { size: 'md' } },
|
||||
Radio: { defaultProps: { size: 'md' } },
|
||||
Switch: { defaultProps: { size: 'md' } },
|
||||
|
||||
// Data Display & Layout
|
||||
Badge: { defaultProps: { size: 'md' } },
|
||||
Pagination: { defaultProps: { size: 'md' } },
|
||||
Table: { defaultProps: { verticalSpacing: 'sm', horizontalSpacing: 'md' } },
|
||||
Card: { defaultProps: { padding: 'md' } },
|
||||
Modal: { defaultProps: { padding: 'md' } },
|
||||
},
|
||||
standard: {
|
||||
spacing: { xs: '0.5rem', sm: '0.75rem', md: '1rem', lg: '1.5rem', xl: '2rem' },
|
||||
},
|
||||
spacious: {
|
||||
spacing: { xs: '0.75rem', sm: '1rem', md: '1.5rem', lg: '2rem', xl: '3rem' },
|
||||
};
|
||||
|
||||
export const compactDensity: MantineThemeOverride = {
|
||||
spacing: { xs: '0.25rem', sm: '0.5rem', md: '0.75rem', lg: '1rem', xl: '1.5rem' },
|
||||
fontSizes: { xs: '0.6875rem', sm: '0.75rem', md: '0.8125rem', lg: '0.9375rem', xl: '1.125rem' },
|
||||
components: {
|
||||
// 1. Text Inputs (Physical size 'sm')
|
||||
TextInput: { defaultProps: { size: 'sm' } },
|
||||
Select: { defaultProps: { size: 'sm' } },
|
||||
NumberInput: { defaultProps: { size: 'sm' } },
|
||||
PasswordInput: { defaultProps: { size: 'sm' } },
|
||||
Textarea: { defaultProps: { size: 'sm' } },
|
||||
MultiSelect: { defaultProps: { size: 'sm' } },
|
||||
DatePickerInput: { defaultProps: { size: 'sm' } },
|
||||
|
||||
// 2. Form Toggles (Checkbox 16px, Switch compact)
|
||||
Checkbox: {
|
||||
defaultProps: { size: 'sm' },
|
||||
styles: {
|
||||
label: { fontSize: 'var(--mantine-font-size-md)' }, // Paksa teks label 13px
|
||||
},
|
||||
},
|
||||
Radio: {
|
||||
defaultProps: { size: 'sm' },
|
||||
styles: {
|
||||
label: { fontSize: 'var(--mantine-font-size-md)' }, // Paksa teks label 13px
|
||||
},
|
||||
},
|
||||
Switch: {
|
||||
defaultProps: { size: 'sm' },
|
||||
styles: {
|
||||
label: { fontSize: 'var(--mantine-font-size-md)' }, // Paksa teks label 13px
|
||||
},
|
||||
},
|
||||
|
||||
// 3. Data Display (Badges, Pagination for Tables)
|
||||
Badge: { defaultProps: { size: 'sm' } },
|
||||
Pagination: { defaultProps: { size: 'sm' } },
|
||||
|
||||
// 4. Tables & Layouts (Tighter padding for ERP grids)
|
||||
Table: {
|
||||
defaultProps: { verticalSpacing: 'sm', horizontalSpacing: 'sm' },
|
||||
styles: {
|
||||
th: { fontSize: 'var(--mantine-font-size-md)' }, // Header tabel 13px
|
||||
td: { fontSize: 'var(--mantine-font-size-md)' }, // Isi tabel 13px
|
||||
},
|
||||
},
|
||||
Card: { defaultProps: { padding: 'sm' } }, // Card padding mengecil
|
||||
Modal: { defaultProps: { padding: 'sm' } }, // Modal padding mengecil
|
||||
|
||||
// 5. Force 13px ('md') text inside 'sm' physical inputs
|
||||
Input: {
|
||||
styles: {
|
||||
input: { fontSize: 'var(--mantine-font-size-md)' },
|
||||
},
|
||||
},
|
||||
InputWrapper: {
|
||||
styles: {
|
||||
label: {
|
||||
fontSize: 'var(--mantine-font-size-md)',
|
||||
fontWeight: '500',
|
||||
},
|
||||
},
|
||||
},
|
||||
Button: {
|
||||
defaultProps: { size: 'sm' },
|
||||
styles: {
|
||||
root: { fontSize: 'var(--mantine-font-size-md)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './colors';
|
||||
export * from './typography';
|
||||
export * from './radius';
|
||||
export * from './density';
|
||||
|
||||
@@ -1,28 +1,5 @@
|
||||
export const typography = {
|
||||
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
|
||||
fontSizes: {
|
||||
xs: 'calc(13px * 0.846)',
|
||||
sm: 'calc(13px * 0.923)',
|
||||
md: '13px',
|
||||
lg: 'calc(13px * 1.154)',
|
||||
xl: 'calc(13px * 1.385)',
|
||||
},
|
||||
lineHeights: {
|
||||
xs: '1.4',
|
||||
sm: '1.45',
|
||||
md: '1.5',
|
||||
lg: '1.55',
|
||||
xl: '1.6',
|
||||
},
|
||||
headings: {
|
||||
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
|
||||
sizes: {
|
||||
h1: { fontSize: 'calc(13px * 2.5)' },
|
||||
h2: { fontSize: 'calc(13px * 2)' },
|
||||
h3: { fontSize: 'calc(13px * 1.75)' },
|
||||
h4: { fontSize: 'calc(13px * 1.5)' },
|
||||
h5: { fontSize: 'calc(13px * 1.25)' },
|
||||
h6: { fontSize: '13px' },
|
||||
},
|
||||
},
|
||||
fontFamily: " -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
||||
fontFamilyMonospace: ' ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
||||
headings: { fontFamily: " -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" },
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user