@fastnd/components 1.0.31 → 1.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/components/QuickAccessCard/QuickAccessCard.d.ts +12 -0
  2. package/dist/components/ScoreBar/ScoreBar.d.ts +8 -0
  3. package/dist/components/index.d.ts +2 -0
  4. package/dist/examples/dashboard/StatusDonutChart/StatusDonutChart.tsx +41 -56
  5. package/dist/examples/data-visualization/CardCarouselPanel/CardCarouselPanel.tsx +171 -0
  6. package/dist/examples/data-visualization/CellRenderers/CellRenderers.tsx +175 -0
  7. package/dist/examples/data-visualization/ColumnConfigPopover/ColumnConfigPopover.tsx +124 -0
  8. package/dist/examples/data-visualization/DataCardView/DataCardView.tsx +223 -0
  9. package/dist/examples/data-visualization/DataExplorerPagination/DataExplorerPagination.tsx +143 -0
  10. package/dist/examples/data-visualization/DataExplorerToolbar/DataExplorerToolbar.tsx +88 -0
  11. package/dist/examples/data-visualization/DataListView/DataListView.tsx +246 -0
  12. package/dist/examples/data-visualization/DataTableView/DataTableView.tsx +449 -0
  13. package/dist/examples/data-visualization/DataVisualizationPage/DataVisualizationPage.tsx +140 -0
  14. package/dist/examples/data-visualization/FilterChipGroup/FilterChipGroup.tsx +125 -0
  15. package/dist/examples/data-visualization/MoreFiltersPopover/MoreFiltersPopover.tsx +132 -0
  16. package/dist/examples/data-visualization/constants.ts +587 -0
  17. package/dist/examples/data-visualization/hooks/use-column-config.ts +76 -0
  18. package/dist/examples/data-visualization/hooks/use-data-explorer-state.ts +313 -0
  19. package/dist/examples/data-visualization/index.ts +1 -0
  20. package/dist/examples/data-visualization/types.ts +99 -0
  21. package/dist/examples/quickaccess/QuickAccess/QuickAccess.tsx +97 -0
  22. package/dist/examples/quickaccess/index.ts +2 -0
  23. package/dist/examples/quickaccess/types.ts +11 -0
  24. package/dist/fastnd-components.js +5708 -5590
  25. package/package.json +1 -1
