Skip to content

Commit

Permalink
feat: add ui-avatar and ui-select-enum components
Browse files Browse the repository at this point in the history
  • Loading branch information
beeman committed Mar 9, 2024
1 parent d044402 commit c872331
Show file tree
Hide file tree
Showing 35 changed files with 363 additions and 44 deletions.
28 changes: 28 additions & 0 deletions apps/web/src/app/features/demo/demo-feature-avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Group, SimpleGrid } from '@mantine/core'
import { UiAvatar, UiCard } from '@pubkey-ui/core'

export function DemoFeatureAvatar() {
return (
<UiCard title="Avatar">
<SimpleGrid cols={6}>
<Group justify="center">
<UiAvatar name="John Doe" tooltipLabel="John Doe" />
<UiAvatar name="Jane Doe" tooltipLabel="Jane Doe" />
<UiAvatar name="Jack Doe" tooltipLabel="Jack Doe" />
<UiAvatar name="Jill Doe" tooltipLabel="Jill Doe" />
<UiAvatar name="Jay Doe" tooltipLabel="Jay Doe" />
<UiAvatar name="Joanne Doe" tooltipLabel="Joanne Doe" />
</Group>
<Group justify="center">
<UiAvatar url={'https://avatars.githubusercontent.com/u/36491?v=4'} />
<UiAvatar size="lg" url={'https://avatars.githubusercontent.com/u/36491?v=4'} />
<UiAvatar
to="https://github.com/beeman"
size="lg"
url={'https://avatars.githubusercontent.com/u/36491?v=4'}
/>
</Group>
</SimpleGrid>
</UiCard>
)
}
1 change: 1 addition & 0 deletions apps/web/src/app/features/demo/demo-feature-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export function DemoFeatureCard() {
<UiCard withBorder title="Card" shadow="lg">
CARD CONTENT
</UiCard>
<UiCard withBorder title="Empty Card" shadow="lg" />
</UiStack>
)
}
4 changes: 2 additions & 2 deletions apps/web/src/app/features/demo/demo-feature-grid-routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { Badge, SimpleGrid } from '@mantine/core'
import { UiCard, UiGridRoutes } from '@pubkey-ui/core'
import { IconDashboard } from '@tabler/icons-react'

