vasuzex 2.3.0 → 2.3.1

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.
@@ -10,6 +10,7 @@
10
10
  * - Rows per page selector
11
11
  * - Action buttons (edit/view/delete/switch)
12
12
  * - Loading and empty states
13
+ * - State persistence (restores page/filters when navigating back)
13
14
  *
14
15
  * @module components/DataTable
15
16
  */
@@ -38,6 +39,8 @@ import { Pagination } from "./Pagination.jsx";
38
39
  * @param {string} props.initialStatusFilter - Initial status filter (all/true/false)
39
40
  * @param {number} props.initialLimit - Initial rows per page
40
41
  * @param {string} props.emptyText - Text to show when no data
42
+ * @param {boolean} props.persistState - Enable state persistence (default: true)
43
+ * @param {string} props.stateKey - Custom key for state storage (default: derived from apiUrl)
41
44
  */
42
45
  export function DataTable(props) {
43
46
  // Internal refresh key for self-refresh
@@ -60,6 +63,8 @@ export function DataTable(props) {
60
63
  onDelete,
61
64
  onToggle,
62
65
  api, // API client instance passed as prop
66
+ persistState = true, // Enable state persistence by default
67
+ stateKey, // Optional custom state key
63
68
  } = props;
64
69
 
65
70
  // Validate that api client is provided
@@ -67,6 +72,66 @@ export function DataTable(props) {
67
72
  throw new Error('DataTable requires "api" prop - pass your API client instance');
68
73
  }
69
74
 
75
+ // Generate unique storage key based on apiUrl or custom stateKey
76
+ const storageKey = React.useMemo(() => {
77
+ if (stateKey) return `datatable_${stateKey}`;
78
+ // Use apiUrl as key (remove query params for consistency)
79
+ const cleanUrl = apiUrl.split('?')[0];
80
+ return `datatable_${cleanUrl.replace(/[^a-zA-Z0-9]/g, '_')}`;
81
+ }, [apiUrl, stateKey]);
82
+
83
+ // Helper to load persisted state
84
+ const loadPersistedState = React.useCallback(() => {
85
+ if (!persistState) return null;
86
+ try {
87
+ const stored = sessionStorage.getItem(storageKey);
88
+ return stored ? JSON.parse(stored) : null;
89
+ } catch (error) {
90
+ return null;
91
+ }
92
+ }, [persistState, storageKey]);
93
+
94
+ // Helper to save state
95
+ const saveState = React.useCallback((state) => {
96
+ if (!persistState) return;
97
+ try {
98
+ sessionStorage.setItem(storageKey, JSON.stringify(state));
99
+ } catch (error) {
100
+ // Silently fail if sessionStorage is not available
101
+ }
102
+ }, [persistState, storageKey]);
103
+
104
+ // Initialize state from persisted data or props
105
+ const persistedState = loadPersistedState();
106
+
107
+ const [page, setPage] = React.useState(persistedState?.page || initialPage);
108
+ const [sortBy, setSortBy] = React.useState(
109
+ persistedState?.sortBy || initialSortBy || (columns.find((c) => c.sortable)?.field) || "",
110
+ );
111
+ const [sortOrder, setSortOrder] = React.useState(persistedState?.sortOrder || initialSortOrder);
112
+ const [search, setSearch] = React.useState(persistedState?.search || initialSearch || "");
113
+ const [statusFilter, setStatusFilter] = React.useState(persistedState?.statusFilter || initialStatusFilter || "all");
114
+ const [limit, setLimit] = React.useState(persistedState?.limit || initialLimit || 10);
115
+ const [data, setData] = React.useState([]);
116
+ const [loading, setLoading] = React.useState(false);
117
+ const [totalPages, setTotalPages] = React.useState(1);
118
+ const [totalItems, setTotalItems] = React.useState(0);
119
+ // Column search state
120
+ const [columnSearch, setColumnSearch] = React.useState(persistedState?.columnSearch || {});
121
+
122
+ // Save state whenever it changes
123
+ React.useEffect(() => {
124
+ saveState({
125
+ page,
126
+ sortBy,
127
+ sortOrder,
128
+ search,
129
+ statusFilter,
130
+ limit,
131
+ columnSearch,
132
+ });
133
+ }, [page, sortBy, sortOrder, search, statusFilter, limit, columnSearch, saveState]);
134
+
70
135
  const handleStatusToggle = async (row) => {
71
136
  if (!toggleLink) return;
72
137
  try {
@@ -81,21 +146,6 @@ export function DataTable(props) {
81
146
  toast.error(error.message || "Failed to update status");
82
147
  }
83
148
  };
84
-
85
- const [page, setPage] = React.useState(initialPage);
86
- const [sortBy, setSortBy] = React.useState(
87
- initialSortBy || (columns.find((c) => c.sortable)?.field) || "",
88
- );
89
- const [sortOrder, setSortOrder] = React.useState(initialSortOrder);
90
- const [search, setSearch] = React.useState(initialSearch || "");
91
- const [statusFilter, setStatusFilter] = React.useState(initialStatusFilter || "all");
92
- const [limit, setLimit] = React.useState(initialLimit || 10);
93
- const [data, setData] = React.useState([]);
94
- const [loading, setLoading] = React.useState(false);
95
- const [totalPages, setTotalPages] = React.useState(1);
96
- const [totalItems, setTotalItems] = React.useState(0);
97
- // Column search state
98
- const [columnSearch, setColumnSearch] = React.useState({});
99
149
 
100
150
  // Reset page to 1 when columnSearch changes
101
151
  React.useEffect(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vasuzex",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "Laravel-inspired framework for Node.js monorepos - V2 with optimized dependencies",
5
5
  "type": "module",
6
6
  "main": "./framework/index.js",