diff --git a/apps/web/src/app/features/demo/demo-feature-avatar.tsx b/apps/web/src/app/features/demo/demo-feature-avatar.tsx
new file mode 100644
index 0000000..f9cb572
--- /dev/null
+++ b/apps/web/src/app/features/demo/demo-feature-avatar.tsx
@@ -0,0 +1,28 @@
+import { Group, SimpleGrid } from '@mantine/core'
+import { UiAvatar, UiCard } from '@pubkey-ui/core'
+
+export function DemoFeatureAvatar() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/web/src/app/features/demo/demo-feature-card.tsx b/apps/web/src/app/features/demo/demo-feature-card.tsx
index bc0c8f8..436f82c 100644
--- a/apps/web/src/app/features/demo/demo-feature-card.tsx
+++ b/apps/web/src/app/features/demo/demo-feature-card.tsx
@@ -7,6 +7,7 @@ export function DemoFeatureCard() {
CARD CONTENT
+
)
}
diff --git a/apps/web/src/app/features/demo/demo-feature-grid-routes.tsx b/apps/web/src/app/features/demo/demo-feature-grid-routes.tsx
index f70fc1b..a07d62f 100644
--- a/apps/web/src/app/features/demo/demo-feature-grid-routes.tsx
+++ b/apps/web/src/app/features/demo/demo-feature-grid-routes.tsx
@@ -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 (
(undefined)
+ const [values, setValues] = useState(undefined)
+ return (
+
+
+
+ value={value} setValue={setValue} options={getEnumOptions(DemoEnum)} />
+
+ values={values} setValues={setValues} options={getEnumOptions(DemoEnum)} />
+
+
+
+
+ )
+}
diff --git a/apps/web/src/app/features/demo/demo-feature-tab-routes.tsx b/apps/web/src/app/features/demo/demo-feature-tab-routes.tsx
index 5f6de49..fb77930 100644
--- a/apps/web/src/app/features/demo/demo-feature-tab-routes.tsx
+++ b/apps/web/src/app/features/demo/demo-feature-tab-routes.tsx
@@ -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 (
- Overview
+ Dashboard
),
},
diff --git a/apps/web/src/app/features/demo/demo-feature-theme-select.tsx b/apps/web/src/app/features/demo/demo-feature-theme-select.tsx
index 56f5640..83df3cf 100644
--- a/apps/web/src/app/features/demo/demo-feature-theme-select.tsx
+++ b/apps/web/src/app/features/demo/demo-feature-theme-select.tsx
@@ -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()
@@ -17,7 +17,6 @@ export function DemoFeatureThemeSelect() {
))}
-
)
}
diff --git a/apps/web/src/app/features/demo/demo-feature.tsx b/apps/web/src/app/features/demo/demo-feature.tsx
index 99f1de2..d33fe6a 100644
--- a/apps/web/src/app/features/demo/demo-feature.tsx
+++ b/apps/web/src/app/features/demo/demo-feature.tsx
@@ -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'
@@ -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'
@@ -31,6 +33,7 @@ export function DemoFeature() {
}[] = [
{ path: 'alerts', label: 'Alerts', element: },
{ path: 'anchor', label: 'Anchor', element: },
+ { path: 'avatar', label: 'Avatar', element: },
{ path: 'back', label: 'Back', element: },
{ path: 'card', label: 'Card', element: },
{ path: 'copy', label: 'Copy', element: },
@@ -46,6 +49,7 @@ export function DemoFeature() {
{ path: 'not-found', label: 'Not Found', element: },
{ path: 'page', label: 'Page', element: },
{ path: 'search-input', label: 'Search Input', element: },
+ { path: 'select-enum', label: 'Select Enum', element: },
{ path: 'stack', label: 'Stack', element: },
{ path: 'tab-routes', label: 'Tab Routes', element: },
{ path: 'theme-select', label: 'Theme Select', element: },
diff --git a/packages/core/src/lib/index.ts b/packages/core/src/lib/index.ts
index 3ef0308..8d655b6 100644
--- a/packages/core/src/lib/index.ts
+++ b/packages/core/src/lib/index.ts
@@ -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'
@@ -10,6 +11,7 @@ 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'
@@ -17,6 +19,7 @@ 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'
diff --git a/packages/core/src/lib/ui-avatar/index.ts b/packages/core/src/lib/ui-avatar/index.ts
new file mode 100644
index 0000000..7702f6f
--- /dev/null
+++ b/packages/core/src/lib/ui-avatar/index.ts
@@ -0,0 +1 @@
+export * from './ui-avatar'
diff --git a/packages/core/src/lib/ui-avatar/ui-avatar.tsx b/packages/core/src/lib/ui-avatar/ui-avatar.tsx
new file mode 100644
index 0000000..e1b4244
--- /dev/null
+++ b/packages/core/src/lib/ui-avatar/ui-avatar.tsx
@@ -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 & {
+ 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 ? (
+
+ ) : (
+
+ {firstLetter?.toUpperCase()}
+
+ )
+
+ const anchor = {content}
+
+ return tooltipLabel ? (
+
+ {anchor}
+
+ ) : (
+ anchor
+ )
+}
diff --git a/packages/core/src/lib/ui-card/ui-card.tsx b/packages/core/src/lib/ui-card/ui-card.tsx
index 5abeaf0..5eab231 100644
--- a/packages/core/src/lib/ui-card/ui-card.tsx
+++ b/packages/core/src/lib/ui-card/ui-card.tsx
@@ -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
}
@@ -14,10 +14,10 @@ export function UiCard({ loading, title, ...props }: UiCardProps) {
return (
- {title ? (
- {typeof title === 'string' ? {title} : title}
- ) : null}
- {loading ? {props.children} : props.children}
+
+ {title ? {typeof title === 'string' ? {title} : title} : null}
+ {props.children ? loading ? {props.children} : props.children : null}
+
)
}
diff --git a/packages/core/src/lib/ui-dashboard-grid/ui-dashboard-grid.tsx b/packages/core/src/lib/ui-dashboard-grid/ui-dashboard-grid.tsx
index e0a58a2..60aacf1 100644
--- a/packages/core/src/lib/ui-dashboard-grid/ui-dashboard-grid.tsx
+++ b/packages/core/src/lib/ui-dashboard-grid/ui-dashboard-grid.tsx
@@ -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
diff --git a/packages/core/src/lib/ui-helpers/get-color-by-index.ts b/packages/core/src/lib/ui-helpers/get-color-by-index.ts
new file mode 100644
index 0000000..772d8ab
--- /dev/null
+++ b/packages/core/src/lib/ui-helpers/get-color-by-index.ts
@@ -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]
+}
diff --git a/packages/core/src/lib/ui-helpers/get-int-from-string.ts b/packages/core/src/lib/ui-helpers/get-int-from-string.ts
new file mode 100644
index 0000000..ff39aa4
--- /dev/null
+++ b/packages/core/src/lib/ui-helpers/get-int-from-string.ts
@@ -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)
+}
diff --git a/packages/core/src/lib/ui-helpers/index.ts b/packages/core/src/lib/ui-helpers/index.ts
new file mode 100644
index 0000000..2183c73
--- /dev/null
+++ b/packages/core/src/lib/ui-helpers/index.ts
@@ -0,0 +1,2 @@
+export * from './get-color-by-index'
+export * from './get-int-from-string'
diff --git a/packages/core/src/lib/ui-select-enum/index.ts b/packages/core/src/lib/ui-select-enum/index.ts
new file mode 100644
index 0000000..fe926fe
--- /dev/null
+++ b/packages/core/src/lib/ui-select-enum/index.ts
@@ -0,0 +1 @@
+export * from './ui-select-enum'
diff --git a/packages/core/src/lib/ui-select-enum/ui-select-enum.tsx b/packages/core/src/lib/ui-select-enum/ui-select-enum.tsx
new file mode 100644
index 0000000..dbe6a22
--- /dev/null
+++ b/packages/core/src/lib/ui-select-enum/ui-select-enum.tsx
@@ -0,0 +1,50 @@
+import { MultiSelect, MultiSelectProps, Select, SelectProps } from '@mantine/core'
+
+export function UiMultiSelectEnum({
+ values,
+ setValues,
+ options,
+ ...props
+}: MultiSelectProps & {
+ values: T[] | undefined
+ setValues: (values: T[] | undefined) => void
+ options: { value: string; label: string }[]
+}) {
+ return (
+ `${v}`) ?? []}
+ onChange={(values) => setValues(values.map((v) => v as T))}
+ data={options}
+ {...props}
+ />
+ )
+}
+
+export function UiSelectEnum({
+ value,
+ setValue,
+ options,
+ ...props
+}: SelectProps & {
+ value: T | undefined
+ setValue: (value: T | undefined) => void
+ options: { value: string; label: string }[]
+}) {
+ return (
+