Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .prototools
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
bun = "1.3.4"
node = "~22"
node = "~24"

[settings]
auto-install = true
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/(docs)/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export default async function Page({
<div className="text-muted-foreground">{metadata.summary}</div>
</div>
<div className="mb-32 flex-1 w-full">
<MDX components={useMDXComponents()} />
<MDX components={useMDXComponents({ slug })} />
</div>
</div>
<div className="col-span-1 hidden xl:block">
Expand Down
85 changes: 85 additions & 0 deletions apps/web/app/examples/data-views/_/columns.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { createColumnBuilder } from '@bazza-ui/data-view/react'
import {
AlarmClockIcon,
CalendarIcon,
CircleDotIcon,
FlameIcon,
TagsIcon,
TextIcon,
} from 'lucide-react'
import { ISSUE_STATUSES, LABELS_BY_ID } from './data'
import type { Issue } from './types'

const col = createColumnBuilder<Issue>()

export const columnsConfig = [
col
.text()
.id('title')
.displayName('Title')
.icon(TextIcon)
.accessor((d) => d.title)
.sortable()
.build(),

col
.option()
.id('status')
.displayName('Status')
.icon(CircleDotIcon)
.accessor((d) => d.status.id)
.sortable()
.options(
ISSUE_STATUSES.map((s) => ({
label: s.name,
value: s.id,
icon: s.icon,
})),
)
.build(),

// Labels: inferred from data via transformValueToOptionFn
// No static .options() — the ColumnDataService scans all rows,
// deduplicates label ids, and maps each through this function.
col
.multiOption()
.id('labels')
.displayName('Labels')
.icon(TagsIcon)
.accessor((d) => d.labels?.map((l) => l.id) ?? [])
.transformValueToOptionFn((labelId) => {
const label = LABELS_BY_ID.get(labelId)
return {
label: label?.name ?? labelId,
value: labelId,
}
})
.build(),

col
.number()
.id('estimatedHours')
.displayName('Est. Hours')
.icon(AlarmClockIcon)
.accessor((d) => d.estimatedHours)
.sortable()
.build(),

col
.date()
.id('startDate')
.displayName('Start Date')
.icon(CalendarIcon)
.accessor((d) => d.startDate as Date)
.sortable()
.build(),

col
.boolean()
.id('isUrgent')
.displayName('Urgent')
.icon(FlameIcon)
.accessor((d) => d.isUrgent)
.toggledStateName('Urgent')
.build(),
] as const
154 changes: 154 additions & 0 deletions apps/web/app/examples/data-views/_/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { sub } from 'date-fns'
import {
CircleCheckIcon,
CircleDashedIcon,
CircleDotIcon,
CircleIcon,
} from 'lucide-react'
import { nanoid } from 'nanoid'
import { randomInteger, sample } from 'remeda'
import type { Issue, IssueLabel, IssueStatus, User } from './types'

// ── Static Reference Data ──────────────────────────────────

export const USERS: User[] = [
{ id: 'u1', name: 'John Smith', picture: '/avatars/john-smith.png' },
{ id: 'u2', name: 'Rose Eve', picture: '/avatars/rose-eve.png' },
{ id: 'u3', name: 'Adam Young', picture: '/avatars/adam-young.png' },
{ id: 'u4', name: 'Michael Scott', picture: '/avatars/michael-scott.png' },
]

export const ISSUE_STATUSES: IssueStatus[] = [
{ id: 'backlog', name: 'Backlog', icon: CircleDashedIcon },
{ id: 'todo', name: 'Todo', icon: CircleIcon },
{ id: 'in-progress', name: 'In Progress', icon: CircleDotIcon },
{ id: 'done', name: 'Done', icon: CircleCheckIcon },
]

export const ISSUE_LABELS: IssueLabel[] = [
{ id: 'l1', name: 'Bug', color: 'red' },
{ id: 'l2', name: 'Enhancement', color: 'green' },
{ id: 'l3', name: 'Task', color: 'blue' },
{ id: 'l4', name: 'Urgent', color: 'pink' },
{ id: 'l5', name: 'Frontend', color: 'orange' },
{ id: 'l6', name: 'Backend', color: 'teal' },
{ id: 'l7', name: 'Performance', color: 'purple' },
{ id: 'l8', name: 'Documentation', color: 'amber' },
{ id: 'l9', name: 'Security', color: 'sky' },
{ id: 'l10', name: 'Testing', color: 'yellow' },
{ id: 'l11', name: 'Refactor', color: 'lime' },
{ id: 'l12', name: 'API', color: 'red' },
{ id: 'l13', name: 'Database', color: 'violet' },
{ id: 'l14', name: 'AI Model', color: 'cyan' },
{ id: 'l15', name: 'Infrastructure', color: 'emerald' },
{ id: 'l16', name: 'Accessibility', color: 'rose' },
{ id: 'l17', name: 'Monitoring', color: 'indigo' },
{ id: 'l18', name: 'Authentication', color: 'fuchsia' },
{ id: 'l19', name: 'Deployment', color: 'green' },
{ id: 'l20', name: 'Feature Request', color: 'orange' },
]

/** Lookup table for label id -> IssueLabel. */
export const LABELS_BY_ID = new Map(ISSUE_LABELS.map((l) => [l.id, l]))

// ── Issue Title Generator ──────────────────────────────────

const VERBS = [
'Fix',
'Add',
'Improve',
'Refactor',
'Update',
'Remove',
'Implement',
'Optimize',
'Redesign',
'Revert',
]

const NOUNS = [
'task sidebar',
'project view',
'keyboard shortcuts',
'user permissions',
'search performance',
'issue modal',
'auth flow',
'API integration',
'activity feed',
'notifications',
'team management',
'board drag & drop',
'custom workflows',
'mobile responsiveness',
'comment threading',
'GitHub sync',
'dark mode',
'date picker',
'status badges',
'workspace settings',
]

const SUFFIXES = [
'in Safari',
'for enterprise customers',
'on slow connections',
'edge case in Firefox',
'when duplicating issues',
'for archived projects',
'in mobile view',
'on user onboarding',
'when using keyboard nav',
'for SSO users',
]

function pick<T>(arr: readonly T[]): T {
return arr[Math.floor(Math.random() * arr.length)]!
}

function generateIssueTitle(): string {
const verb = pick(VERBS)
const noun = pick(NOUNS)
const suffix = Math.random() < 0.5 ? '' : ` ${pick(SUFFIXES)}`
return `${verb} ${noun}${suffix}`
}

// ── Issue Generator ────────────────────────────────────────

function generateIssue(): Issue {
const status = pick(ISSUE_STATUSES)
const assignee = Math.random() > 0.3 ? pick(USERS) : undefined
const labelCount = randomInteger(0, 2)
const labels =
labelCount > 0
? (sample(ISSUE_LABELS, labelCount) as IssueLabel[])
: undefined
const estimatedHours = randomInteger(1, 16)
const startDate =
status.id === 'backlog'
? undefined
: sub(new Date(), { days: randomInteger(1, 90) })
const isUrgent = Math.random() > 0.85

return {
id: nanoid(),
title: generateIssueTitle(),
status,
labels,
assignee,
estimatedHours,
startDate,
isUrgent,
}
}

export function generateIssues(count: number): Issue[] {
const arr: Issue[] = []
for (let i = 0; i < count; i++) {
arr.push(generateIssue())
}
return arr
}

/** Default dataset: 30k rows. */
export const ISSUES = generateIssues(30_000)
Loading