@carefully-built/crud 0.1.2 → 0.1.4
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/index.d.mts +10 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +134 -143
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionHandlers, ActionType, Column, FilterConfig, ResponsiveSheetClassNames, SortState } from "@carefully-built/ui";
|
|
1
|
+
import { ActionHandlers, ActionType, Column, FilterConfig, ResponsiveSheetClassNames, SheetOutsideInteractionGuard, SortState, TableToolbarLabelsInput } from "@carefully-built/ui";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
|
|
4
4
|
//#region src/pagination.d.ts
|
|
@@ -18,6 +18,7 @@ interface CrudFilterDefinition<TItem> {
|
|
|
18
18
|
readonly key: Extract<keyof TItem, string>;
|
|
19
19
|
readonly config: FilterConfig;
|
|
20
20
|
readonly allowAll?: boolean;
|
|
21
|
+
readonly allOptionLabel?: string;
|
|
21
22
|
readonly clearable?: boolean;
|
|
22
23
|
}
|
|
23
24
|
interface UseCrudTableStateOptions<TItem> {
|
|
@@ -50,8 +51,10 @@ interface CrudTableViewProps<TItem> {
|
|
|
50
51
|
readonly columns: readonly Column<TItem>[];
|
|
51
52
|
readonly isLoading: boolean;
|
|
52
53
|
readonly searchPlaceholder?: string;
|
|
54
|
+
readonly toolbarLabels?: TableToolbarLabelsInput;
|
|
53
55
|
readonly filters?: readonly CrudFilterDefinition<TItem>[];
|
|
54
56
|
readonly actions?: readonly ActionType[];
|
|
57
|
+
readonly actionLabels?: Partial<Record<ActionType, string>>;
|
|
55
58
|
readonly actionHandlers?: ActionHandlers<TItem>;
|
|
56
59
|
readonly renderActions?: (item: TItem) => ReactNode;
|
|
57
60
|
readonly noDataMessage?: string;
|
|
@@ -69,6 +72,7 @@ interface CrudDataTableProps<TItem> {
|
|
|
69
72
|
readonly columns: readonly Column<TItem>[];
|
|
70
73
|
readonly isLoading: boolean;
|
|
71
74
|
readonly actions?: readonly ActionType[];
|
|
75
|
+
readonly actionLabels?: Partial<Record<ActionType, string>>;
|
|
72
76
|
readonly actionHandlers?: ActionHandlers<TItem>;
|
|
73
77
|
readonly renderActions?: (item: TItem) => ReactNode;
|
|
74
78
|
readonly noDataMessage?: string;
|
|
@@ -87,6 +91,7 @@ interface CrudDataTableProps<TItem> {
|
|
|
87
91
|
//#region src/crud-data-table.d.ts
|
|
88
92
|
declare function CrudDataTable<TItem extends object>({
|
|
89
93
|
actions,
|
|
94
|
+
actionLabels,
|
|
90
95
|
actionHandlers,
|
|
91
96
|
columns,
|
|
92
97
|
data,
|
|
@@ -125,6 +130,7 @@ interface CrudResourceSheetProps {
|
|
|
125
130
|
readonly confirmLoading?: boolean;
|
|
126
131
|
readonly confirmCloseWhenDirty?: boolean;
|
|
127
132
|
readonly width?: number;
|
|
133
|
+
readonly outsideInteractionGuard?: SheetOutsideInteractionGuard;
|
|
128
134
|
readonly className?: string;
|
|
129
135
|
readonly contentClassName?: string;
|
|
130
136
|
readonly footerClassName?: string;
|
|
@@ -135,6 +141,7 @@ declare function CrudResourceSheet({
|
|
|
135
141
|
formId,
|
|
136
142
|
onConfirm,
|
|
137
143
|
confirmCloseWhenDirty: _confirmCloseWhenDirty,
|
|
144
|
+
outsideInteractionGuard,
|
|
138
145
|
...sheetProps
|
|
139
146
|
}: CrudResourceSheetProps): React.ReactElement;
|
|
140
147
|
//#endregion
|
|
@@ -144,8 +151,10 @@ declare function CrudTableView<TItem extends object>({
|
|
|
144
151
|
columns,
|
|
145
152
|
isLoading,
|
|
146
153
|
searchPlaceholder,
|
|
154
|
+
toolbarLabels,
|
|
147
155
|
filters,
|
|
148
156
|
actions,
|
|
157
|
+
actionLabels,
|
|
149
158
|
actionHandlers,
|
|
150
159
|
renderActions,
|
|
151
160
|
noDataMessage,
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/pagination.ts","../src/types.ts","../src/crud-data-table.tsx","../src/crud-list-table.tsx","../src/crud-resource-sheet.tsx","../src/crud-table-view.tsx","../src/use-crud-table-state.ts","../src/use-url-string-filters.ts","../src/use-url-pagination.ts"],"sourcesContent":[],"mappings":";;;;UAAiB,mBAAA;;;;EAAA,SAAA,QAAA,EAAA,MAAmB;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/pagination.ts","../src/types.ts","../src/crud-data-table.tsx","../src/crud-list-table.tsx","../src/crud-resource-sheet.tsx","../src/crud-table-view.tsx","../src/use-crud-table-state.ts","../src/use-url-string-filters.ts","../src/use-url-pagination.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { ResponsiveSheet, SmartTable, TableToolbar, useTableSorting } from "@carefully-built/ui";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback, useMemo
|
|
3
|
+
import { useCallback, useMemo } from "react";
|
|
4
4
|
import { parseAsInteger, parseAsString, useQueryState, useQueryStates } from "nuqs";
|
|
5
5
|
|
|
6
6
|
//#region src/crud-data-table.tsx
|
|
7
|
-
function CrudDataTable({ actions, actionHandlers, columns, data, fullHeight = true, getRowKey, isLoading, maxHeight, noDataContent, noDataMessage, onRowClick, pagination, renderActions, renderMobileCard, sortState, stickyHeader = true, onSortChange }) {
|
|
7
|
+
function CrudDataTable({ actions, actionLabels, actionHandlers, columns, data, fullHeight = true, getRowKey, isLoading, maxHeight, noDataContent, noDataMessage, onRowClick, pagination, renderActions, renderMobileCard, sortState, stickyHeader = true, onSortChange }) {
|
|
8
8
|
return /* @__PURE__ */ jsx(SmartTable, {
|
|
9
9
|
data: [...data],
|
|
10
10
|
columns: [...columns],
|
|
11
11
|
isLoading,
|
|
12
12
|
actions: actions ? [...actions] : void 0,
|
|
13
|
+
actionLabels,
|
|
13
14
|
actionHandlers,
|
|
14
15
|
renderActions,
|
|
15
16
|
noDataMessage,
|
|
@@ -34,7 +35,7 @@ function CrudListTable(props) {
|
|
|
34
35
|
|
|
35
36
|
//#endregion
|
|
36
37
|
//#region src/crud-resource-sheet.tsx
|
|
37
|
-
function CrudResourceSheet({ children, formId, onConfirm, confirmCloseWhenDirty: _confirmCloseWhenDirty, ...sheetProps }) {
|
|
38
|
+
function CrudResourceSheet({ children, formId, onConfirm, confirmCloseWhenDirty: _confirmCloseWhenDirty, outsideInteractionGuard, ...sheetProps }) {
|
|
38
39
|
const submitForm = () => {
|
|
39
40
|
const form = formId ? document.getElementById(formId) : null;
|
|
40
41
|
if (form instanceof HTMLFormElement) {
|
|
@@ -45,6 +46,7 @@ function CrudResourceSheet({ children, formId, onConfirm, confirmCloseWhenDirty:
|
|
|
45
46
|
};
|
|
46
47
|
return /* @__PURE__ */ jsx(ResponsiveSheet, {
|
|
47
48
|
...sheetProps,
|
|
49
|
+
outsideInteractionGuard,
|
|
48
50
|
onConfirm: formId || onConfirm ? submitForm : void 0,
|
|
49
51
|
children
|
|
50
52
|
});
|
|
@@ -52,7 +54,7 @@ function CrudResourceSheet({ children, formId, onConfirm, confirmCloseWhenDirty:
|
|
|
52
54
|
|
|
53
55
|
//#endregion
|
|
54
56
|
//#region src/crud-table-view.tsx
|
|
55
|
-
function CrudTableView({ state, columns, isLoading, searchPlaceholder = "Search...", filters = [], actions, actionHandlers, renderActions, noDataMessage, initialEmptyContent, noResultsContent, getRowKey, onRowClick, renderMobileCard, stickyHeader = true, fullHeight = true, maxHeight }) {
|
|
57
|
+
function CrudTableView({ state, columns, isLoading, searchPlaceholder = "Search...", toolbarLabels, filters = [], actions, actionLabels, actionHandlers, renderActions, noDataMessage, initialEmptyContent, noResultsContent, getRowKey, onRowClick, renderMobileCard, stickyHeader = true, fullHeight = true, maxHeight }) {
|
|
56
58
|
const emptyContent = state.emptyState === "no-results" ? noResultsContent : initialEmptyContent;
|
|
57
59
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
58
60
|
className: "shrink-0",
|
|
@@ -69,16 +71,19 @@ function CrudTableView({ state, columns, isLoading, searchPlaceholder = "Search.
|
|
|
69
71
|
state.setFilter(filter.key, value);
|
|
70
72
|
},
|
|
71
73
|
allowAll: filter.allowAll,
|
|
74
|
+
allOptionLabel: filter.allOptionLabel,
|
|
72
75
|
clearable: filter.clearable
|
|
73
76
|
})),
|
|
74
77
|
onClearAll: state.clearAll,
|
|
75
|
-
getDraftResultCount: state.getDraftFilterResultCount
|
|
78
|
+
getDraftResultCount: state.getDraftFilterResultCount,
|
|
79
|
+
labels: toolbarLabels
|
|
76
80
|
})
|
|
77
81
|
}), /* @__PURE__ */ jsx(SmartTable, {
|
|
78
82
|
data: state.paginatedData,
|
|
79
83
|
columns: [...columns],
|
|
80
84
|
isLoading,
|
|
81
85
|
actions: actions ? [...actions] : void 0,
|
|
86
|
+
actionLabels,
|
|
82
87
|
actionHandlers,
|
|
83
88
|
renderActions,
|
|
84
89
|
noDataMessage,
|
|
@@ -110,13 +115,104 @@ function matchesCrudSearch(searchText, query) {
|
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
//#endregion
|
|
113
|
-
//#region src/pagination.ts
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
//#region src/use-url-pagination.ts
|
|
119
|
+
function useUrlPagination({ totalItems, pageSize = 20, pageParam = "page" }) {
|
|
120
|
+
const [page, setPage] = useQueryState(pageParam, parseAsInteger.withDefault(1));
|
|
121
|
+
const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
|
122
|
+
const currentPage = Math.min(Math.max(1, page), totalPages);
|
|
118
123
|
const startIndex = (currentPage - 1) * pageSize;
|
|
119
|
-
|
|
124
|
+
const endIndex = Math.min(startIndex + pageSize, totalItems);
|
|
125
|
+
const hasPrevPage = currentPage > 1;
|
|
126
|
+
const hasNextPage = currentPage < totalPages;
|
|
127
|
+
const goToPage = useCallback((newPage) => {
|
|
128
|
+
const validPage = Math.min(Math.max(1, newPage), totalPages);
|
|
129
|
+
setPage(validPage === 1 ? null : validPage);
|
|
130
|
+
}, [setPage, totalPages]);
|
|
131
|
+
const nextPage = useCallback(() => {
|
|
132
|
+
if (hasNextPage) goToPage(currentPage + 1);
|
|
133
|
+
}, [
|
|
134
|
+
currentPage,
|
|
135
|
+
goToPage,
|
|
136
|
+
hasNextPage
|
|
137
|
+
]);
|
|
138
|
+
const prevPage = useCallback(() => {
|
|
139
|
+
if (hasPrevPage) goToPage(currentPage - 1);
|
|
140
|
+
}, [
|
|
141
|
+
currentPage,
|
|
142
|
+
goToPage,
|
|
143
|
+
hasPrevPage
|
|
144
|
+
]);
|
|
145
|
+
const firstPage = useCallback(() => {
|
|
146
|
+
goToPage(1);
|
|
147
|
+
}, [goToPage]);
|
|
148
|
+
const lastPage = useCallback(() => {
|
|
149
|
+
goToPage(totalPages);
|
|
150
|
+
}, [goToPage, totalPages]);
|
|
151
|
+
const paginate = useCallback((data) => data.slice(startIndex, endIndex), [endIndex, startIndex]);
|
|
152
|
+
return useMemo(() => ({
|
|
153
|
+
currentPage,
|
|
154
|
+
pageSize,
|
|
155
|
+
totalPages,
|
|
156
|
+
totalItems,
|
|
157
|
+
startIndex,
|
|
158
|
+
endIndex,
|
|
159
|
+
hasPrevPage,
|
|
160
|
+
hasNextPage,
|
|
161
|
+
goToPage,
|
|
162
|
+
nextPage,
|
|
163
|
+
prevPage,
|
|
164
|
+
firstPage,
|
|
165
|
+
lastPage,
|
|
166
|
+
paginate,
|
|
167
|
+
onPageChange: goToPage
|
|
168
|
+
}), [
|
|
169
|
+
currentPage,
|
|
170
|
+
endIndex,
|
|
171
|
+
firstPage,
|
|
172
|
+
goToPage,
|
|
173
|
+
hasNextPage,
|
|
174
|
+
hasPrevPage,
|
|
175
|
+
lastPage,
|
|
176
|
+
nextPage,
|
|
177
|
+
pageSize,
|
|
178
|
+
paginate,
|
|
179
|
+
prevPage,
|
|
180
|
+
startIndex,
|
|
181
|
+
totalItems,
|
|
182
|
+
totalPages
|
|
183
|
+
]);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/use-url-string-filters.ts
|
|
188
|
+
function buildParserMap(definitions) {
|
|
189
|
+
return Object.fromEntries(definitions.map((definition) => [definition.param ?? definition.key, parseAsString.withDefault(definition.defaultValue ?? "all")]));
|
|
190
|
+
}
|
|
191
|
+
function normalizeFilterValue(value, definition) {
|
|
192
|
+
if (value === (definition.clearValue ?? definition.defaultValue ?? "all")) return null;
|
|
193
|
+
return value.length > 0 ? value : null;
|
|
194
|
+
}
|
|
195
|
+
function useUrlStringFilters(definitions) {
|
|
196
|
+
const [params, setParams] = useQueryStates(useMemo(() => buildParserMap(definitions), [definitions]));
|
|
197
|
+
const values = useMemo(() => Object.fromEntries(definitions.map((definition) => {
|
|
198
|
+
const param = definition.param ?? definition.key;
|
|
199
|
+
return [definition.key, params[param] ?? definition.defaultValue ?? "all"];
|
|
200
|
+
})), [definitions, params]);
|
|
201
|
+
return {
|
|
202
|
+
values,
|
|
203
|
+
setValue: useCallback((key, value) => {
|
|
204
|
+
const definition = definitions.find((item) => item.key === key);
|
|
205
|
+
if (!definition) return;
|
|
206
|
+
setParams({ [definition.param ?? definition.key]: normalizeFilterValue(value, definition) });
|
|
207
|
+
}, [definitions, setParams]),
|
|
208
|
+
clear: useCallback(() => {
|
|
209
|
+
setParams(Object.fromEntries(definitions.map((definition) => [definition.param ?? definition.key, null])));
|
|
210
|
+
}, [definitions, setParams]),
|
|
211
|
+
getDraftValues: useCallback((draftValues) => ({
|
|
212
|
+
...values,
|
|
213
|
+
...draftValues
|
|
214
|
+
}), [values])
|
|
215
|
+
};
|
|
120
216
|
}
|
|
121
217
|
|
|
122
218
|
//#endregion
|
|
@@ -139,45 +235,49 @@ function filterCrudData(data, search, filters, searchFields) {
|
|
|
139
235
|
});
|
|
140
236
|
}
|
|
141
237
|
function useCrudTableState({ data, columns, searchFields = [], filters: filterDefinitions = [], pageSize = 20, initialSortState = null }) {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
238
|
+
const urlFilters = useUrlStringFilters(useMemo(() => [{
|
|
239
|
+
key: "search",
|
|
240
|
+
defaultValue: "",
|
|
241
|
+
clearValue: ""
|
|
242
|
+
}, ...filterDefinitions.map((filter) => ({
|
|
243
|
+
key: filter.key,
|
|
244
|
+
defaultValue: "all",
|
|
245
|
+
clearValue: "all"
|
|
246
|
+
}))], [filterDefinitions]));
|
|
247
|
+
const search = urlFilters.values.search ?? "";
|
|
248
|
+
const filters = useMemo(() => ({
|
|
249
|
+
...buildInitialFilters(filterDefinitions),
|
|
250
|
+
...Object.fromEntries(filterDefinitions.map((filter) => [filter.key, urlFilters.values[filter.key] ?? "all"]))
|
|
251
|
+
}), [filterDefinitions, urlFilters.values]);
|
|
145
252
|
const filteredData = useMemo(() => filterCrudData(data, search, filters, searchFields), [
|
|
146
253
|
data,
|
|
147
254
|
filters,
|
|
148
255
|
search,
|
|
149
256
|
searchFields
|
|
150
257
|
]);
|
|
258
|
+
const pagination = useUrlPagination({
|
|
259
|
+
totalItems: filteredData.length,
|
|
260
|
+
pageSize
|
|
261
|
+
});
|
|
262
|
+
const setCurrentPage = pagination.onPageChange;
|
|
151
263
|
const { sortedData, sortState, setSortState } = useTableSorting({
|
|
152
264
|
data: filteredData,
|
|
153
265
|
columns,
|
|
154
266
|
initialSortState
|
|
155
267
|
});
|
|
156
|
-
const
|
|
157
|
-
const validCurrentPage = getValidPage(currentPage, totalPages);
|
|
158
|
-
const startIndex = (validCurrentPage - 1) * pageSize;
|
|
159
|
-
const endIndex = Math.min(startIndex + pageSize, filteredData.length);
|
|
160
|
-
const paginatedData = useMemo(() => paginateCrudData(sortedData, validCurrentPage, pageSize), [
|
|
161
|
-
pageSize,
|
|
162
|
-
sortedData,
|
|
163
|
-
validCurrentPage
|
|
164
|
-
]);
|
|
268
|
+
const paginatedData = useMemo(() => pagination.paginate(sortedData), [pagination, sortedData]);
|
|
165
269
|
const setFilter = useCallback((key, value) => {
|
|
166
|
-
|
|
167
|
-
...currentFilters,
|
|
168
|
-
[key]: value
|
|
169
|
-
}));
|
|
270
|
+
urlFilters.setValue(key, value);
|
|
170
271
|
setCurrentPage(1);
|
|
171
|
-
}, []);
|
|
272
|
+
}, [setCurrentPage, urlFilters]);
|
|
172
273
|
const updateSearch = useCallback((value) => {
|
|
173
|
-
|
|
274
|
+
urlFilters.setValue("search", value);
|
|
174
275
|
setCurrentPage(1);
|
|
175
|
-
}, []);
|
|
276
|
+
}, [setCurrentPage, urlFilters]);
|
|
176
277
|
const clearAll = useCallback(() => {
|
|
177
|
-
|
|
178
|
-
setFilters(buildInitialFilters(filterDefinitions));
|
|
278
|
+
urlFilters.clear();
|
|
179
279
|
setCurrentPage(1);
|
|
180
|
-
}, [
|
|
280
|
+
}, [setCurrentPage, urlFilters]);
|
|
181
281
|
const getDraftFilterResultCount = useCallback((draftValues) => filterCrudData(data, search, {
|
|
182
282
|
...filters,
|
|
183
283
|
...draftValues
|
|
@@ -204,119 +304,10 @@ function useCrudTableState({ data, columns, searchFields = [], filters: filterDe
|
|
|
204
304
|
emptyState: hasSearch || hasFilters ? "no-results" : "initial",
|
|
205
305
|
sortState,
|
|
206
306
|
setSortState,
|
|
207
|
-
pagination
|
|
208
|
-
currentPage: validCurrentPage,
|
|
209
|
-
totalPages,
|
|
210
|
-
totalItems: filteredData.length,
|
|
211
|
-
pageSize,
|
|
212
|
-
startIndex,
|
|
213
|
-
endIndex,
|
|
214
|
-
onPageChange: setCurrentPage
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
//#endregion
|
|
220
|
-
//#region src/use-url-string-filters.ts
|
|
221
|
-
function buildParserMap(definitions) {
|
|
222
|
-
return Object.fromEntries(definitions.map((definition) => [definition.param ?? definition.key, parseAsString.withDefault(definition.defaultValue ?? "all")]));
|
|
223
|
-
}
|
|
224
|
-
function normalizeFilterValue(value, definition) {
|
|
225
|
-
if (value === (definition.clearValue ?? definition.defaultValue ?? "all")) return null;
|
|
226
|
-
return value.length > 0 ? value : null;
|
|
227
|
-
}
|
|
228
|
-
function useUrlStringFilters(definitions) {
|
|
229
|
-
const [params, setParams] = useQueryStates(useMemo(() => buildParserMap(definitions), [definitions]));
|
|
230
|
-
const values = useMemo(() => Object.fromEntries(definitions.map((definition) => {
|
|
231
|
-
const param = definition.param ?? definition.key;
|
|
232
|
-
return [definition.key, params[param] ?? definition.defaultValue ?? "all"];
|
|
233
|
-
})), [definitions, params]);
|
|
234
|
-
return {
|
|
235
|
-
values,
|
|
236
|
-
setValue: useCallback((key, value) => {
|
|
237
|
-
const definition = definitions.find((item) => item.key === key);
|
|
238
|
-
if (!definition) return;
|
|
239
|
-
setParams({ [definition.param ?? definition.key]: normalizeFilterValue(value, definition) });
|
|
240
|
-
}, [definitions, setParams]),
|
|
241
|
-
clear: useCallback(() => {
|
|
242
|
-
setParams(Object.fromEntries(definitions.map((definition) => [definition.param ?? definition.key, null])));
|
|
243
|
-
}, [definitions, setParams]),
|
|
244
|
-
getDraftValues: useCallback((draftValues) => ({
|
|
245
|
-
...values,
|
|
246
|
-
...draftValues
|
|
247
|
-
}), [values])
|
|
307
|
+
pagination
|
|
248
308
|
};
|
|
249
309
|
}
|
|
250
310
|
|
|
251
|
-
//#endregion
|
|
252
|
-
//#region src/use-url-pagination.ts
|
|
253
|
-
function useUrlPagination({ totalItems, pageSize = 20, pageParam = "page" }) {
|
|
254
|
-
const [page, setPage] = useQueryState(pageParam, parseAsInteger.withDefault(1));
|
|
255
|
-
const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
|
256
|
-
const currentPage = Math.min(Math.max(1, page), totalPages);
|
|
257
|
-
const startIndex = (currentPage - 1) * pageSize;
|
|
258
|
-
const endIndex = Math.min(startIndex + pageSize, totalItems);
|
|
259
|
-
const hasPrevPage = currentPage > 1;
|
|
260
|
-
const hasNextPage = currentPage < totalPages;
|
|
261
|
-
const goToPage = useCallback((newPage) => {
|
|
262
|
-
const validPage = Math.min(Math.max(1, newPage), totalPages);
|
|
263
|
-
setPage(validPage === 1 ? null : validPage);
|
|
264
|
-
}, [setPage, totalPages]);
|
|
265
|
-
const nextPage = useCallback(() => {
|
|
266
|
-
if (hasNextPage) goToPage(currentPage + 1);
|
|
267
|
-
}, [
|
|
268
|
-
currentPage,
|
|
269
|
-
goToPage,
|
|
270
|
-
hasNextPage
|
|
271
|
-
]);
|
|
272
|
-
const prevPage = useCallback(() => {
|
|
273
|
-
if (hasPrevPage) goToPage(currentPage - 1);
|
|
274
|
-
}, [
|
|
275
|
-
currentPage,
|
|
276
|
-
goToPage,
|
|
277
|
-
hasPrevPage
|
|
278
|
-
]);
|
|
279
|
-
const firstPage = useCallback(() => {
|
|
280
|
-
goToPage(1);
|
|
281
|
-
}, [goToPage]);
|
|
282
|
-
const lastPage = useCallback(() => {
|
|
283
|
-
goToPage(totalPages);
|
|
284
|
-
}, [goToPage, totalPages]);
|
|
285
|
-
const paginate = useCallback((data) => data.slice(startIndex, endIndex), [endIndex, startIndex]);
|
|
286
|
-
return useMemo(() => ({
|
|
287
|
-
currentPage,
|
|
288
|
-
pageSize,
|
|
289
|
-
totalPages,
|
|
290
|
-
totalItems,
|
|
291
|
-
startIndex,
|
|
292
|
-
endIndex,
|
|
293
|
-
hasPrevPage,
|
|
294
|
-
hasNextPage,
|
|
295
|
-
goToPage,
|
|
296
|
-
nextPage,
|
|
297
|
-
prevPage,
|
|
298
|
-
firstPage,
|
|
299
|
-
lastPage,
|
|
300
|
-
paginate,
|
|
301
|
-
onPageChange: goToPage
|
|
302
|
-
}), [
|
|
303
|
-
currentPage,
|
|
304
|
-
endIndex,
|
|
305
|
-
firstPage,
|
|
306
|
-
goToPage,
|
|
307
|
-
hasNextPage,
|
|
308
|
-
hasPrevPage,
|
|
309
|
-
lastPage,
|
|
310
|
-
nextPage,
|
|
311
|
-
pageSize,
|
|
312
|
-
paginate,
|
|
313
|
-
prevPage,
|
|
314
|
-
startIndex,
|
|
315
|
-
totalItems,
|
|
316
|
-
totalPages
|
|
317
|
-
]);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
311
|
//#endregion
|
|
321
312
|
export { CrudDataTable, CrudListTable, CrudResourceSheet, CrudTableView, useCrudTableState, useUrlPagination, useUrlStringFilters };
|
|
322
313
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/crud-data-table.tsx","../src/crud-list-table.tsx","../src/crud-resource-sheet.tsx","../src/crud-table-view.tsx","../src/search.ts","../src/pagination.ts","../src/use-crud-table-state.ts","../src/use-url-string-filters.ts","../src/use-url-pagination.ts"],"sourcesContent":["\"use client\";\n\nimport { SmartTable } from \"@carefully-built/ui\";\n\nimport type {\n CrudDataTableProps,\n} from \"./types\";\n\nexport function CrudDataTable<TItem extends object>({\n actions,\n actionHandlers,\n columns,\n data,\n fullHeight = true,\n getRowKey,\n isLoading,\n maxHeight,\n noDataContent,\n noDataMessage,\n onRowClick,\n pagination,\n renderActions,\n renderMobileCard,\n sortState,\n stickyHeader = true,\n onSortChange,\n}: CrudDataTableProps<TItem>): React.ReactElement {\n return (\n <SmartTable\n data={[...data]}\n columns={[...columns]}\n isLoading={isLoading}\n actions={actions ? [...actions] : undefined}\n actionHandlers={actionHandlers}\n renderActions={renderActions}\n noDataMessage={noDataMessage}\n noDataContent={noDataContent}\n getRowKey={getRowKey}\n onRowClick={onRowClick}\n renderMobileCard={renderMobileCard}\n stickyHeader={stickyHeader}\n fullHeight={fullHeight}\n maxHeight={maxHeight}\n sortState={sortState}\n onSortChange={onSortChange}\n pagination={pagination}\n />\n );\n}\n","\"use client\";\n\nimport { CrudDataTable } from \"./crud-data-table\";\nimport type { CrudDataTableProps } from \"./types\";\n\nexport type CrudListTableProps<TItem extends object> = CrudDataTableProps<TItem>;\n\nexport function CrudListTable<TItem extends object>(\n props: CrudListTableProps<TItem>,\n): React.ReactElement {\n return <CrudDataTable {...props} />;\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { ResponsiveSheet } from \"@carefully-built/ui\";\nimport type { ResponsiveSheetClassNames } from \"@carefully-built/ui\";\n\nexport interface CrudResourceSheetProps {\n readonly open: boolean;\n readonly onOpenChange: (open: boolean) => void;\n readonly title: ReactNode;\n readonly children: ReactNode;\n readonly formId?: string;\n readonly description?: ReactNode;\n readonly onCancel?: () => void;\n readonly cancelLabel?: ReactNode;\n readonly onConfirm?: () => void;\n readonly confirmLabel?: ReactNode;\n readonly confirmDisabled?: boolean;\n readonly confirmLoading?: boolean;\n readonly confirmCloseWhenDirty?: boolean;\n readonly width?: number;\n readonly className?: string;\n readonly contentClassName?: string;\n readonly footerClassName?: string;\n readonly classes?: ResponsiveSheetClassNames;\n}\n\nexport function CrudResourceSheet({\n children,\n formId,\n onConfirm,\n confirmCloseWhenDirty: _confirmCloseWhenDirty,\n ...sheetProps\n}: CrudResourceSheetProps): React.ReactElement {\n const submitForm = (): void => {\n const form = formId ? document.getElementById(formId) : null;\n\n if (form instanceof HTMLFormElement) {\n form.requestSubmit();\n return;\n }\n\n onConfirm?.();\n };\n\n return (\n <ResponsiveSheet\n {...sheetProps}\n onConfirm={formId || onConfirm ? submitForm : undefined}\n >\n {children}\n </ResponsiveSheet>\n );\n}\n","\"use client\";\n\nimport type { CrudTableViewProps } from \"./types\";\n\nimport { SmartTable, TableToolbar } from \"@carefully-built/ui\";\n\nexport function CrudTableView<TItem extends object>({\n state,\n columns,\n isLoading,\n searchPlaceholder = \"Search...\",\n filters = [],\n actions,\n actionHandlers,\n renderActions,\n noDataMessage,\n initialEmptyContent,\n noResultsContent,\n getRowKey,\n onRowClick,\n renderMobileCard,\n stickyHeader = true,\n fullHeight = true,\n maxHeight,\n}: CrudTableViewProps<TItem>): React.ReactElement {\n const emptyContent =\n state.emptyState === \"no-results\" ? noResultsContent : initialEmptyContent;\n\n return (\n <>\n <div className=\"shrink-0\">\n <TableToolbar\n search={{\n value: state.search,\n onChange: state.setSearch,\n placeholder: searchPlaceholder,\n }}\n filters={filters.map((filter) => ({\n config: filter.config,\n value: state.filters[filter.key] ?? \"all\",\n onChange: (value) => {\n state.setFilter(filter.key, value);\n },\n allowAll: filter.allowAll,\n clearable: filter.clearable,\n }))}\n onClearAll={state.clearAll}\n getDraftResultCount={state.getDraftFilterResultCount}\n />\n </div>\n\n <SmartTable\n data={state.paginatedData}\n columns={[...columns]}\n isLoading={isLoading}\n actions={actions ? [...actions] : undefined}\n actionHandlers={actionHandlers}\n renderActions={renderActions}\n noDataMessage={noDataMessage}\n noDataContent={emptyContent}\n getRowKey={getRowKey}\n onRowClick={onRowClick}\n renderMobileCard={renderMobileCard}\n stickyHeader={stickyHeader}\n fullHeight={fullHeight}\n maxHeight={maxHeight}\n sortState={state.sortState}\n onSortChange={state.setSortState}\n pagination={state.pagination}\n />\n </>\n );\n}\n","function normalizeSearchText(value: string): string {\n return value\n .toLocaleLowerCase()\n .normalize(\"NFD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .trim();\n}\n\nexport function buildCrudSearchText(values: readonly unknown[]): string {\n return normalizeSearchText(\n values\n .filter(\n (value): value is string | number =>\n typeof value === \"string\" || typeof value === \"number\",\n )\n .map(String)\n .join(\" \"),\n );\n}\n\nexport function matchesCrudSearch(searchText: string, query: string): boolean {\n const normalizedQuery = normalizeSearchText(query);\n\n if (!normalizedQuery) {\n return true;\n }\n\n return normalizedQuery\n .split(/\\s+/)\n .every((token) => searchText.includes(token));\n}\n","export interface CrudPaginationState {\n readonly currentPage: number;\n readonly totalPages: number;\n readonly totalItems: number;\n readonly pageSize: number;\n readonly startIndex: number;\n readonly endIndex: number;\n readonly onPageChange: (page: number) => void;\n}\n\nexport function getValidPage(page: number, totalPages: number): number {\n return Math.min(Math.max(1, page), totalPages);\n}\n\nexport function paginateCrudData<T>(\n data: readonly T[],\n currentPage: number,\n pageSize: number,\n): T[] {\n const startIndex = (currentPage - 1) * pageSize;\n return data.slice(startIndex, startIndex + pageSize);\n}\n","\"use client\";\n\nimport { useCallback, useMemo, useState } from \"react\";\n\nimport type { UseCrudTableStateOptions, CrudTableState } from \"./types\";\n\nimport { useTableSorting } from \"@carefully-built/ui\";\n\nimport { buildCrudSearchText, matchesCrudSearch } from \"./search\";\nimport { getValidPage, paginateCrudData } from \"./pagination\";\n\nfunction buildInitialFilters<TItem>(\n filters: readonly { readonly key: Extract<keyof TItem, string> }[],\n): Record<string, string> {\n return Object.fromEntries(filters.map((filter) => [filter.key, \"all\"]));\n}\n\nfunction itemMatchesFilters<TItem extends object>(\n item: TItem,\n filters: Record<string, string>,\n): boolean {\n const record = item as Record<string, unknown>;\n\n return Object.entries(filters).every(([key, filterValue]) => {\n if (!filterValue || filterValue === \"all\") {\n return true;\n }\n\n return record[key] === filterValue;\n });\n}\n\nfunction filterCrudData<TItem extends object>(\n data: readonly TItem[],\n search: string,\n filters: Record<string, string>,\n searchFields: readonly Extract<keyof TItem, string>[],\n): TItem[] {\n return data.filter((item) => {\n if (!itemMatchesFilters(item, filters)) {\n return false;\n }\n\n const record = item as Record<string, unknown>;\n const searchText = buildCrudSearchText(\n searchFields.map((field) => record[field]),\n );\n return matchesCrudSearch(searchText, search);\n });\n}\n\nexport function useCrudTableState<TItem extends object>({\n data,\n columns,\n searchFields = [],\n filters: filterDefinitions = [],\n pageSize = 20,\n initialSortState = null,\n}: UseCrudTableStateOptions<TItem>): CrudTableState<TItem> {\n const [search, setSearch] = useState(\"\");\n const [filters, setFilters] = useState<Record<string, string>>(() =>\n buildInitialFilters(filterDefinitions),\n );\n const [currentPage, setCurrentPage] = useState(1);\n\n const filteredData = useMemo(\n () => filterCrudData(data, search, filters, searchFields),\n [data, filters, search, searchFields],\n );\n const { sortedData, sortState, setSortState } = useTableSorting({\n data: filteredData,\n columns,\n initialSortState,\n });\n\n const totalPages = Math.max(1, Math.ceil(filteredData.length / pageSize));\n const validCurrentPage = getValidPage(currentPage, totalPages);\n const startIndex = (validCurrentPage - 1) * pageSize;\n const endIndex = Math.min(startIndex + pageSize, filteredData.length);\n const paginatedData = useMemo(\n () => paginateCrudData(sortedData, validCurrentPage, pageSize),\n [pageSize, sortedData, validCurrentPage],\n );\n\n const setFilter = useCallback((key: string, value: string) => {\n setFilters((currentFilters) => ({\n ...currentFilters,\n [key]: value,\n }));\n setCurrentPage(1);\n }, []);\n\n const updateSearch = useCallback((value: string) => {\n setSearch(value);\n setCurrentPage(1);\n }, []);\n\n const clearAll = useCallback(() => {\n setSearch(\"\");\n setFilters(buildInitialFilters(filterDefinitions));\n setCurrentPage(1);\n }, [filterDefinitions]);\n\n const getDraftFilterResultCount = useCallback(\n (draftValues: Record<string, string>) =>\n filterCrudData(\n data,\n search,\n {\n ...filters,\n ...draftValues,\n },\n searchFields,\n ).length,\n [data, filters, search, searchFields],\n );\n\n const hasSearch = search.trim().length > 0;\n const hasFilters = Object.values(filters).some(\n (value) => value && value !== \"all\",\n );\n\n return {\n filteredData,\n sortedData,\n paginatedData,\n search,\n setSearch: updateSearch,\n filters,\n setFilter,\n clearAll,\n getDraftFilterResultCount,\n hasSearch,\n hasFilters,\n emptyState: hasSearch || hasFilters ? \"no-results\" : \"initial\",\n sortState,\n setSortState,\n pagination: {\n currentPage: validCurrentPage,\n totalPages,\n totalItems: filteredData.length,\n pageSize,\n startIndex,\n endIndex,\n onPageChange: setCurrentPage,\n },\n };\n}\n","\"use client\";\n\nimport { parseAsString, useQueryStates } from \"nuqs\";\nimport { useCallback, useMemo } from \"react\";\n\nexport interface UrlStringFilterDefinition<TKey extends string = string> {\n readonly key: TKey;\n readonly param?: string;\n readonly defaultValue?: string;\n readonly clearValue?: string;\n}\n\ntype UrlStringFilterValues<TDefinitions extends readonly UrlStringFilterDefinition[]> = {\n readonly [TDefinition in TDefinitions[number] as TDefinition[\"key\"]]: string;\n};\n\nexport interface UrlStringFiltersState<\n TDefinitions extends readonly UrlStringFilterDefinition[],\n> {\n readonly values: UrlStringFilterValues<TDefinitions>;\n readonly setValue: (key: TDefinitions[number][\"key\"], value: string) => void;\n readonly clear: () => void;\n readonly getDraftValues: (\n draftValues: Record<string, string>,\n ) => UrlStringFilterValues<TDefinitions>;\n}\n\nfunction buildParserMap(definitions: readonly UrlStringFilterDefinition[]) {\n return Object.fromEntries(\n definitions.map((definition) => [\n definition.param ?? definition.key,\n parseAsString.withDefault(definition.defaultValue ?? \"all\"),\n ]),\n );\n}\n\nfunction normalizeFilterValue(\n value: string,\n definition: UrlStringFilterDefinition,\n): string | null {\n const clearValue = definition.clearValue ?? definition.defaultValue ?? \"all\";\n\n if (value === clearValue) {\n return null;\n }\n\n return value.length > 0 ? value : null;\n}\n\nexport function useUrlStringFilters<\n const TDefinitions extends readonly UrlStringFilterDefinition[],\n>(definitions: TDefinitions): UrlStringFiltersState<TDefinitions> {\n const parserMap = useMemo(() => buildParserMap(definitions), [definitions]);\n const [params, setParams] = useQueryStates(parserMap);\n\n const values = useMemo(\n () =>\n Object.fromEntries(\n definitions.map((definition) => {\n const param = definition.param ?? definition.key;\n return [definition.key, params[param] ?? definition.defaultValue ?? \"all\"];\n }),\n ) as UrlStringFilterValues<TDefinitions>,\n [definitions, params],\n );\n\n const setValue = useCallback(\n (key: TDefinitions[number][\"key\"], value: string) => {\n const definition = definitions.find((item) => item.key === key);\n\n if (!definition) {\n return;\n }\n\n void setParams({\n [definition.param ?? definition.key]: normalizeFilterValue(value, definition),\n });\n },\n [definitions, setParams],\n );\n\n const clear = useCallback(() => {\n void setParams(\n Object.fromEntries(\n definitions.map((definition) => [definition.param ?? definition.key, null]),\n ),\n );\n }, [definitions, setParams]);\n\n const getDraftValues = useCallback(\n (draftValues: Record<string, string>) =>\n ({\n ...values,\n ...draftValues,\n }) as UrlStringFilterValues<TDefinitions>,\n [values],\n );\n\n return {\n values,\n setValue,\n clear,\n getDraftValues,\n };\n}\n","\"use client\";\n\nimport { parseAsInteger, useQueryState } from \"nuqs\";\nimport { useCallback, useMemo } from \"react\";\n\nimport type { CrudPaginationState } from \"./pagination\";\n\nexport interface UseUrlPaginationOptions {\n readonly totalItems: number;\n readonly pageSize?: number;\n readonly pageParam?: string;\n}\n\nexport interface UrlPaginationState extends CrudPaginationState {\n readonly hasPrevPage: boolean;\n readonly hasNextPage: boolean;\n readonly goToPage: (page: number) => void;\n readonly nextPage: () => void;\n readonly prevPage: () => void;\n readonly firstPage: () => void;\n readonly lastPage: () => void;\n readonly paginate: <T>(data: readonly T[]) => T[];\n}\n\nexport function useUrlPagination({\n totalItems,\n pageSize = 20,\n pageParam = \"page\",\n}: UseUrlPaginationOptions): UrlPaginationState {\n const [page, setPage] = useQueryState(\n pageParam,\n parseAsInteger.withDefault(1),\n );\n\n const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));\n const currentPage = Math.min(Math.max(1, page), totalPages);\n const startIndex = (currentPage - 1) * pageSize;\n const endIndex = Math.min(startIndex + pageSize, totalItems);\n const hasPrevPage = currentPage > 1;\n const hasNextPage = currentPage < totalPages;\n\n const goToPage = useCallback(\n (newPage: number) => {\n const validPage = Math.min(Math.max(1, newPage), totalPages);\n void setPage(validPage === 1 ? null : validPage);\n },\n [setPage, totalPages],\n );\n\n const nextPage = useCallback(() => {\n if (hasNextPage) {\n goToPage(currentPage + 1);\n }\n }, [currentPage, goToPage, hasNextPage]);\n\n const prevPage = useCallback(() => {\n if (hasPrevPage) {\n goToPage(currentPage - 1);\n }\n }, [currentPage, goToPage, hasPrevPage]);\n\n const firstPage = useCallback(() => {\n goToPage(1);\n }, [goToPage]);\n\n const lastPage = useCallback(() => {\n goToPage(totalPages);\n }, [goToPage, totalPages]);\n\n const paginate = useCallback(\n <T,>(data: readonly T[]): T[] => data.slice(startIndex, endIndex),\n [endIndex, startIndex],\n );\n\n return useMemo(\n () => ({\n currentPage,\n pageSize,\n totalPages,\n totalItems,\n startIndex,\n endIndex,\n hasPrevPage,\n hasNextPage,\n goToPage,\n nextPage,\n prevPage,\n firstPage,\n lastPage,\n paginate,\n onPageChange: goToPage,\n }),\n [\n currentPage,\n endIndex,\n firstPage,\n goToPage,\n hasNextPage,\n hasPrevPage,\n lastPage,\n nextPage,\n pageSize,\n paginate,\n prevPage,\n startIndex,\n totalItems,\n totalPages,\n ],\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/crud-data-table.tsx","../src/crud-list-table.tsx","../src/crud-resource-sheet.tsx","../src/crud-table-view.tsx","../src/search.ts","../src/use-url-pagination.ts","../src/use-url-string-filters.ts","../src/use-crud-table-state.ts"],"sourcesContent":["\"use client\";\n\nimport { SmartTable } from \"@carefully-built/ui\";\n\nimport type {\n CrudDataTableProps,\n} from \"./types\";\n\nexport function CrudDataTable<TItem extends object>({\n actions,\n actionLabels,\n actionHandlers,\n columns,\n data,\n fullHeight = true,\n getRowKey,\n isLoading,\n maxHeight,\n noDataContent,\n noDataMessage,\n onRowClick,\n pagination,\n renderActions,\n renderMobileCard,\n sortState,\n stickyHeader = true,\n onSortChange,\n}: CrudDataTableProps<TItem>): React.ReactElement {\n return (\n <SmartTable\n data={[...data]}\n columns={[...columns]}\n isLoading={isLoading}\n actions={actions ? [...actions] : undefined}\n actionLabels={actionLabels}\n actionHandlers={actionHandlers}\n renderActions={renderActions}\n noDataMessage={noDataMessage}\n noDataContent={noDataContent}\n getRowKey={getRowKey}\n onRowClick={onRowClick}\n renderMobileCard={renderMobileCard}\n stickyHeader={stickyHeader}\n fullHeight={fullHeight}\n maxHeight={maxHeight}\n sortState={sortState}\n onSortChange={onSortChange}\n pagination={pagination}\n />\n );\n}\n","\"use client\";\n\nimport { CrudDataTable } from \"./crud-data-table\";\nimport type { CrudDataTableProps } from \"./types\";\n\nexport type CrudListTableProps<TItem extends object> = CrudDataTableProps<TItem>;\n\nexport function CrudListTable<TItem extends object>(\n props: CrudListTableProps<TItem>,\n): React.ReactElement {\n return <CrudDataTable {...props} />;\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { ResponsiveSheet } from \"@carefully-built/ui\";\nimport type { ResponsiveSheetClassNames, SheetOutsideInteractionGuard } from \"@carefully-built/ui\";\n\nexport interface CrudResourceSheetProps {\n readonly open: boolean;\n readonly onOpenChange: (open: boolean) => void;\n readonly title: ReactNode;\n readonly children: ReactNode;\n readonly formId?: string;\n readonly description?: ReactNode;\n readonly onCancel?: () => void;\n readonly cancelLabel?: ReactNode;\n readonly onConfirm?: () => void;\n readonly confirmLabel?: ReactNode;\n readonly confirmDisabled?: boolean;\n readonly confirmLoading?: boolean;\n readonly confirmCloseWhenDirty?: boolean;\n readonly width?: number;\n readonly outsideInteractionGuard?: SheetOutsideInteractionGuard;\n readonly className?: string;\n readonly contentClassName?: string;\n readonly footerClassName?: string;\n readonly classes?: ResponsiveSheetClassNames;\n}\n\nexport function CrudResourceSheet({\n children,\n formId,\n onConfirm,\n confirmCloseWhenDirty: _confirmCloseWhenDirty,\n outsideInteractionGuard,\n ...sheetProps\n}: CrudResourceSheetProps): React.ReactElement {\n const submitForm = (): void => {\n const form = formId ? document.getElementById(formId) : null;\n\n if (form instanceof HTMLFormElement) {\n form.requestSubmit();\n return;\n }\n\n onConfirm?.();\n };\n\n return (\n <ResponsiveSheet\n {...sheetProps}\n outsideInteractionGuard={outsideInteractionGuard}\n onConfirm={formId || onConfirm ? submitForm : undefined}\n >\n {children}\n </ResponsiveSheet>\n );\n}\n","\"use client\";\n\nimport type { CrudTableViewProps } from \"./types\";\n\nimport { SmartTable, TableToolbar } from \"@carefully-built/ui\";\n\nexport function CrudTableView<TItem extends object>({\n state,\n columns,\n isLoading,\n searchPlaceholder = \"Search...\",\n toolbarLabels,\n filters = [],\n actions,\n actionLabels,\n actionHandlers,\n renderActions,\n noDataMessage,\n initialEmptyContent,\n noResultsContent,\n getRowKey,\n onRowClick,\n renderMobileCard,\n stickyHeader = true,\n fullHeight = true,\n maxHeight,\n}: CrudTableViewProps<TItem>): React.ReactElement {\n const emptyContent =\n state.emptyState === \"no-results\" ? noResultsContent : initialEmptyContent;\n\n return (\n <>\n <div className=\"shrink-0\">\n <TableToolbar\n search={{\n value: state.search,\n onChange: state.setSearch,\n placeholder: searchPlaceholder,\n }}\n filters={filters.map((filter) => ({\n config: filter.config,\n value: state.filters[filter.key] ?? \"all\",\n onChange: (value) => {\n state.setFilter(filter.key, value);\n },\n allowAll: filter.allowAll,\n allOptionLabel: filter.allOptionLabel,\n clearable: filter.clearable,\n }))}\n onClearAll={state.clearAll}\n getDraftResultCount={state.getDraftFilterResultCount}\n labels={toolbarLabels}\n />\n </div>\n\n <SmartTable\n data={state.paginatedData}\n columns={[...columns]}\n isLoading={isLoading}\n actions={actions ? [...actions] : undefined}\n actionLabels={actionLabels}\n actionHandlers={actionHandlers}\n renderActions={renderActions}\n noDataMessage={noDataMessage}\n noDataContent={emptyContent}\n getRowKey={getRowKey}\n onRowClick={onRowClick}\n renderMobileCard={renderMobileCard}\n stickyHeader={stickyHeader}\n fullHeight={fullHeight}\n maxHeight={maxHeight}\n sortState={state.sortState}\n onSortChange={state.setSortState}\n pagination={state.pagination}\n />\n </>\n );\n}\n","function normalizeSearchText(value: string): string {\n return value\n .toLocaleLowerCase()\n .normalize(\"NFD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .trim();\n}\n\nexport function buildCrudSearchText(values: readonly unknown[]): string {\n return normalizeSearchText(\n values\n .filter(\n (value): value is string | number =>\n typeof value === \"string\" || typeof value === \"number\",\n )\n .map(String)\n .join(\" \"),\n );\n}\n\nexport function matchesCrudSearch(searchText: string, query: string): boolean {\n const normalizedQuery = normalizeSearchText(query);\n\n if (!normalizedQuery) {\n return true;\n }\n\n return normalizedQuery\n .split(/\\s+/)\n .every((token) => searchText.includes(token));\n}\n","\"use client\";\n\nimport { parseAsInteger, useQueryState } from \"nuqs\";\nimport { useCallback, useMemo } from \"react\";\n\nimport type { CrudPaginationState } from \"./pagination\";\n\nexport interface UseUrlPaginationOptions {\n readonly totalItems: number;\n readonly pageSize?: number;\n readonly pageParam?: string;\n}\n\nexport interface UrlPaginationState extends CrudPaginationState {\n readonly hasPrevPage: boolean;\n readonly hasNextPage: boolean;\n readonly goToPage: (page: number) => void;\n readonly nextPage: () => void;\n readonly prevPage: () => void;\n readonly firstPage: () => void;\n readonly lastPage: () => void;\n readonly paginate: <T>(data: readonly T[]) => T[];\n}\n\nexport function useUrlPagination({\n totalItems,\n pageSize = 20,\n pageParam = \"page\",\n}: UseUrlPaginationOptions): UrlPaginationState {\n const [page, setPage] = useQueryState(\n pageParam,\n parseAsInteger.withDefault(1),\n );\n\n const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));\n const currentPage = Math.min(Math.max(1, page), totalPages);\n const startIndex = (currentPage - 1) * pageSize;\n const endIndex = Math.min(startIndex + pageSize, totalItems);\n const hasPrevPage = currentPage > 1;\n const hasNextPage = currentPage < totalPages;\n\n const goToPage = useCallback(\n (newPage: number) => {\n const validPage = Math.min(Math.max(1, newPage), totalPages);\n void setPage(validPage === 1 ? null : validPage);\n },\n [setPage, totalPages],\n );\n\n const nextPage = useCallback(() => {\n if (hasNextPage) {\n goToPage(currentPage + 1);\n }\n }, [currentPage, goToPage, hasNextPage]);\n\n const prevPage = useCallback(() => {\n if (hasPrevPage) {\n goToPage(currentPage - 1);\n }\n }, [currentPage, goToPage, hasPrevPage]);\n\n const firstPage = useCallback(() => {\n goToPage(1);\n }, [goToPage]);\n\n const lastPage = useCallback(() => {\n goToPage(totalPages);\n }, [goToPage, totalPages]);\n\n const paginate = useCallback(\n <T,>(data: readonly T[]): T[] => data.slice(startIndex, endIndex),\n [endIndex, startIndex],\n );\n\n return useMemo(\n () => ({\n currentPage,\n pageSize,\n totalPages,\n totalItems,\n startIndex,\n endIndex,\n hasPrevPage,\n hasNextPage,\n goToPage,\n nextPage,\n prevPage,\n firstPage,\n lastPage,\n paginate,\n onPageChange: goToPage,\n }),\n [\n currentPage,\n endIndex,\n firstPage,\n goToPage,\n hasNextPage,\n hasPrevPage,\n lastPage,\n nextPage,\n pageSize,\n paginate,\n prevPage,\n startIndex,\n totalItems,\n totalPages,\n ],\n );\n}\n","\"use client\";\n\nimport { parseAsString, useQueryStates } from \"nuqs\";\nimport { useCallback, useMemo } from \"react\";\n\nexport interface UrlStringFilterDefinition<TKey extends string = string> {\n readonly key: TKey;\n readonly param?: string;\n readonly defaultValue?: string;\n readonly clearValue?: string;\n}\n\ntype UrlStringFilterValues<TDefinitions extends readonly UrlStringFilterDefinition[]> = {\n readonly [TDefinition in TDefinitions[number] as TDefinition[\"key\"]]: string;\n};\n\nexport interface UrlStringFiltersState<\n TDefinitions extends readonly UrlStringFilterDefinition[],\n> {\n readonly values: UrlStringFilterValues<TDefinitions>;\n readonly setValue: (key: TDefinitions[number][\"key\"], value: string) => void;\n readonly clear: () => void;\n readonly getDraftValues: (\n draftValues: Record<string, string>,\n ) => UrlStringFilterValues<TDefinitions>;\n}\n\nfunction buildParserMap(definitions: readonly UrlStringFilterDefinition[]) {\n return Object.fromEntries(\n definitions.map((definition) => [\n definition.param ?? definition.key,\n parseAsString.withDefault(definition.defaultValue ?? \"all\"),\n ]),\n );\n}\n\nfunction normalizeFilterValue(\n value: string,\n definition: UrlStringFilterDefinition,\n): string | null {\n const clearValue = definition.clearValue ?? definition.defaultValue ?? \"all\";\n\n if (value === clearValue) {\n return null;\n }\n\n return value.length > 0 ? value : null;\n}\n\nexport function useUrlStringFilters<\n const TDefinitions extends readonly UrlStringFilterDefinition[],\n>(definitions: TDefinitions): UrlStringFiltersState<TDefinitions> {\n const parserMap = useMemo(() => buildParserMap(definitions), [definitions]);\n const [params, setParams] = useQueryStates(parserMap);\n\n const values = useMemo(\n () =>\n Object.fromEntries(\n definitions.map((definition) => {\n const param = definition.param ?? definition.key;\n return [definition.key, params[param] ?? definition.defaultValue ?? \"all\"];\n }),\n ) as UrlStringFilterValues<TDefinitions>,\n [definitions, params],\n );\n\n const setValue = useCallback(\n (key: TDefinitions[number][\"key\"], value: string) => {\n const definition = definitions.find((item) => item.key === key);\n\n if (!definition) {\n return;\n }\n\n void setParams({\n [definition.param ?? definition.key]: normalizeFilterValue(value, definition),\n });\n },\n [definitions, setParams],\n );\n\n const clear = useCallback(() => {\n void setParams(\n Object.fromEntries(\n definitions.map((definition) => [definition.param ?? definition.key, null]),\n ),\n );\n }, [definitions, setParams]);\n\n const getDraftValues = useCallback(\n (draftValues: Record<string, string>) =>\n ({\n ...values,\n ...draftValues,\n }) as UrlStringFilterValues<TDefinitions>,\n [values],\n );\n\n return {\n values,\n setValue,\n clear,\n getDraftValues,\n };\n}\n","\"use client\";\n\nimport { useCallback, useMemo } from \"react\";\n\nimport type { UseCrudTableStateOptions, CrudTableState } from \"./types\";\n\nimport { useTableSorting } from \"@carefully-built/ui\";\n\nimport { buildCrudSearchText, matchesCrudSearch } from \"./search\";\nimport { useUrlPagination } from \"./use-url-pagination\";\nimport { useUrlStringFilters, type UrlStringFilterDefinition } from \"./use-url-string-filters\";\n\nfunction buildInitialFilters<TItem>(\n filters: readonly { readonly key: Extract<keyof TItem, string> }[],\n): Record<string, string> {\n return Object.fromEntries(filters.map((filter) => [filter.key, \"all\"]));\n}\n\nfunction itemMatchesFilters<TItem extends object>(\n item: TItem,\n filters: Record<string, string>,\n): boolean {\n const record = item as Record<string, unknown>;\n\n return Object.entries(filters).every(([key, filterValue]) => {\n if (!filterValue || filterValue === \"all\") {\n return true;\n }\n\n return record[key] === filterValue;\n });\n}\n\nfunction filterCrudData<TItem extends object>(\n data: readonly TItem[],\n search: string,\n filters: Record<string, string>,\n searchFields: readonly Extract<keyof TItem, string>[],\n): TItem[] {\n return data.filter((item) => {\n if (!itemMatchesFilters(item, filters)) {\n return false;\n }\n\n const record = item as Record<string, unknown>;\n const searchText = buildCrudSearchText(\n searchFields.map((field) => record[field]),\n );\n return matchesCrudSearch(searchText, search);\n });\n}\n\nexport function useCrudTableState<TItem extends object>({\n data,\n columns,\n searchFields = [],\n filters: filterDefinitions = [],\n pageSize = 20,\n initialSortState = null,\n}: UseCrudTableStateOptions<TItem>): CrudTableState<TItem> {\n const urlFilterDefinitions = useMemo(\n () =>\n [\n { key: \"search\", defaultValue: \"\", clearValue: \"\" },\n ...filterDefinitions.map((filter) => ({\n key: filter.key,\n defaultValue: \"all\",\n clearValue: \"all\",\n })),\n ] satisfies readonly UrlStringFilterDefinition[],\n [filterDefinitions],\n );\n const urlFilters = useUrlStringFilters(urlFilterDefinitions);\n const search = urlFilters.values.search ?? \"\";\n const filters = useMemo(\n () => ({\n ...buildInitialFilters(filterDefinitions),\n ...Object.fromEntries(\n filterDefinitions.map((filter) => [filter.key, urlFilters.values[filter.key] ?? \"all\"]),\n ),\n }),\n [filterDefinitions, urlFilters.values],\n );\n\n const filteredData = useMemo(\n () => filterCrudData(data, search, filters, searchFields),\n [data, filters, search, searchFields],\n );\n const pagination = useUrlPagination({\n totalItems: filteredData.length,\n pageSize,\n });\n const setCurrentPage = pagination.onPageChange;\n const { sortedData, sortState, setSortState } = useTableSorting({\n data: filteredData,\n columns,\n initialSortState,\n });\n\n const paginatedData = useMemo(\n () => pagination.paginate(sortedData),\n [pagination, sortedData],\n );\n\n const setFilter = useCallback((key: string, value: string) => {\n urlFilters.setValue(key, value);\n void setCurrentPage(1);\n }, [setCurrentPage, urlFilters]);\n\n const updateSearch = useCallback((value: string) => {\n urlFilters.setValue(\"search\", value);\n void setCurrentPage(1);\n }, [setCurrentPage, urlFilters]);\n\n const clearAll = useCallback(() => {\n urlFilters.clear();\n void setCurrentPage(1);\n }, [setCurrentPage, urlFilters]);\n\n const getDraftFilterResultCount = useCallback(\n (draftValues: Record<string, string>) =>\n filterCrudData(\n data,\n search,\n {\n ...filters,\n ...draftValues,\n },\n searchFields,\n ).length,\n [data, filters, search, searchFields],\n );\n\n const hasSearch = search.trim().length > 0;\n const hasFilters = Object.values(filters).some(\n (value) => value && value !== \"all\",\n );\n\n return {\n filteredData,\n sortedData,\n paginatedData,\n search,\n setSearch: updateSearch,\n filters,\n setFilter,\n clearAll,\n getDraftFilterResultCount,\n hasSearch,\n hasFilters,\n emptyState: hasSearch || hasFilters ? \"no-results\" : \"initial\",\n sortState,\n setSortState,\n pagination,\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carefully-built/crud",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Config-driven CRUD table and form helpers for Carefully Built SaaS apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"access": "public"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@carefully-built/ui": ">=0.1.
|
|
48
|
+
"@carefully-built/ui": ">=0.1.15",
|
|
49
49
|
"nuqs": ">=2.0.0",
|
|
50
50
|
"react": ">=18.2.0 || >=19.0.0",
|
|
51
51
|
"react-dom": ">=18.2.0 || >=19.0.0"
|