@lindaui/blocks 0.1.0 → 0.3.0
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.
- package/dist/table-pane.d.ts +44 -0
- package/dist/table-pane.d.ts.map +1 -0
- package/dist/table-pane.js +137 -0
- package/llms.txt +11 -0
- package/package.json +24 -6
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type TableColumnDef } from "@lindaui/ui/table";
|
|
3
|
+
export interface TablePaneFilter {
|
|
4
|
+
key: string;
|
|
5
|
+
label: string;
|
|
6
|
+
count?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Tabular sibling of `list-pane`: the same controlled chrome (filter tabs +
|
|
10
|
+
* search + loading/error/empty states) wrapped around `@lindaui/ui/table`,
|
|
11
|
+
* plus a `toolbarExtra` slot and an optional footer. Zero data-fetching — the
|
|
12
|
+
* app passes already-filtered `rows` (each cell is a `ReactNode`, so domain
|
|
13
|
+
* chips/avatars live in the consumer) and owns all state.
|
|
14
|
+
*/
|
|
15
|
+
export interface TablePaneProps {
|
|
16
|
+
columns: TableColumnDef[];
|
|
17
|
+
rows: Record<string, ReactNode>[];
|
|
18
|
+
ariaLabel?: string;
|
|
19
|
+
filters?: TablePaneFilter[];
|
|
20
|
+
activeFilter?: string;
|
|
21
|
+
onFilterChange?: (key: string) => void;
|
|
22
|
+
searchValue?: string;
|
|
23
|
+
onSearchChange?: (value: string) => void;
|
|
24
|
+
searchPlaceholder?: string;
|
|
25
|
+
searchLabel?: string;
|
|
26
|
+
toolbarExtra?: ReactNode;
|
|
27
|
+
loading?: boolean;
|
|
28
|
+
error?: string | null;
|
|
29
|
+
onRetry?: () => void;
|
|
30
|
+
errorTitle?: string;
|
|
31
|
+
retryLabel?: string;
|
|
32
|
+
isEmpty?: boolean;
|
|
33
|
+
emptyTitle?: string;
|
|
34
|
+
emptyDescription?: ReactNode;
|
|
35
|
+
emptyIcon?: ReactNode;
|
|
36
|
+
footer?: ReactNode;
|
|
37
|
+
footerCount?: {
|
|
38
|
+
shown: number;
|
|
39
|
+
total: number;
|
|
40
|
+
label?: string;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export declare function TablePane({ columns, rows, ariaLabel, filters, activeFilter, onFilterChange, searchValue, onSearchChange, searchPlaceholder, searchLabel, toolbarExtra, loading, error, onRetry, errorTitle, retryLabel, isEmpty, emptyTitle, emptyDescription, emptyIcon, footer, footerCount, }: TablePaneProps): import("react").JSX.Element;
|
|
44
|
+
//# sourceMappingURL=table-pane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-pane.d.ts","sourceRoot":"","sources":["../src/table-pane.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,OAAO,EAAS,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE/D,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAE7B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAGvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,YAAY,CAAC,EAAE,SAAS,CAAC;IAGzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC;IAGtB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAChE;AAED,wBAAgB,SAAS,CAAC,EACxB,OAAO,EACP,IAAI,EACJ,SAAmB,EACnB,OAAO,EACP,YAAY,EACZ,cAAc,EACd,WAAgB,EAChB,cAAc,EACd,iBAA4B,EAC5B,WAAsB,EACtB,YAAY,EACZ,OAAe,EACf,KAAY,EACZ,OAAO,EACP,UAAgC,EAChC,UAAyB,EACzB,OAAO,EACP,UAA6B,EAC7B,gBAAgB,EAChB,SAAS,EACT,MAAM,EACN,WAAW,GACZ,EAAE,cAAc,+BAiHhB"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { AlertCircle, RotateCcw, FileText } from 'lucide-react';
|
|
4
|
+
import { Button } from '@lindaui/ui/button';
|
|
5
|
+
import { SearchField } from '@lindaui/ui/search-field';
|
|
6
|
+
import { Skeleton } from '@lindaui/ui/skeleton';
|
|
7
|
+
import { Table } from '@lindaui/ui/table';
|
|
8
|
+
|
|
9
|
+
function TablePane({ columns, rows, ariaLabel = "Tabla", filters, activeFilter, onFilterChange, searchValue = "", onSearchChange, searchPlaceholder = "Buscar", searchLabel = "Buscar", toolbarExtra, loading = false, error = null, onRetry, errorTitle = "No se pudo cargar", retryLabel = "Reintentar", isEmpty, emptyTitle = "Sin resultados", emptyDescription, emptyIcon, footer, footerCount }) {
|
|
10
|
+
const showEmpty = !loading && !error && (isEmpty !== undefined ? isEmpty : rows.length === 0);
|
|
11
|
+
const showFooter = !loading && !error && (footer != null || footerCount != null);
|
|
12
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
13
|
+
className: "flex flex-col overflow-hidden rounded-xl border border-border bg-card",
|
|
14
|
+
children: [
|
|
15
|
+
filters && filters.length > 0 && /*#__PURE__*/ jsx("div", {
|
|
16
|
+
className: "flex flex-wrap gap-2 overflow-x-auto border-b border-border p-3",
|
|
17
|
+
children: filters.map((f)=>/*#__PURE__*/ jsxs(Button, {
|
|
18
|
+
variant: activeFilter === f.key ? "primary" : "ghost",
|
|
19
|
+
onPress: ()=>onFilterChange?.(f.key),
|
|
20
|
+
size: "sm",
|
|
21
|
+
children: [
|
|
22
|
+
f.label,
|
|
23
|
+
f.count != null && /*#__PURE__*/ jsx("span", {
|
|
24
|
+
"aria-hidden": true,
|
|
25
|
+
className: "ml-1.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-muted-foreground/15 px-1 text-[11px] font-semibold tabular-nums text-muted-foreground",
|
|
26
|
+
children: f.count
|
|
27
|
+
})
|
|
28
|
+
]
|
|
29
|
+
}, f.key))
|
|
30
|
+
}),
|
|
31
|
+
(onSearchChange || toolbarExtra) && /*#__PURE__*/ jsxs("div", {
|
|
32
|
+
className: "flex flex-wrap items-center gap-3 border-b border-border p-4",
|
|
33
|
+
children: [
|
|
34
|
+
onSearchChange && /*#__PURE__*/ jsx("div", {
|
|
35
|
+
className: "min-w-[240px] flex-1",
|
|
36
|
+
children: /*#__PURE__*/ jsx(SearchField, {
|
|
37
|
+
value: searchValue,
|
|
38
|
+
onChange: onSearchChange,
|
|
39
|
+
placeholder: searchPlaceholder,
|
|
40
|
+
"aria-label": searchLabel
|
|
41
|
+
})
|
|
42
|
+
}),
|
|
43
|
+
toolbarExtra
|
|
44
|
+
]
|
|
45
|
+
}),
|
|
46
|
+
/*#__PURE__*/ jsxs("div", {
|
|
47
|
+
className: "min-h-0 flex-1 overflow-x-auto",
|
|
48
|
+
children: [
|
|
49
|
+
loading && /*#__PURE__*/ jsx("div", {
|
|
50
|
+
role: "status",
|
|
51
|
+
"aria-label": "Cargando",
|
|
52
|
+
className: "space-y-2 p-3",
|
|
53
|
+
children: Array.from({
|
|
54
|
+
length: 6
|
|
55
|
+
}).map((_, i)=>/*#__PURE__*/ jsx(Skeleton, {
|
|
56
|
+
className: "h-12 w-full"
|
|
57
|
+
}, i))
|
|
58
|
+
}),
|
|
59
|
+
!loading && error && /*#__PURE__*/ jsxs("div", {
|
|
60
|
+
className: "m-3 rounded-xl border border-destructive/35 bg-destructive/5 p-6 text-center",
|
|
61
|
+
children: [
|
|
62
|
+
/*#__PURE__*/ jsx(AlertCircle, {
|
|
63
|
+
className: "mx-auto h-6 w-6 text-destructive"
|
|
64
|
+
}),
|
|
65
|
+
/*#__PURE__*/ jsx("p", {
|
|
66
|
+
className: "mt-2 text-sm font-semibold",
|
|
67
|
+
children: errorTitle
|
|
68
|
+
}),
|
|
69
|
+
/*#__PURE__*/ jsx("p", {
|
|
70
|
+
className: "mt-1 text-xs text-muted-foreground",
|
|
71
|
+
children: error
|
|
72
|
+
}),
|
|
73
|
+
onRetry && /*#__PURE__*/ jsxs(Button, {
|
|
74
|
+
variant: "secondary",
|
|
75
|
+
size: "sm",
|
|
76
|
+
onPress: onRetry,
|
|
77
|
+
className: "mt-3",
|
|
78
|
+
children: [
|
|
79
|
+
/*#__PURE__*/ jsx(RotateCcw, {
|
|
80
|
+
className: "h-3.5 w-3.5"
|
|
81
|
+
}),
|
|
82
|
+
retryLabel
|
|
83
|
+
]
|
|
84
|
+
})
|
|
85
|
+
]
|
|
86
|
+
}),
|
|
87
|
+
showEmpty && /*#__PURE__*/ jsxs("div", {
|
|
88
|
+
className: "py-16 text-center",
|
|
89
|
+
children: [
|
|
90
|
+
/*#__PURE__*/ jsx("div", {
|
|
91
|
+
className: "mx-auto grid h-11 w-11 place-items-center rounded-xl bg-secondary",
|
|
92
|
+
children: emptyIcon ?? /*#__PURE__*/ jsx(FileText, {
|
|
93
|
+
className: "h-5 w-5 text-muted-foreground"
|
|
94
|
+
})
|
|
95
|
+
}),
|
|
96
|
+
/*#__PURE__*/ jsx("p", {
|
|
97
|
+
className: "mt-3 text-sm font-semibold",
|
|
98
|
+
children: emptyTitle
|
|
99
|
+
}),
|
|
100
|
+
emptyDescription && /*#__PURE__*/ jsx("div", {
|
|
101
|
+
className: "mt-1 text-xs text-muted-foreground",
|
|
102
|
+
children: emptyDescription
|
|
103
|
+
})
|
|
104
|
+
]
|
|
105
|
+
}),
|
|
106
|
+
!loading && !error && !showEmpty && /*#__PURE__*/ jsx(Table, {
|
|
107
|
+
"aria-label": ariaLabel,
|
|
108
|
+
columns: columns,
|
|
109
|
+
rows: rows
|
|
110
|
+
})
|
|
111
|
+
]
|
|
112
|
+
}),
|
|
113
|
+
showFooter && (footer ?? /*#__PURE__*/ jsx("div", {
|
|
114
|
+
className: "flex items-center justify-between border-t border-border px-5 py-3 text-xs text-muted-foreground",
|
|
115
|
+
children: /*#__PURE__*/ jsxs("span", {
|
|
116
|
+
children: [
|
|
117
|
+
footerCount.label ?? "Mostrando",
|
|
118
|
+
" ",
|
|
119
|
+
/*#__PURE__*/ jsx("span", {
|
|
120
|
+
className: "font-medium text-foreground",
|
|
121
|
+
children: footerCount.shown
|
|
122
|
+
}),
|
|
123
|
+
" ",
|
|
124
|
+
"de",
|
|
125
|
+
" ",
|
|
126
|
+
/*#__PURE__*/ jsx("span", {
|
|
127
|
+
className: "font-medium text-foreground",
|
|
128
|
+
children: footerCount.total
|
|
129
|
+
})
|
|
130
|
+
]
|
|
131
|
+
})
|
|
132
|
+
}))
|
|
133
|
+
]
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export { TablePane };
|
package/llms.txt
CHANGED
|
@@ -55,6 +55,17 @@ Filterable/searchable list shell with loading/empty/error states. Controlled:
|
|
|
55
55
|
`onSearchChange`, `loading`, `error`, `onRetry`, `isEmpty`, empty/error slots. Items go
|
|
56
56
|
in via `children`. Does NOT filter for you — filter your data (use `@lindaui/ui/search`).
|
|
57
57
|
|
|
58
|
+
### @lindaui/blocks/table-pane
|
|
59
|
+
Tabular sibling of `list-pane`: same controlled chrome (filter tabs + search +
|
|
60
|
+
loading/error/empty) around `@lindaui/ui/table`. Props: `columns: {key,label}[]`,
|
|
61
|
+
`rows: Record<string,ReactNode>[]` (each cell a ReactNode → domain chips/avatars live
|
|
62
|
+
in your app), `ariaLabel?`, the same `filters`/`activeFilter`/`onFilterChange` +
|
|
63
|
+
`searchValue`/`onSearchChange`/`searchLabel` + `loading`/`error`/`onRetry`/`isEmpty` +
|
|
64
|
+
empty slots as list-pane, plus `toolbarExtra?` (extra filter controls right of the
|
|
65
|
+
search, e.g. a `<Select>`), `footer?` (escape hatch) and `footerCount?: {shown,total,
|
|
66
|
+
label?}` (default "Mostrando X de Y"). Does NOT filter/fetch — pass already-filtered
|
|
67
|
+
`rows` and own the state.
|
|
68
|
+
|
|
58
69
|
### @lindaui/blocks/detail-panel
|
|
59
70
|
Master-detail right side: empty state + header + meta card + CTA. Controlled.
|
|
60
71
|
|
package/package.json
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lindaui/blocks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Composed UI sections (auth, lists, master-detail, charts) built on @lindaui/ui — importable npm package, not a copy-paste registry.",
|
|
5
5
|
"author": "Franco Carballar <francocarballar@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/francocarballar/lindaui#readme",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/francocarballar/lindaui/issues"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"react",
|
|
13
|
+
"react-components",
|
|
14
|
+
"design-system",
|
|
15
|
+
"blocks",
|
|
16
|
+
"sections",
|
|
17
|
+
"charts",
|
|
18
|
+
"heroui"
|
|
19
|
+
],
|
|
7
20
|
"repository": {
|
|
8
21
|
"type": "git",
|
|
9
22
|
"url": "git+https://github.com/francocarballar/lindaui.git",
|
|
@@ -106,6 +119,10 @@
|
|
|
106
119
|
"./workspace-back-button": {
|
|
107
120
|
"types": "./dist/workspace-back-button.d.ts",
|
|
108
121
|
"import": "./dist/workspace-back-button.js"
|
|
122
|
+
},
|
|
123
|
+
"./table-pane": {
|
|
124
|
+
"types": "./dist/table-pane.d.ts",
|
|
125
|
+
"import": "./dist/table-pane.js"
|
|
109
126
|
}
|
|
110
127
|
},
|
|
111
128
|
"files": [
|
|
@@ -120,12 +137,12 @@
|
|
|
120
137
|
"react-hook-form": ">=7",
|
|
121
138
|
"lucide-react": ">=0.4",
|
|
122
139
|
"recharts": ">=3",
|
|
123
|
-
"@lindaui/ui": "
|
|
124
|
-
"@lindaui/tokens": "
|
|
140
|
+
"@lindaui/ui": ">=0.2.0",
|
|
141
|
+
"@lindaui/tokens": ">=0.2.0"
|
|
125
142
|
},
|
|
126
143
|
"devDependencies": {
|
|
127
144
|
"bunchee": "^6.0.0",
|
|
128
|
-
"vitest": "^2.
|
|
145
|
+
"vitest": "^3.2.6",
|
|
129
146
|
"vite": "^6.4.3",
|
|
130
147
|
"@vitejs/plugin-react": "^4.3.0",
|
|
131
148
|
"@testing-library/react": "^16.0.0",
|
|
@@ -142,12 +159,13 @@
|
|
|
142
159
|
"zod": "^3.23.0",
|
|
143
160
|
"@hookform/resolvers": "^3.9.0",
|
|
144
161
|
"typescript": "^6.0.3",
|
|
145
|
-
"@lindaui/ui": "0.
|
|
146
|
-
"@lindaui/tokens": "0.
|
|
162
|
+
"@lindaui/ui": "0.3.0",
|
|
163
|
+
"@lindaui/tokens": "0.3.0"
|
|
147
164
|
},
|
|
148
165
|
"scripts": {
|
|
149
166
|
"build": "bunchee --no-dts && tsc -p tsconfig.build.json",
|
|
150
167
|
"dev": "bunchee --watch",
|
|
168
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
151
169
|
"test": "vitest run",
|
|
152
170
|
"test:watch": "vitest"
|
|
153
171
|
}
|