Skip to content
Closed
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
465 changes: 465 additions & 0 deletions apps/docs/src/ui/data-table-filter.stories.tsx

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion apps/docs/vite.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@ export default defineConfig({
'@/lib/utils': path.resolve(__dirname, '../../packages/components/lib/utils'),
'@lambdacurry/forms': path.resolve(__dirname, '../../packages/components/src'),
'@lambdacurry/forms/lib': path.resolve(__dirname, '../../packages/components/lib'),
'@lambdacurry/forms/ui': path.resolve(__dirname, '../../packages/components/src/ui'),
'@lambdacurry/forms/ui/data-table-filter': path.resolve(__dirname, '../../packages/components/src/ui/data-table-filter'),
},
},
build: {
rollupOptions: {
external: ['react-router', 'react-router-dom'],
external: [
'react-router',
'react-router-dom',
// Ignore "use client" directive errors
/@radix-ui\/.*\/dist\/index\.mjs/,
/cmdk\/dist\/index\.mjs/
],
},
},
plugins: [tailwindcss()],
Expand Down
1 change: 1 addition & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@radix-ui/react-radio-group": "^1.2.2",
"@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.6",
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './ui';

2 changes: 1 addition & 1 deletion packages/components/src/remix-hook-form/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useRemixFormContext } from 'remix-hook-form';
import {
Checkbox as BaseCheckbox,
CheckboxField as BaseCheckbox,
type CheckboxProps as BaseCheckboxProps,
type CheckboxFieldComponents,
} from '../ui/checkbox-field';
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/remix/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from '../remix-hook-form';

2 changes: 1 addition & 1 deletion packages/components/src/ui/checkbox-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,4 @@ const CheckboxField = ({

CheckboxField.displayName = CheckboxPrimitive.Root.displayName;

export { CheckboxField as Checkbox };
export { CheckboxField };
29 changes: 29 additions & 0 deletions packages/components/src/ui/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as React from 'react';
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
import { CheckIcon } from 'lucide-react';

import { cn } from './utils';

const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
'peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
className={cn('flex items-center justify-center text-current')}
>
<CheckIcon className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
));
Checkbox.displayName = CheckboxPrimitive.Root.displayName;

export { Checkbox };

182 changes: 182 additions & 0 deletions packages/components/src/ui/data-table-filter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# Data Table Filter

This is an enhanced data table filter component for the forms repository, based on the bazza/ui canary branch implementation. It adds client-side filtering with a clean, modern UI inspired by Linear.

## Features

- Complete refactoring with a new API structure
- Internationalization (i18n) support
- Quick Search Filters for option and multiOption columns
- Number Filtering Overhaul with range slider support
- UI improvements and performance enhancements
- Comprehensive filtering capabilities for different data types

## API

The new API structure requires different props:

```tsx
<DataTableFilter
columns={columns}
filters={filters}
actions={{
onFiltersChange: setFilters
}}
locale="en"
strategy="tanstack-table"
/>
```

### Props

- `columns`: The columns to filter on.
- `filters`: The current filter state.
- `actions`: Actions to perform when filters change.
- `onFiltersChange`: Callback function to update the filter state.
- `locale`: The locale to use for translations (default: 'en').
- `strategy`: The strategy to use for filtering (default: 'tanstack-table').
- `table`: Optional table instance for backward compatibility.

## Column Configuration

To make a column filterable, you need to:

1. Use the `filterFn` function for filtering.
2. Add the `meta` property using the `defineMeta` helper function.

```tsx
import { defineMeta, filterFn } from '@lambda-curry/components/ui/data-table-filter';

const columns: ColumnDef<YourDataType>[] = [
{
accessorKey: 'status',
header: 'Status',
filterFn: filterFn('option'),
meta: defineMeta('status', {
displayName: 'Status',
type: 'option',
icon: CircleDotDashedIcon,
options: STATUS_OPTIONS,
}),
},
// ... other columns
];
```

### Column Meta Properties

- `displayName`: The display name for the column.
- `icon`: The icon for the column.
- `type`: The data type of the column (text, number, date, option, multiOption).
- `options`: An optional list of options for option and multiOption columns.
- `transformOptionFn`: An optional function to transform column values into options.
- `max`: An optional "soft" maximum value for the range slider when filtering on a number column.

## Supported Column Types

- `text`: Text data with operators like contains, starts with, ends with, etc.
- `number`: Numerical data with operators like equals, greater than, less than, between, etc.
- `date`: Dates with operators like equals, greater than, less than, between, etc.
- `option`: Single-valued option (e.g., status) with operators like equals, not equals, etc.
- `multiOption`: Multi-valued option (e.g., labels) with operators like is any of, is none of, etc.

## Internationalization

The component supports internationalization through the `locale` prop. Currently supported locales:

- English (en)
- Spanish (es)
- French (fr)
- German (de)

You can add more locales by extending the translations object in the `i18n.ts` file.

## Usage Example

```tsx
import * as React from 'react';
import { type ColumnDef } from '@tanstack/react-table';
import {
DataTableFilter,
defineMeta,
filterFn,
type DataTableFilterState,
} from '@lambda-curry/components/ui/data-table-filter';

// Define your columns with proper metadata
const columns: ColumnDef<YourDataType>[] = [
// ... your columns
];

export default function YourComponent() {
const [filters, setFilters] = React.useState<DataTableFilterState>([]);

// Create table instance
const table = useReactTable({
// ... your table configuration
state: {
columnFilters: filters.map((filter) => ({
id: filter.id,
value: filter.value.values,
})),
},
filterFns: {
text: filterFn('text'),
number: filterFn('number'),
date: filterFn('date'),
option: filterFn('option'),
multiOption: filterFn('multiOption'),
},
});

return (
<div>
<DataTableFilter
columns={columns}
filters={filters}
actions={{
onFiltersChange: setFilters,
}}
locale="en"
strategy="tanstack-table"
/>

{/* Your table component */}
</div>
);
}
```

## Migration Guide

If you're migrating from the old DataTableFacetedFilter component, you'll need to:

1. Update your column definitions to include the `meta` property using the `defineMeta` helper.
2. Use the `filterFn` function for filtering.
3. Manage filter state with useState or a state management library.
4. Replace the old DataTableFacetedFilter component with the new DataTableFilter component.

### Old API

```tsx
<DataTableFacetedFilter
column={tableColumn}
title={column.title}
options={column.options}
/>
```

### New API

```tsx
<DataTableFilter
columns={columns}
filters={filters}
actions={{
onFiltersChange: setFilters
}}
locale="en"
strategy="tanstack-table"
/>
```

Loading