export function DemoFeatureGridRoutes() {
export function DemoFeatureGridRoutes({ basePath = '/demo/grid-routes' }: { basePath?: string }) {
return (
<UiCard title="Grid Routes">
<UiGridRoutes
basePath="/demo/grid-routes"
basePath={basePath}
routes={[
{
path: 'dashboard',
Expand Down
25 changes: 25 additions & 0 deletions apps/web/src/app/features/demo/demo-feature-select-enum.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { SimpleGrid } from '@mantine/core'
import { getEnumOptions, UiCard, UiDebug, UiMultiSelectEnum, UiSelectEnum, UiStack } from '@pubkey-ui/core'
import { useState } from 'react'

enum DemoEnum {
One = 'One',
Two = 'Two',
Three = 'Three',
}
export function DemoFeatureSelectEnum() {
const [value, setValue] = useState<DemoEnum | undefined>(undefined)
const [values, setValues] = useState<DemoEnum[] | undefined>(undefined)
return (
<UiCard title="SelectEnum">
<UiStack>
<SimpleGrid cols={2}>
<UiSelectEnum<DemoEnum> value={value} setValue={setValue} options={getEnumOptions(DemoEnum)} />
<UiDebug data={{ value }} open />
<UiMultiSelectEnum<DemoEnum> values={values} setValues={setValues} options={getEnumOptions(DemoEnum)} />
<UiDebug data={{ values }} open />
</SimpleGrid>
</UiStack>
</UiCard>
)
}
10 changes: 5 additions & 5 deletions apps/web/src/app/features/demo/demo-feature-tab-routes.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { SimpleGrid } from '@mantine/core'
import { UiCard, UiTabRoutes } from '@pubkey-ui/core'

export function DemoFeatureTabRoutes() {
export function DemoFeatureTabRoutes({ basePath = '/demo/tab-routes' }: { basePath?: string }) {
return (
<UiCard title="Tab Routes">
<UiTabRoutes
basePath="/demo/tab-routes"
basePath={basePath}
tabs={[
{
path: 'overview',
label: 'Overview',
path: 'dashboard',
label: 'Dashboard',
element: (
<SimpleGrid cols={2} spacing="md">
<UiCard title="Overview">Overview</UiCard>
<UiCard title="Dashboard">Dashboard</UiCard>
</SimpleGrid>
),
},
Expand Down
3 changes: 1 addition & 2 deletions apps/web/src/app/features/demo/demo-feature-theme-select.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button, Group } from '@mantine/core'
import { UiCard, UiDebugModal, UiStack, UiThemeSelect, useUiThemeSelect } from '@pubkey-ui/core'
import { UiCard, UiStack, UiThemeSelect, useUiThemeSelect } from '@pubkey-ui/core'

export function DemoFeatureThemeSelect() {
const { themes, selected, selectTheme } = useUiThemeSelect()
Expand All @@ -17,7 +17,6 @@ export function DemoFeatureThemeSelect() {
))}
</Group>
</UiCard>
<UiDebugModal data={{ themes, selected }} />
</UiStack>
)
}
4 changes: 4 additions & 0 deletions apps/web/src/app/features/demo/demo-feature.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { UiGridRoutes, UiPage } from '@pubkey-ui/core'
import { ReactNode } from 'react'
import { DemoFeatureAlerts } from './demo-feature-alerts'
import { DemoFeatureAnchor } from './demo-feature-anchor'
import { DemoFeatureAvatar } from './demo-feature-avatar'
import { DemoFeatureBack } from './demo-feature-back'
import { DemoFeatureCard } from './demo-feature-card'
import { DemoFeatureCopy } from './demo-feature-copy'
Expand All @@ -17,6 +18,7 @@ import { DemoFeatureMenu } from './demo-feature-menu'
import { DemoFeatureNotFound } from './demo-feature-not-found'
import { DemoFeaturePage } from './demo-feature-page'
import { DemoFeatureSearchInput } from './demo-feature-search-input'
import { DemoFeatureSelectEnum } from './demo-feature-select-enum'
import { DemoFeatureStack } from './demo-feature-stack'
import { DemoFeatureTabRoutes } from './demo-feature-tab-routes'
import { DemoFeatureThemeSelect } from './demo-feature-theme-select'
Expand All @@ -31,6 +33,7 @@ export function DemoFeature() {
}[] = [
{ path: 'alerts', label: 'Alerts', element: <DemoFeatureAlerts /> },
{ path: 'anchor', label: 'Anchor', element: <DemoFeatureAnchor /> },
{ path: 'avatar', label: 'Avatar', element: <DemoFeatureAvatar /> },
{ path: 'back', label: 'Back', element: <DemoFeatureBack /> },
{ path: 'card', label: 'Card', element: <DemoFeatureCard /> },
{ path: 'copy', label: 'Copy', element: <DemoFeatureCopy /> },
Expand All @@ -46,6 +49,7 @@ export function DemoFeature() {
{ path: 'not-found', label: 'Not Found', element: <DemoFeatureNotFound /> },
{ path: 'page', label: 'Page', element: <DemoFeaturePage /> },
{ path: 'search-input', label: 'Search Input', element: <DemoFeatureSearchInput /> },
{ path: 'select-enum', label: 'Select Enum', element: <DemoFeatureSelectEnum /> },
{ path: 'stack', label: 'Stack', element: <DemoFeatureStack /> },
{ path: 'tab-routes', label: 'Tab Routes', element: <DemoFeatureTabRoutes /> },
{ path: 'theme-select', label: 'Theme Select', element: <DemoFeatureThemeSelect /> },
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './ui-alert'
export * from './ui-anchor'
export * from './ui-avatar'
export * from './ui-back'
export * from './ui-card'
export * from './ui-container'
Expand All @@ -10,13 +11,15 @@ export * from './ui-form'
export * from './ui-grid-routes'
export * from './ui-group'
export * from './ui-header'
export * from './ui-helpers'
export * from './ui-layout'
export * from './ui-loader'
export * from './ui-logo'
export * from './ui-menu'
export * from './ui-not-found'
export * from './ui-page'
export * from './ui-search-input'
export * from './ui-select-enum'
export * from './ui-stack'
export * from './ui-tab-routes'
export * from './ui-theme'
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/lib/ui-avatar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ui-avatar'
32 changes: 32 additions & 0 deletions packages/core/src/lib/ui-avatar/ui-avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Avatar, AvatarProps, Tooltip } from '@mantine/core'
import { getColorByIndex, getIntFromString } from '../ui-helpers'
import { UiAnchor } from '../ui-anchor'

export type UiAvatarProps = Omit<AvatarProps, 'src'> & {
url?: string | null
name?: string | null
to?: string
tooltipLabel?: string
}

export function UiAvatar({ url, name, to, tooltipLabel, ...props }: UiAvatarProps) {
const firstLetter = name?.charAt(0) ?? '?'

const content = url?.length ? (
<Avatar radius={100} src={url} alt={`${name} avatar`} {...props} />
) : (
<Avatar radius={100} color={getColorByIndex(getIntFromString(name ?? ''))} {...props}>
{firstLetter?.toUpperCase()}
</Avatar>
)

const anchor = <UiAnchor to={to}>{content}</UiAnchor>

return tooltipLabel ? (
<Tooltip label={tooltipLabel} withArrow>
{anchor}
</Tooltip>
) : (
anchor
)
}
12 changes: 6 additions & 6 deletions packages/core/src/lib/ui-card/ui-card.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Box, Paper, PaperProps, Skeleton } from '@mantine/core'
import { Box, Paper, PaperProps, Skeleton, Stack } from '@mantine/core'
import { useUiBreakpoints } from '../ui-theme'
import { ReactNode } from 'react'
import { UiCardTitle } from './ui-card-title'

interface UiCardProps extends PaperProps {
children: ReactNode
children?: ReactNode
loading?: boolean
title?: ReactNode
}
Expand All @@ -14,10 +14,10 @@ export function UiCard({ loading, title, ...props }: UiCardProps) {

return (
<Paper p={isSm ? 'xs' : 'md'} withBorder {...props}>
{title ? (
<Box mb={isSm ? 'xs' : 'md'}>{typeof title === 'string' ? <UiCardTitle>{title}</UiCardTitle> : title}</Box>
) : null}
{loading ? <Skeleton visible={loading}>{props.children}</Skeleton> : props.children}
<Stack gap={isSm ? 'xs' : 'md'}>
{title ? <Box>{typeof title === 'string' ? <UiCardTitle>{title}</UiCardTitle> : title}</Box> : null}
{props.children ? loading ? <Skeleton visible={loading}>{props.children}</Skeleton> : props.children : null}
</Stack>
</Paper>
)
}
10 changes: 2 additions & 8 deletions packages/core/src/lib/ui-dashboard-grid/ui-dashboard-grid.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import { SimpleGrid, Text, UnstyledButton, useMantineTheme } from '@mantine/core'
import { useUiTheme } from '../ui-theme'
import { ComponentType } from 'react'

import { getColorByIndex } from '../ui-helpers'
import { useUiTheme } from '../ui-theme'
import classes from './ui-dashboard-grid.module.css'

const linkColors = ['violet', 'indigo', 'blue', 'green', 'teal', 'cyan', 'pink', 'red', 'orange']

export function getColorByIndex(index: number) {
return linkColors[index % linkColors.length]
}

export interface UiDashboardItem {
icon: ComponentType<{ color?: string; size: number | string }>
label: string
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/lib/ui-helpers/get-color-by-index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const colorByIndex = ['violet', 'indigo', 'blue', 'green', 'teal', 'cyan', 'pink', 'red', 'orange']
export function getColorByIndex(index: number, colors: string[] = colorByIndex) {
return colors[index % colors.length]
}
12 changes: 12 additions & 0 deletions packages/core/src/lib/ui-helpers/get-int-from-string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export function getIntFromString(str: string) {
let hash = 0
if (str.length == 0) {
return hash
}
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i)
hash = (hash << 5) - hash + char
hash = hash & hash // Convert to 32bit integer
}
return Math.abs(hash)
}
2 changes: 2 additions & 0 deletions packages/core/src/lib/ui-helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './get-color-by-index'
export * from './get-int-from-string'
1 change: 1 addition & 0 deletions packages/core/src/lib/ui-select-enum/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ui-select-enum'
50 changes: 50 additions & 0 deletions packages/core/src/lib/ui-select-enum/ui-select-enum.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { MultiSelect, MultiSelectProps, Select, SelectProps } from '@mantine/core'

export function UiMultiSelectEnum<T>({
values,
setValues,
options,
...props
}: MultiSelectProps & {
values: T[] | undefined
setValues: (values: T[] | undefined) => void
options: { value: string; label: string }[]
}) {
return (
<MultiSelect
value={values?.map((v) => `${v}`) ?? []}
onChange={(values) => setValues(values.map((v) => v as T))}
data={options}
{...props}
/>
)
}

export function UiSelectEnum<T>({
value,
setValue,
options,
...props
}: SelectProps & {
value: T | undefined
setValue: (value: T | undefined) => void
options: { value: string; label: string }[]
}) {
return (
<Select
value={value?.toString() ?? ''}
onChange={(value) => setValue(value === '' ? undefined : (value as T))}
data={options}
{...props}
/>
)
}

export function getEnumOptions<T extends Record<string, string>>(
enumObject: T,
): { label: string; value: T[keyof T] }[] {
return Object.keys(enumObject).map((key: string) => ({
label: key,
value: enumObject[key as keyof T],
}))
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface ComponentGeneratorSchema {
type:
| 'alert'
| 'anchor'
| 'avatar'
| 'back'
| 'card'
| 'container'
Expand All @@ -26,13 +27,15 @@ export interface ComponentGeneratorSchema {
| 'grid-routes'
| 'group'
| 'header'
| 'helpers'
| 'layout'
| 'loader'
| 'logo'
| 'menu'
| 'not-found'
| 'page'
| 'search-input'
| 'select-enum'
| 'stack'
| 'tab-routes'
| 'theme'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"enum": [
"alert",
"anchor",
"avatar",
"back",
"card",
"container",
Expand All @@ -28,13 +29,15 @@
"grid-routes",
"group",
"header",
"helpers",
"layout",
"loader",
"logo",
"menu",
"not-found",
"page",
"search-input",
"select-enum",
"stack",
"tab-routes",
"theme",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ComponentGeneratorSchema } from './component-generator-schema'
export const componentTypes: ComponentGeneratorSchema['type'][] = [
'alert',
'anchor',
'avatar',
'back',
'card',
'container',
Expand All @@ -13,13 +14,15 @@ export const componentTypes: ComponentGeneratorSchema['type'][] = [
'grid-routes',
'group',
'header',
'helpers',
'layout',
'loader',
'logo',
'menu',
'not-found',
'page',
'search-input',
'select-enum',
'stack',
'tab-routes',
'theme',
Expand Down
Loading

0 comments on commit c872331

Please sign in to comment.