@@ -0,0 +1,125 @@
1
+ import React, { useState } from 'react'
2
+ import { ChevronDown, X } from 'lucide-react'
3
+ import { Button } from '@/components/ui/button'
4
+ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
5
+ import { Checkbox } from '@/components/ui/checkbox'
6
+ import { ScrollArea } from '@/components/ui/scroll-area'
7
+ import { Input } from '@/components/ui/input'
8
+ import { cn } from '@/lib/utils'
9
+ import type { ColumnDef, FilterState } from '../types'
10
+
11
+ export interface FilterChipGroupProps {
12
+ columns: Record<string, ColumnDef>
13
+ filters: FilterState
14
+ onToggleFilter: (column: string, value: string) => void
15
+ onClearFilter: (column: string) => void
16
+ getFilterOptions: (colKey: string) => string[]
17
+ className?: string
18
+ }
19
+
20
+ interface FilterChipProps {
21
+ colKey: string
22
+ col: ColumnDef
23
+ selected: string[]
24
+ onToggleFilter: (column: string, value: string) => void
25
+ onClearFilter: (column: string) => void
26
+ getFilterOptions: (colKey: string) => string[]
27
+ }
28
+
29
+ function FilterChip({
30
+ colKey,
31
+ col,
32
+ selected,
33
+ onToggleFilter,
34
+ onClearFilter,
35
+ getFilterOptions,
36
+ }: FilterChipProps) {
37
+ const [search, setSearch] = useState('')
38
+ const isActive = selected.length > 0
39
+ const options = getFilterOptions(colKey)
40
+ const filteredOptions = search
41
+ ? options.filter((opt) => opt.toLowerCase().includes(search.toLowerCase()))
42
+ : options
43
+
44
+ return (
45
+ <Popover>
46
+ <PopoverTrigger asChild>
47
+ <Button
48
+ variant="outline"
49
+ size="sm"
50
+ className={cn(isActive && 'border-primary text-primary bg-primary/5')}
51
+ >
52
+ {col.label}
53
+ {isActive && ` (${selected.length})`}
54
+ {isActive ? (
55
+ <X
56
+ className="ml-1 size-3.5"
57
+ aria-label="Filter entfernen"
58
+ onClick={(e) => {
59
+ e.stopPropagation()
60
+ onClearFilter(colKey)
61
+ }}
62
+ />
63
+ ) : (
64
+ <ChevronDown className="ml-1 size-3.5" />
65
+ )}
66
+ </Button>
67
+ </PopoverTrigger>
68
+ <PopoverContent className="w-60 p-0" align="start">
69
+ <div className="p-2 border-b border-border">
70
+ <Input
71
+ placeholder="Suchen..."
72
+ value={search}
73
+ onChange={(e) => setSearch(e.target.value)}
74
+ className="h-8"
75
+ />
76
+ </div>
77
+ <ScrollArea className="max-h-[300px]">
78
+ <div className="p-1" role="listbox" aria-label={`${col.label} Filter`}>
79
+ {filteredOptions.map((opt) => (
80
+ <div
81
+ key={opt}
82
+ role="option"
83
+ aria-selected={selected.includes(opt)}
84
+ className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-accent cursor-pointer"
85
+ onClick={() => onToggleFilter(colKey, opt)}
86
+ >
87
+ <Checkbox checked={selected.includes(opt)} tabIndex={-1} />
88
+ <span className="text-sm">{opt}</span>
89
+ </div>
90
+ ))}
91
+ </div>
92
+ </ScrollArea>
93
+ </PopoverContent>
94
+ </Popover>
95
+ )
96
+ }
97
+
98
+ export function FilterChipGroup({
99
+ columns,
100
+ filters,
101
+ onToggleFilter,
102
+ onClearFilter,
103
+ getFilterOptions,
104
+ className,
105
+ }: FilterChipGroupProps) {
106
+ const primaryFilterEntries = Object.entries(columns).filter(
107
+ ([, col]) => col.filterable && col.primaryFilter,
108
+ )
109
+
110
+ return (
111
+ <div className={cn('flex flex-wrap items-center gap-2', className)}>
112
+ {primaryFilterEntries.map(([colKey, col]) => (
113
+ <FilterChip
114
+ key={colKey}
115
+ colKey={colKey}
116
+ col={col}
117
+ selected={filters[colKey] ?? []}
118
+ onToggleFilter={onToggleFilter}
119
+ onClearFilter={onClearFilter}
120
+ getFilterOptions={getFilterOptions}
121
+ />
122
+ ))}
123
+ </div>
124
+ )
125
+ }
@@ -0,0 +1,132 @@
1
+ import React, { useState } from 'react'
2
+ import { Filter, ChevronDown } from 'lucide-react'
3
+ import { Button } from '@/components/ui/button'
4
+ import { Badge } from '@/components/ui/badge'
5
+ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
6
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
7
+ import { Checkbox } from '@/components/ui/checkbox'
8
+ import { ScrollArea } from '@/components/ui/scroll-area'
9
+ import { Input } from '@/components/ui/input'
10
+ import { cn } from '@/lib/utils'
11
+ import type { ColumnDef, FilterState } from '../types'
12
+
13
+ export interface MoreFiltersPopoverProps {
14
+ columns: Record<string, ColumnDef>
15
+ filters: FilterState
16
+ onToggleFilter: (column: string, value: string) => void
17
+ onClearSecondaryFilters: () => void
18
+ getFilterOptions: (colKey: string) => string[]
19
+ className?: string
20
+ }
21
+
22
+ interface SectionProps {
23
+ colKey: string
24
+ col: ColumnDef
25
+ selected: string[]
26
+ onToggleFilter: (column: string, value: string) => void
27
+ getFilterOptions: (colKey: string) => string[]
28
+ }
29
+
30
+ function FilterSection({ colKey, col, selected, onToggleFilter, getFilterOptions }: SectionProps) {
31
+ const [search, setSearch] = useState('')
32
+ const options = getFilterOptions(colKey)
33
+ const filteredOptions = search
34
+ ? options.filter((opt) => opt.toLowerCase().includes(search.toLowerCase()))
35
+ : options
36
+ const count = selected.length
37
+
38
+ return (
39
+ <Collapsible>
40
+ <CollapsibleTrigger className="flex items-center justify-between w-full px-3 py-2 hover:bg-accent text-left">
41
+ <span className="text-sm">{col.label}</span>
42
+ <div className="flex items-center gap-1.5">
43
+ {count > 0 && <Badge variant="secondary">{count}</Badge>}
44
+ <ChevronDown className="size-4 transition-transform data-[state=open]:rotate-180" />
45
+ </div>
46
+ </CollapsibleTrigger>
47
+ <CollapsibleContent>
48
+ <div className="px-2 pb-1.5 pt-0">
49
+ <div className="px-1 pb-1.5">
50
+ <Input
51
+ placeholder="Suchen..."
52
+ value={search}
53
+ onChange={(e) => setSearch(e.target.value)}
54
+ className="h-8"
55
+ />
56
+ </div>
57
+ <div role="listbox" aria-label={`${col.label} Filter`}>
58
+ {filteredOptions.map((opt) => (
59
+ <div
60
+ key={opt}
61
+ role="option"
62
+ aria-selected={selected.includes(opt)}
63
+ className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-accent cursor-pointer"
64
+ onClick={() => onToggleFilter(colKey, opt)}
65
+ >
66
+ <Checkbox checked={selected.includes(opt)} tabIndex={-1} />
67
+ <span className="text-sm">{opt}</span>
68
+ </div>
69
+ ))}
70
+ </div>
71
+ </div>
72
+ </CollapsibleContent>
73
+ </Collapsible>
74
+ )
75
+ }
76
+
77
+ export function MoreFiltersPopover({
78
+ columns,
79
+ filters,
80
+ onToggleFilter,
81
+ onClearSecondaryFilters,
82
+ getFilterOptions,
83
+ className,
84
+ }: MoreFiltersPopoverProps) {
85
+ const secondaryCols = Object.entries(columns).filter(
86
+ ([, col]) => col.filterable && !col.primaryFilter,
87
+ )
88
+ const secondaryCount = secondaryCols.reduce((acc, [k]) => acc + (filters[k]?.length || 0), 0)
89
+ const isActive = secondaryCount > 0
90
+
91
+ return (
92
+ <Popover>
93
+ <PopoverTrigger asChild>
94
+ <Button
95
+ variant={isActive ? 'default' : 'outline'}
96
+ size="sm"
97
+ className={cn(className)}
98
+ >
99
+ <Filter className="size-4" />
100
+ Weitere Filter
101
+ {isActive && (
102
+ <Badge variant="secondary" className="ml-1.5">
103
+ {secondaryCount}
104
+ </Badge>
105
+ )}
106
+ </Button>
107
+ </PopoverTrigger>
108
+ <PopoverContent className="w-80 p-0" align="start">
109
+ <div className="flex items-center justify-between p-3 border-b border-border">
110
+ <span className="text-sm font-semibold">Weitere Filter</span>
111
+ {isActive && (
112
+ <Button variant="ghost" size="sm" onClick={onClearSecondaryFilters}>
113
+ Alle zurücksetzen
114
+ </Button>
115
+ )}
116
+ </div>
117
+ <ScrollArea className="max-h-[400px]">
118
+ {secondaryCols.map(([colKey, col]) => (
119
+ <FilterSection
120
+ key={colKey}
121
+ colKey={colKey}
122
+ col={col}
123
+ selected={filters[colKey] ?? []}
124
+ onToggleFilter={onToggleFilter}
125
+ getFilterOptions={getFilterOptions}
126
+ />
127
+ ))}
128
+ </ScrollArea>
129
+ </PopoverContent>
130
+ </Popover>
131
+ )
132
+ }